
不知道怎么联系作者就发这里吧. @Kivy
最近有机会接触三星的 Tizen TV 系统,发现 Yamby 的 DLNA 投屏功能对三星设备上某些功能不可用(无法播放),但是可以在 KODI 上正常使用,经过各种抓包测试,并且查阅了一些 DLNA 的上古文档,发现问题出在媒体文件的 metadata 上
YAMBY 的 DLNA 报文是这样的
<?xml version="1.0" encoding="utf-8" standalOne="yes"?> <s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <u:SetAVTransportURI xmlns:u="urn:schemas-upnp-org:service:AVTransport:1"> <InstanceID>0</InstanceID> <CurrentURI>http://192.168.0.214:9091/original.mp4?x=y&z=z</CurrentURI> <CurrentURIMetaData><?xml version="1.0"?><DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"><item id="original.mp4" parentID="-1" restricted="1"><dc:title>original.mp4</dc:title><upnp:class>object.item.videoItem</upnp:class><res protocolInfo="http-get:*:video/mp4:*;DLNA.ORG_OP=01;">http://192.168.0.214:9091/original.mp4?x=y&z=z</res></item></DIDL-Lite></CurrentURIMetaData> </u:SetAVTransportURI> </s:Body> </s:Envelope> 对于现代的 DLNA 设备来说 重点需要关注 CurrentURIMetaData
把他解码 是这样的内容
<?xml version="1.0"?> <DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"> <item id="original.mp4" parentID="-1" restricted="1"> <dc:title>original.mp4</dc:title> <upnp:class>object.item.videoItem</upnp:class> <res protocolInfo="http-get:*:video/mp4:*;DLNA.ORG_OP=01;">http://192.168.0.214:9091/original.mp4?x=y&z=z</res> </item> </DIDL-Lite> 然后就会得到三丧电视答复如下内容
<?xml version="1.0" encoding="utf-8"?> <s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <s:Fault> <faultcode>s:Client</faultcode> <faultstring>UPnPError</faultstring> <detail> <UPnPError xmlns="urn:schemas-upnp-org:control-1-0"> <errorCode>402</errorCode> <errorDescription>Invalid Args</errorDescription> </UPnPError> </detail> </s:Fault> </s:Body> </s:Envelope> 其根本原因是因为, 在 XML 节点内的 URL 是需要进行 html 实体转义的, 也就是
CurrentURIMetaData 本身就是一个 html 实体转义后的 XML
其内的 URL 也必须是经过 html 实体转义的, 因为 XML 对某些字符(如 <, >, &, " 等)有严格的限制, 我们使用 YAMBY 主要的场景就是 EMBY 的 stream 拉流播放, 这个接口会通过 Query String 里面传递很多查询参数, 使用了大量的 &, 如果不进行实体转义, 就会对设备的解析造成问题
所以其实是 URL 转义以后的写入 DIDL-Lite ,然后 DIDL-Lite 再转义一次变成上面的样子, 其实就是 URL 被转义了两次.
所以如果一开始报文改成这样, 三星设备就会正确响应
<?xml version="1.0" encoding="utf-8" standalOne="yes"?> <s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <u:SetAVTransportURI xmlns:u="urn:schemas-upnp-org:service:AVTransport:1"> <InstanceID>0</InstanceID> <CurrentURI>http://192.168.0.214:9091/original.mp4?x=y&z=z</CurrentURI> <CurrentURIMetaData><?xml version="1.0"?><DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"><item id="original.mp4" parentID="-1" restricted="1"><dc:title>original.mp4</dc:title><upnp:class>object.item.videoItem</upnp:class><res protocolInfo="http-get:*:video/mp4:*;DLNA.ORG_OP=01;">http://192.168.0.214:9091/original.mkv?x=y&amp;z=z</res></item></DIDL-Lite></CurrentURIMetaData> </u:SetAVTransportURI> </s:Body> </s:Envelope> 另外这版的 Tizen 还有一个问题, 就是如果是 https 的 URL 不可以带端口号, 也就是, 报文里面写 https://example.com:443/example.mp4 是不可以的, 服务器会收到消息和完整的请求, 但是电视会显示错误信息, 并且拒绝播放.
很不幸, YAMBY 就是会带上端口号,哪怕是默认的(比如 https 的 默认端口是 443), 导致无法投屏