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>
34#include <vdr/thread.h>
53 : cThread(
"softhd audio"),
55 m_pConfig(m_pDevice->Config()),
57 m_pEventReceiver(device),
58 m_softVolume(m_pConfig->ConfigAudioSoftvol),
59 m_audioProcessor(BYTES_PER_SAMPLE),
60 m_pMixerChannel(m_pConfig->ConfigAudioMixerChannel)
83 std::vector<std::string>
names;
86 for (
int i = 0;
i <
layout.nb_channels;
i++) {
108 if (
ff.size() !=
alsa.size())
111 for (
size_t i = 0;
i <
ff.size();
i++) {
147 if (
ff.size() !=
alsa.size()) {
148 LOGWARNING(
"audio: %s: Skip channelmap filter, FFmpeg and Alsa channel count differs: FFmpeg %zu ALSA %zu",
__FUNCTION__,
ff.size(),
alsa.size());
153 for (
size_t i = 0;
i <
ff.size();
i++) {
155 if (
i <
ff.size() - 1)
160 for (
size_t i = 0;
i <
alsa.size();
i++) {
162 if (
i <
alsa.size() - 1)
176 std::stringstream
ss;
177 for (
size_t i = 0;
i <
ff.size();
i++) {
223#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7,16,100)
340 LOGDEBUG2(
L_SOUND,
"audio: %s: OUT downmix %d hwNumChannels %d hwSampleRate %d channelLayout %s bytes_per_sample %d",
354 "sample_fmts=%s:sample_rates=%d:channel_layouts=%s",
435 LOGDEBUG2(
L_AV_SYNC,
"audio: %s: dropping %dms audio samples to start in sync with the video (output PTS %s -> %s)",
461 buffer = (
uint16_t *)frame->data[0];
630 }
else if (
err < 0) {
697 LOGERROR(
"audio: %s: Error submitting the frame to the filter fmt %s channels %d %s",
__FUNCTION__,
937 LOGFATAL(
"audio: could not initialize alsa, abort!");
1016 LOGDEBUG(
"audio: stopping thread");
1046 }
else if (
err == 0) {
1186 auto now = std::chrono::steady_clock::now();
1195 LOGDEBUG2(
L_SOUND,
"audio: %s: buffer fill level: %.1fms (target: %.1fms), clock drift compensating pitch: %.1fppm, PID controller: P=%.2fppm I=%.2fppm D=%.2fppm",
ALSA Output Device Header File.
Audio Interface Header File.
Audio Manipulation Interface Header File.
virtual void OnEventReceived(const Event &)=0
double FramesToMsDouble(int frames)
int GetBufferSizeFrames(void)
size_t FramesToBytes(int frames)
int GetHwSampleRate(void)
int GetHwNumChannels(void)
bool HandleError(int)
Handle an alsa error.
int64_t FramesToPts(int frames, double timebase)
int Write(const void *, int)
Write data to the output device.
bool IsPassthroughActive(void)
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 Setup(int, int, bool, int)
Setup ALSA audio for requested format.
int BytesToFrames(size_t bytes)
void SetVolume(int)
Set alsa mixer volume (0-1000)
int64_t MsToPts(int64_t ptsMs, double timebase)
void FlushBuffers(bool)
Flush ALSA buffers internally.
int64_t PtsToMs(int64_t pts, double timebase)
int MsToFrames(int milliseconds)
bool Init(void)
Initialize the ALSA audio output module.
bool CheckWrittenFrames(int, int)
Check, if all frames have been written.
int FramesToMs(int frames)
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.
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)
AVRational m_timebase
AVCodecContext pkts_timebase.
cSoftHdAudio(cSoftHdDevice *)
Create a new audio context.
bool SendPause(void)
Write pause to passthrough device.
void ResetHwDelayBaseline(void)
Reset the hw delay baseline.
virtual void Action(void)
Audio thread loop, started with Start().
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
int m_volume
current volume (0 .. 1000)
void SetHwDelayBaseline(void)
Set the hw delay baseline.
void SetStereoDescent(int)
Set stereo loudness descent.
int GetUsedRingbufferMs(void)
Get used ms in audio ringbuffer.
int64_t GetHardwareOutputPtsMs(void)
Get the hardware output PTS in milliseconds.
int Setup(AVRational, int, int, bool)
Alsa setup wrapper.
std::mutex m_pauseMutex
mutex for a safe thread pausing
int64_t GetHardwareOutputPtsTimebaseUnits(void)
Get the hardware output PTS in timebase units.
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)
void SetEqualizer(bool, int[18])
Set equalizer bands.
AVFilterContext * m_pBuffersrcCtx
AVFilterGraph * m_pFilterGraph
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.
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.
void Enqueue(const uint16_t *, int, int64_t)
Send audio data to ringbuffer.
int64_t GetOutputPtsMs(void)
Get the output PTS of the ringbuffer.
AVFrame * FilterGetFrame(void)
Get frame from filter sink.
void ClockDriftCompensation(void)
Calculate clock drift compensation.
int m_filterChanged
filter has changed
void SetCompression(bool, int)
Set volume compression parameters.
void Stop(void)
Stop the thread.
int64_t m_inputPts
pts clock (last pts in ringbuffer)
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)
void SetPaused(bool)
Set audio playback pause state.
int64_t GetHardwareOutputDelayMs(void)
Get the hardware delay in milliseconds.
bool m_useEqualizer
flag to use equalizer
int m_spdifBurstSize
size of the current spdif burst
int m_filterReady
filter is ready
void DropAlsaBuffers(void)
Drop alsa buffers.
cAlsaDevice m_alsa
alsa device
bool CyclicCall(void)
Cyclic audio playback call.
int m_hwBaseline
saves the hw delay (pause bursts) once a real audio frame to correctly do the AV-Sync
bool m_initialized
class initialized
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_packetCounter
packet counter for logging
int GetUsedRingbufferBytes(void)
Get used bytes in audio ringbuffer.
void EnqueueFrame(AVFrame *)
Place samples in audio output queue.
void FlushAlsaBuffers(void)
Flush alsa buffers.
void SetNormalize(bool, int)
Set normalize volume parameters.
bool m_useNormalizer
flag to use volume normalize
static constexpr int BYTES_PER_SAMPLE
number of bytes per sample
std::chrono::steady_clock::time_point m_lastPidInvocation
last time the PID controller was invoked
std::atomic< bool > m_paused
audio is paused
std::mutex m_queueMutex
mutex for queue safety
cAudioProcessor m_audioProcessor
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.
void RebuildPauseBurst(int)
Rebuild the pause spdif burst with the size of the last recognized normal spdif audio if size changed...
std::vector< uint16_t > m_pauseBurst
holds the burst data itself
void EnqueueSpdif(const uint16_t *, int, int64_t pts)
Enqueue prepared spdif bursts in audio output queue.
void FlushBuffers(void)
Flush audio buffers.
bool m_useCompressor
flag to use compress volume
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
bool ConfigAudioDownmix
config ffmpeg audio downmix
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.
State Machine and Event 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.
static std::vector< std::string > GetFFmpegChannelLayoutAsArray(const AVChannelLayout &layout)
Put FFmpeg channel layout in a dynamic array of strings.
static bool LayoutsMatch(const std::vector< std::string > &ff, const std::vector< std::string > &alsa)
Check, if FFmpeg and Alsa channel layout match.
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)
#define LOGDEBUG2
log to LOG_DEBUG and add a prefix
#define LOGDEBUG
log to LOG_DEBUG
#define LOGERROR
log to LOG_ERR
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, ScheduleResyncAtPtsMsEvent, ResyncEvent, DisplayChangeEvent > Event
#define LOGWARNING
log to LOG_WARN
#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.