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 delete player->decoder;
65 player->decoder = alt;
67 if(player->preferFfmpeg)
68 VDebugLog(
"Using AvFoundation video decoder despite optimization preference.");
70 VDebugLog(
"Using FFmpeg video decoder despite optimization preference.");
72 else if (!player->decoder && !alt)
74 VUserLog(
"AVFoundation and FFmpeg video decoders failed to open the movie.");
75 player->failedLoading =
true;
80 if(player->preferFfmpeg)
81 VDebugLog(
"Using first choice video decoder FFmpeg");
83 VDebugLog(
"Using first choice video decoder AVFoundation");
86 player->isReady = player->decoder != NULL && player->decoder->
IsReady();
88 player->audioFrame.channels = NULL;
89 player->videoFrame.image = NULL;
91 if(player->decoder == NULL)
99 player->decoder->
onReadyToPlay = &VuoVideoPlayer::OnDecoderPlaybackReady;
108 while( !
IsReady() && !failedLoading ) {
109 usleep(USEC_PER_SEC / 24);
115 VuoVideoPlayer::~VuoVideoPlayer()
117 pthread_mutex_lock(&decoderMutex);
123 stopTimer(&video_timer, &video_semaphore);
124 dispatch_release(video_semaphore);
127 stopTimer(&audio_timer, &audio_semaphore);
128 dispatch_release(audio_semaphore);
138 pthread_mutex_unlock(&decoderMutex);
140 int ret = pthread_mutex_destroy(&decoderMutex);
142 VUserLog(
"Error: Couldn't destroy decoder mutex: %s", strerror(ret));
150 void VuoVideoPlayer::OnDecoderPlaybackReady(
bool canPlayMedia)
152 pthread_mutex_lock(&decoderMutex);
171 _SetPlaybackRate(onReadyPlaybackRate);
174 if (!_Seek(onReadySeek))
178 _Play( dispatch_time(DISPATCH_TIME_NOW, 0) );
195 _SetPlaybackRate(onReadyPlaybackRate);
198 if (!_Seek(onReadySeek))
202 _Play(dispatch_time(DISPATCH_TIME_NOW, 0));
206 VUserLog(
"On second thought, AVFoundation decided it couldn't play this video. Fell back on FFmpeg successfully.");
210 VUserLog(
"Both AvFoundation and FFmpeg failed to load this video. Try using a different video or audio codec.");
211 failedLoading =
true;
216 VUserLog(
"Both AvFoundation and FFmpeg failed to load this video. Try using a different video or audio codec.");
217 failedLoading =
true;
221 pthread_mutex_unlock(&decoderMutex);
226 pthread_mutex_lock(&decoderMutex);
234 pthread_mutex_unlock(&decoderMutex);
239 _Play( dispatch_time(DISPATCH_TIME_NOW, 0) );
242 pthread_mutex_unlock(&decoderMutex);
247 pthread_mutex_lock(&decoderMutex);
252 pthread_mutex_unlock(&decoderMutex);
257 pthread_mutex_lock(&decoderMutex);
259 userSetPlaybackRate = rate;
262 onReadyPlaybackRate = rate;
264 _SetPlaybackRate(rate);
266 pthread_mutex_unlock(&decoderMutex);
271 pthread_mutex_lock(&decoderMutex);
273 bool success =
false;
276 onReadySeek = second;
278 success = _Seek(second);
280 pthread_mutex_unlock(&decoderMutex);
285 void VuoVideoPlayer::_Play(dispatch_time_t start)
288 playbackStart = start;
290 lastVideoTimestamp = timestampStart;
292 startTimer(&video_timer, start, &video_semaphore, &VuoVideoPlayer::sendVideoFrame);
293 startTimer(&audio_timer, start, &audio_semaphore, &VuoVideoPlayer::sendAudioFrame);
296 void VuoVideoPlayer::_Pause()
300 if(video_timer != NULL)
301 stopTimer(&video_timer, &video_semaphore);
303 if(audio_timer != NULL)
304 stopTimer(&audio_timer, &audio_semaphore);
307 void VuoVideoPlayer::_SetPlaybackRate(
double rate)
309 bool wasPlaying = isPlaying;
314 playbackRate = rate < 0 ? fmin(rate, -.00001) : fmax(rate, .00001);
321 if(lastSentVideoTime != DISPATCH_TIME_FOREVER)
323 int64_t frame_delta = ((1./decoder->
GetFrameRate()) / playbackRate) * NSEC_PER_SEC;
324 dispatch_time_t elapsed =
MIN( dispatch_time(DISPATCH_TIME_NOW, 0) - lastSentVideoTime, frame_delta );
325 dispatch_time_t timer = dispatch_time(DISPATCH_TIME_NOW, frame_delta);
326 _Play(timer - elapsed);
330 _Play(dispatch_time(DISPATCH_TIME_NOW, 0));
335 bool VuoVideoPlayer::_Seek(
double second)
337 bool wasPlaying = isPlaying;
344 lastSentVideoTime = DISPATCH_TIME_FOREVER;
345 _Play( dispatch_time(DISPATCH_TIME_NOW, 0) );
366 return lastVideoFrameDelta;
371 return lastVideoTimestamp;
379 while(!
IsReady() && !failedLoading)
381 usleep(USEC_PER_SEC / 24);
384 pthread_mutex_lock(&decoderMutex);
386 pthread_mutex_unlock(&decoderMutex);
393 unsigned int channelCount = 0;
395 while(!
IsReady() && !failedLoading)
397 usleep(USEC_PER_SEC / 24);
400 pthread_mutex_lock(&decoderMutex);
402 pthread_mutex_unlock(&decoderMutex);
409 const int MAX_FRAMES_TO_STEP_BEFORE_SEEK = 5;
410 const double floatingPointError = 0.0001;
411 double averageFrameDuration = 1. / decoder->
GetFrameRate();
413 double target = fmax(0, fmin(duration, second));
416 if(videoFrame.image != NULL)
418 double frameDelta = fabs(target - videoFrame.timestamp);
421 if ( ((videoFrame.timestamp < target + floatingPointError) && (target + floatingPointError < videoFrame.timestamp + videoFrame.duration))
430 else if( frameDelta < averageFrameDuration * MAX_FRAMES_TO_STEP_BEFORE_SEEK && (target - videoFrame.timestamp > 0 == playbackRate > 0) )
432 for(
int i = 0; i < MAX_FRAMES_TO_STEP_BEFORE_SEEK; i++)
442 if (videoFrame.timestamp + videoFrame.duration > target + floatingPointError)
453 bool seekSuccess =
false;
457 if(target > duration - .5 && playbackRate > 0)
459 seekSuccess =
Seek(duration - .5);
461 while (videoFrame.timestamp + videoFrame.duration < target + floatingPointError)
479 seekSuccess =
Seek(target);
490 if(videoFrame.image != NULL)
514 void VuoVideoPlayer::startTimer(dispatch_source_t* timer, dispatch_time_t start, dispatch_semaphore_t* semaphore,
void (
VuoVideoPlayer::*func)(
void))
516 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
518 *timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, DISPATCH_TIMER_STRICT, queue);
520 dispatch_source_set_timer(
523 DISPATCH_TIME_FOREVER,
526 dispatch_source_set_event_handler(*timer, ^{ ((*this).*func)(); });
527 dispatch_source_set_cancel_handler(*timer, ^{ dispatch_semaphore_signal(*semaphore); });
529 dispatch_resume(*timer);
532 void VuoVideoPlayer::stopTimer(dispatch_source_t* timer, dispatch_semaphore_t* semaphore)
534 dispatch_source_cancel(*timer);
535 dispatch_semaphore_wait(*semaphore, DISPATCH_TIME_FOREVER);
536 dispatch_release(*timer);
545 void VuoVideoPlayer::sendVideoFrame()
547 if(videoFrame.image != NULL)
551 lastSentVideoTime = dispatch_time(DISPATCH_TIME_NOW, 0);
556 videoFrame.image = NULL;
559 double nextFrameEvent = 0;
564 double presentationRelativeTimestamp = ((videoFrame.timestamp - timestampStart) / playbackRate);
565 lastVideoFrameDelta = videoFrame.timestamp - lastVideoTimestamp;
566 lastVideoTimestamp = videoFrame.timestamp;
567 nextFrameEvent = NSEC_PER_SEC * presentationRelativeTimestamp;
571 videoFrame.image = NULL;
578 pthread_mutex_lock(&decoderMutex);
582 if (playbackRate < 0 && timestampStart == 0)
592 case VuoLoopType_None:
597 case VuoLoopType_Loop:
599 stopTimer(&audio_timer, &audio_semaphore);
602 playbackRate = userSetPlaybackRate;
606 playbackStart = dispatch_time(DISPATCH_TIME_NOW, 0);
608 lastVideoTimestamp = timestampStart;
610 startTimer(&audio_timer, dispatch_time(DISPATCH_TIME_NOW, 0), &audio_semaphore, &VuoVideoPlayer::sendAudioFrame);
613 case VuoLoopType_Mirror:
614 stopTimer(&audio_timer, &audio_semaphore);
615 playbackRate = -playbackRate;
618 playbackStart = dispatch_time(DISPATCH_TIME_NOW, 0);
620 lastVideoTimestamp = timestampStart;
623 startTimer(&audio_timer, dispatch_time(DISPATCH_TIME_NOW, 0), &audio_semaphore, &VuoVideoPlayer::sendAudioFrame);
627 pthread_mutex_unlock(&decoderMutex);
630 if(isPlaying && fabs(playbackRate) > .0001)
633 dispatch_source_set_timer(
635 dispatch_time(playbackStart, nextFrameEvent),
636 DISPATCH_TIME_FOREVER,
641 void VuoVideoPlayer::sendAudioFrame()
649 audioFrame.channels = NULL;
657 double presentationRelativeTimestamp = ((audioFrame.timestamp - timestampStart) / playbackRate);
658 lastAudioTimestamp = audioFrame.timestamp;
660 dispatch_source_set_timer(
662 dispatch_time(playbackStart, NSEC_PER_SEC * presentationRelativeTimestamp),
663 DISPATCH_TIME_FOREVER,
669 audioFrame.channels = NULL;