iOS DLNA Cyberlink,PlatinumKit库完成DLNA功能

   通过一个多月的研究,终于将iOS DLNA搞定。记录一下。c++

   关于DLNA开发,目前有两个框架。一个Cyberlink,一个platinumkit。Cyberlink的好处就是提供了一套OC的api供你调用,很简单方便。可是此框架有不少问题,且功能不全。platinumkit框架底层为c++,若要用此套框架,就得进行oc和c++的混编,以前我没作过oc和c++的混编,因此去看platinumkit的源码时,以为头疼无比,浪费了不少时间,可是使用cyberlink框架又有不少功能没法解决,而且框架常常出问题。最后完成DMC部分的时候仍是用了cyberlink,有问题怎么办,只能硬着头皮改源码了。好在最后仍是搞定了。git

   1.DMS

      首先让手机本身具备DMS的功能,此处用的Platinumkit框架。github上有个demo :https://github.com/wangshuaidavid/DLNA_iOS_Platinum,当时主要就是依赖于这个demo完成了手机dms部分的功能。关于platinumkit库的使用,网上有教程,你们能够参考,就是编译一个.a的文件出来,而后拉进本身的项目。.a的文件分模拟器和真机两个,能够经过终端合成一个。关于DMS部分没什么好说的,主要看那个demo就行了。github

   2.DMC,DMR

     使用cyberlink库完成dmc功能,github上有个demo:https://github.com/FuruyamaTakeshi/DLNA。此demo完成了基本的dmc功能,能够搜索同一局域网内的dms,dmr。api

 

    CGUpnpAvController* avCtrl = [[CGUpnpAvController alloc] init];
    avCtrl.delegate = self;
    [avCtrl search];
    self.avController = avCtrl;

      这个CGupnpAVController类就是关于搜索dms,dmr的类。经过avCtrl能够获取到对应的dms,而后对dms发送浏览文件的action,返回来文件内容,最后一直拿到AVitem,即资源文件的地址。而后dmr获取到这个资源文件地址之后,经过setAVTransportURI去设置真实的dmr设备的请求地址。设置成功后play就能够播放了。可能看到这里会以为很简单,可是cyberlink的dmr只提供了play,stop,pause,next,previous,seek方法,而且next,previous等方法是没用的。若是你想完成多一些功能,要么就去用platinumkit框架,要么就本身动手给dmr添加这些功能。我选了后者。浏览器

      要想改源码首先要搞懂整个DLNA的工做原理。详细的工做原理我也讲不明白,我只是在研究这两个框架的时候,本身慢慢的明白了一些。网络

      首先不管是dms,仍是dmr,都是一个device,经过代码也能看出这点。CGUpnpAVServer,CGUpnpAvRender都是继承自CGUpnpDevice的。CGUpnpDevice包含一个CgUpnpDevice的结构体。而这个CGUpnpDevice类就是咱们去构造DMS,DMR的关键。CGUpnpDevice这个类有一个方法,initWithXMLDescription。即经过一个xml来建立一个device。当你使用dms的标准xml去建立device时,它就是dms。当你使用dmr的标准xml去建立device时,它就是dmr。两个建立device的xml分别以下多线程

- (NSString *)getDMSDespritionXMl1000000000:(NSString *)uuid
{
    return [NSString stringWithFormat:@"<?xml version='1.0' encoding='utf-8' standalone='yes'?><root xmlns='urn:schemas-upnp-org:device-1-0'><specVersion><major>1</major><minor>0</minor></specVersion><device><deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType><manufacturer>Plutinosoft LLC</manufacturer><manufacturerURL>http://www.plutinosoft.com</manufacturerURL><modelDescription>Plutinosoft AV Media Server Device</modelDescription><modelName>AV Media Server Device</modelName><modelURL>http://www.plutinosoft.com/platinum</modelURL><UDN>uuid:%@</UDN><dlna:X_DLNADOC xmlns:dlna='urn:schemas-dlna-org:device-1-0'>DMS-1.50</dlna:X_DLNADOC><serviceList><service><serviceType>urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1</serviceType><serviceId>urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar</serviceId><SCPDURL>/X_MS_MediaReceiverRegistrar/%@/scpd.xml</SCPDURL><controlURL>/X_MS_MediaReceiverRegistrar/%@/control.xml</controlURL><eventSubURL>/X_MS_MediaReceiverRegistrar/%@/event.xml</eventSubURL></service><service><serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType><serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId><SCPDURL>/ContentDirectory/%@/scpd.xml</SCPDURL><controlURL>/ContentDirectory/%@/control.xml</controlURL><eventSubURL>/ContentDirectory/%@/event.xml</eventSubURL></service><service><serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType><serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId><SCPDURL>/ConnectionManager/%@/scpd.xml</SCPDURL><controlURL>/ConnectionManager/%@/control.xml</controlURL><eventSubURL>/ConnectionManager/%@/event.xml</eventSubURL></service></serviceList></device></root>",uuid,uuid,uuid,uuid,uuid,uuid,uuid,uuid,uuid,uuid];
}

 

