npv: x11 fixes and more sugar
[nyanmp.git] / npa / npa.c
blob89238d3d5240992ba190195f31c38dc0801a70c6
1 #ifndef NPA_C
2 #define NPA_C
3 /*
4 * code protected with a GNU affero GPLv3 license
5 * copyright (C) 2020 Sylvain BERTRAND
6 */
7 /*
8 * ABBREVIATIONS
10 * abuf : Audio BUFfer
11 * async : ASYNChronous
12 * b(s) : Byte(S) (often)
13 * buf(s) : BUFfer(S)
14 * cb : Cicular Buffer
15 * chan(s) : CHANnel(S)
16 * cmd(s) : CoMmanD(S)
17 * cp(s) : Code Point(s)
18 * cfg : ConFiGuration
19 * ctx : ConTeX
20 * dec : DECoder/DECoded
21 * dev : DEVice
22 * e : End (usually a pointer on the byte past the last valid byte)
23 * eof : End Of File
24 * esc : ESCape
25 * ep : EPoll
26 * evt(s) : EVenT(S)
27 * err : ERRor
28 * fd : File Descriptor
29 * filt : FILTer
30 * fmt : ForMaT
31 * fr(s) : FRame(S)
32 * inc : INCrement
33 * idx(s) : InDeX(S)
34 * l10n : LocalizatioN (10 chars between L and N)
35 * min(s) : MINute(S)
36 * msec(s) : MilliSECond(S)
37 * n : couNt
38 * nr : NumbeR
39 * out : OUTput
40 * pkt(s) : PacKeT(S)
41 * rd : ReaD
42 * ref(s) : REFerence(S)
43 * s : Start
44 * sd : SenD
45 * sec(s) : SECond(S)
46 * seq : SEQuence
47 * sig(s) : SIGnal(S)
48 * st : STream
49 * str : STRing
50 * sync : SYNChronous
51 * sz : SiZe
52 * thd(s) : THreaD(S)
53 * tb : Time Base
54 * ts : TimeStamp
55 * vol : VOLume
58 * this is not a library, then we could not care less about memory management
59 * and/or similar cleanup: we have a virtual machine with a garbage collector,
60 * it is linux.
62 * we do presume we won't play more than 8 chans and ffmpeg chans layout will
63 * fit alsa chans map
65 * XXX: we don't know how the alsa silence machinery works, then we use brutal
66 * silence bufs
68 /*----------------------------------------------------------------------------*/
69 /* C/posix */
70 #include <stdbool.h>
71 #include <locale.h>
72 #include <stdarg.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <errno.h>
77 #include <signal.h>
78 #include <pthread.h>
79 #include <unistd.h>
80 #include <termios.h>
81 #include <stdint.h>
82 #include <fcntl.h>
83 #include <stdatomic.h>
84 #include <limits.h>
85 #include <time.h>
86 /* linux and compatible */
87 #include <sys/epoll.h>
88 #include <sys/signalfd.h>
89 #include <sys/timerfd.h>
90 /* ffmpeg */
91 #include <libavformat/avformat.h>
92 #include <libavcodec/avcodec.h>
93 #include <libavfilter/avfilter.h>
94 #include <libavfilter/buffersrc.h>
95 #include <libavfilter/buffersink.h>
96 #include <libavutil/opt.h>
97 /* alsa */
98 #include <alsa/asoundlib.h>
99 /* fix C */
100 #define u8 uint8_t
101 #define U8_MAX 255
102 #define u32 uint32_t
103 #define u16 uint16_t
104 #define f32 float
105 #define loop for(;;)
106 #if UCHAR_WIDTH == 8
107 #define atomic_u8 atomic_uchar
108 #else
109 #error "unable to find the right atomic for a 8 bits byte, be sure to have __STDC_WANT_IEC_60559_BFP_EXT__ defined"
110 #endif
112 #define ARRAY_N(x) (sizeof(x) / sizeof((x)[0]))
114 #define POUT(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__)
115 #define PERR(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
116 #define WARNING(fmt, ...) fprintf(stderr, "WARNING:" fmt, ##__VA_ARGS__)
117 #define FATAL(fmt, ...) ({PERR("FATAL:" fmt, ##__VA_ARGS__);stdin_flags_restore();stdin_tty_cfg_restore();exit(1);})
118 #define EXIT(fmt, ...) ({POUT("EXITING:" fmt, ##__VA_ARGS__);stdin_flags_restore();stdin_tty_cfg_restore();exit(0);})
119 #define STR_SZ 255 /* sz and idx fit in 1 byte */
120 /*---------------------------------------------------------------------------*/
121 static u8 *current_url;
122 static int current_st_idx;
123 /* cmd_info must be fast, then a lockless copy of its data */
124 static struct {
125 struct {
126 int64_t duration;
127 enum AVDurationEstimationMethod m;
128 } fmt;
129 struct {
130 AVRational tb;
131 int id;
132 int64_t duration;
133 } st;
134 } cmd_info_data;
135 /*---------------------------------------------------------------------------*/
136 /* linux and compatible */
137 static int ep_fd;
138 static int sig_fd;
139 /*---------------------------------------------------------------------------*/
140 /* linux alsa */
141 static snd_pcm_t *pcm_g;
142 static snd_output_t *pcm_pout;
143 static snd_output_t *pcm_perr;
144 #define PCM_POLLFDS_N_MAX 16 /* banzai */
145 static struct pollfd pcm_pollfds[PCM_POLLFDS_N_MAX];
146 static u8 pcm_pollfds_n;
147 /*---------------------------------------------------------------------------*/
148 /* ffmpeg dec */
150 * XXX: a ffmpeg fr is not 1 audio fr but a collection of frs.
151 * ffmpeg dec fr "nb_samples" is actually the number of audio frs
152 * av_read_frame is actually av_read_pkt, like av_seek*
153 * we personally did not like the resulting confusion.
154 * we purposely "fix" the namespace even if some others won't like it.
156 #define AVFrames AVFrame
157 #define frames_n nb_samples
158 #define av_frames_alloc av_frame_alloc
159 #define av_read_pkt av_read_frame
160 #define av_seek_pkt av_seek_frame
161 #define avcodec_receive_frames avcodec_receive_frame
162 #define AVFrameFormat AVSampleFormat
163 #define av_frames_set_silence av_samples_set_silence
164 #define av_get_frame_fmt_name av_get_sample_fmt_name
165 #define av_get_frame_fmt_string av_get_sample_fmt_string
166 #define frame_rate sample_rate
167 #define av_frame_fmt_is_planar av_sample_fmt_is_planar
168 #define frame_fmt sample_fmt
169 #define av_frames_unref av_frame_unref
170 static AVPacket *rd_thd_pkt;
171 static AVFormatContext *fmt_ctx;
172 static pthread_mutex_t fmt_ctx_mutex;
173 static AVCodec *dec;
174 static AVCodecContext *dec_ctx;
175 /*---------------------------------------------------------------------------*/
176 /* ffmpeg filter graph */
177 static AVFilterGraph *filter_graph;
178 static AVFilterContext *abufsrc_ctx;
179 static const AVFilter *abufsrc_filt;
180 static AVFilterContext *vol_ctx;
181 static const AVFilter *vol_filt;
182 static u8 double_zero_l10n_str[sizeof("xxx.xx")];
183 static AVFilterContext *afmt_ctx;
184 static const AVFilter *afmt_filt;
185 static AVFilterContext *abufsink_ctx;
186 static const AVFilter *abufsink_filt;
187 /*---------------------------------------------------------------------------*/
188 /* a formally proven concurrently accessed lockless cb */
189 struct {
190 atomic_u8 reset;
192 atomic_int st_idx;
193 AVPacket *pkts[U8_MAX + 1];
194 atomic_u8 rd;
195 atomic_u8 sd;
196 } cb;
197 /* running state */
198 struct {
199 AVFrames *av;
200 int64_t most_recent_ts; /* a very "coarse-grained" clock */
201 } dec_frs;
202 struct {
203 AVFrames *av;
204 bool no_more_dec_frs;
205 float vol;
206 bool muted;
207 snd_pcm_uframes_t pcm_written_ufrs_n;
208 } filt_frs;
209 /* we will inject silence frs while paused */
210 bool paused;
211 void *silence_bufs[AV_NUM_DATA_POINTERS];
212 /*----------------------------------------------------------------------------*/
213 /* tty */
214 static bool stdin_tty_cfg_modified;
215 static struct termios stdin_tio_save;
216 static int stdin_flags_save;
217 static bool stdout_is_tty;
218 /*----------------------------------------------------------------------------*/
219 static void cmd_quit(void);
220 static void cmd_rewind(void);
221 static void cmd_fastforward(void);
222 static void cmd_rewind_big(void);
223 static void cmd_fastforward_big(void);
224 static void cmd_vol_up(void);
225 static void cmd_vol_down(void);
226 static void cmd_mute(void);
227 static void cmd_info(void);
228 static void cmd_pause(void);
229 /*--------------------------------------------------------------------------*/
230 #include "npa_config.h"
231 /*----------------------------------------------------------------------------*/
232 /* input "state" machine (2 major states: "utf8" and "esc seq" */
233 static int input_timer_fd;
234 static bool input_esc_seq_mode;
235 static u8 input_b; /* input byte */
237 static u8 utf8_cp[4];
238 static u8 utf8_cp_next_byte; /* idx in utf8_cp */
239 static u8 utf8_cp_sz;
241 static u8 esc_seq[STR_SZ];
242 static u8 esc_seq_next_byte; /* idx in esc_seq */
243 #define esc_seq_sz esc_seq_next_byte /* the idx of the next byte is its sz */
244 /*----------------------------------------------------------------------------*/
245 static void stdin_flags_restore(void)
247 int r;
249 r = fcntl(0, F_SETFL, stdin_flags_save);
250 if (r == -1)
251 WARNING("input:unable to restore the file flags of the standard input\n");
253 static void stdin_tty_cfg_restore(void)
255 int r;
256 struct termios tio_chk;
258 if (!stdin_tty_cfg_modified)
259 return;
261 r = tcsetattr(0, TCSANOW, &stdin_tio_save);
262 if (r == -1) {
263 WARNING("input:unable to restore the terminal line attributes\t");
264 return;
267 memset(&tio_chk, 0, sizeof(tio_chk));
268 r = tcgetattr(0, &tio_chk);
269 if (r == -1) {
270 WARNING("input:unable to get the current terminal line attributes for restoration checking\n");
271 return;
274 r = memcmp(&tio_chk, &stdin_tio_save, sizeof(tio_chk));
275 if (r != 0)
276 WARNING("input:only partial restoration of the terminal line attributes\n");
278 static void fmt_ctx_lock(void)
280 int r;
282 r = pthread_mutex_lock(&fmt_ctx_mutex);
283 if (r != 0)
284 FATAL("unable to lock the format context\n");
286 static void fmt_ctx_unlock(void)
288 int r;
290 r = pthread_mutex_unlock(&fmt_ctx_mutex);
291 if (r != 0)
292 FATAL("unable to unlock the format context\n");
294 static u8 *duration_estimate_to_str(enum AVDurationEstimationMethod m)
296 switch (m) {
297 case AVFMT_DURATION_FROM_PTS:
298 return "from PTS(Presentation TimeStamp)";
299 case AVFMT_DURATION_FROM_STREAM:
300 return "from stream";
301 case AVFMT_DURATION_FROM_BITRATE:
302 return "from bitrate";
303 default:
304 return "unkwown";
307 /* meh... */
308 static u8 *ts_to_str(int64_t ts, AVRational tb, int64_t *remaining)
310 static u8 str[sizeof("~S00:00:00.000 remains S9223372036854775807 time base units")];
311 bool is_neg;
312 int64_t hours_n;
313 int64_t mins_n;
314 int64_t secs_n;
315 int64_t msecs_n;
316 int64_t one_hour; /* in ffmpeg time_base units */
317 int64_t one_min; /* in ffmpeg time_base units */
318 int64_t one_sec; /* in ffmpeg time_base units */
319 int64_t one_msec; /* in ffmpeg time_base units */
321 if (ts < 0) {
322 ts = -ts;
323 is_neg = true;
324 } else
325 is_neg = false;
326 one_hour = INT64_C(3600) * (int64_t)tb.den / (int64_t)tb.num;
327 one_min = INT64_C(60) * (int64_t)tb.den / (int64_t)tb.num;
328 one_sec = (int64_t)tb.den / (int64_t)tb.num;
329 one_msec = one_sec / INT64_C(1000);
331 hours_n = ts / one_hour;
333 *remaining = ts % one_hour;
334 mins_n = *remaining / one_min;
336 *remaining = *remaining % one_min;
337 secs_n = *remaining / one_sec;
339 *remaining = *remaining % one_sec;
340 msecs_n = *remaining / one_msec;
342 /* account for all rounding errors */
343 *remaining = ts - (hours_n * one_hour + mins_n * one_min
344 + secs_n * one_sec + msecs_n * one_msec);
345 if (!is_neg)
346 snprintf(str, sizeof(str), "%02"PRId64":%02"PRId64":%02"PRId64".%03"PRId64, hours_n, mins_n, secs_n, msecs_n);
347 else {
348 str[0] = '-';
349 snprintf(str + 1, sizeof(str) - 1, "%02"PRId64":%02"PRId64":%02"PRId64".%03"PRId64, hours_n, mins_n, secs_n, msecs_n);
351 return str;
353 #define RED if (stdout_is_tty) POUT("\x1b[38;2;255;0;0m")
354 #define GREEN if (stdout_is_tty) POUT("\x1b[38;2;0;255;0m")
355 #define BLUE if (stdout_is_tty) POUT("\x1b[38;2;0;0;255m")
356 #define PURPLE if (stdout_is_tty) POUT("\x1b[38;2;255;0;255m")
357 #define RESTORE if (stdout_is_tty) POUT("\x1b[39;49m");
358 static void cmd_info(void)
360 u8 *ts_str;
361 int64_t remaining;
362 u8 duration_str[sizeof("S9223372036854775807")];
364 RESTORE;
365 GREEN;POUT("================================================================================\n");RESTORE;
366 PURPLE;POUT("%s\n", current_url);RESTORE;
367 ts_str = ts_to_str(dec_frs.most_recent_ts, cmd_info_data.st.tb, &remaining);
368 RED;POUT("%s", ts_str);RESTORE;
369 if (remaining != 0)
370 POUT(" remaining %"PRId64" time base units", remaining);
371 else
372 POUT("\n");
373 POUT("\t%"PRId64" stream time base units (%d/%d seconds)\n", dec_frs.most_recent_ts, cmd_info_data.st.tb.num, cmd_info_data.st.tb.den);
374 BLUE;POUT("--------------------------------------------------------------------------------\n");RESTORE;
375 POUT("format:");
376 if (cmd_info_data.fmt.duration == AV_NOPTS_VALUE) {
377 POUT("duration is not provided\n");
378 } else {
379 snprintf(duration_str, sizeof(duration_str), "%"PRId64, cmd_info_data.fmt.duration);
380 ts_str = ts_to_str(cmd_info_data.fmt.duration, AV_TIME_BASE_Q,
381 &remaining);
382 POUT("duration=");RED;POUT("%s", ts_str);RESTORE;
383 if (remaining != 0)
384 POUT(" remaining %"PRId64" av_time_base units\n", remaining);
385 else
386 POUT("\n");
387 POUT("\t%s av_time_base units (1/%d seconds)\n\testimation method is %s\n", duration_str, AV_TIME_BASE, duration_estimate_to_str(cmd_info_data.fmt.m));
389 POUT("stream:id=%d", cmd_info_data.st.id);
390 if (cmd_info_data.st.duration == AV_NOPTS_VALUE) {
391 POUT(";duration is not provided\n");
392 } else {
393 snprintf(duration_str, sizeof(duration_str), "%"PRId64, cmd_info_data.st.duration);
394 ts_str = ts_to_str(cmd_info_data.st.duration, cmd_info_data.st.tb, &remaining);
395 POUT(";duration=");RED;POUT("%s\n", ts_str);RESTORE;
396 if (remaining != 0)
397 POUT(" remaining %"PRId64" stream time base units\n", remaining);
398 else
399 POUT("\n");
400 POUT("\t%s stream time base units (%d/%d seconds)\n", duration_str, cmd_info_data.st.tb.num, cmd_info_data.st.tb.den);
402 BLUE;POUT("--------------------------------------------------------------------------------\n");RESTORE;
403 POUT("circular buffer: %u/%u/%u (read/send/max)\n", atomic_load(&cb.rd), atomic_load(&cb.sd), U8_MAX);
404 if (paused) {
405 RED;POUT("paused\n");RESTORE;
407 if (filt_frs.muted) {
408 RED;POUT("muted\n");RESTORE;
410 GREEN;POUT("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");RESTORE;
412 #undef RED
413 #undef GREEN
414 #undef BLUE
415 #undef PURPLE
416 #undef RESTORE
417 static void wait(long ns)
419 struct timespec wanted;
420 struct timespec rem;
422 memset(&wanted, 0, sizeof(wanted));
423 memset(&rem, 0, sizeof(rem));
424 wanted.tv_nsec = ns;
425 loop {
426 int r;
428 /* linux bug: cannot specify CLOCK_MONOTONIC_RAW */
429 r = clock_nanosleep(CLOCK_MONOTONIC, 0, &wanted, &rem);
430 if (r == 0)
431 break;
432 if (r != EINTR)
433 FATAL("wait timer failed:%d\n", r);
434 /* r == EINTR */
435 memcpy(&wanted, &rem, sizeof(wanted));
436 memset(&rem, 0, sizeof(rem));
439 #define RESET_DONE 0
440 static void do_reset(void)
442 u8 i;
444 fmt_ctx_lock();
445 avformat_flush(fmt_ctx);
446 avio_flush(fmt_ctx->pb);
447 fmt_ctx_unlock();
448 i = 0;
449 loop {
450 av_packet_unref(cb.pkts[i]);
451 if (i == U8_MAX)
452 break;
453 ++i;
455 atomic_store(&cb.rd, 0);
456 atomic_store(&cb.sd, 0);
457 atomic_store(&cb.reset, RESET_DONE);
459 #undef RESET_DONE
460 #define DO_RESET 1
461 static void rd_loop(void) { loop /* infinite loop */
463 int r;
464 u8 rd;
465 u8 next_rd;
466 u8 sd;
467 u8 reset;
468 int st_idx;
470 * XXX: we actually perform reset on this thd, with a little lockfree
471 * protocol and reasonable wait loops
473 reset = atomic_load(&cb.reset);
474 if (reset == DO_RESET)
475 do_reset();
476 /*--------------------------------------------------------------------*/
477 rd = atomic_load(&cb.rd);
478 sd = atomic_load(&cb.sd);
479 next_rd = rd + 1;
480 if (next_rd == sd) { /* must sd first the sd slot */
481 wait(1000000); /* 1ms */
482 continue;
484 /*--------------------------------------------------------------------*/
485 fmt_ctx_lock();
486 r = av_read_pkt(fmt_ctx, rd_thd_pkt);
487 fmt_ctx_unlock();
488 if (r == AVERROR(EAGAIN)) {
489 /* 1ms: it is sort of aggressive to check for reset */
490 wait(1000000);
491 continue;
492 } else if (r == AVERROR_EOF) {
493 /* The "NULL" pkt ref to signal the EOF to the dec */
494 cb.pkts[rd]->data = 0;
495 cb.pkts[rd]->size = 0;
496 } else if (r != 0)
497 FATAL("ffmpeg:error while demuxing coded/compressed data into packets\n");
498 else { /* r == 0 */
499 st_idx = atomic_load(&cb.st_idx);
500 if (rd_thd_pkt->stream_index != st_idx) { /* sd_idx can be -1 */
501 av_packet_unref(rd_thd_pkt);
502 continue;
504 av_packet_move_ref(cb.pkts[rd], rd_thd_pkt);
506 atomic_store(&cb.rd, next_rd);
508 #undef DO_RESET
509 static void *rd_thd_entry(void *arg)
511 int r;
512 sigset_t sset;
514 r = sigfillset(&sset);
515 if (r == -1)
516 FATAL("read thread:unable to get a full signal mask\n");
517 r = pthread_sigmask(SIG_SETMASK, &sset, 0);
518 if (r != 0)
519 FATAL("read thread:unable to \"block\" \"all\" signals\n");
520 rd_loop();
521 /* unreachable */
523 static void rd_thd_start(void)
525 int r;
526 pthread_t id;
527 pthread_attr_t attr;
529 r = pthread_attr_init(&attr);
530 if (r != 0)
531 FATAL("read thread:unable to initialize read thread attribute\n");
532 r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
533 if (r != 0)
534 FATAL("read thread:unable to set the read thread attribute to detach mode\n");
535 r = pthread_create(&id, &attr, &rd_thd_entry, 0);
536 if (r != 0)
537 FATAL("read thread:unable to create the read thread\n");
538 POUT("read thread %lu\n", (unsigned long)id);
539 pthread_attr_destroy(&attr);
541 static void cmd_quit(void)
543 EXIT("quit command received\n");
545 #define RESET_DONE 0
546 #define DO_RESET 1
547 static void rd_thd_reset(int st_idx)
549 u32 loops_n;
551 loops_n = 0;
552 /* reset = RESET_DONE */
553 atomic_store(&cb.st_idx, st_idx);
554 atomic_store(&cb.reset, DO_RESET);
555 loop {
556 u8 reset;
558 reset = atomic_load(&cb.reset);
559 if (reset == RESET_DONE)
560 break;
561 wait(1000000); /* 1ms */
562 ++loops_n;
563 if (loops_n == 4000) /* 1ms * 4000 = 4s */
564 FATAL("read thread reset timeout\n");
566 /* reset = DO_RESET */
568 #undef RESET_DONE
569 #undef DO_RESET
571 * XXX: if it is ever used significantly, a fine granularity wiring strategy
572 * will be implemented instead of using the default wiring
574 static uint64_t pcm_chmaps2ff_chans_layout(snd_pcm_t *pcm,
575 unsigned int pcm_chans_n, bool print_info)
577 int r;
578 uint64_t ff_chans_layout;
579 snd_pcm_chmap_t *pcm_chmap;
580 u8 chans_layout_str[STR_SZ]; /* should be overkill */
582 pcm_chmap = snd_pcm_get_chmap(pcm);
583 if (pcm_chmap == 0) {
584 if (print_info)
585 POUT("alsa:no pcm channel map available, wiring to default ffmpeg channel layout\n");
586 } else {
587 if (print_info)
588 POUT("alsa:your pcm device support channel maps, but fine granularity wiring strategy is not implemented\n");
589 free(pcm_chmap);
591 ff_chans_layout = av_get_default_channel_layout((int)pcm_chans_n);
592 av_get_channel_layout_string(chans_layout_str, sizeof(chans_layout_str),
593 (int)pcm_chans_n, ff_chans_layout);
594 if (print_info)
595 POUT("alsa channel map wired to ffmpeg channel layout:\"%s\" (%u pcm channels)\n", chans_layout_str, pcm_chans_n);
596 return ff_chans_layout;
598 /* fatal if the wiring cannot be done */
599 static void pcm_layout2ff_fmt_strict(snd_pcm_format_t alsa_fmt,
600 snd_pcm_access_t alsa_access, enum AVSampleFormat *ff_fmt,
601 bool print_info)
604 * ff fmt byte order is always native.
605 * here we handle little endian only
607 switch (alsa_fmt) {
608 case SND_PCM_FORMAT_FLOAT:
609 if (alsa_access == SND_PCM_ACCESS_RW_INTERLEAVED)
610 *ff_fmt = AV_SAMPLE_FMT_FLT;
611 else
612 *ff_fmt = AV_SAMPLE_FMT_FLTP;
613 break;
614 case SND_PCM_FORMAT_S32:
615 if (alsa_access == SND_PCM_ACCESS_RW_INTERLEAVED)
616 *ff_fmt = AV_SAMPLE_FMT_S32;
617 else
618 *ff_fmt = AV_SAMPLE_FMT_S32P;
619 break;
620 case SND_PCM_FORMAT_S16:
621 if (alsa_access == SND_PCM_ACCESS_RW_INTERLEAVED)
622 *ff_fmt = AV_SAMPLE_FMT_S16;
623 else
624 *ff_fmt = AV_SAMPLE_FMT_S16P;
625 break;
626 case SND_PCM_FORMAT_U8:
627 if (alsa_access == SND_PCM_ACCESS_RW_INTERLEAVED)
628 *ff_fmt = AV_SAMPLE_FMT_U8;
629 else
630 *ff_fmt = AV_SAMPLE_FMT_U8P;
631 break;
632 default:
633 FATAL("unable to wire strictly alsa layout \"%s\"/\"%s\" to a ffmpeg format\n", snd_pcm_format_description(alsa_fmt), snd_pcm_access_name(alsa_access));
635 if (print_info) {
636 u8 ff_fmt_str[STR_SZ];
638 av_get_sample_fmt_string(ff_fmt_str, sizeof(ff_fmt_str),
639 *ff_fmt);
640 POUT("alsa pcm layout \"%s\"/\"%s\" wired strictly to ffmpeg format \"%sbits\"\n", snd_pcm_format_description(alsa_fmt), snd_pcm_access_name(alsa_access), ff_fmt_str);
643 static void pcm2ff(snd_pcm_t *pcm, enum AVSampleFormat *ff_fmt,
644 int *ff_rate, int *ff_chans_n, uint64_t *ff_chans_layout,
645 bool print_info)
647 int r;
648 snd_pcm_hw_params_t *hw_params;
649 snd_pcm_access_t pcm_access;
650 snd_pcm_format_t pcm_fmt;
651 unsigned int pcm_rate;
652 unsigned int pcm_chans_n;
654 r = snd_pcm_hw_params_malloc(&hw_params);
655 if (r < 0)
656 FATAL("alsa:unable to allocate hardware parameters context for ffmpeg filter wiring\n");
657 r = snd_pcm_hw_params_current(pcm, hw_params);
658 if (r != 0)
659 FATAL("alsa:unable to get current hardware parameters for ffmpeg filter wiring\n");
660 r = snd_pcm_hw_params_get_access(hw_params, &pcm_access);
661 if (r < 0)
662 FATAL("alsa:unable to get the pcm access for ffmpeg filter wiring\n");
663 r = snd_pcm_hw_params_get_format(hw_params, &pcm_fmt);
664 if (r < 0)
665 FATAL("alsa:unable to get the pcm format for ffmpeg filter wiring\n");
666 /*--------------------------------------------------------------------*/
667 pcm_layout2ff_fmt_strict(pcm_fmt, pcm_access, ff_fmt, print_info);
668 /*--------------------------------------------------------------------*/
669 r = snd_pcm_hw_params_get_rate(hw_params, &pcm_rate,
670 SND_PCM_STREAM_PLAYBACK);
671 if (r < 0)
672 FATAL("alsa:unable to get the pcm rate for ffmpeg filter wiring\n");
673 *ff_rate = (int)pcm_rate;
674 r = snd_pcm_hw_params_get_channels(hw_params, &pcm_chans_n);
675 if (r < 0)
676 FATAL("alsa:unable to get the pcm count of channels for ffmpeg filter wiring\n");
677 *ff_chans_n = (int)pcm_chans_n;
678 /*--------------------------------------------------------------------*/
679 *ff_chans_layout = pcm_chmaps2ff_chans_layout(pcm, pcm_chans_n,
680 print_info);
681 /*--------------------------------------------------------------------*/
682 snd_pcm_hw_params_free(hw_params);
684 static void abufsrc_cfg(enum AVFrameFormat fmt, int rate, int chans_n,
685 uint64_t chans_layout, bool print_info)
687 int r;
688 AVRational tb;
689 u8 chans_layout_str[STR_SZ]; /* should be overkill */
691 abufsrc_filt = avfilter_get_by_name("abuffer");
692 if (abufsrc_filt == 0)
693 FATAL("audio buffer source:could not find the filter\n");
694 abufsrc_ctx = avfilter_graph_alloc_filter(filter_graph, abufsrc_filt,
695 "src_abuf");
696 if (abufsrc_ctx == 0)
697 FATAL("audio buffer source context:could not allocate the instance in the filter graph\n");
698 r = av_opt_set(abufsrc_ctx, "sample_fmt", av_get_frame_fmt_name(fmt),
699 AV_OPT_SEARCH_CHILDREN);
700 if (r < 0)
701 FATAL("audio buffer source context:unable to set the decoder frame format option\n");
702 r = av_opt_set_int(abufsrc_ctx, "sample_rate", rate,
703 AV_OPT_SEARCH_CHILDREN);
704 if (r < 0)
705 FATAL("audio buffer source context:unable to set the decoder rate option\n");
707 * XXX: at the time of coding, bug for 1 chans layout... or I did miss
708 * some valuable information
710 av_get_channel_layout_string(chans_layout_str, sizeof(chans_layout_str),
711 chans_n, chans_layout);
712 if (print_info)
713 POUT("audio buffer source context:using channels layout \"%s\" (%d pcm channels)\n", chans_layout_str, chans_n);
714 r = av_opt_set(abufsrc_ctx, "channel_layout", chans_layout_str,
715 AV_OPT_SEARCH_CHILDREN);
716 if (r < 0)
717 FATAL("audio buffer source context:unable to set the decoder channel layout option\n");
718 r = avfilter_init_str(abufsrc_ctx, 0);
719 if (r < 0)
720 FATAL("audio buffer source context:unable to initialize\n");
722 static void vol_cfg(bool muted, double vol)
724 double vol_double;
725 u8 vol_l10n_str[sizeof("xxx.xx")]; /* should be overkill */
726 int r;
728 vol_filt = avfilter_get_by_name("volume");
729 if (vol_filt == 0)
730 FATAL("volume:could not find the filter\n");
731 vol_ctx = avfilter_graph_alloc_filter(filter_graph, vol_filt, "vol");
732 if (vol_ctx == 0)
733 FATAL("volume context:could not allocate the instance in the filter graph\n");
734 if (muted)
735 vol_double = 0.0;
736 else
737 vol_double = vol;
738 /* yeah the radix is localized, can be '.', ','... */
739 snprintf(vol_l10n_str, sizeof(vol_l10n_str), "%f", vol_double);
740 r = av_opt_set(vol_ctx, "volume", vol_l10n_str, AV_OPT_SEARCH_CHILDREN);
741 if (r < 0)
742 FATAL("volume context:unable to set the volume option\n");
743 r = avfilter_init_str(vol_ctx, 0);
744 if (r < 0)
745 FATAL("volume buffer context:unable to initialize\n");
747 static void afmt_cfg(enum AVFrameFormat fmt, int rate, int chans_n,
748 uint64_t chans_layout, bool print_info)
750 int r;
751 u8 rate_str[sizeof("dddddd")];
752 u8 chans_layout_str[STR_SZ]; /* should be overkill */
754 afmt_filt = avfilter_get_by_name("aformat");
755 if (afmt_filt == 0)
756 FATAL("audio format:could not find the filter");
757 afmt_ctx = avfilter_graph_alloc_filter(filter_graph, afmt_filt, "afmt");
758 if (afmt_ctx == 0)
759 FATAL("audio format:could not allocate the instance in the filter graph\n");
760 r = av_opt_set(afmt_ctx, "sample_fmts", av_get_frame_fmt_name(fmt),
761 AV_OPT_SEARCH_CHILDREN);
762 if (r < 0)
763 FATAL("audio format context:could to set the pcm sample format\n");
764 snprintf(rate_str, sizeof(rate_str), "%d", rate);
765 r = av_opt_set(afmt_ctx, "sample_rates", rate_str,
766 AV_OPT_SEARCH_CHILDREN);
767 if (r < 0)
768 FATAL("audio format context:could not set the pcm rate\n");
769 av_get_channel_layout_string(chans_layout_str, sizeof(chans_layout_str),
770 chans_n, chans_layout);
771 r = av_opt_set(afmt_ctx, "channel_layouts", chans_layout_str,
772 AV_OPT_SEARCH_CHILDREN);
773 if (r < 0)
774 FATAL("audio format context:could not set the layout of channels\n");
775 if (print_info)
776 POUT("audio format context:channel layout is \"%s\"\n", chans_layout_str);
777 r = avfilter_init_str(afmt_ctx, 0);
778 if (r < 0)
779 FATAL("audio format context:unable to initialize\n");
781 static void abufsink_cfg(void)
783 int r;
785 abufsink_filt = avfilter_get_by_name("abuffersink");
786 if (abufsink_filt == 0)
787 FATAL("audio buffer sink:could not find the filter\n");
788 abufsink_ctx = avfilter_graph_alloc_filter(filter_graph, abufsink_filt,
789 "sink_abuf");
790 if (abufsink_ctx == 0)
791 FATAL("audio buffer sink context:could not allocate the instance in the filter graph\n");
792 r = avfilter_init_str(abufsink_ctx, 0);
793 if (r < 0)
794 FATAL("audio buffer sink context:unable to initialize\n");
796 static void dec_ctx_cfg(AVCodecParameters *params)
798 int r;
800 dec = avcodec_find_decoder(params->codec_id);
801 if (dec == 0)
802 FATAL("ffmpeg:unable to find a proper decoder\n");
803 avcodec_free_context(&dec_ctx);
804 dec_ctx = avcodec_alloc_context3(dec);
805 if (dec_ctx == 0)
806 FATAL("ffmpeg:unable to allocate a decoder context\n");
807 /* XXX: useless ? */
808 r = avcodec_parameters_to_context(dec_ctx, params);
809 if (r < 0)
810 FATAL("ffmpeg:unable to apply codec parameters in codec context\n");
811 /* XXX: ffmpeg thread count default is 1, set to 0 = auto */
812 dec_ctx->thread_count = 0;
813 r = avcodec_open2(dec_ctx, dec, 0);
814 if (r < 0)
815 FATAL("ffmpeg:unable to open the decoder context\n");
817 static void filter_graph_cfg(
818 enum AVSampleFormat src_fmt, int src_rate, int src_chans_n,
819 uint64_t src_chans_layout,
820 bool muted, double vol,
821 enum AVSampleFormat dst_fmt, int dst_rate, int dst_chans_n,
822 uint64_t dst_chans_layout, bool print_info)
824 int r;
825 char *dump_str;
827 avfilter_graph_free(&filter_graph);
829 filter_graph = avfilter_graph_alloc();
830 if (filter_graph == 0)
831 FATAL("unable to create filter graph\n");
832 abufsrc_cfg(src_fmt, src_rate, src_chans_n, src_chans_layout,
833 print_info);
834 vol_cfg(muted, vol);
835 afmt_cfg(dst_fmt, dst_rate, dst_chans_n, dst_chans_layout, print_info);
836 abufsink_cfg();
837 r = avfilter_link(abufsrc_ctx, 0, vol_ctx, 0);
838 if (r < 0)
839 FATAL("unable to connect the audio buffer source filter to the volume filter\n");
840 r = avfilter_link(vol_ctx, 0, afmt_ctx, 0);
841 if (r < 0)
842 FATAL("unable to connect the volume filter to the audio format filter\n");
843 r = avfilter_link(afmt_ctx, 0, abufsink_ctx, 0);
844 if (r < 0)
845 FATAL("unable to connect the audio format filter to the audio buffer sink filter\n");
846 r = avfilter_graph_config(filter_graph, 0);
847 if (r < 0)
848 FATAL("unable to configure the filter graph\n");
849 /*--------------------------------------------------------------------*/
850 if (!print_info)
851 return;
852 dump_str = avfilter_graph_dump(filter_graph, 0);
853 if (dump_str == 0) {
854 WARNING("unable to get a filter graph description\n");
855 return;
857 POUT("GRAPH START-------------------------------------------------------\n");
858 POUT("%s", dump_str);
859 av_free(dump_str);
860 POUT("GRAPH END---------------------------------------------------------\n");
862 #define DONT_PRINT_INFO false
863 static void filt_flush(void)
865 enum AVSampleFormat dst_fmt;
866 int dst_rate;
867 int dst_chans_n;
868 uint64_t dst_chans_layout;
870 av_frames_unref(filt_frs.av);
871 filt_frs.pcm_written_ufrs_n = 0;
873 pcm2ff(pcm_g, &dst_fmt, &dst_rate, &dst_chans_n, &dst_chans_layout,
874 DONT_PRINT_INFO);
875 filter_graph_cfg(
876 dec_ctx->sample_fmt, dec_ctx->sample_rate,
877 dec_ctx->channels, dec_ctx->channel_layout,
878 filt_frs.muted, filt_frs.vol,
879 dst_fmt, dst_rate, dst_chans_n, dst_chans_layout,
880 DONT_PRINT_INFO);
882 #undef DONT_PRINT_INFO
883 static void stdin_tty_init_once(void)
885 int r;
886 struct termios tio_new;
887 struct termios tio_chk;
889 r = isatty(0);
890 if (r == 0) {
891 POUT("input:standard input is not a terminal\n");
892 return;
894 memset(&stdin_tio_save, 0, sizeof(stdin_tio_save));
895 r = tcgetattr(0, &stdin_tio_save);
896 if (r == -1)
897 FATAL("input:unable to get the current standard input terminal line attributes\n");
898 tio_new = stdin_tio_save;
899 tio_new.c_lflag &= ~(ICANON|ECHO);
900 tio_new.c_cc[VMIN] = 1; /* 1 "char", could be bytes from a utf8 code point */
901 tio_new.c_cc[VTIME] = 0;
902 r = tcsetattr(0, TCSANOW, &tio_new);
903 stdin_tty_cfg_modified = true;
904 if (r == -1)
905 FATAL("input:unable to set all standard input terminal line\n");
906 r = tcgetattr(0, &tio_chk);
907 if (r == -1)
908 FATAL("input:unable to get the current standard input terminal line attributes for checking\n");
909 r = memcmp(&tio_chk, &tio_new, sizeof(tio_chk));
910 if (r != 0)
911 FATAL("input:setting the wanted terminal line attributes failed\n");
913 static void stdin_flags_init_once(void)
915 int r;
916 /* switch the standard input to non-blocking */
917 r = fcntl(0, F_GETFL);
918 if (r == -1)
919 FATAL("input:unable to get the file flags of the standard input\n");
920 stdin_flags_save = r;
921 r |= O_NONBLOCK;
922 r = fcntl(0, F_SETFL, r);
923 if (r == -1)
924 FATAL("input:unable to set non-blocking operations on the standard input\n");
926 static void stdout_init_once(void)
928 int r;
930 r = isatty(1);
931 if (r == 0) {
932 POUT("output:standard output not is not a terminal\n");
933 stdout_is_tty = false;
934 return;
936 stdout_is_tty = true;
939 * block as much as possible.
940 * handle only async "usual" sigs, with sync signalfd.
941 * allow some signals to go thru though.
942 * always presume the process "controlling terminal" is different than the
943 * terminal connected on standard input and standard output
945 static void sigs_init_once(void)
947 int r;
948 sigset_t sset;
950 r = sigfillset(&sset);
951 if (r == -1)
952 FATAL("unable to get a full signal mask\n");
953 /* the "controlling terminal" line asks for a core dump, leave it be */
954 r = sigdelset(&sset, SIGQUIT);
955 if (r == -1)
956 FATAL("unable to remove SIGQUIT from our signal mask\n");
957 r = pthread_sigmask(SIG_SETMASK, &sset, 0);
958 if (r != 0)
959 FATAL("unable to \"block\" \"all\" signals\n");
960 /* from here, we "steal" signals with signalfd */
961 r = sigemptyset(&sset);
962 if (r == -1)
963 FATAL("unable to get an empty signal mask\n");
964 /* we are asked nicely to terminate */
965 r = sigaddset(&sset, SIGTERM);
966 if (r == -1)
967 FATAL("unable to add SIGTERM to our signal mask\n");
968 /* the "controlling terminal" line (^c) asks nicely to terminate */
969 r = sigaddset(&sset, SIGINT);
970 if (r == -1)
971 FATAL("unable to add SIGINT to our signal mask\n");
972 r = signalfd(-1, &sset, SFD_NONBLOCK);
973 if (r == -1)
974 FATAL("unable to get a signalfd file descriptor\n");
975 sig_fd = r;
977 static void evt_init_once(void)
979 int r;
980 u8 i;
981 struct epoll_event evt;
983 ep_fd = epoll_create1(0);
984 if (ep_fd == -1)
985 FATAL("unable to create the epoll file descriptor\n");
986 /*--------------------------------------------------------------------*/
987 /* signals */
988 evt.events = EPOLLIN;
989 evt.data.fd = sig_fd;
990 r = epoll_ctl(ep_fd, EPOLL_CTL_ADD, sig_fd, &evt);
991 if (r == -1)
992 FATAL("unable to add the signalfd file descriptior to the epoll file descriptor\n");
993 /*--------------------------------------------------------------------*/
994 /* standard input/terminal */
995 evt.events = EPOLLIN;
996 evt.data.fd = 0;
997 r = epoll_ctl(ep_fd, EPOLL_CTL_ADD, 0, &evt);
998 if (r == -1)
999 FATAL("unable to add the standard input to the epoll file descriptor\n");
1000 /*--------------------------------------------------------------------*/
1001 /* the timer in charge of accounting unknown esc seq */
1002 evt.events = EPOLLIN;
1003 evt.data.fd = input_timer_fd;
1004 r = epoll_ctl(ep_fd, EPOLL_CTL_ADD, input_timer_fd, &evt);
1005 if (r == -1)
1006 FATAL("unable to add the timer file descriptor accounting for unknown escape sequences to epoll file descriptor\n");
1008 static void input_state_init_once(void)
1010 input_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
1011 if (input_timer_fd == -1)
1012 FATAL("unable to get a timer file descriptor\n");
1014 input_esc_seq_mode = false;
1016 esc_seq_next_byte = 0;
1017 utf8_cp_next_byte = 0;
1019 static void input_utf8_cp_bind_cmd(void)
1021 u16 i;
1023 i = 0;
1024 loop {
1025 struct tty_bind_t *bind;
1027 if (i == ARRAY_N(tty_binds))
1028 break;
1029 bind = tty_binds + i;
1031 /* exclude esc seq binds */
1032 if ((bind->c[0] != 0x1b) && (utf8_cp_sz == bind->sz)) {
1033 int r;
1035 r = memcmp(utf8_cp, bind->c, utf8_cp_sz);
1036 if (r == 0) {
1037 bind->cmd();
1038 return;
1041 ++i;
1045 * to exit the esc seq input mode, we could implement a smart tree in order to
1046 * know if it is not possible to get any defined esc seq, or we could already
1047 * use the maximum bind esc seq sz
1049 static bool input_esc_seq_bind_cmd(void)
1051 u16 i;
1053 i = 0;
1054 loop {
1055 struct tty_bind_t *bind;
1057 if (i == ARRAY_N(tty_binds))
1058 break;
1059 bind = tty_binds + i;
1060 /* only esc seq binds */
1061 if ((bind->c[0] == 0x1b) && (esc_seq_sz == bind->sz)) {
1062 int r;
1064 r = memcmp(esc_seq, bind->c, esc_seq_sz);
1065 if (r == 0) {
1066 bind->cmd();
1067 return true;
1070 ++i;
1072 return false;
1074 static void input_byte_esc_seq(void)
1076 int r;
1077 struct itimerspec t;
1079 if (input_b == 0x1b) {
1080 esc_seq[0] = 0x1b;
1081 esc_seq_next_byte = 1;
1082 memset(&t, 0, sizeof(t));
1083 t.it_value.tv_sec = INPUT_ESC_SEQ_TIMEOUT_SECS_N;
1084 r = timerfd_settime(input_timer_fd, 0, &t, 0);
1085 if (r == -1)
1086 FATAL("unable to arm the timer to account for unknown input escape sequence from the terminal\n");
1087 return;
1089 esc_seq[esc_seq_next_byte] = input_b;
1090 ++esc_seq_next_byte;
1091 if (!input_esc_seq_bind_cmd() && (esc_seq_next_byte != STR_SZ))
1092 return;
1093 memset(&t, 0, sizeof(t));
1094 r = timerfd_settime(input_timer_fd, 0, &t, 0);
1095 if (r == -1)
1096 FATAL("unable to disarm the timer used to account for unknown input escape sequence from the terminal\n");
1097 esc_seq_next_byte = 0;
1098 input_esc_seq_mode = false;
1099 utf8_cp_next_byte = 0;
1101 static void input_byte_utf8_cp(void)
1103 if ((input_b & 0x80) != 0) { /* utf8 cp > 0x7f */
1104 if ((input_b & 0x40) != 0) { /* utf8 cp start byte */
1105 utf8_cp[0] = input_b;
1106 utf8_cp_next_byte = 1;
1107 if ((input_b & 0x20) == 0)
1108 utf8_cp_sz = 2;
1109 else if ((input_b & 0x10) == 0)
1110 utf8_cp_sz = 3;
1111 else /* if ((input_b & 0x08) == 0) */
1112 utf8_cp_sz = 4; /* must be 4 */
1113 return;
1115 /* (b & 0x40) == 0, utf8 cp continuation byte */
1117 * no start byte, discard (but should skip all following
1118 * continuation bytes and send the utf8 "invalid" cp)
1120 if (utf8_cp_next_byte == 0)
1121 return;
1122 utf8_cp[utf8_cp_next_byte] = input_b;
1123 ++utf8_cp_next_byte;
1124 if (utf8_cp_next_byte != utf8_cp_sz)
1125 return;
1126 } else {/* ascii 0x00 - 0x7f */
1127 if (input_b == 0x1b) {/* esc */
1128 /* change state and process the esc byte */
1129 input_esc_seq_mode = true;
1130 input_byte_esc_seq();
1131 return;
1133 utf8_cp[0] = input_b;
1134 utf8_cp_sz = 1;
1136 input_utf8_cp_bind_cmd();
1138 /* we are either reading an utf8 cp or an esc seq */
1139 static void evt_input_drain(void) { loop
1141 ssize_t r;
1143 errno = 0;
1144 r = read(0, &input_b, 1);
1145 if (r == -1) {
1146 if (errno == EAGAIN) /* no more input data */
1147 break;
1148 if (errno == EINTR) /* restart manually the call */
1149 continue;
1150 FATAL("an error occured while reading the input\n");
1152 if (r == 0)
1153 FATAL("input end of file\n");
1154 if (!input_esc_seq_mode)
1155 input_byte_utf8_cp();
1156 else
1157 input_byte_esc_seq();
1159 static void evt_sigs(void)
1161 int r;
1162 struct signalfd_siginfo siginfo;
1164 /* no short reads */
1165 r = read(sig_fd, &siginfo, sizeof(siginfo));
1166 if (r != sizeof(siginfo))
1167 FATAL("unable to read signal information\n");
1169 switch (siginfo.ssi_signo) {
1170 case SIGTERM:
1171 EXIT("received SIGTERM\n");
1172 case SIGINT:
1173 EXIT("received SIGINT\n");
1174 default:
1175 WARNING("signal handle:unwanted signal %d received, discarding\n", siginfo.ssi_signo);
1178 static void evt_timer(void)
1180 int r;
1181 struct itimerspec t;
1183 memset(&t, 0, sizeof(t));
1184 r = timerfd_settime(input_timer_fd, 0, &t, 0);
1185 if (r == -1)
1186 FATAL("unable to disarm the timer used to account for unknown input escape sequences from the terminal\n");
1188 esc_seq_next_byte = 0;
1189 input_esc_seq_mode = false;
1191 utf8_cp_next_byte = 0;
1193 static void evt_handle(struct epoll_event *evt, bool *pcm_evt)
1195 if (evt->data.fd == 0) {
1196 if ((evt->events & EPOLLIN) != 0)
1197 evt_input_drain();
1198 else
1199 FATAL("event loop wait:input:unexpected event\n");
1200 } else if (evt->data.fd == sig_fd) {
1201 if ((evt->events & EPOLLIN) != 0)
1202 evt_sigs();
1203 else
1204 FATAL("event loop wait:signal:unexpected event\n");
1205 } else if (evt->data.fd == input_timer_fd) {
1206 if ((evt->events & EPOLLIN) != 0)
1207 evt_timer();
1208 else
1209 FATAL("event loop wait:timer:unexpected event\n");
1210 } else { /* only update alsa fds */
1211 u8 i;
1213 i = 0;
1214 loop {
1215 if (i == pcm_pollfds_n)
1216 break;
1218 if (evt->data.fd == pcm_pollfds[i].fd) {
1219 pcm_pollfds[i].revents = evt->events;
1220 *pcm_evt = true;
1222 ++i;
1226 /* fill the dec with pkts */
1227 static void dec_fill(void) { loop
1229 u8 sd;
1230 u8 rd;
1231 u8 next_sd;
1232 int r;
1234 rd = atomic_load(&cb.rd);
1235 sd = atomic_load(&cb.sd);
1236 /* at start, we must read pkt before we can send one */
1237 if (rd == sd)
1238 break;
1239 r = avcodec_send_packet(dec_ctx, cb.pkts[sd]);
1240 /* dec is full and the pkt was rejected, or the decoder is in EOF */
1241 if (r == AVERROR(EAGAIN) || r == AVERROR_EOF)
1242 break;
1243 else if (r != 0)
1244 FATAL("ffmpeg:error while sending the packet to the decoder\n");
1245 /* r == 0 */
1246 av_packet_unref(cb.pkts[sd]);
1247 next_sd = sd + 1;
1248 atomic_store(&cb.sd, next_sd);
1251 * return true if we have 1 dec_frs.av, false if no more dec_frs.av
1252 * will come
1254 static bool dec_frs_get(void) { loop
1256 int r;
1258 dec_fill();
1260 /* will unref the dec_frs.av bufs for us */
1261 r = avcodec_receive_frames(dec_ctx, dec_frs.av);
1262 if (r == AVERROR(EAGAIN))
1263 continue;
1264 else if (r == 0) {
1265 if (dec_frs.av->pts != AV_NOPTS_VALUE)
1266 dec_frs.most_recent_ts = dec_frs.av->pts;
1267 else if (dec_frs.av->pkt_dts != AV_NOPTS_VALUE)
1268 dec_frs.most_recent_ts = dec_frs.av->pkt_dts;
1269 else
1270 dec_frs.most_recent_ts = dec_frs.av->pkt_dts;
1271 return true; /* "return" the current dec_frs.av */
1272 } else if (r == AVERROR_EOF) {
1273 POUT("ffmpeg:last decoder frames reached (receiving)\n");
1274 return false;
1276 FATAL("ffmpeg:error while receiving frames from the decoder\n");
1278 static bool filt_frs_get(void) { loop
1280 int r;
1282 if (!filt_frs.no_more_dec_frs) {
1283 if(!dec_frs_get()) {
1284 filt_frs.no_more_dec_frs = true;
1286 r = av_buffersrc_add_frame_flags(abufsrc_ctx,
1287 0 , AV_BUFFERSRC_FLAG_PUSH
1288 | AV_BUFFERSRC_FLAG_KEEP_REF);
1289 if (r < 0)
1290 FATAL("ffmpeg:unable to notify the end of data to the filter source audio buffer context\n");
1291 } else {
1293 * the dec_frs bufs will be unref in
1294 * avcodec_receive_frames
1296 r = av_buffersrc_add_frame_flags(abufsrc_ctx,
1297 dec_frs.av, AV_BUFFERSRC_FLAG_PUSH
1298 | AV_BUFFERSRC_FLAG_KEEP_REF);
1299 if (r < 0)
1300 FATAL("ffmpeg:unable to submit the decoder frames to the filter source audio buffer context\n");
1304 * the last dec_frs should switch the filter in draining mode,
1305 * and filt_frs.av won't matter.
1307 r = av_buffersink_get_frame(abufsink_ctx, filt_frs.av);
1308 if (r == AVERROR(EAGAIN)) {
1310 * won't happen if no more dec frs are available thanks
1311 * to eof propagation along the processing graph, in theory
1313 continue;
1314 } else if (r >= 0) {
1315 filt_frs.pcm_written_ufrs_n = 0;
1316 return true;
1317 } else if (r == AVERROR_EOF) {
1318 POUT("ffmpeg:last filter frs reached (getting)\n");
1319 return false;
1321 FATAL("ffmpeg:error while getting frs from the filter\n");
1323 #define NO 0
1324 static void chans_buf_init(u8 **chans_buf, int start_fr_idx)
1326 int is_planar_fmt;
1327 int sample_bytes_n;
1329 sample_bytes_n = av_get_bytes_per_sample(filt_frs.av->format);
1331 is_planar_fmt = av_frame_fmt_is_planar(filt_frs.av->format);
1332 if (is_planar_fmt == NO) { /* or is pcm interleaved */
1333 int fr_bytes_n;
1335 fr_bytes_n = sample_bytes_n * filt_frs.av->channels;
1336 chans_buf[0] = (u8*)filt_frs.av->data[0] + start_fr_idx
1337 * fr_bytes_n;
1338 } else { /* ffmpeg planar or pcm noninterleaved */
1339 int p;
1341 p = 0;
1342 loop {
1343 if (p == filt_frs.av->channels)
1344 break;
1345 chans_buf[p] = (u8*)filt_frs.av->data[p]
1346 + start_fr_idx * sample_bytes_n;
1347 ++p;
1351 #undef NO
1352 #define NO 0
1353 static void chans_buf_inc(u8 **chans_buf, int frs_inc)
1355 int is_planar_fmt;
1356 int sample_bytes_n;
1358 sample_bytes_n = av_get_bytes_per_sample(filt_frs.av->format);
1360 is_planar_fmt = av_frame_fmt_is_planar(filt_frs.av->format);
1361 if (is_planar_fmt == NO) { /* or is pcm interleaved */
1362 int fr_bytes_n;
1364 fr_bytes_n = sample_bytes_n * filt_frs.av->channels;
1365 chans_buf[0] += frs_inc * fr_bytes_n;
1366 } else { /* ffmpeg planar or pcm noninterleaved */
1367 u8 p;
1369 p = 0;
1370 loop {
1371 if (p == filt_frs.av->channels)
1372 break;
1373 chans_buf[p] += frs_inc * sample_bytes_n;
1374 ++p;
1378 #undef NO
1379 #define NO 0
1380 static void pcm_silence_frs_write(snd_pcm_uframes_t ufrs_n)
1382 int is_planar_fmt;
1384 if (ufrs_n == 0)
1385 return;
1387 is_planar_fmt = av_frame_fmt_is_planar(filt_frs.av->format);
1388 if (is_planar_fmt == NO)
1389 (void)snd_pcm_writei(pcm_g, silence_bufs[0], ufrs_n);
1390 else
1391 (void)snd_pcm_writen(pcm_g, silence_bufs, ufrs_n);
1393 #undef NO
1394 #define NO 0
1395 static void pcm_filt_frs_write(snd_pcm_uframes_t ufrs_n) { loop
1397 u8 chan_buf;
1398 u8 *chans_buf[AV_NUM_DATA_POINTERS];
1399 snd_pcm_sframes_t r0;
1400 snd_pcm_uframes_t ufrs_to_write_n;
1401 snd_pcm_uframes_t filt_frs_remaining_ufrs_n; /* for clarity */
1403 if (ufrs_n == 0)
1404 return;
1406 /* create the chan buf pointers */
1407 chans_buf_init(chans_buf, (int)filt_frs.pcm_written_ufrs_n);
1409 filt_frs_remaining_ufrs_n = (snd_pcm_uframes_t)filt_frs.av->frames_n
1410 - filt_frs.pcm_written_ufrs_n;
1411 if (filt_frs_remaining_ufrs_n > ufrs_n)
1412 ufrs_to_write_n = ufrs_n;
1413 else
1414 ufrs_to_write_n = filt_frs_remaining_ufrs_n;
1416 loop {
1417 int is_planar_fmt;
1418 snd_pcm_uframes_t written_ufrs_n;
1420 is_planar_fmt = av_frame_fmt_is_planar(filt_frs.av->format);
1421 if (is_planar_fmt == NO)
1422 r0 = snd_pcm_writei(pcm_g, chans_buf[0],
1423 ufrs_to_write_n);
1424 else
1425 r0 = snd_pcm_writen(pcm_g, (void**)chans_buf,
1426 ufrs_to_write_n);
1427 if (r0 < 0) {
1428 if (r0 == -EAGAIN) /* return to epoll */
1429 return;
1430 else if (r0 == -EPIPE || r0 == -ESTRPIPE) {
1431 /* underrun or suspended */
1432 int r1;
1434 r1 = snd_pcm_recover(pcm_g, (int)r0, 0);
1435 if (r1 == 0) {
1436 WARNING("alsa:pcm recovered going back to epoll\n");
1437 return; /* recovered, go back to epoll */
1439 FATAL("alsa:unable to recover from suspend/underrun\n");
1441 FATAL("alsa:fatal/unhandled error while writing the frames\n");
1443 /* r0 >= 0 */
1444 written_ufrs_n = (snd_pcm_uframes_t)r0;
1445 filt_frs.pcm_written_ufrs_n += written_ufrs_n;
1446 ufrs_n -= written_ufrs_n;
1447 ufrs_to_write_n -= written_ufrs_n;
1449 chans_buf_inc(chans_buf, (int)written_ufrs_n);
1451 if (ufrs_to_write_n == 0)
1452 break;
1454 if (filt_frs.pcm_written_ufrs_n
1455 == (snd_pcm_uframes_t)filt_frs.av->frames_n) {
1456 av_frame_unref(filt_frs.av);
1458 if (!filt_frs_get()) { /* no more filt frs ? */
1459 loop { /* spinning */
1460 int r1;
1461 snd_pcm_state_t state;
1463 r1 = snd_pcm_drain(pcm_g);
1464 if (r1 == 0)
1465 break;
1466 if (r1 == -EAGAIN)
1467 continue;
1469 * the pcm state can change asynchronously,
1470 * and if the draining was successful, the
1471 * pcm should be in SETUP state, and in this
1472 * state, snd_pcm_drain _must_ fail (direct
1473 * from alsa devs)
1475 state = snd_pcm_state(pcm_g);
1476 if (state == SND_PCM_STATE_SETUP)
1477 break;
1479 /**********************************************/
1480 EXIT("finished playing\n");
1481 /**********************************************/
1485 #undef NO
1486 static void evt_pcm_write(void)
1488 snd_pcm_sframes_t r0;
1489 /* try only 2 times */
1490 r0 = snd_pcm_avail(pcm_g);
1491 if (r0 < 0) {
1492 if (r0 == -EPIPE || r0 == -ESTRPIPE) {
1493 /* underrun or suspended */
1494 int r1;
1496 r1 = snd_pcm_recover(pcm_g, (int)r0, 0);
1497 if (r1 == 0) {
1498 WARNING("alsa:pcm recovered retrying to get some available frames\n");
1499 r0 = snd_pcm_avail(pcm_g);
1500 if (r0 < 0)
1501 FATAL("alsa:unable to get some available frames after recovery\n");
1502 } else
1503 FATAL("alsa:unable to recover from suspend/underrun\n");
1504 } else
1505 FATAL("alsa:error getting some available frames\n");
1508 if (paused)
1509 pcm_silence_frs_write((snd_pcm_uframes_t)r0);
1510 else
1511 pcm_filt_frs_write((snd_pcm_uframes_t)r0);
1513 #define EPOLL_EVTS_N 8 /* why not */
1514 static void evts_loop(void)
1516 int fds_n;
1517 int fd_idx;
1518 struct epoll_event evts[EPOLL_EVTS_N];
1519 bool pcm_evt;
1520 int r;
1521 short pcm_evts;
1523 errno = 0;
1524 memset(evts, 0, sizeof(evts));
1525 fds_n = epoll_wait(ep_fd, evts, EPOLL_EVTS_N, -1);
1526 if (fds_n == -1) {
1527 if (errno == EINTR) {
1528 WARNING("event loop wait:was interrupted by a signal\n");
1529 return;
1531 FATAL("event loop wait:an error occured\n");
1533 pcm_evt = false;
1534 fd_idx = 0;
1535 loop {
1536 if (fd_idx == fds_n)
1537 break;
1538 evt_handle(&evts[fd_idx], &pcm_evt);
1539 ++fd_idx;
1541 if (!pcm_evt)
1542 return;
1544 * since alsa could use several file descriptors, only once the
1545 * pollfds were properly updated we can actually know we got
1546 * something from alsa
1548 r = snd_pcm_poll_descriptors_revents(pcm_g, pcm_pollfds, pcm_pollfds_n,
1549 &pcm_evts);
1550 if (r != 0)
1551 FATAL("alsa:error processing the poll file descriptors\n");
1552 if ((pcm_evts & ~POLLOUT) != 0)
1553 FATAL("alsa:unexpected events\n");
1554 if ((pcm_evts & POLLOUT) != 0)
1555 evt_pcm_write();
1557 #undef EPOLL_EVTS_N
1558 static void silence_bufs_init_once(void)
1560 memset(silence_bufs, 0, sizeof(silence_bufs));
1562 static void silence_bufs_cfg(snd_pcm_t *pcm, bool print_info)
1564 int r;
1565 snd_pcm_hw_params_t *hw_params;
1566 snd_pcm_uframes_t buf_ufrs_n;
1567 snd_pcm_format_t fmt;
1568 snd_pcm_access_t access;
1569 unsigned int chans_n;
1570 u8 c;
1572 r = snd_pcm_hw_params_malloc(&hw_params);
1573 if (r < 0)
1574 FATAL("silence:alsa:unable to allocate memory for a hardware parameters container\n");
1575 r = snd_pcm_hw_params_current(pcm, hw_params);
1576 if (r != 0)
1577 FATAL("silence:alsa:unable to get the pcm hardware parameters\n");
1578 r = snd_pcm_hw_params_get_buffer_size(hw_params, &buf_ufrs_n);
1579 if (r < 0)
1580 FATAL("silence:alsa:unable to get the number of frames in the pcm buffer\n");
1581 r = snd_pcm_hw_params_get_format(hw_params, &fmt);
1582 if (r < 0)
1583 FATAL("silence:alsa:unable to get the pcm format\n");
1584 r = snd_pcm_hw_params_get_access(hw_params, &access);
1585 if (r < 0)
1586 FATAL("silence:alsa:unable to get the pcm access mode\n");
1587 r = snd_pcm_hw_params_get_channels(hw_params, &chans_n);
1588 if (r < 0)
1589 FATAL("silence:alsa:unable to get the pcm number of channels\n");
1590 /* wipe silence bufs first */
1591 c = 0;
1592 loop {
1593 if (c == AV_NUM_DATA_POINTERS)
1594 break;
1595 if (silence_bufs[c] != 0) {
1596 free(silence_bufs[c]);
1597 silence_bufs[c] = 0;
1599 ++c;
1601 if (access == SND_PCM_ACCESS_RW_INTERLEAVED
1602 || access == SND_PCM_ACCESS_MMAP_INTERLEAVED) {
1603 ssize_t buf_bytes_n;
1605 buf_bytes_n = snd_pcm_frames_to_bytes(pcm,
1606 (snd_pcm_sframes_t)buf_ufrs_n);
1607 if (buf_bytes_n <= 0)
1608 FATAL("silence:alsa:interleaved:unable to get the pcm number of bytes of all buffer frames\n");
1609 silence_bufs[0] = malloc((size_t)buf_bytes_n);
1610 if (silence_bufs[0] == 0)
1611 FATAL("silence:interleaved:unable to allocate the silence buffer of %d bytes\n", (int)buf_bytes_n);
1612 if (print_info)
1613 POUT("silence:interleaved:buffer of %d bytes is allocated\n", (int)buf_bytes_n);
1614 r = snd_pcm_format_set_silence(fmt, silence_bufs[0],
1615 (unsigned int)buf_ufrs_n);
1616 if (r < 0)
1617 FATAL("silence:interleaved:unable to fill with silence the buffer\n");
1618 POUT("silence:interleaved:silence buffer filled with %u silence frames\n", buf_ufrs_n);
1619 } else if (access == SND_PCM_ACCESS_RW_NONINTERLEAVED
1620 || access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED) {
1621 ssize_t buf_bytes_n;
1622 long buf_samples_n;
1624 buf_samples_n = (long)buf_ufrs_n;
1625 buf_bytes_n = snd_pcm_samples_to_bytes(pcm, buf_samples_n);
1626 if (buf_bytes_n <= 0)
1627 FATAL("silence:alsa:non interleaved:unable to get the pcm number of total bytes of all buffer samples\n");
1628 c = 0;
1629 loop {
1630 if (c == chans_n)
1631 break;
1632 silence_bufs[c] = malloc((size_t)buf_bytes_n);
1633 if (silence_bufs[c] == 0)
1634 FATAL("silence:non interleaved:unable to allocate silence buffer %u of %d bytes\n", c, (int)buf_bytes_n);
1635 r = snd_pcm_format_set_silence(fmt, silence_bufs[c],
1636 (unsigned int)buf_samples_n);
1637 if (r < 0)
1638 FATAL("silence:non interleaved:unable to fill with silence the buffer\n");
1639 if (print_info)
1640 POUT("silence:non interleaved:buffer[%u] of %d bytes is allocated\n", c, (int)buf_bytes_n);
1641 ++c;
1643 if (print_info)
1644 POUT("silence:non interleaved:allocated %u silence buffers for %u frames\n", chans_n, (unsigned int)buf_ufrs_n);
1645 } else
1646 FATAL("silence:the pcm access type is not supported\n");
1647 snd_pcm_hw_params_free(hw_params);
1649 static void ffmpeg_log_stdout(void *a, int b, const char *fmt, va_list ap)
1651 vprintf(fmt, ap);
1653 static void usage(void)
1655 POUT("\
1656 npa [-p alsa pcm] [-v volume(0..100)] [-h] url\n");
1658 static void opts_parse(int argc, u8 **args, u8 **url, u8 **pcm_str,
1659 double *initial_vol)
1661 int i;
1662 int url_idx;
1664 i = 1;
1665 url_idx = -1;
1666 loop {
1667 if (i == argc)
1668 break;
1669 if (strcmp("-p", args[i]) == 0) {
1670 if ((i + 1) == argc)
1671 FATAL("-p:alsa pcm is missing\n");
1672 *pcm_str = args[i + 1];
1673 POUT("-p:alsa pcm \"%s\"\n", *pcm_str);
1674 i += 2;
1675 } else if (strcmp("-v", args[i]) == 0) {
1676 unsigned long vol_ul;
1678 if ((i + 1) == argc)
1679 FATAL("-v:initial volume option is missing\n");
1680 vol_ul = strtoul(args[i + 1], 0, 10);
1681 if (vol_ul < 0 || 100 < vol_ul)
1682 FATAL("-v:invalid volume value %lu (0..100)\n", vol_ul);
1683 *initial_vol = (double)vol_ul / 100.0;
1684 POUT("-v:using initial volume %f\n", *initial_vol);
1685 i += 2;
1686 } else if (strcmp("-h", args[i]) == 0) {
1687 usage();
1688 exit(0);
1689 } else {
1690 url_idx = i;
1691 ++i;
1694 if (url_idx == -1)
1695 FATAL("missing url\n");
1696 *url = args[url_idx];
1697 POUT("playing ####%s####\n", *url);
1699 #define RESET_DONE 0
1700 static void cb_init_once(void)
1702 u8 i;
1704 i = 0;
1705 loop {
1706 cb.pkts[i] = av_packet_alloc();
1707 if (cb.pkts[i] == 0)
1708 FATAL("unable to allocate a packet reference for the circular buffer\n");
1709 if (i == U8_MAX)
1710 break;
1711 ++i;
1713 atomic_store(&cb.rd, 0);
1714 atomic_store(&cb.sd, 0);
1715 atomic_store(&cb.st_idx, -1);
1716 atomic_store(&cb.reset, RESET_DONE);
1718 #undef RESET_DONE
1719 static void filter_graph_init_once(void)
1721 filter_graph = 0;
1722 abufsrc_ctx = 0;
1723 abufsrc_filt = 0;
1724 vol_ctx = 0;
1725 vol_filt = 0;
1726 afmt_ctx = 0;
1727 afmt_filt = 0;
1728 abufsink_ctx = 0;
1729 abufsink_filt = 0;
1730 /* floating point strs are localized... */
1731 snprintf(double_zero_l10n_str, sizeof(double_zero_l10n_str), "%f", 0.);
1732 memset(&filt_frs, 0, sizeof(filt_frs));
1733 filt_frs.av = av_frames_alloc();
1734 if (filt_frs.av == 0)
1735 FATAL("ffmpeg:unable to allocate a filtered frames structure\n");
1737 static void dec_init_once(void)
1739 dec = 0;
1740 dec_ctx = 0;
1741 dec_frs.av = av_frames_alloc();
1742 if (dec_frs.av == 0)
1743 FATAL("ffmpeg:unable to allocate a decoded frames structure\n");
1744 dec_frs.most_recent_ts = AV_NOPTS_VALUE;
1746 static void rd_thd_init_once(void)
1748 rd_thd_pkt = av_packet_alloc();
1749 if (rd_thd_pkt == 0)
1750 FATAL("ffmpeg:unable to allocate a packet for the read thread\n");
1752 static void fmt_init_once(u8 *url)
1754 int r;
1756 fmt_ctx = 0;
1757 r = avformat_open_input(&fmt_ctx, url, NULL, NULL);
1758 if (r < 0)
1759 FATAL("ffmpeg:unable to open \"%s\"\n", url);
1760 /* probe beyond the header, if any */
1761 r = avformat_find_stream_info(fmt_ctx, 0);
1762 if (r < 0)
1763 FATAL("ffmpeg:unable to probe \"%s\"\n", url);
1764 r = pthread_mutex_init(&fmt_ctx_mutex, 0);
1765 if (r != 0)
1766 FATAL("unable to init the format context mutex\n");
1768 static void pcm_init_once(u8 *pcm_str)
1770 int r;
1772 r = snd_output_stdio_attach(&pcm_pout, stdout, 0);
1773 if (r < 0)
1774 FATAL("alsa:unable to attach stdout\n");
1775 r = snd_output_stdio_attach(&pcm_perr, stderr, 0);
1776 if (r < 0)
1777 FATAL("alsa:unable to attach stderr\n");
1778 r = snd_pcm_open(&pcm_g, pcm_str, SND_PCM_STREAM_PLAYBACK,
1779 SND_PCM_NONBLOCK);
1780 if (r < 0) {
1781 if (r == -EAGAIN)
1782 FATAL("alsa:\"%s\" pcm is already in use\n", pcm_str);
1783 else
1784 FATAL("alsa:unable to open \"%s\" pcm for playback\n", pcm_str);
1786 r = snd_pcm_poll_descriptors_count(pcm_g);
1787 POUT("alsa:have %d poll file descriptors\n", r);
1788 if ((r <= 0) || (r > PCM_POLLFDS_N_MAX))
1789 FATAL("alsa:invalid count of alsa poll file descriptors\n");
1790 pcm_pollfds_n =(u8)r;
1791 memset(pcm_pollfds, 0, sizeof(pcm_pollfds));
1792 snd_pcm_poll_descriptors(pcm_g, pcm_pollfds, PCM_POLLFDS_N_MAX);
1794 static void init_once(u8 *url, u8 *pcm_str)
1796 stdin_tty_cfg_modified = false;
1797 current_url = url;
1798 paused = false;
1800 sigs_init_once();
1801 stdin_tty_init_once();
1802 stdin_flags_init_once();
1803 stdout_init_once();
1804 input_state_init_once();
1805 cb_init_once();
1806 filter_graph_init_once();
1807 silence_bufs_init_once();
1808 dec_init_once();
1809 pcm_init_once(pcm_str);
1810 rd_thd_init_once();
1811 fmt_init_once(url);
1812 evt_init_once();
1814 static int find_best_st(void)
1816 int r;
1818 r = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, 0, 0);
1819 if (r < 0)
1820 FATAL("ffmpeg:no audio stream found\n");
1821 return r;
1823 static void pcm_hw_chans_n_decide(snd_pcm_t *pcm,
1824 snd_pcm_hw_params_t *pcm_hw_params, unsigned int chans_n)
1826 int r;
1827 unsigned int chans_n_max;
1828 unsigned int chans_n_min;
1830 r = snd_pcm_hw_params_test_channels(pcm, pcm_hw_params, chans_n);
1831 if (r == 0) {
1832 r = snd_pcm_hw_params_set_channels(pcm, pcm_hw_params, chans_n);
1833 if (r != 0)
1834 FATAL("alsa:unable to restrict pcm device to %u channels, count which was successfully tested\n", chans_n);
1835 POUT("alsa:using %u channels\n", chans_n);
1836 return;
1838 POUT("alsa:unable to use %u channels\n", chans_n);
1839 /* try to use the max chans n the pcm can */
1840 r = snd_pcm_hw_params_get_channels_max(pcm_hw_params, &chans_n_max);
1841 if (r != 0)
1842 FATAL("alsa:unable to get the maximum count of pcm device channels\n");
1843 r = snd_pcm_hw_params_test_channels(pcm, pcm_hw_params, chans_n_max);
1844 if (r == 0) {
1845 r = snd_pcm_hw_params_set_channels(pcm, pcm_hw_params,
1846 chans_n_max);
1847 if (r != 0)
1848 FATAL("alsa:unable to restrict pcm device to %u channels, count which was successfully tested\n", chans_n_max);
1849 POUT("alsa:using pcm maximum %u channels\n", chans_n_max);
1850 return;
1852 /* ok... last try, the pcm dev min chans n */
1853 r = snd_pcm_hw_params_get_channels_min(pcm_hw_params, &chans_n_min);
1854 if (r != 0)
1855 FATAL("alsa:unable to get the minimum count of pcm device channels\n");
1856 r = snd_pcm_hw_params_test_channels(pcm, pcm_hw_params, chans_n_min);
1857 if (r == 0) {
1858 r = snd_pcm_hw_params_set_channels(pcm, pcm_hw_params,
1859 chans_n_min);
1860 if (r != 0)
1861 FATAL("alsa:unable to restrict pcm device to %u channels, count which was successfully tested\n", chans_n_min);
1862 POUT("alsa:using pcm device minimum %u channels\n", chans_n_min);
1863 return;
1865 FATAL("alsa:unable to find a suitable count of channels\n");
1867 static void pcm_hw_rate_decide(snd_pcm_t *pcm,
1868 snd_pcm_hw_params_t *pcm_hw_params, unsigned int rate)
1870 int r;
1871 unsigned int rate_max;
1872 unsigned int rate_near;
1873 unsigned int rate_min;
1875 r = snd_pcm_hw_params_test_rate(pcm, pcm_hw_params, rate,
1876 SND_PCM_STREAM_PLAYBACK);
1877 if (r == 0) {
1878 r = snd_pcm_hw_params_set_rate(pcm, pcm_hw_params, rate,
1879 SND_PCM_STREAM_PLAYBACK);
1880 if (r != 0)
1881 FATAL("alsa:unable to restrict pcm device to %uHz, which was successfully tested\n", rate);
1882 POUT("alsa:using %uHz\n", rate);
1883 return;
1885 POUT("alsa:unable to use %uHz\n", rate);
1886 /* try to use the max rate the pcm can */
1887 r = snd_pcm_hw_params_get_rate_max(pcm_hw_params, &rate_max,
1888 SND_PCM_STREAM_PLAYBACK);
1889 if (r != 0)
1890 FATAL("alsa:unable to get the maximum rate of pcm device\n");
1891 r = snd_pcm_hw_params_test_rate(pcm, pcm_hw_params, rate_max,
1892 SND_PCM_STREAM_PLAYBACK);
1893 if (r == 0) {
1894 r = snd_pcm_hw_params_set_rate(pcm, pcm_hw_params, rate_max,
1895 SND_PCM_STREAM_PLAYBACK);
1896 if (r != 0)
1897 FATAL("alsa:unable to restrict pcm device to %uHz, which was successfully tested\n", rate_max);
1898 POUT("alsa:using pcm device %uHz\n", rate_max);
1899 return;
1901 /* try to use a rate "near" of what the pcm dev can */
1902 rate_near = rate;
1903 r = snd_pcm_hw_params_set_rate_near(pcm, pcm_hw_params, &rate_near,
1904 SND_PCM_STREAM_PLAYBACK);
1905 if (r == 0) {
1906 POUT("alsa:using pcm device %uHz\n", rate_near);
1907 return;
1909 /* even a "near" rate did failed... try the min */
1910 r = snd_pcm_hw_params_get_rate_min(pcm_hw_params, &rate_min,
1911 SND_PCM_STREAM_PLAYBACK);
1912 if (r != 0)
1913 FATAL("alsa:unable to get the minimum rate of pcm device\n");
1914 r = snd_pcm_hw_params_test_rate(pcm, pcm_hw_params, rate_min,
1915 SND_PCM_STREAM_PLAYBACK);
1916 if (r == 0) {
1917 r = snd_pcm_hw_params_set_rate(pcm, pcm_hw_params, rate_min,
1918 SND_PCM_STREAM_PLAYBACK);
1919 if (r != 0)
1920 FATAL("alsa:unable to restrict pcm device to %uHz, which was successfully tested\n", rate_min);
1921 POUT("alsa:using pcm device %uHz\n", rate_min);
1922 return;
1924 FATAL("alsa:unable to find a suitable rate\n");
1926 static bool ff_fmt2pcm_layout_best_effort(enum AVSampleFormat ff_fmt,
1927 snd_pcm_format_t *alsa_fmt, snd_pcm_access_t *alsa_access)
1929 static u8 ff_fmt_str[STR_SZ];
1931 av_get_sample_fmt_string(ff_fmt_str, STR_SZ, ff_fmt);
1932 /* XXX: only classic non-mmap ones */
1933 switch (ff_fmt) {
1934 case AV_SAMPLE_FMT_U8:
1935 *alsa_fmt = SND_PCM_FORMAT_U8;
1936 *alsa_access = SND_PCM_ACCESS_RW_INTERLEAVED;
1937 break;
1938 case AV_SAMPLE_FMT_S16:
1939 *alsa_fmt = SND_PCM_FORMAT_S16;
1940 *alsa_access = SND_PCM_ACCESS_RW_INTERLEAVED;
1941 break;
1942 case AV_SAMPLE_FMT_S32:
1943 *alsa_fmt = SND_PCM_FORMAT_S32;
1944 *alsa_access = SND_PCM_ACCESS_RW_INTERLEAVED;
1945 break;
1946 case AV_SAMPLE_FMT_FLT:
1947 *alsa_fmt = SND_PCM_FORMAT_FLOAT;
1948 *alsa_access = SND_PCM_ACCESS_RW_INTERLEAVED;
1949 break;
1950 /* ff "planar" fmts are actually non interleaved fmts */
1951 case AV_SAMPLE_FMT_U8P:
1952 *alsa_fmt = SND_PCM_FORMAT_U8;
1953 *alsa_access = SND_PCM_ACCESS_RW_NONINTERLEAVED;
1954 break;
1955 case AV_SAMPLE_FMT_S16P:
1956 *alsa_fmt = SND_PCM_FORMAT_S16;
1957 *alsa_access = SND_PCM_ACCESS_RW_NONINTERLEAVED;
1958 break;
1959 case AV_SAMPLE_FMT_S32P:
1960 *alsa_fmt = SND_PCM_FORMAT_S32;
1961 *alsa_access = SND_PCM_ACCESS_RW_NONINTERLEAVED;
1962 break;
1963 case AV_SAMPLE_FMT_FLTP:
1964 *alsa_fmt = SND_PCM_FORMAT_FLOAT;
1965 *alsa_access = SND_PCM_ACCESS_RW_NONINTERLEAVED;
1966 break;
1967 default:
1968 POUT("best effort:unable to wire ffmpeg sample format \"%sbits\" to alsa sample format, \n,", ff_fmt_str);
1969 return false;
1971 POUT("best effort:ffmpeg format \"%sbits\" (%u bytes) to alsa layout \"%s\" and access \"%s\"\n", ff_fmt_str, av_get_bytes_per_sample(ff_fmt), snd_pcm_format_description(*alsa_fmt), snd_pcm_access_name(*alsa_access));
1972 return true;
1974 static bool pcm_hw_fmt_decide_x(snd_pcm_t *pcm,
1975 snd_pcm_hw_params_t *pcm_hw_params, snd_pcm_format_t fmt)
1977 int r;
1979 r = snd_pcm_hw_params_test_format(pcm, pcm_hw_params, fmt);
1980 if (r != 0)
1981 return false;
1982 r = snd_pcm_hw_params_set_format(pcm, pcm_hw_params, fmt);
1983 if (r != 0)
1984 FATAL("alsa:unable to restrict pcm device to \"%s\", which was successfully tested\n", snd_pcm_format_description(fmt));
1985 POUT("alsa:using \"%s\" format\n", snd_pcm_format_description(fmt));
1986 return true;
1988 #define PCM_HW_FMT_DECIDE_X(fmt) pcm_hw_fmt_decide_x(pcm, pcm_hw_params, fmt)
1989 static void pcm_hw_fmt_decide(snd_pcm_t *pcm,
1990 snd_pcm_hw_params_t *pcm_hw_params, bool force,
1991 snd_pcm_format_t forced_fmt)
1993 int r;
1994 snd_pcm_format_t *fmt;
1996 if (force) {
1997 r = snd_pcm_hw_params_test_format(pcm, pcm_hw_params,
1998 forced_fmt);
1999 if (r == 0) {
2000 r = snd_pcm_hw_params_set_format(pcm, pcm_hw_params,
2001 forced_fmt);
2002 if (r != 0)
2003 FATAL("alsa:unable to restrict pcm device to \"%s\", which was successfully tested\n", snd_pcm_format_description(forced_fmt));
2004 POUT("alsa:using forced \"%s\" format\n", snd_pcm_format_description(forced_fmt));
2005 return;
2008 /* then we try to select from the reasonable "best" to the lowest */
2009 /* prefer fmts we know supported by ff */
2010 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FORMAT_FLOAT))
2011 return;
2012 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FORMAT_S32))
2013 return;
2014 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FORMAT_S16))
2015 return;
2016 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FORMAT_U8))
2017 return;
2019 * from here, at the time of writting, those fmts have no ff
2020 * wiring, but we are alsa centric here, validate that later
2022 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FORMAT_U32))
2023 return;
2024 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FORMAT_S24))
2025 return;
2026 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FORMAT_U24))
2027 return;
2028 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FORMAT_U16))
2029 return;
2030 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FORMAT_S8))
2031 return;
2032 FATAL("alsa:unable to find a suitable format\n");
2034 #undef PCM_HW_FMT_DECIDE_X
2035 static bool pcm_hw_access_decide_x(snd_pcm_t *pcm,
2036 snd_pcm_hw_params_t *pcm_hw_params, snd_pcm_access_t access)
2038 int r;
2040 r = snd_pcm_hw_params_test_access(pcm, pcm_hw_params, access);
2041 if (r != 0)
2042 return false;
2043 r = snd_pcm_hw_params_set_access(pcm, pcm_hw_params, access);
2044 if (r != 0)
2045 FATAL("alsa:unable to restrict pcm device to \"%s\", which was successfully tested\n", snd_pcm_access_name(access));
2046 POUT("alsa:using \"%s\" access\n", snd_pcm_access_name(access));
2047 return true;
2049 #define PCM_HW_ACCESS_DECIDE_X(access) \
2050 pcm_hw_access_decide_x(pcm, pcm_hw_params, access)
2051 /* XXX: only classic non-mmap ones */
2052 static void pcm_hw_access_decide(snd_pcm_t *pcm,
2053 snd_pcm_hw_params_t *pcm_hw_params, bool force,
2054 snd_pcm_access_t forced_access)
2056 int r;
2057 snd_pcm_access_t access;
2059 if (force) {
2060 r = snd_pcm_hw_params_test_access(pcm, pcm_hw_params,
2061 forced_access);
2062 if (r == 0) {
2063 r = snd_pcm_hw_params_set_access(pcm, pcm_hw_params,
2064 forced_access);
2065 if (r != 0)
2066 FATAL("alsa:unable to restrict pcm device to \"%s\", which was successfully tested\n", snd_pcm_access_name(forced_access));
2067 POUT("alsa:using forced \"%s\" access\n", snd_pcm_access_name(forced_access));
2068 return;
2071 /* brute force */
2072 if (PCM_HW_ACCESS_DECIDE_X(SND_PCM_ACCESS_RW_INTERLEAVED))
2073 return;
2074 if (PCM_HW_ACCESS_DECIDE_X(SND_PCM_ACCESS_RW_NONINTERLEAVED))
2075 return;
2076 FATAL("alsa:unable to find a suitable access\n");
2078 #undef PCM_HW_ACCESS_DECIDE_X
2080 * latency control: some audio bufs can be huge (tested on a pulseaudio with 10
2081 * secs audio buf). if we are careless, we will quickly fill this buf which is
2082 * worth a significant amount of time, hence will add huge latency to our
2083 * interactive audio filtering (vol...). in the case of the 10 secs pulseaudio
2084 * buf, it means if you want to mute the audio, it will happen 10 secs later.
2085 * we add lantency control by limiting the sz of the dev audio buf, in periods
2086 * n.
2087 * we choose roughly 0.25 secs, or roughly (rate / 4) frs.
2089 static void pcm_hw_buf_sz_cfg(snd_pcm_t *pcm,
2090 snd_pcm_hw_params_t *pcm_hw_params)
2092 int r;
2093 snd_pcm_uframes_t latency_control_target_buf_ufrs_n;
2094 snd_pcm_uframes_t latency_control_buf_ufrs_n;
2095 unsigned int rate;
2097 r = snd_pcm_hw_params_get_rate(pcm_hw_params, &rate, 0);
2098 if (r < 0) {
2099 WARNING("alsa:latency control:DISABLING LATENCY CONTROL:unable to get the decided rate from the current device parameters\n");
2100 return;
2102 latency_control_target_buf_ufrs_n = (snd_pcm_uframes_t)rate;
2103 latency_control_target_buf_ufrs_n /= 4;
2104 latency_control_buf_ufrs_n = latency_control_target_buf_ufrs_n;
2105 r = snd_pcm_hw_params_set_buffer_size_near(pcm, pcm_hw_params,
2106 &latency_control_buf_ufrs_n);
2107 if (r < 0) {
2108 WARNING("alsa:latency control:DISABLING_LATENCY_CONTROL:unable to set the audio buffer size (count of frames) to %u periods for the current device parameters\n", latency_control_buf_ufrs_n);
2109 return;
2111 POUT("alsa:latency control:target buffer frame count is %u (~0.25 sec), got an audio buffer size set to %u frames\n", latency_control_target_buf_ufrs_n, latency_control_buf_ufrs_n);
2113 static void pcm_cfg_sw(snd_pcm_t *pcm)
2115 int r;
2116 snd_pcm_sw_params_t *sw_params;
2118 POUT("ALSA:SW_PARAMS START------------------------------------------------------------\n");
2119 r = snd_pcm_sw_params_malloc(&sw_params);
2120 if (r != 0)
2121 FATAL("alsa:unable to allocate software parameters structure\n");
2122 r = snd_pcm_sw_params_current(pcm, sw_params);
2123 if (r != 0)
2124 FATAL("alsa:unable to get current software parameters\n");
2125 r = snd_pcm_sw_params_set_period_event(pcm, sw_params, 1);
2126 if (r != 0)
2127 FATAL("alsa:unable to enable period event\n");
2128 r = snd_pcm_sw_params(pcm, sw_params);
2129 if (r != 0)
2130 FATAL("alsa:unable to install sotfware parameters\n");
2131 snd_pcm_sw_params_dump(sw_params, pcm_pout);
2132 snd_pcm_sw_params_free(sw_params);
2133 POUT("ALSA:SW_PARAMS END--------------------------------------------------------------\n");
2136 * this function will "decide" the pcm dev cfg:
2137 * the goal is to be the "closest" to the provided params,
2138 * the "gap" will have to "filled" with ff filts
2140 * the "strategy" is a "fall-thru" (chans n then ... then ...) which
2141 * will "restrict" the pcm dev cfg further at each step
2143 * we try to use a sensible restrict order regarding audio props
2145 static void pcm_cfg_hw_core(snd_pcm_t *pcm, snd_pcm_hw_params_t *pcm_hw_params,
2146 int chans_n, int rate, enum AVSampleFormat ff_fmt)
2148 int r;
2149 bool best_effort_wiring_success;
2150 snd_pcm_format_t fmt_from_best_effort;
2151 snd_pcm_access_t access_from_best_effort;
2153 /* the return value is from a first refine of the raw hw params */
2154 r = snd_pcm_hw_params_any(pcm, pcm_hw_params);
2155 if (r < 0)
2156 FATAL("alsa:unable to populate the hardware parameters context\n");
2157 pcm_hw_chans_n_decide(pcm, pcm_hw_params, (unsigned int)chans_n);
2158 pcm_hw_rate_decide(pcm, pcm_hw_params, (unsigned int)rate);
2159 /* try our best */
2160 best_effort_wiring_success = ff_fmt2pcm_layout_best_effort(
2161 ff_fmt, &fmt_from_best_effort, &access_from_best_effort);
2162 pcm_hw_fmt_decide(pcm, pcm_hw_params, best_effort_wiring_success,
2163 fmt_from_best_effort);
2164 pcm_hw_access_decide(pcm, pcm_hw_params, best_effort_wiring_success,
2165 access_from_best_effort);
2166 pcm_hw_buf_sz_cfg(pcm, pcm_hw_params);
2168 static void pcm_cfg_hw(snd_pcm_t *pcm, unsigned int chans_n, unsigned int rate,
2169 enum AVSampleFormat ff_fmt)
2171 int r;
2172 snd_pcm_access_t access;
2173 snd_pcm_hw_params_t *hw_params;
2175 POUT("ALSA:HW_PARAMS START------------------------------------------------------------\n");
2176 r = snd_pcm_hw_params_malloc(&hw_params);
2177 if (r < 0)
2178 FATAL("alsa:unable to allocate hardware parameters context\n");
2179 pcm_cfg_hw_core(pcm, hw_params, chans_n, rate, ff_fmt);
2180 r = snd_pcm_hw_params(pcm, hw_params);
2181 if (r != 0)
2182 FATAL("alsa:unable to install the hardware parameters\n");
2183 r = snd_pcm_hw_params_current(pcm, hw_params);
2184 if (r != 0)
2185 FATAL("alsa:unable to get current hardware parameters\n");
2186 snd_pcm_hw_params_dump(hw_params, pcm_pout);
2187 snd_pcm_hw_params_free(hw_params);
2188 POUT("ALSA:HW_PARAMS END--------------------------------------------------------------\n");
2190 static void pcm_cfg(snd_pcm_t *pcm, unsigned int chans_n, unsigned int rate,
2191 enum AVSampleFormat ff_fmt)
2193 pcm_cfg_hw(pcm, chans_n, rate, ff_fmt);
2194 pcm_cfg_sw(pcm);
2195 POUT("ALSA PCM DUMP START-------------------------------------------------------------\n");
2196 snd_pcm_dump(pcm, pcm_pout);
2197 POUT("ALSA PCM DUMP END---------------------------------------------------------------\n");
2199 static void evt_pcm_install(void)
2201 u8 i;
2203 i = 0;
2204 loop {
2205 struct epoll_event evt;
2206 int r;
2208 if (i == pcm_pollfds_n)
2209 break;
2210 evt.events = pcm_pollfds[i].events;
2211 evt.data.fd = pcm_pollfds[i].fd;
2212 r = epoll_ctl(ep_fd, EPOLL_CTL_ADD, pcm_pollfds[i].fd, &evt);
2213 if (r == -1)
2214 FATAL("unable to add alsa poll file descriptor[%d]=%d to epoll file descriptor\n", i, pcm_pollfds[i].fd);
2215 ++i;
2217 POUT("alsa:pcm events installed\n");
2219 static void evt_pcm_uninstall(void)
2221 u8 i;
2223 i = 0;
2224 loop {
2225 if (i == pcm_pollfds_n)
2226 break;
2227 (void)epoll_ctl(ep_fd, EPOLL_CTL_DEL, pcm_pollfds[i].fd, 0);
2228 ++i;
2230 POUT("alsa:pcm events uninstalled\n");
2232 #define PRINT_INFO true
2233 static void play(int st_idx, double initial_vol)
2235 enum AVSampleFormat dst_fmt;
2236 int dst_rate;
2237 int dst_chans_n;
2238 uint64_t dst_chans_layout;
2240 current_st_idx = st_idx;
2241 rd_thd_reset(st_idx);
2242 fmt_ctx_lock();
2243 dec_ctx_cfg(fmt_ctx->streams[st_idx]->codecpar);
2244 dec_frs.most_recent_ts = fmt_ctx->streams[st_idx]->start_time;
2245 cmd_info_data.fmt.duration = fmt_ctx->duration;
2246 cmd_info_data.fmt.m = fmt_ctx->duration_estimation_method;
2247 cmd_info_data.st.tb = fmt_ctx->streams[st_idx]->time_base;
2248 cmd_info_data.st.id = fmt_ctx->streams[st_idx]->id;
2249 cmd_info_data.st.duration = fmt_ctx->streams[st_idx]->duration;
2250 fmt_ctx_unlock();
2252 evt_pcm_uninstall();
2254 * do our best to match the pcm cfg to audio ff dec output, BUT we don't
2255 * expect to match it "exactly": see right below why
2257 pcm_cfg(pcm_g, dec_ctx->channels, dec_ctx->sample_rate,
2258 dec_ctx->sample_fmt);
2259 evt_pcm_install();
2260 /* use a ff filt to fill in the gap between the pcm and audio ff dec */
2261 pcm2ff(pcm_g, &dst_fmt, &dst_rate, &dst_chans_n,
2262 &dst_chans_layout, PRINT_INFO);
2263 filter_graph_cfg(
2264 dec_ctx->sample_fmt, dec_ctx->sample_rate,
2265 dec_ctx->channels, dec_ctx->channel_layout,
2266 false, initial_vol,
2267 dst_fmt, dst_rate, dst_chans_n, dst_chans_layout,
2268 PRINT_INFO);
2269 silence_bufs_cfg(pcm_g, PRINT_INFO);
2270 filt_frs.vol = initial_vol;
2272 #undef PRINT_INFO
2273 static void seek_x(int64_t delta)
2275 int r;
2276 int64_t new_ts;
2277 AVStream *st;
2278 AVRational st_tb;
2280 if (dec_frs.most_recent_ts == AV_NOPTS_VALUE)
2281 WARNING("unable to seek because no time stamp are currently available\n");
2282 fmt_ctx_lock();
2283 st = fmt_ctx->streams[current_st_idx];
2284 st_tb = st->time_base;
2285 fmt_ctx_unlock();
2287 new_ts = dec_frs.most_recent_ts + delta * st_tb.den / st_tb.num;
2288 /* rewind capping */
2289 POUT("trying to seek to %"PRId64" stream time base units\n", new_ts);
2290 fmt_ctx_lock();
2291 r = av_seek_pkt(fmt_ctx, st->id, new_ts, 0);
2292 fmt_ctx_unlock();
2293 if (r < 0)
2294 WARNING("unable to seek to %"PRId64" stream time base units\n", new_ts);
2295 dec_frs.most_recent_ts = AV_NOPTS_VALUE;
2296 rd_thd_reset(current_st_idx);
2297 avcodec_flush_buffers(dec_ctx);
2298 filt_frs.no_more_dec_frs = false; /* reset by the previous line */
2299 filt_flush();
2300 if (!filt_frs_get())
2301 EXIT("no more audio frames to play\n");
2303 static void cmd_rewind(void)
2305 POUT("COMMAND:rewind\n");
2306 seek_x(-SEEK_DELTA);
2308 static void cmd_rewind_big(void)
2310 POUT("COMMAND:rewind big\n");
2311 seek_x(-SEEK_DELTA_BIG);
2313 static void cmd_fastforward(void)
2315 POUT("COMMAND:fastforward\n");
2316 seek_x(SEEK_DELTA);
2318 static void cmd_fastforward_big(void)
2320 POUT("COMMAND:fastforward big\n");
2321 seek_x(SEEK_DELTA_BIG);
2323 static void cmd_pause(void)
2325 if (paused) {
2326 POUT("COMMAND:unpause\n");
2327 paused = false;
2328 } else {
2329 POUT("COMMAND:pause\n");
2330 paused = true;
2333 static void cmd_vol_up(void)
2335 int r;
2336 u8 vol_l10n_str[sizeof("xxx.xx")]; /* should be overkill */
2337 u8 response[STR_SZ];
2339 filt_frs.vol += VOL_DELTA;
2340 if (filt_frs.vol > 1.0)
2341 filt_frs.vol = 1.0;
2342 snprintf(vol_l10n_str, sizeof(vol_l10n_str), "%f", filt_frs.vol);
2343 POUT("COMMAND:volume up to value %s(%s)\n", vol_l10n_str, filt_frs.muted ? "muted" : "unmuted");
2344 if (filt_frs.muted)
2345 return;
2346 r = avfilter_graph_send_command(filter_graph, "vol", "volume",
2347 vol_l10n_str, response, sizeof(response), 0);
2348 if (r < 0)
2349 WARNING("ffmpeg:volume context:unable to set the volume up to \"%s\":response from volume filter:\"%s\"\n", response);
2351 static void cmd_vol_down(void)
2353 int r;
2354 u8 vol_l10n_str[sizeof("xxx.xx")]; /* should be overkill */
2355 u8 response[STR_SZ];
2357 filt_frs.vol -= VOL_DELTA;
2358 if (filt_frs.vol < 0.0)
2359 filt_frs.vol = 0.0;
2360 snprintf(vol_l10n_str, sizeof(vol_l10n_str), "%f", filt_frs.vol);
2361 POUT("COMMAND:volume down to value %s(%s)\n", vol_l10n_str, filt_frs.muted ? "muted" : "unmuted");
2362 if (filt_frs.muted)
2363 return;
2364 r = avfilter_graph_send_command(filter_graph, "vol", "volume",
2365 vol_l10n_str, response, sizeof(response), 0);
2366 if (r < 0)
2367 WARNING("ffmpeg:volume context:unable to set the volume down to \"%s\":response from volume filter:\"%s\"\n", response);
2369 static void cmd_mute(void)
2371 int r;
2372 u8 vol_l10n_str[sizeof("xxx.xx")]; /* should be overkill */
2373 u8 response[STR_SZ];
2375 if (filt_frs.muted) {
2376 POUT("COMMAND:unmuting\n");
2378 snprintf(vol_l10n_str, sizeof(vol_l10n_str), "%f",
2379 filt_frs.vol);
2380 r = avfilter_graph_send_command(filter_graph, "vol", "volume",
2381 vol_l10n_str, response, sizeof(response), 0);
2382 if (r < 0) {
2383 WARNING("ffmpeg:volume context:unable to mute the volume to 0:response from volume filter:%s\n", response);
2384 } else {
2385 filt_frs.muted = false;
2387 } else {
2388 POUT("COMMAND:muting\n");
2390 r = avfilter_graph_send_command(filter_graph, "vol", "volume",
2391 double_zero_l10n_str, response, sizeof(response), 0);
2392 if (r < 0) {
2393 WARNING("ffmpeg:volume context:unable to mute the volume to 0:response from volume filter:%s\n", response);
2394 } else {
2395 filt_frs.muted = true;
2399 int main(int argc, u8 **args)
2401 u8 *url;
2402 u8 *pcm_str;
2403 double initial_vol;
2404 int best_st_idx;
2405 /* "turn on utf8" processing in used libs if any *AND* locale system */
2406 setlocale(LC_ALL, "");
2407 /* av_log_set_level(AV_LOG_VERBOSE); */
2408 /* av_log_set_level(AV_LOG_DEBUG); */
2409 url = 0;
2410 pcm_str = "default";
2411 initial_vol = 1.;
2412 opts_parse(argc, args, &url, &pcm_str, &initial_vol);
2413 init_once(url, pcm_str);
2414 rd_thd_start();
2415 best_st_idx = find_best_st();
2416 play(best_st_idx, initial_vol);
2417 /* switch the ffmpeg log to stdout for metadata/etc dump */
2418 av_log_set_callback(ffmpeg_log_stdout);
2419 fmt_ctx_lock();
2420 av_dump_format(fmt_ctx, 0, url, 0);
2421 fmt_ctx_unlock();
2422 av_log_set_callback(av_log_default_callback);
2423 if (!filt_frs_get()) /* must have some frames to play */
2424 EXIT("no initial audio frames to play\n");
2425 loop evts_loop();
2426 /* unreachable */
2428 /*----------------------------------------------------------------------------*/
2429 #undef ARRAY_N
2430 #undef av_frames_alloc
2431 #undef av_frame_fmt_is_planar
2432 #undef av_frames_set_silence
2433 #undef av_frames_unref
2434 #undef av_get_frame_fmt_name
2435 #undef av_get_frame_fmt_string
2436 #undef av_read_pkt
2437 #undef av_seek_pkt
2438 #undef avcodec_receive_frames
2439 #undef AVFrameFormat
2440 #undef AVFrames
2441 #undef esc_seq_sz
2442 #undef EXIT
2443 #undef f32
2444 #undef FATAL
2445 #undef frame_fmt
2446 #undef frame_rate
2447 #undef frames_n
2448 #undef INPUT_ESC_SEQ_TIMEOUT_SECS_N
2449 #undef loop
2450 #undef PCM_POLLFDS_N_MAX
2451 #undef PERR
2452 #undef PKTS_N_MAX
2453 #undef POUT
2454 #undef SEEK_DELTA
2455 #undef SEEK_DELTA_BIG
2456 #undef STR_SZ
2457 #undef u16
2458 #undef u32
2459 #undef u8
2460 #undef U8_MAX
2461 #undef VOL_DELTA
2462 #undef WARNING
2463 #endif