Skip to content

Commit 0275dbf

Browse files
committed
Add split CMSF object mode and SAP timelines
1 parent 39ef3ea commit 0275dbf

12 files changed

Lines changed: 641 additions & 62 deletions

README.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ It packages MP4 input into CMSF-style publishable objects, supports MOQT draft-s
99
- Parses fragmented MP4 input with `ftyp` + `moov` + `moof`/`mdat`
1010
- Remuxes progressive MP4 input into synthesized fragmented media objects
1111
- Extracts track metadata and RFC 6381 codec identifiers from MP4 sample tables
12-
- Builds a publish plan with initialization and media objects
12+
- Builds a publish plan with catalog, SAP event timeline, and media objects
1313
- Emits generated objects and catalog metadata to disk for inspection
1414
- Supports a configurable track namespace, optional paced publication, and draft-aware MOQT control/object encoding
1515
- Includes packaging, CLI, and MOQT session tests through CTest
@@ -21,7 +21,8 @@ It packages MP4 input into CMSF-style publishable objects, supports MOQT draft-s
2121
For already fragmented input:
2222

2323
- `ftyp` + `moov` are reused as the initialization segment
24-
- each `moof`/`mdat` pair is reused directly as a media object payload
24+
- by default, each fragmented input is split into lower-latency MOQT media objects within the same group when per-sample boundaries can be derived
25+
- `--coalesce-cmaf-chunks` keeps the older one-object-per-fragment behavior
2526
- the pipeline preserves source-byte spans until optional file emission
2627

2728
### Progressive MP4 remux path
@@ -31,6 +32,7 @@ For non-fragmented input:
3132
- the tool reads `stbl` tables such as `stsz`, `stsc`, `stco` or `co64`, `stts`, and optional `ctts` and `stss`
3233
- it synthesizes a fragmented initialization segment by adding `mvex` and `trex`
3334
- it builds synthetic `moof` + `mdat` payloads from the original sample data
35+
- by default, progressive remux output is split into multiple MOQT objects per group for lower latency
3436

3537
This keeps the project aligned with CMAF-style publication while reusing the same publish-plan model for local inspection and transport-driven publication.
3638

@@ -126,7 +128,8 @@ This covers:
126128
Publish-plan numbering note:
127129

128130
- `group_id` is allocated per track, not across all tracks, so interleaved audio and video fragments can both use `0, 1, 2, ...`
129-
- `object_id` is currently `0` for each emitted fragment because this publisher currently emits one object per group
131+
- by default, `object_id` advances within a group when CMAF content is split into multiple MOQT objects for lower latency
132+
- `--coalesce-cmaf-chunks` forces `object_id = 0` for the current one-object-per-group fallback
130133

131134
If you want the packaging and transport tests without the CLI target, use the secondary build tree:
132135

@@ -169,6 +172,7 @@ Use `--emit-dir` to inspect the emitted catalog and media objects on disk:
169172
The output directory should contain:
170173

