summaryrefslogtreecommitdiff
path: root/media/base/android
diff options
context:
space:
mode:
Diffstat (limited to 'media/base/android')
-rw-r--r--media/base/android/demuxer_android.h2
-rw-r--r--media/base/android/media_decoder_job.cc163
-rw-r--r--media/base/android/media_decoder_job.h86
-rw-r--r--media/base/android/media_player_android.h2
-rw-r--r--media/base/android/media_player_bridge.cc41
-rw-r--r--media/base/android/media_player_bridge.h14
-rw-r--r--media/base/android/media_source_player.cc107
-rw-r--r--media/base/android/media_source_player.h27
-rw-r--r--media/base/android/media_source_player_unittest.cc136
9 files changed, 385 insertions, 193 deletions
diff --git a/media/base/android/demuxer_android.h b/media/base/android/demuxer_android.h
index 865dc9d33f..39dc30fe7e 100644
--- a/media/base/android/demuxer_android.h
+++ b/media/base/android/demuxer_android.h
@@ -62,7 +62,7 @@ class MEDIA_EXPORT DemuxerAndroidClient {
// For regular demuxer seeks, |actual_browser_seek_time| is kNoTimestamp() and
// should be ignored by browser player.
virtual void OnDemuxerSeekDone(
- const base::TimeDelta& actual_browser_seek_time) = 0;
+ base::TimeDelta actual_browser_seek_time) = 0;
// Called whenever the demuxer has detected a duration change.
virtual void OnDemuxerDurationChanged(base::TimeDelta duration) = 0;
diff --git a/media/base/android/media_decoder_job.cc b/media/base/android/media_decoder_job.cc
index 535b8d114e..b7eef0cdfb 100644
--- a/media/base/android/media_decoder_job.cc
+++ b/media/base/android/media_decoder_job.cc
@@ -31,12 +31,15 @@ MediaDecoderJob::MediaDecoderJob(
output_eos_encountered_(false),
skip_eos_enqueue_(true),
prerolling_(true),
- weak_this_(this),
request_data_cb_(request_data_cb),
- access_unit_index_(0),
+ current_demuxer_data_index_(0),
input_buf_index_(-1),
stop_decode_pending_(false),
- destroy_pending_(false) {
+ destroy_pending_(false),
+ is_requesting_demuxer_data_(false),
+ is_incoming_data_invalid_(false),
+ weak_factory_(this) {
+ InitializeReceivedData();
}
MediaDecoderJob::~MediaDecoderJob() {}
@@ -44,23 +47,38 @@ MediaDecoderJob::~MediaDecoderJob() {}
void MediaDecoderJob::OnDataReceived(const DemuxerData& data) {
DVLOG(1) << __FUNCTION__ << ": " << data.access_units.size() << " units";
DCHECK(ui_task_runner_->BelongsToCurrentThread());
- DCHECK(!on_data_received_cb_.is_null());
+ DCHECK(NoAccessUnitsRemainingInChunk(false));
TRACE_EVENT_ASYNC_END2(
"media", "MediaDecoderJob::RequestData", this,
"Data type", data.type == media::DemuxerStream::AUDIO ? "AUDIO" : "VIDEO",
"Units read", data.access_units.size());
- base::Closure done_cb = base::ResetAndReturn(&on_data_received_cb_);
+ if (is_incoming_data_invalid_) {
+ is_incoming_data_invalid_ = false;
+
+ // If there is a pending callback, need to request the data again to get
+ // valid data.
+ if (!on_data_received_cb_.is_null())
+ request_data_cb_.Run();
+ else
+ is_requesting_demuxer_data_ = false;
+ return;
+ }
+ size_t next_demuxer_data_index = inactive_demuxer_data_index();
+ received_data_[next_demuxer_data_index] = data;
+ access_unit_index_[next_demuxer_data_index] = 0;
+ is_requesting_demuxer_data_ = false;
+
+ base::Closure done_cb = base::ResetAndReturn(&on_data_received_cb_);
if (stop_decode_pending_) {
OnDecodeCompleted(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0);
return;
}
- access_unit_index_ = 0;
- received_data_ = data;
- done_cb.Run();
+ if (!done_cb.is_null())
+ done_cb.Run();
}
void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) {
@@ -79,8 +97,8 @@ void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) {
}
bool MediaDecoderJob::Decode(
- const base::TimeTicks& start_time_ticks,
- const base::TimeDelta& start_presentation_timestamp,
+ base::TimeTicks start_time_ticks,
+ base::TimeDelta start_presentation_timestamp,
const DecoderCallback& callback) {
DCHECK(decode_cb_.is_null());
DCHECK(on_data_received_cb_.is_null());
@@ -89,23 +107,21 @@ bool MediaDecoderJob::Decode(
decode_cb_ = callback;
if (!HasData()) {
- RequestData(base::Bind(&MediaDecoderJob::DecodeNextAccessUnit,
+ RequestData(base::Bind(&MediaDecoderJob::DecodeCurrentAccessUnit,
base::Unretained(this),
start_time_ticks,
start_presentation_timestamp));
return true;
}
- if (DemuxerStream::kConfigChanged ==
- received_data_.access_units[access_unit_index_].status) {
+ if (DemuxerStream::kConfigChanged == CurrentAccessUnit().status) {
// Clear received data because we need to handle a config change.
decode_cb_.Reset();
- received_data_ = DemuxerData();
- access_unit_index_ = 0;
+ ClearData();
return false;
}
- DecodeNextAccessUnit(start_time_ticks, start_presentation_timestamp);
+ DecodeCurrentAccessUnit(start_time_ticks, start_presentation_timestamp);
return true;
}
@@ -120,14 +136,10 @@ void MediaDecoderJob::Flush() {
// Do nothing, flush when the next Decode() happens.
needs_flush_ = true;
- received_data_ = DemuxerData();
- input_eos_encountered_ = false;
- access_unit_index_ = 0;
- on_data_received_cb_.Reset();
+ ClearData();
}
-void MediaDecoderJob::BeginPrerolling(
- const base::TimeDelta& preroll_timestamp) {
+void MediaDecoderJob::BeginPrerolling(base::TimeDelta preroll_timestamp) {
DVLOG(1) << __FUNCTION__ << "(" << preroll_timestamp.InSecondsF() << ")";
DCHECK(ui_task_runner_->BelongsToCurrentThread());
DCHECK(!is_decoding());
@@ -208,16 +220,12 @@ MediaCodecStatus MediaDecoderJob::QueueInputBuffer(const AccessUnit& unit) {
bool MediaDecoderJob::HasData() const {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
- // When |input_eos_encountered_| is set, |access_units| must not be empty and
- // |access_unit_index_| must be pointing to an EOS unit. We'll reuse this
- // unit to flush the decoder until we hit output EOS.
- DCHECK(!input_eos_encountered_ ||
- (received_data_.access_units.size() > 0 &&
- access_unit_index_ < received_data_.access_units.size()))
- << " (access_units.size(): " << received_data_.access_units.size()
- << ", access_unit_index_: " << access_unit_index_ << ")";
- return access_unit_index_ < received_data_.access_units.size() ||
- input_eos_encountered_;
+ // When |input_eos_encountered_| is set, |access_unit_index_| and
+ // |current_demuxer_data_index_| must be pointing to an EOS unit.
+ // We'll reuse this unit to flush the decoder until we hit output EOS.
+ DCHECK(!input_eos_encountered_ || !NoAccessUnitsRemainingInChunk(true));
+ return !NoAccessUnitsRemainingInChunk(true) ||
+ !NoAccessUnitsRemainingInChunk(false);
}
void MediaDecoderJob::RequestData(const base::Closure& done_cb) {
@@ -225,26 +233,38 @@ void MediaDecoderJob::RequestData(const base::Closure& done_cb) {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
DCHECK(on_data_received_cb_.is_null());
DCHECK(!input_eos_encountered_);
+ DCHECK(NoAccessUnitsRemainingInChunk(false));
TRACE_EVENT_ASYNC_BEGIN0("media", "MediaDecoderJob::RequestData", this);
- received_data_ = DemuxerData();
- access_unit_index_ = 0;
on_data_received_cb_ = done_cb;
+ // If we are already expecting new data, just set the callback and do
+ // nothing.
+ if (is_requesting_demuxer_data_)
+ return;
+
+ // The new incoming data will be stored as the next demuxer data chunk, since
+ // the decoder might still be decoding the current one.
+ size_t next_demuxer_data_index = inactive_demuxer_data_index();
+ received_data_[next_demuxer_data_index] = DemuxerData();
+ access_unit_index_[next_demuxer_data_index] = 0;
+ is_requesting_demuxer_data_ = true;
+
request_data_cb_.Run();
}
-void MediaDecoderJob::DecodeNextAccessUnit(
- const base::TimeTicks& start_time_ticks,
- const base::TimeDelta& start_presentation_timestamp) {
+void MediaDecoderJob::DecodeCurrentAccessUnit(
+ base::TimeTicks start_time_ticks,
+ base::TimeDelta start_presentation_timestamp) {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
DCHECK(!decode_cb_.is_null());
+ RequestCurrentChunkIfEmpty();
+ const AccessUnit& access_unit = CurrentAccessUnit();
// If the first access unit is a config change, request the player to dequeue
// the input buffer again so that it can request config data.
- if (received_data_.access_units[access_unit_index_].status ==
- DemuxerStream::kConfigChanged) {
+ if (access_unit.status == DemuxerStream::kConfigChanged) {
ui_task_runner_->PostTask(FROM_HERE,
base::Bind(&MediaDecoderJob::OnDecodeCompleted,
base::Unretained(this),
@@ -256,7 +276,7 @@ void MediaDecoderJob::DecodeNextAccessUnit(
decoder_task_runner_->PostTask(FROM_HERE, base::Bind(
&MediaDecoderJob::DecodeInternal, base::Unretained(this),
- received_data_.access_units[access_unit_index_],
+ access_unit,
start_time_ticks, start_presentation_timestamp, needs_flush_,
media::BindToCurrentLoop(base::Bind(
&MediaDecoderJob::OnDecodeCompleted, base::Unretained(this)))));
@@ -265,8 +285,8 @@ void MediaDecoderJob::DecodeNextAccessUnit(
void MediaDecoderJob::DecodeInternal(
const AccessUnit& unit,
- const base::TimeTicks& start_time_ticks,
- const base::TimeDelta& start_presentation_timestamp,
+ base::TimeTicks start_time_ticks,
+ base::TimeDelta start_presentation_timestamp,
bool needs_flush,
const MediaDecoderJob::DecoderCallback& callback) {
DVLOG(1) << __FUNCTION__;
@@ -363,7 +383,10 @@ void MediaDecoderJob::DecodeInternal(
decoder_task_runner_->PostDelayedTask(
FROM_HERE,
base::Bind(&MediaDecoderJob::ReleaseOutputBuffer,
- weak_this_.GetWeakPtr(), buffer_index, size, render_output,
+ weak_factory_.GetWeakPtr(),
+ buffer_index,
+ size,
+ render_output,
base::Bind(callback, status, presentation_timestamp)),
time_to_render);
return;
@@ -388,7 +411,7 @@ void MediaDecoderJob::DecodeInternal(
}
void MediaDecoderJob::OnDecodeCompleted(
- MediaCodecStatus status, const base::TimeDelta& presentation_timestamp,
+ MediaCodecStatus status, base::TimeDelta presentation_timestamp,
size_t audio_output_bytes) {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
@@ -411,7 +434,7 @@ void MediaDecoderJob::OnDecodeCompleted(
case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED:
case MEDIA_CODEC_OUTPUT_END_OF_STREAM:
if (!input_eos_encountered_)
- access_unit_index_++;
+ access_unit_index_[current_demuxer_data_index_]++;
break;
case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER:
@@ -428,4 +451,54 @@ void MediaDecoderJob::OnDecodeCompleted(
audio_output_bytes);
}
+const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+ DCHECK(HasData());
+ int index = NoAccessUnitsRemainingInChunk(true) ?
+ inactive_demuxer_data_index() : current_demuxer_data_index_;
+ return received_data_[index].access_units[access_unit_index_[index]];
+}
+
+bool MediaDecoderJob::NoAccessUnitsRemainingInChunk(
+ bool is_active_chunk) const {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+ size_t index = is_active_chunk ? current_demuxer_data_index_ :
+ inactive_demuxer_data_index();
+ return received_data_[index].access_units.size() <= access_unit_index_[index];
+}
+
+void MediaDecoderJob::ClearData() {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+ current_demuxer_data_index_ = 0;
+ InitializeReceivedData();
+ on_data_received_cb_.Reset();
+ if (is_requesting_demuxer_data_)
+ is_incoming_data_invalid_ = true;
+ input_eos_encountered_ = false;
+}
+
+void MediaDecoderJob::RequestCurrentChunkIfEmpty() {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+ DCHECK(HasData());
+ if (!NoAccessUnitsRemainingInChunk(true))
+ return;
+
+ // Requests new data if the the last access unit of the next chunk is not EOS.
+ current_demuxer_data_index_ = inactive_demuxer_data_index();
+ const AccessUnit last_access_unit =
+ received_data_[current_demuxer_data_index_].access_units.back();
+ if (!last_access_unit.end_of_stream &&
+ last_access_unit.status != DemuxerStream::kConfigChanged &&
+ last_access_unit.status != DemuxerStream::kAborted) {
+ RequestData(base::Closure());
+ }
+}
+
+void MediaDecoderJob::InitializeReceivedData() {
+ for (size_t i = 0; i < 2; ++i) {
+ received_data_[i] = DemuxerData();
+ access_unit_index_[i] = 0;
+ }
+}
+
} // namespace media
diff --git a/media/base/android/media_decoder_job.h b/media/base/android/media_decoder_job.h
index 286d27cf75..77caa9feec 100644
--- a/media/base/android/media_decoder_job.h
+++ b/media/base/android/media_decoder_job.h
@@ -19,6 +19,9 @@ namespace media {
// Class for managing all the decoding tasks. Each decoding task will be posted
// onto the same thread. The thread will be stopped once Stop() is called.
+// Data is stored in 2 chunks. When new data arrives, it is always stored in
+// an inactive chunk. And when the current active chunk becomes empty, a new
+// data request will be sent to the renderer.
class MediaDecoderJob {
public:
struct Deleter {
@@ -30,7 +33,7 @@ class MediaDecoderJob {
// If the presentation time is equal to kNoTimestamp(), the decoder job
// skipped rendering of the decoded output and the callback target should
// update its clock to avoid introducing extra delays to the next frame.
- typedef base::Callback<void(MediaCodecStatus, const base::TimeDelta&,
+ typedef base::Callback<void(MediaCodecStatus, base::TimeDelta,
size_t)> DecoderCallback;
// Callback when a decoder job finishes releasing the output buffer.
// Args: audio output bytes, must be 0 for video.
@@ -52,8 +55,8 @@ class MediaDecoderJob {
// called when the decode operation is complete.
// Returns false if a config change is needed. |callback| is ignored
// and will not be called.
- bool Decode(const base::TimeTicks& start_time_ticks,
- const base::TimeDelta& start_presentation_timestamp,
+ bool Decode(base::TimeTicks start_time_ticks,
+ base::TimeDelta start_presentation_timestamp,
const DecoderCallback& callback);
// Called to stop the last Decode() early.
@@ -70,12 +73,16 @@ class MediaDecoderJob {
void Flush();
// Enter prerolling state. The job must not currently be decoding.
- void BeginPrerolling(const base::TimeDelta& preroll_timestamp);
+ void BeginPrerolling(base::TimeDelta preroll_timestamp);
bool prerolling() const { return prerolling_; }
bool is_decoding() const { return !decode_cb_.is_null(); }
+ bool is_requesting_demuxer_data() const {
+ return is_requesting_demuxer_data_;
+ }
+
protected:
MediaDecoderJob(
const scoped_refptr<base::SingleThreadTaskRunner>& decoder_task_runner,
@@ -95,6 +102,8 @@ class MediaDecoderJob {
virtual bool ComputeTimeToRender() const = 0;
private:
+ friend class MediaSourcePlayerTest;
+
// Causes this instance to be deleted on the thread it is bound to.
void Release();
@@ -107,10 +116,10 @@ class MediaDecoderJob {
// |done_cb| is called when more data is available in |received_data_|.
void RequestData(const base::Closure& done_cb);
- // Posts a task to start decoding the next access unit in |received_data_|.
- void DecodeNextAccessUnit(
- const base::TimeTicks& start_time_ticks,
- const base::TimeDelta& start_presentation_timestamp);
+ // Posts a task to start decoding the current access unit in |received_data_|.
+ void DecodeCurrentAccessUnit(
+ base::TimeTicks start_time_ticks,
+ base::TimeDelta start_presentation_timestamp);
// Helper function to decoder data on |thread_|. |unit| contains all the data
// to be decoded. |start_time_ticks| and |start_presentation_timestamp|
@@ -119,8 +128,8 @@ class MediaDecoderJob {
// frame should be rendered. If |needs_flush| is true, codec needs to be
// flushed at the beginning of this call.
void DecodeInternal(const AccessUnit& unit,
- const base::TimeTicks& start_time_ticks,
- const base::TimeDelta& start_presentation_timestamp,
+ base::TimeTicks start_time_ticks,
+ base::TimeDelta start_presentation_timestamp,
bool needs_flush,
const DecoderCallback& callback);
@@ -128,9 +137,33 @@ class MediaDecoderJob {
// Completes any pending job destruction or any pending decode stop. If
// destruction was not pending, passes its arguments to |decode_cb_|.
void OnDecodeCompleted(MediaCodecStatus status,
- const base::TimeDelta& presentation_timestamp,
+ base::TimeDelta presentation_timestamp,
size_t audio_output_bytes);
+ // Helper function to get the current access unit that is being decoded.
+ const AccessUnit& CurrentAccessUnit() const;
+
+ // Check whether a chunk has no remaining access units to decode. If
+ // |is_active_chunk| is true, this function returns whether decoder has
+ // consumed all data in |received_data_[current_demuxer_data_index_]|.
+ // Otherwise, it returns whether decoder has consumed all data in the inactive
+ // chunk.
+ bool NoAccessUnitsRemainingInChunk(bool is_active_chunk) const;
+
+ // Clearn all the received data.
+ void ClearData();
+
+ // Request new data for the current chunk if it runs out of data.
+ void RequestCurrentChunkIfEmpty();
+
+ // Initialize |received_data_| and |access_unit_index_|.
+ void InitializeReceivedData();
+
+ // Return the index to |received_data_| that is not currently being decoded.
+ size_t inactive_demuxer_data_index() const {
+ return 1 - current_demuxer_data_index_;
+ }
+
// The UI message loop where callbacks should be dispatched.
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
@@ -170,10 +203,6 @@ class MediaDecoderJob {
// is not very accurate.
bool prerolling_;
- // Weak pointer passed to media decoder jobs for callbacks. It is bounded to
- // the decoder thread.
- base::WeakPtrFactory<MediaDecoderJob> weak_this_;
-
// Callback used to request more data.
base::Closure request_data_cb_;
@@ -183,11 +212,19 @@ class MediaDecoderJob {
// Callback to run when the current Decode() operation completes.
DecoderCallback decode_cb_;
- // The current access unit being processed.
- size_t access_unit_index_;
-
// Data received over IPC from last RequestData() operation.
- DemuxerData received_data_;
+ // We keep 2 chunks at the same time to reduce the IPC latency between chunks.
+ // If data inside the current chunk are all decoded, we will request a new
+ // chunk from the demuxer and swap the current chunk with the other one.
+ // New data will always be stored in the other chunk since the current
+ // one may be still in use.
+ DemuxerData received_data_[2];
+
+ // Index to the current data chunk that is being decoded.
+ size_t current_demuxer_data_index_;
+
+ // Index to the access unit inside each data chunk that is being decoded.
+ size_t access_unit_index_[2];
// The index of input buffer that can be used by QueueInputBuffer().
// If the index is uninitialized or invalid, it must be -1.
@@ -200,6 +237,17 @@ class MediaDecoderJob {
// while there is a decode in progress.
bool destroy_pending_;
+ // Indicates whether the decoder is in the middle of requesting new data.
+ bool is_requesting_demuxer_data_;
+
+ // Indicates whether the incoming data should be ignored.
+ bool is_incoming_data_invalid_;
+
+ // Weak pointer passed to media decoder jobs for callbacks. It is bounded to
+ // the decoder thread.
+ // NOTE: Weak pointers must be invalidated before all other member variables.
+ base::WeakPtrFactory<MediaDecoderJob> weak_factory_;
+
DISALLOW_IMPLICIT_CONSTRUCTORS(MediaDecoderJob);
};
diff --git a/media/base/android/media_player_android.h b/media/base/android/media_player_android.h
index c7e17447eb..2e5e3715cb 100644
--- a/media/base/android/media_player_android.h
+++ b/media/base/android/media_player_android.h
@@ -52,7 +52,7 @@ class MEDIA_EXPORT MediaPlayerAndroid {
// Seek to a particular position, based on renderer signaling actual seek
// with MediaPlayerHostMsg_Seek. If eventual success, OnSeekComplete() will be
// called.
- virtual void SeekTo(const base::TimeDelta& timestamp) = 0;
+ virtual void SeekTo(base::TimeDelta timestamp) = 0;
// Release the player resources.
virtual void Release() = 0;
diff --git a/media/base/android/media_player_bridge.cc b/media/base/android/media_player_bridge.cc
index 125c6bf999..b1bb3fec3c 100644
--- a/media/base/android/media_player_bridge.cc
+++ b/media/base/android/media_player_bridge.cc
@@ -34,7 +34,9 @@ MediaPlayerBridge::MediaPlayerBridge(
MediaPlayerManager* manager,
const RequestMediaResourcesCB& request_media_resources_cb,
const ReleaseMediaResourcesCB& release_media_resources_cb)
- : MediaPlayerAndroid(player_id, manager, request_media_resources_cb,
+ : MediaPlayerAndroid(player_id,
+ manager,
+ request_media_resources_cb,
release_media_resources_cb),
prepared_(false),
pending_play_(false),
@@ -47,10 +49,10 @@ MediaPlayerBridge::MediaPlayerBridge(
can_pause_(true),
can_seek_forward_(true),
can_seek_backward_(true),
- weak_this_(this),
- listener_(base::MessageLoopProxy::current(),
- weak_this_.GetWeakPtr()),
- is_surface_in_use_(false) {
+ is_surface_in_use_(false),
+ weak_factory_(this) {
+ listener_.reset(new MediaPlayerListener(base::MessageLoopProxy::current(),
+ weak_factory_.GetWeakPtr()));
}
MediaPlayerBridge::~MediaPlayerBridge() {
@@ -72,13 +74,17 @@ void MediaPlayerBridge::Initialize() {
media::MediaResourceGetter* resource_getter =
manager()->GetMediaResourceGetter();
if (url_.SchemeIsFileSystem() || url_.SchemeIs(kBlobScheme)) {
- resource_getter->GetPlatformPathFromURL(url_, base::Bind(
- &MediaPlayerBridge::ExtractMediaMetadata, weak_this_.GetWeakPtr()));
+ resource_getter->GetPlatformPathFromURL(
+ url_,
+ base::Bind(&MediaPlayerBridge::ExtractMediaMetadata,
+ weak_factory_.GetWeakPtr()));
return;
}
- resource_getter->GetCookies(url_, first_party_for_cookies_, base::Bind(
- &MediaPlayerBridge::OnCookiesRetrieved, weak_this_.GetWeakPtr()));
+ resource_getter->GetCookies(url_,
+ first_party_for_cookies_,
+ base::Bind(&MediaPlayerBridge::OnCookiesRetrieved,
+ weak_factory_.GetWeakPtr()));
}
void MediaPlayerBridge::CreateJavaMediaPlayerBridge() {
@@ -110,7 +116,7 @@ void MediaPlayerBridge::SetMediaPlayerListener() {
jobject j_context = base::android::GetApplicationContext();
DCHECK(j_context);
- listener_.CreateMediaPlayerListener(j_context, j_media_player_bridge_.obj());
+ listener_->CreateMediaPlayerListener(j_context, j_media_player_bridge_.obj());
}
void MediaPlayerBridge::SetDuration(base::TimeDelta duration) {
@@ -136,8 +142,9 @@ void MediaPlayerBridge::Prepare() {
CreateJavaMediaPlayerBridge();
if (url_.SchemeIsFileSystem() || url_.SchemeIs(kBlobScheme)) {
manager()->GetMediaResourceGetter()->GetPlatformPathFromURL(
- url_, base::Bind(&MediaPlayerBridge::SetDataSource,
- weak_this_.GetWeakPtr()));
+ url_,
+ base::Bind(&MediaPlayerBridge::SetDataSource,
+ weak_factory_.GetWeakPtr()));
return;
}
@@ -201,9 +208,11 @@ void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) {
void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) {
manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
- url, cookies_, user_agent_,
+ url,
+ cookies_,
+ user_agent_,
base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
- weak_this_.GetWeakPtr()));
+ weak_factory_.GetWeakPtr()));
}
void MediaPlayerBridge::OnMediaMetadataExtracted(
@@ -267,7 +276,7 @@ int MediaPlayerBridge::GetVideoHeight() {
env, j_media_player_bridge_.obj());
}
-void MediaPlayerBridge::SeekTo(const base::TimeDelta& timestamp) {
+void MediaPlayerBridge::SeekTo(base::TimeDelta timestamp) {
// Record the time to seek when OnMediaPrepared() is called.
pending_seek_ = timestamp;
@@ -310,7 +319,7 @@ void MediaPlayerBridge::Release() {
Java_MediaPlayerBridge_release(env, j_media_player_bridge_.obj());
j_media_player_bridge_.Reset();
release_media_resources_cb_.Run(player_id());
- listener_.ReleaseMediaPlayerListenerResources();
+ listener_->ReleaseMediaPlayerListenerResources();
}
void MediaPlayerBridge::SetVolume(double volume) {
diff --git a/media/base/android/media_player_bridge.h b/media/base/android/media_player_bridge.h
index 81e4511f89..b0620b659a 100644
--- a/media/base/android/media_player_bridge.h
+++ b/media/base/android/media_player_bridge.h
@@ -59,7 +59,7 @@ class MEDIA_EXPORT MediaPlayerBridge : public MediaPlayerAndroid {
virtual void SetVideoSurface(gfx::ScopedJavaSurface surface) OVERRIDE;
virtual void Start() OVERRIDE;
virtual void Pause(bool is_media_related_action ALLOW_UNUSED) OVERRIDE;
- virtual void SeekTo(const base::TimeDelta& timestamp) OVERRIDE;
+ virtual void SeekTo(base::TimeDelta timestamp) OVERRIDE;
virtual void Release() OVERRIDE;
virtual void SetVolume(double volume) OVERRIDE;
virtual int GetVideoWidth() OVERRIDE;
@@ -105,6 +105,8 @@ class MEDIA_EXPORT MediaPlayerBridge : public MediaPlayerAndroid {
virtual base::android::ScopedJavaLocalRef<jobject> GetAllowedOperations();
private:
+ friend class MediaPlayerListener;
+
// Set the data source for the media player.
void SetDataSource(const std::string& url);
@@ -168,16 +170,16 @@ class MEDIA_EXPORT MediaPlayerBridge : public MediaPlayerAndroid {
base::RepeatingTimer<MediaPlayerBridge> time_update_timer_;
- // Weak pointer passed to |listener_| for callbacks.
- base::WeakPtrFactory<MediaPlayerBridge> weak_this_;
-
// Listener object that listens to all the media player events.
- MediaPlayerListener listener_;
+ scoped_ptr<MediaPlayerListener> listener_;
// Whether player is currently using a surface.
bool is_surface_in_use_;
- friend class MediaPlayerListener;
+ // Weak pointer passed to |listener_| for callbacks.
+ // NOTE: Weak pointers must be invalidated before all other member variables.
+ base::WeakPtrFactory<MediaPlayerBridge> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(MediaPlayerBridge);
};
diff --git a/media/base/android/media_source_player.cc b/media/base/android/media_source_player.cc
index 584d50b238..e52d0e49fc 100644
--- a/media/base/android/media_source_player.cc
+++ b/media/base/android/media_source_player.cc
@@ -37,7 +37,9 @@ MediaSourcePlayer::MediaSourcePlayer(
const RequestMediaResourcesCB& request_media_resources_cb,
const ReleaseMediaResourcesCB& release_media_resources_cb,
scoped_ptr<DemuxerAndroid> demuxer)
- : MediaPlayerAndroid(player_id, manager, request_media_resources_cb,
+ : MediaPlayerAndroid(player_id,
+ manager,
+ request_media_resources_cb,
release_media_resources_cb),
demuxer_(demuxer.Pass()),
pending_event_(NO_EVENT_PENDING),
@@ -59,9 +61,11 @@ MediaSourcePlayer::MediaSourcePlayer(
pending_seek_(false),
reconfig_audio_decoder_(false),
reconfig_video_decoder_(false),
- weak_this_(this),
drm_bridge_(NULL),
- is_waiting_for_key_(false) {
+ is_waiting_for_key_(false),
+ has_pending_audio_data_request_(false),
+ has_pending_video_data_request_(false),
+ weak_factory_(this) {
demuxer_->Initialize(this);
clock_.SetMaxTime(base::TimeDelta());
}
@@ -106,7 +110,7 @@ void MediaSourcePlayer::SetVideoSurface(gfx::ScopedJavaSurface surface) {
}
void MediaSourcePlayer::ScheduleSeekEventAndStopDecoding(
- const base::TimeDelta& seek_time) {
+ base::TimeDelta seek_time) {
DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ")";
DCHECK(!IsEventPending(SEEK_EVENT_PENDING));
@@ -177,7 +181,7 @@ int MediaSourcePlayer::GetVideoHeight() {
return height_;
}
-void MediaSourcePlayer::SeekTo(const base::TimeDelta& timestamp) {
+void MediaSourcePlayer::SeekTo(base::TimeDelta timestamp) {
DVLOG(1) << __FUNCTION__ << "(" << timestamp.InSecondsF() << ")";
if (IsEventPending(SEEK_EVENT_PENDING)) {
@@ -222,7 +226,7 @@ void MediaSourcePlayer::Release() {
// Clear all the pending events except seeks and config changes.
pending_event_ &= (SEEK_EVENT_PENDING | CONFIG_CHANGE_EVENT_PENDING);
is_surface_in_use_ = false;
- audio_decoder_job_.reset();
+ ResetAudioDecoderJob();
ResetVideoDecoderJob();
// Prevent job re-creation attempts in OnDemuxerConfigsAvailable()
@@ -345,6 +349,20 @@ void MediaSourcePlayer::OnDemuxerConfigsAvailable(
void MediaSourcePlayer::OnDemuxerDataAvailable(const DemuxerData& data) {
DVLOG(1) << __FUNCTION__ << "(" << data.type << ")";
DCHECK_LT(0u, data.access_units.size());
+
+ if (has_pending_audio_data_request_ && data.type == DemuxerStream::AUDIO) {
+ has_pending_audio_data_request_ = false;
+ ProcessPendingEvents();
+ return;
+ }
+
+ if (has_pending_video_data_request_ && data.type == DemuxerStream::VIDEO) {
+ next_video_data_is_iframe_ = false;
+ has_pending_video_data_request_ = false;
+ ProcessPendingEvents();
+ return;
+ }
+
if (data.type == DemuxerStream::AUDIO && audio_decoder_job_) {
audio_decoder_job_->OnDataReceived(data);
} else if (data.type == DemuxerStream::VIDEO) {
@@ -388,7 +406,7 @@ void MediaSourcePlayer::SetDrmBridge(MediaDrmBridge* drm_bridge) {
if (drm_bridge_->GetMediaCrypto().is_null()) {
drm_bridge_->SetMediaCryptoReadyCB(base::Bind(
- &MediaSourcePlayer::OnMediaCryptoReady, weak_this_.GetWeakPtr()));
+ &MediaSourcePlayer::OnMediaCryptoReady, weak_factory_.GetWeakPtr()));
return;
}
@@ -397,7 +415,7 @@ void MediaSourcePlayer::SetDrmBridge(MediaDrmBridge* drm_bridge) {
}
void MediaSourcePlayer::OnDemuxerSeekDone(
- const base::TimeDelta& actual_browser_seek_time) {
+ base::TimeDelta actual_browser_seek_time) {
DVLOG(1) << __FUNCTION__;
ClearPendingEvent(SEEK_EVENT_PENDING);
@@ -419,14 +437,17 @@ void MediaSourcePlayer::OnDemuxerSeekDone(
// player clock to the actual seek target.
if (doing_browser_seek_) {
DCHECK(actual_browser_seek_time != kNoTimestamp());
+ base::TimeDelta seek_time = actual_browser_seek_time;
// A browser seek must not jump into the past. Ideally, it seeks to the
// requested time, but it might jump into the future.
- DCHECK(actual_browser_seek_time >= GetCurrentTime());
+ DCHECK(seek_time >= GetCurrentTime());
DVLOG(1) << __FUNCTION__ << " : setting clock to actual browser seek time: "
- << actual_browser_seek_time.InSecondsF();
- clock_.SetTime(actual_browser_seek_time, actual_browser_seek_time);
+ << seek_time.InSecondsF();
+ clock_.SetTime(seek_time, seek_time);
if (audio_timestamp_helper_)
- audio_timestamp_helper_->SetBaseTimestamp(actual_browser_seek_time);
+ audio_timestamp_helper_->SetBaseTimestamp(seek_time);
+ } else {
+ DCHECK(actual_browser_seek_time == kNoTimestamp());
}
reached_audio_eos_ = false;
@@ -450,7 +471,7 @@ void MediaSourcePlayer::OnDemuxerSeekDone(
}
void MediaSourcePlayer::UpdateTimestamps(
- const base::TimeDelta& presentation_timestamp, size_t audio_output_bytes) {
+ base::TimeDelta presentation_timestamp, size_t audio_output_bytes) {
base::TimeDelta new_max_time = presentation_timestamp;
if (audio_output_bytes > 0) {
@@ -476,6 +497,11 @@ void MediaSourcePlayer::ProcessPendingEvents() {
return;
}
+ if (has_pending_audio_data_request_ || has_pending_video_data_request_) {
+ DVLOG(1) << __FUNCTION__ << " : has pending data request.";
+ return;
+ }
+
if (IsEventPending(PREFETCH_DONE_EVENT_PENDING)) {
DVLOG(1) << __FUNCTION__ << " : PREFETCH_DONE still pending.";
return;
@@ -510,6 +536,14 @@ void MediaSourcePlayer::ProcessPendingEvents() {
if (IsEventPending(PREFETCH_REQUEST_EVENT_PENDING)) {
DVLOG(1) << __FUNCTION__ << " : Handling PREFETCH_REQUEST_EVENT.";
+ // If one of the decoder is not initialized, cancel this event as it will be
+ // called later when Start() is called again.
+ if ((HasVideo() && !video_decoder_job_) ||
+ (HasAudio() && !audio_decoder_job_)) {
+ ClearPendingEvent(PREFETCH_REQUEST_EVENT_PENDING);
+ return;
+ }
+
DCHECK(audio_decoder_job_ || AudioFinished());
DCHECK(video_decoder_job_ || VideoFinished());
@@ -523,8 +557,10 @@ void MediaSourcePlayer::ProcessPendingEvents() {
return;
SetPendingEvent(PREFETCH_DONE_EVENT_PENDING);
- base::Closure barrier = BarrierClosure(count, base::Bind(
- &MediaSourcePlayer::OnPrefetchDone, weak_this_.GetWeakPtr()));
+ base::Closure barrier =
+ BarrierClosure(count,
+ base::Bind(&MediaSourcePlayer::OnPrefetchDone,
+ weak_factory_.GetWeakPtr()));
if (!AudioFinished())
audio_decoder_job_->Prefetch(barrier);
@@ -545,7 +581,7 @@ void MediaSourcePlayer::ProcessPendingEvents() {
void MediaSourcePlayer::MediaDecoderCallback(
bool is_audio, MediaCodecStatus status,
- const base::TimeDelta& presentation_timestamp, size_t audio_output_bytes) {
+ base::TimeDelta presentation_timestamp, size_t audio_output_bytes) {
DVLOG(1) << __FUNCTION__ << ": " << is_audio << ", " << status;
// TODO(xhwang): Drop IntToString() when http://crbug.com/303899 is fixed.
@@ -646,9 +682,11 @@ void MediaSourcePlayer::DecodeMoreAudio() {
DCHECK(!AudioFinished());
if (audio_decoder_job_->Decode(
- start_time_ticks_, start_presentation_timestamp_, base::Bind(
- &MediaSourcePlayer::MediaDecoderCallback,
- weak_this_.GetWeakPtr(), true))) {
+ start_time_ticks_,
+ start_presentation_timestamp_,
+ base::Bind(&MediaSourcePlayer::MediaDecoderCallback,
+ weak_factory_.GetWeakPtr(),
+ true))) {
TRACE_EVENT_ASYNC_BEGIN0("media", "MediaSourcePlayer::DecodeMoreAudio",
audio_decoder_job_.get());
return;
@@ -676,9 +714,11 @@ void MediaSourcePlayer::DecodeMoreVideo() {
DCHECK(!VideoFinished());
if (video_decoder_job_->Decode(
- start_time_ticks_, start_presentation_timestamp_, base::Bind(
- &MediaSourcePlayer::MediaDecoderCallback,
- weak_this_.GetWeakPtr(), false))) {
+ start_time_ticks_,
+ start_presentation_timestamp_,
+ base::Bind(&MediaSourcePlayer::MediaDecoderCallback,
+ weak_factory_.GetWeakPtr(),
+ false))) {
TRACE_EVENT_ASYNC_BEGIN0("media", "MediaSourcePlayer::DecodeMoreVideo",
video_decoder_job_.get());
return;
@@ -747,7 +787,7 @@ bool MediaSourcePlayer::VideoFinished() {
void MediaSourcePlayer::ConfigureAudioDecoderJob() {
if (!HasAudio()) {
- audio_decoder_job_.reset();
+ ResetAudioDecoderJob();
return;
}
@@ -761,8 +801,8 @@ void MediaSourcePlayer::ConfigureAudioDecoderJob() {
DCHECK(!audio_decoder_job_ || !audio_decoder_job_->is_decoding());
+ ResetAudioDecoderJob();
DVLOG(1) << __FUNCTION__ << " : creating new audio decoder job";
-
audio_decoder_job_.reset(AudioDecoderJob::Create(
audio_codec_, sampling_rate_, num_channels_, &audio_extra_data_[0],
audio_extra_data_.size(), media_crypto.obj(),
@@ -777,6 +817,10 @@ void MediaSourcePlayer::ConfigureAudioDecoderJob() {
}
void MediaSourcePlayer::ResetVideoDecoderJob() {
+ if (video_decoder_job_) {
+ has_pending_video_data_request_ =
+ video_decoder_job_->is_requesting_demuxer_data();
+ }
video_decoder_job_.reset();
// Any eventual video decoder job re-creation will use the current |surface_|.
@@ -784,6 +828,14 @@ void MediaSourcePlayer::ResetVideoDecoderJob() {
ClearPendingEvent(SURFACE_CHANGE_EVENT_PENDING);
}
+void MediaSourcePlayer::ResetAudioDecoderJob() {
+ if (audio_decoder_job_) {
+ has_pending_audio_data_request_ =
+ audio_decoder_job_->is_requesting_demuxer_data();
+ }
+ audio_decoder_job_.reset();
+}
+
void MediaSourcePlayer::ConfigureVideoDecoderJob() {
if (!HasVideo() || surface_.IsEmpty()) {
ResetVideoDecoderJob();
@@ -860,7 +912,7 @@ void MediaSourcePlayer::OnDecoderStarved() {
}
void MediaSourcePlayer::StartStarvationCallback(
- const base::TimeDelta& presentation_timestamp) {
+ base::TimeDelta presentation_timestamp) {
// 20ms was chosen because it is the typical size of a compressed audio frame.
// Anything smaller than this would likely cause unnecessary cycling in and
// out of the prefetch state.
@@ -884,9 +936,8 @@ void MediaSourcePlayer::StartStarvationCallback(
timeout = std::max(timeout, kMinStarvationTimeout);
- decoder_starvation_callback_.Reset(
- base::Bind(&MediaSourcePlayer::OnDecoderStarved,
- weak_this_.GetWeakPtr()));
+ decoder_starvation_callback_.Reset(base::Bind(
+ &MediaSourcePlayer::OnDecoderStarved, weak_factory_.GetWeakPtr()));
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE, decoder_starvation_callback_.callback(), timeout);
}
diff --git a/media/base/android/media_source_player.h b/media/base/android/media_source_player.h
index b3af1be238..ed0483cd83 100644
--- a/media/base/android/media_source_player.h
+++ b/media/base/android/media_source_player.h
@@ -50,7 +50,7 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid,
virtual void SetVideoSurface(gfx::ScopedJavaSurface surface) OVERRIDE;
virtual void Start() OVERRIDE;
virtual void Pause(bool is_media_related_action ALLOW_UNUSED) OVERRIDE;
- virtual void SeekTo(const base::TimeDelta& timestamp) OVERRIDE;
+ virtual void SeekTo(base::TimeDelta timestamp) OVERRIDE;
virtual void Release() OVERRIDE;
virtual void SetVolume(double volume) OVERRIDE;
virtual int GetVideoWidth() OVERRIDE;
@@ -70,12 +70,14 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid,
virtual void OnDemuxerConfigsAvailable(const DemuxerConfigs& params) OVERRIDE;
virtual void OnDemuxerDataAvailable(const DemuxerData& params) OVERRIDE;
virtual void OnDemuxerSeekDone(
- const base::TimeDelta& actual_browser_seek_time) OVERRIDE;
+ base::TimeDelta actual_browser_seek_time) OVERRIDE;
virtual void OnDemuxerDurationChanged(base::TimeDelta duration) OVERRIDE;
private:
+ friend class MediaSourcePlayerTest;
+
// Update the current timestamp.
- void UpdateTimestamps(const base::TimeDelta& presentation_timestamp,
+ void UpdateTimestamps(base::TimeDelta presentation_timestamp,
size_t audio_output_bytes);
// Helper function for starting media playback.
@@ -87,7 +89,7 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid,
// Called when the decoder finishes its task.
void MediaDecoderCallback(
bool is_audio, MediaCodecStatus status,
- const base::TimeDelta& presentation_timestamp,
+ base::TimeDelta presentation_timestamp,
size_t audio_output_bytes);
// Gets MediaCrypto object from |drm_bridge_|.
@@ -102,6 +104,7 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid,
// Helper method to clear any pending |SURFACE_CHANGE_EVENT_PENDING|
// and reset |video_decoder_job_| to null.
void ResetVideoDecoderJob();
+ void ResetAudioDecoderJob();
// Helper methods to configure the decoder jobs.
void ConfigureVideoDecoderJob();
@@ -133,13 +136,13 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid,
// |presentation_timestamp| - The presentation timestamp used for starvation
// timeout computations. It represents the timestamp of the last piece of
// decoded data.
- void StartStarvationCallback(const base::TimeDelta& presentation_timestamp);
+ void StartStarvationCallback(base::TimeDelta presentation_timestamp);
// Schedules a seek event in |pending_events_| and calls StopDecode() on all
// the MediaDecoderJobs. Sets clock to |seek_time|, and resets
// |pending_seek_|. There must not already be a seek event in
// |pending_events_|.
- void ScheduleSeekEventAndStopDecoding(const base::TimeDelta& seek_time);
+ void ScheduleSeekEventAndStopDecoding(base::TimeDelta seek_time);
// Schedules a browser seek event. We must not currently be processing any
// seek. Note that there is possibility that browser seek of renderer demuxer
@@ -266,9 +269,6 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid,
// Object to calculate the current audio timestamp for A/V sync.
scoped_ptr<AudioTimestampHelper> audio_timestamp_helper_;
- // Weak pointer passed to media decoder jobs for callbacks.
- base::WeakPtrFactory<MediaSourcePlayer> weak_this_;
-
MediaDrmBridge* drm_bridge_;
// No decryption key available to decrypt the encrypted buffer. In this case,
@@ -282,7 +282,14 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid,
// Whether |surface_| is currently used by the player.
bool is_surface_in_use_;
- friend class MediaSourcePlayerTest;
+ // Whether there are pending data requests by the decoder.
+ bool has_pending_audio_data_request_;
+ bool has_pending_video_data_request_;
+
+ // Weak pointer passed to media decoder jobs for callbacks.
+ // NOTE: Weak pointers must be invalidated before all other member variables.
+ base::WeakPtrFactory<MediaSourcePlayer> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(MediaSourcePlayer);
};
diff --git a/media/base/android/media_source_player_unittest.cc b/media/base/android/media_source_player_unittest.cc
index 9cb04b1829..a3e69e5bd3 100644
--- a/media/base/android/media_source_player_unittest.cc
+++ b/media/base/android/media_source_player_unittest.cc
@@ -408,10 +408,12 @@ class MediaSourcePlayerTest : public testing::Test {
StartAudioDecoderJob(true);
EXPECT_FALSE(GetMediaDecoderJob(true)->is_decoding());
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(0));
+ EXPECT_EQ(2, demuxer_->num_data_requests());
EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
player_.SeekTo(seek_time);
EXPECT_EQ(0.0, GetPrerollTimestamp().InMillisecondsF());
EXPECT_EQ(0, demuxer_->num_seek_requests());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(0));
}
// Seek, including simulated receipt of |kAborted| read between SeekTo() and
@@ -474,7 +476,7 @@ class MediaSourcePlayerTest : public testing::Test {
EXPECT_TRUE(GetMediaDecoderJob(is_audio)->is_decoding());
EXPECT_EQ(target_timestamp, player_.GetCurrentTime());
current_timestamp += 30;
- message_loop_.Run();
+ WaitForDecodeDone(is_audio, !is_audio);
}
EXPECT_LE(target_timestamp, player_.GetCurrentTime());
}
@@ -500,7 +502,8 @@ class MediaSourcePlayerTest : public testing::Test {
// browser seek results once decode completes and surface change processing
// begins.
void BrowserSeekPlayer(bool trigger_with_release_start) {
- int expected_num_data_requests = demuxer_->num_data_requests() + 1;
+ int expected_num_data_requests = demuxer_->num_data_requests() +
+ (trigger_with_release_start ? 1 : 2);
int expected_num_seek_requests = demuxer_->num_seek_requests();
int expected_num_browser_seek_requests =
demuxer_->num_browser_seek_requests();
@@ -512,7 +515,8 @@ class MediaSourcePlayerTest : public testing::Test {
if (trigger_with_release_start) {
ReleasePlayer();
- // Simulate demuxer's response to the video data request.
+ // Simulate demuxer's response to the video data request. The data will be
+ // discarded.
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
EXPECT_FALSE(GetMediaDecoderJob(false));
EXPECT_FALSE(player_.IsPlaying());
@@ -520,6 +524,7 @@ class MediaSourcePlayerTest : public testing::Test {
CreateNextTextureAndSetVideoSurface();
StartVideoDecoderJob(false);
+ EXPECT_FALSE(GetMediaDecoderJob(false));
} else {
// Simulate demuxer's response to the video data request.
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
@@ -537,8 +542,8 @@ class MediaSourcePlayerTest : public testing::Test {
// Wait for the decoder job to finish decoding and be reset pending the
// browser seek.
- while (GetMediaDecoderJob(false))
- message_loop_.RunUntilIdle();
+ WaitForVideoDecodeDone();
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
}
// Only one browser seek should have been initiated, and no further data
@@ -579,7 +584,7 @@ class MediaSourcePlayerTest : public testing::Test {
else
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
- message_loop_.Run();
+ WaitForDecodeDone(is_audio, !is_audio);
// We should have completed the prefetch phase at this point.
expected_num_data_requests++;
@@ -620,10 +625,11 @@ class MediaSourcePlayerTest : public testing::Test {
// assumed to exist for any stream whose decode completion is awaited.
void WaitForDecodeDone(bool wait_for_audio, bool wait_for_video) {
DCHECK(wait_for_audio || wait_for_video);
-
while ((wait_for_audio && GetMediaDecoderJob(true) &&
+ GetMediaDecoderJob(true)->HasData() &&
GetMediaDecoderJob(true)->is_decoding()) ||
(wait_for_video && GetMediaDecoderJob(false) &&
+ GetMediaDecoderJob(false)->HasData() &&
GetMediaDecoderJob(false)->is_decoding())) {
message_loop_.RunUntilIdle();
}
@@ -694,8 +700,7 @@ class MediaSourcePlayerTest : public testing::Test {
// media types configured. Since prefetching may be in progress, we cannot
// reliably expect Run() to complete until we have sent demuxer data for all
// configured media types, above.
- for (int i = 0; i < (have_audio ? 1 : 0) + (have_video ? 1 : 0); i++)
- message_loop_.Run();
+ WaitForDecodeDone(have_audio, have_video);
// Simulate seek while decoding EOS or non-EOS for the appropriate
// stream(s).
@@ -878,20 +883,20 @@ TEST_F(MediaSourcePlayerTest, ChangeMultipleSurfaceWhileDecoding) {
// Wait for the decoder job to finish decoding and be reset pending a browser
// seek.
- while (GetMediaDecoderJob(false))
- message_loop_.RunUntilIdle();
+ WaitForVideoDecodeDone();
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
// Only one browser seek should have been initiated. No further data request
// should have been processed on |message_loop_| before surface change event
// became pending, above.
EXPECT_EQ(1, demuxer_->num_browser_seek_requests());
- EXPECT_EQ(1, demuxer_->num_data_requests());
+ EXPECT_EQ(2, demuxer_->num_data_requests());
// Simulate browser seek is done and confirm player requests more data for new
// video decoder job.
player_.OnDemuxerSeekDone(player_.GetCurrentTime());
EXPECT_TRUE(GetMediaDecoderJob(false));
- EXPECT_EQ(2, demuxer_->num_data_requests());
+ EXPECT_EQ(3, demuxer_->num_data_requests());
EXPECT_EQ(1, demuxer_->num_seek_requests());
}
@@ -909,7 +914,6 @@ TEST_F(MediaSourcePlayerTest, SetEmptySurfaceAndStarveWhileDecoding) {
// While the decoder is decoding, pass an empty surface.
gfx::ScopedJavaSurface empty_surface;
player_.SetVideoSurface(empty_surface.Pass());
-
// Let the player starve. However, it should not issue any new data request in
// this case.
TriggerPlayerStarvation();
@@ -920,7 +924,8 @@ TEST_F(MediaSourcePlayerTest, SetEmptySurfaceAndStarveWhileDecoding) {
// No further seek or data requests should have been received since the
// surface is empty.
EXPECT_EQ(0, demuxer_->num_browser_seek_requests());
- EXPECT_EQ(1, demuxer_->num_data_requests());
+ EXPECT_EQ(2, demuxer_->num_data_requests());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
// Playback resumes once a non-empty surface is passed.
CreateNextTextureAndSetVideoSurface();
@@ -938,10 +943,13 @@ TEST_F(MediaSourcePlayerTest, ReleaseVideoDecoderResourcesWhileDecoding) {
ReleasePlayer();
// The resources will be immediately released since the decoder is idle.
EXPECT_EQ(1, manager_.num_resources_released());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
// Recreate the video decoder.
CreateNextTextureAndSetVideoSurface();
player_.Start();
+ EXPECT_EQ(1, demuxer_->num_browser_seek_requests());
+ player_.OnDemuxerSeekDone(base::TimeDelta());
EXPECT_EQ(2, manager_.num_resources_requested());
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
ReleasePlayer();
@@ -1018,6 +1026,7 @@ TEST_F(MediaSourcePlayerTest, StartImmediatelyAfterPause) {
// Sending data to player.
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(0));
EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
+ EXPECT_EQ(2, demuxer_->num_data_requests());
// Decoder job will not immediately stop after Pause() since it is
// running on another thread.
@@ -1028,12 +1037,12 @@ TEST_F(MediaSourcePlayerTest, StartImmediatelyAfterPause) {
player_.Start();
// Verify that Start() will not destroy and recreate the decoder job.
EXPECT_EQ(decoder_job, GetMediaDecoderJob(true));
- EXPECT_EQ(1, demuxer_->num_data_requests());
- EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
- message_loop_.Run();
- // The decoder job should finish and a new request will be sent.
+
+ while (GetMediaDecoderJob(true)->is_decoding())
+ message_loop_.RunUntilIdle();
+ // The decoder job should finish and wait for data.
EXPECT_EQ(2, demuxer_->num_data_requests());
- EXPECT_FALSE(GetMediaDecoderJob(true)->is_decoding());
+ EXPECT_TRUE(GetMediaDecoderJob(true)->is_requesting_demuxer_data());
}
TEST_F(MediaSourcePlayerTest, DecoderJobsCannotStartWithoutAudio) {
@@ -1074,7 +1083,8 @@ TEST_F(MediaSourcePlayerTest, StartTimeTicksResetAfterDecoderUnderruns) {
for (int i = 0; i < 4; ++i) {
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(i));
EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
- message_loop_.Run();
+ // Decode data until decoder started requesting new data again.
+ WaitForAudioDecodeDone();
}
// The decoder job should finish and a new request will be sent.
@@ -1082,15 +1092,8 @@ TEST_F(MediaSourcePlayerTest, StartTimeTicksResetAfterDecoderUnderruns) {
EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
base::TimeTicks previous = StartTimeTicks();
- // Let the decoder timeout and execute the OnDecoderStarved() callback.
- base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
-
- EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
- EXPECT_TRUE(StartTimeTicks() != base::TimeTicks());
- message_loop_.RunUntilIdle();
-
- // Send new data to the decoder so it can finish the currently
- // pending decode.
+ // Let the decoder starve.
+ TriggerPlayerStarvation();
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(3));
WaitForAudioDecodeDone();
@@ -1104,7 +1107,7 @@ TEST_F(MediaSourcePlayerTest, StartTimeTicksResetAfterDecoderUnderruns) {
EXPECT_TRUE(StartTimeTicks() != base::TimeTicks());
base::TimeTicks current = StartTimeTicks();
- EXPECT_LE(100.0, (current - previous).InMillisecondsF());
+ EXPECT_LE(0, (current - previous).InMillisecondsF());
}
TEST_F(MediaSourcePlayerTest, V_SecondAccessUnitIsEOSAndResumePlayAfterSeek) {
@@ -1116,7 +1119,7 @@ TEST_F(MediaSourcePlayerTest, V_SecondAccessUnitIsEOSAndResumePlayAfterSeek) {
// Send the first input chunk.
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
- message_loop_.Run();
+ WaitForVideoDecodeDone();
VerifyPlaybackCompletesOnEOSDecode(true, false);
VerifyCompletedPlaybackResumesOnSeekPlusStart(false, true);
@@ -1230,9 +1233,7 @@ TEST_F(MediaSourcePlayerTest, AV_NoPrefetchForFinishedVideoOnAudioStarvation) {
// Wait until video EOS is processed and more data (assumed to be audio) is
// requested.
- while (demuxer_->num_data_requests() < 3)
- message_loop_.RunUntilIdle();
- WaitForVideoDecodeDone();
+ WaitForAudioVideoDecodeDone();
EXPECT_EQ(3, demuxer_->num_data_requests());
// Simulate decoder underrun to trigger prefetch while still decoding audio.
@@ -1245,7 +1246,6 @@ TEST_F(MediaSourcePlayerTest, AV_NoPrefetchForFinishedVideoOnAudioStarvation) {
// starvation was triggered.
WaitForAudioDecodeDone();
EXPECT_EQ(4, demuxer_->num_data_requests());
-
player_.OnDemuxerDataAvailable(CreateEOSAck(true)); // Audio EOS
EXPECT_FALSE(GetMediaDecoderJob(false)->is_decoding());
EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
@@ -1263,7 +1263,7 @@ TEST_F(MediaSourcePlayerTest, V_StarvationDuringEOSDecode) {
CreateNextTextureAndSetVideoSurface();
StartVideoDecoderJob(true);
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
- message_loop_.Run();
+ WaitForVideoDecodeDone();
// Simulate decoder underrun to trigger prefetch while decoding EOS.
player_.OnDemuxerDataAvailable(CreateEOSAck(false)); // Video EOS
@@ -1279,7 +1279,7 @@ TEST_F(MediaSourcePlayerTest, A_StarvationDuringEOSDecode) {
// starvation occurs during EOS decode.
StartAudioDecoderJob(true);
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(0));
- message_loop_.Run();
+ WaitForAudioDecodeDone();
// Simulate decoder underrun to trigger prefetch while decoding EOS.
player_.OnDemuxerDataAvailable(CreateEOSAck(true)); // Audio EOS
@@ -1389,21 +1389,20 @@ TEST_F(MediaSourcePlayerTest, BrowserSeek_RegularSeekPendsBrowserSeekDone) {
EXPECT_FALSE(GetMediaDecoderJob(false));
EXPECT_EQ(2, demuxer_->num_seek_requests());
EXPECT_EQ(1, demuxer_->num_browser_seek_requests());
- EXPECT_EQ(1, demuxer_->num_data_requests());
// Simulate regular seek is done and confirm player requests more data for
// new video decoder job.
player_.OnDemuxerSeekDone(kNoTimestamp());
EXPECT_TRUE(GetMediaDecoderJob(false));
- EXPECT_EQ(2, demuxer_->num_data_requests());
+ EXPECT_EQ(3, demuxer_->num_data_requests());
EXPECT_EQ(2, demuxer_->num_seek_requests());
}
-TEST_F(MediaSourcePlayerTest, NoSeekForInitialReleaseAndStart) {
+TEST_F(MediaSourcePlayerTest, BrowserSeek_InitialReleaseAndStart) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
- // Test that no seek is requested if player Release() + Start() occurs prior
- // to receiving any data.
+ // Test that browser seek is requested if player Release() + Start() occurs
+ // prior to receiving any data.
CreateNextTextureAndSetVideoSurface();
StartVideoDecoderJob(true);
ReleasePlayer();
@@ -1413,12 +1412,15 @@ TEST_F(MediaSourcePlayerTest, NoSeekForInitialReleaseAndStart) {
player_.Start();
- // TODO(wolenetz/qinmin): Multiple in-flight data requests for same stream
- // should be prevented. See http://crbug.com/306314.
- EXPECT_EQ(2, demuxer_->num_data_requests());
- EXPECT_TRUE(GetMediaDecoderJob(false));
+ // The new player won't be created until the pending data request is
+ // processed.
+ EXPECT_EQ(1, demuxer_->num_data_requests());
+ EXPECT_FALSE(GetMediaDecoderJob(false));
- EXPECT_EQ(0, demuxer_->num_seek_requests());
+ // A browser seek should be requested.
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ EXPECT_EQ(1, demuxer_->num_browser_seek_requests());
+ EXPECT_EQ(1, demuxer_->num_data_requests());
}
TEST_F(MediaSourcePlayerTest, BrowserSeek_MidStreamReleaseAndStart) {
@@ -1427,7 +1429,6 @@ TEST_F(MediaSourcePlayerTest, BrowserSeek_MidStreamReleaseAndStart) {
// Test that one browser seek is requested if player Release() + Start(), with
// video data received between Release() and Start().
BrowserSeekPlayer(true);
- EXPECT_EQ(1, demuxer_->num_data_requests());
// Simulate browser seek is done and confirm player requests more data.
player_.OnDemuxerSeekDone(base::TimeDelta());
@@ -1474,7 +1475,7 @@ TEST_F(MediaSourcePlayerTest, SeekingAfterCompletingPrerollRestartsPreroll) {
for (int i = 0; i < 4; ++i) {
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(i));
EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
- message_loop_.Run();
+ WaitForAudioDecodeDone();
}
EXPECT_LT(0.0, player_.GetCurrentTime().InMillisecondsF());
EXPECT_FALSE(IsPrerolling(true));
@@ -1492,7 +1493,7 @@ TEST_F(MediaSourcePlayerTest, SeekingAfterCompletingPrerollRestartsPreroll) {
500 + 30 * (i - 1));
player_.OnDemuxerDataAvailable(data);
EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
- message_loop_.Run();
+ WaitForAudioDecodeDone();
}
EXPECT_LT(500.0, player_.GetCurrentTime().InMillisecondsF());
EXPECT_FALSE(IsPrerolling(true));
@@ -1536,13 +1537,13 @@ TEST_F(MediaSourcePlayerTest, PrerollContinuesAcrossReleaseAndStart) {
// verification and prevents multiple in-flight data requests.
ReleasePlayer();
player_.OnDemuxerDataAvailable(data);
- message_loop_.RunUntilIdle();
+ WaitForAudioDecodeDone();
EXPECT_FALSE(GetMediaDecoderJob(true));
StartAudioDecoderJob(true);
} else {
player_.OnDemuxerDataAvailable(data);
EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
- message_loop_.Run();
+ WaitForAudioDecodeDone();
}
EXPECT_TRUE(IsPrerolling(true));
}
@@ -1670,7 +1671,7 @@ TEST_F(MediaSourcePlayerTest, BrowserSeek_PrerollAfterBrowserSeek) {
EXPECT_TRUE(GetMediaDecoderJob(false));
EXPECT_EQ(100.0, player_.GetCurrentTime().InMillisecondsF());
EXPECT_EQ(100.0, GetPrerollTimestamp().InMillisecondsF());
- EXPECT_EQ(2, demuxer_->num_data_requests());
+ EXPECT_EQ(3, demuxer_->num_data_requests());
PrerollDecoderToTime(
false, base::TimeDelta(), base::TimeDelta::FromMilliseconds(100));
@@ -1781,14 +1782,16 @@ TEST_F(MediaSourcePlayerTest,
CreateNextTextureAndSetVideoSurface();
TriggerPlayerStarvation();
WaitForVideoDecodeDone();
+ EXPECT_EQ(0, demuxer_->num_browser_seek_requests());
// Surface change should trigger a seek.
+ player_.OnDemuxerDataAvailable(data);
EXPECT_EQ(1, demuxer_->num_browser_seek_requests());
player_.OnDemuxerSeekDone(base::TimeDelta());
EXPECT_TRUE(GetMediaDecoderJob(false));
// A new data request should be sent.
- EXPECT_EQ(2, demuxer_->num_data_requests());
+ EXPECT_EQ(3, demuxer_->num_data_requests());
}
TEST_F(MediaSourcePlayerTest, ReleaseWithOnPrefetchDoneAlreadyPosted) {
@@ -1804,7 +1807,7 @@ TEST_F(MediaSourcePlayerTest, ReleaseWithOnPrefetchDoneAlreadyPosted) {
// Escape the original prefetch by decoding a single access unit.
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(0));
- message_loop_.Run();
+ WaitForAudioDecodeDone();
// Prime the job with a few more access units, so that a later prefetch,
// triggered by starvation to simulate decoder underrun, can trivially
@@ -1823,8 +1826,7 @@ TEST_F(MediaSourcePlayerTest, ReleaseWithOnPrefetchDoneAlreadyPosted) {
// occurs and should execute after the Release().
OnNextTestDecodeCallbackPostTaskToReleasePlayer();
- while (GetMediaDecoderJob(true))
- message_loop_.RunUntilIdle();
+ WaitForAudioDecodeDone();
EXPECT_TRUE(decoder_callback_hook_executed_);
EXPECT_EQ(2, demuxer_->num_data_requests());
@@ -1850,7 +1852,7 @@ TEST_F(MediaSourcePlayerTest, SeekToThenReleaseThenDemuxerSeekAndDone) {
EXPECT_FALSE(player_.IsPlaying());
// Player should begin prefetch and resume preroll upon Start().
- EXPECT_EQ(1, demuxer_->num_data_requests());
+ EXPECT_EQ(2, demuxer_->num_data_requests());
StartAudioDecoderJob(true);
EXPECT_TRUE(IsPrerolling(true));
EXPECT_EQ(100.0, GetPrerollTimestamp().InMillisecondsF());
@@ -1874,14 +1876,14 @@ TEST_F(MediaSourcePlayerTest, SeekToThenReleaseThenDemuxerSeekThenStart) {
// Player should not prefetch upon Start() nor create the decoder job, due to
// awaiting DemuxerSeekDone.
- EXPECT_EQ(1, demuxer_->num_data_requests());
+ EXPECT_EQ(2, demuxer_->num_data_requests());
StartAudioDecoderJob(false);
player_.OnDemuxerSeekDone(kNoTimestamp());
EXPECT_TRUE(GetMediaDecoderJob(true));
EXPECT_TRUE(IsPrerolling(true));
EXPECT_EQ(100.0, GetPrerollTimestamp().InMillisecondsF());
- EXPECT_EQ(2, demuxer_->num_data_requests());
+ EXPECT_EQ(3, demuxer_->num_data_requests());
// No further seek should have been requested since Release(), above.
EXPECT_EQ(1, demuxer_->num_seek_requests());
@@ -1905,7 +1907,7 @@ TEST_F(MediaSourcePlayerTest, SeekToThenDemuxerSeekThenReleaseThenSeekDone) {
EXPECT_EQ(100.0, GetPrerollTimestamp().InMillisecondsF());
// Player should begin prefetch and resume preroll upon Start().
- EXPECT_EQ(1, demuxer_->num_data_requests());
+ EXPECT_EQ(2, demuxer_->num_data_requests());
StartAudioDecoderJob(true);
EXPECT_TRUE(IsPrerolling(true));
EXPECT_EQ(100.0, GetPrerollTimestamp().InMillisecondsF());
@@ -1927,14 +1929,14 @@ TEST_F(MediaSourcePlayerTest, SeekToThenReleaseThenStart) {
EXPECT_EQ(1, demuxer_->num_seek_requests());
ReleasePlayer();
- EXPECT_EQ(1, demuxer_->num_data_requests());
+ EXPECT_EQ(2, demuxer_->num_data_requests());
StartAudioDecoderJob(false);
player_.OnDemuxerSeekDone(kNoTimestamp());
EXPECT_TRUE(GetMediaDecoderJob(true));
EXPECT_TRUE(IsPrerolling(true));
EXPECT_EQ(100.0, GetPrerollTimestamp().InMillisecondsF());
- EXPECT_EQ(2, demuxer_->num_data_requests());
+ EXPECT_EQ(3, demuxer_->num_data_requests());
// No further seek should have been requested since before Release(), above.
EXPECT_EQ(1, demuxer_->num_seek_requests());
@@ -2004,7 +2006,7 @@ TEST_F(MediaSourcePlayerTest, BrowserSeek_ThenReleaseThenDemuxerSeekDone) {
EXPECT_EQ(expected_preroll_timestamp, GetPrerollTimestamp());
// Player should begin prefetch and resume preroll upon Start().
- EXPECT_EQ(1, demuxer_->num_data_requests());
+ EXPECT_EQ(2, demuxer_->num_data_requests());
CreateNextTextureAndSetVideoSurface();
StartVideoDecoderJob(true);
EXPECT_TRUE(IsPrerolling(false));
@@ -2027,7 +2029,7 @@ TEST_F(MediaSourcePlayerTest, BrowserSeek_ThenReleaseThenStart) {
base::TimeDelta expected_preroll_timestamp = player_.GetCurrentTime();
ReleasePlayer();
- EXPECT_EQ(1, demuxer_->num_data_requests());
+ EXPECT_EQ(2, demuxer_->num_data_requests());
CreateNextTextureAndSetVideoSurface();
StartVideoDecoderJob(false);
@@ -2036,7 +2038,7 @@ TEST_F(MediaSourcePlayerTest, BrowserSeek_ThenReleaseThenStart) {
EXPECT_TRUE(IsPrerolling(false));
EXPECT_EQ(expected_preroll_timestamp, GetPrerollTimestamp());
EXPECT_EQ(expected_preroll_timestamp, player_.GetCurrentTime());
- EXPECT_EQ(2, demuxer_->num_data_requests());
+ EXPECT_EQ(3, demuxer_->num_data_requests());
// No further seek should have been requested since BrowserSeekPlayer().
EXPECT_EQ(1, demuxer_->num_seek_requests());