Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1426159
  • 博文数量: 188
  • 博客积分: 1784
  • 博客等级: 上尉
  • 技术积分: 2772
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-05 22:20
个人简介

发上等愿,结中等缘,享下等福;择高处立,就平处坐,向宽处行。

文章分类

全部博文(188)

文章存档

2020年(12)

2019年(11)

2018年(4)

2017年(3)

2016年(11)

2015年(22)

2014年(19)

2013年(25)

2012年(32)

2011年(49)

分类: 服务器与存储

2014-01-03 16:02:04

  跟 Red5 类似,Wowza 支持服务扩展,用户可以进行自定义应用程序开发,然后将其作为一个模块部署在 Wowza 服务器。Red5 提供了一个 Eclipse 插件进行应用扩展开发(参见《eclipse 的 Red5 插件安装简介》),Wowza 则提供了一个 IDE。本文简要介绍如何使用 Wowza IDE 开发第一个 Wowza 服务器扩展应用。《安装并使用 Wowza 发布你的 RTMP 直播流》一文介绍了如何安装 Wowza 服务器并提供直播服务,本文将继续以此为例,介绍如何使用 Wowza IDE 开发应用对每个流频道进行监控。
        I. 下载 Wowza IDE
        官方下载地址 http://wowza.cn/mediaserver/developers,选择适合你自己的平台的版本进行下载。
        作者上传了一个 Windows 版本的到 CSDN 资源以做备份,如果看官嫌从官网下载速度太慢,可以点击下载:

        II. 安装
        Windows 下直接运行步骤 I 下载的 WowzaIDE-2.0.0.exe。
        安装好以后,开始 -> 程序 -> Wowza IDE 2 -> Wowza IDE 2 启动 IDE,选择一个目录作为你的工作台,进入后的界面跟 Eclipse 一般无二:
Wowza 界面
        III. 新建项目

        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;
        类名栏输入:DefondsLiveModule;
        自定义方法名输入:doSomething,这个方法可以被客户端直接调用(NetConnection.call(“doSomething”, null);)。类 DefondsLiveModule 创建以后,你可以使用 doSomething 同样的标签来创建更多自定义方法;
        Event Methods 部分是留给你捕捉一系列事件的接口,在这些事件发生时,这些方法将被调用。本文例子里保持默认选择,点击 Finish 按钮。
新建类
        IDE 会创建 WMS 模块项目,创建模块类,创建一个运行命令并编译和绑定模块类到一个 jar 文件里,这个 jar 文件会被自动部署到 WMS 安装目录下:
