diff options
author | Teruaki Kawashima <teru@rockbox.org> | 2010-11-26 12:32:00 +0000 |
---|---|---|
committer | Teruaki Kawashima <teru@rockbox.org> | 2010-11-26 12:32:00 +0000 |
commit | 043ebca136cfb38fc9ff1610540100729b4c96db (patch) | |
tree | 3f12b7aced03d439223ee14a65e1cf7e45c44b1b /apps/plugins/mpegplayer | |
parent | b397fe5ae373e3b62fee9150ab786c344c2084cd (diff) | |
download | rockbox-043ebca136cfb38fc9ff1610540100729b4c96db.tar.gz rockbox-043ebca136cfb38fc9ff1610540100729b4c96db.zip |
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
Diffstat (limited to 'apps/plugins/mpegplayer')
-rw-r--r-- | apps/plugins/mpegplayer/mpeg_parser.c | 1 | ||||
-rw-r--r-- | apps/plugins/mpegplayer/mpeg_settings.c | 14 | ||||
-rw-r--r-- | apps/plugins/mpegplayer/mpeg_settings.h | 2 | ||||
-rw-r--r-- | apps/plugins/mpegplayer/mpegplayer.c | 190 | ||||
-rw-r--r-- | apps/plugins/mpegplayer/video_thread.c | 6 |
5 files changed, 181 insertions, 32 deletions
diff --git a/apps/plugins/mpegplayer/mpeg_parser.c b/apps/plugins/mpegplayer/mpeg_parser.c index 6883916149..5821bd57ab 100644 --- a/apps/plugins/mpegplayer/mpeg_parser.c +++ b/apps/plugins/mpegplayer/mpeg_parser.c | |||
@@ -1213,6 +1213,7 @@ int parser_init_stream(void) | |||
1213 | 1213 | ||
1214 | void parser_close_stream(void) | 1214 | void parser_close_stream(void) |
1215 | { | 1215 | { |
1216 | str_send_msg(&video_str, STREAM_CLOSE, 0); | ||
1216 | stream_remove_streams(); | 1217 | stream_remove_streams(); |
1217 | parser_init_state(); | 1218 | parser_init_state(); |
1218 | } | 1219 | } |
diff --git a/apps/plugins/mpegplayer/mpeg_settings.c b/apps/plugins/mpegplayer/mpeg_settings.c index 2a5c4be617..94a375d866 100644 --- a/apps/plugins/mpegplayer/mpeg_settings.c +++ b/apps/plugins/mpegplayer/mpeg_settings.c | |||
@@ -275,6 +275,7 @@ static struct configdata config[] = | |||
275 | {TYPE_INT, 0, 2, { .int_p = &settings.crossfeed }, "Crossfeed", NULL}, | 275 | {TYPE_INT, 0, 2, { .int_p = &settings.crossfeed }, "Crossfeed", NULL}, |
276 | {TYPE_INT, 0, 2, { .int_p = &settings.equalizer }, "Equalizer", NULL}, | 276 | {TYPE_INT, 0, 2, { .int_p = &settings.equalizer }, "Equalizer", NULL}, |
277 | {TYPE_INT, 0, 2, { .int_p = &settings.dithering }, "Dithering", NULL}, | 277 | {TYPE_INT, 0, 2, { .int_p = &settings.dithering }, "Dithering", NULL}, |
278 | {TYPE_INT, 0, 2, { .int_p = &settings.play_mode }, "Play mode", NULL}, | ||
278 | #ifdef HAVE_BACKLIGHT_BRIGHTNESS | 279 | #ifdef HAVE_BACKLIGHT_BRIGHTNESS |
279 | {TYPE_INT, -1, INT_MAX, { .int_p = &settings.backlight_brightness }, | 280 | {TYPE_INT, -1, INT_MAX, { .int_p = &settings.backlight_brightness }, |
280 | "Backlight brightness", NULL}, | 281 | "Backlight brightness", NULL}, |
@@ -286,6 +287,11 @@ static const struct opt_items noyes[2] = { | |||
286 | { "Yes", -1 }, | 287 | { "Yes", -1 }, |
287 | }; | 288 | }; |
288 | 289 | ||
290 | static const struct opt_items singleall[2] = { | ||
291 | { "Single", -1 }, | ||
292 | { "All", -1 }, | ||
293 | }; | ||
294 | |||
289 | static const struct opt_items enabledisable[2] = { | 295 | static const struct opt_items enabledisable[2] = { |
290 | { "Disable", -1 }, | 296 | { "Disable", -1 }, |
291 | { "Enable", -1 }, | 297 | { "Enable", -1 }, |
@@ -1191,7 +1197,7 @@ static void mpeg_settings(void) | |||
1191 | 1197 | ||
1192 | MENUITEM_STRINGLIST(menu, "Settings", mpeg_menu_sysevent_callback, | 1198 | MENUITEM_STRINGLIST(menu, "Settings", mpeg_menu_sysevent_callback, |
1193 | "Display Options", "Audio Options", | 1199 | "Display Options", "Audio Options", |
1194 | "Resume Options", clear_str); | 1200 | "Resume Options", "Play Mode", clear_str); |
1195 | 1201 | ||
1196 | rb->button_clear_queue(); | 1202 | rb->button_clear_queue(); |
1197 | 1203 | ||
@@ -1219,6 +1225,11 @@ static void mpeg_settings(void) | |||
1219 | resume_options(); | 1225 | resume_options(); |
1220 | break; | 1226 | break; |
1221 | 1227 | ||
1228 | case MPEG_SETTING_PLAY_MODE: | ||
1229 | mpeg_set_option("Play mode", &settings.play_mode, | ||
1230 | INT, singleall, 2, NULL); | ||
1231 | break; | ||
1232 | |||
1222 | case MPEG_SETTING_CLEAR_RESUMES: | 1233 | case MPEG_SETTING_CLEAR_RESUMES: |
1223 | clear_resume_count(); | 1234 | clear_resume_count(); |
1224 | break; | 1235 | break; |
@@ -1239,6 +1250,7 @@ void init_settings(const char* filename) | |||
1239 | settings.showfps = 0; /* Do not show FPS */ | 1250 | settings.showfps = 0; /* Do not show FPS */ |
1240 | settings.limitfps = 1; /* Limit FPS */ | 1251 | settings.limitfps = 1; /* Limit FPS */ |
1241 | settings.skipframes = 1; /* Skip frames */ | 1252 | settings.skipframes = 1; /* Skip frames */ |
1253 | settings.play_mode = 0; /* Play single video */ | ||
1242 | settings.resume_options = MPEG_RESUME_MENU_ALWAYS; /* Enable start menu */ | 1254 | settings.resume_options = MPEG_RESUME_MENU_ALWAYS; /* Enable start menu */ |
1243 | settings.resume_count = 0; | 1255 | settings.resume_count = 0; |
1244 | #ifdef HAVE_BACKLIGHT_BRIGHTNESS | 1256 | #ifdef HAVE_BACKLIGHT_BRIGHTNESS |
diff --git a/apps/plugins/mpegplayer/mpeg_settings.h b/apps/plugins/mpegplayer/mpeg_settings.h index 6c8b69a4ce..0910116615 100644 --- a/apps/plugins/mpegplayer/mpeg_settings.h +++ b/apps/plugins/mpegplayer/mpeg_settings.h | |||
@@ -62,6 +62,7 @@ enum mpeg_setting_id | |||
62 | MPEG_SETTING_DISPLAY_SETTINGS, | 62 | MPEG_SETTING_DISPLAY_SETTINGS, |
63 | MPEG_SETTING_AUDIO_SETTINGS, | 63 | MPEG_SETTING_AUDIO_SETTINGS, |
64 | MPEG_SETTING_ENABLE_START_MENU, | 64 | MPEG_SETTING_ENABLE_START_MENU, |
65 | MPEG_SETTING_PLAY_MODE, | ||
65 | MPEG_SETTING_CLEAR_RESUMES, | 66 | MPEG_SETTING_CLEAR_RESUMES, |
66 | }; | 67 | }; |
67 | 68 | ||
@@ -83,6 +84,7 @@ struct mpeg_settings { | |||
83 | #if MPEG_OPTION_DITHERING_ENABLED | 84 | #if MPEG_OPTION_DITHERING_ENABLED |
84 | int displayoptions; | 85 | int displayoptions; |
85 | #endif | 86 | #endif |
87 | int play_mode; /* play single file or all files in directory */ | ||
86 | /* Audio options - simple on/off specification */ | 88 | /* Audio options - simple on/off specification */ |
87 | int tone_controls; | 89 | int tone_controls; |
88 | int channel_modes; | 90 | int channel_modes; |
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c index 513c2f0863..2314d96889 100644 --- a/apps/plugins/mpegplayer/mpegplayer.c +++ b/apps/plugins/mpegplayer/mpegplayer.c | |||
@@ -372,6 +372,13 @@ CONFIG_KEYPAD == SANSA_M200_PAD | |||
372 | #define MIN_FF_REWIND_STEP (TS_SECOND/2) | 372 | #define MIN_FF_REWIND_STEP (TS_SECOND/2) |
373 | #define OSD_MIN_UPDATE_INTERVAL (HZ/2) | 373 | #define OSD_MIN_UPDATE_INTERVAL (HZ/2) |
374 | 374 | ||
375 | enum video_action | ||
376 | { | ||
377 | VIDEO_STOP = 0, | ||
378 | VIDEO_PREV, | ||
379 | VIDEO_NEXT, | ||
380 | }; | ||
381 | |||
375 | /* OSD status - same order as icon array */ | 382 | /* OSD status - same order as icon array */ |
376 | enum osd_status_enum | 383 | enum osd_status_enum |
377 | { | 384 | { |
@@ -1452,7 +1459,7 @@ static void osd_stop(void) | |||
1452 | 1459 | ||
1453 | osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME); | 1460 | osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME); |
1454 | osd_set_status(OSD_STATUS_STOPPED | OSD_NODRAW); | 1461 | osd_set_status(OSD_STATUS_STOPPED | OSD_NODRAW); |
1455 | osd_show(OSD_HIDE | OSD_NODRAW); | 1462 | osd_show(OSD_HIDE); |
1456 | 1463 | ||
1457 | stream_stop(); | 1464 | stream_stop(); |
1458 | 1465 | ||
@@ -1496,6 +1503,53 @@ static void osd_seek(int btn) | |||
1496 | stream_seek(time, SEEK_SET); | 1503 | stream_seek(time, SEEK_SET); |
1497 | } | 1504 | } |
1498 | 1505 | ||
1506 | /* has this file the extension .mpg ? */ | ||
1507 | static bool is_videofile(const char* file) | ||
1508 | { | ||
1509 | const char* ext = rb->strrchr(file, '.'); | ||
1510 | if (ext && !rb->strcasecmp(ext, ".mpg")) | ||
1511 | return true; | ||
1512 | |||
1513 | return false; | ||
1514 | } | ||
1515 | |||
1516 | /* deliver the next/previous video file in the current directory. | ||
1517 | returns 0 if there is none. */ | ||
1518 | static bool get_videofile(int direction, char* videofile, size_t bufsize) | ||
1519 | { | ||
1520 | struct tree_context *tree = rb->tree_get_context(); | ||
1521 | struct entry *dircache = tree->dircache; | ||
1522 | int i, step, end, found = 0; | ||
1523 | char *videoname = rb->strrchr(videofile, '/') + 1; | ||
1524 | size_t rest = bufsize - (videoname - videofile) - 1; | ||
1525 | |||
1526 | if (direction == VIDEO_NEXT) { | ||
1527 | i = 0; | ||
1528 | step = 1; | ||
1529 | end = tree->filesindir; | ||
1530 | } else { | ||
1531 | i = tree->filesindir-1; | ||
1532 | step = -1; | ||
1533 | end = -1; | ||
1534 | } | ||
1535 | for (; i != end; i += step) | ||
1536 | { | ||
1537 | const char* name = dircache[i].name; | ||
1538 | if (!rb->strcmp(name, videoname)) { | ||
1539 | found = 1; | ||
1540 | continue; | ||
1541 | } | ||
1542 | if (found && rb->strlen(name) <= rest && | ||
1543 | !(dircache[i].attr & ATTR_DIRECTORY) && is_videofile(name)) | ||
1544 | { | ||
1545 | rb->strcpy(videoname, name); | ||
1546 | return true; | ||
1547 | } | ||
1548 | } | ||
1549 | |||
1550 | return false; | ||
1551 | } | ||
1552 | |||
1499 | #ifdef HAVE_HEADPHONE_DETECTION | 1553 | #ifdef HAVE_HEADPHONE_DETECTION |
1500 | /* Handle SYS_PHONE_PLUGGED/UNPLUGGED */ | 1554 | /* Handle SYS_PHONE_PLUGGED/UNPLUGGED */ |
1501 | static void osd_handle_phone_plug(bool inserted) | 1555 | static void osd_handle_phone_plug(bool inserted) |
@@ -1531,8 +1585,10 @@ static void osd_handle_phone_plug(bool inserted) | |||
1531 | } | 1585 | } |
1532 | #endif | 1586 | #endif |
1533 | 1587 | ||
1534 | static void button_loop(void) | 1588 | static int button_loop(void) |
1535 | { | 1589 | { |
1590 | int next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT; | ||
1591 | |||
1536 | rb->lcd_setfont(FONT_SYSFIXED); | 1592 | rb->lcd_setfont(FONT_SYSFIXED); |
1537 | #ifdef HAVE_LCD_COLOR | 1593 | #ifdef HAVE_LCD_COLOR |
1538 | rb->lcd_set_foreground(LCD_WHITE); | 1594 | rb->lcd_set_foreground(LCD_WHITE); |
@@ -1550,7 +1606,7 @@ static void button_loop(void) | |||
1550 | /* Start playback at the specified starting time */ | 1606 | /* Start playback at the specified starting time */ |
1551 | if (osd_play(settings.resume_time) < STREAM_OK) { | 1607 | if (osd_play(settings.resume_time) < STREAM_OK) { |
1552 | rb->splash(HZ*2, "Playback failed"); | 1608 | rb->splash(HZ*2, "Playback failed"); |
1553 | return; | 1609 | return VIDEO_STOP; |
1554 | } | 1610 | } |
1555 | 1611 | ||
1556 | /* Gently poll the video player for EOS and handle UI */ | 1612 | /* Gently poll the video player for EOS and handle UI */ |
@@ -1629,6 +1685,8 @@ static void button_loop(void) | |||
1629 | 1685 | ||
1630 | result = mpeg_menu(); | 1686 | result = mpeg_menu(); |
1631 | 1687 | ||
1688 | next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT; | ||
1689 | |||
1632 | /* The menu can change the font, so restore */ | 1690 | /* The menu can change the font, so restore */ |
1633 | rb->lcd_setfont(FONT_SYSFIXED); | 1691 | rb->lcd_setfont(FONT_SYSFIXED); |
1634 | #ifdef HAVE_LCD_COLOR | 1692 | #ifdef HAVE_LCD_COLOR |
@@ -1641,6 +1699,7 @@ static void button_loop(void) | |||
1641 | switch (result) | 1699 | switch (result) |
1642 | { | 1700 | { |
1643 | case MPEG_MENU_QUIT: | 1701 | case MPEG_MENU_QUIT: |
1702 | next_action = VIDEO_STOP; | ||
1644 | osd_stop(); | 1703 | osd_stop(); |
1645 | break; | 1704 | break; |
1646 | 1705 | ||
@@ -1679,6 +1738,7 @@ static void button_loop(void) | |||
1679 | #endif | 1738 | #endif |
1680 | case ACTION_STD_CANCEL: | 1739 | case ACTION_STD_CANCEL: |
1681 | { | 1740 | { |
1741 | next_action = VIDEO_STOP; | ||
1682 | osd_stop(); | 1742 | osd_stop(); |
1683 | break; | 1743 | break; |
1684 | } /* MPEG_STOP: */ | 1744 | } /* MPEG_STOP: */ |
@@ -1718,7 +1778,40 @@ static void button_loop(void) | |||
1718 | case MPEG_RC_FF: | 1778 | case MPEG_RC_FF: |
1719 | #endif | 1779 | #endif |
1720 | { | 1780 | { |
1721 | osd_seek(button); | 1781 | int old_button = button; |
1782 | if (settings.play_mode != 0) | ||
1783 | { | ||
1784 | /* if button has been released: skip to next/previous file */ | ||
1785 | button = rb->button_get_w_tmo(OSD_MIN_UPDATE_INTERVAL); | ||
1786 | } | ||
1787 | switch (button) | ||
1788 | { | ||
1789 | case MPEG_RW | BUTTON_REL: | ||
1790 | { | ||
1791 | /* release within 3 seconds: skip to previous file, else | ||
1792 | start the current video from the beginning */ | ||
1793 | osd_stop(); | ||
1794 | if ( stream_get_resume_time() > 3*TS_SECOND ) { | ||
1795 | osd_play(0); | ||
1796 | osd_show(OSD_SHOW); | ||
1797 | } else { | ||
1798 | next_action = VIDEO_PREV; | ||
1799 | } | ||
1800 | break; | ||
1801 | } | ||
1802 | case MPEG_FF | BUTTON_REL: | ||
1803 | { | ||
1804 | osd_stop(); | ||
1805 | next_action = VIDEO_NEXT; | ||
1806 | break; | ||
1807 | } | ||
1808 | default: | ||
1809 | { | ||
1810 | button = old_button; | ||
1811 | osd_seek(button); | ||
1812 | break; | ||
1813 | } | ||
1814 | } | ||
1722 | break; | 1815 | break; |
1723 | } /* MPEG_RW: MPEG_FF: */ | 1816 | } /* MPEG_RW: MPEG_FF: */ |
1724 | 1817 | ||
@@ -1750,13 +1843,18 @@ static void button_loop(void) | |||
1750 | #endif | 1843 | #endif |
1751 | 1844 | ||
1752 | rb->lcd_setfont(FONT_UI); | 1845 | rb->lcd_setfont(FONT_UI); |
1846 | |||
1847 | return next_action; | ||
1753 | } | 1848 | } |
1754 | 1849 | ||
1755 | enum plugin_status plugin_start(const void* parameter) | 1850 | enum plugin_status plugin_start(const void* parameter) |
1756 | { | 1851 | { |
1852 | static char videofile[MAX_PATH]; | ||
1853 | |||
1757 | int status = PLUGIN_ERROR; /* assume failure */ | 1854 | int status = PLUGIN_ERROR; /* assume failure */ |
1758 | int result; | 1855 | int result; |
1759 | int err; | 1856 | int err; |
1857 | bool quit = false; | ||
1760 | const char *errstring; | 1858 | const char *errstring; |
1761 | 1859 | ||
1762 | if (parameter == NULL) { | 1860 | if (parameter == NULL) { |
@@ -1777,46 +1875,76 @@ enum plugin_status plugin_start(const void* parameter) | |||
1777 | rb->lcd_clear_display(); | 1875 | rb->lcd_clear_display(); |
1778 | rb->lcd_update(); | 1876 | rb->lcd_update(); |
1779 | 1877 | ||
1878 | rb->strcpy(videofile, (const char*) parameter); | ||
1879 | |||
1780 | if (stream_init() < STREAM_OK) { | 1880 | if (stream_init() < STREAM_OK) { |
1781 | DEBUGF("Could not initialize streams\n"); | 1881 | DEBUGF("Could not initialize streams\n"); |
1782 | } else { | 1882 | } else { |
1783 | rb->splash(0, "Loading..."); | 1883 | while (!quit) |
1784 | init_settings((char*)parameter); | 1884 | { |
1885 | int next_action = VIDEO_STOP; | ||
1785 | 1886 | ||
1786 | err = stream_open((char *)parameter); | 1887 | init_settings(videofile); |
1888 | err = stream_open(videofile); | ||
1787 | 1889 | ||
1788 | if (err >= STREAM_OK) { | 1890 | if (err >= STREAM_OK) { |
1789 | /* start menu */ | 1891 | /* start menu */ |
1790 | rb->lcd_clear_display(); | 1892 | rb->lcd_clear_display(); |
1791 | rb->lcd_update(); | 1893 | rb->lcd_update(); |
1792 | result = mpeg_start_menu(stream_get_duration()); | 1894 | result = mpeg_start_menu(stream_get_duration()); |
1793 | 1895 | ||
1794 | if (result != MPEG_START_QUIT) { | 1896 | if (result != MPEG_START_QUIT) { |
1795 | /* Enter button loop and process UI */ | 1897 | /* Enter button loop and process UI */ |
1796 | button_loop(); | 1898 | next_action = button_loop(); |
1797 | } | 1899 | } |
1798 | 1900 | ||
1799 | stream_close(); | 1901 | stream_close(); |
1800 | 1902 | ||
1801 | rb->lcd_clear_display(); | 1903 | rb->lcd_clear_display(); |
1802 | rb->lcd_update(); | 1904 | rb->lcd_update(); |
1803 | 1905 | ||
1804 | save_settings(); | 1906 | save_settings(); |
1805 | status = PLUGIN_OK; | 1907 | status = PLUGIN_OK; |
1806 | 1908 | ||
1807 | mpeg_menu_sysevent_handle(); | 1909 | mpeg_menu_sysevent_handle(); |
1808 | } else { | 1910 | } else { |
1809 | DEBUGF("Could not open %s\n", (char*)parameter); | 1911 | DEBUGF("Could not open %s\n", videofile); |
1810 | switch (err) | 1912 | switch (err) |
1913 | { | ||
1914 | case STREAM_UNSUPPORTED: | ||
1915 | errstring = "Unsupported format"; | ||
1916 | break; | ||
1917 | default: | ||
1918 | errstring = "Error opening file: %d"; | ||
1919 | } | ||
1920 | |||
1921 | rb->splashf(HZ*2, errstring, err); | ||
1922 | status = PLUGIN_ERROR; | ||
1923 | } | ||
1924 | |||
1925 | /* return value of button_loop says, what's next */ | ||
1926 | switch (next_action) | ||
1927 | { | ||
1928 | case VIDEO_NEXT: | ||
1811 | { | 1929 | { |
1812 | case STREAM_UNSUPPORTED: | 1930 | if (!get_videofile(VIDEO_NEXT, videofile, sizeof(videofile))) { |
1813 | errstring = "Unsupported format"; | 1931 | /* quit after finished the last videofile */ |
1932 | quit = true; | ||
1933 | } | ||
1814 | break; | 1934 | break; |
1815 | default: | 1935 | } |
1816 | errstring = "Error opening file: %d"; | 1936 | case VIDEO_PREV: |
1937 | { | ||
1938 | get_videofile(VIDEO_PREV, videofile, sizeof(videofile)); | ||
1939 | /* if there is no previous file, play the same videofile */ | ||
1940 | break; | ||
1941 | } | ||
1942 | case VIDEO_STOP: | ||
1943 | { | ||
1944 | quit = true; | ||
1945 | break; | ||
1946 | } | ||
1817 | } | 1947 | } |
1818 | |||
1819 | rb->splashf(HZ*2, errstring, err); | ||
1820 | } | 1948 | } |
1821 | } | 1949 | } |
1822 | 1950 | ||
diff --git a/apps/plugins/mpegplayer/video_thread.c b/apps/plugins/mpegplayer/video_thread.c index 8feacbdef2..4ccdc8b844 100644 --- a/apps/plugins/mpegplayer/video_thread.c +++ b/apps/plugins/mpegplayer/video_thread.c | |||
@@ -503,6 +503,12 @@ static void video_thread_msg(struct video_thread_data *td) | |||
503 | reply = true; | 503 | reply = true; |
504 | break; | 504 | break; |
505 | 505 | ||
506 | case STREAM_CLOSE: | ||
507 | vo_cleanup(); | ||
508 | mpeg2_close(td->mpeg2dec); | ||
509 | reply = true; | ||
510 | break; | ||
511 | |||
506 | case VIDEO_DISPLAY_IS_VISIBLE: | 512 | case VIDEO_DISPLAY_IS_VISIBLE: |
507 | reply = vo_is_visible(); | 513 | reply = vo_is_visible(); |
508 | break; | 514 | break; |