Wowza Community

OnTextData Events issue

Hello!

I have been trying to inject my srt files following this article:

https://www.wowza.com/docs/how-to-configure-closed-captioning-for-live-streaming

I know I needed to create a new module using Eclipse and I found this example here in the forum.

I’m studying the code but I couldn’t find the error.

package com.morbido.wowza;

import com.wowza.wms.application.*;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import com.wowza.wms.amf.*;
import com.wowza.wms.client.*;
import com.wowza.wms.module.*;
import com.wowza.wms.request.*;
import com.wowza.wms.stream.*;
import com.wowza.wms.timedtext.model.ITimedTextConstants;
import com.wowza.wms.timedtext.model.ITimedTextReader;
import com.wowza.wms.timedtext.model.TimedTextEntry;
import com.wowza.wms.timedtext.model.TimedTextLanguageRendition;
import com.wowza.wms.timedtext.model.TimedTextReaderFactory;
import com.wowza.wms.timedtext.model.TimedTextRepresentation;
import com.wowza.wms.rtp.model.*;
import com.wowza.wms.httpstreamer.model.*;
import com.wowza.wms.httpstreamer.cupertinostreaming.httpstreamer.*;
import com.wowza.wms.httpstreamer.smoothstreaming.httpstreamer.*;
import com.wowza.wms.livestreamrecord.model.ILiveStreamRecord;
import com.wowza.wms.livestreamrecord.model.LiveStreamRecorderMP4;
import com.wowza.wms.media.h264.H264SEIMessages;
import com.wowza.wms.media.model.MediaCodecInfoAudio;
import com.wowza.wms.media.model.MediaCodecInfoVideo;
//Module created by brian and scott to
//1. read SRT data and inject as CEA608 data into a live stream created by stream demo publisher
//2. Create a VOD asset with CEA608 data in itand create
public class ModulePublishSRTAsOnTextData extends ModuleBase
{
	public class MySEIListener implements IMediaStreamH264SEINotify
	{
		TimedTextEntry currCaption = null;
		
		public void onVideoH264Packet(IMediaStream stream, AMFPacket packet, H264SEIMessages seiMessages)
		{
			String text = null;
			boolean sendEvent = false;
			long currTime = packet.getAbsTimecode();
			
			if (!hasSrtFile())
				return;
			
			TimedTextEntry caption = getCaption(currTime);
			// set text to current active caption
			if (caption != null && caption != currCaption)
			{
				text = caption.getText();
				sendEvent = true;
			}
			// if we have an event, send it
			if (sendEvent)
			{
				sendTextDataMessage(stream, text);
				this.currCaption = caption;
				getLogger().info("------- packet Time="+currTime+" "+text);
			}
		}
	}
	
	public class MyMediaStreamListener implements IMediaStreamActionNotify3
	{
		private Map<String, ILiveStreamRecord> recorders = new HashMap<String, ILiveStreamRecord>();
		public void onPublish(IMediaStream stream, String streamName, boolean isRecord, boolean isAppend)
		{
			IApplicationInstance appInstance = stream.getStreams().getAppInstance();
			
			if (!stream.isTranscodeResult())
			{
				
			}
			// read the .srt file for this stream if it exits
			List<TimedTextEntry> list = simpleSRTParse(appInstance, stream);
			setTimedTextList(list);
//			if (hasSrtFile())
//				startRecording(stream, streamName);
		}
		public void onUnPublish(IMediaStream stream, String streamName, boolean isRecord, boolean isAppend)
		{
			// clear the list
			setTimedTextList(null);
//			stopRecording(stream, streamName);
		}
		public void onMetaData(IMediaStream stream, AMFPacket metaDataPacket)
		{
		}
		public void onPauseRaw(IMediaStream stream, boolean isPause, double location)
		{
		}
		public void onPause(IMediaStream stream, boolean isPause, double location)
		{
		}
		public void onPlay(IMediaStream stream, String streamName, double playStart, double playLen, int playReset)
		{
		}
		public void onSeek(IMediaStream stream, double location)
		{
		}
		public void onStop(IMediaStream stream)
		{
		}
		public void onCodecInfoVideo(IMediaStream stream, MediaCodecInfoVideo codecInfoVideo)
		{
		}
		public void onCodecInfoAudio(IMediaStream stream, MediaCodecInfoAudio codecInfoAudio)
		{
		}
		
		
	}
	// local vars
	private List<TimedTextEntry> timedTextList = null;
	private boolean charsetTest = false;
	private final Charset UTF8_CHARSET = Charset.forName("UTF-8"); 
	private boolean foundSrt = false;
	// app startup processing
	public void onAppStart(IApplicationInstance appInstance)
	{
		getLogger().info("ModulePublishSRTAsOnTextData.onAppStart["+appInstance.getContextStr()+"]");
		
		String onTextDataFile = "${com.wowza.wms.context.VHostConfigHome}/content/ontextdata.txt";
		//publishInterval = appInstance.getProperties().getPropertyInt("publishOnTextDataPublishInterval", publishInterval);
		//onTextDataFile = appInstance.getProperties().getPropertyStr("publishOnTextDataFile", onTextDataFile);
		charsetTest = appInstance.getProperties().getPropertyBoolean("publishOnTextCharsetTest", charsetTest);
		Map<String, String> pathMap = new HashMap<String, String>();
		pathMap.put("com.wowza.wms.context.VHost", appInstance.getVHost().getName());
		pathMap.put("com.wowza.wms.context.VHostConfigHome", appInstance.getVHost().getHomePath());
		pathMap.put("com.wowza.wms.context.Application", appInstance.getApplication().getName());
		pathMap.put("com.wowza.wms.context.ApplicationInstance", appInstance.getName());
	}
	
	// hookup stream listeners
	public void onStreamCreate(IMediaStream stream)
	{
		stream.addClientListener(new MyMediaStreamListener());
		stream.addVideoH264SEIListener(new MySEIListener());
	}
	
	// save the timedTextList
	private void setTimedTextList(List<TimedTextEntry> list)
	{
		this.timedTextList = list;
	}
	
	// find and parse .srt file for the specified stream
	private List<TimedTextEntry> simpleSRTParse(IApplicationInstance appInstance, IMediaStream stream)
	{	
		List<TimedTextEntry> list = null;
		String extension = ITimedTextConstants.TIMED_TEXT_READER_EXTENSION_SRT;
		String fileName = stream.getName()+"."+extension;
		String contentPath = stream.getStreamFileForRead().getParent();  // get stream content path
		
		// create and configure a MediaReaderItem for use with TimedTextReaderFactory
		MediaReaderItem mri = new MediaReaderItem(ITimedTextConstants.TIMED_TEXT_READER_EXTENSION_SRT, ITimedTextConstants.DEFAULT_TIMED_TEXT_READER_SRT);
		mri.setFileExtension(ITimedTextConstants.TIMED_TEXT_READER_EXTENSION_SRT);
		// create a TimedTextReader for the .srt file associated with this stream
		ITimedTextReader reader = TimedTextReaderFactory.getInstance(appInstance, mri, contentPath, fileName, extension);
		
		if (reader != null)
		{
			reader.open();
			TimedTextRepresentation tt = reader.getTimedText();
			reader.close();
			if (tt != null)
			{
				TimedTextLanguageRendition rend = tt.getLanguageRendition(Locale.getDefault().getISO3Language());
				// get the list of TimedTextItems
				list = rend.getTimedText();
				this.foundSrt = true;
			}
			else
			{
				getLogger().info("--- No srt file found for "+contentPath+"\\"+stream.getName());
			}
		}
		//dumpTimedTextList(list);
		return list;
	}
	
	// send OnTextData event
	private void sendTextDataMessage(IMediaStream stream, String text)
	{
		try
		{
			AMFDataObj amfData = new AMFDataObj();
			
			amfData.put("text", new AMFDataItem(text));
			amfData.put("language", new AMFDataItem("spa"));
			amfData.put("trackid", new AMFDataItem(99));
							
			
			stream.sendDirect("onTextData", amfData);
			//stream.addDataData(data, offset, size);
		}
		catch(Exception e)
		{
			getLogger().error("ModulePublishSRTAsOnTextData#PublishThread.sendTextDataMessage["+stream.getContextStr()+"]: "+e.toString());
			e.printStackTrace();
		}
	}
	// get the caption active during the time passed in
	private TimedTextEntry getCaption(long time)
	{
		TimedTextEntry entry = null;
		Iterator<TimedTextEntry> itr = this.timedTextList.iterator();
		while(itr.hasNext())
		{
			TimedTextEntry tte = itr.next();
			if (tte.getStartTime() <= time && time < tte.getEndTime())
			{
				entry = tte;
				break;
			}
		}
		return entry;
	}
	
	private boolean hasSrtFile()
	{
		return this.foundSrt;
	}
	// dump the list of TimedTextEntries created from .srt file
	private void dumpTimedTextList(List<TimedTextEntry> list)
	{
		Iterator<TimedTextEntry> itr = list.iterator();
		getLogger().info("--- TimedTextList ----");
		while(itr.hasNext())
		{
			TimedTextEntry tte = itr.next();
			getLogger().info("s:"+tte.getStartTime()+", "+tte.getText()+", e:"+tte.getEndTime());
		}
		getLogger().info("--- ------------ ----");
	}
	
}

I see the results of the captions in the log of my server.

I’m using VLC place to play the video in the RTMP but I can’t see the captions.

I couldn’t find more information about my problem.

Someone could help me please?

Thank you!

Hello,

I do apologize for the long delay in replying, and hope that you have already worked through the issue you were facing through our Support Ticket system.

Please let me know if you are still having any issues with your custom module for Closed Captioning, and if you can explain the outcome you are trying to achieve in greater detail.

Thanks in advance,

Jason Hatchett