mirror of
https://github.com/suitenumerique/meet.git
synced 2026-09-02 21:58:29 +00:00
wip
This commit is contained in:
+26
-12
@@ -37,18 +37,28 @@ the new defaults instead of your tuned values.**
|
|||||||
| `RECORDING_ENCODING_VIDEO_BITRATE_KBPS` | That profile's `kbps`. |
|
| `RECORDING_ENCODING_VIDEO_BITRATE_KBPS` | That profile's `kbps`. |
|
||||||
|
|
||||||
`RECORDING_ENCODING_AUDIO_BITRATE_KBPS` and `RECORDING_ENCODING_KEY_FRAME_INTERVAL_S`
|
`RECORDING_ENCODING_AUDIO_BITRATE_KBPS` and `RECORDING_ENCODING_KEY_FRAME_INTERVAL_S`
|
||||||
are unchanged and keep their values.
|
keep their names and meaning. The keyframe interval now defaults to `0` (unset,
|
||||||
|
encoder's choice) instead of `4.0`.
|
||||||
|
|
||||||
#### If you never set `RECORDING_ENCODING_ENABLED=True`
|
#### If you never set `RECORDING_ENCODING_ENABLED=True`
|
||||||
|
|
||||||
No action is required. The shipped defaults (`RECORDING_ENCODING_DEFAULT_PROFILE=full`,
|
The shipped defaults (`RECORDING_ENCODING_DEFAULT_PROFILE=full`,
|
||||||
`RECORDING_ENCODING_DEFAULT_RESOLUTION=720p`) match LiveKit's built-in
|
`RECORDING_ENCODING_DEFAULT_RESOLUTION=720p`) match LiveKit's built-in
|
||||||
`H264_720P_30` preset: 1280×720, 30 fps, 3000 kbps H.264 MAIN, 128 kbps AAC.
|
`H264_720P_30` preset: 1280×720, 30 fps, 3000 kbps H.264 MAIN, 128 kbps AAC.
|
||||||
|
Video output is therefore unchanged.
|
||||||
|
|
||||||
Note that these values are now sent explicitly as advanced `EncodingOptions`
|
Audio and keyframing may not be. These values are now sent explicitly as advanced
|
||||||
rather than relying on LiveKit's preset, so `RECORDING_ENCODING_AUDIO_BITRATE_KBPS`
|
`EncodingOptions` rather than relying on LiveKit's preset, so
|
||||||
and `RECORDING_ENCODING_KEY_FRAME_INTERVAL_S` now apply to every recording. They
|
`RECORDING_ENCODING_AUDIO_BITRATE_KBPS` and `RECORDING_ENCODING_KEY_FRAME_INTERVAL_S`
|
||||||
previously applied only when `RECORDING_ENCODING_ENABLED` was `True`.
|
now apply to every recording. They previously applied only when
|
||||||
|
`RECORDING_ENCODING_ENABLED` was `True`. **If you set either of them while the
|
||||||
|
feature was disabled, they had no effect and now do**; check them before upgrading.
|
||||||
|
|
||||||
|
If you never set them, no action is required: 128 kbps AAC is what the preset used,
|
||||||
|
and the keyframe interval now defaults to `0`, which leaves the field unset so the
|
||||||
|
encoder keeps picking it as before. Set `RECORDING_ENCODING_KEY_FRAME_INTERVAL_S=4.0`
|
||||||
|
if you want fixed 4-second keyframes (the value the setting defaulted to while it
|
||||||
|
was gated behind `RECORDING_ENCODING_ENABLED`).
|
||||||
|
|
||||||
To keep letting LiveKit pick the encoding instead, set either default to an empty
|
To keep letting LiveKit pick the encoding instead, set either default to an empty
|
||||||
value:
|
value:
|
||||||
@@ -72,11 +82,14 @@ RECORDING_ENCODING_DEFAULT_RESOLUTION=720p
|
|||||||
RECORDING_ENCODING_DEFAULT_PROFILE=my_old_profile
|
RECORDING_ENCODING_DEFAULT_PROFILE=my_old_profile
|
||||||
```
|
```
|
||||||
|
|
||||||
Two constraints are validated at startup and may raise a `ValueError`:
|
Both maps are validated at startup and a malformed one raises a `ValueError`:
|
||||||
|
|
||||||
- every profile in `RECORDING_ENCODING_AVAILABLE_PROFILES` must define a `kbps`
|
- every entry of `RECORDING_ENCODING_AVAILABLE_RESOLUTIONS` must declare `width` and
|
||||||
entry for **exactly** the keys of `RECORDING_ENCODING_AVAILABLE_RESOLUTIONS`;
|
`height`, and every entry of `RECORDING_ENCODING_AVAILABLE_PROFILES` an `fps` and a
|
||||||
overriding one of the two maps usually means overriding both;
|
`kbps` map;
|
||||||
|
- every profile must define a `kbps` entry for **exactly** the keys of
|
||||||
|
`RECORDING_ENCODING_AVAILABLE_RESOLUTIONS`; overriding one of the two maps usually
|
||||||
|
means overriding both;
|
||||||
- `RECORDING_ENCODING_DEFAULT_RESOLUTION` and `RECORDING_ENCODING_DEFAULT_PROFILE`,
|
- `RECORDING_ENCODING_DEFAULT_RESOLUTION` and `RECORDING_ENCODING_DEFAULT_PROFILE`,
|
||||||
when non-empty, must be keys of their respective map.
|
when non-empty, must be keys of their respective map.
|
||||||
|
|
||||||
@@ -84,8 +97,9 @@ Two constraints are validated at startup and may raise a `ValueError`:
|
|||||||
|
|
||||||
`RECORDING_CUSTOM_ENCODING_ENABLED` (default `False`) toggles whether the
|
`RECORDING_CUSTOM_ENCODING_ENABLED` (default `False`) toggles whether the
|
||||||
start-recording API accepts an `encoding` object
|
start-recording API accepts an `encoding` object
|
||||||
(`{"resolution": "720p", "profile": "talking_heads"}`, `profile` optional) that
|
(`{"resolution": "720p", "profile": "talking_heads"}`, `profile` optional. It
|
||||||
overrides the default for a single recording. It does not enable or disable the
|
falls back to `RECORDING_ENCODING_DEFAULT_PROFILE`) that overrides the default for
|
||||||
|
a single recording. It does not enable or disable the
|
||||||
default encoding, which is built from the two `RECORDING_ENCODING_DEFAULT_*`
|
default encoding, which is built from the two `RECORDING_ENCODING_DEFAULT_*`
|
||||||
settings either way. Leaving it at `False` preserves the previous behaviour, where
|
settings either way. Leaving it at `False` preserves the previous behaviour, where
|
||||||
every recording uses the server-side encoding: requests carrying
|
every recording uses the server-side encoding: requests carrying
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ sequenceDiagram
|
|||||||
| **RECORDING_ENCODING_DEFAULT_RESOLUTION** | String | `"720p"` | Resolution used by the default encoding. When set, must be a key of `RECORDING_ENCODING_AVAILABLE_RESOLUTIONS`. Leave unset (together with, or instead of, the default profile) to disable the custom default encoding and fall back to LiveKit's built-in preset (a startup warning is emitted). |
|
| **RECORDING_ENCODING_DEFAULT_RESOLUTION** | String | `"720p"` | Resolution used by the default encoding. When set, must be a key of `RECORDING_ENCODING_AVAILABLE_RESOLUTIONS`. Leave unset (together with, or instead of, the default profile) to disable the custom default encoding and fall back to LiveKit's built-in preset (a startup warning is emitted). |
|
||||||
| **RECORDING_ENCODING_DEFAULT_PROFILE** | String | `"full"` | Profile used by the default encoding. When set, must be a key of `RECORDING_ENCODING_AVAILABLE_PROFILES`. Leave unset (together with, or instead of, the default resolution) to disable the custom default encoding and fall back to LiveKit's built-in preset (a startup warning is emitted). |
|
| **RECORDING_ENCODING_DEFAULT_PROFILE** | String | `"full"` | Profile used by the default encoding. When set, must be a key of `RECORDING_ENCODING_AVAILABLE_PROFILES`. Leave unset (together with, or instead of, the default resolution) to disable the custom default encoding and fall back to LiveKit's built-in preset (a startup warning is emitted). |
|
||||||
| **RECORDING_ENCODING_AUDIO_BITRATE_KBPS** | Integer | `128` | AAC audio bitrate in kbps used in the default encoding. |
|
| **RECORDING_ENCODING_AUDIO_BITRATE_KBPS** | Integer | `128` | AAC audio bitrate in kbps used in the default encoding. |
|
||||||
| **RECORDING_ENCODING_KEY_FRAME_INTERVAL_S** | Float | `4.0` | Keyframe interval in seconds. Drives seek granularity in the recorded MP4 (a player can only seek to keyframe boundaries). Larger values give the encoder slightly more bits for non-keyframe content at a fixed bitrate. `4.0` is a standard VOD value. |
|
| **RECORDING_ENCODING_KEY_FRAME_INTERVAL_S** | Float | `0.0` | Keyframe interval in seconds. Drives seek granularity in the recorded MP4 (a player can only seek to keyframe boundaries). Larger values give the encoder slightly more bits for non-keyframe content at a fixed bitrate. `0` leaves the field unset, letting the encoder pick; `4.0` is a standard VOD value. |
|
||||||
|
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
@@ -135,7 +135,7 @@ Every video recording is encoded from a default resolved from `RECORDING_ENCODIN
|
|||||||
Encoding is chosen from two maps: `RECORDING_ENCODING_AVAILABLE_RESOLUTIONS` (`resolution → {"width", "height"}`) and `RECORDING_ENCODING_AVAILABLE_PROFILES` (`profile → {"fps", "kbps": {resolution: video_bitrate_kbps}}`):
|
Encoding is chosen from two maps: `RECORDING_ENCODING_AVAILABLE_RESOLUTIONS` (`resolution → {"width", "height"}`) and `RECORDING_ENCODING_AVAILABLE_PROFILES` (`profile → {"fps", "kbps": {resolution: video_bitrate_kbps}}`):
|
||||||
|
|
||||||
- **Default**: `RECORDING_ENCODING_DEFAULT_PROFILE` + `RECORDING_ENCODING_DEFAULT_RESOLUTION` set the encoding used by every recording that doesn't override it. Leave either unset to fall back to LiveKit's built-in preset (a startup warning is emitted).
|
- **Default**: `RECORDING_ENCODING_DEFAULT_PROFILE` + `RECORDING_ENCODING_DEFAULT_RESOLUTION` set the encoding used by every recording that doesn't override it. Leave either unset to fall back to LiveKit's built-in preset (a startup warning is emitted).
|
||||||
- **Per recording (opt-in)**: set `RECORDING_CUSTOM_ENCODING_ENABLED=True` to let clients override the default per recording. The start-recording API then accepts an `encoding` object selecting a `resolution` (required) and `profile` (optional): a resolution-only request sets the frame size but leaves LiveKit's default framerate/bitrate; adding a profile pins fps and bitrate too. When `RECORDING_CUSTOM_ENCODING_ENABLED=False`, the API rejects any per-recording `encoding` and the default is used.
|
- **Per recording (opt-in)**: set `RECORDING_CUSTOM_ENCODING_ENABLED=True` to let clients override the default per recording. The start-recording API then accepts an `encoding` object selecting a `resolution` (required) and `profile` (optional): a resolution-only request keeps `RECORDING_ENCODING_DEFAULT_PROFILE` for fps and bitrate, so clients can only pick from pre-defined values. When `RECORDING_CUSTOM_ENCODING_ENABLED=False`, the API rejects any per-recording `encoding` and the default is used.
|
||||||
|
|
||||||
The resolved values are passed straight through LiveKit's `EncodingOptions.advanced` to the GStreamer pipeline (`x264enc` for video, `faac` for audio), so there are no hidden conversions — what the profile/resolution resolve to is what the encoder receives.
|
The resolved values are passed straight through LiveKit's `EncodingOptions.advanced` to the GStreamer pipeline (`x264enc` for video, `faac` for audio), so there are no hidden conversions — what the profile/resolution resolve to is what the encoder receives.
|
||||||
|
|
||||||
|
|||||||
@@ -259,8 +259,8 @@ class EncodingConfig(BaseModel):
|
|||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
resolution: Target video resolution.
|
resolution: Target video resolution.
|
||||||
profile: Encoding profile to balance quality and CPU usage. When `None`,
|
profile: Encoding profile to fps and kbps. When `None`,
|
||||||
LiveKit default framerate/bitrate are used for the resolution.
|
`settings.RECORDING_ENCODING_DEFAULT_PROFILE` applies.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
resolution: str
|
resolution: str
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ _RECORDING_AUDIO_CODEC = livekit_api.AudioCodec.AAC
|
|||||||
_RECORDING_AUDIO_FREQUENCY_HZ = 48000
|
_RECORDING_AUDIO_FREQUENCY_HZ = 48000
|
||||||
|
|
||||||
|
|
||||||
def build_encoding_options(resolution, profile):
|
def build_encoding_options(resolution, profile=None):
|
||||||
"""Assemble the LiveKit ``EncodingOptions`` kwargs for a resolution/profile.
|
"""Assemble the LiveKit ``EncodingOptions`` kwargs for a resolution/profile.
|
||||||
|
|
||||||
Single source of truth shared by the default encoding
|
Single source of truth shared by the default encoding
|
||||||
@@ -32,11 +32,13 @@ def build_encoding_options(resolution, profile):
|
|||||||
|
|
||||||
The profile-independent fields (audio bitrate, keyframe interval and the
|
The profile-independent fields (audio bitrate, keyframe interval and the
|
||||||
pinned codec / frequency constants) are always included.
|
pinned codec / frequency constants) are always included.
|
||||||
The resolution-dependent fields are added only when they can be resolved:
|
|
||||||
width/height require a resolution; framerate/video_bitrate require both a
|
An omitted profile falls back to RECORDING_ENCODING_DEFAULT_PROFILE.
|
||||||
resolution and a profile (a resolution-only encoding leaves framerate and
|
Framerate and bitrate are left to LiveKit only when the operator
|
||||||
bitrate to LiveKit's defaults).
|
declared no default profile at all.
|
||||||
"""
|
"""
|
||||||
|
profile = profile or settings.RECORDING_ENCODING_DEFAULT_PROFILE
|
||||||
|
|
||||||
options: Dict[str, Any] = {
|
options: Dict[str, Any] = {
|
||||||
"audio_bitrate": settings.RECORDING_ENCODING_AUDIO_BITRATE_KBPS,
|
"audio_bitrate": settings.RECORDING_ENCODING_AUDIO_BITRATE_KBPS,
|
||||||
"key_frame_interval": settings.RECORDING_ENCODING_KEY_FRAME_INTERVAL_S,
|
"key_frame_interval": settings.RECORDING_ENCODING_KEY_FRAME_INTERVAL_S,
|
||||||
|
|||||||
@@ -83,28 +83,17 @@ class BaseEgressService:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError("Subclass must implement this method.")
|
raise NotImplementedError("Subclass must implement this method.")
|
||||||
|
|
||||||
def _build_encoding_options(self):
|
def _resolve_encoding_options(self, encoding_options):
|
||||||
"""Build a LiveKit EncodingOptions from the service config, or None.
|
"""Build a LiveKit EncodingOptions from a resolved kwargs dict, or None.
|
||||||
|
|
||||||
|
``encoding_options`` is the per-recording dict persisted by the API in
|
||||||
|
``recording.options["encoding"]["resolved"]``; it falls back to the
|
||||||
|
default encoding carried by the service config.
|
||||||
|
|
||||||
When None is returned, the caller should omit the `advanced` field so
|
When None is returned, the caller should omit the `advanced` field so
|
||||||
LiveKit Egress falls back to its built-in preset (H264_720P_30).
|
LiveKit Egress falls back to its built-in preset (H264_720P_30).
|
||||||
|
|
||||||
The full EncodingOptions kwargs (operator-tunable values + pinned
|
|
||||||
codec / frequency constants) are assembled in `WorkerServiceConfig`,
|
|
||||||
so this method is a thin protobuf adapter.
|
|
||||||
"""
|
|
||||||
opts = self._config.encoding_options
|
|
||||||
if not opts:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return livekit_api.EncodingOptions(**opts)
|
|
||||||
|
|
||||||
def _resolve_encoding_options(self, encoding_options):
|
|
||||||
"""Build LiveKit EncodingOptions from a resolved per-recording dict, or None.
|
|
||||||
|
|
||||||
``encoding_options`` is the dict persisted by the API in
|
|
||||||
``recording.options["encoding"]["resolved"]``.
|
|
||||||
"""
|
"""
|
||||||
|
encoding_options = encoding_options or self._config.encoding_options
|
||||||
if not encoding_options:
|
if not encoding_options:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -137,10 +126,7 @@ class VideoCompositeEgressService(BaseEgressService):
|
|||||||
"layout": "speaker-light",
|
"layout": "speaker-light",
|
||||||
}
|
}
|
||||||
|
|
||||||
advanced = (
|
advanced = self._resolve_encoding_options(encoding_options)
|
||||||
self._resolve_encoding_options(encoding_options)
|
|
||||||
or self._build_encoding_options()
|
|
||||||
)
|
|
||||||
if advanced is not None:
|
if advanced is not None:
|
||||||
request_kwargs["advanced"] = advanced
|
request_kwargs["advanced"] = advanced
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.test import override_settings
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from livekit import api as livekit_api
|
from livekit import api as livekit_api
|
||||||
@@ -41,12 +42,35 @@ def service():
|
|||||||
# --- build_encoding_options ---
|
# --- build_encoding_options ---
|
||||||
|
|
||||||
|
|
||||||
def test_build_options_without_profile_omits_profile_fields():
|
def test_build_options_without_profile_uses_default_profile():
|
||||||
"""A resolution-only config should resolve dimensions but no framerate/bitrate.
|
"""A resolution-only config should fall back to the default profile.
|
||||||
|
|
||||||
The profile-independent fields (audio bitrate, keyframe interval, codec /
|
Left unset, framerate and video_bitrate take LiveKit's own EncodingOptions
|
||||||
frequency pins) are always present, matching the default encoding.
|
defaults. The profile-independent fields (audio bitrate, keyframe interval,
|
||||||
|
codec/frequency pins) are always present, matching the default encoding.
|
||||||
"""
|
"""
|
||||||
|
default_profile = settings.RECORDING_ENCODING_AVAILABLE_PROFILES[
|
||||||
|
settings.RECORDING_ENCODING_DEFAULT_PROFILE
|
||||||
|
]
|
||||||
|
|
||||||
|
resolved = build_encoding_options("540p")
|
||||||
|
|
||||||
|
assert resolved == {
|
||||||
|
"audio_bitrate": settings.RECORDING_ENCODING_AUDIO_BITRATE_KBPS,
|
||||||
|
"key_frame_interval": settings.RECORDING_ENCODING_KEY_FRAME_INTERVAL_S,
|
||||||
|
"video_codec": livekit_api.VideoCodec.H264_MAIN,
|
||||||
|
"audio_codec": livekit_api.AudioCodec.AAC,
|
||||||
|
"audio_frequency": 48000,
|
||||||
|
"width": 960,
|
||||||
|
"height": 540,
|
||||||
|
"framerate": default_profile["fps"],
|
||||||
|
"video_bitrate": default_profile["kbps"]["540p"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(RECORDING_ENCODING_DEFAULT_PROFILE="")
|
||||||
|
def test_build_options_omits_profile_fields_without_default_profile():
|
||||||
|
"""With no default profile declared, framerate/bitrate are left to LiveKit."""
|
||||||
resolved = build_encoding_options("720p", None)
|
resolved = build_encoding_options("720p", None)
|
||||||
|
|
||||||
assert resolved == {
|
assert resolved == {
|
||||||
@@ -110,13 +134,32 @@ def test_resolve_profile_resolution_combinations(service, profile, resolution):
|
|||||||
assert result.audio_frequency == 48000
|
assert result.audio_frequency == 48000
|
||||||
|
|
||||||
|
|
||||||
def test_resolve_options_none_profile_uses_livekit_defaults(service):
|
def test_resolve_options_none_profile_uses_default_profile(service):
|
||||||
"""Missing profile should pass 0 fps/bitrate (LiveKit protobuf default).
|
"""A missing profile should resolve to the default profile's fps/bitrate."""
|
||||||
|
default_profile = settings.RECORDING_ENCODING_AVAILABLE_PROFILES[
|
||||||
|
settings.RECORDING_ENCODING_DEFAULT_PROFILE
|
||||||
|
]
|
||||||
|
|
||||||
The pinned codec / audio fields are still applied even without a profile.
|
resolved = build_encoding_options("720p", None)
|
||||||
|
result = service._resolve_encoding_options(resolved)
|
||||||
|
|
||||||
|
assert result.width == 1280
|
||||||
|
assert result.height == 720
|
||||||
|
assert result.framerate == default_profile["fps"]
|
||||||
|
assert result.video_bitrate == default_profile["kbps"]["720p"]
|
||||||
|
assert result.audio_bitrate == settings.RECORDING_ENCODING_AUDIO_BITRATE_KBPS
|
||||||
|
assert result.video_codec == livekit_api.VideoCodec.H264_MAIN
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(RECORDING_ENCODING_DEFAULT_PROFILE="")
|
||||||
|
def test_resolve_options_passes_zero_when_no_default_profile(service):
|
||||||
|
"""With no default profile, fps/bitrate reach LiveKit unset (protobuf 0).
|
||||||
|
|
||||||
|
The pinned codec / audio fields are still applied.
|
||||||
"""
|
"""
|
||||||
resolved = build_encoding_options("720p", None)
|
resolved = build_encoding_options("720p", None)
|
||||||
result = service._resolve_encoding_options(resolved)
|
result = service._resolve_encoding_options(resolved)
|
||||||
|
|
||||||
assert result.width == 1280
|
assert result.width == 1280
|
||||||
assert result.height == 720
|
assert result.height == 720
|
||||||
assert result.framerate == 0
|
assert result.framerate == 0
|
||||||
|
|||||||
@@ -561,6 +561,36 @@ def test_start_recording_persists_resolved_encoding(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_start_recording_resolution_only_uses_default_profile(
|
||||||
|
settings, mock_worker_service_factory, mock_worker_manager
|
||||||
|
):
|
||||||
|
"""An encoding without a profile should resolve the default profile."""
|
||||||
|
settings.RECORDING_ENABLE = True
|
||||||
|
settings.RECORDING_CUSTOM_ENCODING_ENABLED = True
|
||||||
|
settings.RECORDING_ENCODING_DEFAULT_PROFILE = "talking_heads"
|
||||||
|
room = RoomFactory()
|
||||||
|
user = UserFactory()
|
||||||
|
room.accesses.create(user=user, role="owner")
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
f"/api/v1.0/rooms/{room.id}/start-recording/",
|
||||||
|
{"mode": "screen_recording", "options": {"encoding": {"resolution": "540p"}}},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 201
|
||||||
|
recording = Recording.objects.get(room=room)
|
||||||
|
resolved = recording.options["encoding"]["resolved"]
|
||||||
|
assert resolved["width"] == 960
|
||||||
|
assert resolved["height"] == 540
|
||||||
|
assert resolved["framerate"] == 15
|
||||||
|
assert resolved["video_bitrate"] == 400
|
||||||
|
# The requested payload is persisted as sent: no profile was asked for.
|
||||||
|
assert "profile" not in recording.options["encoding"]
|
||||||
|
|
||||||
|
|
||||||
def test_start_recording_forwards_resolved_encoding_to_worker(
|
def test_start_recording_forwards_resolved_encoding_to_worker(
|
||||||
settings, mock_worker_service, mock_worker_service_factory
|
settings, mock_worker_service, mock_worker_service_factory
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -819,7 +819,7 @@ class Base(Configuration):
|
|||||||
environ_prefix=None,
|
environ_prefix=None,
|
||||||
)
|
)
|
||||||
RECORDING_ENCODING_KEY_FRAME_INTERVAL_S = values.FloatValue(
|
RECORDING_ENCODING_KEY_FRAME_INTERVAL_S = values.FloatValue(
|
||||||
4.0,
|
0.0,
|
||||||
environ_name="RECORDING_ENCODING_KEY_FRAME_INTERVAL_S",
|
environ_name="RECORDING_ENCODING_KEY_FRAME_INTERVAL_S",
|
||||||
environ_prefix=None,
|
environ_prefix=None,
|
||||||
)
|
)
|
||||||
@@ -1218,10 +1218,11 @@ class Base(Configuration):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_recording_encoding_maps(cls):
|
def _check_recording_encoding_maps(cls):
|
||||||
"""Ensure the per-recording encoding maps are mutually consistent.
|
"""Ensure the per-recording encoding maps are well-formed and consistent.
|
||||||
|
|
||||||
Every profile in RECORDING_ENCODING_AVAILABLE_PROFILES must define a bitrate for
|
Each entry of RECORDING_ENCODING_AVAILABLE_RESOLUTIONS must declare a width and
|
||||||
each resolution declared in RECORDING_ENCODING_AVAILABLE_RESOLUTIONS.
|
a height, each entry of RECORDING_ENCODING_AVAILABLE_PROFILES an fps and a kbps
|
||||||
|
map, and every profile must define a bitrate for each declared resolution.
|
||||||
|
|
||||||
The default profile / resolution feed the default encoding. When either is
|
The default profile / resolution feed the default encoding. When either is
|
||||||
missing, no custom default encoding can be built: a warning is emitted and
|
missing, no custom default encoding can be built: a warning is emitted and
|
||||||
@@ -1231,10 +1232,29 @@ class Base(Configuration):
|
|||||||
resolutions = set(cls.RECORDING_ENCODING_AVAILABLE_RESOLUTIONS)
|
resolutions = set(cls.RECORDING_ENCODING_AVAILABLE_RESOLUTIONS)
|
||||||
profiles = set(cls.RECORDING_ENCODING_AVAILABLE_PROFILES)
|
profiles = set(cls.RECORDING_ENCODING_AVAILABLE_PROFILES)
|
||||||
# DictValue resolves to a dict at runtime; pylint sees the descriptor.
|
# DictValue resolves to a dict at runtime; pylint sees the descriptor.
|
||||||
|
for (
|
||||||
|
resolution,
|
||||||
|
resolution_config,
|
||||||
|
) in cls.RECORDING_ENCODING_AVAILABLE_RESOLUTIONS.items(): # pylint: disable=no-member
|
||||||
|
missing_keys = {"width", "height"} - set(resolution_config)
|
||||||
|
if missing_keys:
|
||||||
|
raise ValueError(
|
||||||
|
f"Resolution '{resolution}' in "
|
||||||
|
"RECORDING_ENCODING_AVAILABLE_RESOLUTIONS is missing the key(s): "
|
||||||
|
f"{sorted(missing_keys)}."
|
||||||
|
)
|
||||||
|
|
||||||
for (
|
for (
|
||||||
profile,
|
profile,
|
||||||
profile_config,
|
profile_config,
|
||||||
) in cls.RECORDING_ENCODING_AVAILABLE_PROFILES.items(): # pylint: disable=no-member
|
) in cls.RECORDING_ENCODING_AVAILABLE_PROFILES.items(): # pylint: disable=no-member
|
||||||
|
missing_keys = {"fps", "kbps"} - set(profile_config)
|
||||||
|
if missing_keys:
|
||||||
|
raise ValueError(
|
||||||
|
f"Profile '{profile}' in RECORDING_ENCODING_AVAILABLE_PROFILES is "
|
||||||
|
f"missing the key(s): {sorted(missing_keys)}."
|
||||||
|
)
|
||||||
|
|
||||||
profile_resolutions = set(profile_config["kbps"])
|
profile_resolutions = set(profile_config["kbps"])
|
||||||
if profile_resolutions != resolutions:
|
if profile_resolutions != resolutions:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
|
|||||||
Reference in New Issue
Block a user