Sunday, October 13, 2013

Android Network Video



Network Video Playback

     Android supports HTTP and RTSP video playback in all three video playback methods. Using either the built-in Media Player activity via an intent or the VideoView class to play either form of network video requires no source code changes.Simply use the HTTP or RTSP URL as the video Uri, and it will work as long as the format is supported.

Download Source Code Click Here

Network Video Player Using VideoView

Here is the activity example uses a VideoView with an RTSP URL to a video from YouTube’s mobile site. The only change is the string passed in to construct the videoUri.



MediaPlayer Network Video Player (Using SurfaceView)

Download Source Code Click Here

Working with the MediaPlayer for network video playback is similar to the MediaPlayer and MediaController code.


The StreamingVideoPlayer activity implements many of the available listener and callback abstract classes from MediaPlayer, SurfaceHolder, and MediaController. The OnBufferingUpdateListener is particularly useful when dealing with network delivered media. This class specifies an onBufferingUpdate method that is repeatedly called while the media is buffering, allowing us to keep track of how full the buffer is.


In this code, we’ll use a TextView called statusView to display status messages to the user. The reason we’ll do so is that loading a video for playback via the Internet can take quite a bit of time, and without some sort of status message, the user may think the application has hung.


Among the list of MediaPlayer event listeners, our activity implements and is registered to be the OnBufferingUpdateListener.



Instead of playing back a file from the SD card, we’ll be playing a file served from an RTSP server. The URL to the file is specified in the following String, filePath. We’ll then use the MediaPlayer’s setDataSource method, passing in the filePath String. The MediaPlayer knows how to handle loading and playing data from an RTSP server, so we don’t have to do anything else different to handle it.



We’ll use the MediaPlayer’s prepareAsync method instead of prepare. The prepareAsync method does the preparation in the background on a separate thread. This makes it so that the user interface doesn’t hang. This would allow the user to perform other actions or allow us as the developer to display a loading animation or something similar.


}

Since our activity implements the OnBufferingUpdateListener and is registered to be the listener for the MediaPlayer, the following method will be called periodically as media is downloaded and buffered. The buffering will occur during the preparation stage (after onPrepareAsync or onPrepare is called).

public void onBufferingUpdate(MediaPlayer mp, int bufferedPercent) {
      statusView.setText("MediaPlayer Buffering: " + bufferedPercent + "%");
      Log.v(LOGTAG, "MediaPlayer Buffering: " + bufferedPercent + "%");

Android Thumbnail From Video

Video Thumbnails Using the MediaStore        

We could, starting with Android 2.0 (API Level 5), pull out the thumbnails associated with
each video file from within the loop. We need the ID of the video file that is in our list of
columns to select (MediaStore.Video.Media._ID), which we can then use in the “where”
clause of the managedQuery.

int id = cursor.getInt(cursor.getColumnIndex(MediaStore.Video.Media._ID));
String[] thumbColumns = { MediaStore.Video.Thumbnails.DATA,
MediaStore.Video.Thumbnails.VIDEO_ID};
Cursor thumbCursor = managedQuery(MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI,
thumbColumns, MediaStore.Video.Thumbnails.VIDEO_ID + "=" + id, null, null);
if (thumbCursor.moveToFirst()) {
       Log.v("VideoGallery",thumbCursor.getColumnIndex(MediaStore.Video.Thumbnails.DATA));
}

Full Source Code Here

Full MediaStore Video Example

Here is a full example that retrieves all of the available video files from the MediaStore
and displays each of their thumbnail images and titles. This example uses the MediaStore.Video.Thumbnails class which is available in Android 2.0 (API Level 5) and above.

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;

public class VideoGallery extends Activity implements OnItemClickListener {

Cursor cursor;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

We’ll use a ListView to display the list of videos.

ListView listView = (ListView) this.findViewById(R.id.ListView);

Next is the list of columns we want from the MediaStore.Video.Thumbnails queries.

String[] thumbColumns = {
MediaStore.Video.Thumbnails.DATA,
MediaStore.Video.Thumbnails.VIDEO_ID
};

Then comes the list of columns we want from the MediaStore.Video.Media query.

String[] mediaColumns = {
    MediaStore.Video.Media._ID,
    MediaStore.Video.Media.DATA,
    MediaStore.Video.Media.TITLE,
    MediaStore.Video.Media.MIME_TYPE
};

In the main query, we’ll select all of the videos that are represented in the

MediaStore.cursor = managedQuery(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
mediaColumns, null, null, null);

Each row returned by the query will create an item in the following ArrayList. Each item
will be a VideoViewInfo object, which is a class defined here specifically to hold
information about a video for use in this activity.

ArrayList<VideoViewInfo> videoRows = new ArrayList<VideoViewInfo>();

Here we loop through the data contained in the Cursor object, creating a VideoViewInfo
object for each row and adding it to our ArrayList.

if (cursor.moveToFirst())

We are using a do while loop as we want it to run through the first row of data before
moving to the next row. The do portion happens before the while clause is
tested/executed. In our loop, we’ll create a new VideoViewInfo object for each row of
data returned.

do {
    VideoViewInfo newVVI = new VideoViewInfo();

We can then pull out all of the relevant data from the Cursor. As just described, we’ll
also make another query to pull out a thumbnail image for each video. Each of these
pieces of data will be stored in the VideoViewInfo object.

int id = cursor.getInt(cursor.getColumnIndex(MediaStore.Video.Media._ID));
Cursor thumbCursor = managedQuery(MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI,
thumbColumns, MediaStore.Video.Thumbnails.VIDEO_ID + "=" + id, null, null);

if (thumbCursor.moveToFirst())
{
         newVVI.thumbPath = thumbCursor.getString(
         thumbCursor.getColumnIndex(MediaStore.Video.Thumbnails.DATA));
         Log.v("VideoGallery","Thumb " + newVVI.thumbPath);
}

newVVI.filePath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA));
newVVI.title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.TITLE));
Log.v("VideoGallery","Title " + newVVI.title);
newVVI.mimeType = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.MIME_TYPE));
Log.v("VideoGallery","Mime " + newVVI.mimeType);

Finally, we add the VideoViewInfo to the videoRows ArrayList.

             videoRows.add(newVVI);
     } while (cursor.moveToNext());
}

Once we are done getting all of the videos, we can continue on. We’ll set the adapter of
the ListView object to be a new instance of VideoGalleryAdapter, which is an inner
class defined here. We’ll also set this activity to be the OnItemClickListener for the
 
          ListView.listView.setAdapter(new VideoGalleryAdapter(this,videoRows));      
          listView.setOnItemClickListener(this);
 }

When an item in the ListView is clicked, the onItemClick method will be called. In this
method, we extract the data we need from the Cursor and create an intent to launch the
default media player application on the device to play back the video. We could have
created our own MediaPlayer or used the VideoView class here instead.

public void onItemClick(AdapterView<?> l, View v, int position, long id) {
   if (cursor.moveToPosition(position)) {
   int fileColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
   int mimeColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.MIME_TYPE);
   String videoFilePath = cursor.getString(fileColumn);
   String mimeType = cursor.getString(mimeColumn);
   Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
   File newFile = new File(videoFilePath);
   intent.setDataAndType(Uri.fromFile(newFile), mimeType);
   startActivity(intent);
     }
}

What follows is the very basic VideoViewInfo class, which is used to hold information about each video returned.

class VideoViewInfo
{
    String filePath;
    String mimeType;
    String thumbPath;
    String title;
}

Since we are using a ListView in our activity to display each of the videos returned from the MediaStore query, we’ll be using the ListView to display both the title of the video and a thumbnail. In order to hand the data to the ListView, we need to construct anAdapter. Next, we create an Adapter, VideoGalleryAdapter, which extends BaseAdapter. When this class is constructed, it gets passed the ArrayList that holds all of the videos returned from the MediaStore query.

BaseAdapter is an abstract class, so in order to extend it, we need to implement several methods. Most of them are straightforward and just operate on the ArrayList we passed in, such as getCount and getItem. The method that requires the most attention is the getView method.

class VideoGalleryAdapter extends BaseAdapter
{
      private Context context;
      private List<VideoViewInfo> videoItems;
      LayoutInflater inflater;
      public VideoGalleryAdapter(Context _context, ArrayList<VideoViewInfo> _items) {
          context = _context;
          videoItems = _items;
          inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

public int getCount() {
      return videoItems.size();
}
public Object getItem(int position) {
      return videoItems.get(position);
}
public long getItemId(int position) {
      return position;
}

The getView method is used to return the view for each row represented in the ListView. It is passed in the position that is meant to be returned (along with a View object representing the current View and an object that represents the parent ViewGroup).

public View getView(int position, View convertView, ViewGroup parent) {

To construct the View to be returned, we need to inflate the layout that we are using for
each row. In this case, we are using a layout defined in list_item.xml (shown here).

View videoRow = inflater.inflate(R.layout.list_item, null);

After the layout is inflated, we can get at the individual Views that are defined and use the data from the ArrayList of VideoViewInfo objects to define what to display. Here is how that is done for the ImageView that is used to display each video’s thumbnail.

ImageView videoThumb = (ImageView) videoRow.findViewById(R.id.ImageView);

if (videoItems.get(position).thumbPath != null) {
           videoThumb.setImageURI(Uri.parse(videoItems.get(position).thumbPath));
}

Here we obtain a reference to the TextView for the video title and set the text according to the data in the ArrayList of VideoViewInfo object.

TextView videoTitle = (TextView) videoRow.findViewById(R.id.TextView);
videoTitle.setText(videoItems.get(position).title);

Finally, we return the newly constructed View.
          return videoRow;
        }
    }
}

Here is the main.xml file defining the layout for the activity.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
>
<ListView android:layout_width="wrap_content" android:layout_height="wrap_content"
         android:id="@+id/ListView"></ListView>
</LinearLayout>

Here is the list_item.xml file that is used to define the layout for each row of the ListView.

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

<ImageView android:id="@+id/ImageView"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content">
</ImageView>
<TextView
       android:text="@+id/TextView01"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:id="@+id/TextView">
</TextView>
</LinearLayout>