vdr-plugin-softhddevice-drm-gles 1.6.8-daba64b
audio.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
17#include <chrono>
18#include <cmath>
19#include <cstdint>
20#include <mutex>
21#include <string>
22#include <sstream>
23#include <vector>
24
25extern "C" {
26#include <libavcodec/avcodec.h>
27#include <libavfilter/avfilter.h>
28#include <libavfilter/buffersink.h>
29#include <libavfilter/buffersrc.h>
30#include <libavutil/channel_layout.h>
31#include <libavutil/opt.h>
32}
33
34#include <vdr/thread.h>
35
36#include "alsadevice.h"
37#include "audio.h"
38#include "audioprocessor.h"
39#include "codec_audio.h"
40#include "config.h"
41#include "filllevel.h"
42#include "logger.h"
43#include "misc.h"
44#include "pidcontroller.h"
45#include "ringbuffer.h"
46#include "softhddevice.h"
47#include "statemachine.h"
48
53 : cThread("softhd audio"),
54 m_pDevice(device),
55 m_pConfig(m_pDevice->Config()),
56 m_alsa(m_pConfig),
57 m_softVolume(m_pConfig->ConfigAudioSoftvol),
58 m_audioProcessor(BYTES_PER_SAMPLE),
59 m_pMixerChannel(m_pConfig->ConfigAudioMixerChannel)
60{
65}
66
67/******************************************************************************
68 * Audio Filter
69 *****************************************************************************/
70
80static std::vector<std::string> GetFFmpegChannelLayoutAsArray(const AVChannelLayout &layout)
81{
82 std::vector<std::string> names;
83 char buf[16];
84
85 for (int i = 0; i < layout.nb_channels; i++) {
87 int ret = av_channel_name(buf, sizeof(buf), ch);
88 if (ret < 0)
89 continue;
90 names.push_back(std::string(buf));
91 }
92 return names;
93}
94
105static bool LayoutsMatch(const std::vector<std::string> &ff, const std::vector<std::string> &alsa)
106{
107 if (ff.size() != alsa.size())
108 return false;
109
110 for (size_t i = 0; i < ff.size(); i++) {
111 if (ff[i] != alsa[i])
112 return false;
113 }
114
115 return true;
116}
117
127static bool LayoutIsValid(const std::vector<std::string> &channelLayout)
128{
129 return std::find(channelLayout.begin(), channelLayout.end(), "NA") == channelLayout.end();
130}
131
142{
145
146 if (ff.size() != alsa.size()) {
147 LOGWARNING("audio: %s: Skip channelmap filter, FFmpeg and Alsa channel count differs: FFmpeg %zu ALSA %zu", __FUNCTION__, ff.size(), alsa.size());
148 return "";
149 }
150
151 std::string ffString;
152 for (size_t i = 0; i < ff.size(); i++) {
153 ffString += ff[i];
154 if (i < ff.size() - 1)
155 ffString += " ";
156 }
157
158 std::string alsaString;
159 for (size_t i = 0; i < alsa.size(); i++) {
160 alsaString += alsa[i];
161 if (i < alsa.size() - 1)
162 alsaString += " ";
163 }
164
165 if (!LayoutIsValid(alsa)) {
166 LOGDEBUG2(L_SOUND, "audio: %s: Skip channelmap filter, alsa channel layout isn't valid: %s", __FUNCTION__, alsaString.c_str());
167 return "";
168 }
169
170 if (LayoutsMatch(ff, alsa)) {
171 LOGDEBUG2(L_SOUND, "audio: %s: Skip channelmap filter, FFmpeg and Alsa channel layouts match: %s", __FUNCTION__, ffString.c_str());
172 return "";
173 }
174
175 std::stringstream ss;
176 for (size_t i = 0; i < ff.size(); i++) {
177 if (i != 0)
178 ss << "|";
179 ss << ff[i] << "-" << alsa[i];
180 }
181
182 LOGDEBUG2(L_SOUND, "audio: %s: FFmpeg Channel Layout: %s", __FUNCTION__, ffString.c_str());
183 LOGDEBUG2(L_SOUND, "audio: %s: Alsa Channel Layout : %s", __FUNCTION__, alsaString.c_str());
184 LOGDEBUG2(L_SOUND, "audio: %s: Layouts don't match, map FFmpeg to Alsa: %s", __FUNCTION__, ss.str().c_str());
185
186 return ss.str();
187}
188
204{
205 const AVFilter *abuffer;
207 const AVFilter *channelmap;
208 const AVFilter *eq;
209 const AVFilter *aformat;
210 const AVFilter *abuffersink;
211 char channelLayout[64];
212 char optionsStr[1024];
213 int err, i, numFilter = 0;
214
215 // Before filter init setup HW parameter
216 err = Setup(audioCtx->pkt_timebase, audioCtx->sample_rate, audioCtx->ch_layout.nb_channels, false);
217 if (err < 0) {
218 LOGERROR("audio: %s: failed!", __FUNCTION__);
219 return err;
220 }
221
222#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7,16,100)
224#endif
225
227 LOGERROR("audio: %s: Unable to create filter graph.", __FUNCTION__);
228 return -1;
229 }
230
231 // input buffer
232 if (!(abuffer = avfilter_get_by_name("abuffer"))) {
233 LOGWARNING("audio: %s: Could not find the abuffer filter.", __FUNCTION__);
235 return -1;
236 }
238 LOGWARNING("audio: %s: Could not allocate the m_pBuffersrcCtx instance.", __FUNCTION__);
240 return -1;
241 }
242
244
245 LOGDEBUG2(L_SOUND, "audio: %s: IN channelLayout %s sample_fmt %s sample_rate %d channels %d", __FUNCTION__,
246 channelLayout, av_get_sample_fmt_name(audioCtx->sample_fmt), audioCtx->sample_rate, audioCtx->ch_layout.nb_channels);
247
250 av_opt_set_q (m_pBuffersrcCtx, "time_base", (AVRational){ 1, audioCtx->sample_rate }, AV_OPT_SEARCH_CHILDREN);
251 av_opt_set_int(m_pBuffersrcCtx, "sample_rate", audioCtx->sample_rate, AV_OPT_SEARCH_CHILDREN);
252// av_opt_set_int(m_pBuffersrcCtx, "channel_counts", audioCtx->channels, AV_OPT_SEARCH_CHILDREN);
253
254 // initialize the filter with NULL options, set all options above.
256 LOGWARNING("audio: %s: Could not initialize the abuffer filter.", __FUNCTION__);
258 return -1;
259 }
260
261 // channelmap
262 //
263 // Map FFmpeg channel layout to Alsa channel layout.
264 // Depending on the hardware, e.g. FC and LFE have to be swapped.
265 // This is the case for HDMI on RPI4, so we need to do the following:
266 // FL-FL|FR-FR|FC-LFE|LFE-FC|BL-BL|BR-BR
267 //
268 // The channel mapping is skipped, if
269 // - a stereo downmix is forced (downmix will be done later in aformat filter)
270 // - channel count differs, aformat will handle downmix later
271 if (!(m_alsa.GetDownmix() && m_alsa.GetHwNumChannels() == 2)) {
272 std::string channelMapString;
274
275 if (!channelMapString.empty()) {
276 if (!(channelmap = avfilter_get_by_name("channelmap"))) {
277 LOGWARNING("audio: %s: Could not find the channelmap filter.", __FUNCTION__);
278 return -1;
279 }
281 LOGWARNING("audio: %s: Could not allocate the channelmap instance.", __FUNCTION__);
282 return -1;
283 }
284 snprintf(optionsStr, sizeof(optionsStr),"map=%s", channelMapString.c_str());
286 LOGWARNING("audio: %s: Could not initialize the channelmap filter \"%s\"", __FUNCTION__, optionsStr);
288 return -1;
289 }
290 numFilter++;
291 }
292 }
293
294 // superequalizer
295 if (m_useEqualizer) {
296 if (!(eq = avfilter_get_by_name("superequalizer"))) {
297 LOGWARNING("audio: %s: Could not find the superequalizer filter.", __FUNCTION__);
299 return -1;
300 }
301 if (!(pFilterCtx[numFilter] = avfilter_graph_alloc_filter(m_pFilterGraph, eq, "superequalizer"))) {
302 LOGWARNING("audio: %s: Could not allocate the superequalizer instance.", __FUNCTION__);
304 return -1;
305 }
306
308 snprintf(optionsStr, sizeof(optionsStr), "%s", equalizerOptions.c_str());
309
311 LOGWARNING("audio: %s: Could not initialize the superequalizer filter.", __FUNCTION__);
313 return -1;
314 }
315 numFilter++;
316 }
317
318 // aformat
320 if (m_alsa.GetDownmix() && m_alsa.GetHwNumChannels() == 2) {
321 // explicit stereo downmix
323 } else {
324 if (av_channel_layout_copy(&channel_layout, &audioCtx->ch_layout) < 0) {
325 LOGWARNING("audio: %s: Could not copy channel layout", __FUNCTION__);
326 return -1;
327 }
328
329 // clamp channels if the hardware doesn't support them
330 if (channel_layout.nb_channels > (int)m_alsa.GetHwNumChannels()) {
331 LOGDEBUG2(L_SOUND, "audio: %s: clamp channels from %d -> %d", __FUNCTION__, channel_layout.nb_channels, m_alsa.GetHwNumChannels());
334 }
335 }
338
339 LOGDEBUG2(L_SOUND, "audio: %s: OUT downmix %d hwNumChannels %d hwSampleRate %d channelLayout %s bytes_per_sample %d",
341
342 if (!(aformat = avfilter_get_by_name("aformat"))) {
343 LOGWARNING("audio: %s: Could not find the aformat filter.", __FUNCTION__);
345 return -1;
346 }
348 LOGWARNING("audio: %s: Could not allocate the aformat instance.", __FUNCTION__);
350 return -1;
351 }
353 "sample_fmts=%s:sample_rates=%d:channel_layouts=%s",
356 LOGWARNING("audio: %s: Could not initialize the aformat filter.", __FUNCTION__);
358 return -1;
359 }
360 numFilter++;
361
362 // abuffersink
363 if (!(abuffersink = avfilter_get_by_name("abuffersink"))) {
364 LOGWARNING("audio: %s: Could not find the abuffersink filter.", __FUNCTION__);
366 return -1;
367 }
369 LOGWARNING("audio: %s: Could not allocate the abuffersink instance.", __FUNCTION__);
371 return -1;
372 }
374 LOGWARNING("audio: %s: Could not initialize the abuffersink instance.", __FUNCTION__);
376 return -1;
377 }
378 numFilter++;
379
380 // Connect the filters
381 for (i = 0; i < numFilter; i++) {
382 if (i == 0) {
384 } else {
385 err = avfilter_link(pFilterCtx[i - 1], 0, pFilterCtx[i], 0);
386 }
387 }
388 if (err < 0) {
389 LOGWARNING("audio: %s: Error connecting audio filters", __FUNCTION__);
391 return -1;
392 }
393
394 // Configure the graph.
396 LOGWARNING("audio: %s: Error configuring the audio filter graph", __FUNCTION__);
398 return -1;
399 }
400
402 m_filterChanged = 0;
403 m_filterReady = 1;
404
405 return 0;
406}
407
408/******************************************************************************
409 * Audio stream handling
410 *****************************************************************************/
411
421{
422 std::lock_guard<std::mutex> lock(m_mutex);
423
424 if (!HasInputPts())
425 return;
426
430
431 dropBytes = std::min(dropBytes, (int)m_pRingbuffer.UsedBytes());
432
433 if (dropBytes > 0) {
434 LOGDEBUG2(L_AV_SYNC, "audio: %s: dropping %dms audio samples to start in sync with the video (output PTS %s -> %s)",
436 dropMs,
439
444 }
445}
446
453{
454 if (!frame)
455 return;
456
457 uint16_t *buffer;
458
459 int byteCount = frame->nb_samples * frame->ch_layout.nb_channels * BYTES_PER_SAMPLE;
460 buffer = (uint16_t *)frame->data[0];
461
462 if (m_useCompressor) // in place operation
464
465 if (m_useNormalizer) // in place operation
467
468 Enqueue((uint16_t *)buffer, byteCount, frame->pts);
469
470 av_frame_free(&frame);
471}
472
479{
480 if (size == m_spdifBurstSize)
481 return;
482
483 LOGDEBUG2(L_SOUND, "audio: %s: spdif burst size changed %d -> %d, rebuild pause burst", __FUNCTION__, m_spdifBurstSize, size);
484
485 m_spdifBurstSize = size;
486 m_pauseBurst.resize(size / 2);
487 uint16_t *spdif = m_pauseBurst.data();
488
489 constexpr int IEC61937_PREAMBLE1 = 0xF872;
490 constexpr int IEC61937_PREAMBLE2 = 0x4E1F;
491 constexpr int IEC61937_NULL = 0x00;
492
496 spdif[3] = 0;
497
498 memset(m_pauseBurst.data() + 4, 0, m_spdifBurstSize - 8);
499}
500
511{
512 std::lock_guard<std::mutex> lock(m_pauseMutex);
513
515
516 Enqueue(buffer, count, pts);
517}
518
526void cSoftHdAudio::Enqueue(const uint16_t *buffer, int count, int64_t pts)
527{
528 std::lock_guard<std::mutex> lock(m_mutex);
529
530 // pitch adjustment
531 if (!m_alsa.IsPassthroughActive() && m_pitchAdjustFrameCounter == 0 && std::abs(m_pitchPpm) > 1) { // only adjust if pitch has a significant value to prevent overly large values/division by zero
533
534 if (m_pitchPpm < 0 && m_pRingbuffer.Write((const uint16_t *)buffer, oneFrameBytes)) // insert additional frame
536 else if (m_pitchPpm > 0) // drop frame
537 count = std::max(0, count - oneFrameBytes);
538
539 m_pitchAdjustFrameCounter = std::round(1'000'000.0 / std::abs(m_pitchPpm));
540 }
541
543
544 // write to ringbuffer
545 int bytesWritten = m_pRingbuffer.Write((const uint16_t *)buffer, count);
546 if (bytesWritten != count)
547 LOGERROR("audio: %s: can't place %d samples in ring buffer", __FUNCTION__, count);
548
550
551 if (pts != AV_NOPTS_VALUE) {
552 // Discontinuity check:
553 // - force a resync if the new pts is more than AV_SYNC_BORDER_MS greater than the last
554 // - a PTS wrap is not recognized, because of the ">"
555 // - not sure, if a forward SkipSeconds() could trigger this, but then the resync is skipped in the video thread
557 LOGDEBUG2(L_AV_SYNC, "audio: %s: discontinuity detected in audio PTS %s -> %s", __FUNCTION__,
559 std::lock_guard<std::mutex> lock(m_queueMutex);
561 }
562
563 m_inputPts = pts;
564 } else if (m_inputPts != AV_NOPTS_VALUE) {
566 }
567}
568
584{
585 int err = 0;
586
588
589 // skip setup, nothing changed
590 if (samplerate == (int)m_alsa.GetHwSampleRate() &&
593 return 1;
594
595 if (Active()) {
596 Stop();
598 }
599
601 if (err)
602 LOGERROR("audio: %s: failed!", __FUNCTION__);
603 else
604 Start();
605
606 return err;
607}
608
615{
616 AVFrame *outframe = nullptr;
618 if (!outframe) {
619 LOGERROR("audio: %s: Error allocating frame", __FUNCTION__);
620 return NULL;
621 }
622
624
625 if (err == AVERROR(EAGAIN)) {
626// LOGERROR("audio: %s: Error filtering AVERROR(EAGAIN)", __FUNCTION__);
628 } else if (err == AVERROR_EOF) {
629 LOGERROR("audio: %s: Error filtering AVERROR_EOF", __FUNCTION__);
631 } else if (err < 0) {
632 LOGERROR("audio: %s: Error filtering the data", __FUNCTION__);
634 }
635
636 return outframe;
637}
638
648{
650// 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",
651// __FUNCTION__, m_filterReady,
652// m_pFilterGraph->sink_links_count, m_pFilterGraph->sink_links[0]->channels,
653// m_pFilterGraph->filters[m_pFilterGraph->nb_filters - 1]->nb_outputs,
654// 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,
655// m_filterChanged);
657 m_filterReady = 0;
658 LOGDEBUG2(L_SOUND, "audio: %s: Free the filter graph.", __FUNCTION__);
659 }
660
661 if (!m_filterReady) {
662 if (InitFilter(ctx)) {
663 LOGDEBUG2(L_SOUND, "audio: %s: AudioFilterReady failed!", __FUNCTION__);
664 return 1;
665 }
666 }
667
668 return 0;
669}
670
681{
683 int err = -1;
684 int err_count = 0;
685
686 if (inframe) {
687 while (err < 0) {
690 return;
691 }
692
694 if (err < 0) {
695 if (err_count) {
696 char errbuf[128];
697 av_strerror(err, errbuf, sizeof(errbuf));
698 LOGERROR("audio: %s: Error submitting the frame to the filter fmt %s channels %d %s", __FUNCTION__,
699 av_get_sample_fmt_name(ctx->sample_fmt), ctx->ch_layout.nb_channels, errbuf);
701 return;
702 } else {
703 m_filterChanged = 1;
704 err_count++;
705 LOGDEBUG2(L_SOUND, "audio: %s: m_filterChanged %d err_count %d", __FUNCTION__, m_filterChanged, err_count);
706 }
707 }
708 }
709 }
710
711// if (!inframe)
712// LOGDEBUG2(L_SOUND, "audio: %s: NO inframe!", __FUNCTION__);
713
716}
717
724{
725 std::lock_guard<std::mutex> lock(m_mutex);
726
727 LOGDEBUG2(L_SOUND, "audio: %s", __FUNCTION__);
728
729 if (!m_initialized)
730 return;
731
734
740 m_filterChanged = 1;
741}
742
747{
748 std::lock_guard<std::mutex> lock(m_mutex);
749
750 return m_pRingbuffer.UsedBytes();
751}
752
757{
758 std::lock_guard<std::mutex> lock(m_mutex);
759
761}
762
776{
777 std::lock_guard<std::mutex> lock(m_mutex);
778
779 return GetOutputPtsMsInternal();
780}
781
795
806{
807 std::lock_guard<std::mutex> lock(m_mutex);
808
810 return AV_NOPTS_VALUE;
811
813
814 // subtract baseline to ignore pause bursts already in the buffer
816
818
819 // handle a PTS wrap
820 if (ptsMs < 0)
822
823 return ptsMs;
824}
825
832{
833 std::lock_guard<std::mutex> lock(m_mutex);
834
836 return AV_NOPTS_VALUE;
837
839
841}
842
856
863{
865 // reduce loudness for stereo output
868 if (volume < 0)
869 volume = 0;
870 else if (volume > 1000)
871 volume = 1000;
872 }
873
875 if (!m_softVolume)
877}
878
885{
886 std::lock_guard<std::mutex> lock(m_pauseMutex);
887 LOGDEBUG2(L_SOUND, "audio: %s: %d", __FUNCTION__, pause);
888
889 m_paused = pause;
890}
891
903
915
928
935{
937 SetVolume(m_volume); // update channel delta
938}
939
953{
954 if (m_initialized)
955 return 0;
956
957 if (!m_alsa.Init()) {
958 LOGERROR("audio: could not initialize alsa");
959 return -1;
960 }
961
962 m_initialized = true;
963
964 return 0;
965}
966
975{
976 LOGDEBUG2(L_SOUND, "audio: %s", __FUNCTION__);
977
978 Stop();
979
980 if (!m_initialized)
981 return;
982
984 m_alsa.Exit();
985 m_initialized = false;
986}
987
998
1009
1010/******************************************************************************
1011 * Thread playback
1012 *****************************************************************************/
1013
1019{
1020 LOGDEBUG("audio: thread started");
1021 while (Running()) {
1023 ProcessEvents();
1024
1026 usleep(1000);
1027 else
1028 usleep(10000);
1029 }
1030 LOGDEBUG("audio: thread stopped");
1031}
1032
1037{
1038 if (!Active())
1039 return;
1040
1041 LOGDEBUG("audio: stopping thread");
1042 Cancel(2);
1043}
1044
1057{
1058 std::lock_guard<std::mutex> lock1(m_pauseMutex);
1059
1060 // do nothing in paused PCM mode
1062 return false;
1063
1064 int err = m_alsa.WaitUntilReady();
1065 if (err < 0) {
1066 if (m_alsa.HandleError(err)) {
1067 std::lock_guard<std::mutex> lock(m_queueMutex);
1068 if (!m_pDevice->IsDraining())
1070 }
1071 return false;
1072 } else if (err == 0) {
1073 return true;
1074 }
1075
1076 std::lock_guard<std::mutex> lock2(m_mutex);
1077
1080 return true; // ?? is this correct?
1081 else if (freeAlsaBufferFrames < 0) {
1083 std::lock_guard<std::mutex> lock(m_queueMutex);
1085 }
1086 return false;
1087 }
1088
1090 // only write, if there is space for a full pause burst
1093 return false;
1094
1095 // send a pause burst to keep the audio stream locked
1096 return SendPause();
1097 }
1098
1100}
1101
1102
1112{
1113 int bytesToWrite;
1115
1116 // query ringbuffer fill level
1117 const void *data;
1119
1121
1122 if (bytesToWrite == 0)
1123 return false;
1124
1125 // muting pass-through AC-3, can produce disturbance
1126 if (m_volume == 0 || (m_softVolume && !m_alsa.IsPassthroughActive())) {
1127 // FIXME: quick&dirty cast
1129 // FIXME: if not all are written, we double amplify them
1130 }
1131
1136
1138}
1139
1152
1153
1158{
1160 m_hwBaseline = 0;
1161
1163 return;
1164
1166
1167 LOGDEBUG2(L_SOUND, "audio: %s: first real audio was sent, hwBaseline %ld frames (%dms)", __FUNCTION__, m_hwBaseline, m_alsa.FramesToMs(m_hwBaseline));
1169 }
1170}
1171
1176{
1177 std::lock_guard<std::mutex> lock(m_mutex);
1178
1179 LOGDEBUG2(L_SOUND, "audio: %s: reset hw delay baseline to 0", __FUNCTION__);
1180 m_hwBaseline = 0;
1182}
1183
1188{
1189 std::lock_guard<std::mutex> lock(m_queueMutex);
1190 for (Event event : m_eventQueue)
1192
1193 m_eventQueue.clear();
1194}
1195
1206{
1208 return;
1209
1211 if (m_fillLevel.IsSettled()) {
1212 auto now = std::chrono::steady_clock::now();
1213 std::chrono::duration<double> elapsedSec = now - m_lastPidInvocation;
1215
1217 } else
1219
1220 if (m_packetCounter++ % 1000 == 0) {
1221 LOGDEBUG2(L_SOUND, "audio: %s: buffer fill level: %.1fms (target: %.1fms), clock drift compensating pitch: %.1fppm, PID controller: P=%.2fppm I=%.2fppm D=%.2fppm",
1225 m_pitchPpm.load(),
1229 }
1230
1231 // buffer fill level low pass filter
1233
1234 if (availableFrames >= 0)
1236}
ALSA Output Device Header File.
Audio Interface Header File.
Audio Manipulation Interface Header File.
double FramesToMsDouble(int frames)
Definition alsadevice.h:74
int GetBufferSizeFrames(void)
Definition alsadevice.h:55
size_t FramesToBytes(int frames)
Definition alsadevice.h:67
int GetHwSampleRate(void)
Definition alsadevice.h:58
int GetHwNumChannels(void)
Definition alsadevice.h:57
bool HandleError(int)
Handle an alsa error.
int64_t FramesToPts(int frames, double timebase)
Definition alsadevice.h:73
int Write(const void *, int)
Write data to the output device.
bool IsPassthroughActive(void)
Definition alsadevice.h:60
void Exit(void)
Cleanup the ALSA audio output module.
int GetAvailableBufferFrames(bool)
Get the number of frames that could be written to the device.
int GetHwDelayFrames(void)
Return the current hardware audio delay in frames.
int WaitUntilReady(void)
Wait until data can be written or read to/from the device (Timeout is 150ms currently)
int GetDownmix(void)
Definition alsadevice.h:56
int Setup(int, int, bool, int)
Setup ALSA audio for requested format.
int BytesToFrames(size_t bytes)
Definition alsadevice.h:68
void SetVolume(int)
Set alsa mixer volume (0-1000)
int64_t MsToPts(int64_t ptsMs, double timebase)
Definition alsadevice.h:70
bool IsRunning(void)
Definition alsadevice.h:59
void FlushBuffers(bool)
Flush ALSA buffers internally.
int64_t PtsToMs(int64_t pts, double timebase)
Definition alsadevice.h:69
int MsToFrames(int milliseconds)
Definition alsadevice.h:71
bool Init(void)
Initialize the ALSA audio output module.
bool CheckWrittenFrames(int, int)
Check, if all frames have been written.
int FramesToMs(int frames)
Definition alsadevice.h:72
void SetNormalizer(int)
Set normalize volume parameters.
void ResetNormalizer(void)
void Normalize(uint16_t *, int)
Normalize audio samples.
void SetEqualizer(int[18])
Set equalizer bands.
std::string GetEqualizerOptions(void) const
Get equalizer filter options.
void SetAmplifier(int volume)
void Amplify(int16_t *, int, int)
Amplify the samples in software.
void SetCompressor(int)
Set volume compression parameters.
void Compress(uint16_t *, int)
Compress audio samples.
void ResetCompressor(void)
void ResetFramesCounters()
Resets the received and written frames counters.
Definition filllevel.cpp:28
void UpdateAvgBufferFillLevel(int)
Updates the buffer fill level average.
Definition filllevel.cpp:48
void ReceivedFrames(int count)
Definition filllevel.h:26
void WroteFrames(int count)
Definition filllevel.h:27
void Reset()
Resets the filter state.
Definition filllevel.cpp:18
void Reset()
Reset the internal state (integral sum and error history).
double GetTargetValue()
double GetPTerm()
double Update(double, double)
Calculate the new output value.
double GetDTerm()
void SetTargetValue(double value)
double GetITerm()
AVRational m_timebase
AVCodecContext pkts_timebase.
Definition audio.h:123
cSoftHdAudio(cSoftHdDevice *)
Create a new audio context.
Definition audio.cpp:52
bool SendPause(void)
Write pause to passthrough device.
Definition audio.cpp:1145
static constexpr int64_t PTS_WRAP
wraparound mod for a 33-bit PTS
Definition audio.h:102
void ResetHwDelayBaseline(void)
Reset the hw delay baseline.
Definition audio.cpp:1175
virtual void Action(void)
Audio thread loop, started with Start().
Definition audio.cpp:1018
void Filter(AVFrame *, AVCodecContext *)
Send audio frame to filter and enqueue it.
Definition audio.cpp:680
cSoftHdRingbuffer m_pRingbuffer
sample ring buffer
Definition audio.h:168
int m_pitchAdjustFrameCounter
counter for pitch adjustment frames
Definition audio.h:119
cSoftHdDevice * m_pDevice
pointer to device
Definition audio.h:104
int m_volume
current volume (0 .. 1000)
Definition audio.h:121
void SetHwDelayBaseline(void)
Set the hw delay baseline.
Definition audio.cpp:1157
void SetStereoDescent(int)
Set stereo loudness descent.
Definition audio.cpp:934
int GetUsedRingbufferMs(void)
Get used ms in audio ringbuffer.
Definition audio.cpp:756
int64_t GetHardwareOutputPtsMs(void)
Get the hardware output PTS in milliseconds.
Definition audio.cpp:805
int Setup(AVRational, int, int, bool)
Alsa setup wrapper.
Definition audio.cpp:583
std::mutex m_pauseMutex
mutex for a safe thread pausing
Definition audio.h:115
int64_t GetHardwareOutputPtsTimebaseUnits(void)
Get the hardware output PTS in timebase units.
Definition audio.cpp:848
AVFilterContext * m_pBuffersinkCtx
Definition audio.h:160
cPidController m_pidController
PID controller for clock drift compensation with tuning values coming from educated guesses.
Definition audio.h:108
void SetVolume(int)
Set mixer volume (0-1000)
Definition audio.cpp:862
void SetEqualizer(bool, int[18])
Set equalizer bands.
Definition audio.cpp:922
AVFilterContext * m_pBuffersrcCtx
Definition audio.h:159
AVFilterGraph * m_pFilterGraph
Definition audio.h:158
cBufferFillLevelLowPassFilter m_fillLevel
low pass filter for the buffer fill level
Definition audio.h:107
void ProcessEvents(void)
Process queued events and forward them to event receiver.
Definition audio.cpp:1187
std::vector< Event > m_eventQueue
event queue for incoming events
Definition audio.h:117
bool SendAudio(int)
Write regular audio data from the ringbuffer to the hardware.
Definition audio.cpp:1111
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
Definition audio.cpp:420
void Enqueue(const uint16_t *, int, int64_t)
Send audio data to ringbuffer.
Definition audio.cpp:526
int64_t GetOutputPtsMs(void)
Get the output PTS of the ringbuffer.
Definition audio.cpp:775
AVFrame * FilterGetFrame(void)
Get frame from filter sink.
Definition audio.cpp:614
void ClockDriftCompensation(void)
Calculate clock drift compensation.
Definition audio.cpp:1205
int m_filterChanged
filter has changed
Definition audio.h:156
void SetCompression(bool, int)
Set volume compression parameters.
Definition audio.cpp:910
void Stop(void)
Stop the thread.
Definition audio.cpp:1036
int64_t m_inputPts
pts clock (last pts in ringbuffer)
Definition audio.h:125
int64_t GetOutputPtsMsInternal(void)
Definition audio.cpp:782
cSoftHdConfig * m_pConfig
pointer to config
Definition audio.h:105
std::atomic< double > m_pitchPpm
pitch adjustment in ppm. Positive values are faster
Definition audio.h:118
void Exit(void)
Cleanup audio output module (alsa)
Definition audio.cpp:974
void SetPaused(bool)
Set audio playback pause state.
Definition audio.cpp:884
int64_t GetHardwareOutputDelayMs(void)
Get the hardware delay in milliseconds.
Definition audio.cpp:831
bool m_useEqualizer
flag to use equalizer
Definition audio.h:149
int m_spdifBurstSize
size of the current spdif burst
Definition audio.h:129
int LazyInit(void)
Initialize audio output module (alsa)
Definition audio.cpp:952
int m_filterReady
filter is ready
Definition audio.h:157
void DropAlsaBuffers(void)
Drop alsa buffers.
Definition audio.cpp:1002
bool HasInputPts(void)
Definition audio.h:71
cAlsaDevice m_alsa
alsa device
Definition audio.h:106
bool CyclicCall(void)
Cyclic audio playback call.
Definition audio.cpp:1056
int m_hwBaseline
saves the hw delay (pause bursts) once a real audio frame to correctly do the AV-Sync
Definition audio.h:131
bool m_initialized
class initialized
Definition audio.h:113
int m_stereoDescent
volume descent for stereo
Definition audio.h:122
std::mutex m_mutex
mutex for thread safety
Definition audio.h:114
static constexpr int AV_SYNC_BORDER_MS
absolute max a/v difference in ms which should trigger a resync
Definition audio.h:100
int m_packetCounter
packet counter for logging
Definition audio.h:110
int GetUsedRingbufferBytes(void)
Get used bytes in audio ringbuffer.
Definition audio.cpp:746
void EnqueueFrame(AVFrame *)
Place samples in audio output queue.
Definition audio.cpp:452
void FlushAlsaBuffers(void)
Flush alsa buffers.
Definition audio.cpp:991
void SetNormalize(bool, int)
Set normalize volume parameters.
Definition audio.cpp:898
bool m_useNormalizer
flag to use volume normalize
Definition audio.h:147
static constexpr int BYTES_PER_SAMPLE
number of bytes per sample
Definition audio.h:101
std::chrono::steady_clock::time_point m_lastPidInvocation
last time the PID controller was invoked
Definition audio.h:109
std::atomic< bool > m_paused
audio is paused
Definition audio.h:126
std::mutex m_queueMutex
mutex for queue safety
Definition audio.h:116
cAudioProcessor m_audioProcessor
Definition audio.h:146
bool m_firstRealAudioReceived
false, as long as no real audio was sent - used to trigger the baseline set
Definition audio.h:132
int CheckForFilterReady(AVCodecContext *)
Check if the filter has changed and is ready, init the filter if needed.
Definition audio.cpp:647
int InitFilter(AVCodecContext *)
Init audio filters.
Definition audio.cpp:203
void RebuildPauseBurst(int)
Rebuild the pause spdif burst with the size of the last recognized normal spdif audio if size changed...
Definition audio.cpp:478
std::vector< uint16_t > m_pauseBurst
holds the burst data itself
Definition audio.h:130
void EnqueueSpdif(const uint16_t *, int, int64_t pts)
Enqueue prepared spdif bursts in audio output queue.
Definition audio.cpp:510
void FlushBuffers(void)
Flush audio buffers.
Definition audio.cpp:723
bool m_useCompressor
flag to use compress volume
Definition audio.h:148
bool m_softVolume
flag to use soft volume
Definition audio.h:128
bool ConfigAudioNormalize
config use normalize volume
Definition config.h:86
int ConfigAudioStereoDescent
config reduce stereo loudness
Definition config.h:90
bool ConfigAudioCompression
config use volume compression
Definition config.h:88
int ConfigAudioEqBand[18]
config equalizer filter bands
Definition config.h:94
int ConfigAudioMaxCompression
config max volume compression
Definition config.h:89
int ConfigAudioEq
config equalizer filter
Definition config.h:93
bool ConfigAudioDownmix
config ffmpeg audio downmix
Definition config.h:81
int ConfigAudioMaxNormalize
config max normalize factor
Definition config.h:87
Output Device Implementation.
bool IsDraining(void)
void TriggerEvent(const Event &)
With this wrapper function, the device can directly act as an event reveiver.
size_t UsedBytes(void)
Get used 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.
Audio Decoder Header File.
Plugin Configuration Header File.
Low-pass Filter for Audio Buffer Fill Level Measurement Header File.
std::string BuildChannelMapFilter(const AVChannelLayout &)
Build the "|"-separated mappings list for the channelmap filter.
Definition audio.cpp:141
static std::vector< std::string > GetFFmpegChannelLayoutAsArray(const AVChannelLayout &layout)
Put FFmpeg channel layout in a dynamic array of strings.
Definition audio.cpp:80
static bool LayoutsMatch(const std::vector< std::string > &ff, const std::vector< std::string > &alsa)
Check, if FFmpeg and Alsa channel layout match.
Definition audio.cpp:105
std::vector< std::string > GetChannelLayoutAsArray(void)
Put ALSA channel layout in a dynamic array of strings.
static bool LayoutIsValid(const std::vector< std::string > &channelLayout)
Check, if the channel layout has channels named "NA" (N/A, silent)
Definition audio.cpp:127
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, ScheduleResyncAtPtsMsEvent, ResyncEvent, DisplayChangeEvent > Event
@ AUDIO
#define LOGDEBUG2
log to LOG_DEBUG and add a prefix
Definition logger.h:47
#define LOGDEBUG
log to LOG_DEBUG
Definition logger.h:45
#define LOGERROR
log to LOG_ERR
Definition logger.h:39
#define AV_NOPTS_VALUE
Definition misc.h:74
#define LOGWARNING
log to LOG_WARN
Definition logger.h:41
static const char * Timestamp2String(int64_t ts, uint8_t divisor)
Nice time-stamp string.
Definition misc.h:127
@ L_AV_SYNC
audio/video sync logs
Definition logger.h:57
@ L_SOUND
sound logs
Definition logger.h:58
Logger Header File.
Misc Functions.
PID (proportional, integral, derivative) Controller Header File.
Audio Ringbuffer Header File.
Output Device Header File.
Device State Machine and Event Handler Header File.