Reputation: 62323
I am trying to start a new video with no delay as soon as the first video ends. I tried using the AVQueuePlayer but there is some delay when the first video ends and the second starts.
I need this to be sample synchronous. ie if video 1 is 10.25 seconds long then when I have been playing for 11.5 seconds video 2 should be 1.25 seconds in.
I have been experimenting with keeping 3 AVPlayer objects where I preRoll each player using the same master clock. I am then trying to intercept the AVPlayerItemDidPlayToEndTimeNotification
and then calling setRate:time:atHostTime:
to start the next video going. However I'm not getting any video playing.
I pre-roll my videos using the following code:
- (void) preRollPlayer: (AVPlayer*) pPlayer withMasterClock: (CMClockRef) masterClock atTime: (NSTimeInterval) time
{
[pPlayer setRate: 0.0f];
[pPlayer play];
[pPlayer seekToTime: CMTimeMakeWithSeconds( time, 1000000 ) toleranceBefore: kCMTimeZero toleranceAfter: kCMTimeZero];
[pPlayer setMasterClock: masterClock];
__block volatile int32_t completed = 0;
[pPlayer prerollAtRate: 1.0f completionHandler: ^( BOOL finished )
{
OSAtomicIncrement32( &completed );
}];
while( completed == 0 )
{
[NSThread sleepForTimeInterval: 0.01];
}
}
I then call preRoll as follows:
if ( pAudioVideoEntry.beforeVideoReader )
{
[self preRollPlayer: pAudioVideoEntry.beforeVideoReader.player withMasterClock: hostClock atTime: currentTime];
}
{
[self preRollPlayer: pAudioVideoEntry.videoReader.player withMasterClock: hostClock atTime: currentTime];
}
if ( pAudioVideoEntry.afterVideoReader )
{
[self preRollPlayer: pAudioVideoEntry.afterVideoReader.player withMasterClock: hostClock atTime: currentTime];
}
I finally start the video as follows:
[pAudioVideoEntry.beforeVideoReader.player setRate: 1.0f time: kCMTimeZero atHostTime: syncTime];
pAudioVideoEntry.playingPlayer = pAudioVideoEntry.beforeVideoReader.player;
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector( beforeVideoEnded: )
name: AVPlayerItemDidPlayToEndTimeNotification
object: pAudioVideoEntry.beforeVideoReader.playerItem];
What is odd is that I do not hear the audio, the notification is never sent and every call to my AVPlayerItem
's output AVPlayerItemVideoOutput
's hasNewPixelBufferForItemTime
returns false. So I'm guessing the video has not started. Any ideas why?
Upvotes: 2
Views: 573
Reputation: 62323
So in classic style around an hour after I post this (having spent most of the day battling over it) I find a simple solution here: https://stackoverflow.com/a/11337690/131140
Using an AVMutableComposition
I can add each video in sequence into the composition and I get perfect playback:
// Fill in the assets that make up the composition
CMTime time = kCMTimeZero;
for( AVAsset* pThisAsset in pAssets )
{
CMTimeRange timeRange = CMTimeRangeMake( kCMTimeZero, pThisAsset.duration );
[pComposition insertTimeRange: timeRange ofAsset: pThisAsset atTime: time error: nil];
time = CMTimeAdd( time, pThisAsset.duration );
}
I'm having a slight bug with the video images playing back out of sync with the audio but the audio is performing perfectly. Massively simplified my code as well!
Upvotes: 1