らいぶ配信びゅあー

2月はあっという間に過ぎますね。この分じゃ4月ももうすぐだ。



なんか更新するネタはないかっと探すと、いつやったかも忘れましたが、ライブ配信用のビュアーにコメント(というかチャット)機能がついていました。

あとは、サーバーからクライアント側のメソッドの呼び出しが出来れば、ひとまず基本的なことは出来そうな感じです。

なんの役に立つか定かじゃないですが、ソースでも晒しておきますか


クライアント側[txt]
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()" height="796">
 
<mx:Script>
[CDATA[
 
private var nc:NetConnection;
private var ns:NetStream;
private var cam:Camera;
private var mic:Microphone;
private var chatSO:SharedObject;
 
private var secTimer:Timer;
private var fsecTimer:Timer;
 
private var startTime:int;
private var userId:String;
private var hasNewComment:Boolean = false;
 
private function init():void{
this.setSystemMessage("サーバに接続...",false);
this.nc = new NetConnection();
this.nc.objectEncoding = ObjectEncoding.AMF0;
this.nc.client = new CustomClient();
this.nc.connect("rtmp://localhost/liveCast");
this.nc.addEventListener(NetStatusEvent.NET_STATUS,onNetStatus);
this.nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR,onSecurityError);
this.nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
}
 
private function onNetStatus(evt:NetStatusEvent):void {
switch(evt.info.code) {
case "NetConnection.Connect.Success":
setSystemMessage("成功");
this.connected();
break;
case "NetConnection.Connect.Closed":
setSystemMessage("サーバへの接続が切断されました");
break;
case "NetConnection.Connect.Failed":
setSystemMessage("サーバ接続失敗しました");
break;
case "NetConnection.Connect.Rejected":
setSystemMessage("サーバ接続を拒否られました")
break;
default:
}
}
 
private function onSecurityError(evt:SecurityErrorEvent):void {
trace("Security Error");
}
private function onAsyncError(evt:AsyncErrorEvent):void{
trace("Async Error");
}
 
//接続後の初期設定
private function connected():void{
 
//動画設定
this.setupCameraMic();
this.setupSO();
 
//経過時間更新
this.setupStartTime();
this.secTimer = new Timer(1000);
this.secTimer.addEventListener(TimerEvent.TIMER,setPassage);
this.secTimer.start();
 
//接続数取得
this.setConnectingNumber();
this.fsecTimer = new Timer(5000);
this.fsecTimer.addEventListener(TimerEvent.TIMER,setConnectingNumber);
this.fsecTimer.start();
 
//接続者ID設定
this.setUserId();
}
 
//Video作成
private function setupCameraMic():void{
ns = new NetStream(nc);
ns.client = new CustomClient();
ns.soundTransform = new SoundTransform(0.7);
var video:Video = new Video();
video.width = 640;
video.height = 480;
video.attachNetStream(ns);
ns.play("liveCastVideo",-1);
video_display.addChild(video);
}
//共有オブジェクト作成
private function setupSO():void{
//チャットオブジェクト
chatSO = SharedObject.getRemote("chat",nc.uri);
if(chatSO){
chatSO.addEventListener(SyncEvent.SYNC,onChatSoStatus);
chatSO.connect(nc);
}
}
//チャット用共有オブジェクト更新時の処理
private function onChatSoStatus(evt:SyncEvent):void{
if(chatSO.data.chatMessage != undefined){
comment_list.text = chatSO.data.chatMessage + "\n";
comment_input.text = "";
this.hasNewComment = true;
 
//コメント数
var cn:int = -1;
var ch:String;
var str:String = this.comment_list.text;
for (var i:int = 0; i < str.length; i++) {
ch = str.substr(i, 1);
if (ch == "\n") cn++;
}
this.comment_number.text = cn.toString();
}
}
 
//放送開始時間を取得
private function setupStartTime():void {
nc.call('getStartTime', new Responder(_onResultTime,_onStatus));
}
//放送時間をサーバから受け取る
private function _onResultTime(result:Number):void {
var d:Date;
d = new Date();
d.setTime(result);
this.startTime = d.getTime();
var s:String;
s = d.getHours()+":"+d.getMinutes();
this.setSystemMessage("放送開始時刻取得:"+s);
}
 
//経過時間表示用
private function setPassage(event:TimerEvent):void {
var ntime:int = new Date().time;
var ptime:int = ntime - this.startTime;
var pdate:Date = new Date(ptime);
var re:String = (pdate.hoursUTC).toString() + ":" +
(pdate.minutesUTC+100).toString().substring(1) + ":" +
(pdate.secondsUTC+100).toString().substring(1);
this.passage.text = re;
}
//接続数取得用
private function setConnectingNumber(event:TimerEvent=null):void {
nc.call('getConnectNumber', new Responder(_onConNumResult,_onStatus));
 
}
private function _onConNumResult(result:String):void {
this.connect_number.text = result;
//trace("Call : Success");
}
 
private function _onResult(result:String):void {
trace("Call : Success");
}
private function _onStatus(result:Object):void{
trace("Call : Failed");
}
 
//接続者ID設定用
private function setUserId():void{
this.setSystemMessage("接続者ID設定中...");
nc.call('makeUserId', new Responder(_onUserResult,_onStatus));
}
private function _onUserResult(result:String):void {
this.userId = result;
this.user_id.text = this.userId;
this.setSystemMessage("接続者ID設定完了");
}
 
//システムメッセージ(textarea)に表示
public function setSystemMessage(msg:String,autoBr:Boolean=true):void {
trace(msg);
if(autoBr == true)
this.system_textarea.text += msg + "\n";
else
this.system_textarea.text += msg;
 
system_textarea.verticalScrollPosition = system_textarea.maxHorizontalScrollPosition;
 
}
 
/* ----------------------------------------------------------------------------------------
*
* ---------------------------------------------------------------------------------------- */

 
//音声ボタン(CheckBox)を押したときの処理
private function volumeCheckHandler():void {
if(this.volume_check.selected){
ns.soundTransform = new SoundTransform(this.volume_slider.value);
setSystemMessage("音声有効");
}else{
ns.soundTransform = new SoundTransform(0);
setSystemMessage("音声無効");
}
}
//音声スライダーを動かした時の処理
private function volumeSliderHandler():void {
ns.soundTransform = new SoundTransform(this.volume_slider.value);
trace(ns.soundTransform.volume);
}
//停止ボタンを押したときの処理
private function stopButtonHandler():void {
this.secTimer.removeEventListener(TimerEvent.TIMER,this.setPassage);
this.fsecTimer.removeEventListener(TimerEvent.TIMER,this.setConnectingNumber);
this.ns.close();
this.nc.close();
}
//更新ボタンを押したときの処理
private function updateButtonHandler():void {
this.stopButtonHandler();
this.init();
}
//コメントボタンを押した時の処理
private function commentButtonHandler():void{
if(comment_input.text != ""){
if(this.chatSO.data.chatMessage == undefined)
this.chatSO.setProperty("chatMessage"," ");
var time:String = "["+this.passage.text+"] ";
var line:String = time+this.userId+" : "+this.comment_input.text;
var all:String = this.chatSO.data.chatMessage +"\n"+ line;
this.chatSO.setProperty("chatMessage",all);
}
}
//コメントが更新されたときに呼び出せる処理
private function commentUpdateHandler():void{
if(this.hasNewComment == true){
this.comment_list.verticalScrollPosition = this.comment_list.maxVerticalScrollPosition;
this.hasNewComment = false;
}
}
 
 
 
 
]]>
mx:Script>
<mx:Canvas y="42" width="680" height="700" cornerRadius="10" backgroundColor="#FFFFFF" horizontalCenter="0">
<mx:Canvas width="660" height="680" backgroundColor="#E8E8E8" cornerRadius="10" borderColor="#B7BABC" borderStyle="solid" borderThickness="1" horizontalCenter="0" verticalCenter="0">
<mx:VideoDisplay width="640" height="480" borderColor="#194360" cornerRadius="0" horizontalCenter="0" y="9" id="video_display" borderStyle="solid" borderThickness="2"/>
<mx:Button x="588" y="646" label="コメント" id="comment_button" click="commentButtonHandler()"/>
<mx:TextInput x="220" y="646" width="365" id="comment_input" enter="commentButtonHandler()"/>
<mx:TabNavigator x="10" y="497" width="202" height="171" cornerRadius="5" fontSize="10" creationPolicy="all">
<mx:Canvas label="メイン" width="100%" height="100%" backgroundColor="#FFFFFF">
<mx:Button x="140" y="10" label="更新" id="update_button" click="updateButtonHandler()"/>
<mx:Button x="140" y="38" label="停止" id="stop_button" click="stopButtonHandler()"/>
<mx:Label text="経過時間" fontSize="12" x="10" y="10"/>
<mx:Label text="接続数" fontSize="12" x="10" y="29"/>
<mx:Label text="コメント数" fontSize="12" x="10" y="47"/>
<mx:Text text="00:00" fontSize="12" x="72" y="10" id="passage"/>
<mx:Text text="0" fontSize="12" x="72" y="29" id="connect_number"/>
<mx:Text text="0" fontSize="12" x="72" y="47" id="comment_number"/>
<mx:Label x="9" y="87" text="IDを変更(他と重複はできません)"/>
<mx:Button x="140" y="103" label="変更" id="name_button" enabled="false"/>
<mx:TextInput x="10" y="103" width="122" id="name_input" enabled="false" text="not yet"/>
<mx:Text x="10" y="68" text="接続者ID:" fontSize="12"/>
<mx:Text x="72" y="68" text="ID00" fontSize="12" width="77" id="user_id"/>
mx:Canvas>
<mx:Canvas label="音声" width="100%" height="100%" backgroundColor="#FFFFFF">
<mx:CheckBox x="10" y="10" label="配信音声" fontSize="12" id="volume_check" selected="true" click="volumeCheckHandler()"/>
<mx:CheckBox x="10" y="37" label="BGM" fontSize="12" enabled="false"/>
<mx:HSlider x="91" y="10" width="99" height="24" change="volumeSliderHandler()" id="volume_slider" minimum="0" maximum="1" value="0.7"/>
<mx:HSlider x="91" y="37" width="99" height="24" enabled="false"/>
<mx:Button x="142" y="70" label="選択" id="bgm_select_button" enabled="false"/>
<mx:Button x="142" y="100" label="削除" id="bgm_remove_button" enabled="false"/>
<mx:List x="10" y="69" width="124" height="59" id="bgm_list" enabled="false">mx:List>
mx:Canvas>
<mx:Canvas label="システム" width="100%" height="100%" verticalScrollPolicy="off" horizontalScrollPolicy="off">
 
