基于ffmepg的视频剪辑

news2024/11/14 21:24:53

1.ffmpeg命令实现视频剪辑

  FFmpeg是一个非常强大的视频处理工具,可以用来剪辑视频。以下是一个基本的FFmpeg命令行示例,用于剪辑视频:

$ ffmpeg -i ./最后一滴水.mp4  -ss 0:0:20  -t 50  -c copy output.mp4

  -i ./最后一滴水.mp4 输入文件
  -ss 0:0:20 剪辑的起始时间20s
  -t 50 剪辑的时长50s,也可以写成时分秒格式:00:00:50
  -c copy 复制编码以避免重编码,加快处理速度。
  output.mp4 输出文件

2.调用ffmpeg库实现视频剪辑

  • 音视频裁剪步骤

  (1)打开源媒体文件avformat_open_input
  (2)读取数据包,获取流信息avformat_find_stream_info,输出流信息av_dump_format
  (3)创建输出上下文avformat_alloc_output_context2
  (4)获取源文件中的流数据,拷贝到目标文件avcodec_parameters_copy
  (5)打开输出文件上下文avio_open
  (6)写入头数据avformat_write_header
  (7)设置到剪切的位置av_seek_frame
  (8)循环读取数据跑av_read_frame,对读取的数据包时间转换av_packet_rescale_ts,写入到目的媒体文件av_interleaved_write_frame
  (9)判断截取的尾时间av_q2d(istream->time_base)*pkt.pts >= end_time
  (10)写入文件尾数据av_write_trailer
  (11)释放资源

  • 流程图如下:
    在这里插入图片描述
#include <stdio.h>
#include <libavutil/avutil.h>
#include <libavutil/timestamp.h>
#include <libavutil/rational.h>
#include <libavformat/avformat.h>
#include <stdlib.h>

