Using VLC with Wowza Pro (native RTP)

Note: I am providing these instructions on as “AS-IS” basis. The reason for the qualification is because I have found VLC to be difficult to get to work reliably and predictably.

Step by step instructions for using the VLC media player as an encoder with Wowza Pro (Flash Player version 9.0.115.0 or greater is required).

  • Download and install Wowza Media Server Pro (version: Wowza Pro 1.7.2 or greater)

  • Create a new Wowza Pro application for streaming (may already exist if examples installed)

  • Create the folder [install-dir]/applications/rtplive

  • Create the folder [install-dir]/conf/rtplive

  • Copy the file [install-dir]/conf/Application.xml into this new folder [install-dir]/conf/rtplive

  • Edit the newly copied Application.xml and change Streams/StreamType to rtp-live and change RTP/AVSyncMethod method to systemclock

  • Startup the Wowza Pro server

  • Hook up webcamera to PC

  • Download and install VLC media player

  • Startup VLC media player and extract the web camera and microphone info to build a VLC command line

  • Select menu item File>Open Capture Device… and select the DirectShow from the Capture mode popup

  • Click the Refresh list buttons for the Video device name and Audio device name lists

  • Select your web camera and microphone from the drop down lists and set the Video size field to 640x480

  • Check the Show more options checkbox and copy and paste the string in the Edit Options field into a text document (we will be using this later to build a command line for VLC)

  • In a text editor we are going to build the command line for VLC. It will use the web camera and microphone information we collected above along with some transcoding and transport properties. Your finished command line should look something like this:

    Windows:
    vlc -vvv -I rc dshow:// :dshow-vdev="QuickCam for Notebooks Pro" :dshow-adev="Logitech Mic (Notebooks Pro)" :dshow-size="320x240" --sout "#transcode{venc=x264{keyint=60},vcodec=x264,vb=500,scale=1,acodec=mp4a,ab=32,channels=2,samplerate=22050}:rtp{dst=127.0.0.1,port-video=10000,port-audio=10002,sdp=file://%WMSCONFIG_HOME%/content/vlc.sdp}"
    Windows (iPhone compatible):
    vlc -vvv -I rc dshow:// :dshow-vdev="QuickCam for Notebooks Pro" :dshow-adev="Logitech Mic (Notebooks Pro)" :dshow-size="320x240" --sout "#transcode{venc=x264{keyint=60,profile=baseline,level=3.0,nocabac,qpmax=36,qpmin=10,me=hex,merange=24,subme=9,qcomp=0.6},vcodec=x264,vb=500,scale=1,acodec=mp4a,ab=96,channels=2,samplerate=48000}:rtp{dst=127.0.0.1,port-video=10000,port-audio=10002,sdp= file://%WMSCONFIG_HOME%/content/vlc.sdp}"
    
    

    Where:

  • First section is the web camera and microphone info collected above

  • transcode section describes the video/audio format of the stream

  • rtp section sets the destination ip address and output location of .sdp file

    Or you can re-stream the Extremists.m4v video as a live stream. The following command line worked for me:

    Windows:
    vlc -vvv -I rc "%WMSCONFIG_HOME%/content/Extremist.m4v" --sout "#transcode{venc=x264{keyint=60},vcodec=x264,vb=500,scale=1,acodec=mp4a,ab=32,channels=2,samplerate=22050}:rtp{dst=127.0.0.1,port-video=10000,port-audio=10002,sdp=file://%WMSCONFIG_HOME%/content/vlc.sdp}"
    Windows (iPhone compatible):
    vlc -vvv -I rc "%WMSCONFIG_HOME%/content/Extremist.m4v" --sout "#transcode{venc=x264{keyint=60,profile=baseline,level=3.0,nocabac,qpmax=36,qpmin=10,me=hex,merange=24,subme=9,qcomp=0.6},vcodec=x264,vb=500,scale=1,acodec=mp4a,ab=96,channels=2,samplerate=48000}:rtp{dst=127.0.0.1,port-video=10000,port-audio=10002,sdp= file://%WMSCONFIG_HOME%/content/vlc.sdp}"
    
    

    Or you can re-stream an RTSP/RTP stream (IP camera). The following command line worked for me (where rtsp://192.168.1.108:554/axis-media/media.amp is the URL to my IP camera):

    Windows:
    vlc -vvv -I rc rtsp://192.168.1.108:554/axis-media/media.amp --sout "#transcode{venc=x264{keyint=60},vcodec=x264,vb=500,scale=1,acodec=mp4a,ab=32,channels=2,samplerate=22050}:rtp{dst=127.0.0.1,port-video=10000,port-audio=10002,sdp=file://%WMSCONFIG_HOME%/content/vlc.sdp}"
    Windows (iPhone compatible):
    vlc -vvv -I rc rtsp://192.168.1.108:554/axis-media/media.amp --sout "#transcode{venc=x264{keyint=60,profile=baseline,level=3.0,nocabac,qpmax=36,qpmin=10,me=hex,merange=24,subme=9,qcomp=0.6},vcodec=x264,vb=500,scale=1,acodec=mp4a,ab=96,channels=2,samplerate=48000}:rtp{dst=127.0.0.1,port-video=10000,port-audio=10002,sdp= file://%WMSCONFIG_HOME%/content/vlc.sdp}"
    
    
  • Open a command prompt change directory to: C:\Program Files\VideoLAN\VLC and copy and paste the command line that you build above

  • To play the stream, double click [install-dir]/examples/NativeRTPVideoStreaming/client/live.html, set Server to rtmp://[server-ip-address]/rtplive and Stream to vlc.sdp and click the Play button

    [install-dir]

    Windows (default): C:\Program Files\Wowza Media Systems\Wowza Media Server Pro [version]

    Mac OS X: /Library/WowzaMediaServerPro

    Linux: /usr/local/WowzaMediaServerPro

    Note: Version VLC media player 1.0.2 Goldeneye seems to be working well for me on Windows. The above command line works with my web camera and the Extremists.m4v sample video.

    Note: The audio and video may be out of sync. This is because VLC does not generate RTCP packets which are required to synchronize the audio and video channels. You may also want to experiment with setting the RTP/AVSyncMethod method in [install-dir]/conf/rtplive/Application.xml to rtptimecode to see if this produces better results.

    Note: If you want to host VLC on a different machine as the server hosting Wowza Media Server, you can modify any of the command lines above by changing dst=127.0.0.1 to the ip address of the server running Wowza Media Server. You will also need to output the SDP file to a folder on the machine running VLC then copy it to the [install-dir]/content directory of the server running Wowza Media Server. The SDP file only needs to be updated if any of the encoding parameters or port numbers change.

    Note: If you experience problems getting either the audio or video to play through Flash, double check the version number of the Flash player (Flash player version 9.0.115.0 or above is required). If you still have problems, turn on Wowza Pro debug logging (edit [install-dir]/conf/log4j.properties and change the log4j.rootCategory on the first line from INFO to DEBUG), try the encoder several more times, zip up and send your [install-dir]/logs folder along with screen shots of the encoder setup screens and the LiveVideoStreaming player screen and send a detailed description of your problem to support@wowza.com.

    updated for version: VLC media player 1.0.2 Goldeneye.

    Charlie

You may want to make sure you specify the audio/video ports for each of the encodes to be different. It looks something like this:

#rtp{dst=192.168.1.7,port-video=10000,port-audio=10002,sdp=file:///c:/test.sdp}

Charlie

Please post the VLC command line you are trying to use.

Charlie

I would focus on this part of the command:

sdp=file://globalwowzaserver/rtplive/vlc.sdp

Is it actually saving the .sdp file in the correct location. It doesn’t look right to me. Get this working and it will probably all start working.

Charlie

Cool, glad to hear it is all working for you.

Charlie

I would not mess with these settings. They do not affect the overall server latency. You may want to switch to the live-lowlatency stream type. You can change the stream type by changing the Streams/StreamType value in [install-dir]/conf/[application]/Application.xml. Change this value from live to live-lowlatency.

I have also found that with H.264 live video the latency is never less than two seconds unless you set the NetStream.setBufferTime(seconds) to 0 seconds. This seems to be an issue with the Flash player. If this value is set to anything other than 0 then it will introduce 2-3 seconds of latency. I have also found that setting this time to 0 sometimes throws the audio/video out of sync.

Charlie

You can change the vlc command so that the .sdp file is hosted at a web address. It goes from this:

sdp=file://%WMSCONFIG_HOME%/content/vlc.sdp

To this:

sdp=http://[vlc-ip-address]/vlc.sdp

Where [vlc-ip-address] is the ip address of the server running VLC.

Now, you can use the stream name: http://[vlc-ip-address]/vlc.sdp (again substituting the ip address of the server running VLC).

Charlie

Metadata in the SDP file will be passed through to the onMetaData event in Flash. All you need to do it figure out how to get the metdata into the SDP data. For example, the following SDP data:

v=0
o=- 833618234 1591012244 IN IP4 127.0.0.1
s=NameTest
i=DescriptionTest
a=x-qt-text-cmt:Orban Opticodec-PC
a=x-qt-text-nam:NameTest
a=x-qt-text-inf:DescriptionTest
a=control:*
t=0 0
m=audio 0 RTP/AVP 96
c=IN IP4 127.0.0.1
b=AS:32
a=rtpmap:96 MPEG4-GENERIC/44100/2
a=fmtp:96 streamtype=5;profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=139056E5A0
a=control:trackid=1

Will result in the following metadata elements:

metaData.rtpsessioninfo["name"] = "NameTest";
metaData.rtpsessioninfo["information"] = "DescriptionTest";
metaData.rtpsessioninfo["attributes"]["x-qt-text-cmt"] = "Orban Opticodec-PC";
metaData.rtpsessioninfo["attributes"]["x-qt-text-nam"] = "NameTest";
metaData.rtpsessioninfo["attributes"]["x-qt-text-inf"] = "DescriptionTest";

The trick is to figure out how to get this information into the SDP data.

Charlie

Try install this patch:

WowzaMediaServerPro1.5.3-patch11.zip

There was a problem with a video only stream on the first stream connected. This fixes the problem.

Charlie

Probably because the timecodes are starting over again (going back to zero). Flash does not like it when the timecodes are not in order. I don’t have a solution for this at this time.

Charlie

Yes, you can start it using the MediaCaster interface. Take a look at this module:

package com.wowza.wms.mediacaster;
import java.util.*;
import com.wowza.wms.amf.*;
import com.wowza.wms.application.*;
import com.wowza.wms.client.*;
import com.wowza.wms.module.*;
import com.wowza.wms.request.*;
/**
 * 
 * <p>ModuleMediaCaster: Module for manipulating media casters through a Flash UI.</p>
 * @author Wowza Media Systems
 *
 */
public class ModuleMediaCaster extends ModuleBase implements IModuleOnApp
{
	class MediaCasterHolder
	{
		int lockCount = 0;
		String streamName = null;
		String streamType = null;
		MediaCasterStreamItem mediaCasterItem = null;
		
		public MediaCasterHolder(String streamName, String streamType, MediaCasterStreamItem mediaCasterItem)
		{
			this.streamName = streamName;
			this.streamType = streamType;
			this.mediaCasterItem = mediaCasterItem;
		}
	}
	
	Map<String, MediaCasterHolder> lockMap = new HashMap<String, MediaCasterHolder>();
	
	/**
	 * onAppStart
	 */
	public void onAppStart(IApplicationInstance appInstance)
	{
		getLogger().info("ModuleMediaCaster.onAppStart: "+appInstance.getApplication().getName()+"/"+appInstance.getName());
	}
	/**
	 * onAppStop
	 */
	public void onAppStop(IApplicationInstance appInstance)
	{
		getLogger().info("ModuleMediaCaster.onAppStop: "+appInstance.getApplication().getName()+"/"+appInstance.getName());
		synchronized(lockMap)
		{
			Iterator<MediaCasterHolder> iter = lockMap.values().iterator();
			while(iter.hasNext())
			{
				MediaCasterHolder holder = iter.next();
				while(holder.lockCount > 0)
				{
					getLogger().info("  release: "+holder.streamName);
					holder.mediaCasterItem.release();
					holder.lockCount--;
				}
			}
			lockMap.clear();
		}
	}
	
	/**
	 * Get the current lock count for a stream
	 * @param client client
	 * @param function function
	 * @param params {streamName}
	 */
	public void getLockCount(IClient client, RequestFunction function, AMFDataList params)
	{
		int count = 0;
		
		String streamName = params.getString(PARAM1);
		IApplicationInstance appInstance = client.getAppInstance();
		MediaCasterStreamMap mediaCasterMap = appInstance.getMediaCasterStreams();
		MediaCasterStreamItem mediaCasterItem = mediaCasterMap.getMediaCaster(streamName);
		if (mediaCasterItem != null)
			count = mediaCasterItem.getLockCount();
		else
			getLogger().info("ModuleMediaCaster.getLockCount: MediaCaster not found: "+streamName);
		
		sendResult(client, params, count);
	}
	/**
	 * Get the numbers of players associated with a particular media caster
	 * @param client client
	 * @param function function
	 * @param params  {streamName}
	 */
	public void getPlayerCount(IClient client, RequestFunction function, AMFDataList params)
	{
		int count = 0;
		
		String streamName = params.getString(PARAM1);
		IApplicationInstance appInstance = client.getAppInstance();
		MediaCasterStreamMap mediaCasterMap = appInstance.getMediaCasterStreams();
		MediaCasterStreamItem mediaCasterItem = mediaCasterMap.getMediaCaster(streamName);
		if (mediaCasterItem != null)
			count = mediaCasterItem.getPlayerCount();
		else
			getLogger().info("ModuleMediaCaster.getPlayerCount: MediaCaster not found: "+streamName);
		
		sendResult(client, params, count);
	}
	
	/**
	 * Get an array of stream names that are media casters associate with this application instance (returned as AMFDataArray)
	 * @param client client
	 * @param function function
	 * @param params (no params)
	 */
	public void getStreamNames(IClient client, RequestFunction function, AMFDataList params)
	{
		AMFDataArray ret = new AMFDataArray();
		
		IApplicationInstance appInstance = client.getAppInstance();
		MediaCasterStreamMap mediaCasterMap = appInstance.getMediaCasterStreams();
		List<String> mediaCasterNames = mediaCasterMap.getMediaCasterNames();
		Iterator<String> iter = mediaCasterNames.iterator();
		while(iter.hasNext())
		{
			String mediaCasterName = iter.next();
			ret.add(new AMFDataItem(mediaCasterName));
		}
		
		sendResult(client, params, ret);
	}
	
	private void resetStream(IApplicationInstance appInstance, String streamName)
	{
		MediaCasterStreamMap mediaCasterMap = appInstance.getMediaCasterStreams();
		MediaCasterStreamItem mediaCasterItem = mediaCasterMap.getMediaCaster(streamName);
		if (mediaCasterItem != null)
			mediaCasterItem.reset();
		else
			getLogger().info("ModuleMediaCaster.resetStream: MediaCaster not found: "+streamName);
	}
	
	/**
	 * Reset a media caster stream
	 * @param client client
	 * @param function function
	 * @param params {streamName}
	 */
	public void resetStream(IClient client, RequestFunction function, AMFDataList params)
	{
		String streamName = params.getString(PARAM1);
		IApplicationInstance appInstance = client.getAppInstance();
		MediaCasterStreamMap mediaCasterMap = appInstance.getMediaCasterStreams();
		MediaCasterStreamItem mediaCasterItem = mediaCasterMap.getMediaCaster(streamName);
		if (mediaCasterItem != null)
			mediaCasterItem.reset();
		else
			getLogger().info("ModuleMediaCaster.resetStream: MediaCaster not found: "+streamName);
		
	}
	
	/**
	 * Force shutdown a media caster stream
	 * @param client client
	 * @param function function
	 * @param params {streamName}
	 */
	public void shutdownStream(IClient client, RequestFunction function, AMFDataList params)
	{
		String streamName = params.getString(PARAM1);
		IApplicationInstance appInstance = client.getAppInstance();
		MediaCasterStreamMap mediaCasterMap = appInstance.getMediaCasterStreams();
		MediaCasterStreamItem mediaCasterItem = mediaCasterMap.getMediaCaster(streamName);
		if (mediaCasterItem != null)
			mediaCasterItem.shutdown(false);
		else
			getLogger().info("ModuleMediaCaster.shutdownStream: MediaCaster not found: "+streamName);
		
	}
	
	private MediaCasterStreamItem acquireMediaCaster(IApplicationInstance appInstance, String streamName, String streamType)
	{
		if (streamType == null)
			streamType = appInstance.getStreamType();
		MediaCasterStreamMap mediaCasterMap = appInstance.getMediaCasterStreams();
		MediaCasterStreamItem mediaCasterItem = mediaCasterMap.acquire(streamName, streamType);
		return mediaCasterItem;
	}
	
	private void releaseMediaCaster(MediaCasterStreamItem mediaCasterItem)
	{
		mediaCasterItem.release();
	}
	/**
	 * Increment the lock count of a media caster stream. If the media caster does not exists create it and connect.
	 * @param client client
	 * @param function function
	 * @param params {streamName, streamType [optional]}
	 */
	public void acquireMediaCaster(IClient client, RequestFunction function, AMFDataList params)
	{
		String streamName = params.getString(PARAM1);
		String streamType = PARAM2<params.size()?params.getString(PARAM2):null;
		
		synchronized(lockMap)
		{
			MediaCasterHolder holder = lockMap.get(streamName);
			if (holder == null)
			{
				IApplicationInstance appInstance = client.getAppInstance();
				if (streamType == null)
					streamType = appInstance.getStreamType();
				MediaCasterStreamMap mediaCasterMap = appInstance.getMediaCasterStreams();
				MediaCasterStreamItem mediaCasterItem = mediaCasterMap.acquire(streamName, streamType);
				if (mediaCasterItem != null)
				{
					holder = new MediaCasterHolder(streamName, streamType, mediaCasterItem);
					holder.lockCount++;
					lockMap.put(streamName, holder);
				}
				else
					getLogger().error("ModuleMediaCaster.acquireMediaCaster(streamName:"+streamName+" streamType:"+streamType+"): Failure");
			}
			else
			{
				holder.mediaCasterItem.acquire();
				holder.lockCount++;
			}
			if (holder != null)
				getLogger().info("ModuleMediaCaster.acquireMediaCaster(streamName:"+streamName+" streamType:"+streamType+"): Success");
		}
	}
	
	/**
	 * Decrement the lock count of a media caster stream
	 * @param client client
	 * @param function function
	 * @param params {streamName}
	 */
	public void releaseMediaCaster(IClient client, RequestFunction function, AMFDataList params)
	{
		String streamName = params.getString(PARAM1);
		synchronized(lockMap)
		{
			MediaCasterHolder holder = lockMap.get(streamName);
			if (holder != null)
			{
				holder.mediaCasterItem.release();
				holder.lockCount--;
				if (holder.lockCount == 0)
					lockMap.remove(streamName);
				getLogger().info("ModuleMediaCaster.releaseMediaCaster(streamName:"+streamName+"): Success");
			}
			else
				getLogger().error("ModuleMediaCaster.releaseMediaCaster(streamName:"+streamName+"): Failure");
		}
	}
	
}

It is built into the server at the path com.wowza.wms.mediacaster.ModuleMediaCaster . The acquireMediaCaster and releaseMediaCaster are examples of how you can start/stop a VLC based stream by stream name without the publish command.

does it call OnPublish server side method after client calls NetStream.play

No it does not.

Charlie

You should be able to play both at the same time. If you can’t get this working, zip up and send me your sdp file and zip up your logs folder. Send them to charlie@wowza.com.

Charlie

Be sure that you have modified the vlc command line to point the stream to the ip address of the machine running Wowza Pro. This is most likely the problem.

Charlie

Just leave the stream type as rtp-live. It can still function as an origin. It does not have to be liverepeater-origin for origin/edge to work.

Charlie

You can add the following properties to the MediaCaster/Properties section of [install-dir]/conf/[application]/Application.xml. It will instruct the server to monitor the SDP file and restart the stream when it changes.

<Property>
	<Name>streamTimeout</Name>
	<Value>0</Value>
	<Type>Integer</Type>
</Property>
<Property>
	<Name>sdpFileCheckFreqency</Name>
	<Value>2000</Value>
	<Type>Integer</Type>
</Property>
<Property>
	<Name>sdpHTTPCheckFreqency</Name>
	<Value>2000</Value>
	<Type>Integer</Type>
</Property>

Charlie

Try something more like this command line:

vlc -vvv dshow:// :dshow-vdev="QuickCam for Notebooks Pro" :dshow-adev="Logitech Mic (Notebooks Pro)" :dshow-size="640x480" --sout "#transcode{venc=x264,vcodec=x264,vb=500,scale=1,acodec=mp4a,ab=32,channels=2,samplerate=22100}:rtp{dst=[ec2-public-domain-name],port-video=10000,port-audio=10002,sdp=http://[vlc-ip-address]/vlc.sdp}"

where [ec2-public-domain-name] is the domain name of the ec2 image and [vlc-ip-address] is the ip address of the machine running VLC.

You will also need to open up udp port 10000-10003 in EC2 to traffic:

ec2-authorize default -P udp -p 10000-10003

You will pick up the SDP information by using the stream name:

Server: rtmp://[ec2-public-domain-name]/rtplive

Stream: http://[vlc-ip-address]/vlc.sdp

VLC and EC2 in combination is tricky. It might requires lots and lots of expirmentation and Googling. This should get you going on the right track. I have not tried it myself.

Charlie

Why don’t you try the suggest command line:

vlc -vvv c:\wms\content\Extremists.m4v --sout "#transcode{venc=x264,vcodec=x264,vb=500,scale=1,acodec=mp4a,ab=32,channels=2,samplerate=22100}:rtp{dst=127.0.0.1,sdp=file://%WMSCONFIG_HOME%/content/Extremists.sdp}"

Just change c:\wms\content\Extremists.m4v to a valid path to the sample Extremists.m4v file. To play the stream use the stream name:

Extremists.sdp

Get this working first.

Charlie

We only support H.264/AAC over RTP. So it must be H.264 or AAC content. We do no support MP3 over this protocol.

Charlie

Please post the warning/error messages from the Wowza Pro logs. Be sure that the ip address that Wowza Pro is trying to bind to is correct. It must be valid ip address on the machine running Wowza Pro. You can see the valid ip addresses on a machine by executing the command:

Linux/OSX:

ifconfig

Windows:

ipconfig/all

If it is not then edit the SDP file generated by VLC and change the line that starts with “c=” to point to the correct address.

Charlie

You need to send the stream from the server running VLC to the server running Wowza Pro. The destination ip address in the VLC command line needs to be that of the server running Wowza Pro. The SDP file should contain the ip address of the server running Wowza Pro (not the server running VLC). VLC is sending the stream to the Wowza Pro ip address. Wowza Pro should be binding to the ip address of the server on which it is running.

Make sense?

Charlie