Hey we are now using mp4 embedded subtitles (closed captions) and the next step for us is to move on top of Wowza streaming server with this.
Now Wowza just released this:
http://www.wowza.com/forums/showthread.php?14701-New-MP4-video-on-demand-playback-support-subtitle-t...
Wrap up:
- Streaming server is very common for VOD
- Having JW Player to take the mp4 embedded text from there would be purely awesome!!
Just in case I'll post the code from Wowza forum here if someone is lazy clicking. Point ends here further reading is optional ;)
package com.wowza.wms.plugin.test.module;
import java.util.*;
import com.wowza.util.*;
import com.wowza.wms.module.*;
import com.wowza.wms.amf.*;
import com.wowza.wms.application.*;
import com.wowza.wms.request.*;
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 client = stream.getClient();
if (client == null)
break;
int audioIndex = -1;
int videoIndex = -1;
int dataIndex = -1;
Integer audioIndexObj = (Integer)client.getProperties().getProperty(PROPERTY_AUDIOINDEX);
Integer videoIndexObj = (Integer)client.getProperties().getProperty(PROPERTY_VIDEOINDEX);
Integer dataIndexObj = (Integer)client.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);
}
If this feature works the same as reading built-in MP4 Text Tracks, it will simply work out of the box with the captions plugin.