@@ -44,6 +44,7 @@ def test_amd_amf_available(setup):
44
44
assert c .decoder is True
45
45
assert c .encoder is False
46
46
case SoraVideoCodecType .AV1 :
47
+ # TODO: AV1 decoder は True だが色々課題あり
47
48
assert c .decoder is True
48
49
assert c .encoder is True
49
50
case SoraVideoCodecType .H264 :
@@ -152,3 +153,158 @@ def test_amd_amf_sendonly_recvonly(setup, video_codec_type):
152
153
assert inbound_rtp_stats ["decoderImplementation" ] == "AMF"
153
154
assert inbound_rtp_stats ["bytesReceived" ] > 0
154
155
assert inbound_rtp_stats ["packetsReceived" ] > 0
156
+
157
+
158
+ @pytest .mark .skipif (os .environ .get ("AMD_AMF" ) is None , reason = "AMD AMF でのみ実行する" )
159
+ @pytest .mark .parametrize (
160
+ (
161
+ "video_codec_type" ,
162
+ "expected_implementation" ,
163
+ "video_bit_rate" ,
164
+ "video_width" ,
165
+ "video_height" ,
166
+ "simulcast_count" ,
167
+ ),
168
+ # FIXME: H.265 では、本数が 2 本 や 1 本の場合、エラーになるのでコメントアウトしている
169
+ # FIXME: AV1 では、解像度が一定数より低くなる場合、エラーになるのでコメントアウトしている
170
+ [
171
+ # 1080p
172
+ ("AV1" , "AMF" , 5000 , 1920 , 1080 , 3 ),
173
+ ("H264" , "AMF" , 5000 , 1920 , 1080 , 3 ),
174
+ ("H265" , "AMF" , 5000 , 1920 , 1080 , 3 ),
175
+ # 720p
176
+ ("AV1" , "AMF" , 2500 , 1280 , 720 , 3 ),
177
+ ("H264" , "AMF" , 2500 , 1280 , 720 , 3 ),
178
+ ("H265" , "AMF" , 2500 , 1280 , 720 , 3 ),
179
+ # 540p
180
+ ("AV1" , "AMF" , 1200 , 960 , 540 , 3 ),
181
+ ("H264" , "AMF" , 1200 , 960 , 540 , 3 ),
182
+ ("H265" , "AMF" , 1200 , 960 , 540 , 3 ),
183
+ # 360p
184
+ # ("AV1", "AMF", 700, 640, 360, 2),
185
+ ("H264" , "AMF" , 700 , 640 , 360 , 2 ),
186
+ # ("H265", "AMF", 700, 640, 360, 2),
187
+ # 270p
188
+ # ("AV1", "AMF", 450, 480, 270, 2),
189
+ ("H264" , "AMF" , 450 , 480 , 270 , 2 ),
190
+ # ("H265", "AMF", 257, 480, 270, 2),
191
+ # 180p
192
+ # ("AV1", "AMF", 200, 320, 180, 1),
193
+ ("H264" , "AMF" , 200 , 320 , 180 , 1 ),
194
+ # ("H265", "AMF", 142, 320, 180, 1),
195
+ # 135p
196
+ # ("H265", "AMF", 101, 240, 135, 1),
197
+ ],
198
+ )
199
+ def test_amd_amf_simulcast (
200
+ setup ,
201
+ video_codec_type ,
202
+ expected_implementation ,
203
+ video_bit_rate ,
204
+ video_width ,
205
+ video_height ,
206
+ simulcast_count ,
207
+ ):
208
+ if not is_codec_supported (video_codec_type , SoraVideoCodecImplementation .AMD_AMF ):
209
+ pytest .skip (f"このチップでは { video_codec_type } のエンコードがサポートされていません" )
210
+
211
+ signaling_urls = setup .get ("signaling_urls" )
212
+ channel_id_prefix = setup .get ("channel_id_prefix" )
213
+ metadata = setup .get ("metadata" )
214
+
215
+ channel_id = f"{ channel_id_prefix } _{ __name__ } _{ sys ._getframe ().f_code .co_name } _{ uuid .uuid4 ()} "
216
+
217
+ sendonly = SoraClient (
218
+ signaling_urls ,
219
+ SoraRole .SENDONLY ,
220
+ channel_id ,
221
+ simulcast = True ,
222
+ audio = False ,
223
+ video = True ,
224
+ video_codec_type = video_codec_type ,
225
+ video_bit_rate = video_bit_rate ,
226
+ metadata = metadata ,
227
+ video_width = video_width ,
228
+ video_height = video_height ,
229
+ video_codec_preference = SoraVideoCodecPreference (
230
+ codecs = [
231
+ SoraVideoCodecPreference .Codec (
232
+ type = codec_type_string_to_codec_type (video_codec_type ),
233
+ encoder = SoraVideoCodecImplementation .AMD_AMF ,
234
+ ),
235
+ ]
236
+ ),
237
+ )
238
+ sendonly .connect (fake_video = True )
239
+
240
+ time .sleep (5 )
241
+
242
+ sendonly_stats = sendonly .get_stats ()
243
+
244
+ sendonly .disconnect ()
245
+
246
+ # "type": "answer" の SDP で Simulcast があるかどうか
247
+ assert sendonly .answer_message is not None
248
+ assert "sdp" in sendonly .answer_message
249
+ assert "a=simulcast:send r0;r1;r2" in sendonly .answer_message ["sdp" ]
250
+
251
+ # codec が無かったら StopIteration 例外が上がる
252
+ sendonly_codec_stats = next (s for s in sendonly_stats if s .get ("type" ) == "codec" )
253
+ assert sendonly_codec_stats ["mimeType" ] == f"video/{ video_codec_type } "
254
+
255
+ # 複数の outbound-rtp 統計情報を取得
256
+ outbound_rtp_stats = [
257
+ s for s in sendonly_stats if s .get ("type" ) == "outbound-rtp" and s .get ("kind" ) == "video"
258
+ ]
259
+ # simulcast_count に関係なく統計情報はかならず 3 本出力される
260
+ # これは SDP で rid で ~r0 とかやる減るはず
261
+ assert len (outbound_rtp_stats ) == 3
262
+
263
+ # rid でソート
264
+ sorted_stats = sorted (outbound_rtp_stats , key = lambda x : x .get ("rid" , "" ))
265
+
266
+ for i , s in enumerate (sorted_stats ):
267
+ assert s ["rid" ] == f"r{ i } "
268
+ # simulcast_count が 2 の場合、rid r2 の bytesSent/packetsSent は 0 or 1 になる
269
+ # simulcast_count が 1 の場合、rid r2 と r1 の bytesSent/packetsSent は 0 or 1 になる
270
+ if i < simulcast_count :
271
+ # 1 本になると simulcastEncodingAdapter がなくなる
272
+ # if simulcast_count > 1:
273
+ # assert "SimulcastEncoderAdapter" in s["encoderImplementation"]
274
+
275
+ # targetBitrate が指定したビットレートの 90% 以上、100% 以下に収まることを確認
276
+ expected_bitrate = video_bit_rate * 1000
277
+
278
+ # パケットが一切送られてこない場合は frame_width/frame_height が含まれないので None になる
279
+ frame_width = s .get ("frameWidth" )
280
+ frame_height = s .get ("frameHeight" )
281
+ encoder_implementation = s .get ("encoderImplementation" )
282
+
283
+ print (
284
+ s ["rid" ],
285
+ video_codec_type ,
286
+ expected_bitrate ,
287
+ s ["targetBitrate" ],
288
+ expected_implementation ,
289
+ encoder_implementation ,
290
+ frame_width ,
291
+ frame_height ,
292
+ s ["bytesSent" ],
293
+ s ["packetsSent" ],
294
+ )
295
+ assert s ["bytesSent" ] > 1000
296
+ assert s ["packetsSent" ] > 5
297
+ # 期待値の 20% 以上、100% 以下に収まることを確認
298
+ assert expected_bitrate * 0.2 <= s ["targetBitrate" ] <= expected_bitrate
299
+ else :
300
+ # 本来は 0 なのだが、simulcast_count が 1 の場合、
301
+ # packetSent が 0 ではなく 1 や 2 になる場合がある
302
+ # byteSent は 0
303
+ print (
304
+ s ["rid" ],
305
+ video_codec_type ,
306
+ s ["bytesSent" ],
307
+ s ["packetsSent" ],
308
+ )
309
+ assert s ["bytesSent" ] == 0
310
+ assert s ["packetsSent" ] <= 2
0 commit comments