libmoq 0.6.7

Media over QUIC, C bindings
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
use std::sync::Arc;

use crate::ffi;

// Keep these public constants as the single source for both Rust's return mapping and the
// generated C header. -1, -11, -12, and -39 are retired and remain reserved.
pub const MOQ_ERROR_MOQ: i32 = -2;
pub const MOQ_ERROR_URL: i32 = -3;
pub const MOQ_ERROR_UTF8: i32 = -4;
pub const MOQ_ERROR_CONNECT: i32 = -5;
pub const MOQ_ERROR_INVALID_POINTER: i32 = -6;
pub const MOQ_ERROR_INVALID_ID: i32 = -7;
pub const MOQ_ERROR_NOT_FOUND: i32 = -8;
pub const MOQ_ERROR_UNKNOWN_FORMAT: i32 = -9;
pub const MOQ_ERROR_INIT_FAILED: i32 = -10;
pub const MOQ_ERROR_TIMESTAMP_OVERFLOW: i32 = -13;
pub const MOQ_ERROR_LEVEL: i32 = -14;
pub const MOQ_ERROR_INVALID_CODE: i32 = -15;
pub const MOQ_ERROR_PANIC: i32 = -16;
pub const MOQ_ERROR_OFFLINE: i32 = -17;
pub const MOQ_ERROR_HANG: i32 = -18;
pub const MOQ_ERROR_NO_INDEX: i32 = -19;
pub const MOQ_ERROR_NUL: i32 = -20;
pub const MOQ_ERROR_SESSION_NOT_FOUND: i32 = -21;
pub const MOQ_ERROR_ORIGIN_NOT_FOUND: i32 = -22;
pub const MOQ_ERROR_ANNOUNCEMENT_NOT_FOUND: i32 = -23;
pub const MOQ_ERROR_BROADCAST_NOT_FOUND: i32 = -24;
pub const MOQ_ERROR_CATALOG_NOT_FOUND: i32 = -25;
pub const MOQ_ERROR_MEDIA_NOT_FOUND: i32 = -26;
pub const MOQ_ERROR_TRACK_NOT_FOUND: i32 = -27;
pub const MOQ_ERROR_FRAME_NOT_FOUND: i32 = -28;
pub const MOQ_ERROR_MUX: i32 = -29;
pub const MOQ_ERROR_AUDIO: i32 = -30;
pub const MOQ_ERROR_BUFFER_NOT_CONSUMED: i32 = -31;
pub const MOQ_ERROR_GROUP_NOT_FOUND: i32 = -32;
pub const MOQ_ERROR_NATIVE: i32 = -33;
pub const MOQ_ERROR_UNAUTHORIZED: i32 = -34;
pub const MOQ_ERROR_FORBIDDEN: i32 = -35;
pub const MOQ_ERROR_VIDEO: i32 = -36;
pub const MOQ_ERROR_JSON: i32 = -37;
pub const MOQ_ERROR_JSON_TRACK: i32 = -38;
pub const MOQ_ERROR_INVALID_CONFIG: i32 = -40;
pub const MOQ_ERROR_UNRESOLVABLE_BROADCAST: i32 = -41;

/// Whether a protocol code is from the session or stream registry.
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug)]
pub enum moq_error_scope {
	/// A session close code.
	MOQ_ERROR_SCOPE_SESSION = 0,
	/// A stream reset or stop code.
	MOQ_ERROR_SCOPE_STREAM = 1,
}

