Skip to content

Commit 5c3cf91

Browse files
authored
Merge pull request #13 from mondain/jemalloc
2 parents 95b912d + 8e90a0e commit 5c3cf91

19 files changed

Lines changed: 856 additions & 19 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ It turns MP4 input into CMSF-style publishable objects, builds draft-aware MOQT
1212
- Remuxes progressive MP4 input into synthesized fragmented media objects.
1313
- Extracts track metadata and RFC 6381 codec identifiers.
1414
- Preserves HEVC signaling and normalizes `hev1` to `hvc1` when needed.
15-
- Builds publish plans with catalog, optional SAP event timeline metadata, and media objects.
15+
- Builds publish plans with catalog, optional MSF media timeline and SAP event timeline metadata, and media objects.
1616
- Emits generated objects and catalog metadata to disk for inspection.
1717
- Supports draft-aware MOQT framing for drafts 14, 16, and 18.
1818
- Publishes over Raw QUIC or WebTransport when picoquic and picotls are available.

docs/design.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,20 @@ The catalog format includes:
2727
- `role` with values such as `video` and `audio`
2828
- RFC 6381 `codec` strings such as `avc1.64000C`, `mp4a.40.2`, and HEVC values like `hvc1.1.6.L90.B0`
2929
- `renderGroup` and `isLive`
30+
- MSF media timeline tracks with `packaging: "mediatimeline"` and `role: "mediatimeline"`
3031
- SAP event timeline tracks with `packaging: "eventtimeline"` and `eventType: "org.ietf.moq.cmsf.sap"`
3132
- `width` and `height` for video tracks
3233
- `sampleRate` and `channelCount` for audio tracks
3334
- base64-encoded per-track CMAF initialization segment (`ftyp` + `moov`) in `initData`
3435

36+
With `--msf-timeline`, `catalog.json` also includes a `timeline` metadata track with:
37+
38+
- `packaging: "mediatimeline"`
39+
- `mimeType: "application/json"`
40+
- `depends` pointing to all media tracks
41+
42+
The corresponding `timeline_g0_o0.json` payload is an explicit MSF media timeline array of `[mediaTimeMs, [groupId, objectId], wallclockMs]` records. VOD output uses `0` for wallclock time.
43+
3544
With `--sap`, `catalog.json` also includes:
3645

3746
- one `*_sap` track per media track

docs/publisher-api.md

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ config.track_namespace = "media";
2727
config.forward = false;
2828
config.publish_catalog = false;
2929
config.include_sap = false;
30+
config.include_msf_timeline = false;
3031
config.split_cmaf_chunks = true;
3132
config.paced = false;
3233
config.loop = false;
@@ -134,7 +135,8 @@ Convenience helpers:
134135

135136
## 7. Live Input Publish (Incremental stdin/stream)
136137

137-
For live pipelines (for example ffmpeg piping fragmented MP4):
138+
The default live path expects fragmented MP4, which matches ffmpeg/CMAF
139+
pipelines:
138140

139141
```cpp
140142
const auto status = publisher.publish_live(std::cin, endpoint, tls);
@@ -150,7 +152,39 @@ if (!status.ok) {
150152

151153
`publish_live(...)` uses incremental parsing and live publish flow instead of buffering to EOF.
152154

153-
## 8. ALPN Override Behavior
155+
## 8. Arbitrary Live Object Publish
156+
157+
Applications that already produce MoQ objects directly can bypass fragmented MP4
158+
ingest with `publish_live_objects(...)`.
159+
160+
```cpp
161+
std::vector<openmoq::publisher::LiveObject> objects = {
162+
{
163+
.track_name = "events",
164+
.group_id = 0,
165+
.object_id = 0,
166+
.payload = {'h', 'e', 'l', 'l', 'o'},
167+
},
168+
};
169+
std::size_t next = 0;
170+
171+
openmoq::publisher::LiveObjectSource source;
172+
source.tracks = {{.track_name = "events"}};
173+
source.next_object = [&]() -> std::optional<openmoq::publisher::LiveObject> {
174+
if (next >= objects.size()) {
175+
return std::nullopt;
176+
}
177+
return objects[next++];
178+
};
179+
180+
const auto status = publisher.publish_live_objects(source, endpoint, tls);
181+
```
182+
183+
Each `LiveObject` supplies the target track, group/object IDs, optional media
184+
timing, and the payload bytes to send. The fragmented MP4 `publish_live(...)`
185+
API remains the default live publishing path.
186+
187+
## 9. ALPN Override Behavior
154188
155189
By default, the API applies transport-appropriate ALPN:
156190
@@ -177,8 +211,9 @@ The same override flag exists on:
177211
- `publish_file(...)`
178212
- `publish_stream(...)`
179213
- `publish_live(...)`
214+
- `publish_live_objects(...)`
180215

181-
## 9. Error Handling Pattern
216+
## 10. Error Handling Pattern
182217

183218
All API publish calls return `TransportStatus`:
184219

@@ -200,18 +235,19 @@ if (!status.ok) {
200235
}
201236
```
202237

