源文件:
#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); //音频流
//打开输出IO
re = 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;
//写入音视频帧 会清理pkt
re = 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文件,内容为我们重新封装的音视频流,和解封装之前的视频一样
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » ffmpeg封装和解封装介绍-(6)完成重封装mp4文件并截断10s~20s的视频并生成10s视频文件
发表评论 取消回复