本文详细介绍如何使用ffmpeg把H265解码成YUV420P格式格式的数据。
首先由于采用VS2017开发,禁用了一个4996的错误:
#pragma error(disable:4996) //全部禁用
#pragma warning(disable:4996)
ffmpeg接收H265的数据,之能一帧一帧发送,不能接收步全的数据,所以需要先把H265解析成一帧数据。
这里我的思路是先建一个大大的缓存,把H265数据保存进来,然后再通过nul头(0x00 0x00 0x00 0x01)来区分每一帧数据。
这里和H264的解析基本完全一样,每一帧的开头4个字节一定是nul头。
char H265Buf[1024 * 512];
int H265Lenth = 0;
while (true)
{
int iReadSize = fread(frame_buf, 1, 512, InFile);
if (iReadSize <= 0)
{
break;
}
memcpy(H265Buf+ H265Lenth, frame_buf, iReadSize);
H265Lenth = H265Lenth + iReadSize;
//获取一帧数据
while (true)
{
bool OneFrame = false;
if (H265Lenth<=8)
{
break;
}
for (int i = 4; i < H265Lenth-4; i++)
{
if (H265Buf[i] == 0x00 && H265Buf[i+1] == 0x00&& H265Buf[i+2] == 0x00&& H265Buf[i+3] == 0x01)
{
H265Lenth = H265Lenth - i;
memcpy(H265Buf, H265Buf+i, H265Lenth);
OneFrame = true;
break;
}
}
if (OneFrame)
{
continue;
}
else
{
break;
}
}
}
下面重点介绍使用ffmpeg解析H265
首先各种初始化:
codec = avcodec_find_decoder(AV_CODEC_ID_H265);
c = avcodec_alloc_context3(codec);
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
return false;
}
丢入H265数据
ret = avcodec_send_packet(c, packet);
获取Yuv420P数据
int ret = avcodec_receive_frame(c, frame);
这里获取到的数据,是保存在AVFrame结构体里 面的,这里我琢磨了好久。
AVFrame有一个成员变量如下,来保存数据,其实使用到了data[0],data[1],data[2]
uint8_t *data[AV_NUM_DATA_POINTERS];
本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击领取↓↓
data[0] 保存Y信息
data[1] 保存U信息
data[2] 保存V信息。
但是,不要简单的认为把数据直接写入文件,就是YUV420P的文件了,其实是不对的。这里有富余数据,每一行的Y信息,举个例子,是480,但是他保存所使用的大小为512大小,
可能是为了更快的计算吧,他的每一行的信息,不是绝对信息,而是2的多少次方的放大信息,我们在保存的时候,需要把多余的数据剔除掉。
其中每一行他使用的大小,在linesize里面标识,linesize[0],linesize[1],linesize[2]
int linesize[AV_NUM_DATA_POINTERS];
linesize[0] 保存Y的一行的大小
linesize[1] 保存U的一行的大小
linesize[2] 保存V的一行的大小
想要得到正确的YUV420P的数据,需要进行转换:
int a = 0, i;
for (i = 0; iheight; i++)
{
memcpy(m_YuvBuf + a, frame->data[0] + i * frame->linesize[0], frame->width);
a += frame->width;
}
for (i = 0; iheight / 2; i++)
{
memcpy(m_YuvBuf + a, frame->data[1] + i * frame->linesize[1], frame->width / 2);
a += frame->width / 2;
}
for (i = 0; iheight / 2; i++)
{
memcpy(m_YuvBuf + a, frame->data[2] + i * frame->linesize[2], frame->width / 2);
a += frame->width / 2;
}
***********************每个Y,U,V的大小,在buf里面可以看到
YUV420P里面,Y的大小是U和V的4被,可以理解为一帧图像:
YYYYYYYYUUVV
先是8个Y值,在2个U,在2个V,顺序排列。
至此,H265保存为YUV420P搞定。
为了便于大家学习和交流,把工程demo上传如下:
采用VS2017开发 C++开发。
本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击领取↓↓