一.概述

本文将在Mac os系统上使用FFmpeg进行音视频的H264,H265编码。
使用FFmpeg版本为4.2。

二、编码器初始化

有两点需要注意的是:
1.设置pCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;的目的是可以通过pCodecContext->extradatapCodecContext->extradata_size提取到返回PPS,SPS,VPS数据,适用于直播场景,注释中也写的很清楚Place global headers in extradata instead of every keyframe.

如果没有设置则会在AVPacket.data中和其他数据一起返回,适用于直接写入文件。

2.设置pPacket.flags |= AV_PKT_FLAG_KEY的目的可以在编码后的AVPacket中识别出是否为关键帧。

    int ret;enum AVCodecID codecID = AV_CODEC_ID_H264;if (!kUseH264Encode) {codecID = AV_CODEC_ID_HEVC;}pCodec = avcodec_find_encoder(codecID);pCodecContext = avcodec_alloc_context3(pCodec);pCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;pCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;pCodecContext->width = 1280;pCodecContext->height = 720;pCodecContext->time_base.num = 1;pCodecContext->time_base.den = 25;pCodecContext->bit_rate = 1000 * 1000;pCodecContext->qmin = 10;pCodecContext->qmax = 51;pCodecContext->gop_size = 25;pCodecContext->max_b_frames = 0;// pCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;AVDictionary *param = NULL;if (kUseH264Encode) {av_dict_set(&param, "preset", "slow", 0);av_dict_set(&param, "tune", "zerolatency", 0);}else{av_dict_set(&param, "preset", "ultrafast", 0);av_dict_set(&param, "tune", "zero-latency", 0);}if (avcodec_open2(pCodecContext, pCodec, &param)<0) {return;}pFrame = av_frame_alloc();pFrame->width = pCodecContext->width;pFrame->height = pCodecContext->height;pFrame->format = pCodecContext->pix_fmt;ret = av_frame_get_buffer(pFrame, 0);if (ret < 0) {printf("ret == %s\n", av_err2str(ret));}//初始化avpacketav_init_packet(&pPacket);pPacket.flags |= AV_PKT_FLAG_KEY;

三、编码

在Mac OS系统或者iOS系统中,采集到的一般是CMSampleBufferRef对象,需要先从中拿到CVPixelBufferRef对象,再从其中提取YUV数据。而此处的YUV格式,是一种two-plane模式,即Y和UV分为两个Plane,但是UV(CbCr)为交错存储,而不是分为三个plane,需要最终转换为420P格式,即YYYYUV

// 锁定imageBuffer内存地址开始进行编码if (CVPixelBufferLockBaseAddress(pixelBuffer, 0) == kCVReturnSuccess) {//获取Y分量的地址UInt8 *bufferPtr = (UInt8 *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer,0);//获取UV分量的地址UInt8 *bufferPtr1 = (UInt8 *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer,1);//根据像素获取图片的真实宽度&高度size_t width = CVPixelBufferGetWidth(pixelBuffer);size_t height = CVPixelBufferGetHeight(pixelBuffer);// 获取Y分量长度size_t bytesrow0 = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer,0);size_t bytesrow1  = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer,1);UInt8 *yuv420_data = (UInt8 *)malloc(width * height * 3 / 2);//将NV12数据转成YUV420P(I420)数据UInt8 *pY = bufferPtr;UInt8 *pUV = bufferPtr1;UInt8 *pU = yuv420_data + width * height;UInt8 *pV = pU + width * height / 4;for(int i =0;i<height;i++){memcpy(yuv420_data+i*width,pY+i*bytesrow0,width);}for(int j = 0;j<height/2;j++){for(int i =0;i<width/2;i++){*(pU++) = pUV[i<<1];*(pV++) = pUV[(i<<1) + 1];}pUV += bytesrow1;}// 3.5.分别读取YUV的数据pFrame->data[0] = yuv420_data;pFrame->data[1] = pFrame->data[0] + width * height;pFrame->data[2] = pFrame->data[1] + (width * height) / 4;pFrame->pts = frameCount;// 5.对编码前的原始数据(AVFormat)利用编码器进行编码,将 pFrame 编码后的数据传入pkt 中int ret = avcodec_send_frame(pCodecContext, pFrame);if (ret != 0) {printf("Failed to encode! \n");CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);return;}while (1) {ret = avcodec_receive_packet(pCodecContext, &pPacket);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)break;else if (ret < 0) {fprintf(stderr, "Error encoding audio frame\n");break;}frameCount++;if (pPacket.flags & AV_PKT_FLAG_KEY) {videoFrame.isKeyFrame =  YES;}//write fileNSData *data = [NSData dataWithBytes:pPacket.data length:pPacket.size];if ([self.delegate respondsToSelector:@selector(videoEncoder:encodeData:)]) {[self.delegate videoEncoder:self encodeData:data];}//释放packetav_packet_unref(&pPacket);}// 7.释放yuv数据free(yuv420_data);}CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
}

