Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cpp/obs/src/moq-output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,10 +301,10 @@ void MoQOutput::Reset()
}
audio_tracks.clear();

// Finish the broadcast so the origin unpublishes it immediately; Start()
// Close the broadcast so the origin retracts it immediately; Start()
// creates a fresh one on restart.
if (broadcast > 0) {
moq_publish_finish(broadcast);
moq_publish_close(broadcast);
broadcast = 0;
}
}
Expand Down
2 changes: 1 addition & 1 deletion cpp/obs/test/moq-output-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ int32_t moq_publish_announce(uint32_t, const moq_route *)
return 0;
}

int32_t moq_publish_finish(uint32_t)
int32_t moq_publish_close(uint32_t)
{
return 0;
}
Expand Down
3 changes: 2 additions & 1 deletion dart/moq/test/moq_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ void main() {
client.close();
serverSession.cancel(code: 0);
track.finish();
broadcast.finish();
broadcast.close();
broadcast.close(); // a second close is a no-op
server.close();
});

Expand Down
48 changes: 23 additions & 25 deletions dart/moq_ffi/lib/src/moq.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ import "dart:ffi";
import "dart:io" show Platform, File, Directory;
import "dart:isolate";
import "dart:typed_data";

import "package:ffi/ffi.dart";

import "uniffi_runtime.dart";
export "uniffi_runtime.dart";

Expand Down Expand Up @@ -5900,9 +5898,9 @@ abstract class MoqBroadcastProducerInterface {
required MoqJsonStreamConfig config,
});
void announce({required MoqRoute route});
void close();
MoqBroadcastConsumer consume();
MoqBroadcastDynamic dynamic_();
void finish();
MoqMediaProducer publishAudio({required MoqAudioInit init});
MoqMediaProducer publishAudioOnTrack({
required MoqTrackRequest request,
Expand Down Expand Up @@ -6007,6 +6005,15 @@ class MoqBroadcastProducer implements MoqBroadcastProducerInterface {
}, moqExceptionErrorHandler);
}

void close() {
return rustCall((status) {
uniffi_moq_ffi_fn_method_moqbroadcastproducer_close(
uniffiClonePointer(),
status,
);
}, moqExceptionErrorHandler);
}

