Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/video_player_avplay/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
## NEXT
## 0.8.19

* Omit obvious local variable types.
* Reformat with a line length of 100.
* Replace Ecore pipes with GLib idle callbacks.
* Apply clang-format-16 formatting.
* Use wrapper api.
* Handle consecutive seekTo calls.
* Ignore redundant play/pause calls instead of reporting an error.

## 0.8.18

Expand Down
2 changes: 1 addition & 1 deletion packages/video_player_avplay/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ To use this package, add `video_player_avplay` as a dependency in your `pubspec.

```yaml
dependencies:
video_player_avplay: ^0.8.18
video_player_avplay: ^0.8.19
```

Then you can import `video_player_avplay` in your Dart code:
Expand Down
115 changes: 111 additions & 4 deletions packages/video_player_avplay/lib/src/video_player_tizen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:tizen_window_manager/tizen_window_manager.dart';
Expand All @@ -11,18 +13,54 @@ import '../video_player_platform_interface.dart';
import 'messages.g.dart';
import 'tracks.dart';

class _SeekOperation {
_SeekOperation(this.position);

final int position;
final List<Completer<void>> _waiters = <Completer<void>>[];

Future<void> attach() {
final completer = Completer<void>();
_waiters.add(completer);
return completer.future;
}

void completeAll() {
for (final Completer<void> completer in _waiters) {
if (!completer.isCompleted) {
completer.complete();
}
}
_waiters.clear();
}

void completeErrorAll(Object error) {
for (final Completer<void> completer in _waiters) {
if (!completer.isCompleted) {
completer.completeError(error);
}
}
_waiters.clear();
}
}

/// An implementation of [VideoPlayerPlatform] that uses the
/// Pigeon-generated [VideoPlayerAvplayApi].
class VideoPlayerTizen extends VideoPlayerPlatform {
final VideoPlayerAvplayApi _api = VideoPlayerAvplayApi();

final Map<int, _SeekOperation> _activeSeeks = <int, _SeekOperation>{};

final Map<int, _SeekOperation> _pendingSeeks = <int, _SeekOperation>{};

@override
Future<void> init() {
return _api.initialize();
}

@override
Future<void> dispose(int playerId) {
_cancelAllSeeks(playerId, 'Player was disposed.');
return _api.dispose(PlayerMessage(playerId: playerId));
}

Expand Down Expand Up @@ -116,9 +154,71 @@ class VideoPlayerTizen extends VideoPlayerPlatform {

@override
Future<void> seekTo(int playerId, Duration position) {
return _api.seekTo(
PositionMessage(playerId: playerId, position: position.inMilliseconds),
);
final int targetPosition = position.inMilliseconds;

if (_activeSeeks.containsKey(playerId)) {
final _SeekOperation? existing = _pendingSeeks[playerId];
if (existing != null) {
final op = _SeekOperation(targetPosition);
op._waiters.addAll(existing._waiters);
_pendingSeeks[playerId] = op;
return op.attach();
}
final op = _SeekOperation(targetPosition);
_pendingSeeks[playerId] = op;
return op.attach();
Comment thread
xiaowei-guan marked this conversation as resolved.
}

return _startSeek(playerId, targetPosition);
}

Future<void> _startSeek(int playerId, int position) {
final op = _SeekOperation(position);
final Future<void> future = op.attach();
_activeSeeks[playerId] = op;
unawaited(_runNativeSeek(playerId, op));
return future;
}

Future<void> _runNativeSeek(int playerId, _SeekOperation op) async {
try {
await _api.seekTo(PositionMessage(playerId: playerId, position: op.position));
} catch (error) {
if (identical(_activeSeeks[playerId], op)) {
_completeSeekWithError(playerId, error);
}
}
}

void _handleSeekCompleted(int playerId) {
final _SeekOperation? op = _activeSeeks.remove(playerId);
if (op != null) {
op.completeAll();
}
_startPendingSeekIfAny(playerId);
}

void _startPendingSeekIfAny(int playerId) {
final _SeekOperation? pending = _pendingSeeks.remove(playerId);
if (pending != null) {
_activeSeeks[playerId] = pending;
unawaited(_runNativeSeek(playerId, pending));
}
}

void _completeSeekWithError(int playerId, Object error) {
final _SeekOperation? op = _activeSeeks.remove(playerId);
if (op != null) {
op.completeErrorAll(error);
}
final _SeekOperation? pending = _pendingSeeks.remove(playerId);
if (pending != null) {
pending.completeErrorAll(error);
}
}

void _cancelAllSeeks(int playerId, String reason) {
_completeSeekWithError(playerId, StateError(reason));
}

@override
Expand Down Expand Up @@ -271,6 +371,7 @@ class VideoPlayerTizen extends VideoPlayerPlatform {

@override
Future<void> suspend(int playerId) {
_cancelAllSeeks(playerId, 'Player was suspended.');
return _api.suspend(playerId);
}

Expand All @@ -280,6 +381,7 @@ class VideoPlayerTizen extends VideoPlayerPlatform {
DataSource? dataSource,
int resumeTime = -1,
}) async {
_cancelAllSeeks(playerId, 'Player was restored.');
final message = CreateMessage();

if (dataSource != null) {
Expand Down Expand Up @@ -443,7 +545,6 @@ class VideoPlayerTizen extends VideoPlayerPlatform {
return VideoEvent(eventType: VideoEventType.completed);
case 'bufferingUpdate':
final value = map['value']! as int;

return VideoEvent(
buffered: value,
eventType: VideoEventType.bufferingUpdate,
Expand Down Expand Up @@ -473,9 +574,15 @@ class VideoPlayerTizen extends VideoPlayerPlatform {
eventType: VideoEventType.manifestInfoUpdated,
manifestInfo: map['manifestInfo'] as String?,
);
case 'seekCompleted':
_handleSeekCompleted(playerId);
return VideoEvent(eventType: VideoEventType.unknown);
default:
return VideoEvent(eventType: VideoEventType.unknown);
}
}).handleError((Object error, StackTrace stackTrace) {
_completeSeekWithError(playerId, error);
Error.throwWithStackTrace(error, stackTrace);
});
}

Expand Down
2 changes: 1 addition & 1 deletion packages/video_player_avplay/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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/main/packages/video_player_avplay
version: 0.8.18
version: 0.8.19

environment:
sdk: ">=3.1.0 <4.0.0"
Expand Down
45 changes: 30 additions & 15 deletions packages/video_player_avplay/tizen/src/media_player.cc
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ bool MediaPlayer::Play() {
}
if (state == PLAYER_STATE_PLAYING) {
LOG_INFO("[MediaPlayer] Player already playing.");
return false;
return true;
Comment thread
xiaowei-guan marked this conversation as resolved.
}
ret = player_start(player_);
if (ret != PLAYER_ERROR_NONE) {
Expand All @@ -251,7 +251,7 @@ bool MediaPlayer::Pause() {
}
if (state != PLAYER_STATE_PLAYING) {
LOG_INFO("[MediaPlayer] Player not playing.");
return false;
return true;
}
ret = player_pause(player_);
if (ret != PLAYER_ERROR_NONE) {
Expand Down Expand Up @@ -301,11 +301,18 @@ bool MediaPlayer::SetPlaybackSpeed(double speed) {
bool MediaPlayer::SeekTo(int64_t position, SeekCompletedCallback callback) {
LOG_INFO("[MediaPlayer] position: %lld.", position);

if (is_seeking_) {
LOG_ERROR("[MediaPlayer] Seek is already in progress.");
return false;
}

on_seek_completed_ = std::move(callback);
is_seeking_ = true;
int ret =
player_set_play_position(player_, position, true, OnSeekCompleted, this);
if (ret != PLAYER_ERROR_NONE) {
on_seek_completed_ = nullptr;
is_seeking_ = false;
LOG_ERROR("[MediaPlayer] player_set_play_position failed: %s.",
get_error_message(ret));
return false;
Expand Down Expand Up @@ -700,10 +707,12 @@ void MediaPlayer::OnSeekCompleted(void *user_data) {
LOG_INFO("[MediaPlayer] Seek completed.");

MediaPlayer *self = static_cast<MediaPlayer *>(user_data);
self->is_seeking_ = false;
if (self->on_seek_completed_) {
self->on_seek_completed_();
self->on_seek_completed_ = nullptr;
}
self->SendSeekCompleted();
}

void MediaPlayer::OnPlayCompleted(void *user_data) {
Expand All @@ -726,6 +735,8 @@ void MediaPlayer::OnError(int error_code, void *user_data) {
get_error_message(error_code));

MediaPlayer *self = static_cast<MediaPlayer *>(user_data);
self->on_seek_completed_ = nullptr;
self->is_seeking_ = false;
self->SendError("Media Player error",
std::string("Error: ") + get_error_message(error_code));
}
Expand Down Expand Up @@ -804,36 +815,40 @@ bool MediaPlayer::StopAndDestroy() {
return false;
}

bool success = true;
is_buffering_ = false;
on_seek_completed_ = nullptr;
Comment thread
xiaowei-guan marked this conversation as resolved.
is_seeking_ = 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;
success = false;
}

if (player_stop(player_) != PLAYER_ERROR_NONE) {
LOG_ERROR("[MediaPlayer] Player fail to stop.");
return false;
if (player_state == PLAYER_STATE_PLAYING ||
player_state == PLAYER_STATE_PAUSED) {
if (player_stop(player_) != PLAYER_ERROR_NONE) {
LOG_ERROR("[MediaPlayer] Player fail to stop.");
success = false;
}
}

if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
return false;
if (player_state != PLAYER_STATE_NONE && player_state != PLAYER_STATE_IDLE) {
if (player_unprepare(player_) != PLAYER_ERROR_NONE) {
LOG_ERROR("[MediaPlayer] Player fail to unprepare.");
success = false;
}
}

if (player_destroy(player_) != PLAYER_ERROR_NONE) {
LOG_ERROR("[MediaPlayer] Player fail to destroy.");
return false;
success = false;
}
player_ = nullptr;

return true;
return success;
}

bool MediaPlayer::Suspend() {
Expand Down
1 change: 1 addition & 0 deletions packages/video_player_avplay/tizen/src/media_player.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class MediaPlayer : public VideoPlayer {
std::unique_ptr<DrmManager> drm_manager_;
bool is_buffering_ = false;
SeekCompletedCallback on_seek_completed_;
bool is_seeking_ = false;
std::string url_;
player_state_e pre_state_;
int64_t pre_playing_time_;
Expand Down
Loading
Loading