File -> New -> Wowza Media Server Project,打开新建项目向导,输入项目名 defonds-live-module:
其中,新项目名 defonds-live-module 也会作为 .jar 的文件名,之后作为一个模块被 Wowza IDE 自动部署在 Wowza 服务器 wowza/lib 目录下;Wowza Media Server /Location 应该指向你的 Wowza 服务器的安装目录。点击 Next > 按钮,进入新建 WMS 模块类对话框:
包名栏输入:com.defonds.wms.module;package com.defonds.wms.module; import java.util.HashMap; import java.util.Map; import com.wowza.wms.livestreamrecord.model.ILiveStreamRecord; import com.wowza.wms.livestreamrecord.model.LiveStreamRecorderMP4; import com.wowza.wms.media.model.MediaCodecInfoAudio; import com.wowza.wms.media.model.MediaCodecInfoVideo; import com.wowza.wms.module.*; import com.wowza.wms.stream.*; import com.wowza.wms.amf.AMFPacket; import com.wowza.wms.application.IApplicationInstance; import com.wowza.wms.application.WMSProperties; public class DefondsLiveModule extends ModuleBase implements IModuleOnStream { private Map<String, ILiveStreamRecord> recorders = new HashMap<String, ILiveStreamRecord>(); private IApplicationInstance appInstance; public void onAppStart(IApplicationInstance appInstance) { this.appInstance = appInstance; } class StreamListener implements IMediaStreamActionNotify3 { public void onMetaData(IMediaStream stream, AMFPacket metaDataPacket) { System.out.println("onMetaData[" + stream.getContextStr() + "]: " + metaDataPacket.toString()); } public void onPauseRaw(IMediaStream stream, boolean isPause, double location) { System.out.println("onPauseRaw[" + stream.getContextStr() + "]: isPause:" + isPause + " location:" + location); } public void onPause(IMediaStream stream, boolean isPause, double location) { System.out.println("onPause[" + stream.getContextStr() + "]: isPause:" + isPause + " location:" + location); } public void onPlay(IMediaStream stream, String streamName, double playStart, double playLen, int playReset) { System.out.println("onPlay[" + stream.getContextStr() + "]: playStart:" + playStart + " playLen:" + playLen + " playReset:" + playReset); } public void onPublish(IMediaStream stream, String streamName, boolean isRecord, boolean isAppend) { System.out.println("onPublish[" + stream.getContextStr() + "]: streamName:" + streamName + " isRecord:" + isRecord + " isAppend:" + isAppend); //create a livestreamrecorder instance to create .mp4 files ILiveStreamRecord recorder = new LiveStreamRecorderMP4(); recorder.init(appInstance); recorder.setRecordData(true); recorder.setStartOnKeyFrame(true); recorder.setVersionFile(true); // add it to the recorders list synchronized (recorders) { ILiveStreamRecord prevRecorder = recorders.get(streamName); if (prevRecorder != null) prevRecorder.stopRecording(); recorders.put(streamName, recorder); } // start recording, create 1 minute segments using default content path System.out.println("--- startRecordingSegmentByDuration for 60 minutes"); recorder.startRecordingSegmentByDuration(stream, null, null, 60*60*1000); // start recording, create 1MB segments using default content path //System.out.println("--- startRecordingSegmentBySize for 1MB"); //recorder.startRecordingSegmentBySize(stream, null, null, 1024*1024); // start recording, create new segment at 1:00am each day. //System.out.println("--- startRecordingSegmentBySchedule every "0 1 * * * *"); //recorder.startRecordingSegmentBySchedule(stream, null, null, "0 1 * * * *"); // start recording, using the default content path, do not append (i.e. overwrite if file exists) //System.out.println("--- startRecording"); //recorder.startRecording(stream, false); // log where the recording is being written System.out.println("onPublish[" + stream.getContextStr() + "]: new Recording started:" + recorder.getFilePath()); } public void onUnPublish(IMediaStream stream, String streamName, boolean isRecord, boolean isAppend) { System.out.println("onUnPublish[" + stream.getContextStr() + "]: streamName:" + streamName + " isRecord:" + isRecord + " isAppend:" + isAppend); ILiveStreamRecord recorder = null; synchronized (recorders) { recorder = recorders.remove(streamName); } if (recorder != null) { // grab the current path to the recorded file String filepath = recorder.getFilePath(); // stop recording recorder.stopRecording(); System.out.println("onUnPublish[" + stream.getContextStr() + "]: File Closed:" + filepath); } else { System.out.println("onUnPublish[" + stream.getContextStr() + "]: streamName:" + streamName + " stream recorder not found"); } } public void onSeek(IMediaStream stream, double location) { System.out.println("onSeek[" + stream.getContextStr() + "]: location:" + location); } public void onStop(IMediaStream stream) { System.out.println("onStop[" + stream.getContextStr() + "]: "); } public void onCodecInfoAudio(IMediaStream stream,MediaCodecInfoAudio codecInfoAudio) { System.out.println("onCodecInfoAudio[" + stream.getContextStr() + " Audio Codec" + codecInfoAudio.toCodecsStr() + "]: "); } public void onCodecInfoVideo(IMediaStream stream,MediaCodecInfoVideo codecInfoVideo) { System.out.println("onCodecInfoVideo[" + stream.getContextStr() + " Video Codec" + codecInfoVideo.toCodecsStr() + "]: "); } } public void onStreamCreate(IMediaStream stream) { getLogger().info("onStreamCreate["+stream+"]: clientId:" + stream.getClientId()); IMediaStreamActionNotify3 actionNotify = new StreamListener(); WMSProperties props = stream.getProperties(); synchronized (props) { props.put("streamActionNotifier", actionNotify); } stream.addClientListener(actionNotify); } public void onStreamDestroy(IMediaStream stream) { getLogger().info("onStreamDestroy["+stream+"]: clientId:" + stream.getClientId()); IMediaStreamActionNotify3 actionNotify = null; WMSProperties props = stream.getProperties(); synchronized (props) { actionNotify = (IMediaStreamActionNotify3) stream.getProperties().get("streamActionNotifier"); } if (actionNotify != null) { stream.removeClientListener(actionNotify); getLogger().info("removeClientListener: " + stream.getSrc()); } } }
<Module> <Name>DefondsLiveModule</Name> <Description>DefondsLiveModule</Description> <Class>com.defonds.wms.module.DefondsLiveModule</Class> </Module> <Module> <Name>ModuleServerSide</Name> <Description>Defonds ModuleServerSide</Description> <Class>com.defonds.wms.module.ModuleServerSide</Class> </Module>
这时我们可以在 Wowza IDE 内部启动 Wowza 服务器了。如果你已经启动了一个 service 模式或者 standalone 模式的 Wowza 服务器,那么你要先将其关闭。点击 Wowza IDE 的工具栏里的 Run 菜单里的 WowzaMediaServer_defonds-live-module 启动 Wowza 服务器。当然你也可以点击 Debug 菜单里的 WowzaMediaServer_defonds-live-module 以 debug 模式启动 Wowza。
这时 Wowza 服务器启动起来了,在 Wowza IDE 的下部的控制台标签里可以看到所有的控制台 log 输出。如同 Eclipse 中的 Tomcat,你可以在控制台窗口中点击关闭图标来停止服务器运行。