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