libavformat
是 FFmpeg 项目中的一个库,用于处理多媒体文件的格式解析和封装。H.264 是一种视频编码标准,广泛用于高清视频传输和存储。MP4 是一种多媒体容器格式,可以包含视频、音频、字幕等多种媒体流。
以下是一个简单的示例代码,展示如何使用 libavformat
将 H.264 流复用成 MP4 文件:
#include <stdio.h>
#include <stdlib.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
int main(int argc, char *argv[]) {
AVFormatContext *fmt_ctx = NULL;
AVCodecContext *video_dec_ctx = NULL, *video_enc_ctx = NULL;
AVStream *video_st = NULL;
AVPacket pkt;
int ret, i;
// 初始化 libavformat 和注册所有编解码器、格式和协议
av_register_all();
// 打开输入文件
if ((ret = avformat_open_input(&fmt_ctx, "input.h264", NULL, NULL)) < 0) {
fprintf(stderr, "Could not open input file '%s'", argv[1]);
goto end;
}
// 查找流信息
if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
fprintf(stderr, "Failed to retrieve input stream information");
goto end;
}
// 查找视频流
for (i = 0; i < fmt_ctx->nb_streams; i++) {
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
video_st = fmt_ctx->streams[i];
break;
}
}
if (!video_st) {
fprintf(stderr, "Could not find video stream in the input, aborting\n");
ret = -1;
goto end;
}
// 获取解码器参数并打开解码器
AVCodec *video_dec = avcodec_find_decoder(video_st->codecpar->codec_id);
if (!video_dec) {
fprintf(stderr, "Failed to find decoder for stream #%u\n", video_st->index);
ret = AVERROR_DECODER_NOT_FOUND;
goto end;
}
video_dec_ctx = avcodec_alloc_context3(video_dec);
if (!video_dec_ctx) {
ret = AVERROR(ENOMEM);
goto end;
}
ret = avcodec_parameters_to_context(video_dec_ctx, video_st->codecpar);
if (ret < 0) {
fprintf(stderr, "Failed to copy decoder parameters to input context\n");
goto end;
}
if ((ret = avcodec_open2(video_dec_ctx, video_dec, NULL)) < 0) {
fprintf(stderr, "Failed to open decoder for stream #%u\n", video_st->index);
goto end;
}
// 创建输出格式上下文
AVOutputFormat *out_fmt = av_guess_format(NULL, "output.mp4", NULL);
if (!out_fmt) {
fprintf(stderr, "Could not deduce output format from file extension: using MPEG-4.\n");
out_fmt = av_guess_format("mp4", NULL, NULL);
}
if (!out_fmt) {
fprintf(stderr, "Could not find suitable output format\n");
ret = AVERROR_UNKNOWN;
goto end;
}
AVFormatContext *out_fmt_ctx = NULL;
avformat_alloc_output_context2(&out_fmt_ctx, out_fmt, NULL, "output.mp4");
if (!out_fmt_ctx) {
ret = AVERROR(ENOMEM);
goto end;
}
// 复制视频流
AVCodec *video_enc = avcodec_find_encoder(out_fmt->video_codec);
if (!video_enc) {
fprintf(stderr, "Necessary encoder not found\n");
ret = AVERROR_ENCODER_NOT_FOUND;
goto end;
}
video_enc_ctx = avcodec_alloc_context3(video_enc);
if (!video_enc_ctx) {
ret = AVERROR(ENOMEM);
goto end;
}
video_enc_ctx->width = video_dec_ctx->width;
video_enc_ctx->height = video_dec_ctx->height;
video_enc_ctx->sample_aspect_ratio = video_dec_ctx->sample_aspect_ratio;
video_enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
video_enc_ctx->time_base = video_st->time_base;
if (out_fmt->flags & AVFMT_GLOBALHEADER)
video_enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
ret = avcodec_parameters_from_context(video_enc_ctx->codecpar, video_dec_ctx);
if (ret < 0) {
fprintf(stderr, "Could not copy the stream parameters\n");
goto end;
}
ret = avcodec_open2(video_enc_ctx, video_enc, NULL);
if (ret < 0) {
fprintf(stderr, "Cannot open video encoder for stream #%u\n", video_st->index);
goto end;
}
AVStream *out_stream = avformat_new_stream(out_fmt_ctx, NULL);
if (!out_stream) {
ret = AVERROR(ENOMEM);
goto end;
}
ret = avcodec_parameters_copy(out_stream->codecpar, video_st->codecpar);
if (ret < 0) {
fprintf(stderr, "Failed to copy encoder parameters to output stream #%u\n", out_stream->index);
goto end;
}
out_stream->time_base = video_enc_ctx->time_base;
// 打开输出文件
if (!(out_fmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&out_fmt_ctx->pb, "output.mp4", AVIO_FLAG_WRITE);
if (ret < 0) {
fprintf(stderr, "Could not open output file '%s'", "output.mp4");
goto end;
}
}
// 写入文件头
ret = avformat_write_header(out_fmt_ctx, NULL);
if (ret < 0) {
fprintf(stderr, "Error occurred when opening output file\n");
goto end;
}
// 读取输入文件并写入输出文件
while (1) {
ret = av_read_frame(fmt_ctx, &pkt);
if (ret < 0)
break;
if (pkt.stream_index == video_st->index) {
ret = avcodec_send_packet(video_dec_ctx, &pkt);
if (ret < 0) {
fprintf(stderr, "Error sending a packet for decoding\n");
goto end;
}
while (ret >= 0) {
ret = avcodec_receive_frame(video_dec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
else if (ret < 0) {
fprintf(stderr, "Error during decoding\n");
goto end;
}
frame->pts = frame->pkt_dts;
ret = avcodec_send_frame(video_enc_ctx, frame);
if (ret < 0) {
fprintf(stderr, "Error sending a frame for encoding\n");
goto end;
}
while (ret >= 0) {
ret = avcodec_receive_packet(video_enc_ctx, &out_pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
else if (ret < 0) {
fprintf(stderr, "Error during encoding\n");
goto end;
}
out_pkt.stream_index = out_stream->index;
ret = av_interleaved_write_frame(out_fmt_ctx, &out_pkt);
if (ret < 0) {
fprintf(stderr, "Error while writing output packet\n");
goto end;
}
av_packet_unref(&out_pkt);
}
}
}
av_packet_unref(&pkt);
}
// 写入文件尾
av_write_trailer(out_fmt_ctx);
end:
av_packet_unref(&pkt);
if (video_dec_ctx)
avcodec_close(video_dec_ctx);
if (video_enc_ctx)
avcodec_close(video_enc_ctx);
if (fmt_ctx && !(fmt_ctx->oformat->flags & AVFMT_NOFILE))
avio_closep(&fmt_ctx->pb);
if (out_fmt_ctx && !(out_fmt->flags & AVFMT_NOFILE))
avio_closep(&out_fmt_ctx->pb);
avformat_free_context(fmt_ctx);
avformat_free_context(out_fmt_ctx);
return ret < 0;
}
通过以上步骤和示例代码,你可以将 H.264 流复用成 MP4 文件。如果遇到具体问题,请根据错误信息进行排查和解决。
领取专属 10元无门槛券
手把手带您无忧上云