[DLNA] Initial implementation of Media Server API
authorAlexander Shalamov <alexander.shalamov@intel.com>
Tue, 25 Feb 2014 13:48:31 +0000 (15:48 +0200)
committerAlexander Shalamov <alexander.shalamov@intel.com>
Tue, 18 Mar 2014 08:46:08 +0000 (10:46 +0200)
This patch provides implementation for Media Server API
http://01org.github.io/cloud-dleyna/mediaserver.html

19 files changed:
examples/index.html
examples/mediaserver.html [new file with mode: 0644]
mediaserver/README.md [new file with mode: 0644]
mediaserver/callbacks.h [new file with mode: 0644]
mediaserver/dbus_interfaces/com.intel.dLeynaServer.Manager.xml [new file with mode: 0644]
mediaserver/dbus_interfaces/com.intel.dLeynaServer.MediaDevice.xml [new file with mode: 0644]
mediaserver/dbus_interfaces/org.gnome.UPnP.MediaContainer2.xml [new file with mode: 0644]
mediaserver/dbus_interfaces/org.gnome.UPnP.MediaObject2.xml [new file with mode: 0644]
mediaserver/mediaserver.cc [new file with mode: 0644]
mediaserver/mediaserver.gyp [new file with mode: 0644]
mediaserver/mediaserver.h [new file with mode: 0644]
mediaserver/mediaserver_api.js [new file with mode: 0644]
mediaserver/mediaserver_extension.cc [new file with mode: 0644]
mediaserver/mediaserver_extension.h [new file with mode: 0644]
mediaserver/mediaserver_instance.cc [new file with mode: 0644]
mediaserver/mediaserver_instance.h [new file with mode: 0644]
mediaserver/mediaserver_manager.cc [new file with mode: 0644]
mediaserver/mediaserver_manager.h [new file with mode: 0644]
tizen-wrt.gyp