MoqBroadcastConsumer consume() {
return rustCallWithLifter(
(status) => uniffi_moq_ffi_fn_method_moqbroadcastproducer_consume(
Expand All @@ -6029,15 +6036,6 @@ class MoqBroadcastProducer implements MoqBroadcastProducerInterface {
);
}

void finish() {
return rustCall((status) {
uniffi_moq_ffi_fn_method_moqbroadcastproducer_finish(
uniffiClonePointer(),
status,
);
}, moqExceptionErrorHandler);
}

MoqMediaProducer publishAudio({required MoqAudioInit init}) {
return rustCallWithLifter(
(status) => uniffi_moq_ffi_fn_method_moqbroadcastproducer_publish_audio(
Expand Down Expand Up @@ -10173,26 +10171,26 @@ external void uniffi_moq_ffi_fn_method_moqbroadcastproducer_announce(
Pointer<RustCallStatus> uniffiStatus,
);

@Native<Pointer<Void> Function(Pointer<Void>, Pointer<RustCallStatus>)>(
@Native<Void Function(Pointer<Void>, Pointer<RustCallStatus>)>(
assetId: _uniffiAssetId,
)
external Pointer<Void> uniffi_moq_ffi_fn_method_moqbroadcastproducer_consume(
external void uniffi_moq_ffi_fn_method_moqbroadcastproducer_close(
Pointer<Void> ptr,
Pointer<RustCallStatus> uniffiStatus,
);

@Native<Pointer<Void> Function(Pointer<Void>, Pointer<RustCallStatus>)>(
assetId: _uniffiAssetId,
)
external Pointer<Void> uniffi_moq_ffi_fn_method_moqbroadcastproducer_dynamic(
external Pointer<Void> uniffi_moq_ffi_fn_method_moqbroadcastproducer_consume(
Pointer<Void> ptr,
Pointer<RustCallStatus> uniffiStatus,
);

@Native<Void Function(Pointer<Void>, Pointer<RustCallStatus>)>(
@Native<Pointer<Void> Function(Pointer<Void>, Pointer<RustCallStatus>)>(
assetId: _uniffiAssetId,
)
external void uniffi_moq_ffi_fn_method_moqbroadcastproducer_finish(
external Pointer<Void> uniffi_moq_ffi_fn_method_moqbroadcastproducer_dynamic(
Pointer<Void> ptr,
Pointer<RustCallStatus> uniffiStatus,
);
Expand Down Expand Up @@ -11826,13 +11824,13 @@ uniffi_moq_ffi_checksum_method_moqbroadcastproducer_publish_json_stream();
external int uniffi_moq_ffi_checksum_method_moqbroadcastproducer_announce();

@Native<Uint16 Function()>(assetId: _uniffiAssetId)
external int uniffi_moq_ffi_checksum_method_moqbroadcastproducer_consume();
external int uniffi_moq_ffi_checksum_method_moqbroadcastproducer_close();

@Native<Uint16 Function()>(assetId: _uniffiAssetId)
external int uniffi_moq_ffi_checksum_method_moqbroadcastproducer_dynamic();
external int uniffi_moq_ffi_checksum_method_moqbroadcastproducer_consume();

@Native<Uint16 Function()>(assetId: _uniffiAssetId)
external int uniffi_moq_ffi_checksum_method_moqbroadcastproducer_finish();
external int uniffi_moq_ffi_checksum_method_moqbroadcastproducer_dynamic();

@Native<Uint16 Function()>(assetId: _uniffiAssetId)
external int
Expand Down Expand Up @@ -12335,7 +12333,7 @@ void _checkApiChecksums() {
throw UniffiInternalError.panicked("UniFFI API checksum mismatch");
}
if (uniffi_moq_ffi_checksum_method_moqannouncedbroadcast_available() !=
42497) {
37458) {
throw UniffiInternalError.panicked("UniFFI API checksum mismatch");
}
if (uniffi_moq_ffi_checksum_method_moqannouncedbroadcast_cancel() != 63175) {
Expand Down Expand Up @@ -12375,7 +12373,7 @@ void _checkApiChecksums() {
throw UniffiInternalError.panicked("UniFFI API checksum mismatch");
}
if (uniffi_moq_ffi_checksum_method_moqoriginproducer_create_broadcast() !=
48971) {
45756) {
throw UniffiInternalError.panicked("UniFFI API checksum mismatch");
}
if (uniffi_moq_ffi_checksum_method_moqoriginproducer_dynamic() != 56233) {
Expand All @@ -12399,13 +12397,13 @@ void _checkApiChecksums() {
if (uniffi_moq_ffi_checksum_method_moqbroadcastproducer_announce() != 13700) {
throw UniffiInternalError.panicked("UniFFI API checksum mismatch");
}
if (uniffi_moq_ffi_checksum_method_moqbroadcastproducer_consume() != 27634) {
if (uniffi_moq_ffi_checksum_method_moqbroadcastproducer_close() != 19191) {
throw UniffiInternalError.panicked("UniFFI API checksum mismatch");
}
if (uniffi_moq_ffi_checksum_method_moqbroadcastproducer_dynamic() != 55635) {
if (uniffi_moq_ffi_checksum_method_moqbroadcastproducer_consume() != 27634) {
throw UniffiInternalError.panicked("UniFFI API checksum mismatch");
}
if (uniffi_moq_ffi_checksum_method_moqbroadcastproducer_finish() != 7183) {
if (uniffi_moq_ffi_checksum_method_moqbroadcastproducer_dynamic() != 55635) {
throw UniffiInternalError.panicked("UniFFI API checksum mismatch");
}
if (uniffi_moq_ffi_checksum_method_moqbroadcastproducer_publish_audio() !=
Expand Down
2 changes: 1 addition & 1 deletion doc/lib/c/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ and `target/include/moq.h`.
- **Server.** `moq_server_listen` binds before it returns (a bad address or certificate fails there) and hands each incoming session to `on_request` as a request handle. Read `moq_session_request_path` and `_query` to route and authenticate, then `moq_session_request_accept` (a session handle, with origins like `moq_session_connect`) or `moq_session_request_reject` with an HTTP-style code (401 and 403 become the protocol's unauthorized close). An accepted session reports `1` once SETUP completes and never reconnects. `moq_server_addr` reports an ephemeral port and `moq_server_fingerprints` the hashes a client pins for a `tls_generate` certificate. `moq_server_close` stops listening; its terminal callback fires once the sockets are released.
- **Demand.** A watcher on a published track (`moq_publish_track_demand`, `moq_publish_media_demand`, `moq_encode_video_demand`, `moq_encode_audio_demand`) calls `on_demand` with `MOQ_DEMAND_USED` or `MOQ_DEMAND_UNUSED` right away and again on every change, so an encoder on a battery-powered device runs only while someone is watching. The first call is the current state, so a track that went unused before the watcher existed still reports it. `moq_publish_demand_cancel` stops it; the terminal callback still fires. A container has no single demand and is refused. Demand follows the last real subscriber: an origin that served the track drops its source copy on the unused edge and keeps only the finished groups it already cached warm for 30 seconds, so the cache linger does not delay the unused edge.
- **Requests.** `moq_publish_dynamic` serves subscriptions to tracks the broadcast never declared: each arrives as a request handle, read its name with `moq_track_request_name`, then `moq_track_request_accept` (a raw track handle), `moq_track_request_video` / `_audio` (the media handle `moq_publish_video` / `_audio` return), or `moq_track_request_abort` with an application code the subscriber sees. Without a live handler an unknown name is refused. `moq_publish_track_dynamic` does the same for fetches of groups a track no longer has cached, delivered as `moq_group_request_*` (`sequence`, `priority`, `frame_start`); `moq_group_request_accept` starts the producer at `frame_start` so written frames keep their group indices. Register it with `moq_track_request_dynamic` before accepting a track that was itself requested by a fetch, so that pending group survives the transition. Both handlers stop with `moq_publish_dynamic_cancel`.
- **Everything the bindings can do** ([list](/lib/#what-every-binding-can-do)): media publish and consume with the catalog managed for you, raw pixels and PCM with the codec inside (`moq_encode_video`, `moq_encode_audio`, and the `moq_decode_*` mirrors), raw tracks with timestamps and datagrams, JSON and binary data tracks (snapshot or stream, each advertised in the catalog for as long as it lives), group fetch, catalog sections, shared video properties, and stalled hints. The three advertising operations are `moq_origin_create_broadcast` (unannounced producer, invisible to everyone), `moq_publish_announce` / `moq_publish_unannounce` (exact-path advertisement), and `moq_origin_dynamic` (a claim over a path prefix and everything beneath it; `""` for everything). A route is a capability, not an inventory. `moq_origin_announced` takes a literal prefix and an optional relative pattern filter; `moq_announce_update.prefix` stays relative to the origin, while `captures` reports what each wildcard matched when `has_captures` is true. Paths with a `.`-prefixed segment below the prefix are [hidden](/concept/moq-lite#hidden-broadcasts); name the dot segment in `prefix` to list them.
- **Everything the bindings can do** ([list](/lib/#what-every-binding-can-do)): media publish and consume with the catalog managed for you, raw pixels and PCM with the codec inside (`moq_encode_video`, `moq_encode_audio`, and the `moq_decode_*` mirrors), raw tracks with timestamps and datagrams, JSON and binary data tracks (snapshot or stream, each advertised in the catalog for as long as it lives), group fetch, catalog sections, shared video properties, and stalled hints. The three advertising operations are `moq_origin_create_broadcast` (unannounced producer, invisible to everyone), `moq_publish_announce` / `moq_publish_unannounce` (exact-path advertisement), `moq_publish_close` (ends the broadcast for good and releases its handle), and `moq_origin_dynamic` (a claim over a path prefix and everything beneath it; `""` for everything). A route is a capability, not an inventory. `moq_origin_announced` takes a literal prefix and an optional relative pattern filter; `moq_announce_update.prefix` stays relative to the origin, while `captures` reports what each wildcard matched when `has_captures` is true. Paths with a `.`-prefixed segment below the prefix are [hidden](/concept/moq-lite#hidden-broadcasts); name the dot segment in `prefix` to list them.

```c
moq_client_config config;
Expand Down
3 changes: 2 additions & 1 deletion doc/lib/dart/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ await for (final request in server.requests()) {
The three advertising operations: `moq.createBroadcast(path)` (or
`origin.createBroadcast`) returns an unannounced producer, invisible to everyone;
`broadcast.announce(route:)` / `broadcast.unannounce()` own that exact-path
advertisement; `origin.dynamic_(prefix:, route:)` claims `prefix` and
advertisement, and `broadcast.close()` ends the broadcast for good (a second
call is a no-op); `origin.dynamic_(prefix:, route:)` claims `prefix` and
every path beneath it (`''` for everything; Dart spells the origin method
`dynamic_` because `dynamic` is reserved). Hold the returned handle while the
claim should stay advertised, and reject the requests you will not serve. A
Expand Down
5 changes: 3 additions & 2 deletions doc/lib/go/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,16 @@ video, _ := broadcast.EncodeVideo(
)
_ = video.Write(moq.VideoFrame{TimestampUs: pts, Data: rgba})
_ = broadcast.Announce(moq.Route{})
broadcast.Finish() // keep the producer reachable while publishing, then finish explicitly
broadcast.Close() // keep the producer reachable while publishing, then close explicitly
```

For locally encoded media, call `MediaProducer.Flush(timestampUs)` after `WriteFrame` with the same broadcast-clock PTS. It measures catalog jitter at the transport handoff. File, pipe, and network imports should omit `Flush`; built-in encoders observe their own output.

The three advertising operations: `client.CreateBroadcast(path)` (or
`origin.CreateBroadcast`) returns an unannounced producer, invisible to everyone;
`broadcast.Announce(route)` / `broadcast.Unannounce()` own that exact-path
advertisement; `origin.Dynamic(prefix, route)` claims `prefix` and every
advertisement, and `broadcast.Close()` ends the broadcast for good (a second
call is a no-op); `origin.Dynamic(prefix, route)` claims `prefix` and every
path beneath it (`""` for everything). Hold the returned `OriginDynamic`
while the claim should stay advertised, and reject the requests you will not
serve. A route is a capability, not an inventory. `Announced(options)` combines
Expand Down
4 changes: 3 additions & 1 deletion doc/lib/kt/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ Moq.connect("https://relay.example.com").use { moq ->
The three advertising operations: `moq.createBroadcast(path)` (or
`origin.createBroadcast`) returns an unannounced producer, invisible to everyone;
`broadcast.announce(route)` / `broadcast.unannounce()` own that exact-path
advertisement; `origin.dynamic(prefix, route)` claims `prefix` and every
advertisement, and `broadcast.end()` ends the broadcast for good (a second call
is a no-op; Kotlin spells it `end` because `close()`, or `use { }`, releases the
handle, which ends the broadcast only once no `dynamic()` handle remains); `origin.dynamic(prefix, route)` claims `prefix` and every
path beneath it (`""` for everything). Hold the returned `OriginDynamic`
while the claim should stay advertised, and reject the requests you will not
serve. A route is a capability, not an inventory. `announcements(config)` takes
Expand Down
3 changes: 2 additions & 1 deletion doc/lib/py/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ For already-encoded live output, call `audio.flush(timestamp_us)` after each `au
The three advertising operations, as the other bindings spell them:
`client.create_broadcast(path)` (or `OriginProducer.create_broadcast`) returns
an unannounced producer, invisible to everyone; `broadcast.announce(route)` /
`broadcast.unannounce()` own that exact-path advertisement;
`broadcast.unannounce()` own that exact-path advertisement, and
`broadcast.close()` ends the broadcast for good (a second call is a no-op);
`origin.dynamic(prefix, route)` claims `prefix` and every path beneath it
(`""` for everything). Hold the returned handle while the claim should stay
advertised, and reject the requests you will not serve. A route is a
Expand Down
6 changes: 5 additions & 1 deletion doc/lib/rs/moq-net.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,12 @@ Three operations, on an origin:
- `broadcast.announce(route)` / `broadcast.unannounce()` own that
advertisement. Announcing again re-prices the standing route, which competes
on cost with remote routes at the same path (a tie goes to the local
broadcast). The route retracts on `unannounce()`, `finish()`, or the last
broadcast). The route retracts on `unannounce()`, `close()`, or the last
producer dropping; tracks already in flight carry on to their own end.
- `broadcast.close()` ends the broadcast for good: it retracts, leaves local
discovery, and answers every later track lookup with `Unroutable`. Tracks
already subscribed carry on to their own end. It can never be announced
again. Dropping the last producer does the same.
- `origin.dynamic(prefix, route)` claims `prefix` and every path beneath it
(`""` claims everything). Hold the returned `origin::Dynamic` while the
claim should stay advertised; drop it to retract. A request beneath it with
Expand Down
3 changes: 2 additions & 1 deletion doc/lib/swift/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ For already-encoded live output, call `audio.flush(timestampUs:)` after `writeFr

The three advertising operations: `session.publish.createBroadcast(path:)`
returns an unannounced producer, invisible to everyone; `broadcast.announce(route:)` /
`broadcast.unannounce()` own that exact-path advertisement;
`broadcast.unannounce()` own that exact-path advertisement, and
`broadcast.close()` ends the broadcast for good (a second call is a no-op);
`session.publish.dynamic(prefix:route:)` claims `prefix` and every path
beneath it (`""` for everything). Hold the returned `OriginDynamic` while the
claim should stay advertised, and reject the requests you will not serve. A
Expand Down
6 changes: 3 additions & 3 deletions go/wrapper/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ func ExampleClient_CreateBroadcast() {
if err != nil {
log.Fatal(err)
}
// Finishing unpublishes the broadcast immediately.
defer broadcast.Finish()
// Closing ends the broadcast for good.
defer broadcast.Close()

media, err := broadcast.PublishAudio(moq.AudioFormatOpus, opusHead())
if err != nil {
Expand Down Expand Up @@ -91,7 +91,7 @@ func ExampleBroadcastProducer_PublishVideo_videoHint() {
if err != nil {
log.Fatal(err)
}
defer broadcast.Finish()
defer broadcast.Close()

media, err := broadcast.PublishVideo(moq.VideoFormatAvc3, nil, moq.WithVideoHint(moq.VideoHint{}))
if err != nil {
Expand Down
Loading
Loading