SOP TRTC SDK 仪表盘 Android TRTC 发送自定义采集的视频数据 Android TRTC 发送自定义采集音频解决方案 Android TRTC 实现横屏视频通话 iOS端 TRTC 发送自定义采集视频解决方案 iOS端 TRTC 发送自定义采集音频解决方案 APNS推送 脏字过滤 TXLiteAVSDK中使用 AVAudioSession 问题总结 AndroidStudio编译SDK报错 RoomService部署验证 Xcode编译SDK报错 iOS编译库冲突问题 iOS端移动直播自定义采集实现 iOS端TXLiteAVSDK与IMSDK 3.x集成冲突报错问题 Android端TXLiteAVSDK与IMSDK 3.x集成冲突报错问题 Android端LiteIM sdk升级IM4.x版本报错问题 移动直播iOS 12兼容问题 如何实现好的画质 如何计算PCM音量大小 使用播放器播放视频有黑边 直播拉流播放失败 直播拉流端卡顿现象 短视频上传失败 移动直播SDK对接第三方美颜库 移动直播连麦解决方案 Android移动直播推自定义采集的视频数据 Android移动直播推自定义采集的声音数据 Android直播播放如何获取YUV数据 Android直播播放如何自定义渲染 实时音视频画面黑屏 实时音视频订阅流显示 iOS 12默认新编译系统下文件名冲突问题 TXLiteAVSDK指标监控 进阶:小程序实时音视频参数透传 移动直播 Android 9.0 无法拉流问题 移动直播推流事件回调 移动直播拉流事件回调 短视频实现视频缩略图列表转GIF功能 roomService加入群组时报错invalid group id NTP时间戳转换 提示Role not exists 角色不存在 如何播放背景音乐 iOS端短视频添音频相关问题总结 Web同步终端离线推送TIMOfflinePushInfo说明文档 web端自定义消息发送 web端同步终端的已读回执 web端对群组内用户禁言操作 TRTC v2混流接口setMixTranscodingConfig使用指引

iOS端 TRTC 发送自定义采集音频解决方案

发布日期:2019年5月3日 更新日期:2019年5月3日 贡献者:zilongye

常见场景

  实时音视频 TXLiteAVSDK_TRTC 默认通过调用 startLocalAudio: 接口实现音频的采集。如果开发者工程项目有自己的音频采集或者处理逻辑,SDK 也支持自定义音频采集方案:通过 TRTCCloud 的 enableCustomAudioCapture 接口关闭 TRTC SDK 默认的声音采集流程。然后您可以使用 sendCustomAudioData 接口向 TRTC SDK 填充您自己的声音数据。

/**
 * 启用音频自定义采集模式
 *
 * 开启该模式后,SDK 不在运行原有的音频采集流程,只保留编码和发送能力。
 * 您需要用 sendCustomAudioData() 不断地向 SDK 塞入自己采集的视频画面。
 *
 * @note 由于回声抵消(AEC)需要严格的控制声音采集和播放的时间,所以开启自定义音频采集后,AEC 能力可能会失效。
 *
 * @param enable 是否启用, true:启用;false:关闭
 */
- (void)enableCustomAudioCapture:(BOOL)enable;

/**
 * 向 SDK 投送自己采集的音频数据
 *
 * TRTCAudioFrame 推荐如下填写方式:
 *
 * - data:音频帧 buffer。音频帧数据必须是 PCM 格式,推荐每帧20ms采样数。【48000采样率、单声道的帧长度:48000 × 0.02s × 1 × 16bit = 15360bit = 1920字节】。
 * - sampleRate:采样率,仅支持48000。
 * - channel:频道数量(如果是立体声,数据是交叉的),单声道:1; 双声道:2。
 * - timestamp:如果 timestamp 间隔不均匀,会严重影响音画同步和录制出的 MP4 质量。
 *
 * 参考文档:[自定义采集和渲染](https://cloud.tencent.com/document/product/647/34066)。
 *
 * @param frame 音频数据
 * @note 可以设置 frame 中的 timestamp 为 0,相当于让 SDK 自己设置时间戳,但请“均匀”地控制 sendCustomAudioData 的调用间隔,否则会导致声音断断续续。
 */
- (void)sendCustomAudioData:(TRTCAudioFrame *)frame;

接口调用流程

//1、进房之前启用音频自定义采集模式
- (void)enterRoom {
{
    //TRTCParams 定义参考头文件TRTCCloudDef.h
    TRTCParams *params = [[TRTCParams alloc] init];
    params.sdkAppId    = sdkappid;
    params.userId      = userid;
    params.userSig     = usersig;
    params.roomId      = 908; //输入您想进入的房间

    //开启本地视频的预览画面
    [_trtc startLocalPreview:[TRTCMoreViewController isFrontCamera] view:_localView];
    //启用音频自定义采集模式
    [_trtc enableCustomAudioCapture:YES];
    //进入实时音视频房间
    [trtcCloud enterRoom:params appScene:TRTCAppSceneVideoCall];
}

//2、进房成功回调里面开启开发者自身的音频采集
- (void)onEnterRoom:(NSInteger)elapsed {
    NSString *msg = [NSString stringWithFormat:@"[%@]进房成功[%u]: elapsed[%d]", _userID, _roomID, elapsed];

    //自定义采集音频麦克风数据,CustomAudioCapturor 是示例用来采集音频
    [CustomAudioCapturor sharedInstance].delegate = self;
    //参数分别是采样率、声道数、音频buffer字节长度
    [[CustomAudioCapturor sharedInstance] start:48000 nChannels:1 nSampleLen:1920];
}

//3、向 SDK 投送自己采集的音频数据,onAudioCapturePcm 是 CustomAudioCapturor 定义的代理方法用来返回采集的音频 PCM 数据
#pragma mark - CustomAudioCapturorDelegate
- (void)onAudioCapturePcm:(NSData *)pcmData sampleRate:(int)sampleRate channels:(int)channels ts:(uint32_t)timestampMs {

    //拿到PCM数据调用SDK接口去编码和网络传输
    TRTCAudioFrame * frame = [[TRTCAudioFrame alloc] init];
    frame.data = pcmData;
    frame.sampleRate = sampleRate;
    frame.channels = channels;
    frame.timestamp = timestampMs;
    [_trtc sendCustomAudioData:frame];
}

附录:自定义采集音频测试代码

使用 AudioQueue 采集麦克风音频数据:

1、将 CustomAudioCapturor.h 和 CustomAudioCapturor.m 引入您自己的项目工程;
2、在合适的地方(如上述提到的onEnterRoom回调中)调用 start: 方法开始采集;
3、遵循 CustomAudioCapturorDelegate 代理,在代理方法 onAudioCapturePcm: 中调用 sendCustomAudioData: 向 SDK 投送自己采集的音频数据。

@protocol CustomAudioCapturorDelegate

- (void)onAudioCapturePcm:(NSData *)pcmData sampleRate:(int)sampleRate channels:(int)channels ts:(uint32_t)timestampMs;

@end

@interface CustomAudioCapturor : NSObject

@property(nonatomic, weak)id<CustomAudioCapturorDelegate> delegate;

+ (instancetype) sharedInstance;

- (void)start:(int)sampleRate nChannels:(int)channels nSampleLen:(int)sampleLen;

- (void)stop;

@end
//开启音频采集
- (void)start:(int)sampleRate nChannels:(int)channels nSampleLen:(int)sampleLen {
    aqData.mDataFormat.mFormatID         = kAudioFormatLinearPCM; // 2
    aqData.mDataFormat.mSampleRate       = sampleRate;            // 3
    aqData.mDataFormat.mChannelsPerFrame = channels;              // 4
    aqData.mDataFormat.mBitsPerChannel   = 16;                    // 5
    aqData.mDataFormat.mBytesPerPacket   =                        // 6
    aqData.mDataFormat.mBytesPerFrame =
    aqData.mDataFormat.mChannelsPerFrame * sizeof (SInt16);
    aqData.mDataFormat.mFramesPerPacket  = 1;                     // 7
    aqData.mDataFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;

    _sendBuf = malloc(sampleLen);
    _sendBufLen = 0;

    _sampleLen = sampleLen;

    [self setAudioSession];

    AudioQueueNewInput (                              // 1
                        &aqData.mDataFormat,                          // 2
                        HandleInputBuffer,                            // 3
                        &aqData,                                      // 4
                        NULL,                                         // 5
                        kCFRunLoopCommonModes,                        // 6
                        0,                                            // 7
                        &aqData.mQueue                                // 8
                        );

    UInt32 dataFormatSize = sizeof (aqData.mDataFormat);       // 1

    AudioQueueGetProperty (                                    // 2
                           aqData.mQueue,                                         // 3
                           kAudioQueueProperty_StreamDescription,                 // 4
                           // in Mac OS X, instead use
                           //    kAudioConverterCurrentInputStreamDescription
                           &aqData.mDataFormat,                                   // 5
                           &dataFormatSize                                        // 6
                           );

    DeriveBufferSize (                               // 1
                      aqData.mQueue,                               // 2
                      &aqData.mDataFormat,                          // 3
                      0.03,                                         // 4
                      &aqData.bufferByteSize                       // 5
                      );

    for (int i = 0; i < kNumberBuffers; ++i) {           // 1
        AudioQueueAllocateBuffer (                       // 2
                                  aqData.mQueue,                               // 3
                                  aqData.bufferByteSize,                       // 4
                                  &aqData.mBuffers[i]                          // 5
                                  );

        AudioQueueEnqueueBuffer (                        // 6
                                 aqData.mQueue,                               // 7
                                 aqData.mBuffers[i],                          // 8
                                 0,                                           // 9
                                 NULL                                         // 10
                                 );
    }

    aqData.mCurrentPacket = 0;                           // 1
    aqData.mIsRunning = true;                            // 2

    AudioQueueStart (                                    // 3
                     aqData.mQueue,                                   // 4
                     NULL                                             // 5
                     );
}

读取本地 PCM 文件

1、将 CustomAudioFileReader.h 、CustomAudioFileReader.m 和 CustomAudio48000_1.pcm 引入您自己的项目工程;
2、在合适的地方(如上述提到的onEnterRoom回调中)调用 start: 方法开始读取 PCM 数据;
3、遵循 CustomAudioFileReaderDelegate 代理,在代理方法 onAudioCapturePcm: 中调用 sendCustomAudioData: 向 SDK 投送音频数据。

@protocol CustomAudioFileReaderDelegate

- (void)onAudioCapturePcm:(NSData *)pcmData sampleRate:(int)sampleRate channels:(int)channels ts:(uint32_t)timestampMs;

@end

@interface CustomAudioFileReader : NSObject

@property(nonatomic, weak)id<CustomAudioFileReaderDelegate> delegate;

+ (instancetype) sharedInstance;

- (void)start:(int)sampleRate nChannels:(int)channels nSampleLen:(int)sampleLen;

- (void)stop;

@end
//开始读取本地PCM文件的音频数据
- (void)start:(int)sampleRate nChannels:(int)channels nSampleLen:(int)sampleLen {
    _sampleLen = sampleLen;

    NSString *resourcePath = [[NSBundle mainBundle] pathForResource:@"CustomAudio48000_1" ofType:@"pcm"];
    _fileData = [NSData dataWithContentsOfFile:resourcePath];

    dispatch_queue_t _unitQueue = dispatch_queue_create("audio_read_queue", DISPATCH_QUEUE_SERIAL);

    self.isStart = YES;
    WEAKIFY(self);
    dispatch_async(_unitQueue, ^{
        STRONGIFY_OR_RET(self);
        while (self.isStart) {
            struct timeval tv;
            gettimeofday(&tv,NULL);
            uint64_t currentTime = tv.tv_sec * 1000 + tv.tv_usec / 1000;
            if (self.delegate) {
                [self.delegate onAudioCapturePcm:[NSData dataWithBytes:self->_fileData.bytes+self->_fileDataReadLen length:self->_sampleLen] sampleRate:48000 channels:1 ts:(uint32_t)currentTime];
            }
            self->_fileDataReadLen += self->_sampleLen;
            if (self->_fileDataReadLen+self->_sampleLen > self->_fileData.length) {
                self->_fileDataReadLen = 0;
            }
            usleep(1000*20);
        }
        self->_fileData = nil;
    });
}

测试代码下载链接

常见问题

1、sendCustomAudioData 接口支持的音频数据格式有什么限制?
音频帧数据必须是 PCM 格式,采样率仅支持48000,声道支持单声道和双声道。

2、调用 sendCustomAudioData 自定义采集接口后,房间其它人听到的这个声音异常有声音重叠或者拉长现象?
调用该接口的频率推荐每帧20ms采样数,需要均匀的控制频率,否则会导致声音断断续续现象。另外,单声道的帧长度计算:48000 × 0.02s × 1 × 16bit = 15360bit = 1920字节,帧长度传的不对也会有上述现象。

3、调用自定义采集音频接口出现回声现象?
由于回声抵消(AEC)需要严格的控制声音采集和播放的时间,所以开启自定义音频采集后,AEC 能力可能会失效。


results matching ""

    No results matching ""