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){ int ret = ERROR_SUCCESS; if (!hls_enabled) { return ret; } /* update the hls time, for hos_dispose. */ last_update_time = srs_get_system_time_ms(); SrsSharedPtrMessage* video = shared_video->copy(); SrsAutoFree(SrsSharedPtrMessage, video); /* user can disable the sps parse to workaround when parse sps failed. */ if (is_sps_pps) { /* 是否使能解析 sps,默认使能 */ codec->avc_parse_sps = _srs_config->get_parse_sps(_req->vhost); } sample->clear(); /* demux the video packet in h.264 codec. * the packet mux in FLV/RTMP format defined in flv specification. * demux the video specified data(frame_type, codec_id, ...) to sample. * demux the h.264 specified data(avc_profile, ...) to codec from sequence header. * demux the h.264 NALUs to sample units. */ if ((ret = codec->video_avc_demux(video->payload, video->size, sample)) != ERROR_SUCCESS) { srs_error("hls codec demux video failed. ret=%d", ret); return ret; } /* ignore info frame */ if (sample->frame_type == SrsCodecVideoAVCFrameVideoInfoFrame) { return ret; } if (codec->video_codec_id != SrsCodecVideoAVC) { return ret; } /* ignore sequence header */ if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame && sample->avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) { return hls_cache->on_sequence_header(muxer); } /* TODO: FIXME: config the jitter of HLS. */ if ((ret = jitter->correct(video, SrsRtmpJitterAlgorithmOFF)) != ERROR_SUCCESS) { srs_error("rtmp jitter correct video failed. ret=%d", ret); return ret; } /* flv 的时间戳(单位 ms)将每一秒分为 90000 份,因此为 1/90 ms */ int64_t dts = video->timestamp * 90; stream_dts = dts; /* write video to muxer */ if ((ret = hls_cache->write_video(codec, muxer, dts, sample)) != ERROR_SUCCESS) { srs_error("hls cache write video failed. ret=%d", ret); return ret; } /* pithy print message. */ hls_show_mux_log(); return ret;}
2. SrsAvcAacCodec::video_avc_demux
接收到一个视频消息,首先调用该函数解析该视频数据。
int SrsAvcAacCodec::video_avc_demux(char* data, int size, SrsCodecSample* sample){ int ret = ERROR_SUCCESS; sample->is_video = true; if (!data || size <= 0) { srs_trace("no video present, ignore it."); return ret; } if ((ret = stream->initialize(data, size)) != ERROR_SUCCESS) { return ret; } /* video decode */ if (!stream->require(1)) { ret = ERROR_HLS_DECODE_ERROR; srs_error("avc decode frame_type failed. ret=%d", ret); return ret; } /* Video Tag 数据区的第一个字节是视频信息 */ /* E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 */ int8_t frame_type = stream->read_1bytes(); /* 编码ID:4bits */ int8_t codec_id = frame_type & 0x0f; /* 帧类型:4bits */ frame_type = (frame_type >> 4) & 0x0f; sample->frame_type = (SrsCodecVideoAVCFrame)frame_type; /* ignore info frame without error * @see https://github.com/ossrs/srs/issues/288#issuecomment-69863909 */ if (sample->frame_type == SrsCodecVideoAVCFrameVideoInfoFrame) { srs_warn("avc igone the info frame, ret=%d", ret); return ret; } /* only support h.264/avc */ if (codec_id != SrsCodecVideoAVC) { ret = ERROR_HLS_DECODE_ERROR; srs_error("avc only support video h.264/avc codec. actual=%d, ret=%d", codec_id, ret); return ret; } video_codec_id = codec_id; if (!stream->require(4)) { ret = ERROR_HLS_DECODE_ERROR; srs_error("avc decode avc_packet_type failed. ret=%d", ret); return ret; } /* AVC Packet 类型:1byte, 0: AVC序列头, 1: AVC NALU 单元 */ int8_t avc_packet_type = stream->read_1bytes(); /* CTS: 3bytes,如果 AVC packet 类型为 1,则为 cts 偏移,其他情况则为 0 * cts = (pts - dts) / 90,单位毫秒 */ int32_t composition_time = stream->read_3bytes(); /* pts = dts + cts. */ sample->cts = composition_time; sample->avc_packet_type = (SrsCodecVideoAVCType)avc_packet_type; if (avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) { /* 解析 sps,pps 数据 */ if ((ret = avc_demux_sps_pps(stream)) != ERROR_SUCCESS) { return ret; } } else if (avc_packet_type == SrsCodecVideoAVCTypeNALU){ /* 检测 H264 的封装格式为 AnnexB 还是 "ISO Base Media File Format", * 然后根据相应封装格式的特点提取出 NALU,将该 NALU 保存到 sample 的 * sample_units 数组中 */ if ((ret = video_nalu_demux(stream, sample)) != ERROR_SUCCESS) { return ret; } } else { /* ignored. */ } return ret;}
接下来,若检测接收到的 video packet type 为 1,即为 NAL 单元,则调用 video_nalu_demux 进行解封装。
2.1 SrsAvcAacCodec::video_nalu_demux
int SrsAvcAacCodec::video_nalu_demux(SrsStream* stream, SrsCodecSample* sample){ int ret = ERROR_SUCCESS; /* ensure the sequence header demuxed */ if (!is_avc_codec_ok()) { srs_warn("avc ignore type=%d for no sequence header. ret=%d", SrsCodecVideoAVCTypeNALU, ret); return ret; } /* 在 SrsAvcAacCodec 构造函数中初始化该变量为 SrsAvcPayloadFormatGuess */ /* guess for the first time. */ if (payload_format == SrsAvcPayloadFormatGuess) { /* One or more NALUs (Full frames are required) * try "AnnexB" from H.264-AVC-ISO_IEC_14496-10.pdf, page 211. */ if ((ret = avc_demux_annexb_format(stream, sample)) != ERROR_SUCCESS) { /* stop try when system error. */ if (ret != ERROR_HLS_AVC_TRY_OTHERS) { srs_error("avc demux for annexb failed. ret=%d", ret); return ret; } /* try "ISO Base Media File Format" from * H.264-AVC-ISO_IEC_14496-15.pdf, page 20" */ if ((ret = avc_demux_ibmf_format(stream, sample)) != ERROR_SUCCESS) { return ret; } else { payload_format = SrsAvcPayloadFormatIbmf; srs_info("hls guess avc payload is ibmf format."); } } else { payload_format = SrsAvcPayloadFormatAnnexb; srs_info("hls guess avc payload is annexb format."); } } else if (payload_format == SrsAvcPayloadFormatIbmf) { /* try "ISO Base Media File Format" from H.264-AVC-ISO_IEC_14496-15.pdf, page 20 */ if ((ret = avc_demux_ibmf_format(stream, sample)) != ERROR_SUCCESS) { return ret; } srs_info("hls decode avc payload in ibmf format."); } else { /* One or more NALUs (Full frames are required) * try "AnnexB" from H.264-AVC-ISO_IEC_14496-10.pdf, page 211. */ if ((ret = avc_demux_annexb_format(stream, sample)) != ERROR_SUCCESS) { /* ok, we guess out the payload is annexb, but maybe changed to ibmf. */ if (ret != ERROR_HLS_AVC_TRY_OTHERS) { srs_error("avc demux for annexb failed. ret=%d", ret); return ret; } /* try "ISO Base Media File Format" from * H.264-AVC-ISO_IEC_14496-15.pdf, page 20 */ if ((ret = avc_demux_ibmf_format(stream, sample)) != ERROR_SUCCESS) { return ret; } else { payload_format = SrsAvcPayloadFormatIbmf; srs_warn("hls avc payload change from annexb to ibmf format."); } } srs_info("hls decode avc payload in annexb format."); } return ret;}
下面先尝试为当前的 H264 封装为 Annexb 格式,因此调用 SrsAvcAacCodec::avc_demux_annexb_format 函数进行解析。
2.2 SrsAvcAacCodec::avc_demux_annexb_format
int SrsAvcAacCodec::avc_demux_annexb_format(SrsStream* stream, SrsCodecSample* sample){ int ret = ERROR_SUCCESS; /* not annexb, try others */ if (!srs_avc_startswith_annexb(stream, NULL)) { return ERROR_HLS_AVC_TRY_OTHERS; } /* AnnexB * B.1.1 Byte stream NAL unit syntax, * H.264-AVC-ISO_IEC_14496-10.pdf, page 211. */ while (!stream->empty()) { /* find start code */ int nb_start_code = 0; if (!srs_avc_startswith_annexb(stream, &nb_start_code)) { return ret; } /* skip the start code. */ if (nb_start_code > 0) { stream->skip(nb_start_code); } /* the NALU start bytes. */ char* p = stream->data() + stream->pos(); /* get the last matched NALU */ while (!stream->empty()) { if (srs_avc_startswith_annexb(stream, NULL)) { break; } stream->skip(1); } /* 此时 pp 指向下一个 NALU start bytes */ char* pp = stream->data() + stream->pos(); /* skip the empty. */ if (pp - p <= 0) { continue; } /* 获取到一个 NALU 后,将该 NALU 添加到 sample 中的 sample_units 数组中 */ /* got the NALU. */ if ((ret = sample->add_sample_unit(p, pp - p)) != ERROR_SUCCESS) { srs_error("annexb add video sample failed. ret=%d", ret); return ret; } } return ret;}
2.2.1 srs_avc_startswith_annexb
/* * whether stream starts with the avc NALU in "AnnexB" * from H.264-AVC-ISO_IEC_14496-10.pdf, page 211. * start code must be "N[00] 00 00 01" where N>=0 * @param pnb_start_code, output the size of start code, must >=3. * NULL to ignore. */bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code){ char* bytes = stream->data() + stream->pos(); char* p = bytes; for ( ;; ) { if (!stream->require(p - bytes + 3)) { return false; } /* not match */ if (p[0] != (char)0x00 || p[1] != (char)0x00) { return false; } /* match N[00] 00 00 01, where N>=0 */ if (p[2] == (char)0x01) { if (pnb_start_code) { *pnb_start_code = (int)(p - bytes) + 3; } return true; } p++; } return false;}
由该代码可知,若 H264 为 Annexb 封装格式,则 NALU 之间是以 0x000001(3bytes) 或者 0x00000001(4bytes) 分割。
2.2.2 SrsCodecSample::add_sample_unit
/* * 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; /* sample_units 数组的最大值为 128 */ 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; } /* 从 sample_uints 数组中取出一个 sample_unit,用于存放获取到的 NALU 或 aac raw data */ SrsCodecSampleUnit* sample_unit = &sample_units[nb_sample_units++]; sample_unit->bytes = bytes; /* 该 NALU 单元的大小 或 aac raw data 的大小 */ 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) { /* 若当前 NALU 为 I 帧,则置位该标志位,表示当前 sample_units 数组中含有 I 帧 */ has_idr = true; } else if (nal_unit_type == SrsAvcNaluTypeSPS || nal_unit_type == SrsAvcNaluTypePPS) { /* Whether exists SPS/PPS NALU. */ has_sps_pps = true; } else if (nal_unit_type == SrsAvcNaluTypeAccessUnitDelimiter) { /* Whether exists AUD NALU. */ has_aud = true; } if (first_nalu_type == SrsAvcNaluTypeReserved) { /* 记录 sample_units 数组中第一个 NALU 的类型 */ first_nalu_type = nal_unit_type; } } return ret;}
- 若上面尝试调用
avc_demux_annexb_format
函数失败返回,即表明当前 H.264 不是以 AnnexB 格式封装的(即各 NALU 单元之间不是以 0x000001 或 0x00000001 分割的),则接下来尝试调用avc_demux_ibmf_format
函数进行解封装,即再次尝试该 H.264 是否为ISO Base Media File Format
。
2.3 SrsAvcAacCodec::avc_demux_ibmf_format
/* * demux the avc NALU in "ISO Base Media File Format" * from H.264-AVC-ISO_IEC_14496-15.pdf, page 20 */int SrsAvcAacCodec::avc_demux_ibmf_format(SrsStream* stream, SrsCodecSample* sample){ int ret = ERROR_SUCCESS; int PictureLength = stream->size() - stream->pos(); /* * 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 * 5.2.4.1 AVC decoder configuration record * 5.2.4.1.2 Semantics * The value of this field shall be one of 0, 1, or 3 corresponding to a * length encoded with 1, 2, or 4 bytes, respectively. */ srs_assert(NAL_unit_length != 2); /* * 该 NAL_unit_length 的值即为解析 sps 的获取到的 lengthSizeMinusOne 字段值 */ /* 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 20 */ for (int i = 0; i < PictureLength; ) { /* unsigned int((NAL_unit_length+1)*8) NALUnitLength; */ if (!stream->require(NAL_unit_length + 1)) { ret = ERROR_HLS_DECODE_ERROR; srs_error("avc decode NALU size failed. ret=%d", ret); return ret; } int32_t NALUnitLength = 0; if (NAL_unit_length == 3) { NALUnitLength = stream->read_4bytes(); } else if (NAL_unit_length == 1) { NALUnitLength = stream->read_2bytes(); } else { NALUnitLength = stream->read_1bytes(); } /* maybe stream is invalid format. * see: https://github.com/ossrs/srs/issues/183 */ if (NALUnitLength < 0) { ret = ERROR_HLS_DECODE_ERROR; srs_error("maybe stream is AnnexB format. ret=%d", ret); return ret; } /* NALUnit */ if (!stream->require(NALUnitLength)) { ret = ERROR_HLS_DECODE_ERROR; srs_error("avc decode NALU data failed. ret=%d", ret); return ret; } /* 7.3.1 NAL unit syntax, H.264-AVC-ISO_IEC_14496-10.pdf, page 44. */ if ((ret = sample->add_sample_unit(stream->data() + stream->pos(), NALUnitLength)) != ERROR_SUCCESS) { srs_error("avc add video sample failed. ret=%d", ret); return ret; } stream->skip(NALUnitLength); i += NAL_unit_length + 1 + NALUnitLength; } return ret;}
- 由该函数源码可知,若 H264 为
ISO Base Media File Format
,则各个 NALUnit 之间是以 1byte 或 2bytes 或 4bytes 分割的,这 1byte 或 2bytes 或 4bytes 即为NALUnitLength
所占的字节数,具体为 1byte 还是 2bytes 或者 4bytes 是由 sps 中的lengthSizeMinusOne
值决定的。若lengthSizeMinusOne
值为 3,则NALUnitLength
占 4bytes;若lengthSizeMinusOne
值为 1,则NALUnitLength
占 2bytes;若lengthSizeMinusOne
值为 0,则ALUnitLength
占 1 字节。
3. SrsHlsCache::write_video
/* * write video to muxer. */int SrsHlsCache::write_video(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer, int64_t dts, SrsCodecSample* sample){ int ret = ERROR_SUCCESS; /* write video to cache. */ if ((ret = cache->cache_video(codec, dts, sample)) != ERROR_SUCCESS) { return ret; } /* when segment overflow, reap if possible */ if (muxer->is_segment_overflow()) { /* do reap ts if any of: * a. wait keyframe and got keyframe. * b. always reap when not wait keyframe */ if (!muxer->wait_keyframe() || sample->frame_type == SrsCodecVideoAVCFrameKeyFrame) { /* reap the segmtn, which will also flush the video. */ if ((ret = reap->segment("video", muxer, cache->video->dts)) != ERROR_SUCCESS) { return ret; } } } /* flush video when got one */ if ((ret = muxer->flush_video(cache)) != ERROR_SUCCESS) { srs_error("m3u8 muxer flush video failed. ret=%d", ret); return ret; } return ret;}
- 在
SrsHlsCache::write_video
函数中,首先调用SrsTsCache::cache_video
函数将已经保存在 sample 中成员 sample_units 数组中的 NALU 保存到 cache 中。
3.1 SrsTsCache::cache_video
int SrsTsCache::cache_video(SrsAvcAacCodec* codec, int64_t dts, SrsCodecSample* sample){ int ret = ERROR_SUCCESS; /* create the ts video message. */ if (!video) { video = new SrsTsMessage(); /* * write_pcr: * whether thisi message with pcr info, * generally, the video IDR(I frame, the keyframe of h.264) carray the pcr info. */ /* 若当前帧类型为 I帧,表明携带有 pcr 信息 */ video->write_pcr = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame; /* * start_pts: * the audio cache buffer start pts, to flush audio if full. * @remark, the pts is not the adjust one, it's the original pts. */ video->start_pts = dts; } /* 对于 video,flv/rtmp 的时间戳即为 video 的 dts */ video->dts = dts; /* pts = dts + cts */ video->pts = video->dts + sample->cts * 90; // in ms /* stream id, 视频取值为 (0xe0~0xef),通常为 0xe0,这里即 SrsTsPESStreamIdVideoCommon */ video->sid = SrsTsPESStreamIdVideoCommon; /* write video to cache. */ if ((ret = do_cache_avc(codec, sample)) != ERROR_SUCCESS) { return ret; } return ret;}
若当前 SrsTsCache 之前从未缓存过视频数据,即 video 为 NULL,则构建一个新的 SrsTsMessage 类的对象,video 指向该对象。
3.1.1 SrsTsMessage 构造
/* * the media audio/video message parsed from PES packet. */SrsTsMessage::SrsTsMessage(SrsTsChannel* c, SrsTsPacket* p){ /* * channel and packet: * decoder only, * the ts message does not use them, * for user to get the channel and packet. */ channel = c; packet = p; /* * dts and pts: * the timestamp in 90khz */ dts = pts = 0; /* * sid: * 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: the chunk id. */ continuity_counter = 0; /* * PES_packet_length: the size of payload, 0 indicates the length() of payload. */ PES_packet_length = 0; /* * payload: the payload bytes. */ payload = new SrsSimpleBuffer(); /* * is_discontinuity: whether got discontinuity ts, for example, * sequence header changed. */ is_discontinuity = false; /* * start_pts: 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; /* * write_pcr: * whether this message with pcr info, * generally, the video IDR(I frame, the keyframe of h.264) carray the pcr info. */ write_pcr = false;}
- 在
SrsTsCache::cache_video
函数中,构建 SrsTsMessage 并初始化时间戳等信息后,接着调用SrsTsCache::do_cache_avc
函数将 video 数据写入到SrsTsCache.video->payload
中.
3.1.2 SrsTsCache::do_cache_avc
int SrsTsCache::do_cache_avc(SrsAvcAacCodec* codec, SrsCodecSample* sample){ int ret = ERROR_SUCCESS; /* whether aud inserted. */ bool aud_inserted = false; /* Insert a default AUD NALU when no AUD in samples. */ if (!sample->has_aud) { /* * the aud(access unit delimiter) before each frame. * 7.3.2.4 Access unit delimiter RBSP syntax * H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 66. * * primary_pis_type u(3), the first 3bits, primary_pic_type indicates * that the slice_type values for all slices of the primary coded * picture are numbers of the set listed in Table 7-5 for the given * value of primary_pic_type. * 0, slice_type 2, 7 * 1, slice_type 0, 2, 5, 7 * 2, slice_type 0, 1, 2, 5, 6, 7 * 3, slice_type 4, 9 * 4, slice_type 3, 4, 8, 9 * 5, slice_type 2, 4, 7, 9 * 6, slice_type 0, 2, 3, 4, 5, 7, 8, 9 * 7, slice_type 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 * 7.4.2.4 Access unit delimiter RBSP semantics * H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 102. * * slice_type specifies the coding type of the slice according to Table 7-6. * 0, P (P slice) * 1, B (B slice) * 2, I (I slice) * 3, SP (SP slice) * 4, SI (SI slice) * 5, P (P slice) * 6, B (B slice) * 7, I (I slice) * 8, SP (SP slice) * 9, SI (SI slice) * H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 105. */ static u_int8_t default_aud_nalu[] = { 0x09, 0xf0 }; /* 这里封装 H264 使用的是 Annexb 格式,在 aud 前会插入 * 4 字节的分隔符: 0x00000001 */ srs_avc_insert_aud(video->payload, aud_inserted); video->payload->append((const char*)default_aud_nalu, 2); } /* 除了 AUD 前插入的是 4 字节的分隔符 0x00000001 外,其他的 * NALU 前插入的都为 3 字节的分隔符: 0x000001 */ bool is_sps_pps_appended = false; /* all sample use cont nalu header, except the sps-pps before IDR frame. */ 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) { ret = ERROR_HLS_AVC_SAMPLE_SIZE; srs_error("invalid avc sample length=%d, ret=%d", size, ret); return ret; } /* * 5bits, 7.3.1 NAL unit syntax, * H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 83. */ SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(sample_unit->bytes[0] & 0x1f); /* 在 IDR 帧前先插入 sps 和 pps */ /* * Insert sps/pps before IDR when there is no sps/pps in samples. * The sps/pps is parsed from sequence header(generally the first flv packet). */ if (nal_unit_type == SrsAvcNaluTypeIDR && !sample->has_sps_pps && !is_sps_pps_appended) { if (codec->sequenceParameterSetLength > 0) { /* 插入 3 字节的分隔符: 0x000001 */ srs_avc_insert_aud(video->payload, aud_inserted); /* 接着插入 sps */ video->payload->append(codec->sequenceParameterSetNALUnit, codec->sequenceParameterSetLength); } if (codec->pictureParameterSetLength > 0) { srs_avc_insert_aud(video->payload, aud_inserted); video->payload->append(codec->pictureParameterSetNALUnit, codec->pictureParameterSetLength); } is_sps_pps_appended = true; } /* Insert the NALU to video in annexb. */ srs_avc_insert_aud(video->payload, aud_inserted); video->payload->append(sample->unit->bytes, sample->unit->size); } return ret;}
若当前为接收到 sps 和 pps 后的第二个 video 消息,则根据上面源码可知,在 video->payload 中各 nalu 之间的格式为:
annexb 4B header, 2B aud(nal_unit_type:9)(0x09 0xf0)(AUD)annexb 3B header, 19B sps(nal_unit_type:7)(SPS)annexb 3B header, 4B pps(nal_unit_type:8)(PPS)annexb 3B header, 12B nalu(nal_unit_type:6)(SEI)annexb 3B header, 2762B nalu(nal_unit_type:5)(IDR)
- 在
SrsTsCache::do_cache_avc
函数中,首先检测 sample 中是否已经有 aud(即接入单元定界符),若没有,则首先在video->payload
中插入一个 aud。
3.1.3 srs_avc_insert_aud
void srs_avc_insert_aud(SrsSimpleBuffer* payload, bool& aud_inserted){ /* * mux the samples in annexb format, * H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 324. */ /** * 00 00 00 01 // header * xxxxxxx // data bytes * 00 00 01 // continue header * xxxxxx // data bytes * * nal_unit_type specifies the type of RBSP data structure contained in the NAL * unit as specified in Table 7-1. * Table 7-1 - NAL unit type codec, syntax element categories, and NAL unit * type classes H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 83. * 1, Coded slice of a non-IDR picture slice_layer_without_partitioning_rbsp( ) * 2, Coded slice data partition A slice_data_partition_a_layer_rbsp( ) * 3, Coded slice data partition B slice_data_partition_b_layer_rbsp( ) * 4, Coded slice data partition C slice_data_partition_c_layer_rbsp( ) * 5, Coded slice of an IDR picture slice_layer_without_partitioning_rbsp( ) * 6, Supplemental enhancement information (SEI) sei_rbsp( ) * 7, Sequence parameter set seq_parameter_set_rbsp( ) * 8, Picture parameter set pic_parameter_set_rbsp( ) * 9, Access unit delimiter access_unit_delimiter_rbsp( ) * 10, End of sequence end_of_seq_rbsp( ) * 11, End of stream end_of_stream_rbsp( ) * 12, Filler data filler_data_rbsp( ) * 13, Sequence parameter set extension seq_parameter_set_extension_rbsp( ) * 14, Prefix NAL unit prefix_nal_unit_rbsp( ) * 15, Subset sequence parameter set subset_seq_parameter_set_rbsp( ) * 19, Coded slice of an auxiliary coded picture without partitioning * slice_layer_without_partitioning_rbsp( ) * 20, Coded slice extension slice_layer_extension_rbsp( ) * the first ts message of apple sample: * annexb 4B header, 2B aud(nal_unit_type:9)(0x09 0xf0)(AUD) * annexb 3B header, 19B sps(nal_unit_type:7)(SPS) * annexb 3B header, 4B pps(nal_unit_type:8)(PPS) * annexb 3B header, 12B nalu(nal_unit_type:6)(SEI) * annexb 3B header, 21B nalu(nal_unit_type:6)(SEI) * annexb 3B header, 2762B nalu(nal_unit_type:5)(IDR) * annexb 3B header, 3535B nalu(nal_unit_type:5)(IDR) * the second ts message of apple ts sample: * annexb 4B header, 2B aud(nal_unit_type:9)(0x09 0xf0)(AUD) * annexb 3B header, 21B nalu(nal_unit_type:6)(SEI) * annexb 3B header, 379B nalu(nal_unit_type:1)(non-IDR,P/B) * annexb 3B header, 406B nalu(nal_unit_type:1)(non-IDR,P/B) * @remark we use the sequence of apple * samples http://ossrs.net/apple-sample/bipbopall.m3u8 */ static u_int8_t fresh_nalu_header[] = { 0x00, 0x00, 0x00, 0x01 }; static u_int8_t cont_nalu_header[] = { 0x00, 0x00, 0x01 }; if (!aud_inserted) { aud_inserted = true; payload->append((const char*)fresh_nalu_header, 4); } else { payload->append((const char*)cont_nalu_header, 3); }}
- 回到
SrsHlsCache::write_video
函数中,调用SrsTsCache::cache_video
将 sample 中的所有缓存的 nalu 都插入到SrsTsCache* cache->video->payload
中后,接着调用SrsHlsMuxer::is_segment_overflow
函数检测当前片的时长是否已经大于 hls_fragment 指定的时长(本配置为 10s),若已经满足,则表示已经可以切割该片了。否则调用SrsHlsMuxer::flush_video
函数。
3.2 SrsHlsMuxer::is_segment_overflow
/* * whether segment overflow. * that is whether the current segment duration>=(the segment in config) */bool SrsHlsMuxer::is_segment_overflow(){ srs_assert(current); /* 若当前片的时长小于最小片时长的2倍限制,即小于 2*100=200ms,则 * 表示当前片还不可以切割 */ /* to prevent very small segment. */ if (current->duration * 1000 < 2 * SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS) { return false; } /* use N% deviation, to smoother. */ /* 默认没有使能 hls_ts_floor,即 deviation 为 0.0 */ double deviation = hls_ts_floor ? SRS_HLS_FLOOR_REAP_PERCENT * deviation_ts * hls_fragment : 0.0; return current->duration >= hls_fragment + deviation;}
该函数是检测当前片的时长是否已经大于配置文件中 hls_fragment 指定的片的最小时长,若是,则返回 true,表明可以切割该片了;否则,返回 false。
假设当前片的时长还没满足大于等于 hls_fragment,则
SrsHlsCache::write_video
函数中接着调用SrsHlsMuxer::flush_video
函数。
3.3 SrsHlsMuxer::flush_video
int SrsHlsMuxer::flush_video(SrsTsCache* cache){ int ret = ERROR_SUCCESS; /* if current is NULL, segment is not open, ignore the flush event. */ if (!current) { srs_warn("flush video ignored, for segment is not open."); return ret; } /* 确保 cache 中有 video 数据,否则直接返回 */ if (!cache->video || cache->video->payload->length() <= 0) { return ret; } srs_assert(current); /* update the duration of segment. */ current->update_duration(cache->video->dts); if ((ret = current->muxer->write_video(cache->video)) != ERROR_SUCCESS) { return ret; } /* write success, clear and free the msg */ srs_freep(cache->video); return ret;}
- 该函数首先调用 SrsHlsSegment::update_duration 函数更新当前片的时长。
3.3.1 SrsHlsSegment::update_duration
/* * update the segment duration. * @param current_frame_dts, the dts of frame, in tbn of ts. */void SrsHlsSegment::update_duration(int64_t current_segment_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) { srs_warn("hls timestamp jump %"PRId64"=>%"PRId64, segment_start_dts, current_frame_dts); segment_start_dts = current_frame_dts; } return; } duration = (current_frame_dts - segment_start_dts) / 90000.0; srs_assert(duration >= 0); return;}
- 更新完当前片的时长后,回到
SrsHlsMuxer::flush_video
函数中,接着调用SrsTSMuxer::write_video
函数将 video frame 写入到 ts 中。
3.3.2 SrsTSMuxer::write_video
/* * write a video frame to ts */int SrsTSMuxer::write_video(SrsTsMessage* video){ int ret = ERROR_SUCCESS; /* 将 video frame 写入到 PES packet 中 */ if ((ret = context->encode(writer, video, vcodec, acodec)) != ERROR_SUCCESS) { srs_error("hls encode video failed. ret=%d", ret); return ret; } return ret;}
3.3.3 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; } /* 当首次调用该函数将 audio/video frame 写入到 PES packet 中时, * 需要将 vcodec 和 acodec 写入到 PAT/PMT table 中 */ /* 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; } } if (msg->is_audio()) { return encode_pes(writer, msg, audio_pid, as, vs == SrsTsStreamReserved); } else { return encode_pes(writer, msg, video_pid, vs, vs == SrsTsStreamReserved); }}
- 在该函数中,若为第一次调用该函数将 video/audio 数据写入到 PES packet 中,则需要调用
SrsTsContext::encode_pat_pmt
函数将 video 和 audio 的所用的编码格式 vcodec 和 acodec 以及流类型写入到 PAT/PMT 表中。
3.3.4 SrsTsContext::encode_pat_pmt
该函数的具体分析可见于:
回到
SrsTsContext::encode
函数中,首次将 PAT/PMT 写入到 ts 文件中后,接着检测到当前消息为视频,因此调用SrsTsContext::encode_pes
函数把当前的视频消息封装成 ts 格式,然后写入到 ts 文件中。
3.3.5 SrsTsContext::encode_pes
该函数的具体分析: