summaryrefslogtreecommitdiffstats
path: root/apps/plugins/mpegplayer/mpegplayer.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2010-12-22 11:20:07 +0000
committerMichael Sevakis <jethead71@rockbox.org>2010-12-22 11:20:07 +0000
commit9b4522bacec8cf1eb8ff69684cf959c0bb732c0d (patch)
treea939f4cd549f7def0feed75d9d5728a213bd5c22 /apps/plugins/mpegplayer/mpegplayer.c
parent303aefc406a99cb47f41ed39283a3881f4c7f401 (diff)
downloadrockbox-9b4522bacec8cf1eb8ff69684cf959c0bb732c0d.tar.gz
rockbox-9b4522bacec8cf1eb8ff69684cf959c0bb732c0d.tar.bz2
rockbox-9b4522bacec8cf1eb8ff69684cf959c0bb732c0d.zip
MPEGPlayer: Some UI tweaking and bugfixing
* Allow skip-to-beginning in single-play mode; there is no 3-second delay in that case. * Properly handle and keep track of pauses caused by headphone removal. * Improve skipping over bad files - search in skip direction and allow it to be ended with the stop key. * Add the system message processing done elsewhere to all button queue waits. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28875 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/mpegplayer/mpegplayer.c')
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.c403
1 files changed, 251 insertions, 152 deletions
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c
index 7a2b457aec..d21907f607 100644
--- a/apps/plugins/mpegplayer/mpegplayer.c
+++ b/apps/plugins/mpegplayer/mpegplayer.c
@@ -412,7 +412,9 @@ enum osd_bits
OSD_REFRESH_RESUME = 0x0020, /* Resume playback upon timeout */
OSD_NODRAW = 0x8000, /* OR bitflag - don't draw anything */
OSD_SHOW = 0x4000, /* OR bitflag - show the OSD */
- OSD_HP_PAUSE = 0x2000,
+#ifdef HAVE_HEADPHONE_DETECTION
+ OSD_HP_PAUSE = 0x2000, /* OR bitflag - headphones caused pause */
+#endif
OSD_HIDE = 0x0000, /* hide the OSD (aid readability) */
OSD_REFRESH_ALL = 0x000f, /* Only immediate graphical elements */
};
@@ -835,6 +837,18 @@ static void osd_init(void)
osd_text_init();
}
+#ifdef HAVE_HEADPHONE_DETECTION
+static void osd_set_hp_pause_flag(bool set)
+{
+ if (set)
+ osd.flags |= OSD_HP_PAUSE;
+ else
+ osd.flags &= ~OSD_HP_PAUSE;
+}
+#else
+#define osd_set_hp_pause_flag(set)
+#endif /* HAVE_HEADPHONE_DETECTION */
+
static void osd_schedule_refresh(unsigned refresh)
{
long tick = *rb->current_tick;
@@ -1213,8 +1227,10 @@ static int osd_get_status(void)
return osd.status & OSD_STATUS_MASK;
}
-/* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;) */
-static uint32_t osd_ff_rw(int btn, unsigned refresh)
+/* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;)
+ * Returns last button code
+ */
+static int osd_ff_rw(int btn, unsigned refresh, uint32_t *new_time)
{
unsigned int step = TS_SECOND*rb->global_settings->ff_rewind_min_step;
const long ff_rw_accel = (rb->global_settings->ff_rewind_accel + 3);
@@ -1224,6 +1240,7 @@ static uint32_t osd_ff_rw(int btn, unsigned refresh)
unsigned int max_step = 0;
uint32_t ff_rw_count = 0;
unsigned status = osd.status;
+ int new_btn;
osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME |
OSD_REFRESH_TIME);
@@ -1239,9 +1256,8 @@ static uint32_t osd_ff_rw(int btn, unsigned refresh)
#ifdef MPEG_RC_FF
case MPEG_RC_FF:
#endif
- if (!(btn & BUTTON_REPEAT))
- osd_set_status(OSD_STATUS_FF);
- btn = MPEG_FF | BUTTON_REPEAT; /* simplify code below */
+ osd_set_status(OSD_STATUS_FF);
+ new_btn = btn | BUTTON_REPEAT; /* simplify code below */
break;
case MPEG_RW:
#ifdef MPEG_RW2
@@ -1250,103 +1266,75 @@ static uint32_t osd_ff_rw(int btn, unsigned refresh)
#ifdef MPEG_RC_RW
case MPEG_RC_RW:
#endif
- if (!(btn & BUTTON_REPEAT))
- osd_set_status(OSD_STATUS_RW);
- btn = MPEG_RW | BUTTON_REPEAT; /* simplify code below */
+ osd_set_status(OSD_STATUS_RW);
+ new_btn = btn | BUTTON_REPEAT; /* simplify code below */
break;
default:
- btn = -1;
+ new_btn = BUTTON_NONE; /* Fail tests below but still do proper exit */
}
while (1)
{
stream_keep_disk_active();
- switch (btn)
- {
- case BUTTON_NONE:
- osd_refresh(OSD_REFRESH_DEFAULT);
- break;
-
- case MPEG_FF | BUTTON_REPEAT:
- case MPEG_RW | BUTTON_REPEAT:
-#ifdef MPEG_FF2
- case MPEG_FF2 | BUTTON_REPEAT:
-#endif
-#ifdef MPEG_RW2
- case MPEG_RW2 | BUTTON_REPEAT:
-#endif
-#ifdef MPEG_RC_FF
- case MPEG_RC_FF | BUTTON_REPEAT:
- case MPEG_RC_RW | BUTTON_REPEAT:
-#endif
- break;
+ if (new_btn == (btn | BUTTON_REPEAT)) {
+ if (osd.status == OSD_STATUS_FF) {
+ /* fast forwarding, calc max step relative to end */
+ max_step = muldiv_uint32(duration - (time + ff_rw_count),
+ FF_REWIND_MAX_PERCENT, 100);
+ } else {
+ /* rewinding, calc max step relative to start */
+ max_step = muldiv_uint32(time - ff_rw_count,
+ FF_REWIND_MAX_PERCENT, 100);
+ }
- case MPEG_FF | BUTTON_REL:
- case MPEG_RW | BUTTON_REL:
-#ifdef MPEG_FF2
- case MPEG_FF2 | BUTTON_REL:
-#endif
-#ifdef MPEG_RW2
- case MPEG_RW2 | BUTTON_REL:
-#endif
-#ifdef MPEG_RC_FF
- case MPEG_RC_FF | BUTTON_REL:
- case MPEG_RC_RW | BUTTON_REL:
-#endif
- if (osd.status == OSD_STATUS_FF)
- time += ff_rw_count;
- else if (osd.status == OSD_STATUS_RW)
- time -= ff_rw_count;
+ max_step = MAX(max_step, MIN_FF_REWIND_STEP);
- /* Fall-through */
- case -1:
- default:
- osd_schedule_refresh(refresh);
- osd_set_status(status);
- osd_schedule_refresh(OSD_REFRESH_TIME);
- return time;
- }
+ if (step > max_step)
+ step = max_step;
- if (osd.status == OSD_STATUS_FF) {
- /* fast forwarding, calc max step relative to end */
- max_step = muldiv_uint32(duration - (time + ff_rw_count),
- FF_REWIND_MAX_PERCENT, 100);
- } else {
- /* rewinding, calc max step relative to start */
- max_step = muldiv_uint32(time - ff_rw_count,
- FF_REWIND_MAX_PERCENT, 100);
- }
+ ff_rw_count += step;
- max_step = MAX(max_step, MIN_FF_REWIND_STEP);
+ /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
+ step += step >> ff_rw_accel;
- if (step > max_step)
- step = max_step;
+ if (osd.status == OSD_STATUS_FF) {
+ if (duration - time <= ff_rw_count)
+ ff_rw_count = duration - time;
- ff_rw_count += step;
+ osd.curr_time = time + ff_rw_count;
+ } else {
+ if (time <= ff_rw_count)
+ ff_rw_count = time;
- /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
- step += step >> ff_rw_accel;
+ osd.curr_time = time - ff_rw_count;
+ }
- if (osd.status == OSD_STATUS_FF) {
- if (duration - time <= ff_rw_count)
- ff_rw_count = duration - time;
+ osd_refresh(OSD_REFRESH_TIME);
- osd.curr_time = time + ff_rw_count;
- } else {
- if (time <= ff_rw_count)
- ff_rw_count = time;
-
- osd.curr_time = time - ff_rw_count;
+ new_btn = mpeg_button_get(TIMEOUT_BLOCK);
}
+ else {
+ if (new_btn == (btn | BUTTON_REL)) {
+ if (osd.status == OSD_STATUS_FF)
+ time += ff_rw_count;
+ else if (osd.status == OSD_STATUS_RW)
+ time -= ff_rw_count;
+ }
- osd_refresh(OSD_REFRESH_TIME);
+ *new_time = time;
+
+ osd_schedule_refresh(refresh);
+ osd_set_status(status);
+ osd_schedule_refresh(OSD_REFRESH_TIME);
- btn = rb->button_get_w_tmo(OSD_MIN_UPDATE_INTERVAL);
+ return new_btn;
+ }
}
}
-static int osd_status(void)
+/* Return adjusted STREAM_* status */
+static int osd_stream_status(void)
{
int status = stream_status();
@@ -1394,6 +1382,7 @@ static int osd_play(uint32_t time)
{
int retval;
+ osd_set_hp_pause_flag(false);
osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
retval = stream_seek(time, SEEK_SET);
@@ -1436,6 +1425,8 @@ static int osd_pause(void)
unsigned refresh = osd.auto_refresh;
int status = osd_halt();
+ osd_set_hp_pause_flag(false);
+
if (status == STREAM_PLAYING && (refresh & OSD_REFRESH_RESUME)) {
/* Resume pending - change to a still video frame update */
osd_schedule_refresh(OSD_REFRESH_VIDEO);
@@ -1454,6 +1445,7 @@ static void osd_resume(void)
{
/* Cancel video and resume auto refresh - the resyc when starting
* playback will perform those tasks */
+ osd_set_hp_pause_flag(false);
osd_backlight_on_video_mode(true);
osd_backlight_brightness_video_mode(true);
osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
@@ -1466,6 +1458,7 @@ static void osd_stop(void)
{
uint32_t resume_time;
+ osd_set_hp_pause_flag(false);
osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
osd_set_status(OSD_STATUS_STOPPED | OSD_NODRAW);
osd_show(OSD_HIDE);
@@ -1481,35 +1474,81 @@ static void osd_stop(void)
osd_backlight_brightness_video_mode(false);
}
-/* Perform a seek if seeking is possible for this stream - if playing, a delay
- * will be inserted before restarting in case the user decides to seek again */
-static void osd_seek(int btn)
+/* Perform a seek by button if seeking is possible for this stream.
+ *
+ * A delay will be inserted before restarting in case the user decides to
+ * seek again soon after.
+ *
+ * Returns last button code
+ */
+static int osd_seek_btn(int btn)
{
int status;
- unsigned refresh;
+ unsigned refresh = 0;
uint32_t time;
if (!stream_can_seek())
- return;
+ return true;
- /* Halt playback - not strictly nescessary but nice */
+ /* Halt playback - not strictly necessary but nice when doing
+ * buttons */
status = osd_halt();
if (status == STREAM_STOPPED)
- return;
+ return true;
osd_show(OSD_SHOW);
+ /* Obtain a new playback point according to the buttons */
if (status == STREAM_PLAYING)
refresh = OSD_REFRESH_RESUME; /* delay resume if playing */
else
refresh = OSD_REFRESH_VIDEO; /* refresh if paused */
- /* Obtain a new playback point */
- time = osd_ff_rw(btn, refresh);
+ btn = osd_ff_rw(btn, refresh, &time);
/* Tell engine to resume at that time */
stream_seek(time, SEEK_SET);
+
+ return btn;
+}
+
+/* Perform a seek by time if seeking is possible for this stream
+ *
+ * If playing, the seeking is immediate, otherise a delay is added to showing
+ * a still if paused in case the user does another seek soon after.
+ *
+ * If seeking isn't possible, a time of zero performs a skip to the
+ * beginning.
+ */
+static void osd_seek_time(uint32_t time)
+{
+ int status;
+ unsigned refresh = 0;
+
+ if (!stream_can_seek() && time != 0)
+ return;
+
+ stream_wait_status();
+ status = osd_stream_status();
+
+ if (status == STREAM_STOPPED)
+ return;
+
+ if (status == STREAM_PLAYING) /* merely preserve resume */
+ refresh = osd.auto_refresh & OSD_REFRESH_RESUME;
+ else
+ refresh = OSD_REFRESH_VIDEO; /* refresh if paused */
+
+ /* Cancel print or resume if pending */
+ osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
+
+ /* Tell engine to seek to the given time - no state change */
+ stream_seek(time, SEEK_SET);
+
+ osd_update_time();
+ osd_refresh(OSD_REFRESH_TIME);
+ osd_schedule_refresh(refresh);
}
/* Has this file one of the supported extensions? */
@@ -1537,7 +1576,7 @@ static bool is_videofile(const char* file)
}
/* deliver the next/previous video file in the current directory.
- returns 0 if there is none. */
+ returns false if there is none. */
static bool get_videofile(int direction, char* videofile, size_t bufsize)
{
struct tree_context *tree = rb->tree_get_context();
@@ -1583,11 +1622,12 @@ static void osd_handle_phone_plug(bool inserted)
/* Wait for any incomplete state transition to complete first */
stream_wait_status();
- int status = osd_status();
+ int status = osd_stream_status();
if (inserted) {
if (rb->global_settings->unplug_mode > 1) {
- if (status == STREAM_PAUSED) {
+ if (status == STREAM_PAUSED &&
+ (osd.flags & OSD_HP_PAUSE)) {
osd_resume();
}
}
@@ -1595,6 +1635,8 @@ static void osd_handle_phone_plug(bool inserted)
if (status == STREAM_PLAYING) {
osd_pause();
+ osd_set_hp_pause_flag(true);
+
if (stream_can_seek() && rb->global_settings->unplug_rw) {
stream_seek(-rb->global_settings->unplug_rw*TS_SECOND,
SEEK_CUR);
@@ -1635,12 +1677,7 @@ static int button_loop(void)
/* Gently poll the video player for EOS and handle UI */
while (stream_status() != STREAM_STOPPED)
{
- int button;
-
- mpeg_menu_sysevent_clear();
- button = rb->button_get_w_tmo(OSD_MIN_UPDATE_INTERVAL/2);
-
- button = mpeg_menu_sysevent_callback(button, NULL);
+ int button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL/2);
switch (button)
{
@@ -1761,6 +1798,7 @@ static int button_loop(void)
#endif
case ACTION_STD_CANCEL:
{
+ cancel_playback:
next_action = VIDEO_STOP;
osd_stop();
break;
@@ -1774,7 +1812,7 @@ static int button_loop(void)
case MPEG_RC_PAUSE:
#endif
{
- int status = osd_status();
+ int status = osd_stream_status();
if (status == STREAM_PLAYING) {
/* Playing => Paused */
@@ -1789,54 +1827,72 @@ static int button_loop(void)
} /* MPEG_PAUSE*: */
case MPEG_RW:
- case MPEG_FF:
#ifdef MPEG_RW2
case MPEG_RW2:
#endif
+#ifdef MPEG_RC_RW
+ case MPEG_RC_RW:
+#endif
+ {
+ int old_button = button;
+
+ /* If button has been released: skip to next/previous file */
+ button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL);
+
+ if ((old_button | BUTTON_REL) == button) {
+ /* Check current playback position */
+ osd_update_time();
+
+ if (settings.play_mode == 0 || osd.curr_time >= 3*TS_SECOND) {
+ /* Start the current video from the beginning */
+ osd_seek_time(0*TS_SECOND);
+ }
+ else {
+ /* Release within 3 seconds of start: skip to previous
+ * file */
+ osd_stop();
+ next_action = VIDEO_PREV;
+ }
+ }
+ else if ((button & ~BUTTON_REPEAT) == old_button) {
+ button = osd_seek_btn(old_button);
+ }
+
+ if (button == ACTION_STD_CANCEL)
+ goto cancel_playback; /* jump to stop handling above */
+
+ rb->default_event_handler(button);
+ break;
+ } /* MPEG_RW: */
+
+ case MPEG_FF:
#ifdef MPEG_FF2
case MPEG_FF2:
#endif
-#ifdef MPEG_RC_RW
- case MPEG_RC_RW:
+#ifdef MPEG_RC_FF
case MPEG_RC_FF:
#endif
{
int old_button = button;
+
if (settings.play_mode != 0)
- {
- /* if button has been released: skip to next/previous file */
- button = rb->button_get_w_tmo(OSD_MIN_UPDATE_INTERVAL);
- }
- switch (button)
- {
- case MPEG_RW | BUTTON_REL:
- {
- /* release within 3 seconds: skip to previous file, else
- start the current video from the beginning */
- osd_stop();
- if ( stream_get_resume_time() > 3*TS_SECOND ) {
- osd_play(0);
- osd_show(OSD_SHOW);
- } else {
- next_action = VIDEO_PREV;
- }
- break;
- }
- case MPEG_FF | BUTTON_REL:
- {
+ button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL);
+
+ if ((old_button | BUTTON_REL) == button) {
+ /* If button has been released: skip to next file */
osd_stop();
next_action = VIDEO_NEXT;
- break;
- }
- default:
- {
- button = old_button;
- osd_seek(button);
- break;
- }
}
+ else if ((button & ~BUTTON_REPEAT) == old_button) {
+ button = osd_seek_btn(old_button);
+ }
+
+ if (button == ACTION_STD_CANCEL)
+ goto cancel_playback; /* jump to stop handling above */
+
+ rb->default_event_handler(button);
break;
- } /* MPEG_RW: MPEG_FF: */
+ } /* MPEG_FF: */
#ifdef HAVE_HEADPHONE_DETECTION
case SYS_PHONE_PLUGGED:
@@ -1849,6 +1905,7 @@ static int button_loop(void)
default:
{
+ osd_refresh(OSD_REFRESH_DEFAULT);
rb->default_event_handler(button);
break;
} /* default: */
@@ -1874,10 +1931,7 @@ enum plugin_status plugin_start(const void* parameter)
{
static char videofile[MAX_PATH];
int status = PLUGIN_OK; /* assume success */
- int result;
- int err;
bool quit = false;
- const char *errstring;
if (parameter == NULL) {
/* No file = GTFO */
@@ -1904,19 +1958,24 @@ enum plugin_status plugin_start(const void* parameter)
DEBUGF("Could not initialize streams\n");
status = PLUGIN_ERROR;
} else {
+ int next_action = VIDEO_STOP;
+ bool get_videofile_says = true;
+
while (!quit)
{
- int next_action = VIDEO_STOP;
+ int result;
init_settings(videofile);
- err = stream_open(videofile);
- if (err >= STREAM_OK) {
+ result = stream_open(videofile);
+
+ if (result >= STREAM_OK) {
/* start menu */
rb->lcd_clear_display();
rb->lcd_update();
result = mpeg_start_menu(stream_get_duration());
+ next_action = VIDEO_STOP;
if (result != MPEG_START_QUIT) {
/* Enter button loop and process UI */
next_action = button_loop();
@@ -1928,13 +1987,14 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_update();
save_settings();
-
- mpeg_menu_sysevent_handle();
} else {
/* Problem with file; display message about it - not
* considered a plugin error */
+ long tick;
+ const char *errstring;
+
DEBUGF("Could not open %s\n", videofile);
- switch (err)
+ switch (result)
{
case STREAM_UNSUPPORTED:
errstring = "Unsupported format";
@@ -1943,12 +2003,45 @@ enum plugin_status plugin_start(const void* parameter)
errstring = "Error opening file: %d";
}
- rb->splashf(HZ*2, errstring, err);
+ tick = *rb->current_tick + HZ*2;
- if (settings.play_mode != 0) {
- /* Try the next file if the play mode is not single play */
- next_action = VIDEO_NEXT;
- }
+ rb->splashf(0, errstring, result);
+
+ /* Be sure it doesn't get stuck in an unbreakable loop of bad
+ * files, just in case! Otherwise, keep searching in the
+ * chosen direction until a good one is found. */
+ while (!quit && TIME_BEFORE(*rb->current_tick, tick))
+ {
+ int button = mpeg_button_get(HZ*2);
+
+ switch (button)
+ {
+ case MPEG_STOP:
+ case ACTION_STD_CANCEL:
+ /* Abort the search and exit */
+ next_action = VIDEO_STOP;
+ quit = true;
+ break;
+
+ case BUTTON_NONE:
+ if (settings.play_mode != 0) {
+ if (next_action == VIDEO_STOP) {
+ /* Default to next file */
+ next_action = VIDEO_NEXT;
+ }
+ else if (next_action == VIDEO_PREV &&
+ !get_videofile_says) {
+ /* Was first file already; avoid endlessly
+ * retrying it */
+ next_action = VIDEO_STOP;
+ }
+ }
+ break;
+
+ default:
+ rb->default_event_handler(button);
+ } /* switch */
+ } /* while */
}
/* return value of button_loop says, what's next */
@@ -1956,15 +2049,16 @@ enum plugin_status plugin_start(const void* parameter)
{
case VIDEO_NEXT:
{
- if (!get_videofile(VIDEO_NEXT, videofile, sizeof(videofile))) {
- /* quit after finished the last videofile */
- quit = true;
- }
+ get_videofile_says = get_videofile(VIDEO_NEXT, videofile,
+ sizeof(videofile));
+ /* quit after finished the last videofile */
+ quit = !get_videofile_says;
break;
}
case VIDEO_PREV:
{
- get_videofile(VIDEO_PREV, videofile, sizeof(videofile));
+ get_videofile_says = get_videofile(VIDEO_PREV, videofile,
+ sizeof(videofile));
/* if there is no previous file, play the same videofile */
break;
}
@@ -1974,7 +2068,7 @@ enum plugin_status plugin_start(const void* parameter)
break;
}
}
- }
+ } /* while */
}
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
@@ -1984,5 +2078,10 @@ enum plugin_status plugin_start(const void* parameter)
stream_exit();
rb->talk_disable(false);
+
+ /* Actually handle delayed processing of system events of interest
+ * that were captured in other button loops */
+ mpeg_sysevent_handle();
+
return status;
}