Revisión de código de ejemplo de MediaPlayer de Android* en Arquitectura Intel®

Introducción

La reproducción multimedia se está convirtiendo en uno de los usos más frecuentes que se da a los dispositivos móviles. La gente espera poder reproducir los tipos más comunes de elementos multimedia en estos dispositivos y mirar video mientras se desplaza. En este artículo se analizarán los aspectos básicos de la creación de aplicaciones multimedia para Android y se revisará un código de ejemplo de API MediaPlayer para plataformas basadas en la arquitectura Intel®.

Marco de trabajo multimedia de Android; API MediaPlayer

El marco de trabajo multimedia de Android proporciona a los desarrolladores una manera de integrar con facilidad la reproducción de audio y video en aplicaciones. Además, es compatible con la mayoría de los tipos de medios comunes. La clase MediaPlayer es la clave de este marco de trabajo. Se puede usar para reproducir elementos multimedia en el sistema de archivos local, reproducir archivos multimedia almacenados en los recursos de las aplicaciones y para la transmisión por secuencias de datos en una conexión de red.

Manejo del estado

MediaPlayer es una clase basada en estados; tiene una máquina de estados interna que maneja todos los estados del ciclo de vida de los objetos MediaPlayer. El diagrama de abajo muestra los cambios de estado del objeto MediaPlayer para el control de reproducción. En este diagrama, las flechas simples representan llamadas a métodos sincrónicos y las flechas dobles representan llamadas a métodos asincrónicos y llamadas de respuesta.


Figure 1. Cambios de estado del objeto MediaPlayer (las flechas simples representan llamadas a métodos sincrónicos y las flechas dobles representan llamadas a métodos asincrónicos).

En el diagrama se ve que el objeto MediaPlayer tiene varios estados en el ciclo de vida. Al comienzo, crear un nuevo MediaPlayer o llamar al método reset() hace que MediaPlayer pase al estado Idle (inactivo). En este estado es donde comienza todo, pero todavía no es posible la reproducción. Si se invoca algún método de control de reproducción como start(), stop(), seekTo(int), etc., se producirá un error de programación.

Lo que debe hacer la aplicación a continuación es llamar al método setDataSource() para apuntar a una fuente multimedia válida, lo cual hará que el reproductor pase al estado Initialized (inicializado). La fuente puede ser un archivo local o una transmisión continua de datos desde una conexión de red.

En el estado Initialized y antes de que pueda comenzar la reproducción, la aplicación puede llamar a prepare() o prepareAsync() para pasar al estado Prepared (preparado). El método prepare() se encarga de capturar, almacenar en el búfer y decodificar el archivo multimedia. Sin embargo, prepare() puede tardar mucho en devolver el resultado cuando captura datos multimedia desde la dirección URL de una red, en especial si la conexión de red es lenta. Por lo tanto, no se recomienda ejecutar prepare() en el subproceso de interfaz de usuario de la aplicación; la consecuencia de esto podría ser que la aplicación no responda y la experiencia del usuario sea negativa. Se recomienda usar prepareAsync() en su lugar, que fue ideado para atacar este problema y proporciona una manera cómoda de preparar los datos multimedia sin que deje de responder la interfaz de usuario. prepareAsync() se ejecuta en segundo plano y, cuando termina, devuelve el resultado de inmediato y envía una llamada de respuesta OnPreparedListener.onPrepared() con el fin de llevar al objeto MediaPlayer al estadoPrepared.

Desde el estado Prepared, se puede iniciar la reproducción y controlarla mediante llamadas a start() y seekTo(). Una vez iniciada la reproducción multimedia, el objeto MediaPlayer pasa al estado Started (iniciado).

Después de comenzada la reproducción, se puede controlar con el método pause(), que hace pasar el objeto al estado Paused (en pausa), o llamar a start() para volver al estado Started. Si la reproducción llega al final y no se ha establecido la repetición desde el principio, el MediaPlayer pasa al estado PlaybackCompleted (reproducción terminada). En este punto, se puede llamar al método start() para volver a iniciar la reproducción, y el estado vuelve a Started. Sin embargo, si se llama a stop() desde Started, Paused o PlaybackCompleted, la máquina de estados se mueve al estado Stopped (detenido). Desde aquí, se puede ir al estado End (fin) y liberar el MediaPlayer, o si se quiere volver a reproducir algún elemento, hay que preparar los datos multimedia antes de volver a llamar a start().

