Repository: flutter-tizen/plugins
Pull request #825: [video_player_avplay]  Add _suspend and _restore for handing onPause and onResume lifecycle event
Author: login: xiaowei-guan
Labels: name: p: video_player_avplay
Created: 2025-03-04T10:37:35Z | Merged: 2025-04-10T09:41:58Z

https://github.com/flutter-tizen/plugins/issues/823

we add suspend and restore interface for plugin:
1. plugin native side need to re-create player when  power instant off/on.
2. if plugin plays a live stream, plugin native side need to re-create player when receiving lifecycle onPause and onResume.
3. if plugin plays a vod stream, plugin native side  need to call suspend and restore when receiving lifecycle onPause and onResume.

### Test scenario
#### Scenario 1: When the video sample in the example is playing/pause, if you turn off the TV power and turn it back on within 5 seconds.
* If the video type is LIVE, the video should start playing/pause from the LIVE point. (Live)
* If you change the video's URL and resume time, the video should playing from the resume time. (ChangeURLTest)
* Else, the video should start playing/pause from the point you were watching. (MP4, HLS, DASH, and others)
#### Scenario 2: When the video sample in the example is playing/pause, if the player preempted by other apps and then returns.
*  Same with 'Scenario 1'


Diff:
diff --git a/packages/video_player_avplay/CHANGELOG.md b/packages/video_player_avplay/CHANGELOG.md
index 6f3b035bf..9fcac8d4d 100644
--- a/packages/video_player_avplay/CHANGELOG.md
+++ b/packages/video_player_avplay/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.5.20
+
+* Add suspend and restore interface.
+
 ## 0.5.19
 
 * Upgrade native player
diff --git a/packages/video_player_avplay/README.md b/packages/video_player_avplay/README.md
index 3d455d82f..5c90afec6 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.19
+  video_player_avplay: ^0.5.20
 ```
 
 Then you can import `video_player_avplay` in your Dart code:
diff --git a/packages/video_player_avplay/example/lib/main.dart b/packages/video_player_avplay/example/lib/main.dart
index 770e10d92..16e4b18c9 100644
--- a/packages/video_player_avplay/example/lib/main.dart
+++ b/packages/video_player_avplay/example/lib/main.dart
@@ -22,7 +22,7 @@ class _App extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return DefaultTabController(
-      length: 7,
+      length: 9,
       child: Scaffold(
         key: const ValueKey<String>('home_page'),
         appBar: AppBar(
@@ -37,6 +37,8 @@ class _App extends StatelessWidget {
               Tab(icon: Icon(Icons.cloud), text: 'DRM PlayReady'),
               Tab(icon: Icon(Icons.cloud), text: 'Track'),
               Tab(icon: Icon(Icons.cloud), text: 'Asset'),
+              Tab(icon: Icon(Icons.live_tv), text: 'Live'),
+              Tab(icon: Icon(Icons.local_florist), text: 'ChangeURLTest'),
             ],
           ),
         ),
@@ -49,6 +51,8 @@ class _App extends StatelessWidget {
             _DrmRemoteVideo2(),
             _TrackTest(),
             _AssetVideo(),
+            _LiveRemoteVideo(),
+            _TestRemoteVideo(),
           ],
         ),
       ),
@@ -748,3 +752,142 @@ class _GetTextTrackButton extends StatelessWidget {
     );
   }
 }
+
+class _LiveRemoteVideo extends StatefulWidget {
+  @override
+  State<_LiveRemoteVideo> createState() => _LiveRomoteVideoState();
+}
+
+class _LiveRomoteVideoState extends State<_LiveRemoteVideo> {
+  late VideoPlayerController _controller;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = VideoPlayerController.network(
+      'https://hlive.ktv.go.kr/live/klive_h.stream/playlist.m3u8',
+    );
+
+    _controller.addListener(() {
+      if (_controller.value.hasError) {
+        print(_controller.value.errorDescription);
+      }
+      setState(() {});
+    });
+    _controller.setLooping(true);
+    _controller.initialize().then((_) => setState(() {}));
+    _controller.play();
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return SingleChildScrollView(
+      child: Column(
+        children: <Widget>[
+          Container(padding: const EdgeInsets.only(top: 20.0)),
+          const Text('Playing Live TV'),
+          Container(
+            padding: const EdgeInsets.all(20),
+            child: AspectRatio(
+              aspectRatio: _controller.value.aspectRatio,
+              child: Stack(
+                alignment: Alignment.bottomCenter,
+                children: <Widget>[
+                  VideoPlayer(_controller),
+                  ClosedCaption(text: _controller.value.caption.text),
+                  _ControlsOverlay(controller: _controller),
+                  VideoProgressIndicator(_controller, allowScrubbing: true),
+                ],
+              ),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}
+
+class _TestRemoteVideo extends StatefulWidget {
+  @override
+  State<_TestRemoteVideo> createState() => _TestRemoteVideoState();
+}
+
+class _TestRemoteVideoState extends State<_TestRemoteVideo> {
+  late VideoPlayerController _controller;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = VideoPlayerController.network(
+      'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8',
+    );
+
+    _controller.addListener(() {
+      if (_controller.value.hasError) {
+        print(_controller.value.errorDescription);
+      }
+      setState(() {});
+    });
+    _controller.setLooping(true);
+    _controller.initialize().then((_) => setState(() {}));
+    _controller.play();
+    _controller.setRestoreData(
+      restoreDataSource: restoreDataSource,
+      resumeTime: restoreTime,
+    );
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+
+  DataSource restoreDataSource() {
+    final DataSource dataSource = DataSource(
+      sourceType: DataSourceType.network,
+      uri: 'https://media.w3.org/2010/05/bunny/trailer.mp4',
+    );
+    return dataSource;
+  }
+
+  int restoreTime() {
+    /// if resumeTime >= 0 , it will restore from resumeTime
+    /// if resumeTime is not set or <0, it will restore from the time when suspend is called
+    const int resumeTime = 0;
+    return resumeTime;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return SingleChildScrollView(
+      child: Column(
+        children: <Widget>[
+          Container(padding: const EdgeInsets.only(top: 20.0)),
+          const Text('ChangeURLTest'),
+          Container(
+            padding: const EdgeInsets.all(20),
+            child: AspectRatio(
+              aspectRatio: _controller.value.aspectRatio,
+              child: Stack(
+                alignment: Alignment.bottomCenter,
+                children: <Widget>[
+                  VideoPlayer(_controller),
+                  ClosedCaption(text: _controller.value.caption.text),
+                  _ControlsOverlay(controller: _controller),
+                  VideoProgressIndicator(_controller, allowScrubbing: true),
+                ],
+              ),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/packages/video_player_avplay/lib/src/messages.g.dart b/packages/video_player_avplay/lib/src/messages.g.dart
index 68cb60ee5..2b570e60d 100644
--- a/packages/video_player_avplay/lib/src/messages.g.dart
+++ b/packages/video_player_avplay/lib/src/messages.g.dart
@@ -467,57 +467,60 @@ class _VideoPlayerAvplayApiCodec extends StandardMessageCodec {
     } else if (value is CreateMessage) {
       buffer.putUint8(129);
       writeValue(buffer, value.encode());
-    } else if (value is DashPropertyMapMessage) {
+    } else if (value is CreateMessage) {
       buffer.putUint8(130);
       writeValue(buffer, value.encode());
-    } else if (value is DashPropertyTypeListMessage) {
+    } else if (value is DashPropertyMapMessage) {
       buffer.putUint8(131);
       writeValue(buffer, value.encode());
-    } else if (value is DisplayModeMessage) {
+    } else if (value is DashPropertyTypeListMessage) {
       buffer.putUint8(132);
       writeValue(buffer, value.encode());
-    } else if (value is DurationMessage) {
+    } else if (value is DisplayModeMessage) {
       buffer.putUint8(133);
       writeValue(buffer, value.encode());
-    } else if (value is GeometryMessage) {
+    } else if (value is DurationMessage) {
       buffer.putUint8(134);
       writeValue(buffer, value.encode());
-    } else if (value is LoopingMessage) {
+    } else if (value is GeometryMessage) {
       buffer.putUint8(135);
       writeValue(buffer, value.encode());
-    } else if (value is MixWithOthersMessage) {
+    } else if (value is LoopingMessage) {
       buffer.putUint8(136);
       writeValue(buffer, value.encode());
-    } else if (value is PlaybackSpeedMessage) {
+    } else if (value is MixWithOthersMessage) {
       buffer.putUint8(137);
       writeValue(buffer, value.encode());
-    } else if (value is PlayerMessage) {
+    } else if (value is PlaybackSpeedMessage) {
       buffer.putUint8(138);
       writeValue(buffer, value.encode());
-    } else if (value is PositionMessage) {
+    } else if (value is PlayerMessage) {
       buffer.putUint8(139);
       writeValue(buffer, value.encode());
-    } else if (value is RotationMessage) {
+    } else if (value is PositionMessage) {
       buffer.putUint8(140);
       writeValue(buffer, value.encode());
-    } else if (value is SelectedTracksMessage) {
+    } else if (value is RotationMessage) {
       buffer.putUint8(141);
       writeValue(buffer, value.encode());
-    } else if (value is StreamingPropertyMessage) {
+    } else if (value is SelectedTracksMessage) {
       buffer.putUint8(142);
       writeValue(buffer, value.encode());
-    } else if (value is StreamingPropertyTypeMessage) {
+    } else if (value is StreamingPropertyMessage) {
       buffer.putUint8(143);
       writeValue(buffer, value.encode());
-    } else if (value is TrackMessage) {
+    } else if (value is StreamingPropertyTypeMessage) {
       buffer.putUint8(144);
       writeValue(buffer, value.encode());
-    } else if (value is TrackTypeMessage) {
+    } else if (value is TrackMessage) {
       buffer.putUint8(145);
       writeValue(buffer, value.encode());
-    } else if (value is VolumeMessage) {
+    } else if (value is TrackTypeMessage) {
       buffer.putUint8(146);
       writeValue(buffer, value.encode());
+    } else if (value is VolumeMessage) {
+      buffer.putUint8(147);
+      writeValue(buffer, value.encode());
     } else {
       super.writeValue(buffer, value);
     }
@@ -531,38 +534,40 @@ class _VideoPlayerAvplayApiCodec extends StandardMessageCodec {
       case 129:
         return CreateMessage.decode(readValue(buffer)!);
       case 130:
-        return DashPropertyMapMessage.decode(readValue(buffer)!);
+        return CreateMessage.decode(readValue(buffer)!);
       case 131:
-        return DashPropertyTypeListMessage.decode(readValue(buffer)!);
+        return DashPropertyMapMessage.decode(readValue(buffer)!);
       case 132:
-        return DisplayModeMessage.decode(readValue(buffer)!);
+        return DashPropertyTypeListMessage.decode(readValue(buffer)!);
       case 133:
-        return DurationMessage.decode(readValue(buffer)!);
+        return DisplayModeMessage.decode(readValue(buffer)!);
       case 134:
-        return GeometryMessage.decode(readValue(buffer)!);
+        return DurationMessage.decode(readValue(buffer)!);
       case 135:
-        return LoopingMessage.decode(readValue(buffer)!);
+        return GeometryMessage.decode(readValue(buffer)!);
       case 136:
-        return MixWithOthersMessage.decode(readValue(buffer)!);
+        return LoopingMessage.decode(readValue(buffer)!);
       case 137:
-        return PlaybackSpeedMessage.decode(readValue(buffer)!);
+        return MixWithOthersMessage.decode(readValue(buffer)!);
       case 138:
-        return PlayerMessage.decode(readValue(buffer)!);
+        return PlaybackSpeedMessage.decode(readValue(buffer)!);
       case 139:
-        return PositionMessage.decode(readValue(buffer)!);
+        return PlayerMessage.decode(readValue(buffer)!);
       case 140:
-        return RotationMessage.decode(readValue(buffer)!);
+        return PositionMessage.decode(readValue(buffer)!);
       case 141:
-        return SelectedTracksMessage.decode(readValue(buffer)!);
+        return RotationMessage.decode(readValue(buffer)!);
       case 142:
-        return StreamingPropertyMessage.decode(readValue(buffer)!);
+        return SelectedTracksMessage.decode(readValue(buffer)!);
       case 143:
-        return StreamingPropertyTypeMessage.decode(readValue(buffer)!);
+        return StreamingPropertyMessage.decode(readValue(buffer)!);
       case 144:
-        return TrackMessage.decode(readValue(buffer)!);
+        return StreamingPropertyTypeMessage.decode(readValue(buffer)!);
       case 145:
-        return TrackTypeMessage.decode(readValue(buffer)!);
+        return TrackMessage.decode(readValue(buffer)!);
       case 146:
+        return TrackTypeMessage.decode(readValue(buffer)!);
+      case 147:
         return VolumeMessage.decode(readValue(buffer)!);
       default:
         return super.readValueOfType(type, buffer);
@@ -1164,6 +1169,59 @@ class VideoPlayerAvplayApi {
     }
   }
 
+  Future<void> suspend(int arg_playerId) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+      'dev.flutter.pigeon.video_player_avplay.VideoPlayerAvplayApi.suspend',
+      codec,
+      binaryMessenger: _binaryMessenger,
+    );
+    final List<Object?>? replyList =
+        await channel.send(<Object?>[arg_playerId]) as List<Object?>?;
+    if (replyList == null) {
+      throw PlatformException(
+        code: 'channel-error',
+        message: 'Unable to establish connection on channel.',
+      );
+    } else if (replyList.length > 1) {
+      throw PlatformException(
+        code: replyList[0]! as String,
+        message: replyList[1] as String?,
+        details: replyList[2],
+      );
+    } else {
+      return;
+    }
+  }
+
+  Future<void> restore(
+    int arg_playerId,
+    CreateMessage? arg_msg,
+    int arg_resumeTime,
+  ) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+      'dev.flutter.pigeon.video_player_avplay.VideoPlayerAvplayApi.restore',
+      codec,
+      binaryMessenger: _binaryMessenger,
+    );
+    final List<Object?>? replyList =
+        await channel.send(<Object?>[arg_playerId, arg_msg, arg_resumeTime])
+            as List<Object?>?;
+    if (replyList == null) {
+      throw PlatformException(
+        code: 'channel-error',
+        message: 'Unable to establish connection on channel.',
+      );
+    } else if (replyList.length > 1) {
+      throw PlatformException(
+        code: replyList[0]! as String,
+        message: replyList[1] as String?,
+        details: replyList[2],
+      );
+    } else {
+      return;
+    }
+  }
+
   Future<bool> setData(DashPropertyMapMessage arg_msg) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
       'dev.flutter.pigeon.video_player_avplay.VideoPlayerAvplayApi.setData',
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 00cc7a0de..5d98ab1b2 100644
--- a/packages/video_player_avplay/lib/src/video_player_tizen.dart
+++ b/packages/video_player_avplay/lib/src/video_player_tizen.dart
@@ -253,6 +253,48 @@ class VideoPlayerTizen extends VideoPlayerPlatform {
     );
   }
 
+  @override
+  Future<void> suspend(int playerId) {
+    return _api.suspend(playerId);
+  }
+
+  @override
+  Future<void> restore(
+    int playerId, {
+    DataSource? dataSource,
+    int resumeTime = -1,
+  }) {
+    final CreateMessage message = CreateMessage();
+
+    if (dataSource != null) {
+      switch (dataSource.sourceType) {
+        case DataSourceType.asset:
+          message.asset = dataSource.asset;
+          message.packageName = dataSource.package;
+        case DataSourceType.network:
+          message.uri = dataSource.uri;
+          message.formatHint = _videoFormatStringMap[dataSource.formatHint];
+          message.httpHeaders = dataSource.httpHeaders;
+          message.drmConfigs = dataSource.drmConfigs?.toMap();
+          message.playerOptions = dataSource.playerOptions;
+          message.streamingProperty =
+              dataSource.streamingProperty == null
+                  ? null
+                  : <String, String>{
+                    for (final MapEntry<StreamingPropertyType, String> entry
+                        in dataSource.streamingProperty!.entries)
+                      _streamingPropertyType[entry.key]!: entry.value,
+                  };
+        case DataSourceType.file:
+          message.uri = dataSource.uri;
+        case DataSourceType.contentUri:
+          message.uri = dataSource.uri;
+      }
+    }
+
+    return _api.restore(playerId, message, resumeTime);
+  }
+
   @override
   Future<bool> setData(int playerId, Map<DashPlayerProperty, Object> data) {
     return _api.setData(
@@ -358,9 +400,16 @@ class VideoPlayerTizen extends VideoPlayerPlatform {
       final Map<dynamic, dynamic> map = event as Map<dynamic, dynamic>;
       switch (map['event']) {
         case 'initialized':
+        case 'restored':
           final List<dynamic>? durationVal = map['duration'] as List<dynamic>?;
+          VideoEventType videoEventType;
+          if (map['event'] == 'initialized') {
+            videoEventType = VideoEventType.initialized;
+          } else {
+            videoEventType = VideoEventType.restored;
+          }
           return VideoEvent(
-            eventType: VideoEventType.initialized,
+            eventType: videoEventType,
             duration: DurationRange(
               Duration(milliseconds: durationVal?[0] as int),
               Duration(milliseconds: durationVal?[1] as int),
diff --git a/packages/video_player_avplay/lib/video_player.dart b/packages/video_player_avplay/lib/video_player.dart
index 408d7281f..07be76809 100644
--- a/packages/video_player_avplay/lib/video_player.dart
+++ b/packages/video_player_avplay/lib/video_player.dart
@@ -22,6 +22,12 @@ export 'src/closed_caption_file.dart';
 export 'src/drm_configs.dart';
 export 'src/tracks.dart';
 
+/// This will be used to set the ResumeTime when player restore.
+typedef RestoreTimeCallback = int Function();
+
+/// This will be used to set the RecreateMessage when player restore.
+typedef RestoreDataSourceCallback = DataSource Function();
+
 VideoPlayerPlatform? _lastVideoPlayerPlatform;
 
 VideoPlayerPlatform get _videoPlayerPlatform {
@@ -415,6 +421,8 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
   Completer<void>? _creatingCompleter;
   StreamSubscription<dynamic>? _eventSubscription;
   _VideoAppLifeCycleObserver? _lifeCycleObserver;
+  RestoreDataSourceCallback? _onRestoreDataSource;
+  RestoreTimeCallback? _onRestoreTime;
 
   /// The id of a player that hasn't been initialized.
   @visibleForTesting
@@ -509,6 +517,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
 
       switch (event.eventType) {
         case VideoEventType.initialized:
+        case VideoEventType.restored:
           value = value.copyWith(
             duration: event.duration,
             size: event.size,
@@ -516,22 +525,29 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
             errorDescription: null,
             isCompleted: false,
           );
-          assert(
-            !initializingCompleter.isCompleted,
-            'VideoPlayerController already initialized. This is typically a '
-            'sign that an implementation of the VideoPlayerPlatform '
-            '(${_videoPlayerPlatform.runtimeType}) has a bug and is sending '
-            'more than one initialized event per instance.',
-          );
-          if (initializingCompleter.isCompleted) {
-            throw StateError('VideoPlayerController already initialized');
+          if (VideoEventType.initialized == event.eventType) {
+            assert(
+              !initializingCompleter.isCompleted,
+              'VideoPlayerController already initialized. This is typically a '
+              'sign that an implementation of the VideoPlayerPlatform '
+              '(${_videoPlayerPlatform.runtimeType}) has a bug and is sending '
+              'more than one initialized event per instance.',
+            );
+            if (initializingCompleter.isCompleted) {
+              throw StateError('VideoPlayerController already initialized');
+            }
+            initializingCompleter.complete(null);
           }
-          initializingCompleter.complete(null);
           _applyLooping();
           // NOTE(jsuya): The plusplayer's SetVolume() work when player is
           // paused or played, so it changes the order of _applyPlayPause()
           // and _applyVolume().
-          _applyPlayPause();
+          if (event.eventType == VideoEventType.restored &&
+              _onRestoreDataSource != null) {
+            play();
+          } else {
+            _applyPlayPause();
+          }
           _applyVolume();
           _durationTimer?.cancel();
           _durationTimer = _createDurationTimer();
@@ -564,6 +580,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
               isPlaying: event.isPlaying,
               isCompleted: false,
             );
+            _timer ??= _createTimer();
           } else {
             value = value.copyWith(isPlaying: event.isPlaying);
           }
@@ -664,7 +681,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
 
   /// The duration in the current video.
   Future<DurationRange?> get duration async {
-    if (_isDisposed) {
+    if (_isDisposed || _durationTimer == null) {
       return null;
     }
     return _videoPlayerPlatform.getDuration(_playerId);
@@ -715,18 +732,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
 
       // Cancel previous timer.
       _timer?.cancel();
-      _timer = Timer.periodic(const Duration(milliseconds: 500), (
-        Timer timer,
-      ) async {
-        if (_isDisposed) {
-          return;
-        }
-        final Duration? newPosition = await position;
-        if (newPosition == null) {
-          return;
-        }
-        _updatePosition(newPosition);
-      });
+      _timer = _createTimer();
     } else {
       _timer?.cancel();
       await _videoPlayerPlatform.pause(_playerId);
@@ -750,12 +756,26 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
 
   /// The position in the current video.
   Future<Duration?> get position async {
-    if (_isDisposed) {
+    if (_isDisposed || _timer == null) {
       return null;
     }
     return _videoPlayerPlatform.getPosition(_playerId);
   }
 
+  Timer _createTimer() {
+    return Timer.periodic(const Duration(milliseconds: 500), (
+      Timer timer,
+    ) async {
+      if (_isDisposed) {
+        return;
+      }
+      final Duration? newPosition = await position;
+      if (newPosition != null) {
+        _updatePosition(newPosition);
+      }
+    });
+  }
+
   /// Sets the video's current timestamp to be at [moment]. The next
   /// time the video is played it will resume from the given [moment].
   ///
@@ -865,6 +885,59 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// [optional]Set the restoreDataSource and resumeTime of video.
+  /// For live streaming or DRM-encrypted content playback, you must check whether the
+  /// streaming URL has changed or the DRM session or license has expired, and specify
+  /// the new URL and DRM information as needed.
+  ///
+  /// * restoreDataSource[optional][nullable]: Optional updated restoreDataSource after
+  ///   suspend, includes elements such as the new URL and DRM information.
+  ///   If null, the dataSource stored at 'initailize()' is used.
+  ///   For live streaming or DRM-encrypted content playback, in case the URL has changed
+  ///   or the DRM license or session has expired, checking for and passing the newest URL
+  ///   is recommended.
+  /// * resumeTime[optional][default=-1]: (milliseconds) Optional position from which to
+  ///   resume playback in three scenarios: the streaming type is live, power off/on
+  ///   within 5 seconds, or changing the URL(that is, restoreDataSource is non-null).
+  ///   If less than 0, the position stored at '_suspend()' is used.
+  void setRestoreData({
+    RestoreDataSourceCallback? restoreDataSource,
+    RestoreTimeCallback? resumeTime,
+  }) {
+    _onRestoreDataSource = restoreDataSource;
+    _onRestoreTime = resumeTime;
+  }
+
+  /// Pauses the player when the application is sent to the background.
+  /// Saves the current statistics for the ongoing playback session.
+  Future<void> _suspend() async {
+    if (_isDisposedOrNotInitialized) {
+      return;
+    }
+
+    await _videoPlayerPlatform.suspend(_playerId);
+    _durationTimer?.cancel();
+    _timer?.cancel();
+    _timer = null;
+  }
+
+  /// Restores the player state when the application is resumed.
+  Future<void> _restore() async {
+    if (_isDisposedOrNotInitialized) {
+      return;
+    }
+
+    final DataSource? dataSource =
+        (_onRestoreDataSource != null) ? _onRestoreDataSource!() : null;
+    final int resumeTime = (_onRestoreTime != null) ? _onRestoreTime!() : -1;
+
+    await _videoPlayerPlatform.restore(
+      _playerId,
+      dataSource: dataSource,
+      resumeTime: resumeTime,
+    );
+  }
+
   /// Set dashplusplayer properties,can be called after initialized.
   Future<bool> setData(Map<DashPlayerProperty, Object> data) async {
     if (_isDisposedOrNotInitialized) {
@@ -994,7 +1067,6 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
 class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver {
   _VideoAppLifeCycleObserver(this._controller);
 
-  bool _wasPlayingBeforePause = false;
   final VideoPlayerController _controller;
 
   void initialize() {
@@ -1004,12 +1076,9 @@ class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver {
   @override
   void didChangeAppLifecycleState(AppLifecycleState state) {
     if (state == AppLifecycleState.paused) {
-      _wasPlayingBeforePause = _controller.value.isPlaying;
-      _controller.pause();
+      _controller._suspend();
     } else if (state == AppLifecycleState.resumed) {
-      if (_wasPlayingBeforePause) {
-        _controller.play();
-      }
+      _controller._restore();
     }
   }
 
@@ -1071,10 +1140,18 @@ class _VideoPlayerState extends State<VideoPlayer> {
       if (currentRect != Rect.zero && _playerRect != currentRect) {
         _videoPlayerPlatform.setDisplayGeometry(
           _playerId,
-          currentRect.left.toInt(),
-          currentRect.top.toInt(),
-          currentRect.width.toInt(),
-          currentRect.height.toInt(),
+          (currentRect.left.isInfinite || currentRect.left.isNaN)
+              ? 0
+              : currentRect.left.toInt(),
+          (currentRect.top.isInfinite || currentRect.top.isNaN)
+              ? 0
+              : currentRect.top.toInt(),
+          (currentRect.width.isInfinite || currentRect.width.isNaN)
+              ? 1
+              : currentRect.width.toInt(),
+          (currentRect.height.isInfinite || currentRect.height.isNaN)
+              ? 1
+              : currentRect.height.toInt(),
         );
         _playerRect = currentRect;
       }
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 f4e8dce00..606af40a9 100644
--- a/packages/video_player_avplay/lib/video_player_platform_interface.dart
+++ b/packages/video_player_avplay/lib/video_player_platform_interface.dart
@@ -186,6 +186,20 @@ abstract class VideoPlayerPlatform extends PlatformInterface {
     );
   }
 
+  /// Pauses the player when the application is sent to the background.
+  Future<void> suspend(int playerId) {
+    throw UnimplementedError('suspend() has not been implemented.');
+  }
+
+  /// Restores the player state when the application is resumed.
+  Future<void> restore(
+    int playerId, {
+    DataSource? dataSource,
+    int resumeTime = -1,
+  }) {
+    throw UnimplementedError('restore() has not been implemented.');
+  }
+
   /// Returns a widget displaying the video with a given playerId.
   Widget buildView(int playerId) {
     throw UnimplementedError('buildView() has not been implemented.');
@@ -539,6 +553,9 @@ enum VideoEventType {
   /// phone calls, or other app media such as music players.
   isPlayingStateUpdate,
 
+  /// The video need to restore player.
+  restored,
+
   /// An unknown event has been received.
   unknown,
 }
diff --git a/packages/video_player_avplay/pigeons/messages.dart b/packages/video_player_avplay/pigeons/messages.dart
index 4904d87f4..f99b5ced8 100644
--- a/packages/video_player_avplay/pigeons/messages.dart
+++ b/packages/video_player_avplay/pigeons/messages.dart
@@ -168,6 +168,8 @@ abstract class VideoPlayerAvplayApi {
   void setStreamingProperty(StreamingPropertyMessage msg);
   bool setDisplayRotate(RotationMessage msg);
   bool setDisplayMode(DisplayModeMessage msg);
+  void suspend(int playerId);
+  void restore(int playerId, CreateMessage? msg, int resumeTime);
   bool setData(DashPropertyMapMessage msg);
   DashPropertyMapMessage getData(DashPropertyTypeListMessage msg);
   TrackMessage getActiveTrackInfo(PlayerMessage msg);
diff --git a/packages/video_player_avplay/pubspec.yaml b/packages/video_player_avplay/pubspec.yaml
index 7266b53ce..c81adb096 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.19
+version: 0.5.20
 
 environment:
   sdk: ">=3.1.0 <4.0.0"
diff --git a/packages/video_player_avplay/tizen/src/device_proxy.cc b/packages/video_player_avplay/tizen/src/device_proxy.cc
new file mode 100644
index 000000000..53e686365
--- /dev/null
+++ b/packages/video_player_avplay/tizen/src/device_proxy.cc
@@ -0,0 +1,40 @@
+// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device_proxy.h"
+
+#include <dlfcn.h>
+
+#include "log.h"
+
+typedef power_state_e (*FunDevicePowerGetState)(void);
+
+DeviceProxy::DeviceProxy() {
+  power_state_handle_ = dlopen("libdeviced.so.1", RTLD_LAZY);
+  if (power_state_handle_ == nullptr) {
+    LOG_ERROR("Failed to open libdeviced.so.1");
+  }
+}
+
+DeviceProxy::~DeviceProxy() {
+  if (power_state_handle_) {
+    dlclose(power_state_handle_);
+    power_state_handle_ = nullptr;
+  }
+}
+
+power_state_e DeviceProxy::device_power_get_state(void) {
+  if (!power_state_handle_) {
+    LOG_ERROR("power_state_handle_ is invalid.");
+    return POWER_STATE_ERROR;
+  }
+  FunDevicePowerGetState device_power_get_state =
+      reinterpret_cast<FunDevicePowerGetState>(
+          dlsym(power_state_handle_, "device_power_get_state"));
+  if (!device_power_get_state) {
+    LOG_ERROR("Failed to find device_power_get_state function.");
+    return POWER_STATE_ERROR;
+  }
+  return device_power_get_state();
+}
diff --git a/packages/video_player_avplay/tizen/src/device_proxy.h b/packages/video_player_avplay/tizen/src/device_proxy.h
new file mode 100644
index 000000000..ee218b2da
--- /dev/null
+++ b/packages/video_player_avplay/tizen/src/device_proxy.h
@@ -0,0 +1,27 @@
+// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_PLUGIN_DEVICE_PROXY_H_
+#define FLUTTER_PLUGIN_DEVICE_PROXY_H_
+
+typedef enum {
+  POWER_STATE_NORMAL,          /**< Normal state */
+  POWER_STATE_PICTUREOFF,      /**< Picture off state */
+  POWER_STATE_STANDBY,         /**< Background Standby state */
+  POWER_STATE_OFF,             /**< Cold Standby state */
+  POWER_STATE_SUSPEND_STANDBY, /**< Suspend Standby state */
+  POWER_STATE_ERROR            /**< Error state */
+} power_state_e;
+
+class DeviceProxy {
+ public:
+  DeviceProxy();
+  ~DeviceProxy();
+  power_state_e device_power_get_state(void);
+
+ private:
+  void* power_state_handle_ = nullptr;
+};
+
+#endif  // FLUTTER_PLUGIN_DEVICE_PROXY_H_
diff --git a/packages/video_player_avplay/tizen/src/media_player.cc b/packages/video_player_avplay/tizen/src/media_player.cc
index 30feec95b..933f671d6 100644
--- a/packages/video_player_avplay/tizen/src/media_player.cc
+++ b/packages/video_player_avplay/tizen/src/media_player.cc
@@ -51,6 +51,7 @@ MediaPlayer::MediaPlayer(flutter::BinaryMessenger *messenger,
                          FlutterDesktopViewRef flutter_view)
     : VideoPlayer(messenger, flutter_view) {
   media_player_proxy_ = std::make_unique<MediaPlayerProxy>();
+  device_proxy_ = std::make_unique<DeviceProxy>();
 }
 
 MediaPlayer::~MediaPlayer() {
@@ -78,6 +79,7 @@ int64_t MediaPlayer::Create(const std::string &uri,
     LOG_ERROR("[MediaPlayer] The uri must not be empty.");
     return -1;
   }
+  url_ = uri;
 
   int ret = player_create(&player_);
   if (ret != PLAYER_ERROR_NONE) {
@@ -133,7 +135,8 @@ int64_t MediaPlayer::Create(const std::string &uri,
     return -1;
   }
 
-  SetDisplayRoi(0, 0, 1, 1);
+  SetDisplayRoi(pre_display_roi_x_, pre_display_roi_y_, pre_display_roi_width_,
+                pre_display_roi_height_);
 
   ret = player_set_uri(player_, uri.c_str());
   if (ret != PLAYER_ERROR_NONE) {
@@ -206,6 +209,10 @@ void MediaPlayer::SetDisplayRoi(int32_t x, int32_t y, int32_t width,
     LOG_ERROR("[MediaPlayer] player_set_display_roi_area failed: %s.",
               get_error_message(ret));
   }
+  pre_display_roi_x_ = x;
+  pre_display_roi_y_ = y;
+  pre_display_roi_width_ = width;
+  pre_display_roi_height_ = height;
 }
 
 bool MediaPlayer::Play() {
@@ -642,6 +649,13 @@ bool MediaPlayer::SetDrm(const std::string &uri, int drm_type,
   return true;
 }
 
+void MediaPlayer::OnRestoreCompleted() {
+  if (pre_playing_time_ <= 0 ||
+      !SeekTo(pre_playing_time_, [this]() { SendRestored(); })) {
+    SendRestored();
+  }
+}
+
 void MediaPlayer::OnPrepared(void *user_data) {
   LOG_INFO("[MediaPlayer] Player prepared.");
 
@@ -649,6 +663,10 @@ void MediaPlayer::OnPrepared(void *user_data) {
   if (!self->is_initialized_) {
     self->SendInitialized();
   }
+
+  if (self->is_restored_) {
+    self->OnRestoreCompleted();
+  }
 }
 
 void MediaPlayer::OnBuffering(int percent, void *user_data) {
@@ -760,3 +778,184 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e player_state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &player_state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (player_state == PLAYER_STATE_NONE || player_state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e player_state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &player_state);
+  if (res != PLAYER_ERROR_NONE || player_state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state[%d].",
+              player_state);
+    return false;
+  }
+
+  pre_state_ = player_state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO(
+      "[MediaPlayer] Saved current player state: %d, playing time: %llu ms",
+      pre_state_, pre_playing_time_);
+
+  res = device_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (player_state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (player_state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e player_state = PLAYER_STATE_NONE;
+  if (player_) {
+    int ret = player_get_state(player_, &player_state);
+    if (ret != PLAYER_ERROR_NONE || (player_state != PLAYER_STATE_PAUSED &&
+                                     player_state != PLAYER_STATE_PLAYING)) {
+      LOG_ERROR(
+          "[MediaPlayer] Player get state failed or in invalid state[%d].",
+          player_state);
+      return false;
+    }
+  }
+
+  if (restore_message->uri()) {
+    LOG_INFO(
+        "[MediaPlayer] Restore URL is not emptpy, close the existing "
+        "instance.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    return RestorePlayer(restore_message, resume_time);
+  }
+
+  switch (player_state) {
+    case PLAYER_STATE_NONE:
+      return RestorePlayer(restore_message, resume_time);
+      break;
+    case PLAYER_STATE_PAUSED:
+      if (pre_state_ == PLAYER_STATE_PLAYING) {
+        if (!Play()) {
+          LOG_ERROR("[MediaPlayer] Player play failed.");
+          return false;
+        }
+      }
+      break;
+    case PLAYER_STATE_PLAYING:
+      // might be the case that widget has called
+      // restore more than once, just ignore.
+      break;
+    default:
+      LOG_INFO(
+          "[MediaPlayer] Unhandled state, dont know how to process, just "
+          "return "
+          "false.");
+      return false;
+  }
+  return true;
+}
+
+bool MediaPlayer::RestorePlayer(const CreateMessage *restore_message,
+                                int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] RestorePlayer is called.");
+
+  if (restore_message->uri()) {
+    LOG_INFO("[MediaPlayer] Player previous url: %s", url_.c_str());
+    LOG_INFO("[MediaPlayer] Player new url: %s",
+             restore_message->uri()->c_str());
+    url_ = *restore_message->uri();
+  }
+
+  LOG_INFO("[MediaPlayer] Player previous playing time: %llu ms",
+           pre_playing_time_);
+  LOG_INFO("[MediaPlayer] Player new resume time: %lld ms", resume_time);
+  // resume_time < 0  ==> use previous playing time
+  // resume_time == 0 ==> play from beginning
+  // resume_time > 0  ==> play from resume_time(Third-party settings)
+  if (resume_time >= 0) pre_playing_time_ = static_cast<uint64_t>(resume_time);
+
+  is_restored_ = true;
+  if (Create(url_, *restore_message) < 0) {
+    LOG_ERROR("[MediaPlayer] Fail to create player.");
+    is_restored_ = false;
+    return false;
+  }
+
+  return true;
+}
diff --git a/packages/video_player_avplay/tizen/src/media_player.h b/packages/video_player_avplay/tizen/src/media_player.h
index c8d091315..e91ab79ad 100644
--- a/packages/video_player_avplay/tizen/src/media_player.h
+++ b/packages/video_player_avplay/tizen/src/media_player.h
@@ -11,6 +11,7 @@
 #include <string>
 #include <utility>
 
+#include "device_proxy.h"
 #include "drm_manager.h"
 #include "media_player_proxy.h"
 #include "video_player.h"
@@ -41,6 +42,9 @@ class MediaPlayer : public VideoPlayer {
   bool SetTrackSelection(int32_t track_id, std::string track_type) override;
   bool SetDisplayRotate(int64_t rotation) override;
   bool SetDisplayMode(int64_t display_mode) override;
+  bool Suspend() override;
+  bool Restore(const CreateMessage *restore_message,
+               int64_t resume_time) override;
 
  private:
   bool IsLive();
@@ -48,6 +52,9 @@ class MediaPlayer : public VideoPlayer {
   bool SetDisplay();
   bool SetDrm(const std::string &uri, int drm_type,
               const std::string &license_server_url);
+  bool StopAndDestroy();
+  bool RestorePlayer(const CreateMessage *restore_message, int64_t resume_time);
+  void OnRestoreCompleted();
 
   static void OnPrepared(void *user_data);
   static void OnBuffering(int percent, void *user_data);
@@ -65,9 +72,17 @@ class MediaPlayer : public VideoPlayer {
 
   player_h player_ = nullptr;
   std::unique_ptr<MediaPlayerProxy> media_player_proxy_ = nullptr;
+  std::unique_ptr<DeviceProxy> device_proxy_ = nullptr;
   std::unique_ptr<DrmManager> drm_manager_;
   bool is_buffering_ = false;
   SeekCompletedCallback on_seek_completed_;
+  std::string url_;
+  player_state_e pre_state_;
+  int64_t pre_playing_time_;
+  int32_t pre_display_roi_x_ = 0;
+  int32_t pre_display_roi_y_ = 0;
+  int32_t pre_display_roi_width_ = 1;
+  int32_t pre_display_roi_height_ = 1;
 };
 
 #endif  // FLUTTER_PLUGIN_MEDIA_PLAYER_H_
diff --git a/packages/video_player_avplay/tizen/src/messages.cc b/packages/video_player_avplay/tizen/src/messages.cc
index 221f0bead..ada13c764 100644
--- a/packages/video_player_avplay/tizen/src/messages.cc
+++ b/packages/video_player_avplay/tizen/src/messages.cc
@@ -837,56 +837,59 @@ EncodableValue VideoPlayerAvplayApiCodecSerializer::ReadValueOfType(
       return CustomEncodableValue(CreateMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
     case 130:
-      return CustomEncodableValue(DashPropertyMapMessage::FromEncodableList(
+      return CustomEncodableValue(CreateMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
     case 131:
+      return CustomEncodableValue(DashPropertyMapMessage::FromEncodableList(
+          std::get<EncodableList>(ReadValue(stream))));
+    case 132:
       return CustomEncodableValue(
           DashPropertyTypeListMessage::FromEncodableList(
               std::get<EncodableList>(ReadValue(stream))));
-    case 132:
+    case 133:
       return CustomEncodableValue(DisplayModeMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
-    case 133:
+    case 134:
       return CustomEncodableValue(DurationMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
-    case 134:
+    case 135:
       return CustomEncodableValue(GeometryMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
-    case 135:
+    case 136:
       return CustomEncodableValue(LoopingMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
-    case 136:
+    case 137:
       return CustomEncodableValue(MixWithOthersMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
-    case 137:
+    case 138:
       return CustomEncodableValue(PlaybackSpeedMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
-    case 138:
+    case 139:
       return CustomEncodableValue(PlayerMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
-    case 139:
+    case 140:
       return CustomEncodableValue(PositionMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
-    case 140:
+    case 141:
       return CustomEncodableValue(RotationMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
-    case 141:
+    case 142:
       return CustomEncodableValue(SelectedTracksMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
-    case 142:
+    case 143:
       return CustomEncodableValue(StreamingPropertyMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
-    case 143:
+    case 144:
       return CustomEncodableValue(
           StreamingPropertyTypeMessage::FromEncodableList(
               std::get<EncodableList>(ReadValue(stream))));
-    case 144:
+    case 145:
       return CustomEncodableValue(TrackMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
-    case 145:
+    case 146:
       return CustomEncodableValue(TrackTypeMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
-    case 146:
+    case 147:
       return CustomEncodableValue(VolumeMessage::FromEncodableList(
           std::get<EncodableList>(ReadValue(stream))));
     default:
@@ -914,8 +917,16 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
           stream);
       return;
     }
-    if (custom_value->type() == typeid(DashPropertyMapMessage)) {
+    if (custom_value->type() == typeid(CreateMessage)) {
       stream->WriteByte(130);
+      WriteValue(
+          EncodableValue(
+              std::any_cast<CreateMessage>(*custom_value).ToEncodableList()),
+          stream);
+      return;
+    }
+    if (custom_value->type() == typeid(DashPropertyMapMessage)) {
+      stream->WriteByte(131);
       WriteValue(
           EncodableValue(std::any_cast<DashPropertyMapMessage>(*custom_value)
                              .ToEncodableList()),
@@ -923,7 +934,7 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
       return;
     }
     if (custom_value->type() == typeid(DashPropertyTypeListMessage)) {
-      stream->WriteByte(131);
+      stream->WriteByte(132);
       WriteValue(EncodableValue(
                      std::any_cast<DashPropertyTypeListMessage>(*custom_value)
                          .ToEncodableList()),
@@ -931,14 +942,14 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
       return;
     }
     if (custom_value->type() == typeid(DisplayModeMessage)) {
-      stream->WriteByte(132);
+      stream->WriteByte(133);
       WriteValue(EncodableValue(std::any_cast<DisplayModeMessage>(*custom_value)
                                     .ToEncodableList()),
                  stream);
       return;
     }
     if (custom_value->type() == typeid(DurationMessage)) {
-      stream->WriteByte(133);
+      stream->WriteByte(134);
       WriteValue(
           EncodableValue(
               std::any_cast<DurationMessage>(*custom_value).ToEncodableList()),
@@ -946,7 +957,7 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
       return;
     }
     if (custom_value->type() == typeid(GeometryMessage)) {
-      stream->WriteByte(134);
+      stream->WriteByte(135);
       WriteValue(
           EncodableValue(
               std::any_cast<GeometryMessage>(*custom_value).ToEncodableList()),
@@ -954,7 +965,7 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
       return;
     }
     if (custom_value->type() == typeid(LoopingMessage)) {
-      stream->WriteByte(135);
+      stream->WriteByte(136);
       WriteValue(
           EncodableValue(
               std::any_cast<LoopingMessage>(*custom_value).ToEncodableList()),
@@ -962,7 +973,7 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
       return;
     }
     if (custom_value->type() == typeid(MixWithOthersMessage)) {
-      stream->WriteByte(136);
+      stream->WriteByte(137);
       WriteValue(
           EncodableValue(std::any_cast<MixWithOthersMessage>(*custom_value)
                              .ToEncodableList()),
@@ -970,7 +981,7 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
       return;
     }
     if (custom_value->type() == typeid(PlaybackSpeedMessage)) {
-      stream->WriteByte(137);
+      stream->WriteByte(138);
       WriteValue(
           EncodableValue(std::any_cast<PlaybackSpeedMessage>(*custom_value)
                              .ToEncodableList()),
@@ -978,7 +989,7 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
       return;
     }
     if (custom_value->type() == typeid(PlayerMessage)) {
-      stream->WriteByte(138);
+      stream->WriteByte(139);
       WriteValue(
           EncodableValue(
               std::any_cast<PlayerMessage>(*custom_value).ToEncodableList()),
@@ -986,7 +997,7 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
       return;
     }
     if (custom_value->type() == typeid(PositionMessage)) {
-      stream->WriteByte(139);
+      stream->WriteByte(140);
       WriteValue(
           EncodableValue(
               std::any_cast<PositionMessage>(*custom_value).ToEncodableList()),
@@ -994,7 +1005,7 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
       return;
     }
     if (custom_value->type() == typeid(RotationMessage)) {
-      stream->WriteByte(140);
+      stream->WriteByte(141);
       WriteValue(
           EncodableValue(
               std::any_cast<RotationMessage>(*custom_value).ToEncodableList()),
@@ -1002,7 +1013,7 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
       return;
     }
     if (custom_value->type() == typeid(SelectedTracksMessage)) {
-      stream->WriteByte(141);
+      stream->WriteByte(142);
       WriteValue(
           EncodableValue(std::any_cast<SelectedTracksMessage>(*custom_value)
                              .ToEncodableList()),
@@ -1010,7 +1021,7 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
       return;
     }
     if (custom_value->type() == typeid(StreamingPropertyMessage)) {
-      stream->WriteByte(142);
+      stream->WriteByte(143);
       WriteValue(
           EncodableValue(std::any_cast<StreamingPropertyMessage>(*custom_value)
                              .ToEncodableList()),
@@ -1018,7 +1029,7 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
       return;
     }
     if (custom_value->type() == typeid(StreamingPropertyTypeMessage)) {
-      stream->WriteByte(143);
+      stream->WriteByte(144);
       WriteValue(EncodableValue(
                      std::any_cast<StreamingPropertyTypeMessage>(*custom_value)
                          .ToEncodableList()),
@@ -1026,7 +1037,7 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
       return;
     }
     if (custom_value->type() == typeid(TrackMessage)) {
-      stream->WriteByte(144);
+      stream->WriteByte(145);
       WriteValue(
           EncodableValue(
               std::any_cast<TrackMessage>(*custom_value).ToEncodableList()),
@@ -1034,7 +1045,7 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
       return;
     }
     if (custom_value->type() == typeid(TrackTypeMessage)) {
-      stream->WriteByte(145);
+      stream->WriteByte(146);
       WriteValue(
           EncodableValue(
               std::any_cast<TrackTypeMessage>(*custom_value).ToEncodableList()),
@@ -1042,7 +1053,7 @@ void VideoPlayerAvplayApiCodecSerializer::WriteValue(
       return;
     }
     if (custom_value->type() == typeid(VolumeMessage)) {
-      stream->WriteByte(146);
+      stream->WriteByte(147);
       WriteValue(
           EncodableValue(
               std::any_cast<VolumeMessage>(*custom_value).ToEncodableList()),
@@ -1828,6 +1839,83 @@ void VideoPlayerAvplayApi::SetUp(flutter::BinaryMessenger* binary_messenger,
       channel->SetMessageHandler(nullptr);
     }
   }
+  {
+    auto channel = std::make_unique<BasicMessageChannel<>>(
+        binary_messenger,
+        "dev.flutter.pigeon.video_player_avplay.VideoPlayerAvplayApi.suspend",
+        &GetCodec());
+    if (api != nullptr) {
+      channel->SetMessageHandler(
+          [api](const EncodableValue& message,
+                const flutter::MessageReply<EncodableValue>& reply) {
+            try {
+              const auto& args = std::get<EncodableList>(message);
+              const auto& encodable_player_id_arg = args.at(0);
+              if (encodable_player_id_arg.IsNull()) {
+                reply(WrapError("player_id_arg unexpectedly null."));
+                return;
+              }
+              const int64_t player_id_arg = encodable_player_id_arg.LongValue();
+              std::optional<FlutterError> output = api->Suspend(player_id_arg);
+              if (output.has_value()) {
+                reply(WrapError(output.value()));
+                return;
+              }
+              EncodableList wrapped;
+              wrapped.push_back(EncodableValue());
+              reply(EncodableValue(std::move(wrapped)));
+            } catch (const std::exception& exception) {
+              reply(WrapError(exception.what()));
+            }
+          });
+    } else {
+      channel->SetMessageHandler(nullptr);
+    }
+  }
+  {
+    auto channel = std::make_unique<BasicMessageChannel<>>(
+        binary_messenger,
+        "dev.flutter.pigeon.video_player_avplay.VideoPlayerAvplayApi.restore",
+        &GetCodec());
+    if (api != nullptr) {
+      channel->SetMessageHandler(
+          [api](const EncodableValue& message,
+                const flutter::MessageReply<EncodableValue>& reply) {
+            try {
+              const auto& args = std::get<EncodableList>(message);
+              const auto& encodable_player_id_arg = args.at(0);
+              if (encodable_player_id_arg.IsNull()) {
+                reply(WrapError("player_id_arg unexpectedly null."));
+                return;
+              }
+              const int64_t player_id_arg = encodable_player_id_arg.LongValue();
+              const auto& encodable_msg_arg = args.at(1);
+              const auto* msg_arg = &(std::any_cast<const CreateMessage&>(
+                  std::get<CustomEncodableValue>(encodable_msg_arg)));
+              const auto& encodable_resume_time_arg = args.at(2);
+              if (encodable_resume_time_arg.IsNull()) {
+                reply(WrapError("resume_time_arg unexpectedly null."));
+                return;
+              }
+              const int64_t resume_time_arg =
+                  encodable_resume_time_arg.LongValue();
+              std::optional<FlutterError> output =
+                  api->Restore(player_id_arg, msg_arg, resume_time_arg);
+              if (output.has_value()) {
+                reply(WrapError(output.value()));
+                return;
+              }
+              EncodableList wrapped;
+              wrapped.push_back(EncodableValue());
+              reply(EncodableValue(std::move(wrapped)));
+            } catch (const std::exception& exception) {
+              reply(WrapError(exception.what()));
+            }
+          });
+    } else {
+      channel->SetMessageHandler(nullptr);
+    }
+  }
   {
     auto channel = std::make_unique<BasicMessageChannel<>>(
         binary_messenger,
diff --git a/packages/video_player_avplay/tizen/src/messages.h b/packages/video_player_avplay/tizen/src/messages.h
index 8190e7b86..67b90a870 100644
--- a/packages/video_player_avplay/tizen/src/messages.h
+++ b/packages/video_player_avplay/tizen/src/messages.h
@@ -586,6 +586,10 @@ class VideoPlayerAvplayApi {
       const StreamingPropertyMessage& msg) = 0;
   virtual ErrorOr<bool> SetDisplayRotate(const RotationMessage& msg) = 0;
   virtual ErrorOr<bool> SetDisplayMode(const DisplayModeMessage& msg) = 0;
+  virtual std::optional<FlutterError> Suspend(int64_t player_id) = 0;
+  virtual std::optional<FlutterError> Restore(int64_t player_id,
+                                              const CreateMessage* msg,
+                                              int64_t resume_time) = 0;
   virtual ErrorOr<bool> SetData(const DashPropertyMapMessage& msg) = 0;
   virtual ErrorOr<DashPropertyMapMessage> GetData(
       const DashPropertyTypeListMessage& msg) = 0;
diff --git a/packages/video_player_avplay/tizen/src/plus_player.cc b/packages/video_player_avplay/tizen/src/plus_player.cc
index 086c045ec..9bb499c50 100644
--- a/packages/video_player_avplay/tizen/src/plus_player.cc
+++ b/packages/video_player_avplay/tizen/src/plus_player.cc
@@ -39,7 +39,10 @@ static plusplayer::TrackType ConvertTrackType(std::string track_type) {
 
 PlusPlayer::PlusPlayer(flutter::BinaryMessenger *messenger,
                        FlutterDesktopViewRef flutter_view)
-    : VideoPlayer(messenger, flutter_view) {}
+    : VideoPlayer(messenger, flutter_view) {
+  memento_ = std::make_unique<plusplayer::PlayerMemento>();
+  device_proxy_ = std::make_unique<DeviceProxy>();
+}
 
 PlusPlayer::~PlusPlayer() {
   if (player_) {
@@ -96,6 +99,8 @@ int64_t PlusPlayer::Create(const std::string &uri,
     LOG_ERROR("[PlusPlayer] Fail to open uri :  %s.", uri.c_str());
     return -1;
   }
+  url_ = uri;
+  create_message_ = create_message;
   LOG_INFO("[PlusPlayer] Uri: %s", uri.c_str());
 
   if (create_message.streaming_property() != nullptr &&
@@ -231,15 +236,15 @@ bool PlusPlayer::Deactivate() {
   }
 
   if (!::Deactivate(player_, plusplayer::kTrackTypeVideo)) {
-    LOG_ERROR("[PlusPlayer] Fail to activate video.");
+    LOG_ERROR("[PlusPlayer] Fail to deactivate video.");
     return false;
   }
   if (!::Deactivate(player_, plusplayer::kTrackTypeAudio)) {
-    LOG_ERROR("[PlusPlayer] Fail to activate audio.");
+    LOG_ERROR("[PlusPlayer] Fail to deactivate audio.");
     return false;
   }
   if (!::Deactivate(player_, plusplayer::kTrackTypeSubtitle)) {
-    LOG_ERROR("[PlusPlayer] Fail to activate subtitle.");
+    LOG_ERROR("[PlusPlayer] Fail to deactivate subtitle.");
   }
 
   return true;
@@ -743,6 +748,210 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State player_state = GetState(player_);
+  if (player_state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO(
+      "[PlusPlayer] Memento saved current player state: %d, position: %llu ms, "
+      "is_live: %d",
+      (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    if (!StopAndClose()) {
+      LOG_ERROR("[PlusPlayer] Player is live, StopAndClose fail.");
+      return false;
+    }
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  int power_state = device_proxy_->device_power_get_state();
+  if (power_state == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    if (!StopAndClose()) {
+      LOG_ERROR("[PlusPlayer] Player need to stop and close, but failed.");
+      return false;
+    }
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.",
+             power_state);
+  }
+
+  plusplayer::State player_state = GetState(player_);
+  if (player_state <= plusplayer::State::kTrackSourceReady) {
+    if (!::Close(player_)) {
+      LOG_ERROR("[PlusPlayer] Player close fail.");
+      return false;
+    }
+    LOG_INFO("[PlusPlayer] Player is in invalid state[%d], just close.",
+             player_state);
+    return true;
+  } else if (player_state != plusplayer::State::kPaused) {
+    LOG_INFO("[PlusPlayer] Player calling pause from suspend.");
+    if (::Suspend(player_) == false) {
+      LOG_ERROR(
+          "[PlusPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndClose())
+        LOG_ERROR("[PlusPlayer] Suspend error, player stop and close fail.");
+      return false;
+    }
+    SendIsPlayingState(false);
+  }
+  return true;
+}
+
+bool PlusPlayer::Restore(const CreateMessage *restore_message,
+                         int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] Restore is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player is not initialized.");
+    return false;
+  }
+
+  plusplayer::State player_state = GetState(player_);
+  if (player_state != plusplayer::State::kNone &&
+      player_state != plusplayer::State::kPaused &&
+      player_state != plusplayer::State::kPlaying) {
+    LOG_ERROR("[PlusPlayer] Player is in invalid state[%d].", player_state);
+    return false;
+  }
+
+  if (!memento_) {
+    LOG_ERROR(
+        "[PlusPlayer] No memento to restore. Player is in invalid state[%d]",
+        player_state);
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  if (restore_message->uri()) {
+    LOG_INFO(
+        "[PlusPlayer] Restore URL is not emptpy, close the existing instance.");
+    if (!StopAndClose()) {
+      LOG_ERROR("[PlusPlayer] Player need to stop and close, but failed.");
+      return false;
+    }
+    return RestorePlayer(restore_message, resume_time);
+  }
+
+  switch (player_state) {
+    case plusplayer::State::kNone:
+      return RestorePlayer(restore_message, resume_time);
+      break;
+    case plusplayer::State::kPaused:
+      if (!::Restore(player_, memento_->state)) {
+        if (!StopAndClose()) {
+          LOG_ERROR("[PlusPlayer] Player need to stop and close, but failed.");
+          return false;
+        }
+        return RestorePlayer(restore_message, resume_time);
+      }
+      break;
+    case plusplayer::State::kPlaying:
+      // might be the case that widget has called
+      // restore more than once, just ignore.
+      break;
+    default:
+      LOG_INFO(
+          "[PlusPlayer] Unhandled state, dont know how to process, just return "
+          "false.");
+      return false;
+  }
+  return true;
+}
+
+bool PlusPlayer::RestorePlayer(const CreateMessage *restore_message,
+                               int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] RestorePlayer is called.");
+  LOG_INFO("[PlusPlayer] is_live: %d", memento_->is_live);
+
+  if (restore_message->uri()) {
+    LOG_INFO("[PlusPlayer] Player previous url: %s", url_.c_str());
+    LOG_INFO("[PlusPlayer] Player new url: %s",
+             restore_message->uri()->c_str());
+    url_ = *restore_message->uri();
+    create_message_ = *restore_message;
+  }
+
+  LOG_INFO("[PlusPlayer] Player previous playing time: %llu ms",
+           memento_->playing_time);
+  LOG_INFO("[PlusPlayer] Player new resume time: %lld ms", resume_time);
+  // resume_time < 0  ==> use previous playing time
+  // resume_time == 0 ==> play from beginning
+  // resume_time > 0  ==> play from resume_time(Third-party settings)
+  if (resume_time >= 0)
+    memento_->playing_time = static_cast<uint64_t>(resume_time);
+
+  is_restored_ = true;
+  if (Create(url_, create_message_) < 0) {
+    LOG_ERROR("[PlusPlayer] Fail to create player.");
+    is_restored_ = false;
+    return false;
+  }
+  if (memento_->playing_time > 0 && !Seek(player_, memento_->playing_time)) {
+    LOG_ERROR("[PlusPlayer] Fail to seek.");
+  }
+  SetDisplayRoi(memento_->display_area.x, memento_->display_area.y,
+                memento_->display_area.w, memento_->display_area.h);
+
+  return true;
+}
+
 std::string BuildJsonString(const flutter::EncodableMap &data) {
   rapidjson::Document doc;
   doc.SetObject();
@@ -857,7 +1066,6 @@ bool PlusPlayer::OnLicenseAcquired(int *drm_handle, unsigned int length,
 }
 
 void PlusPlayer::OnPrepareDone(bool ret, void *user_data) {
-  LOG_INFO("[PlusPlayer] Prepare done, result: %d.", ret);
   PlusPlayer *self = reinterpret_cast<PlusPlayer *>(user_data);
 
   if (!SetDisplayVisible(self->player_, true)) {
@@ -867,6 +1075,10 @@ void PlusPlayer::OnPrepareDone(bool ret, void *user_data) {
   if (!self->is_initialized_ && ret) {
     self->SendInitialized();
   }
+
+  if (self->is_restored_ && ret) {
+    self->SendRestored();
+  }
 }
 
 void PlusPlayer::OnBufferStatus(int percent, void *user_data) {
diff --git a/packages/video_player_avplay/tizen/src/plus_player.h b/packages/video_player_avplay/tizen/src/plus_player.h
index 1d889b9aa..55f98f18b 100644
--- a/packages/video_player_avplay/tizen/src/plus_player.h
+++ b/packages/video_player_avplay/tizen/src/plus_player.h
@@ -10,6 +10,7 @@
 #include <memory>
 #include <string>
 
+#include "device_proxy.h"
 #include "drm_manager.h"
 #include "messages.h"
 #include "plusplayer/plusplayer_wrapper.h"
@@ -48,6 +49,9 @@ class PlusPlayer : public VideoPlayer {
                             const std::string &value) override;
   bool SetDisplayRotate(int64_t rotation) override;
   bool SetDisplayMode(int64_t display_mode) override;
+  bool Suspend() override;
+  bool Restore(const CreateMessage *restore_message,
+               int64_t resume_time) override;
   bool SetData(const flutter::EncodableMap &data) override;
   flutter::EncodableMap GetData(const flutter::EncodableList &data) override;
   flutter::EncodableList GetActiveTrackInfo() override;
@@ -62,9 +66,11 @@ class PlusPlayer : public VideoPlayer {
   flutter::EncodableValue ParseAudioTrack(plusplayer::Track audio_track);
   flutter::EncodableValue ParseSubtitleTrack(plusplayer::Track subtitle_track);
   void RegisterListener();
+  bool StopAndClose();
+  bool RestorePlayer(const CreateMessage *restore_message, int64_t resume_time);
+
   static bool OnLicenseAcquired(int *drm_handle, unsigned int length,
                                 unsigned char *pssh_data, void *user_data);
-
   static void OnPrepareDone(bool ret, void *user_data);
   static void OnBufferStatus(const int percent, void *user_data);
   static void OnSeekDone(void *user_data);
@@ -97,6 +103,10 @@ class PlusPlayer : public VideoPlayer {
   bool is_buffering_ = false;
   bool is_prebuffer_mode_ = false;
   SeekCompletedCallback on_seek_completed_;
+  std::unique_ptr<plusplayer::PlayerMemento> memento_ = nullptr;
+  std::string url_;
+  std::unique_ptr<DeviceProxy> device_proxy_ = nullptr;
+  CreateMessage create_message_;
 };
 
 #endif  // FLUTTER_PLUGIN_PLUS_PLAYER_H_
diff --git a/packages/video_player_avplay/tizen/src/video_player.cc b/packages/video_player_avplay/tizen/src/video_player.cc
index 51d82137c..3dcfa41d0 100644
--- a/packages/video_player_avplay/tizen/src/video_player.cc
+++ b/packages/video_player_avplay/tizen/src/video_player.cc
@@ -173,6 +173,27 @@ void VideoPlayer::SendIsPlayingState(bool is_playing) {
   PushEvent(flutter::EncodableValue(result));
 }
 
+void VideoPlayer::SendRestored() {
+  if (is_restored_ && event_sink_) {
+    is_restored_ = false;
+    int32_t width = 0, height = 0;
+    GetVideoSize(&width, &height);
+    auto duration = GetDuration();
+    flutter::EncodableList duration_range{
+        flutter::EncodableValue(duration.first),
+        flutter::EncodableValue(duration.second)};
+
+    flutter::EncodableMap result = {
+        {flutter::EncodableValue("event"), flutter::EncodableValue("restored")},
+        {flutter::EncodableValue("duration"),
+         flutter::EncodableValue(duration_range)},
+        {flutter::EncodableValue("width"), flutter::EncodableValue(width)},
+        {flutter::EncodableValue("height"), flutter::EncodableValue(height)},
+    };
+    PushEvent(flutter::EncodableValue(result));
+  }
+}
+
 void VideoPlayer::SendError(const std::string &error_code,
                             const std::string &error_message) {
   if (event_sink_) {
diff --git a/packages/video_player_avplay/tizen/src/video_player.h b/packages/video_player_avplay/tizen/src/video_player.h
index 2dc4fbe8f..c33cac646 100644
--- a/packages/video_player_avplay/tizen/src/video_player.h
+++ b/packages/video_player_avplay/tizen/src/video_player.h
@@ -59,6 +59,9 @@ class VideoPlayer {
                                     const std::string &value){};
   virtual bool SetDisplayRotate(int64_t rotation) = 0;
   virtual bool SetDisplayMode(int64_t display_mode) = 0;
+  virtual bool Suspend() = 0;
+  virtual bool Restore(const CreateMessage *restore_message,
+                       int64_t resume_time) = 0;
   virtual bool SetData(const flutter::EncodableMap &data) { return false; }
   virtual flutter::EncodableMap GetData(const flutter::EncodableList &data) {
     return flutter::EncodableMap{};
@@ -79,6 +82,7 @@ class VideoPlayer {
   void SendSubtitleUpdate(int32_t duration, const std::string &text);
   void SendPlayCompleted();
   void SendIsPlayingState(bool is_playing);
+  void SendRestored();
   void SendError(const std::string &error_code,
                  const std::string &error_message);
 
@@ -87,6 +91,7 @@ class VideoPlayer {
   flutter::BinaryMessenger *binary_messenger_;
   bool is_initialized_ = false;
   FlutterDesktopViewRef flutter_view_;
+  bool is_restored_ = false;
 
  private:
   void ExecuteSinkEvents();
diff --git a/packages/video_player_avplay/tizen/src/video_player_tizen_plugin.cc b/packages/video_player_avplay/tizen/src/video_player_tizen_plugin.cc
index 696498ece..8681ef987 100644
--- a/packages/video_player_avplay/tizen/src/video_player_tizen_plugin.cc
+++ b/packages/video_player_avplay/tizen/src/video_player_tizen_plugin.cc
@@ -68,6 +68,11 @@ class VideoPlayerTizenPlugin : public flutter::Plugin,
       const DashPropertyTypeListMessage &msg) override;
   ErrorOr<TrackMessage> GetActiveTrackInfo(const PlayerMessage &msg) override;
 
+  std::optional<FlutterError> Suspend(int64_t player_id) override;
+  std::optional<FlutterError> Restore(int64_t palyer_id,
+                                      const CreateMessage *msg,
+                                      int64_t resume_time) override;
+
   static VideoPlayer *FindPlayerById(int64_t player_id) {
     auto iter = players_.find(player_id);
     if (iter != players_.end()) {
@@ -348,6 +353,29 @@ ErrorOr<bool> VideoPlayerTizenPlugin::SetDisplayMode(
   return player->SetDisplayMode(msg.display_mode());
 }
 
+std::optional<FlutterError> VideoPlayerTizenPlugin::Suspend(int64_t player_id) {
+  VideoPlayer *player = FindPlayerById(player_id);
+  if (!player) {
+    return FlutterError("Invalid argument", "Player not found");
+  }
+  if (!player->Suspend()) {
+    return FlutterError("Operation failed", "Player suspend error");
+  }
+  return std::nullopt;
+}
+std::optional<FlutterError> VideoPlayerTizenPlugin::Restore(
+    int64_t player_id, const CreateMessage *msg, int64_t resume_time) {
+  VideoPlayer *player = FindPlayerById(player_id);
+  if (!player) {
+    return FlutterError("Invalid argument", "Player not found");
+  }
+
+  if (!player->Restore(msg, resume_time)) {
+    return FlutterError("Operation failed", "Player restore error");
+  }
+  return std::nullopt;
+}
+
 std::optional<FlutterError> VideoPlayerTizenPlugin::SetStreamingProperty(
     const StreamingPropertyMessage &msg) {
   VideoPlayer *player = FindPlayerById(msg.player_id());


Reviews:
user: login: xiaowei-guan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T01:34:49Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T08:14:29Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T08:14:36Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T08:14:43Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T08:15:15Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T08:15:43Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T08:16:23Z
user: login: JSUYA
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-31T05:45:41Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-31T08:40:20Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-31T08:40:37Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-31T08:40:43Z
user: login: xiaowei-guan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T03:03:30Z
user: login: JSUYA
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-01T04:50:44Z
user: login: xiaowei-guan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T08:17:00Z
user: login: xiaowei-guan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T12:32:33Z
user: login: xiaowei-guan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T01:38:11Z
user: login: JSUYA
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-02T06:23:46Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T01:33:55Z
user: login: JSUYA
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T02:52:53Z
user: login: seungsoo47
body: While testing the restore() and suspend() functions several times, I noticed something odd.
```
[I] flutter: suspend() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24
[I] flutter: suspend() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35
[I] flutter: restore() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24, https://media.w3.org/2010/05/bunny/trailer.mp4
[I] flutter: restore() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35, null
```
Like the logs (I inserted), suspend() and restore() were sometimes called two times.
And then, the video player didn't work properly.
(Note that I did not find the reproduction path, but the logs often appeared when I tested them a lot.)

state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T03:23:48Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T01:35:45Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T02:20:24Z
user: login: JSUYA
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T05:37:13Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T05:38:38Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T05:38:49Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T05:43:02Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T05:44:28Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T05:44:36Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T05:44:56Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T05:45:20Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T05:45:29Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T05:46:03Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T05:54:58Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T06:49:46Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T06:50:00Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T06:53:16Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T06:53:54Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T07:53:44Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T07:54:13Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T07:58:04Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T07:58:20Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T07:59:21Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T08:01:11Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T08:01:26Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T08:01:38Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T08:02:18Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T08:02:26Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T08:03:05Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T08:03:13Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T08:04:54Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T08:08:42Z
user: login: JSUYA
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T10:46:06Z
user: login: xiaowei-guan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T01:40:56Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T02:00:16Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T02:00:33Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T02:00:48Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T02:31:50Z
user: login: JSUYA
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-08T10:21:46Z
user: login: JSUYA
body: Overall, looks good.
It seems to be more cleaner since suspend and restore are switched to private.
Please change PR title and CHANGELOG.

If the issue reported by @seungsoo47 is resolved and there is no problem with operation, please merge it.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-08T10:31:05Z
user: login: gin7773
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T01:18:27Z
user: login: seungsoo47
body: LGTM
The above issue (the function being called twice) will continue to be tracked in https://github.com/flutter-tizen/plugins/issues/846
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T09:24:21Z

Review comments:
diff_hunk: @@ -22,6 +22,11 @@ export 'src/closed_caption_file.dart';
 export 'src/drm_configs.dart';
 export 'src/tracks.dart';
 
+// ignore: public_member_api_docs
path: packages/video_player_avplay/lib/video_player.dart
user: login: xiaowei-guan
body: Please public member api doc
created_at: 2025-03-28T00:41:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,6 +22,11 @@ export 'src/closed_caption_file.dart';
 export 'src/drm_configs.dart';
 export 'src/tracks.dart';
 
+// ignore: public_member_api_docs
path: packages/video_player_avplay/lib/video_player.dart
user: login: xiaowei-guan
body: IntCallback to ResumeTimeCallback
created_at: 2025-03-28T00:56:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,6 +22,11 @@ export 'src/closed_caption_file.dart';
 export 'src/drm_configs.dart';
 export 'src/tracks.dart';
 
+// ignore: public_member_api_docs
+typedef IntCallback = int Function();
+// ignore: public_member_api_docs
path: packages/video_player_avplay/lib/video_player.dart
user: login: xiaowei-guan
body: Add public memeber api doc
created_at: 2025-03-28T00:56:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -865,6 +872,148 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
+  Future<void> setRestoreData({
+    DataSourceCallback? callback1,
path: packages/video_player_avplay/lib/video_player.dart
user: login: xiaowei-guan
body: rename callback1 callback2
created_at: 2025-03-28T00:57:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -865,6 +872,148 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
+  Future<void> setRestoreData({
+    DataSourceCallback? callback1,
+    IntCallback? callback2,
+  }) async {
+    _onRestoreData = callback1;
+    _onRestoreTime = callback2;
+  }
+
+  ///Pauses the player  when the application is sent to the background.
+  ///Saves the current statistics for the ongoing playback session.
+  Future<void> suspend() async {
+    if (_isDisposedOrNotInitialized) {
+      return;
+    }
+
+    await _videoPlayerPlatform.suspend(_playerId);
+    _durationTimer?.cancel();
+    await pause();
+  }
+
+  ///Restores the player state when the application is resumed.
+  ///For live streaming or DRM-encrypted content playback, you must check whether the
+  ///streaming URL has changed or the DRM session or license has expired, and specify
+  ///the new URL and DRM information as needed.
+  Future<void> restore() async {
+    if (_isDisposedOrNotInitialized) {
+      return;
+    }
+
+    final DataSource? dataSource =
+        (_onRestoreData != null) ? _onRestoreData!() : null;
+    final int resumeTime = (_onRestoreTime != null) ? _onRestoreTime!() : -1;
+
+    _timer?.cancel();
+    _timer = Timer.periodic(const Duration(milliseconds: 500), (
+      Timer timer,
+    ) async {
+      if (_isDisposed) {
+        return;
+      }
+      final Duration? newPosition = await position;
+      if (newPosition == null) {
+        return;
+      }
+      _updatePosition(newPosition);
+    });
+
+    await _videoPlayerPlatform.restore(
+      _playerId,
+      dataSource: dataSource,
+      resumeTime: resumeTime,
+    );
+
+    final Completer<void> initializingCompleter = Completer<void>();
+
+    void eventListener(VideoEvent event) {
path: packages/video_player_avplay/lib/video_player.dart
user: login: xiaowei-guan
body: Please check why need to register eventListener again?
created_at: 2025-03-28T01:03:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,228 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: xiaowei-guan
body: Check re_create unique_ptr
created_at: 2025-03-28T01:14:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,228 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_suspended_ = true;
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
+  if (state <= plusplayer::State::kTrackSourceReady) {
+    ret = Close(player_);
+    assert(ret == true && "[PlusPlayer] Player close fail");
+    LOG_INFO("[PlusPlayer] Player is in invalid state [%d].", state);
+    return true;
+  } else if (state != plusplayer::State::kPaused) {
+    LOG_INFO("[PlusPlayer] Player calling pause from suspend.");
+    if (::Suspend(player_) == false) {
+      LOG_ERROR(
+          "[PlusPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      ret = StopAndClose();
+      assert(ret == true &&
+             "[PlusPlayer] Suspend error, palyer StopAndClose fail");
+      return false;
+    }
+  }
+  return true;
+}
+
+bool PlusPlayer::Restore(const CreateMessage *restore_message,
+                         int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] Restore is called.");
+
+  plusplayer::State state;
+  if (player_) {
+    state = GetState(player_);
+    if (state != plusplayer::State::kNone &&
+        state != plusplayer::State::kPaused &&
+        state != plusplayer::State::kPlaying) {
+      LOG_ERROR("[PlusPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (!memento_) {
+    LOG_ERROR(
+        "[PlusPlayer] No memento to restore. Player is in invalid state [%d]",
+        state);
+    return false;
+  }
+
+  is_suspended_ = false;
+  bool ret = false;
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[PlusPlayer] Restore URL is not emptpy, close the existing instance.");
+    ret = StopAndClose();
+    assert(ret == true &&
+           "[PlusPlayer] RestoreUrl is not empty, player StopAndClose fail");
+  }
+
+  state = GetState(player_);
+  switch (state) {
+    case plusplayer::State::kNone:
+      ret = RestorePlayer(restore_message, resume_time);
+      return ret;
+      break;
+    case plusplayer::State::kIdle:
+    case plusplayer::State::kTypeFinderReady:
+    case plusplayer::State::kTrackSourceReady:
+      LOG_ERROR("[PlusPlayer] Player is in invalid state.");
+      return false;
+    case plusplayer::State::kReady:
+    case plusplayer::State::kPaused:
+      if (!player_) {
+        LOG_ERROR("[PlusPlayer] Player is not created.");
+        return false;
+      }
+      if (!::Restore(player_, memento_->state)) {
+        ret = StopAndClose();
+        assert(ret == true && "[PlusPlayer] StopAndClose fail");
+        ret = RestorePlayer(restore_message, resume_time);
+        return ret;
+      } else {
+        return true;
+      }
+      break;
+    case plusplayer::State::kPlaying:
+      // might be the case that widget has called
+      // restore more than once, just ignore.
+      break;
+    default:
+      LOG_INFO(
+          "[PlusPlayer] Unhandled state, dont know how to process, just return "
+          "false.");
+      return false;
+  }
+  return true;
+}
+
+bool PlusPlayer::RestorePlayer(const CreateMessage *restore_message,
+                               int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] RestorePlayer is called.");
+  LOG_INFO("[PlusPlayer] is_live: %d", memento_->is_live);
+
+  if (restore_message->uri()) {
+    LOG_INFO("previous url: %s", url_.c_str());
+    LOG_INFO("new url: %s", restore_message->uri()->c_str());
+    url_ = *restore_message->uri();
+    create_message_ = *restore_message;
+  }
+
+  LOG_INFO("previous playing time: %llu ms", memento_->playing_time);
+  LOG_INFO("new resume time: %lld ms", resume_time);
+  // resume_time < 0  ==> use previous playing time
+  // resume_time == 0 ==> play from beginning
+  // resume_time > 0  ==> play from resume_time(Third-party settings)
+  if (resume_time >= 0)
+    memento_->playing_time = static_cast<uint64_t>(resume_time);
+
+  if (player_) {
+    plusplayer::State state = GetState(player_);
+    if (state >= plusplayer::State::kReady) {
+      LOG_ERROR("[PlusPlayer] Player is in invalid state.");
+      return false;
+    }
+  }
+
+  is_initialized_ = false;
+  if (Create(url_, create_message_) < 0) {
+    LOG_ERROR("[PlusPlayer] Fail to create player.");
+    return false;
+  }
+  if (memento_->playing_time > 0 && !Seek(player_, memento_->playing_time)) {
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: xiaowei-guan
body: Update resume time to createMessage
created_at: 2025-03-28T01:32:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,228 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_suspended_ = true;
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
+  if (state <= plusplayer::State::kTrackSourceReady) {
+    ret = Close(player_);
+    assert(ret == true && "[PlusPlayer] Player close fail");
+    LOG_INFO("[PlusPlayer] Player is in invalid state [%d].", state);
+    return true;
+  } else if (state != plusplayer::State::kPaused) {
+    LOG_INFO("[PlusPlayer] Player calling pause from suspend.");
+    if (::Suspend(player_) == false) {
+      LOG_ERROR(
+          "[PlusPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      ret = StopAndClose();
+      assert(ret == true &&
+             "[PlusPlayer] Suspend error, palyer StopAndClose fail");
+      return false;
+    }
+  }
+  return true;
+}
+
+bool PlusPlayer::Restore(const CreateMessage *restore_message,
+                         int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] Restore is called.");
+
+  plusplayer::State state;
+  if (player_) {
+    state = GetState(player_);
+    if (state != plusplayer::State::kNone &&
+        state != plusplayer::State::kPaused &&
+        state != plusplayer::State::kPlaying) {
+      LOG_ERROR("[PlusPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (!memento_) {
+    LOG_ERROR(
+        "[PlusPlayer] No memento to restore. Player is in invalid state [%d]",
+        state);
+    return false;
+  }
+
+  is_suspended_ = false;
+  bool ret = false;
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[PlusPlayer] Restore URL is not emptpy, close the existing instance.");
+    ret = StopAndClose();
+    assert(ret == true &&
+           "[PlusPlayer] RestoreUrl is not empty, player StopAndClose fail");
+  }
+
+  state = GetState(player_);
+  switch (state) {
+    case plusplayer::State::kNone:
+      ret = RestorePlayer(restore_message, resume_time);
+      return ret;
+      break;
+    case plusplayer::State::kIdle:
+    case plusplayer::State::kTypeFinderReady:
+    case plusplayer::State::kTrackSourceReady:
+      LOG_ERROR("[PlusPlayer] Player is in invalid state.");
+      return false;
+    case plusplayer::State::kReady:
+    case plusplayer::State::kPaused:
+      if (!player_) {
+        LOG_ERROR("[PlusPlayer] Player is not created.");
+        return false;
+      }
+      if (!::Restore(player_, memento_->state)) {
+        ret = StopAndClose();
+        assert(ret == true && "[PlusPlayer] StopAndClose fail");
+        ret = RestorePlayer(restore_message, resume_time);
+        return ret;
+      } else {
+        return true;
+      }
+      break;
+    case plusplayer::State::kPlaying:
+      // might be the case that widget has called
+      // restore more than once, just ignore.
+      break;
+    default:
+      LOG_INFO(
+          "[PlusPlayer] Unhandled state, dont know how to process, just return "
+          "false.");
+      return false;
+  }
+  return true;
+}
+
+bool PlusPlayer::RestorePlayer(const CreateMessage *restore_message,
+                               int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] RestorePlayer is called.");
+  LOG_INFO("[PlusPlayer] is_live: %d", memento_->is_live);
+
+  if (restore_message->uri()) {
+    LOG_INFO("previous url: %s", url_.c_str());
+    LOG_INFO("new url: %s", restore_message->uri()->c_str());
+    url_ = *restore_message->uri();
+    create_message_ = *restore_message;
+  }
+
+  LOG_INFO("previous playing time: %llu ms", memento_->playing_time);
+  LOG_INFO("new resume time: %lld ms", resume_time);
+  // resume_time < 0  ==> use previous playing time
+  // resume_time == 0 ==> play from beginning
+  // resume_time > 0  ==> play from resume_time(Third-party settings)
+  if (resume_time >= 0)
+    memento_->playing_time = static_cast<uint64_t>(resume_time);
+
+  if (player_) {
+    plusplayer::State state = GetState(player_);
+    if (state >= plusplayer::State::kReady) {
+      LOG_ERROR("[PlusPlayer] Player is in invalid state.");
+      return false;
+    }
+  }
+
+  is_initialized_ = false;
+  if (Create(url_, create_message_) < 0) {
+    LOG_ERROR("[PlusPlayer] Fail to create player.");
+    return false;
+  }
+  if (memento_->playing_time > 0 && !Seek(player_, memento_->playing_time)) {
+    LOG_INFO("[PlusPlayer] Fail to seek.");
+  }
+  SetDisplayRoi(memento_->display_area.x, memento_->display_area.y,
+                memento_->display_area.w, memento_->display_area.h);
+
+  if (memento_->state == plusplayer::State::kPlaying ||
+      restore_message->uri() || memento_->is_live) {
+    SendIsPlayingState(true);
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: xiaowei-guan
body: You should keep sendIninialIzed event when receiveing onPrepareDone  callback
created_at: 2025-03-28T01:34:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,6 +22,11 @@ export 'src/closed_caption_file.dart';
 export 'src/drm_configs.dart';
 export 'src/tracks.dart';
 
+// ignore: public_member_api_docs
path: packages/video_player_avplay/lib/video_player.dart
user: login: gin7773
body: done
created_at: 2025-03-28T08:14:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,6 +22,11 @@ export 'src/closed_caption_file.dart';
 export 'src/drm_configs.dart';
 export 'src/tracks.dart';
 
+// ignore: public_member_api_docs
+typedef IntCallback = int Function();
+// ignore: public_member_api_docs
path: packages/video_player_avplay/lib/video_player.dart
user: login: gin7773
body: done
created_at: 2025-03-28T08:14:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -865,6 +872,148 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
+  Future<void> setRestoreData({
+    DataSourceCallback? callback1,
path: packages/video_player_avplay/lib/video_player.dart
user: login: gin7773
body: done
created_at: 2025-03-28T08:14:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -865,6 +872,148 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
+  Future<void> setRestoreData({
+    DataSourceCallback? callback1,
+    IntCallback? callback2,
+  }) async {
+    _onRestoreData = callback1;
+    _onRestoreTime = callback2;
+  }
+
+  ///Pauses the player  when the application is sent to the background.
+  ///Saves the current statistics for the ongoing playback session.
+  Future<void> suspend() async {
+    if (_isDisposedOrNotInitialized) {
+      return;
+    }
+
+    await _videoPlayerPlatform.suspend(_playerId);
+    _durationTimer?.cancel();
+    await pause();
+  }
+
+  ///Restores the player state when the application is resumed.
+  ///For live streaming or DRM-encrypted content playback, you must check whether the
+  ///streaming URL has changed or the DRM session or license has expired, and specify
+  ///the new URL and DRM information as needed.
+  Future<void> restore() async {
+    if (_isDisposedOrNotInitialized) {
+      return;
+    }
+
+    final DataSource? dataSource =
+        (_onRestoreData != null) ? _onRestoreData!() : null;
+    final int resumeTime = (_onRestoreTime != null) ? _onRestoreTime!() : -1;
+
+    _timer?.cancel();
+    _timer = Timer.periodic(const Duration(milliseconds: 500), (
+      Timer timer,
+    ) async {
+      if (_isDisposed) {
+        return;
+      }
+      final Duration? newPosition = await position;
+      if (newPosition == null) {
+        return;
+      }
+      _updatePosition(newPosition);
+    });
+
+    await _videoPlayerPlatform.restore(
+      _playerId,
+      dataSource: dataSource,
+      resumeTime: resumeTime,
+    );
+
+    final Completer<void> initializingCompleter = Completer<void>();
+
+    void eventListener(VideoEvent event) {
path: packages/video_player_avplay/lib/video_player.dart
user: login: gin7773
body: Deleted.
created_at: 2025-03-28T08:15:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,228 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: gin7773
body: add "drm_manager_.reset();"
created_at: 2025-03-28T08:15:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,228 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_suspended_ = true;
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
+  if (state <= plusplayer::State::kTrackSourceReady) {
+    ret = Close(player_);
+    assert(ret == true && "[PlusPlayer] Player close fail");
+    LOG_INFO("[PlusPlayer] Player is in invalid state [%d].", state);
+    return true;
+  } else if (state != plusplayer::State::kPaused) {
+    LOG_INFO("[PlusPlayer] Player calling pause from suspend.");
+    if (::Suspend(player_) == false) {
+      LOG_ERROR(
+          "[PlusPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      ret = StopAndClose();
+      assert(ret == true &&
+             "[PlusPlayer] Suspend error, palyer StopAndClose fail");
+      return false;
+    }
+  }
+  return true;
+}
+
+bool PlusPlayer::Restore(const CreateMessage *restore_message,
+                         int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] Restore is called.");
+
+  plusplayer::State state;
+  if (player_) {
+    state = GetState(player_);
+    if (state != plusplayer::State::kNone &&
+        state != plusplayer::State::kPaused &&
+        state != plusplayer::State::kPlaying) {
+      LOG_ERROR("[PlusPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (!memento_) {
+    LOG_ERROR(
+        "[PlusPlayer] No memento to restore. Player is in invalid state [%d]",
+        state);
+    return false;
+  }
+
+  is_suspended_ = false;
+  bool ret = false;
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[PlusPlayer] Restore URL is not emptpy, close the existing instance.");
+    ret = StopAndClose();
+    assert(ret == true &&
+           "[PlusPlayer] RestoreUrl is not empty, player StopAndClose fail");
+  }
+
+  state = GetState(player_);
+  switch (state) {
+    case plusplayer::State::kNone:
+      ret = RestorePlayer(restore_message, resume_time);
+      return ret;
+      break;
+    case plusplayer::State::kIdle:
+    case plusplayer::State::kTypeFinderReady:
+    case plusplayer::State::kTrackSourceReady:
+      LOG_ERROR("[PlusPlayer] Player is in invalid state.");
+      return false;
+    case plusplayer::State::kReady:
+    case plusplayer::State::kPaused:
+      if (!player_) {
+        LOG_ERROR("[PlusPlayer] Player is not created.");
+        return false;
+      }
+      if (!::Restore(player_, memento_->state)) {
+        ret = StopAndClose();
+        assert(ret == true && "[PlusPlayer] StopAndClose fail");
+        ret = RestorePlayer(restore_message, resume_time);
+        return ret;
+      } else {
+        return true;
+      }
+      break;
+    case plusplayer::State::kPlaying:
+      // might be the case that widget has called
+      // restore more than once, just ignore.
+      break;
+    default:
+      LOG_INFO(
+          "[PlusPlayer] Unhandled state, dont know how to process, just return "
+          "false.");
+      return false;
+  }
+  return true;
+}
+
+bool PlusPlayer::RestorePlayer(const CreateMessage *restore_message,
+                               int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] RestorePlayer is called.");
+  LOG_INFO("[PlusPlayer] is_live: %d", memento_->is_live);
+
+  if (restore_message->uri()) {
+    LOG_INFO("previous url: %s", url_.c_str());
+    LOG_INFO("new url: %s", restore_message->uri()->c_str());
+    url_ = *restore_message->uri();
+    create_message_ = *restore_message;
+  }
+
+  LOG_INFO("previous playing time: %llu ms", memento_->playing_time);
+  LOG_INFO("new resume time: %lld ms", resume_time);
+  // resume_time < 0  ==> use previous playing time
+  // resume_time == 0 ==> play from beginning
+  // resume_time > 0  ==> play from resume_time(Third-party settings)
+  if (resume_time >= 0)
+    memento_->playing_time = static_cast<uint64_t>(resume_time);
+
+  if (player_) {
+    plusplayer::State state = GetState(player_);
+    if (state >= plusplayer::State::kReady) {
+      LOG_ERROR("[PlusPlayer] Player is in invalid state.");
+      return false;
+    }
+  }
+
+  is_initialized_ = false;
+  if (Create(url_, create_message_) < 0) {
+    LOG_ERROR("[PlusPlayer] Fail to create player.");
+    return false;
+  }
+  if (memento_->playing_time > 0 && !Seek(player_, memento_->playing_time)) {
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: gin7773
body: The type is "const", can not be updated
created_at: 2025-03-28T08:16:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -49,6 +51,8 @@ class _App extends StatelessWidget {
             _DrmRemoteVideo2(),
             _TrackTest(),
             _AssetVideo(),
+            _LiveRemoteVideo(),
+            _TestRemoteVideo(),
path: packages/video_player_avplay/example/lib/main.dart
user: login: JSUYA
body: Change the length of TabController to 9.
 `length: 7,`  -> `length: 9,`
created_at: 2025-03-31T05:29:37Z
author_association: MEMBER
diff_hunk: @@ -760,3 +767,228 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: JSUYA
body: I have a question.
The reason why power off/on check is needed in avplay is because there is a problem with video playback when the app cycle is paused/resume.
Does also pause/resume in media player also cause the same hw decoder problem?
Is it really necessary to destroy and re-create the media player? Can't it be replaced with simply pause/resume?
created_at: 2025-03-31T05:45:34Z
author_association: MEMBER
diff_hunk: @@ -760,3 +767,228 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: gin7773
body: We discussed this with MM team and they suggested that it is best to destroy and recreate the media player's instance when power off/on.
created_at: 2025-03-31T08:40:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -49,6 +51,8 @@ class _App extends StatelessWidget {
             _DrmRemoteVideo2(),
             _TrackTest(),
             _AssetVideo(),
+            _LiveRemoteVideo(),
+            _TestRemoteVideo(),
path: packages/video_player_avplay/example/lib/main.dart
user: login: gin7773
body: done
created_at: 2025-03-31T08:40:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,228 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_suspended_ = true;
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
+  if (state <= plusplayer::State::kTrackSourceReady) {
+    ret = Close(player_);
+    assert(ret == true && "[PlusPlayer] Player close fail");
+    LOG_INFO("[PlusPlayer] Player is in invalid state [%d].", state);
+    return true;
+  } else if (state != plusplayer::State::kPaused) {
+    LOG_INFO("[PlusPlayer] Player calling pause from suspend.");
+    if (::Suspend(player_) == false) {
+      LOG_ERROR(
+          "[PlusPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      ret = StopAndClose();
+      assert(ret == true &&
+             "[PlusPlayer] Suspend error, palyer StopAndClose fail");
+      return false;
+    }
+  }
+  return true;
+}
+
+bool PlusPlayer::Restore(const CreateMessage *restore_message,
+                         int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] Restore is called.");
+
+  plusplayer::State state;
+  if (player_) {
+    state = GetState(player_);
+    if (state != plusplayer::State::kNone &&
+        state != plusplayer::State::kPaused &&
+        state != plusplayer::State::kPlaying) {
+      LOG_ERROR("[PlusPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (!memento_) {
+    LOG_ERROR(
+        "[PlusPlayer] No memento to restore. Player is in invalid state [%d]",
+        state);
+    return false;
+  }
+
+  is_suspended_ = false;
+  bool ret = false;
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[PlusPlayer] Restore URL is not emptpy, close the existing instance.");
+    ret = StopAndClose();
+    assert(ret == true &&
+           "[PlusPlayer] RestoreUrl is not empty, player StopAndClose fail");
+  }
+
+  state = GetState(player_);
+  switch (state) {
+    case plusplayer::State::kNone:
+      ret = RestorePlayer(restore_message, resume_time);
+      return ret;
+      break;
+    case plusplayer::State::kIdle:
+    case plusplayer::State::kTypeFinderReady:
+    case plusplayer::State::kTrackSourceReady:
+      LOG_ERROR("[PlusPlayer] Player is in invalid state.");
+      return false;
+    case plusplayer::State::kReady:
+    case plusplayer::State::kPaused:
+      if (!player_) {
+        LOG_ERROR("[PlusPlayer] Player is not created.");
+        return false;
+      }
+      if (!::Restore(player_, memento_->state)) {
+        ret = StopAndClose();
+        assert(ret == true && "[PlusPlayer] StopAndClose fail");
+        ret = RestorePlayer(restore_message, resume_time);
+        return ret;
+      } else {
+        return true;
+      }
+      break;
+    case plusplayer::State::kPlaying:
+      // might be the case that widget has called
+      // restore more than once, just ignore.
+      break;
+    default:
+      LOG_INFO(
+          "[PlusPlayer] Unhandled state, dont know how to process, just return "
+          "false.");
+      return false;
+  }
+  return true;
+}
+
+bool PlusPlayer::RestorePlayer(const CreateMessage *restore_message,
+                               int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] RestorePlayer is called.");
+  LOG_INFO("[PlusPlayer] is_live: %d", memento_->is_live);
+
+  if (restore_message->uri()) {
+    LOG_INFO("previous url: %s", url_.c_str());
+    LOG_INFO("new url: %s", restore_message->uri()->c_str());
+    url_ = *restore_message->uri();
+    create_message_ = *restore_message;
+  }
+
+  LOG_INFO("previous playing time: %llu ms", memento_->playing_time);
+  LOG_INFO("new resume time: %lld ms", resume_time);
+  // resume_time < 0  ==> use previous playing time
+  // resume_time == 0 ==> play from beginning
+  // resume_time > 0  ==> play from resume_time(Third-party settings)
+  if (resume_time >= 0)
+    memento_->playing_time = static_cast<uint64_t>(resume_time);
+
+  if (player_) {
+    plusplayer::State state = GetState(player_);
+    if (state >= plusplayer::State::kReady) {
+      LOG_ERROR("[PlusPlayer] Player is in invalid state.");
+      return false;
+    }
+  }
+
+  is_initialized_ = false;
+  if (Create(url_, create_message_) < 0) {
+    LOG_ERROR("[PlusPlayer] Fail to create player.");
+    return false;
+  }
+  if (memento_->playing_time > 0 && !Seek(player_, memento_->playing_time)) {
+    LOG_INFO("[PlusPlayer] Fail to seek.");
+  }
+  SetDisplayRoi(memento_->display_area.x, memento_->display_area.y,
+                memento_->display_area.w, memento_->display_area.h);
+
+  if (memento_->state == plusplayer::State::kPlaying ||
+      restore_message->uri() || memento_->is_live) {
+    SendIsPlayingState(true);
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: gin7773
body: done
created_at: 2025-03-31T08:40:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -358,9 +400,16 @@ class VideoPlayerTizen extends VideoPlayerPlatform {
       final Map<dynamic, dynamic> map = event as Map<dynamic, dynamic>;
       switch (map['event']) {
         case 'initialized':
+        case 'isRestorePlayer':
           final List<dynamic>? durationVal = map['duration'] as List<dynamic>?;
+          VideoEventType videoEventType;
+          if (map['event'] == 'initialized') {
+            videoEventType = VideoEventType.initialized;
+          } else {
+            videoEventType = VideoEventType.isRestorePlayer;
path: packages/video_player_avplay/lib/src/video_player_tizen.dart
user: login: xiaowei-guan
body: Suggest change isRestorePlayer to restoreCompleted
created_at: 2025-04-01T02:46:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -865,6 +881,53 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
+  Future<void> setRestoreData({
path: packages/video_player_avplay/lib/video_player.dart
user: login: xiaowei-guan
body: Suggest delete async
created_at: 2025-04-01T02:50:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -865,6 +881,53 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
+  Future<void> setRestoreData({
+    RecreateMessageCallback? recreateMessage,
+    ResumeTimeCallback? resumeTime,
+  }) async {
+    _onRestoreData = recreateMessage;
+    _onRestoreTime = resumeTime;
+  }
+
+  ///Pauses the player  when the application is sent to the background.
+  ///Saves the current statistics for the ongoing playback session.
+  Future<void> suspend() async {
+    if (_isDisposedOrNotInitialized) {
+      return;
+    }
+
+    await _videoPlayerPlatform.suspend(_playerId);
+    _durationTimer?.cancel();
+    _timer?.cancel();
+  }
+
+  ///Restores the player state when the application is resumed.
+  ///For live streaming or DRM-encrypted content playback, you must check whether the
+  ///streaming URL has changed or the DRM session or license has expired, and specify
+  ///the new URL and DRM information as needed.
+  Future<void> restore() async {
+    if (_isDisposedOrNotInitialized) {
+      return;
+    }
+
+    final DataSource? dataSource =
+        (_onRestoreData != null) ? _onRestoreData!() : null;
+    final int resumeTime = (_onRestoreTime != null) ? _onRestoreTime!() : -1;
+
+    _timer = _createTimer(500, false);
path: packages/video_player_avplay/lib/video_player.dart
user: login: xiaowei-guan
body: If jsut for update position, suggest to call updatePosition
created_at: 2025-04-01T02:53:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +767,228 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: JSUYA
body: I know. The discussion about that issue was when using plus player. What I'm asking is whether the same issue occurs in the case of media player. If the same issue occurs when using media player, there may be a similar issue in MM of the platform. Also, it seems like the same fix may need to be made to the video_player_tizen plugin.
created_at: 2025-04-01T04:50:44Z
author_association: MEMBER
diff_hunk: @@ -760,3 +767,228 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: xiaowei-guan
body: We can't add these code to video_player_tizen, because we can't modeify video_player.dart directly.
But we can merge these code to video_player_videohole plugin.
created_at: 2025-04-01T08:17:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -115,6 +117,7 @@ void VideoPlayer::SendInitialized() {
          flutter::EncodableValue(duration_range)},
         {flutter::EncodableValue("width"), flutter::EncodableValue(width)},
         {flutter::EncodableValue("height"), flutter::EncodableValue(height)},
+        {flutter::EncodableValue("isLive"), flutter::EncodableValue(is_live)},
path: packages/video_player_avplay/tizen/src/video_player.cc
user: login: xiaowei-guan
body: Actually, we can change to  {flutter::EncodableValue("isLive"), flutter::EncodableValue(IsLive())}
created_at: 2025-04-01T12:20:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,218 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+
+  bool ret = 0;
+  int res = PLAYER_ERROR_NONE;
+  player_state_e state = PLAYER_STATE_NONE;
+
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_get_state(player_, &state);
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+    return false;
+  }
+
+  if (is_buffering_) {
+    LOG_ERROR("[MediaPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  pre_playing_time_ = GetPosition();
+  assert(pre_playing_time_ >= 0 && "[MediaPlayer] GetPosition failed.");
+  ret = player_get_state(player_, &pre_state_);
+  assert(ret == PLAYER_ERROR_NONE &&
+         "[MediaPlayer] player_get_state_ex failed.");
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: xiaowei-guan
body: Only need to call get_state one time .
created_at: 2025-04-02T01:01:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,218 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+
+  bool ret = 0;
+  int res = PLAYER_ERROR_NONE;
+  player_state_e state = PLAYER_STATE_NONE;
+
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_get_state(player_, &state);
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+    return false;
+  }
+
+  if (is_buffering_) {
+    LOG_ERROR("[MediaPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  pre_playing_time_ = GetPosition();
+  assert(pre_playing_time_ >= 0 && "[MediaPlayer] GetPosition failed.");
+  ret = player_get_state(player_, &pre_state_);
+  assert(ret == PLAYER_ERROR_NONE &&
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: xiaowei-guan
body: Suggest remove assert
created_at: 2025-04-02T01:03:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,218 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+
+  bool ret = 0;
+  int res = PLAYER_ERROR_NONE;
+  player_state_e state = PLAYER_STATE_NONE;
+
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_get_state(player_, &state);
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+    return false;
+  }
+
+  if (is_buffering_) {
+    LOG_ERROR("[MediaPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  pre_playing_time_ = GetPosition();
+  assert(pre_playing_time_ >= 0 && "[MediaPlayer] GetPosition failed.");
+  ret = player_get_state(player_, &pre_state_);
+  assert(ret == PLAYER_ERROR_NONE &&
+         "[MediaPlayer] player_get_state_ex failed.");
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[MediaPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    ret = StopAndDestroy();
+    assert(ret == true && "[MediaPlayer] Player StopAndDestroy fail.");
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  res = player_get_state(player_, &state);
+  assert(res == PLAYER_ERROR_NONE && "[MediaPlayer] player_get_state failed.");
+  if (state == PLAYER_STATE_NONE) {
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: xiaowei-guan
body: Duplicated status check, remove
created_at: 2025-04-02T01:11:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,218 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+
+  bool ret = 0;
+  int res = PLAYER_ERROR_NONE;
+  player_state_e state = PLAYER_STATE_NONE;
+
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_get_state(player_, &state);
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+    return false;
+  }
+
+  if (is_buffering_) {
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: xiaowei-guan
body: Suggest remove check buffering code, becacuse the player can be buffering.
created_at: 2025-04-02T01:14:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,218 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+
+  bool ret = 0;
+  int res = PLAYER_ERROR_NONE;
+  player_state_e state = PLAYER_STATE_NONE;
+
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_get_state(player_, &state);
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+    return false;
+  }
+
+  if (is_buffering_) {
+    LOG_ERROR("[MediaPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  pre_playing_time_ = GetPosition();
+  assert(pre_playing_time_ >= 0 && "[MediaPlayer] GetPosition failed.");
+  ret = player_get_state(player_, &pre_state_);
+  assert(ret == PLAYER_ERROR_NONE &&
+         "[MediaPlayer] player_get_state_ex failed.");
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[MediaPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    ret = StopAndDestroy();
+    assert(ret == true && "[MediaPlayer] Player StopAndDestroy fail.");
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  res = player_get_state(player_, &state);
+  assert(res == PLAYER_ERROR_NONE && "[MediaPlayer] player_get_state failed.");
+  if (state == PLAYER_STATE_NONE) {
+    LOG_INFO("[MediaPlayer] Player is in none state, so no-operation.");
+    return true;
+  } else if (state == PLAYER_STATE_IDLE) {
+    ret = StopAndDestroy();
+    assert(ret == true && "[MediaPlayer] Player stop and unprepare fail");
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+    return true;
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      ret = StopAndDestroy();
+      assert(ret == true &&
+             "[MediaPlayer] Suspend error, palyer StopAndDestroy fail");
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  bool ret = false;
+
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state != PLAYER_STATE_PAUSED && state != PLAYER_STATE_PLAYING) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (is_buffering_) {
+    LOG_ERROR("[MediaPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[MediaPlayer] Restore URL is not emptpy, close the existing "
+        "instance.");
+    ret = StopAndDestroy();
+    assert(ret == true &&
+           "[MediaPlayer] RestoreUrl is not empty, player StopAndDestroy fail");
+  }
+
+  player_get_state(player_, &state);
+  switch (state) {
+    case PLAYER_STATE_NONE:
+      ret = RestorePlayer(restore_message, resume_time);
+      return ret;
+      break;
+    case PLAYER_STATE_IDLE:
+      LOG_ERROR("[MediaPlayer] Player is in invalid state.");
+      return false;
+    case PLAYER_STATE_PAUSED:
+      if (!player_) {
+        LOG_ERROR("[MediaPlayer] Player is not created.");
+        return false;
+      }
+      if (pre_state_ == PLAYER_STATE_PLAYING) {
+        ret = Play();
+        assert(ret == true && "[MediaPlayer] player play failed.");
+      }
+      return true;
+      break;
+    case PLAYER_STATE_PLAYING:
+      // might be the case that widget has called
+      // restore more than once, just ignore.
+      break;
+    default:
+      LOG_INFO(
+          "[MediaPlayer] Unhandled state, dont know how to process, just "
+          "return "
+          "false.");
+      return false;
+  }
+  return true;
+}
+
+bool MediaPlayer::RestorePlayer(const CreateMessage *restore_message,
+                                int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] RestorePlayer is called.");
+  player_state_e state = PLAYER_STATE_NONE;
+
+  if (restore_message->uri()) {
+    LOG_INFO("previous url: %s", url_.c_str());
+    LOG_INFO("new url: %s", restore_message->uri()->c_str());
+    url_ = *restore_message->uri();
+  }
+
+  LOG_INFO("previous playing time: %llu ms", pre_playing_time_);
+  LOG_INFO("new resume time: %lld ms", resume_time);
+  // resume_time < 0  ==> use previous playing time
+  // resume_time == 0 ==> play from beginning
+  // resume_time > 0  ==> play from resume_time(Third-party settings)
+  if (resume_time >= 0) pre_playing_time_ = static_cast<uint64_t>(resume_time);
+
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state >= PLAYER_STATE_READY) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state.");
+      return false;
+    }
+  }
+
+  restore_completed_ = true;
+  if (Create(url_, *restore_message) < 0) {
+    LOG_ERROR("[MediaPlayer] Fail to create player.");
+    return false;
+  }
+
+  SetDisplayRoi(pre_display_roi_x_, pre_display_roi_y_, pre_display_roi_width_,
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: xiaowei-guan
body: Remove this line code
created_at: 2025-04-02T01:21:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -509,32 +526,40 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
 
       switch (event.eventType) {
         case VideoEventType.initialized:
+        case VideoEventType.restoreCompleted:
           value = value.copyWith(
             duration: event.duration,
             size: event.size,
             isInitialized: event.duration != null,
             errorDescription: null,
             isCompleted: false,
+            isLive: event.isLive,
           );
-          assert(
-            !initializingCompleter.isCompleted,
-            'VideoPlayerController already initialized. This is typically a '
-            'sign that an implementation of the VideoPlayerPlatform '
-            '(${_videoPlayerPlatform.runtimeType}) has a bug and is sending '
-            'more than one initialized event per instance.',
-          );
-          if (initializingCompleter.isCompleted) {
-            throw StateError('VideoPlayerController already initialized');
+          if (VideoEventType.initialized == event.eventType) {
+            assert(
+              !initializingCompleter.isCompleted,
+              'VideoPlayerController already initialized. This is typically a '
+              'sign that an implementation of the VideoPlayerPlatform '
+              '(${_videoPlayerPlatform.runtimeType}) has a bug and is sending '
+              'more than one initialized event per instance.',
+            );
+            if (initializingCompleter.isCompleted) {
+              throw StateError('VideoPlayerController already initialized');
+            }
+            initializingCompleter.complete(null);
           }
-          initializingCompleter.complete(null);
           _applyLooping();
           // NOTE(jsuya): The plusplayer's SetVolume() work when player is
           // paused or played, so it changes the order of _applyPlayPause()
           // and _applyVolume().
-          _applyPlayPause();
+          if (value.isLive || _onRestoreData != null) {
path: packages/video_player_avplay/lib/video_player.dart
user: login: xiaowei-guan
body: app side decide to play and pause, remove this line code
created_at: 2025-04-02T01:35:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -664,24 +690,30 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
 
   /// The duration in the current video.
   Future<DurationRange?> get duration async {
-    if (_isDisposed) {
+    if (_isDisposed || _durationTimer == null) {
       return null;
     }
     return _videoPlayerPlatform.getDuration(_playerId);
   }
 
-  Timer _createDurationTimer() {
-    return Timer.periodic(const Duration(milliseconds: 1000), (
+  Timer _createTimer(int periodTime, bool isDurationTimer) {
path: packages/video_player_avplay/lib/video_player.dart
user: login: xiaowei-guan
body: Suggest extract to two methods
created_at: 2025-04-02T01:37:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +767,228 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: JSUYA
body: @gin7773 

Does the case using capi-media-player library(player.h with local assets) also cause the same problem as plusplayer in TV power off/on scenario?
I couldn't check the local asset of our example because it was too short.
As far as I know, suspend() is mainly related to maintaining keeps buffer or networks. However, since MediaPlayer only targets local data, I don't know if it is right to destroy it when the App cycle is paused.

@xiaowei-guan

> We can't add these code to video_player_tizen, because we can't modeify video_player.dart directly.

Yes, I was mistaken. As you said, there is no need to add it to video_player_tizen. It should be added to videohole.
created_at: 2025-04-02T06:23:46Z
author_association: MEMBER
diff_hunk: @@ -760,3 +767,228 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: gin7773
body: We consulted MM team. After the media player enters deep sleep, the decoder status may go wrong, and some chips may become abnormal.  So it must be destroyed and recreated after the media player enters deep sleep.
created_at: 2025-04-03T01:33:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +767,228 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: JSUYA
body: OK I understand media player is in the same situation. Thanks for the explanation.
created_at: 2025-04-03T02:52:52Z
author_association: MEMBER
diff_hunk: @@ -467,57 +467,60 @@ class _VideoPlayerAvplayApiCodec extends StandardMessageCodec {
     } else if (value is CreateMessage) {
       buffer.putUint8(129);
       writeValue(buffer, value.encode());
-    } else if (value is DashPropertyMapMessage) {
+    } else if (value is CreateMessage) {
path: packages/video_player_avplay/lib/src/messages.g.dart
user: login: seungsoo47
body: As far as I know, this file is auto-generated.
I am not sure but this line seems to be a duplicate of line 467.
Please double-check.
created_at: 2025-04-03T07:01:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -865,6 +885,48 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
+  void setRestoreData({
+    RecreateMessageCallback? recreateMessage,
+    ResumeTimeCallback? resumeTime,
+  }) {
+    _onRestoreData = recreateMessage;
+    _onRestoreTime = resumeTime;
+  }
+
+  ///Pauses the player  when the application is sent to the background.
+  ///Saves the current statistics for the ongoing playback session.
path: packages/video_player_avplay/lib/video_player.dart
user: login: seungsoo47
body: ```suggestion
  /// Pauses the player when the application is sent to the background.
  /// Saves the current statistics for the ongoing playback session.
```
created_at: 2025-04-03T08:00:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -865,6 +885,48 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
+  void setRestoreData({
+    RecreateMessageCallback? recreateMessage,
+    ResumeTimeCallback? resumeTime,
+  }) {
+    _onRestoreData = recreateMessage;
path: packages/video_player_avplay/lib/video_player.dart
user: login: seungsoo47
body: ```suggestion
    _onRestoreDataSource = recreateMessage;
    or 
    _onRestoreDataSource = restoreDataSource;
```
How about changing the variable name?
created_at: 2025-04-03T08:02:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -865,6 +885,48 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
+  void setRestoreData({
+    RecreateMessageCallback? recreateMessage,
+    ResumeTimeCallback? resumeTime,
+  }) {
+    _onRestoreData = recreateMessage;
+    _onRestoreTime = resumeTime;
+  }
+
+  ///Pauses the player  when the application is sent to the background.
+  ///Saves the current statistics for the ongoing playback session.
+  Future<void> suspend() async {
+    if (_isDisposedOrNotInitialized) {
+      return;
+    }
+
+    await _videoPlayerPlatform.suspend(_playerId);
+    _durationTimer?.cancel();
+    _timer?.cancel();
+    _timer = null;
+  }
+
+  ///Restores the player state when the application is resumed.
+  ///For live streaming or DRM-encrypted content playback, you must check whether the
+  ///streaming URL has changed or the DRM session or license has expired, and specify
+  ///the new URL and DRM information as needed.
path: packages/video_player_avplay/lib/video_player.dart
user: login: seungsoo47
body: ```suggestion
  /// Restores the player state when the application is resumed.
  /// For live streaming or DRM-encrypted content playback, you must check whether the
  /// streaming URL has changed or the DRM session or license has expired, and specify
  /// the new URL and DRM information as needed.
```
created_at: 2025-04-03T08:07:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -748,3 +752,142 @@ class _GetTextTrackButton extends StatelessWidget {
     );
   }
 }
+
+class _LiveRemoteVideo extends StatefulWidget {
+  @override
+  State<_LiveRemoteVideo> createState() => _LiveRomoteVideoState();
+}
+
+class _LiveRomoteVideoState extends State<_LiveRemoteVideo> {
+  late VideoPlayerController _controller;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = VideoPlayerController.network(
+      'https://hlive.ktv.go.kr/live/klive_h.stream/playlist.m3u8',
+    );
+
+    _controller.addListener(() {
+      if (_controller.value.hasError) {
+        print(_controller.value.errorDescription);
+      }
+      setState(() {});
+    });
+    _controller.setLooping(true);
+    _controller.initialize().then((_) => setState(() {}));
+    _controller.play();
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return SingleChildScrollView(
+      child: Column(
+        children: <Widget>[
+          Container(padding: const EdgeInsets.only(top: 20.0)),
+          const Text('Playing Live TV'),
+          Container(
+            padding: const EdgeInsets.all(20),
+            child: AspectRatio(
+              aspectRatio: _controller.value.aspectRatio,
+              child: Stack(
+                alignment: Alignment.bottomCenter,
+                children: <Widget>[
+                  VideoPlayer(_controller),
+                  ClosedCaption(text: _controller.value.caption.text),
+                  _ControlsOverlay(controller: _controller),
+                  VideoProgressIndicator(_controller, allowScrubbing: true),
+                ],
+              ),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}
+
+class _TestRemoteVideo extends StatefulWidget {
+  @override
+  State<_TestRemoteVideo> createState() => _TestRomoteVideoState();
path: packages/video_player_avplay/example/lib/main.dart
user: login: seungsoo47
body: ```suggestion
  State<_TestRemoteVideo> createState() => _TestRemoteVideoState();
```
Please correct a typo.
created_at: 2025-04-03T08:39:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -748,3 +752,142 @@ class _GetTextTrackButton extends StatelessWidget {
     );
   }
 }
+
+class _LiveRemoteVideo extends StatefulWidget {
+  @override
+  State<_LiveRemoteVideo> createState() => _LiveRomoteVideoState();
+}
+
+class _LiveRomoteVideoState extends State<_LiveRemoteVideo> {
+  late VideoPlayerController _controller;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = VideoPlayerController.network(
+      'https://hlive.ktv.go.kr/live/klive_h.stream/playlist.m3u8',
+    );
+
+    _controller.addListener(() {
+      if (_controller.value.hasError) {
+        print(_controller.value.errorDescription);
+      }
+      setState(() {});
+    });
+    _controller.setLooping(true);
+    _controller.initialize().then((_) => setState(() {}));
+    _controller.play();
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return SingleChildScrollView(
+      child: Column(
+        children: <Widget>[
+          Container(padding: const EdgeInsets.only(top: 20.0)),
+          const Text('Playing Live TV'),
+          Container(
+            padding: const EdgeInsets.all(20),
+            child: AspectRatio(
+              aspectRatio: _controller.value.aspectRatio,
+              child: Stack(
+                alignment: Alignment.bottomCenter,
+                children: <Widget>[
+                  VideoPlayer(_controller),
+                  ClosedCaption(text: _controller.value.caption.text),
+                  _ControlsOverlay(controller: _controller),
+                  VideoProgressIndicator(_controller, allowScrubbing: true),
+                ],
+              ),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}
+
+class _TestRemoteVideo extends StatefulWidget {
+  @override
+  State<_TestRemoteVideo> createState() => _TestRomoteVideoState();
+}
+
+class _TestRomoteVideoState extends State<_TestRemoteVideo> {
path: packages/video_player_avplay/example/lib/main.dart
user: login: seungsoo47
body: ```suggestion
class _TestRemoteVideoState extends State<_TestRemoteVideo> {
```
Please correct a typo.
created_at: 2025-04-03T08:40:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: seungsoo47
body: ```suggestion
  if (res != PLAYER_ERROR_NONE || state == PLAYER_STATE_NONE) {
```
Please change to PLAYER_ERROR_NONE for consistency.
created_at: 2025-04-04T00:47:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state != PLAYER_STATE_PAUSED && state != PLAYER_STATE_PLAYING) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: seungsoo47
body: Please use log levels properly.
created_at: 2025-04-04T01:09:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: seungsoo47
body: Please check the return value of player_get_state().
created_at: 2025-04-04T01:11:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state != PLAYER_STATE_PAUSED && state != PLAYER_STATE_PLAYING) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[MediaPlayer] Restore URL is not emptpy, close the existing "
+        "instance.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+  }
+
+  switch (state) {
+    case PLAYER_STATE_NONE:
+      return RestorePlayer(restore_message, resume_time);
+      break;
+    case PLAYER_STATE_IDLE:
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: seungsoo47
body: Is this possible because only paused state and playing state are filtered on line 887?
created_at: 2025-04-04T01:19:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state != PLAYER_STATE_PAUSED && state != PLAYER_STATE_PLAYING) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[MediaPlayer] Restore URL is not emptpy, close the existing "
+        "instance.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+  }
+
+  switch (state) {
+    case PLAYER_STATE_NONE:
+      return RestorePlayer(restore_message, resume_time);
+      break;
+    case PLAYER_STATE_IDLE:
+      LOG_ERROR("[MediaPlayer] Player is in invalid state.");
+      return false;
+    case PLAYER_STATE_PAUSED:
+      if (!player_) {
+        LOG_ERROR("[MediaPlayer] Player is not created.");
+        return false;
+      }
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: seungsoo47
body: I think these lines are unnecessary because the 'state' variable can't have 'PLAYER_STATE_PAUSED' when the 'player_' is nullptr.
Please double-check it.
created_at: 2025-04-04T01:27:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state != PLAYER_STATE_PAUSED && state != PLAYER_STATE_PLAYING) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[MediaPlayer] Restore URL is not emptpy, close the existing "
+        "instance.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+  }
+
+  switch (state) {
+    case PLAYER_STATE_NONE:
+      return RestorePlayer(restore_message, resume_time);
+      break;
+    case PLAYER_STATE_IDLE:
+      LOG_ERROR("[MediaPlayer] Player is in invalid state.");
+      return false;
+    case PLAYER_STATE_PAUSED:
+      if (!player_) {
+        LOG_ERROR("[MediaPlayer] Player is not created.");
+        return false;
+      }
+      if (pre_state_ == PLAYER_STATE_PLAYING) {
+        if (!Play()) {
+          LOG_ERROR("[MediaPlayer] Player play failed.");
+        }
+      }
+      return true;
+      break;
+    case PLAYER_STATE_PLAYING:
+      // might be the case that widget has called
+      // restore more than once, just ignore.
+      break;
+    default:
+      LOG_INFO(
+          "[MediaPlayer] Unhandled state, dont know how to process, just "
+          "return "
+          "false.");
+      return false;
+  }
+  return true;
+}
+
+bool MediaPlayer::RestorePlayer(const CreateMessage *restore_message,
+                                int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] RestorePlayer is called.");
+  player_state_e state = PLAYER_STATE_NONE;
+
+  if (restore_message->uri()) {
+    LOG_INFO("previous url: %s", url_.c_str());
+    LOG_INFO("new url: %s", restore_message->uri()->c_str());
+    url_ = *restore_message->uri();
+  }
+
+  LOG_INFO("previous playing time: %llu ms", pre_playing_time_);
+  LOG_INFO("new resume time: %lld ms", resume_time);
+  // resume_time < 0  ==> use previous playing time
+  // resume_time == 0 ==> play from beginning
+  // resume_time > 0  ==> play from resume_time(Third-party settings)
+  if (resume_time >= 0) pre_playing_time_ = static_cast<uint64_t>(resume_time);
+
+  if (player_) {
+    player_get_state(player_, &state);
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: seungsoo47
body: Please check the return value of player_get_state().
created_at: 2025-04-04T01:32:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: seungsoo47
body: This assert statement will only run in debug mode.
Please add another statement to run in release mode to check if the 'ret' variable is true or not.
And please check the other assert() statements, too.
created_at: 2025-04-04T01:54:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: seungsoo47
body: How about changing the variable name from 'res' to 'power_state' to make it clearer?
created_at: 2025-04-04T02:00:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: seungsoo47
body: How about changing the variable name from 'state' to 'player_state' to make it clearer?
created_at: 2025-04-04T02:05:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
+  if (state <= plusplayer::State::kTrackSourceReady) {
+    ret = Close(player_);
+    assert(ret == true && "[PlusPlayer] Player close fail");
+    LOG_INFO("[PlusPlayer] Player is in invalid state [%d].", state);
+    return true;
+  } else if (state != plusplayer::State::kPaused) {
+    LOG_INFO("[PlusPlayer] Player calling pause from suspend.");
+    if (::Suspend(player_) == false) {
+      LOG_ERROR(
+          "[PlusPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      ret = StopAndClose();
+      assert(ret == true &&
+             "[PlusPlayer] Suspend error, palyer StopAndClose fail");
+      return false;
+    }
+    SendIsPlayingState(false);
+  }
+  return true;
+}
+
+bool PlusPlayer::Restore(const CreateMessage *restore_message,
+                         int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] Restore is called.");
+
+  plusplayer::State state;
+  if (player_) {
+    state = GetState(player_);
+    if (state != plusplayer::State::kNone &&
+        state != plusplayer::State::kPaused &&
+        state != plusplayer::State::kPlaying) {
+      LOG_ERROR("[PlusPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (!memento_) {
+    LOG_ERROR(
+        "[PlusPlayer] No memento to restore. Player is in invalid state [%d]",
+        state);
+    return false;
+  }
+
+  bool ret = false;
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[PlusPlayer] Restore URL is not emptpy, close the existing instance.");
+    ret = StopAndClose();
+    assert(ret == true &&
+           "[PlusPlayer] RestoreUrl is not empty, player StopAndClose fail");
+  }
+
+  state = GetState(player_);
+  switch (state) {
+    case plusplayer::State::kNone:
+      ret = RestorePlayer(restore_message, resume_time);
+      return ret;
+      break;
+    case plusplayer::State::kIdle:
+    case plusplayer::State::kTypeFinderReady:
+    case plusplayer::State::kTrackSourceReady:
+      LOG_ERROR("[PlusPlayer] Player is in invalid state.");
+      return false;
+    case plusplayer::State::kReady:
+    case plusplayer::State::kPaused:
+      if (!player_) {
+        LOG_ERROR("[PlusPlayer] Player is not created.");
+        return false;
+      }
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: seungsoo47
body: I think it would be better to move these lines to the beginning of this function.
created_at: 2025-04-04T02:18:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state != PLAYER_STATE_PAUSED && state != PLAYER_STATE_PLAYING) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: gin7773
body: change to "LOG_INFO"
created_at: 2025-04-07T01:35:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state != PLAYER_STATE_PAUSED && state != PLAYER_STATE_PLAYING) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[MediaPlayer] Restore URL is not emptpy, close the existing "
+        "instance.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+  }
+
+  switch (state) {
+    case PLAYER_STATE_NONE:
+      return RestorePlayer(restore_message, resume_time);
+      break;
+    case PLAYER_STATE_IDLE:
+      LOG_ERROR("[MediaPlayer] Player is in invalid state.");
+      return false;
+    case PLAYER_STATE_PAUSED:
+      if (!player_) {
+        LOG_ERROR("[MediaPlayer] Player is not created.");
+        return false;
+      }
+      if (pre_state_ == PLAYER_STATE_PLAYING) {
+        if (!Play()) {
+          LOG_ERROR("[MediaPlayer] Player play failed.");
+        }
+      }
+      return true;
+      break;
+    case PLAYER_STATE_PLAYING:
+      // might be the case that widget has called
+      // restore more than once, just ignore.
+      break;
+    default:
+      LOG_INFO(
+          "[MediaPlayer] Unhandled state, dont know how to process, just "
+          "return "
+          "false.");
+      return false;
+  }
+  return true;
+}
+
+bool MediaPlayer::RestorePlayer(const CreateMessage *restore_message,
+                                int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] RestorePlayer is called.");
+  player_state_e state = PLAYER_STATE_NONE;
+
+  if (restore_message->uri()) {
+    LOG_INFO("previous url: %s", url_.c_str());
+    LOG_INFO("new url: %s", restore_message->uri()->c_str());
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: JSUYA
body: Please keep `[MediaPlayer]`.
created_at: 2025-04-07T02:18:39Z
author_association: MEMBER
diff_hunk: @@ -467,57 +467,60 @@ class _VideoPlayerAvplayApiCodec extends StandardMessageCodec {
     } else if (value is CreateMessage) {
       buffer.putUint8(129);
       writeValue(buffer, value.encode());
-    } else if (value is DashPropertyMapMessage) {
+    } else if (value is CreateMessage) {
path: packages/video_player_avplay/lib/src/messages.g.dart
user: login: gin7773
body: In 'messges.dart', the type of the parameter msg in the restore API is 'CreateMessage?' which can be empty. So the generated code handles 'CreateMessage' twice (NULL and NonNull).
created_at: 2025-04-07T02:20:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state != PLAYER_STATE_PAUSED && state != PLAYER_STATE_PLAYING) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[MediaPlayer] Restore URL is not emptpy, close the existing "
+        "instance.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+  }
+
+  switch (state) {
+    case PLAYER_STATE_NONE:
+      return RestorePlayer(restore_message, resume_time);
+      break;
+    case PLAYER_STATE_IDLE:
+      LOG_ERROR("[MediaPlayer] Player is in invalid state.");
+      return false;
+    case PLAYER_STATE_PAUSED:
+      if (!player_) {
+        LOG_ERROR("[MediaPlayer] Player is not created.");
+        return false;
+      }
+      if (pre_state_ == PLAYER_STATE_PLAYING) {
+        if (!Play()) {
+          LOG_ERROR("[MediaPlayer] Player play failed.");
+        }
+      }
+      return true;
+      break;
+    case PLAYER_STATE_PLAYING:
+      // might be the case that widget has called
+      // restore more than once, just ignore.
+      break;
+    default:
+      LOG_INFO(
+          "[MediaPlayer] Unhandled state, dont know how to process, just "
+          "return "
+          "false.");
+      return false;
+  }
+  return true;
+}
+
+bool MediaPlayer::RestorePlayer(const CreateMessage *restore_message,
+                                int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] RestorePlayer is called.");
+  player_state_e state = PLAYER_STATE_NONE;
+
+  if (restore_message->uri()) {
+    LOG_INFO("previous url: %s", url_.c_str());
+    LOG_INFO("new url: %s", restore_message->uri()->c_str());
+    url_ = *restore_message->uri();
+  }
+
+  LOG_INFO("previous playing time: %llu ms", pre_playing_time_);
+  LOG_INFO("new resume time: %lld ms", resume_time);
+  // resume_time < 0  ==> use previous playing time
+  // resume_time == 0 ==> play from beginning
+  // resume_time > 0  ==> play from resume_time(Third-party settings)
+  if (resume_time >= 0) pre_playing_time_ = static_cast<uint64_t>(resume_time);
+
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state >= PLAYER_STATE_READY) {
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: JSUYA
body: RestorePlayer() is called when state is PLAYER_STATE_NONE, so this state check is redundant.
created_at: 2025-04-07T03:19:52Z
author_association: MEMBER
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state != PLAYER_STATE_PAUSED && state != PLAYER_STATE_PLAYING) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[MediaPlayer] Restore URL is not emptpy, close the existing "
+        "instance.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+  }
+
+  switch (state) {
+    case PLAYER_STATE_NONE:
+      return RestorePlayer(restore_message, resume_time);
+      break;
+    case PLAYER_STATE_IDLE:
+      LOG_ERROR("[MediaPlayer] Player is in invalid state.");
+      return false;
+    case PLAYER_STATE_PAUSED:
+      if (!player_) {
+        LOG_ERROR("[MediaPlayer] Player is not created.");
+        return false;
+      }
+      if (pre_state_ == PLAYER_STATE_PLAYING) {
+        if (!Play()) {
+          LOG_ERROR("[MediaPlayer] Player play failed.");
+        }
+      }
+      return true;
+      break;
+    case PLAYER_STATE_PLAYING:
+      // might be the case that widget has called
+      // restore more than once, just ignore.
+      break;
+    default:
+      LOG_INFO(
+          "[MediaPlayer] Unhandled state, dont know how to process, just "
+          "return "
+          "false.");
+      return false;
+  }
+  return true;
+}
+
+bool MediaPlayer::RestorePlayer(const CreateMessage *restore_message,
+                                int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] RestorePlayer is called.");
+  player_state_e state = PLAYER_STATE_NONE;
+
+  if (restore_message->uri()) {
+    LOG_INFO("previous url: %s", url_.c_str());
+    LOG_INFO("new url: %s", restore_message->uri()->c_str());
+    url_ = *restore_message->uri();
+  }
+
+  LOG_INFO("previous playing time: %llu ms", pre_playing_time_);
+  LOG_INFO("new resume time: %lld ms", resume_time);
+  // resume_time < 0  ==> use previous playing time
+  // resume_time == 0 ==> play from beginning
+  // resume_time > 0  ==> play from resume_time(Third-party settings)
+  if (resume_time >= 0) pre_playing_time_ = static_cast<uint64_t>(resume_time);
+
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state >= PLAYER_STATE_READY) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state.");
+      return false;
+    }
+  }
+
+  restore_completed_ = true;
+  if (Create(url_, *restore_message) < 0) {
+    LOG_ERROR("[MediaPlayer] Fail to create player.");
+    return false;
+  }
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: JSUYA
body: restore_completed_ seems to be a flag to check if the current creation was called from restore or a normal creation.
In that case, restored_ seems more appropriate. (Or I think there might be another appropriate name)

And how should pre_playing_time_ be managed if create fails?
created_at: 2025-04-07T03:28:34Z
author_association: MEMBER
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: JSUYA
body: please move this variable to the line before using it (816 line).
(785 line also).

Please check https://google.github.io/styleguide/cppguide.html#Local_Variables

created_at: 2025-04-07T03:42:22Z
author_association: MEMBER
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: JSUYA
body: Instead of assert() , please return false with an appropriate error message.
created_at: 2025-04-07T03:54:29Z
author_association: MEMBER
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: JSUYA
body: please use %llu instead of PRIu64.
And, In line 804, you can write `ms, ` and the string following it on one line, not separated.
created_at: 2025-04-07T03:58:25Z
author_association: MEMBER
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
+  if (state <= plusplayer::State::kTrackSourceReady) {
+    ret = Close(player_);
+    assert(ret == true && "[PlusPlayer] Player close fail");
+    LOG_INFO("[PlusPlayer] Player is in invalid state [%d].", state);
+    return true;
+  } else if (state != plusplayer::State::kPaused) {
+    LOG_INFO("[PlusPlayer] Player calling pause from suspend.");
+    if (::Suspend(player_) == false) {
+      LOG_ERROR(
+          "[PlusPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      ret = StopAndClose();
+      assert(ret == true &&
+             "[PlusPlayer] Suspend error, palyer StopAndClose fail");
+      return false;
+    }
+    SendIsPlayingState(false);
+  }
+  return true;
+}
+
+bool PlusPlayer::Restore(const CreateMessage *restore_message,
+                         int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] Restore is called.");
+
+  plusplayer::State state;
+  if (player_) {
+    state = GetState(player_);
+    if (state != plusplayer::State::kNone &&
+        state != plusplayer::State::kPaused &&
+        state != plusplayer::State::kPlaying) {
+      LOG_ERROR("[PlusPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (!memento_) {
+    LOG_ERROR(
+        "[PlusPlayer] No memento to restore. Player is in invalid state [%d]",
+        state);
+    return false;
+  }
+
+  bool ret = false;
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[PlusPlayer] Restore URL is not emptpy, close the existing instance.");
+    ret = StopAndClose();
+    assert(ret == true &&
+           "[PlusPlayer] RestoreUrl is not empty, player StopAndClose fail");
+  }
+
+  state = GetState(player_);
+  switch (state) {
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: JSUYA
body: In this line, the state can only kNone, kPaused, kPlaying, and some state that after ::Close()(ex: kNone). If the state is determined one state after ::Close, there is no need to call GetState() redundantly.
created_at: 2025-04-07T04:21:36Z
author_association: MEMBER
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: JSUYA
body: Is `player_` valid after ::Close() is called? If not, you may assign to null.
created_at: 2025-04-07T04:23:52Z
author_association: MEMBER
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
+  if (state <= plusplayer::State::kTrackSourceReady) {
+    ret = Close(player_);
+    assert(ret == true && "[PlusPlayer] Player close fail");
+    LOG_INFO("[PlusPlayer] Player is in invalid state [%d].", state);
+    return true;
+  } else if (state != plusplayer::State::kPaused) {
+    LOG_INFO("[PlusPlayer] Player calling pause from suspend.");
+    if (::Suspend(player_) == false) {
+      LOG_ERROR(
+          "[PlusPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      ret = StopAndClose();
+      assert(ret == true &&
+             "[PlusPlayer] Suspend error, palyer StopAndClose fail");
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: JSUYA
body: Typo `palyer`-> `player`
created_at: 2025-04-07T04:24:37Z
author_association: MEMBER
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
+  if (state <= plusplayer::State::kTrackSourceReady) {
+    ret = Close(player_);
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: JSUYA
body: ::Close(player_)
created_at: 2025-04-07T04:52:34Z
author_association: MEMBER
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
+  if (state <= plusplayer::State::kTrackSourceReady) {
+    ret = Close(player_);
+    assert(ret == true && "[PlusPlayer] Player close fail");
+    LOG_INFO("[PlusPlayer] Player is in invalid state [%d].", state);
+    return true;
+  } else if (state != plusplayer::State::kPaused) {
+    LOG_INFO("[PlusPlayer] Player calling pause from suspend.");
+    if (::Suspend(player_) == false) {
+      LOG_ERROR(
+          "[PlusPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      ret = StopAndClose();
+      assert(ret == true &&
+             "[PlusPlayer] Suspend error, palyer StopAndClose fail");
+      return false;
+    }
+    SendIsPlayingState(false);
+  }
+  return true;
+}
+
+bool PlusPlayer::Restore(const CreateMessage *restore_message,
+                         int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] Restore is called.");
+
+  plusplayer::State state;
+  if (player_) {
+    state = GetState(player_);
+    if (state != plusplayer::State::kNone &&
+        state != plusplayer::State::kPaused &&
+        state != plusplayer::State::kPlaying) {
+      LOG_ERROR("[PlusPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (!memento_) {
+    LOG_ERROR(
+        "[PlusPlayer] No memento to restore. Player is in invalid state [%d]",
+        state);
+    return false;
+  }
+
+  bool ret = false;
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: JSUYA
body: this declare need to move 878 line.
created_at: 2025-04-07T04:54:28Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,29 @@
+// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_PLUGIN_POWER_STATE_PROXY_H_
+#define FLUTTER_PLUGIN_POWER_STATE_PROXY_H_
+
+#include <player.h>
path: packages/video_player_avplay/tizen/src/power_state_proxy.h
user: login: JSUYA
body: Remove unused header
created_at: 2025-04-07T04:57:36Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,29 @@
+// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_PLUGIN_POWER_STATE_PROXY_H_
+#define FLUTTER_PLUGIN_POWER_STATE_PROXY_H_
+
+#include <player.h>
+
+typedef enum {
+  POWER_STATE_NORMAL,          /**< Normal state */
+  POWER_STATE_PICTUREOFF,      /**< Picture off state */
+  POWER_STATE_STANDBY,         /**< Background Standby state */
+  POWER_STATE_OFF,             /**< Cold Standby state */
+  POWER_STATE_SUSPEND_STANDBY, /**< Suspend Standby state */
+  POWER_STATE_ERROR            /**< Error state */
+} power_state_e;
+
+class PowerStateProxy {
path: packages/video_player_avplay/tizen/src/power_state_proxy.h
user: login: JSUYA
body: DeviceProxy seems more appropriate since this class is a proxy for libdeviced.

created_at: 2025-04-07T05:05:07Z
author_association: MEMBER
diff_hunk: @@ -348,6 +353,29 @@ ErrorOr<bool> VideoPlayerTizenPlugin::SetDisplayMode(
   return player->SetDisplayMode(msg.display_mode());
 }
 
+std::optional<FlutterError> VideoPlayerTizenPlugin::Suspend(int64_t player_id) {
+  VideoPlayer *player = FindPlayerById(player_id);
+  if (!player) {
+    return FlutterError("Invalid argument", "Player not found");
+  }
+  if (player->Suspend() == false) {
path: packages/video_player_avplay/tizen/src/video_player_tizen_plugin.cc
user: login: JSUYA
body: ```suggestion
  if (!player->Suspend()) {
```

Other codes too
created_at: 2025-04-07T05:19:43Z
author_association: MEMBER
diff_hunk: @@ -22,6 +22,12 @@ export 'src/closed_caption_file.dart';
 export 'src/drm_configs.dart';
 export 'src/tracks.dart';
 
+/// This will be used to set the ResumeTime when player restore.
+typedef ResumeTimeCallback = int Function();
+
+/// This will be used to set the RecreateMessage when player restore.
+typedef RecreateMessageCallback = DataSource Function();
path: packages/video_player_avplay/lib/video_player.dart
user: login: JSUYA
body: It would be better to make the names a bit clearer. Both callbacks are used for setRestoreData.

RestoreTimeCallback seems more appropriate because TimeCallback is the time to decide whether to restore or not.

Also, RestoreDataCallback seems more appropriate because the purpose of DataSource Function() is to pass the data to be used when restoring.
created_at: 2025-04-07T05:27:41Z
author_association: MEMBER
diff_hunk: @@ -748,3 +752,142 @@ class _GetTextTrackButton extends StatelessWidget {
     );
   }
 }
+
+class _LiveRemoteVideo extends StatefulWidget {
+  @override
+  State<_LiveRemoteVideo> createState() => _LiveRomoteVideoState();
+}
+
+class _LiveRomoteVideoState extends State<_LiveRemoteVideo> {
+  late VideoPlayerController _controller;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = VideoPlayerController.network(
+      'https://hlive.ktv.go.kr/live/klive_h.stream/playlist.m3u8',
+    );
+
+    _controller.addListener(() {
+      if (_controller.value.hasError) {
+        print(_controller.value.errorDescription);
+      }
+      setState(() {});
+    });
+    _controller.setLooping(true);
+    _controller.initialize().then((_) => setState(() {}));
+    _controller.play();
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return SingleChildScrollView(
+      child: Column(
+        children: <Widget>[
+          Container(padding: const EdgeInsets.only(top: 20.0)),
+          const Text('Playing Live TV'),
+          Container(
+            padding: const EdgeInsets.all(20),
+            child: AspectRatio(
+              aspectRatio: _controller.value.aspectRatio,
+              child: Stack(
+                alignment: Alignment.bottomCenter,
+                children: <Widget>[
+                  VideoPlayer(_controller),
+                  ClosedCaption(text: _controller.value.caption.text),
+                  _ControlsOverlay(controller: _controller),
+                  VideoProgressIndicator(_controller, allowScrubbing: true),
+                ],
+              ),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}
+
+class _TestRemoteVideo extends StatefulWidget {
+  @override
+  State<_TestRemoteVideo> createState() => _TestRomoteVideoState();
+}
+
+class _TestRomoteVideoState extends State<_TestRemoteVideo> {
+  late VideoPlayerController _controller;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = VideoPlayerController.network(
+      'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8',
+    );
+
+    _controller.addListener(() {
+      if (_controller.value.hasError) {
+        print(_controller.value.errorDescription);
+      }
+      setState(() {});
+    });
+    _controller.setLooping(true);
+    _controller.initialize().then((_) => setState(() {}));
+    _controller.play();
+    _controller.setRestoreData(
+      recreateMessage: restoreData,
+      resumeTime: restoreTime,
+    );
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+
+  DataSource restoreData() {
+    final DataSource dataSource = DataSource(
+      sourceType: DataSourceType.network,
+      uri: 'https://media.w3.org/2010/05/bunny/trailer.mp4',
+    );
+    return dataSource;
+  }
+
+  int restoreTime() {
+    /// if resumeTime >= 0 , it will restore from resumeTime
+    /// if resumeTime is not set or <0, it will restore from the time when suspend is called
+    const int resumeTime = 0;
path: packages/video_player_avplay/example/lib/main.dart
user: login: JSUYA
body: Is there a scenario where restore time is needed?
restore is called after suspend is called, and the play time is based on the previous play time.
If user want to go to a different point in time (the beginning or a specific time), they will probably need to call seekTo().

created_at: 2025-04-07T05:37:07Z
author_association: MEMBER
diff_hunk: @@ -865,6 +885,48 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
+  void setRestoreData({
+    RecreateMessageCallback? recreateMessage,
+    ResumeTimeCallback? resumeTime,
+  }) {
+    _onRestoreData = recreateMessage;
path: packages/video_player_avplay/lib/video_player.dart
user: login: gin7773
body: Change to '_onRestoreDataSource = restoreDataSource;'
created_at: 2025-04-07T05:38:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: gin7773
body: Done
created_at: 2025-04-07T05:38:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state != PLAYER_STATE_PAUSED && state != PLAYER_STATE_PLAYING) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[MediaPlayer] Restore URL is not emptpy, close the existing "
+        "instance.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+  }
+
+  switch (state) {
+    case PLAYER_STATE_NONE:
+      return RestorePlayer(restore_message, resume_time);
+      break;
+    case PLAYER_STATE_IDLE:
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: gin7773
body: Only deleted 'case PLAYER_STATE_IDLE'. Because when player is null , state == PLAYER_STATE_NONE, 'case PLAYER_STATE_NONE' should be considered.
created_at: 2025-04-07T05:43:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state != PLAYER_STATE_PAUSED && state != PLAYER_STATE_PLAYING) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[MediaPlayer] Restore URL is not emptpy, close the existing "
+        "instance.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+  }
+
+  switch (state) {
+    case PLAYER_STATE_NONE:
+      return RestorePlayer(restore_message, resume_time);
+      break;
+    case PLAYER_STATE_IDLE:
+      LOG_ERROR("[MediaPlayer] Player is in invalid state.");
+      return false;
+    case PLAYER_STATE_PAUSED:
+      if (!player_) {
+        LOG_ERROR("[MediaPlayer] Player is not created.");
+        return false;
+      }
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: gin7773
body: You're right. Already deleted, thanks.
created_at: 2025-04-07T05:44:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state != PLAYER_STATE_PAUSED && state != PLAYER_STATE_PLAYING) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[MediaPlayer] Restore URL is not emptpy, close the existing "
+        "instance.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+  }
+
+  switch (state) {
+    case PLAYER_STATE_NONE:
+      return RestorePlayer(restore_message, resume_time);
+      break;
+    case PLAYER_STATE_IDLE:
+      LOG_ERROR("[MediaPlayer] Player is in invalid state.");
+      return false;
+    case PLAYER_STATE_PAUSED:
+      if (!player_) {
+        LOG_ERROR("[MediaPlayer] Player is not created.");
+        return false;
+      }
+      if (pre_state_ == PLAYER_STATE_PLAYING) {
+        if (!Play()) {
+          LOG_ERROR("[MediaPlayer] Player play failed.");
+        }
+      }
+      return true;
+      break;
+    case PLAYER_STATE_PLAYING:
+      // might be the case that widget has called
+      // restore more than once, just ignore.
+      break;
+    default:
+      LOG_INFO(
+          "[MediaPlayer] Unhandled state, dont know how to process, just "
+          "return "
+          "false.");
+      return false;
+  }
+  return true;
+}
+
+bool MediaPlayer::RestorePlayer(const CreateMessage *restore_message,
+                                int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] RestorePlayer is called.");
+  player_state_e state = PLAYER_STATE_NONE;
+
+  if (restore_message->uri()) {
+    LOG_INFO("previous url: %s", url_.c_str());
+    LOG_INFO("new url: %s", restore_message->uri()->c_str());
+    url_ = *restore_message->uri();
+  }
+
+  LOG_INFO("previous playing time: %llu ms", pre_playing_time_);
+  LOG_INFO("new resume time: %lld ms", resume_time);
+  // resume_time < 0  ==> use previous playing time
+  // resume_time == 0 ==> play from beginning
+  // resume_time > 0  ==> play from resume_time(Third-party settings)
+  if (resume_time >= 0) pre_playing_time_ = static_cast<uint64_t>(resume_time);
+
+  if (player_) {
+    player_get_state(player_, &state);
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: gin7773
body: Done.
created_at: 2025-04-07T05:44:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: gin7773
body: Done.
created_at: 2025-04-07T05:44:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: gin7773
body: Done.
created_at: 2025-04-07T05:45:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: gin7773
body: Done.
created_at: 2025-04-07T05:45:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
+  if (state <= plusplayer::State::kTrackSourceReady) {
+    ret = Close(player_);
+    assert(ret == true && "[PlusPlayer] Player close fail");
+    LOG_INFO("[PlusPlayer] Player is in invalid state [%d].", state);
+    return true;
+  } else if (state != plusplayer::State::kPaused) {
+    LOG_INFO("[PlusPlayer] Player calling pause from suspend.");
+    if (::Suspend(player_) == false) {
+      LOG_ERROR(
+          "[PlusPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      ret = StopAndClose();
+      assert(ret == true &&
+             "[PlusPlayer] Suspend error, palyer StopAndClose fail");
+      return false;
+    }
+    SendIsPlayingState(false);
+  }
+  return true;
+}
+
+bool PlusPlayer::Restore(const CreateMessage *restore_message,
+                         int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] Restore is called.");
+
+  plusplayer::State state;
+  if (player_) {
+    state = GetState(player_);
+    if (state != plusplayer::State::kNone &&
+        state != plusplayer::State::kPaused &&
+        state != plusplayer::State::kPlaying) {
+      LOG_ERROR("[PlusPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (!memento_) {
+    LOG_ERROR(
+        "[PlusPlayer] No memento to restore. Player is in invalid state [%d]",
+        state);
+    return false;
+  }
+
+  bool ret = false;
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[PlusPlayer] Restore URL is not emptpy, close the existing instance.");
+    ret = StopAndClose();
+    assert(ret == true &&
+           "[PlusPlayer] RestoreUrl is not empty, player StopAndClose fail");
+  }
+
+  state = GetState(player_);
+  switch (state) {
+    case plusplayer::State::kNone:
+      ret = RestorePlayer(restore_message, resume_time);
+      return ret;
+      break;
+    case plusplayer::State::kIdle:
+    case plusplayer::State::kTypeFinderReady:
+    case plusplayer::State::kTrackSourceReady:
+      LOG_ERROR("[PlusPlayer] Player is in invalid state.");
+      return false;
+    case plusplayer::State::kReady:
+    case plusplayer::State::kPaused:
+      if (!player_) {
+        LOG_ERROR("[PlusPlayer] Player is not created.");
+        return false;
+      }
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: gin7773
body: You're right. Already moved, thanks.
created_at: 2025-04-07T05:46:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: gin7773
body: Done.
created_at: 2025-04-07T05:54:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -865,6 +885,48 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
+  void setRestoreData({
+    RecreateMessageCallback? recreateMessage,
+    ResumeTimeCallback? resumeTime,
+  }) {
+    _onRestoreData = recreateMessage;
+    _onRestoreTime = resumeTime;
+  }
+
+  ///Pauses the player  when the application is sent to the background.
+  ///Saves the current statistics for the ongoing playback session.
+  Future<void> suspend() async {
+    if (_isDisposedOrNotInitialized) {
+      return;
+    }
+
+    await _videoPlayerPlatform.suspend(_playerId);
+    _durationTimer?.cancel();
+    _timer?.cancel();
+    _timer = null;
+  }
+
+  ///Restores the player state when the application is resumed.
+  ///For live streaming or DRM-encrypted content playback, you must check whether the
+  ///streaming URL has changed or the DRM session or license has expired, and specify
+  ///the new URL and DRM information as needed.
path: packages/video_player_avplay/lib/video_player.dart
user: login: gin7773
body: Done.
created_at: 2025-04-07T06:49:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -748,3 +752,142 @@ class _GetTextTrackButton extends StatelessWidget {
     );
   }
 }
+
+class _LiveRemoteVideo extends StatefulWidget {
+  @override
+  State<_LiveRemoteVideo> createState() => _LiveRomoteVideoState();
+}
+
+class _LiveRomoteVideoState extends State<_LiveRemoteVideo> {
+  late VideoPlayerController _controller;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = VideoPlayerController.network(
+      'https://hlive.ktv.go.kr/live/klive_h.stream/playlist.m3u8',
+    );
+
+    _controller.addListener(() {
+      if (_controller.value.hasError) {
+        print(_controller.value.errorDescription);
+      }
+      setState(() {});
+    });
+    _controller.setLooping(true);
+    _controller.initialize().then((_) => setState(() {}));
+    _controller.play();
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return SingleChildScrollView(
+      child: Column(
+        children: <Widget>[
+          Container(padding: const EdgeInsets.only(top: 20.0)),
+          const Text('Playing Live TV'),
+          Container(
+            padding: const EdgeInsets.all(20),
+            child: AspectRatio(
+              aspectRatio: _controller.value.aspectRatio,
+              child: Stack(
+                alignment: Alignment.bottomCenter,
+                children: <Widget>[
+                  VideoPlayer(_controller),
+                  ClosedCaption(text: _controller.value.caption.text),
+                  _ControlsOverlay(controller: _controller),
+                  VideoProgressIndicator(_controller, allowScrubbing: true),
+                ],
+              ),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}
+
+class _TestRemoteVideo extends StatefulWidget {
+  @override
+  State<_TestRemoteVideo> createState() => _TestRomoteVideoState();
path: packages/video_player_avplay/example/lib/main.dart
user: login: gin7773
body: Done.
created_at: 2025-04-07T06:50:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: gin7773
body: Done.
created_at: 2025-04-07T06:53:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -865,6 +885,48 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
+  void setRestoreData({
+    RecreateMessageCallback? recreateMessage,
+    ResumeTimeCallback? resumeTime,
+  }) {
+    _onRestoreData = recreateMessage;
+    _onRestoreTime = resumeTime;
+  }
+
+  ///Pauses the player  when the application is sent to the background.
+  ///Saves the current statistics for the ongoing playback session.
path: packages/video_player_avplay/lib/video_player.dart
user: login: gin7773
body: Done.
created_at: 2025-04-07T06:53:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state != PLAYER_STATE_PAUSED && state != PLAYER_STATE_PLAYING) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[MediaPlayer] Restore URL is not emptpy, close the existing "
+        "instance.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+  }
+
+  switch (state) {
+    case PLAYER_STATE_NONE:
+      return RestorePlayer(restore_message, resume_time);
+      break;
+    case PLAYER_STATE_IDLE:
+      LOG_ERROR("[MediaPlayer] Player is in invalid state.");
+      return false;
+    case PLAYER_STATE_PAUSED:
+      if (!player_) {
+        LOG_ERROR("[MediaPlayer] Player is not created.");
+        return false;
+      }
+      if (pre_state_ == PLAYER_STATE_PLAYING) {
+        if (!Play()) {
+          LOG_ERROR("[MediaPlayer] Player play failed.");
+        }
+      }
+      return true;
+      break;
+    case PLAYER_STATE_PLAYING:
+      // might be the case that widget has called
+      // restore more than once, just ignore.
+      break;
+    default:
+      LOG_INFO(
+          "[MediaPlayer] Unhandled state, dont know how to process, just "
+          "return "
+          "false.");
+      return false;
+  }
+  return true;
+}
+
+bool MediaPlayer::RestorePlayer(const CreateMessage *restore_message,
+                                int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] RestorePlayer is called.");
+  player_state_e state = PLAYER_STATE_NONE;
+
+  if (restore_message->uri()) {
+    LOG_INFO("previous url: %s", url_.c_str());
+    LOG_INFO("new url: %s", restore_message->uri()->c_str());
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: gin7773
body: Done.
created_at: 2025-04-07T07:53:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state != PLAYER_STATE_PAUSED && state != PLAYER_STATE_PLAYING) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[MediaPlayer] Restore URL is not emptpy, close the existing "
+        "instance.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+  }
+
+  switch (state) {
+    case PLAYER_STATE_NONE:
+      return RestorePlayer(restore_message, resume_time);
+      break;
+    case PLAYER_STATE_IDLE:
+      LOG_ERROR("[MediaPlayer] Player is in invalid state.");
+      return false;
+    case PLAYER_STATE_PAUSED:
+      if (!player_) {
+        LOG_ERROR("[MediaPlayer] Player is not created.");
+        return false;
+      }
+      if (pre_state_ == PLAYER_STATE_PLAYING) {
+        if (!Play()) {
+          LOG_ERROR("[MediaPlayer] Player play failed.");
+        }
+      }
+      return true;
+      break;
+    case PLAYER_STATE_PLAYING:
+      // might be the case that widget has called
+      // restore more than once, just ignore.
+      break;
+    default:
+      LOG_INFO(
+          "[MediaPlayer] Unhandled state, dont know how to process, just "
+          "return "
+          "false.");
+      return false;
+  }
+  return true;
+}
+
+bool MediaPlayer::RestorePlayer(const CreateMessage *restore_message,
+                                int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] RestorePlayer is called.");
+  player_state_e state = PLAYER_STATE_NONE;
+
+  if (restore_message->uri()) {
+    LOG_INFO("previous url: %s", url_.c_str());
+    LOG_INFO("new url: %s", restore_message->uri()->c_str());
+    url_ = *restore_message->uri();
+  }
+
+  LOG_INFO("previous playing time: %llu ms", pre_playing_time_);
+  LOG_INFO("new resume time: %lld ms", resume_time);
+  // resume_time < 0  ==> use previous playing time
+  // resume_time == 0 ==> play from beginning
+  // resume_time > 0  ==> play from resume_time(Third-party settings)
+  if (resume_time >= 0) pre_playing_time_ = static_cast<uint64_t>(resume_time);
+
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state >= PLAYER_STATE_READY) {
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: gin7773
body: Deleted.
created_at: 2025-04-07T07:54:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -760,3 +778,192 @@ bool MediaPlayer::SetDisplayMode(int64_t display_mode) {
   }
   return true;
 }
+
+bool MediaPlayer::StopAndDestroy() {
+  LOG_INFO("[MediaPlayer] StopAndDestroy is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  player_state_e state = PLAYER_STATE_NONE;
+  int ret = player_get_state(player_, &state);
+  if (ret != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] player_get_state failed: %s.",
+              get_error_message(ret));
+    return false;
+  }
+  if (state == PLAYER_STATE_NONE || state == PLAYER_STATE_IDLE) {
+    LOG_INFO("[MediaPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (player_stop(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
+    return false;
+  }
+
+  if (player_destroy(player_) != PLAYER_ERROR_NONE) {
+    LOG_ERROR("[MediaPlayer] Player fail to destroy.");
+    return false;
+  }
+  player_ = nullptr;
+
+  return true;
+}
+
+bool MediaPlayer::Suspend() {
+  LOG_INFO("[MediaPlayer] Suspend is called.");
+  if (!player_) {
+    LOG_ERROR("[MediaPlayer] Player not created.");
+    return false;
+  }
+
+  player_state_e state = PLAYER_STATE_NONE;
+  int res = player_get_state(player_, &state);
+  if (res != 0 || state == PLAYER_STATE_NONE) {
+    LOG_ERROR("[MediaPlayer] Player get state failed or in invalid state [%d].",
+              state);
+    return false;
+  }
+
+  pre_state_ = state;
+  pre_playing_time_ = GetPosition();
+  if (pre_playing_time_ < 0) {
+    LOG_ERROR("[MediaPlayer] Get position failed.");
+    return false;
+  }
+  LOG_INFO("[MediaPlayer] Saved current state: %d, playing time: %" PRId64 "ms",
+           pre_state_, pre_playing_time_);
+
+  res = power_state_proxy_->device_power_get_state();
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[MediaPlayer] Power state is standby.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[MediaPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  if (state == PLAYER_STATE_IDLE) {
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+    LOG_INFO("[MediaPlayer] Player called in IDLE state, so stop the player.");
+  } else if (state != PLAYER_STATE_PAUSED) {
+    LOG_INFO("[MediaPlayer] Player calling pause from suspend.");
+    if (!Pause()) {
+      LOG_ERROR(
+          "[MediaPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      if (!StopAndDestroy()) {
+        LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayer::Restore(const CreateMessage *restore_message,
+                          int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] Restore is called.");
+
+  player_state_e state = PLAYER_STATE_NONE;
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state != PLAYER_STATE_PAUSED && state != PLAYER_STATE_PLAYING) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[MediaPlayer] Restore URL is not emptpy, close the existing "
+        "instance.");
+    if (!StopAndDestroy()) {
+      LOG_ERROR("[MediaPlayer] Player StopAndDestroy fail.");
+      return false;
+    }
+  }
+
+  switch (state) {
+    case PLAYER_STATE_NONE:
+      return RestorePlayer(restore_message, resume_time);
+      break;
+    case PLAYER_STATE_IDLE:
+      LOG_ERROR("[MediaPlayer] Player is in invalid state.");
+      return false;
+    case PLAYER_STATE_PAUSED:
+      if (!player_) {
+        LOG_ERROR("[MediaPlayer] Player is not created.");
+        return false;
+      }
+      if (pre_state_ == PLAYER_STATE_PLAYING) {
+        if (!Play()) {
+          LOG_ERROR("[MediaPlayer] Player play failed.");
+        }
+      }
+      return true;
+      break;
+    case PLAYER_STATE_PLAYING:
+      // might be the case that widget has called
+      // restore more than once, just ignore.
+      break;
+    default:
+      LOG_INFO(
+          "[MediaPlayer] Unhandled state, dont know how to process, just "
+          "return "
+          "false.");
+      return false;
+  }
+  return true;
+}
+
+bool MediaPlayer::RestorePlayer(const CreateMessage *restore_message,
+                                int64_t resume_time) {
+  LOG_INFO("[MediaPlayer] RestorePlayer is called.");
+  player_state_e state = PLAYER_STATE_NONE;
+
+  if (restore_message->uri()) {
+    LOG_INFO("previous url: %s", url_.c_str());
+    LOG_INFO("new url: %s", restore_message->uri()->c_str());
+    url_ = *restore_message->uri();
+  }
+
+  LOG_INFO("previous playing time: %llu ms", pre_playing_time_);
+  LOG_INFO("new resume time: %lld ms", resume_time);
+  // resume_time < 0  ==> use previous playing time
+  // resume_time == 0 ==> play from beginning
+  // resume_time > 0  ==> play from resume_time(Third-party settings)
+  if (resume_time >= 0) pre_playing_time_ = static_cast<uint64_t>(resume_time);
+
+  if (player_) {
+    player_get_state(player_, &state);
+    if (state >= PLAYER_STATE_READY) {
+      LOG_ERROR("[MediaPlayer] Player is in invalid state.");
+      return false;
+    }
+  }
+
+  restore_completed_ = true;
+  if (Create(url_, *restore_message) < 0) {
+    LOG_ERROR("[MediaPlayer] Fail to create player.");
+    return false;
+  }
path: packages/video_player_avplay/tizen/src/media_player.cc
user: login: gin7773
body: Change 'restore_completed_' to 'is_restored_'.

'pre_playing_time_' will be updated in Suspend() and will only be used in ResotorePlayer(). It does not affect other APIs, so there is no need to handle 'pre_playing_time_' when create fails.
created_at: 2025-04-07T07:58:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: gin7773
body: Done.
created_at: 2025-04-07T07:58:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
+  if (state <= plusplayer::State::kTrackSourceReady) {
+    ret = Close(player_);
+    assert(ret == true && "[PlusPlayer] Player close fail");
+    LOG_INFO("[PlusPlayer] Player is in invalid state [%d].", state);
+    return true;
+  } else if (state != plusplayer::State::kPaused) {
+    LOG_INFO("[PlusPlayer] Player calling pause from suspend.");
+    if (::Suspend(player_) == false) {
+      LOG_ERROR(
+          "[PlusPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      ret = StopAndClose();
+      assert(ret == true &&
+             "[PlusPlayer] Suspend error, palyer StopAndClose fail");
+      return false;
+    }
+    SendIsPlayingState(false);
+  }
+  return true;
+}
+
+bool PlusPlayer::Restore(const CreateMessage *restore_message,
+                         int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] Restore is called.");
+
+  plusplayer::State state;
+  if (player_) {
+    state = GetState(player_);
+    if (state != plusplayer::State::kNone &&
+        state != plusplayer::State::kPaused &&
+        state != plusplayer::State::kPlaying) {
+      LOG_ERROR("[PlusPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (!memento_) {
+    LOG_ERROR(
+        "[PlusPlayer] No memento to restore. Player is in invalid state [%d]",
+        state);
+    return false;
+  }
+
+  bool ret = false;
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  if (restore_message->uri()) {
+    LOG_ERROR(
+        "[PlusPlayer] Restore URL is not emptpy, close the existing instance.");
+    ret = StopAndClose();
+    assert(ret == true &&
+           "[PlusPlayer] RestoreUrl is not empty, player StopAndClose fail");
+  }
+
+  state = GetState(player_);
+  switch (state) {
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: gin7773
body: You're right. That has been deleted. Thanks.
created_at: 2025-04-07T07:59:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: gin7773
body: `player_` is valid after ::Close(), it can't be assigned to `null`. Otherwise the program will crash.
created_at: 2025-04-07T08:01:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
+  if (state <= plusplayer::State::kTrackSourceReady) {
+    ret = Close(player_);
+    assert(ret == true && "[PlusPlayer] Player close fail");
+    LOG_INFO("[PlusPlayer] Player is in invalid state [%d].", state);
+    return true;
+  } else if (state != plusplayer::State::kPaused) {
+    LOG_INFO("[PlusPlayer] Player calling pause from suspend.");
+    if (::Suspend(player_) == false) {
+      LOG_ERROR(
+          "[PlusPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      ret = StopAndClose();
+      assert(ret == true &&
+             "[PlusPlayer] Suspend error, palyer StopAndClose fail");
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: gin7773
body: Done. Thank you.
created_at: 2025-04-07T08:01:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
+  if (state <= plusplayer::State::kTrackSourceReady) {
+    ret = Close(player_);
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: gin7773
body: Done.
created_at: 2025-04-07T08:01:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -743,6 +748,223 @@ bool PlusPlayer::SetDisplayMode(int64_t display_mode) {
                           static_cast<plusplayer::DisplayMode>(display_mode));
 }
 
+bool PlusPlayer::StopAndClose() {
+  LOG_INFO("[PlusPlayer] StopAndClose is called.");
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  is_buffering_ = false;
+  plusplayer::State state = GetState(player_);
+  if (state < plusplayer::State::kReady) {
+    LOG_INFO("[PlusPlayer] Player already stop, nothing to do.");
+    return true;
+  }
+
+  if (!::Stop(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to stop.");
+    return false;
+  }
+
+  if (!::Close(player_)) {
+    LOG_ERROR("[PlusPlayer] Player fail to close.");
+    return false;
+  }
+
+  if (drm_manager_) {
+    drm_manager_->ReleaseDrmSession();
+    drm_manager_.reset();
+  }
+
+  return true;
+}
+
+bool PlusPlayer::Suspend() {
+  LOG_INFO("[PlusPlayer] Suspend is called.");
+  bool ret = false;
+  int res = -1;
+
+  if (!player_) {
+    LOG_ERROR("[PlusPlayer] Player not created.");
+    return false;
+  }
+
+  if (is_prebuffer_mode_) {
+    LOG_ERROR("[PlusPlayer] Player is in prebuffer mode, do nothing.");
+    return true;
+  }
+
+  memento_.reset(new plusplayer::PlayerMemento());
+  if (!GetMemento(player_, memento_.get())) {
+    LOG_ERROR("[PlusPlayer] Player fail to get memento.");
+    return false;
+  }
+  LOG_INFO("[PlusPlayer] Memento saved current state: %d, position: %" PRIu64
+           " ms, "
+           "is_live: %d",
+           (int)memento_->state, memento_->playing_time, memento_->is_live);
+
+  if (memento_->is_live) {
+    memento_->playing_time = 0;
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player is live, StopAndClose fail");
+    LOG_INFO("[PlusPlayer] Player is live: close done successfully.");
+    return true;
+  }
+
+  res = power_state_proxy_->device_power_get_state();
+  assert(res != POWER_STATE_ERROR && "[PlusPlayer] Get power state fail.");
+  if (res == POWER_STATE_STANDBY) {
+    LOG_INFO("[PlusPlayer] Power state is standby.");
+    ret = StopAndClose();
+    assert(ret == true && "[PlusPlayer] Player StopAndClose fail.");
+    LOG_INFO("[PlusPlayer] Standby state: close done successfully.");
+    return true;
+  } else {
+    LOG_INFO("[PlusPlayer] Player state is not standby: %d, do nothing.", res);
+  }
+
+  plusplayer::State state = GetState(player_);
+  if (state <= plusplayer::State::kTrackSourceReady) {
+    ret = Close(player_);
+    assert(ret == true && "[PlusPlayer] Player close fail");
+    LOG_INFO("[PlusPlayer] Player is in invalid state [%d].", state);
+    return true;
+  } else if (state != plusplayer::State::kPaused) {
+    LOG_INFO("[PlusPlayer] Player calling pause from suspend.");
+    if (::Suspend(player_) == false) {
+      LOG_ERROR(
+          "[PlusPlayer] Suspend fail, in restore player instance would be "
+          "created newly.");
+      ret = StopAndClose();
+      assert(ret == true &&
+             "[PlusPlayer] Suspend error, palyer StopAndClose fail");
+      return false;
+    }
+    SendIsPlayingState(false);
+  }
+  return true;
+}
+
+bool PlusPlayer::Restore(const CreateMessage *restore_message,
+                         int64_t resume_time) {
+  LOG_INFO("[PlusPlayer] Restore is called.");
+
+  plusplayer::State state;
+  if (player_) {
+    state = GetState(player_);
+    if (state != plusplayer::State::kNone &&
+        state != plusplayer::State::kPaused &&
+        state != plusplayer::State::kPlaying) {
+      LOG_ERROR("[PlusPlayer] Player is in invalid state [%d].", state);
+      return false;
+    }
+  }
+
+  if (!memento_) {
+    LOG_ERROR(
+        "[PlusPlayer] No memento to restore. Player is in invalid state [%d]",
+        state);
+    return false;
+  }
+
+  bool ret = false;
path: packages/video_player_avplay/tizen/src/plus_player.cc
user: login: gin7773
body: This has been deprecated.
created_at: 2025-04-07T08:02:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,29 @@
+// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_PLUGIN_POWER_STATE_PROXY_H_
+#define FLUTTER_PLUGIN_POWER_STATE_PROXY_H_
+
+#include <player.h>
path: packages/video_player_avplay/tizen/src/power_state_proxy.h
user: login: gin7773
body: Done.
created_at: 2025-04-07T08:02:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,29 @@
+// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_PLUGIN_POWER_STATE_PROXY_H_
+#define FLUTTER_PLUGIN_POWER_STATE_PROXY_H_
+
+#include <player.h>
+
+typedef enum {
+  POWER_STATE_NORMAL,          /**< Normal state */
+  POWER_STATE_PICTUREOFF,      /**< Picture off state */
+  POWER_STATE_STANDBY,         /**< Background Standby state */
+  POWER_STATE_OFF,             /**< Cold Standby state */
+  POWER_STATE_SUSPEND_STANDBY, /**< Suspend Standby state */
+  POWER_STATE_ERROR            /**< Error state */
+} power_state_e;
+
+class PowerStateProxy {
path: packages/video_player_avplay/tizen/src/power_state_proxy.h
user: login: gin7773
body: Already modified.
created_at: 2025-04-07T08:03:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -348,6 +353,29 @@ ErrorOr<bool> VideoPlayerTizenPlugin::SetDisplayMode(
   return player->SetDisplayMode(msg.display_mode());
 }
 
+std::optional<FlutterError> VideoPlayerTizenPlugin::Suspend(int64_t player_id) {
+  VideoPlayer *player = FindPlayerById(player_id);
+  if (!player) {
+    return FlutterError("Invalid argument", "Player not found");
+  }
+  if (player->Suspend() == false) {
path: packages/video_player_avplay/tizen/src/video_player_tizen_plugin.cc
user: login: gin7773
body: Done.
created_at: 2025-04-07T08:03:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,6 +22,12 @@ export 'src/closed_caption_file.dart';
 export 'src/drm_configs.dart';
 export 'src/tracks.dart';
 
+/// This will be used to set the ResumeTime when player restore.
+typedef ResumeTimeCallback = int Function();
+
+/// This will be used to set the RecreateMessage when player restore.
+typedef RecreateMessageCallback = DataSource Function();
path: packages/video_player_avplay/lib/video_player.dart
user: login: gin7773
body: Change `ResumeTimeCallback` to `RestoreTimeCallback`.
Change `RecreateMessageCallback` to `RestoreDataSourceCallback`.
created_at: 2025-04-07T08:04:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -748,3 +752,142 @@ class _GetTextTrackButton extends StatelessWidget {
     );
   }
 }
+
+class _LiveRemoteVideo extends StatefulWidget {
+  @override
+  State<_LiveRemoteVideo> createState() => _LiveRomoteVideoState();
+}
+
+class _LiveRomoteVideoState extends State<_LiveRemoteVideo> {
+  late VideoPlayerController _controller;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = VideoPlayerController.network(
+      'https://hlive.ktv.go.kr/live/klive_h.stream/playlist.m3u8',
+    );
+
+    _controller.addListener(() {
+      if (_controller.value.hasError) {
+        print(_controller.value.errorDescription);
+      }
+      setState(() {});
+    });
+    _controller.setLooping(true);
+    _controller.initialize().then((_) => setState(() {}));
+    _controller.play();
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return SingleChildScrollView(
+      child: Column(
+        children: <Widget>[
+          Container(padding: const EdgeInsets.only(top: 20.0)),
+          const Text('Playing Live TV'),
+          Container(
+            padding: const EdgeInsets.all(20),
+            child: AspectRatio(
+              aspectRatio: _controller.value.aspectRatio,
+              child: Stack(
+                alignment: Alignment.bottomCenter,
+                children: <Widget>[
+                  VideoPlayer(_controller),
+                  ClosedCaption(text: _controller.value.caption.text),
+                  _ControlsOverlay(controller: _controller),
+                  VideoProgressIndicator(_controller, allowScrubbing: true),
+                ],
+              ),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}
+
+class _TestRemoteVideo extends StatefulWidget {
+  @override
+  State<_TestRemoteVideo> createState() => _TestRomoteVideoState();
+}
+
+class _TestRomoteVideoState extends State<_TestRemoteVideo> {
+  late VideoPlayerController _controller;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = VideoPlayerController.network(
+      'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8',
+    );
+
+    _controller.addListener(() {
+      if (_controller.value.hasError) {
+        print(_controller.value.errorDescription);
+      }
+      setState(() {});
+    });
+    _controller.setLooping(true);
+    _controller.initialize().then((_) => setState(() {}));
+    _controller.play();
+    _controller.setRestoreData(
+      recreateMessage: restoreData,
+      resumeTime: restoreTime,
+    );
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+
+  DataSource restoreData() {
+    final DataSource dataSource = DataSource(
+      sourceType: DataSourceType.network,
+      uri: 'https://media.w3.org/2010/05/bunny/trailer.mp4',
+    );
+    return dataSource;
+  }
+
+  int restoreTime() {
+    /// if resumeTime >= 0 , it will restore from resumeTime
+    /// if resumeTime is not set or <0, it will restore from the time when suspend is called
+    const int resumeTime = 0;
path: packages/video_player_avplay/example/lib/main.dart
user: login: gin7773
body: `suspend()` and `restore()` are automatically called by plugin. The user cannot call `restore()` manually, so the time when `restore()` ends cannot be obtained, `seekto()` cannot be called.
created_at: 2025-04-07T08:08:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,27 @@
+// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_PLUGIN_DEVICE_PROXY_H_
+#define FLUTTER_PLUGIN_DEVICE_PROXY_H_
+
+typedef enum {
+  POWER_STATE_NORMAL,          /**< Normal state */
+  POWER_STATE_PICTUREOFF,      /**< Picture off state */
+  POWER_STATE_STANDBY,         /**< Background Standby state */
+  POWER_STATE_OFF,             /**< Cold Standby state */
+  POWER_STATE_SUSPEND_STANDBY, /**< Suspend Standby state */
+  POWER_STATE_ERROR            /**< Error state */
+} power_state_e;
+
+class DeviceProxy {
+ public:
+  DeviceProxy();
+  ~DeviceProxy();
+  power_state_e device_power_get_state(void);
+
+ private:
+  void* power_state_handle_ = nullptr;
+};
+
+#endif  // FLUTTER_PLUGIN_POWER_STATE_PROXY_H_
path: packages/video_player_avplay/tizen/src/device_proxy.h
user: login: JSUYA
body: ```suggestion
#endif  // FLUTTER_PLUGIN_DEVICE_PROXY_H_
```
created_at: 2025-04-07T09:26:36Z
author_association: MEMBER
diff_hunk: @@ -865,6 +885,48 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
path: packages/video_player_avplay/lib/video_player.dart
user: login: JSUYA
body: description also needs to be modified.
+)
I think resumeTime is little ambiguous. This value is the point where the video playbacks again when the video is restored. We use position rather than time in seekTo(), etc. But [avplay webapi ](https://developer.samsung.com/smarttv/develop/api-references/samsung-product-api-references/avplay-api.html)uses resumeTime. hahaha...
created_at: 2025-04-07T09:44:12Z
author_association: MEMBER
diff_hunk: @@ -186,6 +186,20 @@ abstract class VideoPlayerPlatform extends PlatformInterface {
     );
   }
 
+  /// Pauses the player when the application is sent to the background.
+  Future<void> suspend(int playerId) {
+    throw UnimplementedError('suspend() has not been implemented.');
+  }
+
+  /// Restores the player when the application is resumed.
+  Future<void> restore(
+    int playerId, {
+    DataSource? dataSource,
+    int resumeTime = -1,
+  }) {
path: packages/video_player_avplay/lib/video_player_platform_interface.dart
user: login: JSUYA
body: Could you please rewrite the API description to be more detailed ?, especially since the behavior depends on the value of resumeTime.
please refer to https://developer.samsung.com/smarttv/develop/api-references/samsung-product-api-references/avplay-api.html#AVPlayManager-restore
created_at: 2025-04-07T09:48:29Z
author_association: MEMBER
diff_hunk: @@ -748,3 +752,142 @@ class _GetTextTrackButton extends StatelessWidget {
     );
   }
 }
+
+class _LiveRemoteVideo extends StatefulWidget {
+  @override
+  State<_LiveRemoteVideo> createState() => _LiveRomoteVideoState();
+}
+
+class _LiveRomoteVideoState extends State<_LiveRemoteVideo> {
+  late VideoPlayerController _controller;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = VideoPlayerController.network(
+      'https://hlive.ktv.go.kr/live/klive_h.stream/playlist.m3u8',
+    );
+
+    _controller.addListener(() {
+      if (_controller.value.hasError) {
+        print(_controller.value.errorDescription);
+      }
+      setState(() {});
+    });
+    _controller.setLooping(true);
+    _controller.initialize().then((_) => setState(() {}));
+    _controller.play();
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return SingleChildScrollView(
+      child: Column(
+        children: <Widget>[
+          Container(padding: const EdgeInsets.only(top: 20.0)),
+          const Text('Playing Live TV'),
+          Container(
+            padding: const EdgeInsets.all(20),
+            child: AspectRatio(
+              aspectRatio: _controller.value.aspectRatio,
+              child: Stack(
+                alignment: Alignment.bottomCenter,
+                children: <Widget>[
+                  VideoPlayer(_controller),
+                  ClosedCaption(text: _controller.value.caption.text),
+                  _ControlsOverlay(controller: _controller),
+                  VideoProgressIndicator(_controller, allowScrubbing: true),
+                ],
+              ),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}
+
+class _TestRemoteVideo extends StatefulWidget {
+  @override
+  State<_TestRemoteVideo> createState() => _TestRomoteVideoState();
+}
+
+class _TestRomoteVideoState extends State<_TestRemoteVideo> {
+  late VideoPlayerController _controller;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = VideoPlayerController.network(
+      'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8',
+    );
+
+    _controller.addListener(() {
+      if (_controller.value.hasError) {
+        print(_controller.value.errorDescription);
+      }
+      setState(() {});
+    });
+    _controller.setLooping(true);
+    _controller.initialize().then((_) => setState(() {}));
+    _controller.play();
+    _controller.setRestoreData(
+      recreateMessage: restoreData,
+      resumeTime: restoreTime,
+    );
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+
+  DataSource restoreData() {
+    final DataSource dataSource = DataSource(
+      sourceType: DataSourceType.network,
+      uri: 'https://media.w3.org/2010/05/bunny/trailer.mp4',
+    );
+    return dataSource;
+  }
+
+  int restoreTime() {
+    /// if resumeTime >= 0 , it will restore from resumeTime
+    /// if resumeTime is not set or <0, it will restore from the time when suspend is called
+    const int resumeTime = 0;
path: packages/video_player_avplay/example/lib/main.dart
user: login: JSUYA
body: um... suspend() and restore() are exposed externally through video_player_platform_interface. Users can also call suspend() and restore() directly.
User can use setRestoreData() to specify the url and time to start restoring.
However, I understand that resumeTime is needed because we need to provide similar specifications to the avplay web API.

+)
Actually I don't know why setRestoreData() is needed. Can you explain why you added setRestoreData() instead of restore() arguments?
created_at: 2025-04-07T10:32:41Z
author_association: MEMBER
diff_hunk: @@ -865,6 +885,48 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
+  void setRestoreData({
+    RestoreDataSourceCallback? restoreDataSource,
+    RestoreTimeCallback? resumeTime,
+  }) {
+    _onRestoreDataSource = restoreDataSource;
+    _onRestoreTime = resumeTime;
+  }
path: packages/video_player_avplay/lib/video_player.dart
user: login: JSUYA
body: I added the code below to main.dart and tested this API.
```dart
TextButton(
  onPressed: () {
    _controller.suspend();
  },
  child: const Text('Suspend'),
),
TextButton(
  onPressed: () {
    _controller.restore();
  },
  child: const Text('Restore'),
),
```

If no restoreDataSource is provided and only resumeTime is provided, suspend(), restore() behave the same as pause(), resume(). It does not start from resumeTime, and StopAndClose() is not called, only ::Restore(player_, memento_->state) is called.


created_at: 2025-04-07T10:43:08Z
author_association: MEMBER
diff_hunk: @@ -865,6 +885,48 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
+  void setRestoreData({
+    RestoreDataSourceCallback? restoreDataSource,
+    RestoreTimeCallback? resumeTime,
+  }) {
+    _onRestoreDataSource = restoreDataSource;
+    _onRestoreTime = resumeTime;
+  }
path: packages/video_player_avplay/lib/video_player.dart
user: login: xiaowei-guan
body: Hello, why we need restoreDataSource:
1.if plugin plays a vod url, plugin just need to call suspend and restore when receiving onPause and onResume.
2.if plugin plays a live url, plugin need re-create player when receiving onPause and onResume. and sometimes the live URL will become invalid and we need to update a new live url, for example, the following URL must be based on UTC time to be a live URL
```
https://test/index.mpd&start=2025-02-21T01:05:59Z&end=2025-02-21T02:05:59Z"
```
the restoreDataSource can store url and drm license server url etc.

created_at: 2025-04-08T01:40:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -865,6 +885,48 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
path: packages/video_player_avplay/lib/video_player.dart
user: login: gin7773
body: Done. Yes, and AVPlay API also uses 'resumeTime'.
created_at: 2025-04-08T02:00:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -186,6 +186,20 @@ abstract class VideoPlayerPlatform extends PlatformInterface {
     );
   }
 
+  /// Pauses the player when the application is sent to the background.
+  Future<void> suspend(int playerId) {
+    throw UnimplementedError('suspend() has not been implemented.');
+  }
+
+  /// Restores the player when the application is resumed.
+  Future<void> restore(
+    int playerId, {
+    DataSource? dataSource,
+    int resumeTime = -1,
+  }) {
path: packages/video_player_avplay/lib/video_player_platform_interface.dart
user: login: gin7773
body: Done. Please check~
created_at: 2025-04-08T02:00:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,27 @@
+// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_PLUGIN_DEVICE_PROXY_H_
+#define FLUTTER_PLUGIN_DEVICE_PROXY_H_
+
+typedef enum {
+  POWER_STATE_NORMAL,          /**< Normal state */
+  POWER_STATE_PICTUREOFF,      /**< Picture off state */
+  POWER_STATE_STANDBY,         /**< Background Standby state */
+  POWER_STATE_OFF,             /**< Cold Standby state */
+  POWER_STATE_SUSPEND_STANDBY, /**< Suspend Standby state */
+  POWER_STATE_ERROR            /**< Error state */
+} power_state_e;
+
+class DeviceProxy {
+ public:
+  DeviceProxy();
+  ~DeviceProxy();
+  power_state_e device_power_get_state(void);
+
+ private:
+  void* power_state_handle_ = nullptr;
+};
+
+#endif  // FLUTTER_PLUGIN_POWER_STATE_PROXY_H_
path: packages/video_player_avplay/tizen/src/device_proxy.h
user: login: gin7773
body: Done.
created_at: 2025-04-08T02:00:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -865,6 +885,48 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
     return _videoPlayerPlatform.setDisplayMode(_playerId, displayMode);
   }
 
+  /// Set the restore_message and resume_time of video.
+  void setRestoreData({
+    RestoreDataSourceCallback? restoreDataSource,
+    RestoreTimeCallback? resumeTime,
+  }) {
+    _onRestoreDataSource = restoreDataSource;
+    _onRestoreTime = resumeTime;
+  }
path: packages/video_player_avplay/lib/video_player.dart
user: login: gin7773
body: > I added the code below to main.dart and tested this API.
> 
> ```dart
> TextButton(
>   onPressed: () {
>     _controller.suspend();
>   },
>   child: const Text('Suspend'),
> ),
> TextButton(
>   onPressed: () {
>     _controller.restore();
>   },
>   child: const Text('Restore'),
> ),
> ```
> 
> If no restoreDataSource is provided and only resumeTime is provided, suspend(), restore() behave the same as pause(), resume(). It does not start from resumeTime, and StopAndClose() is not called, only ::Restore(player_, memento_->state) is called.

Refer to the implementation of `restore()` in the AVPlay webapi code, I think `resumeTime` is only used when player need to be re-created ,that is, when `restorePlayer()` is called. This includes three scenarios: **the streaming type is live, power off/on within 5 seconds, or changing the URL.** 
So when non-live video is playing/pause but occupied by other apps or click home button on remote controller, resumeTime will not take effect when calling `restore()`.
created_at: 2025-04-08T02:31:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -186,6 +186,20 @@ abstract class VideoPlayerPlatform extends PlatformInterface {
     );
   }
 
+  /// Pauses the player when the application is sent to the background.
+  Future<void> suspend(int playerId) {
+    throw UnimplementedError('suspend() has not been implemented.');
+  }
+
+  /// Restores the player when the application is resumed.
+  Future<void> restore(
+    int playerId, {
+    DataSource? dataSource,
+    int resumeTime = -1,
+  }) {
path: packages/video_player_avplay/lib/video_player_platform_interface.dart
user: login: JSUYA
body: Sorry for bothering you. Since restore api is becoming private, it seems better to move detailed description to setRestoreData()(video_player.dart) rather than here.
Users will be able to understand how suspend and restore work on our TV through the description of setRestoreData().

created_at: 2025-04-08T10:21:45Z
author_association: MEMBER
diff_hunk: @@ -186,6 +186,20 @@ abstract class VideoPlayerPlatform extends PlatformInterface {
     );
   }
 
+  /// Pauses the player when the application is sent to the background.
+  Future<void> suspend(int playerId) {
+    throw UnimplementedError('suspend() has not been implemented.');
+  }
+
+  /// Restores the player when the application is resumed.
+  Future<void> restore(
+    int playerId, {
+    DataSource? dataSource,
+    int resumeTime = -1,
+  }) {
path: packages/video_player_avplay/lib/video_player_platform_interface.dart
user: login: gin7773
body: You're right. That's done.
created_at: 2025-04-09T01:18:27Z
author_association: CONTRIBUTOR

PR comments:
user: login: xiaowei-guan
created_at: 2025-04-03T01:38:34Z
body: @JSUYA Hello，we have finished implementing the suspend and restore api, please review this PR. Thank you~
author_association: CONTRIBUTOR
user: login: JSUYA
created_at: 2025-04-03T02:56:48Z
body: > @JSUYA Hello，we have finished implementing the suspend and restore api, please review this PR. Thank you~

I will be back in the office next Monday. I will review this as soon as possible :)

@seungsoo47 
Could you please test this PR on tv device and review the code?
When the video sample in the example is playing, if you turn off the TV power and turn it back on within 5 seconds, the video should start playing from the point you were watching.
You can also add a separate button to test the suspend API.

author_association: MEMBER
user: login: gin7773
created_at: 2025-04-07T05:51:40Z
body: > While testing the restore() and suspend() functions several times, I noticed something odd.
> 
> ```
> [I] flutter: suspend() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24
> [I] flutter: suspend() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35
> [I] flutter: restore() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24, https://media.w3.org/2010/05/bunny/trailer.mp4
> [I] flutter: restore() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35, null
> ```
> 
> Like the logs (I inserted), suspend() and restore() were sometimes called two times. And then, the video player didn't work properly. (Note that I did not find the reproduction path, but the logs often appeared when I tested them a lot.)

Sorry, I can't reproduce this issue for now. What version of Tizen are you used, and do you have a complete log?
author_association: CONTRIBUTOR
user: login: seungsoo47
created_at: 2025-04-07T06:58:44Z
body: > > While testing the restore() and suspend() functions several times, I noticed something odd.
> > ```
> > [I] flutter: suspend() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24
> > [I] flutter: suspend() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35
> > [I] flutter: restore() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24, https://media.w3.org/2010/05/bunny/trailer.mp4
> > [I] flutter: restore() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35, null
> > ```
> > Like the logs (I inserted), suspend() and restore() were sometimes called two times. And then, the video player didn't work properly. (Note that I did not find the reproduction path, but the logs often appeared when I tested them a lot.)
> 
> Sorry, I can't reproduce this issue for now. What version of Tizen are you used, and do you have a complete log?

I tested it on Tizen 9.0 TV.
I will email you log files.
Please let me know if you would like more information.

author_association: CONTRIBUTOR
user: login: gin7773
created_at: 2025-04-07T07:04:50Z
body: > > > While testing the restore() and suspend() functions several times, I noticed something odd.
> > > ```
> > > [I] flutter: suspend() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24
> > > [I] flutter: suspend() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35
> > > [I] flutter: restore() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24, https://media.w3.org/2010/05/bunny/trailer.mp4
> > > [I] flutter: restore() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35, null
> > > ```
> > > 
> > > 
> > >     
> > >       
> > >     
> > > 
> > >       
> > >     
> > > 
> > >     
> > >   
> > > Like the logs (I inserted), suspend() and restore() were sometimes called two times. And then, the video player didn't work properly. (Note that I did not find the reproduction path, but the logs often appeared when I tested them a lot.)
> > 
> > 
> > Sorry, I can't reproduce this issue for now. What version of Tizen are you used, and do you have a complete log?
> 
> I tested it on Tizen 9.0 TV. I will email you log files. Please let me know if you would like more information.

Do the ‘24, 35’ in the log have a specific meaning, or are the two logs added to different locations? Can you elaborate on the scenario where this issue occurs?
author_association: CONTRIBUTOR
user: login: seungsoo47
created_at: 2025-04-07T07:58:04Z
body: > > > > While testing the restore() and suspend() functions several times, I noticed something odd.
> > > > ```
> > > > [I] flutter: suspend() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24
> > > > [I] flutter: suspend() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35
> > > > [I] flutter: restore() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24, https://media.w3.org/2010/05/bunny/trailer.mp4
> > > > [I] flutter: restore() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35, null
> > > > ```
> > > > 
> > > > 
> > > >     
> > > >       
> > > >     
> > > > 
> > > >       
> > > >     
> > > > 
> > > >     
> > > >   
> > > > Like the logs (I inserted), suspend() and restore() were sometimes called two times. And then, the video player didn't work properly. (Note that I did not find the reproduction path, but the logs often appeared when I tested them a lot.)
> > > 
> > > 
> > > Sorry, I can't reproduce this issue for now. What version of Tizen are you used, and do you have a complete log?
> > 
> > 
> > I tested it on Tizen 9.0 TV. I will email you log files. Please let me know if you would like more information.
> 
> Do the ‘24, 35’ in the log have a specific meaning, or are the two logs added to different locations? Can you elaborate on the scenario where this issue occurs?

24 and 35 are _playerId. I left the logs to check if the suspend() and restore() match.
And I just sent you the log files and reproduction path by email.
author_association: CONTRIBUTOR
user: login: gin7773
created_at: 2025-04-07T08:21:38Z
body: > > > > > While testing the restore() and suspend() functions several times, I noticed something odd.
> > > > > ```
> > > > > [I] flutter: suspend() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24
> > > > > [I] flutter: suspend() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35
> > > > > [I] flutter: restore() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24, https://media.w3.org/2010/05/bunny/trailer.mp4
> > > > > [I] flutter: restore() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35, null
> > > > > ```
> > > > > 
> > > > > 
> > > > >     
> > > > >       
> > > > >     
> > > > > 
> > > > >       
> > > > >     
> > > > > 
> > > > >     
> > > > >   
> > > > > Like the logs (I inserted), suspend() and restore() were sometimes called two times. And then, the video player didn't work properly. (Note that I did not find the reproduction path, but the logs often appeared when I tested them a lot.)
> > > > 
> > > > 
> > > > Sorry, I can't reproduce this issue for now. What version of Tizen are you used, and do you have a complete log?
> > > 
> > > 
> > > I tested it on Tizen 9.0 TV. I will email you log files. Please let me know if you would like more information.
> > 
> > 
> > Do the ‘24, 35’ in the log have a specific meaning, or are the two logs added to different locations? Can you elaborate on the scenario where this issue occurs?
> 
> 24 and 35 are _playerId. I left the logs to check if the suspend() and restore() match. And I just sent you the log files and reproduction path by email.

Thanks a lot. I'll retry now.
author_association: CONTRIBUTOR
user: login: xiaowei-guan
created_at: 2025-04-08T03:09:07Z
body: I think suspend and restore interface just for plugin, app side can't call these two interfaces
author_association: CONTRIBUTOR