void H264Encode::setBitrate(int bitrate) { #if VERSION_OK if(bitrate == m_bitrate) { return; } m_bitrate = bitrate; if(m_compressionSession) { m_encodeMutex.lock(); int v = m_bitrate * 0.9; // headroom CFNumberRef ref = CFNumberCreate(NULL, kCFNumberSInt32Type, &v); VTCompressionSessionCompleteFrames((VTCompressionSessionRef)m_compressionSession, kCMTimeInvalid); VTSessionSetProperty((VTCompressionSessionRef)m_compressionSession, kVTCompressionPropertyKey_AverageBitRate, ref); CFRelease(ref); v = bitrate / 8; CFNumberRef bytes = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &v); v = 1; CFNumberRef duration = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &v); CFMutableArrayRef limit = CFArrayCreateMutable(kCFAllocatorDefault, 2, &kCFTypeArrayCallBacks); CFArrayAppendValue(limit, bytes); CFArrayAppendValue(limit, duration); VTSessionSetProperty((VTCompressionSessionRef)m_compressionSession, kVTCompressionPropertyKey_DataRateLimits, limit); CFRelease(bytes); CFRelease(duration); CFRelease(limit); //m_forceKeyframe = true; m_encodeMutex.unlock(); } #endif }
static void h264_enc_configure(VTH264EncCtx *ctx) { OSStatus err; const char *error_msg = "Could not initialize the VideoToolbox compresson session"; int max_payload_size = ms_factory_get_payload_max_size(ctx->f->factory); CFNumberRef value; CFMutableDictionaryRef pixbuf_attr = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); int pixel_type = kCVPixelFormatType_420YpCbCr8Planar; value = CFNumberCreate(NULL, kCFNumberIntType, &pixel_type); CFDictionarySetValue(pixbuf_attr, kCVPixelBufferPixelFormatTypeKey, value); err =VTCompressionSessionCreate(NULL, ctx->conf.vsize.width, ctx->conf.vsize.height, kCMVideoCodecType_H264, NULL, pixbuf_attr, NULL, (VTCompressionOutputCallback)h264_enc_output_cb, ctx, &ctx->session); CFRelease(pixbuf_attr); if(err) { ms_error("%s: error code %d", error_msg, err); goto fail; } VTSessionSetProperty(ctx->session, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Baseline_AutoLevel); VTSessionSetProperty(ctx->session, kVTCompressionPropertyKey_AllowFrameReordering, kCFBooleanFalse); value = CFNumberCreate(NULL, kCFNumberIntType, &ctx->conf.required_bitrate); VTSessionSetProperty(ctx->session, kVTCompressionPropertyKey_AverageBitRate, value); value = CFNumberCreate(NULL, kCFNumberFloatType, &ctx->conf.fps); VTSessionSetProperty(ctx->session, kVTCompressionPropertyKey_ExpectedFrameRate, value); if((err = VTCompressionSessionPrepareToEncodeFrames(ctx->session)) != 0) { ms_error("Could not prepare the VideoToolbox compression session: error code %d", err); goto fail; } rfc3984_init(&ctx->packer_ctx); rfc3984_set_mode(&ctx->packer_ctx, 1); ctx->packer_ctx.maxsz = max_payload_size; ctx->is_configured = TRUE; return; fail: if(ctx->session) CFRelease(ctx->session); }
int CVideoEncodeVt::CreateEncoder(const STRUCT_PUSH_STREAM_PARAM& aPushStreamParam, CMVideoCodecType aeCodecType, CFStringRef aProfileLevel, CFDictionaryRef aPixelBufferInfo, VTCompressionSessionRef* apSession) { VTCompressionSessionRef session; int ret = 0; do { //创建编码会话 ret = VTCompressionSessionCreate(kCFAllocatorDefault, aPushStreamParam.iVideoPushStreamWidth, aPushStreamParam.iVideoPushStreamHeight, aeCodecType, NULL, aPixelBufferInfo, kCFAllocatorDefault, EncodeOutputCallback, (void*)this, &session); if (0 != ret) { CLog::GetInstance().Log(ENUM_LOG_LEVEL::enum_Log_Level5, "VTCompressionSessionCreate failed!"); assert(false); break; } //realtime ret = VTSessionSetProperty(session, kVTCompressionPropertyKey_RealTime, kCFBooleanTrue); if (0 != ret) { CLog::GetInstance().Log(ENUM_LOG_LEVEL::enum_Log_Level5, "kVTCompressionPropertyKey_RealTime failed!"); break; } //profile level ret = VTSessionSetProperty(session, kVTCompressionPropertyKey_ProfileLevel, aProfileLevel); if (0 != ret) { CLog::GetInstance().Log(ENUM_LOG_LEVEL::enum_Log_Level5, "kVTCompressionPropertyKey_ProfileLevel failed!"); break; } //bit rate int iBitRate = aPushStreamParam.iVideoMaxBitRate; CFNumberRef bitRate = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &iBitRate); ret = VTSessionSetProperty(session, kVTCompressionPropertyKey_AverageBitRate, bitRate); CFRelease(bitRate); if (0 != ret) { CLog::GetInstance().Log(ENUM_LOG_LEVEL::enum_Log_Level5, "kVTCompressionPropertyKey_AverageBitRate failed!"); break; } CFNumberRef keyFrameInterval = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &aPushStreamParam.iVideoFrameRate); ret = VTSessionSetProperty(session, kVTCompressionPropertyKey_MaxKeyFrameInterval, keyFrameInterval); CFRelease(keyFrameInterval); if (0 != ret) { CLog::GetInstance().Log(ENUM_LOG_LEVEL::enum_Log_Level5, "kVTCompressionPropertyKey_MaxKeyFrameInterval failed!"); break; } //fame rate CFNumberRef frameRate = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &aPushStreamParam.iVideoFrameRate); ret = VTSessionSetProperty(session, kVTCompressionPropertyKey_ExpectedFrameRate, frameRate); CFRelease(frameRate); if (0 != ret) { CLog::GetInstance().Log(ENUM_LOG_LEVEL::enum_Log_Level5, "kVTCompressionPropertyKey_ExpectedFrameRate failed!"); break; } //是否使用b帧 if (!m_bHasBFrames) { ret = VTSessionSetProperty(session, kVTCompressionPropertyKey_AllowFrameReordering, kCFBooleanFalse); if (0 != ret) { CLog::GetInstance().Log(ENUM_LOG_LEVEL::enum_Log_Level5, "kVTCompressionPropertyKey_AllowFrameReordering failed!"); break; } } //entropy if (m_iEntropy != H264_ENTROPY_NOT_SET) { CFStringRef entropy = m_iEntropy == H264_CABAC ? kVTH264EntropyMode_CABAC : kVTH264EntropyMode_CAVLC; ret = VTSessionSetProperty(session, kVTCompressionPropertyKey_H264EntropyMode, entropy); if (0 != ret) { CLog::GetInstance().Log(ENUM_LOG_LEVEL::enum_Log_Level5, "kVTCompressionPropertyKey_H264EntropyMode failed!"); break; } } //准备编码 ret = VTCompressionSessionPrepareToEncodeFrames(session); if (0 != ret) { CLog::GetInstance().Log(ENUM_LOG_LEVEL::enum_Log_Level5, "prepare to encode frames failed!"); assert(false); break; } *apSession = session; return 0; }while(0); if(session) { CFRelease(session); session = NULL; } return -1; }
void H264Encode::setupCompressionSession() { #if VERSION_OK // Parts of this code pulled from https://github.com/galad87/HandBrake-QuickSync-Mac/blob/2c1332958f7095c640cbcbcb45ffc955739d5945/libhb/platform/macosx/encvt_h264.c // More info from WWDC 2014 Session 513 OSStatus err = noErr; CFMutableDictionaryRef encoderSpecifications = nullptr; #if !TARGET_OS_IPHONE /** iOS is always hardware-accelerated **/ CFStringRef key = kVTVideoEncoderSpecification_EncoderID; CFStringRef value = CFSTR("com.apple.videotoolbox.videoencoder.h264.gva"); CFStringRef bkey = CFSTR("EnableHardwareAcceleratedVideoEncoder"); CFBooleanRef bvalue = kCFBooleanTrue; CFStringRef ckey = CFSTR("RequireHardwareAcceleratedVideoEncoder"); CFBooleanRef cvalue = kCFBooleanTrue; encoderSpecifications = CFDictionaryCreateMutable( kCFAllocatorDefault, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionaryAddValue(encoderSpecifications, bkey, bvalue); CFDictionaryAddValue(encoderSpecifications, ckey, cvalue); CFDictionaryAddValue(encoderSpecifications, key, value); #endif VTCompressionSessionRef session = nullptr; err = VTCompressionSessionCreate( kCFAllocatorDefault, m_frameW, m_frameH, kCMVideoCodecType_H264, encoderSpecifications, NULL, NULL, &vtCallback, this, &session); if(err == noErr) { m_compressionSession = session; const int32_t v = m_fps*2; // 2-second kfi CFNumberRef ref = CFNumberCreate(NULL, kCFNumberSInt32Type, &v); err = VTSessionSetProperty(session, kVTCompressionPropertyKey_MaxKeyFrameInterval, ref); CFRelease(ref); } if(err == noErr) { const int v = m_fps; CFNumberRef ref = CFNumberCreate(NULL, kCFNumberSInt32Type, &v); err = VTSessionSetProperty(session, kVTCompressionPropertyKey_ExpectedFrameRate, ref); CFRelease(ref); } if(err == noErr) { err = VTSessionSetProperty(session , kVTCompressionPropertyKey_AllowFrameReordering, kCFBooleanFalse); } if(err == noErr) { const int v = m_bitrate; CFNumberRef ref = CFNumberCreate(NULL, kCFNumberSInt32Type, &v); err = VTSessionSetProperty(session, kVTCompressionPropertyKey_AverageBitRate, ref); CFRelease(ref); } if(err == noErr) { err = VTSessionSetProperty(session, kVTCompressionPropertyKey_RealTime, kCFBooleanTrue); } if(err == noErr) { err = VTSessionSetProperty(session, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Baseline_AutoLevel); } if(err == noErr) { VTCompressionSessionPrepareToEncodeFrames(session); } #endif }