- (NSString *)getDMRDescriptionXml11111:(NSString *)uuid
{
    return [NSString stringWithFormat:@"<?xml version='1.0' encoding='UTF-8'?><root xmlns='urn:schemas-upnp-org:device-1-0' xmlns:dlna='urn:schemas-dlna-org:device-1-0'><specVersion><major>1</major><minor>0</minor></specVersion><device><deviceType>urn:schemas-upnp-org:device:MediaRenderer:1</deviceType><friendlyName>hitv_dmr</friendlyName><manufacturer>Plutinosoft LLC</manufacturer><manufacturerURL>http://www.plutinosoft.com</manufacturerURL><modelDescription>Plutinosoft AV Media Renderer Device</modelDescription><modelName>AV Renderer Device</modelName><modelURL>http://www.plutinosoft.com/platinum</modelURL><serialNumber></serialNumber><UDN>uuid:%@</UDN><dlna:X_DLNADOC xmlns:dlna='urn:schemas-dlna-org:device-1-0'>DMR-1.50</dlna:X_DLNADOC><serviceList><service><serviceType>urn:schemas-upnp-org:service:AVTransport:1</serviceType><serviceId>urn:upnp-org:serviceId:AVTransport</serviceId><SCPDURL>/AVTransport/%@/scpd.xml</SCPDURL><controlURL>/AVTransport/%@/control.xml</controlURL><eventSubURL>/AVTransport/%@/event.xml</eventSubURL></service><service><serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType><serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId><SCPDURL>/ConnectionManager/%@/scpd.xml</SCPDURL><controlURL>/ConnectionManager/%@/control.xml</controlURL><eventSubURL>/ConnectionManager/%@/event.xml</eventSubURL></service><service><serviceType>urn:schemas-upnp-org:service:RenderingControl:1</serviceType><serviceId>urn:upnp-org:serviceId:RenderingControl</serviceId><SCPDURL>/RenderingControl/%@/scpd.xml</SCPDURL><controlURL>/RenderingControl/%@/control.xml</controlURL><eventSubURL>/RenderingControl/%@/event.xml</eventSubURL></service></serviceList></device></root>",uuid,uuid,uuid,uuid,uuid,uuid,uuid,uuid,uuid,uuid];
}
 

   此处的uuid都未设备的uuid。经过xml建立好device后,还须要给device设置friendlyName和loactionurl。在cyberlink库里是没有提供设置locationurl的方法,因此得本身加一个。框架

- (void)setLocationURL:(NSString *)url
{
    if (!cObject) {
        return ;
    }
    cg_upnp_device_setlocationfromssdppacket(cObject, (char *)[url UTF8String]);
}

 这个cg_upnp_device_setlocationfromssdppacket方法也是本身加到库里去的。看起来比较麻烦,其实很简单,就是经过device的sspdPacket去修改loactionurl。改好这些之后,你的device就创建立好了,这时候经过dms或dmr对应的initWithcObject方法就能够建立好dms或者dmr了。为何要本身去建立dms或者dmr呢。这是由于我本身开发的须要,我这边不能使用CGupnpAvController这个类去搜索dms或者dmr,因此只能本身去主动建立了,若是你的需求是作正常的搜索功能,不须要这么去作,可是我说这些是为了讲我对于dms,dmr的一些理解。异步

    如今咱们来看,dmr是怎么播放的。ide

if ([self.renderer setAVTransportWithItem:self.avItem]) {
        [self.renderer playWithUrl:[self.avItem.resourceUrl description]];
        self.isPlay = [self.renderer isPlaying];
    }

 就是这几行代码,dmr就播放了对应的dms的资源文件。跟进去这个setAVTransportWithItem方法,发现它主要是为了获取一个aciton,再跟进去,发现先获取一个service。这个service和咱们的dms是不同的。这个service是一个device的service。咱们再能够去看建立device的xml。xml里包含了一些device的基本信息,同时包含了一个servicelist!就是这个servicelist。

