美文网首页奔跑吧 iOSiOS直播/短视频技术
iOS屏幕录制步骤一:截屏、将截屏图片合成视频

iOS屏幕录制步骤一:截屏、将截屏图片合成视频

作者: OC笔记 | 来源:发表于2017-03-01 14:32 被阅读2374次

    写在前面

    公司近期让做一个录制屏幕类的App,我研究了iOS9新增的Replaykit框架,使用起来确实挺简单的,性能也很好,但是获取不到视频文件,这一点就决定了我不能使用这个框架。那么我只能使用最原始的方法,抓取view的截图,然后将这些截图合成视频,最后再把同时录制的音频合成到视频中。这篇文章先介绍如何抓取截图并合成视频。

    抓取截屏

    先贴一下代码:通过一个layer抓取屏幕截图

    /// view => screen shot image
    - (UIImage *)fetchScreenshot {
        UIImage *image = nil;
        if (self.captureLayer) {
            CGSize imageSize = self.captureLayer.bounds.size;
            UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
            CGContextRef context = UIGraphicsGetCurrentContext();
            [self.captureLayer renderInContext:context];
            image = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
        }
        
        return image;
    }
    

    上面的方法主要通过使用图形上下文CGContextRef来抓取layer的内容。代码很简单,不做过多的解释了。

    将CGImage转换成CVPixelBufferRef缓存数据

    /// image => PixelBuffer
    - (CVPixelBufferRef)pixelBufferFromCGImage:(CGImageRef)image {
        NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                                 [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                                 [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
                                 nil];
        
        CVPixelBufferRef pxbuffer = NULL;
        
        CGFloat frameWidth = CGImageGetWidth(image);
        CGFloat frameHeight = CGImageGetHeight(image);
        
        CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault,frameWidth,frameHeight,kCVPixelFormatType_32ARGB,(__bridge CFDictionaryRef) options, &pxbuffer);
        
        NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
        
        CVPixelBufferLockBaseAddress(pxbuffer, 0);
        void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
        NSParameterAssert(pxdata != NULL);
        
        CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
        CGContextRef context = CGBitmapContextCreate(pxdata, frameWidth, frameHeight, 8,CVPixelBufferGetBytesPerRow(pxbuffer),rgbColorSpace,(CGBitmapInfo)kCGImageAlphaNoneSkipFirst);
        
        NSParameterAssert(context);
        CGContextConcatCTM(context, CGAffineTransformIdentity);
        CGContextDrawImage(context, CGRectMake(0, 0,frameWidth,frameHeight),  image);
        CGColorSpaceRelease(rgbColorSpace);
        CGContextRelease(context);
        
        CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
        
        return pxbuffer;
    }
    

    只有将CGImage转换成CVPixelBufferRef缓存数据后,才能存储到视频中。

    将多张图片合成视频

    将图片合成视频需要使用到以下几个类:

    AVAssetWriter

    AVAssetWriter负责将媒体数据写入到文件。创建AVAssetWriter对象需要传入的参数包括文件的输出路径URL和文件格式。文件格式选择AVFileTypeMPEG4即是MP4格式。

    NSURL *fileUrl = [NSURL fileURLWithPath:self.videoPath];
        self.videoWriter = [[AVAssetWriter alloc] initWithURL:fileUrl fileType:AVFileTypeMPEG4 error:&error];
    
    AVAssetWriterInput

    AVAssetWriterInput负责存储视频或者音频缓存数据,AVAssetWriterInput对象创建完成后需要添加到AVAssetWriter中。

    // 设置视频的编码和尺寸
    NSDictionary *videoCompressionProps = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:size.width * size.height], AVVideoAverageBitRateKey, nil];
        
        NSDictionary *videoSettings = @{AVVideoCodecKey: AVVideoCodecH264,
                                        AVVideoWidthKey: @(size.width),
                                        AVVideoHeightKey: @(size.height),
                                        AVVideoCompressionPropertiesKey: videoCompressionProps};
        
        self.videoWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
        
        NSParameterAssert(self.videoWriterInput);
    
        // expectsMediaDataInRealTime设置为YES, 表示实时获取摄像头和麦克风采集到的视频数据和音频数据
        self.videoWriterInput.expectsMediaDataInRealTime = YES;
    
    AVAssetWriterInputPixelBufferAdaptor

    AVAssetWriterInputPixelBufferAdaptor负责将图片转成的缓存数据CVPixelBufferRef追加到AVAssetWriterInput中。

    NSDictionary *bufferAttributes = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, nil];
        
        self.adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:self.videoWriterInput sourcePixelBufferAttributes:bufferAttributes];
    

    以上只写了图片合成视频的主要几个点,如果大家有不明白的,或者需要参考代码的话,请点击以下链接:GitHub

    相关文章

      网友评论

      • 心语风尚:合成视频 为什么没有设置帧率
      • 烟花灬肆意:你好 视频跟声音对不上,视频跑快了 声音慢了
        烟花灬肆意:@OC笔记 弄好了
        OC笔记:这个可能需要你自己实际调试一下,看看音频长度和视频长度是否匹配,从哪个时间点开始匹配不上的,具体问题具体分析吧
      • Jody526:录制的时候 如果里面有视频会录制不了 我录制了全屏 然后直播视频那里是空白的 其他控件都在
        Jody526:@OC笔记 哦哦:joy:
        OC笔记:@桀杉 苹果不让录制视频,因为版权的原因。
      • 机智的昵称:这个截屏方法录制视频和游戏的时候肯定白屏或者黑屏,如果用drawview内存又太高,很令人纠结的问题
      • 随想录:楼主,貌似这个方式合成的视频有些模糊,有办法弄成高清的吗?
        大强哥:请问你找到方法提高了么?
        大强哥:@OC笔记 请问你的视频清晰度怎么提高的?
        OC笔记:你看看截图那块,应该可以使视频清晰点,换换方法,或者改改参数,应该可以。
      • super_阿肥:我在视图中加入了一个AVPlayer,播放时录屏,发现录下来的视频中AVPlayer区域是黑色的(PS,模拟器录制可以采集AVPlayer中的图像而真机不行)大佬求解
        OC笔记:@阿肥YYQ ,avplayer貌似苹果不让录,好像是版权的问题~
        徊家喂猪:我也是遇到了同样的问题,请问解决了吗?我在模拟器都采集不到图像
      • 哥只是个菜鸟:黑屏,楼主不处理一下问题么?
        OC笔记:@哥只是个菜鸟 系统的replaykit试试吧,录屏直播现在不都用这个吗?
        哥只是个菜鸟:@OC笔记 黑屏应该是偶尔出现的,也说不清是什么情况下。我现在在做一个关于摄像头直播流的录屏,用这个方法行不通,直播的方式也是通过把数据流转码最终以图片的方式显示,录下来的视频一直是黑屏的,不知道楼主有什么好建议么?
        OC笔记:不好意思,最近一直加班。我刚刚下载了demo,试过了,没有黑屏的问题啊!顺便说一下,Change Color是为了录制视频时,能看到改变的效果。(这个我是真机测试的,模拟器没试过啊)
      • 心语风尚:视频黑屏
      • df087aeb5bae:先mark 后看。
      • ffbc345c87dc:为什么我用layer抓取屏幕截图,截出来的图片是白屏的
      • ZHU大仙:发现renderInContext这个方法占用CUP和内存比较高,录制时间久了会不会出问题?怎么解决的
        Mikebanana:@C了个JJ 晚上多吃点鸡就解决了
        00822452baa5:+1. renderInContext到后面就炸了 如何解决的
      • LeoDavid:先马,在看。
      • 84e63157d731:为什么我录制的这个视频不能播放啊

      本文标题:iOS屏幕录制步骤一:截屏、将截屏图片合成视频

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