使用VideoToolbox硬编码H.264<转>

文/落影loyinglin(简书作者)
原文链接:http://www.jianshu.com/p/37784e363b8a
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

===========================================

使用VideoToolbox硬编码H.264

前言

H.264是目前很流行的编码层视频压缩格式,目前项目中的协议层有rtmp与http,但是视频的编码层都是使用的H.264。
在熟悉H.264的过程中,为更好的了解H.264,尝试用VideoToolbox硬编码与硬解码H.264的原始码流。

介绍

1、H.264

H.264由视讯编码层(Video Coding Layer,VCL)与网络提取层(Network Abstraction Layer,NAL)组成。
H.264包含一个内建的NAL网络协议适应层,藉由NAL来提供网络的状态,让VCL有更好的编译码弹性与纠错能力。
H.264的介绍看这里
H.264的码流结构
重点对象:

  • 序列参数集SPS:作用于一系列连续的编码图像;
  • 图像参数集PPS:作用于编码视频序列中一个或多个独立的图像;

2、VideoToolbox

VideoToolbox是iOS8以后开放的硬编码与硬解码的API,一组用C语言写的函数。使用流程如下:

  • 1、-initVideoToolBox中调用VTCompressionSessionCreate创建编码session,然后调用VTSessionSetProperty设置参数,最后调用VTCompressionSessionPrepareToEncodeFrames开始编码;
  • 2、开始视频录制,获取到摄像头的视频帧,传入-encode:,调用VTCompressionSessionEncodeFrame传入需要编码的视频帧,如果返回失败,调用VTCompressionSessionInvalidate销毁session,然后释放session;
  • 3、每一帧视频编码完成后会调用预先设置的编码函数didCompressH264,如果是关键帧需要用CMSampleBufferGetFormatDescription获取CMFormatDescriptionRef,然后用
    CMVideoFormatDescriptionGetH264ParameterSetAtIndex取得PPS和SPS;
    最后把每一帧的所有NALU数据前四个字节变成0x00 00 00 01之后再写入文件;
  • 4、调用VTCompressionSessionCompleteFrames完成编码,然后销毁session:VTCompressionSessionInvalidate,释放session。

效果展示

下图是解码出来的图像

贴贴代码

  • 创建session
  •  int width = 480, height = 640;
         OSStatus status = VTCompressionSessionCreate(NULL, width, height, kCMVideoCodecType_H264, NULL, NULL, NULL, didCompressH264, (__bridge void *)(self),  &EncodingSession);
  • 设置session属性
  • // 设置实时编码输出(避免延迟)
         VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_RealTime, kCFBooleanTrue);
         VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Baseline_AutoLevel);
         // 设置关键帧(GOPsize)间隔
         int frameInterval = 10;
         CFNumberRef  frameIntervalRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &frameInterval);
         VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_MaxKeyFrameInterval, frameIntervalRef);
         // 设置期望帧率
         int fps = 10;
         CFNumberRef  fpsRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &fps);
         VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_ExpectedFrameRate, fpsRef);
         //设置码率,上限,单位是bps
         int bitRate = width * height * 3 * 4 * 8;
         CFNumberRef bitRateRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &bitRate);
         VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_AverageBitRate, bitRateRef);
         //设置码率,均值,单位是byte
         int bitRateLimit = width * height * 3 * 4;
         CFNumberRef bitRateLimitRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &bitRateLimit);
         VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_DataRateLimits, bitRateLimitRef);
  • 传入编码帧
  • CVImageBufferRef imageBuffer = (CVImageBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);
     // 帧时间,如果不设置会导致时间轴过长。
     CMTime presentationTimeStamp = CMTimeMake(frameID++, 1000);
     VTEncodeInfoFlags flags;
     OSStatus statusCode = VTCompressionSessionEncodeFrame(EncodingSession,
                                                           imageBuffer,
                                                           presentationTimeStamp,
                                                           kCMTimeInvalid,
                                                           NULL, NULL, &flags);
  • 关键帧获取SPS和PPS
  •  bool keyframe = !CFDictionaryContainsKey( (CFArrayGetValueAtIndex(CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, true), 0)), kCMSampleAttachmentKey_NotSync);
     // 判断当前帧是否为关键帧
     // 获取sps & pps数据
     if (keyframe)
     {
         CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer);
         size_t sparameterSetSize, sparameterSetCount;
         const uint8_t *sparameterSet;
         OSStatus statusCode = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 0, &sparameterSet, &sparameterSetSize, &sparameterSetCount, 0 );
         if (statusCode == noErr)
         {
             // Found sps and now check for pps
             size_t pparameterSetSize, pparameterSetCount;
             const uint8_t *pparameterSet;
             OSStatus statusCode = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 1, &pparameterSet, &pparameterSetSize, &pparameterSetCount, 0 );
             if (statusCode == noErr)
             {
                 // Found pps
                 NSData *sps = [NSData dataWithBytes:sparameterSet length:sparameterSetSize];
                 NSData *pps = [NSData dataWithBytes:pparameterSet length:pparameterSetSize];
                 if (encoder)
                 {
                     [encoder gotSpsPps:sps pps:pps];
                 }
             }
         }
     }
  • 写入数据
  • CMBlockBufferRef dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
     size_t length, totalLength;
     char *dataPointer;
     OSStatus statusCodeRet = CMBlockBufferGetDataPointer(dataBuffer, 0, &length, &totalLength, &dataPointer);
     if (statusCodeRet == noErr) {
         size_t bufferOffset = 0;
         static const int AVCCHeaderLength = 4; // 返回的nalu数据前四个字节不是0001的startcode,而是大端模式的帧长度length
    
         // 循环获取nalu数据
         while (bufferOffset < totalLength - AVCCHeaderLength) {
             uint32_t NALUnitLength = 0;
             // Read the NAL unit length
             memcpy(&NALUnitLength, dataPointer + bufferOffset, AVCCHeaderLength);
    
             // 从大端转系统端
             NALUnitLength = CFSwapInt32BigToHost(NALUnitLength);
    
             NSData* data = [[NSData alloc] initWithBytes:(dataPointer + bufferOffset + AVCCHeaderLength) length:NALUnitLength];
             [encoder gotEncodedData:data isKeyFrame:keyframe];
    
             // Move to the next NAL unit in the block buffer
             bufferOffset += AVCCHeaderLength + NALUnitLength;
         }
     }

    总结

    在网上找到的多个VideoToolboxDemo代码大都类似,更重要是自己尝试实现。
    学习硬编码与硬解码,目的是对H264码流更清晰的了解,实则我们开发过程中并不会触碰到H264的真正编码与解码过程,故而难度远没有想象中那么大。
    这里有代码地址

时间: 2024-10-17 03:42:39

使用VideoToolbox硬编码H.264<转>的相关文章

【流媒体】 Android 实时视频编码—H.264硬编码

[流媒體] Android 实时视频编码—H.264硬编码 SkySeraph Apr 4th 2012 Email:[email protected].com 1  硬编码 & 软编码 硬编码:通过调用Android系统自带的Camera录制视频,实际上是调用了底层的高清编码硬件模块,也即显卡,不使用CPU,速度快 软编码:使用CPU进行编码,如常见C/C++代码,一般编译生成的二进制都是的,速度相对较慢.例如使用Android NDK编译H264生成so库,编写jni接口,再使用java调用

How to use VideoToolbox to decompress H.264 video stream

来源:http://stackoverflow.com/questions/29525000/how-to-use-videotoolbox-to-decompress-h-264-video-stream/ How to use VideoToolbox to decompress H.264 video stream up vote 15 down vote favorite 12 I had a lot of trouble figuring out how to use Apple's

How to encode picture to H264 use AVFoundation on Mac, not use x264(续 :其中提到的用VideoToolBox硬编码,RTMP推流的开源工程 VideoCore project)

