• 注意:本文使用了过时的 Flash ,请尽量避免使用并关注后续的 HTML5 DASH + WebRTC。

    首先在一台 Linux 服务器上下载 AMS ,如果官网太慢,可以下载我转存的 http://pan.baidu.com/s/1eQdFxaa

    ➜  AMS  ./installAMS
    
    Do you agree with the license agreement? (y/n): y
    
    Please enter your Adobe Media Server 5 serial number: #不推荐使用盗版,当然搜一下就能搜到 2333
    
    # 之后一路回车就好,如果不需要 HTTP Fallback 的话可以把 80 端口去掉
    
    service ams start
    

    安装并启动之后,如果端口监听正常,就不需要什么配置

    然后打开 tools/multicast/configurator/configurator.html

    Multicast 类型选择 Peer to Peer ,服务器输入 rtmfp://SERVER_IP/multicast ,password 随便输入一个,点击生成然后选择 Show GroupSpecs

    Groupspec With Auth 就是直播用的,Groupspec Without Auth 是观看用的,两个都记下来。

    打开 OBS 进行如下配置

    其实直接点击推流就可以观看了,但是由于播放器并没有加入 P2P Netgroup , 还需要对播放器动手脚,我们拿 Video.JS 的 SWF 版开刀

    先下载源代码

    git clone https://github.com/videojs/video-js-swf
    
    npm install
    

    用文本编辑器打开,定位到 src/com/videojs/providers/RTMPVideoProvider.as 修改几处内容

    // 第 26 行加入
    
    private var _ng:NetGroup;
    private var groupSpec:String = "你的 GropSpecWithoutAuth";
    
    //  422 行左右
    
    private function initNetStream():void{
        if(_ns != null){
            _ns.removeEventListener(NetStatusEvent.NET_STATUS, onNetStreamStatus);
            _ns = null;
        }
        _ns = new NetStream(_nc,groupSpec);
        _ns.addEventListener(NetStatusEvent.NET_STATUS, onNetStreamStatus);
        _ns.client = this;
        _ns.bufferTime = 1;
        _ns.play(_src.streamURL);
        _videoReference.attachNetStream(_ns);
        _model.broadcastEventExternally(ExternalEventName.ON_LOAD_START);
        _model.broadcastEvent(new VideoPlaybackEvent(VideoPlaybackEvent.ON_STREAM_READY, {ns:_ns}));
    }
    
    // 469 行 左右
    
    private function onNetConnectionStatus(e:NetStatusEvent):void{
        switch(e.info.code){
            case "NetConnection.Connect.Success": // 连接成功
                _model.broadcastEventExternally(ExternalEventName.ON_RTMP_CONNECT_SUCCESS);
                if(_src.connectionURL.indexOf("rtmfp") == 0){ // 如果是 RTMFP 链接
                    _ng = new NetGroup(_nc,groupSpec); // 创建 NetGroup
                    _ng.addEventListener(NetStatusEvent.NET_STATUS,onNetConnectionStatus);
                    _model.broadcastEventExternally("P2P.waitforconf"); // 等待用户选择允许或拒绝 
                    // 不知道那几个国产流氓视频网站是怎么跳过这个是否开启 P2P 的提示
                }else{
                    _ns = new NetStream(_nc);
                    _ns.addEventListener(NetStatusEvent.NET_STATUS, onNetStreamStatus);
                    _ns.client = this;
                    _ns.bufferTime = 1;
                    _ns.play(_src.streamURL);
                    _videoReference.attachNetStream(_ns);
                    _model.broadcastEventExternally(ExternalEventName.ON_LOAD_START);
                    _model.broadcastEvent(new VideoPlaybackEvent(VideoPlaybackEvent.ON_STREAM_READY, {ns:_ns}));
                }                    
                break;
            case "NetConnection.Connect.Failed":
                if(_ncRTMPCurrentRetry < _ncRTMPRetryThreshold){
                    _ncRTMPCurrentRetry++;
                    _model.broadcastErrorEventExternally(ExternalErrorEventName.RTMP_CONNECT_FAILURE);
                    _rtmpRetryTimer.start();
                    _model.broadcastEventExternally(ExternalEventName.ON_RTMP_RETRY);
                }
                break;
            case "NetGroup.Connect.Success": // 用户选择了允许 P2P ,开始播放
               initNetStream();
               break;
            case "NetGroup.Connect.Rejected": // 用户选择了拒绝 P2P ,重新连接,不带 netgroup
                _ns = new NetStream(_nc);
                _ns.addEventListener(NetStatusEvent.NET_STATUS, onNetStreamStatus);
                _ns.client = this;
                _ns.bufferTime = 1;
                _ns.play(_src.streamURL);
                _videoReference.attachNetStream(_ns);
                _model.broadcastEventExternally(ExternalEventName.ON_LOAD_START);
                _model.broadcastEvent(new VideoPlaybackEvent(VideoPlaybackEvent.ON_STREAM_READY, {ns:_ns}));
               break;
            default:
    
                if(e.info.level == "error"){
                    _model.broadcastErrorEventExternally(e.info.code);
                    _model.broadcastErrorEventExternally(e.info.description);
                }
    
                break;
        }
        _model.broadcastEvent(new VideoPlaybackEvent(VideoPlaybackEvent.ON_NETCONNECTION_STATUS, {info:e.info}));
    }
    
    // 608 行左右,default 之前加入
    
    case "NetStream.Video.DimensionChange": // P2P 的视频会比音频后到一会,收到此事件代表真正开始播放
        _model.broadcastEvent(new VideoPlaybackEvent(VideoPlaybackEvent.ON_VIDEO_DIMENSION_UPDATE, {videoWidth: _videoReference.videoWidth, videoHeight: _videoReference.videoHeight}));
        if(_model.metadata && _videoReference) // 修正视频分辨率,使其充满屏幕
        {
            _model.metadata.width = _videoReference.videoWidth;
            _model.metadata.height = _videoReference.videoHeight;
        }
        _model.broadcastEventExternally("videoplaying"); // 给 JS 广播一个视频已开始播放的信号
        break;
    

    至此代码修改完成,编译

    grunt dist
    

    运行之后即可在 dist 得到新的 swf 文件,替换掉 vjs 自带的 swf ,然后通过以下 JS 代码加载视频流

    SWFElement.vjs_setProperty("rtmpConnection","rtmfp://SERVER_IP/multicast");
    SWFElement.vjs_setProperty("rtmpStream","流名称");
    

    打开两台电脑测试,就可以看到上传流量了