// the time stamp of the buffered data.
const base::TimeDelta kMinWaitTimeForPlayback = base::Milliseconds(500);
-// The delay time to check buffering and decide operations.
-const base::TimeDelta kOperationDecisionDelay = base::Milliseconds(500);
+// The value above which packets will be discarded if they're too far away from
+// the seek position.
+const base::TimeDelta kMaxSeekDiff = base::Milliseconds(2000);
player_buffer_size_t GetMaxAudioBufferSize() {
return kPlayerAudioBufferSize;
expected_seek_ = false;
seek_position_ = base::TimeDelta();
pending_seek_ = false;
- pending_seek_position_ = base::TimeDelta();
+ pending_seek_position_ = base::TimeDelta::Max();
should_set_playback_rate_ = false;
ClearBufferQueue(DemuxerStream::VIDEO);
ClearBufferQueue(DemuxerStream::AUDIO);
sink_ = nullptr;
- StopOperationForDataTimer();
-
if (GetPlayerState() == ESPLUSPLAYER_STATE_NONE) {
LOG_ID(ERROR, player_id_)
<< "(" << static_cast<void*>(this) << ") " << __func__
if (!is_media_related_action)
is_paused_ = true;
- // To decide operation is not needed in pause.
- StopOperationForDataTimer();
-
LOG_ID(INFO, player_id_) << "(" << static_cast<void*>(this) << ") "
<< __func__ << " is_buffering : " << is_buffering_
<< " is_media_action : " << is_media_related_action;
ClearBufferQueue(DemuxerStream::VIDEO);
ClearBufferQueue(DemuxerStream::AUDIO);
+ buffer_observer_->ResetBufferStatus();
seek_cb_ = std::move(seek_cb);
SeekInternal(time);
is_readytoseek_ = false;
seek_position_ = time;
pending_seek_ = false;
- pending_seek_position_ = base::TimeDelta();
+ pending_seek_position_ = base::TimeDelta::Max();
if (seek_cb_)
std::move(seek_cb_).Run();
}
if (GetPlayerState() < ESPLUSPLAYER_STATE_READY) {
LOG_ID(INFO, player_id_)
<< "player is not ready, state:" << GetString(GetPlayerState());
+
+ ReportBufferingStateIfNeeded(BUFFERING_HAVE_NOTHING,
+ BUFFERING_CHANGE_REASON_UNKNOWN);
+
if (flush_cb)
std::move(flush_cb).Run();
return;
UpdateBufferedDtsDifference();
expected_seek_ = true;
+
buffer_observer_->ResetBufferStatus();
std::move(flush_cb).Run();
if (!ReadFromBufferQueue(type))
return;
+ PerformOperationForData();
+
// Avoid unnecessary or redundant read requests.
if (!ShouldFeed(type) || ReadRequested(type) || IsEos(type) ||
(is_prepared_ && is_paused_ && !is_seeking_)) {
return;
}
- // If the playing statue is expected,
- // keep running a timer to decide play or pause with data.
- if (!is_paused_) {
- StartOperationForDataTimer();
- }
-
+ // If there are more packets in the queue, process them...
if (!GetBufferQueue(type).empty()) {
ReadBuffer(type);
return;
<< ") player is not prepared ignore eos";
} else
status = SubmitEosPacket(type);
- } else
+ } else {
+ // If we're expecting a seek (i.e. we've received a Flush()), then we need
+ // to discard buffers, otherwise there is a chance that the demux buffer
+ // is filled and the new data required to seek is not received.
+ if (expected_seek_ && is_prepared_) {
+ LOG_ID(INFO, player_id_) << "Discard buffers while expected_seek_";
+ GetBufferQueue(type).pop_front();
+ PostReadBuffer(type);
+ return true;
+ }
+
+ if (is_seeking_) {
+ // If we're seeking, and the data received is *not* for the current seek
+ // point, then discard it, or it can throw an error in the lower levels
+ // (causing an OnError() callback and video to stop).
+ base::TimeDelta diff = buffer->timestamp() - seek_position_;
+
+ if (diff.magnitude() > kMaxSeekDiff) {
+ LOG_ID(WARNING, player_id_) << "is_seeking_ buffer timestamp too far "
+ << "from seek position, discarding";
+ GetBufferQueue(type).pop_front();
+ PostReadBuffer(type);
+ return true;
+ }
+ }
+
status = SubmitEsPacket(type, buffer);
+ }
if (status == ESPLUSPLAYER_SUBMIT_STATUS_FULL) {
// If buffer is full, try again after a while.
<< ", is_key_frame: " << buffer->is_key_frame()
<< ", this: " << this;
- // This filed only set when PushMediaPacket
+ // This filled only set when PushMediaPacket
packet.matroska_color_info = nullptr;
esplusplayer_submit_status status =
esplusplayer_submit_packet(esplayer_, &packet);
LOG_ID(INFO, player_id_) << __func__ << " "
<< DemuxerStream::GetTypeName(type) << " : "
<< GetString(status);
+
if (expected_seek_ && is_prepared_) {
- LOG_ID(INFO, player_id_) << "Ignore the buffer updates while seek";
+ // We need to wait for the Seek() call before we start processing buffers...
+ LOG_ID(INFO, player_id_) << "Ignore the buffer updates while expected seek";
+ if (GetBufferQueue(type).size()) {
+ GetBufferQueue(type).pop_front();
+ }
return;
}
case kBufferNormal:
if (is_seeking_ && is_readytoseek_ == false) {
LOG_ID(INFO, player_id_)
- << "Ignore the buffer status before read to seek";
+ << "Ignore the buffer status before ready to seek";
} else {
is_buffering_ = true;
SetShouldFeed(type, true);
ReportBufferingStateIfNeeded(BUFFERING_HAVE_ENOUGH,
BUFFERING_CHANGE_REASON_UNKNOWN);
is_paused_by_buffering_ = false;
- Play();
+ if (!is_paused_) {
+ Play();
+ }
break;
case OperationForData::PAUSE:
ReportBufferingStateIfNeeded(BUFFERING_HAVE_NOTHING,
is_paused_by_buffering_ = true;
Pause(true);
break;
+ default:
+ break;
}
}
bool should_pause = false;
if (has_video_media_type &&
current_position >= last_frames_.get<DemuxerStream::VIDEO>().first) {
+ ReportBufferingStateIfNeeded(BUFFERING_HAVE_NOTHING,
+ BUFFERING_CHANGE_REASON_UNKNOWN);
should_pause = true;
}
if (has_audio_media_type &&
current_position >= last_frames_.get<DemuxerStream::AUDIO>().first) {
+ ReportBufferingStateIfNeeded(BUFFERING_HAVE_NOTHING,
+ BUFFERING_CHANGE_REASON_UNKNOWN);
should_pause = true;
}
return OperationForData::NONE;
}
-void MediaPlayerESPlusPlayer::StartOperationForDataTimer() {
- if (!operation_for_data_timer_.IsRunning()) {
- operation_for_data_timer_.Start(
- FROM_HERE, kOperationDecisionDelay, this,
- &MediaPlayerESPlusPlayer::PerformOperationForData);
- }
-}
-
-void MediaPlayerESPlusPlayer::StopOperationForDataTimer() {
- if (operation_for_data_timer_.IsRunning()) {
- operation_for_data_timer_.Stop();
- }
-}
-
void MediaPlayerESPlusPlayer::OnReadyToPrepare(
const esplusplayer_stream_type stream_type) {
if (!task_runner_->BelongsToCurrentThread()) {