e_info_client: Implement Screen Recorder (Argument name: -dump_video)
authorSeunghun Lee <shiin.lee@samsung.com>
Tue, 12 Nov 2019 01:43:16 +0000 (10:43 +0900)
committerGwanglim Lee <gl77.lee@samsung.com>
Thu, 6 Feb 2020 05:49:03 +0000 (14:49 +0900)
Recording the screen using streamrecorder library.

usage: winfo -dump_video [--help] [--rate=<framerate>]
   [--resolution=<width>x<height>] <output file>

        --help                          this help text
        --rate=<framerate>              replay frame rate
        --resolution=<width>x<height>   resuoltion size

Change-Id: Ife2769751c485efcb509d6f0b2e6607277b98e27

configure.ac
packaging/enlightenment.spec
src/bin/Makefile.mk
src/bin/e_info_client.c
src/bin/e_info_client_screen_recorder.c [new file with mode: 0644]
src/bin/e_info_client_screen_recorder.h [new file with mode: 0644]

index aeb22d1b49032d37e54b7d7510137ca5c4146dd5..07ff69ad5c65b495d41210a1b3fa869cdc2f155a 100755 (executable)
@@ -292,6 +292,7 @@ PKG_CHECK_MODULES(E_INFO, [
   ecore >= ${efl_version}
   eldbus >= ${efl_version}
   xkbcommon
+  capi-media-streamrecorder
 ])
 
 PKG_CHECK_EXISTS([xkeyboard-config],[
index ba0f5ee5aff5f67c246b752a50fe609823f953d6..37ac686c7c78ca5e4f70308b5d58355eaf4a5adc 100644 (file)
@@ -55,6 +55,10 @@ BuildRequires:  systemd-devel
 BuildRequires:  pkgconfig(libinput)
 BuildRequires:  pkgconfig(presentation-time-server)
 Requires:       libwayland-extension-server
+
+# for recording video in enlightenment_info
+BuildRequires:  pkgconfig(capi-media-streamrecorder)
+
 %if "%{LIBGOMP}" == "use"
 Requires:       libgomp
 %endif
index ad5117b8c110370541342ba168bfd069f5b2e0d8..004b816bb7f71407e7265aa78c95ac42bc973767 100644 (file)
@@ -320,7 +320,8 @@ src_bin_enlightenment_LDFLAGS += @LIBINPUT_LIBS@
 
 src_bin_enlightenment_info_SOURCES = \
 src/bin/e.h \
-src/bin/e_info_client.c
+src/bin/e_info_client.c \
+src/bin/e_info_client_screen_recorder.c
 src_bin_enlightenment_info_LDADD = @E_INFO_LIBS@
 src_bin_enlightenment_info_CPPFLAGS = $(E_CPPFLAGS) @E_INFO_CFLAGS@
 src_bin_enlightenment_info_LDFLAGS = -pie
index 4c1838c0bd10d4469288bc87748593f619059cef..23145c218d994b63cefca4c7b3b28cadc66c0c43 100644 (file)
@@ -1,5 +1,6 @@
 #include "e.h"
 #include "e_info_shared_types.h"
+#include "e_info_client_screen_recorder.h"
 #include <time.h>
 #include <dirent.h>
 #include <sys/mman.h>
@@ -5386,6 +5387,12 @@ usage :
    return;
 }
 
+static void
+_e_info_client_proc_screen_record(int argc, char **argv)
+{
+   e_info_client_screen_recorder_run(argc, argv);
+}
+
 typedef struct _ProcInfo
 {
    const char *option;
@@ -5717,6 +5724,12 @@ static ProcInfo procs_to_execute[] =
       "get focus history",
       _e_info_client_proc_focus_history
    },
+   {
+      "dump_video",  /* Option */
+      SCREEN_RECORDER_USAGE,/* Params */
+      "Recording the screen", /* Description */
+      _e_info_client_proc_screen_record /* func */
+   },
 };
 
 static Eina_List *list_tracelogs = NULL;
diff --git a/src/bin/e_info_client_screen_recorder.c b/src/bin/e_info_client_screen_recorder.c
new file mode 100644 (file)
index 0000000..aee1842
--- /dev/null
@@ -0,0 +1,358 @@
+#include <unistd.h>
+#include <Eina.h>
+#include <Ecore.h>
+#include <Ecore_Input.h>
+#include <streamrecorder.h>
+#include "e_info_client_screen_recorder.h"
+
+int _log_dom = -1;
+
+#define CRT(...)  EINA_LOG_DOM_CRIT(_log_dom, __VA_ARGS__)
+#define ERR(...)  EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__)
+#define WRN(...)  EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__)
+#define INF(...)  EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__)
+#define DBG(...)  EINA_LOG_DOM_DBG(_log_dom, __VA_ARGS__)
+
+typedef struct
+{
+   char filename[PATH_MAX];
+   int framerate;
+   struct
+     {
+        int w, h;
+     } resolution;
+} Screen_Recorder_Args;
+
+streamrecorder_h _stream_recorder;
+
+static Eina_Bool   _screen_recorder_efl_init(void);
+static void        _screen_recorder_efl_shutdown(void);
+static Eina_Bool   _screen_recorder_parse_args(int argc, char **argv, Screen_Recorder_Args *args);
+static Eina_Bool   _stream_recorder_run(const char *filename, int framerate, int w, int h);
+
+static void
+_usage(void)
+{
+   fprintf(stderr, SCREEN_RECORDER_USAGE);
+}
+
+static Eina_Bool
+_screen_recorder_cb_handle_stdin(void *data, Ecore_Fd_Handler *handler EINA_UNUSED)
+{
+   Screen_Recorder_Args *args = data;
+   char c;
+   int ret;
+
+   ret = scanf("%c", &c);
+   if (ret < 1)
+     return ECORE_CALLBACK_RENEW;
+
+   streamrecorder_commit(_stream_recorder);
+   streamrecorder_destroy(_stream_recorder);
+   _stream_recorder = NULL;
+
+   fprintf(stdout, "Finish recording screen: %s\n", args->filename);
+
+   ecore_main_loop_quit();
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+int
+e_info_client_screen_recorder_run(int argc, char **argv)
+{
+   Screen_Recorder_Args args =
+     {  {0, }, /* filename */
+        60, /* framerate */
+        {360, 360} /* widthxheight resolution */
+     };
+   Eina_Bool res;
+   char *val;
+
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(_screen_recorder_efl_init(), -1);
+
+   val = getenv("XDG_RUNTIME_DIR");
+   if (!val)
+     {
+        WRN("Not found \"XDG_RUNTIME_DIR\", Set \"/run\" on its value "
+            "for run screen recording anyway.");
+        setenv("XDG_RUNTIME_DIR", "/run", 1);
+     }
+
+   res = _screen_recorder_parse_args(argc, argv, &args);
+   if (!res)
+     {
+        _usage();
+        goto end;
+     }
+
+   res = _stream_recorder_run(args.filename,
+                              args.framerate,
+                              args.resolution.w,
+                              args.resolution.h);
+   if (!res)
+     {
+        ERR("Could not initialize the streamrecorder");
+        goto end;
+     }
+
+   fprintf(stdout, "Press any key to finish recording");
+   fflush(stdout);
+
+   ecore_main_fd_handler_add(STDIN_FILENO, ECORE_FD_READ,
+                             _screen_recorder_cb_handle_stdin,
+                             &args, NULL, NULL);
+
+   ecore_main_loop_begin();
+
+end:
+   _screen_recorder_efl_shutdown();
+
+   return (res == EINA_TRUE) ? 0 : -1;
+}
+
+static Eina_Bool
+_screen_recorder_efl_init(void)
+{
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_init(), EINA_FALSE);
+
+   _log_dom = eina_log_domain_register("screen_recorder", NULL);
+   if (_log_dom < 0)
+     {
+        EINA_LOG_CRIT("Could not create logging domain '%s'.", "screen_recorder");
+        goto fail_log_dom;
+     }
+
+   EINA_SAFETY_ON_FALSE_GOTO(ecore_init(), fail_ecore);
+
+   DBG("Done efl init");
+
+   return EINA_TRUE;
+
+fail_ecore:
+   eina_log_domain_unregister(_log_dom);
+   _log_dom = -1;
+fail_log_dom:
+   eina_shutdown();
+
+   return EINA_FALSE;
+}
+
+static void
+_screen_recorder_efl_shutdown(void)
+{
+   ecore_shutdown();
+   eina_log_domain_unregister(_log_dom);
+   _log_dom = -1;
+   eina_shutdown();
+}
+
+static Eina_Bool
+_screen_recorder_fullpath_make_with_ext(char *out, const char *filename)
+{
+   char cwd[PATH_MAX], fname_with_ext[PATH_MAX] = {0,};
+   char *dot;
+   const char *ext = ".mp4";
+   const char *prefix = "e-screen-record";
+   int len;
+
+   if (filename)
+     {
+        dot = strrchr(filename, '.');
+        if ((!dot) || (strlen(dot + 1) !=3) || (strncmp(dot, ext, strlen(ext)) != 0))
+          {
+             /* concatenate file extention. */
+             len = strnlen(filename, PATH_MAX - 4);
+             strncat(fname_with_ext, filename, len);
+             strncat(fname_with_ext, ext, 4);
+          }
+        else
+          {
+             strncat(fname_with_ext, filename, (PATH_MAX - 1));
+             fname_with_ext[PATH_MAX - 1] = '\0';
+          }
+
+        if (fname_with_ext[0] != '/')
+          {
+             /* concatenate current working directory or '/tmp'. */
+             if (!getcwd(cwd, sizeof(cwd)))
+               snprintf(cwd, sizeof(cwd), "/tmp");
+             snprintf(out, PATH_MAX, "%s/%s", cwd, fname_with_ext);
+          }
+        else
+          strncpy(out, fname_with_ext, PATH_MAX);
+     }
+   else
+     {
+        time_t timer;
+        struct tm *t, buf;
+
+        timer = time(NULL);
+        t = localtime_r(&timer, &buf);
+        if (!t)
+          return EINA_FALSE;
+
+        if (!getcwd(cwd, sizeof(cwd)))
+          snprintf(cwd, sizeof(cwd), "/tmp");
+
+        snprintf(out, PATH_MAX, "%s/%s-%04d%02d%02d.%02d%02d%02d%s",
+                 cwd, prefix,
+                 t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour,
+                 t->tm_min, t->tm_sec,
+                 ext);
+        out[PATH_MAX - 1] = '\0';
+     }
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_screen_recorder_parse_args(int argc, char **argv, Screen_Recorder_Args *args)
+{
+   char *filename = NULL;
+   int i;
+   Eina_Bool res;
+
+   DBG("Begin Parse Arguments");
+
+   for (i = 0; i < argc; i++)
+     {
+        if (strcmp(argv[i], "-dump_video") == 0)
+          continue;
+        else if ((strcmp(argv[i], "--help") == 0) || (strcmp(argv[i], "-h") == 0))
+          return EINA_FALSE;
+        else if (sscanf(argv[i], "--rate=%d", &args->framerate) == 1)
+          {
+             if (args->framerate < 1)
+               {
+                  fprintf(stderr, "ERROR: invalid framerate, "
+                          "it should be greater than 0: %d\n",
+                          args->framerate);
+                  return EINA_FALSE;
+               }
+          }
+        else if (sscanf(argv[i], "--resolution=%dx%d", &args->resolution.w, &args->resolution.h) == 2)
+          {
+             if ((args->resolution.w < 1) || (args->resolution.h < 1))
+               {
+                  fprintf(stderr, "ERROR: invalid resolution, "
+                          "resolution value should be greater than 0: %dx%d\n",
+                          args->resolution.w, args->resolution.h);
+                  return EINA_FALSE;
+               }
+          }
+        else if ((strcmp(argv[i], "--") == 0) || (argv[i][0] == '-'))
+          {
+             fprintf(stderr, "ERROR: unkown parameter, %s\n", argv[i]);
+             return EINA_FALSE;
+          }
+        else if (i == (argc - 1))
+          filename = argv[i];
+     }
+
+   res = _screen_recorder_fullpath_make_with_ext(args->filename, filename);
+   if (!res)
+     {
+        ERR("invalid file path: %s", args->filename);
+        return EINA_FALSE;
+     }
+
+   DBG("Done Parse Arguments");
+   INF("Arguments: framerate(%d) resolution(%dx%d) filename(%s)",
+       args->framerate, args->resolution.w, args->resolution.h, args->filename);
+
+   return EINA_TRUE;
+}
+
+static void
+_cb_stream_recorder_status(unsigned long long elapsed_time, unsigned long long file_size, void *user_data)
+{
+   DBG("Recording Status: time(%.5f s) size(%lld KB)",
+       (double)elapsed_time / (double)1000, file_size);
+}
+
+static Eina_Bool
+_stream_recorder_run(const char *filename, int framerate, int w, int h)
+{
+   streamrecorder_state_e state;
+   int err;
+
+   DBG("Begin Initializing Streamrecorder");
+
+   err = streamrecorder_create(&_stream_recorder);
+   if (err != STREAMRECORDER_ERROR_NONE)
+     {
+        ERR("failed to create streamrecorder: %d", err);
+        return EINA_FALSE;
+     }
+
+   err = streamrecorder_get_state(_stream_recorder, &state);
+   if (err == STREAMRECORDER_ERROR_NONE)
+     {
+        if (state == STREAMRECORDER_STATE_CREATED)
+          INF("StreamRecorder State: 'Created'");
+        else
+          INF("StreamRecorder State: %d", state);
+     }
+
+   err = streamrecorder_set_video_resolution(_stream_recorder, w, h);
+   if (err != STREAMRECORDER_ERROR_NONE)
+     {
+        ERR("failed to set video resolution: %dx%d", w, h);
+        goto fail;
+     }
+
+   err = streamrecorder_set_video_framerate(_stream_recorder, framerate);
+   if (err != STREAMRECORDER_ERROR_NONE)
+     {
+        ERR("failed to set video framerate: %d", 30);
+        goto fail;
+     }
+
+   err = streamrecorder_set_filename(_stream_recorder, filename);
+   if (err != STREAMRECORDER_ERROR_NONE)
+     {
+        ERR("failed to set filename for streamrecorder: %s", filename);
+        goto fail;
+     }
+
+   err = streamrecorder_set_mode(_stream_recorder,
+                                 STREAMRECORDER_MODE_DEVICE_LOOPBACK);
+   if (err != STREAMRECORDER_ERROR_NONE)
+     {
+        ERR("failed to set mode of streamrecorder: %d", err);
+        goto fail;
+     }
+
+   err = streamrecorder_set_recording_status_cb(_stream_recorder,
+                                                _cb_stream_recorder_status,
+                                                NULL);
+   if (err != STREAMRECORDER_ERROR_NONE)
+     {
+        ERR("failed to set callback for recording status");
+        goto fail;
+     }
+
+   err = streamrecorder_prepare(_stream_recorder);
+   if (err != STREAMRECORDER_ERROR_NONE)
+     {
+        ERR("failed to prepare streamrecorder: %d", err);
+        goto fail;
+     }
+
+   err = streamrecorder_start(_stream_recorder);
+   if (err != STREAMRECORDER_ERROR_NONE)
+     {
+        ERR("failed to start streamrecorder");
+        goto fail;
+     }
+
+   DBG("Done Initializing Streamrecorder");
+
+   return EINA_TRUE;
+fail:
+   streamrecorder_destroy(_stream_recorder);
+   _stream_recorder = NULL;
+
+   return EINA_FALSE;
+}
diff --git a/src/bin/e_info_client_screen_recorder.h b/src/bin/e_info_client_screen_recorder.h
new file mode 100644 (file)
index 0000000..c2b0fbe
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef E_INFO_CLIENT_SCREEN_RECORDER_H
+#define E_INFO_CLIENT_SCREEN_RECORDER_H
+
+#define SCREEN_RECORDER_ARG_NAME    "dump_video"
+
+#define SCREEN_RECORDER_USAGE \
+    "usage: winfo -" SCREEN_RECORDER_ARG_NAME " " \
+    "[--help] [--rate=<framerate>] [--resolution=<width>x<height>] " \
+    "<output file>\n\n" \
+    "\t--help\t\t\t\tthis help text\n" \
+    "\t--rate=<framerate>\t\treplay frame rate (default: 60)\n" \
+    "\t--resolution=<width>x<height>\tresuoltion size (default: 360x360)\n\n"
+
+int e_info_client_screen_recorder_run(int argc, char **argv);
+
+#endif