Skip to content

Commit 3c9ab73

Browse files
feat: Add quantization parameter for video archives (#327)
1 parent a8dbf81 commit 3c9ab73

File tree

4 files changed

+106
-3
lines changed

4 files changed

+106
-3
lines changed

video/src/vonage_video/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '1.4.0'
1+
__version__ = '1.5.0'

video/src/vonage_video/models/archive.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ class Archive(BaseModel):
7171
transcription (Transcription, Optional): Transcription options for the archive.
7272
max_bitrate (int, Optional): The maximum video bitrate of the archive, in bits per
7373
second. This is only valid for composed archives.
74+
quantization_parameter (int, Optional): Quantization parameter (QP) for video encoding,
75+
smaller values generate higher quality and larger archives, larger values generate
76+
lower quality and smaller archives. Range: 15-40. Only valid for composed archives.
7477
"""
7578

7679
id: Optional[str] = None
@@ -97,6 +100,9 @@ class Archive(BaseModel):
97100
url: Optional[str] = None
98101
transcription: Optional[Transcription] = None
99102
max_bitrate: Optional[int] = Field(None, validation_alias='maxBitrate')
103+
quantization_parameter: Optional[int] = Field(
104+
None, validation_alias='quantizationParameter'
105+
)
100106

101107

102108
class CreateArchiveRequest(BaseModel):
@@ -119,9 +125,12 @@ class CreateArchiveRequest(BaseModel):
119125
automatically ("auto", the default) or manually ("manual").
120126
max_bitrate (int, Optional): The maximum video bitrate of the archive, in bits per
121127
second. This is only valid for composed archives.
128+
quantization_parameter (int, Optional): Quantization parameter (QP) for video encoding,
129+
smaller values generate higher quality and larger archives, larger values generate
130+
lower quality and smaller archives. Range: 15-40. Only valid for composed archives.
122131
Raises:
123132
NoAudioOrVideoError: If neither `has_audio` nor `has_video` is set.
124-
IndividualArchivePropertyError: If `resolution` or `layout` is set for individual archives
133+
IndividualArchivePropertyError: If `resolution`, `layout`, or `quantization_parameter` is set for individual archives
125134
or if `has_transcription` is set for composed archives.
126135
"""
127136

@@ -140,6 +149,9 @@ class CreateArchiveRequest(BaseModel):
140149
max_bitrate: Optional[int] = Field(
141150
None, ge=100_000, le=6_000_000, serialization_alias='maxBitrate'
142151
)
152+
quantization_parameter: Optional[int] = Field(
153+
None, ge=15, le=40, serialization_alias='quantizationParameter'
154+
)
143155

144156
@model_validator(mode='after')
145157
def validate_audio_or_video(self):
@@ -159,6 +171,13 @@ def no_layout_or_resolution_for_individual_archives(self):
159171
raise IndividualArchivePropertyError(
160172
'The `layout` property cannot be set for `archive_mode: \'individual\'`.'
161173
)
174+
if (
175+
self.output_mode == OutputMode.INDIVIDUAL
176+
and self.quantization_parameter is not None
177+
):
178+
raise IndividualArchivePropertyError(
179+
'The `quantization_parameter` property cannot be set for `archive_mode: \'individual\'`.'
180+
)
162181
return self
163182

164183
@model_validator(mode='after')

video/tests/data/archive.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@
2020
"event": "archive",
2121
"resolution": "1280x720",
2222
"url": null,
23-
"maxBitrate": 2000000
23+
"maxBitrate": 2000000,
24+
"quantizationParameter": 25
2425
}

video/tests/test_archive.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,87 @@ def test_create_archive_request_composed_output_mode_with_transcription_error():
8989
)
9090

9191

