Bienvenidos sean a este post, hoy vamos a crear un pequeño reproductor multimedia mediante el siguiente paso a paso.
Como siempre abramos nuestro Android Studio y seleccionemos Start a New Android Studio project y completaremos los siguientes campos:

Anuncios
  • Application Name: VideoPlayer
  • Company name: example.org
  • Minimum SDK API: 9
  • Activity: Empty Activity
  • Activity Name: VideoPlayer
  • Layout Name: activity_main

Con nuestro proyecto creado debemos primero descargar el siguiente archivo

Una vez descargado extraigan los cuatro archivos en alguna carpeta de su pc, los cuatro archivos son estos

Seleccionen los cuatro archivos copienlo mediante Ctrl+C o click con el boton derecho y la opcion Copiar, ahora vamos a nuestro proyecto y sobre el recurso drawable ubicado en res/drawable presiona Ctrl+V o click con el boton derecho y la opcion Pegar (Paste) nos aparecera el siguiente cuadro

Deben seleccionar el recurso marcado y pulsen Ok para pasar al siguiente cuadro

Dejenlo como se ve y pulsen Ok para proceder con la copia donde nos quedara de la siguiente forma

Con todos nuestros recursos agregados vamos a proceder a modificar nuestro archivo activity_main.xml con el siguiente codigo:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:id="@+id/layoutBotones"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_alignParentTop="true" >
        <ImageButton
            android:id="@+id/play"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/play"/>
        <ImageButton
            android:id="@+id/pause"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/pause"/>
        <ImageButton
            android:id="@+id/stop"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/stop"/>
        <ImageButton
            android:id="@+id/botonLog"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/log"/>
        <EditText
            android:id="@+id/path"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="/data/video.3gp" />
    </LinearLayout>
    <VideoView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="400px"
        android:layout_below="@id/layoutBotones"/>
    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="200px"
        android:layout_alignParentBottom="true">
        <TextView
            android:id="@+id/log"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Log: " />
    </ScrollView>
</RelativeLayout>
Anuncios

Aca tenemos un layout con cuatro botones, un componente para ver los videos (surfaceView) y otro para ver el log de nuestro archivo, se vera aproximadamente de esta forma

Nuestro proximo paso sera modificar la clase VideoPlayer para poder trabajar con nuestro reproductor para ello vamos a reemplazarlo con el siguiente codigo:

package org.example.videoplayer;

import android.app.Activity;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;

public class VideoPlayer extends Activity
        implements MediaPlayer.OnBufferingUpdateListener,
        MediaPlayer.OnCompletionListener,
        MediaPlayer.OnPreparedListener, SurfaceHolder.Callback {

    private MediaPlayer mediaPlayer;
    private SurfaceView surfaceView;
    private SurfaceHolder surfaceHolder;
    private EditText editText;
    private ImageButton bPlay, bPause, bStop, bLog;
    private TextView logTextView;
    private boolean pause;
    private String path;
    private int savePos = 0;

    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.activity_main);
        surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(this);
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        editText = (EditText) findViewById(R.id.path);
        editText.setText("http://personales.gan.upv.es/~jtomas/video.3gp");

        bPlay = (ImageButton) findViewById(R.id.play);
        bPlay.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {

                if (mediaPlayer!=null){
                    if(pause) {
                        mediaPlayer.start();
                    } else {
                        playVideo();
                    }
                }
            }
        });

        bPause = (ImageButton) findViewById(R.id.pause);
        bPause.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                if (mediaPlayer!=null){
                    pause = true;
                    mediaPlayer.pause();
                }
            }
        });

        bStop = (ImageButton) findViewById(R.id.stop);
        bStop.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                if (mediaPlayer!=null){
                    mediaPlayer.stop();
                    mediaPlayer.release();
                }
            }
        });

        logTextView = (TextView) findViewById(R.id.log);
        bLog = (ImageButton) findViewById(R.id.botonLog);
        bLog.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                if (mediaPlayer!=null){
                    if(logTextView.getVisibility()== TextView.VISIBLE) {
                        logTextView.setVisibility(TextView.INVISIBLE);
                    } else {
                        logTextView.setVisibility(TextView.VISIBLE);
                    }
                }
            }
        });
        log("");
    }
}

Anuncios