171174
- `catalog.json`
175+
- one `*_sap_g*_o*.json` file per emitted SAP event timeline object
172176
- one `*_init.mp4` file per media track
173177
- one `*_media.mp4` file per emitted media object
174178
- one `*_probe.mp4` file per emitted media object for direct `ffprobe` use
@@ -179,6 +183,7 @@ The catalog format includes:
179183
- `role` with values such as `video` and `audio`
180184
- RFC 6381 `codec` strings such as `avc1.64000C` and `mp4a.40.2`
181185
- `renderGroup` and `isLive`
186+
- SAP event timeline tracks with `packaging: "eventtimeline"` and `eventType: "org.ietf.moq.cmsf.sap"`
182187
- `width` and `height` for video tracks
183188
- `sampleRate` and `channelCount` for audio tracks
184189
- base64-encoded per-track CMAF initialization segment (`ftyp` + `moov`) in `initData`
@@ -216,6 +221,8 @@ Behavior notes:
216221
- `--forward 0` waits for inbound `SUBSCRIBE` requests before sending matching media objects
217222
- with `--forward 0`, subscribers are still expected to request tracks explicitly; by default that includes subscribing to `catalog` if they need track discovery
218223
- `--publish-catalog` keeps `--forward 0` for media tracks but proactively publishes the `catalog` track through the normal `PUBLISH` / `PUBLISH_OK` path so downstream consumers can discover available tracks without first subscribing to `catalog`
224+
- media packaging defaults to lower-latency split MOQT objects per group when chunk/sample boundaries are available
225+
- `--coalesce-cmaf-chunks` disables that split and falls back to one media object per group
219226
- when multiple tracks are subscribed, matching objects are served in publish-plan order so time-aligned audio/video stay interleaved instead of draining one track before the next
220227
- `--forward 1` proactively publishes tracks and objects after namespace setup completes
221228
- `--timeout <seconds>` controls how long the publisher waits for inbound `SUBSCRIBE` requests; the default is 3 seconds
@@ -267,6 +274,11 @@ Transport-oriented CLI flags are also present now:
267274
--insecure
268275
```
269276

277+
Chunk/object mapping:
278+
279+
- default behavior is lower-latency split publication, which emits multiple MOQT objects in the same group when CMAF chunk/sample boundaries are available
280+
- `--coalesce-cmaf-chunks` restores one media object per group
281+
270282
ALPN selection:
271283

272284
- draft-14 defaults to `moq-00`
@@ -276,7 +288,8 @@ ALPN selection:
276288
Catalog note:
277289

278290
- `catalog.json` uses the CMSF-style `role` field such as `video` and `audio`
279-
- `publish-plan.txt` and `--dump-plan` still print an internal debug `kind=` label for object type (`catalog` vs `media`); that debug label is not part of the catalog spec
291+
- `catalog.json` also advertises per-track SAP event timeline tracks using CMSF `eventtimeline` metadata
292+
- `publish-plan.txt` and `--dump-plan` still print an internal debug `kind=` label for object type (`catalog`, `metadata`, or `media`); that debug label is not part of the catalog spec
280293

281294
## Creating Fragmented MP4 with FFmpeg
282295

include/openmoq/publisher/cli_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ struct CliOptions {
2020
bool endpoint_alpn_overridden = false;
2121
bool forward = false;
2222
bool publish_catalog = false;
23+
bool split_cmaf_chunks = true;
2324
bool paced = false;
2425
bool dump_plan = false;
2526
std::chrono::seconds subscriber_timeout = std::chrono::seconds(3);

include/openmoq/publisher/cmaf_segmenter.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,24 @@
77

88
namespace openmoq::publisher {
99

10+
enum class CmafObjectMode {
11+
kSplit,
12+
kCoalesced,
13+
};
14+
1015
struct PayloadBuffer {
1116
ByteSpan span;
1217
std::vector<std::uint8_t> owned_bytes;
1318
};
1419

1520
struct MediaFragment {
16-
std::size_t sequence = 0;
21+
std::size_t group_id = 0;
22+
std::size_t object_id = 0;
1723
std::string track_name;
1824
std::uint64_t start_time_us = 0;
1925
std::uint64_t duration_us = 0;
26+
std::uint64_t earliest_presentation_time_us = 0;
27+
std::uint8_t sap_type = 0;
2028
PayloadBuffer payload;
2129
};
2230

@@ -26,7 +34,7 @@ struct SegmentedMp4 {
2634
std::vector<TrackDescription> tracks;
2735
};
2836

29-
SegmentedMp4 segment_for_cmaf(const ParsedMp4& parsed_mp4);
37+
SegmentedMp4 segment_for_cmaf(const ParsedMp4& parsed_mp4, CmafObjectMode object_mode = CmafObjectMode::kSplit);
3038
std::string summarize_tracks(const std::vector<TrackDescription>& tracks);
3139
std::size_t payload_size(const PayloadBuffer& payload);
3240

include/openmoq/publisher/cmsf_packager.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace openmoq::publisher {
1212

1313
enum class CmsfObjectKind {
1414
kInitialization,
15+
kMetadata,
1516
kMedia,
1617
};
1718

include/openmoq/publisher/mp4_box.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ struct TrackDescription {
2727
std::string codec;
2828
std::string sample_entry_type;
2929
std::string track_name;
30+
std::string packaging = "cmaf";
31+
std::string event_type;
32+
std::string mime_type;
33+
std::vector<std::string> depends;
3034
std::uint32_t timescale = 0;
3135
std::uint32_t width = 0;
3236
std::uint32_t height = 0;

src/cli_options.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ CliOptions parse_cli_options(int argc, char** argv) {
114114
options.forward = parse_forward_flag(require_value("--forward"));
115115
} else if (argument == "--publish-catalog") {
116116
options.publish_catalog = true;
117+
} else if (argument == "--coalesce-cmaf-chunks") {
118+
options.split_cmaf_chunks = false;
117119
} else if (argument == "--timeout") {
118120
options.subscriber_timeout = parse_timeout(require_value("--timeout"));
119121
} else if (argument == "--paced") {
@@ -146,7 +148,7 @@ CliOptions parse_cli_options(int argc, char** argv) {
146148
std::string build_usage(const char* argv0) {
147149
return std::string("Usage: ") + argv0 +
148150
" --input <mp4> [--draft 14|16] [--namespace <value>] [--forward 0|1] [--timeout <seconds>]"
149-
" [--publish-catalog] [--paced] [--dump-plan] [--emit-dir <dir>]"
151+
" [--publish-catalog] [--coalesce-cmaf-chunks] [--paced] [--dump-plan] [--emit-dir <dir>]"
150152
" [--endpoint host:port|moqt://host:port/path] [--alpn value] [--sni value]"
151153
" [--cert file] [--key file] [--ca file] [--insecure]";
152154
}

0 commit comments

Comments
 (0)