Add initial support for MediaRenderer API.
authorSrinivasa Ragavan <srinivasa.ragavan.venkateswaran@intel.com>
Wed, 9 Jul 2014 06:55:16 +0000 (12:25 +0530)
committerSrinivasa Ragavan <srinivasa.ragavan.venkateswaran@intel.com>
Wed, 16 Jul 2014 11:04:54 +0000 (16:34 +0530)
BUG=XWALK-1195

19 files changed:
examples/index.html
examples/media_renderer.html [new file with mode: 0644]
media_renderer/callbacks.h [new file with mode: 0644]
media_renderer/dbus_interfaces/com.intel.dLeynaRenderer.Manager.xml [new file with mode: 0644]
media_renderer/dbus_interfaces/com.intel.dLeynaRenderer.PushHost.xml [new file with mode: 0644]
media_renderer/dbus_interfaces/com.intel.dLeynaRenderer.RendererDevice.xml [new file with mode: 0644]
media_renderer/dbus_interfaces/org.mpris.MediaPlayer2.Player.xml [new file with mode: 0644]
media_renderer/dbus_interfaces/org.mpris.MediaPlayer2.xml [new file with mode: 0644]
media_renderer/media_renderer.cc [new file with mode: 0644]
media_renderer/media_renderer.gyp [new file with mode: 0644]
media_renderer/media_renderer.h [new file with mode: 0644]
media_renderer/media_renderer_api.js [new file with mode: 0644]
media_renderer/media_renderer_extension.cc [new file with mode: 0644]
media_renderer/media_renderer_extension.h [new file with mode: 0644]
media_renderer/media_renderer_instance.cc [new file with mode: 0644]
media_renderer/media_renderer_instance.h [new file with mode: 0644]
media_renderer/media_renderer_manager.cc [new file with mode: 0644]
media_renderer/media_renderer_manager.h [new file with mode: 0644]
tizen-wrt.gyp

