AVAssetWriterInputPixelBufferAdaptor renvoie pool de mémoire tampon de pixel null
-
25-10-2019 - |
Question
Je suis le mal de quelque chose que mes attributs tampons, mais ce n'est pas clair pour moi ce que - ce n'est pas bien documenté ce qui est censé y aller, donc je suppose basé sur CVPixelBufferPoolCreate
- et Core Foundation est à peu près une livre fermé pour moi.
// "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
La solution
Lorsque le pixelBufferPool renvoie null, vérifier ce qui suit:
- 1. le fichier de sortie du AVAssetsWriter n'existe pas.
- 2. utiliser le pixelbuffer après avoir appelé startSessionAtTime:. Sur la AVAssetsWriter
- 3. les paramètres de AVAssetWriterInput et AVAssetWriterInputPixelBufferAdaptor sont corrects.
- 4. le temps présent des utilisations appendPixelBuffer ne sont pas les mêmes.
Autres conseils
J'ai eu le même problème, et je pense qu'il est peut-être parce que vous ne l'avez pas configuré votre AVAssetWriterInput
correctement. Ma piscine a commencé à travailler après avoir fait cela. En particulier, la piscine ne serait pas me donner des tampons de pixels à moins d'avoir fourni des données dans AVVideoCompressionPropertiesKey
. Tout d'abord, créer et configurer complètement le AVAssetWriter
(
Rechercher dans /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/AVFoundation.framework/Headers/AVVideoSettings.h
pour les clés et valeurs pour outputSettings
et 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];
créer la carte de mémoire tampon de 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];
Ensuite, récupérer des tampons de pixels de son pool en utilisant:
CVReturn res = CVPixelBufferPoolCreatePixelBuffer (NULL
, [outputPBA pixelBufferPool]
, & outputFrame);
Selon la documentation:
« Cette propriété est NULL avant le premier appel à startSessionAtTime. Sur l'objet AVAssetWriter associé »
Donc, si you'e tente d'accéder à la piscine trop tôt, il sera NULL. Je suis en train d'apprendre ce genre de choses moi-même donc je ne peux pas vraiment expliquer le moment.
Pour chaque toujours à la recherche de la solution: Tout d'abord, assurez-vous que votre AVAssetWriter fonctionne correctement en vérifiant le statut de lui. J'ai eu ce problème et après avoir vérifié l'état, même si je commence appel AVONS ce que certains, l'écrivain tout simplement pas encore commencé. (Dans mon cas, j'ai pointer le chemin d'écriture à un fichier existant, donc après la suppression , il fonctionne comme un charme)
Je suis tout travail! Avec les options de réglage du dictionnaire pour la compatibilité, ils disent que possible d'utiliser le pool de mémoire tampon, voici des échantillons de travail et le code pour l'écriture sans le tampon, mais un bon endroit pour commencer.
Voici le code dont vous avez besoin:
- (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;
}
Il fonctionne quand il n'y a pas de fichier à outputURL
pour 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)")
}
}
}
Utilisation
let assetWriter = try? AVAssetWriter(outputURL: outputURL, fileType: .mov)
FileManager.default.removeItemIfExist(at: outputURL)
// do something