[Livestream] How can we create a snapshot of 1 video stream through HTTPProvider

Hi guys,

I have managed to use HTTPProvider to create thumbnails/snapshots for the stream that is transcoded. But I wonder if it is possible for me to use/re-use/re-factor the code of CreateSnapshot to create the HTTPProvider for capturing snapshots of any stream ?

I have already taken a look at here, there was a comment of ripburn trying to create the HTTPProvider, I tried but it’s not working, here is his codes:

package com.test.wms.CreateSnapshot;
import java.io.*;
import com.wowza.util.FLVUtils;
import com.wowza.wms.amf.AMFPacket;
import com.wowza.wms.application.IApplicationInstance;
import com.wowza.wms.http.*;
import com.wowza.wms.logging.*;
import com.wowza.wms.stream.IMediaStream;
import com.wowza.wms.vhost.*;
public class HTTPInterfaceTest extends HTTProvider2Base {
	public void onHTTPRequest(IVHost vhost, IHTTPRequest req, IHTTPResponse resp) {
		if (!doHTTPAuthentication(vhost, req, resp))
			return;
		
		/*
		 * The variable appName must match whatever application name the live stream is broadcasting to.
		 * 
		 * This provider must be called with a "stream-name" parameter specifying the stream name in the encoder
		 * 
		 * i.e.  http://127.0.0.1:8086/createsnapshot?stream-name=video
		 * 
		 * The "stream-name" parameter can be changed by changing the streamParameterName value below.
		 * 
		 * After the URL call (which can be done in the browser) it will either output the error or 
		 * output the full path of the generated FLV file.
		 */
		OutputStream out = resp.getOutputStream();
		String appName = "live";
		String instanceName = IApplicationInstance.DEFAULT_APPINSTANCE_NAME;
		String responseString = "";
		String streamParameterName = "stream-name";
		
		try
		{
			if (!vhost.applicationExists(appName)) {
				throw new Exception("The folder for " + appName + " is missing in the applications folder.");
			}
			
			if (!vhost.getApplication("live").isAppInstanceLoaded(instanceName)) {
				throw new Exception("The application " + appName + " is not running.  You must broadcast to it before calling the snapshot creator.");
			}
			
			if (!req.getParameterNames().contains(streamParameterName)) {
				throw new Exception("The parameter " + streamParameterName + " must be specified in the URL");				
			}
						
			IApplicationInstance instance = vhost.getApplication(appName).getAppInstance(instanceName);
			String streamName = req.getParameterMap().get(streamParameterName).get(0);			
			IMediaStream stream = instance.getStreams().getStream(streamName);
			
			if (stream == null) {
				throw new Exception("The stream name " + streamName + " is invalid.  Does it match the value in the encoder?");
			}
			
			AMFPacket packet = stream.getLastKeyFrame();
			
			if (packet == null) {
				throw new Exception("Unable to grab screenshot.  Try again shortly.");
			}
			
			//String flvFilename = "app_" + appName + "-stream_" + streamName + "-timecode_" + String.valueOf(packet.getAbsTimecode());
			//File flvFile = stream.getStreamFileForWrite(flvFilename, null, null);			
			//String flvFilePath = flvFile.getAbsolutePath();
			//flvFilePath = flvFilePath.substring(0, flvFilePath.length() - 4) + ".flv";
			
			String flvFilename = streamName + "_" + packet.getAbsTimecode() + ".flv";
			File flvFile = stream.getStreamFileForWrite(streamName, null, null);			
			String flvFilePath = flvFile.getPath().substring(0, flvFile.getPath().length()-4) + "_" + packet.getAbsTimecode() + ".flv";
			
			Object lock = new Object();
			
			String flvDetails = "";
			
			synchronized(lock) {
				BufferedOutputStream bufferedOut = new BufferedOutputStream(new FileOutputStream(new File(flvFilePath), false));
				FLVUtils.writeHeader(bufferedOut, 0, null);
				AMFPacket codecConfig = stream.getVideoCodecConfigPacket(packet.getAbsTimecode());
				
				if (codecConfig != null) {
					FLVUtils.writeChunk(bufferedOut, codecConfig.getDataBuffer(), codecConfig.getSize(), 0, (byte)codecConfig.getType());
					
					switch(codecConfig.getType()) {
						case IVHost.CONTENTTYPE_AUDIO:
							flvDetails = "FLV codec info is audio. ";
							break;
							
						case IVHost.CONTENTTYPE_UKNOWN:
							flvDetails = "FLV codec info is unknown. ";
							break;
							
						case IVHost.CONTENTTYPE_VIDEO:
							flvDetails = "FLV codec info is video. ";
							break;
							
						default:
							flvDetails = "FLV codec info is type " + String.valueOf(codecConfig.getType()) + ". ";			
					}					
					
				} else {
					flvDetails = "FLV codec info is missing. ";
					
				}
				
				FLVUtils.writeChunk(bufferedOut, packet.getDataBuffer(), packet.getSize(), 0, (byte)packet.getType());
				
				switch(packet.getType()) {
					case IVHost.CONTENTTYPE_AUDIO:
						flvDetails += "FLV packet info is audio. ";
						break;
						
					case IVHost.CONTENTTYPE_UKNOWN:
						flvDetails += "FLV packet info is unknown. ";
						break;
						
					case IVHost.CONTENTTYPE_VIDEO:
						flvDetails += "FLV packet info is video. ";
						break;
						
					default:
						flvDetails += "FLV packet info is type " + String.valueOf(codecConfig.getType()) + ". ";					
				}				
				
				bufferedOut.close();
			}
			responseString = "Single-frame FLV saved successfully in " + flvFilePath + ". " + flvDetails;		
			byte[] outBytes = responseString.getBytes();			
			out.write(outBytes);
		}
		catch (IOException e) {
			WMSLoggerFactory.getLogger(null).error("HTTPInterfaceTest IOException: " + e.toString());
		}
		catch (Exception e) {
			WMSLoggerFactory.getLogger(null).error("HTTPInterfaceTest Exception: " + e.toString());
						
			try {
				responseString = e.toString();
				byte[] outBytes = responseString.getBytes();
				out.write(outBytes);
			} catch (IOException e1) {
				WMSLoggerFactory.getLogger(null).error("HTTPInterfaceTest IOException: " + e1.toString());
			}
		}
	}
}

