npv:_reasonable_ "pedanticage" of the code
[nyanmp.git] / npv / local / code.frag.c
blob4ba09e83c5582395b330106a51e4164c20accea1
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 STATIC void prefill_wait(void) { loop
48 struct timespec wanted;
49 struct timespec rem;
50 s64 prefill_audio;
51 s64 prefill_video;
53 npv_pipeline_limits_lock();
54 prefill_audio = npv_pipeline_limits_p.pkts.prefill.audio_bytes_rem;
55 prefill_video = npv_pipeline_limits_p.pkts.prefill.video_bytes_rem;
56 npv_pipeline_limits_unlock();
57 if (prefill_audio <= 0 && prefill_video <= 0)
58 break;
59 memset(&wanted, 0, sizeof(wanted));
60 memset(&rem, 0, sizeof(rem));
61 wanted.tv_nsec = 100000000; /* 100 ms */
62 loop {
63 int r;
65 /* linux bug: cannot specify CLOCK_MONOTONIC_RAW */
66 r = clock_nanosleep(CLOCK_MONOTONIC, 0, &wanted, &rem);
67 if (r == 0)
68 break;
69 if (r != EINTR)
70 fatal("prefill wait timer failed:%d\n", r);
71 /* r == EINTR */
72 memcpy(&wanted, &rem, sizeof(wanted));
73 memset(&rem, 0, sizeof(rem));
76 STATIC void predecode_wait(void)
78 struct timespec wanted;
79 struct timespec rem;
81 memset(&wanted, 0, sizeof(wanted));
82 memset(&rem, 0, sizeof(rem));
83 /* we target ~double audio buf, namely (0.25s * 2 ~ 500ms) */
84 wanted.tv_nsec = 500000000;
85 loop {
86 int r;
88 /* linux bug: cannot specify CLOCK_MONOTONIC_RAW */
89 r = clock_nanosleep(CLOCK_MONOTONIC, 0, &wanted, &rem);
90 if (r == 0)
91 break;
92 if (r != EINTR)
93 fatal("predecoding wait timer failed:%d\n", r);
94 /* r == EINTR */
95 memcpy(&wanted, &rem, sizeof(wanted));
96 memset(&rem, 0, sizeof(rem));
99 STATIC void evt_init_once(void)
101 ep_fd_p = epoll_create1(0);
102 if (ep_fd_p == -1)
103 fatal("unable to create the epoll file descriptor\n");
105 STATIC void evt_add_all_fds(void)
107 int r;
108 u8 i;
109 struct epoll_event evt;
111 /* signals */
112 evt.events = EPOLLIN;
113 evt.data.fd = sig_fd_l;
114 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, sig_fd_l, &evt);
115 if (r == -1)
116 fatal("unable to add the signalfd file descriptior to the epoll file descriptor\n");
117 /*--------------------------------------------------------------------*/
118 /* the video timer */
119 evt.events = EPOLLIN;
120 evt.data.fd = npv_video_timer_fd_p;
121 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, npv_video_timer_fd_p, &evt);
122 if (r == -1)
123 fatal("unable to add the video timer file descriptor\n");
124 /*--------------------------------------------------------------------*/
125 /* the x11 xcb file descriptor */
126 evt.events = EPOLLIN;
127 evt.data.fd = npv_xcb_p.fd;
128 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, npv_xcb_p.fd, &evt);
129 if (r == -1)
130 fatal("unable to add the x11 xcb file descriptor\n");
131 /*--------------------------------------------------------------------*/
132 /* alsa pcm poll file descriptors */
133 i = 0;
134 loop {
135 if (i == npv_audio_pcm_pollfds_n_p)
136 break;
137 evt.events = npv_audio_pcm_pollfds_p[i].events;
138 evt.data.fd = npv_audio_pcm_pollfds_p[i].fd;
139 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD,
140 npv_audio_pcm_pollfds_p[i].fd, &evt);
141 if (r == -1)
142 fatal("unable to add alsa poll file descriptor index %d to epoll file descriptor\n", i);
143 ++i;
145 /*--------------------------------------------------------------------*/
146 /* the draining timer */
147 evt.events = EPOLLIN;
148 evt.data.fd = npv_audio_draining_timer_fd_p;
149 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, npv_audio_draining_timer_fd_p,
150 &evt);
151 if (r == -1)
152 fatal("unable to add the draining timer file descriptor\n");
153 /*--------------------------------------------------------------------*/
154 /* the xcb screensaver heartbeat timer */
155 evt.events = EPOLLIN;
156 evt.data.fd = npv_xcb_p.screensaver_heartbeat_timer_fd;
157 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD,
158 npv_xcb_p.screensaver_heartbeat_timer_fd, &evt);
159 if (r == -1)
160 fatal("unable to add the xcb screensaver heartbeat timer file descriptor\n");
161 /*--------------------------------------------------------------------*/
162 /* the xcb mouse visibility timer */
163 evt.events = EPOLLIN;
164 evt.data.fd = npv_xcb_p.mouse_visibility_timer_fd;
165 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD,
166 npv_xcb_p.mouse_visibility_timer_fd, &evt);
167 if (r == -1)
168 fatal("unable to add the xcb screensaver heartbeat timer file descriptor\n");
170 STATIC void evt_sigs(void)
172 int r;
173 struct signalfd_siginfo siginfo;
175 /* no short reads */
176 r = read(sig_fd_l, &siginfo, sizeof(siginfo));
177 if (r != sizeof(siginfo))
178 fatal("unable to read signal information\n");
180 switch (siginfo.ssi_signo) {
181 case SIGTERM:
182 exit_ok("received SIGTERM\n");
183 case SIGINT:
184 exit_ok("received SIGINT\n");
185 default:
186 warning("signal handle:unwanted signal %d received, discarding\n", siginfo.ssi_signo);
189 STATIC void evt_accumulate(struct epoll_event *evt, bool *have_evt_sigs,
190 bool *have_evt_pcm, bool *have_evt_video, bool *have_evt_pcm_draining,
191 bool *have_evt_x11, bool *have_evt_xcb_screensaver_heartbeat,
192 bool *have_evt_xcb_mouse_visibility)
194 u8 i;
196 if (evt->data.fd == sig_fd_l) {
197 if ((evt->events & EPOLLIN) != 0) {
198 *have_evt_sigs = true;
199 return;
201 fatal("event loop wait:signal:unexpected event\n");
203 /*-------------------------------------------------------------------*/
204 /* only update alsa fds */
205 i = 0;
206 loop {
207 if (i == npv_audio_pcm_pollfds_n_p)
208 break;
209 if (evt->data.fd == npv_audio_pcm_pollfds_p[i].fd) {
210 npv_audio_pcm_pollfds_p[i].revents = evt->events;
211 *have_evt_pcm = true;
212 return;
214 ++i;
216 /*-------------------------------------------------------------------*/
217 if (evt->data.fd == npv_xcb_p.fd) {
218 if ((evt->events & EPOLLIN) != 0)
219 *have_evt_x11 = true;
220 return;
222 /*-------------------------------------------------------------------*/
223 if (evt->data.fd == npv_video_timer_fd_p) {
224 if ((evt->events & EPOLLIN) != 0) {
225 *have_evt_video = true;
226 return;
228 fatal("event loop wait:video:unexpected event\n");
230 /*-------------------------------------------------------------------*/
231 if (evt->data.fd == npv_audio_draining_timer_fd_p) {
232 if ((evt->events & EPOLLIN) != 0) {
233 *have_evt_pcm_draining = true;
234 return;
236 fatal("event loop wait:audio draining timer:unexpected event\n");
238 /*-------------------------------------------------------------------*/
239 if (evt->data.fd == npv_xcb_p.screensaver_heartbeat_timer_fd) {
240 if ((evt->events & EPOLLIN) != 0) {
241 *have_evt_xcb_screensaver_heartbeat = true;
242 return;
244 fatal("event loop wait:xcb screensaver heartbeat timer:unexpected event\n");
246 /*-------------------------------------------------------------------*/
247 if (evt->data.fd == npv_xcb_p.mouse_visibility_timer_fd) {
248 if ((evt->events & EPOLLIN) != 0) {
249 *have_evt_xcb_mouse_visibility = true;
250 return;
252 fatal("event loop wait:xcb mouse visibility timer:unexpected event\n");
256 * XXX: remember that all heavy lifting should be done in other threads.
257 * this thread should not "block" or perform "expensive" work.
258 * "blocking", "expensive" work should be offloaded to other threads.
260 #define EPOLL_EVTS_N 32 /* why not */
261 STATIC void evts_loop(void)
263 int fds_n;
264 int fd_idx;
265 struct epoll_event evts[EPOLL_EVTS_N];
266 bool have_evt_sigs;
267 bool have_evt_pcm;
268 bool have_evt_video;
269 bool have_evt_pcm_draining;
270 bool have_evt_x11;
271 bool have_evt_xcb_screensaver_heartbeat;
272 bool have_evt_xcb_mouse_visibility;
273 int r;
274 short pcm_evts;
276 errno = 0;
277 memset(evts, 0, sizeof(evts));
278 fds_n = epoll_wait(ep_fd_p, evts, EPOLL_EVTS_N, -1);
279 if (fds_n == -1) {
280 if (errno == EINTR) {
281 warning("event loop wait:was interrupted by a signal\n");
282 return;
284 fatal("event loop wait:an error occured\n");
287 have_evt_sigs = false;
288 have_evt_pcm = false;
289 have_evt_video = false;
290 have_evt_pcm_draining = false;
291 have_evt_x11 = false;
292 have_evt_xcb_screensaver_heartbeat = false;
293 have_evt_xcb_mouse_visibility= false;
295 fd_idx = 0;
296 loop {
297 if (fd_idx == fds_n)
298 break;
299 evt_accumulate(&evts[fd_idx], &have_evt_sigs, &have_evt_pcm,
300 &have_evt_video, &have_evt_pcm_draining,
301 &have_evt_x11, &have_evt_xcb_screensaver_heartbeat,
302 &have_evt_xcb_mouse_visibility);
303 ++fd_idx;
306 /* once we have our evts, we use a sort of priority order */
308 /* process any q-ed and we-handle sigs before anything */
309 if (have_evt_sigs)
310 evt_sigs();
312 * XXX: it may be more appropriate to break this in 2 steps: key inputs
313 * (light processing), wins resizing (heavy processing)
315 /* key input and win resizing */
316 if (have_evt_x11)
317 npv_xcb_evt();
318 /* XXX: once in audio draining mode, this should not really happen */
319 /* we are more sensitive to audio issues than video issues */
320 if (have_evt_pcm) {
322 * since alsa could use several file descriptors, only once the
323 * pollfds were properly updated we can actually know we got
324 * something from alsa
326 r = snd_pcm_poll_descriptors_revents(npv_audio_pcm_p,
327 npv_audio_pcm_pollfds_p, npv_audio_pcm_pollfds_n_p,
328 &pcm_evts);
329 if (r != 0)
330 fatal("alsa:error processing the poll file descriptors\n");
331 if ((pcm_evts & ~POLLOUT) != 0)
332 fatal("alsa:unexpected events\n");
333 if ((pcm_evts & POLLOUT) != 0)
334 npv_audio_evt_pcm_write();
336 if (have_evt_video)
337 npv_video_timer_evt();
338 /* while audio is draining, video fr may need to be displayed */
339 if (have_evt_pcm_draining)
340 npv_audio_draining_state_evt();
341 if (have_evt_xcb_screensaver_heartbeat)
342 npv_xcb_screensaver_heartbeat_timer_evt();
343 if (have_evt_xcb_mouse_visibility)
344 npv_xcb_mouse_visibility_timer_evt();
346 #undef EPOLL_EVTS_N
347 STATIC void ff_log_stdout(void *a, int b, const char *fmt, va_list ap)
349 vprintf(fmt, ap);
351 STATIC void usage(void)
353 pout("\
354 npv [-f send a fullscreen message to the window manager at start] [-p alsa pcm]\n\
355 [-v volume(0..100)] [-h window height in pixels] [-w window width in pixels]\n\
356 [-b packet buffer prefill wait(0..100)] [-help] url\n");
358 STATIC void opts_parse(int argc, u8 **args, u8 **url, bool* start_fullscreen,
359 u16 *w, u16 *h, u8 **pcm_str, double *vol, u8 *pkts_prefill_percent)
361 int i;
362 int url_idx;
364 i = 1;
365 url_idx = -1;
366 loop {
367 if (i == argc)
368 break;
369 if (strcmp("-f", args[i]) == 0) {
370 *start_fullscreen = true;
371 ++i;
372 } else if (strcmp("-p", args[i]) == 0) {
373 *pcm_str = args[i + 1];
374 pout("-p:alsa pcm \"%s\"\n", *pcm_str);
375 i += 2;
376 } else if (strcmp("-v", args[i]) == 0) {
377 unsigned long vol_ul;
379 if ((i + 1) == argc)
380 fatal("-v:initial volume is missing\n");
381 vol_ul = strtoul(args[i + 1], 0, 10);
382 if (vol_ul < 0 || 100 < vol_ul)
383 fatal("-v:invalid volume value %lu (0..100)\n", vol_ul);
384 *vol = (double)vol_ul / 100.;
385 pout("-v:using initial volume %f\n", *vol);
386 i += 2;
387 } else if (strcmp("-h", args[i]) == 0) {
388 unsigned long h_ul;
390 if ((i + 1) == argc)
391 fatal("-h:initial window pixel height is missing\n");
392 h_ul = strtoul(args[i + 1], 0, 10);
393 if (h_ul == 0 || h_ul > U16_MAX)
394 fatal("-h:invalid window pixel height %lu (1..%lu)\n", h_ul, U16_MAX);
395 *h = (u16)h_ul;
396 pout("-h:using initial window height %lu pixels\n", h_ul);
397 i += 2;
398 } else if (strcmp("-w", args[i]) == 0) {
399 unsigned long w_ul;
401 if ((i + 1) == argc)
402 fatal("-h:initial window pixel width is missing\n");
403 w_ul = strtoul(args[i + 1], 0, 10);
404 if (w_ul == 0 || w_ul > U16_MAX)
405 fatal("-w:invalid window pixel width %lu (1..%lu)\n", w_ul, U16_MAX);
406 *w = (u16)w_ul;
407 pout("-w:using initial window width %lu pixels\n", w_ul);
408 i += 2;
409 } else if (strcmp("-b", args[i]) == 0) {
410 if ((i + 1) == argc)
411 fatal("-b:percent value for prefill of buffer of packet queues is missing\n");
412 *pkts_prefill_percent = (u8)strtoul(args[i + 1], 0, 10);
413 pout("-v:using a %u prefilled buffer of packet queues\n", *pkts_prefill_percent);
414 i += 2;
415 } else if (strcmp("-help", args[i]) == 0) {
416 usage();
417 exit(0);
418 } else {
419 url_idx = i;
420 ++i;
423 if (url_idx == -1)
424 fatal("missing url\n");
425 *url = args[url_idx];
426 pout("url-->####%s####\n", *url);
428 #define WIDTH_NOT_DEFINED 0
429 #define HEIGHT_NOT_DEFINED 0
430 STATIC void init_once(u8 *url, bool start_fullscreen, double initial_vol,
431 u16 win_width, u16 win_height, u8 *pcm_str,
432 avcodec_params_t **audio_codec_params,
433 avcodec_params_t **video_codec_params)
435 avutil_rational_t *audio_st_tb;
436 avutil_rational_t *video_st_tb;
438 evt_init_once();
439 sigs_init_once();
440 npv_vk_init_once(); /* generic plumbing */
441 npv_audio_filt_init_once(initial_vol);
442 /* before audio_st_idx_p is actually used */
443 npv_audio_init_once(pcm_str);
444 npv_video_init_once(); /* before video_st_idx_p is actually used */
445 npv_clk_init_once();
446 npv_pipeline_init_once();
448 npv_fmt_init_once(url);
449 /* we need something to start with */
450 npv_fmt_probe_best_sts(
451 &npv_video_st_p.idx, &npv_video_st_p.id, &video_st_tb,
452 &npv_video_st_p.start_time, video_codec_params,
453 &npv_audio_st_p.idx, &npv_audio_st_p.id, &audio_st_tb,
454 &npv_audio_st_p.start_time, audio_codec_params);
455 memcpy(&npv_audio_st_p.tb, audio_st_tb, sizeof(*audio_st_tb));
456 memcpy(&npv_video_st_p.tb, video_st_tb, sizeof(*video_st_tb));
457 if (win_width == WIDTH_NOT_DEFINED)
458 win_width = (*video_codec_params)->width;
459 if (win_height == HEIGHT_NOT_DEFINED)
460 win_height = (*video_codec_params)->height;
461 npv_xcb_init_once(win_width, win_height, start_fullscreen);
462 npv_vk_surf_init_once(npv_xcb_p.c, npv_xcb_p.win_id);
464 #undef WIDTH_NOT_DEFINED
465 #undef HEIGHT_NOT_DEFINED
466 #define PRINT_INFO true
467 STATIC void prepare(double initial_vol, u8 pkts_prefill_percent,
468 avcodec_params_t *audio_codec_params,
469 avcodec_params_t *video_codec_params)
471 enum avutil_audio_fr_fmt_t dst_fmt;
472 int dst_rate;
473 int dst_chans_n;
474 uint64_t dst_chans_layout;
476 npv_pipeline_limits_reset();
477 npv_pipeline_prefill_reset(pkts_prefill_percent);
479 npv_audio_dec_ctx_cfg(audio_codec_params);
480 npv_video_dec_ctx_cfg(video_codec_params);
482 * do our best to match the pcm cfg to audio ff dec output, BUT we don't
483 * expect to match it "exactly": see right below why
485 npv_audio_pcm_cfg(npv_audio_pcm_p, npv_audio_dec_ctx_p->chans_n,
486 npv_audio_dec_ctx_p->fr_rate, npv_audio_dec_ctx_p->fr_fmt);
487 /* use a ff filt to fill in the gap between the pcm and audio ff dec */
488 npv_audio_pcm2ff(npv_audio_pcm_p, &dst_fmt, &dst_rate, &dst_chans_n,
489 &dst_chans_layout, PRINT_INFO);
490 npv_audio_filt_cfg(
491 npv_audio_dec_ctx_p->fr_fmt, npv_audio_dec_ctx_p->fr_rate,
492 npv_audio_dec_ctx_p->chans_n, npv_audio_dec_ctx_p->chans_layout,
493 false, initial_vol,
494 dst_fmt, dst_rate, dst_chans_n, dst_chans_layout,
495 PRINT_INFO);
496 npv_audio_pcm_silence_bufs_cfg(npv_audio_pcm_p, PRINT_INFO);
498 evt_add_all_fds();
500 /*==== while waiting for the osd -- start ====================================*/
501 STATIC u8 *duration_estimate_to_str(enum AVDurationEstimationMethod m)
503 switch (m) {
504 case AVFMT_DURATION_FROM_PTS:
505 return "from PTS(Presentation TimeStamp)";
506 case AVFMT_DURATION_FROM_STREAM:
507 return "from stream";
508 case AVFMT_DURATION_FROM_BITRATE:
509 return "from bitrate";
510 default:
511 return "unkwown";
514 STATIC u8 *ts_to_str(int64_t ts, avutil_rational_t time_base,
515 int64_t *remaining)
517 static u8 str[sizeof("~S00:00:00.000 remains S9223372036854775807 time base units")];
518 bool is_neg;
519 int64_t hours_n;
520 int64_t mins_n;
521 int64_t secs_n;
522 int64_t msecs_n;
523 int64_t one_hour; /* in ffmpeg time_base units */
524 int64_t one_min; /* in ffmpeg time_base units */
525 int64_t one_sec; /* in ffmpeg time_base units */
526 int64_t one_msec; /* in ffmpeg time_base units */
528 if (ts < 0) {
529 ts = -ts;
530 is_neg = true;
531 } else
532 is_neg = false;
534 one_hour = INT64_C(3600) * (int64_t)time_base.den
535 / (int64_t)time_base.num;
536 one_min = INT64_C(60) * (int64_t)time_base.den
537 / (int64_t)time_base.num;
538 one_sec = (int64_t)time_base.den / (int64_t)time_base.num;
539 one_msec = one_sec / INT64_C(1000);
541 hours_n = ts / one_hour;
543 *remaining = ts % one_hour;
544 mins_n = *remaining / one_min;
546 *remaining = *remaining % one_min;
547 secs_n = *remaining / one_sec;
549 *remaining = *remaining % one_sec;
550 msecs_n = *remaining / one_msec;
552 /* account for all rounding errors */
553 *remaining = ts - (hours_n * one_hour + mins_n * one_min
554 + secs_n * one_sec + msecs_n * one_msec);
555 if (!is_neg)
556 snprintf(str, sizeof(str), "%02"PRId64":%02"PRId64":%02"PRId64".%03"PRId64, hours_n, mins_n, secs_n, msecs_n);
557 else {
558 str[0] = '-';
559 snprintf(str + 1, sizeof(str) - 1, "%02"PRId64":%02"PRId64":%02"PRId64".%03"PRId64, hours_n, mins_n, secs_n, msecs_n);
561 return str;
563 #define RED if (stdout_is_tty) pout("\x1b[38;2;255;0;0m")
564 #define GREEN if (stdout_is_tty) pout("\x1b[38;2;0;255;0m")
565 #define BLUE if (stdout_is_tty) pout("\x1b[38;2;0;0;255m")
566 #define PURPLE if (stdout_is_tty) pout("\x1b[38;2;255;0;255m")
567 #define RESTORE if (stdout_is_tty) pout("\x1b[39;49m");
568 #define TS_FROM_CLK_OK 0
569 /* we don't lock anything as an act of faith */
570 STATIC void npv_cmd_info(void)
572 int ri;
573 u8 r8;
574 s64 audio_now;
575 bool stdout_is_tty;
576 u8 *ts_str;
577 int64_t remaining;
578 u8 duration_str[sizeof("S9223372036854775807")];
580 ri = isatty(1);
581 if (ri == 0)
582 stdout_is_tty = false;
583 else
584 stdout_is_tty = true;
586 RESTORE;
587 GREEN;pout("================================================================================\n");RESTORE;
588 PURPLE;pout("%s\n", npv_fmt_ctx_p->url);RESTORE;
590 r8 = npv_clk_get_audio_st_ts(&audio_now);
591 if (r8 != TS_FROM_CLK_OK) {
592 pout("information unavailable at the time of the command due to an unavailable audio clock\n");
593 return;
595 ts_str = ts_to_str(audio_now, npv_audio_st_p.tb, &remaining);
596 RED;pout("%s", ts_str);RESTORE;
597 if (remaining != 0)
598 pout(" remaining %"PRId64" time base units", remaining);
599 else
600 pout("\n");
601 pout("\t%"PRId64" stream time base units (%d/%d seconds)\n",
602 audio_now, npv_audio_st_p.tb.num, npv_audio_st_p.tb.den);
603 BLUE;pout("--------------------------------------------------------------------------------\n");RESTORE;
604 pout("format:");
605 if (npv_fmt_ctx_p->duration == AV_NOPTS_VALUE) {
606 pout("duration is not provided\n");
607 } else {
608 snprintf(duration_str, sizeof(duration_str), "%"PRId64,
609 npv_fmt_ctx_p->duration);
610 ts_str = ts_to_str(npv_fmt_ctx_p->duration, AV_TIME_BASE_Q,
611 &remaining);
612 pout("duration=");RED;pout("%s", ts_str);RESTORE;
613 if (remaining != 0)
614 pout(" remaining %"PRId64" av_time_base units\n",
615 remaining);
616 else
617 pout("\n");
618 pout("\t%s av_time_base units (1/%d seconds)\n\testimation method is %s\n",
619 duration_str, AV_TIME_BASE, duration_estimate_to_str(
620 npv_fmt_ctx_p->duration_estimation_method));
622 BLUE;pout("--------------------------------------------------------------------------------\n");RESTORE;
623 pout("stream:audio_id=%d", npv_audio_st_p.id);
624 if (npv_fmt_ctx_p->streams[npv_audio_st_p.idx]->duration == AV_NOPTS_VALUE) {
625 pout(";duration is not provided\n");
626 } else {
627 snprintf(duration_str, sizeof(duration_str), "%"PRId64,
628 npv_fmt_ctx_p->streams[npv_audio_st_p.idx]->duration);
629 ts_str = ts_to_str(
630 npv_fmt_ctx_p->streams[npv_audio_st_p.idx]->duration,
631 npv_audio_st_p.tb, &remaining);
632 pout(";duration=");RED;pout("%s\n", ts_str);RESTORE;
633 if (remaining != 0)
634 pout(" remaining %"PRId64" stream time base units\n",
635 remaining);
636 else
637 pout("\n");
638 pout("\t%s stream time base units (%d/%d seconds)\n",
639 duration_str, npv_audio_st_p.tb.num,
640 npv_audio_st_p.tb.den);
642 BLUE;pout("--------------------------------------------------------------------------------\n");RESTORE;
643 GREEN;pout("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");RESTORE;
645 #undef TS_FROM_CLK_OK
646 #undef RED
647 #undef GREEN
648 #undef BLUE
649 #undef PURPLE
650 #undef RESTORE
651 /*==== while waiting for the osd -- end ======================================*/
652 STATIC void npv_cmd_quit(void)
654 exit_ok("quit command received\n");
656 STATIC void seek_lock(void)
658 /* fmt */
659 npv_fmt_ctx_lock();
660 /* video */
661 npv_pkt_q_lock(npv_video_pkt_q_p);
662 npv_video_dec_frs_lock();
663 npv_video_dec_ctx_lock();
664 /* audio (useless from the main thd though) */
665 npv_pkt_q_lock(npv_audio_pkt_q_p);
666 npv_audio_dec_sets_lock();
667 npv_audio_dec_ctx_lock();
669 npv_pipeline_limits_lock();
671 STATIC void seek_unlock(void)
673 npv_pipeline_limits_unlock();
675 /* audio */
676 npv_audio_dec_ctx_unlock();
677 npv_audio_dec_sets_unlock();
678 npv_pkt_q_unlock(npv_audio_pkt_q_p);
679 /* video */
680 npv_video_dec_ctx_unlock();
681 npv_video_dec_frs_unlock();
682 npv_pkt_q_unlock(npv_video_pkt_q_p);
683 /* fmt */
684 npv_fmt_ctx_unlock();
686 #define TS_FROM_CLK_OK 0
687 STATIC void seek_x(s64 delta)
689 int a;
690 u8 r;
691 s64 new_audio_ts;
692 s64 new_video_ts;
693 s64 audio_now;
694 s64 video_now;
696 if (npv_audio_draining_p) {
697 warning("seek:audio is draining, seeking disable\n");
698 return;
700 if (paused_p) /* we don't seek in pause */
701 return;
703 npv_thdsws_wait_for_idle(npv_video_scaler_p.ctx);
705 seek_lock();
707 r = npv_clk_get_audio_st_ts(&audio_now);
708 if (r != TS_FROM_CLK_OK) {
709 warning("seek:audio:clock timestamp unavailable, ignoring command\n");
710 seek_unlock();
711 return;
713 r = npv_clk_get_video_st_ts(&video_now);
714 if (r != TS_FROM_CLK_OK) {
715 warning("seek:video:clock timestamp unavailable, ignoring command\n");
716 seek_unlock();
717 return;
719 (void)snd_pcm_drop(npv_audio_pcm_p);
721 * XXX: a set of sts can share the same id for seeking. if they share
722 * the same id then the tbs should be the same.
724 /*--------------------------------------------------------------------*/
725 new_audio_ts = audio_now + delta * npv_audio_st_p.tb.den
726 / npv_audio_st_p.tb.num;
727 /* rewind capping if possible */
728 if (npv_audio_st_p.start_time != AV_NOPTS_VALUE)
729 if (new_audio_ts < npv_audio_st_p.start_time)
730 new_audio_ts = npv_audio_st_p.start_time;
731 pout("trying to seek to %"PRId64" audio stream time base units\n", new_audio_ts);
732 a = avformat_seek_pkt(npv_fmt_ctx_p, npv_audio_st_p.id, new_audio_ts,
734 if (a < 0) {
735 pout("unable to seek to %"PRId64" audio stream time base units\n", new_audio_ts);
736 goto try_restore_audio;
738 pout("audio seek to %"PRId64" audio stream time base units\n", new_audio_ts);
739 /*--------------------------------------------------------------------*/
740 new_video_ts = video_now + delta * npv_video_st_p.tb.den
741 / npv_video_st_p.tb.num;
742 /* rewind capping if possible */
743 if (npv_video_st_p.start_time != AV_NOPTS_VALUE)
744 if (new_video_ts < npv_video_st_p.start_time)
745 new_video_ts = npv_video_st_p.start_time;
746 pout("trying to seek to %"PRId64" video stream time base units\n", new_video_ts);
747 a = avformat_seek_pkt(npv_fmt_ctx_p, npv_video_st_p.id, new_video_ts,
749 if (a < 0) {
750 pout("unable to seek to %"PRId64" video stream time base units but audio was seeked)\n", new_video_ts);
751 goto try_restore_video;
753 pout("video seek to %"PRId64" video stream time base units\n", new_video_ts);
754 flush:
755 npv_video_dec_flush();
756 npv_audio_dec_flush();
757 npv_audio_filt_flush();
758 npv_fmt_flush();
759 npv_clk_invalidate();
760 npv_pipeline_limits_reset();
761 npv_pipeline_prefill_reset(npv_pipeline_limits_p.pkts.prefill.percent);
762 seek_unlock();
764 pout("prefilling audio and video buffers...");
765 prefill_wait(); /* use the lock on the pipeline limits */
766 pout("done\n");
767 pout("predecoding audio and video...");
768 predecode_wait(); /* use the lock on the pipeline limits */
769 pout("done\n");
770 (void)snd_pcm_prepare(npv_audio_pcm_p);
771 return;
773 try_restore_video:
774 a = avformat_seek_pkt(npv_fmt_ctx_p, npv_video_st_p.id, video_now, 0);
775 if (a < 0) /* we don't send an application error */
776 exit_ok("unable to restore video to %"PRId64" video stream time base units\n", video_now);
777 try_restore_audio:
778 a = avformat_seek_pkt(npv_fmt_ctx_p, npv_audio_st_p.id, audio_now, 0);
779 if (a < 0) /* we don't send an application error */
780 exit_ok("unable to restore audio to %"PRId64" audio stream time base units\n", audio_now);
781 goto flush;
783 #undef TS_FROM_CLK_OK
784 STATIC void npv_cmd_rewind(void)
786 pout("COMMAND:rewind\n");
787 seek_x(-SEEK_DELTA);
790 STATIC void npv_cmd_rewind_big(void)
792 pout("COMMAND:rewind big\n");
793 seek_x(-SEEK_DELTA_BIG);
796 STATIC void npv_cmd_fastforward(void)
798 pout("COMMAND:fastforward\n");
799 seek_x(SEEK_DELTA);
802 STATIC void npv_cmd_fastforward_big(void)
804 pout("COMMAND:fastforward big\n");
805 seek_x(SEEK_DELTA_BIG);
808 STATIC void npv_cmd_pause(void)
810 if (npv_audio_draining_p) {
811 warning("pause:audio is draining, toggling pause is disable\n");
812 return;
814 if (paused_p) {
815 int r;
817 pout("COMMAND:unpause\n");
818 paused_p = false;
819 npv_fmt_ctx_lock();
820 avformat_read_play(npv_fmt_ctx_p);
821 npv_fmt_ctx_unlock();
822 npv_clk_unpause();
823 npv_video_timer_start();
824 } else {
825 int r;
827 pout("COMMAND:pause\n");
828 paused_p = true;
829 npv_fmt_ctx_lock();
830 avformat_read_pause(npv_fmt_ctx_p);
831 npv_fmt_ctx_unlock();
832 npv_clk_pause();
833 npv_video_timer_slow_start();
836 STATIC void npv_cmd_vol_up(void)
838 npv_audio_filt_cmd_vol_up();
840 STATIC void npv_cmd_vol_down(void)
842 npv_audio_filt_cmd_vol_down();
844 STATIC void npv_cmd_mute(void)
846 npv_audio_filt_cmd_mute();
848 #define WIDTH_NOT_DEFINED 0
849 #define HEIGHT_NOT_DEFINED 0
850 int main(int argc, u8 **args)
852 bool start_fullscreen;
853 u16 win_width;
854 u16 win_height;
855 u8 *pcm_str;
856 u8 *url;
857 double initial_vol;
858 avcodec_params_t *audio_codec_params;
859 avcodec_params_t *video_codec_params;
860 /* "turn on utf8" processing in used libs if any *AND* locale system */
861 setlocale(LC_ALL, "");
862 /* av_log_set_level(AV_LOG_VERBOSE); */
863 /* av_log_set_level(AV_LOG_DEBUG); */
864 start_fullscreen = false;
865 win_width = WIDTH_NOT_DEFINED;
866 win_height = HEIGHT_NOT_DEFINED;
867 url = 0;
868 pcm_str = "default";
869 url = 0;
870 initial_vol = 1.;
871 npv_pipeline_limits_p.pkts.prefill.percent = 0;
872 opts_parse(argc, args, &url, &start_fullscreen, &win_width, &win_height,
873 &pcm_str, &initial_vol,
874 &npv_pipeline_limits_p.pkts.prefill.percent);
875 init_once(url, start_fullscreen, initial_vol, win_width, win_height,
876 pcm_str, &audio_codec_params, &video_codec_params);
877 prepare(initial_vol, npv_pipeline_limits_p.pkts.prefill.percent,
878 audio_codec_params, video_codec_params);
880 /* switch the ffmpeg log to stdout for metadata/etc dump */
881 avutil_log_set_callback(ff_log_stdout);
882 avformat_dump_fmt(npv_fmt_ctx_p, 0, url, 0);
883 avutil_log_set_callback(avutil_log_default_callback);
885 npv_pipeline_read_thd_start();
886 pout("prefilling audio and video buffers...");
887 prefill_wait();
888 pout("done\n");
889 npv_pipeline_audio_thd_start();
890 npv_pipeline_video_thd_start();
891 pout("predecoding audio and video...");
892 predecode_wait();
893 pout("done\n");
894 npv_video_timer_start();
895 npv_xcb_screensaver_heartbeat_timer_start();
897 loop evts_loop();
898 /* unreachable */
900 #undef WIDTH_NOT_DEFINED
901 #undef HEIGHT_NOT_DEFINED