新生成的jar文件
        最后编辑 DefondsLiveModule 类内容如下:
  1. package com.defonds.wms.module;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.Map;  
  5.   
  6. import com.wowza.wms.livestreamrecord.model.ILiveStreamRecord;  
  7. import com.wowza.wms.livestreamrecord.model.LiveStreamRecorderMP4;  
  8. import com.wowza.wms.media.model.MediaCodecInfoAudio;  
  9. import com.wowza.wms.media.model.MediaCodecInfoVideo;  
  10. import com.wowza.wms.module.*;  
  11. import com.wowza.wms.stream.*;  
  12. import com.wowza.wms.amf.AMFPacket;  
  13. import com.wowza.wms.application.IApplicationInstance;  
  14. import com.wowza.wms.application.WMSProperties;  
  15.   
  16. public class DefondsLiveModule extends ModuleBase implements IModuleOnStream  
  17. {  
  18.     private Map recorders = new HashMap();  
  19.     private IApplicationInstance appInstance;  
  20.       
  21.       
  22.     public void onAppStart(IApplicationInstance appInstance)  
  23.     {  
  24.         this.appInstance = appInstance;  
  25.     }  
  26.       
  27.     class StreamListener implements IMediaStreamActionNotify3  
  28.     {  
  29.         public void onMetaData(IMediaStream stream, AMFPacket metaDataPacket)  
  30.         {  
  31.             System.out.println("onMetaData[" + stream.getContextStr() + "]: " + metaDataPacket.toString());  
  32.         }  
  33.   
  34.         public void onPauseRaw(IMediaStream stream, boolean isPause, double location)  
  35.         {  
  36.             System.out.println("onPauseRaw[" + stream.getContextStr() + "]: isPause:" + isPause + " location:" + location);  
  37.         }  
  38.   
  39.         public void onPause(IMediaStream stream, boolean isPause, double location)  
  40.         {  
  41.             System.out.println("onPause[" + stream.getContextStr() + "]: isPause:" + isPause + " location:" + location);  
  42.         }  
  43.   
  44.         public void onPlay(IMediaStream stream, String streamName, double playStart, double playLen, int playReset)  
  45.         {  
  46.             System.out.println("onPlay[" + stream.getContextStr() + "]: playStart:" + playStart + " playLen:" + playLen + " playReset:" + playReset);  
  47.         }  
  48.   
  49.         public void onPublish(IMediaStream stream, String streamName, boolean isRecord, boolean isAppend)  
  50.         {  
  51.             System.out.println("onPublish[" + stream.getContextStr() + "]: streamName:" + streamName + " isRecord:" + isRecord + " isAppend:" + isAppend);  
  52.             //create a livestreamrecorder instance to create .mp4 files  
  53.             ILiveStreamRecord recorder = new LiveStreamRecorderMP4();  
  54.             recorder.init(appInstance);  
  55.             recorder.setRecordData(true);  
  56.             recorder.setStartOnKeyFrame(true);  
  57.             recorder.setVersionFile(true);  
  58.                  
  59.             // add it to the recorders list  
  60.             synchronized (recorders)  
  61.             {  
  62.                 ILiveStreamRecord prevRecorder = recorders.get(streamName);  
  63.                 if (prevRecorder != null)  
  64.                     prevRecorder.stopRecording();  
  65.                 recorders.put(streamName, recorder);  
  66.             }  
  67.             // start recording, create 1 minute segments using default content path  
  68.             System.out.println("--- startRecordingSegmentByDuration for 60 minutes");  
  69.             recorder.startRecordingSegmentByDuration(stream, nullnull60*60*1000);  
  70.             // start recording, create 1MB segments using default content path  
  71.             //System.out.println("--- startRecordingSegmentBySize for 1MB");  
  72.             //recorder.startRecordingSegmentBySize(stream, null, null, 1024*1024);  
  73.             // start recording, create new segment at 1:00am each day.  
  74.             //System.out.println("--- startRecordingSegmentBySchedule every "0 1 * * * *");  
  75.             //recorder.startRecordingSegmentBySchedule(stream, null, null, "0 1 * * * *");  
  76.               
  77.             // start recording, using the default content path, do not append (i.e. overwrite if file exists)  
  78.             //System.out.println("--- startRecording");  
  79.             //recorder.startRecording(stream, false);  
  80.               
  81.             // log where the recording is being written  
  82.             System.out.println("onPublish[" + stream.getContextStr() + "]: new Recording started:" + recorder.getFilePath());  
  83.         }  
  84.           
  85.         public void onUnPublish(IMediaStream stream, String streamName, boolean isRecord, boolean isAppend)  
  86.         {  
  87.             System.out.println("onUnPublish[" + stream.getContextStr() + "]: streamName:" + streamName + " isRecord:" + isRecord + " isAppend:" + isAppend);  
  88.               
  89.             ILiveStreamRecord recorder = null;  
  90.             synchronized (recorders)  
  91.             {  
  92.                 recorder = recorders.remove(streamName);  
  93.             }  
  94.               
  95.             if (recorder != null)  
  96.             {  
  97.                 // grab the current path to the recorded file  
  98.                 String filepath = recorder.getFilePath();  
  99.                   
  100.                 // stop recording  
  101.                 recorder.stopRecording();  
  102.                 System.out.println("onUnPublish[" + stream.getContextStr() + "]: File Closed:" + filepath);  
  103.             }  
  104.             else  
  105.             {  
  106.                 System.out.println("onUnPublish[" + stream.getContextStr() + "]: streamName:" + streamName + " stream recorder not found");  
  107.             }  
  108.         }  
  109.           
  110.         public void onSeek(IMediaStream stream, double location)  
  111.         {  
  112.             System.out.println("onSeek[" + stream.getContextStr() + "]: location:" + location);  
  113.         }  
  114.   
  115.         public void onStop(IMediaStream stream)  
  116.         {  
  117.             System.out.println("onStop[" + stream.getContextStr() + "]: ");  
  118.         }  
  119.   
  120.         public void onCodecInfoAudio(IMediaStream stream,MediaCodecInfoAudio codecInfoAudio) {  
  121.             System.out.println("onCodecInfoAudio[" + stream.getContextStr() + " Audio Codec" + codecInfoAudio.toCodecsStr() + "]: ");  
  122.         }  
  123.   
  124.         public void onCodecInfoVideo(IMediaStream stream,MediaCodecInfoVideo codecInfoVideo) {  
  125.             System.out.println("onCodecInfoVideo[" + stream.getContextStr() + " Video Codec" + codecInfoVideo.toCodecsStr() + "]: ");  
  126.         }  
  127.     }  
  128.   
  129.     public void onStreamCreate(IMediaStream stream)  
  130.     {  
  131.         getLogger().info("onStreamCreate["+stream+"]: clientId:" + stream.getClientId());  
  132.         IMediaStreamActionNotify3 actionNotify = new StreamListener();  
  133.   
  134.         WMSProperties props = stream.getProperties();  
  135.         synchronized (props)  
  136.         {  
  137.             props.put("streamActionNotifier", actionNotify);  
  138.         }  
  139.         stream.addClientListener(actionNotify);  
  140.     }  
  141.   
  142.     public void onStreamDestroy(IMediaStream stream)  
  143.     {  
  144.         getLogger().info("onStreamDestroy["+stream+"]: clientId:" + stream.getClientId());  
  145.   
  146.         IMediaStreamActionNotify3 actionNotify = null;  
  147.         WMSProperties props = stream.getProperties();  
  148.         synchronized (props)  
  149.         {  
  150.             actionNotify = (IMediaStreamActionNotify3) stream.getProperties().get("streamActionNotifier");  
  151.         }  
  152.         if (actionNotify != null)  
  153.         {  
  154.             stream.removeClientListener(actionNotify);  
  155.             getLogger().info("removeClientListener: " + stream.getSrc());  
  156.         }  
  157.     }  
  158. }  

        IV. 导入例子模块的类
        Package Explorer 视图下,右击 defonds-live-module 项目中 src 下的 com.defonds.wms.module -> 选择 Import… -> 在导入对话框里,展开 General 文件夹 -> 选中 File System 后点击 Next > 按钮 -> 点击 Browse… 按钮 -> 浏览至 %Wowza%/examples/ServerSideModules/server 文件夹(这个是 Wowza 服务器安装的一部分) 后确定 -> 勾选 ModuleServerSide.java 后点 Finish 按钮,如下图所示。
