When using the kVTCompressionPropertyKey_DataRateLimits setting with AVVideoCodecTypeH264 and AVVideoProfileLevelH264High41, the resulting output does not include B frames. Compare with AVVideoAverageBitRateKey, which does.
for (NSURL url in urls) {
for (NSNumber bitRate in bitRates) {
for (NSNumber keyFrameInterval in keyFrameIntervals) {
for (NSNumber drl in @[@1, @2, @3]) {
[self testUrl:url bitRate:[bitRate floatValue] keyFrameInterval:[keyFrameInterval integerValue] drl:[drl integerValue]];
}
}
}
}
}
Description
Summary:
When using the kVTCompressionPropertyKey_DataRateLimits setting with AVVideoCodecTypeH264 and AVVideoProfileLevelH264High41, the resulting output does not include B frames. Compare with AVVideoAverageBitRateKey, which does.
Steps to Reproduce:
See attached code.
Verify B-frame count with the following command:
ffprobe -v error -show_frames $FILE | grep pict_type=B | wc -l
Expected Results:
File contains B-frames.
Actual Results:
File does not contain B-frames.
Version/Build:
iOS 12.3
Configuration:
N/A
(void)test { NSArray *const bitRates = @[ @(3e6), ];
NSArray *const urls = @[ [[NSBundle bundleForClass:[self class]] URLForResource:@"file" withExtension:@"mp4"], ];
NSArray *const keyFrameIntervals = @[ @(0.f), ];
for (NSURL url in urls) { for (NSNumber bitRate in bitRates) { for (NSNumber keyFrameInterval in keyFrameIntervals) { for (NSNumber drl in @[@1, @2, @3]) { [self testUrl:url bitRate:[bitRate floatValue] keyFrameInterval:[keyFrameInterval integerValue] drl:[drl integerValue]]; } } } } }
(void)testUrl:(NSURL *)url bitRate:(CGFloat)bitRate keyFrameInterval:(NSInteger)keyFrameInterval drl:(NSInteger)drl {
NSString *extension = nil; if (drl == 1) { extension = @"avg"; } else if (drl == 2) { extension = @"drl"; } else if (drl == 3) { extension = @"both"; }
NSString *filename = [NSString stringWithFormat:@"test-%@-%ld-bps-%ld-s-%@", [[url URLByDeletingPathExtension] lastPathComponent], (long)bitRate, (long)keyFrameInterval, extension];
NSURL *const outputUrl = [[[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:filename] URLByAppendingPathExtension:@"mp4"];
if ([[NSFileManager defaultManager]fileExistsAtPath:outputUrl.path]) { [[NSFileManager defaultManager] removeItemAtURL:outputUrl error:nil]; }
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);
NSMutableDictionary *const compressionProperties = [NSMutableDictionary new]; compressionProperties[AVVideoProfileLevelKey] = AVVideoProfileLevelH264High41; compressionProperties[AVVideoMaxKeyFrameIntervalDurationKey] = @(keyFrameInterval);
if (drl == 1 || drl == 3) { compressionProperties[AVVideoAverageBitRateKey] = @(bitRate); } else if (drl == 2 || drl == 3) { compressionProperties[(id)kVTCompressionPropertyKey_DataRateLimits] = @[@(bitRate / 8.f), @1]; }
AVAssetWriterInput *const input = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:@{ AVVideoCodecKey: AVVideoCodecTypeH264, AVVideoWidthKey: @(videoTrack.naturalSize.width), AVVideoHeightKey: @(videoTrack.naturalSize.height), AVVideoCompressionPropertiesKey: compressionProperties }];
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];
}];
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);
NSLog(@"Wrote to %@", outputUrl); }
Product Version: Created: 2019-05-29T04:08:28.628103 Originated: 2019-05-28T00:00:00 Open Radar Link: http://www.openradar.me/51207635