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