ffmpeg音视频裁剪

音视频裁剪,通常会依据时间轴为基准,从某个起始点到终止点的音视频截取出来,当然音视频文件中存在多路流,所对每一组流进行裁剪 

 基础概念:

编码帧的分类:

I帧(Intra coded frames):  关键帧,采用帧内压缩技术,所占数据的信息量比较大,I帧不需要参考其他画面而生成,解码时仅靠自己就重构完整图像;

P 帧(forward Predicted frames):  向前参考帧,根据本帧与相邻的前一帧(l帧或P帧)的不同点来压缩本帧数据,同时利用了空间和时间上的相关性。压缩时,只参考前面已经处理的帧(I帧或P帧),采用帧间压缩技术。它占I帧的一半大小

B 帧(Bidirectional predicted frames):  双向参考帧,B 帧图像采用双向时间预测,可以大大提高压缩倍数。压缩时,既参考前面已经处理的帧,也参考后面的帧,帧间压缩技术。它占I帧四分之一大小。

        I帧图像是周期性出现在图像序列中的,出现频率可由编码器选择;I帧是P帧和B帧的参考帧(其质量直接影响到同组中以后各帧的质量);I帧是帧组GOP(Group of Pictures)的基础帧(第一帧),且每组只有一个I帧。

        对于一个视频文件,帧的显示顺序:IBBP,但是帧的存储方式可能是:IPBB。现在我们需要在显示B帧之前知道P帧中的信息,这时就需要一个解码时间戳(dts(Decoding Time Stamp))和一个显示时间戳(pts(Presentation Time Stamp))。解码时间戳告诉我们什么时候需要解码,显示时间戳告诉我们什么时间需要显示。通常pts和dts只有在流中有B帧的时候才不同。

        FFmpeg中用AVPacket结构体来描述解码前、后的压缩包,用AVFrame结构体来描述解码后、前的信号帧。 对于视频来说,AVFrame就是视频的一帧图像。这帧图像什么时候显示给用户,就取决于它的PTS。DTS是AVPacket里的一个成员,表示这个压缩包应该什么时候被解码。 如果视频里各帧的编码是按输入顺序(也就是显示顺序)依次进行的,那么解码和显示时间应该是一致的。可事实上,在大多数编解码标准(如H.264或HEVC)中,编码顺序和输入顺序并不一致,于是才会需要PTS和DTS这两种不同的时间戳。所以视频流中的时间总是pts(显示时间) >= dts(解码时间)。

ffmpeg中时间相关时间单位:

        ffmepg中的内部计时单位(时间基),ffmepg中的所有时间都是于它为一个单位,比如AVStream中的duration即以为着这个流的长度为duration个AV_TIME_BASE。AV_TIME_BASE定义为:

#define         AV_TIME_BASE   1000000

ffmpeg提供了一个把AVRatioal结构转换成double的函数:

static inline double av_q2d(AVRational a){
/**
* Convert rational to double.
* @param a rational to convert
**/
    return a.num / (double) a.den;
}

可以根据pts来计算一桢在整个视频中的时间位置:

timestamp(秒) = pts * av_q2d(st->time_base);    //这里的st是一个AVStream对象指针。

计算视频长度的方法:

time(秒) = st->duration * av_q2d(st->time_base);    // 这里的st是一个AVStream对象指针。

时间基转换公式

  • timestamp(ffmpeg内部时间戳) = AV_TIME_BASE * time(秒)
  • time(秒) = AV_TIME_BASE_Q * timestamp(ffmpeg内部时间戳)

所以当需要把视频跳转到N秒的时候可以使用下面的方法:

int64_t timestamp = N * AV_TIME_BASE; // N秒转换为内部时间戳

av_seek_frame(fmtctx, index_of_video, timestamp, AVSEEK_FLAG_BACKWARD);    //  // AVSEEK_FLAG_BACKWARD 向后找到I帧

