From: Seunghun Lee Date: Tue, 12 Nov 2019 01:43:16 +0000 (+0900) Subject: e_info_client: Implement Screen Recorder (Argument name: -dump_video) X-Git-Tag: submit/tizen/20200206.060445~4 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7ba1651072e3ff03848d3382e80d24f05078f0a7;p=platform%2Fupstream%2Fenlightenment.git e_info_client: Implement Screen Recorder (Argument name: -dump_video) Recording the screen using streamrecorder library. usage: winfo -dump_video [--help] [--rate=] [--resolution=x] --help this help text --rate= replay frame rate --resolution=x resuoltion size Change-Id: Ife2769751c485efcb509d6f0b2e6607277b98e27 --- diff --git a/configure.ac b/configure.ac index aeb22d1b49..07ff69ad5c 100755 --- a/configure.ac +++ b/configure.ac @@ -292,6 +292,7 @@ PKG_CHECK_MODULES(E_INFO, [ ecore >= ${efl_version} eldbus >= ${efl_version} xkbcommon + capi-media-streamrecorder ]) PKG_CHECK_EXISTS([xkeyboard-config],[ diff --git a/packaging/enlightenment.spec b/packaging/enlightenment.spec index ba0f5ee5af..37ac686c7c 100644 --- a/packaging/enlightenment.spec +++ b/packaging/enlightenment.spec @@ -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 diff --git a/src/bin/Makefile.mk b/src/bin/Makefile.mk index ad5117b8c1..004b816bb7 100644 --- a/src/bin/Makefile.mk +++ b/src/bin/Makefile.mk @@ -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 diff --git a/src/bin/e_info_client.c b/src/bin/e_info_client.c index 4c1838c0bd..23145c218d 100644 --- a/src/bin/e_info_client.c +++ b/src/bin/e_info_client.c @@ -1,5 +1,6 @@ #include "e.h" #include "e_info_shared_types.h" +#include "e_info_client_screen_recorder.h" #include #include #include @@ -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 index 0000000000..aee1842470 --- /dev/null +++ b/src/bin/e_info_client_screen_recorder.c @@ -0,0 +1,358 @@ +#include +#include +#include +#include +#include +#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 index 0000000000..c2b0fbe2b7 --- /dev/null +++ b/src/bin/e_info_client_screen_recorder.h @@ -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=] [--resolution=x] " \ + "\n\n" \ + "\t--help\t\t\t\tthis help text\n" \ + "\t--rate=\t\treplay frame rate (default: 60)\n" \ + "\t--resolution=x\tresuoltion size (default: 360x360)\n\n" + +int e_info_client_screen_recorder_run(int argc, char **argv); + +#endif