Tous les produits
Search
Centre de documentation

ApsaraVideo Live:Capture audio personnalisée

Dernière mise à jour :Aug 19, 2026

Utilisez le SDK ARTC pour capturer l'audio à partir de sources personnalisées au lieu du module de capture intégré.

Présentation

Le module audio intégré du SDK ARTC couvre la plupart des cas d'utilisation, mais une capture audio personnalisée s'avère nécessaire dans les scénarios suivants :

  • Lorsqu'un périphérique de capture audio est occupé par un autre processus.

  • Pour capturer l'audio depuis une source personnalisée, telle qu'un système propriétaire ou un fichier audio, et l'envoyer au SDK.

La capture audio personnalisée vous permet de gérer vos propres périphériques et sources audio.

Exemples de code

Android : Android/ARTCExample/AdvancedUsage/src/main/java/com/aliyun/artc/api/advancedusage/CustomAudioCaptureAndRender/CustomAudioCaptureActivity.java.

iOS : iOS/ARTCExample/AdvancedUsage/CustomAudioCapture/CustomAudioCaptureVC.swift.

Prérequis

Avant de commencer, assurez-vous d'avoir effectué les opérations suivantes :

Implémentation

image

1. Activer ou désactiver la capture interne

Pour utiliser la capture audio personnalisée, commencez par désactiver le module de capture interne du SDK en transmettant le paramètre extras lors de l'appel à getInstance pour créer le moteur :

user_specified_use_external_audio_record : Désactive la capture interne du SDK pour activer la capture audio personnalisée.

  • "TRUE" : Utilise la capture audio personnalisée (désactive la capture interne).

  • "FALSE" : N'utilise pas la capture audio personnalisée (active la capture interne).

Remarque

Le paramètre extras est une chaîne JSON.

Android

String extras = "{\"user_specified_use_external_audio_record\":\"TRUE\"}";
mAliRtcEngine = AliRtcEngine.getInstance(this, extras);

iOS

// Create and initialize the engine.
var customAudioCaptureConfig: [String: String] = [:]
// Use custom audio capture.
customAudioCaptureConfig["user_specified_use_external_audio_record"] = "TRUE"
// Serialize to JSON.
guard let jsonData = try? JSONSerialization.data(withJSONObject: customAudioCaptureConfig, options: []),
let extras = String(data: jsonData, encoding: .utf8) else {
     print("JSON serialization failed")
     return
 }
let engine = AliRtcEngine.sharedInstance(self, extras:extras)

Mac

NSString * extras = @"{\"user_specified_use_external_audio_record\":\"TRUE\"}";
mAliRtcEngine = [AliRtcEngine sharedInstance:self extras:extras];

Windows

/* Windows supports enabling or disabling audio capture during engine creation. */
/* Disable internal capture. */
char* extra = "{\"user_specified_enable_use_virtual_audio_device\":\"TRUE\", \"user_specified_use_external_audio_record\":\"TRUE\"}";
mAliRtcEngine = AliRtcEngine.Create(extra);

/* Enable internal capture. */
char* extra = "{\"user_specified_enable_use_virtual_audio_device\":\"FALSE\", \"user_specified_use_external_audio_record\":\"FALSE\"}";
mAliRtcEngine = AliRtcEngine.Create(extra);

2. Ajouter un flux audio externe

Appelez addExternalAudioStream pour ajouter un flux audio externe et obtenir son ID de flux. Pour activer le traitement audio 3A (annulation d'écho acoustique, contrôle automatique du gain et suppression du bruit), définissez le paramètre enable3A dans l'objet AliRtcExternalAudioStreamConfig.

Remarque

Moment approprié pour appeler cette méthode :

  • Si vous devez utiliser la fonction 3A, nous vous recommandons d'appeler addExternalAudioStream après la publication réussie du flux audio et lorsque le module de capture personnalisé obtient la première trame audio. Autrement dit, vous devez appeler la méthode après que l'interface onAudioPublishStateChanged a renvoyé newState avec la valeur AliRtcStatsPublished (3).

  • Si vous n'avez pas besoin du traitement audio 3A (par exemple, lors de la diffusion d'audio à partir d'un fichier local, d'une source réseau ou de données générées par synthèse vocale) : Vous pouvez appeler cette méthode immédiatement après la création du moteur. Ensuite, commencez à pousser les données audio une fois le flux publié.

