summaryrefslogtreecommitdiff
path: root/apps/playback.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2013-07-14 07:59:39 -0400
committerMichael Sevakis <jethead71@rockbox.org>2014-03-10 04:12:30 +0100
commit31b712286721dd606940c7b557d03e3f714b9604 (patch)
tree8c4a4cc32e9000ea721ebb23aa3c0129ca97bf53 /apps/playback.c
parentdda54b85daa83b7803b4fb189ab45859f96ff3f9 (diff)
downloadrockbox-31b712286721dd606940c7b557d03e3f714b9604.tar.gz
rockbox-31b712286721dd606940c7b557d03e3f714b9604.zip
Implement time-based resume and playback start.
This complements offset-based resume and playback start funcionality. The implementation is global on both HWCODEC and SWCODEC. Basically, if either the specified elapsed or offset are non-zero, it indicates a mid-track resume. To resume by time only, set elapsed to nonzero and offset to zero. To resume by offset only, set offset to nonzero and elapsed to zero. Which one the codec uses and which has priority is up to the codec; however, using an elapsed time covers more cases: * Codecs not able to use an offset such as VGM or other atomic formats * Starting playback at a nonzero elapsed time from a source that contains no offset, such as a cuesheet The change re-versions pretty much everything from tagcache to nvram. Change-Id: Ic7aebb24e99a03ae99585c5e236eba960d163f38 Reviewed-on: http://gerrit.rockbox.org/516 Reviewed-by: Michael Sevakis <jethead71@rockbox.org> Tested: Michael Sevakis <jethead71@rockbox.org>
Diffstat (limited to 'apps/playback.c')
-rw-r--r--apps/playback.c137
1 files changed, 92 insertions, 45 deletions
diff --git a/apps/playback.c b/apps/playback.c
index 5e234beb36..80a0585b17 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -151,6 +151,12 @@ enum audio_id3_types
151}; 151};
152static struct mp3entry static_id3_entries[ID3_TYPE_NUM_STATIC]; /* (A,O) */ 152static struct mp3entry static_id3_entries[ID3_TYPE_NUM_STATIC]; /* (A,O) */
153 153
154struct audio_resume_info
155{
156 unsigned long elapsed;
157 unsigned long offset;
158};
159
154/* Peeking functions can yield and mess us up */ 160/* Peeking functions can yield and mess us up */
155static struct mutex id3_mutex SHAREDBSS_ATTR; /* (A,O)*/ 161static struct mutex id3_mutex SHAREDBSS_ATTR; /* (A,O)*/
156 162
@@ -325,7 +331,8 @@ enum audio_start_playback_flags
325 AUDIO_START_NEWBUF = 0x2, /* Mark the audiobuffer as invalid */ 331 AUDIO_START_NEWBUF = 0x2, /* Mark the audiobuffer as invalid */
326}; 332};
327 333
328static void audio_start_playback(size_t offset, unsigned int flags); 334static void audio_start_playback(const struct audio_resume_info *resume_info,
335 unsigned int flags);
329static void audio_stop_playback(void); 336static void audio_stop_playback(void);
330static void buffer_event_buffer_low_callback(void *data); 337static void buffer_event_buffer_low_callback(void *data);
331static void buffer_event_rebuffer_callback(void *data); 338static void buffer_event_rebuffer_callback(void *data);
@@ -792,7 +799,11 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
792 /* codec messages */ 799 /* codec messages */
793 { Q_AUDIO_PLAY, Q_AUDIO_PLAY }, 800 { Q_AUDIO_PLAY, Q_AUDIO_PLAY },
794 }; 801 };
802
803 static struct audio_resume_info resume;
804
795 bool give_up = false; 805 bool give_up = false;
806
796 /* filebuflen is, at this point, the buffering.c buffer size, 807 /* filebuflen is, at this point, the buffering.c buffer size,
797 * i.e. the audiobuf except voice, scratch mem, pcm, ... */ 808 * i.e. the audiobuf except voice, scratch mem, pcm, ... */
798 ssize_t extradata_size = old_size - filebuflen; 809 ssize_t extradata_size = old_size - filebuflen;
@@ -813,7 +824,9 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
813 824
814 825
815 /* TODO: Do it without stopping playback, if possible */ 826 /* TODO: Do it without stopping playback, if possible */
816 long offset = audio_current_track()->offset; 827 struct mp3entry *id3 = audio_current_track();
828 unsigned long elapsed = id3->elapsed;
829 unsigned long offset = id3->offset;
817 /* resume if playing */ 830 /* resume if playing */
818 bool playing = (audio_status() == AUDIO_STATUS_PLAY); 831 bool playing = (audio_status() == AUDIO_STATUS_PLAY);
819 /* There's one problem with stoping and resuming: If it happens in a too 832 /* There's one problem with stoping and resuming: If it happens in a too
@@ -825,10 +838,20 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
825 * queue_post from the last call to get the correct offset. This also 838 * queue_post from the last call to get the correct offset. This also
826 * lets us conviniently remove the queue event so Q_AUDIO_PLAY is only 839 * lets us conviniently remove the queue event so Q_AUDIO_PLAY is only
827 * processed once. */ 840 * processed once. */
828 bool play_queued = queue_peek_ex(&audio_queue, &ev, QPEEK_REMOVE_EVENTS, filter_list); 841 bool play_queued = queue_peek_ex(&audio_queue, &ev, QPEEK_REMOVE_EVENTS,
842 filter_list);
829 843
830 if (playing && offset > 0) /* current id3->offset is king */ 844 if (playing && ev.data != (intptr_t)&resume)
831 ev.data = offset; 845 {
846 resume = *(struct audio_resume_info *)ev.data;
847
848 /* current id3->elapsed/offset are king */
849 if (elapsed > 0)
850 resume.elapsed = elapsed;
851
852 if (offset > 0)
853 resume.offset = offset;
854 }
832 855
833 /* don't call audio_hard_stop() as it frees this handle */ 856 /* don't call audio_hard_stop() as it frees this handle */
834 if (thread_self() == audio_thread_id) 857 if (thread_self() == audio_thread_id)
@@ -867,7 +890,7 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
867 if (playing || play_queued) 890 if (playing || play_queued)
868 { 891 {
869 /* post, to make subsequent calls not break the resume position */ 892 /* post, to make subsequent calls not break the resume position */
870 audio_queue_post(Q_AUDIO_PLAY, ev.data); 893 audio_queue_post(Q_AUDIO_PLAY, (intptr_t)&resume);
871 } 894 }
872 895
873 return BUFLIB_CB_OK; 896 return BUFLIB_CB_OK;
@@ -1099,7 +1122,8 @@ static void audio_update_and_announce_next_track(const struct mp3entry *id3_next
1099 1122
1100/* Bring the user current mp3entry up to date and set a new offset for the 1123/* Bring the user current mp3entry up to date and set a new offset for the
1101 buffered metadata */ 1124 buffered metadata */
1102static void playing_id3_sync(struct track_info *user_info, off_t offset) 1125static void playing_id3_sync(struct track_info *user_info,
1126 unsigned long elapsed, unsigned long offset)
1103{ 1127{
1104 id3_mutex_lock(); 1128 id3_mutex_lock();
1105 1129
@@ -1113,9 +1137,14 @@ static void playing_id3_sync(struct track_info *user_info, off_t offset)
1113 1137
1114 id3_write(PLAYING_ID3, id3); 1138 id3_write(PLAYING_ID3, id3);
1115 1139
1116 if (offset < 0) 1140 if (elapsed == (unsigned long)-1)
1117 { 1141 {
1118 playing_id3->elapsed = e; 1142 playing_id3->elapsed = e;
1143 elapsed = 0;
1144 }
1145
1146 if (offset == (unsigned long)-1)
1147 {
1119 playing_id3->offset = o; 1148 playing_id3->offset = o;
1120 offset = 0; 1149 offset = 0;
1121 } 1150 }
@@ -1123,7 +1152,10 @@ static void playing_id3_sync(struct track_info *user_info, off_t offset)
1123 pcm_play_unlock(); 1152 pcm_play_unlock();
1124 1153
1125 if (id3) 1154 if (id3)
1155 {
1156 id3->elapsed = elapsed;
1126 id3->offset = offset; 1157 id3->offset = offset;
1158 }
1127 1159
1128 id3_mutex_unlock(); 1160 id3_mutex_unlock();
1129} 1161}
@@ -1299,19 +1331,16 @@ static bool audio_get_track_metadata(int offset, struct mp3entry *id3)
1299 return false; 1331 return false;
1300} 1332}
1301 1333
1302/* Get a resume rewind adjusted offset from the ID3 */ 1334/* Get resume rewind adjusted progress from the ID3 */
1303static unsigned long resume_rewind_adjusted_offset(const struct mp3entry *id3) 1335static void resume_rewind_adjust_progress(const struct mp3entry *id3,
1336 unsigned long *elapsed,
1337 unsigned long *offset)
1304{ 1338{
1305 unsigned long offset = id3->offset; 1339 unsigned int rewind = MAX(global_settings.resume_rewind, 0);
1306 size_t resume_rewind = global_settings.resume_rewind * 1340 unsigned long d_e = rewind*1000;
1307 id3->bitrate * (1000/8); 1341 *elapsed = id3->elapsed - MIN(id3->elapsed, d_e);
1308 1342 unsigned long d_o = rewind * id3->bitrate * (1000/8);
1309 if (offset < resume_rewind) 1343 *offset = id3->offset - MIN(id3->offset, d_o);
1310 offset = 0;
1311 else
1312 offset -= resume_rewind;
1313
1314 return offset;
1315} 1344}
1316 1345
1317/* Get the codec into ram and initialize it - keep it if it's ready */ 1346/* Get the codec into ram and initialize it - keep it if it's ready */
@@ -1436,7 +1465,7 @@ static bool audio_start_codec(bool auto_skip)
1436#ifdef HAVE_TAGCACHE 1465#ifdef HAVE_TAGCACHE
1437 bool autoresume_enable = global_settings.autoresume_enable; 1466 bool autoresume_enable = global_settings.autoresume_enable;
1438 1467
1439 if (autoresume_enable && !cur_id3->offset) 1468 if (autoresume_enable && !(cur_id3->elapsed || cur_id3->offset))
1440 { 1469 {
1441 /* Resume all manually selected tracks */ 1470 /* Resume all manually selected tracks */
1442 bool resume = !auto_skip; 1471 bool resume = !auto_skip;
@@ -1466,10 +1495,13 @@ static bool audio_start_codec(bool auto_skip)
1466 } 1495 }
1467 1496
1468 if (!resume) 1497 if (!resume)
1498 {
1499 cur_id3->elapsed = 0;
1469 cur_id3->offset = 0; 1500 cur_id3->offset = 0;
1501 }
1470 1502
1471 logf("%s: Set offset for %s to %lX\n", __func__, 1503 logf("%s: Set resume for %s to %lu %lX", __func__,
1472 cur_id3->title, cur_id3->offset); 1504 cur_id3->title, cur_id3->elapsed, cur_id3->offset);
1473 } 1505 }
1474#endif /* HAVE_TAGCACHE */ 1506#endif /* HAVE_TAGCACHE */
1475 1507
@@ -1481,7 +1513,8 @@ static bool audio_start_codec(bool auto_skip)
1481 and back again will cause accumulation of silent rewinds - that's not 1513 and back again will cause accumulation of silent rewinds - that's not
1482 our job to track directly nor could it be in any reasonable way 1514 our job to track directly nor could it be in any reasonable way
1483 */ 1515 */
1484 cur_id3->offset = resume_rewind_adjusted_offset(cur_id3); 1516 resume_rewind_adjust_progress(cur_id3, &cur_id3->elapsed,
1517 &cur_id3->offset);
1485 1518
1486 /* Update the codec API with the metadata and track info */ 1519 /* Update the codec API with the metadata and track info */
1487 id3_write(CODEC_ID3, cur_id3); 1520 id3_write(CODEC_ID3, cur_id3);
@@ -1494,7 +1527,7 @@ static bool audio_start_codec(bool auto_skip)
1494 codec_go(); 1527 codec_go();
1495 1528
1496#ifdef HAVE_TAGCACHE 1529#ifdef HAVE_TAGCACHE
1497 if (!autoresume_enable || cur_id3->offset) 1530 if (!autoresume_enable || cur_id3->elapsed || cur_id3->offset)
1498#endif 1531#endif
1499 { 1532 {
1500 /* Send the "buffer" event now */ 1533 /* Send the "buffer" event now */
@@ -1923,7 +1956,9 @@ static int audio_finish_load_track(struct track_info *info)
1923 1956
1924 /** Finally, load the audio **/ 1957 /** Finally, load the audio **/
1925 size_t file_offset = 0; 1958 size_t file_offset = 0;
1926 track_id3->elapsed = 0; 1959
1960 if (track_id3->elapsed > track_id3->length)
1961 track_id3->elapsed = 0;
1927 1962
1928 if (track_id3->offset >= info->filesize) 1963 if (track_id3->offset >= info->filesize)
1929 track_id3->offset = 0; 1964 track_id3->offset = 0;
@@ -1933,7 +1968,11 @@ static int audio_finish_load_track(struct track_info *info)
1933 1968
1934 /* Adjust for resume rewind so we know what to buffer - starting the codec 1969 /* Adjust for resume rewind so we know what to buffer - starting the codec
1935 calls it again, so we don't save it (and they shouldn't accumulate) */ 1970 calls it again, so we don't save it (and they shouldn't accumulate) */
1936 size_t offset = resume_rewind_adjusted_offset(track_id3); 1971 unsigned long elapsed, offset;
1972 resume_rewind_adjust_progress(track_id3, &elapsed, &offset);
1973
1974 logf("%s: Set resume for %s to %lu %lX", __func__,
1975 id3->title, elapsed, offset);
1937 1976
1938 enum data_type audiotype = rbcodec_format_is_atomic(track_id3->codectype) ? 1977 enum data_type audiotype = rbcodec_format_is_atomic(track_id3->codectype) ?
1939 TYPE_ATOMIC_AUDIO : TYPE_PACKET_AUDIO; 1978 TYPE_ATOMIC_AUDIO : TYPE_PACKET_AUDIO;
@@ -2168,7 +2207,7 @@ static void audio_on_finish_load_track(int id3_hid)
2168 change otherwise */ 2207 change otherwise */
2169 bool was_valid = valid_mp3entry(id3_get(PLAYING_ID3)); 2208 bool was_valid = valid_mp3entry(id3_get(PLAYING_ID3));
2170 2209
2171 playing_id3_sync(info, -1); 2210 playing_id3_sync(info, -1, -1);
2172 2211
2173 if (!was_valid) 2212 if (!was_valid)
2174 { 2213 {
@@ -2306,7 +2345,7 @@ static void audio_begin_track_change(enum pcm_track_change_type type,
2306 if (audio_start_codec(auto_skip)) 2345 if (audio_start_codec(auto_skip))
2307 { 2346 {
2308 if (!auto_skip) 2347 if (!auto_skip)
2309 playing_id3_sync(info, -1); 2348 playing_id3_sync(info, -1, -1);
2310 return; 2349 return;
2311 } 2350 }
2312 2351
@@ -2455,8 +2494,11 @@ static void audio_on_track_changed(void)
2455/* Begin playback from an idle state, transition to a new playlist or 2494/* Begin playback from an idle state, transition to a new playlist or
2456 invalidate the buffer and resume (if playing). 2495 invalidate the buffer and resume (if playing).
2457 (usually Q_AUDIO_PLAY, Q_AUDIO_REMAKE_AUDIO_BUFFER) */ 2496 (usually Q_AUDIO_PLAY, Q_AUDIO_REMAKE_AUDIO_BUFFER) */
2458static void audio_start_playback(size_t offset, unsigned int flags) 2497static void audio_start_playback(const struct audio_resume_info *resume_info,
2498 unsigned int flags)
2459{ 2499{
2500 struct audio_resume_info resume =
2501 *(resume_info ?: &(struct audio_resume_info){ 0, 0 } );
2460 enum play_status old_status = play_status; 2502 enum play_status old_status = play_status;
2461 2503
2462 if (flags & AUDIO_START_NEWBUF) 2504 if (flags & AUDIO_START_NEWBUF)
@@ -2469,7 +2511,8 @@ static void audio_start_playback(size_t offset, unsigned int flags)
2469 2511
2470 if (old_status != PLAY_STOPPED) 2512 if (old_status != PLAY_STOPPED)
2471 { 2513 {
2472 logf("%s(%lu): skipping", __func__, (unsigned long)offset); 2514 logf("%s(%lu, %lu): skipping", __func__, resume.elapsed,
2515 resume.offset);
2473 2516
2474 halt_decoding_track(true); 2517 halt_decoding_track(true);
2475 2518
@@ -2481,7 +2524,8 @@ static void audio_start_playback(size_t offset, unsigned int flags)
2481 /* Clear out some stuff to resume the current track where it 2524 /* Clear out some stuff to resume the current track where it
2482 left off */ 2525 left off */
2483 pcmbuf_play_stop(); 2526 pcmbuf_play_stop();
2484 offset = id3_get(PLAYING_ID3)->offset; 2527 resume.elapsed = id3_get(PLAYING_ID3)->elapsed;
2528 resume.offset = id3_get(PLAYING_ID3)->offset;
2485 track_list_clear(TRACK_LIST_CLEAR_ALL); 2529 track_list_clear(TRACK_LIST_CLEAR_ALL);
2486 } 2530 }
2487 else 2531 else
@@ -2505,7 +2549,8 @@ static void audio_start_playback(size_t offset, unsigned int flags)
2505 return; /* Must already be playing */ 2549 return; /* Must already be playing */
2506 2550
2507 /* Cold playback start from a stopped state */ 2551 /* Cold playback start from a stopped state */
2508 logf("%s(%lu): starting", __func__, offset); 2552 logf("%s(%lu, %lu): starting", __func__, resume.elapsed,
2553 resume.offset);
2509 2554
2510 /* Set audio parameters */ 2555 /* Set audio parameters */
2511#if INPUT_SRC_CAPS != 0 2556#if INPUT_SRC_CAPS != 0
@@ -2555,7 +2600,7 @@ static void audio_start_playback(size_t offset, unsigned int flags)
2555 if (trackstat >= LOAD_TRACK_OK) 2600 if (trackstat >= LOAD_TRACK_OK)
2556 { 2601 {
2557 /* This is the currently playing track - get metadata, stat */ 2602 /* This is the currently playing track - get metadata, stat */
2558 playing_id3_sync(track_list_current(0), offset); 2603 playing_id3_sync(track_list_current(0), resume.elapsed, resume.offset);
2559 2604
2560 if (valid_mp3entry(id3_get(PLAYING_ID3))) 2605 if (valid_mp3entry(id3_get(PLAYING_ID3)))
2561 { 2606 {
@@ -2892,7 +2937,9 @@ static void audio_on_ff_rewind(long time)
2892 2937
2893 if (!haltres) 2938 if (!haltres)
2894 { 2939 {
2895 /* If codec must be (re)started, reset the offset */ 2940 /* If codec must be (re)started, reset the resume info so that
2941 it doesn't execute resume procedures */
2942 ci_id3->elapsed = 0;
2896 ci_id3->offset = 0; 2943 ci_id3->offset = 0;
2897 } 2944 }
2898 2945
@@ -2970,7 +3017,7 @@ static void audio_on_audio_flush(void)
2970 not possible so a restart is required in order to continue the 3017 not possible so a restart is required in order to continue the
2971 currently playing track without the now invalid future track 3018 currently playing track without the now invalid future track
2972 playing */ 3019 playing */
2973 audio_start_playback(0, AUDIO_START_RESTART); 3020 audio_start_playback(NULL, AUDIO_START_RESTART);
2974 break; 3021 break;
2975 3022
2976 default: /* Nothing else is a state */ 3023 default: /* Nothing else is a state */
@@ -3008,7 +3055,7 @@ void audio_playback_handler(struct queue_event *ev)
3008 /** Control messages **/ 3055 /** Control messages **/
3009 case Q_AUDIO_PLAY: 3056 case Q_AUDIO_PLAY:
3010 LOGFQUEUE("playback < Q_AUDIO_PLAY"); 3057 LOGFQUEUE("playback < Q_AUDIO_PLAY");
3011 audio_start_playback(ev->data, 0); 3058 audio_start_playback((struct audio_resume_info *)ev->data, 0);
3012 break; 3059 break;
3013 3060
3014#ifdef HAVE_RECORDING 3061#ifdef HAVE_RECORDING
@@ -3082,7 +3129,7 @@ void audio_playback_handler(struct queue_event *ev)
3082 case Q_AUDIO_REMAKE_AUDIO_BUFFER: 3129 case Q_AUDIO_REMAKE_AUDIO_BUFFER:
3083 /* buffer needs to be reinitialized */ 3130 /* buffer needs to be reinitialized */
3084 LOGFQUEUE("playback < Q_AUDIO_REMAKE_AUDIO_BUFFER"); 3131 LOGFQUEUE("playback < Q_AUDIO_REMAKE_AUDIO_BUFFER");
3085 audio_start_playback(0, AUDIO_START_RESTART | AUDIO_START_NEWBUF); 3132 audio_start_playback(NULL, AUDIO_START_RESTART | AUDIO_START_NEWBUF);
3086 if (play_status == PLAY_STOPPED) 3133 if (play_status == PLAY_STOPPED)
3087 return; /* just need to change buffer state */ 3134 return; /* just need to change buffer state */
3088 break; 3135 break;
@@ -3368,8 +3415,8 @@ struct mp3entry * audio_next_track(void)
3368 return id3; 3415 return id3;
3369} 3416}
3370 3417
3371/* Start playback at the specified offset */ 3418/* Start playback at the specified elapsed time or offset */
3372void audio_play(long offset) 3419void audio_play(unsigned long elapsed, unsigned long offset)
3373{ 3420{
3374 logf("audio_play"); 3421 logf("audio_play");
3375 3422
@@ -3379,8 +3426,9 @@ void audio_play(long offset)
3379 talk_force_shutup(); 3426 talk_force_shutup();
3380#endif 3427#endif
3381 3428
3382 LOGFQUEUE("audio >| audio Q_AUDIO_PLAY: %ld", offset); 3429 LOGFQUEUE("audio >| audio Q_AUDIO_PLAY: %lu %lX", elapsed, offset);
3383 audio_queue_send(Q_AUDIO_PLAY, offset); 3430 audio_queue_send(Q_AUDIO_PLAY,
3431 (intptr_t)&(struct audio_resume_info){ elapsed, offset });
3384} 3432}
3385 3433
3386/* Stop playback if playing */ 3434/* Stop playback if playing */
@@ -3582,11 +3630,10 @@ void playback_release_aa_slot(int slot)
3582} 3630}
3583#endif /* HAVE_ALBUMART */ 3631#endif /* HAVE_ALBUMART */
3584 3632
3585/* Would normally calculate byte offset from an elapsed time but is not 3633/* Return file byte offset */
3586 used on SWCODEC */
3587int audio_get_file_pos(void) 3634int audio_get_file_pos(void)
3588{ 3635{
3589 return 0; 3636 return id3_get(PLAYING_ID3)->offset;
3590} 3637}
3591 3638
3592/* Return total file buffer length after accounting for the talk buf */ 3639/* Return total file buffer length after accounting for the talk buf */