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(
"drmdev audio"),
55 m_pConfig(m_pDevice->Config()),
57 m_softVolume(m_pConfig->ConfigAudioSoftvol),
58 m_audioProcessor(BYTES_PER_SAMPLE),
59 m_pMixerChannel(m_pConfig->ConfigAudioMixerChannel)
82 std::vector<std::string>
names;
85 for (
int i = 0;
i <
layout.nb_channels;
i++) {
107 if (
ff.size() !=
alsa.size())
110 for (
size_t i = 0;
i <
ff.size();
i++) {
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());
152 for (
size_t i = 0;
i <
ff.size();
i++) {
154 if (
i <
ff.size() - 1)
159 for (
size_t i = 0;
i <
alsa.size();
i++) {
161 if (
i <
alsa.size() - 1)
175 std::stringstream
ss;
176 for (
size_t i = 0;
i <
ff.size();
i++) {
222#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7,16,100)
339 LOGDEBUG2(
L_SOUND,
"audio: %s: OUT downmix %d hwNumChannels %d hwSampleRate %d channelLayout %s bytes_per_sample %d",
353 "sample_fmts=%s:sample_rates=%d:channel_layouts=%s",
434 LOGDEBUG2(
L_AV_SYNC,
"audio: %s: dropping %dms audio samples from ringbuffer (output PTS %s -> %s)",
460 buffer = (
uint16_t *)frame->data[0];
631 }
else if (
err < 0) {
698 LOGERROR(
"audio: %s: Error submitting the frame to the filter fmt %s channels %d %s",
__FUNCTION__,
1019 LOGERROR(
"audio: could not initialize alsa");
1102 LOGDEBUG(
"audio: stopping thread");
1141 }
else if (
err == 0) {
1281 auto now = std::chrono::steady_clock::now();
1290 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.
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)
static constexpr int AV_SYNC_BORDER_MS
absolute max a/v difference in ms which should trigger a resync
bool m_firstRealAudioReceived
false, as long as no real audio was sent - used to trigger the baseline set
void SetHwDelayBaseline(void)
Set the hw delay baseline.
bool IsForwardTrickSpeed(void)
std::vector< uint16_t > m_pauseBurst
holds the burst data itself
int GetUsedRingbufferMs(void)
Get used ms in audio ringbuffer.
cDeviceConfig * m_pConfig
pointer to config
bool IsSlowTrickSpeed(void)
int m_pitchAdjustFrameCounter
counter for pitch adjustment frames
void ProcessEvents(void)
Process queued events and forward them to event receiver.
std::atomic< int64_t > m_trickSpeedPts
pts of the last received (undecoded) packet during audio-only trickspeed
AVRational m_timebase
AVCodecContext pkts_timebase.
void ResetHwDelayBaseline(void)
Reset the hw delay baseline.
int m_filterReady
filter is ready
int m_spdifBurstSize
size of the current spdif burst
void Filter(AVFrame *, AVCodecContext *)
Send audio frame to filter and enqueue it.
void Stop(void)
Stop the thread.
static constexpr double MAX_TRICKSPEED_STEP_DELAY_MS
upper bound for a single AdvanceTrickSpeedPts(), in case of large/ erratic pts jumps
void SetNormalize(bool, int)
Set normalize volume parameters.
void DropAlsaBuffers(void)
Drop alsa buffers.
AVFilterContext * m_pBuffersrcCtx
void RebuildPauseBurst(int)
Rebuild the pause spdif burst with the size of the last recognized normal spdif audio if size changed...
void SetStereoDescent(int)
Set stereo loudness descent.
bool SendPause(void)
Write pause to passthrough device.
int m_stereoDescent
volume descent for stereo
int CheckForFilterReady(AVCodecContext *)
Check if the filter has changed and is ready, init the filter if needed.
bool m_useCompressor
flag to use compress volume
std::mutex m_mutex
mutex for thread safety
void SetEqualizer(bool, int[18])
Set equalizer bands.
int64_t GetHardwareOutputPtsMs(void)
Get the hardware output PTS in milliseconds.
std::vector< Event > m_eventQueue
event queue for incoming events
int m_packetCounter
packet counter for logging
int64_t m_inputPts
pts clock (last pts in ringbuffer)
cOutputDevice * m_pDevice
pointer to device
void ClockDriftCompensation(void)
Calculate clock drift compensation.
std::atomic< double > m_trickspeedFactor
current trickspeed factor for AdvanceTrickSpeedPts()
std::mutex m_pauseMutex
mutex for a safe thread pausing
AVFilterContext * m_pBuffersinkCtx
cBufferFillLevelLowPassFilter m_fillLevel
low pass filter for the buffer fill level
void Exit(void)
Cleanup audio output module (alsa)
cRingbuffer m_pRingbuffer
sample ring buffer
bool m_useNormalizer
flag to use volume normalize
int Setup(AVRational, int, int, bool)
Alsa setup wrapper.
AVFrame * FilterGetFrame(void)
Get frame from filter sink.
int64_t GetOutputPtsMs(void)
Get the output PTS of the ringbuffer.
int m_filterChanged
filter has changed
void FlushAlsaBuffers(void)
Flush alsa buffers.
std::atomic< bool > m_paused
audio is paused
int m_hwBaseline
saves the hw delay (pause bursts) once a real audio frame to correctly do the AV-Sync
int64_t GetOutputPtsMsInternal(void)
int m_volume
current volume (0 .. 1000)
bool m_useEqualizer
flag to use equalizer
void SetPaused(bool)
Set audio playback pause state.
std::mutex m_queueMutex
mutex for queue safety
void Enqueue(const uint16_t *, int, int64_t)
Send audio data to ringbuffer.
void SetTrickSpeed(double, bool, bool)
Set the trickspeed.
int64_t GetHardwareOutputDelayMs(void)
Get the hardware delay in milliseconds.
bool m_softVolume
flag to use soft volume
bool SendAudio(int)
Write regular audio data from the ringbuffer to the hardware.
bool m_initialized
class initialized
int64_t GetHardwareOutputPtsTimebaseUnits(void)
Get the hardware output PTS in timebase units.
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
void EnqueueFrame(AVFrame *)
Place samples in audio output queue.
std::atomic< bool > m_forwardTrickspeed
current trickspeed direction
std::chrono::steady_clock::time_point m_lastPidInvocation
last time the PID controller was invoked
int LazyInit(void)
Initialize audio output module (alsa)
static constexpr int BYTES_PER_SAMPLE
number of bytes per sample
cAlsaDevice m_alsa
alsa device
cAudioStream(cOutputDevice *)
Create a new audio context.
int InitFilter(AVCodecContext *)
Init audio filters.
void SetVolume(int)
Set mixer volume (0-1000)
virtual void Action(void)
Audio thread loop, started with Start().
std::atomic< bool > m_trickspeed
true, if trickspeed is active
void SetCompression(bool, int)
Set volume compression parameters.
void FlushBuffers(void)
Flush audio buffers.
cPidController m_pidController
PID controller for clock drift compensation with tuning values coming from educated guesses.
void EnqueueSpdif(const uint16_t *, int, int64_t pts)
Enqueue prepared spdif bursts in audio output queue.
cAudioProcessor m_audioProcessor
int GetUsedRingbufferBytes(void)
Get used bytes in audio ringbuffer.
bool CyclicCall(void)
Cyclic audio playback call.
static constexpr int64_t PTS_WRAP
wraparound mod for a 33-bit PTS
std::atomic< double > m_pitchPpm
pitch adjustment in ppm. Positive values are faster
void AdvanceTrickSpeedPts(int64_t)
Advance the pts in trickspeed mode and delay "playback".
AVFilterGraph * m_pFilterGraph
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.
int ConfigAudioEqBand[18]
config equalizer filter bands
bool ConfigAudioNormalize
config use normalize volume
int ConfigAudioMaxNormalize
config max normalize factor
bool ConfigAudioCompression
config use volume compression
bool ConfigAudioDownmix
config ffmpeg audio downmix
int ConfigAudioEq
config equalizer filter
int ConfigAudioStereoDescent
config reduce stereo loudness
int ConfigAudioMaxCompression
config max volume compression
Output Device Implementation.
void TriggerEvent(const Event &)
With this wrapper function, the device can directly act as an event reveiver.
bool IsAudioOnlyPlayback(void)
void Reset()
Reset the internal state (integral sum and error history).
double Update(double, double)
Calculate the new output value.
void SetTargetValue(double value)
size_t Write(const void *, size_t)
Write to a ring buffer.
void Reset(void)
Reset ring buffer pointers.
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 UsedBytes(void)
Get used bytes in ring buffer.
Audio Decoder Header File.
Plugin Configuration Header File.
Output Device Header File.
Low-pass Filter for Audio Buffer Fill Level Measurement Header File.
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)
std::string BuildChannelMapFilter(const AVChannelLayout &)
Build the "|"-separated mappings list for the channelmap filter.
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, ScheduleResyncAtPtsMsEvent, ResyncEvent, DisplayChangeEvent > Event
#define LOGDEBUG2
log to LOG_DEBUG and add a prefix
#define LOGDEBUG
log to LOG_DEBUG
#define LOGERROR
log to LOG_ERR
static const char * Timestamp2StringMs(int64_t ts)
Input PTS is in ms.
#define LOGWARNING
log to LOG_WARN
@ L_AV_SYNC
audio/video sync logs
PID (proportional, integral, derivative) Controller Header File.
Audio Ringbuffer Header File.
Device State Machine and Event Handler Header File.