1 /*
2  * This is an interesting hybrid / use case in its own right. Expose the encode
3  * session as a 'remote desktop' kind of scenario where the client is allowed to
4  * provide input, but at the same time is receiving A/V/E data.
5  */
6 
7 #include <arcan_shmif.h>
8 #include <arcan_shmif_server.h>
9 
10 #include "a12.h"
11 
12 #include <errno.h>
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <sys/stat.h>
16 #include <signal.h>
17 #include <netdb.h>
18 #include <poll.h>
19 #include <inttypes.h>
20 
21 #include "anet_helper.h"
22 
23 struct dispatch_data {
24 	struct arcan_shmif_cont* C;
25 	struct anet_options net_cfg;
26 	struct a12_vframe_opts video_cfg;
27 };
28 
on_client_event(struct arcan_shmif_cont * cont,int chid,struct arcan_event * ev,void * tag)29 static void on_client_event(
30 	struct arcan_shmif_cont* cont, int chid, struct arcan_event* ev, void* tag)
31 {
32 	if (!cont){
33 		return;
34 	}
35 
36 /* The client isn't 'normal' (afsrv_net for those) as such in that many of the
37  * shmif events do not make direct sense to just forward verbatim. The input
38  * model match 1:1, however, so forward those. */
39 
40 	if (ev->category == EVENT_IO){
41 		arcan_shmif_enqueue(cont, ev);
42 	}
43 }
44 
flush_av(struct a12_state * S,struct dispatch_data * data)45 static void flush_av(struct a12_state* S, struct dispatch_data* data)
46 {
47 /* so we have a video and/or audio buffer, forward that to the a12 state */
48 	a12_set_channel(S, 0);
49 
50 /* video buffer? we don't have any guarantee that there is a significant change
51  * (the dirty region yields no guarantees here), due to just how costly the enc.
52  * stage can be, run a row- row- checksum check against the dirty region */
53 	struct arcan_shmif_cont* C = data->C;
54 	size_t y_in = C->h;
55 	size_t y_out = 0;
56 
57 	if (data->C->addr->abufused[0]){
58 /* forward audio as well */
59 		data->C->addr->abufused[0] = 0;
60 	}
61 /*
62  * S, buffer, n_samples, cfg on channels and samplerate,
63  * then method to raw
64 	a12_channel_aframe(S, &(struct shmifsrv_abuffer)
65  */
66 	if (!C->addr->vready)
67 		return;
68 
69 /* this is a design flaw in the encode- stage, ideally the cached dirty in
70  * cont should be updated on the STEPFRAME - as to not expose the page layout */
71 	struct arcan_shmif_region dregion = atomic_load(&C->addr->dirty);
72 
73 /* sanity check */
74 	if (dregion.x1 > dregion.x2)
75 		dregion.x1 = 0;
76 	if (dregion.x2 > C->w)
77 		dregion.x2 = C->w;
78 
79 	if (dregion.y1 > dregion.y2)
80 		dregion.y1 = 0;
81 	if (dregion.y2 > C->h)
82 		dregion.y2 = C->h;
83 
84 	a12_channel_vframe(S, &(struct shmifsrv_vbuffer){
85 		.buffer = C->vidp,
86 		.w = C->w,
87 		.h = C->h,
88 		.pitch = C->pitch,
89 		.stride = C->stride,
90 		.region = dregion,
91 		.flags = {
92 			.subregion = true
93 		},
94 	}, data->video_cfg);
95 }
96 
process_shmif(struct a12_state * S,struct dispatch_data * data)97 static void process_shmif(struct a12_state* S, struct dispatch_data* data)
98 {
99 	arcan_event ev;
100 	while(arcan_shmif_poll(data->C, &ev) > 0){
101 		if (ev.category != EVENT_TARGET)
102 			continue;
103 
104 		switch(ev.tgt.kind){
105 
106 /* this is a good testing basis for adaptive heavy preview- frame delivery
107  * that gets sent in advanced, and forwarded unless the main frame arrives
108  * in time. Then the SNR can be adjusted to match the quality of the link
109  * for the time being. */
110 		case TARGET_COMMAND_STEPFRAME:{
111 			flush_av(S, data);
112 			arcan_shmif_signal(data->C, SHMIF_SIGVID | SHMIF_SIGAUD);
113 		}
114 		default:
115 		break;
116 		}
117 	}
118 }
dispatch_single(struct a12_state * S,int fd,void * tag)119 static void dispatch_single(struct a12_state* S, int fd, void* tag)
120 {
121 	struct arcan_event ev;
122 	struct dispatch_data* data = tag;
123 
124 	static const short errmask = POLLERR | POLLNVAL | POLLHUP;
125 	size_t n_fd = 2;
126 	struct pollfd fds[3] = {
127 		{.fd = data->C->epipe, POLLIN | errmask},
128 		{.fd = fd, POLLIN | errmask},
129 		{.fd = fd, POLLOUT | errmask}
130 	};
131 
132 	a12_set_destination(S, data->C, 0);
133 
134 	uint8_t* outbuf = NULL;
135 	size_t outbuf_sz = 0;
136 	uint8_t inbuf[9000];
137 
138 	for(;;){
139 /* break out and clean-up on pollset error */
140 		if (
141 				((-1 == poll(fds, n_fd, -1) && (errno != EAGAIN || errno != EINTR))) ||
142 				(fds[0].revents & errmask) || (fds[1].revents & errmask)
143 		){
144 				break;
145 		}
146 
147 /* shmif takes priority */
148 		if (fds[0].revents){
149 			process_shmif(S, data);
150 		}
151 
152 /* incoming data? update a12 state machine */
153 		if (fds[1].revents){
154 			ssize_t nr = recv(fd, inbuf, 9000, 0);
155 
156 /* half-open client closed? or other error? give up */
157 			if ((-1 == nr && errno != EAGAIN &&
158 				errno != EWOULDBLOCK && errno != EINTR) || 0 == nr){
159 				break;
160 			}
161 
162 /* populate state buffer, refill if needed */
163 			a12_unpack(S, inbuf, nr, NULL, on_client_event);
164 		}
165 
166 		if (!outbuf_sz)
167 			outbuf_sz = a12_flush(S, &outbuf, A12_FLUSH_ALL);
168 
169 /* flush output buffer */
170 		if (n_fd == 3 && (fds[2].revents & POLLOUT) && outbuf_sz){
171 			ssize_t nw = write(fd, outbuf, outbuf_sz);
172 			if (nw > 0){
173 				outbuf += nw;
174 				outbuf_sz -= nw;
175 			}
176 		}
177 
178 /* update pollset if there is something to write, this will cause the next
179  * poll to bring us into the flush stage */
180 		n_fd = outbuf_sz > 0 ? 3 : 2;
181 	}
182 
183 /* always clean-up the client socket */
184 	shutdown(fd, SHUT_RDWR);
185 	close(fd);
186 }
187 
decode_args(struct arg_arr * arg,struct dispatch_data * dst)188 static bool decode_args(struct arg_arr* arg, struct dispatch_data* dst)
189 {
190 	dst->net_cfg.port = "6680";
191 
192 	if (arg_lookup(arg, "port", 0, &dst->net_cfg.port)){
193 		if (!dst->net_cfg.port || strlen(dst->net_cfg.port) == 0){
194 			arcan_shmif_last_words(dst->C, "missing port value");
195 			return false;
196 		}
197 	}
198 
199 	dst->net_cfg.opts = a12_sensitive_alloc(sizeof(struct a12_context_options));
200 
201 /* optional listening interface */
202 	arg_lookup(arg, "host", 0, &dst->net_cfg.host);
203 
204 	const char* pass;
205 	if (arg_lookup(arg, "pass", 0, &pass)){
206 		if (!pass){
207 			arcan_shmif_last_words(dst->C, "missing pass key");
208 			free(dst->net_cfg.opts);
209 			return false;
210 		}
211 		size_t len = strlen(pass);
212 
213 /* empty length is allowed */
214 		if (len && len > 32){
215 			arcan_shmif_last_words(dst->C, "password is too long");
216 			free(dst->net_cfg.opts);
217 			return false;
218 		}
219 		memcpy(dst->net_cfg.opts->secret, pass, len);
220 	}
221 
222 	dst->video_cfg = (struct a12_vframe_opts){
223 		.method = VFRAME_METHOD_DZSTD,
224 		.bias = VFRAME_BIAS_QUALITY
225 	};
226 
227 	const char* method;
228 	if (arg_lookup(arg, "vcodec", 0, &method) && method){
229 		if (strcasecmp(method, "h264") == 0){
230 			dst->video_cfg.method = VFRAME_METHOD_H264;
231 		}
232 		else if (strcasecmp(method, "raw") == 0){
233 			dst->video_cfg.method = VFRAME_METHOD_RAW_NOALPHA;
234 		}
235 		else if (strcasecmp(method, "raw565") == 0){
236 			dst->video_cfg.method = VFRAME_METHOD_RAW_RGB565;
237 		}
238 		else if (strcasecmp(method, "dpng") == 0){
239 /* no-op, default */
240 		}
241 		else
242 			LOG("unknown vcodec: %s\n", method);
243 	}
244 
245 	const char* bias;
246 	if (arg_lookup(arg, "vcodec_bias", 0, &bias) && bias){
247 		if (strcasecmp(bias, "latency") == 0){
248 			dst->video_cfg.bias = VFRAME_BIAS_LATENCY;
249 		}
250 		else if (strcasecmp(bias, "balanced") == 0){
251 			dst->video_cfg.bias = VFRAME_BIAS_BALANCED;
252 		}
253 		else if (strcasecmp(bias, "quality") == 0){
254 			dst->video_cfg.bias = VFRAME_BIAS_QUALITY;
255 		}
256 		else
257 			LOG("unknown vcodec bias: %s\n", bias);
258 	}
259 
260 	const char* tmp = NULL;
261 	if (arg_lookup(arg, "trace", 0, &tmp) && tmp){
262 		long arg = strtol(tmp, NULL, 10);
263 		a12_set_trace_level(arg, stderr);
264 	}
265 
266 	return true;
267 }
268 
a12_serv_run(struct arg_arr * arg,struct arcan_shmif_cont cont)269 void a12_serv_run(struct arg_arr* arg, struct arcan_shmif_cont cont)
270 {
271 	struct dispatch_data data = {
272 		.C = &cont
273 	};
274 
275 	if (!decode_args(arg, &data)){
276 		return;
277 	}
278 
279 /*
280  * For the sake of oversimplification, use a single active client mode for
281  * the time being. The other option is the complex (multiple active clients)
282  * and the expensive (one active client, multiple observers).
283  */
284 	char* errdst;
285 	if (!anet_listen(&data.net_cfg, &errdst, dispatch_single, &data)){
286 		arcan_shmif_last_words(&cont, errdst);
287 	}
288 
289 	arcan_shmif_drop(&cont);
290 	exit(EXIT_SUCCESS);
291 }
292