IMPORTANT: Per accedir als fitxer de subversion: http://acacha.org/svn (sense password). Poc a poc s'aniran migrant els enllaços. Encara però funciona el subversion de la farga però no se sap fins quan... (usuari: prova i la paraula de pas 123456)

El Android multimedia framework inclou suport per executar una amplia diversitat de tipus de medis de forma que sigui fàcil d'integrar audio, video o imatges a les aplicacions Android.

Els fitxers de media es pode executar des de fitxers emmagatzemats en local que poden estar dins dels fitxers o recursos Android de la mateixa aplicació ( raw resources), de fitxers del sistema de fitxers local (p. ex. la memòria SSD) o d'streams de dades (Streaming). S'utilitzen les API MediaPlayer

NOTA: No és possible executar sons durant una trucada

Classes i paquets

android.media

android.media.MediaPlayer

MediaPlayer és la classe principal de l'API i permet executar tant audio com vídeo.

android.media.AudioManager

AudioManager This class manages audio sources and audio output on a device.

Permisos -> Fitxer de manifest

Al fitxer de manifest de l'aplicació (vegeu Manifest.xml) cal assignar els permisos correctes a l'aplicació:

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

Diagrama d'estats

El control de l'execució de medis (aka playback) es controlat per una màquina d'estats. El següent diagrama mostra el cicle de vida i els estats de l'objecte MediaPlayer:

mediaplayer_state_diagram.gif

Les operacions que es poden realitzar són:

Els ovoides representen els estats. A les fletxes apareixen les operacions de playback que provoquen un transició d'estat. Les fletxes simples representen mètodes síncrons, en canvi els dobles representen mètodes asíncrons.

