commit d98eab59a3e2994d66a60a1de0e8290f835db3bb Author: Tom Moor Date: Tue Feb 19 16:14:41 2013 -0800 Initial commit, ongoing refactoring and audio channels diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..496ee2c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git a/p2p.fla b/p2p.fla new file mode 100644 index 0000000..a43dd75 Binary files /dev/null and b/p2p.fla differ diff --git a/p2p.swf b/p2p.swf new file mode 100644 index 0000000..6f3cdf9 Binary files /dev/null and b/p2p.swf differ diff --git a/sqwiggle/Member.as b/sqwiggle/Member.as new file mode 100644 index 0000000..c7354b9 --- /dev/null +++ b/sqwiggle/Member.as @@ -0,0 +1 @@ +package sqwiggle { import flash.display.Sprite; import flash.events.NetStatusEvent; import flash.media.Video; import flash.media.SoundChannel; import flash.net.NetConnection; import flash.net.NetStream; import flash.display.Shape; import flash.display.MovieClip; import flash.events.MouseEvent; import flash.utils.Timer; public class Member extends Sprite { public var connection :NetConnection; public var userId :String; public var peerId :String; var stream:NetStream; var video :Video; var rectangle:Shape; var bar:MovieClip; var t:MovieClip; public function Member(userId:String, peerId:String) { this.userId = userId; this.peerId = peerId; rectangle = new Shape; // initializing the variable named rectangle rectangle.graphics.beginFill(0xFF0000); // choosing the colour for the fill, here it is red rectangle.graphics.drawRect(0, 0, 320,240); // (x spacing, y spacing, width, height) rectangle.graphics.endFill(); // not always needed but I like to put it in to end the fill video = new Video(); video.width = 291; video.height = 218; addChild(video); bar = new fuckyou(); bar.y = video.height; bar.visible=false; addChild(bar); t = new talk(); t.x = 4; t.buttonMode=true; t.useHandCursor=true; t.y = video.height - 4; addChild(t); t.visible=false; addEventListener(MouseEvent.MOUSE_OVER, mover); addEventListener(MouseEvent.MOUSE_OUT, mout); } public function connectToVideo(videoConnection:NetConnection):void { //trace('Connecting to id: ' + id + ', stream: ' + peerId); stream = new NetStream(videoConnection, peerId); stream.addEventListener(NetStatusEvent.NET_STATUS, onVideoStatus); stream.play(this.userId); video.attachNetStream(stream); } public function connectToAudio(audioConnection:NetConnection, peerId:String):void { //trace('Connecting to id: ' + id + ', stream: ' + peerId); var astream = new NetStream(audioConnection, peerId); astream.addEventListener(NetStatusEvent.NET_STATUS, onVideoStatus); astream.play('audio-' + this.userId); // todo: does this need a separate object? video.attachNetStream(astream); } public function onVideoStatus(e:NetStatusEvent):void { //trace('Member ' + peerId + ': ' + e.info.code); } public function remove():void { removeChild(video); stream.close(); //removeChild(rectangle); } function mover(e:*):void { bar.visible = true; t.visible = true; } function mout(e:*):void { bar.visible = false; t.visible = false; } } } \ No newline at end of file diff --git a/sqwiggle/Self.as b/sqwiggle/Self.as new file mode 100644 index 0000000..b667182 --- /dev/null +++ b/sqwiggle/Self.as @@ -0,0 +1 @@ +package sqwiggle { import flash.events.NetStatusEvent; import flash.media.Camera; import flash.net.NetConnection; import flash.net.NetStream; import sqwiggle.Member; import flash.media.Microphone; import flash.events.MouseEvent; import fl.motion.MotionEvent; public class Self extends Member { var cam:Camera; public function Self(userId:String, peerId:String) { super(userId, peerId); cam = Camera.getCamera(); cam.setQuality(0, 100); } public override function connectToAudio(audioConnection:NetConnection, peerId:String):void { var stream = new NetStream(audioConnection, NetStream.DIRECT_CONNECTIONS); stream.addEventListener(NetStatusEvent.NET_STATUS, onAudioStatus); stream.attachAudio(Microphone.getMicrophone()); stream.publish('audio-' + this.userId); } public function onAudioStatus(e:NetStatusEvent):void { // nothing here just yet } public override function connectToVideo(videoConnection:NetConnection):void { //trace('Publishing to id ' + id + ', stream: ' + peerId); var stream = new NetStream(videoConnection, NetStream.DIRECT_CONNECTIONS); stream.addEventListener(NetStatusEvent.NET_STATUS, onVideoStatus); stream.attachCamera(this.cam); stream.publish(this.userId); var client:Object = new Object(); client.onPeerConnect = function(caller:NetStream):Boolean { trace('Callee connecting to stream: ' + caller.farID); return true; }; stream.client = client; video.attachCamera(this.cam); video.smoothing = true; video.deblocking = 5; } } } \ No newline at end of file diff --git a/sqwiggle/p2p.as b/sqwiggle/p2p.as new file mode 100644 index 0000000..866f0d0 --- /dev/null +++ b/sqwiggle/p2p.as @@ -0,0 +1 @@ +package sqwiggle { import flash.display.LoaderInfo; import flash.display.Sprite; import flash.display.StageAlign; import flash.events.NetStatusEvent; import flash.external.ExternalInterface; import flash.net.NetConnection; import flash.events.MouseEvent; import sqwiggle.Self; import sqwiggle.Member; public class p2p extends Sprite { var flashvars :Object; var videoConnection :NetConnection; var audioConnection :NetConnection; var self :Self; var members :Array; var conversation :Array; var conversationInitiator :Boolean; public function p2p() { flashvars = LoaderInfo(this.root.loaderInfo).parameters; members = new Array(); conversation = new Array(); stage.align = StageAlign.TOP; ExternalInterface.addCallback('addMembers', addMembers); ExternalInterface.addCallback('addMember', addMember); ExternalInterface.addCallback('removeMember', removeMember); ExternalInterface.addCallback('updateAudioChannel', updateAudioChannel); videoConnection = new NetConnection(); videoConnection.addEventListener(NetStatusEvent.NET_STATUS, onVideoStatus); videoConnection.connect('rtmfp://p2p.rtmfp.net', '56ec6665a4c675c72fb85db7-b6153286fa1e'); var client:Object = new Object(); client.onRelay = function(id:String):void { trace('onRelay: ' + id); } videoConnection.client = client; } private function onVideoStatus(e:NetStatusEvent):void { switch (e.info.code) { case 'NetConnection.Connect.Success': self = new Self(flashvars.self_id, videoConnection.nearID); self.connectToVideo(videoConnection); addChild(self); trace('adding self (`id` => ' + flashvars.self_id + ', `peerId` => ' + videoConnection.nearID) ExternalInterface.call('update_peer_id', flashvars.self_id, videoConnection.nearID); break; } } public function addMembers(strIds:String, strPeerIds:String):void { trace(strIds, strPeerIds); var ids :Array = strIds .split(','); var peerIds:Array = strPeerIds.split(','); for (var i = 0; i < peerIds.length; i++) { if (peerIds[i] != videoConnection.nearID) addMember(ids[i], peerIds[i]); } } public function updateAudioChannel(strIds:String, strPeerIds:String):void { trace(strIds, strPeerIds); var ids :Array = strIds .split(','); var peerIds:Array = strPeerIds.split(','); if (audioConnection) { // TODO: handle this } else { audioConnection = new NetConnection(); audioConnection.addEventListener(NetStatusEvent.NET_STATUS, onAudioStatus); audioConnection.connect('rtmfp://p2p.rtmfp.net', '56ec6665a4c675c72fb85db7-b6153286fa1e'); } // for all the peers in the conversation for (var i = 0; i < peerIds.length; i++) { // check that it isn't ourself if (peerIds[i] != audioConnection.nearID) { // for all members in the company for (var j in members) { // if this member should be in the conversation if (members[j].peerId == peerIds[i]) { // make sure they are // TODO: what happens if they already are connected? members[j].connectToAudio(audioConnection, peerIds[i]); break; } } } } } public function addMember(memberId:String, peerId:String):void { // prevent multiple connections if the user opens another tab if (self.userId === memberId) return; trace('new member (`id` => ' + memberId + ', `peerId` => ' + peerId) var member:Member = new Member(memberId, peerId); member.connectToVideo(videoConnection); member.addEventListener(MouseEvent.CLICK, createAudioChannel); addChild(member); members.push(member); //trace(members.length + ' .. x: ' + 320*((members.length) % 3) + ' y: ' + 240*(Math.ceil((members.length) / 3))); member.x = member.width * (members.length % 3); member.y = member.height * (Math.ceil((members.length+1) / 3)-1); trace('pos: ' + member.x + ', ' + member.y + ', members: ' + members.length); } public function removeMember(id:String) { trace('member left (`id` => ' + id + ')'); for (var i = 0; i < members.length; i++) { var member = members[i]; if (member.userId === id) { member.remove(); removeChild(member); members.splice(i, 1); break; } } for (i = 0; i < members.length; i++) { member = members[i]; member.x = member.width * ((i+1) % 3); member.y = member.height * (Math.ceil((i+2) / 3)-1); trace('repos: ' + member.x + ', ' + member.y + ', members: ' + members.length); } } private function createAudioChannel(event:MouseEvent):void { // already existing connection if (audioConnection) { // TODO: make sure that this connection is to the correct people // TODO: ensure mic is unmuted // no connection available, were creating a new audio chat } else { conversationInitiator = true; // create a new audio channel audioConnection = new NetConnection(); audioConnection.addEventListener(NetStatusEvent.NET_STATUS, onAudioStatus); audioConnection.connect('rtmfp://p2p.rtmfp.net', '56ec6665a4c675c72fb85db7-b6153286fa1e'); // add this user to the conversation conversation.push(event.target); } } private function onAudioStatus(e:NetStatusEvent):void { switch (e.info.code) { case 'NetConnection.Connect.Success': // connect to the stream ourselves self.connectToAudio(audioConnection, audioConnection.nearID); if (conversationInitiator) { // and add people in conversation to the stream sendAudioRequests(); } break; } } private function sendAudioRequests():void { var userIds:Array = new Array(); var peerIds:Array = new Array(); for (var i in conversation) { userIds.push(conversation[i].userId); peerIds.push(conversation[i].peerId); } for (var j in conversation) { if (conversation[j].userId === self.userId) return; ExternalInterface.call('sendAudioRequest', conversation[j].userId, userIds.join(','), peerIds.join(',')); } } } } \ No newline at end of file