在FFmpeg中,H264在编码前必须要转换成YUV420P,本文就分享一下怎么将h264转成YUV420P。
以下就是yuv420:
八个像素为:[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3][Y5 U5 V5] [Y6 U6 V6] [Y7U7 V7] [Y8 U8 V8]
码流为:Y0 U0 Y1 Y2 U2 Y3 Y5 V5 Y6 Y7 V7 Y8
映射出的像素点为:[Y0 U0 V5] [Y1 U0 V5] [Y2 U2 V7] [Y3 U2 V7][Y5 U0 V5] [Y6 U0 V5] [Y7U2 V7] [Y8 U2 V7]
注意:码流12字节个代表8个像素
理解需要画矩阵,如下:
码流数据:(4:2:0 ~ 4:0:2)
Y0 U0
Y1
Y2 U2
Y3
Y5 V5
Y6
Y7 V7
Y8
映射像素:
Y0 U0 V5
Y1 U0 V5
Y2 U2 V7
Y3 U2 V7
Y5 U0 V5
Y6 U0 V5
Y7 U2 V7
Y8 U2 V7
YUV 4:2:0采样,每四个Y共用一组UV分量。
所以要把H264解码YUV420。首先需要把ffmpeg初始化:
代码如下:
//下面初始化h264解码库
avcodec_init();
av_register_all();
AVFrame *pFrame_ = NULL;
AVCodecContext *codec_ = avcodec_alloc_context();
/* find the video encoder */
AVCodec *videoCodec = avcodec_find_decoder(CODEC_ID_H264);
if (!videoCodec)
{
cout << "codec not found!" << endl;
return -1;
}
//初始化参数,下面的参数应该由具体的业务决定
codec_->time_base.num = 1;
codec_->frame_number = 1; //每包一个视频帧
codec_->codec_type = AVMEDIA_TYPE_VIDEO;
codec_->bit_rate = 0;
codec_->time_base.den = 30;//帧率
codec_->width = 1280;//视频宽
codec_->height = 720;//视频高
if(avcodec_open(codec_, videoCodec) >= 0)
pFrame_ = avcodec_alloc_frame();// Allocate video frame
else
return -1;
初始化完成,然后就需要把h264帧传进去进行解码出YUV420:
代码如下:
AVPacket pAvPacket = { 0 };
decoderObj.mVideoFrame420->pict_type = picType;
pAvPacket.data = buf;
pAvPacket.size = size;
int res = 0;
int gotPic = 0;
res = avcodec_decode_video2(decoderObj.pVideoCodecCtx, decoderObj.mVideoFrame420, &gotPic, &pAvPacket);
if (!gotPic) return -9;
decoderObj.pSws_ctx = sws_getContext(decoderObj.pVideoCodecCtx->width, decoderObj.pVideoCodecCtx->height,
decoderObj.pVideoCodecCtx->pix_fmt, decoderObj.pVideoCodecCtx->width, decoderObj.pVideoCodecCtx->height,
AV_PIX_FMT_YUV420P, SWS_FAST_BILINEAR, NULL, NULL, NULL);
sws_scale(decoderObj.pSws_ctx, decoderObj.mVideoFrame420->data, decoderObj.mVideoFrame420->linesize, 0,
decoderObj.mVideoFrame420->height, decoderObj.pYuvFrame.data, decoderObj.pYuvFrame.linesize);
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓
拿到的decoderObj.pYuvFrame.data[0]就是YUV420数据。
最后也不要忘记释放内存。
代码如下:
if (NULL != decoderObj.mVideoFrame420)
{
av_frame_free(&decoderObj.mVideoFrame420);
decoderObj.mVideoFrame420 = NULL;
}
if (NULL != decoderObj.pVideoCodecCtx)
{
avcodec_close(decoderObj.pVideoCodecCtx);
if (NULL != decoderObj.pVideoCodecCtx->priv_data) free(decoderObj.pVideoCodecCtx->priv_data);
if (NULL != decoderObj.pVideoCodecCtx->extradata) free(decoderObj.pVideoCodecCtx->extradata);
avcodec_free_context(&decoderObj.pVideoCodecCtx);
decoderObj.pVideoCodecCtx = NULL;
}
if (NULL != &decoderObj.pYuvFrame)
{
avpicture_free(&decoderObj.pYuvFrame);
//decoderObj.pYuvFrame = NULL;
}
if (NULL != decoderObj.pSws_ctx)
{
sws_freeContext(decoderObj.pSws_ctx);
decoderObj.pSws_ctx = NULL;
}
if (NULL != decoderObj.pVideoCodec)
{
decoderObj.pVideoCodec = NULL;
}
if (NULL != decoderObj.pBuffYuv420)
{
av_free(decoderObj.pBuffYuv420);
decoderObj.pBuffYuv420 = NULL;
}
if (decoderObj.pSws_ctx) {
sws_freeContext(decoderObj.pSws_ctx);
decoderObj.pSws_ctx = NULL;
}
最终效果:使用ffplay指令播放yuv一帧数据
ffplay -i -video_size 700*700 $FILE
PS:avcodec_decode_video2这个函数会修改codec_里面的参数的,也就是说如果原来里面填的分别率是1280X720,运行avcodec_decode_video2后codec_里面会变成实际视频的分辨率。