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 :
- 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épertoireassets/
afin d'y accéder avec une URI de typefile://android_asset/nom_du_fichier.format_du_fichier
. Il s'agit de la solution la plus simple, mais aussi de la moins souple. - 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.
- 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éthodevoid 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éthodevoid 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 valeurMediaRecorder.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 valeurMediaRecorder.OutputFormat.DEFAULT
, mais la valeurMediaRecorder.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 tempsMediaRecorder.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 microMediaRecorder.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 utiliseAudioFormat.CHANNEL_IN_MONO
; s'il s'agit de stéréo, on utiliseAudioFormat.CHANNEL_IN_STEREO
. - On peut préciser le format avec
audioFormat
, mais en pratique on mettra toujoursAudioFormat.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éthodestatic 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)
où 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éthodevoid onPictureTaken(byte[] data, Camera camera)
avec l'image contenue dansdata
et lacamera
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 leSurfaceView
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 auSurfaceView
.- À 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 à unMediaPlayer
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ôtAudioRecord
. - 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 à unSurfaceView
.