[gnome-network-displays/cc-tmp: 26/80] cc: json functions




commit 4c5bf6e5c01e08281953f2445fbb3f3106ad9176
Author: Anupam Kumar <kyteinsky gmail com>
Date:   Wed Aug 31 13:16:06 2022 +0530

    cc: json functions

 src/cc/cc-comm.c        |  15 +--
 src/cc/cc-common.h      |  53 ++++++++
 src/cc/cc-ctrl.c        | 342 ++++++++++++++++++++++++++++++++++++++----------
 src/cc/cc-ctrl.h        |   3 +-
 src/cc/cc-json-helper.c | 342 +++++++++++++++++++++++++-----------------------
 src/cc/cc-json-helper.h |  11 +-
 src/nd-cc-sink.c        |   7 +-
 7 files changed, 524 insertions(+), 249 deletions(-)
---
diff --git a/src/cc/cc-comm.c b/src/cc/cc-comm.c
index 65a5b3e..0e3b9e2 100644
--- a/src/cc/cc-comm.c
+++ b/src/cc/cc-comm.c
@@ -144,15 +144,6 @@ cc_comm_parse_received_data (CcComm *comm, uint8_t * input_buffer, gssize input_
   // go for another round while we process this one
   cc_comm_listen (comm);
 
-  CcReceivedMessageType type = cc_json_helper_get_message_type (message, NULL);
-
-  if (type == CC_RWAIT_TYPE_PING || type == CC_RWAIT_TYPE_PONG || type == -1)
-    return;
-
-  g_debug ("CcComm: Received message:");
-  cc_json_helper_dump_message (message);
-
-  // actual message handling
   comm->closure->message_received_cb (comm->closure, message);
 
   cast__channel__cast_message__free_unpacked (message, NULL);
@@ -595,10 +586,12 @@ cc_comm_send_request (CcComm       *comm,
   memcpy (sock_buffer, &packed_size_be, 4);
   cast__channel__cast_message__pack (&message, 4 + sock_buffer);
 
-  if (message_type != CC_MESSAGE_TYPE_PING && message_type != CC_MESSAGE_TYPE_PONG)
+  if (message_type != CC_MESSAGE_TYPE_PING
+      && message_type != CC_MESSAGE_TYPE_PONG
+      && message_type != CC_MESSAGE_TYPE_AUTH)
     {
       g_debug ("CcComm: Sending message:");
-      cc_json_helper_dump_message (&message);
+      cc_json_helper_dump_message (&message, FALSE);
     }
 
   return cc_comm_tls_send (comm,
diff --git a/src/cc/cc-common.h b/src/cc/cc-common.h
index 2a68fe9..f49cabc 100644
--- a/src/cc/cc-common.h
+++ b/src/cc/cc-common.h
@@ -61,4 +61,57 @@ typedef enum {
 
 typedef CcReceivedMessageType CcWaitingFor;
 
+typedef struct _Stream {
+  gint index;
+
+  // Default channel count is 1, e.g. for video.
+  gint channels;
+  gint rtp_payload_type;
+  gint ssrc;
+  gint target_delay;
+
+  // AES Key and IV mask format is very strict: a 32 digit hex string that
+  // must be converted to a 16 digit byte array.
+  // uint8_t aes_key[16];
+  // uint8_t aes_iv_mask[16];
+  gchar *aes_iv_mask;
+  gchar *aes_key;
+  gboolean receiver_rtcp_event_log;
+  gchar *receiver_rtcp_dscp;
+  // gint rtp_timebase;
+  gchar *rtp_timebase;
+
+  // The codec parameter field honors the format laid out in RFC 6381:
+  // https://datatracker.ietf.org/doc/html/rfc6381.
+  gchar *codec_parameter;
+} Stream;
+
+typedef struct _AudioStream {
+  Stream stream;
+  gchar *codec;
+  gint bit_rate;
+  gint sample_rate;
+  gchar *profile;
+} AudioStream;
+
+typedef struct _VideoStream {
+  Stream stream;
+  gchar *codec;
+  gchar *max_frame_rate;
+  gint max_bit_rate;
+  gchar *protection;
+  gchar *profile;
+  gchar *level;
+  gint **resolutions;
+  gchar *error_recovery_mode;
+} VideoStream;
+
+typedef struct _Offer {
+  gchar *cast_mode;
+  guint seq_num;
+  gboolean receiver_get_status;
+  AudioStream audio_stream;
+  VideoStream video_stream;
+} Offer;
+
 G_END_DECLS
diff --git a/src/cc/cc-ctrl.c b/src/cc/cc-ctrl.c
index 229c1b7..7b14193 100644
--- a/src/cc/cc-ctrl.c
+++ b/src/cc/cc-ctrl.c
@@ -19,6 +19,26 @@
 #include "cc-ctrl.h"
 #include "cc-comm.h"
 
+// WAITING FOR
+
+static void
+cc_ctrl_set_waiting_for (CcCtrl *ctrl, CcWaitingFor waiting_for)
+{
+  ctrl->waiting_for |= waiting_for;
+}
+
+static void
+cc_ctrl_unset_waiting_for (CcCtrl *ctrl, CcWaitingFor waiting_for)
+{
+  ctrl->waiting_for &= ~waiting_for;
+}
+
+static gboolean
+cc_ctrl_is_waiting_for (CcCtrl *ctrl, CcWaitingFor waiting_for)
+{
+  return (ctrl->waiting_for & waiting_for) > CC_RWAIT_TYPE_NONE;
+}
+
 // SEND HELPER FUNCTIONS
 
 static gboolean
@@ -38,10 +58,30 @@ cc_ctrl_send_connect (CcCtrl *ctrl, gchar *destination_id, GError **error)
 {
   g_debug ("CcCtrl: Sending CONNECT");
 
+  JsonNode *senderInfo;
+  JsonNode *origin;
+  cc_json_helper_build_node (&origin, NULL);
+  cc_json_helper_build_node (&senderInfo,
+    "sdkType", CC_JSON_TYPE_INT, 2,
+    "version", CC_JSON_TYPE_STRING, "X11; Linux x86_64",
+    "browserVersion", CC_JSON_TYPE_STRING, "X11; Linux x86_64",
+    "platform", CC_JSON_TYPE_INT, 6,
+    "connectionType", CC_JSON_TYPE_INT, 1,
+    NULL);
+
+  gchar *json;
+  cc_json_helper_build_string (&json,
+    "type", CC_JSON_TYPE_STRING, "CONNECT",
+    "userAgent", CC_JSON_TYPE_STRING, "GND/0.90.5  (X11; Linux x86_64)",
+    "connType", CC_JSON_TYPE_INT, 0,
+    "origin", CC_JSON_TYPE_OBJECT, origin,
+    "senderInfo", CC_JSON_TYPE_OBJECT, senderInfo,
+    NULL);
+
   return cc_comm_send_request (&ctrl->comm,
                                destination_id,
                                CC_MESSAGE_TYPE_CONNECT,
-                               "{ \"type\": \"CONNECT\", \"userAgent\": \"GND/0.90.5  (X11; Linux x86_64)\", 
\"connType\": 0, \"origin\": {}, \"senderInfo\": { \"sdkType\": 2, \"version\": \"X11; Linux x86_64\", 
\"browserVersion\": \"X11; Linux x86_64\", \"platform\": 6, \"connectionType\": 1 } }",
+                               json,
                                error);
 }
 
@@ -50,10 +90,15 @@ cc_ctrl_send_disconnect (CcCtrl *ctrl, gchar *destination_id, GError **error)
 {
   g_debug ("CcCtrl: Sending CLOSE");
 
+  gchar *json;
+  cc_json_helper_build_string (&json,
+    "type", CC_JSON_TYPE_STRING, "CLOSE",
+    NULL);
+
   return cc_comm_send_request (&ctrl->comm,
                                destination_id,
                                CC_MESSAGE_TYPE_DISCONNECT,
-                               "{ \"type\": \"CLOSE\" }",
+                               json,
                                error);
 }
 
@@ -62,13 +107,21 @@ cc_ctrl_send_get_status (CcCtrl *ctrl, gchar *destination_id, GError **error)
 {
   g_debug ("CcCtrl: Sending GET_STATUS");
 
-  g_autoptr (GString) json = g_string_new ("{ \"type\": \"GET_STATUS\", ");
-  g_string_append_printf (json, "\"requestId\": %d }", ctrl->request_id++);
-  return cc_comm_send_request (&ctrl->comm,
+  gchar *json;
+  cc_json_helper_build_string (&json,
+    "type", CC_JSON_TYPE_STRING, "GET_STATUS",
+    "requestId", CC_JSON_TYPE_INT, ctrl->request_id++,
+    NULL);
+
+  gboolean send_ok = cc_comm_send_request (&ctrl->comm,
                                destination_id,
                                CC_MESSAGE_TYPE_RECEIVER,
-                               json->str,
+                               json,
                                error);
+  if (send_ok)
+    cc_ctrl_set_waiting_for (ctrl, CC_RWAIT_TYPE_RECEIVER_STATUS);
+
+  return send_ok;
 }
 
 static gboolean
@@ -76,13 +129,25 @@ cc_ctrl_send_get_app_availability (CcCtrl *ctrl, gchar *destination_id, gchar *a
 {
   g_debug ("CcCtrl: Sending GET_APP_AVAILABILITY");
 
-  g_autoptr (GString) json = g_string_new ("{ \"type\": \"GET_APP_AVAILABILITY\", ");
-  g_string_append_printf (json, "\"appId\": [\"%s\"], \"requestId\": %d }", appId, ctrl->request_id++);
-  return cc_comm_send_request (&ctrl->comm,
+  g_autoptr (GArray) appIds = g_array_new (FALSE, FALSE, sizeof (gchar *));
+  g_array_append_val (appIds, CC_MIRRORING_APP_ID);
+
+  gchar *json;
+  cc_json_helper_build_string (&json,
+    "type", CC_JSON_TYPE_STRING, "GET_APP_AVAILABILITY",
+    "appId", CC_JSON_TYPE_ARRAY_STRING, appIds,
+    "requestId", CC_JSON_TYPE_INT, ctrl->request_id++,
+    NULL);
+
+  gboolean send_ok = cc_comm_send_request (&ctrl->comm,
                                destination_id,
                                CC_MESSAGE_TYPE_RECEIVER,
-                               json->str,
+                               json,
                                error);
+
+  if (send_ok)
+    cc_ctrl_set_waiting_for (ctrl, CC_RWAIT_TYPE_GET_APP_AVAILABILITY);
+  return send_ok;
 }
 
 static gboolean
@@ -90,13 +155,23 @@ cc_ctrl_send_launch_app (CcCtrl *ctrl, gchar *destination_id, gchar *appId, GErr
 {
   g_debug ("CcCtrl: Sending LAUNCH");
 
-  g_autoptr (GString) json = g_string_new ("{ \"type\": \"LAUNCH\", \"language\": \"en-US\", ");
-  g_string_append_printf (json, "\"appId\": \"%s\", \"requestId\": %d }", appId, ctrl->request_id++);
-  return cc_comm_send_request (&ctrl->comm,
+  gchar *json;
+  cc_json_helper_build_string (&json,
+    "type", CC_JSON_TYPE_STRING, "LAUNCH",
+    "launguage", CC_JSON_TYPE_STRING, "en-US",
+    "appId", CC_JSON_TYPE_STRING, appId,
+    "requestId", CC_JSON_TYPE_INT, ctrl->request_id++,
+    NULL);
+
+  gboolean send_ok = cc_comm_send_request (&ctrl->comm,
                                destination_id,
                                CC_MESSAGE_TYPE_RECEIVER,
-                               json->str,
+                               json,
                                error);
+
+  if (send_ok)
+    cc_ctrl_set_waiting_for (ctrl, CC_RWAIT_TYPE_RECEIVER_STATUS);
+  return send_ok;
 }
 
 static gboolean
@@ -104,46 +179,127 @@ cc_ctrl_send_close_app (CcCtrl *ctrl, gchar *sessionId, GError **error)
 {
   g_debug ("CcCtrl: Sending STOP");
 
-  g_autoptr (GString) json = g_string_new ("{ \"type\": \"STOP\", ");
-  g_string_append_printf (json, "\"sessionId\": \"%s\", \"requestId\": %d }", sessionId, ctrl->request_id++);
-  return cc_comm_send_request (&ctrl->comm,
+  gchar *json;
+  cc_json_helper_build_string (&json,
+    "type", CC_JSON_TYPE_STRING, "STOP",
+    "sessionId", CC_JSON_TYPE_STRING, sessionId,
+    "requestId", CC_JSON_TYPE_INT, ctrl->request_id++,
+    NULL);
+
+  gboolean send_ok = cc_comm_send_request (&ctrl->comm,
                                sessionId,
                                CC_MESSAGE_TYPE_RECEIVER,
-                               json->str,
+                               json,
                                error);
-}
-
-static gboolean
-cc_ctrl_send_offer (CcCtrl *ctrl, gchar *destination_id, GError **error)
-{
-  g_debug ("CcCtrl: Sending OFFER");
 
-  /* look into [ adaptive_playout_delay, rtpExtensions, rtpPayloadType, rtpProfile, aes stuff, ssrc 
increment in received msg ] */
-  return cc_comm_send_request (&ctrl->comm,
-                               destination_id,
-                               CC_MESSAGE_TYPE_WEBRTC,
-                               "{ \"offer\": { \"castMode\": \"mirroring\", \"receiverGetStatus\": true, 
\"supportedStreams\": [ { \"aesIvMask\": \"1D20EA1C710E5598ECF80FB26ABC57B0\", \"aesKey\": 
\"BB0CAE24F76EA1CAC9A383CFB1CFD54E\", \"bitRate\": 102000, \"channels\": 2, \"codecName\": \"aac\", 
\"index\": 0, \"receiverRtcpEventLog\": true, \"rtpExtensions\": \"adaptive_playout_delay\", 
\"rtpPayloadType\": 127, \"rtpProfile\": \"cast\", \"sampleRate\": 48000, \"ssrc\": 144842, \"targetDelay\": 
400, \"timeBase\": \"1/48000\", \"type\": \"audio_source\" }, { \"aesIvMask\": 
\"1D20EA1C710E5598ECF80FB26ABC57B0\", \"aesKey\": \"BB0CAE24F76EA1CAC9A383CFB1CFD54E\", \"codecName\": 
\"h264\", \"index\": 1, \"maxBitRate\": 5000000, \"maxFrameRate\": \"30000/1000\", \"receiverRtcpEventLog\": 
true, \"renderMode\": \"video\", \"resolutions\": [{ \"height\": 1080, \"width\": 1920 }], \"rtpExtensions\": 
\"adaptive_playout_delay\", \"rtpPayloadType\": 96, \"rtpProfile\": \"cast\", \"ssrc\": 545579, \"
 targetDelay\": 400, \"timeBase\": \"1/90000\", \"type\": \"video_source\" } ] }, \"seqNum\": 730137397, 
\"type\": \"OFFER\" }",
-                               error);
+  if (send_ok)
+    cc_ctrl_set_waiting_for (ctrl, CC_RWAIT_TYPE_RECEIVER_STATUS);
+  return send_ok;
 }
 
-// WAITING FOR
+// OFFER MESSAGE
 
-static void
-cc_ctrl_set_waiting_for (CcCtrl *ctrl, CcWaitingFor waiting_for)
+JsonNode *
+build_audio_source (AudioStream *audio_stream)
 {
-  ctrl->waiting_for |= waiting_for;
+  JsonNode *node;
+
+  cc_json_helper_build_node (&node,
+    "aesIvMask", CC_JSON_TYPE_STRING, audio_stream->stream.aes_iv_mask,
+    "aesKey", CC_JSON_TYPE_STRING, audio_stream->stream.aes_key,
+    "bitRate", CC_JSON_TYPE_INT, audio_stream->bit_rate,
+    "codecName", CC_JSON_TYPE_STRING, audio_stream->codec,
+    "index", CC_JSON_TYPE_INT, audio_stream->stream.index,
+    "receiverRtcpEventLog", CC_JSON_TYPE_BOOLEAN, audio_stream->stream.receiver_rtcp_event_log,
+    "rtpExtensions", CC_JSON_TYPE_STRING, "adaptive_playout_delay",
+    "rtpPayloadType", CC_JSON_TYPE_INT, audio_stream->stream.rtp_payload_type,
+    "rtpProfile", CC_JSON_TYPE_STRING, audio_stream->profile,
+    "sampleRate", CC_JSON_TYPE_INT, audio_stream->sample_rate,
+    "ssrc", CC_JSON_TYPE_INT, audio_stream->stream.ssrc,
+    "targetDelay", CC_JSON_TYPE_INT, audio_stream->stream.target_delay,
+    "timeBase", CC_JSON_TYPE_STRING, audio_stream->stream.rtp_timebase,
+    "type", CC_JSON_TYPE_STRING, "audio_source",
+    NULL);
+
+  return g_steal_pointer (&node);
 }
 
-static void
-cc_ctrl_unset_waiting_for (CcCtrl *ctrl, CcWaitingFor waiting_for)
+JsonNode *
+build_video_source (VideoStream *video_stream)
 {
-  ctrl->waiting_for &= ~waiting_for;
+  JsonNode *node;
+  JsonNode *resolution;
+  g_autoptr (GArray) resolutions = g_array_new (FALSE, FALSE, sizeof (JsonNode *));
+
+  cc_json_helper_build_node (&resolution,
+    "height", CC_JSON_TYPE_INT, 1080,
+    "width", CC_JSON_TYPE_INT, 1920,
+    NULL);
+  g_array_append_val (resolutions, resolution);
+
+  cc_json_helper_build_node (&node,
+    "aesIvMask", CC_JSON_TYPE_STRING, video_stream->stream.aes_iv_mask,
+    "aesKey", CC_JSON_TYPE_STRING, video_stream->stream.aes_key,
+    "codecName", CC_JSON_TYPE_STRING, video_stream->codec,
+    "index", CC_JSON_TYPE_INT, video_stream->stream.index,
+    "maxBitRate", CC_JSON_TYPE_INT, video_stream->max_bit_rate,
+    "maxFrameRate", CC_JSON_TYPE_STRING, video_stream->max_frame_rate,
+    "receiverRtcpEventLog", CC_JSON_TYPE_BOOLEAN, video_stream->stream.receiver_rtcp_event_log,
+    "renderMode", CC_JSON_TYPE_STRING, "video",
+    "resolutions", CC_JSON_TYPE_ARRAY_OBJECT, resolutions,
+    "rtpExtensions", CC_JSON_TYPE_STRING, "adaptive_playout_delay",
+    "rtpPayloadType", CC_JSON_TYPE_INT, video_stream->stream.rtp_payload_type,
+    "rtpProfile", CC_JSON_TYPE_STRING, video_stream->profile,
+    "ssrc", CC_JSON_TYPE_INT, video_stream->stream.ssrc,
+    "targetDelay", CC_JSON_TYPE_INT, video_stream->stream.target_delay,
+    "timeBase", CC_JSON_TYPE_STRING, video_stream->stream.rtp_timebase,
+    "type", CC_JSON_TYPE_STRING, "video_source",
+    NULL);
+
+  return g_steal_pointer (&node);
 }
 
 static gboolean
-cc_ctrl_is_waiting_for (CcCtrl *ctrl, CcWaitingFor waiting_for)
+cc_ctrl_send_offer (CcCtrl *ctrl, gchar *destination_id, GError **error)
 {
-  return (ctrl->waiting_for & waiting_for) > CC_RWAIT_TYPE_NONE;
+  g_debug ("CcCtrl: Sending OFFER");
+
+  /* look into [ adaptive_playout_delay, rtpExtensions, rtpPayloadType, rtpProfile, aes stuff, ssrc 
increment in received msg ] */
+
+  Offer *offer = ctrl->closure->get_offer_message (ctrl->closure);
+  JsonNode *audio_source_node = build_audio_source (&offer->audio_stream);
+  JsonNode *video_source_node = build_video_source (&offer->video_stream);
+
+  g_autoptr (GArray) streams = g_array_new (FALSE, FALSE, sizeof (JsonNode *));
+  g_array_append_val (streams, audio_source_node);
+  g_array_append_val (streams, video_source_node);
+
+  JsonNode *offer_key;
+  cc_json_helper_build_node (&offer_key,
+    "castMode", CC_JSON_TYPE_STRING, offer->cast_mode,
+    "receiverGetStatus", CC_JSON_TYPE_BOOLEAN, offer->receiver_get_status,
+    "supportedStreams", CC_JSON_TYPE_ARRAY_OBJECT, streams,
+    NULL);
+
+  JsonNode *root;
+  cc_json_helper_build_node (&root,
+    "offer", CC_JSON_TYPE_OBJECT, offer_key,
+    "seqNum", CC_JSON_TYPE_INT, offer->seq_num,
+    "type", CC_JSON_TYPE_STRING, "OFFER",
+    NULL);
+  
+  gchar *json;
+  cc_json_helper_node_to_string (&json, root);
+
+  gboolean send_ok = cc_comm_send_request (&ctrl->comm,
+                               destination_id,
+                               CC_MESSAGE_TYPE_WEBRTC,
+                               json,
+                               error);
+
+  if (send_ok)
+    cc_ctrl_set_waiting_for (ctrl, CC_RWAIT_TYPE_ANSWER);
+  return send_ok;
 }
 
 // INTERVAL FUNCTIONS
@@ -189,12 +345,12 @@ cc_ctrl_send_ping (CcCtrl *ctrl)
 }
 
 static gboolean
-cc_ctrl_send_gaa_cb (CcCtrl *ctrl)
+cc_ctrl_send_offer_cb (CcCtrl *ctrl)
 {
   g_autoptr (GError) error = NULL;
-  if (!cc_ctrl_send_get_app_availability (ctrl, CC_DEFAULT_RECEIVER_ID, CC_MIRRORING_APP_ID, &error))
-    g_warning ("CcCtrl: Failed to send GET_APP_AVAILABILITY to the mirroring app: %s", error->message);
-  return FALSE;
+  if (!cc_ctrl_send_offer (ctrl, ctrl->session_id, &error))
+    g_warning ("CcCtrl: Failed to send OFFER to the mirroring app: %s", error->message);
+  return G_SOURCE_REMOVE;
 }
 
 static void
@@ -206,23 +362,36 @@ cc_ctrl_mirroring_app_init (CcCtrl *ctrl, GError **error)
       return;
     }
 
-  // send get_app_availability message after 2 seconds
-  g_timeout_add_seconds (2, G_SOURCE_FUNC (cc_ctrl_send_gaa_cb), ctrl);
+  // send offer message after 1 second
+  g_timeout_add_seconds (1, G_SOURCE_FUNC (cc_ctrl_send_offer_cb), ctrl);
 }
 
 // HANDLE MESSAGE
 
-// should be status received callback
 static void
 cc_ctrl_handle_get_app_availability (CcCtrl *ctrl, JsonReader *reader)
 {
   g_autoptr (GError) error = NULL;
 
-  // TODO: reader
-  if (!cc_ctrl_send_offer (ctrl, ctrl->session_id, &error))
+  if (json_reader_read_member (reader, "availability"))
     {
-      g_warning ("CcCtrl: Failed to send offer: %s", error->message);
-      return;
+      if (json_reader_read_member (reader, CC_MIRRORING_APP_ID))
+        {
+          const gchar *available = json_reader_get_string_value (reader);
+          if (g_strcmp0 (available, "APP_AVAILABLE"))
+            {
+              // launch the app now
+              if (!cc_ctrl_send_launch_app (ctrl, CC_DEFAULT_RECEIVER_ID, CC_MIRRORING_APP_ID, &error))
+                {
+                  g_warning ("CcCtrl: Failed to launch the app: %s", error->message);
+                  return;
+                }
+            }
+          
+          // since the app is not available, stop attempts
+          g_warning ("CcCtrl: %s app is not available, quiting", CC_MIRRORING_APP_ID);
+          ctrl->closure->end_stream (ctrl->closure);
+        }
     }
 }
 
@@ -231,8 +400,8 @@ static void
 cc_ctrl_handle_receiver_status (CcCtrl *ctrl, JsonParser *parser)
 {
   // reports all the open apps (the relevant stuff)
-  // if the app is open, it has a sessionId: hijack the session
-  // connect to it, send a stop, and then propose an offer
+  // if the app is open, it has a sessionId: opened by this app or not, it is hijackable
+  // connect to it, and then propose an offer
 
   g_autoptr (GError) error = NULL;
        g_autoptr (JsonNode) app_status = NULL;
@@ -260,7 +429,6 @@ cc_ctrl_handle_receiver_status (CcCtrl *ctrl, JsonParser *parser)
           return;
         }
 
-      cc_ctrl_set_waiting_for (ctrl, CC_RWAIT_TYPE_RECEIVER_STATUS);
       ctrl->state = CC_CTRL_STATE_LAUNCH_SENT;
       return;
     }
@@ -284,30 +452,56 @@ cc_ctrl_handle_receiver_status (CcCtrl *ctrl, JsonParser *parser)
               if (g_strcmp0 (appId, CC_MIRRORING_APP_ID) == 0)
                 {
                   // takeover the session, doesn't matter which sender opened it
-                  g_debug ("CcCtrl: Mirroring app is open,");
+                  g_debug ("CcCtrl: Mirroring app is open");
                   ctrl->state = CC_CTRL_STATE_APP_OPEN;
-                  /* is this freed automatically? */
+                  g_clear_pointer (&ctrl->session_id, g_free);
                   ctrl->session_id = g_strdup (sessionId);
 
                   cc_ctrl_mirroring_app_init (ctrl, &error);
-                  cc_ctrl_set_waiting_for (ctrl, CC_RWAIT_TYPE_GET_APP_AVAILABILITY);
-
                   return;
                 }
 
-              if (!cc_ctrl_send_launch_app (ctrl, CC_DEFAULT_RECEIVER_ID, CC_MIRRORING_APP_ID, &error))
+              // some other app is open, check if `CC_MIRRORING_APP_ID` is available
+              if (!cc_ctrl_send_get_app_availability (ctrl, CC_MIRRORING_APP_ID, CC_DEFAULT_RECEIVER_ID, 
&error))
                 {
-                  g_warning ("CcCtrl: Failed to launch the app: %s", error->message);
+                  g_warning ("CcCtrl: Failed to send GET_APP_AVAILABILITY: %s", error->message);
                   return;
                 }
-
-              cc_ctrl_set_waiting_for (ctrl, CC_RWAIT_TYPE_RECEIVER_STATUS);
-              ctrl->state = CC_CTRL_STATE_LAUNCH_SENT;
             }
         }
     }
 }
 
+static void
+cc_ctrl_handle_media_status (CcCtrl *ctrl, Cast__Channel__CastMessage *message, JsonReader *reader)
+{
+  // since answer and media_status are received one after another, we discard this
+  // for the mirroring app
+  // and since this stream is LIVE, we won't need any of it
+  // if (g_strcmp0 (message->source_id, ctrl->session_id))
+  //   return;
+}
+
+static void
+cc_ctrl_handle_close (CcCtrl *ctrl, Cast__Channel__CastMessage *message)
+{
+  g_autoptr (GError) error = NULL;
+
+  if (g_strcmp0 (message->source_id, CC_DEFAULT_RECEIVER_ID) == 0)
+    {
+      g_warning ("CcCtrl: Receiver closed the connection");
+      ctrl->closure->end_stream (ctrl->closure);
+      return;
+    }
+
+  // the app closed
+  g_debug ("CcCtrl: App sent a close message, launching again");
+  if (!cc_ctrl_send_launch_app (ctrl, CC_DEFAULT_RECEIVER_ID, CC_MIRRORING_APP_ID, &error))
+    {
+      g_warning ("CcCtrl: Failed to launch app");
+    }
+}
+
 void
 cc_ctrl_handle_received_msg (CcCommClosure *closure,
                              Cast__Channel__CastMessage *message)
@@ -320,8 +514,8 @@ cc_ctrl_handle_received_msg (CcCommClosure *closure,
   parser = json_parser_new ();
   if (!json_parser_load_from_data (parser, message->payload_utf8, -1, &error))
     {
-      cc_json_helper_dump_message (message);
       g_warning ("CcCtrl: Error parsing received messaage JSON: %s", error->message);
+      cc_json_helper_dump_message (message, TRUE);
       return;
     }
 
@@ -329,6 +523,12 @@ cc_ctrl_handle_received_msg (CcCommClosure *closure,
 
   CcReceivedMessageType type = cc_json_helper_get_message_type (message, reader);
 
+  if (!(type == CC_RWAIT_TYPE_PING || type == CC_RWAIT_TYPE_PONG || type == -1))
+    {
+      g_debug ("CcComm: Received message:");
+      cc_json_helper_dump_message (message, FALSE);
+    }
+
   switch (type)
     {
     case CC_RWAIT_TYPE_RECEIVER_STATUS:
@@ -340,7 +540,8 @@ cc_ctrl_handle_received_msg (CcCommClosure *closure,
       cc_ctrl_handle_get_app_availability (ctrl, reader);
       break;
     case CC_RWAIT_TYPE_LAUNCH_ERROR:
-      // cc_ctrl_handle_launch_error (ctrl, reader);
+      g_warning ("CcCtrl: Failed to launch app");
+      ctrl->closure->end_stream (ctrl->closure);
       break;
     case CC_RWAIT_TYPE_ANSWER:
       cc_ctrl_unset_waiting_for (ctrl, CC_RWAIT_TYPE_ANSWER);
@@ -348,7 +549,7 @@ cc_ctrl_handle_received_msg (CcCommClosure *closure,
       break;
     case CC_RWAIT_TYPE_MEDIA_STATUS:
       cc_ctrl_unset_waiting_for (ctrl, CC_RWAIT_TYPE_MEDIA_STATUS);
-      // cc_ctrl_handle_media_status (ctrl, reader);
+      cc_ctrl_handle_media_status (ctrl, message, reader);
       break;
     case CC_RWAIT_TYPE_PING:
       cc_ctrl_unset_waiting_for (ctrl, CC_RWAIT_TYPE_PING);
@@ -357,11 +558,12 @@ cc_ctrl_handle_received_msg (CcCommClosure *closure,
       cc_ctrl_unset_waiting_for (ctrl, CC_RWAIT_TYPE_PONG);
       break;
     case CC_RWAIT_TYPE_CLOSE:
-      // cc_ctrl_handle_close (ctrl, reader);
+      cc_ctrl_handle_close (ctrl, message);
       break;
     case CC_RWAIT_TYPE_UNKNOWN:
     default:
       g_warning ("CcCtrl: Unknown message type");
+      ctrl->closure->end_stream (ctrl->closure);
       break;
     }
 }
@@ -369,7 +571,7 @@ cc_ctrl_handle_received_msg (CcCommClosure *closure,
 void
 cc_ctrl_fatal_error (CcCommClosure *closure, GError **error)
 {
-  // XXX
+  // XXX: add error arg in end_stream and display an error message to user
   CcCtrl *ctrl = (CcCtrl *) closure->userdata;
   ctrl->closure->end_stream (ctrl->closure);
 }
@@ -387,8 +589,6 @@ cc_ctrl_get_callback_closure (CcCtrl *ctrl)
 gboolean
 cc_ctrl_connection_init (CcCtrl *ctrl, gchar *remote_address)
 {
-  // pay attn to the receiver ids sent before the messages
-
   g_autoptr (GError) error = NULL;
 
   ctrl->state = CC_CTRL_STATE_DISCONNECTED;
@@ -430,7 +630,6 @@ cc_ctrl_connection_init (CcCtrl *ctrl, gchar *remote_address)
       g_warning ("CcCtrl: Failed to send get status: %s", error->message);
       return FALSE;
     }
-  cc_ctrl_set_waiting_for (ctrl, CC_RWAIT_TYPE_RECEIVER_STATUS);
 
   return TRUE;
 }
@@ -474,3 +673,6 @@ cc_ctrl_finish (CcCtrl *ctrl, GError **r_error)
   // close the socket connection
   cc_comm_close_connection (&ctrl->comm);
 }
+
+// TODO: make the code less coupled with the mirroring app
+// TODO: use waiting_for for error messages
diff --git a/src/cc/cc-ctrl.h b/src/cc/cc-ctrl.h
index 4633978..9e5adc5 100644
--- a/src/cc/cc-ctrl.h
+++ b/src/cc/cc-ctrl.h
@@ -40,8 +40,9 @@ struct _CcCtrlClosure
 {
   gpointer userdata;
   // TODO
-  void (*end_stream) (struct _CcCtrlClosure *closure);
+  Offer * (*get_offer_message) (struct _CcCtrlClosure *closure);
   void (*start_stream) (struct _CcCtrlClosure *closure);
+  void (*end_stream) (struct _CcCtrlClosure *closure);
 };
 
 typedef struct _CcCtrlClosure CcCtrlClosure;
diff --git a/src/cc/cc-json-helper.c b/src/cc/cc-json-helper.c
index cbf33d8..16c3738 100644
--- a/src/cc/cc-json-helper.c
+++ b/src/cc/cc-json-helper.c
@@ -18,149 +18,147 @@
 
 #include "cc-json-helper.h"
 
-// static void
-// cc_json_helper_add_type_value (JsonBuilder *builder,
-//                                CcJsonType   type,
-//                                gpointer     value)
-// {
-//   switch (type)
-//     {
-//     case CC_JSON_TYPE_STRING:
-//       json_builder_add_string_value (builder, (gchar *) *value);
-//       break;
-//     case CC_JSON_TYPE_INT:
-//       json_builder_add_int_value (builder, (gint) *value);
-//       break;
-//     case CC_JSON_TYPE_DOUBLE:
-//       json_builder_add_double_value (builder, (gdouble) *value);
-//       break;
-//     case CC_JSON_TYPE_BOOLEAN:
-//       json_builder_add_boolean_value (builder, (gboolean) *value);
-//       break;
-//     case CC_JSON_TYPE_NULL: /* no additional arg is required here */
-//       json_builder_add_null_value (builder);
-//       break;
-//     case CC_JSON_TYPE_OBJECT:
-//       json_builder_begin_object (builder);
-//       json_builder_add_value (builder, (JsonNode *) value);
-//       json_builder_end_object (builder);
-//       break;
-//     /* only 1D arrays supported */
-//     }
-// }
-
-// void
-// cc_json_helper_build_root (JsonBuilder *builder,
-//                            const gchar *first_key,
-//                            va_list      var_args)
-// {
-//   gchar *key = first_key;
-
-//   while (key)
-//     {
-//       json_builder_set_member_name (builder, key);
-//       CcJsonType type = va_arg (var_args, CcJsonType);
-
-//       if (type == CC_JSON_TYPE_ARRAY)
-//         {
-//           json_builder_begin_array (builder);
-//           gint length = va_arg (var_args, gint);
-//           for (gint i = 0; i < length; i++)
-//             {
-//               cc_json_helper_add_type_value (builder, type, va_arg (var_args, gpointer));
-//             }
-//           json_builder_end_array (builder);
-//         }
-//       switch (type)
-//         {
-//         case CC_JSON_TYPE_STRING:
-//           json_builder_add_string_value (builder, va_arg (var_args, gchar *));
-//           break;
-//         case CC_JSON_TYPE_INT:
-//           json_builder_add_int_value (builder, va_arg (var_args, gint));
-//           break;
-//         case CC_JSON_TYPE_DOUBLE:
-//           json_builder_add_double_value (builder, va_arg (var_args, gdouble));
-//           break;
-//         case CC_JSON_TYPE_BOOLEAN:
-//           json_builder_add_boolean_value (builder, va_arg (var_args, gboolean));
-//           break;
-//         case CC_JSON_TYPE_NULL: /* no additional arg is required here */
-//           json_builder_add_null_value (builder);
-//           break;
-//         case CC_JSON_TYPE_OBJECT:
-//           json_builder_begin_object (builder);
-//           json_builder_add_value (builder, va_arg (var_args, JsonNode *));
-//           json_builder_end_object (builder);
-//           break;
-//         case CC_JSON_TYPE_ARRAY: /* type for array elements is also required here */
-//           json_builder_begin_array (builder);
-//           CcJsonType array_type = va_arg (var_args, CcJsonType);
-//           /* GArray */
-//           json_builder_end_array (builder);
-//           break;
-//         default:
-//           output = NULL;
-//           return;
-//         }
-
-//       key = va_arg (var_args, gchar *);
-//     }
-// }
-
-// void
-// cc_json_helper_build_string (gchar       *output,
-//                              const gchar *first_key,
-//                              ...)
-// {
-//   va_list var_args;
-//   va_start (var_args, first_key);
-
-//   JsonBuilder *builder = json_builder_new ();
-
-//   json_builder_begin_object (builder);
-//   cc_json_helper_build_root (builder, first_key, var_args);
-//   json_builder_end_object (builder);
-
-//   JsonGenerator *gen = json_generator_new ();
-//   JsonNode *root = json_builder_get_root (builder);
-//   json_generator_set_root (gen, root);
-
-//   output = json_generator_to_data (gen, NULL);
-
-//   va_end (var_args);
-//   json_node_free (root);
-//   g_object_unref (gen);
-//   g_object_unref (builder);
-// }
-
-// void
-// cc_json_helper_build_string (gchar       *output,
-//                              const gchar *first_key,
-//                              ...)
-// {
-//   va_list var_args;
-//   va_start (var_args, first_key);
-
-//   JsonBuilder *builder = json_builder_new ();
-
-//   json_builder_begin_object (builder);
-//   cc_json_helper_build_root (builder, first_key, var_args);
-//   json_builder_end_object (builder);
-
-//   JsonGenerator *gen = json_generator_new ();
-//   JsonNode *root = json_builder_get_root (builder);
-//   json_generator_set_root (gen, root);
-
-//   output = json_generator_to_data (gen, NULL);
-
-//   va_end (var_args);
-//   json_node_free (root);
-//   g_object_unref (gen);
-//   g_object_unref (builder);
-// }
+static void
+cc_json_helper_build_internal (JsonBuilder *builder,
+                               gchar       *first_key,
+                               va_list     var_args)
+{
+  gchar *key = first_key;
 
+  while (key)
+    {
+      json_builder_set_member_name (builder, key);
+      CcJsonType type = va_arg (var_args, CcJsonType);
 
+      if (type < CC_JSON_TYPE_STRING || type > CC_JSON_TYPE_ARRAY_OBJECT)
+        {
+          g_warning ("CcJsonHelper: Incorrect type passed in json contructor: %d", type);
+          return;
+        }
+
+      switch (type)
+        {
+        case CC_JSON_TYPE_STRING:
+          json_builder_add_string_value (builder, va_arg (var_args, gchar *));
+          break;
+        case CC_JSON_TYPE_INT:
+          json_builder_add_int_value (builder, va_arg (var_args, gint));
+          break;
+        case CC_JSON_TYPE_DOUBLE:
+          json_builder_add_double_value (builder, va_arg (var_args, gdouble));
+          break;
+        case CC_JSON_TYPE_BOOLEAN:
+          json_builder_add_boolean_value (builder, va_arg (var_args, gboolean));
+          break;
+        case CC_JSON_TYPE_NULL: /* no additional arg is required here */
+          json_builder_add_null_value (builder);
+          break;
+        case CC_JSON_TYPE_OBJECT:
+          json_builder_add_value (builder, va_arg (var_args, JsonNode *));
+          break;
+        default:
+          break;
+        }
+
+      if (type < CC_JSON_TYPE_ARRAY_STRING)
+        {
+          key = va_arg (var_args, gchar *);
+          continue;
+        }
+
+      json_builder_begin_array (builder);
+      GArray *arr = va_arg (var_args, GArray *);
+      guint i;
+
+      for (i=0; i<arr->len; i++)
+        {
+          switch (type)
+          {
+            case CC_JSON_TYPE_ARRAY_STRING:
+              json_builder_add_string_value (builder, g_array_index (arr, gchar *, i));
+              break;
+            case CC_JSON_TYPE_ARRAY_INT:
+              json_builder_add_int_value (builder, g_array_index (arr, gint, i));
+              break;
+            case CC_JSON_TYPE_ARRAY_DOUBLE:
+              json_builder_add_double_value (builder, g_array_index (arr, gdouble, i));
+              break;
+            case CC_JSON_TYPE_ARRAY_BOOLEAN:
+              json_builder_add_boolean_value (builder, g_array_index (arr, gboolean, i));
+              break;
+            case CC_JSON_TYPE_ARRAY_NULL:
+              json_builder_add_null_value (builder);
+              break;
+            case CC_JSON_TYPE_ARRAY_OBJECT:
+              json_builder_add_value (builder, g_array_index (arr, JsonNode *, i));
+              break;
+            default:
+              break;
+          }
+        }
+
+      json_builder_end_array (builder);
+
+      key = va_arg (var_args, gchar *);
+    }
+}
+
+void
+cc_json_helper_build_node (JsonNode **output,
+                           gchar     *first_key,
+                           ...)
+{
+  va_list var_args;
+  va_start (var_args, first_key);
+
+  JsonBuilder *builder = json_builder_new ();
+
+  json_builder_begin_object (builder);
+  cc_json_helper_build_internal (builder, first_key, var_args);
+  json_builder_end_object (builder);
+
+  *output = json_builder_get_root (builder);
+
+  va_end (var_args);
+  g_object_unref (builder);
+}
+
+void
+cc_json_helper_build_string (gchar  **output,
+                            //  gboolean pretty_print,
+                             gchar   *first_key,
+                             ...)
+{
+  va_list var_args;
+  va_start (var_args, first_key);
+
+  JsonBuilder *builder = json_builder_new ();
+
+  json_builder_begin_object (builder);
+  cc_json_helper_build_internal (builder, first_key, var_args);
+  json_builder_end_object (builder);
+
+  JsonNode *root = json_builder_get_root (builder);
+  JsonGenerator *gen = json_generator_new ();
+
+  // json_generator_set_pretty (gen, pretty_print);
+  json_generator_set_root (gen, root);
+  *output = json_generator_to_data (gen, NULL);
+
+  va_end (var_args);
+  json_node_free (root);
+  g_object_unref (gen);
+  g_object_unref (builder);
+}
+
+void
+cc_json_helper_node_to_string (gchar **output, JsonNode *node)
+{
+  JsonGenerator *gen = json_generator_new ();
+  json_generator_set_root (gen, node);
+  *output = json_generator_to_data (gen, NULL);
+  g_object_unref (gen);
+}
 
 CcReceivedMessageType
 cc_json_helper_get_message_type (Cast__Channel__CastMessage *message,
@@ -169,20 +167,20 @@ cc_json_helper_get_message_type (Cast__Channel__CastMessage *message,
   const gchar *message_type;
   g_autoptr (GError) error = NULL;
 
-  if (reader == NULL)
-    {
-      g_autoptr(JsonParser) parser = NULL;
+  // if (reader == NULL)
+  //   {
+  //     g_autoptr(JsonParser) parser = NULL;
 
-      parser = json_parser_new ();
-      if (!json_parser_load_from_data (parser, message->payload_utf8, -1, &error))
-        {
-          cc_json_helper_dump_message (message);
-          g_warning ("CcJsonHelper: Error parsing received message JSON: %s", error->message);
-          return -1;
-        }
+  //     parser = json_parser_new ();
+  //     if (!json_parser_load_from_data (parser, message->payload_utf8, -1, &error))
+  //       {
+  //         cc_json_helper_dump_message (message);
+  //         g_warning ("CcJsonHelper: Error parsing received message JSON: %s", error->message);
+  //         return -1;
+  //       }
 
-      reader = json_reader_new (json_parser_get_root (parser));
-    }
+  //     reader = json_reader_new (json_parser_get_root (parser));
+  //   }
 
   gboolean typeExists = json_reader_read_member (reader, "type");
   if (typeExists)
@@ -194,8 +192,8 @@ cc_json_helper_get_message_type (Cast__Channel__CastMessage *message,
         message_type = json_reader_get_string_value (reader);
       else
         {
-          cc_json_helper_dump_message (message);
           g_warning ("CcJsonHelper: Error parsing received message JSON: no type or responseType keys");
+          cc_json_helper_dump_message (message, TRUE);
           return -1;
         }
     }
@@ -224,14 +222,34 @@ cc_json_helper_get_message_type (Cast__Channel__CastMessage *message,
     } cc_end
 }
 
+/* borked var reduces extra computation */
 void
-cc_json_helper_dump_message (Cast__Channel__CastMessage *message)
+cc_json_helper_dump_message (Cast__Channel__CastMessage *message, gboolean borked)
 {
-  // TODO: pretty print json object
-  g_debug ("{ source_id: %s, destination_id: %s, namespace_: %s, payload_type: %d, payload_utf8: %s }",
-    message->source_id,
-    message->destination_id,
-    message->namespace_,
-    message->payload_type,
-    message->payload_utf8);
+  JsonNode *payload_utf8_node;
+  JsonParser *parser = json_parser_new ();
+  g_autoptr (GError) error = NULL;
+
+  if (borked || !json_parser_load_from_data (parser, message->payload_utf8, -1, &error))
+    {
+      g_warning ("CcJsonHelper: Error parsing received JSON payload: %s", error->message);
+      g_debug ("{ source_id: %s, destination_id: %s, namespace_: %s, payload_utf8: %s }",
+        message->source_id,
+        message->destination_id,
+        message->namespace_,
+        message->payload_utf8);
+      return;
+    }
+
+  payload_utf8_node = json_parser_get_root (parser);
+
+  gchar *output;
+  cc_json_helper_build_string (&output, //TRUE,
+    "source_id", CC_JSON_TYPE_STRING, message->source_id,
+    "destination_id", CC_JSON_TYPE_STRING, message->destination_id,
+    "namespace", CC_JSON_TYPE_STRING, message->namespace_,
+    "payload_utf8", CC_JSON_TYPE_OBJECT, payload_utf8_node,
+    NULL);
+  
+  g_debug ("%s", output);
 }
diff --git a/src/cc/cc-json-helper.h b/src/cc/cc-json-helper.h
index 0f22124..02bcd80 100644
--- a/src/cc/cc-json-helper.h
+++ b/src/cc/cc-json-helper.h
@@ -25,7 +25,8 @@
 
 G_BEGIN_DECLS
 
-typedef enum {
+typedef enum
+{
   CC_JSON_TYPE_STRING,
   CC_JSON_TYPE_INT,
   CC_JSON_TYPE_DOUBLE,
@@ -41,8 +42,14 @@ typedef enum {
   CC_JSON_TYPE_ARRAY_OBJECT,
 } CcJsonType;
 
+
+// void cc_json_helper_build_node (JsonNode **output, gchar *first_key, ...) G_GNUC_NULL_TERMINATED;
+void cc_json_helper_build_node (JsonNode **output, gchar *first_key, ...);
+void cc_json_helper_build_string (gchar **output, gchar *first_key, ...);
+// void cc_json_helper_build_string (gchar **output, gboolean pretty_print, gchar *first_key, ...);
+void cc_json_helper_node_to_string (gchar **output, JsonNode *node);
 CcReceivedMessageType cc_json_helper_get_message_type (Cast__Channel__CastMessage *message,
                                                        JsonReader *json_reader);
-void cc_json_helper_dump_message (Cast__Channel__CastMessage *message);
+void cc_json_helper_dump_message (Cast__Channel__CastMessage *message, gboolean borked);
 
 G_END_DECLS
diff --git a/src/nd-cc-sink.c b/src/nd-cc-sink.c
index ff83e9a..6ec5e73 100644
--- a/src/nd-cc-sink.c
+++ b/src/nd-cc-sink.c
@@ -24,6 +24,7 @@
 #include "wfd/wfd-media-factory.h"
 #include "wfd/wfd-server.h"
 #include "cc/cc-ctrl.h"
+#include "cc/cc-common.h"
 
 // TODO: add cancellable everywhere
 
@@ -327,8 +328,8 @@ nd_cc_sink_get_callback_closure (NdCCSink *sink)
 {
   CcCtrlClosure *closure = (CcCtrlClosure *) g_malloc (sizeof (CcCtrlClosure));
   closure->userdata = sink;
-  closure->end_stream = nd_cc_sink_error_in_ctrl;
   closure->start_stream = nd_cc_sink_start_webrtc_stream;
+  closure->end_stream = nd_cc_sink_error_in_ctrl;
   return closure;
 }
 
@@ -413,8 +414,8 @@ nd_cc_sink_sink_stop_stream_int (NdCCSink *self)
 {
   cc_ctrl_finish (&self->ctrl, NULL);
 
-  g_cancellable_cancel (self->cancellable);
-  g_clear_object (&self->cancellable);
+  // g_cancellable_cancel (self->cancellable);
+  // g_clear_object (&self->cancellable);
 
   self->cancellable = g_cancellable_new ();
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]