Note: Wowza Media Server 2.1.2 or greater is required for RTMP and RTSP functionality.
This is a quick code sample to show how to use the IMediaReaderActionNotify listener interface to select audio channels from an multi-channel MP4 file. This module looks for the query parameter audioindex as part of the stream name to select audio channels from the MP4 file. For example, to play the first audio track use the stream name:
Code:
Server: rtmp://[wowza-ip-address]/vod Stream: mp4:sample.mp4?audioindex=0
Code:
Server: rtmp://[wowza-ip-address]/vod Stream: mp4:sample.mp4?audioindex=1
Note: Wowza Media Server 3.0.5 or greater is required for HTTP
To play the second audio track, with a HTTP streaming url, use the following:
Code:
http://[wowza-ip-address]:1935/vod/mp4:sample.mp4/playlist.m3u8?audioindex=1
To play the second audio track, with an RTSP streaming url, use the following:
Code:
rtsp://[wowza-ip-address]:1935/vod/mp4:sample.mp4?audioindex=1
This is just an example of how to accomplish track selection. The important part that is being illustrated here is how to use the MediaReaderH264.setTrackIndexAudio(index) to select the audio track.
Here is the source code for the module:
Code:
Here is the module that select audio channels, as well as video and subtitle (data) channels. It currently only supports selecting tracks by Id but could be extended very easily to do it by language.
package com.wowza.wms.plugin.test.module;
import java.util.*;
import com.wowza.util.*;
import com.wowza.wms.httpstreamer.model.IHTTPStreamerSession;
import com.wowza.wms.module.*;
import com.wowza.wms.amf.*;
import com.wowza.wms.application.*;
import com.wowza.wms.request.*;
import com.wowza.wms.rtp.model.RTPSession;
import com.wowza.wms.stream.*;
import com.wowza.wms.mediareader.h264.*;
import com.wowza.wms.client.*;
import com.wowza.wms.mediareader.h264.atom.*;
public class ModuleMP4AudioChannelSelector extends ModuleBase
{
public static final String PROPERTY_audioindex = "audioindex";
public static final String PROPERTY_VIDEOINDEX = "videoindex";
public static final String PROPERTY_DATAINDEX = "dataindex";
public static final String[] PROPERTY_INDEXES = {PROPERTY_audioindex, PROPERTY_VIDEOINDEX, PROPERTY_DATAINDEX};
class MediaReaderListener implements IMediaReaderActionNotify
{
public void onMediaReaderCreate(IMediaReader mediaReader)
{
getLogger().info("ModuleMediaReaderNotify#MediaReaderListener.onMediaReaderCreate");
}
public void onMediaReaderInit(IMediaReader mediaReader, IMediaStream stream)
{
getLogger().info("ModuleMediaReaderNotify#MediaReaderListener.onMediaReaderInit: "+stream.getName());
}
public void onMediaReaderOpen(IMediaReader mediaReader, IMediaStream stream)
{
getLogger().info("ModuleMediaReaderNotify#MediaReaderListener.onMediaReaderOpen: "+stream.getName());
while(true)
{
IClient RTMPClient = null;
RTPSession RTSPClient = null;
IHTTPStreamerSession HTTPClient = null;
try { RTMPClient = stream.getClient(); } catch (Exception client) {}
try { RTSPClient = stream.getRTPStream().getSession(); } catch (Exception client) {}
try { HTTPClient = stream.getHTTPStreamerSession(); } catch (Exception client) {}
if (RTMPClient == null && RTSPClient == null && HTTPClient == null)
break;
int audioindex = -1;
int videoIndex = -1;
int dataIndex = -1;
if ( RTMPClient != null )
{
Integer audioindexObj = (Integer)RTMPClient.getProperties().getProperty(PROPERTY_audioindex);
Integer videoIndexObj = (Integer)RTMPClient.getProperties().getProperty(PROPERTY_VIDEOINDEX);
Integer dataIndexObj = (Integer)RTMPClient.getProperties().getProperty(PROPERTY_DATAINDEX);
if (audioindexObj != null)
audioindex = audioindexObj.intValue();
if (videoIndexObj != null)
videoIndex = videoIndexObj.intValue();
if (dataIndexObj != null)
dataIndex = dataIndexObj.intValue();
}
if ( RTSPClient != null )
{
Integer audioindexObj = (Integer)RTSPClient.getProperties().getProperty(PROPERTY_audioindex);
Integer videoIndexObj = (Integer)RTSPClient.getProperties().getProperty(PROPERTY_VIDEOINDEX);
Integer dataIndexObj = (Integer)RTSPClient.getProperties().getProperty(PROPERTY_DATAINDEX);
if (audioindexObj != null)
audioindex = audioindexObj.intValue();
if (videoIndexObj != null)
videoIndex = videoIndexObj.intValue();
if (dataIndexObj != null)
dataIndex = dataIndexObj.intValue();
}
if ( HTTPClient != null )
{
Integer audioindexObj = (Integer)HTTPClient.getProperties().getProperty(PROPERTY_audioindex);
Integer videoIndexObj = (Integer)HTTPClient.getProperties().getProperty(PROPERTY_VIDEOINDEX);
Integer dataIndexObj = (Integer)HTTPClient.getProperties().getProperty(PROPERTY_DATAINDEX);
if (audioindexObj != null)
audioindex = audioindexObj.intValue();
if (videoIndexObj != null)
videoIndex = videoIndexObj.intValue();
if (dataIndexObj != null)
dataIndex = dataIndexObj.intValue();
}
if (mediaReader instanceof MediaReaderH264)
{
MediaReaderH264 mediaReaderH264 = (MediaReaderH264)mediaReader;
int audioTrackCount = mediaReaderH264.getTrackCountAudio();
for(int i=0;i<audioTrackCount;i++)
{
String langStr = mediaReaderH264.getTrackLanguageAudio(i);
long trackId = mediaReaderH264.getTrackAudioTrackId(i);
QTAtomtrak trackAtom = mediaReaderH264.getTrackAudioAtom(i);
getLogger().info(" audio["+i+"]: trackId:"+trackId+" lang:"+langStr+" more:"+trackAtom.getTkhdAtom().toString());
}
int videoTrackCount = mediaReaderH264.getTrackCountVideo();
for(int i=0;i<videoTrackCount;i++)
{
long trackId = mediaReaderH264.getTrackVideoTrackId(i);
long trackWidth = mediaReaderH264.getTrackVideoWidth(i);
long trackHeight = mediaReaderH264.getTrackVideoHeight(i);
QTAtomtrak trackAtom = mediaReaderH264.getTrackVideoAtom(i);
getLogger().info(" video["+i+"]: trackId:"+trackId+" width:"+trackWidth+" height:"+trackHeight+" more:"+trackAtom.getTkhdAtom().toString());
}
int dataTrackCount = mediaReaderH264.getTrackCountData();
for(int i=0;i<dataTrackCount;i++)
{
String langStr = mediaReaderH264.getTrackLanguageData(i);
long trackId = mediaReaderH264.getTrackDataTrackId(i);
QTAtomtrak trackAtom = mediaReaderH264.getTrackDataAtom(i);
getLogger().info(" data["+i+"]: trackId:"+trackId+" lang:"+langStr+" more:"+trackAtom.getTkhdAtom().toString());
}
if (audioindex >= 0)
{
getLogger().info(" setTrackIndexAudio: "+audioindex);
mediaReaderH264.setTrackIndexAudio(audioindex);
}
if (videoIndex >= 0)
{
getLogger().info(" setTrackIndexVideo: "+videoIndex);
mediaReaderH264.setTrackIndexVideo(videoIndex);
}
if (dataIndex >= 0)
{
getLogger().info(" setTrackIndexData: "+dataIndex);
mediaReaderH264.setTrackIndexData(dataIndex);
}
}
break;
}
}
public void onMediaReaderExtractMetaData(IMediaReader mediaReader, IMediaStream stream)
{
getLogger().info("ModuleMediaReaderNotify#MediaReaderListener.onMediaReaderExtractMetaData: "+stream.getName());
}
public void onMediaReaderClose(IMediaReader mediaReader, IMediaStream stream)
{
getLogger().info("ModuleMediaReaderNotify#MediaReaderListener.onMediaReaderClose: "+stream.getName());
}
}
public void onAppStart(IApplicationInstance appInstance)
{
appInstance.addMediaReaderListener(new MediaReaderListener());
}
public void play(IClient client, RequestFunction function, AMFDataList params)
{
String streamName = params.getString(PARAM1);
getLogger().info("ModuleMediaReaderNotify.play: "+streamName);
if (streamName != null)
{
int qindex = streamName.indexOf("?");
if (qindex >= 0)
{
String queryStr = streamName.substring(qindex+1);
Map<String, String> queryParams = HTTPUtils.splitQueryStr(queryStr);
for(int i=0;i<PROPERTY_INDEXES.length;i++)
{
String indexStr = PROPERTY_INDEXES[i];
if (queryParams.containsKey(indexStr))
{
int index = -1;
try
{
index = Integer.parseInt(queryParams.get(indexStr));
}
catch(Exception e)
{
}
if (index >= 0)
{
client.getProperties().setProperty(indexStr, new Integer(index));
getLogger().info(" "+indexStr+": "+index);
}
}
}
}
}
invokePrevious(client, function, params);
}
public void onHTTPSessionCreate(IHTTPStreamerSession httpSession)
{
String streamName = httpSession.getUri();
String queryStr = httpSession.getQueryStr();
getLogger().info("ModuleMediaReaderNotify.onHTTPSessionCreate: "+streamName+" queryStr:"+queryStr);
Map<String, String> queryParams = HTTPUtils.splitQueryStr(queryStr);
for(int i=0;i<PROPERTY_INDEXES.length;i++)
{
String indexStr = PROPERTY_INDEXES[i];
if (queryParams.containsKey(indexStr))
{
int index = -1;
try
{
index = Integer.parseInt(queryParams.get(indexStr));
}
catch(Exception e)
{
}
if (index >= 0)
{
httpSession.getProperties().setProperty(indexStr, new Integer(index));
getLogger().info(" "+indexStr+": "+index);
}
}
}
}
public void onRTPSessionCreate(RTPSession rtpSession)
{
String streamName = rtpSession.getUri();
String queryStr = rtpSession.getQueryStr();
getLogger().info("ModuleMediaReaderNotify.onRTPSessionCreate: "+streamName+" queryStr:"+queryStr);
Map<String, String> queryParams = HTTPUtils.splitQueryStr(queryStr);
for(int i=0;i<PROPERTY_INDEXES.length;i++)
{
String indexStr = PROPERTY_INDEXES[i];
if (queryParams.containsKey(indexStr))
{
int index = -1;
try
{
index = Integer.parseInt(queryParams.get(indexStr));
}
catch(Exception e)
{
}
if (index >= 0)
{
rtpSession.getProperties().setProperty(indexStr, new Integer(index));
getLogger().info(" "+indexStr+": "+index);
}
}
}
}
}
Once compiled, you can add this module to an Application.xml file by adding the following <Module> definition to the end of the <Modules> list:
Code:
<Module> <Name>ModuleMP4AudioChannelSelector</Name> <Description>ModuleMP4AudioChannelSelector</Description> <Class>com.wowza.wms.plugin.collection.module.ModuleMP4AudioChannelSelector</Class> </Module>
Note: The HTTP functionality is only supported in Wowza Media Server 3.0.5 or greater.
- Click here, if you are having problems or would like to discuss this article.
- Leave a comment below, if there is some aspect of this article you would like to see changed or improved.


Article List
Categories
Wowza Media