优质的菏泽网站建设,成都网络推广网站,东莞房价下跌最惨一览表,网站建设注意细节问题引言 在视频录制中#xff0c;有时会碰到这样一个需求#xff0c;将不同摄像头的画面写入到一个视频文件#xff0c;这个叫法很多#xff0c;有的厂家叫合流模式#xff0c;有的叫多画面多流模式。无论如何#xff0c;它们的实质都是在一个视频文件上实现多路不同分辨率视… 引言 在视频录制中有时会碰到这样一个需求将不同摄像头的画面写入到一个视频文件这个叫法很多有的厂家叫合流模式有的叫多画面多流模式。无论如何它们的实质都是在一个视频文件上实现多路不同分辨率视频的保存。 经过调查支持这种需求封装格式的有MP4、MOV、MKV 等这里因为MP4 格式应用最广泛。 原理 ffmpeg 有一个map命令可以将多路视频轨封装在一个视频容器掰ffmpeg源码发现其实新建一个新的AVStream修改stream-index就可以实现多流录制的目的。 ffmpeg -i input.mp4 -i test.mp4 -map 0:v:0 -map 1:v -map 0:a -map 1:a -c copy -y mix.mp4 #include iostream
#include stringextern C
{#include libavutil/timestamp.h#include libavformat/avformat.h
}typedef struct
{char* file_name;AVFormatContext* fmt_ctx;int video_index;int audio_index;int source_index;double last_pts[2];double last_dts[2];AVRational video_time_base;AVRational audio_time_base;bool is_end;
}InputStream;int create_stream(InputStream *input_stream, AVFormatContext* out_fmt_ctx, int stream_num)
{int i 0;int ret 0;if ((ret avformat_open_input(input_stream-fmt_ctx, input_stream-file_name, 0, 0)) 0) //打开输出文件{fprintf(stderr, Could not open input file %s, input_stream-file_name);goto end;}if ((ret avformat_find_stream_info(input_stream-fmt_ctx, 0)) 0) //打开输入{fprintf(stderr, Failed to retrieve input stream information);goto end;}//av_dump_format(input_stream-fmt_ctx, 0, input_stream-file_name, 0);//打印信息for (size_t i 0; i input_stream-fmt_ctx-nb_streams; i){AVStream* in_stream input_stream-fmt_ctx-streams[i];AVStream* out_stream avformat_new_stream(out_fmt_ctx, in_stream-codec-codec);if (in_stream-codecpar-codec_type AVMEDIA_TYPE_AUDIO){input_stream-audio_index i;input_stream-audio_time_base in_stream-time_base;printf(流 %d 是音频 \n, input_stream-source_index i);}if (in_stream-codecpar-codec_type AVMEDIA_TYPE_VIDEO){input_stream-video_index i;input_stream-video_time_base in_stream-time_base;printf(流 %d 是视频 \n, input_stream-source_index i);}if (!out_stream){fprintf(stderr, Failed allocating output stream\n);goto end;}ret avcodec_copy_context(out_stream-codec, in_stream-codec);if (ret 0){fprintf(stderr, Failed to copy context from input to output stream codec context\n);goto end;}out_stream-codec-codec_tag 0;if (out_fmt_ctx-oformat-flags AVFMT_GLOBALHEADER)out_stream-codec-flags | AV_CODEC_FLAG_GLOBAL_HEADER;stream_num;}
end:return ret;
}int write_a_channel(InputStream *stream, AVFormatContext *out_fmt_ctx)
{int ret -1;AVPacket packet;ret av_read_frame(stream-fmt_ctx, packet);if (ret 0){if (ret AVERROR_EOF){//printf(readFrame报错 %d\n, ret);av_free_packet(packet);return ret;}}AVRational time_base {0};AVStream* in_stream stream-fmt_ctx-streams[packet.stream_index];AVStream* out_stream out_fmt_ctx-streams[packet.stream_index];packet.stream_index stream-source_index packet.stream_index;if (in_stream-codecpar-codec_type AVMEDIA_TYPE_VIDEO){time_base stream-video_time_base;}if (in_stream-codecpar-codec_type AVMEDIA_TYPE_AUDIO){time_base stream-audio_time_base;}packet.pts av_rescale_q_rnd(packet.pts, time_base, out_stream-time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));packet.dts av_rescale_q_rnd(packet.dts, time_base, out_stream-time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));if (packet.pts 0){packet.pts 0;}if (packet.dts 0){packet.dts 0;}//printf(%d, pts %d dts %d \n, packet.stream_index, packet.pts, packet.dts);ret av_interleaved_write_frame(out_fmt_ctx, packet);if (ret 0){printf(写入错误\n);}av_free_packet(packet);return ret;
}int main(int argc, char** argv)
{int ret -1;char file_path[100][100] {D:\\素材\\test1.mp4, D:\\素材\\test2.mp4, D:\\素材\\test3.mp4, D:\\素材\\test4.mp4};int count sizeof(file_path) / sizeof(char);const char *out_fileName my_muxing.mp4;av_register_all();AVOutputFormat* ofmt NULL;AVFormatContext* out_fmt_ctx NULL;const int channel_num 4;avformat_alloc_output_context2(out_fmt_ctx, NULL, NULL, out_fileName);//创建streams InputStream stream_arry[channel_num] {0};int stream_index 0;for (int i 0; i channel_num; i){ int stream_num 0;stream_arry[i].file_name file_path[i];stream_arry[i].source_index stream_index;ret create_stream(stream_arry[i], out_fmt_ctx, stream_index);stream_index stream_num;}if (!out_fmt_ctx){return -1;}ofmt out_fmt_ctx-oformat;if (!(ofmt-flags AVFMT_NOFILE)){ret avio_open(out_fmt_ctx-pb, out_fileName, AVIO_FLAG_WRITE);if (ret 0){fprintf(stderr, Could not open output file %s, out_fileName);return -1;}}AVDictionary* opts NULL;av_dict_set(opts, movflags, frag_keyframeempty_moov, 0);//fmp4输出ret avformat_write_header(out_fmt_ctx, opts);if (ret 0){fprintf(stderr, Error occurred when opening output file\n);return -1;}while (true){int finish_count 0;for (int i 0; i channel_num; i){if (!stream_arry[i].is_end){ret write_a_channel(stream_arry[i], out_fmt_ctx);if (ret AVERROR_EOF){stream_arry[i].is_end true;finish_count;}else{continue;}}}if (finish_count channel_num - 1){break;}}//清理for (size_t i 0; i channel_num; i){avformat_close_input(stream_arry[i].fmt_ctx);}ret av_write_trailer(out_fmt_ctx);printf(合并完毕写文件尾部 %d\n, ret);return 0;
}