M1 Max设备kCVPixelFormatType_64ARGB格式ProRes编码失败问询
问题描述
有一套已稳定运行多年的ProRes文件写入代码,在新款M1 Max MacBook上运行时出现异常:使用像素类型kCVPixelFormatType_64ARGB构造缓冲区时,写入数帧后追加操作失败,返回错误码-12905,提示写入系统无法完成该缓冲区类型的格式转换。该代码在英特尔芯片、基础款M1芯片原生运行环境下均可正常运行。
可复现问题的示例代码
// // main.m // TestProresWriting // #import <Foundation/Foundation.h> #import <AVFoundation/AVFoundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { int timescale = 24; int width = 1920; int height = 1080; NSURL *url = [NSURL fileURLWithPath:@"/Users/diftil/TempData/testfile.mov"]; NSLog(@"Output file = %@", [url absoluteURL]); NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error = nil; [fileManager removeItemAtURL:url error:&error]; // Set up the writer AVAssetWriter *trackWriter = [[AVAssetWriter alloc] initWithURL:url fileType:AVFileTypeQuickTimeMovie error:&error]; // Set up the track NSDictionary *videoSettings = @{AVVideoCodecKey: AVVideoCodecTypeAppleProRes4444, AVVideoWidthKey: @(width), AVVideoHeightKey: @(height)}; AVAssetWriterInput *track = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings]; // Set up the adapter // This pixel type causes problems on M1 Max NSDictionary *attributes = @{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_64ARGB), (id)kCVPixelBufferWidthKey: @(width), (id)kCVPixelBufferHeightKey: @(height)}; // This pixel type works on M1 Max // NSDictionary *attributes = // @{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32ARGB), // (id)kCVPixelBufferWidthKey: @(width), // (id)kCVPixelBufferHeightKey: @(height)}; AVAssetWriterInputPixelBufferAdaptor *pixelBufferAdaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:track sourcePixelBufferAttributes:attributes]; // Add the track and start writing [trackWriter addInput:track]; [trackWriter startWriting]; CMTime startTime = CMTimeMake(0, timescale); [trackWriter startSessionAtSourceTime:startTime]; while(!track.readyForMoreMediaData); CVPixelBufferRef frameBuffer = NULL; for (int i = 0; i < 100; i++) { NSLog(@"Frame %d", i); CVPixelBufferPoolRef PixelBufferPool = pixelBufferAdaptor.pixelBufferPool; if (PixelBufferPool == nil) { NSLog(@"PixelBufferPool is invalid."); exit(1); } CVReturn ret = CVPixelBufferPoolCreatePixelBuffer(nil, PixelBufferPool, &frameBuffer); if (ret != kCVReturnSuccess) { NSLog(@"Error creating framebuffer from pool"); exit(1); } CVPixelBufferLockBaseAddress(frameBuffer, 0); void *imagebuf = CVPixelBufferGetBaseAddress(frameBuffer); size_t bytesPerRow = CVPixelBufferGetBytesPerRow(frameBuffer); memset(imagebuf, 0x00, bytesPerRow * height); CVPixelBufferUnlockBaseAddress(frameBuffer, 0); while(!track.readyForMoreMediaData); CMTime presentationTime = CMTimeMake(i, timescale); BOOL result = [pixelBufferAdaptor appendPixelBuffer:frameBuffer withPresentationTime:presentationTime]; if (result == NO) { NSLog(@"Error appending to track."); NSError *error = trackWriter.error; if(error!=nil) { NSLog(@"Unresolved error %@,%@.", error, [error userInfo]); } exit(1); } CVPixelBufferRelease(frameBuffer); } // Close everything if ( trackWriter.status == AVAssetWriterStatusWriting) [track markAsFinished]; __block BOOL isOpen = YES; [trackWriter finishWritingWithCompletionHandler:^{ isOpen = NO; NSLog(@"Closed."); }]; while (isOpen); NSLog(@"Completed."); } return 0; }
运行报错信息
2021-11-13 14:11:37.032427-0700 TestProresWriting[56235:2066349] Output file = file:///Users/diftil/TempData/testfile.mov 2021-11-13 14:11:37.054946-0700 TestProresWriting[56235:2066349] Metal API Validation Enabled 2021-11-13 14:11:37.155398-0700 TestProresWriting[56235:2066349] Frame 0 2021-11-13 14:11:37.158434-0700 TestProresWriting[56235:2066349] Frame 1 2021-11-13 14:11:37.158607-0700 TestProresWriting[56235:2066349] Frame 2 2021-11-13 14:11:37.158715-0700 TestProresWriting[56235:2066349] Frame 3 2021-11-13 14:11:37.159803-0700 TestProresWriting[56235:2066349] Frame 4 2021-11-13 14:11:37.172890-0700 TestProresWriting[56235:2066349] Frame 5 2021-11-13 14:11:37.176622-0700 TestProresWriting[56235:2066349] Error appending to track. 2021-11-13 14:11:37.176723-0700 TestProresWriting[56235:2066349] Unresolved error Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSLocalizedFailureReason=An unknown error occurred (-12905), NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x10602c1e0 {Error Domain=NSOSStatusErrorDomain Code=-12905 "(null)"}},{ NSLocalizedDescription = "The operation could not be completed"; NSLocalizedFailureReason = "An unknown error occurred (-12905)"; NSUnderlyingError = "Error Domain=NSOSStatusErrorDomain Code=-12905 \"(null)\""; }. Program ended with exit code: 1
若将kCVPixelFormatType_64ARGB替换为kCVPixelFormatType_32ARGB,代码可在M1 Max设备上正常运行。
问题解答
你的代码没有配置错误,该问题是M1 Max/Pro系列芯片搭载的专用ProRes硬件编码器的兼容性缺陷导致的:基础款M1无专用ProRes硬件编码模块,英特尔芯片也走软件编码路径,二者都不会触发该问题,只有M1 Max/Pro调用硬件编码时对大端序的kCVPixelFormatType_64ARGB格式支持存在bug。
可根据业务需求选择以下解决方案:
- 方案一:强制使用软件编码,兼容原有逻辑。在初始化AVAssetWriter时添加禁用硬件加速的配置即可,修改代码如下:
NSDictionary *writerConfiguration = @{ AVAssetWriterEnableHardwareAcceleratedVideoEncoderKey: @NO }; AVAssetWriter *trackWriter = [[AVAssetWriter alloc] initWithURL:url fileType:AVFileTypeQuickTimeMovie outputSettings:writerConfiguration error:&error];
- 方案二:保留硬件编码性能,更换为硬件编码器支持的16位像素格式,比如小端序的
kCVPixelFormatType_64ARGBLE或者半精度浮点格式kCVPixelFormatType_64RGBAHalf,无需修改其他业务逻辑即可正常运行。 - 方案三:若业务不需要16位色深,直接改用测试验证过的
kCVPixelFormatType_32ARGB格式,可正常调用硬件编码,性能最优。
内容的提问来源于stack exchange,提问作者Tony
相关产品推荐
相关产品推荐

