Skip to content

playerRecord

shixuemei edited this page Apr 22, 2017 · 6 revisions

短视频录制功能也可以理解为录屏功能,可以将当前正在播放的视频(可以包含有UI界面)录制为mp4或flv格式的文件保存到本地。

如果不需要录制UI,可以直接获取播放器返回的原始音视频数据写入文件,这种方法比较简单,此处不再赘述。本文则重点介绍如何完成含有UI的播放录制功能。

一、依赖组件

因为视频录制功能需要音视频的编码和复用功能,而这些功能对于独立的播放SDK并不存在也不需要,所以要使用短视频录制功能,需要集成的SDK为融合版SDK,使用其中的KSYGPUPicMixerKSYAudioMixerKSYGPUPicOutput等模块

二、方案

2.1 方案一(图层混合方案)

v1.9.5及以上版本支持

播放时通过播放器中的textureBlock获取渲染的TextureId,与UIElement叠加后得到视频数据送入KSYStreamerBase模块;原始音频数据则从播放器的audioDataBlock中获取,经过KSYAudioMixer后送入KSYStreamerBase模块,示意图如下:

播放录屏方案一

优点:

  • 支持自定义录制UI层级
  • 能够按照视频帧率录制

缺点:

  • 在某些设备上及资源占用严重

2.2 方案二(截屏方案)

v2.1.1及以上版本支持

音频数据的获取与处理方式同方案一,不同之处是视频和UI数据的获取,方案二通过直接截取UIView上内容来获取当前的显示内容,转换为CVPixerBuffer后直接送入KSYStreamerBase模块,示意图如下:

播放录屏方案二

优点:

  • 支持自定义录制UI层级
  • 可自定义录制帧率
  • 资源占用相对较少

缺点:

  • 无法按照视频播放的帧率进行录制

三、使用说明

KSYLive_iOS的Demo中提供了KSYUIRecorderKit类实现录屏功能,KSYRecordVC类中演示了如何使用KSYUIRecorderKit进行短视频录制 录制功能的具体实现请参见KSYUIRecorderKit.m文件源码,此处简单介绍直接使用该类的方法

  1. 引入所需头文件

    #import "KSYUIRecorderKit.h"
    #import <GPUImage/GPUImage.h>
    #import <libksygpulive/libksygpulive.h>
    
  2. 声明KSYMoviePlayerController对象和KSYUIRecorderKit对象

    @property (strong, nonatomic) KSYMoviePlayerController *player;
    @property (strong, nonatomic) KSYUIRecorderKit* kit;
    
  3. 初始化KSYUIRecorderKit对象

    -(void)setupUIKit{
        _kit = [[KSYUIRecorderKit alloc]init];
        [self addUIToKit];
    }
    
    //addUIToKit将需要录制的界面元素添加到_kit.contentView,未添加的元素将不能被录制下来
    -(void)addUIToKit{
        
        [_kit.contentView addSubview:labelVolume];
        [_kit.contentView addSubview:sliderVolume];
        [_kit.contentView addSubview:lableHWCodec];
        [_kit.contentView addSubview:switchHwCodec];
        [_kit.contentView addSubview:btnPlay];
        [_kit.contentView addSubview:btnPause];
        [_kit.contentView addSubview:btnResume];
        [_kit.contentView addSubview:btnStop];
        [_kit.contentView addSubview:btnQuit];
        [_kit.contentView addSubview:btnStartRecord];
        [_kit.contentView addSubview:btnStopRecord];
        [_kit.contentView addSubview:stat];
        
        [_kit.contentView addSubview:_player.view];
        [self.view addSubview:_kit.contentView];
        [_kit.contentView sendSubviewToBack:videoView];
    }
    
  4. 初始化KSYMoviePlayerController对象并设置音视频数据的回调
    此处对播放器的初始化使用initWithContentURL:sharegroup:进行初始化操作

    self.player = [[KSYMoviePlayerController alloc] initWithContentURL:_url sharegroup:[[[GPUImageContext sharedImageProcessingContext] context] sharegroup]];
    [self setupObservers];
    
    //player视频数据输入
    __weak KSYUIRecorderKit* weakKit = _kit;
    _player.textureBlock = ^(GLuint textureId, int width, int height, double pts){
        CGSize size = CGSizeMake(width, height);
        CMTime _pts = CMTimeMake((int64_t)(pts * 1000), 1000);
        [weakKit processWithTextureId:textureId TextureSize:size Time:_pts];
    };
    
    //player音频数据输入
    _player.audioDataBlock = ^(CMSampleBufferRef buf){
        CMTime pts = CMSampleBufferGetPresentationTimeStamp(buf);
        if(pts.value < 0)
        {            
            return;
        }
        [weakKit processAudioSampleBuffer:buf];
    };
    
    
  5. 开始录制
    path为录制文件的保存路径,目前支持mp4和flv格式,依靠后缀名区分

    NSString* recordFilePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/RecordAv.mp4"];
    NSURL * path =[[NSURL alloc] initWithString:recordFilePath];
    [_kit startRecord:path];
    
  6. 停止录制

    [_kit stopRecord];
    

四、录制完成后对于文件的操作

因为stopRecord操作为异步操作,所以stopRecord调用结束后并不表示文件已经写完,需要用以下方式处理

  1. 启动录制前注册KSYStreamStateDidChangeNotification消息

    [[NSNotificationCenter defaultCenter]addObserver:self
                                    selector:@selector(onStreamStateChange:)
                                        name:(KSYStreamStateDidChangeNotification)
                                      object:nil];
    
  2. 当收到KSYStreamStateDidChangeNotification消息,且_kit.writer.streamState的值为KSYStreamStateIdle时才表示文件已经写完,此时可以进一步处理文件,否则有可能因为文件被占用导致操作失败

    - (void) onStreamStateChange :(NSNotification *)notification{	    
        if (_kit.writer.streamState == KSYStreamStateIdle && _kit.bPlayRecord == NO){
            [self saveVideoToAlbum: recordFilePath];
        }	    
    }
    
Clone this wiki locally