avcodec_send_packet
avcodec_receive_frame
H264 帧分割
ffmpeg -i v1080.mp4 -s 400x300 test.h264
我们使用这个命令生成一个编码后的 h264 文件。
基于 ffmpeg 接口完成视频解码
117_test_decode.cpp
#include <iostream>
#include <fstream>
using namespace std;
extern "C" // 指定函数是 C 语言函数,函数目标名不包含重载标识,C++ 中调用 C 函数需要使用 extern "C"
{
// 引用 ffmpeg 头文件
#include "libavcodec/avcodec.h"
}
// 预处理指令导入库
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avutil.lib")
int main()
{
//1 分割h264 存入AVPacket
// ffmpeg -i v1080.mp4 -s 400x300 test.h264
string file_name = "test.h264";
ifstream ifs;
AVCodec* codec = nullptr;
AVCodecID codec_id = AV_CODEC_ID_H264;
AVCodecContext* context = nullptr;
AVCodecParserContext* parser = nullptr;
AVPacket* packet = nullptr;
AVFrame* frame = nullptr;
unsigned char inbuf[4096] = { 0 };
int inbuf_len = 0;
int ret = 0;
ifs.open(file_name, ios::in | ios::binary);
if (!ifs)
{
return -1;
}
//1 找解码器
codec = avcodec_find_decoder(codec_id);
if (codec == nullptr)
{
cerr << "avcodec_find_decoder failed!" << endl;
return -1;
}
//2 创建上下文
context = avcodec_alloc_context3(codec);
if (context == nullptr)
{
cerr << "avcodec_alloc_context3 failed!" << endl;
return -1;
}
//3 打开上下文
ret = avcodec_open2(context, nullptr, nullptr);
if (ret < 0)
{
cerr << "avcodec_open2 failed!" << endl;
return -1;
}
// 分割上下文
parser = av_parser_init(codec_id);
if (parser == nullptr)
{
cerr << "av_parser_init failed!" << endl;
return -1;
}
packet = av_packet_alloc();
if (packet == nullptr)
{
cerr << "av_packet_alloc failed!" << endl;
return -1;
}
frame = av_frame_alloc();
if (frame == nullptr)
{
cerr << "av_frame_alloc failed!" << endl;
return -1;
}
while (!ifs.eof())
{
int len = 0;
unsigned char* data = inbuf;
ifs.read((char*)inbuf, sizeof(inbuf));
len = ifs.gcount(); // 读取的字节数
while (len > 0)
{
// 通过 0001 截断输出到AVPacket 如果满足一个 AVPackret 则返回这个 AVPacket 的大小 不足一个AVPactet 则返回余下的数据大小 不足一个AVPactet的数据会被缓存起来
int in_len = av_parser_parse2(parser, context,
&packet->data, &packet->size, // 输出
data, len, // 输入
AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0
);
data += in_len;
len -= in_len;
if (packet->size > 0)
{
// cout << packet->size << " " << flush;
//发送packet到解码线程
ret = avcodec_send_packet(context, packet);
if (ret != 0)
{
cerr << "avcodec_send_packet failed!" << endl;
break;
}
/* 获取多帧解码数据 */
ret = avcodec_receive_frame(context, frame);
while (ret == 0)
{
//每次会调用av_frame_unref
cout << frame->format << " " << flush;
ret = avcodec_receive_frame(context, frame);
}
}
}
}
/* 取出缓存数据 */
avcodec_send_packet(context, nullptr);
ret = avcodec_receive_frame(context, frame);
while (ret == 0)
{
cout << frame->format << "-" << flush;
ret = avcodec_receive_frame(context, frame);
}
av_frame_free(&frame);
av_packet_free(&packet);
av_parser_close(parser);
avcodec_free_context(&context);
ifs.close();
return 0;
}
我们打开h264文件读取数据到缓冲区中,对这片缓冲区中的数据进行解析,解析出 AVPacket,然后对 AVPacket 进行解码,解码出 AVFrame。
执行结果如下图所示:
我们打印了解码后原始帧的像素格式,0表示使用 YUV420P 像素格式。