//音视频裁剪
int main(int argc,char **argv)
{
	av_log_set_level(AV_LOG_DEBUG);
	if(argc!=5)
	{
		av_log(NULL,AV_LOG_INFO,"格式:./app <源文件> <目标文件> <起始时间> <结束时间>\n");
		return 0;
	}
	char *src=argv[1];//源文件
	char *dst=argv[2];//目标文件
	double start_time=atof(argv[3]);//起始时间
	double end_time=atof(argv[4]);//结束时间
	av_log(NULL,AV_LOG_INFO,"start=%.1f\t end=%.1f\n",start_time,end_time);
	if(end_time<=start_time)
	{
		av_log(NULL,AV_LOG_ERROR,"裁剪的结束时间应大于起始时间\n");
		return 0;
	}
	//1.打开源媒体文件
	AVFormatContext *pfmtctx=NULL;
	AVFormatContext *ofmtctx=NULL;
	int64_t *start_time_dts=NULL;//保存每路流的dts起始时间
	int64_t *start_time_pts=NULL;//保存每路流的pts起始时间
	int *stream_arr=NULL;//用于保存有效流的下标
	int ret=avformat_open_input(&pfmtctx, src,NULL,NULL);
	if(ret!=0){
		av_log(NULL,AV_LOG_ERROR,"打开源媒体文件失败ret=%s\n",av_err2str(ret));
		return 0;
	}
	//2.读取数据包,获取流信息
	ret=avformat_find_stream_info(pfmtctx, NULL);
	if(ret<0){
		av_log(NULL,AV_LOG_ERROR,"获取流信息失败ret=%s\n",av_err2str(ret));
		goto _fil;
	}
	//输出流信息
	//av_dump_format(AVFormatContext * ic, int index, const char * url, int is_output)
	av_log(pfmtctx,AV_LOG_INFO,"文件名:%s\n",pfmtctx->iformat->name);
	//时长
	 if (pfmtctx->duration != AV_NOPTS_VALUE) {
            int64_t hours, mins, secs, us;
            int64_t duration = pfmtctx->duration + (pfmtctx->duration <= INT64_MAX - 5000 ? 5000 : 0);
            secs  = duration / AV_TIME_BASE;
            us    = duration % AV_TIME_BASE;
            mins  = secs / 60;
            secs %= 60;
            hours = mins / 60;
            mins %= 60;
            av_log(NULL, AV_LOG_INFO, "播放时长:%02"PRId64":%02"PRId64":%02"PRId64".%02"PRId64"\n", hours, mins, secs,
                   (100 * us) / AV_TIME_BASE);
      } 
	 //3.创建目标媒体文件上下文件
	 ret=avformat_alloc_output_context2(&ofmtctx,NULL,NULL, dst);
	 if(ret<0){
		av_log(NULL,AV_LOG_ERROR,"创建输出媒体文件上下文失败ret=%s\n",av_err2str(ret));
		goto _fil;
	 }
	 //4.读取源文件的所有流数据
	 stream_arr=av_calloc(pfmtctx->nb_streams, sizeof(int));//用于保存流下标
	 int stream_index=0;
	 for(int i=0;i<pfmtctx->nb_streams;i++)

	 {
		AVStream *istream=pfmtctx->streams[i];//输入流数据
		AVStream *ostream=NULL;
		if(istream->codecpar->codec_type!=AVMEDIA_TYPE_VIDEO &&  //视频
		   istream->codecpar->codec_type!=AVMEDIA_TYPE_AUDIO &&  //音频
		   istream->codecpar->codec_type!=AVMEDIA_TYPE_SUBTITLE )  //字幕
		{
			stream_arr[i]=-1;//将除此之外的流下标置为-1
			continue;
		}
		stream_arr[i]=stream_index++;//正常流下标从0开始
		//创建一个输出流
		ostream=avformat_new_stream(ofmtctx,NULL);
		if(ostream==NULL){
			av_log(ofmtctx,AV_LOG_ERROR,"创建输出流失败\n");
			goto _fil;
		}
		//将源文件流数据拷贝到目标文件
		ret=avcodec_parameters_copy(ostream->codecpar, istream->codecpar);
		if(ret<0){
			av_log(ofmtctx,AV_LOG_ERROR,"拷贝流数据失败\n");
			goto _fil;
		}
		istream->codecpar->codec_tag=0;//解码器标志,填0表示由系统决定
	 }
	 //5.打开目标文件
	 ret=avio_open(&ofmtctx->pb, dst,AVIO_FLAG_READ_WRITE);
	 if(ret<0){
		av_log(ofmtctx,AV_LOG_ERROR,"打开目标文件失败ret=%s\n",av_err2str(ret));
		goto _fil;
	 }
	 //6.写入文件头数据
	 ret=avformat_write_header(ofmtctx, NULL);
	 if(ret<0){
		av_log(&ofmtctx,AV_LOG_ERROR,"写入头数据失败err=%s\n",av_err2str(ret));
		goto _fil;
	 }
	 /*
	 	7.设置要截取的起始位置
		int av_seek_frame(AVFormatContext *s, int stream_index,int64_t timestamp, int flags)
		形参:s --媒体文件上下文指针
		      stream_index --流下标位置,填-1,时间戳会自动从AV_TIME_BASE单位转换为流特定的时基
		      timestamp --要跳转到的位置,用设置的秒时间*AV_TIME_BASE
		      flags  --查找流数据帧的处理方式,AVSEEK_FLAG_BACKWARD表示向后查找到关键帧(这对视频来说很重要)
		返回值:成功返回>=0

		AV_TIME_BASE -->该参数是ffmpeg内部的时间基准,pts、dts 、duration等均需使用该参数转换
	 */
	 ret=av_seek_frame(pfmtctx,-1,start_time*AV_TIME_BASE ,AVSEEK_FLAG_BACKWARD);
	 if(ret){
		av_log(&pfmtctx,AV_LOG_ERROR,"跳转到指定位置失败err=%s\n",av_err2str(ret));
		goto _fil;
	 }
	 
	 start_time_dts=av_calloc(pfmtctx->nb_streams, sizeof(int64_t));
	 start_time_pts=av_calloc(pfmtctx->nb_streams, sizeof(int64_t));
	 for(int i=0;i<pfmtctx->nb_streams;i++)
	 {
		start_time_dts[i]=-1;//将默认值初始化为-1
		start_time_pts[i]=-1;
	 }
	 //8.从源文件中读取数据流
	 AVPacket pkt;
	 while(av_read_frame(pfmtctx,&pkt)==0)
	 {
		//判断读取的流是否为我们需要的流数据
		AVStream *istream=pfmtctx->streams[pkt.stream_index];//输入流数据
		AVStream *ostream=NULL;
		if(stream_arr[pkt.stream_index]==-1)//不需要的流数据
		{
			av_packet_unref(&pkt);//释放包
			continue;
		}
		if(istream->codecpar->codec_type==AVMEDIA_TYPE_VIDEO)
		{
			av_log(pfmtctx,AV_LOG_INFO,"pts=%.1f s\n",av_q2d(istream->time_base)*pkt.pts);//显示pts时间
		}
		if(av_q2d(istream->time_base)*pkt.pts>=end_time)break;//判断是否结束时间到
		if(start_time_dts[pkt.stream_index]==-1 && pkt.dts>0)
		{
			start_time_dts[pkt.stream_index]=pkt.dts;//保存截取的起始时间dts
		}

		if(start_time_pts[pkt.stream_index]==-1 && pkt.pts>0)
		{
			start_time_pts[pkt.stream_index]=pkt.pts;//保存截取的起始时间pts
		}
		pkt.dts-=start_time_dts[pkt.stream_index];//解码时间戳
		pkt.pts-=start_time_pts[pkt.stream_index];//显示时间戳
		if(pkt.pts<pkt.dts)
		{
			pkt.pts=pkt.dts;//显示时间戳pts>=解码时戳dts
		}
		pkt.stream_index=stream_arr[pkt.stream_index];//当前流下标
		ostream=ofmtctx->streams[pkt.stream_index];//输出流
		
		//时间转换:dts、pts、durations
		av_packet_rescale_ts(&pkt,istream->time_base, ostream->time_base);
		pkt.pos=-1;//相对位置
		av_interleaved_write_frame(ofmtctx, &pkt);
		av_packet_unref(&pkt);//减少引用次数
		
	 }
	 //写入文件尾数据
	 av_write_trailer(ofmtctx);
	
_fil:
	avformat_close_input(&pfmtctx);
	avformat_free_context(ofmtctx);
	av_free(start_time_dts);
	av_free(start_time_pts);
	av_free(stream_arr);
}

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

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

相关文章

利用PyTorch进行模型量化

利用PyTorch进行模型量化 目录 利用PyTorch进行模型量化 一、模型量化概述 1.为什么需要模型量化&#xff1f; 2.模型量化的挑战 二、使用PyTorch进行模型量化 1.PyTorch的量化优势 2.准备工作 3.选择要量化的模型 4.量化前的准备工作 三、PyTorch的量化工具包 1.介…

Linux复习02

一、什么是操作系统 操作系统是一款做软硬件管理的软件&#xff01; 一个好的操作系统&#xff0c;衡量的指标是&#xff1a;稳定、快、安全 操作系统的核心工作&#xff1a; 通过对下管理好软硬件资源的手段&#xff0c;达到对上提供良好的&#xff08;稳定&#xff0c;快…

【MindSpore学习打卡】应用实践-LLM原理和实践-文本解码原理 —— 以MindNLP为例

在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;文本生成是一项重要且具有挑战性的任务。从对话系统到自动文本补全&#xff0c;文本生成技术无处不在。本文将深入探讨自回归语言模型的文本解码原理&#xff0c;使用MindNLP工具进行示例演示&#xff0c;并详细分析…

240719_图像二分类任务中图像像素值的转换-[0,255]-[0,1]

240719_图像二分类任务中图像像素值的转换-[0,255]-[0,1] 在做语义分割二分类任务时&#xff0c;有时下载到的数据集或者我们自己制作的数据集&#xff0c;标签像素值会是[0,255]&#xff08;或者含有一些杂乱像素&#xff09;&#xff0c;但在该类任务中&#xff0c;往往0代表…

androidkiller重编译apk失败的问题

androidkiller重编译apk失败 参考&#xff1a; https://blog.csdn.net/qq_38393271/article/details/127057187 https://blog.csdn.net/hkz0704/article/details/132855098 已解决&#xff1a;“apktool” W: invalid resource directory name:XXX\res navigation 关键是编译…

脑肿瘤有哪些分类? 哪些人会得脑肿瘤?

脑肿瘤&#xff0c;作为一类严重的脑部疾病&#xff0c;其分类复杂多样&#xff0c;主要分为原发性脑肿瘤和脑转移瘤两大类。原发性脑肿瘤起源于颅内组织&#xff0c;常见的有胶质瘤、脑膜瘤、生殖细胞瘤、颅内表皮样囊肿及鞍区肿瘤等。其中&#xff0c;胶质瘤作为最常见的脑神…

# Redis 入门到精通(九)-- 主从复制

Redis 入门到精通&#xff08;九&#xff09;-- 主从复制&#xff08;1&#xff09; 一、redis 主从复制 – 主从复制简介 1、互联网“三高”架构 高并发高性能高可用 2、你的“Redis”是否高可用&#xff1f; 1&#xff09;单机 redis 的风险与问题 问题1.机器故障  现…

WeTest 海外本地化测试的全生命周期服务 第一期

伴随全球化和数字化的加速推进&#xff0c;越来越多的国内企业希望将其产品服务推向国际&#xff0c;以便在全球数字市场中占有一席之地。除去传统的欧美市场&#xff0c;国内企业也积极开拓东南亚、南亚、拉美、中东和非洲等新兴市场。这些地区的互联网普及率和数字化需求正在…

