@@ -28,6 +28,7 @@ import (
28
28
"math/rand"
29
29
"os"
30
30
"path"
31
+ "runtime"
31
32
"sync"
32
33
"testing"
33
34
"time"
@@ -114,3 +115,97 @@ func TestRtmpPublish_HlsPlay_Basic(t *testing.T) {
114
115
}
115
116
}
116
117
}
118
+
119
+ func TestRtmpPublish_HlsPlay_HEVC_Basic (t * testing.T ) {
120
+ // This case is run in parallel.
121
+ t .Parallel ()
122
+
123
+ // Setup the max timeout for this case.
124
+ ctx , cancel := context .WithTimeout (logger .WithContext (context .Background ()), time .Duration (* srsTimeout )* time .Millisecond )
125
+ defer cancel ()
126
+
127
+ // Only enable for github actions, ignore for darwin.
128
+ if runtime .GOOS == "darwin" {
129
+ logger .Tf (ctx , "Depends on FFmpeg(HEVC over RTMP), only available for GitHub actions" )
130
+ return
131
+ }
132
+
133
+ // Check a set of errors.
134
+ var r0 , r1 , r2 , r3 , r4 , r5 , r6 error
135
+ defer func (ctx context.Context ) {
136
+ if err := filterTestError (ctx .Err (), r0 , r1 , r2 , r3 , r4 , r5 , r6 ); err != nil {
137
+ t .Errorf ("Fail for err %+v" , err )
138
+ } else {
139
+ logger .Tf (ctx , "test done with err %+v" , err )
140
+ }
141
+ }(ctx )
142
+
143
+ var wg sync.WaitGroup
144
+ defer wg .Wait ()
145
+
146
+ // Start SRS server and wait for it to be ready.
147
+ svr := NewSRSServer (func (v * srsServer ) {
148
+ v .envs = []string {
149
+ "SRS_HTTP_SERVER_ENABLED=on" ,
150
+ "SRS_VHOST_HLS_ENABLED=on" ,
151
+ }
152
+ })
153
+ wg .Add (1 )
154
+ go func () {
155
+ defer wg .Done ()
156
+ r0 = svr .Run (ctx , cancel )
157
+ }()
158
+
159
+ // Start FFmpeg to publish stream.
160
+ streamID := fmt .Sprintf ("stream-%v-%v" , os .Getpid (), rand .Int ())
161
+ streamURL := fmt .Sprintf ("rtmp://localhost:%v/live/%v" , svr .RTMPPort (), streamID )
162
+ ffmpeg := NewFFmpeg (func (v * ffmpegClient ) {
163
+ v .args = []string {
164
+ "-stream_loop" , "-1" , "-re" , "-i" , * srsPublishAvatar , "-acodec" , "copy" , "-vcodec" , "libx265" , "-f" , "flv" , streamURL ,
165
+ }
166
+ })
167
+ wg .Add (1 )
168
+ go func () {
169
+ defer wg .Done ()
170
+ <- svr .ReadyCtx ().Done ()
171
+ r1 = ffmpeg .Run (ctx , cancel )
172
+ }()
173
+
174
+ // Start FFprobe to detect and verify stream.
175
+ duration := time .Duration (* srsFFprobeDuration ) * time .Millisecond
176
+ ffprobe := NewFFprobe (func (v * ffprobeClient ) {
177
+ v .dvrFile = path .Join (svr .WorkDir (), "objs" , fmt .Sprintf ("srs-ffprobe-%v.ts" , streamID ))
178
+ v .streamURL = fmt .Sprintf ("http://localhost:%v/live/%v.m3u8" , svr .HTTPPort (), streamID )
179
+ v .duration , v .timeout = duration , time .Duration (* srsFFprobeTimeout )* time .Millisecond
180
+ })
181
+ wg .Add (1 )
182
+ go func () {
183
+ defer wg .Done ()
184
+ <- svr .ReadyCtx ().Done ()
185
+ r2 = ffprobe .Run (ctx , cancel )
186
+ }()
187
+
188
+ // Fast quit for probe done.
189
+ select {
190
+ case <- ctx .Done ():
191
+ case <- ffprobe .ProbeDoneCtx ().Done ():
192
+ defer cancel ()
193
+
194
+ str , m := ffprobe .Result ()
195
+ if len (m .Streams ) != 2 {
196
+ r3 = errors .Errorf ("invalid streams=%v, %v, %v" , len (m .Streams ), m .String (), str )
197
+ }
198
+
199
+ // Note that HLS score is low, so we only check duration. Note that only check part of duration, because we
200
+ // might get only some pieces of segments.
201
+ if dv := m .Duration (); dv < duration / 3 {
202
+ r4 = errors .Errorf ("short duration=%v < %v, %v, %v" , dv , duration / 3 , m .String (), str )
203
+ }
204
+
205
+ if v := m .Video (); v == nil {
206
+ r5 = errors .Errorf ("no video %v, %v" , m .String (), str )
207
+ } else if v .CodecName != "hevc" {
208
+ r6 = errors .Errorf ("invalid video codec=%v, %v, %v" , v .CodecName , m .String (), str )
209
+ }
210
+ }
211
+ }
0 commit comments