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

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

  1. SRS之SrsHls::on_video详解

    1. SrsHls::on_video /* * mux the video packets to ts. * @param shared_video, directly ptr, copy it i ...

  2. SRS之SrsHlsCache::reap_segment详解

    1. 是否可切片的检测 首先在调用 SrsHlsCache::reap_segment 函数进行切片时,针对音频或视频,都会有一个函数来进行检测当前片的时长是否符合所要求的时长. 对于音频,会调用 S ...

  3. SRS之SrsRtmpConn::stream_service_cycle详解

    首先使用 obs 推流符合如下流程:参考自 Hanvision Makito X cann't publish to SRS.. FFMPEG: C/S: Handshake C: ConnectAp ...

  4. SRS之SrsRtmpConn::service_cycle详解

    1. SrsRtmpConn::service_cycle 当服务器在 conn 线程的开始调用 connect_app 函数接收并解析客户端发送的 connect 消息后,调用该 service_c ...

  5. SRS之SrsTsContext::encode_pes详解

    1. SrsTsContext::encode_pes 该函数位于 srs_kernel_ts.cpp 中.下面的分析基于假设当前要封装的消息是视频. /* * @msg: 要写入到 ts 文件中的音 ...

  6. SRS之SrsRtmpConn::publishing详解

    1. SrsRtmpConn::publishing int SrsRtmpConn::publishing(SrsSource* source) { int ret = ERROR_SUCCESS; ...

  7. SRS之SrsRtmpServer::connect_app详解

    1. connect('live') 2. SrsRtmpServer::connect_app 位于 srs_rtmp_stack.cpp.在 SRS 的 RTMP 连接处理线程 conn 中,当与 ...

  8. linux查看端口及端口详解

    今天现场查看了TCP端口的占用情况,如下图   红色部分是IP,现场那边问我是不是我的程序占用了tcp的链接,,我远程登陆现场查看了一下,这种类型的tcp链接占用了400多个,,后边查了一下资料,说E ...

  9. JMeter学习-023-JMeter 命令行(非GUI)模式详解(一)-执行、输出结果及日志、简单分布执行脚本

    前文 讲述了JMeter分布式运行脚本,以更好的达到预设的性能测试(并发)场景.同时,在前文的第一章节中也提到了 JMeter 命令行(非GUI)模式,那么此文就继续前文,针对 JMeter 的命令行 ...

随机推荐

  1. react——css样式

    1.行内样式: 两个大括号包着.第一个大括号表示里面写js,第二个大括号里面是样式对象 2.传对象 将对象和结构分离,直接写一个大括号,里面写对象 3.将所有的样式对象合并成一个大对象,直接点 以上样 ...

  2. python 利用pyttsx3文字转语音(转)

    原文链接作者 # -*- coding: utf-8 -*- import pyttsx3 engine = pyttsx3.init() with open("all.txt", ...

  3. 5.Servlet 对象(request-response)

    /*ServletResponse*/ /*responese常见应用*/ 1.向客户端输出中文数据 (分别以OutputStream 和 PrintWriter输出) 2.文件下载和中文文件的下载 ...

  4. 简单服务器通信 模型socketserver

    硬件服务器:主机 集群 厂商 :IBM   HP  联想  浪潮 软件服务器 :编写的服务端应用程序,在硬件服务器上运行,一般依托于操作系统,给用户提供一套完整的服务 httpserver --> ...

  5. 《数字图像处理(MATLAB)》冈萨雷斯

    <数字图像处理(MATLAB)>冈萨雷斯 未完结! 参考:数字图像处理——https://blog.csdn.net/dujing2019/article/category/8820151 ...

  6. JAVA中AES对称加密和解密以及与Python兼容

    引言:本文主要解决Java中用AES加密及解密,同时可通过Python脚本对Java加密后的字符进行解密的操作. 由于近期工作中用到需要使用Java对一串密钥进行加密,并且后台通过Python语言读取 ...

  7. tensorflow保存数据为.pb格式和加载pb文件

    转自:https://blog.csdn.net/u014264373/article/details/79943389 https://blog.csdn.net/fu6543210/article ...

  8. Repeater POJ - 3768 (分形)

    Repeater POJ - 3768 Harmony is indispensible in our daily life and no one can live without it----may ...

  9. 【转】GO语言map类型interface{}转换踩坑小记

    原文:https://www.az1314.cn/art/69 ------------------------------------------ mapA := make([string]inte ...

  10. 【换根dp】9.22小偷

    换根都不会了 题目大意 给定一棵$n$个点的树和树上一撮关键点,求到所有$m$个关键点距离的最大值$dis_{max}\le LIM$的点的个数. $n,m\le 30000,LIM\le 30000 ...