这里的实现是使用ffmpeg4.1版本。
环境:Centos7.4
FFmpeg安装目录:/usr/local/ffmpeg
g++ 版本:4.8.5
在ffmpeg4.1的版本中有一个转封装的示例代码:remuxing.c,实现的是视频文件的封装转换。
用这个示例修改,不断读取图片文件,封装到目标的视频文件中。并设置相应的帧信息。
本文不涉及音视频的编解码,是直接将图片从一种封装格式文件中获取出来然后打包成另外一种视频文件封装格式的文件。
/**
* @file
* libavformat/libavcodec demuxing and muxing API example.
*
* Remux streams from one container format to another.
* @example remuxing.c
*/
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
// 自定义变量
const char* images[] = {"/opt/test/workdir/1_00001.png", "/opt/test/workdir/1_00002.png", "/opt/test/workdir/1_00003.png", "/opt/test/workdir/1_00004.png", "/opt/test/workdir/1_00005.png", "/opt/test/workdir/1_00006.png", "/opt/test/workdir/1_00007.png", "/opt/test/workdir/1_00008.png", "/opt/test/workdir/1_00009.png", "/opt/test/workdir/1_00010.png"};
static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, const char *tag)
{
AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
tag,
av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
pkt->stream_index);
}
int main(int argc, char **argv)
{
// 输出流格式结构体
AVOutputFormat *ofmt = NULL;
// 输入输出流处理上下文结构体
AVFormatContext *ifmt_ctx = NULL;
// 输出流处理上下文结构体
AVFormatContext *ofmt_ctx = NULL;
// 包结构体
AVPacket pkt;
const char *in_filename, *out_filename;
int ret, i;
int stream_index = 0;
int *stream_mapping = NULL;
int stream_mapping_size = 0;
// if (argc < 3) {
// printf("usage: %s input output\n"
// "API example program to remux a media file with libavformat and libavcodec.\n"
// "The output format is guessed according to the file extension.\n"
// "\n", argv[0]);
// return 1;
// }
// in_filename = argv[1];
// out_filename = argv[2];
out_filename = "/opt/test/workdir/test.avi";
// 打开多媒体数据并且获取一些信息,生成处理上下文结构体
if ((ret = avformat_open_input(&ifmt_ctx, images[0], 0, 0)) < 0) {
fprintf(stderr, "Could not open input file '%s'", images[0]);
return -1;
}
// 主要是读一些包(packets ),然后从中提取初流的信息, 把信息放在处理上下文中
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
fprintf(stderr, "Failed to retrieve input stream information");
return -1;
}
// 打印关于输入或输出格式的详细信息,例如持续时间,比特率,流,容器,程序,元数据,边数据,编解码器和时基
// 最后一个参数 is_output 选择指定的上下文是输入(0)还是输出(1),
av_dump_format(ifmt_ctx, 0, images[0], 0);
// 创建一个输出媒体格式上下文
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
if (!ofmt_ctx) {
fprintf(stderr, "Could not create output context\n");
ret = AVERROR_UNKNOWN;
return -1;
}
// 视音频流的个数
stream_mapping_size = ifmt_ctx->nb_streams;
stream_mapping = av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping));
if (!stream_mapping) {
ret = AVERROR(ENOMEM);
return -1;
}
// 获取输出流的格式结构
ofmt = ofmt_ctx->oformat;
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream *out_stream;
AVStream *in_stream = ifmt_ctx->streams[i];
// 设置解码器参数
AVCodecParameters *in_codecpar = in_stream->codecpar;
if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
stream_mapping[i] = -1;
continue;
}
stream_mapping[i] = stream_index++;
// 用输出流上下文创建输出流
out_stream = avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream) {
fprintf(stderr, "Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
return -1;
}
// 将解码器参数复制给编码器
ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
if (ret < 0) {
fprintf(stderr, "Failed to copy codec parameters\n");
return -1;
}
out_stream->codecpar->codec_tag = 0;
out_stream->codecpar->bit_rate = 25;
//out_stream->time_base = AVRational{1, 25};
out_stream->time_base.den = 10;
out_stream->time_base.num = 1;
}
// 打印关于输入或输出格式的详细信息,例如持续时间,比特率,流,容器,程序,元数据,边数据,编解码器和时基
// 最后一个参数 is_output 选择指定的上下文是输入(0)还是输出(1),
av_dump_format(ofmt_ctx, 0, out_filename, 1);
// 打开或创建输出文件
if (!(ofmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
if (ret < 0) {
fprintf(stderr, "Could not open output file '%s'", out_filename);
return -1;
}
}
// 输出文件写头信息
ret = avformat_write_header(ofmt_ctx, NULL);
AVStream *out_stream = NULL;
if (ret < 0) {
fprintf(stderr, "Error occurred when opening output file\n");
return -1;
}
for (i = 0; i < 10; i++) {
// 重新创建输入流
if (i > 0) {
// 打开多媒体数据并且获取一些信息,生成处理上下文结构体
if ((ret = avformat_open_input(&ifmt_ctx, images[i], 0, 0)) < 0) {
fprintf(stderr, "Could not open input file '%s'", images[i]);
return -1;
}
// 主要是读一些包(packets ),然后从中提取初流的信息, 把信息放在处理上下文中
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
fprintf(stderr, "Failed to retrieve input stream information");
return -1;
}
// 打印关于输入或输出格式的详细信息,例如持续时间,比特率,流,容器,程序,元数据,边数据,编解码器和时基
// 最后一个参数 is_output 选择指定的上下文是输入(0)还是输出(1),
av_dump_format(ifmt_ctx, 0, images[i], 0);
}
while (1) {
// 定义输入流和输出流
AVStream *in_stream = NULL;
// 输入流读出帧包
ret = av_read_frame(ifmt_ctx, &pkt);
if (ret < 0)
break;
in_stream = ifmt_ctx->streams[pkt.stream_index];
if (pkt.stream_index >= stream_mapping_size ||
stream_mapping[pkt.stream_index] < 0) {
av_packet_unref(&pkt);
continue;
}
pkt.stream_index = stream_mapping[pkt.stream_index];
if (out_stream == NULL) {
out_stream = ofmt_ctx->streams[pkt.stream_index];
}
log_packet(ifmt_ctx, &pkt, "in");
/* copy packet */
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
pkt.pos = i;
pkt.pts = i * 2; // 取值为time_base.num 和 time_base.den之前的一个值。
log_packet(ofmt_ctx, &pkt, "out");
// 输出文件写入帧包
printf("pkt size: %d\n", pkt.size);
ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
if (ret < 0) {
fprintf(stderr, "Error muxing packet\n");
break;
}
// 将帧包内容清除
av_packet_unref(&pkt);
}
avformat_close_input(&ifmt_ctx);
}
// 写入文件尾部署数据
av_write_trailer(ofmt_ctx);
// 关闭输入流上下文
avformat_close_input(&ifmt_ctx);
/* close output */
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_closep(&ofmt_ctx->pb);
// 关闭输出流上下文
avformat_free_context(ofmt_ctx);
av_freep(&stream_mapping);
if (ret < 0 && ret != AVERROR_EOF) {
fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
return 1;
}
return 0;
}
编译文件名:g++ c++文件名 -L/usr/local/ffmpeg/lib -lavcodec -lavdevice -lavfilter -lavformat -lavutil -lswscale -I/usr/local/ffmpeg/include