Wowza Community

Custom module to create single frame snapshots of live and VOD stream

Hi Charlie,

 AMFPacket codecConfig = stream.getVideoCodecConfigPacket(packet.getAbsTimecode());

how do I extract such a codecConfig Packet from a recorded h264 file?

Regards

Markus

Hi Charlie,

I want to take a snapshot from a FLV-File which was recorded some time ago.

Therefore I don’t have a IMediaStream Object at the

time I want to create the snapshot.

My first step is to “load” the file via:

BufferedInputStream is = new BufferedInputStream(new FileInputStream(snapshot.getFlvFile()));

Then I search the AMFPacket at the given timecode by:

FLVUtils.readHeader(is);
				AMFPacket amfPacket;
				
				while ((amfPacket = FLVUtils.readChunk(is)) != null)
				{
					if (lastVideoKeyFrame != null && amfPacket.getTimecode() > timecodeInMillis)
						break;
					if (amfPacket.getType() != IVHost.CONTENTTYPE_VIDEO)
						continue;
					if (FLVUtils.getFrameType(amfPacket.getFirstByte()) == FLVUtils.FLV_KFRAME)
						lastVideoKeyFrame = amfPacket;
				}
				is.close();

In a last step I write this packet (lastVideoKeyFrame) to a new flv-File.

The code for this step is:

BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(snapshot.getFlvSnapshotFile(), false));
					FLVUtils.writeHeader(out, 0, null);
					//FLVUtils.writeChunk(out, lastVideoKeyFrame.getDataBuffer(), lastVideoKeyFrame.getSize(), 0, (byte)lastVideoKeyFrame.getType());
					
					
					AMFPacket codecConfig = [COLOR="Red"]stream[/COLOR].getVideoCodecConfigPacket(packet.getAbsTimecode());
					if (codecConfig != null){
						FLVUtils.writeChunk(out, codecConfig.getDataBuffer(), codecConfig.getSize(), 0, (byte)codecConfig.getType());
					}
					FLVUtils.writeChunk(out, lastVideoKeyFrame.getDataBuffer(), lastVideoKeyFrame.getSize(), 0, (byte)lastVideoKeyFrame.getType());
					
					
					out.close();

Regards,

Markus

Hi Charlie,

grabbing a image directly from a saved flv-file by ffmpeg

is much slower than creating a mini-flv and convert this mini-flv to an image afterwards.

Regards,

Markus

Yesterday, I tried to grab 3 thumbnails (beginning, middle, end) from a recorded FLV file (H264), using ffmpeg in an external command line. Everything works fine.

Now I’m wondering about ffmepg behaviour when grabbing frames. A few years ago, when I tried this, I notice ffmpeg browse the whole file to seek in the file. When the FLV file is 2 hours long for instance, it could be really slow !

Let’s say FLVUtils offer a native PNG/JPG export function of a given frame, what would be FLVUtils behaviour in this case ?

No matter, your module works great and the feature is really impressive (using ffmpeg to convert to png then delete flv frame) …

Thank you !

As I know, NetConnection object is ActionScript.

You can’t call server-side functions with C#, you need either Flash or Flex piece of code …

how do you use it on AS3… ¿? nc.call() ? any example… please…

I’m curious if there is public documentation of the structure of the keyframe data in an AMFPacket. If there were, I would be happy to take a stab at converting it to a bitmap which would then make it relatively simple to drop to disk as a jpg or png or without requiring an external process to fire off ffmpeg.

Also, it has been more than 6 months since your note about h.264 snapshots. Any change of that status? Again, if I can get the structure of the keyframe buffer documented somewhere, it can’t be too difficult to turn it into an uncompressed bitmap (I hope).

Could one create a subclass of the default MediaWriter class (or an implementation of IMediaWriter that delegates to the default) and execute the snapshot code directly in the writePackets() method? It would sure be nice to have snapshots waiting as soon as the stream completes writing. If so, would it be possible to get some example code? I can probably figure it out from the existing examples, but it is always nicer to have explicit instructions. I can inspect the jar files and see that I likely need to subclass:

com/wowza/wms/mediawriter/flv/MediaWriterFLV.class

com/wowza/wms/mediawriter/h264/MediaWriterH264.class

Perhaps it is possible to write multiple keyframes into a single file and then tell ffmpeg to extract every frame of the snapshot movie? that would be a convenient way to grab a snapshot every 30 seconds or so.

Also, I’m excited to hear that h.264 snapshots are now working. Time to start playing with switching over, I guess.

Hi Charlie,

I implemented the following class based on your sample code for creating snapshots (with necessary modifications to call this from another java class method):


public class WMSSnapshotCreator extends ModuleBase {

Object lock = new Object();

public void createSnapshotLive(IApplicationInstance appInstance, String streamName) {

getLogger().info("********** WMSSnapshotCreator::createSnapshotLive(): Stream Name: " + streamName + " **********");

String fileName = “”;

MediaStreamMap streams = appInstance.getStreams();

IMediaStream stream = streams.getStream(streamName);

if(stream != null) {

AMFPacket packet = stream.getLastKeyFrame();

if (packet != null) {

fileName = streamName + “_” + packet.getAbsTimecode() + “.flv”;

//getLogger().info("********** WMSSnapshotCreator::createSnapshotLive(): fileName: " + fileName + " **********");

File newFile = stream.getStreamFileForWrite();

String filePath = newFile.getPath().substring(0, newFile.getPath().length() - 4) + “_” + packet.getAbsTimecode() + “.flv”;

//getLogger().info("********** WMSSnapshotCreator::createSnapshotLive(): FLV Thumbnail File Path: " + filePath + " **********");

//get the flv thumbnail file name alone from the file path

int fileNameIndex = 0 ;

//get the index of last occurence of ‘’ in the file path

fileNameIndex = filePath.lastIndexOf(’\’) ;

String flvFileName = “” ;

if(fileNameIndex != -1) {

flvFileName = filePath.substring(fileNameIndex + 1) ;

} else {

flvFileName = null ;

}

getLogger().info("********** WMSSnapshotCreator::createSnapshotLive(): FLV Thumbnail File Name: " + flvFileName + " **********");

try {

synchronized(lock) {

BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(new File(filePath), false));

FLVUtils.writeHeader(out, 0, null);

FLVUtils.writeChunk(out, packet.getDataBuffer(), packet.getSize(), 0, (byte)packet.getType());

out.close();

}

getLogger().info("********** WMSSnapshotCreator::createSnapshotLive(): Snapshot Created Successfully **********");

} catch (Exception e) {

getLogger().error("********** WMSSnapshotCreator::createSnapshotLive(): Error Creating Snapshot !!!" + e.toString() + " **********");

}

}

} else {

getLogger().error("********** WMSSnapshotCreator::createSnapshotLive(): Error Creating Snapshot - NULL Stream !!!" + " **********");

}

}

} //End of WMSSnapshotCreator


I am calling createSnapshotLive() using the following code:


WMSSnapshotCreator creator = new WMSSnapshotCreator() ;

creator.createSnapshotLive(appInstance, streamName) ;

with proper values for appInstance and streamName.

Behavior noticed:

An .flv file with a single frame gets created (with a size of about 3 KB) as expected.

NOTE:

My streamtype is ‘rtp-live-record’ and the codecs I am using are H.264 and AAC.

A bigger .flv file gets created with the audio / video data as expected and it can be played out in VLC.

Requirement and Issues:

I need to convert the .flv thumbnail to .jpg thumbnail now. For this, I tried using the FFmpeg binaries as suggested in some Wowza posts. But when I do this with the single frame .flv file, I am getting the following error in command prompt:

Command:

ffmpeg -i mob1@nist.gov_mob2@nist.gov_3739073223_20090304_145240_1236158594066.flv -vframes 1 -an -f rawvideo -s 160x120 -y mob1@nist.gov_mob2@nist.gov_3739073223_20090304_145240_1236158594066.jpg

Logs:

FFmpeg version SVN-r15815, Copyright © 2000-2008 Fabrice Bellard, et al.

configuration: --enable-memalign-hack --enable-postproc --enable-swscale --ena

ble-gpl --enable-libfaac --enable-libfaad --enable-libgsm --enable-libmp3lame –

enable-libvorbis --enable-libtheora --enable-libx264 --enable-libxvid --disable-

ffserver --disable-vhook --enable-avisynth --enable-pthreads

libavutil 49.12. 0 / 49.12. 0

libavcodec 52. 3. 0 / 52. 3. 0

libavformat 52.23. 1 / 52.23. 1

libavdevice 52. 1. 0 / 52. 1. 0

libswscale 0. 6. 1 / 0. 6. 1

libpostproc 51. 2. 0 / 51. 2. 0

