#include <iostream>
#include <fstream>
#include <thread>
extern "C"{
#include <libavformat/avformat.h>
}
//预处理指令导入库
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avcodec.lib")
#define CERR(err) do{if(err!=0)\
{printErr(err); return -1;}\
}while(0)
void printErr(int err)
{
char buf[1024] = { 0 };
av_strerror(err, buf, sizeof(buf) - 1);
cerr << buf << endl;
}
int main(int argc, char* argv[])
{
/**********解封装初始化************/
//打开媒体文件
const char* url = "v1080.mp4";
//1, 生成解封装输入上下文, ic需要释放空间
AVFormatContext* ic = nullptr;
int re = avformat_open_input(&ic,
url,
NULL, //解封装格式,自动探测,以后缀名,或者文件头
NULL);//参数设置, rtsp需要设置
CERR(re);
//2, 获取媒体信息 无头部格式的
re = avformat_find_stream_info(ic, NULL);
CERR(re);
//打印封装信息
av_dump_format(ic, 0, url, 0);
//提取音视频流, 后面直接使用as, vs
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;
}
}
/***************************/
/**********封装初始化************/
//1, 封装上下文 ec需要释放空间
AVFormatContext* ec = nullptr;
re = avformat_alloc_output_context2(&ec, NULL, NULL, out_url);
CERR(re);
//2, 添加视频流, 音频流
AVStream* mvs = avformat_new_stream(ec, NULL); //在后面通过上下文设置编码器指针
AVStream* mas = avformat_new_stream(ec, NULL);
//3, 打开输出IO 需要关闭
re = avio_open(&ec->pb, out_url, AVIO_FLAG_WRITE);
CERR(re);
//如果需要重编码, 下面4,5步骤的这些参数的设置就要重新计算
//4, 设置编码音视频流参数
mvs->time_base = vs->time_base; //时间基数与原视频一致
mas->time_base = as->time_base;
mvs->codecpar->codec_tag = 0;
mas->codecpar->codec_tag = 0;
//5, 从解封装复制参数
avcodec_parameters_copy(mvs->codecpar, vs->codecpar);
avcodec_parameters_copy(mas->codecpar, as->codecpar);
//6, 写入文件头
re = avformat_write_header(ec, NULL);
CERR(re);
//打印输出上下文
av_dump_format(ec, 0, out_url, 1);
/***************************/
/**********截取视频seek 10-20s************/
//取多不取少 假定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, 以视频流为准, 到第20s的pts就结束封装
//换算公式 播放时间time = pts * timebase
//令 vs->pts * vs->timebase = time
if (vs && vs->time_base.num > 0) { //除以分数就要判断分数的分子不为零
double t = (double)vs->time_base.den / (double)vs->time_base.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输入媒体文件 移动到第十秒的关键帧位置
re = av_seek_frame(ic, vs->index, begin_pts,
AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);//向后关键帧
CERR(re);
/****************************************/
AVPacket pkt;
while (1){
re = av_read_frame(ic, &pkt); //从封装中读一包,有可能是视频包也有可能是音频包
if (re != 0) CERR(re);
AVStream* out_stream = nullptr;
long long offset_pts = 0;
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;
}
if (as && pkt.stream_index == as->index) {//读到的是音频包
cout << "音频";
out_stream = ec->streams[1];
offset_pts = begin_audio_pts;
}
AVStream* in_stream = nullptr;
if(pkt.stream_index <= 1){
in_stream = ic->streams[pkt.stream_index];//可能是视频,也可能是音频
//重新计算pts dts duration
//a * bq (输入basetime)/ cq(输出basetime) basetime存在AVStream中
//因为进行了seek操作, 所以要输入的pts减去前面跳过的pts
pkt.pts = av_rescale_q_rnd(pkt.pts - offset_pts,
in_stream->time_base, out_stream->time_base,
(AVRounding)(AV_ROUND_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_INF | AV_ROUND_PASS_MINMAX));
pkt.duration = av_rescale_q(pkt.duration,
in_stream->time_base, out_stream->time_base);
pkt.pos = -1;
}
//写入音视频帧
av_interleaved_write_frame(ec, &pkt);
av_packet_unref(&pkt);
}
//写入结尾
re = av_write_trailer(ec);
CERR(re);
avformat_close_input(&ic);
avio_closep(&ec->pb);
avformat_free_context(ec);
ec = nullptr;
return 0;
}
ffmpeg之mp4文件解封装截取一段视频并重封装
最新推荐文章于 2023-12-31 14:07:47 发布