четверг, января 31, 2008

Видео во Flash/Flex. Проблемы позиционирования.

Задачу быстро сделать проигрыватель видео (управление видео-презентацией) я оценил в пару часов. Т.к. совсем недавно разобрался с воспроизведением высококачественного видео, с тех-частью проблем не должно было возникнуть. Проект начал делать на Flex, дабы лишний раз попрактиковаться.

Воспроизводить видео действительно получилось быстро. Класс VideoDisplay, управляемый несколькими кнопками и слайдером.
И тут встали более сложные задачи.
  1. Необходимо в определенные моменты времени останавливать видео (презентация).
  2. Небходима навигация по этим стоп-точкам.

Первая задача создала одну основную проблему: Получить видео-файл, содержащий сигнальные-точки (cue points).
Видео мы выгоняли из AfterEffects в MOV кодеком H.264. Можно ли туда загнать сигнальные-точки (которые, собственно были расставлены в тайм-лайне) и как это сделать - не смогли выяснить.
Решили экспортировать в FLV (задрав качество до порядка 3000) - расставить сигнальные точки пришлось вручную в "Adobe Flash CS3 Video Encoder". В принципе, была идея выгнать видео в MOV (высокое качество) и FLV (минимального качества), использовать сигнальные точки FLV а воспроизводить MOV.
Для получения сигнальных точек использую следующий код:
private function onMeta(event:MetadataEvent):void {
this.slider.maximum=event.info.duration;
this.__cue_points=event.info.cuePoints;
}
. . .
<mx:VideoDisplay metadataReceived="this.onMeta(event)" . . . >
. . .

Таким образом, по событию metadataReceived - когда Flex получает метаданные видео-файла - мы получаем массив сигнальных точек.
Внимание - свойство cuePoints класса VideoDisplay не будет содержать сигнальные точки, которые находятся в метаданных видео-файла. Это свойство нужно для "ручной" установки сигнальных точек. Управление (добавление/удаление) сигнальными точками cuePoints осуществляется менеджером сигнальных точек cuePointManager. Можно задать их прямо во Flex:
<mx:VideoDisplay . . . >
<mx:cuePointManagerClass>mx.controls.videoClasses.CuePointManager</mx:cuePointManagerClass>
<mx:cuePoints>
<mx:Array>
<mx:Object time="0" name="point1"/>
<mx:Object time="3.034" name="point2"/>
<mx:Object time="4.774" name="point3"/>
<mx:Object time="8.344" name="point4"/>
<mx:Object time="12.271" name="point5"/>
</mx:Array>
</mx:cuePoints>
</mx:VideoDisplay>

Итак, мы можем контролировать сигнальные точки. Переходим к обработке пауз на этих точках. Событие cuePoint отлично справляется с нашей задачей:
private function onCue(event:CuePointEvent):void {
this.video_display.pause();
}
<mx:VideoDisplay id="video_display" cuePoint="this.onCue(event)" . . . >

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

Задача номер два. А теперь начинается самое нехорошее. Мы имеем массив точек (каждый элемент содержит объект с двумя свойствами: name - имя маркера, time - время в секундах), и можем позиционировать воспроизведение видео по его элементам. Казалось бы, чего проще:
private function onClickNext(event:FlexEvent):void {
this.video_display.playheadTime=cue_points[position].time;
}

Ан нет. Видео позиционируется не точно в указанную позицию - то перескочет на пару секунд, то недотянет. Причем точность настолько низка, что нам пришлось отказаться от такого способа позиционирования.
По этому поводу, в документации Flex к свойству playheadTime сказано, что для FLV-файла позиционирование происходит в ключевой фрейм, ближайший к указанной позиции, причем эти ключевые фреймы формируются во время кодирования видео. Поэтому, извиняйте, но точно спозиционироваться в указанное место не получится, если там по близости нет ключевого фрейма. Попробовал позиционировать по тем же контрольным точкам MOV файл. Та же петрушка.
Эксперименты показали, что если видео экспортировать в SWF и позиционироваться по фреймам тайм-лайна, точность позиционирования абсолютна.

Раз нельзя реализовать нормальную навигацию по подгружаемому MOV/FLV видео, то дальнейшая разработка этого проекта на Flex потеряла всякий смысл.
Проще было импортировать видео в SWF, загнать его в мой старый, проверенный презентационный AS2 движок и обработать массив фреймов (составленный вручную за 15 минут), что и было сделано примерно за час.

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

4 комментария:

Unknown комментирует...

Подскажите, пожалуйста, у меня такая проблема при использовании VideoDisplay для проигрывыния локальных файлов (ОС ВИСТА):

SecurityError: Error #2148: SWF-файл file:///D|/Users/Di/Documents/Учеба/8%2Dой%20семестр/МТ/Сайт/flex/bin%2Drelease/main.swf не может осуществить доступ к локальному ресурсу file:///D|/Users/Di/Documents/Учеба/8%2Dой%20семестр/МТ/Сайт/flex/bin%2Drelease/../pages/video.flv. Доступ к локальному ресурсу могут осуществлять только SWF-файлы из local-with-filesystem и доверенные локальные SWF-файлы.
at flash.net::NetStream/play()
at mx.controls.videoClasses::VideoPlayer/_play()
at mx.controls.videoClasses::VideoPlayer/setUpStream()
at mx.controls.videoClasses::VideoPlayer/_load()
at mx.controls.videoClasses::VideoPlayer/load()
at mx.controls::VideoDisplay/load()
at mx.controls::VideoDisplay/autoPlaying()
at mx.controls::VideoDisplay/creationCompleteHandler()
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.core::UIComponent/dispatchEvent()
at mx.core::UIComponent/set initialized()
at mx.managers::LayoutManager/doPhasedInstantiation()
at Function/http://adobe.com/AS3/2006/builtin::apply()
at mx.core::UIComponent/callLaterDispatcher2()
at mx.core::UIComponent/callLaterDispatcher()


То есть из за какой то ошибки не дает доступа к ролику. Не подскажите, что делать?

Unknown комментирует...

Да тут, пожалуй, всё просто - это нарушение локальной политики безопасности.
Для того, чтобы воспроизводить локальные файлы локально, нужно в директивах компилятора добавить
-use-network=false (настройки проекта -> Flex Compiler -> Additional compiler arguments). Я с этим сталкивался здесь: http://racer242.blogspot.com/2008/03/flex.html

Анонимный комментирует...

вообще-то можно задавать количество i-фреймов при кодировании. я так понимаю, что скачок происходит именно к ближайшему такому кадру, и, соответственно, чем они стоят чаще, тем точность попадания будет выше!)))

Unknown комментирует...

Да, возможно это поможет. Объем правда возрастет. Вообще, конечно, в вопросах позиционирования flv-видео совсем не надежно, не говоря уже об обратном воспроизведении видео.
Лучше в таких случаях секвенциями пользоваться, но с ними проблем тоже хватает - объемы и потребляемая память.