19 "title" :
"VuoVideoPlayer",
26 "VuoList_VuoAudioSamples",
36 #define TRY_RELEASE_VIDEO(v) { if(v.image != NULL) { VuoRelease(v.image); v.image = NULL; v.timestamp = -1.0; } }
39 #define TRY_RELEASE_AUDIO(a) { if (a.channels != NULL) { VuoRelease(a.channels); a.channels = NULL; a.timestamp = -1.0; } }
45 player->video_semaphore = dispatch_semaphore_create(0);
46 player->audio_semaphore = dispatch_semaphore_create(0);
48 player->playbackRate = 1.;
49 player->audioEnabled =
true;
50 player->isPlaying =
false;
51 player->lastVideoTimestamp = 0.;
52 player->lastVideoFrameDelta = 0.;
55 player->preferFfmpeg = optimization == VuoVideoOptimization_Random;
59 if(player->decoder == NULL || !player->decoder->
CanPlayAudio())
63 if(alt != NULL && (player->decoder == NULL || alt->
CanPlayAudio()) )
65 delete player->decoder;
67 player->decoder = alt;
69 if(player->preferFfmpeg)
70 VDebugLog(
"Using AvFoundation video decoder despite optimization preference.");
72 VDebugLog(
"Using FFmpeg video decoder despite optimization preference.");
74 else if (!player->decoder && !alt)
76 VUserLog(
"AVFoundation and FFmpeg video decoders failed to open the movie.");
77 player->failedLoading =
true;
82 if(player->preferFfmpeg)
83 VDebugLog(
"Using first choice video decoder FFmpeg");
85 VDebugLog(
"Using first choice video decoder AVFoundation");
88 player->isReady = player->decoder != NULL && player->decoder->
IsReady();
90 player->audioFrame.channels = NULL;
91 player->videoFrame.image = NULL;
93 if(player->decoder == NULL)
101 player->decoder->
onReadyToPlay = &VuoVideoPlayer::OnDecoderPlaybackReady;
110 while( !
IsReady() && !failedLoading ) {
111 usleep(USEC_PER_SEC / 24);
117 VuoVideoPlayer::~VuoVideoPlayer()
119 pthread_mutex_lock(&decoderMutex);
125 stopTimer(&video_timer, &video_semaphore);
126 dispatch_release(video_semaphore);
129 stopTimer(&audio_timer, &audio_semaphore);
130 dispatch_release(audio_semaphore);
140 pthread_mutex_unlock(&decoderMutex);
142 int ret = pthread_mutex_destroy(&decoderMutex);
144 VUserLog(
"Error: Couldn't destroy decoder mutex: %s", strerror(ret));
152 void VuoVideoPlayer::OnDecoderPlaybackReady(
bool canPlayMedia)
154 pthread_mutex_lock(&decoderMutex);
173 _SetPlaybackRate(onReadyPlaybackRate);
176 if (!_Seek(onReadySeek))
180 _Play( dispatch_time(DISPATCH_TIME_NOW, 0) );
197 _SetPlaybackRate(onReadyPlaybackRate);
200 if (!_Seek(onReadySeek))
204 _Play(dispatch_time(DISPATCH_TIME_NOW, 0));
208 VUserLog(
"On second thought, AVFoundation decided it couldn't play this video. Fell back on FFmpeg successfully.");
212 VUserLog(
"Both AvFoundation and FFmpeg failed to load this video. Try using a different video or audio codec.");
213 failedLoading =
true;
218 VUserLog(
"Both AvFoundation and FFmpeg failed to load this video. Try using a different video or audio codec.");
219 failedLoading =
true;
223 pthread_mutex_unlock(&decoderMutex);
228 pthread_mutex_lock(&decoderMutex);
236 pthread_mutex_unlock(&decoderMutex);
241 _Play( dispatch_time(DISPATCH_TIME_NOW, 0) );
244 pthread_mutex_unlock(&decoderMutex);
249 pthread_mutex_lock(&decoderMutex);
254 pthread_mutex_unlock(&decoderMutex);
259 pthread_mutex_lock(&decoderMutex);
261 userSetPlaybackRate = rate;
264 onReadyPlaybackRate = rate;
266 _SetPlaybackRate(rate);
268 pthread_mutex_unlock(&decoderMutex);
273 pthread_mutex_lock(&decoderMutex);
275 bool success =
false;
278 onReadySeek = second;
280 success = _Seek(second);
282 pthread_mutex_unlock(&decoderMutex);
287 void VuoVideoPlayer::_Play(dispatch_time_t start)
290 playbackStart = start;
292 lastVideoTimestamp = timestampStart;
294 startTimer(&video_timer, start, &video_semaphore, &VuoVideoPlayer::sendVideoFrame);
295 startTimer(&audio_timer, start, &audio_semaphore, &VuoVideoPlayer::sendAudioFrame);
298 void VuoVideoPlayer::_Pause()
302 if(video_timer != NULL)
303 stopTimer(&video_timer, &video_semaphore);
305 if(audio_timer != NULL)
306 stopTimer(&audio_timer, &audio_semaphore);
309 void VuoVideoPlayer::_SetPlaybackRate(
double rate)
311 bool wasPlaying = isPlaying;
316 playbackRate = rate < 0 ? fmin(rate, -.00001) : fmax(rate, .00001);
323 if(lastSentVideoTime != DISPATCH_TIME_FOREVER)
325 int64_t frame_delta = ((1./decoder->
GetFrameRate()) / playbackRate) * NSEC_PER_SEC;
326 dispatch_time_t elapsed =
MIN( dispatch_time(DISPATCH_TIME_NOW, 0) - lastSentVideoTime, frame_delta );
327 dispatch_time_t timer = dispatch_time(DISPATCH_TIME_NOW, frame_delta);
328 _Play(timer - elapsed);
332 _Play(dispatch_time(DISPATCH_TIME_NOW, 0));
337 bool VuoVideoPlayer::_Seek(
double second)
339 bool wasPlaying = isPlaying;
346 lastSentVideoTime = DISPATCH_TIME_FOREVER;
347 _Play( dispatch_time(DISPATCH_TIME_NOW, 0) );
368 return lastVideoFrameDelta;
373 return lastVideoTimestamp;
384 usleep(USEC_PER_SEC / 24);
387 failedLoading =
true;
389 pthread_mutex_lock(&decoderMutex);
391 pthread_mutex_unlock(&decoderMutex);
398 unsigned int channelCount = 0;
403 usleep(USEC_PER_SEC / 24);
406 failedLoading =
true;
408 pthread_mutex_lock(&decoderMutex);
410 pthread_mutex_unlock(&decoderMutex);
420 const int MAX_FRAMES_TO_STEP_BEFORE_SEEK = 5;
421 const double floatingPointError = 0.0001;
422 double averageFrameDuration = 1. / decoder->
GetFrameRate();
424 double target = fmax(0, fmin(duration, second));
427 if(videoFrame.image != NULL)
429 double frameDelta = fabs(target - videoFrame.timestamp);
432 if ( ((videoFrame.timestamp < target + floatingPointError) && (target + floatingPointError < videoFrame.timestamp + videoFrame.duration))
441 else if( frameDelta < averageFrameDuration * MAX_FRAMES_TO_STEP_BEFORE_SEEK && (target - videoFrame.timestamp > 0 == playbackRate > 0) )
443 for(
int i = 0; i < MAX_FRAMES_TO_STEP_BEFORE_SEEK; i++)
453 if (videoFrame.timestamp + videoFrame.duration > target + floatingPointError)
464 bool seekSuccess =
false;
468 if(target > duration - .5 && playbackRate > 0)
470 seekSuccess =
Seek(duration - .5);
472 while (videoFrame.timestamp + videoFrame.duration < target + floatingPointError)
490 seekSuccess =
Seek(target);
501 if(videoFrame.image != NULL)
525 void VuoVideoPlayer::startTimer(dispatch_source_t* timer, dispatch_time_t start, dispatch_semaphore_t* semaphore,
void (
VuoVideoPlayer::*func)(
void))
527 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
529 *timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, DISPATCH_TIMER_STRICT, queue);
531 dispatch_source_set_timer(
534 DISPATCH_TIME_FOREVER,
537 dispatch_source_set_event_handler(*timer, ^{ ((*this).*func)(); });
538 dispatch_source_set_cancel_handler(*timer, ^{ dispatch_semaphore_signal(*semaphore); });
540 dispatch_resume(*timer);
543 void VuoVideoPlayer::stopTimer(dispatch_source_t* timer, dispatch_semaphore_t* semaphore)
545 dispatch_source_cancel(*timer);
546 dispatch_semaphore_wait(*semaphore, DISPATCH_TIME_FOREVER);
547 dispatch_release(*timer);
556 void VuoVideoPlayer::sendVideoFrame()
558 if(videoFrame.image != NULL)
562 lastSentVideoTime = dispatch_time(DISPATCH_TIME_NOW, 0);
567 videoFrame.image = NULL;
570 double nextFrameEvent = 0;
575 double presentationRelativeTimestamp = ((videoFrame.timestamp - timestampStart) / playbackRate);
576 lastVideoFrameDelta = videoFrame.timestamp - lastVideoTimestamp;
577 lastVideoTimestamp = videoFrame.timestamp;
578 nextFrameEvent = NSEC_PER_SEC * presentationRelativeTimestamp;
582 videoFrame.image = NULL;
589 pthread_mutex_lock(&decoderMutex);
593 if (playbackRate < 0 && timestampStart == 0)
603 case VuoLoopType_None:
608 case VuoLoopType_Loop:
610 stopTimer(&audio_timer, &audio_semaphore);
613 playbackRate = userSetPlaybackRate;
617 playbackStart = dispatch_time(DISPATCH_TIME_NOW, 0);
619 lastVideoTimestamp = timestampStart;
621 startTimer(&audio_timer, dispatch_time(DISPATCH_TIME_NOW, 0), &audio_semaphore, &VuoVideoPlayer::sendAudioFrame);
624 case VuoLoopType_Mirror:
625 stopTimer(&audio_timer, &audio_semaphore);
626 playbackRate = -playbackRate;
629 playbackStart = dispatch_time(DISPATCH_TIME_NOW, 0);
631 lastVideoTimestamp = timestampStart;
634 startTimer(&audio_timer, dispatch_time(DISPATCH_TIME_NOW, 0), &audio_semaphore, &VuoVideoPlayer::sendAudioFrame);
638 pthread_mutex_unlock(&decoderMutex);
641 if(isPlaying && fabs(playbackRate) > .0001)
644 dispatch_source_set_timer(
646 dispatch_time(playbackStart, nextFrameEvent),
647 DISPATCH_TIME_FOREVER,
652 void VuoVideoPlayer::sendAudioFrame()
660 audioFrame.channels = NULL;
668 double presentationRelativeTimestamp = ((audioFrame.timestamp - timestampStart) / playbackRate);
669 lastAudioTimestamp = audioFrame.timestamp;
671 dispatch_source_set_timer(
673 dispatch_time(playbackStart, NSEC_PER_SEC * presentationRelativeTimestamp),
674 DISPATCH_TIME_FOREVER,
680 audioFrame.channels = NULL;