203-
## 10. Integration Pattern for Larger Applications
238+
## 11. Integration Pattern for Larger Applications
204239

205240
For service-style integration:
206241

207242
1. Construct one `Publisher` per runtime configuration profile.
208243
2. On ingest, call `prepare_file(...)` or `prepare_stream(...)`.
209244
3. Store or inspect `PreparedPublish` metadata as needed.
210245
4. Publish to one or more endpoints with `publish(...)`.
211-
5. For continuous input, run `publish_live(...)` in a worker thread.
212-
6. Use `TransportStatus` messages for metrics and retry decisions.
246+
5. For continuous fragmented MP4 input, run `publish_live(...)` in a worker thread.
247+
6. For direct object producers, provide a `LiveObjectSource` and call `publish_live_objects(...)`.
248+
7. Use `TransportStatus` messages for metrics and retry decisions.
213249

214-
## 11. Publish Summary (`stats`)
250+
## 12. Publish Summary (`stats`)
215251

216252
The publisher API is blocking: `publish(...)`, `publish_file(...)`,
217253
`publish_stream(...)`, and `publish_live(...)` run the session on the calling
@@ -243,6 +279,7 @@ Current fields:
243279
- `groupsPublished`: total (track, group) units published in the current/last session
244280
- `splitCmafChunks`: current packaging mode (`true` = split chunks, `false` = coalesced chunks)
245281
- `includeSap`: whether SAP track/object packaging is enabled
282+
- `includeMsfTimeline`: whether MSF media timeline track/object packaging is enabled
246283
- `transport`, `host`, `port`, `path`: endpoint context for the current/last session
247284
- `connectionId`: last known transport connection ID
248285
- `lastError`: last publisher-level error, if any
@@ -269,6 +306,7 @@ Example:
269306
"groupsPublished": 42,
270307
"splitCmafChunks": true,
271308
"includeSap": false,
309+
"includeMsfTimeline": false,
272310
"transport": "webtransport",
273311
"host": "relay.example.com",
274312
"port": 443,

docs/quickstart.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ Inspect the same input with SAP event timeline metadata enabled:
1616
./build/openmoq-publisher --input sample.mp4 --sap --dump-plan
1717
```
1818

19+
Inspect the same input with an MSF media timeline track enabled:
20+
21+
```bash
22+
./build/openmoq-publisher --input sample.mp4 --msf-timeline --dump-plan
23+
```
24+
1925
Try the draft-16 compatibility profile:
2026

2127
```bash
@@ -42,7 +48,7 @@ The output directory should contain:
4248
- one `*_probe.mp4` file per emitted media object for direct `ffprobe` use
4349
- `publish-plan.txt`
4450

45-
When `--sap` is enabled, the output directory also contains one `*_sap_g*_o*.json` file per emitted SAP event timeline object.
51+
When `--msf-timeline` is enabled, the output directory also contains `timeline_g0_o0.json` with explicit MSF media timeline records. When `--sap` is enabled, the output directory also contains one `*_sap_g*_o*.json` file per emitted SAP event timeline object.
4652

4753
## Use Standard Input
4854

@@ -104,6 +110,7 @@ cat sample.mp4 | ./build/openmoq-publisher \
104110
## Output Notes
105111

106112
- default output includes the `catalog` object plus media objects
113+
- `--msf-timeline` additionally creates a `timeline` media timeline track and metadata object
107114
- `--sap` additionally creates `*_sap` metadata tracks and objects
108115
- default packaging emits lower-latency split MOQT objects per group when chunk/sample boundaries are available
109116
- `--coalesce-cmaf-chunks` restores one media object per group

docs/testing.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Publish-plan numbering notes:
2626
- `group_id` is allocated per track, not across all tracks, so interleaved audio and video fragments can both use `0, 1, 2, ...`
2727
- by default, `object_id` advances within a group when CMAF content is split into multiple MOQT objects for lower latency
2828
- `--coalesce-cmaf-chunks` forces `object_id = 0` for the current one-object-per-group fallback
29+
- MSF media timeline tracks are disabled by default; add `--msf-timeline` when you want a `timeline` metadata track and object
2930
- SAP event timeline tracks are disabled by default; add `--sap` when you want catalog and metadata objects for `*_sap` tracks
3031

3132
If you want the packaging and transport tests without the CLI target, use the secondary build tree:
@@ -61,6 +62,7 @@ Input source notes:
6162
- `--input <path>` reads an MP4 from a regular file
6263
- `--input -` reads the MP4 byte stream from standard input
6364
- fragmented and progressive MP4 inputs are both supported through either source type
65+
- MSF media timeline tracks are disabled by default; add `--msf-timeline` to include the `timeline` metadata track and object in the publish plan
6466
- SAP event timeline tracks are disabled by default; add `--sap` to include `*_sap` metadata tracks and objects in the publish plan
6567