/// A recognized protocol kind. Pair with [`moq_error_scope`]: `CANCEL` is 0 on a session
/// and 1 on a stream. `APP` and `UNKNOWN` keep the numeric code in [`moq_protocol_error`].
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug)]
pub enum moq_protocol_kind {
	/// Cancel.
	MOQ_PROTOCOL_KIND_CANCEL = 0,
	/// Internal.
	MOQ_PROTOCOL_KIND_INTERNAL = 1,
	/// Unauthorized.
	MOQ_PROTOCOL_KIND_UNAUTHORIZED = 2,
	/// Protocol violation.
	MOQ_PROTOCOL_KIND_PROTOCOL_VIOLATION = 3,
	/// Key value formatting.
	MOQ_PROTOCOL_KIND_KEY_VALUE_FORMATTING = 4,
	/// Goaway timeout.
	MOQ_PROTOCOL_KIND_GOAWAY_TIMEOUT = 5,
	/// Timeout.
	MOQ_PROTOCOL_KIND_TIMEOUT = 6,
	/// Version.
	MOQ_PROTOCOL_KIND_VERSION = 7,
	// 8 through 10 are unassigned; the values that follow stay put for compiled consumers.
	/// Delivery timeout.
	MOQ_PROTOCOL_KIND_DELIVERY_TIMEOUT = 11,
	/// Session closed.
	MOQ_PROTOCOL_KIND_SESSION_CLOSED = 12,
	/// Going away.
	MOQ_PROTOCOL_KIND_GOING_AWAY = 13,
	/// Too far behind.
	MOQ_PROTOCOL_KIND_TOO_FAR_BEHIND = 14,
	/// Malformed track.
	MOQ_PROTOCOL_KIND_MALFORMED_TRACK = 15,
	/// Not found.
	MOQ_PROTOCOL_KIND_NOT_FOUND = 16,
	/// Unroutable.
	MOQ_PROTOCOL_KIND_UNROUTABLE = 17,
	/// Old.
	MOQ_PROTOCOL_KIND_OLD = 18,
	/// Evicted.
	MOQ_PROTOCOL_KIND_EVICTED = 19,
	/// Wrong size.
	MOQ_PROTOCOL_KIND_WRONG_SIZE = 20,
	/// Frame too large.
	MOQ_PROTOCOL_KIND_FRAME_TOO_LARGE = 21,
	/// Timestamp mismatch.
	MOQ_PROTOCOL_KIND_TIMESTAMP_MISMATCH = 22,
	/// App.
	MOQ_PROTOCOL_KIND_APP = 23,
	/// Unknown.
	MOQ_PROTOCOL_KIND_UNKNOWN = 24,
}

/// A protocol failure a peer sent: scope, verbatim wire code, and recognized kind.
///
/// Filled by [`crate::moq_error_protocol`] after a call returned a negative code. Do not parse
/// [`crate::moq_error`] for this; that string is diagnostics only.
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug)]
pub struct moq_protocol_error {
	/// [`moq_error_scope`] discriminant.
	pub scope: u32,
	/// The integer on the wire, kept verbatim.
	pub code: u32,
	/// [`moq_protocol_kind`] discriminant.
	pub kind: u32,
}

/// Status code returned by FFI functions.
///
/// Negative values indicate errors, zero indicates success,
/// and positive values are valid resource handles.
pub type Status = i32;

