Shaka Player Embedded
apple_audio_renderer.cc
Go to the documentation of this file.
1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
16 
17 #include <AudioToolbox/AudioToolbox.h>
18 #include <glog/logging.h>
19 
20 #include <atomic>
21 #include <list>
22 
23 #include "src/debug/mutex.h"
25 #include "src/media/media_utils.h"
26 #include "src/util/cfref.h"
27 
28 namespace shaka {
29 
30 namespace util {
31 
32 // Add a specialization so a CFRef<T> can be used with a AudioQueue.
33 template <>
34 struct RefTypeTraits<AudioQueueRef> {
35  static constexpr const bool AcquireWithRaw = false;
36 
37  static AudioQueueRef Duplicate(AudioQueueRef arg) {
38  // Cannot duplicate queue objects.
39  LOG(FATAL) << "Not reached";
40  }
41 
42  static void Release(AudioQueueRef arg) {
43  AudioQueueDispose(arg, /* inImmediate= */ true);
44  }
45 };
46 
47 } // namespace util
48 
49 namespace media {
50 
51 namespace {
52 
54 constexpr const size_t kMaxNumBuffers = 8;
55 
56 bool SetSampleFormatFields(SampleFormat format,
57  AudioStreamBasicDescription* desc) {
58  switch (format) {
59  case SampleFormat::PackedU8:
60  case SampleFormat::PlanarU8:
61  desc->mFormatFlags = 0;
62  desc->mBitsPerChannel = 8;
63  return true;
64  case SampleFormat::PackedS16:
65  case SampleFormat::PlanarS16:
66  desc->mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
67  desc->mBitsPerChannel = 16;
68  return true;
69  case SampleFormat::PackedS32:
70  case SampleFormat::PlanarS32:
71  desc->mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
72  desc->mBitsPerChannel = 32;
73  return true;
74  case SampleFormat::PackedS64:
75  case SampleFormat::PlanarS64:
76  desc->mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
77  desc->mBitsPerChannel = 64;
78  return true;
79  case SampleFormat::PackedFloat:
80  case SampleFormat::PlanarFloat:
81  desc->mFormatFlags = kLinearPCMFormatFlagIsFloat;
82  desc->mBitsPerChannel = 32;
83  return true;
84  case SampleFormat::PackedDouble:
85  case SampleFormat::PlanarDouble:
86  desc->mFormatFlags = kLinearPCMFormatFlagIsFloat;
87  desc->mBitsPerChannel = 64;
88  return true;
89 
90  default:
91  LOG(DFATAL) << "Unsupported sample format: " << format;
92  return false;
93  }
94 }
95 
103 class BufferList final {
104  public:
105  BufferList() : mutex_("BufferList"), queue_(nullptr) {}
106  ~BufferList() {
107  Reset();
108  }
109 
110  void SetQueue(AudioQueueRef q) {
111  std::unique_lock<Mutex> lock(mutex_);
112  Reset();
113  queue_ = q;
114  }
115 
116  AudioQueueBufferRef FindBuffer(size_t size) {
117  std::unique_lock<Mutex> lock(mutex_);
118  // Look for a buffer that is the exact requested size; if there isn't one
119  // available, look for one that is larger than needed.
120  auto found = buffers_.end();
121  auto capacity = [](decltype(found) it) {
122  return (*it)->mAudioDataBytesCapacity;
123  };
124  for (auto it = buffers_.begin(); it != buffers_.end(); it++) {
125  if (capacity(it) == size) {
126  found = it;
127  break;
128  } else if (capacity(it) > size &&
129  (found == buffers_.end() || capacity(it) < capacity(found))) {
130  // Use the smallest buffer we can, but favor earlier elements.
131  found = it;
132  }
133  }
134 
135  if (found != buffers_.end()) {
136  auto* ret = *found;
137  buffers_.erase(found);
138  return ret;
139  }
140 
141  AudioQueueBufferRef ret;
142  const auto status = AudioQueueAllocateBuffer(queue_, size, &ret);
143  if (status != 0) {
144  LOG(DFATAL) << "Error creating AudioQueueBuffer: " << status;
145  return nullptr;
146  }
147  return ret;
148  }
149 
150  void ReuseBuffer(AudioQueueBufferRef buffer) {
151  std::unique_lock<Mutex> lock(mutex_);
152  buffers_.emplace_front(buffer);
153  while (buffers_.size() > kMaxNumBuffers) {
154  FreeBuffer(buffers_.back());
155  buffers_.pop_back();
156  }
157  }
158 
159  private:
160  void Reset() {
161  for (auto* buf : buffers_)
162  FreeBuffer(buf);
163  buffers_.clear();
164  }
165 
166  void FreeBuffer(AudioQueueBufferRef buffer) {
167  const auto status = AudioQueueFreeBuffer(queue_, buffer);
168  if (status != 0)
169  LOG(DFATAL) << "Error freeing AudioQueueBuffer: " << status;
170  }
171 
172  Mutex mutex_;
173  AudioQueueRef queue_;
174  std::list<AudioQueueBufferRef> buffers_;
175 };
176 
177 } // namespace
178 
180  public:
181  Impl() : queue_size_(0) {}
182 
183  ~Impl() override {
184  Stop();
185  // Clear the buffer so we get all our buffers back from the device so the
186  // BufferList can free them. This ensures we free the buffers before our
187  // members are destructed.
188  ClearBuffer();
189  }
190 
191  bool InitDevice(std::shared_ptr<DecodedFrame> frame, double volume) override {
192  // Clear the buffer first so we delete any existing buffers with the old
193  // queue.
194  ClearBuffer();
195 
196  AudioStreamBasicDescription desc = {0};
197  if (!SetSampleFormatFields(get<SampleFormat>(frame->format), &desc))
198  return false;
199  desc.mFormatID = kAudioFormatLinearPCM;
200  desc.mSampleRate = frame->stream_info->sample_rate;
201  desc.mChannelsPerFrame = frame->stream_info->channel_count;
202  desc.mFramesPerPacket = 1;
203  desc.mBytesPerFrame = desc.mBitsPerChannel * desc.mChannelsPerFrame / 8;
204  desc.mBytesPerPacket = desc.mBytesPerFrame * desc.mFramesPerPacket;
205 
206  AudioQueueRef q;
207  auto status = AudioQueueNewOutput(
208  &desc, &OnAudioCallback, this, /* inCallbackRunLoop= */ nullptr,
209  /* inCallbackRunLoopMode= */ nullptr, /* inFlags= */ 0, &q);
210  if (status != 0) {
211  LOG(DFATAL) << "Error creating AudioQueue: " << status;
212  return false;
213  }
214 
215  queue_size_.store(0, std::memory_order_relaxed);
216  buffers_.SetQueue(q);
217  // Update queue_ after updating buffers_ so the buffer list can free the
218  // old buffers while the old queue is still valid.
219  queue_ = q;
220  UpdateVolume(volume);
221  return true;
222  }
223 
224  bool AppendBuffer(const uint8_t* data, size_t size) override {
225  auto* buffer = buffers_.FindBuffer(size);
226  if (!buffer)
227  return false;
228 
229  memcpy(buffer->mAudioData, data, size);
230  queue_size_.fetch_add(size, std::memory_order_relaxed);
231  buffer->mAudioDataByteSize = size;
232  buffer->mPacketDescriptionCount = 0;
233  const auto status = AudioQueueEnqueueBuffer(queue_, buffer, 0, nullptr);
234  if (status != 0)
235  LOG(DFATAL) << "Error queuing AudioQueueBuffer: " << status;
236  return status == 0;
237  }
238 
239  void ClearBuffer() override {
240  if (!queue_)
241  return;
242 
243  const auto status = AudioQueueReset(queue_);
244  if (status != 0)
245  LOG(DFATAL) << "Error clearing AudioQueue: " << status;
246  DCHECK_EQ(GetBytesBuffered(), 0); // Should have all buffers returned to us
247  }
248 
249  size_t GetBytesBuffered() const override {
250  return queue_size_.load(std::memory_order_relaxed);
251  }
252 
253  void SetDeviceState(bool is_playing) override {
254  if (!queue_)
255  return;
256 
257  OSStatus status;
258  if (is_playing)
259  status = AudioQueueStart(queue_, nullptr);
260  else
261  status = AudioQueuePause(queue_);
262  if (status != 0)
263  LOG(DFATAL) << "Error changing AudioQueue pause state: " << status;
264  }
265 
266  void UpdateVolume(double volume) override {
267  if (!queue_)
268  return;
269 
270  const auto status = AudioQueueSetParameter(queue_, kAudioQueueParam_Volume,
271  static_cast<float>(volume));
272  if (status != 0)
273  LOG(DFATAL) << "Error setting AudioQueue volume: " << status;
274  }
275 
276  static void OnAudioCallback(void* user_data, AudioQueueRef queue,
277  AudioQueueBufferRef buffer) {
278  auto* impl = reinterpret_cast<Impl*>(user_data);
279  DCHECK_EQ(queue, impl->queue_);
280  DCHECK_LE(buffer->mAudioDataByteSize, impl->GetBytesBuffered());
281  DCHECK_GT(buffer->mAudioDataByteSize, 0);
282 
283  // Now that the buffer is returned to us, it is no longer used by the audio
284  // device, so the buffer size should be reduced.
285  impl->queue_size_.fetch_sub(buffer->mAudioDataByteSize,
286  std::memory_order_relaxed);
287 
288  impl->buffers_.ReuseBuffer(buffer);
289  }
290 
291  util::CFRef<AudioQueueRef> queue_;
292  BufferList buffers_;
293  // Tracks the number of bytes buffered by the audio device.
294  std::atomic<size_t> queue_size_;
295 };
296 
297 AppleAudioRenderer::AppleAudioRenderer() : impl_(new Impl) {}
298 
300 
302  impl_->SetPlayer(player);
303 }
304 
306  impl_->Attach(stream);
307 }
308 
310  impl_->Detach();
311 }
312 
314  return impl_->Volume();
315 }
316 
317 void AppleAudioRenderer::SetVolume(double volume) {
318  impl_->SetVolume(volume);
319 }
320 
322  return impl_->Muted();
323 }
324 
326  impl_->SetMuted(muted);
327 }
328 
329 } // namespace media
330 } // namespace shaka
void Attach(const DecodedStream *stream) override
bool InitDevice(std::shared_ptr< DecodedFrame > frame, double volume) override
std::shared_ptr< shaka::media::DecodedFrame > frame
bool AppendBuffer(const uint8_t *data, size_t size) override
void SetPlayer(const MediaPlayer *player) override
void UpdateVolume(double volume) override
void SetDeviceState(bool is_playing) override
static void OnAudioCallback(void *user_data, AudioQueueRef queue, AudioQueueBufferRef buffer)
static AudioQueueRef Duplicate(AudioQueueRef arg)
void SetVolume(double volume) override
void SetMuted(bool muted) override