dbus websocket: add screen capture http request [1/1]

PD#SWPL-184678

Problem:
add screen capture http request

Solution:
add screen capture http request

Verify:
local

Change-Id: I5eaca13de273d601abf8dab139df515ce8f57731
Signed-off-by: Daogao Xu <daogao.xu@amlogic.com>
diff --git a/aml_dbus_ws_br/jsonrpc-dbus.c b/aml_dbus_ws_br/jsonrpc-dbus.c
index 11a756e..bd9c98e 100644
--- a/aml_dbus_ws_br/jsonrpc-dbus.c
+++ b/aml_dbus_ws_br/jsonrpc-dbus.c
@@ -81,6 +81,8 @@
 
 static int dbus_sig_get_len(const char *sig) {
   char buf[64], s = 0, pos = 1, ch = *sig;
+  if (*sig == '\0')
+    return 0;
   do {
     if ((ch == 'a' || ch == '(' || ch == '{') && s < sizeof(buf)) {
       buf[s++] = ch;
diff --git a/aml_dbus_ws_br/main.c b/aml_dbus_ws_br/main.c
index d58099d..b564248 100644
--- a/aml_dbus_ws_br/main.c
+++ b/aml_dbus_ws_br/main.c
@@ -33,8 +33,12 @@
 #include <stdio.h>
 #include <string.h>
 #include <systemd/sd-bus.h>
+#include <fcntl.h>              /* Obtain O_* constant definitions */
+#include <unistd.h>
+
 
 #define WS_PROTOCOL_NAME "ambus-ws-br"
+#define WS_PROTOCOL_EXT "http-ext"
 
 struct json_msg_buf {
   struct json_msg_buf *next;
@@ -197,10 +201,118 @@
 
 static void sul_timer(struct lws_sorted_usec_list *sul) { dbus_process(); }
 
+struct http_capture_request {
+  struct lws *wsi, *wsi_pipe;
+  sd_bus_slot *slot;
+  int fds[2];
+  int len;
+};
+
+static int call_capture_screen_reply(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+  struct http_capture_request *pss = (struct http_capture_request *)userdata;
+  int ret = 0;
+  close(pss->fds[1]);
+  if (sd_bus_message_is_method_error(m, NULL)) {
+    lwsl_warn("capture fail %s\n", sd_bus_message_get_error(m)->message);
+    ret = lws_return_http_status(pss->wsi, HTTP_STATUS_SERVICE_UNAVAILABLE, sd_bus_message_get_error(m)->message);
+  } else {
+    fcntl(pss->fds[0], F_SETFL, fcntl(pss->fds[0], F_GETFL, 0) | O_NONBLOCK);
+    lws_sock_file_fd_type sock;
+    sock.sockfd = pss->fds[0];
+    pss->wsi_pipe = lws_adopt_descriptor_vhost(data.vhost, LWS_ADOPT_RAW_FILE_DESC, sock, WS_PROTOCOL_EXT, NULL);
+    if (pss->wsi_pipe == NULL) {
+      lwsl_warn("%s: foreign socket adoption failed\n", __func__);
+      ret = lws_return_http_status(pss->wsi, HTTP_STATUS_SERVICE_UNAVAILABLE, "foreign socket adoption failed");
+    } else {
+      uint8_t buf[4096], *p = buf, *end = &buf[sizeof(buf) - 1];
+      lws_set_opaque_user_data(pss->wsi_pipe, pss);
+      ret =
+          lws_add_http_common_headers(pss->wsi, HTTP_STATUS_OK, "image/jpeg", LWS_ILLEGAL_HTTP_CONTENT_LEN, &p, end) ||
+          lws_finalize_write_http_header(pss->wsi, buf, &p, end);
+      goto done;
+    }
+  }
+  ret = ret || lws_http_transaction_completed(pss->wsi);
+done:
+  if (ret)
+    lwsl_warn("http result %d\n", ret);
+  return 0;
+}
+
+static int callback_http_ext(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
+  struct http_capture_request *pss = (struct http_capture_request *)user;
+  lwsl_info("callback_http_ext %s reason:%d\n", lws_wsi_tag(wsi), reason);
+  switch (reason) {
+  case LWS_CALLBACK_HTTP: {
+    pss->wsi = wsi;
+    uint8_t buf[32];
+    if (!lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) /* not a GET */
+      break;
+    int width = lws_get_urlarg_by_name_safe(wsi, "width", buf, sizeof(buf) - 1) > 0 ? atoi(buf) : 1280;
+    int height = lws_get_urlarg_by_name_safe(wsi, "height", buf, sizeof(buf) - 1) > 0 ? atoi(buf) : 720;
+    int type = lws_get_urlarg_by_name_safe(wsi, "type", buf, sizeof(buf) - 1) > 0 ? atoi(buf) : 2;
+    if (pipe2(pss->fds, O_CLOEXEC)) {
+      lwsl_warn("%s: call pipe fail %d %s\n", __func__, errno, strerror(errno));
+      return 1;
+    }
+    pss->len = 0;
+    int res = sd_bus_call_method_async(data.bus, &pss->slot, "amlogic.yocto.plf1", "/amlogic/yocto/ScreenCapture1",
+                                       "amlogic.yocto.ScreenCapture1", "start", call_capture_screen_reply, pss, "hiii",
+                                       pss->fds[1], width, height, type);
+    if (res < 0) {
+      lwsl_warn("%s: call amlogic.yocto.ScreenCapture1 start fail %d\n", __func__, res);
+      return 1;
+    }
+    return 0;
+  } break;
+  case LWS_CALLBACK_HTTP_WRITEABLE: {
+    int complete = 0;
+    if (pss->wsi_pipe) {
+      uint8_t buf[LWS_PRE + 4096], *start = &buf[LWS_PRE];
+      int len = read(pss->fds[0], start, sizeof(buf) - LWS_PRE);
+      lwsl_info("callback_http_ext screencapture data len %d @%d\n", len, pss->len);
+      if (len > 0) {
+        pss->len += len;
+        if (lws_write(wsi, start, len, LWS_WRITE_HTTP) != len)
+          return 1;
+      } else {
+        lwsl_info("capture done, write len %d, %d errno %d %s\n", pss->len, len, errno, strerror(errno));
+        if (len == 0 || errno != EAGAIN) {
+          lws_close_free_wsi(pss->wsi_pipe, LWS_CLOSE_STATUS_NOSTATUS, "screencap pipe");
+          pss->wsi_pipe = NULL;
+          len = lws_write(wsi, start, 0, LWS_WRITE_HTTP_FINAL);
+          complete = 1;
+        }
+      }
+    } else
+      complete = 1;
+    if (complete && lws_http_transaction_completed(wsi))
+      return -1;
+    return 0;
+  } break;
+  case LWS_CALLBACK_RAW_RX_FILE:
+    pss = lws_get_opaque_user_data(wsi);
+    lws_callback_on_writable(pss->wsi);
+    return 0;
+  case LWS_CALLBACK_RAW_CLOSE_FILE:
+    pss = lws_get_opaque_user_data(wsi);
+    pss->wsi_pipe = NULL;
+    return 0;
+  case LWS_CALLBACK_CLOSED_HTTP:
+    if (pss->wsi_pipe)
+        lws_close_free_wsi(pss->wsi_pipe, LWS_CLOSE_STATUS_NOSTATUS, "screencap pipe");
+    break;
+  default:
+    break;
+  }
+  return lws_callback_http_dummy(wsi, reason, user, in, len);
+}
+
 static struct lws_protocols protocols[] = {
     {"http", lws_callback_http_dummy, 0, 0},
     {WS_PROTOCOL_NAME, callback_ambus_ws_br, sizeof(struct json_rpc_ws_session), 0, 0, NULL, 0},
     {"dbusraw", callback_dbus, 0, 0},
+    {WS_PROTOCOL_EXT, callback_http_ext, sizeof(struct http_capture_request), 0},
     {NULL, NULL, 0, 0} /* terminator */
 };
 
@@ -253,8 +365,28 @@
   }
 }
 
-static struct lws_http_mount mount = {
+static struct lws_http_mount mount_http_ext = {
     /* .mount_next */ NULL,  /* linked-list "next" */
+    /* .mountpoint */ "/screencapture",   /* mountpoint URL */
+    /* .origin */ NULL,       /* serve from dir */
+    /* .def */ NULL, /* default filename */
+    /* .protocol */ WS_PROTOCOL_EXT,
+    /* .cgienv */ NULL,
+    /* .extra_mimetypes */ NULL,
+    /* .interpret */ NULL,
+    /* .cgi_timeout */ 0,
+    /* .cache_max_age */ 0,
+    /* .auth_mask */ 0,
+    /* .cache_reusable */ 0,
+    /* .cache_revalidate */ 0,
+    /* .cache_intermediaries */ 0,
+    /* .origin_protocol */ LWSMPRO_CALLBACK, /* files in a dir */
+    /* .mountpoint_len */ 14,             /* char count */
+    /* .basic_auth_login_file */ NULL,
+};
+
+static struct lws_http_mount mount = {
+    /* .mount_next */ &mount_http_ext,  /* linked-list "next" */
     /* .mountpoint */ "/",   /* mountpoint URL */
     /* .origin */ ".",       /* serve from dir */
     /* .def */ "index.html", /* default filename */