389 lines
14 KiB
XML
Executable File
389 lines
14 KiB
XML
Executable File
<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
<!--
|
|
|
|
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
|
|
|
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
|
|
|
This program is free software; you can redistribute it and/or modify it under the
|
|
terms of the GNU Lesser General Public License as published by the Free Software
|
|
Foundation; either version 3.0 of the License, or (at your option) any later
|
|
version.
|
|
|
|
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License along
|
|
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
-->
|
|
|
|
<mx:Application xmlns:mx="library://ns.adobe.com/flex/mx"
|
|
xmlns:fx="http://ns.adobe.com/mxml/2009"
|
|
creationComplete="onCreationComplete()"
|
|
pageTitle="WebcamView" layout="absolute">
|
|
<fx:Script>
|
|
<![CDATA[
|
|
import flash.filters.ConvolutionFilter;
|
|
import flash.net.NetConnection;
|
|
|
|
import mx.core.UIComponent;
|
|
import mx.utils.URLUtil;
|
|
|
|
private var _video:Video;
|
|
private var _videoHolder:UIComponent;
|
|
private var ns:NetStream;
|
|
private var nc:NetConnection;
|
|
private var aspectRatio:Number = 1;
|
|
private var camWidth:Number = 320;
|
|
private var camHeight:Number = 240;
|
|
|
|
private var _url:String;
|
|
private var _stream:String;
|
|
private var _avatarURL:String;
|
|
private var displayAvatar:Boolean = false;
|
|
|
|
private var smoothVideo:Boolean = true;
|
|
private var applyConvolutionFilter:Boolean = false;
|
|
private var convolutionFilter:Array = [-1, 0, -1, 0, 6, 0, -1, 0, -1];
|
|
private var filterBias:Number = 0;
|
|
private var filterDivisor:Number = 4;
|
|
private var enableH264:Boolean = false;
|
|
private var h264Level:String = "2.1";
|
|
private var h264Profile:String = "main";
|
|
|
|
private function onCreationComplete():void{
|
|
_videoHolder = new UIComponent();
|
|
_videoHolder.width = this.width;
|
|
_videoHolder.height = this.height;
|
|
|
|
trace("WebcamViewSA:: onCreationComplete [" + this.width + "," + this.height + "]");
|
|
this.addChild(_videoHolder);
|
|
|
|
_videoHolder.addChild(loader);
|
|
|
|
Security.allowDomain(determineHTMLURL());
|
|
trace("WebcamViewSA:: Security.allowDomain(" + determineHTMLURL() + ");");
|
|
|
|
initExternalInterface();
|
|
callWebcamViewStandaloneReady();
|
|
}
|
|
|
|
private function determineHTMLURL():String {
|
|
var serverName:String = "*";
|
|
if(ExternalInterface.available) {
|
|
try {
|
|
var htmlURL:String = String(ExternalInterface.call("window.location.href.toString"));
|
|
serverName = URLUtil.getServerName(htmlURL);
|
|
trace("WebcamViewSA::determineHTMLURL HTML URL [" + htmlURL + "]");
|
|
} catch(s:Error) {
|
|
trace("WebcamViewSA::determineHTMLURL Cannot determine HTML URL");
|
|
}
|
|
}
|
|
|
|
return serverName;
|
|
}
|
|
|
|
private function initExternalInterface():void {
|
|
trace('WebcamViewSA::initExternalInterface');
|
|
if (ExternalInterface.available) {
|
|
ExternalInterface.addCallback("startViewCameraStream", handleStartViewCameraRequest);
|
|
ExternalInterface.addCallback("stopViewCameraStream", handleStopViewCamera);
|
|
}
|
|
}
|
|
|
|
private function callWebcamViewStandaloneReady():void {
|
|
if (ExternalInterface.available) {
|
|
ExternalInterface.call("webcamViewStandaloneAppReady");
|
|
}
|
|
}
|
|
|
|
private function connect(url:String):void {
|
|
nc = new NetConnection();
|
|
nc.proxyType = "best";
|
|
nc.client = this;
|
|
nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
|
|
nc.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
|
|
nc.addEventListener(NetStatusEvent.NET_STATUS, onNetConnectStatus);
|
|
nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
|
|
nc.connect(url);
|
|
}
|
|
|
|
public function onBWCheck(... rest):Number {
|
|
return 0;
|
|
}
|
|
|
|
public function onBWDone(... rest):void {
|
|
var p_bw:Number;
|
|
if (rest.length > 0) p_bw = rest[0];
|
|
// your application should do something here
|
|
// when the bandwidth check is complete
|
|
trace("WebcamViewSA:: bandwidth = " + p_bw + " Kbps.");
|
|
}
|
|
|
|
private function getVideoResolution(stream:String):void {
|
|
var pattern:RegExp = new RegExp("(\\d+x\\d+)-([A-Za-z0-9]+)-\\d+", "");
|
|
if (pattern.test(stream)) {
|
|
trace("WebcamViewSA:: The stream name is well formatted [" + stream + "]");
|
|
trace("WebcamViewSA:: Stream resolution is [" + pattern.exec(stream)[1] + "] Userid [" + pattern.exec(stream)[2] + "]");
|
|
var res:Array = pattern.exec(stream)[1].split("x");
|
|
camWidth = Number(res[0]);
|
|
camHeight = Number(res[1]);
|
|
} else {
|
|
camWidth = 320;
|
|
camHeight = 240;
|
|
}
|
|
}
|
|
|
|
|
|
private function onIOError(event:NetStatusEvent):void{
|
|
}
|
|
|
|
private function onNetConnectStatus(event:NetStatusEvent):void{
|
|
switch(event.info.code){
|
|
case "NetConnection.Connect.Success":
|
|
playWebcamStream();
|
|
break;
|
|
default:
|
|
trace("WebcamViewSA:: [" + event.info.code + "] for [" + _url + "]");
|
|
break;
|
|
}
|
|
}
|
|
|
|
private function onSecurityError(event:NetStatusEvent):void{
|
|
}
|
|
|
|
private function handleStartViewCameraRequest(url:String, stream:String, avatarURL:String):void {
|
|
displayAvatar = false;
|
|
_avatarURL = avatarURL;
|
|
_url = url;
|
|
_stream = stream;
|
|
trace("WebcamViewSA::handleStartViewCameraRequest avatar=[" + _avatarURL + "]");
|
|
|
|
connect(url);
|
|
}
|
|
|
|
private function handleStopViewCamera(avatarURL:String):void {
|
|
|
|
_avatarURL = avatarURL;
|
|
trace("WebcamViewSA::handleStopViewCamera avatar=[" + _avatarURL + "]");
|
|
|
|
stopViewing();
|
|
|
|
}
|
|
|
|
private var loader:Loader = new Loader();
|
|
private var request:URLRequest;
|
|
|
|
private function loadAvatar():void {
|
|
request = new URLRequest(_avatarURL);
|
|
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadingComplete);
|
|
loader.load(request);
|
|
}
|
|
|
|
private var avatarWidth:Number = 320;
|
|
private var avatarHeight:Number = 240;
|
|
|
|
private function onLoadingComplete(event:Event):void {
|
|
|
|
if (!displayAvatar) {
|
|
// The user might have started sharing webcam again.
|
|
return;
|
|
}
|
|
|
|
if (_video != null) _videoHolder.removeChild(_video);
|
|
_videoHolder.addChild(loader);
|
|
|
|
// Save the size of the avatar image.
|
|
avatarWidth = loader.content.width;
|
|
avatarHeight = loader.content.height;
|
|
|
|
onResizeAvatar();
|
|
}
|
|
|
|
private function onResizeAvatar():void {
|
|
|
|
if (loader != null && _videoHolder != null) {
|
|
if (avatarIsSmallerThanWindow()) {
|
|
// The avatar image is smaller than the window. Just center the image.
|
|
_videoHolder.width = loader.width = avatarWidth;
|
|
_videoHolder.height = loader.height = avatarHeight;
|
|
|
|
} else {
|
|
// The avatar is bigger than the window. Fit into the window maintaining aspect ratio.
|
|
fitAvatarToWindow();
|
|
}
|
|
|
|
_videoHolder.x = (this.width - loader.width) / 2;
|
|
_videoHolder.y = (this.height - loader.height) / 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private function fitAvatarToWindow():void {
|
|
if (_videoHolder.width < _videoHolder.height) {
|
|
fitAvatarToHeightAndAdjustWidthToMaintainAspectRatio();
|
|
} else {
|
|
fitAvatarToWidthAndAdjustHeightToMaintainAspectRatio();
|
|
}
|
|
}
|
|
|
|
private function avatarIsSmallerThanWindow():Boolean {
|
|
return (avatarWidth < _videoHolder.width) && (avatarHeight < _videoHolder.height);
|
|
}
|
|
|
|
private function fitAvatarToWidthAndAdjustHeightToMaintainAspectRatio():void {
|
|
var aspect:Number = avatarHeight / avatarWidth;
|
|
loader.width = _videoHolder.width = this.width;
|
|
|
|
// Maintain aspect-ratio
|
|
_videoHolder.height= loader.height = loader.width * aspect;
|
|
}
|
|
|
|
private function resetVideoHolder():void {
|
|
_videoHolder.width = this.width;
|
|
_videoHolder.height = this.height;
|
|
_videoHolder.x = _videoHolder.y = 0;
|
|
}
|
|
|
|
private function fitAvatarToHeightAndAdjustWidthToMaintainAspectRatio():void {
|
|
var aspect:Number = avatarWidth / avatarHeight;
|
|
loader.height = _videoHolder.height = this.height;
|
|
|
|
// Maintain aspect-ratio
|
|
_videoHolder.width = loader.width = loader.height * aspect;
|
|
}
|
|
|
|
private function stopViewing():void {
|
|
|
|
resetVideoHolder();
|
|
|
|
if (_video != null) {
|
|
trace("WebcamViewSA:: removing video from holder");
|
|
_videoHolder.removeChild(_video);
|
|
_video.clear();
|
|
_video = null;
|
|
}
|
|
|
|
trace("WebcamViewSA:: closing stream");
|
|
if (ns != null) {
|
|
ns.close();
|
|
}
|
|
|
|
displayAvatar = true;
|
|
loadAvatar();
|
|
}
|
|
|
|
private function playWebcamStream():void {
|
|
ns = new NetStream(nc);
|
|
ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
|
|
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
|
|
ns.client = this;
|
|
ns.bufferTime = 0;
|
|
ns.receiveVideo(true);
|
|
ns.receiveAudio(false);
|
|
|
|
getVideoResolution(_stream);
|
|
|
|
trace("WebcamViewSA:: WINDOW [" + this.width + "," + this.height + "]");
|
|
|
|
resetVideoHolder();
|
|
|
|
_video = new Video(camWidth, camHeight);
|
|
_video.width = camWidth;
|
|
_video.height = camHeight;
|
|
trace("WebcamViewSA:: resolution [" + _video.width + "," + _video.height + "]");
|
|
|
|
onResize();
|
|
|
|
_video.attachNetStream(ns);
|
|
|
|
_video.smoothing = smoothVideo;
|
|
|
|
if (applyConvolutionFilter) {
|
|
var filter:ConvolutionFilter = new flash.filters.ConvolutionFilter();
|
|
filter.matrixX = 3;
|
|
filter.matrixY = 3;
|
|
filter.matrix = convolutionFilter;
|
|
filter.bias = filterBias;
|
|
filter.divisor = filterDivisor;
|
|
_video.filters = [filter];
|
|
}
|
|
|
|
trace("WebcamViewSA:: adding video to video holder");
|
|
_videoHolder.removeChild(loader);
|
|
_videoHolder.addChild(_video);
|
|
|
|
_videoHolder.visible = true;
|
|
ns.play(_stream);
|
|
}
|
|
|
|
private function onAsyncError(e:AsyncErrorEvent):void{
|
|
trace("WebcamViewSA::asyncerror " + e.toString());
|
|
}
|
|
|
|
public function onMetaData(info:Object):void{
|
|
trace("WebcamViewSA:: metadata: width=" + info.width + " height=" + info.height);
|
|
}
|
|
|
|
private function onNetStatus(e:NetStatusEvent):void{
|
|
switch(e.info.code){
|
|
case "NetStream.Publish.Start":
|
|
trace("WebcamViewSA:: NetStream.Publish.Start");
|
|
break;
|
|
case "NetStream.Play.UnpublishNotify":
|
|
trace("WebcamViewSA:: NetStream.Play.UnpublishNotify");
|
|
stopViewing();
|
|
break;
|
|
case "NetStream.Play.Start":
|
|
trace("WebcamViewSA:: Netstatus: " + e.info.code);
|
|
break;
|
|
case "NetStream.Play.FileStructureInvalid":
|
|
trace("WebcamViewSA:: The MP4's file structure is invalid.");
|
|
break;
|
|
case "NetStream.Play.NoSupportedTrackFound":
|
|
trace("WebcamViewSA:: The MP4 doesn't contain any supported tracks");
|
|
break;
|
|
}
|
|
}
|
|
|
|
private function onResize():void {
|
|
if (_videoHolder != null) {
|
|
fitVideoToWindow();
|
|
_video.x = (this.width - _video.width) / 2;
|
|
_video.y = (this.height - _video.height) / 2;
|
|
}
|
|
}
|
|
|
|
private function fitVideoToWindow():void {
|
|
if (_videoHolder.width < _videoHolder.height) {
|
|
fitToHeightAndAdjustWidthToMaintainAspectRatio();
|
|
} else {
|
|
fitToWidthAndAdjustHeightToMaintainAspectRatio();
|
|
}
|
|
}
|
|
|
|
private function fitToWidthAndAdjustHeightToMaintainAspectRatio():void {
|
|
var aspect:Number = camHeight / camWidth;
|
|
_video.width = _videoHolder.width;
|
|
|
|
// Maintain aspect-ratio
|
|
_video.height = _video.width * aspect;
|
|
}
|
|
|
|
private function fitToHeightAndAdjustWidthToMaintainAspectRatio():void {
|
|
var aspect:Number = camWidth / camHeight;
|
|
_video.height = _videoHolder.height;
|
|
|
|
// Maintain aspect-ratio
|
|
_video.width = _video.height * aspect;
|
|
}
|
|
|
|
]]>
|
|
</fx:Script>
|
|
|
|
</mx:Application>
|