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