phonypianistのメモ

調査したことなどをメモ代わりに書いています

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>&lt;DIDL-Lite xmlns=&quot;urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/&quot; xmlns:dc=&quot;http://purl.org/dc/elements/1.1/&quot; xmlns:upnp=&quot;urn:schemas-upnp-org:metadata-1-0/upnp/&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xsi:schemaLocation=&quot;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&quot;&gt;(中略)&lt;/DIDL-Lite&gt;</Result>
      <NumberReturned>4</NumberReturned>
      <TotalMatches>4</TotalMatches>
      <UpdateID>1229</UpdateID>
    </u:BrowseResponse>
  </s:Body>
</s:Envelope>

Resultタグの中がさらにXMLデータになっていて、&lt;やら&gt;やらを変換して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すれば、その続きから取得できる。


最初の方のリクエストがややこしいが、コンテンツの一覧さえ取得できれば、あとは繰り返し同じリクエストを使えるため、楽に取得できる。

次回は、コンテンツのURIを取得する。