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详解

原文地址:https://www.cnblogs.com/jimodetiantang/p/9133348.html

时间: 2024-10-25 09:57:31

SRS之SrsHls::on_audio详解的相关文章

SRS之SrsHls::on_video详解

1. SrsHls::on_video /* * mux the video packets to ts. * @param shared_video, directly ptr, copy it if need to save it. * @param is_sps_pps, whether the video is h.264 sps/pps. */ int SrsHls::on_video(SrsSharedPtrMessage* shared_video, bool is_sps_pps

SRS之SrsHlsCache::reap_segment详解

1. 是否可切片的检测 首先在调用 SrsHlsCache::reap_segment 函数进行切片时,针对音频或视频,都会有一个函数来进行检测当前片的时长是否符合所要求的时长. 对于音频,会调用 SrsHlsMuxer::is_segment_absolutely_overflow 函数进行检测,如下: bool SrsHlsMuxer::is_segment_absolutely_overflow() { srs_assert(current); /* 若当前片的时长小于 200 ms,则直

SRS之SrsRtmpConn::service_cycle详解

1. SrsRtmpConn::service_cycle 当服务器在 conn 线程的开始调用 connect_app 函数接收并解析客户端发送的 connect 消息后,调用该 service_cycle 函数开始服务客户端的具体请求. /** * when valid and connected to vhost/app, service the client. */ int SrsRtmpConn::service_cycle() { int ret = ERROR_SUCCESS; /

Oracle 11g数据库详解(2015-1-18更新)

Oracle 11g数据库详解 整理者:高压锅 QQ:280604597 Email:[email protected] 大家有什么不明白的地方,或者想要详细了解的地方可以联系我,我会认真回复的 1   简介 数据库操作主要有以下几步: 1.  启动.停止数据库 2.  连接.断开数据库 3.  创建.修改.删除数据库用户 4.  表空间 5.  新建.修改.删除表 6.  查询.插入.修改.删除表数据 7.  新建.修改.删除视图 8.  新建.修改.删除存储过程 9.  新建.修改.删除触发

Oracle 11g数据库详解(2015-02-28更新)

Oracle 11g数据库详解 整理者:高压锅 QQ:280604597 Email:[email protected] 大家有什么不明白的地方,或者想要详细了解的地方可以联系我,我会认真回复的 1   简介 数据库操作主要有以下几步: 1.  启动.停止数据库 2.  连接.断开数据库 3.  创建.修改.删除数据库用户 4.  表空间 5.  新建.修改.删除表 6.  查询.插入.修改.删除表数据 7.  新建.修改.删除视图 8.  新建.修改.删除存储过程 9.  新建.修改.删除触发

linux查看端口及端口详解

今天现场查看了TCP端口的占用情况,如下图 红色部分是IP,现场那边问我是不是我的程序占用了tcp的链接,,我远程登陆现场查看了一下,这种类型的tcp链接占用了400多个,,后边查了一下资料,说ESTABLISHED状态 ESTABLISHED的意思是建立连接.表示两台机器正在通信.      之后查找  ncube-lm  发现ncube-lm是一个端口,是nCube License Manager (即ncube管理的一个许可证明),意思是被允许,被认证开放的意思,,, 之后查看端口号 是1

详解EBS接口开发之WIP模块接口

总体说明 文档目的 本文档针对WIP模块业务功能和接口进行分析和研究,对采用并发请求方式和调用API方式分别进行介绍 内容 WIP模块常用标准表简介 WIP事物处理组成 WIP相关业务流程 WIP相关API研究事例 (十)参考文档(七)采购相关的一些知识 (一)WIP模块常用标准表简介 1.1   常用标准表 如下表中列出了与WIP导入相关的表和说明: 表名 说明 其他信息 BOM_STRUCTURES_B BOM头信息 BOM_COMPONENTS_B BOM组件信息 BOM_OPERATIO

Spring事务管理(详解+实例)

写这篇博客之前我首先读了<Spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: Spring事务机制详解 Spring事务配置的五种方式 Spring中的事务管理实例详解 1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是要么都执行要么都

转载:DenseNet算法详解

原文连接:http://blog.csdn.net/u014380165/article/details/75142664 参考连接:http://blog.csdn.net/u012938704/article/details/53468483 本文这里仅当学习笔记使用,具体细节建议前往原文细度. 论文:Densely Connected Convolutional Networks 论文链接:https://arxiv.org/pdf/1608.06993.pdf 代码的github链接:h