25#include <alsa/asoundlib.h>
28#include <libavcodec/avcodec.h>
29#include <libavfilter/avfilter.h>
30#include <libavfilter/buffersink.h>
31#include <libavfilter/buffersrc.h>
32#include <libavutil/channel_layout.h>
33#include <libavutil/opt.h>
36#include <vdr/thread.h>
53 : cThread(
"softhd audio"),
55 m_pConfig(m_pDevice->Config()),
56 m_pEventReceiver(device),
57 m_downmix(m_pConfig->ConfigAudioDownmix),
58 m_softVolume(m_pConfig->ConfigAudioSoftvol),
59 m_passthrough(m_pConfig->ConfigAudioPassthroughState ? m_pConfig->ConfigAudioPassthroughMask : 0),
60 m_pPCMDevice(m_pConfig->ConfigAudioPCMDevice),
61 m_pPassthroughDevice(m_pConfig->ConfigAudioPassthroughDevice),
62 m_appendAES(m_pConfig->ConfigAudioAutoAES),
63 m_pMixerChannel(m_pConfig->ConfigAudioMixerChannel)
99 for (i = 0; i < n; ++i) {
118 factor = ((INT16_MAX / 8) * 1000U) / (uint32_t) sqrt(avg);
130 LOGDEBUG2(
L_SOUND,
"audio: %s: avg %8d, fac=%6.3f, norm=%6.3f", __FUNCTION__,
149 }
else if (t > INT16_MAX) {
181 factor = (INT16_MAX * 1000) / maxSample;
194 LOGDEBUG2(
L_SOUND,
"audio: %s: max %5d, fac=%6.3f, com=%6.3f", __FUNCTION__, maxSample,
204 }
else if (t > INT16_MAX) {
225 memset(samples, 0, count);
235 }
else if (t > INT16_MAX) {
257 for (i = 0; i < 18; i++) {
327 if (!strcmp(alsaName,
"RL"))
return "BL";
328 if (!strcmp(alsaName,
"RR"))
return "BR";
344 std::vector<std::string>
layout;
349 for (
unsigned int i = 0;
i <
map->channels;
i++) {
353 layout.push_back(std::string(name));
370 std::vector<std::string>
names;
373 for (
int i = 0;
i <
layout.nb_channels;
i++) {
395 if (
ff.size() !=
alsa.size())
398 for (
size_t i = 0;
i <
ff.size();
i++) {
421 if (
ff.size() !=
alsa.size()) {
427 for (
size_t i = 0;
i <
ff.size();
i++) {
429 if (
i <
ff.size() - 1)
434 for (
size_t i = 0;
i <
alsa.size();
i++) {
436 if (
i <
alsa.size() - 1)
445 std::stringstream
ss;
446 for (
size_t i = 0;
i <
ff.size();
i++) {
497#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7,16,100)
582 ":6b=%.2f:7b=%.2f:8b=%.2f:9b=%.2f:10b=%.2f:11b=%.2f:12b=%.2f:13b=%.2f:14b=%.2f:"
617 LOGDEBUG2(
L_SOUND,
"audio: %s: OUT m_downmix %d m_hwNumChannels %d m_hwSampleRate %d channelLayout %s bytes_per_sample %d",
631 "sample_fmts=%s:sample_rates=%d:channel_layouts=%s",
712 LOGDEBUG2(
L_AV_SYNC,
"audio: %s: dropping %dms audio samples to start in sync with the video (output PTS %s -> %s)",
738 buffer = (
uint16_t *)frame->data[0];
889 }
else if (
err < 0) {
956 LOGERROR(
"audio: %s: Error submitting the frame to the filter fmt %s channels %d %s",
__FUNCTION__,
1106 }
else if (
volume > 1000) {
1342 LOGDEBUG(
"audio: stopping thread");
1371 }
else if (
ret == 0) {
1571 if (!(
strchr(device,
':'))) {
1572 sprintf(
tmp,
"%s:AES0=%d,AES1=%d,AES2=0,AES3=%d",
1578 sprintf(
tmp,
"%s,AES0=%d,AES1=%d,AES2=0,AES3=%d",
1599 return (
char *)device;
1626 while (*
n !=
NULL) {
1650 char *device =
NULL;
1704 if (!
strcmp(device,
"null"))
1708 LOGINFO(
"audio: using %sdevice '%s'",
1737 if (!(device =
getenv(
"ALSA_MIXER"))) {
1868 LOGERROR(
"audio: %s: set params error: %s\n"
1869 " Channels %d SampleRate %d\n"
1870 " HWChannels %d HWSampleRate %d SampleFormat %s\n"
1872 " AlsaBufferTime %dms pcm state: %s\n"
1873 " periodSize %d frames, bufferSize %d frames",
1885 for (
size_t i = 0;
i <
alsaMap.size();
i++) {
1893 " Channels %d (%s) SampleRate %d%s\n"
1894 " HWChannels %d HWSampleRate %d SampleFormat %s\n"
1896 " AlsaBufferTime %dms, pcm state: %s\n"
1897 " periodSize %d frames, bufferSize %d frames",
1921 const char *
fmt, ...)
1973 auto now = std::chrono::steady_clock::now();
1982 LOGDEBUG2(
L_SOUND,
"audio: %s: buffer fill level: %.1fms (target: %.1fms), clock drift compensating pitch: %.1fppm, PID controller: P=%.2fppm I=%.2fppm D=%.2fppm",
Audio and Alsa Interface Header File.
virtual void OnEventReceived(const Event &)=0
void ResetFramesCounters()
Resets the received and written frames counters.
void UpdateAvgBufferFillLevel(int)
Updates the buffer fill level average.
void ReceivedFrames(int count)
void WroteFrames(int count)
double GetBufferFillLevelFramesAvg()
void Reset()
Resets the filter state.
void Reset()
Reset the internal state (integral sum and error history).
double Update(double, double)
Calculate the new output value.
void SetTargetValue(double value)
void LazyInit(void)
Initialize audio output module (alsa)
cSoftHdAudio(cSoftHdDevice *)
Create a new audio context.
char * OpenAlsaDevice(const char *, int)
Open an alsa device.
bool SendPause(void)
Write pause to passthrough device.
int m_alsaBufferSizeFrames
alsa buffer size in frames
char * FindAlsaDevice(const char *, const char *, int)
Find alsa device giving some search hints.
void ResetHwDelayBaseline(void)
Reset the hw delay baseline.
virtual void Action(void)
Audio thread loop, started with Start().
bool m_appendAES
flag ato utomatic append AES
void Filter(AVFrame *, AVCodecContext *)
Send audio frame to filter and enqueue it.
cSoftHdRingbuffer m_pRingbuffer
sample ring buffer
int m_pitchAdjustFrameCounter
counter for pitch adjustment frames
double FramesToMsDouble(int frames)
int Setup(AVCodecContext *, int, int, int)
Alsa setup wrapper.
void Enqueue(uint16_t *, int, AVFrame *)
Send audio data to ringbuffer.
int m_compressionMaxFactor
max. compression factor
const char * m_pPCMDevice
PCM device name.
int m_volume
current volume (0 .. 1000)
void SetPassthroughMask(int)
Set audio passthrough mask.
void SetHwDelayBaseline(void)
Set the hw delay baseline.
void SetStereoDescent(int)
Set stereo loudness descent.
int64_t GetHardwareOutputPtsMs(void)
Get the hardware output PTS in milliseconds.
std::mutex m_pauseMutex
mutex for a safe thread pausing
int64_t GetHardwareOutputPtsTimebaseUnits(void)
Get the hardware output PTS in timebase units.
void EnqueueSpdif(uint16_t *, int, AVFrame *)
Enqueue prepared spdif bursts in audio output queue.
AVFilterContext * m_pBuffersinkCtx
cPidController m_pidController
PID controller for clock drift compensation with tuning values coming from educated guesses.
void SetVolume(int)
Set mixer volume (0-1000)
AVFilterContext * m_pBuffersrcCtx
void HandleError(int)
Handle an alsa error.
AVFilterGraph * m_pFilterGraph
int m_passthrough
passthrough mask
const int m_bytesPerSample
number of bytes per sample
const char * m_pMixerChannel
mixer channel name
void FlushAlsaBuffersInternal(bool)
Flush alsa buffers internally.
unsigned int m_hwSampleRate
hardware sample rate in Hz
int64_t PtsToMs(int64_t pts)
cBufferFillLevelLowPassFilter m_fillLevel
low pass filter for the buffer fill level
IEventReceiver * m_pEventReceiver
pointer to event receiver
void ProcessEvents(void)
Process queued events and forward them to event receiver.
int MsToFrames(int milliseconds)
std::vector< Event > m_eventQueue
event queue for incoming events
bool SendAudio(int)
Write regular audio data from the ringbuffer to the hardware.
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
AVRational * m_pTimebase
pointer to AVCodecContext pkts_timebase
bool m_compression
flag to use compress volume
int64_t GetOutputPtsMs(void)
Get the output PTS of the ringbuffer.
bool m_normalize
flag to use volume normalize
AVFrame * FilterGetFrame(void)
Get frame from filter sink.
void ClockDriftCompensation(void)
Calculate clock drift compensation.
int m_filterChanged
filter has changed
void AlsaExit(void)
Cleanup the alsa audio output module.
void SetCompression(bool, int)
Set volume compression parameters.
void Stop(void)
Stop the thread.
snd_mixer_elem_t * m_pAlsaMixerElem
alsa mixer element
void BuildPauseBurst(void)
Build a pause spdif burst with the size of the last recognized normal spdif audio.
void AlsaInit(void)
Initialize the alsa audio output module.
void Compress(uint16_t *, int)
Compress audio samples.
int64_t m_inputPts
pts clock (last pts in ringbuffer)
int m_normalizeFactor
current normalize factor
void AlsaSetVolume(int)
Set alsa mixer volume (0-1000)
int64_t GetOutputPtsMsInternal(void)
cSoftHdConfig * m_pConfig
pointer to config
std::atomic< double > m_pitchPpm
pitch adjustment in ppm. Positive values are faster
void Exit(void)
Cleanup audio output module (alsa)
int m_amplifier
software volume amplify factor
int GetUsedBytes(void)
Get used bytes in audio ringbuffer.
void SetPaused(bool)
Set audio playback pause state.
void AlsaInitMixer(void)
Initialize alsa mixer.
int AlsaSetup(int, int, int)
Setup alsa audio for requested format.
static constexpr int NORMALIZE_MAX_INDEX
number of normalize average samples
const char * m_pPassthroughDevice
passthrough device name
int m_normalizeMaxFactor
max. normalize factor
int64_t GetHardwareOutputDelayMs(void)
Get the hardware delay in milliseconds.
bool m_alsaUseMmap
use mmap
void Normalize(uint16_t *, int)
Normalize audio samples.
int m_compressionFactor
current compression factor
int64_t MsToPts(int64_t ptsMs)
int m_spdifBurstSize
size of the current spdif burst
const int m_normalizeMinFactor
min. normalize factor
const int m_normalizeSamples
number of normalize samples
int m_filterReady
filter is ready
void SetEq(int[18], int)
Set equalizer bands.
int m_normalizeReady
index normalize counter
const char * m_pMixerDevice
mixer device name (not used)
void DropAlsaBuffers(void)
Drop alsa buffers.
uint32_t m_normalizeAverage[NORMALIZE_MAX_INDEX]
average of n last normalize sample blocks
bool CyclicCall(void)
Cyclic audio playback call.
void AlsaInitPCMDevice(void)
Search for an alsa pcm device and open it.
unsigned int m_hwNumChannels
number of hardware channels
bool m_initialized
class initialized
int FramesToMs(int frames)
int m_stereoDescent
volume descent for stereo
std::mutex m_mutex
mutex for thread safety
static constexpr int AV_SYNC_BORDER_MS
absolute max a/v difference in ms which should trigger a resync
int m_alsaRatio
internal -> mixer ratio * 1000
int m_packetCounter
packet counter for logging
void EnqueueFrame(AVFrame *)
Place samples in audio output queue.
void SoftAmplify(int16_t *, int)
Amplify the samples in software.
void FlushAlsaBuffers(void)
Flush alsa buffers.
void SetNormalize(bool, int)
Set normalize volume parameters.
snd_pcm_t * m_pAlsaPCMHandle
alsa pcm handle
int m_downmix
set stereo downmix
int m_useEqualizer
flag to use equalizer
float m_equalizerBand[18]
equalizer band
snd_mixer_t * m_pAlsaMixer
alsa mixer handle
snd_pcm_sframes_t m_hwBaseline
saves the hw delay (pause bursts) once a real audio frame to correctly do the AV-Sync
int m_normalizeIndex
index into normalize average table
std::chrono::steady_clock::time_point m_lastPidInvocation
last time the PID controller was invoked
int m_normalizeCounter
normalize sample counter
std::atomic< bool > m_paused
audio is paused
bool m_firstRealAudioReceived
false, as long as no real audio was sent - used to trigger the baseline set
int CheckForFilterReady(AVCodecContext *)
Check if the filter has changed and is ready, init the filter if needed.
int InitFilter(AVCodecContext *)
Init audio filters.
std::vector< uint16_t > m_pauseBurst
holds the burst data itself
void FlushBuffers(void)
Flush audio buffers.
bool m_softVolume
flag to use soft volume
bool ConfigAudioNormalize
config use normalize volume
int ConfigAudioStereoDescent
config reduce stereo loudness
bool ConfigAudioCompression
config use volume compression
int ConfigAudioEqBand[18]
config equalizer filter bands
int ConfigAudioMaxCompression
config max volume compression
int ConfigAudioEq
config equalizer filter
int ConfigAudioMaxNormalize
config max normalize factor
Output Device Implementation.
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.
__attribute__((weak)) union gbm_bo_handle gbm_bo_get_handle_for_plane(struct gbm_bo *bo
State Machine and Event Header File.
Low-pass Filter for Audio Buffer Fill Level Measurement Header File.
static const char * alsaToFFmpegChannel(const char *alsaName)
FFmpeg does not have channels called "RL" or "RR" So "rename" Alsas RL (rear left) and RR (rear right...
static std::string BuildChannelMapFilter(snd_pcm_t *pcmHandle, const AVChannelLayout &layout)
Build the "|"-separated mappings list for the channelmap filter.
static std::vector< std::string > GetFFmpegChannelLayoutAsArray(const AVChannelLayout &layout)
Put FFmpeg channel layout in a dynamic array of strings.
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.
static bool LayoutsMatch(const std::vector< std::string > &ff, const std::vector< std::string > &alsa)
Check, if FFmpeg and Alsa channel layout match.
static std::vector< std::string > GetAlsaChannelLayoutAsArray(snd_pcm_t *pcmHandle)
Put Alsa channel layout in a dynamic array of strings.
#define LOGDEBUG2
log to LOG_DEBUG and add a prefix
#define LOGDEBUG
log to LOG_DEBUG
#define LOGERROR
log to LOG_ERR
#define LOGWARNING
log to LOG_WARN
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, PipEvent, ScheduleResyncAtPtsMsEvent, ResyncEvent > Event
#define LOGINFO
log to LOG_INFO
#define LOGFATAL
log to LOG_ERR and abort
static const char * Timestamp2String(int64_t ts, uint8_t divisor)
Nice time-stamp string.
@ L_AV_SYNC
audio/video sync logs
PID (proportional, integral, derivative) Controller Header File.
Audio Ringbuffer Header File.
Output Device Header File.