Omówienie MediaRecorder

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 z MIC.

    Uwaga: większość źródeł dźwięku (w tym DEFAULT) przetwarza dane sygnału dźwiękowego. Aby nagrywać nieprzetworzony dźwięk, wybierz UNPROCESSED Niektóre urządzenia nie obsługują nieprzetworzonych danych dane wejściowe. Zadzwoń pod numer AudioManager.getProperty(AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED), aby sprawdzić, czy usługa jest dostępna. Jeśli nie, spróbuj użyć zamiast niego VOICE_RECOGNITION, który nie stosuje treści generowanych przez użytkowników ani eliminowania szumu. Możesz użyć usługi UNPROCESSED 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.