基于iOS的网络音视频实时传输系统(二)- 捕获音视频数据

下载


GitHub:

client 端:https://github.com/AmoAmoAmo/Smart_Device_Client

server端:https://github.com/AmoAmoAmo/Smart_Device_Server

另还写了一份macOS版的server,但是目前还有一些问题,有兴趣的去看看吧:https://github.com/AmoAmoAmo/Server_Mac



获取音视频数据,这里使用的是AVCaptureSession,可以方便地自定义相机界面,看起来大概是这样的:


关于AVCaptureSession的使用步骤这里不赘述,网上一搜一大把,无非就是设置输入输出对象,设置代理、预览层之类。

这里直接上代码了,具体注释都有哦。


过程


初始化session

    self.avSession = [[AVCaptureSession alloc] init];
    self.avSession.sessionPreset = AVCaptureSessionPreset640x480;
     
    // 设备对象 (audio)
    AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
    // 输入流
    AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:nil];
    // 输出流
    AVCaptureAudioDataOutput *audioOutput = [[AVCaptureAudioDataOutput alloc] init];
    [audioOutput setSampleBufferDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
    // 添加输入输出流
    if ([_avSession canAddInput:audioInput]) {
        [_avSession addInput:audioInput];
    }
    if ([_avSession canAddOutput:audioOutput]) {
        [_avSession addOutput:audioOutput];
    }
    
    
    // 设备对象 (video)
    AVCaptureDevice *inputCamera = nil;
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    for (AVCaptureDevice *device in devices)
    {
        if ([device position] == AVCaptureDevicePositionBack)
        {
            inputCamera = device;
        }
    }
    
    AVCaptureDeviceInput *videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:inputCamera error:nil];
    
    if ([self.avSession canAddInput:videoInput]) {
        [self.avSession addInput:videoInput];
    }
    
    self.videoOutput = [[AVCaptureVideoDataOutput alloc] init];
    [self.videoOutput setAlwaysDiscardsLateVideoFrames:NO];  // 是否抛弃延迟的帧:NO
    
    [self.videoOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];
    [self.videoOutput setSampleBufferDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
    if ([self.avSession canAddOutput:self.videoOutput]) {
        [self.avSession addOutput:self.videoOutput];
    }
    AVCaptureConnection *connection = [self.videoOutput connectionWithMediaType:AVMediaTypeVideo];
    [connection setVideoOrientation:AVCaptureVideoOrientationLandscapeRight]; // 因为要横屏,所以让输出视频图像旋转90°
    
    self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.avSession];
    [self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    [[self.previewLayer connection] setVideoOrientation:AVCaptureVideoOrientationLandscapeRight];// 预览层也让它右转90°
    [self.previewLayer setFrame:self.captureView.bounds];
    [self.captureView.layer insertSublayer:self.previewLayer above:0];


AVCapture输出流代理

设备捕获到的音视频数据都在这里取得,然后进行编码等操作。

// 默认情况下,为30 fps,意味着该函数每秒调用30次
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
    // 获取输入设备数据,有可能是音频有可能是视频
    if (captureOutput == self.videoOutput) {

        // 收到数据,开始编码
        [self.videoEncoder startH264EncodeWithSampleBuffer:sampleBuffer andReturnData:^(NSData *data) {

            // 当TCP需要开始传输数据时,开始传编码后的数据
            if (self.isReadyToEncode) {
                // 返回一个编码后的数据 data,传给TCP 开始发送给client
                [self.tcpServer sendVideoDataToClientWithData:data];
            }
        }];
     
    }
    else
    {
        // 音频
        dispatch_sync(mEncodeQueue, ^{
            // block回调 返回编码后的音频数据
//            printf("----- audio -------\n");
            [self.audioEncoder encodeSampleBuffer:sampleBuffer completionBlock:^(NSData *encodedData, NSError *error) {
                printf("----- Audio encodedData length = %d ----- \n", (int)[encodedData length]);
                // 写入socket
                if (self.isReadyToEncode) {
                    [self.tcpServer sendAudioDataToClientWithData:encodedData];
                }
            }];
        });
        
    }
}



问题记录

server端使用VideoToolbox进行视频编码时,发现在client端解析出来的视频画面方向发生了旋转,client端如下所示:

首先排除了client端接收数据或硬解码的过程中的问题,因为把server端编码后的数据使用NSFileHandle写入文件,用VLC播放的情况与上图所示一致。

首先,判断源及目标图像是否翻转:

BOOL isFlipped = CVImageBufferIsFlipped(pixelBuffer);
NSLog(@"pixelBuffer is %s", isFlipped ? "flipped" : "not flipped");
打印结果如下:

pixelBuffer is flipped
发现在原始数据中,图像都是翻转的

找了好久,最后发现在server端设置捕获视频的output的地方把它旋转(左右横屏):

AVCaptureConnection *connection = [self.videoOutput connectionWithMediaType:AVMediaTypeVideo];
[connection setVideoOrientation:AVCaptureVideoOrientationLandscapeRight]; // 因为要横屏,所以让输出视频图像旋转90°

运行一下:

问题就这么解决啦



相关文章


基于iOS的网络音视频实时传输系统(一)- 前言

基于iOS的网络音视频实时传输系统(二)- 捕获音视频数据

基于iOS的网络音视频实时传输系统(三)- VideoToolbox编码音视频数据为H264、AAC

基于iOS的网络音视频实时传输系统(四)- 自定义socket协议(TCP、UDP)

基于iOS的网络音视频实时传输系统(五)- 使用VideoToolbox硬解码H264

基于iOS的网络音视频实时传输系统(六)- AudioQueue播放音频,OpenGL渲染显示图像


猜你喜欢

转载自blog.csdn.net/a997013919/article/details/78089240