built on Nov 13 2008 10:28:29, gcc: 4.2.4 (TDM-1 for MinGW)

[h264 @ 0x28a7f0]no frame!

[flv @ 0x289660]Could not find codec parameters (Video: h264, yuv420p)

[flv @ 0x289660]Could not find codec parameters (Audio: 0x0000, 0 channels, s16)

mob1@nist.gov_mob2@nist.gov_3739073223_20090304_145240_1236158594066.flv: could not find codec parameters

Can you please let me know why I am getting such an error from FFmpeg? Am I doing anything wrong?

Regards,

Senthil

Thanks Charlie.

Do you have any idea when a similar system will be available for H.264 video?

Regards,

Senthil

Thanks Charlie.

Do you have any idea when a similar system will be available for H.264 video?

Regards,

Senthil

Also, please let me know if there is any work around for achieving this now - like extending the code in createSnapshotLive()?

Charlie,

The Java code modifications didn’t helped out… Still I am getting ‘could not find codec parameters’ error.


FFmpeg Command Executed:

**********************

ffmpeg -i mob1@nist.gov_mob2@nist.gov_1365117498_20090306_112948_1236319204062.flv

-an -r 1 -y video%d.jpg

FFmpeg command line Output:

***********************

skipping flv packet: type 18, size 75, flags 0

skipping flv packet: type 9, size 41, flags 23

skipping flv packet: type 9, size 1597, flags 23

mob1@nist.gov_mob2@nist.gov_1365117498_20090306_112948_1236319204062.flv: could not find codec parameters

************************************************************

Regards,

Senthil

Hi Charlie,

Even with your FFmpeg command, its not working for me. So, I doubt the version of FFmpeg I am using. Can you let me know the FFmpeg version you are using?

Regards,

Senthil

Hi Charlie,

Even with your FFmpeg command, its not working for me. So, I doubt the version of FFmpeg I am using. Can you let me know the FFmpeg version you are using?

Regards,

Senthil

Please try with my .flv thumbnail file (mob1@nist.gov_mob2@nist.gov_923701976_20090306_110839_1236317978936.flv) created by Wowza using the WMSSnapShotCreator module. I am sending this as an email to you…

Thanks Charlie,

Yes - I too got that working with the version of FFmpeg you pointed out.

Regards,

Senthil

Hi Charlie,

I am using livestreamrecord application to capture video from a webcam. The video application and audio only applications are working perfectly. However when I try to use “Snapshot” functionality, nothing is being captured. If I understood right, the snapshot functionality generates an flv file with frames captured. However I am not seeing any flv file for the snapshot. I have integrated livestreamrecord with JWPlayer. I am reproducing below the code being used:

The flash side actionscript snippet is as follows

function snapshot()
{
	var resultObj:Object = new Object();
	resultObj.onResult = function(fileName:String)
	{
		trace("result: "+fileName);
	}
	nc.call("createSnapshotLive", resultObj, "test");
	
}
doSnapshot.onPress = snapshot;

The application file contains the following:

