关键词:
参考技术A 四、视频的编解码-编码篇时间 2016-08-05 10:22:59 Twenty's 时间念
原文 http://blog.img421.com/si-shi-pin-de-bian-jie-ma-bian-ma-pian/
主题 iOS开发 MacOS
在此之前我们通常使用的FFmpeg多媒体库,利用CPU来进行视频的编解码,占用CPU资源,效率低下,俗称软编解码.而苹果在2014年的iOS8中,开放了VideoToolbox.framwork框架,此框架使用GPU或专用的处理器来进行编解码,俗称硬编解码.而此框架在此之前只有MAC OS系统中可以使用,在iOS作为私有框架.终于苹果在iOS8.0中得到开放引入.
2014年的WWDC Direct Access to Video Encoding and Decoding 中,苹果介绍了使用videoToolbox硬编解码.
使用硬编解码有几个优点: * 提高性能; * 增加效率; * 延长电量的使用
对于编解码,AVFoundation框架只有以下几个功能: 1. 直接解压后显示;
2. 直接压缩到一个文件当中;
而对于Video Toolbox,我们可以通过以下功能获取到数据,进行网络流传输等多种保存: 1. 解压为图像的数据结构;
2. 压缩为视频图像的容器数据结构.
一、videoToolbox的基本数据
Video Toolbox视频编解码前后需要应用的数据结构进行说明。
CVPixelBuffer:编码前和解码后的图像数据结构。此内容包含一系列的CVPixelBufferPool内容
CMTime、CMClock和CMTimebase:时间戳相关。时间以64-bit/32-bit的形式出现。
pixelBufferAttributes:字典设置.可能包括Width/height、pixel format type、• Compatibility (e.g., OpenGL ES, Core Animation)
CMBlockBuffer:编码后,结果图像的数据结构。
CMVideoFormatDescription:图像存储方式,编解码器等格式描述。
(CMSampleBuffer:存放编解码前后的视频图像的容器数据结构。
CMClock
CMTimebase: 关于CMClock的一个控制视图,包含CMClock、时间映射(Time mapping)、速率控制(Rate control)
由二、采集视频数据可知,我们获取到的数据(CMSampleBufferRef)sampleBuffer为未编码的数据;
图1.1
上图中,编码前后的视频图像都封装在CMSampleBuffer中,编码前以CVPixelBuffer进行存储;编码后以CMBlockBuffer进行存储。除此之外两者都包括CMTime、CMVideoFormatDesc.
二、视频数据流编码并上传到服务器
1.将CVPixelBuffer使用VTCompressionSession进行数据流的硬编码。
(1)初始化VTCompressionSession
VT_EXPORT OSStatus VTCompressionSessionCreate( CM_NULLABLE CFAllocatorRef allocator, int32_t width, int32_t height, CMVideoCodecType codecType, CM_NULLABLE CFDictionaryRef encoderSpecification, CM_NULLABLE CFDictionaryRef sourceImageBufferAttributes, CM_NULLABLE CFAllocatorRef compressedDataAllocator, CM_NULLABLE VTCompressionOutputCallback outputCallback, void * CM_NULLABLE outputCallbackRefCon, CM_RETURNS_RETAINED_PARAMETER CM_NULLABLE VTCompressionSessionRef * CM_NONNULL compressionSessionOut) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_8_0);
VTCompressionSession的初始化参数说明:
allocator:分配器,设置NULL为默认分配
width: 宽
height: 高
codecType: 编码类型,如kCMVideoCodecType_H264
encoderSpecification: 编码规范。设置NULL由videoToolbox自己选择
sourceImageBufferAttributes: 源像素缓冲区属性.设置NULL不让videToolbox创建,而自己创建
compressedDataAllocator: 压缩数据分配器.设置NULL,默认的分配
outputCallback: 当VTCompressionSessionEncodeFrame被调用压缩一次后会被异步调用.注:当你设置NULL的时候,你需要调用VTCompressionSessionEncodeFrameWithOutputHandler方法进行压缩帧处理,支持iOS9.0以上
outputCallbackRefCon: 回调客户定义的参考值.
compressionSessionOut: 压缩会话变量。
(2)配置VTCompressionSession
使用VTSessionSetProperty()调用进行配置compression。 * kVTCompressionPropertyKey AllowFrameReordering: 允许帧重新排序.默认为true * kVTCompressionPropertyKey AverageBitRate: 设置需要的平均编码率 * kVTCompressionPropertyKey H264EntropyMode:H264的 熵编码 模式。有两种模式:一种基于上下文的二进制算数编码CABAC和可变长编码VLC.在slice层之上(picture和sequence)使用定长或变长的二进制编码,slice层及其以下使用VLC或CABAC. 详情请参考 * kVTCompressionPropertyKey RealTime: 视频编码压缩是否是实时压缩。可设置CFBoolean或NULL.默认为NULL * kVTCompressionPropertyKey ProfileLevel: 对于编码流指定配置和标准 .比如kVTProfileLevel H264 Main AutoLevel
配置过VTCompressionSession后,可以可选的调用VTCompressionSessionPrepareToEncodeFrames进行准备工作编码帧。
(3)开始硬编码流入的数据
使用VTCompressionSessionEncodeFrame方法进行编码.当编码结束后调用outputCallback回调函数。
VT_EXPORT OSStatus VTCompressionSessionEncodeFrame( CM_NONNULL VTCompressionSessionRef session, CM_NONNULL CVImageBufferRef imageBuffer, CMTime presentationTimeStamp, CMTime duration,// may be kCMTimeInvalidCM_NULLABLE CFDictionaryRef frameProperties,void* CM_NULLABLE sourceFrameRefCon, VTEncodeInfoFlags * CM_NULLABLE infoFlagsOut ) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_8_0);
presentationTimeStamp: 获取到的这个sample buffer数据的展示时间戳。每一个传给这个session的时间戳都要大于前一个展示时间戳.
duration: 对于获取到sample buffer数据,这个帧的展示时间.如果没有时间信息,可设置kCMTimeInvalid.
frameProperties: 包含这个帧的属性.帧的改变会影响后边的编码帧.
sourceFrameRefCon: 回调函数会引用你设置的这个帧的参考值.
infoFlagsOut: 指向一个VTEncodeInfoFlags来接受一个编码操作.如果使用异步运行,kVTEncodeInfo_Asynchronous被设置;同步运行,kVTEncodeInfo_FrameDropped被设置;设置NULL为不想接受这个信息.
(4)执行VTCompressionOutputCallback回调函数
typedefvoid(*VTCompressionOutputCallback)(void* CM_NULLABLE outputCallbackRefCon,void* CM_NULLABLE sourceFrameRefCon, OSStatus status, VTEncodeInfoFlags infoFlags, CM_NULLABLE CMSampleBufferRef sampleBuffer );
outputCallbackRefCon: 回调函数的参考值
sourceFrameRefCon: VTCompressionSessionEncodeFrame函数中设置的帧的参考值
status: 压缩的成功为noErr,如失败有错误码
infoFlags: 包含编码操作的信息标识
sampleBuffer: 如果压缩成功或者帧不丢失,则包含这个已压缩的数据CMSampleBuffer,否则为NULL
(5)将压缩成功的sampleBuffer数据进行处理为基本流NSData上传到服务器
MPEG-4是一套用于音频、视频信息的压缩编码标准.
由 图1.1 可知,已压缩 $$CMSampleBuffer = CMTime(可选) + CMBlockBuffer + CMVideoFormatDesc$$。
5.1 先判断压缩的数据是否正确
//不存在则代表压缩不成功或帧丢失if(!sampleBuffer)return;if(status != noErr)return;//返回sampleBuffer中包括可变字典的不可变数组,如果有错误则为NULLCFArrayRefarray= CMSampleBufferGetSampleAttachmentsArray(sampleBuffer,true);if(!array)return; CFDictionaryRef dic = CFArrayGetValueAtIndex(array,0);if(!dic)return;//issue 3:kCMSampleAttachmentKey_NotSync:没有这个键意味着同步, yes: 异步. no:同步BOOL keyframe = !CFDictionaryContainsKey(dic, kCMSampleAttachmentKey_NotSync);//此代表为同步
而对于 issue 3 从字面意思理解即为以上的说明,但是网上看到很多都是做为查询是否是视频关键帧,而查询文档看到有此关键帧key值kCMSampleBufferAttachmentKey_ForceKeyFrame存在,因此对此值如若有了解情况者敬请告知详情.
5.2 获取CMVideoFormatDesc数据由 三、解码篇 可知CMVideoFormatDesc 包括编码所用的profile,level,图像的宽和高,deblock滤波器等.具体包含 第一个NALU的SPS (Sequence Parameter Set)和 第二个NALU的PPS (Picture Parameter Set).
//if (keyframe && !encoder -> sps) //获取sample buffer 中的 CMVideoFormatDesc CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer); //获取H264参数集合中的SPS和PPS const uint8_t * sparameterSet;size_t sparameterSetSize,sparameterSetCount ; OSStatus statusCode = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 0, &sparameterSet, &sparameterSetSize, &sparameterSetCount,0);if (statusCode == noErr) size_t pparameterSetSize, pparameterSetCount; const uint8_t *pparameterSet;OSStatus statusCode = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 1, &pparameterSet, &pparameterSetSize, &pparameterSetCount,0);if (statusCode == noErr) encoder->sps = [NSData dataWithBytes:sparameterSetlength:sparameterSetSize];encoder->pps = [NSData dataWithBytes:pparameterSetlength:pparameterSetSize];
5.3 获取CMBlockBuffer并转换成数据
CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer); size_t lengthAtOffset,totalLength;char*dataPointer;//接收到的数据展示OSStatus blockBufferStatus = CMBlockBufferGetDataPointer(blockBuffer,0, &lengthAtOffset, &totalLength, &dataPointer);if(blockBufferStatus != kCMBlockBufferNoErr) size_t bufferOffset =0;staticconstintAVCCHeaderLength =4;while(bufferOffset < totalLength - AVCCHeaderLength) // Read the NAL unit lengthuint32_t NALUnitLength =0;/**
* void *memcpy(void *dest, const void *src, size_t n);
* 从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中
*/memcpy(&NALUnitLength, dataPointer + bufferOffset, AVCCHeaderLength);//字节从高位反转到低位NALUnitLength = CFSwapInt32BigToHost(NALUnitLength); RTAVVideoFrame * frame = [RTAVVideoFramenew]; frame.sps = encoder -> sps; frame.pps = encoder -> pps; frame.data = [NSData dataWithBytes:(dataPointer+bufferOffset+AVCCHeaderLength) length:NALUnitLength]; bufferOffset += NALUnitLength + AVCCHeaderLength;
此得到的H264数据应用于后面的RTMP协议做推流准备。
聊聊视频中的编解码器,你所不知道的h264h265vp8vp9和av1编解码库
...h265/vp8/vp9编解码库吗?我们日常生活中使用最广泛的五种视频编码:H264(AVC)、H265(HEVC)、vp8、vp9、av1都分别是什么?由哪些组织/公司实现的?编解码库的授权协议都是什么?他们又分别有什么优点?今天就让博主带领诸位小伙伴... 查看详情
ffmpeg学习1:视频解码
在视频解码前,先了解以下几个基本的概念:编解码器(CODEC):能够进行视频和音频压缩(CO)与解压缩(DEC),是视频编解码的核心部分。容器/多媒体文件(Container/File):没有了解视频的编解码之前,总是错误的认为平常... 查看详情
聊聊视频中的编解码器,你所不知道的h264h265vp8vp9和av1编解码库
...vp8/vp9编解码库吗?我们日常生活中使用最广泛的五种视频编码:H264(AVC)、H265(HEVC)、vp8、vp9、av1都分别是什么?由哪些组织/公司实现的?编解码库的授权协议都是什么?他们又分别有什么优点?今天就让博... 查看详情
YouTube“书呆子统计”如何运作。或者如何从 JavaScript 获取正在播放的视频的编解码器信息
...子统计”如何运作。或者如何从JavaScript获取正在播放的视频的编解码器信息【英文标题】:HowYouTube"statsfornerds"work.OrhowtogetthecodecinformationofavideobeingplayedfromJavaScript【发布时间】:2019-03-2606:35:32【问题描述】:书呆子的YouTu... 查看详情
视频编解码
所谓视频编码方式就是指通过特定的压缩技术,将某个视频格式的文件转换成另一种视频格式文件的方式。视频流传输中最为重要的编解码标准有国际电联的H.261、H.263、H.264,运动静止图像专家组的M-JPEG和国际标准化组织运动图... 查看详情
rte领域的发展,为视频编解码标准带来哪些新变化?丨devfordev专栏
本文为「DevforDev专栏」系列内容,作者为声网资深视频算法负责人戴伟。01视频编解码标准的历史和现在1990年左右H.261标准的制定,开启了视频编解码标准化的历程。经过30多年的努力,视频的编码效率得到了极大幅... 查看详情
如何在 Grails 3 中更改每个插件的编解码器?
】如何在Grails3中更改每个插件的编解码器?【英文标题】:Howtochangeper-plugincodecinGrails3?【发布时间】:2017-12-0915:22:24【问题描述】:虽然我知道这不是“最佳实践”,但我有很多旧插件要升级到Grails3,我需要将它们的GSP编码设... 查看详情
java编码与解码
...rticle/details/80356870java编码与解码(一)编码表概述和常见的编码表概述:有字符及其对应的数值组成的一张表常见的编码表ASCII:美国标准信息交换表ISO8859-1:拉丁码表,欧洲码表GB2312:中国的中文编码表GBK:中国的中文编码表升级G... 查看详情
如何在 Xcode 中使用 OpenCV 3.4.14 识别 .mp4 视频文件的编解码器?
】如何在Xcode中使用OpenCV3.4.14识别.mp4视频文件的编解码器?【英文标题】:Howdoyouidentifythecodecofa.mp4videofileusingOpenCV3.4.14inXcode?【发布时间】:2021-10-0112:41:16【问题描述】:我试过了cv::VideoCapturecap(argv[1]);unsignedf=(unsigned)cap.get(cv::CAP... 查看详情
android硬编码——音频编码视频编码及音视频混合(代码片段)
视频编解码对许多Android程序员来说都是Android中比较难的一个知识点。在Android4.1以前,Android并没有提供硬编硬解的API,所以之前基本上都是采用FFMpeg来做视频软件编解码的,现在FFMpeg在Android的编解码上依旧广泛应用... 查看详情
基于ffmpeg的视频解码(libavcodec,致敬雷霄骅)(代码片段)
基于FFMPEG的视频解码(libavcodec,致敬雷霄骅)本文参考了雷博士的博客:最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器基本上雷博士这篇博客已经把这个问题讲的挺清楚了。但是ffmpeg新版本的A... 查看详情
基于ffmpeg的视频解码(libavcodec,致敬雷霄骅)(代码片段)
基于FFMPEG的视频解码(libavcodec,致敬雷霄骅)本文参考了雷博士的博客:最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器基本上雷博士这篇博客已经把这个问题讲的挺清楚了。但是ffmpeg新版本的A... 查看详情
QR码(二维条码)的编解码算法? [关闭]
】QR码(二维条码)的编解码算法?[关闭]【英文标题】:QRcode(2Dbarcode)codinganddecodingalgorithms?[closed]【发布时间】:2010-09-1821:44:47【问题描述】:寻找免费/开源代码或算法描述来编码(简单)和解码(硬)二维条码QRcode。这似乎... 查看详情
ffmpeg/avconv:使用相同的编解码器和参数作为输入进行转码
】ffmpeg/avconv:使用相同的编解码器和参数作为输入进行转码【英文标题】:ffmpeg/avconv:transcodeusingsamecodecandparamsasinput【发布时间】:2016-03-3006:30:03【问题描述】:有没有办法告诉ffmpeg和/或avconv使用与输入编码的编解码器相同的... 查看详情
android音视频开发之音视频编码原理
一、前言在上一篇Android音视频开发之音视频基础知识文章中,我们学习了播放一个视频的流程:解协议、解封装、解码、音视频同步、音视频数据的渲染;其中解码是非常关键的一步,那为什么要进行解码,所谓... 查看详情
pyqt5学习笔记--摄像头实时视频展示多线程处理视频编解码(代码片段)
目录1--前言2--基于QtDesigner设计ui文件3--视频的编解码操作4--完整代码5--结果展示6--存在的问题7--参考1--前言①创建两个线程,主线程为ui线程,子线程用于读取摄像头视频,将处理后的图像帧数据(处理操作可以... 查看详情
将 H265 编码为 hvc1 编解码器
...】:这是我的源文件的信息:我想保持音频质量并且只对视频轨道进行编码,所以我使用了这个命令:ffmpeg-iINPUT-c:acopy-c:vlibx265video-h265.mp4这是结果:但是视频轨道的编解码器是hev1。我想要的是hvc1【问题讨论】:为什么提供文字... 查看详情
转常见视频编码方式以及封装格式
常见视频编码方式以及封装格式 常见视频编码方式 所谓视频编码方式就是指通过特定的压缩技术,将某个视频格式的文件转换成另一种视频格式文件的方式。视频流传输中最为重要的编解码标准有国际电联的H.261、H... 查看详情