Back to article list Go to page: 1 2
This is the third and final part in our Share Media on Twitter Using Flex series (read Part I [http://articles.sitepoint.com/article/share-media-flex-twitter] and Part II [http://articles.sitepoint.com/article/share-media-flex-twitter-images]). 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.
Once you’re done, be sure to test your knowledge in our Article Quiz [http://www.sitepoint.com/quiz/adobe/share-media-flex-twitter-video]!
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.
Before we start, you should download the code archive [http://sitepointstatic.com/examples/flextwitter-part3/flextwitter3.zip] 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.
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.
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.
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:
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:
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();
}
/# pagebreak '' #/
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.
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:
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.
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:
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 has a VideoElement component positioned in the component’s layout using a Group. VideoElement is a new component introduced with Flex 4: it’s basically just a chrome-less version of the VideoPlayer component. It’s really handy for grabbing video frames, as there’s no need to strip away the player controls. The Group element is necessary for reasons I’ll explain shortly.
The setVideoPlayer function, which we called from handleVideoData, assigns the video’s URL to the source property of the VideoElement component. This will make the component download and play the video. We’re also using a ProgressBar component, set to indeterminate mode, to give the user a visual cue that the file is being downloaded. To hide the progress bar once the download is complete, we need to add a new event listener to the component’s init function. The listener calls the videoLoadProgress function, which hides the progress bar and enables the snapshot:
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;
}
}
We need one more event handler, to enable the Replay button once the video is done playing. We’ll add another listener to the init function (using the videoElement.COMPLETE event) and create a method called enableReplayBtn, which simply sets the value of the button’s enabled property to true. It might seem like it would be easier just to add an inline listener to the videoElement itself, since our enableReplayBtn function is only one line of code. However, in the interest of keeping our code readable and maintainable, it makes more sense to have all our listeners in the same place.
The Snapshot button calls the frameGrab function, which I’ve listed below. In order to take a snapshot of the video, we’re using a function of Flex’s built-in ImageSnapshot helper class. Rather than actually grabbing a frame directly from the video, captureImage just returns the current image displayed by the element you point it at. This is why we used VideoElement instead of VideoPlayer: we’d prefer to avoid grabbing the player controls along with the screenshot.
The ImageSnapshot class can capture from any component that implements the flash.display.IBitmapDrawable class. This includes Flex UIComponents, but doesn’t include VideoPlayer or VideoElement; this is why we wrapped the VideoElement in a Group.
We assign the data grabbed by the captureImage function to a variable (so we can access it later from the main application), typecasting it as a ByteArray using the as keyword. Then we assign that variable to the source attribute of a new Image component to display a preview of the snapshot. Finally, we enable the Upload button:
private function frameGrab(evt:Event):void {
var imageSnap:ImageSnapshot = ImageSnapshot.captureImage(videoGroup);
snapshotPreview.source = imageSnap.data as ByteArray;
uploadSnapshotBtn.enabled = true;
}
With this code in place, users can view the video, replay it as often as they like, and take snapshots of it while it’s playing. Next, we’ll add functionality to allow them to upload the current snapshot to Flickr.