Page 1 of 7 123 ... LastLast
Results 1 to 10 of 67

Thread: file name control

  1. #1
    Join Date
    Oct 2010
    Posts
    10

    Default file name control

    Hey, I'm super new to Wowza and the whole video streaming scene..

    My situation:
    - Linux box with wowza installed and working perfectly
    - 2 Axis IP cameras
    - RTSP Stream being captured by wowza (rtp-record)
    - Stream auto started via startupstreams xml

    My need:
    - To record multiple cameras for an unlimited duration, with the files being split into manageable (for searching purposes) chunks.

    Im not sure how to accomplish this.. Id like to have it store in a format like:
    Camera1-YYY-MM-DD-HH.flv and automatically start a new file for each hour

    I know VERY LITTLE JAVA so I get super lost looking at these code examples... I know my way around PHP/MySQL/Compiling stuff on linux etc... I can probably get eclipse to fire up and compile code if need be...

    Is this possible? Can the recording module be setup to do this, can an event be triggered to stop/start the stream with a new stream name (reading the correct url from a file) every hour (crontab + shell script?)?
    Last edited by emac08; 10-13-2010 at 04:38 PM.

  2. #2

    Default

    You can do it but you are going to have to take the plunge and compile a module.
    http://www.wowzamedia.com/downloads/...UsersGuide.pdf

    Here a module I use for this purpose. It uses the name of the *.stream file as the recorded stream name.

    It also requires the LiveStreamRecord addon package.
    http://www.wowzamedia.com/forums/con...ream-on-demand

    There was also an issue with Wowza trying to call the record command twice so you need to make sure that you are using the latest patch version of wowza.
    http://www.wowzamedia.com/downloads/...1.2-patch5.zip

    Carefully ready the IMPORTANT!!! notice in the README.txt file of the patch. Several core components have been upgraded:

    ************************************************** *******************
    IMPORTANT!!!

    Several core components have been updraded to newer versions. Before
    applying this patch, delete the following files from your Wowza Media Server
    installation:

    [install-dir]/lib/bcprov-ext-jdk15-143.jar
    [install-dir]/lib/commons-lang-2.4.jar
    [install-dir]/lib/log4j-1.2.15.jar

    You can set a startHour, endHour and timezone as properties in your Application.xml. Add them to the last properties section in the file.

    It will record the files in a separate folder each day 00 - 31 in 1 hour long files. At the end of the month it will start overwriting the old files.

    Code:
    package streamrecord.hourly.monthlyrollover;
    
    import java.io.File;
    import java.util.Calendar;
    import java.util.Collections;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    import java.util.TimeZone;
    
    import com.wowza.wms.application.*;
    import com.wowza.wms.module.*;
    import com.wowza.wms.plugin.integration.liverecord.*;
    import com.wowza.wms.stream.IMediaStream;
    import com.wowza.wms.stream.IMediaStreamActionNotify;
    import com.wowza.wms.stream.IMediaStreamNotify;
    
    public class ModuleStreamRecord extends ModuleBase implements IMediaStreamNotify {
    	private IApplicationInstance appInstance;
    	private String timezone;
    	private StreamTimer streamTimer;
    	private int date;
    	private int startHour = 0;
    	private int endHour = 23;
    	private int hourOfDay = -1;
    	private boolean record = false;
    	private PublishNotifier publishNotifier;
    	private List<String> streams;
    	
    	public static final int FORMAT_UNKNOWN = 0;
    	public static final int FORMAT_FLV = 1;
    	public static final int FORMAT_MP4 = 2;
    	
    	private Map<String, ILiveStreamRecord> recorders = null;
    
    	private class StreamTimer extends Thread {
    		private boolean doQuit = false;
    
    		public synchronized void quit() {
    			doQuit = true;
    		}
    
    		public void run() {
    			while (true) {
    				try {
    					TimeZone tz = TimeZone.getTimeZone(timezone);
    					Calendar cal = Calendar.getInstance(tz);
    
    					date = cal.get(Calendar.DATE);
    					int prevHour = hourOfDay;
    					hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
    					int start = startHour;
    					int end = endHour;
    					if (start > end && hourOfDay > start) {
    						end += 24;
    					}
    
    					if (end < start && hourOfDay < end) {
    						start -=24;
    					}
    
    					if (hourOfDay >= start && hourOfDay < end) {
    						record = true;
    					} else {
    						record = false;
    					}
    
    					streams = appInstance.getMediaCasterStreams().getMediaCasterNames();
    
    					if (prevHour != hourOfDay && record) {
    						for (String streamName : streams) {
    							IMediaStream stream = appInstance.getStreams().getStream(streamName);
    							appInstance.getVHost().getHandlerThreadPool().execute(new DoStartRecording(stream, streamName));
    						}
    					} else if(!record) {
    						for (String streamName : streams) {
    							ILiveStreamRecord recorder = recorders.remove(streamName);
    							if(recorder != null)
    								recorder.stopRecording();
    						}
    					}
    					
    					Thread.currentThread();
    					Thread.sleep(60000);
    					synchronized (this) {
    						if (doQuit) {
    							break;
    						}
    					}
    				} catch (InterruptedException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    
    	public void onAppStart(IApplicationInstance appInstance) {
    		this.appInstance = appInstance;
    		WMSProperties props = appInstance.getProperties();
    		startHour = props.getPropertyInt("startHour", 0);
    		endHour = props.getPropertyInt("endHour", 24);
    		timezone = props.getPropertyStr("timezone", "America/Chicago");
    
    		appInstance.addMediaStreamListener(this);
    		recorders = Collections.synchronizedMap(new HashMap<String, ILiveStreamRecord>());
    		publishNotifier = new PublishNotifier();
    		
    		streamTimer = new StreamTimer();
    		streamTimer.setName("RecordController-" + appInstance.getApplication().getName());
    		streamTimer.setDaemon(true);
    		streamTimer.start();
    	}
    
    	public void onAppStop(IApplicationInstance appInstance) {
    		streamTimer.quit();
    
    		// cleanup any recorders that are still running
    		synchronized (recorders) {
    			Iterator<String> iter = recorders.keySet().iterator();
    			while(iter.hasNext())
    			{
    				String streamName = iter.next();
    				ILiveStreamRecord recorder = recorders.get(streamName);
    				recorder.stopRecording();
    				getLogger().info("  stopRecording: "+streamName);
    			}
    			recorders.clear();
    		}
    		
    		recorders = null;
    		appInstance.removeMediaStreamListener(this);
    		publishNotifier = null;
    	}
    
    	private synchronized void startRecording(IMediaStream stream) {
    		String streamAlias = stream.getName().substring(0, stream.getName().indexOf(".stream"));
    		
    		if (!streamAlias.isEmpty()) {
    			String outputPath = appInstance.getStreamStorageDir()+"/"+String.format("%02d", date); //day;
    			boolean append = false;
    			File path = new File(outputPath);
    			if (path.exists()) {
    				File outputFile = new File(path.getPath()+File.separator+streamAlias+"_"+String.format("%02d", hourOfDay)+".mp4");
    				Date today = new Date();
    				if (!(outputFile.lastModified() < today.getTime() - 86400000)) {
    					append = true;
    				}
    				
    			} else {
    				path.mkdirs();
    			}
    			
    			recordStream(stream, FORMAT_MP4, append, outputPath+File.separator+streamAlias+"_"+String.format("%02d", hourOfDay)+".mp4", false, true, true);
    		}
    	}
    	
    	private void recordStream(IMediaStream stream, int format, boolean append, String outputPath, boolean versionFile, boolean startOnKeyFrame, boolean recordData)
    	{
    
    		String streamName = stream.getName();
    		
    		// if a format was not specified then check the stream prefix and choose accordingly
    		if (format == FORMAT_UNKNOWN)
    		{
    			format = FORMAT_FLV;
    			String extStr = stream.getExt();
    			if (extStr.equals("mp4"))
    				format = FORMAT_MP4;
    		}
    		
    		String params = "stream:"+streamName;
    		params += " format:"+(format==FORMAT_MP4?"mp4":"flv");
    		params += " append:"+append;
    		if (outputPath != null)
    			params += " outputPath:"+outputPath;
    		else
    		{
    			File writeFile = stream.getStreamFileForWrite();
    			params += " outputPath:"+writeFile.getAbsolutePath();
    		}
    		params += " versionFile:"+versionFile;
    		params += " startOnKeyFrame:"+startOnKeyFrame;
    		params += " recordData:"+recordData;
    		
    		getLogger().info("ModuleStreamRecord.startRecording: "+params);
    		
    		// create a stream recorder and save it in a map of recorders
    		ILiveStreamRecord recorder = null;
    		
    		// create the correct recorder based on format
    		if (format == FORMAT_MP4)
    			recorder = new LiveStreamRecorderMP4();
    		else
    			recorder = new LiveStreamRecorderFLV();
    		
    		// add it to the recorders list
    		ILiveStreamRecord prevRecorder = recorders.get(streamName);
    		if (prevRecorder != null)
    			prevRecorder.stopRecording();
    		recorders.put(streamName, recorder);
    		
    		// if you want to record data packets as well as video/audio
    		recorder.setRecordData(recordData);
    		
    		// Set to true if you want to version the previous file rather than overwrite it
    		recorder.setVersionFile(versionFile);
    		
    		// If recording only audio set this to false so the recording starts immediately
    		recorder.setStartOnKeyFrame(startOnKeyFrame);
    		
    		// start recording
    		recorder.startRecording(stream, outputPath, append);
    	}
    
    		@Override
    		public void onMediaStreamCreate(IMediaStream stream) {
    			stream.addClientListener(publishNotifier);
    		}
    
    		@Override
    		public void onMediaStreamDestroy(IMediaStream stream) {
    			stream.removeClientListener(publishNotifier);
    			if(!stream.isPlay()) {
    				ILiveStreamRecord recorder = recorders.remove(stream.getName());
    				if (recorder != null)
    					recorder.stopRecording();
    			}
    		}
    		
    	private class PublishNotifier implements IMediaStreamActionNotify {
    
    		@Override
    		public void onPause(IMediaStream stream, boolean isPause,
    				double location) {
    			// TODO Auto-generated method stub
    			
    		}
    
    		@Override
    		public void onPlay(IMediaStream stream, String streamName,
    				double playStart, double playLen, int playReset) {
    			// TODO Auto-generated method stub
    			
    		}
    
    		@Override
    		public void onPublish(IMediaStream stream, String streamName,
    				boolean isRecord, boolean isAppend) {
    			if(record)
    				appInstance.getVHost().getHandlerThreadPool().execute(new DoStartRecording(stream, streamName));
    		}
    
    		@Override
    		public void onSeek(IMediaStream stream, double location) {
    			// TODO Auto-generated method stub
    			
    		}
    
    		@Override
    		public void onStop(IMediaStream stream) {
    			// TODO Auto-generated method stub
    			
    		}
    
    		@Override
    		public void onUnPublish(IMediaStream stream, String streamName,
    				boolean isRecord, boolean isAppend) {
    			ILiveStreamRecord recorder = recorders.remove(streamName);
    
    			if (recorder != null)
    			{
    				// stop recording
    				recorder.stopRecording();
    			}
    		}
    	}
    	
    	private class DoStartRecording implements Runnable {
    		private IMediaStream stream;
    		private String streamName;
    		private DoStartRecording(IMediaStream stream, String streamName) {
    			this.stream = stream;
    			this.streamName = streamName;
    		}
    
    		public void run() {
    			List<String> mediaCasters = appInstance.getMediaCasterStreams().getMediaCasterNames();
    			if (mediaCasters.contains(streamName)) {
    				int lockCount = appInstance.getMediaCasterStreams().getMediaCaster(streamName).getLockCount();
    				if (lockCount > 0)
    					startRecording(stream);
    			}
    		}
    	}
    }
    Look at the tutorials for info on how to compile and deploy a module.
    Last edited by rogerlittin; 12-30-2010 at 08:41 PM. Reason: Fixed missing first hour.

  3. #3
    Join Date
    Oct 2010
    Posts
    10

    Default

    OK... So first off, OMG THANK YOU, Im relieved to see this is possible...

    Im not sure its working tho :/

    Heres what I did:
    - Applied the patch, deleted necessary files first, copied new files, restart wowza
    - Copied the /lib folder of the livestreamrecord zip you linked me into the installation lib folder, added module tag to my rtplive Application.xml file. according to the readme file in the zip.
    - Installed Wowza IDE2 on my windows machine (JDK+WowzaIDE2+Wowza2), created a new project put that package name you had in your example and class name you had in your example...
    - Pasted your example code into WowzaIDE2 and it said it built a JAR successfully on my Windows machine...
    - I copied that jar to my linux box into the /lib folder
    - I inserted, into my rtplive Application.xml file, a new Module tag like so:
    Code:
     <Module> 
      <Name>ModuleStreamRecord</Name> 
      <Description>File Management</Description> 
      <Class>ModuleStreamRecord</Class> 
     </Module>
    - Restarted Wowza

    Wowza gives me this error:

    Code:
    ERROR server comment - loadModFunctions: java.lang.ClassNotFoundException: ModuleStreamRecord
    java.lang.ClassNotFoundException: ModuleStreamRecord
            at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:323)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:268)
            at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:336)
            at java.lang.Class.forName0(Native Method)
            at java.lang.Class.forName(Class.java:186)
            at com.wowza.wms.util.ModuleUtils.addModuleToApp(Unknown Source)
            at com.wowza.wms.util.ApplicationUtils.loadModules(Unknown Source)
            at com.wowza.wms.util.ApplicationUtils.loadConfigFile(Unknown Source)
            at com.wowza.wms.application.ApplicationInstance.loadConfig(Unknown Source)
            at com.wowza.wms.application.ApplicationInstance.<init>(Unknown Source)
            at com.wowza.wms.application.Application.getAppInstance(Unknown Source)
            at com.wowza.wms.vhost.VHost.startStartupStreams(Unknown Source)
            at com.wowza.wms.server.Server.startVHost(Unknown Source)
            at com.wowza.wms.server.Server.startVHosts(Unknown Source)
            at com.wowza.wms.server.Server.start(Unknown Source)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
            at java.lang.reflect.Method.invoke(Method.java:616)
            at com.wowza.wms.bootstrap.Bootstrap.startServer(Unknown Source)
            at com.wowza.wms.bootstrap.Bootstrap.main(Unknown Source)

    Im lost

  4. #4

    Default

    <Class>ModuleStreamRecord</Class> This needs to be the full class name ie. <Class>streamrecord.hourly.monthlyrollover.ModuleS treamRecord</Class>

  5. #5
    Join Date
    Oct 2010
    Posts
    10

    Default

    I tried that still doesnt work...

    Am I missing a step or putting the jar in the wrong spot?
    Do I need to tell it to load the jar or something in other than the Application.xml?

    Error:
    Code:
    ERROR server comment - loadModFunctions: java.lang.ClassNotFoundException: streamrecord.hourly.monthlyrollover.ModuleStreamRecord
    java.lang.ClassNotFoundException: streamrecord.hourly.monthlyrollover.ModuleStreamRecord
            at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:323)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:268)
            at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:336)
            at java.lang.Class.forName0(Native Method)
            at java.lang.Class.forName(Class.java:186)
            at com.wowza.wms.util.ModuleUtils.addModuleToApp(Unknown Source)
            at com.wowza.wms.util.ApplicationUtils.loadModules(Unknown Source)
            at com.wowza.wms.util.ApplicationUtils.loadConfigFile(Unknown Source)
            at com.wowza.wms.application.ApplicationInstance.loadConfig(Unknown Source)
            at com.wowza.wms.application.ApplicationInstance.<init>(Unknown Source)
            at com.wowza.wms.application.Application.getAppInstance(Unknown Source)
            at com.wowza.wms.vhost.VHost.startStartupStreams(Unknown Source)
            at com.wowza.wms.server.Server.startVHost(Unknown Source)
            at com.wowza.wms.server.Server.startVHosts(Unknown Source)
            at com.wowza.wms.server.Server.start(Unknown Source)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
            at java.lang.reflect.Method.invoke(Method.java:616)
            at com.wowza.wms.bootstrap.Bootstrap.startServer(Unknown Source)
            at com.wowza.wms.bootstrap.Bootstrap.main(Unknown Source)
    INFO server comment - ModuleStreamRecord.onAppStart
    Application.xml:
    Code:
     <Modules>
                            <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>flvplayback</Name>
                                    <Description>FLVPlayback</Description>
                                    <Class>com.wowza.wms.module.ModuleFLVPlayback</Class>
                            </Module>
    
    
    <Module>
            <Name>ModuleLiveStreamRecord</Name>
            <Description>ModuleLiveStreamRecord</Description>
            <Class>com.wowza.wms.plugin.livestreamrecord.ModuleLiveStreamRecord</Class>
    </Module>
     <Module>
      <Name>ModuleStreamRecord</Name>
      <Description>File Management</Description>
      <Class>streamrecord.hourly.monthlyrollover.ModuleStreamRecord</Class>
     </Module>

    Code in IDE:
    Code:
    package streamrecord.hourly.monthlyrollover;
    
    import java.io.File;
    import java.util.Calendar;
    import java.util.Collections;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    import java.util.TimeZone;
    
    import com.wowza.wms.application.*;
    import com.wowza.wms.module.*;
    import com.wowza.wms.plugin.integration.liverecord.*;
    import com.wowza.wms.stream.IMediaStream;
    import com.wowza.wms.stream.IMediaStreamActionNotify;
    import com.wowza.wms.stream.IMediaStreamNotify;
     
    
    public class ModuleStreamRecord extends ModuleBase implements IMediaStreamNotify {
    	private IApplicationInstance appInstance;
    	private String timezone;
    	private StreamTimer streamTimer;
    	private int date;
    	private int startHour = 0;
    	private int endHour = 23;
    	private int hourOfDay = -1;
    	private PublishNotifier publishNotifier;
    	private List<String> streams;
    	
    	public static final int FORMAT_UNKNOWN = 0;
    	public static final int FORMAT_FLV = 1;
    	public static final int FORMAT_MP4 = 2;
    	
    	private Map<String, ILiveStreamRecord> recorders = null;
    
    	private class StreamTimer extends Thread {
    		private boolean doQuit = false;
    
    		public synchronized void quit() {
    			doQuit = true;
    		}
    
    		public void run() {
    			while (true) {
    				try { 
    					TimeZone tz = TimeZone.getTimeZone(timezone);
    					Calendar cal = Calendar.getInstance(tz);
    
    					date = cal.get(Calendar.DATE);
    					int prevHour = hourOfDay;
    					hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
    					int start = startHour;
    					int end = endHour;
    					if (start > end && hourOfDay > start) {
    						end += 24;
    					}
    
    					if (end < start && hourOfDay < end) {
    						start -=24;
    					}
    					boolean record = false;
    					if (hourOfDay >= start && hourOfDay < end) {
    						record = true;
    					}
    					streams = appInstance.getMediaCasterStreams().getMediaCasterNames();
    
    					if (prevHour != hourOfDay && record) {
    						for (String streamName : streams) {
    							IMediaStream stream = appInstance.getStreams().getStream(streamName);
    							appInstance.getVHost().getHandlerThreadPool().execute(new DoStartRecording(stream, streamName));
    						}
    					} else if(!record) {
    						for (String streamName : streams) {
    							ILiveStreamRecord recorder = recorders.remove(streamName);
    							if(recorder != null)
    								recorder.stopRecording();
    						}
    					}
    					
    					Thread.currentThread();
    					Thread.sleep(60000);
    					synchronized (this) {
    						if (doQuit) {
    							break;
    						}
    					}
    				} catch (InterruptedException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    
    	public void onAppStart(IApplicationInstance appInstance) {
    		TimeZone tz = TimeZone.getTimeZone(timezone);
    		Calendar cal = Calendar.getInstance(tz);
    		hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
    
    		this.appInstance = appInstance;
    		WMSProperties props = appInstance.getProperties();
    		startHour = props.getPropertyInt("startHour", 0);
    		endHour = props.getPropertyInt("endHour", 24);
    		timezone = props.getPropertyStr("timezone", "America/Chicago");
    		appInstance.addMediaStreamListener(this);
    		recorders = Collections.synchronizedMap(new HashMap<String, ILiveStreamRecord>());
    		publishNotifier = new PublishNotifier();
    		
    		streamTimer = new StreamTimer();
    		streamTimer.setName("RecordController-" + appInstance.getApplication().getName());
    		streamTimer.setDaemon(true);
    		streamTimer.start();
    	}
    
    	public void onAppStop(IApplicationInstance appInstance) {
    		streamTimer.quit();
    
    		// cleanup any recorders that are still running
    		synchronized (recorders) {
    			Iterator<String> iter = recorders.keySet().iterator();
    			while(iter.hasNext())
    			{
    				String streamName = iter.next();
    				ILiveStreamRecord recorder = recorders.get(streamName);
    				recorder.stopRecording();
    				getLogger().info("  stopRecording: "+streamName);
    			}
    			recorders.clear();
    		}
    		
    		recorders = null;
    		appInstance.removeMediaStreamListener(this);
    		publishNotifier = null;
    	}
    
    	private synchronized void startRecording(IMediaStream stream) {
    		String streamAlias = stream.getName().substring(0, stream.getName().indexOf(".stream"));
    		
    		if (!streamAlias.isEmpty()) {
    			String outputPath = appInstance.getStreamStorageDir()+"/"+String.format("%02d", date); //day;
    			boolean append = false;
    			File path = new File(outputPath);
    			if (path.exists()) {
    				File outputFile = new File(path.getPath()+File.separator+streamAlias+"_"+String.format("%02d", hourOfDay)+".mp4");
    				Date today = new Date();
    				if (!(outputFile.lastModified() < today.getTime() - 86400000)) {
    					append = true;
    				}
    				
    			} else {
    				path.mkdirs();
    			}
    			
    			recordStream(stream, FORMAT_MP4, append, outputPath+File.separator+streamAlias+"_"+String.format("%02d", hourOfDay)+".mp4", false, true, true);
    		}
    	}
    	
    	private void recordStream(IMediaStream stream, int format, boolean append, String outputPath, boolean versionFile, boolean startOnKeyFrame, boolean recordData)
    	{
    
    		String streamName = stream.getName();
    		
    		// if a format was not specified then check the stream prefix and choose accordingly
    		if (format == FORMAT_UNKNOWN)
    		{
    			format = FORMAT_FLV;
    			String extStr = stream.getExt();
    			if (extStr.equals("mp4"))
    				format = FORMAT_MP4;
    		}
    		
    		String params = "stream:"+streamName;
    		params += " format:"+(format==FORMAT_MP4?"mp4":"flv");
    		params += " append:"+append;
    		if (outputPath != null)
    			params += " outputPath:"+outputPath;
    		else
    		{
    			File writeFile = stream.getStreamFileForWrite();
    			params += " outputPath:"+writeFile.getAbsolutePath();
    		}
    		params += " versionFile:"+versionFile;
    		params += " startOnKeyFrame:"+startOnKeyFrame;
    		params += " recordData:"+recordData;
    		
    		getLogger().info("ModuleStreamRecord.startRecording: "+params);
    		
    		// create a stream recorder and save it in a map of recorders
    		ILiveStreamRecord recorder = null;
    		
    		// create the correct recorder based on format
    		if (format == FORMAT_MP4)
    			recorder = new LiveStreamRecorderMP4();
    		else
    			recorder = new LiveStreamRecorderFLV();
    		
    		// add it to the recorders list
    		ILiveStreamRecord prevRecorder = recorders.get(streamName);
    		if (prevRecorder != null)
    			prevRecorder.stopRecording();
    		recorders.put(streamName, recorder);
    		
    		// if you want to record data packets as well as video/audio
    		recorder.setRecordData(recordData);
    		
    		// Set to true if you want to version the previous file rather than overwrite it
    		recorder.setVersionFile(versionFile);
    		
    		// If recording only audio set this to false so the recording starts immediately
    		recorder.setStartOnKeyFrame(startOnKeyFrame);
    		
    		// start recording
    		recorder.startRecording(stream, outputPath, append);
    	}
    
    		@Override
    		public void onMediaStreamCreate(IMediaStream stream) {
    			stream.addClientListener(publishNotifier);
    		}
    
    		@Override
    		public void onMediaStreamDestroy(IMediaStream stream) {
    			stream.removeClientListener(publishNotifier);
    			if(!stream.isPlay()) {
    				ILiveStreamRecord recorder = recorders.remove(stream.getName());
    				if (recorder != null)
    					recorder.stopRecording();
    			}
    		}
    		
    	private class PublishNotifier implements IMediaStreamActionNotify {
    
    		@Override
    		public void onPause(IMediaStream stream, boolean isPause,
    				double location) {
    			// TODO Auto-generated method stub
    			
    		}
    
    		@Override
    		public void onPlay(IMediaStream stream, String streamName,
    				double playStart, double playLen, int playReset) {
    			// TODO Auto-generated method stub
    			
    		}
    
    		@Override
    		public void onPublish(IMediaStream stream, String streamName,
    				boolean isRecord, boolean isAppend) {
    //			appInstance.getVHost().getHandlerThreadPool().execute(new DoStartRecording(stream, streamName));
    		}
    
    		@Override
    		public void onSeek(IMediaStream stream, double location) {
    			// TODO Auto-generated method stub
    			
    		}
    
    		@Override
    		public void onStop(IMediaStream stream) {
    			// TODO Auto-generated method stub
    			
    		}
    
    		@Override
    		public void onUnPublish(IMediaStream stream, String streamName,
    				boolean isRecord, boolean isAppend) {
    			ILiveStreamRecord recorder = recorders.remove(streamName);
    
    			if (recorder != null)
    			{
    				// stop recording
    				recorder.stopRecording();
    			}
    		}
    	}
    	
    	private class DoStartRecording implements Runnable {
    		private IMediaStream stream;
    		private String streamName;
    		private DoStartRecording(IMediaStream stream, String streamName) {
    			this.stream = stream;
    			this.streamName = streamName;
    		}
    
    		public void run() {
    			List<String> mediaCasters = appInstance.getMediaCasterStreams().getMediaCasterNames();
    			if (mediaCasters.contains(streamName)) {
    				int lockCount = appInstance.getMediaCasterStreams().getMediaCaster(streamName).getLockCount();
    				if (lockCount > 0)
    					startRecording(stream);
    			}
    		}
    	}
    }

    Structure on left side of IDE:
    Code:
    ModuleStreamRecord
    -> src
    - -> streamrecord.hourly.monthlyrollover
    - - -> ModuleStreamRecord


    IDE Output:
    Code:
    Buildfile: C:\Documents and Settings\Administrator\Documents\workspace\ModuleStreamRecord\build.xml
    
    jar:
             [jar] Building jar: C:\Program Files\Wowza Media Systems\Wowza Media Server 2.1.2\lib\ModuleStreamRecord.jar
    BUILD SUCCESSFUL

    I copied that .jar to the /usr/local/WowzaMediaServer/lib folder on my linux box (the one im running, and is giving this error)

    What am I missing??

  6. #6
    Join Date
    Oct 2010
    Posts
    10

    Default

    I seemed to get it LOADING when i recompiled under a diff name BUT
    it gives me an error about timezone library:

    Code:
    ERROR server comment - invoke(onAppStart): java.lang.NullPointerException: java.util.TimeZone.parseCustomTimeZone(TimeZone.java:712)
    java.lang.NullPointerException
            at java.util.TimeZone.parseCustomTimeZone(TimeZone.java:712)
            at java.util.TimeZone.getTimeZone(TimeZone.java:488)
            at java.util.TimeZone.getTimeZone(TimeZone.java:482)
            at com.wowza.wms.plugin.streamrecord.StreamRecordModule.onAppStart(StreamRecordModule.java:98)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
            at java.lang.reflect.Method.invoke(Method.java:616)
            at com.wowza.wms.module.ModuleFunction.invoke(Unknown Source)
            at com.wowza.wms.module.ModuleFunctions.invokeSpecial(Unknown Source)
            at com.wowza.wms.module.ModuleFunctions.onAppStart(Unknown Source)
            at com.wowza.wms.application.Application.getAppInstance(Unknown Source)
            at com.wowza.wms.vhost.VHost.startStartupStreams(Unknown Source)
            at com.wowza.wms.server.Server.startVHost(Unknown Source)
            at com.wowza.wms.server.Server.startVHosts(Unknown Source)
            at com.wowza.wms.server.Server.start(Unknown Source)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
            at java.lang.reflect.Method.invoke(Method.java:616)
            at com.wowza.wms.bootstrap.Bootstrap.startServer(Unknown Source)
            at com.wowza.wms.bootstrap.Bootstrap.main(Unknown Source)
    My application.xml
    Code:
     <Module>
    	<Name>StreamRecordModule</Name>
    	<Description>StreamRecordModule</Description>
    	<Class>com.wowza.wms.plugin.streamrecord.StreamRecordModule</Class>
    </Module>
    
    		</Modules>
    		<!-- Properties defined here will be added to the IApplication.getProperties() and IApplicationInstance.getProperties() collections -->
    		<Properties>
    		<Property>
    			<Name>starHour</Name>
    			<Value>00</Value>
    			<Type>Integer</Type>
    		</Property>
    		<Property>
    			<Name>endHour</Name>
    			<Value>24</Value>
    			<Type>Integer</Type>
    		</Property>
    		<Property>
    			<Name>timezone</Name>
    			<Value>GMT-08:00</Value>
    			<Type>String</Type>
    		</Property>
    
    
    		</Properties>
    Last edited by emac08; 10-14-2010 at 12:30 PM.

  7. #7
    Join Date
    Oct 2010
    Posts
    10

    Default

    As an experiment I tore out all the timezone related calls, and made it just pull from a new Date() object... (I dont care what timezone the files are named for)...

    The new JAR executes without error when WMS is started ... but the file is still recordered to camera.stream.flv, even when stopping/starting the server it stays as camera.stream.flv

    Code:
    package com.wowza.wms.plugin.streamrecord;
    
    import java.io.File;
    import java.util.Calendar;
    import java.util.Collections;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    
    import com.wowza.wms.application.*;
    import com.wowza.wms.module.*;
    import com.wowza.wms.plugin.integration.liverecord.*;
    import com.wowza.wms.stream.IMediaStream;
    import com.wowza.wms.stream.IMediaStreamActionNotify;
    import com.wowza.wms.stream.IMediaStreamNotify;
    import java.text.SimpleDateFormat;
    
    
    public class StreamRecordModule extends ModuleBase implements IMediaStreamNotify {
    	private IApplicationInstance appInstance;
    	private StreamTimer streamTimer;
    	private int date;
    	private int startHour = 0;
    	private int endHour = 23;
    	private int hourOfDay = -1;
    	private PublishNotifier publishNotifier;
    	private List<String> streams;
    	
    	public static final int FORMAT_UNKNOWN = 0;
    	public static final int FORMAT_FLV = 1;
    	public static final int FORMAT_MP4 = 2;
    	
    	private Map<String, ILiveStreamRecord> recorders = null;
    
    	private class StreamTimer extends Thread {
    		private boolean doQuit = false;
    
    		public synchronized void quit() {
    			doQuit = true;
    		}
    
    		@SuppressWarnings("deprecation")
    		public void run() {
    			while (true) {
    				try { 
    
    					Date mydate = new Date();
    					int prevHour = hourOfDay;
    					hourOfDay = mydate.getHours();
    					int start = startHour;
    					int end = endHour;
    					if (start > end && hourOfDay > start) {
    						end += 24;
    					}
    
    					if (end < start && hourOfDay < end) {
    						start -=24;
    					}
    					boolean record = false;
    					if (hourOfDay >= start && hourOfDay < end) {
    						record = true;
    					}
    					streams = appInstance.getMediaCasterStreams().getMediaCasterNames();
    
    					if (prevHour != hourOfDay && record) {
    						for (String streamName : streams) {
    							IMediaStream stream = appInstance.getStreams().getStream(streamName);
    							appInstance.getVHost().getHandlerThreadPool().execute(new DoStartRecording(stream, streamName));
    						}
    					} else if(!record) {
    						for (String streamName : streams) {
    							ILiveStreamRecord recorder = recorders.remove(streamName);
    							if(recorder != null)
    								recorder.stopRecording();
    						}
    					}
    					
    					Thread.currentThread();
    					Thread.sleep(60000);
    					synchronized (this) {
    						if (doQuit) {
    							break;
    						}
    					}
    				} catch (InterruptedException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    
    	@SuppressWarnings("deprecation")
    	public void onAppStart(IApplicationInstance appInstance) {
    		Date mydate = new Date();
    		hourOfDay = mydate.getHours();
    		this.appInstance = appInstance;
    		WMSProperties props = appInstance.getProperties();
    		startHour = props.getPropertyInt("startHour", 0);
    		endHour = props.getPropertyInt("endHour", 24);
    		
    		appInstance.addMediaStreamListener(this);
    		recorders = Collections.synchronizedMap(new HashMap<String, ILiveStreamRecord>());
    		publishNotifier = new PublishNotifier();
    		
    		streamTimer = new StreamTimer();
    		streamTimer.setName("RecordController-" + appInstance.getApplication().getName());
    		streamTimer.setDaemon(true);
    		streamTimer.start();
    	}
    
    	public void onAppStop(IApplicationInstance appInstance) {
    		streamTimer.quit();
    
    		// cleanup any recorders that are still running
    		synchronized (recorders) {
    			Iterator<String> iter = recorders.keySet().iterator();
    			while(iter.hasNext())
    			{
    				String streamName = iter.next();
    				ILiveStreamRecord recorder = recorders.get(streamName);
    				recorder.stopRecording();
    				getLogger().info("  stopRecording: "+streamName);
    			}
    			recorders.clear();
    		}
    		
    		recorders = null;
    		appInstance.removeMediaStreamListener(this);
    		publishNotifier = null;
    	}
    
    	private synchronized void startRecording(IMediaStream stream) {
    		String streamAlias = stream.getName().substring(0, stream.getName().indexOf(".stream"));
    		
    		if (!streamAlias.isEmpty()) {
    			SimpleDateFormat dateformatYYYYMMDD = new SimpleDateFormat("yyyyMMdd");
    			 Date dateNow = new Date ();
    			 StringBuilder nowYYYYMMDD = new StringBuilder( dateformatYYYYMMDD.format( dateNow ) );
    
    			String outputPath = appInstance.getStreamStorageDir()+"/"+nowYYYYMMDD; //day;
    			boolean append = false;
    			File path = new File(outputPath);
    			if (path.exists()) {
    				File outputFile = new File(path.getPath()+File.separator+streamAlias+"_"+String.format("%02d", hourOfDay)+".mp4");
    				Date today = new Date();
    				if (!(outputFile.lastModified() < today.getTime() - 86400000)) {
    					append = true;
    				}
    				
    			} else {
    				path.mkdirs();
    			}
    			
    			recordStream(stream, FORMAT_MP4, append, outputPath+File.separator+streamAlias+"_"+String.format("%02d", hourOfDay)+".mp4", false, true, true);
    		}
    	}
    	
    	private void recordStream(IMediaStream stream, int format, boolean append, String outputPath, boolean versionFile, boolean startOnKeyFrame, boolean recordData)
    	{
    
    		String streamName = stream.getName();
    		
    		// if a format was not specified then check the stream prefix and choose accordingly
    		if (format == FORMAT_UNKNOWN)
    		{
    			format = FORMAT_FLV;
    			String extStr = stream.getExt();
    			if (extStr.equals("mp4"))
    				format = FORMAT_MP4;
    		}
    		
    		String params = "stream:"+streamName;
    		params += " format:"+(format==FORMAT_MP4?"mp4":"flv");
    		params += " append:"+append;
    		if (outputPath != null)
    			params += " outputPath:"+outputPath;
    		else
    		{
    			File writeFile = stream.getStreamFileForWrite();
    			params += " outputPath:"+writeFile.getAbsolutePath();
    		}
    		params += " versionFile:"+versionFile;
    		params += " startOnKeyFrame:"+startOnKeyFrame;
    		params += " recordData:"+recordData;
    		
    		getLogger().info("ModuleStreamRecord.startRecording: "+params);
    		
    		// create a stream recorder and save it in a map of recorders
    		ILiveStreamRecord recorder = null;
    		
    		// create the correct recorder based on format
    		if (format == FORMAT_MP4)
    			recorder = new LiveStreamRecorderMP4();
    		else
    			recorder = new LiveStreamRecorderFLV();
    		
    		// add it to the recorders list
    		ILiveStreamRecord prevRecorder = recorders.get(streamName);
    		if (prevRecorder != null)
    			prevRecorder.stopRecording();
    		recorders.put(streamName, recorder);
    		
    		// if you want to record data packets as well as video/audio
    		recorder.setRecordData(recordData);
    		
    		// Set to true if you want to version the previous file rather than overwrite it
    		recorder.setVersionFile(versionFile);
    		
    		// If recording only audio set this to false so the recording starts immediately
    		recorder.setStartOnKeyFrame(startOnKeyFrame);
    		
    		// start recording
    		recorder.startRecording(stream, outputPath, append);
    	}
    
    		@Override
    		public void onMediaStreamCreate(IMediaStream stream) {
    			stream.addClientListener(publishNotifier);
    		}
    
    		@Override
    		public void onMediaStreamDestroy(IMediaStream stream) {
    			stream.removeClientListener(publishNotifier);
    			if(!stream.isPlay()) {
    				ILiveStreamRecord recorder = recorders.remove(stream.getName());
    				if (recorder != null)
    					recorder.stopRecording();
    			}
    		}
    		
    	private class PublishNotifier implements IMediaStreamActionNotify {
    
    		@Override
    		public void onPause(IMediaStream stream, boolean isPause,
    				double location) {
    			// TODO Auto-generated method stub
    			
    		}
    
    		@Override
    		public void onPlay(IMediaStream stream, String streamName,
    				double playStart, double playLen, int playReset) {
    			// TODO Auto-generated method stub
    			
    		}
    
    		@Override
    		public void onPublish(IMediaStream stream, String streamName,
    				boolean isRecord, boolean isAppend) {
    //			appInstance.getVHost().getHandlerThreadPool().execute(new DoStartRecording(stream, streamName));
    		}
    
    		@Override
    		public void onSeek(IMediaStream stream, double location) {
    			// TODO Auto-generated method stub
    			
    		}
    
    		@Override
    		public void onStop(IMediaStream stream) {
    			// TODO Auto-generated method stub
    			
    		}
    
    		@Override
    		public void onUnPublish(IMediaStream stream, String streamName,
    				boolean isRecord, boolean isAppend) {
    			ILiveStreamRecord recorder = recorders.remove(streamName);
    
    			if (recorder != null)
    			{
    				// stop recording
    				recorder.stopRecording();
    			}
    		}
    	}
    	
    	private class DoStartRecording implements Runnable {
    		private IMediaStream stream;
    		private String streamName;
    		private DoStartRecording(IMediaStream stream, String streamName) {
    			this.stream = stream;
    			this.streamName = streamName;
    		}
    
    		public void run() {
    			List<String> mediaCasters = appInstance.getMediaCasterStreams().getMediaCasterNames();
    			if (mediaCasters.contains(streamName)) {
    				int lockCount = appInstance.getMediaCasterStreams().getMediaCaster(streamName).getLockCount();
    				if (lockCount > 0)
    					startRecording(stream);
    			}
    		}
    	}
    }

  8. #8
    Join Date
    Oct 2010
    Posts
    10

    Default

    My apologies.. It appears to be working just fine! Guess I had to wait for the hour to roll around...

    Thank you very very much!

  9. #9
    Join Date
    Oct 2010
    Posts
    10

    Default

    uhhh... does camera.stream.flv (original stream file) just record everything still????

    i dont want camera.stream.flv to be 50GB by the end of the month, on top of each hour .mp4 file .. essentially requiring double storage

    ... can it flush that .flv ?

  10. #10

    Default

    The missing first hour was a bug that I never really got around to fixing. As it was running 24/7 it wasn't too much of a problem.

    To stop the original stream from recording, set the stream type to one of the non record types in the Application.xml.

    Also, you do not need the following in your module.
    <Module>
    <Name>ModuleLiveStreamRecord</Name>
    <Description>ModuleLiveStreamRecord</Description>
    <Class>com.wowza.wms.plugin.livestreamrecord.Modul eLiveStreamRecord</Class>
    </Module>
    Your new module replaces this.

    ERROR server comment - invoke(onAppStart): java.lang.NullPointerException: java.util.TimeZone.parseCustomTimeZone(TimeZone.ja va:712)
    This is my fault I had added some code to onAppStart to try and overcome the missing first hour. but put it in the wrong place.

    Code:
    	public void onAppStart(IApplicationInstance appInstance) {
    		TimeZone tz = TimeZone.getTimeZone(timezone);
    		Calendar cal = Calendar.getInstance(tz);
    		hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
    
    		this.appInstance = appInstance;
    		WMSProperties props = appInstance.getProperties();
    		startHour = props.getPropertyInt("startHour", 0);
    		endHour = props.getPropertyInt("endHour", 24);
    		timezone = props.getPropertyStr("timezone", "America/Chicago");
    The first 3 lines need to be moved to below the
    timezone = props.getPropertyStr("timezone", "America/Chicago");
    like this

    Code:
    	public void onAppStart(IApplicationInstance appInstance) {
    		this.appInstance = appInstance;
    		WMSProperties props = appInstance.getProperties();
    		startHour = props.getPropertyInt("startHour", 0);
    		endHour = props.getPropertyInt("endHour", 24);
    		timezone = props.getPropertyStr("timezone", "America/Chicago");
    
    		TimeZone tz = TimeZone.getTimeZone(timezone);
    		Calendar cal = Calendar.getInstance(tz);
    		hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
    Where it was, timezone was null which is what the error was.
    Last edited by rogerlittin; 10-14-2010 at 02:07 PM.

Page 1 of 7 123 ... LastLast

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •