示例code:
#include <iostream> #include <thread> using namespace std; extern "C" { // 指定函数是C语言函数,以C语言的方式去编译 #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); cout << buf << endl; } #define CERR(err) if (err != 0) {PrintErr(err); return -1;} int main() { // 打开媒体文件 const char* url = "v1080.mp4"; // 解封装的输入上下文 AVFormatContext* ic = nullptr; 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; avformat_alloc_output_context2(&ec, NULL, NULL, out_url // 根据文件名推测封装格式 ); CERR(re); // 添加视频流和音频流 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); // 设置编码的音视频参数 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); // 1 代表是输出 ///////////////////////////////////////////////////////////////////// /// 截取10s~20s之间的音频和视频 double begin_sec = 10.0; // 截取的开始时间 double end_sec = 20.0; // 截取的结束时间 long long begin_pts = 0; // 截取的开始pts(视频) long long begin_audio_pts = 0; // 截取的开始pts(音频) long long end_pts = 0; // 将截取时间换算成pts, 以视频流为准 if (vs && vs->time_base.num > 0) { // 运算依据: // 最终转换的pts = 秒数 / 时间基数 = sec / (num / den) = sec * (den / num) 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 输入的媒体,移动到第10s的关键帧位置 if (vs) { re = av_seek_frame( ic, // 输入媒体的上下文 vs->index, // 以谁为基准进行seek begin_pts, // 从哪个位置开始seek AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD // 优先移动到关键帧位置(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秒即退出,只截取并存储10s~20s之间的数据 if (pkt.pts > end_pts) { av_packet_unref(&pkt); // 注意在退出前需要释放AVPacket, 否则容易内存泄漏 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 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; // 写入音视频数据, 会自动清理Packet re = av_interleaved_write_frame(ec, &pkt); if (re != 0) { PrintErr(re); } // 这里一定要注意AVPacket 内部空间的释放,因为调用av_read_frame后并不会释放前一次申请的内部数据空间 // 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; }
<完>