Licence CC BY-NC-SA

Le multimédia

Ce contenu est obsolète. Il peut contenir des informations intéressantes mais soyez prudent avec celles-ci.

Il y a une époque pas si lointaine où, quand on voulait écouter de la musique en faisant son jogging, il fallait avoir un lecteur dédié, un walkman. Et si on voulait regarder un film dans le train, il fallait un lecteur DVD portable. Heureusement, avec les progrès de la miniaturisation, il est maintenant possible de le faire n'importe où et n'importe quand, avec n'importe quel smartphone. Clairement, les appareils mobiles doivent désormais remplir de nouvelles fonctions, et il faut des applications pour assumer ces fonctions.

C'est pourquoi nous verrons ici comment lire des musiques ou des vidéos, qu'elles soient sur un support ou en streaming. Mais nous allons aussi voir comment effectuer des enregistrements audio et vidéo.

Le lecteur multimédia

Où trouver des fichiers multimédia ?

Il existe trois emplacements à partir desquels vous pourrez lire des fichiers multimédia :

  1. Vous pouvez tout d'abord les insérer en tant que ressources dans votre projet, auquel cas il faut les mettre dans le répertoire res/raw. Vous pouvez aussi les insérer dans le répertoire assets/ afin d'y accéder avec une URI de type file://android_asset/nom_du_fichier.format_du_fichier. Il s'agit de la solution la plus simple, mais aussi de la moins souple.
  2. Vous pouvez stocker les fichiers sur l'appareil, par exemple sur le répertoire local de l'application en interne, auquel cas ils ne seront disponibles que pour cette application, ou alors sur un support externe (genre carte SD), auquel cas ils seront disponibles pour toutes les applications de l'appareil.
  3. Il est aussi possible de lire des fichiers en streaming sur internet.

Formats des fichiers qui peuvent être lus

Tout d'abord, pour le streaming, on accepte le RTSP, RTP et le streaming via HTTP.

Ensuite, je vais vous présenter tous les formats que connaît Android de base. En effet, il se peut que le constructeur de votre téléphone ait rajouté des capacités que je ne peux connaître. Ainsi, Android pourra toujours lire tous les fichiers présentés ci-dessous. Vous devriez comprendre toutes les colonnes de ce tableau, à l'exception peut-être de la colonne « Encodeur » : elle vous indique si oui ou non Android est capable de convertir un fichier vers ce format.

Audio

Format

Encodeur

Extension

AAC LC

oui

3GPP (.3gp), MPEG-4 (.mp4, .m4a)

HE-AACv1 (AAC+)

3GPP (.3gp), MPEG-4 (.mp4, .m4a)

HE-AACv2 (enhanced AAC+)

3GPP (.3gp), MPEG-4 (.mp4, .m4a)

AMR-NB

oui

3GPP (.3gp)

AMR-WB

oui

3GPP (.3gp)

MP3

MP3 (.mp3)

MIDI

Type 0 and 1 (.mid, .xmf, .mxmf), RTTTL/RTX (.rtttl, .rtx), OTA (.ota), iMelody (.imy)

Vorbis

Ogg (.ogg), Matroska (.mkv, Android 4.0+)

PCM/WAVE

WAVE (.wav)

Vidéo

Format

Encodeur

Extension

H.263

oui

3GPP (.3gp), MPEG-4 (.mp4)

H.264 AVC

3GPP (.3gp), MPEG-4 (.mp4)

MPEG-4 SP

3GPP (.3gp)

Le lecteur multimédia

Permissions

La première chose qu'on va faire, c'est penser aux permissions qu'il faut demander. Il n'y a pas de permission en particulier pour la lecture ou l'enregistrement ; en revanche, certaines fonctionnalités nécessitent quand même une autorisation. Par exemple, pour le streaming, il faut demander l'autorisation d'accéder à internet :

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

De même, il est possible que vous vouliez faire en sorte que l'appareil ne se mette jamais en veille de façon à ce que l'utilisateur puisse continuer à regarder une vidéo qui dure longtemps sans être interrompu :

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

La lecture

La lecture de fichiers multimédia se fait avec la classe MediaPlayer. Sa vie peut être représentée par une machine à état, c'est-à-dire qu'elle traverse différents états et que la transition entre chaque état est symbolisée par des appels à des méthodes.

Comme pour une activité ?

Mais oui, exactement, vous avez tout compris !

Je pourrais très bien expliquer toutes les étapes et toutes les transitions, mais je doute que cela puisse vous être réellement utile, je ne ferais que vous embrouiller, je vais donc simplifier le processus. On va ainsi ne considérer que cinq états : initialisé quand on crée le lecteur, préparé quand on lui attribue un média, démarré tant que le média est joué, en pause quand la lecture est mise en pause ou arrêté quand elle est arrêtée, et enfin terminé quand la lecture est terminée.

Tout d'abord, pour créer un MediaPlayer, il existe un constructeur par défaut qui ne prend pas de paramètre. Un lecteur ainsi créé se trouve dans l'état initialisé. Vous pouvez ensuite lui indiquer un fichier à lire avec void setDataSource(String path) ou void setDataSource(Context context, Uri uri). Il nous faut ensuite passer de l'état initialisé à préparé (c'est-à-dire que le lecteur aura commencé à lire le fichier dans sa mémoire pour pouvoir commencer la lecture). Pour cela, on utilise une méthode qui s'appelle simplement void prepare().

Cette méthode est synchrone, elle risque donc de bloquer le thread dans lequel elle se trouve. Ainsi, si vous appelez cette méthode dans le thread UI, vous risquez de le bloquer. En général, si vous essayez de lire dans un fichier cela devrait passer, mais pour un flux streaming il ne faut jamais faire cela. De manière générale, il faut appeler prepare() dans un thread différent du thread UI. Vous pouvez aussi appeler la méthode void prepareAsync(), qui est asynchrone et qui le fait de manière automatique pour vous.

Il est aussi possible de créer un lecteur multimédia directement préparé avec une méthode de type create :

1
2
3
4
5
6
7
// public static MediaPlayer create (Context context, int resid)
MediaPlayer media = MediaPlayer.create(getContext(), R.raw.file);
// public static MediaPlayer create (Context context, Uri uri)
media = MediaPlayer.create(getContext(), Uri.parse("file://android_asset/fichier.mp4");
media = MediaPlayer.create(getContext(), Uri.parse("file://sdcard/music/fichier.mp3");
media = MediaPlayer.create(getContext(), Uri.parse("http://www.site_trop_cool.com/musique.mp3");
media = MediaPlayer.create(getContext(), Uri.parse("rtsp://www.site_trop_cool.com/streaming.mov");

Maintenant que notre lecteur est en mode préparé, on veut passer en mode démarré qui symbolise la lecture du média ! Pour passer en mode démarré, on utilise la méthode void start().

On peut ensuite passer à deux états différents :

  • L'état en pause, en utilisant la méthode void pause(). On peut revenir à tout moment à l'état démarré avec la méthode void resume().
  • L'état arrêté, qui est enclenché en utilisant la méthode void stop(). À partir de cet état, on ne peut pas revenir directement à démarré. En effet, il faudra repasser à l'état préparé, puis indiquer qu'on veut retourner au début du média (avec la méthode void seekTo(int msec) qui permet de se balader dans le média).
1
2
3
4
player.stop();
player.prepare();
// On retourne au début du média, 0 est la première milliseconde
player.seekTo(0);

Enfin, une fois la lecture terminée, on passe à l'état terminé. À partir de là, on peut recommencer la lecture depuis le début avec void start().

Enfin, n'oubliez pas de libérer la mémoire de votre lecteur multimédia avec la méthode void release(), on pourrait ainsi voir dans l'activité qui contient votre lecteur :

1
2
3
4
5
6
7
@Override
protected void onDestroy() {
  if(player != null) {
    player.release();
    player = null;
  }
}

Le volume et l'avancement

Pour changer le volume du lecteur, il suffit d'utiliser la méthode void setVolume(float leftVolume, float rightVolume) avec leftVolume un entier entre 0.0f (pour silencieux) et 1.0f (pour le volume maximum) du côté gauche, et rightVolume idem pour le côté droit. De base, si vous appuyez sur les boutons pour changer le volume, seul le volume de la sonnerie sera modifié. Si vous voulez que ce soit le volume du lecteur qui change et non celui de la sonnerie, indiquez-le avec void setVolumeControlStream(AudioManager.STREAM_MUSIC).

Si vous voulez que l'écran ne s'éteigne pas quand vous lisez un média, utilisez void setScreenOnWhilePlaying(boolean screenOn).

Enfin, si vous voulez que la lecture se fasse en boucle, c'est-à-dire qu'une fois arrivé à terminé on passe à démarré, utilisez void setLooping(boolean looping).

La lecture de vidéos

Maintenant qu'on sait lire des fichiers audio, on va faire en sorte de pouvoir regarder des vidéos. Eh oui, parce qu'en plus du son, on aura besoin de la vidéo. Pour cela, on aura besoin d'une vue qui s'appelle VideoView. Elle ne prend pas d'attributs particuliers en XML :

1
2
3
4
5
6
7
8
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent" >
  <VideoView android:id="@+id/videoView"  
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />
</LinearLayout>

Puis on va attribuer à ce VideoView un MediaController. Mais qu'est-ce qu'un MediaController? Nous n'en avons pas encore parlé ! Il s'agit en fait d'un layout qui permet de contrôler un média, aussi bien un son qu'une vidéo. Contrairement aux vues standards, on n'implémente pas un MediaController en XML mais dans le code. Tout d'abord, on va le construire avec public MediaController(Context context), puis on l'attribue au VideoView avec void setMediaController(MediaController controller) :

1
2
3
4
VideoView video = (VideoView) findViewById(R.id.videoView);
video.setMediaController(new MediaController(getContext()));
video.setVideoURI(Uri.parse("file://sdcard/video/example.avi"));
video.start();

Enregistrement

On aura besoin d'une permission pour enregistrer :

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

Il existe deux manières d'enregistrer.

Enregistrement sonore standard

Vous aurez besoin d'utiliser un MediaRecorder pour tous les enregistrements, dont les vidéos — mais nous le verrons plus tard. Ensuite c'est très simple, il suffit d'utiliser les méthodes suivantes :

  • On indique quel est le matériel qui va enregistrer le son avec void setAudioSource(int audio_source). Pour le micro, on lui donnera comme valeur MediaRecorder.AudioSource.MIC.
  • Ensuite, vous pouvez choisir le format de sortie avec void setOutputFormat(int output_format). De manière générale, on va mettre la valeur MediaRecorder.OutputFormat.DEFAULT, mais la valeur MediaRecorder.OutputFormat.THREE_GPP est aussi acceptable.
  • Nous allons ensuite déclarer quelle méthode d'encodage audio nous voulons grâce à void setAudioEncoder(int audio_encoder), qui prendra la plupart du temps MediaRecorder.AudioEncoder.DEFAULT.
  • La prochaine chose à faire est de définir où sera enregistré le fichier avec void setOutputFile(String path).
  • Puis, comme pour le lecteur multimédia, on passe l'enregistreur en état préparé avec void prepare().
  • Enfin, on commence l'enregistrement avec void start().

Pas facile à retenir, tout ça ! L'avantage ici, c'est que tout est automatique, alors vous n'avez « que » ces étapes à respecter.

1
2
3
4
5
6
7
MediaRecorder recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
recorder.setOutputFile(PATH_NAME);
recorder.prepare();
recorder.start();

Une fois que vous avez décidé de finir l’enregistrement, il vous suffit d'appeler la méthode void stop(), puis de libérer la mémoire :

1
2
3
recorder.stop();
recorder.release();
recorder = null;

Enregistrer du son au format brut

L'avantage du son au format brut, c'est qu'il n'est pas traité et permet par conséquent certains traitements que la méthode précédente ne permettait pas. De cette manière, le son est de bien meilleure qualité. On va ici gérer un flux sonore, et non des fichiers. C'est très pratique dès qu'il faut effectuer des analyses du signal en temps réel.

Nous allons utiliser ici un buffer, c'est-à-dire un emplacement mémoire temporaire qui fait l'intermédiaire entre deux matériels ou processus différents. Ici, le buffer récupérera les données du flux sonore pour que nous puissions les utiliser dans notre code.

La classe à utiliser cette fois est AudioRecord, et on peut en construire une instance avec public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes) où :

  • audioSource est la source d'enregistrement ; souvent on utilisera le micro MediaRecorder.AudioSource.MIC.
  • Le taux d'échantillonnage est à indiquer dans sampleRateInHz, même si dans la pratique on ne met que 44100.
  • Il faut mettre dans channelConfig la configuration des canaux audio ; s'il s'agit de mono, on utilise AudioFormat.CHANNEL_IN_MONO ; s'il s'agit de stéréo, on utilise AudioFormat.CHANNEL_IN_STEREO.
  • On peut préciser le format avec audioFormat, mais en pratique on mettra toujours AudioFormat.ENCODING_PCM_16BIT.
  • Enfin,on va mettre la taille totale du buffer dans bufferSizeInBytes. Si vous n'y comprenez rien, ce n'est pas grave, la méthode static int AudioRecord.getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) vous fournira une bonne valeur à utiliser.

Une utilisation typique pourrait être :

1
2
3
4
5
int sampleRateInHz = 44100;
int channelconfig = AudioFormat.CHANNEL_IN_STEREO;
int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
int bufferSize = AudioRecord.getMinBufferSize(sampleRateInHz, channelconfig, audioFormat)
AudioRecord recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelconfig, audioFormat, bufferSize);

Chaque lecture que nous ferons dans AudioRecord prendra la taille du buffer, il nous faudra donc avoir un tableau qui fait la taille de ce buffer pour récupérer les données :

1
short[] buffer = new short[bufferSize];

Puis vous pouvez lire le flux en temps réel avec int read(short[] audioData, int offsetInShorts, int sizeInShorts) :

1
2
3
4
while(recorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
  // Retourne le nombre de « shorts » lus, parce qu'il peut y en avoir moins que la taille du tableau
  int nombreDeShorts = audioRecord.read(buffer, 0, bufferSize);
}

Enfin, il ne faut pas oublier de fermer le flux et de libérer la mémoire :

1
2
3
recorder.stop();
recorder.release();
recorder = null;

Prendre des photos

Demander à une autre application de le faire

La première chose que nous allons voir, c'est la solution de facilité : comment demander à une autre application de prendre des photos pour nous, puis ensuite les récupérer. On va bien entendu utiliser un intent, et son action sera MediaStore.ACTION_IMAGE_CAPTURE. Vous vous rappelez comment on lance une activité en lui demandant un résultat, j'espère ! Avec void startActivityForResult(Intent intent, int requestCode)requestCode est un code qui permet d'identifier le retour. Le résultat sera ensuite disponible dans void onActivityResult(int requestCode, int resultCode, Intent data) avec requestCode qui vaut comme le requestCode que vous avez passé précédemment. On va ensuite préciser qu'on veut que l'image soit en extra dans le retour :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// L'endroit où sera enregistrée la photo
// Remarquez que mFichier est un attribut de ma classe
mFichier = new File(Environment.getExternalStorageDirectory(), "photo.jpg");
// On récupère ensuite l'URI associée au fichier
Uri fileUri = Uri.fromFile(mFichier);

// Maintenant, on crée l'intent
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Et on déclare qu'on veut que l'image soit enregistrée là où pointe l'URI
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

// Enfin, on lance l'intent pour que l'application de photo se lance
startActivityForResult(intent, PHOTO_RESULT);

Il faut ensuite récupérer la photo dès que l'utilisateur revient dans l’application. On a ici un problème, parce que toutes les applications ne renverront pas le même résultat. Certaines renverront une image comme nous le voulons ; d'autres, juste une miniature… Nous allons donc voir ici comment gérer ces deux cas :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  // Si on revient de l'activité qu'on avait lancée avec le code PHOTO_RESULT  
  if (requestCode == PHOTO_RESULT && resultCode == RESULT_OK) {
    // Si l'image est une miniature
    if (data != null) {
      if (data.hasExtra("data"))
        Bitmap thumbnail = data.getParcelableExtra("data");
    } else {
      // On sait ici que le fichier pointé par mFichier est accessible, on peut donc faire ce qu'on veut avec, par exemple en faire un Bitmap
      Bitmap image = BitmapFactory.decodeFile(mFichier);
    }
  }
}

Tout gérer nous-mêmes

La technique précédente peut dépanner par moments, mais ce n'est pas non plus la solution à tout. Il se peut qu'on veuille avoir le contrôle total sur notre caméra ! Pour cela, on aura besoin de la permission de l'utilisateur d'utiliser sa caméra :

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

Vous pouvez ensuite manipuler très simplement la caméra avec la classe Camera. Pour récupérer une instance de cette classe, on utilise la méthode static Camera Camera.open().

Il est ensuite possible de modifier les paramètres de l'appareil avec void setParameters(Camera.Parameters params). Cependant, avant toute chose, il faut s'assurer que l'appareil peut supporter les paramètres qu'on va lui donner. En effet, chaque appareil aura un objectif photographique différent et par conséquent des caractéristiques différentes, alors il faudra faire en sorte de gérer le plus de cas possible. On va donc récupérer les paramètres avec Camera.Parameters getParameters(), puis on pourra vérifier les modes supportés par l'appareil avec différentes méthodes, par exemple :

1
2
3
4
5
6
7
8
Camera camera = Camera.open();
Camera.Parameters params = camera.getParameters();

// Pour connaître les modes de flash supportés
List<String> flashs = params.getSupportedFlashModes();

// Pour connaître les tailles d'image supportées
List<Camera.Size> tailles = getSupportedPictureSizes();

Vous trouverez plus d'informations sur les modes supportés sur la page de Camera.Parameters. Une fois que vous connaissez les modes compatibles, vous pouvez manipuler la caméra à volonté :

1
2
camera.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
camera.setPictureSize(1028, 768);

Ensuite, il existe deux méthodes pour prendre une photo :

1
2
3
void takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg);

void takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback postview, Camera.PictureCallback jpeg);

À noter que la seconde méthode, celle avec postview, ne sera accessible que si vous avez activé la prévisualisation.

On rencontre ici deux types de classes appelées en callback :

  • Camera.ShutterCallback est utilisée pour indiquer le moment exact où la photo est prise. Elle ne contient qu'une méthode, void onShutter().
  • Camera.PictureCallback est utilisée une fois que l'image est prête. Elle contient la méthode void onPictureTaken(byte[] data, Camera camera) avec l'image contenue dans data et la camera avec laquelle la photo a été prise.

Ainsi, shutter est lancé dès que l'image est prise, mais avant qu'elle soit prête. raw correspond à l'instant où l'image est prête mais pas encore traitée pour correspondre aux paramètres que vous avez entrés. Encore après sera appelé postview, quand l'image sera redimensionnée comme vous l'avez demandé (ce n'est pas supporté par tous les appareils). Enfin, jpeg sera appelé dès que l'image finale sera prête. Vous pouvez passer null à tous les callbacks si vous n'en avez rien à faire :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private void takePicture(Camera camera) {
  // Jouera un son au moment où on prend une photo
  Camera.ShutterCallback shutterCallback = new Camera.ShutterCallback() {
    public void onShutter() {
      MediaPlayer media = MediaPlayer.create(getBaseContext(), R.raw.sonnerie);
      media.start();
      // Une fois la lecture terminée
      media.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
        public  void onCompletion(MediaPlayer mp) {
          // On libère le lecteur multimédia
          mp.release();
        }
      });
    }
  };

  // Sera lancée une fois l'image traitée, on enregistre l'image sur le support externe
  Camera.PictureCallback jpegCallback = new Camera.PictureCallback() {
    public void onPictureTaken(byte[] data, Camera camera) {
      FileOutputStream stream = null;
      try {
        String path = Environment.getExternalStorageDirectory() + "\\photo.jpg";
        stream = new FileOutputStream(path);
        stream.write(data);
      } catch (Exception e) {

      } finally {
        try { stream.close();} catch (Exception e) {}
      }
    }
  };

  camera.takePicture(shutterCallback, null, jpegCallback);
}

