Metadata
Metadata that travels through your OptiView Live stream — whether pushed via the API or embedded as SEI — surfaces on the player as cues on a text track. The general approach is the same in every case:
- Listen for
addtrackonplayer.textTracksso you find out when a new metadata track shows up. - Subscribe to a cue event on that track.
- Inspect the cue to make sure it carries the metadata you care about.
- Read the payload from
cue.content.
The properties you match on, the cue event you subscribe to, and the shape of cue.content all depend on which kind of metadata is being delivered.
User-data-unregistered SEI and API push
UDU SEI and API-pushed payloads surface as ID3 PRIV frames inside an ID3 v2.4 tag embedded in an emsg box, carried by the CMAF segments of the stream.
- The owner identifier of the
PRIVframe will be set to:optiview.live:meta:<your-uuid> - The frame's private data will contain your metadata as raw binary data.
(See section 4.27 of the ID3 v2.4 Native Frames specification and ID3 in CMAF for more details.)
The OptiView Player iOS SDK exposes these ID3 frames on a text track with TextTrack.type equal to "id3". Listen for the cue event that matches when you want to react:
TextTrackEventTypes.ADD_CUEfires as soon as a cue becomes available on the track, before playback reaches it — useful when you want to prefetch data or update application state ahead of time.TextTrackEventTypes.ENTER_CUEfires when playback enters the cue — useful when you want to act in sync with the video, for example to render an overlay at the moment the cue was inserted.
The ID3 frame will be contained in cue.contentDictionary with id equal to "PRIV" and ownerIdentifier equal to optiview.live:meta:<your-uuid>.
Make sure to always check the frame's id and ownerIdentifier before using it, to avoid processing unrelated ID3 frames.
Suppose your UUID is 11111111-1111-1111-1111-111111111111:
_ = theoplayer.textTracks.addEventListener(type: TextTrackListEventTypes.ADD_TRACK, listener: { (addTrackEvent) in
guard let track = addTrackEvent.track as? TextTrack,
track.type == "id3"
else { return }
// ID3 track was added. Listen for incoming ID3 cues.
_ = track.addEventListener(type: TextTrackEventTypes.ADD_CUE, listener: { (addCueEvent) in
guard let cue = addCueEvent.cue as? Id3Cue,
let frame = cue.contentDictionary else { return }
guard frame["id"] == "PRIV",
frame["ownerIdentifier"] == "optiview.live:meta:11111111-1111-1111-1111-111111111111"
else { return }
print("Metadata: \(frame["data"])")
})
})
Here the frame["data"] carries the binary payload exactly as it was sent — your application is responsible for parsing it (for example UTF-8 JSON, protobuf, or your own format).