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

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

第一步,下载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>
复制代码

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

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




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/463981.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一个女孩子居然做了十年硬件。​。。

本文转自面包板社区。--正文--2011年&#xff0c;一个三本大学的电子信息专业的大三女学生跟2个通信专业的大二男生组成了一组代表学校参加2011年“瑞萨杯”全国大学生电子设计大赛&#xff0c;很意外的获得了湖北赛区省三等奖&#xff0c;虽然很意外&#xff0c;但还是挺高兴的…

之前字符串反转的题目

之前发的字符串反转的题目这道字符串反转的题目&#xff0c;你能想到更好的方法吗&#xff1f;有很多人评论了&#xff0c;有的人还写了自己的解题思路&#xff0c;还有人写了自己的代码还有其中呼声很高的压栈解法我相信很多人在笔试的时候一定会遇到这类题目&#xff0c;给你…

hdu 3488

可以作为KM 二分图最大权匹配模板 View Code #include <stdio.h>#include <iostream>#include <string.h>using namespace std;const int N210;const int inf0x2fffffff;const int Max20000;int match[N],n,m,lack,w[N][N],lx[N],ly[N];bool vx[N],vy[N];bo…

心情不好,我就这样写代码

在 GitHub 上有一个项目&#xff0c;它描述了「最佳垃圾代码」的十九条关键准则。从变量命名到注释编写&#xff0c;这些准则将指导你写出最亮眼的烂代码。为了保持与原 GitHub 项目一致的风格&#xff0c;下文没有进行转换。读者们可以以相反的角度来理解所有观点&#xff0c;…

递归是会更秀strtok

前几天发的字符串反转题目&#xff0c;后面有一个新同学用了递归的方法来实现&#xff0c;看了下&#xff0c;真的是很秀。之前字符串反转的题目代码如下#include "stdio.h" #include "string.h" char input[] {"the sky is blue cris 1212321 apple…

ios开发网络篇—HTTP协议 - 转

一.URL 1.基本介绍 URL的全称是Uniform Resource Locator(统一资源定位符) &#xff0c;通过1个URL&#xff0c;能找到互联网唯一的1个资源 &#xff0c;URL就是资源的地址&#xff0c;位置&#xff0c;互联网上的每个资源都有一个唯一的URL 2.URL中常见的协议 (1)HTTP&#…

总结的一些内存问题

前言之前在实习时&#xff0c;听了 OOM 的分享之后&#xff0c;就对 Linux 内核内存管理充满兴趣&#xff0c;但是这块知识非常庞大&#xff0c;没有一定积累&#xff0c;不敢写下&#xff0c;担心误人子弟&#xff0c;所以经过一个一段时间的积累&#xff0c;对内核内存有一定…

云计算-从基础到应用架构系列-云计算的演进

为什么80%的码农都做不了架构师&#xff1f;>>> 开篇 本篇是主要讲述云计算的发展历程&#xff0c;由于云计算本身提出来也不是太久&#xff0c;并且其实云计算也是经过前人的一些经验总结提出&#xff0c;所以我们对之前的一 些计算机的发展史有个一定的了解&…

这样理解mmap,挺有意思!

大概雍正皇帝怎么也不会想到&#xff0c;自己在西历2022年的男生和女生眼里&#xff0c;会是截然不同的两种形象。1以我对身边同学朋友的观察&#xff0c;男生们大多爱看《雍正王朝》&#xff0c;他们眼中的雍正&#xff0c;大约是个推行了“火耗归公”、“摊丁入亩”等遏制贪腐…

软件开发中的11个系统思维定律

为什么80%的码农都做不了架构师&#xff1f;>>> http://sd.csdn.net/a/20101217/284119.html?1292550154 彼得圣吉在其著作《第五项修炼》中提到的系统思维定律同样适用于软件开发。 1. 今日的问题源于昨日的解决方案&#xff08;Today’s problems come from yes…

为什么我对流程情有独钟?

写这个标题的原因是我有一个同事兼朋友&#xff0c;他的名字刚好和流程谐音&#xff0c;最近他刚离职回苏州工作&#xff0c;在球场下&#xff0c;他是我的良师益友&#xff0c;在球场上&#xff0c;他是我们可以信任的队友&#xff0c;我们不仅一次把比我们高大、速度比我们快…

基于boost asio实现的支持ssl的通用socket框架

情景分析现已存在一个可用稳定的异步客户端类http_client_base&#xff0c;该类基于boost asio实现了连接服务器&#xff0c;发送请求&#xff0c;获取响应和解析http数据等操作&#xff0c;该类的大致实现框架如下1classhttp_client_base 2{ 3public: 4 http_client_ba…

C#创建简单的验证码

首先&#xff0c;创建一个CLASS类&#xff0c;然后需要add Reference的方式添加 System.Drawing&#xff08;画画的类&#xff09; 方法代码如下&#xff1a; 1/**//**//**//// <summary> 2 /// 定义显示的随机字符 3 /// </summary> 4 /// &…

昨天的事情想说一下

发那篇文章的目的昨天发文章之后&#xff0c;我的一个好朋友微信找我&#xff0c;跟我说了很多关于文章的事情&#xff0c;所以&#xff0c;我自己也思考了许多。关于泄愤这个事情&#xff0c;我还是挺想说的。可能很多人看到了一个不好的东西&#xff0c;然后网上发发这个&…

Multidimensional Queries(二进制枚举+线段树+Educational Codeforces Round 56 (Rated for Div. 2))...

题目链接&#xff1a; https://codeforces.com/contest/1093/problem/G 题目&#xff1a; 题意&#xff1a; 在k维空间中有n个点&#xff0c;每次给你两种操作&#xff0c;一种是将某一个点的坐标改为另一个坐标&#xff0c;一种操作是查询[l,r]中曼哈顿距离最大的两个点的最大…

poj 3342

概率dp&#xff0c;不解释。 View Code #include<iostream>#include<map>#include<cstdio>#include<vector>using namespace std;const int maxn201;int dp[210][2];vector<int>edge[maxn];void dfs(int u,int p){int i,j; dp[u][1]1;dp[u][…

最全是一次I2C总结

博主将 I2C spec 文章总结为一篇&#xff0c;目录如下I2C Introduction I2C Architecture I2C Transfer I2C Synchronization And Arbitration I2C Hs-mode1、I2C Introduction1、I2C 历史I2C&#xff1a;Inter-Integrated Circuit&#xff0c;集成电路总线。I2C 是 Philips 公…

Lync Server 2010标准版系列PART6:启用Lync

在我们花费了众多的精力和时间之后&#xff0c;我们终于完成了Lync Server标准版的搭建&#xff0c;接下来当然是为我们AD中的用户启用Lync&#xff0c;来看下我们的部署成果。首先我们需要在AD中创建两个帐户&#xff0c;这样便于我们后期的测试&#xff0c;在DC上打开AD用户和…

8位MCU跑RTOS有没有意义?

相信大多数人在学习单片机的时候&#xff0c;都是从最基本的8位MCU开始的。一般来说&#xff0c;8位单片机最常见的是三个系列是&#xff1a;51系列、AVR系列、PIC系列。而前段时间&#xff0c;群里讨论了一个问题&#xff1a;在51单片机上跑RTOS有没有意义&#xff1f;关于这个…

ViewState机制由浅入深1

1 ViewState机制是什么&#xff1f; ViewState机制是asp.net中对同一个Page的多次请求&#xff08;PostBack&#xff09;之间维持Page及控件状态的一种机制。在WebForm中每次请求完&#xff0c;Page对象都会被释放&#xff0c;对同一个Page的多次请求之间的状态信息&am…