mpegtsenc.
更改记录:
3.2.10到3.3.11
添加了切HEVC;mpegts_flags添加initial_discontinuity属性。
为什么时间戳开始都是pts:133200,dts:126000?
avcodec_encode_video2过后,pts,dts的值为序号,
没有B帧时:如: (0 0),(1 1),(2 2)。。。。。。
有B帧时:如: 0 -2 , 3 -1 , 2 0, 1 1。。。。。。
在write_packet中,传入的PTS为0,DTS为-7200,由于avoid_negative_ts,算的avoid_negative_ts。接着pts,dts都变为非0,如下:
if (pkt->dts != AV_NOPTS_VALUE)
pkt->dts += offset;
if (pkt->pts != AV_NOPTS_VALUE)
pkt->pts += offset;
结果PTS为7200,DTS为0。
static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
{
。。。
进入此函数时,pkt->pts =7200,pkt->dts=0。
// o->mux_max_delay = 0.7;
//oc->max_delay = (int)(o->mux_max_delay * AV_TIME_BASE);
constint64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE) * 2;
//此时delay变为126000。
int64_tdts = pkt->dts, pts = pkt->pts;
if(ts->copyts < 1) {
if (pts != AV_NOPTS_VALUE)
pts += delay; //pts为133200
if (dts != AV_NOPTS_VALUE)
dts += delay; //dts为126000
}
。。。
}
static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
const uint8_t *payload, int payload_size,
int64_t pts, int64_t dts, int key, int stream_id)
{
MpegTSWriteStream *ts_st = st->priv_data;
MpegTSWrite *ts = s->priv_data;
uint8_t buf[TS_PACKET_SIZE];
uint8_t *q;
int val, is_start, len, header_len, write_pcr, is_dvb_subtitle, is_dvb_teletext, flags;
int afc_len, stuffing_len;
int64_t pcr = -1; /* avoid warning */
int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE); //delay为63000
...
}
pts,dts时间戳
PTS,DTS就是音视频同步时间戳,时间戳其实就是一次采样的颗粒(简单理解就是数据),以视频来举例,视频同步时钟90K hz(27M/300),如果帧率是25fps的话,一帧数据采样时间40ms,那么时间戳就是90K x 40ms= 3600(估算值)。
avpriv_set_pts_info(st, 33, 1, 90000); //通过avpriv_set_pts_info(st, 33, 1, 90000)函数,设置AVStream->time_base为1/90000。为什么是90000?因为mpeg的pts、dts都是以90kHz来采样的,所以采样间隔为1/90000秒。
//写pcr
<1>PCR的值和插入时间间隔根据mux_rate的值不同,计算方式都有区别;
<2>当我们设定了mux_rate大于1时,根据我们设定的pcr间隔(默认30ms)进行发送,pcr值为(avio_tell(pb) + 11)* (8 *PCR_TIME_BASE)/ts->mux_rate + ts->first_pcr
<3>当我们设定了mux_rate不大于1时,pcr计算是根据ts_st->user_tb.den / (10 *ts_st->user_tb.num)来计算的。对于25帧视频来说pcr_packet_period值为2,pcr计算方式为(dts - delay) * 300
if(ts->mux_rate > 1) {
service->pcr_packet_period = (int64_t)ts->mux_rate *ts->pcr_period / (TS_PACKET_SIZE * 8 * 1000);
ts->sdt_packet_period =(int64_t)ts->mux_rate * SDT_RETRANS_TIME / (TS_PACKET_SIZE * 8 * 1000);
ts->pat_packet_period =(int64_t)ts->mux_rate * PAT_RETRANS_TIME / (TS_PACKET_SIZE * 8 * 1000);
if (ts->copyts < 1)
ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE,AV_TIME_BASE);
}else {
/* Arbitrary values, PAT/PMT will also be written on video key frames */
ts->sdt_packet_period = 200;
ts->pat_packet_period = 40;
if (pcr_st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
int frame_size = av_get_audio_frame_duration2(pcr_st->codecpar, 0);
if (!frame_size) {
av_log(s, AV_LOG_WARNING,"frame size not set\n");
service->pcr_packet_period = pcr_st->codecpar->sample_rate / (10 * 512);
} else {
service->pcr_packet_period = pcr_st->codecpar->sample_rate / (10 * frame_size);
}
} else {
// max delta PCR 0.1s
// TODO: should be avg_frame_rate
service->pcr_packet_period = ts_st->user_tb.den / (10 *ts_st->user_tb.num);
}
if (!service->pcr_packet_period)
service->pcr_packet_period = 1;
}
//如何写pcr
static int write_pcr_bits(uint8_t *buf,int64_t pcr)
{
int64_t pcr_low = pcr % 300, pcr_high = pcr / 300;
*buf++ = pcr_high >> 25;
*buf++ = pcr_high >> 17;
*buf++ = pcr_high >> 9;
*buf++ = pcr_high >> 1;
*buf++ = pcr_high << 7 |pcr_low >> 8 | 0x7e;
*buf++ = pcr_low;
return 6;
}
//写pts
static void write_pts(uint8_t *q, intfourbits, int64_t pts)
{
int val;
val = fourbits << 4 |(((pts >> 30) & 0x07) << 1) | 1;
*q++ = val;
val = (((pts >> 15) &0x7fff) << 1) | 1;
*q++ = val >> 8;
*q++ = val;
val = (((pts) & 0x7fff)<< 1) | 1;
*q++ = val >> 8;
*q++ = val;
}
mpegts_write_pes
mpegts_write_pes(AVFormatContext *s,AVStream *st,
const uint8_t *payload, intpayload_size,
int64_t pts, int64_t dts)
这个函数就是TS打包的主函数了,这个函数主要功能就是把一帧数据拆分成188字节(感觉效率低了点),并加入PTS,DTS同步信息,这个函数封装的对象是一帧视频或者音频数据,payload,payload_size分别是数据和大小。上面提到的PAT,PMT表在每个188字节都会检查一次,
while (payload_size > 0) {
retransmit_si_info(s, force_pat, dts);
//pes head
/* header size */
header_len = q - buf;
/* data len */
len = TS_PACKET_SIZE - header_len; //能装数据的大小
memcpy(buf + TS_PACKET_SIZE - len, payload, len); //拷贝数据
payload += len;
payload_size -= len;
mpegts_prefix_m2ts_header(s);
avio_write(s->pb, buf, TS_PACKET_SIZE);
}
retransmit_si_info
函数如下,可以看出条件(++ts->pat_packet_count== ts->pat_packet_freq)成立,就会加入PAT,PMT信息,而ts->pat_packet_freq这个值是根据码流大小计算出来。
/* send SDT, PAT and PMT tables regularly*/
static voidretransmit_si_info(AVFormatContext *s, int force_pat, int64_t dts)
{
MpegTSWrite *ts = s->priv_data;
int i;
if (++ts->sdt_packet_count == ts->sdt_packet_period ||
(dts != AV_NOPTS_VALUE && ts->last_sdt_ts == AV_NOPTS_VALUE)||
(dts != AV_NOPTS_VALUE && dts - ts->last_sdt_ts >=ts->sdt_period*90000.0)
){
ts->sdt_packet_count = 0;
if (dts != AV_NOPTS_VALUE)
ts->last_sdt_ts = FFMAX(dts, ts->last_sdt_ts);
mpegts_write_sdt(s);
}
if (++ts->pat_packet_count == ts->pat_packet_period ||
(dts != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE)||
(dts != AV_NOPTS_VALUE && dts - ts->last_pat_ts >=ts->pat_period*90000.0) ||
force_pat) {
ts->pat_packet_count = 0;
if (dts != AV_NOPTS_VALUE)
ts->last_pat_ts = FFMAX(dts, ts->last_pat_ts);
mpegts_write_pat(s);
for (i = 0; i < ts->nb_services; i++)
mpegts_write_pmt(s, ts->services[i]);
}
}