MythTV master
audioreencodebuffer.cpp
Go to the documentation of this file.
1
3
4#include <algorithm> // for min/max
5
6extern "C" {
7#include "libavutil/mem.h"
8}
9
10
12 : m_buffer((uint8_t *)av_malloc(ABLOCK_SIZE))
13{
14 if (m_buffer == nullptr)
15 {
16 throw std::bad_alloc();
17 }
18}
19
21 : m_size(old.m_size), m_realsize(old.m_realsize),
22 m_frames(old.m_frames), m_time(old.m_time)
23{
24 m_buffer = (uint8_t *)av_malloc(m_realsize);
25 if (m_buffer == nullptr)
26 {
27 throw std::bad_alloc();
28 }
29 memcpy(m_buffer, old.m_buffer, m_size);
30}
31
33{
34 av_free(m_buffer);
35}
36
37void AudioBuffer::appendData(unsigned char *buffer, int len, int frames,
38 std::chrono::milliseconds time)
39{
40 if ((m_size + len) > m_realsize)
41 {
42 // buffer is too small to fit all
43 // can't use av_realloc as it doesn't guarantee reallocated memory
44 // to be 16 bytes aligned
45 m_realsize = ((m_size + len) / ABLOCK_SIZE + 1 ) * ABLOCK_SIZE;
46 auto *tmp = (uint8_t *)av_malloc(m_realsize);
47 if (tmp == nullptr)
48 {
49 throw std::bad_alloc();
50 }
51 memcpy(tmp, m_buffer, m_size);
52 av_free(m_buffer);
53 m_buffer = tmp;
54 }
55
56 memcpy(m_buffer + m_size, buffer, len);
57 m_size += len;
58 m_frames += frames;
59 m_time = time;
60}
61
63
65 int audio_channels, bool passthru)
66 : m_initpassthru(passthru)
67{
69 const AudioSettings settings(audio_format, audio_channels, AV_CODEC_ID_NONE, 0, false);
71}
72
74{
76 delete m_saveBuffer;
77}
78
83{
84 m_passthru = settings.m_usePassthru;
85 m_channels = settings.m_channels;
89 m_isConfigured = true;
90}
91
96{
97 m_eff_audiorate = (dsprate / 100);
98}
99
101{
102 QMutexLocker locker(&m_bufferMutex);
103 for (AudioBuffer *ab : std::as_const(m_bufferList))
104 {
105 delete ab;
106 }
107 m_bufferList.clear();
108}
109
120bool AudioReencodeBuffer::AddFrames(void *buffer, int frames, std::chrono::milliseconds timecode)
121{
122 return AddData(buffer, frames * m_bytes_per_frame, timecode, frames);
123}
124
136bool AudioReencodeBuffer::AddData(void *buffer, int len, std::chrono::milliseconds timecode,
137 int frames)
138{
139 auto *buf = (unsigned char *)buffer;
140
141 // Test if target is using a fixed buffer size.
143 {
144 int index = 0;
145
146 // Target has a fixed buffer size, which may not match len.
147 // Redistribute the bytes into appropriately sized buffers.
148 while (index < len)
149 {
150 // See if we have some saved from last iteration in
151 // m_saveBuffer. If not create a new empty buffer.
152 if (!m_saveBuffer)
154
155 // Use as many of the remaining frames as will fit in the space
156 // left in the buffer.
157 int bufsize = m_saveBuffer->size();
158 int part = std::min(len - index, m_audioFrameSize - bufsize);
159 int out_frames = part / m_bytes_per_frame;
160 timecode += std::chrono::milliseconds(out_frames * 1000 / m_eff_audiorate);
161
162 // Store frames in buffer, basing frame count on number of
163 // bytes, which works only for uncompressed data.
164 m_saveBuffer->appendData(&buf[index], part,
165 out_frames, timecode);
166
167 // If we have filled the buffer...
169 {
170 QMutexLocker locker(&m_bufferMutex);
171
172 // store the buffer
174 // mark m_saveBuffer as emtpy.
175 m_saveBuffer = nullptr;
176 // m_last_audiotime is updated iff we store a buffer.
177 m_last_audiotime = timecode;
178 }
179
180 index += part;
181 }
182 }
183 else
184 {
185 // Target has no fixed buffer size. We can use a simpler algorithm
186 // and use 'frames' directly rather than 'len / m_bytes_per_frame',
187 // thus also covering the passthrough case.
189 timecode += std::chrono::milliseconds(frames * 1000 / m_eff_audiorate);
190 m_saveBuffer->appendData(buf, len, frames, timecode);
191
192 QMutexLocker locker(&m_bufferMutex);
194 m_saveBuffer = nullptr;
195 m_last_audiotime = timecode;
196 }
197
198 return true;
199}
200
201AudioBuffer *AudioReencodeBuffer::GetData(std::chrono::milliseconds time)
202{
203 QMutexLocker locker(&m_bufferMutex);
204
205 if (m_bufferList.isEmpty())
206 return nullptr;
207
208 AudioBuffer *ab = m_bufferList.front();
209
210 if (ab->m_time <= time)
211 {
212 m_bufferList.pop_front();
213 return ab;
214 }
215
216 return nullptr;
217}
218
219long long AudioReencodeBuffer::GetSamples(std::chrono::milliseconds time)
220{
221 QMutexLocker locker(&m_bufferMutex);
222
223 if (m_bufferList.isEmpty())
224 return 0;
225
226 long long samples = 0;
227 for (auto *ab : std::as_const(m_bufferList))
228 {
229 if (ab->m_time <= time)
230 samples += ab->m_frames;
231 else
232 break;
233 }
234 return samples;
235}
236
237void AudioReencodeBuffer::SetTimecode(std::chrono::milliseconds timecode)
238{
239 m_last_audiotime = timecode;
240}
241
242/* vim: set expandtab tabstop=4 shiftwidth=4: */
243
static constexpr size_t ABLOCK_SIZE
std::chrono::milliseconds m_time
void appendData(unsigned char *buffer, int len, int frames, std::chrono::milliseconds time)
uint8_t * m_buffer
int size(void) const
static int SampleSize(AudioFormat format)
bool m_isConfigured
Definition: audiooutput.h:226
std::chrono::milliseconds m_last_audiotime
void SetTimecode(std::chrono::milliseconds timecode) override
AudioBuffer * m_saveBuffer
bool AddData(void *buffer, int len, std::chrono::milliseconds timecode, int frames) override
Add data to the audiobuffer for playback.
void SetEffDsp(int dsprate) override
QList< AudioBuffer * > m_bufferList
bool AddFrames(void *buffer, int frames, std::chrono::milliseconds timecode) override
Add frames to the audiobuffer for playback.
AudioBuffer * GetData(std::chrono::milliseconds time)
void Reconfigure(const AudioSettings &settings) override
reconfigure sound out for new params
void Reset(void) override
long long GetSamples(std::chrono::milliseconds time)
AudioReencodeBuffer(AudioFormat audio_format, int audio_channels, bool passthru)
AudioFormat m_format
Definition: audiosettings.h:72
static const std::array< const uint64_t, 4 > samples
Definition: element.cpp:46