XMux.h
继承于XFormat
作用:处理多媒体文件的封装,提供了一系列函数用于打开封装上下文、写入数据包、写入头信息和结束信息,同时处理音视频的时间基准。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #pragma once #include "xformat.h" class XMux : public XFormat{ public : static AVFormatContext *Open (const char *url, AVCodecParameters *video_para = nullptr , AVCodecParameters *audio_para = nullptr ) ; bool WriteHead () ; bool Write (AVPacket *pkt) ; bool WriteEnd () ; void set_src_video_time_base (AVRational *tb) ; void set_src_audio_time_base (AVRational *tb) ; ~XMux (); private : AVRational *src_video_time_base_ = nullptr ; AVRational *src_audio_time_base_ = nullptr ; long long begin_video_pts_ = -1 ; long long begin_audio_pts_ = -1 ; };
参数说明:
1 2 3 4 5 6 7 private : AVRational *src_video_time_base_ = nullptr ; AVRational *src_audio_time_base_ = nullptr ; long long begin_video_pts_ = -1 ; long long begin_audio_pts_ = -1 ;
src_video_time_base_
参数 src_video_time_base_
在 XMux
类中起到了关键作用,主要用于管理视频流的时间基准。时间基准(Time Base)是指视频或音频流中时间戳的单位和精度。以下是具体作用和使用场景的详细分析:
作用
时间戳转换 :
视频流中的每个数据包(frame)都有一个时间戳(timestamp),表示这个数据包在视频播放中的具体时间点。src_video_time_base_
用于定义这些时间戳的单位和精度,确保时间戳在封装和解封装过程中保持一致。
时间基准设置 :
set_src_video_time_base
函数用于设置 src_video_time_base_
的值。如果传入的 AVRational
指针非空,则将其值赋给 src_video_time_base_
。这通常在封装开始之前调用,以确保视频流的时间戳基准正确设置。
时间戳调整 :
在写入数据包时,需要根据 src_video_time_base_
进行时间戳的调整和转换,以确保写入的数据包时间戳与封装文件的时间基准一致。
使用场景
封装视频流 :
在封装(muxing)过程中,视频流的数据包需要按照正确的时间戳顺序写入文件。src_video_time_base_
确保这些时间戳按照预期的时间基准进行转换和写入。
多媒体处理 :
在处理包含多个视频流或音频流的多媒体文件时,每个流可能有不同的时间基准。src_video_time_base_
用于统一和管理这些时间基准,以确保时间戳的一致性和准确性。
AVFormatContext
AVFormatContext
是一个多媒体文件的容器,管理文件的整体格式信息,包括文件的输入输出、流信息等。
主要作用
文件格式信息 :
管理多媒体文件的格式信息(如文件类型、文件名等)。
多媒体流管理 :
包含了文件中所有音频、视频、字幕流的信息。
通过 streams
数组存储每个流的 AVStream
信息。
I/O 管理 :
负责文件的输入输出操作,如读取或写入文件数据。
包含 AVIOContext
指针,用于管理具体的 I/O 操作。
主要字段
AVIOContext *pb
:指向 I/O 上下文,用于管理文件的读写。
unsigned int nb_streams
:文件中包含的流的数量。
AVStream **streams
:指向流数组,每个流由一个 AVStream
结构体表示。
与AVCodecContext的区别 :
AVFormatContext
和 AVCodecContext
是 FFmpeg 库中的两个核心结构体,分别用于管理多媒体文件的格式和编解码过程。
AVCodecContext
是一个编解码器的上下文,用于管理音视频数据的编解码过程。
主要作用
编解码参数管理 :
包含了编解码器的相关参数(如比特率、采样率、帧率、分辨率等)。
编解码操作 :
负责具体的编解码操作,包括初始化、编码、解码、关闭等。
编解码器配置 :
需要与特定的 AVCodec
配合使用,AVCodec
描述了具体的编解码器(如 H.264、AAC 等)。
主要字段
AVCodec *codec
:指向具体的编解码器。
int bit_rate
:比特率。
int width, height
:视频的宽度和高度。
int sample_rate
:音频的采样率。
AVRational time_base
:时间基准,用于时间戳的转换。
关系与区别
管理层级 :
AVFormatContext
主要用于管理文件级别的格式信息和流信息。
AVCodecContext
主要用于管理单个流的编解码过程。
职责范围 :
AVFormatContext
负责文件的整体操作,包括读取、写入、格式解析等。
AVCodecContext
负责具体的数据处理,包括编码、解码等。
使用场景 :
在处理多媒体文件时,首先使用 AVFormatContext
打开文件并读取流信息,然后对每个流使用相应的 AVCodecContext
进行编解码操作。
XMux.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 #include "xmux.h" #include <iostream> #include <thread> using namespace std;extern "C" {#include <libavformat\avformat.h> } static void PrintErr (int err) { char buf[1024 ] = { 0 }; av_strerror (err, buf, sizeof (buf) - 1 ); cerr << buf << endl; } #define BERR(err) if (err != 0) {PrintErr(err); return 0;} void XMux::set_src_video_time_base (AVRational *tb) { if (!tb) return ; unique_lock<mutex> lock (mux_) ; if (!src_video_time_base_) src_video_time_base_ = new AVRational (); *src_video_time_base_ = *tb; } void XMux::set_src_audio_time_base (AVRational *tb) { if (!tb) return ; unique_lock<mutex> lock (mux_) ; if (!src_audio_time_base_) src_audio_time_base_ = new AVRational (); *src_audio_time_base_ = *tb; } XMux::~XMux () { unique_lock<mutex> lock (mux_) ; if (src_video_time_base_) delete src_video_time_base_; src_video_time_base_ = nullptr ; if (src_audio_time_base_) delete src_audio_time_base_; src_audio_time_base_ = nullptr ; } AVFormatContext *XMux::Open (const char *url, AVCodecParameters *video_para, AVCodecParameters *audio_para) { AVFormatContext *c = nullptr ; auto re = avformat_alloc_output_context2 (&c, NULL , NULL , url); BERR (re); if (video_para) { auto vs = avformat_new_stream (c, NULL ); avcodec_parameters_copy (vs->codecpar, video_para); } if (audio_para) { auto as = avformat_new_stream (c, NULL ); avcodec_parameters_copy (as->codecpar, audio_para); } re = avio_open (&c->pb, url, AVIO_FLAG_WRITE); BERR (re); av_dump_format (c, 0 , url, 1 ); return c; } bool XMux::Write (AVPacket *pkt) { if (!pkt) return false ; unique_lock<mutex> lock (mux_) ; if (!c_) return false ; if (pkt->pts == AV_NOPTS_VALUE) { pkt->pts = 0 ; pkt->dts = 0 ; } if (pkt->stream_index == video_index_) { if (begin_video_pts_ < 0 ) begin_video_pts_ = pkt->pts; lock.unlock (); RescaleTime (pkt, begin_video_pts_, src_video_time_base_); lock.lock (); } else if (pkt->stream_index == audio_index_) { if (begin_audio_pts_ < 0 ) begin_audio_pts_ = pkt->pts; lock.unlock (); RescaleTime (pkt, begin_audio_pts_, src_audio_time_base_); lock.lock (); } auto re = av_interleaved_write_frame (c_, pkt); BERR (re); return true ; } bool XMux::WriteEnd () { unique_lock<mutex> lock (mux_) ; if (!c_) return false ; av_interleaved_write_frame (c_, nullptr ); auto re = av_write_trailer (c_); BERR (re); return true ; } bool XMux::WriteHead () { unique_lock<mutex> lock (mux_) ; if (!c_) return false ; cout << "============================" << endl; auto re = avformat_write_header (c_, nullptr ); BERR (re); cout << "============================" << endl; av_dump_format (c_, 0 , c_->url, 1 ); this ->begin_audio_pts_ = -1 ; this ->begin_video_pts_ = -1 ; return true ; }
set_src_video/audio_time_base()
作用:设置视频和音频的时间基准。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void XMux::set_src_video_time_base (AVRational *tb) { if (!tb) return ; unique_lock<mutex> lock (mux_) ; if (!src_video_time_base_) src_video_time_base_ = new AVRational (); *src_video_time_base_ = *tb; } void XMux::set_src_audio_time_base (AVRational *tb) { if (!tb) return ; unique_lock<mutex> lock (mux_) ; if (!src_audio_time_base_) src_audio_time_base_ = new AVRational (); *src_audio_time_base_ = *tb; }
~XMux()
作用:析构函数,用于清理时间基准,防止出现内存泄漏
1 2 3 4 5 6 7 8 9 10 11 XMux::~XMux () { unique_lock<mutex> lock (mux_) ; if (src_video_time_base_) delete src_video_time_base_; src_video_time_base_ = nullptr ; if (src_audio_time_base_) delete src_audio_time_base_; src_audio_time_base_ = nullptr ; }
Open()
用于创建和初始化一个 AVFormatContext
上下文,并为其添加视频和音频流。
Open
函数的主要作用是:
创建一个新的 AVFormatContext
上下文,用于输出多媒体文件。
根据传入的视频和音频参数添加视频和音频流。
打开输入输出(I/O)上下文,准备写入操作。
将输出文件 url
与 AVFormatContext
相关联。
返回初始化好的 AVFormatContext
上下文。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 AVFormatContext *XMux::Open (const char *url, AVCodecParameters *video_para, AVCodecParameters *audio_para) { AVFormatContext *c = nullptr ; auto re = avformat_alloc_output_context2 (&c, NULL , NULL , url); BERR (re); if (video_para) { auto vs = avformat_new_stream (c, NULL ); avcodec_parameters_copy (vs->codecpar, video_para); } if (audio_para) { auto as = avformat_new_stream (c, NULL ); avcodec_parameters_copy (as->codecpar, audio_para); } re = avio_open (&c->pb, url, AVIO_FLAG_WRITE); BERR (re); av_dump_format (c, 0 , url, 1 ); return c; }
Write()
作用:用于将外部编码后 的数据包(AVPacket
)写入多媒体文件中。
Write
函数的主要作用是:
检查数据包的有效性。
根据数据包的时间戳和流类型进行时间戳调整。
将调整后的数据包写入多媒体文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 bool XMux::Write (AVPacket *pkt) { if (!pkt) return false ; unique_lock<mutex> lock (mux_) ; if (!c_) return false ; if (pkt->pts == AV_NOPTS_VALUE) { pkt->pts = 0 ; pkt->dts = 0 ; } if (pkt->stream_index == video_index_) { if (begin_video_pts_ < 0 ) begin_video_pts_ = pkt->pts; lock.unlock (); RescaleTime (pkt, begin_video_pts_, src_video_time_base_); lock.lock (); } else if (pkt->stream_index == audio_index_) { if (begin_audio_pts_ < 0 ) begin_audio_pts_ = pkt->pts; lock.unlock (); RescaleTime (pkt, begin_audio_pts_, src_audio_time_base_); lock.lock (); } auto re = av_interleaved_write_frame (c_, pkt); BERR (re); return true ; }
WriteEnd()
作用:用于结束音视频数据的写入操作。该函数的主要目的是在所有数据包都写入后,写入文件尾部信息并清理资源。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 bool XMux::WriteEnd () { unique_lock<mutex> lock (mux_) ; if (!c_) return false ; av_interleaved_write_frame (c_, nullptr ); auto re = av_write_trailer (c_); BERR (re); return true ; }
WriteHead()
作用:用于在音视频数据写入之前,初始化文件头并设置一些初始状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 bool XMux::WriteHead () { unique_lock<mutex> lock (mux_) ; if (!c_) return false ; cout << "============================" << endl; auto re = avformat_write_header (c_, nullptr ); BERR (re); cout << "============================" << endl; av_dump_format (c_, 0 , c_->url, 1 ); this ->begin_audio_pts_ = -1 ; this ->begin_video_pts_ = -1 ; return true ; }