Como pueden ver parece que va a ser un caos e imposible de entender pero no es tan dificil como aparece, nuestra primera linea es la implementacion de todas las clases de MediaPlayer que vamos a utilizar para los distintos estados de nuestro video, los tres primeros son para nuestro video en sus distintas facetas (buffering, completacion y preparado) y por ultimo uno para llamar de nuevo, despues crearemos distintos objetos de distintas clases para crear nuestros enlaces a los elementos del layout, pueden observar que estan los botones, los reproductores pero tambien vamos a tener dos variables, una llamada pause de tipo boolean para indicar cuando nuestro elemento esta pausado o no, otra de tipo String llamada path para almacenar la ubicacion de nuestro video y por ultima una donde se almacenara la posicion de nuestro video (savePos), en el metodo onCreate(), este bloque:

surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

Se encargara de preparar a nuestro elemento VideoView para poder procesar el video que el enviemos, para luego pasar al siguiente bloque:

editText = (EditText) findViewById(R.id.path);
editText.setText("http://personales.gan.upv.es/~jtomas/video.3gp");

El cual se encargara de asignarle ese valor a nuestro elemento path del layout, por ultimo siguen cuatro bloques que trabajan de forma similar pero cada uno se encarga de distintas tareas, el primero se encarga de la funcion play, es decir arrancar o despausar el video, la segunda se encarga de pausar el video, la tercera de parar el video y el ultimo se encarga de mostrar o no en pantalla nuestro log, todos los cuadros trabajan de forma similar, es decir primero crea el enlace con el boton, luego le agregaremos el evento escuchador de click, luego en el metodo interno (onClick) verificamos si mediaPlayer es distinto de null, es decir que tiene un contenido, y ejecutara la o las acciones en su respectivo bloque, por ultimo le enviamos un texto vacio a la funcion log().
En este momento si probamos nuestra app, nos dara varios errores porque primero nos faltan declarar y definir dos metodos, log() y playVideo(), y tambien debemos sobrecargar algunas funciones para nuestra clase, por este prosigamos. Primero vamos a crear nuestra funcion playVideo() para ello debemos agregar el siguiente codigo dentro de la clase:

private void playVideo(){
    try {
        pause = false;
        path = editText.getText().toString();
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setDataSource(path);
        mediaPlayer.setSurface(surfaceHolder);
        mediaPlayer.prepare();
        mediaPlayer.setOnBufferingUpdateListener(this);
        mediaPlayer.setOnCompletionListener(this);
        mediaPlayer.setOnPreparedListener(this);
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mediaPlayer.seekTo(savePos);
    }
    catch (Exception e){
        log("Error: " + e.getMessage());
    }
}
Nota: Para los casos de Streaming la linea mediaPlayer.prepare() debe ser reemplazada por mediaPlayer.PrepareAsync()

Como pueden ver utilizaremos un metodo try, para capturar cualquier error, luego pondremos a pause como false, en nuestro siguiente caso setearemos a path con el valor del elemento editText, creamos un nuevo mediaPlayer, le definimos el archivo a reproducir, luego pondremos una imagen, para comenzar a prepararlo, y despues prepararemos los distintos tipos de estado del mismo, para luego setear el tipo de audio y por ultimo la posicion del mismo, despues tenemos un metodo catch para mandar al metodo log() el error ocurrido. Ahora agregaremos algunos metodos mas para verificar el estado de nuestro video por medio de log(), para ello primero agreguemos estos tres metodos:

Anuncios
public void onBufferingUpdate(MediaPlayer arg0, int porcentaje){
    log("onBufferingUpdate porcentaje: " + porcentaje)
}
public void onCompletion(MediaPlayer arg0){
    log("onCompletion llamado.");
}
public void onPrepared(MediaPlayer mediaPlayer){
    log("onPrepared llamado");
    int mVideoWidth=mediaPlayer.getVideoWidth();
    int mVideoHeight=mediaPlayer.getVideoHeight();
    if mVideoWidth!=0 && mVideoHeight != 0){
        surfaceHolder.setFixedSize(mVideoWidth, mVideoHeight);
        mediaPlayer.start();
    }
}

El primer metodo se encarga de implementar la interfaz onBufferingUpdate para chequear la actualizacion del buffering, y luego envia ese porcentaje al log, el segundo metodo se encarga de enviar al log cuando se completo la carga del mismo por medio de la interfaz onCompletion, y el ultimo metodo se encarga de chequear si el video esta preparado, lo envia al log pero a suu vez se encarga de obtener el ancho y el alto de nuestro video, el condicional chequea que los valores sean distinto de cero y setea a surfaceHolder con estos por medio del metodo setFixedSize() y luego le da comienzo al mismo. Con esto tenemos implementadas tres de las cuatro interfaces de la clase, ahora pasaremos a ver la ultima llamada surfaceHolder.Callback pero para ello implemetaremos los siguiente tres metodos:

public void surfaceCreate(SurfaceHolder holder){
    log("entramos en SurfaceCreate");
    playVideo();
}
public void surfaceChanged(SurfaceHolder surfaceHolder,
                            int i, int j, int k){
    log("entramos en surfaceChanged");
}
public void surfaceDestroyed(SurfaceHolder surfaceHolder){
    log("entramos en surfaceDestroyed");
}


Estos tres metodos se encargaran de chequear tres estados de surfaceHolder, el primero sera la creacion, luego si fue modificado y por ultimo si fue destruido, en los tres caso enviara la informacion a nuestro log, para ir finalizando ahora vamos a crear unos metodos que se encargaran de controlar y chequear nuestros ciclos de vida de la actividad, donde luego crearemos los metodos para guardar y restaurar nuestros estados y por ultimo nuestro metodo para crear el log. Comencemos con los metodos para nuestros ciclos de vida:

@Override protected void onDestroy(){
    super.onDestroy();
    if (mediaPlayer!=null){
        mediaPlayer.release();
        mediaPlayer = null;
    }
}

@Override protected void onPause(){
    super.onPause();
    if(mediaPlayer!=null && !pause){
        mediaPlayer.pause();
    }
}

@Override protected void onResume(){
    super.onResume();
    if (mediaPlayer!=null && !pause){
        mediaPlayer.start();
    }
}
Anuncios

Como pueden ver chequeara tres estados, el primero si es destruido, liberara la memoria, al mediaPlayer y lo seteara como null, en el segundo metodo, cuando deje estar en primer plano pero visible pausara el video, y el ultimo se encarga de retomarlo cuando la actividad vuelva al primer plano, ahora veremos los metodos para guardar y restauarar los estados de la actividad:

@Override
protected void onSaveInstanceState(Bundle guardarEstado){
    super.onSaveInstanceState(guardarEstado);
    if (mediaPlayer!=null){
        int pos = mediaPlayer.getCurrentPosition();
        guardarEstado.putString("ruta",path);
        guardarEstado.putInt("posicion",pos);
    }
}

@Override
protected void onRestoreInstanceState(Bundle recEstado){
    super.onRestoreInstanceState(recEstado);
    if(recEstado!=null){
        path = recEstado.getString("ruta");
        savePos = recEstado.getInt("posicion");
    }
}

En el primer metodo nos encargaremos de almacenar el valor de path y crearemos una nueva variable llamada pos donde guardaremos el lugar actual del video, a path lo almacenaremos como ruta y a pos como posicion. En el siguiente metodo nos encargaremos de recuperar dicha informacion, ahora para terminar con nuestra clase vamos a agregar el siguiente metodo:

private void log(String s){
    logTextView.append(s + "\n");
}

El cual se encarga unicamente de agregar en el elemento logTextView todos los mensajes que le vayamos enviando. Para ir finalizando debemos hacer una modificacion mas a nuestro proyecto, para ello debemos ir al archivo de manifesto (AndroidManifest.xml) donde agregaremos la siguiente linea:

<uses-permission android:name="android.permission.INTERNET" />

Esta se encargara de conceder permisos de acceso a internet para nuestra actividad, con esta modificacion efectuada podemos probar nuestra app pero antes les muestro como queda finalmente nuestro archivo AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.example.videoplayer">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".VideoPlayer">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category
                    android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-permission android:name="android.permission.INTERNET" />

</manifest>
Anuncios

Y como quedo finalmente nuestra clase VideoPlayer:

package org.example.videoplayer;

import android.app.Activity;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;

public class VideoPlayer extends Activity
        implements MediaPlayer.OnBufferingUpdateListener,
        MediaPlayer.OnCompletionListener,
        MediaPlayer.OnPreparedListener, SurfaceHolder.Callback {

    private MediaPlayer mediaPlayer;
    private SurfaceView surfaceView;
    private SurfaceHolder surfaceHolder;
    private EditText editText;
    private ImageButton bPlay, bPause, bStop, bLog;
    private TextView logTextView;
    private boolean pause;
    private String path;
    private int savePos = 0;

    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.activity_main);
        surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(this);
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        editText = (EditText) findViewById(R.id.path);
        editText.setText("http://personales.gan.upv.es/~jtomas/video.3gp");

        bPlay = (ImageButton) findViewById(R.id.play);
        bPlay.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {

                if (mediaPlayer!=null){
                    if(pause) {
                        mediaPlayer.start();
                    } else {
                        playVideo();
                    }
                }
            }
        });

        bPause = (ImageButton) findViewById(R.id.pause);
        bPause.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                if (mediaPlayer!=null){
                    pause = true;
                    mediaPlayer.pause();
                }
            }
        });

        bStop = (ImageButton) findViewById(R.id.stop);
        bStop.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                if (mediaPlayer!=null){
                    mediaPlayer.stop();
                    mediaPlayer.release();
                }
            }
        });

        logTextView = (TextView) findViewById(R.id.log);
        bLog = (ImageButton) findViewById(R.id.botonLog);
        bLog.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                if (mediaPlayer!=null){
                    if(logTextView.getVisibility()== TextView.VISIBLE) {
                        logTextView.setVisibility(TextView.INVISIBLE);
                    } else {
                        logTextView.setVisibility(TextView.VISIBLE);
                    }
                }
            }
        });
        log("");
    }

    private void playVideo(){
        try {
            pause = false;
            path = editText.getText().toString();
            mediaPlayer = new MediaPlayer();
            mediaPlayer.setDataSource(path);
            mediaPlayer.setDisplay(surfaceHolder);
            mediaPlayer.prepare();
            mediaPlayer.setOnBufferingUpdateListener(this);
            mediaPlayer.setOnCompletionListener(this);
            mediaPlayer.setOnPreparedListener(this);
            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mediaPlayer.seekTo(savePos);
        }
        catch (Exception e){
            log("Error: " + e.getMessage());
        }
    }


    public void onBufferingUpdate(MediaPlayer arg0, int porcentaje){
        log("onBufferingUpdate porcentaje: " + porcentaje);
    }
    public void onCompletion(MediaPlayer arg0){
        log("onCompletion llamado.");
    }
    public void onPrepared(MediaPlayer mediaPlayer){
        log("onPrepared llamado");
        int mVideoWidth=mediaPlayer.getVideoWidth();
        int mVideoHeight=mediaPlayer.getVideoHeight();
        if (mVideoWidth!=0 && mVideoHeight != 0){
            surfaceHolder.setFixedSize(mVideoWidth, mVideoHeight);
            mediaPlayer.start();
        }
    }

    public void surfaceCreated(SurfaceHolder holder){
        log("entramos en SurfaceCreate");
        playVideo();
    }
    public void surfaceChanged(SurfaceHolder surfaceHolder,
                                int i, int j, int k){
        log("entramos en surfaceChanged");
    }
    public void surfaceDestroyed(SurfaceHolder surfaceHolder){
        log("entramos en surfaceDestroyed");
    }

    @Override
    protected void onDestroy(){
        super.onDestroy();
        if (mediaPlayer!=null){
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }

    @Override
    protected void onPause(){
        super.onPause();
        if(mediaPlayer!=null && !pause){
            mediaPlayer.pause();
        }
    }

    @Override
    protected void onResume(){
        super.onResume();
        if (mediaPlayer!=null && !pause){
            mediaPlayer.start();
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle guardarEstado){
        super.onSaveInstanceState(guardarEstado);
        if (mediaPlayer!=null){
            int pos = mediaPlayer.getCurrentPosition();
            guardarEstado.putString("ruta",path);
            guardarEstado.putInt("posicion",pos);
        }
    }

    @Override
    protected void onRestoreInstanceState(Bundle recEstado){
        super.onRestoreInstanceState(recEstado);
        if(recEstado!=null){
            path = recEstado.getString("ruta");
            savePos = recEstado.getInt("posicion");
        }
    }

    private void log(String s){
        logTextView.append(s + "\n");
    }
}


Con todas nuestras modificaciones realizadas podemos probar nuestra app, el resultado debera ser similar al que se ve en el siguiente video, disculpen la calidad pero mi emulador no es de lo mejor

Nuevamente disculpen la mala calidad, les recomiendo hacer la comprobacion desde un equipo real, o si tienen un mejor emulador, pero se puede ver como trabajan los botones, tuve dos inconvenientes que no pude solucionar, el primero es cuando detenemos el video y lo volvemos a reproducir no funciona y devuelve un error null, la verdad no me doy cuenta, y el segundo es cuando le quitamos importancia y volvemos nos genera otro mediaPlayer y se superpone al primer video pero como es una app para ver los conocimientos basicos y como funcionan les recomiendo probarlo.

Anuncios

En resumen, hoy hemos armado de forma muy basica un reproductor multimedia, hemos visto como crear una botonera, el reproductor, un log y ver como hacerlo trabajar, espero les haya sido util sigueme en Twitter, Facebook o Google+ para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00