ffmpeg综合应用示例(五)——多路视频合并(Linux版本)
本文的目的为方便Linux下编译运行多路视频合成Demo
原文:ffmpeg综合应用示例(五)——多路视频合并
Ubuntu 20.04 + ffmpeg version ffmpeg-4.4-x86_64
编译
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/workspace/dengzr/linux-x64/lib:/home/workspace/dengzr/ffmpeg-4.4-x86_64/lib/ D_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/workspace/dengzr/linux-x64/lib:/home/works
g++ -o video_combine video_combine.cpp -I /home/workspace/dengzr/ffmpeg-4.4-x86_64/include/ -L /home/workspace/dengzr/ffmpeg-4.4-x86_64/lib/ -Wl,-Bstatic -Wl,-Bdynamic -lpthread -Wl,-Bstatic -Wl,-Bdynamic -lstdc++ -Wl,-Bstatic -Wl,-Bdynamic -ldl -Wl,-Bstatic -Wl,-Bdynamic -lavcodec -lavformat -lswscale -lavutil -lavfilter -lfdk-aac -lvpx -lpostproc -lavfilter -lm -lva -lOpenCL -lmfx -lstdc++ -ldl -lavformat -lm -lz -lavcodec -lvpx -lm -lvpx -lm -lvpx -lm -lvpx -lm -lz -lfdk-aac -lvorbis -lvorbisenc -lx264 -lpthread -lm -ldl -lx265 -lva -lmfx -lstdc++ -ldl -lpostproc -lm -lswscale -lm -lswresample -lm -lavutil -lva-drm -lva -lva-x11 -lva -lm -ldrm -lmfx -lstdc++ -ldl -lOpenCL -lva -lX11 -lXext -lva -ldrm -lm -lpthread -lz -Wl,-Bstatic -Wl,-Bdynamic -lavcodec -lavformat -lswscale -lavutil -lavfilter -lfdk-aac -lvpx -lpostproc -lOpenCL -lmfx -lstdc++ -ldl -lvorbis -lvorbisenc -lx264 -lx265 -lswresample -lva-drm -lva-x11 -lX11 -lXext -Wl,-Bstatic -Wl,-Bdynamic -L /home/workspace/dengzr/ffmpeg-4.4-x86_64/lib/ -L /home/workspace/dengzr/linux-x64/lib -I /home/workspace/dengzr/linux-x64/include/ -lSDL2
Linux下修改后的代码
/**
*
* 张晖 Hui Zhang
* zhanghuicuc@gmail.com
* 中国传媒大学/数字电视技术
* Communication University of China / Digital TV Technology
*
* 本程序实现了对多路视频进行合并实现分屏效果,并且可以添加不同的视频滤镜。
* 目前还不支持音频合并
*/#include <stdio.h>
extern "C"
{
#include "libavutil/opt.h"
#include "libavutil/time.h"
#include "libavutil/mathematics.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
// #include "libavfilter/avfiltergraph.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
};
// #include <crtdbg.h>/*
FIX: H.264 in some container format (FLV, MP4, MKV etc.) need
"h264_mp4toannexb" bitstream filter (BSF)
*Add SPS,PPS in front of IDR frame
*Add start code ("0,0,0,1") in front of NALU
H.264 in some container (MPEG2TS) don't need this BSF.
*/
//'1': Use H.264 Bitstream Filter
#define USE_H264BSF 0 /*
FIX:AAC in some container format (FLV, MP4, MKV etc.) need
"aac_adtstoasc" bitstream filter (BSF)
*/
//'1': Use AAC Bitstream Filter
#define USE_AACBSF 0 typedef enum{VFX_NULL = 0,VFX_EDGE = 1,VFX_NEGATE = 2
}VFX;typedef enum{AFX_NULL = 0,
}AFX;AVBitStreamFilterContext *aacbsfc = NULL;
AVBitStreamFilterContext* h264bsfc = NULL;
//multiple input
static AVFormatContext **ifmt_ctx;
AVFrame **frame = NULL;
//single output
static AVFormatContext *ofmt_ctx;typedef struct FilteringContext {AVFilterContext *buffersink_ctx;AVFilterContext **buffersrc_ctx;AVFilterGraph *filter_graph;
} FilteringContext;
static FilteringContext *filter_ctx;typedef struct InputFile{const char* filenames;/** position index* 0 - 1 - 2* 3 - 4 - 5* 6 - 7 - 8* ……*/uint32_t video_idx;//scale level, 0 means keep the same//uint32_t video_expand;uint32_t video_effect;uint32_t audio_effect;
} InputFile;
InputFile* inputfiles;typedef struct GlobalContext{//always be a square,such as 2x2, 3x3uint32_t grid_num;uint32_t video_num;uint32_t enc_width;uint32_t enc_height;uint32_t enc_bit_rate;InputFile* input_file;const char* outfilename;
} GlobalContext;
GlobalContext* global_ctx;static int global_ctx_config()
{int i;if (global_ctx->grid_num < global_ctx->video_num){av_log(NULL, AV_LOG_ERROR, "Setting a wrong grid_num %d \t The grid_num is smaller than video_num!! \n", global_ctx->grid_num);global_ctx->grid_num = global_ctx->video_num;//global_ctx->stride = sqrt((double)global_ctx->grid_num);av_log(NULL, AV_LOG_ERROR, "Automatically change the grid_num to be same as video_num!! \n");}//global_ctx->stride = sqrt((double)global_ctx->grid_num);for (i = 0; i < global_ctx->video_num; i++){if (global_ctx->input_file[i].video_idx >= global_ctx->grid_num){av_log(NULL, AV_LOG_ERROR, "Invalid video_inx value in the No.%d input\n", global_ctx->input_file[i].video_idx);return -1;}}return 0;
}static int open_input_file(InputFile *input_file)
{int ret;unsigned int i;unsigned int j;ifmt_ctx = (AVFormatContext**)av_malloc((global_ctx->video_num)*sizeof(AVFormatContext*));for (i = 0; i < global_ctx->video_num; i++){*(ifmt_ctx + i) = NULL;if ((ret = avformat_open_input((ifmt_ctx + i), input_file[i].filenames, NULL, NULL)) < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");return ret;}if ((ret = avformat_find_stream_info(ifmt_ctx[i], NULL)) < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");return ret;}for (j = 0; j < ifmt_ctx[i]->nb_streams; j++) {AVStream *stream;AVCodecContext *codec_ctx;stream = ifmt_ctx[i]->streams[j];codec_ctx = stream->codec;if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO|| codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {/* Open decoder */ret = avcodec_open2(codec_ctx,avcodec_find_decoder(codec_ctx->codec_id), NULL);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream #%u\n", i);return ret;}}}av_dump_format(ifmt_ctx[i], 0, input_file[i].filenames, 0);}return 0;
}static int open_output_file(const char *filename)
{AVStream *out_stream;AVStream *in_stream;AVCodecContext *dec_ctx, *enc_ctx;AVCodec *encoder;int ret;unsigned int i;ofmt_ctx = NULL;avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", filename);if (!ofmt_ctx) {av_log(NULL, AV_LOG_ERROR, "Could not create output context\n");return AVERROR_UNKNOWN;}for (i = 0; i < ifmt_ctx[0]->nb_streams; i++) {out_stream = avformat_new_stream(ofmt_ctx, NULL);if (!out_stream) {av_log(NULL, AV_LOG_ERROR, "Failed allocating output stream\n");return AVERROR_UNKNOWN;}in_stream = ifmt_ctx[0]->streams[i];out_stream->time_base = in_stream->time_base;dec_ctx = in_stream->codec;enc_ctx = out_stream->codec;if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO|| dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { /* In this example, we transcode to same properties (picture size,* sample rate etc.). These properties can be changed for output* streams easily using filters */if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {/* in this example, we choose transcoding to same codec */encoder = avcodec_find_encoder(AV_CODEC_ID_H264);enc_ctx->height = global_ctx->enc_height;enc_ctx->width = global_ctx->enc_width;enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;/* take first format from list of supported formats */enc_ctx->pix_fmt = encoder->pix_fmts[0];enc_ctx->me_range = 16;enc_ctx->max_qdiff = 4;enc_ctx->bit_rate = global_ctx->enc_bit_rate;enc_ctx->qcompress = 0.6;/* video time_base can be set to whatever is handy and supported by encoder */enc_ctx->time_base.num = 1;enc_ctx->time_base.den = 25;enc_ctx->gop_size = 250;enc_ctx->max_b_frames = 3;AVDictionary * d = NULL;char *k = av_strdup("preset"); // if your strings are already allocated,char *v = av_strdup("ultrafast"); // you can avoid copying them like thisav_dict_set(&d, k, v, AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);ret = avcodec_open2(enc_ctx, encoder, &d);}else {encoder = avcodec_find_encoder(AV_CODEC_ID_AAC);enc_ctx->sample_rate = dec_ctx->sample_rate;enc_ctx->channel_layout = dec_ctx->channel_layout;enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);/* take first format from list of supported formats */enc_ctx->sample_fmt = encoder->sample_fmts[0];AVRational time_base = { 1, enc_ctx->sample_rate };enc_ctx->time_base = time_base;ret = avcodec_open2(enc_ctx, encoder, NULL);}if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot open video encoder for stream #%u\n", i);return ret;} }else if (dec_ctx->codec_type == AVMEDIA_TYPE_UNKNOWN) {av_log(NULL, AV_LOG_FATAL, "Elementary stream #%d is of unknown type, cannot proceed\n", i);return AVERROR_INVALIDDATA;}else {/* if this stream must be remuxed */ret = avcodec_copy_context(ofmt_ctx->streams[i]->codec,ifmt_ctx[0]->streams[i]->codec);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Copying stream context failed\n");return ret;}}if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;}av_dump_format(ofmt_ctx, 0, filename, 1);if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {ret = avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Could not open output file '%s'", filename);return ret;}}/* init muxer, write output file header */ret = avformat_write_header(ofmt_ctx, NULL);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Error occurred when opening output file\n");return ret;}#if USE_H264BSF h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
#endif
#if USE_AACBSF aacbsfc = av_bitstream_filter_init("aac_adtstoasc");
#endif return 0;
}static int init_filter(FilteringContext* fctx, AVCodecContext **dec_ctx,AVCodecContext *enc_ctx, const char *filter_spec)
{char args[512];char pad_name[10];int ret = 0;int i;AVFilter **buffersrc = (AVFilter**)av_malloc(global_ctx->video_num*sizeof(AVFilter*));AVFilter *buffersink = NULL;AVFilterContext **buffersrc_ctx = (AVFilterContext**)av_malloc(global_ctx->video_num*sizeof(AVFilterContext*));AVFilterContext *buffersink_ctx = NULL;AVFilterInOut **outputs = (AVFilterInOut**)av_malloc(global_ctx->video_num*sizeof(AVFilterInOut*));AVFilterInOut *inputs = avfilter_inout_alloc();AVFilterGraph *filter_graph = avfilter_graph_alloc();for (i = 0; i < global_ctx->video_num; i++){buffersrc[i] = NULL;buffersrc_ctx[i] = NULL;outputs[i] = avfilter_inout_alloc();}if (!outputs || !inputs || !filter_graph) {ret = AVERROR(ENOMEM);goto end;}if (dec_ctx[0]->codec_type == AVMEDIA_TYPE_VIDEO) {for (i = 0; i < global_ctx->video_num; i++){buffersrc[i] = (AVFilter *)avfilter_get_by_name("buffer");}buffersink = (AVFilter *)avfilter_get_by_name("buffersink");if (!buffersrc || !buffersink) {av_log(NULL, AV_LOG_ERROR, "filtering source or sink element not found\n");ret = AVERROR_UNKNOWN;goto end;}for (i = 0; i < global_ctx->video_num; i++){snprintf(args, sizeof(args),"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",dec_ctx[i]->width, dec_ctx[i]->height, dec_ctx[i]->pix_fmt,dec_ctx[i]->time_base.num, dec_ctx[i]->time_base.den,dec_ctx[i]->sample_aspect_ratio.num,dec_ctx[i]->sample_aspect_ratio.den);snprintf(pad_name, sizeof(pad_name), "in%d", i);ret = avfilter_graph_create_filter(&(buffersrc_ctx[i]), buffersrc[i], pad_name,args, NULL, filter_graph);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");goto end;}}ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",NULL, NULL, filter_graph);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");goto end;}ret = av_opt_set_bin(buffersink_ctx, "pix_fmts",(uint8_t*)&enc_ctx->pix_fmt, sizeof(enc_ctx->pix_fmt),AV_OPT_SEARCH_CHILDREN);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n");goto end;}}else if (dec_ctx[0]->codec_type == AVMEDIA_TYPE_AUDIO) {for (i = 0; i < global_ctx->video_num; i++){buffersrc[i] = (AVFilter *)avfilter_get_by_name("abuffer");}buffersink = (AVFilter *)avfilter_get_by_name("abuffersink");if (!buffersrc || !buffersink) {av_log(NULL, AV_LOG_ERROR, "filtering source or sink element not found\n");ret = AVERROR_UNKNOWN;goto end;}for (i = 0; i < global_ctx->video_num; i++){if (!dec_ctx[i]->channel_layout)dec_ctx[i]->channel_layout =av_get_default_channel_layout(dec_ctx[i]->channels);snprintf(args, sizeof(args),"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%I64x",dec_ctx[i]->time_base.num, dec_ctx[i]->time_base.den, dec_ctx[i]->sample_rate,av_get_sample_fmt_name(dec_ctx[i]->sample_fmt),dec_ctx[i]->channel_layout);snprintf(pad_name, sizeof(pad_name), "in%d", i);ret = avfilter_graph_create_filter(&(buffersrc_ctx[i]), buffersrc[i], pad_name,args, NULL, filter_graph);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");goto end;}}if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source\n");goto end;}ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",NULL, NULL, filter_graph);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer sink\n");goto end;}ret = av_opt_set_bin(buffersink_ctx, "sample_fmts",(uint8_t*)&enc_ctx->sample_fmt, sizeof(enc_ctx->sample_fmt),AV_OPT_SEARCH_CHILDREN);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot set output sample format\n");goto end;}ret = av_opt_set_bin(buffersink_ctx, "channel_layouts",(uint8_t*)&enc_ctx->channel_layout,sizeof(enc_ctx->channel_layout), AV_OPT_SEARCH_CHILDREN);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot set output channel layout\n");goto end;}ret = av_opt_set_bin(buffersink_ctx, "sample_rates",(uint8_t*)&enc_ctx->sample_rate, sizeof(enc_ctx->sample_rate),AV_OPT_SEARCH_CHILDREN);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Cannot set output sample rate\n");goto end;}}/* Endpoints for the filter graph. */for (i = 0; i < global_ctx->video_num; i++){snprintf(pad_name, sizeof(pad_name), "in%d", i);outputs[i]->name = av_strdup(pad_name);outputs[i]->filter_ctx = buffersrc_ctx[i];outputs[i]->pad_idx = 0;if (i == global_ctx->video_num - 1)outputs[i]->next = NULL;elseoutputs[i]->next = outputs[i + 1];}inputs->name = av_strdup("out");inputs->filter_ctx = buffersink_ctx;inputs->pad_idx = 0;inputs->next = NULL;if (!outputs[0]->name || !inputs->name) {ret = AVERROR(ENOMEM);goto end;}if ((ret = avfilter_graph_parse_ptr(filter_graph, filter_spec,&inputs, outputs, NULL)) < 0)goto end;if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)goto end;/* Fill FilteringContext */fctx->buffersrc_ctx = buffersrc_ctx;fctx->buffersink_ctx = buffersink_ctx;fctx->filter_graph = filter_graph;
end:avfilter_inout_free(&inputs);av_free(buffersrc);// av_free(buffersrc_ctx);avfilter_inout_free(outputs);av_free(outputs);return ret;
}static int init_spec_filter(void)
{char filter_spec[512];char spec_temp[128];unsigned int i;unsigned int j;unsigned int k;unsigned int x_coor;unsigned int y_coor;AVCodecContext** dec_ctx_array;int stream_num = ifmt_ctx[0]->nb_streams;int stride = (int)sqrt((long double)global_ctx->grid_num);int ret;filter_ctx = (FilteringContext *)av_malloc_array(stream_num, sizeof(*filter_ctx));dec_ctx_array = (AVCodecContext**)av_malloc(global_ctx->video_num*sizeof(AVCodecContext));if (!filter_ctx || !dec_ctx_array)return AVERROR(ENOMEM);for (i = 0; i < stream_num; i++) {filter_ctx[i].buffersrc_ctx = NULL;filter_ctx[i].buffersink_ctx = NULL;filter_ctx[i].filter_graph = NULL;for (j = 0; j < global_ctx->video_num; j++)dec_ctx_array[j] = ifmt_ctx[j]->streams[i]->codec;if (!(ifmt_ctx[0]->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO|| ifmt_ctx[0]->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO))continue;if (ifmt_ctx[0]->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){if (global_ctx->grid_num == 1)snprintf(filter_spec, sizeof(filter_spec), "null");else{snprintf(filter_spec, sizeof(filter_spec), "color=c=black@1:s=%dx%d[x0];", global_ctx->enc_width, global_ctx->enc_height);k = 1;for (j = 0; j < global_ctx->video_num; j++){switch (global_ctx->input_file[j].video_effect){case VFX_NULL:snprintf(spec_temp, sizeof(spec_temp), "[in%d]null[ine%d];", j, j);strcat(filter_spec, spec_temp);break;case VFX_EDGE:snprintf(spec_temp, sizeof(spec_temp), "[in%d]edgedetect[ine%d];", j, j);strcat(filter_spec, spec_temp);break;case VFX_NEGATE:snprintf(spec_temp, sizeof(spec_temp), "[in%d]negate[ine%d];", j, j);strcat(filter_spec, spec_temp);break;}x_coor = global_ctx->input_file[j].video_idx % stride;y_coor = global_ctx->input_file[j].video_idx / stride;snprintf(spec_temp, sizeof(spec_temp), "[ine%d]scale=w=%d:h=%d[inn%d];[x%d][inn%d]overlay=%d*%d/%d:%d*%d/%d[x%d];", j, global_ctx->enc_width / stride, global_ctx->enc_height / stride, j, k - 1, j, global_ctx->enc_width, x_coor, stride, global_ctx->enc_height, y_coor, stride, k);k++;strcat(filter_spec, spec_temp);}snprintf(spec_temp, sizeof(spec_temp), "[x%d]null[out]", k - 1, global_ctx->enc_width, global_ctx->enc_height);strcat(filter_spec, spec_temp);}}else{if (global_ctx->video_num == 1)snprintf(filter_spec, sizeof(filter_spec), "anull");else{snprintf(filter_spec, sizeof(filter_spec), "");for (j = 0; j < global_ctx->video_num; j++){snprintf(spec_temp, sizeof(spec_temp), "[in%d]", j);strcat(filter_spec, spec_temp);}snprintf(spec_temp, sizeof(spec_temp), "amix=inputs=%d[out]", global_ctx->video_num);strcat(filter_spec, spec_temp);}}ret = init_filter(&filter_ctx[i], dec_ctx_array,ofmt_ctx->streams[i]->codec, filter_spec);if (ret)return ret;}av_free(dec_ctx_array);return 0;
}int videocombine(GlobalContext* video_ctx)
{int ret;int tmp = 0;int got_frame_num = 0;unsigned int stream_index;AVPacket packet;AVPacket enc_pkt;AVFrame* picref;enum AVMediaType mediatype;int read_frame_done = 0;int flush_now = 0;int framecnt = 0;int i, j;int got_frame;int enc_got_frame = 0;int(*dec_func)(AVCodecContext *, AVFrame *, int *, const AVPacket *);int(*enc_func)(AVCodecContext *, AVPacket *, const AVFrame *, int *);global_ctx = video_ctx;global_ctx_config();frame = (AVFrame**)av_malloc(global_ctx->video_num*sizeof(AVFrame*));picref = av_frame_alloc();av_register_all();avfilter_register_all();if ((ret = open_input_file(global_ctx->input_file)) < 0)goto end;if ((ret = open_output_file(global_ctx->outfilename)) < 0)goto end;if ((ret = init_spec_filter()) < 0)goto end;while (1) {for (i = 0; i < global_ctx->video_num; i++){if (read_frame_done < 0){flush_now = 1;goto flush;} while ((read_frame_done=av_read_frame(ifmt_ctx[i], &packet)) >= 0){stream_index = packet.stream_index;mediatype = ifmt_ctx[i]->streams[stream_index]->codec->codec_type;if (mediatype == AVMEDIA_TYPE_VIDEO || mediatype == AVMEDIA_TYPE_AUDIO){frame[i] = av_frame_alloc();if (!(frame[i])){ret = AVERROR(ENOMEM);goto end;}av_packet_rescale_ts(&packet,ifmt_ctx[i]->streams[stream_index]->time_base,ifmt_ctx[i]->streams[stream_index]->codec->time_base);dec_func = (mediatype == AVMEDIA_TYPE_VIDEO) ? avcodec_decode_video2 : avcodec_decode_audio4;ret = dec_func(ifmt_ctx[i]->streams[stream_index]->codec, frame[i], &got_frame, &packet);if (ret < 0){av_frame_free(&frame[i]);av_log(NULL, AV_LOG_ERROR, "Decoding failed\n");goto end;}if (got_frame) {frame[i]->pts = av_frame_get_best_effort_timestamp(frame[i]);ret = av_buffersrc_add_frame(filter_ctx[stream_index].buffersrc_ctx[i], frame[i]);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n");goto end;}}else{av_frame_free(&(frame[i]));}}av_free_packet(&packet);if (got_frame){got_frame = 0;break;}}}while (1) {ret = av_buffersink_get_frame_flags(filter_ctx[stream_index].buffersink_ctx, picref, 0);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){ret = 0;break;}if (ret < 0)goto end;if (picref) { enc_pkt.data = NULL;enc_pkt.size = 0;av_init_packet(&enc_pkt);enc_func = (mediatype == AVMEDIA_TYPE_VIDEO) ? avcodec_encode_video2 : avcodec_encode_audio2;ret = enc_func(ofmt_ctx->streams[stream_index]->codec, &enc_pkt,picref, &enc_got_frame); if (ret < 0){av_log(NULL, AV_LOG_ERROR, "Encoding failed\n");goto end;}if (enc_got_frame == 1){framecnt++;enc_pkt.stream_index = stream_index;//Write PTSAVRational time_base = ofmt_ctx->streams[stream_index]->time_base;//{ 1, 1000 };AVRational r_framerate1 = ifmt_ctx[0]->streams[stream_index]->r_frame_rate;// { 50, 2 };AVRational time_base_q = { 1, AV_TIME_BASE };//Duration between 2 frames (us)int64_t calc_duration = (double)(AV_TIME_BASE)*(1 / av_q2d(r_framerate1)); //内部时间戳//Parameters//enc_pkt.pts = (double)(framecnt*calc_duration)*(double)(av_q2d(time_base_q)) / (double)(av_q2d(time_base));enc_pkt.pts = av_rescale_q(framecnt*calc_duration, time_base_q, time_base);enc_pkt.dts = enc_pkt.pts;enc_pkt.duration = av_rescale_q(calc_duration, time_base_q, time_base); //(double)(calc_duration)*(double)(av_q2d(time_base_q)) / (double)(av_q2d(time_base));enc_pkt.pos = -1;#if USE_H264BSF av_bitstream_filter_filter(h264bsfc, in_stream->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);
#endif
#if USE_AACBSF av_bitstream_filter_filter(aacbsfc, out_stream->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);
#endif ret = av_interleaved_write_frame(ofmt_ctx, &enc_pkt);av_log(NULL, AV_LOG_INFO, "write frame %d\n", framecnt);av_free_packet(&enc_pkt);}av_frame_unref(picref);} }}flush:/* flush filters and encoders */for (i = 0; i < ifmt_ctx[0]->nb_streams; i++) {stream_index = i;/* flush encoder */if (!(ofmt_ctx->streams[stream_index]->codec->codec->capabilities &AV_CODEC_CAP_DELAY))return 0;while (1) {enc_pkt.data = NULL;enc_pkt.size = 0;av_init_packet(&enc_pkt);enc_func = (ifmt_ctx[0]->streams[stream_index]->codec->codec_type == AVMEDIA_TYPE_VIDEO) ?avcodec_encode_video2 : avcodec_encode_audio2;ret = enc_func(ofmt_ctx->streams[stream_index]->codec, &enc_pkt,NULL, &enc_got_frame);av_frame_free(NULL);if (ret < 0){av_log(NULL, AV_LOG_ERROR, "Encoding failed\n");goto end;}if (!enc_got_frame){ret = 0;break;}printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n", enc_pkt.size);//Write PTSAVRational time_base = ofmt_ctx->streams[stream_index]->time_base;//{ 1, 1000 };AVRational r_framerate1 = ifmt_ctx[0]->streams[stream_index]->r_frame_rate;// { 50, 2 };AVRational time_base_q = { 1, AV_TIME_BASE };//Duration between 2 frames (us)int64_t calc_duration = (double)(AV_TIME_BASE)*(1 / av_q2d(r_framerate1)); //内部时间戳//Parametersenc_pkt.pts = av_rescale_q(framecnt*calc_duration, time_base_q, time_base);enc_pkt.dts = enc_pkt.pts;enc_pkt.duration = av_rescale_q(calc_duration, time_base_q, time_base);/* copy packet*///转换PTS/DTS(Convert PTS/DTS)enc_pkt.pos = -1;framecnt++;ofmt_ctx->duration = enc_pkt.duration * framecnt;/* mux encoded frame */ret = av_interleaved_write_frame(ofmt_ctx, &enc_pkt);}}av_write_trailer(ofmt_ctx);#if USE_H264BSF av_bitstream_filter_close(h264bsfc);
#endif
#if USE_AACBSF av_bitstream_filter_close(aacbsfc);
#endif end:av_free_packet(&packet);for (i = 0; i < global_ctx->video_num; i++){av_frame_free(&(frame[i]));for (j = 0; j < ofmt_ctx->nb_streams; j++) {avcodec_close(ifmt_ctx[i]->streams[j]->codec);}}av_free(frame);av_free(picref);for (i = 0; i<ofmt_ctx->nb_streams; i++){if (ofmt_ctx && ofmt_ctx->nb_streams > i && ofmt_ctx->streams[i] && ofmt_ctx->streams[i]->codec)avcodec_close(ofmt_ctx->streams[i]->codec);av_free(filter_ctx[i].buffersrc_ctx);if (filter_ctx && filter_ctx[i].filter_graph)avfilter_graph_free(&filter_ctx[i].filter_graph);}av_free(filter_ctx);for (i = 0; i < global_ctx->video_num; i++)avformat_close_input(&(ifmt_ctx[i]));if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE))avio_close(ofmt_ctx->pb);avformat_free_context(ofmt_ctx);av_free(ifmt_ctx);if (ret < 0)av_log(NULL, AV_LOG_ERROR, "Error occurred\n");return (ret ? 1 : 0);
}int main(int argc, char **argv)
{//test 2x2inputfiles = (InputFile*)av_malloc_array(4, sizeof(InputFile));if (!inputfiles)return AVERROR(ENOMEM);inputfiles[0].filenames = "in1.flv";//inputfiles[0].video_expand = 0;inputfiles[0].video_idx = 0;inputfiles[0].video_effect = VFX_EDGE;inputfiles[0].audio_effect = AFX_NULL;inputfiles[1].filenames = "in2.flv";//inputfiles[1].video_expand = 0;inputfiles[1].video_idx = 1;inputfiles[1].video_effect = VFX_NULL;inputfiles[1].audio_effect = AFX_NULL;inputfiles[2].filenames = "in3.flv";//inputfiles[2].video_expand = 0;inputfiles[2].video_idx = 2;inputfiles[2].video_effect = VFX_NULL;inputfiles[2].audio_effect = AFX_NULL;inputfiles[3].filenames = "in4.flv";//inputfiles[3].video_expand = 0;inputfiles[3].video_idx = 3;inputfiles[3].video_effect = VFX_NEGATE;inputfiles[3].audio_effect = AFX_NULL;global_ctx = (GlobalContext*)av_malloc(sizeof(GlobalContext));global_ctx->video_num = 4;global_ctx->grid_num = 4;global_ctx->enc_bit_rate = 500000;global_ctx->enc_height = 360;global_ctx->enc_width = 640;global_ctx->outfilename = "combined.flv";global_ctx->input_file = inputfiles;return videocombine(global_ctx);
}相关文章:
ffmpeg综合应用示例(五)——多路视频合并(Linux版本)
本文的目的为方便Linux下编译运行多路视频合成Demo 原文:ffmpeg综合应用示例(五)——多路视频合并 Ubuntu 20.04 ffmpeg version ffmpeg-4.4-x86_64 编译 export LD_LIBRARY_PATH$LD_LIBRARY_PATH:/home/workspace/dengzr/linux-x64/lib…...
Node.js-http模块服务端请求与响应操作,请求报文与响应报文
简单案例创建HTTP服务端: // 导入 http 模块 const http require("http"); // 创建服务对象 const server http.createServer((request, response) > {// 设置编码格式,解决中文乱码问题response.setHeader("content-type", &…...
除了PS,还有那些软件可以打开PSD文件
设计师在交接文件时,会看到各种格式的扩展文件,不同的格式需要不同的软件来运行。大多数人都听说过流行的文件格式PSD,因为它是最常用的图片格式之一,还有JPG、PNG等。然而,与JPG和PNG不同的是,PSD格式文件…...
uniapp h5支付宝支付后端返回Form表单,前端如何处理
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言1.调取接口拿到后端返回的form表单 前言 uniapp h5 支付宝支付,后端返回一串form表单,前端如何拿到支付串并且调用支付 1.调取接口拿到…...
【华秋干货铺】PCB布线技巧升级:高速信号篇
如下表所示,接口信号能工作在8Gbps及以上速率,由于速率很高,PCB布线设计要求会更严格,在前几篇关于PCB布线内容的基础上,还需要根据本篇内容的要求来进行PCB布线设计。 高速信号布线时尽量少打孔换层,换层优…...
c#:ObservableCollection<T>的用法
1.说明: ObservableCollection:表示一个动态数据收集,该集合在添加或删除项或刷新整个列表时提供通知。 2.使用: 首先声明一个类 public ObservableCollection ProItems;//具体情况具体写对应的信息 表格DataGrid案例ÿ…...
Linux 端口号占用如何处理(使用命令处理)
查看被占用端口号 sudo netstat -tlnp 端口号 示例: sudo netstat -tlnp 3380杀死进程 sudo kill 进程Id sudo kill 11032...
ubuntu git操作记录设置ssh key
用到的命令: 安装git sudo apt-get install git配置git用户和邮箱 git config --global user.name “用户名” git config --global user.email “邮箱地址”安装ssh sudo apt-get install ssh然后查看安装状态: ps -e | grep sshd4. 查看有无ssh k…...
SystemVerilog数组参数传递及引用方法总结
一、将常数数组传递给task/function 如下面的程序,将一个常数数组传递给function module my_array_test();function array_test(int array[4]);foreach(array[i]) begin$display("array[%0d] %0d", i, array[i]);endendfunctioninitial beginarray_tes…...
Shell脚本学习-While循环1
当型循环和直到型循环: 循环语句常用于重复执行一条指令或一组指令,直到条件不满足时停止。 在企业实际应用中,常用于守护进程或者持续运行的程序。 while语法结构: while 条件 do指令... done while循环语句对后面的条件表达…...
docker for Windows, WSL2 ,Hyper-v的关系
Hyper-v Hyper-V是由微软开发的一种虚拟化技术和虚拟机管理器。它允许在Windows操作系统上创建和运行多个虚拟机实例,每个虚拟机可以运行独立的操作系统和应用程序。属于硬件虚拟化。 WSL2 WSL2在技术上与WSL有很大的不同。在WSL2中,Windows 10引入了…...
SAS-数据集SQL水平合并
一、SQL水平合并基本语法 sql的合并有两步,step1:进行笛卡尔乘积运算,第一个表的每一行合并第二个表的每一行,即表a有3行,表b有3行,则合并后3*39行。笛卡尔过程包含源数据的所有列,相同列名会合…...
企业既要用u盘又要防止u盘泄密怎么办?
企业在日常生产生活过程中,使用u盘交换数据是最企业最常用也是最便携的方式,但是在使用u盘的同时,也给企业的数据保密工作带来了很大的挑战,往往很多情况下企业的是通过u盘进行数据泄漏的。很多企业采用一刀切的方式,直…...
汉明距离,两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。
题记: 两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。 给你两个整数 x 和 y,计算并返回它们之间的汉明距离。 示例 1: 输入:x 1, y 4 输出:2 解释: 1 (0 0 0 1) 4 (0 1 0 0…...
Android 之 使用 Camera 拍照
本节引言 本节给大家带来的是Android中Camera的使用,简单点说就是拍照咯,无非两种: 1.调用系统自带相机拍照,然后获取拍照后的图片 2.要么自己写个拍照页面 本节我们来写两个简单的例子体验下上面的这两种情况~ 1.调用系统自带…...
盘点7月Sui生态发展,了解Sui的近期成长历程!
自5月Sui主网上线三个月以来,7月是Sui网络进行最多次重要更新的一个月,Sui网络和生态正呈指数形式不断向上发展。为吸引更多的项目或开发者加入生态构建以及活跃用户参与生态,Sui基金会推出了Builder House、黑客松、Bullshark Quests、NFT再…...
6.物联网操作系统信号量
一。信号量的概念与应用 信号量定义 FreeRTOS信号量介绍 FreeRTOS信号量工作原理 1.信号量的定义 多任务环境下使用,用来协调多个任务正确合理使用临界资源。 2.FreeRTOS信号量介绍 Semaphore包括Binary,Count,Mutex; Mutex包…...
《向量数据库指南》——使用Milvus Cloud操作员安装Milvus Cloud独立版
Milvus cloud操作员HelmDocker Compose Milvus cloud Operator是一种解决方案,帮助您在目标Kubernetes(K8s)集群上部署和管理完整的Milvus cloud服务堆栈。该堆栈包含所有Milvus cloud组件和相关依赖项,如etcd、Pulsar和MinIO。本主题介绍如何使用Milvus cloud Operator安…...
Redis的基础知识
目录 一、什么是Redis 二、关于Redis的一些基本知识 (1)set命令 (2)get命令 三、Redis中的一些常用命令 (1)keys (2)exists (3)type (4…...
Sorting Layer与Order in Layer
就像是两个数相比,比如34与26,Sorting Layer决定的是十位,而Order in Layer决定的是个位,如果Sorting Layer的级别比较高,则可以忽略Order in Layer的比较,当比较的二者的Sorting Layer级别相同,…...
游戏服务器检测扣除消耗防算数溢出的安全判断及解决方法
游戏服务器检测扣除消耗防算数溢出的安全判断及解决方法 数量 > (类型最大值 / 价格) 负数存在风险 价格 > (类型最大值 / 数量) || 价格 < (最小值 / 数量) 游戏服务器在处理道具消耗时需防止数值溢出问题。当检测扣除消耗时,应进行双重安全判…...
电子测试岗面试翻车实录:我的硬件知识与英语短板,以及如何逆袭”
一:首先进行英文的自我介绍Hello, my name isxxx .你好,我叫xxx。I’m 20 years old, and I’m currently a third-year student majoring inElectronic Information Engineering at xxxx我今年20岁,目前是xxx电子信息工程专业的大三学生。My…...
Phi-4-mini-reasoning实战教程:批量处理CSV数学题库生成标准答案
Phi-4-mini-reasoning实战教程:批量处理CSV数学题库生成标准答案 1. 引言 数学老师们经常面临一个共同挑战:批改大量数学作业和试卷需要花费大量时间。传统方法需要逐题检查,效率低下且容易出错。今天,我们将介绍如何利用Phi-4-…...
500套帐篷发往西非:我们凭什么拿下这单?
一句吐槽,让我们抓住了机会年初,天津京路发科技收到一封西非询盘:500套支架帐篷,用于安置点。客户顺带吐槽了一句:“之前的帐篷,没撑过上一个雨季。”我们懂了——价格不是关键,耐造才是。先看气…...
大麦抢票自动化工具:技术赋能下的抢票效率革命
大麦抢票自动化工具:技术赋能下的抢票效率革命 【免费下载链接】DamaiHelper 大麦网演唱会演出抢票脚本。 项目地址: https://gitcode.com/gh_mirrors/dama/DamaiHelper 在热门演出门票抢购场景中,用户常常面临手动操作反应迟缓、重复劳动效率低下…...
告别CANoe依赖:手把手教你用Visual Studio 2019为UDS $27服务开发通用DLL(附Python调用脚本)
从零构建UDS安全访问DLL:Visual Studio 2019实战指南与Python无缝集成 在汽车电子诊断领域,UDS(Unified Diagnostic Services)协议的安全访问服务($27服务)是保护ECU敏感操作的核心机制。传统方案往往依赖C…...
香橙派Armbian系统下,用apt一键安装OpenCV的完整流程(含GPG报错解决)
香橙派Armbian系统下OpenCV-Python极简安装指南:绕过源码编译的终极方案 在单板计算机领域,香橙派凭借其出色的性价比逐渐崭露头角。当开发者尝试在这类ARM架构设备上构建计算机视觉应用时,OpenCV往往是不可或缺的核心工具。然而,…...
[LangChain智能体本质论-01]两种视角看待Agent和ReAct循环
作为LangChain智能体的Agent采用一种被称为ReAct循环的执行流程(如下图所示),这是一种结合了“推理”(Reasoning)与“行动”(Acting)的交互模式,旨在让Agent能像人类一样通过逻辑思考…...
从Java到AI Agent:传统后端工程师的下一站,不是学AI,是成为系统工程师!
文章探讨了在AI技术发展的背景下,传统后端工程师的转型方向。作者认为,未来的竞争焦点不再是单纯的技术能力,而是如何将AI技术融入现有系统,构建自动化系统。文章提出了AI Agent工程师的概念,强调系统工程能力的重要性…...
RK3568 Serdes方案调试:基于THCV244的I2C透传与MIPI CSI链路配置
1. RK3568与THCV244 Serdes方案概述 在车载摄像头和工业视觉应用中,Serdes(串行器/解串器)技术正变得越来越重要。RK3568作为一款高性能处理器,配合THCV244 Serdes芯片,能够实现远距离传感器数据的稳定传输。这套方案的…...
