mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
RTMP: Support enhanced RTMP specification for HEVC. v6.0.42 (#3495)
* 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>
This commit is contained in:
parent
dcd02fe69c
commit
26aabe413d
10 changed files with 351 additions and 78 deletions
|
@ -8,8 +8,9 @@ The changelog for SRS.
|
||||||
|
|
||||||
## SRS 6.0 Changelog
|
## 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)
|
* 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)
|
||||||
* 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-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-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 [#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)
|
* 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)
|
||||||
|
|
File diff suppressed because one or more lines are too long
9
trunk/research/players/js/mpegts-1.7.3.min.js
vendored
Normal file
9
trunk/research/players/js/mpegts-1.7.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -102,7 +102,7 @@
|
||||||
</body>
|
</body>
|
||||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
<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/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/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/dash-v4.5.1.all.min.js"></script>
|
||||||
<script type="text/javascript" src="js/json2.js"></script>
|
<script type="text/javascript" src="js/json2.js"></script>
|
||||||
|
|
|
@ -9,6 +9,6 @@
|
||||||
|
|
||||||
#define VERSION_MAJOR 6
|
#define VERSION_MAJOR 6
|
||||||
#define VERSION_MINOR 0
|
#define VERSION_MINOR 0
|
||||||
#define VERSION_REVISION 41
|
#define VERSION_REVISION 42
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -154,7 +154,9 @@ bool SrsFlvVideo::keyframe(char* data, int size)
|
||||||
return false;
|
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;
|
frame_type = (frame_type >> 4) & 0x0F;
|
||||||
|
|
||||||
return frame_type == SrsVideoAvcFrameTypeKeyFrame;
|
return frame_type == SrsVideoAvcFrameTypeKeyFrame;
|
||||||
|
@ -174,11 +176,20 @@ bool SrsFlvVideo::sh(char* data, int size)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char frame_type = data[0];
|
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;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
char avc_packet_type = data[1];
|
// Note that SrsVideoHEVCFrameTraitPacketTypeSequenceStart is equal to SrsVideoAvcFrameTraitSequenceHeader
|
||||||
|
|
||||||
return frame_type == SrsVideoAvcFrameTypeKeyFrame
|
return frame_type == SrsVideoAvcFrameTypeKeyFrame
|
||||||
&& avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader;
|
&& avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader;
|
||||||
}
|
}
|
||||||
|
@ -204,8 +215,24 @@ bool SrsFlvVideo::hevc(char* data, int size)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char codec_id = data[0];
|
uint8_t frame_type = data[0];
|
||||||
codec_id = codec_id & 0x0F;
|
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;
|
return codec_id == SrsVideoCodecIdHEVC;
|
||||||
}
|
}
|
||||||
|
@ -218,13 +245,35 @@ bool SrsFlvVideo::acceptable(char* data, int size)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char frame_type = data[0];
|
uint8_t frame_type = data[0];
|
||||||
SrsVideoCodecId codec_id = (SrsVideoCodecId)(uint8_t)(frame_type & 0x0f);
|
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;
|
frame_type = (frame_type >> 4) & 0x0f;
|
||||||
|
|
||||||
if (frame_type < 1 || frame_type > 5) {
|
if (frame_type < 1 || frame_type > 5) {
|
||||||
return false;
|
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) {
|
if (codec_id != SrsVideoCodecIdAVC && codec_id != SrsVideoCodecIdAV1 && codec_id != SrsVideoCodecIdHEVC) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -776,32 +825,6 @@ srs_error_t SrsFormat::on_video(int64_t timestamp, char* data, int size)
|
||||||
SrsBuffer* buffer = new SrsBuffer(data, size);
|
SrsBuffer* buffer = new SrsBuffer(data, size);
|
||||||
SrsAutoFree(SrsBuffer, buffer);
|
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);
|
return video_avc_demux(buffer, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -848,17 +871,54 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
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
|
// @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 = SrsVideoCodecIdForbidden;
|
||||||
SrsVideoCodecId codec_id = (SrsVideoCodecId)(frame_type & 0x0f);
|
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;
|
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;
|
video->frame_type = (SrsVideoAvcFrameType)frame_type;
|
||||||
|
|
||||||
// ignore info frame without error,
|
// ignore info frame without error,
|
||||||
// @see https://github.com/ossrs/srs/issues/288#issuecomment-69863909
|
// @see https://github.com/ossrs/srs/issues/288#issuecomment-69863909
|
||||||
if (video->frame_type == SrsVideoAvcFrameTypeVideoInfoFrame) {
|
if (video->frame_type == SrsVideoAvcFrameTypeVideoInfoFrame) {
|
||||||
srs_warn("avc igone the info frame");
|
srs_warn("avc ignore the info frame");
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -872,16 +932,28 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
|
||||||
}
|
}
|
||||||
vcodec->id = codec_id;
|
vcodec->id = codec_id;
|
||||||
|
|
||||||
|
int32_t composition_time = 0;
|
||||||
|
if (!is_ext_header) {
|
||||||
|
// See rtmp_specification_1.0.pdf
|
||||||
if (!stream->require(4)) {
|
if (!stream->require(4)) {
|
||||||
return srs_error_new(ERROR_HLS_DECODE_ERROR, "avc decode avc_packet_type");
|
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.
|
// pts = dts + cts.
|
||||||
video->dts = timestamp;
|
video->dts = timestamp;
|
||||||
video->cts = composition_time;
|
video->cts = composition_time;
|
||||||
video->avc_packet_type = (SrsVideoAvcFrameTrait)avc_packet_type;
|
video->avc_packet_type = packet_type;
|
||||||
|
|
||||||
// Update the RAW AVC data.
|
// Update the RAW AVC data.
|
||||||
raw = stream->data() + stream->pos();
|
raw = stream->data() + stream->pos();
|
||||||
|
@ -890,12 +962,12 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
|
||||||
// Parse sequence header for H.265/HEVC.
|
// Parse sequence header for H.265/HEVC.
|
||||||
if (codec_id == SrsVideoCodecIdHEVC) {
|
if (codec_id == SrsVideoCodecIdHEVC) {
|
||||||
#ifdef SRS_H265
|
#ifdef SRS_H265
|
||||||
if (avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader) {
|
if (packet_type == SrsVideoAvcFrameTraitSequenceHeader) {
|
||||||
// TODO: demux vps/sps/pps for hevc
|
// TODO: demux vps/sps/pps for hevc
|
||||||
if ((err = hevc_demux_hvcc(stream)) != srs_success) {
|
if ((err = hevc_demux_hvcc(stream)) != srs_success) {
|
||||||
return srs_error_wrap(err, "demux hevc VPS/SPS/PPS");
|
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
|
// TODO: demux nalu for hevc
|
||||||
if ((err = video_nalu_demux(stream)) != srs_success) {
|
if ((err = video_nalu_demux(stream)) != srs_success) {
|
||||||
return srs_error_wrap(err, "demux hevc NALU");
|
return srs_error_wrap(err, "demux hevc NALU");
|
||||||
|
@ -908,12 +980,12 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse sequence header for H.264/AVC.
|
// 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.
|
// TODO: FIXME: Maybe we should ignore any error for parsing sps/pps.
|
||||||
if ((err = avc_demux_sps_pps(stream)) != srs_success) {
|
if ((err = avc_demux_sps_pps(stream)) != srs_success) {
|
||||||
return srs_error_wrap(err, "demux SPS/PPS");
|
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) {
|
if ((err = video_nalu_demux(stream)) != srs_success) {
|
||||||
return srs_error_wrap(err, "demux NALU");
|
return srs_error_wrap(err, "demux NALU");
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,12 +64,32 @@ std::string srs_video_codec_id2str(SrsVideoCodecId codec);
|
||||||
enum SrsVideoAvcFrameTrait
|
enum SrsVideoAvcFrameTrait
|
||||||
{
|
{
|
||||||
// set to the max value to reserved, for array map.
|
// set to the max value to reserved, for array map.
|
||||||
SrsVideoAvcFrameTraitReserved = 3,
|
SrsVideoAvcFrameTraitReserved = 6,
|
||||||
SrsVideoAvcFrameTraitForbidden = 3,
|
SrsVideoAvcFrameTraitForbidden = 6,
|
||||||
|
|
||||||
SrsVideoAvcFrameTraitSequenceHeader = 0,
|
SrsVideoAvcFrameTraitSequenceHeader = 0,
|
||||||
SrsVideoAvcFrameTraitNALU = 1,
|
SrsVideoAvcFrameTraitNALU = 1,
|
||||||
SrsVideoAvcFrameTraitSequenceHeaderEOF = 2,
|
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,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -3897,7 +3897,8 @@ VOID TEST(KernelCodecTest, VideoFormat)
|
||||||
|
|
||||||
HELPER_EXPECT_SUCCESS(f.on_video(0, NULL, 0));
|
HELPER_EXPECT_SUCCESS(f.on_video(0, NULL, 0));
|
||||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x00", 0));
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x00", 0));
|
||||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x00", 1));
|
HELPER_EXPECT_FAILED(f.on_video(0, (char*)"\x00", 1));
|
||||||
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x57", 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (true) {
|
if (true) {
|
||||||
|
@ -4001,7 +4002,16 @@ VOID TEST(KernelCodecTest, HevcVideoFormat)
|
||||||
|
|
||||||
HELPER_EXPECT_SUCCESS(f.on_video(0, NULL, 0));
|
HELPER_EXPECT_SUCCESS(f.on_video(0, NULL, 0));
|
||||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x00", 0));
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x00", 0));
|
||||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x00", 1));
|
HELPER_EXPECT_FAILED(f.on_video(0, (char*)"\x00", 1));
|
||||||
|
|
||||||
|
// enhanced rtmp/flv
|
||||||
|
HELPER_EXPECT_SUCCESS(f.on_video(0, NULL, 0));
|
||||||
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x80", 0));
|
||||||
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x90", 0));
|
||||||
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\xd0\x68\x76\x63\x31", 5));
|
||||||
|
HELPER_EXPECT_FAILED(f.on_video(0, (char*)"\x80", 1));
|
||||||
|
HELPER_EXPECT_FAILED(f.on_video(0, (char*)"\x90", 1));
|
||||||
|
HELPER_EXPECT_FAILED(f.on_video(0, (char*)"\x90\x68\x76\x63\x31", 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (true) {
|
if (true) {
|
||||||
|
@ -4010,11 +4020,22 @@ VOID TEST(KernelCodecTest, HevcVideoFormat)
|
||||||
|
|
||||||
//HEVC: 0x5c
|
//HEVC: 0x5c
|
||||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x5c", 1));
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x5c", 1));
|
||||||
|
HELPER_EXPECT_FAILED(f.on_video(0, (char*)"\x1c", 1));
|
||||||
|
|
||||||
// CodecId: 0x00
|
// CodecId: 0x00
|
||||||
SrsBuffer b((char*)"\x00", 1);
|
SrsBuffer b((char*)"\x00", 1);
|
||||||
srs_error_t err = f.video_avc_demux(&b, 0);
|
srs_error_t err = f.video_avc_demux(&b, 0);
|
||||||
HELPER_EXPECT_FAILED(err);
|
HELPER_EXPECT_FAILED(err);
|
||||||
|
|
||||||
|
// enhanced rtmp/flv
|
||||||
|
SrsBuffer b1((char*)"\x80", 1);
|
||||||
|
HELPER_EXPECT_FAILED(f.video_avc_demux(&b1, 0));
|
||||||
|
SrsBuffer b2((char*)"\x90", 1);
|
||||||
|
HELPER_EXPECT_FAILED(f.video_avc_demux(&b2, 0));
|
||||||
|
SrsBuffer b3((char*)"\x90\x68\x76\x63\x31", 5);
|
||||||
|
HELPER_EXPECT_FAILED(f.video_avc_demux(&b3, 0));
|
||||||
|
SrsBuffer b4((char*)"\xd0\x68\x76\x63\x31", 5);
|
||||||
|
HELPER_EXPECT_SUCCESS(f.video_avc_demux(&b4, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t vps_sps_pps[] = {
|
uint8_t vps_sps_pps[] = {
|
||||||
|
@ -4075,6 +4096,93 @@ VOID TEST(KernelCodecTest, HevcVideoFormat)
|
||||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)rawIBMF, sizeof(rawIBMF)));
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)rawIBMF, sizeof(rawIBMF)));
|
||||||
EXPECT_EQ(1, f.video->nb_samples);
|
EXPECT_EQ(1, f.video->nb_samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// enhanced rtmp
|
||||||
|
uint8_t ext_vps_sps_pps[] = {
|
||||||
|
// IsExHeader | FrameType: UB[4]
|
||||||
|
// PacketType: UB[4]
|
||||||
|
0x90,
|
||||||
|
// Video FourCC
|
||||||
|
0x68, 0x76, 0x63, 0x31,
|
||||||
|
// SrsHevcDecoderConfigurationRecord
|
||||||
|
0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xf0, 0x00, 0xfc, 0xfd, 0xf8, 0xf8, 0x00, 0x00, 0x0f, 0x03,
|
||||||
|
// Nalus
|
||||||
|
// data_byte(1B)+num_nalus(2B)+nal_unit_length(2B)
|
||||||
|
0x20, 0x00, 0x01, 0x00, 0x18,
|
||||||
|
// VPS
|
||||||
|
0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x5d, 0x95, 0x98, 0x09,
|
||||||
|
// data_byte(1B)+num_nalus(2B)+nal_unit_length(2B)
|
||||||
|
0x21, 0x00, 0x01, 0x00, 0x28,
|
||||||
|
// SPS
|
||||||
|
0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x5d, 0xa0, 0x02, 0x80, 0x80, 0x2d, 0x16,
|
||||||
|
0x59, 0x59, 0xa4, 0x93, 0x2b, 0xc0, 0x40, 0x40, 0x00, 0x00, 0xfa, 0x40, 0x00, 0x17, 0x70, 0x02,
|
||||||
|
// data_byte(1B)+num_nalus(2B)+nal_unit_length(2B)
|
||||||
|
0x22, 0x00, 0x01, 0x00, 0x07,
|
||||||
|
// PPS
|
||||||
|
0x44, 0x01, 0xc1, 0x72, 0xb4, 0x62, 0x40
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t ext_rawIBMF[] = {
|
||||||
|
// IsExHeader | FrameType: UB[4]
|
||||||
|
// PacketType: UB[4]
|
||||||
|
0x93,
|
||||||
|
// Video FourCC
|
||||||
|
0x68, 0x76, 0x63, 0x31,
|
||||||
|
// HEVC NALU
|
||||||
|
0x00, 0x00, 0x00, 0x0b,
|
||||||
|
0x28, 0x1, 0xaf, 0x1d, 0x18, 0x38, 0xd4, 0x38, 0x32, 0xda, 0x23
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t ext_rawIBMF1[] = {
|
||||||
|
// IsExHeader | FrameType: UB[4]
|
||||||
|
// PacketType: UB[4]
|
||||||
|
0x91,
|
||||||
|
// Video FourCC
|
||||||
|
0x68, 0x76, 0x63, 0x31,
|
||||||
|
// CompositionTime Offset
|
||||||
|
0x00, 0x00, 0x7d,
|
||||||
|
// HEVC NALU
|
||||||
|
0x00, 0x00, 0x00, 0x0b,
|
||||||
|
0x28, 0x1, 0xaf, 0x1d, 0x18, 0x38, 0xd4, 0x38, 0x32, 0xda, 0x23
|
||||||
|
};
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
SrsFormat f;
|
||||||
|
HELPER_EXPECT_SUCCESS(f.initialize());
|
||||||
|
|
||||||
|
// firstly demux sequence header
|
||||||
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)ext_vps_sps_pps, sizeof(ext_vps_sps_pps)));
|
||||||
|
EXPECT_EQ(1, f.video->frame_type);
|
||||||
|
EXPECT_EQ(0, f.video->avc_packet_type);
|
||||||
|
EXPECT_EQ(3, f.vcodec->hevc_dec_conf_record_.nalu_vec.size());
|
||||||
|
EXPECT_EQ(1280, f.vcodec->width);
|
||||||
|
EXPECT_EQ(720, f.vcodec->height);
|
||||||
|
|
||||||
|
// secondly demux sequence header
|
||||||
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)ext_vps_sps_pps, sizeof(ext_vps_sps_pps)));
|
||||||
|
EXPECT_EQ(1, f.video->frame_type);
|
||||||
|
EXPECT_EQ(0, f.video->avc_packet_type);
|
||||||
|
EXPECT_EQ(3, f.vcodec->hevc_dec_conf_record_.nalu_vec.size());
|
||||||
|
EXPECT_EQ(1280, f.vcodec->width);
|
||||||
|
EXPECT_EQ(720, f.vcodec->height);
|
||||||
|
|
||||||
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)ext_rawIBMF, sizeof(ext_rawIBMF)));
|
||||||
|
EXPECT_EQ(1, f.video->frame_type);
|
||||||
|
EXPECT_EQ(3, f.video->avc_packet_type);
|
||||||
|
EXPECT_EQ(1, f.video->nb_samples);
|
||||||
|
|
||||||
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)ext_rawIBMF, sizeof(ext_rawIBMF)));
|
||||||
|
EXPECT_EQ(1, f.video->frame_type);
|
||||||
|
EXPECT_EQ(3, f.video->avc_packet_type);
|
||||||
|
EXPECT_EQ(1, f.video->nb_samples);
|
||||||
|
|
||||||
|
// check cts
|
||||||
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)ext_rawIBMF1, sizeof(ext_rawIBMF1)));
|
||||||
|
EXPECT_EQ(1, f.video->frame_type);
|
||||||
|
EXPECT_EQ(125, f.video->cts);
|
||||||
|
EXPECT_EQ(1, f.video->avc_packet_type);
|
||||||
|
EXPECT_EQ(1, f.video->nb_samples);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -411,3 +411,75 @@ VOID TEST(KernelFileWriterTest, RealfileTest)
|
||||||
EXPECT_STREQ("HelloWorld", str.substr(20).c_str());
|
EXPECT_STREQ("HelloWorld", str.substr(20).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VOID TEST(KernelRTMPExtTest, ExtRTMPTest)
|
||||||
|
{
|
||||||
|
srs_error_t err;
|
||||||
|
|
||||||
|
// For legacy RTMP specification, without ext tag header.
|
||||||
|
if (true) {
|
||||||
|
SrsFormat f;
|
||||||
|
HELPER_ASSERT_SUCCESS(f.initialize());
|
||||||
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*) "\x17\x01\x00\x00\x12", 5));
|
||||||
|
|
||||||
|
// Verify the frame type, codec id, avc packet type and composition time.
|
||||||
|
EXPECT_EQ(SrsVideoAvcFrameTypeKeyFrame, f.video->frame_type);
|
||||||
|
EXPECT_EQ(SrsVideoCodecIdAVC, f.vcodec->id);
|
||||||
|
EXPECT_EQ(SrsVideoAvcFrameTraitNALU, f.video->avc_packet_type);
|
||||||
|
EXPECT_EQ(0x12, f.video->cts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For new RTMP enhanced specification, with ext tag header.
|
||||||
|
if (true) {
|
||||||
|
SrsFormat f;
|
||||||
|
HELPER_ASSERT_SUCCESS(f.initialize());
|
||||||
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*) "\x91hvc1\x00\x00\x12", 8));
|
||||||
|
|
||||||
|
// Verify the frame type, codec id, avc packet type and composition time.
|
||||||
|
EXPECT_EQ(SrsVideoAvcFrameTypeKeyFrame, f.video->frame_type);
|
||||||
|
EXPECT_EQ(SrsVideoCodecIdHEVC, f.vcodec->id);
|
||||||
|
EXPECT_EQ(SrsVideoHEVCFrameTraitPacketTypeCodedFrames, f.video->avc_packet_type);
|
||||||
|
EXPECT_EQ(0x12, f.video->cts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If packet type is 3, which is coded frame X, the composition time is 0.
|
||||||
|
if (true) {
|
||||||
|
SrsFormat f;
|
||||||
|
HELPER_ASSERT_SUCCESS(f.initialize());
|
||||||
|
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*) "\x93hvc1", 5));
|
||||||
|
|
||||||
|
// Verify the frame type, codec id, avc packet type and composition time.
|
||||||
|
EXPECT_EQ(SrsVideoAvcFrameTypeKeyFrame, f.video->frame_type);
|
||||||
|
EXPECT_EQ(SrsVideoCodecIdHEVC, f.vcodec->id);
|
||||||
|
EXPECT_EQ(SrsVideoHEVCFrameTraitPacketTypeCodedFramesX, f.video->avc_packet_type);
|
||||||
|
EXPECT_EQ(0, f.video->cts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should fail if only 1 byte for ext tag header, should be more bytes for fourcc.
|
||||||
|
if (true) {
|
||||||
|
SrsFormat f;
|
||||||
|
HELPER_ASSERT_SUCCESS(f.initialize());
|
||||||
|
HELPER_EXPECT_FAILED(f.on_video(0, (char*) "\x91", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should fail if only 5 bytes for ext tag header, should be more bytes for fourcc.
|
||||||
|
if (true) {
|
||||||
|
SrsFormat f;
|
||||||
|
HELPER_ASSERT_SUCCESS(f.initialize());
|
||||||
|
HELPER_EXPECT_FAILED(f.on_video(0, (char*) "\x91hvc1", 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should fail if codec id is hvc2 for ext tag header, should be hvc1.
|
||||||
|
if (true) {
|
||||||
|
SrsFormat f;
|
||||||
|
HELPER_ASSERT_SUCCESS(f.initialize());
|
||||||
|
HELPER_EXPECT_FAILED(f.on_video(0, (char*) "\x93hvc2", 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should fail if codec id is mvc1 for ext tag header, should be hvc1.
|
||||||
|
if (true) {
|
||||||
|
SrsFormat f;
|
||||||
|
HELPER_ASSERT_SUCCESS(f.initialize());
|
||||||
|
HELPER_EXPECT_FAILED(f.on_video(0, (char*) "\x93mvc1", 5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue