1. SrsHls::on_video

  1. /*
  2. * mux the video packets to ts.
  3. * @param shared_video, directly ptr, copy it if need to save it.
  4. * @param is_sps_pps, whether the video is h.264 sps/pps.
  5. */
  6. int SrsHls::on_video(SrsSharedPtrMessage* shared_video, bool is_sps_pps)
  7. {
  8. int ret = ERROR_SUCCESS;
  9. if (!hls_enabled) {
  10. return ret;
  11. }
  12. /* update the hls time, for hos_dispose. */
  13. last_update_time = srs_get_system_time_ms();
  14. SrsSharedPtrMessage* video = shared_video->copy();
  15. SrsAutoFree(SrsSharedPtrMessage, video);
  16. /* user can disable the sps parse to workaround when parse sps failed. */
  17. if (is_sps_pps) {
  18. /* 是否使能解析 sps,默认使能 */
  19. codec->avc_parse_sps = _srs_config->get_parse_sps(_req->vhost);
  20. }
  21. sample->clear();
  22. /* demux the video packet in h.264 codec.
  23. * the packet mux in FLV/RTMP format defined in flv specification.
  24. * demux the video specified data(frame_type, codec_id, ...) to sample.
  25. * demux the h.264 specified data(avc_profile, ...) to codec from sequence header.
  26. * demux the h.264 NALUs to sample units. */
  27. if ((ret = codec->video_avc_demux(video->payload, video->size, sample))
  28. != ERROR_SUCCESS) {
  29. srs_error("hls codec demux video failed. ret=%d", ret);
  30. return ret;
  31. }
  32. /* ignore info frame */
  33. if (sample->frame_type == SrsCodecVideoAVCFrameVideoInfoFrame) {
  34. return ret;
  35. }
  36. if (codec->video_codec_id != SrsCodecVideoAVC) {
  37. return ret;
  38. }
  39. /* ignore sequence header */
  40. if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame
  41. && sample->avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) {
  42. return hls_cache->on_sequence_header(muxer);
  43. }
  44. /* TODO: FIXME: config the jitter of HLS. */
  45. if ((ret = jitter->correct(video, SrsRtmpJitterAlgorithmOFF)) != ERROR_SUCCESS) {
  46. srs_error("rtmp jitter correct video failed. ret=%d", ret);
  47. return ret;
  48. }
  49. /* flv 的时间戳(单位 ms)将每一秒分为 90000 份,因此为 1/90 ms */
  50. int64_t dts = video->timestamp * 90;
  51. stream_dts = dts;
  52. /* write video to muxer */
  53. if ((ret = hls_cache->write_video(codec, muxer, dts, sample)) != ERROR_SUCCESS) {
  54. srs_error("hls cache write video failed. ret=%d", ret);
  55. return ret;
  56. }
  57. /* pithy print message. */
  58. hls_show_mux_log();
  59. return ret;
  60. }

2. SrsAvcAacCodec::video_avc_demux

接收到一个视频消息,首先调用该函数解析该视频数据。

  1. int SrsAvcAacCodec::video_avc_demux(char* data, int size, SrsCodecSample* sample)
  2. {
  3. int ret = ERROR_SUCCESS;
  4. sample->is_video = true;
  5. if (!data || size <= 0) {
  6. srs_trace("no video present, ignore it.");
  7. return ret;
  8. }
  9. if ((ret = stream->initialize(data, size)) != ERROR_SUCCESS) {
  10. return ret;
  11. }
  12. /* video decode */
  13. if (!stream->require(1)) {
  14. ret = ERROR_HLS_DECODE_ERROR;
  15. srs_error("avc decode frame_type failed. ret=%d", ret);
  16. return ret;
  17. }
  18. /* Video Tag 数据区的第一个字节是视频信息 */
  19. /* E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 */
  20. int8_t frame_type = stream->read_1bytes();
  21. /* 编码ID:4bits */
  22. int8_t codec_id = frame_type & 0x0f;
  23. /* 帧类型:4bits */
  24. frame_type = (frame_type >> 4) & 0x0f;
  25. sample->frame_type = (SrsCodecVideoAVCFrame)frame_type;
  26. /* ignore info frame without error
  27. * @see https://github.com/ossrs/srs/issues/288#issuecomment-69863909 */
  28. if (sample->frame_type == SrsCodecVideoAVCFrameVideoInfoFrame) {
  29. srs_warn("avc igone the info frame, ret=%d", ret);
  30. return ret;
  31. }
  32. /* only support h.264/avc */
  33. if (codec_id != SrsCodecVideoAVC) {
  34. ret = ERROR_HLS_DECODE_ERROR;
  35. srs_error("avc only support video h.264/avc codec. actual=%d, ret=%d",
  36. codec_id, ret);
  37. return ret;
  38. }
  39. video_codec_id = codec_id;
  40. if (!stream->require(4)) {
  41. ret = ERROR_HLS_DECODE_ERROR;
  42. srs_error("avc decode avc_packet_type failed. ret=%d", ret);
  43. return ret;
  44. }
  45. /* AVC Packet 类型:1byte, 0: AVC序列头, 1: AVC NALU 单元 */
  46. int8_t avc_packet_type = stream->read_1bytes();
  47. /* CTS: 3bytes,如果 AVC packet 类型为 1,则为 cts 偏移,其他情况则为 0
  48. * cts = (pts - dts) / 90,单位毫秒 */
  49. int32_t composition_time = stream->read_3bytes();
  50. /* pts = dts + cts. */
  51. sample->cts = composition_time;
  52. sample->avc_packet_type = (SrsCodecVideoAVCType)avc_packet_type;
  53. if (avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) {
  54. /* 解析 sps,pps 数据 */
  55. if ((ret = avc_demux_sps_pps(stream)) != ERROR_SUCCESS) {
  56. return ret;
  57. }
  58. } else if (avc_packet_type == SrsCodecVideoAVCTypeNALU){
  59. /* 检测 H264 的封装格式为 AnnexB 还是 "ISO Base Media File Format",
  60. * 然后根据相应封装格式的特点提取出 NALU,将该 NALU 保存到 sample 的
  61. * sample_units 数组中 */
  62. if ((ret = video_nalu_demux(stream, sample)) != ERROR_SUCCESS) {
  63. return ret;
  64. }
  65. } else {
  66. /* ignored. */
  67. }
  68. return ret;
  69. }

接下来,若检测接收到的 video packet type 为 1,即为 NAL 单元,则调用 video_nalu_demux 进行解封装。

2.1 SrsAvcAacCodec::video_nalu_demux

  1. int SrsAvcAacCodec::video_nalu_demux(SrsStream* stream, SrsCodecSample* sample)
  2. {
  3. int ret = ERROR_SUCCESS;
  4. /* ensure the sequence header demuxed */
  5. if (!is_avc_codec_ok()) {
  6. srs_warn("avc ignore type=%d for no sequence header. ret=%d",
  7. SrsCodecVideoAVCTypeNALU, ret);
  8. return ret;
  9. }
  10. /* 在 SrsAvcAacCodec 构造函数中初始化该变量为 SrsAvcPayloadFormatGuess */
  11. /* guess for the first time. */
  12. if (payload_format == SrsAvcPayloadFormatGuess) {
  13. /* One or more NALUs (Full frames are required)
  14. * try "AnnexB" from H.264-AVC-ISO_IEC_14496-10.pdf, page 211. */
  15. if ((ret = avc_demux_annexb_format(stream, sample)) != ERROR_SUCCESS) {
  16. /* stop try when system error. */
  17. if (ret != ERROR_HLS_AVC_TRY_OTHERS) {
  18. srs_error("avc demux for annexb failed. ret=%d", ret);
  19. return ret;
  20. }
  21. /* try "ISO Base Media File Format" from
  22. * H.264-AVC-ISO_IEC_14496-15.pdf, page 20" */
  23. if ((ret = avc_demux_ibmf_format(stream, sample)) != ERROR_SUCCESS) {
  24. return ret;
  25. } else {
  26. payload_format = SrsAvcPayloadFormatIbmf;
  27. srs_info("hls guess avc payload is ibmf format.");
  28. }
  29. } else {
  30. payload_format = SrsAvcPayloadFormatAnnexb;
  31. srs_info("hls guess avc payload is annexb format.");
  32. }
  33. } else if (payload_format == SrsAvcPayloadFormatIbmf) {
  34. /* try "ISO Base Media File Format" from H.264-AVC-ISO_IEC_14496-15.pdf, page 20 */
  35. if ((ret = avc_demux_ibmf_format(stream, sample)) != ERROR_SUCCESS) {
  36. return ret;
  37. }
  38. srs_info("hls decode avc payload in ibmf format.");
  39. } else {
  40. /* One or more NALUs (Full frames are required)
  41. * try "AnnexB" from H.264-AVC-ISO_IEC_14496-10.pdf, page 211. */
  42. if ((ret = avc_demux_annexb_format(stream, sample)) != ERROR_SUCCESS) {
  43. /* ok, we guess out the payload is annexb, but maybe changed to ibmf. */
  44. if (ret != ERROR_HLS_AVC_TRY_OTHERS) {
  45. srs_error("avc demux for annexb failed. ret=%d", ret);
  46. return ret;
  47. }
  48. /* try "ISO Base Media File Format" from
  49. * H.264-AVC-ISO_IEC_14496-15.pdf, page 20 */
  50. if ((ret = avc_demux_ibmf_format(stream, sample)) != ERROR_SUCCESS) {
  51. return ret;
  52. } else {
  53. payload_format = SrsAvcPayloadFormatIbmf;
  54. srs_warn("hls avc payload change from annexb to ibmf format.");
  55. }
  56. }
  57. srs_info("hls decode avc payload in annexb format.");
  58. }
  59. return ret;
  60. }

下面先尝试为当前的 H264 封装为 Annexb 格式,因此调用 SrsAvcAacCodec::avc_demux_annexb_format 函数进行解析。

2.2 SrsAvcAacCodec::avc_demux_annexb_format

  1. int SrsAvcAacCodec::avc_demux_annexb_format(SrsStream* stream, SrsCodecSample* sample)
  2. {
  3. int ret = ERROR_SUCCESS;
  4. /* not annexb, try others */
  5. if (!srs_avc_startswith_annexb(stream, NULL)) {
  6. return ERROR_HLS_AVC_TRY_OTHERS;
  7. }
  8. /* AnnexB
  9. * B.1.1 Byte stream NAL unit syntax,
  10. * H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
  11. */
  12. while (!stream->empty()) {
  13. /* find start code */
  14. int nb_start_code = 0;
  15. if (!srs_avc_startswith_annexb(stream, &nb_start_code)) {
  16. return ret;
  17. }
  18. /* skip the start code. */
  19. if (nb_start_code > 0) {
  20. stream->skip(nb_start_code);
  21. }
  22. /* the NALU start bytes. */
  23. char* p = stream->data() + stream->pos();
  24. /* get the last matched NALU */
  25. while (!stream->empty()) {
  26. if (srs_avc_startswith_annexb(stream, NULL)) {
  27. break;
  28. }
  29. stream->skip(1);
  30. }
  31. /* 此时 pp 指向下一个 NALU start bytes */
  32. char* pp = stream->data() + stream->pos();
  33. /* skip the empty. */
  34. if (pp - p <= 0) {
  35. continue;
  36. }
  37. /* 获取到一个 NALU 后,将该 NALU 添加到 sample 中的 sample_units 数组中 */
  38. /* got the NALU. */
  39. if ((ret = sample->add_sample_unit(p, pp - p)) != ERROR_SUCCESS) {
  40. srs_error("annexb add video sample failed. ret=%d", ret);
  41. return ret;
  42. }
  43. }
  44. return ret;
  45. }

2.2.1 srs_avc_startswith_annexb

  1. /*
  2. * whether stream starts with the avc NALU in "AnnexB"
  3. * from H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
  4. * start code must be "N[00] 00 00 01" where N>=0
  5. * @param pnb_start_code, output the size of start code, must >=3.
  6. * NULL to ignore.
  7. */
  8. bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code)
  9. {
  10. char* bytes = stream->data() + stream->pos();
  11. char* p = bytes;
  12. for ( ;; ) {
  13. if (!stream->require(p - bytes + 3)) {
  14. return false;
  15. }
  16. /* not match */
  17. if (p[0] != (char)0x00 || p[1] != (char)0x00) {
  18. return false;
  19. }
  20. /* match N[00] 00 00 01, where N>=0 */
  21. if (p[2] == (char)0x01) {
  22. if (pnb_start_code) {
  23. *pnb_start_code = (int)(p - bytes) + 3;
  24. }
  25. return true;
  26. }
  27. p++;
  28. }
  29. return false;
  30. }

由该代码可知,若 H264 为 Annexb 封装格式,则 NALU 之间是以 0x000001(3bytes) 或者 0x00000001(4bytes) 分割。

2.2.2 SrsCodecSample::add_sample_unit

  1. /*
  2. * add the a sample unit, it's a h.264 NALU or aac raw data.
  3. * the sample unit directly use the ptr of packet bytes,
  4. * so user must never use sample unit when packet is destroyed.
  5. * in a word, user must clear sample before demux it.
  6. */
  7. int SrsCodecSample::add_sample_unit(char* bytes, int size)
  8. {
  9. int ret = ERROR_SUCCESS;
  10. /* sample_units 数组的最大值为 128 */
  11. if (nb_sample_units >= SRS_SRS_MAX_CODEC_SAMPLE) {
  12. ret = ERROR_HLS_DECODE_ERROR;
  13. srs_error("hls decode samples error, "
  14. "exceed the max count: %d, ret=%d", SRS_SRS_MAX_CODEC_SAMPLE, ret);
  15. return ret;
  16. }
  17. /* 从 sample_uints 数组中取出一个 sample_unit,用于存放获取到的 NALU 或 aac raw data */
  18. SrsCodecSampleUnit* sample_unit = &sample_units[nb_sample_units++];
  19. sample_unit->bytes = bytes;
  20. /* 该 NALU 单元的大小 或 aac raw data 的大小 */
  21. sample_unit->size = size;
  22. // for video, parse the nalu type, set the IDR flag.
  23. if (is_video) {
  24. SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(bytes[0] & 0x1f);
  25. if (nal_unit_type == SrsAvcNaluTypeIDR) {
  26. /* 若当前 NALU 为 I 帧,则置位该标志位,表示当前 sample_units 数组中含有 I 帧 */
  27. has_idr = true;
  28. } else if (nal_unit_type == SrsAvcNaluTypeSPS ||
  29. nal_unit_type == SrsAvcNaluTypePPS) {
  30. /* Whether exists SPS/PPS NALU. */
  31. has_sps_pps = true;
  32. } else if (nal_unit_type == SrsAvcNaluTypeAccessUnitDelimiter) {
  33. /* Whether exists AUD NALU. */
  34. has_aud = true;
  35. }
  36. if (first_nalu_type == SrsAvcNaluTypeReserved) {
  37. /* 记录 sample_units 数组中第一个 NALU 的类型 */
  38. first_nalu_type = nal_unit_type;
  39. }
  40. }
  41. return ret;
  42. }
  • 若上面尝试调用 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

  1. /*
  2. * demux the avc NALU in "ISO Base Media File Format"
  3. * from H.264-AVC-ISO_IEC_14496-15.pdf, page 20
  4. */
  5. int SrsAvcAacCodec::avc_demux_ibmf_format(SrsStream* stream, SrsCodecSample* sample)
  6. {
  7. int ret = ERROR_SUCCESS;
  8. int PictureLength = stream->size() - stream->pos();
  9. /*
  10. * 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
  11. * 5.2.4.1 AVC decoder configuration record
  12. * 5.2.4.1.2 Semantics
  13. * The value of this field shall be one of 0, 1, or 3 corresponding to a
  14. * length encoded with 1, 2, or 4 bytes, respectively.
  15. */
  16. srs_assert(NAL_unit_length != 2);
  17. /*
  18. * 该 NAL_unit_length 的值即为解析 sps 的获取到的 lengthSizeMinusOne 字段值
  19. */
  20. /* 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 20 */
  21. for (int i = 0; i < PictureLength; ) {
  22. /* unsigned int((NAL_unit_length+1)*8) NALUnitLength; */
  23. if (!stream->require(NAL_unit_length + 1)) {
  24. ret = ERROR_HLS_DECODE_ERROR;
  25. srs_error("avc decode NALU size failed. ret=%d", ret);
  26. return ret;
  27. }
  28. int32_t NALUnitLength = 0;
  29. if (NAL_unit_length == 3) {
  30. NALUnitLength = stream->read_4bytes();
  31. } else if (NAL_unit_length == 1) {
  32. NALUnitLength = stream->read_2bytes();
  33. } else {
  34. NALUnitLength = stream->read_1bytes();
  35. }
  36. /* maybe stream is invalid format.
  37. * see: https://github.com/ossrs/srs/issues/183 */
  38. if (NALUnitLength < 0) {
  39. ret = ERROR_HLS_DECODE_ERROR;
  40. srs_error("maybe stream is AnnexB format. ret=%d", ret);
  41. return ret;
  42. }
  43. /* NALUnit */
  44. if (!stream->require(NALUnitLength)) {
  45. ret = ERROR_HLS_DECODE_ERROR;
  46. srs_error("avc decode NALU data failed. ret=%d", ret);
  47. return ret;
  48. }
  49. /* 7.3.1 NAL unit syntax, H.264-AVC-ISO_IEC_14496-10.pdf, page 44. */
  50. if ((ret = sample->add_sample_unit(stream->data() + stream->pos(), NALUnitLength))
  51. != ERROR_SUCCESS) {
  52. srs_error("avc add video sample failed. ret=%d", ret);
  53. return ret;
  54. }
  55. stream->skip(NALUnitLength);
  56. i += NAL_unit_length + 1 + NALUnitLength;
  57. }
  58. return ret;
  59. }
  • 由该函数源码可知,若 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

  1. /*
  2. * write video to muxer.
  3. */
  4. int SrsHlsCache::write_video(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer,
  5. int64_t dts, SrsCodecSample* sample)
  6. {
  7. int ret = ERROR_SUCCESS;
  8. /* write video to cache. */
  9. if ((ret = cache->cache_video(codec, dts, sample)) != ERROR_SUCCESS) {
  10. return ret;
  11. }
  12. /* when segment overflow, reap if possible */
  13. if (muxer->is_segment_overflow()) {
  14. /* do reap ts if any of:
  15. * a. wait keyframe and got keyframe.
  16. * b. always reap when not wait keyframe */
  17. if (!muxer->wait_keyframe() ||
  18. sample->frame_type == SrsCodecVideoAVCFrameKeyFrame) {
  19. /* reap the segmtn, which will also flush the video. */
  20. if ((ret = reap->segment("video", muxer, cache->video->dts))
  21. != ERROR_SUCCESS) {
  22. return ret;
  23. }
  24. }
  25. }
  26. /* flush video when got one */
  27. if ((ret = muxer->flush_video(cache)) != ERROR_SUCCESS) {
  28. srs_error("m3u8 muxer flush video failed. ret=%d", ret);
  29. return ret;
  30. }
  31. return ret;
  32. }
  • SrsHlsCache::write_video 函数中,首先调用 SrsTsCache::cache_video 函数将已经保存在 sample 中成员 sample_units 数组中的 NALU 保存到 cache 中。

3.1 SrsTsCache::cache_video

  1. int SrsTsCache::cache_video(SrsAvcAacCodec* codec, int64_t dts,
  2. SrsCodecSample* sample)
  3. {
  4. int ret = ERROR_SUCCESS;
  5. /* create the ts video message. */
  6. if (!video) {
  7. video = new SrsTsMessage();
  8. /*
  9. * write_pcr:
  10. * whether thisi message with pcr info,
  11. * generally, the video IDR(I frame, the keyframe of h.264) carray the pcr info.
  12. */
  13. /* 若当前帧类型为 I帧,表明携带有 pcr 信息 */
  14. video->write_pcr = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame;
  15. /*
  16. * start_pts:
  17. * the audio cache buffer start pts, to flush audio if full.
  18. * @remark, the pts is not the adjust one, it's the original pts.
  19. */
  20. video->start_pts = dts;
  21. }
  22. /* 对于 video,flv/rtmp 的时间戳即为 video 的 dts */
  23. video->dts = dts;
  24. /* pts = dts + cts */
  25. video->pts = video->dts + sample->cts * 90; // in ms
  26. /* stream id, 视频取值为 (0xe0~0xef),通常为 0xe0,这里即 SrsTsPESStreamIdVideoCommon */
  27. video->sid = SrsTsPESStreamIdVideoCommon;
  28. /* write video to cache. */
  29. if ((ret = do_cache_avc(codec, sample)) != ERROR_SUCCESS) {
  30. return ret;
  31. }
  32. return ret;
  33. }

若当前 SrsTsCache 之前从未缓存过视频数据,即 video 为 NULL,则构建一个新的 SrsTsMessage 类的对象,video 指向该对象。

3.1.1 SrsTsMessage 构造

  1. /*
  2. * the media audio/video message parsed from PES packet.
  3. */
  4. SrsTsMessage::SrsTsMessage(SrsTsChannel* c, SrsTsPacket* p)
  5. {
  6. /*
  7. * channel and packet:
  8. * decoder only,
  9. * the ts message does not use them,
  10. * for user to get the channel and packet.
  11. */
  12. channel = c;
  13. packet = p;
  14. /*
  15. * dts and pts:
  16. * the timestamp in 90khz
  17. */
  18. dts = pts = 0;
  19. /*
  20. * sid:
  21. * the id of pes stream to indicates the payload codec.
  22. * @remark use is_audio() and is_video() to check it,
  23. * and stream_number() to finger it out.
  24. */
  25. sid = (SrsTsPESStreamId)0x00;
  26. /*
  27. * continuity_counter: the chunk id.
  28. */
  29. continuity_counter = 0;
  30. /*
  31. * PES_packet_length: the size of payload, 0 indicates the length() of payload.
  32. */
  33. PES_packet_length = 0;
  34. /*
  35. * payload: the payload bytes.
  36. */
  37. payload = new SrsSimpleBuffer();
  38. /*
  39. * is_discontinuity: whether got discontinuity ts, for example,
  40. * sequence header changed.
  41. */
  42. is_discontinuity = false;
  43. /*
  44. * start_pts: the audio cache buffer start pts, to flush audio if full.
  45. * @remark the pts is not the adjust one, it's the orignal pts.
  46. */
  47. start_pts = 0;
  48. /*
  49. * write_pcr:
  50. * whether this message with pcr info,
  51. * generally, the video IDR(I frame, the keyframe of h.264) carray the pcr info.
  52. */
  53. write_pcr = false;
  54. }
  • SrsTsCache::cache_video 函数中,构建 SrsTsMessage 并初始化时间戳等信息后,接着调用 SrsTsCache::do_cache_avc 函数将 video 数据写入到 SrsTsCache.video->payload 中.

3.1.2 SrsTsCache::do_cache_avc

  1. int SrsTsCache::do_cache_avc(SrsAvcAacCodec* codec, SrsCodecSample* sample)
  2. {
  3. int ret = ERROR_SUCCESS;
  4. /* whether aud inserted. */
  5. bool aud_inserted = false;
  6. /* Insert a default AUD NALU when no AUD in samples. */
  7. if (!sample->has_aud) {
  8. /*
  9. * the aud(access unit delimiter) before each frame.
  10. * 7.3.2.4 Access unit delimiter RBSP syntax
  11. * H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 66.
  12. *
  13. * primary_pis_type u(3), the first 3bits, primary_pic_type indicates
  14. * that the slice_type values for all slices of the primary coded
  15. * picture are numbers of the set listed in Table 7-5 for the given
  16. * value of primary_pic_type.
  17. * 0, slice_type 2, 7
  18. * 1, slice_type 0, 2, 5, 7
  19. * 2, slice_type 0, 1, 2, 5, 6, 7
  20. * 3, slice_type 4, 9
  21. * 4, slice_type 3, 4, 8, 9
  22. * 5, slice_type 2, 4, 7, 9
  23. * 6, slice_type 0, 2, 3, 4, 5, 7, 8, 9
  24. * 7, slice_type 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
  25. * 7.4.2.4 Access unit delimiter RBSP semantics
  26. * H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 102.
  27. *
  28. * slice_type specifies the coding type of the slice according to Table 7-6.
  29. * 0, P (P slice)
  30. * 1, B (B slice)
  31. * 2, I (I slice)
  32. * 3, SP (SP slice)
  33. * 4, SI (SI slice)
  34. * 5, P (P slice)
  35. * 6, B (B slice)
  36. * 7, I (I slice)
  37. * 8, SP (SP slice)
  38. * 9, SI (SI slice)
  39. * H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 105.
  40. */
  41. static u_int8_t default_aud_nalu[] = { 0x09, 0xf0 };
  42. /* 这里封装 H264 使用的是 Annexb 格式,在 aud 前会插入
  43. * 4 字节的分隔符: 0x00000001 */
  44. srs_avc_insert_aud(video->payload, aud_inserted);
  45. video->payload->append((const char*)default_aud_nalu, 2);
  46. }
  47. /* 除了 AUD 前插入的是 4 字节的分隔符 0x00000001 外,其他的
  48. * NALU 前插入的都为 3 字节的分隔符: 0x000001 */
  49. bool is_sps_pps_appended = false;
  50. /* all sample use cont nalu header, except the sps-pps before IDR frame. */
  51. for (int i = 0; i < sample->nb_sample_units, i++) {
  52. SrsCodecSampleUnit* sample_unit = &sample->sample_units[i];
  53. int32_t size = sample_unit->size;
  54. if (!sample_unit->bytes || size <= 0) {
  55. ret = ERROR_HLS_AVC_SAMPLE_SIZE;
  56. srs_error("invalid avc sample length=%d, ret=%d", size, ret);
  57. return ret;
  58. }
  59. /*
  60. * 5bits, 7.3.1 NAL unit syntax,
  61. * H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 83.
  62. */
  63. SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(sample_unit->bytes[0] & 0x1f);
  64. /* 在 IDR 帧前先插入 sps 和 pps */
  65. /*
  66. * Insert sps/pps before IDR when there is no sps/pps in samples.
  67. * The sps/pps is parsed from sequence header(generally the first flv packet).
  68. */
  69. if (nal_unit_type == SrsAvcNaluTypeIDR &&
  70. !sample->has_sps_pps && !is_sps_pps_appended) {
  71. if (codec->sequenceParameterSetLength > 0) {
  72. /* 插入 3 字节的分隔符: 0x000001 */
  73. srs_avc_insert_aud(video->payload, aud_inserted);
  74. /* 接着插入 sps */
  75. video->payload->append(codec->sequenceParameterSetNALUnit,
  76. codec->sequenceParameterSetLength);
  77. }
  78. if (codec->pictureParameterSetLength > 0) {
  79. srs_avc_insert_aud(video->payload, aud_inserted);
  80. video->payload->append(codec->pictureParameterSetNALUnit,
  81. codec->pictureParameterSetLength);
  82. }
  83. is_sps_pps_appended = true;
  84. }
  85. /* Insert the NALU to video in annexb. */
  86. srs_avc_insert_aud(video->payload, aud_inserted);
  87. video->payload->append(sample->unit->bytes, sample->unit->size);
  88. }
  89. return ret;
  90. }