Android

AliRtcEngine.AliRtcExternalAudioStreamConfig config = new AliRtcEngine.AliRtcExternalAudioStreamConfig();
config.sampleRate = SAMPLE_RATE; // Sample rate
config.channels = CHANNEL; // Number of channels
// Publish volume
config.publishVolume = 100;
// Local playout volume
config.playoutVolume = isLocalPlayout ? 100 : 0;
config.enable3A = true;

int result = mAliRtcEngine.addExternalAudioStream(config);
if (result <= 0) {
    return;
}
// The return value is the stream ID. You need it to push data to the SDK.
mExternalAudioStreamId = result;

iOS

/* Set parameters based on your application's needs. */
AliRtcExternalAudioStreamConfig *config = [AliRtcExternalAudioStreamConfig new];
// This must match the number of channels of the external PCM audio stream. Set to 1 for mono or 2 for stereo.
config.channels = _pcmChannels;
// This must match the sample rate of the external PCM audio stream.
config.sampleRate = _pcmSampleRate;
config.playoutVolume = 0;
config.publishVolume = 100;
_externalPlayoutStreamId = [self.engine addExternalAudioStream:config];

Mac

/* Set parameters based on your application's needs. */
AliRtcExternalAudioStreamConfig *config = [AliRtcExternalAudioStreamConfig new];
config.channels = pcmChannels;
/** Sample rate. Default: 48000. Supported values: 8000, 12000, 16000, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000. */
config.sampleRate = pcmSampleRate;
config.playoutVolume = 0;
config.publishVolume = 100;
int ret = [self.engine addExternalAudioStream:config];

Windows

/* Get the media engine. */
IAliEngineMediaEngine* mAliRtcMediaEngine = nullptr;
    
mAliRtcEngine->QueryInterface(AliEngineInterfaceMediaEngine, (void **)&mAliRtcMediaEngine);
/* Set parameters based on your application's needs. */
AliEngineExternalAudioStreamConfig config;
config.playoutVolume = currentAudioPlayoutVolume;
config.publishVolume = currentAudioPublishVolume;
config.channels = 1;
config.sampleRate = 48000;
config.publishStream = 0;
audioStreamID = mAliRtcMediaEngine->AddExternalAudioStream(config);

mAliRtcMediaEngine->Release();

3. Implémenter un module de capture audio personnalisé

Vous devez implémenter la logique permettant de capturer et de traiter les données audio, puis de les envoyer au SDK.

Alibaba Cloud fournit un exemple de capture personnalisé qui montre comment lire des données au format PCM à partir d'un fichier local ou d'un microphone.

4. Pousser les données audio vers le SDK

Une fois le flux audio publié (le rappel onAudioPublishStateChanged indique AliRtcStatsPublished), appelez pushExternalAudioStreamRawData avec l'ID de flux obtenu à l'étape 2 pour envoyer les données audio capturées au SDK. Convertissez les données audio en un objet AliRtcAudioFrame avec les propriétés suivantes :

  • data : Les données audio.

  • numSamples : Le nombre d'échantillons par canal dans les données fournies.

  • bytesPerSample : Octets par échantillon (profondeur de bits / 8). Par exemple, cette valeur est de 2 pour un audio 16 bits.

  • numChannels : Nombre de canaux audio.

  • samplesPerSec : La fréquence d'échantillonnage, en Hz (par exemple, 16000 ou 48000).

Remarque
  • Vous ne devez commencer à pousser les données qu'après la publication du flux audio (lorsque le rappel onAudioPublishStateChanged signale un état AliRtcStatsPublished).

  • Définissez le paramètre numSamples dans l'objet AliRtcAudioFrame sur la longueur réelle des données capturées. Des méthodes telles que AudioRecord.read peuvent renvoyer moins de données que la taille du tampon ; vous devez donc utiliser la valeur de retour de la méthode pour déterminer la longueur réelle des données.

  • L'appel à pushExternalAudioStreamRawData peut échouer si le tampon interne est plein. Votre application doit gérer cette erreur et mettre en œuvre un mécanisme de nouvelle tentative.

  • Nous vous recommandons d'appeler pushExternalAudioStreamRawData toutes les 10 ms pour envoyer les données.

