diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2011-01-03 16:41:19 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2011-01-03 16:41:19 +0000 |
commit | b664f62e36b5f0ac296567e423816dab3811075d (patch) | |
tree | 7ca49c59d7332d7c1e51139efa12466b6730e511 /apps/plugins | |
parent | f8fde296a63dd06efef5cf71c9fdb2c26c5a3fd6 (diff) | |
download | rockbox-b664f62e36b5f0ac296567e423816dab3811075d.tar.gz rockbox-b664f62e36b5f0ac296567e423816dab3811075d.zip |
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
Diffstat (limited to 'apps/plugins')
-rw-r--r-- | apps/plugins/mpegplayer/mpeg_settings.c | 31 | ||||
-rw-r--r-- | apps/plugins/mpegplayer/mpegplayer.c | 130 | ||||
-rw-r--r-- | apps/plugins/mpegplayer/stream_mgr.c | 40 | ||||
-rw-r--r-- | apps/plugins/mpegplayer/stream_mgr.h | 8 | ||||
-rw-r--r-- | apps/plugins/mpegplayer/stream_thread.h | 13 | ||||
-rw-r--r-- | apps/plugins/mpegplayer/video_out.h | 2 | ||||
-rw-r--r-- | apps/plugins/mpegplayer/video_out_rockbox.c | 73 | ||||
-rw-r--r-- | apps/plugins/mpegplayer/video_thread.c | 129 |
8 files changed, 333 insertions, 93 deletions
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) | |||
601 | 601 | ||
602 | static bool display_thumb_image(const struct vo_rect *rc) | 602 | static bool display_thumb_image(const struct vo_rect *rc) |
603 | { | 603 | { |
604 | bool retval = true; | ||
605 | unsigned ltgray = MYLCD_LIGHTGRAY; | ||
606 | unsigned dkgray = MYLCD_DARKGRAY; | ||
607 | |||
608 | int oldcolor = mylcd_get_foreground(); | ||
609 | |||
604 | if (!stream_display_thumb(rc)) | 610 | if (!stream_display_thumb(rc)) |
605 | { | 611 | { |
606 | mylcd_splash(0, "Frame not available"); | 612 | /* Display "No Frame" and erase any border */ |
607 | return false; | 613 | const char * const str = "No Frame"; |
614 | int x, y, w, h; | ||
615 | |||
616 | mylcd_getstringsize(str, &w, &h); | ||
617 | x = (rc->r + rc->l - w) / 2; | ||
618 | y = (rc->b + rc->t - h) / 2; | ||
619 | mylcd_putsxy(x, y, str); | ||
620 | |||
621 | mylcd_update_rect(x, y, w, h); | ||
622 | |||
623 | ltgray = dkgray = mylcd_get_background(); | ||
624 | retval = false; | ||
608 | } | 625 | } |
609 | 626 | ||
610 | /* Draw a raised border around the frame */ | 627 | /* Draw a raised border around the frame (or erase if no frame) */ |
611 | int oldcolor = mylcd_get_foreground(); | 628 | |
612 | mylcd_set_foreground(MYLCD_LIGHTGRAY); | 629 | mylcd_set_foreground(ltgray); |
613 | 630 | ||
614 | mylcd_hline(rc->l-1, rc->r-1, rc->t-1); | 631 | mylcd_hline(rc->l-1, rc->r-1, rc->t-1); |
615 | mylcd_vline(rc->l-1, rc->t, rc->b-1); | 632 | mylcd_vline(rc->l-1, rc->t, rc->b-1); |
616 | 633 | ||
617 | mylcd_set_foreground(MYLCD_DARKGRAY); | 634 | mylcd_set_foreground(dkgray); |
618 | 635 | ||
619 | mylcd_hline(rc->l-1, rc->r, rc->b); | 636 | mylcd_hline(rc->l-1, rc->r, rc->b); |
620 | mylcd_vline(rc->r, rc->t-1, rc->b); | 637 | mylcd_vline(rc->r, rc->t-1, rc->b); |
@@ -626,7 +643,7 @@ static bool display_thumb_image(const struct vo_rect *rc) | |||
626 | mylcd_update_rect(rc->l-1, rc->b, rc->r - rc->l + 2, 1); | 643 | mylcd_update_rect(rc->l-1, rc->b, rc->r - rc->l + 2, 1); |
627 | mylcd_update_rect(rc->r, rc->t, 1, rc->b - rc->t); | 644 | mylcd_update_rect(rc->r, rc->t, 1, rc->b - rc->t); |
628 | 645 | ||
629 | return true; | 646 | return retval; |
630 | } | 647 | } |
631 | 648 | ||
632 | /* Add an amount to the specified time - with saturation */ | 649 | /* 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 | |||
380 | /* 3% of 30min file == 54s step size */ | 380 | /* 3% of 30min file == 54s step size */ |
381 | #define MIN_FF_REWIND_STEP (TS_SECOND/2) | 381 | #define MIN_FF_REWIND_STEP (TS_SECOND/2) |
382 | #define OSD_MIN_UPDATE_INTERVAL (HZ/2) | 382 | #define OSD_MIN_UPDATE_INTERVAL (HZ/2) |
383 | #define FPS_UPDATE_INTERVAL (HZ) /* Get new FPS reading each second */ | ||
383 | 384 | ||
384 | enum video_action | 385 | enum video_action |
385 | { | 386 | { |
@@ -457,9 +458,25 @@ struct osd | |||
457 | uint32_t curr_time; | 458 | uint32_t curr_time; |
458 | unsigned auto_refresh; | 459 | unsigned auto_refresh; |
459 | unsigned flags; | 460 | unsigned flags; |
461 | int font; | ||
462 | }; | ||
463 | |||
464 | struct fps | ||
465 | { | ||
466 | /* FPS Display */ | ||
467 | struct vo_rect rect; /* OSD coordinates */ | ||
468 | int pf_x; /* Screen coordinates */ | ||
469 | int pf_y; | ||
470 | int pf_width; | ||
471 | int pf_height; | ||
472 | long update_tick; /* When to next update FPS reading */ | ||
473 | #define FPS_FORMAT "%d.%02d" | ||
474 | #define FPS_DIMSTR "999.99" /* For establishing rect size */ | ||
475 | #define FPS_BUFSIZE sizeof("999.99") | ||
460 | }; | 476 | }; |
461 | 477 | ||
462 | static struct osd osd; | 478 | static struct osd osd; |
479 | static struct fps fps NOCACHEBSS_ATTR; /* Accessed on other processor */ | ||
463 | 480 | ||
464 | static void osd_show(unsigned show); | 481 | static void osd_show(unsigned show); |
465 | 482 | ||
@@ -573,6 +590,12 @@ static void draw_scrollbar_draw_rect(const struct vo_rect *rc, int min, | |||
573 | min, max, val); | 590 | min, max, val); |
574 | } | 591 | } |
575 | 592 | ||
593 | static void draw_setfont(int font) | ||
594 | { | ||
595 | osd.font = font; | ||
596 | mylcd_setfont(font); | ||
597 | } | ||
598 | |||
576 | #ifdef LCD_PORTRAIT | 599 | #ifdef LCD_PORTRAIT |
577 | /* Portrait displays need rotated text rendering */ | 600 | /* Portrait displays need rotated text rendering */ |
578 | 601 | ||
@@ -646,7 +669,7 @@ static void draw_putsxy_oriented(int x, int y, const char *str) | |||
646 | unsigned short ch; | 669 | unsigned short ch; |
647 | unsigned short *ucs; | 670 | unsigned short *ucs; |
648 | int ofs = MIN(x, 0); | 671 | int ofs = MIN(x, 0); |
649 | struct font* pf = rb->font_get(FONT_UI); | 672 | struct font* pf = rb->font_get(osd.font); |
650 | 673 | ||
651 | ucs = rb->bidi_l2v(str, 1); | 674 | ucs = rb->bidi_l2v(str, 1); |
652 | 675 | ||
@@ -696,6 +719,96 @@ static void draw_putsxy_oriented(int x, int y, const char *str) | |||
696 | } | 719 | } |
697 | #endif /* LCD_PORTRAIT */ | 720 | #endif /* LCD_PORTRAIT */ |
698 | 721 | ||
722 | /** FPS Display **/ | ||
723 | |||
724 | /* Post-frame callback (on video thread) - update the FPS rectangle from the | ||
725 | * framebuffer */ | ||
726 | static void fps_post_frame_callback(void) | ||
727 | { | ||
728 | vo_lock(); | ||
729 | mylcd_update_rect(fps.pf_x, fps.pf_y, | ||
730 | fps.pf_width, fps.pf_height); | ||
731 | vo_unlock(); | ||
732 | } | ||
733 | |||
734 | /* Set up to have the callback only update the intersection of the video | ||
735 | * rectangle and the FPS text rectangle - if they don't intersect, then | ||
736 | * the callback is set to NULL */ | ||
737 | static void fps_update_post_frame_callback(void) | ||
738 | { | ||
739 | void (*cb)(void) = NULL; | ||
740 | |||
741 | if (settings.showfps) { | ||
742 | struct vo_rect cliprect; | ||
743 | |||
744 | if (stream_vo_get_clip(&cliprect)) { | ||
745 | /* Oriented screen coordinates -> OSD coordinates */ | ||
746 | vo_rect_offset(&cliprect, -osd.x, -osd.y); | ||
747 | |||
748 | if (vo_rect_intersect(&cliprect, &cliprect, &fps.rect)) { | ||
749 | int x = cliprect.l; | ||
750 | int y = cliprect.t; | ||
751 | int width = cliprect.r - cliprect.l; | ||
752 | int height = cliprect.b - cliprect.t; | ||
753 | |||
754 | /* OSD coordinates -> framebuffer coordinates */ | ||
755 | fps.pf_x = _X; | ||
756 | fps.pf_y = _Y; | ||
757 | fps.pf_width = _W; | ||
758 | fps.pf_height = _H; | ||
759 | |||
760 | cb = fps_post_frame_callback; | ||
761 | } | ||
762 | } | ||
763 | } | ||
764 | |||
765 | stream_set_callback(VIDEO_SET_POST_FRAME_CALLBACK, cb); | ||
766 | } | ||
767 | |||
768 | /* Refresh the FPS display */ | ||
769 | static void fps_refresh(void) | ||
770 | { | ||
771 | char str[FPS_BUFSIZE]; | ||
772 | struct video_output_stats stats; | ||
773 | int w, h, sw; | ||
774 | long tick; | ||
775 | |||
776 | tick = *rb->current_tick; | ||
777 | |||
778 | if (TIME_BEFORE(tick, fps.update_tick)) | ||
779 | return; | ||
780 | |||
781 | fps.update_tick = tick + FPS_UPDATE_INTERVAL; | ||
782 | |||
783 | stream_video_stats(&stats); | ||
784 | |||
785 | rb->snprintf(str, FPS_BUFSIZE, FPS_FORMAT, | ||
786 | stats.fps / 100, stats.fps % 100); | ||
787 | |||
788 | w = fps.rect.r - fps.rect.l; | ||
789 | h = fps.rect.b - fps.rect.t; | ||
790 | |||
791 | draw_clear_area(fps.rect.l, fps.rect.t, w, h); | ||
792 | mylcd_getstringsize(str, &sw, NULL); | ||
793 | draw_putsxy_oriented(fps.rect.r - sw, fps.rect.t, str); | ||
794 | |||
795 | vo_lock(); | ||
796 | draw_update_rect(fps.rect.l, fps.rect.t, w, h); | ||
797 | vo_unlock(); | ||
798 | } | ||
799 | |||
800 | /* Initialize the FPS display */ | ||
801 | static void fps_init(void) | ||
802 | { | ||
803 | fps.update_tick = *rb->current_tick; | ||
804 | fps.rect.l = fps.rect.t = 0; | ||
805 | mylcd_getstringsize(FPS_DIMSTR, &fps.rect.r, &fps.rect.b); | ||
806 | vo_rect_offset(&fps.rect, -osd.x, -osd.y); | ||
807 | fps_update_post_frame_callback(); | ||
808 | } | ||
809 | |||
810 | /** OSD **/ | ||
811 | |||
699 | #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) | 812 | #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) |
700 | /* So we can refresh the overlay */ | 813 | /* So we can refresh the overlay */ |
701 | static void osd_lcd_enable_hook(void* param) | 814 | static void osd_lcd_enable_hook(void* param) |
@@ -743,7 +856,7 @@ static void osd_text_init(void) | |||
743 | int phys; | 856 | int phys; |
744 | int spc_width; | 857 | int spc_width; |
745 | 858 | ||
746 | mylcd_setfont(FONT_UI); | 859 | draw_setfont(FONT_UI); |
747 | 860 | ||
748 | osd.x = 0; | 861 | osd.x = 0; |
749 | osd.width = SCREEN_WIDTH; | 862 | osd.width = SCREEN_WIDTH; |
@@ -812,7 +925,7 @@ static void osd_text_init(void) | |||
812 | #endif | 925 | #endif |
813 | osd.y = SCREEN_HEIGHT - osd.height; | 926 | osd.y = SCREEN_HEIGHT - osd.height; |
814 | 927 | ||
815 | mylcd_setfont(FONT_SYSFIXED); | 928 | draw_setfont(FONT_SYSFIXED); |
816 | } | 929 | } |
817 | 930 | ||
818 | static void osd_init(void) | 931 | static void osd_init(void) |
@@ -835,6 +948,7 @@ static void osd_init(void) | |||
835 | osd.auto_refresh = OSD_REFRESH_TIME; | 948 | osd.auto_refresh = OSD_REFRESH_TIME; |
836 | osd.next_auto_refresh = *rb->current_tick; | 949 | osd.next_auto_refresh = *rb->current_tick; |
837 | osd_text_init(); | 950 | osd_text_init(); |
951 | fps_init(); | ||
838 | } | 952 | } |
839 | 953 | ||
840 | #ifdef HAVE_HEADPHONE_DETECTION | 954 | #ifdef HAVE_HEADPHONE_DETECTION |
@@ -1047,6 +1161,9 @@ static void osd_refresh(int hint) | |||
1047 | 1161 | ||
1048 | tick = *rb->current_tick; | 1162 | tick = *rb->current_tick; |
1049 | 1163 | ||
1164 | if (settings.showfps) | ||
1165 | fps_refresh(); | ||
1166 | |||
1050 | if (hint == OSD_REFRESH_DEFAULT) { | 1167 | if (hint == OSD_REFRESH_DEFAULT) { |
1051 | /* The default which forces no updates */ | 1168 | /* The default which forces no updates */ |
1052 | 1169 | ||
@@ -1116,7 +1233,7 @@ static void osd_refresh(int hint) | |||
1116 | oldfg = mylcd_get_foreground(); | 1233 | oldfg = mylcd_get_foreground(); |
1117 | oldbg = mylcd_get_background(); | 1234 | oldbg = mylcd_get_background(); |
1118 | 1235 | ||
1119 | mylcd_setfont(FONT_UI); | 1236 | draw_setfont(FONT_UI); |
1120 | mylcd_set_foreground(osd.fgcolor); | 1237 | mylcd_set_foreground(osd.fgcolor); |
1121 | mylcd_set_background(osd.bgcolor); | 1238 | mylcd_set_background(osd.bgcolor); |
1122 | 1239 | ||
@@ -1140,7 +1257,7 @@ static void osd_refresh(int hint) | |||
1140 | } | 1257 | } |
1141 | 1258 | ||
1142 | /* Go back to defaults */ | 1259 | /* Go back to defaults */ |
1143 | mylcd_setfont(FONT_SYSFIXED); | 1260 | draw_setfont(FONT_SYSFIXED); |
1144 | mylcd_set_foreground(oldfg); | 1261 | mylcd_set_foreground(oldfg); |
1145 | mylcd_set_background(oldbg); | 1262 | mylcd_set_background(oldbg); |
1146 | 1263 | ||
@@ -1391,6 +1508,7 @@ static int osd_play(uint32_t time) | |||
1391 | osd_backlight_on_video_mode(true); | 1508 | osd_backlight_on_video_mode(true); |
1392 | osd_backlight_brightness_video_mode(true); | 1509 | osd_backlight_brightness_video_mode(true); |
1393 | stream_show_vo(true); | 1510 | stream_show_vo(true); |
1511 | |||
1394 | retval = stream_play(); | 1512 | retval = stream_play(); |
1395 | 1513 | ||
1396 | if (retval >= STREAM_OK) | 1514 | if (retval >= STREAM_OK) |
@@ -1747,6 +1865,8 @@ static int button_loop(void) | |||
1747 | 1865 | ||
1748 | next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT; | 1866 | next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT; |
1749 | 1867 | ||
1868 | fps_update_post_frame_callback(); | ||
1869 | |||
1750 | /* The menu can change the font, so restore */ | 1870 | /* The menu can change the font, so restore */ |
1751 | rb->lcd_setfont(FONT_SYSFIXED); | 1871 | rb->lcd_setfont(FONT_SYSFIXED); |
1752 | #ifdef HAVE_LCD_COLOR | 1872 | #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) | |||
695 | if (disk_buf_status() != STREAM_STOPPED) | 695 | if (disk_buf_status() != STREAM_STOPPED) |
696 | break; /* Prepare image if not playing */ | 696 | break; /* Prepare image if not playing */ |
697 | 697 | ||
698 | if (!parser_prepare_image(str_parser.last_seek_time)) | 698 | /* Ignore return and try video thread anyway */ |
699 | return false; /* Preparation failed */ | 699 | parser_prepare_image(str_parser.last_seek_time); |
700 | 700 | ||
701 | /* Image ready - pass message to video thread */ | 701 | /* Image ready - pass message to video thread */ |
702 | break; | 702 | break; |
@@ -766,6 +766,25 @@ void stream_vo_set_clip(const struct vo_rect *rc) | |||
766 | stream_mgr_unlock(); | 766 | stream_mgr_unlock(); |
767 | } | 767 | } |
768 | 768 | ||
769 | bool stream_vo_get_clip(struct vo_rect *rc) | ||
770 | { | ||
771 | bool retval; | ||
772 | |||
773 | if (!rc) | ||
774 | return false; | ||
775 | |||
776 | stream_mgr_lock(); | ||
777 | |||
778 | retval = send_video_msg(VIDEO_GET_CLIP_RECT, | ||
779 | (intptr_t)&stream_mgr.parms.rc); | ||
780 | |||
781 | *rc = stream_mgr.parms.rc; | ||
782 | |||
783 | stream_mgr_unlock(); | ||
784 | |||
785 | return retval; | ||
786 | } | ||
787 | |||
769 | #ifndef HAVE_LCD_COLOR | 788 | #ifndef HAVE_LCD_COLOR |
770 | /* Show/hide the gray video overlay (independently of vo visibility). */ | 789 | /* Show/hide the gray video overlay (independently of vo visibility). */ |
771 | void stream_gray_show(bool show) | 790 | void stream_gray_show(bool show) |
@@ -810,6 +829,23 @@ bool stream_draw_frame(bool no_prepare) | |||
810 | return retval; | 829 | return retval; |
811 | } | 830 | } |
812 | 831 | ||
832 | bool stream_set_callback(long id, void *fn) | ||
833 | { | ||
834 | bool retval = false; | ||
835 | |||
836 | stream_mgr_lock(); | ||
837 | |||
838 | switch (id) | ||
839 | { | ||
840 | case VIDEO_SET_POST_FRAME_CALLBACK: | ||
841 | retval = send_video_msg(id, (intptr_t)fn); | ||
842 | } | ||
843 | |||
844 | stream_mgr_unlock(); | ||
845 | |||
846 | return retval; | ||
847 | } | ||
848 | |||
813 | /* Return the time playback should resume if interrupted */ | 849 | /* Return the time playback should resume if interrupted */ |
814 | uint32_t stream_get_resume_time(void) | 850 | uint32_t stream_get_resume_time(void) |
815 | { | 851 | { |
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); | |||
106 | /* Set the visible section of video */ | 106 | /* Set the visible section of video */ |
107 | void stream_vo_set_clip(const struct vo_rect *rc); | 107 | void stream_vo_set_clip(const struct vo_rect *rc); |
108 | 108 | ||
109 | /* Return current visible section of video */ | ||
110 | bool stream_vo_get_clip(struct vo_rect *rc); | ||
111 | |||
109 | #ifndef HAVE_LCD_COLOR | 112 | #ifndef HAVE_LCD_COLOR |
110 | void stream_gray_show(bool show); | 113 | void stream_gray_show(bool show); |
111 | #endif | 114 | #endif |
@@ -149,6 +152,11 @@ static inline uint32_t stream_get_duration(void) | |||
149 | static inline bool stream_can_seek(void) | 152 | static inline bool stream_can_seek(void) |
150 | { return parser_can_seek(); } | 153 | { return parser_can_seek(); } |
151 | 154 | ||
155 | static inline void stream_video_stats(struct video_output_stats *s) | ||
156 | { video_thread_get_stats(s); } | ||
157 | |||
158 | bool stream_set_callback(long id, void * fn); | ||
159 | |||
152 | /* Keep the disk spinning (for seeking and browsing) */ | 160 | /* Keep the disk spinning (for seeking and browsing) */ |
153 | static inline void stream_keep_disk_active(void) | 161 | static inline void stream_keep_disk_active(void) |
154 | { | 162 | { |
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 | |||
108 | VIDEO_PRINT_FRAME, /* Print the frame at the current position */ | 108 | VIDEO_PRINT_FRAME, /* Print the frame at the current position */ |
109 | VIDEO_PRINT_THUMBNAIL, /* Print a thumbnail of the current position */ | 109 | VIDEO_PRINT_THUMBNAIL, /* Print a thumbnail of the current position */ |
110 | VIDEO_SET_CLIP_RECT, /* Set the visible video area */ | 110 | VIDEO_SET_CLIP_RECT, /* Set the visible video area */ |
111 | VIDEO_GET_CLIP_RECT, /* Return the visible video area */ | ||
112 | VIDEO_SET_POST_FRAME_CALLBACK, /* Set a callback after frame is drawn */ | ||
111 | STREAM_MESSAGE_LAST, | 113 | STREAM_MESSAGE_LAST, |
112 | }; | 114 | }; |
113 | 115 | ||
@@ -160,9 +162,20 @@ extern struct stream audio_str IBSS_ATTR; | |||
160 | 162 | ||
161 | bool video_thread_init(void); | 163 | bool video_thread_init(void); |
162 | void video_thread_exit(void); | 164 | void video_thread_exit(void); |
165 | |||
166 | struct video_output_stats | ||
167 | { | ||
168 | int num_drawn; /* Number of frames drawn since reset */ | ||
169 | int num_skipped; /* Number of frames skipped since reset */ | ||
170 | int fps; /* fps rate in 100ths of a frame per second */ | ||
171 | }; | ||
172 | |||
173 | void video_thread_get_stats(struct video_output_stats *s); | ||
174 | |||
163 | bool audio_thread_init(void); | 175 | bool audio_thread_init(void); |
164 | void audio_thread_exit(void); | 176 | void audio_thread_exit(void); |
165 | 177 | ||
178 | |||
166 | /* Some queue function wrappers to keep things clean-ish */ | 179 | /* Some queue function wrappers to keep things clean-ish */ |
167 | 180 | ||
168 | /* For stream use only */ | 181 | /* 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); | |||
58 | bool vo_is_visible(void); | 58 | bool vo_is_visible(void); |
59 | void vo_setup (const mpeg2_sequence_t * sequence); | 59 | void vo_setup (const mpeg2_sequence_t * sequence); |
60 | void vo_set_clip_rect(const struct vo_rect *rc); | 60 | void vo_set_clip_rect(const struct vo_rect *rc); |
61 | bool vo_get_clip_rect(struct vo_rect *rc); | ||
61 | void vo_dimensions(struct vo_ext *sz); | 62 | void vo_dimensions(struct vo_ext *sz); |
62 | void vo_cleanup (void); | 63 | void vo_cleanup (void); |
64 | void vo_set_post_draw_callback(void (*cb)(void)); | ||
63 | 65 | ||
64 | #if NUM_CORES > 1 | 66 | #if NUM_CORES > 1 |
65 | void vo_lock(void); | 67 | 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 | |||
41 | unsigned flags; | 41 | unsigned flags; |
42 | struct vo_rect rc_vid; | 42 | struct vo_rect rc_vid; |
43 | struct vo_rect rc_clip; | 43 | struct vo_rect rc_clip; |
44 | void (*post_draw_callback)(void); | ||
44 | }; | 45 | }; |
45 | 46 | ||
46 | #if NUM_CORES > 1 | 47 | #if NUM_CORES > 1 |
@@ -80,9 +81,10 @@ static inline void video_unlock(void) | |||
80 | 81 | ||
81 | 82 | ||
82 | /* Draw a black rectangle if no video frame is available */ | 83 | /* Draw a black rectangle if no video frame is available */ |
83 | static void vo_draw_black(void) | 84 | static void vo_draw_black(struct vo_rect *rc) |
84 | { | 85 | { |
85 | int foreground; | 86 | int foreground; |
87 | int x, y, w, h; | ||
86 | 88 | ||
87 | video_lock(); | 89 | video_lock(); |
88 | 90 | ||
@@ -90,10 +92,30 @@ static void vo_draw_black(void) | |||
90 | 92 | ||
91 | mylcd_set_foreground(MYLCD_BLACK); | 93 | mylcd_set_foreground(MYLCD_BLACK); |
92 | 94 | ||
93 | mylcd_fillrect(vo.output_x, vo.output_y, vo.output_width, | 95 | if (rc) |
94 | vo.output_height); | 96 | { |
95 | mylcd_update_rect(vo.output_x, vo.output_y, vo.output_width, | 97 | x = rc->l; |
96 | vo.output_height); | 98 | y = rc->t; |
99 | w = rc->r - rc->l; | ||
100 | h = rc->b - rc->t; | ||
101 | } | ||
102 | else | ||
103 | { | ||
104 | #if LCD_WIDTH >= LCD_HEIGHT | ||
105 | x = vo.output_x; | ||
106 | y = vo.output_y; | ||
107 | w = vo.output_width; | ||
108 | h = vo.output_height; | ||
109 | #else | ||
110 | x = LCD_WIDTH - vo.output_height - vo.output_y; | ||
111 | y = vo.output_x; | ||
112 | w = vo.output_height; | ||
113 | h = vo.output_width; | ||
114 | #endif | ||
115 | } | ||
116 | |||
117 | mylcd_fillrect(x, y, w, h); | ||
118 | mylcd_update_rect(x, y, w, h); | ||
97 | 119 | ||
98 | mylcd_set_foreground(foreground); | 120 | mylcd_set_foreground(foreground); |
99 | 121 | ||
@@ -122,19 +144,22 @@ void vo_draw_frame(uint8_t * const * buf) | |||
122 | /* Frame is hidden - either by being set invisible or is clipped | 144 | /* Frame is hidden - either by being set invisible or is clipped |
123 | * away - copout */ | 145 | * away - copout */ |
124 | DEBUGF("vo hidden\n"); | 146 | DEBUGF("vo hidden\n"); |
125 | return; | ||
126 | } | 147 | } |
127 | else if (buf == NULL) | 148 | else if (buf == NULL) |
128 | { | 149 | { |
129 | /* No frame exists - draw black */ | 150 | /* No frame exists - draw black */ |
130 | vo_draw_black(); | 151 | vo_draw_black(NULL); |
131 | DEBUGF("vo no frame\n"); | 152 | DEBUGF("vo no frame\n"); |
132 | return; | 153 | } |
154 | else | ||
155 | { | ||
156 | yuv_blit(buf, 0, 0, vo.image_width, | ||
157 | vo.output_x, vo.output_y, vo.output_width, | ||
158 | vo.output_height); | ||
133 | } | 159 | } |
134 | 160 | ||
135 | yuv_blit(buf, 0, 0, vo.image_width, | 161 | if (vo.post_draw_callback) |
136 | vo.output_x, vo.output_y, vo.output_width, | 162 | vo.post_draw_callback(); |
137 | vo.output_height); | ||
138 | } | 163 | } |
139 | 164 | ||
140 | static inline void vo_rect_clear_inl(struct vo_rect *rc) | 165 | 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) | |||
348 | int thumb_width, thumb_height; | 373 | int thumb_width, thumb_height; |
349 | int thumb_uv_width, thumb_uv_height; | 374 | int thumb_uv_width, thumb_uv_height; |
350 | 375 | ||
351 | if (buf == NULL) | ||
352 | return false; | ||
353 | |||
354 | /* Obtain rectangle as clipped to the screen */ | 376 | /* Obtain rectangle as clipped to the screen */ |
355 | vo_rect_set_ext(&thumb_rc, 0, 0, LCD_WIDTH, LCD_HEIGHT); | 377 | vo_rect_set_ext(&thumb_rc, 0, 0, LCD_WIDTH, LCD_HEIGHT); |
356 | if (!vo_rect_intersect(&thumb_rc, rc, &thumb_rc)) | 378 | if (!vo_rect_intersect(&thumb_rc, rc, &thumb_rc)) |
357 | return true; | 379 | return true; |
358 | 380 | ||
381 | if (buf == NULL) | ||
382 | goto no_thumb_exit; | ||
383 | |||
359 | DEBUGF("thumb_rc: %d, %d, %d, %d\n", thumb_rc.l, thumb_rc.t, | 384 | DEBUGF("thumb_rc: %d, %d, %d, %d\n", thumb_rc.l, thumb_rc.t, |
360 | thumb_rc.r, thumb_rc.b); | 385 | thumb_rc.r, thumb_rc.b); |
361 | 386 | ||
@@ -377,7 +402,7 @@ bool vo_draw_frame_thumb(uint8_t * const * buf, const struct vo_rect *rc) | |||
377 | ) | 402 | ) |
378 | { | 403 | { |
379 | DEBUGF("thumb: insufficient buffer\n"); | 404 | DEBUGF("thumb: insufficient buffer\n"); |
380 | return false; | 405 | goto no_thumb_exit; |
381 | } | 406 | } |
382 | 407 | ||
383 | yuv[0] = mem; | 408 | yuv[0] = mem; |
@@ -411,6 +436,10 @@ bool vo_draw_frame_thumb(uint8_t * const * buf, const struct vo_rect *rc) | |||
411 | #endif /* LCD_WIDTH >= LCD_HEIGHT */ | 436 | #endif /* LCD_WIDTH >= LCD_HEIGHT */ |
412 | 437 | ||
413 | return true; | 438 | return true; |
439 | |||
440 | no_thumb_exit: | ||
441 | vo_draw_black(&thumb_rc); | ||
442 | return false; | ||
414 | } | 443 | } |
415 | 444 | ||
416 | void vo_setup(const mpeg2_sequence_t * sequence) | 445 | void vo_setup(const mpeg2_sequence_t * sequence) |
@@ -512,6 +541,20 @@ void vo_set_clip_rect(const struct vo_rect *rc) | |||
512 | vo.output_height = rc_out.b - rc_out.t; | 541 | vo.output_height = rc_out.b - rc_out.t; |
513 | } | 542 | } |
514 | 543 | ||
544 | bool vo_get_clip_rect(struct vo_rect *rc) | ||
545 | { | ||
546 | rc->l = vo.output_x; | ||
547 | rc->t = vo.output_y; | ||
548 | rc->r = rc->l + vo.output_width; | ||
549 | rc->b = rc->t + vo.output_height; | ||
550 | return (vo.flags & VO_NON_NULL_RECT) != 0; | ||
551 | } | ||
552 | |||
553 | void vo_set_post_draw_callback(void (*cb)(void)) | ||
554 | { | ||
555 | vo.post_draw_callback = cb; | ||
556 | } | ||
557 | |||
515 | #if NUM_CORES > 1 | 558 | #if NUM_CORES > 1 |
516 | void vo_lock(void) | 559 | void vo_lock(void) |
517 | { | 560 | { |
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 | |||
37 | int state; /* Thread state */ | 37 | int state; /* Thread state */ |
38 | int status; /* Media status */ | 38 | int status; /* Media status */ |
39 | struct queue_event ev; /* Our event queue to receive commands */ | 39 | struct queue_event ev; /* Our event queue to receive commands */ |
40 | int num_drawn; /* Number of frames drawn since reset */ | ||
41 | int num_skipped; /* Number of frames skipped since reset */ | ||
42 | uint32_t eta_stream; /* Current time of stream */ | 40 | uint32_t eta_stream; /* Current time of stream */ |
43 | uint32_t eta_video; /* Time that frame has been scheduled for */ | 41 | uint32_t eta_video; /* Time that frame has been scheduled for */ |
44 | int32_t eta_early; /* How early has the frame been decoded? */ | 42 | int32_t eta_early; /* How early has the frame been decoded? */ |
45 | int32_t eta_late; /* How late has the frame been decoded? */ | 43 | int32_t eta_late; /* How late has the frame been decoded? */ |
46 | int frame_drop_level; /* Drop severity */ | 44 | int frame_drop_level; /* Drop severity */ |
47 | int skip_level; /* Skip severity */ | 45 | int skip_level; /* Skip severity */ |
48 | long last_showfps; /* Last time the FPS display was updated */ | ||
49 | long last_render; /* Last time a frame was drawn */ | 46 | long last_render; /* Last time a frame was drawn */ |
50 | uint32_t curr_time; /* Current due time of frame */ | 47 | uint32_t curr_time; /* Current due time of frame */ |
51 | uint32_t period; /* Frame period in clock ticks */ | 48 | uint32_t period; /* Frame period in clock ticks */ |
52 | int syncf_perfect; /* Last sync fit result */ | 49 | int syncf_perfect; /* Last sync fit result */ |
53 | }; | 50 | }; |
54 | 51 | ||
52 | /* Number drawn since reset */ | ||
53 | static int video_num_drawn SHAREDBSS_ATTR; | ||
54 | /* Number skipped since reset */ | ||
55 | static int video_num_skipped SHAREDBSS_ATTR; | ||
56 | |||
55 | /* TODO: Check if 4KB is appropriate - it works for my test streams, | 57 | /* TODO: Check if 4KB is appropriate - it works for my test streams, |
56 | so maybe we can reduce it. */ | 58 | so maybe we can reduce it. */ |
57 | #define VIDEO_STACKSIZE (4*1024) | 59 | #define VIDEO_STACKSIZE (4*1024) |
@@ -60,35 +62,6 @@ static struct event_queue video_str_queue SHAREDBSS_ATTR; | |||
60 | static struct queue_sender_list video_str_queue_send SHAREDBSS_ATTR; | 62 | static struct queue_sender_list video_str_queue_send SHAREDBSS_ATTR; |
61 | struct stream video_str IBSS_ATTR; | 63 | struct stream video_str IBSS_ATTR; |
62 | 64 | ||
63 | static void draw_fps(struct video_thread_data *td) | ||
64 | { | ||
65 | uint32_t start; | ||
66 | uint32_t clock_ticks = stream_get_ticks(&start); | ||
67 | int fps = 0; | ||
68 | int buf_pct; | ||
69 | char str[80]; | ||
70 | |||
71 | clock_ticks -= start; | ||
72 | if (clock_ticks != 0) | ||
73 | fps = muldiv_uint32(CLOCK_RATE*100, td->num_drawn, clock_ticks); | ||
74 | |||
75 | buf_pct = muldiv_uint32(100, pcm_output_used(), PCMOUT_BUFSIZE); | ||
76 | |||
77 | rb->snprintf(str, sizeof(str), "v:%d.%02d %d %d a:%02d%% %d %d ", | ||
78 | /* Video information */ | ||
79 | fps / 100, fps % 100, td->num_skipped, | ||
80 | td->info->display_picture->temporal_reference, | ||
81 | /* Audio information */ | ||
82 | buf_pct, pcm_underruns, pcm_skipped); | ||
83 | mylcd_putsxy(0, 0, str); | ||
84 | |||
85 | vo_lock(); | ||
86 | mylcd_update_rect(0, 0, LCD_WIDTH, 8); | ||
87 | vo_unlock(); | ||
88 | |||
89 | td->last_showfps = *rb->current_tick; | ||
90 | } | ||
91 | |||
92 | #if defined(DEBUG) || defined(SIMULATOR) | 65 | #if defined(DEBUG) || defined(SIMULATOR) |
93 | static unsigned char pic_coding_type_char(unsigned type) | 66 | static unsigned char pic_coding_type_char(unsigned type) |
94 | { | 67 | { |
@@ -452,6 +425,31 @@ sync_finished: | |||
452 | return retval; | 425 | return retval; |
453 | } | 426 | } |
454 | 427 | ||
428 | static bool frame_print_handler(struct video_thread_data *td) | ||
429 | { | ||
430 | bool retval; | ||
431 | uint8_t * const * buf = NULL; | ||
432 | |||
433 | if (td->info != NULL && td->info->display_fbuf != NULL && | ||
434 | td->syncf_perfect > 0) | ||
435 | buf = td->info->display_fbuf->buf; | ||
436 | |||
437 | if (td->ev.id == VIDEO_PRINT_THUMBNAIL) | ||
438 | { | ||
439 | /* Print a thumbnail of whatever was last decoded - scale and | ||
440 | * position to fill the specified rectangle */ | ||
441 | retval = vo_draw_frame_thumb(buf, (struct vo_rect *)td->ev.data); | ||
442 | } | ||
443 | else | ||
444 | { | ||
445 | /* Print the last frame decoded */ | ||
446 | vo_draw_frame(buf); | ||
447 | retval = buf != NULL; | ||
448 | } | ||
449 | |||
450 | return retval; | ||
451 | } | ||
452 | |||
455 | /* This only returns to play or quit */ | 453 | /* This only returns to play or quit */ |
456 | static void video_thread_msg(struct video_thread_data *td) | 454 | static void video_thread_msg(struct video_thread_data *td) |
457 | { | 455 | { |
@@ -520,8 +518,7 @@ static void video_thread_msg(struct video_thread_data *td) | |||
520 | 518 | ||
521 | if (td->ev.data) | 519 | if (td->ev.data) |
522 | { | 520 | { |
523 | if (td->info != NULL && td->info->display_fbuf != NULL) | 521 | frame_print_handler(td); |
524 | vo_draw_frame(td->info->display_fbuf->buf); | ||
525 | } | 522 | } |
526 | else | 523 | else |
527 | { | 524 | { |
@@ -547,10 +544,9 @@ static void video_thread_msg(struct video_thread_data *td) | |||
547 | td->eta_late = 0; | 544 | td->eta_late = 0; |
548 | td->frame_drop_level = 0; | 545 | td->frame_drop_level = 0; |
549 | td->skip_level = 0; | 546 | td->skip_level = 0; |
550 | td->num_drawn = 0; | 547 | td->last_render = *rb->current_tick - HZ; |
551 | td->num_skipped = 0; | 548 | video_num_drawn = 0; |
552 | td->last_showfps = *rb->current_tick - HZ; | 549 | video_num_skipped = 0; |
553 | td->last_render = td->last_showfps; | ||
554 | 550 | ||
555 | reply = true; | 551 | reply = true; |
556 | break; | 552 | break; |
@@ -573,28 +569,17 @@ static void video_thread_msg(struct video_thread_data *td) | |||
573 | str_data_notify_received(&video_str); | 569 | str_data_notify_received(&video_str); |
574 | break; | 570 | break; |
575 | 571 | ||
572 | case VIDEO_PRINT_FRAME: | ||
576 | case VIDEO_PRINT_THUMBNAIL: | 573 | case VIDEO_PRINT_THUMBNAIL: |
577 | /* Print a thumbnail of whatever was last decoded - scale and | 574 | reply = frame_print_handler(td); |
578 | * position to fill the specified rectangle */ | ||
579 | if (td->info != NULL && td->info->display_fbuf != NULL) | ||
580 | { | ||
581 | vo_draw_frame_thumb(td->info->display_fbuf->buf, | ||
582 | (struct vo_rect *)td->ev.data); | ||
583 | reply = true; | ||
584 | } | ||
585 | break; | 575 | break; |
586 | 576 | ||
587 | case VIDEO_SET_CLIP_RECT: | 577 | case VIDEO_SET_CLIP_RECT: |
588 | vo_set_clip_rect((const struct vo_rect *)td->ev.data); | 578 | vo_set_clip_rect((const struct vo_rect *)td->ev.data); |
589 | break; | 579 | break; |
590 | 580 | ||
591 | case VIDEO_PRINT_FRAME: | 581 | case VIDEO_GET_CLIP_RECT: |
592 | /* Print the last frame decoded */ | 582 | reply = vo_get_clip_rect((struct vo_rect *)td->ev.data); |
593 | if (td->info != NULL && td->info->display_fbuf != NULL) | ||
594 | { | ||
595 | vo_draw_frame(td->info->display_fbuf->buf); | ||
596 | reply = true; | ||
597 | } | ||
598 | break; | 583 | break; |
599 | 584 | ||
600 | case VIDEO_GET_SIZE: | 585 | case VIDEO_GET_SIZE: |
@@ -621,6 +606,11 @@ static void video_thread_msg(struct video_thread_data *td) | |||
621 | reply = video_str_scan(td, (struct str_sync_data *)td->ev.data); | 606 | reply = video_str_scan(td, (struct str_sync_data *)td->ev.data); |
622 | break; | 607 | break; |
623 | 608 | ||
609 | case VIDEO_SET_POST_FRAME_CALLBACK: | ||
610 | vo_set_post_draw_callback((void (*)(void))td->ev.data); | ||
611 | reply = true; | ||
612 | break; | ||
613 | |||
624 | case STREAM_QUIT: | 614 | case STREAM_QUIT: |
625 | /* Time to go - make thread exit */ | 615 | /* Time to go - make thread exit */ |
626 | td->state = TSTATE_EOS; | 616 | td->state = TSTATE_EOS; |
@@ -802,6 +792,8 @@ static void video_thread(void) | |||
802 | if (td.info->display_fbuf == NULL) | 792 | if (td.info->display_fbuf == NULL) |
803 | break; /* No picture */ | 793 | break; /* No picture */ |
804 | 794 | ||
795 | td.syncf_perfect = 1; /* yes, a frame exists */ | ||
796 | |||
805 | /* Get presentation times in audio samples - quite accurate | 797 | /* Get presentation times in audio samples - quite accurate |
806 | enough - add previous frame duration if not stamped */ | 798 | enough - add previous frame duration if not stamped */ |
807 | td.curr_time = (td.info->display_picture->flags & PIC_FLAG_TAGS) ? | 799 | td.curr_time = (td.info->display_picture->flags & PIC_FLAG_TAGS) ? |
@@ -902,8 +894,8 @@ static void video_thread(void) | |||
902 | { | 894 | { |
903 | /* This frame was set to skip so skip it after having updated | 895 | /* This frame was set to skip so skip it after having updated |
904 | timing information */ | 896 | timing information */ |
905 | td.num_skipped++; | ||
906 | td.eta_early = INT32_MIN; | 897 | td.eta_early = INT32_MIN; |
898 | video_num_skipped++; | ||
907 | goto picture_skip; | 899 | goto picture_skip; |
908 | } | 900 | } |
909 | 901 | ||
@@ -913,8 +905,8 @@ static void video_thread(void) | |||
913 | /* Render drop was set previously but nothing was dropped in the | 905 | /* Render drop was set previously but nothing was dropped in the |
914 | decoder or it's been to long since drawing the last frame. */ | 906 | decoder or it's been to long since drawing the last frame. */ |
915 | td.skip_level = 0; | 907 | td.skip_level = 0; |
916 | td.num_skipped++; | ||
917 | td.eta_early = INT32_MIN; | 908 | td.eta_early = INT32_MIN; |
909 | video_num_skipped++; | ||
918 | goto picture_skip; | 910 | goto picture_skip; |
919 | } | 911 | } |
920 | 912 | ||
@@ -970,18 +962,11 @@ static void video_thread(void) | |||
970 | picture_draw: | 962 | picture_draw: |
971 | /* Record last frame time */ | 963 | /* Record last frame time */ |
972 | td.last_render = *rb->current_tick; | 964 | td.last_render = *rb->current_tick; |
965 | |||
973 | vo_draw_frame(td.info->display_fbuf->buf); | 966 | vo_draw_frame(td.info->display_fbuf->buf); |
974 | td.num_drawn++; | 967 | video_num_drawn++; |
975 | 968 | ||
976 | picture_skip: | 969 | picture_skip: |
977 | if (!settings.showfps) | ||
978 | break; | ||
979 | |||
980 | if (TIME_BEFORE(*rb->current_tick, td.last_showfps + HZ)) | ||
981 | break; | ||
982 | |||
983 | /* Calculate and display fps */ | ||
984 | draw_fps(&td); | ||
985 | break; | 970 | break; |
986 | } | 971 | } |
987 | 972 | ||
@@ -1032,3 +1017,19 @@ void video_thread_exit(void) | |||
1032 | video_str.thread = 0; | 1017 | video_str.thread = 0; |
1033 | } | 1018 | } |
1034 | } | 1019 | } |
1020 | |||
1021 | |||
1022 | /** Misc **/ | ||
1023 | void video_thread_get_stats(struct video_output_stats *s) | ||
1024 | { | ||
1025 | uint32_t start; | ||
1026 | uint32_t now = stream_get_ticks(&start); | ||
1027 | s->num_drawn = video_num_drawn; | ||
1028 | s->num_skipped = video_num_skipped; | ||
1029 | |||
1030 | s->fps = 0; | ||
1031 | |||
1032 | if (now > start) | ||
1033 | s->fps = muldiv_uint32(CLOCK_RATE*100, s->num_drawn, now - start); | ||
1034 | } | ||
1035 | |||