基于ffmepg的视频剪辑

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);
}

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

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

相关文章

图像生成(Text-to-Image)发展脉络

这篇博客对 图像生成&#xff08;image generation&#xff09; 领域的经典工作发展进行了梳理&#xff0c;包括重要的一些改进&#xff0c;目的是帮助读者对此领域有一个整体的发展方向把握&#xff0c;并非是对每个工作的详细介绍。 脉络发展&#xff08;时间顺序&#xff0…

探究大语言模型(LLM)漏洞和安全优秀实践

你可能已听说过LLM强势亮相&#xff0c;至少ChatGPT就是代表。 大语言模型(LLM)指语言处理模型。这类模型经过训练&#xff0c;可以执行各种各样的语言任务&#xff1a;翻译、文本生成和问题回答等。 有几个LLM家族和架构&#xff0c;最著名的是GPT(生成式预训练Transformer)…

Grafana :利用Explore方式实现多条件查询

背景 日志统一推送到Grafana上管理。所以&#xff0c;有了在Grafana上进行日志搜索的需求&#xff0c;而进行日志搜索通常需要多条件组合。 解决方案 通过Grafana的Explore的方式实现多条件查询。 直接看操作步骤&#xff1a; 在主页搜索框中输入“Explore” 进入这个界面…

python—日期相差多少天(PythonTip)

[题目描述] 编写一个程序&#xff0c;计算两个日期之间的天数。 导入datetime模块。定义函数calculate_days_between()数&#xff0c;其中有两个参数&#xff1a;(date1, date2)&#xff0c;类型为字符串&#xff0c;格式为YYYY-MM-DD。在函数内&#xff0c;将字符串转换为date…

全面战争模拟器免费下载地址,纯分享

全面战争模拟器以其独特的物理引擎和搞笑的战斗场面吸引了大量玩家&#xff0c;并在游戏社区中赢得了极高的评价。它不仅提供了丰富的策略性玩法&#xff0c;还通过滑稽的视觉效果和搞笑的战斗带来了极大的娱乐性。游戏的沙盒模式和自定义功能更是让玩家能够充分发挥创意&#…

磁盘的作业

1、新添加一块硬盘&#xff0c;大小为5g&#xff0c;给这块硬盘分一个mbr格式的主分区(大小为3g)&#xff0c;给此主分区创建ext2的文件系统&#xff0c;挂载到/guazai1目录&#xff0c;并写入文件内容为"this is fist disk"文件名为1.txt的文件。 [rootwyk ~]# fdis…

02线性表 - 链表

这里是只讲干货不讲废话的炽念&#xff0c;这个系列的文章是为了我自己以后复习数据结构而写&#xff0c;所以可能会用一种我自己能够听懂的方式来描述&#xff0c;不会像书本上那么枯燥和无聊&#xff0c;且全系列的代码均是可运行的代码&#xff0c;关键地方会给出注释^_^ 全…

【时时三省】(C语言基础)函数和数组

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ——csdn时时三省 函数 跟数学里面的函数很相似 数组 一组相同类型的元素的集合 比如把5个整形1-5存起来 int arr&#xff3b;10&#xff3d;&#xff1d;&#xff5b;1&#xff0c;2&#xff0c;3&#x…

逻辑门的题目怎么做?

FPGA语法练习——二输入逻辑门&#xff0c;一起来听~~ FPGA语法练习——二输入逻辑门 题目介绍&#xff1a;F学社-全球FPGA技术提升平台 (zzfpga.com)

高功能自闭症:挑战与潜能并存

高功能自闭症是一种复杂的神经发育障碍&#xff0c;其患者通常在认知、语言和行为方面存在发育障碍&#xff0c;但保持相对正常的社交互动和自理能力。这种疾病由大脑神经发育异常引起&#xff0c;涉及神经递质、脑结构和功能连接等多个方面&#xff0c;导致信息处理和整合困难…

WebRTC音视频-前言介绍