index 5be6692..5618563 100644 (file)
@@ -31,5 +31,6 @@ div.block {
 <a href="filesystem.html"><div class="block">filesystem</div></a>
 <a href="application.html"><div class="block">application</div></a>
 <a href="callhistory.html"><div class="block">Call History</div></a>
+<a href="mediaserver.html"><div class="block">mediaserver</div></a>
 </body>
 </html>
diff --git a/examples/mediaserver.html b/examples/mediaserver.html
new file mode 100644 (file)
index 0000000..501e030
--- /dev/null
@@ -0,0 +1,191 @@
+<html>
+<head>
+<title>MediaServer example</title>
+</head>
+<body>
+
+<button onclick='scanNetwork()'>Scan for media servers</button>
+
+<div id='serverContainer'></div>
+<div id='mediaObjectsContainer'></div>
+
+<script>
+navigator.mediaServer.addEventListener('serverfound', onServerFound);
+navigator.mediaServer.addEventListener('serverlost', onServerLost);
+
+var servers = {};
+
+function scanNetwork() {
+  emptyContainer('serverContainer');
+  navigator.mediaServer.scanNetwork();
+}
+
+function getServerId() {
+  return document.getElementById('serverContainer').serverId;
+}
+
+function browseRootContainer(id) {
+  document.getElementById('serverContainer').serverId = id;
+  browseContainer(servers[id].root.id);
+}
+
+function browseContainer(id) {
+  emptyContainer('mediaObjectsContainer');
+  var server = servers[getServerId()];
+  server.browse(id, '', 0 , 0).then(listMediaObjects);
+ }
+
+function searchContainer(id) {
+  emptyContainer('mediaObjectsContainer');
+  document.getElementById('serverContainer').serverId = id;
+  var server = servers[id];
+  var selectedIndex = document.getElementById('searchParameter').selectedIndex;
+  var selectedParameter = document.getElementById('searchParameter').options[selectedIndex].text;
+  var searchValue = document.getElementById('searchField').value;
+  var query = selectedParameter + ' contains ' + '"' + searchValue + '"';
+  server.find(server.root.id, query, '', 0, 0).then(listMediaObjects);
+}
+
+function createContainer(id) {
+  document.getElementById('serverContainer').serverId = id;
+  var server = servers[id];
+  var title = document.getElementById('createContainerField').value;
+  server.createFolder(title).then(onContainerCreated, onCreateFailed);
+}
+
+function onContainerCreated(result) {
+  console.log("Folder successfully created!");
+}
+
+function onCreateFailed(err) {
+  console.log("Cannot create container!");
+}
+
+function upload(id) {
+  document.getElementById('serverContainer').serverId = id;
+  var server = servers[id];
+  var path = document.getElementById('uploadInputField').value;
+  server.upload(path).then(onUploadedComplete, onUploadFailure);
+}
+
+function onUploadedComplete(result) {
+  console.log("Uploaded file!");
+}
+
+function onUploadFailure(err) {
+  console.log("Upload failed!");
+}
+
+function listMediaObjects(response) {
+  var container = document.getElementById('mediaObjectsContainer');
+  for(var i = 0; i < response.length; i++) {
+    if(response[i].type === 'container')
+      addMediaContainerObject(response[i]);
+    else
+      addMediaItemObject(response[i]);
+  }
+}
+
+function addMediaContainerObject(obj) {
+  var container = document.getElementById('mediaObjectsContainer');
+  var newElement = document.createElement('button');
+  newElement.id = obj.id;
+  newElement.innerHTML = 'Browse  ' + obj.title;
+  newElement.setAttribute('onclick', 'browseContainer(this.id)');
+  container.appendChild(newElement);
+}
+
+function addMediaItemObject(obj) {
+  var container = document.getElementById('mediaObjectsContainer');
+  var newElement = document.createElement('div');
+  newElement.innerHTML = obj.title;
+  container.appendChild(newElement);
+
+  if (obj.type === 'image') {
+    var img = document.createElement('img');
+    img.setAttribute('src', obj.sourceUri);
+    img.setAttribute('height', obj.height);
+    img.setAttribute('width', obj.width);
+    container.appendChild(img);
+  } else if (obj.type === 'audio' || obj.type === 'music') {
+    var audio = document.createElement('audio');
+    audio.src = obj.sourceUri;
+    audio.controls=true;
+    container.appendChild(audio);
+  } else if (obj.type === 'video') {
+    var video = document.createElement('video');
+    video.src = obj.sourceUri;
+    video.controls = true;
+    container.appendChild(video);
+  }
+}
+
+function emptyContainer(id) {
+  var container = document.getElementById(id);
+  while(container.hasChildNodes())
+    container.removeChild(container.lastChild);
+}
+
+function onServerFound(event) {
+  var container = document.getElementById('serverContainer');
+  var browseButton = document.createElement('button');
+  servers[event.server.id] = event.server;
+  browseButton.id = event.server.id;
+  browseButton.innerHTML = 'Browse root container of ' + event.server.friendlyName;
+  browseButton.setAttribute('onclick', 'browseRootContainer(this.id)');
+  container.appendChild(browseButton);
+
+  var p = document.createElement('p')
+  var selectElement = document.createElement('select');
+  selectElement.id = 'searchParameter';
+  event.server.searchAttrs.forEach(function(searchAttr) {
+    var option = document.createElement('option');
+    option.text = searchAttr;
+    selectElement.add(option);
+  });
+
+  var label_search = document.createElement('Label');
+  var searchField = document.createElement('input');
+  searchField.id = 'searchField';
+  label_search.for = searchField.id;
+  label_search.innerHTML = 'Search server: ';
+  searchField.serverId = event.server.id;
+  searchField.setAttribute('onkeydown', 'if (event.keyCode == 13) searchContainer(this.serverId)');
+  p.appendChild(label_search);
+  p.appendChild(selectElement);
+  p.appendChild(searchField);
+
+  var label = document.createElement('Label');
+  var createContainerField = document.createElement('input');
+  createContainerField.id = 'createContainerField';
+  label.for = createContainerField.id;
+  label.innerHTML='Create container: ';
+  createContainerField.serverId = event.server.id;
+  createContainerField.setAttribute('onkeydown', 'if (event.keyCode == 13) createContainer(this.serverId)');
+  p.appendChild(document.createElement('br'));
+  p.appendChild(label);
+  p.appendChild(createContainerField);
+
+  var label_upload = document.createElement('Label');
+  var uploadInputField = document.createElement('input');
+  uploadInputField.id = 'uploadInputField';
+  uploadInputField.serverId = event.server.id;
+  label_upload.for = uploadInputField.id;
+  label_upload.innerHTML="Upload file: ";
+  uploadInputField.setAttribute('onkeydown', 'if (event.keyCode == 13) upload(this.serverId)');
+  p.appendChild(document.createElement('br'));
+  p.appendChild(label_upload);
+  p.appendChild(uploadInputField);
+
+  container.appendChild(p);
+}
+
+function onServerLost(event) {
+  var button = document.getElementById(event.id);
+  document.removeChild(button);
+}
+
+</script>
+
+</body>
+</html>
diff --git a/mediaserver/README.md b/mediaserver/README.md
new file mode 100644 (file)
index 0000000..ea59938
--- /dev/null
@@ -0,0 +1,6 @@
+## Introduction
+This folder contains implementation of MediaServer API for Crosswalk runtime.
+API specification can be located at http://01org.github.io/cloud-dleyna/mediaserver.html
+
+Backend that provides core functionality is dLeyna and more information about that
+component could be found at http://01.org/dleyna/
\ No newline at end of file
diff --git a/mediaserver/callbacks.h b/mediaserver/callbacks.h
new file mode 100644 (file)
index 0000000..0c171a1
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIASERVER_CALLBACKS_H_
+#define MEDIASERVER_CALLBACKS_H_
+
+struct CallbackData {
+  CallbackData(void* data, double async_id)
+      : data_(data), async_id_(async_id) {}
+  void* data_;
+  double async_id_;
+};
+
+#define CALLBACK_METHOD(METHOD, SENDER, ARG0, DATA_TYPE)                       \
+  static void METHOD ## CallBack(SENDER sender, ARG0 res, gpointer userdata) { \
+    reinterpret_cast<DATA_TYPE*>(userdata)->METHOD(sender, res);               \
+  }                                                                            \
+                                                                               \
+  void METHOD(SENDER, ARG0);
+
+#define CALLBACK_METHOD_WITH_ID(METHOD, SENDER, ARG0, DATA_TYPE)               \
+  static void METHOD ## CallBack(SENDER sender, ARG0 res, gpointer userdata) { \
+    CallbackData* d = static_cast<CallbackData*>(userdata);                    \
+    DATA_TYPE* data = reinterpret_cast<DATA_TYPE*>(d->data_);                  \
+    if (!data->isCancelled())                                                  \
+      data->METHOD(sender, res, d->async_id_);                                 \
+    delete d;                                                                  \
+  }                                                                            \
+                                                                               \
+  void METHOD(SENDER, ARG0, double async_call_id);
+
+#endif  // MEDIASERVER_CALLBACKS_H_
diff --git a/mediaserver/dbus_interfaces/com.intel.dLeynaServer.Manager.xml b/mediaserver/dbus_interfaces/com.intel.dLeynaServer.Manager.xml
new file mode 100644 (file)
index 0000000..f4b5615
--- /dev/null
@@ -0,0 +1,42 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<!-- GDBus 2.32.4 -->
+<node>
+  <interface name="com.intel.dLeynaServer.Manager">
+    <method name="GetVersion">
+      <arg type="s" name="Version" direction="out">
+      </arg>
+    </method>
+    <method name="Release">
+    </method>
+    <method name="GetServers">
+      <arg type="ao" name="Servers" direction="out">
+      </arg>
+    </method>
+    <method name="Rescan">
+    </method>
+    <method name="SetProtocolInfo">
+      <arg type="s" name="ProtocolInfo" direction="in">
+      </arg>
+    </method>
+    <method name="PreferLocalAddresses">
+      <arg type="b" name="Prefer" direction="in">
+      </arg>
+    </method>
+    <signal name="FoundServer">
+      <arg type="o" name="Path">
+      </arg>
+    </signal>
+    <signal name="LostServer">
+      <arg type="o" name="Path">
+      </arg>
+    </signal>
+    <property type="as" name="NeverQuit" access="readwrite">
+    </property>
+    <property type="as" name="WhiteListEntries" access="readwrite">
+    </property>
+    <property type="b" name="WhiteListEnabled" access="readwrite">
+    </property>
+  </interface>
+  <node name="server"/>
+</node>
diff --git a/mediaserver/dbus_interfaces/com.intel.dLeynaServer.MediaDevice.xml b/mediaserver/dbus_interfaces/com.intel.dLeynaServer.MediaDevice.xml
new file mode 100644 (file)
index 0000000..4b35a3c
--- /dev/null
@@ -0,0 +1,127 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<!-- GDBus 2.32.4 -->
+<node>
+  <interface name="com.intel.dLeynaServer.MediaDevice">
+    <method name="UploadToAnyContainer">
+      <arg type="s" name="DisplayName" direction="in">
+      </arg>
+      <arg type="s" name="FilePath" direction="in">
+      </arg>
+      <arg type="u" name="UploadId" direction="out">
+      </arg>
+      <arg type="o" name="Path" direction="out">
+      </arg>
+    </method>
+    <method name="GetUploadStatus">
+      <arg type="u" name="UploadId" direction="in">
+      </arg>
+      <arg type="s" name="UploadStatus" direction="out">
+      </arg>
+      <arg type="t" name="Length" direction="out">
+      </arg>
+      <arg type="t" name="Total" direction="out">
+      </arg>
+    </method>
+    <method name="GetUploadIDs">
+      <arg type="au" name="Total" direction="out">
+      </arg>
+    </method>
+    <method name="CancelUpload">
+      <arg type="u" name="UploadId" direction="in">
+      </arg>
+    </method>
+    <method name="CreateContainerInAnyContainer">
+      <arg type="s" name="DisplayName" direction="in">
+      </arg>
+      <arg type="s" name="Type" direction="in">
+      </arg>
+      <arg type="as" name="ChildTypes" direction="in">
+      </arg>
+      <arg type="o" name="Path" direction="out">
+      </arg>
+    </method>
+    <method name="Cancel">
+    </method>
+    <method name="Wake">
+    </method>
+    <method name="GetIcon">
+      <arg type="s" name="RequestedMimeType" direction="in">
+      </arg>
+      <arg type="s" name="Resolution" direction="in">
+      </arg>
+      <arg type="ay" name="Bytes" direction="out">
+      </arg>
+      <arg type="s" name="MimeType" direction="out">
+      </arg>
+    </method>
+    <method name="BrowseObjects">
+      <arg type="ao" name="Objects" direction="in">
+      </arg>
+      <arg type="as" name="Filter" direction="in">
+      </arg>
+      <arg type="aa{sv}" name="Children" direction="out">
+      </arg>
+    </method>
+    <signal name="ContainerUpdateIDs">
+      <arg type="a(ou)" name="ContainerPathsIDs">
+      </arg>
+    </signal>
+    <signal name="Changed">
+      <arg type="aa{sv}" name="ChangedObjects">
+      </arg>
+    </signal>
+    <signal name="UploadUpdate">
+      <arg type="u" name="UploadId">
+      </arg>
+      <arg type="s" name="UploadStatus">
+      </arg>
+      <arg type="t" name="Length">
+      </arg>
+      <arg type="t" name="Total">
+      </arg>
+    </signal>
+    <property type="s" name="Location" access="read">
+    </property>
+    <property type="s" name="UDN" access="read">
+    </property>
+    <property type="s" name="DeviceType" access="read">
+    </property>
+    <property type="s" name="FriendlyName" access="read">
+    </property>
+    <property type="s" name="Manufacturer" access="read">
+    </property>
+    <property type="s" name="ManufacturerUrl" access="read">
+    </property>
+    <property type="s" name="ModelDescription" access="read">
+    </property>
+    <property type="s" name="ModelName" access="read">
+    </property>
+    <property type="s" name="ModelNumber" access="read">
+    </property>
+    <property type="s" name="ModelURL" access="read">
+    </property>
+    <property type="s" name="SerialNumber" access="read">
+    </property>
+    <property type="s" name="PresentationURL" access="read">
+    </property>
+    <property type="s" name="IconURL" access="read">
+    </property>
+    <property type="b" name="Sleeping" access="read">
+    </property>
+    <property type="a{sv}" name="DLNACaps" access="read">
+    </property>
+    <property type="as" name="SearchCaps" access="read">
+    </property>
+    <property type="as" name="SortCaps" access="read">
+    </property>
+    <property type="as" name="SortExtCaps" access="read">
+    </property>
+    <property type="a(ssao)" name="FeatureList" access="read">
+    </property>
+    <property type="u" name="SystemUpdateID" access="read">
+    </property>
+    <property type="s" name="ServiceResetToken" access="read">
+    </property>
+  </interface>
+</node>
diff --git a/mediaserver/dbus_interfaces/org.gnome.UPnP.MediaContainer2.xml b/mediaserver/dbus_interfaces/org.gnome.UPnP.MediaContainer2.xml
new file mode 100644 (file)
index 0000000..ae7c26d
--- /dev/null
@@ -0,0 +1,161 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<!-- GDBus 2.32.4 -->
+<node>
+  <interface name="org.gnome.UPnP.MediaContainer2">
+    <method name="ListChildren">
+      <arg type="u" name="Offset" direction="in">
+      </arg>
+      <arg type="u" name="Max" direction="in">
+      </arg>
+      <arg type="as" name="Filter" direction="in">
+      </arg>
+      <arg type="aa{sv}" name="Children" direction="out">
+      </arg>
+    </method>
+    <method name="ListChildrenEx">
+      <arg type="u" name="Offset" direction="in">
+      </arg>
+      <arg type="u" name="Max" direction="in">
+      </arg>
+      <arg type="as" name="Filter" direction="in">
+      </arg>
+      <arg type="s" name="SortBy" direction="in">
+      </arg>
+      <arg type="aa{sv}" name="Children" direction="out">
+      </arg>
+    </method>
+    <method name="ListContainers">
+      <arg type="u" name="Offset" direction="in">
+      </arg>
+      <arg type="u" name="Max" direction="in">
+      </arg>
+      <arg type="as" name="Filter" direction="in">
+      </arg>
+      <arg type="aa{sv}" name="Children" direction="out">
+      </arg>
+    </method>
+    <method name="ListContainersEx">
+      <arg type="u" name="Offset" direction="in">
+      </arg>
+      <arg type="u" name="Max" direction="in">
+      </arg>
+      <arg type="as" name="Filter" direction="in">
+      </arg>
+      <arg type="s" name="SortBy" direction="in">
+      </arg>
+      <arg type="aa{sv}" name="Children" direction="out">
+      </arg>
+    </method>
+    <method name="ListItems">
+      <arg type="u" name="Offset" direction="in">
+      </arg>
+      <arg type="u" name="Max" direction="in">
+      </arg>
+      <arg type="as" name="Filter" direction="in">
+      </arg>
+      <arg type="aa{sv}" name="Children" direction="out">
+      </arg>
+    </method>
+    <method name="ListItemsEx">
+      <arg type="u" name="Offset" direction="in">
+      </arg>
+      <arg type="u" name="Max" direction="in">
+      </arg>
+      <arg type="as" name="Filter" direction="in">
+      </arg>
+      <arg type="s" name="SortBy" direction="in">
+      </arg>
+      <arg type="aa{sv}" name="Children" direction="out">
+      </arg>
+    </method>
+    <method name="SearchObjects">
+      <arg type="s" name="Query" direction="in">
+      </arg>
+      <arg type="u" name="Offset" direction="in">
+      </arg>
+      <arg type="u" name="Max" direction="in">
+      </arg>
+      <arg type="as" name="Filter" direction="in">
+      </arg>
+      <arg type="aa{sv}" name="Children" direction="out">
+      </arg>
+    </method>
+    <method name="SearchObjectsEx">
+      <arg type="s" name="Query" direction="in">
+      </arg>
+      <arg type="u" name="Offset" direction="in">
+      </arg>
+      <arg type="u" name="Max" direction="in">
+      </arg>
+      <arg type="as" name="Filter" direction="in">
+      </arg>
+      <arg type="s" name="SortBy" direction="in">
+      </arg>
+      <arg type="aa{sv}" name="Children" direction="out">
+      </arg>
+      <arg type="u" name="TotalItems" direction="out">
+      </arg>
+    </method>
+    <method name="Upload">
+      <arg type="s" name="DisplayName" direction="in">
+      </arg>
+      <arg type="s" name="FilePath" direction="in">
+      </arg>
+      <arg type="u" name="UploadId" direction="out">
+      </arg>
+      <arg type="o" name="Path" direction="out">
+      </arg>
+    </method>
+    <method name="CreateContainer">
+      <arg type="s" name="DisplayName" direction="in">
+      </arg>
+      <arg type="s" name="Type" direction="in">
+      </arg>
+      <arg type="as" name="ChildTypes" direction="in">
+      </arg>
+      <arg type="o" name="Path" direction="out">
+      </arg>
+    </method>
+    <method name="GetCompatibleResource">
+      <arg type="s" name="ProtocolInfo" direction="in">
+      </arg>
+      <arg type="as" name="Filter" direction="in">
+      </arg>
+      <arg type="a{sv}" name="Properties" direction="out">
+      </arg>
+    </method>
+    <method name="CreateReference">
+      <arg type="o" name="Path" direction="in">
+      </arg>
+      <arg type="o" name="RefPath" direction="out">
+      </arg>
+    </method>
+    <property type="u" name="ChildCount" access="read">
+    </property>
+    <property type="b" name="Searchable" access="read">
+    </property>
+    <property type="a(sb)" name="CreateClasses" access="read">
+    </property>
+    <property type="u" name="ContainerUpdateID" access="read">
+    </property>
+    <property type="u" name="TotalDeletedChildCount" access="read">
+    </property>
+    <property type="aa{sv}" name="Resources" access="read">
+    </property>
+    <property type="as" name="URLs" access="read">
+    </property>
+    <property type="s" name="MIMEType" access="read">
+    </property>
+    <property type="s" name="DLNAProfile" access="read">
+    </property>
+    <property type="a{sb}" name="DLNAConversion" access="read">
+    </property>
+    <property type="a{sb}" name="DLNAOperation" access="read">
+    </property>
+    <property type="a{sb}" name="DLNAFlags" access="read">
+    </property>
+    <property type="x" name="Size" access="read">
+    </property>
+  </interface>
+</node>
diff --git a/mediaserver/dbus_interfaces/org.gnome.UPnP.MediaObject2.xml b/mediaserver/dbus_interfaces/org.gnome.UPnP.MediaObject2.xml
new file mode 100644 (file)
index 0000000..d7c6a65
--- /dev/null
@@ -0,0 +1,37 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<!-- GDBus 2.32.4 -->
+<node>
+  <interface name="org.gnome.UPnP.MediaObject2">
+    <method name="Delete">
+    </method>
+    <method name="Update">
+      <arg type="a{sv}" name="ToAddUpdate" direction="in">
+      </arg>
+      <arg type="as" name="ToDelete" direction="in">
+      </arg>
+    </method>
+    <method name="GetMetaData">
+      <arg type="s" name="MetaData" direction="out">
+      </arg>
+    </method>
+    <property type="o" name="Parent" access="read">
+    </property>
+    <property type="s" name="Type" access="read">
+    </property>
+    <property type="s" name="TypeEx" access="read">
+    </property>
+    <property type="o" name="Path" access="read">
+    </property>
+    <property type="s" name="DisplayName" access="read">
+    </property>
+    <property type="s" name="Creator" access="read">
+    </property>
+    <property type="b" name="Restricted" access="read">
+    </property>
+    <property type="a{sb}" name="DLNAManaged" access="read">
+    </property>
+    <property type="u" name="ObjectUpdateID" access="read">
+    </property>
+  </interface>
+</node>
diff --git a/mediaserver/mediaserver.cc b/mediaserver/mediaserver.cc
new file mode 100644 (file)
index 0000000..2e5da42
--- /dev/null
@@ -0,0 +1,596 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mediaserver/mediaserver.h"
+
+#include <map>
+#include "common/extension.h"
+
+namespace {
+
+const gchar kdLeynaInterfaceName[] = "com.intel.dleyna-server";
+
+const gchar kMediaObjectPath[] = "Path";
+const gchar kMediaObjectType[] = "Type";
+const gchar kMediaObjectDisplayName[] = "DisplayName";
+const gchar kMediaObjectURLs[] = "URLs";
+const gchar kMediaObjectMIMEType[] = "MIMEType";
+const gchar kMediaObjectDate[] = "Date";
+const gchar kMediaObjectSize[] = "Size";
+const gchar kMediaObjectWidth[] = "Width";
+const gchar kMediaObjectHeight[] = "Height";
+const gchar kMediaObjectDuration[] = "Duration";
+const gchar kMediaObjectBitrate[] = "Bitrate";
+const gchar kMediaObjectAlbum[] = "Album";
+const gchar kMediaObjectArtist[] = "Artist";
+const gchar kMediaObjectGenre[] = "Genre";
+const gchar kMediaObjectTrackNumber[] = "TrackNumber";
+
+// map between GUPnP and w3c spec names
+const std::map<std::string, std::string> g_gupnp_w3c_map = {
+  {kMediaObjectPath, "id"},
+  {kMediaObjectType, "type"},
+  {kMediaObjectDisplayName, "title"},
+  {kMediaObjectURLs, "sourceUri"},
+  {kMediaObjectMIMEType, "mimeType"},
+  {kMediaObjectDate, "createDate"},
+  {kMediaObjectSize, "fileSize"},
+  {kMediaObjectWidth, "width"},
+  {kMediaObjectHeight, "height"},
+  {kMediaObjectDuration, "duration"},
+  {kMediaObjectBitrate, "audioSampleRate"},
+  {kMediaObjectAlbum, "collection"},
+  {kMediaObjectArtist, "author"},
+  {kMediaObjectGenre, "category"},
+  {kMediaObjectTrackNumber, "trackNumber"}
+};
+
+const gchar kMediaObjectTypeContainer[] = "container";
+const gchar kMediaObjectTypeVideo[] = "video";
+const gchar kMediaObjectTypeAudio[] = "audio";
+const gchar kMediaObjectTypeImage[] = "image";
+
+const gchar *const kArgFilter[] = {
+    kMediaObjectPath,
+    kMediaObjectType,
+    kMediaObjectDisplayName,
+    kMediaObjectURLs,
+    kMediaObjectMIMEType,
+    kMediaObjectDate,
+    kMediaObjectSize,
+    kMediaObjectWidth,
+    kMediaObjectHeight,
+    kMediaObjectDuration,
+    kMediaObjectBitrate,
+    kMediaObjectAlbum,
+    kMediaObjectArtist,
+    kMediaObjectGenre,
+    kMediaObjectTrackNumber,
+    NULL
+};
+
+const gchar *const kContainerObjectTypes[] = { "*", NULL };
+
+}  // namespace
+
+static void setChildCount(picojson::value::object& container,
+                          const std::string& object_path) {
+  GError* gerror = NULL;
+  upnpMediaContainer2* root_container_proxy =
+      upnp_media_container2_proxy_new_for_bus_sync(
+      G_BUS_TYPE_SESSION,
+      G_DBUS_PROXY_FLAGS_NONE,
+      kdLeynaInterfaceName,
+      object_path.c_str(),
+      NULL,
+      &gerror);
+
+  if (!gerror) {
+    container["childCount"] = picojson::value(
+        static_cast<double>(
+            upnp_media_container2_get_child_count(root_container_proxy)));
+    g_object_unref(root_container_proxy);
+  } else {
+    // child count may be null.
+    g_error_free(gerror);
+  }
+}
+
+static picojson::value variantToJSON(GVariant* variant) {
+  picojson::value value;
+  switch (g_variant_classify(variant)) {
+    case G_VARIANT_CLASS_STRING:
+    case G_VARIANT_CLASS_OBJECT_PATH:
+    {
+      gsize length = 0;
+      const gchar* str = g_variant_get_string(variant, &length);
+      if (length)
+        value = picojson::value(str);
+    }
+      break;
+
+    case G_VARIANT_CLASS_INT32:
+      value =
+          picojson::value(static_cast<double>(g_variant_get_int32(variant)));
+      break;
+
+    case G_VARIANT_CLASS_INT64:
+      value =
+          picojson::value(static_cast<double>(g_variant_get_int64(variant)));
+      break;
+
+    // Only used for URLs, spec supports only 1 URL
+    case G_VARIANT_CLASS_ARRAY:
+    {
+      GVariantIter* it;
+      gchar* str = NULL;
+      g_variant_get(variant, "as", &it);
+      if (g_variant_iter_loop(it, "s", &str)) {
+        g_variant_iter_free(it);
+        value = picojson::value(str);
+      }
+    }
+      break;
+
+    default:
+      std::cerr << "Cannot process GVariant with type: "
+                << g_variant_get_type_string(variant) << "\n";
+      break;
+  }
+
+  return value;
+}
+
+static picojson::value toJSONValue(const gchar* value) {
+  return value ? picojson::value(value) : picojson::value();
+}
+
+static picojson::value toJSONValueArray(const gchar* const* values) {
+  picojson::array array;
+  while (*values)
+    array.push_back(picojson::value(*values++));
+  return picojson::value(array);
+}
+
+MediaServer::MediaServer(common::Instance* instance,
+                         const std::string& object_path)
+    : instance_(instance),
+      mediadevice_proxy_(0),
+      object_path_(object_path),
+      cancellable_(g_cancellable_new()) {
+  GError* gerror = NULL;
+  mediadevice_proxy_ = dleyna_media_device_proxy_new_for_bus_sync(
+      G_BUS_TYPE_SESSION,
+      G_DBUS_PROXY_FLAGS_NONE,
+      kdLeynaInterfaceName,
+      object_path.c_str(),
+      NULL,
+      &gerror);
+
+  if (gerror) {
+    g_error_free(gerror);
+    return;
+  }
+}
+
+MediaServer::~MediaServer() {
+  g_object_unref(mediadevice_proxy_);
+}
+
+picojson::value MediaServer::toJSON() {
+  if (!object_.size()) {
+    object_["id"] = toJSONValue(
+        dleyna_media_device_get_location(mediadevice_proxy_));
+    object_["friendlyName"] = toJSONValue(
+        dleyna_media_device_get_friendly_name(mediadevice_proxy_));
+    object_["manufacturer"] = toJSONValue(
+        dleyna_media_device_get_manufacturer(mediadevice_proxy_));
+    object_["manufacturerURL"] = toJSONValue(
+        dleyna_media_device_get_manufacturer_url(mediadevice_proxy_));
+    object_["modelDescription"] = toJSONValue(
+        dleyna_media_device_get_model_description(mediadevice_proxy_));
+    object_["modelName"] = toJSONValue(
+        dleyna_media_device_get_model_name(mediadevice_proxy_));
+    object_["modelNumber"] = toJSONValue(
+        dleyna_media_device_get_model_number(mediadevice_proxy_));
+    object_["serialNumber"] = toJSONValue(
+        dleyna_media_device_get_serial_number(mediadevice_proxy_));
+    object_["UDN"] = toJSONValue(
+        dleyna_media_device_get_udn(mediadevice_proxy_));
+    object_["presentationURL"] = toJSONValue(
+        dleyna_media_device_get_presentation_url(mediadevice_proxy_));
+    object_["iconURL"] = toJSONValue(
+        dleyna_media_device_get_icon_url(mediadevice_proxy_));
+    object_["deviceType"] = toJSONValue(
+        dleyna_media_device_get_device_type(mediadevice_proxy_));
+
+    picojson::value::object rootContainer;
+    rootContainer["id"] = picojson::value(object_path_);
+    rootContainer["title"] = picojson::value("root");
+    rootContainer["type"] = picojson::value(kMediaObjectTypeContainer);
+    setChildCount(rootContainer, object_path_);
+
+    object_["root"] = picojson::value(rootContainer);
+    object_["canCreateContainer"] = picojson::value("true");
+    object_["canUpload"] = picojson::value("true");
+    object_["searchAttrs"] = toJSONValueArray(
+        dleyna_media_device_get_search_caps(mediadevice_proxy_));
+    object_["sortAttrs"] = toJSONValueArray(
+        dleyna_media_device_get_sort_caps(mediadevice_proxy_));
+  }
+
+  return picojson::value(object_);
+}
+
+picojson::value MediaServer::mediaObjectToJSON(GVariant* variant) {
+  GVariantIter iter;
+  GVariant *value;
+  gchar *key;
+  bool is_container = false;
+  picojson::value::object object;
+  std::map<std::string, std::string>::const_iterator it;
+  g_variant_iter_init(&iter, variant);
+  while (g_variant_iter_next(&iter, "{sv}", &key, &value)) {
+    if ((it = g_gupnp_w3c_map.find(key)) != g_gupnp_w3c_map.end()) {
+      object[(*it).second] = variantToJSON(value);
+      if (!g_strcmp0(kMediaObjectType, key)) {
+        is_container = true;
+        object["rootContainerId"] = picojson::value(
+            g_dbus_proxy_get_object_path(
+                reinterpret_cast<GDBusProxy*>(mediadevice_proxy_)));
+      }
+    }
+    g_variant_unref(value);
+    g_free(key);
+  }
+
+  if (is_container)
+    setChildCount(object, object["id"].get<std::string>());
+
+  return picojson::value(object);
+}
+
+void MediaServer::browse(const picojson::value& value) {
+  GError* gerror = NULL;
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  upnpMediaContainer2* container_proxy =
+      upnp_media_container2_proxy_new_for_bus_sync(
+          G_BUS_TYPE_SESSION,
+          G_DBUS_PROXY_FLAGS_NONE,
+          kdLeynaInterfaceName,
+          value.get("containerId").to_str().c_str(),
+          NULL,
+          &gerror);
+
+  if (gerror) {
+    postError(async_call_id);
+    g_error_free(gerror);
+    return;
+  }
+
+  upnp_media_container2_call_list_children_ex(
+      container_proxy,
+      value.get("offset").get<double>(),
+      value.get("count").get<double>(),
+      kArgFilter,
+      value.get("sortMode").to_str().c_str(),
+      cancellable_,
+      OnBrowseCallBack,
+      new CallbackData(this, async_call_id));
+}
+
+void MediaServer::find(const picojson::value& value) {
+  GError* gerror = NULL;
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  upnpMediaContainer2* container_proxy =
+      upnp_media_container2_proxy_new_for_bus_sync(
+          G_BUS_TYPE_SESSION,
+          G_DBUS_PROXY_FLAGS_NONE,
+          kdLeynaInterfaceName,
+          value.get("containerId").to_str().c_str(),
+          NULL,
+          &gerror);
+
+  if (gerror) {
+    postError(async_call_id);
+    g_error_free(gerror);
+    return;
+  }
+
+  if (!upnp_media_container2_get_searchable(container_proxy)) {
+    postError(async_call_id);
+    return;
+  }
+
+  upnp_media_container2_call_search_objects_ex(
+      container_proxy,
+      value.get("searchFilter").to_str().c_str(),
+      value.get("offset").get<double>(),
+      value.get("count").get<double>(),
+      kArgFilter,
+      value.get("sortMode").to_str().c_str(),
+      cancellable_,
+      OnFindCallBack,
+      new CallbackData(this, async_call_id));
+}
+
+void MediaServer::createFolder(const picojson::value& value) {
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  if (!mediadevice_proxy_) {
+    postError(async_call_id);
+    return;
+  }
+
+  dleyna_media_device_call_create_container_in_any_container(
+      mediadevice_proxy_,
+      value.get("folderName").to_str().c_str(),
+      kMediaObjectTypeContainer,
+      kContainerObjectTypes,
+      cancellable_,
+      OnCreateFolderCallBack,
+      new CallbackData(this, async_call_id));
+}
+
+void MediaServer::upload(const picojson::value& value) {
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  if (!mediadevice_proxy_) {
+    postError(async_call_id);
+    return;
+  }
+
+  dleyna_media_device_call_upload_to_any_container(
+      mediadevice_proxy_,
+      value.get("title").to_str().c_str(),
+      value.get("path").to_str().c_str(),
+      cancellable_,
+      OnUploadCallBack,
+      new CallbackData(this, async_call_id));
+}
+
+void MediaServer::cancel(const picojson::value& value) {
+  g_cancellable_cancel(cancellable_);
+  postResult("cancelCompleted", value.get("asyncCallId").get<double>());
+}
+
+bool MediaServer::isCancelled() const {
+  return g_cancellable_is_cancelled(cancellable_);
+}
+
+void MediaServer::uploadToContainer(const picojson::value& value) {
+  GError* gerror = NULL;
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  upnpMediaContainer2* container_proxy =
+      upnp_media_container2_proxy_new_for_bus_sync(
+          G_BUS_TYPE_SESSION,
+          G_DBUS_PROXY_FLAGS_NONE,
+          kdLeynaInterfaceName,
+          value.get("containerId").to_str().c_str(),
+          NULL,
+          &gerror);
+
+  if (gerror) {
+    postError(async_call_id);
+    g_error_free(gerror);
+    return;
+  }
+
+  upnp_media_container2_call_upload(
+      container_proxy,
+      value.get("title").to_str().c_str(),
+      value.get("path").to_str().c_str(),
+      cancellable_,
+      OnUploadToContainerCallBack,
+      new CallbackData(this, async_call_id));
+}
+
+void MediaServer::createFolderInContainer(const picojson::value& value) {
+  GError* gerror = NULL;
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  upnpMediaContainer2* container_proxy =
+      upnp_media_container2_proxy_new_for_bus_sync(
+          G_BUS_TYPE_SESSION,
+          G_DBUS_PROXY_FLAGS_NONE,
+          kdLeynaInterfaceName,
+          value.get("containerId").to_str().c_str(),
+          NULL,
+          &gerror);
+
+  if (gerror) {
+    postError(async_call_id);
+    g_error_free(gerror);
+    return;
+  }
+
+  upnp_media_container2_call_create_container(
+      container_proxy,
+      value.get("title").to_str().c_str(),
+      kMediaObjectTypeContainer,
+      kContainerObjectTypes,
+      cancellable_,
+      OnCreateFolderInContainerCallBack,
+      new CallbackData(this, async_call_id));
+}
+
+void MediaServer::postResult(
+    GVariant* objects,
+    const char* completed_operation,
+    double async_operation_id) {
+  GVariantIter it;
+  GVariant *variant;
+  picojson::array json_objects;
+
+  g_variant_iter_init(&it, objects);
+  while (variant = g_variant_iter_next_value(&it)) {
+    json_objects.push_back(mediaObjectToJSON(variant));
+    g_variant_unref(variant);
+  }
+  g_variant_unref(objects);
+
+  picojson::value::object object;
+  object["cmd"] = picojson::value(completed_operation);
+  object["asyncCallId"] = picojson::value(async_operation_id);
+  object["mediaObjects"] = picojson::value(json_objects);
+  picojson::value value(object);
+  instance_->PostMessage(value.serialize().c_str());
+}
+
+void MediaServer::postResult(
+    const char* completed_operation,
+    double async_operation_id) {
+  picojson::value::object object;
+  object["cmd"] = picojson::value(completed_operation);
+  object["asyncCallId"] = picojson::value(async_operation_id);
+  picojson::value value(object);
+  instance_->PostMessage(value.serialize().c_str());
+}
+
+void MediaServer::postError(double async_operation_id) {
+  picojson::value::object object;
+  object["cmd"] = picojson::value("asyncCallError");
+  object["asyncCallId"] = picojson::value(async_operation_id);
+  picojson::value value(object);
+  instance_->PostMessage(value.serialize().c_str());
+}
+
+void MediaServer::OnBrowse(
+    GObject *source_object,
+    GAsyncResult *res,
+    double async_id) {
+  GError* gerror = NULL;
+  GVariant* objects;
+  if (upnp_media_container2_call_list_children_ex_finish(
+      reinterpret_cast<upnpMediaContainer2*>(source_object),
+      &objects,
+      res,
+      &gerror))
+    postResult(objects, "browseCompleted", async_id);
+  else
+    postError(async_id);
+
+  if (gerror)
+    g_error_free(gerror);
+
+  g_object_unref(source_object);
+}
+
+void MediaServer::OnFind(
+    GObject *source_object,
+    GAsyncResult *res,
+    double async_id) {
+  GError* gerror = NULL;
+  GVariant* objects;
+  guint totalItems;
+
+  if (upnp_media_container2_call_search_objects_ex_finish(
+      reinterpret_cast<upnpMediaContainer2*>(source_object),
+      &objects,
+      &totalItems,
+      res,
+      &gerror))
+    postResult(objects, "findCompleted", async_id);
+  else
+    postError(async_id);
+
+  if (gerror)
+    g_error_free(gerror);
+
+  g_object_unref(source_object);
+}
+
+void MediaServer::OnCreateFolder(
+    GObject *source_object,
+    GAsyncResult *res,
+    double async_id) {
+  GError* gerror = NULL;
+  gchar **out_Paths;
+
+  if (dleyna_media_device_call_create_container_in_any_container_finish(
+      mediadevice_proxy_,
+      out_Paths,
+      res,
+      &gerror))
+    postResult("createFolderCompleted", async_id);
+  else
+    postError(async_id);
+
+  if (gerror)
+    g_error_free(gerror);
+}
+
+void MediaServer::OnUpload(
+    GObject *source_object,
+    GAsyncResult *res,
+    double async_id) {
+  GError* gerror = NULL;
+  guint upload_id;
+  gchar **out_Path;
+
+  if (dleyna_media_device_call_upload_to_any_container_finish(
+      mediadevice_proxy_,
+      &upload_id,
+      out_Path,
+      res,
+      &gerror))
+    postResult("uploadCompleted", async_id);
+  else
+    postError(async_id);
+
+  if (gerror) {
+    g_error_free(gerror);
+  }
+}
+
+void MediaServer::OnUploadToContainer(
+    GObject *source_object,
+    GAsyncResult *res,
+    double async_id) {
+  GError* gerror = NULL;
+  guint out_UploadId;
+  gchar **out_Path;
+
+  if (upnp_media_container2_call_upload_finish(
+      reinterpret_cast<upnpMediaContainer2*>(source_object),
+      &out_UploadId,
+      out_Path,
+      res,
+      &gerror))
+    postResult("uploadToContainerCompleted", async_id);
+  else
+    postError(async_id);
+
+  if (gerror) {
+    g_error_free(gerror);
+  }
+
+  g_object_unref(source_object);
+}
+
+void MediaServer::OnCreateFolderInContainer(
+    GObject *source_object,
+    GAsyncResult *res,
+    double async_id) {
+  GError* gerror = NULL;
+  gchar **out_Path;
+
+  if (upnp_media_container2_call_create_container_finish(
+      reinterpret_cast<upnpMediaContainer2*>(source_object),
+      out_Path,
+      res,
+      &gerror))
+    postResult("createFolderInContainerCompleted", async_id);
+  else
+    postError(async_id);
+
+  if (gerror) {
+    g_error_free(gerror);
+  }
+
+  g_object_unref(source_object);
+}
diff --git a/mediaserver/mediaserver.gyp b/mediaserver/mediaserver.gyp
new file mode 100644 (file)
index 0000000..3ef371d
--- /dev/null
@@ -0,0 +1,169 @@
+{
+  'includes':[
+    '../common/common.gypi',
+  ],
+  'variables': {
+    'gen_dbus_proxy_path': '<(SHARED_INTERMEDIATE_DIR)/mediaserver',
+  },
+  'targets': [
+    {
+      'target_name': 'tizen_mediaserver',
+      'type': 'loadable_module',
+      'variables': {
+        'packages': [
+          'gio-2.0',
+          'gio-unix-2.0',
+        ],
+      },
+      'dependencies': [
+        'tizen_mediaserver_gen',
+      ],
+      'sources': [
+        'mediaserver_api.js',
+        'mediaserver.cc',
+        'mediaserver.h',
+        'mediaserver_extension.cc',
+        'mediaserver_extension.h',
+        'mediaserver_instance.cc',
+        'mediaserver_instance.h',
+        'mediaserver_manager.cc',
+        'mediaserver_manager.h',
+        '../common/extension.cc',
+        '../common/extension.h',
+      ],
+      'includes': [
+        '../common/pkg-config.gypi',
+      ],
+    },
+    {
+      'target_name': 'tizen_mediaserver_gen',
+      'type': 'static_library',
+      'variables': {
+        'packages': [
+          'gio-2.0',
+          'gio-unix-2.0',
+        ],
+      },
+      'include_dirs': [
+        './',
+      ],
+      'actions': [
+        {
+          'variables': {
+            'generate_args': [
+              '--interface-prefix',
+              'com.intel.dLeynaServer.',
+              '--c-namespace',
+              'dleyna',
+              '--generate-c-code',
+              '<(gen_dbus_proxy_path)/dleyna_manager_gen',
+            ],
+          },
+          'action_name': 'dleyna_manager_gen',
+          'inputs': [
+            'dbus_interfaces/com.intel.dLeynaServer.Manager.xml',
+          ],
+          'outputs': [
+            '<(gen_dbus_proxy_path)/dleyna_manager_gen.c',
+            '<(gen_dbus_proxy_path)/dleyna_manager_gen.h',
+          ],
+          'action': [
+            'gdbus-codegen',
+            '<@(generate_args)',
+            '<@(_inputs)',
+          ],
+        },
+        {
+          'variables': {
+            'generate_args': [
+              '--interface-prefix',
+              'com.intel.dLeynaServer.',
+              '--c-namespace',
+              'dleyna',
+              '--generate-c-code',
+              '<(gen_dbus_proxy_path)/dleyna_media_device_gen',
+            ],
+          },
+          'action_name': 'dleyna_media_device_gen',
+          'inputs': [
+            'dbus_interfaces/com.intel.dLeynaServer.MediaDevice.xml',
+          ],
+          'outputs': [
+            '<(gen_dbus_proxy_path)/dleyna_media_device_gen.c',
+            '<(gen_dbus_proxy_path)/dleyna_media_device_gen.h',
+          ],
+          'action': [
+            'gdbus-codegen',
+            '<@(generate_args)',
+            '<@(_inputs)',
+          ],
+        },
+        {
+          'variables': {
+            'generate_args': [
+              '--interface-prefix',
+              'org.gnome.UPnP.',
+              '--c-namespace',
+              'upnp',
+              '--generate-c-code',
+              '<(gen_dbus_proxy_path)/upnp_media_container_gen',
+            ],
+          },
+          'action_name': 'upnp_media_container_gen',
+          'inputs': [
+            'dbus_interfaces/org.gnome.UPnP.MediaContainer2.xml',
+          ],
+          'outputs': [
+            '<(gen_dbus_proxy_path)/upnp_media_container_gen.c',
+            '<(gen_dbus_proxy_path)/upnp_media_container_gen.h',
+          ],
+          'action': [
+            'gdbus-codegen',
+            '<@(generate_args)',
+            '<@(_inputs)',
+          ],
+        },
+        {
+          'variables': {
+            'generate_args': [
+              '--interface-prefix',
+              'org.gnome.UPnP.',
+              '--c-namespace',
+              'upnp',
+              '--generate-c-code',
+              '<(gen_dbus_proxy_path)/upnp_media_object_gen',
+            ],
+          },
+          'action_name': 'upnp_media_object_gen',
+          'inputs': [
+            'dbus_interfaces/org.gnome.UPnP.MediaObject2.xml',
+          ],
+          'outputs': [
+            '<(gen_dbus_proxy_path)/upnp_media_object_gen.c',
+            '<(gen_dbus_proxy_path)/upnp_media_object_gen.h',
+          ],
+          'action': [
+            'gdbus-codegen',
+            '<@(generate_args)',
+            '<@(_inputs)',
+          ],
+        },
+      ],
+      # Compile generated dbus proxies without C++11 flag
+      'cflags!': [ '-std=c++0x' ],
+      'sources': [
+        '<(gen_dbus_proxy_path)/dleyna_manager_gen.c',
+        '<(gen_dbus_proxy_path)/dleyna_manager_gen.h',
+        '<(gen_dbus_proxy_path)/dleyna_media_device_gen.c',
+        '<(gen_dbus_proxy_path)/dleyna_media_device_gen.h',
+        '<(gen_dbus_proxy_path)/upnp_media_container_gen.c',
+        '<(gen_dbus_proxy_path)/upnp_media_container_gen.h',
+        '<(gen_dbus_proxy_path)/upnp_media_object_gen.c',
+        '<(gen_dbus_proxy_path)/upnp_media_object_gen.h',
+      ],
+      'includes': [
+        '../common/pkg-config.gypi',
+      ],
+    },
+  ],
+}
diff --git a/mediaserver/mediaserver.h b/mediaserver/mediaserver.h
new file mode 100644 (file)
index 0000000..6335138
--- /dev/null
@@ -0,0 +1,63 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIASERVER_MEDIASERVER_H_
+#define MEDIASERVER_MEDIASERVER_H_
+
+namespace common {
+class Instance;
+}
+
+#include <string>
+#include "common/picojson.h"
+#include "mediaserver/callbacks.h"
+#include "mediaserver/dleyna_media_device_gen.h"
+#include "mediaserver/upnp_media_container_gen.h"
+
+class MediaServer {
+ public:
+  MediaServer(common::Instance* instance, const std::string& object_path);
+  virtual ~MediaServer();
+
+  void browse(const picojson::value& value);
+  void find(const picojson::value& value);
+  void createFolder(const picojson::value& value);
+  void upload(const picojson::value& value);
+  void cancel(const picojson::value& value);
+  bool isCancelled() const;
+
+  // MediaContainer methods
+  void uploadToContainer(const picojson::value& value);
+  void createFolderInContainer(const picojson::value& value);
+
+  picojson::value toJSON();
+
+ private:
+  picojson::value mediaObjectToJSON(GVariant* variant);
+
+  void postResult(GVariant* objects,
+                  const char* completed_operation,
+                  double async_operation_id);
+  void postResult(const char* completed_operation,
+                  double async_operation_id);
+  void postError(double async_operation_id);
+
+  CALLBACK_METHOD_WITH_ID(OnBrowse, GObject*, GAsyncResult*, MediaServer);
+  CALLBACK_METHOD_WITH_ID(OnFind, GObject*, GAsyncResult*, MediaServer);
+  CALLBACK_METHOD_WITH_ID(OnCreateFolder, GObject*, GAsyncResult*, MediaServer);
+  CALLBACK_METHOD_WITH_ID(OnUpload, GObject*, GAsyncResult*, MediaServer);
+  CALLBACK_METHOD_WITH_ID(OnUploadToContainer, GObject*,
+                          GAsyncResult*, MediaServer);
+  CALLBACK_METHOD_WITH_ID(OnCreateFolderInContainer, GObject*,
+                          GAsyncResult*, MediaServer);
+
+ private:
+  common::Instance* instance_;
+  picojson::value::object object_;
+  dleynaMediaDevice* mediadevice_proxy_;
+  std::string object_path_;
+  GCancellable* cancellable_;
+};
+
+#endif  // MEDIASERVER_MEDIASERVER_H_
diff --git a/mediaserver/mediaserver_api.js b/mediaserver/mediaserver_api.js
new file mode 100644 (file)
index 0000000..97f7280
--- /dev/null
@@ -0,0 +1,325 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+///////////////////////////////////////////////////////////////////////////////
+// Utilities
+///////////////////////////////////////////////////////////////////////////////
+
+var g_next_async_call_id = 0;
+var g_async_calls = {};
+
+function AsyncCall(resolve, reject) {
+  this.resolve = resolve;
+  this.reject = reject;
+}
+
+function createPromise(msg) {
+  var promise = new Promise(function(resolve, reject) {
+    g_async_calls[g_next_async_call_id] = new AsyncCall(resolve, reject);
+  });
+  msg.asyncCallId = g_next_async_call_id;
+  extension.postMessage(JSON.stringify(msg));
+  ++g_next_async_call_id;
+  return promise;
+}
+
+function _addConstProperty(obj, propertyKey, propertyValue) {
+  Object.defineProperty(obj, propertyKey, {
+    configurable: true,
+    writable: false,
+    value: propertyValue
+  });
+}
+
+function _addConstructorProperty(obj, constructor) {
+  Object.defineProperty(obj, 'constructor', {
+    enumerable: false,
+    value: constructor
+  });
+}
+
+function _addConstPropertyFromObject(obj, propertyKey, propObject) {
+  if (propObject.hasOwnProperty(propertyKey)) {
+    Object.defineProperty(obj, propertyKey, {
+      configurable: true,
+      writable: false,
+      value: propObject[propertyKey]
+    });
+  }
+}
+
+function derive(child, parent) {
+  child.prototype = Object.create(parent.prototype);
+  child.prototype.constructor = child;
+  _addConstructorProperty(child.prototype, child);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Exports and main entry point for the MediaServer API
+///////////////////////////////////////////////////////////////////////////////
+
+var g_media_server_manager = new MediaServerManager();
+exports = g_media_server_manager;
+
+var g_media_server_manager_listeners = {};
+g_media_server_manager_listeners['serverfound'] = [];
+g_media_server_manager_listeners['serverlost'] = [];
+
+extension.setMessageListener(function(json) {
+  var msg = JSON.parse(json);
+  switch (msg.cmd) {
+    case 'serverFound':
+      handleMediaServerFound(msg);
+      break;
+    case 'serverLost':
+      handleMediaServerLost(msg);
+      break;
+    case 'browseCompleted':
+    case 'findCompleted':
+      handleBrowseCompleted(msg);
+      break;
+    case 'createFolderCompleted':
+    case 'uploadCompleted':
+    case 'cancelCompleted':
+    case 'uploadToContainerCompleted':
+    case 'createFolderInContainerCompleted':
+      handleAsyncCallSuccess(msg);
+      break;
+    case 'asyncCallError':
+      handleAsyncCallError(msg);
+      break;
+  }
+});
+
+function handleMediaServerFound(msg) {
+  var event = new CustomEvent('serverfound');
+  _addConstProperty(event, 'server', new MediaServer(msg.server));
+  g_media_server_manager.dispatchEvent(event);
+  if (g_media_server_manager.onserverfound)
+    g_media_server_manager.onserverfound(event);
+}
+
+function handleMediaServerLost(msg) {
+  var event = new CustomEvent('serverlost');
+  _addConstProperty(event, 'id', msg.lostServerId);
+  g_media_server_manager.dispatchEvent(event);
+  if (g_media_server_manager.onserverlost)
+    g_media_server_manager.onserverlost(event);
+}
+
+function handleBrowseCompleted(msg) {
+  var mediaObjects = convertToMediaObjects(msg.mediaObjects);
+  g_async_calls[msg.asyncCallId].resolve(mediaObjects);
+}
+
+function handleAsyncCallSuccess(msg) {
+  g_async_calls[msg.asyncCallId].resolve();
+}
+
+function handleAsyncCallError(msg) {
+  g_async_calls[msg.asyncCallId].reject(Error('Async operation failed'));
+}
+
+function convertToMediaObjects(objArray) {
+  var objects = [];
+  objArray.forEach(function(element) {
+    this.push(convertToMediaObject(element));
+  }, objects);
+  return objects;
+}
+
+function convertToMediaObject(obj) {
+  if (obj.type === 'container')
+    return new MediaContainer(obj);
+  else
+    return new MediaItem(obj);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// MediaServerManager
+///////////////////////////////////////////////////////////////////////////////
+
+function MediaServerManager() {
+  this.onserverfound = null;
+  this.onserverlost = null;
+}
+
+function isValidType(type) {
+  return (type === 'serverlost' || type === 'serverfound');
+}
+
+MediaServerManager.prototype.addEventListener = function(type, callback) {
+  if (callback != null && isValidType(type))
+    if (~~g_media_server_manager_listeners[type].indexOf(callback))
+      g_media_server_manager_listeners[type].push(callback);
+};
+
+MediaServerManager.prototype.removeEventListener = function(type, callback) {
+  if (callback != null && isValidType(type)) {
+    var index = g_media_server_manager_listeners[type].indexOf(callback);
+    if (~index)
+      g_media_server_manager_listeners[type].slice(index, 1);
+  }
+};
+
+MediaServerManager.prototype.dispatchEvent = function(event) {
+  var handled = true;
+
+  if (typeof event !== 'object' || !isValidType(event.type))
+    return false;
+
+  g_media_server_manager_listeners[event.type].forEach(function(callback) {
+    var res = callback(event);
+    if (!res && handled)
+      handled = false;
+  });
+
+  return handled;
+};
+
+MediaServerManager.prototype.scanNetwork = function() {
+  var msg = {
+    'cmd': 'scanNetwork'
+  };
+  extension.postMessage(JSON.stringify(msg));
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// MediaServer
+///////////////////////////////////////////////////////////////////////////////
+
+function MediaServer(obj) {
+  _addConstPropertyFromObject(this, 'id', obj);
+  _addConstPropertyFromObject(this, 'friendlyName', obj);
+  _addConstPropertyFromObject(this, 'manufacturer', obj);
+  _addConstPropertyFromObject(this, 'manufacturerURL', obj);
+  _addConstPropertyFromObject(this, 'modelDescription', obj);
+  _addConstPropertyFromObject(this, 'modelName', obj);
+  _addConstPropertyFromObject(this, 'modelNumber', obj);
+  _addConstPropertyFromObject(this, 'serialNumber', obj);
+  _addConstPropertyFromObject(this, 'UDN', obj);
+  _addConstPropertyFromObject(this, 'presentationURL', obj);
+  _addConstPropertyFromObject(this, 'iconURL', obj);
+  _addConstPropertyFromObject(this, 'deviceType', obj);
+  _addConstProperty(this, 'root', new MediaContainer(obj.root));
+  _addConstPropertyFromObject(this, 'canCreateContainer', obj);
+  _addConstPropertyFromObject(this, 'canUpload', obj);
+  _addConstPropertyFromObject(this, 'searchAttrs', obj);
+  _addConstPropertyFromObject(this, 'sortAttrs', obj);
+  _addConstPropertyFromObject(this, 'searchAttrs', obj);
+  this.oncontainerchanged = null;
+}
+
+MediaServer.prototype.upload = function(path) {
+  var msg = {
+    'cmd': 'upload',
+    'serverId': this.root.id,
+    'title': path.replace(/^.*[\\\/]/, ''),
+    'path': path
+  };
+  return createPromise(msg);
+};
+
+MediaServer.prototype.createFolder = function(folderName) {
+  var msg = {
+    'cmd': 'createFolder',
+    'serverId': this.root.id,
+    'folderName': folderName
+  };
+  return createPromise(msg);
+};
+
+MediaServer.prototype.browse = function(containerId, sortMode, count, offset) {
+  var msg = {
+    'cmd': 'browse',
+    'serverId': this.root.id,
+    'containerId': containerId,
+    'sortMode': sortMode,
+    'count': count,
+    'offset': offset
+  };
+  return createPromise(msg);
+};
+
+MediaServer.prototype.find =
+    function(containerId, searchFilter, sortMode, count, offset) {
+  var msg = {
+    'cmd': 'find',
+    'serverId': this.root.id,
+    'containerId': containerId,
+    'searchFilter': searchFilter,
+    'sortMode': sortMode,
+    'count': count,
+    'offset': offset
+  };
+  return createPromise(msg);
+};
+
+MediaServer.prototype.cancel = function() {
+  var msg = {
+    'cmd': 'cancel',
+    'serverId': this.root.id
+  };
+  return createPromise(msg);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// MediaObject, MediaItem and MediaContainer
+///////////////////////////////////////////////////////////////////////////////
+
+function MediaObject(obj) {
+  _addConstPropertyFromObject(this, 'id', obj);
+  _addConstPropertyFromObject(this, 'type', obj);
+  _addConstPropertyFromObject(this, 'title', obj);
+}
+
+function MediaContainer(obj) {
+  MediaObject.call(this, obj);
+  _addConstPropertyFromObject(this, 'childCount', obj);
+  _addConstPropertyFromObject(this, 'rootContainerId', obj);
+}
+derive(MediaContainer, MediaObject);
+
+MediaContainer.prototype.upload = function(title, path) {
+  var msg = {
+    'cmd': 'uploadToContainer',
+    'containerId': this.id,
+    'rootContainerId': this.rootContainerId,
+    'title': title,
+    'path': path
+  };
+  return createPromise(msg);
+};
+
+MediaContainer.prototype.createFolder = function(title) {
+  var msg = {
+    'cmd': 'createFolderInContainer',
+    'containerId': this.id,
+    'rootContainerId': this.rootContainerId,
+    'title': title
+  };
+  return createPromise(msg);
+};
+
+function MediaItem(obj) {
+  // Strip .* from the type
+  var index = obj.type.indexOf('.');
+  if (index !== -1)
+    obj.type = obj.type.substr(0, index);
+
+  MediaObject.call(this, obj);
+  _addConstPropertyFromObject(this, 'mimeType', obj);
+  _addConstProperty(this, 'sourceUri', obj.sourceUri);
+  _addConstProperty(this, 'createDate', obj.createDate);
+  _addConstProperty(this, 'fileSize', obj.fileSize);
+  _addConstProperty(this, 'width', obj.width);
+  _addConstProperty(this, 'height', obj.height);
+  _addConstProperty(this, 'duration', obj.duration);
+  _addConstProperty(this, 'audioSampleRate', obj.audioSampleRate);
+  _addConstProperty(this, 'collection', obj.width);
+  _addConstProperty(this, 'author', obj.width);
+  _addConstProperty(this, 'category', obj.width);
+  _addConstProperty(this, 'trackNumber', obj.width);
+}
+derive(MediaItem, MediaObject);
diff --git a/mediaserver/mediaserver_extension.cc b/mediaserver/mediaserver_extension.cc
new file mode 100644 (file)
index 0000000..50ca32f
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mediaserver/mediaserver_extension.h"
+
+#include <glib-object.h>
+#include "mediaserver/mediaserver_instance.h"
+
+common::Extension* CreateExtension() {
+  g_type_init();
+  return new MediaServerExtension;
+}
+
+extern const char kSource_mediaserver_api[];
+
+MediaServerExtension::MediaServerExtension() {
+  SetExtensionName("navigator.mediaServer");
+  SetJavaScriptAPI(kSource_mediaserver_api);
+}
+
+MediaServerExtension::~MediaServerExtension() {}
+
+common::Instance* MediaServerExtension::CreateInstance() {
+  return new MediaServerInstance;
+}
diff --git a/mediaserver/mediaserver_extension.h b/mediaserver/mediaserver_extension.h
new file mode 100644 (file)
index 0000000..ba299c1
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIASERVER_MEDIASERVER_EXTENSION_H_
+#define MEDIASERVER_MEDIASERVER_EXTENSION_H_
+
+#include "common/extension.h"
+
+class MediaServerExtension : public common::Extension {
+ public:
+  MediaServerExtension();
+  virtual ~MediaServerExtension();
+
+ private:
+  // common::Extension implementation.
+  virtual common::Instance* CreateInstance();
+};
+
+#endif  // MEDIASERVER_MEDIASERVER_EXTENSION_H_
diff --git a/mediaserver/mediaserver_instance.cc b/mediaserver/mediaserver_instance.cc
new file mode 100644 (file)
index 0000000..0e5f214
--- /dev/null
@@ -0,0 +1,50 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mediaserver/mediaserver_instance.h"
+
+#include <string>
+
+#include "common/picojson.h"
+#include "mediaserver/mediaserver_manager.h"
+
+MediaServerInstance::MediaServerInstance() {
+  media_server_manager_ = new MediaServerManager(this);
+}
+
+MediaServerInstance::~MediaServerInstance() {
+  delete media_server_manager_;
+}
+
+void MediaServerInstance::HandleMessage(const char* message) {
+  picojson::value v;
+
+  std::string err;
+  picojson::parse(v, message, message + strlen(message), &err);
+  if (!err.empty()) {
+    return;
+  }
+
+  std::string cmd = v.get("cmd").to_str();
+  if (cmd == "scanNetwork")
+    media_server_manager_->scanNetwork();
+  else if (cmd == "browse")
+    media_server_manager_->handleBrowse(v);
+  else if (cmd == "find")
+    media_server_manager_->handleFind(v);
+  else if (cmd == "createFolder")
+    media_server_manager_->handleCreateFolder(v);
+  else if (cmd == "upload")
+    media_server_manager_->handleUpload(v);
+  else if (cmd == "cancel")
+    media_server_manager_->handleCancel(v);
+  else if (cmd == "uploadToContainer")
+    media_server_manager_->handleUploadToContainer(v);
+  else if (cmd == "createFolderInContainer")
+    media_server_manager_->handleCreateFolderInContainer(v);
+  else
+    std::cerr << "Received unknown message: " << cmd << "\n";
+}
+
+void MediaServerInstance::HandleSyncMessage(const char* message) {}
diff --git a/mediaserver/mediaserver_instance.h b/mediaserver/mediaserver_instance.h
new file mode 100644 (file)
index 0000000..e9c7768
--- /dev/null
@@ -0,0 +1,30 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIASERVER_MEDIASERVER_INSTANCE_H_
+#define MEDIASERVER_MEDIASERVER_INSTANCE_H_
+
+#include "common/extension.h"
+
+class MediaServerManager;
+
+namespace picojson {
+class value;
+}
+
+class MediaServerInstance : public common::Instance {
+ public:
+  MediaServerInstance();
+  virtual ~MediaServerInstance();
+
+ private:
+  // common::Instance implementation.
+  virtual void HandleMessage(const char* msg);
+  virtual void HandleSyncMessage(const char* msg);
+
+ private:
+  MediaServerManager* media_server_manager_;
+};
+
+#endif  // MEDIASERVER_MEDIASERVER_INSTANCE_H_
diff --git a/mediaserver/mediaserver_manager.cc b/mediaserver/mediaserver_manager.cc
new file mode 100644 (file)
index 0000000..e29f028
--- /dev/null
@@ -0,0 +1,159 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mediaserver/mediaserver_manager.h"
+
+#include <utility>
+#include <string>
+#include "common/extension.h"
+#include "mediaserver/mediaserver.h"
+
+typedef std::pair<std::string, std::shared_ptr<MediaServer>> MediaServerPair;
+
+MediaServerManager::MediaServerManager(common::Instance* instance)
+    : instance_(instance) {
+  GError* gerror = NULL;
+  manager_proxy_ = dleyna_manager_proxy_new_for_bus_sync(
+      G_BUS_TYPE_SESSION,
+      G_DBUS_PROXY_FLAGS_NONE,
+      "com.intel.dleyna-server",
+      "/com/intel/dLeynaServer",
+      NULL,
+      &gerror);
+
+  if (gerror) {
+    g_error_free(gerror);
+    return;
+  }
+
+  g_signal_connect(
+      manager_proxy_,
+      "found-server",
+      G_CALLBACK(OnFoundServerCallBack),
+      this);
+
+  g_signal_connect(
+      manager_proxy_,
+      "lost-server",
+      G_CALLBACK(OnLostServerCallBack),
+      this);
+}
+
+MediaServerManager::~MediaServerManager() {
+  g_object_unref(manager_proxy_);
+}
+
+void MediaServerManager::scanNetwork() {
+  if (!manager_proxy_)
+    return;
+
+  dleyna_manager_call_get_servers(
+      manager_proxy_,
+      NULL,
+      OnGetServersCallBack,
+      this);
+}
+
+void MediaServerManager::handleBrowse(const picojson::value& value) {
+  if (MediaServerPtr server = getMediaServerById(value))
+    server->browse(value);
+}
+
+void MediaServerManager::handleFind(const picojson::value& value) {
+  if (MediaServerPtr server = getMediaServerById(value))
+    server->find(value);
+}
+
+void MediaServerManager::handleCreateFolder(const picojson::value& value) {
+  if (MediaServerPtr server = getMediaServerById(value))
+    server->createFolder(value);
+}
+
+void MediaServerManager::handleUpload(const picojson::value& value) {
+  if (MediaServerPtr server = getMediaServerById(value))
+    server->upload(value);
+}
+
+void MediaServerManager::handleCancel(const picojson::value& value) {
+  if (MediaServerPtr server = getMediaServerById(value))
+    server->cancel(value);
+}
+
+void MediaServerManager::handleUploadToContainer(const picojson::value& value) {
+  if (MediaServerPtr server =
+      getMediaServerById(value.get("rootContainerId").to_str()))
+    server->uploadToContainer(value);
+}
+
+void MediaServerManager::handleCreateFolderInContainer(
+    const picojson::value& value) {
+  if (MediaServerPtr server =
+      getMediaServerById(value.get("rootContainerId").to_str()))
+    server->createFolderInContainer(value);
+}
+
+
+void MediaServerManager::postServerFound(const std::string& path) {
+  MediaServerPtr media_server(new MediaServer(instance_, path));
+  media_servers_.insert(MediaServerPair(path, media_server));
+  picojson::value::object object;
+  object["cmd"] = picojson::value("serverFound");
+  object["server"] = media_server->toJSON();
+  picojson::value value(object);
+  instance_->PostMessage(value.serialize().c_str());
+}
+
+MediaServerPtr MediaServerManager::getMediaServerById(
+    const picojson::value& id) {
+  return getMediaServerById(id.get("serverId").to_str());
+}
+
+MediaServerPtr MediaServerManager::getMediaServerById(const std::string& id) {
+  if (media_servers_.size()) {
+    std::map<std::string, MediaServerPtr>::const_iterator it;
+    if ((it = media_servers_.find(id)) != media_servers_.end())
+      return (*it).second;
+  }
+  return MediaServerPtr();
+}
+
+void MediaServerManager::OnGetServers(
+    GObject *source_object,
+    GAsyncResult *res) {
+  GError* gerror = NULL;
+  gchar **out_Servers;
+  if (!dleyna_manager_call_get_servers_finish(
+      manager_proxy_,
+      &out_Servers,
+      res,
+      &gerror)) {
+    g_error_free(gerror);
+    return;
+  }
+
+  while (gchar* server_path = *out_Servers) {
+    postServerFound(std::string(*out_Servers));
+    out_Servers++;
+    g_free(server_path);
+  }
+}
+
+void MediaServerManager::OnFoundServer(
+    dleynaManager *object,
+    const gchar *arg_Path) {
+  postServerFound(std::string(arg_Path));
+}
+
+void MediaServerManager::OnLostServer(
+    dleynaManager *object,
+    const gchar *arg_Path) {
+  if (MediaServerPtr server = getMediaServerById(std::string(arg_Path))) {
+      picojson::value::object object;
+      object["cmd"] = picojson::value("serverLost");
+      object["serverId"] = server->toJSON().get("id");
+      picojson::value value(object);
+      instance_->PostMessage(value.serialize().c_str());
+      media_servers_.erase(arg_Path);
+  }
+}
diff --git a/mediaserver/mediaserver_manager.h b/mediaserver/mediaserver_manager.h
new file mode 100644 (file)
index 0000000..6d2891b
--- /dev/null
@@ -0,0 +1,52 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIASERVER_MEDIASERVER_MANAGER_H_
+#define MEDIASERVER_MEDIASERVER_MANAGER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include "mediaserver/dleyna_manager_gen.h"
+#include "mediaserver/mediaserver.h"
+#include "mediaserver/callbacks.h"
+
+namespace common {
+class Instance;
+}
+
+typedef std::shared_ptr<MediaServer> MediaServerPtr;
+
+class MediaServerManager {
+ public:
+  explicit MediaServerManager(common::Instance* instance);
+  virtual ~MediaServerManager();
+
+  void scanNetwork();
+  void handleBrowse(const picojson::value& value);
+  void handleFind(const picojson::value& value);
+  void handleCreateFolder(const picojson::value& value);
+  void handleUpload(const picojson::value& value);
+  void handleCancel(const picojson::value& value);
+  void handleUploadToContainer(const picojson::value& value);
+  void handleCreateFolderInContainer(const picojson::value& value);
+
+ private:
+  void postServerFound(const std::string& path);
+  MediaServerPtr getMediaServerById(const picojson::value& id);
+  MediaServerPtr getMediaServerById(const std::string& id);
+
+  CALLBACK_METHOD(OnGetServers, GObject*, GAsyncResult*, MediaServerManager);
+  CALLBACK_METHOD(OnLostServer, dleynaManager*,
+                  const gchar*, MediaServerManager);
+  CALLBACK_METHOD(OnFoundServer, dleynaManager*,
+                  const gchar*, MediaServerManager);
+
+ private:
+  common::Instance* instance_;
+  dleynaManager* manager_proxy_;
+  std::map<std::string, MediaServerPtr> media_servers_;
+};
+
+#endif  // MEDIASERVER_MEDIASERVER_MANAGER_H_
index c95b754..dfad2ba 100644 (file)
@@ -10,6 +10,7 @@
       'dependencies': [
         'bluetooth/bluetooth.gyp:*',
         'filesystem/filesystem.gyp:*',
+        'mediaserver/mediaserver.gyp:*',
         'network_bearer_selection/network_bearer_selection.gyp:*',
         'notification/notification.gyp:*',
         'power/power.gyp:*',