1 /*
2  * Encode reference frameserver archetype
3  * Copyright 2012-2018, Björn Ståhl
4  * License: 3-Clause BSD, see COPYING file in arcan source repository.
5  * Reference: http://arcan-fe.com
6  * Depends: FFMPEG (GPLv2,v3,LGPL)
7  */
8 
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <stdbool.h>
12 #include <stdint.h>
13 #include <unistd.h>
14 
15 #include <fcntl.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <assert.h>
19 
20 #include <libavcodec/avcodec.h>
21 #include <libavcodec/version.h>
22 #include <libavutil/opt.h>
23 #include <libavutil/imgutils.h>
24 #include <libavformat/avformat.h>
25 #include <libswscale/swscale.h>
26 #include <libswresample/swresample.h>
27 
28 #include <arcan_shmif.h>
29 #include "encode_presets.h"
30 #include "frameserver.h"
31 
32 extern void a12_serv_run(struct arg_arr*, struct arcan_shmif_cont);
33 
34 #ifdef HAVE_VNCSERVER
35 extern void vnc_serv_run(struct arg_arr*, struct arcan_shmif_cont);
36 #endif
37 
38 void png_stream_run(struct arg_arr* args, struct arcan_shmif_cont cont);
39 
40 #ifdef HAVE_OCR
41 void ocr_serv_run(struct arg_arr* args, struct arcan_shmif_cont cont);
42 #endif
43 
44 /* don't build / link to older versions */
45 #if LIBAVCODEC_VERSION_MAJOR < 54
46 	extern char* dated_ffmpeg_refused_old_build[-1];
47 #endif
48 
49 static struct {
50 /* IPC */
51 	struct arcan_shmif_cont shmcont;
52 	int last_fd;        /* sent from parent */
53 
54 /* Multiplexing / Output */
55 	AVFormatContext* fcontext;
56 	AVPacket packet;
57 	AVFrame* pframe;
58 
59 /* VIDEO */
60 /* color format conversion (ccontext) is also used for
61  * just populating the image/ color planes properly */
62 	struct SwsContext* ccontext;
63 	AVCodecContext* vcontext;
64 	AVStream* vstream;
65 	AVCodec* vcodec;
66 	int bpp;
67 	uint8_t* encvbuf;
68 
69 /* set to ~twice the size of a full frame, larger than that and
70  * we have terrible "compression" on our hands */
71 	size_t encvbuf_sz;
72 	int vpts_ofs;
73 /* used to rougly displace A/V synchronisation in encoded frames */
74 
75 /* Timing (shared) */
76 	long long starttime;       /* monotonic clock time-stamp */
77 	unsigned long framecount;  /* number of frames treated, multiply with fps */
78 	float fps;
79 
80 /* AUDIO */
81 /* containers and metadata */
82 	AVCodecContext* acontext;
83 	AVCodec* acodec;
84 	AVStream* astream;
85 	int channel_layout;
86 	int apts_ofs; /* used to roughly displace A/V
87 									 synchronisation in encoded frames */
88 	int silence_samples; /* used to dynamically drop or insert
89 													silence bytes in buffer_flush */
90 
91 /* needed for intermediate buffering and format conversion */
92 	bool float_samples;
93 
94 	uint8_t* encabuf;
95 	off_t encabuf_ofs;
96 	size_t encabuf_sz;
97 
98 /* needed for encode_audio frame settings */
99 	int aframe_smplcnt;
100 	size_t aframe_insz, aframe_sz;
101 	unsigned long aframe_ptscnt;
102 
103 /* for re-using this compilation unit from other frameservers */
104 } recctx;
105 
106 struct cl_track {
107 	unsigned conn_id;
108 };
109 
110 static bool encode_audio(bool);
111 static int encode_video(bool);
112 
stop_output()113 static void stop_output()
114 {
115 	if (recctx.last_fd == -1)
116 		return;
117 
118 	if (recctx.acontext)
119 		encode_audio(true);
120 
121 	if (recctx.vcontext)
122 		encode_video(true);
123 
124 	av_write_trailer(recctx.fcontext);
125 
126 	if (recctx.astream){
127 		LOG("(encode) closing audio stream\n");
128 		avcodec_close(recctx.astream->codec);
129 	}
130 
131 	if (recctx.vstream){
132 		LOG("(encode) closing video stream\n");
133 		avcodec_close(recctx.vstream->codec);
134 	}
135 
136 /*
137  * good form says that we should do this, have received
138  * some crashes here though.
139  	 if (!(recctx.fcontext->oformat->flags & AVFMT_NOFILE)){
140 		avio_close(recctx.fcontext->pb);
141 	}
142  */
143 
144 	avformat_free_context(recctx.fcontext);
145 	close(recctx.last_fd);
146 	recctx.last_fd = -1;
147 }
148 
149 /* flush the audio buffer present in the shared memory page as
150  * quick as possible, resample if necessary, then use the intermediate
151  * buffer to feed encoder */
flush_audbuf()152 static void flush_audbuf()
153 {
154 	size_t ntc = recctx.shmcont.addr->abufused[0];
155 	uint8_t* dataptr = (uint8_t*) recctx.shmcont.audp;
156 
157 	if (!recctx.acontext){
158 		recctx.shmcont.addr->abufused[0] = 0;
159 		return;
160 	}
161 
162 /* parent events can modify this buffer to compensate for streaming desynch,
163  * extra work for sample size alignment as shm api calculates
164  * bytes and allows truncating (terrible) */
165 	if (recctx.silence_samples > 0){ /* insert n 0- level samples */
166 		size_t nti = (recctx.silence_samples << 2) > recctx.encabuf_sz -
167 			recctx.encabuf_ofs ? recctx.encabuf_sz - recctx.encabuf_ofs :
168 			recctx.silence_samples << 2;
169 		nti = nti - (nti % 4);
170 
171 		memset(recctx.encabuf + recctx.encabuf_ofs, 0, nti);
172 		recctx.encabuf_ofs += nti;
173 		recctx.silence_samples = nti >> 2;
174 
175 	}
176 	else if (recctx.silence_samples < 0){ /* drop n samples */
177 		size_t ntd = (ntc >> 2) > recctx.silence_samples ?
178 			recctx.silence_samples << 2 : ntc;
179 		if (ntd == ntc){
180 			recctx.silence_samples -= recctx.silence_samples << 2;
181 			recctx.shmcont.addr->abufused[0] = 0;
182 			return;
183 		}
184 
185 		dataptr += ntd;
186 		ntc -= ntd;
187 	}
188 	else
189 		;
190 
191 	if (ntc + recctx.encabuf_ofs > recctx.encabuf_sz){
192 		ntc = recctx.encabuf_sz - recctx.encabuf_ofs;
193 		static bool warned;
194 		if (!warned){
195 			warned = true;
196 			printf("audio buffer overflow\n");
197 			LOG("(encode) audio buffer overflow, consider different"
198 				"	encoding options.\n");
199 		}
200 	}
201 
202 	memcpy(&recctx.encabuf[recctx.encabuf_ofs], dataptr, ntc);
203 	recctx.encabuf_ofs += ntc;
204 
205 /* worst case, we get overflown buffers and need to drop sound */
206 	recctx.shmcont.addr->abufused[0] = 00;
207 }
208 
209 /*
210  * This is somewhat ugly, a real ffmpeg expert could probably help out here --
211  * we don't actually use the resampler for resampling purposes,
212  * output encoder samplerate is forced to the same as SHMPAGE_SAMPLERATE,
213  * but the resampler API is used to convert between all *** possible
214  * expected output formats and filling out plane- alignments etc.
215  */
s16swrconv(int * size,int * nsamp)216 static uint8_t* s16swrconv(int* size, int* nsamp)
217 {
218 	static struct SwrContext* resampler = NULL;
219 	static uint8_t** resamp_outbuf = NULL;
220 
221 	if (!resampler){
222 		resampler = swr_alloc_set_opts(NULL, AV_CH_LAYOUT_STEREO,
223 			recctx.acontext->sample_fmt,
224 			recctx.acontext->sample_rate, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16,
225 			ARCAN_SHMIF_SAMPLERATE, 0, NULL);
226 
227 		resamp_outbuf = av_malloc(sizeof(uint8_t*) * ARCAN_SHMIF_ACHANNELS);
228 		av_samples_alloc(resamp_outbuf, NULL, ARCAN_SHMIF_ACHANNELS,
229 			recctx.aframe_smplcnt, recctx.acontext->sample_fmt, 0);
230 
231 		if (swr_init(resampler) < 0 ){
232 			LOG("(encode) couldn't allocate resampler, giving up.\n");
233 			exit(1);
234 		}
235 	}
236 
237 	const uint8_t* indata[] = {recctx.encabuf, NULL};
238 	int rc = swr_convert(resampler, resamp_outbuf, recctx.aframe_smplcnt,
239 		indata, recctx.aframe_smplcnt);
240 
241 	if (rc < 0){
242 		LOG("(encode) couldn't resample, giving up.\n");
243 		exit(1);
244 	}
245 
246 	*nsamp = rc;
247 	*size = av_samples_get_buffer_size(NULL, ARCAN_SHMIF_ACHANNELS, rc,
248 		recctx.acontext->sample_fmt, 0);
249 	memmove(recctx.encabuf, recctx.encabuf + recctx.aframe_insz,
250 		recctx.encabuf_ofs - recctx.aframe_insz);
251 
252 	recctx.encabuf_ofs -= recctx.aframe_insz;
253 
254 	return resamp_outbuf[0];
255 }
256 
encode_audio(bool flush)257 static bool encode_audio(bool flush)
258 {
259 	AVCodecContext* ctx = recctx.acontext;
260 	AVFrame* frame;
261 	bool forcetog = false;
262 	int got_packet = false;
263 
264 /* NOTE:
265  * for real sample-rate conversion, this test would need to
266  * reflect the state of the resampler internal buffers */
267 	if (!flush && recctx.aframe_insz > recctx.encabuf_ofs)
268 		return false;
269 
270 	AVPacket pkt = {0};
271 	av_init_packet(&pkt);
272 
273 	frame = av_frame_alloc();
274 	frame->channel_layout = ctx->channel_layout;
275 
276 	int buffer_sz;
277 	uint8_t* ptr;
278 
279 forceencode:
280 	ptr = s16swrconv(&buffer_sz, &frame->nb_samples);
281 
282 	if ( avcodec_fill_audio_frame(frame, ARCAN_SHMIF_ACHANNELS,
283 		ctx->sample_fmt, ptr, buffer_sz, 0) < 0 ){
284 		LOG("(encode) couldn't fill target audio frame.\n");
285 		exit(EXIT_FAILURE);
286 	}
287 
288 	frame->pts = recctx.aframe_ptscnt;
289 	recctx.aframe_ptscnt += frame->nb_samples;
290 
291 	int rv = avcodec_encode_audio2(ctx, &pkt, frame, &got_packet);
292 
293 	if (0 != rv && !flush){
294 		LOG("(encode) : encode_audio, couldn't encode, giving up.\n");
295 		exit(EXIT_FAILURE);
296 	}
297 
298 	if (got_packet){
299 		if (pkt.pts != AV_NOPTS_VALUE)
300 			pkt.pts = av_rescale_q(pkt.pts, ctx->time_base,
301 				recctx.astream->time_base);
302 
303 		if (pkt.dts != AV_NOPTS_VALUE)
304 			pkt.dts = av_rescale_q(pkt.dts, ctx->time_base,
305 				recctx.astream->time_base);
306 
307 /*
308  * NOTE:
309  * we might be mistreating duration both here and in video,
310  * investigate!
311  */
312 		if (pkt.duration > 0)
313 			pkt.duration = av_rescale_q(pkt.duration, ctx->time_base,
314 				recctx.astream->time_base);
315 
316 		pkt.stream_index = recctx.astream->index;
317 
318 		if (0 != av_interleaved_write_frame(recctx.fcontext, &pkt) && !flush){
319 			LOG("(encode) : encode_audio, write_frame failed, giving up.\n");
320 			exit(EXIT_FAILURE);
321 		}
322 
323 		av_freep(&frame);
324 	}
325 
326 	av_packet_unref(&pkt);
327 
328 /*
329  * for the flush case, we may have a little bit of buffers left, both in the
330  * encoder and the resampler,
331  * CODEC_CAP_DELAY = pframe can be NULL and encode audio is used to flush
332  * CODEC_CAP_SMALL_LAST_FRAME or CODEC_CAP_VARIABLE_FRAME_SIZE =
333  * we can the last few buffer bytes can be stored as well otherwise those
334  * will be discarded
335  */
336 
337 	if (flush){
338 /*
339  * setup a partial new frame with as many samples as we can fit,
340  * change the expected "frame size" to match
341  * and then re-use the encode / conversion code
342  */
343 		if (!forcetog &&
344 			((ctx->flags & AV_CODEC_CAP_SMALL_LAST_FRAME) > 0 ||
345 				(ctx->flags & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) > 0)){
346 			recctx.aframe_insz = recctx.encabuf_ofs;
347 			recctx.aframe_smplcnt = recctx.aframe_insz >> 2;
348 			frame = av_frame_alloc();
349 			frame->channel_layout = ctx->channel_layout;
350 
351 			forcetog = true;
352 			goto forceencode;
353 		}
354 
355 		if ( (ctx->flags & AV_CODEC_CAP_DELAY) > 0 ){
356 			int gotpkt;
357 			do {
358 				AVPacket flushpkt = {0};
359 				av_init_packet(&flushpkt);
360 				if (0 == avcodec_encode_audio2(ctx, &flushpkt, NULL, &gotpkt)){
361 					av_interleaved_write_frame(recctx.fcontext, &flushpkt);
362 					av_packet_unref(&flushpkt);
363 				}
364 			} while (gotpkt);
365 		}
366 
367 		return false;
368 	}
369 
370 	return true;
371 }
372 
encode_video(bool flush)373 static int encode_video(bool flush)
374 {
375 	uint8_t* srcpl[4] = {(uint8_t*)recctx.shmcont.vidp, NULL, NULL, NULL};
376 	int srcstr[4] = {recctx.shmcont.addr->w * recctx.bpp};
377 
378 /* the main problem here is that the source material may encompass many
379  * framerates, in fact, even be variable (!) the samplerate we're running
380  * with that is of interest. Thus compare the current time against the next
381  * expected time-slots, if we're running behind, just repeat the last
382  * frame N times as to not get out of synch with possible audio. */
383 	double mspf = 1000.0 / recctx.fps;
384 	long long next_frame = mspf * (double)(recctx.framecount + 1);
385 	long long frametime  = arcan_timemillis() - recctx.starttime;
386 
387 	if (frametime < next_frame - mspf * 0.5)
388 		return 0;
389 
390 	frametime -= next_frame;
391 	int fc = frametime > 0 ? floor(frametime / mspf) : 0;
392 
393 	sws_scale(recctx.ccontext, (const uint8_t* const*) srcpl, srcstr, 0,
394 		recctx.shmcont.addr->h, recctx.pframe->data, recctx.pframe->linesize);
395 
396 	AVCodecContext* ctx = recctx.vcontext;
397 	AVPacket pkt = {0};
398 	int got_outp = false;
399 
400 	av_init_packet(&pkt);
401 	recctx.pframe->pts = recctx.framecount++;
402 
403 	int rs = avcodec_encode_video2(recctx.vcontext, &pkt, flush ?
404 		NULL : recctx.pframe, &got_outp);
405 
406 	if (rs < 0 && !flush) {
407 		LOG("(encode) encode_video failed, terminating.\n");
408 		exit(EXIT_FAILURE);
409 	}
410 
411 	if (got_outp){
412 		if (pkt.pts != AV_NOPTS_VALUE)
413 			pkt.pts = av_rescale_q_rnd(pkt.pts, ctx->time_base,
414 				recctx.vstream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
415 
416 		if (pkt.dts != AV_NOPTS_VALUE)
417 			pkt.dts = av_rescale_q_rnd(pkt.dts, ctx->time_base,
418 				recctx.vstream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
419 
420 /*
421  * deprecated, seems from code inspection that the flag is set in the packet
422  * in the encoder instead
423 		if (recctx.pframe->flags ctx->coded_frame->key_frame)
424 			pkt.flags |= AV_PKT_FLAG_KEY;
425  */
426 
427 		if (pkt.dts > pkt.pts){
428 			static bool dts_warn;
429 
430 			if (!dts_warn){
431 				LOG("(encode) DTS > PTS inconsistency\n");
432 				dts_warn = true;
433 			}
434 
435 			pkt.dts = pkt.pts;
436 		}
437 
438 		pkt.duration = av_rescale_q(pkt.duration,
439 			ctx->time_base, recctx.vstream->time_base);
440 		pkt.stream_index = recctx.vstream->index;
441 
442 		if (av_interleaved_write_frame(recctx.fcontext, &pkt) != 0 && !flush){
443 			LOG("(encode) writing encoded video failed, terminating.\n");
444 			exit(EXIT_FAILURE);
445 		}
446 	}
447 
448 	av_packet_unref(&pkt);
449 	return fc;
450 }
451 
arcan_frameserver_stepframe()452 void arcan_frameserver_stepframe()
453 {
454 	static bool first_audio = false;
455 	double apts, vpts;
456 
457 	flush_audbuf();
458 
459 /* some recording sources start video before audio, to not start with
460  * bad interleaving, wait for some audio frames before start pushing video */
461 	if (!first_audio && recctx.acontext){
462 		if (recctx.encabuf_ofs > 0){
463 			first_audio = true;
464 			recctx.starttime = arcan_timemillis();
465 		}
466 
467 		goto end;
468 	}
469 
470 /* interleave audio / video */
471 	if (recctx.astream && recctx.vstream){
472 		while(1){
473 			apts = av_stream_get_end_pts(recctx.astream);
474 			vpts = av_stream_get_end_pts(recctx.vstream);
475 
476 			if (apts < vpts){
477 				if (!encode_audio(false))
478 					break;
479 			}
480 			else {
481 				if (encode_video(false) == 0)
482 					break;
483 			}
484 		}
485 	}
486 /* audio or video only */
487 	else if (recctx.astream)
488 		while (encode_audio(false));
489 	else
490 		while (encode_video(false) > 0);
491 
492 end:
493 	recctx.shmcont.addr->vready = false;
494 }
495 
encoder_atexit()496 static void encoder_atexit()
497 {
498 	if (!recctx.fcontext)
499 		return;
500 
501 	stop_output();
502 }
503 
log_callback(void * ptr,int level,const char * fmt,va_list vl)504 static void log_callback(void* ptr, int level, const char* fmt, va_list vl)
505 {
506 	vfprintf(stderr, fmt, vl);
507 }
508 
509 /*
510  * expects ccontext to be populated elsewhere
511  */
setup_ffmpeg_encode(struct arg_arr * args,int desw,int desh)512 static bool setup_ffmpeg_encode(struct arg_arr* args, int desw, int desh)
513 {
514 	struct arcan_shmif_page* shared = recctx.shmcont.addr;
515 	assert(shared);
516 
517 #ifdef _DEBUG
518 	av_log_set_level( AV_LOG_DEBUG );
519 #else
520 	av_log_set_level( AV_LOG_WARNING );
521 #endif
522 	av_log_set_callback(log_callback);
523 
524 	if (desw % 2 != 0 || desh % 2 != 0){
525 		LOG("(encode) source image format (%"PRIu16" * %"PRIu16") must be evenly"
526 		"	divisible by 2.\n", shared->w, shared->h);
527 
528 		return false;
529 	}
530 
531 /* codec stdvals, these may be overridden by the codec- options,
532  * mostly used as hints to the setup- functions from the presets.* files */
533 	unsigned vbr = 5, abr = 5, samplerate = ARCAN_SHMIF_SAMPLERATE,
534 		channels = 2, presilence = 0, bpp = 4;
535 
536 	bool noaudio = false, stream_outp = false;
537 	float fps    = 25;
538 
539 	const char (* vck) = NULL, (* ack) = NULL, (* cont) = NULL,
540 		(* streamdst) = NULL;
541 
542 	const char* val;
543 	if (arg_lookup(args, "vbitrate", 0, &val))
544 		vbr = strtoul(val, NULL, 10) * 1024;
545 	if (arg_lookup(args, "abitrate", 0, &val))
546 		abr = strtoul(val, NULL, 10) * 1024;
547 	if (arg_lookup(args, "vpreset",  0, &val)) vbr =
548 		( (vbr = strtoul(val, NULL, 10)) > 10 ? 10 : vbr);
549 	if (arg_lookup(args, "apreset",  0, &val)) abr =
550 		( (abr = strtoul(val, NULL, 10)) > 10 ? 10 : abr);
551 	if (arg_lookup(args, "fps", 0, &val)) fps = strtof(val, NULL);
552 	if (arg_lookup(args, "noaudio", 0, &val)) noaudio = true;
553 	if (arg_lookup(args, "presilence", 0, &val)) presilence =
554 		( (presilence = strtoul(val, NULL, 10)) > 0 ? presilence : 0);
555 	if (arg_lookup(args, "vptsofs", 0, &val))
556 		recctx.vpts_ofs = ( strtoul(val, NULL, 10) );
557 	if (arg_lookup(args, "aptsofs", 0, &val))
558 		recctx.apts_ofs = ( strtoul(val, NULL, 10) );
559 
560 	arg_lookup(args, "vcodec", 0, &vck);
561 	arg_lookup(args, "acodec", 0, &ack);
562 	arg_lookup(args, "container", 0, &cont);
563 
564 /* sanity- check decoded values */
565 	if (fps < 4 || fps > 60){
566 			LOG("(encode:) bad framerate (fps) argument, "
567 				"defaulting to 25.0fps\n");
568 			fps = 25;
569 	}
570 
571 	LOG("(encode) Avcodec version: %d.%d\n",
572 		LIBAVCODEC_VERSION_MAJOR, LIBAVCODEC_VERSION_MINOR);
573 	LOG("(encode:args) Parsing complete, values:\nvcodec: (%s:%f "
574 		"fps @ %d %s), acodec: (%s:%d rate %d %s), container: (%s)\n",
575 		vck?vck:"default",  fps, vbr, vbr <= 10 ? "qual.lvl" : "b/s",
576 		ack?ack:"default", samplerate, abr, abr <= 10 ? "qual.lvl" : "b/s",
577 		cont?cont:"default");
578 
579 /* overrides some of the other options to provide RDP output etc. */
580 	if (cont && strcmp(cont, "stream") == 0){
581 		avformat_network_init();
582 		stream_outp = true;
583 		cont = "stream";
584 
585 		LOG("(encode) enabled streaming output\n");
586 		if (!arg_lookup(args, "streamdst", 0, &streamdst) ||
587 			strncmp("rtmp://", streamdst, 7) != 0){
588 			LOG("(encode:args) Streaming requested, but no "
589 				"valid streamdst set, giving up.\n");
590 			return false;
591 		}
592 	}
593 
594 	struct codec_ent muxer =
595 		encode_getcontainer( cont, recctx.last_fd, streamdst);
596 	struct codec_ent video = encode_getvcodec(
597 		vck, muxer.storage.container.format->flags);
598 	struct codec_ent audio = encode_getacodec(
599 		ack, muxer.storage.container.format->flags);
600 
601 	if (!video.storage.container.context){
602 		LOG("(encode) No valid output container found, aborting.\n");
603 		return false;
604 	}
605 
606 	if (!video.storage.video.codec && !audio.storage.audio.codec){
607 		LOG("(encode) No valid video or audio setup found, aborting.\n");
608 		return false;
609 	}
610 
611 	if (video.storage.video.codec){
612 		if ( video.setup.video(&video, desw, desh, fps, vbr, stream_outp) ){
613 			recctx.encvbuf_sz = desw * desh * bpp;
614 			recctx.bpp = bpp;
615 			recctx.encvbuf = av_malloc(recctx.encvbuf_sz);
616 			recctx.vstream=avformat_new_stream(muxer.storage.container.context,NULL);
617 			recctx.vcodec = video.storage.video.codec;
618 			recctx.vcontext= video.storage.video.context;
619 			recctx.pframe = video.storage.video.pframe;
620 
621 			recctx.vstream->codec = recctx.vcontext;
622 			recctx.fps = fps;
623 			LOG("(encode) Video output stream: %d x %d %f fps\n", desw, desh, fps);
624 		}
625 	}
626 
627 	if (!noaudio && video.storage.audio.codec){
628 		if ( audio.setup.audio(&audio, channels, samplerate, abr) ){
629 			recctx.encabuf_sz = recctx.shmcont.addr->abufsize * 2;
630 			recctx.encabuf_ofs = 0;
631 			recctx.encabuf = av_malloc(recctx.encabuf_sz);
632 
633 			recctx.astream=avformat_new_stream(muxer.storage.container.context,NULL);
634 			recctx.acontext = audio.storage.audio.context;
635 			recctx.acodec = audio.storage.audio.codec;
636 			recctx.astream->codec = recctx.acontext;
637 
638 /* feeding audio encoder by this much each time,
639  * frame_size = number of samples per frame, might need to supply the
640  * encoder with a fixed amount, each sample covers n channels.
641  * aframe_sz is based on S16LE 2ch stereo as this is aligned to the INPUT data,
642  * for float conversion, we need to double afterwards
643  */
644 
645 			if ( (recctx.acodec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) > 0){
646 				recctx.aframe_smplcnt = recctx.acontext->frame_size ?
647 					recctx.acontext->frame_size : round( samplerate / fps );
648 			}
649 			else {
650 				recctx.aframe_smplcnt = recctx.acontext->frame_size;
651 			}
652 
653 			recctx.aframe_insz = recctx.aframe_smplcnt *
654 				ARCAN_SHMIF_ACHANNELS * sizeof(uint16_t);
655 			recctx.aframe_sz = recctx.aframe_smplcnt * ARCAN_SHMIF_ACHANNELS *
656 				av_get_bytes_per_sample(recctx.acontext->sample_fmt);
657 			LOG("(encode) audio: bytes per sample: %d, samples per frame: %d\n",
658 				av_get_bytes_per_sample( recctx.acontext->sample_fmt ),
659 			 	recctx.aframe_smplcnt);
660 		}
661 	}
662 
663 /* lastly, now that all streams are added, write the header */
664 	recctx.fcontext = muxer.storage.container.context;
665 	if (!muxer.setup.muxer(&muxer)){
666 		LOG("(encode) muxer setupa failed, giving up.\n");
667 		return false;
668 	}
669 
670 	if (presilence > 0 && recctx.acontext)
671 	{
672 		 presilence *= (float)ARCAN_SHMIF_SAMPLERATE / 1000.0;
673 		 if (presilence > recctx.encabuf_sz >> 2)
674 			 presilence = recctx.encabuf_sz >> 2;
675 			recctx.silence_samples = presilence;
676 	}
677 
678 	return true;
679 }
680 
dump_help()681 static void dump_help()
682 {
683 	fprintf(stdout, "Encode should be run authoritatively (spawned from arcan)\n");
684 	fprintf(stdout, "ARCAN_ARG (environment variable, "
685 		"key1=value:key2:key3=value), arguments: \n"
686 		"  key   \t   value   \t   description\n"
687 		"--------\t-----------\t-----------------\n"
688 		"protocol\t name      \t switch protocol/mode, default=video\n\n"
689 #ifdef HAVE_VNCSERVER
690 		"protocol=vnc\n"
691 		"  key   \t   value   \t   description\n"
692 		"--------\t-----------\t-----------------\n"
693 		" name   \t string    \t set exported 'desktopName'\n"
694 		" pass   \t string    \t set server password (insecure)\n"
695 		" port   \t number    \t set server listen port\n\n"
696 #endif
697 #ifdef HAVE_OCR
698 		"protocol=ocr\n"
699 		"  key   \t   value   \t   description\n"
700 		"--------\t-----------\t-----------------\n"
701 		" lang   \t string    \t set OCR engine language (default: eng)\n\n"
702 #endif
703 		"protocol=a12\n"
704 		" key    \t   value   \t   description\n"
705 		"--------\t-----------\t-----------------\n"
706 		" authk  \t key       \t set authentication pre-shared key\n"
707 		" pubk   \t b64(key)  \t allow connection from pre-authenticated public key\n"
708 		" port   \t number    \t set server listening port\n\n"
709 		"protocol=png\n"
710 		"  key   \t   value   \t   description\n"
711 		"--------\t-----------\t-----------------\n"
712 		"prefix  \t filename  \t (png) set prefix_number.png\n"
713 		"limit   \t number    \t stop after 'number' frames\n"
714 		"skip    \t number    \t skip first 'number' frames\n\n"
715 		"protocol=video\n"
716 		"  key   \t   value   \t   description\n"
717 		"----------\t-----------\t-----------------\n"
718 		"vbitrate  \t kilobits  \t nominal video bitrate\n"
719 		"abitrate  \t kilobits  \t nominal audio bitrate\n"
720 		"vpreset   \t 1..10     \t video preset quality level\n"
721 		"apreset   \t 1..10     \t audio preset quality level\n"
722 		"fps       \t float     \t targeted framerate\n"
723 		"noaudio   \t           \t ignore/omit audio encoding\n"
724 		"vptsofs   \t ms        \t delay video presentation\n"
725 		"aptsofs   \t ms        \t delay audio presentation\n"
726 		"presilence\t ms        \t buffer audio with silence\n"
727 		"vcodec    \t format    \t try to specify video codec\n"
728 		"acodec    \t format    \t try to specify audio codec\n"
729 		"container \t format    \t try to specify container format\n"
730 		"stream    \t           \t enable remote streaming\n"
731 		"streamdst \t rtmp://.. \t stream to server url\n\n"
732 	);
733 }
734 
afsrv_encode(struct arcan_shmif_cont * cont,struct arg_arr * args)735 int afsrv_encode(struct arcan_shmif_cont* cont, struct arg_arr* args)
736 {
737 	if (!args || !cont){
738 		dump_help();
739 		return EXIT_FAILURE;
740 	}
741 
742 	const char* argval;
743 	if (arg_lookup(args, "protocol", 0, &argval)){
744 
745 #ifdef HAVE_VNCSERVER
746 		if (strcmp(argval, "vnc") == 0){
747 			vnc_serv_run(args, *cont);
748 			return EXIT_SUCCESS;
749 		}
750 #endif
751 
752 		if (strcmp(argval, "a12") == 0){
753 			a12_serv_run(args, *cont);
754 		}
755 
756 #ifdef HAVE_OCR
757 		if (strcmp(argval, "ocr") == 0){
758 			ocr_serv_run(args, *cont);
759 			return EXIT_SUCCESS;
760 		}
761 #endif
762 
763 		if (strcmp(argval, "png") == 0){
764 			png_stream_run(args, *cont);
765 			return EXIT_SUCCESS;
766 		}
767 		else if (strcmp(argval, "video") == 0){
768 		}
769 		else {
770 			LOG("unsupported encoding protocol (%s) specified, giving up.\n", argval);
771 			return EXIT_FAILURE;
772 		}
773 	}
774 
775 	recctx.shmcont = *cont;
776 	bool firstframe = false;
777 	recctx.last_fd = -1;
778 	if (arg_lookup(args, "file", 0, &argval) == 0 && argval){
779 		recctx.last_fd = open(argval, O_CREAT | O_RDWR, 0600);
780 		if (-1 == recctx.last_fd){
781 			LOG("couldn't open output (%s)\n", argval);
782 			return EXIT_FAILURE;
783 		}
784 	}
785 
786 	while (true){
787 /* fail here means there's something wrong with
788  * frameserver - main app connection */
789 		arcan_event ev;
790 		if (!arcan_shmif_wait(&recctx.shmcont, &ev))
791 			break;
792 
793 		if (ev.category == EVENT_TARGET){
794 			switch (ev.tgt.kind){
795 
796 /* on the first one, we get the target for storage - but there is also the case
797  * where we get a DEVICEHINT (extend to accelerated) and then zero-copy platform
798  * handles if/where supported */
799 			case TARGET_COMMAND_STORE:
800 				recctx.last_fd = dup(ev.tgt.ioevs[0].iv);
801 				LOG("received file-descriptor, setting up encoder.\n");
802 				atexit(encoder_atexit);
803 				if (!setup_ffmpeg_encode(args, recctx.shmcont.addr->w,
804 					recctx.shmcont.addr->h))
805 					return EXIT_FAILURE;
806 				else{
807 					recctx.ccontext   = sws_getContext(
808 						recctx.shmcont.addr->w, recctx.shmcont.addr->h,
809 							SHMIF_RGBA(0,0,255,0) == 0xff ? AV_PIX_FMT_BGRA : AV_PIX_FMT_RGBA,
810 						recctx.shmcont.addr->w, recctx.shmcont.addr->h, AV_PIX_FMT_YUV420P,
811 						SWS_FAST_BILINEAR, NULL, NULL, NULL
812 					);
813 				}
814 			break;
815 
816 /* the atexit handler flushes stream buffers and finalizes output headers */
817 			case TARGET_COMMAND_EXIT:
818 				LOG("(encode) parent requested termination, quitting.\n");
819 				return EXIT_SUCCESS;
820 			break;
821 
822 			case TARGET_COMMAND_AUDDELAY:
823 				LOG("(encode) adjust audio buffering, %d milliseconds.\n",
824 					ev.tgt.ioevs[0].iv);
825 				recctx.silence_samples += (double)
826 					(ARCAN_SHMIF_SAMPLERATE / 1000.0) * ev.tgt.ioevs[0].iv;
827 			break;
828 
829 			case TARGET_COMMAND_STEPFRAME:
830 				if (!firstframe){
831 					firstframe = true;
832 					recctx.starttime = arcan_timemillis();
833 				}
834 
835 /* should practically never trigger, would require some weird OoO */
836 				while(!recctx.shmcont.addr->vready){
837 				}
838 
839 				arcan_frameserver_stepframe();
840 			break;
841 
842 			default:
843 			break;
844 			}
845 		}
846 	}
847 
848 	return EXIT_SUCCESS;
849 }
850