Dynamic Ads Insertion

Dear all,

I want to ask about how to get SCTE-35 markers on live streaming application. There is a documentation that we have to build java based code to get that file Monitor MPEG-TS ingestion to process additional data streams (SCTE-35, KLV, etc.) .

import java.util.*;

import com.wowza.wms.amf.*;
import com.wowza.wms.logging.*;
import com.wowza.wms.rtp.model.*;
import com.wowza.wms.stream.*;
import com.wowza.wms.transport.mpeg2.*;
import com.wowza.wms.transport.mpeg2.ProgramMapTable.*;
import com.wowza.wms.transport.mpeg2.section.cue.*;

public class RTPDePacketizerMPEGTSMonitorCUE extends RTPDePacketizerMPEGTSNotifyBase
{
	private static final Class<RTPDePacketizerMPEGTSMonitorCUE> CLASS = RTPDePacketizerMPEGTSMonitorCUE.class;
	private static final String CLASSNAME = "RTPDePacketizerMPEGTSMonitorCUE";

	private IMediaStream stream = null;
	private RTPDePacketizerMPEGTS rtDePacketizerMPEGTS = null;
	private boolean isTimecodeReady = false;
	private RTPTrack rtpTrack = null;
	private boolean debugLog = false;

	class MPEGTSMonitorCUE implements IMPEG2UserMonitorSectionNotify
	{
		@Override
		public void onMonitorStart()
		{
		}

		@Override
		public void onMonitorStop()
		{
		}

		@Override
		public void onDataSection(int pid, AdaptationField field, MPEG2Section section)
		{
			if (section.getTableID() == SpliceInformationTable.SIT_TABLE_ID)
			{
				try
				{
					SpliceInformationTable spliceInformationTable = new SpliceInformationTable(section);
					if (spliceInformationTable != null)
					{
						if (debugLog)
							WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME + ".onDataSection: " + spliceInformationTable.toString());

						SpliceInformationTableSerializeAMFContext serializeContext = new SpliceInformationTableSerializeAMFContext();

						serializeContext.timeReference = rtDePacketizerMPEGTS.getVideoTC();
						serializeContext.rtpTrack = rtpTrack;

						AMFDataObj amfData = spliceInformationTable.serializeAMF(serializeContext);
						if (amfData != null)
							stream.sendDirect("onCUE", amfData);
					}
				}
				catch(Exception e)
				{
					WMSLoggerFactory.getLogger(CLASS).error(CLASSNAME+".onDataSection: ", e);
				}
			}
		}
	}

	@Override
	public void onInit(RTPDePacketizerMPEGTS rtDePacketizerMPEGTS, RTPContext rtpContext, RTPDePacketizerItem rtpDePacketizerItem)
	{
		this.debugLog = rtDePacketizerMPEGTS.getProperties().getPropertyBoolean("rtpDePacketizerMPEGTSMonitorKLVDebugLog", this.debugLog);
	}

	@Override
	public void onStartup(RTPDePacketizerMPEGTS rtDePacketizerMPEGTS, RTPTrack rtpTrack)
	{
		WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME+".onStartup");

		this.rtDePacketizerMPEGTS = rtDePacketizerMPEGTS;
		this.rtpTrack = rtpTrack;

		RTPStream rtpStream = rtpTrack.getRTPStream();
		if (rtpStream != null)
			this.stream = rtpStream.getStream();
	}

	@Override
	public void onTimecodeReady(RTPDePacketizerMPEGTS rtDePacketizerMPEGTS)
	{
		WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME+".onTimecodeReady");

		this.isTimecodeReady = true;
	}

	@Override
	public void onPMT(RTPDePacketizerMPEGTS rtDePacketizerMPEGTS, ProgramMapTable newPMT)
	{
		WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME+".onPMT");

		boolean SCTE35RegDescFound = false;

		ArrayList<Descriptor> regDescriptors = newPMT.programDescriptors.get(Descriptor.DESCRIPTOR_TAG_REGISTRATION);

		if (regDescriptors != null && regDescriptors.size() > 0)
		{
			for (Descriptor desc : regDescriptors)
			{
				SCTE35RegDescFound |= ((RegistrationDescriptor)desc).formatIdentifier == RegistrationDescriptor.REG_IDENTIFICATION_SCTE_SPLICE_FORMAT;
			}
		}

		for (StreamInfo s : newPMT.streams.values())
		{
			if (SCTE35RegDescFound)
			{
				ArrayList<Descriptor> descriptors = null;

				if (descriptors == null)
					descriptors = s.descriptors.get(Descriptor.DESCRIPTOR_TAG_CUE_IDENTIFIER);

				if (descriptors == null)
					descriptors = s.descriptors.get(Descriptor.DESCRIPTOR_TAG_STREAM_IDENTIFIER);

				if (descriptors != null)
				{
					WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME+".onPMT: Hit cue point PID: 0x"+Integer.toHexString(s.PID));

					if (!rtDePacketizerMPEGTS.containsPIDMonitorMap(s.PID))
					{
						rtDePacketizerMPEGTS.putPIDMonitorMap(s.PID, new MPEGTSMonitorCUE());
					}
				}
			}
		}
	}
}

The code example above as MPEG-TS listener and PID monitor for processing ad markers and inserting the onCUE AMF data event into the stream with the ad marker data.

The are some question i want to know about

note : our topology is camera --> encoder --> transcoder(ffmpeg) + wowza origin --> edge server.

  • Where do put that code? do have to compile it become jar file?

Compile into a jar file and put under lib folder of Wowza, then configure the application to load that module, refer the guideline

  • Do i still need to setup

    Use generic Stream Target API to prepare Apple HLS streams for ad insertion (SCTE-35)

    because our vendor just need to get the SCTE-35 markers.

Setup Stream Target to output to UDP (MPEG-TS)

  • Do i need to put that code on all our wowza server?

Only on the server that processing the stream and insert the SCTE-35 marker, then output to MPEG-TS (UDP)

Checking with engineers this morning- will respond shortly. Thank you for your patience.

If you want to use the ‘RTPDePacketizerMPEGTSMonitorCUE’, unchanged, then there is a version that is included, so you don’t need to compile the example one. The name is com.wowza.wms.rtp.depacketizer.RTPDePacketizerMPEGTSMonitorCUE.

For HLS playback using splice markers, you do need to compile the Stream target code, but if you want to do something else with the data then you will need to use your own code. You would need to enable the depacketizer monitor on each server that is ingesting SCTE-35.

If you would like additional assistance setting up this workflow, you can post in our Hire a Consultant Forum or reach out in a support ticket if you receive error codes. Thanks.

@Rose Power-Wowza Community Manager,

we don’t want to do anything else with the data stream, so for our needs with need to setup and compile the java code an put it on our edge server, isn’t it? how-to-use-generic-stream-target-api-to-prepare-apple-hls-streams-for-ad-insertion-scte-35

@Rose Power-Wowza Community Manager,

we don’t want to do anything else with the data stream, so for our needs with need to setup and compile the java code an put it on our edge server, isn’t it? how-to-use-generic-stream-target-api-to-prepare-apple-hls-streams-for-ad-insertion-scte-35

Hello @Iyus Dedi Putra, thank you for that information. The engineers our requesting you submit a support ticket so they can take a closer look at it and walk you through the steps. They only do this through support tickets and not in the forums for tracking purposes and to properly run tests for you. This is the quickest way to make sure you have it setup correctly. They will looking for your ticket here:

https://www.wowza.com/support/open-ticket

Thanks and our engineers will help you with the proper configuration.