ffmpeg封装和解封装介绍-(6)完成重封装mp4文件并截断10s~20s的视频并生成10s视频文件

embedded/2024/12/22 20:36:16/

源文件:

#include <iostream>
#include <thread>
using namespace std;
extern "C" { //指定函数是c语言函数,函数名不包含重载标注
//引用ffmpeg头文件
#include <libavformat/avformat.h>
}
//预处理指令导入库
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")
void PrintErr(int err)
{char buf[1024] = { 0 };av_strerror(err, buf, sizeof(buf) - 1);cerr << endl;
}
#define CERR(err) if(err!=0){ PrintErr(err);getchar();return -1;}int main(int argc, char* argv[])
{//打开媒体文件const char* url = "v1080.mp4";/// 解封装//解封装输入上下文AVFormatContext* ic = nullptr;//初始化格式上下文IC//*Open an input streamand read the header.The codecs are not opened.
//* The stream must be closed with avformat_close_input().auto re = avformat_open_input(&ic, url,NULL,       //封装器格式 null 自动探测 根据后缀名或者文件头NULL        //参数设置,rtsp需要设置);CERR(re);//获取媒体信息 无头部格式re = avformat_find_stream_info(ic, NULL);CERR(re);//打印封装信息av_dump_format(ic, 0, url,0 //0表示上下文是输入 1 输出);AVStream* as = nullptr; //音频流AVStream* vs = nullptr; //视频流for (int i = 0; i < ic->nb_streams; i++){//音频if (ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){as = ic->streams[i];cout << "=====音频=====" << endl;cout << "sample_rate:" << as->codecpar->sample_rate << endl;}else if (ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){vs = ic->streams[i];cout << "=========视频=========" << endl;cout << "width:" << vs->codecpar->width << endl;cout << "height:" << vs->codecpar->height << endl;}}/// 解封装/开始编码到文件中//编码器上下文const char* out_url = "test_mux.mp4";AVFormatContext* ec = nullptr;re = avformat_alloc_output_context2(&ec, NULL, NULL,out_url         //根据文件名推测封装格式);CERR(re);//再output文件的上下文ec中添加视频流、音频流auto mvs = avformat_new_stream(ec, NULL);  //视频流auto mas = avformat_new_stream(ec, NULL);  //音频流//打开输出IOre = avio_open(&ec->pb, out_url, AVIO_FLAG_WRITE);CERR(re);//设置编码音视频流参数//ec->streams[0];//mvs->codecpar;//视频参数if (vs){mvs->time_base = vs->time_base;// 时间基数与原视频一致//从解封装复制参数avcodec_parameters_copy(mvs->codecpar, vs->codecpar);}if (as){mas->time_base = as->time_base;//从解封装复制参数avcodec_parameters_copy(mas->codecpar, as->codecpar);}//写入文件头re = avformat_write_header(ec, NULL);CERR(re);//打印输出上下文av_dump_format(ec, 0, out_url, 1);/// 截取10 ~ 20 秒之间的音频视频 取多不取少// 假定 9 11秒有关键帧 我们取第9秒double begin_sec = 10.0;    //截取开始时间double end_sec = 20.0;      //截取结束时间long long begin_pts = 0;long long begin_audio_pts = 0;  //音频的开始时间long long end_pts = 0;//换算成pts 换算成输入ic的pts,以视频流为准if (vs && vs->time_base.num > 0){//sec /timebase = pts// pts =  sec/(num/den) = sec* (den/num)  double t = (double)vs->time_base.den / (double)vs->time_base.num;//den分母/num分子begin_pts = begin_sec * t;end_pts = end_sec * t;}if (as && as->time_base.num > 0)begin_audio_pts = begin_sec * ((double)as->time_base.den / (double)as->time_base.num);//seek输入媒体 移动到第十秒的关键帧位置if (vs)re = av_seek_frame(ic, vs->index, begin_pts,AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD); //向后关键帧CERR(re);AVPacket pkt;for (;;){re = av_read_frame(ic, &pkt);if (re != 0){PrintErr(re);break;}AVStream* in_stream = ic->streams[pkt.stream_index];AVStream* out_stream = nullptr;long long offset_pts = 0; //偏移pts,用于截断的开头pts运算if (vs && pkt.stream_index == vs->index){cout << "视频:";//超过第20秒退出,只存10~20秒if (pkt.pts > end_pts){av_packet_unref(&pkt);break;}out_stream = ec->streams[0];offset_pts = begin_pts;}else if (as && pkt.stream_index == as->index){cout << "音频:";out_stream = ec->streams[1];offset_pts = begin_audio_pts;}cout << pkt.pts << " : " << pkt.dts << " :" << pkt.size << endl;//重新计算pts dts duration//`a * bq(输入basetime) / cq(输出basetime)`if (out_stream){pkt.pts = av_rescale_q_rnd(pkt.pts - offset_pts, in_stream->time_base,out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));pkt.dts = av_rescale_q_rnd(pkt.dts - offset_pts, in_stream->time_base,out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);}pkt.pos = -1;//写入音视频帧 会清理pktre = av_interleaved_write_frame(ec,&pkt);if (re != 0){PrintErr(re);}//av_packet_unref(&pkt);//this_thread::sleep_for(100ms);}//写入结尾 包含文件偏移索引re = av_write_trailer(ec);if (re != 0)PrintErr(re);avformat_close_input(&ic);avio_closep(&ec->pb);avformat_free_context(ec);ec = nullptr;return 0;
}

