iOS DLNA Cyberlink,PlatinumKit库完成DLNA功能

来源:互联网 发布:c语言atm机程序 编辑:程序博客网 时间:2024/06/05 18:12

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

   1.DMS

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

   2.DMC,DMR

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

 

    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是怎么播放的。

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暴露在局域网里,所以只能去主动构建,为了这个需求走了好多弯路,一直研究了一个多月才搞定,写篇博文保留一下当时的研究过程。。。。

0 0
原创粉丝点击