vdr-plugin-softhddevice-drm-gles 1.4.0
audio.cpp
Go to the documentation of this file.
1
26 #include <algorithm>
27
28#include <stdint.h>
29#include <math.h>
30
31#include <libintl.h>
32#include <alsa/asoundlib.h>
33
34#include <pthread.h>
35
36extern "C"
37{
38#include <libavcodec/avcodec.h>
39#include <libavfilter/avfilter.h>
40#include <libavfilter/buffersink.h>
41#include <libavfilter/buffersrc.h>
42#include <libavutil/channel_layout.h>
43#include <libavutil/opt.h>
44}
45
46#include "misc.h"
47
48#include "iatomic.h"
49
50#include "ringbuffer.h"
51#include "audio.h"
52#include "videorender.h"
53#include "codec_audio.h"
54#include "videostream.h"
55
56#include "logger.h"
57#include "threads.h"
58
59/******************************************************************************
60 * cSoftHdAudio class
61 *****************************************************************************/
62
67{
68 m_pDevice = device;
69 m_pEventReceiver = device;
71
74
75 m_pFilterGraph = nullptr;
76
77 m_pAlsaMixer = nullptr;
78 m_pMixerDevice = nullptr;
80 m_pAlsaMixerElem = nullptr;
81
85
93 m_passthrough = 0;
96
97 m_alsaCanPause = false;
98
100}
101
106{
107}
108
109/******************************************************************************
110 * Audio filter and manipulation
111 *****************************************************************************/
112
124static void ReorderAudioFrame(uint16_t * buf, int size, int channels)
125{
126 int i;
127 int c;
128 int ls;
129 int rs;
130 int lfe;
131
132 switch (channels) {
133 case 5:
134 size /= 2;
135 for (i = 0; i < size; i += 5) {
136 c = buf[i + 2];
137 ls = buf[i + 3];
138 rs = buf[i + 4];
139 buf[i + 2] = ls;
140 buf[i + 3] = rs;
141 buf[i + 4] = c;
142 }
143 break;
144 case 6:
145 size /= 2;
146 for (i = 0; i < size; i += 6) {
147 c = buf[i + 2];
148 lfe = buf[i + 3];
149// ls = buf[i + 4]; tested from jsffm
150// rs = buf[i + 5];
151// buf[i + 2] = ls;
152// buf[i + 3] = rs;
153 buf[i + 2] = lfe;
154 buf[i + 3] = c;
155// buf[i + 4] = c;
156// buf[i + 5] = lfe;
157 }
158 break;
159 case 8:
160 size /= 2;
161 for (i = 0; i < size; i += 8) {
162 c = buf[i + 2];
163 lfe = buf[i + 3];
164 ls = buf[i + 4];
165 rs = buf[i + 5];
166 buf[i + 2] = ls;
167 buf[i + 3] = rs;
168 buf[i + 4] = c;
169 buf[i + 5] = lfe;
170 }
171 break;
172 }
173}
174
181void cSoftHdAudio::Normalize(uint16_t *samples, int count)
182{
183 int i;
184 int l;
185 int n;
186 uint32_t avg;
187 int factor;
188 uint16_t *data;
189
190 // average samples
191 l = count / m_bytesPerSample;
192 data = samples;
193 do {
194 n = l;
197 }
199 for (i = 0; i < n; ++i) {
200 int t;
201
202 t = data[i];
203 avg += (t * t) / m_normalizeSamples;
204 }
210 } else {
211 avg = 0;
212 for (i = 0; i < NORMALIZE_MAX_INDEX; ++i) {
214 }
215
216 // calculate normalize factor
217 if (avg > 0) {
218 factor = ((INT16_MAX / 8) * 1000U) / (uint32_t) sqrt(avg);
219 // smooth normalize
220 m_normalizeFactor = (m_normalizeFactor * 500 + factor * 500) / 1000;
223 }
226 }
227 } else {
228 factor = 1000;
229 }
230 LOGDEBUG2(L_SOUND, "audio: %s: avg %8d, fac=%6.3f, norm=%6.3f", __FUNCTION__,
231 avg, factor / 1000.0, m_normalizeFactor / 1000.0);
232 }
233
237 }
238 data += n;
239 l -= n;
240 } while (l > 0);
241
242 // apply normalize factor
243 for (i = 0; i < count / m_bytesPerSample; ++i) {
244 int t;
245
246 t = (samples[i] * m_normalizeFactor) / 1000;
247 if (t < INT16_MIN) {
248 t = INT16_MIN;
249 } else if (t > INT16_MAX) {
250 t = INT16_MAX;
251 }
252 samples[i] = t;
253 }
254}
255
262void cSoftHdAudio::Compress(uint16_t *samples, int count)
263{
264 int maxSample;
265 int i;
266 int factor;
267
268 // find loudest sample
269 maxSample = 0;
270 for (i = 0; i < count / m_bytesPerSample; ++i) {
271 int t;
272
273 t = abs(samples[i]);
274 if (t > maxSample) {
275 maxSample = t;
276 }
277 }
278
279 // calculate compression factor
280 if (maxSample > 0) {
281 factor = (INT16_MAX * 1000) / maxSample;
282 // smooth compression (FIXME: make configurable?)
283 m_compressionFactor = (m_compressionFactor * 950 + factor * 50) / 1000;
284 if (m_compressionFactor > factor) {
285 m_compressionFactor = factor; // no clipping
286 }
289 }
290 } else {
291 return; // silent nothing todo
292 }
293
294 LOGDEBUG2(L_SOUND, "audio: %s: max %5d, fac=%6.3f, com=%6.3f", __FUNCTION__, maxSample,
295 factor / 1000.0, m_compressionFactor / 1000.0);
296
297 // apply compression factor
298 for (i = 0; i < count / m_bytesPerSample; ++i) {
299 int t;
300
301 t = (samples[i] * m_compressionFactor) / 1000;
302 if (t < INT16_MIN) {
303 t = INT16_MIN;
304 } else if (t > INT16_MAX) {
305 t = INT16_MAX;
306 }
307 samples[i] = t;
308 }
309}
310
319void cSoftHdAudio::SoftAmplify(int16_t *samples, int count)
320{
321 int i;
322
323 // silence
324 if (m_volume == 0 || !m_amplifier) {
325 memset(samples, 0, count);
326 return;
327 }
328
329 for (i = 0; i < count / m_bytesPerSample; ++i) {
330 int t;
331
332 t = (samples[i] * m_amplifier) / 1000;
333 if (t < INT16_MIN) {
334 t = INT16_MIN;
335 } else if (t > INT16_MAX) {
336 t = INT16_MAX;
337 }
338 samples[i] = t;
339 }
340}
341
348void cSoftHdAudio::SetEq(int band[18], int onoff)
349{
350 int i;
351/*
352 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__,
353 band[0], band[1], band[2], band[3], band[4], band[5], band[6], band[7],
354 band[8], band[9], band[10], band[11], band[12], band[13], band[14],
355 band[15], band[16], band[17], onoff);
356*/
357 for (i = 0; i < 18; i++) {
358 switch (band[i]) {
359 case 1:
360 m_equalizerBand[i] = 1.5;
361 break;
362 case 0:
363 m_equalizerBand[i] = 1;
364 break;
365 case -1:
366 m_equalizerBand[i] = 0.95;
367 break;
368 case -2:
369 m_equalizerBand[i] = 0.9;
370 break;
371 case -3:
372 m_equalizerBand[i] = 0.85;
373 break;
374 case -4:
375 m_equalizerBand[i] = 0.8;
376 break;
377 case -5:
378 m_equalizerBand[i] = 0.75;
379 break;
380 case -6:
381 m_equalizerBand[i] = 0.7;
382 break;
383 case -7:
384 m_equalizerBand[i] = 0.65;
385 break;
386 case -8:
387 m_equalizerBand[i] = 0.6;
388 break;
389 case -9:
390 m_equalizerBand[i] = 0.55;
391 break;
392 case -10:
393 m_equalizerBand[i] = 0.5;
394 break;
395 case -11:
396 m_equalizerBand[i] = 0.45;
397 break;
398 case -12:
399 m_equalizerBand[i] = 0.4;
400 break;
401 case -13:
402 m_equalizerBand[i] = 0.35;
403 break;
404 case -14:
405 m_equalizerBand[i] = 0.3;
406 break;
407 case -15:
408 m_equalizerBand[i] = 0.25;
409 break;
410 }
411 }
412
413 m_filterChanged = 1;
414 m_useEqualizer = onoff;
415}
416
424int cSoftHdAudio::InitFilter(AVCodecContext *audioCtx)
425{
426 const AVFilter *abuffer;
427 AVFilterContext *pfilterCtx[3];
428 const AVFilter *eq;
429 const AVFilter *aformat;
430 const AVFilter *abuffersink;
431 char channelLayout[64];
432 char optionsStr[1024];
433 int err, i, numFilter = 0;
434
435 // Before filter init set HW parameter.
436 if (audioCtx->sample_rate != (int)m_hwSampleRate ||
437 (audioCtx->ch_layout.nb_channels != (int)m_hwNumChannels &&
438 !(m_downmix && m_hwNumChannels == 2))) {
439
440 err = AlsaSetup(audioCtx->ch_layout.nb_channels, audioCtx->sample_rate, 0);
441 if (err)
442 return err;
443 }
444
445 m_pTimebase = &audioCtx->pkt_timebase;
446
447#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7,16,100)
448 avfilter_register_all();
449#endif
450
451 if (!(m_pFilterGraph = avfilter_graph_alloc())) {
452 LOGERROR("audio: %s: Unable to create filter graph.", __FUNCTION__);
453 return -1;
454 }
455
456 // input buffer
457 if (!(abuffer = avfilter_get_by_name("abuffer"))) {
458 LOGWARNING("audio: %s: Could not find the abuffer filter.", __FUNCTION__);
459 avfilter_graph_free(&m_pFilterGraph);
460 return -1;
461 }
462 if (!(m_pBuffersrcCtx = avfilter_graph_alloc_filter(m_pFilterGraph, abuffer, "src"))) {
463 LOGWARNING("audio: %s: Could not allocate the m_pBuffersrcCtx instance.", __FUNCTION__);
464 avfilter_graph_free(&m_pFilterGraph);
465 return -1;
466 }
467
468 av_channel_layout_describe(&audioCtx->ch_layout, channelLayout, sizeof(channelLayout));
469
470 LOGDEBUG2(L_SOUND, "audio: %s: IN channelLayout %s sample_fmt %s sample_rate %d channels %d", __FUNCTION__,
471 channelLayout, av_get_sample_fmt_name(audioCtx->sample_fmt), audioCtx->sample_rate, audioCtx->ch_layout.nb_channels);
472
473 av_opt_set (m_pBuffersrcCtx, "channel_layout", channelLayout, AV_OPT_SEARCH_CHILDREN);
474 av_opt_set (m_pBuffersrcCtx, "sample_fmt", av_get_sample_fmt_name(audioCtx->sample_fmt), AV_OPT_SEARCH_CHILDREN);
475 av_opt_set_q (m_pBuffersrcCtx, "time_base", (AVRational){ 1, audioCtx->sample_rate }, AV_OPT_SEARCH_CHILDREN);
476 av_opt_set_int(m_pBuffersrcCtx, "sample_rate", audioCtx->sample_rate, AV_OPT_SEARCH_CHILDREN);
477// av_opt_set_int(m_pBuffersrcCtx, "channel_counts", audioCtx->channels, AV_OPT_SEARCH_CHILDREN);
478
479 // initialize the filter with NULL options, set all options above.
480 if (avfilter_init_str(m_pBuffersrcCtx, NULL) < 0) {
481 LOGWARNING("audio: %s: Could not initialize the abuffer filter.", __FUNCTION__);
482 avfilter_graph_free(&m_pFilterGraph);
483 return -1;
484 }
485
486 // superequalizer
487 if (m_useEqualizer) {
488 if (!(eq = avfilter_get_by_name("superequalizer"))) {
489 LOGWARNING("audio: %s: Could not find the superequalizer filter.", __FUNCTION__);
490 avfilter_graph_free(&m_pFilterGraph);
491 return -1;
492 }
493 if (!(pfilterCtx[numFilter] = avfilter_graph_alloc_filter(m_pFilterGraph, eq, "superequalizer"))) {
494 LOGWARNING("audio: %s: Could not allocate the superequalizer instance.", __FUNCTION__);
495 avfilter_graph_free(&m_pFilterGraph);
496 return -1;
497 }
498 snprintf(optionsStr, sizeof(optionsStr),"1b=%.2f:2b=%.2f:3b=%.2f:4b=%.2f:5b=%.2f"
499 ":6b=%.2f:7b=%.2f:8b=%.2f:9b=%.2f:10b=%.2f:11b=%.2f:12b=%.2f:13b=%.2f:14b=%.2f:"
500 "15b=%.2f:16b=%.2f:17b=%.2f:18b=%.2f ", m_equalizerBand[0], m_equalizerBand[1],
505 if (avfilter_init_str(pfilterCtx[numFilter], optionsStr) < 0) {
506 LOGWARNING("audio: %s: Could not initialize the superequalizer filter.", __FUNCTION__);
507 avfilter_graph_free(&m_pFilterGraph);
508 return -1;
509 }
510 numFilter++;
511 }
512
513 // aformat
514 AVChannelLayout channel_layout;
515 av_channel_layout_default(&channel_layout, m_hwNumChannels);
516 av_channel_layout_describe(&channel_layout, channelLayout, sizeof(channelLayout));
517 av_channel_layout_uninit(&channel_layout);
518 // should use IN layout if more then 2 ch!?
519 LOGDEBUG2(L_SOUND, "audio: %s: OUT m_downmix %d m_hwNumChannels %d m_hwSampleRate %d channelLayout %s bytes_per_sample %d",
520 __FUNCTION__, m_downmix, m_hwNumChannels, m_hwSampleRate, channelLayout, av_get_bytes_per_sample(AV_SAMPLE_FMT_S16));
521 if (!(aformat = avfilter_get_by_name("aformat"))) {
522 LOGWARNING("audio: %s: Could not find the aformat filter.", __FUNCTION__);
523 avfilter_graph_free(&m_pFilterGraph);
524 return -1;
525 }
526 if (!(pfilterCtx[numFilter] = avfilter_graph_alloc_filter(m_pFilterGraph, aformat, "aformat"))) {
527 LOGWARNING("audio: %s: Could not allocate the aformat instance.", __FUNCTION__);
528 avfilter_graph_free(&m_pFilterGraph);
529 return -1;
530 }
531 snprintf(optionsStr, sizeof(optionsStr),
532 "sample_fmts=%s:sample_rates=%d:channel_layouts=%s",
533 av_get_sample_fmt_name(AV_SAMPLE_FMT_S16), m_hwSampleRate, channelLayout);
534 if (avfilter_init_str(pfilterCtx[numFilter], optionsStr) < 0) {
535 LOGWARNING("audio: %s: Could not initialize the aformat filter.", __FUNCTION__);
536 avfilter_graph_free(&m_pFilterGraph);
537 return -1;
538 }
539 numFilter++;
540
541 // abuffersink
542 if (!(abuffersink = avfilter_get_by_name("abuffersink"))) {
543 LOGWARNING("audio: %s: Could not find the abuffersink filter.", __FUNCTION__);
544 avfilter_graph_free(&m_pFilterGraph);
545 return -1;
546 }
547 if (!(pfilterCtx[numFilter] = avfilter_graph_alloc_filter(m_pFilterGraph, abuffersink, "sink"))) {
548 LOGWARNING("audio: %s: Could not allocate the abuffersink instance.", __FUNCTION__);
549 avfilter_graph_free(&m_pFilterGraph);
550 return -1;
551 }
552 if (avfilter_init_str(pfilterCtx[numFilter], NULL) < 0) {
553 LOGWARNING("audio: %s: Could not initialize the abuffersink instance.", __FUNCTION__);
554 avfilter_graph_free(&m_pFilterGraph);
555 return -1;
556 }
557 numFilter++;
558
559 // Connect the filters
560 for (i = 0; i < numFilter; i++) {
561 if (i == 0) {
562 err = avfilter_link(m_pBuffersrcCtx, 0, pfilterCtx[i], 0);
563 } else {
564 err = avfilter_link(pfilterCtx[i - 1], 0, pfilterCtx[i], 0);
565 }
566 }
567 if (err < 0) {
568 LOGWARNING("audio: %s: Error connecting audio filters", __FUNCTION__);
569 avfilter_graph_free(&m_pFilterGraph);
570 return -1;
571 }
572
573 // Configure the graph.
574 if (avfilter_graph_config(m_pFilterGraph, NULL) < 0) {
575 LOGWARNING("audio: %s: Error configuring the audio filter graph", __FUNCTION__);
576 avfilter_graph_free(&m_pFilterGraph);
577 return -1;
578 }
579
580 m_pBuffersinkCtx = pfilterCtx[numFilter - 1];
581 m_filterChanged = 0;
582 m_filterReady = 1;
583
584 return 0;
585}
586
587/******************************************************************************
588 * Audio stream handling
589 *****************************************************************************/
590
600{
601 std::lock_guard<std::mutex> lock(m_mutex);
602
603 if (!HasPts())
604 return;
605
606 int64_t dropMs = std::max((int64_t)0, ptsMs - GetOutputPtsMsInternal());
607 int dropBytes = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, MsToFrames(dropMs));
608
609 dropBytes = std::min(dropBytes, (int)m_pRingbuffer.UsedBytes());
610
611 if (dropBytes > 0) {
612 LOGDEBUG2(L_AV_SYNC, "audio: %s: dropping %dms audio samples to start in sync with the video (output PTS %s -> %s)",
613 __FUNCTION__,
614 dropMs,
616 Timestamp2String(ptsMs, 1));
617
618 m_pRingbuffer.ReadAdvance(dropBytes);
619 }
620}
621
627void cSoftHdAudio::EnqueueFrame(AVFrame *frame)
628{
629 if (!frame)
630 return;
631
632 uint16_t *buffer;
633
634 int byteCount = frame->nb_samples * frame->ch_layout.nb_channels * m_bytesPerSample;
635 buffer = (uint16_t *)frame->data[0];
636
637 if (m_compression) { // in place operation
638 Compress(buffer, byteCount);
639 }
640 if (m_normalize) { // in place operation
641 Normalize(buffer, byteCount);
642 }
643 ReorderAudioFrame(buffer, byteCount, frame->ch_layout.nb_channels);
644
645 Enqueue((uint16_t *)buffer, byteCount, frame);
646
647 av_frame_free(&frame);
648}
649
657void cSoftHdAudio::Enqueue(uint16_t *buffer, int count, AVFrame *frame)
658{
659 std::lock_guard<std::mutex> lock(m_mutex);
660
661 size_t n = m_pRingbuffer.Write((const uint16_t *)buffer, count);
662 if (n != (size_t) count)
663 LOGERROR("audio: %s: can't place %d samples in ring buffer", __FUNCTION__, count);
664
665 if (frame->pts != AV_NOPTS_VALUE)
666 m_inputPts = frame->pts;
667}
668
682int cSoftHdAudio::Setup(AVCodecContext *ctx, int samplerate, int channels, int passthrough)
683{
684 int err = 0;
685
686 if (samplerate != (int)m_hwSampleRate ||
687 (channels != (int)m_hwNumChannels && !(m_downmix && m_hwNumChannels == 2))) {
688
689 err = AlsaSetup(channels, samplerate, passthrough);
690 if (err) {
691 LOGERROR("audio: %s: failed!", __FUNCTION__);
692 return err;
693 }
694 }
695 m_pTimebase = &ctx->pkt_timebase;
696
697 return 0;
698}
699
706{
707 AVFrame *outframe = nullptr;
708 outframe = av_frame_alloc();
709 if (!outframe) {
710 LOGERROR("audio: %s: Error allocating frame", __FUNCTION__);
711 return NULL;
712 }
713
714 int err = av_buffersink_get_frame(m_pBuffersinkCtx, outframe);
715
716 if (err == AVERROR(EAGAIN)) {
717// LOGERROR("audio: %s: Error filtering AVERROR(EAGAIN)", __FUNCTION__);
718 av_frame_free(&outframe);
719 } else if (err == AVERROR_EOF) {
720 LOGERROR("audio: %s: Error filtering AVERROR_EOF", __FUNCTION__);
721 av_frame_free(&outframe);
722 } else if (err < 0) {
723 LOGERROR("audio: %s: Error filtering the data", __FUNCTION__);
724 av_frame_free(&outframe);
725 }
726
727 return outframe;
728}
729
738int cSoftHdAudio::CheckForFilterReady(AVCodecContext *ctx)
739{
741// 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",
742// __FUNCTION__, m_filterReady,
743// m_pFilterGraph->sink_links_count, m_pFilterGraph->sink_links[0]->channels,
744// m_pFilterGraph->filters[m_pFilterGraph->nb_filters - 1]->nb_outputs,
745// 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,
746// m_filterChanged);
747 avfilter_graph_free(&m_pFilterGraph);
748 m_filterReady = 0;
749 LOGDEBUG2(L_SOUND, "audio: %s: Free the filter graph.", __FUNCTION__);
750 }
751
752 if (!m_filterReady) {
753 if (InitFilter(ctx)) {
754 LOGDEBUG2(L_SOUND, "audio: %s: AudioFilterReady failed!", __FUNCTION__);
755 return 1;
756 }
757 }
758
759 return 0;
760}
761
771void cSoftHdAudio::Filter(AVFrame *inframe, AVCodecContext *ctx)
772{
773 AVFrame *outframe = NULL;
774 int err = -1;
775 int err_count = 0;
776
777 if (inframe) {
778 while (err < 0) {
779 if (CheckForFilterReady(ctx)) {
780 av_frame_unref(inframe);
781 return;
782 }
783
784 err = av_buffersrc_add_frame(m_pBuffersrcCtx, inframe);
785 if (err < 0) {
786 if (err_count) {
787 char errbuf[128];
788 av_strerror(err, errbuf, sizeof(errbuf));
789 LOGERROR("audio: %s: Error submitting the frame to the filter fmt %s channels %d %s", __FUNCTION__,
790 av_get_sample_fmt_name(ctx->sample_fmt), ctx->ch_layout.nb_channels, errbuf);
791 av_frame_unref(inframe);
792 return;
793 } else {
794 m_filterChanged = 1;
795 err_count++;
796 LOGDEBUG2(L_SOUND, "audio: %s: m_filterChanged %d err_count %d", __FUNCTION__, m_filterChanged, err_count);
797 }
798 }
799 }
800 }
801
802// if (!inframe)
803// LOGDEBUG2(L_SOUND, "audio: %s: NO inframe!", __FUNCTION__);
804
805 outframe = FilterGetFrame();
806 EnqueueFrame(outframe);
807}
808
816{
817 std::lock_guard<std::mutex> lock(m_mutex);
818
819 LOGDEBUG2(L_SOUND, "audio: %s", __FUNCTION__);
820
821 if (!m_initialized)
822 return;
823
826
829 m_filterChanged = 1;
830}
831
836{
837 std::lock_guard<std::mutex> lock(m_mutex);
838
839 return m_pRingbuffer.FreeBytes();
840}
841
846{
847 // FIXME: not correct, if multiple buffer are in use
848 return m_pRingbuffer.UsedBytes();
849}
850
864{
865 std::lock_guard<std::mutex> lock(m_mutex);
866
867 return GetOutputPtsMsInternal();
868}
869
871{
872 return PtsToMs(m_inputPts) - FramesToMs(snd_pcm_bytes_to_frames(m_pAlsaPCMHandle, m_pRingbuffer.UsedBytes()));
873}
874
885{
886 std::lock_guard<std::mutex> lock(m_mutex);
887
889 return AV_NOPTS_VALUE;
890
891 snd_pcm_sframes_t delayFrames;
892 if (snd_pcm_delay(m_pAlsaPCMHandle, &delayFrames) < 0)
893 delayFrames = 0L;
894
895 if (delayFrames < 0) {
896 LOGDEBUG2(L_SOUND, "audio: %s: delay < 0", __FUNCTION__);
897 delayFrames = 0L;
898 }
899
900 return GetOutputPtsMsInternal() - FramesToMs(delayFrames);
901}
902
909 int64_t ptsMs = GetHardwareOutputPtsMs();
910 if (ptsMs == AV_NOPTS_VALUE)
911 return AV_NOPTS_VALUE;
912
913 return MsToPts(ptsMs);
914}
915
922{
923 m_volume = volume;
924 // reduce loudness for stereo output
926 volume -= m_stereoDescent;
927 if (volume < 0) {
928 volume = 0;
929 } else if (volume > 1000) {
930 volume = 1000;
931 }
932 }
933 m_amplifier = volume;
934 if (!m_softVolume) {
935 AlsaSetVolume(volume);
936 }
937}
938
945{
946 LOGDEBUG2(L_SOUND, "audio: %s: %d", __FUNCTION__, pause);
947
948 std::lock_guard<std::mutex> lock(m_pauseMutex);
949
950 m_paused = pause;
951
952 if (m_alsaCanPause) {
953 snd_pcm_state_t expectedState;
954 if (pause)
955 expectedState = SND_PCM_STATE_RUNNING;
956 else
957 expectedState = SND_PCM_STATE_PAUSED;
958
959 if (snd_pcm_state(m_pAlsaPCMHandle) == expectedState) {
960 LOGDEBUG2(L_SOUND, "audio: %s: state running, try snd_pcm_pause(1)!", __FUNCTION__);
961
962 int ret = snd_pcm_pause(m_pAlsaPCMHandle, pause);
963 if (ret)
964 LOGERROR("audio: %s: snd_pcm_pause(): %s", __FUNCTION__, snd_strerror(ret));
965 }
966 }
967}
968
975void cSoftHdAudio::SetNormalize(bool enable, int maxfac)
976{
977 m_normalize = enable;
978 m_normalizeMaxFactor = maxfac;
979}
980
987void cSoftHdAudio::SetCompression(bool enable, int maxfac)
988{
989 m_compression = enable;
990
991 m_compressionMaxFactor = maxfac;
992 if (!m_compressionFactor) {
993 m_compressionFactor = 1000;
994 }
997 }
998}
999
1006{
1007 m_stereoDescent = delta;
1008 SetVolume(m_volume); // update channel delta
1009}
1010
1017{
1018 m_passthrough = mask;
1019}
1020
1026{
1027 if (!m_initialized) {
1028 AlsaInit();
1029 m_initialized = true;
1030 }
1031}
1032
1037{
1038 LOGDEBUG2(L_SOUND, "audio: %s", __FUNCTION__);
1039
1040 if (m_initialized) {
1041 if (m_pAudioThread->Active())
1043 delete m_pAudioThread;
1044
1045 avfilter_graph_free(&m_pFilterGraph);
1046
1047 AlsaExit();
1048 }
1049 m_initialized = false;
1050}
1051
1052/******************************************************************************
1053 * A L S A
1054 *****************************************************************************/
1055
1060{
1061 int err;
1062 snd_pcm_state_t state;
1063
1064 err = snd_pcm_prepare(m_pAlsaPCMHandle);
1065 if (err < 0) {
1066 state = snd_pcm_state(m_pAlsaPCMHandle);
1067 LOGERROR("audio: %s: Can't recovery from xrun: %s pcm state: %s", __FUNCTION__,
1068 snd_strerror(err), snd_pcm_state_name(state));
1069 }
1070}
1071
1076{
1077 int err;
1078 snd_pcm_state_t state;
1079
1080 LOGDEBUG2(L_SOUND, "audio: %s", __FUNCTION__);
1081
1082 state = snd_pcm_state(m_pAlsaPCMHandle);
1083 if (state != SND_PCM_STATE_OPEN) {
1084 if ((err = snd_pcm_drop(m_pAlsaPCMHandle)) < 0)
1085 LOGERROR("audio: %s: snd_pcm_drop(): %s", __FUNCTION__, snd_strerror(err));
1086 // alsa crash, when in open state here ?
1087 if ((err = snd_pcm_prepare(m_pAlsaPCMHandle)) < 0)
1088 LOGERROR("audio: %s: snd_pcm_prepare(): %s", __FUNCTION__, snd_strerror(err));
1089 state = snd_pcm_state(m_pAlsaPCMHandle);
1090 LOGDEBUG2(L_SOUND, "audio: %s: pcm state %s", __FUNCTION__, snd_pcm_state_name(state));
1091 }
1092
1093 m_compressionFactor = 2000;
1096
1098 m_normalizeReady = 0;
1099
1100 for (int i = 0; i < NORMALIZE_MAX_INDEX; ++i)
1101 m_normalizeAverage[i] = 0U;
1102
1103 m_normalizeFactor = 1000;
1104}
1105
1106/******************************************************************************
1107 * Thread playback
1108 *****************************************************************************/
1109
1119{
1120 std::lock_guard<std::mutex> lock1(m_pauseMutex);
1121
1122 if (m_paused)
1123 return false;
1124
1125 // wait for space in kernel buffers
1126 int ret = snd_pcm_wait(m_pAlsaPCMHandle, 150);
1127 if (ret < 0) {
1128 ret = snd_pcm_recover(m_pAlsaPCMHandle, ret, 0);
1129 return false;
1130 } else if (ret == 0) {
1131 LOGERROR("audio: %s: snd_pcm_wait() timeout", __FUNCTION__);
1132 return false;
1133 }
1134
1135 std::lock_guard<std::mutex> lock2(m_mutex);
1136
1137 // query available space in alsa buffer
1138 int freeAlsaBufferFrameCount = snd_pcm_avail(m_pAlsaPCMHandle);
1139 if (freeAlsaBufferFrameCount < 0) {
1140 if (freeAlsaBufferFrameCount == -EAGAIN)
1141 return false;
1142
1143 if (snd_pcm_recover(m_pAlsaPCMHandle, freeAlsaBufferFrameCount, 0) < 0)
1144 LOGERROR("audio: %s: failed to recover from snd_pcm_avail: %s", __FUNCTION__, snd_strerror(freeAlsaBufferFrameCount));
1145
1146 LOGERROR("audio: %s: snd_pcm_avail(): %s", __FUNCTION__, snd_strerror(freeAlsaBufferFrameCount));
1147 return false;
1148 }
1149
1150 // calculcate amount of data to write
1151 const void *data;
1152 ssize_t inputBufferFillLevelBytes = m_pRingbuffer.GetReadPointer(&data);
1153
1154 if (inputBufferFillLevelBytes == 0)
1156
1157 int bytesToWrite = std::min(snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, freeAlsaBufferFrameCount), inputBufferFillLevelBytes);
1158
1159 if (bytesToWrite == 0)
1160 return false;
1161
1162 // muting pass-through AC-3, can produce disturbance
1163 if (m_volume == 0 || (m_softVolume && !m_passthrough)) {
1164 // FIXME: quick&dirty cast
1165 SoftAmplify((int16_t *) data, bytesToWrite);
1166 // FIXME: if not all are written, we double amplify them
1167 }
1168
1169 int framesToWrite = snd_pcm_bytes_to_frames(m_pAlsaPCMHandle, bytesToWrite);
1170
1171 int framesWritten;
1172 if (m_alsaUseMmap)
1173 framesWritten = snd_pcm_mmap_writei(m_pAlsaPCMHandle, data, framesToWrite);
1174 else
1175 framesWritten = snd_pcm_writei(m_pAlsaPCMHandle, data, framesToWrite);
1176
1177 m_pRingbuffer.ReadAdvance(snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, framesWritten));
1178
1179 if (framesWritten != framesToWrite) {
1180 if (framesWritten < 0) {
1181 if (framesWritten == -EAGAIN)
1182 return false;
1183
1184 LOGWARNING("audio: %s: writei failed: %s", __FUNCTION__, snd_strerror(framesWritten));
1185
1186 if (snd_pcm_recover(m_pAlsaPCMHandle, framesWritten, 0) < 0)
1187 LOGERROR("audio: %s: failed to recover from writei: %s", __FUNCTION__, snd_strerror(framesWritten));
1188
1189 return false;
1190 } else {
1191 LOGWARNING("audio: %s: not all frames written", __FUNCTION__);
1192 return false;
1193 }
1194 }
1195
1196 return true;
1197}
1198
1207char *cSoftHdAudio::OpenAlsaDevice(const char *device, int passthrough)
1208{
1209 int err;
1210 char tmp[80];
1211
1212 if (!device)
1213 return NULL;
1214
1215 LOGDEBUG2(L_SOUND, "audio: %s: try opening %sdevice '%s'", __FUNCTION__, passthrough ? "pass-through " : "", device);
1216
1217 if (passthrough && m_appendAES) {
1218 if (!(strchr(device, ':'))) {
1219 sprintf(tmp, "%s:AES0=%d,AES1=%d,AES2=0,AES3=%d",
1220 device,
1221 IEC958_AES0_NONAUDIO | IEC958_AES0_PRO_EMPHASIS_NONE,
1222 IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
1223 IEC958_AES3_CON_FS_48000);
1224 } else {
1225 sprintf(tmp, "%s,AES0=%d,AES1=%d,AES2=0,AES3=%d",
1226 device,
1227 IEC958_AES0_NONAUDIO | IEC958_AES0_PRO_EMPHASIS_NONE,
1228 IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
1229 IEC958_AES3_CON_FS_48000);
1230 }
1231 LOGDEBUG2(L_SOUND, "audio: %s: auto append AES: %s -> %s", __FUNCTION__, device, tmp);
1232 } else {
1233 sprintf(tmp, "%s", device);
1234 }
1235
1236 // open none blocking; if device is already used, we don't want wait
1237 if ((err = snd_pcm_open(&m_pAlsaPCMHandle, tmp, SND_PCM_STREAM_PLAYBACK,
1238 SND_PCM_NONBLOCK)) < 0) {
1239
1240 LOGWARNING("audio: %s: could not open device '%s' error: %s", __FUNCTION__, device, snd_strerror(err));
1241 return NULL;
1242 }
1243
1244 LOGDEBUG2(L_SOUND, "audio: %s: opened %sdevice '%s'", __FUNCTION__, passthrough ? "pass-through " : "", device);
1245
1246 return (char *)device;
1247}
1248
1259char *cSoftHdAudio::FindAlsaDevice(const char *devname, const char *hint, int passthrough)
1260{
1261 char **hints;
1262 int err;
1263 char **n;
1264 char *name;
1265
1266 err = snd_device_name_hint(-1, devname, (void ***)&hints);
1267 if (err != 0) {
1268 LOGWARNING("audio: %s: Cannot get device names for %s!", __FUNCTION__, hint);
1269 return NULL;
1270 }
1271
1272 n = hints;
1273 while (*n != NULL) {
1274 name = snd_device_name_get_hint(*n, "NAME");
1275
1276 if (name && strstr(name, hint)) {
1277 if (OpenAlsaDevice(name, passthrough)) {
1278 snd_device_name_free_hint((void **)hints);
1279 return name;
1280 }
1281 }
1282
1283 if (name)
1284 free(name);
1285 n++;
1286 }
1287
1288 snd_device_name_free_hint((void **)hints);
1289 return NULL;
1290}
1291
1296{
1297 char *device = NULL;
1298 bool freeDevice = false; // track if device needs to be freed
1299 int err;
1300 LOGDEBUG2(L_SOUND, "audio: %s: passthrough %d", __FUNCTION__, m_passthrough);
1301
1302 // try user set device
1303 if (m_passthrough)
1305
1306 if (!device && m_passthrough)
1307 device = OpenAlsaDevice(getenv("ALSA_PASSTHROUGH_DEVICE"), m_passthrough);
1308
1309 if (!device)
1311
1312 if (!device)
1313 device = OpenAlsaDevice(getenv("ALSA_DEVICE"), m_passthrough);
1314
1315 // walkthrough hdmi: devices
1316 if (!device) {
1317 LOGDEBUG2(L_SOUND, "audio: %s: Try hdmi: devices...", __FUNCTION__);
1318 device = FindAlsaDevice("pcm", "hdmi:", m_passthrough);
1319 freeDevice = (device != NULL); // FindAlsaDevice allocates memory
1320 }
1321
1322 // walkthrough default: devices
1323 if (!device) {
1324 LOGDEBUG2(L_SOUND, "audio: %s: Try default: devices...", __FUNCTION__);
1325 device = FindAlsaDevice("pcm", "default:", m_passthrough);
1326 freeDevice = (device != NULL); // FindAlsaDevice allocates memory
1327 }
1328
1329 // try default device
1330 if (!device) {
1331 LOGDEBUG2(L_SOUND, "audio: %s: Try default device...", __FUNCTION__);
1332 device = OpenAlsaDevice("default", m_passthrough);
1333 }
1334
1335 // use null device
1336 if (!device) {
1337 LOGDEBUG2(L_SOUND, "audio: %s: Try null device...", __FUNCTION__);
1338 device = OpenAlsaDevice("null", m_passthrough);
1339 }
1340
1341 if (!device)
1342 LOGFATAL("audio: %s: could not open any device, abort!", __FUNCTION__);
1343
1344 if (!strcmp(device, "null"))
1345 LOGWARNING("audio: %s: using %sdevice '%s'", __FUNCTION__,
1346 m_passthrough ? "pass-through " : "", device);
1347 else
1348 LOGINFO("audio: using %sdevice '%s'",
1349 m_passthrough ? "pass-through " : "", device);
1350
1351 // Free device string if it was allocated by FindAlsaDevice
1352 if (freeDevice)
1353 free(device);
1354
1355 if ((err = snd_pcm_nonblock(m_pAlsaPCMHandle, 0)) < 0) {
1356 LOGERROR("audio: %s: can't set block mode: %s", __FUNCTION__, snd_strerror(err));
1357 }
1358}
1359
1360/******************************************************************************
1361 * Alsa Mixer
1362 *****************************************************************************/
1363
1368{
1369 const char *device;
1370 const char *channel;
1371 snd_mixer_t *alsaMixer;
1372 snd_mixer_elem_t *alsaMixerElem;
1373 long alsaMixerElemMin;
1374 long alsaMixerElemMax;
1375
1376 if (!(device = m_pMixerDevice)) {
1377 if (!(device = getenv("ALSA_MIXER"))) {
1378 device = "default";
1379 }
1380 }
1381 if (!(channel = m_pMixerChannel)) {
1382 if (!(channel = getenv("ALSA_MIXER_CHANNEL"))) {
1383 channel = "PCM";
1384 }
1385 }
1386 LOGDEBUG2(L_SOUND, "audio: %s: mixer %s - %s open", __FUNCTION__, device, channel);
1387 snd_mixer_open(&alsaMixer, 0);
1388 if (alsaMixer && snd_mixer_attach(alsaMixer, device) >= 0
1389 && snd_mixer_selem_register(alsaMixer, NULL, NULL) >= 0
1390 && snd_mixer_load(alsaMixer) >= 0) {
1391
1392 const char *const alsaMixerElem_name = channel;
1393
1394 alsaMixerElem = snd_mixer_first_elem(alsaMixer);
1395 while (alsaMixerElem) {
1396 const char *name;
1397
1398 name = snd_mixer_selem_get_name(alsaMixerElem);
1399 if (!strcasecmp(name, alsaMixerElem_name)) {
1400 snd_mixer_selem_get_playback_volume_range(alsaMixerElem, &alsaMixerElemMin, &alsaMixerElemMax);
1401 m_alsaRatio = 1000 * (alsaMixerElemMax - alsaMixerElemMin);
1402 LOGDEBUG2(L_SOUND, "audio: %s: %s mixer found %ld - %ld ratio %d", __FUNCTION__, channel, alsaMixerElemMin, alsaMixerElemMax, m_alsaRatio);
1403 break;
1404 }
1405
1406 alsaMixerElem = snd_mixer_elem_next(alsaMixerElem);
1407 }
1408
1409 m_pAlsaMixer = alsaMixer;
1410 m_pAlsaMixerElem = alsaMixerElem;
1411 } else {
1412 LOGERROR("audio: %s: can't open mixer '%s'", __FUNCTION__, device);
1413 }
1414}
1415
1422{
1423 int v;
1425 v = (volume * m_alsaRatio) / (1000 * 1000);
1426 snd_mixer_selem_set_playback_volume(m_pAlsaMixerElem, SND_MIXER_SCHN_FRONT_LEFT, v);
1427 snd_mixer_selem_set_playback_volume(m_pAlsaMixerElem, SND_MIXER_SCHN_FRONT_RIGHT, v);
1428 }
1429}
1430
1444int cSoftHdAudio::AlsaSetup(int channels, int sample_rate, int passthrough)
1445{
1446 snd_pcm_hw_params_t *hwparams;
1447 snd_pcm_state_t state;
1448 int err;
1449 unsigned bufferTimeUs = 100'000;
1450
1451 if (m_pAudioThread) {
1453 delete m_pAudioThread;
1454
1456 }
1457
1458 m_downmix = 0;
1459
1460 state = snd_pcm_state(m_pAlsaPCMHandle);
1461 if (state == SND_PCM_STATE_XRUN) {
1462 LOGERROR("audio: %s: recover from xrun pcm state: %s", __FUNCTION__, snd_pcm_state_name(state));
1463 XrunRecovery();
1464 }
1465
1466 snd_pcm_hw_params_alloca(&hwparams);
1467 if ((err = snd_pcm_hw_params_any(m_pAlsaPCMHandle, hwparams)) < 0) {
1468 LOGERROR("audio: %s: Read HW config failed! %s", __FUNCTION__, snd_strerror(err));
1469 return -1;
1470 }
1471
1472 if (!snd_pcm_hw_params_test_access(m_pAlsaPCMHandle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
1473 m_alsaUseMmap = true;
1474 }
1475
1476 m_hwSampleRate = sample_rate;
1477 if ((err = snd_pcm_hw_params_set_rate_near(m_pAlsaPCMHandle, hwparams, &m_hwSampleRate, 0) < 0)) {
1478 LOGERROR("audio: %s: SampleRate %d not supported! %s", __FUNCTION__, sample_rate, snd_strerror(err));
1479 return -1;
1480 }
1481 if ((int)m_hwSampleRate != sample_rate) {
1482 LOGDEBUG2(L_SOUND, "audio: %s: sample_rate %d m_hwSampleRate %d", __FUNCTION__, sample_rate, m_hwSampleRate);
1483 }
1484
1485 m_hwNumChannels = channels;
1486 if ((err = snd_pcm_hw_params_set_channels_near(m_pAlsaPCMHandle, hwparams, &m_hwNumChannels)) < 0) {
1487 LOGWARNING("audio: %s: %d channels not supported! %s", __FUNCTION__, m_hwNumChannels, snd_strerror(err));
1488 }
1489 if ((int)m_hwNumChannels != channels && !passthrough) {
1490 m_downmix = 1;
1491 }
1492
1493 if ((err = snd_pcm_hw_params_set_buffer_time_near(m_pAlsaPCMHandle, hwparams, &bufferTimeUs, NULL)) < 0) {
1494 LOGWARNING("audio: %s: bufferTime %d not supported! %s", __FUNCTION__, bufferTimeUs, snd_strerror(err));
1495 }
1496
1497 m_alsaCanPause = snd_pcm_hw_params_can_pause(hwparams);
1498
1499/* err = snd_pcm_hw_params_test_format(m_pAlsaPCMHandle, hwparams, SND_PCM_FORMAT_S16);
1500 if (err < 0) // err == 0 if is supported
1501 LOGERROR("audio: %s: SND_PCM_FORMAT_S16 not supported! %s", __FUNCTION__,
1502 snd_strerror(err));
1503*/
1504 if ((err = snd_pcm_set_params(m_pAlsaPCMHandle, SND_PCM_FORMAT_S16,
1505 m_alsaUseMmap ? SND_PCM_ACCESS_MMAP_INTERLEAVED :
1506 SND_PCM_ACCESS_RW_INTERLEAVED, m_hwNumChannels, m_hwSampleRate, 1, bufferTimeUs))) {
1507
1508 state = snd_pcm_state(m_pAlsaPCMHandle);
1509 LOGERROR("audio: %s: set params error: %s\n"
1510 " Channels %d SampleRate %d\n"
1511 " HWChannels %d HWSampleRate %d SampleFormat %s\n"
1512 " Supports pause: %s mmap: %s\n"
1513 " AlsaBufferTime %dms pcm state: %s",
1514 __FUNCTION__,
1515 snd_strerror(err), channels, sample_rate, m_hwNumChannels,
1516 m_hwSampleRate, snd_pcm_format_name(SND_PCM_FORMAT_S16),
1517 m_alsaCanPause ? "yes" : "no", m_alsaUseMmap ? "yes" : "no",
1518 bufferTimeUs / 1000, snd_pcm_state_name(state));
1519 return -1;
1520 }
1521
1522 LOGINFO("audio: alsa set up:\n"
1523 " Channels %d SampleRate %d%s\n"
1524 " HWChannels %d HWSampleRate %d SampleFormat %s\n"
1525 " Supports pause: %s mmap: %s\n"
1526 " AlsaBufferTime %dms",
1527 channels, sample_rate, passthrough ? " -> passthrough" : "",
1529 snd_pcm_format_name(SND_PCM_FORMAT_S16),
1530 m_alsaCanPause ? "yes" : "no", m_alsaUseMmap ? "yes" : "no",
1531 bufferTimeUs / 1000);
1532
1533 m_pAudioThread = new cAudioThread(this);
1534
1535 return 0;
1536}
1537
1541static void AlsaNoopCallback( __attribute__ ((unused))
1542 const char *file, __attribute__ ((unused))
1543 int line, __attribute__ ((unused))
1544 const char *function, __attribute__ ((unused))
1545 int err, __attribute__ ((unused))
1546 const char *fmt, ...)
1547{
1548}
1549
1554{
1555#ifdef ALSA_DEBUG
1556 (void)AlsaNoopCallback;
1557#else
1558 // disable display of alsa error messages
1559 snd_lib_error_set_handler(AlsaNoopCallback);
1560#endif
1561
1563 AlsaInitMixer();
1564}
1565
1570{
1571 if (m_pAlsaPCMHandle) {
1572 snd_pcm_close(m_pAlsaPCMHandle);
1573 m_pAlsaPCMHandle = NULL;
1574 }
1575 if (m_pAlsaMixer) {
1576 snd_mixer_close(m_pAlsaMixer);
1577 m_pAlsaMixer = NULL;
1578 m_pAlsaMixerElem = NULL;
1579 }
1580}
1581
1586{
1587 for (Event event : m_eventQueue)
1589
1590 m_eventQueue.clear();
1591}
static void ReorderAudioFrame(uint16_t *buf, int size, int channels)
Reorder audio frame.
Definition: audio.cpp:124
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:1541
Audio and alsa module header file.
#define NORMALIZE_MAX_INDEX
number of average values
Definition: audio.h:38
virtual void OnEventReceived(const Event &)=0
void Stop(void)
Definition: threads.cpp:153
void LazyInit(void)
Initialize audio output module.
Definition: audio.cpp:1025
cSoftHdAudio(cSoftHdDevice *)
cSoftHdAudio constructor
Definition: audio.cpp:66
char * OpenAlsaDevice(const char *, int)
Open alsa device.
Definition: audio.cpp:1207
char * FindAlsaDevice(const char *, const char *, int)
Find alsa device giving some search hints.
Definition: audio.cpp:1259
bool m_appendAES
flag ato utomatic append AES
Definition: audio.h:113
void XrunRecovery(void)
xrun recovery
Definition: audio.cpp:1059
void Filter(AVFrame *, AVCodecContext *)
Send audio frame to filter and enqueue it.
Definition: audio.cpp:771
cSoftHdRingbuffer m_pRingbuffer
sample ring buffer
Definition: audio.h:155
int AlsaSetup(int channels, int sample_rate, int passthrough)
Setup alsa audio for requested format.
Definition: audio.cpp:1444
int Setup(AVCodecContext *, int, int, int)
Setup alsa.
Definition: audio.cpp:682
void Enqueue(uint16_t *, int, AVFrame *)
Send audio data to ringbuffer.
Definition: audio.cpp:657
int m_compressionMaxFactor
max. compression factor
Definition: audio.h:129
cSoftHdDevice * m_pDevice
pointer to device
Definition: audio.h:87
const char * m_pPCMDevice
PCM device name.
Definition: audio.h:111
int m_volume
current volume (0 .. 1000)
Definition: audio.h:134
cAudioThread * m_pAudioThread
pointer to audio thread
Definition: audio.h:92
virtual ~cSoftHdAudio(void)
cSoftHdAudio denstructor
Definition: audio.cpp:105
void SetStereoDescent(int)
Set stereo loudness descent.
Definition: audio.cpp:1005
int64_t GetHardwareOutputPtsMs(void)
Get the hardware output PTS in milliseconds.
Definition: audio.cpp:884
std::mutex m_pauseMutex
mutex for a safe thread pausing
Definition: audio.h:101
int64_t GetHardwareOutputPtsTimebaseUnits(void)
Get the hardware output PTS in timebase units.
Definition: audio.cpp:908
bool HasPts(void)
Definition: audio.h:65
AVFilterContext * m_pBuffersinkCtx
Definition: audio.h:149
void SetVolume(int)
Set mixer volume (0-1000)
Definition: audio.cpp:921
AVFilterContext * m_pBuffersrcCtx
Definition: audio.h:148
bool m_alsaCanPause
hw supports pause
Definition: audio.h:170
AVFilterGraph * m_pFilterGraph
Definition: audio.h:147
int m_passthrough
passthrough mask
Definition: audio.h:110
const int m_bytesPerSample
number of bytes per sample
Definition: audio.h:96
const char * m_pMixerChannel
mixer channel name
Definition: audio.h:142
unsigned int m_hwSampleRate
hardware sample rate in Hz
Definition: audio.h:97
int64_t PtsToMs(int64_t pts)
Definition: audio.h:181
IEventReceiver * m_pEventReceiver
pointer to event receiver
Definition: audio.h:89
void ProcessEvents(void)
Process queued events and forward to event receiver.
Definition: audio.cpp:1585
int MsToFrames(int milliseconds)
Definition: audio.h:183
std::vector< Event > m_eventQueue
event queue for incoming events
Definition: audio.h:102
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
Definition: audio.cpp:599
AVRational * m_pTimebase
pointer to AVCodecContext pkts_timebase
Definition: audio.h:99
bool m_compression
flag to use compress volume
Definition: audio.h:127
int64_t GetOutputPtsMs(void)
Get the output PTS of the ringbuffer.
Definition: audio.cpp:863
bool m_normalize
flag to use volume normalize
Definition: audio.h:116
AVFrame * FilterGetFrame(void)
Get frame from filter sink.
Definition: audio.cpp:705
int m_filterChanged
filter has changed
Definition: audio.h:145
void AlsaExit(void)
Cleanup alsa audio output module.
Definition: audio.cpp:1569
void SetCompression(bool, int)
Set volume compression parameters.
Definition: audio.cpp:987
snd_mixer_elem_t * m_pAlsaMixerElem
alsa mixer element
Definition: audio.h:167
void AlsaInit(void)
Initialize alsa audio output module.
Definition: audio.cpp:1553
void Compress(uint16_t *, int)
Compress audio.
Definition: audio.cpp:262
int64_t m_inputPts
pts clock (last pts in ringbuffer)
Definition: audio.h:106
int m_normalizeFactor
current normalize factor
Definition: audio.h:122
void AlsaSetVolume(int)
Set alsa mixer volume (0-1000)
Definition: audio.cpp:1421
int64_t GetOutputPtsMsInternal(void)
Definition: audio.cpp:870
cSoftHdConfig * m_pConfig
pointer to config
Definition: audio.h:88
void Exit(void)
Cleanup audio output module.
Definition: audio.cpp:1036
int m_amplifier
software volume amplify factor
Definition: audio.h:132
int GetUsedBytes(void)
Get used bytes in audio ringbuffer.
Definition: audio.cpp:845
void SetPaused(bool)
Set audio playback paused state.
Definition: audio.cpp:944
void AlsaInitMixer(void)
Initialize alsa mixer.
Definition: audio.cpp:1367
const char * m_pPassthroughDevice
passthrough device name
Definition: audio.h:112
void SetPassthrough(int)
Set audio passthrough mask.
Definition: audio.cpp:1016
int m_normalizeMaxFactor
max. normalize factor
Definition: audio.h:124
bool m_alsaUseMmap
use mmap
Definition: audio.h:169
void Normalize(uint16_t *, int)
Normalize audio.
Definition: audio.cpp:181
int m_compressionFactor
current compression factor
Definition: audio.h:128
int64_t MsToPts(int64_t ptsMs)
Definition: audio.h:182
const int m_normalizeMinFactor
min. normalize factor
Definition: audio.h:123
const int m_normalizeSamples
number of normalize samples
Definition: audio.h:117
int m_filterReady
filter is ready
Definition: audio.h:146
void SetEq(int[18], int)
Set equalizer bands.
Definition: audio.cpp:348
int m_normalizeReady
index normalize counter
Definition: audio.h:121
const char * m_pMixerDevice
mixer device name (not used)
Definition: audio.h:141
uint32_t m_normalizeAverage[NORMALIZE_MAX_INDEX]
average of n last normalize sample blocks
Definition: audio.h:119
bool CyclicCall(void)
Cyclic audio playback call.
Definition: audio.cpp:1118
void AlsaInitPCMDevice(void)
Search for an alsa pcm device and open it.
Definition: audio.cpp:1295
unsigned int m_hwNumChannels
number of hardware channels
Definition: audio.h:98
bool m_initialized
class initialized
Definition: audio.h:95
int FramesToMs(int frames)
Definition: audio.h:184
int m_stereoDescent
volume descent for stereo
Definition: audio.h:133
std::mutex m_mutex
mutex for thread safety
Definition: audio.h:100
int m_alsaRatio
internal -> mixer ratio * 1000
Definition: audio.h:168
int GetFreeBytes(void)
Get free bytes in audio ringbuffer.
Definition: audio.cpp:835
void EnqueueFrame(AVFrame *)
Place samples in audio output queue.
Definition: audio.cpp:627
void SoftAmplify(int16_t *, int)
Software amplifier.
Definition: audio.cpp:319
void FlushAlsaBuffers(void)
Flush alsa buffers.
Definition: audio.cpp:1075
void SetNormalize(bool, int)
Set normalize volume parameters.
Definition: audio.cpp:975
snd_pcm_t * m_pAlsaPCMHandle
alsa pcm handle
Definition: audio.h:165
int m_downmix
set stereo downmix
Definition: audio.h:104
int m_useEqualizer
flag to use equalizer
Definition: audio.h:137
float m_equalizerBand[18]
equalizer band
Definition: audio.h:138
snd_mixer_t * m_pAlsaMixer
alsa mixer handle
Definition: audio.h:166
int m_normalizeIndex
index into normalize average table
Definition: audio.h:120
int m_normalizeCounter
normalize sample counter
Definition: audio.h:118
std::atomic< bool > m_paused
audio is paused
Definition: audio.h:107
int CheckForFilterReady(AVCodecContext *)
Check if the filter has changed and is ready, init the filter if needed.
Definition: audio.cpp:738
int InitFilter(AVCodecContext *)
Init filter.
Definition: audio.cpp:424
void FlushBuffers(void)
Flush audio buffers.
Definition: audio.cpp:815
bool m_softVolume
flag to use soft volume
Definition: audio.h:109
bool ConfigAudioNormalize
config use normalize volume
Definition: config.h:48
int ConfigAudioAutoAES
config automatic AES handling
Definition: config.h:53
int ConfigAudioStereoDescent
config reduce stereo loudness
Definition: config.h:52
bool ConfigAudioCompression
config use volume compression
Definition: config.h:50
bool ConfigAudioPassthroughState
flag audio-passthrough on/off
Definition: config.h:45
const char * ConfigAudioPCMDevice
audio PCM device
Definition: config.h:75
bool ConfigAudioSoftvol
config use software volume
Definition: config.h:47
int ConfigAudioEqBand[18]
config equalizer filter bands
Definition: config.h:55
int ConfigAudioMaxCompression
config max volume compression
Definition: config.h:51
const char * ConfigAudioMixerChannel
audio mixer channel name
Definition: config.h:77
int ConfigAudioEq
config equalizer filter
Definition: config.h:54
int ConfigAudioPassthroughMask
config audio pass-through mask
Definition: config.h:44
bool ConfigAudioDownmix
config ffmpeg audio downmix
Definition: config.h:46
const char * ConfigAudioPassthroughDevice
audio passthrough device
Definition: config.h:76
int ConfigAudioMaxNormalize
config max normalize factor
Definition: config.h:49
cSoftHdConfig * Config(void)
Definition: softhddevice.h:174
size_t UsedBytes(void)
Get used bytes in ring buffer.
Definition: ringbuffer.cpp:301
size_t FreeBytes(void)
Get free bytes in ring buffer.
Definition: ringbuffer.cpp:291
size_t ReadAdvance(size_t)
Advance read pointer in ring buffer.
Definition: ringbuffer.cpp:188
size_t GetReadPointer(const void **)
Get read pointer and used bytes at this position of ring buffer.
Definition: ringbuffer.cpp:266
size_t Write(const void *, size_t)
Write to a ring buffer.
Definition: ringbuffer.cpp:120
void Reset(void)
Reset ring buffer pointers.
Definition: ringbuffer.cpp:69
Audio decoder header file.
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, PipEvent > Event
Definition: event.h:72
@ AUDIO
Definition: event.h:26
Atomic wrapper macros function header file.
Logger class header file.
#define LOGDEBUG2
Definition: logger.h:50
#define LOGERROR
Definition: logger.h:46
#define L_SOUND
Definition: logger.h:60
#define L_AV_SYNC
Definition: logger.h:59
#define LOGWARNING
Definition: logger.h:47
#define LOGINFO
Definition: logger.h:48
#define LOGFATAL
Logger macros.
Definition: logger.h:45
Misc function header file.
#define AV_NOPTS_VALUE
Definition: misc.h:35
static const char * Timestamp2String(int64_t ts, uint8_t divisor)
Workaround for av_err2str() not working with C++.
Definition: misc.h:81
Ringbuffer class header file.
Thread classes header file.
Rendering class header file.
Videostream class header file.