1 /*
2  * Copyright (c) 2021-2021 Huawei Device Co., Ltd.
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  *     http://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  */
15 
16 #define HST_LOG_TAG "SdlAudioSinkPlugin"
17 
18 #include "sdl_audio_sink_plugin.h"
19 #include <functional>
20 #include "foundation/cpp_ext/memory_ext.h"
21 #include "foundation/log.h"
22 #include "foundation/osal/utils/util.h"
23 #include "foundation/pre_defines.h"
24 #include "foundation/utils/constants.h"
25 #include "plugin/common/plugin_attr_desc.h"
26 #include "plugin/common/plugin_audio_tags.h"
27 #include "plugin/common/plugin_buffer.h"
28 #include "plugin/common/plugin_time.h"
29 #include "plugins/ffmpeg_adapter/utils/ffmpeg_utils.h"
30 
31 namespace {
32 using namespace OHOS::Media::Plugin;
33 using namespace Sdl;
34 constexpr int MAX_AUDIO_FRAME_SIZE = 192000;
35 constexpr uint32_t DEFAULT_OUTPUT_CHANNELS = 2;
36 constexpr AudioChannelLayout DEFAULT_OUTPUT_CHANNEL_LAYOUT = AudioChannelLayout::STEREO;
37 std::function<void(void*, uint8_t*, int)> g_audioCallback;
38 
RegisterAudioCallback(std::function<void (void *,uint8_t *,int)> callback)39 void RegisterAudioCallback(std::function<void(void*, uint8_t*, int)> callback)
40 {
41     g_audioCallback = std::move(callback);
42 }
SDLAudioCallback(void * userdata,uint8_t * stream,int len)43 void SDLAudioCallback(void* userdata, uint8_t* stream, int len) // NOLINT: void*
44 {
45     g_audioCallback(userdata, stream, len);
46 }
TranslateFormat(AudioSampleFormat format)47 AVSampleFormat TranslateFormat(AudioSampleFormat format)
48 {
49     switch (format) {
50         case AudioSampleFormat::U8:
51             return AV_SAMPLE_FMT_U8;
52         case AudioSampleFormat::U8P:
53             return AV_SAMPLE_FMT_U8P;
54         case AudioSampleFormat::F32:
55             return AV_SAMPLE_FMT_FLT;
56         case AudioSampleFormat::F32P:
57             return AV_SAMPLE_FMT_FLTP;
58         case AudioSampleFormat::F64:
59             return AV_SAMPLE_FMT_DBL;
60         case AudioSampleFormat::F64P:
61             return AV_SAMPLE_FMT_DBLP;
62         case AudioSampleFormat::S32:
63             return AV_SAMPLE_FMT_S32;
64         case AudioSampleFormat::S32P:
65             return AV_SAMPLE_FMT_S32P;
66         case AudioSampleFormat::S16:
67             return AV_SAMPLE_FMT_S16;
68         case AudioSampleFormat::S16P:
69             return AV_SAMPLE_FMT_S16P;
70         default:
71             return AV_SAMPLE_FMT_NONE;
72     }
73 }
74 
IsPlanes(AudioSampleFormat format)75 bool IsPlanes(AudioSampleFormat format)
76 {
77     switch (format) {
78         case AudioSampleFormat::F32P:
79         case AudioSampleFormat::F64P:
80         case AudioSampleFormat::S32P:
81         case AudioSampleFormat::S16P:
82             return true;
83         default:
84             return false;
85     }
86 }
87 
AudioSinkPluginCreator(const std::string & name)88 std::shared_ptr<AudioSinkPlugin> AudioSinkPluginCreator(const std::string& name)
89 {
90     return std::make_shared<SdlAudioSinkPlugin>(name);
91 }
92 
SdlAudioRegister(const std::shared_ptr<Register> & reg)93 const Status SdlAudioRegister(const std::shared_ptr<Register>& reg)
94 {
95     AudioSinkPluginDef definition;
96     definition.name = "sdl_audio_sink";
97     definition.rank = 100; // 100
98     Capability cap(OHOS::Media::MEDIA_MIME_AUDIO_RAW);
99     cap.AppendDiscreteKeys<AudioSampleFormat>(
100         Capability::Key::AUDIO_SAMPLE_FORMAT,
101         {AudioSampleFormat::U8, AudioSampleFormat::U8P, AudioSampleFormat::S8, AudioSampleFormat::S8P,
102          AudioSampleFormat::U16, AudioSampleFormat::U16P, AudioSampleFormat::S16, AudioSampleFormat::S16P,
103          AudioSampleFormat::U32, AudioSampleFormat::U32P, AudioSampleFormat::S32, AudioSampleFormat::S32P,
104          AudioSampleFormat::F32, AudioSampleFormat::F32P, AudioSampleFormat::F64, AudioSampleFormat::F64P});
105     definition.inCaps.emplace_back(cap);
106     definition.creator = AudioSinkPluginCreator;
107     return reg->AddPlugin(definition);
108 }
109 
__anonb2affe2c0202null110 PLUGIN_DEFINITION(SdlAudioSink, LicenseType::LGPL, SdlAudioRegister, [] {});
111 } // namespace
112 
113 namespace OHOS {
114 namespace Media {
115 namespace Plugin {
116 namespace Sdl {
SdlAudioSinkPlugin(std::string name)117 SdlAudioSinkPlugin::SdlAudioSinkPlugin(std::string name)
118     : AudioSinkPlugin(std::move(name)),
119       volume_(SDL_MIX_MAXVOLUME)
120 {
121 }
Init()122 Status SdlAudioSinkPlugin::Init()
123 {
124     std::weak_ptr<SdlAudioSinkPlugin> weakPtr(shared_from_this());
125     RegisterAudioCallback([weakPtr](void* userdata, uint8_t* stream, int len) {
126         auto ptr = weakPtr.lock();
127         if (ptr) {
128             ptr->AudioCallback(userdata, stream, len);
129         }
130     });
131     return Status::OK;
132 }
133 
Deinit()134 Status SdlAudioSinkPlugin::Deinit()
135 {
136     if (resample_) {
137         resample_.reset();
138     }
139     return Status::OK;
140 }
141 
Prepare()142 Status SdlAudioSinkPlugin::Prepare()
143 {
144     MEDIA_LOG_I("SDL SINK Prepare enter...");
145     reSrcFfFmt_ = TranslateFormat(audioFormat_);
146     srcFrameSize_ = av_samples_get_buffer_size(nullptr, channels_, samplesPerFrame_, reSrcFfFmt_, 1);
147     rb = CppExt::make_unique<RingBuffer>(srcFrameSize_ * 10); // 最大缓存10帧
148     rb->Init();
149     if (reSrcFfFmt_ != reFfDestFmt_) {
150         needResample_ = true;
151     }
152     wantedSpec_.freq = sampleRate_;
153     wantedSpec_.format = AUDIO_S16SYS;
154     wantedSpec_.channels = channels_;
155     wantedSpec_.samples = samplesPerFrame_;
156     wantedSpec_.silence = 0;
157     wantedSpec_.callback = SDLAudioCallback;
158     if (SDL_OpenAudio(&wantedSpec_, nullptr) < 0) {
159         MEDIA_LOG_E("sdl cannot open audio with error: " PUBLIC_LOG_S, SDL_GetError());
160         return Status::ERROR_UNKNOWN;
161     }
162     if (bitsPerSample_ == 8 || bitsPerSample_ == 24) { // 8 24
163         needResample_ = true;
164     }
165     if (needResample_) {
166         resample_ = std::make_shared<Ffmpeg::Resample>();
167         Ffmpeg::ResamplePara resamplePara {
168             channels_,
169             sampleRate_,
170             bitsPerSample_,
171             static_cast<int64_t>(channelLayout_),
172             reSrcFfFmt_,
173             samplesPerFrame_,
174             reFfDestFmt_,
175         };
176         FALSE_RETURN_V_MSG(resample_->Init(resamplePara) == Status::OK, Status::ERROR_UNKNOWN, "Resample init error");
177     }
178     return Status::OK;
179 }
180 
Reset()181 Status SdlAudioSinkPlugin::Reset()
182 {
183     if (resample_) {
184         resample_.reset();
185     }
186     return Status::OK;
187 }
188 
Start()189 Status SdlAudioSinkPlugin::Start()
190 {
191     MEDIA_LOG_I("SDL SINK start...");
192     SDL_PauseAudio(0);
193     rb->SetActive(true);
194     return Status::OK;
195 }
196 
Stop()197 Status SdlAudioSinkPlugin::Stop()
198 {
199     MEDIA_LOG_I("SDL SINK Stop...");
200     rb->SetActive(false);
201     SDL_PauseAudio(1);
202     Flush();
203     SDL_CloseAudio();
204     MEDIA_LOG_I("SDL SINK Stop end");
205     return Status::OK;
206 }
207 
GetParameter(Tag tag,ValueType & value)208 Status SdlAudioSinkPlugin::GetParameter(Tag tag, ValueType& value)
209 {
210     switch (tag) {
211         case Tag::AUDIO_OUTPUT_CHANNELS: {
212             value = DEFAULT_OUTPUT_CHANNELS;
213             MEDIA_LOG_D("Get outputChannels: " PUBLIC_LOG_U32, DEFAULT_OUTPUT_CHANNELS);
214             break;
215         }
216         case Tag::AUDIO_OUTPUT_CHANNEL_LAYOUT: {
217             value = DEFAULT_OUTPUT_CHANNEL_LAYOUT;
218             MEDIA_LOG_D("Get outputChannelLayout: " PUBLIC_LOG_U64, DEFAULT_OUTPUT_CHANNEL_LAYOUT);
219             break;
220         }
221         default: {
222             MEDIA_LOG_W("receive one parameter with unconcern key: " PUBLIC_LOG_S, Tag2String(tag));
223             break;
224         }
225     }
226     return Status::OK;
227 }
228 
SetParameter(Tag tag,const ValueType & value)229 Status SdlAudioSinkPlugin::SetParameter(Tag tag, const ValueType& value)
230 {
231 #define RETURN_ERROR_IF_CHECK_ERROR(typenames)                                                                         \
232     if (!Plugin::Any::IsSameTypeWith<typenames>(value)) {                                                              \
233         return Status::ERROR_MISMATCHED_TYPE;                                                                          \
234     }
235     MEDIA_LOG_I("SetParameter entered, key: " PUBLIC_LOG_S, Tag2String(tag));
236     switch (tag) {
237         case Tag::AUDIO_OUTPUT_CHANNELS: {
238             RETURN_ERROR_IF_CHECK_ERROR(uint32_t);
239             channels_ = Plugin::AnyCast<uint32_t>(value);
240             if (channels_ > DEFAULT_OUTPUT_CHANNELS) {
241                 channels_ = DEFAULT_OUTPUT_CHANNELS;
242             }
243             MEDIA_LOG_D("Set outputChannels: " PUBLIC_LOG_U32, channels_);
244             break;
245         }
246         case Tag::AUDIO_SAMPLE_RATE:
247             RETURN_ERROR_IF_CHECK_ERROR(uint32_t);
248             sampleRate_ = Plugin::AnyCast<uint32_t>(value);
249             break;
250         case Tag::AUDIO_SAMPLE_PER_FRAME:
251             RETURN_ERROR_IF_CHECK_ERROR(uint32_t);
252             samplesPerFrame_ = Plugin::AnyCast<uint32_t>(value);
253             break;
254         case Tag::AUDIO_OUTPUT_CHANNEL_LAYOUT: {
255             RETURN_ERROR_IF_CHECK_ERROR(AudioChannelLayout);
256             auto chanLayout = Plugin::AnyCast<AudioChannelLayout>(value);
257             if (chanLayout == AudioChannelLayout::MONO) {
258                 channelLayout_ = AV_CH_LAYOUT_MONO;
259             } else if (chanLayout == AudioChannelLayout::STEREO) {
260                 channelLayout_ = AV_CH_LAYOUT_STEREO;
261             }
262             MEDIA_LOG_D("Set outputChannelLayout: " PUBLIC_LOG_U64, channelLayout_);
263             break;
264         }
265         case Tag::AUDIO_SAMPLE_FORMAT:
266             RETURN_ERROR_IF_CHECK_ERROR(AudioSampleFormat);
267             audioFormat_ = Plugin::AnyCast<AudioSampleFormat>(value);
268             break;
269         case Tag::BITS_PER_CODED_SAMPLE:
270             RETURN_ERROR_IF_CHECK_ERROR(uint32_t);
271             bitsPerSample_ = Plugin::AnyCast<uint32_t>(value);
272             break;
273         default: {
274             MEDIA_LOG_W("receive one parameter with unconcern key: " PUBLIC_LOG_S, Tag2String(tag));
275             break;
276         }
277     }
278     return Status::OK;
279 }
280 
GetAllocator()281 std::shared_ptr<Allocator> SdlAudioSinkPlugin::GetAllocator()
282 {
283     return nullptr;
284 }
285 
SetCallback(Callback * cb)286 Status SdlAudioSinkPlugin::SetCallback(Callback* cb)
287 {
288     UNUSED_VARIABLE(cb);
289     return Status::ERROR_UNIMPLEMENTED;
290 }
291 
GetMute(bool & mute)292 Status SdlAudioSinkPlugin::GetMute(bool& mute)
293 {
294     UNUSED_VARIABLE(mute);
295     return Status::ERROR_UNIMPLEMENTED;
296 }
297 
SetMute(bool mute)298 Status SdlAudioSinkPlugin::SetMute(bool mute)
299 {
300     UNUSED_VARIABLE(mute);
301     return Status::ERROR_UNIMPLEMENTED;
302 }
303 
GetVolume(float & volume)304 Status SdlAudioSinkPlugin::GetVolume(float& volume)
305 {
306     UNUSED_VARIABLE(volume);
307     volume = volume_ * 1.0 / SDL_MIX_MAXVOLUME;
308     return Status::OK;
309 }
310 
SetVolume(float volume)311 Status SdlAudioSinkPlugin::SetVolume(float volume)
312 {
313     UNUSED_VARIABLE(volume);
314     volume_ = static_cast<int>(volume * SDL_MIX_MAXVOLUME);
315     return Status::OK;
316 }
317 
GetSpeed(float & speed)318 Status SdlAudioSinkPlugin::GetSpeed(float& speed)
319 {
320     UNUSED_VARIABLE(speed);
321     return Status::ERROR_UNIMPLEMENTED;
322 }
323 
SetSpeed(float speed)324 Status SdlAudioSinkPlugin::SetSpeed(float speed)
325 {
326     UNUSED_VARIABLE(speed);
327     return Status::ERROR_UNIMPLEMENTED;
328 }
329 
Pause()330 Status SdlAudioSinkPlugin::Pause()
331 {
332     MEDIA_LOG_I("Paused");
333     SDL_PauseAudio(1);
334     return Status::OK;
335 }
336 
Resume()337 Status SdlAudioSinkPlugin::Resume()
338 {
339     MEDIA_LOG_I("Resume");
340     rb->SetActive(true);
341     SDL_PauseAudio(0);
342     return Status::OK;
343 }
344 
GetLatency(uint64_t & nanoSec)345 Status SdlAudioSinkPlugin::GetLatency(uint64_t& nanoSec)
346 {
347     nanoSec = 100 * HST_MSECOND; // 100ms
348     return Status::OK;
349 }
350 
GetFrameSize(size_t & size)351 Status SdlAudioSinkPlugin::GetFrameSize(size_t& size)
352 {
353     UNUSED_VARIABLE(size);
354     return Status::ERROR_UNIMPLEMENTED;
355 }
356 
GetFrameCount(uint32_t & count)357 Status SdlAudioSinkPlugin::GetFrameCount(uint32_t& count)
358 {
359     UNUSED_VARIABLE(count);
360     return Status::ERROR_UNIMPLEMENTED;
361 }
362 
Write(const std::shared_ptr<Buffer> & buffer)363 Status SdlAudioSinkPlugin::Write(const std::shared_ptr<Buffer>& buffer)
364 {
365     MEDIA_LOG_DD("SdlSink Write begin");
366     FALSE_RETURN_V_MSG_W(buffer != nullptr && !buffer->IsEmpty(), Status::OK, "Receive empty buffer.");
367     auto mem = buffer->GetMemory();
368     auto srcBuffer = mem->GetReadOnlyData();
369     auto destBuffer = const_cast<uint8_t*>(srcBuffer);
370     auto srcLength = mem->GetSize();
371     auto destLength = srcLength;
372     if (needResample_ && resample_) {
373         FALSE_LOG(resample_->Convert(srcBuffer, srcLength, destBuffer, destLength) == Status::OK);
374     }
375     MEDIA_LOG_DD("SdlSink Write before ring buffer");
376     rb->WriteBuffer(destBuffer, destLength);
377     MEDIA_LOG_DD("SdlSink Write end");
378     return Status::OK;
379 }
380 
Flush()381 Status SdlAudioSinkPlugin::Flush()
382 {
383     SDL_ClearQueuedAudio(1);
384     rb->SetActive(false);
385     return Status::OK;
386 }
387 
Drain()388 Status SdlAudioSinkPlugin::Drain()
389 {
390     MEDIA_LOG_I("Drain begin");
391     while (rb->GetSize()) {
392         OSAL::SleepFor(10);  // 10
393     }
394     MEDIA_LOG_I("Drain end");
395     return Status::OK;
396 }
397 
AudioCallback(void * userdata,uint8_t * stream,int len)398 void SdlAudioSinkPlugin::AudioCallback(void* userdata, uint8_t* stream, int len) // NOLINT: void*
399 {
400     UNUSED_VARIABLE(userdata);
401     MEDIA_LOG_DD("sdl audio callback begin");
402     if (mixCache_.capacity() < len) {
403         mixCache_.reserve(len);
404     }
405     auto realLen = rb->ReadBuffer(mixCache_.data(), len);
406     if (realLen == 0) {
407         MEDIA_LOG_DD("sdl audio callback end with 0");
408         return;
409     }
410     SDL_memset(stream, 0, len);
411     SDL_MixAudio(stream, mixCache_.data(), realLen, volume_);
412     SDL_PauseAudio(0);
413     MEDIA_LOG_DD("sdl audio callback end with " PUBLIC_LOG_ZU, realLen);
414 }
415 } // namespace Sdl
416 } // namespace Plugin
417 } // namespace Media
418 } // namespace OHOS