Цель данной статьи — подробно разобраться в том, как сделать он-лайн видео чат на сайте.
Будет рассмотрен пример по созданию под ключ простенького видео чата, позволяющего:
— Обмениваться текстовыми сообщениями в чате.
— Включать трансляцию со своей Веб камеры.
— Просматривать трансляции других участников чата.
— Показывать Вам кто просматривает Вашу трансляцию.
— Обмениваться приватными сообщениями (тест версия).
Создание и тестирование будет производиться под 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-приложения. Прошу учесть, что примеры гарантированно безошибочно работают, только при использовании указанного программного обеспечения.
Итак, запускаем файл setup-Red5-1.0.1

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

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

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

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

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

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

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

Это окно говорит что установка закончена, жмём Finish.
Теперь запускаем сервер, заходим в C:\Program Files\Red5 и кликаем два раза файл Red5.

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

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

На этом установка RED5 закончена и пока оставим его в покое…
Запускаем файл jdk-7u17-windows-i586

Нажимаем Next>.

Нажимаем Next>.

Нажимаем Next>.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Нажимает Finish.

У нас появился новый проект, вспомогательные папки и чистый холст. И если заглянуть в папку C:\vChat там появилось ещё больше всего интересного, например в папке C:\vChat\bin-debug появился флеш ролик habr в котором и будет наш видео чат. А в папке C:\vChat\ bin-debug файл habr.html который мы будем открывать в броузере для просмотра того, что мы сделали…
Ну теперь приступим к самому интересному, попытаемся написать клиентскую (Flex) часть видеочата и медиа серверную (Java для RED5 ).
Запускаем Flex Builder 3. У нас появляется наш открытый проект habr и чистый лист для построения на нём Flex елементов в визуальном режиме, в этом Вы сможете по упражняться потом.
А сейчас переходим в режим Source который позволяет описывать элементы с помощью языка MXML и встраивать в документ программы на Action Script для Flex.
Переходим на вкладку Source на рисунке, она обведена красным.

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

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

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

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

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

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

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

Теперь нужно создать в папке C:\vChat\ bin-debug небольшой xml файлик в который я вынес конфигурационную информацию, называться он должен video-chat-config.xml и содержать следующий код:
Ну и последнее, для работы чата необходимо несколько картинок, которые будут лежать в папке
C:\vChat\ bin-debug\img.
Скачать картинки в архиве можно тут red5.reolan.ru/img.zip.
Самое время протестировать клиентскую часть.
Открываем в броузере файл C:\vChat\ bin-debug\habr.html
Если Вы всё сделали правильно, должно появиться следующее:

Ура клиентская часть работает, но если ввести Ник и нажать Подключиться, ничего не получиться так как,
с медиа сервера нам никто не ответит, идём дальше…
Открываем, установленный ранее Eclipse.
Начинаем создавать классы. Кликаем правой кнопкой на проекте WEB-INF/src, выбираем New -> Class.

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

Вставляем в него следующий код:
Будет рассмотрен пример по созданию под ключ простенького видео чата, позволяющего:
— Обмениваться текстовыми сообщениями в чате.
— Включать трансляцию со своей Веб камеры.
— Просматривать трансляции других участников чата.
— Показывать Вам кто просматривает Вашу трансляцию.
— Обмениваться приватными сообщениями (тест версия).
Программное обеспечение
Создание и тестирование будет производиться под 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

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

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

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

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

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

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

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

Это окно говорит что установка закончена, жмём Finish.
Теперь запускаем сервер, заходим в C:\Program Files\Red5 и кликаем два раза файл Red5.

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

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

На этом установка RED5 закончена и пока оставим его в покое…
2. Установка Java.
Запускаем файл jdk-7u17-windows-i586

Нажимаем Next>.

Нажимаем Next>.

Нажимаем Next>.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Нажимает Finish.

У нас появился новый проект, вспомогательные папки и чистый холст. И если заглянуть в папку 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 на рисунке, она обведена красным.

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

Заменяем всё что там есть на следующий код:
<?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.

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

По аналогии с главным документом 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>
После этих манипуляций наш проект должен выглядеть так:

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

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

Вставляем в документ следующий код:
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 проект должен приобрести вот такой законченный вид:

Теперь нужно создать в папке 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
Если Вы всё сделали правильно, должно появиться следующее:

Ура клиентская часть работает, но если ввести Ник и нажать Подключиться, ничего не получиться так как,
с медиа сервера нам никто не ответит, идём дальше…
2. Серверная часть Java для RED5.
Открываем, установленный ранее Eclipse.
Начинаем создавать классы. Кликаем правой кнопкой на проекте WEB-INF/src, выбираем New -> Class.

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

Вставляем в него следующий код:
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;
}
}
После добавления классов наш проект должен выглядеть так :

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

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

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

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

Как видно все красные ошибки пропали, вернее если Вы всё сделали правильно, должны пропасть.
Ну теперь осталось совсем немного. Необходимо в папке нашего проекта, а именно
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 должна выглядеть так:

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

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

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