npv: media pipeline
[nyanmp.git] / npv / local / code.frag.c
blob215d04414d96c0e8fb66a01b1df431da12057626
1 /* meh... */
2 /*NSPC*/
3 static u8 *ts_to_str(int64_t ts, avutil_rational_t time_base,
4 int64_t *remaining)
6 static u8 str[sizeof("~S00:00:00.000 remains S9223372036854775807 time base units")];
7 bool is_neg;
8 int64_t hours_n;
9 int64_t mins_n;
10 int64_t secs_n;
11 int64_t msecs_n;
12 int64_t one_hour; /* in ffmpeg time_base units */
13 int64_t one_min; /* in ffmpeg time_base units */
14 int64_t one_sec; /* in ffmpeg time_base units */
15 int64_t one_msec; /* in ffmpeg time_base units */
17 if (ts < 0) {
18 ts = -ts;
19 is_neg = true;
20 } else
21 is_neg = false;
23 one_hour = INT64_C(3600) * (int64_t)time_base.den
24 / (int64_t)time_base.num;
25 one_min = INT64_C(60) * (int64_t)time_base.den
26 / (int64_t)time_base.num;
27 one_sec = (int64_t)time_base.den / (int64_t)time_base.num;
28 one_msec = one_sec / INT64_C(1000);
30 hours_n = ts / one_hour;
32 *remaining = ts % one_hour;
33 mins_n = *remaining / one_min;
35 *remaining = *remaining % one_min;
36 secs_n = *remaining / one_sec;
38 *remaining = *remaining % one_sec;
39 msecs_n = *remaining / one_msec;
41 /* account for all rounding errors */
42 *remaining = ts - (hours_n * one_hour + mins_n * one_min
43 + secs_n * one_sec + msecs_n * one_msec);
44 if (!is_neg)
45 snprintf(str, sizeof(str), "%02"PRId64":%02"PRId64":%02"PRId64".%03"PRId64, hours_n, mins_n, secs_n, msecs_n);
46 else {
47 str[0] = '-';
48 snprintf(str + 1, sizeof(str) - 1, "%02"PRId64":%02"PRId64":%02"PRId64".%03"PRId64, hours_n, mins_n, secs_n, msecs_n);
50 return str;
52 /*--------------------------------------------------------------------------*/
54 * block as much as possible.
55 * handle only async "usual" sigs, with sync signalfd.
56 * allow some signals to go thru though. many sync signal can only
57 * be handles with an async handler.
58 * always presume the process "controlling terminal" is different than the
59 * terminal connected on standard input and standard output
61 /*NSPC*/
62 static void sigs_init_once(void)
64 int r;
65 sigset_t sset;
67 r = sigfillset(&sset);
68 if (r == -1)
69 FATAL("unable to get a full signal mask\n");
71 /* the "controlling terminal" line asks for a core dump, leave it be */
72 r = sigdelset(&sset, SIGQUIT);
73 if (r == -1)
74 FATAL("unable to remove SIGQUIT from our signal mask\n");
76 r = pthread_sigmask(SIG_SETMASK, &sset, 0);
77 if (r != 0)
78 FATAL("unable to \"block\" \"all\" signals\n");
80 /* from here, we "steal" signals with signalfd */
82 r = sigemptyset(&sset);
83 if (r == -1)
84 FATAL("unable to get an empty signal mask\n");
85 /* we are asked nicely to terminate */
86 r = sigaddset(&sset, SIGTERM);
87 if (r == -1)
88 FATAL("unable to add SIGTERM to our signal mask\n");
89 /* the "controlling terminal" line (^c) asks nicely to terminate */
90 r = sigaddset(&sset, SIGINT);
91 if (r == -1)
92 FATAL("unable to add SIGINT to our signal mask\n");
94 r = signalfd(-1, &sset, SFD_NONBLOCK);
95 if (r == -1)
96 FATAL("unable to get a signalfd file descriptor\n");
97 sig_fd_l = r;
99 /*NSPC*/
100 static void evt_init_once(void)
102 ep_fd_p = epoll_create1(0);
103 if (ep_fd_p == -1)
104 FATAL("unable to create the epoll file descriptor\n");
106 /*NSPC*/
107 static void evt_add_all_fds(void)
109 int r;
110 u8 i;
111 struct epoll_event evt;
113 /* signals */
114 evt.events = EPOLLIN;
115 evt.data.fd = sig_fd_l;
116 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, sig_fd_l, &evt);
117 if (r == -1)
118 FATAL("unable to add the signalfd file descriptior to the epoll file descriptor\n");
119 /*--------------------------------------------------------------------*/
120 ///* the input timer */
121 //evt.events = EPOLLIN;
122 //evt.data.fd = input_timer_fd_p;
123 //r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, input_timer_fd_p, &evt);
124 //if (r == -1)
125 // FATAL("unable to add the input timer file descriptor\n");
126 /*--------------------------------------------------------------------*/
127 /* the video timer */
128 evt.events = EPOLLIN;
129 evt.data.fd = video_timer_fd_p;
130 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, video_timer_fd_p, &evt);
131 if (r == -1)
132 FATAL("unable to add the video timer file descriptor\n");
133 /*--------------------------------------------------------------------*/
134 /* the x11 xcb file descriptor */
135 evt.events = EPOLLIN;
136 evt.data.fd = npv_xcb_p.fd;
137 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, npv_xcb_p.fd, &evt);
138 if (r == -1)
139 FATAL("unable to add the x11 xcb file descriptor\n");
140 /*--------------------------------------------------------------------*/
141 /* alsa pcm poll file descriptors */
142 i = 0;
143 loop {
144 if (i == audio_pcm_pollfds_n_p)
145 break;
146 evt.events = audio_pcm_pollfds_p[i].events;
147 evt.data.fd = audio_pcm_pollfds_p[i].fd;
148 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, audio_pcm_pollfds_p[i].fd,
149 &evt);
150 if (r == -1)
151 FATAL("unable to add alsa poll file descriptor index %d to epoll file descriptor\n", i);
152 ++i;
154 /*--------------------------------------------------------------------*/
155 /* the draining timer */
156 evt.events = EPOLLIN;
157 evt.data.fd = audio_draining_timer_fd_p;
158 r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, audio_draining_timer_fd_p, &evt);
159 if (r == -1)
160 FATAL("unable to add the draining timer file descriptor\n");
162 /*NSPC*/
163 static void evt_sigs(void)
165 int r;
166 struct signalfd_siginfo siginfo;
168 /* no short reads */
169 r = read(sig_fd_l, &siginfo, sizeof(siginfo));
170 if (r != sizeof(siginfo))
171 FATAL("unable to read signal information\n");
173 switch (siginfo.ssi_signo) {
174 case SIGTERM:
175 EXIT("received SIGTERM\n");
176 case SIGINT:
177 EXIT("received SIGINT\n");
178 default:
179 WARNING("signal handle:unwanted signal %d received, discarding\n", siginfo.ssi_signo);
182 /*NSPC*/
183 static void evt_accumulate(struct epoll_event *evt, bool *have_evt_sigs,
184 bool *have_evt_pcm, bool *have_evt_video, bool *have_evt_pcm_draining,
185 bool *have_evt_x11) //bool *have_evt_input)
187 u8 i;
189 if (evt->data.fd == sig_fd_l) {
190 if ((evt->events & EPOLLIN) != 0) {
191 *have_evt_sigs = true;
192 return;
194 FATAL("event loop wait:signal:unexpected event\n");
196 /*-------------------------------------------------------------------*/
197 /* only update alsa fds */
198 i = 0;
199 loop {
200 if (i == audio_pcm_pollfds_n_p)
201 break;
202 if (evt->data.fd == audio_pcm_pollfds_p[i].fd) {
203 audio_pcm_pollfds_p[i].revents = evt->events;
204 *have_evt_pcm = true;
205 return;
207 ++i;
209 /*-------------------------------------------------------------------*/
210 if (evt->data.fd == npv_xcb_p.fd) {
211 if ((evt->events & EPOLLIN) != 0)
212 *have_evt_x11 = true;
213 return;
215 /*-------------------------------------------------------------------*/
216 if (evt->data.fd == video_timer_fd_p) {
217 if ((evt->events & EPOLLIN) != 0) {
218 *have_evt_video = true;
219 return;
221 FATAL("event loop wait:video:unexpected event\n");
223 /*-------------------------------------------------------------------*/
224 //if (evt->data.fd == input_timer_fd_p) {
225 // if ((evt->events & EPOLLIN) != 0) {
226 // *have_evt_input = true;
227 // return;
228 // }
229 // FATAL("event loop wait:input:unexpected event\n");
231 /*-------------------------------------------------------------------*/
232 if (evt->data.fd == audio_draining_timer_fd_p) {
233 if ((evt->events & EPOLLIN) != 0) {
234 *have_evt_pcm_draining = true;
235 return;
237 FATAL("event loop wait:audio draining timer:unexpected event\n");
241 * XXX: remember that all heavy lifting should be done in other threads.
242 * this thread should not "block" or perform "expensive" work.
243 * "blocking", "expensive" work should be offloaded to other threads.
245 #define EPOLL_EVTS_N 32 /* why not */
246 /*NSPC*/
247 static void evts_loop(void)
249 int fds_n;
250 int fd_idx;
251 struct epoll_event evts[EPOLL_EVTS_N];
252 bool have_evt_sigs;
253 bool have_evt_pcm;
254 bool have_evt_video;
255 bool have_evt_pcm_draining;
256 bool have_evt_x11;
257 //bool have_evt_input;
258 int r;
259 short pcm_evts;
261 errno = 0;
262 memset(evts, 0, sizeof(evts));
263 fds_n = epoll_wait(ep_fd_p, evts, EPOLL_EVTS_N, -1);
264 if (fds_n == -1) {
265 if (errno == EINTR) {
266 WARNING("event loop wait:was interrupted by a signal\n");
267 return;
269 FATAL("event loop wait:an error occured\n");
272 have_evt_sigs = false;
273 have_evt_pcm = false;
274 have_evt_video = false;
275 have_evt_pcm_draining = false;
276 have_evt_x11 = false;
277 //have_evt_input = false;
279 fd_idx = 0;
280 loop {
281 if (fd_idx == fds_n)
282 break;
283 evt_accumulate(&evts[fd_idx], &have_evt_sigs, &have_evt_pcm,
284 &have_evt_video, &have_evt_pcm_draining,
285 &have_evt_x11);
286 //&have_evt_input);
287 ++fd_idx;
290 /* once we have our evts, we use a sort of priority order */
292 /* process any q-ed and we-handle sigs before anything */
293 if (have_evt_sigs)
294 evt_sigs();
296 * XXX: it may be more appropriate to break this in 2 steps: key inputs
297 * (light processing), wins resizing (heavy processing)
299 /* key input and win resizing */
300 if (have_evt_x11)
301 npv_xcb_evt();
302 /* XXX: once in audio draining mode, this should not really happen */
303 /* we are more sensitive to audio issues than video issues */
304 if (have_evt_pcm) {
306 * since alsa could use several file descriptors, only once the
307 * pollfds were properly updated we can actually know we got
308 * something from alsa
310 r = snd_pcm_poll_descriptors_revents(audio_pcm_p,
311 audio_pcm_pollfds_p, audio_pcm_pollfds_n_p, &pcm_evts);
312 if (r != 0)
313 FATAL("alsa:error processing the poll file descriptors\n");
315 if ((pcm_evts & ~POLLOUT) != 0)
316 FATAL("alsa:unexpected events\n");
318 if ((pcm_evts & POLLOUT) != 0)
319 audio_evt_pcm_write();
321 if (have_evt_video)
322 video_timer_evt();
323 //if (have_evt_input)
324 // input_timer_evt();
325 /* while audio is draining, video fr may need to be displayed */
326 if (have_evt_pcm_draining)
327 audio_draining_state_evt();
329 #undef EPOLL_EVTS_N
330 /*NSPC*/
331 static void ff_log_stdout(void *a, int b, const char *fmt, va_list ap)
333 vprintf(fmt, ap);
335 /*NSPC*/
336 static void usage(void)
338 POUT("\
339 npv [-p alsa pcm] [-v volume(0..100)] [-h height in pixels] [-w width in pixels]\
340 [-b packet buffer prefill wait(0..100)] [-help] url\n");
342 /*NSPC*/
343 static void opts_parse(int argc, u8 **args, u8 **url, u16 *w, u16 *h,
344 u8 **pcm_str, double *vol, u8 *pkts_prefill_percent)
346 int i;
347 int url_idx;
349 i = 1;
350 url_idx = -1;
351 loop {
352 if (i == argc)
353 break;
355 if (strcmp("-p", args[i]) == 0) {
356 unsigned long vol_ul;
358 if ((i + 1) == argc)
359 FATAL("-p:alsa pcm is missing\n");
361 *pcm_str = args[i + 1];
362 POUT("-p:alsa pcm \"%f\"\n", *pcm_str);
363 i += 2;
364 } else if (strcmp("-v", args[i]) == 0) {
365 unsigned long vol_ul;
367 if ((i + 1) == argc)
368 FATAL("-v:initial volume is missing\n");
370 vol_ul = strtoul(args[i + 1], 0, 10);
371 if (vol_ul < 0 || 100 < vol_ul)
372 FATAL("-v:invalid volume value %lu (0..100)\n", vol_ul);
374 *vol = (double)vol_ul / 100.;
375 POUT("-v:using initial volume %f\n", *vol);
376 i += 2;
377 } else if (strcmp("-h", args[i]) == 0) {
378 unsigned long h_ul;
380 if ((i + 1) == argc)
381 FATAL("-h:initial window pixel height is missing\n");
383 h_ul = strtoul(args[i + 1], 0, 10);
384 if (h_ul == 0 || h_ul > U16_MAX)
385 FATAL("-h:invalid window pixel height %lu (1..%lu)\n", h_ul, U16_MAX);
386 *h = (u16)h_ul;
387 POUT("-h:using initial window height %lu pixels\n", h_ul);
388 i += 2;
389 } else if (strcmp("-w", args[i]) == 0) {
390 unsigned long w_ul;
392 if ((i + 1) == argc)
393 FATAL("-h:initial window pixel width is missing\n");
395 w_ul = strtoul(args[i + 1], 0, 10);
396 if (w_ul == 0 || w_ul > U16_MAX)
397 FATAL("-w:invalid window pixel width %lu (1..%lu)\n", w_ul, U16_MAX);
398 *w = (u16)w_ul;
399 POUT("-w:using initial window width %lu pixels\n", w_ul);
400 i += 2;
401 } else if (strcmp("-b", args[i]) == 0) {
402 if ((i + 1) == argc)
403 FATAL("-b:percent value for prefill of buffer of packet queues is missing\n");
405 *pkts_prefill_percent = (u8)strtoul(args[i + 1], 0, 10);
406 POUT("-v:using a %u prefilled buffer of packet queues\n", *pkts_prefill_percent);
407 i += 2;
408 } else if (strcmp("-help", args[i]) == 0) {
409 usage();
410 exit(0);
411 } else {
412 url_idx = i;
413 ++i;
416 if (url_idx == -1)
417 FATAL("missing url\n");
418 *url = args[url_idx];
419 POUT("url-->####%s####\n", *url);
421 /*NSPC*/
422 #define WIDTH_NOT_DEFINED 0
423 #define HEIGHT_NOT_DEFINED 0
424 static void init_once(u8 *url, u16 win_width, u16 win_height, u8 *pcm_str,
425 u8 pkts_prefill_percent, avcodec_params_t **audio_codec_params,
426 avcodec_params_t **video_codec_params)
428 avutil_rational_t *audio_st_tb;
429 avutil_rational_t *video_st_tb;
431 evt_init_once();
432 sigs_init_once();
433 npv_vk_init_once(); /* generic plumbing */
434 audio_filt_init_once();
435 audio_init_once(pcm_str); /* before audio_st_idx_p is actually used */
436 video_init_once(); /* before video_st_idx_p is actually used */
437 clk_init_once();
438 pipeline_init_once(pkts_prefill_percent);
440 fmt_init_once(url);
441 /* we need something to start with */
442 fmt_probe_best_sts(
443 &video_st_p.idx, &video_st_p.id, &video_st_tb,
444 &video_st_p.start_time, video_codec_params,
445 &audio_st_p.idx, &audio_st_p.id, &audio_st_tb,
446 &audio_st_p.start_time, audio_codec_params);
447 memcpy(&audio_st_p.tb, audio_st_tb, sizeof(*audio_st_tb));
448 memcpy(&video_st_p.tb, video_st_tb, sizeof(*video_st_tb));
449 if (win_width == WIDTH_NOT_DEFINED)
450 win_width = (*video_codec_params)->width;
451 if (win_height == HEIGHT_NOT_DEFINED)
452 win_height = (*video_codec_params)->height;
453 npv_xcb_init_once(win_width, win_height);
454 npv_vk_surf_init_once(npv_xcb_p.c, npv_xcb_p.win_id);
456 #undef WIDTH_NOT_DEFINED
457 #undef HEIGHT_NOT_DEFINED
458 /*NSPC*/
459 #define PRINT_INFO true
460 static void prepare(double initial_vol, avcodec_params_t *audio_codec_params,
461 avcodec_params_t *video_codec_params)
463 enum avutil_audio_fr_fmt_t dst_fmt;
464 int dst_rate;
465 int dst_chans_n;
466 uint64_t dst_chans_layout;
468 audio_dec_ctx_cfg(audio_codec_params);
469 video_dec_ctx_cfg(video_codec_params);
471 * do our best to match the pcm cfg to audio ff dec output, BUT we don't
472 * expect to match it "exactly": see right below why
474 audio_pcm_cfg(audio_pcm_p, audio_dec_ctx_p->chans_n,
475 audio_dec_ctx_p->fr_rate, audio_dec_ctx_p->fr_fmt);
476 /* use a ff filt to fill in the gap between the pcm and audio ff dec */
477 audio_pcm2ff(audio_pcm_p, &dst_fmt, &dst_rate, &dst_chans_n,
478 &dst_chans_layout, PRINT_INFO);
479 audio_filt_cfg(
480 audio_dec_ctx_p->fr_fmt, audio_dec_ctx_p->fr_rate,
481 audio_dec_ctx_p->chans_n, audio_dec_ctx_p->chans_layout,
482 false, initial_vol,
483 dst_fmt, dst_rate, dst_chans_n, dst_chans_layout,
484 PRINT_INFO);
485 audio_pcm_silence_bufs_cfg(PRINT_INFO);
487 evt_add_all_fds();
489 #undef PRINT_INFO
490 static void cmd_info(void)
492 //TODO: info OSD toggle
495 static void cmd_quit(void)
497 EXIT("quit command received\n");
499 /*NSPC*/
500 static void seek_lock(void)
502 /* fmt */
503 fmt_ctx_lock();
504 /* video */
505 pkt_q_lock(video_pkt_q_p);
506 video_dec_frs_lock();
507 video_dec_ctx_lock();
508 /* audio */
509 pkt_q_lock(audio_pkt_q_p);
510 audio_dec_sets_lock();
511 audio_dec_ctx_lock();
513 /*NSPC*/
514 static void seek_unlock(void)
516 /* audio */
517 audio_dec_ctx_unlock();
518 audio_dec_sets_unlock();
519 pkt_q_unlock(audio_pkt_q_p);
520 /* video */
521 video_dec_ctx_unlock();
522 video_dec_frs_unlock();
523 pkt_q_unlock(video_pkt_q_p);
524 /* fmt */
525 fmt_ctx_unlock();
527 /*NSPC*/
528 #define TS_FROM_CLK_OK 0
529 static void seek_x(s64 delta)
531 int a;
532 u8 r;
533 s64 new_audio_ts;
534 s64 new_video_ts;
535 s64 audio_now;
536 s64 video_now;
538 if (audio_draining_p) {
539 WARNING("seek:audio is draining, seeking disable\n");
540 return;
543 thdsws_wait_for_idle(video_scaler_p.ctx);
545 seek_lock();
547 r = clk_get_audio_st_ts(&audio_now);
548 if (r != TS_FROM_CLK_OK) {
549 WARNING("seek:audio:clock timestamp unavailable, ignoring command\n");
550 seek_unlock();
551 return;
553 r = clk_get_video_st_ts(&video_now);
554 if (r != TS_FROM_CLK_OK) {
555 WARNING("seek:video:clock timestamp unavailable, ignoring command\n");
556 seek_unlock();
557 return;
560 * XXX: a set of sts can share the same id for seeking. if they share
561 * the same id then the tbs should be the same.
563 /*--------------------------------------------------------------------*/
564 new_audio_ts = audio_now + delta * audio_st_p.tb.den
565 / audio_st_p.tb.num;
566 /* rewind capping if possible */
567 if (audio_st_p.start_time != AV_NOPTS_VALUE)
568 if (new_audio_ts < audio_st_p.start_time)
569 new_audio_ts = audio_st_p.start_time;
570 POUT("trying to seek to %"PRId64" audio stream time base units\n", new_audio_ts);
571 a = avformat_seek_pkt(fmt_ctx_p, audio_st_p.id, new_audio_ts, 0);
572 if (a < 0) {
573 WARNING("unable to seek to %"PRId64" audio stream time base units\n", new_audio_ts);
574 goto try_restore_audio;
576 POUT("audio seek to %"PRId64" audio stream time base units\n", new_audio_ts);
577 /*--------------------------------------------------------------------*/
578 new_video_ts = video_now + delta * video_st_p.tb.den
579 / video_st_p.tb.num;
580 /* rewind capping if possible */
581 if (video_st_p.start_time != AV_NOPTS_VALUE)
582 if (new_video_ts < video_st_p.start_time)
583 new_video_ts = video_st_p.start_time;
584 POUT("trying to seek to %"PRId64" video stream time base units\n", new_video_ts);
585 a = avformat_seek_pkt(fmt_ctx_p, video_st_p.id, new_video_ts, 0);
586 if (a < 0) {
587 WARNING("unable to seek to %"PRId64" video stream time base units but audio was seeked)\n", new_video_ts);
588 goto try_restore_video;
590 POUT("video seek to %"PRId64" video stream time base units\n", new_video_ts);
591 flush:
592 video_dec_flush();
593 audio_dec_flush();
594 audio_filt_flush();
595 fmt_flush();
596 clk_invalidate();
597 //input_bootstrap_audio_video();
598 seek_unlock();
599 return;
601 try_restore_video:
602 a = avformat_seek_pkt(fmt_ctx_p, video_st_p.id, video_now, 0);
603 if (a < 0) /* we don't send an application error */
604 EXIT("unable to restore video to %"PRId64" video stream time base units\n", video_now);
605 try_restore_audio:
606 a = avformat_seek_pkt(fmt_ctx_p, audio_st_p.id, audio_now, 0);
607 if (a < 0) /* we don't send an application error */
608 EXIT("unable to restore audio to %"PRId64" audio stream time base units\n", audio_now);
609 goto flush;
611 #undef TS_FROM_CLK_OK
612 static void cmd_rewind(void)
614 POUT("COMMAND:rewind\n");
615 seek_x(-SEEK_DELTA);
618 static void cmd_rewind_big(void)
620 POUT("COMMAND:rewind big\n");
621 seek_x(-SEEK_DELTA_BIG);
624 static void cmd_fastforward(void)
626 POUT("COMMAND:fastforward\n");
627 seek_x(SEEK_DELTA);
630 static void cmd_fastforward_big(void)
632 POUT("COMMAND:fastforward big\n");
633 seek_x(SEEK_DELTA_BIG);
636 static void cmd_pause(void)
638 if (audio_draining_p) {
639 WARNING("pause:audio is draining, toggling pause is disable\n");
640 return;
642 if (paused_p) {
643 int r;
645 POUT("COMMAND:unpause\n");
646 paused_p = false;
647 fmt_ctx_lock();
648 avformat_read_play(fmt_ctx_p);
649 fmt_ctx_unlock();
650 } else {
651 int r;
653 POUT("COMMAND:pause\n");
654 paused_p = true;
655 fmt_ctx_lock();
656 avformat_read_pause(fmt_ctx_p);
657 fmt_ctx_unlock();
661 static void cmd_vol_up(void)
663 audio_filt_cmd_vol_up();
666 static void cmd_vol_down(void)
668 audio_filt_cmd_vol_down();
671 static void cmd_mute(void)
673 audio_filt_cmd_mute();
675 /*NSPC*/
676 static void prefill_wait(void) { loop
678 struct timespec wanted;
679 struct timespec rem;
680 s64 prefill_audio;
681 s64 prefill_video;
683 pipeline_limits_lock();
684 prefill_audio = pipeline_limits_p.pkts.prefill.audio_bytes_rem;
685 prefill_video = pipeline_limits_p.pkts.prefill.video_bytes_rem;
686 pipeline_limits_unlock();
687 if (prefill_audio <= 0 && prefill_video <= 0)
688 break;
689 memset(&wanted, 0, sizeof(wanted));
690 memset(&rem, 0, sizeof(rem));
691 wanted.tv_nsec = 100000000; /* 100 ms */
692 loop {
693 int r;
695 /* linux bug: cannot specify CLOCK_MONOTONIC_RAW */
696 r = clock_nanosleep(CLOCK_MONOTONIC, 0, &wanted, &rem);
697 if (r == 0)
698 break;
699 if (r != EINTR)
700 FATAL("prefill wait timer failed:%d\n", r);
701 /* r == EINTR */
702 memcpy(&wanted, &rem, sizeof(wanted));
703 memset(&rem, 0, sizeof(rem));
706 /*NSPC*/
707 static void predecoding_wait(void)
709 struct timespec wanted;
710 struct timespec rem;
712 memset(&wanted, 0, sizeof(wanted));
713 memset(&rem, 0, sizeof(rem));
714 /* we target ~double audio buf, namely (0.25s * 2 ~ 500ms) */
715 wanted.tv_nsec = 500000000;
716 loop {
717 int r;
719 /* linux bug: cannot specify CLOCK_MONOTONIC_RAW */
720 r = clock_nanosleep(CLOCK_MONOTONIC, 0, &wanted, &rem);
721 if (r == 0)
722 break;
723 if (r != EINTR)
724 FATAL("predecoding wait timer failed:%d\n", r);
725 /* r == EINTR */
726 memcpy(&wanted, &rem, sizeof(wanted));
727 memset(&rem, 0, sizeof(rem));
730 #define WIDTH_NOT_DEFINED 0
731 #define HEIGHT_NOT_DEFINED 0
732 int main(int argc, u8 **args)
734 u16 win_width;
735 u16 win_height;
736 u8 *pcm_str;
737 u8 *url;
738 double initial_vol;
739 u8 pkts_prefill_percent;
740 avcodec_params_t *audio_codec_params;
741 avcodec_params_t *video_codec_params;
743 /* "turn on utf8" processing in used libs if any *AND* locale system */
744 setlocale(LC_ALL, "");
745 /* av_log_set_level(AV_LOG_VERBOSE); */
746 /* av_log_set_level(AV_LOG_DEBUG); */
748 win_width = WIDTH_NOT_DEFINED;
749 win_height = HEIGHT_NOT_DEFINED;
750 url = 0;
751 pcm_str = "default";
752 url = 0;
753 initial_vol = 1.;
754 pkts_prefill_percent = 0;
755 opts_parse(argc, args, &url, &win_width, &win_height, &pcm_str,
756 &initial_vol, &pkts_prefill_percent);
757 init_once(url, win_width, win_height, pcm_str, pkts_prefill_percent,
758 &audio_codec_params, &video_codec_params);
759 prepare(initial_vol, audio_codec_params, video_codec_params);
761 /* switch the ffmpeg log to stdout for metadata/etc dump */
762 avutil_log_set_callback(ff_log_stdout);
763 avformat_dump_fmt(fmt_ctx_p, 0, url, 0);
764 avutil_log_set_callback(avutil_log_default_callback);
766 pipeline_read_thd_start();
767 POUT("prefilling audio and video buffers...");
768 prefill_wait();
769 POUT("done\n");
770 pipeline_audio_thd_start();
771 pipeline_video_thd_start();
772 POUT("predecoding audio and video...");
773 predecoding_wait();
774 POUT("done\n");
775 video_timer_start();
777 loop evts_loop();
778 /* unreachable */
780 #undef WIDTH_NOT_DEFINED
781 #undef HEIGHT_NOT_DEFINED