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 ClearError();
85
86 m_passthru = settings.m_usePassthru;
87 m_channels = settings.m_channels;
91}
92
97{
98 m_eff_audiorate = (dsprate / 100);
99}
100
102{
103 QMutexLocker locker(&m_bufferMutex);
104 for (AudioBuffer *ab : std::as_const(m_bufferList))
105 {
106 delete ab;
107 }
108 m_bufferList.clear();
109}
110
121bool AudioReencodeBuffer::AddFrames(void *buffer, int frames, std::chrono::milliseconds timecode)
122{
123 return AddData(buffer, frames * m_bytes_per_frame, timecode, frames);
124}
125
137bool AudioReencodeBuffer::AddData(void *buffer, int len, std::chrono::milliseconds timecode,
138 int frames)
139{
140 auto *buf = (unsigned char *)buffer;
141
142 // Test if target is using a fixed buffer size.
144 {
145 int index = 0;
146
147 // Target has a fixed buffer size, which may not match len.
148 // Redistribute the bytes into appropriately sized buffers.
149 while (index < len)
150 {
151 // See if we have some saved from last iteration in
152 // m_saveBuffer. If not create a new empty buffer.
153 if (!m_saveBuffer)
155
156 // Use as many of the remaining frames as will fit in the space
157 // left in the buffer.
158 int bufsize = m_saveBuffer->size();
159 int part = std::min(len - index, m_audioFrameSize - bufsize);
160 int out_frames = part / m_bytes_per_frame;
161 timecode += std::chrono::milliseconds(out_frames * 1000 / m_eff_audiorate);
162
163 // Store frames in buffer, basing frame count on number of
164 // bytes, which works only for uncompressed data.
165 m_saveBuffer->appendData(&buf[index], part,
166 out_frames, timecode);
167
168 // If we have filled the buffer...
170 {
171 QMutexLocker locker(&m_bufferMutex);
172
173 // store the buffer
175 // mark m_saveBuffer as emtpy.
176 m_saveBuffer = nullptr;
177 // m_last_audiotime is updated iff we store a buffer.
178 m_last_audiotime = timecode;
179 }
180
181 index += part;
182 }
183 }
184 else
185 {
186 // Target has no fixed buffer size. We can use a simpler algorithm
187 // and use 'frames' directly rather than 'len / m_bytes_per_frame',
188 // thus also covering the passthrough case.
190 timecode += std::chrono::milliseconds(frames * 1000 / m_eff_audiorate);
191 m_saveBuffer->appendData(buf, len, frames, timecode);
192
193 QMutexLocker locker(&m_bufferMutex);
195 m_saveBuffer = nullptr;
196 m_last_audiotime = timecode;
197 }
198
199 return true;
200}
201
202AudioBuffer *AudioReencodeBuffer::GetData(std::chrono::milliseconds time)
203{
204 QMutexLocker locker(&m_bufferMutex);
205
206 if (m_bufferList.isEmpty())
207 return nullptr;
208
209 AudioBuffer *ab = m_bufferList.front();
210
211 if (ab->m_time <= time)
212 {
213 m_bufferList.pop_front();
214 return ab;
215 }
216
217 return nullptr;
218}
219
220long long AudioReencodeBuffer::GetSamples(std::chrono::milliseconds time)
221{
222 QMutexLocker locker(&m_bufferMutex);
223
224 if (m_bufferList.isEmpty())
225 return 0;
226
227 long long samples = 0;
228 for (auto *ab : std::as_const(m_bufferList))
229 {
230 if (ab->m_time <= time)
231 samples += ab->m_frames;
232 else
233 break;
234 }
235 return samples;
236}
237
238void AudioReencodeBuffer::SetTimecode(std::chrono::milliseconds timecode)
239{
240 m_last_audiotime = timecode;
241}
242
243/* vim: set expandtab tabstop=4 shiftwidth=4: */
244
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)
void ClearError(void)
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
static guint32 * tmp
Definition: goom_core.cpp:26