vdr-plugin-softhddevice-drm-gles 1.6.2
audio.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
28#include <chrono>
29#include <cmath>
30#include <cstdint>
31#include <mutex>
32#include <vector>
33
34#include <alsa/asoundlib.h>
35
36extern "C" {
37#include <libavcodec/avcodec.h>
38#include <libavfilter/avfilter.h>
39#include <libavfilter/buffersink.h>
40#include <libavfilter/buffersrc.h>
41#include <libavutil/channel_layout.h>
42#include <libavutil/opt.h>
43}
44
45#include <vdr/thread.h>
46
47#include "audio.h"
48#include "codec_audio.h"
49#include "config.h"
50#include "event.h"
51#include "filllevel.h"
52#include "logger.h"
53#include "misc.h"
54#include "pidcontroller.h"
55#include "ringbuffer.h"
56#include "softhddevice.h"
57
62 : cThread("softhd audio"),
63 m_pDevice(device),
64 m_pConfig(m_pDevice->Config()),
65 m_pEventReceiver(device),
66 m_downmix(m_pConfig->ConfigAudioDownmix),
67 m_softVolume(m_pConfig->ConfigAudioSoftvol),
68 m_passthrough(m_pConfig->ConfigAudioPassthroughState ? m_pConfig->ConfigAudioPassthroughMask : 0),
69 m_pPCMDevice(m_pConfig->ConfigAudioPCMDevice),
70 m_pPassthroughDevice(m_pConfig->ConfigAudioPassthroughDevice),
71 m_appendAES(m_pConfig->ConfigAudioAutoAES),
72 m_pMixerChannel(m_pConfig->ConfigAudioMixerChannel)
73{
78}
79
80/******************************************************************************
81 * Audio filter and manipulation
82 *****************************************************************************/
83
95static void ReorderAudioFrame(uint16_t * buf, int size, int channels)
96{
97 int i;
98 int c;
99 int ls;
100 int rs;
101 int lfe;
102
103 switch (channels) {
104 case 5:
105 size /= 2;
106 for (i = 0; i < size; i += 5) {
107 c = buf[i + 2];
108 ls = buf[i + 3];
109 rs = buf[i + 4];
110 buf[i + 2] = ls;
111 buf[i + 3] = rs;
112 buf[i + 4] = c;
113 }
114 break;
115 case 6:
116 size /= 2;
117 for (i = 0; i < size; i += 6) {
118 c = buf[i + 2];
119 lfe = buf[i + 3];
120// ls = buf[i + 4]; tested from jsffm
121// rs = buf[i + 5];
122// buf[i + 2] = ls;
123// buf[i + 3] = rs;
124 buf[i + 2] = lfe;
125 buf[i + 3] = c;
126// buf[i + 4] = c;
127// buf[i + 5] = lfe;
128 }
129 break;
130 case 8:
131 size /= 2;
132 for (i = 0; i < size; i += 8) {
133 c = buf[i + 2];
134 lfe = buf[i + 3];
135 ls = buf[i + 4];
136 rs = buf[i + 5];
137 buf[i + 2] = ls;
138 buf[i + 3] = rs;
139 buf[i + 4] = c;
140 buf[i + 5] = lfe;
141 }
142 break;
143 }
144}
145
152void cSoftHdAudio::Normalize(uint16_t *samples, int count)
153{
154 int i;
155 int l;
156 int n;
157 uint32_t avg;
158 int factor;
159 uint16_t *data;
160
161 // average samples
162 l = count / m_bytesPerSample;
163 data = samples;
164 do {
165 n = l;
168 }
170 for (i = 0; i < n; ++i) {
171 int t;
172
173 t = data[i];
174 avg += (t * t) / m_normalizeSamples;
175 }
181 } else {
182 avg = 0;
183 for (i = 0; i < NORMALIZE_MAX_INDEX; ++i) {
185 }
186
187 // calculate normalize factor
188 if (avg > 0) {
189 factor = ((INT16_MAX / 8) * 1000U) / (uint32_t) sqrt(avg);
190 // smooth normalize
191 m_normalizeFactor = (m_normalizeFactor * 500 + factor * 500) / 1000;
194 }
197 }
198 } else {
199 factor = 1000;
200 }
201 LOGDEBUG2(L_SOUND, "audio: %s: avg %8d, fac=%6.3f, norm=%6.3f", __FUNCTION__,
202 avg, factor / 1000.0, m_normalizeFactor / 1000.0);
203 }
204
208 }
209 data += n;
210 l -= n;
211 } while (l > 0);
212
213 // apply normalize factor
214 for (i = 0; i < count / m_bytesPerSample; ++i) {
215 int t;
216
217 t = (samples[i] * m_normalizeFactor) / 1000;
218 if (t < INT16_MIN) {
219 t = INT16_MIN;
220 } else if (t > INT16_MAX) {
221 t = INT16_MAX;
222 }
223 samples[i] = t;
224 }
225}
226
233void cSoftHdAudio::Compress(uint16_t *samples, int count)
234{
235 int maxSample;
236 int i;
237 int factor;
238
239 // find loudest sample
240 maxSample = 0;
241 for (i = 0; i < count / m_bytesPerSample; ++i) {
242 int t;
243
244 t = abs(samples[i]);
245 if (t > maxSample) {
246 maxSample = t;
247 }
248 }
249
250 // calculate compression factor
251 if (maxSample > 0) {
252 factor = (INT16_MAX * 1000) / maxSample;
253 // smooth compression (FIXME: make configurable?)
254 m_compressionFactor = (m_compressionFactor * 950 + factor * 50) / 1000;
255 if (m_compressionFactor > factor) {
256 m_compressionFactor = factor; // no clipping
257 }
260 }
261 } else {
262 return; // silent nothing todo
263 }
264
265 LOGDEBUG2(L_SOUND, "audio: %s: max %5d, fac=%6.3f, com=%6.3f", __FUNCTION__, maxSample,
266 factor / 1000.0, m_compressionFactor / 1000.0);
267
268 // apply compression factor
269 for (i = 0; i < count / m_bytesPerSample; ++i) {
270 int t;
271
272 t = (samples[i] * m_compressionFactor) / 1000;
273 if (t < INT16_MIN) {
274 t = INT16_MIN;
275 } else if (t > INT16_MAX) {
276 t = INT16_MAX;
277 }
278 samples[i] = t;
279 }
280}
281
290void cSoftHdAudio::SoftAmplify(int16_t *samples, int count)
291{
292 int i;
293
294 // silence
295 if (m_volume == 0 || !m_amplifier) {
296 memset(samples, 0, count);
297 return;
298 }
299
300 for (i = 0; i < count / m_bytesPerSample; ++i) {
301 int t;
302
303 t = (samples[i] * m_amplifier) / 1000;
304 if (t < INT16_MIN) {
305 t = INT16_MIN;
306 } else if (t > INT16_MAX) {
307 t = INT16_MAX;
308 }
309 samples[i] = t;
310 }
311}
312
319void cSoftHdAudio::SetEq(int band[18], int onoff)
320{
321 int i;
322/*
323 LOGDEBUG2(L_SOUND, "audio: %s: %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i onoff %d", __FUNCTION__,
324 band[0], band[1], band[2], band[3], band[4], band[5], band[6], band[7],
325 band[8], band[9], band[10], band[11], band[12], band[13], band[14],
326 band[15], band[16], band[17], onoff);
327*/
328 for (i = 0; i < 18; i++) {
329 switch (band[i]) {
330 case 1:
331 m_equalizerBand[i] = 1.5;
332 break;
333 case 0:
334 m_equalizerBand[i] = 1;
335 break;
336 case -1:
337 m_equalizerBand[i] = 0.95;
338 break;
339 case -2:
340 m_equalizerBand[i] = 0.9;
341 break;
342 case -3:
343 m_equalizerBand[i] = 0.85;
344 break;
345 case -4:
346 m_equalizerBand[i] = 0.8;
347 break;
348 case -5:
349 m_equalizerBand[i] = 0.75;
350 break;
351 case -6:
352 m_equalizerBand[i] = 0.7;
353 break;
354 case -7:
355 m_equalizerBand[i] = 0.65;
356 break;
357 case -8:
358 m_equalizerBand[i] = 0.6;
359 break;
360 case -9:
361 m_equalizerBand[i] = 0.55;
362 break;
363 case -10:
364 m_equalizerBand[i] = 0.5;
365 break;
366 case -11:
367 m_equalizerBand[i] = 0.45;
368 break;
369 case -12:
370 m_equalizerBand[i] = 0.4;
371 break;
372 case -13:
373 m_equalizerBand[i] = 0.35;
374 break;
375 case -14:
376 m_equalizerBand[i] = 0.3;
377 break;
378 case -15:
379 m_equalizerBand[i] = 0.25;
380 break;
381 }
382 }
383
384 m_filterChanged = 1;
385 m_useEqualizer = onoff;
386}
387
401int cSoftHdAudio::InitFilter(AVCodecContext *audioCtx)
402{
403 const AVFilter *abuffer;
404 AVFilterContext *pfilterCtx[3];
405 const AVFilter *eq;
406 const AVFilter *aformat;
407 const AVFilter *abuffersink;
408 char channelLayout[64];
409 char optionsStr[1024];
410 int err, i, numFilter = 0;
411
412 // Before filter init set HW parameter.
413 if (audioCtx->sample_rate != (int)m_hwSampleRate ||
414 (audioCtx->ch_layout.nb_channels != (int)m_hwNumChannels &&
415 !(m_downmix && m_hwNumChannels == 2))) {
416
417 err = AlsaSetup(audioCtx->ch_layout.nb_channels, audioCtx->sample_rate, 0);
418 if (err)
419 return err;
420 }
421
422 m_pTimebase = &audioCtx->pkt_timebase;
423
424#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7,16,100)
425 avfilter_register_all();
426#endif
427
428 if (!(m_pFilterGraph = avfilter_graph_alloc())) {
429 LOGERROR("audio: %s: Unable to create filter graph.", __FUNCTION__);
430 return -1;
431 }
432
433 // input buffer
434 if (!(abuffer = avfilter_get_by_name("abuffer"))) {
435 LOGWARNING("audio: %s: Could not find the abuffer filter.", __FUNCTION__);
436 avfilter_graph_free(&m_pFilterGraph);
437 return -1;
438 }
439 if (!(m_pBuffersrcCtx = avfilter_graph_alloc_filter(m_pFilterGraph, abuffer, "src"))) {
440 LOGWARNING("audio: %s: Could not allocate the m_pBuffersrcCtx instance.", __FUNCTION__);
441 avfilter_graph_free(&m_pFilterGraph);
442 return -1;
443 }
444
445 av_channel_layout_describe(&audioCtx->ch_layout, channelLayout, sizeof(channelLayout));
446
447 LOGDEBUG2(L_SOUND, "audio: %s: IN channelLayout %s sample_fmt %s sample_rate %d channels %d", __FUNCTION__,
448 channelLayout, av_get_sample_fmt_name(audioCtx->sample_fmt), audioCtx->sample_rate, audioCtx->ch_layout.nb_channels);
449
450 av_opt_set (m_pBuffersrcCtx, "channel_layout", channelLayout, AV_OPT_SEARCH_CHILDREN);
451 av_opt_set (m_pBuffersrcCtx, "sample_fmt", av_get_sample_fmt_name(audioCtx->sample_fmt), AV_OPT_SEARCH_CHILDREN);
452 av_opt_set_q (m_pBuffersrcCtx, "time_base", (AVRational){ 1, audioCtx->sample_rate }, AV_OPT_SEARCH_CHILDREN);
453 av_opt_set_int(m_pBuffersrcCtx, "sample_rate", audioCtx->sample_rate, AV_OPT_SEARCH_CHILDREN);
454// av_opt_set_int(m_pBuffersrcCtx, "channel_counts", audioCtx->channels, AV_OPT_SEARCH_CHILDREN);
455
456 // initialize the filter with NULL options, set all options above.
457 if (avfilter_init_str(m_pBuffersrcCtx, NULL) < 0) {
458 LOGWARNING("audio: %s: Could not initialize the abuffer filter.", __FUNCTION__);
459 avfilter_graph_free(&m_pFilterGraph);
460 return -1;
461 }
462
463 // superequalizer
464 if (m_useEqualizer) {
465 if (!(eq = avfilter_get_by_name("superequalizer"))) {
466 LOGWARNING("audio: %s: Could not find the superequalizer filter.", __FUNCTION__);
467 avfilter_graph_free(&m_pFilterGraph);
468 return -1;
469 }
470 if (!(pfilterCtx[numFilter] = avfilter_graph_alloc_filter(m_pFilterGraph, eq, "superequalizer"))) {
471 LOGWARNING("audio: %s: Could not allocate the superequalizer instance.", __FUNCTION__);
472 avfilter_graph_free(&m_pFilterGraph);
473 return -1;
474 }
475 snprintf(optionsStr, sizeof(optionsStr),"1b=%.2f:2b=%.2f:3b=%.2f:4b=%.2f:5b=%.2f"
476 ":6b=%.2f:7b=%.2f:8b=%.2f:9b=%.2f:10b=%.2f:11b=%.2f:12b=%.2f:13b=%.2f:14b=%.2f:"
477 "15b=%.2f:16b=%.2f:17b=%.2f:18b=%.2f ", m_equalizerBand[0], m_equalizerBand[1],
482 if (avfilter_init_str(pfilterCtx[numFilter], optionsStr) < 0) {
483 LOGWARNING("audio: %s: Could not initialize the superequalizer filter.", __FUNCTION__);
484 avfilter_graph_free(&m_pFilterGraph);
485 return -1;
486 }
487 numFilter++;
488 }
489
490 // aformat
491 AVChannelLayout channel_layout;
492 av_channel_layout_default(&channel_layout, m_hwNumChannels);
493 av_channel_layout_describe(&channel_layout, channelLayout, sizeof(channelLayout));
494 av_channel_layout_uninit(&channel_layout);
495 // should use IN layout if more then 2 ch!?
496 LOGDEBUG2(L_SOUND, "audio: %s: OUT m_downmix %d m_hwNumChannels %d m_hwSampleRate %d channelLayout %s bytes_per_sample %d",
497 __FUNCTION__, m_downmix, m_hwNumChannels, m_hwSampleRate, channelLayout, av_get_bytes_per_sample(AV_SAMPLE_FMT_S16));
498 if (!(aformat = avfilter_get_by_name("aformat"))) {
499 LOGWARNING("audio: %s: Could not find the aformat filter.", __FUNCTION__);
500 avfilter_graph_free(&m_pFilterGraph);
501 return -1;
502 }
503 if (!(pfilterCtx[numFilter] = avfilter_graph_alloc_filter(m_pFilterGraph, aformat, "aformat"))) {
504 LOGWARNING("audio: %s: Could not allocate the aformat instance.", __FUNCTION__);
505 avfilter_graph_free(&m_pFilterGraph);
506 return -1;
507 }
508 snprintf(optionsStr, sizeof(optionsStr),
509 "sample_fmts=%s:sample_rates=%d:channel_layouts=%s",
510 av_get_sample_fmt_name(AV_SAMPLE_FMT_S16), m_hwSampleRate, channelLayout);
511 if (avfilter_init_str(pfilterCtx[numFilter], optionsStr) < 0) {
512 LOGWARNING("audio: %s: Could not initialize the aformat filter.", __FUNCTION__);
513 avfilter_graph_free(&m_pFilterGraph);
514 return -1;
515 }
516 numFilter++;
517
518 // abuffersink
519 if (!(abuffersink = avfilter_get_by_name("abuffersink"))) {
520 LOGWARNING("audio: %s: Could not find the abuffersink filter.", __FUNCTION__);
521 avfilter_graph_free(&m_pFilterGraph);
522 return -1;
523 }
524 if (!(pfilterCtx[numFilter] = avfilter_graph_alloc_filter(m_pFilterGraph, abuffersink, "sink"))) {
525 LOGWARNING("audio: %s: Could not allocate the abuffersink instance.", __FUNCTION__);
526 avfilter_graph_free(&m_pFilterGraph);
527 return -1;
528 }
529 if (avfilter_init_str(pfilterCtx[numFilter], NULL) < 0) {
530 LOGWARNING("audio: %s: Could not initialize the abuffersink instance.", __FUNCTION__);
531 avfilter_graph_free(&m_pFilterGraph);
532 return -1;
533 }
534 numFilter++;
535
536 // Connect the filters
537 for (i = 0; i < numFilter; i++) {
538 if (i == 0) {
539 err = avfilter_link(m_pBuffersrcCtx, 0, pfilterCtx[i], 0);
540 } else {
541 err = avfilter_link(pfilterCtx[i - 1], 0, pfilterCtx[i], 0);
542 }
543 }
544 if (err < 0) {
545 LOGWARNING("audio: %s: Error connecting audio filters", __FUNCTION__);
546 avfilter_graph_free(&m_pFilterGraph);
547 return -1;
548 }
549
550 // Configure the graph.
551 if (avfilter_graph_config(m_pFilterGraph, NULL) < 0) {
552 LOGWARNING("audio: %s: Error configuring the audio filter graph", __FUNCTION__);
553 avfilter_graph_free(&m_pFilterGraph);
554 return -1;
555 }
556
557 m_pBuffersinkCtx = pfilterCtx[numFilter - 1];
558 m_filterChanged = 0;
559 m_filterReady = 1;
560
561 return 0;
562}
563
564/******************************************************************************
565 * Audio stream handling
566 *****************************************************************************/
567
577{
578 std::lock_guard<std::mutex> lock(m_mutex);
579
580 if (!HasInputPts())
581 return;
582
583 int64_t dropMs = std::max((int64_t)0, ptsMs - GetOutputPtsMsInternal());
584 int dropFrames = MsToFrames(dropMs);
585 int dropBytes = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, dropFrames);
586
587 dropBytes = std::min(dropBytes, (int)m_pRingbuffer.UsedBytes());
588
589 if (dropBytes > 0) {
590 LOGDEBUG2(L_AV_SYNC, "audio: %s: dropping %dms audio samples to start in sync with the video (output PTS %s -> %s)",
591 __FUNCTION__,
592 dropMs,
594 Timestamp2String(ptsMs, 1));
595
598 m_fillLevel.WroteFrames(dropFrames);
599 m_pRingbuffer.ReadAdvance(dropBytes);
600 }
601}
602
608void cSoftHdAudio::EnqueueFrame(AVFrame *frame)
609{
610 if (!frame)
611 return;
612
613 uint16_t *buffer;
614
615 int byteCount = frame->nb_samples * frame->ch_layout.nb_channels * m_bytesPerSample;
616 buffer = (uint16_t *)frame->data[0];
617
618 if (m_compression) { // in place operation
619 Compress(buffer, byteCount);
620 }
621 if (m_normalize) { // in place operation
622 Normalize(buffer, byteCount);
623 }
624 ReorderAudioFrame(buffer, byteCount, frame->ch_layout.nb_channels);
625
626 Enqueue((uint16_t *)buffer, byteCount, frame);
627
628 av_frame_free(&frame);
629}
630
635{
636 uint16_t *spdif = m_pauseBurst.data();
637
638 spdif[0] = htole16(IEC61937_PREAMBLE1);
639 spdif[1] = htole16(IEC61937_PREAMBLE2);
640 spdif[2] = htole16(IEC61937_NULL);
641 spdif[3] = 0;
642
643 memset(m_pauseBurst.data() + 4, 0, m_spdifBurstSize - 8);
644}
645
655void cSoftHdAudio::EnqueueSpdif(uint16_t *buffer, int count, AVFrame *frame)
656{
657 std::lock_guard<std::mutex> lock(m_pauseMutex);
658
659 if (count != m_spdifBurstSize) {
660 LOGDEBUG2(L_SOUND, "audio: %s: spdif burst size changed %d -> %d, rebuild pause burst", __FUNCTION__, m_spdifBurstSize, count);
661 m_spdifBurstSize = count;
662 m_pauseBurst.resize(m_spdifBurstSize / 2);
663
665 }
666
667 Enqueue(buffer, count, frame);
668}
669
677void cSoftHdAudio::Enqueue(uint16_t *buffer, int count, AVFrame *frame)
678{
679 std::lock_guard<std::mutex> lock(m_mutex);
680
681 // pitch adjustment
682 if (m_pitchAdjustFrameCounter == 0 && std::abs(m_pitchPpm) > 1) { // only adjust if pitch has a significant value to prevent overly large values/division by zero
683 int oneFrameBytes = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, 1);
684
685 if (m_pitchPpm < 0 && m_pRingbuffer.Write((const uint16_t *)buffer, oneFrameBytes)) // insert additional frame
687 else if (m_pitchPpm > 0) // drop frame
688 count = std::max(0, count - oneFrameBytes);
689
690 m_pitchAdjustFrameCounter = std::round(1'000'000.0 / std::abs(m_pitchPpm));
691 }
692
693 m_pitchAdjustFrameCounter = std::max(0, m_pitchAdjustFrameCounter - (int)snd_pcm_bytes_to_frames(m_pAlsaPCMHandle, count));
694
695 // write to ringbuffer
696 int bytesWritten = m_pRingbuffer.Write((const uint16_t *)buffer, count);
697 if (bytesWritten != count)
698 LOGERROR("audio: %s: can't place %d samples in ring buffer", __FUNCTION__, count);
699
700 m_fillLevel.ReceivedFrames(snd_pcm_bytes_to_frames(m_pAlsaPCMHandle, bytesWritten));
701
702 if (frame->pts != AV_NOPTS_VALUE) {
703 // discontinuity check, force a resync if the new pts differs more than AV_SYNC_BORDER_MS to the last
704 if (m_inputPts != AV_NOPTS_VALUE && std::abs(PtsToMs(m_inputPts) - PtsToMs(frame->pts)) > AV_SYNC_BORDER_MS) {
705 LOGDEBUG2(L_AV_SYNC, "audio: %s: discontinuity detected in audio PTS %s -> %s%s", __FUNCTION__,
707 PtsToMs(m_inputPts) > PtsToMs(frame->pts) ? " (PTS wrapped)" : "");
708 m_eventQueue.push_back(ScheduleResyncAtPtsMsEvent{PtsToMs(frame->pts)});
709 }
710
711 m_inputPts = frame->pts;
712 }
713}
714
729int cSoftHdAudio::Setup(AVCodecContext *ctx, int samplerate, int channels, int passthrough)
730{
731 int err = 0;
732
733 m_pTimebase = &ctx->pkt_timebase;
734
735 // skip setup, nothing changed
736 if (samplerate == (int)m_hwSampleRate &&
737 (channels == (int)m_hwNumChannels || (m_downmix && m_hwNumChannels == 2)))
738 return 1;
739
740 err = AlsaSetup(channels, samplerate, passthrough);
741 if (err)
742 LOGERROR("audio: %s: failed!", __FUNCTION__);
743
744 return err;
745}
746
753{
754 AVFrame *outframe = nullptr;
755 outframe = av_frame_alloc();
756 if (!outframe) {
757 LOGERROR("audio: %s: Error allocating frame", __FUNCTION__);
758 return NULL;
759 }
760
761 int err = av_buffersink_get_frame(m_pBuffersinkCtx, outframe);
762
763 if (err == AVERROR(EAGAIN)) {
764// LOGERROR("audio: %s: Error filtering AVERROR(EAGAIN)", __FUNCTION__);
765 av_frame_free(&outframe);
766 } else if (err == AVERROR_EOF) {
767 LOGERROR("audio: %s: Error filtering AVERROR_EOF", __FUNCTION__);
768 av_frame_free(&outframe);
769 } else if (err < 0) {
770 LOGERROR("audio: %s: Error filtering the data", __FUNCTION__);
771 av_frame_free(&outframe);
772 }
773
774 return outframe;
775}
776
785int cSoftHdAudio::CheckForFilterReady(AVCodecContext *ctx)
786{
788// LOGDEBUG2(L_SOUND, "audio: %s: m_filterReady %d sink_links_count %d channels %d nb_filters %d nb_outputs %d channels %d m_filterChanged %d",
789// __FUNCTION__, m_filterReady,
790// m_pFilterGraph->sink_links_count, m_pFilterGraph->sink_links[0]->channels,
791// m_pFilterGraph->filters[m_pFilterGraph->nb_filters - 1]->nb_outputs,
792// m_pFilterGraph->nb_filters, m_pFilterGraph->filters[m_pFilterGraph->nb_filters - 1]->outputs[m_pFilterGraph->filters[m_pFilterGraph->nb_filters - 1]->nb_outputs - 1]->channels,
793// m_filterChanged);
794 avfilter_graph_free(&m_pFilterGraph);
795 m_filterReady = 0;
796 LOGDEBUG2(L_SOUND, "audio: %s: Free the filter graph.", __FUNCTION__);
797 }
798
799 if (!m_filterReady) {
800 if (InitFilter(ctx)) {
801 LOGDEBUG2(L_SOUND, "audio: %s: AudioFilterReady failed!", __FUNCTION__);
802 return 1;
803 }
804 }
805
806 return 0;
807}
808
818void cSoftHdAudio::Filter(AVFrame *inframe, AVCodecContext *ctx)
819{
820 AVFrame *outframe = NULL;
821 int err = -1;
822 int err_count = 0;
823
824 if (inframe) {
825 while (err < 0) {
826 if (CheckForFilterReady(ctx)) {
827 av_frame_unref(inframe);
828 return;
829 }
830
831 err = av_buffersrc_add_frame(m_pBuffersrcCtx, inframe);
832 if (err < 0) {
833 if (err_count) {
834 char errbuf[128];
835 av_strerror(err, errbuf, sizeof(errbuf));
836 LOGERROR("audio: %s: Error submitting the frame to the filter fmt %s channels %d %s", __FUNCTION__,
837 av_get_sample_fmt_name(ctx->sample_fmt), ctx->ch_layout.nb_channels, errbuf);
838 av_frame_unref(inframe);
839 return;
840 } else {
841 m_filterChanged = 1;
842 err_count++;
843 LOGDEBUG2(L_SOUND, "audio: %s: m_filterChanged %d err_count %d", __FUNCTION__, m_filterChanged, err_count);
844 }
845 }
846 }
847 }
848
849// if (!inframe)
850// LOGDEBUG2(L_SOUND, "audio: %s: NO inframe!", __FUNCTION__);
851
852 outframe = FilterGetFrame();
853 EnqueueFrame(outframe);
854}
855
862{
863 std::lock_guard<std::mutex> lock(m_mutex);
864
865 LOGDEBUG2(L_SOUND, "audio: %s", __FUNCTION__);
866
867 if (!m_initialized)
868 return;
869
872
878 m_filterChanged = 1;
879}
880
885{
886 // FIXME: not correct, if multiple buffer are in use
887 return m_pRingbuffer.UsedBytes();
888}
889
903{
904 std::lock_guard<std::mutex> lock(m_mutex);
905
906 return GetOutputPtsMsInternal();
907}
908
910{
911 return PtsToMs(m_inputPts) - FramesToMs(snd_pcm_bytes_to_frames(m_pAlsaPCMHandle, m_pRingbuffer.UsedBytes()));
912}
913
924{
925 std::lock_guard<std::mutex> lock(m_mutex);
926
928 return AV_NOPTS_VALUE;
929
930 snd_pcm_sframes_t delayFrames;
931 if (snd_pcm_delay(m_pAlsaPCMHandle, &delayFrames) < 0)
932 delayFrames = 0L;
933
934 // subtract baseline to ignore pause bursts already in the buffer
935 delayFrames -= m_hwBaseline;
936
937 return GetOutputPtsMsInternal() - FramesToMs(delayFrames);
938}
939
946{
947 std::lock_guard<std::mutex> lock(m_mutex);
948
950 return AV_NOPTS_VALUE;
951
952 snd_pcm_sframes_t delayFrames;
953 if (snd_pcm_delay(m_pAlsaPCMHandle, &delayFrames) < 0)
954 delayFrames = 0L;
955
956 return FramesToMs(delayFrames);
957}
958
965{
966 int64_t ptsMs = GetHardwareOutputPtsMs();
967 if (ptsMs == AV_NOPTS_VALUE)
968 return AV_NOPTS_VALUE;
969
970 return MsToPts(ptsMs);
971}
972
979{
980 m_volume = volume;
981 // reduce loudness for stereo output
983 volume -= m_stereoDescent;
984 if (volume < 0) {
985 volume = 0;
986 } else if (volume > 1000) {
987 volume = 1000;
988 }
989 }
990 m_amplifier = volume;
991 if (!m_softVolume) {
992 AlsaSetVolume(volume);
993 }
994}
995
1002{
1003 std::lock_guard<std::mutex> lock(m_pauseMutex);
1004 LOGDEBUG2(L_SOUND, "audio: %s: %d", __FUNCTION__, pause);
1005
1006 m_paused = pause;
1007}
1008
1015void cSoftHdAudio::SetNormalize(bool enable, int maxfac)
1016{
1017 m_normalize = enable;
1018 m_normalizeMaxFactor = maxfac;
1019}
1020
1027void cSoftHdAudio::SetCompression(bool enable, int maxfac)
1028{
1029 m_compression = enable;
1030
1031 m_compressionMaxFactor = maxfac;
1032 if (!m_compressionFactor) {
1033 m_compressionFactor = 1000;
1034 }
1037 }
1038}
1039
1046{
1047 m_stereoDescent = delta;
1048 SetVolume(m_volume); // update channel delta
1049}
1050
1057{
1058 m_passthrough = mask;
1059
1060 // Reset m_pitchPpm if we change the passthrough handling, otherwise
1061 // we may continue to adjust the pitch in Enqueue() when in passthrough
1062 if (m_passthrough)
1063 m_pitchPpm = 0;
1064}
1065
1076{
1077 if (!m_initialized) {
1078 AlsaInit();
1079 m_initialized = true;
1080 }
1081}
1082
1091{
1092 LOGDEBUG2(L_SOUND, "audio: %s", __FUNCTION__);
1093
1094 Stop();
1095
1096 if (!m_initialized)
1097 return;
1098
1099 avfilter_graph_free(&m_pFilterGraph);
1100 AlsaExit();
1101 m_initialized = false;
1102}
1103
1104/******************************************************************************
1105 * A L S A
1106 *****************************************************************************/
1107
1112{
1113
1114 if (snd_pcm_state(m_pAlsaPCMHandle) == SND_PCM_STATE_XRUN && m_passthrough == 0)
1116
1117 if (snd_pcm_recover(m_pAlsaPCMHandle, error, 0) < 0)
1118 LOGERROR("audio: %s: Cannot recover: %s", __FUNCTION__, snd_strerror(error));
1119
1120 snd_pcm_prepare(m_pAlsaPCMHandle);
1121}
1122
1130
1138
1145{
1146 snd_pcm_state_t state = snd_pcm_state(m_pAlsaPCMHandle);
1147 if (state == SND_PCM_STATE_OPEN)
1148 return;
1149
1150 LOGDEBUG2(L_SOUND, "audio: %s entered in pcm state %s", __FUNCTION__, snd_pcm_state_name(state));
1151
1152 int err;
1153 if (m_passthrough && !drop) {
1154 switch (state) {
1155 case SND_PCM_STATE_SETUP:
1156 case SND_PCM_STATE_XRUN:
1157 case SND_PCM_STATE_DRAINING:
1158 err = snd_pcm_prepare(m_pAlsaPCMHandle);
1159 if (err < 0)
1160 LOGERROR("audio: %s: snd_pcm_prepare(): %s", __FUNCTION__, snd_strerror(err));
1161 break;
1162 default:
1163 break;
1164 }
1165 } else {
1166 err = snd_pcm_drop(m_pAlsaPCMHandle);
1167 if (err < 0)
1168 LOGERROR("audio: %s: snd_pcm_drop(): %s", __FUNCTION__, snd_strerror(err));
1169 err = snd_pcm_prepare(m_pAlsaPCMHandle);
1170 if (err < 0)
1171 LOGERROR("audio: %s: snd_pcm_prepare(): %s", __FUNCTION__, snd_strerror(err));
1172 }
1173
1174 // reset audio processing values
1175 m_compressionFactor = 2000;
1178
1180 m_normalizeReady = 0;
1181
1182 for (int i = 0; i < NORMALIZE_MAX_INDEX; ++i)
1183 m_normalizeAverage[i] = 0U;
1184
1185 m_normalizeFactor = 1000;
1186
1187 state = snd_pcm_state(m_pAlsaPCMHandle);
1188 LOGDEBUG2(L_SOUND, "audio: %s left in pcm state %s", __FUNCTION__, snd_pcm_state_name(state));
1189}
1190
1191/******************************************************************************
1192 * Thread playback
1193 *****************************************************************************/
1194
1200{
1201 LOGDEBUG("audio: thread started");
1202 while (Running()) {
1203 bool scheduleImmediately = CyclicCall();
1204 ProcessEvents();
1205
1206 if (scheduleImmediately)
1207 usleep(1000);
1208 else
1209 usleep(10000);
1210 }
1211 LOGDEBUG("audio: thread stopped");
1212}
1213
1218{
1219 if (!Active())
1220 return;
1221
1222 LOGDEBUG("audio: stopping thread");
1223 Cancel(2);
1224}
1225
1238{
1239 std::lock_guard<std::mutex> lock1(m_pauseMutex);
1240
1241 // do nothing in paused PCM mode
1242 if (m_paused && !m_passthrough)
1243 return false;
1244
1245 // check, if the alsa device is ready for input
1246 int ret = snd_pcm_wait(m_pAlsaPCMHandle, 150);
1247 if (ret < 0) {
1248 LOGDEBUG2(L_SOUND, "audio: %s: Handle error in wait", __FUNCTION__);
1249 HandleError(ret);
1250 return false;
1251 } else if (ret == 0) {
1252 snd_pcm_state_t state = snd_pcm_state(m_pAlsaPCMHandle);
1253 LOGERROR("audio: %s: snd_pcm_wait() timeout (state %s)", __FUNCTION__, snd_pcm_state_name(state));
1254 if (state == SND_PCM_STATE_PREPARED) {
1255 LOGDEBUG2(L_SOUND, "audio: %s: force start", __FUNCTION__);
1256 snd_pcm_start(m_pAlsaPCMHandle);
1257 return true;
1258 }
1259 return false;
1260 }
1261
1262 std::lock_guard<std::mutex> lock2(m_mutex);
1263
1264 // query available space in alsa buffer
1265 int freeAlsaBufferFrames = snd_pcm_avail_update(m_pAlsaPCMHandle);
1266 if (freeAlsaBufferFrames < 0) {
1267 if (freeAlsaBufferFrames == -EAGAIN) {
1268 LOGDEBUG2(L_SOUND, "audio: %s: -EAGAIN", __FUNCTION__);
1269 return true;
1270 }
1271
1272 LOGDEBUG2(L_SOUND, "audio: %s: Handle error in avail", __FUNCTION__);
1273 HandleError(freeAlsaBufferFrames);
1274 return false;
1275 }
1276
1277 size_t freeAlsaBufferBytes = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, freeAlsaBufferFrames);
1278 if (m_passthrough && m_paused) {
1279 // only write, if there is space for a full pause burst
1280 if ((int)freeAlsaBufferBytes < m_spdifBurstSize)
1281 return false;
1282
1283 // send a pause burst to keep the audio stream locked
1284 return SendPause();
1285 }
1286
1287 return SendAudio(freeAlsaBufferFrames);
1288}
1289
1298bool cSoftHdAudio::SendAudio(int freeAlsaBufferFrames)
1299{
1300 int bytesToWrite;
1301 int freeAlsaBufferBytes = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, freeAlsaBufferFrames);
1302
1303 // query ringbuffer fill level
1304 const void *data;
1305 ssize_t ringBufferFillLevelBytes = m_pRingbuffer.GetReadPointer(&data);
1306
1307 bytesToWrite = std::min(freeAlsaBufferBytes, (int)ringBufferFillLevelBytes);
1308
1309 if (bytesToWrite == 0)
1310 return false;
1311
1312 // muting pass-through AC-3, can produce disturbance
1313 if (m_volume == 0 || (m_softVolume && !m_passthrough)) {
1314 // FIXME: quick&dirty cast
1315 SoftAmplify((int16_t *) data, bytesToWrite);
1316 // FIXME: if not all are written, we double amplify them
1317 }
1318
1319 int framesToWrite = snd_pcm_bytes_to_frames(m_pAlsaPCMHandle, bytesToWrite);
1320 int framesWritten;
1321 if (m_alsaUseMmap)
1322 framesWritten = snd_pcm_mmap_writei(m_pAlsaPCMHandle, data, framesToWrite);
1323 else
1324 framesWritten = snd_pcm_writei(m_pAlsaPCMHandle, data, framesToWrite);
1325
1326 m_fillLevel.WroteFrames(framesWritten);
1327
1328 int bytesWritten = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, framesWritten);
1329 m_pRingbuffer.ReadAdvance(bytesWritten);
1330
1331 if (framesWritten < 0) {
1332 if (framesWritten == -EAGAIN) {
1333 LOGDEBUG2(L_SOUND, "audio: %s: -EAGAIN", __FUNCTION__);
1334 return true;
1335 }
1336
1337 LOGWARNING("audio: %s: writei failed: %s", __FUNCTION__, snd_strerror(framesWritten));
1338
1339 if (snd_pcm_recover(m_pAlsaPCMHandle, framesWritten, 0) < 0)
1340 LOGERROR("audio: %s: failed to recover from writei: %s", __FUNCTION__, snd_strerror(framesWritten));
1341
1342 return false;
1343 }
1344
1345 if (framesWritten != framesToWrite) {
1346 LOGWARNING("audio: %s: not all frames written", __FUNCTION__);
1347 return false;
1348 }
1349
1350 return true;
1351}
1352
1359{
1360 int framesToWrite = snd_pcm_bytes_to_frames(m_pAlsaPCMHandle, m_spdifBurstSize);
1361
1362 int framesWritten;
1363 if (m_alsaUseMmap)
1364 framesWritten = snd_pcm_mmap_writei(m_pAlsaPCMHandle, m_pauseBurst.data(), framesToWrite);
1365 else
1366 framesWritten = snd_pcm_writei(m_pAlsaPCMHandle, m_pauseBurst.data(), framesToWrite);
1367
1368 if (framesWritten != framesToWrite) {
1369 if (framesWritten < 0) {
1370 if (framesWritten == -EAGAIN)
1371 return true;
1372
1373 LOGWARNING("audio: %s: writei failed: %s", __FUNCTION__, snd_strerror(framesWritten));
1374
1375 if (snd_pcm_recover(m_pAlsaPCMHandle, framesWritten, 0) < 0)
1376 LOGERROR("audio: %s: failed to recover from writei: %s", __FUNCTION__, snd_strerror(framesWritten));
1377
1378 return false;
1379 } else {
1380 LOGWARNING("audio: %s: not all frames written", __FUNCTION__);
1381 return false;
1382 }
1383 }
1384
1385// LOGDEBUG2(L_SOUND, "audio: %s: %d frames (%dms) written", __FUNCTION__, framesWritten, FramesToMs(framesWritten));
1386 return true;
1387}
1388
1393{
1395 m_hwBaseline = 0;
1396
1397 if (!m_passthrough)
1398 return;
1399
1400 snd_pcm_sframes_t delayFrames = 0;
1401 if (snd_pcm_delay(m_pAlsaPCMHandle, &delayFrames) >= 0)
1402 m_hwBaseline = delayFrames;
1403
1404 LOGDEBUG2(L_SOUND, "audio: %s: first real audio was sent, hwBaseline %ld frames (%dms)", __FUNCTION__, m_hwBaseline, FramesToMs(m_hwBaseline));
1406 }
1407}
1408
1413{
1414 std::lock_guard<std::mutex> lock(m_mutex);
1415
1416 LOGDEBUG2(L_SOUND, "audio: %s: reset hw delay baseline to 0", __FUNCTION__);
1417 m_hwBaseline = 0;
1419}
1420
1425{
1426 for (Event event : m_eventQueue)
1428
1429 m_eventQueue.clear();
1430}
1431
1440char *cSoftHdAudio::OpenAlsaDevice(const char *device, int passthrough)
1441{
1442 int err;
1443 char tmp[80];
1444
1445 if (!device)
1446 return NULL;
1447
1448 LOGDEBUG2(L_SOUND, "audio: %s: try opening %sdevice '%s'", __FUNCTION__, passthrough ? "pass-through " : "", device);
1449
1450 if (passthrough && m_appendAES) {
1451 if (!(strchr(device, ':'))) {
1452 sprintf(tmp, "%s:AES0=%d,AES1=%d,AES2=0,AES3=%d",
1453 device,
1454 IEC958_AES0_NONAUDIO | IEC958_AES0_PRO_EMPHASIS_NONE,
1455 IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
1456 IEC958_AES3_CON_FS_48000);
1457 } else {
1458 sprintf(tmp, "%s,AES0=%d,AES1=%d,AES2=0,AES3=%d",
1459 device,
1460 IEC958_AES0_NONAUDIO | IEC958_AES0_PRO_EMPHASIS_NONE,
1461 IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
1462 IEC958_AES3_CON_FS_48000);
1463 }
1464 LOGDEBUG2(L_SOUND, "audio: %s: auto append AES: %s -> %s", __FUNCTION__, device, tmp);
1465 } else {
1466 sprintf(tmp, "%s", device);
1467 }
1468
1469 // open none blocking; if device is already used, we don't want wait
1470 if ((err = snd_pcm_open(&m_pAlsaPCMHandle, tmp, SND_PCM_STREAM_PLAYBACK,
1471 SND_PCM_NONBLOCK)) < 0) {
1472
1473 LOGWARNING("audio: %s: could not open device '%s' error: %s", __FUNCTION__, device, snd_strerror(err));
1474 return NULL;
1475 }
1476
1477 LOGDEBUG2(L_SOUND, "audio: %s: opened %sdevice '%s'", __FUNCTION__, passthrough ? "pass-through " : "", device);
1478
1479 return (char *)device;
1480}
1481
1492char *cSoftHdAudio::FindAlsaDevice(const char *devname, const char *hint, int passthrough)
1493{
1494 char **hints;
1495 int err;
1496 char **n;
1497 char *name;
1498
1499 err = snd_device_name_hint(-1, devname, (void ***)&hints);
1500 if (err != 0) {
1501 LOGWARNING("audio: %s: Cannot get device names for %s!", __FUNCTION__, hint);
1502 return NULL;
1503 }
1504
1505 n = hints;
1506 while (*n != NULL) {
1507 name = snd_device_name_get_hint(*n, "NAME");
1508
1509 if (name && strstr(name, hint)) {
1510 if (OpenAlsaDevice(name, passthrough)) {
1511 snd_device_name_free_hint((void **)hints);
1512 return name;
1513 }
1514 }
1515
1516 if (name)
1517 free(name);
1518 n++;
1519 }
1520
1521 snd_device_name_free_hint((void **)hints);
1522 return NULL;
1523}
1524
1529{
1530 char *device = NULL;
1531 bool freeDevice = false; // track if device needs to be freed
1532 int err;
1533 LOGDEBUG2(L_SOUND, "audio: %s: passthrough %d", __FUNCTION__, m_passthrough);
1534
1535 // try user set device
1536 if (m_passthrough)
1538
1539 if (!device && m_passthrough)
1540 device = OpenAlsaDevice(getenv("ALSA_PASSTHROUGH_DEVICE"), m_passthrough);
1541
1542 if (!device)
1544
1545 if (!device)
1546 device = OpenAlsaDevice(getenv("ALSA_DEVICE"), m_passthrough);
1547
1548 // walkthrough hdmi: devices
1549 if (!device) {
1550 LOGDEBUG2(L_SOUND, "audio: %s: Try hdmi: devices...", __FUNCTION__);
1551 device = FindAlsaDevice("pcm", "hdmi:", m_passthrough);
1552 freeDevice = (device != NULL); // FindAlsaDevice allocates memory
1553 }
1554
1555 // Rockchip mainline kernel
1556 if (!device) {
1557 LOGDEBUG2(L_SOUND, "audio: %s: Try default:CARD=hdmisound devices...", __FUNCTION__);
1558 device = FindAlsaDevice("pcm", "default:CARD=hdmisound", m_passthrough);
1559 freeDevice = (device != NULL); // FindAlsaDevice allocates memory
1560 }
1561
1562 // walkthrough default: devices
1563 if (!device) {
1564 LOGDEBUG2(L_SOUND, "audio: %s: Try default: devices...", __FUNCTION__);
1565 device = FindAlsaDevice("pcm", "default:", m_passthrough);
1566 freeDevice = (device != NULL); // FindAlsaDevice allocates memory
1567 }
1568
1569 // try default device
1570 if (!device) {
1571 LOGDEBUG2(L_SOUND, "audio: %s: Try default device...", __FUNCTION__);
1572 device = OpenAlsaDevice("default", m_passthrough);
1573 }
1574
1575 // use null device
1576 if (!device) {
1577 LOGDEBUG2(L_SOUND, "audio: %s: Try null device...", __FUNCTION__);
1578 device = OpenAlsaDevice("null", m_passthrough);
1579 }
1580
1581 if (!device)
1582 LOGFATAL("audio: %s: could not open any device, abort!", __FUNCTION__);
1583
1584 if (!strcmp(device, "null"))
1585 LOGWARNING("audio: %s: using %sdevice '%s'", __FUNCTION__,
1586 m_passthrough ? "pass-through " : "", device);
1587 else
1588 LOGINFO("audio: using %sdevice '%s'",
1589 m_passthrough ? "pass-through " : "", device);
1590
1591 // Free device string if it was allocated by FindAlsaDevice
1592 if (freeDevice)
1593 free(device);
1594
1595 if ((err = snd_pcm_nonblock(m_pAlsaPCMHandle, 0)) < 0) {
1596 LOGERROR("audio: %s: can't set block mode: %s", __FUNCTION__, snd_strerror(err));
1597 }
1598}
1599
1600/******************************************************************************
1601 * Alsa Mixer
1602 *****************************************************************************/
1603
1608{
1609 const char *device;
1610 const char *channel;
1611 snd_mixer_t *alsaMixer;
1612 snd_mixer_elem_t *alsaMixerElem;
1613 long alsaMixerElemMin;
1614 long alsaMixerElemMax;
1615
1616 if (!(device = m_pMixerDevice)) {
1617 if (!(device = getenv("ALSA_MIXER"))) {
1618 device = "default";
1619 }
1620 }
1621 if (!(channel = m_pMixerChannel)) {
1622 if (!(channel = getenv("ALSA_MIXER_CHANNEL"))) {
1623 channel = "PCM";
1624 }
1625 }
1626 LOGDEBUG2(L_SOUND, "audio: %s: mixer %s - %s open", __FUNCTION__, device, channel);
1627 snd_mixer_open(&alsaMixer, 0);
1628 if (alsaMixer && snd_mixer_attach(alsaMixer, device) >= 0
1629 && snd_mixer_selem_register(alsaMixer, NULL, NULL) >= 0
1630 && snd_mixer_load(alsaMixer) >= 0) {
1631
1632 const char *const alsaMixerElem_name = channel;
1633
1634 alsaMixerElem = snd_mixer_first_elem(alsaMixer);
1635 while (alsaMixerElem) {
1636 const char *name;
1637
1638 name = snd_mixer_selem_get_name(alsaMixerElem);
1639 if (!strcasecmp(name, alsaMixerElem_name)) {
1640 snd_mixer_selem_get_playback_volume_range(alsaMixerElem, &alsaMixerElemMin, &alsaMixerElemMax);
1641 m_alsaRatio = 1000 * (alsaMixerElemMax - alsaMixerElemMin);
1642 LOGDEBUG2(L_SOUND, "audio: %s: %s mixer found %ld - %ld ratio %d", __FUNCTION__, channel, alsaMixerElemMin, alsaMixerElemMax, m_alsaRatio);
1643 break;
1644 }
1645
1646 alsaMixerElem = snd_mixer_elem_next(alsaMixerElem);
1647 }
1648
1649 m_pAlsaMixer = alsaMixer;
1650 m_pAlsaMixerElem = alsaMixerElem;
1651 } else {
1652 LOGERROR("audio: %s: can't open mixer '%s'", __FUNCTION__, device);
1653 }
1654}
1655
1662{
1663 int v;
1665 v = (volume * m_alsaRatio) / (1000 * 1000);
1666 snd_mixer_selem_set_playback_volume(m_pAlsaMixerElem, SND_MIXER_SCHN_FRONT_LEFT, v);
1667 snd_mixer_selem_set_playback_volume(m_pAlsaMixerElem, SND_MIXER_SCHN_FRONT_RIGHT, v);
1668 }
1669}
1670
1683int cSoftHdAudio::AlsaSetup(int channels, int sample_rate, int passthrough)
1684{
1685 snd_pcm_hw_params_t *hwparams;
1686 int err;
1687 unsigned bufferTimeUs = 100'000;
1688
1689 if (Active()) {
1690 Stop();
1692 }
1693
1694 m_downmix = 0;
1695
1696 snd_pcm_hw_params_alloca(&hwparams);
1697 if ((err = snd_pcm_hw_params_any(m_pAlsaPCMHandle, hwparams)) < 0) {
1698 LOGERROR("audio: %s: Read HW config failed! %s", __FUNCTION__, snd_strerror(err));
1699 return -1;
1700 }
1701
1702 if (!snd_pcm_hw_params_test_access(m_pAlsaPCMHandle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
1703 m_alsaUseMmap = true;
1704 }
1705
1706 m_hwSampleRate = sample_rate;
1707 if ((err = snd_pcm_hw_params_set_rate_near(m_pAlsaPCMHandle, hwparams, &m_hwSampleRate, 0)) < 0) {
1708 LOGERROR("audio: %s: SampleRate %d not supported! %s", __FUNCTION__, sample_rate, snd_strerror(err));
1709 return -1;
1710 }
1711 if ((int)m_hwSampleRate != sample_rate) {
1712 LOGDEBUG2(L_SOUND, "audio: %s: sample_rate %d m_hwSampleRate %d", __FUNCTION__, sample_rate, m_hwSampleRate);
1713 }
1714
1715 m_hwNumChannels = channels;
1716 if ((err = snd_pcm_hw_params_set_channels_near(m_pAlsaPCMHandle, hwparams, &m_hwNumChannels)) < 0) {
1717 LOGWARNING("audio: %s: %d channels not supported! %s", __FUNCTION__, m_hwNumChannels, snd_strerror(err));
1718 }
1719 if ((int)m_hwNumChannels != channels && !passthrough) {
1720 m_downmix = 1;
1721 }
1722
1723 if ((err = snd_pcm_hw_params_set_buffer_time_near(m_pAlsaPCMHandle, hwparams, &bufferTimeUs, NULL)) < 0) {
1724 LOGWARNING("audio: %s: bufferTime %d not supported! %s", __FUNCTION__, bufferTimeUs, snd_strerror(err));
1725 }
1726
1727 snd_pcm_uframes_t periodSize = 0;
1728 if ((err = snd_pcm_hw_params_get_period_size_max(hwparams, &periodSize, NULL)) < 0) {
1729 LOGWARNING("audio: %s: getting max periodSize not supported! %s", __FUNCTION__, snd_strerror(err));
1730 }
1731
1732 snd_pcm_uframes_t bufferSize = 0;
1733 if ((err = snd_pcm_hw_params_get_buffer_size_max(hwparams, &bufferSize)) < 0) {
1734 LOGWARNING("audio: %s: getting max bufferSize not supported! %s", __FUNCTION__, snd_strerror(err));
1735 }
1736
1737 m_alsaBufferSizeFrames = MsToFrames(bufferTimeUs / 1000);
1738
1739/* err = snd_pcm_hw_params_test_format(m_pAlsaPCMHandle, hwparams, SND_PCM_FORMAT_S16);
1740 if (err < 0) // err == 0 if is supported
1741 LOGERROR("audio: %s: SND_PCM_FORMAT_S16 not supported! %s", __FUNCTION__,
1742 snd_strerror(err));
1743*/
1744 if ((err = snd_pcm_set_params(m_pAlsaPCMHandle, SND_PCM_FORMAT_S16,
1745 m_alsaUseMmap ? SND_PCM_ACCESS_MMAP_INTERLEAVED :
1746 SND_PCM_ACCESS_RW_INTERLEAVED, m_hwNumChannels, m_hwSampleRate, 1, bufferTimeUs))) {
1747
1748 snd_pcm_state_t state = snd_pcm_state(m_pAlsaPCMHandle);
1749 LOGERROR("audio: %s: set params error: %s\n"
1750 " Channels %d SampleRate %d\n"
1751 " HWChannels %d HWSampleRate %d SampleFormat %s\n"
1752 " mmap: %s\n"
1753 " AlsaBufferTime %dms pcm state: %s\n"
1754 " periodSize %d frames, bufferSize %d frames",
1755 __FUNCTION__,
1756 snd_strerror(err), channels, sample_rate, m_hwNumChannels,
1757 m_hwSampleRate, snd_pcm_format_name(SND_PCM_FORMAT_S16),
1758 m_alsaUseMmap ? "yes" : "no",
1759 bufferTimeUs / 1000, snd_pcm_state_name(state),
1760 periodSize, bufferSize);
1761 return -1;
1762 }
1763
1764 snd_pcm_state_t state = snd_pcm_state(m_pAlsaPCMHandle);
1765 LOGINFO("audio: %s:\n"
1766 " Channels %d SampleRate %d%s\n"
1767 " HWChannels %d HWSampleRate %d SampleFormat %s\n"
1768 " mmap: %s\n"
1769 " AlsaBufferTime %dms, pcm state: %s\n"
1770 " periodSize %d frames, bufferSize %d frames",
1771 __FUNCTION__,
1772 channels, sample_rate, passthrough ? " -> passthrough" : "",
1774 snd_pcm_format_name(SND_PCM_FORMAT_S16),
1775 m_alsaUseMmap ? "yes" : "no",
1776 bufferTimeUs / 1000, snd_pcm_state_name(state),
1777 periodSize, bufferSize);
1778
1779 Start();
1780
1781 return 0;
1782}
1783
1787static void AlsaNoopCallback( __attribute__ ((unused))
1788 const char *file, __attribute__ ((unused))
1789 int line, __attribute__ ((unused))
1790 const char *function, __attribute__ ((unused))
1791 int err, __attribute__ ((unused))
1792 const char *fmt, ...)
1793{
1794}
1795
1800{
1801#ifdef ALSA_DEBUG
1802 (void)AlsaNoopCallback;
1803#else
1804 // disable display of alsa error messages
1805 snd_lib_error_set_handler(AlsaNoopCallback);
1806#endif
1807
1809 AlsaInitMixer();
1810}
1811
1816{
1817 if (m_pAlsaPCMHandle) {
1818 snd_pcm_close(m_pAlsaPCMHandle);
1819 m_pAlsaPCMHandle = NULL;
1820 }
1821 if (m_pAlsaMixer) {
1822 snd_mixer_close(m_pAlsaMixer);
1823 m_pAlsaMixer = NULL;
1824 m_pAlsaMixerElem = NULL;
1825 }
1826}
1827
1838{
1839 if (m_passthrough)
1840 return;
1841
1842 double bufferFillLevelMs = FramesToMsDouble(m_fillLevel.GetBufferFillLevelFramesAvg());
1843 if (m_fillLevel.IsSettled()) {
1844 auto now = std::chrono::steady_clock::now();
1845 std::chrono::duration<double> elapsedSec = now - m_lastPidInvocation;
1846 m_lastPidInvocation = now;
1847
1848 m_pitchPpm = m_pidController.Update(bufferFillLevelMs, elapsedSec.count()) * -1;
1849 } else
1850 m_pidController.SetTargetValue(bufferFillLevelMs);
1851
1852 if (m_packetCounter++ % 1000 == 0) {
1853 LOGDEBUG2(L_SOUND, "audio: %s: buffer fill level: %.1fms (target: %.1fms), clock drift compensating pitch: %.1fppm, PID controller: P=%.2fppm I=%.2fppm D=%.2fppm",
1854 __FUNCTION__,
1855 bufferFillLevelMs,
1857 m_pitchPpm.load(),
1861 }
1862
1863 // buffer fill level low pass filter
1864 int hardwareBufferFillLevelFrames = m_alsaBufferSizeFrames - snd_pcm_avail(m_pAlsaPCMHandle);
1865
1866 if (hardwareBufferFillLevelFrames < 0)
1867 LOGWARNING("audio: %s: snd_pcm_avail() failes: %s", __FUNCTION__, snd_strerror(hardwareBufferFillLevelFrames));
1868 else
1869 m_fillLevel.UpdateAvgBufferFillLevel(hardwareBufferFillLevelFrames);
1870}
1871
Audio and Alsa Interface Header File.
virtual void OnEventReceived(const Event &)=0
void ReceivedFrames(int count)
Definition filllevel.h:29
void WroteFrames(int count)
Definition filllevel.h:30
double GetTargetValue()
double GetPTerm()
double GetDTerm()
void SetTargetValue(double value)
double GetITerm()
int m_alsaBufferSizeFrames
alsa buffer size in frames
Definition audio.h:101
bool m_appendAES
flag ato utomatic append AES
Definition audio.h:125
cSoftHdRingbuffer m_pRingbuffer
sample ring buffer
Definition audio.h:181
int m_pitchAdjustFrameCounter
counter for pitch adjustment frames
Definition audio.h:114
double FramesToMsDouble(int frames)
Definition audio.h:210
int m_compressionMaxFactor
max. compression factor
Definition audio.h:152
const char * m_pPCMDevice
PCM device name.
Definition audio.h:123
int m_volume
current volume (0 .. 1000)
Definition audio.h:158
std::mutex m_pauseMutex
mutex for a safe thread pausing
Definition audio.h:111
AVFilterContext * m_pBuffersinkCtx
Definition audio.h:174
cPidController m_pidController
PID controller for clock drift compensation with tuning values coming from educated guesses.
Definition audio.h:99
AVFilterContext * m_pBuffersrcCtx
Definition audio.h:173
AVFilterGraph * m_pFilterGraph
Definition audio.h:172
int m_passthrough
passthrough mask
Definition audio.h:122
const int m_bytesPerSample
number of bytes per sample
Definition audio.h:106
const char * m_pMixerChannel
mixer channel name
Definition audio.h:167
unsigned int m_hwSampleRate
hardware sample rate in Hz
Definition audio.h:107
int64_t PtsToMs(int64_t pts)
Definition audio.h:206
cBufferFillLevelLowPassFilter m_fillLevel
low pass filter for the buffer fill level
Definition audio.h:98
IEventReceiver * m_pEventReceiver
pointer to event receiver
Definition audio.h:97
int MsToFrames(int milliseconds)
Definition audio.h:208
std::vector< Event > m_eventQueue
event queue for incoming events
Definition audio.h:112
AVRational * m_pTimebase
pointer to AVCodecContext pkts_timebase
Definition audio.h:109
bool m_compression
flag to use compress volume
Definition audio.h:150
bool m_normalize
flag to use volume normalize
Definition audio.h:138
int m_filterChanged
filter has changed
Definition audio.h:170
snd_mixer_elem_t * m_pAlsaMixerElem
alsa mixer element
Definition audio.h:186
int64_t m_inputPts
pts clock (last pts in ringbuffer)
Definition audio.h:118
int m_normalizeFactor
current normalize factor
Definition audio.h:144
cSoftHdConfig * m_pConfig
pointer to config
Definition audio.h:96
std::atomic< double > m_pitchPpm
pitch adjustment in ppm. Positive values are faster
Definition audio.h:113
int m_amplifier
software volume amplify factor
Definition audio.h:156
static constexpr int NORMALIZE_MAX_INDEX
number of normalize average samples
Definition audio.h:93
const char * m_pPassthroughDevice
passthrough device name
Definition audio.h:124
int m_normalizeMaxFactor
max. normalize factor
Definition audio.h:146
bool m_alsaUseMmap
use mmap
Definition audio.h:188
int m_compressionFactor
current compression factor
Definition audio.h:151
int64_t MsToPts(int64_t ptsMs)
Definition audio.h:207
int m_spdifBurstSize
size of the current spdif burst
Definition audio.h:126
const int m_normalizeMinFactor
min. normalize factor
Definition audio.h:145
const int m_normalizeSamples
number of normalize samples
Definition audio.h:139
int m_filterReady
filter is ready
Definition audio.h:171
int m_normalizeReady
index normalize counter
Definition audio.h:143
const char * m_pMixerDevice
mixer device name (not used)
Definition audio.h:166
bool HasInputPts(void)
Definition audio.h:65
uint32_t m_normalizeAverage[NORMALIZE_MAX_INDEX]
average of n last normalize sample blocks
Definition audio.h:141
unsigned int m_hwNumChannels
number of hardware channels
Definition audio.h:108
bool m_initialized
class initialized
Definition audio.h:105
int FramesToMs(int frames)
Definition audio.h:209
int m_stereoDescent
volume descent for stereo
Definition audio.h:157
std::mutex m_mutex
mutex for thread safety
Definition audio.h:110
static constexpr int AV_SYNC_BORDER_MS
absolute max a/v difference in ms which should trigger a resync
Definition audio.h:94
int m_alsaRatio
internal -> mixer ratio * 1000
Definition audio.h:187
int m_packetCounter
packet counter for logging
Definition audio.h:102
snd_pcm_t * m_pAlsaPCMHandle
alsa pcm handle
Definition audio.h:184
int m_downmix
set stereo downmix
Definition audio.h:116
int m_useEqualizer
flag to use equalizer
Definition audio.h:162
float m_equalizerBand[18]
equalizer band
Definition audio.h:163
snd_mixer_t * m_pAlsaMixer
alsa mixer handle
Definition audio.h:185
snd_pcm_sframes_t m_hwBaseline
saves the hw delay (pause bursts) once a real audio frame to correctly do the AV-Sync
Definition audio.h:128
int m_normalizeIndex
index into normalize average table
Definition audio.h:142
std::chrono::steady_clock::time_point m_lastPidInvocation
last time the PID controller was invoked
Definition audio.h:100
int m_normalizeCounter
normalize sample counter
Definition audio.h:140
std::atomic< bool > m_paused
audio is paused
Definition audio.h:119
bool m_firstRealAudioReceived
false, as long as no real audio was sent - used to trigger the baseline set
Definition audio.h:129
std::vector< uint16_t > m_pauseBurst
holds the burst data itself
Definition audio.h:127
bool m_softVolume
flag to use soft volume
Definition audio.h:121
bool ConfigAudioNormalize
config use normalize volume
Definition config.h:48
int ConfigAudioStereoDescent
config reduce stereo loudness
Definition config.h:52
bool ConfigAudioCompression
config use volume compression
Definition config.h:50
int ConfigAudioEqBand[18]
config equalizer filter bands
Definition config.h:55
int ConfigAudioMaxCompression
config max volume compression
Definition config.h:51
int ConfigAudioEq
config equalizer filter
Definition config.h:54
int ConfigAudioMaxNormalize
config max normalize factor
Definition config.h:49
Output Device Implementation.
Audio Decoder Header File.
Plugin Configuration Header File.
State Machine and Event Header File.
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, PipEvent, ScheduleResyncAtPtsMsEvent, ResyncEvent > Event
Definition event.h:80
@ AUDIO
Definition event.h:27
Low-pass Filter for Audio Buffer Fill Level Measurement Header File.
void LazyInit(void)
Initialize audio output module (alsa)
Definition audio.cpp:1075
cSoftHdAudio(cSoftHdDevice *)
Create a new audio context.
Definition audio.cpp:61
char * OpenAlsaDevice(const char *, int)
Opens an alsa device.
Definition audio.cpp:1440
bool SendPause(void)
Write pause to passthrough device.
Definition audio.cpp:1358
char * FindAlsaDevice(const char *, const char *, int)
Find alsa device giving some search hints.
Definition audio.cpp:1492
void ResetHwDelayBaseline(void)
Reset the hw delay baseline.
Definition audio.cpp:1412
virtual void Action(void)
Audio thread loop, started with Start().
Definition audio.cpp:1199
void Filter(AVFrame *, AVCodecContext *)
Send audio frame to filter and enqueue it.
Definition audio.cpp:818
int AlsaSetup(int channels, int sample_rate, int passthrough)
Setup alsa audio for requested format.
Definition audio.cpp:1683
void Reset()
Reset the internal state (integral sum and error history).
int Setup(AVCodecContext *, int, int, int)
Alsa setup wrapper.
Definition audio.cpp:729
void Enqueue(uint16_t *, int, AVFrame *)
Send audio data to ringbuffer.
Definition audio.cpp:677
void SetPassthroughMask(int)
Set audio passthrough mask.
Definition audio.cpp:1056
void SetHwDelayBaseline(void)
Set the hw delay baseline.
Definition audio.cpp:1392
void SetStereoDescent(int)
Set stereo loudness descent.
Definition audio.cpp:1045
int64_t GetHardwareOutputPtsMs(void)
Get the hardware output PTS in milliseconds.
Definition audio.cpp:923
int64_t GetHardwareOutputPtsTimebaseUnits(void)
Get the hardware output PTS in timebase units.
Definition audio.cpp:964
static void ReorderAudioFrame(uint16_t *buf, int size, int channels)
Reorder audio frame.
Definition audio.cpp:95
void EnqueueSpdif(uint16_t *, int, AVFrame *)
Enqueue prepared spdif bursts in audio output queue.
Definition audio.cpp:655
void SetVolume(int)
Set mixer volume (0-1000)
Definition audio.cpp:978
void HandleError(int)
Handle an alsa error.
Definition audio.cpp:1111
void FlushAlsaBuffersInternal(bool)
Flush alsa buffers internally.
Definition audio.cpp:1144
void ProcessEvents(void)
Process queued events and forward them to event receiver.
Definition audio.cpp:1424
bool SendAudio(int)
Write regular audio data from the ringbuffer to the hardware.
Definition audio.cpp:1298
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
Definition audio.cpp:576
size_t UsedBytes(void)
Get used bytes in ring buffer.
int64_t GetOutputPtsMs(void)
Get the output PTS of the ringbuffer.
Definition audio.cpp:902
AVFrame * FilterGetFrame(void)
Get frame from filter sink.
Definition audio.cpp:752
void ClockDriftCompensation(void)
Calculate clock drift compensation.
Definition audio.cpp:1837
void AlsaExit(void)
Cleanup the alsa audio output module.
Definition audio.cpp:1815
void SetCompression(bool, int)
Set volume compression parameters.
Definition audio.cpp:1027
void Stop(void)
Stop the thread.
Definition audio.cpp:1217
void BuildPauseBurst(void)
Build a pause spdif burst with the size of the last recognized normal spdif audio.
Definition audio.cpp:634
void AlsaInit(void)
Initialize the alsa audio output module.
Definition audio.cpp:1799
void Compress(uint16_t *, int)
Compress audio samples.
Definition audio.cpp:233
double Update(double, double)
Calculate the new output value.
void AlsaSetVolume(int)
Set alsa mixer volume (0-1000)
Definition audio.cpp:1661
int64_t GetOutputPtsMsInternal(void)
Definition audio.cpp:909
void Exit(void)
Cleanup audio output module (alsa)
Definition audio.cpp:1090
int GetUsedBytes(void)
Get used bytes in audio ringbuffer.
Definition audio.cpp:884
void SetPaused(bool)
Set audio playback pause state.
Definition audio.cpp:1001
void AlsaInitMixer(void)
Initialize alsa mixer.
Definition audio.cpp:1607
int64_t GetHardwareOutputDelayMs(void)
Get the hardware delay in milliseconds.
Definition audio.cpp:945
static void AlsaNoopCallback(__attribute__((unused)) const char *file, __attribute__((unused)) int line, __attribute__((unused)) const char *function, __attribute__((unused)) int err, __attribute__((unused)) const char *fmt,...)
Empty log callback.
Definition audio.cpp:1787
void Normalize(uint16_t *, int)
Normalize audio samples.
Definition audio.cpp:152
size_t ReadAdvance(size_t)
Advance read pointer in ring buffer.
void SetEq(int[18], int)
Set equalizer bands.
Definition audio.cpp:319
void DropAlsaBuffers(void)
Drop alsa buffers.
Definition audio.cpp:1134
bool CyclicCall(void)
Cyclic audio playback call.
Definition audio.cpp:1237
size_t GetReadPointer(const void **)
Get read pointer and used bytes at this position of ring buffer.
void AlsaInitPCMDevice(void)
Search for an alsa pcm device and open it.
Definition audio.cpp:1528
size_t Write(const void *, size_t)
Write to a ring buffer.
void EnqueueFrame(AVFrame *)
Place samples in audio output queue.
Definition audio.cpp:608
void SoftAmplify(int16_t *, int)
Amplify the samples in software.
Definition audio.cpp:290
void FlushAlsaBuffers(void)
Flush alsa buffers.
Definition audio.cpp:1126
void SetNormalize(bool, int)
Set normalize volume parameters.
Definition audio.cpp:1015
int CheckForFilterReady(AVCodecContext *)
Check if the filter has changed and is ready, init the filter if needed.
Definition audio.cpp:785
int InitFilter(AVCodecContext *)
Init audio filters.
Definition audio.cpp:401
void Reset(void)
Reset ring buffer pointers.
void FlushBuffers(void)
Flush audio buffers.
Definition audio.cpp:861
@ IEC61937_NULL
no data
Definition codec_audio.h:44
@ IEC61937_PREAMBLE1
Definition codec_audio.h:58
@ IEC61937_PREAMBLE2
Definition codec_audio.h:59
__attribute__((weak)) union gbm_bo_handle gbm_bo_get_handle_for_plane(struct gbm_bo *bo
#define LOGDEBUG2
log to LOG_DEBUG and add a prefix
Definition logger.h:42
void ResetFramesCounters()
Resets the received and written frames counters.
Definition filllevel.cpp:33
void UpdateAvgBufferFillLevel(int)
Updates the buffer fill level average.
Definition filllevel.cpp:53
#define LOGDEBUG
log to LOG_DEBUG
Definition logger.h:40
#define LOGERROR
log to LOG_ERR
Definition logger.h:34
#define AV_NOPTS_VALUE
Definition misc.h:74
#define LOGWARNING
log to LOG_WARN
Definition logger.h:36
#define LOGINFO
log to LOG_INFO
Definition logger.h:38
#define LOGFATAL
log to LOG_ERR and abort
Definition logger.h:32
void Reset()
Resets the filter state.
Definition filllevel.cpp:23
static const char * Timestamp2String(int64_t ts, uint8_t divisor)
Nice time-stamp string.
Definition misc.h:127
@ L_AV_SYNC
audio/video sync logs
Definition logger.h:52
@ L_SOUND
sound logs
Definition logger.h:53
Logger Header File.
Misc Functions.
PID (proportional, integral, derivative) Controller Header File.
Audio Ringbuffer Header File.
Output Device Header File.