IVS-Broadcast-SDK: Benutzerdefinierte Audio-Quellen | Echtzeit-Streaming - Amazon IVS

IVS-Broadcast-SDK: Benutzerdefinierte Audio-Quellen | Echtzeit-Streaming

Hinweis: Diese Anleitung führt Sie durch die Schritte zum Einstieg in das Android Broadcast-SDK für IVS-Streaming. Informationen für die iOS- und Web-SDKs werden zukünftig veröffentlicht.

Benutzerdefinierte Audioeingangsquellen ermöglichen es einer Anwendung, ihre eigenen Audioeingänge an das Broadcast-SDK zu liefern, anstatt auf das integrierte Mikrofon des Geräts beschränkt zu sein. Eine benutzerdefinierte Audioquelle ermöglicht es Anwendungen, verarbeitetes Audio mit Effekten zu streamen, mehrere Audiostreams zu mischen oder in Audioverarbeitungsbibliotheken von Drittanbietern zu integrieren.

Beachten Sie, dass das Broadcast-SDK nicht mehr für die Verwaltung der Kamera verantwortlich ist, wenn Sie eine benutzerdefinierte Bildeingabequelle zur benutzerdefinierten Steuerung der Kamera verwenden. Stattdessen ist Ihre Anwendung für die Erfassung, Verarbeitung und Übertragung von Audiodaten an die benutzerdefinierte Quelle verantwortlich.

Der Arbeitsablauf für benutzerdefinierte Audioquellen folgt diesen Schritten:

  1. Audioeingang – Erstellen Sie eine benutzerdefinierte Audioquelle mit einem bestimmten Audioformat (Samplerate, Kanäle, Format).

  2. Ihre Verarbeitung – Erfassen oder generieren Sie Audiodaten aus Ihrer Audioverarbeitungspipeline.

  3. Benutzerdefinierte Audioquelle – Senden Sie Audiopuffer an die benutzerdefinierte Quelle mithilfe vonappendBuffer().

  4. Stage – In LocalStageStream abschließen und über Ihre StageStrategy in der Stage veröffentlichen.

  5. Teilnehmer – Die Teilnehmer der Phase erhalten das bearbeitete Audio in Echtzeit.

Android

Erstellen einer benutzerdefinierten Audioquelle

Erstellen Sie nach dem Erstellen einer DeviceDiscovery-Sitzung eine Audioeingabequelle:

DeviceDiscovery deviceDiscovery = new DeviceDiscovery(context); // Create custom audio source with specific format CustomAudioSource customAudioSource = deviceDiscovery.createAudioInputSource( 2, // Number of channels (1 = mono, 2 = stereo) BroadcastConfiguration.AudioSampleRate.RATE_48000, // Sample rate AudioDevice.Format.INT16 // Audio format (16-bit PCM) );

Diese Methode gibt eine CustomAudioSource zurück, die unformatierte PCM-Audiodaten akzeptiert. Die benutzerdefinierte Audioquelle muss mit demselben Audioformat konfiguriert sein, das Ihre Audioverarbeitungspipeline erzeugt.

Unterstützte Formate

Parameter Optionen Beschreibung
Kanäle 1 (Mono), 2 (Stereo) Die Anzahl der Audiokanäle.
Abtastrate RATE_16000, RATE_44100, RATE_48000 Audio-Abtastrate in Hz. Für hohe Qualität werden 48 kHz empfohlen.
Format INT16, FLOAT32 Format der Hörprobe. INT16 ist 16-Bit-Festkomma-PCM, FLOAT32 ist 32-Bit-Gleitkomma-PCM. Es sind sowohl verschachtelte als auch planare Formate verfügbar.

Audioeinstellungen

Verwenden Sie die Methode appendBuffer(), um Audiodaten an die benutzerdefinierte Quelle zu senden:

// Prepare audio data in a ByteBuffer ByteBuffer audioBuffer = ByteBuffer.allocateDirect(bufferSize); audioBuffer.put(pcmAudioData); // Your processed audio data // Calculate the number of bytes long byteCount = pcmAudioData.length; // Submit audio to the custom source // presentationTimeUs should be generated by and come from your audio source int samplesProcessed = customAudioSource.appendBuffer( audioBuffer, byteCount, presentationTimeUs ); if (samplesProcessed > 0) { Log.d(TAG, "Successfully submitted " + samplesProcessed + " samples"); } else { Log.w(TAG, "Failed to submit audio samples"); } // Clear buffer for reuse audioBuffer.clear();

Wichtige Überlegungen:

  • Audiodaten müssen in dem Format vorliegen, das bei der Erstellung der benutzerdefinierten Quelle angegeben wurde.

  • Die Zeitstempel sollten monoton ansteigend sein und von Ihrer Audioquelle bereitgestellt werden, um eine reibungslose Audiowiedergabe zu gewährleisten.

  • Reichen Sie regelmäßig Audiodateien ein, um Lücken im Stream zu vermeiden.

  • Die Methode gibt die Anzahl der verarbeiteten Samples zurück (0 bedeutet Fehler).

In einer Stage veröffentlichen

Die CustomAudioSource in einem AudioLocalStageStream abschließen und von Ihrer StageStrategy zurücksenden:

// Create the audio stream from custom source AudioLocalStageStream audioStream = new AudioLocalStageStream(customAudioSource); // Define your stage strategy Strategy stageStrategy = new Strategy() { @NonNull @Override public List<LocalStageStream> stageStreamsToPublishForParticipant( @NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { List<LocalStageStream> streams = new ArrayList<>(); streams.add(audioStream); // Publish custom audio return streams; } @Override public boolean shouldPublishFromParticipant( @NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return true; // Control when to publish } @Override public Stage.SubscribeType shouldSubscribeToParticipant( @NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return Stage.SubscribeType.AUDIO_VIDEO; } }; // Create and join the stage Stage stage = new Stage(context, stageToken, stageStrategy);

Vollständiges Beispiel: Integration der Audioverarbeitung

Hier ist ein vollständiges Beispiel, das die Integration mit einem Audioverarbeitungs-SDK zeigt:

public class AudioStreamingActivity extends AppCompatActivity { private DeviceDiscovery deviceDiscovery; private CustomAudioSource customAudioSource; private AudioLocalStageStream audioStream; private Stage stage; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Configure audio manager StageAudioManager.getInstance(this) .setPreset(StageAudioManager.UseCasePreset.VIDEO_CHAT); // Initialize IVS components initializeIVSStage(); // Initialize your audio processing SDK initializeAudioProcessing(); } private void initializeIVSStage() { deviceDiscovery = new DeviceDiscovery(this); // Create custom audio source (48kHz stereo, 16-bit) customAudioSource = deviceDiscovery.createAudioInputSource( 2, // Stereo BroadcastConfiguration.AudioSampleRate.RATE_48000, AudioDevice.Format.INT16 ); // Create audio stream audioStream = new AudioLocalStageStream(customAudioSource); // Create stage with strategy Strategy strategy = new Strategy() { @NonNull @Override public List<LocalStageStream> stageStreamsToPublishForParticipant( @NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return Collections.singletonList(audioStream); } @Override public boolean shouldPublishFromParticipant( @NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return true; } @Override public Stage.SubscribeType shouldSubscribeToParticipant( @NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return Stage.SubscribeType.AUDIO_VIDEO; } }; stage = new Stage(this, getStageToken(), strategy); } private void initializeAudioProcessing() { // Initialize your audio processing SDK // Set up callback to receive processed audio yourAudioSDK.setAudioCallback(new AudioCallback() { @Override public void onProcessedAudio(byte[] audioData, int sampleRate, int channels, long timestamp) { // Submit processed audio to IVS Stage submitAudioToStage(audioData, timestamp); } }); } // The timestamp is required to come from your audio source and you // should not be generating one on your own, unless your audio source // does not provide one. If that is the case, create your own epoch // timestamp and manually calculate the duration between each sample // using the number of frames and frame size. private void submitAudioToStage(byte[] audioData, long timestamp) { try { // Allocate direct buffer ByteBuffer buffer = ByteBuffer.allocateDirect(audioData.length); buffer.put(audioData); // Submit to custom audio source int samplesProcessed = customAudioSource.appendBuffer( buffer, audioData.length, timestamp > 0 ? timestamp : System.nanoTime() / 1000 ); if (samplesProcessed <= 0) { Log.w(TAG, "Failed to submit audio samples"); } buffer.clear(); } catch (Exception e) { Log.e(TAG, "Error submitting audio: " + e.getMessage(), e); } } @Override protected void onDestroy() { super.onDestroy(); if (stage != null) { stage.release(); } } }

Bewährte Methoden

Audioformatkonsistenz

Stellen Sie sicher, dass das von Ihnen eingereichte Audioformat dem Format entspricht, das bei der Erstellung der benutzerdefinierten Quelle angegeben wurde:

// If you create with 48kHz stereo INT16 customAudioSource = deviceDiscovery.createAudioInputSource( 2, RATE_48000, INT16 ); // Your audio data must be: // - 2 channels (stereo) // - 48000 Hz sample rate // - 16-bit interleaved PCM format

Pufferverwaltung

Verwenden Sie direkte ByteBuffers und verwenden Sie sie erneut, um die Garbage Collection zu minimieren:

// Allocate once private ByteBuffer audioBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE); // Reuse in callback public void onAudioData(byte[] data) { audioBuffer.clear(); audioBuffer.put(data); customAudioSource.appendBuffer(audioBuffer, data.length, getTimestamp()); audioBuffer.clear(); }

Timing und Synchronisation

Für eine reibungslose Audiowiedergabe müssen Sie die von Ihrer Audioquelle bereitgestellten Zeitstempel verwenden. Wenn Ihre Audioquelle keinen eigenen Zeitstempel hat, erstellen Sie Ihren eigenen Epochenzeitstempel und berechnen Sie die Dauer zwischen den einzelnen Samples manuell anhand der Anzahl der Frames und der Framegröße.

// "audioFrameTimestamp" should be generated by your audio source // Consult your audio source’s documentation for information on how to get this long timestamp = audioFrameTimestamp;

Fehlerbehandlung

Überprüfen Sie immer den Rückgabewert von appendBuffer():

int samplesProcessed = customAudioSource.appendBuffer(buffer, count, timestamp); if (samplesProcessed <= 0) { Log.w(TAG, "Audio submission failed - buffer may be full or format mismatch"); // Handle error: check format, reduce submission rate, etc. }