导入例子模块
        这样子我们就把 ModuleServerSide.java 给导入进来了,Package Explorer 视图中双击导入的类名,发现有编译错误:
编译有错误
        将 package com.mycompany.wms.module; 换成我们自定义的包名 package com.defonds.wms.module; 即可。
        V. 配置 Application.xml
        现在我们已经使用 Wowza IDE 构建好了我们自己定义的 defonds-live-module.jar,我们还需要指示 Wowza 服务器加载这个新模块。
        编辑 %Wowza%/conf/live/Application.xml 文件,将以下模块定义添加进 部分的结尾:
  1. <Module>  
  2.      <Name>DefondsLiveModuleName>  
  3.      <Description>DefondsLiveModuleDescription>  
  4.      <Class>com.defonds.wms.module.DefondsLiveModuleClass>  
  5. Module>  
  6. <Module>  
  7.     <Name>ModuleServerSideName>  
  8.     <Description>Defonds ModuleServerSideDescription>  
  9.     <Class>com.defonds.wms.module.ModuleServerSideClass>  
  10. Module>   

        这时我们的新模块才正式生效了。如果这一步不明白可以去看《安装并使用 Wowza 发布你的 RTMP 直播流》。
        VI. 启动服务

        这时我们可以在 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,你可以在控制台窗口中点击关闭图标来停止服务器运行。

停止服务
        VII. 模块调试
        现在我们来测试一下新模块的运行情况。使用你的 RTMP Client 发送 RTMP 流到 Wowza,比如 Server URL 为 rtmp://172.21.30.104/live,流名为 xxxxS_2059a0734ccfqvga,成功连接 Wowza 服务器。
测试成功
        Wowza IDE 控制台有 onPublish[live/_definst_/xxxxS_2059a0734ccfqvga]: streamName:xxxxS_2059a0734ccfqvga isRecord:false isAppend:false 输出,这个正是我们 DefondsLiveModule 类里的 StreamListener.onPublish 里定义的,测试成功。
        参考资料
阅读(1930) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~