高仿映客(iOS)

下载地址:

高仿映客

开发工具: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

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容