使用这个模块可以动态的调整每一个RTSP直播流的RTP/AVSyncMethod(例如一个从IP摄像头或mpeg-ts编码器输出的流)。
只要你设置一个文本文件来记录每一个RTSP流和它们希望的音视频同步方式,当这个流输入到Wowza时,这个模块就会解析这个文件。
下面的代码,你无需修改就可以直接编译使用,或者你可以根据你的需求修改它。
下面是这个Module的完整的代码示例:
import java.io.BufferedReader; import java.io.FileInputStream; import java.io.InputStreamReader; import java.util.ArrayList; import com.wowza.wms.amf.*; import com.wowza.wms.bootstrap.Bootstrap; import com.wowza.wms.logging.WMSLoggerFactory; import com.wowza.wms.module.*; import com.wowza.wms.stream.*; import com.wowza.wms.media.model.*; import com.wowza.wms.rtp.model.*; public class ModuleIPCameraDynamicallySetAVSync extends ModuleBase { private String logPrefix = "ModuleIPCameraDynamicallySetAVSync::"; class StreamListener implements IMediaStreamActionNotify3 { private String logPrefix = "ModuleIPCameraDynamicallySetAVSync::StreamListener::"; public void onMetaData(IMediaStream stream, AMFPacket metaDataPacket) { } public void onPauseRaw(IMediaStream stream, boolean isPause, double location) { } public void onPause(IMediaStream stream, boolean isPause, double location) { } public void onPlay(IMediaStream stream, String streamName, double playStart, double playLen, int playReset) { } private int getAvsyncMethodPropertyInt(String name){ if(name.toLowerCase().equalsIgnoreCase("rtptimecode")){ return RTPStream.AVSYNCMETHODS_RTPTIMECODE; } else if(name.toLowerCase().equalsIgnoreCase("sendreport")){ return RTPStream.AVSYNCMETHODS_SENDERREPORT; } else if(name.toLowerCase().equalsIgnoreCase("systemclock")){ return RTPStream.AVSYNCMETHODS_SYSTEMCLOCK; } return RTPStream.AVSYNCMETHODS_UNKNOWN; } private int getAvsyncStreamMethod(String filePath, String streamName) { ArrayList<String> mediaCacheFiles = new ArrayList<String>(); try { FileInputStream in = new FileInputStream(filePath); BufferedReader br = new BufferedReader(new InputStreamReader(in)); String item = ""; while ((item = br.readLine()) != null) { item = item.trim(); if (!item.startsWith("#") && item.length() > 0) { String[] parts = item.split(" "); if(parts.length==2){ String fileStreamName = parts[0].trim(); String fileSyncMethod = parts[1].trim(); if(fileStreamName.equals(streamName)){ return this.getAvsyncMethodPropertyInt(fileSyncMethod); } } } } } catch (Exception e) { WMSLoggerFactory.getLogger(null).info( this.logPrefix + "getAvsyncStreamMethod::" + e.getMessage()); } return -1; } public void onPublish(IMediaStream stream, String streamName, boolean isRecord, boolean isAppend) { String avSyncTxtFilePath = Bootstrap .getServerHome(Bootstrap.CONFIGHOME) + "/conf/avsyncstreams.txt"; getLogger().info( this.logPrefix+"#onPublish[" + stream.getContextStr() + "]: " + streamName+" :: "+avSyncTxtFilePath); int method = this.getAvsyncStreamMethod(avSyncTxtFilePath, streamName); if(method>=0){ RTPStream rtpStream = stream.getRTPStream(); if (rtpStream != null){ getLogger().info( this.logPrefix+"#onPublish[" + stream.getContextStr() + "]: " + streamName +" set Avsync Method to "+method); rtpStream.setAVSyncMethod(method); } else{ getLogger().info( this.logPrefix+"#onPublish[" + stream.getContextStr() + "]: " + streamName +" is not RTPStream"); } } else{ getLogger().info( this.logPrefix+"#onPublish[" + stream.getContextStr() + "]: " + streamName+" is not a valid stream"); } } public void onSeek(IMediaStream stream, double location) { } public void onStop(IMediaStream stream) { } public void onUnPublish(IMediaStream stream, String streamName, boolean isRecord, boolean isAppend) { } public void onCodecInfoVideo(IMediaStream stream, MediaCodecInfoVideo codecInfoVideo) { } public void onCodecInfoAudio(IMediaStream stream, MediaCodecInfoAudio codecInfoAudio) { } } public void onStreamCreate(IMediaStream stream) { IMediaStreamActionNotify2 actionNotify = new StreamListener(); stream.addClientListener(actionNotify); } }在上面的publish事件中,这个代码尝试用/conf目录下的avsyncstreams.txt文件来匹配一个输入流。如果匹配到了,就会接着去找到对应的音视频同步的参数,然后将其设置到setAVSyncmethod方法中。
可以使用以下几种音视频同步方式:
- senderreport – 使用Real Time Transport Control Protocol (RTCP)协议的Sender Report (SR) 数据包。这是默认的方式。
- rtptimecode – 假定RTP timecodes 是绝对的时间戳
- systemclock – 用系统时钟来进行同步。
配置
- 使用Wowza IDE编译这个模块的源代码(Module 部分)
- 将下面的Module添加到你的[install-dir]/conf/[application]/Application.xml文件的Root/Application/Modules 部分:
<Module> <Name>ModuleIPCameraDynamicallySetAVSync</Name> <Description>ModuleIPCameraDynamicallySetAVSync</Description> <Class>com.wowza.wms.plugin.test.module.ModuleIPCameraDynamicallySetAVSync</Class> </Module>
- 在[install-dir]/conf目录下添加avsyncstreams.txt文件
- 按下面的格式添加流的名字和对应的音视频同步方式:
#StreamName[space]AVSyncMethod #camera.stream syncmethod cam1.stream sendreport