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

  • 相关阅读:
    VSCode:无法创建临时目录
    网页很卡的原因
    用css做三角形
    移动端加载页面出现抖动、未加载完成时布局杂乱问题解决
    vue中使用axios进行ajax请求数据(跨域配置)
    fetch和XMLHttpRequest
    1-5-JS基础-数组应用及实例应用
    图片左右切换
    轮播图片切换
    轮播图片切换(函数合并)
  • 原文地址:https://www.cnblogs.com/jimodetiantang/p/9147124.html
Copyright © 2020-2023  润新知