Tuesday, April 12, 2016

Capture frames in VideoView using MediaMetadataRetriever


Previous posts show how to " VideoView example to play video from Internet" and "Add MediaController to VideoView". We can capture frame in the video using MediaMetadataRetriever.

This video show how it run on Android Emulator running Marshmallow.


MainActivity.java
package com.blogspot.android_er.androidvideoview;

import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.MediaController;
import android.widget.Spinner;
import android.widget.Toast;
import android.widget.VideoView;

import java.util.HashMap;

public class MainActivity extends AppCompatActivity {

    Button btnCapture;
    Spinner spOption;
    VideoView myVideoView;

    String videoSource =
            "https://sites.google.com/site/androidexample9/download/RunningClock.mp4";
    Uri uriVideoSource;

    MediaController myMediaController;
    MediaMetadataRetriever myMediaMetadataRetriever;

    String[] stringOpts = {
            "none",
            "OPTION_CLOSEST",
            "OPTION_CLOSEST_SYNC",
            "OPTION_NEXT_SYNC",
            "OPTION_PREVIOUS_SYNC"};
    int[] valOptions ={
            0,  //will not be used
            MediaMetadataRetriever.OPTION_CLOSEST,
            MediaMetadataRetriever.OPTION_CLOSEST_SYNC,
            MediaMetadataRetriever.OPTION_NEXT_SYNC,
            MediaMetadataRetriever.OPTION_PREVIOUS_SYNC};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myVideoView = (VideoView)findViewById(R.id.vview);

        prepareVideo();

        spOption = (Spinner)findViewById(R.id.option);
        ArrayAdapter<String> adapter
                = new ArrayAdapter<String>(MainActivity.this,
                android.R.layout.simple_list_item_1, stringOpts);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spOption.setAdapter(adapter);

        btnCapture = (Button)findViewById(R.id.capture);
        btnCapture.setOnClickListener(btnCaptureOnClickListener);
    }

    View.OnClickListener btnCaptureOnClickListener = new View.OnClickListener(){
        @Override
        public void onClick(View v) {

            int currentPosition = myVideoView.getCurrentPosition(); //in millisecond
            Toast.makeText(MainActivity.this,
                    "Current Position: " + currentPosition + " (ms)",
                    Toast.LENGTH_LONG).show();

            Bitmap bmFrame;
            int pos = currentPosition * 1000;   //unit in microsecond
            int opt = spOption.getSelectedItemPosition();
            if(opt == 0){
                bmFrame = myMediaMetadataRetriever
                    .getFrameAtTime(pos);
            }else{
                bmFrame = myMediaMetadataRetriever
                        .getFrameAtTime(pos,
                                valOptions[opt]);
            }

            if(bmFrame == null){
                Toast.makeText(MainActivity.this,
                        "bmFrame == null!",
                        Toast.LENGTH_LONG).show();
            }else {
                AlertDialog.Builder myCaptureDialog =
                        new AlertDialog.Builder(MainActivity.this);
                ImageView capturedImageView = new ImageView(MainActivity.this);
                capturedImageView.setImageBitmap(bmFrame);
                LinearLayout.LayoutParams capturedImageViewLayoutParams =
                        new LinearLayout.LayoutParams(
                                LinearLayout.LayoutParams.WRAP_CONTENT,
                                LinearLayout.LayoutParams.WRAP_CONTENT);
                capturedImageView.setLayoutParams(capturedImageViewLayoutParams);

                myCaptureDialog.setView(capturedImageView);
                myCaptureDialog.show();
            }

        }
    };

    private void prepareVideo(){

        myMediaMetadataRetriever = new MediaMetadataRetriever();
        myMediaMetadataRetriever.setDataSource(
                videoSource, new HashMap<String, String>());

        myMediaController = new MediaController(MainActivity.this);
        myVideoView.setMediaController(myMediaController);

        Toast.makeText(MainActivity.this, videoSource, Toast.LENGTH_LONG).show();

        uriVideoSource = Uri.parse(videoSource);

        myVideoView.setVideoURI(uriVideoSource);

        myVideoView.setOnCompletionListener(myVideoViewCompletionListener);
        myVideoView.setOnPreparedListener(MyVideoViewPreparedListener);
        myVideoView.setOnErrorListener(myVideoViewErrorListener);

        myVideoView.requestFocus();
        myVideoView.start();

    }

    MediaPlayer.OnCompletionListener myVideoViewCompletionListener =
            new MediaPlayer.OnCompletionListener() {

                @Override
                public void onCompletion(MediaPlayer arg0) {
                    Toast.makeText(MainActivity.this, "End of Video",
                            Toast.LENGTH_LONG).show();
                }
            };

    MediaPlayer.OnPreparedListener MyVideoViewPreparedListener =
            new MediaPlayer.OnPreparedListener() {

                @Override
                public void onPrepared(MediaPlayer mp) {

                    long duration = myVideoView.getDuration(); //in millisecond
                    Toast.makeText(MainActivity.this,
                            "Duration: " + duration + " (ms)",
                            Toast.LENGTH_LONG).show();

                }
            };

    MediaPlayer.OnErrorListener myVideoViewErrorListener =
            new MediaPlayer.OnErrorListener() {

                @Override
                public boolean onError(MediaPlayer mp, int what, int extra) {

                    String errWhat = "";
                    switch (what){
                        case MediaPlayer.MEDIA_ERROR_UNKNOWN:
                            errWhat = "MEDIA_ERROR_UNKNOWN";
                            break;
                        case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
                            errWhat = "MEDIA_ERROR_SERVER_DIED";
                            break;
                        default: errWhat = "unknown what";
                    }

                    String errExtra = "";
                    switch (extra){
                        case MediaPlayer.MEDIA_ERROR_IO:
                            errExtra = "MEDIA_ERROR_IO";
                            break;
                        case MediaPlayer.MEDIA_ERROR_MALFORMED:
                            errExtra = "MEDIA_ERROR_MALFORMED";
                            break;
                        case MediaPlayer.MEDIA_ERROR_UNSUPPORTED:
                            errExtra = "MEDIA_ERROR_UNSUPPORTED";
                            break;
                        case MediaPlayer.MEDIA_ERROR_TIMED_OUT:
                            errExtra = "MEDIA_ERROR_TIMED_OUT";
                            break;
                        default:
                            errExtra = "...others";

                    }

                    Toast.makeText(MainActivity.this,
                            "Error!!!\n" +
                            "what: " + errWhat + "\n" +
                            "extra: " + errExtra,
                            Toast.LENGTH_LONG).show();
                    return true;
                }
            };
}


layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidvideoview.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:id="@+id/capture"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:text="Capture"/>
        <Spinner
            android:id="@+id/option"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"/>
    </LinearLayout>

    <VideoView
        android:id="@+id/vview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>


uses-permission of "android.permission.INTERNET" is needed in AndroidManifest.xml.

2 comments:

mariano said...

Hello! Love the example but I have an issue... the performance is terrible when trying to create the new instance of the MediaMetadataRetriever. Even on a thread... the first time I open the activity is really slow.

Andr.oid Eric said...

hello mariano,

I think it depends on the download speed from internet mainly.