diff options
author | Drew DeVault <sir@cmpwn.com> | 2015-11-27 14:21:38 -0500 |
---|---|---|
committer | Drew DeVault <sir@cmpwn.com> | 2015-11-27 14:21:38 -0500 |
commit | 2ef7cf9e977008f20be6072b336b0928f02b9f59 (patch) | |
tree | 4b55bf377f4220a7e02e15b8c6adf55be627e27e /swaygrab | |
parent | Improve CONTRIBUTING.md (diff) | |
download | sway-2ef7cf9e977008f20be6072b336b0928f02b9f59.tar.gz sway-2ef7cf9e977008f20be6072b336b0928f02b9f59.tar.zst sway-2ef7cf9e977008f20be6072b336b0928f02b9f59.zip |
Add ffmpeg capture to swaygrab (with limitations)
This needs to be multithreaded to have any sort of realistic expectation
of performance, due to issues with syncronous I/O.
Diffstat (limited to 'swaygrab')
-rw-r--r-- | swaygrab/CMakeLists.txt | 2 | ||||
-rw-r--r-- | swaygrab/main.c | 112 |
2 files changed, 104 insertions, 10 deletions
diff --git a/swaygrab/CMakeLists.txt b/swaygrab/CMakeLists.txt index 5b47a694..8bc8ed8b 100644 --- a/swaygrab/CMakeLists.txt +++ b/swaygrab/CMakeLists.txt | |||
@@ -10,6 +10,8 @@ add_executable(swaygrab | |||
10 | ${common} | 10 | ${common} |
11 | ) | 11 | ) |
12 | 12 | ||
13 | TARGET_LINK_LIBRARIES(swaygrab rt) | ||
14 | |||
13 | install( | 15 | install( |
14 | TARGETS swaygrab | 16 | TARGETS swaygrab |
15 | RUNTIME DESTINATION bin | 17 | RUNTIME DESTINATION bin |
diff --git a/swaygrab/main.c b/swaygrab/main.c index c05f62cd..2c169017 100644 --- a/swaygrab/main.c +++ b/swaygrab/main.c | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <getopt.h> | 4 | #include <getopt.h> |
5 | #include <unistd.h> | 5 | #include <unistd.h> |
6 | #include <math.h> | 6 | #include <math.h> |
7 | #include <time.h> | ||
7 | #include "log.h" | 8 | #include "log.h" |
8 | #include "ipc-client.h" | 9 | #include "ipc-client.h" |
9 | 10 | ||
@@ -21,19 +22,28 @@ int numlen(int n) { | |||
21 | return 1; | 22 | return 1; |
22 | } | 23 | } |
23 | 24 | ||
24 | void grab_and_apply_magick(const char *file, const char *output, int socketfd) { | 25 | void grab_and_apply_magick(const char *file, const char *output, |
26 | int socketfd, int raw) { | ||
25 | uint32_t len = strlen(output); | 27 | uint32_t len = strlen(output); |
26 | char *pixels = ipc_single_command(socketfd, | 28 | char *pixels = ipc_single_command(socketfd, |
27 | IPC_SWAY_GET_PIXELS, output, &len); | 29 | IPC_SWAY_GET_PIXELS, output, &len); |
28 | uint32_t *u32pixels = (uint32_t *)(pixels + 1); | 30 | uint32_t *u32pixels = (uint32_t *)(pixels + 1); |
29 | uint32_t width = u32pixels[0]; | 31 | uint32_t width = u32pixels[0]; |
30 | uint32_t height = u32pixels[1]; | 32 | uint32_t height = u32pixels[1]; |
33 | len -= 9; | ||
31 | pixels += 9; | 34 | pixels += 9; |
32 | 35 | ||
33 | if (width == 0 || height == 0) { | 36 | if (width == 0 || height == 0) { |
34 | sway_abort("Unknown output %s.", output); | 37 | sway_abort("Unknown output %s.", output); |
35 | } | 38 | } |
36 | 39 | ||
40 | if (raw) { | ||
41 | fwrite(pixels, 1, len, stdout); | ||
42 | fflush(stdout); | ||
43 | free(pixels - 9); | ||
44 | return; | ||
45 | } | ||
46 | |||
37 | const char *fmt = "convert -depth 8 -size %dx%d+0 rgba:- -flip %s"; | 47 | const char *fmt = "convert -depth 8 -size %dx%d+0 rgba:- -flip %s"; |
38 | char *cmd = malloc(strlen(fmt) - 6 /*args*/ | 48 | char *cmd = malloc(strlen(fmt) - 6 /*args*/ |
39 | + numlen(width) + numlen(height) + strlen(file) + 1); | 49 | + numlen(width) + numlen(height) + strlen(file) + 1); |
@@ -43,13 +53,76 @@ void grab_and_apply_magick(const char *file, const char *output, int socketfd) { | |||
43 | fwrite(pixels, 1, len, f); | 53 | fwrite(pixels, 1, len, f); |
44 | fflush(f); | 54 | fflush(f); |
45 | fclose(f); | 55 | fclose(f); |
46 | free(pixels); | 56 | free(pixels - 9); |
57 | free(cmd); | ||
58 | } | ||
59 | |||
60 | void grab_and_apply_movie_magic(const char *file, const char *output, | ||
61 | int socketfd, int raw, int framerate) { | ||
62 | if (raw) { | ||
63 | sway_log(L_ERROR, "Raw capture data is not yet supported. Proceeding with ffmpeg normally."); | ||
64 | } | ||
65 | |||
66 | uint32_t len = strlen(output); | ||
67 | char *pixels = ipc_single_command(socketfd, | ||
68 | IPC_SWAY_GET_PIXELS, output, &len); | ||
69 | uint32_t *u32pixels = (uint32_t *)(pixels + 1); | ||
70 | uint32_t width = u32pixels[0]; | ||
71 | uint32_t height = u32pixels[1]; | ||
72 | pixels += 9; | ||
73 | |||
74 | if (width == 0 || height == 0) { | ||
75 | sway_abort("Unknown output %s.", output); | ||
76 | } | ||
77 | |||
78 | const char *fmt = "ffmpeg -f rawvideo -framerate %d " | ||
79 | "-video_size %dx%d -pixel_format argb " | ||
80 | "-i pipe:0 -r %d -vf vflip %s"; | ||
81 | char *cmd = malloc(strlen(fmt) - 8 /*args*/ | ||
82 | + numlen(width) + numlen(height) + numlen(framerate) * 2 | ||
83 | + strlen(file) + 1); | ||
84 | sprintf(cmd, fmt, framerate, width, height, framerate, file); | ||
85 | |||
86 | long ns = (long)(1000000000 * (1.0 / framerate)); | ||
87 | struct timespec start, finish, ts; | ||
88 | ts.tv_sec = 0; | ||
89 | |||
90 | FILE *f = popen(cmd, "w"); | ||
91 | fwrite(pixels, 1, len, f); | ||
92 | free(pixels - 9); | ||
93 | int sleep = 0; | ||
94 | while (sleep != -1) { | ||
95 | clock_gettime(CLOCK_MONOTONIC, &start); | ||
96 | len = strlen(output); | ||
97 | pixels = ipc_single_command(socketfd, | ||
98 | IPC_SWAY_GET_PIXELS, output, &len); | ||
99 | pixels += 9; | ||
100 | len -= 9; | ||
101 | |||
102 | fwrite(pixels, 1, len, f); | ||
103 | |||
104 | clock_gettime(CLOCK_MONOTONIC, &finish); | ||
105 | ts.tv_nsec = ns; | ||
106 | double fts = (double)finish.tv_sec + 1.0e-9*finish.tv_nsec; | ||
107 | double sts = (double)start.tv_sec + 1.0e-9*start.tv_nsec; | ||
108 | long diff = (fts - sts) * 1000000000; | ||
109 | sway_log(L_INFO, "%f %f %ld", sts, fts, diff); | ||
110 | ts.tv_nsec = ns - diff; | ||
111 | if (ts.tv_nsec < 0) { | ||
112 | ts.tv_nsec = 0; | ||
113 | } | ||
114 | sleep = nanosleep(&ts, NULL); | ||
115 | } | ||
116 | fflush(f); | ||
117 | |||
118 | fclose(f); | ||
47 | free(cmd); | 119 | free(cmd); |
48 | } | 120 | } |
49 | 121 | ||
50 | int main(int argc, char **argv) { | 122 | int main(int argc, char **argv) { |
51 | static int capture = 0; | 123 | static int capture = 0, raw = 0; |
52 | char *socket_path = NULL; | 124 | char *socket_path = NULL; |
125 | int framerate = 30; | ||
53 | 126 | ||
54 | init_log(L_INFO); | 127 | init_log(L_INFO); |
55 | 128 | ||
@@ -57,13 +130,15 @@ int main(int argc, char **argv) { | |||
57 | {"capture", no_argument, &capture, 'c'}, | 130 | {"capture", no_argument, &capture, 'c'}, |
58 | {"version", no_argument, NULL, 'v'}, | 131 | {"version", no_argument, NULL, 'v'}, |
59 | {"socket", required_argument, NULL, 's'}, | 132 | {"socket", required_argument, NULL, 's'}, |
133 | {"raw", no_argument, &raw, 'r'}, | ||
134 | {"rate", required_argument, NULL, 'R'}, | ||
60 | {0, 0, 0, 0} | 135 | {0, 0, 0, 0} |
61 | }; | 136 | }; |
62 | 137 | ||
63 | int c; | 138 | int c; |
64 | while (1) { | 139 | while (1) { |
65 | int option_index = 0; | 140 | int option_index = 0; |
66 | c = getopt_long(argc, argv, "cvs:", long_options, &option_index); | 141 | c = getopt_long(argc, argv, "cvs:r", long_options, &option_index); |
67 | if (c == -1) { | 142 | if (c == -1) { |
68 | break; | 143 | break; |
69 | } | 144 | } |
@@ -73,6 +148,15 @@ int main(int argc, char **argv) { | |||
73 | case 's': // Socket | 148 | case 's': // Socket |
74 | socket_path = strdup(optarg); | 149 | socket_path = strdup(optarg); |
75 | break; | 150 | break; |
151 | case 'r': | ||
152 | raw = 1; | ||
153 | break; | ||
154 | case 'c': | ||
155 | capture = 1; | ||
156 | break; | ||
157 | case 'R': // Frame rate | ||
158 | framerate = atoi(optarg); | ||
159 | break; | ||
76 | case 'v': | 160 | case 'v': |
77 | #if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE | 161 | #if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE |
78 | fprintf(stdout, "sway version %s (%s, branch \"%s\")\n", SWAY_GIT_VERSION, SWAY_VERSION_DATE, SWAY_GIT_BRANCH); | 162 | fprintf(stdout, "sway version %s (%s, branch \"%s\")\n", SWAY_GIT_VERSION, SWAY_VERSION_DATE, SWAY_GIT_BRANCH); |
@@ -91,19 +175,27 @@ int main(int argc, char **argv) { | |||
91 | } | 175 | } |
92 | } | 176 | } |
93 | 177 | ||
94 | if (optind >= argc - 1) { | 178 | char *file, *output; |
95 | sway_abort("Expected output and file on command line. See `man swaygrab`"); | 179 | if (raw) { |
180 | if (optind >= argc) { | ||
181 | sway_abort("Invalid usage. See `man swaygrab` %d %d", argc, optind); | ||
182 | } | ||
183 | output = argv[optind]; | ||
184 | } else { | ||
185 | if (optind >= argc - 1) { | ||
186 | sway_abort("Invalid usage. See `man swaygrab`"); | ||
187 | } | ||
188 | file = argv[optind + 1]; | ||
189 | output = argv[optind]; | ||
96 | } | 190 | } |
97 | 191 | ||
98 | char *file = argv[optind + 1]; | ||
99 | char *output = argv[optind]; | ||
100 | int socketfd = ipc_open_socket(socket_path); | 192 | int socketfd = ipc_open_socket(socket_path); |
101 | free(socket_path); | 193 | free(socket_path); |
102 | 194 | ||
103 | if (!capture) { | 195 | if (!capture) { |
104 | grab_and_apply_magick(file, output, socketfd); | 196 | grab_and_apply_magick(file, output, socketfd, raw); |
105 | } else { | 197 | } else { |
106 | sway_abort("Capture is not yet supported"); | 198 | grab_and_apply_movie_magic(file, output, socketfd, raw, framerate); |
107 | } | 199 | } |
108 | 200 | ||
109 | close(socketfd); | 201 | close(socketfd); |