1
0
Fork 0
forked from len0rd/rockbox

FS#8607: MPEG video playlist

Add mode to play multiple mpeg files in the same directory in the order the file browser shows.
In this mode, Mpegplayer exits after finishing the last .mpg file.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28667 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Teruaki Kawashima 2010-11-26 12:32:00 +00:00
parent b397fe5ae3
commit 043ebca136
5 changed files with 183 additions and 34 deletions

View file

@ -1213,6 +1213,7 @@ int parser_init_stream(void)
void parser_close_stream(void) void parser_close_stream(void)
{ {
str_send_msg(&video_str, STREAM_CLOSE, 0);
stream_remove_streams(); stream_remove_streams();
parser_init_state(); parser_init_state();
} }

View file

@ -275,6 +275,7 @@ static struct configdata config[] =
{TYPE_INT, 0, 2, { .int_p = &settings.crossfeed }, "Crossfeed", NULL}, {TYPE_INT, 0, 2, { .int_p = &settings.crossfeed }, "Crossfeed", NULL},
{TYPE_INT, 0, 2, { .int_p = &settings.equalizer }, "Equalizer", NULL}, {TYPE_INT, 0, 2, { .int_p = &settings.equalizer }, "Equalizer", NULL},
{TYPE_INT, 0, 2, { .int_p = &settings.dithering }, "Dithering", NULL}, {TYPE_INT, 0, 2, { .int_p = &settings.dithering }, "Dithering", NULL},
{TYPE_INT, 0, 2, { .int_p = &settings.play_mode }, "Play mode", NULL},
#ifdef HAVE_BACKLIGHT_BRIGHTNESS #ifdef HAVE_BACKLIGHT_BRIGHTNESS
{TYPE_INT, -1, INT_MAX, { .int_p = &settings.backlight_brightness }, {TYPE_INT, -1, INT_MAX, { .int_p = &settings.backlight_brightness },
"Backlight brightness", NULL}, "Backlight brightness", NULL},
@ -286,6 +287,11 @@ static const struct opt_items noyes[2] = {
{ "Yes", -1 }, { "Yes", -1 },
}; };
static const struct opt_items singleall[2] = {
{ "Single", -1 },
{ "All", -1 },
};
static const struct opt_items enabledisable[2] = { static const struct opt_items enabledisable[2] = {
{ "Disable", -1 }, { "Disable", -1 },
{ "Enable", -1 }, { "Enable", -1 },
@ -1191,7 +1197,7 @@ static void mpeg_settings(void)
MENUITEM_STRINGLIST(menu, "Settings", mpeg_menu_sysevent_callback, MENUITEM_STRINGLIST(menu, "Settings", mpeg_menu_sysevent_callback,
"Display Options", "Audio Options", "Display Options", "Audio Options",
"Resume Options", clear_str); "Resume Options", "Play Mode", clear_str);
rb->button_clear_queue(); rb->button_clear_queue();
@ -1219,6 +1225,11 @@ static void mpeg_settings(void)
resume_options(); resume_options();
break; break;
case MPEG_SETTING_PLAY_MODE:
mpeg_set_option("Play mode", &settings.play_mode,
INT, singleall, 2, NULL);
break;
case MPEG_SETTING_CLEAR_RESUMES: case MPEG_SETTING_CLEAR_RESUMES:
clear_resume_count(); clear_resume_count();
break; break;
@ -1239,6 +1250,7 @@ void init_settings(const char* filename)
settings.showfps = 0; /* Do not show FPS */ settings.showfps = 0; /* Do not show FPS */
settings.limitfps = 1; /* Limit FPS */ settings.limitfps = 1; /* Limit FPS */
settings.skipframes = 1; /* Skip frames */ settings.skipframes = 1; /* Skip frames */
settings.play_mode = 0; /* Play single video */
settings.resume_options = MPEG_RESUME_MENU_ALWAYS; /* Enable start menu */ settings.resume_options = MPEG_RESUME_MENU_ALWAYS; /* Enable start menu */
settings.resume_count = 0; settings.resume_count = 0;
#ifdef HAVE_BACKLIGHT_BRIGHTNESS #ifdef HAVE_BACKLIGHT_BRIGHTNESS

View file

@ -62,6 +62,7 @@ enum mpeg_setting_id
MPEG_SETTING_DISPLAY_SETTINGS, MPEG_SETTING_DISPLAY_SETTINGS,
MPEG_SETTING_AUDIO_SETTINGS, MPEG_SETTING_AUDIO_SETTINGS,
MPEG_SETTING_ENABLE_START_MENU, MPEG_SETTING_ENABLE_START_MENU,
MPEG_SETTING_PLAY_MODE,
MPEG_SETTING_CLEAR_RESUMES, MPEG_SETTING_CLEAR_RESUMES,
}; };
@ -83,6 +84,7 @@ struct mpeg_settings {
#if MPEG_OPTION_DITHERING_ENABLED #if MPEG_OPTION_DITHERING_ENABLED
int displayoptions; int displayoptions;
#endif #endif
int play_mode; /* play single file or all files in directory */
/* Audio options - simple on/off specification */ /* Audio options - simple on/off specification */
int tone_controls; int tone_controls;
int channel_modes; int channel_modes;

View file

@ -372,6 +372,13 @@ CONFIG_KEYPAD == SANSA_M200_PAD
#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)
enum video_action
{
VIDEO_STOP = 0,
VIDEO_PREV,
VIDEO_NEXT,
};
/* OSD status - same order as icon array */ /* OSD status - same order as icon array */
enum osd_status_enum enum osd_status_enum
{ {
@ -1452,7 +1459,7 @@ static void osd_stop(void)
osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME); osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
osd_set_status(OSD_STATUS_STOPPED | OSD_NODRAW); osd_set_status(OSD_STATUS_STOPPED | OSD_NODRAW);
osd_show(OSD_HIDE | OSD_NODRAW); osd_show(OSD_HIDE);
stream_stop(); stream_stop();
@ -1496,6 +1503,53 @@ static void osd_seek(int btn)
stream_seek(time, SEEK_SET); stream_seek(time, SEEK_SET);
} }
/* has this file the extension .mpg ? */
static bool is_videofile(const char* file)
{
const char* ext = rb->strrchr(file, '.');
if (ext && !rb->strcasecmp(ext, ".mpg"))
return true;
return false;
}
/* deliver the next/previous video file in the current directory.
returns 0 if there is none. */
static bool get_videofile(int direction, char* videofile, size_t bufsize)
{
struct tree_context *tree = rb->tree_get_context();
struct entry *dircache = tree->dircache;
int i, step, end, found = 0;
char *videoname = rb->strrchr(videofile, '/') + 1;
size_t rest = bufsize - (videoname - videofile) - 1;
if (direction == VIDEO_NEXT) {
i = 0;
step = 1;
end = tree->filesindir;
} else {
i = tree->filesindir-1;
step = -1;
end = -1;
}
for (; i != end; i += step)
{
const char* name = dircache[i].name;
if (!rb->strcmp(name, videoname)) {
found = 1;
continue;
}
if (found && rb->strlen(name) <= rest &&
!(dircache[i].attr & ATTR_DIRECTORY) && is_videofile(name))
{
rb->strcpy(videoname, name);
return true;
}
}
return false;
}
#ifdef HAVE_HEADPHONE_DETECTION #ifdef HAVE_HEADPHONE_DETECTION
/* Handle SYS_PHONE_PLUGGED/UNPLUGGED */ /* Handle SYS_PHONE_PLUGGED/UNPLUGGED */
static void osd_handle_phone_plug(bool inserted) static void osd_handle_phone_plug(bool inserted)
@ -1531,8 +1585,10 @@ static void osd_handle_phone_plug(bool inserted)
} }
#endif #endif
static void button_loop(void) static int button_loop(void)
{ {
int next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT;
rb->lcd_setfont(FONT_SYSFIXED); rb->lcd_setfont(FONT_SYSFIXED);
#ifdef HAVE_LCD_COLOR #ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(LCD_WHITE); rb->lcd_set_foreground(LCD_WHITE);
@ -1550,7 +1606,7 @@ static void button_loop(void)
/* Start playback at the specified starting time */ /* Start playback at the specified starting time */
if (osd_play(settings.resume_time) < STREAM_OK) { if (osd_play(settings.resume_time) < STREAM_OK) {
rb->splash(HZ*2, "Playback failed"); rb->splash(HZ*2, "Playback failed");
return; return VIDEO_STOP;
} }
/* Gently poll the video player for EOS and handle UI */ /* Gently poll the video player for EOS and handle UI */
@ -1629,6 +1685,8 @@ static void button_loop(void)
result = mpeg_menu(); result = mpeg_menu();
next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT;
/* 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
@ -1641,6 +1699,7 @@ static void button_loop(void)
switch (result) switch (result)
{ {
case MPEG_MENU_QUIT: case MPEG_MENU_QUIT:
next_action = VIDEO_STOP;
osd_stop(); osd_stop();
break; break;
@ -1679,6 +1738,7 @@ static void button_loop(void)
#endif #endif
case ACTION_STD_CANCEL: case ACTION_STD_CANCEL:
{ {
next_action = VIDEO_STOP;
osd_stop(); osd_stop();
break; break;
} /* MPEG_STOP: */ } /* MPEG_STOP: */
@ -1718,8 +1778,41 @@ static void button_loop(void)
case MPEG_RC_FF: case MPEG_RC_FF:
#endif #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:
{
osd_stop();
next_action = VIDEO_NEXT;
break;
}
default:
{
button = old_button;
osd_seek(button); osd_seek(button);
break; break;
}
}
break;
} /* MPEG_RW: MPEG_FF: */ } /* MPEG_RW: MPEG_FF: */
#ifdef HAVE_HEADPHONE_DETECTION #ifdef HAVE_HEADPHONE_DETECTION
@ -1750,13 +1843,18 @@ static void button_loop(void)
#endif #endif
rb->lcd_setfont(FONT_UI); rb->lcd_setfont(FONT_UI);
return next_action;
} }
enum plugin_status plugin_start(const void* parameter) enum plugin_status plugin_start(const void* parameter)
{ {
static char videofile[MAX_PATH];
int status = PLUGIN_ERROR; /* assume failure */ int status = PLUGIN_ERROR; /* assume failure */
int result; int result;
int err; int err;
bool quit = false;
const char *errstring; const char *errstring;
if (parameter == NULL) { if (parameter == NULL) {
@ -1777,13 +1875,17 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_clear_display(); rb->lcd_clear_display();
rb->lcd_update(); rb->lcd_update();
rb->strcpy(videofile, (const char*) parameter);
if (stream_init() < STREAM_OK) { if (stream_init() < STREAM_OK) {
DEBUGF("Could not initialize streams\n"); DEBUGF("Could not initialize streams\n");
} else { } else {
rb->splash(0, "Loading..."); while (!quit)
init_settings((char*)parameter); {
int next_action = VIDEO_STOP;
err = stream_open((char *)parameter); init_settings(videofile);
err = stream_open(videofile);
if (err >= STREAM_OK) { if (err >= STREAM_OK) {
/* start menu */ /* start menu */
@ -1793,7 +1895,7 @@ enum plugin_status plugin_start(const void* parameter)
if (result != MPEG_START_QUIT) { if (result != MPEG_START_QUIT) {
/* Enter button loop and process UI */ /* Enter button loop and process UI */
button_loop(); next_action = button_loop();
} }
stream_close(); stream_close();
@ -1806,7 +1908,7 @@ enum plugin_status plugin_start(const void* parameter)
mpeg_menu_sysevent_handle(); mpeg_menu_sysevent_handle();
} else { } else {
DEBUGF("Could not open %s\n", (char*)parameter); DEBUGF("Could not open %s\n", videofile);
switch (err) switch (err)
{ {
case STREAM_UNSUPPORTED: case STREAM_UNSUPPORTED:
@ -1817,6 +1919,32 @@ enum plugin_status plugin_start(const void* parameter)
} }
rb->splashf(HZ*2, errstring, err); rb->splashf(HZ*2, errstring, err);
status = PLUGIN_ERROR;
}
/* return value of button_loop says, what's next */
switch (next_action)
{
case VIDEO_NEXT:
{
if (!get_videofile(VIDEO_NEXT, videofile, sizeof(videofile))) {
/* quit after finished the last videofile */
quit = true;
}
break;
}
case VIDEO_PREV:
{
get_videofile(VIDEO_PREV, videofile, sizeof(videofile));
/* if there is no previous file, play the same videofile */
break;
}
case VIDEO_STOP:
{
quit = true;
break;
}
}
} }
} }

View file

@ -503,6 +503,12 @@ static void video_thread_msg(struct video_thread_data *td)
reply = true; reply = true;
break; break;
case STREAM_CLOSE:
vo_cleanup();
mpeg2_close(td->mpeg2dec);
reply = true;
break;
case VIDEO_DISPLAY_IS_VISIBLE: case VIDEO_DISPLAY_IS_VISIBLE:
reply = vo_is_visible(); reply = vo_is_visible();
break; break;