Nunca hay que olvidar llamar al método release() después de cada uso para poner el objeto MediaPlayer en el estado End; si no, continúa consumiendo recursos del sistema. Si se siguen creando instancias de MediaPlayer sin llamar al método release(), la aplicación podría consumir la totalidad de los recursos muy rápidamente.

Si se registró OnErrorListener, se invocará el método de llamada de respuesta OnErrorListener.onError() en todas las condiciones de error, para que se puedan manejar los errores según corresponda.

Uso de MediaPlayer para reproducir audio y video

Ahora echemos un vistazo al código para reproducir audio y video con MediaPlayer.

Para audio:

private void playAudio(Integer media) {
    try {
        switch (media) {
            case LOCAL_AUDIO:
                path = "/sdcard/Download/music/1.mp3";
                mMediaPlayer = new MediaPlayer();
                mMediaPlayer.setDataSource(path);
                mMediaPlayer.prepare();
                mMediaPlayer.start();
                break;
            case RESOURCES_AUDIO:
                mMediaPlayer = MediaPlayer.create(this, R.raw.test_cbr);
                mMediaPlayer.start();

        }
    } catch (Exception e) {
        Log.e(TAG, "error: " + e.getMessage(), e);
    }

}

@Override
protected void onDestroy() {
    super.onDestroy();
    // TODO Auto-generated method stub
    if (mMediaPlayer != null) {
        mMediaPlayer.release();
        mMediaPlayer = null;
    }

}

Para video:

private void playVideo(Integer Media) {
    doCleanUp();
    try {

        switch (Media) {
            case LOCAL_VIDEO:
                path = "/sdcard/Download/video/2.mp4";
                break;
            case STREAM_VIDEO:
                path = "Your URL here";
                break;
        }
        mMediaPlayer = new MediaPlayer();
        mMediaPlayer.setDataSource(path);
        mMediaPlayer.setDisplay(holder);
        mMediaPlayer.prepare();
        mMediaPlayer.setOnBufferingUpdateListener(this);
        mMediaPlayer.setOnCompletionListener(this);
        mMediaPlayer.setOnPreparedListener(this);
        mMediaPlayer.setOnVideoSizeChangedListener(this);
        mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

    } catch (Exception e) {
        Log.e(TAG, "error: " + e.getMessage(), e);
    }
}
public void onBufferingUpdate(MediaPlayer arg0, int percent) {
    Log.d(TAG, "onBufferingUpdate percent:" + percent);
}
public void onCompletion(MediaPlayer arg0) {
    Log.d(TAG, "onCompletion called");
}
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
    Log.v(TAG, "onVideoSizeChanged called");
    if (width == 0 || height == 0) {
        Log.e(TAG, "invalid video width(" + width + ") or height(" + height + ")");
        return;
    }
    mIsVideoSizeKnown = true;
    mVideoWidth = width;
    mVideoHeight = height;
    if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
		startVideoPlayback();
    }
}
public void onPrepared(MediaPlayer mediaplayer) {
    Log.d(TAG, "onPrepared called");
    mIsVideoReadyToBePlayed = true;
    if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
        startVideoPlayback();
    }
}
@Override
protected void onPause() {
    super.onPause();
    releaseMediaPlayer();
    doCleanUp();
}
@Override
protected void onDestroy() {
    super.onDestroy();
    releaseMediaPlayer();
    doCleanUp();
}
private void releaseMediaPlayer() {
    if (mMediaPlayer != null) {
        mMediaPlayer.release();
        mMediaPlayer = null;
    }
}
private void doCleanUp() {
    mVideoWidth = 0;
    mVideoHeight = 0;
    mIsVideoReadyToBePlayed = false;
    mIsVideoSizeKnown = false;
}
private void startVideoPlayback() {
    Log.v(TAG, "startVideoPlayback");
    holder.setFixedSize(mVideoWidth, mVideoHeight);
    mMediaPlayer.start();
}

Como se puede apreciar a partir del código, tanto para audio como para video, el MediaPlayer se mueve por el ciclo de vida y los cambios de estado como se indica en el diagrama de cambios de estado que incluimos más arriba.

En la página Intel Android se pueden encontrar más recursos para aplicaciones en proyectos Android en la Arquitectura Intel.

Enlaces de consulta

  1. http://developer.android.com/reference/android/media/MediaPlayer.html
  2. http://developer.android.com/reference/android/widget/MediaController.html