vdr-plugin-softhddevice-drm-gles 1.5.9-20e15de
audio.cpp
Go to the documentation of this file.
1
26#include <chrono>
27#include <cmath>
28#include <cstdint>
29#include <mutex>
30#include <vector>
31
32#include <alsa/asoundlib.h>
33
34extern "C" {
35#include <libavcodec/avcodec.h>
36#include <libavfilter/avfilter.h>
37#include <libavfilter/buffersink.h>
38#include <libavfilter/buffersrc.h>
39#include <libavutil/channel_layout.h>
40#include <libavutil/opt.h>
41}
42
43#include "audio.h"
44#include "config.h"
45#include "event.h"
46#include "filllevel.h"
47#include "logger.h"
48#include "misc.h"
49#include "pidcontroller.h"
50#include "ringbuffer.h"
51#include "softhddevice.h"
52#include "threads.h"
53
54/******************************************************************************
55 * cSoftHdAudio class
56 *****************************************************************************/
57
62 : m_pDevice(device),
63 m_pConfig(m_pDevice->Config()),
64 m_pEventReceiver(device),
65 m_downmix(m_pConfig->ConfigAudioDownmix),
66 m_softVolume(m_pConfig->ConfigAudioSoftvol),
67 m_passthrough(m_pConfig->ConfigAudioPassthroughState ? m_pConfig->ConfigAudioPassthroughMask : 0),
68 m_pPCMDevice(m_pConfig->ConfigAudioPCMDevice),
69 m_pPassthroughDevice(m_pConfig->ConfigAudioPassthroughDevice),
70 m_appendAES(m_pConfig->ConfigAudioAutoAES),
71 m_pMixerChannel(m_pConfig->ConfigAudioMixerChannel)
72{
77
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
395int cSoftHdAudio::InitFilter(AVCodecContext *audioCtx)
396{
397 const AVFilter *abuffer;
398 AVFilterContext *pfilterCtx[3];
399 const AVFilter *eq;
400 const AVFilter *aformat;
401 const AVFilter *abuffersink;
402 char channelLayout[64];
403 char optionsStr[1024];
404 int err, i, numFilter = 0;
405
406 // Before filter init set HW parameter.
407 if (audioCtx->sample_rate != (int)m_hwSampleRate ||
408 (audioCtx->ch_layout.nb_channels != (int)m_hwNumChannels &&
409 !(m_downmix && m_hwNumChannels == 2))) {
410
411 err = AlsaSetup(audioCtx->ch_layout.nb_channels, audioCtx->sample_rate, 0);
412 if (err)
413 return err;
414 }
415
416 m_pTimebase = &audioCtx->pkt_timebase;
417
418#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7,16,100)
419 avfilter_register_all();
420#endif
421
422 if (!(m_pFilterGraph = avfilter_graph_alloc())) {
423 LOGERROR("audio: %s: Unable to create filter graph.", __FUNCTION__);
424 return -1;
425 }
426
427 // input buffer
428 if (!(abuffer = avfilter_get_by_name("abuffer"))) {
429 LOGWARNING("audio: %s: Could not find the abuffer filter.", __FUNCTION__);
430 avfilter_graph_free(&m_pFilterGraph);
431 return -1;
432 }
433 if (!(m_pBuffersrcCtx = avfilter_graph_alloc_filter(m_pFilterGraph, abuffer, "src"))) {
434 LOGWARNING("audio: %s: Could not allocate the m_pBuffersrcCtx instance.", __FUNCTION__);
435 avfilter_graph_free(&m_pFilterGraph);
436 return -1;
437 }
438
439 av_channel_layout_describe(&audioCtx->ch_layout, channelLayout, sizeof(channelLayout));
440
441 LOGDEBUG2(L_SOUND, "audio: %s: IN channelLayout %s sample_fmt %s sample_rate %d channels %d", __FUNCTION__,
442 channelLayout, av_get_sample_fmt_name(audioCtx->sample_fmt), audioCtx->sample_rate, audioCtx->ch_layout.nb_channels);
443
444 av_opt_set (m_pBuffersrcCtx, "channel_layout", channelLayout, AV_OPT_SEARCH_CHILDREN);
445 av_opt_set (m_pBuffersrcCtx, "sample_fmt", av_get_sample_fmt_name(audioCtx->sample_fmt), AV_OPT_SEARCH_CHILDREN);
446 av_opt_set_q (m_pBuffersrcCtx, "time_base", (AVRational){ 1, audioCtx->sample_rate }, AV_OPT_SEARCH_CHILDREN);
447 av_opt_set_int(m_pBuffersrcCtx, "sample_rate", audioCtx->sample_rate, AV_OPT_SEARCH_CHILDREN);
448// av_opt_set_int(m_pBuffersrcCtx, "channel_counts", audioCtx->channels, AV_OPT_SEARCH_CHILDREN);
449
450 // initialize the filter with NULL options, set all options above.
451 if (avfilter_init_str(m_pBuffersrcCtx, NULL) < 0) {
452 LOGWARNING("audio: %s: Could not initialize the abuffer filter.", __FUNCTION__);
453 avfilter_graph_free(&m_pFilterGraph);
454 return -1;
455 }
456
457 // superequalizer
458 if (m_useEqualizer) {
459 if (!(eq = avfilter_get_by_name("superequalizer"))) {
460 LOGWARNING("audio: %s: Could not find the superequalizer filter.", __FUNCTION__);
461 avfilter_graph_free(&m_pFilterGraph);
462 return -1;
463 }
464 if (!(pfilterCtx[numFilter] = avfilter_graph_alloc_filter(m_pFilterGraph, eq, "superequalizer"))) {
465 LOGWARNING("audio: %s: Could not allocate the superequalizer instance.", __FUNCTION__);
466 avfilter_graph_free(&m_pFilterGraph);
467 return -1;
468 }
469 snprintf(optionsStr, sizeof(optionsStr),"1b=%.2f:2b=%.2f:3b=%.2f:4b=%.2f:5b=%.2f"
470 ":6b=%.2f:7b=%.2f:8b=%.2f:9b=%.2f:10b=%.2f:11b=%.2f:12b=%.2f:13b=%.2f:14b=%.2f:"
471 "15b=%.2f:16b=%.2f:17b=%.2f:18b=%.2f ", m_equalizerBand[0], m_equalizerBand[1],
476 if (avfilter_init_str(pfilterCtx[numFilter], optionsStr) < 0) {
477 LOGWARNING("audio: %s: Could not initialize the superequalizer filter.", __FUNCTION__);
478 avfilter_graph_free(&m_pFilterGraph);
479 return -1;
480 }
481 numFilter++;
482 }
483
484 // aformat
485 AVChannelLayout channel_layout;
486 av_channel_layout_default(&channel_layout, m_hwNumChannels);
487 av_channel_layout_describe(&channel_layout, channelLayout, sizeof(channelLayout));
488 av_channel_layout_uninit(&channel_layout);
489 // should use IN layout if more then 2 ch!?
490 LOGDEBUG2(L_SOUND, "audio: %s: OUT m_downmix %d m_hwNumChannels %d m_hwSampleRate %d channelLayout %s bytes_per_sample %d",
491 __FUNCTION__, m_downmix, m_hwNumChannels, m_hwSampleRate, channelLayout, av_get_bytes_per_sample(AV_SAMPLE_FMT_S16));
492 if (!(aformat = avfilter_get_by_name("aformat"))) {
493 LOGWARNING("audio: %s: Could not find the aformat filter.", __FUNCTION__);
494 avfilter_graph_free(&m_pFilterGraph);
495 return -1;
496 }
497 if (!(pfilterCtx[numFilter] = avfilter_graph_alloc_filter(m_pFilterGraph, aformat, "aformat"))) {
498 LOGWARNING("audio: %s: Could not allocate the aformat instance.", __FUNCTION__);
499 avfilter_graph_free(&m_pFilterGraph);
500 return -1;
501 }
502 snprintf(optionsStr, sizeof(optionsStr),
503 "sample_fmts=%s:sample_rates=%d:channel_layouts=%s",
504 av_get_sample_fmt_name(AV_SAMPLE_FMT_S16), m_hwSampleRate, channelLayout);
505 if (avfilter_init_str(pfilterCtx[numFilter], optionsStr) < 0) {
506 LOGWARNING("audio: %s: Could not initialize the aformat filter.", __FUNCTION__);
507 avfilter_graph_free(&m_pFilterGraph);
508 return -1;
509 }
510 numFilter++;
511
512 // abuffersink
513 if (!(abuffersink = avfilter_get_by_name("abuffersink"))) {
514 LOGWARNING("audio: %s: Could not find the abuffersink filter.", __FUNCTION__);
515 avfilter_graph_free(&m_pFilterGraph);
516 return -1;
517 }
518 if (!(pfilterCtx[numFilter] = avfilter_graph_alloc_filter(m_pFilterGraph, abuffersink, "sink"))) {
519 LOGWARNING("audio: %s: Could not allocate the abuffersink instance.", __FUNCTION__);
520 avfilter_graph_free(&m_pFilterGraph);
521 return -1;
522 }
523 if (avfilter_init_str(pfilterCtx[numFilter], NULL) < 0) {
524 LOGWARNING("audio: %s: Could not initialize the abuffersink instance.", __FUNCTION__);
525 avfilter_graph_free(&m_pFilterGraph);
526 return -1;
527 }
528 numFilter++;
529
530 // Connect the filters
531 for (i = 0; i < numFilter; i++) {
532 if (i == 0) {
533 err = avfilter_link(m_pBuffersrcCtx, 0, pfilterCtx[i], 0);
534 } else {
535 err = avfilter_link(pfilterCtx[i - 1], 0, pfilterCtx[i], 0);
536 }
537 }
538 if (err < 0) {
539 LOGWARNING("audio: %s: Error connecting audio filters", __FUNCTION__);
540 avfilter_graph_free(&m_pFilterGraph);
541 return -1;
542 }
543
544 // Configure the graph.
545 if (avfilter_graph_config(m_pFilterGraph, NULL) < 0) {
546 LOGWARNING("audio: %s: Error configuring the audio filter graph", __FUNCTION__);
547 avfilter_graph_free(&m_pFilterGraph);
548 return -1;
549 }
550
551 m_pBuffersinkCtx = pfilterCtx[numFilter - 1];
552 m_filterChanged = 0;
553 m_filterReady = 1;
554
555 return 0;
556}
557
558/******************************************************************************
559 * Audio stream handling
560 *****************************************************************************/
561
571{
572 std::lock_guard<std::mutex> lock(m_mutex);
573
574 if (!HasPts())
575 return;
576
577 int64_t dropMs = std::max((int64_t)0, ptsMs - GetOutputPtsMsInternal());
578 int dropFrames = MsToFrames(dropMs);
579 int dropBytes = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, dropFrames);
580
581 dropBytes = std::min(dropBytes, (int)m_pRingbuffer.UsedBytes());
582
583 if (dropBytes > 0) {
584 LOGDEBUG2(L_AV_SYNC, "audio: %s: dropping %dms audio samples to start in sync with the video (output PTS %s -> %s)",
585 __FUNCTION__,
586 dropMs,
588 Timestamp2String(ptsMs, 1));
589
592 m_fillLevel.WroteFrames(dropFrames);
593 m_pRingbuffer.ReadAdvance(dropBytes);
594 }
595}
596
602void cSoftHdAudio::EnqueueFrame(AVFrame *frame)
603{
604 if (!frame)
605 return;
606
607 uint16_t *buffer;
608
609 int byteCount = frame->nb_samples * frame->ch_layout.nb_channels * m_bytesPerSample;
610 buffer = (uint16_t *)frame->data[0];
611
612 if (m_compression) { // in place operation
613 Compress(buffer, byteCount);
614 }
615 if (m_normalize) { // in place operation
616 Normalize(buffer, byteCount);
617 }
618 ReorderAudioFrame(buffer, byteCount, frame->ch_layout.nb_channels);
619
620 Enqueue((uint16_t *)buffer, byteCount, frame);
621
622 av_frame_free(&frame);
623}
624
632void cSoftHdAudio::Enqueue(uint16_t *buffer, int count, AVFrame *frame)
633{
634 std::lock_guard<std::mutex> lock(m_mutex);
635
636 // pitch adjustment
637 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
638 int oneFrameBytes = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, 1);
639
640 if (m_pitchPpm < 0 && m_pRingbuffer.Write((const uint16_t *)buffer, oneFrameBytes)) // insert additional frame
642 else if (m_pitchPpm > 0) // drop frame
643 count = std::max(0, count - oneFrameBytes);
644
645 m_pitchAdjustFrameCounter = std::round(1'000'000.0 / std::abs(m_pitchPpm));
646 }
647
648 m_pitchAdjustFrameCounter = std::max(0, m_pitchAdjustFrameCounter - (int)snd_pcm_bytes_to_frames(m_pAlsaPCMHandle, count));
649
650 // write to ringbuffer
651 int bytesWritten = m_pRingbuffer.Write((const uint16_t *)buffer, count);
652 if (bytesWritten != count)
653 LOGERROR("audio: %s: can't place %d samples in ring buffer", __FUNCTION__, count);
654
655 m_fillLevel.ReceivedFrames(snd_pcm_bytes_to_frames(m_pAlsaPCMHandle, bytesWritten));
656
657 if (frame->pts != AV_NOPTS_VALUE) {
658 // discontinuity check, force a resync if the new pts differs more than AV_SYNC_BORDER_MS to the last
659 if (m_inputPts != AV_NOPTS_VALUE && std::abs(PtsToMs(m_inputPts) - PtsToMs(frame->pts)) > AV_SYNC_BORDER_MS) {
660 LOGDEBUG2(L_AV_SYNC, "audio: %s: discontinuity detected in audio PTS %s -> %s%s", __FUNCTION__,
662 PtsToMs(m_inputPts) > PtsToMs(frame->pts) ? " (PTS wrapped)" : "");
663 m_eventQueue.push_back(ScheduleResyncAtPtsMsEvent{PtsToMs(frame->pts)});
664 }
665
666 m_inputPts = frame->pts;
667 }
668}
669
683int cSoftHdAudio::Setup(AVCodecContext *ctx, int samplerate, int channels, int passthrough)
684{
685 int err = 0;
686
687 if (samplerate != (int)m_hwSampleRate ||
688 (channels != (int)m_hwNumChannels && !(m_downmix && m_hwNumChannels == 2))) {
689
690 err = AlsaSetup(channels, samplerate, passthrough);
691 if (err) {
692 LOGERROR("audio: %s: failed!", __FUNCTION__);
693 return err;
694 }
695 }
696 m_pTimebase = &ctx->pkt_timebase;
697
698 return 0;
699}
700
707{
708 AVFrame *outframe = nullptr;
709 outframe = av_frame_alloc();
710 if (!outframe) {
711 LOGERROR("audio: %s: Error allocating frame", __FUNCTION__);
712 return NULL;
713 }
714
715 int err = av_buffersink_get_frame(m_pBuffersinkCtx, outframe);
716
717 if (err == AVERROR(EAGAIN)) {
718// LOGERROR("audio: %s: Error filtering AVERROR(EAGAIN)", __FUNCTION__);
719 av_frame_free(&outframe);
720 } else if (err == AVERROR_EOF) {
721 LOGERROR("audio: %s: Error filtering AVERROR_EOF", __FUNCTION__);
722 av_frame_free(&outframe);
723 } else if (err < 0) {
724 LOGERROR("audio: %s: Error filtering the data", __FUNCTION__);
725 av_frame_free(&outframe);
726 }
727
728 return outframe;
729}
730
739int cSoftHdAudio::CheckForFilterReady(AVCodecContext *ctx)
740{
742// 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",
743// __FUNCTION__, m_filterReady,
744// m_pFilterGraph->sink_links_count, m_pFilterGraph->sink_links[0]->channels,
745// m_pFilterGraph->filters[m_pFilterGraph->nb_filters - 1]->nb_outputs,
746// 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,
747// m_filterChanged);
748 avfilter_graph_free(&m_pFilterGraph);
749 m_filterReady = 0;
750 LOGDEBUG2(L_SOUND, "audio: %s: Free the filter graph.", __FUNCTION__);
751 }
752
753 if (!m_filterReady) {
754 if (InitFilter(ctx)) {
755 LOGDEBUG2(L_SOUND, "audio: %s: AudioFilterReady failed!", __FUNCTION__);
756 return 1;
757 }
758 }
759
760 return 0;
761}
762
772void cSoftHdAudio::Filter(AVFrame *inframe, AVCodecContext *ctx)
773{
774 AVFrame *outframe = NULL;
775 int err = -1;
776 int err_count = 0;
777
778 if (inframe) {
779 while (err < 0) {
780 if (CheckForFilterReady(ctx)) {
781 av_frame_unref(inframe);
782 return;
783 }
784
785 err = av_buffersrc_add_frame(m_pBuffersrcCtx, inframe);
786 if (err < 0) {
787 if (err_count) {
788 char errbuf[128];
789 av_strerror(err, errbuf, sizeof(errbuf));
790 LOGERROR("audio: %s: Error submitting the frame to the filter fmt %s channels %d %s", __FUNCTION__,
791 av_get_sample_fmt_name(ctx->sample_fmt), ctx->ch_layout.nb_channels, errbuf);
792 av_frame_unref(inframe);
793 return;
794 } else {
795 m_filterChanged = 1;
796 err_count++;
797 LOGDEBUG2(L_SOUND, "audio: %s: m_filterChanged %d err_count %d", __FUNCTION__, m_filterChanged, err_count);
798 }
799 }
800 }
801 }
802
803// if (!inframe)
804// LOGDEBUG2(L_SOUND, "audio: %s: NO inframe!", __FUNCTION__);
805
806 outframe = FilterGetFrame();
807 EnqueueFrame(outframe);
808}
809
817{
818 std::lock_guard<std::mutex> lock(m_mutex);
819
820 LOGDEBUG2(L_SOUND, "audio: %s", __FUNCTION__);
821
822 if (!m_initialized)
823 return;
824
827
833 m_filterChanged = 1;
834}
835
840{
841 std::lock_guard<std::mutex> lock(m_mutex);
842
843 return m_pRingbuffer.FreeBytes();
844}
845
850{
851 // FIXME: not correct, if multiple buffer are in use
852 return m_pRingbuffer.UsedBytes();
853}
854
868{
869 std::lock_guard<std::mutex> lock(m_mutex);
870
871 return GetOutputPtsMsInternal();
872}
873
875{
876 return PtsToMs(m_inputPts) - FramesToMs(snd_pcm_bytes_to_frames(m_pAlsaPCMHandle, m_pRingbuffer.UsedBytes()));
877}
878
889{
890 std::lock_guard<std::mutex> lock(m_mutex);
891
893 return AV_NOPTS_VALUE;
894
895 snd_pcm_sframes_t delayFrames;
896 if (snd_pcm_delay(m_pAlsaPCMHandle, &delayFrames) < 0)
897 delayFrames = 0L;
898
899 if (delayFrames < 0) {
900 LOGDEBUG2(L_SOUND, "audio: %s: delay < 0", __FUNCTION__);
901 delayFrames = 0L;
902 }
903
904 return GetOutputPtsMsInternal() - FramesToMs(delayFrames);
905}
906
913 int64_t ptsMs = GetHardwareOutputPtsMs();
914 if (ptsMs == AV_NOPTS_VALUE)
915 return AV_NOPTS_VALUE;
916
917 return MsToPts(ptsMs);
918}
919
926{
927 m_volume = volume;
928 // reduce loudness for stereo output
930 volume -= m_stereoDescent;
931 if (volume < 0) {
932 volume = 0;
933 } else if (volume > 1000) {
934 volume = 1000;
935 }
936 }
937 m_amplifier = volume;
938 if (!m_softVolume) {
939 AlsaSetVolume(volume);
940 }
941}
942
949{
950 LOGDEBUG2(L_SOUND, "audio: %s: %d", __FUNCTION__, pause);
951
952 std::lock_guard<std::mutex> lock(m_pauseMutex);
953
954 m_paused = pause;
955}
956
963void cSoftHdAudio::SetNormalize(bool enable, int maxfac)
964{
965 m_normalize = enable;
966 m_normalizeMaxFactor = maxfac;
967}
968
975void cSoftHdAudio::SetCompression(bool enable, int maxfac)
976{
977 m_compression = enable;
978
979 m_compressionMaxFactor = maxfac;
980 if (!m_compressionFactor) {
981 m_compressionFactor = 1000;
982 }
985 }
986}
987
994{
995 m_stereoDescent = delta;
996 SetVolume(m_volume); // update channel delta
997}
998
1005{
1006 m_passthrough = mask;
1007
1008 // Reset m_pitchPpm if we change the passthrough handling, otherwise
1009 // we may continue to adjust the pitch in Enqueue() when in passthrough
1010 if (m_passthrough)
1011 m_pitchPpm = 0;
1012}
1013
1019{
1020 if (!m_initialized) {
1021 AlsaInit();
1022 m_initialized = true;
1023 }
1024}
1025
1030{
1031 LOGDEBUG2(L_SOUND, "audio: %s", __FUNCTION__);
1032
1033 if (m_initialized) {
1034 if (m_pAudioThread->Active())
1036 delete m_pAudioThread;
1037
1038 avfilter_graph_free(&m_pFilterGraph);
1039
1040 AlsaExit();
1041 }
1042 m_initialized = false;
1043}
1044
1045/******************************************************************************
1046 * A L S A
1047 *****************************************************************************/
1048
1053{
1054 if (snd_pcm_state(m_pAlsaPCMHandle) == SND_PCM_STATE_XRUN && m_passthrough == 0)
1056
1057 if (snd_pcm_recover(m_pAlsaPCMHandle, error, 0) < 0)
1058 LOGERROR("audio: %s: Cannot recover: %s", __FUNCTION__, snd_strerror(error));
1059
1060 snd_pcm_prepare(m_pAlsaPCMHandle);
1061}
1062
1067{
1068 int err;
1069 snd_pcm_state_t state;
1070
1071 LOGDEBUG2(L_SOUND, "audio: %s", __FUNCTION__);
1072
1073 state = snd_pcm_state(m_pAlsaPCMHandle);
1074 if (state != SND_PCM_STATE_OPEN) {
1075 if ((err = snd_pcm_drop(m_pAlsaPCMHandle)) < 0)
1076 LOGERROR("audio: %s: snd_pcm_drop(): %s", __FUNCTION__, snd_strerror(err));
1077 // alsa crash, when in open state here ?
1078 if ((err = snd_pcm_prepare(m_pAlsaPCMHandle)) < 0)
1079 LOGERROR("audio: %s: snd_pcm_prepare(): %s", __FUNCTION__, snd_strerror(err));
1080 state = snd_pcm_state(m_pAlsaPCMHandle);
1081 LOGDEBUG2(L_SOUND, "audio: %s: pcm state %s", __FUNCTION__, snd_pcm_state_name(state));
1082 }
1083
1084 m_compressionFactor = 2000;
1087
1089 m_normalizeReady = 0;
1090
1091 for (int i = 0; i < NORMALIZE_MAX_INDEX; ++i)
1092 m_normalizeAverage[i] = 0U;
1093
1094 m_normalizeFactor = 1000;
1095}
1096
1097/******************************************************************************
1098 * Thread playback
1099 *****************************************************************************/
1100
1110{
1111 std::lock_guard<std::mutex> lock1(m_pauseMutex);
1112
1113 if (m_paused)
1114 return false;
1115
1116 // wait for space in kernel buffers
1117 int ret = snd_pcm_wait(m_pAlsaPCMHandle, 150);
1118 if (ret < 0) {
1119 HandleError(ret);
1120 return false;
1121 } else if (ret == 0) {
1122 LOGERROR("audio: %s: snd_pcm_wait() timeout", __FUNCTION__);
1123 return false;
1124 }
1125
1126 std::lock_guard<std::mutex> lock2(m_mutex);
1127
1128 // query available space in alsa buffer
1129 int freeAlsaBufferFrameCount = snd_pcm_avail(m_pAlsaPCMHandle);
1130 if (freeAlsaBufferFrameCount < 0) {
1131 if (freeAlsaBufferFrameCount == -EAGAIN)
1132 return false;
1133
1134 HandleError(freeAlsaBufferFrameCount);
1135 return false;
1136 }
1137
1138 // calculcate amount of data to write
1139 const void *data;
1140 ssize_t inputBufferFillLevelBytes = m_pRingbuffer.GetReadPointer(&data);
1141
1142 int bytesToWrite = std::min(snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, freeAlsaBufferFrameCount), inputBufferFillLevelBytes);
1143
1144 if (bytesToWrite == 0)
1145 return false;
1146
1147 // muting pass-through AC-3, can produce disturbance
1148 if (m_volume == 0 || (m_softVolume && !m_passthrough)) {
1149 // FIXME: quick&dirty cast
1150 SoftAmplify((int16_t *) data, bytesToWrite);
1151 // FIXME: if not all are written, we double amplify them
1152 }
1153
1154 int framesToWrite = snd_pcm_bytes_to_frames(m_pAlsaPCMHandle, bytesToWrite);
1155
1156 int framesWritten;
1157 if (m_alsaUseMmap)
1158 framesWritten = snd_pcm_mmap_writei(m_pAlsaPCMHandle, data, framesToWrite);
1159 else
1160 framesWritten = snd_pcm_writei(m_pAlsaPCMHandle, data, framesToWrite);
1161
1162 m_fillLevel.WroteFrames(framesWritten);
1163 m_pRingbuffer.ReadAdvance(snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, framesWritten));
1164
1165 if (framesWritten != framesToWrite) {
1166 if (framesWritten < 0) {
1167 if (framesWritten == -EAGAIN)
1168 return false;
1169
1170 LOGWARNING("audio: %s: writei failed: %s", __FUNCTION__, snd_strerror(framesWritten));
1171
1172 if (snd_pcm_recover(m_pAlsaPCMHandle, framesWritten, 0) < 0)
1173 LOGERROR("audio: %s: failed to recover from writei: %s", __FUNCTION__, snd_strerror(framesWritten));
1174
1175 return false;
1176 } else {
1177 LOGWARNING("audio: %s: not all frames written", __FUNCTION__);
1178 return false;
1179 }
1180 }
1181
1182 return true;
1183}
1184
1193char *cSoftHdAudio::OpenAlsaDevice(const char *device, int passthrough)
1194{
1195 int err;
1196 char tmp[80];
1197
1198 if (!device)
1199 return NULL;
1200
1201 LOGDEBUG2(L_SOUND, "audio: %s: try opening %sdevice '%s'", __FUNCTION__, passthrough ? "pass-through " : "", device);
1202
1203 if (passthrough && m_appendAES) {
1204 if (!(strchr(device, ':'))) {
1205 sprintf(tmp, "%s:AES0=%d,AES1=%d,AES2=0,AES3=%d",
1206 device,
1207 IEC958_AES0_NONAUDIO | IEC958_AES0_PRO_EMPHASIS_NONE,
1208 IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
1209 IEC958_AES3_CON_FS_48000);
1210 } else {
1211 sprintf(tmp, "%s,AES0=%d,AES1=%d,AES2=0,AES3=%d",
1212 device,
1213 IEC958_AES0_NONAUDIO | IEC958_AES0_PRO_EMPHASIS_NONE,
1214 IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
1215 IEC958_AES3_CON_FS_48000);
1216 }
1217 LOGDEBUG2(L_SOUND, "audio: %s: auto append AES: %s -> %s", __FUNCTION__, device, tmp);
1218 } else {
1219 sprintf(tmp, "%s", device);
1220 }
1221
1222 // open none blocking; if device is already used, we don't want wait
1223 if ((err = snd_pcm_open(&m_pAlsaPCMHandle, tmp, SND_PCM_STREAM_PLAYBACK,
1224 SND_PCM_NONBLOCK)) < 0) {
1225
1226 LOGWARNING("audio: %s: could not open device '%s' error: %s", __FUNCTION__, device, snd_strerror(err));
1227 return NULL;
1228 }
1229
1230 LOGDEBUG2(L_SOUND, "audio: %s: opened %sdevice '%s'", __FUNCTION__, passthrough ? "pass-through " : "", device);
1231
1232 return (char *)device;
1233}
1234
1245char *cSoftHdAudio::FindAlsaDevice(const char *devname, const char *hint, int passthrough)
1246{
1247 char **hints;
1248 int err;
1249 char **n;
1250 char *name;
1251
1252 err = snd_device_name_hint(-1, devname, (void ***)&hints);
1253 if (err != 0) {
1254 LOGWARNING("audio: %s: Cannot get device names for %s!", __FUNCTION__, hint);
1255 return NULL;
1256 }
1257
1258 n = hints;
1259 while (*n != NULL) {
1260 name = snd_device_name_get_hint(*n, "NAME");
1261
1262 if (name && strstr(name, hint)) {
1263 if (OpenAlsaDevice(name, passthrough)) {
1264 snd_device_name_free_hint((void **)hints);
1265 return name;
1266 }
1267 }
1268
1269 if (name)
1270 free(name);
1271 n++;
1272 }
1273
1274 snd_device_name_free_hint((void **)hints);
1275 return NULL;
1276}
1277
1282{
1283 char *device = NULL;
1284 bool freeDevice = false; // track if device needs to be freed
1285 int err;
1286 LOGDEBUG2(L_SOUND, "audio: %s: passthrough %d", __FUNCTION__, m_passthrough);
1287
1288 // try user set device
1289 if (m_passthrough)
1291
1292 if (!device && m_passthrough)
1293 device = OpenAlsaDevice(getenv("ALSA_PASSTHROUGH_DEVICE"), m_passthrough);
1294
1295 if (!device)
1297
1298 if (!device)
1299 device = OpenAlsaDevice(getenv("ALSA_DEVICE"), m_passthrough);
1300
1301 // walkthrough hdmi: devices
1302 if (!device) {
1303 LOGDEBUG2(L_SOUND, "audio: %s: Try hdmi: devices...", __FUNCTION__);
1304 device = FindAlsaDevice("pcm", "hdmi:", m_passthrough);
1305 freeDevice = (device != NULL); // FindAlsaDevice allocates memory
1306 }
1307
1308 // Rockchip mainline kernel
1309 if (!device) {
1310 LOGDEBUG2(L_SOUND, "audio: %s: Try default:CARD=hdmisound devices...", __FUNCTION__);
1311 device = FindAlsaDevice("pcm", "default:CARD=hdmisound", m_passthrough);
1312 freeDevice = (device != NULL); // FindAlsaDevice allocates memory
1313 }
1314
1315 // walkthrough default: devices
1316 if (!device) {
1317 LOGDEBUG2(L_SOUND, "audio: %s: Try default: devices...", __FUNCTION__);
1318 device = FindAlsaDevice("pcm", "default:", m_passthrough);
1319 freeDevice = (device != NULL); // FindAlsaDevice allocates memory
1320 }
1321
1322 // try default device
1323 if (!device) {
1324 LOGDEBUG2(L_SOUND, "audio: %s: Try default device...", __FUNCTION__);
1325 device = OpenAlsaDevice("default", m_passthrough);
1326 }
1327
1328 // use null device
1329 if (!device) {
1330 LOGDEBUG2(L_SOUND, "audio: %s: Try null device...", __FUNCTION__);
1331 device = OpenAlsaDevice("null", m_passthrough);
1332 }
1333
1334 if (!device)
1335 LOGFATAL("audio: %s: could not open any device, abort!", __FUNCTION__);
1336
1337 if (!strcmp(device, "null"))
1338 LOGWARNING("audio: %s: using %sdevice '%s'", __FUNCTION__,
1339 m_passthrough ? "pass-through " : "", device);
1340 else
1341 LOGINFO("audio: using %sdevice '%s'",
1342 m_passthrough ? "pass-through " : "", device);
1343
1344 // Free device string if it was allocated by FindAlsaDevice
1345 if (freeDevice)
1346 free(device);
1347
1348 if ((err = snd_pcm_nonblock(m_pAlsaPCMHandle, 0)) < 0) {
1349 LOGERROR("audio: %s: can't set block mode: %s", __FUNCTION__, snd_strerror(err));
1350 }
1351}
1352
1353/******************************************************************************
1354 * Alsa Mixer
1355 *****************************************************************************/
1356
1361{
1362 const char *device;
1363 const char *channel;
1364 snd_mixer_t *alsaMixer;
1365 snd_mixer_elem_t *alsaMixerElem;
1366 long alsaMixerElemMin;
1367 long alsaMixerElemMax;
1368
1369 if (!(device = m_pMixerDevice)) {
1370 if (!(device = getenv("ALSA_MIXER"))) {
1371 device = "default";
1372 }
1373 }
1374 if (!(channel = m_pMixerChannel)) {
1375 if (!(channel = getenv("ALSA_MIXER_CHANNEL"))) {
1376 channel = "PCM";
1377 }
1378 }
1379 LOGDEBUG2(L_SOUND, "audio: %s: mixer %s - %s open", __FUNCTION__, device, channel);
1380 snd_mixer_open(&alsaMixer, 0);
1381 if (alsaMixer && snd_mixer_attach(alsaMixer, device) >= 0
1382 && snd_mixer_selem_register(alsaMixer, NULL, NULL) >= 0
1383 && snd_mixer_load(alsaMixer) >= 0) {
1384
1385 const char *const alsaMixerElem_name = channel;
1386
1387 alsaMixerElem = snd_mixer_first_elem(alsaMixer);
1388 while (alsaMixerElem) {
1389 const char *name;
1390
1391 name = snd_mixer_selem_get_name(alsaMixerElem);
1392 if (!strcasecmp(name, alsaMixerElem_name)) {
1393 snd_mixer_selem_get_playback_volume_range(alsaMixerElem, &alsaMixerElemMin, &alsaMixerElemMax);
1394 m_alsaRatio = 1000 * (alsaMixerElemMax - alsaMixerElemMin);
1395 LOGDEBUG2(L_SOUND, "audio: %s: %s mixer found %ld - %ld ratio %d", __FUNCTION__, channel, alsaMixerElemMin, alsaMixerElemMax, m_alsaRatio);
1396 break;
1397 }
1398
1399 alsaMixerElem = snd_mixer_elem_next(alsaMixerElem);
1400 }
1401
1402 m_pAlsaMixer = alsaMixer;
1403 m_pAlsaMixerElem = alsaMixerElem;
1404 } else {
1405 LOGERROR("audio: %s: can't open mixer '%s'", __FUNCTION__, device);
1406 }
1407}
1408
1415{
1416 int v;
1418 v = (volume * m_alsaRatio) / (1000 * 1000);
1419 snd_mixer_selem_set_playback_volume(m_pAlsaMixerElem, SND_MIXER_SCHN_FRONT_LEFT, v);
1420 snd_mixer_selem_set_playback_volume(m_pAlsaMixerElem, SND_MIXER_SCHN_FRONT_RIGHT, v);
1421 }
1422}
1423
1437int cSoftHdAudio::AlsaSetup(int channels, int sample_rate, int passthrough)
1438{
1439 snd_pcm_hw_params_t *hwparams;
1440 int err;
1441 unsigned bufferTimeUs = 100'000;
1442
1443 if (m_pAudioThread) {
1445 delete m_pAudioThread;
1446
1448 }
1449
1450 m_downmix = 0;
1451
1452 snd_pcm_hw_params_alloca(&hwparams);
1453 if ((err = snd_pcm_hw_params_any(m_pAlsaPCMHandle, hwparams)) < 0) {
1454 LOGERROR("audio: %s: Read HW config failed! %s", __FUNCTION__, snd_strerror(err));
1455 return -1;
1456 }
1457
1458 if (!snd_pcm_hw_params_test_access(m_pAlsaPCMHandle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
1459 m_alsaUseMmap = true;
1460 }
1461
1462 m_hwSampleRate = sample_rate;
1463 if ((err = snd_pcm_hw_params_set_rate_near(m_pAlsaPCMHandle, hwparams, &m_hwSampleRate, 0) < 0)) {
1464 LOGERROR("audio: %s: SampleRate %d not supported! %s", __FUNCTION__, sample_rate, snd_strerror(err));
1465 return -1;
1466 }
1467 if ((int)m_hwSampleRate != sample_rate) {
1468 LOGDEBUG2(L_SOUND, "audio: %s: sample_rate %d m_hwSampleRate %d", __FUNCTION__, sample_rate, m_hwSampleRate);
1469 }
1470
1471 m_hwNumChannels = channels;
1472 if ((err = snd_pcm_hw_params_set_channels_near(m_pAlsaPCMHandle, hwparams, &m_hwNumChannels)) < 0) {
1473 LOGWARNING("audio: %s: %d channels not supported! %s", __FUNCTION__, m_hwNumChannels, snd_strerror(err));
1474 }
1475 if ((int)m_hwNumChannels != channels && !passthrough) {
1476 m_downmix = 1;
1477 }
1478
1479 if ((err = snd_pcm_hw_params_set_buffer_time_near(m_pAlsaPCMHandle, hwparams, &bufferTimeUs, NULL)) < 0) {
1480 LOGWARNING("audio: %s: bufferTime %d not supported! %s", __FUNCTION__, bufferTimeUs, snd_strerror(err));
1481 }
1482
1483 m_alsaBufferSizeFrames = MsToFrames(bufferTimeUs / 1000);
1484
1485/* err = snd_pcm_hw_params_test_format(m_pAlsaPCMHandle, hwparams, SND_PCM_FORMAT_S16);
1486 if (err < 0) // err == 0 if is supported
1487 LOGERROR("audio: %s: SND_PCM_FORMAT_S16 not supported! %s", __FUNCTION__,
1488 snd_strerror(err));
1489*/
1490 if ((err = snd_pcm_set_params(m_pAlsaPCMHandle, SND_PCM_FORMAT_S16,
1491 m_alsaUseMmap ? SND_PCM_ACCESS_MMAP_INTERLEAVED :
1492 SND_PCM_ACCESS_RW_INTERLEAVED, m_hwNumChannels, m_hwSampleRate, 1, bufferTimeUs))) {
1493
1494 snd_pcm_state_t state = snd_pcm_state(m_pAlsaPCMHandle);
1495 LOGERROR("audio: %s: set params error: %s\n"
1496 " Channels %d SampleRate %d\n"
1497 " HWChannels %d HWSampleRate %d SampleFormat %s\n"
1498 " mmap: %s\n"
1499 " AlsaBufferTime %dms pcm state: %s",
1500 __FUNCTION__,
1501 snd_strerror(err), channels, sample_rate, m_hwNumChannels,
1502 m_hwSampleRate, snd_pcm_format_name(SND_PCM_FORMAT_S16),
1503 m_alsaUseMmap ? "yes" : "no",
1504 bufferTimeUs / 1000, snd_pcm_state_name(state));
1505 return -1;
1506 }
1507
1508 LOGINFO("audio: alsa set up:\n"
1509 " Channels %d SampleRate %d%s\n"
1510 " HWChannels %d HWSampleRate %d SampleFormat %s\n"
1511 " mmap: %s\n"
1512 " AlsaBufferTime %dms",
1513 channels, sample_rate, passthrough ? " -> passthrough" : "",
1515 snd_pcm_format_name(SND_PCM_FORMAT_S16),
1516 m_alsaUseMmap ? "yes" : "no",
1517 bufferTimeUs / 1000);
1518
1519 m_pAudioThread = new cAudioThread(this);
1520
1521 return 0;
1522}
1523
1527static void AlsaNoopCallback( __attribute__ ((unused))
1528 const char *file, __attribute__ ((unused))
1529 int line, __attribute__ ((unused))
1530 const char *function, __attribute__ ((unused))
1531 int err, __attribute__ ((unused))
1532 const char *fmt, ...)
1533{
1534}
1535
1540{
1541#ifdef ALSA_DEBUG
1542 (void)AlsaNoopCallback;
1543#else
1544 // disable display of alsa error messages
1545 snd_lib_error_set_handler(AlsaNoopCallback);
1546#endif
1547
1549 AlsaInitMixer();
1550}
1551
1556{
1557 if (m_pAlsaPCMHandle) {
1558 snd_pcm_close(m_pAlsaPCMHandle);
1559 m_pAlsaPCMHandle = NULL;
1560 }
1561 if (m_pAlsaMixer) {
1562 snd_mixer_close(m_pAlsaMixer);
1563 m_pAlsaMixer = NULL;
1564 m_pAlsaMixerElem = NULL;
1565 }
1566}
1567
1572{
1573 for (Event event : m_eventQueue)
1575
1576 m_eventQueue.clear();
1577}
1578
1589{
1590 if (m_passthrough)
1591 return;
1592
1593 double bufferFillLevelMs = FramesToMsDouble(m_fillLevel.GetBufferFillLevelFramesAvg());
1594 if (m_fillLevel.IsSettled()) {
1595 auto now = std::chrono::steady_clock::now();
1596 std::chrono::duration<double> elapsedSec = now - m_lastPidInvocation;
1597 m_lastPidInvocation = now;
1598
1599 m_pitchPpm = m_pidController.Update(bufferFillLevelMs, elapsedSec.count()) * -1;
1600 } else
1601 m_pidController.SetTargetValue(bufferFillLevelMs);
1602
1603 if (m_packetCounter++ % 1000 == 0) {
1604 LOGDEBUG2(L_SOUND, "audio: %s: buffer fill level: %.1fms (target: %.1fms), clock drift compensating pitch: %.1fppm, PID controller: P=%.2fppm I=%.2fppm D=%.2fppm",
1605 __FUNCTION__,
1606 bufferFillLevelMs,
1608 m_pitchPpm.load(),
1612 }
1613
1614 // buffer fill level low pass filter
1615 int hardwareBufferFillLevelFrames = m_alsaBufferSizeFrames - snd_pcm_avail(m_pAlsaPCMHandle);
1616
1617 if (hardwareBufferFillLevelFrames < 0)
1618 LOGWARNING("audio: %s: snd_pcm_avail() failes: %s", __FUNCTION__, snd_strerror(hardwareBufferFillLevelFrames));
1619 else
1620 m_fillLevel.UpdateAvgBufferFillLevel(hardwareBufferFillLevelFrames);
1621}
static void ReorderAudioFrame(uint16_t *buf, int size, int channels)
Reorder audio frame.
Definition audio.cpp:95
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:1527
Audio and alsa module header file.
#define NORMALIZE_MAX_INDEX
number of average values
Definition audio.h:41
#define AV_SYNC_BORDER_MS
absolute max a/v difference in ms which should trigger a resync
Definition audio.h:42
virtual void OnEventReceived(const Event &)=0
Audio thread class.
Definition threads.h:84
void Stop(void)
Definition threads.cpp:145
void ResetFramesCounters()
Resets the received and written frames counters.
Definition filllevel.cpp:37
void UpdateAvgBufferFillLevel(int)
Updates the buffer fill level average.
Definition filllevel.cpp:57
void ReceivedFrames(int count)
Definition filllevel.h:29
void WroteFrames(int count)
Definition filllevel.h:30
void Reset()
Resets the filter state.
Definition filllevel.cpp:26
void Reset()
Resets the internal state (integral sum and error history)
double GetTargetValue()
double GetPTerm()
double Update(double, double)
Calculates the new output value.
double GetDTerm()
void SetTargetValue(double value)
double GetITerm()
void LazyInit(void)
Initialize audio output module.
Definition audio.cpp:1018
cSoftHdAudio(cSoftHdDevice *)
cSoftHdAudio constructor
Definition audio.cpp:61
char * OpenAlsaDevice(const char *, int)
Open alsa device.
Definition audio.cpp:1193
int m_alsaBufferSizeFrames
alsa buffer size in frames
Definition audio.h:99
char * FindAlsaDevice(const char *, const char *, int)
Find alsa device giving some search hints.
Definition audio.cpp:1245
bool m_appendAES
flag ato utomatic append AES
Definition audio.h:126
void Filter(AVFrame *, AVCodecContext *)
Send audio frame to filter and enqueue it.
Definition audio.cpp:772
cSoftHdRingbuffer m_pRingbuffer
sample ring buffer
Definition audio.h:168
int m_pitchAdjustFrameCounter
counter for pitch adjustment frames
Definition audio.h:115
double FramesToMsDouble(int frames)
Definition audio.h:197
int AlsaSetup(int channels, int sample_rate, int passthrough)
Setup alsa audio for requested format.
Definition audio.cpp:1437
int Setup(AVCodecContext *, int, int, int)
Setup alsa.
Definition audio.cpp:683
void Enqueue(uint16_t *, int, AVFrame *)
Send audio data to ringbuffer.
Definition audio.cpp:632
int m_compressionMaxFactor
max. compression factor
Definition audio.h:142
const char * m_pPCMDevice
PCM device name.
Definition audio.h:124
int m_volume
current volume (0 .. 1000)
Definition audio.h:147
cAudioThread * m_pAudioThread
pointer to audio thread
Definition audio.h:103
void SetStereoDescent(int)
Set stereo loudness descent.
Definition audio.cpp:993
int64_t GetHardwareOutputPtsMs(void)
Get the hardware output PTS in milliseconds.
Definition audio.cpp:888
std::mutex m_pauseMutex
mutex for a safe thread pausing
Definition audio.h:112
int64_t GetHardwareOutputPtsTimebaseUnits(void)
Get the hardware output PTS in timebase units.
Definition audio.cpp:912
bool HasPts(void)
Definition audio.h:70
AVFilterContext * m_pBuffersinkCtx
Definition audio.h:162
cPidController m_pidController
PID controller for clock drift compensation with tuning values coming from educated guesses.
Definition audio.h:97
void SetVolume(int)
Set mixer volume (0-1000)
Definition audio.cpp:925
AVFilterContext * m_pBuffersrcCtx
Definition audio.h:161
void HandleError(int)
handle error
Definition audio.cpp:1052
AVFilterGraph * m_pFilterGraph
Definition audio.h:160
int m_passthrough
passthrough mask
Definition audio.h:123
const int m_bytesPerSample
number of bytes per sample
Definition audio.h:107
const char * m_pMixerChannel
mixer channel name
Definition audio.h:155
unsigned int m_hwSampleRate
hardware sample rate in Hz
Definition audio.h:108
int64_t PtsToMs(int64_t pts)
Definition audio.h:193
cBufferFillLevelLowPassFilter m_fillLevel
low pass filter for the buffer fill level
Definition audio.h:96
IEventReceiver * m_pEventReceiver
pointer to event receiver
Definition audio.h:95
void ProcessEvents(void)
Process queued events and forward to event receiver.
Definition audio.cpp:1571
int MsToFrames(int milliseconds)
Definition audio.h:195
std::vector< Event > m_eventQueue
event queue for incoming events
Definition audio.h:113
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
Definition audio.cpp:570
AVRational * m_pTimebase
pointer to AVCodecContext pkts_timebase
Definition audio.h:110
bool m_compression
flag to use compress volume
Definition audio.h:140
int64_t GetOutputPtsMs(void)
Get the output PTS of the ringbuffer.
Definition audio.cpp:867
bool m_normalize
flag to use volume normalize
Definition audio.h:129
AVFrame * FilterGetFrame(void)
Get frame from filter sink.
Definition audio.cpp:706
void ClockDriftCompensation(void)
Calculate clock drift compensation.
Definition audio.cpp:1588
int m_filterChanged
filter has changed
Definition audio.h:158
void AlsaExit(void)
Cleanup alsa audio output module.
Definition audio.cpp:1555
void SetCompression(bool, int)
Set volume compression parameters.
Definition audio.cpp:975
snd_mixer_elem_t * m_pAlsaMixerElem
alsa mixer element
Definition audio.h:180
void AlsaInit(void)
Initialize alsa audio output module.
Definition audio.cpp:1539
void Compress(uint16_t *, int)
Compress audio.
Definition audio.cpp:233
int64_t m_inputPts
pts clock (last pts in ringbuffer)
Definition audio.h:119
int m_normalizeFactor
current normalize factor
Definition audio.h:135
void AlsaSetVolume(int)
Set alsa mixer volume (0-1000)
Definition audio.cpp:1414
int64_t GetOutputPtsMsInternal(void)
Definition audio.cpp:874
cSoftHdConfig * m_pConfig
pointer to config
Definition audio.h:94
std::atomic< double > m_pitchPpm
pitch adjustment in ppm. Positive values are faster
Definition audio.h:114
void Exit(void)
Cleanup audio output module.
Definition audio.cpp:1029
int m_amplifier
software volume amplify factor
Definition audio.h:145
int GetUsedBytes(void)
Get used bytes in audio ringbuffer.
Definition audio.cpp:849
void SetPaused(bool)
Set audio playback paused state.
Definition audio.cpp:948
void AlsaInitMixer(void)
Initialize alsa mixer.
Definition audio.cpp:1360
const char * m_pPassthroughDevice
passthrough device name
Definition audio.h:125
void SetPassthrough(int)
Set audio passthrough mask.
Definition audio.cpp:1004
int m_normalizeMaxFactor
max. normalize factor
Definition audio.h:137
bool m_alsaUseMmap
use mmap
Definition audio.h:182
void Normalize(uint16_t *, int)
Normalize audio.
Definition audio.cpp:152
int m_compressionFactor
current compression factor
Definition audio.h:141
int64_t MsToPts(int64_t ptsMs)
Definition audio.h:194
const int m_normalizeMinFactor
min. normalize factor
Definition audio.h:136
const int m_normalizeSamples
number of normalize samples
Definition audio.h:130
int m_filterReady
filter is ready
Definition audio.h:159
void SetEq(int[18], int)
Set equalizer bands.
Definition audio.cpp:319
int m_normalizeReady
index normalize counter
Definition audio.h:134
const char * m_pMixerDevice
mixer device name (not used)
Definition audio.h:154
uint32_t m_normalizeAverage[NORMALIZE_MAX_INDEX]
average of n last normalize sample blocks
Definition audio.h:132
bool CyclicCall(void)
Cyclic audio playback call.
Definition audio.cpp:1109
void AlsaInitPCMDevice(void)
Search for an alsa pcm device and open it.
Definition audio.cpp:1281
unsigned int m_hwNumChannels
number of hardware channels
Definition audio.h:109
bool m_initialized
class initialized
Definition audio.h:106
int FramesToMs(int frames)
Definition audio.h:196
int m_stereoDescent
volume descent for stereo
Definition audio.h:146
std::mutex m_mutex
mutex for thread safety
Definition audio.h:111
int m_alsaRatio
internal -> mixer ratio * 1000
Definition audio.h:181
int m_packetCounter
packet counter for logging
Definition audio.h:100
int GetFreeBytes(void)
Get free bytes in audio ringbuffer.
Definition audio.cpp:839
void EnqueueFrame(AVFrame *)
Place samples in audio output queue.
Definition audio.cpp:602
void SoftAmplify(int16_t *, int)
Software amplifier.
Definition audio.cpp:290
void FlushAlsaBuffers(void)
Flush alsa buffers.
Definition audio.cpp:1066
void SetNormalize(bool, int)
Set normalize volume parameters.
Definition audio.cpp:963
snd_pcm_t * m_pAlsaPCMHandle
alsa pcm handle
Definition audio.h:178
int m_downmix
set stereo downmix
Definition audio.h:117
int m_useEqualizer
flag to use equalizer
Definition audio.h:150
float m_equalizerBand[18]
equalizer band
Definition audio.h:151
snd_mixer_t * m_pAlsaMixer
alsa mixer handle
Definition audio.h:179
int m_normalizeIndex
index into normalize average table
Definition audio.h:133
std::chrono::steady_clock::time_point m_lastPidInvocation
last time the PID controller was invoked
Definition audio.h:98
int m_normalizeCounter
normalize sample counter
Definition audio.h:131
std::atomic< bool > m_paused
audio is paused
Definition audio.h:120
int CheckForFilterReady(AVCodecContext *)
Check if the filter has changed and is ready, init the filter if needed.
Definition audio.cpp:739
int InitFilter(AVCodecContext *)
Init filter.
Definition audio.cpp:395
void FlushBuffers(void)
Flush audio buffers.
Definition audio.cpp:816
bool m_softVolume
flag to use soft volume
Definition audio.h:122
bool ConfigAudioNormalize
config use normalize volume
Definition config.h:47
int ConfigAudioStereoDescent
config reduce stereo loudness
Definition config.h:51
bool ConfigAudioCompression
config use volume compression
Definition config.h:49
int ConfigAudioEqBand[18]
config equalizer filter bands
Definition config.h:54
int ConfigAudioMaxCompression
config max volume compression
Definition config.h:50
int ConfigAudioEq
config equalizer filter
Definition config.h:53
int ConfigAudioMaxNormalize
config max normalize factor
Definition config.h:48
size_t UsedBytes(void)
Get used bytes in ring buffer.
size_t FreeBytes(void)
Get free bytes in ring buffer.
size_t ReadAdvance(size_t)
Advance read pointer in ring buffer.
size_t GetReadPointer(const void **)
Get read pointer and used bytes at this position of ring buffer.
size_t Write(const void *, size_t)
Write to a ring buffer.
void Reset(void)
Reset ring buffer pointers.
SoftHdDevice config 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:79
@ AUDIO
Definition event.h:26
Logger class header file.
#define LOGDEBUG2
Definition logger.h:45
#define LOGERROR
Definition logger.h:41
#define L_SOUND
Definition logger.h:55
#define L_AV_SYNC
Definition logger.h:54
#define LOGWARNING
Definition logger.h:42
#define LOGINFO
Definition logger.h:43
#define LOGFATAL
Logger macros.
Definition logger.h:40
Misc function header file.
#define AV_NOPTS_VALUE
Definition misc.h:69
static const char * Timestamp2String(int64_t ts, uint8_t divisor)
Workaround for av_err2str() not working with C++.
Definition misc.h:119
Proportinal, Integral, Derivative Controller.
Ringbuffer class header file.
Device class header file.
Thread classes header file.