Статьи

Делитесь медиа в Твиттере, используя Flex, Часть III: Видео

Это третья и последняя часть нашей публикации в Media с использованием серии Flex (см. Часть I и Часть II ). В первой статье мы показали, как создать приложение Twitter с помощью инфраструктуры Flex в Flash Builder, импортируя обложки Photoshop в Flash Catalyst для создания интерфейса и его взаимодействий. Во второй статье рассказывалось о процессах и библиотеках, необходимых для загрузки изображений в Flickr и использования API bit.ly для сокращения ссылок на эти изображения. В этой последней статье этой серии мы расширим приложение для загрузки видео на Flickr и будем использовать несколько встроенных функций Flex, чтобы получать скриншоты из видео и загружать их.

Как только вы закончите, не забудьте проверить свои знания в нашей статье викторины !

В предыдущей части мы обсудили потенциальные способы размещения изображений в Интернете для сопровождения твитов. Мы решили, что самым простым подходом было загрузить их на Flickr, создать свернутую ссылку на страницу Flickr с помощью API bit.ly, а затем включить эту ссылку в твит. Этот подход работал хорошо, поэтому мы сделаем то же самое для видео.

Прежде чем мы начнем, вы должны скачать архив кода, чтобы следовать вместе с. В этот раз мы будем работать с cheepcheep_video_flashbuilder.fxp в папке Flex projects (там также есть и предыдущие версии приложения, так что вы можете увидеть, что изменилось). Импортируйте этот файл в Flash Builder, и вы готовы начать работу.

Загрузка видео на Flickr

Наша первая задача — изменить фильтр, используемый элементом FileReference для включения расширений видеофайлов, поддерживаемых Flickr. Мы добавили avi, wmv, mov, mpg, mp4, m4v и 3gp в FileFilter который мы создали в предыдущей статье. Хотя все эти расширения официально поддерживаются Flickr, существует риск, что Flickr не сможет кодировать некоторые файлы; Это связано с тем, что видео может быть сжато различными кодеками, и поддерживаются только наиболее часто используемые (например, H.264). Мы рекомендуем вам проверить документацию Flickr на предмет того, что поддерживается.

Flickr позволяет загружать видео продолжительностью до 90 секунд и размером до 150 МБ для бесплатных учетных записей или до 500 МБ для учетных записей Pro. Понимая, что видеофайлы могут быть больше изображений, мы настроили оригинальный интерфейс, добавив дополнительный компонент Label для отображения размера выбранного файла. Мы также добавили кнопку для просмотра параметров видео, если выбран видеофайл. Мы рассмотрим эту кнопку более подробно позже; сейчас просто обратите внимание, что по умолчанию он отключен.

Функция fileAccessed , вызываемая после выбора файла, была изменена для получения дополнительной информации: размера файла (который мы отображаем с нашей новой Label ) и его расширения. FileReference имеет свойство type, но оно ненадежно (например, оно не может определить расширение файла GIF), поэтому мы используем нашу собственную переменную. Чтобы получить расширение, мы разбиваем имя файла в каждом периоде и получаем последний элемент полученного массива. Мы также добавили массивы в верхней части основного приложения для хранения приемлемых расширений для фото и видео файлов:

 private var photoExtensions:Array = ["gif","jpeg","jpg","png"];  private var videoExtensions:Array = ["avi","wmv","mov","mpg","mp4","m4v","3gp"];   ...   private function fileAccessed(evt:Event):void {   photoFileSize.text = (Number(fileReference.size)/100) + "kb";   photoFileName.text = fileReference.name;   var filename:Array = fileReference.name.split(".");   flickrUploadType = filename[filename.length - 1];   photoUploadBtn.enabled = true;  } 

