DLNAサーバ内の探索
前回は、DLNAサーバの一覧を取得した。 今回は、その中からコンテンツを保持するサーバの中を辿ってみる。
サービスの絞り込み
UPnPの応答で、STの項目にContentDirectoryを含むものが、コンテンツを保持するサーバとみなしてよい。 その応答内のLOCATION項目のURIを取得する。
HTTP/1.1 200 OK CACHE-CONTROL: max-age=1800 DATE: Tue, 29 Jan 2019 15:04:52 GMT EXT: LOCATION: http://xxx.xxx.xxx.25:60606/D8AFF1C0AF18/Server0/ddd SERVER: Linux/4.0 UPnP/1.0 Panasonic-UPnP-MW/1.0 ST: urn:schemas-upnp-org:service:ContentDirectory:2 USN: uuid:4D454930-0100-1000-8000-D8AFF1C0AF18::urn:schemas-upnp-org:service:ContentDirectory:2
上記だと「http://xxx.xxx.xxx.25:60606/D8AFF1C0AF18/Server0/ddd」を取得する。 このURIにGETリクエストを送ると、次の応答が返ってくる。
<?xml version="1.0"?> <root xmlns="urn:schemas-upnp-org:device-1-0" xmlns:dlna="urn:schemas-dlna-org:device-1-0" xmlns:vli="urn:schemas-panasonic-com:vli" xmlns:hdlnk="urn:schemas-hdlnk-org:device-1-0" xmlns:sptv="urn:schemas-skyperfectv-co-jp:device-1-0" xmlns:jlabs="urn:schemas-jlabs-or-jp:device-1-0" xmlns:viera="urn:schemas-panasonic-com:viera"> <specVersion> <major>1</major> <minor>0</minor> </specVersion> <device> <deviceType>urn:schemas-upnp-org:device:MediaServer:2</deviceType> <friendlyName>DMR-UBZ2030</friendlyName> <manufacturer>Panasonic</manufacturer> <modelName>BD/DVD Recorder</modelName> <modelNumber>DMR-UBZ2030</modelNumber> (中略) <serviceList> <service> <serviceType>urn:schemas-upnp-org:service:ContentDirectory:2</serviceType> <serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId> <SCPDURL>http://xxx.xxx.xxx.25:60606/Server0/CDS_SCPD</SCPDURL> <controlURL>http://xxx.xxx.xxx.25:60606/Server0/CDS_control</controlURL> <eventSubURL>http://xxx.xxx.xxx.25:60606/Server0/CDS_event</eventSubURL> </service> <service> <serviceType>urn:schemas-upnp-org:service:ConnectionManager:2</serviceType> <serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId> <SCPDURL>http://xxx.xxx.xxx.25:60606/Server0/CMS_SCPD</SCPDURL> <controlURL>http://xxx.xxx.xxx.25:60606/Server0/CMS_control</controlURL> <eventSubURL>http://xxx.xxx.xxx.25:60606/Server0/CMS_event</eventSubURL> </service> <service> <serviceType>urn:schemas-upnp-org:service:ScheduledRecording:1</serviceType> <serviceId>urn:upnp-org:serviceId:ScheduledRecording</serviceId> <SCPDURL>http://xxx.xxx.xxx.25:60606/Server0/SRS_SCPD</SCPDURL> <controlURL>http://xxx.xxx.xxx.25:60606/Server0/SRS_control</controlURL> <eventSubURL>http://xxx.xxx.xxx.25:60606/Server0/SRS_event</eventSubURL> </service> <service> <serviceType>urn:schemas-xsrs-org:service:X_ScheduledRecordingExt:1</serviceType> <serviceId>urn:xsrs-org:serviceId:X_ScheduledRecordingExt</serviceId> <SCPDURL>http://xxx.xxx.xxx.25:60606/Server0/XSRSExt_SCPD</SCPDURL> <controlURL>http://xxx.xxx.xxx.25:60606/Server0/XSRSExt_control</controlURL> <eventSubURL>http://xxx.xxx.xxx.25:60606/Server0/XSRSExt_event</eventSubURL> </service> </serviceList> </device> </root>
前半はサーバの名称等の情報、途中省略している部分はエンコードの種類等の情報、後半はサービスのURIが含まれる。 サービスのURIも複数あるが、今回の目的であるコンテンツを管理するサーバを表すものを絞り込む。 serviceTypeにContentDirectoryが含まれるものが該当し、そのcontrolURLを取得すればよい。 上記だと「http://xxx.xxx.xxx.25:60606/Server0/CDS_control」となる。
コンテンツの探索
ここからは、先ほど取得したURIに対してPOSTリクエストを送り、コンテンツを探索することになる。形式はSOAPっぽい。 送るリクエストボディは次の通り。
<?xml version="1.0"?> <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <s:Body> <u:Browse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:2"> <ObjectID>0</ObjectID> <BrowseFlag>BrowseDirectChildren</BrowseFlag> <Filter>*</Filter> <StartingIndex>0</StartingIndex> <RequestedCount>0</RequestedCount> <SortCriteria></SortCriteria> </u:Browse> </s:Body> </s:Envelope>
<u:Browse> タグのNamespaceは、先の応答結果のserviceTypeと同じにしておく。
レスポンスは次のようなものになる。
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <s:Body> <u:BrowseResponse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:2"> <Result><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/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/ http://www.upnp.org/schemas/av/didl-lite-v2-20060531.xsd urn:schemas-upnp-org:metadata-1-0/upnp/ http://www.upnp.org/schemas/av/upnp-v2-20060531.xsd">(中略)</DIDL-Lite></Result> <NumberReturned>4</NumberReturned> <TotalMatches>4</TotalMatches> <UpdateID>1229</UpdateID> </u:BrowseResponse> </s:Body> </s:Envelope>
Resultタグの中がさらにXMLデータになっていて、<やら>やらを変換してXML解釈することで、中身を確認できる。 上記は中略しているが、解釈した内容は次のようになる。
<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/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/ http://www.upnp.org/schemas/av/didl-lite-v2-20060531.xsd urn:schemas-upnp-org:metadata-1-0/upnp/ http://www.upnp.org/schemas/av/upnp-v2-20060531.xsd"> <container id="HDD" parentID="0" restricted="0"> <dc:title>HDD</dc:title> <upnp:writeStatus>NOT_WRITABLE</upnp:writeStatus> <upnp:recordable>0</upnp:recordable> <upnp:class name="container">object.container</upnp:class> </container> <container id="USB_HDD" parentID="0" restricted="0"> <dc:title>録画用USB-HDD</dc:title> <upnp:writeStatus>NOT_WRITABLE</upnp:writeStatus> <upnp:recordable>0</upnp:recordable> <upnp:class name="container">object.container</upnp:class> </container> <container id="SQV_HDD" parentID="0" restricted="0"> <dc:title>SeeQVault-HDD</dc:title> <upnp:writeStatus>NOT_WRITABLE</upnp:writeStatus> <upnp:recordable>0</upnp:recordable> <upnp:class name="container">object.container</upnp:class> </container> <container id="TUNER" parentID="0" restricted="1"> <dc:title>チューナ</dc:title><upnp:writeStatus>NOT_WRITABLE</upnp:writeStatus> <upnp:recordable>0</upnp:recordable> <upnp:class name="container">object.container</upnp:class> </container> </DIDL-Lite>
containerタグが要素を表していて、そのid属性がキーとなる。 さらにこの下を辿るには、先ほど送ったリクエストボディのObjectIDにcontainerタグのid属性値を設定して、再度同じURIにPOSTすればよい。 配下を辿るのは、この繰り返しとなる。
なお、コンテンツが多い場合は一度にすべてのcontainerを取得できない。 StartingIndex + NumberReturned < TotalMatches の場合は、StartingIndexに値を設定して再度POSTすれば、その続きから取得できる。
最初の方のリクエストがややこしいが、コンテンツの一覧さえ取得できれば、あとは繰り返し同じリクエストを使えるため、楽に取得できる。