FFMPEG重要结构体的讲解
FFMPEG中有六个比较重要的结构体,分别是AVFormatContext、AVOutputFormat、 AVStream、AVCodec、AVCodecContext、AVPacket、AVFrame、AVIOContext结构体,这几个结构体是贯穿着整个FFMPEG核心功能。
AVFormatContext
这个结构体是统领全局的基本结构体,这个结构体最主要作用的是处理封装、解封装等核心功能。
AVInputFormat * iformat:输入数据的封装格式,仅作用于解封装用avformat_open_input
AVOutputFormat * ofomat:输出数据的封装格式,仅作用于解封装用avformat_write_header
AVIOContext * pb:I/O的上下文,在解封装中由用户在avformat_open_input之前来设置,若封装的时候用户在avformat_write_header之前设置
int nb_streams:流的个数,若只有视频流nb_streams = 1, 若同时有视频流和音频流则nb_streams = 2。
AVStream **stream:列出文件中所有流的列表
int64_t start_time:第一帧的位置
int64_t duration:流的持续时间
int64_t bit_rate:流的比特率
int64_t probsize:输入读取的用于确定容器格式的大小
AVDictionary *metadata:元数据
AVCodec * video_codec:视频编解码器
AVCodec * audio_codec:音频编解码器
AVCodec *subtitle_codec:字幕编解码器
AVCodec *data_codec:数据编解码器
AVOutputFormat
这个结构体的功能类似于COM接口,表示文件容器输出格式,这个结构体的特点是着重于函数的实现
2.2.1 const char *name; //描述的名称
2.2.2. const char *long_name;//格式的描述性名称,易于阅读。
2.2.3. enum AVCodecID audio_codec; //默认的音频编解码器
2.2.4. enum AVCodecID video_codec; //默认的视频编解码器
2.2.5. enum AVCodecID subtitle_codec; //默认的字幕编解码器
2.2.6. struct AVOutputFormat *next; //链表NEXT
2.2.7. int (*write_header)(struct AVFormatContext *); //写入数据头部
2.2.8. int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);//写一个数据包。 如果在标志中设置AVFMT_ALLOW_FLUSH,则pkt可以为NULL。
2.2.9. int (*write_trailer)(struct AVFormatContext *); //写入数据尾部
2.2.10. int (*interleave_packet)(struct AVFormatContext *, AVPacket *out, AVPacket *in, int flush); //刷新AVPacket,并写入
2.2.11. int (*control_message)(struct AVFormatContext *s, int type, void *data, size_t data_size);//允许从应用程序向设备发送消息。
2.2.12. int (*write_uncoded_frame)(struct AVFormatContext *, int stream_index, AVFrame **frame, unsigned flags);//写一个未编码的AVFrame。
2.2.13. int (*init)(struct AVFormatContext *);//初始化格式。 可以在此处分配数据,并设置在发送数据包之前需要设置的任何AVFormatContext或AVStream参数。
2.2.14.void (*deinit)(struct AVFormatContext *);//取消初始化格式。
2.2.15. int (*check_bitstream)(struct AVFormatContext *, const AVPacket *pkt);//设置任何必要的比特流过滤,并提取全局头部所需的任何额外数据
AVStream
AVStream包含了每一个视频/音频流信息的结构体
2.3.1. index/id:这个数字是自动生成的,根据index可以从streams表中找到该流
2.3.2.time_base:流的时间基,这是一个实数,该AVStream中流媒体数据的PTS和DTS都将会以这个时间基准。视频时间基准以帧率为基准,音频以采样率为时间基准
2.3.3.start_time:流的起始时间,以流的时间基准为单位,通常是该流的第一个PTS
2.3.4.duration:流的总时间,以流的时间基准为单位
2.3.5.need_parsing:对该流的parsing过程的控制
2.3.6.nb_frames:流内的帧数目
2.3.7.avg_frame_rate:平均帧率
2.3.8.codec:该流对应的AVCodecContext结构,调用avformat_open_input生成
2.3.9.parser:指向该流对应的AVCodecParserContext结构,调用av_find_stream_info生成
AVCodec结构体
AVCodec是ffmpeg的音视频编解码,它提供了个钟音视频的编码库和解码库,FFMPEG通过AVCODEC可以将音视频数据编码成对应的数据压缩包。
2.4.1. AVCodecID:AVCODECID编码器的ID号,这里的编码器ID包含了视频的编码器ID,如:AV_CODEC_ID_H264、AV_CODEC_ID_H265等等。音频的编码器ID:AV_CODEC_ID_AAC、AV_CODEC_ID_MP3等等。
2.4.2. AVMediaType:指明当前编码器的类型,包括:视频(AVMEDIA_TYPE_VIDEO)、音频(AVMEDIA_TYPE_AUDIO)、字幕(AVMEDIA_TYPE_SUBTITILE)。
2.4.3. AVRotational supported_framerates:支持的帧率,这个参数仅支持视频设置
2.4.4. enum AVPixelFormat * pix_fmts:支持的像素格式,这个参数支持视频设置
2.4.5. int * supported_samplerates:支持的采样率,这个参数支持音频设置
2.4.6. enum AVSampleFormat * sample_fmts:支持的采样格式,这个参数仅支持音频设置
2.4.7. uint64_t * channel_layouts:支持的声道数,这个参数仅支持音频设置
2.4.8. int private_data_size:私有数据的大小
AVCodecContext
AVCodecContext是FFMPEG编解码上下文的结构体,它内部包含了AVCodec编解码参数结构体。除了AVCodec结构体外,还有AVCodecInternal、AVRotational结构体,这包含了AVCodecID、AVMediaType、AVPixelFormat、AVSampleFormat等类型,其中包含视频的分辨率width、height、帧率framerate、码率bitrate等。
AVMediaType codec_type:指明当前编码器的类型,包括:视频(AVMEDIA_TYPE_VIDEO)、音频(AVMEDIA_TYPE_AUDIO)、字幕(AVMEDIA_TYPE_SUBTITILE)。
AVMediaType具体定义:
enum AVMediaType {
AVMEDIA_TYPE_UNKNOWN = -1, ///< Usually treated as AVMEDIA_TYPE_DATA
AVMEDIA_TYPE_VIDEO,
AVMEDIA_TYPE_AUDIO,
AVMEDIA_TYPE_DATA, ///< Opaque data information usually continuous
AVMEDIA_TYPE_SUBTITLE,
AVMEDIA_TYPE_ATTACHMENT, ///< Opaque data information usually sparse
AVMEDIA_TYPE_NB
};
2.5.2. AVMediaType codec_type:指明当前编码器的类型,包括:视频(AVMEDIA_TYPE_VIDEO)、音频(AVMEDIA_TYPE_AUDIO)、字幕(AVMEDIA_TYPE_SUBTITILE)。
2.5.3. AVCodec * codec:指明相应的编解码器,如H264/H265等等
2.5.4. AVCodecID * codec_id:编解码器的ID,这个在上面有详细的说明
2.5.5. void * priv_data:指向相对应的编解码器
2.5.6. int bit_rate:编码的码流,这里包含了音频码流和视频码流码率的设置
2.5.7.thread_count:编解码时候线程的数量,这个由用户自己设置和CPU数量有关。
2.5.8. AVRational time_base:根据该参数可以将pts转换为时间
2.5.9. int width, height:每一帧的宽和高
2.5.10. AVRational time_base:根据该参数可以将pts转换为时间
2.5.11. int gop_size:一组图片的数量,专门用于视频编码
2.5.12. enum AVPixelFormat pix_fmt:像素格式,编码的时候用户设置
2.5.13. int refs:参考帧的数量
2.5.14.enum AVColorSpace colorspace:YUV色彩空间类型
2.5.15.enum AVColorSpace color_range:MPEG JPEG YUV范围
2.5.16.int sample_rate:音频采样率
2.5.17.int channel:声道数(音频)
2.5.18. AVSampleFormat sample_fmt:采样格式
2.5.19.int frame_size:每个音频帧中每个声道的采样数量
2.5.20.int profile:配置类型
2.5.21.int level:级别
AVPacket
AVPacket是FFMPEG中一个非常重要的结构体,它保存了解复用之后,解码之前的数据,它存的是压缩数据的音视频数据。除了压缩数据后,它还包含了一些重要的参数信息,如显示时间戳(pts)、解码时间戳(dts)、数据时长、流媒体索引等。
2.6.1. AVBufferRef * buf:对数据包所在的引用的计数缓冲区引用
2.6.2. int64_t pts:显示时间戳,它是来视频播放的时候用于控制视频显示顺序和控制速度。PTS是一个相对的时间戳,通常它以毫秒为单位,表示该帧在播放器的哪个时间节点进行显示
2.6.3. int64_t dts:解码时间戳,它是来指视频或者音频在解码时候的时间戳,用于控制解码器的解码顺序和速度。DTS也是一个相对的时间戳,通常它以毫秒为单位,表示该帧在解码器中哪个时间节点进行解码
2.6.4. uint8_t * data:具体的缓冲区数据,这里的数据可以是视频H264数据也可以是音频的AAC数据
2.6.5. int size:缓冲区长度,这里就是具体的码流数据的长度
2.6.6.int stream_index:流媒体索引值,假设同时有音频和视频。那stream_index = 0等于视频数据,stream_index = 1等于音频数据
2.6.7.flags:专门用在视频编码码流的标识符,默认都要添加AV_PKT_FLAG,这指的是每一帧都要添加一个关键帧,否则画面则无法正常解码出来。
2.6.8. AVPacketSideData * side_data:容器可以提供的额外数据包数据,包含多种类型的边信息。
2.6.9. int side_data_element:额外数据包的长度
2.6.10. int64_t duration:AVPacket的持续时间,它的时间以AVStream->time_base为单位作为计算,若未知则为0.
2.6.11.int64_t pos:字节的位置,这个很少用
AVFrame
AVFrame的结构体一般存储音视频的原始数据,若存储视频数据的话则存储YUV/RGB等数据;若存储音频数据的话,则会存储PCM数据,此外还包含了一些重要的相关信息。
2.8.1. unsigned char * buffer:缓存开始位置
2.8.2. int buffer_size:缓存大小
2.8.3. unsigned char * buf_end:缓存结束的位置
2.8.4. void * opaque:URLContext结构体,下面我们来看看URLContext结构体的详细
URLContext结构体成员变量:
typedef struct URLContext {
const AVClass *av_class; ///< information for av_log(). Set by url_open().
struct URLProtocol *prot;
int flags;
int is_streamed; /**< true if streamed (no seek possible), default = false */
int max_packet_size; /**< if non zero, the stream is packetized with this max packet size */
void *priv_data;
char *filename; /**< specified URL */
int is_connected;
AVIOInterruptCB interrupt_callback;
} URLContext;
2.8.4.1. AVClass:它是在FFMPEG中具体应用很多,它类似于C++中的虚基类的结构体,用于实现多继承的功能。它定义了一系列的函数指针,这些函数指针可以被子类所覆盖了,从而实现子类对父类的重载。
2.8.4.2. URLProtocol:每个协议对应一个URLProtocol,这个结构体也不在FFMPEG提供的头文件中。我们具体来看看这个结构体的定义
typedef struct URLProtocol {
const char *name;
int (*url_open)(URLContext *h, const char *url, int flags);
int (*url_read)(URLContext *h, unsigned char *buf, int size);
int (*url_write)(URLContext *h, const unsigned char *buf, int size);
int64_t (*url_seek)(URLContext *h, int64_t pos, int whence);
int (*url_close)(URLContext *h);
struct URLProtocol *next;
int (*url_read_pause)(URLContext *h, int pause);
int64_t (*url_read_seek)(URLContext *h, int stream_index,
int64_t timestamp, int flags);
int (*url_get_file_handle)(URLContext *h);
int priv_data_size;
const AVClass *priv_data_class;
int flags;
int (*url_check)(URLContext *h, int mask);
} URLProtocol;
2.8.4.2.1. url_open:打开对应的文件IO的url地址
2.8.4.2.2. url_read:读取对应的文件IO的url地址的数据内容
2.8.4.2.3. url_write:写入对应的文件IO的url地址的数据内容
2.8.4.2.4. url_seek:移动文件指针
2.8.4.2.5. url_close:关闭文件指针
FFMPEG输出模块初始化
FFMPEG输出配置的框图
分配FFMPEG AVFormatContext输出的上下文结构体指针
第一个传输参数:AVFormatContext结构体指针的指针,是存储音视频封装格式中包含的信息的结构体,所有对文件的封装、编码都是从这个结构体开始。
第二个传输参数:AVOutputFormat的结构体指针,它主要存储复合流信息的常规配置,默认为设置NULL。
第三个传输参数:format_name指的是复合流的格式,比方说:flv、ts、mp4等等
第四个传输参数:filename是输出地址,输出地址可以是本地文件(如:xxx.mp4、xxx.ts等等)。也可以是网络流地址(如:rtmp://xxx.xxx.xxx.xxx:1935/live/01)
配置推流器编码参数和AVStream结构体
第一个传输参数:AVFormatContext的结构体指针
第二个传输参数:AVDictionary结构体指针的指针
返回值:AVStream结构体指针
设置对应的推流器编码器参数
第一个传输参数:传递参数AVCodecID
根据编码器ID分配AVCodecContext结构体
第一个参数:传递AVCodec结构体指针
avcodec_find_encoder的主要作用是通过codec_id(编码器id )找到对应的AVCodec结构体。在RV1126推流项目中codec_id我们使用两种,分别是AV_CODEC_ID_H264、AV_CODEC_ID_H265。并利用avcodec_alloc_context3去创建AVCodecContext上下文。
初始化完AVStream和编码上下文结构体之后,我们就需要对这些参数进行配置。重点:推流编码器参数和RV1126编码器的参数要完全一样,否则可能会出问题.
1920 * 1080编码器和FFMPEG推流器的配置
1280* 720编码器和FFMPEG推流器的配置
AV_CODEC_FLAG_GLOBAL_HEADER:发送视频数据的时候都会在关键帧前面添加SPS/PPS,这个标识符在FFMPEG初始化的时候都需要添加。
设置完上述参数之后,拷贝参数到AVStream编解码器
拷贝参数到AVStream,我们封装到open_video自定义函数里面,要先调用avcodec_open2打开编码器,然后再调用avcodec_parameters_from_context把编码器参数传输到AVStream里面
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
这个函数的具体作用是,打开编解码器
第一个参数:AVCodecContext结构体指针
第二个参数:AVCodec结构体指针
第三个参数:AVDictionary二级指针
int avcodec_parameters_from_context(AVCodecParameters *par, const AVCodecContext *codec);
这个函数的具体作用是,把AVCodecContext的参数拷贝到AVCodecParameters里面。
第一个参数:AVCodecParameters结构体指针
第二个参数:AVCodecContext结构体指针
打开IO文件操作
使用avio_open打开对应的文件,注意这里的文件不仅是指本地的文件也指的是网络流媒体文件,下面是avio_open的定义。
int avio_open(AVIOContext **s, const char *url, int flags);
第一个参数:AVIOContext的结构体指针,它主要是管理数据输入输出的结构体
第二个参数: url地址,这个URL地址既包括本地文件如(xxx.ts、xxx.mp4),也可以是网络流媒体地址,如(rtmp://192.168.22.22:1935/live/01)等
第三个参数:flags标识符
#define AVIO_FLAG_READ 1 /**< read-only */
#define AVIO_FLAG_WRITE 2 /**< write-only */
#define AVIO_FLAG_READ_WRITE (AVIO_FLAG_READ|AVIO_FLAG_WRITE) /**< read-write pseudo flag */
avformat_write_header对头部进行初始化,输出模块头部进行初始化
int avformat_write_header(AVFormatContext *s, AVDictionary **options);
第一个参数:传递AVFormatContext结构体指针
第二个参数:传递AVDictionary结构体指针的指针
FLV流数据
FLV流媒体协议是美国Adobe公司推出来的一种流媒体协议。FLV流媒体格式的特点是封装过后的音视频数据非常小、并且封装的规范相对更加简单,所以FLV流媒体格式非常适合网络传输。但是FLV格式是Adobe公司的私有协议,所以它支持的网络传输协议比较有限:如RTMP、HTTP-FLV。
FLV流媒体格式
FLV流媒体封装格式一般由两个部分组成,一个是FLV Header、另外一个是FLV Body。其中,FLV Header长度固定式9个字节,FLV BODY则是由一组组的Previous Tags Size + Tag组成。Previous Tag Size一般在整个Tag的前面,它一般记录前一个Tag的大小。Tag的类型一般分为三种、分别是脚本数据帧类型、视频数据类型、音频数据类型。
FLV Header
FLV BODY
FLV BODY一般由FLV Tag Header和Tag Data组成。如果是视频的TAG DATA,则FLV Tag Header+ Video Tag Data。而如果是音频的TAG DATA,则是FLV Tag Header + Audio Tag Data。但无论是视频的Tag Data还是音频的Tag Data它们的FLV Tag Header都是相同的,下面我们来看看公共部分的FLV Tag Header
FLV Script Tag
FLV Script Tag也是由FLV Tag Header + Script Data Tag组成。Script Tag的类型一般被称为MedtaData Tag,它一般会存储一些关于FLV音视频的参数信息,比方说:分辨率(width、height)、duration,通常来说Script Tag Data是第一个出现的Tag,并且有且只有一个。
Script Tag是由两个AMF包组合起来(AMF 包 = 数据类型(看下图) + 数据长度 + 数据)。AMF1的第一个字节表示包类型、默认0x02。第2-3个字节代表的是字符串的长度,默认0X00A。 而后面的字节是具体的字符串(“onMetaData”)用十六进制表示:(6f、6e、4d、65、74、61、44、61、74、61
AMF数据类型
而第二个AMF包,第一个字节是0x08表示数组类型。第2-5个字节表示的是数组元素的个数、而后面的数组则是每个数组的键值对。
AMF2数组对应的键值对
FLV VIDEO TAG
FLV VIDEO TAG是由两部分组成,FLV Tag HEADER + VIDEO DATA TAG。
FLV AUDIO TAG
FLV AUDIO TAG是由两部分组成,FLV Tag HEADER + VIDEO DATA TAG。
RTMP协议
RTMP协议是实时消息传输协议的缩写(Real Time Messaging Protocol)的缩写。RTMP协议是基于TCP协议开发的消息传输协议,它是Adobe公司开发的一种私有传输协议。它主要的用途是音视频推流、实时交互语音和数据交互等功能。
RTMP的交互流程
RTMP交互的过程总共可以分为以下几步:握手、建立网络对话、创建网络流、播放。握手,RTMP的握手主要是TCP和RTMP协议之间的握手,它是整个交互过程的开始。建立网络对话,当握手成功之后,就要建立起Client和Server端的连同关系。建立网络流,就是当网络会话建立成功后,建立起发送多媒体数据的通道。播放,指的是Client和Server传输音视频数据的过程。下面是一个大体的RTMP 客户端和服务端交互的过程。
RTMP通信步骤
握手
一个RTMP的开始都是以握手开始的,握手的过程分别为以下步骤:
1). Client端发送C0(版本号),C1(随机字符串)到Server,而Server收到C0或者C1后发送应答S0,S1
2). 当Client收到Server端发来的应答数据S0(版本号),S1(随机字符串)后,则发送C2数据到Server。若此时Server同时收到C0、C1后,就开始发送S2到Client
3). 当Client端和Server端分别收到S2和C2信号后,则代表握手完成。
建立网络会话
1). Client端发送Connect指令到Server端,这个指令的用处就是请求和Server端建立一个连接。
2). Server端接收到Client端发送的指令后,接着发送窗口协议到Client端,并同时连接到应用程序。
3). Server端发送设置带宽的协议到Client端
4). Client端处理带宽协议后,发送确认窗口大小协议到Server端
5). Server端发送用户控制信息指令“Stream Begin”到Client端
6). Server端发送用户消息指令”_result”通知Client端连接状态
建立网络流(NetStream)
1). Client端发送指令”createStream”到Server端
2). Server端接收到”createStream”指令后,则成功创建网络流。S并同时发送”_result”指令通知Client端.
播放流(Play)
1). Client端发送指令”play”到Server端。
2). Server端接收后,Server端发送ChunkSize协议消息到Client端
3). Server端发送控制指令”streambegin”给Client端
4). 若3)步发送成功之后,Server端会发送”响应状态” NetStream.Play.Start & NetStream.Play.reset通知客户端“Play”指令成功。
RTMP消息协议
RTMP的消息协议一般分为三种,分别是消息(Message)、消息块(Chunk)、消息分块(Msg)。
消息(Messgae)
Message是RTMP传输协议的最基本单元。在Messgae中,不同类型的Messgae含有不同的Message Type Id。在RTMP协议中,一共有十几种Message Type,比方说Message Type ID区间在1-6则代表此消息协议是控制协议。
Message Type ID为8代表的是此消息传输音频数据,若Message Type ID 为9则代表的是此消息传输的是视频数据。Message Type ID区间在15到20则用于发送AMF编码指令(AMF指的是Flash和服务端常见的编码方式),比方说播放、暂停、Client和Server端进行交互等。
说完Message Type Id后,我们来介绍一下RTMP的Message的首部信息(Rtmp Header),Rtmp Header是由三元组组成,包括:timestamp delta、mesage_length、type_id。
timestamp delta:和上一个chunk的时间差,若这个值大于等于16777215,该字段必须等于16777215
timestamp :时间戳,每一个Messgae生成的时间戳。
BodySize:指的是message的长度
Typeid:指的是message 类型
消息块(Chunk)
由于在网络传输中,若网络环境不好的情况下,一个消息单元需要拆分成数据量更小的数据块才能够适应网络的传输。在RTMP协议中规定,每个消息都需要被拆分才能够正常传输。拆分的消息分块(Chunk)由Chunk Header 和 Chunk Data组成。Chunk Header由三部分组成:Chunk Basic Header(块基本头)、Chunk Message Header(块消息头)、Extended TimeStamp(扩展时间戳),
Chunk Basic Header:这个字段包含流ID(chunk stream id)和分块类型(fmt),流id一般用csid来表示,chunk type决定了Message Header的格式,Chunk Type长度固定为2bit。若Basic Header长度等于1Byte,则cs id取值范围在[0,64]。
而CSID: 0, 1是由协议保存的特殊协议:0代表的是整个Chunk Basic Header要占用两个字节,CSID的取值范围是[64,319]; 1代表的还是占用三个字节,CSID取值范围[64,65599]。
Chunk Basic Header:1 Byte (8 bit)
Chunk Basic Header:2 Byte (16bit),csid = 0
上面这个图是chunk basic header长度为2byte的情况,这种chunk header的特点是type所在的位置全部设置成0。而第三个字节用CSID----64表现出来,CSID取值范围是[64,319]。
Chunk Basic Header:3 Byte (24bit),csid = 1
上面这个图是chunk basic header长度为3byte的情况,这种情况的chunk header的特点是,第一个字节的位置全部设置为1。而剩余的CSID取值的范围是[64,65599]。
Chunk Message Header:Chunk Message Header一般分成四种类型的消息头,分别是0, 1 , 2 , 3。Message Header主要包含了要发送的具体信息。
Chunk Type(fmt) = 0,占用11bytes(88bit)、
当fmt = 0的时候,整个Message Header占用了11个字节,下面我们来分析每一个数据单元的作用:
TimeStamp(时间戳):表示的是时间戳,占用三个字节。它代表的是解析时候的实时时间戳,它最大的取值范围是0xffffff,当时间戳超过这个最大值的时候,所有字节位设置为1。
Message Length(消息数据的长度):Message Length也和TimeStamp一样占用3个字节,它代表的是实际发送的数据信息,比方说:音频数据长度、视频数据长度。这里要注意的是,Message Length是整个Chunk的总长度,并不是Chunk Data的长度
Message type id(消息类型id):消息类型id占用1个字节,它代表的是实际发送数据的类型id,比方说: 8代表的是音频数据、9代表的是视频数据。
Message stream id(消息流id):消息流id占用4个字节,它代表的是chunk所在的流id。
FMPEG时间戳、时间基、时间转换
FFMPEG 时间基
时间基(time_base):时间基也称之为时间基准,它代表的是每个刻度是多少秒。比方说:视频帧率是25FPS,那它的时间刻度是{1,25}。相当于1s内划分出25个等分,也就是每隔1/25秒后显示一帧视频数据。具体的如下图所示:
时间戳(PTS、DTS)
首先时间戳它指的是在时间轴里面占了多少个格子,时间戳的单位不是具体的秒数,而是时间刻度。只有当时间基和时间戳结合在一起的时候,才能够真正表达出来时间是多少。
比方说:
有一把尺子pts = 25个刻度,time_base = {1,25} 每一个刻度是1/25厘米
所以这把尺子的长度 = pts * time_base = 25* 1/25= 1厘米
PTS:全称是Presentation Time Stamp(显示时间戳),它主要的作用是度量解码后的视频帧什么时候显示出来。
视频PTS计算:n为第n帧视频帧,timebase是{1,framerate},fps是framerate
pts = n *(( 1 / timebase) / fps):
pts = pts++;
举例子:n = 1, pts = 1
n = 2, pts = 2
n =3, pts = 3
音频PTS计算:n为第n帧音频帧,nb_samples指的是采样个数(AAC默认1024),timebase是{1,samplerate},samplerate是采样率
Samplerate = 48000, nb_sample=1024, timebase = {1,48000}
num_pkt = samplerate/nb_samples
pts = n * ( ( 1/ timebase) / num_pkt)
pts = pts+1024
举例子:n = 1, pts = 1024
n = 2, pts = 2048
n = 3, pts = 3072
DTS:表示的是压缩解码的时间戳,在没有B帧的情况下PTS 等于 DTS。假设编码的里面引入了B帧,则还要计算B帧的时间。
没有B帧:dts = pts
存在B帧:dts = pts + b_time
时间转换的原理
在FFMPEG中由于不同的复合流,时间基是不同的,比方说:flv的时间基time_base= {1,1000},假设一个视频time_base = {1,25},我们需要合成mpegts文件,它就需要把time_base = {1,25}占的格子转换成time_base = {1,1000}占的格子。
void av_packet_rescale_ts(AVPacket *pkt, AVRational tb_src, AVRational tb_dst);
第一个参数:AVPacket结构体指针
第二个参数:源时间基
第三个参数:目的时间基
上面这个api的用法是,把AVPacket的时间基tb_src转换成时间基tb_dst。下面我们用H264和AAC时间基TS转换的例子来说明这个转换时间基的用法:
视频H264时间基转换成MPEGTS时间基:
**DST_VIDEO_PTS = VIDEO_PTS * VIDEO_TIME_BASE / DST_TIME_BASE
H264 {1,25} flv{1,1000}
pts = 1 pts = 40
pts = 2 av_packet_rescale_ts pts = 80
pts = 3 pts = 120
pts = 4 pts = 160
音频AAC时间基转换成FLV时间基:
**DST_AUDIO_PTS = AUDIO_PTS * AUDIO_TIME_BASE / DST_TIME_BASE
AAC {1,48000} FLV{1,1000}
pts =1024 pts ~= 21.3
pts =2048 av_packet_rescale_ts pts ~= 42.6
pts =3072 pts ~= 64
pts =4096 pts ~= 85.3
从上述推导的结果可以看出来,如果使用av_packet_rescale_ts的API对视频时间基进行转换,实际上是使用DST_VIDEO_PTS = VIDEO_PTS * VIDEO_TIME_BASE / DST_TIME_BASE去计算推流的视频时间戳。
同理用av_packet_rescale_ts对音频时间基进行转换,实际上是使用DST_AUDIO_PTS = AUDIO_PTS * AUDIO_TIME_BASE / DST_TIME_BASE去计算我们真实推流的音频时间戳。