This is the third and final part in our Share Media on Twitter Using Flex series (read Part I and Part II). In the first article we showed you how to build a Twitter application with the Flex framework in Flash Builder, importing Photoshop artwork into Flash Catalyst to build both the interface and its interactions. The second article took you through the processes and libraries needed to upload images to Flickr and use the bit.ly API to shorten the links to those images. With this final article in the series we’ll extend the application to upload video to Flickr, and use a few built-in Flex features to grab screenshots from the video and upload them as well.

这是我们在Twitter上使用Flex系列共享媒体的第三部分,也是最后一部分(请阅读第一部分第二部分 )。 在第一篇文章中,我们向您展示了如何使用Flash Builder中的Flex框架构建Twitter应用程序,如何将Photoshop图稿导入Flash Catalyst中以构建界面及其交互。 第二篇文章介绍了将图像上传到Flickr并使用bit.ly API缩短到这些图像的链接所需的过程和库。 在本系列的最后一篇文章中,我们将扩展应用程序以将视频上传到Flickr,并使用一些内置的Flex功能从视频中截取屏幕截图并上传它们。

Once you’re done, be sure to test your knowledge in our Article Quiz!

完成后,请务必在我们的文章测验中测试您的知识!

In the previous installment we discussed potential ways of hosting images online to accompany a Tweet. We decided the simplest approach was to upload them to Flickr, create a minimized link to the Flickr page using the bit.ly API, and then include that link in a tweet. That approach worked well, so we’ll do the same for video.

在上一部分中,我们讨论了在线托管图像以伴随Tweet的潜在方法。 我们认为最简单的方法是将它们上传到Flickr,使用bit.ly API创建到Flickr页面的最小链接,然后将该链接包含在推文中。 这种方法效果很好,因此我们将对视频执行相同的操作。

Before we start, you should download the code archive to follow along with. The file we’ll be working with this time around is cheepcheep_video_flashbuilder.fxp, in the Flex projects folder (the previous versions of the application are there as well, so you can see what’s changed). Import that file into Flash Builder, and you’re ready to get started.

在我们开始之前,您应该下载代码归档以进行后续操作。 这次我们要使用的文件是cheepcheep_video_flashbuilder.fxp ,位于Flex projects文件夹中(该应用程序的先前版本也存在,因此您可以看到更改了)。 将该文件导入Flash Builder,就可以开始了。

(Uploading Video to Flickr)

Our first task is to modify the filter used by the FileReference element to include the video file extensions supported by Flickr. We’ve added avi, wmv, mov, mpg, mp4, m4v, and 3gp to the FileFilter that we created in the last article. Although these extensions are all officially supported by Flickr, there is still a risk that Flickr will be unable to encode some files; this is because video can be compressed with a variety of different codecs and only the most commonly used (like H.264) are supported. We recommend that you check the Flickr documentation for what’s supported.

我们的第一个任务是修改FileReference元素使用的过滤器,以包括Flickr支持的视频文件扩展名。 我们已经在上一篇文章中创建的FileFilter中添加了avi,wmv,mov,mpg,mp4,m4v和3gp。 尽管Flickr正式支持了这些扩展名,但仍有Flickr无法编码某些文件的风险。 这是因为可以使用各种不同的编解码器压缩视频,并且仅支持最常用的编解码器(例如H.264)。 我们建议您检查Flickr文档以了解受支持的内容。

Flickr allows you to upload video of up to 90 seconds in duration and 150MB in size for free accounts, or up to 500MB for Pro accounts. Realizing that video files can be larger than images, we’ve tweaked the original interface, adding an extra Label component to display the size of the selected file. We’ve also added a button to view video options if a video file is selected. We’ll look at this button in more detail later; for now just notice that it’s disabled by default.

Flickr允许您上传长达90秒的视频,免费帐户上传150MB的视频,而Pro帐户上传高达500MB的视频。 考虑到视频文件可能比图像大,我们对原始界面进行了调整,添加了一个额外的Label组件以显示所选文件的大小。 如果选择了视频文件,我们还添加了一个按钮来查看视频选项。 稍后,我们将更详细地介绍该按钮。 现在只需注意默认情况下它处于禁用状态。

The fileAccessed function, called once a file has been selected, has been modified to pull in extra information: the size of the file (which we display with our new Label), and its extension. FileReference does have a type property, but it’s unreliable (for example, it’s unable to determine the extension of a GIF file), so we’re using our own variable. To obtain the extension, we split the filename at each period and grab the last element of the resulting array. We’ve also added arrays at the top of the main application to store the acceptable extensions for both photo and video files:

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; 
}

A successful upload to Flickr will call the uploadCompleteHandler function. We’ve modified that function to assign the uploaded media’s ID to a new flickrUploadID variable. We also test the extension of the file to see if it’s in the array of accepted video extensions. If it is, we enable the new Video Options button. The rest of this function, which grabs the URL of the upload from Flickr and submits it to our bit.ly service, is unchanged from the last article:

成功上传到Flickr将会调用uploadCompleteHandler函数。 我们已经修改了该函数,以将上载的媒体ID分配给新的flickrUploadID变量。 我们还测试了文件的扩展名,以查看它是否在可接受的视频扩展名数组中。 如果是这样,我们启用新的“ 视频选项”按钮。 该功能的其余部分(从Flickr抓取上传的URL并将其提交给我们的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(); 
}
(Fetching the Video from Flickr)

We’re going to use the Video Options button to switch to a new state (videoOptions) that we’ve added to the application. This state displays a new component that we created with Flash Catalyst: its purpose is to display the uploaded video to enable the user to grab a snapshot of the video as an image for uploading as well. Before switching to this state, however, we need to retrieve the video from Flickr for playback. So we’ll write one function to send a request to Flickr when the button is clicked, and another to switch the application’s state when a response is received.

我们将使用“ 视频选项”按钮切换到已添加到应用程序的新状态( videoOptions )。 此状态显示了我们使用Flash Catalyst创建的新组件:其目的是显示上传的视频,以使用户也可以将视频的快照作为要上传的图像。 但是,在切换到此状态之前,我们需要从Flickr检索视频以进行播放。 因此,我们将编写一个函数,以在单击按钮时向Flickr发送请求,而另一个函数则在收到响应时切换应用程序的状态。

The first function, which we’ve called showVideoOptions, calls the getSizes method of the Flickr API using the same Flickr ActionScript library employed in the last article. It also sets an event listener to handle the API response:

第一个函数称为showVideoOptions ,它使用上一篇文章中使用的相同Flickr ActionScript库调用Flickr API的getSizes方法。 它还设置了一个事件侦听器来处理API响应:

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

Now let’s take a look at our callback, the handleVideoData function. If the upload is still being processed by Flickr, the FlickrResultEvent that gets passed to this function will contain an error to notify us that the video hasn’t been found. On the other hand, if the video has been processed, the result will contain an array of photo sizes. One of the “photo sizes” is actually the MP4 video.

现在让我们看一下我们的回调handleVideoData函数。 如果Flickr仍在处理上载,则传递给此函数的FlickrResultEvent将包含错误,以通知我们未找到视频。 另一方面,如果视频已经过处理,则结果将包含一组照片尺寸。 “照片大小”之一实际上是MP4视频。

Our first task then is to see if the data received contains an error. We can do this using the built-in function hasOwnProperty. If we find an error, we display an Alert to the user to inform them that the video is still processing. Otherwise, we switch to the videoOptions state to display the new component, then loop over the photoSizes array looking for a value of “Site MP4” (which is the “size” string that Flickr assigns to MP4 video). We then pass the source property of that array index to our custom component’s videoLocation property, and call the component’s setVideoPlayer method:

然后,我们的第一个任务是查看接收到的数据是否包含错误。 我们可以使用内置函数hasOwnProperty做到这一点。 如果发现错误,则会向用户显示Alert ,以通知他们视频仍在处理中。 否则,我们将切换到videoOptions状态以显示新组件,然后在photoSizes数组上循环查找“ Site MP4”值(这是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;  
      }  
    }  
  }  
}
(Displaying the Video)
CustomComponent3
VideoElement
Group
VideoElement
VideoPlayer
Group
setVideoPlayer
handleVideoData
VideoElement
ProgressBar
indeterminate
init
videoLoadProgress
init
videoElement.COMPLETE
enableReplayBtn
enabled
true
videoElement
enableReplayBtn
frameGrab
ImageSnapshot
captureImage
VideoElement
VideoPlayer
ImageSnapshot
flash.display.IBitmapDrawable
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;  
}
Upload
FileReference
FileReference
Upload
uploadBytes
Upload.as
com.adobe.webapis.flickr.methodgroups
ByteArray
Upload.as
uploadBytesComplete
uploadBytes
upload
upload
uploadVideoCap
uploadFlickr
uploadBytes
upload
flickr.permission
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
uploadSnapshot
uploadVideoCap
uploadSnapshot
init
uploadVideoCap
mx.core.FlexGlobals.topLevelApplication
   private function uploadSnapshot(evt:Event):void {     mx.core.FlexGlobals.topLevelApplication.uploadVideoCap(imageByteArray);     closeState();   }
videoOptions
twitterDisplay
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";       }   
ImageSnapshot
CustomComponent3
VideoElement
Group
VideoElement
VideoPlayer
Group
setVideoPlayer
handleVideoData
VideoElement
ProgressBar
indeterminate
init
videoLoadProgress
init
videoElement.COMPLETE
enableReplayBtn
enabled
true
videoElement
enableReplayBtn
frameGrab
ImageSnapshot
captureImage
VideoElement
VideoPlayer
ImageSnapshot
flash.display.IBitmapDrawable
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;  
}
Upload
FileReference
FileReference
Upload
uploadBytes
Upload.as
com.adobe.webapis.flickr.methodgroups
ByteArray
Upload.as
uploadBytesComplete
uploadBytes
upload
upload
uploadVideoCap
uploadFlickr
uploadBytes
upload
flickr.permission
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
uploadSnapshot
uploadVideoCap
uploadSnapshot
init
uploadVideoCap
mx.core.FlexGlobals.topLevelApplication
   private function uploadSnapshot(evt:Event):void {     mx.core.FlexGlobals.topLevelApplication.uploadVideoCap(imageByteArray);     closeState();   }
videoOptions
twitterDisplay
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";       }   
ImageSnapshot

翻译自: https://www.sitepoint.com/share-media-flex-twitter-video/