Успешная загрузка в Flickr вызовет функцию uploadCompleteHandler . Мы изменили эту функцию, чтобы назначить идентификатор загруженного носителя новой переменной flickrUploadID . Мы также проверяем расширение файла, чтобы увидеть, находится ли он в массиве принятых расширений видео. Если это так, мы включаем новую кнопку Параметры видео . Остальная часть этой функции, которая захватывает URL-адрес загрузки с Flickr и передает ее нашему сервису bit.ly, не изменилась по сравнению с предыдущей статьей:

 private function uploadCompleteHandler(evt:DataEvent):void {   CursorManager.removeBusyCursor();   var xData:XML = new XML(evt.data);   flickrUploadID = xData.photoid;   if ( videoExtensions.indexOf(flickrUploadType) != -1 ) {     videoOptionsBtn.enabled = true;   }   photoUrl = "http://www.flickr.com/photos/"+flickrNsid+"/"+xData.photoid;   bitlyService.send();  } 

Извлечение видео из Flickr

Мы собираемся использовать кнопку « Параметры видео» , чтобы перейти в новое состояние ( videoOptions ), которое мы добавили в приложение. Это состояние отображает новый компонент, который мы создали с помощью Flash Catalyst: его цель — отображать загруженное видео, чтобы пользователь мог также сделать снимок видео в качестве изображения для загрузки. Однако, прежде чем перейти в это состояние, нам нужно извлечь видео с Flickr для воспроизведения. Поэтому мы напишем одну функцию для отправки запроса в Flickr при нажатии кнопки, а другую — для переключения состояния приложения при получении ответа.

Первая функция, которую мы назвали showVideoOptions , вызывает метод getSizes API Flickr, используя ту же библиотеку Flickr ActionScript, которая использовалась в предыдущей статье. Он также устанавливает прослушиватель событий для обработки ответа API:

 private function showVideoOptions():void {   flickr = new FlickrService(flickrApiKey);   flickr.addEventListener(FlickrResultEvent.PHOTOS_GET_SIZES, handleVideoData);   flickr.photos.getSizes(flickrUploadID);  } 

Теперь давайте посмотрим на наш обратный вызов, функцию handleVideoData . Если загрузка все еще обрабатывается Flickr, то FlickrResultEvent , переданный этой функции, будет содержать ошибку, уведомляющую нас о том, что видео не было найдено. С другой стороны, если видео было обработано, результат будет содержать массив размеров фотографий. Одним из «размеров фотографий» на самом деле является видео MP4.

Наша первая задача — выяснить, нет ли в полученных данных ошибки. Мы можем сделать это с помощью встроенной функции hasOwnProperty . Если мы обнаружим ошибку, мы отобразим Alert для пользователя, чтобы сообщить ему, что видео все еще обрабатывается. В противном случае мы переключаемся в состояние videoOptions чтобы отобразить новый компонент, а затем переходим по массиву photoSizes ища значение «Site MP4» (которое является строкой «size», которую Flickr назначает для видео MP4). Затем мы передаем свойство source этого индекса массива в свойство videoLocation нашего пользовательского компонента и вызываем метод setVideoPlayer компонента:

 private function handleVideoData(evt:FlickrResultEvent):void {   if ( evt.data.hasOwnProperty("error") ) {     Alert.show("It appears that Flickr is still processing your video,     please wait another minute and try again", "Video not available");   } else {     currentState = "videoOptions";     var len:Number = evt.data.photoSizes.length;     for (var i:Number = 0;i<len;i++) {       if ( evt.data.photoSizes[i].label == "Site MP4" ) {         customcomponent31.videoLocation = evt.data.photoSizes[i].source;         customcomponent31.setVideoPlayer();         break;       }     }   }  } 

Отображение видео

  CustomComponent3 имеет компонент VideoElement расположенный в макете компонента с использованием Group .  VideoElement - это новый компонент, представленный в Flex 4: это просто версия Chrome без компонента VideoPlayer .  Это действительно удобно для захвата видеокадров, так как нет необходимости убирать управление плеером.  Элемент Group необходим по причинам, которые я объясню в ближайшее время. 

