AVAssetWriterInputPixelBufferAdaptor возвращает пул буферов нулевых пикселей

Я уверен, что что-то не так с моими атрибутами буфера, но мне непонятно, что именно - плохо документировано, что там должно происходить, поэтому я предполагаю, основываясь на CVPixelBufferPoolCreate - а Core Foundation - это в значительной степени закрытая книга для меня.

    // "width" and "height" are const ints
    CFNumberRef cfWidth = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &width);
    CFNumberRef cfHeight = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &height);

    CFStringRef keys[] = {
        kCVPixelBufferWidthKey,
        kCVPixelBufferHeightKey,
        kCVPixelBufferCGImageCompatibilityKey
    };
    CFTypeRef values[] = {
        cfWidth,
        cfHeight,
        kCFBooleanTrue
    };
    int numValues = sizeof(keys) / sizeof(keys[0]);

    CFDictionaryRef bufferAttributes = CFDictionaryCreate(kCFAllocatorDefault, 
                                                          (const void **)&keys, 
                                                          (const void **)&values,
                                                          numValues,
                                                          &kCFTypeDictionaryKeyCallBacks,
                                                          &kCFTypeDictionaryValueCallBacks
                                                          );

    AVAssetWriterInputPixelBufferAdaptor *adaptor = [[AVAssetWriterInputPixelBufferAdaptor 
                                                      assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput
                                                      sourcePixelBufferAttributes:(NSDictionary*)bufferAttributes] retain];
    CVPixelBufferPoolRef bufferPool = adaptor.pixelBufferPool;
    NSParameterAssert(bufferPool != NULL); // fails

