iOS上的设备的采集篇

作者: Link913 | 来源:发表于2016-12-22 15:19 被阅读386次

    前言

    如果只是单纯的想快速实现iOS端的采集,编码,推流,美颜,第三方框架或者商业SDK都能够快速实现,并且使用方便,易于上手,如果有什么不懂的地方问客服也基本上能够尽快的给你解决.由于公司需求的奇葩性我没办法使用市场上现有的轮子,只能自己去写.

    LFLiveKit

    这是我接触的第一个比较方便的框架吧,使用起来也很简单,这是框架在github上的地址:链接

    使用步骤

    • 1 pod 'LFLiveKit',注意这个框架内部是包含了GPUimage的,所以重复添加可能会产生一些问题,请妥善解决.

    • 2 添加以下依赖库

    使用范例

    我个人感觉作者readme写的还是比较简单的,我这里会详细的写写,我自己写的demo地址:链接,如果文章描述的不清楚大家可以看看具体demo.

    • 1首先懒加载一个LFLiveSession,这很重要,音视频的一些参数都是在这里配置的.默认参数都是配置好了的,大家点进去看就好了.
    -(LFLiveSession *)session{
            if (nil == _session) {
                /**
                 默认音频质量 audio sample rate: 44MHz(默认44.1Hz iphoneg6以上48Hz), audio bitrate: 64Kbps
                 分辨率: 540 *960 帧数:30 码率:800Kps
                 方向竖屏
                 */
                
                // 视频配置(质量 & 是否是横屏)可以点进去看
                _session = [[LFLiveSession alloc] initWithAudioConfiguration:[LFLiveAudioConfiguration defaultConfiguration] videoConfiguration:[LFLiveVideoConfiguration defaultConfigurationForQuality:LFLiveVideoQuality_Medium3 landscape:NO]];
                
                _session.delegate = self;
                _session.showDebugInfo = self;
                _session.preView = self.view;
            }
            return _session;
        }
    
    • 2 设置流信息

    这里的url我是这么使用的,使用nginx服务器, Mac上是很好配置的,然后下载VLC,这里的URL就是rtmp://(你的ip):1935/rtmplive/room,将他配置到VLC应该代码完成后就可以正常推流显示了.

            LFLiveStreamInfo * sterm = [LFLiveStreamInfo new];
            
            sterm.url = @"rtmp://192.168.1.42:1935/rtmplive/room";
            
            [self.session startLive:sterm];
    
    • 3 美颜开关一键设置

    具体的参数可以自己调,我个人觉得他的美颜效果还是不错的.

    设置self.session.beautyFace为Yes或者No
    
    • 4前后镜头反转

         AVCaptureDevicePosition devicePosition = self.session.captureDevicePosition;
        
        self.session.captureDevicePosition = (devicePosition == AVCaptureDevicePositionBack) ? AVCaptureDevicePositionFront : AVCaptureDevicePositionBack; 
      
    • 5闪光灯

    通过AVCaptureTorchMode这个类对其进行控制.

    • 6镜面翻转

      [self.session setMirror:YES];
      
    • 7请求授权

    iOS10中应该是要在plist文件中再加入一些键值对的.

    - (void)requestAccessForVideo {
            @weakify(self);
            AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
            switch (status) {
                case AVAuthorizationStatusNotDetermined: {
                    // 许可对话没有出现,发起授权许可
                    [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
                        if (granted) {
                            dispatch_async(dispatch_get_main_queue(), ^{
                                @strongify(self);
                                [self.session setRunning:YES];
                            });
                        }
                    }];
                    break;
                }
                case AVAuthorizationStatusAuthorized: {
                    // 已经开启授权,可继续
                    dispatch_async(dispatch_get_main_queue(), ^{
                        @strongify(self);
                        [self.session setRunning:YES];
                    });
                    break;
                }
                case AVAuthorizationStatusDenied:
                case AVAuthorizationStatusRestricted:
                    // 用户明确地拒绝授权,或者相机设备无法访问
                    
                    break;
                default:
                    break;
            }
        }
    
        - (void)requestAccessForAudio {
            AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
            switch (status) {
                case AVAuthorizationStatusNotDetermined: {
                    [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
                    }];
                    break;
                }
                case AVAuthorizationStatusAuthorized: {
                    break;
                }
                case AVAuthorizationStatusDenied:
                case AVAuthorizationStatusRestricted:
                    break;
                default:
                    break;
            }
        }
    
    • 8 监听推送状态的代理方法

      -(void)liveSession:(LFLiveSession *)session liveStateDidChange:(LFLiveState)state{
            self.liveStateLabel.textColor = [UIColor blackColor];
            switch (state) {
                case LFLiveReady:
                    NSLog(@"未连接");
                    self.liveStateLabel.text = @"未连接";
                    break;
                case LFLivePending:
                    NSLog(@"连接中");
                    [FYProgressHUD showWithMessage:@"正在连接..."];
                    self.liveStateLabel.text = @"正在连接...";
                    break;
                case LFLiveStart:
                    NSLog(@"已连接");
                    [FYProgressHUD showSuccess:@"连接成功"];
                    self.liveStateLabel.text = @"正在直播";
                    break;
                case LFLiveError:
                    NSLog(@"连接错误");
                    [FYProgressHUD showError:@"连接错误"];
                    self.liveStateLabel.text = @"未连接";
                    break;
                case LFLiveStop:
                    self.liveStateLabel.text = @"未连接";
                    NSLog(@"未连接");
                    break;
                default:
                    break;
            }
        }
      

    至此如果你只是想快速实现推流那么就已经结束了,可以不必看下文了.

    使用系统方法获取数据(不带美颜效果)

    一般情况下我们是不考虑这种方法的,因为当今直播市场非常火爆,如果你直接做一个不带美颜功能的,我想你这app估计很多主播都不好意思用吧,这里只是以介绍为主.

    • 1 创建音视频输入

      /**
         创建音视频输入
         */
        -(void)createCaptureDevice{
            
            //创建摄像头
            NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
            
            //初始化摄像头
            self.frontCamera = [AVCaptureDeviceInput deviceInputWithDevice:videoDevices.lastObject error:nil];
            self.backCamera = [AVCaptureDeviceInput deviceInputWithDevice:videoDevices.firstObject error:nil];
            
            //麦克风
            AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
            self.audioInputDevice = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:nil];
            
            //设置当前视频输入设备
            self.videoInputDevice = self.backCamera;
        }
      
    • 2 创建输出

    设置代理的时候注意遵守着两个协议

    AVCaptureVideoDataOutputSampleBufferDelegate
    AVCaptureAudioDataOutputSampleBufferDelegate
    

    这两个协议的代理方法是我们后面获取SampleBuffer的途径

        /**
         创建输出
         */
        -(void)createOutput{
            
            //创建队列
            dispatch_queue_t queue = dispatch_queue_create(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL);
            
            //创建视频输出数据
            self.videoDataOutput = [[AVCaptureVideoDataOutput alloc]init];
            
            //设置代理和队列
            [self.videoDataOutput setSampleBufferDelegate:self queue:queue];
            
            //抛弃过期帧,保证实时性
            self.videoDataOutput.alwaysDiscardsLateVideoFrames = YES;
            
            //设置参数
            NSDictionary * settings = @{(__bridge id)kCVPixelBufferPixelFormatTypeKey:@(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)};
            self.videoDataOutput.videoSettings = settings;
            
            //创建音频输出数据
            self.audioDataOutput = [[AVCaptureAudioDataOutput alloc]init];
            [self.audioDataOutput setSampleBufferDelegate:self queue:queue];
        }
    
    • 3 创建会话

      /**
         创建会话
         */
        -(void)createCaptureSession{
            
            self.captureSession = [[AVCaptureSession alloc]init];
            
            [self.captureSession beginConfiguration];
            
            //添加视频输入
            if ([self.captureSession canAddInput:self.videoInputDevice]) {
                [self.captureSession addInput:self.videoInputDevice];
            }
            
            //添加音频输入
            if ([self.captureSession canAddInput:self.audioInputDevice]) {
                [self.captureSession addInput:self.audioInputDevice];
            }
            
            //添加视频输出
            if([self.captureSession canAddOutput:self.videoDataOutput]){
                [self.captureSession addOutput:self.videoDataOutput];
        //        [self setVideoOutConfig];
            }
            
            //添加音频输出
            if([self.captureSession canAddOutput:self.audioDataOutput]){
                [self.captureSession addOutput:self.audioDataOutput];
            }
            
            //设置预览分辨率
            //这个分辨率有一个值得注意的点:
            //iphone4录制视频时 前置摄像头只能支持 480*640 后置摄像头不支持 540*960 但是支持 720*1280
            //诸如此类的限制,所以需要写一些对分辨率进行管理的代码。
            //目前的处理是,对于不支持的分辨率会抛出一个异常
            //但是这样做是不够、不完整的,最好的方案是,根据设备,提供不同的分辨率。
            //如果必须要用一个不支持的分辨率,那么需要根据需求对数据和预览进行裁剪,缩放。
            //设置采集质量
        //    if (![self.captureSession canSetSessionPreset:self.captureSessionPreset]) {
        //        @throw [NSException exceptionWithName:@"Not supported captureSessionPreset" reason:[NSString stringWithFormat:@"captureSessionPreset is [%@]", self.captureSessionPreset] userInfo:nil];
        //    }
            
            //设置采集质量
            self.captureSession.sessionPreset = AVCaptureSessionPresetHigh;//默认就为高
            
            //提交配置变更
            [self.captureSession commitConfiguration];
      
        }
      
    • 4 创建预览

      /**
         创建预览
         */
        -(void)createPreviewLayer{
            
            self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
            
            self.previewLayer.frame = self.view.frame;
            
            //保留纵横比
            self.previewLayer.videoGravity = AVVideoScalingModeResizeAspectFill;
            
            [self.view.layer addSublayer:self.previewLayer];
            
        }
      
    • 5 退出销毁的时候

      if (self.captureSession) {
        [self.captureSession removeInput:self.audioInputDevice];
        [self.captureSession removeInput:self.videoInputDevice];
        [self.captureSession removeOutput:self.self.videoDataOutput];
        [self.captureSession removeOutput:self.self.audioDataOutput];
      }
      
      [self.captureSession stopRunning];
      self.captureSession = nil;
      
    • 6 获取数据的地方

    在这个代理方法里是获取音视频数据的地方,这里是我们对采集到的原始数据进行编码的地方,后面会讲到如何去编码,这个方法是能够区分音频和视频信息的.

    -(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection;
    

    使用GPUimage

    -(void)onInit{
            //摄像头初始化
            // AWGPUImageVideoCamera 继承自 GPUImageVideoCamera。继承是为了获取音频数据,原代码中,默认情况下音频数据发送给了 audioEncodingTarget。
            // 这个东西一看类型是GPUImageMovieWriter,应该是文件写入功能。果断覆盖掉processAudioSampleBuffer方法,拿到音频数据后自己处理。
            // 音频就这样可以了,GPUImage主要工作还是在视频处理这里。
            // 设置预览分辨率 self.captureSessionPreset是根据AWVideoConfig的设置,获取的分辨率。设置前置、后置摄像头。
            _videoCamera = [[AWGPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPresetHigh cameraPosition:AVCaptureDevicePositionFront];
            
            //开启捕获声音
            [_videoCamera addAudioInputsAndOutputs];
            
            //设置输出图像方向,可用于横屏推流。
            _videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
            
            //镜像策略,这里这样设置是最自然的。跟系统相机默认一样。
            _videoCamera.horizontallyMirrorRearFacingCamera = NO;
            _videoCamera.horizontallyMirrorFrontFacingCamera = YES;
            
            //设置预览view
            _gpuImageView = [[GPUImageView alloc] initWithFrame:self.view.bounds];
            [self.view addSubview:_gpuImageView];
            
            //初始化美颜滤镜
            _beautifyFilter = [[GPUImageBeautifyFilter alloc] init];
            
            //相机获取视频数据输出至美颜滤镜
            [_videoCamera addTarget:_beautifyFilter];
            
            //美颜后输出至预览
            [_beautifyFilter addTarget:_gpuImageView];
            
            // 到这里我们已经能够打开相机并预览了。
            // 因为要推流,除了预览之外,我们还要截取到视频数据。这就需要使用GPUImage中的GPUImageRawDataOutput,它能将美颜后的数据输出,便于我们处理后发送出去。
            // AWGPUImageAVCaptureDataHandler继承自GPUImageRawDataOutput,从 newFrameReadyAtTime 方法中就可以获取到美颜后输出的数据。
            // 输出的图片格式为BGRA。
            _dataHandler = [[AWGPUImageAVCaptureDataHandler alloc]initWithImageSize:CGSizeMake(self.view.frame.size.width, self.view.frame.size.height) resultsInBGRAFormat:YES ];
            [_beautifyFilter addTarget:_dataHandler];
            
            // 令AWGPUImageAVCaptureDataHandler实现AWGPUImageVideoCameraDelegate协议,并且让camera的awAudioDelegate指向_dataHandler对象。
            // 将音频数据转到_dataHandler中处理。然后音视频数据就可以都在_dataHandler中处理了。
            _videoCamera.awAudioDelegate = _dataHandler;
            
            //开始捕获视频
            [self.videoCamera startCameraCapture];
            
        }
    

    相关文章

      网友评论

      • 75bf442e0838:你好,LFLiveKit要像ijkplayery一样生成framework吗,还是直接导入LFLiveKit再添加依赖库就可以了
        Link913:@小强神坑之神 大神谈不上,不会的可以多交流
        75bf442e0838:@SkyHarute 我一般不用pod,我先试试手动集成,不懂再请教大神
        Link913:@小强神坑之神 直接pod就可以

      本文标题:iOS上的设备的采集篇

      本文链接:https://www.haomeiwen.com/subject/twhcuttx.html