aboutsummaryrefslogtreecommitdiffstats
path: root/swaygrab
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2015-11-27 14:21:38 -0500
committerLibravatar Drew DeVault <sir@cmpwn.com>2015-11-27 14:21:38 -0500
commit2ef7cf9e977008f20be6072b336b0928f02b9f59 (patch)
tree4b55bf377f4220a7e02e15b8c6adf55be627e27e /swaygrab
parentImprove CONTRIBUTING.md (diff)
downloadsway-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.txt2
-rw-r--r--swaygrab/main.c112
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
13TARGET_LINK_LIBRARIES(swaygrab rt)
14
13install( 15install(
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
24void grab_and_apply_magick(const char *file, const char *output, int socketfd) { 25void 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
60void 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
50int main(int argc, char **argv) { 122int 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);