Реализация DVR-функций в flash видео плеере

В данной статье детально рассматривается разработка видео флэш плеера с возможностями использования DVR-функций, таких как функция «TimeShift» и функция постановки на паузу «живого» потока.

В основе, представленного здесь, видео флэш плеера лежит компонент от компании Adobe – FLVPlayback 2.5, а также классы: DynamicStream, DynamicStreamItem.

Создаем новый объект vid, который является экземпляром класса FLVPlayback, а также контейнер для него — объект container типа UIComponent. Помещаем в контейнер объект vid с помощью метода addChild.

\\Создаем новый экземпляр класса FLVPlayback
var vid = new FLVPlayback();
\\Создаем новый экземпляр класса UIComponent
var container:UIComponent = new UIComponent();
container.addChild(vid);

Свойствам объекта vid, присваиваем значения для начала работы с DVR-функциями.

vid.isDVR = true; \\Если поток DVR, то true
vid.isLive = true; \\Если поток «живой», то true
vid.dvrSnapToLive = true; \\Если можно перемещаться к концу, то true
vid.dvrFixedDuration = false; \\Если постоянная длинна, то true
vid.dvrIncrement = 1800;
vid.dvrIncrementVariance = 300;

Добавляем обработчики событий (METADATA_RECEIVED, CUE_POINT, STATE_CHANGE, PROGRESS, PLAYHEAD_UPDATE) объекту vid:

vid.addEventListener(MetadataEvent.METADATA_RECEIVED, onMetaData);
vid.addEventListener(VideoEvent.STATE_CHANGE, onVideoStateChange);
vid.addEventListener(VideoProgressEvent.PROGRESS, progressEventHandler);
vid.addEventListener(VideoEvent.PLAYHEAD_UPDATE, onPlayheadUpdate);

Функция «TimeShift»

DVR-функция «TimeShift» представляет собой возможность воспроизведения «живого» потока с осуществлением функции «перемотки».
Добавляем таймер для исчисления диапазона времени, по которому можно производить перемотку потока назад, и в рамках которого будет осуществляться функция постановки потока на паузу. Для этого создаем новый объект timerNow типа Timer. Добавляем событие, которое через каждую секунду вызывает функцию onTimerNowTick.

var timerNow:Timer = new Timer(1000, 0);

timerNow.addEventListener(TimerEvent.TIMER, onTimerNowTick); Функция onTimerNowTick осуществляет подсчет временного диапазона, присвоение объектам timeEnd , timeStart значений, а также смещает временное окно на секунду назад путем изменения свойств maximum и minimum объекта slider.

private function onTimerNowTick(evt:TimerEvent):void
{
bufferCurrentTime = String(DynamicStream(vid.getVideoPlayer
(vid.activeVideoPlayerIndex).netStream).bufferLength);
if(!isSliderDragging)
{
totalTime++;
slider.maximum = totalTime;
slider.minimum = totalTime - 3600;
}
var timeNOW:Date = new Date();
var h:Number = timeNOW.getHours();
var m:Number = timeNOW.getMinutes();
var s:Number = timeNOW.getSeconds();
timeEnd = (h == 0 ? "00" + ":" : (h < 10 ? "0" + h.toString()
+ ":" : h.toString() + ":"))
+ (m < 10 ? "0" + m.toString() : m.toString()) + ":"
+ (s < 10 ? "0" + s.toString() : s.toString());
if(h == 0) h = 23; else h--;
timeStart = (h == 0 ? "00" + ":" : (h < 10 ? "0" + h.toString()
+ ":" : h.toString() + ":"))
+ (m < 10 ? "0" + m.toString() : m.toString())
+ ":" + (s < 10 ? "0"
+ s.toString() : s.toString());
}

Описываем обработчик события при получении метаданных. Присваиваем totalTime значение максимальной длительности записанного потока в текущий момент, а также корректируем свойства maximum и minimum объекта slider. Получение метаданных происходит один раз при подключении к потоку по инициативе серверной части.