I have added the HTTPProvider as he commented

<HTTPProvider>
    <BaseClass>com.test.wms.CreateSnapshot.HTTPInterfaceTest</BaseClass>
    <RequestFilters>createsnapshot*</RequestFilters>
    <AuthenticationMethod>none</AuthenticationMethod>
</HTTPProvider>

Basically I don’t want (actually I can’t) use the Flash Client to get the image, because I’m gonna build up a web service for receiving a command (record/stop/snapshot) and return the URI of the videos/images. I worked with WCF web services. If it’s possible to use the Flash Client to do this, please give me some hints, I really appreciate all of your suggestions :slight_smile:

In additional, I was trying to work with Wowza IDE, but when I tried to start Wowza IDE, it showed bunch of errors (I started Wowza IDE after turning of all the Wowza Streaming Engine services). I don’t have Java installed, just the JRE, is it the cause of this ?

Best.

Anh-Dzung

I tried your HTTPProvider, and it worked great.

But I found that it only worked with VP6 video, not h.264, which I think is a limitation of the module you started with.

Richard

I will see what I can find out about extending it. There isn’t as much VP6 and Spark video around anymore.

Richard

Hello,

I compiled this module, added a possibility to download created .flv via http instead of creating files on the server side etc.

It works great on Wowza 3.6 but on 4.x module hangs on this line

FLVUtils.writeChunk(bufferedOut, codecConfig.getDataBuffer(), codecConfig.getSize(), 0, (byte)codecConfig.getType());

I dont know why. Any clues? What changed in 4.x here?

Hi,

I suspect this is related to the the change from FLV being the default stream prefix to MP4 now being the default.

You can switch back to FLV to see if that is the issue. In the UI, under server > server setup. Click edit.

Change the Default Stream Prefix from MP4 to FLV. Then restart the server through the UI to effect the change.

Daren

Hi,

The trick with h.264 is to write the same packet twice to the flv.

				AMFPacket codecConfig = stream.getVideoCodecConfigPacket(packet.getAbsTimecode());
				if (codecConfig != null)
					FLVUtils.writeChunk(bufferedOut, codecConfig.getDataBuffer(), codecConfig.getSize(), 0, (byte)codecConfig.getType());
				FLVUtils.writeChunk(bufferedOut, packet.getDataBuffer(), packet.getSize(), 0, (byte)packet.getType());
				FLVUtils.writeChunk(bufferedOut, packet.getDataBuffer(), packet.getSize(), 100, (byte)packet.getType());

Not sure why it works , it just does.

Roger.

all I had was the text “Wowza Streaming Engine 4 Trial Edition (Expires: sept. 19, 2014) 4.0.3 build10989”

The reason you will be getting this response is because your HttpProvider is not configured properly in the VHost.xml Hostport configuration.

You need to edit the VHost.xml and add your HTTPProvider.

Roger.

Now it works like charm. Many thanks for your help Roger!!! It would be great for other guys who want to do the same with H264 to know this if you can update it in the CreateSnapshot as well :wink:

Good to hear it is working. I have added a note to update the CreateSnapshot module for the next release of the collection.

Roger.

Hi Richard,

Thanks for your response, it helps me a lot.

yes, I just tried with the h.264 video. I don’t know if there is another module which is suitable for this video stream.

Regards

AD

I will see what I can find out about extending it. There isn’t as much VP6 and Spark video around anymore.

Richard

It will be a great deal if you can manage to get some snapshot through HTTP Provider, with it, I will have all basic functions I need from Wowza!

Additionally, when I tried to do some outputs (html format) to test how the code flows, if I don’t have the exception, I always got the

Wowza Streaming Engine 4 Trial Edition (Expires: sept. 19, 2014) 4.0.3 build10989

result, although after the checking code (of streamname, …), I tried to output the hello word phrase in the web but fail. Is that greetings is the default return value for wowza when we call http provider stuff ?

Regards

Hi,

The trick with h.264 is to write the same packet twice to the flv.

				AMFPacket codecConfig = stream.getVideoCodecConfigPacket(packet.getAbsTimecode());
				if (codecConfig != null)
					FLVUtils.writeChunk(bufferedOut, codecConfig.getDataBuffer(), codecConfig.getSize(), 0, (byte)codecConfig.getType());
				FLVUtils.writeChunk(bufferedOut, packet.getDataBuffer(), packet.getSize(), 0, (byte)packet.getType());
				FLVUtils.writeChunk(bufferedOut, packet.getDataBuffer(), packet.getSize(), 100, (byte)packet.getType());

Not sure why it works , it just does.

Roger.

Hi Roger,

Thanks for your help, but when I tried to add your codes into the current work, there’s still now flv image appears, all I had was the text “Wowza Streaming Engine 4 Trial Edition (Expires: sept. 19, 2014) 4.0.3 build10989”

