Fix Media Plugin refresh, add seek API
authorManuel Bachmann <manuel.bachmann@iot.bzh>
Mon, 25 Jan 2016 12:05:45 +0000 (13:05 +0100)
committerManuel Bachmann <manuel.bachmann@iot.bzh>
Mon, 25 Jan 2016 12:05:45 +0000 (13:05 +0100)
We no longer fail if init() is called multiple times
(typical refresh case).

We now support a "seek?value=<seconds>" API.

Signed-off-by: Manuel Bachmann <manuel.bachmann@iot.bzh>
plugins/media/media-api.c
plugins/media/media-rygel.c
plugins/media/media-rygel.h

index 05237d3..257b1cf 100644 (file)
@@ -42,14 +42,18 @@ STATIC void freeMedia (void *context, void *handle) {
 
 STATIC json_object* init (AFB_request *request) {        /* AFB_SESSION_CHECK */
 
+    mediaCtxHandleT *ctx;
     json_object *jresp;
 
     /* create a private client context */
     if (!request->context)
         request->context = initMediaCtx();
 
+    ctx = (mediaCtxHandleT*)request->context;
+
     /* initialize server connection */
-    _rygel_init (request->context);
+    if (!ctx->media_server)
+      _rygel_init (request->context);
 
     jresp = json_object_new_object();
     json_object_object_add(jresp, "info", json_object_new_string ("Media initialized"));
@@ -72,7 +76,7 @@ STATIC json_object* list (AFB_request *request) {        /* AFB_SESSION_CHECK */
     return jresp;
 }
 
-STATIC json_object* choose (AFB_request *request) {      /* AFB_SESSION_CHECK */
+STATIC json_object* selecting (AFB_request *request) {   /* AFB_SESSION_CHECK */
 
     mediaCtxHandleT *ctx = (mediaCtxHandleT*)request->context;
     const char *value = getQueryValue (request, "value");
@@ -95,7 +99,7 @@ STATIC json_object* choose (AFB_request *request) {      /* AFB_SESSION_CHECK */
     else if (atoi(value) >= 0) {
         index = (unsigned int) atoi(value);
 
-        if (!_rygel_choose (ctx, index))
+        if (!_rygel_select (ctx, index))
           return jsonNewMessage(AFB_FAIL, "Chosen index superior to current media count");
 
         ctx->index = index;
@@ -110,7 +114,7 @@ STATIC json_object* play (AFB_request *request) {        /* AFB_SESSION_CHECK */
 
     mediaCtxHandleT *ctx = (mediaCtxHandleT*)request->context;
 
-    if (!_rygel_do (ctx, PLAY))
+    if (!_rygel_do (ctx, PLAY, NULL))
       return jsonNewMessage(AFB_FAIL, "Could not play chosen media");
 
     return jsonNewMessage(AFB_SUCCESS, "PLaying media");
@@ -120,22 +124,37 @@ STATIC json_object* stop (AFB_request *request) {        /* AFB_SESSION_CHECK */
 
     mediaCtxHandleT *ctx = (mediaCtxHandleT*)request->context;
 
-    if (!_rygel_do (ctx, STOP))
+    if (!_rygel_do (ctx, STOP, NULL))
       return jsonNewMessage(AFB_FAIL, "Could not stop chosen media");
 
     return jsonNewMessage(AFB_SUCCESS, "Stopped media");
 }
 
-STATIC json_object* paused (AFB_request *request) {      /* AFB_SESSION_CHECK */
+STATIC json_object* pausing (AFB_request *request) {     /* AFB_SESSION_CHECK */
 
     mediaCtxHandleT *ctx = (mediaCtxHandleT*)request->context;
 
-    if (!_rygel_do (ctx, PAUSE))
+    if (!_rygel_do (ctx, PAUSE, NULL))
       return jsonNewMessage(AFB_FAIL, "Could not pause chosen media");
 
     return jsonNewMessage(AFB_SUCCESS, "Paused media");
 }
 
+STATIC json_object* seek (AFB_request *request) {        /* AFB_SESSION_CHECK */
+
+    mediaCtxHandleT *ctx = (mediaCtxHandleT*)request->context;
+    const char *value = getQueryValue (request, "value");
+
+    /* no "?value=" parameter : return error */
+    if (!value)
+      return jsonNewMessage(AFB_FAIL, "You must provide a time");
+
+    if (!_rygel_do (ctx, SEEK, value))
+      return jsonNewMessage(AFB_FAIL, "Could not seek chosen media");
+
+    return jsonNewMessage(AFB_SUCCESS, "Seeked media");
+}
+
 STATIC json_object* upload (AFB_request *request) {      /* AFB_SESSION_CHECK */
 
     mediaCtxHandleT *ctx = (mediaCtxHandleT*)request->context;
@@ -165,10 +184,11 @@ STATIC json_object* ping (AFB_request *request) {         /* AFB_SESSION_NONE */
 STATIC AFB_restapi pluginApis[]= {
   {"init"   , AFB_SESSION_CHECK,  (AFB_apiCB)init       , "Media API - init"   },
   {"list"   , AFB_SESSION_CHECK,  (AFB_apiCB)list       , "Media API - list"   },
-  {"choose" , AFB_SESSION_CHECK,  (AFB_apiCB)choose     , "Media API - choose" },
+  {"select" , AFB_SESSION_CHECK,  (AFB_apiCB)selecting  , "Media API - select" },
   {"play"   , AFB_SESSION_CHECK,  (AFB_apiCB)play       , "Media API - play"   },
   {"stop"   , AFB_SESSION_CHECK,  (AFB_apiCB)stop       , "Media API - stop"   },
-  {"pause"  , AFB_SESSION_CHECK,  (AFB_apiCB)paused     , "Media API - pause"  },
+  {"pause"  , AFB_SESSION_CHECK,  (AFB_apiCB)pausing    , "Media API - pause"  },
+  {"seek"   , AFB_SESSION_CHECK,  (AFB_apiCB)seek       , "Media API - seek"   },
   {"upload" , AFB_SESSION_CHECK,  (AFB_apiCB)upload     , "Media API - upload" },
   {"ping"   , AFB_SESSION_NONE,   (AFB_apiCB)ping       , "Media API - ping"   },
   {NULL}
index 539b618..9e94a9e 100644 (file)
@@ -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]->action_args = NULL;
     dev_ctx[client_count]->transfer_started = 0;
 
     client_count++;
@@ -124,7 +125,7 @@ PUBLIC char* _rygel_list (mediaCtxHandleT *ctx) {
     return result;
 }
 
-PUBLIC unsigned char _rygel_choose (mediaCtxHandleT *ctx, unsigned int index) {
+PUBLIC unsigned char _rygel_select (mediaCtxHandleT *ctx, unsigned int index) {
 
     dev_ctx_T *dev_ctx_c = (dev_ctx_T*)ctx->media_server;
     unsigned int count;
@@ -160,7 +161,7 @@ PUBLIC unsigned char _rygel_upload (mediaCtxHandleT *ctx, char *path) {
     return _rygel_start_uploading (dev_ctx_c, path, upload_id);
 }
 
-PUBLIC unsigned char _rygel_do (mediaCtxHandleT *ctx, State state) {
+PUBLIC unsigned char _rygel_do (mediaCtxHandleT *ctx, State state, char *args) {
 
     dev_ctx_T *dev_ctx_c = (dev_ctx_T*)ctx->media_server;
     unsigned int index = ctx->index;
@@ -178,7 +179,7 @@ PUBLIC unsigned char _rygel_do (mediaCtxHandleT *ctx, State state) {
     metadata = _rygel_find_metadata_for_id (dev_ctx_c, id);
          uri = _rygel_find_uri_for_metadata (dev_ctx_c, metadata);
 
-    return _rygel_start_doing (dev_ctx_c, uri, metadata, state);
+    return _rygel_start_doing (dev_ctx_c, uri, metadata, state, args);
 }
 
 /* --- LOCAL HELPER FUNCTIONS --- */
@@ -311,6 +312,22 @@ STATIC char* _rygel_find_uri_for_metadata (dev_ctx_T* dev_ctx_c, char *metadata)
     return uri;
 }
 
+STATIC char * _rygel_time_for_string (char *string) {
+
+    int total_seconds;
+    unsigned int hours, minutes, seconds;
+    char *time;
+
+    total_seconds = atoi (string);
+    hours = total_seconds / 3600;
+    minutes = (total_seconds / 60) - (hours * 60);
+    seconds = total_seconds - (hours * 3600) - (minutes * 60);
+
+    asprintf (&time, "%u:%02u:%02u", hours, minutes, seconds);
+
+    return time;
+}
+
 STATIC unsigned char _rygel_start_uploading (dev_ctx_T* dev_ctx_c, char *path, char *upload_id) {
 
     GUPnPServiceProxy *content_dir_proxy;
@@ -366,7 +383,7 @@ STATIC unsigned char _rygel_start_uploading (dev_ctx_T* dev_ctx_c, char *path, c
     return 1;
 }
 
-STATIC unsigned char _rygel_start_doing (dev_ctx_T* dev_ctx_c, char *uri, char *metadata, State state) {
+STATIC unsigned char _rygel_start_doing (dev_ctx_T* dev_ctx_c, char *uri, char *metadata, State state, char *args) {
 
     GUPnPServiceProxy *av_transport_proxy;
     struct timeval tv_start, tv_now;
@@ -376,6 +393,7 @@ STATIC unsigned char _rygel_start_doing (dev_ctx_T* dev_ctx_c, char *uri, char *
          return 0;
     }
     dev_ctx_c->target_state = state;
+    dev_ctx_c->action_args = args;
     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,
@@ -543,6 +561,7 @@ STATIC void _rygel_select_cb (GUPnPServiceProxy *av_transport, GUPnPServiceProxy
     dev_ctx_T *dev_ctx_c = (dev_ctx_T*)data;
     GUPnPServiceProxy *av_transport_proxy;
     GError *error;
+    char *time;
     struct timeval tv_start, tv_now;
 
     av_transport_proxy = GUPNP_SERVICE_PROXY (av_transport);
@@ -566,6 +585,13 @@ STATIC void _rygel_select_cb (GUPnPServiceProxy *av_transport, GUPnPServiceProxy
                                            "InstanceID", G_TYPE_UINT, 0,
                                             NULL);
           break;
+       case SEEK:
+          time = _rygel_time_for_string (dev_ctx_c->action_args);
+          gupnp_service_proxy_begin_action (av_transport_proxy, "Seek", _rygel_do_cb, dev_ctx_c,
+                                           "InstanceID", G_TYPE_UINT, 0,
+                                           "Unit", G_TYPE_STRING, "ABS_TIME",
+                                           "Target", G_TYPE_STRING, time,
+                                            NULL);
        default:
         break;
     }
index c70ff5c..278881c 100644 (file)
@@ -32,7 +32,7 @@
 #define URN_CONTENT_DIR    "urn:schemas-upnp-org:service:ContentDirectory"
 #define URN_AV_TRANSPORT   "urn:schemas-upnp-org:service:AVTransport"
 
-typedef enum { PLAY, PAUSE, STOP } State;
+typedef enum { PLAY, PAUSE, STOP, SEEK } State;
 typedef struct dev_ctx dev_ctx_T;
 
 struct dev_ctx {
@@ -45,6 +45,7 @@ struct dev_ctx {
     int content_num;
     State state;
     State target_state;
+    char *action_args;
     char *transfer_path;
     unsigned char transfer_started;
 };
@@ -55,7 +56,7 @@ 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_start_doing (dev_ctx_T *, char *, char *, State, char *);
 STATIC unsigned char _rygel_find_av_transport (dev_ctx_T *);
 STATIC void _rygel_device_cb (GUPnPControlPoint *, GUPnPDeviceProxy *, gpointer);
 STATIC void _rygel_av_transport_cb (GUPnPControlPoint *, GUPnPDeviceProxy *, gpointer);