若当前为接收到 sps 和 pps 后的第二个 video 消息,则根据上面源码可知,在 video->payload 中各 nalu 之间的格式为:

  1. annexb 4B header, 2B aud(nal_unit_type:9)(0x09 0xf0)(AUD)
  2. annexb 3B header, 19B sps(nal_unit_type:7)(SPS)
  3. annexb 3B header, 4B pps(nal_unit_type:8)(PPS)
  4. annexb 3B header, 12B nalu(nal_unit_type:6)(SEI)
  5. 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

  1. void srs_avc_insert_aud(SrsSimpleBuffer* payload, bool& aud_inserted)
  2. {
  3. /*
  4. * mux the samples in annexb format,
  5. * H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 324. */
  6. /**
  7. * 00 00 00 01 // header
  8. * xxxxxxx // data bytes
  9. * 00 00 01 // continue header
  10. * xxxxxx // data bytes
  11. *
  12. * nal_unit_type specifies the type of RBSP data structure contained in the NAL
  13. * unit as specified in Table 7-1.
  14. * Table 7-1 - NAL unit type codec, syntax element categories, and NAL unit
  15. * type classes H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 83.
  16. * 1, Coded slice of a non-IDR picture slice_layer_without_partitioning_rbsp( )
  17. * 2, Coded slice data partition A slice_data_partition_a_layer_rbsp( )
  18. * 3, Coded slice data partition B slice_data_partition_b_layer_rbsp( )
  19. * 4, Coded slice data partition C slice_data_partition_c_layer_rbsp( )
  20. * 5, Coded slice of an IDR picture slice_layer_without_partitioning_rbsp( )
  21. * 6, Supplemental enhancement information (SEI) sei_rbsp( )
  22. * 7, Sequence parameter set seq_parameter_set_rbsp( )
  23. * 8, Picture parameter set pic_parameter_set_rbsp( )
  24. * 9, Access unit delimiter access_unit_delimiter_rbsp( )
  25. * 10, End of sequence end_of_seq_rbsp( )
  26. * 11, End of stream end_of_stream_rbsp( )
  27. * 12, Filler data filler_data_rbsp( )
  28. * 13, Sequence parameter set extension seq_parameter_set_extension_rbsp( )
  29. * 14, Prefix NAL unit prefix_nal_unit_rbsp( )
  30. * 15, Subset sequence parameter set subset_seq_parameter_set_rbsp( )
  31. * 19, Coded slice of an auxiliary coded picture without partitioning
  32. * slice_layer_without_partitioning_rbsp( )
  33. * 20, Coded slice extension slice_layer_extension_rbsp( )
  34. * the first ts message of apple sample:
  35. * annexb 4B header, 2B aud(nal_unit_type:9)(0x09 0xf0)(AUD)
  36. * annexb 3B header, 19B sps(nal_unit_type:7)(SPS)
  37. * annexb 3B header, 4B pps(nal_unit_type:8)(PPS)
  38. * annexb 3B header, 12B nalu(nal_unit_type:6)(SEI)
  39. * annexb 3B header, 21B nalu(nal_unit_type:6)(SEI)
  40. * annexb 3B header, 2762B nalu(nal_unit_type:5)(IDR)
  41. * annexb 3B header, 3535B nalu(nal_unit_type:5)(IDR)
  42. * the second ts message of apple ts sample:
  43. * annexb 4B header, 2B aud(nal_unit_type:9)(0x09 0xf0)(AUD)
  44. * annexb 3B header, 21B nalu(nal_unit_type:6)(SEI)
  45. * annexb 3B header, 379B nalu(nal_unit_type:1)(non-IDR,P/B)
  46. * annexb 3B header, 406B nalu(nal_unit_type:1)(non-IDR,P/B)
  47. * @remark we use the sequence of apple
  48. * samples http://ossrs.net/apple-sample/bipbopall.m3u8
  49. */
  50. static u_int8_t fresh_nalu_header[] = { 0x00, 0x00, 0x00, 0x01 };
  51. static u_int8_t cont_nalu_header[] = { 0x00, 0x00, 0x01 };
  52. if (!aud_inserted) {
  53. aud_inserted = true;
  54. payload->append((const char*)fresh_nalu_header, 4);
  55. } else {
  56. payload->append((const char*)cont_nalu_header, 3);
  57. }
  58. }
  • 回到 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

  1. /*
  2. * whether segment overflow.
  3. * that is whether the current segment duration>=(the segment in config)
  4. */
  5. bool SrsHlsMuxer::is_segment_overflow()
  6. {
  7. srs_assert(current);
  8. /* 若当前片的时长小于最小片时长的2倍限制,即小于 2*100=200ms,则
  9. * 表示当前片还不可以切割 */
  10. /* to prevent very small segment. */
  11. if (current->duration * 1000 < 2 * SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS) {
  12. return false;
  13. }
  14. /* use N% deviation, to smoother. */
  15. /* 默认没有使能 hls_ts_floor,即 deviation 为 0.0 */
  16. double deviation = hls_ts_floor ?
  17. SRS_HLS_FLOOR_REAP_PERCENT * deviation_ts * hls_fragment : 0.0;
  18. return current->duration >= hls_fragment + deviation;
  19. }
  • 该函数是检测当前片的时长是否已经大于配置文件中 hls_fragment 指定的片的最小时长,若是,则返回 true,表明可以切割该片了;否则,返回 false。

  • 假设当前片的时长还没满足大于等于 hls_fragment,则 SrsHlsCache::write_video 函数中接着调用 SrsHlsMuxer::flush_video 函数。

