1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

H265: Support HEVC over RTMP or HTTP-FLV. (#3272)

1. Support configure with --h265=on.
2. Parse HEVC(H.265) from FLV or RTMP packet.
3. Support HEVC over RTMP or HTTP-FLV.

Co-authored-by: runner365 <shi.weibd@hotmail.com>
This commit is contained in:
Winlin 2022-11-23 08:34:13 +08:00 committed by GitHub
parent 7e02d972ea
commit 178e40a5fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 440 additions and 44 deletions

View file

@ -126,7 +126,7 @@ jobs:
which make gcc g++ patch cmake pkg-config uname grep sed &&
(make --version; gcc --version; patch --version; cmake --version; pkg-config --version) &&
(aclocal --version; autoconf --version; automake --version; uname -a) &&
cd $(cygpath -u $SRS_WORKSPACE)/trunk && ./configure && make
cd $(cygpath -u $SRS_WORKSPACE)/trunk && ./configure --gb28181=on --h265=on && make
##################################################################################################################
- name: Package SRS
env:

View file

@ -23,7 +23,7 @@ WORKDIR /srs/trunk
# Build and install SRS.
# Note that SRT is enabled by default, so we configure without --srt=on.
RUN ./configure --gb28181=on --sanitizer-static=on && make && make install
RUN ./configure --gb28181=on --h265=on --sanitizer-static=on && make && make install
# All config files for SRS.
RUN cp -R conf /usr/local/srs/conf && \

View file

@ -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

View file

@ -8,5 +8,5 @@ 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 --gcov=on --sanitizer=off
RUN scl enable devtoolset-7 -- ./configure --srt=on --gb28181=on --h265=on --utest=on --gcov=on --sanitizer=off
RUN scl enable devtoolset-7 -- make utest

View file

@ -10,5 +10,5 @@ RUN yum install -y zip
# Build and install SRS.
ADD srs-server-${version}.tar.gz /srs
WORKDIR /srs/srs-server-${version}/trunk
RUN ./scripts/package.sh --x86-x64 --tag=${version}
RUN ./scripts/package.sh --x86-x64 --gb28181=on --h265=on --tag=${version}

View file

@ -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.

View file

@ -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

View file

@ -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=<path> The absolute installation path. Default: $SRS_PREFIX
--config=<path> 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)"

18
trunk/conf/hevc.flv.conf Normal file
View file

@ -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;
}
}

View file

@ -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
<a name="v5-changes"></a>

View file

@ -27,7 +27,7 @@ ProcessorCount(JOBS)
# We should always configure SRS for switching between branches.
IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
EXECUTE_PROCESS(
COMMAND ./configure --osx --srt=on --gb28181=on --utest=on --jobs=${JOBS}
COMMAND ./configure --osx --srt=on --gb28181=on --h265=on --utest=on --jobs=${JOBS}
WORKING_DIRECTORY ${SRS_DIR} RESULT_VARIABLE ret)
ELSE ()
EXECUTE_PROCESS(

View file

@ -621,10 +621,12 @@ 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;
@ -1063,14 +1065,22 @@ srs_error_t SrsOriginHub::on_video(SrsSharedPtrMessage* shared_video, bool is_se
// when got video stream info.
SrsStatistic* stat = SrsStatistic::instance();
if ((err = stat->on_video_info(req_, SrsVideoCodecIdAVC, c->avc_profile, c->avc_level, c->width, c->height)) != srs_success) {
if (c->id == SrsVideoCodecIdAVC) {
err = stat->on_video_info(req_, c->id, c->avc_profile, c->avc_level, c->width, c->height);
srs_trace("%dB video sh, codec(%d, profile=%s, level=%s, %dx%d, %dkbps, %.1ffps, %.1fs)",
msg->size, c->id, srs_avc_profile2str(c->avc_profile).c_str(), srs_avc_level2str(c->avc_level).c_str(),
c->width, c->height, c->video_data_rate / 1000, c->frame_rate, c->duration);
#ifdef SRS_H265
} else if (c->id == SrsVideoCodecIdHEVC) {
// TODO: FIXME: Use the correct information for HEVC.
err = stat->on_video_info(req_, c->id, c->avc_profile, c->avc_level, c->width, c->height);
srs_trace("%dB video sh, codec(%d)", msg->size, c->id);
#endif
}
if (err != srs_success) {
return srs_error_wrap(err, "stat video");
}
srs_trace("%dB video sh, codec(%d, profile=%s, level=%s, %dx%d, %dkbps, %.1ffps, %.1fs)",
msg->size, c->id, srs_avc_profile2str(c->avc_profile).c_str(),
srs_avc_level2str(c->avc_level).c_str(), c->width, c->height,
c->video_data_rate / 1000, c->frame_rate, c->duration);
}
// Ignore video data when no sps/pps

View file

@ -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<std::string, SrsStatisticStream*>::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)

View file

@ -9,6 +9,6 @@
#define VERSION_MAJOR 6
#define VERSION_MINOR 0
#define VERSION_REVISION 1
#define VERSION_REVISION 2
#endif

View file

@ -162,10 +162,12 @@ 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) {
@ -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;
}
@ -597,6 +614,12 @@ srs_error_t SrsVideoFrame::add_sample(char* bytes, int size)
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);
@ -708,10 +731,12 @@ srs_error_t SrsFormat::on_video(int64_t timestamp, char* data, int size)
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();
@ -779,9 +804,13 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
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;
@ -800,6 +829,27 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
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<char>(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) {

View file

@ -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 @@ public:
* 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<uint8_t> nal_unit_data;
};
struct SrsHevcHvccNalu {
uint8_t array_completeness;
uint8_t nal_unit_type;
uint16_t num_nalus;
std::vector<SrsHevcNalData> 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<SrsHevcHvccNalu> nalu_vec;
};
#endif
/**
* Table 7-6 Name association to slice_type
* ISO_IEC_14496-10-AVC-2012.pdf, page 105.
@ -639,6 +748,10 @@ public:
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 @@ private:
// 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);

View file

@ -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) {