SRT2RTMP: fix srt bridge hevc to rtmp error. v7.0.84 (#4446)

try to fix #4428.

## Cause

rtmp do not support hevc, rtmp enhanced do.

## How to reproduce

1. start srs.
   `./objs/srs -c conf/srt.conf`
2. publish hevc (h.265) stream to srs by srt.
`ffmpeg -re -i ./doc/source.flv -c:v libx265 -crf 28 -preset medium -c:a
copy -pes_payload_size 0 -f mpegts
'srt://127.0.0.1:10080?streamid=#!::r=live/livestream,m=publish'`
3. probe the rtmp stream
   `ffprobe rtmp://localhost/live/livestream`

## About the Failed BlackBox test
The failed blackbox test: `TestSlow_SrtPublish_RtmpPlay_HEVC_Basic`
`TestSlow_SrtPublish_HttpFlvPlay_HEVC_Basic`

### Cause: 

The ffmpeg 5 is used to record a piece of video (DRV), the ffmpeg will
transcode the enhanced flv format to TS format, but ffmpeg 5 don't
support enhanced rtmp (or flv) in this case.

The solution is to replace the ffmpeg to version 7 in those 2 test
cases.

### why not upgrade ffmpeg to version 7?

The black tests dependency on ffmpeg 5 will fail, and there are a few of
them are not easy to resolve in ffmpeg 7.

---------

Co-authored-by: winlin <winlinvip@gmail.com>
This commit is contained in:
Jacob Su 2025-09-10 09:10:04 +08:00 committed by GitHub
parent 3a29e5c550
commit a6d14eb09a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 32 additions and 20 deletions

View File

@ -709,6 +709,8 @@ func TestSlow_SrtPublish_RtmpPlay_HEVC_Basic(t *testing.T) {
v.dvrFile = path.Join(svr.WorkDir(), "objs", fmt.Sprintf("srs-ffprobe-%v.ts", streamID))
v.streamURL = fmt.Sprintf("rtmp://localhost:%v/live/%v", svr.RTMPPort(), streamID)
v.duration, v.timeout = duration, time.Duration(*srsFFprobeTimeout)*time.Millisecond
v.ffmpegCmdName = "ffmpeg7" // ffmpeg 5 don't support enhanced rtmp, so use ffmpeg 7 instead.
v.ffprobeCmdName = "ffprobe7"
})
wg.Add(1)
go func() {
@ -807,6 +809,8 @@ func TestSlow_SrtPublish_HttpFlvPlay_HEVC_Basic(t *testing.T) {
v.dvrFile = path.Join(svr.WorkDir(), "objs", fmt.Sprintf("srs-ffprobe-%v.ts", streamID))
v.streamURL = fmt.Sprintf("http://localhost:%v/live/%v.flv", svr.HTTPPort(), streamID)
v.duration, v.timeout = duration, time.Duration(*srsFFprobeTimeout)*time.Millisecond
v.ffmpegCmdName = "ffmpeg7" // ffmpeg 5 don't support enhanced rtmp, so use ffmpeg 7 instead.
v.ffprobeCmdName = "ffprobe7"
})
wg.Add(1)
go func() {

View File

@ -666,6 +666,8 @@ type ffmpegClient struct {
// The backend service process.
process *backendService
// FFmpeg cmd name
ffmpegCmdName string
// FFmpeg cli args, without ffmpeg binary.
args []string
// Let the process quit, do not cancel the case.
@ -678,6 +680,7 @@ func NewFFmpeg(opts ...func(v *ffmpegClient)) FFmpegClient {
v := &ffmpegClient{
process: newBackendService(),
cancelCaseWhenQuit: true,
ffmpegCmdName: *srsFFmpeg,
}
// Do cleanup.
@ -702,7 +705,7 @@ func (v *ffmpegClient) ReadyCtx() context.Context {
func (v *ffmpegClient) Run(ctx context.Context, cancel context.CancelFunc) error {
logger.Tf(ctx, "Starting FFmpeg by %v", strings.Join(v.args, " "))
v.process.name = *srsFFmpeg
v.process.name = v.ffmpegCmdName
v.process.args = v.args
v.process.env = os.Environ()
v.process.duration = v.ffmpegDuration
@ -746,6 +749,10 @@ type ffprobeClient struct {
// The timeout to wait for task to done.
timeout time.Duration
// the FFprobe cmd name
ffprobeCmdName string
// the ffmpeg cmd name
ffmpegCmdName string
// Whether do DVR by FFmpeg, if using SRS DVR, please set to false.
dvrByFFmpeg bool
// The stream to DVR for probing. Ignore if not DVR by ffmpeg
@ -764,8 +771,10 @@ type ffprobeClient struct {
func NewFFprobe(opts ...func(v *ffprobeClient)) FFprobeClient {
v := &ffprobeClient{
metadata: &ffprobeObject{},
dvrByFFmpeg: true,
metadata: &ffprobeObject{},
dvrByFFmpeg: true,
ffprobeCmdName: *srsFFprobe,
ffmpegCmdName: *srsFFmpeg,
}
v.doneCtx, v.doneCancel = context.WithCancel(context.Background())
@ -842,7 +851,7 @@ func (v *ffprobeClient) doDVR(ctx context.Context) error {
}
process := newBackendService()
process.name = *srsFFmpeg
process.name = v.ffmpegCmdName
process.args = []string{
"-t", fmt.Sprintf("%v", int64(v.duration/time.Second)),
"-i", v.streamURL, "-c", "copy", "-y", v.dvrFile,
@ -869,7 +878,7 @@ func (v *ffprobeClient) doDVR(ctx context.Context) error {
func (v *ffprobeClient) doProbe(ctx context.Context, cancel context.CancelFunc) error {
process := newBackendService()
process.name = *srsFFprobe
process.name = v.ffprobeCmdName
process.args = []string{
"-show_error", "-show_private_data", "-v", "quiet", "-find_stream_info",
"-analyzeduration", fmt.Sprintf("%v", int64(v.duration/time.Microsecond)),

View File

@ -7,6 +7,7 @@ The changelog for SRS.
<a name="v7-changes"></a>
## SRS 7.0 Changelog
* v7.0, 2025-09-09, Merge [#4446](https://github.com/ossrs/srs/pull/4446): SRT2RTMP: fix srt bridge hevc to rtmp error. v7.0.84 (#4446)
* v7.0, 2025-09-09, Merge [#4482](https://github.com/ossrs/srs/pull/4482): AI: Fix naming issue for protocol module. v7.0.83 (#4482)
* v7.0, 2025-09-07, Merge [#4479](https://github.com/ossrs/srs/pull/4479): AI: Fix naming problem in kernel module. v7.0.82 (#4479)
* v7.0, 2025-09-06, Merge [#4478](https://github.com/ossrs/srs/pull/4478): AI: Add more utests for kernel module. v7.0.81 (#4478)

View File

@ -671,6 +671,7 @@ srs_error_t SrsSrtFrameBuilder::check_vps_sps_pps_change(SrsTsMessage *msg)
// ts tbn to flv tbn.
uint32_t dts = (uint32_t)(msg->dts_ / 90);
uint32_t pts = (uint32_t)(msg->pts_ / 90);
std::string sh;
SrsUniquePtr<SrsRawHEVCStream> hevc(new SrsRawHEVCStream());
@ -682,8 +683,14 @@ srs_error_t SrsSrtFrameBuilder::check_vps_sps_pps_change(SrsTsMessage *msg)
// h265 packet to flv packet.
char *flv = NULL;
int nb_flv = 0;
if ((err = hevc->mux_avc2flv(sh, SrsVideoAvcFrameTypeKeyFrame, SrsVideoAvcFrameTraitSequenceHeader, dts, dts, &flv, &nb_flv)) != srs_success) {
return srs_error_wrap(err, "avc to flv");
if ((err = hevc->mux_avc2flv_enhanced(sh,
SrsVideoAvcFrameTypeKeyFrame,
SrsVideoHEVCFrameTraitPacketTypeSequenceStart,
dts,
pts,
&flv,
&nb_flv)) != srs_success) {
return srs_error_wrap(err, "hevc sh to flv");
}
SrsMessageHeader header;
@ -713,8 +720,6 @@ srs_error_t SrsSrtFrameBuilder::on_hevc_frame(SrsTsMessage *msg, vector<pair<cha
// ts tbn to flv tbn.
uint32_t dts = (uint32_t)(msg->dts_ / 90);
uint32_t pts = (uint32_t)(msg->pts_ / 90);
int32_t cts = pts - dts;
// for IDR frame, the frame is keyframe.
SrsVideoAvcFrameType frame_type = SrsVideoAvcFrameTypeInterFrame;
@ -736,16 +741,9 @@ srs_error_t SrsSrtFrameBuilder::on_hevc_frame(SrsTsMessage *msg, vector<pair<cha
SrsBuffer payload(rtmp.payload(), rtmp.size());
// Write 5bytes video tag header.
// @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
// Frame Type, Type of video frame.
// CodecID, Codec Identifier.
// set the rtmp header
payload.write_1bytes((frame_type << 4) | SrsVideoCodecIdHEVC);
// hevc_type: nalu
payload.write_1bytes(0x01);
// composition time
payload.write_3bytes(cts);
// @see: https://veovera.org/docs/enhanced/enhanced-rtmp-v1.pdf, page 8
payload.write_1bytes(SRS_FLV_IS_EX_HEADER | (frame_type << 4) | SrsVideoHEVCFrameTraitPacketTypeCodedFramesX);
payload.write_4bytes(0x68766331); // 'h' 'v' 'c' '1'
// Write video nalus.
for (size_t i = 0; i != ipb_frames.size(); ++i) {

View File

@ -9,6 +9,6 @@
#define VERSION_MAJOR 7
#define VERSION_MINOR 0
#define VERSION_REVISION 83
#define VERSION_REVISION 84
#endif