Benutzerdefinierter Audiotreiber (Android)

Übersicht

Mit dem OpenTok Android SDK können Sie einen benutzerdefinierten Audiotreiber für Publisher und Subscriber einrichten. Mithilfe eines benutzerdefinierten Audiotreibers können Sie die an den Stream eines Publishers gesendeten Audiodaten anpassen. Außerdem können Sie die Wiedergabe der Audiodaten von abonnierten Streams anpassen.

Einrichten Ihres Projekts

Der Code für diesen Abschnitt ist im Projekt „Basic-Audio-Driver-Java“ der opentok-android-sdk-Beispiele Repo. Wenn Sie dies noch nicht getan haben, müssen Sie das Projektarchiv in ein lokales Verzeichnis klonen. Führen Sie in der Befehlszeile Folgendes aus:

git clone git@github.com:Vonage/vonage-video-android-sdk-samples.git

Öffnen Sie die Grundlagen von Audio in Java Projekt in Android Studio, um die Entwicklung zu verfolgen.

Anmerkung: In diesem Tutorial wird die Java-Version der Android-Beispiel-App behandelt. Eine Kotlin-Version mit Dokumentation finden Sie unter Android Beispielanwendung Repo.

Schritt 1: Hinzufügen eines benutzerdefinierten Audiotreibers

Diese Beispielanwendung verwendet den benutzerdefinierten Audiotreiber, um weißes Rauschen (ein zufälliges Audiosignal) im Audiostrom zu veröffentlichen. Außerdem wird der benutzerdefinierte Audiotreiber verwendet, um das Audio von abonnierten Streams zu erfassen und in einer Datei zu speichern.

Einrichten des Audiogeräts und des Audiobusses

Bei der Verwendung eines benutzerdefinierten Audiotreibers definieren Sie einen benutzerdefinierten Audiotreiber und einen Audiobus, der von der Anwendung verwendet werden soll.

Die NoiseAudioDevice Klasse definiert eine grundlegende Schnittstelle für Audiogeräte, die von der Anwendung verwendet werden soll. Sie erweitert die BaseAudioDevice Klasse, die vom OpenTok Android SDK definiert wird. Um einen benutzerdefinierten Audiotreiber zu verwenden, rufen Sie die AudioDeviceManager.setAudioDevice() Methode. Dieses Beispiel setzt das Audiogerät auf eine Instanz der Methode NoiseAudioDevice Klasse:

AudioDeviceManager.setAudioDevice(noiseAudioDevice);

Verwenden Sie die AudioSettings Klasse, die im OpenTok Android SDK definiert ist, um das vom benutzerdefinierten Audiotreiber verwendete Audioformat festzulegen. Die NoiseAudioDevice() Konstruktor instanziiert zwei AudioSettings Instanzen - eine für den benutzerdefinierten Audio-Capturer und eine für den benutzerdefinierten Audio-Renderer. Sie legt die Samplerate und die Anzahl der Kanäle für jede Instanz fest:

public NoiseAudioDevice(Context context) {
    this.context = context;

    captureSettings = new AudioSettings(SAMPLING_RATE, NUM_CHANNELS_CAPTURING);
    rendererSettings = new AudioSettings(SAMPLING_RATE, NUM_CHANNELS_RENDERING);

    capturerStarted = false;
    rendererStarted = false;

    audioDriverPaused = false;

    capturerHandler = new Handler();
    rendererHandler = new Handler();
}

Der Konstruktor richtet auch einige lokale Eigenschaften ein, die angeben, ob das Gerät aufnimmt oder rendert. Er legt auch eine Handler-Instanz fest, um die capturer Lauffähiges Objekt.

Die NoiseAudioDevice.getAudioBus() Methode erhält die AudioBus Instanz, die dieses Audiogerät verwendet, definiert durch den NoiseAudioDevice.AudioBus Klasse. Verwenden Sie die AudioBus Instanz zum Senden und Empfangen von Hörproben zu und von einer Sitzung. Der Herausgeber greift auf die AudioBus Objekt, um die Hörproben zu erhalten. Die Abonnenten senden Hörproben (aus abonnierten Streams) an das AudioBus-Objekt.

Aufzeichnung von Audiodaten zur Verwendung durch einen Verlag

Die BaseAudioDevice.startCapturer() wird aufgerufen, wenn das Audiogerät mit der Aufnahme von Audiodaten beginnen soll, die veröffentlicht werden sollen. Die NoiseAudioDevice Implementierung dieser Methode startet die capturer Thread, der nach 1 Sekunde in der Warteschlange ausgeführt werden soll:

public boolean startCapturer() {
    capturerStarted = true;
    capturerHandler.postDelayed(capturer, capturerIntervalMillis);
    return true;
}

Die capturer Thread erzeugt einen Puffer mit Stichproben von Zufallsdaten (weißes Rauschen). Dann ruft er die writeCaptureData() Methode der AudioBus Objekt, das die Samples an den Audiobus sendet. Der Publisher in der Anwendung verwendet die an den Audiobus gesendeten Samples, um sie als Audio im veröffentlichten Stream zu übertragen. Wenn dann noch eine Aufnahme läuft (wenn die Anwendung veröffentlicht), wird das capturer Thread wird nach einer weiteren Sekunde erneut ausgeführt:

private Runnable capturer = new Runnable() {
    @Override
    public void run() {
        capturerBuffer.rewind();

        Random rand = new Random();
        rand.nextBytes(capturerBuffer.array());

        getAudioBus().writeCaptureData(capturerBuffer, SAMPLING_RATE);

        if(capturerStarted && !audioDriverPaused) {
            capturerHandler.postDelayed(capturer, capturerIntervalMillis);
        }
    }
};

Die Klasse „AudioDevice“ enthält weitere Methoden, die von der Klasse „NoiseAudioDevice“ implementiert werden. Da in diesem Beispiel jedoch keine interessanten Funktionen in diesen Methoden ausgeführt werden, werden sie hier nicht näher behandelt.

Schritt 2: Hinzufügen eines benutzerdefinierten Audio-Renderers

Sie implementieren einen einfachen Audio-Renderer für den Ton der abonnierten Streams.

Die NoiseAudioDevice() Die Konstruktormethode richtet eine Datei ein, um das eingehende Audiosignal in einer Datei zu speichern. Dies dient lediglich dazu, eine Anwendungsmöglichkeit des Audio-Renderers des benutzerdefinierten Audiotreibers zu veranschaulichen. Die App benötigt die folgenden Berechtigungen, die in der AndroidManifest.xml Datei:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Die BaseAudioDevice.initRenderer() Methode wird aufgerufen, wenn die Anwendung den Audio-Renderer initialisiert. Die NoiseAudioDevice Implementierung dieser Methode instanziiert ein neues File-Objekt, in das die Anwendung Audiodaten schreiben wird:

@Override
public boolean initRenderer() {
    rendererBuffer = ByteBuffer.allocateDirect(SAMPLING_RATE * 2); // Each sample has 2 bytes
    File documentsDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
    rendererFile = new File(documentsDirectory, "output.raw");

    if (!rendererFile.exists()) {
        try {
            rendererFile.getParentFile().mkdirs();
            rendererFile.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    return true;
}

Die BaseAudioDevice.startRendering() Methode wird aufgerufen, wenn das Audiogerät mit dem Rendern (Abspielen) von Audio aus abonnierten Streams beginnen soll. Die NoiseAudioDevice Implementierung dieser Methode startet die capturer Thread, der nach 1 Sekunde in der Warteschlange ausgeführt werden soll:

 @Override
 public boolean startRenderer() {
     rendererStarted = true;
     rendererHandler.postDelayed(renderer, rendererIntervalMillis);
     return true;
 }

Die renderer Thread holt sich 1 Sekunde Audio vom Audiobus, indem er die readRenderData() Methode der AudioBus Objekt. Dann schreibt es die Audiodaten in die Datei (für Samplezwecke). Und wenn das Audiogerät noch zum Rendern von Audiosamples verwendet wird, wird ein Timer gesetzt, um die rendererHandler Thread nach 0,1 Sekunden erneut:

private Handler rendererHandler;

private Runnable renderer = new Runnable() {
    @Override
    public void run() {
        rendererBuffer.clear();
        getAudioBus().readRenderData(rendererBuffer, SAMPLING_RATE);

        try {
            FileOutputStream stream = new FileOutputStream(rendererFile);
            stream.write(rendererBuffer.array());
            stream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (rendererStarted && !audioDriverPaused) {
            rendererHandler.postDelayed(renderer, rendererIntervalMillis);
        }
    }
};

Dieses Beispiel ist zu Lehrzwecken bewusst einfach gehalten – es schreibt lediglich die Audiodaten in eine Datei. In einer praxisnahen Anwendung eines benutzerdefinierten Audiotreibers könnten Sie diesen nutzen, um Audio auf einem Bluetooth-Gerät wiederzugeben oder um Audiodaten vor der Wiedergabe zu verarbeiten.

Herzlichen Glückwunsch! Sie haben das Tutorial zum Erstellen eines benutzerdefinierten Audiotreibers für Android abgeschlossen.
Du kannst den Code, den du hier entwickelt hast, weiter ausprobieren und anpassen oder dir die Nächste Schritte unten.

Nächste Schritte

Wenn Sie hier fertig sind, können Sie Ihre OpenTok-Application mit diesen hilfreichen Ressourcen weiter ausbauen und verbessern: