头文件
#pragma once#ifndef _VIDEO_DECODING_HEADER_
#define _VIDEO_DECODING_HEADER_#define INBUF_SIZE 4096
#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFILL_THRESH 4096extern "C"
{
#include "libavutil/opt.h"
#include "libavcodec/avcodec.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/imgutils.h"
#include "libavutil/mathematics.h"
#include "libavutil/samplefmt.h"
}/*************************************************
Struct: CodecCtx
Description: FFMpeg编解码器上下文
*************************************************/
typedef struct
{AVCodec *pCodec; //编解码器实例指针AVCodecContext *pCodecContext; //编解码器上下文,指定了编解码的参数AVCodecParserContext *pCodecParserCtx; //编解码解析器,从码流中截取完整的一个NAL Unit数据AVFrame *frame; //封装图像对象指针AVPacket pkt; //封装码流对象实例
} CodecCtx;#endif
cpp文件
/*************
解码主要步骤:
1.解析输入参数,获取待解码的码流数据
2.初始化相应的FFMpeg结构
3.循环读取并解析输入码流数据--由2进制码流解析为FFMpeg可以处理的包数据
4.解码解析出的包为像素数据
5.写出像素数据
6.收尾工作
*************/#include<stdio.h>
#include<stdint.h>
#include"VideoDecodingHeader.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 以二进制只读的方式打开inputFileName,返回FILE指针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 input file failed.\n");return -1;}return 0;
}
//打开解码器且初始化
static int open_decoder()
{//注册组件avcodec_register_all();//初始化最后输出的pktav_init_packet(&pkt);//查找解码器pCodec = avcodec_find_decoder(AV_CODEC_ID_H264);if (!pCodec){return -1;}pCodecContext = avcodec_alloc_context3(pCodec);if (!pCodecContext){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 condec failed.\n");return -1;}//开辟frame空间frame = av_frame_alloc();if (!frame){printf("Error:alloc frame failed.\n");return -1;}return 0;}
//写出YUV数据
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 Close()
{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) < 0){return -1;}else{printf("Open input/ouput file succeed\n");}if (open_decoder() < 0){return -1;}else{printf("Open decoder file succeed\n");}//解码循环int uDataSize = 0, len = 0;int got_frame = 0;uint8_t* pDataPtr = NULL;while (true){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;//pkt.size==0没有解析完if (pkt.size == 0){continue;}//!=0成功解析出一个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("Decoded 1 frame OK! Width x Height: (%d x %d)\n", frame->width, frame->height);write_out_yuv_frame(frame);}else{break;}}}pkt.data = NULL;pkt.size = 0;while (true){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 Decoded 1 frame OK! Width x Height: (%d x %d)\n", frame->width, frame->height);write_out_yuv_frame(frame);}}Close();return 0;
}