Кодирование ProRes на M1 Max не удается для буферов с высокой разрядностью

#macos #video-processing #apple-m1 #apple-silicon

Вопрос:

У меня есть код, который много лет работал для написания файлов ProRes, и теперь он выходит из строя на новом MacBook M1 Max. В частности, если я создам буферы с типом пикселя « kCVPixelFormatType_64ARGB «, после нескольких кадров записи операция добавления завершится ошибкой -12905 . Это указывает на то, что система записи не смогла выполнить преобразование для этого типа буфера, что является очень странным и неожиданным поведением. Опять же, этот код отлично работает на процессорах, отличных от Max (изначально Intel и base 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:amp;error];
    
    // Set up the writer
    AVAssetWriter *trackWriter =
        [[AVAssetWriter alloc] initWithURL:url
                                  fileType:AVFileTypeQuickTimeMovie
                                     error:amp;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,
                                   amp;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 , он отлично работает на моем MacBook Pro с M1 Max. Опять же, исходный код отлично работает на любом другом компьютере.