<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>
--- /dev/null
+<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>
--- /dev/null
+## 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
--- /dev/null
+// 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_
--- /dev/null
+<!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>
--- /dev/null
+<!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>
--- /dev/null
+<!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>
--- /dev/null
+<!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>
--- /dev/null
+// 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);
+}
--- /dev/null
+{
+ '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',
+ ],
+ },
+ ],
+}
--- /dev/null
+// 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_
--- /dev/null
+// 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);
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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_
--- /dev/null
+// 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) {}
--- /dev/null
+// 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_
--- /dev/null
+// 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);
+ }
+}
--- /dev/null
+// 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_
'dependencies': [
'bluetooth/bluetooth.gyp:*',
'filesystem/filesystem.gyp:*',
+ 'mediaserver/mediaserver.gyp:*',
'network_bearer_selection/network_bearer_selection.gyp:*',
'notification/notification.gyp:*',
'power/power.gyp:*',