Skip to content

Commit e9330fc

Browse files
committed
add AV1/VP9/H265.
1 parent e156261 commit e9330fc

2 files changed

Lines changed: 62 additions & 15 deletions

File tree

livekit-rtc/rust-sdks

Submodule rust-sdks updated 45 files

livekit-rtc/tests/test_change_video_quality.py

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
1-
"""End-to-end Test for simulcast video quality layers.
1+
"""End-to-end Test for simulcast / SVC video quality layers.
22
3-
The test publishes a 1280x720 simulcast video track (rolling colored bars) using
4-
both VP8 and H264 codecs, and on the receiver side verifies that subscribing to
5-
each simulcast quality layer (HIGH=f, MEDIUM=h, LOW=q) yields frames of the
3+
The test publishes a 1280x720 video track (rolling colored bars) with four
4+
codec configurations and, on the receiver side, verifies that subscribing
5+
to each quality layer (HIGH=f, MEDIUM=h, LOW=q) yields frames of the
66
expected resolution.
77
8+
Codecs covered:
9+
- VP8 (simulcast: three independent encoders)
10+
- H264 (simulcast: three independent encoders)
11+
- H265 (simulcast: three independent encoders, hardware-only on most platforms)
12+
- VP9 (SVC: single bitstream with three spatial layers)
13+
- AV1 (SVC: single bitstream with three spatial layers)
14+
15+
For VP9 and AV1, libwebrtc converts the multiple RtpEncodingParameters
16+
produced by `simulcast=True` into SVC spatial layers, so the SFU still
17+
exposes the same f/h/q quality switches.
18+
819
Requires the following environment variables to run:
920
LIVEKIT_URL
1021
LIVEKIT_API_KEY
@@ -15,6 +26,7 @@
1526

1627
import asyncio
1728
import os
29+
import sys
1830
import time
1931
import uuid
2032
from typing import Callable, Optional, Tuple
@@ -213,19 +225,46 @@ async def _wait_for_layer(
213225
raise AssertionError(f"timed out waiting for ~{expected_w}x{expected_h}, last seen={last}")
214226

215227

228+
# H265 is hardware-only on most platforms and unreliable in CI — opt in via
229+
# LIVEKIT_TEST_H265=1. H264 on macOS depends on VideoToolbox being available
230+
# to the CI runner, which we cannot assume — opt in via LIVEKIT_TEST_H264_MACOS=1.
231+
_H265_ENABLED = os.getenv("LIVEKIT_TEST_H265") == "1"
232+
_H264_MACOS_ENABLED = os.getenv("LIVEKIT_TEST_H264_MACOS") == "1"
233+
_IS_MACOS = sys.platform == "darwin"
234+
235+
216236
@skip_if_no_credentials()
217237
@pytest.mark.asyncio
218238
@pytest.mark.parametrize(
219-
"video_codec, codec_name",
239+
"video_codec, codec_name, mode",
220240
[
221-
(rtc.VideoCodec.VP8, "vp8"),
222-
(rtc.VideoCodec.H264, "h264"),
241+
(rtc.VideoCodec.VP8, "vp8", "simulcast"),
242+
pytest.param(
243+
rtc.VideoCodec.H264,
244+
"h264",
245+
"simulcast",
246+
marks=pytest.mark.skipif(
247+
_IS_MACOS and not _H264_MACOS_ENABLED,
248+
reason="H264 disabled on macOS by default; set LIVEKIT_TEST_H264_MACOS=1 to enable",
249+
),
250+
),
251+
pytest.param(
252+
rtc.VideoCodec.H265,
253+
"h265",
254+
"simulcast",
255+
marks=pytest.mark.skipif(
256+
not _H265_ENABLED,
257+
reason="H265 disabled by default; set LIVEKIT_TEST_H265=1 to enable",
258+
),
259+
),
260+
(rtc.VideoCodec.VP9, "vp9", "svc"),
261+
(rtc.VideoCodec.AV1, "av1", "svc"),
223262
],
224263
)
225264
async def test_simulcast_quality_layers(
226-
video_codec: rtc.VideoCodec.ValueType, codec_name: str
265+
video_codec: rtc.VideoCodec.ValueType, codec_name: str, mode: str
227266
) -> None:
228-
room_name = unique_room_name(f"py-simulcast-{codec_name}")
267+
room_name = unique_room_name(f"py-{mode}-{codec_name}")
229268
url = os.environ["LIVEKIT_URL"]
230269

231270
sender, receiver = rtc.Room(), rtc.Room()
@@ -234,13 +273,20 @@ async def test_simulcast_quality_layers(
234273
await _ensure_all_connected([sender, receiver])
235274

236275
source = rtc.VideoSource(PUBLISH_WIDTH, PUBLISH_HEIGHT)
237-
track = rtc.LocalVideoTrack.create_video_track(f"simulcast-{codec_name}", source)
276+
track = rtc.LocalVideoTrack.create_video_track(f"{mode}-{codec_name}", source)
238277
options = rtc.TrackPublishOptions(
239278
source=rtc.TrackSource.SOURCE_CAMERA,
240279
simulcast=True,
241280
video_codec=video_codec,
242281
video_encoding=rtc.VideoEncoding(max_bitrate=3_000_000, max_framerate=PUBLISH_FPS),
243282
)
283+
# For SVC codecs, ask libwebrtc to emit three spatial × three temporal
284+
# layers in a single bitstream. Without an explicit scalability_mode AV1
285+
# falls back to a single-layer stream and the SFU has nothing to switch
286+
# down to. VP9 happens to work without it, but setting it is more
287+
# predictable.
288+
if mode == "svc":
289+
options.scalability_mode = "L3T3_KEY"
244290

245291
stop = asyncio.Event()
246292
pub_task = asyncio.create_task(_publish_loop(source, stop))
@@ -264,10 +310,11 @@ async def test_simulcast_quality_layers(
264310
remote_pub = await _ensure_track_subscribed(receiver, local_pub.sid)
265311
assert remote_pub.track is not None
266312

267-
# Give the SFU a moment to propagate simulcast layer metadata and
268-
# let the encoder/bandwidth estimator ramp up to all layers before
269-
# we start switching qualities.
270-
await asyncio.sleep(5.0)
313+
# Give the SFU a moment to propagate layer metadata and let the
314+
# encoder/bandwidth estimator ramp up to all layers before we start
315+
# switching qualities. SVC codecs (VP9/AV1) typically need more
316+
# time than VP8/H264 simulcast to produce all spatial layers.
317+
await asyncio.sleep(10.0 if mode == "svc" else 5.0)
271318
print(
272319
f"[{codec_name}] remote_pub: sid={remote_pub.sid} "
273320
f"simulcasted={remote_pub.simulcasted} "

0 commit comments

Comments
 (0)