[audioplayers] Fix seek and playback completion handling - #1168
seungsoo47 wants to merge 4 commits into
Conversation
seungsoo47
commented
Sep 22, 2026
- Serialize seeks and defer playback controls until seeking completes.
- Avoid missing network seek completion callbacks after stop by rewinding while paused.
- Reset the playback position before reporting completion in stop mode.
|
@codex review |
| ResetPlayer(); | ||
| PreparePlayer(); |
There was a problem hiding this comment.
PreparePlayer() is called right after ResetPlayer() (which calls player_unprepare()) without re-setting the source. Same at L521-L523.
From the player_unprepare() doc comment in the Tizen SDK player.h:
The most recently used media is reset and no longer associated with the player. Playback is no longer possible. If you want to use the player again, you must set the data URI and call player_prepare() again.
Every existing unprepare→prepare path in this file re-sets the source in between: Play() L71/L80, SetUrl() L188, SetDataSource() L202. Only the two paths added by this PR skip it.
Extract the IDLE branch of Play() (L69-L87) into a PrepareSource() helper and use it in all three places.
| if (player->should_seek_to_ >= 0) { | ||
| try { | ||
| int position = player->should_seek_to_; | ||
| player->should_seek_to_ = -1; | ||
| player->Seek(position); | ||
| return G_SOURCE_REMOVE; |
There was a problem hiding this comment.
When Stop() is called mid-seek, L129-L132 only queues pending_action_ = kPause and should_seek_to_ = 0 and returns. On the first seek completion this block (L470-L475) runs the queued Seek(0) first and returns, so the pause is applied only after the second seek completes (L480-L489). In between the player is still PLAYING while rewinding, and Dart stop() has already returned.
Right after seeking_ = false, apply Pause() first when pending_action_ == kPause, then run the queued seek. That makes it the "rewind while paused" the PR description describes.
player->seeking_ = false;
if (player->pending_action_ == PendingAction::kPause) {
player->pending_action_ = PendingAction::kNone;
try {
player->Pause();
} catch (const AudioPlayerError &error) {
player->OnLog(error.code() + ": " + error.message());
}
}
if (player->should_seek_to_ >= 0) {
| } catch (const AudioPlayerError &error) { | ||
| player->OnLog(error.code() + ": " + error.message()); | ||
| } |
There was a problem hiding this comment.
If the queued second Seek() fails, this only logs and then falls through to seek_completed_listener_ at L496. Dart AudioPlayer.seek() awaits onSeekComplete.first (audioplayers 6.8.1 audioplayer.dart L293-L304), so it resolves successfully although the target was never reached.
OnPrepared in this same PR reports the deferred-seek failure via error_listener_ (L433-L435). Please do the same here and suppress the success event.
| } | ||
|
|
||
| void AudioPlayer::Seek(int32_t position) { | ||
| completing_ = false; |
There was a problem hiding this comment.
On completion in stop mode, L521-L523 sets completing_ = true and starts an async re-prepare; the completion event is sent only from OnPrepared L411-L414 while completing_ is still set. A seek() arriving before the re-prepare finishes clears it here, so onPlayerComplete is never delivered. A seek does not start new playback and should not cancel a completion that already happened.
Don't clear completing_ in Seek(); only Play()/Stop()/ResetPlayer() genuinely supersede it
42f1ca1 to
ca10229
Compare
ca10229 to
6d7c6d1
Compare
| } | ||
|
|
||
| void AudioPlayer::Pause() { | ||
| completing_ = false; |
There was a problem hiding this comment.
The same pending-completion issue remains in Pause(). In stop mode, OnPlayCompleted() sets completing_ and starts an async re-prepare. If pause() arrives before OnPrepared() handles it, this line clears the flag and the finished track's onPlayerComplete event is lost. Pausing should not discard a completion that has already occurred. Please remove this line; Stop() already clears the flag before calling Pause().
- Do not discard a pending completion on pause. - Report a pending completion before play, stop and source reset. - Report completion even if re-preparing fails after playback ends. - Run the deferred play when a chained seek fails.