<serviceList><service><serviceType>urn:schemas-upnp-org:service:AVTransport:1</serviceType><serviceId>urn:upnp-org:serviceId:AVTransport</serviceId><SCPDURL>/AVTransport/%@/scpd.xml</SCPDURL><controlURL>/AVTransport/%@/control.xml</controlURL><eventSubURL>/AVTransport/%@/event.xml</eventSubURL></service><service><serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType><serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId><SCPDURL>/ConnectionManager/%@/scpd.xml</SCPDURL><controlURL>/ConnectionManager/%@/control.xml</controlURL><eventSubURL>/ConnectionManager/%@/event.xml</eventSubURL></service><service><serviceType>urn:schemas-upnp-org:service:RenderingControl:1</serviceType><serviceId>urn:upnp-org:serviceId:RenderingControl</serviceId><SCPDURL>/RenderingControl/%@/scpd.xml</SCPDURL><controlURL>/RenderingControl/%@/control.xml</controlURL><eventSubURL>/RenderingControl/%@/event.xml</eventSubURL></service></serviceList>

这下应该明白了,每一个device里都有一个servicelist,经过名称获取到对应的service,而后再经过service获取到你想要的action,而后再把action post出去,咱们的真实设备就能够在局域网里收到这个action,响应动做。包括dms,也是同样的工做原理,经过post action,去获取dms里的文件目录。

    那么咱们要如何给dmr添加咱们想要的快进,快退,调节音量等功能呢?    你仔细看这个servicelist,里面有几个xml的路径,你的locationurl地址在加上xml路径,而后在浏览器里打开(在火狐浏览器打开)。其中一个就有actionlist,actionlist里包含各类aciton,而后你就能够本身去主动获取这些action,而后post出去,去给本身的dmr加本身想要的功能。

    ps:cyberlink里有一些坑,我也记得不太清楚了,能补充多少是多少。

    1:setAVTRansportURI这个方法里面没有去区分item的类别,好比是音乐仍是图片仍是视频,须要本身去改一下。我改为了setAVTransportWithItem,把AVItem传进去,而后去判断item的类型

- (BOOL)setAVTransportWithItem:(CGUpnpAvItem *)item
{
    CGUpnpAction *action = [self actionOfTransportServiceForName:@"SetAVTransportURI"];
    if (!action)
        return NO;
    [action setArgumentValue:@"0" forName:@"InstanceID"];
    [action setArgumentValue:[item.resourceUrl description] forName:@"CurrentURI"];
    NSString *classStr = [NSString string];
    if ([item isAudioClass]) 
        classStr = @"audioItem";
    }
    else if([item isVideoClass])
    {
        classStr = @"videoItem";
    }
    else if([item isImageClass])
    {
        classStr = @"imageItem";
    }
    [action setArgumentValue:[self getCurrentURIMetaData:[item.resourceUrl description] With:classStr] forName:@"CurrentURIMetaData"];
    if (![action post])
        return NO;
    
    return YES;
}

   2:在建立device的时候,其实你的servicelist没有被初始化,具体的缘由是什么我也不清楚,多是框架自己的缺陷。因此须要本身主动的去初始化一次。

首先你去获取service,而后从service里获取action时,你会发现service里的actionlist要么是空,要么只有一个action,跟进去getActionForName方法,把

#ifdef CG_OPTIMIZED_CP_MODE 这行注掉,这个时候它就会去判断是否service被初始化过,没有就初始化一次。固然这样作仍是不保险,最好能本身在建立device后主动初始化一次servicelist。因此我在CGUpnpDevice里添加了一个初始化servicelist的方法

- (void)parisedSCPDUrl
{
    if (!cObject) {
        return ;
    }
    NSArray *servise = self.services;
    for (int i = 0; i < servise.count; i++) {
        CGUpnpService *ser = servise[i];
        cg_upnp_controlpoint_parsescservicescpd(ser.cObject);
    }
}

 这样就没问题了。这样每次当你去获取action的时候,里面servicelist都已经被初始化了,不会有action取不到的状况。

  3:不少关于网络请求的地方都是没有用多线程的,好比去初始化这个servicelist,最好能本身在加一个异步的操做,避免阻塞主线程。

  目前能想到的就这么多了,确实cyberlink坑比较多,一开始完成正常的dms,dmc等功能其实挺快的,可是咱们的需求不同,不容许让dms,dmr暴露在局域网里,因此只能去主动构建,为了这个需求走了好多弯路,一直研究了一个多月才搞定,写篇博文保留一下当时的研究过程。。。。

相关文章
相关标签/搜索