92+
def test_create_archive_request_valid_quantization_parameter():
93+
"""Test that quantization_parameter is accepted for composed archives with valid
94+
values."""
95+
request = CreateArchiveRequest(
96+
session_id="1_MX40NTY3NjYzMn5-MTQ4MTY3NjYzMn5",
97+
has_audio=True,
98+
has_video=True,
99+
output_mode=OutputMode.COMPOSED,
100+
quantization_parameter=25,
101+
)
102+
assert request.quantization_parameter == 25
103+
104+
105+
def test_create_archive_request_quantization_parameter_boundary_values():
106+
"""Test that quantization_parameter accepts boundary values (15 and 40)."""
107+
# Test minimum value
108+
request_min = CreateArchiveRequest(
109+
session_id="1_MX40NTY3NjYzMn5-MTQ4MTY3NjYzMn5",
110+
has_audio=True,
111+
quantization_parameter=15,
112+
)
113+
assert request_min.quantization_parameter == 15
114+
115+
# Test maximum value
116+
request_max = CreateArchiveRequest(
117+
session_id="1_MX40NTY3NjYzMn5-MTQ4MTY3NjYzMn5",
118+
has_audio=True,
119+
quantization_parameter=40,
120+
)
121+
assert request_max.quantization_parameter == 40
122+
123+
124+
def test_create_archive_request_quantization_parameter_invalid_low():
125+
"""Test that quantization_parameter rejects values below 15."""
126+
with raises(ValueError):
127+
CreateArchiveRequest(
128+
session_id="1_MX40NTY3NjYzMn5-MTQ4MTY3NjYzMn5",
129+
has_audio=True,
130+
quantization_parameter=14,
131+
)
132+
133+
134+
def test_create_archive_request_quantization_parameter_invalid_high():
135+
"""Test that quantization_parameter rejects values above 40."""
136+
with raises(ValueError):
137+
CreateArchiveRequest(
138+
session_id="1_MX40NTY3NjYzMn5-MTQ4MTY3NjYzMn5",
139+
has_audio=True,
140+
quantization_parameter=41,
141+
)
142+
143+
144+
def test_create_archive_request_individual_output_mode_with_quantization_parameter():
145+
"""Test that quantization_parameter is rejected for individual archives."""
146+
with raises(IndividualArchivePropertyError):
147+
CreateArchiveRequest(
148+
session_id="1_MX40NTY3NjYzMn5-MTQ4MTY3NjYzMn5",
149+
has_audio=True,
150+
output_mode=OutputMode.INDIVIDUAL,
151+
quantization_parameter=25,
152+
)
153+
154+
155+
def test_create_archive_request_serialization_with_quantization_parameter():
156+
"""Test that quantization_parameter is properly serialized with the correct alias."""
157+
request = CreateArchiveRequest(
158+
session_id="1_MX40NTY3NjYzMn5-MTQ4MTY3NjYzMn5",
159+
has_audio=True,
160+
has_video=True,
161+
output_mode=OutputMode.COMPOSED,
162+
quantization_parameter=30,
163+
)
164+
165+
serialized = request.model_dump(by_alias=True, exclude_unset=True)
166+
assert 'quantizationParameter' in serialized
167+
assert serialized['quantizationParameter'] == 30
168+
assert (
169+
'quantization_parameter' not in serialized
170+
) # Ensure Python field name is not used
171+
172+
92173
def test_layout_custom_without_stylesheet():
93174
with raises(LayoutStylesheetError):
94175
ComposedLayout(type=LayoutType.CUSTOM)
@@ -194,6 +275,7 @@ def test_start_archive():
194275
assert archive.name == 'first archive test'
195276
assert archive.resolution == '1280x720'
196277
assert archive.max_bitrate == 2_000_000
278+
assert archive.quantization_parameter == 25
197279

198280

199281
@responses.activate
@@ -215,6 +297,7 @@ def test_get_archive():
215297
assert archive.status == 'started'
216298
assert archive.name == 'first archive test'
217299
assert archive.resolution == '1280x720'
300+
assert archive.quantization_parameter == 25
218301

219302

220303
@responses.activate

0 commit comments

Comments
 (0)