前言

参与公司的一个项目,手机作为遥控器,与公司的硬件产品(智能机顶盒,类似小米盒子、天猫魔盒等)进行配合,通过手机控制电视上的操作,如前后左右、关机、音量大小、返回、主页、菜单等。硬件采用的协议是DLNA协议,是基于PlatinumKit(C++)写的。由于该项目的时间比较短,顾没有去使用PlatinumKit,因为那需要去进一步研究相关的C++代码,需要一定的时间。在Github上搜索了一下,现成的IOS三方库就只有FuruyamaTakeshi/DLNAwangshuaidavid/DLNA_iOS_Platinum,而后者只实现了DMS,没有实现DMC,对于手机遥控器来说需要的正是DMC,所以采用了前者。

导入整个clinkc工程使用

DLNA CyberLink是基于C语言写的一套代码,存在很多的依赖(头文件),它只提供了一个clinkc.xcodeproj的工程文件,并没有向其他三方库一样有单独出来的一套容易使用的文件目录,而且也很难去单独剥离出来(是可以的,我也尝试了),最后选择了将整个工程导入,只使用其中可以用到的IOS封装。
需要注意一下两点:

  1. 将导入的clinkc.xcodeproj的Build Settings下的Architectures修改为ARCHS_STANDARD也就是支持armv7和arm64的(保持和你的原有项目一直,现在的机子都支持arm64,选ARCHS_STANDARD就对了),在相应的Valid Architectures下也要和主项目一致,如图:

  2. 两个Build Settings下Development中的Skip Install要设置正确,clinkc.xcodeproj中设置为YES,在自己的主工程中设置为NO,这也就标示了你的工程是这工程,如图:

DLNA CyberLink的使用

  • 导入头文件

    1
    #import <CyberLink/UPnPAV.h>
  • 创建CGUpnpAvController并搜索

    1
    2
    3
    4
    5
    6
    7
    8
    9
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
    if (self.avRender != nil) {
    self.avRender = nil;
    }
    self.avController = [[CGUpnpAvController alloc] init]; // CGUpnpAvController实例变量
    self.avController.delegate = self;
    [self.avController search]; // 开始搜索
    });

    使用是相当简单的,只需要new一个实例变量,然后search即可,这里我使用的是异步加载,因为它这套库用的都是同步,而启动这个服务search前会调用start方法,这个工程需要几秒…

  • 实现CGUpnpControlPoint Delegate

    1
    2
    3
    4
    -  (void) controlPoint: (CGUpnpControlPoint *) controlPoint deviceAdded: (NSString *) deviceUdn;
    - (void) controlPoint: (CGUpnpControlPoint *) controlPoint deviceUpdated: (NSString *) deviceUdn;
    - (void) controlPoint: (CGUpnpControlPoint *) controlPoint deviceRemoved: (NSString *) deviceUdn;
    - (void) controlPoint: (CGUpnpControlPoint *) controlPoint deviceInvalid: (NSString *) deviceUdn;

    它这里有四个代理,都是Optional的,我只实现了deviceUpdated这个代理,就我个人的实践发现deviceRemoved和deviceInvalid这两个代理很难会被调用,貌似安卓很成熟的库也有这个问题,很难监听到设备的移除(正常和非正常的关闭)。有效代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    - (void)controlPoint:(CGUpnpControlPoint *)controlPoint deviceUpdated:(NSString *)deviceUdn {
    if (nil == controlPoint) {
    return;
    }

    CGUpnpAvController *controller = (CGUpnpAvController *)controlPoint;
    if (controller != nil) {
    NSArray *renderArr = [controller renderers]; // 关键代码,获得搜索到的所有设备
    NSLog(@"render count: %lu", (unsigned long)renderArr.count);
    if (renderArr.count == 0) {
    return;
    }
    // 将搜索到的设备,保存为CGUpnpDevice,不管是DMS,DMC都是一个device,均继承于CGUpnpDevice
    // ... 还有一些其他的操作,这个和个人业务有关
    }
  • 获得控制实例

    1
    CGUpnpAvRenderer *avRender = [self getConnectDevice:udn];   // 伪代码

    上面为伪代码,搜索出来有很多设备,而你需要找到你连接的那个设备以进行控制,我的做法是通过选择时将连接设备的und(唯一的)传过来,通过udn在搜索到的设备数组中找到,然后确定CGUpnpAvRenderer实例。

  • 控制 CyberLink提供了- (BOOL)setAVTransportURI:(NSString *)aURL这个方法,当你确定了CGUpnpAvRenderer实例后,就可以用该实例进行控制啦

    1
    2
    3
    4
    BOOL res = [self.avRender setAVTransportURI:@"media url"];  // 视频、音频或图片的url地址
    if (res) {
    NSLog(@"control success...\n");
    }

    通过上面方法,即可将手机上播放的在线图片或视频投放到电视端了。当然还有一些播放、暂停、下一曲、上一曲之类的函数,使用也非常的简单,只需要调用相应的如

  • 修改其代码,适应自有项目需求 上面说到的播放、暂停、停止、上一曲、下一曲等我都没有用到,而是用到了向左、向右、确定、返回、音量加减、将手机本机的图片和视频(非网络,需要手机作为httpServer)投放到电视端。可以修改其setAVTransportURI方法进行定制,达到想要的效果,当然这也需要硬件方面做相同的约束规定。

写在结尾

大致的使用就是上面这些了,使用起来还是挺简单的。但是它也真的坑了我很久,一开始是导入整个工程的时候死活包架构错误,然后它是基于同步的,在一定程度上需要依靠良好的网络,会有一定的卡顿现象,我甚至去改过它的socket连接异步…就现成的三方库来说,它没有选择,但是对于一个产品来说,这样的效果可能还是会影响用户的体验,有时间和需求的话,我会采用PlatinumKit进行重新封装,达到更好的效果。