diff --git a/trunk/Dockerfile.builds b/trunk/Dockerfile.builds index d672102206..fbd80244c5 100644 --- a/trunk/Dockerfile.builds +++ b/trunk/Dockerfile.builds @@ -13,7 +13,7 @@ RUN cd /srs/trunk && ./configure --srt=off --gb28181=off --nasm=off --srtp-nasm= FROM ossrs/srs:dev-cache AS centos7-all COPY . /srs -RUN cd /srs/trunk && ./configure --srt=on --gb28181=on && make +RUN cd /srs/trunk && ./configure --srt=on --gb28181=on --h265=on && make FROM ossrs/srs:dev-cache AS centos7-ansi-no-ffmpeg COPY . /srs @@ -26,7 +26,7 @@ RUN cd /srs/trunk && ./configure --srt=off --gb28181=off --cxx11=off --cxx14=off FROM ossrs/srs:dev6-cache AS centos6-all COPY . /srs -RUN cd /srs/trunk && ./configure --srt=on --gb28181=on --cxx11=off --cxx14=off --sanitizer=off && make +RUN cd /srs/trunk && ./configure --srt=on --gb28181=on --h265=on --cxx11=off --cxx14=off --sanitizer=off && make ######################################################## FROM ossrs/srs:ubuntu16-cache AS ubuntu16-baseline @@ -35,7 +35,7 @@ RUN cd /srs/trunk && ./configure --srt=off --gb28181=off && make FROM ossrs/srs:ubuntu16-cache AS ubuntu16-all COPY . /srs -RUN cd /srs/trunk && ./configure --srt=on --gb28181=on && make +RUN cd /srs/trunk && ./configure --srt=on --gb28181=on --h265=on && make ######################################################## FROM ossrs/srs:ubuntu18-cache AS ubuntu18-baseline @@ -44,7 +44,7 @@ RUN cd /srs/trunk && ./configure --srt=off --gb28181=off && make FROM ossrs/srs:ubuntu18-cache AS ubuntu18-all COPY . /srs -RUN cd /srs/trunk && ./configure --srt=on --gb28181=on && make +RUN cd /srs/trunk && ./configure --srt=on --gb28181=on --h265=on && make ######################################################## FROM ossrs/srs:ubuntu20-cache AS ubuntu20-baseline @@ -53,7 +53,7 @@ RUN cd /srs/trunk && ./configure --srt=off --gb28181=off && make FROM ossrs/srs:ubuntu20-cache AS ubuntu20-all COPY . /srs -RUN cd /srs/trunk && ./configure --srt=on --gb28181=on && make +RUN cd /srs/trunk && ./configure --srt=on --gb28181=on --h265=on && make ######################################################## FROM ossrs/srs:ubuntu16-cross-arm AS ubuntu16-cross-armv7 diff --git a/trunk/Dockerfile.test b/trunk/Dockerfile.test index b4e37dabdb..b266af512a 100644 --- a/trunk/Dockerfile.test +++ b/trunk/Dockerfile.test @@ -6,7 +6,7 @@ COPY . /srs WORKDIR /srs/trunk # Note that we must enable the gcc7 or link failed. -RUN scl enable devtoolset-7 -- ./configure --srt=on --gb28181=on --utest=on +RUN scl enable devtoolset-7 -- ./configure --srt=on --gb28181=on --h265=on --utest=on RUN scl enable devtoolset-7 -- make utest # Build benchmark tool. diff --git a/trunk/auto/auto_headers.sh b/trunk/auto/auto_headers.sh index fcc4a5b968..1552df70ad 100755 --- a/trunk/auto/auto_headers.sh +++ b/trunk/auto/auto_headers.sh @@ -86,6 +86,12 @@ else srs_undefine_macro "SRS_FFMPEG_FIT" $SRS_AUTO_HEADERS_H fi +if [[ $SRS_H265 == YES ]]; then + srs_define_macro "SRS_H265" $SRS_AUTO_HEADERS_H +else + srs_undefine_macro "SRS_H265" $SRS_AUTO_HEADERS_H +fi + if [[ $SRS_SIMULATOR == YES ]]; then srs_define_macro "SRS_SIMULATOR" $SRS_AUTO_HEADERS_H else diff --git a/trunk/auto/options.sh b/trunk/auto/options.sh index acdb240e57..d802ca2b92 100755 --- a/trunk/auto/options.sh +++ b/trunk/auto/options.sh @@ -6,6 +6,7 @@ help=no SRS_HDS=NO SRS_SRT=YES SRS_RTC=YES +SRS_H265=NO SRS_GB28181=NO SRS_CXX11=YES SRS_CXX14=NO @@ -136,6 +137,7 @@ Features: --cxx14=on|off Whether enable the C++14. Default: $(value2switch $SRS_CXX14) --backtrace=on|off Whether show backtrace when crashing. Default: $(value2switch $SRS_BACKTRACE) --ffmpeg-fit=on|off Whether enable the FFmpeg fit(source code). Default: $(value2switch $SRS_FFMPEG_FIT) + --h265=on|off Whether build the HEVC(H.265) support. Default: $(value2switch $SRS_H265) --prefix= The absolute installation path. Default: $SRS_PREFIX --config= The default config file for SRS. Default: $SRS_DEFAULT_CONFIG @@ -305,6 +307,7 @@ function parse_user_option() { --generate-objs) SRS_GENERATE_OBJS=$(switch2value $value) ;; --single-thread) SRS_SINGLE_THREAD=$(switch2value $value) ;; --ffmpeg-fit) SRS_FFMPEG_FIT=$(switch2value $value) ;; + --h265) SRS_H265=$(switch2value $value) ;; --gb28181) SRS_GB28181=$(switch2value $value) ;; --cxx11) SRS_CXX11=$(switch2value $value) ;; @@ -587,6 +590,7 @@ function regenerate_options() { SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cherrypy=$(value2switch $SRS_CHERRYPY)" SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --srt=$(value2switch $SRS_SRT)" SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --rtc=$(value2switch $SRS_RTC)" + SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --h265=$(value2switch $SRS_H265)" SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gb28181=$(value2switch $SRS_GB28181)" SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --simulator=$(value2switch $SRS_SIMULATOR)" SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cxx11=$(value2switch $SRS_CXX11)" diff --git a/trunk/conf/hevc.flv.conf b/trunk/conf/hevc.flv.conf new file mode 100644 index 0000000000..e8ce5cfe0d --- /dev/null +++ b/trunk/conf/hevc.flv.conf @@ -0,0 +1,18 @@ +listen 1935; +max_connections 1000; +daemon off; +srs_log_tank console; +http_api { + enabled on; + listen 1985; +} +http_server { + enabled on; + listen 8080; +} +vhost __defaultVhost__ { + http_remux { + enabled on; + mount [vhost]/[app]/[stream].flv; + } +} diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index 8d3779a56d..e29b973a89 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -8,7 +8,8 @@ The changelog for SRS. ## SRS 6.0 Changelog -* v5.0, 2022-11-22, Merge [#3268](https://github.com/ossrs/srs/pull/3268): H265: Update mpegts.js to play HEVC over HTTP-TS/FLV. v6.0.1 +* v6.0, 2022-11-22, Merge [#3272](https://github.com/ossrs/srs/pull/3272): H265: Support HEVC over RTMP or HTTP-FLV. v6.0.2 +* v6.0, 2022-11-22, Merge [#3268](https://github.com/ossrs/srs/pull/3268): H265: Update mpegts.js to play HEVC over HTTP-TS/FLV. v6.0.1 * v6.0, 2022-11-22, Init SRS 6. v6.0.0 diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index ca64be1525..87d7314bda 100755 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -621,11 +621,13 @@ srs_error_t SrsGopCache::cache(SrsSharedPtrMessage* shared_msg) // got video, update the video count if acceptable if (msg->is_video()) { - // drop video when not h.264 - if (!SrsFlvVideo::h264(msg->payload, msg->size)) { - return err; - } - + // Drop video when not h.264 or h.265. + bool codec_ok = SrsFlvVideo::h264(msg->payload, msg->size); +#ifdef SRS_H265 + codec_ok = codec_ok ? : SrsFlvVideo::hevc(msg->payload, msg->size); +#endif + if (!codec_ok) return err; + cached_video_count++; audio_after_last_video_count = 0; } diff --git a/trunk/src/app/srs_app_statistic.cpp b/trunk/src/app/srs_app_statistic.cpp index 9ac98edf3f..aa6c0ff366 100644 --- a/trunk/src/app/srs_app_statistic.cpp +++ b/trunk/src/app/srs_app_statistic.cpp @@ -622,6 +622,17 @@ void SrsStatistic::dumps_hints_kv(std::stringstream & ss) if (kbps->get_send_kbps_30s()) { ss << "&send=" << kbps->get_send_kbps_30s(); } + +#ifdef SRS_H265 + // For HEVC, we should check active stream which is HEVC codec. + for (std::map::iterator it = streams.begin(); it != streams.end(); it++) { + SrsStatisticStream* stream = it->second; + if (stream->vcodec == SrsVideoCodecIdHEVC) { + ss << "&h265=1"; + break; + } + } +#endif } void SrsStatistic::dumps_cls_summaries(SrsClsSugar* sugar) diff --git a/trunk/src/core/srs_core_version6.hpp b/trunk/src/core/srs_core_version6.hpp index 3bb9d3d2fe..65873f752f 100644 --- a/trunk/src/core/srs_core_version6.hpp +++ b/trunk/src/core/srs_core_version6.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 6 #define VERSION_MINOR 0 -#define VERSION_REVISION 1 +#define VERSION_REVISION 2 #endif diff --git a/trunk/src/kernel/srs_kernel_codec.cpp b/trunk/src/kernel/srs_kernel_codec.cpp index 7991717966..95849d02dc 100644 --- a/trunk/src/kernel/srs_kernel_codec.cpp +++ b/trunk/src/kernel/srs_kernel_codec.cpp @@ -162,11 +162,13 @@ bool SrsFlvVideo::keyframe(char* data, int size) bool SrsFlvVideo::sh(char* data, int size) { - // sequence header only for h264 - if (!h264(data, size)) { - return false; - } - + // Check sequence header only for H.264 or H.265 + bool codec_ok = h264(data, size); +#ifdef SRS_H265 + codec_ok = codec_ok? : hevc(data, size); +#endif + if (!codec_ok) return false; + // 2bytes required. if (size < 2) { return false; @@ -194,6 +196,21 @@ bool SrsFlvVideo::h264(char* data, int size) return codec_id == SrsVideoCodecIdAVC; } +#ifdef SRS_H265 +bool SrsFlvVideo::hevc(char* data, int size) +{ + // 1bytes required. + if (size < 1) { + return false; + } + + char codec_id = data[0]; + codec_id = codec_id & 0x0F; + + return codec_id == SrsVideoCodecIdHEVC; +} +#endif + bool SrsFlvVideo::acceptable(char* data, int size) { // 1bytes required. @@ -202,14 +219,14 @@ bool SrsFlvVideo::acceptable(char* data, int size) } char frame_type = data[0]; - char codec_id = frame_type & 0x0f; + SrsVideoCodecId codec_id = (SrsVideoCodecId)(uint8_t)(frame_type & 0x0f); frame_type = (frame_type >> 4) & 0x0f; if (frame_type < 1 || frame_type > 5) { return false; } - if (codec_id < 2 || codec_id > 7) { + if (codec_id != SrsVideoCodecIdAVC && codec_id != SrsVideoCodecIdAV1 && codec_id != SrsVideoCodecIdHEVC) { return false; } @@ -596,6 +613,12 @@ srs_error_t SrsVideoFrame::add_sample(char* bytes, int size) if ((err = SrsFrame::add_sample(bytes, size)) != srs_success) { return srs_error_wrap(err, "add frame"); } + +#ifdef SRS_H265 + SrsVideoCodecConfig* c = vcodec(); + bool parse_nalus = !c || c->id == SrsVideoCodecIdAVC || c->id == SrsVideoCodecIdForbidden; + if (!parse_nalus) return err; +#endif // for video, parse the nalu type, set the IDR flag. SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(bytes[0] & 0x1f); @@ -707,11 +730,13 @@ srs_error_t SrsFormat::on_video(int64_t timestamp, char* data, int size) // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 int8_t frame_type = buffer->read_1bytes(); SrsVideoCodecId codec_id = (SrsVideoCodecId)(frame_type & 0x0f); - - // TODO: Support other codecs. - if (codec_id != SrsVideoCodecIdAVC) { - return err; - } + + // Check codec for H.264 and H.265. + bool codec_ok = (codec_id == SrsVideoCodecIdAVC); +#ifdef SRS_H265 + codec_ok = codec_ok ? : (codec_id == SrsVideoCodecIdHEVC); +#endif + if (!codec_ok) return err; if (!vcodec) { vcodec = new SrsVideoCodecConfig(); @@ -778,10 +803,14 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp) srs_warn("avc igone the info frame"); return err; } - - // only support h.264/avc - if (codec_id != SrsVideoCodecIdAVC) { - return srs_error_new(ERROR_HLS_DECODE_ERROR, "avc only support video h.264/avc, actual=%d", codec_id); + + // Check codec for H.264 and H.265. + bool codec_ok = (codec_id == SrsVideoCodecIdAVC); +#ifdef SRS_H265 + codec_ok = codec_ok ? : (codec_id == SrsVideoCodecIdHEVC); +#endif + if (!codec_ok) { + return srs_error_new(ERROR_HLS_DECODE_ERROR, "only support video H.264/H.265, actual=%d", codec_id); } vcodec->id = codec_id; @@ -799,7 +828,28 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp) // Update the RAW AVC data. raw = stream->data() + stream->pos(); nb_raw = stream->size() - stream->pos(); - + + // Parse sequence header for H.265/HEVC. + if (codec_id == SrsVideoCodecIdHEVC) { +#ifdef SRS_H265 + if (avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader) { + // TODO: demux vps/sps/pps for hevc + if ((err = hevc_demux_hvcc(stream)) != srs_success) { + return srs_error_wrap(err, "demux hevc SPS/PPS"); + } + } else if (avc_packet_type == SrsVideoAvcFrameTraitNALU) { + // TODO: demux nalu for hevc + if ((err = video_nalu_demux(stream)) != srs_success) { + return srs_error_wrap(err, "demux hevc NALU"); + } + } + return err; +#else + return srs_error_new(ERROR_HLS_DECODE_ERROR, "H.265 is disabled"); +#endif + } + + // Parse sequence header for H.264/AVC. if (avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader) { // TODO: FIXME: Maybe we should ignore any error for parsing sps/pps. if ((err = avc_demux_sps_pps(stream)) != srs_success) { @@ -819,6 +869,175 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp) // For media server, we don't care the codec, so we just try to parse sps-pps, and we could ignore any error if fail. // LCOV_EXCL_START +#ifdef SRS_H265 +// Parse the hevc vps/sps/pps +srs_error_t SrsFormat::hevc_demux_hvcc(SrsBuffer* stream) +{ + int avc_extra_size = stream->size() - stream->pos(); + if (avc_extra_size > 0) { + char *copy_stream_from = stream->data() + stream->pos(); + vcodec->avc_extra_data = std::vector(copy_stream_from, copy_stream_from + avc_extra_size); + } + + const int HEVC_MIN_SIZE = 23; // From configuration_version to numOfArrays + if (!stream->require(HEVC_MIN_SIZE)) { + return srs_error_new(ERROR_HLS_DECODE_ERROR, "requires %d only %d bytes", HEVC_MIN_SIZE, stream->left()); + } + + SrsHevcDecoderConfigurationRecord* dec_conf_rec_p = &(vcodec->hevc_dec_conf_record_); + dec_conf_rec_p->configuration_version = stream->read_1bytes(); + if (dec_conf_rec_p->configuration_version != 1) { + return srs_error_new(ERROR_HLS_DECODE_ERROR, "invalid version=%d", dec_conf_rec_p->configuration_version); + } + + // Read general_profile_space(2bits), general_tier_flag(1bit), general_profile_idc(5bits) + uint8_t data_byte = stream->read_1bytes(); + dec_conf_rec_p->general_profile_space = (data_byte >> 6) & 0x03; + dec_conf_rec_p->general_tier_flag = (data_byte >> 5) & 0x01; + dec_conf_rec_p->general_profile_idc = data_byte & 0x1F; + srs_info("hevc version:%d, general_profile_space:%d, general_tier_flag:%d, general_profile_idc:%d", + dec_conf_rec_p->configuration_version, dec_conf_rec_p->general_profile_space, dec_conf_rec_p->general_tier_flag, + dec_conf_rec_p->general_profile_idc); + + //general_profile_compatibility_flags: 32bits + dec_conf_rec_p->general_profile_compatibility_flags = (uint32_t)stream->read_4bytes(); + + //general_constraint_indicator_flags: 48bits + uint64_t data_64bit = (uint64_t)stream->read_4bytes(); + data_64bit = (data_64bit << 16) | (stream->read_2bytes()); + dec_conf_rec_p->general_constraint_indicator_flags = data_64bit; + + //general_level_idc: 8bits + dec_conf_rec_p->general_level_idc = stream->read_1bytes(); + //min_spatial_segmentation_idc: xxxx 14bits + dec_conf_rec_p->min_spatial_segmentation_idc = stream->read_2bytes() & 0x0fff; + //parallelism_type: xxxx xx 2bits + dec_conf_rec_p->parallelism_type = stream->read_1bytes() & 0x03; + //chroma_format: xxxx xx 2bits + dec_conf_rec_p->chroma_format = stream->read_1bytes() & 0x03; + //bit_depth_luma_minus8: xxxx x 3bits + dec_conf_rec_p->bit_depth_luma_minus8 = stream->read_1bytes() & 0x07; + //bit_depth_chroma_minus8: xxxx x 3bits + dec_conf_rec_p->bit_depth_chroma_minus8 = stream->read_1bytes() & 0x07; + srs_info("general_constraint_indicator_flags:0x%x, general_level_idc:%d, min_spatial_segmentation_idc:%d, parallelism_type:%d, chroma_format:%d, bit_depth_luma_minus8:%d, bit_depth_chroma_minus8:%d", + dec_conf_rec_p->general_constraint_indicator_flags, dec_conf_rec_p->general_level_idc, + dec_conf_rec_p->min_spatial_segmentation_idc, dec_conf_rec_p->parallelism_type, dec_conf_rec_p->chroma_format, + dec_conf_rec_p->bit_depth_luma_minus8, dec_conf_rec_p->bit_depth_chroma_minus8); + + //avg_frame_rate: 16bits + dec_conf_rec_p->avg_frame_rate = stream->read_2bytes(); + //8bits: constant_frame_rate(2bits), num_temporal_layers(3bits), + // temporal_id_nested(1bit), length_size_minus_one(2bits) + data_byte = stream->read_1bytes(); + dec_conf_rec_p->constant_frame_rate = (data_byte >> 6) & 0x03; + dec_conf_rec_p->num_temporal_layers = (data_byte >> 3) & 0x07; + dec_conf_rec_p->temporal_id_nested = (data_byte >> 2) & 0x01; + dec_conf_rec_p->length_size_minus_one = data_byte & 0x03; + + uint8_t numOfArrays = stream->read_1bytes(); + srs_info("avg_frame_rate:%d, constant_frame_rate:%d, num_temporal_layers:%d, temporal_id_nested:%d, length_size_minus_one:%d, numOfArrays:%d", + dec_conf_rec_p->avg_frame_rate, dec_conf_rec_p->constant_frame_rate, dec_conf_rec_p->num_temporal_layers, + dec_conf_rec_p->temporal_id_nested, dec_conf_rec_p->length_size_minus_one, numOfArrays); + + //parse vps/pps/sps + for (int index = 0; index < numOfArrays; index++) { + SrsHevcHvccNalu hevc_unit; + + if (!stream->require(5)) { + return srs_error_new(ERROR_HLS_DECODE_ERROR, "requires 5 only %d bytes", stream->left()); + } + data_byte = stream->read_1bytes(); + hevc_unit.array_completeness = (data_byte >> 7) & 0x01; + hevc_unit.nal_unit_type = data_byte & 0x3f; + hevc_unit.num_nalus = stream->read_2bytes(); + + for (int i = 0; i < hevc_unit.num_nalus; i++) { + SrsHevcNalData data_item; + data_item.nal_unit_length = stream->read_2bytes(); + + if (!stream->require(data_item.nal_unit_length)) { + return srs_error_new(ERROR_HLS_DECODE_ERROR, "requires %d only %d bytes", + data_item.nal_unit_length, stream->left()); + } + //copy vps/pps/sps data + data_item.nal_unit_data.resize(data_item.nal_unit_length); + + stream->read_bytes((char*)(&data_item.nal_unit_data[0]), data_item.nal_unit_length); + srs_info("hevc nalu type:%d, array_completeness:%d, num_nalus:%d, i:%d, nal_unit_length:%d", + hevc_unit.nal_unit_type, hevc_unit.array_completeness, hevc_unit.num_nalus, i, data_item.nal_unit_length); + hevc_unit.nal_data_vec.push_back(data_item); + } + dec_conf_rec_p->nalu_vec.push_back(hevc_unit); + } + + return srs_success; +} + +srs_error_t SrsFormat::hevc_demux_ibmf_format(SrsBuffer* stream) +{ + srs_error_t err = srs_success; + + int PictureLength = stream->size() - stream->pos(); + int nal_len_size = vcodec->hevc_dec_conf_record_.length_size_minus_one; + + // 5.3.4.2.1 Syntax, ISO_IEC_14496-15-AVC-format-2012.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_len_size != 2); + + // 5.3.4.2.1 Syntax, ISO_IEC_14496-15-AVC-format-2012.pdf, page 20 + for (int i = 0; i < PictureLength;) { + if (i + nal_len_size >= PictureLength) { + break; + } + // unsigned int((NAL_unit_length+1)*8) NALUnitLength; + if (!stream->require(nal_len_size + 1)) { + return srs_error_new(ERROR_HLS_DECODE_ERROR, "nal_len_size:%d, PictureLength:%d, i:%d", + nal_len_size, PictureLength, i); + } + int32_t NALUnitLength = 0; + + if (nal_len_size == 3) { + NALUnitLength = stream->read_4bytes(); + } else if (nal_len_size == 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) { + return srs_error_new(ERROR_HLS_DECODE_ERROR, "pic length:%d, NAL_unit_length:%d, NALUnitLength:%d", + PictureLength, nal_len_size, NALUnitLength); + } + + // NALUnit + if (!stream->require(NALUnitLength)) { + return srs_error_new(ERROR_HLS_DECODE_ERROR, "avc decode NALU data"); + } + + uint8_t* header_p = (uint8_t*)(stream->data() + stream->pos()); + uint8_t nalu_type = (*header_p & 0x3f) >> 1; + bool irap = (SrsHevcNaluType_CODED_SLICE_BLA <= nalu_type) && (nalu_type <= SrsHevcNaluType_RESERVED_23); + if (irap) { + video->has_idr = true; + } + + if ((err = video->add_sample(stream->data() + stream->pos(), NALUnitLength)) != srs_success) { + return srs_error_wrap(err, "avc add video frame"); + } + stream->skip(NALUnitLength); + + i += vcodec->NAL_unit_length + 1 + NALUnitLength; + } + + return err; +} +#endif + srs_error_t SrsFormat::avc_demux_sps_pps(SrsBuffer* stream) { // AVCDecoderConfigurationRecord @@ -832,7 +1051,7 @@ srs_error_t SrsFormat::avc_demux_sps_pps(SrsBuffer* stream) if (!stream->require(6)) { return srs_error_new(ERROR_HLS_DECODE_ERROR, "avc decode sequence header"); } - //int8_t configurationVersion = stream->read_1bytes(); + //int8_t configuration_version = stream->read_1bytes(); stream->read_1bytes(); //int8_t AVCProfileIndication = stream->read_1bytes(); vcodec->avc_profile = (SrsAvcProfile)stream->read_1bytes(); @@ -1192,6 +1411,13 @@ srs_error_t SrsFormat::video_nalu_demux(SrsBuffer* stream) return err; } +#ifdef SRS_H265 + if (vcodec->id == SrsVideoCodecIdHEVC) { + // TODO: FIXME: Might need to guess format? + return hevc_demux_ibmf_format(stream); + } +#endif + // Parse the SPS/PPS in ANNEXB or IBMF format. if (vcodec->payload_format == SrsAvcPayloadFormatIbmf) { if ((err = avc_demux_ibmf_format(stream)) != srs_success) { diff --git a/trunk/src/kernel/srs_kernel_codec.hpp b/trunk/src/kernel/srs_kernel_codec.hpp index e4f781bc4e..41ad19bf1a 100644 --- a/trunk/src/kernel/srs_kernel_codec.hpp +++ b/trunk/src/kernel/srs_kernel_codec.hpp @@ -25,6 +25,7 @@ class SrsBuffer; * 5 = On2 VP6 with alpha channel * 6 = Screen video version 2 * 7 = AVC + * 12 = HEVC */ enum SrsVideoCodecId { @@ -258,6 +259,10 @@ class SrsFlvVideo * check codec h264. */ static bool h264(char* data, int size); +#ifdef SRS_H265 + // Check whether codec is HEVC(H.265). + static bool hevc(char* data, int size); +#endif /** * check the video RTMP/flv header info, * @return true if video RTMP/flv header is ok. @@ -394,6 +399,110 @@ enum SrsAvcNaluType }; std::string srs_avc_nalu2str(SrsAvcNaluType nalu_type); +#ifdef SRS_H265 +// The enum NALU type for HEVC. +enum SrsHevcNaluType { + SrsHevcNaluType_CODED_SLICE_TRAIL_N = 0, + SrsHevcNaluType_CODED_SLICE_TRAIL_R, //1 + SrsHevcNaluType_CODED_SLICE_TSA_N, //2 + SrsHevcNaluType_CODED_SLICE_TLA, //3 + SrsHevcNaluType_CODED_SLICE_STSA_N, //4 + SrsHevcNaluType_CODED_SLICE_STSA_R, //5 + SrsHevcNaluType_CODED_SLICE_RADL_N, //6 + SrsHevcNaluType_CODED_SLICE_DLP, //7 + SrsHevcNaluType_CODED_SLICE_RASL_N, //8 + SrsHevcNaluType_CODED_SLICE_TFD, //9 + SrsHevcNaluType_RESERVED_10, + SrsHevcNaluType_RESERVED_11, + SrsHevcNaluType_RESERVED_12, + SrsHevcNaluType_RESERVED_13, + SrsHevcNaluType_RESERVED_14, + SrsHevcNaluType_RESERVED_15, + SrsHevcNaluType_CODED_SLICE_BLA, //16 + SrsHevcNaluType_CODED_SLICE_BLANT, //17 + SrsHevcNaluType_CODED_SLICE_BLA_N_LP, //18 + SrsHevcNaluType_CODED_SLICE_IDR, //19 + SrsHevcNaluType_CODED_SLICE_IDR_N_LP, //20 + SrsHevcNaluType_CODED_SLICE_CRA, //21 + SrsHevcNaluType_RESERVED_22, + SrsHevcNaluType_RESERVED_23, + SrsHevcNaluType_RESERVED_24, + SrsHevcNaluType_RESERVED_25, + SrsHevcNaluType_RESERVED_26, + SrsHevcNaluType_RESERVED_27, + SrsHevcNaluType_RESERVED_28, + SrsHevcNaluType_RESERVED_29, + SrsHevcNaluType_RESERVED_30, + SrsHevcNaluType_RESERVED_31, + SrsHevcNaluType_VPS, // 32 + SrsHevcNaluType_SPS, // 33 + SrsHevcNaluType_PPS, // 34 + SrsHevcNaluType_ACCESS_UNIT_DELIMITER, // 35 + SrsHevcNaluType_EOS, // 36 + SrsHevcNaluType_EOB, // 37 + SrsHevcNaluType_FILLER_DATA, // 38 + SrsHevcNaluType_SEI , // 39 Prefix SEI + SrsHevcNaluType_SEI_SUFFIX, // 40 Suffix SEI + SrsHevcNaluType_RESERVED_41, + SrsHevcNaluType_RESERVED_42, + SrsHevcNaluType_RESERVED_43, + SrsHevcNaluType_RESERVED_44, + SrsHevcNaluType_RESERVED_45, + SrsHevcNaluType_RESERVED_46, + SrsHevcNaluType_RESERVED_47, + SrsHevcNaluType_UNSPECIFIED_48, + SrsHevcNaluType_UNSPECIFIED_49, + SrsHevcNaluType_UNSPECIFIED_50, + SrsHevcNaluType_UNSPECIFIED_51, + SrsHevcNaluType_UNSPECIFIED_52, + SrsHevcNaluType_UNSPECIFIED_53, + SrsHevcNaluType_UNSPECIFIED_54, + SrsHevcNaluType_UNSPECIFIED_55, + SrsHevcNaluType_UNSPECIFIED_56, + SrsHevcNaluType_UNSPECIFIED_57, + SrsHevcNaluType_UNSPECIFIED_58, + SrsHevcNaluType_UNSPECIFIED_59, + SrsHevcNaluType_UNSPECIFIED_60, + SrsHevcNaluType_UNSPECIFIED_61, + SrsHevcNaluType_UNSPECIFIED_62, + SrsHevcNaluType_UNSPECIFIED_63, + SrsHevcNaluType_INVALID, +}; + +struct SrsHevcNalData { + uint16_t nal_unit_length; + std::vector nal_unit_data; +}; + +struct SrsHevcHvccNalu { + uint8_t array_completeness; + uint8_t nal_unit_type; + uint16_t num_nalus; + std::vector nal_data_vec; +}; + +struct SrsHevcDecoderConfigurationRecord { + uint8_t configuration_version; + uint8_t general_profile_space; + uint8_t general_tier_flag; + uint8_t general_profile_idc; + uint32_t general_profile_compatibility_flags; + uint64_t general_constraint_indicator_flags; + uint8_t general_level_idc; + uint16_t min_spatial_segmentation_idc; + uint8_t parallelism_type; + uint8_t chroma_format; + uint8_t bit_depth_luma_minus8; + uint8_t bit_depth_chroma_minus8; + uint16_t avg_frame_rate; + uint8_t constant_frame_rate; + uint8_t num_temporal_layers; + uint8_t temporal_id_nested; + uint8_t length_size_minus_one; + std::vector nalu_vec; +}; +#endif + /** * Table 7-6 – Name association to slice_type * ISO_IEC_14496-10-AVC-2012.pdf, page 105. @@ -639,6 +748,10 @@ class SrsVideoCodecConfig : public SrsCodecConfig public: // the avc payload format. SrsAvcPayloadFormat payload_format; +#ifdef SRS_H265 +public: + SrsHevcDecoderConfigurationRecord hevc_dec_conf_record_; +#endif public: SrsVideoCodecConfig(); virtual ~SrsVideoCodecConfig(); @@ -757,13 +870,18 @@ class SrsFormat // Demux the sps/pps from sequence header. // Demux the samples from NALUs. virtual srs_error_t video_avc_demux(SrsBuffer* stream, int64_t timestamp); +#ifdef SRS_H265 +private: + virtual srs_error_t hevc_demux_hvcc(SrsBuffer* stream); + virtual srs_error_t hevc_demux_ibmf_format(SrsBuffer* stream); +#endif private: // Parse the H.264 SPS/PPS. virtual srs_error_t avc_demux_sps_pps(SrsBuffer* stream); virtual srs_error_t avc_demux_sps(); virtual srs_error_t avc_demux_sps_rbsp(char* rbsp, int nb_rbsp); private: - // Parse the H.264 NALUs. + // Parse the H.264 or H.265 NALUs. virtual srs_error_t video_nalu_demux(SrsBuffer* stream); // Demux the avc NALU in "AnnexB" from ISO_IEC_14496-10-AVC-2003.pdf, page 211. virtual srs_error_t avc_demux_annexb_format(SrsBuffer* stream); diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 808ee201d0..b3ec8e6951 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -3327,7 +3327,9 @@ VOID TEST(KernelCodecTest, CoverAll) EXPECT_TRUE(!v.acceptable((char*)"\xf0", 1)); EXPECT_TRUE(!v.acceptable((char*)"\x10", 1)); EXPECT_TRUE(!v.acceptable((char*)"\x1f", 1)); - EXPECT_TRUE(v.acceptable((char*)"\x13", 1)); + EXPECT_TRUE(v.acceptable((char*)"\x17", 1)); // AVC = 7 + EXPECT_TRUE(v.acceptable((char*)"\x1c", 1)); // HEVC = 12 + EXPECT_TRUE(v.acceptable((char*)"\x1d", 1)); // AV1 = 13 } if (true) {