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