xref: /aosp_15_r20/external/openscreen/cast/standalone_receiver/sdl_audio_player.cc (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "cast/standalone_receiver/sdl_audio_player.h"
6 
7 #include <chrono>
8 #include <sstream>
9 #include <utility>
10 
11 #include "absl/types/span.h"
12 #include "cast/standalone_receiver/avcodec_glue.h"
13 #include "util/big_endian.h"
14 #include "util/chrono_helpers.h"
15 #include "util/osp_logging.h"
16 #include "util/trace_logging.h"
17 
18 namespace openscreen {
19 namespace cast {
20 
21 namespace {
22 
23 constexpr char kAudioMediaType[] = "audio";
24 constexpr SDL_AudioFormat kSDLAudioFormatUnknown = 0;
25 
SDLAudioSpecsAreDifferent(const SDL_AudioSpec & a,const SDL_AudioSpec & b)26 bool SDLAudioSpecsAreDifferent(const SDL_AudioSpec& a, const SDL_AudioSpec& b) {
27   return a.freq != b.freq || a.format != b.format || a.channels != b.channels ||
28          a.samples != b.samples;
29 }
30 
31 // Convert |num_channels| separate |planes| of audio, each containing
32 // |num_samples| samples, into a single array of |interleaved| samples. The
33 // memory backing all of the input arrays and the output array is assumed to be
34 // suitably aligned.
35 template <typename Element>
InterleaveAudioSamples(const uint8_t * const planes[],int num_channels,int num_samples,uint8_t * interleaved)36 void InterleaveAudioSamples(const uint8_t* const planes[],
37                             int num_channels,
38                             int num_samples,
39                             uint8_t* interleaved) {
40   TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneReceiver);
41   // Note: This could be optimized with SIMD intrinsics for much better
42   // performance.
43   auto* dest = reinterpret_cast<Element*>(interleaved);
44   for (int ch = 0; ch < num_channels; ++ch) {
45     auto* const src = reinterpret_cast<const Element*>(planes[ch]);
46     for (int i = 0; i < num_samples; ++i) {
47       dest[i * num_channels] = src[i];
48     }
49     ++dest;
50   }
51 }
52 
53 }  // namespace
54 
SDLAudioPlayer(ClockNowFunctionPtr now_function,TaskRunner * task_runner,Receiver * receiver,AudioCodec codec,std::function<void ()> error_callback)55 SDLAudioPlayer::SDLAudioPlayer(ClockNowFunctionPtr now_function,
56                                TaskRunner* task_runner,
57                                Receiver* receiver,
58                                AudioCodec codec,
59                                std::function<void()> error_callback)
60     : SDLPlayerBase(now_function,
61                     task_runner,
62                     receiver,
63                     CodecToString(codec),
64                     std::move(error_callback),
65                     kAudioMediaType) {}
66 
~SDLAudioPlayer()67 SDLAudioPlayer::~SDLAudioPlayer() {
68   if (device_ > 0) {
69     SDL_CloseAudioDevice(device_);
70   }
71 }
72 
RenderNextFrame(const SDLPlayerBase::PresentableFrame & next_frame)73 ErrorOr<Clock::time_point> SDLAudioPlayer::RenderNextFrame(
74     const SDLPlayerBase::PresentableFrame& next_frame) {
75   TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneReceiver);
76   OSP_DCHECK(next_frame.decoded_frame);
77   const AVFrame& frame = *next_frame.decoded_frame;
78 
79   pending_audio_spec_ = device_spec_;
80   pending_audio_spec_.freq = frame.sample_rate;
81 
82   // Punt if the AVFrame's format is not compatible with those supported by SDL.
83   const auto frame_format = static_cast<AVSampleFormat>(frame.format);
84   pending_audio_spec_.format = GetSDLAudioFormat(frame_format);
85   if (pending_audio_spec_.format == kSDLAudioFormatUnknown) {
86     std::ostringstream error;
87     error << "SDL does not support AVSampleFormat " << frame_format;
88     return Error(Error::Code::kUnknownError, error.str());
89   }
90 
91   // Punt if the number of channels is not supported by SDL.
92   constexpr int kSdlSupportedChannelCounts[] = {1, 2, 4, 6};
93   if (std::find(std::begin(kSdlSupportedChannelCounts),
94                 std::end(kSdlSupportedChannelCounts),
95                 frame.channels) == std::end(kSdlSupportedChannelCounts)) {
96     std::ostringstream error;
97     error << "SDL does not support " << frame.channels << " audio channels.";
98     return Error(Error::Code::kUnknownError, error.str());
99   }
100   pending_audio_spec_.channels = frame.channels;
101 
102   // If |device_spec_| is different from what is required, re-compute the sample
103   // buffer size and the amount of time that represents. The |device_spec_| will
104   // be updated to match |pending_audio_spec_| later, in Present().
105   if (SDLAudioSpecsAreDifferent(device_spec_, pending_audio_spec_)) {
106     // Find the smallest power-of-two number of samples that represents at least
107     // 20ms of audio.
108     constexpr auto kMinBufferDuration = milliseconds(20);
109     constexpr auto kOneSecond = seconds(1);
110     const auto required_samples = static_cast<int>(
111         pending_audio_spec_.freq * kMinBufferDuration / kOneSecond);
112     OSP_DCHECK_GE(required_samples, 1);
113     pending_audio_spec_.samples = 1 << av_log2(required_samples);
114     if (pending_audio_spec_.samples < required_samples) {
115       pending_audio_spec_.samples *= 2;
116     }
117 
118     approximate_lead_time_ =
119         (pending_audio_spec_.samples * Clock::to_duration(kOneSecond)) /
120         pending_audio_spec_.freq;
121   }
122 
123   // If the decoded audio is in planar format, interleave it for SDL.
124   const int bytes_per_sample = av_get_bytes_per_sample(frame_format);
125   const int byte_count = frame.nb_samples * frame.channels * bytes_per_sample;
126   if (av_sample_fmt_is_planar(frame_format)) {
127     interleaved_audio_buffer_.resize(byte_count);
128     switch (bytes_per_sample) {
129       case 1:
130         InterleaveAudioSamples<uint8_t>(frame.data, frame.channels,
131                                         frame.nb_samples,
132                                         &interleaved_audio_buffer_[0]);
133         break;
134       case 2:
135         InterleaveAudioSamples<uint16_t>(frame.data, frame.channels,
136                                          frame.nb_samples,
137                                          &interleaved_audio_buffer_[0]);
138         break;
139       case 4:
140         InterleaveAudioSamples<uint32_t>(frame.data, frame.channels,
141                                          frame.nb_samples,
142                                          &interleaved_audio_buffer_[0]);
143         break;
144       default:
145         OSP_NOTREACHED();
146         break;
147     }
148     pending_audio_ = absl::Span<const uint8_t>(interleaved_audio_buffer_);
149   } else {
150     if (!interleaved_audio_buffer_.empty()) {
151       interleaved_audio_buffer_.clear();
152       interleaved_audio_buffer_.shrink_to_fit();
153     }
154     pending_audio_ = absl::Span<const uint8_t>(frame.data[0], byte_count);
155   }
156 
157   // SDL provides no way to query the actual lead time before audio samples will
158   // be output by the sound hardware. The only advice seems to be a quick
159   // comment about "the intent is double buffered audio." Thus, schedule the
160   // "push" of this data to happen such that the audio will be playing out of
161   // the hardware at the intended moment in time.
162   return next_frame.presentation_time - approximate_lead_time_;
163 }
164 
RenderWhileIdle(const PresentableFrame * frame)165 bool SDLAudioPlayer::RenderWhileIdle(const PresentableFrame* frame) {
166   // Do nothing. The SDL audio buffer will underrun and result in silence.
167   return false;
168 }
169 
Present()170 void SDLAudioPlayer::Present() {
171   TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneReceiver);
172   if (state() != kScheduledToPresent) {
173     // In all other states, just do nothing. The SDL audio buffer will underrun
174     // and result in silence.
175     return;
176   }
177 
178   // Re-open audio device, if the audio format has changed.
179   if (SDLAudioSpecsAreDifferent(pending_audio_spec_, device_spec_)) {
180     if (device_ > 0) {
181       SDL_CloseAudioDevice(device_);
182       device_spec_ = SDL_AudioSpec{};
183     }
184 
185     device_ = SDL_OpenAudioDevice(nullptr,  // Pick default device.
186                                   0,        // For playback, not recording.
187                                   &pending_audio_spec_,  // Desired format.
188                                   &device_spec_,  // [output] Obtained format.
189                                   0  // Disallow formats other than desired.
190     );
191     if (device_ <= 0) {
192       std::ostringstream error;
193       error << "SDL_OpenAudioDevice failed: " << SDL_GetError();
194       OnFatalError(error.str());
195       return;
196     }
197     OSP_DCHECK(!SDLAudioSpecsAreDifferent(pending_audio_spec_, device_spec_));
198 
199     constexpr int kSdlResumePlaybackCommand = 0;
200     SDL_PauseAudioDevice(device_, kSdlResumePlaybackCommand);
201   }
202 
203   SDL_QueueAudio(device_, pending_audio_.data(), pending_audio_.size());
204 }
205 
206 // static
GetSDLAudioFormat(AVSampleFormat format)207 SDL_AudioFormat SDLAudioPlayer::GetSDLAudioFormat(AVSampleFormat format) {
208   switch (format) {
209     case AV_SAMPLE_FMT_U8P:
210     case AV_SAMPLE_FMT_U8:
211       return AUDIO_U8;
212 
213     case AV_SAMPLE_FMT_S16P:
214     case AV_SAMPLE_FMT_S16:
215       return IsBigEndianArchitecture() ? AUDIO_S16MSB : AUDIO_S16LSB;
216 
217     case AV_SAMPLE_FMT_S32P:
218     case AV_SAMPLE_FMT_S32:
219       return IsBigEndianArchitecture() ? AUDIO_S32MSB : AUDIO_S32LSB;
220 
221     case AV_SAMPLE_FMT_FLTP:
222     case AV_SAMPLE_FMT_FLT:
223       return IsBigEndianArchitecture() ? AUDIO_F32MSB : AUDIO_F32LSB;
224 
225     default:
226       // Either NONE, or the 64-bit formats are unsupported.
227       break;
228   }
229 
230   return kSDLAudioFormatUnknown;
231 }
232 
233 }  // namespace cast
234 }  // namespace openscreen
235