3.3 SrsHlsMuxer::flush_video

  1. int SrsHlsMuxer::flush_video(SrsTsCache* cache)
  2. {
  3. int ret = ERROR_SUCCESS;
  4. /* if current is NULL, segment is not open, ignore the flush event. */
  5. if (!current) {
  6. srs_warn("flush video ignored, for segment is not open.");
  7. return ret;
  8. }
  9. /* 确保 cache 中有 video 数据,否则直接返回 */
  10. if (!cache->video || cache->video->payload->length() <= 0) {
  11. return ret;
  12. }
  13. srs_assert(current);
  14. /* update the duration of segment. */
  15. current->update_duration(cache->video->dts);
  16. if ((ret = current->muxer->write_video(cache->video))
  17. != ERROR_SUCCESS) {
  18. return ret;
  19. }
  20. /* write success, clear and free the msg */
  21. srs_freep(cache->video);
  22. return ret;
  23. }
  • 该函数首先调用 SrsHlsSegment::update_duration 函数更新当前片的时长。

3.3.1 SrsHlsSegment::update_duration

  1. /*
  2. * update the segment duration.
  3. * @param current_frame_dts, the dts of frame, in tbn of ts.
  4. */
  5. void SrsHlsSegment::update_duration(int64_t current_segment_dts)
  6. {
  7. /*
  8. * we use video/audio to update segment duration,
  9. * so when reap segment, some previous audio frame will
  10. * update the segment duration, which is nagetive,
  11. * just ignore it.
  12. */
  13. if (current_frame_dts < segment_start_dts) {
  14. /* for atc and timestamp jump, reset the start dts. */
  15. if (current_frame_dts < segment_start_dts -
  16. SRS_AUTO_HLS_SEGMENT_TIMESTAMP_JUMP_MS * 90) {
  17. srs_warn("hls timestamp jump %"PRId64"=>%"PRId64,
  18. segment_start_dts, current_frame_dts);
  19. segment_start_dts = current_frame_dts;
  20. }
  21. return;
  22. }
  23. duration = (current_frame_dts - segment_start_dts) / 90000.0;
  24. srs_assert(duration >= 0);
  25. return;
  26. }
  • 更新完当前片的时长后,回到 SrsHlsMuxer::flush_video 函数中,接着调用 SrsTSMuxer::write_video 函数将 video frame 写入到 ts 中。

3.3.2 SrsTSMuxer::write_video

  1. /*
  2. * write a video frame to ts
  3. */
  4. int SrsTSMuxer::write_video(SrsTsMessage* video)
  5. {
  6. int ret = ERROR_SUCCESS;
  7. /* 将 video frame 写入到 PES packet 中 */
  8. if ((ret = context->encode(writer, video, vcodec, acodec))
  9. != ERROR_SUCCESS) {
  10. srs_error("hls encode video failed. ret=%d", ret);
  11. return ret;
  12. }
  13. return ret;
  14. }

