用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
英文地址,請注明出處:http://en.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…