diff --git a/packages/audioplayers/tizen/src/audio_player.cc b/packages/audioplayers/tizen/src/audio_player.cc index cc4c4a669..45d34dbd4 100644 --- a/packages/audioplayers/tizen/src/audio_player.cc +++ b/packages/audioplayers/tizen/src/audio_player.cc @@ -1,39 +1,27 @@ #include "audio_player.h" #include "audio_player_error.h" -#include "log.h" AudioPlayer::AudioPlayer(const std::string &player_id, bool low_latency, PreparedListener prepared_listener, - StartPlayingListener start_playing_listener, + UpdatePositionListener update_position_listener, SeekCompletedListener seek_completed_listener, PlayCompletedListener play_completed_listener, - ErrorListener error_listener) { - LOG_INFO("AudioPlayer %s is constructing...", player_id.c_str()); - player_id_ = player_id; - low_latency_ = low_latency; - prepared_listener_ = prepared_listener; - start_playing_listener_ = start_playing_listener; - seek_completed_listener_ = seek_completed_listener; - play_completed_listener_ = play_completed_listener; - error_listener_ = error_listener; - - volume_ = 1.0; - playback_rate_ = 1.0; - release_mode_ = RELEASE; - should_seek_to_ = -1; -} + ErrorListener error_listener) + : player_id_(player_id), + low_latency_(low_latency), + prepared_listener_(prepared_listener), + update_position_listener_(update_position_listener), + seek_completed_listener_(seek_completed_listener), + play_completed_listener_(play_completed_listener), + error_listener_(error_listener) {} -AudioPlayer::~AudioPlayer() { - LOG_INFO("AudioPlayer %s is destructing...", player_id_.c_str()); - Release(); -} +AudioPlayer::~AudioPlayer() { Release(); } void AudioPlayer::Play() { - LOG_INFO("AudioPlayer %s will play audio...", player_id_.c_str()); player_state_e state = GetPlayerState(); if (state == PLAYER_STATE_IDLE && preparing_) { - LOG_DEBUG("player is preparing, play will be called in prepared callback"); + // Player is preparing, play will be called in prepared callback. should_play_ = true; return; } @@ -42,106 +30,116 @@ void AudioPlayer::Play() { CreatePlayer(); } - int result; switch (state) { case PLAYER_STATE_NONE: - case PLAYER_STATE_IDLE: + case PLAYER_STATE_IDLE: { if (audio_data_.size() > 0) { - LOG_DEBUG("set audio buffer, buffer size : %d", audio_data_.size()); - result = player_set_memory_buffer(player_, (void *)audio_data_.data(), - audio_data_.size()); - HandleResult("player_set_memory_buffer", result); + int ret = player_set_memory_buffer(player_, audio_data_.data(), + audio_data_.size()); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_set_memory_buffer failed", + get_error_message(ret)); + } } else { - LOG_DEBUG("set uri (%s)", url_.c_str()); - result = player_set_uri(player_, url_.c_str()); - HandleResult("player_set_uri", result); + int ret = player_set_uri(player_, url_.c_str()); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_set_uri failed", + get_error_message(ret)); + } } should_play_ = true; PreparePlayer(); break; + } case PLAYER_STATE_READY: - case PLAYER_STATE_PAUSED: - result = player_start(player_); - HandleResult("player_start", result); + case PLAYER_STATE_PAUSED: { + int ret = player_start(player_); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_start failed", get_error_message(ret)); + } should_play_ = false; - start_playing_listener_(player_id_); + EmitPositionUpdates(); break; + } default: - LOG_DEBUG("player is already playing audio"); + // Player is already playing audio. break; } } void AudioPlayer::Pause() { - LOG_INFO("AudioPlayer %s is pausing...", player_id_.c_str()); if (GetPlayerState() == PLAYER_STATE_PLAYING) { - int result = player_pause(player_); - HandleResult("player_pause", result); + int ret = player_pause(player_); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_pause failed", get_error_message(ret)); + } } + should_play_ = false; } void AudioPlayer::Stop() { - LOG_INFO("AudioPlayer %s is stopping...", player_id_.c_str()); - if (release_mode_ == RELEASE) { - Release(); - } else { - player_state_e state = GetPlayerState(); - if (state == PLAYER_STATE_PLAYING || state == PLAYER_STATE_PAUSED) { - int result = player_stop(player_); - HandleResult("player_stop", result); + player_state_e state = GetPlayerState(); + if (state == PLAYER_STATE_PLAYING || state == PLAYER_STATE_PAUSED) { + int ret = player_stop(player_); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_stop failed", get_error_message(ret)); } } + should_play_ = false; seeking_ = false; + + if (release_mode_ == ReleaseMode::kRelease) { + Release(); + } } void AudioPlayer::Release() { - LOG_INFO("AudioPlayer %s is releasing...", player_id_.c_str()); - if (player_ != nullptr) { + if (player_) { player_unset_completed_cb(player_); player_unset_interrupted_cb(player_); player_unset_error_cb(player_); player_destroy(player_); player_ = nullptr; } + if (timer_) { + ecore_timer_del(timer_); + timer_ = nullptr; + } } -void AudioPlayer::Seek(int position) { - LOG_INFO("AudioPlayer %s is seeking...", player_id_.c_str()); +void AudioPlayer::Seek(int32_t position) { if (seeking_) { - LOG_DEBUG("player is already seeking, can't seek again"); return; } player_state_e state = GetPlayerState(); if (state == PLAYER_STATE_READY || state == PLAYER_STATE_PLAYING || state == PLAYER_STATE_PAUSED) { - LOG_DEBUG("set play position %d", position); seeking_ = true; - int result = player_set_play_position(player_, position, true, - OnSeekCompleted, (void *)this); - if (result != PLAYER_ERROR_NONE) { + int ret = player_set_play_position(player_, position, true, OnSeekCompleted, + this); + if (ret != PLAYER_ERROR_NONE) { seeking_ = false; - std::string error(get_error_message(result)); - LOG_ERROR("player_set_play_position failed : %s", error.c_str()); - throw AudioPlayerError(error, "player_set_play_position failed"); + throw AudioPlayerError("player_set_play_position failed", + get_error_message(ret)); } } else { - LOG_DEBUG("player is unprepared, do seek in prepared callback"); + // Player is unprepared, do seek in prepared callback. should_seek_to_ = position; } } void AudioPlayer::SetUrl(const std::string &url) { - LOG_INFO("AudioPlayer %s is setting url...", player_id_.c_str()); if (url != url_) { url_ = url; ResetPlayer(); - LOG_DEBUG("set new uri (%s)", url.c_str()); - int result = player_set_uri(player_, url.c_str()); - HandleResult("player_set_uri", result); + int ret = player_set_uri(player_, url.c_str()); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_set_uri failed", get_error_message(ret)); + } PreparePlayer(); } @@ -149,125 +147,149 @@ void AudioPlayer::SetUrl(const std::string &url) { } void AudioPlayer::SetDataSource(std::vector &data) { - LOG_INFO("AudioPlayer %s is setting buffer...", player_id_.c_str()); if (data != audio_data_) { audio_data_.swap(data); ResetPlayer(); - LOG_DEBUG("set audio buffer, buffer size : %d", audio_data_.size()); - int result = player_set_memory_buffer(player_, (void *)audio_data_.data(), - audio_data_.size()); - HandleResult("player_set_memory_buffer", result); + int ret = player_set_memory_buffer(player_, audio_data_.data(), + audio_data_.size()); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_set_memory_buffer failed", + get_error_message(ret)); + } PreparePlayer(); } } void AudioPlayer::SetVolume(double volume) { - LOG_INFO("AudioPlayer %s is setting volume %f...", player_id_.c_str(), - volume); if (volume_ != volume) { volume_ = volume; if (GetPlayerState() != PLAYER_STATE_NONE) { - LOG_DEBUG("set volume : %f", volume_); - int result = player_set_volume(player_, volume_, volume_); - HandleResult("player_set_volume", result); + int ret = player_set_volume(player_, volume_, volume_); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_set_volume failed", + get_error_message(ret)); + } } } } void AudioPlayer::SetPlaybackRate(double rate) { - LOG_INFO("AudioPlayer %s is setting plackback rate %f...", player_id_.c_str(), - rate); if (playback_rate_ != rate) { playback_rate_ = rate; player_state_e state = GetPlayerState(); if (state == PLAYER_STATE_READY || state == PLAYER_STATE_PLAYING || state == PLAYER_STATE_PAUSED) { - LOG_DEBUG("set plackback rate : %f", rate); - int result = player_set_playback_rate(player_, rate); - HandleResult("player_set_playback_rate", result); + int ret = player_set_playback_rate(player_, rate); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_set_playback_rate failed", + get_error_message(ret)); + } } } } void AudioPlayer::SetReleaseMode(ReleaseMode mode) { - LOG_INFO("AudioPlayer %s is setting ReleaseMode %d...", player_id_.c_str(), - mode); if (release_mode_ != mode) { release_mode_ = mode; if (GetPlayerState() != PLAYER_STATE_NONE) { - LOG_DEBUG("set looping : %d", release_mode_ == LOOP); - int result = player_set_looping(player_, (release_mode_ == LOOP)); - HandleResult("player_set_looping", result); + int ret = + player_set_looping(player_, (release_mode_ == ReleaseMode::kLoop)); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_set_looping failed", + get_error_message(ret)); + } } } } int AudioPlayer::GetDuration() { - int duration; - int result = player_get_duration(player_, &duration); - HandleResult("player_get_duration", result); - LOG_INFO("audio (%s) duration: %d", url_.c_str(), duration); + int32_t duration; + int ret = player_get_duration(player_, &duration); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_get_duration failed", + get_error_message(ret)); + } return duration; } int AudioPlayer::GetCurrentPosition() { - int position; - int result = player_get_play_position(player_, &position); - HandleResult("player_get_play_position", result); - LOG_INFO("audio (%s) position: %d", url_.c_str(), position); + int32_t position; + int ret = player_get_play_position(player_, &position); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_get_play_position failed", + get_error_message(ret)); + } return position; } -std::string AudioPlayer::GetPlayerId() const { return player_id_; } - bool AudioPlayer::IsPlaying() { return (GetPlayerState() == PLAYER_STATE_PLAYING); } void AudioPlayer::CreatePlayer() { - LOG_INFO("create audio player..."); should_play_ = false; preparing_ = false; - int result = player_create(&player_); - HandleResult("player_create", result); + int ret = player_create(&player_); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_create failed", get_error_message(ret)); + } if (low_latency_) { - result = player_set_audio_latency_mode(player_, AUDIO_LATENCY_MODE_LOW); - HandleResult("player_set_audio_latency_mode", result); + ret = player_set_audio_latency_mode(player_, AUDIO_LATENCY_MODE_LOW); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_set_audio_latency_mode failed", + get_error_message(ret)); + } } - result = player_set_completed_cb(player_, OnPlayCompleted, (void *)this); - HandleResult("player_set_completed_cb", result); + ret = player_set_completed_cb(player_, OnPlayCompleted, this); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_set_completed_cb failed", + get_error_message(ret)); + } - result = player_set_interrupted_cb(player_, OnInterrupted, (void *)this); - HandleResult("player_set_interrupted_cb", result); + ret = player_set_interrupted_cb(player_, OnInterrupted, this); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_set_interrupted_cb failed", + get_error_message(ret)); + } - result = player_set_error_cb(player_, OnErrorOccurred, (void *)this); - HandleResult("player_set_error_cb", result); + ret = player_set_error_cb(player_, OnError, this); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_set_error_cb failed", + get_error_message(ret)); + } } void AudioPlayer::PreparePlayer() { - LOG_DEBUG("set volume %f", volume_); - int result = player_set_volume(player_, volume_, volume_); - HandleResult("player_set_volume", result); + int ret = player_set_volume(player_, volume_, volume_); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_set_volume failed", get_error_message(ret)); + } - LOG_DEBUG("set looping %d", (release_mode_ == LOOP)); - result = player_set_looping(player_, (release_mode_ == LOOP)); - HandleResult("player_set_looping", result); + ret = player_set_looping(player_, (release_mode_ == ReleaseMode::kLoop)); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_set_looping failed", get_error_message(ret)); + } + + ret = player_prepare_async(player_, OnPrepared, this); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_prepare_async failed", + get_error_message(ret)); + } - LOG_DEBUG("prepare audio player asynchronously"); - result = player_prepare_async(player_, OnPrepared, (void *)this); - HandleResult("player_prepare_async", result); preparing_ = true; seeking_ = false; } +void AudioPlayer::EmitPositionUpdates() { + ecore_main_loop_thread_safe_call_async(StartPositionUpdates, this); +} + void AudioPlayer::ResetPlayer() { - LOG_INFO("reset audio player..."); - int result; player_state_e state = GetPlayerState(); switch (state) { case PLAYER_STATE_NONE: @@ -275,103 +297,128 @@ void AudioPlayer::ResetPlayer() { break; case PLAYER_STATE_IDLE: if (preparing_) { - LOG_DEBUG("player is preparing, unprepare the player"); - result = player_unprepare(player_); - HandleResult("player_unprepare", result); + // Cancel preparing if it's already preparing. + int ret = player_unprepare(player_); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_unprepare failed", + get_error_message(ret)); + } preparing_ = false; } break; case PLAYER_STATE_READY: case PLAYER_STATE_PLAYING: case PLAYER_STATE_PAUSED: - LOG_DEBUG("unprepare audio player"); - result = player_unprepare(player_); - HandleResult("player_unprepare", result); + int ret = player_unprepare(player_); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_unprepare failed", + get_error_message(ret)); + } break; } } player_state_e AudioPlayer::GetPlayerState() { - player_state_e state; - if (player_ == nullptr) { - state = PLAYER_STATE_NONE; - } else { - int result = player_get_state(player_, &state); - if (result != PLAYER_ERROR_NONE) { - state = PLAYER_STATE_NONE; + player_state_e state = PLAYER_STATE_NONE; + if (player_) { + int ret = player_get_state(player_, &state); + if (ret != PLAYER_ERROR_NONE) { + throw AudioPlayerError("player_get_state failed", get_error_message(ret)); } } return state; } -void AudioPlayer::HandleResult(const std::string &func_name, int result) { - if (result != PLAYER_ERROR_NONE) { - std::string error(get_error_message(result)); - LOG_ERROR("%s failed : %s", func_name.c_str(), error.c_str()); - throw AudioPlayerError(error, func_name + " failed"); - } -} - void AudioPlayer::OnPrepared(void *data) { - LOG_INFO("Audio player is prepared"); - AudioPlayer *player = (AudioPlayer *)data; + auto *player = reinterpret_cast(data); player->preparing_ = false; - int duration = 0; - int result = player_get_duration(player->player_, &duration); - if (result == PLAYER_ERROR_NONE) { - player->prepared_listener_(player->player_id_, duration); + try { + player->prepared_listener_(player->player_id_, player->GetDuration()); + } catch (const AudioPlayerError &error) { + player->error_listener_(player->player_id_, error.code()); + return; } - player_set_playback_rate(player->player_, player->playback_rate_); if (player->should_play_) { - LOG_DEBUG("start to play audio"); - result = player_start(player->player_); - if (result == PLAYER_ERROR_NONE) { - player->start_playing_listener_(player->player_id_); + int ret = player_start(player->player_); + if (ret != PLAYER_ERROR_NONE) { + player->error_listener_(player->player_id_, "player_start failed."); + return; } + player->EmitPositionUpdates(); player->should_play_ = false; } if (player->should_seek_to_ > 0) { - LOG_DEBUG("set play position %d", player->should_seek_to_); player->seeking_ = true; - result = player_set_play_position(player->player_, player->should_seek_to_, - true, OnSeekCompleted, data); - if (result != PLAYER_ERROR_NONE) { - LOG_ERROR("failed to set play position"); + int ret = player_set_play_position(player->player_, player->should_seek_to_, + true, OnSeekCompleted, data); + if (ret != PLAYER_ERROR_NONE) { player->seeking_ = false; + player->error_listener_(player->player_id_, + "player_set_play_position failed."); + return; } player->should_seek_to_ = -1; } } void AudioPlayer::OnSeekCompleted(void *data) { - LOG_DEBUG("completed to set position"); - AudioPlayer *player = (AudioPlayer *)data; + auto *player = reinterpret_cast(data); player->seek_completed_listener_(player->player_id_); player->seeking_ = false; } void AudioPlayer::OnPlayCompleted(void *data) { - LOG_DEBUG("completed to play audio"); - AudioPlayer *player = (AudioPlayer *)data; - if (player->release_mode_ != LOOP) { - player->Stop(); + auto *player = reinterpret_cast(data); + try { + if (player->release_mode_ != ReleaseMode::kLoop) { + player->Stop(); + } + player->play_completed_listener_(player->player_id_); + } catch (const AudioPlayerError &error) { + player->error_listener_(player->player_id_, error.code()); } - player->play_completed_listener_(player->player_id_); } void AudioPlayer::OnInterrupted(player_interrupted_code_e code, void *data) { - LOG_ERROR("interruption occurred: %d", code); - AudioPlayer *player = (AudioPlayer *)data; - player->error_listener_(player->player_id_, "player - Interrupted"); + auto *player = reinterpret_cast(data); + player->error_listener_(player->player_id_, "Player interrupted."); +} + +void AudioPlayer::OnError(int code, void *data) { + auto *player = reinterpret_cast(data); + player->error_listener_(player->player_id_, get_error_message(code)); } -void AudioPlayer::OnErrorOccurred(int code, void *data) { - std::string error(get_error_message(code)); - LOG_ERROR("error occurred: %s", error.c_str()); - AudioPlayer *player = (AudioPlayer *)data; - player->error_listener_(player->player_id_, "error occurred: " + error); +void AudioPlayer::StartPositionUpdates(void *data) { + auto *player = reinterpret_cast(data); + if (!player->timer_) { + // The audioplayers app facing package expects position + // update events to fire roughly every 200 milliseconds. + const double kTimeInterval = 0.2; + player->timer_ = ecore_timer_add(kTimeInterval, OnPositionUpdate, data); + if (!player->timer_) { + player->error_listener_(player->player_id_, + "Failed to add a position update timer."); + } + } +} + +Eina_Bool AudioPlayer::OnPositionUpdate(void *data) { + auto *player = reinterpret_cast(data); + try { + if (player->IsPlaying()) { + int32_t duration = player->GetDuration(); + int32_t position = player->GetCurrentPosition(); + player->update_position_listener_(player->player_id_, duration, position); + return ECORE_CALLBACK_RENEW; + } + } catch (const AudioPlayerError &error) { + player->error_listener_(player->player_id_, "Failed to update position."); + } + player->timer_ = nullptr; + return ECORE_CALLBACK_CANCEL; } diff --git a/packages/audioplayers/tizen/src/audio_player.h b/packages/audioplayers/tizen/src/audio_player.h index 037272261..792090c6b 100644 --- a/packages/audioplayers/tizen/src/audio_player.h +++ b/packages/audioplayers/tizen/src/audio_player.h @@ -1,17 +1,20 @@ -#ifndef AUDIO_PLAYER_H_ -#define AUDIO_PLAYER_H_ +#ifndef FLUTTER_PLUGIN_AUDIO_PLAYER_H_ +#define FLUTTER_PLUGIN_AUDIO_PLAYER_H_ +#include #include #include #include #include -#include "audio_player_options.h" +enum class ReleaseMode { kRelease, kLoop, kStop }; using PreparedListener = - std::function; -using StartPlayingListener = std::function; + std::function; +using UpdatePositionListener = + std::function; using SeekCompletedListener = std::function; using PlayCompletedListener = std::function; using ErrorListener = std::function audio_data_; - double volume_; - double playback_rate_; - ReleaseMode release_mode_; - int should_seek_to_; + double volume_ = 1.0; + double playback_rate_ = 1.0; + ReleaseMode release_mode_ = ReleaseMode::kRelease; + int should_seek_to_ = -1; bool preparing_ = false; bool seeking_ = false; bool should_play_ = false; + Ecore_Timer *timer_ = nullptr; PreparedListener prepared_listener_; - StartPlayingListener start_playing_listener_; + UpdatePositionListener update_position_listener_; SeekCompletedListener seek_completed_listener_; PlayCompletedListener play_completed_listener_; ErrorListener error_listener_; }; -#endif // AUDIO_PLAYER_H_ +#endif // FLUTTER_PLUGIN_AUDIO_PLAYER_H_ diff --git a/packages/audioplayers/tizen/src/audio_player_error.h b/packages/audioplayers/tizen/src/audio_player_error.h index 2517f622f..d8216cfbb 100644 --- a/packages/audioplayers/tizen/src/audio_player_error.h +++ b/packages/audioplayers/tizen/src/audio_player_error.h @@ -1,5 +1,5 @@ -#ifndef AUDIO_PLAYER_ERROR_H_ -#define AUDIO_PLAYER_ERROR_H_ +#ifndef FLUTTER_PLUGIN_AUDIO_PLAYER_ERROR_H_ +#define FLUTTER_PLUGIN_AUDIO_PLAYER_ERROR_H_ #include @@ -19,12 +19,12 @@ class AudioPlayerError { return *this; } - std::string GetCode() const { return code_; } - std::string GetMessage() const { return message_; } + std::string code() const { return code_; } + std::string message() const { return message_; } private: std::string code_; std::string message_; }; -#endif // AUDIO_PLAYER_ERROR_H_ +#endif // FLUTTER_PLUGIN_AUDIO_PLAYER_ERROR_H_ diff --git a/packages/audioplayers/tizen/src/audio_player_options.h b/packages/audioplayers/tizen/src/audio_player_options.h deleted file mode 100644 index d757d419b..000000000 --- a/packages/audioplayers/tizen/src/audio_player_options.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef AUDIO_PLAYER_OPTIONS_H_ -#define AUDIO_PLAYER_OPTIONS_H_ - -enum ReleaseMode { RELEASE, LOOP, STOP }; - -#endif // AUDIO_PLAYER_OPTIONS_H_ diff --git a/packages/audioplayers/tizen/src/audioplayers_tizen_plugin.cc b/packages/audioplayers/tizen/src/audioplayers_tizen_plugin.cc index e6d99d548..2aeb0798b 100644 --- a/packages/audioplayers/tizen/src/audioplayers_tizen_plugin.cc +++ b/packages/audioplayers/tizen/src/audioplayers_tizen_plugin.cc @@ -1,194 +1,181 @@ #include "audioplayers_tizen_plugin.h" -#include #include #include #include #include #include +#include +#include +#include #include "audio_player.h" #include "audio_player_error.h" -#include "audio_player_options.h" -#include "log.h" -#define TIMEOUT 0.2 +namespace { + +const char *kInvalidArgument = "Invalid argument"; + +template +bool GetValueFromEncodableMap(const flutter::EncodableMap *map, const char *key, + T &out) { + auto iter = map->find(flutter::EncodableValue(key)); + if (iter != map->end() && !iter->second.IsNull()) { + if (auto *value = std::get_if(&iter->second)) { + out = *value; + return true; + } + } + return false; +} + +template +T GetRequiredArg(const flutter::EncodableMap *arguments, const char *key) { + T value; + if (GetValueFromEncodableMap(arguments, key, value)) { + return value; + } + std::string message = + "No " + std::string(key) + " provided or has invalid type or value."; + throw std::invalid_argument(message); +} + +ReleaseMode StringToReleaseMode(std::string release_mode) { + if (release_mode == "ReleaseMode.RELEASE") { + return ReleaseMode::kRelease; + } else if (release_mode == "ReleaseMode.LOOP") { + return ReleaseMode::kLoop; + } else if (release_mode == "ReleaseMode.STOP") { + return ReleaseMode::kStop; + } + throw std::invalid_argument("Invalid release mode."); +} class AudioplayersTizenPlugin : public flutter::Plugin { public: static void RegisterWithRegistrar(flutter::PluginRegistrar *registrar) { - auto plugin = std::make_unique(registrar); - registrar->AddPlugin(std::move(plugin)); - } - - AudioplayersTizenPlugin(flutter::PluginRegistrar *registrar) { auto channel = std::make_unique>( registrar->messenger(), "xyz.luan/audioplayers", &flutter::StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique(); + channel->SetMethodCallHandler( - [plugin = this](const auto &call, auto result) { - plugin->HandleMethodCall(call, std::move(result)); + [plugin_pointer = plugin.get()](const auto &call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); }); + plugin->channel_ = std::move(channel); - channel_ = std::move(channel); - timer_ = nullptr; + registrar->AddPlugin(std::move(plugin)); } - virtual ~AudioplayersTizenPlugin() { - if (timer_) { - ecore_timer_del(timer_); - timer_ = nullptr; - } - } + AudioplayersTizenPlugin() {} + + virtual ~AudioplayersTizenPlugin() {} private: void HandleMethodCall( const flutter::MethodCall &method_call, std::unique_ptr> result) { - LOG_DEBUG("HandleMethodCall: %s", method_call.method_name().c_str()); - const flutter::EncodableValue *args = method_call.arguments(); - if (std::holds_alternative(*args)) { - flutter::EncodableMap encodables = std::get(*args); - flutter::EncodableValue &player_id_value = - encodables[flutter::EncodableValue("playerId")]; - std::string player_id; - if (std::holds_alternative(player_id_value)) { - player_id = std::get(player_id_value); - LOG_DEBUG("Audio player ID: %s", player_id.c_str()); - } else { - result->Error("Invalid Player ID", "Invalid Player ID for method " + - method_call.method_name()); - return; - } + const auto *arguments = + std::get_if(method_call.arguments()); + if (!arguments) { + result->Error(kInvalidArgument, "No arguments provided."); + return; + } - flutter::EncodableValue &mode_value = - encodables[flutter::EncodableValue("mode")]; - std::string mode; - if (std::holds_alternative(mode_value)) { - mode = std::get(mode_value); - LOG_DEBUG("Audio player Mode: %s", mode.c_str()); - } + std::string player_id, mode; + if (!GetValueFromEncodableMap(arguments, "playerId", player_id)) { + result->Error(kInvalidArgument, "No playerId provided."); + return; + } + GetValueFromEncodableMap(arguments, "mode", mode); + + AudioPlayer *player = GetAudioPlayer(player_id, mode); + + try { + const std::string &method_name = method_call.method_name(); + if (method_name == "play") { + double volume = 0.0; + if (GetValueFromEncodableMap(arguments, "volume", volume)) { + player->SetVolume(volume); + } + + std::string url; + if (GetValueFromEncodableMap(arguments, "url", url)) { + player->SetUrl(url); + } + + player->Play(); + + int32_t position = 0; + if (GetValueFromEncodableMap(arguments, "position", position)) { + player->Seek(position); + } + + result->Success(flutter::EncodableValue(1)); + } else if (method_name == "playBytes") { + double volume = 0.0; + if (GetValueFromEncodableMap(arguments, "volume", volume)) { + player->SetVolume(volume); + } + + std::vector bytes; + if (GetValueFromEncodableMap(arguments, "bytes", bytes)) { + player->SetDataSource(bytes); + } + + player->Play(); - AudioPlayer *player = GetAudioPlayer(player_id, mode); - try { - if (method_call.method_name().compare("play") == 0) { - flutter::EncodableValue &volume = - encodables[flutter::EncodableValue("volume")]; - if (std::holds_alternative(volume)) { - player->SetVolume(std::get(volume)); - } - flutter::EncodableValue &url = - encodables[flutter::EncodableValue("url")]; - if (std::holds_alternative(url)) { - player->SetUrl(std::get(url)); - } - player->Play(); - flutter::EncodableValue &position = - encodables[flutter::EncodableValue("position")]; - if (std::holds_alternative(position)) { - player->Seek(std::get(position)); - } - } else if (method_call.method_name().compare("playBytes") == 0) { - flutter::EncodableValue &volume = - encodables[flutter::EncodableValue("volume")]; - if (std::holds_alternative(volume)) { - player->SetVolume(std::get(volume)); - } - flutter::EncodableValue &bytes = - encodables[flutter::EncodableValue("bytes")]; - if (std::holds_alternative>(bytes)) { - player->SetDataSource(std::get>(bytes)); - } - player->Play(); - flutter::EncodableValue &position = - encodables[flutter::EncodableValue("position")]; - if (std::holds_alternative(position)) { - player->Seek(std::get(position)); - } - } else if (method_call.method_name().compare("resume") == 0) { - player->Play(); - } else if (method_call.method_name().compare("pause") == 0) { - player->Pause(); - } else if (method_call.method_name().compare("stop") == 0) { - player->Stop(); - } else if (method_call.method_name().compare("release") == 0) { - player->Release(); - } else if (method_call.method_name().compare("seek") == 0) { - flutter::EncodableValue &position = - encodables[flutter::EncodableValue("position")]; - if (std::holds_alternative(position)) { - player->Seek(std::get(position)); - } else { - result->Error("Invalid position", - "seek failed because of invalid position"); - } - } else if (method_call.method_name().compare("setVolume") == 0) { - flutter::EncodableValue &volume = - encodables[flutter::EncodableValue("volume")]; - if (std::holds_alternative(volume)) { - player->SetVolume(std::get(volume)); - } else { - result->Error("Invalid volume", - "setVolume failed because of invalid volume"); - } - } else if (method_call.method_name().compare("setUrl") == 0) { - flutter::EncodableValue &url = - encodables[flutter::EncodableValue("url")]; - if (std::holds_alternative(url)) { - player->SetUrl(std::get(url)); - } else { - result->Error("Invalid url", - "SetUrl failed because of invalid url"); - } - } else if (method_call.method_name().compare("setPlaybackRate") == 0) { - flutter::EncodableValue &rate = - encodables[flutter::EncodableValue("playbackRate")]; - if (std::holds_alternative(rate)) { - player->SetPlaybackRate(std::get(rate)); - } else { - result->Error("Invalid rate", - "setPlaybackRate failed because of invalid rate"); - } - } else if (method_call.method_name().compare("setReleaseMode") == 0) { - flutter::EncodableValue &release_mode_value = - encodables[flutter::EncodableValue("releaseMode")]; - if (std::holds_alternative(release_mode_value)) { - std::string release_mode = - std::get(release_mode_value); - if (release_mode.compare("ReleaseMode.RELEASE") == 0) { - player->SetReleaseMode(RELEASE); - } else if (release_mode.compare("ReleaseMode.LOOP") == 0) { - player->SetReleaseMode(LOOP); - } else if (release_mode.compare("ReleaseMode.STOP") == 0) { - player->SetReleaseMode(STOP); - } - } else { - result->Error( - "Invalid ReleaseMode", - "setReleaseMode failed because of invalid ReleaseMode"); - } - } else if (method_call.method_name().compare("getDuration") == 0) { - int duration = player->GetDuration(); - result->Success(flutter::EncodableValue(duration)); - return; - } else if (method_call.method_name().compare("getCurrentPosition") == - 0) { - int position = player->GetCurrentPosition(); - result->Success(flutter::EncodableValue(position)); - return; - } else { - result->NotImplemented(); - return; + int32_t position = 0; + if (GetValueFromEncodableMap(arguments, "position", position)) { + player->Seek(position); } + + result->Success(flutter::EncodableValue(1)); + } else if (method_name == "resume") { + player->Play(); + result->Success(flutter::EncodableValue(1)); + } else if (method_name == "pause") { + player->Pause(); + result->Success(flutter::EncodableValue(1)); + } else if (method_name == "stop") { + player->Stop(); + result->Success(flutter::EncodableValue(1)); + } else if (method_name == "release") { + player->Release(); + result->Success(flutter::EncodableValue(1)); + } else if (method_name == "seek") { + player->Seek(GetRequiredArg(arguments, "position")); result->Success(flutter::EncodableValue(1)); - } catch (const AudioPlayerError &e) { - result->Error(e.GetCode(), e.GetMessage()); + } else if (method_name == "setVolume") { + player->SetVolume(GetRequiredArg(arguments, "volume")); + result->Success(flutter::EncodableValue(1)); + } else if (method_name == "setUrl") { + player->SetUrl(GetRequiredArg(arguments, "url")); + result->Success(flutter::EncodableValue(1)); + } else if (method_name == "setPlaybackRate") { + player->SetPlaybackRate( + GetRequiredArg(arguments, "playbackRate")); + result->Success(flutter::EncodableValue(1)); + } else if (method_name == "setReleaseMode") { + std::string release_mode = + GetRequiredArg(arguments, "releaseMode"); + player->SetReleaseMode(StringToReleaseMode(release_mode)); + result->Success(flutter::EncodableValue(1)); + } else if (method_name == "getDuration") { + result->Success(flutter::EncodableValue(player->GetDuration())); + } else if (method_name == "getCurrentPosition") { + result->Success(flutter::EncodableValue(player->GetCurrentPosition())); + } else { + result->NotImplemented(); } - } else { - result->Error("Invalid arguments", "Invalid arguments for method " + - method_call.method_name()); + } catch (const std::invalid_argument &error) { + result->Error(kInvalidArgument, error.what()); + } catch (const AudioPlayerError &error) { + result->Error(error.code(), error.message()); } } @@ -199,21 +186,16 @@ class AudioplayersTizenPlugin : public flutter::Plugin { return iter->second.get(); } - bool low_latency = false; - LOG_DEBUG("mode is %s", mode.c_str()); - if (mode.compare("PlayerMode.LOW_LATENCY") == 0) { - low_latency = true; - } - - PreparedListener prepared_listener = - [channel = channel_.get()](const std::string &player_id, int duration) { - flutter::EncodableMap wrapped = {{flutter::EncodableValue("playerId"), - flutter::EncodableValue(player_id)}, - {flutter::EncodableValue("value"), - flutter::EncodableValue(duration)}}; - auto arguments = std::make_unique(wrapped); - channel->InvokeMethod("audio.onDuration", std::move(arguments)); - }; + PreparedListener prepared_listener = [channel = channel_.get()]( + const std::string &player_id, + int32_t duration) { + flutter::EncodableMap wrapped = {{flutter::EncodableValue("playerId"), + flutter::EncodableValue(player_id)}, + {flutter::EncodableValue("value"), + flutter::EncodableValue(duration)}}; + auto arguments = std::make_unique(wrapped); + channel->InvokeMethod("audio.onDuration", std::move(arguments)); + }; SeekCompletedListener seek_completed_listener = [channel = channel_.get()](const std::string &player_id) { @@ -225,10 +207,27 @@ class AudioplayersTizenPlugin : public flutter::Plugin { channel->InvokeMethod("audio.onSeekComplete", std::move(arguments)); }; - StartPlayingListener start_playing_listener = - [plugin = this](const std::string &player_id) { - ecore_main_loop_thread_safe_call_async(StartPositionUpdates, - (void *)plugin); + UpdatePositionListener update_position_listener = + [channel = channel_.get()](const std::string &player_id, + const int32_t duration, + const int32_t position) { + flutter::EncodableMap duration_wrapped = { + {flutter::EncodableValue("playerId"), + flutter::EncodableValue(player_id)}, + {flutter::EncodableValue("value"), + flutter::EncodableValue(duration)}}; + channel->InvokeMethod( + "audio.onDuration", + std::make_unique(duration_wrapped)); + + flutter::EncodableMap position_wrapped = { + {flutter::EncodableValue("playerId"), + flutter::EncodableValue(player_id)}, + {flutter::EncodableValue("value"), + flutter::EncodableValue(position)}}; + channel->InvokeMethod( + "audio.onCurrentPosition", + std::make_unique(position_wrapped)); }; PlayCompletedListener play_completed_listener = @@ -252,72 +251,21 @@ class AudioplayersTizenPlugin : public flutter::Plugin { channel->InvokeMethod("audio.onError", std::move(arguments)); }; + bool low_latency = mode == "PlayerMode.LOW_LATENCY"; auto player = std::make_unique( - player_id, low_latency, prepared_listener, start_playing_listener, + player_id, low_latency, prepared_listener, update_position_listener, seek_completed_listener, play_completed_listener, error_listener); audio_players_[player_id] = std::move(player); - return audio_players_[player_id].get(); - } - - static void StartPositionUpdates(void *data) { - AudioplayersTizenPlugin *plugin = (AudioplayersTizenPlugin *)data; - if (!plugin->timer_) { - LOG_DEBUG("add timer to update position of playing audio"); - plugin->timer_ = ecore_timer_add(TIMEOUT, UpdatePosition, data); - if (plugin->timer_ == nullptr) { - LOG_ERROR("failed to add timer for UpdatePosition"); - } - } - } - - static Eina_Bool UpdatePosition(void *data) { - AudioplayersTizenPlugin *plugin = (AudioplayersTizenPlugin *)data; - bool none_playing = true; - auto iter = plugin->audio_players_.begin(); - while (iter != plugin->audio_players_.end()) { - try { - std::string player_id = iter->second->GetPlayerId(); - if (iter->second->IsPlaying()) { - LOG_DEBUG("Audio player %s is playing", player_id.c_str()); - none_playing = false; - flutter::EncodableMap duration = { - {flutter::EncodableValue("playerId"), - flutter::EncodableValue(player_id)}, - {flutter::EncodableValue("value"), - flutter::EncodableValue(iter->second->GetDuration())}}; - plugin->channel_->InvokeMethod( - "audio.onDuration", - std::make_unique(duration)); - flutter::EncodableMap position = { - {flutter::EncodableValue("playerId"), - flutter::EncodableValue(player_id)}, - {flutter::EncodableValue("value"), - flutter::EncodableValue(iter->second->GetCurrentPosition())}}; - plugin->channel_->InvokeMethod( - "audio.onCurrentPosition", - std::make_unique(position)); - } - iter++; - } catch (...) { - LOG_ERROR("failed to update position for player %s", - iter->second->GetPlayerId().c_str()); - } - } - - if (none_playing) { - plugin->timer_ = nullptr; - return ECORE_CALLBACK_CANCEL; - } else { - return ECORE_CALLBACK_RENEW; - } + return audio_players_[player_id].get(); } - Ecore_Timer *timer_; std::unique_ptr> channel_; std::map> audio_players_; }; +} // namespace + void AudioplayersTizenPluginRegisterWithRegistrar( FlutterDesktopPluginRegistrarRef registrar) { AudioplayersTizenPlugin::RegisterWithRegistrar(