不知道怎么联系作者就发这里吧. @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), 导致无法投屏