来源:https://github.com/jgh-/VideoCore Code Issues 77 Pull requests 4 Wiki Pulse Graphs SSH clone URL You can clone with , , or . An audio and video manipulation pipeline 421 commits 5 branches 43 releases 10 contributors C++ 58.0% Objective-C++ 32.9%

例程:如何使用PX2硬解码H.264裸码流 [CODE_PX2]Decode_RAW_H264_FILE

Rayeager PX2开发板具有非常强大的多媒体处理能力,如果需要调用硬件加速针对普通媒体文件/码流进行解码,只需按照安卓标准调用多媒体相关接口即可. 针对一些行业用户的特殊需求,Rayeager PX2实际上也开放了接口可以对H.264等裸码流进行解码. 这里提供一份代码即可实现H.264裸码流的解码,如果您具有一定的Android系统开发经验,很快就能理解并进行相关改写.使用方法: 在PX2的Android编译环境根目录下将代码解压,并进入ChipSPARK_PX2_H264_DECODE

H.264格式,iOS硬编解码 以及 iOS 11对HEVC硬编解码的支持

H.264格式,iOS硬编解码 以及 iOS 11对HEVC硬编解码的支持 1,H.264格式 网络表示层NAL,如图H.264流由一帧一帧的NALU组成: SPS:序列参数集,作用于一系列连续的编码图像: PPS:图像参数集,作用于编码视频序列中一个或多个独立的图像: 这两个帧也是独立的NALU. I-Frame:关键帧,帧内编码后的帧,显示比较完全的一帧: P-Frame:参考前一帧,可能只是对比前一帧的运动估计的变化部分: B-Frame:会参照前后的帧,其他类似P-Frame.B和P F

H.264开源解码器评测

转自:http://wmnmtm.blog.163.com/blog/static/38245714201142883032575/ 要播放HDTV,就首先要正确地解开封装,然后进行视频音频解码.所以我们需要分离器,视频解码器和音频解码器,俗称hdtv的“三件套”,又统称滤镜. H264的分离器: 常见的有Gabest MP4分离器,就是MP4splitter,也是Gabest编写的,Halli的分离器和NDigital分离器等. H264的视频解码器: CoreAVC的H264视频解码器Cor

H.264硬编码&硬解码

Firefly-RK3288拥有强大的VPU(视像处理器),能够流畅实现720P和1080P视频的H.264编解码: 而H.264的压缩率更高,可以更大程度更小视频的空间占用. 详细看视频演示 1. 演示介绍 基于Firefly开发板:视频监控演示: 需要两块开发板:一块开发板摄像头采集+硬编码,网络传输. 另一块开发板 网络接收.硬解码+显示. Demo中采样5GHz Wi-Fi传输,摄像头使用OV13850,或UVC camera 2. H.264技术介绍 H.264是一种高性能视频编解码技

C++实现RTMP协议发送H.264编码及AAC编码的音视频,摄像头直播

C++实现RTMP协议发送H.264编码及AAC编码的音视频 RTMP(Real Time Messaging Protocol)是专门用来传输音视频数据的流媒体协议,最初由Macromedia 公司创建,后来归Adobe公司所有,是一种私有协议,主要用来联系Flash Player和RtmpServer,如FMS, Red5, crtmpserver等.RTMP协议可用于实现直播.点播应用,通过FMLE(Flash Media Live Encoder)推送音视频数据至RtmpServer,可

最简单的视频编码器:基于libx264(编码YUV为H.264)

本文记录一个最简单的基于libx264的H.264视频编码器.此前记录的H.264编码器都是基于FFmpeg调用libx264完成编码的,例如:<最简单的基于FFMPEG的视频编码器(YUV编码为H.264)>相比与上文中的编码器,本文记录的编码器属于"轻量级"的编码器.因为它不再包含FFmpeg的代码,直接调用libx264完成编码.因此项目的体积非常小巧.该编码器可以将输入的YUV数据编码为H.264码流文件. 流程图 调用libx264进行视频编码的流程图如下所示.