视频解码
。视频解码是视频处理的一项操作之一,是播放,分析内容等后续工作的基础
。视频解码是编码的逆过程,将视频由压缩域的码流解码为像素域的图像信号
。视频解码的实际实现由针对不同编码格式的解码器实现,每种解码器可以针对某一种特定标准格式的视频进行解码
FFmpeg视频解码的主要步骤
。解析输入参数----获取待解码的码流数据
。初始化相应的FFmpeg结构
。循环读取并解析输入码流数据----由二进制码流解析为FFmpeg可以处理的包数据
。解码解析出的包为像素数据
。写出像素数据
。收尾工作
源代码
extern"C"
{
#include "libavcodec/avcodec.h"
#include "libavutil/opt.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/imgutils.h"
#include "libavutil/mathematics.h"
#include "libavutil/samplefmt.h"
}
#define INBUF_SIZE 4096
FILE *pFin = NULL;
FILE *pFout = NULL;
AVCodec *pCodec = NULL;//编解码器实例的指针
AVCodecContext *pCodecContext = NULL;//编解码的参数信息
AVCodecParserContext *pCodecParserCtx = NULL;//用于解析码流,生成可以供解码器解码的包
AVFrame *frame = NULL;//用于保存解码完成的像素数据
AVPacket pkt;//用于保存码流解析出来的包
static int open_input_output_file(char **argv)
{
const char *inputFileName = argv[1];
const char *outputFileName = argv[2];
fopen_s(&pFin, inputFileName, "rb+");
if (!pFin)
{
printf("Error:open input file failed.\n");
return -1;
}
fopen_s(&pFout, outputFileName, "wb+");
if (!pFout)
{
printf("Error:open output file failed.\n");
return -1;
}
return 0;
}
static int open_decoder()
{
avcodec_register_all();
av_init_packet(&pkt);
pCodec = avcodec_find_decoder(AV_CODEC_ID_H264);//H264解码器
if (!pCodec)
{
printf("Error:find codec failed.\n");
return -1;
}
pCodecContext = avcodec_alloc_context3(pCodec);
if (!pCodecContext)
{
printf("Error:alloc codecCtx failed.\n");
return -1;
}
//读取码流的时候可能不是按一个完整的包读取的
if (pCodec->capabilities&AV_CODEC_CAP_TRUNCATED)//截断的方式读取
{
pCodecContext->flags |= AV_CODEC_CAP_TRUNCATED;
}
//初始化解析器
pCodecParserCtx = av_parser_init(AV_CODEC_ID_H264);
if (!pCodecParserCtx)
{
printf("Error:alloc parser failed.\n");
return -1;
}
//打开编解码器
if (avcodec_open2(pCodecContext, pCodec, NULL) < 0)
{
printf("Error:open codec failed.\n");
return -1;
}
//分配frame
frame = av_frame_alloc();
if (!frame)
{
printf("Error:alloc frame failed.\n");
return -1;
}
return 0;
}
static void write_out_yuv_frame(AVFrame *frame)
{
uint8_t **pBuf = frame->data;//指向像素保存的地址
int *pStride = frame->linesize;//位宽
for (int color_idx = 0; color_idx < 3; color_idx++)
{
int nWidth = color_idx == 0 ? frame->width : frame->width / 2;
int nHeight = color_idx == 0 ? frame->height : frame->height / 2;
for (int idx = 0; idx < nHeight; idx++)
{
fwrite(pBuf[color_idx], 1, nWidth, pFout);
pBuf[color_idx] += pStride[color_idx];
}
fflush(pFout);
}
}
static void Colse()
{
fclose(pFin);
fclose(pFout);
avcodec_close(pCodecContext);
av_free(pCodecContext);
av_frame_free(&frame);
}
int main(int argc ,char **argv)
{
uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
if (open_input_output_file(argv))
{
return -1;
}
if (open_decoder() < 0)
{
return -1;
}
printf("sucess\n");
//while (1);
int uDataSize = 0;//一次读入到我们定义的缓存的数据的长度
int len = 0;
uint8_t *pDataPtr = NULL;
int got_frame = 0;//是否解码出完整的像素。
while (1)
{
//读取码流数据到缓存
uDataSize = fread_s(inbuf, INBUF_SIZE, 1, INBUF_SIZE, pFin);
if (uDataSize == 0)
{
break;
}
pDataPtr = inbuf;//指向缓存的首地址
while (uDataSize > 0)
{
//解析缓存里的码流数据
len = av_parser_parse2(pCodecParserCtx, pCodecContext, &pkt.data, &pkt.size, pDataPtr, uDataSize, AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE);
pDataPtr += len;
uDataSize -= len;
if (pkt.size == 0)
{
continue;
}
//成功解析出一个packet的码流
printf("Parse 1 packet.\n");
int ret = avcodec_decode_video2(pCodecContext, frame, &got_frame, &pkt);
if (ret < 0)
{
printf("Error:decode failed.\n");
return -1;
}
if (got_frame)//解码出一帧完整的图像
{
printf("decode 1 frame OK Width*Height:(%d*%d).\n", frame->width, frame->height);
//YUV像素数据写入文件
write_out_yuv_frame(frame);
}
}
}
//解码解码器中剩余的数据
pkt.data = NULL;
pkt.size = 0;
while (1)
{
int ret = avcodec_decode_video2(pCodecContext, frame, &got_frame, &pkt);
if (ret < 0)
{
printf("Error:decode failed.\n");
return -1;
}
if (got_frame)//解码出一帧完整的图像
{
printf("Flush decoder OK .\n");
//YUV像素数据写入文件
write_out_yuv_frame(frame);
}
else
{
break;
}
}
//收尾工作
Colse();
return 0;
}