Platforma multimedialna Androida obsługuje przechwytywanie i kodowanie
formatów audio i wideo. Możesz używać interfejsów API MediaRecorder
, jeśli są one obsługiwane
przez sprzęt.
Z tego dokumentu dowiesz się, jak przy użyciu narzędzia MediaRecorder
napisać aplikację, która rejestruje dźwięk z urządzenia
mikrofon, zapisać dźwięk i odtworzyć go (przy użyciu funkcji MediaPlayer
). Aby nagrać film:
używać aparatu urządzenia i usługi MediaRecorder
. To opisano w przewodniku po aparacie.
Uwaga: emulator Androida nie może rejestrować audio. Pamiętaj, aby przetestować kod na prawdziwym urządzeniu, które potrafi nagrywać.
Prośba o zgodę na nagrywanie dźwięku
Aby móc nagrywać, aplikacja musi poinformować użytkownika, że będzie mieć dostęp do wejścia audio urządzenia. W pliku manifestu aplikacji musisz umieścić ten tag uprawnień:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
RECORD_AUDIO
jest uważany za
„niebezpieczny” uprawnienia
, ponieważ może to stanowić zagrożenie dla prywatności użytkownika. Aplikacje na Androida w wersji 6.0 (poziom interfejsu API 23)
który korzysta z niebezpiecznych uprawnień, musi w czasie działania aplikacji prosić użytkownika o zatwierdzenie. Gdy użytkownik
aplikacji z przyznanym uprawnieniem, powinna ją zapamiętać i nie pytać ponownie. Poniższy przykładowy kod pokazuje, jak
zaimplementuj to zachowanie za pomocą:
ActivityCompat.requestPermissions()
Tworzenie i uruchamianie funkcji MediaRecorder
Zainicjuj nową instancję instancji MediaRecorder
z tymi wywołaniami:
- Ustaw źródło dźwięku za pomocą
setAudioSource()
Za prawdopodobnie korzystasz zMIC
.Uwaga: większość źródeł dźwięku (w tym
DEFAULT
) przetwarza dane sygnału dźwiękowego. Aby nagrywać nieprzetworzony dźwięk, wybierzUNPROCESSED
Niektóre urządzenia nie obsługują nieprzetworzonych danych dane wejściowe. Zadzwoń pod numerAudioManager.getProperty(AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED)
, aby sprawdzić, czy usługa jest dostępna. Jeśli nie, spróbuj użyć zamiast niegoVOICE_RECOGNITION
, który nie stosuje treści generowanych przez użytkowników ani eliminowania szumu. Możesz użyć usługiUNPROCESSED
jako źródła dźwięku, nawet jeśli dana usługa nie jest obsługiwana, w takim przypadku sygnał nie zostanie przetworzony. - Ustaw format pliku wyjściowego za pomocą
setOutputFormat()
Pamiętaj, że od Androida 8.0 (poziom interfejsu API 26)MediaRecorder
obsługuje MPEG2_TS który sprawdza się przy strumieniowaniu:Kotlin
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS)
Java
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
- Ustaw nazwę pliku wyjściowego za pomocą funkcji
setOutputFile()
Musisz określić deskryptor pliku, który reprezentuje rzeczywisty plik. - Skonfiguruj koder dźwięku za pomocą
setAudioEncoder()
- Dokończ inicjalizację, wywołując
prepare()
Uruchom i zatrzymaj dyktafon, dzwoniąc
start()
i
stop()
.
Gdy skończysz korzystać z instancji MediaRecorder
, zwolnisz jej zasoby
jak najszybciej, dzwoniąc do
release()
Uwaga: na urządzeniach z Androidem 9 (poziom interfejsu API 28) lub
aplikacje działające w tle nie mają dostępu do mikrofonu. Dlatego
aplikacja powinna nagrywać dźwięk tylko wtedy, gdy działa na pierwszym planie lub gdy
uwzględnij wystąpienie MediaRecorder
w
usługę działającą na pierwszym planie.
Korzystanie z MediaMuxer do nagrywania wielu kanałów
Począwszy od Androida 8.0 (poziom interfejsu API 26) możesz używać MediaMuxer
umożliwia nagrywanie kilku jednoczesnych strumieni audio i wideo. We wcześniejszych wersjach Androida można tylko
nagrywać tylko jednej ścieżki audio i/lub jednej ścieżki wideo.
Użyj funkcji addTrack()
aby mieszać kilka utworów.
Możesz też dodać jedną lub więcej ścieżek metadanych z niestandardowymi informacjami o każdej ramce. tylko do kontenerów MP4. Aplikacja określa format i zawartość metadanych.
Dodawanie metadanych
Metadane przydają się podczas przetwarzania w trybie offline. Na przykład dane zebrane z okresu można wykorzystać żyroskop do stabilizacji wideo.
Gdy dodajesz ścieżkę metadanych, format MIME ścieżki musi zaczynać się od prefiksu
application/
Zapisywanie metadanych przebiega tak samo, jak zapisywanie danych wideo i audio, z tą różnicą
że dane nie pochodzą z MediaCodec
. Zamiast tego aplikacja przekazuje
ByteBuffer
z sygnaturą czasową powiązaną z
Metoda writeSampleData()
.
Sygnatura czasowa musi obejmować tę samą podstawę czasową co ścieżki wideo i audio.
Wygenerowany plik MP4 korzysta z dokumentu TextMetaDataSampleEntry
zdefiniowanego w sekcji 12.3.3.2
specyfikacji ISO BMFF
sygnalizując format MIME metadanych. Jeśli używasz MediaExtractor
do wyodrębnienia pliku zawierającego ścieżki metadanych, MIME metadanych
jest wyświetlane jako wystąpienie ciągu MediaFormat
.
Kod demonstracyjny
MediaRecorder przykład pokazuje, jak nagrać film za pomocą MediaRecorder i interfejsu Camera API.
Przykładowa aktywność poniżej pokazuje, jak za pomocą aplikacji MediaRecorder
nagrać plik audio. it
Również korzysta z usługi MediaPlayer
do odtwarzania dźwięku.
Kotlin
package com.android.audiorecordtest import android.Manifest import android.content.Context import android.content.pm.PackageManager import android.media.MediaPlayer import android.media.MediaRecorder import android.os.Bundle import android.support.v4.app.ActivityCompat import android.support.v7.app.AppCompatActivity import android.util.Log import android.view.View.OnClickListener import android.view.ViewGroup import android.widget.Button import android.widget.LinearLayout import java.io.IOException private const val LOG_TAG = "AudioRecordTest" private const val REQUEST_RECORD_AUDIO_PERMISSION = 200 class AudioRecordTest : AppCompatActivity() { private var fileName: String = "" private var recordButton: RecordButton? = null private var recorder: MediaRecorder? = null private var playButton: PlayButton? = null private var player: MediaPlayer? = null // Requesting permission to RECORD_AUDIO private var permissionToRecordAccepted = false private var permissions: Array<String> = arrayOf(Manifest.permission.RECORD_AUDIO) override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<String>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) permissionToRecordAccepted = if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) { grantResults[0] == PackageManager.PERMISSION_GRANTED } else { false } if (!permissionToRecordAccepted) finish() } private fun onRecord(start: Boolean) = if (start) { startRecording() } else { stopRecording() } private fun onPlay(start: Boolean) = if (start) { startPlaying() } else { stopPlaying() } private fun startPlaying() { player = MediaPlayer().apply { try { setDataSource(fileName) prepare() start() } catch (e: IOException) { Log.e(LOG_TAG, "prepare() failed") } } } private fun stopPlaying() { player?.release() player = null } private fun startRecording() { recorder = MediaRecorder().apply { setAudioSource(MediaRecorder.AudioSource.MIC) setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP) setOutputFile(fileName) setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) try { prepare() } catch (e: IOException) { Log.e(LOG_TAG, "prepare() failed") } start() } } private fun stopRecording() { recorder?.apply { stop() release() } recorder = null } internal inner class RecordButton(ctx: Context) : Button(ctx) { var mStartRecording = true var clicker: OnClickListener = OnClickListener { onRecord(mStartRecording) text = when (mStartRecording) { true -> "Stop recording" false -> "Start recording" } mStartRecording = !mStartRecording } init { text = "Start recording" setOnClickListener(clicker) } } internal inner class PlayButton(ctx: Context) : Button(ctx) { var mStartPlaying = true var clicker: OnClickListener = OnClickListener { onPlay(mStartPlaying) text = when (mStartPlaying) { true -> "Stop playing" false -> "Start playing" } mStartPlaying = !mStartPlaying } init { text = "Start playing" setOnClickListener(clicker) } } override fun onCreate(icicle: Bundle?) { super.onCreate(icicle) // Record to the external cache directory for visibility fileName = "${externalCacheDir.absolutePath}/audiorecordtest.3gp" ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION) recordButton = RecordButton(this) playButton = PlayButton(this) val ll = LinearLayout(this).apply { addView(recordButton, LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0f)) addView(playButton, LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0f)) } setContentView(ll) } override fun onStop() { super.onStop() recorder?.release() recorder = null player?.release() player = null } }
Java
package com.android.audiorecordtest; import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; import android.media.MediaPlayer; import android.media.MediaRecorder; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.LinearLayout; import java.io.IOException; public class AudioRecordTest extends AppCompatActivity { private static final String LOG_TAG = "AudioRecordTest"; private static final int REQUEST_RECORD_AUDIO_PERMISSION = 200; private static String fileName = null; private RecordButton recordButton = null; private MediaRecorder recorder = null; private PlayButton playButton = null; private MediaPlayer player = null; // Requesting permission to RECORD_AUDIO private boolean permissionToRecordAccepted = false; private String [] permissions = {Manifest.permission.RECORD_AUDIO}; @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode){ case REQUEST_RECORD_AUDIO_PERMISSION: permissionToRecordAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED; break; } if (!permissionToRecordAccepted ) finish(); } private void onRecord(boolean start) { if (start) { startRecording(); } else { stopRecording(); } } private void onPlay(boolean start) { if (start) { startPlaying(); } else { stopPlaying(); } } private void startPlaying() { player = new MediaPlayer(); try { player.setDataSource(fileName); player.prepare(); player.start(); } catch (IOException e) { Log.e(LOG_TAG, "prepare() failed"); } } private void stopPlaying() { player.release(); player = null; } private void startRecording() { recorder = new MediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC); recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); recorder.setOutputFile(fileName); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); try { recorder.prepare(); } catch (IOException e) { Log.e(LOG_TAG, "prepare() failed"); } recorder.start(); } private void stopRecording() { recorder.stop(); recorder.release(); recorder = null; } class RecordButton extends Button { boolean mStartRecording = true; OnClickListener clicker = new OnClickListener() { public void onClick(View v) { onRecord(mStartRecording); if (mStartRecording) { setText("Stop recording"); } else { setText("Start recording"); } mStartRecording = !mStartRecording; } }; public RecordButton(Context ctx) { super(ctx); setText("Start recording"); setOnClickListener(clicker); } } class PlayButton extends Button { boolean mStartPlaying = true; OnClickListener clicker = new OnClickListener() { public void onClick(View v) { onPlay(mStartPlaying); if (mStartPlaying) { setText("Stop playing"); } else { setText("Start playing"); } mStartPlaying = !mStartPlaying; } }; public PlayButton(Context ctx) { super(ctx); setText("Start playing"); setOnClickListener(clicker); } } @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); // Record to the external cache directory for visibility fileName = getExternalCacheDir().getAbsolutePath(); fileName += "/audiorecordtest.3gp"; ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION); LinearLayout ll = new LinearLayout(this); recordButton = new RecordButton(this); ll.addView(recordButton, new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0)); playButton = new PlayButton(this); ll.addView(playButton, new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0)); setContentView(ll); } @Override public void onStop() { super.onStop(); if (recorder != null) { recorder.release(); recorder = null; } if (player != null) { player.release(); player = null; } } }
Więcej informacji
Te strony zawierają omówienie tematów związanych z nagrywaniem, przechowywaniem i odtwarzaniem dźwięku i wideo.