/// Error types that can occur in the FFI layer.
///
/// Each error variant maps to a specific negative error code
/// returned to C callers.
#[derive(Debug, thiserror::Error, Clone)]
#[non_exhaustive]
pub enum Error {
	/// Error from the underlying MoQ protocol layer.
	#[error("moq error: {0}")]
	Moq(#[from] moq_net::Error),

	/// Error from the native helper layer (moq-tokio).
	#[error("native error: {0}")]
	Native(#[from] moq_tokio::Error),

	/// URL parsing error.
	#[error("url error: {0}")]
	Url(String),

	/// UTF-8 string validation error.
	#[error("utf8 error: {0}")]
	Utf8(#[from] std::str::Utf8Error),

	/// Connection establishment error.
	#[error("connect error: {0}")]
	Connect(Arc<anyhow::Error>),

	/// Null or invalid pointer passed from C.
	#[error("invalid pointer")]
	InvalidPointer,

	/// Invalid resource ID.
	#[error("invalid id")]
	InvalidId,

	/// Resource not found.
	#[error("not found")]
	NotFound,

	/// Session task not found.
	#[error("session not found")]
	SessionNotFound,

	/// Origin producer not found.
	#[error("origin not found")]
	OriginNotFound,

	/// Announcement not found.
	#[error("announcement not found")]
	AnnouncementNotFound,

	/// Broadcast not found.
	#[error("broadcast not found")]
	BroadcastNotFound,

	/// Catalog not found.
	#[error("catalog not found")]
	CatalogNotFound,

	/// Media decoder not found.
	#[error("media not found")]
	MediaNotFound,

	/// Track task not found.
	#[error("track not found")]
	TrackNotFound,

	/// Group producer not found.
	#[error("group not found")]
	GroupNotFound,

	/// Frame not found.
	#[error("frame not found")]
	FrameNotFound,

	/// Unknown media format specified.
	#[error("unknown format: {0}")]
	UnknownFormat(String),

	/// Initialization failed (e.g. logging setup).
	#[error("init failed: {0}")]
	InitFailed(Arc<anyhow::Error>),

	/// Buffer was not fully consumed.
	#[error("buffer was not fully consumed")]
	BufferNotConsumed,

	/// Timestamp value overflow.
	#[error("timestamp overflow")]
	TimestampOverflow(#[from] moq_net::TimeOverflow),

	/// Log level parsing error.
	#[error("level error: {0}")]
	Level(String),

	/// Invalid error code conversion.
	#[error("invalid code")]
	InvalidCode,

	/// Panic occurred in Rust code.
	#[error("panic")]
	Panic,

	/// Session is offline.
	#[error("offline")]
	Offline,

	/// Connection was rejected as unauthorized by the server.
	#[error("unauthorized")]
	Unauthorized,

	/// Connection was forbidden by the server.
	#[error("forbidden")]
	Forbidden,

	/// Error from the hang media layer.
	#[error("hang error: {0}")]
	Hang(#[from] hang::Error),

	/// Error from the moq-mux consumer layer.
	#[error("mux error: {0}")]
	Mux(#[from] moq_mux::Error),

	/// Index out of bounds.
	#[error("no index")]
	NoIndex,

	/// Null byte found in C string.
	#[error("nul error")]
	NulError(#[from] std::ffi::NulError),

	/// Error from the moq-audio codec layer.
	#[error("audio error: {0}")]
	Audio(Arc<moq_audio::Error>),

	/// Error from the moq-video codec layer.
	#[error("video error: {0}")]
	Video(Arc<moq_video::Error>),

	/// Invalid JSON passed for a catalog section.
	#[error("json error: {0}")]
	Json(String),

	/// Error from the moq-json snapshot/stream layer.
	#[error("json track error: {0}")]
	JsonTrack(Arc<moq_json::Error>),

	/// A client configuration value could not be parsed or initialized.
	#[error("invalid config: {0}")]
	InvalidConfig(String),

	/// A catalog rendition named another broadcast, but the broadcast it came from was not
	/// resolved through an origin, so there is nothing to resolve the reference against.
	#[error("unresolvable broadcast reference: {0}")]
	UnresolvableBroadcast(String),
}

impl From<moq_json::Error> for Error {
	fn from(err: moq_json::Error) -> Self {
		match err {
			moq_json::Error::Net(e) => Error::Moq(e),
			e => Error::JsonTrack(Arc::new(e)),
		}
	}
}

// Dependency errors are flattened to their message so their crates stay out of this crate's
// public API.
impl From<serde_json::Error> for Error {
	fn from(err: serde_json::Error) -> Self {
		Error::Json(err.to_string())
	}
}

impl From<moq_net::InvalidPattern> for Error {
	fn from(err: moq_net::InvalidPattern) -> Self {
		Error::InvalidConfig(err.to_string())
	}
}

impl From<url::ParseError> for Error {
	fn from(err: url::ParseError) -> Self {
		Error::Url(err.to_string())
	}
}

impl From<moq_audio::Error> for Error {
	fn from(err: moq_audio::Error) -> Self {
		Error::Audio(Arc::new(err))
	}
}

impl From<moq_video::Error> for Error {
	fn from(err: moq_video::Error) -> Self {
		Error::Video(Arc::new(err))
	}
}

impl From<tracing::metadata::ParseLevelError> for Error {
	fn from(err: tracing::metadata::ParseLevelError) -> Self {
		Error::Level(err.to_string())
	}
}

impl Error {
	/// Structured protocol details when this is a session or stream code, not a local failure.
	pub(crate) fn protocol(&self) -> Option<moq_protocol_error> {
		std::iter::successors(Some(self as &(dyn std::error::Error + 'static)), |err| err.source())
			.find_map(|err| err.downcast_ref::<moq_net::Error>().and_then(protocol_of_net))
	}
}

fn protocol_of_net(err: &moq_net::Error) -> Option<moq_protocol_error> {
	match err {
		moq_net::Error::Transport(_) => None,
		moq_net::Error::Session(err) => Some(from_session(err)),
		moq_net::Error::Stream(err) => Some(from_stream(err)),
		moq_net::Error::App(app) => Some(from_stream(&moq_net::StreamError::App(*app))),
		_ => None,
	}
}

fn from_session(err: &moq_net::SessionError) -> moq_protocol_error {
	moq_protocol_error {
		scope: moq_error_scope::MOQ_ERROR_SCOPE_SESSION as u32,
		code: err.to_code(),
		kind: session_kind(err) as u32,
	}
}

fn from_stream(err: &moq_net::StreamError) -> moq_protocol_error {
	moq_protocol_error {
		scope: moq_error_scope::MOQ_ERROR_SCOPE_STREAM as u32,
		code: err.to_code(),
		kind: stream_kind(err) as u32,
	}
}

fn session_kind(err: &moq_net::SessionError) -> moq_protocol_kind {
	use moq_protocol_kind::*;
	match err {
		moq_net::SessionError::Cancel => MOQ_PROTOCOL_KIND_CANCEL,
		moq_net::SessionError::Internal => MOQ_PROTOCOL_KIND_INTERNAL,
		moq_net::SessionError::Unauthorized => MOQ_PROTOCOL_KIND_UNAUTHORIZED,
		moq_net::SessionError::ProtocolViolation => MOQ_PROTOCOL_KIND_PROTOCOL_VIOLATION,
		moq_net::SessionError::KeyValueFormatting => MOQ_PROTOCOL_KIND_KEY_VALUE_FORMATTING,
		moq_net::SessionError::GoawayTimeout => MOQ_PROTOCOL_KIND_GOAWAY_TIMEOUT,
		moq_net::SessionError::Timeout => MOQ_PROTOCOL_KIND_TIMEOUT,
		moq_net::SessionError::Version => MOQ_PROTOCOL_KIND_VERSION,
		moq_net::SessionError::App(_) => MOQ_PROTOCOL_KIND_APP,
		moq_net::SessionError::Unknown(_) => MOQ_PROTOCOL_KIND_UNKNOWN,
		_ => MOQ_PROTOCOL_KIND_UNKNOWN,
	}
}

fn stream_kind(err: &moq_net::StreamError) -> moq_protocol_kind {
	use moq_protocol_kind::*;
	match err {
		moq_net::StreamError::Session(_) => MOQ_PROTOCOL_KIND_SESSION_CLOSED,
		moq_net::StreamError::Internal => MOQ_PROTOCOL_KIND_INTERNAL,
		moq_net::StreamError::Cancel => MOQ_PROTOCOL_KIND_CANCEL,
		moq_net::StreamError::DeliveryTimeout => MOQ_PROTOCOL_KIND_DELIVERY_TIMEOUT,
		moq_net::StreamError::GoingAway => MOQ_PROTOCOL_KIND_GOING_AWAY,
		moq_net::StreamError::TooFarBehind => MOQ_PROTOCOL_KIND_TOO_FAR_BEHIND,
		moq_net::StreamError::MalformedTrack => MOQ_PROTOCOL_KIND_MALFORMED_TRACK,
		moq_net::StreamError::NotFound => MOQ_PROTOCOL_KIND_NOT_FOUND,
		moq_net::StreamError::Unroutable => MOQ_PROTOCOL_KIND_UNROUTABLE,
		moq_net::StreamError::Old => MOQ_PROTOCOL_KIND_OLD,
		moq_net::StreamError::Evicted => MOQ_PROTOCOL_KIND_EVICTED,
		moq_net::StreamError::WrongSize => MOQ_PROTOCOL_KIND_WRONG_SIZE,
		moq_net::StreamError::FrameTooLarge => MOQ_PROTOCOL_KIND_FRAME_TOO_LARGE,
		moq_net::StreamError::TimestampMismatch => MOQ_PROTOCOL_KIND_TIMESTAMP_MISMATCH,
		moq_net::StreamError::App(_) => MOQ_PROTOCOL_KIND_APP,
		moq_net::StreamError::Unknown(_) => MOQ_PROTOCOL_KIND_UNKNOWN,
		_ => MOQ_PROTOCOL_KIND_UNKNOWN,
	}
}

impl ffi::ReturnCode for Error {
	fn error(&self) -> Option<&Error> {
		Some(self)
	}

	fn code(&self) -> i32 {
		match self {
			Error::Moq(_) => MOQ_ERROR_MOQ,
			Error::Url(_) => MOQ_ERROR_URL,
			Error::Utf8(_) => MOQ_ERROR_UTF8,
			Error::Connect(_) => MOQ_ERROR_CONNECT,
			Error::InvalidPointer => MOQ_ERROR_INVALID_POINTER,
			Error::InvalidId => MOQ_ERROR_INVALID_ID,
			Error::NotFound => MOQ_ERROR_NOT_FOUND,
			Error::UnknownFormat(_) => MOQ_ERROR_UNKNOWN_FORMAT,
			Error::InitFailed(_) => MOQ_ERROR_INIT_FAILED,
			Error::TimestampOverflow(_) => MOQ_ERROR_TIMESTAMP_OVERFLOW,
			Error::Level(_) => MOQ_ERROR_LEVEL,
			Error::InvalidCode => MOQ_ERROR_INVALID_CODE,
			Error::Panic => MOQ_ERROR_PANIC,
			Error::Offline => MOQ_ERROR_OFFLINE,
			Error::Hang(_) => MOQ_ERROR_HANG,
			Error::NoIndex => MOQ_ERROR_NO_INDEX,
			Error::NulError(_) => MOQ_ERROR_NUL,
			Error::SessionNotFound => MOQ_ERROR_SESSION_NOT_FOUND,
			Error::OriginNotFound => MOQ_ERROR_ORIGIN_NOT_FOUND,
			Error::AnnouncementNotFound => MOQ_ERROR_ANNOUNCEMENT_NOT_FOUND,
			Error::BroadcastNotFound => MOQ_ERROR_BROADCAST_NOT_FOUND,
			Error::CatalogNotFound => MOQ_ERROR_CATALOG_NOT_FOUND,
			Error::MediaNotFound => MOQ_ERROR_MEDIA_NOT_FOUND,
			Error::TrackNotFound => MOQ_ERROR_TRACK_NOT_FOUND,
			Error::FrameNotFound => MOQ_ERROR_FRAME_NOT_FOUND,
			Error::Mux(_) => MOQ_ERROR_MUX,
			Error::Audio(_) => MOQ_ERROR_AUDIO,
			Error::BufferNotConsumed => MOQ_ERROR_BUFFER_NOT_CONSUMED,
			Error::GroupNotFound => MOQ_ERROR_GROUP_NOT_FOUND,
			Error::Native(_) => MOQ_ERROR_NATIVE,
			Error::Unauthorized => MOQ_ERROR_UNAUTHORIZED,
			Error::Forbidden => MOQ_ERROR_FORBIDDEN,
			Error::Video(_) => MOQ_ERROR_VIDEO,
			Error::Json(_) => MOQ_ERROR_JSON,
			Error::JsonTrack(_) => MOQ_ERROR_JSON_TRACK,
			Error::InvalidConfig(_) => MOQ_ERROR_INVALID_CONFIG,
			Error::UnresolvableBroadcast(_) => MOQ_ERROR_UNRESOLVABLE_BROADCAST,
		}
	}
}