mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-10-14 02:27:39 -04:00
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
This commit is contained in:
parent
f8fde296a6
commit
b664f62e36
8 changed files with 333 additions and 93 deletions
|
@ -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)
|
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))
|
if (!stream_display_thumb(rc))
|
||||||
{
|
{
|
||||||
mylcd_splash(0, "Frame not available");
|
/* Display "No Frame" and erase any border */
|
||||||
return false;
|
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 */
|
/* Draw a raised border around the frame (or erase if no frame) */
|
||||||
int oldcolor = mylcd_get_foreground();
|
|
||||||
mylcd_set_foreground(MYLCD_LIGHTGRAY);
|
mylcd_set_foreground(ltgray);
|
||||||
|
|
||||||
mylcd_hline(rc->l-1, rc->r-1, rc->t-1);
|
mylcd_hline(rc->l-1, rc->r-1, rc->t-1);
|
||||||
mylcd_vline(rc->l-1, rc->t, rc->b-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_hline(rc->l-1, rc->r, rc->b);
|
||||||
mylcd_vline(rc->r, rc->t-1, 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->l-1, rc->b, rc->r - rc->l + 2, 1);
|
||||||
mylcd_update_rect(rc->r, rc->t, 1, rc->b - rc->t);
|
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 */
|
/* Add an amount to the specified time - with saturation */
|
||||||
|
|
|
@ -380,6 +380,7 @@ CONFIG_KEYPAD == SANSA_M200_PAD
|
||||||
/* 3% of 30min file == 54s step size */
|
/* 3% of 30min file == 54s step size */
|
||||||
#define MIN_FF_REWIND_STEP (TS_SECOND/2)
|
#define MIN_FF_REWIND_STEP (TS_SECOND/2)
|
||||||
#define OSD_MIN_UPDATE_INTERVAL (HZ/2)
|
#define OSD_MIN_UPDATE_INTERVAL (HZ/2)
|
||||||
|
#define FPS_UPDATE_INTERVAL (HZ) /* Get new FPS reading each second */
|
||||||
|
|
||||||
enum video_action
|
enum video_action
|
||||||
{
|
{
|
||||||
|
@ -457,9 +458,25 @@ struct osd
|
||||||
uint32_t curr_time;
|
uint32_t curr_time;
|
||||||
unsigned auto_refresh;
|
unsigned auto_refresh;
|
||||||
unsigned flags;
|
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 osd osd;
|
||||||
|
static struct fps fps NOCACHEBSS_ATTR; /* Accessed on other processor */
|
||||||
|
|
||||||
static void osd_show(unsigned show);
|
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);
|
min, max, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void draw_setfont(int font)
|
||||||
|
{
|
||||||
|
osd.font = font;
|
||||||
|
mylcd_setfont(font);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef LCD_PORTRAIT
|
#ifdef LCD_PORTRAIT
|
||||||
/* Portrait displays need rotated text rendering */
|
/* 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 ch;
|
||||||
unsigned short *ucs;
|
unsigned short *ucs;
|
||||||
int ofs = MIN(x, 0);
|
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);
|
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 */
|
#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)
|
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
|
||||||
/* So we can refresh the overlay */
|
/* So we can refresh the overlay */
|
||||||
static void osd_lcd_enable_hook(void* param)
|
static void osd_lcd_enable_hook(void* param)
|
||||||
|
@ -743,7 +856,7 @@ static void osd_text_init(void)
|
||||||
int phys;
|
int phys;
|
||||||
int spc_width;
|
int spc_width;
|
||||||
|
|
||||||
mylcd_setfont(FONT_UI);
|
draw_setfont(FONT_UI);
|
||||||
|
|
||||||
osd.x = 0;
|
osd.x = 0;
|
||||||
osd.width = SCREEN_WIDTH;
|
osd.width = SCREEN_WIDTH;
|
||||||
|
@ -812,7 +925,7 @@ static void osd_text_init(void)
|
||||||
#endif
|
#endif
|
||||||
osd.y = SCREEN_HEIGHT - osd.height;
|
osd.y = SCREEN_HEIGHT - osd.height;
|
||||||
|
|
||||||
mylcd_setfont(FONT_SYSFIXED);
|
draw_setfont(FONT_SYSFIXED);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void osd_init(void)
|
static void osd_init(void)
|
||||||
|
@ -835,6 +948,7 @@ static void osd_init(void)
|
||||||
osd.auto_refresh = OSD_REFRESH_TIME;
|
osd.auto_refresh = OSD_REFRESH_TIME;
|
||||||
osd.next_auto_refresh = *rb->current_tick;
|
osd.next_auto_refresh = *rb->current_tick;
|
||||||
osd_text_init();
|
osd_text_init();
|
||||||
|
fps_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_HEADPHONE_DETECTION
|
#ifdef HAVE_HEADPHONE_DETECTION
|
||||||
|
@ -1047,6 +1161,9 @@ static void osd_refresh(int hint)
|
||||||
|
|
||||||
tick = *rb->current_tick;
|
tick = *rb->current_tick;
|
||||||
|
|
||||||
|
if (settings.showfps)
|
||||||
|
fps_refresh();
|
||||||
|
|
||||||
if (hint == OSD_REFRESH_DEFAULT) {
|
if (hint == OSD_REFRESH_DEFAULT) {
|
||||||
/* The default which forces no updates */
|
/* The default which forces no updates */
|
||||||
|
|
||||||
|
@ -1116,7 +1233,7 @@ static void osd_refresh(int hint)
|
||||||
oldfg = mylcd_get_foreground();
|
oldfg = mylcd_get_foreground();
|
||||||
oldbg = mylcd_get_background();
|
oldbg = mylcd_get_background();
|
||||||
|
|
||||||
mylcd_setfont(FONT_UI);
|
draw_setfont(FONT_UI);
|
||||||
mylcd_set_foreground(osd.fgcolor);
|
mylcd_set_foreground(osd.fgcolor);
|
||||||
mylcd_set_background(osd.bgcolor);
|
mylcd_set_background(osd.bgcolor);
|
||||||
|
|
||||||
|
@ -1140,7 +1257,7 @@ static void osd_refresh(int hint)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Go back to defaults */
|
/* Go back to defaults */
|
||||||
mylcd_setfont(FONT_SYSFIXED);
|
draw_setfont(FONT_SYSFIXED);
|
||||||
mylcd_set_foreground(oldfg);
|
mylcd_set_foreground(oldfg);
|
||||||
mylcd_set_background(oldbg);
|
mylcd_set_background(oldbg);
|
||||||
|
|
||||||
|
@ -1391,6 +1508,7 @@ static int osd_play(uint32_t time)
|
||||||
osd_backlight_on_video_mode(true);
|
osd_backlight_on_video_mode(true);
|
||||||
osd_backlight_brightness_video_mode(true);
|
osd_backlight_brightness_video_mode(true);
|
||||||
stream_show_vo(true);
|
stream_show_vo(true);
|
||||||
|
|
||||||
retval = stream_play();
|
retval = stream_play();
|
||||||
|
|
||||||
if (retval >= STREAM_OK)
|
if (retval >= STREAM_OK)
|
||||||
|
@ -1747,6 +1865,8 @@ static int button_loop(void)
|
||||||
|
|
||||||
next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT;
|
next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT;
|
||||||
|
|
||||||
|
fps_update_post_frame_callback();
|
||||||
|
|
||||||
/* The menu can change the font, so restore */
|
/* The menu can change the font, so restore */
|
||||||
rb->lcd_setfont(FONT_SYSFIXED);
|
rb->lcd_setfont(FONT_SYSFIXED);
|
||||||
#ifdef HAVE_LCD_COLOR
|
#ifdef HAVE_LCD_COLOR
|
||||||
|
|
|
@ -695,8 +695,8 @@ static intptr_t send_video_msg(long id, intptr_t data)
|
||||||
if (disk_buf_status() != STREAM_STOPPED)
|
if (disk_buf_status() != STREAM_STOPPED)
|
||||||
break; /* Prepare image if not playing */
|
break; /* Prepare image if not playing */
|
||||||
|
|
||||||
if (!parser_prepare_image(str_parser.last_seek_time))
|
/* Ignore return and try video thread anyway */
|
||||||
return false; /* Preparation failed */
|
parser_prepare_image(str_parser.last_seek_time);
|
||||||
|
|
||||||
/* Image ready - pass message to video thread */
|
/* Image ready - pass message to video thread */
|
||||||
break;
|
break;
|
||||||
|
@ -766,6 +766,25 @@ void stream_vo_set_clip(const struct vo_rect *rc)
|
||||||
stream_mgr_unlock();
|
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
|
#ifndef HAVE_LCD_COLOR
|
||||||
/* Show/hide the gray video overlay (independently of vo visibility). */
|
/* Show/hide the gray video overlay (independently of vo visibility). */
|
||||||
void stream_gray_show(bool show)
|
void stream_gray_show(bool show)
|
||||||
|
@ -810,6 +829,23 @@ bool stream_draw_frame(bool no_prepare)
|
||||||
return retval;
|
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 */
|
/* Return the time playback should resume if interrupted */
|
||||||
uint32_t stream_get_resume_time(void)
|
uint32_t stream_get_resume_time(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -106,6 +106,9 @@ bool stream_show_vo(bool show);
|
||||||
/* Set the visible section of video */
|
/* Set the visible section of video */
|
||||||
void stream_vo_set_clip(const struct vo_rect *rc);
|
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
|
#ifndef HAVE_LCD_COLOR
|
||||||
void stream_gray_show(bool show);
|
void stream_gray_show(bool show);
|
||||||
#endif
|
#endif
|
||||||
|
@ -149,6 +152,11 @@ static inline uint32_t stream_get_duration(void)
|
||||||
static inline bool stream_can_seek(void)
|
static inline bool stream_can_seek(void)
|
||||||
{ return parser_can_seek(); }
|
{ 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) */
|
/* Keep the disk spinning (for seeking and browsing) */
|
||||||
static inline void stream_keep_disk_active(void)
|
static inline void stream_keep_disk_active(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -108,6 +108,8 @@ enum stream_message
|
||||||
VIDEO_PRINT_FRAME, /* Print the frame at the current position */
|
VIDEO_PRINT_FRAME, /* Print the frame at the current position */
|
||||||
VIDEO_PRINT_THUMBNAIL, /* Print a thumbnail of the current position */
|
VIDEO_PRINT_THUMBNAIL, /* Print a thumbnail of the current position */
|
||||||
VIDEO_SET_CLIP_RECT, /* Set the visible video area */
|
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,
|
STREAM_MESSAGE_LAST,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -160,9 +162,20 @@ extern struct stream audio_str IBSS_ATTR;
|
||||||
|
|
||||||
bool video_thread_init(void);
|
bool video_thread_init(void);
|
||||||
void video_thread_exit(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);
|
bool audio_thread_init(void);
|
||||||
void audio_thread_exit(void);
|
void audio_thread_exit(void);
|
||||||
|
|
||||||
|
|
||||||
/* Some queue function wrappers to keep things clean-ish */
|
/* Some queue function wrappers to keep things clean-ish */
|
||||||
|
|
||||||
/* For stream use only */
|
/* For stream use only */
|
||||||
|
|
|
@ -58,8 +58,10 @@ bool vo_show (bool show);
|
||||||
bool vo_is_visible(void);
|
bool vo_is_visible(void);
|
||||||
void vo_setup (const mpeg2_sequence_t * sequence);
|
void vo_setup (const mpeg2_sequence_t * sequence);
|
||||||
void vo_set_clip_rect(const struct vo_rect *rc);
|
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_dimensions(struct vo_ext *sz);
|
||||||
void vo_cleanup (void);
|
void vo_cleanup (void);
|
||||||
|
void vo_set_post_draw_callback(void (*cb)(void));
|
||||||
|
|
||||||
#if NUM_CORES > 1
|
#if NUM_CORES > 1
|
||||||
void vo_lock(void);
|
void vo_lock(void);
|
||||||
|
|
|
@ -41,6 +41,7 @@ struct vo_data
|
||||||
unsigned flags;
|
unsigned flags;
|
||||||
struct vo_rect rc_vid;
|
struct vo_rect rc_vid;
|
||||||
struct vo_rect rc_clip;
|
struct vo_rect rc_clip;
|
||||||
|
void (*post_draw_callback)(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
#if NUM_CORES > 1
|
#if NUM_CORES > 1
|
||||||
|
@ -80,9 +81,10 @@ static inline void video_unlock(void)
|
||||||
|
|
||||||
|
|
||||||
/* Draw a black rectangle if no video frame is available */
|
/* 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 foreground;
|
||||||
|
int x, y, w, h;
|
||||||
|
|
||||||
video_lock();
|
video_lock();
|
||||||
|
|
||||||
|
@ -90,10 +92,30 @@ static void vo_draw_black(void)
|
||||||
|
|
||||||
mylcd_set_foreground(MYLCD_BLACK);
|
mylcd_set_foreground(MYLCD_BLACK);
|
||||||
|
|
||||||
mylcd_fillrect(vo.output_x, vo.output_y, vo.output_width,
|
if (rc)
|
||||||
vo.output_height);
|
{
|
||||||
mylcd_update_rect(vo.output_x, vo.output_y, vo.output_width,
|
x = rc->l;
|
||||||
vo.output_height);
|
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);
|
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
|
/* Frame is hidden - either by being set invisible or is clipped
|
||||||
* away - copout */
|
* away - copout */
|
||||||
DEBUGF("vo hidden\n");
|
DEBUGF("vo hidden\n");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
else if (buf == NULL)
|
else if (buf == NULL)
|
||||||
{
|
{
|
||||||
/* No frame exists - draw black */
|
/* No frame exists - draw black */
|
||||||
vo_draw_black();
|
vo_draw_black(NULL);
|
||||||
DEBUGF("vo no frame\n");
|
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,
|
if (vo.post_draw_callback)
|
||||||
vo.output_x, vo.output_y, vo.output_width,
|
vo.post_draw_callback();
|
||||||
vo.output_height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void vo_rect_clear_inl(struct vo_rect *rc)
|
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_width, thumb_height;
|
||||||
int thumb_uv_width, thumb_uv_height;
|
int thumb_uv_width, thumb_uv_height;
|
||||||
|
|
||||||
if (buf == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* Obtain rectangle as clipped to the screen */
|
/* Obtain rectangle as clipped to the screen */
|
||||||
vo_rect_set_ext(&thumb_rc, 0, 0, LCD_WIDTH, LCD_HEIGHT);
|
vo_rect_set_ext(&thumb_rc, 0, 0, LCD_WIDTH, LCD_HEIGHT);
|
||||||
if (!vo_rect_intersect(&thumb_rc, rc, &thumb_rc))
|
if (!vo_rect_intersect(&thumb_rc, rc, &thumb_rc))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (buf == NULL)
|
||||||
|
goto no_thumb_exit;
|
||||||
|
|
||||||
DEBUGF("thumb_rc: %d, %d, %d, %d\n", thumb_rc.l, thumb_rc.t,
|
DEBUGF("thumb_rc: %d, %d, %d, %d\n", thumb_rc.l, thumb_rc.t,
|
||||||
thumb_rc.r, thumb_rc.b);
|
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");
|
DEBUGF("thumb: insufficient buffer\n");
|
||||||
return false;
|
goto no_thumb_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
yuv[0] = mem;
|
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 */
|
#endif /* LCD_WIDTH >= LCD_HEIGHT */
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
no_thumb_exit:
|
||||||
|
vo_draw_black(&thumb_rc);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vo_setup(const mpeg2_sequence_t * sequence)
|
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;
|
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
|
#if NUM_CORES > 1
|
||||||
void vo_lock(void)
|
void vo_lock(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,21 +37,23 @@ struct video_thread_data
|
||||||
int state; /* Thread state */
|
int state; /* Thread state */
|
||||||
int status; /* Media status */
|
int status; /* Media status */
|
||||||
struct queue_event ev; /* Our event queue to receive commands */
|
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_stream; /* Current time of stream */
|
||||||
uint32_t eta_video; /* Time that frame has been scheduled for */
|
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_early; /* How early has the frame been decoded? */
|
||||||
int32_t eta_late; /* How late has the frame been decoded? */
|
int32_t eta_late; /* How late has the frame been decoded? */
|
||||||
int frame_drop_level; /* Drop severity */
|
int frame_drop_level; /* Drop severity */
|
||||||
int skip_level; /* Skip 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 */
|
long last_render; /* Last time a frame was drawn */
|
||||||
uint32_t curr_time; /* Current due time of frame */
|
uint32_t curr_time; /* Current due time of frame */
|
||||||
uint32_t period; /* Frame period in clock ticks */
|
uint32_t period; /* Frame period in clock ticks */
|
||||||
int syncf_perfect; /* Last sync fit result */
|
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,
|
/* TODO: Check if 4KB is appropriate - it works for my test streams,
|
||||||
so maybe we can reduce it. */
|
so maybe we can reduce it. */
|
||||||
#define VIDEO_STACKSIZE (4*1024)
|
#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;
|
static struct queue_sender_list video_str_queue_send SHAREDBSS_ATTR;
|
||||||
struct stream video_str IBSS_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)
|
#if defined(DEBUG) || defined(SIMULATOR)
|
||||||
static unsigned char pic_coding_type_char(unsigned type)
|
static unsigned char pic_coding_type_char(unsigned type)
|
||||||
{
|
{
|
||||||
|
@ -452,6 +425,31 @@ sync_finished:
|
||||||
return retval;
|
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 */
|
/* This only returns to play or quit */
|
||||||
static void video_thread_msg(struct video_thread_data *td)
|
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->ev.data)
|
||||||
{
|
{
|
||||||
if (td->info != NULL && td->info->display_fbuf != NULL)
|
frame_print_handler(td);
|
||||||
vo_draw_frame(td->info->display_fbuf->buf);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -547,10 +544,9 @@ static void video_thread_msg(struct video_thread_data *td)
|
||||||
td->eta_late = 0;
|
td->eta_late = 0;
|
||||||
td->frame_drop_level = 0;
|
td->frame_drop_level = 0;
|
||||||
td->skip_level = 0;
|
td->skip_level = 0;
|
||||||
td->num_drawn = 0;
|
td->last_render = *rb->current_tick - HZ;
|
||||||
td->num_skipped = 0;
|
video_num_drawn = 0;
|
||||||
td->last_showfps = *rb->current_tick - HZ;
|
video_num_skipped = 0;
|
||||||
td->last_render = td->last_showfps;
|
|
||||||
|
|
||||||
reply = true;
|
reply = true;
|
||||||
break;
|
break;
|
||||||
|
@ -573,28 +569,17 @@ static void video_thread_msg(struct video_thread_data *td)
|
||||||
str_data_notify_received(&video_str);
|
str_data_notify_received(&video_str);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case VIDEO_PRINT_FRAME:
|
||||||
case VIDEO_PRINT_THUMBNAIL:
|
case VIDEO_PRINT_THUMBNAIL:
|
||||||
/* Print a thumbnail of whatever was last decoded - scale and
|
reply = frame_print_handler(td);
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIDEO_SET_CLIP_RECT:
|
case VIDEO_SET_CLIP_RECT:
|
||||||
vo_set_clip_rect((const struct vo_rect *)td->ev.data);
|
vo_set_clip_rect((const struct vo_rect *)td->ev.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIDEO_PRINT_FRAME:
|
case VIDEO_GET_CLIP_RECT:
|
||||||
/* Print the last frame decoded */
|
reply = vo_get_clip_rect((struct vo_rect *)td->ev.data);
|
||||||
if (td->info != NULL && td->info->display_fbuf != NULL)
|
|
||||||
{
|
|
||||||
vo_draw_frame(td->info->display_fbuf->buf);
|
|
||||||
reply = true;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIDEO_GET_SIZE:
|
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);
|
reply = video_str_scan(td, (struct str_sync_data *)td->ev.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case VIDEO_SET_POST_FRAME_CALLBACK:
|
||||||
|
vo_set_post_draw_callback((void (*)(void))td->ev.data);
|
||||||
|
reply = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case STREAM_QUIT:
|
case STREAM_QUIT:
|
||||||
/* Time to go - make thread exit */
|
/* Time to go - make thread exit */
|
||||||
td->state = TSTATE_EOS;
|
td->state = TSTATE_EOS;
|
||||||
|
@ -802,6 +792,8 @@ static void video_thread(void)
|
||||||
if (td.info->display_fbuf == NULL)
|
if (td.info->display_fbuf == NULL)
|
||||||
break; /* No picture */
|
break; /* No picture */
|
||||||
|
|
||||||
|
td.syncf_perfect = 1; /* yes, a frame exists */
|
||||||
|
|
||||||
/* Get presentation times in audio samples - quite accurate
|
/* Get presentation times in audio samples - quite accurate
|
||||||
enough - add previous frame duration if not stamped */
|
enough - add previous frame duration if not stamped */
|
||||||
td.curr_time = (td.info->display_picture->flags & PIC_FLAG_TAGS) ?
|
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
|
/* This frame was set to skip so skip it after having updated
|
||||||
timing information */
|
timing information */
|
||||||
td.num_skipped++;
|
|
||||||
td.eta_early = INT32_MIN;
|
td.eta_early = INT32_MIN;
|
||||||
|
video_num_skipped++;
|
||||||
goto picture_skip;
|
goto picture_skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -913,8 +905,8 @@ static void video_thread(void)
|
||||||
/* Render drop was set previously but nothing was dropped in the
|
/* Render drop was set previously but nothing was dropped in the
|
||||||
decoder or it's been to long since drawing the last frame. */
|
decoder or it's been to long since drawing the last frame. */
|
||||||
td.skip_level = 0;
|
td.skip_level = 0;
|
||||||
td.num_skipped++;
|
|
||||||
td.eta_early = INT32_MIN;
|
td.eta_early = INT32_MIN;
|
||||||
|
video_num_skipped++;
|
||||||
goto picture_skip;
|
goto picture_skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -970,18 +962,11 @@ static void video_thread(void)
|
||||||
picture_draw:
|
picture_draw:
|
||||||
/* Record last frame time */
|
/* Record last frame time */
|
||||||
td.last_render = *rb->current_tick;
|
td.last_render = *rb->current_tick;
|
||||||
|
|
||||||
vo_draw_frame(td.info->display_fbuf->buf);
|
vo_draw_frame(td.info->display_fbuf->buf);
|
||||||
td.num_drawn++;
|
video_num_drawn++;
|
||||||
|
|
||||||
picture_skip:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1032,3 +1017,19 @@ void video_thread_exit(void)
|
||||||
video_str.thread = 0;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue