Как стать автором
Обновить

Создание видео чата на Red5 и Flex

Цель данной статьи — подробно разобраться в том, как сделать он-лайн видео чат на сайте.
Будет рассмотрен пример по созданию под ключ простенького видео чата, позволяющего:

— Обмениваться текстовыми сообщениями в чате.
— Включать трансляцию со своей Веб камеры.
— Просматривать трансляции других участников чата.
— Показывать Вам кто просматривает Вашу трансляцию.
— Обмениваться приватными сообщениями (тест версия).

Программное обеспечение


Создание и тестирование будет производиться под Windows XP SP2 на локальной машине.

Внимание очень важно: Нужно следить за совпадением версий Java которые установлены на локальной машине и на удалённом сервере и использовать версию медиа сервера RED5 под соответствующую вeрсию Java. Приведённые ниже примеры работают под Red5 1.0.1 и Java 7.17

Нам потребуется:

— Бесплатный меда сервер RED5. Скачать можно тут www.red5.org или кодах Гугл, важно чтобы он был под 7 версию Java.

— Java машина 7 версии файл называется jdk-7u17-windows-i586. Скачать можно тут www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html.

— Eclipse для написания и компилирования программ на Java. Скачать можно тут www.eclipse.org/downloads нам нужен Eclipse IDE for Java EE Developers.

Указанные выше программы можно скачать одним архивом тут http://red5.reolan.ru/Red5-Java -Eclipse.zip

— Также нам потребуется flex builder 3 это интегрированная среда разработки для написания программ на MXML и последующей компиляции в файл swf, для встраивания в Веб страницу.

Здесь вам придётся покопаться в интернете и скачать триальную версию, найти трудно, но можно.
Можно скачивать более новую версию, например тут: creative.adobe.com/products/flash-builder.
Но, установив его у себя, я понял, что в нём нет режима «Дизайн» и невозможно визуально строить элементы.
Можете также установить просто компилятор кода mxml в swf, но тогда вы лишите себя возможности визуального моделирования вашего flash-приложения. Прошу учесть, что примеры гарантированно безошибочно работают, только при использовании указанного программного обеспечения.

1. Установка медиа сервера RED5 на локальную машину.

Итак, запускаем файл setup-Red5-1.0.1

image

Просто нажимаем Next>

image

Здесь нужно согласиться с использованием лицензии нажимаем I Agree>

image

Здесь предлагается выбрать место для установки оставляем по умолчанию Нажимаем Next>

image

Здесь выбираем раздел меню, оставляем по умолчанию, нажимаем Install>
Лично у меня на старом компе в конце установки сильно подвисает поэтому ждём…

image

Здесь нас просят ввести IP адрес нашего будущего сервера, вводим локальный 127.0.0.1

image

Здесь просят ввести порт для запросов по HTTP протоколу (не путать с RTMP). Вводим 5080.

image

Это окно говорит, о завершении установки жмём Next>.

image

Это окно говорит что установка закончена, жмём Finish.

Теперь запускаем сервер, заходим в C:\Program Files\Red5 и кликаем два раза файл Red5.

image

Далее должно открыться окно MS Dos и произойти запуск сервера.

image

В этом окне выводится информация о запуске сервера и об ошибках при запуске, если таковые будут.
Посмотрели, если всё нормально идём дальше (окно Dos не закрывать там мы сможем потом отслеживать подключения к меди серверу), если закроем — сервер остановится.

Далее набираем в любимом броузере 127.0.0.1:5080

Если всё сделано правильно, вы увидите примерно это:

image

На этом установка RED5 закончена и пока оставим его в покое…

2. Установка Java.


Запускаем файл jdk-7u17-windows-i586

image

Нажимаем Next>.

image

Нажимаем Next>.

image

Нажимаем Next>.

image

Установка закончена, нажимаем Close.
Теперь нужно кое-что, прописать в системные переменные. Кликаем правой кнопкой на Мой компьютер выбираем Свойства.

image

Нажимаем Переменные среды.

image

В разделе переменные среды пользователя НА РИСУНКЕ Вы видите переменную JAVA_HOME.
У вас её нет, нажимаете Создать и прописываете как на рисунке.
Далее в разделе системные переменные выбираете переменную Path, и нажимаете Изменить.
Дописывает в конец строки после точки с запятой ;Program Files\Java\jdk1.7.0_17\bin
Чтобы получилось как на рисунке.

image

Вот и всё Java машина установлена.

3. Установка Eclipse

Установка Eclipse, наверное, самое простое, потому что установки не требуется.
Просто распаковываем архив eclipse-jee-juno-SR1-win32.rar в удобное место.
Запускается с помощью лилового шарика.

image

Сразу произведём некоторые манипуляции в Eclipse, а именно создадим пустой проект и подготовим директории.
Запускаем Eclipse, сразу скажу у меня запускается долго…

image

При запуске, первое что спросит Eclipse, выбрать рабочее пространство, а проще где хранить проекты.

image

Здесь, важно выбрать именно папку webapps нашего серверва Red5. Именно в это папке содержатся все приложении медиа сервера, которые запускаются при запуске сервера.

После загрузки появится такая картинка:

image

Закрываем окно приветствия. Далее в правом верхнем углу нажимаем квадратик с крестиком.

image

Далее выбираем Java.

image

Создаём новый Java проект.

image

Называем его habr, и нажимаем Finish.

image

В результате у нас появляется новый проект. С подключенной библиотекой Java, которая была установлена вместе с Java машиной. Как добавить в проект библиотеку RED5 покажу позже…

image

Теперь создадим в проекте новую папку WEB-INF название папки не случайно, все приложения для Red5 должны содержать такую папку, в которой будут храниться скомпилированные классы Java.
Нажимает правой кнопкой на названии проекта и выбираем New->Folder. Названия лучше вводить в ручную, а не копировать из буфера, могут быть ошибки.

image

Вводим название папки. Нажимаем Finish.

image

Теперь аналогично создадим ещё две папки classes и src в папке WEB-INF. Для этого правой кнопкой по папке WEB-INF и далее по порядку как в предыдущем примере. Должно получиться так:

image

Теперь определим назначение созданных папок. Папку src будем использовать для хранения исходных кодов. Кликаем правой кнопкой, делаем как на картинке:

image

Папку classes будем использовать для хранения скомпилированных Java программ. Кликаем правой кнопкой по проекту, выбираем Properties.

image

Далее выбираем слева в списке Java Build Path вправа вкладку Source.

image

и внизу в строке Default Output Folder нажимаем Browse и выбираем папку
habr/WEB-INF/classes.

image

Ну вот настройки произведены теперь наш проект выглядит так:

image

4. Установка Flex Builder 3.

Установка не представляет собой сложности, поэтому я думаю Вы найдете в сети 3-ю версию Flex Builder.
После установки вы должны получить такой экран.

image

Увеличить

Давайте немного его настроим. Создадим новый Flex проект habr. Но перед этим создадим на диске C:\ папку для хранения Flex проекта, например vChat.

image

Вводим название проекта habr и указываем папку для хранения проекта, которую мы создали C:\vChat.

image

Нажимает Finish.

image

У нас появился новый проект, вспомогательные папки и чистый холст. И если заглянуть в папку C:\vChat там появилось ещё больше всего интересного, например в папке C:\vChat\bin-debug появился флеш ролик habr в котором и будет наш видео чат. А в папке C:\vChat\ bin-debug файл habr.html который мы будем открывать в броузере для просмотра того, что мы сделали…

Программирование.


Ну теперь приступим к самому интересному, попытаемся написать клиентскую (Flex) часть видеочата и медиа серверную (Java для RED5 ).

1. Клиентская часть видеочата.

Запускаем Flex Builder 3. У нас появляется наш открытый проект habr и чистый лист для построения на нём Flex елементов в визуальном режиме, в этом Вы сможете по упражняться потом.
А сейчас переходим в режим Source который позволяет описывать элементы с помощью языка MXML и встраивать в документ программы на Action Script для Flex.
Переходим на вкладку Source на рисунке, она обведена красным.

image

Получаем пустой шаблон Mxml.

image

Заменяем всё что там есть на следующий код:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="configxml.send()" layout="absolute" width="1000" height="600" cornerRadius="20" borderColor="#66AFE2" themeColor="#14547C" backgroundColor="#EFEFEF" backgroundGradientAlphas="[1.0, 0.0]" backgroundGradientColors="[#DCDCDC, #EBE7E7]">
<mx:Style>
.useronlinebox {
backgroundColor: #dddddd;
border-top-left-radius : 10;
}
</mx:Style>
<mx:Script>
<![CDATA[
import mx.core.UIComponent;
import mx.containers.Canvas;
import mx.controls.Alert;
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
import mx.utils.ObjectUtil;
import mx.controls.Label;
import mx.controls.Image;
import mx.utils.ArrayUtil;
import mx.rpc.xml.SimpleXMLDecoder;
import mx.events.ValidationResultEvent;
import mx.events.CloseEvent;
import mx.containers.TitleWindow;
import mx.managers.PopUpManager;
import mx.utils.URLUtil;
import mx.core.IFlexDisplayObject;
import mx.collections.ArrayCollection;
import mx.controls.RadioButton;
import mx.controls.Menu;
import mx.events.MenuEvent;



import blz.red5demo.ChatHistoryItem;
import blz.red5demo.User;
//import blz.red5demo.RemoteNetConnection;

public var camera:Camera;
public var microphone:Microphone;
public var stream:NetStream;
public var server_script: String;
public var server_media: String;
public var max_count_video: int=1;
public var user_avtoriz: String ;
public var nc: NetConnection;
public var chatIsReady: Boolean=false;
public var chatName: String="0-0-0" ;
public var chatSex: String="boy" ;
public var chatAvatar: String;
public var chatIdred5: String;
public var chatCam: String = "";
public var localHistory: ArrayCollection = new ArrayCollection ();
public var UserList: ArrayCollection = new ArrayCollection ();
public var chat : SharedObject;
private var AutorizFreeWin:AutorisTitle;
private var PrivatFreeWin:PrivatMessege;
private var PrivatNewMess:newmess;

public var titleWindow:TitleWindow;
public var maxcount_video:int=1;
public var online_count_video:int=0;
public var camera_dostup:Boolean=false;

public var path_src: String;
public var point1:Point = new Point();
public var myMenu:Menu;
public var myMenuId:String;
public var myMenuName:String;

public function initApp():void {
//Alert.show("configxml.lastResult = " + ObjectUtil.toString(configxml.lastResult));
server_media=configxml.lastResult.rtmp_server; //configxml.lastResult.rtmp_server; "rtmp://127.0.0.1:1935/video2/public"
maxcount_video=configxml.lastResult.max_col_video;
user_avtoriz=configxml.lastResult.user_avtoriz;
path_src= "C:\\vChat\\bin-debug";

AutorizFreeWin = PopUpManager.createPopUp(this, AutorisTitle, true) as AutorisTitle;
PopUpManager.centerPopUp(AutorizFreeWin);

}

public function sex_change(evt:Event):void {
var radio:RadioButton = RadioButton(evt.currentTarget);
chatSex = radio.name;
}
public function createConnect ():void
{//Alert.show("server_media"+AutorizFreeWin.NicName.text);
chatName = AutorizFreeWin.NicName.text;
AutorizFreeWin.sendConnect.visible=false;
chatAvatar = path_src+"\\img\\" + chatSex + ".png";
nc = new NetConnection ();

nc.objectEncoding = ObjectEncoding.AMF3;
nc.addEventListener ( NetStatusEvent.NET_STATUS, netStatuscreateConnect );
nc.connect (server_media, chatName, path_src+"\\img\\"+ chatSex + "2.png", chatCam );
nc.client=this;

}

private function netStatuscreateConnect ( event:NetStatusEvent ):void
{
if ( event.info.code == "NetConnection.Connect.Rejected" )
{
Alert.show("Ошибка !(Rejected) -" + event.info.application);
AutorizFreeWin.sendConnect.visible=true;
}
if ( event.info.code == "NetConnection.Connect.Failed" )
{
Alert.show("ОШИБКА Нет соединения с медиа сервером");
AutorizFreeWin.sendConnect.visible=true;
}
if ( event.info.code == "NetConnection.Connect.Success" )
{
//Alert.show("Соединились с медиа сервером");
PopUpManager.removePopUp(AutorizFreeWin);
chat = SharedObject.getRemote("chat", nc.uri);
chat.addEventListener(SyncEvent.SYNC, onSync)
chat.connect(nc);
GetUserList ();
GetUseridred5();
bcam.addEventListener(MouseEvent.CLICK, InitCamera);
}
}
private function netStatus ( event:NetStatusEvent ):void
{
if ( event.info.code == "NetConnection.Connect.Rejected" ){ Alert.show("Ошибка !(Rejected) -" + event.info.application);}
if ( event.info.code == "NetConnection.Connect.Failed" ){Alert.show("ОШИБКА Нет соединения с медиа сервером");}
if ( event.info.code == "NetConnection.Connect.Success" ){ }
}
private function onSync(event:SyncEvent):void {
if (chatIsReady == false) { chatIsReady = true; nc.call("chatLogin", null, chatName, "0xbb0f00" ,chatAvatar); }
localHistory.addItem(chat.data.remoteHistory[0]);
setTimeout(function():void{chatmessege.validateNow();chatmessege.verticalScrollPosition = chatmessege.maxVerticalScrollPosition;},100);
// chatmessege.validateNow();
// chatmessege.verticalScrollPosition = chatmessege.maxVerticalScrollPosition;
}

private function makesendmessege(event:Event):void{

var result:ValidationResultEvent = stringValidator.validate();
switch (result.type) {
case ValidationResultEvent.INVALID:
Alert.show(result.message, result.type);
break;
case ValidationResultEvent.VALID:
newtextmessege.visible=false;
labelsend.visible=true;
var textColor:String = "0x"+youcolor.selectedColor.toString(16);
if (! chatIsReady) { Alert.show("Сначала нужно подключиться к серверу"); }
else {
newtextmessege.visible=false;
labelsend.visible=true;
nc.call("chatMessage", null, chatName, textmessege.text, textColor, chatAvatar);
setTimeout(function():void{newtextmessege.visible = true; labelsend.visible = false; textmessege.text="";},10000);
}
break;
}
}

private function httpService_fault(evt:FaultEvent):void {
var title:String = evt.type + " (" + evt.fault.faultCode + ")";
var text:String = evt.fault.faultString;
}

private function GetUseridred5 ():void {
var r : Responder = new Responder(function(id:String):void { chatIdred5 = id; }, netStatus);
nc.call("getUserId", r);
}
private function GetUserList ():void {
var r : Responder = new Responder(onGetUserList, netStatus);
nc.call("getUserList", r);
}

private function onGetUserList(r: Object):void {

//Alert.show("UserLeaveRoom= " + ObjectUtil.toString(r));

var qwee:String;
for(var bnn:String in r){
if(r[bnn].cam == "on") { qwee = path_src+"\\img\\mmm.png"; }
else qwee = null;
UserList.addItem({id:r[bnn].id,nikname:r[bnn].nikname,sex:r[bnn].sex,isWatching:r[bnn].isWatching,cam:qwee});

}
}
public function UserLeaveRoom(r:Object):void {

for (var i:int = 0; i < UserList.source.length; i++) {
if(UserList.source[i].id == r.id) {
UserList.removeItemAt(i);
//Alert.show("UserLeaveRoom= " + ObjectUtil.toString(UserList.source));
//break;
}
}
}
public function UserJoinRoom(r: Object):void {
UserList.addItem(r);
}
public function onchange_webcam(r: Object):void {
//Alert.show("onchange_webcam= " + ObjectUtil.toString(UserList));

for (var i:int = 0; i < UserList.source.length; i++) {
if(UserList.source[i].id == r.id) {
if(r.status == "on"){ UserList.source[i].cam = path_src+"\\img\\mmm.png";}
else{UserList.source[i].cam = null}
UserList.refresh();
}
}
}
public function onwatch(r: Object):void {
//Alert.show("onwatch= " + ObjectUtil.toString(r));
for (var i:int = 0; i < UserList.source.length; i++) {
if(UserList.source[i].id == r) {
UserList.source[i].isWatching = path_src+"\\img\\yee.png";
UserList.refresh();
}
}
}

public function offwatch(r: Object):void {
for (var i:int = 0; i < UserList.source.length; i++) {
if(UserList.source[i].id == r) {
UserList.source[i].isWatching = null;
UserList.refresh();
}
}
}

private function InitCamera(event:Event):void{
camera=Camera.getCamera ();
microphone=Microphone.getMicrophone ();
microphone.gain=SoundSlide.value;
//microphone.setLoopBack(true);
microphone.addEventListener(ActivityEvent.ACTIVITY, activity);
camera.addEventListener(StatusEvent.STATUS, CameraStatus);

if ( camera != null )
{

var widthcam:int = 320;
var heightcam:int = 240;
camera.setMode ( widthcam, heightcam, 25 );
camera.setQuality ( 0, 100 );
vd.attachCamera(camera);
if(camera_dostup == true) sendCamera ();

} else Alert.show("WEB камер не обнаружено !!!");
}
private function CameraStatus(evt:StatusEvent):void{
if(evt.code == "Camera.Unmuted") {sendCamera (); camera_dostup=true;}
}
private function sendCamera ():void{
bcam.label="Выключить";
bcam.removeEventListener(MouseEvent.CLICK, InitCamera);
bcam.addEventListener(MouseEvent.CLICK, StopCamera);
createStream ();
nc.call("change_webcam", null, "on");
}

private function activity(event:ActivityEvent):void{
addEventListener(Event.ENTER_FRAME, showMicLevel);
}

private function showMicLevel(event:Event):void{
level.graphics.clear();
level.graphics.beginFill(0x555555, 1);
level.graphics.drawRect(0, 0, (microphone.activityLevel * 2), 2);
level.graphics.endFill();
}
private function StopCamera(event:Event):void{
bcam.label="Включить Видео";
vd.attachCamera(null);
camera = null;
microphone = null;
stream = null;
nc.call("change_webcam", null,"off");
bcam.removeEventListener(MouseEvent.CLICK, StopCamera);
bcam.addEventListener(MouseEvent.CLICK, InitCamera);
}
private function createStream ():void
{
//Alert.show("cam"+ ObjectUtil.toString(httpService.lastResult));
stream = new NetStream ( nc );
stream.addEventListener( NetStatusEvent.NET_STATUS, netStatus );
stream.attachCamera(camera);
stream.attachAudio(microphone);
stream.publish ( chatIdred5, "live" );
}
public function InitVideo(evt:Event):void {
//Alert.show("online_count_video"+ ObjectUtil.toString(online_count_video));
if(online_count_video < maxcount_video){
titleWindow = new TitleWindow();
titleWindow.name=evt.currentTarget.name;
titleWindow.title = evt.currentTarget.styleName;
titleWindow.showCloseButton = true;
titleWindow.width = 348;
titleWindow.height = 288;
titleWindow.addEventListener(CloseEvent.CLOSE, titleWindow_close);

var INstream:NetStream = new NetStream ( nc );
INstream.addEventListener ( NetStatusEvent.NET_STATUS, netStatus );

var video:Video = new Video(320,240);
var uiComp:UIComponent = new UIComponent();
uiComp.addChild( video );
uiComp.width = 320;
uiComp.height = 240;
INstream.play(evt.currentTarget.name, -1);
video.attachNetStream( INstream );
video.smoothing = true;
video.width=320;
video.height=240;
titleWindow.addChild( uiComp );

PopUpManager.addPopUp(titleWindow, this, false);
online_count_video++;
PopUpManager.centerPopUp(titleWindow);
nc.call("watch", null,evt.currentTarget.name);


//Alert.show("getChildByName"+ ObjectUtil.toString(CanPoisk.title));


} else Alert.show("Максимальное количество трансляций : " + ObjectUtil.toString(maxcount_video));
}
public function titleWindow_close(evt:CloseEvent):void {
PopUpManager.removePopUp(evt.target as IFlexDisplayObject);
online_count_video--;
nc.call("offwatch", null,evt.currentTarget.name);
}

public function showMenu(evt:MouseEvent):void {
myMenu = Menu.createMenu(null, myMenuData, false);
myMenu.labelField = "@label";
myMenu.addEventListener(MenuEvent.ITEM_CLICK, menuHandler);
myMenuId = evt.currentTarget.name;
myMenuName = evt.currentTarget.text;
// Calculate position of Menu in Application's coordinates. evt.currentTarget.name
point1.x = evt.target.x;
point1.y = evt.target.y;
point1 = evt.target.localToGlobal(point1);

myMenu.show(point1.x + 3, point1.y + 5);
}

// sendprivatmessege
public function menuHandler(evt:MenuEvent):void {
switch(evt.index)
{
case 0:

PrivatFreeWin = PopUpManager.createPopUp(this, PrivatMessege, true) as PrivatMessege;
//PrivatFreeWin.title= "Личное сообщения для " + myMenuName;
PopUpManager.centerPopUp(PrivatFreeWin);
PrivatFreeWin.pmess.name=myMenuId;
PrivatFreeWin.title="Личное сообщение для " + myMenuName;
break;

}
}

public function sendprivatmessege(mess:String,id:String,fr:String):void{
//Alert.show("Сообщение "+ mess+"Id=" +id+"from="+fr);

nc.call("privatMessage", null, mess,id,fr);
// PopUpManager.removePopUp(PrivatFreeWin);
}
public function takeprivatmessege(r: Object):void{
//Alert.show(" Object"+ ObjectUtil.toString(r));

PrivatNewMess = PopUpManager.createPopUp(this, newmess, true) as newmess;
PrivatNewMess.privatnewmess.text = r.text;
PrivatNewMess.prm.text = "От " + r.nik;
PrivatNewMess.Privmesscon.name = r.id;
PrivatNewMess.Privmesscon.styleName = r.nik;
PopUpManager.centerPopUp(PrivatNewMess);

//Alert.show("Вам личное сообщение "+ r);
}
]]>
</mx:Script>
<mx:XML id="myMenuData">
/>

</mx:XML>

<mx:Dissolve id="dissolveOut" duration="1000" alphaFrom="1.0" alphaTo="0.0"/>
<mx:Dissolve id="dissolveIn" duration="1000" alphaFrom="0.0" alphaTo="1.0"/>
<mx:Zoom id="zoomAll" zoomWidthTo="1" zoomHeightTo="1" zoomWidthFrom="1" zoomHeightFrom="0" />

<mx:StringValidator id="stringValidator"
source="{textmessege}"
property="text"
minLength="5"
maxLength="200"
tooShortError="Слишком короткое сообщение минимум 5 знака"
requiredFieldError="Неверное сообщение Допустимо минимум 5 максимум 200 знаков"
/>
<mx:HTTPService id="configxml"
resultFormat="e4x"
url="video-chat-config.xml"
fault="httpService_fault(event);"
result="initApp();"
/>
<mx:Panel x="742" y="23" width="234" height="272" layout="absolute" title="Моя WEB Камера" cornerRadius="10" themeColor="#494B4C" alpha="1.0" backgroundColor="#D8D4D4">
<mx:VideoDisplay x="0" y="0" width="214" height="164" id="vd"/>
<mx:Button id="bcam" x="10" y="171" label="Включить Видео" width="194" height="25"/>
<mx:Canvas x="0" y="204" width="214" height="2" id="level">
</mx:Canvas>
<mx:HSlider x="41" y="214" id="SoundSlide" value="70" height="8" minimum="20" maximum="100" snapInterval="10" change="microphone.gain=SoundSlide.value;"/>
<mx:Label x="7" y="211" text="Звук"/>
</mx:Panel>

<mx:TextInput restrict="a-z A-Z а-я А-Я 0-9 \. \- ! ? ( )" x="215" y="530" width="519" height="30" id="textmessege" fontSize="13" fontWeight="bold" cornerRadius="10"/>
<mx:Button x="581" y="568" click="makesendmessege(event)" label="Отправить" id="newtextmessege" cornerRadius="6"/>
<mx:ColorPicker x="230" y="568" width="51" id="youcolor" change="textmessege.setStyle("color",'0x'+youcolor.selectedColor.toString(16));" fontSize="15"/>

<mx:Label x="576" y="572" text="Сообщение отправляется" width="172" id="labelsend" visible="false"/>
<mx:Label x="10" y="6" text="В Чате:" width="226" height="19" fontSize="12" fontWeight="bold" color="#556569"/>

<mx:Label x="244" y="5" text="Сообщения:" width="98" fontSize="12" fontWeight="bold" color="#5A6163"/>




<mx:DataGrid x="0" y="23" width="207" height="537" editable="false" dataProvider="{UserList}" id="useronline" verticalScrollPolicy="on" alternatingItemColors="0xefefef" borderColor="#FBF9F9" dropShadowColor="#BF6565" verticalGridLines="false">
<mx:columns>
<mx:DataGridColumn dataField="sex" headerText="" width="20" >
<mx:itemRenderer>
<mx:Component>

<mx:Image source="{data.sex}" scaleContent="true"/>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>

<mx:DataGridColumn dataField="nikname" headerText="Имя" >
<mx:itemRenderer>
<mx:Component>
<mx:HBox>
<mx:Script>
<![CDATA[
override public function set data(value:Object):void {
super.data = value;
nicgd.addEventListener(MouseEvent.CLICK, outerDocument.showMenu);
}
]]>
</mx:Script>
<mx:Label styleName="{data.nikname}" text="{data.nikname}" id="nicgd" name="{data.id}" />
</mx:HBox>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>


<mx:DataGridColumn dataField="cam" headerText="" width="25" >
<mx:itemRenderer>
<mx:Component>
<mx:HBox>
<mx:Script>
<![CDATA[
override public function set data(value:Object):void {
super.data = value;
if(data.cam != null) {
imgcam.addEventListener(MouseEvent.CLICK, outerDocument.InitVideo);
}
}
]]>
</mx:Script>
<mx:Image styleName="{data.nikname}" source="{data.cam}" id="imgcam" name="{data.id}" width="20" height="20" scaleContent="true"/>
</mx:HBox>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
<mx:DataGridColumn dataField="isWatching" headerText="" width="25">
<mx:itemRenderer>
<mx:Component>
<mx:HBox>
<mx:Script>
<![CDATA[
override public function set data(value:Object):void {
super.data = value;
if(data.isWatching == "false") { isWatching.source = null; isWatching.toolTip=null;}
else { isWatching.source = data.isWatching; isWatching.toolTip="Смотрит Вашу трансляцию";}
}
]]>
</mx:Script>
<mx:Image id="isWatching" width="20" height="20" scaleContent="true"/>
</mx:HBox>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
</mx:columns>
</mx:DataGrid>

<mx:List x="215" y="23" id="chatmessege" width="519" height="499" variableRowHeight="true" dataProvider="{localHistory}">
<mx:itemRenderer>
<mx:Component>
<mx:Canvas width="100%" minHeight="50" verticalScrollPolicy="off">
<mx:Script>
<![CDATA[
override public function set data(value:Object):void {
super.data = value;
this.validateNow();
}
]]>
</mx:Script>
<mx:Image source="{data.chatAvatar}" scaleContent="true" x="0" y="0" />
<mx:Label text="{data.user}" width="123" x="55" y="0" fontSize="12" fontWeight="bold" color="#A23D46"/>
<mx:Text id="newTextArea" text="{data.message}" width="100%" x="55" y="20" color="{data.textColor}" fontSize="12" enabled="false" />
<mx:Label x="266" y="2" text="{data.date}" width="134" fontSize="9" color="#C7CACB"/>
</mx:Canvas>
</mx:Component>
</mx:itemRenderer>
</mx:List>
<mx:Label x="305" y="568" text="Всего 200 осталось:" width="120"/>
<mx:Label x="422" y="568" text="{200 - textmessege.text.length}"/>
<mx:Text x="759" y="323" text="Для отправки личных сообщений и использования других персональных функций - кликните на желаемый Ник в списке On Line (В Чате)." width="196" height="249"/>

</mx:Application>


Но это ещё не всё, нужно добавить ещё несколько элементов. Кликаем правой кнопкой на название проекта. Выбираем новый MXML Component.

image

Вводим название элемента AutorisTitle нажимаем Finish.

image

По аналогии с главным документом habr, также переходим в режим Source, и вводим следующий код.
НЕ забываем всегда нажимать сохранить!

<?xml version="1.0" encoding="utf-8"?>

<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
title="Свободный Вход"
creationComplete="init_autoris()"
width="300"
height="224" backgroundColor="#FAC4C4" barColor="#F79494" backgroundAlpha="1.0" borderColor="#FAC4C4">

<mx:Script>
<![CDATA[
import mx.core.Application;
import mx.controls.Alert;

public function init_autoris():void {
if(Application.application.user_avtoriz == "true"){

}
else {

}
}
public function valid_nic():void {// "+chr(0x7F)+"-"+chr(0xff)+"
var reg_exp: RegExp =new RegExp("[a-zA-Zа-яА-Я]{3,10}","ig");

if(reg_exp.test(NicName.text as String)) Application.application.createConnect ();
else Alert.show("В Нике Допустимы Только буквы от 3 до 10");
}
]]>
</mx:Script>
<mx:TextInput x="60" y="42" id="NicName"/>
<mx:RadioButton change="Application.application.sex_change(event);" name="boy" x="46" y="122" label="Мужчина" selected="true"/>
<mx:RadioButton change="Application.application.sex_change(event);" name="girl" x="164" y="122" label="Женщина"/>
<mx:Label x="60" y="19" text="Введите Ваш Ник:" width="160"/>
<mx:Image x="10" y="114" source="img/boy2.png" />
<mx:Image x="244" y="114" source="img/girl2.png"/>

<mx:ControlBar>
<mx:Button label="Подключиться" id="sendConnect" click="valid_nic();" />
</mx:ControlBar>

</mx:TitleWindow>

По аналогии создаём ещё два Flex Компонента.
Первый называется newmess и содержит следующий код:
<?xml version="1.0" encoding="utf-8"?>

<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
title="Личное сообщения "
showCloseButton="true"
close="removeMe();"
width="300"
creationComplete="Privmesscon.addEventListener(MouseEvent.CLICK, creat_mess);"
height="244" backgroundColor="#66A5D1" barColor="#F79494" backgroundAlpha="1.0" borderColor="#196A76">

