iOS 录制视频

作者: Abson在简书 | 来源:发表于2015-11-25 00:41 被阅读11710次

    最近开发中遇到一个需求,就是想微信那样录制一个小视频,然后在录制视频的图层上播放,然后发布到朋友圈,无声播放,但有滚动起来不影响性能。一开始接到这个需求的时候我是很兴奋的,可以好好研究一番 AVFoundation 的东西了。但是在研究中不断的高潮迭起,也是让我心力交瘁呀。但是,做程序猿的成长就是这样的嘛。题外话了,好了,今天我们就说一下怎么用 AVCaptureSession + __ AVCaptureMovieFileOutput__ 来录制视频,并通过AVAssetExportSeeion 手段来压缩视频并转换为 MP4 格式

    一开始我们要了解一下 AVFoundation 做视频的类应该有那些,并且他们有什么用呢?

    AVCaptureSession

     AVCaptureSession:媒体(音、视频)捕获会话,负责把捕获的音视频数据输出到输出设备中。一个AVCaptureSession可以有多个输入输出。
     AVCaptureDevice :输入设备,包括麦克风、摄像头,通过该对象可以设置物理设备的一些属性(例如相机聚焦、白平衡等)。
     AVCaptureDeviceInput :设备输入数据管理对象,可以根据AVCaptureDevice创建对应的AVCaptureDeviceInput对象,该对象将会被添加到AVCaptureSession中管理。
     AVCaptureVideoPreviewLayer :相机拍摄预览图层,是CALayer的子类,使用该对象可以实时查看拍照或视频录制效果,创建该对象需要指定对应的 AVCaptureSession对象。
    
     AVCaptureOutput :输出数据管理对象,用于接收各类输出数据,通常使用对应的子类AVCaptureAudioDataOutput、AVCaptureStillImageOutput、
     AVCaptureVideoDataOutput、AVCaptureFileOutput, 该对象将会被添加到AVCaptureSession中管理。
     注意:前面几个对象的输出数据都是NSData类型,而AVCaptureFileOutput代表数据以文件形式输出,类似的,AVCcaptureFileOutput也不会直接创建使用,通常会使用其子类:
     AVCaptureAudioFileOutput、AVCaptureMovieFileOutput。当把一个输入或者输出添加到AVCaptureSession之后AVCaptureSession就会在所有相符的输入、输出设备之间
     建立连接(AVCaptionConnection)。
    

    那么建立视频拍摄的步骤如下 :
    1.创建AVCaptureSession对象。

    // 创建会话 (AVCaptureSession) 对象。
    _captureSession = [[AVCaptureSession alloc] init];
    if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset640x480]) {
        // 设置会话的 sessionPreset 属性, 这个属性影响视频的分辨率
        [_captureSession setSessionPreset:AVCaptureSessionPreset640x480];
    }
    

    2.使用AVCaptureDevice的静态方法获得需要使用的设备,例如拍照和录像就需要获得摄像头设备,录音就要获得麦克风设备。

    // 获取摄像头输入设备, 创建 AVCaptureDeviceInput 对象
    // 在获取摄像头的时候,摄像头分为前后摄像头,我们创建了一个方法通过用摄像头的位置来获取摄像头 
    AVCaptureDevice *videoCaptureDevice = [self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];
    if (!captureDevice) {
        NSLog(@"---- 取得后置摄像头时出现问题---- ");
        return;
    }
    
    // 添加一个音频输入设备
    // 直接可以拿数组中的数组中的第一个
    AVCaptureDevice *audioCaptureDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];
    

    3.利用输入设备AVCaptureDevice初始化AVCaptureDeviceInput对象。

    // 视频输入对象
    // 根据输入设备初始化输入对象,用户获取输入数据
    _videoCaptureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:captureDevice error:&error];
    if (error) {
        NSLog(@"---- 取得设备输入对象时出错 ------ %@",error);
        return;
    } 
    
    //  音频输入对象
    //根据输入设备初始化设备输入对象,用于获得输入数据
    _audioCaptureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:audioCaptureDevice error:&error];
    if (error) {
        NSLog(@"取得设备输入对象时出错 ------ %@",error);
        return;
    }
    

    4.初始化输出数据管理对象,如果要拍照就初始化AVCaptureStillImageOutput对象;如果拍摄视频就初始化AVCaptureMovieFileOutput对象。

    // 拍摄视频输出对象
    // 初始化输出设备对象,用户获取输出数据
    _caputureMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
    

    5.将数据输入对象AVCaptureDeviceInput、数据输出对象AVCaptureOutput添加到媒体会话管理对象AVCaptureSession中。

    // 将视频输入对象添加到会话 (AVCaptureSession) 中
    if ([_captureSession canAddInput:_videoCaptureDeviceInput]) {
        [_captureSession addInput:_videoCaptureDeviceInput];
    }
    
    // 将音频输入对象添加到会话 (AVCaptureSession) 中
    if ([_captureSession canAddInput:_captureDeviceInput]) {
        [_captureSession addInput:audioCaptureDeviceInput];
        AVCaptureConnection *captureConnection = [_caputureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];
        // 标识视频录入时稳定音频流的接受,我们这里设置为自动
        if ([captureConnection isVideoStabilizationSupported]) {
            captureConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
        }
    }
    

    6.创建视频预览图层AVCaptureVideoPreviewLayer并指定媒体会话,添加图层到显示容器中,调用AVCaptureSession的startRuning方法开始捕获。

    // 通过会话 (AVCaptureSession) 创建预览层
    _captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
    
    // 显示在视图表面的图层
    CALayer *layer = self.viewContrain.layer;
    layer.masksToBounds = true;
    
    _captureVideoPreviewLayer.frame = layer.bounds;
    _captureVideoPreviewLayer.masksToBounds = true;
    _captureVideoPreviewLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//填充模式
    [layer addSublayer:_captureVideoPreviewLayer];
    
    // 让会话(AVCaptureSession)勾搭好输入输出,然后把视图渲染到预览层上
    [_captureSession startRunning];
    

    7.将捕获的音频或视频数据输出到指定文件。

    创建一个拍摄的按钮,当我们点击这个按钮就会触发视频录制,并将这个录制的视频放到 temp 文件夹中
    
    - (IBAction)takeMovie:(id)sender {
    [(UIButton *)sender setSelected:![(UIButton *)sender isSelected]];
    if ([(UIButton *)sender isSelected]) {
         AVCaptureConnection *captureConnection=[self.caputureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];
        // 开启视频防抖模式
        AVCaptureVideoStabilizationMode stabilizationMode = AVCaptureVideoStabilizationModeCinematic;
        if ([self.captureDeviceInput.device.activeFormat isVideoStabilizationModeSupported:stabilizationMode]) {
            [captureConnection setPreferredVideoStabilizationMode:stabilizationMode];
        }
        
        //如果支持多任务则则开始多任务
        if ([[UIDevice currentDevice] isMultitaskingSupported]) {
           self.backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
        }
        // 预览图层和视频方向保持一致,这个属性设置很重要,如果不设置,那么出来的视频图像可以是倒向左边的。
        captureConnection.videoOrientation=[self.captureVideoPreviewLayer connection].videoOrientation;
        
       // 设置视频输出的文件路径,这里设置为 temp 文件
        NSString *outputFielPath=[NSTemporaryDirectory() stringByAppendingString:MOVIEPATH];
        
        // 路径转换成 URL 要用这个方法,用 NSBundle 方法转换成 URL 的话可能会出现读取不到路径的错误
        NSURL *fileUrl=[NSURL fileURLWithPath:outputFielPath];
        
       // 往路径的 URL 开始写入录像 Buffer ,边录边写
        [self.caputureMovieFileOutput startRecordingToOutputFileURL:fileUrl recordingDelegate:self];
    }
    else {
       // 取消视频拍摄
        [self.caputureMovieFileOutput stopRecording];
        [self.captureSession stopRunning];
        [self completeHandle];
    }
    }
    

    当然我们录制的开始与结束都是有监听方法的,AVCaptureFileOutputRecordingDelegate 这个代理里面就有我们想要做的

    - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections
    {
       NSLog(@"---- 开始录制 ----");
    }
    
    - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
    {
        NSLog(@"---- 录制结束 ----");
    }
    

    到此,我们录制视频就结束了,那么是不是我们录制好了视频,就可以马上把这个视频上传给服务器分享给你的小伙伴们看了呢?
    我们可以用如下方法测试一下我们录制出来的视频有多大 (m)
    - (CGFloat)getfileSize:(NSString *)path
    {
    NSDictionary *outputFileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
    NSLog (@"file size: %f", (unsigned long long)[outputFileAttributes fileSize]/1024.00 /1024.00);
    return (CGFloat)[outputFileAttributes fileSize]/1024.00 /1024.00;
    }
    个人在这里做过测试,录制了 10s 的小视频得到的文件大小为 4.1M 左右,而且我用的分辨率还是640x480。。。很无语了是不是?
    如果我们录制的视频,录制完成后要与服务器进行必要的上传,那么,我们肯定不能把这个刚刚录制出来的视频上传给服务器的,我们有必要对这个视频进行压缩了。那么我们的压缩方法,就要用到 AVAssetExportSeeion 这个类了。

    // 这里我们创建一个按钮,当点击这个按钮,我们就会调用压缩视频的方法,然后再去重新计算大小,这样就会跟未被压缩前的大小有个明显的对比了
    
    
    // 压缩视频
    - (IBAction)compressVideo:(id)sender
    {
        NSString *cachePath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
        NSString *savePath=[cachePath stringByAppendingPathComponent:MOVIEPATH];
        NSURL *saveUrl=[NSURL fileURLWithPath:savePath];
    
        // 通过文件的 url 获取到这个文件的资源
        AVURLAsset *avAsset = [[AVURLAsset alloc] initWithURL:saveUrl options:nil];
        // 用 AVAssetExportSession 这个类来导出资源中的属性
        NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
    
        // 压缩视频
        if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality]) { // 导出属性是否包含低分辨率
        // 通过资源(AVURLAsset)来定义 AVAssetExportSession,得到资源属性来重新打包资源 (AVURLAsset, 将某一些属性重新定义
        AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:AVAssetExportPresetLowQuality];
        // 设置导出文件的存放路径
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"yyyy-MM-dd-HH:mm:ss"];
        NSDate    *date = [[NSDate alloc] init];
        NSString *outPutPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true) lastObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"output-%@.mp4",[formatter stringFromDate:date]]];
        exportSession.outputURL = [NSURL fileURLWithPath:outPutPath];
        
        // 是否对网络进行优化
        exportSession.shouldOptimizeForNetworkUse = true;
        
        // 转换成MP4格式
        exportSession.outputFileType = AVFileTypeMPEG4;
        
        // 开始导出,导出后执行完成的block
        [exportSession exportAsynchronouslyWithCompletionHandler:^{
            // 如果导出的状态为完成
            if ([exportSession status] == AVAssetExportSessionStatusCompleted) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    // 更新一下显示包的大小
                    self.videoSize.text = [NSString stringWithFormat:@"%f MB",[self getfileSize:outPutPath]];
                });
            }
        }];
    }
    }
    

    经过我们的压缩,这个时候10s 的 4M 视频就只剩下不够 1M 了。


    以下是一些扩展

    自动闪光灯开启

    - (IBAction)flashAutoClick:(UIButton *)sender {
        [self setFlashMode:AVCaptureFlashModeAuto];
        [self setFlashModeButtonStatus];
    }
    

    打开闪光灯

    - (IBAction)flashOnClick:(UIButton *)sender {
        [self setFlashMode:AVCaptureFlashModeOn];
        [self setFlashModeButtonStatus];
    }
    

    关闭闪光灯

    - (IBAction)flashOffClick:(UIButton *)sender {
        [self setFlashMode:AVCaptureFlashModeOff];
        [self setFlashModeButtonStatus];
    }
    

    通知

    /**
     *  给输入设备添加通知
     */
    -(void)addNotificationToCaptureDevice:(AVCaptureDevice *)captureDevice{
    //注意添加区域改变捕获通知必须首先设置设备允许捕获
    [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
        captureDevice.subjectAreaChangeMonitoringEnabled=YES;
    }];
    NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
    //捕获区域发生改变
    [notificationCenter addObserver:self selector:@selector(areaChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
    }
    -(void)removeNotificationFromCaptureDevice:(AVCaptureDevice *)captureDevice{
    NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
    [notificationCenter removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
    }
    /**
     *  移除所有通知
     */
    -(void)removeNotification{
        NSNotificationCenter *notificationCenter= [NSNotificationCenter     defaultCenter];
        [notificationCenter removeObserver:self];
    }
    
    -(void)addNotificationToCaptureSession:(AVCaptureSession *)captureSession{
    NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
    //会话出错
    [notificationCenter addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:captureSession];
    }
    
    /**
     *  设备连接成功
     *
     *  @param notification 通知对象
     */
    -(void)deviceConnected:(NSNotification *)notification{
        NSLog(@"设备已连接...");
    }
    /**
     *  设备连接断开
     *
     *  @param notification 通知对象
     */
    -(void)deviceDisconnected:(NSNotification *)notification{
    NSLog(@"设备已断开.");
    }
    /**
     *  捕获区域改变
     *
     *  @param notification 通知对象
     */
    -(void)areaChange:(NSNotification *)notification{
        NSLog(@"捕获区域改变...");
    }
    
    /**
     *  会话出错
     *
     *  @param notification 通知对象
     */
    -(void)sessionRuntimeError:(NSNotification *)notification{
    NSLog(@"会话发生错误.");
    

    }

    私有方法

    /**
     *  取得指定位置的摄像头
     *
     *  @param position 摄像头位置
     *
     *  @return 摄像头设备
     */
    -(AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition )position{
        NSArray *cameras= [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
        for (AVCaptureDevice *camera in cameras) {
            if ([camera position]==position) {
                return camera;
            }
        }
        return nil;
    }
    
    /**
     *  改变设备属性的统一操作方法
     *
     *  @param propertyChange 属性改变操作
     */
    -(void)changeDeviceProperty:(PropertyChangeBlock)propertyChange{
        AVCaptureDevice *captureDevice= [self.captureDeviceInput device];
        NSError *error;
        //注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁
        if ([captureDevice lockForConfiguration:&error]) {
            propertyChange(captureDevice);
            [captureDevice unlockForConfiguration];
        }else{
            NSLog(@"设置设备属性过程发生错误,错误信息:%@",error.localizedDescription);
        }
    }
    
    /**
     *  设置闪光灯模式
     *
     *  @param flashMode 闪光灯模式
     */
    -(void)setFlashMode:(AVCaptureFlashMode )flashMode{
        [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
            if ([captureDevice isFlashModeSupported:flashMode]) {
                [captureDevice setFlashMode:flashMode];
            }
        }];
    }
    /**
     *  设置聚焦模式
     *
     *  @param focusMode 聚焦模式
     */
    -(void)setFocusMode:(AVCaptureFocusMode )focusMode{
        [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
            if ([captureDevice isFocusModeSupported:focusMode]) {
                [captureDevice setFocusMode:focusMode];
            }
        }];
    }
    /**
     *  设置曝光模式
     *
     *  @param exposureMode 曝光模式
     */
    -(void)setExposureMode:(AVCaptureExposureMode)exposureMode{
        [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
            if ([captureDevice isExposureModeSupported:exposureMode]) {
                [captureDevice setExposureMode:exposureMode];
            }
        }];
    }
    
    /**
     *  设置聚焦点
     *
     *  @param point 聚焦点
     */
    -(void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point{
        [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
            if ([captureDevice isFocusModeSupported:focusMode]) {
                [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
            }
            if ([captureDevice isFocusPointOfInterestSupported]) {
                [captureDevice setFocusPointOfInterest:point];
            }
            if ([captureDevice isExposureModeSupported:exposureMode]) {
                [captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];
            }
            if ([captureDevice isExposurePointOfInterestSupported]) {
                [captureDevice setExposurePointOfInterest:point];
            }
        }];
    }
    
    /**
     *  添加点按手势,点按时聚焦
     */
    -(void)addGenstureRecognizer{
    UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapScreen:)];
        [self.viewContainer addGestureRecognizer:tapGesture];
    }
    -(void)tapScreen:(UITapGestureRecognizer *)tapGesture{
        CGPoint point= [tapGesture locationInView:self.viewContainer];
        //将UI坐标转化为摄像头坐标
        CGPoint cameraPoint= [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];
        [self setFocusCursorWithPoint:point];
        [self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint];
    }
    
    /**
     *  设置闪光灯按钮状态
     */
    -(void)setFlashModeButtonStatus{
        AVCaptureDevice *captureDevice=[self.captureDeviceInput device];
        AVCaptureFlashMode flashMode=captureDevice.flashMode;
        if([captureDevice isFlashAvailable]){
            self.flashAutoButton.hidden=NO;
        self.flashOnButton.hidden=NO;
        self.flashOffButton.hidden=NO;
        self.flashAutoButton.enabled=YES;
        self.flashOnButton.enabled=YES;
        self.flashOffButton.enabled=YES;
        switch (flashMode) {
            case AVCaptureFlashModeAuto:
                self.flashAutoButton.enabled=NO;
                break;
            case AVCaptureFlashModeOn:
                self.flashOnButton.enabled=NO;
                break;
            case AVCaptureFlashModeOff:
                self.flashOffButton.enabled=NO;
                break;
            default:
                break;
        }
    }else{
        self.flashAutoButton.hidden=YES;
        self.flashOnButton.hidden=YES;
        self.flashOffButton.hidden=YES;
    }
    }
    
    /**
     *  设置聚焦光标位置
     *
     *  @param point 光标位置
     */
    -(void)setFocusCursorWithPoint:(CGPoint)point{
        self.focusCursor.center=point;
        self.focusCursor.transform=CGAffineTransformMakeScale(1.5, 1.5);
        self.focusCursor.alpha=1.0;
        [UIView animateWithDuration:1.0 animations:^{
            self.focusCursor.transform=CGAffineTransformIdentity;
        } completion:^(BOOL finished) {
            self.focusCursor.alpha=0;
        
        }];
    }
    

    @end

    心如止水,奋力前行

    相关文章

      网友评论

      • MissLu16:同求demo.(1922562135@qq.com)
      • GG266:前置摄像头拍摄完成会有镜面效果,为前置摄像头的connect设置了videoMirrored为yes,videoOrientation为Portrait也没有达到所预想的效果。所以,想请问博主有没有解决这个问题,或者所大家一起讨论一下这个问题
      • 西风颂:亲,麻烦问一下就是横屏录制的视频在播放的时候为什么是竖屏播放呢?怎么才能设置为横屏播放呢?
      • Society2012:录制的视频和音频是分开的吗?
      • zhaihongxia:请问,这样写录制视频时会不会手机发热
      • 妹子爱编程:AVCaptureConnection *captureConnection = [_caputureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];
        // 标识视频录入时稳定音频流的接受,我们这里设置为自动
        if ([captureConnection isVideoStabilizationSupported]) {
        captureConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
        }

        大神我用swift 重写你的这个代码,为啥运行的时候这里总是崩溃
      • xclidongbo:怎么自定义视频的尺寸? 比如16:9 大小的.
      • KeepStrong:求楼主一份demo,邮箱1301808418@qq.com
      • zero000:感谢分享,求demo 1915853255@qq.com
      • 232ae72d57c9:发下源码谢谢啦:pray:lynne0522@163.com
      • 来宝:大神,demo 460876003@qq.com
      • 277ccd5ab3f3:这样录制的视频格式是什么呢
      • LV大树:弱弱问一下留邮箱会有DEMO吗?
      • LV大树:好人一生苹果平安。
      • 安静野心家:录像写入时写到temp目录,而压缩成MP4时却是从document目录取出来压缩,逗我呢???而且添加输出设备也没写,if([_session canAddOutput:_output]){
        [_session addOutput:_output];
        }
        安静野心家:@葱神大大 你明白我说的什么吗?我哪里表达了我不明白压缩视频的用意?我说的是你这篇文章的内容,录像写入在A目录,然后压缩时不应该从A目录取出来吗?而你是从B目录取出来。那么问题来了,你录的视频是怎么从A目录到B目录的?
        Abson在简书:@安静野心家 压缩只是为了上传文件体积减少,自己观看的是高清的视频,明白么,跟图片是同一个道理
      • 常绿箩:写的很不错,请问可以给下demo吗,1944606304@qq.com
      • 柯柯格格:一看就知道,从网上copy过来的,九成以上可能没有自己敲一下,有些通知都已经废弃了,根本不能用,哎
        Abson在简书:@柯柯格格 我在项目当中还用着呢!心浮气躁之人,将项目得东西分享出来了,不懂感恩就算了,还指望别人帮你写完,写好?那你留美一点自学能力?靠什么在这个行业立足呢?
        柯柯格格:@葱神大大 你就说你自己测试了没就可以了,如果测试了,我给你道歉
        Abson在简书:@柯柯格格 是吧,别人的成果你还诋毁,不知所谓,不跟你这种人一般见识
      • 凯文Kevin21:大神,可以把demo放到github上去啊。开源大法好啊,java保平安。
      • AKinCoder:640*480 视频预览是正方形的,拍摄完成后又称3:4了,怎么破?
      • Y像梦一样自由:大神,同求demo 邮箱376113892@qq.com
      • 经文纬武:大神能发个demo吗 2834384208@qq.com
      • 丶逝水流年:大神,求个demo 905736705@qq.com
      • ea5b2dd6cdd0:求一份Demo大神,825797402@qq.com
      • 经文纬武:求大神发个演示。现在公司要求做个小​​视频上传功能,急,拜托了。 28343384208 @ qq.com
      • ef60b7f382dc:10829580@qq.com 朋友,请发一份demo,感谢。
      • 原味豆浆:大神,求demo(495419587@qq.com)
      • 天亮説晚安:请教一个问题,发现视频播放时候的比例一直是3:4,如何修改这个比例呢?或者如何调整视频播放时的大小呢?
      • Jennifer_鑫:大神,为什么程序启动的时候,屏幕会先黑一下?
      • SAW_: 请教个问题,在tableView上,没当cell出现的时候,尽心赋值,给avplayer设置AVPlayerItem的时候,总是卡顿,视频一多,就卡的不要不要的,有什么思路吗?
        文兴:如果怕滚动的时候,视频是黑的,可以用一个imageview覆盖,播放的时候隐藏就行
        文兴:@SAW 监听srollviewdidend事件,滚动结束才设置playeritem
      • Peak_One:大神求 demo lxh_ios@126.com
      • 72b1e4f01ff8:求demo,大神,muyunli627@163.com
      • 8bad0e5aeda6:求demo youlong118@qq.com
      • 18d8bae340ec:大神求下demo 1443466376@qq.com
      • style_施:怎么获取你录制视频的路径 然后上传呢?
      • RYC:求 DeMO A 787725121@qq.com 大神
      • 6dbbc3992d92:mark 求教一下 ,如何录制双人视频的画面,我无法获取另一个人的视频源 ,所以我暂时只能录制自己的画面,也就是自己的视频源,这个你有涉及到么,求指教。谢谢
      • 桐生一猫:mark 一下 ,以后要压缩和转mp4格式 :+1:
      • zl520k:你好,能发一个demo给我吗?我的邮箱是373907050@qq.com,谢谢
      • 萧城x:大神 求demo 邮箱 asd3306642@qq.com
      • zhao1zhihui:求个demo大神1217212724@qq.com 谢谢!!
      • 1525defe0eda:1250253467@qq.com谢谢!!!
      • puppySweet:你怎么还没有更新啊……
      • O_Mys:如果使用那些私有的方法,是不是会导致APP审核通不过?
        前行哲:@O_Mys ...
        O_Mys:@止于浮水 取得指定位置的摄像头以及下面的方法,你不是写着是私有方法吗?
        Abson在简书:@O_Mys 这里并没有私有方法喔
      • 13ba66721fb5:大神,可以发个Demo吗? 1574463301@qq.com
      • 5aa454cbdc88:你压缩成lowquaitty 根本看不清楚啊 谁有办法大神们 这里的压缩还是不行啊
        Abson在简书:@puppySweet 这个比较复杂,就这么打几行字无法说明,而且 ffmpeg 这个库比较庞大,接下来我会写一写博客慢慢说明
        puppySweet:@止于浮水 ffmpeg怎么编译成h265
        Abson在简书:@WX了个g 那就用 ffmepg 吧,自己去编译一下,把包集成过去,编译成 h264就可以了
      • 欧阳蓝缺:代码这里有 https://github.com/AndyFightting/VideoRecord
        欧阳蓝缺:@puppySweet 没有。上面的代码也是我无意中找到的。只能再找找。
        puppySweet:@欧阳蓝缺 有没有 讲如何设置压缩比率 比特率 帧数 尺寸的demo AVsset搞了一周还是搞不定疯了哎
      • bellchen:求demo 1010542165@qq.com
      • 1bfe74af6929:798009633@qq.com,谢谢老板
      • 27ab7fcb648a:你好 方便发一份Demo吗 ? ( 695587725@qq.com ) 谢谢。
      • 权仔:大神,最好贴出源码git地址比较方便 也想学习下,发个demo吧 1551205489@qq.com ,谢谢
      • dengxf:求相关系列demo学习一下,之前对这边了解的不多
        dxf1125@126.com 3q.
      • ryugaku:马克
      • 166ad58f7be3:大神 发个demo吧 10543398282qq.com
      • Se7ven:大神,跪求发个demo,谢谢!Q2502305577
      • 946e6dad0540:大牛求demo 494969990@qq.com
      • 8a3b2566b8ca:self.viewContrain指的是什么啊
      • 90a272ed37b2:可以的话希望大神贴下demo,最近也对这个很有兴趣
      • b1f19a146295:麻烦您发给Demo给我借鉴学习一下,谢谢 hnistduanao@outlook.com
      • 44bc540e0746:能发个demo吗,谢谢了(1214044269@qq.com)
      • youn_ger:能不能把写的demo上传一下贴个地址出来呢
      • 8c57f78d7ad6:谢谢分享,请问可以压缩成h.264格式吗
      • zgcrichard:可以考虑传到网盘上,地址贴出来。
      • 易木:1014003798@qq.com亲。可以发demo?
      • 进击的小学生:求demo,494969990@qq.com :heart_eyes:
      • 拾忆_aa:大神 你好 能否发个demo我学习下么 初学者 870037485@qq.com
      • 梦的梦:Demo能发邮箱吗 最近需要写视频录制 没有头绪 792237442@qq.com
      • 00d10f8a50de:1191548306@qq.com 谢了昂
      • Ashoka_APP:求demo 初学者 没怎么看懂563583336@qq.com
      • fcb856a4cf0e::heart_eyes:大神 求Demo1205822423@qq.com
      • 颖颖颖火的虫:大神 你好 能否发个demo我学习下 ,211818965@qq.com 谢谢啦
      • 平平无奇DeBug:写的很全面,我现在要做的也有这个,只是还要再加上一些滤镜效果。demo可否发一份参考,150861311@qq.com,另外怎么做添加滤镜效果,能指点一二么
        Abson在简书:@BlackRing GPUImage 能帮到你
      • e6a4db1a9746:大神,demo 496288307@qq.com
        b3b346dd7e43:能给我一份你写的demo吗?137338612@qq.com
        欧阳蓝缺:网上找的,https://github.com/AndyFightting/VideoRecord

      本文标题:iOS 录制视频

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