Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RTMP: Support enhanced RTMP specification for HEVC. v6.0.42 #3495

Merged
merged 9 commits into from
Apr 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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