Friday, July 01, 2011

Getting Images laid (pause) over a video in android

This post is a short tutorial on how to overlay images on video in android. I created this tutorial, after making an android application that plays a video and with certain user interactions displays images on top of the video. This Image-Video effect can also be achieved through action script, but in this tutorial we avoid any extra programming tools and stick to working with the android API.

Before we begin, we need to giver a quick overview of concepts:

Most of the user interface components on Android are Views. A View represents a rectangular area on the screen and it is responsible for drawing and event handling. The ImageView class displays an arbitrary image, such as an icon. The VideoView class displays a video file. The ViewGroup class is considered a special view that can contain other views (called children.) This class is the base class for layouts.

In Android, a layout holds all the elements that appear to the user, and defines where they will be placed. The layout can be declared in an XML file or can be programmatically defined by creating View Objects. A particular type of layout is RelativeLayout. This Class holds the concept that each component in the interface can be described in relation to each other or to its parent.

The overall idea is that the image on video overlay can be accomplished by using RelativeLayout and placing the VideoView as the first child of the RelativeLayout and the ImageView as the second child. This way in the camera preview, the ImageView will appear to be "on top of" the VideoView.

The step-by-step instructions are as follows:

1. In Eclipse, create a Simple Android Project From Scratch (Make sure to have created a main activity).

2. Under the res folder in your project go to the drawable folder (if you don't have a folder titled "drawable" in res, create it) and add all of the images you plan on working with there.

3. Add the video or videos you plan on working with to the SD memory card of your android phone. (This can be done by connecting your phone via USB to your PC and on the phone, selecting "notifications", then "USB connected", and in the new window that appears clicking: "Turn on USB storage". After a few seconds a new removable disk should appear on your PC. Copy to it your videos. The video format of my videos was mp4)

4. Back in your android project in your layout folder, add a new xml file with the name of your choice (for example video_over_image.xml) .In this xml we will define the elements and the layout of our application. For this particular application, we want the following layout:
A text-box in the upper part of the window, where the user types the name of the video they wish to play. The space below the text-box is where the video will be displayed.
The image overlaid on the video will appear on the upper portion of the video. (But it is possible to place it wherever one desires).
Our XML file to accomplish this layout is as follows:


<?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">

<TextView android:id="@+id/label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Type video name here:" />
<EditText
android:id="@+id/edittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>

<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<Button android:id="@+id/topBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Top"
android:layout_centerHorizontal="true">
</Button>

<VideoView android:id="@+id/surface_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

<ImageView android:id="@+id/overlayImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_below="@+id/topBtn"
/>

</RelativeLayout>

</LinearLayout>



An interesting point to notice about this layout is that we added a dummy button to it. Because the video is defined right after this dummy button, the video will completely "cover" the button, so it will not appear on the interface. This button helps in positioning our image; Our image is set relative to this button. In this case, because we sought for the image to appear in the "mid-top" portion of the video the image's layout was set to be below this button. It is also important to note, that the image was declared after the video, because this permits the image to be displayed "on top of" the video.


5. In our java activity file in the onCreate method, we now need to establish that we will be using this layout. We also need to establish a listener for the textbox, which after the user has typed the name of the video to play and pressed "enter" will start playing the desired video. Furthermore it is also necessary to establish what images will be overlaid and when that will occur.
To facilitate this example, we will establish that when the user types 1, video A (which should already be on the phone's SD card) will be played and image c1 will be overlaid on the video. Similarly, when the user types 2, video B will be played and image c2 will now be overlaid on the video. We will also add some effects to the image, in specific alpha blending.
In the following, we will present all the code to accomplish this task and subsequently review each part of it:



package com.example.android.videooverimage;


import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.util.Log;
import android.widget.MediaController;
import android.widget.VideoView;
import android.net.Uri;
import android.widget.EditText;
import android.view.KeyEvent;
import android.view.View.OnKeyListener;
import android.view.View;
import android.content.res.Resources;
import android.widget.ImageView;



public class VideoOverImageActivity extends Activity
{



public VideoOverImageActivity()
{

}


public void onCreate(Bundle icicle)
{

super.onCreate(icicle);
setContentView(R.layout.video_over_image_activity);
final EditText edittext = (EditText) findViewById(R.id.edittext);

edittext.setOnKeyListener(new OnKeyListener()
{
public boolean onKey(View v, int keyCode, KeyEvent event)
{
// If the event is a key-down event on the "enter" button
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
// Perform action on key press

int aInt=0;
try
{
aInt = Integer.parseInt(edittext.getText().toString());
}
catch (NumberFormatException e)
{
Log.e("debug","Error finding Image: "+e.getMessage());

}

VideoView videoHolder = (VideoView) findViewById(R.id.surface_view);
MediaController mc=new MediaController(VideoOverImageActivity.this);
Boolean returnValue=true;

switch (aInt)
{

case 1:


startPlaying(videoHolder,mc,"file:///sdcard/video1.mp4",0);
break;

case 2:
startPlaying(videoHolder,mc,"file:///sdcard/video2.mp4",1);
break;

default:
returnValue=false;
break;

}


return returnValue;


}

return false;
}
});

}

public void startPlaying(VideoView videoHolder,MediaController mc,String nameVideo, int song)
{

Resources res = VideoOverImageActivity.this.getResources();


ImageView image = (ImageView) findViewById(R.id.overlayImage);

try
{
R.drawable.class.getField("b" + song).getInt(0);
image.setImageDrawable(res.getDrawable(R.drawable.class.getField("b" + song).getInt(0)));
image.getDrawable().setAlpha(55);
}
catch (Exception e)
{
Log.e("debug","Error finding Image: "+e.getMessage());

}

videoHolder.setMediaController(mc);
videoHolder.setVideoURI(Uri.parse(nameVideo));
videoHolder.requestFocus();
videoHolder.start();

videoHolder.setOnCompletionListener(new OnCompletionListener()
{
public void onCompletion(MediaPlayer arg0)
{
try
{
Log.e("debug","MediaPlayer seek to 0...");
arg0.seekTo(0);
Log.e("debug","MediaPlayer start...");
arg0.start();
Log.e("debug","MediaPlayer started");
}

catch(Exception ex)
{
Log.e("debug","MediaPlayer error: "+ex.toString());
}
}
});



}


}