<mx:Label x="5" y="1" text="システムメッセージ"/>
<mx:TextArea x="5" y="15" width="191" height="118" id="system_textarea" selectable="true" editable="false"/>
mx:Canvas>
mx:TabNavigator>
<mx:TextArea x="220" y="497" width="428" height="141" id="comment_list" editable="false" wordWrap="false" updateComplete="commentUpdateHandler()"/>
mx:Canvas>
mx:Canvas>
mx:Application>
 

ところどころインデントとか<,>(不等号)とかがおかしいですね。手直しとか面倒すぎるので勘弁^^

特にナンセンスなのが、接続数を5秒ごとにサーバに問い合わせているところ。こんなのは、接続、切断をサーバで感知してクライアントメソッドを呼び出せばよいのですが、クライアントメソッドの呼び出しがうまくいかなかったようです。あじゃばー。

配信者用[txt]
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
creationComplete="init()"
backgroundAlpha="1" backgroundColor="0xffffff">
<mx:Script>
[CDATA[
 
private var nc:NetConnection;
private var ns:NetStream;
private var cam:Camera;
private var mic:Microphone;
 
public var so:SharedObject;
 
private function init():void{
nc = new NetConnection();
nc.client = new CustomClient();
nc.objectEncoding = ObjectEncoding.AMF0;
nc.connect("rtmp://localhost/liveCast");
nc.addEventListener(NetStatusEvent.NET_STATUS,onNetStatus);
nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR,onSecurityError);
 
}
public function test():void {
trace("test成功");
}
private function onNetStatus(evt:NetStatusEvent):void {
switch(evt.info.code) {
case "NetConnection.Connect.Success":
ns = new NetStream( nc );
setupCameraMic();
trace("Success");
break;
case "NetConnection.Connect.Closed":
trace("Closed");
break;
case "NetConnection.Connect.Failed":
trace("Failed");
break;
case "NetConnection.Connect.Rejected":
trace("Rejected");
break;
default:
}
}
private function onSecurityError(evt:SecurityErrorEvent):void {
trace("Security Error");
}
 
private function setupCameraMic():void{
cam = Camera.getCamera();
cam.setMode(640,480,15);
cam.setQuality(0,90);
mic = Microphone.getMicrophone();
mic.rate = 44;
if(cam != null) {
videoContainer.attachCamera(cam);
}
}
 
private function setupStartTime():void {
/*
// 共有オブジェクト使えないいぃぃぃ
var so:SharedObject;
so = SharedObject.getRemote("start",nc.uri);
var d:Date = new Date();
so.setProperty("startTime",d.time);
so.connect(nc);
trace("StartTime:"+so.data.startTime);
*/

 
nc.call('startCast', new Responder(_onResult,_onStatus));
 
}
private function _onResult(result:String):void {
trace("Call:Success " + result);
}
private function _onStatus(result:Object):void{
trace("Call:Failed "+ result.code);
}
 
 
private function broadcastClick():void{
ns.attachCamera(cam);
ns.attachAudio(mic);
ns.publish( "liveCastVideo" , "live" );
 
setupStartTime();
 
playButton.enabled = false;
stopButton.enabled = true;
}
 
private function stopClick():void{
ns.close();
playButton.enabled = true;
stopButton.enabled = false;
}
 
 
 
]]>
mx:Script>
<mx:Canvas width="684" height="518"
backgroundAlpha="0.5" backgroundColor="0xffffff"
dropShadowColor="0x000000" dropShadowEnabled="true"
cornerRadius="10" borderColor="#B7BABC" borderStyle="solid" borderThickness="3"
horizontalCenter="0" y="10">
<mx:VideoDisplay id="videoContainer" width="640" height="480" horizontalCenter="0" verticalCenter="0"/>
mx:Canvas>
<mx:Canvas y="536" width="164" height="49"
backgroundAlpha="0.5" backgroundColor="0xffffff"
dropShadowColor="0x000000" dropShadowEnabled="true"
cornerRadius="10" borderColor="#B7BABC" borderStyle="solid" borderThickness="3"
horizontalCenter="260">
<mx:Button id="playButton" x="10" y="10" label="broadcast" click="broadcastClick()"/>
<mx:Button id="stopButton" x="101" y="10" label="stop" click="stopClick()" enabled="false"/>
mx:Canvas>
 