6668
Use `--dump-plan` to inspect the generated publish plan without touching the network:
@@ -75,6 +77,12 @@ Include SAP event timeline output explicitly:
7577
./build/openmoq-publisher --input sample.mp4 --draft 14 --sap --dump-plan
7678
```
7779

80+
Include MSF media timeline output explicitly:
81+
82+
```bash
83+
./build/openmoq-publisher --input sample.mp4 --draft 14 --msf-timeline --dump-plan
84+
```
85+
7886
Use stdin when the source is already being produced by another command:
7987

8088
```bash

include/openmoq/publisher/cli_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ struct CliOptions {
3232
bool forward = false;
3333
bool publish_catalog = false;
3434
bool include_sap = false;
35+
bool include_msf_timeline = false;
3536
bool split_cmaf_chunks = true;
3637
bool paced = false;
3738
bool loop = false;

include/openmoq/publisher/cmsf_packager.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ struct PublishPlan {
4141
std::vector<CmsfObject> objects;
4242
};
4343

44-
PublishPlan build_publish_plan(const SegmentedMp4& segmented_mp4, DraftVersion version, bool include_sap = false);
44+
PublishPlan build_publish_plan(const SegmentedMp4& segmented_mp4,
45+
DraftVersion version,
46+
bool include_sap = false,
47+
bool include_msf_timeline = false);
4548
std::string render_publish_plan(const PublishPlan& plan);
4649
PublishPlan materialize_publish_plan(const PublishPlan& plan, std::span<const std::uint8_t> bytes);
4750
void emit_plan_objects(const PublishPlan& plan,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <functional>
5+
#include <optional>
6+
#include <string>
7+
#include <vector>
8+
9+
namespace openmoq::publisher {
10+
11+
struct LiveTrack {
12+
std::string track_name;
13+
};
14+
15+
struct LiveObject {
16+
std::string track_name;
17+
std::size_t group_id = 0;
18+
std::uint64_t subgroup_id = 0;
19+
std::size_t object_id = 0;
20+
std::uint64_t media_time_us = 0;
21+
std::uint64_t media_duration_us = 0;
22+
std::vector<std::uint8_t> payload;
23+
bool subgroup_contains_group_largest = true;
24+
bool final_in_subgroup = true;
25+
};
26+
27+
struct LiveObjectSource {
28+
std::vector<LiveTrack> tracks;
29+
std::function<std::optional<LiveObject>()> next_object;
30+
};
31+
32+
} // namespace openmoq::publisher

include/openmoq/publisher/publisher_api.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <vector>
1414

1515
#include "openmoq/publisher/cmsf_packager.h"
16+
#include "openmoq/publisher/live_object.h"
1617
#include "openmoq/publisher/moq_draft.h"
1718
#include "openmoq/publisher/transport/publisher_transport.h"
1819

@@ -24,6 +25,7 @@ struct PublisherConfig {
2425
bool forward = false;
2526
bool publish_catalog = false;
2627
bool include_sap = false;
28+
bool include_msf_timeline = false;
2729
bool split_cmaf_chunks = true;
2830
bool paced = false;
2931
bool loop = false;
@@ -42,6 +44,7 @@ struct PublisherStats {
4244
std::uint64_t groups_published = 0;
4345
bool split_cmaf_chunks = true;
4446
bool include_sap = false;
47+
bool include_msf_timeline = false;
4548
transport::TransportKind transport = transport::TransportKind::kRawQuic;
4649
std::string host;
4750
std::uint16_t port = 0;
@@ -82,6 +85,10 @@ class Publisher {
8285
const transport::EndpointConfig& endpoint,
8386
const transport::TlsConfig& tls = {},
8487
bool endpoint_alpn_overridden = false) const;
88+
transport::TransportStatus publish_live_objects(const LiveObjectSource& source,
89+
const transport::EndpointConfig& endpoint,
90+
const transport::TlsConfig& tls = {},
91+
bool endpoint_alpn_overridden = false) const;
8592
transport::TransportStatus disconnect(std::uint64_t application_error_code = 0) const;
8693
PublisherStats stats() const;
8794
[[deprecated("Use stats(); live polling is not supported by the blocking publish API.")]]

include/openmoq/publisher/transport/moqt_session.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include "openmoq/publisher/cmsf_packager.h"
4+
#include "openmoq/publisher/live_object.h"
45
#include "openmoq/publisher/transport/publisher_transport.h"
56

67
#include <iosfwd>
@@ -42,6 +43,8 @@ class MoqtSession {
4243
TransportStatus publish_live(std::istream& input,
4344
openmoq::publisher::DraftVersion draft_version,
4445
bool split_cmaf_chunks);
46+
TransportStatus publish_live_objects(const openmoq::publisher::LiveObjectSource& source,
47+
openmoq::publisher::DraftVersion draft_version);
4548
TransportStatus close(std::uint64_t application_error_code = 0);
4649
PublishStats publish_stats() const;
4750

0 commit comments

Comments
 (0)