[Windows Phone 8] Ejecución de código en segundo plano (III y Fin)

Hola a todos! Vamos a por el tercer artículo sobre ejecución de código en Background.

Volvemos a la carga, tras ver como realizar geolocalización en background y como ejecutar tareas programadas en background. En esta ocasión vamos a descubrir como reproducir música independientemente de nuestra app, de forma que si el usuario la cierra, la música siga reproduciéndose. Podemos ver ejemplos de apps que usan reproducción de audio en segundo plano, tales como Nokia MixRadio, P|Cast, Spotify y muchas otras.

Existen dos tipos de agentes de audio diferentes: El agente de reproducción de audio y el agente de streaming de audio. Su uso es exactamente el mismo. Su principal diferencia es la forma en la que entregan el audio al sistema. El agente de audio simplemente obtiene una Uri (local o remota) y la pasa al sistema de reproducción. El agente de streaming de audio debe implementar su propia clase de decodificación de audio (MediaStreamSource) encargada de decodificar el audio y pasárselo al sistema. Esto último nos permite una gran libertad a la hora de trabajar con audio, pero requiere un esfuerzo y conocimientos sobre audio y codecs extra, pues seremos los encargados de su creación de principio a fin.

En general, todo audio que se reproduce en Windows Phone, sigue la siguiente estructura:

image

En el gráfico anterior podemos separar tres partes, representadas por colores.

Las cajas grises indican partes del sistema. Entre ellas encontramos el reproductor en background del sistema, la cola de multimedia de Zune (si, Zune sigue vivo en lo más profundo de vuestros Windows Phone, aunque desde Microsoft se esfuercen en borrar su nombre de la historia…) y el control de volumen universal.

Las cajas naranjas indican las partes que debemos implementar para crear una aplicación capaz de reproducir audio en background. Simplemente tenemos la aplicación en si misma y el agente de audio.

Por último, las cajas verdes simbolizan el código que tendremos que añadir para usar un agente de streaming de audio.

Como podemos ver en el gráfico, la parte común entre ambos sistemas es que siempre tenemos que crear un agente de audio, por lo que nos centraremos en él en este artículo. Si quieres información más profunda sobre el streaming de audio, empieza tu viaje por la madriguera de conejo aquí. Suerte con la pastilla roja.

Después de haber visto las tareas programadas, esto os va a “sonar” mucho. De echo, la mecánica es la misma. tendremos un proyecto para la reproducción en background, un agente, que se integrará con nuestra aplicación y con los controles de audio del sistema. De esta forma, desde nuestra app podremos indicarle que reproducir y cuando empezar, parar o cambiar de una canción a otra. Pero al mismo tiempo, el usuario podrá abandonar nuestro programa y usar los controles de audio (reproducir, pausar, adelante y atrás) integrados en el sistema para gestionar el audio. Cada vez que el usuario realice una acción en estos controles, se invocará un método OnUserAction en nuestro agente. De la misma forma, cada vez que el estado de reproducción cambie, se invocará un método OnPlayStateChanged, por ejemplo cuando acabe una canción, para que podamos reproducir la siguiente.

Como la ejecución es bajo demanda, es decir, es el usuario o el audio quienes marcan cuando se ejecutarán estos métodos, no tenemos intervalos prefijados como en el agente de tareas programadas. Pero eso no nos libra de tener restricciones:

  • Memoria: tendremos un límite de consumo. Hasta Windows Phone 8 GDR3, este límite es de 20Mb. Con GDR3 el límite sube a los 25Mb. Es importante tener en cuenta también que, con un agente en streaming, compartiremos esa memoria con el agente de audio. no tendremos 20/25Mb para cada uno.
  • Tiempo: Aunque la ejecución se realiza bajo demanda, tendremos que llamar a NotifyComplete o Abort antes de pasar 30 segundos o se cerrará el agente.

Ahora que ya conocemos toda la teoría necesaria, vamos a ver un ejemplo práctico de reproducción de audio en segundo plano. Lo primero que vamos a hacer es crear una nueva solución, con un proyecto Blank Application y un proyecto “Windows Phone Audio Playback agent”. Por defecto nos creará una clase llamada AudioAgent.cs que hereda de AudioPlayerAgent y sobre escribe una serie de métodos:

  • OnPlayStateChanged
  • OnUserAction
  • OnError

Por defecto veremos que el método OnUserAction ya tiene una lógica básica implementada, con un Switch() case que se encarga de parar, reproducir, saltar canción, rebobinar, avanzar…

OnUserAction
protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
{
    switch (action)
    {
        case UserAction.Play:
            if (player.PlayerState != PlayState.Playing)
            {
                player.Play();
            }
            break;
        case UserAction.Stop:
            player.Stop();
            break;
        case UserAction.Pause:
            player.Pause();
            break;
        case UserAction.FastForward:
            player.FastForward();
            break;
        case UserAction.Rewind:
            player.Rewind();
            break;
        case UserAction.Seek:
            player.Position = (TimeSpan)param;
            break;
        case UserAction.SkipNext:
            player.Track = GetNextTrack();
            break;
        case UserAction.SkipPrevious:
            AudioTrack previousTrack = GetPreviousTrack();
            if (previousTrack != null)
            {
                player.Track = previousTrack;
            }
            break;
    }

    NotifyComplete();
}

También tiene dos métodos ya creados: GetPreviousTrack y GetNextTrack. Estos métodos son llamados desde OnUserAction y OnPlayStateChanged. Para hacer un reproductor básico, solo deberíamos incluir código en estos dos métodos finales para obtener la canción anterior y siguiente respectivamente.

Lo primero que vamos a hacer en la clase AudioAgent es crearnos una lista estática de AudioTracks, para este ejemplo voy a usar la url de los MP3 del podcast WPControla que hice con Rafa Serna hace un tiempo:

AudioTrack list
private static IList<AudioTrack> tracks = new List<AudioTrack>
{
    new AudioTrack
    {
        Source = new Uri(«http://audios.wpcontrola.com/capitulo1.mp3»),
        Title = «WPControla 1»,
        Artist = «Rafa y Yeray»,
        Album = «WPControla greatest hits»
    },
    new AudioTrack
    {
        Source = new Uri(«http://audios.wpcontrola.com/capitulo2.mp3»),
        Title = «Captulo 2»,
        Artist = «Rafa y Yeray»,
        Album = «WPControla greatest hits»
    },

};

Por supuesto en una aplicación real tendrías un servicio que fuese el encargado de devolverte la lista de canciones, no las tendrías codificadas en el agente de audio. Pero lo que si es recomendable es que tengas una lista estática, pues así esta no se crearía y destruiría con las diferentes invocaciones del control del sistema.

También necesitaremos un entero que nos permita seguir el índice de la lista que se está reproduciendo en cada momento.

Una vez que tenemos nuestra lista creada, vamos a añadir el código necesario a los métodos GetPreviousTrack y GetNextTrack:

GetPreviousTrack/GetNextTrack
private AudioTrack GetNextTrack()
{
    if (++currentTrackNumber >= tracks.Count)
    {
        currentTrackNumber = 0;
    }

    return tracks[currentTrackNumber];
}

private AudioTrack GetPreviousTrack()
{
    if (currentTrackNumber < 0)
    {
        currentTrackNumber = tracks.Count 1;
    }

    return tracks[currentTrackNumber];
}

Simplemente nos movemos por la lista, devolviendo la track que corresponda en cada caso. Por último para terminar con el código de nuestro agente, solo nos queda controlar la acción Play en el método OnUserAction. Si la track del reproductor no está establecida, la establecemos nosotros:

Acción Play
case UserAction.Play:
    if (player.PlayerState != PlayState.Playing)
    {
        if (player.Track == null)
            player.Track = tracks[currentTrackNumber];
        player.Play();
    }
    break;

Y ahora ya podemos ir a nuestra aplicación. Este ejemplo es muy simple. Tendremos un botón “play” con el que comenzar la reproducción de nuestros tracks en segundo plano. Para ello usaremos la clase BackgroundAudioPlayer, que contiene la instancia global del reproductor del sistema:

PlayAudio command execution
private void PlayAudioExecute()
{
    if (BackgroundAudioPlayer.Instance.PlayerState != PlayState.Playing)
        BackgroundAudioPlayer.Instance.Play();
    else
        BackgroundAudioPlayer.Instance.Pause();
}

Y Voila! Ya tenemos nuestro reproductor del Podcast de WPControla integrado con el sistema!

Si ejecutamos la aplicación y presionamos el botón “play”, empezaremos a escuchar el capítulo 1 de WPControla. Podemos volver a la pantalla de inicio del sistema y presionar la tecla de volumen para ver que se muestra la información de cada track y responde a nuestras ordenes.

NOTA: Para que todo funcione, pruébalo en un dispositivo real, en los emuladores tiene un comportamiento errático que lo hace fallar a veces.

Y con esto llegamos al final de esta serie dedicada a la ejecución de código en segundo plano. Como siempre, aquí tienes el código listo para descarga, de forma que tengas un punto de partida, con MVVM y bien colocado.

Un saludo y Happy Coding!!

P.D.: Si quieres escuchar todos los capítulos de WPControla, puedes hacerlo aquí!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *