杭州手机网站制作公司哪家好,设计网站兼职赚钱,太原seo关键词优化,公司办网站大概多少钱一、引言
从《音视频入门基础#xff1a;MPEG2-TS专题#xff08;3#xff09;——TS Header简介》可以知道#xff0c;TS格式有三种#xff1a;分别为transport packet长度固定为188、192和204字节。而FFmpeg源码中是通过read_packet函数从一段MPEG2-TS传输流/TS文件中读…一、引言
从《音视频入门基础MPEG2-TS专题3——TS Header简介》可以知道TS格式有三种分别为transport packet长度固定为188、192和204字节。而FFmpeg源码中是通过read_packet函数从一段MPEG2-TS传输流/TS文件中读取出一个transport packet的。 二、read_packet函数
一read_packet函数的定义
read_packet函数定义在FFmpeg源码本文演示用的FFmpeg源码版本为7.0.1的源文件libavformat/mpegts.c中
/* return AVERROR_something if error or EOF. Return 0 if OK. */
static int read_packet(AVFormatContext *s, uint8_t *buf, int raw_packet_size,const uint8_t **data)
{AVIOContext *pb s-pb;int len;for (;;) {len ffio_read_indirect(pb, buf, TS_PACKET_SIZE, data);if (len ! TS_PACKET_SIZE)return len 0 ? len : AVERROR_EOF;/* check packet sync byte */if ((*data)[0] ! 0x47) {/* find a new packet start */if (mpegts_resync(s, raw_packet_size, *data) 0)return AVERROR(EAGAIN);elsecontinue;} else {break;}}return 0;
}
该函数的作用是从一段MPEG2-TS传输流/TS文件或内存中读取出接下来的一个transport packet的前188个字节。transport packet长度为192和204字节的TS格式实际上是在188字节的Packet后部加上额外的字段所以read_packet函数只会读取出transport packet的前188字节。对于transport packet长度固定为188字节的TS格式使用read_packet函数可以读取出一个transport packet的全部数据。 形参s既是输入型参数也是输出型参数。指向一个AVFormatContext类型变量。 形参buf输出型参数。仅当“AVIOContext输入缓冲区中还未被读取的数据量不小于计划要读取的字节数TS_PACKET_SIZE188个字节”并且“该MPEG2-TS传输流/TS文件被打开不是为了写入的”时有意义。保存读上来的数据的缓冲区。 形参data输出型参数。“*data”指向保存读上来的数据的缓冲区。执行read_packet函数后“*data”指向缓冲区会存贮被读取到的这个transport packet的前188字节。 返回值返回0表示成功返回一个负数表示出错。 二read_packet函数的内部实现分析
TS_PACKET_SIZE为宏定义等价于188表示一个普通transport packet的长度
#define TS_PACKET_SIZE 188 read_packet函数中首先通过ffio_read_indirect函数从内存或TS文件或socket中读取188个字节数据如果实际读取到的数据小于188字节表示文件读完了或出错了read_packet函数直接返回。关于ffio_read_indirect函数的用法可以参考《FFmpeg源码ffio_read_indirect函数分析》 len ffio_read_indirect(pb, buf, TS_PACKET_SIZE, data);if (len ! TS_PACKET_SIZE)return len 0 ? len : AVERROR_EOF; 如果上述读取到的数据的第一个字节不是同步字节0x47执行mpegts_resync函数进行重新同步操作让AVIOContext文件位置指针s-pb-buf_ptr指向MPEG2-TS传输流/TS文件中的值为“0x47”的同步字节。再通过continue关键字在for循环中重新执行ffio_read_indirect函数重新读取188个字节数据保证读取到的数据的第一个字节为同步字节从而保证通过read_packet函数读取的是一个transport packet的前188个字节数据 /* check packet sync byte */if ((*data)[0] ! 0x47) {/* find a new packet start */if (mpegts_resync(s, raw_packet_size, *data) 0)return AVERROR(EAGAIN);elsecontinue;} else {break;} 三、mpegts_resync函数
一mpegts_resync函数的定义
mpegts_resync函数定义在源文件libavformat/mpegts.c中
static int mpegts_resync(AVFormatContext *s, int seekback, const uint8_t *current_packet)
{MpegTSContext *ts s-priv_data;AVIOContext *pb s-pb;int c, i;uint64_t pos avio_tell(pb);int64_t back FFMIN(seekback, pos);//Special case for files like 01c56b0dc1.tsif (current_packet[0] 0x80 current_packet[12] 0x47 pos TS_PACKET_SIZE) {avio_seek(pb, 12 - TS_PACKET_SIZE, SEEK_CUR);return 0;}avio_seek(pb, -back, SEEK_CUR);for (i 0; i ts-resync_size; i) {c avio_r8(pb);if (avio_feof(pb))return AVERROR_EOF;if (c 0x47) {int new_packet_size, ret;avio_seek(pb, -1, SEEK_CUR);pos avio_tell(pb);ret ffio_ensure_seekback(pb, PROBE_PACKET_MAX_BUF);if (ret 0)return ret;new_packet_size get_packet_size(s);if (new_packet_size 0 new_packet_size ! ts-raw_packet_size) {av_log(ts-stream, AV_LOG_WARNING, changing packet size to %d\n, new_packet_size);ts-raw_packet_size new_packet_size;}avio_seek(pb, pos, SEEK_SET);return 0;}}av_log(s, AV_LOG_ERROR,max resync size reached, could not find sync byte\n);/* no sync found */return AVERROR_INVALIDDATA;
}
该函数的作用是进行重新同步操作让AVIOContext文件位置指针s-pb-buf_ptr指向MPEG2-TS传输流/TS文件中的值为“0x47”的同步字节。 形参s既是输入型参数也是输出型参数。指向一个AVFormatContext类型变量。 形参seekback输入型参数。需要回退的大小值一般为该MPEG2-TS传输流/TS文件中的一个transport packet的长度以字节为单位。 形参current_packet输入型参数保存一个transport packet的数据。 返回值返回0表示成功返回一个负数表示出错。 二mpegts_resync函数的内部实现分析
mpegts_resync函数中首先通过avio_tell函数得到文件位置指针当前位置s-buf_ptr相对于TS文件的文件首s-buffer的偏移字节数。关于avio_tell函数用法可以参考《FFmpeg源码avio_tell函数分析》 uint64_t pos avio_tell(pb); 得到需要回退的最小值 int64_t back FFMIN(seekback, pos); 判断该媒体文件/流是不是属于TS文件的特殊例子非标的TS文件如果是把AVIOContext的文件位置指针回退到离当前位置12 - TS_PACKET_SIZE字节处。关于avio_seek函数的有用法可以参考《FFmpeg源码avio_seek函数分析》 //Special case for files like 01c56b0dc1.tsif (current_packet[0] 0x80 current_packet[12] 0x47 pos TS_PACKET_SIZE) {avio_seek(pb, 12 - TS_PACKET_SIZE, SEEK_CUR);return 0;} 不断通过avio_r8函数读取一个字节数据关于avio_r8函数的有用法可以参考FFmpeg源码avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析如果读取到TS文件的末尾了avio_feof(pb)为真返回AVERROR_EOF关于avio_feof函数的有用法可以参考FFmpeg源码avio_feof函数分析。如果还没读取到末尾并且读取到的数据是“0x47”表示是同步字节执行if (c 0x47)为真时大括号里的操作 for (i 0; i ts-resync_size; i) {c avio_r8(pb);if (avio_feof(pb))return AVERROR_EOF;if (c 0x47) {//...return 0;}} c 0x47为真时首先通过avio_seek函数让AVIOContext的文件位置指针回退一个字节这样pb-buf_ptr就会指向值为“0x47”的同步字节 int new_packet_size, ret;avio_seek(pb, -1, SEEK_CUR); 重新得到此时文件位置指针当前位置s-buf_ptr相对于TS文件的文件首s-buffer的偏移字节数 pos avio_tell(pb); 确保请求的seekback缓冲区大小可用 ret ffio_ensure_seekback(pb, PROBE_PACKET_MAX_BUF);if (ret 0)return ret; 得到这段MPEG2-TS传输流/TS文件中每个transport packet的长度赋值给变量new_packet_size。关于get_packet_size函数的用法可以参考《音视频入门基础MPEG2-TS专题6——FFmpeg源码中获取MPEG2-TS传输流每个transport packet长度的实现》 new_packet_size get_packet_size(s); 如果上述获取到的transport packet的长度跟原来内存中保存的transport packet长度不一致打印日志changing packet size to XXX调整内存中保存的transport packet长度ts-raw_packet_size为新的长度 if (new_packet_size 0 new_packet_size ! ts-raw_packet_size) {av_log(ts-stream, AV_LOG_WARNING, changing packet size to %d\n, new_packet_size);ts-raw_packet_size new_packet_size;} 由于执行上述get_packet_size函数后AVIOContext的文件位置指针pb-buf_ptr会改变所以重新执行一次avio_seek函数确保pb-buf_ptr指向值为“0x47”的同步字节 avio_seek(pb, pos, SEEK_SET);return 0;