zoukankan      html  css  js  c++  java
  • SRS之SrsHls::on_audio详解

    1. SrsHls::on_audio

    将音频数据封装到 ts 文件中。

    /*
     * mux the audio packet to ts.
     * @param shared_audio, directly ptr, copy it if need to save it.
     */
    int SrsHls::on_audio(SrsSharedPtrMessage* shared_audio)
    {
        int ret = ERROR_SUCCESS;
        
        /* 检测是够使能了 hls */
        if (!hls_enabled) {
            return ret;
        }
        
        /* hls_dispose: HLS 清理的过期时间(s),系统重启或者超过这个时间时,
         * 清理 HLS 所有的文件,包括 m3u8 和 ts。默认为 0,即不清理 */
        /* update the hls time, for hls_dispose */
        last_update_time = srs_get_system_time_ms();
        
        /* 将传入的音频消息拷贝一个副本 */
        SrsSharedPtrMessage* audio = shared_audio->copy();
        SrsAutoFree(SrsSharedPtrMessage, audio);
        
        /* clear all samples.
         * the sample units never copy the bytes, it directly use the ptr,
         * so when video/audio packet is destroyed, the sample must be clear.
         * in a word, user must clear sample before demux it.
         * @remark demux sample use SrsAvcAacCodec.audio_aac_demux or video_avc_demux.*/
        sample->clear();
        /* 解析音频数据
         * 对于 aac sequence header,会将该消息的负载拷贝到 aac_extra_data 中,
         * 并解析其中相关字段;
         * 对于 aac raw,则直接将负载数据的首地址赋给 sample_units[i]->bytes */
        if ((ret = codec->audio_aac_demux(audio->payload, audio->size, sample)) 
            != ERROR_SUCCESS) {
            if (ret != ERROR_HLS_TRY_MP3) {
                srs_error("hls aac demux audio failed. ret=%d", ret);
                return ret;
            }
            if ((ret = codec->audio_mp3_demux(audio->payload, audio->size, sample)) 
                != ERROR_SUCCESS) {
                srs_error("hls mp3 demux audio failed. ret=%d", ret);
                return ret;
            }
        }
        SrsCodecAudio acodec = (SrsCodecAudio)codec->audio_codec_id;
        
        /* ts support audio codec: aac/mp3 */
        if (acodec != SrsCodecAudioAAC && acodec != SrsCodecAudioMP3) {
            return ret;
        }
        
        /* update_acodec:
         * when open ts, we donot write the header (PSI),
         * for user may need to update the acodec to mp3 or others,
         * so we use delay write PSI, when write audio or video.
         * @remark for audio aac codec, for example, SRS1, 
         *     it's ok to write PSI when open ts.
         * @see https://github.com/ossrs/srs/issues/301
         */
        /* when codec changed, write new header. */
        if ((ret = muxer->update_acodec(acodec)) != ERROR_SUCCESS) {
            srs_error("http: ts audio write header failed. ret=%d", ret);
            return ret;
        }
        
        /* 忽略该 aac sequence header,即暂时不将该消息写入到 ts 中 */
        /* ignore sequence header */
        if (acodec == SrsCodecAudioAAC && 
            sample->aac_packet_type == SrsCodecAudioTypeSequenceHeader) {
            return hls_cache->on_sequence_header(muxer);
        }
        
        /* 第二个参数表示禁止 jitter correct */
        /* TODO: FIXME: config the jitter of HLS. */
        if ((ret = jitter->correct(audio, SrsRtmpJitterAlgorithmOFF)) 
            != ERROR_SUCCESS) {
            srs_error("rtmp jitter correct audio failed. ret=%d", ret);
            return ret;
        }
        
        /* 时间戳将每一秒分成 90000 份,即将每一毫秒分成 90 份,
         * 在 flv 中直接存储的都是毫秒级,在 ts 存储的是时间戳级 */
        /* the dts calc from rtmp/flv header. */
        int64_t dts = audio->timestamp * 90;
        
        /* for pure audio, we need to update the stream dts also. */
        stream_dts = dts;
        
        /* write audio to cache, if need to flush, flush to muxer. */
        if ((ret = hls_cache->write_audio(codec, muxer, dts, sample)) 
            != ERROR_SUCCESS) {
            srs_error("hls cache write audio failed. ret=%d", ret);
            return ret;
        }
        
        return ret;
    }
    

    1.1 SrsAvcAacCodec::audio_aac_demux

    /*
     * demux the audio packet in aac codec.
     * the packet mux in FLV/RTMP format defined in flv specification.
     * demux the audio specified data(sound_format, sound_size, ...) to sample.
     * demux the aac specified data(aac_profile, ...) to codec from sequence header
     * demux the aac raw to sample units.
     */
    int SrsAvcAacCodec::audio_aac_demux(char* data, int size, 
        SrsCodecSample* sample)
    {
        int ret = ERROR_SUCCESS;
        
        sample->is_video = false;
        
        if (!data || size <= 0) {
            srs_trace("no audio present, ignore it.");
            return ret;
        }
        
        if ((ret = stream->initialize(data, size)) != ERROR_SUCCESS) {
            return ret;
        }
        
        /* audio decode */
        if (!stream->require(1)) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("aac decode sound_format failed. ret=%d", ret);
            return ret;
        }
        
        /* @see: E.4.2 Audio Tags, video_file_format_spec_v10_1.pdf, page 76 */
        int8_t sound_format = stream->read_1bytes();
        
        /* 声道:单声道 or 双声道,对于 AAC 永远是双声道 */
        int8_t sound_type = sound_format & 0x01;
        /* 采样精度: 8bit/16bit, 压缩过的音频都是 16bit */
        int8_t sound_size = (sound_format >> 1) & 0x01;
        /* 采样率: AAC 为 44KHz */
        int8_t sound_rate = (sound_format >> 2) & 0x03;
        /* 音频格式: 为 10,即 AAC */
        sound_format = (sound_format >> 4) & 0x0f;
        
        audio_codec_id = sound_format;
        sample->acodec = (SrsCodecAudio)audio_codec_id;
    
        sample->sound_type = (SrsCodecAudioSoundType)sound_type;
        sample->sound_rate = (SrsCodecAudioSampleRate)sound_rate;
        sample->sound_size = (SrsCodecAudioSampleSize)sound_size;
        
        /* we support h.264+mp3 for hls. */
        if (audio_codec_id == SrsCodecAudioMP3) {
            return ERROR_HLS_TRY_MP3;
        }
        
        /* only support aac */
        if (audio_codec_id != SrsCodecAudioAAC) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("aac only support mp3/aac codec. actual=%d, ret=%d", 
                      audio_codec_id, ret);
            return ret;
        }
        
        if (!stream->require(1)) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("aac decode aac_packet_type failed. ret=%d", ret);
            return ret;
        }
        
        /* 0: AAC sequence header, 1: AAC raw */
        int8_t aac_packet_type = stream->read_1bytes();
        sample->aac_packet_type = (SrsCodecAudioType)aac_packet_type;
        
        if (aac_packet_type == SrsCodecAudioTypeSequenceHeader) {
            /* AudioSpecificConfig
             * 1.6.2.1 AudioSpecificConfig, 
             * in aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 33. */
            aac_extra_size = stream->size() - stream->pos();
            if (aac_extra_size > 0) {
                srs_freepa(aac_extra_data);
                aac_extra_data = new char[aac_extra_size];
                memcpy(aac_extra_data, stream->data() + stream->pos(), 
                       aac_extra_size);
    
                /* demux the sequence header. */
                if ((ret = audio_aac_sequence_header_demux(aac_extra_data, aac_extra_size)) 
                    != ERROR_SUCCESS) {
                    return ret;
                }
            }
        } else if (aac_packet_type == SrsCodecAudioTypeRawData) {
            if (!is_aac_codec_ok()) {
                srs_warn("aac ignore type=%d for no sequence header. ret=%d", 
                         aac_packet_type, ret);
                return ret;
            }
            
            if ((ret = sample->add_sample_unit(stream->data() + stream->pos(), 
                       stream->size() - stream->pos())) != ERROR_SUCCESS) {
                srs_error("aac add sample failed. ret=%d", ret);
                return ret;
            }
        } else {
            /* ignored */
        }
        
        /* reset the sample rate by sequence header */
        if (aac_sample_rate != SRS_AAC_SAMPLE_RATE_UNSET) {
            static int aac_sample_rates[] = {
                96000, 88200, 64000, 48000,
                44100, 32000, 24000, 22050,
                16000, 12000, 11025,  8000,
                7350,     0,     0,    0
            };
            /* 根据采样率索引值在采样率表中找到对应的采样率 */
            switch (aac_sample_rates[aac_sample_rate]) {
                case 11025:
                    sample->sound_rate = SrsCodecAudioSampleRate11025;
                    break;
                case 22050:
                    sample->sound_rate = SrsCodecAudioSampleRate22050;
                    break;
                case 44100:
                    sample->sound_rate = SrsCodecAudioSampleRate44100;
                    break;
                default:
                    break;
            };
        }
        
        return ret;
    }
    

    1.1.1 SrsAvcAacCodec::audio_aac_sequence_header_demux

    int SrsAvcAacCodec::audio_aac_sequence_header_demux(char* data, int size)
    {
        int ret = ERROR_SUCCESS;
        
        if ((ret = stream->initialize(data, size)) != ERROR_SUCCESS) {
            return ret;
        }
        
        /*
         * only need to decode the first 2bytes:
         *     audioObjectType, aac_profile, 5bits.
         *     samplingFrequencyIndex, aac_sample_rate, 4bits.
         *     channelConfiguration, aac_channels, 4bits
         */
        if (!stream->require(2)) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("audio codec decode aac sequence header failed. ret=%d", ret);
            return ret;
        }
        u_int8_t profile_ObjectType = stream->read_1bytes();
        u_int8_t samplingFrequencyIndex = stream->read_1bytes();
        
        /* AAC profile(5bits) */
        aac_channels = (samplingFrequencyIndex >> 3) & 0x0f;
        /* 采样率表中的索引值 */
        samplingFrequencyIndex = ((profile_ObjectType << 1) & 0x0e) | 
                                 ((samplingFrequencyIndex >> 7) & 0x01);
        profile_ObjectType = (profile_ObjectType >> 3) & 0x1f;
        
        /* set the aac sample rate.*/
        aac_sample_rate = samplingFrequencyIndex;
        
        /* convert the object type in sequence header to aac profile of ADTS. */
        aac_object = (SrsAacObjectType)profile_ObjectType;
        if (aac_object == SrsAacObjectTypeReserved) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("audio codec decode aac sequence header failed, "
                "adts object=%d invalid. ret=%d", profile_ObjectType, ret);
            return ret;
        }
        
        return ret;
    }
    

    1.1.2 SrsCodecSample::add_sample_unit

    若该音频包为 raw data,则调用该函数进行处理.

    /*
     * add the a sample unit, it's a h.264 NALU or aac raw data.
     * the sample unit directly use the ptr of packet bytes,
     * so user must never use sample unit when packet is destroyed.
     * in a word, user must clear sample before demux it.
     */
    int SrsCodecSample::add_sample_unit(char* bytes, int size)
    {
        int ret = ERROR_SUCCESS;
        
        /*
         * nb_sample_units:
         * each audio/video raw data packet will dumps to one or multiple buffers,
         * the buffers will write to hls and clear to reset.
         * generally, aac audio packet corresponding to one buffer,
         * where avc/h264 video packet may contains multiple buffer.
         */
        if (nb_sample_units >= SRS_SRS_MAX_CODEC_SAMPLE) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("hls decode samples error, "
                "exceed the max count: %d, ret=%d", SRS_SRS_MAX_CODEC_SAMPLE, ret);
            return ret;
        }
        
        SrsCodecSampleUnit* sample_unit = &sample_units[nb_sample_units++];
        sample_unit->bytes = bytes;
        sample_unit->size = size;
        
        /* for video, parse the nalu type, set the IDR flag. */
        if (is_video) {
            SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(bytes[0] & 0x1f);
            
            if (nal_unit_type == SrsAvcNaluTypeIDR) {
                /* whether sample_units contains IDR frame. */
                has_idr = true;
            } else if (nal_unit_type == SrsAvcNaluTypeSPS || 
                       nal_unit_type == SrsAvcNaluTypePPS) {
                has_sps_pps = true;
            } else if (nal_unit_type == SrsAvcNaluTypeAccessUnitDelimiter) {
                has_aud = true;
            }
        
            if (first_nalu_type == SrsAvcNaluTypeReserved) {
                /* The first nalu type. */
                first_nalu_type = nal_unit_type;
            }
        }
        
        return ret;
    }
    

    1.2 SrsHlsCache::on_sequence_header

    /*
     * when get sequence header,
     * must write a #EXT-X-DISCONTINUITY to m3u8.
     * @see: hls-m3u8-draft-pantos-http-live-streaming-12.txt
     * @see: 3.4.11.  EXT-X-DISCONTINUITY
     */
    int SrsHlsCache::on_sequence_header(SrsHlsMuxer* muxer)
    {
        /* TODO: support discontinuity for the same stream
         * currently we reap and insert discontinity when encoder republish,
         * but actually, event when stream is not republish, the
         * sequence header may change, for example,
         * ffmpeg ingest a external rtmp stream and push to srs,
         * when the sequence header changed, the stream is not republish.
         */
        return muxer->on_sequence_header();
    }
    

    1.2.1 SrsHlsMuxer::on_sequence_header

    int SrsHlsMuxer::on_sequence_header()
    {
        int ret = ERROR_SUCCESS;
        
        srs_assert(current);
        
        /* set the current segment to sequence header,
         * when close the segement, it will write a discontinuity to m3u8 file. */
        current->is_sequence_header = true;
        
        return ret;
    }
    

    1.3 SrsHlsCache::write_audio

    将 audio data 写入到缓存中,并将其 flush 到 ts 封装中。

    /*
     * write audio to cache, if need to flush, flush to muxer.
     */
    int SrsHlsCache::write_audio(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer, 
        int64_t pts, SrsCodecSample* sample)
    {
        int ret = ERROR_SUCCESS;
        
        /* write audio to cache. */
        if ((ret = cache->cache_audio(codec, pts, sample)) != ERROR_SUCCESS) {
            return ret;
        }
        
        /* 
         * reap when current source is pure audio.
         * it maybe changed when stream info changed,
         * for example, pure audio when start, audio/video when publishing,
         * pure audio again for audio disabled.
         * so we reap event when the audio incoming when segment overflow.
         * @see https://github.com/ossrs/srs/issues/151
         * we use absolutely overflow of segment to make jwplayer/ffplay happy
         * @see https://github.com/ossrs/srs/issues/151#issuecomment-71155184 */
        if (cache->audio && muxer->is_segment_absolutely_overflow()) {
            srs_info("hls: absolute audio reap segment.");
            /* 若为纯音频时,当 ts 时长超过 hls_fragment*hls_aof_ratio,
             * 这里即为 10*2.0=20s 时,就调用 reap_segment 切割 ts 文件,
             * 这里暂不分析该函数,当纯音频时再分析 */
            if ((ret = reap_segment("audio", muxer, cache->audio->pts)) != ERROR_SUCCESS) {
                return ret;
            }
        }
        
        /* for pure audio, aggregate some frame to one. */
        if (muxer->pure_audio() && cache->audio) {
            if (pts - cache->audio->start_pts < SRS_CONSTS_HLS_PURE_AUDIO_AGGREGATE) {
                return ret;
            }
        }
        
        /* 
         * directly write the audio frame by frame to ts,
         * it's ok for the hls overload, or maye cause the audio corrupt,
         * which introduced by aggregate the audios to a big one.
         * @see https://github.com/ossrs/srs/issues/512
         */
        if ((ret = muxer->flush_audio(cache)) != ERROR_SUCCESS) {
            return ret;
        }
        
        return ret;
    }
    

    1.3.1 SrsTsCache::cache_audio

    /* write audio to cache */
    int SrsTsCache::cache_audio(SrsAvcAacCodec* codec, int64_t dts, SrsCodecSample* sample)
    {
        int ret = ERROR_SUCCESS;
        
        /* 若还没有创建 SrsTsMessage 类的 audio 对象,则新建一个,
         * 该 SrsTsMessage* audio 表征当前的 ts message */
        if (!audio) {
            audio = new SrsTsMessage();
            /* 
             * whether this message with pcr info,
             * generally, the video IDR(I frame, the keyframe of h.264) carray the pcr info. */
            audio->write_pcr = false;
            /* the timestamp in 90khz */
            audio->dts = audio->pts = audio->start_pts = dts;
        }
        
        /* sid 为 pes 层的 stream id,通常为 0xc0,即 SrsTsPESStreamIdAudioCommon */
        /* the id of pes stream to indicates the paylaod codec.
         * @remark use is_audio() and is_video() to check it, 
         * and stream_number() to finger it out. */
        audio->sid = SrsTsPESStreamIdAudioCommon;
        
        /* must be aac or mp3 */
        SrsCodecAudio acodec = (SrsCodecAudio)codec->audio_codec_id;
        srs_assert(acodec == SrsCodecAudioAAC || acodec == SrsCodecAudioMP3);
        
        /* write audio to cache. */
        if (codec->audio_codec_id == SrsCodecAudioAAC) {
            /* 将音频数据写入到 cache 中 */
            if ((ret = do_cache_aac(codec, sample)) != ERROR_SUCCESS) {
                return ret;
            }
        } else {
            if ((ret = do_cache_mp3(codec, sample)) != ERROR_SUCCESS) {
                return ret;
            }
        }
        
        return ret;
    }
    

    1.3.2 SrsTsMessage 构造

    /*
     * the media audio/video message parsed from PES packet.
     */
    SrsTsMessage::SrsTsMessage(SrsTsChannel* c, SrsTsPacket* p)
    {
        /* decoder only, 
         * the ts message does not use them,
         * for user to get the channel and packet. */
        channel = c;
        packet = p;
    
        /* the timestamp in 90khz */
        dts = pts = 0;
        /* the id of pes stream to indicates the payload codec.
         * @remark, use is_audio() and is_video() to check it, 
         * and stream_number() to finger it out. */
        sid = (SrsTsPESStreamId)0x00;
        continuity_counter = 0;
        /* the size of payload, 0 indicates the length() of payload. */
        PES_packet_length = 0;
        /* the payload bytes */
        payload = new SrsSimpleBuffer();
        /* whether got discontinuity ts, for example, sequence header changed. */
        is_discontinuity = false;
    
        /* the audio cache buffer start pts, to flush audio if full.
         * @remark, the pts is not the adjust one, it's the orignal pts. */
        start_pts = 0;
        /* whether this message with pcr info,
         * generally, the video IDR(I frame, the keyframe of h.264) 
         * carray the pcr info. */
        write_pcr = false;
    }
    

    1.3.3 SrsTsCache::do_cache_aac

    int SrsTsCache::do_cache_aac(SrsAvcAacCodec* codec, SrsCodecSample* sample)
    {
        int ret = ERROR_SUCCESS;
        
        /*
         * nb_sample_units: s
         * each audio/video raw data packet will dumps to one or multiple buffers,
         * the buffers will write to hls and clear to reset.
         * generally, aac audio packet corresponding to one buffer,
         * where avc/h264 video packet may contains multiple buffer.
         */
        for (int i = 0; i < sample->nb_sample_units; i++) {
            SrsCodecSampleUnit* sample_unit = &sample->sample_units[i];
            int32_t size = sample_unit->size;
            
            if (!sample_unit->bytes || size <= 0 || size > 0x1fff) {
                ret = ERROR_HLS_AAC_FRAME_LENGTH;
                srs_error("invalid aac frame length=%d, ret=%d", size, ret);
                return ret;
            }
            
            /* the frame length is the AAC raw data plus the adts header size. */
            int32_t frame_length = size + 7;
            
            /*
             * AAC-ADTS
             * 6.2 Audio Data Transport Stream, ADTS
             * in aac-iso-13818-7.pdf, page 26.
             * fixed 7bytes header
             */
            u_int8_t adts_header[7] = {0xff, 0xf9, 0x00, 0x00, 0x00, 0x0f, 0xfc};
            
            /*
             * adts_fixed_header
             * 2B, 16bits
             * int16_t syncword; // 12bits, '1111 1111 1111'
             * int8_t ID; // 1bit, '1'
             * int8_t layer; // 2bits, '00'
             * int8_t protection_absent; // 1bit, can be '1'
             *
             * 12bits
             * int8_t profile; // 2bit, 7.1 Profiles, page 40
             * TSAacSampleFrequency sampling_frequency_index; // 4bits, Table 35, page 46
             * int8_t private_bit; // 1bit, can be '0'
             * int8_t channel_configuration; // 3bits, Table 8
             * int8_t original_or_copy; // 1bit, can be '0'
             * int8_t home; // 1bit, can be '0'
             *
             * adts_variable_header
             * 28bits
             * int8_t copyright_identification_bit; // 1bit, can be '0'
             * int8_t copyright_identification_start; // 1bit, can be '0'
             * int16_t frame_length; // 13bits
             * int16_t adts_buffer_fullness; // 11bits, 7FF signals that the bitstream 
             *                                  is a variable rate bitstream.
             * int8_t number_of_raw_data_blocks_in_frame; // 2bits, 
             *        // 0 indicating 1 raw_data_block()
             */
            /* profile, 2bits */
            SrsAacProfile aac_profile = srs_codec_aac_rtmp2ts(codec->aac_object);
            adts_header[2] = (aac_profile << 6) & 0xc0;
            /* sampling_frequency_index 4bits */
            adts_header[2] |= (codec->aac_sample_rate << 2) & 0x3c;
            /* channel_configuration 3bits */
            adts_header[2] |= (codec->aac_channels >> 2) & 0x01;
            adts_header[3] = (codec->aac_channels << 6) & 0xc0;
            /* frame_length 13bits */
            adts_header[3] |= (frame_length >> 11) & 0x03;
            adts_header[4] = (frame_length >> 3) & 0xff;
            adts_header[5] = ((frame_length << 5) & 0xe0);
            /* adts_buffer_fullness; 11bits */
            adts_header[5] |= 0x1f;
            
            /* copy to audio buffer */
            audio->payload->append((const char*)adts_header, sizeof(adts_header));
            audio->payload->append(sample_unit->bytes, sample_unit->size);
        }
        
        return ret;
    }
    
    • 该函数将每一个音频包都前都加上一个 7 字节的 adts_header。

    1.3.4 SrsSimpleBuffer::append

    /*
     * append specified bytes to buffer.
     * @param size, the size of bytes
     * @remark, assert size is positive
     */
    void SrsSimpleBuffer::append(const char* bytes, int size)
    {
        srs_assert(size > 0);
        
        data.insert(data.end(), bytes, bytes + size);
    }
    

    1.3.5 SrsHlsMuxer::is_segment_absolutely_overflow

    /* drop the segment when duration of ts too small. */
    #define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100
    
    /*
     * whether segment absolutely overflow, for pure audio to reap segment,
     * that is whether the current segment duration>=2*(the segment in config)
     * @see https://github.com/ossrs/srs/issues/151#issuecomment-71155184
     */
    bool SrsHlsMuxer::is_segment_absolutely_overflow()
    {
        /* @see https://github.com/ossrs/srs/issues/151#issuecomment-83553950 */
        srs_assert(current);
        
        /* to prevent very small segment */
        if (current->duration * 1000 < 2 * SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS) {
            return false;
        }
        
        /* use N% deviation, to smoother. */
        double deviation = hls_ts_floor? SRS_HLS_FLOOR_REAP_PERCENT * 
            deviation_ts * hls_fragment : 0.0;
        srs_info("hls: dur=%.2f, tar=%.2f, dev=%.2fms/%dp, frag=%.2f",
                 current->duration, hls_fragment + deviation, 
                 deviation, deviation_ts, hls_fragment);
        
        /* hls_aof_ratio: 倍数,纯音频时,当 ts 时长超过配置的 hls_fragment 
         * 乘以这个系数时就切割文件。例如,当 hls_fragment 是 10 秒,
         * hls_aof_ratio 是 2.0 时,对于纯音频,10s * 2.0 = 20 秒就切割 ts 文件 */
        return current->duration >= hls_aof_ratio * hls_fragment + deviation;
    }
    

    1.3.6 SrsHlsCache::reap_segment

    /*
     * reopen the muxer for a new hls segment,
     * close current segment, open a new segment,
     * then write the key frame to the new segment.
     * so, user must reap_segment then flush_video to hls muxer.
     */
    int SrsHlsCache::reap_segment(string log_desc, SrsHlsMuxer* muxer, 
        int64_t segment_start_dts)
    {
        int ret = ERROR_SUCCESS;
        
        /* TODO: flush audio before or after segment?
         * TODO: fresh segment begin with audio or video? */
        
        /* close current ts. */
        if ((ret = muxer->segment_close(log_desc)) != ERROR_SUCCESS) {
            srs_error("m3u8 muxer close segment failed. ret=%d", ret);
            return ret;
        }
        
        /* open new ts. */
        if ((ret = muxer->segment_open(segment_start_dts)) != ERROR_SUCCES) {
            srs_error("m3u8 muxer open segment failed. ret=%d", ret);
            return ret;
        }
        
        /* segment open, flush video first. */
        if ((ret = muxer->flush_video(cache)) != ERROR_SUCCESS) {
            srs_error("m3u8 muxer flush video failed. ret=%d", ret);
            return ret;
        }
        
        /* segment open, flush the audio.
         * @see: ngx_rtmp_hls_open_fragment
         * start fragment with audio to make iPhone happy */
        if ((ret = muxer->flush_audio(cache)) != ERROR_SUCCESS) {
            srs_error("m3u8 muxer flush audio failed. ret=%d", ret);
            return ret;
        }
        
        return ret;
    }
    

    1.3.7 SrsHlsMuxer::pure_audio

    /* whether current hls muxer is pure audio mode. */
    bool SrsHlsMuxer::pure_audio()
    {
        return current && current->muxer && 
               current->muxer->video_codec() == SrsCodecVideoDisabled;
    }
    

    检测当前 hls muxer 是否是纯音频。

    1.3.8 SrsHlsMuxer::flush_audio

    直接将音频帧逐帧写入到 ts 文件中。

    int SrsHlsMuxer::flush_audio(SrsTsCache* cache)
    {
        int ret = ERROR_SUCCESS;
        
        /* 检测是否已经打开当前要写入的片 */
        /* if current is NULL, segment is not open, ignore the flush event. */
        if (!current) {
            srs_warn("flush audio ignored, for segment is not open.");
            return ret;
        }
        
        /* cacha 缓冲必须有数据 */
        if (!cache->audio || cache->audio->payload->length() <= 0) {
            return ret;
        }
        
        /* 更新当前片的时长 */
        current->update_duration(cache->audio->pts);
        
        /* 将一个音频帧写入到 ts 文件中 */
        if ((ret = current->muxer->write_audio(cache->audio)) != ERROR_SUCCESS) 
        {
            return ret;
        }
        
        /* write success, clear and free the msg */
        srs_freep(cache->audio);
    
        return ret;
    }
    

    1.3.9 SrsHlsSegment::update_duration

    该函数用于更新切片的时长。

    /*
     * update the segment duration.
     * @current_frame_dts, the dts of frame, in tbn of ts.
     */
    void SrsHlsSegment::update_duration(int64_t current_frame_dts)
    {
        /* we use video/audio to update segment duration,
         * so when reap segment, some previous audio frame will
         * update the segment duration, which is nagetive, 
         * just ignore it. */
        if (current_frame_dts < segment_start_dts) {
            /* for atc and timestamp jump, reset the start dts. */
            if (current_frame_dts < 
                segment_start_dts - SRS_AUTO_HLS_SEGMENT_TIMESTAMP_JUMP_MS * 90) 
                
                         segment_start_dts, current_frame_dts);
                segment_start_dts = current_frame_dts;
            }
            return ;
        }
        
        /* duration: duration in seconds in m3u8. */
        duration = (current_frame_dts - segment_start_dts) / 90000.0;
        srs_assert(duration >= 0);
        
        return;
    }
    

    1.3.10 SrsTSMuxer::write_audio

    /* write an audio frame to ts  */
    int SrsTSMuxer::write_audio(SrsTsMessage* audio)
    {
        int ret = ERROR_SUCCESS;
        
        /* ts 文件分三层:
         * 1. ts 层: 是在 pes 层上加入数据流的识别和传输必须的信息
         * 2. pes 层:是在音视频数据上加入了时间戳等对数据帧的说明信息
         * 3. es 层:即音视频数据 */
        /* 将音频帧写入到 PES packet 中 */
        if ((ret = context->encode(writer, audio, vcodec, acodec)) 
            != ERROR_SUCCESS) {
            srs_error("hls encode audio failed. ret=%d", ret);
            return ret;
        }
        
        return ret;
    }
    

    1.3.11 SrsTsContext::encode

    /*
     * write the PES packet, the video/audio stream.
     * @param msg, the video/audio msg to write to ts.
     * @param vc, the video codec, write the PAT/PMT table when changed.
     * @param ac, the audio codec, write the PAT/PMT table when changed.
     */
    int SrsTsContext::encode(SrsFileWriter* writer, SrsTsMessage* msg, 
        SrsCodecVideo vc, SrsCodecAudio ac)
    {
        int ret = ERROR_SUCCESS;
        
        SrsTsStream vs, as;
        int16_t video_pid = 0, audio_pid = 0;
        switch (vc) {
            case SrsCodecVideoAVC:
                vs = SrsTsStreamVideoH264; 
                video_pid = TS_VIDEO_AVC_PID;
                break;
            case SrsCodecVideoDisabled:
                vs = SrsTsStreamReserved;
                break;
            case SrsCodecVideoReserved:
            case SrsCodecVideoReserved1:
            case SrsCodecVideoReserved2:
            case SrsCodecVideoSorensonH263:
            case SrsCodecVideoScreenVideo:
            case SrsCodecVideoOn2VP6:
            case SrsCodecVideoOn2VP6WithAlphaChannel:
            case SrsCodecVideoScreenVideoVersion2:
                vs = SrsTsStreamReserved;
                break;
        }
        switch (ac) {
            case SrsCodecAudioAAC:
                as = SrsTsStreamAudioAAC; 
                audio_pid = TS_AUDIO_AAC_PID;
                break;
            case SrsCodecAudioMP3:
                as = SrsTsStreamAudioMp3; 
                audio_pid = TS_AUDIO_MP3_PID;
                break;
            case SrsCodecAudioDisabled:
                as = SrsTsStreamReserved;
                break;
            case SrsCodecAudioReserved1:
            case SrsCodecAudioLinearPCMPlatformEndian:
            case SrsCodecAudioADPCM:
            case SrsCodecAudioLinearPCMLittleEndian:
            case SrsCodecAudioNellymoser16kHzMono:
            case SrsCodecAudioNellymoser8kHzMono:
            case SrsCodecAudioNellymoser:
            case SrsCodecAudioReservedG711AlawLogarithmicPCM:
            case SrsCodecAudioReservedG711MuLawLogarithmicPCM:
            case SrsCodecAudioReserved:
            case SrsCodecAudioSpeex:
            case SrsCodecAudioReservedMP3_8kHz:
            case SrsCodecAudioReservedDeviceSpecificSound:
                as = SrsTsStreamReserved;
                break;
        }
        
        if (as == SrsTsStreamReserved && vs == SrsTsStreamReserved) {
            ret = ERROR_HLS_NO_STREAM;
            srs_error("hls: no video or audio stream, vcodec=%d, acodec=%d. ret=%d", 
                      vc, ac, ret);
            return ret;
        }
        
        /* when any codec changed, write PAT/PMT table. */
        if (vcodec != vc || acodec != ac) {
            vcodec = vc;
            acodec = ac;
            if ((ret = encode_pat_pmt(writer, video_pid, vs, audio_pid, as)) 
                != ERROR_SUCCESS) {
                return ret;
            }
        }
        
        /* encode the media frame to PES packets over TS. */
        if (msg->is_audio()) {
            /* 将音频数据封装成 pes 包 */
            return encode_pes(writer, msg, audio_pid, as, vs == SrsTsStreamReserved);
        } else {
            return encode_pes(writer, msg, video_pid, vs, vs == SrsTsStreamReserved);
        }
    }
    

    1.3.12 SrsTsContext::encode_pes

    该函数的具体分析可参考: SRS之SrsTsContext::encode_pes详解

  • 相关阅读:
    Rust语言环境配置;Rust版本1.45.2学习
    《代码英雄》系列整理
    Pingcap High Performance TiDB 课程学习记录@yaowenxu
    11. C语言百钱买百鸡问题(详解版)
    10. C语言输出菱形(详解版)
    9. C语言判断素数(求素数)(两种方法)
    8. C语言求圆周率π(三种方法)
    7. C语言杨辉三角(两种方法)
    6. C语言九九乘法表(五种输出形式)
    5. C语言希尔排序算法
  • 原文地址:https://www.cnblogs.com/jimodetiantang/p/9133348.html
Copyright © 2011-2022 走看看