不同时间基之间的转换函数(作用是计算a * bq / cq,来把时间戳从一个时基调整到另外一个时基。在进行时基转换的时候,我们应该首选这个函数,因为它可以避免溢出的情况发生。)

int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)

裁剪音视频代码实例:

//裁剪多媒体文件(因为视频存在I帧B帧P帧,所以裁剪结果和输入时长有误差)

//编译链接:gcc -o cut cut.c `pkg-config --libs --cflags libavutil libavformat libavcodec`

//执行 ./cut test.mp4 cut.mp4  (starttime)  (endtime)(单位秒)

#include<stdio.h>
#include<stdlib.h>
#include<libavutil/log.h>
#include <libavformat/avformat.h>


int main(int argc, char* argv[])
{

	int ret = -1;
	int idx = -1;
	int i = 0;
	int stream_idx = 0;

	// 处理输入参数
	char* src, * dst;
	double starttime, endtime;
	int64_t* dts_start_time, * pts_start_time;

	int* stream_map = NULL;

	AVFormatContext* pFmtCtx = NULL;	// 多媒体上下文
	AVFormatContext* oFmtCtx = NULL;	// 目标文件上下文信息

	const AVOutputFormat* outFmt = NULL;		// 输出文件格式信息

	AVPacket pkt;		// 包


	av_log_set_level(AV_LOG_DEBUG);
	if (argc < 5) {	//该可执行程序  源文件   目标文件 起始时间 结束时间
		av_log(NULL, AV_LOG_INFO, "Arguments must be more than 5.");
		exit(-1);
	}
	src = argv[1];
	dst = argv[2];
	starttime = atof(argv[3]);
	endtime = atof(argv[4]);
	if (endtime < starttime) {
		av_log(NULL, AV_LOG_INFO, "Cut time error!.");
		exit(-1);
	}

	// 打开多媒体文件(包含文件头和文件体)
	if ((ret = avformat_open_input(&pFmtCtx, src, NULL, NULL)))
	{
		av_log(NULL, AV_LOG_ERROR, "%s\n", av_err2str(ret));
		exit(-1);
	}



	// 打开目的文件的上下文
	avformat_alloc_output_context2(&oFmtCtx, NULL, NULL, dst);
	if (!oFmtCtx) {
		av_log(NULL, AV_LOG_ERROR, "NO Memory!\n");
		goto _ERROR;
	}

	stream_map = av_calloc(pFmtCtx->nb_streams, sizeof(int));
	if (!stream_map) {
		av_log(NULL, AV_LOG_ERROR, "NO Memory!\n");
		goto _ERROR;
	}

	// 遍历源文件每一条流
	for (i = 0; i < pFmtCtx->nb_streams; i++) {
		AVStream* outStream = NULL;
		AVStream* inStream = pFmtCtx->streams[i];
		AVCodecParameters* inCodecPar = inStream->codecpar;

		// 只处理音、视频、字幕数据
		if (inCodecPar->codec_type != AVMEDIA_TYPE_AUDIO &&
			inCodecPar->codec_type != AVMEDIA_TYPE_VIDEO &&
			inCodecPar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
			stream_map[i] = -1;
			continue;
		}
		stream_map[i] = stream_idx++;

		// 为目的文件创建一个新的视频流
		outStream = avformat_new_stream(oFmtCtx, NULL);
		if (!outStream) {
			av_log(oFmtCtx, AV_LOG_ERROR, "NO Memory!\n");
			goto _ERROR;
		}

		avcodec_parameters_copy(outStream->codecpar, inStream->codecpar);	//将源文件的内容复制到目的文件 
		outStream->codecpar->codec_tag = 0;	// 根据多媒体文件自动识别编解码器


	}

	//上下文信息与输出文件绑定
	ret = avio_open2(&oFmtCtx->pb, dst, AVIO_FLAG_WRITE, NULL, NULL);
	if (ret < 0) {
		av_log(NULL, AV_LOG_ERROR, "%s", av_err2str(ret));
		goto _ERROR;

	}

	// 写多媒体文件头(包含多媒体的类型、版本等信息)到目标文件
	ret = avformat_write_header(oFmtCtx, NULL);
	if (ret < 0) {
		av_log(oFmtCtx, AV_LOG_ERROR, "%s", av_err2str(ret));
		goto _ERROR;

	}

	// 跳转到时间点
	ret = av_seek_frame(pFmtCtx, -1, starttime * AV_TIME_BASE, AVSEEK_FLAG_BACKWARD); // AVSEEK_FLAG_BACKWARD 向后找到I帧
	if (ret < 0) {
		av_log(oFmtCtx, AV_LOG_ERROR, "%s", av_err2str(ret));
		goto _ERROR;
	}

	// 记录第一个包的时间戳
	dts_start_time = av_calloc(pFmtCtx->nb_streams, sizeof(int64_t));
	pts_start_time = av_calloc(pFmtCtx->nb_streams, sizeof(int64_t));
	for (int t = 0; t < pFmtCtx->nb_streams; t++) {
		dts_start_time[t] = -1;
		pts_start_time[t] = -1;
	}

	// 从源多媒体文件中读到音、视频、字幕数据
	while (av_read_frame(pFmtCtx, &pkt) >= 0) {  // 从多媒体文件读取到帧数据,读取码流中的音频若干帧或者视频一帧
		AVStream* inStream, * outStream;

		// 记录每组流截取开始的时间戳
		if (dts_start_time[pkt.stream_index] == -1 && pkt.dts > 0) {
			dts_start_time[pkt.stream_index] = pkt.dts;
		}
		if (pts_start_time[pkt.stream_index] == -1 && pkt.pts > 0) {
			pts_start_time[pkt.stream_index] = pkt.pts;
		}


		inStream = pFmtCtx->streams[pkt.stream_index];
		if (av_q2d(inStream->time_base) * pkt.pts > endtime) {	// 结束时间
			av_log(oFmtCtx, AV_LOG_INFO, "cut success!\n");
			break;
		}
		if (stream_map[pkt.stream_index] < 0) {		// 流编号为-1, 不是音、视频、字幕流数据
			av_packet_unref(&pkt);	// 释放packet
			continue;
		}
		
		// 相对时间
		pkt.pts = pkt.pts - pts_start_time[pkt.stream_index];
		pkt.dts = pkt.dts - dts_start_time[pkt.stream_index];
		if (pkt.dts > pkt.pts) {	// 音频dts、pts 相等,视频的pts >= dts
			pkt.pts = pkt.dts;
		}

		pkt.stream_index = stream_map[pkt.stream_index];

		outStream = oFmtCtx->streams[pkt.stream_index];
		av_packet_rescale_ts(&pkt, inStream->time_base, outStream->time_base);	// 修改时间戳
		
		pkt.pos = -1;			// 偏移位置
		av_interleaved_write_frame(oFmtCtx, &pkt);		// 将视频帧写入目标文件中
		av_packet_unref(&pkt);

	}
	// 写多媒体文件尾到文件中
	av_write_trailer(oFmtCtx);

	// 将申请的资源释放掉
_ERROR:
	if (pFmtCtx) {
		avformat_close_input(&pFmtCtx);
		pFmtCtx = NULL;
	}
	if (oFmtCtx->pb) {
		avio_close(oFmtCtx->pb);
	}
	if (oFmtCtx) {
		avformat_free_context(oFmtCtx);
		oFmtCtx = NULL;
	}
	if (stream_map) {
		av_free(stream_map);
	}
	if (dts_start_time) {
		av_free(dts_start_time);
	}
	if (pts_start_time) {
		av_free(pts_start_time);
	}
	return 0;
}

