–Ты знаешь, – начала Алина, – сколько помню себя как разработчика...
– Как разработчика? – переспросил Антон, делая большой глоток пива из бокала, – и давно ты в разработчиках?
– Достаточно, но к делу это отношения не имеет, по сути. Так вот. Все это время, каждый год Flash кто-нибудь хоронит. Все время идут разговоры о том, что он безбожно устарел, и вот появляется что-то – то SilverLight, то HTML5, – что должно стремительно его заменить. Но флеш почему-то совершенно не собирается умирать. Наоборот, он постоянно выпускает что-то невероятное, что его воскрешает в новом качестве. То ActionScript3, то поддержку 3D видео, или вот, например, p2p протокол RTMFP.
Ироничная улыбочка с лица Антона куда-то незаметно сползла, уступив место отвисшей челюсти. Образ молодой, привлекательной девушки в короткой юбке, сидящей на его рабочем столе, как-то совершенно не вязался с такими словами как P2P и ActionScript. Антон понял, что впадает в ступор, и, чтобы как-то заполнить неловкую паузу, спросил. RTMFP? – а можешь рассказать о нем поподробнее? Конечно, весело подхватила девушка, я на нем не одну собаку уже съела.
Это, как я уже говорила, P2P протокол. Главная его особенность в том, что все данные передаются непосредственно от пользователя к пользователю, минуя сервер. Он работает на UDP и за счет его фишек с широковещательной адресацией позволяет коннектиться даже юзерам, сидящим за NAT-ами. Но если в сетке зарублен UDP трафик, тут уж извиняй, работать не будет вообще. Для работы нужен флеш версии не ниже 10.1 и специальный rendezvous-сервер, который позволяет нодам найти друг друга. Подсоединившись к серверу, пользователь получает специальный peerID, который его собственно и идентифицирует в образовавшейся сети.
Значит, по сервакам. Можно заюзать Adobe Flash Media Saerver, в простонародье FMS. Кажется, с версии 4.5 там появилась поддержка RTMFP. Милая вещица, хорошо работает, возможен серверный скриптинг на том же ActionScript, но в лицензии стоит каких-то астрономических денег. Есть фришный аналог: Cumulus. Достаточно серьезный проект, с хорошей поддержкой. Есть что-то еще, но на тот момент, когда я искала, там все было совсем плохо. В Cumulus есть только одна проблема, которая может слегка напрягать по первости: серверные скрипты пишутся на языке LUA. Впрочем, язык очень даже годный и в освоении не особо сложен. Вот, ну а на периоде разработки и отладки можно с чистой совестью и широкой улыбкой юзать совершенно бесплатную штуку от того же Adobe – Cirrus. http://labs.adobe.com/technologies/cirrus/. Там регишься, получаешь developer key и все. Если надумаешь работать с FMS, то его можно брать в аренду на Amazon EC2. Но я в свое время остановилась на Cumulus и, как говорится, осталась с почтением.
С клиентской стороны все работает достаточно просто.
Нужно создать объект NetConnection и подключиться к серверу:
var connectUrl:String = "rtmfp://p2p.rtmfp.net";
var DeveloperKey:String = "твой ключ";
netConnection = new NetConnection();
netConnection.connect(connectUrl, DeveloperKey);
примерно так. Конечно, надо отлавливать события, он ведь может и не соединиться:
тогда добавляется строчка:
netConnection = new NetConnection();
netConnection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
netConnection.connect(connectUrl, DeveloperKey);
и собственно метод обработчик.
public function netStatusHandler(event:NetStatusEvent):void
в event.info.code будет код события. т.е. рабочий вариант функции будет выглядеть примерно так:
public function netStatusHandler(event:NetStatusEvent):void{
switch (event.info.code)
{
case "NetConnection.Connect.Success":
peerID = nc.nearID;
//Что то делаем дальше
break;
case "NetConnection.Connect.Failed":
//наступает, если не удалось подсоедениться к серверу. Например, если закрыт
//UDP. Таймаут около минуты
break;
}
}
что можно делать дальше? Тут открывается несколько вариантов. Предположим, мы хотим сделать что-то вроде видеочата. т.е. транслировать видео и звук с камеры и микрофона другим пользователям и получать такие же потоки от них. Тогда нужно определиться вот с чем, RTMFP предлагает на выбор два варианта взаимодействия: прямые соединения и группы. Во время прямого соединения между двумя пирами устанавливается некое виртуальное соединение и они могут обмениваться данными между собой. Все данные которые ты передаешь попадают одному и только одному клиенту. Второй вариант это группы. Тут все немного сложнее, пользователи не устанавливают прямых соединений а объединяются в группы в которых обмен данными идет по принципу торрента, т.е. каждый участник в зависимости от ситуации может участвовать в передаче данных. т.е. группа получает трафик и дальше дербанит его межу собой в зависимости от взаимного расположения и толщины каналов участников этой группы. Схематически это выглядит вот так:
Плюсы и минусы: Передача данных в группах работает медленно. Прямые соединения гораздо быстрее, но, в обмен группы представляют возможность вещать сразу на огромную аудиторию. Например, один профессор вещает свою видео лекцию в сети а его смотрят сразу же десять тысяч студентов. Реально он отдает трафик одному двум пирам, а они уже передают дальше еще какому-то количеству участников и те в свою очередь тоже и т.д. Прямые соединения значительно быстрее но соединиться можно лишь с таким количеством пиров, которое вытянет твой канал. Выбирать конечно нужно под конкретную ситуацию. Давай разберем оба варианта и начнем с прямых соединений, так как они проще в реализации и понимании.
После успешного соединения с сервером создадим объект NetStream для передачи своего потока. Для этого добавим в netStatusHandler в секции "NetConnection.Connect.Success" следующий код:
case "NetConnection.Connect.Success":
outStream = new NetStream(netConnection, NetStream.DIRECT_CONNECTIONS);
outStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
outStream.attachAudio(Microphone.getMicrophone());
outStream.attachCamera(Camera.getCamera());
outStream.publish("MediaChannel");
break;
еще добавим в наш класс пару переменных что бы это все заработало
public var netConnection:NetConnection;
private var outStream:NetStream;
теперь в netStatusHandler будут обрабатываться еще и события исходящего потока. А Любой пир подключенный к этому серверу сможет начать просмотр, конечно если знает peerID вещателя и имя канала, в нашем случае "MediaChannel"
делается это так:
private var inStream:NetStream;
inStream = new NetStream(netConnection, id_of_publishing_client);
inStream.addEventListener(NetStatusEvent.NET_STATUS, netStreamHandler);
recvStream.play("MediaChannel");
звук заиграет сам по себе а вот видео можно будет снять при помощи объекта Video, например так:
public var screen:Video;
screen = new Video();
screen.width = stage.stageWidth;
screen.height = stage.stageHeight;
screen.scaleX < screen.scaleY ? screen.scaleY = screen.scaleX : screen.scaleX = screen.scaleY;
addChild(screen);
screen.attachNetStream(inStream);
Дополнительно можно отметить пару моментов. Как я уже говорила, любой кто знает peerID и имя канала может подключиться. То, откуда он их узнает протокол RTMFP не регламентирует, т. е. должна существовать какая-то система обмена peerID например на основе php скриптов. Но тут полная свобода выбора разработчика, стандартов или даже рекомендаций не существует.
Раздающий, может решать кому показывать стрим а кому нет. При создании outStream можно добавить вот такой объект:
var o:Object = new Object();
o.onPeerConnect = function(subscriberStream:NetStream):Boolean {
if(accept) {
return true;
} else {
return false;
}
}
outStream.client = o;
здесь subscriberStream – это копия объекта NetStream запрашивающего соединение. Можно выудить из него peerID обращающегося subscriberStream.farID и на основе этого каким-то образом принять решение об отказе или принятии соединения. Для отказа достаточно вернуть false;
с помощью свойства netConnection.maxPeerConnections можно устанавливать число подписчиков способных подключиться одновременно. По умолчанию там стоит 8.
Ну вот, теперь о группах.
В начале нам нужно создать группу, делается это так:
private var groupSpecifier:GroupSpecifier;
private var netGroup:NetGroup;
groupSpecifier = new GroupSpecifier("com.example.p2p");
groupSpecifier.multicastEnabled = true;
groupSpecifier.objectReplicationEnabled = true;
groupSpecifier.postingEnabled = true;
groupSpecifier.routingEnabled = true;
groupSpecifier.serverChannelEnabled = true;
groupSpecifier.ipMulticastMemberUpdatesEnabled = true;
netGroup = new NetGroup(netConnection,groupSpecifier.groupspecWithAuthorizations());
netGroup.addEventListener(NetStatusEvent.NET_STATUS,netStatusHandler);
тут в начале мы создаем объект groupSpecifier, выставляем в нем всевозможные параметры и потом собственно подключаемся. Следует помнить что группа описывается ее именем, в нашем случае это "com.example.p2p" и набором параметров, т. е. если кто-то подключится к группе у которой имя будет тем-же а вот скажем postingEnabled будет выставлен в false то «по факту» он подключится уже к другой группе. Соответственно если группы ранее не существовало, то она создается, если уже кем-то была создана, то происходит просто подключение.
О том что подключение к группе произошло успешно нам поведает событие "NetGroup.Connect.Success" в netStatusHandler(). Псоле успешного соединения можно начинать что ни будь транслировать и/или смотреть. Точно так же как и в предыдущем случае создаем NetStream только коннектимся не к пиру а к ргуппе.
netStream = new NetStream(netConnection, groupSpecifier.groupspecWithAuthorizations());
далее все как и раньше.
netStream.publish() или netStream.play()
Что еще важно знать о группах. В группы можно постить сообщения. Причем, передавать можно достаточно приличные объемы данных. К примеру, если ты хочешь передавать видео не с камеры а из файла, то тут иначе просто не обойтись. Нельзя закачать видео даже в flv формате в NetStream так, что бы он начал его транслировать, удивительно но факт: Его сперва нужно передать в виде сообщения а потом на принимающей стороне загнать в NetStream. Делается все это примерно так:
var message:Object = new Object();
message.seq = ++sec;
message.peer = netConnection.nearID;
message.text = "Some text message";
netGroup.post(message);
В сообщение мы добавляем некое инкрементирующееся значение sec для обеспечения уникальности сообщений. Дело в том, что одно и тоже сообщение не может быть доставлено более одного раза, а внутреннего механизма контроля версий не предусмотрено, поэтому, если мы два раза отправим сообщение с одним и тем же набором полей и их значений то на второй раз оно просто не дойдет. Принимающая сторона ловит сообщения.
public function netStatusHandler(event:NetStatusEvent):void{
...
case "NetGroup.Posting.Notify":
if(event.info.message.hasOwnProperty('peer'))
{
trace(event.info.message.text);
}
break;
...
}
вот, в общих чертах все. Если будет интересно, полазай по вот этим полезным ссылочкам, там раскрыта куча полезных фишек и подробностей...
Антон открыл глаза и подскочил с постели. Эх, ну и приснится же, девушка-программист, рассказывающая про какую-то хрень. Как там его, RTMFP, хах, реалистичное название. Уф. Он вытер со лба холодный пот и подошел к компьютеру. Так, гугл, интересно, есть на самом деле такое сокращение? RTMFP... О, блин, и правда что-то есть! Real Time Media Flow Protocol — и правда p2p для флеша... Но... но этого же просто не может быть! Антон почувствовал, как пол уходит куда-то из-под ног. Так, какие она там кидала ссылки?
2 комментария:
хм беглым глазом пробежал..очень хорошая статья и буду первый раз изучать данный протокол из поверхностных ваших примеров. Вот интересует к какому сервер все же шел коннект? к серверу пиров adobe или нужно свой сервер подымать и какая будет нагрузка?
хм беглым глазом пробежал..очень хорошая статья и буду первый раз изучать данный протокол из поверхностных ваших примеров. Вот интересует к какому сервер все же шел коннект? к серверу пиров adobe или нужно свой сервер подымать и какая будет нагрузка?
Отправить комментарий