From b664f62e36b5f0ac296567e423816dab3811075d Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Mon, 3 Jan 2011 16:41:19 +0000 Subject: MPEGPlayer graphics mutation: Implement a more visible FPS display and remove the debugging info from it. Tweak thumbnailing and printing of unavailable frames. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28960 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/mpegplayer/mpeg_settings.c | 31 +++++-- apps/plugins/mpegplayer/mpegplayer.c | 130 ++++++++++++++++++++++++++-- apps/plugins/mpegplayer/stream_mgr.c | 40 ++++++++- apps/plugins/mpegplayer/stream_mgr.h | 8 ++ apps/plugins/mpegplayer/stream_thread.h | 13 +++ apps/plugins/mpegplayer/video_out.h | 2 + apps/plugins/mpegplayer/video_out_rockbox.c | 73 ++++++++++++---- apps/plugins/mpegplayer/video_thread.c | 129 +++++++++++++-------------- 8 files changed, 333 insertions(+), 93 deletions(-) (limited to 'apps') diff --git a/apps/plugins/mpegplayer/mpeg_settings.c b/apps/plugins/mpegplayer/mpeg_settings.c index f84a30ddfe..1b1ac8e2f0 100644 --- a/apps/plugins/mpegplayer/mpeg_settings.c +++ b/apps/plugins/mpegplayer/mpeg_settings.c @@ -601,20 +601,37 @@ static void draw_slider(uint32_t range, uint32_t pos, struct vo_rect *rc) static bool display_thumb_image(const struct vo_rect *rc) { + bool retval = true; + unsigned ltgray = MYLCD_LIGHTGRAY; + unsigned dkgray = MYLCD_DARKGRAY; + + int oldcolor = mylcd_get_foreground(); + if (!stream_display_thumb(rc)) { - mylcd_splash(0, "Frame not available"); - return false; + /* Display "No Frame" and erase any border */ + const char * const str = "No Frame"; + int x, y, w, h; + + mylcd_getstringsize(str, &w, &h); + x = (rc->r + rc->l - w) / 2; + y = (rc->b + rc->t - h) / 2; + mylcd_putsxy(x, y, str); + + mylcd_update_rect(x, y, w, h); + + ltgray = dkgray = mylcd_get_background(); + retval = false; } - /* Draw a raised border around the frame */ - int oldcolor = mylcd_get_foreground(); - mylcd_set_foreground(MYLCD_LIGHTGRAY); + /* Draw a raised border around the frame (or erase if no frame) */ + + mylcd_set_foreground(ltgray); mylcd_hline(rc->l-1, rc->r-1, rc->t-1); mylcd_vline(rc->l-1, rc->t, rc->b-1); - mylcd_set_foreground(MYLCD_DARKGRAY); + mylcd_set_foreground(dkgray); mylcd_hline(rc->l-1, rc->r, rc->b); mylcd_vline(rc->r, rc->t-1, rc->b); @@ -626,7 +643,7 @@ static bool display_thumb_image(const struct vo_rect *rc) mylcd_update_rect(rc->l-1, rc->b, rc->r - rc->l + 2, 1); mylcd_update_rect(rc->r, rc->t, 1, rc->b - rc->t); - return true; + return retval; } /* Add an amount to the specified time - with saturation */ diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c index d21907f607..5ea17f9a3e 100644 --- a/apps/plugins/mpegplayer/mpegplayer.c +++ b/apps/plugins/mpegplayer/mpegplayer.c @@ -380,6 +380,7 @@ CONFIG_KEYPAD == SANSA_M200_PAD /* 3% of 30min file == 54s step size */ #define MIN_FF_REWIND_STEP (TS_SECOND/2) #define OSD_MIN_UPDATE_INTERVAL (HZ/2) +#define FPS_UPDATE_INTERVAL (HZ) /* Get new FPS reading each second */ enum video_action { @@ -457,9 +458,25 @@ struct osd uint32_t curr_time; unsigned auto_refresh; unsigned flags; + int font; +}; + +struct fps +{ + /* FPS Display */ + struct vo_rect rect; /* OSD coordinates */ + int pf_x; /* Screen coordinates */ + int pf_y; + int pf_width; + int pf_height; + long update_tick; /* When to next update FPS reading */ + #define FPS_FORMAT "%d.%02d" + #define FPS_DIMSTR "999.99" /* For establishing rect size */ + #define FPS_BUFSIZE sizeof("999.99") }; static struct osd osd; +static struct fps fps NOCACHEBSS_ATTR; /* Accessed on other processor */ static void osd_show(unsigned show); @@ -573,6 +590,12 @@ static void draw_scrollbar_draw_rect(const struct vo_rect *rc, int min, min, max, val); } +static void draw_setfont(int font) +{ + osd.font = font; + mylcd_setfont(font); +} + #ifdef LCD_PORTRAIT /* Portrait displays need rotated text rendering */ @@ -646,7 +669,7 @@ 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); + struct font* pf = rb->font_get(osd.font); ucs = rb->bidi_l2v(str, 1); @@ -696,6 +719,96 @@ static void draw_putsxy_oriented(int x, int y, const char *str) } #endif /* LCD_PORTRAIT */ +/** FPS Display **/ + +/* Post-frame callback (on video thread) - update the FPS rectangle from the + * framebuffer */ +static void fps_post_frame_callback(void) +{ + vo_lock(); + mylcd_update_rect(fps.pf_x, fps.pf_y, + fps.pf_width, fps.pf_height); + vo_unlock(); +} + +/* Set up to have the callback only update the intersection of the video + * rectangle and the FPS text rectangle - if they don't intersect, then + * the callback is set to NULL */ +static void fps_update_post_frame_callback(void) +{ + void (*cb)(void) = NULL; + + if (settings.showfps) { + struct vo_rect cliprect; + + if (stream_vo_get_clip(&cliprect)) { + /* Oriented screen coordinates -> OSD coordinates */ + vo_rect_offset(&cliprect, -osd.x, -osd.y); + + if (vo_rect_intersect(&cliprect, &cliprect, &fps.rect)) { + int x = cliprect.l; + int y = cliprect.t; + int width = cliprect.r - cliprect.l; + int height = cliprect.b - cliprect.t; + + /* OSD coordinates -> framebuffer coordinates */ + fps.pf_x = _X; + fps.pf_y = _Y; + fps.pf_width = _W; + fps.pf_height = _H; + + cb = fps_post_frame_callback; + } + } + } + + stream_set_callback(VIDEO_SET_POST_FRAME_CALLBACK, cb); +} + +/* Refresh the FPS display */ +static void fps_refresh(void) +{ + char str[FPS_BUFSIZE]; + struct video_output_stats stats; + int w, h, sw; + long tick; + + tick = *rb->current_tick; + + if (TIME_BEFORE(tick, fps.update_tick)) + return; + + fps.update_tick = tick + FPS_UPDATE_INTERVAL; + + stream_video_stats(&stats); + + rb->snprintf(str, FPS_BUFSIZE, FPS_FORMAT, + stats.fps / 100, stats.fps % 100); + + w = fps.rect.r - fps.rect.l; + h = fps.rect.b - fps.rect.t; + + draw_clear_area(fps.rect.l, fps.rect.t, w, h); + mylcd_getstringsize(str, &sw, NULL); + draw_putsxy_oriented(fps.rect.r - sw, fps.rect.t, str); + + vo_lock(); + draw_update_rect(fps.rect.l, fps.rect.t, w, h); + vo_unlock(); +} + +/* Initialize the FPS display */ +static void fps_init(void) +{ + fps.update_tick = *rb->current_tick; + fps.rect.l = fps.rect.t = 0; + mylcd_getstringsize(FPS_DIMSTR, &fps.rect.r, &fps.rect.b); + vo_rect_offset(&fps.rect, -osd.x, -osd.y); + fps_update_post_frame_callback(); +} + +/** OSD **/ + #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) /* So we can refresh the overlay */ static void osd_lcd_enable_hook(void* param) @@ -743,7 +856,7 @@ static void osd_text_init(void) int phys; int spc_width; - mylcd_setfont(FONT_UI); + draw_setfont(FONT_UI); osd.x = 0; osd.width = SCREEN_WIDTH; @@ -812,7 +925,7 @@ static void osd_text_init(void) #endif osd.y = SCREEN_HEIGHT - osd.height; - mylcd_setfont(FONT_SYSFIXED); + draw_setfont(FONT_SYSFIXED); } static void osd_init(void) @@ -835,6 +948,7 @@ static void osd_init(void) osd.auto_refresh = OSD_REFRESH_TIME; osd.next_auto_refresh = *rb->current_tick; osd_text_init(); + fps_init(); } #ifdef HAVE_HEADPHONE_DETECTION @@ -1047,6 +1161,9 @@ static void osd_refresh(int hint) tick = *rb->current_tick; + if (settings.showfps) + fps_refresh(); + if (hint == OSD_REFRESH_DEFAULT) { /* The default which forces no updates */ @@ -1116,7 +1233,7 @@ static void osd_refresh(int hint) oldfg = mylcd_get_foreground(); oldbg = mylcd_get_background(); - mylcd_setfont(FONT_UI); + draw_setfont(FONT_UI); mylcd_set_foreground(osd.fgcolor); mylcd_set_background(osd.bgcolor); @@ -1140,7 +1257,7 @@ static void osd_refresh(int hint) } /* Go back to defaults */ - mylcd_setfont(FONT_SYSFIXED); + draw_setfont(FONT_SYSFIXED); mylcd_set_foreground(oldfg); mylcd_set_background(oldbg); @@ -1391,6 +1508,7 @@ static int osd_play(uint32_t time) osd_backlight_on_video_mode(true); osd_backlight_brightness_video_mode(true); stream_show_vo(true); + retval = stream_play(); if (retval >= STREAM_OK) @@ -1747,6 +1865,8 @@ static int button_loop(void) next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT; + fps_update_post_frame_callback(); + /* The menu can change the font, so restore */ rb->lcd_setfont(FONT_SYSFIXED); #ifdef HAVE_LCD_COLOR diff --git a/apps/plugins/mpegplayer/stream_mgr.c b/apps/plugins/mpegplayer/stream_mgr.c index cee384fb75..6607402d5a 100644 --- a/apps/plugins/mpegplayer/stream_mgr.c +++ b/apps/plugins/mpegplayer/stream_mgr.c @@ -695,8 +695,8 @@ static intptr_t send_video_msg(long id, intptr_t data) if (disk_buf_status() != STREAM_STOPPED) break; /* Prepare image if not playing */ - if (!parser_prepare_image(str_parser.last_seek_time)) - return false; /* Preparation failed */ + /* Ignore return and try video thread anyway */ + parser_prepare_image(str_parser.last_seek_time); /* Image ready - pass message to video thread */ break; @@ -766,6 +766,25 @@ void stream_vo_set_clip(const struct vo_rect *rc) stream_mgr_unlock(); } +bool stream_vo_get_clip(struct vo_rect *rc) +{ + bool retval; + + if (!rc) + return false; + + stream_mgr_lock(); + + retval = send_video_msg(VIDEO_GET_CLIP_RECT, + (intptr_t)&stream_mgr.parms.rc); + + *rc = stream_mgr.parms.rc; + + stream_mgr_unlock(); + + return retval; +} + #ifndef HAVE_LCD_COLOR /* Show/hide the gray video overlay (independently of vo visibility). */ void stream_gray_show(bool show) @@ -810,6 +829,23 @@ bool stream_draw_frame(bool no_prepare) return retval; } +bool stream_set_callback(long id, void *fn) +{ + bool retval = false; + + stream_mgr_lock(); + + switch (id) + { + case VIDEO_SET_POST_FRAME_CALLBACK: + retval = send_video_msg(id, (intptr_t)fn); + } + + stream_mgr_unlock(); + + return retval; +} + /* Return the time playback should resume if interrupted */ uint32_t stream_get_resume_time(void) { diff --git a/apps/plugins/mpegplayer/stream_mgr.h b/apps/plugins/mpegplayer/stream_mgr.h index a07305a847..7dba9acc09 100644 --- a/apps/plugins/mpegplayer/stream_mgr.h +++ b/apps/plugins/mpegplayer/stream_mgr.h @@ -106,6 +106,9 @@ bool stream_show_vo(bool show); /* Set the visible section of video */ void stream_vo_set_clip(const struct vo_rect *rc); +/* Return current visible section of video */ +bool stream_vo_get_clip(struct vo_rect *rc); + #ifndef HAVE_LCD_COLOR void stream_gray_show(bool show); #endif @@ -149,6 +152,11 @@ static inline uint32_t stream_get_duration(void) static inline bool stream_can_seek(void) { return parser_can_seek(); } +static inline void stream_video_stats(struct video_output_stats *s) + { video_thread_get_stats(s); } + +bool stream_set_callback(long id, void * fn); + /* Keep the disk spinning (for seeking and browsing) */ static inline void stream_keep_disk_active(void) { diff --git a/apps/plugins/mpegplayer/stream_thread.h b/apps/plugins/mpegplayer/stream_thread.h index 1d3a445735..5791a49e7f 100644 --- a/apps/plugins/mpegplayer/stream_thread.h +++ b/apps/plugins/mpegplayer/stream_thread.h @@ -108,6 +108,8 @@ enum stream_message 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 */ + VIDEO_GET_CLIP_RECT, /* Return the visible video area */ + VIDEO_SET_POST_FRAME_CALLBACK, /* Set a callback after frame is drawn */ STREAM_MESSAGE_LAST, }; @@ -160,9 +162,20 @@ extern struct stream audio_str IBSS_ATTR; bool video_thread_init(void); void video_thread_exit(void); + +struct video_output_stats +{ + int num_drawn; /* Number of frames drawn since reset */ + int num_skipped; /* Number of frames skipped since reset */ + int fps; /* fps rate in 100ths of a frame per second */ +}; + +void video_thread_get_stats(struct video_output_stats *s); + bool audio_thread_init(void); void audio_thread_exit(void); + /* Some queue function wrappers to keep things clean-ish */ /* For stream use only */ diff --git a/apps/plugins/mpegplayer/video_out.h b/apps/plugins/mpegplayer/video_out.h index 808f233ac1..2a3364c382 100644 --- a/apps/plugins/mpegplayer/video_out.h +++ b/apps/plugins/mpegplayer/video_out.h @@ -58,8 +58,10 @@ 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); +bool vo_get_clip_rect(struct vo_rect *rc); void vo_dimensions(struct vo_ext *sz); void vo_cleanup (void); +void vo_set_post_draw_callback(void (*cb)(void)); #if NUM_CORES > 1 void vo_lock(void); diff --git a/apps/plugins/mpegplayer/video_out_rockbox.c b/apps/plugins/mpegplayer/video_out_rockbox.c index fe3deafd01..214bdf3b5c 100644 --- a/apps/plugins/mpegplayer/video_out_rockbox.c +++ b/apps/plugins/mpegplayer/video_out_rockbox.c @@ -41,6 +41,7 @@ struct vo_data unsigned flags; struct vo_rect rc_vid; struct vo_rect rc_clip; + void (*post_draw_callback)(void); }; #if NUM_CORES > 1 @@ -80,9 +81,10 @@ static inline void video_unlock(void) /* Draw a black rectangle if no video frame is available */ -static void vo_draw_black(void) +static void vo_draw_black(struct vo_rect *rc) { int foreground; + int x, y, w, h; video_lock(); @@ -90,10 +92,30 @@ static void vo_draw_black(void) mylcd_set_foreground(MYLCD_BLACK); - mylcd_fillrect(vo.output_x, vo.output_y, vo.output_width, - vo.output_height); - mylcd_update_rect(vo.output_x, vo.output_y, vo.output_width, - vo.output_height); + if (rc) + { + x = rc->l; + y = rc->t; + w = rc->r - rc->l; + h = rc->b - rc->t; + } + else + { +#if LCD_WIDTH >= LCD_HEIGHT + x = vo.output_x; + y = vo.output_y; + w = vo.output_width; + h = vo.output_height; +#else + x = LCD_WIDTH - vo.output_height - vo.output_y; + y = vo.output_x; + w = vo.output_height; + h = vo.output_width; +#endif + } + + mylcd_fillrect(x, y, w, h); + mylcd_update_rect(x, y, w, h); mylcd_set_foreground(foreground); @@ -122,19 +144,22 @@ void vo_draw_frame(uint8_t * const * buf) /* Frame is hidden - either by being set invisible or is clipped * away - copout */ DEBUGF("vo hidden\n"); - return; } else if (buf == NULL) { /* No frame exists - draw black */ - vo_draw_black(); + vo_draw_black(NULL); DEBUGF("vo no frame\n"); - return; + } + else + { + yuv_blit(buf, 0, 0, vo.image_width, + vo.output_x, vo.output_y, vo.output_width, + vo.output_height); } - yuv_blit(buf, 0, 0, vo.image_width, - vo.output_x, vo.output_y, vo.output_width, - vo.output_height); + if (vo.post_draw_callback) + vo.post_draw_callback(); } static inline void vo_rect_clear_inl(struct vo_rect *rc) @@ -348,14 +373,14 @@ bool vo_draw_frame_thumb(uint8_t * const * buf, const struct vo_rect *rc) int thumb_width, thumb_height; int thumb_uv_width, thumb_uv_height; - if (buf == NULL) - return false; - /* Obtain rectangle as clipped to the screen */ vo_rect_set_ext(&thumb_rc, 0, 0, LCD_WIDTH, LCD_HEIGHT); if (!vo_rect_intersect(&thumb_rc, rc, &thumb_rc)) return true; + if (buf == NULL) + goto no_thumb_exit; + DEBUGF("thumb_rc: %d, %d, %d, %d\n", thumb_rc.l, thumb_rc.t, thumb_rc.r, thumb_rc.b); @@ -377,7 +402,7 @@ bool vo_draw_frame_thumb(uint8_t * const * buf, const struct vo_rect *rc) ) { DEBUGF("thumb: insufficient buffer\n"); - return false; + goto no_thumb_exit; } yuv[0] = mem; @@ -411,6 +436,10 @@ bool vo_draw_frame_thumb(uint8_t * const * buf, const struct vo_rect *rc) #endif /* LCD_WIDTH >= LCD_HEIGHT */ return true; + +no_thumb_exit: + vo_draw_black(&thumb_rc); + return false; } void vo_setup(const mpeg2_sequence_t * sequence) @@ -512,6 +541,20 @@ void vo_set_clip_rect(const struct vo_rect *rc) vo.output_height = rc_out.b - rc_out.t; } +bool vo_get_clip_rect(struct vo_rect *rc) +{ + rc->l = vo.output_x; + rc->t = vo.output_y; + rc->r = rc->l + vo.output_width; + rc->b = rc->t + vo.output_height; + return (vo.flags & VO_NON_NULL_RECT) != 0; +} + +void vo_set_post_draw_callback(void (*cb)(void)) +{ + vo.post_draw_callback = cb; +} + #if NUM_CORES > 1 void vo_lock(void) { diff --git a/apps/plugins/mpegplayer/video_thread.c b/apps/plugins/mpegplayer/video_thread.c index 69d94f8ba0..aa88590b8e 100644 --- a/apps/plugins/mpegplayer/video_thread.c +++ b/apps/plugins/mpegplayer/video_thread.c @@ -37,21 +37,23 @@ struct video_thread_data int state; /* Thread state */ int status; /* Media status */ struct queue_event ev; /* Our event queue to receive commands */ - int num_drawn; /* Number of frames drawn since reset */ - int num_skipped; /* Number of frames skipped since reset */ uint32_t eta_stream; /* Current time of stream */ uint32_t eta_video; /* Time that frame has been scheduled for */ int32_t eta_early; /* How early has the frame been decoded? */ int32_t eta_late; /* How late has the frame been decoded? */ int frame_drop_level; /* Drop severity */ int skip_level; /* Skip severity */ - long last_showfps; /* Last time the FPS display was updated */ long last_render; /* Last time a frame was drawn */ uint32_t curr_time; /* Current due time of frame */ uint32_t period; /* Frame period in clock ticks */ int syncf_perfect; /* Last sync fit result */ }; +/* Number drawn since reset */ +static int video_num_drawn SHAREDBSS_ATTR; +/* Number skipped since reset */ +static int video_num_skipped SHAREDBSS_ATTR; + /* TODO: Check if 4KB is appropriate - it works for my test streams, so maybe we can reduce it. */ #define VIDEO_STACKSIZE (4*1024) @@ -60,35 +62,6 @@ static struct event_queue video_str_queue SHAREDBSS_ATTR; static struct queue_sender_list video_str_queue_send SHAREDBSS_ATTR; struct stream video_str IBSS_ATTR; -static void draw_fps(struct video_thread_data *td) -{ - uint32_t start; - uint32_t clock_ticks = stream_get_ticks(&start); - int fps = 0; - int buf_pct; - char str[80]; - - clock_ticks -= start; - if (clock_ticks != 0) - fps = muldiv_uint32(CLOCK_RATE*100, td->num_drawn, clock_ticks); - - buf_pct = muldiv_uint32(100, pcm_output_used(), PCMOUT_BUFSIZE); - - rb->snprintf(str, sizeof(str), "v:%d.%02d %d %d a:%02d%% %d %d ", - /* Video information */ - fps / 100, fps % 100, td->num_skipped, - td->info->display_picture->temporal_reference, - /* Audio information */ - buf_pct, pcm_underruns, pcm_skipped); - mylcd_putsxy(0, 0, str); - - vo_lock(); - mylcd_update_rect(0, 0, LCD_WIDTH, 8); - vo_unlock(); - - td->last_showfps = *rb->current_tick; -} - #if defined(DEBUG) || defined(SIMULATOR) static unsigned char pic_coding_type_char(unsigned type) { @@ -452,6 +425,31 @@ sync_finished: return retval; } +static bool frame_print_handler(struct video_thread_data *td) +{ + bool retval; + uint8_t * const * buf = NULL; + + if (td->info != NULL && td->info->display_fbuf != NULL && + td->syncf_perfect > 0) + buf = td->info->display_fbuf->buf; + + if (td->ev.id == VIDEO_PRINT_THUMBNAIL) + { + /* Print a thumbnail of whatever was last decoded - scale and + * position to fill the specified rectangle */ + retval = vo_draw_frame_thumb(buf, (struct vo_rect *)td->ev.data); + } + else + { + /* Print the last frame decoded */ + vo_draw_frame(buf); + retval = buf != NULL; + } + + return retval; +} + /* This only returns to play or quit */ static void video_thread_msg(struct video_thread_data *td) { @@ -520,8 +518,7 @@ static void video_thread_msg(struct video_thread_data *td) if (td->ev.data) { - if (td->info != NULL && td->info->display_fbuf != NULL) - vo_draw_frame(td->info->display_fbuf->buf); + frame_print_handler(td); } else { @@ -547,10 +544,9 @@ static void video_thread_msg(struct video_thread_data *td) td->eta_late = 0; td->frame_drop_level = 0; td->skip_level = 0; - td->num_drawn = 0; - td->num_skipped = 0; - td->last_showfps = *rb->current_tick - HZ; - td->last_render = td->last_showfps; + td->last_render = *rb->current_tick - HZ; + video_num_drawn = 0; + video_num_skipped = 0; reply = true; break; @@ -573,28 +569,17 @@ static void video_thread_msg(struct video_thread_data *td) str_data_notify_received(&video_str); break; + case VIDEO_PRINT_FRAME: case VIDEO_PRINT_THUMBNAIL: - /* Print a thumbnail of whatever was last decoded - scale and - * position to fill the specified rectangle */ - if (td->info != NULL && td->info->display_fbuf != NULL) - { - vo_draw_frame_thumb(td->info->display_fbuf->buf, - (struct vo_rect *)td->ev.data); - reply = true; - } + reply = frame_print_handler(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) - { - vo_draw_frame(td->info->display_fbuf->buf); - reply = true; - } + case VIDEO_GET_CLIP_RECT: + reply = vo_get_clip_rect((struct vo_rect *)td->ev.data); break; case VIDEO_GET_SIZE: @@ -621,6 +606,11 @@ static void video_thread_msg(struct video_thread_data *td) reply = video_str_scan(td, (struct str_sync_data *)td->ev.data); break; + case VIDEO_SET_POST_FRAME_CALLBACK: + vo_set_post_draw_callback((void (*)(void))td->ev.data); + reply = true; + break; + case STREAM_QUIT: /* Time to go - make thread exit */ td->state = TSTATE_EOS; @@ -802,6 +792,8 @@ static void video_thread(void) if (td.info->display_fbuf == NULL) break; /* No picture */ + td.syncf_perfect = 1; /* yes, a frame exists */ + /* Get presentation times in audio samples - quite accurate enough - add previous frame duration if not stamped */ td.curr_time = (td.info->display_picture->flags & PIC_FLAG_TAGS) ? @@ -902,8 +894,8 @@ static void video_thread(void) { /* This frame was set to skip so skip it after having updated timing information */ - td.num_skipped++; td.eta_early = INT32_MIN; + video_num_skipped++; goto picture_skip; } @@ -913,8 +905,8 @@ static void video_thread(void) /* Render drop was set previously but nothing was dropped in the decoder or it's been to long since drawing the last frame. */ td.skip_level = 0; - td.num_skipped++; td.eta_early = INT32_MIN; + video_num_skipped++; goto picture_skip; } @@ -970,18 +962,11 @@ static void video_thread(void) picture_draw: /* Record last frame time */ td.last_render = *rb->current_tick; + vo_draw_frame(td.info->display_fbuf->buf); - td.num_drawn++; + video_num_drawn++; picture_skip: - if (!settings.showfps) - break; - - if (TIME_BEFORE(*rb->current_tick, td.last_showfps + HZ)) - break; - - /* Calculate and display fps */ - draw_fps(&td); break; } @@ -1032,3 +1017,19 @@ void video_thread_exit(void) video_str.thread = 0; } } + + +/** Misc **/ +void video_thread_get_stats(struct video_output_stats *s) +{ + uint32_t start; + uint32_t now = stream_get_ticks(&start); + s->num_drawn = video_num_drawn; + s->num_skipped = video_num_skipped; + + s->fps = 0; + + if (now > start) + s->fps = muldiv_uint32(CLOCK_RATE*100, s->num_drawn, now - start); +} + -- cgit v1.2.3