参考:

ffmpeg中的时间单位_pkt.duration的值-CSDN博客

https://blog.51cto.com/moonfdd/6266754?articleABtest=0

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringCloud学习笔记(一)微服务介绍、服务拆分和RestTemplate远程调用、Eureka注册中心

文章目录 1 认识微服务1.1 单体架构1.2 分布式架构1.3 微服务1.4 SpringCloud1.5 总结 2 服务拆分与远程调用2.1 服务拆分原则2.2 服务拆分示例2.2.1 搭建项目2.2.2 创建数据库和表2.2.3 实现远程调用2.2.3.1 需求描述2.2.3.2 注册RestTemplate2.2.3.3 实现远程调用 2.2.4 提供…

【网络】HTTP协议

文章目录 一. 认识 URL1. URL 初识2. URL 的组成① 协议名称② 域名③ 端口号④ 文件路径⑤ 查询参数 3. URL中的字符3.1 合法字符3.2 保留字符3.3 其他字符3.4 URL中的字符总结 二. HTTP 协议1. HTTP 介绍2. 请求报文2.1 请求报文的格式2.2 请求方法介绍2.3 请求报文中常见的 …

【LeetCode:1103. 分糖果 II + 模拟】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

CUDA架构介绍与设计模式解析

文章目录 **CUDA**架构介绍与设计模式解析**1** CUDA 介绍CUDA发展历程CUDA主要特性CUDA系统架构CUDA应用场景编程语言支持CUDA计算过程线程层次存储层次 **2** CUDA 系统架构分层架构并行计算模式生产-消费者模式工作池模式异步编程模式 **3** CUDA 中的设计模式工厂模式策略模…

