Shaka Player Embedded
sdl_audio_renderer.cc
Go to the documentation of this file.
1 // Copyright 2019 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 <SDL2/SDL.h>
18 
19 #include "shaka/utils.h"
21 #include "src/util/utils.h"
22 
23 namespace shaka {
24 namespace media {
25 
26 namespace {
27 
28 bool SDLFormatFromShaka(variant<PixelFormat, SampleFormat> format,
29  SDL_AudioFormat* result) {
30  // Try to use the same format to avoid work by swresample.
31  switch (get<SampleFormat>(format)) {
34  *result = AUDIO_U8;
35  return true;
38  *result = AUDIO_S16SYS;
39  return true;
42  *result = AUDIO_S32SYS;
43  return true;
46  *result = AUDIO_F32SYS;
47  return true;
48 
51  LOG(DFATAL) << "SDL doesn't support double-precision audio.";
52  return false;
53 
56  LOG(DFATAL) << "SDL doesn't support 64-bit audio.";
57  return false;
58 
59  default:
60  LOG(DFATAL) << "Unknown audio sample format: " << format;
61  return false;
62  }
63 }
64 
65 bool InitSdl() {
66  if (!SDL_WasInit(SDL_INIT_AUDIO)) {
67  SDL_SetMainReady();
68  if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
69  LOG(ERROR) << "Error initializing SDL: " << SDL_GetError();
70  return false;
71  }
72  }
73  return true;
74 }
75 
76 } // namespace
77 
79  public:
80  explicit Impl(const std::string& device_name)
81  : device_name_(device_name), audio_device_(0), volume_(0) {
82  // Use "playback" mode on iOS. This ensures the audio remains playing when
83  // locked or muted.
84  SDL_SetHint(SDL_HINT_AUDIO_CATEGORY, "playback");
85  }
86 
87  ~Impl() override {
88  Stop();
89  ResetDevice();
90  }
91 
92  void Detach() override {
94  // We can only open a device once; so once we detach, we should close the
95  // device so another renderer can run.
96  ResetDevice();
97  }
98 
99  private:
100  bool InitDevice(std::shared_ptr<DecodedFrame> frame, double volume) override {
101  if (!InitSdl())
102  return false;
103  ResetDevice();
104 
105  SDL_AudioSpec obtained_audio_spec;
106  SDL_AudioSpec audio_spec;
107  memset(&audio_spec, 0, sizeof(audio_spec));
108  if (!SDLFormatFromShaka(frame->format, &audio_spec.format))
109  return false;
110  audio_spec.freq = frame->stream_info->sample_rate;
111  audio_spec.channels = static_cast<Uint8>(frame->stream_info->channel_count);
112  audio_spec.samples = static_cast<Uint16>(frame->sample_count);
113 
114  const char* device = device_name_.empty() ? nullptr : device_name_.c_str();
115  audio_device_ =
116  SDL_OpenAudioDevice(device, 0, &audio_spec, &obtained_audio_spec, 0);
117  if (audio_device_ == 0) {
118  LOG(DFATAL) << "Error opening audio device: " << SDL_GetError();
119  return false;
120  }
121 
122  format_ = obtained_audio_spec.format;
123  volume_ = volume;
124  return true;
125  }
126 
127  bool AppendBuffer(const uint8_t* data, size_t size) override {
128  std::vector<uint8_t> temp(size);
129  SDL_MixAudioFormat(temp.data(), data, format_, size,
130  static_cast<int>(volume_ * SDL_MIX_MAXVOLUME));
131  if (SDL_QueueAudio(audio_device_, temp.data(), size) != 0) {
132  LOG(DFATAL) << "Error appending audio: " << SDL_GetError();
133  return false;
134  }
135  return true;
136  }
137 
138  void ClearBuffer() override {
139  if (audio_device_ != 0)
140  SDL_ClearQueuedAudio(audio_device_);
141  }
142 
143  size_t GetBytesBuffered() const override {
144  if (audio_device_ != 0)
145  return SDL_GetQueuedAudioSize(audio_device_);
146  else
147  return 0;
148  }
149 
150  void SetDeviceState(bool is_playing) override {
151  if (audio_device_ != 0)
152  SDL_PauseAudioDevice(audio_device_, is_playing ? 0 : 1);
153  }
154 
155  void UpdateVolume(double volume) override {
156  volume_ = volume;
157  }
158 
159  void ResetDevice() {
160  if (audio_device_ > 0) {
161  SDL_CloseAudioDevice(audio_device_);
162  audio_device_ = 0;
163  }
164  }
165 
166  const std::string device_name_;
167  SDL_AudioDeviceID audio_device_;
168  SDL_AudioFormat format_;
169  double volume_;
170 };
171 
172 
173 SdlAudioRenderer::SdlAudioRenderer(const std::string& device_name)
174  : impl_(new Impl(device_name)) {}
176 
177 std::vector<std::string> SdlAudioRenderer::ListDevices() {
178  if (!InitSdl())
179  return {};
180 
181  std::vector<std::string> ret(SDL_GetNumAudioDevices(/* iscapture= */ 0));
182  for (size_t i = 0; i < ret.size(); i++)
183  ret[i] = SDL_GetAudioDeviceName(i, /* iscapture= */ 0);
184  return ret;
185 }
186 
188  impl_->SetPlayer(player);
189 }
191  impl_->Attach(stream);
192 }
194  impl_->Detach();
195 }
196 
197 double SdlAudioRenderer::Volume() const {
198  return impl_->Volume();
199 }
200 void SdlAudioRenderer::SetVolume(double volume) {
201  impl_->SetVolume(volume);
202 }
204  return impl_->Muted();
205 }
206 void SdlAudioRenderer::SetMuted(bool muted) {
207  impl_->SetMuted(muted);
208 }
209 
210 } // namespace media
211 } // namespace shaka
void Attach(const DecodedStream *stream) override
double Volume() const override
void SetVolume(double volume) override
SdlAudioRenderer(const std::string &device_name)
std::shared_ptr< shaka::media::DecodedFrame > frame
void SetPlayer(const MediaPlayer *player) override
Impl(const std::string &device_name)
void SetMuted(bool muted) override
static std::vector< std::string > ListDevices()