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);	// 释放packetcontinue;}// 相对时间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 >= dtspkt.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.mzph.cn/web/7738.shtml

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

相关文章

【智能算法】人类进化优化算法(HEOA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.代码获取 1.背景 2024年&#xff0c;J Lian受到人类进化启发&#xff0c;提出了人类进化优化算法&#xff08;Human Evolutionary Optimization Algorithm, HEOA&#xff09;。 2.算法原理 2.1算法思想 …

算法学习笔记(差分约束系统)

前置&#xff1a;spfa 从例题入手&#xff1a; 【模板】差分约束系统 | StarryCoding 题目描述 给定 n n n未知量和一个大小为 m m m的不等式&#xff08;或等式&#xff09;组&#xff0c;请你判断这个不等式&#xff08;或等式&#xff09;组是否有解。 1 1 1 i i i j …

【Linux 命令操作】如何在 Linux 中使用多行注释呢?

文章目录 1. 给代码进行多行注释2. 给代码取消多行注释 1. 给代码进行多行注释 &#x1f427;① 首先用 vim 打开代码&#xff0c;按 Esc进入命令模式(Normal mode)&#xff1b; &#x1f427;② 然后按住 ctrl v 进入列模式&#xff1b; &#x1f427;③ 再通过按 h(左)、j(…

Yarn:下一代JavaScript包管理器的安装与实战指南

当然&#xff0c;让我们深入探讨Yarn——一个高效、可靠的JavaScript包管理器&#xff0c;它为前端开发带来了新的速度和便利。Yarn由Facebook、Google、Exponent和Tilde公司共同推出&#xff0c;旨在解决npm&#xff08;Node.js包管理器&#xff09;存在的问题&#xff0c;如依…

19.删除链表的倒数第n个结点

刷算法题&#xff1a; 第一遍&#xff1a;1.看5分钟&#xff0c;没思路看题解 2.通过题解改进自己的解法&#xff0c;并且要写每行的注释以及自己的思路。 3.思考自己做到了题解的哪一步&#xff0c;下次怎么才能做对(总结方法) 4.整理到自己的自媒体平台。 5.再刷重复的类…

python元组

创建元组 元组的创建使用小括号&#xff08;&#xff09; 创建空元组&#xff1a;tuple&#xff08;&#xff09; 可以使用tuple()函数和range()函数来生成数值元组。 注意&#xff1a;当元组中只包含一个元素时&#xff0c;需要在元素后面添加逗号&#xff0c;否则括号会被…

常见概念之事件驱动

简介 事件驱动是一种软件架构模式&#xff0c;其中系统的组件通过触发和响应事件来进行通信和协作。在事件驱动架构中&#xff0c;系统的各个组件之间通过发布和订阅事件的方式进行解耦&#xff0c;从而实现松散耦合和高度可扩展性。 一般工作流程 事件产生&#xff08;Even…

QCefView 在 Linux 下的编译(更新)

在前面的文章《QT 应用程序中集成浏览器》中已经介绍过 QCefView 的构建。这几天发现 QCefView 代码进行了更新,构建方式也发生了一点点变化,所以在此更新一下 QCefView 的编译方法。 QCefView 其实包含了两个项目,一个就是 QCefView 项目本身,另外一个就是 CefViewCore。…

Docker容器:Docker-Consul 的容器服务更新与发现

目录 前言 一、什么是服务注册与发现 二、 Docker-Consul 概述 1、Consul 概念 2、Consul 提供的一些关键特性 3、Consul 的优缺点 4、传统模式与自动发现注册模式的区别 4.1 传统模式 4.2 自动发现注册模式 5、Consul 核心组件 5.1 Consul-Template组件 5.2 Consu…

深度学习之基于Vgg16卷积神经网络乳腺癌诊断系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 基于VGG16卷积神经网络的乳腺癌诊断系统项目是一个结合深度学习技术和医学图像处理的创新项目&#xff0c;旨在提高…

代码随想录Day 40|Leetcode|Python|139.单词拆分 ● 关于多重背包,你该了解这些! ● 背包问题总结篇!

139.单词拆分 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。 注意&#xff1a;不要求字典中出现的单词全部都使用&#xff0c;并且字典中的单词可以重复使用。 解题思路&#xff1a; 确定dp数组含义…

路飞吃桃递归问题

在写代码之前&#xff0c;补充两个知识点 1.C语言递归的模版 2.递归是怎么工作的 好!话不多说让我们开始吧&#xff1a; 我们知道路飞吃了n天&#xff0c;每次都是吃一半&#xff0b;1&#xff0c;知道最后一天&#xff0c;只有一个桃子了&#xff0c;所以就可以列出式子&…

列转行(spark 与presto语法)

一、Presto 语法 原始数据&#xff1a; 期望数据&#xff1a; 代码&#xff1a; SELECT info, value FROM ( select 张三 as name,18 as age,男 as gender,清华 as schoolunion allselect 李四 as name,18 as age,男 as gender,清华 as school ) as a CROSS JOIN UNNEST(…

Linux实现Flappy bird项目

目录 1、项目介绍 2、功能总结 3、前期准备 3.1 Ncurses库 3.2 信号机制 3.2.1 设置信号响应方式 3.2.2 设置定时器 4、代码实现 4.1 头文件引用及变量、函数定义 4.2 主函数 4.3 curses初始化 4.4 设置定时器 4.5 定时器响应函数 4.6 小鸟控制相关函数 4…

C语言 自定义类型——联合体

目录: 一、联合体是&#xff1f;声明计算内存大小 二、联合体的特点例如 三、联合体大小的计算规则&#xff1a; 四、应用习1习2 一、联合体是&#xff1f; 联合体和结构体差不多&#xff0c;但是其最大的区别在于联合体所有的成员共用一块内存空间。所以联合体也叫共用体。联…

stm32f103c8t6最小系统板

STM32F103C8T6最小系统板是为基于ARM Cortex-M3内核的STM32F103C8T6微控制器设计的电路板&#xff0c;它包含了单片机正常运行所需的最基本组件。以下是构成STM32F103C8T6最小系统板的基本部分&#xff1a; 单片机芯片&#xff1a;STM32F103C8T6本身&#xff0c;它是一款32位微…

Java_方法引用

方法引用就是把已经有的方法拿过来用&#xff0c;当作函数式接口中抽象方法的方法体。 条件&#xff1a; 1.引用处需要是函数式接口 2.被引用的方法需要已经存在 3.被引用的方法的形参和返回值需要跟抽象方法的形参和返回值保持一致 4.被引用方法的功能需要满足当前的要求 简…

搭建父模块和工具子模块

第一章 项目父模块搭建 1.1 nancal-idsa 作为所有工程的父工程&#xff0c;用于管理项目的所有依赖版本。 1.2 指定 pom 类型模块&#xff0c;删除 src 目录&#xff0c;点击Reload project 1.3 添加依赖 pom.xml <parent> <groupId>org.springframework.…

Python爬虫教程:入门爬取网页数据

1.遵守法律法规 爬虫在获取网页数据时&#xff0c;需要遵守以下几点&#xff0c;以确保不违反法律法规&#xff1a; 不得侵犯网站的知识产权&#xff1a;爬虫不得未经授权&#xff0c;获取和复制网站的内容&#xff0c;这包括文本、图片、音频、视频等。 不得违反网站的使用条…

【华为机考模拟题】Words、Vowel、计算字符串重新排列数

目录 一、Words 二、Vowel 三、计算字符串重新排列数 一、Words 每个句子由多个单词组成&#xff0c;句子中的每个单词的长度都可能不一样&#xff0c;假设每个单词的长度 Ni 为该单词的重量&#xff0c;你需要做的就是给出整个句子的平均重量 V。 输入&#xff1a; Who L…