index be7d8d2..c8cea61 100644 (file)
@@ -27,6 +27,7 @@ div.block {
 <a href="content.html"><div class="block">content</div></a>
 <a href="download.html"><div class="block">download</div></a>
 <a href="filesystem.html"><div class="block">filesystem</div></a>
+<a href="media_renderer.html"><div class="block">Media Renderer</div></a>
 <a href="mediaserver.html"><div class="block">mediaserver</div></a>
 <a href="network_bearer_selection.html"><div class="block">network bearer selection</div></a>
 <a href="notification.html"><div class="block">notification</div></a>
diff --git a/examples/media_renderer.html b/examples/media_renderer.html
new file mode 100644 (file)
index 0000000..d307475
--- /dev/null
@@ -0,0 +1,133 @@
+<html>
+<head>
+<title>MediaRenderer example</title>
+</head>
+<body>
+
+<button onclick='scanNetwork()'>Scan for media renderers</button>
+
+<div id='rendererContainer'></div>
+<script>
+navigator.mediaRenderer.addEventListener('rendererfound', onRendererFound);
+navigator.mediaRenderer.addEventListener('rendererlost', onRendererLost);
+
+var renderers = {};
+
+var mute=false;
+var speed=1.0;
+var track=1;
+var volume=1.0;
+function scanNetwork() {
+  emptyContainer('rendererContainer');
+  navigator.mediaRenderer.scanNetwork();
+}
+
+function emptyContainer(id) {
+  var container = document.getElementById(id);
+  while(container && container.hasChildNodes())
+    container.removeChild(container.lastChild);
+}
+
+function openURI(id) {
+  renderers[id].openURI("/home/sragavan/video.avi", "");
+}
+
+function prefetchURI(id) {
+  renderers[id].prefetchURI("/home/sragavan/video1.mp4", "");
+}
+
+function cancel(id) {
+  renderers[id].cancel();
+}
+
+function play(id) {
+  renderers[id].controller.play();
+}
+
+function pause(id) {
+  renderers[id].controller.pause();
+}
+
+function stop(id) {
+  renderers[id].controller.stop();
+}
+
+function next(id) {
+  renderers[id].controller.next();
+}
+
+function previous(id) {
+  renderers[id].controller.previous();
+}
+
+function mute(id) {
+  mute = !mute;
+  renderers[id].controller.mute(mute);
+}
+
+function setSpeed(id) {
+  if (speed > 0.9)
+    speed = 0.5;
+  else
+    speed = 1.0;
+  renderers[id].controller.setSpeed(speed);
+}
+
+function setVolume(id) {
+  if (volume > 0.9)
+    volume = 0.5;
+  else
+    volume = 1.0;
+  renderers[id].controller.setVolume(volume);
+}
+
+function gotoTrack(id) {
+  if (track == 1)
+    track = 2;
+  else
+    track = 1;
+  var renderer = renderers[id];
+  var controller = renderer.controller;
+  renderers[id].controller.gotoTrack(track);
+}
+
+function addButton(container, event, name, action) {
+  var button = document.createElement('button');
+  button.innerHTML = name;
+  button.id = event.renderer.id;
+  button.setAttribute('onclick', action);  
+  container.appendChild(button);  
+}
+
+function onRendererFound(event) {
+  var container = document.getElementById('rendererContainer');
+  var rendererControls = document.createElement('div');
+  renderers[event.renderer.id] = event.renderer;
+  rendererControls.id = event.renderer.id;
+  rendererControls.innerHTML = 'Renderer: ' + event.renderer.friendlyName;
+  container.appendChild(rendererControls);
+
+  addButton(rendererControls, event, 'Open', 'openURI(this.id)');
+  addButton(rendererControls, event, 'Prefetch', 'prefetchURI(this.id)');
+  addButton(rendererControls, event, 'Cancel', 'cancel(this.id)');
+  addButton(rendererControls, event, 'Play', 'play(this.id)');  
+  addButton(rendererControls, event, 'Pause', 'pause(this.id)');
+  addButton(rendererControls, event, 'Stop', 'stop(this.id)');
+  addButton(rendererControls, event, 'Next', 'next(this.id)');
+  addButton(rendererControls, event, 'Previous', 'openURI(this.id)');
+  addButton(rendererControls, event, 'Mute', 'mute(this.id)');  
+  addButton(rendererControls, event, 'setSpeed', 'setSpeed(this.id)');
+  addButton(rendererControls, event, 'setVolume', 'setVolume(this.id)');
+  addButton(rendererControls, event, 'gotoTrack', 'gotoTrack(this.id)');
+
+}
+
+function onRendererLost(event) {
+  var button = document.getElementById(event.id);
+  document.removeChild(button);
+}
+
+</script>
+
+</body>
+</html>
diff --git a/media_renderer/callbacks.h b/media_renderer/callbacks.h
new file mode 100644 (file)
index 0000000..fdb5f15
--- /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 MEDIA_RENDERER_CALLBACKS_H_
+#define MEDIA_RENDERER_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  // MEDIA_RENDERER_CALLBACKS_H_
diff --git a/media_renderer/dbus_interfaces/com.intel.dLeynaRenderer.Manager.xml b/media_renderer/dbus_interfaces/com.intel.dLeynaRenderer.Manager.xml
new file mode 100644 (file)
index 0000000..eb5108f
--- /dev/null
@@ -0,0 +1,33 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<!-- GDBus 2.38.2 -->
+<node>
+  <interface name="com.intel.dLeynaRenderer.Manager">
+    <method name="GetVersion">
+      <arg type="s" name="Version" direction="out">
+      </arg>
+    </method>
+    <method name="Release">
+    </method>
+    <method name="GetRenderers">
+      <arg type="ao" name="Renderers" direction="out">
+      </arg>
+    </method>
+    <method name="Rescan">
+    </method>
+    <signal name="FoundRenderer">
+      <arg type="o" name="Path">
+      </arg>
+    </signal>
+    <signal name="LostRenderer">
+      <arg type="o" name="Path">
+      </arg>
+    </signal>
+    <property type="b" name="NeverQuit" access="readwrite">
+    </property>
+    <property type="as" name="WhiteListEntries" access="readwrite">
+    </property>
+    <property type="b" name="WhiteListEnabled" access="readwrite">
+    </property>
+  </interface>
+</node>
diff --git a/media_renderer/dbus_interfaces/com.intel.dLeynaRenderer.PushHost.xml b/media_renderer/dbus_interfaces/com.intel.dLeynaRenderer.PushHost.xml
new file mode 100644 (file)
index 0000000..9526d9a
--- /dev/null
@@ -0,0 +1,17 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<!-- GDBus 2.38.2 -->
+<node>
+  <interface name="com.intel.dLeynaRenderer.PushHost">
+    <method name="HostFile">
+      <arg type="s" name="Path" direction="in">
+      </arg>
+      <arg type="s" name="Uri" direction="out">
+      </arg>
+    </method>
+    <method name="RemoveFile">
+      <arg type="s" name="Path" direction="in">
+      </arg>
+    </method>
+  </interface>
+</node>
diff --git a/media_renderer/dbus_interfaces/com.intel.dLeynaRenderer.RendererDevice.xml b/media_renderer/dbus_interfaces/com.intel.dLeynaRenderer.RendererDevice.xml
new file mode 100644 (file)
index 0000000..96d221d
--- /dev/null
@@ -0,0 +1,46 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<!-- GDBus 2.38.2 -->
+<node>
+  <interface name="com.intel.dLeynaRenderer.RendererDevice">
+    <method name="Cancel">
+    </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>
+    <property type="as" name="DeviceClasses" access="read">
+    </property>
+    <property type="s" name="DeviceType" access="read">
+    </property>
+    <property type="s" name="UDN" access="read">
+    </property>
+    <property type="s" name="FriendlyName" access="read">
+    </property>
+    <property type="s" name="IconURL" 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="SerialNumber" access="read">
+    </property>
+    <property type="s" name="PresentationURL" access="read">
+    </property>
+    <property type="s" name="ProtocolInfo" access="read">
+    </property>
+  </interface>
+</node>
+
diff --git a/media_renderer/dbus_interfaces/org.mpris.MediaPlayer2.Player.xml b/media_renderer/dbus_interfaces/org.mpris.MediaPlayer2.Player.xml
new file mode 100644 (file)
index 0000000..67b8e20
--- /dev/null
@@ -0,0 +1,103 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<!-- GDBus 2.38.2 -->
+<node>
+  <interface name="org.mpris.MediaPlayer2.Player">
+    <method name="Play">
+    </method>
+    <method name="Pause">
+    </method>
+    <method name="PlayPause">
+    </method>
+    <method name="Stop">
+    </method>
+    <method name="Next">
+    </method>
+    <method name="Previous">
+    </method>
+    <method name="OpenUri">
+      <arg type="s" name="Uri" direction="in">
+      </arg>
+    </method>
+    <method name="OpenUriEx">
+      <arg type="s" name="Uri" direction="in">
+      </arg>
+      <arg type="s" name="Metadata" direction="in">
+      </arg>
+    </method>
+    <method name="OpenNextUri">
+      <arg type="s" name="Uri" direction="in">
+      </arg>
+      <arg type="s" name="Metadata" direction="in">
+      </arg>
+    </method>
+    <method name="SetUri">
+      <arg type="s" name="Uri" direction="in">
+      </arg>
+      <arg type="s" name="Metadata" direction="in">
+      </arg>
+    </method>
+    <method name="Seek">
+      <arg type="x" name="offset" direction="in">
+      </arg>
+    </method>
+    <method name="ByteSeek">
+      <arg type="x" name="offset" direction="in">
+      </arg>
+    </method>
+    <method name="SetPosition">
+      <arg type="o" name="trackid" direction="in">
+      </arg>
+      <arg type="x" name="position" direction="in">
+      </arg>
+    </method>
+    <method name="SetBytePosition">
+      <arg type="o" name="trackid" direction="in">
+      </arg>
+      <arg type="x" name="byte_position" direction="in">
+      </arg>
+    </method>
+    <method name="GotoTrack">
+      <arg type="u" name="TrackNumber" direction="in">
+      </arg>
+    </method>
+    <property type="s" name="PlaybackStatus" access="read">
+    </property>
+    <property type="d" name="Rate" access="readwrite">
+    </property>
+    <property type="d" name="MinimumRate" access="read">
+    </property>
+    <property type="d" name="MaximumRate" access="read">
+    </property>
+    <property type="ad" name="TransportPlaySpeeds" access="read">
+    </property>
+    <property type="d" name="Volume" access="readwrite">
+    </property>
+    <property type="b" name="CanPlay" access="read">
+    </property>
+    <property type="b" name="CanSeek" access="read">
+    </property>
+    <property type="b" name="CanByteSeek" access="read">
+    </property>
+    <property type="b" name="CanControl" access="read">
+    </property>
+    <property type="b" name="CanPause" access="read">
+    </property>
+    <property type="b" name="CanGoNext" access="read">
+    </property>
+    <property type="b" name="CanGoPrevious" access="read">
+    </property>
+    <property type="x" name="Position" access="read">
+    </property>
+    <property type="x" name="BytePosition" access="read">
+    </property>
+    <property type="a{sv}" name="Metadata" access="read">
+    </property>
+    <property type="u" name="CurrentTrack" access="read">
+    </property>
+    <property type="u" name="NumberOfTracks" access="read">
+    </property>
+    <property type="b" name="Mute" access="readwrite">
+    </property>
+  </interface>
+</node>
diff --git a/media_renderer/dbus_interfaces/org.mpris.MediaPlayer2.xml b/media_renderer/dbus_interfaces/org.mpris.MediaPlayer2.xml
new file mode 100644 (file)
index 0000000..d69f422
--- /dev/null
@@ -0,0 +1,25 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<!-- GDBus 2.38.2 -->
+<node>
+  <interface name="org.mpris.MediaPlayer2">
+    <method name="Raise">
+    </method>
+    <method name="Quit">
+    </method>
+    <property type="b" name="CanQuit" access="read">
+    </property>
+    <property type="b" name="CanRaise" access="read">
+    </property>
+    <property type="b" name="CanSetFullscreen" access="read">
+    </property>
+    <property type="b" name="HasTrackList" access="read">
+    </property>
+    <property type="s" name="Identity" access="read">
+    </property>
+    <property type="as" name="SupportedUriSchemes" access="read">
+    </property>
+    <property type="as" name="SupportedMimeTypes" access="read">
+    </property>
+  </interface>
+</node>
diff --git a/media_renderer/media_renderer.cc b/media_renderer/media_renderer.cc
new file mode 100644 (file)
index 0000000..385ce13
--- /dev/null
@@ -0,0 +1,543 @@
+// 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 "media_renderer/media_renderer.h"
+
+#include <map>
+#include "common/extension.h"
+
+namespace {
+
+const gchar kdLeynaRendererInterfaceName[] = "com.intel.dleyna-renderer";
+const gchar kdLeynaPushHostInterfaceName[] = "com.intel.dleyna-renderer";
+const gchar kMprisPlayerInterfaceName[] = "com.intel.dleyna-renderer";
+
+}  // namespace
+
+static picojson::value toJSONValue(const gchar* value) {
+  return value ? picojson::value(value) : picojson::value();
+}
+
+static picojson::value toJSONValueArray(GVariant* values) {
+  picojson::array array;
+  GVariantIter iter;
+  GVariant* child;
+  gint value;
+
+  g_variant_iter_init(&iter, values);
+  while (g_variant_iter_next(&iter, "{d}", &value))
+    array.push_back(picojson::value(static_cast<double>(value)));
+  return picojson::value(array);
+}
+
+MediaRenderer::MediaRenderer(common::Instance* instance,
+                             const std::string& object_path)
+    : instance_(instance),
+      rendererdevice_proxy_(0),
+      mediaplayer2_proxy_(0),
+      mprisplayer_proxy_(0),
+      object_path_(object_path),
+      cancellable_(g_cancellable_new()) {
+  GError* gerror = NULL;
+
+  rendererdevice_proxy_ = dleyna_renderer_device_proxy_new_for_bus_sync(
+      G_BUS_TYPE_SESSION,
+      G_DBUS_PROXY_FLAGS_NONE,
+      kdLeynaRendererInterfaceName,
+      object_path.c_str(),
+      NULL,
+      &gerror);
+
+  if (gerror) {
+    g_error_free(gerror);
+    return;
+  }
+
+  pushhost_proxy_ = dleyna_push_host_proxy_new_for_bus_sync(
+      G_BUS_TYPE_SESSION,
+      G_DBUS_PROXY_FLAGS_NONE,
+      kdLeynaPushHostInterfaceName,
+      object_path.c_str(),
+      NULL,
+      &gerror);
+
+  if (gerror) {
+    g_error_free(gerror);
+    return;
+  }
+
+  mprisplayer_proxy_ = mprismediaplayer2_player_proxy_new_for_bus_sync(
+      G_BUS_TYPE_SESSION,
+      G_DBUS_PROXY_FLAGS_NONE,
+      kMprisPlayerInterfaceName,
+      object_path.c_str(),
+      NULL,
+      &gerror);
+
+  if (gerror) {
+    g_error_free(gerror);
+    return;
+  }
+}
+
+MediaRenderer::~MediaRenderer() {
+  g_object_unref(rendererdevice_proxy_);
+  g_object_unref(pushhost_proxy_);
+  g_object_unref(mprisplayer_proxy_);
+}
+
+picojson::value MediaRenderer::ToJSON() {
+  if (object_.size())
+    return picojson::value(object_);
+
+  object_["id"] = picojson::value(object_path_);
+  object_["friendlyName"] = toJSONValue(
+      dleyna_renderer_device_get_friendly_name(rendererdevice_proxy_));
+  object_["manufacturer"] = toJSONValue(
+      dleyna_renderer_device_get_manufacturer(rendererdevice_proxy_));
+  object_["manufacturerURL"] = toJSONValue(
+      dleyna_renderer_device_get_manufacturer_url(rendererdevice_proxy_));
+  object_["modelDescription"] = toJSONValue(
+      dleyna_renderer_device_get_model_description(rendererdevice_proxy_));
+  object_["modelName"] = toJSONValue(
+      dleyna_renderer_device_get_model_name(rendererdevice_proxy_));
+  object_["modelNumber"] = toJSONValue(
+      dleyna_renderer_device_get_model_number(rendererdevice_proxy_));
+  object_["serialNumber"] = toJSONValue(
+      dleyna_renderer_device_get_serial_number(rendererdevice_proxy_));
+  object_["UDN"] = toJSONValue(
+      dleyna_renderer_device_get_udn(rendererdevice_proxy_));
+  object_["presentationURL"] = toJSONValue(
+      dleyna_renderer_device_get_presentation_url(rendererdevice_proxy_));
+  object_["iconURL"] = toJSONValue(
+      dleyna_renderer_device_get_icon_url(rendererdevice_proxy_));
+  object_["deviceType"] = toJSONValue(
+      dleyna_renderer_device_get_device_type(rendererdevice_proxy_));
+  object_["protocolInfo"] = toJSONValue(
+      dleyna_renderer_device_get_protocol_info(rendererdevice_proxy_));
+
+  picojson::value::object controller_object;
+  controller_object["id"] = picojson::value(object_path_);
+  controller_object["playbackStatus"] = toJSONValue(
+      mprismediaplayer2_player_get_playback_status(mprisplayer_proxy_));
+  controller_object["muted"] = picojson::value(static_cast<bool>
+     (mprismediaplayer2_player_get_mute(mprisplayer_proxy_)));
+  controller_object["volume"] = picojson::value(
+      mprismediaplayer2_player_get_volume(mprisplayer_proxy_));
+  controller_object["track"] = picojson::value(static_cast<double>
+      (mprismediaplayer2_player_get_current_track(mprisplayer_proxy_)));
+  controller_object["speed"] = picojson::value(
+      mprismediaplayer2_player_get_rate(mprisplayer_proxy_));
+
+  object_["controller"] = picojson::value(controller_object);
+
+  return picojson::value(object_);
+}
+
+// MediaRenderer methods
+void MediaRenderer::OpenURI(const picojson::value& value) {
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  if (!mprisplayer_proxy_) {
+    PostError(async_call_id);
+    return;
+  }
+
+  if (!pushhost_proxy_) {
+    PostError(async_call_id);
+    return;
+  }
+
+  dleyna_push_host_call_host_file(
+      pushhost_proxy_,
+      value.get("mediaURI").to_str().c_str(),
+      cancellable_,
+      OnHostFileCallBack,
+      new CallbackData(this, async_call_id));
+}
+
+void MediaRenderer::PrefetchURI(const picojson::value& value) {
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  if (!mprisplayer_proxy_) {
+    PostError(async_call_id);
+    return;
+  }
+
+  mprismediaplayer2_player_call_open_next_uri(
+      mprisplayer_proxy_,
+      value.get("mediaURI").to_str().c_str(),
+      value.get("metaData").to_str().c_str(),
+      cancellable_,
+      OnPrefetchURICallBack,
+      new CallbackData(this, async_call_id));
+}
+
+void MediaRenderer::Cancel(const picojson::value& value) {
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  if (!rendererdevice_proxy_) {
+    PostError(async_call_id);
+    return;
+  }
+
+  dleyna_renderer_device_call_cancel(
+      rendererdevice_proxy_,
+      cancellable_,
+      OnCancelCallBack,
+      new CallbackData(this, async_call_id));
+}
+
+bool MediaRenderer::IsCancelled() const {
+  return g_cancellable_is_cancelled(cancellable_);
+}
+
+// MediaController methods
+void MediaRenderer::Play(const picojson::value& value) {
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  if (!mprisplayer_proxy_) {
+    PostError(async_call_id);
+    return;
+  }
+
+  mprismediaplayer2_player_call_play(
+      mprisplayer_proxy_,
+      cancellable_,
+      OnPlayCallBack,
+      new CallbackData(this, async_call_id));
+}
+
+void MediaRenderer::Pause(const picojson::value& value) {
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  if (!mprisplayer_proxy_) {
+    PostError(async_call_id);
+    return;
+  }
+
+  mprismediaplayer2_player_call_pause(
+      mprisplayer_proxy_,
+      cancellable_,
+      OnPauseCallBack,
+      new CallbackData(this, async_call_id));
+}
+
+void MediaRenderer::Stop(const picojson::value& value) {
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  if (!mprisplayer_proxy_) {
+    PostError(async_call_id);
+    return;
+  }
+
+  mprismediaplayer2_player_call_stop(
+      mprisplayer_proxy_,
+      cancellable_,
+      OnStopCallBack,
+      new CallbackData(this, async_call_id));
+}
+
+void MediaRenderer::Next(const picojson::value& value) {
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  if (!mprisplayer_proxy_) {
+    PostError(async_call_id);
+    return;
+  }
+
+  mprismediaplayer2_player_call_next(
+      mprisplayer_proxy_,
+      cancellable_,
+      OnNextCallBack,
+      new CallbackData(this, async_call_id));
+}
+
+void MediaRenderer::Previous(const picojson::value& value) {
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  if (!mprisplayer_proxy_) {
+    PostError(async_call_id);
+    return;
+  }
+
+  mprismediaplayer2_player_call_previous(
+      mprisplayer_proxy_,
+      cancellable_,
+      OnPreviousCallBack,
+      new CallbackData(this, async_call_id));
+}
+
+void MediaRenderer::Mute(const picojson::value& value) {
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  if (!mprisplayer_proxy_) {
+    PostError(async_call_id);
+    return;
+  }
+
+  mprismediaplayer2_player_set_mute(
+      mprisplayer_proxy_,
+      (gboolean)value.get("mute").get<double>());
+  PostResult("setMuteCompleted", value.get("asyncCallId").get<double>());
+}
+
+void MediaRenderer::SetSpeed(const picojson::value& value) {
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  if (!mprisplayer_proxy_) {
+    PostError(async_call_id);
+    return;
+  }
+
+  mprismediaplayer2_player_set_rate(
+      mprisplayer_proxy_,
+      value.get("speed").get<double>());
+  PostResult("setSpeedCompleted", value.get("asyncCallId").get<double>());
+}
+
+void MediaRenderer::SetVolume(const picojson::value& value) {
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  if (!mprisplayer_proxy_) {
+    PostError(async_call_id);
+    return;
+  }
+  mprismediaplayer2_player_set_rate(
+      mprisplayer_proxy_,
+      value.get("volume").get<double>());
+  PostResult("setVolumeCompleted", value.get("asyncCallId").get<double>());
+}
+
+void MediaRenderer::GotoTrack(const picojson::value& value) {
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  if (!mprisplayer_proxy_) {
+    PostError(async_call_id);
+    return;
+  }
+
+  mprismediaplayer2_player_call_goto_track(
+      mprisplayer_proxy_,
+      (guint) value.get("track").get<double>(),
+      cancellable_,
+      OnGoToTrackCallBack,
+      new CallbackData(this, async_call_id));
+}
+
+void MediaRenderer::PostResult(
+    const std::string& 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 MediaRenderer::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 MediaRenderer::OnOpenURI(
+    GObject* source_object,
+    GAsyncResult* res,
+    double async_id) {
+  GError* gerror = NULL;
+
+  if (mprismediaplayer2_player_call_open_uri_ex_finish(
+      reinterpret_cast<mprismediaplayer2Player*>(source_object),
+      res,
+      &gerror))
+    PostResult("openURICompleted", async_id);
+  else
+    PostError(async_id);
+
+  if (gerror)
+    g_error_free(gerror);
+}
+
+void MediaRenderer::OnHostFile(
+    GObject* source_object,
+    GAsyncResult* res,
+    double async_id) {
+  GError* gerror = NULL;
+  gchar* uploaded_path = NULL;
+
+  if (dleyna_push_host_call_host_file_finish(
+      reinterpret_cast<dleynaPushHost*>(source_object),
+      &uploaded_path,
+      res,
+      &gerror)) {
+    mprismediaplayer2_player_call_open_uri_ex(
+    mprisplayer_proxy_,
+    uploaded_path,
+    uploaded_path,
+    cancellable_,
+    OnOpenURICallBack,
+    new CallbackData(this, async_id));
+  } else {
+    PostError(async_id);
+  }
+
+  if (gerror)
+    g_error_free(gerror);
+}
+
+void MediaRenderer::OnPrefetchURI(
+    GObject* source_object,
+    GAsyncResult* res,
+    double async_id) {
+  GError* gerror = NULL;
+  GVariant* objects;
+  guint totalItems;
+
+  if (mprismediaplayer2_player_call_open_next_uri_finish(
+      reinterpret_cast<mprismediaplayer2Player*>(source_object),
+      res,
+      &gerror))
+    PostResult("prefetchURICompleted", async_id);
+  else
+    PostError(async_id);
+
+  if (gerror)
+    g_error_free(gerror);
+}
+
+void MediaRenderer::OnCancel(
+    GObject* source_object,
+    GAsyncResult* res,
+    double async_id) {
+  GError* gerror = NULL;
+  gchar** out_Paths = NULL;
+
+  if (dleyna_renderer_device_call_cancel_finish(
+      reinterpret_cast<dleynaRendererDevice*>(source_object),
+      res,
+      &gerror))
+    PostResult("cancelCompleted", async_id);
+  else
+    PostError(async_id);
+
+  if (gerror)
+    g_error_free(gerror);
+}
+
+void MediaRenderer::OnPlay(
+    GObject* source_object,
+    GAsyncResult* res,
+    double async_id) {
+  GError* gerror = NULL;
+  gchar** out_Paths = NULL;
+
+  if (mprismediaplayer2_player_call_play_finish(
+      reinterpret_cast<mprismediaplayer2Player*>(source_object),
+      res,
+      &gerror))
+    PostResult("playCompleted", async_id);
+  else
+    PostError(async_id);
+
+  if (gerror)
+    g_error_free(gerror);
+}
+
+void MediaRenderer::OnPause(
+    GObject* source_object,
+    GAsyncResult* res,
+    double async_id) {
+  GError* gerror = NULL;
+  gchar** out_Paths = NULL;
+
+  if (mprismediaplayer2_player_call_pause_finish(
+      reinterpret_cast<mprismediaplayer2Player*>(source_object),
+      res,
+      &gerror))
+    PostResult("pauseCompleted", async_id);
+  else
+    PostError(async_id);
+
+  if (gerror)
+    g_error_free(gerror);
+}
+
+void MediaRenderer::OnStop(
+    GObject* source_object,
+    GAsyncResult* res,
+    double async_id) {
+  GError* gerror = NULL;
+  gchar** out_Paths = NULL;
+
+  if (mprismediaplayer2_player_call_stop_finish(
+      reinterpret_cast<mprismediaplayer2Player*>(source_object),
+      res,
+      &gerror))
+    PostResult("stopCompleted", async_id);
+  else
+    PostError(async_id);
+
+  if (gerror)
+    g_error_free(gerror);
+}
+
+void MediaRenderer::OnNext(
+    GObject* source_object,
+    GAsyncResult* res,
+    double async_id) {
+  GError* gerror = NULL;
+  gchar** out_Paths = NULL;
+
+  if (mprismediaplayer2_player_call_next_finish(
+      reinterpret_cast<mprismediaplayer2Player*>(source_object),
+      res,
+      &gerror))
+    PostResult("nextCompleted", async_id);
+  else
+    PostError(async_id);
+
+  if (gerror)
+    g_error_free(gerror);
+}
+
+void MediaRenderer::OnPrevious(
+    GObject* source_object,
+    GAsyncResult* res,
+    double async_id) {
+  GError* gerror = NULL;
+  gchar** out_Paths = NULL;
+
+  if (mprismediaplayer2_player_call_previous_finish(
+      reinterpret_cast<mprismediaplayer2Player*>(source_object),
+      res,
+      &gerror))
+    PostResult("previousCompleted", async_id);
+  else
+    PostError(async_id);
+
+  if (gerror)
+    g_error_free(gerror);
+}
+
+void MediaRenderer::OnGoToTrack(
+    GObject* source_object,
+    GAsyncResult* res,
+    double async_id) {
+  GError* gerror = NULL;
+  gchar** out_Paths = NULL;
+
+  if (mprismediaplayer2_player_call_goto_track_finish(
+      reinterpret_cast<mprismediaplayer2Player*>(source_object),
+      res,
+      &gerror))
+    PostResult("gotoTrackCompleted", async_id);
+  else
+    PostError(async_id);
+
+  if (gerror)
+    g_error_free(gerror);
+}
diff --git a/media_renderer/media_renderer.gyp b/media_renderer/media_renderer.gyp
new file mode 100644 (file)
index 0000000..39f093f
--- /dev/null
@@ -0,0 +1,194 @@
+{
+  'includes':[
+    '../common/common.gypi',
+  ],
+  'variables': {
+    'gen_dbus_proxy_path': '<(SHARED_INTERMEDIATE_DIR)/media_renderer',
+  },
+  'targets': [
+    {
+      'target_name': 'tizen_media_renderer',
+      'type': 'loadable_module',
+      'variables': {
+        'packages': [
+          'gio-2.0',
+          'gio-unix-2.0',
+        ],
+      },
+      'dependencies': [
+        'tizen_media_renderer_gen',
+      ],
+      'sources': [
+        'media_renderer_api.js',
+        'media_renderer.cc',
+        'media_renderer.h',
+        'media_renderer_extension.cc',
+        'media_renderer_extension.h',
+        'media_renderer_instance.cc',
+        'media_renderer_instance.h',
+        'media_renderer_manager.cc',
+        'media_renderer_manager.h',
+      ],
+      'includes': [
+        '../common/pkg-config.gypi',
+      ],
+    },
+    {
+      'target_name': 'tizen_media_renderer_gen',
+      'type': 'static_library',
+      'variables': {
+        'packages': [
+          'gio-2.0',
+          'gio-unix-2.0',
+        ],
+      },
+      'include_dirs': [
+        './',
+      ],
+      'actions': [
+        {
+          'variables': {
+            'generate_args': [
+              '--interface-prefix',
+              'com.intel.dLeynaRenderer.',
+              '--c-namespace',
+              'dleyna',
+              '--generate-c-code',
+              '<(gen_dbus_proxy_path)/dleyna_manager_gen',
+            ],
+          },
+          'action_name': 'dleyna_manager_gen',
+          'inputs': [
+            'dbus_interfaces/com.intel.dLeynaRenderer.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.dLeynaRenderer.',
+              '--c-namespace',
+              'dleyna',
+              '--generate-c-code',
+              '<(gen_dbus_proxy_path)/dleyna_renderer_device_gen',
+            ],
+          },
+          'action_name': 'dleyna_media_device_gen',
+          'inputs': [
+            'dbus_interfaces/com.intel.dLeynaRenderer.RendererDevice.xml',
+          ],
+          'outputs': [
+            '<(gen_dbus_proxy_path)/dleyna_renderer_device_gen.c',
+            '<(gen_dbus_proxy_path)/dleyna_renderer_device_gen.h',
+          ],
+          'action': [
+            'gdbus-codegen',
+            '<@(generate_args)',
+            '<@(_inputs)',
+          ],
+        },
+        {
+          'variables': {
+            'generate_args': [
+              '--interface-prefix',
+              'com.intel.dLeynaRenderer',
+              '--c-namespace',
+              'dleyna',
+              '--generate-c-code',
+              '<(gen_dbus_proxy_path)/dleyna_pushhost_gen',
+            ],
+          },
+          'action_name': 'dleyna_pushhost_gen',
+          'inputs': [
+            'dbus_interfaces/com.intel.dLeynaRenderer.PushHost.xml',
+          ],
+          'outputs': [
+            '<(gen_dbus_proxy_path)/dleyna_pushhost_gen.c',
+            '<(gen_dbus_proxy_path)/dleyna_pushhost_gen.h',
+          ],
+          'action': [
+            'gdbus-codegen',
+            '<@(generate_args)',
+            '<@(_inputs)',
+          ],
+        },
+        {
+          'variables': {
+            'generate_args': [
+              '--interface-prefix',
+              'org.mpris.',
+              '--c-namespace',
+              'mpris',
+              '--generate-c-code',
+              '<(gen_dbus_proxy_path)/mpris_mediaplayer2_gen',
+            ],
+          },
+          'action_name': 'mpris_mediaplayer2_gen',
+          'inputs': [
+            'dbus_interfaces/org.mpris.MediaPlayer2.xml',
+          ],
+          'outputs': [
+            '<(gen_dbus_proxy_path)/mpris_mediaplayer2_gen.c',
+            '<(gen_dbus_proxy_path)/mpris_mediaplayer2_gen.h',
+          ],
+          'action': [
+            'gdbus-codegen',
+            '<@(generate_args)',
+            '<@(_inputs)',
+          ],
+        },
+        {
+          'variables': {
+            'generate_args': [
+              '--interface-prefix',
+              'org.mpris.MediaPlayer2.',
+              '--c-namespace',
+              'mprismediaplayer2',
+              '--generate-c-code',
+              '<(gen_dbus_proxy_path)/mpris_mediaplayer2_player_gen',
+            ],
+          },
+          'action_name': 'mpris_mediaplayer2_player_gen',
+          'inputs': [
+            'dbus_interfaces/org.mpris.MediaPlayer2.Player.xml',
+          ],
+          'outputs': [
+            '<(gen_dbus_proxy_path)/mpris_mediaplayer2_player_gen.c',
+            '<(gen_dbus_proxy_path)/mpris_mediaplayer2_player_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_renderer_device_gen.c',
+        '<(gen_dbus_proxy_path)/dleyna_renderer_device_gen.h',
+        '<(gen_dbus_proxy_path)/dleyna_pushhost_gen.c',
+        '<(gen_dbus_proxy_path)/dleyna_pushhost_gen.h',
+        '<(gen_dbus_proxy_path)/mpris_mediaplayer2_player_gen.c',
+        '<(gen_dbus_proxy_path)/mpris_mediaplayer2_player_gen.h',
+        '<(gen_dbus_proxy_path)/mpris_mediaplayer2_gen.c',
+        '<(gen_dbus_proxy_path)/mpris_mediaplayer2_gen.h',
+      ],
+      'includes': [
+        '../common/pkg-config.gypi',
+      ],
+    },
+  ],
+}
diff --git a/media_renderer/media_renderer.h b/media_renderer/media_renderer.h
new file mode 100644 (file)
index 0000000..632d35a
--- /dev/null
@@ -0,0 +1,74 @@
+// 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 MEDIA_RENDERER_MEDIA_RENDERER_H_
+#define MEDIA_RENDERER_MEDIA_RENDERER_H_
+
+namespace common {
+
+class Instance;
+
+}  // namespace common
+
+#include <string>
+#include "common/picojson.h"
+#include "media_renderer/callbacks.h"
+#include "media_renderer/dleyna_renderer_device_gen.h"
+#include "media_renderer/dleyna_pushhost_gen.h"
+#include "media_renderer/mpris_mediaplayer2_gen.h"
+#include "media_renderer/mpris_mediaplayer2_player_gen.h"
+
+class MediaRenderer {
+ public:
+  MediaRenderer(common::Instance* instance, const std::string& object_path);
+  virtual ~MediaRenderer();
+
+  void OpenURI(const picojson::value& value);
+  void PrefetchURI(const picojson::value& value);
+  void Cancel(const picojson::value& value);
+  bool IsCancelled() const;
+
+  // MediaController methods
+  void Play(const picojson::value& value);
+  void Pause(const picojson::value& value);
+  void Stop(const picojson::value& value);
+  void Next(const picojson::value& value);
+  void Previous(const picojson::value& value);
+  void Mute(const picojson::value& value);
+  void SetSpeed(const picojson::value& value);
+  void SetVolume(const picojson::value& value);
+  void GotoTrack(const picojson::value& value);
+
+  picojson::value ToJSON();
+
+ private:
+  void PostResult(const std::string& completed_operation,
+                  double async_operation_id);
+  void PostError(double async_operation_id);
+
+  CALLBACK_METHOD_WITH_ID(OnOpenURI, GObject*, GAsyncResult*, MediaRenderer);
+  CALLBACK_METHOD_WITH_ID(OnPrefetchURI, GObject*,
+      GAsyncResult*, MediaRenderer);
+  CALLBACK_METHOD_WITH_ID(OnCancel, GObject*, GAsyncResult*, MediaRenderer);
+  CALLBACK_METHOD_WITH_ID(OnHostFile, GObject*, GAsyncResult*, MediaRenderer);
+
+  CALLBACK_METHOD_WITH_ID(OnPlay, GObject*, GAsyncResult*, MediaRenderer);
+  CALLBACK_METHOD_WITH_ID(OnPause, GObject*, GAsyncResult*, MediaRenderer);
+  CALLBACK_METHOD_WITH_ID(OnStop, GObject*, GAsyncResult*, MediaRenderer);
+  CALLBACK_METHOD_WITH_ID(OnNext, GObject*, GAsyncResult*, MediaRenderer);
+  CALLBACK_METHOD_WITH_ID(OnPrevious, GObject*, GAsyncResult*, MediaRenderer);
+  CALLBACK_METHOD_WITH_ID(OnGoToTrack, GObject*, GAsyncResult*, MediaRenderer);
+
+ private:
+  common::Instance* instance_;
+  picojson::value::object object_;
+  dleynaRendererDevice* rendererdevice_proxy_;
+  dleynaPushHost* pushhost_proxy_;
+  mprisMediaPlayer2* mediaplayer2_proxy_;
+  mprismediaplayer2Player* mprisplayer_proxy_;
+  std::string object_path_;
+  GCancellable* cancellable_;
+};
+
+#endif  // MEDIA_RENDERER_MEDIA_RENDERER_H_
diff --git a/media_renderer/media_renderer_api.js b/media_renderer/media_renderer_api.js
new file mode 100644 (file)
index 0000000..95d0712
--- /dev/null
@@ -0,0 +1,321 @@
+// 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]
+    });
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Exports and main entry point for the MediaRenderer API
+///////////////////////////////////////////////////////////////////////////////
+
+var g_media_renderer_manager = new MediaRendererManager();
+exports = g_media_renderer_manager;
+
+var g_media_renderer_manager_listeners = {};
+g_media_renderer_manager_listeners['rendererfound'] = [];
+g_media_renderer_manager_listeners['rendererlost'] = [];
+
+extension.setMessageListener(function(json) {
+  var msg = JSON.parse(json);
+  switch (msg.cmd) {
+    case 'rendererFound':
+      handleMediaRendererFound(msg);
+      break;
+    case 'rendererLost':
+      handleMediaRendererLost(msg);
+      break;
+    case 'getRenderersCompleted':
+      handleGetRenderersCompleted(msg);
+      break;
+    case 'openURICompleted':
+    case 'prefetchURICompleted':
+    case 'cancelCompleted':
+    case 'playCompleted':
+    case 'pauseCompleted':
+    case 'stopCompleted':
+    case 'nextCompleted':
+    case 'previousCompleted':
+    case 'muteCompleted':
+    case 'setSpeedCompleted':
+    case 'setVolumeCompleted':
+    case 'gotoTrackCompleted':
+      handleAsyncCallSuccess(msg);
+      break;
+    case 'asyncCallError':
+      handleAsyncCallError(msg);
+      break;
+    default:
+      console.error('[MediaRenderer]:' + "Unknown signal: '" + msg.cmd + "' from backend");
+  }
+});
+
+function handleMediaRendererFound(msg) {
+  var event = new CustomEvent('rendererfound');
+  _addConstProperty(event, 'renderer', new MediaRenderer(msg.renderer));
+  g_media_renderer_manager.dispatchEvent(event);
+  if (g_media_renderer_manager.onrendererfound)
+    g_media_renderer_manager.onrendererfound(event);
+}
+
+function handleMediaRendererLost(msg) {
+  var event = new CustomEvent('rendererlost');
+  _addConstProperty(event, 'id', msg.lostRendererId);
+  g_media_renderer_manager.dispatchEvent(event);
+  if (g_media_renderer_manager.onrendererlost)
+    g_media_renderer_manager.onrendererlost(event);
+}
+
+function handleAsyncCallSuccess(msg) {
+  g_async_calls[msg.asyncCallId].resolve();
+}
+
+function handleAsyncCallError(msg) {
+  g_async_calls[msg.asyncCallId].reject(Error('Async operation failed'));
+}
+
+function handleGetRenderersCompleted(msg) {
+  g_async_calls[msg.asyncCallId].resolve(msg.renderers);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// MediaRendererManager
+///////////////////////////////////////////////////////////////////////////////
+
+function MediaRendererManager() {
+  this.onrendererfound = null;
+  this.onrendererlost = null;
+}
+
+function isValidType(type) {
+  return (type === 'rendererlost' || type === 'rendererfound');
+}
+
+MediaRendererManager.prototype.addEventListener = function(type, callback) {
+  if (callback != null && isValidType(type))
+    if (~~g_media_renderer_manager_listeners[type].indexOf(callback))
+      g_media_renderer_manager_listeners[type].push(callback);
+};
+
+MediaRendererManager.prototype.removeEventListener = function(type, callback) {
+  if (callback == null || !isValidType(type))
+    return;
+
+  var index = g_media_renderer_manager_listeners[type].indexOf(callback);
+  if (~index)
+    g_media_renderer_manager_listeners[type].slice(index, 1);
+};
+
+MediaRendererManager.prototype.dispatchEvent = function(event) {
+  var handled = true;
+
+  if (typeof event !== 'object' || !isValidType(event.type))
+    return false;
+
+  g_media_renderer_manager_listeners[event.type].forEach(function(callback) {
+    var res = callback(event);
+    if (!res && handled)
+      handled = false;
+  });
+
+  return handled;
+};
+
+MediaRendererManager.prototype.scanNetwork = function() {
+  var msg = {
+    'cmd': 'scanNetwork'
+  };
+  extension.postMessage(JSON.stringify(msg));
+};
+
+function getRenderers() {
+  var msg = {
+    'cmd': 'getRenderers'
+  };
+  return createPromise(msg);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// MediaRenderer
+///////////////////////////////////////////////////////////////////////////////
+
+function MediaRenderer(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);
+  _addConstPropertyFromObject(this, 'protocolInfo', obj);
+  _addConstProperty(this, 'controller', new MediaController(obj.controller));
+  this.oncontainerchanged = null;
+}
+
+MediaRenderer.prototype.openURI = function(mediaURI, metaData) {
+  var msg = {
+    'cmd': 'openURI',
+    'rendererId': this.id,
+    'mediaURI': mediaURI,
+    'metaData': metaData
+  };
+  return createPromise(msg);
+};
+
+MediaRenderer.prototype.prefetchURI = function(mediaURI, metaData) {
+  var msg = {
+    'cmd': 'prefetchURI',
+    'rendererId': this.id,
+    'mediaURI': mediaURI,
+    'metaData': metaData
+  };
+  return createPromise(msg);
+};
+
+MediaRenderer.prototype.cancel = function() {
+  var msg = {
+    'cmd': 'cancel',
+    'rendererId': this.id
+  };
+  return createPromise(msg);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// MediaController
+///////////////////////////////////////////////////////////////////////////////
+
+function MediaController(obj) {
+  _addConstPropertyFromObject(this, 'id', obj);
+  _addConstPropertyFromObject(this, 'playbackStatus', obj);
+  _addConstPropertyFromObject(this, 'muted', obj);
+  _addConstPropertyFromObject(this, 'volume', obj);
+  _addConstPropertyFromObject(this, 'track', obj);
+  _addConstPropertyFromObject(this, 'speed', obj);
+  _addConstPropertyFromObject(this, 'playSpeeds', obj);
+  this.onstatuschanged = null;
+}
+
+MediaController.prototype.play = function() {
+  var msg = {
+    'cmd': 'play',
+    'rendererId': this.id
+  };
+  return createPromise(msg);
+};
+
+MediaController.prototype.pause = function() {
+  var msg = {
+    'cmd': 'pause',
+    'rendererId': this.id
+  };
+  return createPromise(msg);
+};
+
+MediaController.prototype.stop = function() {
+  var msg = {
+    'cmd': 'stop',
+    'rendererId': this.id
+  };
+  return createPromise(msg);
+};
+
+MediaController.prototype.next = function() {
+  var msg = {
+    'cmd': 'next',
+    'rendererId': this.id
+  };
+  return createPromise(msg);
+};
+
+MediaController.prototype.previous = function() {
+  var msg = {
+    'cmd': 'previous',
+    'rendererId': this.id
+  };
+  return createPromise(msg);
+};
+
+MediaController.prototype.mute = function(mute) {
+  var msg = {
+    'cmd': 'mute',
+    'rendererId': this.id,
+    'mute': mute
+  };
+  return createPromise(msg);
+};
+
+MediaController.prototype.setSpeed = function(speed) {
+  var msg = {
+    'cmd': 'setSpeed',
+    'rendererId': this.id,
+    'speed': speed
+  };
+  return createPromise(msg);
+};
+
+MediaController.prototype.setVolume = function(volume) {
+  var msg = {
+    'cmd': 'setVolume',
+    'rendererId': this.id,
+    'volume': volume
+  };
+  return createPromise(msg);
+};
+
+MediaController.prototype.gotoTrack = function(track) {
+  var msg = {
+    'cmd': 'gotoTrack',
+    'rendererId': this.id,
+    'track': track
+  };
+  return createPromise(msg);
+};
diff --git a/media_renderer/media_renderer_extension.cc b/media_renderer/media_renderer_extension.cc
new file mode 100644 (file)
index 0000000..3c746a8
--- /dev/null
@@ -0,0 +1,24 @@
+// 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 "media_renderer/media_renderer_extension.h"
+
+#include "media_renderer/media_renderer_instance.h"
+
+common::Extension* CreateExtension() {
+  return new MediaRendererExtension;
+}
+
+extern const char kSource_media_renderer_api[];
+
+MediaRendererExtension::MediaRendererExtension() {
+  SetExtensionName("navigator.mediaRenderer");
+  SetJavaScriptAPI(kSource_media_renderer_api);
+}
+
+MediaRendererExtension::~MediaRendererExtension() {}
+
+common::Instance* MediaRendererExtension::CreateInstance() {
+  return new MediaRendererInstance;
+}
diff --git a/media_renderer/media_renderer_extension.h b/media_renderer/media_renderer_extension.h
new file mode 100644 (file)
index 0000000..64efade
--- /dev/null
@@ -0,0 +1,20 @@
+// 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 MEDIA_RENDERER_MEDIA_RENDERER_EXTENSION_H_
+#define MEDIA_RENDERER_MEDIA_RENDERER_EXTENSION_H_
+
+#include "common/extension.h"
+
+class MediaRendererExtension : public common::Extension {
+ public:
+  MediaRendererExtension();
+  virtual ~MediaRendererExtension();
+
+ private:
+  // common::Extension implementation.
+  virtual common::Instance* CreateInstance();
+};
+
+#endif  // MEDIA_RENDERER_MEDIA_RENDERER_EXTENSION_H_
diff --git a/media_renderer/media_renderer_instance.cc b/media_renderer/media_renderer_instance.cc
new file mode 100644 (file)
index 0000000..6519790
--- /dev/null
@@ -0,0 +1,91 @@
+// 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 "media_renderer/media_renderer_instance.h"
+
+#include <string>
+
+#include "common/picojson.h"
+#include "media_renderer/media_renderer_manager.h"
+
+MediaRendererInstance::MediaRendererInstance()
+    : worker_thread_(&MediaRendererInstance::InitWorkerThread, this) {
+  worker_thread_.detach();
+}
+
+MediaRendererInstance::~MediaRendererInstance() {
+  g_main_loop_quit(worker_loop_);
+  delete media_renderer_manager_;
+}
+
+gboolean MediaRendererInstance::CreateMediaRendererManager(void* data) {
+  if (!data) {
+    std::cerr << "Null pointer is passed to callback" << std::endl;
+    return FALSE;
+  }
+
+  MediaRendererInstance* instance = static_cast<MediaRendererInstance*>(data);
+  instance->media_renderer_manager_ = new MediaRendererManager(instance);
+  return FALSE;
+}
+
+void MediaRendererInstance::InitWorkerThread() {
+  GMainContext* context = g_main_context_default();
+  worker_loop_ = g_main_loop_new(context, FALSE);
+  g_main_context_push_thread_default(context);
+  GSource* source = g_idle_source_new();
+  g_source_set_callback(source,
+                        &MediaRendererInstance::CreateMediaRendererManager,
+                        this,
+                        NULL);
+  g_source_attach(source, context);
+  g_main_loop_run(worker_loop_);
+  g_source_destroy(source);
+  g_source_unref(source);
+  g_main_loop_unref(worker_loop_);
+}
+
+void MediaRendererInstance::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_renderer_manager_->ScanNetwork();
+  else if (cmd == "getRenderers")
+    media_renderer_manager_->GetRenderers(v);
+  else if (cmd == "openURI")
+    media_renderer_manager_->HandleOpenURI(v);
+  else if (cmd == "prefetchURI")
+    media_renderer_manager_->HandlePrefetchURI(v);
+  else if (cmd == "cancel")
+    media_renderer_manager_->HandleCancel(v);
+  else if (cmd == "play")
+    media_renderer_manager_->HandlePlay(v);
+  else if (cmd == "pause")
+    media_renderer_manager_->HandlePause(v);
+  else if (cmd == "stop")
+    media_renderer_manager_->HandleStop(v);
+  else if (cmd == "next")
+    media_renderer_manager_->HandleNext(v);
+  else if (cmd == "previous")
+    media_renderer_manager_->HandlePrevious(v);
+  else if (cmd == "mute")
+    media_renderer_manager_->HandleMute(v);
+  else if (cmd == "setSpeed")
+    media_renderer_manager_->HandleSetSpeed(v);
+  else if (cmd == "setVolume")
+    media_renderer_manager_->HandleSetVolume(v);
+  else if (cmd == "gotoTrack")
+    media_renderer_manager_->HandleGotoTrack(v);
+  else
+    std::cerr << "Received unknown message: " << cmd << "\n";
+}
+
+void MediaRendererInstance::HandleSyncMessage(const char* message) {}
diff --git a/media_renderer/media_renderer_instance.h b/media_renderer/media_renderer_instance.h
new file mode 100644 (file)
index 0000000..ceca7b3
--- /dev/null
@@ -0,0 +1,38 @@
+// 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 MEDIA_RENDERER_MEDIA_RENDERER_INSTANCE_H_
+#define MEDIA_RENDERER_MEDIA_RENDERER_INSTANCE_H_
+
+#include <glib.h>
+#include <thread>  // NOLINT
+
+#include "common/extension.h"
+
+class MediaRendererManager;
+
+namespace picojson {
+
+class value;
+
+}  // namespace picojson
+
+class MediaRendererInstance : public common::Instance {
+ public:
+  MediaRendererInstance();
+  virtual ~MediaRendererInstance();
+
+ private:
+  void InitWorkerThread();
+  static gboolean CreateMediaRendererManager(void* data);
+  // common::Instance implementation.
+  virtual void HandleMessage(const char* msg);
+  virtual void HandleSyncMessage(const char* msg);
+
+  std::thread worker_thread_;
+  GMainLoop* worker_loop_ = NULL;
+  MediaRendererManager* media_renderer_manager_ = NULL;
+};
+
+#endif  // MEDIA_RENDERER_MEDIA_RENDERER_INSTANCE_H_
diff --git a/media_renderer/media_renderer_manager.cc b/media_renderer/media_renderer_manager.cc
new file mode 100644 (file)
index 0000000..9ff6108
--- /dev/null
@@ -0,0 +1,234 @@
+// 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 "media_renderer/media_renderer_manager.h"
+
+#include <utility>
+#include <string>
+#include "common/extension.h"
+#include "media_renderer/media_renderer.h"
+
+typedef std::pair<std::string, std::shared_ptr<MediaRenderer>>
+    MediaRendererPair;
+
+MediaRendererManager::MediaRendererManager(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-renderer",
+      "/com/intel/dLeynaRenderer",
+      NULL,
+      &gerror);
+
+  if (gerror) {
+    g_error_free(gerror);
+    return;
+  }
+
+  g_signal_connect(
+      manager_proxy_,
+      "found-renderer",
+      G_CALLBACK(OnFoundRendererCallBack),
+      this);
+
+  g_signal_connect(
+      manager_proxy_,
+      "lost-renderer",
+      G_CALLBACK(OnLostRendererCallBack),
+      this);
+}
+
+MediaRendererManager::~MediaRendererManager() {
+  g_object_unref(manager_proxy_);
+}
+
+void MediaRendererManager::ScanNetwork() {
+  if (!manager_proxy_)
+    return;
+
+  dleyna_manager_call_get_renderers(
+      manager_proxy_,
+      NULL,
+      OnScanNetworkCallBack,
+      this);
+}
+
+void MediaRendererManager::GetRenderers(const picojson::value& value) {
+  if (!manager_proxy_)
+    return;
+
+  double async_call_id = value.get("asyncCallId").get<double>();
+
+  dleyna_manager_call_get_renderers(
+      manager_proxy_,
+      cancellable_,
+      OnGetRenderersCallBack,
+      new CallbackData(this, async_call_id));
+}
+
+void MediaRendererManager::HandleOpenURI(const picojson::value& value) {
+  if (MediaRendererPtr renderer = GetMediaRendererById(value))
+    renderer->OpenURI(value);
+}
+
+void MediaRendererManager::HandlePrefetchURI(
+    const picojson::value& value) {
+  if (MediaRendererPtr renderer = GetMediaRendererById(value))
+    renderer->PrefetchURI(value);
+}
+
+void MediaRendererManager::HandleCancel(const picojson::value& value) {
+  if (MediaRendererPtr renderer = GetMediaRendererById(value))
+    renderer->Cancel(value);
+}
+
+void MediaRendererManager::HandlePlay(const picojson::value& value) {
+  if (MediaRendererPtr renderer = GetMediaRendererById(value))
+    renderer->Play(value);
+}
+
+void MediaRendererManager::HandlePause(const picojson::value& value) {
+  if (MediaRendererPtr renderer = GetMediaRendererById(value))
+    renderer->Pause(value);
+}
+
+void MediaRendererManager::HandleStop(const picojson::value& value) {
+  if (MediaRendererPtr renderer = GetMediaRendererById(value))
+    renderer->Stop(value);
+}
+
+void MediaRendererManager::HandleNext(const picojson::value& value) {
+  if (MediaRendererPtr renderer = GetMediaRendererById(value))
+    renderer->Next(value);
+}
+
+void MediaRendererManager::HandlePrevious(const picojson::value& value) {
+  if (MediaRendererPtr renderer = GetMediaRendererById(value))
+    renderer->Previous(value);
+}
+
+void MediaRendererManager::HandleMute(const picojson::value& value) {
+  if (MediaRendererPtr renderer = GetMediaRendererById(value))
+    renderer->Mute(value);
+}
+
+void MediaRendererManager::HandleSetSpeed(const picojson::value& value) {
+  if (MediaRendererPtr renderer = GetMediaRendererById(value))
+    renderer->SetSpeed(value);
+}
+
+void MediaRendererManager::HandleSetVolume(const picojson::value& value) {
+  if (MediaRendererPtr renderer = GetMediaRendererById(value))
+    renderer->SetVolume(value);
+}
+
+void MediaRendererManager::HandleGotoTrack(const picojson::value& value) {
+  if (MediaRendererPtr renderer = GetMediaRendererById(value))
+    renderer->GotoTrack(value);
+}
+
+void MediaRendererManager::PostRendererFound(const std::string& path) {
+  MediaRendererPtr media_renderer(new MediaRenderer(instance_, path));
+  media_renderers_.insert(MediaRendererPair(path, media_renderer));
+  picojson::value::object object;
+  object["cmd"] = picojson::value("rendererFound");
+  object["renderer"] = media_renderer->ToJSON();
+  picojson::value value(object);
+  instance_->PostMessage(value.serialize().c_str());
+}
+
+
+MediaRendererPtr MediaRendererManager::GetMediaRendererById(
+    const picojson::value& id) {
+  return GetMediaRendererById(id.get("rendererId").to_str());
+}
+
+MediaRendererPtr MediaRendererManager::GetMediaRendererById(
+    const std::string& id) {
+  if (!media_renderers_.size())
+    return MediaRendererPtr();
+
+  std::map<std::string, MediaRendererPtr>::const_iterator it;
+  if ((it = media_renderers_.find(id)) != media_renderers_.end())
+    return (*it).second;
+
+  return MediaRendererPtr();
+}
+
+void MediaRendererManager::OnScanNetwork(
+    GObject* source_object,
+    GAsyncResult* res) {
+  GError* gerror = NULL;
+  gchar** out_renderers;
+  if (!dleyna_manager_call_get_renderers_finish(
+      manager_proxy_,
+      &out_renderers,
+      res,
+      &gerror)) {
+    g_error_free(gerror);
+    return;
+  }
+
+  while (gchar* renderer_path = *out_renderers) {
+    PostRendererFound(std::string(*out_renderers));
+    out_renderers++;
+    g_free(renderer_path);
+  }
+}
+
+void MediaRendererManager::OnGetRenderers(
+    GObject* source_object,
+    GAsyncResult* res,
+    double async_id) {
+  GError* gerror = NULL;
+  gchar** out_renderers;
+  if (!dleyna_manager_call_get_renderers_finish(
+      manager_proxy_,
+      &out_renderers,
+      res,
+      &gerror)) {
+    g_error_free(gerror);
+    return;
+  }
+
+  picojson::value::array renderers;
+
+  while (gchar* renderer_path = *out_renderers) {
+    MediaRendererPtr media_renderer(new MediaRenderer(instance_,
+        std::string(*out_renderers)));
+    media_renderers_.insert(MediaRendererPair(std::string(*out_renderers),
+        media_renderer));
+    renderers.push_back(media_renderer->ToJSON());
+    out_renderers++;
+    g_free(renderer_path);
+  }
+
+  picojson::value::object object;
+  object["cmd"] = picojson::value("getRenderersCompleted");
+  object["asyncCallId"] = picojson::value(async_id);
+  object["renderers"] =   picojson::value(renderers);
+  picojson::value value(object);
+  instance_->PostMessage(value.serialize().c_str());
+}
+
+void MediaRendererManager::OnFoundRenderer(dleynaManager* object,
+                                           const gchar* arg_Path) {
+  PostRendererFound(std::string(arg_Path));
+}
+
+void MediaRendererManager::OnLostRenderer(dleynaManager* object,
+                                          const gchar* arg_Path) {
+  MediaRendererPtr renderer = GetMediaRendererById(std::string(arg_Path));
+  if (!renderer)
+    return;
+
+  picojson::value::object pobject;
+  pobject["cmd"] = picojson::value("rendererLost");
+  pobject["rendererId"] = renderer->ToJSON().get("id");
+  picojson::value value(pobject);
+  instance_->PostMessage(value.serialize().c_str());
+  media_renderers_.erase(arg_Path);
+}
diff --git a/media_renderer/media_renderer_manager.h b/media_renderer/media_renderer_manager.h
new file mode 100644 (file)
index 0000000..de7ea3d
--- /dev/null
@@ -0,0 +1,64 @@
+// 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 MEDIA_RENDERER_MEDIA_RENDERER_MANAGER_H_
+#define MEDIA_RENDERER_MEDIA_RENDERER_MANAGER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include "media_renderer/dleyna_manager_gen.h"
+#include "media_renderer/media_renderer.h"
+#include "media_renderer/callbacks.h"
+
+namespace common {
+
+class Instance;
+
+}  // namespace common
+
+typedef std::shared_ptr<MediaRenderer> MediaRendererPtr;
+
+class MediaRendererManager {
+ public:
+  explicit MediaRendererManager(common::Instance* instance);
+  virtual ~MediaRendererManager();
+
+  void ScanNetwork();
+  void GetRenderers(const picojson::value& value);
+  void HandleOpenURI(const picojson::value& value);
+  void HandlePrefetchURI(const picojson::value& value);
+  void HandleCancel(const picojson::value& value);
+  void HandlePlay(const picojson::value& value);
+  void HandlePause(const picojson::value& value);
+  void HandleStop(const picojson::value& value);
+  void HandleNext(const picojson::value& value);
+  void HandlePrevious(const picojson::value& value);
+  void HandleMute(const picojson::value& value);
+  void HandleSetSpeed(const picojson::value& value);
+  void HandleSetVolume(const picojson::value& value);
+  void HandleGotoTrack(const picojson::value& value);
+
+ private:
+  void PostRendererFound(const std::string& path);
+  MediaRendererPtr GetMediaRendererById(const picojson::value& id);
+  MediaRendererPtr GetMediaRendererById(const std::string& id);
+  bool IsCancelled() { return g_cancellable_is_cancelled(cancellable_); }
+
+  CALLBACK_METHOD(OnScanNetwork, GObject*, GAsyncResult*, MediaRendererManager);
+  CALLBACK_METHOD_WITH_ID(OnGetRenderers, GObject*, GAsyncResult*,
+      MediaRendererManager);
+  CALLBACK_METHOD(OnLostRenderer, dleynaManager*,
+                  const gchar*, MediaRendererManager);
+  CALLBACK_METHOD(OnFoundRenderer, dleynaManager*,
+                  const gchar*, MediaRendererManager);
+
+ private:
+  common::Instance* instance_;
+  dleynaManager* manager_proxy_;
+  std::map<std::string, MediaRendererPtr> media_renderers_;
+  GCancellable* cancellable_;
+};
+
+#endif  // MEDIA_RENDERER_MEDIA_RENDERER_MANAGER_H_
index a86957c..52d9080 100644 (file)
@@ -9,6 +9,7 @@
       'type': 'none',
       'dependencies': [
         'bluetooth/bluetooth.gyp:*',
+        'media_renderer/media_renderer.gyp:*',
         'mediaserver/mediaserver.gyp:*',
         'network_bearer_selection/network_bearer_selection.gyp:*',
         'notification/notification.gyp:*',