From a5fc3f4df4bec2b6ae1c22fb83cf495f31773122 Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Thu, 3 Jan 2008 17:14:28 +0000 Subject: Initial WVS for mpegplayer. Adjusts to the user's preferred font and uses FF/RW preferences set for playback. Picked a random color for the base WVS color but it could be configured. Some engine tweaks to accomodate it since certain nescessities are clearer now. Fix a clipped YUV output bug in the SIM. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15991 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugin.c | 2 + apps/plugin.h | 4 +- apps/plugins/bitmaps/mono/SOURCES | 5 + .../mono/mpegplayer_status_icons_12x12x1.bmp | Bin 0 -> 158 bytes .../mono/mpegplayer_status_icons_16x16x1.bmp | Bin 0 -> 254 bytes .../bitmaps/mono/mpegplayer_status_icons_8x8x1.bmp | Bin 0 -> 126 bytes apps/plugins/mpegplayer/Makefile | 4 +- apps/plugins/mpegplayer/audio_thread.c | 3 +- apps/plugins/mpegplayer/disk_buf.h | 3 + apps/plugins/mpegplayer/mpeg_parser.c | 6 +- apps/plugins/mpegplayer/mpegplayer.c | 1066 +++++++++++++++++++- apps/plugins/mpegplayer/stream_mgr.c | 133 ++- apps/plugins/mpegplayer/stream_mgr.h | 10 + apps/plugins/mpegplayer/stream_thread.h | 1 + apps/plugins/mpegplayer/video_out.h | 25 + apps/plugins/mpegplayer/video_out_rockbox.c | 171 +++- apps/plugins/mpegplayer/video_thread.c | 9 + uisimulator/sdl/lcd-bitmap.c | 2 +- 18 files changed, 1340 insertions(+), 104 deletions(-) create mode 100644 apps/plugins/bitmaps/mono/mpegplayer_status_icons_12x12x1.bmp create mode 100644 apps/plugins/bitmaps/mono/mpegplayer_status_icons_16x16x1.bmp create mode 100644 apps/plugins/bitmaps/mono/mpegplayer_status_icons_8x8x1.bmp 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 = { trigger_cpu_boost, cancel_cpu_boost, #endif + sound_unit, + sound_val2phys, #endif /* CONFIG_CODEC == SWCODEC */ }; 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 @@ #define PLUGIN_MAGIC 0x526F634B /* RocK */ /* increase this every time the api struct changes */ -#define PLUGIN_API_VERSION 92 +#define PLUGIN_API_VERSION 93 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any @@ -715,6 +715,8 @@ struct plugin_api { void (*trigger_cpu_boost)(void); void (*cancel_cpu_boost)(void); #endif + const char * (*sound_unit)(int setting); + int (*sound_val2phys)(int setting, int value); #endif /* CONFIG_CODEC == SWCODEC */ }; 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 #endif #endif +/* MPEGplayer */ +mpegplayer_status_icons_8x8x1.bmp +mpegplayer_status_icons_12x12x1.bmp +mpegplayer_status_icons_16x16x1.bmp + #if LCD_WIDTH == 160 && LCD_HEIGHT == 128 && LCD_DEPTH < 16 superdom_boarditems.160x128x1.bmp #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 Binary files /dev/null and b/apps/plugins/bitmaps/mono/mpegplayer_status_icons_12x12x1.bmp 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 Binary files /dev/null and b/apps/plugins/bitmaps/mono/mpegplayer_status_icons_16x16x1.bmp 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 Binary files /dev/null and b/apps/plugins/bitmaps/mono/mpegplayer_status_icons_8x8x1.bmp 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: ifndef SIMVER $(OBJDIR)/mpegplayer.elf: $(OBJS) $(LINKFILE) $(BUILDDIR)/libmad-mpegplayer.a $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) -o $@ $(OBJS) -L$(BUILDDIR) -lplugin -lmad-mpegplayer -lgcc\ - -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/mpegplayer.map + $(LINKBITMAPS) -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/mpegplayer.map $(OUTPUT): $(OBJDIR)/mpegplayer.elf $(call PRINTS,OBJCOPY $(@F))$(OC) -O binary $< $@ @@ -51,7 +51,7 @@ else # This is the SDL simulator version $(OUTPUT): $(OBJS) $(BUILDDIR)/libmad-mpegplayer.a - $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(OBJS) -L$(BUILDDIR) -lplugin -lmad-mpegplayer -o $@ + $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(OBJS) -L$(BUILDDIR) -lplugin $(LINKBITMAPS) -lmad-mpegplayer -o $@ ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) # 'x' must be kept or you'll have "Win32 error 5" # $ 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) audio_queue.used = 0; audio_queue.read = 0; audio_queue.write = 0; + rb->memset(audio_queue.descs, 0, sizeof (audio_queue.descs)); audio_queue.curr = audiodesc_queue_head(); - audio_queue.curr->time = 0; - audio_queue.curr->size = 0; } static 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, bool disk_buf_init(void); void disk_buf_exit(void); +static inline int disk_buf_status(void) + { return disk_buf.status; } + int disk_buf_open(const char *filename); void disk_buf_close(void); ssize_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) switch (id) { case VIDEO_DISPLAY_SHOW: - if (data != 0 && stream_status() != STREAM_PLAYING) + if (data != 0 && disk_buf_status() == STREAM_STOPPED) { /* Only prepare image if showing and not playing */ prepare_image(str_parser.last_seek_time); } break; case VIDEO_PRINT_FRAME: + if (data) + break; case VIDEO_PRINT_THUMBNAIL: - if (stream_status() == STREAM_PLAYING) + if (disk_buf_status() != STREAM_STOPPED) break; /* Prepare image if not playing */ 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 #define MPEG_PAUSE BUTTON_ON #define MPEG_VOLDOWN BUTTON_DOWN #define MPEG_VOLUP BUTTON_UP +#define MPEG_RW BUTTON_LEFT +#define MPEG_FF BUTTON_RIGHT #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \ (CONFIG_KEYPAD == IPOD_1G2G_PAD) @@ -124,6 +126,8 @@ PLUGIN_IRAM_DECLARE #define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT) #define MPEG_VOLDOWN BUTTON_SCROLL_BACK #define MPEG_VOLUP BUTTON_SCROLL_FWD +#define MPEG_RW BUTTON_LEFT +#define MPEG_FF BUTTON_RIGHT #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD #define MPEG_MENU (BUTTON_REC | BUTTON_REL) @@ -131,6 +135,8 @@ PLUGIN_IRAM_DECLARE #define MPEG_PAUSE BUTTON_PLAY #define MPEG_VOLDOWN BUTTON_DOWN #define MPEG_VOLUP BUTTON_UP +#define MPEG_RW BUTTON_LEFT +#define MPEG_FF BUTTON_RIGHT #elif CONFIG_KEYPAD == GIGABEAT_PAD #define MPEG_MENU BUTTON_MENU @@ -141,13 +147,17 @@ PLUGIN_IRAM_DECLARE #define MPEG_VOLUP BUTTON_RIGHT #define MPEG_VOLDOWN2 BUTTON_VOL_DOWN #define MPEG_VOLUP2 BUTTON_VOL_UP +#define MPEG_RW BUTTON_UP +#define MPEG_FF BUTTON_DOWN #elif CONFIG_KEYPAD == IRIVER_H10_PAD -#define MPEG_MENU (BUTTON_REW | BUTTON_REL) +#define MPEG_MENU BUTTON_LEFT #define MPEG_STOP BUTTON_POWER #define MPEG_PAUSE BUTTON_PLAY #define MPEG_VOLDOWN BUTTON_SCROLL_DOWN #define MPEG_VOLUP BUTTON_SCROLL_UP +#define MPEG_RW BUTTON_REW +#define MPEG_FF BUTTON_FF #elif CONFIG_KEYPAD == SANSA_E200_PAD #define MPEG_MENU BUTTON_SELECT @@ -155,6 +165,8 @@ PLUGIN_IRAM_DECLARE #define MPEG_PAUSE BUTTON_UP #define MPEG_VOLDOWN BUTTON_SCROLL_UP #define MPEG_VOLUP BUTTON_SCROLL_DOWN +#define MPEG_RW BUTTON_LEFT +#define MPEG_FF BUTTON_RIGHT #elif CONFIG_KEYPAD == SANSA_C200_PAD #define MPEG_MENU BUTTON_SELECT @@ -162,6 +174,8 @@ PLUGIN_IRAM_DECLARE #define MPEG_PAUSE BUTTON_UP #define MPEG_VOLDOWN BUTTON_VOL_DOWN #define MPEG_VOLUP BUTTON_VOL_UP +#define MPEG_RW BUTTON_LEFT +#define MPEG_FF BUTTON_RIGHT #elif CONFIG_KEYPAD == MROBE500_PAD #define MPEG_MENU BUTTON_RC_HEART @@ -169,6 +183,8 @@ PLUGIN_IRAM_DECLARE #define MPEG_PAUSE BUTTON_TOUCHPAD #define MPEG_VOLDOWN BUTTON_RC_VOL_DOWN #define MPEG_VOLUP BUTTON_RC_VOL_UP +#define MPEG_RW BUTTON_RC_REW +#define MPEG_FF BUTTON_RC_FF #else #error MPEGPLAYER: Unsupported keypad @@ -179,31 +195,997 @@ struct plugin_api* rb; CACHE_FUNCTION_WRAPPERS(rb); ALIGN_BUFFER_WRAPPER(rb); -static bool button_loop(void) +/* One thing we can do here for targets with remotes is having a display + * always on the remote instead of always forcing a popup on the main display */ + +#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */ + /* 3% of 30min file == 54s step size */ +#define MIN_FF_REWIND_STEP (TS_SECOND/2) +#define WVS_MIN_UPDATE_INTERVAL (HZ/2) + +/* WVS status - same order as icon array */ +enum wvs_status_enum +{ + WVS_STATUS_STOPPED = 0, + WVS_STATUS_PAUSED, + WVS_STATUS_PLAYING, + WVS_STATUS_FF, + WVS_STATUS_RW, + WVS_STATUS_COUNT, + WVS_STATUS_MASK = 0x7 +}; + +enum wvs_bits { - bool ret = true; + WVS_REFRESH_DEFAULT = 0x0000, + WVS_REFRESH_VOLUME = 0x0001, + WVS_REFRESH_TIME = 0x0002, + WVS_REFRESH_STATUS = 0x0004, + WVS_REFRESH_BACKGROUND = 0x0008, + WVS_REFRESH_VIDEO = 0x0010, + WVS_REFRESH_RESUME = 0x0020, + WVS_NODRAW = 0x8000, + WVS_SHOW = 0x4000, + WVS_HIDE = 0x0000, + WVS_REFRESH_ALL = 0x001f, +}; + +extern const unsigned char mpegplayer_status_icons_8x8x1[]; +extern const unsigned char mpegplayer_status_icons_12x12x1[]; +extern const unsigned char mpegplayer_status_icons_16x16x1[]; + +#define WVS_BDR_L 2 +#define WVS_BDR_T 2 +#define WVS_BDR_R 2 +#define WVS_BDR_B 2 + +struct wvs +{ + long hide_tick; + long show_for; + long print_tick; + long print_delay; + long resume_tick; + long resume_delay; + long next_auto_refresh; + int x; + int y; + int width; + int height; + unsigned fgcolor; + unsigned bgcolor; +#ifdef HAVE_LCD_COLOR + unsigned prog_fillcolor; + struct vo_rect update_rect; +#endif + struct vo_rect prog_rect; + struct vo_rect time_rect; + struct vo_rect dur_rect; + struct vo_rect vol_rect; + const unsigned char *icons; + struct vo_rect stat_rect; + int status; + uint32_t curr_time; + unsigned auto_refresh; + unsigned flags; +}; + +static struct wvs wvs; + +static void wvs_show(unsigned show); + +#ifdef LCD_LANDSCAPE + #define _X (x + wvs.x) + #define _Y (y + wvs.y) + #define _W width + #define _H height +#else + #define _X (LCD_WIDTH - (y + wvs.y) - height) + #define _Y (x + wvs.x) + #define _W height + #define _H width +#endif + +#ifdef HAVE_LCD_COLOR +static unsigned draw_blendcolor(unsigned c1, unsigned c2, unsigned char amount) +{ + int r1 = RGB_UNPACK_RED(c1); + int g1 = RGB_UNPACK_GREEN(c1); + int b1 = RGB_UNPACK_BLUE(c1); + + int r2 = RGB_UNPACK_RED(c2); + int g2 = RGB_UNPACK_GREEN(c2); + int b2 = RGB_UNPACK_BLUE(c2); + + return LCD_RGBPACK(amount*(r2 - r1) / 255 + r1, + amount*(g2 - g1) / 255 + g1, + amount*(b2 - b1) / 255 + b1); +} +#endif + +static void draw_fill_rect(int x, int y, int width, int height) +{ + rb->lcd_fillrect(_X, _Y, _W, _H); +} + +#ifdef HAVE_LCD_COLOR +static void draw_update_rect(int x, int y, int width, int height) +{ + rb->lcd_update_rect(_X, _Y, _W, _H); +} +#endif + +static void draw_clear_area(int x, int y, int width, int height) +{ + rb->screen_clear_area(rb->screens[SCREEN_MAIN], _X, _Y, _W, _H); +} + +static void draw_clear_area_rect(const struct vo_rect *rc) +{ + int x = rc->l; + int y = rc->t; + int width = rc->r - rc->l; + int height = rc->b - rc->t; + rb->screen_clear_area(rb->screens[SCREEN_MAIN], _X, _Y, _W, _H); +} + +static void draw_scrollbar_draw(int x, int y, int width, int height, + int items, int min_shown, int max_shown) +{ +#ifdef HAVE_LCD_COLOR + int oldbg = rb->lcd_get_background(); + rb->lcd_set_background(wvs.prog_fillcolor); +#endif + + rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN], _X, _Y, + _W, _H, items, min_shown, max_shown, + 0 +#ifdef LCD_LANDSCAPE + | HORIZONTAL +#endif +#ifdef HAVE_LCD_COLOR + | INNER_BGFILL | FOREGROUND +#endif + ); + +#ifdef HAVE_LCD_COLOR + rb->lcd_set_background(oldbg); +#endif +} + +static void draw_scrollbar_draw_rect(const struct vo_rect *rc, int items, + int min_shown, int max_shown) +{ + draw_scrollbar_draw(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t, + items, min_shown, max_shown); +} + +static void draw_hline(int x1, int x2, int y) +{ +#ifdef LCD_LANDSCAPE + rb->lcd_hline(x1 + wvs.x, x2 + wvs.x, y + wvs.y); +#else + y = LCD_WIDTH - (y + wvs.y) - 1; + rb->lcd_vline(y, x1 + wvs.x, x2 + wvs.x); +#endif +} + +#ifdef LCD_PORTRAIT +/* Portrait displays need rotated text rendering */ + +/* Limited function that only renders in DRMODE_FG */ +static void draw_oriented_mono_bitmap_part(const unsigned char *src, + int src_x, int src_y, + int stride, int x, int y, + int width, int height) +{ + const unsigned char *src_end; + fb_data *dst, *dst_end; + unsigned fg_pattern, bg_pattern; + + if (x + width > SCREEN_WIDTH) + width = SCREEN_WIDTH - x; /* Clip right */ + if (x < 0) + width += x, x = 0; /* Clip left */ + if (width <= 0) + return; /* nothing left to do */ + + if (y + height > SCREEN_HEIGHT) + height = SCREEN_HEIGHT - y; /* Clip bottom */ + if (y < 0) + height += y, y = 0; /* Clip top */ + if (height <= 0) + return; /* nothing left to do */ + + fg_pattern = rb->lcd_get_foreground(); + bg_pattern = rb->lcd_get_background(); + + src += stride * (src_y >> 3) + src_x; /* move starting point */ + src_y &= 7; + src_end = src + width; + + dst = rb->lcd_framebuffer + (LCD_WIDTH - y) + x*LCD_WIDTH; + do + { + const unsigned char *src_col = src++; + unsigned data = *src_col >> src_y; + int numbits = 8 - src_y; + + fb_data *dst_col = dst; + dst_end = dst_col - height; + dst += LCD_WIDTH; + + do + { + dst_col--; + + if (data & 1) + *dst_col = fg_pattern; +#if 0 + else + *dst_col = bg_pattern; +#endif + data >>= 1; + if (--numbits == 0) { + src_col += stride; + data = *src_col; + numbits = 8; + } + } + while (dst_col > dst_end); + } + while (src < src_end); +} + +static void draw_putsxy_oriented(int x, int y, const char *str) +{ + unsigned short ch; + unsigned short *ucs; + int ofs = MIN(x, 0); + struct font* pf = rb->font_get(FONT_UI); + + ucs = rb->bidi_l2v(str, 1); + + x += wvs.x; + y += wvs.y; + + while ((ch = *ucs++) != 0 && x < SCREEN_WIDTH) + { + int width; + const unsigned char *bits; + + /* get proportional width and glyph bits */ + width = rb->font_get_width(pf, ch); + + if (ofs > width) { + ofs -= width; + continue; + } + + bits = rb->font_get_bits(pf, ch); + + draw_oriented_mono_bitmap_part(bits, ofs, 0, width, x, y, + width - ofs, pf->height); + + x += width - ofs; + ofs = 0; + } +} +#else +static void draw_oriented_mono_bitmap_part(const unsigned char *src, + int src_x, int src_y, + int stride, int x, int y, + int width, int height) +{ + int mode = rb->lcd_get_drawmode(); + rb->lcd_set_drawmode(DRMODE_FG); + rb->lcd_mono_bitmap_part(src, src_x, src_y, stride, x, y, width, height); + rb->lcd_set_drawmode(mode); +} + +static void draw_putsxy_oriented(int x, int y, const char *str) +{ + int mode = rb->lcd_get_drawmode(); + rb->lcd_set_drawmode(DRMODE_FG); + rb->lcd_putsxy(x + wvs.x, y + wvs.y, str); + rb->lcd_set_drawmode(mode); +} +#endif /* LCD_PORTRAIT */ + + +static void wvs_text_init(void) +{ + struct hms hms; + char buf[32]; + int phys; + int spc_width; + + rb->lcd_setfont(FONT_UI); + + wvs.x = 0; + wvs.width = SCREEN_WIDTH; + + vo_rect_clear(&wvs.time_rect); + vo_rect_clear(&wvs.stat_rect); + vo_rect_clear(&wvs.prog_rect); + vo_rect_clear(&wvs.vol_rect); + + ts_to_hms(stream_get_duration(), &hms); + hms_format(buf, sizeof (buf), &hms); + rb->lcd_getstringsize(buf, &wvs.time_rect.r, + &wvs.time_rect.b); + + /* Choose well-sized bitmap images relative to font height */ + if (wvs.time_rect.b < 12) { + wvs.icons = mpegplayer_status_icons_8x8x1; + wvs.stat_rect.r = wvs.stat_rect.b = 8; + } else if (wvs.time_rect.b < 16) { + wvs.icons = mpegplayer_status_icons_12x12x1; + wvs.stat_rect.r = wvs.stat_rect.b = 12; + } else { + wvs.icons = mpegplayer_status_icons_16x16x1; + wvs.stat_rect.r = wvs.stat_rect.b = 16; + } + + if (wvs.stat_rect.b < wvs.time_rect.b) { + vo_rect_offset(&wvs.stat_rect, 0, + (wvs.time_rect.b - wvs.stat_rect.b) / 2 + WVS_BDR_T); + vo_rect_offset(&wvs.time_rect, WVS_BDR_L, WVS_BDR_T); + } else { + vo_rect_offset(&wvs.time_rect, WVS_BDR_L, + wvs.stat_rect.b - wvs.time_rect.b + WVS_BDR_T); + vo_rect_offset(&wvs.stat_rect, 0, WVS_BDR_T); + } + + wvs.dur_rect = wvs.time_rect; + + phys = rb->sound_val2phys(SOUND_VOLUME, rb->sound_min(SOUND_VOLUME)); + rb->snprintf(buf, sizeof(buf), "%d%s", phys, + rb->sound_unit(SOUND_VOLUME)); + + rb->lcd_getstringsize(" ", &spc_width, NULL); + rb->lcd_getstringsize(buf, &wvs.vol_rect.r, &wvs.vol_rect.b); + + wvs.prog_rect.r = SCREEN_WIDTH - WVS_BDR_L - spc_width - + wvs.vol_rect.r - WVS_BDR_R; + wvs.prog_rect.b = 3*wvs.stat_rect.b / 4; + vo_rect_offset(&wvs.prog_rect, wvs.time_rect.l, + wvs.time_rect.b); + + vo_rect_offset(&wvs.stat_rect, + (wvs.prog_rect.r + wvs.prog_rect.l - wvs.stat_rect.r) / 2, + 0); + + vo_rect_offset(&wvs.dur_rect, + wvs.prog_rect.r - wvs.dur_rect.r, 0); + + vo_rect_offset(&wvs.vol_rect, wvs.prog_rect.r + spc_width, + (wvs.prog_rect.b + wvs.prog_rect.t - wvs.vol_rect.b) / 2); + + wvs.height = WVS_BDR_T + MAX(wvs.prog_rect.b, wvs.vol_rect.b) - + MIN(wvs.time_rect.t, wvs.stat_rect.t) + WVS_BDR_B; + +#if LCD_PIXELFORMAT == VERTICAL_PACKING + wvs.height = ALIGN_UP(wvs.height, 8); +#else + wvs.height = ALIGN_UP(wvs.height, 2); +#endif + wvs.y = SCREEN_HEIGHT - wvs.height; rb->lcd_setfont(FONT_SYSFIXED); +} + +static void wvs_init(void) +{ + wvs.flags = 0; + wvs.show_for = HZ*4; + wvs.print_delay = 75*HZ/100; + wvs.resume_delay = HZ/2; +#ifdef HAVE_LCD_COLOR + wvs.bgcolor = LCD_RGBPACK(0x73, 0x75, 0xbd); + wvs.fgcolor = LCD_WHITE; + wvs.prog_fillcolor = LCD_BLACK; +#else + wvs.bgcolor = LCD_LIGHTGRAY; + wvs.fgcolor = LCD_BLACK; +#endif + wvs.curr_time = 0; + wvs.status = WVS_STATUS_STOPPED; + wvs.auto_refresh = WVS_REFRESH_TIME; + wvs.next_auto_refresh = *rb->current_tick; + wvs_text_init(); +} + +static void wvs_schedule_refresh(unsigned refresh) +{ + long tick = *rb->current_tick; + + if (refresh & WVS_REFRESH_VIDEO) + wvs.print_tick = tick + wvs.print_delay; + + if (refresh & WVS_REFRESH_RESUME) + wvs.resume_tick = tick + wvs.resume_delay; + + wvs.auto_refresh |= refresh; +} + +static void wvs_cancel_refresh(unsigned refresh) +{ + wvs.auto_refresh &= ~refresh; +} + +static void wvs_refresh_background(void) +{ + char buf[32]; + struct hms hms; + + int bg = rb->lcd_get_background(); + rb->lcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID); + +#ifdef HAVE_LCD_COLOR + rb->lcd_set_background(draw_blendcolor(bg, LCD_WHITE, 192)); + draw_hline(0, wvs.width, 0); + + rb->lcd_set_background(draw_blendcolor(bg, LCD_WHITE, 80)); + draw_hline(0, wvs.width, 1); + + rb->lcd_set_background(draw_blendcolor(bg, LCD_BLACK, 48)); + draw_hline(0, wvs.width, wvs.height-2); + + rb->lcd_set_background(draw_blendcolor(bg, LCD_BLACK, 128)); + draw_hline(0, wvs.width, wvs.height-1); + + rb->lcd_set_background(bg); + draw_clear_area(0, 2, wvs.width, wvs.height - 4); + + vo_rect_set_ext(&wvs.update_rect, 0, 0, wvs.width, wvs.height); +#else + rb->lcd_set_background(LCD_DARKGRAY); + draw_hline(0, wvs.width, 0); + + rb->lcd_set_background(bg); + draw_clear_area(0, 1, wvs.width, wvs.height - 1); +#endif + + rb->lcd_set_drawmode(DRMODE_SOLID); + + if (stream_get_duration() != INVALID_TIMESTAMP) { + /* Don't know the duration */ + ts_to_hms(stream_get_duration(), &hms); + hms_format(buf, sizeof (buf), &hms); + draw_putsxy_oriented(wvs.dur_rect.l, wvs.dur_rect.t, buf); + } +} + +static void wvs_refresh_time(void) +{ + char buf[32]; + struct hms hms; + + uint32_t duration = stream_get_duration(); + + draw_scrollbar_draw_rect(&wvs.prog_rect, duration, 0, + wvs.curr_time); + + ts_to_hms(wvs.curr_time, &hms); + hms_format(buf, sizeof (buf), &hms); + + draw_clear_area_rect(&wvs.time_rect); + draw_putsxy_oriented(wvs.time_rect.l, wvs.time_rect.t, buf); + +#ifdef HAVE_LCD_COLOR + vo_rect_union(&wvs.update_rect, &wvs.update_rect, + &wvs.prog_rect); + vo_rect_union(&wvs.update_rect, &wvs.update_rect, + &wvs.time_rect); +#endif +} + +static void wvs_refresh_volume(void) +{ + char buf[32]; + int width; + + int volume = rb->global_settings->volume; + rb->snprintf(buf, sizeof (buf), "%d%s", + rb->sound_val2phys(SOUND_VOLUME, volume), + rb->sound_unit(SOUND_VOLUME)); + rb->lcd_getstringsize(buf, &width, NULL); + + /* Right-justified */ + draw_clear_area_rect(&wvs.vol_rect); + draw_putsxy_oriented(wvs.vol_rect.r - width, wvs.vol_rect.t, buf); + +#ifdef HAVE_LCD_COLOR + vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.vol_rect); +#endif +} + +static void wvs_refresh_status(void) +{ + int icon_size = wvs.stat_rect.r - wvs.stat_rect.l; + + draw_clear_area_rect(&wvs.stat_rect); + +#ifdef HAVE_LCD_COLOR + /* Draw status icon with a drop shadow */ + unsigned oldfg = rb->lcd_get_foreground(); + int i = 1; + + rb->lcd_set_foreground(draw_blendcolor(rb->lcd_get_background(), + LCD_BLACK, 96)); + + while (1) + { + draw_oriented_mono_bitmap_part(wvs.icons, + icon_size*wvs.status, + 0, + icon_size*WVS_STATUS_COUNT, + wvs.stat_rect.l + wvs.x + i, + wvs.stat_rect.t + wvs.y + i, + icon_size, icon_size); + + if (--i < 0) + break; + + rb->lcd_set_foreground(oldfg); + } + + vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.stat_rect); +#else + draw_oriented_mono_bitmap_part(wvs.icons, + icon_size*wvs.status, + 0, + icon_size*WVS_STATUS_COUNT, + wvs.stat_rect.l + wvs.x, + wvs.stat_rect.t + wvs.y, + icon_size, icon_size); +#endif +} + +static bool wvs_update_status(void) +{ + int status; + + switch (stream_status()) + { + default: + status = WVS_STATUS_STOPPED; + break; + case STREAM_PAUSED: + status = (wvs.auto_refresh & WVS_REFRESH_RESUME) ? + WVS_STATUS_PLAYING : WVS_STATUS_PAUSED; + break; + case STREAM_PLAYING: + status = WVS_STATUS_PLAYING; + break; + } + + if (status != wvs.status) { + wvs.status = status; + return true; + } + + return false; +} + +static void wvs_update_time(void) +{ + uint32_t start; + wvs.curr_time = stream_get_seek_time(&start); + wvs.curr_time -= start; +} + +static void wvs_refresh(int hint) +{ + long tick; + unsigned oldbg, oldfg; + + tick = *rb->current_tick; + + if (hint == WVS_REFRESH_DEFAULT) { + + if ((wvs.auto_refresh & WVS_REFRESH_VIDEO) && + TIME_AFTER(tick, wvs.print_tick)) { + wvs.auto_refresh &= ~WVS_REFRESH_VIDEO; + stream_draw_frame(false); + } + + if ((wvs.auto_refresh & WVS_REFRESH_RESUME) && + TIME_AFTER(tick, wvs.resume_tick)) { + wvs.auto_refresh &= ~(WVS_REFRESH_RESUME | WVS_REFRESH_VIDEO); + stream_resume(); + } + + if (!(wvs.flags & WVS_SHOW)) + return; + + if (TIME_AFTER(tick, wvs.hide_tick)) { + wvs_show(WVS_HIDE); + return; + } + } else { + if (!(wvs.flags & WVS_SHOW)) { + wvs_show(WVS_SHOW | WVS_NODRAW); + hint = WVS_REFRESH_ALL; + } + + wvs.print_tick = tick + wvs.print_delay; + wvs.hide_tick = tick + wvs.show_for; + } + + if (TIME_AFTER(tick, wvs.next_auto_refresh)) { + wvs.next_auto_refresh = tick + WVS_MIN_UPDATE_INTERVAL; + + if (wvs.auto_refresh & WVS_REFRESH_STATUS) { + if (wvs_update_status()) + hint |= WVS_REFRESH_STATUS; + } + + if (wvs.auto_refresh & WVS_REFRESH_TIME) { + wvs_update_time(); + hint |= WVS_REFRESH_TIME; + } + } + + if (hint == 0) + return; + + oldfg = rb->lcd_get_foreground(); + oldbg = rb->lcd_get_background(); + + rb->lcd_setfont(FONT_UI); + rb->lcd_set_foreground(wvs.fgcolor); + rb->lcd_set_background(wvs.bgcolor); + +#ifdef HAVE_LCD_COLOR + vo_rect_clear(&wvs.update_rect); +#endif + + if (hint & WVS_REFRESH_BACKGROUND) { + wvs_refresh_background(); + hint |= WVS_REFRESH_ALL; /* Requires a redraw of everything */ + } + + if (hint & WVS_REFRESH_TIME) { + wvs_refresh_time(); + } + + if (hint & WVS_REFRESH_VOLUME) { + wvs_refresh_volume(); + } + + if (hint & WVS_REFRESH_STATUS) { + wvs_refresh_status(); + } + + rb->lcd_setfont(FONT_SYSFIXED); + rb->lcd_set_foreground(oldfg); + rb->lcd_set_background(oldbg); + +#ifdef HAVE_LCD_COLOR + vo_lock(); + + draw_update_rect(wvs.update_rect.l, + wvs.update_rect.t, + wvs.update_rect.r - wvs.update_rect.l, + wvs.update_rect.b - wvs.update_rect.t); + + vo_unlock(); +#else + gray_deferred_lcd_update(); +#endif +} + +static void wvs_show(unsigned show) +{ + if (((show ^ wvs.flags) & WVS_SHOW) == 0) + return; + + if (show & WVS_SHOW) { + struct vo_rect rc = { 0, 0, SCREEN_WIDTH, wvs.y }; + + wvs.flags |= WVS_SHOW; + + stream_vo_set_clip(&rc); + + if (!(show & WVS_NODRAW)) + wvs_refresh(WVS_REFRESH_ALL); + } else { + wvs.flags &= ~WVS_SHOW; + + stream_vo_set_clip(NULL); + + draw_clear_area(0, 0, wvs.width, wvs.height); + + if (!(show & WVS_NODRAW)) { +#ifdef HAVE_LCD_COLOR + vo_lock(); + draw_update_rect(0, 0, wvs.width, wvs.height); + vo_unlock(); +#endif + stream_draw_frame(false); + } + } +} + +static void wvs_set_status(int status) +{ + bool draw = (status & WVS_NODRAW) == 0; + + status &= WVS_STATUS_MASK; + + if (wvs.status != status) { + + wvs.status = status; + + if (draw) + wvs_refresh(WVS_REFRESH_STATUS); + } +} + +/* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;) */ +static uint32_t wvs_ff_rw(int btn, unsigned refresh) +{ + unsigned int step = TS_SECOND*rb->global_settings->ff_rewind_min_step; + const long ff_rw_accel = rb->global_settings->ff_rewind_accel; + long accel_tick = *rb->current_tick + ff_rw_accel*HZ; + uint32_t start; + uint32_t time = stream_get_seek_time(&start); + const uint32_t duration = stream_get_duration(); + unsigned int max_step = 0; + uint32_t ff_rw_count = 0; + unsigned status = wvs.status; + + wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME | + WVS_REFRESH_TIME); + + time -= start; /* Absolute clock => stream-relative */ + + switch (btn) + { + case MPEG_FF: + wvs_set_status(WVS_STATUS_FF); + break; + case MPEG_RW: + wvs_set_status(WVS_STATUS_RW); + break; + default: + btn = -1; + } + + btn |= BUTTON_REPEAT; + + while (1) + { + long tick = *rb->current_tick; + stream_keep_disk_active(); + + switch (btn) + { + case BUTTON_NONE: + wvs_refresh(WVS_REFRESH_DEFAULT); + break; + + case MPEG_FF | BUTTON_REPEAT: + case MPEG_RW | BUTTON_REPEAT: + break; + + case MPEG_FF | BUTTON_REL: + case MPEG_RW | BUTTON_REL: + if (wvs.status == WVS_STATUS_FF) + time += ff_rw_count; + else if (wvs.status == WVS_STATUS_RW) + time -= ff_rw_count; + + /* Fall-through */ + case -1: + default: + wvs_schedule_refresh(refresh); + wvs_set_status(status); + wvs_schedule_refresh(WVS_REFRESH_TIME); + return time; + } + + if (wvs.status == WVS_STATUS_FF) { + /* fast forwarding, calc max step relative to end */ + max_step = muldiv_uint32(duration - (time + ff_rw_count), + FF_REWIND_MAX_PERCENT, 100); + } else { + /* rewinding, calc max step relative to start */ + max_step = muldiv_uint32(time - ff_rw_count, + FF_REWIND_MAX_PERCENT, 100); + } + + max_step = MAX(max_step, MIN_FF_REWIND_STEP); + + if (step > max_step) + step = max_step; + + ff_rw_count += step; + + if (ff_rw_accel != 0 && TIME_AFTER(tick, accel_tick)) { + step *= 2; + accel_tick = tick + ff_rw_accel*HZ; + } + + if (wvs.status == WVS_STATUS_FF) { + if (duration - time <= ff_rw_count) + ff_rw_count = duration - time; + + wvs.curr_time = time + ff_rw_count; + } else { + if (time <= ff_rw_count) + ff_rw_count = time; + + wvs.curr_time = time - ff_rw_count; + } + + wvs_refresh(WVS_REFRESH_TIME); + + btn = rb->button_get_w_tmo(WVS_MIN_UPDATE_INTERVAL); + } +} + +static int wvs_status(void) +{ + int status = stream_status(); + + /* Coerce to STREAM_PLAYING if paused with a pending resume */ + if (status == STREAM_PAUSED) { + if (wvs.auto_refresh & WVS_REFRESH_RESUME) + status = STREAM_PLAYING; + } + + return status; +} + +static void wvs_set_volume(int delta) +{ + int vol = rb->global_settings->volume; + int limit; + + vol += delta; + + if (delta < 0) { + limit = rb->sound_min(SOUND_VOLUME); + if (vol < limit) + vol = limit; + } else { + limit = rb->sound_max(SOUND_VOLUME); + if (vol > limit) + vol = limit; + } + + if (vol != rb->global_settings->volume) { + rb->sound_set(SOUND_VOLUME, vol); + rb->global_settings->volume = vol; + } + + wvs_refresh(WVS_REFRESH_VOLUME); +} + +static int wvs_play(uint32_t time) +{ + int retval; + + wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME); + + retval = stream_seek(time, SEEK_SET); + + if (retval >= STREAM_OK) { + stream_show_vo(true); + retval = stream_play(); + + if (retval >= STREAM_OK) + wvs_set_status(WVS_STATUS_PLAYING | WVS_NODRAW); + } + + return retval; +} + +static int wvs_halt(void) +{ + int status = stream_pause(); + + /* Coerce to STREAM_PLAYING if paused with a pending resume */ + if (status == STREAM_PAUSED) { + if (wvs.auto_refresh & WVS_REFRESH_RESUME) + status = STREAM_PLAYING; + } + + wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME); + + return status; +} + +static int wvs_pause(void) +{ + unsigned refresh = wvs.auto_refresh; + int status = wvs_halt(); + + if (status == STREAM_PLAYING && (refresh & WVS_REFRESH_RESUME)) { + wvs_cancel_refresh(WVS_REFRESH_RESUME); + wvs_schedule_refresh(WVS_REFRESH_VIDEO); + } + + wvs_set_status(WVS_STATUS_PAUSED); + + return status; +} + +static void wvs_resume(void) +{ + wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME); + wvs_set_status(WVS_STATUS_PLAYING); + stream_resume(); +} + +static void wvs_stop(void) +{ + wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME); + wvs_show(WVS_HIDE | WVS_NODRAW); + + if (stream_stop() != STREAM_STOPPED) + settings.resume_time = stream_get_resume_time(); +} + +static void wvs_seek(int btn) +{ + int status; + unsigned refresh; + uint32_t time; + + if (!stream_can_seek()) + return; + + status = wvs_halt(); + + if (status == STREAM_STOPPED) + return; + + wvs_show(WVS_SHOW); + + if (status == STREAM_PLAYING) + refresh = WVS_REFRESH_RESUME; /* delay resume if playing */ + else + refresh = WVS_REFRESH_VIDEO; /* refresh if paused */ + + time = wvs_ff_rw(btn, refresh); + + stream_seek(time, SEEK_SET); +} + +static void button_loop(void) +{ + rb->lcd_setfont(FONT_SYSFIXED); rb->lcd_clear_display(); rb->lcd_update(); + /* Turn off backlight timeout */ + /* backlight control in lib/helper.c */ + backlight_force_on(rb); + + wvs_init(); + /* Start playback at the specified starting time */ - if (stream_seek(settings.resume_time, SEEK_SET) < STREAM_OK || - (stream_show_vo(true), stream_play()) < STREAM_OK) - { + if (wvs_play(settings.resume_time) < STREAM_OK) { rb->splash(HZ*2, "Playback failed"); - return false; + return; } /* Gently poll the video player for EOS and handle UI */ while (stream_status() != STREAM_STOPPED) { - int button = rb->button_get_w_tmo(HZ/2); + int button = rb->button_get_w_tmo(WVS_MIN_UPDATE_INTERVAL); switch (button) { case BUTTON_NONE: + { + wvs_refresh(WVS_REFRESH_DEFAULT); continue; + } /* BUTTON_NONE: */ case MPEG_VOLUP: case MPEG_VOLUP|BUTTON_REPEAT: @@ -212,14 +1194,7 @@ static bool button_loop(void) case MPEG_VOLUP2|BUTTON_REPEAT: #endif { - int vol = rb->global_settings->volume; - int maxvol = rb->sound_max(SOUND_VOLUME); - - if (vol < maxvol) { - vol++; - rb->sound_set(SOUND_VOLUME, vol); - rb->global_settings->volume = vol; - } + wvs_set_volume(+1); break; } /* MPEG_VOLUP*: */ @@ -230,23 +1205,17 @@ static bool button_loop(void) case MPEG_VOLDOWN2|BUTTON_REPEAT: #endif { - int vol = rb->global_settings->volume; - int minvol = rb->sound_min(SOUND_VOLUME); - - if (vol > minvol) { - vol--; - rb->sound_set(SOUND_VOLUME, vol); - rb->global_settings->volume = vol; - } + wvs_set_volume(-1); break; } /* MPEG_VOLDOWN*: */ case MPEG_MENU: { - int state = stream_pause(); /* save previous state */ + int state = wvs_halt(); /* save previous state */ int result; /* Hide video output */ + wvs_show(WVS_HIDE | WVS_NODRAW); stream_show_vo(false); backlight_use_settings(rb); @@ -258,17 +1227,19 @@ static bool button_loop(void) switch (result) { case MPEG_MENU_QUIT: - stream_stop(); + wvs_stop(); break; default: /* If not stopped, show video again */ - if (state != STREAM_STOPPED) + if (state != STREAM_STOPPED) { + wvs_show(WVS_SHOW); stream_show_vo(true); + } /* If stream was playing, restart it */ if (state == STREAM_PLAYING) { backlight_force_on(rb); - stream_resume(); + wvs_resume(); } break; } @@ -277,7 +1248,7 @@ static bool button_loop(void) case MPEG_STOP: { - stream_stop(); + wvs_stop(); break; } /* MPEG_STOP: */ @@ -286,27 +1257,34 @@ static bool button_loop(void) case MPEG_PAUSE2: #endif { - if (stream_status() == STREAM_PLAYING) { + int status = wvs_status(); + + if (status == STREAM_PLAYING) { /* Playing => Paused */ - stream_pause(); + wvs_pause(); backlight_use_settings(rb); } - else if (stream_status() == STREAM_PAUSED) { + else if (status == STREAM_PAUSED) { /* Paused => Playing */ backlight_force_on(rb); - stream_resume(); + wvs_resume(); } break; } /* MPEG_PAUSE*: */ + case MPEG_RW: + case MPEG_FF: + { + wvs_seek(button); + break; + } /* MPEG_RW: MPEG_FF: */ + case SYS_POWEROFF: case SYS_USB_CONNECTED: /* Stop and get the resume time before closing the file early */ - stream_stop(); - settings.resume_time = stream_get_resume_time(); + wvs_stop(); stream_close(); - ret = false; /* Fall-through */ default: rb->default_event_handler(button); @@ -316,9 +1294,12 @@ static bool button_loop(void) rb->yield(); } /* end while */ + wvs_stop(); + rb->lcd_setfont(FONT_UI); - return ret; + /* Turn on backlight timeout (revert to settings) */ + backlight_use_settings(rb); } enum 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) result = mpeg_start_menu(stream_get_duration()); if (result != MPEG_START_QUIT) { - /* Turn off backlight timeout */ - /* backlight control in lib/helper.c */ - backlight_force_on(rb); - /* Enter button loop and process UI */ - if (button_loop()) { - settings.resume_time = stream_get_resume_time(); - } - - /* Turn on backlight timeout (revert to settings) */ - backlight_use_settings(rb); + button_loop(); } 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) static uint32_t time_from_whence(uint32_t time, int whence) { int64_t currtime; + uint32_t start; switch (whence) { @@ -262,12 +263,8 @@ static uint32_t time_from_whence(uint32_t time, int whence) case SEEK_CUR: /* Seek forward or backward from the current time * (time = signed offset from current) */ - if (stream_mgr.seeked) - currtime = str_parser.last_seek_time; - else - currtime = TICKS_TO_TS(pcm_output_get_clock()); - - currtime -= str_parser.start_pts; + currtime = stream_get_seek_time(&start); + currtime -= start; currtime += (int32_t)time; if (currtime < 0) @@ -525,27 +522,28 @@ static void stream_on_stop(bool reply) if (status != STREAM_STOPPED) { - /* Not stopped = paused or playing */ - stream_mgr.seeked = false; - /* Pause the clock */ pcm_output_play_pause(false); + /* Assume invalidity */ + stream_mgr.resume_time = 0; + if (stream_can_seek()) { - /* Read the current stream time */ - uint32_t time = TICKS_TO_TS(pcm_output_get_clock()); - - /* Assume invalidity */ - stream_mgr.resume_time = 0; + /* Read the current stream time or the last seeked position */ + uint32_t start; + uint32_t time = stream_get_seek_time(&start); if (time >= str_parser.start_pts && time < str_parser.end_pts) { /* Save the current stream time */ - stream_mgr.resume_time = time - str_parser.start_pts; + stream_mgr.resume_time = time - start; } } + /* Not stopped = paused or playing */ + stream_mgr.seeked = false; + /* Stop buffering */ disk_buf_send_msg(STREAM_STOP, 0); @@ -578,10 +576,10 @@ static void stream_on_seek(struct stream_seek_data *skd) if (stream_mgr.filename == NULL) break; - stream_mgr_reply_msg(STREAM_OK); - stream_keep_disk_active(); + stream_mgr_reply_msg(STREAM_OK); + stream_mgr_lock(); if (!stream_can_seek()) @@ -705,9 +703,11 @@ bool stream_show_vo(bool show) vis = parser_send_video_msg(VIDEO_DISPLAY_SHOW, show); #ifndef HAVE_LCD_COLOR - GRAY_VIDEO_FLUSH_ICACHE(); + GRAY_VIDEO_INVALIDATE_ICACHE(); GRAY_INVALIDATE_ICACHE(); + gray_show(show); + GRAY_FLUSH_ICACHE(); #endif stream_mgr_unlock(); @@ -743,6 +743,32 @@ bool stream_vo_get_size(struct vo_ext *sz) return retval; } +void stream_vo_set_clip(const struct vo_rect *rc) +{ + stream_mgr_lock(); + + if (rc) + { + stream_mgr.parms.rc = *rc; + rc = &stream_mgr.parms.rc; + } +#ifndef HAVE_LCD_COLOR + else + { + vo_rect_set_ext(&stream_mgr.parms.rc, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + rc = &stream_mgr.parms.rc; + } +#endif + + parser_send_video_msg(VIDEO_SET_CLIP_RECT, (intptr_t)rc); + +#ifndef HAVE_LCD_COLOR + stream_set_gray_rect(rc); +#endif + + stream_mgr_unlock(); +} + #ifndef HAVE_LCD_COLOR /* Set the rectangle for the gray video overlay - clipped to screen */ bool stream_set_gray_rect(const struct vo_rect *rc) @@ -756,21 +782,38 @@ bool stream_set_gray_rect(const struct vo_rect *rc) if (vo_rect_intersect(&rc_gray, &rc_gray, rc)) { - bool vo_vis = stream_show_vo(false); + bool vis = parser_send_video_msg(VIDEO_DISPLAY_SHOW, false); - GRAY_VIDEO_FLUSH_ICACHE(); + /* The impudence! Keeps the image from disappearing anyway. */ +#ifdef SIMULATOR + rb->sim_lcd_ex_init(0, NULL); +#else + rb->timer_unregister(); +#endif + GRAY_VIDEO_INVALIDATE_ICACHE(); GRAY_INVALIDATE_ICACHE(); + vo_lock(); + gray_init(rb, stream_mgr.graymem, stream_mgr.graysize, false, rc_gray.r - rc_gray.l, rc_gray.b - rc_gray.t, 32, 2<<8, NULL); gray_set_position(rc_gray.l, rc_gray.t); - GRAY_FLUSH_ICACHE(); - if (vo_vis) + vo_unlock(); + + GRAY_INVALIDATE_ICACHE(); + + if (stream_mgr.status != STREAM_PLAYING) + parser_send_video_msg(VIDEO_PRINT_FRAME, true); + + GRAY_VIDEO_FLUSH_ICACHE(); + + if (vis) { - stream_show_vo(true); + gray_show(true); + parser_send_video_msg(VIDEO_DISPLAY_SHOW, true); } } @@ -784,9 +827,11 @@ void stream_gray_show(bool show) { stream_mgr_lock(); - GRAY_VIDEO_FLUSH_ICACHE(); + GRAY_VIDEO_INVALIDATE_ICACHE(); GRAY_INVALIDATE_ICACHE(); + gray_show(show); + GRAY_FLUSH_ICACHE(); stream_mgr_unlock(); @@ -803,11 +848,32 @@ bool stream_display_thumb(const struct vo_rect *rc) stream_mgr_lock(); + GRAY_INVALIDATE_ICACHE(); + stream_mgr.parms.rc = *rc; retval = parser_send_video_msg(VIDEO_PRINT_THUMBNAIL, (intptr_t)&stream_mgr.parms.rc); + GRAY_VIDEO_FLUSH_ICACHE(); + stream_mgr_unlock(); + + return retval; +} + +bool stream_draw_frame(bool no_prepare) +{ + bool retval; + stream_mgr_lock(); + + GRAY_INVALIDATE_ICACHE(); + + retval = parser_send_video_msg(VIDEO_PRINT_FRAME, no_prepare); + + GRAY_VIDEO_FLUSH_ICACHE(); + + stream_mgr_unlock(); + return retval; } @@ -826,6 +892,25 @@ uint32_t stream_get_resume_time(void) return resume_time; } +uint32_t stream_get_seek_time(uint32_t *start) +{ + uint32_t time; + + stream_mgr_lock(); + + if (stream_mgr.seeked) + time = str_parser.last_seek_time; + else + time = TICKS_TO_TS(pcm_output_get_clock()); + + if (start != NULL) + *start = str_parser.start_pts; + + stream_mgr_unlock(); + + return time; +} + /* Returns the smallest file window that includes all active streams' * windows */ static 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); /* Show/Hide the video image at the current seekpoint */ bool stream_show_vo(bool show); +/* Set the visible section of video */ +void stream_vo_set_clip(const struct vo_rect *rc); + #ifndef HAVE_LCD_COLOR /* Set the gray overlay rectangle */ bool stream_set_gray_rect(const struct vo_rect *rc); @@ -114,12 +117,19 @@ void stream_gray_show(bool show); /* Display thumbnail of the current seekpoint */ bool stream_display_thumb(const struct vo_rect *rc); +/* Draw the frame at the current position */ +bool stream_draw_frame(bool no_prepare); + /* Return video dimensions */ bool stream_vo_get_size(struct vo_ext *sz); /* Returns the resume time in timestamp ticks */ uint32_t stream_get_resume_time(void); +/* Returns stream_get_time if no seek is pending or else the + last time give to seek */ +uint32_t stream_get_seek_time(uint32_t *start); + /* Return the absolute stream time in clock ticks - adjusted by * master clock stream via audio timestamps */ static 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 VIDEO_GET_SIZE, /* Get the video dimensions */ VIDEO_PRINT_FRAME, /* Print the frame at the current position */ VIDEO_PRINT_THUMBNAIL, /* Print a thumbnail of the current position */ + VIDEO_SET_CLIP_RECT, /* Set the visible video area */ #ifdef GRAY_CACHE_MAINT VIDEO_GRAY_CACHEOP, #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 @@ #ifndef VIDEO_OUT_H #define VIDEO_OUT_H +#if LCD_WIDTH >= LCD_HEIGHT +#define SCREEN_WIDTH LCD_WIDTH +#define SCREEN_HEIGHT LCD_HEIGHT +#define LCD_LANDSCAPE +#else /* Assume the screen is rotated on portrait LCDs */ +#define SCREEN_WIDTH LCD_HEIGHT +#define SCREEN_HEIGHT LCD_WIDTH +#define LCD_PORTRAIT +#endif + /* Structure to hold width and height values */ struct vo_ext { @@ -43,9 +53,18 @@ bool vo_init (void); bool vo_show (bool show); bool vo_is_visible(void); void vo_setup (const mpeg2_sequence_t * sequence); +void vo_set_clip_rect(const struct vo_rect *rc); void vo_dimensions(struct vo_ext *sz); void vo_cleanup (void); +#if NUM_CORES > 1 || !defined (HAVE_LCD_COLOR) +void vo_lock(void); +void vo_unlock(void); +#else +static inline void vo_lock(void) {} +static inline void vo_unlock(void) {} +#endif + /* Sets all coordinates of a vo_rect to 0 */ void vo_rect_clear(struct vo_rect *rc); /* Returns true if left >= right or top >= bottom */ @@ -68,4 +87,10 @@ bool vo_rect_intersect(struct vo_rect *rc_dst, const struct vo_rect *rc1, const struct vo_rect *rc2); +bool vo_rect_union(struct vo_rect *rc_dst, + const struct vo_rect *rc1, + const struct vo_rect *rc2); + +void vo_rect_offset(struct vo_rect *rc, int dx, int dy); + #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 @@ #include "plugin.h" #include "mpegplayer.h" +#define VO_NON_NULL_RECT 0x1 +#define VO_VISIBLE 0x2 + struct vo_data { int image_width; @@ -34,8 +37,9 @@ struct vo_data int output_width; int output_height; bool visible; - bool thumb_mode; - void *last; + unsigned flags; + struct vo_rect rc_vid; + struct vo_rect rc_clip; }; #ifdef PROC_NEEDS_CACHEALIGN @@ -48,10 +52,40 @@ static uint8_t __vo_data[CACHEALIGN_UP(sizeof(struct vo_data))] static struct vo_data vo; #endif +#if NUM_CORES > 1 +static struct mutex vo_mtx NOCACHEBSS_ATTR; +#endif + +static inline void video_lock_init(void) +{ +#if NUM_CORES > 1 + rb->mutex_init(&vo_mtx); +#endif +} + +static inline void video_lock(void) +{ +#if NUM_CORES > 1 + rb->mutex_lock(&vo_mtx); +#endif +} + +static inline void video_unlock(void) +{ +#if NUM_CORES > 1 + rb->mutex_unlock(&vo_mtx); +#endif +} + + /* Draw a black rectangle if no video frame is available */ static void vo_draw_black(void) { - int foreground = lcd_(get_foreground)(); + int foreground; + + video_lock(); + + foreground = lcd_(get_foreground)(); lcd_(set_foreground)(DRAW_BLACK); @@ -61,21 +95,27 @@ static void vo_draw_black(void) vo.output_height); lcd_(set_foreground)(foreground); + + video_unlock(); } static inline void yuv_blit(uint8_t * const * buf, int src_x, int src_y, int stride, int x, int y, int width, int height) { + video_lock(); + #ifdef HAVE_LCD_COLOR rb->lcd_yuv_blit(buf, src_x, src_y, stride, x, y , width, height); #else gray_ub_gray_bitmap_part(buf[0], src_x, src_y, stride, x, y, width, height); #endif + + video_unlock(); } void vo_draw_frame(uint8_t * const * buf) { - if (!vo.visible) + if (vo.flags == 0) { /* Frame is hidden - copout */ DEBUGF("vo hidden\n"); @@ -94,14 +134,6 @@ void vo_draw_frame(uint8_t * const * buf) vo.output_height); } -#if LCD_WIDTH >= LCD_HEIGHT -#define SCREEN_WIDTH LCD_WIDTH -#define SCREEN_HEIGHT LCD_HEIGHT -#else /* Assume the screen is rotated on portrait LCDs */ -#define SCREEN_WIDTH LCD_HEIGHT -#define SCREEN_HEIGHT LCD_WIDTH -#endif - static inline void vo_rect_clear_inl(struct vo_rect *rc) { rc->l = rc->t = rc->r = rc->b = 0; @@ -172,6 +204,48 @@ bool vo_rect_intersect(struct vo_rect *rc_dst, return false; } +bool vo_rect_union(struct vo_rect *rc_dst, + const struct vo_rect *rc1, + const struct vo_rect *rc2) +{ + if (rc_dst != NULL) + { + if (!vo_rect_empty_inl(rc1)) + { + if (!vo_rect_empty_inl(rc2)) + { + rc_dst->l = MIN(rc1->l, rc2->l); + rc_dst->t = MIN(rc1->t, rc2->t); + rc_dst->r = MAX(rc1->r, rc2->r); + rc_dst->b = MAX(rc1->b, rc2->b); + } + else + { + *rc_dst = *rc1; + } + + return true; + } + else if (!vo_rect_empty(rc2)) + { + *rc_dst = *rc2; + return true; + } + + vo_rect_clear_inl(rc_dst); + } + + return false; +} + +void vo_rect_offset(struct vo_rect *rc, int dx, int dy) +{ + rc->l += dx; + rc->t += dy; + rc->r += dx; + rc->b += dy; +} + /* Shink or stretch each axis - rotate counter-clockwise to retain upright * orientation on rotated displays (they rotate clockwise) */ void stretch_image_plane(const uint8_t * src, uint8_t *dst, int stride, @@ -350,25 +424,27 @@ void vo_setup(const mpeg2_sequence_t * sequence) if (sequence->display_width >= SCREEN_WIDTH) { - vo.output_width = SCREEN_WIDTH; - vo.output_x = 0; + vo.rc_vid.l = 0; + vo.rc_vid.r = SCREEN_WIDTH; } else { - vo.output_width = sequence->display_width; - vo.output_x = (SCREEN_WIDTH - sequence->display_width) / 2; + vo.rc_vid.l = (SCREEN_WIDTH - sequence->display_width) / 2; + vo.rc_vid.r = vo.rc_vid.l + sequence->display_width; } if (sequence->display_height >= SCREEN_HEIGHT) { - vo.output_height = SCREEN_HEIGHT; - vo.output_y = 0; + vo.rc_vid.t = 0; + vo.rc_vid.b = SCREEN_HEIGHT; } else { - vo.output_height = sequence->display_height; - vo.output_y = (SCREEN_HEIGHT - sequence->display_height) / 2; + vo.rc_vid.t = (SCREEN_HEIGHT - sequence->display_height) / 2; + vo.rc_vid.b = vo.rc_vid.t + sequence->display_height; } + + vo_set_clip_rect(&vo.rc_clip); } void vo_dimensions(struct vo_ext *sz) @@ -379,23 +455,68 @@ void vo_dimensions(struct vo_ext *sz) bool vo_init(void) { - vo.visible = false; + vo.flags = 0; + vo_rect_set_ext(&vo.rc_clip, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + video_lock_init(); return true; } bool vo_show(bool show) { - bool vis = vo.visible; - vo.visible = show; + bool vis = vo.flags & VO_VISIBLE; + + if (show) + vo.flags |= VO_VISIBLE; + else + vo.flags &= ~VO_VISIBLE; + return vis; } bool vo_is_visible(void) { - return vo.visible; + return vo.flags & VO_VISIBLE; } void vo_cleanup(void) { - vo.visible = false; + vo.flags = 0; } + +void vo_set_clip_rect(const struct vo_rect *rc) +{ + struct vo_rect rc_out; + + if (rc == NULL) + vo_rect_set_ext(&vo.rc_clip, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + else + vo.rc_clip = *rc; + + if (!vo_rect_intersect(&rc_out, &vo.rc_vid, &vo.rc_clip)) + vo.flags &= ~VO_NON_NULL_RECT; + else + vo.flags |= VO_NON_NULL_RECT; + + vo.output_x = rc_out.l; + vo.output_y = rc_out.t; + vo.output_width = rc_out.r - rc_out.l; + vo.output_height = rc_out.b - rc_out.t; +} + +#if NUM_CORES > 1 || !defined (HAVE_LCD_COLOR) +void vo_lock(void) +{ +#ifndef HAVE_LCD_COLOR + set_irq_level(HIGHEST_IRQ_LEVEL); +#endif + video_lock(); +} + +void vo_unlock(void) +{ + video_unlock(); +#ifndef HAVE_LCD_COLOR + set_irq_level(0); +#endif +} +#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) fps / 100, fps % 100, td->num_skipped, td->info->display_picture->temporal_reference); rb->lcd_putsxy(0, 0, str); + + vo_lock(); rb->lcd_update_rect(0, 0, LCD_WIDTH, 8); + vo_unlock(); td->last_showfps = *rb->current_tick; } @@ -522,7 +525,9 @@ static void video_thread_msg(struct video_thread_data *td) else { IF_COP(invalidate_icache()); + vo_lock(); rb->lcd_update(); + vo_unlock(); } #else GRAY_FLUSH_ICACHE(); @@ -580,6 +585,10 @@ static void video_thread_msg(struct video_thread_data *td) } break; + case VIDEO_SET_CLIP_RECT: + vo_set_clip_rect((const struct vo_rect *)td->ev.data); + break; + case VIDEO_PRINT_FRAME: /* Print the last frame decoded */ if (td->info != NULL && td->info->display_fbuf != NULL) diff --git a/uisimulator/sdl/lcd-bitmap.c b/uisimulator/sdl/lcd-bitmap.c index 12cc063288..7b6e2217c6 100644 --- a/uisimulator/sdl/lcd-bitmap.c +++ b/uisimulator/sdl/lcd-bitmap.c @@ -359,7 +359,7 @@ void lcd_yuv_blit(unsigned char * const src[3], #if LCD_WIDTH >= LCD_HEIGHT lcd_update_rect(x, y, width, height); #else - lcd_update_rect(y, x, height, width); + lcd_update_rect(LCD_WIDTH - y - height, x, height, width); #endif } #endif /* HAVE_LCD_COLOR */ -- cgit v1.2.3