summaryrefslogtreecommitdiff
path: root/apps/plugins/mpegplayer
diff options
context:
space:
mode:
authorDave Chapman <dave@dchapman.com>2006-08-20 23:12:56 +0000
committerDave Chapman <dave@dchapman.com>2006-08-20 23:12:56 +0000
commitc8e69dfb71d936b4bc5e18f6246ac126c629f772 (patch)
treed43563a0c30011d3ec7af1e13e1892bdfff60a46 /apps/plugins/mpegplayer
parent18cfe431d7556f8cd7047018febe191073c26a1f (diff)
downloadrockbox-c8e69dfb71d936b4bc5e18f6246ac126c629f772.zip
rockbox-c8e69dfb71d936b4bc5e18f6246ac126c629f772.tar.gz
rockbox-c8e69dfb71d936b4bc5e18f6246ac126c629f772.tar.bz2
rockbox-c8e69dfb71d936b4bc5e18f6246ac126c629f772.tar.xz
Move FPS display out of video_out_rockbox.c and into mpegplayer.c. Also add frame-rate limiting and frame-skipping (skipping display only, not decoding) to try and achieve real-time playback. Frame-rate limiting and frame skipping (and FPS display) are enabled via options in a new menu and are currently all off by default.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10669 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/mpegplayer')
-rw-r--r--apps/plugins/mpegplayer/SOURCES1
-rw-r--r--apps/plugins/mpegplayer/mpeg_settings.c118
-rw-r--r--apps/plugins/mpegplayer/mpeg_settings.h14
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.c153
-rw-r--r--apps/plugins/mpegplayer/video_out_rockbox.c25
5 files changed, 279 insertions, 32 deletions
diff --git a/apps/plugins/mpegplayer/SOURCES b/apps/plugins/mpegplayer/SOURCES
index 2ea6f30..4646599 100644
--- a/apps/plugins/mpegplayer/SOURCES
+++ b/apps/plugins/mpegplayer/SOURCES
@@ -7,4 +7,5 @@ idct.c
motion_comp.c
slice.c
video_out_rockbox.c
+mpeg_settings.c
mpegplayer.c
diff --git a/apps/plugins/mpegplayer/mpeg_settings.c b/apps/plugins/mpegplayer/mpeg_settings.c
new file mode 100644
index 0000000..dc63626
--- /dev/null
+++ b/apps/plugins/mpegplayer/mpeg_settings.c
@@ -0,0 +1,118 @@
+#include "plugin.h"
+#include "lib/configfile.h"
+
+#include "mpeg_settings.h"
+
+extern struct plugin_api* rb;
+
+struct mpeg_settings settings;
+static struct mpeg_settings old_settings;
+
+#define SETTINGS_VERSION 1
+#define SETTINGS_MIN_VERSION 1
+#define SETTINGS_FILENAME "mpegplayer.cfg"
+
+static char* showfps_options[] = {"No", "Yes"};
+static char* limitfps_options[] = {"No", "Yes"};
+static char* skipframes_options[] = {"No", "Yes"};
+
+static struct configdata config[] =
+{
+ {TYPE_ENUM, 0, 2, &settings.showfps, "Show FPS", showfps_options, NULL},
+ {TYPE_ENUM, 0, 2, &settings.limitfps, "Limit FPS", limitfps_options, NULL},
+ {TYPE_ENUM, 0, 2, &settings.skipframes, "Skip frames", skipframes_options, NULL},
+};
+
+bool mpeg_menu(void)
+{
+ int m;
+ int result;
+ int menu_quit=0;
+
+ static const struct opt_items noyes[2] = {
+ { "No", -1 },
+ { "Yes", -1 },
+ };
+
+ static const struct menu_item items[] = {
+ { "Display FPS", NULL },
+ { "Limit FPS", NULL },
+ { "Skip frames", NULL },
+ { "Quit mpegplayer", NULL },
+ };
+
+ m = rb->menu_init(items, sizeof(items) / sizeof(*items),
+ NULL, NULL, NULL, NULL);
+
+ rb->button_clear_queue();
+
+ while (!menu_quit) {
+ result=rb->menu_show(m);
+
+ switch(result)
+ {
+ case 0: /* Show FPS */
+ rb->set_option("Display FPS",&settings.showfps,INT,
+ noyes, 2, NULL);
+ break;
+ case 1: /* Limit FPS */
+ rb->set_option("Limit FPS",&settings.limitfps,INT,
+ noyes, 2, NULL);
+ break;
+ case 2: /* Skip frames */
+ rb->set_option("Skip frames",&settings.skipframes,INT,
+ noyes, 2, NULL);
+ break;
+ default:
+ menu_quit=1;
+ break;
+ }
+ }
+
+ rb->menu_exit(m);
+
+ rb->lcd_clear_display();
+ rb->lcd_update();
+
+ return (result==3);
+}
+
+
+void init_settings(void)
+{
+ /* Set the default settings */
+ settings.showfps = 0; /* Do not show FPS */
+ settings.limitfps = 0; /* Do not limit FPS */
+ settings.skipframes = 0; /* Do not skip frames */
+
+ configfile_init(rb);
+
+ if (configfile_load(SETTINGS_FILENAME, config,
+ sizeof(config)/sizeof(*config),
+ SETTINGS_MIN_VERSION
+ ) < 0)
+ {
+ /* If the loading failed, save a new config file (as the disk is
+ already spinning) */
+ configfile_save(SETTINGS_FILENAME, config,
+ sizeof(config)/sizeof(*config),
+ SETTINGS_VERSION);
+ }
+
+ /* Keep a copy of the saved version of the settings - so we can check if
+ the settings have changed when we quit */
+ old_settings = settings;
+}
+
+void save_settings(void)
+{
+ /* Save the user settings if they have changed */
+ if (rb->memcmp(&settings,&old_settings,sizeof(settings))!=0) {
+ configfile_save(SETTINGS_FILENAME, config,
+ sizeof(config)/sizeof(*config),
+ SETTINGS_VERSION);
+
+ /* Store the settings in old_settings - to check for future changes */
+ old_settings = settings;
+ }
+}
diff --git a/apps/plugins/mpegplayer/mpeg_settings.h b/apps/plugins/mpegplayer/mpeg_settings.h
new file mode 100644
index 0000000..cd2dcd2
--- /dev/null
+++ b/apps/plugins/mpegplayer/mpeg_settings.h
@@ -0,0 +1,14 @@
+
+#include "plugin.h"
+
+struct mpeg_settings {
+ int showfps;
+ int limitfps;
+ int skipframes;
+};
+
+extern struct mpeg_settings settings;
+
+bool mpeg_menu(void);
+void init_settings(void);
+void save_settings(void);
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c
index be028c0..67569a7 100644
--- a/apps/plugins/mpegplayer/mpegplayer.c
+++ b/apps/plugins/mpegplayer/mpegplayer.c
@@ -27,6 +27,7 @@
#include "plugin.h"
#include "mpeg2.h"
+#include "mpeg_settings.h"
#include "video_out.h"
PLUGIN_HEADER
@@ -41,44 +42,88 @@ extern char iend[];
struct plugin_api* rb;
-/* The main buffer storing the compressed video data */
-#define BUFFER_SIZE (MEM-6)*1024*1024
-
static mpeg2dec_t * mpeg2dec;
static int total_offset = 0;
/* button definitions */
#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
+#define MPEG_MENU BUTTON_MODE
#define MPEG_STOP BUTTON_OFF
#define MPEG_PAUSE BUTTON_ON
#elif (CONFIG_KEYPAD == IPOD_3G_PAD) || (CONFIG_KEYPAD == IPOD_4G_PAD)
-#define MPEG_STOP BUTTON_MENU
-#define MPEG_PAUSE BUTTON_PLAY
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
+#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
#elif CONFIG_KEYPAD == IAUDIO_X5_PAD
+#define MPEG_MENU (BUTTON_REC | BUTTON_REL)
#define MPEG_STOP BUTTON_POWER
#define MPEG_PAUSE BUTTON_PLAY
#elif CONFIG_KEYPAD == GIGABEAT_PAD
+#define MPEG_MENU BUTTON_MENU
#define MPEG_STOP BUTTON_A
#define MPEG_PAUSE BUTTON_SELECT
#elif CONFIG_KEYPAD == IRIVER_H10_PAD
+#define MPEG_MENU (BUTTON_REW | BUTTON_REL)
#define MPEG_STOP BUTTON_POWER
#define MPEG_PAUSE BUTTON_PLAY
#else
#error MPEGPLAYER: Unsupported keypad
#endif
+
+static int tick_enabled = 0;
+
+#define MPEG_CURRENT_TICK ((unsigned int)((*rb->current_tick - tick_offset)))
+
+/* The value to subtract from current_tick to get the current mpeg tick */
+static int tick_offset;
+
+/* The last tick - i.e. the time to reset the tick_offset to when unpausing */
+static int last_tick;
+
+void start_timer(void)
+{
+ last_tick = 0;
+ tick_offset = *rb->current_tick;
+}
+
+void unpause_timer(void)
+{
+ tick_offset = *rb->current_tick - last_tick;
+}
+
+void pause_timer(void)
+{
+ /* Save the current MPEG tick */
+ last_tick = *rb->current_tick - tick_offset;
+}
+
+
static bool button_loop(void)
{
+ bool result;
int button = rb->button_get(false);
+
switch (button)
{
+ case MPEG_MENU:
+ pause_timer();
+
+ result = mpeg_menu();
+
+ unpause_timer();
+
+ return result;
+
case MPEG_STOP:
return true;
+
case MPEG_PAUSE:
+ pause_timer(); /* Freeze time */
button = BUTTON_NONE;
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
@@ -91,7 +136,9 @@ static bool button_loop(void)
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(true);
#endif
+ unpause_timer(); /* Resume time */
break;
+
default:
if(rb->default_event_handler(button) == SYS_USB_CONNECTED)
return true;
@@ -99,10 +146,59 @@ static bool button_loop(void)
return false;
}
+/*
+
+NOTES:
+
+MPEG System Clock is 27MHz - i.e. 27000000 ticks/second.
+
+FPS is represented in terms of a frame period - this is always an
+integer number of 27MHz ticks.
+
+e.g. 29.97fps (30000/1001) NTSC video has an exact frame period of
+900900 27MHz ticks.
+
+In libmpeg2, info->sequence->frame_period contains the frame_period.
+
+Working with Rockbox's 100Hz tick, the common frame rates would need
+to be as follows:
+
+FPS | 27Mhz | 100Hz
+--------|----------------
+10* | 2700000 | 10
+12* | 2250000 | 8.3333
+15* | 1800000 | 6.6667
+23.9760 | 1126125 | 4.170833333
+24 | 1125000 | 4.166667
+25 | 1080000 | 4
+29.9700 | 900900 | 3.336667
+30 | 900000 | 3.333333
+
+
+*Unofficial framerates
+
+*/
+
+static uint64_t eta;
+
static bool decode_mpeg2 (uint8_t * current, uint8_t * end)
{
const mpeg2_info_t * info;
mpeg2_state_t state;
+ char str[80];
+ static int skipped = 0;
+ static int frame = 0;
+ static int starttick = 0;
+ static int lasttick;
+ unsigned int eta2;
+ unsigned int x;
+
+ int fps;
+
+ if (starttick == 0) {
+ starttick=*rb->current_tick-1; /* Avoid divby0 */
+ lasttick=starttick;
+ }
mpeg2_buffer (mpeg2dec, current, end);
total_offset += end - current;
@@ -122,6 +218,7 @@ static bool decode_mpeg2 (uint8_t * current, uint8_t * end)
info->sequence->chroma_width,
info->sequence->chroma_height);
mpeg2_skip (mpeg2dec, false);
+
break;
case STATE_PICTURE:
break;
@@ -129,8 +226,44 @@ static bool decode_mpeg2 (uint8_t * current, uint8_t * end)
case STATE_END:
case STATE_INVALID_END:
/* draw current picture */
- if (info->display_fbuf)
- vo_draw_frame(info->display_fbuf->buf);
+ if (info->display_fbuf) {
+ /* We start the timer when we draw the first frame */
+ if (!tick_enabled) {
+ start_timer();
+ tick_enabled = 1 ;
+ }
+
+ eta += (info->sequence->frame_period);
+ eta2 = eta / (27000000 / HZ);
+
+ if (settings.limitfps) {
+ if (eta2 > MPEG_CURRENT_TICK) {
+ rb->sleep(eta2-MPEG_CURRENT_TICK);
+ }
+ }
+ x = MPEG_CURRENT_TICK;
+
+ /* If we are more than 1/20 second behind schedule (and
+ more than 1/20 second into the decoding), skip frame */
+ if (settings.skipframes && (x > HZ/20) &&
+ (eta2 < (x - (HZ/20)))) {
+ skipped++;
+ } else {
+ vo_draw_frame(info->display_fbuf->buf);
+ }
+
+ /* Calculate fps */
+ frame++;
+ if (settings.showfps && (*rb->current_tick-lasttick>=HZ)) {
+ fps=(frame*(HZ*10))/x;
+ rb->snprintf(str,sizeof(str),"%d.%d %d %d %d",
+ (fps/10),fps%10,skipped,x,eta2);
+ rb->lcd_putsxy(0,0,str);
+ rb->lcd_update_rect(0,0,LCD_WIDTH,8);
+
+ lasttick = *rb->current_tick;
+ }
+ }
break;
default:
break;
@@ -147,8 +280,10 @@ static void es_loop (int in_file, uint8_t* buffer, size_t buffer_size)
if (buffer==NULL)
return;
+ eta = 0;
do {
rb->splash(0,true,"Buffering...");
+ save_settings(); /* Save settings (if they have changed) */
end = buffer + rb->read (in_file, buffer, buffer_size);
if (decode_mpeg2 (buffer, end))
break;
@@ -202,6 +337,8 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
return PLUGIN_ERROR;
}
+ init_settings();
+
mpeg2dec = mpeg2_init ();
if (mpeg2dec == NULL)
@@ -230,6 +367,8 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
rb->cpu_boost(false);
#endif
+ save_settings(); /* Save settings (if they have changed) */
+
#ifdef CONFIG_BACKLIGHT
/* reset backlight settings */
rb->backlight_set_timeout(rb->global_settings->backlight_timeout);
diff --git a/apps/plugins/mpegplayer/video_out_rockbox.c b/apps/plugins/mpegplayer/video_out_rockbox.c
index 258416f..38a76a7 100644
--- a/apps/plugins/mpegplayer/video_out_rockbox.c
+++ b/apps/plugins/mpegplayer/video_out_rockbox.c
@@ -30,9 +30,6 @@ extern struct plugin_api* rb;
#include "mpeg2.h"
#include "video_out.h"
-static int starttick = 0;
-static int lasttick = 0;
-
#define CSUB_X 2
#define CSUB_Y 2
@@ -191,10 +188,6 @@ static void yuv_bitmap_part(unsigned char * const src[3],
void vo_draw_frame (uint8_t * const * buf)
{
- char str[80];
- static int frame=0;
- int ticks,fps;
-
#ifdef SIMULATOR
yuv_bitmap_part(buf,0,0,image_width,
output_x,output_y,output_width,output_height);
@@ -204,24 +197,6 @@ void vo_draw_frame (uint8_t * const * buf)
0,0,image_width,
output_x,output_y,output_width,output_height);
#endif
-
- if (starttick==0) {
- starttick=*rb->current_tick-1; /* Avoid divby0 */
- lasttick=starttick;
- }
-
- /* Calculate fps */
- if (*rb->current_tick-lasttick>=2*HZ) {
- ticks=(*rb->current_tick)-starttick;
-
- fps=(frame*1000)/ticks;
- rb->snprintf(str,sizeof(str),"%d.%d",(fps/10),fps%10);
- rb->lcd_putsxy(0,0,str);
- rb->lcd_update_rect(0,0,80,8);
-
- lasttick+=2*HZ;
- }
- frame++;
}
void vo_setup(unsigned int width, unsigned int height,