summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/plugin.c2
-rw-r--r--apps/plugin.h4
-rw-r--r--apps/plugins/bitmaps/mono/SOURCES5
-rw-r--r--apps/plugins/bitmaps/mono/mpegplayer_status_icons_12x12x1.bmpbin0 -> 158 bytes
-rw-r--r--apps/plugins/bitmaps/mono/mpegplayer_status_icons_16x16x1.bmpbin0 -> 254 bytes
-rw-r--r--apps/plugins/bitmaps/mono/mpegplayer_status_icons_8x8x1.bmpbin0 -> 126 bytes
-rw-r--r--apps/plugins/mpegplayer/Makefile4
-rw-r--r--apps/plugins/mpegplayer/audio_thread.c3
-rw-r--r--apps/plugins/mpegplayer/disk_buf.h3
-rw-r--r--apps/plugins/mpegplayer/mpeg_parser.c6
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.c1066
-rw-r--r--apps/plugins/mpegplayer/stream_mgr.c133
-rw-r--r--apps/plugins/mpegplayer/stream_mgr.h10
-rw-r--r--apps/plugins/mpegplayer/stream_thread.h1
-rw-r--r--apps/plugins/mpegplayer/video_out.h25
-rw-r--r--apps/plugins/mpegplayer/video_out_rockbox.c171
-rw-r--r--apps/plugins/mpegplayer/video_thread.c9
17 files changed, 1339 insertions, 103 deletions
diff --git a/apps/plugin.c b/apps/plugin.c
index 0192590361..78c3aa9370 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -580,6 +580,8 @@ static const struct plugin_api rockbox_api = {
580 trigger_cpu_boost, 580 trigger_cpu_boost,
581 cancel_cpu_boost, 581 cancel_cpu_boost,
582#endif 582#endif
583 sound_unit,
584 sound_val2phys,
583#endif /* CONFIG_CODEC == SWCODEC */ 585#endif /* CONFIG_CODEC == SWCODEC */
584}; 586};
585 587
diff --git a/apps/plugin.h b/apps/plugin.h
index 5b6d7cf872..771b5738e8 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -119,7 +119,7 @@
119#define PLUGIN_MAGIC 0x526F634B /* RocK */ 119#define PLUGIN_MAGIC 0x526F634B /* RocK */
120 120
121/* increase this every time the api struct changes */ 121/* increase this every time the api struct changes */
122#define PLUGIN_API_VERSION 92 122#define PLUGIN_API_VERSION 93
123 123
124/* update this to latest version if a change to the api struct breaks 124/* update this to latest version if a change to the api struct breaks
125 backwards compatibility (and please take the opportunity to sort in any 125 backwards compatibility (and please take the opportunity to sort in any
@@ -715,6 +715,8 @@ struct plugin_api {
715 void (*trigger_cpu_boost)(void); 715 void (*trigger_cpu_boost)(void);
716 void (*cancel_cpu_boost)(void); 716 void (*cancel_cpu_boost)(void);
717#endif 717#endif
718 const char * (*sound_unit)(int setting);
719 int (*sound_val2phys)(int setting, int value);
718#endif /* CONFIG_CODEC == SWCODEC */ 720#endif /* CONFIG_CODEC == SWCODEC */
719}; 721};
720 722
diff --git a/apps/plugins/bitmaps/mono/SOURCES b/apps/plugins/bitmaps/mono/SOURCES
index 36fd2b02b0..bc75c3a678 100644
--- a/apps/plugins/bitmaps/mono/SOURCES
+++ b/apps/plugins/bitmaps/mono/SOURCES
@@ -38,6 +38,11 @@ flipit_cursor.16x13x1.bmp
38#endif 38#endif
39#endif 39#endif
40 40
41/* MPEGplayer */
42mpegplayer_status_icons_8x8x1.bmp
43mpegplayer_status_icons_12x12x1.bmp
44mpegplayer_status_icons_16x16x1.bmp
45
41#if LCD_WIDTH == 160 && LCD_HEIGHT == 128 && LCD_DEPTH < 16 46#if LCD_WIDTH == 160 && LCD_HEIGHT == 128 && LCD_DEPTH < 16
42superdom_boarditems.160x128x1.bmp 47superdom_boarditems.160x128x1.bmp
43#endif 48#endif
diff --git a/apps/plugins/bitmaps/mono/mpegplayer_status_icons_12x12x1.bmp b/apps/plugins/bitmaps/mono/mpegplayer_status_icons_12x12x1.bmp
new file mode 100644
index 0000000000..61f6a52fbc
--- /dev/null
+++ b/apps/plugins/bitmaps/mono/mpegplayer_status_icons_12x12x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/mono/mpegplayer_status_icons_16x16x1.bmp b/apps/plugins/bitmaps/mono/mpegplayer_status_icons_16x16x1.bmp
new file mode 100644
index 0000000000..944bd5132e
--- /dev/null
+++ b/apps/plugins/bitmaps/mono/mpegplayer_status_icons_16x16x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/mono/mpegplayer_status_icons_8x8x1.bmp b/apps/plugins/bitmaps/mono/mpegplayer_status_icons_8x8x1.bmp
new file mode 100644
index 0000000000..d4b71fe1e0
--- /dev/null
+++ b/apps/plugins/bitmaps/mono/mpegplayer_status_icons_8x8x1.bmp
Binary files differ
diff --git a/apps/plugins/mpegplayer/Makefile b/apps/plugins/mpegplayer/Makefile
index c43dde04bc..97c9e6a919 100644
--- a/apps/plugins/mpegplayer/Makefile
+++ b/apps/plugins/mpegplayer/Makefile
@@ -43,7 +43,7 @@ libmad-mpegplayer:
43ifndef SIMVER 43ifndef SIMVER
44$(OBJDIR)/mpegplayer.elf: $(OBJS) $(LINKFILE) $(BUILDDIR)/libmad-mpegplayer.a 44$(OBJDIR)/mpegplayer.elf: $(OBJS) $(LINKFILE) $(BUILDDIR)/libmad-mpegplayer.a
45 $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) -o $@ $(OBJS) -L$(BUILDDIR) -lplugin -lmad-mpegplayer -lgcc\ 45 $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) -o $@ $(OBJS) -L$(BUILDDIR) -lplugin -lmad-mpegplayer -lgcc\
46 -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/mpegplayer.map 46 $(LINKBITMAPS) -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/mpegplayer.map
47 47
48$(OUTPUT): $(OBJDIR)/mpegplayer.elf 48$(OUTPUT): $(OBJDIR)/mpegplayer.elf
49 $(call PRINTS,OBJCOPY $(@F))$(OC) -O binary $< $@ 49 $(call PRINTS,OBJCOPY $(@F))$(OC) -O binary $< $@
@@ -51,7 +51,7 @@ else
51# This is the SDL simulator version 51# This is the SDL simulator version
52 52
53$(OUTPUT): $(OBJS) $(BUILDDIR)/libmad-mpegplayer.a 53$(OUTPUT): $(OBJS) $(BUILDDIR)/libmad-mpegplayer.a
54 $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(OBJS) -L$(BUILDDIR) -lplugin -lmad-mpegplayer -o $@ 54 $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(OBJS) -L$(BUILDDIR) -lplugin $(LINKBITMAPS) -lmad-mpegplayer -o $@
55ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) 55ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN)
56# 'x' must be kept or you'll have "Win32 error 5" 56# 'x' must be kept or you'll have "Win32 error 5"
57# $ fgrep 5 /usr/include/w32api/winerror.h | head -1 57# $ fgrep 5 /usr/include/w32api/winerror.h | head -1
diff --git a/apps/plugins/mpegplayer/audio_thread.c b/apps/plugins/mpegplayer/audio_thread.c
index ee6ff05d2d..78d28e40c5 100644
--- a/apps/plugins/mpegplayer/audio_thread.c
+++ b/apps/plugins/mpegplayer/audio_thread.c
@@ -140,9 +140,8 @@ static void audio_queue_reset(void)
140 audio_queue.used = 0; 140 audio_queue.used = 0;
141 audio_queue.read = 0; 141 audio_queue.read = 0;
142 audio_queue.write = 0; 142 audio_queue.write = 0;
143 rb->memset(audio_queue.descs, 0, sizeof (audio_queue.descs));
143 audio_queue.curr = audiodesc_queue_head(); 144 audio_queue.curr = audiodesc_queue_head();
144 audio_queue.curr->time = 0;
145 audio_queue.curr->size = 0;
146} 145}
147 146
148static void audio_queue_advance_pos(ssize_t len) 147static void audio_queue_advance_pos(ssize_t len)
diff --git a/apps/plugins/mpegplayer/disk_buf.h b/apps/plugins/mpegplayer/disk_buf.h
index 90e72fac2f..79c3328535 100644
--- a/apps/plugins/mpegplayer/disk_buf.h
+++ b/apps/plugins/mpegplayer/disk_buf.h
@@ -104,6 +104,9 @@ static inline bool disk_buf_is_data_ready(struct stream_hdr *sh,
104bool disk_buf_init(void); 104bool disk_buf_init(void);
105void disk_buf_exit(void); 105void disk_buf_exit(void);
106 106
107static inline int disk_buf_status(void)
108 { return disk_buf.status; }
109
107int disk_buf_open(const char *filename); 110int disk_buf_open(const char *filename);
108void disk_buf_close(void); 111void disk_buf_close(void);
109ssize_t _disk_buf_getbuffer(size_t size, void **pp, void **pwrap, 112ssize_t _disk_buf_getbuffer(size_t size, void **pp, void **pwrap,
diff --git a/apps/plugins/mpegplayer/mpeg_parser.c b/apps/plugins/mpegplayer/mpeg_parser.c
index d4b0ff10fb..5b863dabac 100644
--- a/apps/plugins/mpegplayer/mpeg_parser.c
+++ b/apps/plugins/mpegplayer/mpeg_parser.c
@@ -1012,15 +1012,17 @@ intptr_t parser_send_video_msg(long id, intptr_t data)
1012 switch (id) 1012 switch (id)
1013 { 1013 {
1014 case VIDEO_DISPLAY_SHOW: 1014 case VIDEO_DISPLAY_SHOW:
1015 if (data != 0 && stream_status() != STREAM_PLAYING) 1015 if (data != 0 && disk_buf_status() == STREAM_STOPPED)
1016 { /* Only prepare image if showing and not playing */ 1016 { /* Only prepare image if showing and not playing */
1017 prepare_image(str_parser.last_seek_time); 1017 prepare_image(str_parser.last_seek_time);
1018 } 1018 }
1019 break; 1019 break;
1020 1020
1021 case VIDEO_PRINT_FRAME: 1021 case VIDEO_PRINT_FRAME:
1022 if (data)
1023 break;
1022 case VIDEO_PRINT_THUMBNAIL: 1024 case VIDEO_PRINT_THUMBNAIL:
1023 if (stream_status() == STREAM_PLAYING) 1025 if (disk_buf_status() != STREAM_STOPPED)
1024 break; /* Prepare image if not playing */ 1026 break; /* Prepare image if not playing */
1025 1027
1026 if (!prepare_image(str_parser.last_seek_time)) 1028 if (!prepare_image(str_parser.last_seek_time))
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c
index 03ec5ba821..e9f6b9011f 100644
--- a/apps/plugins/mpegplayer/mpegplayer.c
+++ b/apps/plugins/mpegplayer/mpegplayer.c
@@ -116,6 +116,8 @@ PLUGIN_IRAM_DECLARE
116#define MPEG_PAUSE BUTTON_ON 116#define MPEG_PAUSE BUTTON_ON
117#define MPEG_VOLDOWN BUTTON_DOWN 117#define MPEG_VOLDOWN BUTTON_DOWN
118#define MPEG_VOLUP BUTTON_UP 118#define MPEG_VOLUP BUTTON_UP
119#define MPEG_RW BUTTON_LEFT
120#define MPEG_FF BUTTON_RIGHT
119 121
120#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \ 122#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
121 (CONFIG_KEYPAD == IPOD_1G2G_PAD) 123 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
@@ -124,6 +126,8 @@ PLUGIN_IRAM_DECLARE
124#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT) 126#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
125#define MPEG_VOLDOWN BUTTON_SCROLL_BACK 127#define MPEG_VOLDOWN BUTTON_SCROLL_BACK
126#define MPEG_VOLUP BUTTON_SCROLL_FWD 128#define MPEG_VOLUP BUTTON_SCROLL_FWD
129#define MPEG_RW BUTTON_LEFT
130#define MPEG_FF BUTTON_RIGHT
127 131
128#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD 132#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
129#define MPEG_MENU (BUTTON_REC | BUTTON_REL) 133#define MPEG_MENU (BUTTON_REC | BUTTON_REL)
@@ -131,6 +135,8 @@ PLUGIN_IRAM_DECLARE
131#define MPEG_PAUSE BUTTON_PLAY 135#define MPEG_PAUSE BUTTON_PLAY
132#define MPEG_VOLDOWN BUTTON_DOWN 136#define MPEG_VOLDOWN BUTTON_DOWN
133#define MPEG_VOLUP BUTTON_UP 137#define MPEG_VOLUP BUTTON_UP
138#define MPEG_RW BUTTON_LEFT
139#define MPEG_FF BUTTON_RIGHT
134 140
135#elif CONFIG_KEYPAD == GIGABEAT_PAD 141#elif CONFIG_KEYPAD == GIGABEAT_PAD
136#define MPEG_MENU BUTTON_MENU 142#define MPEG_MENU BUTTON_MENU
@@ -141,13 +147,17 @@ PLUGIN_IRAM_DECLARE
141#define MPEG_VOLUP BUTTON_RIGHT 147#define MPEG_VOLUP BUTTON_RIGHT
142#define MPEG_VOLDOWN2 BUTTON_VOL_DOWN 148#define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
143#define MPEG_VOLUP2 BUTTON_VOL_UP 149#define MPEG_VOLUP2 BUTTON_VOL_UP
150#define MPEG_RW BUTTON_UP
151#define MPEG_FF BUTTON_DOWN
144 152
145#elif CONFIG_KEYPAD == IRIVER_H10_PAD 153#elif CONFIG_KEYPAD == IRIVER_H10_PAD
146#define MPEG_MENU (BUTTON_REW | BUTTON_REL) 154#define MPEG_MENU BUTTON_LEFT
147#define MPEG_STOP BUTTON_POWER 155#define MPEG_STOP BUTTON_POWER
148#define MPEG_PAUSE BUTTON_PLAY 156#define MPEG_PAUSE BUTTON_PLAY
149#define MPEG_VOLDOWN BUTTON_SCROLL_DOWN 157#define MPEG_VOLDOWN BUTTON_SCROLL_DOWN
150#define MPEG_VOLUP BUTTON_SCROLL_UP 158#define MPEG_VOLUP BUTTON_SCROLL_UP
159#define MPEG_RW BUTTON_REW
160#define MPEG_FF BUTTON_FF
151 161
152#elif CONFIG_KEYPAD == SANSA_E200_PAD 162#elif CONFIG_KEYPAD == SANSA_E200_PAD
153#define MPEG_MENU BUTTON_SELECT 163#define MPEG_MENU BUTTON_SELECT
@@ -155,6 +165,8 @@ PLUGIN_IRAM_DECLARE
155#define MPEG_PAUSE BUTTON_UP 165#define MPEG_PAUSE BUTTON_UP
156#define MPEG_VOLDOWN BUTTON_SCROLL_UP 166#define MPEG_VOLDOWN BUTTON_SCROLL_UP
157#define MPEG_VOLUP BUTTON_SCROLL_DOWN 167#define MPEG_VOLUP BUTTON_SCROLL_DOWN
168#define MPEG_RW BUTTON_LEFT
169#define MPEG_FF BUTTON_RIGHT
158 170
159#elif CONFIG_KEYPAD == SANSA_C200_PAD 171#elif CONFIG_KEYPAD == SANSA_C200_PAD
160#define MPEG_MENU BUTTON_SELECT 172#define MPEG_MENU BUTTON_SELECT
@@ -162,6 +174,8 @@ PLUGIN_IRAM_DECLARE
162#define MPEG_PAUSE BUTTON_UP 174#define MPEG_PAUSE BUTTON_UP
163#define MPEG_VOLDOWN BUTTON_VOL_DOWN 175#define MPEG_VOLDOWN BUTTON_VOL_DOWN
164#define MPEG_VOLUP BUTTON_VOL_UP 176#define MPEG_VOLUP BUTTON_VOL_UP
177#define MPEG_RW BUTTON_LEFT
178#define MPEG_FF BUTTON_RIGHT
165 179
166#elif CONFIG_KEYPAD == MROBE500_PAD 180#elif CONFIG_KEYPAD == MROBE500_PAD
167#define MPEG_MENU BUTTON_RC_HEART 181#define MPEG_MENU BUTTON_RC_HEART
@@ -169,6 +183,8 @@ PLUGIN_IRAM_DECLARE
169#define MPEG_PAUSE BUTTON_TOUCHPAD 183#define MPEG_PAUSE BUTTON_TOUCHPAD
170#define MPEG_VOLDOWN BUTTON_RC_VOL_DOWN 184#define MPEG_VOLDOWN BUTTON_RC_VOL_DOWN
171#define MPEG_VOLUP BUTTON_RC_VOL_UP 185#define MPEG_VOLUP BUTTON_RC_VOL_UP
186#define MPEG_RW BUTTON_RC_REW
187#define MPEG_FF BUTTON_RC_FF
172 188
173#else 189#else
174#error MPEGPLAYER: Unsupported keypad 190#error MPEGPLAYER: Unsupported keypad
@@ -179,31 +195,997 @@ struct plugin_api* rb;
179CACHE_FUNCTION_WRAPPERS(rb); 195CACHE_FUNCTION_WRAPPERS(rb);
180ALIGN_BUFFER_WRAPPER(rb); 196ALIGN_BUFFER_WRAPPER(rb);
181 197
182static bool button_loop(void) 198/* One thing we can do here for targets with remotes is having a display
199 * always on the remote instead of always forcing a popup on the main display */
200
201#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
202 /* 3% of 30min file == 54s step size */
203#define MIN_FF_REWIND_STEP (TS_SECOND/2)
204#define WVS_MIN_UPDATE_INTERVAL (HZ/2)
205
206/* WVS status - same order as icon array */
207enum wvs_status_enum
208{
209 WVS_STATUS_STOPPED = 0,
210 WVS_STATUS_PAUSED,
211 WVS_STATUS_PLAYING,
212 WVS_STATUS_FF,
213 WVS_STATUS_RW,
214 WVS_STATUS_COUNT,
215 WVS_STATUS_MASK = 0x7
216};
217
218enum wvs_bits
183{ 219{
184 bool ret = true; 220 WVS_REFRESH_DEFAULT = 0x0000,
221 WVS_REFRESH_VOLUME = 0x0001,
222 WVS_REFRESH_TIME = 0x0002,
223 WVS_REFRESH_STATUS = 0x0004,
224 WVS_REFRESH_BACKGROUND = 0x0008,
225 WVS_REFRESH_VIDEO = 0x0010,
226 WVS_REFRESH_RESUME = 0x0020,
227 WVS_NODRAW = 0x8000,
228 WVS_SHOW = 0x4000,
229 WVS_HIDE = 0x0000,
230 WVS_REFRESH_ALL = 0x001f,
231};
232
233extern const unsigned char mpegplayer_status_icons_8x8x1[];
234extern const unsigned char mpegplayer_status_icons_12x12x1[];
235extern const unsigned char mpegplayer_status_icons_16x16x1[];
236
237#define WVS_BDR_L 2
238#define WVS_BDR_T 2
239#define WVS_BDR_R 2
240#define WVS_BDR_B 2
241
242struct wvs
243{
244 long hide_tick;
245 long show_for;
246 long print_tick;
247 long print_delay;
248 long resume_tick;
249 long resume_delay;
250 long next_auto_refresh;
251 int x;
252 int y;
253 int width;
254 int height;
255 unsigned fgcolor;
256 unsigned bgcolor;
257#ifdef HAVE_LCD_COLOR
258 unsigned prog_fillcolor;
259 struct vo_rect update_rect;
260#endif
261 struct vo_rect prog_rect;
262 struct vo_rect time_rect;
263 struct vo_rect dur_rect;
264 struct vo_rect vol_rect;
265 const unsigned char *icons;
266 struct vo_rect stat_rect;
267 int status;
268 uint32_t curr_time;
269 unsigned auto_refresh;
270 unsigned flags;
271};
272
273static struct wvs wvs;
274
275static void wvs_show(unsigned show);
276
277#ifdef LCD_LANDSCAPE
278 #define _X (x + wvs.x)
279 #define _Y (y + wvs.y)
280 #define _W width
281 #define _H height
282#else
283 #define _X (LCD_WIDTH - (y + wvs.y) - height)
284 #define _Y (x + wvs.x)
285 #define _W height
286 #define _H width
287#endif
288
289#ifdef HAVE_LCD_COLOR
290static unsigned draw_blendcolor(unsigned c1, unsigned c2, unsigned char amount)
291{
292 int r1 = RGB_UNPACK_RED(c1);
293 int g1 = RGB_UNPACK_GREEN(c1);
294 int b1 = RGB_UNPACK_BLUE(c1);
295
296 int r2 = RGB_UNPACK_RED(c2);
297 int g2 = RGB_UNPACK_GREEN(c2);
298 int b2 = RGB_UNPACK_BLUE(c2);
299
300 return LCD_RGBPACK(amount*(r2 - r1) / 255 + r1,
301 amount*(g2 - g1) / 255 + g1,
302 amount*(b2 - b1) / 255 + b1);
303}
304#endif
305
306static void draw_fill_rect(int x, int y, int width, int height)
307{
308 rb->lcd_fillrect(_X, _Y, _W, _H);
309}
310
311#ifdef HAVE_LCD_COLOR
312static void draw_update_rect(int x, int y, int width, int height)
313{
314 rb->lcd_update_rect(_X, _Y, _W, _H);
315}
316#endif
317
318static void draw_clear_area(int x, int y, int width, int height)
319{
320 rb->screen_clear_area(rb->screens[SCREEN_MAIN], _X, _Y, _W, _H);
321}
322
323static void draw_clear_area_rect(const struct vo_rect *rc)
324{
325 int x = rc->l;
326 int y = rc->t;
327 int width = rc->r - rc->l;
328 int height = rc->b - rc->t;
329 rb->screen_clear_area(rb->screens[SCREEN_MAIN], _X, _Y, _W, _H);
330}
331
332static void draw_scrollbar_draw(int x, int y, int width, int height,
333 int items, int min_shown, int max_shown)
334{
335#ifdef HAVE_LCD_COLOR
336 int oldbg = rb->lcd_get_background();
337 rb->lcd_set_background(wvs.prog_fillcolor);
338#endif
339
340 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN], _X, _Y,
341 _W, _H, items, min_shown, max_shown,
342 0
343#ifdef LCD_LANDSCAPE
344 | HORIZONTAL
345#endif
346#ifdef HAVE_LCD_COLOR
347 | INNER_BGFILL | FOREGROUND
348#endif
349 );
350
351#ifdef HAVE_LCD_COLOR
352 rb->lcd_set_background(oldbg);
353#endif
354}
355
356static void draw_scrollbar_draw_rect(const struct vo_rect *rc, int items,
357 int min_shown, int max_shown)
358{
359 draw_scrollbar_draw(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t,
360 items, min_shown, max_shown);
361}
362
363static void draw_hline(int x1, int x2, int y)
364{
365#ifdef LCD_LANDSCAPE
366 rb->lcd_hline(x1 + wvs.x, x2 + wvs.x, y + wvs.y);
367#else
368 y = LCD_WIDTH - (y + wvs.y) - 1;
369 rb->lcd_vline(y, x1 + wvs.x, x2 + wvs.x);
370#endif
371}
372
373#ifdef LCD_PORTRAIT
374/* Portrait displays need rotated text rendering */
375
376/* Limited function that only renders in DRMODE_FG */
377static void draw_oriented_mono_bitmap_part(const unsigned char *src,
378 int src_x, int src_y,
379 int stride, int x, int y,
380 int width, int height)
381{
382 const unsigned char *src_end;
383 fb_data *dst, *dst_end;
384 unsigned fg_pattern, bg_pattern;
385
386 if (x + width > SCREEN_WIDTH)
387 width = SCREEN_WIDTH - x; /* Clip right */
388 if (x < 0)
389 width += x, x = 0; /* Clip left */
390 if (width <= 0)
391 return; /* nothing left to do */
392
393 if (y + height > SCREEN_HEIGHT)
394 height = SCREEN_HEIGHT - y; /* Clip bottom */
395 if (y < 0)
396 height += y, y = 0; /* Clip top */
397 if (height <= 0)
398 return; /* nothing left to do */
399
400 fg_pattern = rb->lcd_get_foreground();
401 bg_pattern = rb->lcd_get_background();
402
403 src += stride * (src_y >> 3) + src_x; /* move starting point */
404 src_y &= 7;
405 src_end = src + width;
406
407 dst = rb->lcd_framebuffer + (LCD_WIDTH - y) + x*LCD_WIDTH;
408 do
409 {
410 const unsigned char *src_col = src++;
411 unsigned data = *src_col >> src_y;
412 int numbits = 8 - src_y;
413
414 fb_data *dst_col = dst;
415 dst_end = dst_col - height;
416 dst += LCD_WIDTH;
417
418 do
419 {
420 dst_col--;
421
422 if (data & 1)
423 *dst_col = fg_pattern;
424#if 0
425 else
426 *dst_col = bg_pattern;
427#endif
428 data >>= 1;
429 if (--numbits == 0) {
430 src_col += stride;
431 data = *src_col;
432 numbits = 8;
433 }
434 }
435 while (dst_col > dst_end);
436 }
437 while (src < src_end);
438}
439
440static void draw_putsxy_oriented(int x, int y, const char *str)
441{
442 unsigned short ch;
443 unsigned short *ucs;
444 int ofs = MIN(x, 0);
445 struct font* pf = rb->font_get(FONT_UI);
446
447 ucs = rb->bidi_l2v(str, 1);
448
449 x += wvs.x;
450 y += wvs.y;
451
452 while ((ch = *ucs++) != 0 && x < SCREEN_WIDTH)
453 {
454 int width;
455 const unsigned char *bits;
456
457 /* get proportional width and glyph bits */
458 width = rb->font_get_width(pf, ch);
459
460 if (ofs > width) {
461 ofs -= width;
462 continue;
463 }
464
465 bits = rb->font_get_bits(pf, ch);
466
467 draw_oriented_mono_bitmap_part(bits, ofs, 0, width, x, y,
468 width - ofs, pf->height);
469
470 x += width - ofs;
471 ofs = 0;
472 }
473}
474#else
475static void draw_oriented_mono_bitmap_part(const unsigned char *src,
476 int src_x, int src_y,
477 int stride, int x, int y,
478 int width, int height)
479{
480 int mode = rb->lcd_get_drawmode();
481 rb->lcd_set_drawmode(DRMODE_FG);
482 rb->lcd_mono_bitmap_part(src, src_x, src_y, stride, x, y, width, height);
483 rb->lcd_set_drawmode(mode);
484}
485
486static void draw_putsxy_oriented(int x, int y, const char *str)
487{
488 int mode = rb->lcd_get_drawmode();
489 rb->lcd_set_drawmode(DRMODE_FG);
490 rb->lcd_putsxy(x + wvs.x, y + wvs.y, str);
491 rb->lcd_set_drawmode(mode);
492}
493#endif /* LCD_PORTRAIT */
494
495
496static void wvs_text_init(void)
497{
498 struct hms hms;
499 char buf[32];
500 int phys;
501 int spc_width;
502
503 rb->lcd_setfont(FONT_UI);
504
505 wvs.x = 0;
506 wvs.width = SCREEN_WIDTH;
507
508 vo_rect_clear(&wvs.time_rect);
509 vo_rect_clear(&wvs.stat_rect);
510 vo_rect_clear(&wvs.prog_rect);
511 vo_rect_clear(&wvs.vol_rect);
512
513 ts_to_hms(stream_get_duration(), &hms);
514 hms_format(buf, sizeof (buf), &hms);
515 rb->lcd_getstringsize(buf, &wvs.time_rect.r,
516 &wvs.time_rect.b);
517
518 /* Choose well-sized bitmap images relative to font height */
519 if (wvs.time_rect.b < 12) {
520 wvs.icons = mpegplayer_status_icons_8x8x1;
521 wvs.stat_rect.r = wvs.stat_rect.b = 8;
522 } else if (wvs.time_rect.b < 16) {
523 wvs.icons = mpegplayer_status_icons_12x12x1;
524 wvs.stat_rect.r = wvs.stat_rect.b = 12;
525 } else {
526 wvs.icons = mpegplayer_status_icons_16x16x1;
527 wvs.stat_rect.r = wvs.stat_rect.b = 16;
528 }
529
530 if (wvs.stat_rect.b < wvs.time_rect.b) {
531 vo_rect_offset(&wvs.stat_rect, 0,
532 (wvs.time_rect.b - wvs.stat_rect.b) / 2 + WVS_BDR_T);
533 vo_rect_offset(&wvs.time_rect, WVS_BDR_L, WVS_BDR_T);
534 } else {
535 vo_rect_offset(&wvs.time_rect, WVS_BDR_L,
536 wvs.stat_rect.b - wvs.time_rect.b + WVS_BDR_T);
537 vo_rect_offset(&wvs.stat_rect, 0, WVS_BDR_T);
538 }
539
540 wvs.dur_rect = wvs.time_rect;
541
542 phys = rb->sound_val2phys(SOUND_VOLUME, rb->sound_min(SOUND_VOLUME));
543 rb->snprintf(buf, sizeof(buf), "%d%s", phys,
544 rb->sound_unit(SOUND_VOLUME));
545
546 rb->lcd_getstringsize(" ", &spc_width, NULL);
547 rb->lcd_getstringsize(buf, &wvs.vol_rect.r, &wvs.vol_rect.b);
548
549 wvs.prog_rect.r = SCREEN_WIDTH - WVS_BDR_L - spc_width -
550 wvs.vol_rect.r - WVS_BDR_R;
551 wvs.prog_rect.b = 3*wvs.stat_rect.b / 4;
552 vo_rect_offset(&wvs.prog_rect, wvs.time_rect.l,
553 wvs.time_rect.b);
554
555 vo_rect_offset(&wvs.stat_rect,
556 (wvs.prog_rect.r + wvs.prog_rect.l - wvs.stat_rect.r) / 2,
557 0);
558
559 vo_rect_offset(&wvs.dur_rect,
560 wvs.prog_rect.r - wvs.dur_rect.r, 0);
561
562 vo_rect_offset(&wvs.vol_rect, wvs.prog_rect.r + spc_width,
563 (wvs.prog_rect.b + wvs.prog_rect.t - wvs.vol_rect.b) / 2);
564
565 wvs.height = WVS_BDR_T + MAX(wvs.prog_rect.b, wvs.vol_rect.b) -
566 MIN(wvs.time_rect.t, wvs.stat_rect.t) + WVS_BDR_B;
567
568#if LCD_PIXELFORMAT == VERTICAL_PACKING
569 wvs.height = ALIGN_UP(wvs.height, 8);
570#else
571 wvs.height = ALIGN_UP(wvs.height, 2);
572#endif
573 wvs.y = SCREEN_HEIGHT - wvs.height;
185 574
186 rb->lcd_setfont(FONT_SYSFIXED); 575 rb->lcd_setfont(FONT_SYSFIXED);
576}
577
578static void wvs_init(void)
579{
580 wvs.flags = 0;
581 wvs.show_for = HZ*4;
582 wvs.print_delay = 75*HZ/100;
583 wvs.resume_delay = HZ/2;
584#ifdef HAVE_LCD_COLOR
585 wvs.bgcolor = LCD_RGBPACK(0x73, 0x75, 0xbd);
586 wvs.fgcolor = LCD_WHITE;
587 wvs.prog_fillcolor = LCD_BLACK;
588#else
589 wvs.bgcolor = LCD_LIGHTGRAY;
590 wvs.fgcolor = LCD_BLACK;
591#endif
592 wvs.curr_time = 0;
593 wvs.status = WVS_STATUS_STOPPED;
594 wvs.auto_refresh = WVS_REFRESH_TIME;
595 wvs.next_auto_refresh = *rb->current_tick;
596 wvs_text_init();
597}
598
599static void wvs_schedule_refresh(unsigned refresh)
600{
601 long tick = *rb->current_tick;
602
603 if (refresh & WVS_REFRESH_VIDEO)
604 wvs.print_tick = tick + wvs.print_delay;
605
606 if (refresh & WVS_REFRESH_RESUME)
607 wvs.resume_tick = tick + wvs.resume_delay;
608
609 wvs.auto_refresh |= refresh;
610}
611
612static void wvs_cancel_refresh(unsigned refresh)
613{
614 wvs.auto_refresh &= ~refresh;
615}
616
617static void wvs_refresh_background(void)
618{
619 char buf[32];
620 struct hms hms;
621
622 int bg = rb->lcd_get_background();
623 rb->lcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
624
625#ifdef HAVE_LCD_COLOR
626 rb->lcd_set_background(draw_blendcolor(bg, LCD_WHITE, 192));
627 draw_hline(0, wvs.width, 0);
628
629 rb->lcd_set_background(draw_blendcolor(bg, LCD_WHITE, 80));
630 draw_hline(0, wvs.width, 1);
631
632 rb->lcd_set_background(draw_blendcolor(bg, LCD_BLACK, 48));
633 draw_hline(0, wvs.width, wvs.height-2);
634
635 rb->lcd_set_background(draw_blendcolor(bg, LCD_BLACK, 128));
636 draw_hline(0, wvs.width, wvs.height-1);
637
638 rb->lcd_set_background(bg);
639 draw_clear_area(0, 2, wvs.width, wvs.height - 4);
640
641 vo_rect_set_ext(&wvs.update_rect, 0, 0, wvs.width, wvs.height);
642#else
643 rb->lcd_set_background(LCD_DARKGRAY);
644 draw_hline(0, wvs.width, 0);
645
646 rb->lcd_set_background(bg);
647 draw_clear_area(0, 1, wvs.width, wvs.height - 1);
648#endif
649
650 rb->lcd_set_drawmode(DRMODE_SOLID);
651
652 if (stream_get_duration() != INVALID_TIMESTAMP) {
653 /* Don't know the duration */
654 ts_to_hms(stream_get_duration(), &hms);
655 hms_format(buf, sizeof (buf), &hms);
656 draw_putsxy_oriented(wvs.dur_rect.l, wvs.dur_rect.t, buf);
657 }
658}
659
660static void wvs_refresh_time(void)
661{
662 char buf[32];
663 struct hms hms;
664
665 uint32_t duration = stream_get_duration();
666
667 draw_scrollbar_draw_rect(&wvs.prog_rect, duration, 0,
668 wvs.curr_time);
669
670 ts_to_hms(wvs.curr_time, &hms);
671 hms_format(buf, sizeof (buf), &hms);
672
673 draw_clear_area_rect(&wvs.time_rect);
674 draw_putsxy_oriented(wvs.time_rect.l, wvs.time_rect.t, buf);
675
676#ifdef HAVE_LCD_COLOR
677 vo_rect_union(&wvs.update_rect, &wvs.update_rect,
678 &wvs.prog_rect);
679 vo_rect_union(&wvs.update_rect, &wvs.update_rect,
680 &wvs.time_rect);
681#endif
682}
683
684static void wvs_refresh_volume(void)
685{
686 char buf[32];
687 int width;
688
689 int volume = rb->global_settings->volume;
690 rb->snprintf(buf, sizeof (buf), "%d%s",
691 rb->sound_val2phys(SOUND_VOLUME, volume),
692 rb->sound_unit(SOUND_VOLUME));
693 rb->lcd_getstringsize(buf, &width, NULL);
694
695 /* Right-justified */
696 draw_clear_area_rect(&wvs.vol_rect);
697 draw_putsxy_oriented(wvs.vol_rect.r - width, wvs.vol_rect.t, buf);
698
699#ifdef HAVE_LCD_COLOR
700 vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.vol_rect);
701#endif
702}
703
704static void wvs_refresh_status(void)
705{
706 int icon_size = wvs.stat_rect.r - wvs.stat_rect.l;
707
708 draw_clear_area_rect(&wvs.stat_rect);
709
710#ifdef HAVE_LCD_COLOR
711 /* Draw status icon with a drop shadow */
712 unsigned oldfg = rb->lcd_get_foreground();
713 int i = 1;
714
715 rb->lcd_set_foreground(draw_blendcolor(rb->lcd_get_background(),
716 LCD_BLACK, 96));
717
718 while (1)
719 {
720 draw_oriented_mono_bitmap_part(wvs.icons,
721 icon_size*wvs.status,
722 0,
723 icon_size*WVS_STATUS_COUNT,
724 wvs.stat_rect.l + wvs.x + i,
725 wvs.stat_rect.t + wvs.y + i,
726 icon_size, icon_size);
727
728 if (--i < 0)
729 break;
730
731 rb->lcd_set_foreground(oldfg);
732 }
733
734 vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.stat_rect);
735#else
736 draw_oriented_mono_bitmap_part(wvs.icons,
737 icon_size*wvs.status,
738 0,
739 icon_size*WVS_STATUS_COUNT,
740 wvs.stat_rect.l + wvs.x,
741 wvs.stat_rect.t + wvs.y,
742 icon_size, icon_size);
743#endif
744}
745
746static bool wvs_update_status(void)
747{
748 int status;
749
750 switch (stream_status())
751 {
752 default:
753 status = WVS_STATUS_STOPPED;
754 break;
755 case STREAM_PAUSED:
756 status = (wvs.auto_refresh & WVS_REFRESH_RESUME) ?
757 WVS_STATUS_PLAYING : WVS_STATUS_PAUSED;
758 break;
759 case STREAM_PLAYING:
760 status = WVS_STATUS_PLAYING;
761 break;
762 }
763
764 if (status != wvs.status) {
765 wvs.status = status;
766 return true;
767 }
768
769 return false;
770}
771
772static void wvs_update_time(void)
773{
774 uint32_t start;
775 wvs.curr_time = stream_get_seek_time(&start);
776 wvs.curr_time -= start;
777}
778
779static void wvs_refresh(int hint)
780{
781 long tick;
782 unsigned oldbg, oldfg;
783
784 tick = *rb->current_tick;
785
786 if (hint == WVS_REFRESH_DEFAULT) {
787
788 if ((wvs.auto_refresh & WVS_REFRESH_VIDEO) &&
789 TIME_AFTER(tick, wvs.print_tick)) {
790 wvs.auto_refresh &= ~WVS_REFRESH_VIDEO;
791 stream_draw_frame(false);
792 }
793
794 if ((wvs.auto_refresh & WVS_REFRESH_RESUME) &&
795 TIME_AFTER(tick, wvs.resume_tick)) {
796 wvs.auto_refresh &= ~(WVS_REFRESH_RESUME | WVS_REFRESH_VIDEO);
797 stream_resume();
798 }
799
800 if (!(wvs.flags & WVS_SHOW))
801 return;
802
803 if (TIME_AFTER(tick, wvs.hide_tick)) {
804 wvs_show(WVS_HIDE);
805 return;
806 }
807 } else {
808 if (!(wvs.flags & WVS_SHOW)) {
809 wvs_show(WVS_SHOW | WVS_NODRAW);
810 hint = WVS_REFRESH_ALL;
811 }
812
813 wvs.print_tick = tick + wvs.print_delay;
814 wvs.hide_tick = tick + wvs.show_for;
815 }
816
817 if (TIME_AFTER(tick, wvs.next_auto_refresh)) {
818 wvs.next_auto_refresh = tick + WVS_MIN_UPDATE_INTERVAL;
819
820 if (wvs.auto_refresh & WVS_REFRESH_STATUS) {
821 if (wvs_update_status())
822 hint |= WVS_REFRESH_STATUS;
823 }
824
825 if (wvs.auto_refresh & WVS_REFRESH_TIME) {
826 wvs_update_time();
827 hint |= WVS_REFRESH_TIME;
828 }
829 }
830
831 if (hint == 0)
832 return;
833
834 oldfg = rb->lcd_get_foreground();
835 oldbg = rb->lcd_get_background();
836
837 rb->lcd_setfont(FONT_UI);
838 rb->lcd_set_foreground(wvs.fgcolor);
839 rb->lcd_set_background(wvs.bgcolor);
840
841#ifdef HAVE_LCD_COLOR
842 vo_rect_clear(&wvs.update_rect);
843#endif
844
845 if (hint & WVS_REFRESH_BACKGROUND) {
846 wvs_refresh_background();
847 hint |= WVS_REFRESH_ALL; /* Requires a redraw of everything */
848 }
849
850 if (hint & WVS_REFRESH_TIME) {
851 wvs_refresh_time();
852 }
853
854 if (hint & WVS_REFRESH_VOLUME) {
855 wvs_refresh_volume();
856 }
857
858 if (hint & WVS_REFRESH_STATUS) {
859 wvs_refresh_status();
860 }
861
862 rb->lcd_setfont(FONT_SYSFIXED);
863 rb->lcd_set_foreground(oldfg);
864 rb->lcd_set_background(oldbg);
865
866#ifdef HAVE_LCD_COLOR
867 vo_lock();
868
869 draw_update_rect(wvs.update_rect.l,
870 wvs.update_rect.t,
871 wvs.update_rect.r - wvs.update_rect.l,
872 wvs.update_rect.b - wvs.update_rect.t);
873
874 vo_unlock();
875#else
876 gray_deferred_lcd_update();
877#endif
878}
879
880static void wvs_show(unsigned show)
881{
882 if (((show ^ wvs.flags) & WVS_SHOW) == 0)
883 return;
884
885 if (show & WVS_SHOW) {
886 struct vo_rect rc = { 0, 0, SCREEN_WIDTH, wvs.y };
887
888 wvs.flags |= WVS_SHOW;
889
890 stream_vo_set_clip(&rc);
891
892 if (!(show & WVS_NODRAW))
893 wvs_refresh(WVS_REFRESH_ALL);
894 } else {
895 wvs.flags &= ~WVS_SHOW;
896
897 stream_vo_set_clip(NULL);
898
899 draw_clear_area(0, 0, wvs.width, wvs.height);
900
901 if (!(show & WVS_NODRAW)) {
902#ifdef HAVE_LCD_COLOR
903 vo_lock();
904 draw_update_rect(0, 0, wvs.width, wvs.height);
905 vo_unlock();
906#endif
907 stream_draw_frame(false);
908 }
909 }
910}
911
912static void wvs_set_status(int status)
913{
914 bool draw = (status & WVS_NODRAW) == 0;
915
916 status &= WVS_STATUS_MASK;
917
918 if (wvs.status != status) {
919
920 wvs.status = status;
921
922 if (draw)
923 wvs_refresh(WVS_REFRESH_STATUS);
924 }
925}
926
927/* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;) */
928static uint32_t wvs_ff_rw(int btn, unsigned refresh)
929{
930 unsigned int step = TS_SECOND*rb->global_settings->ff_rewind_min_step;
931 const long ff_rw_accel = rb->global_settings->ff_rewind_accel;
932 long accel_tick = *rb->current_tick + ff_rw_accel*HZ;
933 uint32_t start;
934 uint32_t time = stream_get_seek_time(&start);
935 const uint32_t duration = stream_get_duration();
936 unsigned int max_step = 0;
937 uint32_t ff_rw_count = 0;
938 unsigned status = wvs.status;
939
940 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME |
941 WVS_REFRESH_TIME);
942
943 time -= start; /* Absolute clock => stream-relative */
944
945 switch (btn)
946 {
947 case MPEG_FF:
948 wvs_set_status(WVS_STATUS_FF);
949 break;
950 case MPEG_RW:
951 wvs_set_status(WVS_STATUS_RW);
952 break;
953 default:
954 btn = -1;
955 }
956
957 btn |= BUTTON_REPEAT;
958
959 while (1)
960 {
961 long tick = *rb->current_tick;
962 stream_keep_disk_active();
963
964 switch (btn)
965 {
966 case BUTTON_NONE:
967 wvs_refresh(WVS_REFRESH_DEFAULT);
968 break;
969
970 case MPEG_FF | BUTTON_REPEAT:
971 case MPEG_RW | BUTTON_REPEAT:
972 break;
973
974 case MPEG_FF | BUTTON_REL:
975 case MPEG_RW | BUTTON_REL:
976 if (wvs.status == WVS_STATUS_FF)
977 time += ff_rw_count;
978 else if (wvs.status == WVS_STATUS_RW)
979 time -= ff_rw_count;
980
981 /* Fall-through */
982 case -1:
983 default:
984 wvs_schedule_refresh(refresh);
985 wvs_set_status(status);
986 wvs_schedule_refresh(WVS_REFRESH_TIME);
987 return time;
988 }
989
990 if (wvs.status == WVS_STATUS_FF) {
991 /* fast forwarding, calc max step relative to end */
992 max_step = muldiv_uint32(duration - (time + ff_rw_count),
993 FF_REWIND_MAX_PERCENT, 100);
994 } else {
995 /* rewinding, calc max step relative to start */
996 max_step = muldiv_uint32(time - ff_rw_count,
997 FF_REWIND_MAX_PERCENT, 100);
998 }
999
1000 max_step = MAX(max_step, MIN_FF_REWIND_STEP);
1001
1002 if (step > max_step)
1003 step = max_step;
1004
1005 ff_rw_count += step;
1006
1007 if (ff_rw_accel != 0 && TIME_AFTER(tick, accel_tick)) {
1008 step *= 2;
1009 accel_tick = tick + ff_rw_accel*HZ;
1010 }
1011
1012 if (wvs.status == WVS_STATUS_FF) {
1013 if (duration - time <= ff_rw_count)
1014 ff_rw_count = duration - time;
1015
1016 wvs.curr_time = time + ff_rw_count;
1017 } else {
1018 if (time <= ff_rw_count)
1019 ff_rw_count = time;
1020
1021 wvs.curr_time = time - ff_rw_count;
1022 }
1023
1024 wvs_refresh(WVS_REFRESH_TIME);
1025
1026 btn = rb->button_get_w_tmo(WVS_MIN_UPDATE_INTERVAL);
1027 }
1028}
1029
1030static int wvs_status(void)
1031{
1032 int status = stream_status();
1033
1034 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1035 if (status == STREAM_PAUSED) {
1036 if (wvs.auto_refresh & WVS_REFRESH_RESUME)
1037 status = STREAM_PLAYING;
1038 }
1039
1040 return status;
1041}
1042
1043static void wvs_set_volume(int delta)
1044{
1045 int vol = rb->global_settings->volume;
1046 int limit;
1047
1048 vol += delta;
1049
1050 if (delta < 0) {
1051 limit = rb->sound_min(SOUND_VOLUME);
1052 if (vol < limit)
1053 vol = limit;
1054 } else {
1055 limit = rb->sound_max(SOUND_VOLUME);
1056 if (vol > limit)
1057 vol = limit;
1058 }
1059
1060 if (vol != rb->global_settings->volume) {
1061 rb->sound_set(SOUND_VOLUME, vol);
1062 rb->global_settings->volume = vol;
1063 }
1064
1065 wvs_refresh(WVS_REFRESH_VOLUME);
1066}
1067
1068static int wvs_play(uint32_t time)
1069{
1070 int retval;
1071
1072 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1073
1074 retval = stream_seek(time, SEEK_SET);
1075
1076 if (retval >= STREAM_OK) {
1077 stream_show_vo(true);
1078 retval = stream_play();
1079
1080 if (retval >= STREAM_OK)
1081 wvs_set_status(WVS_STATUS_PLAYING | WVS_NODRAW);
1082 }
1083
1084 return retval;
1085}
1086
1087static int wvs_halt(void)
1088{
1089 int status = stream_pause();
1090
1091 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1092 if (status == STREAM_PAUSED) {
1093 if (wvs.auto_refresh & WVS_REFRESH_RESUME)
1094 status = STREAM_PLAYING;
1095 }
1096
1097 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1098
1099 return status;
1100}
1101
1102static int wvs_pause(void)
1103{
1104 unsigned refresh = wvs.auto_refresh;
1105 int status = wvs_halt();
1106
1107 if (status == STREAM_PLAYING && (refresh & WVS_REFRESH_RESUME)) {
1108 wvs_cancel_refresh(WVS_REFRESH_RESUME);
1109 wvs_schedule_refresh(WVS_REFRESH_VIDEO);
1110 }
1111
1112 wvs_set_status(WVS_STATUS_PAUSED);
1113
1114 return status;
1115}
1116
1117static void wvs_resume(void)
1118{
1119 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1120 wvs_set_status(WVS_STATUS_PLAYING);
1121 stream_resume();
1122}
1123
1124static void wvs_stop(void)
1125{
1126 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1127 wvs_show(WVS_HIDE | WVS_NODRAW);
1128
1129 if (stream_stop() != STREAM_STOPPED)
1130 settings.resume_time = stream_get_resume_time();
1131}
1132
1133static void wvs_seek(int btn)
1134{
1135 int status;
1136 unsigned refresh;
1137 uint32_t time;
1138
1139 if (!stream_can_seek())
1140 return;
1141
1142 status = wvs_halt();
1143
1144 if (status == STREAM_STOPPED)
1145 return;
1146
1147 wvs_show(WVS_SHOW);
1148
1149 if (status == STREAM_PLAYING)
1150 refresh = WVS_REFRESH_RESUME; /* delay resume if playing */
1151 else
1152 refresh = WVS_REFRESH_VIDEO; /* refresh if paused */
1153
1154 time = wvs_ff_rw(btn, refresh);
1155
1156 stream_seek(time, SEEK_SET);
1157}
1158
1159static void button_loop(void)
1160{
1161 rb->lcd_setfont(FONT_SYSFIXED);
187 rb->lcd_clear_display(); 1162 rb->lcd_clear_display();
188 rb->lcd_update(); 1163 rb->lcd_update();
189 1164
1165 /* Turn off backlight timeout */
1166 /* backlight control in lib/helper.c */
1167 backlight_force_on(rb);
1168
1169 wvs_init();
1170
190 /* Start playback at the specified starting time */ 1171 /* Start playback at the specified starting time */
191 if (stream_seek(settings.resume_time, SEEK_SET) < STREAM_OK || 1172 if (wvs_play(settings.resume_time) < STREAM_OK) {
192 (stream_show_vo(true), stream_play()) < STREAM_OK)
193 {
194 rb->splash(HZ*2, "Playback failed"); 1173 rb->splash(HZ*2, "Playback failed");
195 return false; 1174 return;
196 } 1175 }
197 1176
198 /* Gently poll the video player for EOS and handle UI */ 1177 /* Gently poll the video player for EOS and handle UI */
199 while (stream_status() != STREAM_STOPPED) 1178 while (stream_status() != STREAM_STOPPED)
200 { 1179 {
201 int button = rb->button_get_w_tmo(HZ/2); 1180 int button = rb->button_get_w_tmo(WVS_MIN_UPDATE_INTERVAL);
202 1181
203 switch (button) 1182 switch (button)
204 { 1183 {
205 case BUTTON_NONE: 1184 case BUTTON_NONE:
1185 {
1186 wvs_refresh(WVS_REFRESH_DEFAULT);
206 continue; 1187 continue;
1188 } /* BUTTON_NONE: */
207 1189
208 case MPEG_VOLUP: 1190 case MPEG_VOLUP:
209 case MPEG_VOLUP|BUTTON_REPEAT: 1191 case MPEG_VOLUP|BUTTON_REPEAT:
@@ -212,14 +1194,7 @@ static bool button_loop(void)
212 case MPEG_VOLUP2|BUTTON_REPEAT: 1194 case MPEG_VOLUP2|BUTTON_REPEAT:
213#endif 1195#endif
214 { 1196 {
215 int vol = rb->global_settings->volume; 1197 wvs_set_volume(+1);
216 int maxvol = rb->sound_max(SOUND_VOLUME);
217
218 if (vol < maxvol) {
219 vol++;
220 rb->sound_set(SOUND_VOLUME, vol);
221 rb->global_settings->volume = vol;
222 }
223 break; 1198 break;
224 } /* MPEG_VOLUP*: */ 1199 } /* MPEG_VOLUP*: */
225 1200
@@ -230,23 +1205,17 @@ static bool button_loop(void)
230 case MPEG_VOLDOWN2|BUTTON_REPEAT: 1205 case MPEG_VOLDOWN2|BUTTON_REPEAT:
231#endif 1206#endif
232 { 1207 {
233 int vol = rb->global_settings->volume; 1208 wvs_set_volume(-1);
234 int minvol = rb->sound_min(SOUND_VOLUME);
235
236 if (vol > minvol) {
237 vol--;
238 rb->sound_set(SOUND_VOLUME, vol);
239 rb->global_settings->volume = vol;
240 }
241 break; 1209 break;
242 } /* MPEG_VOLDOWN*: */ 1210 } /* MPEG_VOLDOWN*: */
243 1211
244 case MPEG_MENU: 1212 case MPEG_MENU:
245 { 1213 {
246 int state = stream_pause(); /* save previous state */ 1214 int state = wvs_halt(); /* save previous state */
247 int result; 1215 int result;
248 1216
249 /* Hide video output */ 1217 /* Hide video output */
1218 wvs_show(WVS_HIDE | WVS_NODRAW);
250 stream_show_vo(false); 1219 stream_show_vo(false);
251 backlight_use_settings(rb); 1220 backlight_use_settings(rb);
252 1221
@@ -258,17 +1227,19 @@ static bool button_loop(void)
258 switch (result) 1227 switch (result)
259 { 1228 {
260 case MPEG_MENU_QUIT: 1229 case MPEG_MENU_QUIT:
261 stream_stop(); 1230 wvs_stop();
262 break; 1231 break;
263 default: 1232 default:
264 /* If not stopped, show video again */ 1233 /* If not stopped, show video again */
265 if (state != STREAM_STOPPED) 1234 if (state != STREAM_STOPPED) {
1235 wvs_show(WVS_SHOW);
266 stream_show_vo(true); 1236 stream_show_vo(true);
1237 }
267 1238
268 /* If stream was playing, restart it */ 1239 /* If stream was playing, restart it */
269 if (state == STREAM_PLAYING) { 1240 if (state == STREAM_PLAYING) {
270 backlight_force_on(rb); 1241 backlight_force_on(rb);
271 stream_resume(); 1242 wvs_resume();
272 } 1243 }
273 break; 1244 break;
274 } 1245 }
@@ -277,7 +1248,7 @@ static bool button_loop(void)
277 1248
278 case MPEG_STOP: 1249 case MPEG_STOP:
279 { 1250 {
280 stream_stop(); 1251 wvs_stop();
281 break; 1252 break;
282 } /* MPEG_STOP: */ 1253 } /* MPEG_STOP: */
283 1254
@@ -286,27 +1257,34 @@ static bool button_loop(void)
286 case MPEG_PAUSE2: 1257 case MPEG_PAUSE2:
287#endif 1258#endif
288 { 1259 {
289 if (stream_status() == STREAM_PLAYING) { 1260 int status = wvs_status();
1261
1262 if (status == STREAM_PLAYING) {
290 /* Playing => Paused */ 1263 /* Playing => Paused */
291 stream_pause(); 1264 wvs_pause();
292 backlight_use_settings(rb); 1265 backlight_use_settings(rb);
293 } 1266 }
294 else if (stream_status() == STREAM_PAUSED) { 1267 else if (status == STREAM_PAUSED) {
295 /* Paused => Playing */ 1268 /* Paused => Playing */
296 backlight_force_on(rb); 1269 backlight_force_on(rb);
297 stream_resume(); 1270 wvs_resume();
298 } 1271 }
299 1272
300 break; 1273 break;
301 } /* MPEG_PAUSE*: */ 1274 } /* MPEG_PAUSE*: */
302 1275
1276 case MPEG_RW:
1277 case MPEG_FF:
1278 {
1279 wvs_seek(button);
1280 break;
1281 } /* MPEG_RW: MPEG_FF: */
1282
303 case SYS_POWEROFF: 1283 case SYS_POWEROFF:
304 case SYS_USB_CONNECTED: 1284 case SYS_USB_CONNECTED:
305 /* Stop and get the resume time before closing the file early */ 1285 /* Stop and get the resume time before closing the file early */
306 stream_stop(); 1286 wvs_stop();
307 settings.resume_time = stream_get_resume_time();
308 stream_close(); 1287 stream_close();
309 ret = false;
310 /* Fall-through */ 1288 /* Fall-through */
311 default: 1289 default:
312 rb->default_event_handler(button); 1290 rb->default_event_handler(button);
@@ -316,9 +1294,12 @@ static bool button_loop(void)
316 rb->yield(); 1294 rb->yield();
317 } /* end while */ 1295 } /* end while */
318 1296
1297 wvs_stop();
1298
319 rb->lcd_setfont(FONT_UI); 1299 rb->lcd_setfont(FONT_UI);
320 1300
321 return ret; 1301 /* Turn on backlight timeout (revert to settings) */
1302 backlight_use_settings(rb);
322} 1303}
323 1304
324enum plugin_status plugin_start(struct plugin_api* api, void* parameter) 1305enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
@@ -367,17 +1348,8 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
367 result = mpeg_start_menu(stream_get_duration()); 1348 result = mpeg_start_menu(stream_get_duration());
368 1349
369 if (result != MPEG_START_QUIT) { 1350 if (result != MPEG_START_QUIT) {
370 /* Turn off backlight timeout */
371 /* backlight control in lib/helper.c */
372 backlight_force_on(rb);
373
374 /* Enter button loop and process UI */ 1351 /* Enter button loop and process UI */
375 if (button_loop()) { 1352 button_loop();
376 settings.resume_time = stream_get_resume_time();
377 }
378
379 /* Turn on backlight timeout (revert to settings) */
380 backlight_use_settings(rb);
381 } 1353 }
382 1354
383 stream_close(); 1355 stream_close();
diff --git a/apps/plugins/mpegplayer/stream_mgr.c b/apps/plugins/mpegplayer/stream_mgr.c
index 06f269ca31..1baa2032ec 100644
--- a/apps/plugins/mpegplayer/stream_mgr.c
+++ b/apps/plugins/mpegplayer/stream_mgr.c
@@ -251,6 +251,7 @@ static void set_stream_clock(uint32_t time)
251static uint32_t time_from_whence(uint32_t time, int whence) 251static uint32_t time_from_whence(uint32_t time, int whence)
252{ 252{
253 int64_t currtime; 253 int64_t currtime;
254 uint32_t start;
254 255
255 switch (whence) 256 switch (whence)
256 { 257 {
@@ -262,12 +263,8 @@ static uint32_t time_from_whence(uint32_t time, int whence)
262 case SEEK_CUR: 263 case SEEK_CUR:
263 /* Seek forward or backward from the current time 264 /* Seek forward or backward from the current time
264 * (time = signed offset from current) */ 265 * (time = signed offset from current) */
265 if (stream_mgr.seeked) 266 currtime = stream_get_seek_time(&start);
266 currtime = str_parser.last_seek_time; 267 currtime -= start;
267 else
268 currtime = TICKS_TO_TS(pcm_output_get_clock());
269
270 currtime -= str_parser.start_pts;
271 currtime += (int32_t)time; 268 currtime += (int32_t)time;
272 269
273 if (currtime < 0) 270 if (currtime < 0)
@@ -525,27 +522,28 @@ static void stream_on_stop(bool reply)
525 522
526 if (status != STREAM_STOPPED) 523 if (status != STREAM_STOPPED)
527 { 524 {
528 /* Not stopped = paused or playing */
529 stream_mgr.seeked = false;
530
531 /* Pause the clock */ 525 /* Pause the clock */
532 pcm_output_play_pause(false); 526 pcm_output_play_pause(false);
533 527
528 /* Assume invalidity */
529 stream_mgr.resume_time = 0;
530
534 if (stream_can_seek()) 531 if (stream_can_seek())
535 { 532 {
536 /* Read the current stream time */ 533 /* Read the current stream time or the last seeked position */
537 uint32_t time = TICKS_TO_TS(pcm_output_get_clock()); 534 uint32_t start;
538 535 uint32_t time = stream_get_seek_time(&start);
539 /* Assume invalidity */
540 stream_mgr.resume_time = 0;
541 536
542 if (time >= str_parser.start_pts && time < str_parser.end_pts) 537 if (time >= str_parser.start_pts && time < str_parser.end_pts)
543 { 538 {
544 /* Save the current stream time */ 539 /* Save the current stream time */
545 stream_mgr.resume_time = time - str_parser.start_pts; 540 stream_mgr.resume_time = time - start;
546 } 541 }
547 } 542 }
548 543
544 /* Not stopped = paused or playing */
545 stream_mgr.seeked = false;
546
549 /* Stop buffering */ 547 /* Stop buffering */
550 disk_buf_send_msg(STREAM_STOP, 0); 548 disk_buf_send_msg(STREAM_STOP, 0);
551 549
@@ -578,10 +576,10 @@ static void stream_on_seek(struct stream_seek_data *skd)
578 if (stream_mgr.filename == NULL) 576 if (stream_mgr.filename == NULL)
579 break; 577 break;
580 578
581 stream_mgr_reply_msg(STREAM_OK);
582
583 stream_keep_disk_active(); 579 stream_keep_disk_active();
584 580
581 stream_mgr_reply_msg(STREAM_OK);
582
585 stream_mgr_lock(); 583 stream_mgr_lock();
586 584
587 if (!stream_can_seek()) 585 if (!stream_can_seek())
@@ -705,9 +703,11 @@ bool stream_show_vo(bool show)
705 703
706 vis = parser_send_video_msg(VIDEO_DISPLAY_SHOW, show); 704 vis = parser_send_video_msg(VIDEO_DISPLAY_SHOW, show);
707#ifndef HAVE_LCD_COLOR 705#ifndef HAVE_LCD_COLOR
708 GRAY_VIDEO_FLUSH_ICACHE(); 706 GRAY_VIDEO_INVALIDATE_ICACHE();
709 GRAY_INVALIDATE_ICACHE(); 707 GRAY_INVALIDATE_ICACHE();
708
710 gray_show(show); 709 gray_show(show);
710
711 GRAY_FLUSH_ICACHE(); 711 GRAY_FLUSH_ICACHE();
712#endif 712#endif
713 stream_mgr_unlock(); 713 stream_mgr_unlock();
@@ -743,6 +743,32 @@ bool stream_vo_get_size(struct vo_ext *sz)
743 return retval; 743 return retval;
744} 744}
745 745
746void stream_vo_set_clip(const struct vo_rect *rc)
747{
748 stream_mgr_lock();
749
750 if (rc)
751 {
752 stream_mgr.parms.rc = *rc;
753 rc = &stream_mgr.parms.rc;
754 }
755#ifndef HAVE_LCD_COLOR
756 else
757 {
758 vo_rect_set_ext(&stream_mgr.parms.rc, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
759 rc = &stream_mgr.parms.rc;
760 }
761#endif
762
763 parser_send_video_msg(VIDEO_SET_CLIP_RECT, (intptr_t)rc);
764
765#ifndef HAVE_LCD_COLOR
766 stream_set_gray_rect(rc);
767#endif
768
769 stream_mgr_unlock();
770}
771
746#ifndef HAVE_LCD_COLOR 772#ifndef HAVE_LCD_COLOR
747/* Set the rectangle for the gray video overlay - clipped to screen */ 773/* Set the rectangle for the gray video overlay - clipped to screen */
748bool stream_set_gray_rect(const struct vo_rect *rc) 774bool stream_set_gray_rect(const struct vo_rect *rc)
@@ -756,21 +782,38 @@ bool stream_set_gray_rect(const struct vo_rect *rc)
756 782
757 if (vo_rect_intersect(&rc_gray, &rc_gray, rc)) 783 if (vo_rect_intersect(&rc_gray, &rc_gray, rc))
758 { 784 {
759 bool vo_vis = stream_show_vo(false); 785 bool vis = parser_send_video_msg(VIDEO_DISPLAY_SHOW, false);
760 786
761 GRAY_VIDEO_FLUSH_ICACHE(); 787 /* The impudence! Keeps the image from disappearing anyway. */
788#ifdef SIMULATOR
789 rb->sim_lcd_ex_init(0, NULL);
790#else
791 rb->timer_unregister();
792#endif
793 GRAY_VIDEO_INVALIDATE_ICACHE();
762 GRAY_INVALIDATE_ICACHE(); 794 GRAY_INVALIDATE_ICACHE();
763 795
796 vo_lock();
797
764 gray_init(rb, stream_mgr.graymem, stream_mgr.graysize, 798 gray_init(rb, stream_mgr.graymem, stream_mgr.graysize,
765 false, rc_gray.r - rc_gray.l, rc_gray.b - rc_gray.t, 799 false, rc_gray.r - rc_gray.l, rc_gray.b - rc_gray.t,
766 32, 2<<8, NULL); 800 32, 2<<8, NULL);
767 801
768 gray_set_position(rc_gray.l, rc_gray.t); 802 gray_set_position(rc_gray.l, rc_gray.t);
769 GRAY_FLUSH_ICACHE();
770 803
771 if (vo_vis) 804 vo_unlock();
805
806 GRAY_INVALIDATE_ICACHE();
807
808 if (stream_mgr.status != STREAM_PLAYING)
809 parser_send_video_msg(VIDEO_PRINT_FRAME, true);
810
811 GRAY_VIDEO_FLUSH_ICACHE();
812
813 if (vis)
772 { 814 {
773 stream_show_vo(true); 815 gray_show(true);
816 parser_send_video_msg(VIDEO_DISPLAY_SHOW, true);
774 } 817 }
775 } 818 }
776 819
@@ -784,9 +827,11 @@ void stream_gray_show(bool show)
784{ 827{
785 stream_mgr_lock(); 828 stream_mgr_lock();
786 829
787 GRAY_VIDEO_FLUSH_ICACHE(); 830 GRAY_VIDEO_INVALIDATE_ICACHE();
788 GRAY_INVALIDATE_ICACHE(); 831 GRAY_INVALIDATE_ICACHE();
832
789 gray_show(show); 833 gray_show(show);
834
790 GRAY_FLUSH_ICACHE(); 835 GRAY_FLUSH_ICACHE();
791 836
792 stream_mgr_unlock(); 837 stream_mgr_unlock();
@@ -803,11 +848,32 @@ bool stream_display_thumb(const struct vo_rect *rc)
803 848
804 stream_mgr_lock(); 849 stream_mgr_lock();
805 850
851 GRAY_INVALIDATE_ICACHE();
852
806 stream_mgr.parms.rc = *rc; 853 stream_mgr.parms.rc = *rc;
807 retval = parser_send_video_msg(VIDEO_PRINT_THUMBNAIL, 854 retval = parser_send_video_msg(VIDEO_PRINT_THUMBNAIL,
808 (intptr_t)&stream_mgr.parms.rc); 855 (intptr_t)&stream_mgr.parms.rc);
809 856
857 GRAY_VIDEO_FLUSH_ICACHE();
858
810 stream_mgr_unlock(); 859 stream_mgr_unlock();
860
861 return retval;
862}
863
864bool stream_draw_frame(bool no_prepare)
865{
866 bool retval;
867 stream_mgr_lock();
868
869 GRAY_INVALIDATE_ICACHE();
870
871 retval = parser_send_video_msg(VIDEO_PRINT_FRAME, no_prepare);
872
873 GRAY_VIDEO_FLUSH_ICACHE();
874
875 stream_mgr_unlock();
876
811 return retval; 877 return retval;
812} 878}
813 879
@@ -826,6 +892,25 @@ uint32_t stream_get_resume_time(void)
826 return resume_time; 892 return resume_time;
827} 893}
828 894
895uint32_t stream_get_seek_time(uint32_t *start)
896{
897 uint32_t time;
898
899 stream_mgr_lock();
900
901 if (stream_mgr.seeked)
902 time = str_parser.last_seek_time;
903 else
904 time = TICKS_TO_TS(pcm_output_get_clock());
905
906 if (start != NULL)
907 *start = str_parser.start_pts;
908
909 stream_mgr_unlock();
910
911 return time;
912}
913
829/* Returns the smallest file window that includes all active streams' 914/* Returns the smallest file window that includes all active streams'
830 * windows */ 915 * windows */
831static bool stream_get_window_callback(struct list_item *item, 916static bool stream_get_window_callback(struct list_item *item,
diff --git a/apps/plugins/mpegplayer/stream_mgr.h b/apps/plugins/mpegplayer/stream_mgr.h
index 63452ecbc0..dd5d8cabec 100644
--- a/apps/plugins/mpegplayer/stream_mgr.h
+++ b/apps/plugins/mpegplayer/stream_mgr.h
@@ -105,6 +105,9 @@ int stream_seek(uint32_t time, int whence);
105/* Show/Hide the video image at the current seekpoint */ 105/* Show/Hide the video image at the current seekpoint */
106bool stream_show_vo(bool show); 106bool stream_show_vo(bool show);
107 107
108/* Set the visible section of video */
109void stream_vo_set_clip(const struct vo_rect *rc);
110
108#ifndef HAVE_LCD_COLOR 111#ifndef HAVE_LCD_COLOR
109/* Set the gray overlay rectangle */ 112/* Set the gray overlay rectangle */
110bool stream_set_gray_rect(const struct vo_rect *rc); 113bool stream_set_gray_rect(const struct vo_rect *rc);
@@ -114,12 +117,19 @@ void stream_gray_show(bool show);
114/* Display thumbnail of the current seekpoint */ 117/* Display thumbnail of the current seekpoint */
115bool stream_display_thumb(const struct vo_rect *rc); 118bool stream_display_thumb(const struct vo_rect *rc);
116 119
120/* Draw the frame at the current position */
121bool stream_draw_frame(bool no_prepare);
122
117/* Return video dimensions */ 123/* Return video dimensions */
118bool stream_vo_get_size(struct vo_ext *sz); 124bool stream_vo_get_size(struct vo_ext *sz);
119 125
120/* Returns the resume time in timestamp ticks */ 126/* Returns the resume time in timestamp ticks */
121uint32_t stream_get_resume_time(void); 127uint32_t stream_get_resume_time(void);
122 128
129/* Returns stream_get_time if no seek is pending or else the
130 last time give to seek */
131uint32_t stream_get_seek_time(uint32_t *start);
132
123/* Return the absolute stream time in clock ticks - adjusted by 133/* Return the absolute stream time in clock ticks - adjusted by
124 * master clock stream via audio timestamps */ 134 * master clock stream via audio timestamps */
125static inline uint32_t stream_get_time(void) 135static inline uint32_t stream_get_time(void)
diff --git a/apps/plugins/mpegplayer/stream_thread.h b/apps/plugins/mpegplayer/stream_thread.h
index 1962a66878..58cb7b93e4 100644
--- a/apps/plugins/mpegplayer/stream_thread.h
+++ b/apps/plugins/mpegplayer/stream_thread.h
@@ -106,6 +106,7 @@ enum stream_message
106 VIDEO_GET_SIZE, /* Get the video dimensions */ 106 VIDEO_GET_SIZE, /* Get the video dimensions */
107 VIDEO_PRINT_FRAME, /* Print the frame at the current position */ 107 VIDEO_PRINT_FRAME, /* Print the frame at the current position */
108 VIDEO_PRINT_THUMBNAIL, /* Print a thumbnail of the current position */ 108 VIDEO_PRINT_THUMBNAIL, /* Print a thumbnail of the current position */
109 VIDEO_SET_CLIP_RECT, /* Set the visible video area */
109#ifdef GRAY_CACHE_MAINT 110#ifdef GRAY_CACHE_MAINT
110 VIDEO_GRAY_CACHEOP, 111 VIDEO_GRAY_CACHEOP,
111#endif 112#endif
diff --git a/apps/plugins/mpegplayer/video_out.h b/apps/plugins/mpegplayer/video_out.h
index 08cd7aa848..ed8c4c5907 100644
--- a/apps/plugins/mpegplayer/video_out.h
+++ b/apps/plugins/mpegplayer/video_out.h
@@ -24,6 +24,16 @@
24#ifndef VIDEO_OUT_H 24#ifndef VIDEO_OUT_H
25#define VIDEO_OUT_H 25#define VIDEO_OUT_H
26 26
27#if LCD_WIDTH >= LCD_HEIGHT
28#define SCREEN_WIDTH LCD_WIDTH
29#define SCREEN_HEIGHT LCD_HEIGHT
30#define LCD_LANDSCAPE
31#else /* Assume the screen is rotated on portrait LCDs */
32#define SCREEN_WIDTH LCD_HEIGHT
33#define SCREEN_HEIGHT LCD_WIDTH
34#define LCD_PORTRAIT
35#endif
36
27/* Structure to hold width and height values */ 37/* Structure to hold width and height values */
28struct vo_ext 38struct vo_ext
29{ 39{
@@ -43,9 +53,18 @@ bool vo_init (void);
43bool vo_show (bool show); 53bool vo_show (bool show);
44bool vo_is_visible(void); 54bool vo_is_visible(void);
45void vo_setup (const mpeg2_sequence_t * sequence); 55void vo_setup (const mpeg2_sequence_t * sequence);
56void vo_set_clip_rect(const struct vo_rect *rc);
46void vo_dimensions(struct vo_ext *sz); 57void vo_dimensions(struct vo_ext *sz);
47void vo_cleanup (void); 58void vo_cleanup (void);
48 59
60#if NUM_CORES > 1 || !defined (HAVE_LCD_COLOR)
61void vo_lock(void);
62void vo_unlock(void);
63#else
64static inline void vo_lock(void) {}
65static inline void vo_unlock(void) {}
66#endif
67
49/* Sets all coordinates of a vo_rect to 0 */ 68/* Sets all coordinates of a vo_rect to 0 */
50void vo_rect_clear(struct vo_rect *rc); 69void vo_rect_clear(struct vo_rect *rc);
51/* Returns true if left >= right or top >= bottom */ 70/* Returns true if left >= right or top >= bottom */
@@ -68,4 +87,10 @@ bool vo_rect_intersect(struct vo_rect *rc_dst,
68 const struct vo_rect *rc1, 87 const struct vo_rect *rc1,
69 const struct vo_rect *rc2); 88 const struct vo_rect *rc2);
70 89
90bool vo_rect_union(struct vo_rect *rc_dst,
91 const struct vo_rect *rc1,
92 const struct vo_rect *rc2);
93
94void vo_rect_offset(struct vo_rect *rc, int dx, int dy);
95
71#endif /* VIDEO_OUT_H */ 96#endif /* VIDEO_OUT_H */
diff --git a/apps/plugins/mpegplayer/video_out_rockbox.c b/apps/plugins/mpegplayer/video_out_rockbox.c
index ff0d39eba6..6efbaf0a01 100644
--- a/apps/plugins/mpegplayer/video_out_rockbox.c
+++ b/apps/plugins/mpegplayer/video_out_rockbox.c
@@ -21,6 +21,9 @@
21#include "plugin.h" 21#include "plugin.h"
22#include "mpegplayer.h" 22#include "mpegplayer.h"
23 23
24#define VO_NON_NULL_RECT 0x1
25#define VO_VISIBLE 0x2
26
24struct vo_data 27struct vo_data
25{ 28{
26 int image_width; 29 int image_width;
@@ -34,8 +37,9 @@ struct vo_data
34 int output_width; 37 int output_width;
35 int output_height; 38 int output_height;
36 bool visible; 39 bool visible;
37 bool thumb_mode; 40 unsigned flags;
38 void *last; 41 struct vo_rect rc_vid;
42 struct vo_rect rc_clip;
39}; 43};
40 44
41#ifdef PROC_NEEDS_CACHEALIGN 45#ifdef PROC_NEEDS_CACHEALIGN
@@ -48,10 +52,40 @@ static uint8_t __vo_data[CACHEALIGN_UP(sizeof(struct vo_data))]
48static struct vo_data vo; 52static struct vo_data vo;
49#endif 53#endif
50 54
55#if NUM_CORES > 1
56static struct mutex vo_mtx NOCACHEBSS_ATTR;
57#endif
58
59static inline void video_lock_init(void)
60{
61#if NUM_CORES > 1
62 rb->mutex_init(&vo_mtx);
63#endif
64}
65
66static inline void video_lock(void)
67{
68#if NUM_CORES > 1
69 rb->mutex_lock(&vo_mtx);
70#endif
71}
72
73static inline void video_unlock(void)
74{
75#if NUM_CORES > 1
76 rb->mutex_unlock(&vo_mtx);
77#endif
78}
79
80
51/* Draw a black rectangle if no video frame is available */ 81/* Draw a black rectangle if no video frame is available */
52static void vo_draw_black(void) 82static void vo_draw_black(void)
53{ 83{
54 int foreground = lcd_(get_foreground)(); 84 int foreground;
85
86 video_lock();
87
88 foreground = lcd_(get_foreground)();
55 89
56 lcd_(set_foreground)(DRAW_BLACK); 90 lcd_(set_foreground)(DRAW_BLACK);
57 91
@@ -61,21 +95,27 @@ static void vo_draw_black(void)
61 vo.output_height); 95 vo.output_height);
62 96
63 lcd_(set_foreground)(foreground); 97 lcd_(set_foreground)(foreground);
98
99 video_unlock();
64} 100}
65 101
66static inline void yuv_blit(uint8_t * const * buf, int src_x, int src_y, 102static inline void yuv_blit(uint8_t * const * buf, int src_x, int src_y,
67 int stride, int x, int y, int width, int height) 103 int stride, int x, int y, int width, int height)
68{ 104{
105 video_lock();
106
69#ifdef HAVE_LCD_COLOR 107#ifdef HAVE_LCD_COLOR
70 rb->lcd_yuv_blit(buf, src_x, src_y, stride, x, y , width, height); 108 rb->lcd_yuv_blit(buf, src_x, src_y, stride, x, y , width, height);
71#else 109#else
72 gray_ub_gray_bitmap_part(buf[0], src_x, src_y, stride, x, y, width, height); 110 gray_ub_gray_bitmap_part(buf[0], src_x, src_y, stride, x, y, width, height);
73#endif 111#endif
112
113 video_unlock();
74} 114}
75 115
76void vo_draw_frame(uint8_t * const * buf) 116void vo_draw_frame(uint8_t * const * buf)
77{ 117{
78 if (!vo.visible) 118 if (vo.flags == 0)
79 { 119 {
80 /* Frame is hidden - copout */ 120 /* Frame is hidden - copout */
81 DEBUGF("vo hidden\n"); 121 DEBUGF("vo hidden\n");
@@ -94,14 +134,6 @@ void vo_draw_frame(uint8_t * const * buf)
94 vo.output_height); 134 vo.output_height);
95} 135}
96 136
97#if LCD_WIDTH >= LCD_HEIGHT
98#define SCREEN_WIDTH LCD_WIDTH
99#define SCREEN_HEIGHT LCD_HEIGHT
100#else /* Assume the screen is rotated on portrait LCDs */
101#define SCREEN_WIDTH LCD_HEIGHT
102#define SCREEN_HEIGHT LCD_WIDTH
103#endif
104
105static inline void vo_rect_clear_inl(struct vo_rect *rc) 137static inline void vo_rect_clear_inl(struct vo_rect *rc)
106{ 138{
107 rc->l = rc->t = rc->r = rc->b = 0; 139 rc->l = rc->t = rc->r = rc->b = 0;
@@ -172,6 +204,48 @@ bool vo_rect_intersect(struct vo_rect *rc_dst,
172 return false; 204 return false;
173} 205}
174 206
207bool vo_rect_union(struct vo_rect *rc_dst,
208 const struct vo_rect *rc1,
209 const struct vo_rect *rc2)
210{
211 if (rc_dst != NULL)
212 {
213 if (!vo_rect_empty_inl(rc1))
214 {
215 if (!vo_rect_empty_inl(rc2))
216 {
217 rc_dst->l = MIN(rc1->l, rc2->l);
218 rc_dst->t = MIN(rc1->t, rc2->t);
219 rc_dst->r = MAX(rc1->r, rc2->r);
220 rc_dst->b = MAX(rc1->b, rc2->b);
221 }
222 else
223 {
224 *rc_dst = *rc1;
225 }
226
227 return true;
228 }
229 else if (!vo_rect_empty(rc2))
230 {
231 *rc_dst = *rc2;
232 return true;
233 }
234
235 vo_rect_clear_inl(rc_dst);
236 }
237
238 return false;
239}
240
241void vo_rect_offset(struct vo_rect *rc, int dx, int dy)
242{
243 rc->l += dx;
244 rc->t += dy;
245 rc->r += dx;
246 rc->b += dy;
247}
248
175/* Shink or stretch each axis - rotate counter-clockwise to retain upright 249/* Shink or stretch each axis - rotate counter-clockwise to retain upright
176 * orientation on rotated displays (they rotate clockwise) */ 250 * orientation on rotated displays (they rotate clockwise) */
177void stretch_image_plane(const uint8_t * src, uint8_t *dst, int stride, 251void stretch_image_plane(const uint8_t * src, uint8_t *dst, int stride,
@@ -350,25 +424,27 @@ void vo_setup(const mpeg2_sequence_t * sequence)
350 424
351 if (sequence->display_width >= SCREEN_WIDTH) 425 if (sequence->display_width >= SCREEN_WIDTH)
352 { 426 {
353 vo.output_width = SCREEN_WIDTH; 427 vo.rc_vid.l = 0;
354 vo.output_x = 0; 428 vo.rc_vid.r = SCREEN_WIDTH;
355 } 429 }
356 else 430 else
357 { 431 {
358 vo.output_width = sequence->display_width; 432 vo.rc_vid.l = (SCREEN_WIDTH - sequence->display_width) / 2;
359 vo.output_x = (SCREEN_WIDTH - sequence->display_width) / 2; 433 vo.rc_vid.r = vo.rc_vid.l + sequence->display_width;
360 } 434 }
361 435
362 if (sequence->display_height >= SCREEN_HEIGHT) 436 if (sequence->display_height >= SCREEN_HEIGHT)
363 { 437 {
364 vo.output_height = SCREEN_HEIGHT; 438 vo.rc_vid.t = 0;
365 vo.output_y = 0; 439 vo.rc_vid.b = SCREEN_HEIGHT;
366 } 440 }
367 else 441 else
368 { 442 {
369 vo.output_height = sequence->display_height; 443 vo.rc_vid.t = (SCREEN_HEIGHT - sequence->display_height) / 2;
370 vo.output_y = (SCREEN_HEIGHT - sequence->display_height) / 2; 444 vo.rc_vid.b = vo.rc_vid.t + sequence->display_height;
371 } 445 }
446
447 vo_set_clip_rect(&vo.rc_clip);
372} 448}
373 449
374void vo_dimensions(struct vo_ext *sz) 450void vo_dimensions(struct vo_ext *sz)
@@ -379,23 +455,68 @@ void vo_dimensions(struct vo_ext *sz)
379 455
380bool vo_init(void) 456bool vo_init(void)
381{ 457{
382 vo.visible = false; 458 vo.flags = 0;
459 vo_rect_set_ext(&vo.rc_clip, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
460 video_lock_init();
383 return true; 461 return true;
384} 462}
385 463
386bool vo_show(bool show) 464bool vo_show(bool show)
387{ 465{
388 bool vis = vo.visible; 466 bool vis = vo.flags & VO_VISIBLE;
389 vo.visible = show; 467
468 if (show)
469 vo.flags |= VO_VISIBLE;
470 else
471 vo.flags &= ~VO_VISIBLE;
472
390 return vis; 473 return vis;
391} 474}
392 475
393bool vo_is_visible(void) 476bool vo_is_visible(void)
394{ 477{
395 return vo.visible; 478 return vo.flags & VO_VISIBLE;
396} 479}
397 480
398void vo_cleanup(void) 481void vo_cleanup(void)
399{ 482{
400 vo.visible = false; 483 vo.flags = 0;
401} 484}
485
486void vo_set_clip_rect(const struct vo_rect *rc)
487{
488 struct vo_rect rc_out;
489
490 if (rc == NULL)
491 vo_rect_set_ext(&vo.rc_clip, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
492 else
493 vo.rc_clip = *rc;
494
495 if (!vo_rect_intersect(&rc_out, &vo.rc_vid, &vo.rc_clip))
496 vo.flags &= ~VO_NON_NULL_RECT;
497 else
498 vo.flags |= VO_NON_NULL_RECT;
499
500 vo.output_x = rc_out.l;
501 vo.output_y = rc_out.t;
502 vo.output_width = rc_out.r - rc_out.l;
503 vo.output_height = rc_out.b - rc_out.t;
504}
505
506#if NUM_CORES > 1 || !defined (HAVE_LCD_COLOR)
507void vo_lock(void)
508{
509#ifndef HAVE_LCD_COLOR
510 set_irq_level(HIGHEST_IRQ_LEVEL);
511#endif
512 video_lock();
513}
514
515void vo_unlock(void)
516{
517 video_unlock();
518#ifndef HAVE_LCD_COLOR
519 set_irq_level(0);
520#endif
521}
522#endif
diff --git a/apps/plugins/mpegplayer/video_thread.c b/apps/plugins/mpegplayer/video_thread.c
index aeaf942058..950d49a935 100644
--- a/apps/plugins/mpegplayer/video_thread.c
+++ b/apps/plugins/mpegplayer/video_thread.c
@@ -75,7 +75,10 @@ static void draw_fps(struct video_thread_data *td)
75 fps / 100, fps % 100, td->num_skipped, 75 fps / 100, fps % 100, td->num_skipped,
76 td->info->display_picture->temporal_reference); 76 td->info->display_picture->temporal_reference);
77 rb->lcd_putsxy(0, 0, str); 77 rb->lcd_putsxy(0, 0, str);
78
79 vo_lock();
78 rb->lcd_update_rect(0, 0, LCD_WIDTH, 8); 80 rb->lcd_update_rect(0, 0, LCD_WIDTH, 8);
81 vo_unlock();
79 82
80 td->last_showfps = *rb->current_tick; 83 td->last_showfps = *rb->current_tick;
81} 84}
@@ -522,7 +525,9 @@ static void video_thread_msg(struct video_thread_data *td)
522 else 525 else
523 { 526 {
524 IF_COP(invalidate_icache()); 527 IF_COP(invalidate_icache());
528 vo_lock();
525 rb->lcd_update(); 529 rb->lcd_update();
530 vo_unlock();
526 } 531 }
527#else 532#else
528 GRAY_FLUSH_ICACHE(); 533 GRAY_FLUSH_ICACHE();
@@ -580,6 +585,10 @@ static void video_thread_msg(struct video_thread_data *td)
580 } 585 }
581 break; 586 break;
582 587
588 case VIDEO_SET_CLIP_RECT:
589 vo_set_clip_rect((const struct vo_rect *)td->ev.data);
590 break;
591
583 case VIDEO_PRINT_FRAME: 592 case VIDEO_PRINT_FRAME:
584 /* Print the last frame decoded */ 593 /* Print the last frame decoded */
585 if (td->info != NULL && td->info->display_fbuf != NULL) 594 if (td->info != NULL && td->info->display_fbuf != NULL)