高仿映客(iOS)

作者: 余生丶91 | 来源:发表于2016-12-28 17:58 被阅读945次

    下载地址:

    高仿映客

    开发工具:Xcode8 真机上运行,模拟器会报错

    开发语言:Objective-C
    SDK:RTMPCHybridEngine-IOS

    实现:模仿映客,包含推流、拉流、连麦、弹幕、美颜、发礼物等。

    效果图:

    热门.png 附近.jpg 个人中心.png 搜索.jpg 游客端(直播).jpg 送礼物.jpg 弹框.jpg 主播端(直播).jpg

    Gif效果图:

    主界面.gif 直播间.gif 送礼物.gif 连麦.gif

    比较

    RTMPCHybridEngine是为移动端应用量身打造的基于RTMP和RTC混合引擎的连麦互动流媒体直播系统。通过集成本SDK,只需几个简单API调用,便可实现一套完整的连线麦互动直播流媒体应用。包含了流媒体应用中:『采集->编码->传输->解码->播放->连麦视频互动』的所有步骤。

    AnyRTC云通讯创新性实现了RTMP+RTC的技术融合,使用RTC技术实现基于标准的RTMP协议的互动连线视频直播,在视频处理,音频降噪,码率控制,硬件加速,美颜滤镜,实时通讯,移动端性能等方面性能优越。

    优势:

    • 超低延时

    • 超低内存

    • 无缝连接(原有方案不变的情况,直接嵌入SDK)

    • 文字互动、弹幕消息

    • 人员上下线

    • 多达4人同时在线连麦视频互动

    注意事项:

    1、直播间页面为香港卫视rtmp数据(当有两台设备可实现适时直播);

    2、演示推流、拉流、连麦至少需要两部iPhone手机;

    3、一部手机进入直播页面点击开启直播,开启显示成功后,另一部手机在热门列表下拉刷新,点击列表进入直播间页面。

    4、成功后游客端点击左上方连麦按钮,同意后进行连麦。

    项目结构:

    结构.png

    常见问题:

    1、无法推流、拉流、连麦->AppDelegate配置SDK失效->官方文档

    2、SDK接入需要添加网络权限、访问相机、麦克风权限;
    3、可能遇到的问题:

    ld: library not found for -XXX
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    

    解决:Build Settings->Other Linker Flags->删除XXX

    4、 ios Undefined symbols for architecture arm64
    解决

    5、使用SDWebImage内存暴增解决办法

    开发过程:

    准备工作:项目中图片资源通过PP助手获取映客ipa包,接口数据通过抓包工具获得。

    SDK集成:项目主要依赖RTMPC SDK 具体请参考官方SDK文档

    Code:

    ++++++++++++游客端拉流、连麦的实现++++++++++++

    - (void)repareStartPlay{
        //初始化
        self.guestKit = [[RTMPCGuestKit alloc] initWithDelegate:self withCaptureDevicePosition:RTMPC_SCRN_Portrait withLivingAudioOnly:NO];
        
        
        //只支持rtmp流
        if (!self.livingItem) {
            //假数据  rtmp://live.hkstv.hk.lxdns.com/live/hks
             [self.guestKit StartRtmpPlay:@"rtmp://live.hkstv.hk.lxdns.com/live/hks" andRender:self.showView];
        } else{
            //如果有两部手机运行,且创建项目,可实现实时推流、拉流
            [self.guestKit StartRtmpPlay:self.livingItem.rtmp_url andRender:self.showView];
        }
        
    //    [self.guestKit StartRtmpPlay:self.adressStr andRender:self.showView];
    //    [self.guestKit setVideoContentMode:VideoShowModeScaleAspectFill];
    
    }
    
    //游客端RTC连麦
    - (void)prepareRtc{
        self.guestKit.rtc_delegate = self;
        
        NSString *strUrl = [NSString stringWithFormat:@"http://img2.inke.cn/MTQ4MTI4NTI4NTMxMyMzNjYjanBn.jpg"];
        
        NSDictionary *customDict = [NSDictionary dictionaryWithObjectsAndKeys:@"AnyRTC",@"nickName",strUrl,@"headUrl" ,nil];
        NSString *customStr = [self JSONTOString:customDict];
        
        //当andyrtcId为空,假数据,RTC连麦失败
        [self.guestKit JoinRTCLine:self.livingItem.andyrtcId andCustomID:@"游客A" andUserData:customStr];
    }
    

    ++++++++++++主播端推流、连麦的实现++++++++++++

    #pragma 推流
    - (void)prepareStream{
        //初始化主播端  推流代理
        self.hosterKit = [[RTMPCHosterKit alloc]initWithDelegate:self withCaptureDevicePosition:RTMPC_SCRN_Portrait withLivingAudioOnly:NO];
        //推流模式
        [self.hosterKit SetNetAdjustMode:RTMP_NA_Fast];
        [self.hosterKit SetVideoCapturer:self.view andUseFront:YES];
        [self.hosterKit SetBeautyEnable:YES];
        //推流质量
        [self.hosterKit SetVideoMode:RTMPC_Video_SD];
    }
    
    //rtc连麦
    - (void)prepareRtc{
        NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"hostID",@"hosterId",self.rtmpUrl,@"rtmp_url",self.hlsUrl,@"hls_url",self.livingName?self.livingName:[self getTopName],@"topic",self.randomStr,@"anyrtcId",[NSNumber numberWithBool:_isAudioLiving],@"isAudioOnly", nil];
        NSString *jsonString = [self JSONTOString:dict];
        //rtc的代理
        self.hosterKit.rtc_delegate = self;
        //打开RTC连麦功能
        [self.hosterKit OpenRTCLine:self.randomStr andCustomID:@"主播端" andUserData:jsonString andRtcArea:@"CN"];
    }
    

    NearbyController 使用SDWebImage内存暴增

    static BOOL SDImageCacheOldShouldDecompressImages = YES;
    static BOOL SDImagedownloderOldShouldDecompressImages = YES;
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        index = 0;
         [self.view addSubview:self.nearCollectView];
        [self.locationManager startUpdatingLocation];
        self.view.backgroundColor = [UIColor whiteColor];
        
        SDImageCache *canche = [SDImageCache sharedImageCache];
        SDImageCacheOldShouldDecompressImages = canche.shouldDecompressImages;
        canche.shouldDecompressImages = NO;
        //保存原设置,禁用解压缩
        SDWebImageDownloader *downloder = [SDWebImageDownloader sharedDownloader];
        SDImagedownloderOldShouldDecompressImages = downloder.shouldDecompressImages;
        downloder.shouldDecompressImages = NO;
    }
    
    - (void)dealloc{
        self.nearCollectView.dataSource = nil;
        self.nearCollectView.delegate = nil;
        self.locationManager.delegate = nil;
        
        //恢复原设置
        SDImageCache *canche = [SDImageCache sharedImageCache];
        canche.shouldDecompressImages = SDImageCacheOldShouldDecompressImages;
        
        SDWebImageDownloader *downloder = [SDWebImageDownloader sharedDownloader];
        downloder.shouldDecompressImages = SDImagedownloderOldShouldDecompressImages;
    }
    

    搜索页长按图像动画的实现:

    //长按
    - (void)longPressClick:(UILongPressGestureRecognizer *)sender{
        NSInteger tag = [sender view].tag;
        if (sender.state == UIGestureRecognizerStateBegan) {
            [UIView animateWithDuration:0.5 animations:^{
                switch (tag) {
                    case 50:
                        self.imageViewF.transform = CGAffineTransformMakeScale(0.8, 0.8);
                        break;
                    case 51:
                        self.imageViewS.transform = CGAffineTransformMakeScale(0.8, 0.8);
                        break;
                    case 52:
                        self.imageViewT.transform = CGAffineTransformMakeScale(0.8, 0.8);
                        break;
                    default:
                        break;
                }
            }];
        } else if (sender.state == UIGestureRecognizerStateEnded) {
            [UIView animateWithDuration:0.5 animations:^{
                self.imageViewF.transform = CGAffineTransformMakeScale(1.0, 1.0);
                self.imageViewS.transform = CGAffineTransformMakeScale(1.0, 1.0);
                self.imageViewT.transform = CGAffineTransformMakeScale(1.0, 1.0);
            }];
        }
    }
    

    +++++++++++++++部分动画的实现+++++++++++++++

    弹框实现 CameraView

        //关键帧  damp阻尼
        [UIView animateWithDuration:0.8 delay:0.1 usingSpringWithDamping:0.6 initialSpringVelocity:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
            _liveButton.frame = CGRectMake(0, LiveGetY, LiveWidth, LiveWidth);
        } completion:^(BOOL finished) {
    
        }];
    

    附近动画NearbyController

    - (void)showAnimation{
        self.iconImageView.layer.transform = CATransform3DMakeScale(0.1, 0.1, 1);
        [UIView animateWithDuration:0.25 animations:^{
            self.iconImageView.layer.transform = CATransform3DMakeScale(1, 1, 1);
        }];
    }
    

    进入主播页动画CameraViewController:

    - (void)viewWillAppear:(BOOL)animated{
        //由小变大的圆形动画
        CGFloat radius = [UIScreen mainScreen].bounds.size.height;
        UIBezierPath *startMask =  [UIBezierPath bezierPathWithOvalInRect:CGRectMake(self.view.centerX, self.view.centerY, 0, 0)];
        UIBezierPath *endMask = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(CGRectMake(self.view.centerX, self.view.centerY, 0, 0), -radius, -radius)];
        CAShapeLayer *maskLayer = [CAShapeLayer layer];
        maskLayer.path = endMask.CGPath;
        maskLayer.backgroundColor = (__bridge CGColorRef)([UIColor whiteColor]);
        CABasicAnimation *maskLayerAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
        maskLayerAnimation.fromValue = (__bridge id)(startMask.CGPath);
        maskLayerAnimation.toValue = (__bridge id)((endMask.CGPath));
        maskLayerAnimation.duration = 0.8f;
        [maskLayer addAnimation:maskLayerAnimation forKey:@"path"];
        self.view.layer.mask = maskLayer;
    }
    

    声明

    本项目仅供学习,侵权立删!

    总结

    第一次仿写直播项目,希望能帮到有需要的朋友,之前也没接触过直播类的项目,写的不好的地方见谅!项目中发现什么问题@我或者私信我,大家一起交流学习,有时间会修复项目中的BUG,有时间更新。

    参考

    RTMPC

    仿映客刷礼物效果---基本逻辑实现

    直播类送礼动画<豪华礼物+小礼物>

    相关文章

      网友评论

      • 飞天小猪猪:可以写一下详细点的技术文档吗 下载了工程 看的迷迷糊糊的:smile:
      • 4d8aa2739b6d:方便共享一下项目源码吗:smile:
      • 土豆的疑问:你好,demo 编译报错 “_OBJC_CLASS_$_RTMPCGuest Kit referenced from:”,请问你知道怎么解决吗?
        余生丶91:真机运行
      • Alex_BlackMamba:这个sdk是收费的还是免费的?我是官网注册账号,怎么看似的按照流量收费的?
      • 198a9a0ce2cd:楼主 github下载之后解压不了
      • HANSAHACKER:出现了架构错误Undefined symbols for architecture x86_64: 你上面提的方法也解决不了 还有什么解决方法?
        HANSAHACKER:@川2010丶 开始直播按钮 事件处理不了 给view截获了 不知道是我这原因,还是demo问题
        余生丶91:你可以试试http://www.jianshu.com/search?q=Undefined%20symbols%20for%20architecture%20x86_64&page=1&type=note
      • 1111e57bf6c6:厉害,学习了。

      本文标题:高仿映客(iOS)

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