解码流程
avformat_open_input(): 打开视频频文件,获取里面的内容(解封装)
avformat_find_stream_info(): 获取视频信息
av_find_best_stream(): 获取视频索引
avcodec_find_decoder(): 寻找解码器
avcodec_alloc_context3(): 根据解码器申请解码器相关上下文
avcodec_parameters_to_context():初始化解码器context
avcodec_open2(): 打开解码器
av_image_get_buffer_size(): 计算申请存放yuv数据buf的大小
av_read_frame(): 从视频文件中读取视频帧
avcodec_send_packet(): 发送一帧视频给解码器。
avcodec_receive_frame(): 接收解码器解码后的一帧视频(AVFrame)
av_image_copy_to_buffer(): 把解码数据copy到buf中
代码
#include<iostream>
#include<string>
extern "C"
{
#include "libavutil/samplefmt.h"
#include "libavutil/timestamp.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswresample/swresample.h"
#include "libavutil/imgutils.h"
}
using namespace std;
int main()
{
av_log(NULL, AV_LOG_INFO, "ffmpeg decode video\n");
string filename = "test.mp4";
string dst_filename = "test.yuv";
FILE *dst_file = NULL;
AVFormatContext *fmt_ctx = NULL;
AVCodecContext *codec_ctx = NULL;
AVFrame *decoded_frame = NULL;
SwrContext *actx = NULL;
AVPacket *pPacket = NULL;
//打开输入文件
if (avformat_open_input(&fmt_ctx, filename.c_str(), NULL, NULL) < 0) {
fprintf(stderr, "Could not open source file %s\n", filename.c_str());
exit(1);
}
if(avformat_find_stream_info(fmt_ctx,NULL) < 0)
{
cout<<"get stream fail"<<endl;
return -1;
}
dst_file = fopen(dst_filename.c_str(),"wb");
if(!dst_file)
{
cout<<"open test.yuv fail"<<endl;
return -1;
}
//查找视频索引
int video_index = -1;
video_index = av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);
if(video_index < 0)
{
cout<<"not find video index"<<endl;
return -1;
}
//打印输入语音文件信息
av_dump_format(fmt_ctx, 0,filename.c_str(),0);
//查找解码器
AVCodec *vCodec = NULL;
vCodec = avcodec_find_decoder(fmt_ctx->streams[video_index]->codecpar->codec_id);
if(!vCodec)
{
cout<<"find video decoder fail"<<endl;
return -1;
}
//初始化解码器上下文
codec_ctx = avcodec_alloc_context3(vCodec);
if(!codec_ctx)
{
cout<<"init codec_ctx fail"<<endl;
return -1;
}
int ret = avcodec_parameters_to_context(codec_ctx,fmt_ctx->streams[video_index]->codecpar);
if(ret < 0)
{
cout<<"avcodec_parameters_to_contex fail"<<endl;
return -1;
}
//打开解码器
ret = avcodec_open2(codec_ctx,vCodec,NULL);
if(ret != 0)
{
cout<<"avcodec_open2 fail"<<endl;
return -1;
}
pPacket = av_packet_alloc();
decoded_frame = av_frame_alloc();
if (!decoded_frame) {
fprintf(stderr, "Could not allocate frame\n");
ret = AVERROR(ENOMEM);
return -1;
}
uint8_t *byte_buffer = NULL;
int byte_buffer_size = av_image_get_buffer_size(codec_ctx->pix_fmt, codec_ctx->width, codec_ctx->height, 1);
cout<<"width="<< codec_ctx->width<<" height="<< codec_ctx->height<<endl;
byte_buffer = (uint8_t*)av_malloc(byte_buffer_size);
if (!byte_buffer)
{
av_log(NULL, AV_LOG_ERROR, "allocate buffer fail\n");
return AVERROR(ENOMEM);
}
while(1)
{
ret = av_read_frame(fmt_ctx, pPacket);
if (ret != 0)
{
cout<<"ret="<<ret<<endl;
av_packet_unref(pPacket);
break;
}
if (ret >= 0 && pPacket->stream_index != video_index)
{
av_packet_unref(pPacket);
continue;
}
// 发送待解码包
int len = avcodec_send_packet(codec_ctx, pPacket);
if (len < 0)
{
av_log(NULL, AV_LOG_ERROR, "send packet fail\n");
av_packet_unref(pPacket);
continue;
}
//接收解码数据
while (len >= 0)
{
len = avcodec_receive_frame(codec_ctx, decoded_frame);
if (len == AVERROR_EOF)
{
break;
}
else if (len == AVERROR(EAGAIN))
{
len = 0;
break;
}
else if (len < 0)
{
av_log(NULL, AV_LOG_ERROR, "Error decoding frame\n");
av_frame_unref(decoded_frame);
break;
}
int number_of_written_bytes = av_image_copy_to_buffer(byte_buffer,
byte_buffer_size,
(const uint8_t* const *)decoded_frame->data,
(const int*) decoded_frame->linesize,
codec_ctx->pix_fmt,
codec_ctx->width,
codec_ctx->height,
1);
if (number_of_written_bytes < 0)
{
av_log(NULL, AV_LOG_ERROR, "Can't copy image to buffer\n");
av_frame_unref(decoded_frame);
continue;
}
// 写文件保存解码后视频数据
fwrite(byte_buffer, number_of_written_bytes, 1, dst_file);
fflush(dst_file);
av_frame_unref(decoded_frame);
}
av_packet_unref(pPacket);
}
//释放
fclose(dst_file);
av_frame_free(&decoded_frame);
avcodec_free_context(codec_ctx);
av_packet_free(&pPacket);
avformat_close_input(fmt_ctx);
return 0;
}
版权声明:本文为feifagehao原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。