<mx:Script>
<![CDATA[
import mx.core.Application;
import mx.controls.Alert;
import mx.managers.PopUpManager;

private var PrivatFreeWin:PrivatMessege;

private function removeMe():void {
PopUpManager.removePopUp(this);
}
public function creat_mess(evt:MouseEvent):void {// "+chr(0x7F)+"-"+chr(0xff)+"

PrivatFreeWin = PopUpManager.createPopUp(this, PrivatMessege, true) as PrivatMessege;
//PrivatFreeWin.title= "Личное сообщения для " + myMenuName;
PopUpManager.centerPopUp(PrivatFreeWin);
PrivatFreeWin.pmess.name=evt.currentTarget.name;
PrivatFreeWin.title="Личное сообщение для " + evt.currentTarget.styleName;
removeMe();
}

]]>
</mx:Script>
<mx:Label x="10" y="19" id="prm" text="vvv" width="160"/>
<mx:TextArea x="10" y="45" id="privatnewmess" width="260" height="113"/>

<mx:ControlBar>
<mx:Button name="" styleName="" label="Ответить" id="Privmesscon" />
</mx:ControlBar>

</mx:TitleWindow>


Второй называется PrivatMessege и содержит следующий код:

<?xml version="1.0" encoding="utf-8"?>

<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
title=""
showCloseButton="true"
close="removeMe();"
width="300"
creationComplete="pmess.addEventListener(MouseEvent.CLICK, valid_nic);"
height="244" backgroundColor="#FAC4C4" barColor="#F79494" backgroundAlpha="1.0" borderColor="#FAC4C4">

<mx:Script>
<![CDATA[
import mx.core.Application;
import mx.controls.Alert;
import mx.managers.PopUpManager;

private function removeMe():void {
PopUpManager.removePopUp(this);
}

public function valid_nic(evt:MouseEvent):void {// "+chr(0x7F)+"-"+chr(0xff)+"
var reg_exp: RegExp =new RegExp("[a-zA-Zа-яА-Я]{3,100}","ig");

if(reg_exp.test(privatm.text as String)) {
Application.application.sendprivatmessege (privatm.text,evt.currentTarget.name,Application.application.chatName);
removeMe();
}
else Alert.show(" Допустимы Только буквы от 3 до 100");
}
]]>
</mx:Script>
<mx:Label x="10" y="19" id="prm" text="Текст Сообщения:" width="160"/>
<mx:TextArea x="10" y="45" id="privatm" width="260" height="113"/>

<mx:ControlBar>
<mx:Button name="" label="Отправить" id="pmess" />
</mx:ControlBar>

</mx:TitleWindow>


После этих манипуляций наш проект должен выглядеть так:

image

Осталось совсем чуть чуть, нужно создать ещё несколько классов Action Script.
Правой кнопкой на проект habr далее новый Action Script Class.

image

Вводим название пакета blz.red5demo название класса ChatHistoryItem.

image

Вставляем в документ следующий код:

package blz.red5demo
{
[RemoteClass(alias="blz.red5demo.ChatHistoryItem")]
public class ChatHistoryItem {
public var user:String;
public var date:Date;
public var message:String;
public var textColor:String;
public var chatAvatar:String;
public function ChatHistoryItem() { }
}
}


Аналогично создаём ещё один класс.
Вводим название пакета blz.red5demo название класса User.
Вставляем в него следующий код:

package blz.red5demo
{
[RemoteClass(alias="blz.red5demo.User")]
public class User
{
public var id:String;
public var nikname:String;
public var sex:String;
public var isWatching:String;
public var cam:String;
public function User(){}
}
}


В результате нашей работы наш Flex проект должен приобрести вот такой законченный вид:

image

Теперь нужно создать в папке C:\vChat\ bin-debug небольшой xml файлик в который я вынес конфигурационную информацию, называться он должен video-chat-config.xml и содержать следующий код:

<?xml version="1.0" encoding="WINDOWS-1251" standalone="yes" ?>
<rtmp_server>rtmp://127.0.0.1:1935/habr/public</rtmp_server>
<max_col_video>3</max_col_video>
<user_avtoriz>false</user_avtoriz>


Ну и последнее, для работы чата необходимо несколько картинок, которые будут лежать в папке
C:\vChat\ bin-debug\img.
Скачать картинки в архиве можно тут red5.reolan.ru/img.zip.
Самое время протестировать клиентскую часть.
Открываем в броузере файл C:\vChat\ bin-debug\habr.html

Если Вы всё сделали правильно, должно появиться следующее:

image

Ура клиентская часть работает, но если ввести Ник и нажать Подключиться, ничего не получиться так как,
с медиа сервера нам никто не ответит, идём дальше…

2. Серверная часть Java для RED5.


Открываем, установленный ранее Eclipse.
Начинаем создавать классы. Кликаем правой кнопкой на проекте WEB-INF/src, выбираем New -> Class.

image

Вводим название пакета chat2, название класса Application. Нажимаем Finish.

image

Вставляем в него следующий код:

package chat2;

import org.red5.server.adapter.ApplicationAdapter;
import org.red5.server.api.IConnection;
import org.red5.server.api.IClient;
import org.red5.server.api.scope.IScope;
import org.red5.server.api.Red5;
import org.red5.server.api.service.IServiceCapableConnection;
import org.red5.server.api.so.ISharedObject;
import org.red5.server.util.ScopeUtils;
import org.red5.server.api.so.ISharedObjectService;
import org.red5.server.api.*;
import org.red5.server.api.scheduling.*;
import org.red5.server.api.service.*;
import java.util.*;
import chat2.ChatHistoryItem;
import chat2.ClientManager;
import chat2.User;

import java.applet.*;
import java.awt.*;
import java.net.*;
import java.io.*;

