Я пытаюсь воспроизвести видео в цикле на AVSampleBufferDisplayLayer. Я могу заставить его играть хотя бы раз без проблем. Но, когда я пытаюсь его зацикливать, он не продолжает играть.Looping Video в AVFoundation AVSampleBufferDisplayLayer
В ответ на AVFoundation to reproduce a video loop нет способа перемотать AVAssetReader, чтобы я его повторно создал. (Я видел ответ на Looping a video with AVFoundation AVPlayer?, но AVPlayer более полнофункциональный. Я читаю файл, но хочу, чтобы AVSampleBufferDisplayLayer по-прежнему.)
Одна из гипотез заключается в том, что мне нужно остановить некоторые заголовки H264, но я не знаю, поможет ли это (и как). Другое дело, что это имеет какое-то отношение к CMTimebase, но я пробовал несколько вещей безрезультатно.
код ниже, основанный на WWDC говорить от Apple на прямой доступ к видео Кодирование:
- (void)viewDidLoad {
[super viewDidLoad];
NSString *filepath = [[NSBundle mainBundle] pathForResource:@"sample-mp4" ofType:@"mp4"];
NSURL *fileURL = [NSURL fileURLWithPath:filepath];
AVAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
UIView *view = self.view;
self.videoLayer = [[AVSampleBufferDisplayLayer alloc] init];
self.videoLayer.bounds = view.bounds;
self.videoLayer.position = CGPointMake(CGRectGetMidX(view.bounds), CGRectGetMidY(view.bounds));
self.videoLayer.videoGravity = AVLayerVideoGravityResizeAspect;
self.videoLayer.backgroundColor = [[UIColor greenColor] CGColor];
CMTimebaseRef controlTimebase;
CMTimebaseCreateWithMasterClock(CFAllocatorGetDefault(), CMClockGetHostTimeClock(), &controlTimebase);
self.videoLayer.controlTimebase = controlTimebase;
CMTimebaseSetTime(self.videoLayer.controlTimebase, CMTimeMake(5, 1));
CMTimebaseSetRate(self.videoLayer.controlTimebase, 1.0);
[[view layer] addSublayer:_videoLayer];
dispatch_queue_t assetQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //??? right queue?
__block AVAssetReader *assetReaderVideo = [self createAssetReader:asset];
__block AVAssetReaderTrackOutput *outVideo = [assetReaderVideo outputs][0];
if([assetReaderVideo startReading])
{
[_videoLayer requestMediaDataWhenReadyOnQueue: assetQueue usingBlock: ^{
while([_videoLayer isReadyForMoreMediaData])
{
CMSampleBufferRef sampleVideo;
if (([assetReaderVideo status] == AVAssetReaderStatusReading) && (sampleVideo = [outVideo copyNextSampleBuffer])) {
[_videoLayer enqueueSampleBuffer:sampleVideo];
CFRelease(sampleVideo);
CMTimeShow(CMTimebaseGetTime(_videoLayer.controlTimebase));
}
else {
[_videoLayer stopRequestingMediaData];
//CMTimebaseSetTime(_videoLayer.controlTimebase, CMTimeMake(5, 1));
//CMTimebaseSetRate(self.videoLayer.controlTimebase, 1.0);
//CMTimeShow(CMTimebaseGetTime(_videoLayer.controlTimebase));
assetReaderVideo = [self createAssetReader:asset];
outVideo = [assetReaderVideo outputs][0];
[assetReaderVideo startReading];
//sampleVideo = [outVideo copyNextSampleBuffer];
//[_videoLayer enqueueSampleBuffer:sampleVideo];
}
}
}];
}
}
-(AVAssetReader *)createAssetReader:(AVAsset*)asset {
NSError *error=nil;
AVAssetReader *assetReaderVideo = [[AVAssetReader alloc] initWithAsset:asset error:&error];
NSArray *videoTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
AVAssetReaderTrackOutput *outVideo = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTracks[0] outputSettings:nil]; //dic];
[outVideo res]
[assetReaderVideo addOutput:outVideo];
return assetReaderVideo;
}
Спасибо так много.
У меня была такая же проблема сегодня. Я нашел частичное решение, выполнив следующие действия: вызывая [self.videoLayer flushAndRemoveImage] каждый цикл и увеличивая значение itemTime.epoch для каждого цикла. Он работает большую часть времени, но не всегда. – sbwilson
Вызов enqueueSampleBuffer: в главной очереди отправки, похоже, теперь он исправлен для меня. – sbwilson
Вот исходный код для рабочего решения, которое использует аппаратные кодировки и декодирования API, чтобы получить бесшовный цикл без AVAssetReader. https://stackoverflow.com/questions/33285826/looping-avplayer-seamlessly/33335884#33335884 – MoDJ