电脑技巧:推荐一款非常好用的媒体播放器PotPlayer

目录 一、 软件简介 二、功能介绍 2.1 格式兼容性强 2.2 高清播放与硬件加速 2.3 自定义皮肤与界面布局 2.4 多音轨切换与音效增强 2.5 字幕支持与编辑 2.6 视频截图与录像 2.7 网络流媒体播放 三、软件特色 四、使用技巧 五、总结 一、 软件简介 PotPlayer播放器 …

【MATLAB源码-第201期】基于matlab的黏菌群优化算法(SMA)无人机三维路径规划,输出做短路径图和适应度曲线

操作环境&#xff1a; MATLAB 2022a 1、算法描述 黏菌优化算法&#xff08;Slime Mould Algorithm, SMA&#xff09;是一种新颖的启发式优化方法&#xff0c;其灵感来源于自然界中的真菌——黏菌。这种算法模拟了黏菌在寻找食物时的行为和网络形成策略。在本文中&#xff0c…

【Linux】yum、vim

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343&#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/qinjh_/category_12625432.html 目录 Linux 软件包管理器 yum 什么是软件包 查看软件包 如何安装软件 如何卸载软…

网络安全的重要性及人才需求

安全现在是大趋势&#xff0c;说是铁饭碗也不为过&#xff0c;就业前景好&#xff0c;方向多比传统计算机行业就业舒服点。但是大厂依然是985&#xff0c;211的天下&#xff0c;是双非能进大厂的&#xff0c;只是凤毛麟角。前提是你的能力可以让公司忽略你的学历。 以2023年为…

【华为】VRRP的实验配置

【华为】VRRP的实验配置 实验需求拓扑LSW 3LSW 1基础配置VRRPDHCPOSPF默认路由 LSW 2基本配置VRRPDHCPOSPF默认路由 R1ISPPC1PC2 测试上网VRRP实验需求监视端口 配置文档 实验需求 ① 该公司有市场部和技术部&#xff0c;分别划在VLAN 10 和 VLAN 20里面 ② 此时为了网络的稳…

万兆以太网MAC设计(12)万兆UDP协议栈上板与主机网卡通信

文章目录 一、设置IP以及MAC二、上板效果2.1、板卡与主机数据回环测试2.2、板卡满带宽发送数据 一、设置IP以及MAC 顶层模块设置源MAC地址 module XC7Z100_Top#(parameter P_SRC_MAC 48h01_02_03_04_05_06,parameter P_DST_MAC 48hff_ff_ff_ff_ff_ff )(input …

双目深度估计原理立体视觉

双目深度估计原理&立体视觉 0. 写在前面1. 双目估计的大致步骤2. 理想双目系统的深度估计公式推导3. 双目标定公式推导4. 极线校正理论推导 0. 写在前面 双目深度估计是通过两个相机的对同一个点的视差来得到给该点的深度。 标准系统的双目深度估计的公式推导需要满足:1)两…

ASR语音转录Prompt优化

ASR语音转录Prompt优化 一、前言 在ASR转录的时候&#xff0c;我们能很明显的感受到有时候语音识别不是很准确&#xff0c;这过程中常见的文本错误主要可以归纳为以下几类&#xff1a; 同音错误&#xff08;Homophone Errors&#xff09; 同音错误发生在不同词语发音相似或相…

Modelsim自动仿真平台的搭建

Modelsim自动仿真平台的搭建 如果要搭建自动仿真平台脚本那就需要更改下面3个文件。run_simulation.bat、complie.do和wave.do文件。注&#xff1a;前提是安装了modulsim并且配置好了环境变量&#xff0c;这里不过多介绍。 一、下面是run_simulation.bat文件的内容 : 注释的…

MySQL-查询数据-练习

练习 1.创建一个查询&#xff0c;显示收入超过 12,000 的雇员的名字和薪水。 select LAST_NAME,SALARY from employees where SALARY > 12000;2.创建一个查询&#xff0c;显示雇员号为 176 的雇员的名字和部门号。 select LAST_NAME,DEPARTMENT_ID from employees where …

前端vue如何生成二维码

有时候有需要链接直接生成二维码在手机上看的需求&#xff0c;比如下载&#xff0c;比如信息&#xff0c;比如excel 下面先引入包 import QRCode from qrcode; 然后上代码 // 将res转换成二维码const qrCodeData JSON.stringify(res); // 将res转换为字符串作为二维码数据// …

WebSocket 全面解析

&#x1f31f; 引言 WebSocket&#xff0c;一个让实时通信变得轻而易举的神器&#xff0c;它打破了传统HTTP协议的限制&#xff0c;实现了浏览器与服务器间的全双工通信。想象一下&#xff0c;即时消息、在线游戏、实时股票报价…这一切都离不开WebSocket的魔力&#x1f4ab;。…

xLua热更新解决方案

图中灰色的无法实现热更新&#xff0c;而Lua代码可以打包成AB包&#xff0c;并上传到资源服务器&#xff0c; 当进入游戏检测是否有资源需要更新&#xff0c;需要则会从资源服务器下载。 学习目标 1.导入xLua框架 2.C#调用Lua 3.Lua调用C# 4.xLua热补丁 xLua框架导入和AB…

什么是网络安全等级保护测评(等保测评)?

什么是网络安全等级保护测评&#xff08;等保测评&#xff09;呢&#xff1f;今天永恒无限就为大家介绍下网络安全等级保护测评&#xff08;等保测评&#xff09; 网络安全等级保护测评&#xff08;等保测评&#xff09;是指对信息和信息系统按照重要性等级进行的保护测评。它…

爱普生晶振在物联网LoRa通讯中的应用

LoRa 是LPWAN通信技术中的一种&#xff0c;是美国Semtech公司采用和推广的一种基于扩频技术的超远距离无线传输方案。这一方案改变了以往关于传输距离与功耗的折衷考虑方式&#xff0c;为用户提供一种简单的能实现远距离、长电池寿命、大容量的系统&#xff0c;进而扩展传感网络…

C语言:项目实践(贪吃蛇)

前言&#xff1a; 相信大家都玩过贪吃蛇这款游戏吧&#xff0c;贪吃蛇是久负盛名的游戏&#xff0c;它也和俄罗斯方块&#xff0c;扫雷等游戏位列经典游戏的行列&#xff0c;那贪吃蛇到底是怎么实现的呢&#xff1f; 今天&#xff0c;我就用C语言带着大家一起来实现一下这款游戏…