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短视频直播源码开发,视频录制的实现”的全部内容了,希望对大家有帮助。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一对一交友聊天视频社交一对一系统,独立的Android、iOS手机社交APP,融合了语音聊天、视频直播、一键约聊、小视频拍摄等功能,按分钟计时收费聊天方式,支持房间礼物打赏,为平台运营方和主播提供更多的盈利变现方式。程序源完全开源,支持二次开发,根据客户不同应用场景需求,定制个性化解决方案,不带教程,未测试,不解释了,因为我也不擅长java 一对一交友聊天视频社交一对一系统,独立的Android、iOS手机社交APP,融合了语音聊天、视频直播、一键约聊、小视频拍摄等功能,按 分钟计时收费聊天方式,支持房间礼物打赏,为平台运营方和主播提供更多的盈利变现方式。程序源完全开源,支持二次开发,根据客户不同应 用场景需求,定制个性化解决方案。 首页主播列表;包含推荐主播、附近主播和关注主播列表,按照在线>推荐值>星级进行主播排序。 搜索筛选主播;根据主播昵称或ID搜索主播,按照主播性别、通话类型筛选主播,发起聊天请求。 一对一视频聊天;高清视频聊天直播间,按分钟计时付费,可进行美颜设置、送礼打赏、发送消息内容。 一对一语音聊天;按分钟计时付费,主播和用户可以进行一对一语音聊天,支持送礼打赏。 主播详情页介绍;主要分为两部分显示,优先展示主播上传的个人图片及小视频内容,以及主播基本资料内容;上滑显示主播详细介绍资料,主播 形象标签、用户印象标签、主播礼物柜等内容。 印象标签功能;后台可设置主播形象标签及用户印象标签,每次聊天结束后,用户可给予主播星级评定,同时给主播添加印象标签,印象标签会在 主播详情页内显示。 私信消息功能;显示系统通知、官方公告、认证通过通知等;预约列表显示当前收到的预约聊天信息;私信内容支持文字、图片、语音、表情等不 同形式。 用户个人中心;主播端:编辑个人资料、主播认证、充值提现、主播动态、主播印象、礼物柜、聊天方式等。 用户端;编辑个人资料、认证主播、钱包充值、勿扰模式等。 匹配聊天;用户可选择与主播的通话类型、聊天价格区间,匹配相应主播发起聊天请求。 动态发布;主播可发布个人动态,支持文字、语音、视频等内容,支持点赞、评论等付费内容查看主播可上传图片和小视频内容,并设置付费观看 模式,用户需要充值会员或进行付费查看。 平台分享推广;每位用户拥有单独的推广邀请,可分享邀请推广平台,获取平台佣金成。 自主研发的小视频智能推荐机制,付费小视频内容观看,可在小视频也发起一对一视频聊天。 小视频录制;可录制上传小视频内容,支持本地小视频上传,可对视频内容进行编辑,增加相关视频特效 系统应用场景;陌生人社交/语音聊天交友/游戏陪玩社交/O2O社交交友 huzhan上卖1万5
  在iOS应用开发中有很多涉及硬件访问和传感器编程。本书我们将向大家介绍苹果iOS设备主要的传感器应用开发。本书是我们智捷iOS课堂团队编写iOS系列丛书中的一本,目的是使一个有iOS开发基础的程序员通过本书的学习,能够开发iOS 硬件访问和传感器应用的程序。另外,对于没有iOS开发基础读者,我们推荐先阅读我们已经出版的《iOS开发指南——从0基础到App Store上线》(网站:http://www.iosbook1.com),这两本书都是我们编写的,知识体系衔接的很好。 更多关于本书内容请关注本书网站http://www.51work6.cn,本书页面http://www.51work6.cn/?page_id=1426 更多iOS开发相关视频教程请关注http://v.51work6.com    本书是介绍了iOS设备主要的传感器应用开发的相关知识,其中包括手势识别、加速度计、陀螺仪、指南针、磁力计、照相机、摄像头、音频设备访问、定位和蓝牙通信等。而且全部采用基于iOS 7最新的API。    全书分为9章:开篇综述、手势识别、加速度计与陀螺仪、指南针与磁力计、照相机与摄像头、音频设备访问、使用蓝牙、定位和iOS敏捷开发项目实战——增强现实版本铅锤应用。    第2章介绍了手势识别,通过两种方式实现iOS常用手势,这些手势包括了:Tap(点击)、Long Press(长按)、Pan(拖动)、Swipe(滑动)、Rotation(旋转)和Pinch(手指的合拢和张开)。    第3章介绍了加速度计与陀螺仪,加速度计与陀螺仪是可以感知设备运动的两个传感器,通过这些传感器,我们可以开发出很多有趣的应用和游戏。    第4章我们介绍了磁力计,磁力计可以感知周围的磁场,可以开发指南针、罗盘等导航应用,还可以开发特斯拉计(或高斯计)等感知磁场强度的应用等。    第5章我们介绍了照相机与摄像头,对于iOS设备,它们事实上是一个硬件,这一章中我们介绍了图片抓取和视频捕获等技术。    第6章我们介绍了音频设备访问,音频的输入是通过麦克风设备实现,而音频的输出是通过扬声器设备实现的。本章我们介绍AVFoundation框架实现音频播放和音频录制。    第7章我们介绍了使用蓝牙,在蓝牙4.0之后将通信模式分为高速和低速类型。高速类型是传统蓝牙,在iOS中高速蓝牙通信开发可以通过Game Kit实现。低速类型被称为低功耗蓝牙(BLE Bluetooth low energy),可以连接一些量测型传感器,例如:心率监视器、血压计和温度计等,开发BLE可以通过CoreBluetooth.framework实现。本章我们重点介绍Game Kit和低功耗蓝牙通讯。    第8章我们介绍了定位技术,介绍了iOS设备如何实现定位应用开发,其中包括:定位服务编程、地理信息编和地理信息反编。此外,还介绍了苹果的微定位技术iBeacon,以及接近传感器。    第9章是项目实战,通过增强现实版本铅锤应用介绍了iOS敏捷开发过程。本项目中用到了视频捕获实现的增强现实技术,还有加速度计等传感器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值