AVAssetWriterInputPixelBufferAdaptor и CMTime
Я пишу некоторые кадры для видео с AVAssetWriterInputPixelBufferAdaptor
и поведение во времени не то, что я ожидал.
Если я напишу только один кадр:
[videoWriter startSessionAtSourceTime:kCMTimeZero];
[adaptor appendPixelBuffer:pxBuffer withPresentationTime:kCMTimeZero];
это дает мне видео нулевой длины, чего я и ожидаю.
Но если я продолжу добавлять второй кадр:
// 3000/600 = 5 sec, right?
CMTime nextFrame = CMTimeMake(3000, 600);
[adaptor appendPixelBuffer:pxBuffer withPresentationTime:nextFrame];
Я получаю десять секунд видео, где я ожидаю пять.
Что тут происходит? Есть ли withPresentationTime
каким-то образом установить как начало кадра, так и длительность?
Обратите внимание, что я не звоню endSessionAtSourceTime
, просто finishWriting
,
3 ответа
Попробуйте взглянуть на этот пример и перепроектировать, чтобы добавить 1 кадр через 5 секунд...
Вот пример ссылки на код: git@github.com:RudyAramayo/AVAssetWriterInputPixelBufferAdaptorSample.git
Вот код, который вам нужен:
- (void) testCompressionSession
{
CGSize size = CGSizeMake(480, 320);
NSString *betaCompressionDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Movie.m4v"];
NSError *error = nil;
unlink([betaCompressionDirectory UTF8String]);
//----initialize compression engine
AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:betaCompressionDirectory]
fileType:AVFileTypeQuickTimeMovie
error:&error];
NSParameterAssert(videoWriter);
if(error)
NSLog(@"error = %@", [error localizedDescription]);
NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecH264, AVVideoCodecKey,
[NSNumber numberWithInt:size.width], AVVideoWidthKey,
[NSNumber numberWithInt:size.height], AVVideoHeightKey, nil];
AVAssetWriterInput *writerInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
NSDictionary *sourcePixelBufferAttributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, nil];
AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput
sourcePixelBufferAttributes:sourcePixelBufferAttributesDictionary];
NSParameterAssert(writerInput);
NSParameterAssert([videoWriter canAddInput:writerInput]);
if ([videoWriter canAddInput:writerInput])
NSLog(@"I can add this input");
else
NSLog(@"i can't add this input");
[videoWriter addInput:writerInput];
[videoWriter startWriting];
[videoWriter startSessionAtSourceTime:kCMTimeZero];
//---
// insert demo debugging code to write the same image repeated as a movie
CGImageRef theImage = [[UIImage imageNamed:@"Lotus.png"] CGImage];
dispatch_queue_t dispatchQueue = dispatch_queue_create("mediaInputQueue", NULL);
int __block frame = 0;
[writerInput requestMediaDataWhenReadyOnQueue:dispatchQueue usingBlock:^{
while ([writerInput isReadyForMoreMediaData])
{
if(++frame >= 120)
{
[writerInput markAsFinished];
[videoWriter finishWriting];
[videoWriter release];
break;
}
CVPixelBufferRef buffer = (CVPixelBufferRef)[self pixelBufferFromCGImage:theImage size:size];
if (buffer)
{
if(![adaptor appendPixelBuffer:buffer withPresentationTime:CMTimeMake(frame, 20)])
NSLog(@"FAIL");
else
NSLog(@"Success:%d", frame);
CFRelease(buffer);
}
}
}];
NSLog(@"outside for loop");
}
- (CVPixelBufferRef )pixelBufferFromCGImage:(CGImageRef)image size:(CGSize)size
{
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
[NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil];
CVPixelBufferRef pxbuffer = NULL;
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, size.width, size.height, kCVPixelFormatType_32ARGB, (CFDictionaryRef) options, &pxbuffer);
// CVReturn status = CVPixelBufferPoolCreatePixelBuffer(NULL, adaptor.pixelBufferPool, &pxbuffer);
NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
CVPixelBufferLockBaseAddress(pxbuffer, 0);
void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
NSParameterAssert(pxdata != NULL);
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pxdata, size.width, size.height, 8, 4*size.width, rgbColorSpace, kCGImageAlphaPremultipliedFirst);
NSParameterAssert(context);
CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);
CGColorSpaceRelease(rgbColorSpace);
CGContextRelease(context);
CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
return pxbuffer;
}
Вы пытались использовать это в качестве первого звонка
CMTime t = CMTimeMake(0, 600);
[videoWriter startSessionAtSourceTime:t];
[adaptor appendPixelBuffer:pxBuffer withPresentationTime:t];
Согласно документации г.
-[AVAssetWriterInput appendSampleBuffer:]
метод:
Для типов дорожек, отличных от звуковых дорожек, для определения длительности всех сэмплов в выходном файле, кроме самого последнего добавленного семпла, будет использоваться разница между выходным DTS буфера семплов и выходным DTS следующего буфера семплов. Продолжительность последней выборки определяется следующим образом:
- Если буфер образца маркера с kCMSampleBufferAttachmentKey_EndsPreviousSampleDuration добавляется после последнего образца носителя носителя, будет использоваться разница между выходным DTS буфера образца маркера и выходным DTS последнего образца носителя носителя.
- Если буфер выборки маркера не предоставлен и если продолжительность вывода последней выборки, несущей носитель, действительна, она будет использоваться.
- если продолжительность вывода последней выборки, несущей носитель, недействительна, будет использоваться продолжительность предпоследней выборки.
Итак, в основном вы находитесь в ситуации № 3:
- Продолжительность первой выборки составляет , исходя из разницы PTS между первой и второй выборкой.
- Длительность второй выборки
5s
тоже, потому чтоthe duration of the second-to-last sample
было использовано