Skip to content

Commit

Permalink
RTMP: Support enhanced RTMP specification for HEVC. v6.0.42 (#3495)
Browse files Browse the repository at this point in the history
* RTMP: Support enhanced RTMP specification for HEVC,  v6.0.42.
* Player: Upgrade mpegts.js to support it.

Enhanced RTMP specification: https://github.com/veovera/enhanced-rtmp

First, start SRS `v6.0.42+` with HTTP-TS support:

```bash
./objs/srs -c conf/http.ts.live.conf
```

Then, you can use [OBS 29.1+](https://github.com/obsproject/obs-studio/releases) to push HEVC via RTMP.
Start OBS with the following settings in the `Settings > Stream` tab:

* Server: `rtmp://localhost/live`
* Stream Key: `livestream`
* Encoder: Please select the HEVC hardware encoder.

Finally, open the player http://localhost:8080/players/srs_player.html?stream=livestream.ts

Or use VLS or ffplay to play `http://localhost:8080/live/livestream.ts`

---------

Co-authored-by: chundonglinlin <chundonglinlin@163.com>
  • Loading branch information
winlinvip and chundonglinlin committed Apr 8, 2023
1 parent dcd02fe commit 26aabe4
Show file tree
Hide file tree
Showing 10 changed files with 351 additions and 78 deletions.
5 changes: 3 additions & 2 deletions trunk/doc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ The changelog for SRS.

## SRS 6.0 Changelog

* v5.0, 2023-04-01, Merge [#3392](https://github.com/ossrs/srs/pull/3392): Support composited bridges for 1:N protocols converting. v6.0.41 (#3392)
* v5.0, 2023-04-01, Merge [#3458](https://github.com/ossrs/srs/pull/3450): API: Support HTTP basic authentication for API. v6.0.40 (#3458)
* v6.0, 2023-04-08, Merge [#3495](https://github.com/ossrs/srs/pull/3495): RTMP: Support enhanced RTMP specification for HEVC. v6.0.42 (#3495)
* v6.0, 2023-04-01, Merge [#3392](https://github.com/ossrs/srs/pull/3392): Support composited bridges for 1:N protocols converting. v6.0.41 (#3392)
* v6.0, 2023-04-01, Merge [#3458](https://github.com/ossrs/srs/pull/3450): API: Support HTTP basic authentication for API. v6.0.40 (#3458)
* v6.0, 2023-03-27, Merge [#3450](https://github.com/ossrs/srs/pull/3450): WebRTC: Error message carries the SDP when failed. v6.0.39 (#3450)
* v6.0, 2023-03-25, Merge [#3477](https://github.com/ossrs/srs/pull/3477): Remove unnecessary NULL check in srs_freep. v6.0.38 (#3477)
* v6.0, 2023-03-25, Merge [#3455](https://github.com/ossrs/srs/pull/3455): RTC: Call on_play before create session, for it might be freed for timeout. v6.0.37 (#3455)
Expand Down
9 changes: 0 additions & 9 deletions trunk/research/players/js/mpegts-1.7.2.min.js

This file was deleted.

9 changes: 9 additions & 0 deletions trunk/research/players/js/mpegts-1.7.3.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion trunk/research/players/js/mpegts.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion trunk/research/players/srs_player.html
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
</body>
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
<script type="text/javascript" src="js/mpegts-1.7.2.min.js"></script>
<script type="text/javascript" src="js/mpegts-1.7.3.min.js"></script>
<script type="text/javascript" src="js/hls-0.14.17.min.js"></script>
<script type="text/javascript" src="js/dash-v4.5.1.all.min.js"></script>
<script type="text/javascript" src="js/json2.js"></script>
Expand Down
2 changes: 1 addition & 1 deletion trunk/src/core/srs_core_version6.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@

#define VERSION_MAJOR 6
#define VERSION_MINOR 0
#define VERSION_REVISION 41
#define VERSION_REVISION 42

#endif
192 changes: 132 additions & 60 deletions trunk/src/kernel/srs_kernel_codec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,10 @@ bool SrsFlvVideo::keyframe(char* data, int size)
if (size < 1) {
return false;
}

char frame_type = data[0];

// See rtmp_specification_1.0.pdf
// See https://github.com/veovera/enhanced-rtmp
uint8_t frame_type = data[0] & 0x7f;
frame_type = (frame_type >> 4) & 0x0F;

return frame_type == SrsVideoAvcFrameTypeKeyFrame;
Expand All @@ -173,14 +175,23 @@ bool SrsFlvVideo::sh(char* data, int size)
if (size < 2) {
return false;
}

char frame_type = data[0];
frame_type = (frame_type >> 4) & 0x0F;

char avc_packet_type = data[1];


uint8_t frame_type = data[0];
bool is_ext_header = frame_type & 0x80;
SrsVideoAvcFrameTrait avc_packet_type = SrsVideoAvcFrameTraitForbidden;
if (!is_ext_header) {
// See rtmp_specification_1.0.pdf
frame_type = (frame_type >> 4) & 0x0F;
avc_packet_type = (SrsVideoAvcFrameTrait)data[1];
} else {
// See https://github.com/veovera/enhanced-rtmp
avc_packet_type = (SrsVideoAvcFrameTrait)(frame_type & 0x0f);
frame_type = (frame_type >> 4) & 0x07;
}

// Note that SrsVideoHEVCFrameTraitPacketTypeSequenceStart is equal to SrsVideoAvcFrameTraitSequenceHeader
return frame_type == SrsVideoAvcFrameTypeKeyFrame
&& avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader;
&& avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader;
}

bool SrsFlvVideo::h264(char* data, int size)
Expand All @@ -204,8 +215,24 @@ bool SrsFlvVideo::hevc(char* data, int size)
return false;
}

char codec_id = data[0];
codec_id = codec_id & 0x0F;
uint8_t frame_type = data[0];
bool is_ext_header = frame_type & 0x80;
SrsVideoCodecId codec_id = SrsVideoCodecIdForbidden;
if (!is_ext_header) {
// See rtmp_specification_1.0.pdf
codec_id = (SrsVideoCodecId)(frame_type & 0x0F);
} else {
// See https://github.com/veovera/enhanced-rtmp
if (size < 5) {
return false;
}

// Video FourCC
if (data[1] != 'h' || data[2] != 'v' || data[3] != 'c' || data[4] != '1') {
return false;
}
codec_id = SrsVideoCodecIdHEVC;
}

return codec_id == SrsVideoCodecIdHEVC;
}
Expand All @@ -218,12 +245,34 @@ bool SrsFlvVideo::acceptable(char* data, int size)
return false;
}

char frame_type = data[0];
SrsVideoCodecId codec_id = (SrsVideoCodecId)(uint8_t)(frame_type & 0x0f);
frame_type = (frame_type >> 4) & 0x0f;

if (frame_type < 1 || frame_type > 5) {
return false;
uint8_t frame_type = data[0];
bool is_ext_header = frame_type & 0x80;
SrsVideoCodecId codec_id = SrsVideoCodecIdForbidden;
if (!is_ext_header) {
// See rtmp_specification_1.0.pdf
codec_id = (SrsVideoCodecId)(frame_type & 0x0f);
frame_type = (frame_type >> 4) & 0x0f;

if (frame_type < 1 || frame_type > 5) {
return false;
}
} else {
// See https://github.com/veovera/enhanced-rtmp
uint8_t packet_type = frame_type & 0x0f;
frame_type = (frame_type >> 4) & 0x07;

if (packet_type > SrsVideoHEVCFrameTraitPacketTypeMPEG2TSSequenceStart || frame_type > SrsVideoAvcFrameTypeVideoInfoFrame) {
return false;
}

if (size < 5) {
return false;
}

if (data[1] != 'h' || data[2] != 'v' || data[3] != 'c' || data[4] != '1') {
return false;
}
codec_id = SrsVideoCodecIdHEVC;
}

if (codec_id != SrsVideoCodecIdAVC && codec_id != SrsVideoCodecIdAV1 && codec_id != SrsVideoCodecIdHEVC) {
Expand Down Expand Up @@ -775,33 +824,7 @@ srs_error_t SrsFormat::on_video(int64_t timestamp, char* data, int size)

SrsBuffer* buffer = new SrsBuffer(data, size);
SrsAutoFree(SrsBuffer, buffer);

// We already checked the size is positive and data is not NULL.
srs_assert(buffer->require(1));

// @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);

// Check codec for H.264 and H.265.
bool codec_ok = (codec_id == SrsVideoCodecIdAVC);
#ifdef SRS_H265
codec_ok = codec_ok ? true : (codec_id == SrsVideoCodecIdHEVC);
#endif
if (!codec_ok) return err;

if (!vcodec) {
vcodec = new SrsVideoCodecConfig();
}
if (!video) {
video = new SrsVideoFrame();
}

if ((err = video->initialize(vcodec)) != srs_success) {
return srs_error_wrap(err, "init video");
}

buffer->skip(-1 * buffer->pos());
return video_avc_demux(buffer, timestamp);
}

Expand Down Expand Up @@ -847,18 +870,55 @@ bool SrsFormat::is_avc_sequence_header()
srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
{
srs_error_t err = srs_success;


if (!stream->require(1)) {
return srs_error_new(ERROR_HLS_DECODE_ERROR, "video avc demux shall atleast 1bytes");
}

// Parse the frame type and the first bit indicates the ext header.
uint8_t frame_type = stream->read_1bytes();
bool is_ext_header = frame_type & 0x80;

// @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
int8_t frame_type = stream->read_1bytes();
SrsVideoCodecId codec_id = (SrsVideoCodecId)(frame_type & 0x0f);
frame_type = (frame_type >> 4) & 0x0f;

SrsVideoCodecId codec_id = SrsVideoCodecIdForbidden;
SrsVideoAvcFrameTrait packet_type = SrsVideoAvcFrameTraitForbidden;
if (!is_ext_header) {
// See rtmp_specification_1.0.pdf
codec_id = (SrsVideoCodecId)(frame_type & 0x0f);
frame_type = (frame_type >> 4) & 0x0f;
} else {
// See https://github.com/veovera/enhanced-rtmp
packet_type = (SrsVideoAvcFrameTrait)(frame_type & 0x0f);
frame_type = (frame_type >> 4) & 0x07;

if (!stream->require(4)) {
return srs_error_new(ERROR_HLS_DECODE_ERROR, "fourCC requires 4bytes, only %dbytes", stream->left());
}

uint32_t four_cc = stream->read_4bytes();
if (four_cc == 0x68766331) { // 'hvc1'=0x68766331
codec_id = SrsVideoCodecIdHEVC;
}
}

if (!vcodec) {
vcodec = new SrsVideoCodecConfig();
}

if (!video) {
video = new SrsVideoFrame();
}

if ((err = video->initialize(vcodec)) != srs_success) {
return srs_error_wrap(err, "init video");
}

video->frame_type = (SrsVideoAvcFrameType)frame_type;

// ignore info frame without error,
// @see https://github.com/ossrs/srs/issues/288#issuecomment-69863909
if (video->frame_type == SrsVideoAvcFrameTypeVideoInfoFrame) {
srs_warn("avc igone the info frame");
srs_warn("avc ignore the info frame");
return err;
}

Expand All @@ -871,17 +931,29 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
return srs_error_new(ERROR_HLS_DECODE_ERROR, "only support video H.264/H.265, actual=%d", codec_id);
}
vcodec->id = codec_id;

if (!stream->require(4)) {
return srs_error_new(ERROR_HLS_DECODE_ERROR, "avc decode avc_packet_type");

int32_t composition_time = 0;
if (!is_ext_header) {
// See rtmp_specification_1.0.pdf
if (!stream->require(4)) {
return srs_error_new(ERROR_HLS_DECODE_ERROR, "requires 4bytes, only %dbytes", stream->left());
}
packet_type = (SrsVideoAvcFrameTrait)stream->read_1bytes();
composition_time = stream->read_3bytes();
} else {
// See https://github.com/veovera/enhanced-rtmp
if (packet_type == SrsVideoHEVCFrameTraitPacketTypeCodedFrames) {
if (!stream->require(3)) {
return srs_error_new(ERROR_HLS_DECODE_ERROR, "requires 3 bytes, only %dbytes", stream->left());
}
composition_time = stream->read_3bytes();
}
}
int8_t avc_packet_type = stream->read_1bytes();
int32_t composition_time = stream->read_3bytes();


// pts = dts + cts.
video->dts = timestamp;
video->cts = composition_time;
video->avc_packet_type = (SrsVideoAvcFrameTrait)avc_packet_type;
video->avc_packet_type = packet_type;

// Update the RAW AVC data.
raw = stream->data() + stream->pos();
Expand All @@ -890,12 +962,12 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
// Parse sequence header for H.265/HEVC.
if (codec_id == SrsVideoCodecIdHEVC) {
#ifdef SRS_H265
if (avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader) {
if (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 VPS/SPS/PPS");
}
} else if (avc_packet_type == SrsVideoAvcFrameTraitNALU) {
} else if (packet_type == SrsVideoAvcFrameTraitNALU || packet_type == SrsVideoHEVCFrameTraitPacketTypeCodedFramesX) {
// TODO: demux nalu for hevc
if ((err = video_nalu_demux(stream)) != srs_success) {
return srs_error_wrap(err, "demux hevc NALU");
Expand All @@ -908,12 +980,12 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
}

// Parse sequence header for H.264/AVC.
if (avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader) {
if (packet_type == SrsVideoAvcFrameTraitSequenceHeader) {
// TODO: FIXME: Maybe we should ignore any error for parsing sps/pps.
if ((err = avc_demux_sps_pps(stream)) != srs_success) {
return srs_error_wrap(err, "demux SPS/PPS");
}
} else if (avc_packet_type == SrsVideoAvcFrameTraitNALU){
} else if (packet_type == SrsVideoAvcFrameTraitNALU){
if ((err = video_nalu_demux(stream)) != srs_success) {
return srs_error_wrap(err, "demux NALU");
}
Expand Down
24 changes: 22 additions & 2 deletions trunk/src/kernel/srs_kernel_codec.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,32 @@ std::string srs_video_codec_id2str(SrsVideoCodecId codec);
enum SrsVideoAvcFrameTrait
{
// set to the max value to reserved, for array map.
SrsVideoAvcFrameTraitReserved = 3,
SrsVideoAvcFrameTraitForbidden = 3,
SrsVideoAvcFrameTraitReserved = 6,
SrsVideoAvcFrameTraitForbidden = 6,

SrsVideoAvcFrameTraitSequenceHeader = 0,
SrsVideoAvcFrameTraitNALU = 1,
SrsVideoAvcFrameTraitSequenceHeaderEOF = 2,

SrsVideoHEVCFrameTraitPacketTypeSequenceStart = 0,
SrsVideoHEVCFrameTraitPacketTypeCodedFrames = 1,
SrsVideoHEVCFrameTraitPacketTypeSequenceEnd = 2,
// CompositionTime Offset is implied to equal zero. This is
// an optimization to save putting SI24 composition time value of zero on
// the wire. See pseudo code below in the VideoTagBody section
SrsVideoHEVCFrameTraitPacketTypeCodedFramesX = 3,
// VideoTagBody does not contain video data. VideoTagBody
// instead contains an AMF encoded metadata. See Metadata Frame
// section for an illustration of its usage. As an example, the metadata
// can be HDR information. This is a good way to signal HDR
// information. This also opens up future ways to express additional
// metadata that is meant for the next video sequence.
//
// note: presence of PacketTypeMetadata means that FrameType
// flags at the top of this table should be ignored
SrsVideoHEVCFrameTraitPacketTypeMetadata = 4,
// Carriage of bitstream in MPEG-2 TS format
SrsVideoHEVCFrameTraitPacketTypeMPEG2TSSequenceStart = 5,
};

/**
Expand Down
Loading

0 comments on commit 26aabe4

Please sign in to comment.