public class Application extends ApplicationAdapter {

public HashMap<String,User> users = new HashMap<String,User>();
private ClientManager clientMgr = new ClientManager("users_so",true);
public class cam_send {
public String id = null;
public String status=null;

public cam_send (String id,String status) {
this.id = id;
this.status = status;
}
}
public class ret_priv_mess {
public String text = null;
public String id = null;
public String nik=null;

public ret_priv_mess (String text,String id,String nik) {
this.text = text;
this.id = id;
this.nik = nik;
}
}
public boolean appStart(IScope app) {
return super.appStart(app);
}

public void appStop()
{}
public boolean getpassword ( String user_id )
{
try {
String data ="mydata=mydatavalue&mydata2=n";
// Send the request
//System.out.println("Начало соединения..");
URL url = new URL("http://sex22.ru/password_video.php");
URLConnection conn = url.openConnection();
//System.out.println("Соединились..");
conn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
//write parameters
writer.write(data);
writer.flush();
// Get the response
StringBuffer answer = new StringBuffer();
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
// answer.append(line);
//System.out.println("Получение ответа от сервера.." + line);
String falseString = "NOTFOUND";


}

writer.close();
reader.close();
return true;
}
catch (Exception e) {
return false;
}

}
public boolean appConnect(IConnection conn, Object[] params)
{
if (params == null || params.length != 3) {
rejectClient("Client must pass 2 params !.");
return false;
}
////
//if(getpassword( params[0].toString() ) == true ) rejectClient("true");
//if(getpassword( params[0].toString() ) == false ) rejectClient("false");

//getSession ( params[0].toString() );
////
String id = conn.getClient().getId();
String nikname = params[0].toString();
String sex = params[1].toString();
String isWatching="false";
String cam= params[2].toString();
// test if username is already used
if (users.containsKey(nikname)) {
rejectClient("Ошибка!!! Ник: <<"+nikname+">> уже используется." );
return false;
}
users.put(nikname,new User(id,nikname,sex,isWatching,cam));
clientMgr.addClient(scope, nikname, id);
ServiceUtils.invokeOnAllConnections("UserJoinRoom", new Object[] {new User(id,nikname,sex,isWatching,cam)} );
return true;
}

public void appDisconnect(IConnection conn)
{
IScope appScope = Red5.getConnectionLocal().getScope();
String id = conn.getClient().getId();
String nikname = clientMgr.removeClient(scope, id);
User user=users.get(nikname);
users.remove(nikname);

ServiceUtils.invokeOnAllConnections("UserLeaveRoom", new Object[] {user} );

super.appDisconnect(conn);
}


public boolean roomJoin(IClient client, IScope room) {


return super.roomJoin(client, room);
}

public void change_webcam(String status) {
IConnection conn = Red5.getConnectionLocal();
String id = conn.getClient().getId();
ServiceUtils.invokeOnAllConnections("onchange_webcam", new Object[] {new cam_send(id,status)} );


IScope appScope = Red5.getConnectionLocal().getScope();
String pseudo = clientMgr.returnClient(scope, id);
User user=users.get(pseudo);
//users.remove(pseudo);

users.put(pseudo,new User(user.id,user.nikname,user.sex,user.isWatching,"on"));

}
public void watch(String UserId) {
IConnection conn2 = Red5.getConnectionLocal();
String uid = conn2.getClient().getId();
IScope appScope = Red5.getConnectionLocal().getScope();

Collection<Set> connecciones = appScope.getConnections();
for(Set listConnection : connecciones) {
Iterator it = listConnection.iterator();
while (it.hasNext()) {
IConnection conn = it.next();
String id=conn.getClient().getId();
if (!(UserId.equals(id))) continue;
if (conn instanceof IServiceCapableConnection) {
((IServiceCapableConnection) conn).invoke("onwatch", new Object[]{uid});
return;
}
}
}
}
public void offwatch(String UserId) {
IConnection conn2 = Red5.getConnectionLocal();
String uid = conn2.getClient().getId();
IScope appScope = Red5.getConnectionLocal().getScope();

Collection<Set> connecciones = appScope.getConnections();
for(Set listConnection : connecciones) {
Iterator it = listConnection.iterator();
while (it.hasNext()) {
IConnection conn = it.next();
String id=conn.getClient().getId();
if (!(UserId.equals(id))) continue;
if (conn instanceof IServiceCapableConnection) {
((IServiceCapableConnection) conn).invoke("offwatch", new Object[]{uid});
return;
}
}
}
}
public void privatMessage(String Text,String UserId,String fr) {
IConnection conn2 = Red5.getConnectionLocal();
String uid = conn2.getClient().getId();
IScope appScope = Red5.getConnectionLocal().getScope();

Collection<Set> connecciones = appScope.getConnections();
for(Set listConnection : connecciones) {
Iterator it = listConnection.iterator();
while (it.hasNext()) {
IConnection conn = it.next();
String id=conn.getClient().getId();
if (!(UserId.equals(id))) continue;
if (conn instanceof IServiceCapableConnection) {
((IServiceCapableConnection) conn).invoke("takeprivatmessege", new Object[] {new ret_priv_mess(Text,uid,fr)} );
return;
}
}
}
}


public void roomLeave(IClient client, IScope room) {

//String id = client.getId();
//String nikname = clientMgr.removeClient(scope, id);
//User user=users.get(nikname);
//users.remove(nikname);
//ServiceUtils.invokeOnAllConnections (room,"UserLeaveRoom", new Object[] {"cc"} );//user.id,user.nikname,user.sex,user.isWatching
super.roomLeave(client, room);
}

public String getUserId() {
IConnection conn2 = Red5.getConnectionLocal();
String uid = conn2.getClient().getId();

return uid;
}

public HashMap<String,User> getUserList() {
return users;
}
public void chatLogin(String userName, String textColor, String chatAvatar ) {
chatMessage(userName, "Пользователь вошел в чат",textColor,chatAvatar);
}

public void chatMessage(String userName, String message, String textColor, String chatAvatar) {
IScope scope = Red5.getConnectionLocal().getScope();
ISharedObject chat = getSharedObject(scope, "chat");
// List history = (List) chat.getAttribute("remoteHistory");
java.util.List history = new ArrayList();

ChatHistoryItem item = new ChatHistoryItem();
item.user = userName;
item.date = new Date();
item.message = message;
item.textColor = textColor;
item.chatAvatar = chatAvatar;
history.add(item);

//chat.removeAttribute("remoteHistory");
chat.setAttribute("remoteHistory", history);

}

public boolean roomStart(IScope room) {
if (!super.roomStart(room))
return false;
createSharedObject(room, "chat", false);
ISharedObject chat = getSharedObject(room, "chat");
java.util.List history = new ArrayList();
ChatHistoryItem item = new ChatHistoryItem();
item.user = "Administrator";
item.date = new Date();
item.message = "Red5Flexchat start";
item.textColor = "0xbb0f00";
item.chatAvatar = "";
history.add(item);
chat.setAttribute("remoteHistory", history);
return true;
}
}

