19 "title" :
"VuoVideoPlayer",
26 "VuoList_VuoAudioSamples",
34 #define TRY_RELEASE_VIDEO(v) { if(v.image != NULL) { VuoRelease(v.image); v.image = NULL; v.timestamp = -1.0; } }
37 #define TRY_RELEASE_AUDIO(a) { if (a.channels != NULL) { VuoRelease(a.channels); a.channels = NULL; a.timestamp = -1.0; } }
43 player->video_semaphore = dispatch_semaphore_create(0);
44 player->audio_semaphore = dispatch_semaphore_create(0);
46 player->playbackRate = 1.;
47 player->audioEnabled =
true;
48 player->isPlaying =
false;
49 player->lastVideoTimestamp = 0.;
50 player->lastVideoFrameDelta = 0.;
53 player->preferFfmpeg = optimization == VuoVideoOptimization_Random;
57 if(player->decoder == NULL || !player->decoder->
CanPlayAudio())
61 if(alt != NULL && (player->decoder == NULL || alt->
CanPlayAudio()) )
63 if(player->decoder != NULL)
64 delete player->decoder;
66 player->decoder = alt;
68 if(player->preferFfmpeg)
69 VDebugLog(
"Using AvFoundation video decoder despite optimization preference.");
71 VDebugLog(
"Using FFmpeg video decoder despite optimization preference.");
73 else if (!player->decoder && !alt)
75 VUserLog(
"AVFoundation and FFmpeg video decoders failed to open the movie.");
76 player->failedLoading =
true;
81 if(player->preferFfmpeg)
82 VDebugLog(
"Using first choice video decoder FFmpeg");
84 VDebugLog(
"Using first choice video decoder AVFoundation");
87 player->isReady = player->decoder != NULL && player->decoder->
IsReady();
89 player->audioFrame.channels = NULL;
90 player->videoFrame.image = NULL;
92 if(player->decoder == NULL)
100 player->decoder->
onReadyToPlay = &VuoVideoPlayer::OnDecoderPlaybackReady;
109 while( !
IsReady() && !failedLoading ) {
110 usleep(USEC_PER_SEC / 24);
116 VuoVideoPlayer::~VuoVideoPlayer()
118 pthread_mutex_lock(&decoderMutex);
124 stopTimer(&video_timer, &video_semaphore);
125 dispatch_release(video_semaphore);
128 stopTimer(&audio_timer, &audio_semaphore);
129 dispatch_release(audio_semaphore);
141 pthread_mutex_unlock(&decoderMutex);
143 int ret = pthread_mutex_destroy(&decoderMutex);
148 VUserLog(
"Failed destroying decoder mutex: EBUSY");
152 VUserLog(
"Failed destroying decoder mutex: EINVAL");
163 void VuoVideoPlayer::OnDecoderPlaybackReady(
bool canPlayMedia)
165 pthread_mutex_lock(&decoderMutex);
185 _SetPlaybackRate(onReadyPlaybackRate);
188 if (!_Seek(onReadySeek))
192 _Play( dispatch_time(DISPATCH_TIME_NOW, 0) );
212 _SetPlaybackRate(onReadyPlaybackRate);
215 if (!_Seek(onReadySeek))
219 _Play(dispatch_time(DISPATCH_TIME_NOW, 0));
223 VUserLog(
"On second thought, AVFoundation decided it couldn't play this video. Fell back on FFmpeg successfully.");
227 VUserLog(
"Both AvFoundation and FFmpeg failed to load this video. Try using a different video or audio codec.");
228 failedLoading =
true;
233 VUserLog(
"Both AvFoundation and FFmpeg failed to load this video. Try using a different video or audio codec.");
234 failedLoading =
true;
238 pthread_mutex_unlock(&decoderMutex);
243 pthread_mutex_lock(&decoderMutex);
251 pthread_mutex_unlock(&decoderMutex);
256 _Play( dispatch_time(DISPATCH_TIME_NOW, 0) );
259 pthread_mutex_unlock(&decoderMutex);
264 pthread_mutex_lock(&decoderMutex);
269 pthread_mutex_unlock(&decoderMutex);
274 pthread_mutex_lock(&decoderMutex);
276 userSetPlaybackRate = rate;
279 onReadyPlaybackRate = rate;
281 _SetPlaybackRate(rate);
283 pthread_mutex_unlock(&decoderMutex);
288 pthread_mutex_lock(&decoderMutex);
290 bool success =
false;
293 onReadySeek = second;
295 success = _Seek(second);
297 pthread_mutex_unlock(&decoderMutex);
302 void VuoVideoPlayer::_Play(dispatch_time_t start)
305 playbackStart = start;
307 lastVideoTimestamp = timestampStart;
309 startTimer(&video_timer, start, &video_semaphore, &VuoVideoPlayer::sendVideoFrame);
310 startTimer(&audio_timer, start, &audio_semaphore, &VuoVideoPlayer::sendAudioFrame);
313 void VuoVideoPlayer::_Pause()
317 if(video_timer != NULL)
318 stopTimer(&video_timer, &video_semaphore);
320 if(audio_timer != NULL)
321 stopTimer(&audio_timer, &audio_semaphore);
324 void VuoVideoPlayer::_SetPlaybackRate(
double rate)
326 bool wasPlaying = isPlaying;
331 playbackRate = rate < 0 ? fmin(rate, -.00001) : fmax(rate, .00001);
338 if(lastSentVideoTime != DISPATCH_TIME_FOREVER)
340 int64_t frame_delta = ((1./decoder->
GetFrameRate()) / playbackRate) * NSEC_PER_SEC;
341 dispatch_time_t elapsed =
MIN( dispatch_time(DISPATCH_TIME_NOW, 0) - lastSentVideoTime, frame_delta );
342 dispatch_time_t timer = dispatch_time(DISPATCH_TIME_NOW, frame_delta);
343 _Play(timer - elapsed);
347 _Play(dispatch_time(DISPATCH_TIME_NOW, 0));
352 bool VuoVideoPlayer::_Seek(
double second)
354 bool wasPlaying = isPlaying;
361 lastSentVideoTime = DISPATCH_TIME_FOREVER;
362 _Play( dispatch_time(DISPATCH_TIME_NOW, 0) );
383 return lastVideoFrameDelta;
388 return lastVideoTimestamp;
396 while(!
IsReady() && !failedLoading)
398 usleep(USEC_PER_SEC / 24);
401 pthread_mutex_lock(&decoderMutex);
403 pthread_mutex_unlock(&decoderMutex);
410 unsigned int channelCount = 0;
412 while(!
IsReady() && !failedLoading)
414 usleep(USEC_PER_SEC / 24);
417 pthread_mutex_lock(&decoderMutex);
419 pthread_mutex_unlock(&decoderMutex);
426 const int MAX_FRAMES_TO_STEP_BEFORE_SEEK = 5;
427 const double floatingPointError = 0.0001;
428 double averageFrameDuration = 1. / decoder->
GetFrameRate();
430 double target = fmax(0, fmin(duration, second));
433 if(videoFrame.image != NULL)
435 double frameDelta = fabs(target - videoFrame.timestamp);
438 if ( ((videoFrame.timestamp < target + floatingPointError) && (target + floatingPointError < videoFrame.timestamp + videoFrame.duration))
447 else if( frameDelta < averageFrameDuration * MAX_FRAMES_TO_STEP_BEFORE_SEEK && (target - videoFrame.timestamp > 0 == playbackRate > 0) )
449 for(
int i = 0; i < MAX_FRAMES_TO_STEP_BEFORE_SEEK; i++)
459 if (videoFrame.timestamp + videoFrame.duration > target + floatingPointError)
470 bool seekSuccess =
false;
474 if(target > duration - .5 && playbackRate > 0)
476 seekSuccess =
Seek(duration - .5);
478 while (videoFrame.timestamp + videoFrame.duration < target + floatingPointError)
496 seekSuccess =
Seek(target);
507 if(videoFrame.image != NULL)
531 void VuoVideoPlayer::startTimer(dispatch_source_t* timer, dispatch_time_t start, dispatch_semaphore_t* semaphore,
void (
VuoVideoPlayer::*func)(
void))
533 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
535 *timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, DISPATCH_TIMER_STRICT, queue);
537 dispatch_source_set_timer(
540 DISPATCH_TIME_FOREVER,
543 dispatch_source_set_event_handler(*timer, ^{ ((*this).*func)(); });
544 dispatch_source_set_cancel_handler(*timer, ^{ dispatch_semaphore_signal(*semaphore); });
546 dispatch_resume(*timer);
549 void VuoVideoPlayer::stopTimer(dispatch_source_t* timer, dispatch_semaphore_t* semaphore)
551 dispatch_source_cancel(*timer);
552 dispatch_semaphore_wait(*semaphore, DISPATCH_TIME_FOREVER);
553 dispatch_release(*timer);
562 void VuoVideoPlayer::sendVideoFrame()
564 if(videoFrame.image != NULL)
568 lastSentVideoTime = dispatch_time(DISPATCH_TIME_NOW, 0);
573 videoFrame.image = NULL;
576 double nextFrameEvent = 0;
581 double presentationRelativeTimestamp = ((videoFrame.timestamp - timestampStart) / playbackRate);
582 lastVideoFrameDelta = videoFrame.timestamp - lastVideoTimestamp;
583 lastVideoTimestamp = videoFrame.timestamp;
584 nextFrameEvent = NSEC_PER_SEC * presentationRelativeTimestamp;
588 videoFrame.image = NULL;
595 pthread_mutex_lock(&decoderMutex);
599 if (playbackRate < 0 && timestampStart == 0)
609 case VuoLoopType_None:
614 case VuoLoopType_Loop:
616 stopTimer(&audio_timer, &audio_semaphore);
619 playbackRate = userSetPlaybackRate;
623 playbackStart = dispatch_time(DISPATCH_TIME_NOW, 0);
625 lastVideoTimestamp = timestampStart;
627 startTimer(&audio_timer, dispatch_time(DISPATCH_TIME_NOW, 0), &audio_semaphore, &VuoVideoPlayer::sendAudioFrame);
630 case VuoLoopType_Mirror:
631 stopTimer(&audio_timer, &audio_semaphore);
632 playbackRate = -playbackRate;
635 playbackStart = dispatch_time(DISPATCH_TIME_NOW, 0);
637 lastVideoTimestamp = timestampStart;
640 startTimer(&audio_timer, dispatch_time(DISPATCH_TIME_NOW, 0), &audio_semaphore, &VuoVideoPlayer::sendAudioFrame);
644 pthread_mutex_unlock(&decoderMutex);
647 if(isPlaying && fabs(playbackRate) > .0001)
650 dispatch_source_set_timer(
652 dispatch_time(playbackStart, nextFrameEvent),
653 DISPATCH_TIME_FOREVER,
658 void VuoVideoPlayer::sendAudioFrame()
666 audioFrame.channels = NULL;
674 double presentationRelativeTimestamp = ((audioFrame.timestamp - timestampStart) / playbackRate);
675 lastAudioTimestamp = audioFrame.timestamp;
677 dispatch_source_set_timer(
679 dispatch_time(playbackStart, NSEC_PER_SEC * presentationRelativeTimestamp),
680 DISPATCH_TIME_FOREVER,
686 audioFrame.channels = NULL;