3.3.3 SrsTsContext::encode

  1. /*
  2. * write the PES packet, the video/audio stream.
  3. * @param msg, the video/audio msg to write to ts.
  4. * @param vc, the video codec, write the PAT/PMT table when changed.
  5. * @param ac, the audio codec, write the PAT/PMT table when changed.
  6. */
  7. int SrsTsContext::encode(SrsFileWriter* writer, SrsTsMessage* msg,
  8. SrsCodecVideo vc, SrsCodecAudio ac)
  9. {
  10. int ret = ERROR_SUCCESS;
  11. SrsTsStream vs, as;
  12. int16_t video_pid = 0, audio_pid = 0;
  13. switch (vc) {
  14. case SrsCodecVideoAVC:
  15. vs = SrsTsStreamVideoH264;
  16. video_pid = TS_VIDEO_AVC_PID;
  17. break;
  18. case SrsCodecVideoDisabled:
  19. vs = SrsTsStreamReserved;
  20. break;
  21. case SrsCodecVideoReserved:
  22. case SrsCodecVideoReserved1:
  23. case SrsCodecVideoReserved2:
  24. case SrsCodecVideoSorensonH263:
  25. case SrsCodecVideoScreenVideo:
  26. case SrsCodecVideoOn2VP6:
  27. case SrsCodecVideoOn2VP6WithAlphaChannel:
  28. case SrsCodecVideoScreenVideoVersion2:
  29. vs = SrsTsStreamReserved;
  30. break;
  31. }
  32. switch (ac) {
  33. case SrsCodecAudioAAC:
  34. as = SrsTsStreamAudioAAC;
  35. audio_pid = TS_AUDIO_AAC_PID;
  36. break;
  37. case SrsCodecAudioMP3:
  38. as = SrsTsStreamAudioMp3;
  39. audio_pid = TS_AUDIO_MP3_PID;
  40. break;
  41. case SrsCodecAudioDisabled:
  42. as = SrsTsStreamReserved;
  43. break;
  44. case SrsCodecAudioReserved1:
  45. case SrsCodecAudioLinearPCMPlatformEndian:
  46. case SrsCodecAudioADPCM:
  47. case SrsCodecAudioLinearPCMLittleEndian:
  48. case SrsCodecAudioNellymoser16kHzMono:
  49. case SrsCodecAudioNellymoser8kHzMono:
  50. case SrsCodecAudioNellymoser:
  51. case SrsCodecAudioReservedG711AlawLogarithmicPCM:
  52. case SrsCodecAudioReservedG711MuLawLogarithmicPCM:
  53. case SrsCodecAudioReserved:
  54. case SrsCodecAudioSpeex:
  55. case SrsCodecAudioReservedMP3_8kHz:
  56. case SrsCodecAudioReservedDeviceSpecificSound:
  57. as = SrsTsStreamReserved;
  58. break;
  59. }
  60. if (as == SrsTsStreamReserved && vs == SrsTsStreamReserved) {
  61. ret = ERROR_HLS_NO_STREAM;
  62. srs_error("hls: no video or audio stream, vcodec=%d, acodec=%d. ret=%d",
  63. vc, ac, ret);
  64. return ret;
  65. }
  66. /* 当首次调用该函数将 audio/video frame 写入到 PES packet 中时,
  67. * 需要将 vcodec 和 acodec 写入到 PAT/PMT table 中 */
  68. /* when any codec changed, write PAT/PMT table. */
  69. if (vcodec != vc || acodec != ac) {
  70. vcodec = vc;
  71. acodec = ac;
  72. if ((ret = encode_pat_pmt(writer, video_pid, vs, audio_pid, as))
  73. != ERROR_SUCCESS) {
  74. return ret;
  75. }
  76. }
  77. if (msg->is_audio()) {
  78. return encode_pes(writer, msg, audio_pid, as, vs == SrsTsStreamReserved);
  79. } else {
  80. return encode_pes(writer, msg, video_pid, vs, vs == SrsTsStreamReserved);
  81. }
  82. }
  • 在该函数中,若为第一次调用该函数将 video/audio 数据写入到 PES packet 中,则需要调用 SrsTsContext::encode_pat_pmt 函数将 video 和 audio 的所用的编码格式 vcodec 和 acodec 以及流类型写入到 PAT/PMT 表中。

3.3.4 SrsTsContext::encode_pat_pmt

  • 该函数的具体分析可见于: SRS之TS封装PAT和PMT

  • 回到 SrsTsContext::encode 函数中,首次将 PAT/PMT 写入到 ts 文件中后,接着检测到当前消息为视频,因此调用 SrsTsContext::encode_pes 函数把当前的视频消息封装成 ts 格式,然后写入到 ts 文件中。

3.3.5 SrsTsContext::encode_pes

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

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

  1. SRS之SrsHls::on_audio详解

    1. SrsHls::on_audio 将音频数据封装到 ts 文件中. /* * mux the audio packet to ts. * @param shared_audio, directl ...

  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. C++ STL 之 内建函数对象

    STL 内建了一些函数对象.分为:算数类函数对象,关系运算类函数对象,逻辑运算类仿函数.这些仿函数所产生的对象,用法和一般函数完全相同,当然我们还可以产生无名的临时对象来履行函数功能.使用内建函数对象 ...

  2. MSP432 BSL流程(UART)

    升级流程 PC程序会解析脚本中的命令,根据命令码做相应的操作.数据来自于命令后的文件(当前目录下的数据文件) # cat script_P4xx_uart.txt LOG //记录日志 MODE P4 ...

  3. 【异常】ERROR main:com.cloudera.enterprise.dbutil.SqlFileRunner: Exception while executing ddl scripts. com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'ROLES' already exists

    1 详细异常 2019-10-11 10:33:55,865 INFO main:com.cloudera.server.cmf.Main: ============================= ...

  4. TAP/TUN浅析

    转:http://www.cnblogs.com/yml435/p/5917628.html 参考链接:https://www.ibm.com/developerworks/cn/linux/1310 ...

  5. Mysql(七):视图、触发器、事务、存储过程、函数

    一 视图 视图是一个虚拟表(非真实存在),其本质是[根据SQL语句获取动态的数据集,并为其命名],用户使用时只需使用[名称]即可获取结果集,可以将该结果集当做表来使用. 使用视图我们可以把查询过程中的 ...

  6. MPU6050应用

    @2019-08-07 [小记] MPU6050开发 -- 基本概念简介 MPU6050原理详解及实例应用 详解卡尔曼滤波原理 卡尔曼算法精讲与C++实现

  7. js动画fireworks烟花

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. 红米k30评测+全面解读

    暂时还没有图片等页面美化..其实网上有很多美图,这里不再粘贴了 红米k30是小米公司子品牌红米最近推出的一款性价比非常高的手机,以下进行多方面解读: 本人会尽量用不懂手机都能理解的语言来各方面讲解k3 ...

  9. luogu4422 [COCI2017-2018#1] Deda[线段树二分]

    讨论帖:线段树二分的题..我还考场切过..白学 这题我一年前的模拟赛考场还切过,现在就不会了..好菜啊. 显然直接线段树拆成$\log n$个区间,然后每个区间在进行线段树二分即可. UPD:复杂度分 ...

  10. 【HDU5952】Counting Cliques

    题目大意:给定一个\(N\)个点,\(M\)条边的无向图,求图中有多少个大小为\(S\)的团.\(N \le 100,deg(i)\le 20,i\in [1,n]\). 题解: 考虑搜索. 需要确定 ...