1. SrsTsContext::encode_pes
该函数位于 srs_kernel_ts.cpp 中。下面的分析基于假设当前要封装的消息是视频。
/*
* @msg: 要写入到 ts 文件中的音视频消息
* @pid: 音视频消息对应的 PID 值,视频 AVC 的 PID 为 0x100,
* 音频 AAC 的 PID 为 0x101
* @sid: 表示音视频的流类型,视频为 SrsTsStreamVideoH264(0x1b)
* 音频为 SrsTsStreamAudioAAC(0x0f)
* @pure_audio: 表示是否是纯音频
*/
int SrsTsContext::encode_pes(SrsFileWriter* writer, SrsTsMessage* msg,
int16_t pid, SrsTsStream sid, bool pure_audio)
{
int ret = ERROR_SUCCESS;
/* 在首次将 PAT/PMT 写入到 ts 文件中成功后,
* 会将 ready 置为 true */
/* Sometimes, the context is not ready(PAT/PMT write failed),
* error in this situation. */
if (!ready) {
ret = ERROR_TS_CONTEXT_NOT_READY;
srs_error("TS: context not ready, ret=%d", ret);
return ret;
}
if (msg->payload->length() == 0) {
return ret;
}
if (sid != SrsTsStreamVideoH264 && sid != SrsTsStreamAudioMp3 &&
sid != SrsTsStreamAudioAAC) {
srs_info("ts: ignore the unknown stream, sid=%d", sid);
return ret;
}
/* 在首次将 PAT/PMT 写入到 ts 中时,会根据音视频 PID 的构建一个
* SrsTsChannel,并将其放入到 pids 数组中 */
/* 假设当前要编码的消息为视频,则 PID 为 0x100 */
SrsTsChannel* channel = get(pid);
srs_assert(channel);
/* 指向视频消息负载的起始 */
char* start = msg->payload->bytes();
char* end = start + msg->payload->length();
char* p = start;
while (p < end) {
SrsTsPacket* pkt = NULL;
/* 若为首次将该 视频/音频 msg 封装为 PES 包 */
if (p == start) {
/* write pcr according to message. */
/* 若当前的视频消息的 frame_type 为 1,
* 即为 SrsCodecVideoAVCFrameKeyFrame,
* 则表示有 pcr 信息 */
bool write_pcr = msg->write_pcr;
/* for pure audio, always write pcr.
* TODO: FIXME: maybe only need to write at begin and end of ts. */
if (pure_audio && msg->is_audio()) {
write_pcr = true;
}
/* it's ok to set pcr equals to dts,
* @see https://github.com/ossrs/srs/issues/311
* Fig. 3.18. Program Clock Reference of Digital-Video-and-Audio-
* Broadcasting-Technology, page 65
* In MPEG-2, these are the "Program Clock Refer- ence" (PCR) values which
* are nothing else than an up-to-date copy of the STC counter fed into
* transport stream at a certain time. The data stream thus carries an accurate
* internal "clock time". All coding and de- coding processes are controlled by
* this clock time. To do this, the receiver, i.e. thee MPEG decoder, must read
* out the system clock, that is to say its own 42 bit counter. */
int64_t pcr = write_pcr ? msg->dts : -1;
/* TODO: FIXME: finger it why use discontinuity of msg */
pkt = SrsTsPacket::create_pes_first(this,
pid, msg->sid, channel->continuity_counter++, msg->is_discontinuity,
pcr, msg->dts, msg->pts, msg->payload->length());
} else {
/* 该视频还有数据未写入到 ts 中,则继续进行构造一个 ts packet */
pkt = SrsTsPacket::create_pes_continue(this,
pid, msg->sid, channel->continuity_counter++
);
}
SrsAutoFree(SrsTsPacket, pkt);
/* 创建一个 188 字节的临时缓存,因为一个 TS packet 固定为 188 字节 */
char* buf = new char[SRS_TS_PACKET_SIZE];
SrsAutoFreeA(char, buf);
/* set the left bytes with 0xFF */
int nb_buf = pkt->size();
srs_assert(nb_buf < SRS_TS_PACKET_SIZE);
int left = (int)srs_min(end - p, SRS_TS_PACKET_SIZE - nb_buf);
int nb_stuffings = SRS_TS_PACKET_SIZE - nb_buf - left;
if (nb_stuffings > 0) {
/* set all bytes to stuffings. */
memset(buf, 0xFF, SRS_TS_PACKET_SIZE);
/* padding with stuffings. */
pkt->padding(nb_stuffings);
/* size changed, recalc it. */
nb_buf = pkt->size();
srs_assert(nb_buf < SRS_TS_PACKET_SIZE);
left = (int)srs_min(end - p, SRS_TS_PACKET_SIZE - nb_buf);
nb_stuffings = SRS_TS_PACKET_SIZE - nb_buf - left;
srs_assert(nb_stuffings == 0);
}
/* 将 p 指向的视频数据拷贝 left 字节到 buf + nb_buf 地址处 */
memcpy(buf + nb_buf, p, left);
p += left;
SrsStream stream;
if ((ret = stream.initialize(buf, nb_buf)) != ERROR_SUCCESS) {
return ret;
}
/* 将上面构建好的 TS packet 数据写到 stream,即 buf 中 */
if ((ret = pkt->encode(&stream)) != ERROR_SUCCESS) {
srs_error("ts encode ts packet failed. ret=%d", ret);
return ret;
}
/* 将该 188 字节的 buf 数据写入到 ts 文件中 */
if ((ret = writer->write(buf, SRS_TS_PACKET_SIZE, NULL))
!= ERROR_SUCCESS) {
srs_error("ts write ts packet failed. ret=%d", ret);
return ret;
}
}
return ret;
}
- 该函数首先调用
SrsTsPacket::create_pes_first
编码第一个 PES 包(PES 包就是在音视频帧上加入了时间戳等信息).
2. SrsTsPacket::create_pes_first
SrsTsPacket* SrsTsPacket::create_pes_first(SrsTsContext* context,
int16_t pid, SrsTsPESStreamId sid, u_int8_t continuity_counter,
bool discontinuity, int64_t pcr, int64_t dts, int64_t pts, int size
) {
SrsTsPacket* pkt = new SrsTsPacket(context);
/* 1. TS 之 Header */
/* 同步字节,固定为 0x47 */
pkt->sync_byte = 0x47;
/* 传输错误标志 */
pkt->transport_error_indicator = 0;
/* 当 TS Packet 中携带有 PES 数据或 PSI 数据时,该标志需要被置为 1.
* 当 TS Packet 的负载包含有 PES packet 数据时,payload_unit_start_indicator
* 有如下含义:
* 为 1:指示这个 TS Packet 的负载将会以 PES Packet 的第一个字节开始
* 为 0:指示这个 TS Packet 的负载将不会是以 PES Packet 的第一个字节开始
* 若 payload_unit_start_indicator 被设为 1,那么在只有一个 PES Packet 在
* TS Packet 中开始。这也适用于流类型为 6 的私有流。 */
pkt->payload_unit_start_indicator = 1;
/* 传输优先级低 */
pkt->transport_priority = 0;
/* 若当前编码的为视频,则这里设置为视频的 PID,即 0x100 */
pkt->pid = (SrsTsPid)pid;
/* 这里禁止传输加密 */
pkt->transport_scrambling_control = SrsTsScrambledDisabled;
/* 暂时初始化为 0x01: No adaptation_field, payload only */
pkt->adaption_field_control = SrsTsAdaptationFieldTypePayloadOnly;
/* 循环计数器,每编码一个具有相同 PID 的 TS Packet 时,该值就会加一.
* continuity_counter 增加到最大值 15 后又会从 0 开始, 并且当
* adaptation_field_control 的值为 '00' 或 '10' 时不会增加,也即当
* TS Packet 没有负载的时候,该值不会增加. */
pkt->continuity_counter = continuity_counter;
pkt->adaptation_field = NULL;
SrsTsPayloadPES* pes = new SrsTsPayloadPES(pkt);
pkt->payload = pes;
/* 若当前视频消息的帧类型为 1,即 keyframe 时,表明有 pcr */
if (pcr >= 0) {
/* 2. TS 之 adaptation field */
SrsTsAdaptationField* af = new SrsTsAdaptationField(pkt);
/* 指向 adaptation field */
pkt->adaptation_field = af;
/* 重新设置 adaption_field_control 为 '11',表示既有 adaptation field,
* 也有 payload */
pkt->adaption_field_control = SrsTsAdaptationFieldTypeBoth;
af->adaption_field_length = 0; // calc in size.
af->discontinuity_indicator = discontinuity;
af->random_access_indicator = 0;
/* 具有相同 PID 的 TS packet 中 es 数据的优先级 */
af->elementary_stream_priority_indicator = 0;
/* 为 1 表示 adaptation field 有两部分编码的节目参考时钟(PCR) */
af->PCR_flag = 1;
af->OPCR_flag = 0;
af->splicing_point_flag = 0;
af->transport_private_data_flag = 0;
af->adaptation_field_extension_flag = 0;
af->program_clock_reference_base = pcr;
af->program_clock_reference_extension = 0;
}
/* 3. TS 之 paylaod(这里为 PES) */
/* 开始码,固定为 0x000001 */
pes->packet_start_code_prefix = 0x01;
/* 这里视频为0x1b,音频为0x0f */
pes->stream_id = (u_int8_t)sid;
/* 后面 pes 数据的长度,0表示长度不限制,只有视频数据长度会超过 0xffff */
pes->PES_packet_length = (size > 0xFFFF)? 0:size;
/* 数据不加密 */
pes->PES_scrambling_control = 0;
/* 无优先级 */
pes->PES_priority = 0;
pes->data_alignment_indicator = 0;
/* 无版权 */
pes->copyright = 0;
/* 备份 */
pes->original_or_copy = 0;
/* 当 dts 与 pts 相等时,为 0x02,表示仅有 PTS;
* 当 dts 与 pts 不等时,为 0x03,表示同时有 PTS 和 DTS */
pes->PTS_DTS_flags = (dts == pts)? 0x02:0x03;
/* 没有 ESCR 字段 */
pes->ESCR_flag = 0;
/* 没有 ES_rate 字段 */
pes->ES_rate_flag = 0;
/* 没有 trick mode */
pes->DSM_trick_mode_flag = 0;
/* 没有 additional_copy_info */
pes->additional_copy_info_flag = 0;
/* 无 CRC */
pes->PES_CRC_flag = 0;
pes->PES_extension_flag = 0;
pes->PES_header_data_length = 0; // calc in size.
pes->pts = pts;
pes->dts = dts;
return pkt;
}
- 在该函数中,当构造 TS 的负载 PES 数据数据时,是通过 SrsTsPayloadPES 类来构造的,该类的构造函数如下:
2.1 SrsTsPayloadPES 构造
/**
* the PES payload of ts packet.
* 2.4.3.6 PES packet, hls-mpeg-ts-iso13818-1.pdf, page 49
*/
SrsTsPayloadPES::SrsTsPayloadPES(SrsTsPacket* p) : SrsTsPayload(p)
{
PES_private_data = NULL;
pack_field = NULL;
PES_extension_field = NULL;
nb_stuffings = 0;
nb_bytes = 0;
nb_paddings = 0;
const2bits = 0x02;
const1_value0 = 0x07;
}
- 在 create_pes_first 函数中,当 pcr 大于等于 0,即当前为视频帧的 IDR 帧时,表明该帧携带有 pcr 信息,该 pcr 信息封装在 TS 的 adaptation field 中,该部分数据用 SrsTsAdaptationField 类来构造,如下为该类的构造函数。
2.2 SrsTsAdaptationField 构造
/*
* the adaption field of ts packet.
* 2.4.3.5 Semantic definition of fields in adaptation field,
* hls-mpeg-ts-iso13818-1.pdf, page 39
* Table 2-6 - Transport Stream adaptation field,
* hls-mpeg-ts-iso13818-1.pdf, page 40
*/
SrsTsAdaptationField::SrsTsAdaptationField(SrsTsPacket* pkt)
{
packet = pkt;
adaption_field_length = 0;
discontinuity_indicator = 0;
random_access_indicator = 0;
elementary_stream_priority_indicator = 0;
PCR_flag = 0;
OPCR_flag = 0;
splicing_point_flag = 0;
transport_private_data_flag = 0;
adaptation_field_extension_flag = 0;
program_clock_reference_base = 0;
program_clock_reference_extension = 0;
original_program_clock_reference_base = 0;
original_program_clock_reference_extension = 0;
splice_countdown = 0;
transport_private_data_length = 0;
transport_private_data = NULL;
adaptation_field_extension_length = 0;
ltw_flag = 0;
piecewise_rate_flag = 0;
seamless_splice_flag = 0;
ltw_valid_flag = 0;
ltw_offset = 0;
piecewise_rate = 0;
splice_type = 0;
DTS_next_AU0 = 0;
marker_bit0 = 0;
DTS_next_AU1 = 0;
marker_bit1 = 0;
DTS_next_AU2 = 0;
marker_bit2 = 0;
nb_af_ext_reserved = 0;
nb_af_reserved = 0;
const1_value0 = 0x3F;
const1_value1 = 0x1F;
const1_value2 = 0x3F;
}
- 在 encode_pes 函数中,首次调用 create_pes_first 函数构造好一个 pes 包后,接着调用 SrsTsPacket::encode 将该 TS packet 编码到临时缓存中。
3. SrsTsPacket::encode
int SrsTsPacket::encode(SrsStream* stream)
{
int ret = ERROR_SUCCESS;
/* 4B ts packet header. */
if (!stream->require(4)) {
ret = ERROR_STREAM_CASTER_TS_HEADER;
srs_error("ts: mux header failed. ret=%d", ret);
return ret;
}
stream->write_1bytes(sync_byte);
int16_t pidv = pid & 0x1FFF;
pidv |= (transport_priority << 13) & 0x2000;
pidv |= (transport_error_indicator << 15) & 0x8000;
pidv |= (payload_unit_start_indicator << 14) & 0x4000;
stream->write_2bytes(pidv);
int8_t ccv = continuity_counter & 0x0F;
ccv |= (transport_scrambling_control << 6) & 0xC0;
ccv |= (adaption_field_control << 4) & 0x30;
stream->write_1bytes(ccv);
/* 在上面构造的第一个 PES 包中,存在 adaptation filed,并且若该
* 视频消息的帧类型为 keyframe 时,会存在 pcr 信息,该信息是保存
* 在 adaptation field 中的 program_clock_reference_base(节目时钟
* 参考基)字段中 */
if (adaptation_field) {
/* adaptation_field 指向 SrsTsAdaptationField 类对象 */
if ((ret = adaptation_field->encode(stream)) != ERROR_SUCCESS) {
srs_error("ts: mux af faield. ret=%d", ret);
return ret;
}
srs_verbose("ts: mux af ok.");
}
if (payload) {
/* payload 指向 SrsTsPayloadPES 类对象,因此调用该类对象实现的 encode */
if ((ret = payload->encode(stream)) != ERROR_SUCCESS) {
srs_error("ts: mux payload failed. ret=%d", ret);
return ret;
}
srs_verbose("ts: mux payload ok.");
}
return ret;
}
- 存在 adaptation_field,则调用 SrsTsAdaptationField::encode 函数将 adaptation field 中的数据写入到 stream 中。
3.1 SrsTsAdaptationField::encode
int SrsTsAdaptationField::encode(SrsStream* stream)
{
int ret = ERROR_SUCCESS;
if (!stream->require(2)) {
ret = ERROR_STREAM_CASTER_TS_AF;
srs_error("ts: mux af failed. ret=%d", ret);
return ret;
}
stream->write_1bytes(adaption_field_length);
/* When the adaptation_field_control value is '11', the value of the
* adaptation_field_length shall be in the range 0 to 182 */
if (packet->adaption_field_control == SrsTsAdaptationFieldTypeBoth &&
adaption_field_length > 182) {
ret = ERROR_STREAM_CASTER_TS_AF;
srs_error("ts: mux af length failed, must in [0, 182], actual=%d. ret=%d",
adaption_field_length, ret);
return ret;
}
/* When the adaptation_field_control value is '10', the value of the
* adaptation_filed_length shall be 183 */
if (packet->adaption_field_control == SrsTsAdaptationFieldTypeAdaptionOnly &&
adaption_field_length != 183) {
ret = ERROR_STREAM_CASTER_TS_AF;
srs_error("ts: mux af length failed, must be 183, actual=%d. ret=%d",
adaption_field_length, ret);
return ret;
}
/* no adaptation field. */
if (adaption_field_length == 0) {
srs_info("ts: mux af empty.");
return ret;
}
int8_t tmpv = adaptation_field_extension_flag & 0x01;
tmpv |= (discontinuity_indicator << 7) & 0x80;
tmpv |= (random_access_indicator << 6) & 0x40;
tmpv |= (elementary_stream_priority_indicator << 5) & 0x20;
tmpv |= (PCR_flag << 4) & 0x10;
tmpv |= (OPCR_flag << 3) & 0x08;
tmpv |= (splicing_point_flag << 2) & 0x04;
tmpv |= (transport_private_data_flag << 1) & 0x02;
stream->write_1bytes(tmpv);
/* 若存在 PCR 信息 */
if (PCR_flag) {
if (!stream->require(6)) {
ret = ERROR_STREAM_CASTER_TS_AF;
srs_error("ts: mux af PCR_flag failed. ret=%d", ret);
return ret;
}
char* pp = NULL;
char* p = stream->data() + stream->pos();
stream->skip(6);
/* @remark, use pcr base and ignore the extension
* @see https://github.com/ossrs/srs/issues/250#issuecomment-71349370
*/
int64_t pcrv = program_clock_reference_extension & 0x1ff;
pcrv |= (const1_value0 << 9) & 0x7E00;
pcrv |= (program_clock_reference_base << 15) & 0xFFFFFFFF8000LL;
pp = (char*)&pcrv;
*p++ = pp[5];
*p++ = pp[4];
*p++ = pp[3];
*p++ = pp[2];
*p++ = pp[1];
*p++ = pp[0];
}
if (OPCR_flag) {
if (!stream->require(6)) {
ret = ERROR_STREAM_CASTER_TS_AF;
srs_error("ts: demux af OPCR_flag failed. ret=%d", ret);
return ret;
}
stream->skip(6);
srs_warn("ts: mux af ignore OPCR");
}
if (splicing_point_flag) {
if (!stream->require(1)) {
ret = ERROR_STREAM_CASTER_TS_AF;
srs_error("ts: mux af splicing_point_flag failed. ret=%d", ret);
return ret;
}
stream->write_1bytes(splice_countdown);
}
if (transport_private_data_flag) {
if (!stream->require(1)) {
ret = ERROR_STREAM_CASTER_TS_AF;
srs_error("ts: mux af transport_private_data_flag failed. ret=%d",
ret);
return ret;
}
stream->write_1bytes(transport_private_data_length);
if (transport_private_data_length > 0) {
if (!stream->require(transport_private_data_length)) {
ret = ERROR_STREAM_CASTER_TS_AF;
srs_error("ts: mux af transport_private_data_flag failed. ret=%d",
ret);
return ret;
}
stream->write_bytes(transport_private_data,
transport_private_data_length);
}
}
if (adaptation_field_extension_flag) {
if (!stream->require(2)) {
ret = ERROR_STREAM_CASTER_TS_AF;
srs_error("ts: mux af adaptation_field_extension_flag failed. ret=%d", ret);
return ret;
}
stream->write_1bytes(adaptation_field_extension_length);
int8_t ltwfv = const1_value1 & 0x1F;
ltwfv |= (ltw_flag << 7) & 0x80;
ltwfv |= (piecewise_rate_flag << 6) & 0x40;
ltwfv |= (seamless_splice_flag << 5) & 0x20;
stream->write_1bytes(ltwfv);
if (ltw_flag) {
if (!stream->require(2)) {
ret = ERROR_STREAM_CASTER_TS_AF;
srs_error("ts: mux af ltw_flag failed. ret=%d", ret);
return ret;
}
stream->skip(2);
srs_warn("ts: mux af ignore ltw");
}
if (piecewise_rate_flag) {
if (!stream->require(3)) {
ret = ERROR_STREAM_CASTER_TS_AF;
srs_error("ts: mux af piecewise_rate_flag failed. ret=%d", ret);
return ret;
}
stream->skip(3);
srs_warn("ts: mux af ignore piecewise_rate");
}
if (seamless_splice_flag) {
if (!stream->require(5)) {
ret = ERROR_STREAM_CASTER_TS_AF;
srs_error("ts: mux af seamless_splice_flag failed. ret=%d", ret);
return ret;
}
stream->skip(5);
srs_warn("ts: mux af ignore seamless_splice");
}
if (nb_af_ext_reserved) {
stream->skip(nb_af_ext_reserved);
}
}
if (nb_af_reserved) {
stream->skip(nb_af_reserved);
}
return ret;
}
- 在 SrsTsPacket::encode 函数中,将 adaptation field 数据写入到 stream 中后, 接着调用 SrsTsPayloadPES::encode 函数将 paylaod 数据写入到 stream 中.
3.2 SrsTsPayloadPES::encode
int SrsTsPayloadPES::encode(SrsStream* stream)
{
int ret = ERROR_SUCCESS;
/* PES 包大致分为三部分:
* 1. pes fix header(6bytes)
* 2. pes optional header
* 3. pes payload,即 es */
/* 6B fixed header. */
if (!stream->require(6)) {
ret = ERROR_STREAM_CASTER_TS_PSE;
srs_error("ts: mux PSE failed. ret=%d", ret);
return ret;
}
/* 3B */
stream->write_3bytes(packet_start_code_prefix);
/* 1B */
stream->write_1bytes(stream_id);
/* 2B
* the PES_packet_length is the actual bytes size, the pplv
* write to ts is the actual bytes plus the header size. */
int32_t pplv = 0;
if (PES_packet_length > 0) {
pplv = PES_packet_length + 3 + PES_header_data_length;
pplv = (pplv > 0xFFFF)? 0 : pplv;
}
stream->write_2bytes(pplv);
/* check the packet start prefix. */
packet_start_code_prefix &= 0xFFFFFF;
if (packet_start_code_prefix != 0x01) {
ret = ERROR_STREAM_CASTER_TS_PSE;
srs_error("ts: mux PSE start code failed, expect=0x01, actual=%#x. ret=%d",
packet_start_code_prefix, ret);
return ret;
}
/* 3B flags. */
if (!stream->require(3)) {
ret = ERROR_STREAM_CASTER_TS_PSE;
srs_error("ts: mux PSE flags failed. ret=%d", ret);
return ret;
}
/* 1B */
int8_t oocv = original_or_copy & 0x01;
oocv |= (const2bits << 6) & 0xC0;
oocv |= (PES_scrambling_control << 4) & 0x30;
oocv |= (PES_priority << 3) & 0x08;
oocv |= (data_alignment_indicator << 2) & 0x04;
oocv |= (copyright << 1) & 0x02;
stream->write_1bytes(oocv);
/* 1B */
int8_t pefv = PES_extension_flag & 0x01;
pefv |= (PTS_DTS_flags << 6) & 0xC0;
pefv |= (ESCR_flag << 5) & 0x20;
pefv |= (ES_rate_flag << 4) & 0x10;
pefv |= (DSM_trick_mode_flag << 3) & 0x08;
pefv |= (additional_copy_info_flag << 2) & 0x04;
pefv |= (PES_CRC_flag << 1) & 0x02;
stream->write_1bytes(pefv);
/* 1B */
stream->write_1bytes(PES_header_data_length);
/* check required together. */
int nb_required = 0;
nb_required += (PTS_DTS_flags == 0x2)? 5:0;
nb_required += (PTS_DTS_flags == 0x3)? 10:0;
nb_required += ESCR_flag? 6:0;
nb_required += ES_rate_flag? 3:0;
nb_required += DSM_trick_mode_flag? 1:0;
nb_required += additional_copy_info_flag? 1:0;
nb_required += PES_CRC_flag? 2:0;
nb_required += PES_extension_flag? 1:0;
if (!stream->require(nb_required)) {
ret = ERROR_STREAM_CASTER_TS_PSE;
srs_error("ts: mux PSE payload failed. ret=%d", ret);
return ret;
}
/* 5B */
if (PTS_DTS_flags == 0x2) {
if ((ret = encode_33bits_dts_pts(stream, 0x02, pts))
!= ERROR_SUCCESS) {
return ret;
}
}
/* 10B */
if (PTS_DTS_flags == 0x3) {
if ((ret = encode_33bits_dts_pts(stream, 0x03, pts)) != ERROR_SUCCESS) {
return ret;
}
if ((ret = encode_33bits_dts_pts(stream, 0x01, dts)) != ERROR_SUCCESS) {
return ret;
}
/* check sync, the diff of dts and pts should never greater than 1s. */
if (dts - pts > 90000 || pts - dts > 90000) {
srs_warn("ts: sync dts=%"PRId64", pts=%"PRId64, dts, pts);
}
}
/* 6B */
if (ESCR_flag) {
stream->skip(6);
srs_warn("ts: demux PES, ignore the escr.");
}
/* 3B */
if (ES_rate_flag) {
stream->skip(3);
srs_warn("ts: demux PES, ignore the ES_rate.");
}
/* 1B */
if (DSM_trick_mode_flag) {
stream->skip(1);
srs_warn("ts: demux PES, ignore the DSM_trick_mode.");
}
/* 1B */
if (additional_copy_info_flag) {
stream->skip(1);
srs_warn("ts: demux PES, ignore the additional_copy_info.");
}
/* 2B */
if (PES_CRC_flag) {
stream->skip(2);
srs_warn("ts: demux PES, ignore the PES_CRC.");
}
/* 1B */
if (PES_extension_flag) {
int8_t efv = PES_extension_flag_2 & 0x01;
efv |= (PES_private_data_flag << 7) & 0x80;
efv |= (pack_header_field_flag << 6) & 0x40;
efv |= (program_packet_sequence_counter_flag << 5) & 0x20;
efv |= (P_STD_buffer_flag << 4) & 0x10;
efv |= (const1_value0 << 1) & 0xE0;
stream->write_1bytes(efv);
nb_required = 0;
nb_required += PES_private_data_flag? 16:0;
nb_required += pack_header_field_flag? 1+pack_field_length:0; // 1+x bytes.
nb_required += program_packet_sequence_counter_flag? 2:0;
nb_required += P_STD_buffer_flag? 2:0;
nb_required += PES_extension_flag_2? 1+PES_extension_field_length:0; // 1+x bytes.
if (!stream->require(nb_required)) {
ret = ERROR_STREAM_CASTER_TS_PSE;
srs_error("ts: mux PSE ext payload failed. ret=%d", ret);
return ret;
}
stream->skip(nb_required);
srs_warn("ts: demux PES, ignore the PES_extension.");
}
/* stuffing_byte */
if (nb_stuffings) {
stream->skip(nb_stuffings);
srs_warn("ts: demux PES, ignore the stuffings.");
}
return ret;
}
- 在封装 DTS/PTS 时间戳时,会调用 SrsTsPayloadPES::encode_33bits_dts_pts 函数进行打包.
3.3 SrsTsPayloadPES::encode_33bits_dts_pts
int SrsTsPayloadPES::encode_33bits_dts_pts(SrsStream* stream,
u_int8_t fb, int64_t v)
{
int ret = ERROR_SUCCESS;
if (!stream->require(5)) {
ret = ERROR_STREAM_CASTER_TS_PSE;
srs_error("ts: mux PSE dts/pts failed. ret=%d", ret);
return ret;
}
char* p = stream->data() + stream->pos();
stream->skip(5);
int32_t val = 0;
val = fb << 4 | (((v >> 30) & 0x07) << 1) | 1;
*p++ = val;
val = (((v >> 15) & 0x7fff) << 1) | 1;
*p++ = (val >> 8);
*p++ = val;
val = (((v) & 0x7fff) << 1) | 1;
*p++ = (val >> 8);
*p++ = val;
return ret;
}
- 最后将该封装好的第一个 PES 包写入到 ts 文件中。
如下图,帧类型为 keyframe 的视频消息封装的第一个 PES 包
- 接下来,由于当前视频消息还有许多数据没有封装为 PES 包并写入到 ts 文件中,因此,在 SrsTsContext::encode_pes 函数的循环中会继续调用 SrsTsPacket::create_pes_continue 函数封装一个 PES 包,然后将余下视频数据写入到该 PES 的负载中,最后将整个 PES 包写入到 ts 文件中。
4. SrsTsPacket::create_pes_continue
SrsTsPacket* SrsTsPacket::create_pes_continue(SrsTsContext* context,
int16_t pid, SrsTsPESStreamId sid, u_int8_t continuity_counter
{
SrsTsPacket* pkt = new SrsTsPacket(context);
pkt->sync_byte = 0x47;
pkt->transport_error_indicator = 0;
pkt->payload_unit_start_indicator = 0;
pkt->transport_priority = 0;
pkt->pid = (SrsTsPid)pid;
pkt->transport_scrambling_control = SrsTsScrambledDisabled;
/* 仅有 payload,无 adaptation field */
pkt->adaption_field_control = SrsTsAdaptationFieldTypePayloadOnly;
/* 每封装一个 PES 包,该值加 1 */
pkt->continuity_counter = continuity_counter;
pkt->adaptation_field = NULL;
pkt->payload = NULL;
return pkt;
}
- 由该函数比较打包第一个 PES 包所调用的函数 create_pes_first 可知,从第二个 PES 包开始到最后,直到将所有视频数据都封装为 PES 包,并写入到 ts 文件中为止,这往后的所有 PES 包都仅包含两部分:固定 4 字节 ts header 头部和 视频数据(即负载).
下图为将视频数据封装的第二个 PES 包并写入到 ts 文件中
从该图可知,该 PES 包包含两部分:开始固定 4 字节的 TS header,以及 视频数据(即payload)。然后的 PES 包都为这种格式,直到将当前视频帧的数据都封装完为止。