使用ffmpeg解码H264文件
参考资料:
-
FFmpeg编解码器文档:FFmpeg Codecs Documentation
-
FFMPEG结构体分析AVFormatContext: https://blog.csdn.net/leixiaohua1020/article/details/14214705
-
FFMPEG结构体分析:AVFrame:http://blog.csdn.net/leixiaohua1020/article/details/14214577
-
FFMPEG结构体分析:AVFormatContext:http://blog.csdn.net/leixiaohua1020/article/details/14214705
-
FFMPEG结构体分析:AVCodecContext:http://blog.csdn.net/leixiaohua1020/article/details/14214859
-
FFMPEG结构体分析:AVIOContext:http://blog.csdn.net/leixiaohua1020/article/details/14215369
-
FFMPEG结构体分析:AVCodec:http://blog.csdn.net/leixiaohua1020/article/details/14215833
-
FFMPEG结构体分析:AVStream:http://blog.csdn.net/leixiaohua1020/article/details/14215821
-
FFMPEG结构体分析:AVPacket:http://blog.csdn.net/leixiaohua1020/article/details/14215755
这一小节将分别演示使用命令和代码的方式实现H264文件解码生成YUV文件。
1.使用命令解码H264文件
执行这行命令会将yuv420p的H264文件转换为yuv文件。
ffmpeg -i input.h264 -pix_fmt yuv420p output.yuv
注意:执行前需要安装ffmpeg,并提前准备input.h264文件。
执行完成后会自动生成解码完成的output.yuv文件,使用yuvplayer软件打开,选择对应的分辨率和格式即可正常播放。
2.使用程序解码H264文件
我们使用程序进行H264的解码和前面命令用到的库是一样的,H264解码流程如下所示:
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
}
int main(int argc, char *argv[]) {
//输入和输出文件名
const char *input_filename = "input.h264";
const char *output_filename = "output.yuv";
av_register_all();// 注册所有的编解码器和格式
AVFormatContext *format_ctx = nullptr;//存储文件格式信息
//存储文件格式信息
if (avformat_open_input(&format_ctx, input_filename, nullptr, nullptr) != 0) {
fprintf(stderr, "Could not open input file.\n");
return -1;
}
// 获取流信息
if (avformat_find_stream_info(format_ctx, nullptr) < 0) {
fprintf(stderr, "Could not find stream information.\n");
return -1;
}
AVCodec *codec = nullptr;
AVCodecContext *codec_ctx = nullptr;
int video_stream_index = -1;
// 查找视频流
for (unsigned int i = 0; i < format_ctx->nb_streams; i++) {
if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
video_stream_index = i;
// 查找解码器
codec = avcodec_find_decoder(format_ctx->streams[i]->codecpar->codec_id);
// 申请编解码器参数
codec_ctx = avcodec_alloc_context3(codec);
//// 将查到编解码器参数复制出来
avcodec_parameters_to_context(codec_ctx, format_ctx->streams[i]->codecpar);
break;
}
}
// 检查是否找到解码器
if (codec == nullptr) {
fprintf(stderr, "Could not find codec.\n");
return -1;
}
// 打开编解码器
if (avcodec_open2(codec_ctx, codec, nullptr) < 0) {
fprintf(stderr, "Could not open codec.\n");
return -1;
}
// 分配帧和包
AVFrame *frame = av_frame_alloc();
AVPacket packet;
// 打开输出文件
FILE *output_file = fopen(output_filename, "wb");
//读取帧
while (av_read_frame(format_ctx, &packet) >= 0) {
// 检查是否是视频流
if (packet.stream_index == video_stream_index) {
// 发送包到解码器
if (avcodec_send_packet(codec_ctx, &packet) == 0) {
// 接收解码后的帧
while (avcodec_receive_frame(codec_ctx, frame) == 0) {
// 写入YUV数据到输出文件
fwrite(frame->data[0], 1, frame->linesize[0] * codec_ctx->height, output_file);
fwrite(frame->data[1], 1, frame->linesize[1] * codec_ctx->height / 2, output_file);
fwrite(frame->data[2], 1, frame->linesize[2] * codec_ctx->height / 2, output_file);
}
}
}
// 释放包
av_packet_unref(&packet);
}
// 关闭输出文件
fclose(output_file);
// 释放帧
av_frame_free(&frame);
// 释放编解码器上下文
avcodec_free_context(&codec_ctx);
// 关闭输入文件
avformat_close_input(&format_ctx);
return 0;
}