Skip to content

Commit 6dd1bf8

Browse files
runner365winlinvip
authored andcommitted
H265: Support HEVC over HTTP-TS. v6.0.4
1. Update TS video codec to HEVC during streaming. 2. Return error when HEVC is disabled. 3. Parse HEVC NALU type by SrsHevcNaluTypeParse. 4. Show message when codec change for TS.
1 parent f316e9a commit 6dd1bf8

10 files changed

+151
-30
lines changed

trunk/doc/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ The changelog for SRS.
88

99
## SRS 6.0 Changelog
1010

11+
* v6.0, 2022-11-23, Merge [#3275](https://github.com/ossrs/srs/pull/3275): H265: Support HEVC over HTTP-TS. v6.0.4
1112
* v6.0, 2022-11-23, Merge [#3274](https://github.com/ossrs/srs/pull/3274): H265: Support parse multiple NALUs in a frame. v6.0.3
1213
* 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
1314
* 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

trunk/ide/srs_clion/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ TARGET_LINK_LIBRARIES(srs dl)
8888
TARGET_LINK_LIBRARIES(srs ${DEPS_LIBS})
8989
TARGET_LINK_LIBRARIES(srs -ldl -pthread)
9090
TARGET_LINK_LIBRARIES(srs -rdynamic)
91+
TARGET_LINK_LIBRARIES(srs -fsanitize=address -fno-omit-frame-pointer)
9192

9293
###########################################################
9394
# For utest.

trunk/src/app/srs_app_hls.cpp

+9-3
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ srs_error_t SrsHlsMuxer::flush_audio(SrsTsMessageCache* cache)
555555
return err;
556556
}
557557

558-
srs_error_t SrsHlsMuxer::flush_video(SrsTsMessageCache* cache)
558+
srs_error_t SrsHlsMuxer::flush_video(SrsTsMessageCache* cache, SrsVideoFrame* frame)
559559
{
560560
srs_error_t err = srs_success;
561561

@@ -573,6 +573,12 @@ srs_error_t SrsHlsMuxer::flush_video(SrsTsMessageCache* cache)
573573

574574
// update the duration of segment.
575575
current->append(cache->video->dts / 90);
576+
577+
// The video codec might change during streaming. Note that the frame might be NULL, when reap segment.
578+
if (frame && frame->vcodec()) {
579+
SrsTsContextWriter* tscw = current->tscw;
580+
tscw->update_video_codec(frame->vcodec()->id);
581+
}
576582

577583
if ((err = current->tscw->write_video(cache->video)) != srs_success) {
578584
return srs_error_wrap(err, "hls: write video");
@@ -1025,7 +1031,7 @@ srs_error_t SrsHlsController::write_video(SrsVideoFrame* frame, int64_t dts)
10251031
}
10261032

10271033
// flush video when got one
1028-
if ((err = muxer->flush_video(tsmc)) != srs_success) {
1034+
if ((err = muxer->flush_video(tsmc, frame)) != srs_success) {
10291035
return srs_error_wrap(err, "hls: flush video");
10301036
}
10311037

@@ -1057,7 +1063,7 @@ srs_error_t SrsHlsController::reap_segment()
10571063
}
10581064

10591065
// segment open, flush video first.
1060-
if ((err = muxer->flush_video(tsmc)) != srs_success) {
1066+
if ((err = muxer->flush_video(tsmc, NULL)) != srs_success) {
10611067
return srs_error_wrap(err, "hls: flush video");
10621068
}
10631069

trunk/src/app/srs_app_hls.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ class SrsHlsMuxer
193193
// Whether current hls muxer is pure audio mode.
194194
virtual bool pure_audio();
195195
virtual srs_error_t flush_audio(SrsTsMessageCache* cache);
196-
virtual srs_error_t flush_video(SrsTsMessageCache* cache);
196+
virtual srs_error_t flush_video(SrsTsMessageCache* cache, SrsVideoFrame* frame);
197197
// Close segment(ts).
198198
virtual srs_error_t segment_close();
199199
private:

trunk/src/core/srs_core_version6.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99

1010
#define VERSION_MAJOR 6
1111
#define VERSION_MINOR 0
12-
#define VERSION_REVISION 3
12+
#define VERSION_REVISION 4
1313

1414
#endif

trunk/src/kernel/srs_kernel_codec.cpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -620,11 +620,11 @@ srs_error_t SrsVideoFrame::add_sample(char* bytes, int size)
620620
// For HEVC(H.265), try to parse the IDR from NALUs.
621621
if (c && c->id == SrsVideoCodecIdHEVC) {
622622
#ifdef SRS_H265
623-
SrsHevcNaluType nalu_type = (SrsHevcNaluType)(uint8_t)((bytes[0] & 0x3f) >> 1);
623+
SrsHevcNaluType nalu_type = SrsHevcNaluTypeParse(bytes[0]);
624624
has_idr = (SrsHevcNaluType_CODED_SLICE_BLA <= nalu_type) && (nalu_type <= SrsHevcNaluType_RESERVED_23);
625625
return err;
626626
#else
627-
return srs_error_new(ERROR_HLS_DECODE_ERROR, "H.265 is disabled");
627+
return srs_error_new(ERROR_HEVC_DISABLED, "H.265 is disabled");
628628
#endif
629629
}
630630

@@ -854,7 +854,7 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
854854
}
855855
return err;
856856
#else
857-
return srs_error_new(ERROR_HLS_DECODE_ERROR, "H.265 is disabled");
857+
return srs_error_new(ERROR_HEVC_DISABLED, "H.265 is disabled");
858858
#endif
859859
}
860860

@@ -1373,7 +1373,7 @@ srs_error_t SrsFormat::video_nalu_demux(SrsBuffer* stream)
13731373
// TODO: FIXME: Might need to guess format?
13741374
return do_avc_demux_ibmf_format(stream);
13751375
#else
1376-
return srs_error_new(ERROR_HLS_DECODE_ERROR, "H.265 is disabled");
1376+
return srs_error_new(ERROR_HEVC_DISABLED, "H.265 is disabled");
13771377
#endif
13781378
}
13791379