Enfin, on va voir comment permettre à l'utilisateur de prévisualiser ce qu'il va prendre en photo. Pour cela, on a besoin d'une vue particulière : SurfaceView. Il n'y a pas d'attributs particuliers à connaître pour la déclaration XML :

1
2
3
4
<SurfaceView
  android:id="@+id/surface_view"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent" />

On aura ensuite besoin de récupérer le SurfaceHolder associé à notre SurfaceView, et ce avec la méthode SurfaceHolder getHolder(). On a ensuite besoin de lui attribuer un type, ce qui donne :

1
2
3
SurfaceView surface = (SurfaceView)findViewById(R.id.surfaceView);
SurfaceHolder holder = surface.getHolder();
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

Ne vous inquiétez pas, c'est bientôt fini ! On n'a plus qu'à implémenter des méthodes de callback de manière à pouvoir gérer correctement le cycle de vie de la caméra et de la surface de prévisualisation. Pour cela, on utilise l'interface SurfaceHolder.Callback qui contient trois méthodes de callback qu'il est possible d'implémenter :

  • void surfaceChanged(SurfaceHolder holder, int format, int width, int height) est lancée quand le SurfaceView change de dimensions.
  • void surfaceCreated(SurfaceHolder holder) est appelée dès que la surface est créée. C'est dedans qu'on va associer la caméra au SurfaceView.
  • À l'opposé, au moment de la destruction de la surface, la méthode void surfaceDestroyed(SurfaceHolder holder) sera exécutée. Elle permettra de dissocier la caméra et la surface.

Voici maintenant un exemple d'implémentation de cette synergie :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Notre classe implémente SurfaceHolder.Callback
public class CameraActivity extends Activity implements SurfaceHolder.Callback {
  private Camera mCamera = null;

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

    SurfaceView surface = (SurfaceView)findViewById(R.id.menu_settings);

    SurfaceHolder holder = surface.getHolder();
    holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

    // On déclare que la classe actuelle gérera les callbacks
    holder.addCallback(this);
  }

  // Se déclenche quand la surface est créée
  public void surfaceCreated(SurfaceHolder holder) {
    try {
      mCamera.setPreviewDisplay(holder);
      mCamera.startPreview();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  // Se déclenche quand la surface est détruite
  public void surfaceDestroyed(SurfaceHolder holder) {
    mCamera.stopPreview();
  }

  // Se déclenche quand la surface change de dimensions ou de format
  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
  }

  @Override
  protected void onResume() {
    super.onResume();
    mCamera = Camera.open();
  }

  @Override
  protected void onPause() {
    super.onPause();
    mCamera.release();
  }
}

Enfin, pour libérer la caméra, on utilise la méthode void release().

Enregistrer des vidéos

Demander à une autre application de le faire à notre place

Encore une fois, il est tout à fait possible de demander à une autre application de prendre une vidéo pour nous, puis de la récupérer afin de la traiter. Cette fois, l'action à spécifier est MediaStore.ACTION_VIDEO_CAPTURE. Pour préciser dans quel emplacement stocker la vidéo, il faut utiliser l'extra MediaStore.EXTRA_OUTPUT :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private static final int VIDEO = 0;

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

  Uri emplacement = Uri.parse(new File(Environment.getExternalStorageDirectory() + "\\video\\nouvelle.3gp"));

  Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
  intent.putExtra(MediaStore.EXTRA_OUTPUT, emplacement);

  startActivityForResult(intent, VIDEO);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (requestCode == VIDEO) {
    if(resultCode == RESULT_OK) {
      Uri emplacement = data.getData();
    }
  }
}

Tout faire nous-mêmes

Tout d'abord, on a besoin de trois autorisations : une pour utiliser la caméra, une pour enregistrer le son et une pour enregistrer la vidéo :

1
2
3
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.RECORD_VIDEO" />
<uses-permission android:name="android.permission.CAMERA" />

Au final, maintenant qu'on sait enregistrer du son, enregistrer de la vidéo n'est pas beaucoup plus complexe. En effet, on va encore utiliser MediaRecorder. Cependant, avant cela, il faut débloquer la caméra pour qu'elle puisse être utilisée avec le MediaRecorder. Il suffit pour cela d'appeler sur votre caméra la méthode void unlock(). Vous pouvez maintenant associer votre MediaRecorder et votre Camera avec la méthode void setCamera(Camera camera). Puis, comme pour l'enregistrement audio, il faut définir les sources :

1
2
3
4
5
camera.unlock();
mediaRecorder.setCamera(camera);
// Cette fois, on choisit un micro qui se trouve le plus proche possible de l'axe de la caméra
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

Cependant, quand on enregistre une vidéo, il est préférable de montrer à l'utilisateur ce qu'il est en train de filmer de manière à ce qu'il ne filme pas à l'aveugle. Comme nous l'avons déjà fait pour la prise de photographies, il est possible de donner un SurfaceView au MediaRecorder. La méthode à utiliser pour cela est void setPreviewDisplay(SurfaceView surface). Encore une fois, vous pouvez implémenter les méthodes de callback contenues dans SurfaceHolder.Callback.

Enfin, comme pour l'enregistrement audio, on doit définir l'emplacement où enregistrer le fichier, préparer le lecteur, puis lancer l'enregistrement.

1
2
3
mediaRecorder.setOutputFile(PATH_NAME);
mediaRecorder.prepare();
mediaRecorder.start();

Toujours appeler setPreviewDisplay avant prepare, sinon vous aurez une erreur.

Enfin, il faut libérer la mémoire une fois la lecture terminée :

1
2
3
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;

  • Android est capable de lire nativement beaucoup de formats de fichier différents, ce qui en fait un lecteur multimédia mobile idéal.
  • Pour lire des fichiers multimédia, on peut utiliser un objet MediaPlayer. Il s'agit d'un objet qui se comporte comme une machine à états, il est donc assez délicat et lourd à manipuler ; cependant, il permet de lire des fichiers efficacement dès qu'on a appris à le maîtriser.
  • Pour afficher des vidéos, on devra passer par une VideoView, qu'il est possible de lier à un MediaPlayer auquel on donnera des fichiers vidéo qu'il pourra lire nativement.
  • L'enregistrement sonore est plus délicat, il faut réfléchir à l'avance à ce qu'on va faire en fonction de ce qu'on désire faire. Par exemple, MediaRecorder est en général utilisé, mais si on veut quelque chose de moins lourd, sur lequel on peut effectuer des traitements en temps réel, on utilisera plutôt AudioRecord.
  • Il est possible de prendre une photo avec Camera. Il est possible de personnaliser à l'extrême son utilisation pour celui qui désire contrôler tous les aspects de la prise d'images.
  • Pour prendre des vidéos, on utilisera aussi un MediaRecorder, mais on fera en sorte d'afficher une prévisualisation du résultat grâce à un SurfaceView.