Here are my codes so far.

	public void onHTTPRequest(IVHost vhost, IHTTPRequest req, IHTTPResponse resp) {
		if (!doHTTPAuthentication(vhost, req, resp))
			return;
		
		/*
		String helloStr1 = "Hello world";
		String retStr1 = "<html><head><title>" + helloStr1
				+ "</title></head><body>" + helloStr1 + "</body></html>";
		try {
			OutputStream out = resp.getOutputStream();
			byte[] outBytes = retStr1.getBytes();
			out.write(outBytes);
		} catch (Exception e) {
			WMSLoggerFactory.getLogger(null).error(
					"MyHTTPProvider: " + e.toString());
		}
		*/
		/*
		 * The variable appName must match whatever application name the live stream is broadcasting to.
		 * 
		 * This provider must be called with a "stream-name" parameter specifying the stream name in the encoder
		 * 
		 * i.e.  http://127.0.0.1:8086/createsnapshot?stream-name=video
		 * 
		 * The "stream-name" parameter can be changed by changing the streamParameterName value below.
		 * 
		 * After the URL call (which can be done in the browser) it will either output the error or 
		 * output the full path of the generated FLV file.
		 */
		OutputStream out = resp.getOutputStream();
		String appName = "live";
		String instanceName = IApplicationInstance.DEFAULT_APPINSTANCE_NAME;
		String responseString = "";
		String streamParameterName = "stream-name";
		
		try
		{
			if (!vhost.applicationExists(appName)) {
				throw new Exception("The folder for " + appName + " is missing in the applications folder.");
			}
			
			if (!vhost.getApplication("live").isAppInstanceLoaded(instanceName)) {
				throw new Exception("The application " + appName + " is not running.  You must broadcast to it before calling the snapshot creator.");
			}
			
			if (!req.getParameterNames().contains(streamParameterName)) {
				throw new Exception("The parameter " + streamParameterName + " must be specified in the URL");				
			}
				
			String helloStr1 = "Instance ok!";
			String retStr1 = "<html><head><title>" + helloStr1
					+ "</title></head><body>" + helloStr1 + "</body></html>";
			try {
				//OutputStream out = resp.getOutputStream();
				byte[] outBytes = retStr1.getBytes();
				out.write(outBytes);
			} catch (Exception e) {
				WMSLoggerFactory.getLogger(null).error(
						"MyHTTPProvider: " + e.toString());
			}
			
			
			IApplicationInstance instance = vhost.getApplication(appName).getAppInstance(instanceName);
			
						
			
			String streamName = req.getParameterMap().get(streamParameterName).get(0);			
			IMediaStream stream = instance.getStreams().getStream(streamName);
			
			String helloStr = "Hello World! You are trying to get snapshot from stream: " + streamParameterName;
			String retStr = "<html><head><title>" + helloStr
					+ "</title></head><body>" + helloStr + "</body></html>";
			try {
				OutputStream output = resp.getOutputStream();
				byte[] outBytes = retStr.getBytes();
				output.write(outBytes);
			} catch (Exception e) {
				WMSLoggerFactory.getLogger(null).error(
						"MyHTTPProvider: " + e.toString());
			}
			
			if (stream == null) {
				throw new Exception("The stream name " + streamName + " is invalid.  Does it match the value in the encoder?");
			}
			
			AMFPacket packet = stream.getLastKeyFrame();
			
			if (packet == null) {
				throw new Exception("Unable to grab screenshot.  Try again shortly.");
			}
			
			//String flvFilename = "app_" + appName + "-stream_" + streamName + "-timecode_" + String.valueOf(packet.getAbsTimecode());
			//File flvFile = stream.getStreamFileForWrite(flvFilename, null, null);			
			//String flvFilePath = flvFile.getAbsolutePath();
			//flvFilePath = flvFilePath.substring(0, flvFilePath.length() - 4) + ".flv";
			
			String flvFilename = streamName + "_" + packet.getAbsTimecode() + ".flv";
			File flvFile = stream.getStreamFileForWrite(streamName, null, null);			
			String flvFilePath = flvFile.getPath().substring(0, flvFile.getPath().length()-4) + "_" + packet.getAbsTimecode() + ".flv";
			
			Object lock = new Object();
			
			String flvDetails = "";
			
			synchronized(lock) {
				BufferedOutputStream bufferedOut = new BufferedOutputStream(new FileOutputStream(new File(flvFilePath), false));
				FLVUtils.writeHeader(bufferedOut, 0, null);
				AMFPacket codecConfig = stream.getVideoCodecConfigPacket(packet.getAbsTimecode());
				
				if (codecConfig != null) {
					FLVUtils.writeChunk(bufferedOut, codecConfig.getDataBuffer(), codecConfig.getSize(), 0, (byte)codecConfig.getType());
					
					switch(codecConfig.getType()) {
						case IVHost.CONTENTTYPE_AUDIO:
							flvDetails = "FLV codec info is audio. ";
							break;
							
						case IVHost.CONTENTTYPE_UKNOWN:
							flvDetails = "FLV codec info is unknown. ";
							break;
							
						case IVHost.CONTENTTYPE_VIDEO:
							flvDetails = "FLV codec info is video. ";
							break;
							
						default:
							flvDetails = "FLV codec info is type " + String.valueOf(codecConfig.getType()) + ". ";			
					}					
					
				} else {
					flvDetails = "FLV codec info is missing. ";
					
				}
				
				FLVUtils.writeChunk(bufferedOut, packet.getDataBuffer(), packet.getSize(), 0, (byte)packet.getType());
				FLVUtils.writeChunk(bufferedOut, packet.getDataBuffer(), packet.getSize(), 100, (byte)packet.getType());
				
				switch(packet.getType()) {
					case IVHost.CONTENTTYPE_AUDIO:
						flvDetails += "FLV packet info is audio. ";
						break;
						
					case IVHost.CONTENTTYPE_UKNOWN:
						flvDetails += "FLV packet info is unknown. ";
						break;
						
					case IVHost.CONTENTTYPE_VIDEO:
						flvDetails += "FLV packet info is video. ";
						break;
						
					default:
						flvDetails += "FLV packet info is type " + String.valueOf(codecConfig.getType()) + ". ";					
				}				
				
				bufferedOut.close();
			}
			responseString = "Single-frame FLV saved successfully in " + flvFilePath + ". " + flvDetails;		
			byte[] outBytes = responseString.getBytes();			
			out.write(outBytes);
		}
		catch (IOException e) {
			WMSLoggerFactory.getLogger(null).error("HTTPInterfaceTest IOException: " + e.toString());
		}
		catch (Exception e) {
			WMSLoggerFactory.getLogger(null).error("HTTPInterfaceTest Exception: " + e.toString());
						
			try {
				responseString = e.toString();
				byte[] outBytes = responseString.getBytes();
				out.write(outBytes);
			} catch (IOException e1) {
				WMSLoggerFactory.getLogger(null).error("HTTPInterfaceTest IOException: " + e1.toString());
			}
		}
	}

Regards,

Anh-dzung

The reason you will be getting this response is because your HttpProvider is not configured properly in the VHost.xml Hostport configuration.

You need to edit the VHost.xml and add your HTTPProvider.

Roger.

Ok, I have registered but I put the * in front, that’s why it didn’t work out. I have to change into

<HTTPProvider> 
    <BaseClass>com.mycompany.wms.module.MyHTTPProvider</BaseClass> 
    <RequestFilters>myhttpprovider*</RequestFilters> 
    <AuthenticationMethod>none</AuthenticationMethod> 
</HTTPProvider>

Now it works like charm. Many thanks for your help Roger!!! It would be great for other guys who want to do the same with H264 to know this if you can update it in the CreateSnapshot as well :wink:

Regards,

Anh-Dzung