<Root>
	<Application>
		<!-- Uncomment to set application level timeout values
		<ApplicationTimeout>60000</ApplicationTimeout>
		<PingTimeout>12000</PingTimeout>
		<ValidationFrequency>8000</ValidationFrequency>
		<MaximumPendingWriteBytes>0</MaximumPendingWriteBytes>
		<MaximumSetBufferTime>60000</MaximumSetBufferTime>
		<MaximumStorageDirDepth>25</MaximumStorageDirDepth>
		-->
		<Connections>
			<AutoAccept>true</AutoAccept>
			<AllowDomains></AllowDomains>
		</Connections>
		<!--
			StorageDir path variables
			
			${com.wowza.wms.AppHome} - Application home directory
			${com.wowza.wms.ConfigHome} - Configuration home directory
			${com.wowza.wms.context.VHost} - Virtual host name
			${com.wowza.wms.context.VHostConfigHome} - Virtual host config directory
			${com.wowza.wms.context.Application} - Application name
			${com.wowza.wms.context.ApplicationInstance} - Application instance name
			
		-->
		<Streams>
			<StreamType>live-record</StreamType>
			<StorageDir>D:/apache-tomcat-6.0.18/webapps/shrisemr/mm/content</StorageDir>
			<Properties>
				<!-- Properties defined here will override any properties defined in conf/Streams.xml for any streams types loaded by this application -->
				<!--
				<Property>
					<Name></Name>
					<Value></Value>
				</Property>
				-->
			</Properties>
		</Streams>
		<SharedObjects>
			<StorageDir></StorageDir>
		</SharedObjects>
		<Client>
			<IdleFrequency>-1</IdleFrequency>
			<Access>
				<StreamReadAccess>*</StreamReadAccess>
				<StreamWriteAccess>*</StreamWriteAccess>
				<StreamAudioSampleAccess></StreamAudioSampleAccess>
				<StreamVideoSampleAccess></StreamVideoSampleAccess>
				<SharedObjectReadAccess>*</SharedObjectReadAccess>
				<SharedObjectWriteAccess>*</SharedObjectWriteAccess>
			</Access>
		</Client>
		<RTP>
			<!-- RTP/Authentication/Methods defined in Authentication.xml. Default setup includes; none, basic, digest -->
			<Authentication>
				<Method>digest</Method>
			</Authentication>
			<!-- RTP/AVSyncMethod. Valid values are: senderreport, systemclock, rtptimecode -->
			<AVSyncMethod>senderreport</AVSyncMethod>
			<MaxRTCPWaitTime>12000</MaxRTCPWaitTime>
			<Properties>
				<!-- Properties defined here will override any properties defined in conf/RTP.xml for any depacketizers loaded by this application -->
				<!--
				<Property>
					<Name></Name>
					<Value></Value>
				</Property>
				-->
			</Properties>
		</RTP>
		<MediaCaster>
			<Properties>
				<!-- Properties defined here will override any properties defined in conf/MediaCasters.xml for any MediaCasters loaded by this applications -->
				<!--
				<Property>
					<Name></Name>
					<Value></Value>
				</Property>
				-->
			</Properties>
		</MediaCaster>
		<MediaReader>
			<Properties>
				<!-- Properties defined here will override any properties defined in conf/MediaReaders.xml for any MediaReaders loaded by this applications -->
				<!--
				<Property>
					<Name></Name>
					<Value></Value>
				</Property>
				-->
			</Properties>
		</MediaReader>
		<!-- 
		<Repeater>
			<OriginURL></OriginURL>
		</Repeater> 
		-->
		<Modules>
			<Module>
				<Name>snapshot</Name>
				<Description>Create Snapshot</Description>
				<Class>com.wowza.wms.plugin.test.module.CreateSnapshot</Class>
			</Module>
			<Module>
				<Name>base</Name>
				<Description>Base</Description>
				<Class>com.wowza.wms.module.ModuleCore</Class>
			</Module>
			<Module>
				<Name>properties</Name>
				<Description>Properties</Description>
				<Class>com.wowza.wms.module.ModuleProperties</Class>
			</Module>
			<Module>
				<Name>logging</Name>
				<Description>Client Logging</Description>
				<Class>com.wowza.wms.module.ModuleClientLogging</Class>
			</Module>
			<Module>
				<Name>livestreamrecord</Name>
				<Description>Live Stream Record</Description>
				<Class>com.wowza.wms.plugin.livestreamrecord.ModuleLiveStreamRecord</Class>
			</Module>
			<Module>
				<Name>flvplayback</Name>
				<Description>FLVPlayback</Description>
				<Class>com.wowza.wms.module.ModuleFLVPlayback</Class>
			</Module> 
		</Modules>
		<Properties>
			<!-- Properties defined here will be added to the IApplication.getProperties() and IApplicationInstance.getProperties() collections -->
			<!--
			<Property>
				<Name></Name>
				<Value></Value>
			</Property>
			-->
		</Properties>
	</Application>
</Root>

Thanks in advance for your help,

Regards,

Kishore

I try to build this custom snapshot module, but get following errors…

•The method getStreamFileForWrite(String, null, null) is undefined for the type IMediaStream

•The method getVideoCodecConfigPacket() in the type IMediaStream is not applicable for the arguments (long)

•The method isVideoKeyFrame(AMFPacket) is undefined for the type FLVUtils

Am I missing something or what?

What version of Wowza Media Server are you building the code against? It needs to be the latest 1.7.2.

Charlie

I updated 1.6.0 -> 1.7.2 and now it’s working fine. Thanks!

We have installed this custom module, and it works perfect to create snapshots for streams that are being broadcasted through flash! However we’re stuck on how to create snapshots for broadcasts coming from for example Adobe Flash Media Encoder. Would there be a way to trigger the createsnapshot event on the serverside for an active broadcast by any chance? Any input is highly appreciated.