Android

// Assume the captured audio data is in `audioData`, the size is `bytesRead` bytes, and it represents 10 ms of data.
if (mAliRtcEngine != null && bytesRead > 0) {
    // Construct an AliRtcAudioFrame object. `bitsPerSample` is the bit depth, which is typically 16.
    AliRtcEngine.AliRtcAudioFrame sample = new AliRtcEngine.AliRtcAudioFrame();
    sample.data = audioData;
    sample.numSamples = bytesRead / (channels * (bitsPerSample / 8)); // Calculate the number of samples based on the actual number of bytes read.
    sample.numChannels = channels;
    sample.samplesPerSec = sampleRate;
    sample.bytesPerSample = bitsPerSample / 8;

    int ret = 0;
    // Retry the push operation if it fails because the buffer is full.
    int retryCount = 0;
    final int MAX_RETRY_COUNT = 20;
    final int BUFFER_WAIT_MS = 10;
    do {
        // Push the captured data to the SDK.
        ret = mAliRtcEngine.pushExternalAudioStreamRawData(mExternalAudioStreamId, sample);
        if(ret == ErrorCodeEnum.ERR_SDK_AUDIO_INPUT_BUFFER_FULL) {
            // Handle the buffer full scenario. Wait for a short period and retry.
            retryCount++;
            if(mExternalAudioStreamId <= 0 || retryCount >= MAX_RETRY_COUNT) {
                // The stream has been stopped or the maximum retry count is reached. Exit the loop.
                break;
            }

            try {
                // Pause for a short interval.
                Thread.sleep(BUFFER_WAIT_MS);
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }
        } else {
            // Push succeeded or another error occurred. Exit the loop.
            break;
        }
    } while (retryCount < MAX_RETRY_COUNT);
}

iOS

// Construct an AliRtcAudioFrame object from the captured audio data.
let sample = AliRtcAudioFrame()
sample.dataPtr = UnsafeMutableRawPointer(mutating: pcmData)
sample.samplesPerSec = pcmSampleRate
sample.bytesPerSample = Int32(MemoryLayout<Int16>.size)
sample.numOfChannels = pcmChannels
sample.numOfSamples = numOfSamples

var retryCount = 0

while retryCount < 20 {
    if !(pcmInputThread?.isExecuting ?? false) {
        break
    }
    // Push the audio data to the SDK.
    let rc = rtcEngine?.pushExternalAudioStream(externalPublishStreamId, rawData: sample) ?? 0

    // Handle a full buffer.
    // 0x01070101 SDK_AUDIO_INPUT_BUFFER_FULL: The buffer is full. Retransmission is required.
    if rc == 0x01070101 && !(pcmInputThread?.isCancelled ?? true) {
        Thread.sleep(forTimeInterval: 0.03) // 30ms
        retryCount += 1;
    } else {
        if rc < 0 {
            "pushExternalAudioStream error, ret: \(rc)".printLog()
        }
        break
    }
}

Mac

while ( true ) {
    
    if (![pcmInputThread isExecuting]) {
        push_error = YES;
        break;
    }

    AliRtcAudioFrame *sample = [AliRtcAudioFrame new];
    sample.dataPtr = pcmData;
    sample.samplesPerSec = pcmSampleRate;
    sample.bytesPerSample = sizeof(int16_t);
    sample.numOfChannels = pcmChannels;
    sample.numOfSamples = numOfSamples;
    int rc = [self.engine pushExternalAudioStream:_externalPublishStreamId rawData:sample];
    count = count + 1;

    /* If the error is AliRtcErrAudioBufferFull, sleep for a moment and then continue pushing. */
    if ( rc == AliRtcErrAudioBufferFull && [pcmInputThread isCancelled ] == NO ) {
        [NSThread sleepForTimeInterval:0.04] ;
    }else {
        if ( rc < 0 ) {
            push_error = true ;
        }

        break ;
    }

}

Windows

Remarque

Avant d'implémenter la capture personnalisée sur Windows, vous devez appeler la méthode QueryInterface pour obtenir l'objet du moteur multimédia.

/* Get the media engine. */
IAliEngineMediaEngine* mAliRtcMediaEngine = nullptr;   
mAliRtcEngine->QueryInterface(AliEngineInterfaceMediaEngine, (void **)&mAliRtcMediaEngine);

// Construct an audio frame from the data.
AliEngineAudioRawData rawData;
rawData.dataPtr = frameInfo.audio_data[0];
rawData.numOfSamples = (int) (frameInfo.audio_data[0].length / (2 * frameInfo.audio_channels));
rawData.bytesPerSample = 2;
rawData.numOfChannels = frameInfo.audio_channels;
rawData.samplesPerSec = frameInfo.audio_sample_rate;
// Push the data to the SDK.
int ret = mAliRtcMediaEngine->PushExternalAudioStreamRawData(audioStreamID, rawData);
// Handle buffer full and other errors.
if ( ret == AliEngineErrorAudioBufferFull ) {
    Sleep(40);
    continue ;
} 

// Release the media engine.
mAliRtcMediaEngine->Release();

5. Supprimer le flux audio externe

Pour arrêter la publication de l'audio depuis la source personnalisée, appelez removeExternalAudioStream.

Android

mAliRtcEngine.removeExternalAudioStream(mExternalAudioStreamId);

iOS

[self.engine removeExternalAudioStream:_externalPublishStreamId];

Mac

[self.engine removeExternalAudioStream:_externalPublishStreamId];

Windows

/* Get the media engine. */
IAliEngineMediaEngine* mAliRtcMediaEngine = nullptr;
    
mAliRtcEngine->QueryInterface(AliEngineInterfaceMediaEngine, (void **)&mAliRtcMediaEngine);

mAliRtcMediaEngine->RemoveExternalAudioStream(audioStreamID);
mAliRtcMediaEngine->Release();

6. (Facultatif) Activer ou désactiver dynamiquement la capture interne

Pour activer ou désactiver dynamiquement la capture interne du SDK pendant un appel, utilisez la méthode setParameter.

Android

/* Dynamically disable internal capture. */
String parameter = "{\"audio\":{\"enable_system_audio_device_record\":\"FALSE\"}}";
mAliRtcEngine.setParameter(parameter);

/* Dynamically enable internal capture. */
String parameter = "{\"audio\":{\"enable_system_audio_device_record\":\"TRUE\"}}"; 
mAliRtcEngine.setParameter(parameter);

iOS

// Dynamically disable internal capture.
engine.setParameter("{\"audio\":{\"enable_system_audio_device_record\":\"FALSE\"}}")
// Dynamically enable internal capture.
engine.setParameter("{\"audio\":{\"enable_system_audio_device_record\":\"TRUE\"}}")

Mac

// Dynamically disable internal capture.
[self setParameter:@"{\"audio\":{\"enable_system_audio_device_record\":\"FALSE\"}}"];
// Dynamically enable internal capture.
[self setParameter:@"{\"audio\":{\"enable_system_audio_device_record\":\"TRUE\"}}"];

Windows

/* Dynamically disable internal capture. */
mAliRtcEngine->SetParameter("{\"audio\":{\"enable_system_audio_device_record\":\"FALSE\"}}");

/* Dynamically enable internal capture. */
mAliRtcEngine->SetParameter("{\"audio\":{\"enable_system_audio_device_record\":\"TRUE\"}}");

FAQ

  • Quelle est la fréquence recommandée pour appeler pushExternalAudioStreamRawData ?

    • Nous vous recommandons de synchroniser les appels avec l'horloge du périphérique audio physique, en appelant la méthode chaque fois que le périphérique fournit un nouveau paquet de données.

    • Si aucune horloge de périphérique physique n'est disponible, nous vous recommandons d'envoyer les données toutes les 10 à 50 ms.

  • Puis-je utiliser le traitement audio 3A interne du SDK (AEC, AGC et ANS) avec la capture audio personnalisée ?

    • Oui. Comme décrit à l'étape 2, vous pouvez définir le paramètre enable3A lors de l'ajout du flux audio externe pour activer ou désactiver le traitement audio 3A interne du SDK.