private function onMetaData(evt:MetadataEvent):void
{
totalTime = evt.info.duration;
slider.maximum = totalTime;
slider.minimum = totalTime - 3600;

}

Описываем обработчик события при изменении состояния потока. При получении события PLAYING (начало воспроизведения) добавляем объекту netStream обработчик события NET_STATUS.

private function onVideoStateChange(evt:VideoEvent):void
{
switch(vid.state)
{
case VideoState.PLAYING:
\\Если статус «воспроизведение»
bufferingCanvas.visible = false;
DynamicStream(vid.getVideoPlayer
(vid.activeVideoPlayerIndex).netStream)
.addEventListener(NetStatusEvent.NET_STATUS, onNSStatus);
break;
case VideoState.BUFFERING:
\\Если статус «буферизация»
break;
case VideoState.STOPPED:
\\Если статус «остановлено»
break;
case VideoState.PAUSED:
\\Если статус «пауза»
break;
case VideoState.CONNECTION_ERROR:
\\Если статус «ошибка соединения»
break;
case VideoState.DISCONNECTED:
\\Если статус «соединение закрыто»
break;
case VideoState.LOADING:
\\Если статус «загрузка»
break;
case VideoState.REWINDING:
\\Если статус «перемотка»
break;
}
}

Описываем обработчик события при изменении playheadTime потока. Корректируем переменную currentTime, а также позицую объекта slider.

private function onPlayheadUpdate(evt:VideoEvent):void
{
if(!isSliderDragging)
slider.value = evt.playheadTime;
currentTime = evt.playheadTime;
if(!timerNow.running)
timerNow.start();

} Функция toggleDragging вызывается при перемещении ползунка объекта slider. Если ползунок захвачен, то в эту функцию передается значение true, в противном случае false. Полученное значение входящего параметра присваиваем переменной isSliderDragging. Если полученное значение не true, то вызываем функцию doSeek.

private function toggleDragging(param:Boolean):void
{
isSliderDragging = param;
if (!param)
doSeek();
}

Функция doSeek осуществляет вызов метода seek объекта vid, и передачу этому методу текущие значение свойства value объекта slider. После вызова этого метода осуществляется «прыжок» воспроизведения на указанную секунду.

private function doSeek():void
{
try{
vid.seek(slider.value);
}catch(e:Error){
}
}

Функция постановки на паузу «живого» потока

Описываем обработчик события при изменении состояния netStream. Если воспроизведение потока находится в состоянии пауза, то присваиваем значение true переменной pausedState, в противном случае значение false.

private function onNSStatus(evt:NetStatusEvent):void
{
switch (evt.info.code)
{
case "NetStream.Pause.Notify":
pausedState = true;
break;
case "NetStream.Unpause.Notify":
pausedState = false;
break;
}
}

Функция onPlay вызывается каждый раз при нажатии на кнопку btnPlay. Если текущий активный VideoPlayer объекта vid не равен null, то в зависимости от состояния переменной pausedState осуществляем постановку на паузу, либо начинаем воспроизведение видео потока. Если же текущий активный VideoPlayer объекта vid равен null, то вызываем метод play2, объекта vid, и передаем ему ссылку на объект dsi. После вызова этого метода, будет осуществлено соединение с FMS сервером и начато воспроизведение потока, указанного в dsi.

 private function onPlay():void
 {
 if(DynamicStream(vid.getVideoPlayer(vid.activeVideoPlayerIndex)
 .netStream) != null)
 {
 switch (pausedState)
 {
 case true:
 btnPlay.label = "Play";
 DynamicStream(vid.getVideoPlayer
 (vid.activeVideoPlayerIndex).netStream).resume();
 break;
 case false:
 btnPlay.label = "Pause";
 DynamicStream(vid.getVideoPlayer
 (vid.activeVideoPlayerIndex).netStream).pause();
 break;
 }
 }else{
 vid.play2(dsi);
 }
 }