Permissions d'acquisition audio et vidéo
-
Utiliser le micro ou la caméra requiert des permissions sensibles :
- android.permission.RECORD_AUDIO
- android.permission.RECORD_VIDEO
- Nécessité de déclarer ces permissions dans le manifeste ET de demander explicitement l'accord par un dialogue lors de l'exécution
- Depuis Android 12, l'utilisateur peut désactiver son micro et/ou sa caméra globalement
MediaRecorder
API
- Permet d'enregistrer du son et de la vidéo
-
Pour enregistrer du son (et facultativement de la vidéo) :
- MediaRecorder mr = new MediaRecorder() ;
- mr.setAudioSource(MediaRecorder.AudioSource.{DEFAULT, MIC, VOICE_CALL, VOICE_COMMUNICATION, VOICE_RECOGNITION, VOICE_DOWNLINK, VOICE_UPLINK})
- mr.setVideoSource(MediaRecorder.VideoSource.{DEFAULT, CAMERA})
- mr.setOutputFormat(MediaRecorder.OutputFormat.{DEFAULT, THREE_GP, MPEG_4, AMR_NB, AMR_WB, AAC_ADTS, ...})
- mr.setAudioEncoder(MediaRecoarder.AudioEncoder.{DEFAULT, AAC, AMR_NB, AMR_WB, ...})
- mr.setVideoEncoder(MediaRecorder.VideoEncoder.{DEFAULT, H263, H264})
- mr.setOutputFile(path)
- mr.prepare()
- mr.start() ;
- ...
- mr.stop() :
- mr.release() ;

Exemple
Exemple de l'utilisation de MediaRecorder pour enregistrer une vidéo de l'écran de l'appareil en utilisant MediaProjector :
// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License package fr.upem.coursand.screenrecorder import android.Manifest import android.app.Activity import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.hardware.display.DisplayManager import android.media.MediaRecorder import android.media.projection.MediaProjection import android.media.projection.MediaProjectionManager import android.os.Build import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.annotation.RequiresApi import android.util.DisplayMetrics import android.hardware.display.VirtualDisplay import android.os.Environment import androidx.core.content.ContextCompat import android.widget.Toast import fr.upem.coursand.R import fr.upem.coursand.launcher.ActivityMetadata import kotlinx.android.synthetic.main.activity_screen_recorder.* import java.io.File @RequiresApi(Build.VERSION_CODES.LOLLIPOP) @ActivityMetadata(permissions= [Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO], minApi=Build.VERSION_CODES.LOLLIPOP) class ScreenRecorderActivity : AppCompatActivity() { val CAPTURE_REQUEST_ID = 1 private val mediaProjectionManager by lazy { getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager } private var mediaProjection: MediaProjection? = null private val displayMetrics by lazy { val metrics = DisplayMetrics() windowManager.defaultDisplay.getMetrics(metrics) metrics } private val mediaRecorder by lazy { MediaRecorder() } private var virtualDisplay: VirtualDisplay? = null private var mediaProjectionCallback = object: MediaProjection.Callback() { override fun onStop() { super.onStop() stopMediaRecorder() } } var name: String? = null private var temporaryFile: File? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_screen_recorder) startButton.setOnClickListener { createMediaProjector() } stopButton.setOnClickListener { stopMediaRecorder() } recordAudioView.isEnabled = ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED val recordable = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED startButton.isEnabled = recordable if (! recordable) Toast.makeText(this, "The permission WRITE_EXTERNAL_STORAGE is not granted", Toast.LENGTH_SHORT).show() } private fun createMediaProjector() { if (mediaProjection != null) return // nothing to do it is already created startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), CAPTURE_REQUEST_ID) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == CAPTURE_REQUEST_ID && resultCode == Activity.RESULT_OK && data != null) { val mp = mediaProjectionManager.getMediaProjection(resultCode, data) mediaProjection = mp mp.registerCallback(mediaProjectionCallback, null) startMediaRecorder(nameView.text.toString()) } } private fun startMediaRecorder(givenName: String) { startButton.isEnabled = false stopButton.isEnabled = true mediaRecorder.apply { val audio = recordAudioView.isChecked if (audio) setAudioSource(MediaRecorder.AudioSource.MIC) setVideoSource(MediaRecorder.VideoSource.SURFACE) setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP) val tmpFile = File.createTempFile("screenRecord", ".mp4", Environment.getExternalStorageDirectory()) temporaryFile = tmpFile name = givenName setOutputFile(tmpFile.absolutePath) setVideoSize(displayMetrics.widthPixels, displayMetrics.heightPixels) setVideoEncoder(MediaRecorder.VideoEncoder.H264) if (audio) setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) setVideoEncodingBitRate(512 * 1000) setVideoFrameRate(30) prepare() virtualDisplay = mediaProjection?.createVirtualDisplay("screenRecorder", displayMetrics.widthPixels, displayMetrics.heightPixels, displayMetrics.densityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mediaRecorder.surface, null, null) start() } } private fun stopMediaRecorder() { mediaRecorder.stop() mediaRecorder.reset() mediaProjection?.apply { unregisterCallback(mediaProjectionCallback) stop() } virtualDisplay?.release() virtualDisplay = null mediaProjection = null val uri = saveInMediaStore(MediaType.VIDEO, "video/mp4", name!!, temporaryFile!!) Toast.makeText(this, "Saved to $uri", Toast.LENGTH_SHORT).show() startButton.isEnabled = true stopButton.isEnabled = false } }
Camera
Appel de l'application caméra
Exemple d'activité appelant l'application caméra de l'appareil pour enregister une photo ou vidéo.
Ne nécessite pas de permission spécifique
// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License package fr.upem.coursand.camera; import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.util.Log; import android.view.View; import android.widget.Toast; import fr.upem.coursand.R; /** * An example of the use of Intent to launch the camera application to take pictures. * Note that this activity does not require any special permission since it does * not use directly the camera API. */ public class CameraCaller extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_camera_caller); } private final static int IMAGE_CAPTURE_REQUEST_CODE = 1; private final static int VIDEO_CAPTURE_REQUEST_CODE = 2; public void startCameraActivity(boolean video) { Intent intent = new Intent((video)?MediaStore.ACTION_VIDEO_CAPTURE:MediaStore.ACTION_IMAGE_CAPTURE); // We can also use MediaStore.ACTION_IMAGE_CAPTURE_SECURE to capture an image in lock mode // To capture a video, we use MediaStore.ACTION_VIDEO_CAPTURE File pictDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), this.getClass().getSimpleName()); pictDir.mkdir(); String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + (video ? ".mp4":".jpg"); File dest = new File(pictDir, timestamp); // Destination file intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(dest)); // Specify the destination file // Now, we start the picture capture activity try { startActivityForResult(intent, (video) ? VIDEO_CAPTURE_REQUEST_CODE : IMAGE_CAPTURE_REQUEST_CODE); } catch (ActivityNotFoundException e) { Log.e(getClass().getName(), "Cannot start the activity due to an exception", e); Toast.makeText(this, "An exception encountered: " + e, Toast.LENGTH_SHORT).show(); } } public void requestPicture(View v) { startCameraActivity(false); } public void requestVideo(View v) { startCameraActivity(true); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case IMAGE_CAPTURE_REQUEST_CODE: switch (resultCode) { case RESULT_OK: Toast.makeText(this, "Image saved to " + data.getData(), Toast.LENGTH_LONG).show(); // ((ImageView)findViewById(R.id.imageCaptureView)).setImageURI(data.getData()); break; case RESULT_CANCELED: default: Toast.makeText(this, "Capture of image failed", Toast.LENGTH_SHORT).show(); } break; case VIDEO_CAPTURE_REQUEST_CODE: switch (resultCode) { case RESULT_OK: Toast.makeText(this, "Video saved to " + data.getData(), Toast.LENGTH_LONG).show(); break; case RESULT_CANCELED: default: Toast.makeText(this, "Capture of video failed", Toast.LENGTH_SHORT).show(); } } } }
Usage de l'API caméra
-
Combien de caméras ? Informations
- Camera.getNumberOfCameras()
- Camera.getCameraInfo(int cameraid, CameraInfo i)
-
Ouvrir une caméra : Camera.open(int cameraid)
- Appel à Camera.open() synchrone pouvant bloquer quelques secondes
- Préférable d'utiliser l'API Camera dans une thread secondaire
- Récupération et fixation des paramètres : getParameters(), setParameters(Camera.Parameters)
- Ne pas oublier d'appeler release() pour la libérer
<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" />
Prévisualisation de caméra
-
Où envoyer les données de prévisualisation ?
- Sur une texture OpenGL : setPreviewTexture(SurfaceTexture st)
- Sur un SurfaceHolder (typiquement obtenu avec SurfaceView.getHolder()) : setPreviewDisplay(SurfaceHolder h)
-
Sur une méthode callback : setPreviewCallback(Camera.PreviewCallback cb)
- setPreviewFormat(int) définit le format binaire des previews (par défaut NV21)
- PreviewCallback.onPreviewFrame(byte[] data, Camera c) doit être implanté
-
Contrôle de la prévisualisation
- startPreview() pour démarrer
- stopPreview() pour arrêter
Zoom et capture
-
Contrôle du zoom
- getMaxZoom() : zoom maximal (grand angle=0)
- setZoom(int value) : fixation du zoom
- startSmoothZoom(int value), stopSmoothZoom() : zoom progressif (possibilité d'utiliser un listener)
-
Capture
- takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback postview, Camera.PictureCallback jpeg)
- shutter.onShutter() sert à signaler la prise de photo (on peut jouer par exemple un son)
- Chaque PictureCallback est optionnel (peut être null) selon les données souhaitées : brutes, post-traitées ou compressées en JPEG. La méthode onPictureTaken(byte[] data, Camera camera) doit être implantée.