zoukankan      html  css  js  c++  java
  • iOS短视频直播源码开发,视频录制的实现

    在短视频直播源码开发时,短视频录制和短视频编辑是最重要的基础功能,其中短视频录制还需要开发出自定义的相机,所以今天我们就一起来了解一下在短视频直播源码开发中短视频录制时如何实现的吧。

    BSFramework 组件包:

    • 2D、3D无限轮播图组件
    • 图片视频选择、图片视频预览、图片视频拍摄组件
    • GitHub 地址

    框架:<AVFoundation/AVFoundation.h>

    • 关键类: AVAssetWriter

    大致需要属性

    @property (nonatomic ,strong) AVAssetWriter *writer;//视频采集
    @property (nonatomic ,strong) AVAssetWriterInput *writerAudioInput;//视频采集
    @property (nonatomic ,strong) AVAssetWriterInput *writerVideoInput;//视频采集
    

    大致流程:

    • session 初始化
    • device 初始化
    • input,output 初始化,并关联device
    • session 添加 input 和 output
    • 初始化预览层 AVCaptureVideoPreviewLayer
    • session startRunning
    • 点击录制按钮, 录制视频 代理回调后,处理数据,写入文件,生成视频

    短视频直播源码中视频的输入输出初始化

    #pragma mark 设置视频类型的输入输出源
    -(void)addVideoIO{
    
        //视频输出源
        NSDictionary *videoSetting = @{(id)kCVPixelBufferPixelFormatTypeKey:@(kCVPixelFormatType_32BGRA)};
    
        self.videoPutData = [[AVCaptureVideoDataOutput alloc]init];
        self.videoPutData.videoSettings = videoSetting;
    
        dispatch_queue_t videoQueue = dispatch_queue_create("vidio", DISPATCH_QUEUE_CONCURRENT);
        [self.videoPutData setSampleBufferDelegate:self queue:videoQueue];
    
        if ([self.session canAddOutput:self.videoPutData]) {
            [self.session addOutput:self.videoPutData];
        }
    
    
        //音频输入源
        AVCaptureDevice *audioDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio]firstObject];
        NSError *audioError = nil;
        self.audioInput = [[AVCaptureDeviceInput alloc]initWithDevice:audioDevice error:&audioError];
        if (!audioError) {
            if ([self.session canAddInput:self.audioInput]) {
                [self.session addInput:self.audioInput];
            }
        }
    
        //音频输出源
        self.audioPutData = [[AVCaptureAudioDataOutput alloc]init];
        if ([self.session canAddOutput:self.audioPutData]) {
            [self.session addOutput:self.audioPutData];
        }
    
        dispatch_queue_t audioQueue = dispatch_queue_create("audio", DISPATCH_QUEUE_CONCURRENT);
        [self.audioPutData setSampleBufferDelegate:self queue:audioQueue];
    }
    

    点击拍摄即初始化 writer

    // 配置 AVAssetWriter
    -(void)configWriter{
    
        dispatch_queue_t writeQueueCreate = dispatch_queue_create("writeQueueCreate", DISPATCH_QUEUE_CONCURRENT);
    
        dispatch_async(writeQueueCreate, ^{
    
            NSError *error = nil;
            self.preVideoURL = [self getVideoURL];
    
            self.writer = [AVAssetWriter assetWriterWithURL:self.preVideoURL fileType:AVFileTypeMPEG4 error:&error];
    
            if (!error) {
    
                NSInteger numPixels = SCREEN_WIDTH * SCREEN_HEIGHT;
                //每像素比特
                CGFloat bitsPerPixel = 12.0;
                NSInteger bitsPerSecond = numPixels * bitsPerPixel;
    
                // 码率和帧率设置
                NSDictionary *compressionProperties = @{ AVVideoAverageBitRateKey : @(bitsPerSecond),
                                                         AVVideoExpectedSourceFrameRateKey : @(15),
                                                         AVVideoMaxKeyFrameIntervalKey : @(10),
                                                         AVVideoProfileLevelKey : AVVideoProfileLevelH264BaselineAutoLevel };
    
                //视频属性
                NSDictionary *videoSetting = @{ AVVideoCodecKey : AVVideoCodecH264,
                                                AVVideoWidthKey : @(SCREEN_HEIGHT * 2),
                                                AVVideoHeightKey : @(SCREEN_WIDTH * 2),
                                                AVVideoScalingModeKey : AVVideoScalingModeResizeAspectFill,
                                                AVVideoCompressionPropertiesKey : compressionProperties };
    
                NSDictionary *audioSetting = @{ AVEncoderBitRatePerChannelKey : @(28000),
                                                AVFormatIDKey : @(kAudioFormatMPEG4AAC),
                                                AVNumberOfChannelsKey : @(1),
                                                AVSampleRateKey : @(22050) };
    
                self.writerAudioInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:audioSetting];
                self.writerAudioInput.expectsMediaDataInRealTime = YES;
    
                self.writerVideoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSetting];
                self.writerVideoInput.expectsMediaDataInRealTime = YES;
    
                self.writerVideoInput.transform = CGAffineTransformMakeRotation(M_PI/2.0);
    
                if ([self.writer canAddInput:self.writerAudioInput]) {
                    [self.writer addInput:self.writerAudioInput];
                }
    
                if ([self.writer canAddInput:self.writerVideoInput]) {
                    [self.writer addInput:self.writerVideoInput];
                }
    
                self.videoRecording = YES;
    
            }else{
                NSLog(@"write 初始化失败:%@",error);
            }
    
        });
    }
    

    短视频直播源码开发,处理回调

    //视频录制回调
    -(void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{
    
        if (!self.videoRecording) {
            return;
        }
    
        CMFormatDescriptionRef desMedia = CMSampleBufferGetFormatDescription(sampleBuffer);
        CMMediaType mediaType = CMFormatDescriptionGetMediaType(desMedia);
    
        if (mediaType == kCMMediaType_Video) {
            
            /**
             * 要点1:
             * 由于 self.canWritting = YES 放在 startSessionAtSourceTime
             * 下,会出现录制的视频 前几帧相同(可能2-3帧都是第一帧) 的问题,故而将
             * self.canWritting = YES 放在 startsession 上,目前测试没出现问题
             */
            
            /**
             * 要点2:
             * 对于 startSessionAtSourceTime 开启时机需要放在类型为
             * kCMMediaType_Video 里判断,因为如果放在外边,可能会导致录制的时候
             * 是没有画面的,但是有声音,这就导致了预览视频的时候发现开头有一段空白视频
             * 但是是有声音的
             */
            
            /**
             * 要点3:
             * 需要将 startSessionAtSourceTime 方法放在类型为kCMMediaType_Video里
             * 确保第一帧为图像在开启录制
             */
            
            if (!self.canWritting) {
                [self.writer startWriting];
    
                CMTime timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
    
                self.canWritting = YES;
                [self.writer startSessionAtSourceTime:timestamp];
            }
        }
        
        
        if (self.canWritting) {
            
            if (mediaType == kCMMediaType_Video) {
                if (self.writerVideoInput.readyForMoreMediaData ) {
                    
                    BOOL success = [self.writerVideoInput appendSampleBuffer:sampleBuffer];
                    if (!success) {
                        NSLog(@"video write failed");
                    }
                }
                
            }else if (mediaType == kCMMediaType_Audio && self.canWritting){
                
                if (self.writerAudioInput.readyForMoreMediaData) {
                    BOOL success = [self.writerAudioInput appendSampleBuffer:sampleBuffer];
                    if (!success) {
                        NSLog(@"audio write failed");
                    }
                }
            }
        }
    }
    

    PS:处理视频回调有几个需要注意的点,都在代码的注释上写好了

    视频预览

    // 视频录制完成后 预览
    -(void)previewVideo{
    
        if (self.playerLayer) {
            [self.playerLayer removeFromSuperlayer];
            self.player = nil;
            self.playerLayer = nil;
        }
    
        AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:[AVAsset assetWithURL:self.preVideoURL]];
        self.player = [[AVPlayer alloc]initWithPlayerItem:playerItem];
    
        self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
        self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
        self.playerLayer.frame = self.view.frame;
    
        [self.view.layer addSublayer:self.playerLayer];
        [self.player play];
        
        [self.view bringSubviewToFront:self.bottomView];
    
    }
    
    

    存储视频

    //videoPath为视频下载到本地之后的本地路径
    - (void)saveVideoToAlbum:(NSString*)videoPath{
    
        if(videoPath) {
    
            BOOL compatible = UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(videoPath);
    
            if(compatible){
                UISaveVideoAtPathToSavedPhotosAlbum(videoPath,self,@selector(video:didFinishSavingWithError:contextInfo:),nil);
            }
        }
    }
    
    //保存视频完成之后的回调
    - (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{
    
        if(error) {
            NSLog(@"保存视频失败%@", error);
        }else{
            // 保存视频成功后退出,刷新相册,退出界面
            if ([self.delegate respondsToSelector:@selector(photoCameraNextBtnClickedWithVideoPath:)]) {
                [self.delegate photoCameraNextBtnClickedWithVideoPath:videoPath];
            }
            [[NSNotificationCenter defaultCenter]postNotificationName:@"didFinishSelectVideo" object:videoPath];
            [self.navigationController dismissViewControllerAnimated:YES completion:nil];
        }
    }
    

    以上就是“iOS短视频直播源码开发,视频录制的实现”的全部内容了,希望对大家有帮助。
    本文转载自网络,转载仅为分享干货知识,如有侵权欢迎联系云豹科技进行删除处理
    原文链接:https://www.jianshu.com/p/61c546aadd88

  • 相关阅读:
    尝试使用word发布博客
    设计模式学习系列7 建造者模式
    设计模式学习系列6 原型模式(prototype)
    最近比较闲
    提高程序运行效率的10个简单方法(转)
    设计模式学习系列5 工厂模式
    【LINUX/UNIX网络编程】之使用消息队列,信号量和命名管道实现的多进程服务器(多人群聊系统)
    三十分钟掌握STL
    在python包管理中使用easy_install软件的步骤
    【转】推荐给大家的7本游戏开发书
  • 原文地址:https://www.cnblogs.com/yunbao/p/14977609.html
Copyright © 2011-2022 走看看