aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--README.md3
-rw-r--r--plugins/media/media-api.c24
-rw-r--r--plugins/media/media-rygel.c206
-rw-r--r--plugins/media/media-rygel.h7
5 files changed, 215 insertions, 27 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 920899a3..6049f875 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -42,7 +42,7 @@ PKG_CHECK_MODULES(dbus REQUIRED dbus-1)
PKG_CHECK_MODULES(alsa alsa)
PKG_CHECK_MODULES(pulseaudio libpulse libpulse-simple)
PKG_CHECK_MODULES(librtlsdr librtlsdr>=0.5.0)
-PKG_CHECK_MODULES(gupnp gupnp-1.0 gssdp-1.0 gobject-2.0)
+PKG_CHECK_MODULES(gupnp gupnp-1.0 gupnp-av-1.0 gssdp-1.0 gobject-2.0 gio-2.0)
IF(alsa_FOUND)
MESSAGE(STATUS "ALSA found ; will compile Audio plugin... (PLUGIN)")
diff --git a/README.md b/README.md
index 5ba5ceb8..0cfc8da3 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,7 @@ optionally, for plugins :
* alsa ("libasound2-dev/alsa-devel");
* pulseaudio ("libpulse-dev/libpulse-devel");
* rtl-sdr >= 0.5.0 (fetch and build from "git://git.osmocom.org/rtl-sdr");
+ * GUPnP ("libglib2.0-dev libgupnp-av-1.0-dev/glib2-devel libgupnp-av-devel");
and the following tools:
* pkg-config;
@@ -27,7 +28,7 @@ and the following tools:
To install all dependencies under OpenSUSE (excepting rtl-sdr), please type:
```
-$ zypper in file-devel libmicrohttpd-devel libjson-c-devel libuuid-devel dbus-1-devel alsa-devel libpulse-devel pkg-config cmake
+$ zypper in file-devel libmicrohttpd-devel libjson-c-devel libuuid-devel dbus-1-devel alsa-devel libpulse-devel glib2-devel libgupnp-av-devel pkg-config cmake
```
To build, move to the root directory and type:
diff --git a/plugins/media/media-api.c b/plugins/media/media-api.c
index dc73136f..05237d39 100644
--- a/plugins/media/media-api.c
+++ b/plugins/media/media-api.c
@@ -136,6 +136,27 @@ STATIC json_object* paused (AFB_request *request) { /* AFB_SESSION_CHECK */
return jsonNewMessage(AFB_SUCCESS, "Paused media");
}
+STATIC json_object* upload (AFB_request *request) { /* AFB_SESSION_CHECK */
+
+ mediaCtxHandleT *ctx = (mediaCtxHandleT*)request->context;
+ const char *value = getQueryValue (request, "value");
+ json_object *jresp;
+ char path[256];
+
+ /* no "?value=" parameter : return error */
+ if (!value)
+ return jsonNewMessage(AFB_FAIL, "You must provide a file name");
+
+ snprintf (path, sizeof(path), "/tmp/%s", value);
+ if (access (path, R_OK) == -1)
+ return jsonNewMessage(AFB_FAIL, "File not found");
+
+ if (!_rygel_upload (ctx, path))
+ return jsonNewMessage(AFB_FAIL, "Error when uploading file... could not complete");
+
+ return jsonNewMessage(AFB_SUCCESS, "File successfully uploaded");
+}
+
STATIC json_object* ping (AFB_request *request) { /* AFB_SESSION_NONE */
return jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon - Media API");
}
@@ -147,7 +168,8 @@ STATIC AFB_restapi pluginApis[]= {
{"choose" , AFB_SESSION_CHECK, (AFB_apiCB)choose , "Media API - choose" },
{"play" , AFB_SESSION_CHECK, (AFB_apiCB)play , "Media API - play" },
{"stop" , AFB_SESSION_CHECK, (AFB_apiCB)stop , "Media API - stop" },
- {"paused" , AFB_SESSION_CHECK, (AFB_apiCB)paused , "Media API - paused" },
+ {"pause" , AFB_SESSION_CHECK, (AFB_apiCB)paused , "Media API - pause" },
+ {"upload" , AFB_SESSION_CHECK, (AFB_apiCB)upload , "Media API - upload" },
{"ping" , AFB_SESSION_NONE, (AFB_apiCB)ping , "Media API - ping" },
{NULL}
};
diff --git a/plugins/media/media-rygel.c b/plugins/media/media-rygel.c
index 3ad008ef..539b6189 100644
--- a/plugins/media/media-rygel.c
+++ b/plugins/media/media-rygel.c
@@ -65,6 +65,7 @@ PUBLIC unsigned char _rygel_init (mediaCtxHandleT *ctx) {
dev_ctx[client_count]->av_transport = NULL;
dev_ctx[client_count]->state = STOP;
dev_ctx[client_count]->target_state = STOP;
+ dev_ctx[client_count]->transfer_started = 0;
client_count++;
@@ -96,27 +97,28 @@ PUBLIC char* _rygel_list (mediaCtxHandleT *ctx) {
return NULL;
raw = _rygel_list_raw (dev_ctx_c, NULL);
+ if (!raw)
+ return NULL;
- if (raw) {
- start = strstr (raw, "<dc:title>");
- if (!start) return NULL;
-
- result = strdup("");
+ start = strstr (raw, "<dc:title>");
+ if (!start)
+ return NULL;
- while (start) {
- start = strstr (start, "<dc:title>");
- if (!start) break;
- end = strstr (start, "</dc:title>");
- start += 10; length = end - start;
+ result = strdup("");
+ while (start) {
+ start = strstr (start, "<dc:title>");
+ if (!start) break;
+ end = strstr (start, "</dc:title>");
+ start += 10;
+ length = end - start;
- title = (char*) malloc (length+1);
- strncpy (title, start, length);
- title[length] = '\0';
+ title = (char*) malloc (length+1);
+ strncpy (title, start, length);
+ title[length] = '\0';
- asprintf (&result, "%s%02d:%s::", result, i, title);
+ asprintf (&result, "%s%02d:%s::", result, i, title);
- free (title); i++;
- }
+ free (title); i++;
}
return result;
@@ -140,6 +142,24 @@ PUBLIC unsigned char _rygel_choose (mediaCtxHandleT *ctx, unsigned int index) {
return 1;
}
+PUBLIC unsigned char _rygel_upload (mediaCtxHandleT *ctx, char *path) {
+
+ dev_ctx_T *dev_ctx_c = (dev_ctx_T*)ctx->media_server;
+ char *raw, *upload_id;
+
+ if (!dev_ctx_c)
+ return 0;
+
+ raw = _rygel_list_raw (dev_ctx_c, NULL);
+ if (!raw)
+ return 0;
+
+ /* for now, we always use the same upload container id */
+ upload_id = _rygel_find_upload_id (dev_ctx_c, raw);
+
+ return _rygel_start_uploading (dev_ctx_c, path, upload_id);
+}
+
PUBLIC unsigned char _rygel_do (mediaCtxHandleT *ctx, State state) {
dev_ctx_T *dev_ctx_c = (dev_ctx_T*)ctx->media_server;
@@ -170,7 +190,6 @@ STATIC char* _rygel_list_raw (dev_ctx_T* dev_ctx_c, unsigned int *count) {
dev_ctx_c->content_res = NULL;
dev_ctx_c->content_num = 0;
-
content_dir_proxy = GUPNP_SERVICE_PROXY (dev_ctx_c->content_dir);
gupnp_service_proxy_begin_action (content_dir_proxy, "Browse", _rygel_content_cb, dev_ctx_c,
@@ -197,6 +216,21 @@ STATIC char* _rygel_list_raw (dev_ctx_T* dev_ctx_c, unsigned int *count) {
return dev_ctx_c->content_res;
}
+STATIC char* _rygel_find_upload_id (dev_ctx_T* dev_ctx_c, char *raw) {
+
+ char *found;
+ char id[33];
+
+ found = strstr (raw, "parentID=\"");
+ found += 10;
+
+ /* IDs are 32-bit strings */
+ strncpy (id, found, 32);
+ id[32] = '\0';
+
+ return strdup (id);
+}
+
STATIC char* _rygel_find_id_for_index (dev_ctx_T* dev_ctx_c, char *raw, unsigned int index) {
char *found = raw;
@@ -208,13 +242,13 @@ STATIC char* _rygel_find_id_for_index (dev_ctx_T* dev_ctx_c, char *raw, unsigned
found += 9;
if (i == index) {
- /* IDs are 32-bit numbers */
+ /* IDs are 32-bit strings */
strncpy (id, found, 32);
id[32] = '\0';
}
}
- return strdup(id);
+ return strdup (id);
}
STATIC char* _rygel_find_metadata_for_id (dev_ctx_T* dev_ctx_c, char *id) {
@@ -277,6 +311,61 @@ STATIC char* _rygel_find_uri_for_metadata (dev_ctx_T* dev_ctx_c, char *metadata)
return uri;
}
+STATIC unsigned char _rygel_start_uploading (dev_ctx_T* dev_ctx_c, char *path, char *upload_id) {
+
+ GUPnPServiceProxy *content_dir_proxy;
+ GUPnPDIDLLiteWriter *didl_writer;
+ GUPnPDIDLLiteObject *didl_object;
+ char *didl, *content_type, *mime_type, *upnp_class;
+ struct timeval tv_start, tv_now;
+
+ didl_writer = gupnp_didl_lite_writer_new (NULL);
+ didl_object = GUPNP_DIDL_LITE_OBJECT (gupnp_didl_lite_writer_add_item (didl_writer));
+
+ /* create the metadata for the file */
+ gupnp_didl_lite_object_set_parent_id (didl_object, upload_id);
+ gupnp_didl_lite_object_set_id (didl_object, "");
+ gupnp_didl_lite_object_set_restricted (didl_object, FALSE);
+ gupnp_didl_lite_object_set_title (didl_object, g_path_get_basename (path));
+ /* deduce the UPnP class from the MIME type ("audio/ogg" e.g.) */
+ content_type = g_content_type_guess (path, NULL, 0, NULL);
+ mime_type = g_content_type_get_mime_type (content_type);
+ if (strstr (mime_type, "audio/"))
+ upnp_class = strdup ("object.item.audioItem.musicTrack");
+ else if (strstr (mime_type, "video/"))
+ upnp_class = strdup ("object.item.videoItem");
+ else if (strstr (mime_type, "image/"))
+ upnp_class = strdup ("object.item.imageItem");
+ else
+ upnp_class = strdup ("object.item");
+ gupnp_didl_lite_object_set_upnp_class (didl_object, upnp_class);
+ didl = gupnp_didl_lite_writer_get_string (didl_writer);
+
+ dev_ctx_c->transfer_path = path;
+ dev_ctx_c->transfer_started = 0;
+ content_dir_proxy = GUPNP_SERVICE_PROXY (dev_ctx_c->content_dir);
+
+ gupnp_service_proxy_begin_action (content_dir_proxy, "CreateObject", _rygel_upload_cb, dev_ctx_c,
+ "ContainerID", G_TYPE_STRING, upload_id,
+ "Elements", G_TYPE_STRING, didl,
+ NULL);
+
+ gettimeofday (&tv_start, NULL);
+ gettimeofday (&tv_now, NULL);
+ while (tv_now.tv_sec - tv_start.tv_sec <= 5) {
+
+ g_main_context_iteration (dev_ctx_c->loop, FALSE);
+
+ if (dev_ctx_c->transfer_started)
+ break;
+ gettimeofday (&tv_now, NULL);
+ }
+ if (!dev_ctx_c->transfer_started)
+ return 0;
+
+ return 1;
+}
+
STATIC unsigned char _rygel_start_doing (dev_ctx_T* dev_ctx_c, char *uri, char *metadata, State state) {
GUPnPServiceProxy *av_transport_proxy;
@@ -287,14 +376,13 @@ STATIC unsigned char _rygel_start_doing (dev_ctx_T* dev_ctx_c, char *uri, char *
return 0;
}
dev_ctx_c->target_state = state;
-
av_transport_proxy = GUPNP_SERVICE_PROXY (dev_ctx_c->av_transport);
gupnp_service_proxy_begin_action (av_transport_proxy, "SetAVTransportURI", _rygel_select_cb, dev_ctx_c,
"InstanceID", G_TYPE_UINT, 0,
"CurrentURI", G_TYPE_STRING, uri,
"CurrentURIMetaData", G_TYPE_STRING, metadata,
- NULL);
+ NULL);
gettimeofday (&tv_start, NULL);
gettimeofday (&tv_now, NULL);
@@ -449,7 +537,7 @@ STATIC void _rygel_metadata_cb (GUPnPServiceProxy *content_dir, GUPnPServiceProx
}
STATIC void _rygel_select_cb (GUPnPServiceProxy *av_transport, GUPnPServiceProxyAction *action,
- gpointer data)
+ gpointer data)
{
dev_ctx_T *dev_ctx_c = (dev_ctx_T*)data;
@@ -494,14 +582,84 @@ STATIC void _rygel_select_cb (GUPnPServiceProxy *av_transport, GUPnPServiceProxy
}
}
+STATIC void _rygel_upload_cb (GUPnPServiceProxy *content_dir, GUPnPServiceProxyAction *action,
+ gpointer data)
+{
+ dev_ctx_T *dev_ctx_c = (dev_ctx_T*)data;
+ GUPnPServiceProxy *content_dir_proxy;
+ GError *error;
+ char *result, *start, *end, *dst_uri, *src_uri;
+ int length;
+ struct timeval tv_start, tv_now;
+
+ content_dir_proxy = GUPNP_SERVICE_PROXY (content_dir);
+
+ if (!gupnp_service_proxy_end_action (content_dir, action, &error,
+ "Result", G_TYPE_STRING, &result,
+ NULL))
+ return;
+
+ start = strstr (result, "<res importUri=\"");
+ if (!start)
+ return;
+
+ start += 16;
+ end = strstr (start, "\"");
+ length = end - start;
+
+ dst_uri = (char*) malloc(length+1);
+ strncpy (dst_uri, start, length);
+ dst_uri[length] = '\0';
+
+ asprintf (&src_uri, "http://%s:%u%s", gupnp_context_get_host_ip (dev_ctx_c->context),
+ gupnp_context_get_port (dev_ctx_c->context),
+ dev_ctx_c->transfer_path);
+
+ /* host the file */
+ gupnp_context_host_path (dev_ctx_c->context, dev_ctx_c->transfer_path,
+ dev_ctx_c->transfer_path);
+
+ gupnp_service_proxy_begin_action (content_dir_proxy, "ImportResource", _rygel_transfer_cb, dev_ctx_c,
+ "SourceURI", G_TYPE_STRING, src_uri,
+ "DestinationURI", G_TYPE_STRING, dst_uri,
+ NULL);
+
+ gettimeofday (&tv_start, NULL);
+ gettimeofday (&tv_now, NULL);
+ while (tv_now.tv_sec - tv_start.tv_sec <= 5) {
+
+ g_main_context_iteration (dev_ctx_c->loop, FALSE);
+
+ if (dev_ctx_c->transfer_started)
+ break;
+ gettimeofday (&tv_now, NULL);
+ }
+}
+
+STATIC void _rygel_transfer_cb (GUPnPServiceProxy *content_dir, GUPnPServiceProxyAction *action,
+ gpointer data)
+{
+ dev_ctx_T *dev_ctx_c = (dev_ctx_T*)data;
+ GError *error;
+ guint transfer_id;
+
+ if (!gupnp_service_proxy_end_action (content_dir, action, &error,
+ "TransferID", G_TYPE_UINT, &transfer_id,
+ NULL))
+ return;
+
+ dev_ctx_c->transfer_started = 1;
+}
+
STATIC void _rygel_do_cb (GUPnPServiceProxy *av_transport, GUPnPServiceProxyAction *action,
gpointer data)
{
dev_ctx_T *dev_ctx_c = (dev_ctx_T*)data;
GError *error;
- gupnp_service_proxy_end_action (av_transport, action, &error,
- NULL);
+ if (!gupnp_service_proxy_end_action (av_transport, action, &error,
+ NULL))
+ return;
dev_ctx_c->state = dev_ctx_c->target_state;
}
diff --git a/plugins/media/media-rygel.h b/plugins/media/media-rygel.h
index da028c13..c70ff5c3 100644
--- a/plugins/media/media-rygel.h
+++ b/plugins/media/media-rygel.h
@@ -23,6 +23,7 @@
#include <sys/time.h>
#include <libgupnp/gupnp-control-point.h>
+#include <libgupnp-av/gupnp-av.h>
#include "local-def.h"
@@ -44,12 +45,16 @@ struct dev_ctx {
int content_num;
State state;
State target_state;
+ char *transfer_path;
+ unsigned char transfer_started;
};
STATIC char* _rygel_list_raw (dev_ctx_T *, unsigned int *);
+STATIC char* _rygel_find_upload_id (dev_ctx_T *, char *);
STATIC char* _rygel_find_id_for_index (dev_ctx_T *, char *, unsigned int);
STATIC char* _rygel_find_metadata_for_id (dev_ctx_T *, char *);
STATIC char* _rygel_find_uri_for_metadata (dev_ctx_T *, char *);
+STATIC unsigned char _rygel_start_uploading (dev_ctx_T *, char *, char *);
STATIC unsigned char _rygel_start_doing (dev_ctx_T *, char *, char *, State);
STATIC unsigned char _rygel_find_av_transport (dev_ctx_T *);
STATIC void _rygel_device_cb (GUPnPControlPoint *, GUPnPDeviceProxy *, gpointer);
@@ -57,6 +62,8 @@ STATIC void _rygel_av_transport_cb (GUPnPControlPoint *, GUPnPDeviceProxy *, gpo
STATIC void _rygel_content_cb (GUPnPServiceProxy *, GUPnPServiceProxyAction *, gpointer);
STATIC void _rygel_metadata_cb (GUPnPServiceProxy *, GUPnPServiceProxyAction *, gpointer);
STATIC void _rygel_select_cb (GUPnPServiceProxy *, GUPnPServiceProxyAction *, gpointer);
+STATIC void _rygel_upload_cb (GUPnPServiceProxy *, GUPnPServiceProxyAction *, gpointer);
+STATIC void _rygel_transfer_cb (GUPnPServiceProxy *, GUPnPServiceProxyAction *, gpointer);
STATIC void _rygel_do_cb (GUPnPServiceProxy *, GUPnPServiceProxyAction *, gpointer);
static unsigned int client_count = 0;