iOS

通过阅读本文,您可以了解 iOS 输入外部音视频流的方法。

输入外部视频流

说明

SDK允许先推流然后开启外部视频输入,但这种情况下,默认开始推流时,先推送出的是本地原始采集源(摄像头或屏幕捕获)的视频数据,直到启用外部输入。

1. 调用 setExternalVideoSource 打开外部视频输入开关。

int ret = [DingRtcClient.instance.rtcEngine setExternalVideoSource:YES track:DingRtcVideoTrackCamera];

2. 调用 pushExternalVideoFrame 输入视频数据。

说明
  • 此处需要重新开启线程调用 pushExternalVideoFrame。

FILE * _yuvInputFile;

@property (strong, nonatomic) NSThread *yuvInputThread;
@property (assign, nonatomic) int yuvDataWidth;
@property (assign, nonatomic) int yuvDataHeight;

- (void)runInputYUV {
    
    int width = self.yuvDataWidth;
    int height = self.yuvDataHeight;
    int dataSize = width * height * 3 / 2;
    
    char *yuv_read_data = (char *)malloc(dataSize);
    
    while (true) {
        
        if ([self.yuvInputThread isCancelled]) {
            break;
        }
        
        size_t read = fread(yuv_read_data, dataSize, 1, _yuvInputFile);
        
        if (read > dataSize) {
            break;
        }
        if (read == 0) {
            fseek(_yuvInputFile, 0, SEEK_SET); 
            if (self.yuvInputThread) {
                continue;
            } else {
                break;
            }
        }
        
        BOOL push_error = NO;
        if (![self.yuvInputThread isExecuting]) {
            push_error = YES;
            break;
        }
        
        DingRtcVideoFrame *frame = [[DingRtcVideoFrame alloc] init];
        frame.frameType = DingRtcVideoFrameRaw;
        frame.timestamp = [[NSDate date] timeIntervalSince1970] * 1000;
        frame.width = width;
        frame.height = height;
        frame.rotation = 0;
        frame.count = 3;
        frame.data = (void *)yuv_read_data;
        frame.offset = @[@(0), @(width * height), @(width * height * 5 / 4)];
        frame.stride = @[@(width), @(width/2), @(width/2)];
        frame.pixelFormat = DingRtcVideoI420;
        int ret = [DingRtcClient.instance.rtcEngine pushExternalVideoFrame:frame track:DingRtcVideoTrackCamera];
        if (ret != 0) {
            push_error = YES;
        }
        
        //请根据实际情况控制帧率
        [NSThread sleepForTimeInterval:0.03];
        
        if (push_error) {
            break;
        }
    }
    
    free(yuv_read_data);
    fclose(_yuvInputFile);
    _yuvInputFile = NULL;
}

输入外部音频流

1. 调用 setExternalAudioSource 启用外部音频输入。

//采样率
@property (assign, nonatomic) int sampleRate;

//声道数
@property (assign, nonatomic) int channels;

int ret = [DingRtcClient.instance.rtcEngine setExternalAudioSource:YES withSampleRate:self.sampleRate channelsPerFrame:self.channels];

2. 调用 pushExternalAudioFrame 输入音频数据。

FILE * _inputFile;

@property (strong, nonatomic) NSThread *inputThread;

//每个音频采样字节数, 通常是16bit(即2字节) */
@property (assign, nonatomic) int bytesPerSample;

//采样率
@property (assign, nonatomic) int sampleRate;

//声道数
@property (assign, nonatomic) int channels;

- (void)runInputPCM {
    
    int freq = 40; // audio duration (ms) each time push
    float samplesToRead = self.sampleRate / (1000.0 / freq);
    int bufferSize = samplesToRead * self.channels * self.bytesPerSample;
    
    char *buffer = (char *)malloc(bufferSize);
    
    DingRtcAudioDataSample *frame = [[DingRtcAudioDataSample alloc] init];
    frame.data = (void *)buffer;
    frame.bytesPerSample = self.bytesPerSample;
    frame.numOfChannels = self.channels;
    frame.sampleRate = self.sampleRate;
    
    while (true) {
        
        if ([self.inputThread isCancelled]) {
            break;
        }
        
        size_t readBytes = fread(buffer, 1, bufferSize, _inputFile);
    
        if (readBytes == 0) {
            fseek(_inputFile, 0, SEEK_SET);
            if (self.inputThread) {
                continue;
            } else {
                break;
            }
        }
        
        BOOL push_error = NO;
        if (![self.inputThread isExecuting]) {
            push_error = YES;
            break;
        }
        
        frame.numOfSamples = (int)(readBytes / self.bytesPerSample / selfchannels);
        int ret = [DingRtcClient.instance.rtcEngine pushExternalAudioFrame:frame];
        if (ret != 0) {
            push_error = YES;
        }
        
        //请根据实际情况控制帧率
        [NSThread sleepForTimeInterval:freq/1000.0];
        
        if (push_error) {
            break;
        }
    }
    
    free(buffer);
    fclose(_inputFile);
    _inputFile = NULL;
}