目录 效果预期 1&#xff1a;WebRTC相关简介 1.1&#xff1a;WebRTC和RTC 1.2&#xff1a;WebRTC前景和应用 2&#xff1a;WebRTC通话原理 2.1&#xff1a;媒体协商 2.2&#xff1a;网络协商 2.3&#xff1a;信令服务器 效果预期 1&#xff1a;WebRTC相关简介 1.1&…

SpringCloud的认识和初步搭建

目录 一.认识SpringCloud 二.SpringCloud的部署 2.1开发环境 2.2数据库的建立 2.3SpringCloud的部署 第一步&#xff1a; 创建Maven项目 第二步&#xff1a;完善pom文件 第三步&#xff1a;创建两个子项目 第四步&#xff1a;声明项目依赖以及构建插件 第五步&#xf…

Docker核心技术:容器技术要解决哪些问题

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 Docker核心技术 系列文章&#xff1a;容器技术要解决哪些问题&#xff0c;其他文章快捷链接如下&#xff1a; 应用架构演进容器技术要解决哪些问题&#xff08;本文&#xff09;Docker的基本使用Docker是如何实…

Ubuntu20.04从零开搭PX4MavrosGazebo环境并测试

仅仅是个人搭建记录 参考链接&#xff1a; https://zhuanlan.zhihu.com/p/686439920 仿真平台基础配置&#xff08;对应PX4 1.13版&#xff09; 语雀 mkdir -p ~/tzb/catkin_ws/src mkdir -p ~/tzb/catkin_ws/scripts cd catkin_ws && catkin init catkin build cd…

RV1103使用rtsp和opencv推流视频到网页端

参考&#xff1a; Luckfox-Pico/Luckfox-Pico-RV1103/Luckfox-Pico-pinout/CSI-Camera Luckfox-Pico/RKMPI-example Luckfox-Pico/RKMPI-example 下载源码 其中源码位置&#xff1a;https://github.com/luckfox-eng29/luckfox_pico_rtsp_opencv 使用git clone由于项目比较大&am…

共享模型之无锁

一、问题提出 1.1 需求描述 有如下的需求&#xff0c;需要保证 account.withdraw() 取款方法的线程安全&#xff0c;代码如下&#xff1a; interface Account {// 获取余额Integer getBalance();// 取款void withdraw(Integer amount);/*** 方法内会启动 1000 个线程&#xf…

纯净IP代理网站解析与代理推荐

一、纯净IP代理网站解析 在浩瀚的代理服务市场中&#xff0c;纯净IP代理以其特有的优势脱颖而出。它们不仅提供高质量、无污染的IP资源&#xff0c;还注重服务的稳定性和安全性&#xff0c;确保用户在使用过程中能够享受到好的网络体验。纯净IP代理的优势在于其能够有效解决因…

科技论文在线--适合练习期刊写作和快速发表科技成果论文投稿网站

中国科技论文在线这个平台可以作为练手的一个渠道&#xff0c;至少可以锻炼一下中文写作&#xff0c;或者写一些科研方向的简单综述性文章。当然&#xff0c;如果你的老师期末要求也是交一份科技论文在线的刊载证明的话&#xff0c;这篇文章可以给你提供一些经验。 中国科技论…

【引领未来智造新纪元:量化机器人的革命性应用】

在日新月异的科技浪潮中&#xff0c;量化机器人正以其超凡的智慧与精准的操作&#xff0c;悄然改变着各行各业的生产面貌&#xff0c;成为推动产业升级、提升竞争力的关键力量。今天&#xff0c;让我们一同探索量化机器人在不同领域的广泛应用价值&#xff0c;见证它如何以科技…

皇后游戏1

先把这个推导看完 现在我们来讲一下总结 首先&#xff0c;我们要观察到 c i c_i ci​递增&#xff0c;这样才能更简单&#xff0c;就不用像国王游戏那样在交换前后比较 i i i和 j j j的max了&#xff08;就是说&#xff0c;国王游戏需要比较 m a x ( c i , c j ) max(c_i,c_j)…