四、提取SPS,PPS,VPS数据

上文说了要单独提取SPS,PPS,VPS数据,需开始设置pCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER

uint8_t *extra_data = pCodecContext->extradata;
int extra_size = pCodecContext->extradata_size;

1.H264编码时,拿到的extra_data如下所示:

00000001 6764001f acb300a0 0b742000 00030020 00000651 e3064d00 00000168 e9732c8b

很明显SPS,PPS被4个字节的start code= 00 00 00 01分割开,NALU header只有一个字节:

00 00 00 01 67  ---> (0x67 & 0x1f) = 7 ---> PPS
00 00 00 01 68  ---> (0x68 & 0x1f) = 8 ---> SPS

代码如下:

int pos = 0;
int pps_pos = 0,pps_length = 0;
int sps_pos = 0,sps_length = 0;
while (pos < (extra_size - 4)) {if (extra_data[pos] == 0 &&extra_data[pos+1] == 0 &&extra_data[pos+2] == 0 &&extra_data[pos+3] == 1) {if ((extra_data[pos+4] & 0x1f) == 7) {//spssps_pos = pos+4;}else if ((extra_data[pos+4] & 0x1f) == 8){//ppspps_pos = pos+4;}}pos ++;
}
sps_length = pps_pos - sps_pos - 4;
pps_length = extra_size - pps_pos;

2.H265编码时,同样方法拿到的extra_data提取SPS,PPS,VPS``NALU header有两个字节,提取方法如下:

00 00 00 01 40 01  ---> (0x40 & 0x7E)>>1 = 32 ---> VPS
00 00 00 01 42 01  ---> (0x42 & 0x7E)>>1 = 33 ---> SPS
00 00 00 01 44 01  ---> (0x44 & 0x7E)>>1 = 34 ---> PPS

需要注意的是,此处还可能包含被3个字节的start code= 00 00 01分割开的NAL_UNIT_SEI数据:

00 00 01 4e 01  ---> (0x4e & 0x7E)>>1 = 39 ---> SEI

五、编码结束

编码结束时,需要冲洗编码器,将编码器中缓存的数据冲洗出来,防止丢帧。方法是发送avcodec_send_frame(pCodecContext, NULL),当avcodec_receive_packet的返回值为AVERROR_EOF则表示冲洗完成。最后再释放内存。

Mac OS使用FFmpeg进行视频H264,H265编码相关推荐

  1. 流媒体播放器播放h264编码视频与h265编码视频哪个更清晰?

    h265编码是h264编码的升级版,h265目前在视频点播方面使用的更加普遍,而在视频直播方面,由于难以达到h265编码的解码速度,运用起来还是有些难度的,还需要看未来我们的流媒体技术的发展.那么既然 ...

  2. Java调用ffmpeg进行视频.H264抽帧,并保存为图片

    Java调用ffmpeg进行视频.H264抽帧,并保存为图片 1. 需求 2. 解决 3. 源码 参考 1. 需求 对视频 D:\data\01-test.H264进行抽帧并保存为图片,图片命名为1. ...

  3. golang基于FFmpeg实现视频H264编解码

    文章目录 一.基本知识 1.1 FFmpeg相关 1.2 H.264相关 1.3 YUV相关 二.H264编码原理 2.1 帧类型分析 2.2 帧内/帧间预测 2.3 变换+量化 2.4 滤波 2.5 ...

  4. FFMPEG H264/H265 编码延迟问题

    参考:http://blog.csdn.net/aoshilang2249/article/details/40397199?utm_source=tuicool&utm_medium=ref ...

  5. iOS 使用FFmpeg实现视频H264编码

    本文借鉴:https://www.jianshu.com/p/70b0af4d0ec7   以及 https://www.jianshu.com/p/31d1ca4999c6 ffmpeg 相关命令行 ...

  6. Android6.0 dump h264/h265编码裸流(二)

    1.dump h264/h265的NALU(VPS/SPS/PPS)与数据 路径:frameworks/av/media/libstagefright/MPEG4Writer.cpp<1> ...

  7. “Mac OS X“录屏幕视频并转成gif

    第一步: 使用软件QuickTime Player录屏幕视频,创建方式选择新建屏幕录制: 选择区域录制,录好保存后,就需要转gif,需要另外一个软件. 第二步: 使用GIFBrewery软件创建gif ...

  8. FFmpeg源码编译出支持音频AAC编码以及H264,H265编码的库

    先决条件: 需要安装msys2(是一个在windows中模拟linux操作系统的软件) 由于在linux中编译ffmpeg比较简单, 所以利用一下msys2, 直接去官网下载安装即可 需要安装visu ...

  9. iOS视频开发(二):视频H264硬编码

    1.前言 前面我们已经介绍了在iOS开发中如果调用摄像头进行视频数据的采集和编解码.但折腾了这么多,对于YUV这玩意儿还是不是特别理解.其实在我的个人实践过程中我也一直搞不懂这个YUV,一顿恶补之后, ...

  10. 用ffmpeg剪辑视频(无编码变化快速切割)(带有快速无转码批量剪辑视频工具下载)

    剪辑视频(准确起止时间.无编码变化快速切割)[参考](https://blog.csdn.net/S_gy_Zetrov/article/details/88594306) 代码 ffmpeg -i ...

最新文章

  1. 差分进化算法_差分进化算法
  2. 数据结构 - 把一个整数数组放到二叉树中使其有序(C++)
  3. 20150917html第二次课
  4. Visual Studio Code设置中文包/配置中文语言
  5. sublime text2 用ctags插件实现方法定位(转)
  6. python 让异常名称显示出来
  7. html5调盒子边框大小,CSS3 - 盒子大小(CSS3 - Box Sizing)
  8. 钉钉推出“钉工牌”,门禁、差旅、员工福利一码通用
  9. leetcode 打印_剑指 Offer 总结 - leetcode 剑指offer系列
  10. 【elasticsearch 】logstash elasticsearch output plugin 的阻塞问题
  11. 每周一个设计模式之工厂方法与抽象工厂
  12. 一阶广义差分模型_微波射频差分探针去嵌入理论研究
  13. 二层、三层、四层交换机、路由器的区别
  14. python3.6学习十四 提示和传递
  15. c语言入门:比较三个数的大小
  16. 滑雪教程-新手必看(上)
  17. avi格式视频转换高清mp4的方法
  18. python(2)兔子产子
  19. LTspice使用教程笔记
  20. 医学影像图像处理若干关键问题的研究——开题报告

热门文章

  1. linux如何停掉计划任务,Linux系统的任务计划
  2. 我的第一个MASM32程序(MASM32环境配置)
  3. 学会PDF转Word,PDF编辑不再是问题!
  4. 谷歌地球到底有多厉害?附查看高清卫星影像方法
  5. 手机麦克风结构原理图_麦克风工作原理是什么
  6. qq连连看看外挂-我的QQ连连看“辅助”程序源码
  7. 服务器raid的原理以及怎么恢复数据
  8. struts2环境搭建教程
  9. 金蝶服务器选项没有账套信息,金蝶财务软件帐套属性设置保存和帐套启用报错的解决方法...
  10. PostgreSQL update多张表关联查询更新