How to use Publisher API and JSpeex to publish an audio stream (VOIP integration)

The following is a quick example of how to use JSpeex with the Publisher API to send raw audio data to a Wowza media server.

First, download Extremists.wav, a sample 16000Hz file, and copy it to the [install-dir]/content folder.

Note: This file was created from the Extremists.flv file using the following ffmpeg command:
cd [install-dir]/content
ffmpeg -i Extremists.flv -vn -ac 2 -ar 16000 Extremists.wav

Second, download the JSpeex library jspeex-0.9.7.zip from Sourceforge.net, then unzip and copy the dist/jspeex.jar into the [install-dir]/lib folder of the server.

Next, create a server listener that is going to start the encoding/publishing process when Wowza Media Server starts:

package com.wowza.wms.plugin.test.integration;

import com.wowza.wms.server.*;

public class ServerListenerSpeexPublisher implements IServerNotify
{
	public void onServerCreate(IServer server)
	{
		System.out.println("ServerListenerSpeexPublisher.onServerCreate");
	}

	public void onServerInit(IServer server)
	{
		System.out.println("ServerListenerSpeexPublisher.onServerInit");
		SpeexPublisher worker = new SpeexPublisher();
		worker.start();
	}

	public void onServerShutdownComplete(IServer server)
	{
	}

	public void onServerShutdownStart(IServer server)
	{
	}

}

Next, create the class that is going to inject the Speex packets using the Publisher API.

package com.wowza.wms.plugin.test.integration;

//ffmpeg -i Extremists.flv -vn -ac 2 -ar 16000 Extremists.wav

import java.io.*;

import org.xiph.speex.*;

import com.wowza.wms.bootstrap.*;
import com.wowza.wms.media.wav.*;
import com.wowza.wms.stream.publish.*;
import com.wowza.wms.vhost.*;

public class SpeexPublisher extends Thread
{
	public SpeexPublisher()
	{
		System.out.println("SpeexPublisher.create");
	}

	public void run()
	{
		String filename = Bootstrap.getServerHome(Bootstrap.APPHOME) + "/content/" + "Extremists.wav";

		try
		{
			RandomAccessFile inf = new RandomAccessFile(filename, "r");
			WAVHeaderInfo wavInfo = WAVUtils.readWAVHeader(inf);
			System.out.println("wavInfo: " + wavInfo);

			int mode = 1; // wide band
			int sampleRate = (int) wavInfo.getSampleRate();
			int channels = wavInfo.getChannelCount();
			int quality = 10; // high quality

			long fileLen = inf.length();
			long startTime = System.currentTimeMillis();
			long timecode = 0;
			int numSamples = 0;
			byte headerByte = (byte)((IVHost.CODEC_AUDIO_SPEEX<<4) + 2 /* 16bits per sample */ + (channels - 1));

			SpeexEncoder speexEncoder = new SpeexEncoder();
			speexEncoder.init(mode, quality, sampleRate, channels);
			numSamples = speexEncoder.getFrameSize();
			System.out.println("numSamples: " + numSamples);

			IVHost vhost = VHostSingleton.getInstance(VHost.VHOST_DEFAULT);
			Publisher publisher = Publisher.createInstance(vhost, "live");

			publisher.setFileExtension("flv");
			publisher.setStreamType("live");

			publisher.publish("myStream", "live");

			while (true)
			{
				byte[] packet = WAVUtils.readSamples(inf, wavInfo, numSamples);

				if (speexEncoder.processData(packet, 0, packet.length))
				{
					int encsize = speexEncoder.getProcessedData(packet, 0);

					byte[] dataPacket = new byte[1 + encsize];
					dataPacket[0] = headerByte;
					System.arraycopy(packet, 0, dataPacket, 1, encsize);

					System.out.println("sendPacket: timecode:" + ((timecode * 1000) / sampleRate) + " len:" + dataPacket.length);
					publisher.addAudioData(dataPacket, dataPacket.length, ((timecode * 1000) / sampleRate));
				}

				long realTime = startTime + ((timecode * 1000) / sampleRate);
				long currTime = System.currentTimeMillis();

				int inc = (int) (realTime - currTime);
				if (inc > 0)
				{
					Thread.currentThread().sleep(inc);
				}

				timecode += numSamples;
				if (inf.getFilePointer() >= fileLen)
					break;
			}

			publisher.unpublish();
			publisher.close();
		}
		catch (Exception e)
		{
			System.out.println("Error: " + e.toString());
		}
	}
}

Next, edit [install-dir]/conf/Server.xml and add the following ServerListener to the <ServerListeners> list:

<ServerListener>
	<BaseClass>com.wowza.wms.plugin.test.integration.ServerListenerSpeexPublisher</BaseClass>
</ServerListener>

Start Wowza Media Server and you will see it start to publish the stream immediately.

To play the stream, open:
[install-dir]/examples/LiveVideoStreaming/FlashRTMPPlayer/player.html (Wowza Media Server 3.5 and later)
-or-
[install-dir]/examples/LiveVideoStreaming/client/live.html (prior version of Wowza Media Server)

Enter the following information and click Connect or Play.
Server: rtmp://[wowza-ip-address]/live
Stream: myStream
 

Notes:
  • Be sure to set up a live application as described in the QuickStart.html guide.
  • The Flash player can only decode 16000Hz wide-band Speex samples.

Originally Published: 10-03-2010.

If you're having problems or want to discuss this article, post in our forum.