SRS之SrsTsContext::encode_pes详解

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 包都为这种格式,直到将当前视频帧的数据都封装完为止。

posted @ 2018-06-07 15:10  季末的天堂  阅读(788)  评论(0编辑  收藏  举报