person David Moles    schedule 27.04.2011    source источник
comment
Привет, у меня такая же проблема, вы нашли решение?   -  person TheRonin    schedule 18.05.2011
comment
Не совсем. Я просто создаю буфер пикселей для каждого кадра вместо использования пула. :(   -  person David Moles    schedule 18.05.2011
comment
Хорошо, мы нашли такое же решение. Спасибо !   -  person TheRonin    schedule 19.05.2011
comment
Привет, @DavidMoles, вы нашли решение, или у вас есть рабочий код с созданием буфера пикселей для каждого кадра?   -  person Iraniya Naynesh    schedule 22.09.2019
comment
@IraniyaNaynesh Извините, я не смотрел на это много лет. Но в документации для pixelBufferPool теперь говорится: «Это свойство имеет значение NULL до того, как первый вызов startSessionAtTime: для связанного объекта AVAssetWriter ». Так, может быть, проблема была в этом?   -  person David Moles    schedule 22.09.2019


Ответы (6)


Когда pixelBufferPool возвращает null, проверьте следующее:

    1. the output file of the AVAssetsWriter doesn't exist.
    2. use the pixelbuffer after calling startSessionAtTime: on the AVAssetsWriter.
    3. the settings of AVAssetWriterInput and AVAssetWriterInputPixelBufferAdaptor are correct.
    4. the present times of appendPixelBuffer uses are not the same.
person roamer    schedule 21.11.2013
comment
№1 тоже помог мне. Такое простое решение! Я действительно смотрел слишком глубоко. Кстати, моя ошибка была, когда я вызвал CVPixelBufferPoolCreatePixelBuffer, он возвращал -6661. - person VaporwareWolf; 25.03.2016
comment
№1 сделал это и для меня. Спасибо! - person Trevor Alyn; 24.05.2016
comment
как вы, ребята, исправили это с номером один? Я быстро использую NSHomeDirectory. Может кто-нибудь выложить пример на github? - person rocky raccoon; 17.04.2017
comment
№1 и для меня. - person Rocket Garden; 31.08.2018
comment
Я не понимаю №1 - Почему файл должен существовать? Если я записываю новое видео, его может не быть. Мне никогда не приходилось сначала создавать файл ни в одном другом приложении, которое я сделал. --- ОБНОВЛЕНИЕ --- вы получите сообщение об ошибке, если файл ДЕЙСТВИТЕЛЬНО существует и вы пытаетесь перезаписать его. Попробуйте переименовать конечный файл. - person JCutting8; 14.06.2020

У меня была такая же проблема, и я думаю, что это, возможно, потому, что вы неправильно настроили свой AVAssetWriterInput. Мой бассейн заработал после того, как я это сделал. В частности, пул не предоставил бы мне пиксельные буферы, если я не предоставил данные в AVVideoCompressionPropertiesKey. Сначала создайте и полностью настройте AVAssetWriter (посмотрите в /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/AVFoundation.framework/Headers/AVVideoSettings.h ключи и значения для outputSettings и compressionSettings):

NSError * err = 0;
AVAssetWriter * outputWriter = [AVAssetWriter
    assetWriterWithURL: [NSURL fileURLWithPath:outputPath]
              fileType: AVFileTypeAppleM4V
                 error: & err];

NSMutableDictionary * outputSettings
    = [[NSMutableDictionary alloc] init];
[outputSettings setObject: AVVideoCodecH264
                   forKey: AVVideoCodecKey];
[outputSettings setObject: [NSNumber numberWithInt: width_]
                   forKey: AVVideoWidthKey];
[outputSettings setObject: [NSNumber numberWithInt: height_]
                   forKey: AVVideoHeightKey];

NSMutableDictionary * compressionProperties
    = [[NSMutableDictionary alloc] init];
[compressionProperties setObject: [NSNumber numberWithInt: 1000000]
                          forKey: AVVideoAverageBitRateKey];
[compressionProperties setObject: [NSNumber numberWithInt: 16]
                          forKey: AVVideoMaxKeyFrameIntervalKey];
[compressionProperties setObject: AVVideoProfileLevelH264Main31
                          forKey: AVVideoProfileLevelKey];

[outputSettings setObject: compressionProperties
                   forKey: AVVideoCompressionPropertiesKey];

AVAssetWriterInput * writerInput = [AVAssetWriterInput
    assetWriterInputWithMediaType: AVMediaTypeVideo
                   outputSettings: outputSettings];

[compressionProperties release];
[outputSettings release];

Создайте адаптер буфера пикселей:

NSMutableDictionary * pixBufSettings = [[NSMutableDictionary alloc] init];
[pixBufSettings setObject: [NSNumber numberWithInt: kCVPixelFormatType_32BGRA]
                   forKey: (NSString *) kCVPixelBufferPixelFormatTypeKey];
[pixBufSettings setObject: [NSNumber numberWithInt: width_]
                   forKey: (NSString *) kCVPixelBufferWidthKey];
[pixBufSettings setObject: [NSNumber numberWithInt: height_]
                   forKey: (NSString *) kCVPixelBufferHeightKey];

AVAssetWriterInputPixelBufferAdaptor * outputPBA =
    [AVAssetWriterInputPixelBufferAdaptor
    assetWriterInputPixelBufferAdaptorWithAssetWriterInput: outputInput
                               sourcePixelBufferAttributes: nil];

Затем извлеките буферы пикселей из своего пула, используя:

CVReturn res = CVPixelBufferPoolCreatePixelBuffer (NULL
    , [outputPBA pixelBufferPool]
    , & outputFrame);
person William Gallafent    schedule 27.05.2011
comment
вы не использовали pixBufSettings - person Or Arbel; 14.06.2012
comment
но если вы передадите nil в sourcePixelBufferAttributes, нет никаких шансов, что адаптер вообще создаст какой-либо буфер пикселей - person HepaKKes; 14.05.2014

По документации:

«Это свойство имеет значение NULL перед первым вызовом startSessionAtTime: для связанного объекта AVAssetWriter».

Поэтому, если вы пытаетесь получить доступ к пулу слишком рано, он будет NULL. Я только сам изучаю этот материал, поэтому в данный момент не могу подробно остановиться.

person Bored Astronaut    schedule 20.06.2011
comment
что значит слишком рано ?? Могу заверить вас, что я вызываю CVPixelBufferPoolCreatePixelBuffer (kCFAllocatorDefault, _pixelBufferAdaptor.pixelBufferPool и pixelBuffer) после вызова методов, которые должны быть вызваны в assetWriter, и, тем не менее, он продолжает возвращать ошибку, по-видимому, пул буферов не был создан - person HepaKKes; 14.05.2014

Для всех, кто все еще ищет решение: во-первых, убедитесь, что ваш AVAssetWriter работает правильно, проверив его статус. У меня была эта проблема, и после проверки статуса, хотя я вызвал start some what, писатель просто еще не начал. (В моем случае я указал путь записи к существующему файлу, поэтому после удаления это работает как шарм)

person MatthewLuiHK    schedule 15.08.2015

У меня все заработало! Когда словарь параметров установлен на совместимость, они говорят, что можно использовать пул буферов. Вот рабочие образцы и код для записи без буфера, но это хорошее место для начала.

Вот пример кода ссылка

Вот код, который вам нужен:

- (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;
}
person Orbitus007    schedule 01.06.2011
comment
Не могли бы вы объяснить, какой pixelBuffer предполагается использовать в этом решении. - person HepaKKes; 14.05.2014
comment
Apple должна уволить весь отдел, который пишет их ужасные, ужасающие, ужасные и отвратительные неполные, расплывчатые, вводящие в заблуждение документы. Они позор. - person Duck; 18.12.2016

Это работает, когда нет файла в outputURL для AVAssetWriter.

extension FileManager {
    func removeItemIfExist(at url: URL) {
        do {
            if FileManager.default.fileExists(atPath: url.path) {
                try FileManager.default.removeItem(at: url)
            }
        } catch {
            fatalError("\(error)")
        }
    }
}

использование

let assetWriter = try? AVAssetWriter(outputURL: outputURL, fileType: .mov)
FileManager.default.removeItemIfExist(at: outputURL)
// do something
person Changnam Hong    schedule 09.08.2018