npv:vulkan synchronization less wrong
[nyanmp.git] / npv / local / code.frag.c
blob2220fc3fe52fa326fc56551b69375bc1994e46b0
1 /*
2 * block as much as possible.
3 * handle only async "usual" sigs, with sync signalfd.
4 * allow some signals to go thru though. many sync signal can only
5 * be handles with an async handler.
6 * always presume the process "controlling terminal" is different than the
7 * terminal connected on standard input and standard output
8 */
9 STATIC void sigs_init_once(void)
11 int r;
12 sigset_t sset;
14 r = sigfillset(&sset);
15 if (r == -1)
16 fatal("unable to get a full signal mask\n");
18 /* the "controlling terminal" line asks for a core dump, leave it be */
19 r = sigdelset(&sset, SIGQUIT);
20 if (r == -1)
21 fatal("unable to remove SIGQUIT from our signal mask\n");
23 r = pthread_sigmask(SIG_SETMASK, &sset, 0);
24 if (r != 0)
25 fatal("unable to \"block\" \"all\" signals\n");
27 /* from here, we "steal" signals with signalfd */
29 r = sigemptyset(&sset);
30 if (r == -1)
31 fatal("unable to get an empty signal mask\n");
32 /* we are asked nicely to terminate */
33 r = sigaddset(&sset, SIGTERM);
34 if (r == -1)
35 fatal("unable to add SIGTERM to our signal mask\n");
36 /* the "controlling terminal" line (^c) asks nicely to terminate */
37 r = sigaddset(&sset, SIGINT);
38 if (r == -1)
39 fatal("unable to add SIGINT to our signal mask\n");
41 r = signalfd(-1, &sset, SFD_NONBLOCK);
42 if (r == -1)
43 fatal("unable to get a signalfd file descriptor\n");
44 sig_fd_l = r;
46 /* we need a timer for the pre[fill|decode] state */
47 STATIC void pre_x_init_once(void)
49 errno = 0;
50 pre_x_timer_fd_p = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
51 if (pre_x_timer_fd_p == -1)
52 fatal("unable to get a timer file descriptor for the pre[fill|decode] states:%s\n", strerror(errno));
54 STATIC void evt_init_once(void)
56 ep_fd_p = epoll_create1(0);
57 if (ep_fd_p == -1)
58 fatal("unable to create the epoll file descriptor\n");
60 STATIC void evt_add_all_fds(void)
62 int r;
63 u8 i;
64 struct epoll_event evt;
65 /*--------------------------------------------------------------------*/
66 /* signals */
67 evt.events = EPOLLIN;
68 evt.data.fd = sig_fd_l;
69 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, sig_fd_l, &evt);
70 if (r == -1)
71 fatal("unable to add the signalfd file descriptior to the epoll file descriptor\n");
72 /*--------------------------------------------------------------------*/
73 /* pre[fill|decode] timer */
74 evt.events = EPOLLIN;
75 evt.data.fd = pre_x_timer_fd_p;
76 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, pre_x_timer_fd_p, &evt);
77 if (r == -1)
78 fatal("unable to add the pre[fill|decode] timer file descriptor\n");
79 /*--------------------------------------------------------------------*/
80 /* the video timer */
81 evt.events = EPOLLIN;
82 evt.data.fd = npv_video_timer_fd_p;
83 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, npv_video_timer_fd_p, &evt);
84 if (r == -1)
85 fatal("unable to add the video timer file descriptor\n");
86 /*--------------------------------------------------------------------*/
87 /* the x11 xcb file descriptor */
88 evt.events = EPOLLIN;
89 evt.data.fd = npv_xcb_p.fd;
90 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, npv_xcb_p.fd, &evt);
91 if (r == -1)
92 fatal("unable to add the x11 xcb file descriptor\n");
93 /*--------------------------------------------------------------------*/
94 /* alsa pcm poll file descriptors */
95 i = 0;
96 loop {
97 if (i == npv_audio_pcm_pollfds_n_p)
98 break;
99 evt.events = npv_audio_pcm_pollfds_p[i].events;
100 evt.data.fd = npv_audio_pcm_pollfds_p[i].fd;
101 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD,
102 npv_audio_pcm_pollfds_p[i].fd, &evt);
103 if (r == -1)
104 fatal("unable to add alsa poll file descriptor index %d to epoll file descriptor\n", i);
105 ++i;
107 /*--------------------------------------------------------------------*/
108 /* the draining timer */
109 evt.events = EPOLLIN;
110 evt.data.fd = npv_audio_draining_timer_fd_p;
111 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, npv_audio_draining_timer_fd_p,
112 &evt);
113 if (r == -1)
114 fatal("unable to add the draining timer file descriptor\n");
115 /*--------------------------------------------------------------------*/
116 /* the xcb screensaver heartbeat timer */
117 evt.events = EPOLLIN;
118 evt.data.fd = npv_xcb_p.screensaver_heartbeat_timer_fd;
119 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD,
120 npv_xcb_p.screensaver_heartbeat_timer_fd, &evt);
121 if (r == -1)
122 fatal("unable to add the xcb screensaver heartbeat timer file descriptor\n");
123 /*--------------------------------------------------------------------*/
124 /* the xcb mouse visibility timer */
125 evt.events = EPOLLIN;
126 evt.data.fd = npv_xcb_p.mouse_visibility_timer_fd;
127 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD,
128 npv_xcb_p.mouse_visibility_timer_fd, &evt);
129 if (r == -1)
130 fatal("unable to add the xcb screensaver heartbeat timer file descriptor\n");
132 STATIC void evt_sigs(void)
134 int r;
135 struct signalfd_siginfo siginfo;
137 /* no short reads */
138 r = read(sig_fd_l, &siginfo, sizeof(siginfo));
139 if (r != sizeof(siginfo))
140 fatal("unable to read signal information\n");
142 switch (siginfo.ssi_signo) {
143 case SIGTERM:
144 exit_ok("received SIGTERM\n");
145 case SIGINT:
146 exit_ok("received SIGINT\n");
147 default:
148 warning("signal handle:unwanted signal %d received, discarding\n", siginfo.ssi_signo);
151 STATIC void evt_accumulate(struct epoll_event *evt, bool *have_evt_sigs,
152 bool *have_evt_pcm, bool *have_evt_video, bool *have_evt_pcm_draining,
153 bool *have_evt_x11, bool *have_evt_xcb_screensaver_heartbeat,
154 bool *have_evt_xcb_mouse_visibility, bool *have_evt_pre_x)
156 u8 i;
158 if (evt->data.fd == sig_fd_l) {
159 if ((evt->events & EPOLLIN) != 0) {
160 *have_evt_sigs = true;
161 return;
163 fatal("event loop wait:signal:unexpected event\n");
165 /*-------------------------------------------------------------------*/
166 /* only update alsa fds */
167 i = 0;
168 loop {
169 if (i == npv_audio_pcm_pollfds_n_p)
170 break;
171 if (evt->data.fd == npv_audio_pcm_pollfds_p[i].fd) {
172 npv_audio_pcm_pollfds_p[i].revents = evt->events;
173 *have_evt_pcm = true;
174 return;
176 ++i;
178 /*-------------------------------------------------------------------*/
179 if (evt->data.fd == npv_xcb_p.fd) {
180 if ((evt->events & EPOLLIN) != 0) {
181 *have_evt_x11 = true;
182 return;
185 /*-------------------------------------------------------------------*/
186 if (evt->data.fd == npv_video_timer_fd_p) {
187 if ((evt->events & EPOLLIN) != 0) {
188 *have_evt_video = true;
189 return;
191 fatal("event loop wait:video:unexpected event\n");
193 /*-------------------------------------------------------------------*/
194 if (evt->data.fd == npv_audio_draining_timer_fd_p) {
195 if ((evt->events & EPOLLIN) != 0) {
196 *have_evt_pcm_draining = true;
197 return;
199 fatal("event loop wait:audio draining timer:unexpected event\n");
201 /*-------------------------------------------------------------------*/
202 if (evt->data.fd == npv_xcb_p.screensaver_heartbeat_timer_fd) {
203 if ((evt->events & EPOLLIN) != 0) {
204 *have_evt_xcb_screensaver_heartbeat = true;
205 return;
207 fatal("event loop wait:xcb screensaver heartbeat timer:unexpected event\n");
209 /*-------------------------------------------------------------------*/
210 if (evt->data.fd == npv_xcb_p.mouse_visibility_timer_fd) {
211 if ((evt->events & EPOLLIN) != 0) {
212 *have_evt_xcb_mouse_visibility = true;
213 return;
215 fatal("event loop wait:xcb mouse visibility timer:unexpected event\n");
217 /*-------------------------------------------------------------------*/
218 if (evt->data.fd == pre_x_timer_fd_p) {
219 if ((evt->events & EPOLLIN) != 0) {
220 *have_evt_pre_x = true;
221 return;
223 fatal("event loop wait:pre[fill|decode] timer:unexpected event\n");
227 * do _not_ block decoding based on a hard limit of video/audio data
228 * availability, use a soft timer
230 STATIC void predecode_wait_start(void)
232 struct itimerspec t;
233 int r;
235 /* we target ~double audio buf, namely (0.25s * 2 ~ 500ms) */
236 memset(&t, 0, sizeof(t));
237 t.it_value.tv_nsec = 500000000; /* in ns */
238 r = timerfd_settime(pre_x_timer_fd_p, 0, &t, 0);
239 if (r == -1)
240 fatal("unable to arm the predecode timer to 500 ms\n");
242 #define PREDECODE 2
243 STATIC void prefill_chk(void)
245 s64 prefill;
246 struct itimerspec t;
247 int r;
249 npv_pipeline_limits_lock();
250 prefill = npv_pipeline_limits_p.pkts.prefill.bytes_rem;
251 npv_pipeline_limits_unlock();
252 if (prefill <= 0) {
253 /* prefill is done, switch to predecode */
254 atomic_store(&pre_x_p, PREDECODE);
255 predecode_wait_start();
256 return;
258 /* arm the timer for 100 ms */
259 memset(&t, 0, sizeof(t));
260 t.it_value.tv_nsec = 100000000; /* in ns */
261 r = timerfd_settime(pre_x_timer_fd_p, 0, &t, 0);
262 if (r == -1)
263 fatal("unable to arm the prefill timer to 100 ms\n");
265 #undef PREDECODE
266 #define PREFILL 1
267 #define PREDECODE 2
268 #define NONE 0
269 STATIC void pre_x_evt(void)
271 int r;
272 uint64_t exps_n;
273 u8 pre_x;
275 /* timer ack */
276 exps_n = 0;
277 r = read(pre_x_timer_fd_p, &exps_n, sizeof(exps_n));
278 if (r == -1)
279 warning("unable to read the number of the pre[fill|decode] timer expirations\n");
281 pre_x = atomic_load(&pre_x_p);
282 if (pre_x == PREFILL) {
283 prefill_chk_do_p = true;
284 return;
285 } else if (pre_x == PREDECODE) {
286 /* predecode is done, switch to running/paused, namel no pre_x */
287 atomic_store(&pre_x_p, NONE);
288 return;
290 warning("got an spurious pre[fill|decode] timer event\n");
292 #undef PREFILL
293 #undef PREDECODE
294 #undef NONE
296 * XXX: remember that all heavy lifting should be done in other threads.
297 * this thread should not "block" or perform "expensive" work.
298 * "blocking", "expensive" work should be offloaded to other threads.
300 #define EPOLL_EVTS_N 32 /* why not */
301 #define NONE 0
302 #define PREFILL 1
303 #define PREDECODE 2
304 STATIC void evts_loop(void) { loop
306 u8 pre_x;
307 int fds_n;
308 int fd_idx;
309 struct epoll_event evts[EPOLL_EVTS_N];
310 bool have_evt_sigs;
311 bool have_evt_pcm;
312 bool have_evt_video;
313 bool have_evt_pcm_draining;
314 bool have_evt_x11;
315 bool have_evt_xcb_screensaver_heartbeat;
316 bool have_evt_xcb_mouse_visibility;
317 bool have_evt_pre_x;
318 int r;
319 short pcm_evts;
321 pre_x = atomic_load(&pre_x_p);
322 if (pre_x == PREFILL && prefill_chk_do_p) {
323 prefill_chk();
324 prefill_chk_do_p = false;
326 errno = 0;
327 memset(evts, 0, sizeof(evts));
328 fds_n = epoll_wait(ep_fd_p, evts, EPOLL_EVTS_N, -1);
329 if (fds_n == -1) {
330 if (errno == EINTR) {
331 warning("event loop wait:was interrupted by a signal\n");
332 return;
334 fatal("event loop wait:an error occured\n");
337 have_evt_sigs = false;
338 have_evt_pcm = false;
339 have_evt_video = false;
340 have_evt_pcm_draining = false;
341 have_evt_x11 = false;
342 have_evt_xcb_screensaver_heartbeat = false;
343 have_evt_xcb_mouse_visibility = false;
344 have_evt_pre_x = false;
346 fd_idx = 0;
347 loop {
348 if (fd_idx == fds_n)
349 break;
350 evt_accumulate(&evts[fd_idx], &have_evt_sigs, &have_evt_pcm,
351 &have_evt_video, &have_evt_pcm_draining,
352 &have_evt_x11, &have_evt_xcb_screensaver_heartbeat,
353 &have_evt_xcb_mouse_visibility, &have_evt_pre_x);
354 ++fd_idx;
357 /* once we have our evts, we use a sort of priority order */
359 /* process any q-ed and we-handle sigs before anything */
360 if (have_evt_sigs)
361 evt_sigs();
363 * XXX: it may be more appropriate to break this in 2 steps: key inputs
364 * (light processing), wins resizing (heavy processing)
366 /* key input and win resizing */
367 if (have_evt_x11)
368 npv_xcb_evt();
369 /* pre[fill|decode] timer evt */
370 if (have_evt_pre_x)
371 pre_x_evt();
372 /* XXX: once in audio draining mode, this should not really happen */
373 /* we are more sensitive to audio issues than video issues */
374 if (have_evt_pcm) {
376 * since alsa could use several file descriptors, only once the
377 * pollfds were properly updated we can actually know we got
378 * something from alsa
380 r = snd_pcm_poll_descriptors_revents(npv_audio_pcm_p,
381 npv_audio_pcm_pollfds_p, npv_audio_pcm_pollfds_n_p,
382 &pcm_evts);
383 if (r != 0)
384 fatal("alsa:error processing the poll file descriptors\n");
385 /* XXX: you can get POLLERR if in XRUN, will be handled later */
386 if ((pcm_evts & POLLOUT) != 0)
387 npv_audio_evt_pcm_write();
389 if (have_evt_video)
390 npv_video_timer_evt();
391 /* while audio is draining, video fr may need to be displayed */
392 if (have_evt_pcm_draining)
393 npv_audio_draining_state_evt();
394 if (have_evt_xcb_screensaver_heartbeat)
395 npv_xcb_screensaver_heartbeat_timer_evt();
396 if (have_evt_xcb_mouse_visibility)
397 npv_xcb_mouse_visibility_timer_evt();
399 #undef EPOLL_EVTS_N
400 #undef NONE
401 #undef PREFILL
402 #undef PREDECODE
403 STATIC void ff_log_stdout(void *a, int b, const char *fmt, va_list ap)
405 vprintf(fmt, ap);
407 struct ff_supported_audio_fr_fmt_t {
408 u8 *str;
409 enum avutil_audio_fr_fmt_t audio_fr_fmt;
411 /* this is the intersection of ff audio fr fmt and alsa pcm fmt */
412 STATIC struct ff_supported_audio_fr_fmt_t ff_supported_audio_fr_fmts[] = {
413 {"u8", AVUTIL_AUDIO_FR_FMT_U8},
414 {"u8planar", AVUTIL_AUDIO_FR_FMT_U8P},
415 {"s16", AVUTIL_AUDIO_FR_FMT_S16},
416 {"s16planar", AVUTIL_AUDIO_FR_FMT_S16P},
417 {"s32", AVUTIL_AUDIO_FR_FMT_S32},
418 {"s32planar", AVUTIL_AUDIO_FR_FMT_S32P},
419 {"float32", AVUTIL_AUDIO_FR_FMT_FLT},
420 {"float32planar", AVUTIL_AUDIO_FR_FMT_FLTP},
421 {"float64", AVUTIL_AUDIO_FR_FMT_DBL},
422 {"float64planar", AVUTIL_AUDIO_FR_FMT_DBLP},
423 {0,0}
425 STATIC void usage(void)
427 struct ff_supported_audio_fr_fmt_t *fmt;
429 pout("\
430 npv [-f send a fullscreen message to the window manager at start]\n\
431 [-l live mode (seek/pause are disabled)]\n\
432 [-p alsa pcm]\n\
433 [-fc override initial ffmpeg count of channels used to approximate the alsa\n\
434 pcm configuration]\n\
435 [-fr override initial ffmpeg rate(hz) used to approximate the alsa pcm\n\
436 configuration]\n\
437 [-ff override initial ffmpeg audio frame format used to approximate the alsa\n\
438 pcm configuration, see below for a list]\n\
439 [-v volume(0..100)]\n\
440 [-h window height in pixels]\n\
441 [-w window width in pixels]\n\
442 [-b packet buffer prefill wait(0..100)]\n\
443 [-help]\n\
444 url\n\
446 the ffmpeg audio frame formats which intersect alsa pcm audio formats are:\n"
448 fmt = ff_supported_audio_fr_fmts;
449 loop {
450 if (fmt->str == 0)
451 break;
452 pout("\t%s\n", fmt->str);
453 ++fmt;
456 STATIC void opts_parse(int argc, u8 **args, u8 **url, bool *live,
457 bool* start_fullscreen, u16 *w, u16 *h, u8 **pcm_str,
458 int *override_initial_ff_chans_n, int *override_initial_ff_rate,
459 enum avutil_audio_fr_fmt_t *override_initial_ff_audio_fr_fmt,
460 double *vol, u8 *pkts_prefill_percent)
462 int i;
463 int url_idx;
465 i = 1;
466 url_idx = -1;
467 loop {
468 if (i == argc)
469 break;
470 if (strcmp("-f", args[i]) == 0) {
471 *start_fullscreen = true;
472 ++i;
473 } else if (strcmp("-l", args[i]) == 0) {
474 *live = true;
475 ++i;
476 } else if (strcmp("-v", args[i]) == 0) {
477 unsigned long vol_ul;
479 if ((i + 1) == argc) {
480 perr("-v:initial volume is missing\n");
481 usage();
482 exit(1);
484 vol_ul = strtoul(args[i + 1], 0, 10);
485 if (vol_ul < 0 || 100 < vol_ul)
486 fatal("-v:invalid volume value %lu (0..100)\n", vol_ul);
487 *vol = (double)vol_ul / 100.;
488 pout("-v:using initial volume %f\n", *vol);
489 i += 2;
490 } else if (strcmp("-h", args[i]) == 0) {
491 unsigned long h_ul;
493 if ((i + 1) == argc) {
494 perr("-h:initial window pixel height is missing\n");
495 usage();
496 exit(1);
498 h_ul = strtoul(args[i + 1], 0, 10);
499 if (h_ul == 0 || h_ul > U16_MAX)
500 fatal("-h:invalid window pixel height %lu (1..%lu)\n", h_ul, U16_MAX);
501 *h = (u16)h_ul;
502 pout("-h:using initial window height %lu pixels\n", h_ul);
503 i += 2;
504 } else if (strcmp("-w", args[i]) == 0) {
505 unsigned long w_ul;
507 if ((i + 1) == argc) {
508 perr("-h:initial window pixel width is missing\n");
509 usage();
510 exit(1);
512 w_ul = strtoul(args[i + 1], 0, 10);
513 if (w_ul == 0 || w_ul > U16_MAX)
514 fatal("-w:invalid window pixel width %lu (1..%lu)\n", w_ul, U16_MAX);
515 *w = (u16)w_ul;
516 pout("-w:using initial window width %lu pixels\n", w_ul);
517 i += 2;
518 } else if (strcmp("-b", args[i]) == 0) {
519 if ((i + 1) == argc) {
520 perr("-b:percent value for prefill of buffer of packet queues is missing\n");
521 usage();
522 exit(1);
524 *pkts_prefill_percent = (u8)strtoul(args[i + 1], 0, 10);
525 pout("-v:using a %u prefilled buffer of packet queues\n", *pkts_prefill_percent);
526 i += 2;
527 } else if (strcmp("-p", args[i]) == 0) {
528 *pcm_str = args[i + 1];
529 pout("-p:alsa pcm \"%s\"\n", *pcm_str);
530 i += 2;
531 /*------------------------------------------------------------*/
532 /* ff initial override for alsa pcm cfg -- start */
533 } else if (strcmp("-fr", args[i]) == 0) {
534 if ((i + 1) == argc) {
535 perr("-fr:override initial ffmpeg rate(hz) is missing\n");
536 usage();
537 exit(1);
539 *override_initial_ff_rate = (int)strtol(args[i + 1], 0,
540 10);
541 pout("-fr:override initial ffmpeg audio rate to %dHz used for alsa pcm configuration\n", *override_initial_ff_rate);
542 i += 2;
543 } else if (strcmp("-fc", args[i]) == 0) {
544 if ((i + 1) == argc) {
545 perr("-fc:override initial ffmpeg channel count is missing\n");
546 usage();
547 exit(1);
549 *override_initial_ff_chans_n = (int)strtol(args[i + 1],
550 0, 10);
551 pout("-fc:override initial ffmpeg count of channels to %d used for alsa pcm configuration\n", *override_initial_ff_chans_n);
552 i += 2;
553 } else if (strcmp("-ff", args[i]) == 0) {
554 struct ff_supported_audio_fr_fmt_t *fmt;
556 if ((i + 1) == argc) {
557 perr("-fc:override initial ffmpeg audio frame format is missing\n");
558 usage();
559 exit(1);
561 fmt = ff_supported_audio_fr_fmts;
562 loop {
563 if (fmt->str == 0) {
564 perr("-ff:unknown ffmpeg audio frame format\n");
565 usage();
566 exit(1);
568 if (strcmp(fmt->str, args[i + 1]) == 0) {
569 *override_initial_ff_audio_fr_fmt =
570 fmt->audio_fr_fmt;
571 break;
573 ++fmt;
575 pout("-ff:override initial ffmpeg audio frame format is %s\n", avutil_get_audio_fr_fmt_name(fmt->audio_fr_fmt));
576 i += 2;
577 /* ff initial override for alsa pcm cfg -- end */
578 /*------------------------------------------------------------*/
579 } else if (strcmp("-help", args[i]) == 0) {
580 usage();
581 exit(0);
582 } else {
583 url_idx = i;
584 ++i;
587 if (url_idx == -1) {
588 perr("missing url\n");
589 usage();
590 exit(1);
592 *url = args[url_idx];
593 pout("url-->####%s####\n", *url);
595 #define PREFILL 1
596 STATIC void states_init_once(void)
599 * this state is driving several threads, we start in PREFILL asking for
600 * a prefill chk (it is possible to be in PREFILL state without asking
601 * for a prefill chk).
603 atomic_store(&pre_x_p, PREFILL);
604 prefill_chk_do_p = true;
605 /* the following is accessed only from the main thread */
606 paused_p = false;
608 #undef PREFILL
609 #define WIDTH_NOT_DEFINED 0
610 #define HEIGHT_NOT_DEFINED 0
611 STATIC void init_once(u8 *url, bool live, bool start_fullscreen,
612 double initial_vol, u16 win_width, u16 win_height, u8 *pcm_str,
613 avcodec_params_t **audio_codec_params,
614 avcodec_params_t **video_codec_params,
615 u8 **faces)
617 avutil_rational_t *audio_st_tb;
618 avutil_rational_t *video_st_tb;
620 live_p = live;
621 states_init_once();
622 evt_init_once();
623 sigs_init_once();
624 pre_x_init_once();
625 npv_vk_init_once(); /* generic plumbing */
626 npv_audio_filt_init_once(initial_vol);
627 /* before audio_st_idx_p is actually used */
628 npv_audio_init_once(pcm_str);
629 npv_video_init_once(); /* before video_st_idx_p is actually used */
630 npv_video_osd_init_once(faces);
631 npv_clk_init_once();
632 npv_pipeline_init_once();
634 npv_fmt_init_once(url);
635 /* we need something to start with */
636 npv_fmt_probe_best_sts(
637 &npv_video_st_p.idx, &npv_video_st_p.id, &video_st_tb,
638 &npv_video_st_p.start_time, video_codec_params,
639 &npv_audio_st_p.idx, &npv_audio_st_p.id, &audio_st_tb,
640 &npv_audio_st_p.start_time, audio_codec_params);
641 memcpy(&npv_audio_st_p.tb, audio_st_tb, sizeof(*audio_st_tb));
642 memcpy(&npv_video_st_p.tb, video_st_tb, sizeof(*video_st_tb));
643 if (win_width == WIDTH_NOT_DEFINED)
644 win_width = (*video_codec_params)->width;
645 if (win_height == HEIGHT_NOT_DEFINED)
646 win_height = (*video_codec_params)->height;
647 npv_xcb_init_once(win_width, win_height, start_fullscreen);
648 npv_vk_surf_init_once(npv_xcb_p.c, npv_xcb_p.win_id);
650 #undef WIDTH_NOT_DEFINED
651 #undef HEIGHT_NOT_DEFINED
652 STATIC void prepare(double initial_vol, int override_initial_ff_chans_n,
653 int override_initial_ff_rate,
654 enum avutil_audio_fr_fmt_t override_initial_ff_fmt,
655 u8 pkts_prefill_percent, avcodec_params_t *audio_codec_params,
656 avcodec_params_t *video_codec_params)
658 npv_pipeline_limits_reset();
659 npv_pipeline_prefill_reset(pkts_prefill_percent);
660 npv_audio_dec_ctx_cfg(audio_codec_params);
661 npv_video_dec_ctx_cfg(video_codec_params);
662 npv_audio_prepare(override_initial_ff_chans_n, override_initial_ff_rate,
663 override_initial_ff_fmt);
664 evt_add_all_fds();
666 STATIC void npv_cmd_quit(void)
668 exit_ok("quit command received\n");
670 STATIC void seek_lock(void)
672 /* see lock_hierarchy file */
673 npv_pkt_q_lock(npv_video_pkt_q_p);
674 npv_video_dec_ctx_lock();
675 npv_video_dec_frs_lock();
676 npv_pkt_q_lock(npv_audio_pkt_q_p);
677 npv_audio_dec_ctx_lock();
678 npv_audio_dec_sets_lock();
679 npv_pipeline_limits_lock();
680 npv_fmt_ctx_lock();
682 STATIC void seek_unlock(void)
684 /* see lock_hierarchy file */
685 npv_fmt_ctx_unlock();
686 npv_pipeline_limits_unlock();
687 npv_audio_dec_sets_unlock();
688 npv_audio_dec_ctx_unlock();
689 npv_pkt_q_unlock(npv_audio_pkt_q_p);
690 npv_video_dec_frs_unlock();
691 npv_video_dec_ctx_unlock();
692 npv_pkt_q_unlock(npv_video_pkt_q_p);
694 #define TS_FROM_CLK_OK 0
695 #define PREFILL 1
696 STATIC void seek_x(s64 delta)
698 int a;
699 u8 r;
700 s64 now_audio_filt_ts;
701 avutil_rational_t audio_filt_tb;
702 s64 new_audio_filt_ts;
703 s64 new_audio_st_ts;
704 s64 now_audio_st_ts;
706 if (npv_audio_draining_p) {
707 warning("seek:audio is draining, seeking disable\n");
708 return;
710 if (paused_p) {/* we don't seek in pause */
711 warning("seek:disabled while paused\n");
712 return;
714 if (live_p) {/* we don't seek in live mode */
715 warning("seek:disabled in live mode\n");
716 return;
719 npv_thdsws_wait_for_idle(npv_video_scaler_p.ctx);
721 seek_lock();
723 r = npv_clk_get_audio_filt_ts(&now_audio_filt_ts);
724 if (r != TS_FROM_CLK_OK) {
725 warning("seek:audio:clock timestamp unavailable, ignoring command\n");
726 seek_unlock();
727 return;
729 (void)snd_pcm_drop(npv_audio_pcm_p);
730 /* XXX: a set of sts can share the same id for seeking */
731 /*--------------------------------------------------------------------*/
732 audio_filt_tb = avfilter_bufsink_tb_get(npv_audio_filt_p.abufsink_ctx);
733 new_audio_filt_ts = now_audio_filt_ts + delta * audio_filt_tb.den
734 / audio_filt_tb.num;
735 new_audio_st_ts = avutil_rescale_q(new_audio_filt_ts, audio_filt_tb,
736 npv_audio_st_p.tb);
737 pout("trying to seek to %"PRId64" audio filter time base units/%"PRId64" audio stream time base units\n", new_audio_filt_ts, new_audio_st_ts);
738 a = avformat_seek_pkt(npv_fmt_ctx_p, npv_audio_st_p.id, new_audio_st_ts,
740 if (a < 0) {
741 pout("unable to seek to %"PRId64" audio filter time base units/%"PRId64" audio stream time base units\n", new_audio_filt_ts, new_audio_st_ts);
742 goto try_restore_audio;
744 pout("global seek using audio seek to %"PRId64" audio filter time base units/%"PRId64" audio stream time base units\n", new_audio_filt_ts, new_audio_st_ts);
745 flush:
746 npv_video_dec_flush();
747 npv_audio_dec_flush();
748 npv_audio_filt_flush();
749 npv_fmt_flush();
750 npv_clk_invalidate();
751 npv_pipeline_limits_reset();
752 npv_pipeline_prefill_reset(npv_pipeline_limits_p.pkts.prefill.percent);
753 seek_unlock();
755 atomic_store(&pre_x_p, PREFILL);
756 prefill_chk_do_p = true;
757 (void)snd_pcm_prepare(npv_audio_pcm_p);
758 return;
760 try_restore_audio:
761 now_audio_st_ts = avutil_rescale_q(now_audio_filt_ts, audio_filt_tb,
762 npv_audio_st_p.tb);
763 a = avformat_seek_pkt(npv_fmt_ctx_p, npv_audio_st_p.id, now_audio_st_ts,
765 if (a < 0) /* we don't send an application error */
766 exit_ok("unable to restore audio to %"PRId64" audio filter time base units/%"PRId64" audio stream time base units\n", now_audio_filt_ts, now_audio_st_ts);
767 goto flush;
769 #undef TS_FROM_CLK_OK
770 #undef PREFILL
771 STATIC void npv_cmd_rewind(void)
773 pout("COMMAND:rewind\n");
774 seek_x(-SEEK_DELTA);
777 STATIC void npv_cmd_rewind_big(void)
779 pout("COMMAND:rewind big\n");
780 seek_x(-SEEK_DELTA_BIG);
783 STATIC void npv_cmd_fastforward(void)
785 pout("COMMAND:fastforward\n");
786 seek_x(SEEK_DELTA);
789 STATIC void npv_cmd_fastforward_big(void)
791 pout("COMMAND:fastforward big\n");
792 seek_x(SEEK_DELTA_BIG);
795 STATIC void npv_cmd_pause(void)
797 if (npv_audio_draining_p) {
798 warning("pause:audio is draining, toggling pause is disable\n");
799 return;
801 if (live_p) {
802 warning("pause:disabled in live mode\n");
803 return;
805 if (paused_p) {
806 int r;
808 pout("COMMAND:unpause\n");
809 paused_p = false;
810 npv_fmt_ctx_lock();
811 avformat_read_play(npv_fmt_ctx_p);
812 npv_fmt_ctx_unlock();
813 npv_clk_unpause();
814 npv_video_timer_start();
815 } else {
816 int r;
818 pout("COMMAND:pause\n");
819 paused_p = true;
820 npv_fmt_ctx_lock();
821 avformat_read_pause(npv_fmt_ctx_p);
822 npv_fmt_ctx_unlock();
823 npv_clk_pause();
824 npv_video_timer_slow_start();
827 STATIC void npv_cmd_vol_up(void)
829 npv_audio_filt_cmd_vol_up();
831 STATIC void npv_cmd_vol_down(void)
833 npv_audio_filt_cmd_vol_down();
835 STATIC void npv_cmd_vol_up_small(void)
837 npv_audio_filt_cmd_vol_up_small();
839 STATIC void npv_cmd_vol_down_small(void)
841 npv_audio_filt_cmd_vol_down_small();
843 STATIC void npv_cmd_mute(void)
845 npv_audio_filt_cmd_mute();
847 #define WIDTH_NOT_DEFINED 0
848 #define HEIGHT_NOT_DEFINED 0
849 #define CHANS_N_NOT_OVERRIDDEN 0
850 #define RATE_NOT_OVERRIDDEN 0
851 #define AUDIO_FR_FMT_NOT_OVERRIDDEN AVUTIL_AUDIO_FR_FMT_NONE
852 int main(int argc, u8 **args)
854 bool live;
855 bool start_fullscreen;
856 u16 win_width;
857 u16 win_height;
858 u8 *pcm_str;
859 /* audio override -- start */
861 * we could have got direct parameters for alsa, but doing only an
862 * override triggers an autoconfiguration of any missing parameters
863 * based on initial codec params. In other words, the user is not
864 * required to provide all audio params, the code will try to fit the
865 * missing ones with the initial codec params, which is the default
866 * behavior.
868 int override_initial_ff_chans_n;
869 int override_initial_ff_rate;
870 enum avutil_audio_fr_fmt_t override_initial_ff_audio_fr_fmt;
871 /* audio override -- end */
872 u8 *url;
873 double initial_vol;
874 avcodec_params_t *audio_codec_params;
875 avcodec_params_t *video_codec_params;
876 /* "turn on utf8" processing in used libs if any *AND* locale system */
877 setlocale(LC_ALL, "");
878 /* av_log_set_level(AV_LOG_VERBOSE); */
879 /* av_log_set_level(AV_LOG_DEBUG); */
880 #ifdef NPV_DEBUG
881 npv_debug_p = false;
882 #endif
883 live = false;
884 start_fullscreen = false;
885 win_width = WIDTH_NOT_DEFINED;
886 win_height = HEIGHT_NOT_DEFINED;
887 url = 0;
888 pcm_str = "default";
889 override_initial_ff_chans_n = CHANS_N_NOT_OVERRIDDEN;
890 override_initial_ff_rate = RATE_NOT_OVERRIDDEN;
891 override_initial_ff_audio_fr_fmt = AUDIO_FR_FMT_NOT_OVERRIDDEN;
892 url = 0;
893 initial_vol = 1.;
894 npv_pipeline_limits_p.pkts.prefill.percent = 0;
895 opts_parse(argc, args, &url, &live, &start_fullscreen,
896 &win_width, &win_height,
897 &pcm_str, &override_initial_ff_chans_n,
898 &override_initial_ff_rate,
899 &override_initial_ff_audio_fr_fmt,
900 &initial_vol,
901 &npv_pipeline_limits_p.pkts.prefill.percent);
902 init_once(url, live, start_fullscreen, initial_vol, win_width,
903 win_height, pcm_str, &audio_codec_params, &video_codec_params,
904 npv_faces);
905 prepare(initial_vol, override_initial_ff_chans_n,
906 override_initial_ff_rate, override_initial_ff_audio_fr_fmt,
907 npv_pipeline_limits_p.pkts.prefill.percent, audio_codec_params,
908 video_codec_params);
910 /* switch the ffmpeg log to stdout for metadata/etc dump */
911 avutil_log_set_callback(ff_log_stdout);
912 avformat_dump_fmt(npv_fmt_ctx_p, 0, url, 0);
913 avutil_log_set_callback(avutil_log_default_callback);
914 /* we are in prefill state */
915 npv_pipeline_read_thd_start();
916 npv_pipeline_audio_thd_start();
917 npv_pipeline_video_thd_start();
918 npv_video_timer_start();
919 npv_xcb_screensaver_heartbeat_timer_start();
921 evts_loop();
922 /* unreachable */
924 #undef WIDTH_NOT_DEFINED
925 #undef HEIGHT_NOT_DEFINED
926 #undef CHANS_N_NOT_OVERRIDDEN
927 #undef RATE_NOT_OVERRIDDEN
928 #undef AUDIO_FR_FMT_NOT_OVERRIDDEN