Функция setVideoPlayer , которую мы вызвали из handleVideoData , назначает URL-адрес видео свойству VideoElement компонента VideoElement . Это заставит компонент загрузить и воспроизвести видео. Мы также используем компонент ProgressBar , установленный в indeterminate режим, чтобы дать пользователю визуальную подсказку о том, что файл загружается. Чтобы скрыть индикатор выполнения после завершения загрузки, нам нужно добавить новый прослушиватель событий в функцию init компонента. Слушатель вызывает функцию videoLoadProgress , которая скрывает индикатор выполнения и включает снимок:

 private function init():void {   ...   videoPlayer.addEventListener(ProgressEvent.PROGRESS,videoLoadProgress);   ...  }   ...   private function videoLoadProgress(evt:ProgressEvent):void {   if ( evt.bytesLoaded == evt.bytesTotal || videoPlayer.playing ) {     videoProgress.visible = false;     snapshotBtn.enabled = true;   }  } 

Нам нужен еще один обработчик событий, чтобы включить кнопку воспроизведения после завершения воспроизведения видео. Мы добавим еще одного слушателя в функцию init (используя событие videoElement.COMPLETE ) и создадим метод с именем enableReplayBtn , который просто устанавливает значение свойства enabled кнопки в true . Может показаться, что было бы проще просто добавить встроенный слушатель к videoElement , поскольку наша функция enableReplayBtn - это всего лишь одна строка кода. Тем не менее, в целях сохранения читабельности и удобства сопровождения нашего кода, имеет смысл размещать всех наших слушателей в одном месте.

Снимки

Кнопка Snapshot вызывает функцию frameGrab , которую я перечислил ниже. Чтобы сделать снимок видео, мы используем функцию встроенного вспомогательного класса ImageSnapshot . Вместо того, чтобы фактически захватывать кадр непосредственно из видео, captureImage просто возвращает текущее изображение, отображаемое элементом, на который вы указываете. Вот почему мы использовали VideoElement вместо VideoPlayer : мы бы предпочли не брать элементы управления плеером вместе со скриншотом.

Класс ImageSnapshot может быть ImageSnapshot из любого компонента, который реализует класс flash.display.IBitmapDrawable . Это включает Flex UIComponents, но не включает VideoPlayer или VideoElement ; Вот почему мы обернули VideoElement в Group .

Мы присваиваем данные, захваченные функцией captureImage , переменной (чтобы мы могли получить к ней доступ позже из основного приложения), типизируя их как ByteArray используя ключевое слово as . Затем мы назначаем эту переменную атрибуту источника нового компонента Image чтобы отобразить предварительный просмотр снимка. Наконец, мы включаем кнопку « Загрузить» :

 private function frameGrab(evt:Event):void {   var imageSnap:ImageSnapshot = ImageSnapshot.captureImage(videoGroup);   snapshotPreview.source = imageSnap.data as ByteArray;   uploadSnapshotBtn.enabled = true;  } 

С этим кодом пользователи могут просматривать видео, воспроизводить его так часто, как им нравится, и делать его снимки во время воспроизведения. Далее мы добавим функциональность, которая позволит им загружать текущий снимок на Flickr.

Загрузка ByteArray как изображения

Класс Upload используемой нами библиотеки Flickr был разработан для получения файла для загрузки из файловой системы пользователя. Для загрузки фотографий мы использовали элемент FileReference . Однако снимок, который мы записываем, является не файлом, а значением, которое существует в памяти и недоступно для элемента FileReference . Итак, как мы можем загрузить его?

