FMS. Часть 2. Программирование на стороне клиента.

Tags:

Вступление

Разрабатывать под FMS можно двумя способами:

  • по-простому, не заморачиваясь
  • по-сложному, как следует заморочившись :)

Надо сказать, что Адоб сделал все возможное, чтобы первый способ работал. Действительно, можно писать полноценные мультимедиа и многопользовательские приложения почти не разбираясь в FMS, мельком проглядев документацию. Порог вхождения в разработку очень низкий.

Адоб постарался, чтобы максимум возможностей были доступны из клиентского кода, и чтобы разработчик мог избегать написания серверного кода. Учитывая, что целевая аудитория Адоба -- флэш-разработчики -- в основном специалисты по клиентской части, и не многие из них хотят и могут писать серверный код, то такой подход вполне оправдан.

Но уж если пришлось писать серверный код, то и тут можно не заморачиваться, не вникать в особенности языка ServerSide ActionScript. Можно просто просмотреть документацию и примеры кода, и дальше самому писать по наитию. Это работает :) Но если уж хочется заморочиться, то можно копнуть глубже.

Итак, на стороне клиента нас интересуют 6 классов: NetConnection, NetStream, SharedObject, Camera, Microphone, Video.

Класс NetConnection обеспечивает соединение с FMS сервером. Соединение позволяет вызывать методы на стороне сервера и представляет серверу возможность вызывать методы у себя.

Внутри одного соединения может быть неограниченное количество потоков NetStream. Поток может передавать видео, аудио и другие бинарные данные, но только в одном направлении. Чтобы передавать и получать данные нужно как минимум два потока.

Обычно создается один экземпляр NetConnection и нужное количество экземпляров NetStream. Хотя никто не запрещает создать несколько NetConnection к разным FMS-приложениям и разным серверам.

Класс SharedObject позволяет работать не только с клиентскими SharedObjects, но и серверными. Хотя API у них немного разные.

Классы Camera и Microphone, очевидно, позволяют захватить видео с вебкамеры и звук с микрофона, и через NetStream публиковать их на сервер. Класс Video позволяет показать видео, полученное с сервера.

Вот и все, собственно.

Установка соединения

Установка соединения с FMS сервером -- штука простая. Главное знать URI, куда соединяться. А URI этот выглядит так:

protocol://host:port/appname/instanceName

Протокол может быть один из: rtmp, rtmpe, rtmps, rtmpt, and rtmpte.

Хост -- доменное имя или IP адресс

Порт можно не указывать если он дефолтный (1935)

appname -- имя приложения на fms сервере

instanceName -- экземпляр приложения

Одно приложение может иметь несколько экземпляров, которые работают независимо друг от друга. Каждый экземпляр имеет своих клиентов, потоки, SharedObjects и т.д.


package
{
import flash.display.Sprite;
import flash.events.IOErrorEvent;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.net.NetConnection;
import flash.net.ObjectEncoding;

public class Sample_01_Connect extends Sprite
{
	public var nc : NetConnection;

	// constructor
	public function Sample_01_Connect()
	{
		nc = new NetConnection();
		//nc.objectEncoding = ObjectEncoding.AMF0;
		
		nc.addEventListener(NetStatusEvent.NET_STATUS, OnNetStatus);
		nc.addEventListener(IOErrorEvent.IO_ERROR, OnIOError);
		nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, OnSecurityError);

		nc.connect('rtmp://localhost/test', {});
		//nc.close();
	}

	protected function OnNetStatus(e:NetStatusEvent):void
	{
		switch(e.info.code)
		{
			case "NetConnection.Connect.Success":
				trace('Connection to ' + nc.uri + ' success');
				break;
			case "NetConnection.Connect.Rejected":
				trace('Connection to ' + nc.uri + ' rejected');
				break;
			case "NetConnection.Connect.Failed":
				trace('Failed connect to ' + nc.uri);
				break;
			case "NetConnection.Connect.Closed":
				trace('Connection to ' + nc.uri + ' closed');
				break;
			default: trace(e.info.code);
		}
	}

	private function OnIOError(e:IOErrorEvent):void
	{
		trace("IO error: " + e.text);
	}

	private function OnSecurityError(e:SecurityErrorEvent):void
	{
		trace("Security error: " + e.text);
	}
}
}

Как видите, код простой и очевидный, комментировать тут особо нечего.

Проигрывание видео

Эта задача хорошо знакома любому флэшеру. Наверное, нет такого флэш-разработчика, который не написал бы свой видеоплейер :) Так что примера кода тут не будет. Зато будет некоторая специфика FMS.

После того, как мы соеденились с FMS сервером, можно создавать экземпляр NetStream и вызывать метод play( name:Object, start:Number, len:Number, reset:Object):void

Обязательный параметр только первый -- имя файла. Далее можно задать, с какой позиции проигрывать файл и какую часть файла проигрывать. Последний параметр связан с плейлистами.

Плейлисты -- фича, специфичная для FMS. Фишка в том, что можно вызвать play несколько раз подряд, передавая четвертым параметром false (кроме первого вызова). Новые видеофайлы будут становится в очередь, формируя плейлист.


stream = new NetStream(nc);
stream.play( "advertisement", 0, 30 );
stream.play( "video1", 10, -1, false );
stream.play( "video2", 0, -1, false );
stream.play( "video3", 30, 120, false);

Немаловажная задача -- определение длинны видео файла. Это обязательно нужно, чтобы реализовать ползунок для отображения и изменения текущей позиции воспроизведения. Обычно длину видео берут из метаданных. Но может статься, что она там не будет указана (это зависит от того, каким софтом создавался файл).

Но у FMS есть другой способ. Длину видео можно определить на стороне сервера следующим кодом


var length = Stream.length("flv_file")

и потом передать эту инфу клиенту.

Публикация и подписка

Это тоже очень простая задача. Опять же, после соединения с сервером можно создавать NetStream, захватывать камеру и микрофон и публиковать с помощью следующего кода:


var cam : Camera = Camera.getCamera();
var mic : Microphone = Microphone.getMicrophone();

var ns : NetStream = new NetStream(nc);
ns.attachCamera(cam);
ns.attachAudio(mic);

ns.publish("myStream", "live");

На публикуемый поток могут подписаться много других клиентов, получать и отображать его.


var ns : NetStream = new NetStream(nc);
ns.play("myStream");

var vid : Video = new Video();
vid.attachNetStream(nsPlayer);

addChild(vid);

Вместо публикации видео в live его можно записывать в файл. Разница будет только во втором параметре метода publish()


ns.publish("myStream", "record");

Вместо видео можно передавать бинарные данные, для этого нужно использовать метод send(). Правда для этого NetStream все равно должен что-то публиковать, и данные будут получать те, кто подписан на эту публикацию.


// publisher code
var ns : NetStream = new NetStream(nc);
ns.publish("myStream", "live");
ns.send("SomeHandler", argument1, argument2);

// subscriber code
ns : NetStream = new NetStream(nc);
ns.client = new NsListener();
ns.play("myStream");

class NsListener()
{
	public function SomeHandler(argument1 : Object, argument2 : Object) : void
	{
		trace('SomeHandler ' + argument1 + ' ' + argument2);
	}
}

Работа с ServerSide SharedObject

Для работы с ServerSide SharedObject тоже нужно установленное соединение с FMS и экземпляр объекта NetConnection.


var nc : NetConnection;

so : SharedObject = SharedObject.getRemote('myData', nc.uri, persistence);

so.addEventListener(SyncEvent.SYNC, OnSyncEvent);
so.addEventListener(NetStatusEvent.NET_STATUS, OnNetStatus);
so.addEventListener(AsyncErrorEvent.ASYNC_ERROR, OnAsyncError);

so.connect(nc);

//so.close();

private function OnSyncEvent(e:SyncEvent):void
{
	for(var i:int = 0; i < e.changeList.length; i++)
	{
		var desc:Object = e.changeList[i];
		trace(desc.name + ' ' + desc.code);
	}
}

private function OnNetStatus(e:NetStatusEvent):void
{
}

private function OnAsyncError(e:AsyncErrorEvent):void
{
}

В отличие от локального SharedObject, читать данные нужно с помощью метода getProperty() а писать с помощью setProperty().

Тут важно обработать SyncEvent. Оно срабатывает один раз, когда устанавливается соединение с ShareObject, и потом срабатывает каждый раз, когда какой-либо клиент меняет там данные.

SyncEvent содержит массив объектов со следующими данными:

  • name -- имя измененного свойства;
  • oldValue -- прежнее значение измененного свойства;
  • code -- код изменения.

Коды могут быть следующие:

  • success -- изменение свойства принято сервером;
  • reject -- изменение свойства отвергнуто сервером;
  • change -- свойство изменено другим клиентом;
  • delete -- свойство было удалено;
  • clear -- все свойства были удалены (shared object очищен).

Comments

Я работаю с wowza у вас он не описан,но думаю принципиальной разницы быть не должно.

У меня стоит задача сделать текстуру с видео и наложить ее на грань трехмерного объекта
Для этого, прежде когда я работал с обычным, не потоковым видео использовал bitmapdata.draw

При попытке использовать потоковое видео столкнулся со следующей проблемой:
Нарушение изолированной среды: BitmapData.draw: .../some.swf не может осуществить доступ к rtmp://video.somedomain.net:1935/vod/. Не предоставлен доступ к файлам политики.

Насколько я понял из туториалов все дело в настройках приложения application.xml
в узле <клиент> нужно добавить звездочки в к видео и аудио узлам
"<.Client.>
<.IdleFrequency.>-1<./IdleFrequency.>
<.Access>
<.StreamReadAccess.>*
<.StreamWriteAccess>*<./StreamWriteAccess.>
<.StreamAudioSampleAccess>*<./StreamAudioSampleAccess>
<.StreamVideoSampleAccess>*<./StreamVideoSampleAccess>
<.SharedObjectReadAccess>*<./SharedObjectReadAccess>
<.SharedObjectWriteAccess>*<./SharedObjectWriteAccess>
<./Access>
<./Client>"

Я менял этот application в трех места в корневом conf в conf/vod а также в applications/vod (хотя я вроде бы читал, что все настройки приложений должны быть в conf) К сожалению мне это не помогло (

Add new comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
CAPTCHA
question for bots )
Image CAPTCHA
Enter the characters shown in the image.