运行结果:

成功生产力另一个mp4文件,内容为我们重新封装的音视频流,和解封装之前的视频一样


http://www.ppmy.cn/embedded/50147.html

相关文章

Flutter 简化CustomPainter的绘制

文章目录 前言一、为何简化&#xff1f;1、通常做法&#xff08;1&#xff09;、绘制形状1&#xff08;2&#xff09;、绘制形状2&#xff08;3&#xff09;、界面显示 2、简化 二、完整代码三、使用示例1、绘制图形2、动态触发绘制 总结 前言 使用Flutter做界面时&#xff0c…

浏览器调试小技巧

一. 使用XSwitch工具代理本地服务地址 1. 谷歌提供了一个扩展程序: XSwitch 工具描述: 一个重定向URL并允许CORS使本地开发体验轻松愉快的工具。 ps: 这个工具只有谷歌有, 只能翻墙后下载 安装成功后 长这样: 2. 全局安装http-server , 用于在本地启动一个服务 npm i http-…

WPF 使用Image控件显示图片

Source属性 Source属性用来告诉Image组件要展示哪张图片资源的一个入口&#xff0c;通常是图片的路径。也许是本地路径&#xff0c;也许是网络路径。 本地图片路径加载方式 使用相对路径&#xff0c;相对于工程目录的路径&#xff0c;当设置Width属性时&#xff0c;图片会等…

Nginx+KeepAlived高可用负载均衡集群的部署

目录 一.KeepAlived补充知识 1.一个合格的群集应该具备的特点 2.健康检查&#xff08;探针&#xff09;常用的工作方式 3.相关面试问题 问题1 问题2 二.Keepealived脑裂现象 1.现象 2.原因 硬件原因 运用配置原因 3.解决 4.预防 方法1 方法2 方法3 方法4 三.…

Java Websocket分片发送

一、分片发送和接收(复杂) 如果数据量太大&#xff0c;需要分多次发送&#xff0c; 需要考虑数据划分和重组的问题。 二、具体思路 每次发送和接收用一个布尔值变量指定是否为最后一个分片。 三、具体使用 (一)字符串分片发送&#xff1a; sendText(文本&#xff0c; 布尔值)…

【Jmeter】插件详解:PerfMon Metrics Collector 服务器性能监控插件

目录 一、前言 二、PerfMon Metrics Collector 插件详解 (1)插件简介 (2)功能介绍 (3)应用场景 (4)使用指南 ① 环境准备 ② 服务端插件配置 ③ 监听器配置 ④ 图表设置 ⑤ 非 GUI 模式 三、ServerAgent 下载 四、ServerAgent 安装 (1)安装 (…

C++~~期末复习题目讲解---lijiajia版本

目录 1.类和对象 &#xff08;3&#xff09;创建对象的个数 &#xff08;3&#xff09;全局变量&#xff0c;局部变量 &#xff08;4&#xff09;构造函数的执行次数 &#xff08;5&#xff09;静态动态析构和构造顺序 &#xff08;6&#xff09;初始化顺序和声明顺序 &a…

9.2JavaEE——JDBCTemplate的常用方法(二)update()方法

二、update()方法 JdbcTemplate类中常用的update()方法 方法说明int update(String sql)该方法是最简单的update()方法重载形式,它直接执行传入的SQL语句,并返回受影响的行数。int update(PreparedStatementCreator psc)该方法执行参数psc返回的语句,然后返回受影响的行数。i…