diff --git a/packages/audioplayers/CHANGELOG.md b/packages/audioplayers/CHANGELOG.md index 4c8862062..372fed936 100644 --- a/packages/audioplayers/CHANGELOG.md +++ b/packages/audioplayers/CHANGELOG.md @@ -1,6 +1,12 @@ -## NEXT +## 3.1.2 * Update code format. +* Handle player resource cleanup in destructor +* Rename Release() to ReleaseMediaSource() for clearer responsibility +* Handle sending null values when exceptions occur in GetDuration() and GetPosition() +* Add logging for unsupported features +* Fix range limitation of SetVolume() and SetPlaybackRate() + ## 3.1.1 diff --git a/packages/audioplayers/README.md b/packages/audioplayers/README.md index 78b34352e..90cc2175e 100644 --- a/packages/audioplayers/README.md +++ b/packages/audioplayers/README.md @@ -11,7 +11,7 @@ This package is not an _endorsed_ implementation of `audioplayers`. Therefore, y ```yaml dependencies: audioplayers: ^6.4.0 - audioplayers_tizen: ^3.1.1 + audioplayers_tizen: ^3.1.2 ``` @@ -70,3 +70,4 @@ For detailed information on Tizen privileges, see [Tizen Docs: API Privileges](h - `onPlayerComplete` event will not be fired when `ReleaseMode` is set to loop which differs from the behavior specified in the [documentation](https://pub.dev/documentation/audioplayers/latest/audioplayers/AudioPlayer/onPlayerComplete.html). And playback rate will reset to 1.0 when audio is replayed. - `setVolume` will have no effect on TV devices. +- `setPlaybackRate` is limited to values between 0.5 and 2.0 on TV and is not supported on RPI. diff --git a/packages/audioplayers/example/lib/main.dart b/packages/audioplayers/example/lib/main.dart index c3fdd26bf..7616f3b20 100644 --- a/packages/audioplayers/example/lib/main.dart +++ b/packages/audioplayers/example/lib/main.dart @@ -15,6 +15,8 @@ const defaultPlayerCount = 4; typedef OnError = void Function(Exception exception); +// ignore_for_file: unnecessary_breaks + /// The app is deployed at: https://bluefireteam.github.io/audioplayers/ void main() { runApp(const MaterialApp(home: _ExampleApp())); diff --git a/packages/audioplayers/pubspec.yaml b/packages/audioplayers/pubspec.yaml index 61045a8c7..0c1f0f92b 100644 --- a/packages/audioplayers/pubspec.yaml +++ b/packages/audioplayers/pubspec.yaml @@ -2,7 +2,7 @@ name: audioplayers_tizen description: Tizen implementation of the audioplayers plugin. homepage: https://github.com/flutter-tizen/plugins repository: https://github.com/flutter-tizen/plugins/tree/master/packages/audioplayers -version: 3.1.1 +version: 3.1.2 environment: sdk: ">=3.1.0 <4.0.0" diff --git a/packages/audioplayers/tizen/src/audio_player.cc b/packages/audioplayers/tizen/src/audio_player.cc index 1e1ec191e..9dd948340 100644 --- a/packages/audioplayers/tizen/src/audio_player.cc +++ b/packages/audioplayers/tizen/src/audio_player.cc @@ -22,7 +22,19 @@ AudioPlayer::AudioPlayer(const std::string &player_id, CreatePlayer(); } -AudioPlayer::~AudioPlayer() { Release(); } +AudioPlayer::~AudioPlayer() { + 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::Play() { player_state_e state = GetPlayerState(); @@ -42,15 +54,17 @@ void AudioPlayer::Play() { throw AudioPlayerError("player_set_memory_buffer failed", get_error_message(ret)); } - } else { + should_play_ = true; + PreparePlayer(); + } else if (url_.size() > 0) { 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(); } - should_play_ = true; - PreparePlayer(); break; } case PLAYER_STATE_READY: @@ -93,23 +107,14 @@ void AudioPlayer::Stop() { seeking_ = false; if (release_mode_ == ReleaseMode::kRelease) { - Release(); + ReleaseMediaSource(); } } -void AudioPlayer::Release() { - 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; - } - released_ = true; +void AudioPlayer::ReleaseMediaSource() { + url_.clear(); + audio_data_.clear(); + ResetPlayer(); } void AudioPlayer::Seek(int32_t position) { @@ -134,6 +139,12 @@ void AudioPlayer::Seek(int32_t position) { } } +void AudioPlayer::OnLog(const std::string &message) { + if (log_listener_) { + log_listener_(player_id_, message); + } +} + void AudioPlayer::SetUrl(const std::string &url) { url_ = url; ResetPlayer(); @@ -164,7 +175,13 @@ void AudioPlayer::SetDataSource(std::vector &data) { } void AudioPlayer::SetVolume(double volume) { + if (volume > 1) { + volume = 1; + } else if (volume < 0) { + volume = 0; + } volume_ = volume; + if (GetPlayerState() != PLAYER_STATE_NONE) { int ret = player_set_volume(player_, volume_, volume_); if (ret != PLAYER_ERROR_NONE) { @@ -175,6 +192,8 @@ void AudioPlayer::SetVolume(double volume) { } void AudioPlayer::SetPlaybackRate(double playback_rate) { + // TODO(seungsoo47): The player_set_playback_rate() API has a limitation of + // 0.5-2x on TV and is not supported on RPI. playback_rate_ = playback_rate; player_state_e state = GetPlayerState(); if (state == PLAYER_STATE_READY || state == PLAYER_STATE_PLAYING || @@ -221,16 +240,6 @@ int AudioPlayer::GetDuration() { } int AudioPlayer::GetCurrentPosition() { - // TODO(jsuya) : When stop() or pause() is called in AudioPlayer 6.1.0, - // PositionUpdater's stopAndUpdate() is called. At this time, getPosition() is - // called, but in ReleaseMode, the player is released after Stop(), so an - // exception is thrown. Since there are differences from the implementation in - // the frontend package, an exception is not thrown in this case. - if (!player_ && released_ && release_mode_ == ReleaseMode::kRelease) { - LOG_ERROR("The player has already been released."); - return 0; - } - int32_t position; int ret = player_get_play_position(player_, &position); if (ret != PLAYER_ERROR_NONE) { @@ -270,8 +279,6 @@ void AudioPlayer::CreatePlayer() { throw AudioPlayerError("player_set_error_cb failed", get_error_message(ret)); } - - released_ = false; } void AudioPlayer::PreparePlayer() { @@ -441,9 +448,7 @@ Eina_Bool AudioPlayer::OnPositionUpdate(void *data) { auto *player = reinterpret_cast(data); try { if (player->IsPlaying()) { - int32_t duration = player->GetDuration(); - player->duration_listener_(player->player_id_, duration); - + player->duration_listener_(player->player_id_, player->GetDuration()); return ECORE_CALLBACK_RENEW; } } catch (const AudioPlayerError &error) { diff --git a/packages/audioplayers/tizen/src/audio_player.h b/packages/audioplayers/tizen/src/audio_player.h index 39ff7cf75..a5c5a6c5e 100644 --- a/packages/audioplayers/tizen/src/audio_player.h +++ b/packages/audioplayers/tizen/src/audio_player.h @@ -38,8 +38,9 @@ class AudioPlayer { void Play(); void Pause(); void Stop(); - void Release(); + void ReleaseMediaSource(); void Seek(int32_t position); // milliseconds + void OnLog(const std::string &message); // If you use HTTP or RTSP, URI must start with "http://" or "rtsp://". // The default protocol is "file://". @@ -83,7 +84,6 @@ class AudioPlayer { bool preparing_ = false; bool seeking_ = false; bool should_play_ = false; - bool released_ = false; Ecore_Timer *timer_ = nullptr; PreparedListener prepared_listener_; diff --git a/packages/audioplayers/tizen/src/audioplayers_tizen_plugin.cc b/packages/audioplayers/tizen/src/audioplayers_tizen_plugin.cc index c61eed417..a4f62a015 100644 --- a/packages/audioplayers/tizen/src/audioplayers_tizen_plugin.cc +++ b/packages/audioplayers/tizen/src/audioplayers_tizen_plugin.cc @@ -192,7 +192,7 @@ class AudioplayersTizenPlugin : public flutter::Plugin { player->Stop(); result->Success(); } else if (method_name == "release") { - player->Release(); + player->ReleaseMediaSource(); result->Success(); } else if (method_name == "seek") { player->Seek(GetRequiredArg(arguments, "position")); @@ -200,6 +200,9 @@ class AudioplayersTizenPlugin : public flutter::Plugin { } else if (method_name == "setVolume") { player->SetVolume(GetRequiredArg(arguments, "volume")); result->Success(); + } else if (method_name == "setBalance") { + player->OnLog("SetBalance() is not supported on Tizen"); + result->NotImplemented(); } else if (method_name == "setSourceUrl") { bool is_local = false; GetValueFromEncodableMap(arguments, "isLocal", is_local); @@ -221,15 +224,35 @@ class AudioplayersTizenPlugin : public flutter::Plugin { player->SetReleaseMode(StringToReleaseMode(release_mode)); result->Success(); } else if (method_name == "getDuration") { - result->Success(flutter::EncodableValue(player->GetDuration())); + // TODO(seungsoo47): If an exception occurs, null is sent. + try { + result->Success(flutter::EncodableValue(player->GetDuration())); + } catch (const AudioPlayerError &error) { + player->OnLog(error.code() + error.message()); + result->Success(flutter::EncodableValue(std::monostate())); + } } else if (method_name == "getCurrentPosition") { - result->Success(flutter::EncodableValue(player->GetCurrentPosition())); + // TODO(seungsoo47): If an exception occurs, null is sent. + try { + result->Success( + flutter::EncodableValue(player->GetCurrentPosition())); + } catch (const AudioPlayerError &error) { + player->OnLog(error.code() + error.message()); + result->Success(flutter::EncodableValue(std::monostate())); + } } else if (method_name == "setPlayerMode") { bool low_latency = GetRequiredArg(arguments, "playerMode") == "PlayerMode.lowLatency"; player->SetLatencyMode(low_latency); result->Success(); + } else if (method_name == "setAudioContext") { + player->OnLog("Setting AudioContext is not supported on Tizen"); + result->NotImplemented(); + } else if (method_name == "emitLog") { + auto message = GetRequiredArg(arguments, "message"); + player->OnLog(message); + result->Success(); } else { result->NotImplemented(); } @@ -252,21 +275,13 @@ class AudioplayersTizenPlugin : public flutter::Plugin { } audio_players_.clear(); } else if (method_name == "setAudioContext") { - const std::string message = - "Setting AudioContext is not supported on Tizen"; - flutter::EncodableMap map = {{flutter::EncodableValue("event"), - flutter::EncodableValue("audio.onLog")}, - {flutter::EncodableValue("value"), - flutter::EncodableValue(message)}}; - global_event_sinks_->Success(flutter::EncodableValue(map)); + OnGlobalLog("Setting AudioContext is not supported on Tizen"); + result->NotImplemented(); + return; } else if (method_name == "emitLog") { if (arguments) { auto message = GetRequiredArg(arguments, "message"); - flutter::EncodableMap map = {{flutter::EncodableValue("event"), - flutter::EncodableValue("audio.onLog")}, - {flutter::EncodableValue("value"), - flutter::EncodableValue(message)}}; - global_event_sinks_->Success(flutter::EncodableValue(map)); + OnGlobalLog(message); } } else if (method_name == "emitError") { if (arguments) { @@ -359,6 +374,14 @@ class AudioplayersTizenPlugin : public flutter::Plugin { event_sinks_.erase(player_id); } + void OnGlobalLog(const std::string &message) { + flutter::EncodableMap map = { + {flutter::EncodableValue("event"), + flutter::EncodableValue("audio.onLog")}, + {flutter::EncodableValue("value"), flutter::EncodableValue(message)}}; + global_event_sinks_->Success(flutter::EncodableValue(map)); + } + std::map> audio_players_; std::map> event_sinks_; std::unique_ptr global_event_sinks_;