Wowza Community

Can't get live captioning and/or metadata working

We are trying to setup a scenario where we use Wowza to stream live video to clients, but inject captioning and metadata into the stream from an external source at various points in time. As such I have implemented the following module following guides found here:

public class CaptioningInjectionModule extends HTTProvider2Base {
	private IServer server;
	public void onServerInit(IServer server) {
		this.server = server;
	}
	@Override
	public void onHTTPRequest(IVHost ivHost, IHTTPRequest ihttpRequest, IHTTPResponse ihttpResponse) {
		String appName = ihttpRequest.getParameter("appName");
		String appInstanceName = ihttpRequest.getParameter("appInstanceName");
		appInstanceName = appInstanceName == null ? "_definst_" : appInstanceName;
		String streamName = ihttpRequest.getParameter("streamName");
		BufferedReader reader = new BufferedReader(new InputStreamReader(ihttpRequest.getInputStream()));
		String text = null;
		try {
			text = reader.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
		String language = ihttpRequest.getParameter("language");
		String trackId = ihttpRequest.getParameter("trackId");
		IMediaStream stream = ivHost.getApplication(appName).getAppInstance(appInstanceName).getStreams()
				.getStream(streamName);
		setCaption(stream, text, language, trackId);
	}
	private void setCaption(final IMediaStream stream, final String text, final String language, final String trackId) {
		if (stream != null) {
			AMFDataObj data = new AMFDataObj();
			data.put("text", new AMFDataItem(text));
			data.put("language", new AMFDataItem(language));
			data.put("trackid", new AMFDataItem(trackId));
			stream.sendDirect("onTextData", data);
			((MediaStream) stream).processSendDirectMessages();
		}
	}
}

and

public class MetadataInjectionModule extends HTTProvider2Base {
	private IServer server;
	public void onServerInit(IServer server) {
		this.server = server;
	}
	@Override
	public void onHTTPRequest(IVHost ivHost, IHTTPRequest ihttpRequest, IHTTPResponse ihttpResponse) {
		String appName = ihttpRequest.getParameter("appName");
		String appInstanceName = ihttpRequest.getParameter("appInstanceName");
		appInstanceName = appInstanceName == null ? "_definst_" : appInstanceName;
		String streamName = ihttpRequest.getParameter("streamName");
		BufferedReader reader = new BufferedReader(new InputStreamReader(ihttpRequest.getInputStream()));
		StringBuilder data = new StringBuilder();
		try {
			String line;
			while((line = reader.readLine()) != null) {
				data.append(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		IMediaStream stream = ivHost.getApplication(appName).getAppInstance(appInstanceName).getStreams()
				.getStream(streamName);
		injectMetadata(stream, data.toString());
	}
	private void injectMetadata(final IMediaStream stream, final String data) {
		if (stream != null) {
			AMFDataList amfList = new AMFDataList();
			amfList.add(new AMFDataItem("@setDataFrame"));
			amfList.add(new AMFDataItem("onMetaData"));
			AMFDataMixedArray metaData = new AMFDataMixedArray();
			metaData.put("param1", data);
			metaData.put("param2", new AMFDataItem("data2"));
			amfList.add(metaData);
			synchronized (stream) {
				byte[] dataData = amfList.serialize();
				int size = dataData.length;
				long timecode = Math.max(stream.getAudioTC(), stream.getVideoTC());
				stream.setDataTC(timecode);
				stream.setDataSize(size);
				stream.startDataPacket();
				stream.addDataData(dataData, 0, size);
			}
		}
	}
}

and set these up in my VHost.xml file:

<HTTPProvider>
	<BaseClass>gamesys.wowza.server.modules.CaptioningInjectionModule</BaseClass>
	<RequestFilters>caption*</RequestFilters>
	<AuthenticationMethod>none</AuthenticationMethod>
</HTTPProvider>
<HTTPProvider>
	<BaseClass>gamesys.wowza.server.modules.MetadataInjectionModule</BaseClass>
	<RequestFilters>metadata*</RequestFilters>
	<AuthenticationMethod>none</AuthenticationMethod>
</HTTPProvider>

Both endpoints work and I don’t see any exceptions in the Wowza server logs. My question is how can I get hold of this data on the client. I have tried various players like flowplayer and jwplayer by binding to their onMetadata or onCuepoint etc. events but I never seem to get anything come through. Is there a stage in the middle I am missing?

Hi,

Apologies if you have seen this already or if it’s not directly relevant but we do have an article covering live closed captioning which also includes a sample module including source for writing live captions using AMF onTextData events, which can be used with Flowplayer, JW Player etc.

Paul

Hi James,

Good to hear you’re progressing. We’ve the following article covering injecting metadata into RTMP (or HLS if converted to id3 tags). There may be others more relevant to you. I’d also suggest a look at our articles list or search the forum. I think these questions do pop up now and again.

Paul

Hi,

Apologies if you have seen this already or if it’s not directly relevant but we do have an article covering live closed captioning which also includes a sample module including source for writing live captions using AMF onTextData events, which can be used with Flowplayer, JW Player etc.

Paul

Thanks Paul,

This section: https://www.wowza.com/docs/how-to-configure-closed-captioning-for-live-streaming#ontextdata resolved my issue. Are there similar sample modules etc. for streaming live metadata?

Hi Paul,

I did have a read of the article you posted. I am just getting confused as some of the articles seem to be contradictory (although maybe it is my lack of understanding) i.e.

This article suggests that captions be injected by calling stream.sendDirect(“onTextData”…) and metadata be injected by calling stream.addDataData(…)

https://www.wowza.com/docs/how-to-inject-cue-points-or-metadata

But then this article is talking about converting “onTextData” events to ID3 metadata, even though the prior one says to use this for captioning:

https://www.wowza.com/docs/how-to-convert-ontextdata-events-in-a-live-or-vod-stream-to-timed-events-id3-tags-in-an-apple-hls-stream

Maybe there is more than one way to do this but as a novice its quite confusing.