How to control cropping dynamically with the Wowza transcoding API

The Wowza transcoder does allow you to configure many stream items before encoding starts providing very granular control, dynamically.

Outlined in this article is the use of the ILiveStreamTranscoderNotify and ILiveStreamTranscoderActionNotify interfaces. The application module adds a listener using addLiveStreamTranscoderListener to the application instance and from there adds an additional listener addActionListener.

Each class is shown below

TemplateControl Class

package guru.thewowza.example.transcoder.crop.dynamic;

import com.wowza.wms.application.*;
import com.wowza.wms.module.*;

public class TemplateControl extends ModuleBase {
	
	private TranscoderNotifier transNotify = null;
	private Logger log = null;
	
	public void onAppStart(IApplicationInstance appInstance) {
		String fullname = appInstance.getApplication().getName() + "/"
				+ appInstance.getName();
		
		this.log = new Logger(getLogger());
 		this.transNotify = new TranscoderNotifier(this.log);
		appInstance.addLiveStreamTranscoderListener(this.transNotify);
		this.log.LogMessage("onAppStart: " + fullname);
	}

	public void onAppStop(IApplicationInstance appInstance) {
		String fullname = appInstance.getApplication().getName() + "/"
				+ appInstance.getName();
		
		this.log.LogMessage("onAppStop: " + fullname);
	}
	

}

TranscoderNotifier Class

package guru.thewowza.example.transcoder.crop.dynamic;

import com.wowza.wms.stream.IMediaStream;
import com.wowza.wms.stream.livetranscoder.ILiveStreamTranscoder;
import com.wowza.wms.stream.livetranscoder.ILiveStreamTranscoderNotify;
import com.wowza.wms.transcoder.model.LiveStreamTranscoder;

public class TranscoderNotifier implements ILiveStreamTranscoderNotify
	{
	private Logger log = null;

	public TranscoderNotifier ( Logger log )
		{ this.log = log; }	
		 
	public void onLiveStreamTranscoderCreate(ILiveStreamTranscoder liveStreamTranscoder, IMediaStream stream) 
	{
		((LiveStreamTranscoder)liveStreamTranscoder).addActionListener(
				new TranscoderActionNotifier(stream,this.log));
	}
	  
	public void onLiveStreamTranscoderDestroy(ILiveStreamTranscoder liveStreamTranscoder, IMediaStream stream) {}
	public void onLiveStreamTranscoderInit(ILiveStreamTranscoder liveStreamTranscoder, IMediaStream stream) {} 
	}

ILiveStreamTranscoderActionNotify Class

package guru.thewowza.example.transcoder.crop.dynamic;

import java.util.Iterator;
import java.util.List;

import com.wowza.wms.application.IApplicationInstance;
import com.wowza.wms.media.model.MediaCodecInfoAudio;
import com.wowza.wms.media.model.MediaCodecInfoVideo;
import com.wowza.wms.stream.IMediaStream;
import com.wowza.wms.stream.livetranscoder.LiveStreamTranscoderItem;
import com.wowza.wms.transcoder.model.ILiveStreamTranscoderActionNotify;
import com.wowza.wms.transcoder.model.LiveStreamTranscoder;
import com.wowza.wms.transcoder.model.TranscoderSessionAudioEncode;
import com.wowza.wms.transcoder.model.TranscoderSessionDataEncode;
import com.wowza.wms.transcoder.model.TranscoderSessionDestination;
import com.wowza.wms.transcoder.model.TranscoderSessionVideoEncode;
import com.wowza.wms.transcoder.model.TranscoderStream;
import com.wowza.wms.transcoder.model.TranscoderStreamDestination;
import com.wowza.wms.transcoder.model.TranscoderStreamNameGroup;

class TranscoderActionNotifier implements ILiveStreamTranscoderActionNotify
{
	  private IMediaStream transSourceStream = null;
	  private Logger log = null;

		
			public TranscoderActionNotifier (
					IMediaStream transsourcestream,
					Logger log
					)
				{
				this.transSourceStream = transsourcestream;
				this.log = log;
				}

 
	public void onInitBeforeLoadTemplate(LiveStreamTranscoder liveStreamTranscoder) {

		// Here we output the template name, the template folder and the source 
		// stream name.
		
		 this.log.LogMessage("Template name is "+liveStreamTranscoder.getTemplateName());
		 this.log.LogMessage("Template folder is "+liveStreamTranscoder.getTemplateDir());
		 this.log.LogMessage("Source stream name is "+this.transSourceStream.getName());
		 
		// Here we output we are changing the template name.
		// You could additional logic to the template selection based on stream name
		// or other criteria
		 	 
		 //liveStreamTranscoder.setTemplateName("myTemplateName.xml");
		}			
			
 public void onInitStart(LiveStreamTranscoder liveStreamTranscoder, String streamName, String transcoderName, 
IApplicationInstance appInstance, LiveStreamTranscoderItem liveStreamTranscoderItem) { }

 public void onInitAfterLoadTemplate(LiveStreamTranscoder liveStreamTranscoder) { 
	 
		while(true)
		 	{
			 TranscoderStream transcoderStream = liveStreamTranscoder.getTranscodingStream();
			 if (transcoderStream == null)
			 	{
				break;
			 	}
			 List destinations = transcoderStream.getDestinations();
			 Iterator iter = destinations.iterator();
			 while(iter.hasNext())
			 	{
				 TranscoderStreamDestination destination = iter.next();
				 
				 if ( destination.isEnable() )
				 {
					 String destStreamName = destination.getStreamName();
					 this.log.LogMessage("Destination streamname is '"+destStreamName+"'");
					 if ( destStreamName.endsWith("360p") )
					 {
						 // Crop numbers are
						 // Pixels from left
						 // Pixels from right
						 // Pixels from top
						 // Pixels from bottom
						 int[] cropSettings = { 50,60,70, 80 }; 
						 this.log.LogMessage("Adding crop to '"+destStreamName+"' L-50, R-60, T-70, B-80");
						 destination.getVideo().setCrop(cropSettings);
					 }
				 }
			 	
			 	}
	   break;
	  }
 }
 public void onInitStop(LiveStreamTranscoder liveStreamTranscoder) { }
 public void onCalculateSourceVideoBitrate(LiveStreamTranscoder liveStreamTranscoder, long bitrate) { }
 public void onCalculateSourceAudioBitrate(LiveStreamTranscoder liveStreamTranscoder, long bitrate) { }
 public void onSessionDestinationCreate(LiveStreamTranscoder liveStreamTranscoder, TranscoderSessionDestination sessionDestination) { }
 public void onSessionVideoEncodeCreate(LiveStreamTranscoder liveStreamTranscoder, TranscoderSessionVideoEncode sessionVideoEncode) { }
 public void onSessionAudioEncodeCreate(LiveStreamTranscoder liveStreamTranscoder, TranscoderSessionAudioEncode sessionAudioEncode) { }
 public void onSessionDataEncodeCreate(LiveStreamTranscoder liveStreamTranscoder, TranscoderSessionDataEncode sessionDataEncode) { }
 public void onSessionVideoEncodeInit(LiveStreamTranscoder liveStreamTranscoder, TranscoderSessionVideoEncode sessionVideoEncode) { }
 public void onSessionAudioEncodeInit(LiveStreamTranscoder liveStreamTranscoder, TranscoderSessionAudioEncode sessionAudioEncode) { }
 public void onSessionDataEncodeInit(LiveStreamTranscoder liveStreamTranscoder, TranscoderSessionDataEncode sessionDataEncode) { }
 public void onSessionVideoEncodeSetup(LiveStreamTranscoder liveStreamTranscoder, TranscoderSessionVideoEncode sessionVideoEncode) { }
 public void onSessionAudioEncodeSetup(LiveStreamTranscoder liveStreamTranscoder, TranscoderSessionAudioEncode sessionAudioEncode) { }
 public void onSessionVideoEncodeCodecInfo(LiveStreamTranscoder liveStreamTranscoder, TranscoderSessionVideoEncode sessionVideoEncode, MediaCodecInfoVideo codecInfoVideo) { }
 public void onSessionAudioEncodeCodecInfo(LiveStreamTranscoder liveStreamTranscoder, TranscoderSessionAudioEncode sessionAudioEncode, MediaCodecInfoAudio codecInfoAudio) { }
 public void onSessionVideoDecodeCodecInfo(LiveStreamTranscoder liveStreamTranscoder, MediaCodecInfoVideo codecInfoVideo) { }
 public void onSessionAudioDecodeCodecInfo(LiveStreamTranscoder liveStreamTranscoder, MediaCodecInfoAudio codecInfoAudio) { }
 public void onRegisterStreamNameGroup(LiveStreamTranscoder liveStreamTranscoder, TranscoderStreamNameGroup streamNameGroup) { }
 public void onUnregisterStreamNameGroup(LiveStreamTranscoder liveStreamTranscoder, TranscoderStreamNameGroup streamNameGroup) { }
 public void onShutdownStart(LiveStreamTranscoder liveStreamTranscoder) { } 
 public void onShutdownStop(LiveStreamTranscoder liveStreamTranscoder) { }
 public void onResetStream(LiveStreamTranscoder liveStreamTranscoder) { }
}

Logger Class

package guru.thewowza.example.transcoder.crop.dynamic;

import com.wowza.wms.logging.WMSLogger;

public class Logger 
	{
	private WMSLogger logger = null;
	
	public Logger( WMSLogger Log ) 
		{ logger = Log; }
	
	public void LogMessage(	String Message ) 
	{
		logger.info("guru.thewowza.example.transcoder.crop.dynamic: '"+Message+"'");
	}

	}

The source code crops any rendition which ends with 360p in the name and the crop functionality is 4 numbers which represent how the image should be cropped. The example given in any Wowza transcoding template is 0,0,0,0 and they mean the following

First number – Number of pixels to crop from the LEFT
Second Number – Number of pixels to crop from the RIGHT
Third Number – Number of pixels to crop from the TOP
Forth Number – Number of pixels to crop from the BOTTOM

The example code is set to crop 50,60,70,80 so making it simpler to follow in this example with a logging statement.

To use the example code you will need to add a Module into your Application as the last module in the Modules section.

<Module>
	<Name>CropControl</Name>
	<Description>CropControl</Description>
	<BaseClass>guru.thewowza.example.transcoder.crop.dynamic.TemplateControl</BaseClass>
</Module>

Comments are closed.