How to modify/add a STANAG Klv metadata on the fly.

Let's assume you have to modify some Klv fields in a live STANAG 4609 stream. Or add/delete existing metadata.

In this article, we'll show how to put together a simple app that extracts and decodes existing MISB Klv metadata, modifies it, and re-inserts it back into the stream with the same (in case of SYNC Klv) timestamp. We'll use KlvInjector SDK.

First things first, let's create a VS project, add the references and make sure all the required dependencies are copied to the target directory (we can do it using the 'post-build event')
Next, we create an instance of the Klv Injector, initialize it and add events:
// Create Player instance

CKlvInjector m_KlvInjector = new CKlvInjector();

// Setup events
m_KlvInjector.PlayerEvent += new NotifyPlayerEvent(OnPlayerEvent);
m_KlvInjector.PidDetectionEvent += new NotifyPidDetection(OnPidDetectionEvent);
m_KlvInjector.SyncFrameEvent += new NotifySyncFrame(OnSyncFrameEvent);
There are some additional parameters we can set up.
m_KlvInjector.RenderVideo = true; // to show video

m_KlvInjector.Caching = 200; // Caching delay, needed to get smooth video playback (if you have a stream jitter)

m_KlvInjector.Hwnd = 0; // Where to render video

m_KlvInjector.VideoCaptureMode.uncompressedVideo = UncompressedVideoModeWr.UncompressedVideoMode_None;
m_KlvInjector.VideoCaptureMode.fCompressedVideo = true;

// Set the output target
m_KlvInjector.NetTarget.Url = targetUrl;
m_KlvInjector.NetTarget.Use = true;

// Initialize
m_KlvInjector.Init("udp://"); // source stream Url
By default, KlvInjector will pass through all existing KLV data from the source to the output. This is not what we want. We need to instruct KvInjector to remove the existing KlvPid from the multiplex, receive the data, modify it and re-insert.
Let's set up the  NotifyPidDetection event, it should trigger shortly after the playback is started. That's the place where we can receive the information on the existing elementary streams. Here we can configure what should be included in our output stream.
static void OnPidDetectionEvent(List pidList)
    pidList.ForEach(pid =>
       // Find video pid. We'll use it for statistics.
       if (pid.streamType == StreamType.VIDEO)
           m_VideoPid = pid.streamId;

        // Find first Klv PID. If already present in the stream, we'll replace it with our data. If there is no KLV, we'll use the default 0X1F1 value.

        if (pid.streamType == StreamType.KLV && m_ReceivedKlvPid == -1){
           m_ReceivedKlvPid = pid.streamId;

   // Add KLV PID to multiplexer
   m_InsertKlvPid = m_userKlvPid;
   m_KlvInjector.AddKlvPidToOutput(m_InsertKlvPid, m_klvProps);
The straightforward method - we simply get the decoded data, replace the required value and encode the whole packet back. A better option, which would eliminate a "generation loss" while decoding/encoding compressed data is to only reencode the specific klv item value and re-calculate the packet checksum.
static void OnSyncFrameEvent(List streamList)
     streamList.ForEach(delegate (StreamFrameInfoWr streamFrame)
         switch (streamFrame.streamType)
             case StreamType.KLV:
                // Here we got a klv packet
                KlvFrameInfoWr kf = streamFrame as KlvFrameInfoWr;

                if(kf.decodedData == null)

                JObject decodedData = (JObject)kf.decodedData;
                // Here, as an example, we'll add/overite tag 3 'Mission Id"
                decodedData["3"] = "New name";
                byte[] pckt =  KlvInjector.Misb601.EncodePacket(decodedData);
                                          m_KlvInjector.WritePacketToOutputPid(m_InsertKlvPid, pckt, kf.timeStamp);

That’s it! You can now start KlvInjector,  open your stream with any STANAG player and see the results.

No Comments

Post a Comment