Нет простого решения, но, к счастью для нас, другой человек столкнулся с этой проблемой и изменил класс Upload в библиотеке Flickr, чтобы добавить необходимые функции ( http://blog.dannypatterson.com/?p=250 ). Мы скопировали метод uploadBytes из этого сообщения в блоге и добавили его в файл Upload.as (расположенный в com.adobe.webapis.flickr.methodgroups ). Эта функция позволяет нам загружать наш ByteArray как если бы это было изображение. Нам нужно немного изменить функцию, добавив прослушиватель событий, который будет запущен после завершения загрузки. Этот слушатель вызывает другую новую функцию, которую мы также добавили в Upload.as : uploadBytesComplete . Мы используем его для отправки события, чтобы мы могли перехватить его в нашем приложении после завершения загрузки изображения:

 public function uploadBytes(...) : Boolean {   ...   loader.addEventListener(Event.COMPLETE, uploadBytesComplete);   ...  }   private function uploadBytesComplete(event:Event):void {   var result:FlickrResultEvent = new FlickrResultEvent(PHOTOS_UPLOAD_BYTES_COMPLETE );   MethodGroupHelper.processAndDispatch( _service, URLLoader( event.target.loader ).data, result,"photoid", MethodGroupHelper.parseUploadBytesResult );  } 

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

Чтобы использовать эту добавленную функциональность, мы написали новую функцию в нашем главном файле приложения: uploadVideoCap . Эта функция очень похожа на uploadFlickr , за исключением того, что мы используем uploadBytes вместо upload . Вы заметите, что мы устанавливаем flickr.permission для «записи». Это необходимо для uploadBytes функции uploadBytes :

 public function uploadVideoCap(capData:ByteArray):void {   flickr = new FlickrService(flickrApiKey);   flickr.secret = flickrSecret;   flickr.token = flickrAuthToken;   flickr.permission = "write";    flickr.addEventListener(     FlickrResultEvent.PHOTOS_UPLOAD_BYTES_COMPLETE,     videoCapUploaded   );    var uploader:Upload = new Upload(flickr);   uploader.uploadBytes(capData, "Video snapshot", "From Flex", "twitter,test,video,snapshot", true);   CursorManager.setBusyCursor();  } 

Когда скриншот будет загружен, мы вызываем функцию videoCapUploaded , которая просто захватывает URL-адрес Flickr и отправляет его в сервис bit.ly, который мы создали в предыдущей статье:

 private function videoCapUploaded(evt:FlickrResultEvent):void {   CursorManager.removeBusyCursor();   photoUrl = "http://www.flickr.com/photos/"+flickrNsid+"/"+evt.data.photoid;   bitlyService.send();  } 

Вернувшись в наш пользовательский компонент, мы создали функцию uploadSnapshot для вызова uploadVideoCap . uploadSnapshot привязывается к кнопке Upload с помощью прослушивателя событий в функции init компонента. Чтобы вызвать функцию uploadVideoCap из компонента, нам нужно указать его путь, используя mx.core.FlexGlobals.topLevelApplication :
private function uploadSnapshot(evt:Event):void { mx.core.FlexGlobals.topLevelApplication.uploadVideoCap(imageByteArray); closeState(); }

Мы почти закончили! Теперь все, что нам нужно, это метод закрытия состояния videoOptions , чтобы убедиться, что мы остановили воспроизведение видео и вернули приложение в основное состояние twitterDisplay . Сам метод closeState довольно closeState . Обратите внимание, что он имеет необязательный параметр evt:Event . Это потому, что мы также будем вызывать метод как обработчик события для кнопки закрытия компонента (в этом случае ему будет неявно передан параметр Event ). Этот обработчик события объявлен в функции init компонента:

private function init():void { ... closeBtn.addEventListener(MouseEvent.CLICK,closeState); ... } ... private function closeState(evt:Event = null):void { videoPlayer.stop(); mx.core.FlexGlobals.topLevelApplication.currentState = "twitterDisplay"; }

Вот и все! Это было интересное приложение, чтобы собрать. Хотя это, безусловно, функционально, есть множество функций, которые можно добавить для его округления: например, вы можете ограничить ввод текста в Твиттере до 140 символов или улучшить взаимодействие с моментальным снимком видео. Функциональность ImageSnapshot настолько проста, что ее легко представить в других видеоприложениях: например, добавление аннотаций для обратной связи оператору или редактору камеры.

Надеемся, что эта серия покажет вам, как легко проектировать и создавать многофункциональные интернет-приложения с использованием Flash Catalyst и Flash Builder. Вы также должны понимать, как взаимодействовать с несколькими основными веб-API, которые очень пригодятся при разработке ваших собственных приложений для социальных сетей!

Чего же ты ждешь? Выходи и построй что-нибудь классное! Но прежде чем сделать это, почему бы вам не проверить то, что вы узнали, пройдя наш тест по статье ?