AVAssetWriterInputPixelBufferAdaptor restituisce null pool buffer pixel
-
25-10-2019 - |
Domanda
mi sbaglio sicuro di qualcosa con i miei attributi di buffer, ma non è chiaro per me che cosa - non è ben documentato quello che dovrebbe andare lì, quindi sto cercando di indovinare basa sulla CVPixelBufferPoolCreate
- e Core Foundation è praticamente un libro chiuso per me.
// "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
Soluzione
Quando il pixelBufferPool restituisce null, controllare quanto segue:
- 1. il file di output del AVAssetsWriter non esiste.
- 2. utilizzare il pixelbuffer dopo aver chiamato startSessionAtTime:. Sulla AVAssetsWriter
- 3. le impostazioni del AVAssetWriterInput e AVAssetWriterInputPixelBufferAdaptor siano corrette.
- 4. attuali tempi di usi appendPixelBuffer non sono gli stessi.
Altri suggerimenti
Ho avuto lo stesso problema, e penso che è forse perché non avete configurato correttamente il tuo AVAssetWriterInput
. La mia piscina ha iniziato a lavorare dopo che avevo fatto questo. In particolare, la piscina non mi avrebbe dato i buffer di pixel a meno che non avevo fornito dati in AVVideoCompressionPropertiesKey
. In primo luogo, creare e configurare pienamente il AVAssetWriter
(
Cerca in /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/AVFoundation.framework/Headers/AVVideoSettings.h
per le chiavi e valori per outputSettings
e 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];
Crea l'adattatore buffer di pixel:
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];
quindi recuperare i buffer di pixel dalla sua piscina utilizzando:
CVReturn res = CVPixelBufferPoolCreatePixelBuffer (NULL
, [outputPBA pixelBufferPool]
, & outputFrame);
Secondo la documentazione:
"Questa struttura è NULL prima della prima chiamata a startSessionAtTime:. Sull'oggetto AVAssetWriter associato"
Quindi, se you'e cercando di accedere alla piscina troppo presto, sarà NULL. Sto imparando questa roba me stesso, quindi non posso davvero approfondire in questo momento.
Per ogni uno ancora cercando la soluzione: In primo luogo, assicurarsi che l'AVAssetWriter funzioni correttamente controllando il suo stato. Ho avuto questo problema e previa verifica dello stato, anche se ho chiamata'VE metto qualche cosa, lo scrittore proprio non hanno ancora iniziato. (Nel mio caso, ho puntare il percorso di scrittura di un file esistente, così dopo l'eliminazione essa, funziona come un fascino)
Ho tutto lavoro! Con le opzioni di set dizionario alla compatibilità, dicono il suo possibile utilizzare il pool di buffer, ecco campioni e codice di lavoro per la scrittura, senza il buffer, ma è un buon punto di partenza.
Ecco il codice di esempio link
Ecco il codice necessario:
- (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;
}
Funziona quando non c'è alcun file a outputURL
per 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)")
}
}
}
Uso
let assetWriter = try? AVAssetWriter(outputURL: outputURL, fileType: .mov)
FileManager.default.removeItemIfExist(at: outputURL)
// do something