AVAssetReader claims media damaged on iPhone Xs

Originator:cellsworth
Number:rdar://46460721 Date Originated:2018-12-04
Status:Closed Resolved:2019-08-12
Product:AVFoundation Product Version:iOS 12.1
Classification:Serious Bug Reproducible:Always
 
Summary:

We have a growing set of videos that we are unable to run through an AVAssetReader on iPhone Xs (+Max) running iOS 12.1 by doing a simple sample buffer copy. There seems to be something "interesting" about the encoding that makes it incompatible with these devices. The error is:

"Error Domain=AVFoundationErrorDomain Code=-11829 "Cannot Open" UserInfo={NSLocalizedFailureReason=This media may be damaged., NSLocalizedDescription=Cannot Open, NSUnderlyingError=0x2831006f0 {Error Domain=NSOSStatusErrorDomain Code=-12137 "(null)"}}"

The test passes on an iPhone 6 / iOS 11.4 and iPhone 8 / iOS 12.1.

If I re-encode these videos with the following command, they are able to be processed on the iPhone Xs:

ffmpeg -i in.mp4 -c:v libx264 out.mp4

Steps to Reproduce:

Run the attached test.

Expected Results:

The test passes.

Actual Results:

The test fails.

Version/Build:

iOS 12.1

Configuration:


--------

- (void)testiPhoneXs {
    NSURL *const outputUrl = [[[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]] URLByAppendingPathExtension:@"mp4"];
    NSURL *const url = [[NSBundle bundleForClass:[self class]] URLForResource:@"iPhoneXsBug" withExtension:@"mp4"];
    AVAsset *const asset = [AVAsset assetWithURL:url];
    AVAssetTrack *const videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
    NSError *error = nil;
    AVAssetReader *const assetReader = [AVAssetReader assetReaderWithAsset:asset error:&error];
    XCTAssertNil(error);
    AVAssetWriter *const assetWriter = [AVAssetWriter assetWriterWithURL:outputUrl fileType:AVFileTypeMPEG4 error:&error];
    XCTAssertNil(error);

    AVAssetWriterInput *const input = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo
                                                                     outputSettings:@{
                                                                                      AVVideoCodecKey: AVVideoCodecTypeH264,
                                                                                      AVVideoWidthKey: @(videoTrack.naturalSize.width),
                                                                                      AVVideoHeightKey: @(videoTrack.naturalSize.height),
                                                                                      }];

    AVAssetReaderOutput *const output = [[AVAssetReaderTrackOutput alloc] initWithTrack:videoTrack
                                                                         outputSettings:@{
                                                                                          (id)kCVPixelBufferPixelFormatTypeKey:
                                                                                              @(kCVPixelFormatType_32BGRA),
                                                                                          }];

    [assetReader addOutput:output];
    [assetWriter addInput:input];

    XCTAssertNil(assetReader.error);
    XCTAssertNil(assetWriter.error);

    [assetReader startReading];
    [assetWriter startWriting];

    [assetWriter startSessionAtSourceTime:kCMTimeZero];

    const dispatch_queue_t queue = dispatch_queue_create("transcode", DISPATCH_QUEUE_SERIAL);
    const dispatch_semaphore_t transcode = dispatch_semaphore_create(0);
    [input requestMediaDataWhenReadyOnQueue:queue usingBlock:^{
        while ([input isReadyForMoreMediaData]) {
            const CMSampleBufferRef sampleBuffer = [output copyNextSampleBuffer];

            XCTAssertNil(assetReader.error);
            XCTAssertNil(assetWriter.error);

            if (sampleBuffer) {
                [input appendSampleBuffer:sampleBuffer];
                CFRelease(sampleBuffer);
            } else {
                [input markAsFinished];
                dispatch_semaphore_signal(transcode);
            }
        }
    }];

    dispatch_semaphore_wait(transcode, DISPATCH_TIME_FOREVER);

    const dispatch_semaphore_t write = dispatch_semaphore_create(0);
    [assetWriter finishWritingWithCompletionHandler:^{
        dispatch_semaphore_signal(write);
    }];

    XCTAssertNil(assetWriter.error);

    dispatch_semaphore_wait(write, DISPATCH_TIME_FOREVER);
    AVAsset *const outputAsset = [AVAsset assetWithURL:outputUrl];
    XCTAssertGreaterThan(CMTimeGetSeconds(outputAsset.duration), 0);
    AVAssetTrack *const outputTrack = [[outputAsset tracksWithMediaType:AVMediaTypeVideo] firstObject];
    XCTAssertTrue(CGSizeEqualToSize(outputTrack.naturalSize, videoTrack.naturalSize));
}

Comments

I've narrowed the problem down to the AVAssetReaderTrackOutput. If I instead use an AVAssetReaderVideoCompositionOutput, I can read the file without error.

By cellsworth at Dec. 5, 2018, 1:36 a.m. (reply...)

Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!