janus-api-streaming 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- janus_api_streaming-0.2.0.dist-info/METADATA +57 -0
- janus_api_streaming-0.2.0.dist-info/RECORD +14 -0
- janus_api_streaming-0.2.0.dist-info/WHEEL +4 -0
- janus_api_streaming-0.2.0.dist-info/entry_points.txt +2 -0
- janus_streaming/__init__.py +67 -0
- janus_streaming/_compat.py +638 -0
- janus_streaming/admin.py +211 -0
- janus_streaming/errors.py +39 -0
- janus_streaming/models.py +214 -0
- janus_streaming/py.typed +0 -0
- janus_streaming/requests.py +489 -0
- janus_streaming/responses.py +189 -0
- janus_streaming/runtime.py +143 -0
- janus_streaming/viewer.py +170 -0
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
"""Validated request bodies for :mod:`janus.plugin.streaming`.
|
|
2
|
+
|
|
3
|
+
These models intentionally live in the Streaming distribution rather than in
|
|
4
|
+
``janus-api-core``. Core transports plugin bodies as opaque mappings; this
|
|
5
|
+
module owns the Streaming protocol vocabulary and its field-level validation.
|
|
6
|
+
|
|
7
|
+
The wire field names follow the current Janus multistream API. Deprecated
|
|
8
|
+
single-track create/configure fields are retained for compatibility, while new
|
|
9
|
+
code should prefer ``media`` and ``streams``.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import Annotated, Literal, TypedDict
|
|
15
|
+
|
|
16
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
17
|
+
|
|
18
|
+
type MediaKind = Literal["audio", "video", "data"]
|
|
19
|
+
type MountpointType = Literal["rtp", "live", "ondemand", "rtsp"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Track(TypedDict, total=False):
|
|
23
|
+
"""Wire-level track options accepted by dynamic mountpoint creation."""
|
|
24
|
+
|
|
25
|
+
type: MediaKind
|
|
26
|
+
mid: str
|
|
27
|
+
msid: str | None
|
|
28
|
+
label: str | None
|
|
29
|
+
mcast: str | None
|
|
30
|
+
iface: str | None
|
|
31
|
+
port: int | None
|
|
32
|
+
rtcpport: int | None
|
|
33
|
+
pt: int | None
|
|
34
|
+
codec: str | None
|
|
35
|
+
fmtp: str | None
|
|
36
|
+
skew: bool
|
|
37
|
+
simulcast: bool
|
|
38
|
+
port2: int | None
|
|
39
|
+
port3: int | None
|
|
40
|
+
svc: bool
|
|
41
|
+
h264sps: str | None
|
|
42
|
+
collision: int | None
|
|
43
|
+
datatype: Literal["text", "binary"]
|
|
44
|
+
databuffermsg: bool
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AudioTrack(TypedDict, total=False):
|
|
48
|
+
type: Literal["audio"]
|
|
49
|
+
mid: str
|
|
50
|
+
msid: str | None
|
|
51
|
+
label: str | None
|
|
52
|
+
mcast: str | None
|
|
53
|
+
iface: str | None
|
|
54
|
+
port: int | None
|
|
55
|
+
rtcpport: int | None
|
|
56
|
+
pt: int | None
|
|
57
|
+
codec: str | None
|
|
58
|
+
fmtp: str | None
|
|
59
|
+
skew: bool
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class VideoTrack(TypedDict, total=False):
|
|
63
|
+
type: Literal["video"]
|
|
64
|
+
mid: str
|
|
65
|
+
msid: str | None
|
|
66
|
+
label: str | None
|
|
67
|
+
mcast: str | None
|
|
68
|
+
iface: str | None
|
|
69
|
+
port: int | None
|
|
70
|
+
rtcpport: int | None
|
|
71
|
+
pt: int | None
|
|
72
|
+
codec: str | None
|
|
73
|
+
fmtp: str | None
|
|
74
|
+
skew: bool
|
|
75
|
+
simulcast: bool
|
|
76
|
+
port2: int | None
|
|
77
|
+
port3: int | None
|
|
78
|
+
svc: bool
|
|
79
|
+
h264sps: str | None
|
|
80
|
+
collision: int | None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class DataTrack(TypedDict, total=False):
|
|
84
|
+
type: Literal["data"]
|
|
85
|
+
mid: str
|
|
86
|
+
msid: str | None
|
|
87
|
+
label: str | None
|
|
88
|
+
mcast: str | None
|
|
89
|
+
iface: str | None
|
|
90
|
+
port: int | None
|
|
91
|
+
datatype: Literal["text", "binary"]
|
|
92
|
+
databuffermsg: bool
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
type MediaStream = AudioTrack | VideoTrack | DataTrack
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class MountPoint(TypedDict, total=False):
|
|
99
|
+
"""Keyword options accepted by :meth:`StreamingPlugin.create`.
|
|
100
|
+
|
|
101
|
+
Runtime validation is performed by :class:`CreateRequest`; this TypedDict
|
|
102
|
+
exists to keep the convenience helper discoverable without depending on a
|
|
103
|
+
core-owned named-plugin type.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
type: MountpointType
|
|
107
|
+
id: int
|
|
108
|
+
name: str | None
|
|
109
|
+
description: str | None
|
|
110
|
+
metadata: str | None
|
|
111
|
+
secret: str | None
|
|
112
|
+
pin: str | None
|
|
113
|
+
is_private: bool
|
|
114
|
+
permanent: bool
|
|
115
|
+
enabled: bool
|
|
116
|
+
media: list[MediaStream | CreateMedia]
|
|
117
|
+
filename: str | None
|
|
118
|
+
audio: bool | None
|
|
119
|
+
video: bool | None
|
|
120
|
+
data: bool | None
|
|
121
|
+
audioport: int | None
|
|
122
|
+
audiortcpport: int | None
|
|
123
|
+
audiomcast: str | None
|
|
124
|
+
audioiface: str | None
|
|
125
|
+
audiopt: int | None
|
|
126
|
+
audiocodec: str | None
|
|
127
|
+
audiofmtp: str | None
|
|
128
|
+
audioskew: bool | None
|
|
129
|
+
videoport: int | None
|
|
130
|
+
videortcpport: int | None
|
|
131
|
+
videomcast: str | None
|
|
132
|
+
videoiface: str | None
|
|
133
|
+
videopt: int | None
|
|
134
|
+
videocodec: str | None
|
|
135
|
+
videofmtp: str | None
|
|
136
|
+
videosimulcast: bool | None
|
|
137
|
+
videoport2: int | None
|
|
138
|
+
videoport3: int | None
|
|
139
|
+
videoskew: bool | None
|
|
140
|
+
videosvc: bool | None
|
|
141
|
+
h264sps: str | None
|
|
142
|
+
collision: int | None
|
|
143
|
+
dataport: int | None
|
|
144
|
+
datamcast: str | None
|
|
145
|
+
dataiface: str | None
|
|
146
|
+
datatype: Literal["text", "binary"] | None
|
|
147
|
+
databuffermsg: bool | None
|
|
148
|
+
threads: int | None
|
|
149
|
+
bufferkf_ms: int | None
|
|
150
|
+
bufferkf_bytes: int | None
|
|
151
|
+
srtpsuite: Literal[32, 80] | None
|
|
152
|
+
srtpcrypto: str | None
|
|
153
|
+
e2ee: bool | None
|
|
154
|
+
playoutdelay_ext: bool | None
|
|
155
|
+
abscapturetime_src_ext_id: int | None
|
|
156
|
+
url: str | None
|
|
157
|
+
rtsp_user: str | None
|
|
158
|
+
rtsp_pwd: str | None
|
|
159
|
+
rtsp_quirk: bool | None
|
|
160
|
+
rtsp_failcheck: bool | None
|
|
161
|
+
rtspiface: str | None
|
|
162
|
+
rtsp_reconnect_delay: int | None
|
|
163
|
+
rtsp_session_timeout: int | None
|
|
164
|
+
rtsp_timeout: int | None
|
|
165
|
+
rtsp_conn_timeout: int | None
|
|
166
|
+
rtsp_notify_changes: bool | None
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class StreamingRequestModel(BaseModel):
|
|
170
|
+
"""Strict, immutable base for outbound Streaming request bodies."""
|
|
171
|
+
|
|
172
|
+
model_config = ConfigDict(
|
|
173
|
+
extra="forbid",
|
|
174
|
+
frozen=True,
|
|
175
|
+
populate_by_name=True,
|
|
176
|
+
str_strip_whitespace=True,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class CreateMediaBase(StreamingRequestModel):
|
|
181
|
+
"""Fields shared by tracks in a multistream RTP mountpoint."""
|
|
182
|
+
|
|
183
|
+
type: MediaKind
|
|
184
|
+
mid: str = Field(min_length=1, max_length=64)
|
|
185
|
+
msid: str | None = None
|
|
186
|
+
label: str | None = None
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class CreateAudioMedia(CreateMediaBase):
|
|
190
|
+
type: Literal["audio"] = "audio"
|
|
191
|
+
mcast: str | None = None
|
|
192
|
+
iface: str | None = None
|
|
193
|
+
port: int | None = Field(default=None, ge=1, le=65535)
|
|
194
|
+
rtcpport: int | None = Field(default=None, ge=1, le=65535)
|
|
195
|
+
pt: int | None = Field(default=None, ge=0, le=127)
|
|
196
|
+
codec: str | None = None
|
|
197
|
+
fmtp: str | None = None
|
|
198
|
+
skew: bool = False
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class CreateVideoMedia(CreateMediaBase):
|
|
202
|
+
type: Literal["video"] = "video"
|
|
203
|
+
mcast: str | None = None
|
|
204
|
+
iface: str | None = None
|
|
205
|
+
port: int | None = Field(default=None, ge=1, le=65535)
|
|
206
|
+
rtcpport: int | None = Field(default=None, ge=1, le=65535)
|
|
207
|
+
pt: int | None = Field(default=None, ge=0, le=127)
|
|
208
|
+
codec: str | None = None
|
|
209
|
+
fmtp: str | None = None
|
|
210
|
+
skew: bool = False
|
|
211
|
+
simulcast: bool = False
|
|
212
|
+
port2: int | None = Field(default=None, ge=1, le=65535)
|
|
213
|
+
port3: int | None = Field(default=None, ge=1, le=65535)
|
|
214
|
+
svc: bool = False
|
|
215
|
+
h264sps: str | None = None
|
|
216
|
+
collision: int | None = Field(default=None, ge=0)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class CreateDataMedia(CreateMediaBase):
|
|
220
|
+
type: Literal["data"] = "data"
|
|
221
|
+
mcast: str | None = None
|
|
222
|
+
iface: str | None = None
|
|
223
|
+
port: int | None = Field(default=None, ge=1, le=65535)
|
|
224
|
+
datatype: Literal["text", "binary"] = "text"
|
|
225
|
+
databuffermsg: bool = False
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
type CreateMedia = Annotated[
|
|
229
|
+
CreateAudioMedia | CreateVideoMedia | CreateDataMedia,
|
|
230
|
+
Field(discriminator="type"),
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class RecordingMedia(StreamingRequestModel):
|
|
235
|
+
"""Per-track recording selection for the modern recording API."""
|
|
236
|
+
|
|
237
|
+
mid: str = Field(min_length=1, max_length=64)
|
|
238
|
+
filename: str | None = None
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class ConfigureStream(StreamingRequestModel):
|
|
242
|
+
"""One media-line update in a ``configure`` request."""
|
|
243
|
+
|
|
244
|
+
mid: str = Field(min_length=1, max_length=64)
|
|
245
|
+
send: bool | None = None
|
|
246
|
+
substream: int | None = Field(default=None, ge=0, le=2)
|
|
247
|
+
temporal: int | None = Field(default=None, ge=0, le=2)
|
|
248
|
+
fallback: int | None = Field(default=250_000, ge=0, description="Microseconds")
|
|
249
|
+
spatial_layer: int | None = Field(default=None, ge=0, le=1)
|
|
250
|
+
temporal_layer: int | None = Field(default=None, ge=0, le=2)
|
|
251
|
+
min_delay: int | None = Field(default=None, ge=0)
|
|
252
|
+
max_delay: int | None = Field(default=None, ge=0)
|
|
253
|
+
|
|
254
|
+
@model_validator(mode="after")
|
|
255
|
+
def validate_delay_range(self) -> ConfigureStream:
|
|
256
|
+
if (
|
|
257
|
+
self.min_delay is not None
|
|
258
|
+
and self.max_delay is not None
|
|
259
|
+
and self.min_delay > self.max_delay
|
|
260
|
+
):
|
|
261
|
+
raise ValueError("min_delay must not exceed max_delay")
|
|
262
|
+
return self
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class ListRequest(StreamingRequestModel):
|
|
266
|
+
request: Literal["list"] = "list"
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class InfoRequest(StreamingRequestModel):
|
|
270
|
+
request: Literal["info"] = "info"
|
|
271
|
+
id: int = Field(ge=0)
|
|
272
|
+
secret: str | None = Field(default=None, repr=False)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class CreateRequest(StreamingRequestModel):
|
|
276
|
+
"""Create an RTP, RTSP, live-file, or on-demand mountpoint."""
|
|
277
|
+
|
|
278
|
+
request: Literal["create"] = "create"
|
|
279
|
+
admin_key: str | None = Field(default=None, repr=False)
|
|
280
|
+
type: MountpointType
|
|
281
|
+
id: int | None = Field(default=None, ge=0)
|
|
282
|
+
name: str | None = None
|
|
283
|
+
description: str | None = None
|
|
284
|
+
metadata: str | None = None
|
|
285
|
+
secret: str | None = Field(default=None, repr=False)
|
|
286
|
+
pin: str | None = Field(default=None, repr=False)
|
|
287
|
+
is_private: bool = True
|
|
288
|
+
permanent: bool = False
|
|
289
|
+
enabled: bool = False
|
|
290
|
+
media: list[CreateMedia] = Field(default_factory=list)
|
|
291
|
+
|
|
292
|
+
# File mountpoints.
|
|
293
|
+
filename: str | None = None
|
|
294
|
+
audio: bool | None = None
|
|
295
|
+
video: bool | None = None
|
|
296
|
+
data: bool | None = None
|
|
297
|
+
|
|
298
|
+
# Legacy single-track RTP syntax.
|
|
299
|
+
audioport: int | None = Field(default=None, ge=1, le=65535)
|
|
300
|
+
audiortcpport: int | None = Field(default=None, ge=1, le=65535)
|
|
301
|
+
audiomcast: str | None = None
|
|
302
|
+
audioiface: str | None = None
|
|
303
|
+
audiopt: int | None = Field(default=None, ge=0, le=127)
|
|
304
|
+
audiocodec: str | None = None
|
|
305
|
+
audiofmtp: str | None = None
|
|
306
|
+
audioskew: bool | None = None
|
|
307
|
+
videoport: int | None = Field(default=None, ge=1, le=65535)
|
|
308
|
+
videortcpport: int | None = Field(default=None, ge=1, le=65535)
|
|
309
|
+
videomcast: str | None = None
|
|
310
|
+
videoiface: str | None = None
|
|
311
|
+
videopt: int | None = Field(default=None, ge=0, le=127)
|
|
312
|
+
videocodec: str | None = None
|
|
313
|
+
videofmtp: str | None = None
|
|
314
|
+
videosimulcast: bool | None = None
|
|
315
|
+
videoport2: int | None = Field(default=None, ge=1, le=65535)
|
|
316
|
+
videoport3: int | None = Field(default=None, ge=1, le=65535)
|
|
317
|
+
videoskew: bool | None = None
|
|
318
|
+
videosvc: bool | None = None
|
|
319
|
+
h264sps: str | None = None
|
|
320
|
+
collision: int | None = Field(default=None, ge=0)
|
|
321
|
+
dataport: int | None = Field(default=None, ge=1, le=65535)
|
|
322
|
+
datamcast: str | None = None
|
|
323
|
+
dataiface: str | None = None
|
|
324
|
+
datatype: Literal["text", "binary"] | None = None
|
|
325
|
+
databuffermsg: bool | None = None
|
|
326
|
+
|
|
327
|
+
threads: int | None = Field(default=None, ge=0)
|
|
328
|
+
bufferkf_ms: int | None = Field(default=None, ge=0)
|
|
329
|
+
bufferkf_bytes: int | None = Field(default=None, ge=0)
|
|
330
|
+
srtpsuite: Literal[32, 80] | None = None
|
|
331
|
+
srtpcrypto: str | None = Field(default=None, repr=False)
|
|
332
|
+
e2ee: bool | None = None
|
|
333
|
+
playoutdelay_ext: bool | None = None
|
|
334
|
+
abscapturetime_src_ext_id: int | None = Field(default=None, ge=1, le=14)
|
|
335
|
+
|
|
336
|
+
# RTSP mountpoints.
|
|
337
|
+
url: str | None = None
|
|
338
|
+
rtsp_user: str | None = None
|
|
339
|
+
rtsp_pwd: str | None = Field(default=None, repr=False)
|
|
340
|
+
rtsp_quirk: bool | None = None
|
|
341
|
+
rtsp_failcheck: bool | None = None
|
|
342
|
+
rtspiface: str | None = None
|
|
343
|
+
rtsp_reconnect_delay: int | None = Field(default=None, ge=0)
|
|
344
|
+
rtsp_session_timeout: int | None = Field(default=None, ge=0)
|
|
345
|
+
rtsp_timeout: int | None = Field(default=None, ge=0)
|
|
346
|
+
rtsp_conn_timeout: int | None = Field(default=None, ge=0)
|
|
347
|
+
rtsp_notify_changes: bool | None = None
|
|
348
|
+
|
|
349
|
+
@model_validator(mode="after")
|
|
350
|
+
def validate_srtp_pair(self) -> CreateRequest:
|
|
351
|
+
if (self.srtpsuite is None) != (self.srtpcrypto is None):
|
|
352
|
+
raise ValueError("srtpsuite and srtpcrypto must be provided together")
|
|
353
|
+
return self
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class DestroyRequest(StreamingRequestModel):
|
|
357
|
+
request: Literal["destroy"] = "destroy"
|
|
358
|
+
id: int = Field(ge=0)
|
|
359
|
+
secret: str | None = Field(default=None, repr=False)
|
|
360
|
+
permanent: bool = False
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class EditRequest(StreamingRequestModel):
|
|
364
|
+
request: Literal["edit"] = "edit"
|
|
365
|
+
id: int = Field(ge=0)
|
|
366
|
+
secret: str | None = Field(default=None, repr=False)
|
|
367
|
+
new_description: str | None = None
|
|
368
|
+
new_metadata: str | None = None
|
|
369
|
+
new_secret: str | None = Field(default=None, repr=False)
|
|
370
|
+
new_pin: str | None = Field(default=None, repr=False)
|
|
371
|
+
new_is_private: bool | None = None
|
|
372
|
+
permanent: bool = False
|
|
373
|
+
edited_event: bool = False
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
class EnableRequest(StreamingRequestModel):
|
|
377
|
+
request: Literal["enable"] = "enable"
|
|
378
|
+
id: int = Field(ge=0)
|
|
379
|
+
secret: str | None = Field(default=None, repr=False)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
class DisableRequest(StreamingRequestModel):
|
|
383
|
+
request: Literal["disable"] = "disable"
|
|
384
|
+
id: int = Field(ge=0)
|
|
385
|
+
stop_recording: bool = True
|
|
386
|
+
secret: str | None = Field(default=None, repr=False)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class KickAllRequest(StreamingRequestModel):
|
|
390
|
+
request: Literal["kick_all"] = "kick_all"
|
|
391
|
+
id: int = Field(ge=0)
|
|
392
|
+
secret: str | None = Field(default=None, repr=False)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
class RecordingRequest(StreamingRequestModel):
|
|
396
|
+
request: Literal["recording"] = "recording"
|
|
397
|
+
action: Literal["start", "stop"]
|
|
398
|
+
id: int = Field(ge=0)
|
|
399
|
+
media: list[RecordingMedia] = Field(default_factory=list)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
class WatchRequest(StreamingRequestModel):
|
|
403
|
+
request: Literal["watch"] = "watch"
|
|
404
|
+
id: int = Field(ge=0)
|
|
405
|
+
pin: str | None = Field(default=None, repr=False)
|
|
406
|
+
media: list[str] = Field(default_factory=list)
|
|
407
|
+
offer_audio: bool | None = None
|
|
408
|
+
offer_video: bool | None = None
|
|
409
|
+
offer_data: bool | None = None
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
class StartRequest(StreamingRequestModel):
|
|
413
|
+
request: Literal["start"] = "start"
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
class PauseRequest(StreamingRequestModel):
|
|
417
|
+
request: Literal["pause"] = "pause"
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class ConfigureRequest(StreamingRequestModel):
|
|
421
|
+
request: Literal["configure"] = "configure"
|
|
422
|
+
streams: list[ConfigureStream] = Field(default_factory=list)
|
|
423
|
+
audio: bool | None = None
|
|
424
|
+
video: bool | None = None
|
|
425
|
+
data: bool | None = None
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
class SwitchRequest(StreamingRequestModel):
|
|
429
|
+
request: Literal["switch"] = "switch"
|
|
430
|
+
id: int = Field(ge=0)
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
class StopRequest(StreamingRequestModel):
|
|
434
|
+
request: Literal["stop"] = "stop"
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
type StreamingRequest = (
|
|
438
|
+
ListRequest
|
|
439
|
+
| InfoRequest
|
|
440
|
+
| CreateRequest
|
|
441
|
+
| DestroyRequest
|
|
442
|
+
| EditRequest
|
|
443
|
+
| EnableRequest
|
|
444
|
+
| DisableRequest
|
|
445
|
+
| KickAllRequest
|
|
446
|
+
| RecordingRequest
|
|
447
|
+
| WatchRequest
|
|
448
|
+
| StartRequest
|
|
449
|
+
| PauseRequest
|
|
450
|
+
| ConfigureRequest
|
|
451
|
+
| SwitchRequest
|
|
452
|
+
| StopRequest
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
__all__ = (
|
|
457
|
+
"AudioTrack",
|
|
458
|
+
"ConfigureRequest",
|
|
459
|
+
"ConfigureStream",
|
|
460
|
+
"CreateAudioMedia",
|
|
461
|
+
"CreateDataMedia",
|
|
462
|
+
"CreateMedia",
|
|
463
|
+
"CreateMediaBase",
|
|
464
|
+
"CreateRequest",
|
|
465
|
+
"CreateVideoMedia",
|
|
466
|
+
"DataTrack",
|
|
467
|
+
"DestroyRequest",
|
|
468
|
+
"DisableRequest",
|
|
469
|
+
"EditRequest",
|
|
470
|
+
"EnableRequest",
|
|
471
|
+
"InfoRequest",
|
|
472
|
+
"KickAllRequest",
|
|
473
|
+
"ListRequest",
|
|
474
|
+
"MediaKind",
|
|
475
|
+
"MediaStream",
|
|
476
|
+
"MountPoint",
|
|
477
|
+
"MountpointType",
|
|
478
|
+
"PauseRequest",
|
|
479
|
+
"RecordingMedia",
|
|
480
|
+
"RecordingRequest",
|
|
481
|
+
"StartRequest",
|
|
482
|
+
"StopRequest",
|
|
483
|
+
"StreamingRequest",
|
|
484
|
+
"StreamingRequestModel",
|
|
485
|
+
"SwitchRequest",
|
|
486
|
+
"Track",
|
|
487
|
+
"VideoTrack",
|
|
488
|
+
"WatchRequest",
|
|
489
|
+
)
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""Forward-compatible response bodies for :mod:`janus.plugin.streaming`.
|
|
2
|
+
|
|
3
|
+
Janus wraps these bodies in its generic plugin-data envelope. The named
|
|
4
|
+
response vocabulary belongs here, while ``janus-api-core`` remains responsible
|
|
5
|
+
only for parsing and transporting the outer Janus envelope.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Literal
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
13
|
+
|
|
14
|
+
from .models import SessionDescription
|
|
15
|
+
from .requests import MediaKind, MountpointType
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class StreamingResponseModel(BaseModel):
|
|
19
|
+
"""Base that tolerates fields introduced by newer Janus releases."""
|
|
20
|
+
|
|
21
|
+
model_config = ConfigDict(
|
|
22
|
+
extra="allow",
|
|
23
|
+
frozen=True,
|
|
24
|
+
populate_by_name=True,
|
|
25
|
+
str_strip_whitespace=True,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class MediaSummary(StreamingResponseModel):
|
|
30
|
+
mid: str
|
|
31
|
+
label: str | None = None
|
|
32
|
+
msid: str | None = None
|
|
33
|
+
type: MediaKind
|
|
34
|
+
age_ms: int | None = Field(default=None, ge=0)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class MediaInfo(MediaSummary):
|
|
38
|
+
mindex: int | None = Field(default=None, ge=0)
|
|
39
|
+
pt: int | None = Field(default=None, ge=0, le=127)
|
|
40
|
+
codec: str | None = None
|
|
41
|
+
rtpmap: str | None = None
|
|
42
|
+
fmtp: str | None = None
|
|
43
|
+
port: int | None = Field(default=None, ge=1, le=65535)
|
|
44
|
+
rtp_port: int | None = Field(default=None, ge=1, le=65535)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class MountPointSummary(StreamingResponseModel):
|
|
48
|
+
id: int = Field(ge=0)
|
|
49
|
+
type: MountpointType
|
|
50
|
+
description: str | None = None
|
|
51
|
+
metadata: str | None = None
|
|
52
|
+
enabled: bool | None = None
|
|
53
|
+
media: list[MediaSummary] = Field(default_factory=list)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class MountPointInfo(StreamingResponseModel):
|
|
57
|
+
id: int = Field(ge=0)
|
|
58
|
+
name: str | None = None
|
|
59
|
+
description: str | None = None
|
|
60
|
+
metadata: str | None = None
|
|
61
|
+
secret: str | None = Field(default=None, repr=False)
|
|
62
|
+
pin: str | None = Field(default=None, repr=False)
|
|
63
|
+
is_private: bool | None = None
|
|
64
|
+
viewers: int | None = Field(default=None, ge=0)
|
|
65
|
+
enabled: bool | None = None
|
|
66
|
+
type: MountpointType
|
|
67
|
+
media: list[MediaInfo] = Field(default_factory=list)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class CreatedPort(StreamingResponseModel):
|
|
71
|
+
type: MediaKind
|
|
72
|
+
mid: str
|
|
73
|
+
msid: str | None = None
|
|
74
|
+
port: int = Field(ge=1, le=65535)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class CreatedMountPoint(StreamingResponseModel):
|
|
78
|
+
id: int = Field(ge=0)
|
|
79
|
+
type: MountpointType
|
|
80
|
+
description: str | None = None
|
|
81
|
+
is_private: bool | None = None
|
|
82
|
+
ports: list[CreatedPort] = Field(default_factory=list)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ListResponse(StreamingResponseModel):
|
|
86
|
+
streaming: Literal["list"] = "list"
|
|
87
|
+
mountpoints: list[MountPointSummary] = Field(alias="list", default_factory=list)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class InfoResponse(StreamingResponseModel):
|
|
91
|
+
streaming: Literal["info"] = "info"
|
|
92
|
+
mountpoint: MountPointInfo = Field(alias="info")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class CreatedResponse(StreamingResponseModel):
|
|
96
|
+
streaming: Literal["created"] = "created"
|
|
97
|
+
create: str
|
|
98
|
+
permanent: bool = False
|
|
99
|
+
stream: CreatedMountPoint
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class EditedResponse(StreamingResponseModel):
|
|
103
|
+
streaming: Literal["edited"] = "edited"
|
|
104
|
+
id: int = Field(ge=0)
|
|
105
|
+
permanent: bool | None = None
|
|
106
|
+
metadata: str | None = None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class DestroyedResponse(StreamingResponseModel):
|
|
110
|
+
streaming: Literal["destroyed"] = "destroyed"
|
|
111
|
+
id: int = Field(ge=0)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class OkResponse(StreamingResponseModel):
|
|
115
|
+
streaming: Literal["ok"] = "ok"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class KickedAllResponse(StreamingResponseModel):
|
|
119
|
+
streaming: Literal["kicked_all"] = "kicked_all"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class PreparingResponse(StreamingResponseModel):
|
|
123
|
+
status: Literal["preparing"] = "preparing"
|
|
124
|
+
jsep: SessionDescription | None = None
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class StartingResponse(StreamingResponseModel):
|
|
128
|
+
status: Literal["starting"] = "starting"
|
|
129
|
+
jsep: SessionDescription | None = None
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class PausingResponse(StreamingResponseModel):
|
|
133
|
+
status: Literal["pausing"] = "pausing"
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class StoppingResponse(StreamingResponseModel):
|
|
137
|
+
status: Literal["stopping"] = "stopping"
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class SwitchedResponse(StreamingResponseModel):
|
|
141
|
+
switched: Literal["ok"] = "ok"
|
|
142
|
+
id: int = Field(ge=0)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class StreamingErrorResponse(StreamingResponseModel):
|
|
146
|
+
error_code: int | None = None
|
|
147
|
+
error: str
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
type StreamingResponse = (
|
|
151
|
+
ListResponse
|
|
152
|
+
| InfoResponse
|
|
153
|
+
| CreatedResponse
|
|
154
|
+
| EditedResponse
|
|
155
|
+
| DestroyedResponse
|
|
156
|
+
| OkResponse
|
|
157
|
+
| KickedAllResponse
|
|
158
|
+
| PreparingResponse
|
|
159
|
+
| StartingResponse
|
|
160
|
+
| PausingResponse
|
|
161
|
+
| StoppingResponse
|
|
162
|
+
| SwitchedResponse
|
|
163
|
+
| StreamingErrorResponse
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
__all__ = (
|
|
168
|
+
"CreatedMountPoint",
|
|
169
|
+
"CreatedPort",
|
|
170
|
+
"CreatedResponse",
|
|
171
|
+
"DestroyedResponse",
|
|
172
|
+
"EditedResponse",
|
|
173
|
+
"InfoResponse",
|
|
174
|
+
"KickedAllResponse",
|
|
175
|
+
"ListResponse",
|
|
176
|
+
"MediaInfo",
|
|
177
|
+
"MediaSummary",
|
|
178
|
+
"MountPointInfo",
|
|
179
|
+
"MountPointSummary",
|
|
180
|
+
"OkResponse",
|
|
181
|
+
"PausingResponse",
|
|
182
|
+
"PreparingResponse",
|
|
183
|
+
"StartingResponse",
|
|
184
|
+
"StoppingResponse",
|
|
185
|
+
"StreamingErrorResponse",
|
|
186
|
+
"StreamingResponse",
|
|
187
|
+
"StreamingResponseModel",
|
|
188
|
+
"SwitchedResponse",
|
|
189
|
+
)
|