Els estats possibles són:

  • Idle: és l'estat en que es troba l'objecte just després de ser creat o just després d'un reset. Estat inicial
  • Error: Per múltiples raons (per posar alguns exemples: un format no suportat, un timeout d'Streaming, resolució massa alta, etc.) el reproductor pot fallar en qualsevol moment. Sigui quin sigui l'error el sistema invoca sempre el mètode OnErrorListener.onError() sempre i quan s'hagi registrat prèviament un OnErrorListener amb el mètode setOnErrorListener(android.media.MediaPlayer.OnErrorListener)'. En tot cas l'objecte entrarà en l'estat d'error i només es podrà sortir d'aquest estat i reutilitzar l'objecte amb un reset o amb un release.
  • Initialized: Cal executar un dels següents mètodes setDataSource(FileDescriptor), setDataSource(String, setDataSource(Context, Uri) o setDataSource(FileDescriptor, long, long) per passar a l'estat inicialitzat. Cal tenir en compte que els MediaPlayer creats amb un new van a l'estat Idle, però els creats amb mètodes creadors de l'objecte no, aquest últims es creen ja inicialitzats. Un excepció de tipus IllegalStateException succeix quan es crida a qualsevol dels mètodes setDataSource() des de un estat diferent al Idle. Hi ha dos formes en que es pot arribar a l'estat prepared:
  • Prepared: l'objecte MediaPlayer ha d'entrar en aquest estat abans de poder fer un play dels continguts multimèdia. Hi ha dos formes d'assolir aquest estat
  • Sincronament: si arriba amb el mètode prepare() un cop s'ha executat amb èxit.
  • Asíncronament: si arriba amb el mètode prepareAsync() , en aquest cas primer es canvia l'estat de l'objecte a Prepared i mentrestant es realitzen les tasques per preparar realment l'objecte. Consulteu Execució asíncrona
  • Un cop la preparació s'acaba (sigui síncrona o asíncrona) el sistema crida al mètode onPrepared() de la interfície OnPreparedListener, sempre i quan s'hagui enregistrat un objecte de tipus OnPreparedListener utilitzant el mètode: setOnPreparedListener(android.media.MediaPlayer.OnPreparedListener). Els mètodes prepare() o prepareAsync() només es poden cridar des de l'estat initialized o stoppped sinó succeirà una IllegalStateException.
En aquest estats es poden fer ajustos com el controlar el volum, looping invocant els mètodes corresponents...
  • Started: S'arriba a aquest al finalitzar correctament l'execució del mètode start(). Es pot utilitzar el mètode isPlaying() per tal de comprovar si un MediaPlayer s'està executant. Durant l'execució es crida al mètode OnBufferingUpdateListener.onBufferingUpdate() si prèviament s'ha registrat un OnBufferingUpdateListener amb el mètode: setOnBufferingUpdateListener(OnBufferingUpdateListener). Això permet controlar els buffers al fer Streaming.Cridar el mètode start en un MediaPlayer que ja estigui en aquest estat no té cap efecte.
  • Paused: s'entra en aquest estat quan el mètode pause() s'executa correctament. Cal fer notar que la transició de Started a paused és asíncrona i pot tardar diversos segons, per exemple en el cas de continguts d'Streaming.
  • Stopped: S'arriba a aquest estat un cop executat correctament el mètode stop(). A diferència de la pausa no es pot reprendre l'execució amb start() si no es torna a preparar l'objecte amb els mètode prepare() o prepareAsync.
  • PlayBackcompleted: si arriba un cop acabada la reproducció dels medis i un cop executat el mètode OnCompletion.onCompletion() sempre i quan s'hagi registrat un OnCompletionListener amb setOnCompletionListener(OnCompletionListener) i sinó està activat el Looping (setLooping(boolean)). Un start torna a executar el medi des de l'inici.
  • End: just després de cridar release(). L'objecte deixa d'existir i utilitzar recursos dels sistema. Estat final

Utilitzar el MediaPlayer

Un objecte de la classe MediaPlayer facilita l'execució d'audio i vídeo amb una configuració mínima.

És possible executar:

Podeu consultar una llista dels formats suportats a:

http://developer.android.com/guide/appendix/media-formats.html

Vegem un exemple:

 MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
 mediaPlayer.start(); // no need to call prepare(); create() does that for you

L'exemple executa un audio d'un raw resource ( carpeta res/raw/):

Ara vegem un exemple de com utilitzar una URI:

 Uri myUri = ....; // initialize Uri here
 MediaPlayer mediaPlayer = new MediaPlayer();
 mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
 mediaPlayer.setDataSource(getApplicationContext(), myUri);
 mediaPlayer.prepare();
 mediaPlayer.start();

Consulteu també Android_Multimedia#Obtenir_medis_d.27un_Content_Resolver

O un exemple d'una URL remota:

 String url = "http://........"; // your URL here
 MediaPlayer mediaPlayer = new MediaPlayer();
 mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
 mediaPlayer.setDataSource(url);
 mediaPlayer.prepare(); // might take long! (for buffering, etc)
 mediaPlayer.start();

IMPORTANT: cal implementar les excepcions adients per controlar els errors en cas que el recurs a executar no existeixi (IllegalArgumentException i IOException )

Alliberar recursos. Releasing MediaPlayer

Els MediaPlayer consumeixen recursos i els recursos són un valor important en aplicacions mòbils. Per aquesta raó quan ja no s'utilitza un MediaPLayer cal alliberar recursos amb el mètode:

release()

Per exemple, al mètode onStop() d'una activitat (a no ser que estiguem executant audio en segon terme, aleshores és recomana utilitzar un servei)

El codi per alliberar recursos és:

 mediaPlayer.release();
 mediaPlayer = null;

IMPORTANT: si no s'alliberen recursos a més poden succeir duplicacions quan l'activitat es torni a reprendre, per exemple al canviar entre landscape i portrait. Vegeu Handling Runtime Changes

OnDestroy

És important assegurar-se que s'alliberen recursos per aquesta raó és una bona pràctica sobrescriure el mètode onDestroy() de les Actitivitats o Serveis Android:

public class MyService extends Service {
   MediaPlayer mMediaPlayer;
   // ...

   @Override
   public void onDestroy() {
       if (mMediaPlayer != null) mMediaPlayer.release();
   }
}

Sempre que es pugui cal mirar d'alliberar recursos sempre que es preveixi que durant un temps considerable no s'utilitzaran, per exemple al perdre el audio focus. En canvi si és poca estona més val la pena aturar l'execució una estona i reprendre-la.

Fils d'execució. Execució asíncrona

Tal i com s'explica a Procesos Android, cal evitar executar operacions lentes des del UI thread. Per això caldria crear els objectes MediaPlayer des de un fil d'execució diferent. Com que aquest patró de disseny és molt habitual el sistema ja us proporciona el mètode:

prepareAsync()

Aquest mètode prepara els medis a executar i retorna immediatament mentrestant es fa la tasca per no penjar l'aplicació. Un cop el contingut està preparat, es crida el mètode

onPrepared()

del Listener

MediaPlayer.OnPreparedListener

que es configura amb el mètode:

setOnPreparedListener()

Executar un medi com un servei en segon terme

L'única manera d'executar un medi en segon terme (cosa que només sol tenir sentit en el cas d'àudio i no pas de vídeo, ja que en segon terme vol dir que no es veu el contingut de reproductor per pantalla però si que es sent l'audio) cal utilitzar un Servei Android i controlar l'execució des del servei.

Executar de forma asíncrona

Vegem un exemple, utilitzant prepareAsync() en comptes de prepare i com implementem el mètode:

MediaPlayer.OnPreparedListener
public class MyService extends Service implements MediaPlayer.OnPreparedListener {
    private static final ACTION_PLAY = "com.example.action.PLAY";
    MediaPlayer mMediaPlayer = null;

    public int onStartCommand(Intent intent, int flags, int startId) {
        ...
        if (intent.getAction().equals(ACTION_PLAY)) {
            mMediaPlayer = ... // initialize it here
            mMediaPlayer.setOnPreparedListener(this);
            mMediaPlayer.prepareAsync(); // prepare async to not block main thread
        }
    }

    /** Called when MediaPlayer is ready */
    public void onPrepared(MediaPlayer player) {
        player.start();
    }
}

Handling asynchronous errors

Al executar operacions de forma asíncrona ens hem d'assegurar que se'ns notifiquen els errors. En el cas del MediaPlayer cal crear un Listener

MediaPlayer.OnErrorListener

Un exemple:

public class MyService extends Service implements MediaPlayer.OnErrorListener {
    MediaPlayer mMediaPlayer;

    public void initMediaPlayer() {
        // ...initialize the MediaPlayer here...

        mMediaPlayer.setOnErrorListener(this);
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        // ... react appropriately ...
        // The MediaPlayer has moved to the Error state, must be reset!
    }
}

Cal recordar que quan succeïx un error, el MediaPlayer es mou cap a l'estat Error i cal resetejar-lo abans de poder tornar a utilitzar-lo.

Wake locks

When designing applications that play media in the background, the device may go to sleep while your service is running. Because the Android system tries to conserve battery while the device is sleeping, the system tries to shut off any of the phone's features that are not necessary, including the CPU and the WiFi hardware. However, if your service is playing or streaming music, you want to prevent the system from interfering with your playback.

In order to ensure that your service continues to run under those conditions, you have to use "wake locks." A wake lock is a way to signal to the system that your application is using some feature that should stay available even if the phone is idle.

Notice: You should always use wake locks sparingly and hold them only for as long as truly necessary, because they significantly reduce the battery life of the device.

To ensure that the CPU continues running while your MediaPlayer is playing, call the setWakeMode() method when initializing your MediaPlayer. Once you do, the MediaPlayer holds the specified lock while playing and releases the lock when paused or stopped:

mMediaPlayer = new MediaPlayer();
// ... other initialization here ...
mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

However, the wake lock acquired in this example guarantees only that the CPU remains awake. If you are streaming media over the network and you are using Wi-Fi, you probably want to hold a WifiLock as well, which you must acquire and release manually. So, when you start preparing the MediaPlayer with the remote URL, you should create and acquire the Wi-Fi lock. For example:

WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
    .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");

wifiLock.acquire();

When you pause or stop your media, or when you no longer need the network, you should release the lock:

wifiLock.release();

Executant com un servei en segon terme

Vegeu també Android_Services#Executant_un_servei_en_Foreground i PendingIntent.

Els serveis s'utilitzen sovint per realitzar tasques en segon terme com obtenir correus electrònics, sincronitzar dades, descarregar continguts, entre d'altres. En tots aquests casos l'usuari no és activament conscient de l'execució i per exemple si s'atura un moment i després torna a començar l'usuari ni se n'adonarà.

El cas de la música és molt diferent, ja que les interrupcions donaran a l'usuari una mala experiència. Per aquest casos és important utilitzar un foreground service, com hem vist a Procesos Android el sistema operatiu dona una gran importància a estos serveis i casi mai matarà aquest tipus de serveis.

Vegem un exemple amb startForeground():

String songName;
// assign the song name to songName
PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0,
                new Intent(getApplicationContext(), MainActivity.class),
                PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification();
notification.tickerText = text;
notification.icon = R.drawable.play0;
notification.flags |= Notification.FLAG_ONGOING_EVENT;
notification.setLatestEventInfo(getApplicationContext(), "MusicPlayerSample",
                "Playing: " + songName, pi);
startForeground(NOTIFICATION_ID, notification);

Fixeu-vos en l'ús de notificacions:

notification1.png
notification2.png

Cal que només s'executin les notificacions mentrestant l'usuari està al cas del que s'està fent. Atureu el servei en segon terme amb:

stopForeground()

Consulteu també Notificacions Android

Audio focus

Even though only one activity can run at any given time, Android is a multi-tasking environment. This poses a particular challenge to applications that use audio, because there is only one audio output and there may be several media services competing for its use. Before Android 2.2, there was no built-in mechanism to address this issue, which could in some cases lead to a bad user experience. For example, when a user is listening to music and another application needs to notify the user of something very important, the user might not hear the notification tone due to the loud music. Starting with Android 2.2, the platform offers a way for applications to negotiate their use of the device's audio output. This mechanism is called Audio Focus.

When your application needs to output audio such as music or a notification, you should always request audio focus. Once it has focus, it can use the sound output freely, but it should always listen for focus changes. If it is notified that it has lost the audio focus, it should immediately either kill the audio or lower it to a quiet level (known as "ducking"—there is a flag that indicates which one is appropriate) and only resume loud playback after it receives focus again.

Audio Focus is cooperative in nature. That is, applications are expected (and highly encouraged) to comply with the audio focus guidelines, but the rules are not enforced by the system. If an application wants to play loud music even after losing audio focus, nothing in the system will prevent that. However, the user is more likely to have a bad experience and will be more likely to uninstall the misbehaving application.

To request audio focus, you must call requestAudioFocus() from the AudioManager, as the example below demonstrates:

AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,

   AudioManager.AUDIOFOCUS_GAIN);

if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {

   // could not get audio focus.

}

The first parameter to requestAudioFocus() is an AudioManager.OnAudioFocusChangeListener, whose onAudioFocusChange() method is called whenever there is a change in audio focus. Therefore, you should also implement this interface on your service and activities. For example:

class MyService extends Service

               implements AudioManager.OnAudioFocusChangeListener {
   // ....
   public void onAudioFocusChange(int focusChange) {
       // Do something based on focus change...
   }

}

The focusChange parameter tells you how the audio focus has changed, and can be one of the following values (they are all constants defined in AudioManager):

   AUDIOFOCUS_GAIN: You have gained the audio focus.
   AUDIOFOCUS_LOSS: You have lost the audio focus for a presumably long time. You must stop all audio playback. Because you should expect not to have focus back for a long time, this would be a good place to clean up your resources as much as possible. For example, you should release the MediaPlayer.
   AUDIOFOCUS_LOSS_TRANSIENT: You have temporarily lost audio focus, but should receive it back shortly. You must stop all audio playback, but you can keep your resources because you will probably get focus back shortly.
   AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: You have temporarily lost audio focus, but you are allowed to continue to play audio quietly (at a low volume) instead of killing audio completely.

Here is an example implementation:

public void onAudioFocusChange(int focusChange) {

   switch (focusChange) {
       case AudioManager.AUDIOFOCUS_GAIN:
           // resume playback
           if (mMediaPlayer == null) initMediaPlayer();
           else if (!mMediaPlayer.isPlaying()) mMediaPlayer.start();
           mMediaPlayer.setVolume(1.0f, 1.0f);
           break;
       case AudioManager.AUDIOFOCUS_LOSS:
           // Lost focus for an unbounded amount of time: stop playback and release media player
           if (mMediaPlayer.isPlaying()) mMediaPlayer.stop();
           mMediaPlayer.release();
           mMediaPlayer = null;
           break;
       case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
           // Lost focus for a short time, but we have to stop
           // playback. We don't release the media player because playback
           // is likely to resume
           if (mMediaPlayer.isPlaying()) mMediaPlayer.pause();
           break;
       case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
           // Lost focus for a short time, but it's ok to keep playing
           // at an attenuated level
           if (mMediaPlayer.isPlaying()) mMediaPlayer.setVolume(0.1f, 0.1f);
           break;
   }

}

Keep in mind that the audio focus APIs are available only with API level 8 (Android 2.2) and above, so if you want to support previous versions of Android, you should adopt a backward compatibility strategy that allows you to use this feature if available, and fall back seamlessly if not.

You can achieve backward compatibility either by calling the audio focus methods by reflection or by implementing all the audio focus features in a separate class (say, AudioFocusHelper). Here is an example of such a class:

public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener {

   AudioManager mAudioManager;
   // other fields here, you'll probably hold a reference to an interface
   // that you can use to communicate the focus changes to your Service
   public AudioFocusHelper(Context ctx, /* other arguments here */) {
       mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
       // ...
   }
   public boolean requestFocus() {
       return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
           mAudioManager.requestAudioFocus(mContext, AudioManager.STREAM_MUSIC,
           AudioManager.AUDIOFOCUS_GAIN);
   }
   public boolean abandonFocus() {
       return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
           mAudioManager.abandonAudioFocus(this);
   }
   @Override
   public void onAudioFocusChange(int focusChange) {
       // let your service know about the focus change
   }

}

You can create an instance of AudioFocusHelper class only if you detect that the system is running API level 8 or above. For example:

if (android.os.Build.VERSION.SDK_INT >= 8) {

   mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(), this);

} else {

   mAudioFocusHelper = null;

}


VideoView

VideoView és un Android Widget que permet veure de forma simple un vídeo a la nostra aplicació.

NOTA: Internament utilitza MediaPlayer

Exemple bàsic:

Layout XML.

<?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"
    android:background="#FFFFFFFF" >

    <VideoView
        android:id="@+id/videoView_video"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentTop="true"
        android:layout_centerInParent="true" />

</RelativeLayout>

Codi Java

 @Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		VideoView videoView = (VideoView) findViewById(R.id.videoView_video);

		Uri path = Uri.parse("android.resource://com.example.reproducirvideo/"
				+ R.raw.intromono);

		videoView.setVideoURI(path);
                videoView.setMediaController(new MediaController(this));
                videoView.requestFocus();
		videoView.start();
	}

Recursos:

Exemples

Obtenir medis d'un Content Resolver

Això ens permet obtenir medis que l'usuari té al dispositiu:

ContentResolver contentResolver = getContentResolver();
Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor == null) {
    // query failed, handle error.
} else if (!cursor.moveToFirst()) {
    // no media on the device
} else {
    int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);
    int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
    do {
       long thisId = cursor.getLong(idColumn);
       String thisTitle = cursor.getString(titleColumn);
       // ...process entry...
    } while (cursor.moveToNext());
}

Per utilitzar-lo:

long id = /* retrieve it from somewhere */;
Uri contentUri = ContentUris.withAppendedId(
        android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);

mMediaPlayer = new MediaPlayer();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setDataSource(getApplicationContext(), contentUri);

// ...prepare and start...

Aturar la reproducció quan es passa de treballar amb cascos a treballar sense. AUDIO_BECOMING_NOISY Intent

El Intent AUDIO_BECOMING_NOISY permet que s'aturi l'execució d'un audio quan l'usuari passa d'utilitzar els cascos a utilitzar els altaveus externs.

You can ensure your app stops playing music in these situations by handling the ACTION_AUDIO_BECOMING_NOISY intent, for which you can register a receiver by adding the following to your manifest:

Al fitxer de manifest:

<receiver android:name=".MusicIntentReceiver">
   <intent-filter>
      <action android:name="android.media.AUDIO_BECOMING_NOISY" />
   </intent-filter>
</receiver>

Això registra la classe MusicIntentReceiver com a broadcast receiver per aquest tipus d'intent. Aleshores cal implementar la classe:

public class MusicIntentReceiver implements android.content.BroadcastReceiver {
   @Override
   public void onReceive(Context ctx, Intent intent) {
      if (intent.getAction().equals(
                    android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
          // signal your service to stop playback
          // (via an Intent, for instance)
      }
   }
}

Camera

Cridar una aplicació externa de fotos pre obtenir una foto i mostrar-la a un imageView. Intent

Recursos

Exemple per agafar imatge de les carpetes internes (apartat 3):

Exemple per fer foto directe (apartat 4):

Exemple per cridar app externa amb intent (app de camera):

Altres

Ídem anterior amb vídeos

Recursos:

Llibreries

Imatges

Vegeu Android Images/Android Imatges

Vegeu també

Enllaços externs