When used in the form @[(targetBitrate / 8.f), @1], where targetBitrate is the desired average bitrate in bits per second, the kVTCompressionPropertyKey_DataRateLimits setting can produce anything from an average bitrate of 80% to 150% of the target, depending on content and target bitrate. According to these docs, an average is expected to be maintained: https://developer.apple.com/library/archive/qa/qa1958/_index.html
AVVideoAverageBitRateKey, in comparison, seems to do a much better job adhering, especially at higher bitrates.
Steps to Reproduce:
See attached code.
Bitrates are derived by the following command:
ffprobe -v error $FILE -select_streams v:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1
Expected Results:
Target bitrates are roughly respected
Actual Results:
Target bitrates are often missed, in either direction.
Description
Summary:
When used in the form @[(targetBitrate / 8.f), @1], where targetBitrate is the desired average bitrate in bits per second, the kVTCompressionPropertyKey_DataRateLimits setting can produce anything from an average bitrate of 80% to 150% of the target, depending on content and target bitrate. According to these docs, an average is expected to be maintained: https://developer.apple.com/library/archive/qa/qa1958/_index.html
AVVideoAverageBitRateKey, in comparison, seems to do a much better job adhering, especially at higher bitrates.
Steps to Reproduce: See attached code.
Bitrates are derived by the following command: ffprobe -v error $FILE -select_streams v:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1
Expected Results: Target bitrates are roughly respected
Actual Results: Target bitrates are often missed, in either direction.
Version/Build: iOS 12.3
Configuration: N/A
(void)test { NSArray *const bitRates = @[ @(2e6), @(3e6), @(5e6), @(7e6), @(9e6), @(12e6), @(15e6) ];
NSArray *const urls = @[ [[NSBundle bundleForClass:[self class]] URLForResource:@"file1" withExtension:@"mp4"], [[NSBundle bundleForClass:[self class]] URLForResource:@"file2" withExtension:@"mp4"], [[NSBundle bundleForClass:[self class]] URLForResource:@"file3" withExtension:@"mp4"], ];
for (NSURL url in urls) { for (NSNumber bitRate in bitRates) { [self testUrl:url bitRate:[bitRate floatValue]]; } } }
(void)testUrl:(NSURL )url bitRate:(CGFloat)bitRate { NSString filename = [NSString stringWithFormat:@"test-%@-%ld-bps-drl", [[url URLByDeletingPathExtension] lastPathComponent], (long)bitRate];
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);
AVAssetWriterInput const input = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:@{ AVVideoCodecKey: AVVideoCodecTypeH264, AVVideoWidthKey: @(videoTrack.naturalSize.width), AVVideoHeightKey: @(videoTrack.naturalSize.height), AVVideoCompressionPropertiesKey: @{ // AVVideoAverageBitRateKey: @(bitRate), (NSString )kVTCompressionPropertyKey_DataRateLimits: @[ @((bitRate) / 8.f), @1, ], AVVideoProfileLevelKey: AVVideoProfileLevelH264High41, } }];
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-25T00:47:18.075283 Originated: 2019-05-24T00:00:00 Open Radar Link: http://www.openradar.me/51127979