• 流媒体服务器搭建实例——可实现录音,录像功能


    由于我也是刚开始接触这个东东,原理什么的不是很清楚,这里我就不说了,免得误人子弟,嘿嘿!

    第一步,下载FlashMediaServer3.5,网上有很多资源,这里就不提供了,大家google一下就可以了,这里给一个序列号:1373-5209-5319-9982-4515-7002,我用地就是这一个。安装完后,打开FlashMediaServer3.5服务,一个是Start Adobe Flash Media Server 3.5.2,另一个是Start Flash Media Administration Server 3.5.2。

    第二步:在FlashMediaServer3.5安装目录下的applications文件夹下新建一个测试文件夹“tests”。这个文件夹后面会用到。

    第三步:下载Flex Builder 3,地址我也不提供了,网上google一下,有很多资源的。安装Flex Builder3

    第四步:编写录音,录像程序MyTest。这里的代码是从网上扒的。原文地址:http://hi.baidu.com/xulina809/blog/item/6d456db603fb90788bd4b200.html 其中修改了一下我服务器的地址,具体代码如下:

    View Code
    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="playinit()" width="366" height="350" >
    <mx:Script>
    <![CDATA[
    import mx.events.SliderEvent;
    import mx.events.VideoEvent;
    import mx.collections.ArrayCollection;
    import mx.rpc.events.ResultEvent;
    import mx.core.UIComponent;
    import flash.events.StatusEvent;
    import flash.events.SecurityErrorEvent;
    import flash.media.Camera;
    import flash.media.Microphone;
    import flash.net.NetConnection;
    //由于fms使用的是amf0而flex3中的as3默认使用的是amf3.所以要让flex使用AFM0
    NetConnection.defaultObjectEncoding = flash.net.ObjectEncoding.AMF0;
    //视频服务端地址
    private var _videoServerURL:String = "rtmp://192.168.2.105/tests";
    private var _camera:Camera; //定义一个摄像头
    private var _mic:Microphone; //定义一个麦克风
    private var _localVideo:Video; //定义一个本地视频
    private var _netConnection:NetConnection;
    private var _outStream:NetStream; //定义一个输出流
    private var _inStream:NetStream; //定义一个输入流
    private var isplaying:Boolean=false; //定义是否正在播放标记
    private var isrecing:Boolean = false; //定义是否正在录制标记
    private var ispauseing:Boolean = false; //定义是否正在暂停标记
    private var _duration:Number; //定义视频持续时间
    private var playPosition:Number; //定义播放进度位置
    private var soundPosition:Number; //定义声音大小控制条的位置
    private function playinit():void{
    t_hs_control.enabled=false;
    t_btn_play.enabled = false;
    t_btn_stop.enabled = false;
    t_btn_rec.enabled = false;
    t_btn_save.enabled = false;
    t_lbl_rec.visible = false;
    initCameraAndMic(); //初始化摄像头
    }
    //初始化摄像头
    //判断是否存在摄像头和访问权限
    private function initCameraAndMic():void
    {
    _camera = Camera.getCamera();
    if(_camera != null)
    {
    _camera.addEventListener(StatusEvent.STATUS,__onStatusHandler);
    _camera.setMode(320,420,30);
    //t_flv_video.attachCamera(_camera);
    _localVideo = new Video();
    _localVideo.width = 320;
    _localVideo.height = 240;
    _localVideo.attachCamera(_camera);
    t_flv_video.addChild(_localVideo);
    }
    _mic = Microphone.getMicrophone();
    if(_mic != null)
    {
    //未添加侦听麦克连接状态
    //设置本自本地的麦克风的音频传送到本地系统扬声器
    /*

    _mic.setUseEchoSuppression(true);
    _mic.setLoopBack(true);
    */
    _mic.setSilenceLevel(0,-1); //设置麦克风保持活动状态并持续接收集音频数据
    _mic.gain = 80; //设置麦克风声音大小
    }
    }
    //开始录制视频
    //检测网络连接状态
    private function beginOrShowRecVideo():void
    {
    _netConnection = new NetConnection();
    _netConnection.addEventListener(NetStatusEvent.NET_STATUS,__onNetStatusHandler);
    _netConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR,__onSecurityErrorHandler);
    _netConnection.connect(_videoServerURL);
    }
    //录制视频,向服务器传送视频及音频流
    private function beginRecConnectStream():void
    {
    if(_localVideo != null)
    {
    _localVideo.clear();
    t_flv_video.removeChild(_localVideo);
    _localVideo = new Video();
    _localVideo.width = 320;
    _localVideo.height = 240;
    _localVideo.attachCamera(_camera);
    t_flv_video.addChild(_localVideo);
    }
    _outStream = new NetStream(_netConnection);
    _outStream.attachCamera(_camera);
    _outStream.attachAudio(_mic);
    _outStream.publish("testVideo","record");
    }
    //播放视频
    private function showRecConnectStream():void
    {
    _inStream = new NetStream(_netConnection);
    _inStream.addEventListener(NetStatusEvent.NET_STATUS,__onNetStatusHandler);
    _inStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR,__onStreamErrorHandler);
    //定义onMetaData,获取视频相关数据
    var customClient:Object = new Object();
    customClient.onMetaData = function(metadata:Object):void
    {
    _duration = metadata.duration; //获取视频持续时间
    t_hs_control.maximum = _duration; //设置播放进度条最大值
    }
    _inStream.client = customClient;
    //删除原_localVideo,便于在录制和播放视频之间切换
    _localVideo.clear();
    t_flv_video.removeChild(_localVideo);
    _localVideo = new Video();
    _localVideo.width = 320;
    _localVideo.height = 240;
    _localVideo.attachNetStream(_inStream);
    _inStream.play("testVideo");
    t_flv_video.addChild(_localVideo);
    }
    //播放按钮点击后事件:播放视频,同时分析是否播放来调整BUTTON上的标签,显示为播放或者暂停;
    //并监听播放器
    private function flvplay(event:Event):void{
    t_hs_control.enabled=true;
    t_btn_stop.enabled = true;
    t_btn_rec.enabled = false;
    if(!isplaying)
    {
    isplaying = true;
    beginOrShowRecVideo();
    }
    else
    {
    _inStream.togglePause(); //自动在停止和播放之间切换
    }
    if(isplaying)
    {
    if(ispauseing){
    t_btn_play.label="播放"
    }else {
    t_btn_play.label="暂停"
    }
    ispauseing = !ispauseing;
    }
    addEventListener(Event.ENTER_FRAME,__onEnterFrame);
    }
    //停止按钮和视频播放完毕
    //重设一些值变量、按钮enabled值等
    private function resetSomeParam():void
    {
    _inStream.close();
    t_btn_play.label = "播放";
    t_lbl_playtime.text = "0:00 / "+ formatTimes(_duration);
    t_hs_control.value = 0;
    isplaying = false;
    ispauseing = false;
    t_hs_control.enabled=false;
    t_btn_rec.enabled = true;
    t_btn_stop.enabled = false;
    }
    //停止播放按钮点击事件:停止视频,同时调整相关BUTTON上的标签
    private function flvStop(event:Event):void
    {
    resetSomeParam();
    removeEventListener(Event.ENTER_FRAME,__onEnterFrame);
    }
    //拉动进度条
    private function thumbPress(event:SliderEvent):void{
    _inStream.togglePause();
    removeEventListener(Event.ENTER_FRAME,__onEnterFrame);
    }
    //进度条改变后,得到的值赋予PLAYPOSITION;
    private function thumbChanges(event:SliderEvent):void{
    playPosition = t_hs_control.value;
    }
    //放开进度条,再把PLAYPOSITION的值发给播放器;
    private function thumbRelease(event:SliderEvent):void{
    _inStream.seek(playPosition);
    _inStream.togglePause();
    addEventListener(Event.ENTER_FRAME,__onEnterFrame);
    }
    //声音音量控制
    private function sound_thumbChanges(event:SliderEvent):void{
    soundPosition = hs_sound.value;
    }
    private function sound_thumbRelease(event:SliderEvent):void{
    t_flv_video.volume = soundPosition;
    }
    //格式化时间
    private function formatTimes(value:int):String{
    var result:String = (value % 60).toString();
    if (result.length == 1){
    result = Math.floor(value / 60).toString() + ":0" + result;
    } else {
    result = Math.floor(value / 60).toString() + ":" + result;
    }
    return result;
    }
    //录制按钮点击后事件:录制视频,同时分析是否播放来调整BUTTON上的标签,显示为开始录制或者停止录制;
    //并监听播放器
    private function recVideo(event:MouseEvent):void
    {
    if(!isrecing) //开始录制
    {
    isrecing = true;
    t_btn_rec.label = "停止录制";
    t_btn_play.enabled = false;
    t_btn_save.enabled = false;
    t_lbl_rec.visible = true;
    beginOrShowRecVideo();
    }
    else //停止录制
    {
    isrecing = false;
    t_btn_rec.label = "开始录制";
    t_btn_play.enabled = true;
    t_btn_save.enabled = true;
    t_lbl_rec.visible = false;
    _outStream.close();
    }
    }
    //检测摄像头权限事件
    private function __onStatusHandler(event:StatusEvent):void
    {
    if(!_camera.muted)
    {
    t_btn_rec.enabled = true;
    }
    else
    {
    trace("错误:无法链接到活动摄像头!")
    }
    _camera.removeEventListener(StatusEvent.STATUS,__onStatusHandler);
    }
    //网络链接事件
    //如果网络连接成功,开始录制或观看视频
    private function __onNetStatusHandler(event:NetStatusEvent):void
    {
    switch (event.info.code)
    {
    case "NetConnection.Connect.Success":
    if(isrecing)
    {
    beginRecConnectStream();
    }
    else
    {
    showRecConnectStream();
    }
    break;
    case "NetConnection.Connect.Failed":
    trace("连接失败!");
    break;
    case "NetStream.Play.StreamNotFound":
    trace("Stream not found: " + event);
    break;
    }
    }
    private function __onSecurityErrorHandler(event:SecurityErrorEvent):void
    {
    trace("securityErrorHandler:" + event);
    }
    private function __onStreamErrorHandler(event:AsyncErrorEvent):void
    {
    trace(event.error.message);
    }
    //播放视频实时事件
    //实时更改播放进度条值和播放时间值,当视频播放完成时删除实时侦听事件并重新设置一些初始值
    private function __onEnterFrame(event:Event):void
    {
    if(_duration > 0 && _inStream.time > 0)
    {
    t_hs_control.value =_inStream.time;
    t_lbl_playtime.text = formatTimes(_inStream.time) + " / "+ formatTimes(_duration);
    }
    if(_inStream.time == _duration)
    {
    removeEventListener(Event.ENTER_FRAME,__onEnterFrame);
    resetSomeParam();
    }
    }
    ]]>
    </mx:Script>

    <!--通过HTTPSERVICE来分析XML,然后得出RESULT,结果的反馈在SCRIPT里-->
    <!--此为读取XML扩展内容
    <mx:HTTPService id="videoserver" url="assets/videos.xml" result="readXml(event)" />
    -->
    <!--主要的视频播放窗口 设置ID为FLVVIDEO,这个很重要,其他坐标可以随自己喜欢 -->
    <mx:Panel x="12" y="10" width="342" height="282" layout="absolute">
    <mx:VideoDisplay id="t_flv_video" x="1" y="1" width="320" height="240"/>
    <mx:Label x="243.5" y="6" text="正在录制中…" id="t_lbl_rec" color="#666666" fontSize="12"/>
    </mx:Panel>
    <!--播放器的播放进度条,用FLEX自带的HSLIDER来表现播放进度,同时可以拖动影片-->

    <mx:HSlider id="t_hs_control" x="12" y="296" minimum="0"
    thumbPress="thumbPress(event)"
    thumbRelease="thumbRelease(event)"
    change="thumbChanges(event)" />
    <!--播放器声音控制-->
    <mx:HSlider id="hs_sound" x="260" y="295" width="80"
    minimum="0" maximum="1"
    thumbRelease="sound_thumbRelease(event)"
    change="sound_thumbChanges(event)"
    value="{t_flv_video.volume}" />
    <!--播放按钮,根据是否在播放,按钮显示为:播放 或者 暂停-->

    <mx:Button id="t_btn_play" x="22" y="320" click="flvplay(event)" label="播放" fontSize="12" />
    <!--播放按钮,停止播放影片-->
    <mx:Button id="t_btn_stop" label="停止" x="85" y="320"
    click="flvStop(event)" fontSize="12" enabled="true"/>
    <!--时间显示-->
    <mx:Label x="170" y="300" id="t_lbl_playtime"
    text="0:00 / 0:00" color="#ffffff"/>
    <!--录制按钮,根据是否在录制,按钮显示为:开始录制 或者 停止录制-->
    <mx:Button x="210" y="320" label="开始录制" click="recVideo(event)" fontSize="12" id="t_btn_rec"/>
    <!--保存视频按钮-->
    <mx:Button x="299" y="320" label="保存" fontSize="12" id="t_btn_save" enabled="true"/>
    </mx:Application>

    第五步:编写同步播放的程序VideoPlayer,这个也是从网上扒的,原文地址:http://www.cnblogs.com/wuhenke/archive/2009/11/03/1595436.html 我修改了服务器地址,由于源码是flex4的,所以我修改成了Flex3。我的代码如下:

    videoConfig.xml

    View Code
    <?xml version="1.0"?>
    <videoConfig>
    <item>
    <rtmpUrl>rtmp://192.168.2.105/tests/</rtmpUrl>
    <filmName>testVideo.flv</filmName>
    </item>
    </videoConfig>

    VideoEvent.as

    View Code
    package
    {
    import flash.events.EventDispatcher;
    public class VideoEvent extends EventDispatcher
    {
    // 静态常量,定义事件类型
    public static const VidoPlay:String="VideoPlay";
    // 静态实例
    private static var _instance:VideoEvent;
    public static function getInstance():VideoEvent
    {
    if(_instance==null)
    _instance=new VideoEvent();
    return _instance;
    }
    }
    }

    VideoPlayer.mxml

    View Code
    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()" width="366" height="350">
    <!-- Place non-visual elements (e.g., services, value objects) here -->

    <!--<mx:HTTPService id="myService" url="videoConfig.xml" result="resultHandler(event)"/>-->

    <!-- Place non-visual elements (e.g., services, value objects) here -->

    <mx:HTTPService id="myService" url="videoConfig.xml" result="resultHandler(event)"/>


    <mx:Script>
    <![CDATA[
    import mx.rpc.events.ResultEvent;
    import mx.controls.Alert;

    //定义视频播放事件监听对象
    public var instance:VideoEvent=VideoEvent.getInstance();

    private var filmSource:String="";//IronMan.flv

    private function init():void
    {
    //发送读取配置的请求
    myService.send();
    //定义视频播放事件监听
    instance.addEventListener("VideoPlay",playVideoHandler);

    }
    //视频监听的处理
    private function playVideoHandler(event:Event):void
    {
    var myVideo:SharedObject;
    //将播放头置于视频开始处
    myVideo=SharedObject.getLocal("videoCookie");
    var vName:String=myVideo.data.vName;
    //播放选中的视频
    film.source=filmSource+vName;
    }
    private function changeSource():void
    {
    var myVideo:SharedObject;
    //将播放头置于视频开始处
    myVideo=SharedObject.getLocal("videoCookie");
    //将视频的文件名称,存放到共享文件里
    myVideo.data.vName="DarkKnight.flv";
    //一定要先存放VCODE到共享对象里,再分发事件
    instance.dispatchEvent(new Event("VideoPlay"));
    }

    //读取配置文件
    private function resultHandler(event:ResultEvent):void
    {
    //获取流媒体服务器 地址
    filmSource=event.result.videoConfig.item.rtmpUrl;
    //获取流媒体文件名
    var filmName:String=event.result.videoConfig.item.filmName;
    //获取流媒体完整路径
    film.source=filmSource+filmName;
    }
    ]]>
    </mx:Script>

    <mx:VideoDisplay width="396" height="294" id="film">

    </mx:VideoDisplay>
    <mx:Button label="更换播放源" buttonDown="changeSource()" x="8" y="301"/>
    </mx:Application>

    第六步:运行,效果图如下:左图是录像,右图是播放。

    (注意:本文中的程序在现实运行中,播放的画面比实时录像的画面要延时几秒钟,谁有更好的解决方案,请不吝赐教!谢谢)




  • 相关阅读:
    24张图,九大数据结构安排得明明白白
    mysql中的mvcc解读
    常见电商项目的数据库表设计(MySQL版)
    两万字深度介绍分布式系统原理,一文入魂
    使用消息中间件时,如何保证消息仅仅被消费一次?
    GCC/G++选项 -Wl,-Bstatic和-Wl,-Bdynamic
    sql 练习
    设计模式-单例模式
    设计模式-抽象工厂模式
    设计模式-工厂方法模式
  • 原文地址:https://www.cnblogs.com/top5/p/2381850.html
Copyright © 2020-2023  润新知