npv:_reasonable_ "pedanticage" of the code
[nyanmp.git] / npv / audio / local / code.frag.c
blobfe5fda005d7d42a0ee836c9bf677f1f0d9f228cb
1 STATIC void fatal(u8 *fmt, ...)
3 va_list ap;
5 npv_perr("audio:");
6 va_start(ap, fmt);
7 npv_vfatal(fmt, ap);
8 va_end(ap); /* unreachable */
10 STATIC void warning(u8 *fmt, ...)
12 va_list ap;
14 npv_perr("audio:");
15 va_start(ap, fmt);
16 npv_vwarning(fmt, ap);
17 va_end(ap);
19 STATIC void pout(u8 *fmt, ...)
21 va_list ap;
23 npv_pout("audio:");
24 va_start(ap, fmt);
25 npv_vpout(fmt, ap);
26 va_end(ap);
28 STATIC bool ff_fmt2pcm_layout_best_effort(enum avutil_audio_fr_fmt_t ff_fmt,
29 snd_pcm_fmt_t *alsa_fmt, snd_pcm_access_t *alsa_access)
31 static u8 ff_fmt_str[STR_SZ];
33 avutil_get_audio_fr_fmt_str(ff_fmt_str, STR_SZ, ff_fmt);
34 /* XXX: only classic non-mmap ones */
35 switch (ff_fmt) {
36 case AVUTIL_AUDIO_FR_FMT_U8:
37 *alsa_fmt = SND_PCM_FMT_U8;
38 *alsa_access = SND_PCM_ACCESS_RW_INTERLEAVED;
39 break;
40 case AVUTIL_AUDIO_FR_FMT_S16:
41 *alsa_fmt = SND_PCM_FMT_S16;
42 *alsa_access = SND_PCM_ACCESS_RW_INTERLEAVED;
43 break;
44 case AVUTIL_AUDIO_FR_FMT_S32:
45 *alsa_fmt = SND_PCM_FMT_S32;
46 *alsa_access = SND_PCM_ACCESS_RW_INTERLEAVED;
47 break;
48 case AVUTIL_AUDIO_FR_FMT_FLT:
49 *alsa_fmt = SND_PCM_FMT_FLOAT;
50 *alsa_access = SND_PCM_ACCESS_RW_INTERLEAVED;
51 break;
52 /* ff "planar" fmts are actually non interleaved fmts */
53 case AVUTIL_AUDIO_FR_FMT_U8P:
54 *alsa_fmt = SND_PCM_FMT_U8;
55 *alsa_access = SND_PCM_ACCESS_RW_NONINTERLEAVED;
56 break;
57 case AVUTIL_AUDIO_FR_FMT_S16P:
58 *alsa_fmt = SND_PCM_FMT_S16;
59 *alsa_access = SND_PCM_ACCESS_RW_NONINTERLEAVED;
60 break;
61 case AVUTIL_AUDIO_FR_FMT_S32P:
62 *alsa_fmt = SND_PCM_FMT_S32;
63 *alsa_access = SND_PCM_ACCESS_RW_NONINTERLEAVED;
64 break;
65 case AVUTIL_AUDIO_FR_FMT_FLTP:
66 *alsa_fmt = SND_PCM_FMT_FLOAT;
67 *alsa_access = SND_PCM_ACCESS_RW_NONINTERLEAVED;
68 break;
69 default:
70 pout("best effort:unable to wire ffmpeg sample format \"%sbits\" to alsa sample format, \n,", ff_fmt_str);
71 return false;
73 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_fmt_desc(*alsa_fmt), snd_pcm_access_name(*alsa_access));
74 return true;
76 STATIC void pcm_hw_chans_n_decide(snd_pcm_t *pcm,
77 snd_pcm_hw_params_t *pcm_hw_params, unsigned int chans_n)
79 int r;
80 unsigned int chans_n_max;
81 unsigned int chans_n_min;
83 r = snd_pcm_hw_params_test_chans_n(pcm, pcm_hw_params, chans_n);
84 if (r == 0) {
85 r = snd_pcm_hw_params_set_chans_n(pcm, pcm_hw_params, chans_n);
86 if (r != 0)
87 fatal("alsa:unable to restrict pcm device to %u channels, count which was successfully tested\n", chans_n);
88 pout("alsa:using %u channels\n", chans_n);
89 return;
91 pout("alsa:unable to use %u channels\n", chans_n);
92 /* try to use the max chans n the pcm can */
93 r = snd_pcm_hw_params_get_chans_n_max(pcm_hw_params, &chans_n_max);
94 if (r != 0)
95 fatal("alsa:unable to get the maximum count of pcm device channels\n");
96 r = snd_pcm_hw_params_test_chans_n(pcm, pcm_hw_params, chans_n_max);
97 if (r == 0) {
98 r = snd_pcm_hw_params_set_chans_n(pcm, pcm_hw_params,
99 chans_n_max);
100 if (r != 0)
101 fatal("alsa:unable to restrict pcm device to %u channels, count which was successfully tested\n", chans_n_max);
102 pout("alsa:using pcm maximum %u channels\n", chans_n_max);
103 return;
105 /* ok... last try, the pcm dev min chans n */
106 r = snd_pcm_hw_params_get_chans_n_min(pcm_hw_params, &chans_n_min);
107 if (r != 0)
108 fatal("alsa:unable to get the minimum count of pcm device channels\n");
109 r = snd_pcm_hw_params_test_chans_n(pcm, pcm_hw_params, chans_n_min);
110 if (r == 0) {
111 r = snd_pcm_hw_params_set_chans_n(pcm, pcm_hw_params,
112 chans_n_min);
113 if (r != 0)
114 fatal("alsa:unable to restrict pcm device to %u channels, count which was successfully tested\n", chans_n_min);
115 pout("alsa:using pcm device minimum %u channels\n", chans_n_min);
116 return;
118 fatal("alsa:unable to find a suitable count of channels\n");
120 STATIC void pcm_hw_rate_decide(snd_pcm_t *pcm,
121 snd_pcm_hw_params_t *pcm_hw_params, unsigned int rate)
123 int r;
124 unsigned int rate_max;
125 unsigned int rate_near;
126 unsigned int rate_min;
128 r = snd_pcm_hw_params_test_rate(pcm, pcm_hw_params, rate,
129 SND_PCM_ST_PLAYBACK);
130 if (r == 0) {
131 r = snd_pcm_hw_params_set_rate(pcm, pcm_hw_params, rate,
132 SND_PCM_ST_PLAYBACK);
133 if (r != 0)
134 fatal("alsa:unable to restrict pcm device to %uHz, which was successfully tested\n", rate);
135 pout("alsa:using %uHz\n", rate);
136 return;
138 pout("alsa:unable to use %uHz\n", rate);
139 /* try to use the max rate the pcm can */
140 r = snd_pcm_hw_params_get_rate_max(pcm_hw_params, &rate_max,
141 SND_PCM_ST_PLAYBACK);
142 if (r != 0)
143 fatal("alsa:unable to get the maximum rate of pcm device\n");
144 r = snd_pcm_hw_params_test_rate(pcm, pcm_hw_params, rate_max,
145 SND_PCM_ST_PLAYBACK);
146 if (r == 0) {
147 r = snd_pcm_hw_params_set_rate(pcm, pcm_hw_params, rate_max,
148 SND_PCM_ST_PLAYBACK);
149 if (r != 0)
150 fatal("alsa:unable to restrict pcm device to %uHz, which was successfully tested\n", rate_max);
151 pout("alsa:using pcm device %uHz\n", rate_max);
152 return;
154 /* try to use a rate "near" of what the pcm dev can */
155 rate_near = rate;
156 r = snd_pcm_hw_params_set_rate_near(pcm, pcm_hw_params, &rate_near,
157 SND_PCM_ST_PLAYBACK);
158 if (r == 0) {
159 pout("alsa:using pcm device %uHz\n", rate_near);
160 return;
162 /* even a "near" rate did failed... try the min */
163 r = snd_pcm_hw_params_get_rate_min(pcm_hw_params, &rate_min,
164 SND_PCM_ST_PLAYBACK);
165 if (r != 0)
166 fatal("alsa:unable to get the minimum rate of pcm device\n");
167 r = snd_pcm_hw_params_test_rate(pcm, pcm_hw_params, rate_min,
168 SND_PCM_ST_PLAYBACK);
169 if (r == 0) {
170 r = snd_pcm_hw_params_set_rate(pcm, pcm_hw_params, rate_min,
171 SND_PCM_ST_PLAYBACK);
172 if (r != 0)
173 fatal("alsa:unable to restrict pcm device to %uHz, which was successfully tested\n", rate_min);
174 pout("alsa:using pcm device %uHz\n", rate_min);
175 return;
177 fatal("alsa:unable to find a suitable rate\n");
179 STATIC bool pcm_hw_fmt_decide_x(snd_pcm_t *pcm,
180 snd_pcm_hw_params_t *pcm_hw_params, snd_pcm_fmt_t fmt)
182 int r;
184 r = snd_pcm_hw_params_test_fmt(pcm, pcm_hw_params, fmt);
185 if (r != 0)
186 return false;
187 r = snd_pcm_hw_params_set_fmt(pcm, pcm_hw_params, fmt);
188 if (r != 0)
189 fatal("alsa:unable to restrict pcm device to \"%s\", which was successfully tested\n", snd_pcm_fmt_desc(fmt));
190 pout("alsa:using \"%s\" format\n", snd_pcm_fmt_desc(fmt));
191 return true;
193 #define PCM_HW_FMT_DECIDE_X(fmt) pcm_hw_fmt_decide_x(pcm, pcm_hw_params, fmt)
194 STATIC void pcm_hw_fmt_decide(snd_pcm_t *pcm,
195 snd_pcm_hw_params_t *pcm_hw_params, bool force,
196 snd_pcm_fmt_t forced_fmt)
198 int r;
199 snd_pcm_fmt_t *fmt;
201 if (force) {
202 r = snd_pcm_hw_params_test_fmt(pcm, pcm_hw_params, forced_fmt);
203 if (r == 0) {
204 r = snd_pcm_hw_params_set_fmt(pcm, pcm_hw_params,
205 forced_fmt);
206 if (r != 0)
207 fatal("alsa:unable to restrict pcm device to \"%s\", which was successfully tested\n", snd_pcm_fmt_desc(forced_fmt));
208 pout("alsa:using forced \"%s\" format\n", snd_pcm_fmt_desc(forced_fmt));
209 return;
212 /* then we try to select from the reasonable "best" to the lowest */
213 /* prefer fmts we know supported by ff */
214 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FMT_FLOAT))
215 return;
216 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FMT_S32))
217 return;
218 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FMT_S16))
219 return;
220 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FMT_U8))
221 return;
223 * from here, at the time of writting, those fmts have no ff
224 * wiring, but we are alsa centric here, validate that later
226 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FMT_U32))
227 return;
228 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FMT_S24))
229 return;
230 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FMT_U24))
231 return;
232 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FMT_U16))
233 return;
234 if (PCM_HW_FMT_DECIDE_X(SND_PCM_FMT_S8))
235 return;
236 fatal("alsa:unable to find a suitable format\n");
238 #undef PCM_HW_FMT_DECIDE_X
239 STATIC bool pcm_hw_access_decide_x(snd_pcm_t *pcm,
240 snd_pcm_hw_params_t *pcm_hw_params, snd_pcm_access_t access)
242 int r;
244 r = snd_pcm_hw_params_test_access(pcm, pcm_hw_params, access);
245 if (r != 0)
246 return false;
247 r = snd_pcm_hw_params_set_access(pcm, pcm_hw_params, access);
248 if (r != 0)
249 fatal("alsa:unable to restrict pcm device to \"%s\", which was successfully tested\n", snd_pcm_access_name(access));
250 pout("alsa:using \"%s\" access\n", snd_pcm_access_name(access));
251 return true;
253 #define PCM_HW_ACCESS_DECIDE_X(access) \
254 pcm_hw_access_decide_x(pcm, pcm_hw_params, access)
255 /* XXX: only classic non-mmap ones */
256 STATIC void pcm_hw_access_decide(snd_pcm_t *pcm,
257 snd_pcm_hw_params_t *pcm_hw_params, bool force,
258 snd_pcm_access_t forced_access)
260 int r;
261 snd_pcm_access_t access;
263 if (force) {
264 r = snd_pcm_hw_params_test_access(pcm, pcm_hw_params,
265 forced_access);
266 if (r == 0) {
267 r = snd_pcm_hw_params_set_access(pcm, pcm_hw_params,
268 forced_access);
269 if (r != 0)
270 fatal("alsa:unable to restrict pcm device to \"%s\", which was successfully tested\n", snd_pcm_access_name(forced_access));
271 pout("alsa:using forced \"%s\" access\n", snd_pcm_access_name(forced_access));
272 return;
275 /* brute force */
276 if (PCM_HW_ACCESS_DECIDE_X(SND_PCM_ACCESS_RW_INTERLEAVED))
277 return;
278 if (PCM_HW_ACCESS_DECIDE_X(SND_PCM_ACCESS_RW_NONINTERLEAVED))
279 return;
280 fatal("alsa:unable to find a suitable access\n");
282 #undef PCM_HW_ACCESS_DECIDE_X
284 * latency control: some audio bufs can be huge (tested on a pulseaudio with 10
285 * secs audio buf). if we are careless, we will quickly fill this buf which is
286 * worth a significant amount of time, hence will add huge latency to our
287 * interactive audio filtering (vol...). in the case of the 10 secs pulseaudio
288 * buf, it means if you want to mute the audio, it will happen 10 secs later.
289 * we add lantency control by limiting the sz of the dev audio buf, in periods
290 * n.
291 * we choose roughly 0.25 secs, or roughly (rate / 4) frs.
293 STATIC void pcm_hw_buf_sz_cfg(snd_pcm_t *pcm,
294 snd_pcm_hw_params_t *pcm_hw_params)
296 int r;
297 snd_pcm_ufrs_t latency_control_target_buf_ufrs_n;
298 snd_pcm_ufrs_t latency_control_buf_ufrs_n;
299 unsigned int rate;
301 r = snd_pcm_hw_params_get_rate(pcm_hw_params, &rate, 0);
302 if (r < 0) {
303 warning("alsa:latency control:DISABLING LATENCY CONTROL:unable to get the decided rate from the current device parameters\n");
304 return;
306 latency_control_target_buf_ufrs_n = (snd_pcm_ufrs_t)rate;
307 latency_control_target_buf_ufrs_n /= 4;
308 latency_control_buf_ufrs_n = latency_control_target_buf_ufrs_n;
309 r = snd_pcm_hw_params_set_buf_sz_near(pcm, pcm_hw_params,
310 &latency_control_buf_ufrs_n);
311 if (r < 0) {
312 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);
313 return;
315 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);
318 * this function will "decide" the pcm dev cfg:
319 * the goal is to be the "closest" to the provided params,
320 * the "gap" will have to "filled" with ff filts
322 * the "strategy" is a "fall-thru" (chans n then ... then ...) which
323 * will "restrict" the pcm dev cfg further at each step
325 * we try to use a sensible restrict order regarding audio props
327 STATIC void pcm_cfg_hw_core(snd_pcm_t *pcm, snd_pcm_hw_params_t *pcm_hw_params,
328 int chans_n, int rate, enum avutil_audio_fr_fmt_t ff_fmt)
330 int r;
331 bool best_effort_wiring_success;
332 snd_pcm_fmt_t fmt_from_best_effort;
333 snd_pcm_access_t access_from_best_effort;
335 /* the return value is from a first refine of the raw hw params */
336 r = snd_pcm_hw_params_any(pcm, pcm_hw_params);
337 if (r < 0)
338 fatal("alsa:unable to populate the hardware parameters context\n");
339 pcm_hw_chans_n_decide(pcm, pcm_hw_params, (unsigned int)chans_n);
340 pcm_hw_rate_decide(pcm, pcm_hw_params, (unsigned int)rate);
341 /* try our best */
342 best_effort_wiring_success = ff_fmt2pcm_layout_best_effort(
343 ff_fmt, &fmt_from_best_effort, &access_from_best_effort);
344 pcm_hw_fmt_decide(pcm, pcm_hw_params, best_effort_wiring_success,
345 fmt_from_best_effort);
346 pcm_hw_access_decide(pcm, pcm_hw_params, best_effort_wiring_success,
347 access_from_best_effort);
348 pcm_hw_buf_sz_cfg(pcm, pcm_hw_params);
350 /* base on kernel api at the time we wrote this code */
351 STATIC u8 *kernel_ts_types_str[] = {
352 "compat",
353 "default",
354 "link",
355 "link absolute",
356 "link estimated",
357 "link synchonized"
359 STATIC void pcm_cfg_hw(snd_pcm_t *pcm, unsigned int chans_n, unsigned int rate,
360 enum avutil_audio_fr_fmt_t ff_fmt)
362 int r;
363 s8 i;
364 snd_pcm_access_t access;
365 snd_pcm_hw_params_t *hw_params;
367 pout("ALSA:HW_PARAMS START------------------------------------------------------------\n");
368 r = snd_pcm_hw_params_malloc(&hw_params);
369 if (r < 0)
370 fatal("alsa:unable to allocate hardware parameters context\n");
372 pcm_cfg_hw_core(pcm, hw_params, chans_n, rate, ff_fmt);
374 r = snd_pcm_hw_params(pcm, hw_params);
375 if (r != 0)
376 fatal("alsa:unable to install the hardware parameters\n");
377 r = snd_pcm_hw_params_current(pcm, hw_params);
378 if (r != 0)
379 fatal("alsa:unable to get current hardware parameters\n");
380 snd_pcm_hw_params_dump(hw_params, pcm_pout_l);
382 i = 0;
383 selected_ts_type_p = -1;
384 loop {
385 if (i == ARRAY_N(kernel_ts_types_str))
386 break;
387 r = snd_pcm_hw_params_supports_audio_ts_type(hw_params, i);
388 if (r == 1) {
389 selected_ts_type_p = i;
390 pout("kernel audio timestamp type \"%s\" is supported for the current configuration\n", kernel_ts_types_str[i]);
392 ++i;
395 * we selected the most accurate, namely with the highest idx, audio ts
396 * type
398 pout("%s will be used for the audio based clock\n", kernel_ts_types_str[selected_ts_type_p]);
399 snd_pcm_hw_params_free(hw_params);
400 pout("ALSA:HW_PARAMS END--------------------------------------------------------------\n");
402 STATIC void pcm_cfg_sw(snd_pcm_t *pcm)
404 int r;
405 snd_pcm_sw_params_t *sw_params;
407 pout("ALSA:SW_PARAMS START------------------------------------------------------------\n");
408 r = snd_pcm_sw_params_malloc(&sw_params);
409 if (r != 0)
410 fatal("alsa:unable to allocate software parameters structure\n");
411 r = snd_pcm_sw_params_current(pcm, sw_params);
412 if (r != 0)
413 fatal("alsa:unable to get current software parameters\n");
414 r = snd_pcm_sw_params_set_period_evt(pcm, sw_params, 1);
415 if (r != 0)
416 fatal("alsa:unable to enable period event\n");
417 /* enable ts to be sure */
418 r = snd_pcm_sw_params_set_tstamp_mode(pcm, sw_params,
419 SND_PCM_TSTAMP_ENABLE);
420 if (r < 0)
421 fatal("unable to set timestamp mode:%s\n", snd_strerror(r));
422 r = snd_pcm_sw_params(pcm, sw_params);
423 if (r != 0)
424 fatal("alsa:unable to install sotfware parameters\n");
425 snd_pcm_sw_params_dump(sw_params, pcm_pout_l);
426 snd_pcm_sw_params_free(sw_params);
427 pout("ALSA:SW_PARAMS END--------------------------------------------------------------\n");
429 STATIC void dec_a_grow(void)
431 u32 new_idx;
433 new_idx = dec_sets_p.n_max;
434 dec_sets_p.a = realloc(dec_sets_p.a, sizeof(*dec_sets_p.a)
435 * (dec_sets_p.n_max + 1));
436 if (dec_sets_p.a == 0)
437 fatal("unable to allocate memory for an additional pointer on a reference of a decoder set of frames\n");
438 dec_sets_p.a[new_idx] = avutil_audio_set_ref_alloc();
439 if (dec_sets_p.a[new_idx] == 0)
440 fatal("ffmpeg:unable to allocate a reference of a decoder set of frames\n");
441 ++dec_sets_p.n_max;
443 #define AGAIN 0
444 #define RECOVERED 1
445 #define CONTINUE 2
446 STATIC u8 alsa_recover(snd_pcm_sfrs_t r)
448 if (r >= 0)
449 return CONTINUE;
450 /* r < 0 */
451 if (r == -EAGAIN)
452 return AGAIN;
453 else if (r == -EPIPE || r == -ESTRPIPE) {
454 /* underrun or suspended */
455 int r_recovered;
457 r_recovered = snd_pcm_recover(pcm_p, (int)r, 0);
458 if (r_recovered == 0) {
459 warning("alsa:pcm recovered\n");
460 return RECOVERED;
462 fatal("alsa:unable to recover from suspend/underrun\n");
464 fatal("alsa:fatal/unhandled error\n");
466 #undef AGAIN
467 #undef RECOVERED
468 #undef CONTINUE
469 #define NO 0
470 #define AGAIN 0
471 #define RECOVERED 1
472 STATIC void pcm_silence_frs_write(snd_pcm_ufrs_t ufrs_n) { loop
474 int alsa_r;
475 u8 r_recover;
476 int is_planar_fmt;
478 if (ufrs_n == 0)
479 break;
480 is_planar_fmt = avutil_audio_fr_fmt_is_planar(
481 npv_audio_filt_p.set->fmt);
482 if (is_planar_fmt == NO)
483 alsa_r = snd_pcm_writei(pcm_p, pcm_silence_bufs_l[0], ufrs_n);
484 else
485 alsa_r = snd_pcm_writen(pcm_p, pcm_silence_bufs_l, ufrs_n);
486 r_recover = alsa_recover(alsa_r);
487 if (r_recover == AGAIN)
488 continue;
489 else if (r_recover == RECOVERED)
490 break;
491 /* r_recover == CONTINUE */
492 ufrs_n -= (snd_pcm_ufrs_t)alsa_r;
494 #undef NO
495 #undef AGAIN
496 #undef RECOVERED
497 #define NO 0
498 STATIC void chans_buf_init(u8 **chans_buf, int start_fr_idx)
500 int is_planar_fmt;
501 int sample_bytes_n;
503 sample_bytes_n = avutil_get_bytes_per_sample(npv_audio_filt_p.set->fmt);
504 is_planar_fmt = avutil_audio_fr_fmt_is_planar(
505 npv_audio_filt_p.set->fmt);
506 if (is_planar_fmt == NO) { /* or is pcm interleaved */
507 int fr_bytes_n;
509 fr_bytes_n = sample_bytes_n * npv_audio_filt_p.set->chans_n;
510 chans_buf[0] = (u8*)npv_audio_filt_p.set->data[0] + start_fr_idx
511 * fr_bytes_n;
512 } else { /* ff planar or pcm noninterleaved */
513 int p;
515 p = 0;
516 loop {
517 if (p == npv_audio_filt_p.set->chans_n)
518 break;
519 chans_buf[p] = (u8*)npv_audio_filt_p.set->data[p]
520 + start_fr_idx * sample_bytes_n;
521 ++p;
525 #undef NO
526 #define NO 0
527 STATIC void chans_buf_inc(u8 **chans_buf, int inc)
529 int is_planar_fmt;
530 int sample_bytes_n;
532 sample_bytes_n = avutil_get_bytes_per_sample(npv_audio_filt_p.set->fmt);
533 is_planar_fmt = avutil_audio_fr_fmt_is_planar(
534 npv_audio_filt_p.set->fmt);
535 if (is_planar_fmt == NO) { /* or is pcm interleaved */
536 int fr_bytes_n;
538 fr_bytes_n = sample_bytes_n * npv_audio_filt_p.set->chans_n;
539 chans_buf[0] = (u8*)chans_buf[0] + inc * fr_bytes_n;
540 } else { /* ff planar or pcm noninterleaved */
541 int p;
543 p = 0;
544 loop {
545 if (p == npv_audio_filt_p.set->chans_n)
546 break;
547 chans_buf[p] = (u8*)chans_buf[p] + inc * sample_bytes_n;
548 ++p;
552 #undef NO
553 STATIC void draining_state_handle(void)
555 int r;
557 r = snd_pcm_drain(pcm_p);
558 if (r != 0) {
559 snd_pcm_state_t state;
561 if (r == -EAGAIN)
562 return;
564 * the pcm state can change asynchronously, and if the draining
565 * was successful, the pcm should be in SETUP state, and in
566 * this state, snd_pcm_drain _must_ fail (direct from alsa
567 * devs)
569 state = snd_pcm_state(pcm_p);
570 if (state != SND_PCM_STATE_SETUP)
571 fatal("alsa:an error occured switching to/checking the pcm draining state\n");
572 /* here pcm state is SND_PCM_STATE_SETUP */
574 npv_exit("alsa pcm drained or similar, exiting\n");
576 STATIC void draining_state_switch(void)
578 int r;
579 u8 i;
580 struct itimerspec t;
582 draining_p = true;
583 draining_state_handle();
584 /* remove the alsa epoll fds */
585 i = 0;
586 loop {
587 if (i == pcm_pollfds_n_p)
588 break;
589 /* in theory, it is thread safe */
590 r = epoll_ctl(npv_ep_fd_p, EPOLL_CTL_DEL, pcm_pollfds_p[i].fd,
592 if (r == -1)
593 fatal("unable to remove the alsa file descriptors from epoll\n");
594 ++i;
596 /* start the draining timer */
597 memset(&t, 0, sizeof(t));
598 /* initial and interval */
599 t.it_value.tv_nsec = DRAINING_TIMER_INTERVAL_NSECS_N;
600 t.it_interval.tv_nsec = DRAINING_TIMER_INTERVAL_NSECS_N;
601 r = timerfd_settime(draining_timer_fd_p, 0, &t, 0);
602 if (r == -1)
603 fatal("unable to arm the draining timer\n");
605 #define NO 0
606 #define AGAIN 0
607 #define RECOVERED 1
608 #define CONTINUE 2
609 #define HAVE_FILT_SET 1
610 #define EOF_FILT 2
611 #define NO_DEC_SET 2
612 STATIC void pcm_filt_frs_write(snd_pcm_ufrs_t ufrs_n) { loop
614 u8 chan_buf;
615 u8 *chans_buf[AVUTIL_DATA_PTRS_N];
616 snd_pcm_ufrs_t ufrs_to_write_n;
617 snd_pcm_ufrs_t filt_set_remaining_ufrs_n; /* for clarity */
618 int is_planar_fmt;
619 snd_pcm_ufrs_t written_ufrs_n; /* for clarity */
621 if (ufrs_n == 0)
622 break;
624 * in this loop we try to get some filt frs from what we got from the
625 * dec
627 if (npv_audio_filt_p.set->frs_n == 0) loop {
628 u8 r;
630 /* we _really_ want audio data */
631 (void)filt_push_dec_sets();
632 r = npv_audio_filt_set_try_get();
633 if (r == EOF_FILT) {
634 draining_state_switch();
635 return;
636 } else if (r == HAVE_FILT_SET) {
637 npv_audio_filt_p.pcm_written_ufrs_n = 0;
638 break;
640 /* r == AGAIN */
642 chans_buf_init(chans_buf, (int)npv_audio_filt_p.pcm_written_ufrs_n);
643 filt_set_remaining_ufrs_n = (snd_pcm_ufrs_t)npv_audio_filt_p.set->frs_n
644 - npv_audio_filt_p.pcm_written_ufrs_n;
645 if (filt_set_remaining_ufrs_n > ufrs_n)
646 ufrs_to_write_n = ufrs_n;
647 else
648 ufrs_to_write_n = filt_set_remaining_ufrs_n;
649 is_planar_fmt = avutil_audio_fr_fmt_is_planar(
650 npv_audio_filt_p.set->fmt);
651 written_ufrs_n = 0;
652 loop { /* short write loop */
653 snd_pcm_sfrs_t alsa_r;
654 u8 r_recover;
656 if (is_planar_fmt == NO)
657 alsa_r = snd_pcm_writei(pcm_p, chans_buf[0],
658 ufrs_to_write_n - written_ufrs_n);
659 else
660 alsa_r = snd_pcm_writen(pcm_p, (void**)chans_buf,
661 ufrs_to_write_n - written_ufrs_n);
662 r_recover = alsa_recover(alsa_r);
663 if (r_recover == AGAIN)
664 continue;
665 else if (r_recover == RECOVERED) {
666 /* account for the written frs anyway */
667 if (npv_audio_filt_p.pcm_written_ufrs_n == 0)
668 npv_clk_ref_time_point_update(
669 npv_audio_filt_p.set->pts,
670 written_ufrs_n);
671 npv_audio_filt_p.pcm_written_ufrs_n += written_ufrs_n;
672 if ((int)npv_audio_filt_p.pcm_written_ufrs_n ==
673 npv_audio_filt_p.set->frs_n)
674 /* set audio_filt_p.set->frs_n = 0 */
675 avutil_audio_set_unref(npv_audio_filt_p.set);
676 return;
678 /* r_recover == CONTINUE */
679 written_ufrs_n += (snd_pcm_ufrs_t)alsa_r;
680 if (written_ufrs_n == ufrs_to_write_n)
681 break;
682 chans_buf_inc(chans_buf, (int)alsa_r);
685 * this is here we update our ref time point for the audio clk
686 * because with a new filt set of frs, we get a new ts
688 * XXX: getting the "right" ts from ff is convoluted
690 if (npv_audio_filt_p.pcm_written_ufrs_n == 0)
691 npv_clk_ref_time_point_update(npv_audio_filt_p.set->pts,
692 written_ufrs_n);
693 npv_audio_filt_p.pcm_written_ufrs_n += written_ufrs_n;
694 ufrs_n -= written_ufrs_n;
696 if ((int)npv_audio_filt_p.pcm_written_ufrs_n
697 == npv_audio_filt_p.set->frs_n)
698 /* set audio_filt_p.av->frs_n = 0 */
699 avutil_audio_set_unref(npv_audio_filt_p.set);
701 #undef NO
702 #undef AGAIN
703 #undef RECOVERED
704 #undef CONTINUE
705 #undef HAVE_FILT_SET
706 #undef EOF_FILT
707 #undef NO_DEC_SET
708 /* fatal if the wiring cannot be done */
709 STATIC void pcm_layout2ff_fmt_strict(snd_pcm_fmt_t alsa_fmt,
710 snd_pcm_access_t alsa_access, enum avutil_audio_fr_fmt_t *ff_fmt,
711 bool print_info)
714 * ff fmt byte order is always native.
715 * here we handle little endian only
717 switch (alsa_fmt) {
718 case SND_PCM_FMT_FLOAT:
719 if (alsa_access == SND_PCM_ACCESS_RW_INTERLEAVED)
720 *ff_fmt = AVUTIL_AUDIO_FR_FMT_FLT;
721 else
722 *ff_fmt = AVUTIL_AUDIO_FR_FMT_FLTP;
723 break;
724 case SND_PCM_FMT_S32:
725 if (alsa_access == SND_PCM_ACCESS_RW_INTERLEAVED)
726 *ff_fmt = AVUTIL_AUDIO_FR_FMT_S32;
727 else
728 *ff_fmt = AVUTIL_AUDIO_FR_FMT_S32P;
729 break;
730 case SND_PCM_FMT_S16:
731 if (alsa_access == SND_PCM_ACCESS_RW_INTERLEAVED)
732 *ff_fmt = AVUTIL_AUDIO_FR_FMT_S16;
733 else
734 *ff_fmt = AVUTIL_AUDIO_FR_FMT_S16P;
735 break;
736 case SND_PCM_FMT_U8:
737 if (alsa_access == SND_PCM_ACCESS_RW_INTERLEAVED)
738 *ff_fmt = AVUTIL_AUDIO_FR_FMT_U8;
739 else
740 *ff_fmt = AVUTIL_AUDIO_FR_FMT_U8P;
741 break;
742 default:
743 fatal("unable to wire strictly alsa layout \"%s\"/\"%s\" to a ffmpeg format\n", snd_pcm_fmt_desc(alsa_fmt), snd_pcm_access_name(alsa_access));
745 if (print_info) {
746 u8 ff_fmt_str[STR_SZ];
748 avutil_get_audio_fr_fmt_str(ff_fmt_str, sizeof(ff_fmt_str),
749 *ff_fmt);
750 pout("alsa pcm layout \"%s\"/\"%s\" wired strictly to ffmpeg format \"%sbits\"\n", snd_pcm_fmt_desc(alsa_fmt), snd_pcm_access_name(alsa_access), ff_fmt_str);
754 * XXX: if it is ever used significantly, a fine granularity wiring strategy
755 * will be implemented instead of using the default wiring
757 STATIC uint64_t pcm_chmaps2ff_chans_layout(snd_pcm_t *pcm,
758 unsigned int pcm_chans_n, bool print_info)
760 int r;
761 uint64_t ff_chans_layout;
762 snd_pcm_chmap_t *pcm_chmap;
763 u8 chans_layout_str[STR_SZ]; /* should be overkill */
765 pcm_chmap = snd_pcm_get_chmap(pcm);
766 if (pcm_chmap == 0) {
767 if (print_info)
768 pout("alsa:no pcm channel map available, wiring to default ffmpeg channel layout\n");
769 } else {
770 if (print_info)
771 pout("alsa:your pcm device support channel maps, but fine granularity wiring strategy is not implemented\n");
772 free(pcm_chmap);
774 ff_chans_layout = avutil_get_default_chans_layout((int)pcm_chans_n);
775 avutil_get_chans_layout_str(chans_layout_str, sizeof(chans_layout_str),
776 (int)pcm_chans_n, ff_chans_layout);
777 if (print_info)
778 pout("alsa channel map wired to ffmpeg channel layout:\"%s\" (%u pcm channels)\n", chans_layout_str, pcm_chans_n);
779 return ff_chans_layout;
781 STATIC void init_pcm_once_public(u8 *pcm_str)
783 int r;
785 r = snd_pcm_open(&pcm_p, pcm_str, SND_PCM_ST_PLAYBACK,
786 SND_PCM_NONBLOCK);
787 if (r < 0) {
788 if (r == -EAGAIN)
789 fatal("alsa:\"%s\" pcm is already in use\n", pcm_str);
790 else
791 fatal("alsa:unable to open \"%s\" pcm for playback\n", pcm_str);
794 r = snd_pcm_poll_descriptors_n(pcm_p);
795 pout("alsa:have %d poll file descriptors\n", r);
796 if ((r <= 0) || (r > pcm_pollfds_n_max))
797 fatal("alsa:invalid count of alsa poll file descriptors\n");
798 pcm_pollfds_n_p =(u8)r;
799 memset(pcm_pollfds_p, 0, sizeof(pcm_pollfds_p));
800 snd_pcm_poll_descriptors(pcm_p, pcm_pollfds_p, pcm_pollfds_n_max);
802 STATIC void init_once_public(u8 *pcm_str)
804 int r;
806 memset(&st_p, 0, sizeof(st_p));
807 pkt_q_p = npv_pkt_q_new("audio");
808 dec_ctx_p = 0;
809 dec_sets_p.eof_receive = false;
810 dec_sets_p.n_max = 0;
811 dec_sets_p.n = 0;
812 dec_sets_p.a = 0;
813 init_pcm_once_public(pcm_str);
814 selected_ts_type_p = -1;
815 /* linux bug: still no CLOCK_MONOTONIC_RAW for timerfd */
816 errno = 0;
817 draining_timer_fd_p = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
818 if (draining_timer_fd_p == -1)
819 fatal("unable to get a draining timer file descriptor:%s\n", strerror(errno));
820 draining_p = false;
821 r = pthread_mutex_init(&dec_ctx_mutex_l, 0);
822 if (r != 0)
823 fatal("%d:unable to init the decoder mutex\n", r);
824 r = pthread_mutex_init(&dec_sets_p.mutex, 0);
825 if (r != 0)
826 fatal("%d:unable to init the mutex for the array of decoder sets\n", r);
828 STATIC void init_once_local(void)
830 int r;
832 dec_l = 0;
833 r = snd_output_stdio_attach(&pcm_pout_l, stdout, 0);
834 if (r < 0)
835 fatal("alsa:unable to attach stdout\n");
836 r = snd_output_stdio_attach(&pcm_perr_l, stderr, 0);
837 if (r < 0)
838 fatal("alsa:unable to attach stderr\n");
839 memset(pcm_silence_bufs_l, 0, sizeof(pcm_silence_bufs_l));
841 STATIC void dec_a_unref_all(void)
843 u16 set;
845 set = 0;
846 loop {
847 if (set == dec_sets_p.n)
848 break;
849 avutil_audio_set_unref(dec_sets_p.a[set]);
850 ++set;
852 dec_sets_p.n = 0;