mx:Application>
 


サーバ側
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
package main;
 
import java.util.*;
import java.text.*;
 
import org.red5.server.adapter.ApplicationAdapter;
import org.red5.server.api.IConnection;
 
public class Application extends ApplicationAdapter {
 
private int totalAccess;
private long startTime;
private boolean isCast;
 
private Application(){
this.totalAccess = 0;
}
 
/* ----------------------------------------------------------
* 配信者用メソッド
------------------------------------------------------------*/

 
//放送開始
public void startCast() {
long t = new Date().getTime();
this.startTime = t;
this.isCast = true;
}
//放送停止
public void stopCast() {
this.isCast = false;
}
 
/* ----------------------------------------------------------
* 受信者用メソッド
------------------------------------------------------------*/

 
//接続数を返す
public int getConnectNumber() {
return getClients().size()-1;
}
//放送開始時間を返す
public long getStartTime() {
return this.startTime;
}
//新規接続の際に実行する処理
public void newConnect(){
this.totalAccess++;
}
 
//IDを返す
public String makeUserId() {
DecimalFormat df = new DecimalFormat();
df.applyLocalizedPattern("000");
String id = df.format(this.totalAccess);
id = "ID"+id;
return id;
}
}
 
 

Javaにはよく起こられます。ん~、Pythonで書きたい。Jythonには対応してるっぽいんだけど。
関連記事

コメント

コメントの投稿

非公開コメント

このブログについて
□ ブログ内容
決まった趣旨はありません。
興味を持ったこと・日常で行ったことを何でも書きます。

3DCG・プログラミングなどが多めです。

□ 現在の活動
・ウェブサイト制作
 (http://tiblab.net)
・3Dゲーム制作
 (コックパニック)
検索フォーム
ユーザータグ

Blender キャプチャ blendファイル BGE Python GameEngine ムービー Android CG  Red5 Terragen C# C++ 

カテゴリー
プロフィール

TiBra

Author:TiBra
趣味でCG制作、プログラミング等を行っています。メイカーズに憧れています。

ネットを通じた交流を広げたく思っていますので、コメント・メールはお気軽にどぞー

戯言程度のことは、こちらのブログに投稿しています。基本戯言なので、実質移転しているようなものです。

Mail:tibraあっとlive.jp
HP:TibLabmemocode
動画:VimeoFC2動画ニコニコ
ファイル:SkyDrive
企画:3Dゲーム作業実況

Blogリンク
不都合がございましたらご連絡ください。
当ブログのリンクバナー
FC2 ID
FC2カウンター
RSSフィード+解析コード