Справа сразу появляется куча красных квадратиков ошибок, пока не обращаем на них внимания.
Создаём по аналогии следующие классы.
Класс ChatHistoryItem содержащий следующий код:

package chat2;

import java.util.Date;

public class ChatHistoryItem {
public String user;
public Date date;
public String message;
public String textColor;
public String chatAvatar;
}


Класс ClientManager содержащий следующий код:

package chat2;

import org.red5.server.api.scope.IScope;
import org.red5.server.util.ScopeUtils;
import org.red5.server.api.so.ISharedObject;
import org.red5.server.api.so.ISharedObjectService;

public class ClientManager {

/** Stores the name of the SharedObject to use. */
private String name;

/** Should the SharedObject be persistent? */
private boolean persistent;

/**
* Create a new instance of the client manager.
*
* @param name
* name of the shared object to use
* @param persistent
* should the shared object be persistent
*/
public ClientManager(String name, boolean persistent) {
this.name = name;
this.persistent = persistent;
}

/**
* Return the shared object to use for the given scope.
*
* @param scope
* the scope to return the shared object for
* @return the shared object to use
*/
private ISharedObject getSharedObject(IScope scope) {
ISharedObjectService service = (ISharedObjectService) ScopeUtils
.getScopeService(scope,
ISharedObjectService.class,
false);
return service.getSharedObject(scope, name, persistent);
}

/**
* A new client connected. This adds the username to
* the shared object of the passed scope.
*
* @param scope
* scope the client connected to
* @param username
* name of the user that connected
* @param uid
* the unique id of the user that connected
*/
@SuppressWarnings("unchecked")
public void addClient(IScope scope, String username, String uid) {
ISharedObject so = getSharedObject(scope);
so.setAttribute(uid, username);
}

/**
* A client disconnected. This removes the username from
* the shared object of the passed scope.
*
* @param scope
* scope the client disconnected from
* @param uid
* unique id of the user that disconnected
* @return the username of the disconnected user
*/
@SuppressWarnings("unchecked")
public String removeClient(IScope scope, String uid) {
ISharedObject so = getSharedObject(scope);
if (!so.hasAttribute(uid)) {
// SharedObject is empty. This happes when the last client
// disconnects.
return null;
}

String username = so.getStringAttribute(uid);
so.removeAttribute(uid);
return username;
}
@SuppressWarnings("unchecked")
public String returnClient(IScope scope, String uid) {
ISharedObject so = getSharedObject(scope);
if (!so.hasAttribute(uid)) {
// SharedObject is empty. This happes when the last client
// disconnects.
return null;
}

String username = so.getStringAttribute(uid);
//so.removeAttribute(uid);
return username;
}
}


И класс User содержащий следующий код:

package chat2;

public class User {
public String id = null;
public String nikname = null;
public String sex = null;
public String isWatching=null;
public String cam=null;

public User (String id,String nikname,String sex,String isWatching,String cam) {
this.id = id;
this.nikname = nikname;
this.sex = sex;
this.isWatching = "false";
this.cam = cam;
}
}


После добавления классов наш проект должен выглядеть так :

image

Теперь давайте разбираться с ошибками, сначала откроем классы в которых показываются ошибки.
А именно Application и ClientManager. Чтобы ошибки исчезли, нам нужно просто присоединить библиотеку RED5, которая идёт вместе с ним. Кликаем правой кнопкой на проект habr. Выбираем Prorities.

image

В открывшемся окне слева выбираем Java Build Path. Далее вкладку Libraries и нажимаем кнопку Add External JARs.

image

Выбираем библиотеку red5-server-1.0 которая лежит в папке установленного RED5.

image

В результате в проекте у нас появиться новая подключенная библиотека.

image

Как видно все красные ошибки пропали, вернее если Вы всё сделали правильно, должны пропасть.
Ну теперь осталось совсем немного. Необходимо в папке нашего проекта, а именно
C:\Program Files\Red5\webapps\habr\WEB-INF разместить три небольших конфигурационных файла. Эти файлы не придуманы мной, а являются обязательными для всех приложений для REDR5. Без них Ваша программа просто не запуститься корректно. И так создаём ..

Первый файл должен называться red5-web.properties, создать можно в текстовом редакторе WordPad.
Хочу обратить Ваше ВНИМАНИЕ что сохранять его нужно как просто Текстовый документ.
У меня из за неверных форматов файла Java приложение чата не запускалось.
Он должен содержать следующий код:

webapp.contextPath=/habr
webapp.virtualHosts=*

Файлы xml можно создавать в блокноте.
Второй файл будет red5-web.xml он должен содержать следующий код:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd">

/>


/>

/>
/>
/>
/>
/>
/>


/>



Третий файл должен называться web.xml и содержать следующий код:

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">

<display-name>habr</display-name>

<context-param>
<param-name>webAppRootKey</param-name>
<param-value>/habr</param-value>
</context-param>

<servlet-name>rtmpt</servlet-name>
<servlet-class>org.red5.server.net.rtmpt.RTMPTServlet</servlet-class>
<load-on-startup>1</load-on-startup>


<servlet-mapping>
<servlet-name>rtmpt</servlet-name>
<url-pattern>/fcs/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>rtmpt</servlet-name>
<url-pattern>/open/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>rtmpt</servlet-name>
<url-pattern>/close/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>rtmpt</servlet-name>
<url-pattern>/send/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>rtmpt</servlet-name>
<url-pattern>/idle/*</url-pattern>
</servlet-mapping>

</web-app>


После этих действий наша папка C:\Program Files\Red5\webapps\habr\WEB-INF должна выглядеть так:

image

И это значит, что всё готово и можно попробовать наше серверное приложение и видео чат.
Запускаем RED5.

image

Не закрываем с сеансом Dos в котором происходит запуск, там потом можно просмотреть ошибки и отследить соединения.
Если всё заработало, то Вы войдёте в чатик и сможете поиграться…

image
Увеличить

Если соединения с медиа сервером не происходит, попробуйте удалить папку
C:\Program C:\Program Files\Red5\webapps\habr\persistence.
Мне помогало, а вообще хочется сказать, что под Windows всё работает как то странно..
Но главное мы разобрались в последовательностях действий.
Скачать готовые коды можно на red5.reolan.ru.

В следующем посте рассажу как поставить медиа сервер на удалённый сервер Ubuntu12.
Всем удачи.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.