Skip to content
Kecheng edited this page May 2, 2017 · 8 revisions

如何实现转屏时保持推流预览视图_previewView与手机相对静止

在iOS中横竖屏旋转存在比较多的方案,该方案是一种让预览视图与手机相对静止的方案,用户体验会比较好,具体效果请参考2.0.3之后的demo

1. 实现思路

  • 无论横屏还是竖屏,默认app方向是portrait,建议使用UINavigationController作为winow的根控制器,使用topVC的Autorotate来管理app的旋转模式和支持的方向

  • 建议在预览视图所在控制器创建一个父类容器_bgView,用于_kit.preview的size进行参考

  • viewWillAppear:方法中初始化好_bgView的frame和transform方向(支持横屏推流必须初始化)并设置好推流对象_kit的videoOrientation

  • 在_bgView上开启预览

  • 旋转时,在系统的旋转回调方法中进行_bgView的旋转(若版本低于8.0,需要适配8.0及8.0之前,在demo中两种方法都提供了)

2. 示例代码

其中推流预览所在控制器为streamerVC,推流对象为_kit

  • 在预览所在控制器对象streamerVC中:

创建推流对象_kit,并初始化预览父控件_bgView

- (void)viewDidLoad {
    if (self.kit == nil){
        _kit = [[KSYGPUStreamerKit alloc] initWithDefaultCfg];
    }
}

在viewWillAppear:中根据状态栏方向初始化预览的_bgView,设置videoOrientation并开启预览

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [self layoutPreviewBgView];
    if (_kit) { // init with default filter
        _kit.videoOrientation = [[UIApplication sharedApplication] statusBarOrientation];
        [_kit setupFilter:self.ksyFilterView.curFilter];
        [_kit startPreview:_bgView];
    }
}

- (void)layoutPreviewBgView{
    _bgView = [[UIView alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:_bgView];

    // size
    CGFloat minLength = MIN(_bgView.frame.size.width, _bgView.frame.size.height);
    CGFloat maxLength = MAX(_bgView.frame.size.width, _bgView.frame.size.height);
    CGRect newFrame;
    // frame
    CGAffineTransform newTransform;
    
    UIInterfaceOrientation currentInterfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;
    
    if (currentInterfaceOrientation == UIInterfaceOrientationPortrait) {
        newTransform = CGAffineTransformIdentity;
        newFrame = CGRectMake(0, 0, minLength, maxLength);
    } else {
        newTransform = CGAffineTransformMakeRotation(M_PI_2*(currentInterfaceOrientation == UIInterfaceOrientationLandscapeLeft ? 1 : -1));
        newFrame = CGRectMake(0, 0, maxLength, minLength);
    }
    
    _bgView.transform = newTransform;
    _bgView.frame = newFrame;
}

当window旋转或改变控制器的size时,需要在回调方法里对_bgView进行调整

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator NS_AVAILABLE_IOS(8_0)
{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
    
    // size
    CGSize screenSize = [UIScreen mainScreen].bounds.size;
    CGFloat minLength = MIN(screenSize.width, screenSize.height);
    CGFloat maxLength = MAX(screenSize.width, screenSize.height);
    CGRect newFrame;
    
    // frame
    CGAffineTransform newTransform;
    // need stay frame after animation
    CGAffineTransform newTransformOfStay;
    // whether need to stay
    __block BOOL needStay = NO;
    
    UIInterfaceOrientation currentInterfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;
    UIDeviceOrientation toDeviceOrientation = [UIDevice currentDevice].orientation;
    
    if (toDeviceOrientation == UIDeviceOrientationPortrait) {
        newTransform = CGAffineTransformIdentity;
        newFrame = CGRectMake(0, 0, minLength, maxLength);
    } else {
        if (currentInterfaceOrientation == UIInterfaceOrientationPortrait) {
            newTransform = CGAffineTransformMakeRotation(M_PI_2*(toDeviceOrientation == UIDeviceOrientationLandscapeRight ? 1 : -1));
        } else {
            needStay = YES;
            if (currentInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
                newTransform = CGAffineTransformRotate(self.bgView.transform, M_PI * 1.00001);
                newTransformOfStay = CGAffineTransformRotate(self.bgView.transform, M_PI);
            }else{
                newTransform = CGAffineTransformRotate(self.bgView.transform, SYSTEM_VERSION_GE_TO(@"8.0") ? 1.00001 * M_PI : M_PI * 0.99999);
                newTransformOfStay = CGAffineTransformRotate(self.bgView.transform, M_PI);
            }
        }
        newFrame = CGRectMake(0, 0, maxLength, minLength);
    }
    
    __weak typeof(self) weakSelf = self;
    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        __strong __typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) {
            return ;
        }
        strongSelf.bgView.transform = newTransform;
        strongSelf.bgView.frame =  newFrame;
    } completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (!strongSelf) {
            return ;
        }
        if (needStay) {
            strongSelf.bgView.transform = newTransformOfStay;
            strongSelf.bgView.frame = newFrame;
            needStay = NO;
        }
    }];
}

适配8.0之前

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    
    // size
    CGSize screenSize = [UIScreen mainScreen].bounds.size;
    CGFloat minLength = MIN(screenSize.width, screenSize.height);
    CGFloat maxLength = MAX(screenSize.width, screenSize.height);
    CGRect newFrame;
    
    // frame
    CGAffineTransform newTransform;
    // need stay frame after animation
    CGAffineTransform newTransformOfStay;
    // whether need to stay
    __block BOOL needStay = NO;
    
    if (toInterfaceOrientation == UIInterfaceOrientationPortrait) {
        newTransform = CGAffineTransformIdentity;
        newFrame = CGRectMake(0, 0, minLength, maxLength);
    } else {
        if (self.interfaceOrientation == UIInterfaceOrientationPortrait) {
            newTransform = CGAffineTransformMakeRotation(M_PI_2*(toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft ? 1 : -1));
        } else {
            needStay = YES;
            if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
                newTransform = CGAffineTransformRotate(self.bgView.transform,M_PI * 1.00001);
                newTransformOfStay = CGAffineTransformRotate(self.bgView.transform, M_PI);
            }else{
                newTransform = CGAffineTransformRotate(self.bgView.transform,SYSTEM_VERSION_GE_TO(@"8.0") ? 1.00001 * M_PI : M_PI * 0.99999);
                newTransformOfStay = CGAffineTransformRotate(self.bgView.transform, M_PI);
                
            }
        }
        newFrame = CGRectMake(0, 0, maxLength, minLength);
    }
    
    __weak typeof(self) weakSelf = self;
    [UIView animateWithDuration:duration animations:^{
        __strong __typeof(weakSelf) strongSelf = weakSelf;
        // sometimes strongSelf can be nil in iOS version 7.0
        if (!strongSelf) {
            return ;
        }
        strongSelf.bgView.transform = newTransform;
        strongSelf.bgView.frame = newFrame;
    }completion:^(BOOL finished) {
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (!strongSelf) {
            return ;
        }
        if (needStay) {
            strongSelf.bgView.transform = newTransformOfStay;
            strongSelf.bgView.frame = newFrame;
            needStay = NO;
        }
    }];
}

1. 推流环节说明

2. 特色功能说明

2.1 采集

2.2 音频处理

2.3 视频处理

2.4 编码

2.5 推流

2.6 输入多样化

2.7 集成

3. 第三方功能

4. 技术专栏

5. 已知问题

8. FAQ

金山云计算

Clone this wiki locally