

# SDK de transmissão do IVS: fontes de imagens personalizadas \| streaming em tempo real
<a name="broadcast-custom-audio-sources"></a>

**Nota:** este guia aplica-se apenas ao SDK de transmissão de streaming em tempo real do IVS para Android. As informações sobre os SDKs do iOS e da web serão publicadas no futuro.

As fontes de entrada personalizada de áudio permitem que uma aplicação forneça a própria entrada de áudio para o SDK de transmissão, em vez de ficar limitado ao microfone embutido do dispositivo. Uma fonte de áudio personalizada permite que as aplicações transmitam áudio processado com efeitos, mixem vários fluxos de áudio ou se integrem a bibliotecas de processamento de áudio de terceiros.

Ao usar uma fonte de entrada de áudio personalizada, o SDK de transmissão deixa de ser responsável pelo gerenciamento direto do microfone. Em vez disso, sua aplicação é responsável por capturar, processar e enviar dados de áudio para a fonte personalizada.

O fluxo de trabalho da fonte de áudio personalizada segue estas etapas:

1. Entrada de áudio: crie uma fonte de áudio personalizada com formato de áudio especificado (taxa de amostragem, canais, formato). 

1. Seu processamento: capture ou gere dados de áudio do seu pipeline de processamento de áudio.

1. Fonte de áudio personalizada: envie buffers de áudio para a fonte personalizada usando `appendBuffer()`.

1. Palco: encapsulamento no `LocalStageStream` e publicação no palco por meio de seu `StageStrategy`. 

1. Participantes: os participantes do palco recebem o áudio processado em tempo real.

## Android
<a name="custom-audio-sources-android"></a>

### Criar uma fonte de áudio personalizada
<a name="custom-audio-sources-android-creating-a-custom-audio-source"></a>

Após criar uma sessão de `DeviceDiscovery`, crie uma fonte de entrada de áudio personalizada:

```
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) 
);
```

Esse método retorna um `CustomAudioSource`, que aceita dados brutos de áudio PCM. A fonte de áudio personalizada deve ser configurada com o mesmo formato de áudio que seu pipeline de processamento de áudio produz.

#### Formatos de áudio compatíveis
<a name="custom-audio-sources-android-submitting-audio-data-supportedi-audio-formats"></a>


| Parâmetro | Opções | Descrição | 
| --- | --- | --- | 
| Canais | 1 (mono), 2 (estéreo) | O número de canais de áudio. | 
| Taxa de amostra | RATE\_16000, RATE\_44100, RATE\_48000 | Taxa de amostragem de áudio em Hz. 48kHz recomendados para alta qualidade. | 
| Formato | INT16, FLOAT32 | Formato de amostra de áudio. INT16 é PCM de ponto fixo de 16 bits, FLOAT32 é PCM de ponto flutuante de 32 bits. Os formatos intercalado e plano estão disponíveis. | 

### Enviar dados de áudio
<a name="custom-audio-sources-android-submitting-audio-data"></a>

Para enviar dados de áudio para a fonte personalizada, use o método `appendBuffer()`:

```
// 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();
```

**Considerações importantes:**
+ Os dados de áudio devem estar no formato especificado ao criar a fonte personalizada.
+ Os carimbos de data/hora devem aumentar monotonicamente e serem fornecidos pela fonte de áudio para uma reprodução suave do áudio.
+ Envie áudio regularmente para evitar lacunas na transmissão.
+ O método retorna o número de amostras processadas (0 indica falha). 

### Publicar em um palco
<a name="custom-audio-sources-android-publishing-to-a-stage"></a>

Encapsule o `CustomAudioSource` em um `AudioLocalStageStream` e retorne-o ao seu `StageStrategy`:

```
// 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);
```

### Exemplo completo: integração de processamento de áudio
<a name="custom-audio-sources-android-complete-example"></a>

Aqui está um exemplo completo que mostra a integração com um SDK de processamento de áudio:

```
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(); 
      } 
   } 
}
```

### Práticas recomendadas
<a name="custom-audio-sources-android-best-practices"></a>

#### Consistência do formato de áudio
<a name="custom-audio-sources-android-best-practices-audio-format-consistency"></a>

Certifique-se de que o formato de áudio enviado corresponda ao formato especificado ao criar a fonte personalizada:

```
// 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
```

#### Gerenciamento de buffer
<a name="custom-audio-sources-android-best-practices-buffer-managemetn"></a>

Use diretamente o `ByteBuffers` e reutilize-o para minimizar a coleta de resíduos: 

```
// 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(); 
}
```

#### Cronometragem e sincronização
<a name="custom-audio-sources-android-best-practices-timing-and-synchronization"></a>

Você deve usar os carimbos de data/hora fornecidos pela fonte de áudio para uma reprodução suave do áudio. Se sua fonte de áudio não fornecer seu próprio carimbo de data/hora, crie seu próprio carimbo de data/hora de época e calcule manualmente a duração entre cada amostra usando o número de quadros e o tamanho do quadro. 

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

#### Como tratar erros
<a name="custom-audio-sources-android-best-practices-error-handling"></a>

Sempre verifique o valor de retorno de `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. 
}
```