From 3c6882d8e3b3e0cdaac3432c94b79654f1060a0f Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Fri, 14 Mar 2025 18:59:43 +0800 Subject: [PATCH 1/2] Add error event --- .../lib/src/video_player_tizen.dart | 5 + .../video_player_avplay/lib/video_player.dart | 16 ++- .../lib/video_player_platform_interface.dart | 88 ++++++++++++++ .../tizen/src/media_player.cc | 49 +++++++- .../tizen/src/plus_player.cc | 109 ++++++++---------- .../tizen/src/video_player.cc | 14 +-- .../tizen/src/video_player.h | 81 ++++++++++++- 7 files changed, 284 insertions(+), 78 deletions(-) diff --git a/packages/video_player_avplay/lib/src/video_player_tizen.dart b/packages/video_player_avplay/lib/src/video_player_tizen.dart index 7dd096340..16b4adf7a 100644 --- a/packages/video_player_avplay/lib/src/video_player_tizen.dart +++ b/packages/video_player_avplay/lib/src/video_player_tizen.dart @@ -340,6 +340,11 @@ class VideoPlayerTizen extends VideoPlayerPlatform { eventType: VideoEventType.isPlayingStateUpdate, isPlaying: map['isPlaying']! as bool, ); + case 'error': + return VideoEvent( + eventType: VideoEventType.error, + playerError: PlayerError.values[map['errorCode']! as int], + ); default: return VideoEvent(eventType: VideoEventType.unknown); } diff --git a/packages/video_player_avplay/lib/video_player.dart b/packages/video_player_avplay/lib/video_player.dart index d0a565296..ef0ee7bec 100644 --- a/packages/video_player_avplay/lib/video_player.dart +++ b/packages/video_player_avplay/lib/video_player.dart @@ -57,6 +57,7 @@ class VideoPlayerValue { this.playbackSpeed = 1.0, this.errorDescription, this.isCompleted = false, + this.playerError = PlayerError.none, }); /// Returns an instance for a video that hasn't been loaded. @@ -135,6 +136,9 @@ class VideoPlayerValue { /// Indicates whether or not the video has been loaded and is ready to play. final bool isInitialized; + /// + final PlayerError? playerError; + /// Indicates whether or not the video is in an error state. If this is true /// [errorDescription] should have information about the problem. bool get hasError => errorDescription != null; @@ -174,6 +178,7 @@ class VideoPlayerValue { double? playbackSpeed, String? errorDescription = _defaultErrorDescription, bool? isCompleted, + PlayerError? playerError, }) { return VideoPlayerValue( duration: duration ?? this.duration, @@ -194,6 +199,7 @@ class VideoPlayerValue { ? errorDescription : this.errorDescription, isCompleted: isCompleted ?? this.isCompleted, + playerError: playerError ?? PlayerError.none, ); } @@ -214,7 +220,8 @@ class VideoPlayerValue { 'volume: $volume, ' 'playbackSpeed: $playbackSpeed, ' 'errorDescription: $errorDescription, ' - 'isCompleted: $isCompleted),'; + 'isCompleted: $isCompleted, ' + 'playerError: $playerError'; } @override @@ -236,7 +243,8 @@ class VideoPlayerValue { volume == other.volume && playbackSpeed == other.playbackSpeed && errorDescription == other.errorDescription && - isCompleted == other.isCompleted; + isCompleted == other.isCompleted && + playerError == other.playerError; @override int get hashCode => Object.hash( @@ -255,6 +263,7 @@ class VideoPlayerValue { playbackSpeed, errorDescription, isCompleted, + playerError, ); } @@ -567,7 +576,8 @@ class VideoPlayerController extends ValueNotifier { } else { value = value.copyWith(isPlaying: event.isPlaying); } - + case VideoEventType.error: + value = value.copyWith(playerError: event.playerError); case VideoEventType.unknown: break; } diff --git a/packages/video_player_avplay/lib/video_player_platform_interface.dart b/packages/video_player_avplay/lib/video_player_platform_interface.dart index 67cdee833..7224498cd 100644 --- a/packages/video_player_avplay/lib/video_player_platform_interface.dart +++ b/packages/video_player_avplay/lib/video_player_platform_interface.dart @@ -434,6 +434,85 @@ enum DashPlayerProperty { dashStreamInfo, } +/// Specifies the player error messages. +enum PlayerError { + /// Operation has successfully completed; no error. + none, + + /// Out of memory. + outOfMemory, + + /// Unable to find the parameter. + invalidParameter, + + /// Unable to find the specified media content. + noSuchFile, + + /// Invalid API Call at the moment. + invalidOperation, + + /// No space left on the device. + fileNoSpaceOnDevice, + + /// Not supported Feature. + featureNotSupportedOnDevice, + + /// Failed to perform seek operation, or seek operation called during an + /// invalid state + seekFailed, + + /// AVPlay API method was called during an invalid state. + invalidState, + + /// Multimedia file type not supported. + notSupportedFile, + + /// Input URI is in an invalid format. + invalidUri, + + /// Sound policy error. + soundPolicy, + + /// Failed multiple attempts to connect to the specified content server. + connectionFailed, + + /// Expired license. + drmExpired, + + /// License for future use. + drmFutureUse, + + /// No license. + drmNoLicense, + + /// Format not permitted. + drmNotPermitted, + + /// Resource limit. + resourceLimit, + + /// Permission denied. + permissionDenied, + + /// Socket connection lost. + serviceDisconnected, + + /// No buffer space available. + bufferSpace, + + /// Not supported audio codec but video can be played. + notSupportedAudioCodec, + + /// Not supported video codec but audio can be played. + notSupportedVideoCodec, + + /// Not supported subtitle format. + notSupportedSubtitle, + + /// Multimedia file format not supported. + notSupportedFormat, +} + /// Event emitted from the platform implementation. @immutable class VideoEvent { @@ -454,6 +533,7 @@ class VideoEvent { this.buffered, this.text, this.isPlaying, + this.playerError, }); /// The type of the event. @@ -484,6 +564,11 @@ class VideoEvent { /// Only used if [eventType] is [VideoEventType.isPlayingStateUpdate]. final bool? isPlaying; + /// Error event of the video. + /// + /// Only used if [eventType] is [VideoEventType.error]. + final PlayerError? playerError; + @override bool operator ==(Object other) { return identical(this, other) || @@ -534,6 +619,9 @@ enum VideoEventType { /// phone calls, or other app media such as music players. isPlayingStateUpdate, + /// Error messages notifications. + error, + /// An unknown event has been received. unknown, } diff --git a/packages/video_player_avplay/tizen/src/media_player.cc b/packages/video_player_avplay/tizen/src/media_player.cc index 30feec95b..8b5ea3f8c 100644 --- a/packages/video_player_avplay/tizen/src/media_player.cc +++ b/packages/video_player_avplay/tizen/src/media_player.cc @@ -10,6 +10,47 @@ #include "log.h" +std::map kMediaPlayerErrorMap = { + {player_error_e::PLAYER_ERROR_NONE, PlayerError::kNone}, + {player_error_e::PLAYER_ERROR_OUT_OF_MEMORY, PlayerError::kOutOfMemory}, + {player_error_e::PLAYER_ERROR_INVALID_PARAMETER, + PlayerError::kInvalidParameter}, + {player_error_e::PLAYER_ERROR_NO_SUCH_FILE, PlayerError::kNoSuchFile}, + {player_error_e::PLAYER_ERROR_INVALID_OPERATION, + PlayerError::kInvalidOperation}, + {player_error_e::PLAYER_ERROR_FILE_NO_SPACE_ON_DEVICE, + PlayerError::kFileNoSpaceOnDevice}, + {player_error_e::PLAYER_ERROR_FEATURE_NOT_SUPPORTED_ON_DEVICE, + PlayerError::kFeatureNotSupportedOnDevice}, + {player_error_e::PLAYER_ERROR_SEEK_FAILED, PlayerError::kSeekFailed}, + {player_error_e::PLAYER_ERROR_INVALID_STATE, PlayerError::kInvalidState}, + {player_error_e::PLAYER_ERROR_NOT_SUPPORTED_FILE, + PlayerError::kNotSupportedFile}, + {player_error_e::PLAYER_ERROR_INVALID_URI, PlayerError::kInvalidUri}, + {player_error_e::PLAYER_ERROR_SOUND_POLICY, PlayerError::kSoundPolicy}, + {player_error_e::PLAYER_ERROR_CONNECTION_FAILED, + PlayerError::kConnectionFailed}, + {player_error_e::PLAYER_ERROR_DRM_EXPIRED, PlayerError::kDrmExpired}, + {player_error_e::PLAYER_ERROR_DRM_NO_LICENSE, PlayerError::kDrmNoLicense}, + {player_error_e::PLAYER_ERROR_DRM_FUTURE_USE, PlayerError::kDrmFutureUse}, + {player_error_e::PLAYER_ERROR_DRM_NOT_PERMITTED, + PlayerError::kDrmNotPermitted}, + {player_error_e::PLAYER_ERROR_RESOURCE_LIMIT, PlayerError::kResourceLimit}, + {player_error_e::PLAYER_ERROR_PERMISSION_DENIED, + PlayerError::kPermissionDenied}, + {player_error_e::PLAYER_ERROR_SERVICE_DISCONNECTED, + PlayerError::kServiceDisconnected}, + {player_error_e::PLAYER_ERROR_BUFFER_SPACE, PlayerError::kBufferSpace}, + {player_error_e::PLAYER_ERROR_NOT_SUPPORTED_AUDIO_CODEC, + PlayerError::kNotSupportedAudioCodec}, + {player_error_e::PLAYER_ERROR_NOT_SUPPORTED_VIDEO_CODEC, + PlayerError::kNotSupportedVideoCodec}, + {player_error_e::PLAYER_ERROR_NOT_SUPPORTED_SUBTITLE, + PlayerError::kNotSupportedSubtitle}, + {player_error_e::PLAYER_ERROR_NOT_SUPPORTED_FORMAT, + PlayerError::kNotSupportedFormat}, +}; + static std::vector split(const std::string &s, char delim) { std::stringstream ss(s); std::string item; @@ -696,8 +737,12 @@ void MediaPlayer::OnError(int error_code, void *user_data) { get_error_message(error_code)); MediaPlayer *self = static_cast(user_data); - self->SendError("Media Player error", - std::string("Error: ") + get_error_message(error_code)); + std::map::iterator iter = + kMediaPlayerErrorMap.find(static_cast(error_code)); + if (iter != kMediaPlayerErrorMap.end()) { + self->SendError( + kMediaPlayerErrorMap[static_cast(error_code)]); + } } void MediaPlayer::OnSubtitleUpdated(unsigned long duration, char *text, diff --git a/packages/video_player_avplay/tizen/src/plus_player.cc b/packages/video_player_avplay/tizen/src/plus_player.cc index 6368918a7..a2cdd38cf 100644 --- a/packages/video_player_avplay/tizen/src/plus_player.cc +++ b/packages/video_player_avplay/tizen/src/plus_player.cc @@ -14,6 +14,41 @@ #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" +std::map kPlusPlayerErrorMap = { + {plusplayer::ErrorType::kNone, PlayerError::kNone}, + {plusplayer::ErrorType::kOutOfMemory, PlayerError::kOutOfMemory}, + {plusplayer::ErrorType::kInvalidParameter, PlayerError::kInvalidParameter}, + {plusplayer::ErrorType::kNoSuchFile, PlayerError::kNoSuchFile}, + {plusplayer::ErrorType::kInvalidOperation, PlayerError::kInvalidOperation}, + {plusplayer::ErrorType::kFileNoSpaceOnDevice, + PlayerError::kFileNoSpaceOnDevice}, + {plusplayer::ErrorType::kFeatureNotSupportedOnDevice, + PlayerError::kFeatureNotSupportedOnDevice}, + {plusplayer::ErrorType::kSeekFailed, PlayerError::kSeekFailed}, + {plusplayer::ErrorType::kInvalidState, PlayerError::kInvalidState}, + {plusplayer::ErrorType::kNotSupportedFile, PlayerError::kNotSupportedFile}, + {plusplayer::ErrorType::kInvalidUri, PlayerError::kInvalidUri}, + {plusplayer::ErrorType::kSoundPolicy, PlayerError::kSoundPolicy}, + {plusplayer::ErrorType::kConnectionFailed, PlayerError::kConnectionFailed}, + {plusplayer::ErrorType::kDrmExpired, PlayerError::kDrmExpired}, + {plusplayer::ErrorType::kDrmNoLicense, PlayerError::kDrmNoLicense}, + {plusplayer::ErrorType::kDrmFutureUse, PlayerError::kDrmFutureUse}, + {plusplayer::ErrorType::kDrmNotPermitted, PlayerError::kDrmNotPermitted}, + {plusplayer::ErrorType::kResourceLimit, PlayerError::kResourceLimit}, + {plusplayer::ErrorType::kPermissionDenied, PlayerError::kPermissionDenied}, + {plusplayer::ErrorType::kServiceDisconnected, + PlayerError::kServiceDisconnected}, + {plusplayer::ErrorType::kBufferSpace, PlayerError::kBufferSpace}, + {plusplayer::ErrorType::kNotSupportedAudioCodec, + PlayerError::kNotSupportedAudioCodec}, + {plusplayer::ErrorType::kNotSupportedVideoCodec, + PlayerError::kNotSupportedVideoCodec}, + {plusplayer::ErrorType::kNotSupportedSubtitle, + PlayerError::kNotSupportedSubtitle}, + {plusplayer::ErrorType::kNotSupportedFormat, + PlayerError::kNotSupportedFormat}, +}; + static std::vector split(const std::string &s, char delim) { std::stringstream ss(s); std::string item; @@ -845,80 +880,26 @@ void PlusPlayer::OnResourceConflicted(void *user_data) { self->SendIsPlayingState(false); } -std::string GetErrorMessage(plusplayer::ErrorType error_code) { - switch (error_code) { - case plusplayer::ErrorType::kNone: - return "Successful"; - case plusplayer::ErrorType::kOutOfMemory: - return "Out of memory"; - case plusplayer::ErrorType::kInvalidParameter: - return "Invalid parameter"; - case plusplayer::ErrorType::kNoSuchFile: - return "No such file or directory"; - case plusplayer::ErrorType::kInvalidOperation: - return "Invalid operation"; - case plusplayer::ErrorType::kFileNoSpaceOnDevice: - return "No space left on the device"; - case plusplayer::ErrorType::kFeatureNotSupportedOnDevice: - return "Not supported file on this device"; - case plusplayer::ErrorType::kSeekFailed: - return "Seek operation failure"; - case plusplayer::ErrorType::kInvalidState: - return "Invalid player state"; - case plusplayer::ErrorType::kNotSupportedFile: - return "File format not supported"; - case plusplayer::ErrorType::kNotSupportedFormat: - return "Not supported media format"; - case plusplayer::ErrorType::kInvalidUri: - return "Invalid URI"; - case plusplayer::ErrorType::kSoundPolicy: - return "Sound policy error"; - case plusplayer::ErrorType::kConnectionFailed: - return "Streaming connection failed"; - case plusplayer::ErrorType::kVideoCaptureFailed: - return "Video capture failed"; - case plusplayer::ErrorType::kDrmExpired: - return "DRM license expired"; - case plusplayer::ErrorType::kDrmNoLicense: - return "DRM no license"; - case plusplayer::ErrorType::kDrmFutureUse: - return "License for future use"; - case plusplayer::ErrorType::kDrmNotPermitted: - return "DRM format not permitted"; - case plusplayer::ErrorType::kResourceLimit: - return "Resource limit"; - case plusplayer::ErrorType::kPermissionDenied: - return "Permission denied"; - case plusplayer::ErrorType::kServiceDisconnected: - return "Service disconnected"; - case plusplayer::ErrorType::kBufferSpace: - return "No buffer space available"; - case plusplayer::ErrorType::kNotSupportedAudioCodec: - return "Not supported audio codec but video can be played"; - case plusplayer::ErrorType::kNotSupportedVideoCodec: - return "Not supported video codec but audio can be played"; - case plusplayer::ErrorType::kNotSupportedSubtitle: - return "Not supported subtitle format"; - default: - return "Not defined error"; - } -} - void PlusPlayer::OnError(const plusplayer::ErrorType &error_code, void *user_data) { LOG_ERROR("[PlusPlayer] Error code: %d", error_code); PlusPlayer *self = reinterpret_cast(user_data); - - self->SendError("[PlusPlayer] error", - std::string("Error: ") + GetErrorMessage(error_code)); + std::map::iterator iter = + kPlusPlayerErrorMap.find(error_code); + if (iter != kPlusPlayerErrorMap.end()) { + self->SendError(kPlusPlayerErrorMap[error_code]); + } } void PlusPlayer::OnErrorMsg(const plusplayer::ErrorType &error_code, const char *error_msg, void *user_data) { LOG_ERROR("[PlusPlayer] Error code: %d, message: %s.", error_code, error_msg); PlusPlayer *self = reinterpret_cast(user_data); - - self->SendError("PlusPlayer error", std::string("Error: ") + error_msg); + std::map::iterator iter = + kPlusPlayerErrorMap.find(error_code); + if (iter != kPlusPlayerErrorMap.end()) { + self->SendError(kPlusPlayerErrorMap[error_code]); + } } void PlusPlayer::OnDrmInitData(int *drm_handle, unsigned int len, diff --git a/packages/video_player_avplay/tizen/src/video_player.cc b/packages/video_player_avplay/tizen/src/video_player.cc index 51d82137c..8bdd702cf 100644 --- a/packages/video_player_avplay/tizen/src/video_player.cc +++ b/packages/video_player_avplay/tizen/src/video_player.cc @@ -173,13 +173,13 @@ void VideoPlayer::SendIsPlayingState(bool is_playing) { PushEvent(flutter::EncodableValue(result)); } -void VideoPlayer::SendError(const std::string &error_code, - const std::string &error_message) { - if (event_sink_) { - std::lock_guard lock(queue_mutex_); - error_event_queue_.push(std::make_pair(error_code, error_message)); - ecore_pipe_write(sink_event_pipe_, nullptr, 0); - } +void VideoPlayer::SendError(const PlayerError &error_code) { + flutter::EncodableMap result = { + {flutter::EncodableValue("event"), flutter::EncodableValue("error")}, + {flutter::EncodableValue("errorCode"), + flutter::EncodableValue(static_cast(error_code))}, + }; + PushEvent(flutter::EncodableValue(result)); } void *VideoPlayer::GetWindowHandle() { diff --git a/packages/video_player_avplay/tizen/src/video_player.h b/packages/video_player_avplay/tizen/src/video_player.h index 1bceae947..58c7ddb08 100644 --- a/packages/video_player_avplay/tizen/src/video_player.h +++ b/packages/video_player_avplay/tizen/src/video_player.h @@ -19,6 +19,84 @@ #include "ecore_wl2_window_proxy.h" #include "messages.h" +enum class PlayerError { + // Operation has successfully completed; no error. + kNone, + + // Out of memory. + kOutOfMemory, + + // Unable to find the parameter. + kInvalidParameter, + + // Unable to find the specified media content. + kNoSuchFile, + + // Invalid API Call at the moment. + kInvalidOperation, + + // No space left on the device. + kFileNoSpaceOnDevice, + + // Not supported Feature. + kFeatureNotSupportedOnDevice, + + // Failed to perform seek operation, or seek operation called during an + // invalid state + kSeekFailed, + + // AVPlay API method was called during an invalid state. + kInvalidState, + + // Multimedia file type not supported. + kNotSupportedFile, + + // Input URI is in an invalid format. + kInvalidUri, + + // Sound policy error. + kSoundPolicy, + + // Failed multiple attempts to connect to the specified content server. + kConnectionFailed, + + // Expired license. + kDrmExpired, + + // License for future use. + kDrmFutureUse, + + // No license. + kDrmNoLicense, + + // Format not permitted. + kDrmNotPermitted, + + // Resource limit. + kResourceLimit, + + // Permission denied. + kPermissionDenied, + + // Socket connection lost. + kServiceDisconnected, + + // No buffer space available. + kBufferSpace, + + // Not supported audio codec but video can be played. + kNotSupportedAudioCodec, + + // Not supported video codec but audio can be played. + kNotSupportedVideoCodec, + + // Not supported subtitle format. + kNotSupportedSubtitle, + + // Multimedia file format not supported. + kNotSupportedFormat, +}; + class VideoPlayer { public: using SeekCompletedCallback = std::function; @@ -76,8 +154,7 @@ class VideoPlayer { void SendSubtitleUpdate(int32_t duration, const std::string &text); void SendPlayCompleted(); void SendIsPlayingState(bool is_playing); - void SendError(const std::string &error_code, - const std::string &error_message); + void SendError(const PlayerError &error_code); std::mutex queue_mutex_; std::unique_ptr ecore_wl2_window_proxy_ = nullptr; From f50f82bfc9c9765927a93183a8559cd487a1e38a Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Mon, 17 Mar 2025 10:11:46 +0800 Subject: [PATCH 2/2] Version up --- packages/video_player_avplay/CHANGELOG.md | 4 ++++ packages/video_player_avplay/README.md | 2 +- packages/video_player_avplay/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/video_player_avplay/CHANGELOG.md b/packages/video_player_avplay/CHANGELOG.md index 341fc692f..8db84e786 100644 --- a/packages/video_player_avplay/CHANGELOG.md +++ b/packages/video_player_avplay/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.18 + +* Add player error event. + ## 0.5.17 * Update readme. diff --git a/packages/video_player_avplay/README.md b/packages/video_player_avplay/README.md index c2c1b07c5..38d5b769f 100644 --- a/packages/video_player_avplay/README.md +++ b/packages/video_player_avplay/README.md @@ -12,7 +12,7 @@ To use this package, add `video_player_avplay` as a dependency in your `pubspec. ```yaml dependencies: - video_player_avplay: ^0.5.17 + video_player_avplay: ^0.5.18 ``` Then you can import `video_player_avplay` in your Dart code: diff --git a/packages/video_player_avplay/pubspec.yaml b/packages/video_player_avplay/pubspec.yaml index 5216287a8..6a7c2567d 100644 --- a/packages/video_player_avplay/pubspec.yaml +++ b/packages/video_player_avplay/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avplay description: Flutter plugin for displaying inline video on Tizen TV devices. homepage: https://github.com/flutter-tizen/plugins repository: https://github.com/flutter-tizen/plugins/tree/master/packages/video_player_avplay -version: 0.5.17 +version: 0.5.18 environment: sdk: ">=3.1.0 <4.0.0"