Skip to content

Commit 70d5618

Browse files
winlinviprunner365
andauthored
H265: Support HEVC over HTTP-TS. v6.0.4 (#3275)
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. Co-authored-by: runner365 <[email protected]>
1 parent 8debbe6 commit 70d5618

10 files changed

+178
-33
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/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)