用ffmpeg 进行视频的拼接


author: hjjdebug
date: 2025年 07月 22日 星期二 17:06:02 CST
descrip: 用ffmpeg 进行视频的拼接


文章目录

  • 1. 指定协议为concat 方式.
    • 1.1 协议为concat 模式,会调用 concat_open 函数
    • 1.2 当读数据时,会调用concat_read
  • 2. 指定file_format 为 concat 方式
    • 2.1 调用concat_read_header 时,读入文件信息
    • 2.2 调用concat_read_packet 来读取数据包
    • 2.3 怎样打开下一个文件
  • 3. 使用 filter concat

1. 指定协议为concat 方式.

举例:
ffmpeg -i “concat:short1.ts|1.ts” -c copy output.ts

工作原理:
在libavformat/concat.c 文件有该协议的实现

1.1 协议为concat 模式,会调用 concat_open 函数

会引起读写数据时,由concat协议控制从文件中读数据,当第一个文件读到尾时,
接着从第二个文件读
分析字符串:“concat:short1.ts|1.ts”, 找到文件名 “short1.ts”,“1.ts”,
用一个循环把文件都打开.
err = ffurl_open_whitelist(&uc, node_uri, flags,
&h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist, h);
并保留uc 到nodes
nodes[i].uc = uc;
nodes[i].size = size;
total_size += size;

1.2 当读数据时,会调用concat_read

static int concat_read(URLContext *h, unsigned char *buf, int size)
{int result, total = 0;struct concat_data  *data  = h->priv_data;  //拿到数据上下文struct concat_nodes *nodes = data->nodes;size_t i                   = data->current;while (size > 0) {result = ffurl_read(nodes[i].uc, buf, size); //从URLContext 中读取数据if (result == AVERROR_EOF) { //如果到了文件尾if (i + 1 == data->length ||   //存在下一个文件ffurl_seek(nodes[++i].uc, 0, SEEK_SET) < 0) //从下一个文件读取数据break;result = 0;}if (result < 0)return total ? total : result;total += result;buf   += result;size  -= result;}data->current = i;return total ? total : result;
}

2. 指定file_format 为 concat 方式

举例:
创建 filelist.txt 文件,内容如下:
file ‘short1.ts’
file ‘1.ts’
ffmpeg -f concat -i filelist.txt -c copy output.mp4 -y

工作原理:
在libavformat/concatdec.c 文件有该demuxer的实现
定义了concat_read_header, concat_read_packet, concat_seek 等函数
当指定format 为concat 会找到 concat_demuxer

2.1 调用concat_read_header 时,读入文件信息

具体代码:

static int concat_read_header(AVFormatContext *avf)
{ConcatContext *cat = avf->priv_data; //拿到上下文int64_t time = 0;unsigned i;int ret = concat_parse_script(avf); //分析输入文件if (ret < 0) return ret;for (i = 0; i < cat->nb_files; i++)  //枚举处理每一个输入文件, 但中途退出了.{if (cat->files[i].start_time == AV_NOPTS_VALUE)cat->files[i].start_time = time;elsetime = cat->files[i].start_time;if (cat->files[i].user_duration == AV_NOPTS_VALUE) {if (cat->files[i].inpoint == AV_NOPTS_VALUE || cat->files[i].outpoint == AV_NOPTS_VALUE ||cat->files[i].outpoint - (uint64_t)cat->files[i].inpoint != av_sat_sub64(cat->files[i].outpoint, cat->files[i].inpoint))break; //但这里中途退出了,相当于i==0, 没有给 duration 赋值cat->files[i].user_duration = cat->files[i].outpoint - cat->files[i].inpoint;}cat->files[i].duration = cat->files[i].user_duration;time += cat->files[i].user_duration;}if (i == cat->nb_files) {avf->duration = time;cat->seekable = 1;}cat->stream_match_mode = avf->nb_streams ? MATCH_EXACT_ID :MATCH_ONE_TO_ONE;if ((ret = open_file(avf, 0)) < 0) //前面代码都没有用, 此处avf已经知道了文件名,用第一个文件打开AVFormatCtxreturn ret;return 0;
}

2.2 调用concat_read_packet 来读取数据包

可以在这里控制从第一个文件中组包, 当第一个文件到达文件尾时,从第2个文件读包.
具体实现:

static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt)
{ConcatContext *cat = avf->priv_data;int ret;int64_t delta;ConcatStream *cs;AVStream *st;FFStream *sti;if (cat->eof)return AVERROR_EOF;if (!cat->avf)return AVERROR(EIO);while (1) {ret = av_read_frame(cat->avf, pkt);  // 靠 读取frame 来处理数据if (ret == AVERROR_EOF) { //文件到达尾部后if ((ret = open_next_file(avf)) < 0)  //切换到下一个文件,继续读包return ret;continue;}if (ret < 0) return ret;//流不匹配返回错误if ((ret = match_streams(avf)) < 0) {return ret;}//包位置判断if (packet_after_outpoint(cat, pkt)) {av_packet_unref(pkt);if ((ret = open_next_file(avf)) < 0)return ret;continue;}//获取ConcatStream 指针供后续使用cs = &cat->cur_file->streams[pkt->stream_index];if (cs->out_stream_index < 0) {av_packet_unref(pkt);continue;}break;}if ((ret = filter_packet(avf, cs, pkt)) < 0) return ret; //检查是否需要bsf处理,一般格式会直接返回st = cat->avf->streams[pkt->stream_index];sti = ffstream(st);//时间戳转换av_log(avf, AV_LOG_DEBUG, "file:%d stream:%d pts:%s pts_time:%s dts:%s dts_time:%s",(unsigned)(cat->cur_file - cat->files), pkt->stream_index,av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));delta = av_rescale_q(cat->cur_file->start_time - cat->cur_file->file_inpoint,AV_TIME_BASE_Q,cat->avf->streams[pkt->stream_index]->time_base);if (pkt->pts != AV_NOPTS_VALUE) pkt->pts += delta;if (pkt->dts != AV_NOPTS_VALUE) pkt->dts += delta;av_log(avf, AV_LOG_DEBUG, " -> pts:%s pts_time:%s dts:%s dts_time:%s\n",av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));//metadata 处理if (cat->cur_file->metadata) {size_t metadata_len;char* packed_metadata = av_packet_pack_dictionary(cat->cur_file->metadata, &metadata_len);if (!packed_metadata)return AVERROR(ENOMEM);ret = av_packet_add_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA,packed_metadata, metadata_len);if (ret < 0) {av_freep(&packed_metadata);return ret;}}//时间戳处理if (cat->cur_file->duration == AV_NOPTS_VALUE && sti->cur_dts != AV_NOPTS_VALUE) {int64_t next_dts = av_rescale_q(sti->cur_dts, st->time_base, AV_TIME_BASE_Q);if (cat->cur_file->next_dts == AV_NOPTS_VALUE || next_dts > cat->cur_file->next_dts) {cat->cur_file->next_dts = next_dts;}}//pkt流索引号pkt->stream_index = cs->out_stream_index;return 0;
}

2.3 怎样打开下一个文件

static int open_next_file(AVFormatContext *avf)
{ConcatContext *cat = avf->priv_data;          //拿到上下文unsigned fileno = cat->cur_file - cat->files; //取到文件号 0,1,2...cat->cur_file->duration = get_best_effort_duration(cat->cur_file, cat->avf); //获取当前文件时长if (++fileno >= cat->nb_files) { //文件号加1 并判断是否到尾.cat->eof = 1;return AVERROR_EOF;}return open_file(avf, fileno); //用fileno 打开AVFormatContext
}

concat 作为协议,与concat 作为demux 控制的时机是不同的.
前者是读数据的时候.
后者是组包的时候.

组包是先读取数据,再从数据中挑出有用的负载组成包.

3. 使用 filter concat

举例:
ffmpeg -i input1.mp4 -i input2.mp4 -filter_complex “[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1” output.mp4 -y
v=1 和 a=1:表示只保留 1 个视频流和 1 个音频流。

编码参数中不能够使用-c copy,
Filtering and streamcopy cannot be used together.
过滤器有解码和编码参与,使得执行速度大大降低,只有2-3倍速而已. 详细过程也没分析透,此处忽略.

其它filter 使用举例.
举例:由scale 和 crop:统一分辨率。然后用overlay进行叠加的过滤器链. 这里用了3个过滤器,scale,crop,overlay
ffmpeg -i input1.mp4 -i input2.mp4 -filter_complex “[0:v]scale=1280:720[bg];[1:v]crop=1280:720[fg];[bg][fg]overlay=0:0” output.mp4

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/915621.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/915621.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTTP与HTTPS技术细节及TLS密钥交换与证书校验全流程

HTTP与HTTPS技术细节及TLS密钥交换与证书校验全流程 引言 文档目的与范围 核心技术栈概述 本文档的核心技术栈围绕传输层安全协议&#xff08;TLS&#xff09;展开。TLS协议作为安全套接字层&#xff08;SSL&#xff09;的后继标准&#xff0c;是现代网络安全通信的基础&am…

广播分发中心-广播注册流程

广播是怎么注册的呢&#xff1f;阶段组件/数据结构作用描述存储位置/关联关系App进程阶段BroadcastReceiver开发者自定义的广播接收器&#xff0c;实现onReceive方法处理事件。App进程&#xff08;Activity/Service等组件内&#xff09;ReceiverDispatcher将BroadcastReceiver封…

OpenCV计算机视觉实战(16)——图像分割技术

OpenCV计算机视觉实战&#xff08;16&#xff09;——图像分割技术0. 前言1. 分水岭算法1.1 应用场景1.2 实现过程2. GrabCut 交互式分割2.1 应用场景2.2 实现过程3. FloodFill3.1 应用场景3.2 实现过程小结系列链接0. 前言 图像分割是计算机视觉中将像素划分为具有特定语义或…

Coturn打洞服务器

* 概念理解&#xff1a;1. SDP协议&#xff1a;会话描述协议&#xff0c;视频通话的双方通过交换SDP信息进行媒体协商&#xff0c;从而选择使用某一相同的媒体协议进行通信&#xff1b;TLS协议&#xff1a;基于TCP的安全层传输协议DTLS协议&#xff1a;基于UDP的安全层传输协议…

python flusk 监控

# 创建虚拟环境目录 python3 -m venv /sda1/xunjian/venv # 激活虚拟环境 source /sda1/xunjian/venv/bin/activate # 激活后终端会显示 (venv)创建虚拟环境&#xff08;在当前目录&#xff09;&#xff1a;bashpython3 -m venv venv激活虚拟环境&#xff1a;bashsource venv/b…

VUE2 项目学习笔记 ? 语法 v-if/v-show

?语法页面渲染的时候&#xff0c;需要服务器传过来的对象中的一个属性&#xff0c;然后根据这个属性用v-for渲染标签&#xff0c;这里写的v-for".... in dataList.goodsList"但是当解析到这行语法的时候&#xff0c;dataList还没返回&#xff0c;因此控制台会报错找…

使用qemu命令启动虚拟机

1. 安装相关软件 yum install qemu edk2* libvirt -y 启动libvirt服务 systemctl start libvirtd systemctl status libvirtd2. 创建虚拟机 2.1. qemu启动命令示例 /usr/bin/qemu-system-loongarch64 \-machine virt,accelkvm \-nodefaults \-m 2048 \-smp 2,maxcpus4,co…

大模型系统化学习路线

人工智能大模型系统化学习路线一、基础理论筑基&#xff08;1-2个月) 目标&#xff1a;建立大模型核心认知框架 核心内容&#xff1a; 深度学习基础&#xff1a;神经网络原理、CNN/RNN结构、梯度下降算法大模型本质&#xff1a;Transformer架构&#xff08;重点掌握注意力机制、…

LLaMA-Factory 微调可配置的模型基本参数

LLaMA-Factory 微调可配置的模型基本参数 flyfish 基本参数 一、模型加载与路径配置参数名类型描述默认值model_name_or_pathOptional[str]模型路径&#xff08;本地路径或 Huggingface/ModelScope 路径&#xff09;。Noneadapter_name_or_pathOptional[str]适配器路径&#xf…

Ubuntu 22 安装 ZooKeeper 3.9.3 记录

Ubuntu 22 安装 ZooKeeper 3.9.3 记录 本文记录在 Ubuntu 22.04 系统上安装 ZooKeeper 3.9.3 的过程&#xff0c;包含 Java 环境准备、配置文件调整、启动与停机操作、以及如何将 ZooKeeper 注册为系统服务。 一、准备环境 ZooKeeper 3.9.x 要求 Java 11 或更高版本&#xff…

FreeSwitch通过Websocket(流式双向语音)对接AI实时语音大模型技术方案(mod_ppy_aduio_stream)

FreeSwitch通过WebSocket对接AI实时语音大模型插件技术方案1. 方案概述 基于FreeSWITCH的实时通信能力&#xff0c;通过WebSocket协议桥接AI大模型服务&#xff0c;实现低延迟、高并发的智能语音交互系统。支持双向语音流处理、实时ASR/TTS转换和动态业务指令执行。 1753095153…

航班调度优化策略全局概览

在机场关闭场景下的航班恢复工作&#xff0c;是将机场关闭期间所有的航班进行取消然后恢复还是将机场关闭期间航班全部延误而后调整呢&#xff1f;简单来说&#xff0c;在实际操作中&#xff0c;既不是无差别地全部取消&#xff0c;也不是无差别地全部延误。这两种“一刀切”的…

spring boot 异步线程@Async 传递 threadLocal数据

将父类的 threadLocal 的数据 在线程池时&#xff0c;可以转给子线程使用。 Async 的使用。 第一步在启动服务加上 EnableAsync 注解。 EnableAsync public class NetCoreApplication {... ... }第二步&#xff1a;导入阿里 线程工具类<dependency><groupId>com.a…

AI产品经理成长记《零号列车》第一集 邂逅0XAI列车

《零号列车》绝非传统意义上的 AI 产品经理教程 —— 它是我沉淀二十多年跨行业数字化转型与工业 4.0 实战经验后,首创的100集大型小说体培养指南。那些曾在千行百业验证过的知识与经验,不再是枯燥的文字堆砌,而是化作一场沉浸式的学习旅程。​ 这里没有生硬的理论灌输,而…

[C++11]范围for循环/using使用

范围for循环 范围for循环&#xff08;Range-based for loop&#xff09;是 C11 引入的一种简洁的循环语法&#xff0c;用于遍历容器中的元素或者其他支持迭代的数据结构。 范围for循环可以让代码更加简洁和易读&#xff0c;避免了传统for循环中索引的操作。 下面是范围for循环的…

简单了解下npm、yarn 和 pnpm 中 add 与 install(i) 命令的区别(附上两图带你一目明了)

目录 pnpm 中 add 和 i 的区别 npm 中 add 和 i 的区别 yarn 中 add 和 i 的区别 附上两图带你一目明了&#xff1a; npm、yarn和pnpm的三者区别图&#xff1a; i 和 add 的核心区别图&#xff1a; 个人建议&#xff1a;在项目中保持命令使用的一致性&#xff0c;选择一种…

ESP32-S3学习笔记<2>:GPIO的应用

ESP32-S3学习笔记&#xff1c;2&#xff1e;&#xff1a;GPIO的应用1. 头文件包含2. GPIO的配置2.1 pin_bit_mask2.2 mode2.3 pull_up_en和pull_down_en2.4 intr_type3. 设置GPIO输出/获取GPIO输入4. 中断的使用4.1 gpio_install_isr_service4.2 gpio_isr_handler_add4.3 gpio_…

得物视觉算法面试30问全景精解

得物视觉算法面试30问全景精解 ——潮流电商 商品鉴别 视觉智能&#xff1a;得物视觉算法面试核心考点全览 前言 得物App作为中国领先的潮流电商与鉴别平台&#xff0c;持续推动商品识别、真假鉴别、图像搜索、内容审核、智能推荐等视觉AI技术的创新与落地。得物视觉算法岗位…

[Linux入门] Linux 账号和权限管理入门:从基础到实践

一、Linux 用户账号&#xff1a;谁能访问系统&#xff1f; 1️⃣超级用户&#xff08;root&#xff09; 2️⃣普通用户 3️⃣程序用户 二、组账号&#xff1a;让用户管理更高效 1️⃣组的类型 2️⃣特殊组 三、用户与组的 “身份证”&#xff1a;UID 和 GID 四、配置文…

阿里云ssl证书自动安装及续订(acme)

目录 一、shell命令安装 二、docker run安装 三、docker compose安装 一、shell命令安装 # 安装acme curl https://get.acme.sh | sh -s emailfloxxx5163.com# 注册zerossl .acme.sh/acme.sh --register-account -m flowxxx25163.com --server zerossl# 获取证书 export Al…