@@ -1500,6 +1500,7 @@ srs_error_t SrsFormat::do_avc_demux_ibmf_format(SrsBuffer* stream)
15001500
// 5.3.4.2.1 Syntax, ISO_IEC_14496-15-AVC-format-2012.pdf, page 20
15011501
for (int i = 0; i < PictureLength;) {
15021502
// unsigned int((NAL_unit_length+1)*8) NALUnitLength;
1503+
// TODO: FIXME: Should ignore error? See https://github.com/ossrs/srs-gb28181/commit/a13b9b54938a14796abb9011e7a8ee779439a452
15031504
if (!stream->require(vcodec->NAL_unit_length + 1)) {
15041505
return srs_error_new(ERROR_HLS_DECODE_ERROR, "PictureLength:%d, i:%d, NaluLength:%d, left:%d",
15051506
PictureLength, i, vcodec->NAL_unit_length, stream->left());

trunk/src/kernel/srs_kernel_codec.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ enum SrsHevcNaluType {
468468
SrsHevcNaluType_UNSPECIFIED_63,
469469
SrsHevcNaluType_INVALID,
470470
};
471+
#define SrsHevcNaluTypeParse(code) (SrsHevcNaluType)((code & 0x7E) >> 1)
471472

472473
struct SrsHevcNalData {
473474
uint16_t nal_unit_length;

trunk/src/kernel/srs_kernel_error.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,8 @@
266266
XX(ERROR_INOTIFY_OPENFD , 3094, "InotifyOpenFd", "Failed to open inotify fd for config listener") \
267267
XX(ERROR_INOTIFY_WATCH , 3095, "InotfyWatch", "Failed to watch inotify for config listener") \
268268
XX(ERROR_HTTP_URL_UNESCAPE , 3096, "HttpUrlUnescape", "Failed to unescape URL for HTTP") \
269-
XX(ERROR_HTTP_WITH_BODY , 3097, "HttpWithBody", "Failed for HTTP body")
269+
XX(ERROR_HTTP_WITH_BODY , 3097, "HttpWithBody", "Failed for HTTP body") \
270+
XX(ERROR_HEVC_DISABLED , 3098, "HevcDisabled", "HEVC is disabled")
270271

271272
/**************************************************/
272273
/* HTTP/StreamConverter protocol error. */

trunk/src/kernel/srs_kernel_ts.cpp

+121-19
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ string srs_ts_stream2string(SrsTsStream stream)
4343
case SrsTsStreamAudioAC3: return "AC3";
4444
case SrsTsStreamAudioDTS: return "AudioDTS";
4545
case SrsTsStreamVideoH264: return "H.264";
46+
#ifdef SRS_H265
47+
case SrsTsStreamVideoHEVC: return "H.265";
48+
#endif
4649
case SrsTsStreamVideoMpeg4: return "MP4";
4750
case SrsTsStreamAudioMpeg4: return "MP4A";
4851
default: return "Other";
@@ -285,6 +288,14 @@ srs_error_t SrsTsContext::encode(ISrsStreamWriter* writer, SrsTsMessage* msg, Sr
285288
vs = SrsTsStreamVideoH264;
286289
video_pid = TS_VIDEO_AVC_PID;
287290
break;
291+
case SrsVideoCodecIdHEVC:
292+
#ifdef SRS_H265
293+
vs = SrsTsStreamVideoHEVC;
294+
video_pid = TS_VIDEO_AVC_PID;
295+
break;
296+
#else
297+
return srs_error_new(ERROR_HEVC_DISABLED, "H.265 is disabled");
298+
#endif
288299
case SrsVideoCodecIdDisabled:
289300
vs = SrsTsStreamReserved;
290301
break;
@@ -296,7 +307,6 @@ srs_error_t SrsTsContext::encode(ISrsStreamWriter* writer, SrsTsMessage* msg, Sr
296307
case SrsVideoCodecIdOn2VP6:
297308
case SrsVideoCodecIdOn2VP6WithAlphaChannel:
298309
case SrsVideoCodecIdScreenVideoVersion2:
299-
case SrsVideoCodecIdHEVC:
300310
case SrsVideoCodecIdAV1:
301311
vs = SrsTsStreamReserved;
302312
break;
@@ -335,10 +345,13 @@ srs_error_t SrsTsContext::encode(ISrsStreamWriter* writer, SrsTsMessage* msg, Sr
335345
return srs_error_new(ERROR_HLS_NO_STREAM, "ts: no a/v stream, vcodec=%d, acodec=%d", vc, ac);
336346
}
337347

338-
// when any codec changed, write PAT/PMT table.
348+
// When any codec changed, write PAT/PMT table.
339349
if (vcodec != vc || acodec != ac) {
340-
vcodec = vc;
341-
acodec = ac;
350+
if (vcodec != SrsVideoCodecIdReserved || acodec != SrsAudioCodecIdReserved1) {
351+
srs_trace("TS: Refresh PMT when vcodec=%d=>%d, acodec=%d=>%d", vcodec, vc, acodec, ac);
352+
}
353+
vcodec = vc; acodec = ac;
354+
342355
if ((err = encode_pat_pmt(writer, video_pid, vs, audio_pid, as)) != srs_success) {
343356
return srs_error_wrap(err, "ts: encode PAT/PMT");
344357
}
@@ -355,8 +368,12 @@ srs_error_t SrsTsContext::encode(ISrsStreamWriter* writer, SrsTsMessage* msg, Sr
355368
srs_error_t SrsTsContext::encode_pat_pmt(ISrsStreamWriter* writer, int16_t vpid, SrsTsStream vs, int16_t apid, SrsTsStream as)
356369
{
357370
srs_error_t err = srs_success;
358-
359-
if (vs != SrsTsStreamVideoH264 && as != SrsTsStreamAudioAAC && as != SrsTsStreamAudioMp3) {
371+
372+
bool codec_ok = (vs == SrsTsStreamVideoH264 || as == SrsTsStreamAudioAAC || as == SrsTsStreamAudioMp3);
373+
#ifdef SRS_H265
374+
codec_ok = codec_ok ? : (vs == SrsTsStreamVideoHEVC);
375+
#endif
376+
if (!codec_ok) {
360377
return srs_error_new(ERROR_HLS_NO_STREAM, "ts: no PID, vs=%d, as=%d", vs, as);
361378
}
362379

@@ -425,8 +442,12 @@ srs_error_t SrsTsContext::encode_pes(ISrsStreamWriter* writer, SrsTsMessage* msg
425442
if (msg->payload->length() == 0) {
426443
return err;
427444
}
428-
429-
if (sid != SrsTsStreamVideoH264 && sid != SrsTsStreamAudioMp3 && sid != SrsTsStreamAudioAAC) {
445+
446+
bool codec_ok = (sid == SrsTsStreamVideoH264 || sid == SrsTsStreamAudioAAC || sid == SrsTsStreamAudioMp3);
447+
#ifdef SRS_H265
448+
codec_ok = codec_ok ? : (sid == SrsTsStreamVideoHEVC);
449+
#endif
450+
if (!codec_ok) {
430451
srs_info("ts: ignore the unknown stream, sid=%d", sid);
431452
return err;
432453
}
@@ -750,10 +771,14 @@ SrsTsPacket* SrsTsPacket::create_pmt(SrsTsContext* context,
750771
pmt->current_next_indicator = 1;
751772
pmt->section_number = 0;
752773
pmt->last_section_number = 0;
753-
754-
// must got one valid codec.
755-
srs_assert(vs == SrsTsStreamVideoH264 || as == SrsTsStreamAudioAAC || as == SrsTsStreamAudioMp3);
756-
774+
775+
// Here we must get the correct codec.
776+
bool codec_ok = (vs == SrsTsStreamVideoH264 || as == SrsTsStreamAudioAAC || as == SrsTsStreamAudioMp3);
777+
#ifdef SRS_H265
778+
codec_ok = codec_ok ? : (vs == SrsTsStreamVideoHEVC);
779+
#endif
780+
srs_assert(codec_ok);
781+
757782
// if mp3 or aac specified, use audio to carry pcr.
758783
if (as == SrsTsStreamAudioAAC || as == SrsTsStreamAudioMp3) {
759784
// use audio to carray pcr by default.
@@ -762,8 +787,12 @@ SrsTsPacket* SrsTsPacket::create_pmt(SrsTsContext* context,
762787
pmt->infos.push_back(new SrsTsPayloadPMTESInfo(as, apid));
763788
}
764789

765-
// if h.264 specified, use video to carry pcr.
766-
if (vs == SrsTsStreamVideoH264) {
790+
// If h.264/h.265 specified, use video to carry pcr.
791+
codec_ok = (vs == SrsTsStreamVideoH264);
792+
#ifdef SRS_H265
793+
codec_ok = codec_ok ? : (vs == SrsTsStreamVideoHEVC);
794+
#endif
795+
if (codec_ok) {
767796
pmt->PCR_PID = vpid;
768797
pmt->infos.push_back(new SrsTsPayloadPMTESInfo(vs, vpid));
769798
}
@@ -2533,6 +2562,9 @@ srs_error_t SrsTsPayloadPMT::psi_decode(SrsBuffer* stream)
25332562
// update the apply pid table
25342563
switch (info->stream_type) {
25352564
case SrsTsStreamVideoH264:
2565+
#ifdef SRS_H265
2566+
case SrsTsStreamVideoHEVC:
2567+
#endif
25362568
case SrsTsStreamVideoMpeg4:
25372569
packet->context->set(info->elementary_PID, SrsTsPidApplyVideo, info->stream_type);
25382570
break;
@@ -2616,6 +2648,9 @@ srs_error_t SrsTsPayloadPMT::psi_encode(SrsBuffer* stream)
26162648
// update the apply pid table
26172649
switch (info->stream_type) {
26182650
case SrsTsStreamVideoH264:
2651+
#ifdef SRS_H265
2652+
case SrsTsStreamVideoHEVC:
2653+
#endif
26192654
case SrsTsStreamVideoMpeg4:
26202655
packet->context->set(info->elementary_PID, SrsTsPidApplyVideo, info->stream_type);
26212656
break;
@@ -2685,6 +2720,11 @@ SrsVideoCodecId SrsTsContextWriter::video_codec()
26852720
return vcodec;
26862721
}
26872722

2723+
void SrsTsContextWriter::update_video_codec(SrsVideoCodecId v)
2724+
{
2725+
vcodec = v;
2726+
}
2727+
26882728
SrsEncFileWriter::SrsEncFileWriter()
26892729
{
26902730
memset(iv,0,16);
@@ -2832,8 +2872,17 @@ srs_error_t SrsTsMessageCache::cache_video(SrsVideoFrame* frame, int64_t dts)
28322872
video->dts = dts;
28332873
video->pts = video->dts + frame->cts * 90;
28342874
video->sid = SrsTsPESStreamIdVideoCommon;
2835-
2836-
// write video to cache.
2875+
2876+
// Write H.265 video frame to cache.
2877+
if (frame && frame->vcodec()->id == SrsVideoCodecIdHEVC) {
2878+
#ifdef SRS_H265
2879+
return do_cache_hevc(frame);
2880+
#else
2881+
return srs_error_new(ERROR_HEVC_DISABLED, "H.265 is disabled");
2882+
#endif
2883+
}
2884+
2885+
// Write H.264 video frame to cache.
28372886
if ((err = do_cache_avc(frame)) != srs_success) {
28382887
return srs_error_wrap(err, "ts: cache avc");
28392888
}
@@ -2924,7 +2973,7 @@ srs_error_t SrsTsMessageCache::do_cache_aac(SrsAudioFrame* frame)
29242973
return err;
29252974
}
29262975

2927-
void srs_avc_insert_aud(SrsSimpleStream* payload, bool& aud_inserted)
2976+
void srs_avc_insert_aud(SrsSimpleStream* payload, bool aud_inserted)
29282977
{
29292978
// mux the samples in annexb format,
29302979
// ISO_IEC_14496-10-AVC-2012.pdf, page 324.
@@ -3064,6 +3113,52 @@ srs_error_t SrsTsMessageCache::do_cache_avc(SrsVideoFrame* frame)
30643113
return err;
30653114
}
30663115

3116+
#ifdef SRS_H265
3117+
srs_error_t SrsTsMessageCache::do_cache_hevc(SrsVideoFrame* frame)
3118+
{
3119+
srs_error_t err = srs_success;
3120+
3121+
// Whether aud inserted.
3122+
bool aud_inserted = false;
3123+
3124+
SrsVideoCodecConfig* codec = frame->vcodec();
3125+
srs_assert(codec);
3126+
3127+
bool is_sps_pps_appended = false;
3128+
3129+
// all sample use cont nalu header, except the sps-pps before IDR frame.
3130+
for (int i = 0; i < frame->nb_samples; i++) {
3131+
SrsSample* sample = &frame->samples[i];
3132+
int32_t size = sample->size;
3133+
3134+
if (!sample->bytes || size <= 0) {
3135+
return srs_error_new(ERROR_HLS_AVC_SAMPLE_SIZE, "ts: invalid avc sample length=%d", size);
3136+
}
3137+
3138+
// Insert aud before NALU for HEVC.
3139+
SrsHevcNaluType nalu_type = (SrsHevcNaluType)SrsHevcNaluTypeParse(sample->bytes[0]);
3140+
bool is_idr = (SrsHevcNaluType_CODED_SLICE_BLA <= nalu_type) && (nalu_type <= SrsHevcNaluType_RESERVED_23);
3141+
if (is_idr && !frame->has_sps_pps && !is_sps_pps_appended) {
3142+
for (size_t i = 0; i < codec->hevc_dec_conf_record_.nalu_vec.size(); i++) {
3143+
const SrsHevcHvccNalu& nalu = codec->hevc_dec_conf_record_.nalu_vec[i];
3144+
if (nalu.num_nalus <= 0 || nalu.nal_data_vec.empty()) continue;
3145+
3146+
srs_avc_insert_aud(video->payload, aud_inserted);
3147+
const SrsHevcNalData& data = nalu.nal_data_vec.at(0);
3148+
video->payload->append((char*)&data.nal_unit_data[0], (int)data.nal_unit_data.size());
3149+
is_sps_pps_appended = true;
3150+
}
3151+
}
3152+
3153+
// Insert the NALU to video in annexb.
3154+
srs_avc_insert_aud(video->payload, aud_inserted);
3155+
video->payload->append(sample->bytes, sample->size);
3156+
}
3157+
3158+
return err;
3159+
}
3160+
#endif
3161+
30673162
SrsTsTransmuxer::SrsTsTransmuxer()
30683163
{
30693164
writer = NULL;
@@ -3158,10 +3253,17 @@ srs_error_t SrsTsTransmuxer::write_video(int64_t timestamp, char* data, int size
31583253
if (format->video->frame_type == SrsVideoAvcFrameTypeVideoInfoFrame) {
31593254
return err;
31603255
}
3161-
3162-
if (format->vcodec->id != SrsVideoCodecIdAVC) {
3256+
3257+
bool codec_ok = (format->vcodec->id != SrsVideoCodecIdAVC);
3258+
#ifdef SRS_H265
3259+
codec_ok = codec_ok ? : (format->vcodec->id != SrsVideoCodecIdHEVC);
3260+
#endif
3261+
if (!codec_ok) {
31633262
return err;
31643263
}
3264+
3265+
// The video codec might change during streaming.
3266+
tscw->update_video_codec(format->vcodec->id);
31653267

31663268
// ignore sequence header
31673269
if (format->video->frame_type == SrsVideoAvcFrameTypeKeyFrame && format->video->avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader) {

0 commit comments

Comments
 (0)