概述
使用VideoToolBox进行视频编码流程
官方文档地址:https://developer.apple.com/documentation/videotoolbox
1.VTCompressionSessionCreate
Create a compression session using VTCompressionSessionCreate.
static void EncodeCallBack(void *outputCallbackRefCon,void *souceFrameRefCon,OSStatus status,VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer) {
}
VTCompressionSessionRef session;
OSStatus status = VTCompressionSessionCreate(NULL,width,height,codecType,NULL,NULL,NULL,EncodeCallBack,(__bridge void *)self,&session);
if (status != noErr) {
}
2.VTSessionSetProperty or VTSessionSetProperties
Optionally, configure the session with your desired Compression Properties by calling VTSessionSetProperty or VTSessionSetProperties.
判断要设置的属性是否支持
- (BOOL)isSupportPropertyWithSession:(VTCompressionSessionRef)session key:(CFStringRef)key {
OSStatus status;
static CFDictionaryRef supportedPropertyDictionary;
if (!supportedPropertyDictionary) {
//得到所有支持的属性
status = VTSessionCopySupportedPropertyDictionary(session, &supportedPropertyDictionary);
if (status != noErr) {
return NO;
}
}
//判断对应的属性是否支持
BOOL isSupport = [NSNumber numberWithBool:CFDictionaryContainsKey(supportedPropertyDictionary, key)].intValue;
return isSupport;
}
设置对应的属性
- (OSStatus)setSessionPropertyWithSession:(VTCompressionSessionRef)session key:(CFStringRef)key value:(CFTypeRef)value {
if (value == nil || value == NULL || value == 0x0) {
return noErr;
}
OSStatus status = VTSessionSetProperty(session, key, value);
if (status != noErr)
{
log4cplus_error("Video Encoder:", "Set session of %s Failed, status = %d",CFStringGetCStringPtr(key, kCFStringEncodingUTF8),status);
}
return status;
}
- (void)setCompressionSessionPropertyWithSession:(VTCompressionSessionRef)session fps:(int)fps bitrate:(int)bitrate isSupportRealtimeEncode:(BOOL)isSupportRealtimeEncode iFrameDuration:(int)iFrameDuration EncoderType:(VideoEncoderType)encoderType {
int maxCount = 3;
if (!isSupportRealtimeEncode) {
if([self isSupportPropertyWithSession:session key:kVTCompressionPropertyKey_MaxFrameDelayCount]) {
CFNumberRef ref
= CFNumberCreate(NULL, kCFNumberSInt32Type, &maxCount);
[self setSessionPropertyWithSession:session key:kVTCompressionPropertyKey_MaxFrameDelayCount value:ref];
CFRelease(ref);
}
}
if(fps) {
if([self isSupportPropertyWithSession:session key:kVTCompressionPropertyKey_ExpectedFrameRate]) {
int
value = fps;
CFNumberRef ref
= CFNumberCreate(NULL, kCFNumberSInt32Type, &value);
[self setSessionPropertyWithSession:session key:kVTCompressionPropertyKey_ExpectedFrameRate value:ref];
CFRelease(ref);
}
}else {
log4cplus_error("Video Encoder:", "Current fps is 0");
return;
}
if(bitrate) {
if([self isSupportPropertyWithSession:session key:kVTCompressionPropertyKey_AverageBitRate]) {
int value = bitrate;
CFNumberRef ref = CFNumberCreate(NULL, kCFNumberSInt32Type, &value);
[self setSessionPropertyWithSession:session key:kVTCompressionPropertyKey_AverageBitRate value:ref];
CFRelease(ref);
}
}else {
log4cplus_error("Video Encoder:", "Current bitrate is 0");
return;
}
if([self isSupportPropertyWithSession:session key:kVTCompressionPropertyKey_RealTime]) {
log4cplus_info("Video Encoder:", "use realTimeEncoder");
[self setSessionPropertyWithSession:session key:kVTCompressionPropertyKey_RealTime value:isSupportRealtimeEncode ? kCFBooleanTrue : kCFBooleanFalse];
}
// Ban B frame.
if([self isSupportPropertyWithSession:session key:kVTCompressionPropertyKey_AllowFrameReordering]) {
[self setSessionPropertyWithSession:session key:kVTCompressionPropertyKey_AllowFrameReordering value:kCFBooleanFalse];
}
if (encoderType == H264Encoder) {
if (isSupportRealtimeEncode) {
if([self isSupportPropertyWithSession:session key:kVTCompressionPropertyKey_ProfileLevel]) {
[self setSessionPropertyWithSession:session key:kVTCompressionPropertyKey_ProfileLevel value:kVTProfileLevel_H264_Main_AutoLevel];
}
}else {
if([self isSupportPropertyWithSession:session key:kVTCompressionPropertyKey_ProfileLevel]) {
[self setSessionPropertyWithSession:session key:kVTCompressionPropertyKey_ProfileLevel value:kVTProfileLevel_H264_Baseline_AutoLevel];
}
if([self isSupportPropertyWithSession:session key:kVTCompressionPropertyKey_H264EntropyMode]) {
[self setSessionPropertyWithSession:session key:kVTCompressionPropertyKey_H264EntropyMode value:kVTH264EntropyMode_CAVLC];
}
}
}else if (encoderType == H265Encoder) {
if([self isSupportPropertyWithSession:session key:kVTCompressionPropertyKey_ProfileLevel]) {
[self setSessionPropertyWithSession:session key:kVTCompressionPropertyKey_ProfileLevel value:kVTProfileLevel_HEVC_Main_AutoLevel];
}
}
if([self isSupportPropertyWithSession:session key:kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration]) {
int
value
= iFrameDuration;
CFNumberRef ref
= CFNumberCreate(NULL, kCFNumberSInt32Type, &value);
[self setSessionPropertyWithSession:session key:kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration value:ref];
CFRelease(ref);
}
log4cplus_info("Video Encoder:", "The compression session max frame delay count = %d, expected frame rate = %d, average bitrate = %d, is support realtime encode = %d, I frame duration = %d",maxCount, fps, bitrate, isSupportRealtimeEncode,iFrameDuration);
}
3.VTCompressionSessionEncodeFrame, VTCompressionOutputCallback
Encode video frames using VTCompressionSessionEncodeFrame and receive the compressed video frames in the session’s VTCompressionOutputCallback.
-(void)startEncodeWithBuffer:(CMSampleBufferRef)sampleBuffer session:(VTCompressionSessionRef)session isNeedFreeBuffer:(BOOL)isNeedFreeBuffer isDrop:(BOOL)isDrop
needForceInsertKeyFrame:(BOOL)needForceInsertKeyFrame lock:(NSLock *)lock {
[lock lock];
if(session == NULL) {
log4cplus_error("Video Encoder:", "%s,session is empty",__func__);
[self handleEncodeFailedWithIsNeedFreeBuffer:isNeedFreeBuffer sampleBuffer:sampleBuffer];
return;
}
//the first frame must be iframe then create the reference timeStamp;
static BOOL isFirstFrame = YES;
if(isFirstFrame && g_capture_base_time == 0) {
CMTime pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
g_capture_base_time = CMTimeGetSeconds(pts);// system absolutly time(s)
//
g_capture_base_time = g_tvustartcaptureTime - (ntp_time_offset/1000);
isFirstFrame = NO;
log4cplus_error("Video Encoder:","start capture time = %u",g_capture_base_time);
}
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CMTime presentationTimeStamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
// Switch different source data will show mosaic because timestamp not sync.
static int64_t lastPts = 0;
int64_t currentPts = (int64_t)(CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer)) * 1000);
if (currentPts - lastPts < 0) {
log4cplus_error("Video Encoder:","Switch different source data the timestamp < last timestamp, currentPts = %lld, lastPts = %lld, duration = %lld",currentPts, lastPts, currentPts - lastPts);
[self handleEncodeFailedWithIsNeedFreeBuffer:isNeedFreeBuffer sampleBuffer:sampleBuffer];
return;
}
lastPts = currentPts;
OSStatus status = noErr;
NSDictionary *properties = @{(__bridge NSString *)kVTEncodeFrameOptionKey_ForceKeyFrame:@(needForceInsertKeyFrame)};
status = VTCompressionSessionEncodeFrame(session,
imageBuffer,
presentationTimeStamp,
kCMTimeInvalid,
(__bridge CFDictionaryRef)properties,
NULL,
NULL);
if(status != noErr) {
log4cplus_error("Video Encoder:", "encode frame failed");
[self handleEncodeFailedWithIsNeedFreeBuffer:isNeedFreeBuffer sampleBuffer:sampleBuffer];
}
[lock unlock];
if (isNeedFreeBuffer) {
if (sampleBuffer != NULL) {
CFRelease(sampleBuffer);
log4cplus_debug("Video Encoder:", "release the sample buffer");
}
}
}
- (void)handleEncodeFailedWithIsNeedFreeBuffer:(BOOL)isNeedFreeBuffer sampleBuffer:(CMSampleBufferRef)sampleBuffer {
// if sample buffer are from system needn't to release, if sample buffer are from we create need to release.
[self.lock unlock];
if (isNeedFreeBuffer) {
if (sampleBuffer != NULL) {
CFRelease(sampleBuffer);
log4cplus_debug("Video Encoder:", "release the sample buffer");
}
}
}
4.VTCompressionSessionCompleteFrames
To force the completion of some or all pending frames, call VTCompressionSessionCompleteFrames.
5. VTCompressionSessionInvalidate
When you finish with the compression session, call VTCompressionSessionInvalidate to invalidate it and CFRelease to free its memory.
-(void)tearDownSessionWithSession:(VTCompressionSessionRef)session lock:(NSLock *)lock {
log4cplus_error("Video Encoder:","tear down session");
[lock lock];
if (session == NULL) {
log4cplus_error("Video Encoder:", "%s current compression is NULL",__func__);
[lock unlock];
return;
}else {
VTCompressionSessionCompleteFrames(session, kCMTimeInvalid);
VTCompressionSessionInvalidate(session);
CFRelease(session);
session = NULL;
}
[lock unlock];
}
CMSampleBufferRef
在视频编码回调 VTCompressionOutputCallback 中得到 CMSampleBufferRef 通过以下方法可以获得相应的信息
1.获取PTS CMSampleBufferGetPresentationTimeStamp(CMSampleBufferRef)
2.获取DTS CMSampleBufferGetDecodeTimeStamp(CMSampleBufferRef)
3.判断是否是关键帧
BOOL isKeyFrame = NO;
CFArrayRef attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, false);
if(attachments != NULL) {
CFDictionaryRef attachment =(CFDictionaryRef)CFArrayGetValueAtIndex(attachments, 0);
CFBooleanRef dependsOnOthers = (CFBooleanRef)CFDictionaryGetValue(attachment, kCMSampleAttachmentKey_DependsOnOthers);
isKeyFrame = (dependsOnOthers == kCFBooleanFalse);
}
4.将 CMSampleBufferRef 转换为 CMBlockBufferRef
CMBlockBufferRef block = CMSampleBufferGetDataBuffer(sampleBuffer);
解码流程
A decompression session supports the decompression of a sequence of video frames. Here’s the basic workflow:
1.VTDecompressionSessionCreate
Create a decompression session by calling
VTDecompressionSessionCreate(allocator:formatDescription:decoderSpecification:imageBufferAttributes:outputCallback:decompressionSessionOut:).
2.VTSessionSetProperty or VTSessionSetProperties
Optionally, configure the session with your desired Decompression Properties by calling VTSessionSetProperty(: key:value:) or VTSessionSetProperties(:propertyDictionary:).
3.VTDecompressionSessionDecodeFrame(_:sampleBuffer: flags:frameRefcon:infoFlagsOut:)
Decode video frames using VTDecompressionSessionDecodeFrame(_:sampleBuffer: flags:frameRefcon:infoFlagsOut:).
4.VTDecompressionSessionInvalidate
When you finish with the decompression session, call VTDecompressionSessionInvalidate(_: ) to tear it down, and call CFRelease to free its memory.
最后
以上就是真实音响为你收集整理的VideoToolBox编解码的全部内容,希望文章能够帮你解决VideoToolBox编解码所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复