# 自定义音频采集和渲染

# 简介

实时音频传输过程中,JC SDK 会启动默认的音频模块进行采集和渲染。当遇到不支持系统标准 API 的音频设备的时候,或者希望使用自己已经拥有的音频模块进行音频采集和渲染的时候,您可以选择在音频被采集前自定义音频采集,也可以选择在音频播放前自定义音频渲染

# 前提条件

在自定义音频采集和渲染前,确保已经初始化了 JCMediaDevice 模块。

# 自定义音频采集

参照下述步骤,在音频输入前,修改音频输入源和采样率等参数:

  1. 发起通话前,将 JCMediaDevice 中 audioParam 的 autoStartAudioInputDevice (opens new window) 设置为 false。这种情况下才可以获取音频输入数据。
  2. 调用 inputCustomAudioData (opens new window) 自定义音频采集。 建议在一对一通话状态为连接中时或者加入频道成功后,在子线程不断的调用此方法传入音频数据。
  3. 在结束通话的时候,关闭子线程停止音频采集。
// 在开启前将 autoStartAudioOutputDevice 属性设为 false
- (void)onAutoAudioInputDeviceChanged {
    _mediaDevice.audioParam.autoStartAudioInputDevice = false;
}

// 声明一个子线程用于输入自定义音频数据
@property (nonatomic) NSThread *customAudioInputThread;

/** 
 * 0.当收到一对一通话状态更新时且通话状态是否为连接中,或收到成功加入多方通话时,可执行以下方法。
 * 1.初始化 customAudioInputThread。
 * 2.将音频文件转为 NSData 数据类型。
 * 3.获取音频数据长度,并按照一定长度等分成若干音频数据。
 * 4.while 循环不断将音频数据通过 inputCustomAudioData 接口传入到媒体层,每次传入后停止一秒。
 * 5.子线程开始工作。
 */
- (void)startInputAudio {
    if (!_customAudioInputThread) {
        _customAudioInputThread = [[NSThread alloc] initWithBlock:^{
            NSString *path = [[NSBundle mainBundle] pathForResource:@"customAudio" ofType:@"pcm"];
            NSData *data= [NSData dataWithContentsOfFile:path];
            int len = (int)(data.length);
            int packetLen = 32 * 1024;
            int packetNum = len / packetLen;
            NSMutableArray *dataList = [NSMutableArray array];
            for (int i = 0; i < packetNum; i++) {
                NSData *d = [data subdataWithRange:NSMakeRange(i * packetLen, packetLen)];
                [dataList addObject:d];
            }
            int num = 0;
            while (([_call getActiveCallItem] && [_call getActiveCallItem].state == JCCallStateConnecting) || _mediaChannel.state == JCMediaChannelStateJoined) {
                [_mediaDevice inputCustomAudioData:dataList[num] sampleRateHz:16000 channels:1 playDelayMS:0 recDelayMS:0 clockDrift:0];
                ++num;
                num = num % packetNum;
                [NSThread sleepForTimeInterval:1];
            }
        }];
    }
    if (!_customAudioInputThread.isExecuting && !_customAudioInputThread.isFinished && !_customAudioInputThread.isCancelled) {
        [_customAudioInputThread start];
    }
}

# 自定义音频渲染

参照下述步骤,在音频播放前,修改音频输入源和采样率等参数:

  1. 发起通话前,将 JCMediaDevice 中 audioParam 的 autoStartAudioInputDevice (opens new window) 设置为 false。这种情况下才可以获取音频输出数据。
  2. 调用 getAudioOutputData (opens new window) 自定义音频渲染。建议在一对一通话状态为连接中时或者加入频道成功后,在子线程不断的调用此方法渲染音频数据。
  3. 在结束通话的时候,关闭子线程停止音频渲染。

# 示例代码

// 在开启前将 autoStartAudioOutputDevice 属性设为 false
- (void)onAutoAudioOutputDeviceChanged {
    _mediaDevice.audioParam.autoStartAudioOutputDevice = false;
}

// 声明一个子线程用于获取音频输出数据
@property (nonatomic) NSThread *outputAudioThread;

/** 
 * 0.当收到一对一通话状态更新时且通话状态是否为连接中,或收到成功加入多方通话时,可执行以下方法。
 * 1.初始化 outputAudioThread。
 * 2.while 循环不断通过 getAudioOutputData 接口获取对端音频数据。
 * 3.子线程开始工作。
 */
- (void)playOutputAudio {
    if (!_outputAudioThread) {
        _outputAudioThread = [[NSThread alloc] initWithBlock:^{
            while (([_call getActiveCallItem] && [_call getActiveCallItem].state == JCCallStateConnecting) || _mediaChannel.state == JCMediaChannelStateJoined) {
                if (!_mediaDevice.audioParam.autoStartAudioOutputDevice) {
                    NSData *data = [NSData data];
                    int len = 1024;
                    unsigned char * buf = malloc(len);
                    [_mediaDevice getAudioOutputData:buf length:len sampleRateHz:16000 channels:1];
                    data = [NSData dataWithBytes:buf length:len];
                    if (data) {
                        // 此处可播放音频数据 data
                    }
                    free(buf);
                }
            }
        }];
    }
    if (!_outputAudioThread.isFinished && !_outputAudioThread.isExecuting && !_outputAudioThread.isCancelled) {
        [_outputAudioThread start];
    }
}
最后更新时间: 2021/3/30 14:57:27