Another approach is to intercept the RTP stream earlier in the process. You can write your own RTP depacketizer for ulaw stream, depacketize the audio stream, transcode and inject Speex packets into the stream. This is the cleanest solution. Here is the source code for the Speex depacketizer:
import java.net.*;
import com.wowza.util.*;
import com.wowza.wms.logging.*;
import com.wowza.wms.rtp.model.*;
import com.wowza.wms.rtp.packetizer.RTPPacketizerBase;
import com.wowza.wms.vhost.*;
public class RTPDePacketizerSpeex extends RTPDePacketizerAudioBase implements IRTPDePacketizer, IRTPTimecodeProvider
{
private RTCPEventHandlerGeneric rtcpEventHandler = new RTCPEventHandlerGeneric();
private RTPSequence seq = new RTPSequence();
private long lastTimecode = -1;
private RolloverLong timecode = new RolloverLong(32);
private RTPPacket workingPacket = null;
private int packetCount = 0;
private boolean bitrateWarning = true;
public void init(RTPContext rtpContext, RTPDePacketizerItem rtpDePacketizerItem)
{
super.init(rtpContext, rtpDePacketizerItem);
if (debugLog)
WMSLoggerFactory.getLogger(null).debug("RTPDePacketizerSpeex.init");
}
public boolean canHandle(RTPTrack rtpTrack)
{
if (rtpTrack.isAudio())
{
while (true)
{
String sampleType = rtpTrack.getSampleType();
if (sampleType == null)
break;
if (sampleType.toLowerCase().startsWith("speex"))
return true;
break;
}
}
return false;
}
public void handleRTCPPacket(SocketAddress socketAddr, RTPTrack rtpTrack, byte[] bytes, int offset, int len)
{
if (debugLog)
{
int dsize = Math.min(len, 16);
WMSLoggerFactory.getLogger(null).debug("rtcp["+rtpTrack.getTrackId()+":"+len+"] {"+DebugUtils.formatBytesShort(bytes, offset, dsize)+"}");
}
if (!checkRTCPSSRC(socketAddr, rtpTrack, bytes, offset, len))
return;
rtcpHandler.handleRTCPPacket(socketAddr, rtpTrack, bytes, offset, len);
}
public void handleRTPPacket(SocketAddress socketAddr, RTPTrack rtpTrack, byte[] bytes, int offset, int len)
{
if (debugLog)
{
int dsize = Math.min(len, 16);
WMSLoggerFactory.getLogger(null).debug("rtp["+rtpTrack.getTrackId()+":"+len+"] {"+DebugUtils.formatBytesShort(bytes, offset, dsize)+"}");
}
if (!checkRTPSSRC(socketAddr, rtpTrack, bytes, offset, len))
return;
seq.handleRTPPacket(rtpTrack, bytes, offset, len);
//hande lower level rtpTrack.getRTPStream().incrementMediaInBytes(len);
long timeval = BufferUtils.byteArrayToLong(bytes, offset+4, 4);
timecode.set(timeval);
int timescale = rtpTrack.getTimescale();
int channels = rtpTrack.getChannelCount();
setAudioCodecId(rtpTrack, IVHost.CODEC_AUDIO_SPEEX);
try
{
int rtpHeaderSize = skipRTPExtensions(bytes, offset, len, RTPPacketizerBase.RTPHEADERSIZE);
int index = 0;
if (workingPacket == null)
{
workingPacket = new RTPPacket();
workingPacket.setType(IVHost.CONTENTTYPE_AUDIO);
workingPacket.setCodec(IVHost.CODEC_AUDIO_SPEEX);
int frameType = 2;
frameType += channels-1;
if (timescale != 16000 && bitrateWarning)
{
WMSLoggerFactory.getLogger(null).warn("RTPDePacketizerSpeex.handleRTPPacket: Flash only supports SPEEX at a bitrate of 16000");
bitrateWarning = false;
}
workingPacket.setFrameType(frameType);
workingPacket.setTimecode(timecode.get());
}
int tailOffset = 0;
for(int i=1;i<3;i++)
{
if (bytes[offset+len-i] == (byte)0xff || bytes[offset+len-i] == (byte)0x7f)
{
tailOffset++;
if (bytes[offset+len-i] == (byte)0x7f)
break;
else
continue;
}
break;
}
RTPPacketFragment packetFragment = new RTPPacketFragment(bytes, offset+rtpHeaderSize+index, len-(rtpHeaderSize+index+tailOffset));
workingPacket.addFragment(packetFragment);
packetCount++;
while(true)
{
boolean timeSyncReady = rtcpHandler.isTimeSyncReady(rtpTrack, workingPacket.getTimecode());
if (!timeSyncReady)
{
if (!timeSyncReady)
checkRTCPMissingWarning();
break;
}
long adjTimecode = rtcpHandler.convertTimeSyncTimecode(timecode.get(), timescale);
if (debugLog)
workingPacket.setDebugLog(true);
workingPacket.write(rtpTrack, adjTimecode);
break;
}
workingPacket = null;
packetCount = 0;
}
catch (Exception e)
{
WMSLoggerFactory.getLogger(null).debug("RTPDePacketizerSpeex.handleRTPPacket: "+e.toString());
}
}
public void startup(RTPTrack rtpTrack)
{
rtcpEventHandler.setTimecodeProvider(this);
rtcpHandler.addEventListener(rtcpEventHandler);
setupAppInstanceRTCPEventHandler(this, rtcpHandler, rtpTrack);
}
public void shutdown(RTPTrack rtpTrack)
{
}
public long getAdjTimecode(RTPTrack rtpTrack)
{
if (lastTimecode != -1 && rtcpHandler.isTimeSyncReady(rtpTrack, lastTimecode))
{
long adjTimecode = rtcpHandler.convertTimeSyncTimecode(lastTimecode, rtpTrack.getTimescale());
return adjTimecode;
}
return -1;
}
}
You will need to add your class to the [install-dir]/conf/RTP.xml de-packetizer list. You should be able to play around with this class to figure out how to intercept the stream. If you return true from the canHandle method then you will receive that streams RTP packets.
Charlie