vue+watermark-dom实现页面水印效果

前言 页面水印大家应该都不陌生&#xff0c;它可以用于验证数字媒体的来源和完整性&#xff0c;还可以用于版权保护和信息识别&#xff0c;这些信息可以在不影响媒体质量的情况下嵌入&#xff0c;‌并在需要时进行提取。‌本文将通过 vue 结合 watermark-dom 库&#xff0c;教大…

《AIGC 实战宝典》(2024版) 正式发布!

2024 新年伊始&#xff0c;OpenAI 推出文生视频 Sora&#xff0c;风靡整个科技圈。 最近又发布了 ChatGPT-4o&#xff0c;这是一个全新模型&#xff0c;不仅能处理文本&#xff0c;还能实时理解和生成音频和图像。OpenAI 用实际行动给全世界的科技公司又上了一课。 如何从0到1…

零基础STM32单片机编程入门(十五) DHT11温湿度传感器模块实战含源码

文章目录 一.概要二.DHT11主要性能参数三.DHT11温度传感器内部框图四.DTH11模块原理图五.DHT11模块跟单片机板子接线和通讯时序1.单片机跟DHT11模块连接示意图2.单片机跟DHT11模块通讯流程与时序 六.STM32单片机DHT11温度传感器实验七.CubeMX工程源代码下载八.小结 一.概要 DH…

offer题目51:数组中的逆序对

题目描述&#xff1a;在数组中的两个数字&#xff0c;如果前面一个数字大于后面的数字&#xff0c;则这两个数字组成一个逆序对。输入一个数组&#xff0c;求出这个数组中的逆序对的总数。例如&#xff0c;在数组{7,5,6,4}中&#xff0c;一共存在5个逆序对&#xff0c;分别是(7…

[Vulnhub] TORMENT IRC+FTP+CUPS+SMTP+apache配置文件权限提升+pkexec权限提升

信息收集 IP AddressOpening Ports192.168.101.152TCP:21,22,25,80,111,139,143,445,631 $ nmap -p- 192.168.101.152 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 2.0.8 or later | ftp-anon: Anonymous FTP login a…

新建一个git仓库并且把已有项目推送到git远程仓库

总贴 1. 创建一个空项目&#xff0c;不会看新建仓库 2. 克隆这个项目到某个文件夹去&#xff0c;比如我想克隆到我的E盘的code下面 3. 我的这个文件夹下面是有东西的&#xff0c;一点都不影响 . 4. 用命令行进入这个文件夹 命令行已经显示了已经在E盘下面code文件夹, 不会…

【linux】报错解决:配置RAIDA1之后系统识别不到

【linux】报错解决&#xff1a;配置RAIDA1之后系统识别不到 一、问题描述&#xff1a; 我的主板是华南金牌X99-F8D PLUS&#xff0c;安装了ubuntu20.04&#xff0c;通过BIOS创建了RAID1数组&#xff0c;进入系统之后识别不到我创建的RAID1数组。 二、原因分析&#xff1a; 可…

【算法】算法模板

算法模板 文章目录 算法模板简介数组字符串列表数学树图动态规划 简介 博主在LeetCode网站中学习算法的过程中使用到并总结的算法模板&#xff0c;在算法方面算是刚过初学者阶段&#xff0c;竞赛分数仅2000。 为了节省读者的宝贵时间&#xff0c;部分基础的算法与模板未列出。…

IMU提升相机清晰度

近期&#xff0c;一项来自北京理工大学和北京师范大学的团队公布了一项创新性的研究成果&#xff0c;他们将惯性测量单元&#xff08;IMU&#xff09;和图像处理算法相结合&#xff0c;显著提升了非均匀相机抖动下图像去模糊的准确性。 研究团队利用IMU捕捉相机的运动数据&…

用程序画出三角形图案

创建各类三角形图案 直角三角形&#xff08;左下角&#xff09; #include <iostream> using namespace std;int main() {int rows;cout << "输入行数: ";cin >> rows;for(int i 1; i < rows; i){for(int j 1; j < i; j){cout << &…

阿里巴巴1688商品详情API返回值全面解析-商品基本信息

阿里巴巴1688商品详情API的返回值是一个包含了商品详细信息的JSON对象&#xff0c;这些信息对于开发者在电商平台上展示商品、进行数据分析等场景非常重要。以下是对阿里巴巴1688商品详情API返回值的全面解析&#xff1a; 一、商品基本信息 商品ID&#xff1a;商品的唯一标识…

gds-linkstack:泛型链式栈

类似于C的stack的泛型容器&#xff0c;初始化、销毁、清空、入栈、出栈、取栈顶、栈空。