Starting from 2.12.1 SDK version, Writer object can record frame-level ancillary data (ANC) as a separate, frame-wrapped SMPTE ST 436 (SMPTE 436M) data track in an MXF OP1a file. Use this track to carry CEA-708 closed captions in caption distribution packets (CDPs), OP-47 teletext subtitles, and SCTE-104 cues alongside the video.
Enable ANC recording
Set format='mxf' and mxf.anc_write='true' in the configuration string you pass to WriterSet/WriterNameSet. Then attach ANC raw metadata to frames using MFStrSet.
"format='mxf' mxf.anc_write='true' video::codec='mpeg2video' ...ANC recording is off by default. The writer uses this option when encoding MXF OP1a at the input frame rate. Workflows that change the frame rate, transwrap existing essence, or use play while recording are outside the supported scope of this option. Other containers ignore the setting.
Attach ANC packets to each frame
Use MFStrSet() with a key of the form ANC.Line.<lineNumber>. The XML root has the same name as the key. Put one <packet> element per packet inside the root, in the order you want the packets written. Each element has DID and SDID attributes and a hexadecimal payload as its text.
| Packet | DID (decimal) | SDID (decimal) | Lines in the 1080i/25 example |
|---|---|---|---|
| OP-47 teletext | 67 | 2 | 11 and 574 |
| SCTE-104 cue | 65 | 7 | 11, after the OP-47 packet |
| Caption distribution packet (CDP) | 97 | 1 | 9 |
Choose line numbers that match the video format of the frames you send to the writer. In this example, line 11 is in the first field and line 574 is in the second field. If you need to convert the video format, prepare frames in the target format before attaching the packets.
Supply the encoded ANC payload for each packet. The writer preserves line numbers, packet IDs, payload bytes, packet order, and the association with the input frame. It skips malformed packets and logs a warning.
C# example
Use the writer configured above. Call this method for each frame you want to write. The payload arguments are hexadecimal strings containing the bytes for the current frame. Pass null for an absent payload. This example assumes the input frames have no existing ANC metadata. If you add a packet to a line that already contains ANC, read the existing XML with MFStrGet() and append the new packet before writing the updated line.
using MFORMATSLib;
using System.Xml.Linq;
static void WriteFrame(MFWriterClass writer, MFFrame frame,
string op47FirstFieldHex, string op47SecondFieldHex,
string scte104Hex, string cdpHex)
{
var line11 = new XElement("ANC.Line.11");
if (!string.IsNullOrEmpty(op47FirstFieldHex))
line11.Add(Packet(67, 2, op47FirstFieldHex));
if (!string.IsNullOrEmpty(scte104Hex))
line11.Add(Packet(65, 7, scte104Hex));
if (line11.HasElements)
frame.MFStrSet("ANC.Line.11", line11.ToString(SaveOptions.DisableFormatting));
if (!string.IsNullOrEmpty(op47SecondFieldHex))
{
var line574 = new XElement("ANC.Line.574", Packet(67, 2, op47SecondFieldHex));
frame.MFStrSet("ANC.Line.574", line574.ToString(SaveOptions.DisableFormatting));
}
if (!string.IsNullOrEmpty(cdpHex))
{
var line9 = new XElement("ANC.Line.9", Packet(97, 1, cdpHex));
frame.MFStrSet("ANC.Line.9", line9.ToString(SaveOptions.DisableFormatting));
}
writer.ReceiverFramePut(frame, -1, "");
}
static XElement Packet(int did, int sdid, string payloadHex)
{
return new XElement("packet", new XAttribute("DID", did),
new XAttribute("SDID", sdid), payloadHex);
}For an input frame that already carries ANC.Line.* metadata, you can pass it directly to ReceiverFramePut() to record those packets. Call WriterClose(0) after sending the final frame.
In MPlatform SDK you should edit frames in the same way within a synchronous OnFrameSafe callback.
Verify the recording
- Reopen the finalized MXF with
MFReader/MFile. To inspect the raw OP-47 packets, use the reader settings shown below. - Read each frame with
SourceFrameGet()and retrieve the relevant line withMFStrGet(). Compare the packet IDs, payload bytes, and order with the input. Check the expected frame positions as well. The first ANC packet or a one-off SCTE-104 cue may occur after the first frame. - In MediaInfo, look for the SMPTE ST 436 ANC essence. Depending on the packets present, the report can also identify OP-47 and SCTE-104.
var reader = new MFReaderClass();
reader.PropsSet("external_process", "false");
reader.ReaderOpen(outputPath, "op47.decode=false");
// Repeat for the frames you want to inspect.
reader.SourceFrameGet(-1, out MFFrame recordedFrame, "");
if (recordedFrame != null)
{
try
{
recordedFrame.MFStrGet("ANC.Line.11", out string line11Xml);
// Inspect line11Xml when this frame is expected to carry packets.
}
finally
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(recordedFrame);
}
}
reader.ReaderClose();For frames containing a CDP, MFDataGet("C708", ...) also exposes the caption data as a binary buffer. See Working with Frame Metadata for an example of reading the buffer.
Still have questions?
Tell us what didn’t work or what you’d like clarified.