summaryrefslogtreecommitdiff
path: root/chromium/media/base/android/media_codec_player.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/base/android/media_codec_player.cc')
-rw-r--r--chromium/media/base/android/media_codec_player.cc998
1 files changed, 847 insertions, 151 deletions
diff --git a/chromium/media/base/android/media_codec_player.cc b/chromium/media/base/android/media_codec_player.cc
index aa05fdf5829..4c297f01b87 100644
--- a/chromium/media/base/android/media_codec_player.cc
+++ b/chromium/media/base/android/media_codec_player.cc
@@ -12,8 +12,11 @@
#include "base/threading/thread.h"
#include "media/base/android/media_codec_audio_decoder.h"
#include "media/base/android/media_codec_video_decoder.h"
+#include "media/base/android/media_drm_bridge.h"
#include "media/base/android/media_player_manager.h"
-#include "media/base/buffers.h"
+#include "media/base/android/media_task_runner.h"
+#include "media/base/bind_to_current_loop.h"
+#include "media/base/timestamp_constants.h"
#define RUN_ON_MEDIA_THREAD(METHOD, ...) \
do { \
@@ -28,48 +31,41 @@
namespace media {
-class MediaThread : public base::Thread {
- public:
- MediaThread() : base::Thread("BrowserMediaThread") {
- Start();
- }
-};
-
-// Create media thread
-base::LazyInstance<MediaThread>::Leaky
- g_media_thread = LAZY_INSTANCE_INITIALIZER;
-
-
-scoped_refptr<base::SingleThreadTaskRunner> GetMediaTaskRunner() {
- return g_media_thread.Pointer()->task_runner();
-}
-
// MediaCodecPlayer implementation.
MediaCodecPlayer::MediaCodecPlayer(
int player_id,
base::WeakPtr<MediaPlayerManager> manager,
- const RequestMediaResourcesCB& request_media_resources_cb,
+ const OnDecoderResourcesReleasedCB& on_decoder_resources_released_cb,
scoped_ptr<DemuxerAndroid> demuxer,
const GURL& frame_url)
: MediaPlayerAndroid(player_id,
manager.get(),
- request_media_resources_cb,
+ on_decoder_resources_released_cb,
frame_url),
ui_task_runner_(base::ThreadTaskRunnerHandle::Get()),
demuxer_(demuxer.Pass()),
- state_(STATE_PAUSED),
+ state_(kStatePaused),
interpolator_(&default_tick_clock_),
pending_start_(false),
+ pending_seek_(kNoTimestamp()),
+ drm_bridge_(nullptr),
+ cdm_registration_id_(0),
+ key_is_required_(false),
+ key_is_added_(false),
media_weak_factory_(this) {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
DVLOG(1) << "MediaCodecPlayer::MediaCodecPlayer: player_id:" << player_id;
- request_resources_cb_ = base::Bind(request_media_resources_cb_, player_id);
-
completion_cb_ =
base::Bind(&MediaPlayerManager::OnPlaybackComplete, manager, player_id);
+ waiting_for_decryption_key_cb_ = base::Bind(
+ &MediaPlayerManager::OnWaitingForDecryptionKey, manager, player_id);
+ seek_done_cb_ =
+ base::Bind(&MediaPlayerManager::OnSeekComplete, manager, player_id);
+ error_cb_ = base::Bind(&MediaPlayerManager::OnError, manager, player_id);
+
attach_listener_cb_ = base::Bind(&MediaPlayerAndroid::AttachListener,
WeakPtrForUIThread(), nullptr);
detach_listener_cb_ =
@@ -90,6 +86,23 @@ MediaCodecPlayer::~MediaCodecPlayer()
{
DVLOG(1) << "MediaCodecPlayer::~MediaCodecPlayer";
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+
+ // Currently the unit tests wait for the MediaCodecPlayer destruction by
+ // watching the demuxer, which is destroyed as one of the member variables.
+ // Release the codecs here, before any member variable is destroyed to make
+ // the unit tests happy.
+
+ if (video_decoder_)
+ video_decoder_->ReleaseDecoderResources();
+ if (audio_decoder_)
+ audio_decoder_->ReleaseDecoderResources();
+
+ media_stat_->StopAndReport(GetInterpolatedTime());
+
+ if (drm_bridge_) {
+ DCHECK(cdm_registration_id_);
+ drm_bridge_->UnregisterPlayer(cdm_registration_id_);
+ }
}
void MediaCodecPlayer::Initialize() {
@@ -127,16 +140,70 @@ void MediaCodecPlayer::SetVideoSurface(gfx::ScopedJavaSurface surface) {
DVLOG(1) << __FUNCTION__ << (surface.IsEmpty() ? " empty" : " non-empty");
- // I assume that if video decoder already has the surface,
- // there will be two calls:
- // (1) SetVideoSurface(0)
- // (2) SetVideoSurface(new_surface)
- video_decoder_->SetPendingSurface(surface.Pass());
+ // Save the empty-ness before we pass the surface to the decoder.
+ bool surface_is_empty = surface.IsEmpty();
+
+ // Apparently RemoveVideoSurface() can be called several times in a row,
+ // ignore the second and subsequent calls.
+ if (surface_is_empty && !video_decoder_->HasVideoSurface()) {
+ DVLOG(1) << __FUNCTION__ << ": surface already removed, ignoring";
+ return;
+ }
+
+ // Do not set unprotected surface if we know that we need a protected one.
+ // Empty surface means the surface removal and we always allow for it.
+ if (!surface_is_empty && video_decoder_->IsProtectedSurfaceRequired() &&
+ !surface.is_protected()) {
+ DVLOG(0) << __FUNCTION__ << ": surface is not protected, ignoring";
+ return;
+ }
- if (video_decoder_->HasPendingSurface() &&
- state_ == STATE_WAITING_FOR_SURFACE) {
- SetState(STATE_PLAYING);
- StartPlaybackDecoders();
+ video_decoder_->SetVideoSurface(surface.Pass());
+
+ if (surface_is_empty) {
+ // Remove video surface.
+ switch (state_) {
+ case kStatePlaying:
+ if (VideoFinished())
+ break;
+
+ DVLOG(1) << __FUNCTION__ << ": stopping and restarting";
+ // Stop decoders as quickly as possible.
+ StopDecoders(); // synchronous stop
+
+ // Prefetch or wait for initial configuration.
+ if (HasAudio() || HasVideo()) {
+ SetState(kStatePrefetching);
+ StartPrefetchDecoders();
+ } else {
+ SetState(kStateWaitingForConfig);
+ }
+ break;
+
+ default:
+ break; // ignore
+ }
+ } else {
+ // Replace video surface.
+ switch (state_) {
+ case kStateWaitingForSurface:
+ SetState(kStatePlaying);
+ StartPlaybackOrBrowserSeek();
+ break;
+
+ case kStatePlaying:
+ if (VideoFinished())
+ break;
+
+ DVLOG(1) << __FUNCTION__ << ": requesting to stop and restart";
+ SetState(kStateStopping);
+ RequestToStopDecoders();
+ SetPendingStart(true);
+ break;
+
+ default:
+ break; // ignore
+ }
}
}
@@ -146,19 +213,30 @@ void MediaCodecPlayer::Start() {
DVLOG(1) << __FUNCTION__;
switch (state_) {
- case STATE_PAUSED:
+ case kStatePaused:
+ // Request play permission or wait for initial configuration.
if (HasAudio() || HasVideo()) {
- SetState(STATE_PREFETCHING);
- StartPrefetchDecoders();
+ SetState(kStateWaitingForPermission);
+ RequestPlayPermission();
} else {
- SetState(STATE_WAITING_FOR_CONFIG);
+ SetState(kStateWaitingForConfig);
}
break;
- case STATE_STOPPING:
+ case kStateStopping:
+ case kStateWaitingForSeek:
SetPendingStart(true);
break;
+ case kStateWaitingForConfig:
+ case kStateWaitingForPermission:
+ case kStatePrefetching:
+ case kStatePlaying:
+ case kStateWaitingForSurface:
+ case kStateWaitingForKey:
+ case kStateWaitingForMediaCrypto:
+ case kStateError:
+ break; // Ignore
default:
- // Ignore
+ NOTREACHED();
break;
}
}
@@ -168,21 +246,29 @@ void MediaCodecPlayer::Pause(bool is_media_related_action) {
DVLOG(1) << __FUNCTION__;
+ SetPendingStart(false);
+
switch (state_) {
- case STATE_PREFETCHING:
- SetState(STATE_PAUSED);
- StopDecoders();
- break;
- case STATE_WAITING_FOR_SURFACE:
- SetState(STATE_PAUSED);
+ case kStateWaitingForConfig:
+ case kStateWaitingForPermission:
+ case kStatePrefetching:
+ case kStateWaitingForSurface:
+ case kStateWaitingForKey:
+ case kStateWaitingForMediaCrypto:
+ SetState(kStatePaused);
StopDecoders();
break;
- case STATE_PLAYING:
- SetState(STATE_STOPPING);
+ case kStatePlaying:
+ SetState(kStateStopping);
RequestToStopDecoders();
break;
+ case kStatePaused:
+ case kStateStopping:
+ case kStateWaitingForSeek:
+ case kStateError:
+ break; // Ignore
default:
- // Ignore
+ NOTREACHED();
break;
}
}
@@ -191,16 +277,77 @@ void MediaCodecPlayer::SeekTo(base::TimeDelta timestamp) {
RUN_ON_MEDIA_THREAD(SeekTo, timestamp);
DVLOG(1) << __FUNCTION__ << " " << timestamp;
- NOTIMPLEMENTED();
+
+ switch (state_) {
+ case kStatePaused:
+ SetState(kStateWaitingForSeek);
+ RequestDemuxerSeek(timestamp);
+ break;
+ case kStateWaitingForConfig:
+ case kStateWaitingForPermission:
+ case kStatePrefetching:
+ case kStateWaitingForSurface:
+ case kStateWaitingForKey:
+ case kStateWaitingForMediaCrypto:
+ SetState(kStateWaitingForSeek);
+ StopDecoders();
+ SetPendingStart(true);
+ RequestDemuxerSeek(timestamp);
+ break;
+ case kStatePlaying:
+ SetState(kStateStopping);
+ RequestToStopDecoders();
+ SetPendingStart(true);
+ SetPendingSeek(timestamp);
+ break;
+ case kStateStopping:
+ SetPendingSeek(timestamp);
+ break;
+ case kStateWaitingForSeek:
+ SetPendingSeek(timestamp);
+ break;
+ case kStateError:
+ break; // ignore
+ default:
+ NOTREACHED();
+ break;
+ }
}
void MediaCodecPlayer::Release() {
+ // TODO(qinmin): the callback should be posted onto the UI thread when
+ // Release() finishes on media thread. However, the BrowserMediaPlayerManager
+ // could be gone in that case, which cause the MediaThrottler unable to
+ // track the active players. We should pass
+ // MediaThrottler::OnDecodeRequestFinished() to this class in the ctor, but
+ // also need a way for BrowserMediaPlayerManager to track active players.
+ if (ui_task_runner_->BelongsToCurrentThread())
+ on_decoder_resources_released_cb_.Run(player_id());
+
RUN_ON_MEDIA_THREAD(Release);
DVLOG(1) << __FUNCTION__;
- SetState(STATE_PAUSED);
+ // Stop decoding threads and delete MediaCodecs, but keep IPC between browser
+ // and renderer processes going. Seek should work across and after Release().
+
ReleaseDecoderResources();
+
+ SetPendingStart(false);
+
+ if (state_ != kStateWaitingForSeek)
+ SetState(kStatePaused);
+
+ // Crear encryption key related flags.
+ key_is_required_ = false;
+ key_is_added_ = false;
+
+ base::TimeDelta pending_seek_time = GetPendingSeek();
+ if (pending_seek_time != kNoTimestamp()) {
+ SetPendingSeek(kNoTimestamp());
+ SetState(kStateWaitingForSeek);
+ RequestDemuxerSeek(pending_seek_time);
+ }
}
void MediaCodecPlayer::SetVolume(double volume) {
@@ -232,7 +379,10 @@ base::TimeDelta MediaCodecPlayer::GetDuration() {
bool MediaCodecPlayer::IsPlaying() {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
- return state_ == STATE_PLAYING;
+
+ // TODO(timav): Use another variable since |state_| should only be accessed on
+ // Media thread.
+ return state_ == kStatePlaying || state_ == kStateStopping;
}
bool MediaCodecPlayer::CanPause() {
@@ -261,8 +411,39 @@ bool MediaCodecPlayer::IsPlayerReady() {
}
void MediaCodecPlayer::SetCdm(BrowserCdm* cdm) {
- DCHECK(ui_task_runner_->BelongsToCurrentThread());
- NOTIMPLEMENTED();
+ RUN_ON_MEDIA_THREAD(SetCdm, cdm);
+
+ DVLOG(1) << __FUNCTION__;
+
+ // Currently we don't support DRM change during the middle of playback, even
+ // if the player is paused. There is no current plan to support it, see
+ // http://crbug.com/253792.
+ if (state_ != kStatePaused || GetInterpolatedTime() > base::TimeDelta()) {
+ VLOG(0) << "Setting DRM bridge after playback has started is not supported";
+ return;
+ }
+
+ if (drm_bridge_) {
+ NOTREACHED() << "Currently we do not support resetting CDM.";
+ return;
+ }
+
+ DCHECK(cdm);
+ drm_bridge_ = static_cast<MediaDrmBridge*>(cdm);
+
+ DCHECK(drm_bridge_);
+
+ cdm_registration_id_ = drm_bridge_->RegisterPlayer(
+ base::Bind(&MediaCodecPlayer::OnKeyAdded, media_weak_this_),
+ base::Bind(&MediaCodecPlayer::OnCdmUnset, media_weak_this_));
+
+ MediaDrmBridge::MediaCryptoReadyCB cb = BindToCurrentLoop(
+ base::Bind(&MediaCodecPlayer::OnMediaCryptoReady, media_weak_this_));
+
+ // Post back to UI thread.
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&MediaDrmBridge::SetMediaCryptoReadyCB,
+ drm_bridge_->WeakPtrForUIThread(), cb));
}
// Callbacks from Demuxer.
@@ -304,7 +485,67 @@ void MediaCodecPlayer::OnDemuxerSeekDone(
DVLOG(1) << __FUNCTION__ << " actual_time:" << actual_browser_seek_time;
- NOTIMPLEMENTED();
+ DCHECK(seek_info_.get());
+ DCHECK(seek_info_->seek_time != kNoTimestamp());
+
+ // A browser seek must not jump into the past. Ideally, it seeks to the
+ // requested time, but it might jump into the future.
+ DCHECK(!seek_info_->is_browser_seek ||
+ seek_info_->seek_time <= actual_browser_seek_time);
+
+ // Restrict the current time to be equal to seek_time
+ // for the next StartPlaybackDecoders() call.
+
+ base::TimeDelta seek_time = seek_info_->is_browser_seek
+ ? actual_browser_seek_time
+ : seek_info_->seek_time;
+
+ interpolator_.SetBounds(seek_time, seek_time);
+
+ audio_decoder_->SetBaseTimestamp(seek_time);
+
+ audio_decoder_->SetPrerollTimestamp(seek_time);
+ video_decoder_->SetPrerollTimestamp(seek_time);
+
+ // The Flush() might set the state to kStateError.
+ if (state_ == kStateError) {
+ // Notify the Renderer.
+ if (!seek_info_->is_browser_seek)
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::Bind(seek_done_cb_, seek_time));
+
+ seek_info_.reset();
+ return;
+ }
+
+ DCHECK_EQ(kStateWaitingForSeek, state_);
+
+ base::TimeDelta pending_seek_time = GetPendingSeek();
+ if (pending_seek_time != kNoTimestamp()) {
+ // Keep kStateWaitingForSeek
+ SetPendingSeek(kNoTimestamp());
+ RequestDemuxerSeek(pending_seek_time);
+ return;
+ }
+
+ if (HasPendingStart()) {
+ SetPendingStart(false);
+ // Request play permission or wait for initial configuration.
+ if (HasAudio() || HasVideo()) {
+ SetState(kStateWaitingForPermission);
+ RequestPlayPermission();
+ } else {
+ SetState(kStateWaitingForConfig);
+ }
+ } else {
+ SetState(kStatePaused);
+ }
+
+ // Notify the Renderer.
+ if (!seek_info_->is_browser_seek)
+ ui_task_runner_->PostTask(FROM_HERE, base::Bind(seek_done_cb_, seek_time));
+
+ seek_info_.reset();
}
void MediaCodecPlayer::OnDemuxerDurationChanged(
@@ -315,8 +556,58 @@ void MediaCodecPlayer::OnDemuxerDurationChanged(
duration_ = duration;
}
+void MediaCodecPlayer::SetDecodersTimeCallbackForTests(
+ DecodersTimeCallback cb) {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+ decoders_time_cb_ = cb;
+}
+
+void MediaCodecPlayer::SetCodecCreatedCallbackForTests(
+ CodecCreatedCallback cb) {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+ DCHECK(audio_decoder_ && video_decoder_);
+
+ audio_decoder_->SetCodecCreatedCallbackForTests(
+ base::Bind(cb, DemuxerStream::AUDIO));
+ video_decoder_->SetCodecCreatedCallbackForTests(
+ base::Bind(cb, DemuxerStream::VIDEO));
+}
+
+void MediaCodecPlayer::SetAlwaysReconfigureForTests(DemuxerStream::Type type) {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+ DCHECK(audio_decoder_ && video_decoder_);
+
+ if (type == DemuxerStream::AUDIO)
+ audio_decoder_->SetAlwaysReconfigureForTests();
+ else if (type == DemuxerStream::VIDEO)
+ video_decoder_->SetAlwaysReconfigureForTests();
+}
+
+bool MediaCodecPlayer::IsPrerollingForTests(DemuxerStream::Type type) const {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+ DCHECK(audio_decoder_ && video_decoder_);
+
+ if (type == DemuxerStream::AUDIO)
+ return audio_decoder_->IsPrerollingForTests();
+ else if (type == DemuxerStream::VIDEO)
+ return video_decoder_->IsPrerollingForTests();
+ else
+ return false;
+}
+
// Events from Player, called on UI thread
+void MediaCodecPlayer::RequestPermissionAndPostResult(
+ base::TimeDelta duration) {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+ DVLOG(1) << __FUNCTION__ << " duration:" << duration;
+
+ bool granted = manager()->RequestPlay(player_id(), duration);
+ GetMediaTaskRunner()->PostTask(
+ FROM_HERE, base::Bind(&MediaCodecPlayer::OnPermissionDecided,
+ media_weak_this_, granted));
+}
+
void MediaCodecPlayer::OnMediaMetadataChanged(base::TimeDelta duration,
const gfx::Size& video_size) {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
@@ -340,6 +631,35 @@ void MediaCodecPlayer::OnTimeUpdate(base::TimeDelta current_timestamp,
manager()->OnTimeUpdate(player_id(), current_timestamp, current_time_ticks);
}
+// Event from manager, called on Media thread
+
+void MediaCodecPlayer::OnPermissionDecided(bool granted) {
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+
+ DVLOG(1) << __FUNCTION__ << ": " << (granted ? "granted" : "denied");
+
+ switch (state_) {
+ case kStateWaitingForPermission:
+ if (granted) {
+ SetState(kStatePrefetching);
+ StartPrefetchDecoders();
+ } else {
+ SetState(kStatePaused);
+ StopDecoders();
+ }
+ break;
+
+ case kStatePaused:
+ case kStateWaitingForSeek:
+ case kStateError:
+ break; // ignore
+
+ default:
+ NOTREACHED() << __FUNCTION__ << ": wrong state " << AsString(state_);
+ break;
+ }
+}
+
// Events from Decoders, called on Media thread
void MediaCodecPlayer::RequestDemuxerData(DemuxerStream::Type stream_type) {
@@ -361,59 +681,143 @@ void MediaCodecPlayer::RequestDemuxerData(DemuxerStream::Type stream_type) {
void MediaCodecPlayer::OnPrefetchDone() {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
- DVLOG(1) << __FUNCTION__;
- if (state_ != STATE_PREFETCHING)
+ if (state_ != kStatePrefetching) {
+ DVLOG(1) << __FUNCTION__ << " wrong state " << AsString(state_)
+ << " ignoring";
return; // Ignore
+ }
+
+ DVLOG(1) << __FUNCTION__;
if (!HasAudio() && !HasVideo()) {
// No configuration at all after prefetching.
// This is an error, initial configuration is expected
// before the first data chunk.
- GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
+ DCHECK(!internal_error_cb_.is_null());
+ GetMediaTaskRunner()->PostTask(FROM_HERE, internal_error_cb_);
+ return;
+ }
+
+ if (HasVideo() && !video_decoder_->HasVideoSurface()) {
+ SetState(kStateWaitingForSurface);
return;
}
- if (HasVideo() && !HasPendingSurface()) {
- SetState(STATE_WAITING_FOR_SURFACE);
+ if (key_is_required_ && !key_is_added_) {
+ SetState(kStateWaitingForKey);
+ ui_task_runner_->PostTask(FROM_HERE, waiting_for_decryption_key_cb_);
return;
}
- SetState(STATE_PLAYING);
- StartPlaybackDecoders();
+ SetState(kStatePlaying);
+ StartPlaybackOrBrowserSeek();
}
-void MediaCodecPlayer::OnStopDone() {
+void MediaCodecPlayer::OnPrerollDone() {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+
+ if (state_ != kStatePlaying) {
+ DVLOG(1) << __FUNCTION__ << ": in state " << AsString(state_)
+ << ", ignoring";
+ return;
+ }
+
DVLOG(1) << __FUNCTION__;
- if (!(audio_decoder_->IsStopped() && video_decoder_->IsStopped()))
+ StartStatus status = StartDecoders();
+ if (status != kStartOk)
+ GetMediaTaskRunner()->PostTask(FROM_HERE, internal_error_cb_);
+}
+
+void MediaCodecPlayer::OnDecoderDrained(DemuxerStream::Type type) {
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+ DVLOG(1) << __FUNCTION__ << " " << type;
+
+ // We expect that OnStopDone() comes next.
+
+ DCHECK(type == DemuxerStream::AUDIO || type == DemuxerStream::VIDEO);
+
+ // DCHECK(state_ == kStatePlaying || state_ == kStateStopping)
+ // << __FUNCTION__ << " illegal state: " << AsString(state_);
+ //
+ // With simultaneous reconfiguration of audio and video streams the state
+ // can be kStatePrefetching as well:
+ // OnLastFrameRendered VIDEO (VIDEO decoder is stopped)
+ // OnLastFrameRendered AUDIO (AUDIO decoder is stopped)
+ // OnDecoderDrained VIDEO (kStatePlaying -> kStateStopping)
+ // OnStopDone VIDEO (kStateStopping -> kStatePrefetching)
+ // OnDecoderDrained AUDIO
+ // OnStopDone AUDIO
+ //
+ // TODO(timav): combine OnDecoderDrained() and OnStopDone() ?
+
+ switch (state_) {
+ case kStatePlaying:
+ SetState(kStateStopping);
+ SetPendingStart(true);
+
+ if (type == DemuxerStream::AUDIO && !VideoFinished()) {
+ DVLOG(1) << __FUNCTION__ << " requesting to stop video";
+ video_decoder_->RequestToStop();
+ } else if (type == DemuxerStream::VIDEO && !AudioFinished()) {
+ DVLOG(1) << __FUNCTION__ << " requesting to stop audio";
+ audio_decoder_->RequestToStop();
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void MediaCodecPlayer::OnStopDone(DemuxerStream::Type type) {
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+ DVLOG(1) << __FUNCTION__ << " " << type
+ << " interpolated time:" << GetInterpolatedTime();
+
+ if (!(audio_decoder_->IsStopped() && video_decoder_->IsStopped())) {
+ DVLOG(1) << __FUNCTION__ << " both audio and video has to be stopped"
+ << ", ignoring";
return; // Wait until other stream is stopped
+ }
// At this point decoder threads should not be running
if (interpolator_.interpolating())
interpolator_.StopInterpolating();
+ base::TimeDelta seek_time;
switch (state_) {
- case STATE_STOPPING:
- if (HasPendingStart()) {
+ case kStateStopping: {
+ base::TimeDelta seek_time = GetPendingSeek();
+ if (seek_time != kNoTimestamp()) {
+ SetState(kStateWaitingForSeek);
+ SetPendingSeek(kNoTimestamp());
+ RequestDemuxerSeek(seek_time);
+ } else if (HasPendingStart()) {
SetPendingStart(false);
- SetState(STATE_PREFETCHING);
- StartPrefetchDecoders();
+ SetState(kStateWaitingForPermission);
+ RequestPlayPermission();
} else {
- SetState(STATE_PAUSED);
+ SetState(kStatePaused);
}
- break;
- case STATE_PLAYING:
+ } break;
+ case kStatePlaying:
// Unexpected stop means completion
- SetState(STATE_PAUSED);
+ SetState(kStatePaused);
break;
default:
- DVLOG(0) << __FUNCTION__ << " illegal state: " << AsString(state_);
- NOTREACHED();
- break;
+ // DVLOG(0) << __FUNCTION__ << " illegal state: " << AsString(state_);
+ // NOTREACHED();
+ // Ignore! There can be a race condition: audio posts OnStopDone,
+ // then video posts, then first OnStopDone arrives at which point
+ // both streams are already stopped, then second OnStopDone arrives. When
+ // the second one arrives, the state us not kStateStopping any more.
+ return;
}
+ media_stat_->StopAndReport(GetInterpolatedTime());
+
// DetachListener to UI thread
ui_task_runner_->PostTask(FROM_HERE, detach_listener_cb_);
@@ -421,46 +825,86 @@ void MediaCodecPlayer::OnStopDone() {
ui_task_runner_->PostTask(FROM_HERE, completion_cb_);
}
+void MediaCodecPlayer::OnMissingKeyReported(DemuxerStream::Type type) {
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+ DVLOG(1) << __FUNCTION__ << " " << type;
+
+ // Request stop and restart to pick up the key.
+ key_is_required_ = true;
+
+ if (state_ == kStatePlaying) {
+ SetState(kStateStopping);
+ RequestToStopDecoders();
+ SetPendingStart(true);
+ }
+}
+
void MediaCodecPlayer::OnError() {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
DVLOG(1) << __FUNCTION__;
- // STATE_ERROR blocks all events
- SetState(STATE_ERROR);
+ // kStateError blocks all events
+ SetState(kStateError);
ReleaseDecoderResources();
+
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::Bind(error_cb_, MEDIA_ERROR_DECODE));
}
void MediaCodecPlayer::OnStarvation(DemuxerStream::Type type) {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
DVLOG(1) << __FUNCTION__ << " stream type:" << type;
- if (state_ != STATE_PLAYING)
+ if (state_ != kStatePlaying)
return; // Ignore
- SetState(STATE_STOPPING);
+ SetState(kStateStopping);
RequestToStopDecoders();
SetPendingStart(true);
+
+ media_stat_->AddStarvation();
}
void MediaCodecPlayer::OnTimeIntervalUpdate(DemuxerStream::Type type,
base::TimeDelta now_playing,
- base::TimeDelta last_buffered) {
+ base::TimeDelta last_buffered,
+ bool postpone) {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
- interpolator_.SetBounds(now_playing, last_buffered);
+ DVLOG(2) << __FUNCTION__ << ": stream type:" << type << " [" << now_playing
+ << "," << last_buffered << "]" << (postpone ? " postpone" : "");
+
+ // For testing only: report time interval as we receive it from decoders
+ // as an indication of what is being rendered. Do not post this callback
+ // for postponed frames: although the PTS is correct, the tests also care
+ // about the wall clock time this callback arrives and deduce the rendering
+ // moment from it.
+ if (!decoders_time_cb_.is_null() && !postpone) {
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(decoders_time_cb_, type, now_playing, last_buffered));
+ }
- // Post to UI thread
- ui_task_runner_->PostTask(FROM_HERE,
- base::Bind(time_update_cb_, GetInterpolatedTime(),
- base::TimeTicks::Now()));
-}
+ // I assume that audio stream cannot be added after we get configs by
+ // OnDemuxerConfigsAvailable(), but that audio can finish early.
-void MediaCodecPlayer::OnVideoCodecCreated() {
- DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+ if (type == DemuxerStream::VIDEO) {
+ // Ignore video PTS if there is audio stream or if it's behind current
+ // time as set by audio stream.
+ if (!AudioFinished() ||
+ (HasAudio() && now_playing < interpolator_.GetInterpolatedTime()))
+ return;
+ }
- // This callback requests resources by releasing other players.
- ui_task_runner_->PostTask(FROM_HERE, request_resources_cb_);
+ interpolator_.SetBounds(now_playing, last_buffered);
+
+ // Post to UI thread
+ if (!postpone) {
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::Bind(time_update_cb_, GetInterpolatedTime(),
+ base::TimeTicks::Now()));
+ }
}
void MediaCodecPlayer::OnVideoResolutionChanged(const gfx::Size& size) {
@@ -473,24 +917,86 @@ void MediaCodecPlayer::OnVideoResolutionChanged(const gfx::Size& size) {
FROM_HERE, base::Bind(metadata_changed_cb_, kNoTimestamp(), size));
}
-// State machine operations, called on Media thread
+// Callbacks from MediaDrmBridge.
-void MediaCodecPlayer::SetState(PlayerState new_state) {
+void MediaCodecPlayer::OnMediaCryptoReady(
+ MediaDrmBridge::JavaObjectPtr media_crypto,
+ bool needs_protected_surface) {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+ DVLOG(1) << __FUNCTION__ << " protected surface is "
+ << (needs_protected_surface ? "required" : "not required");
- DVLOG(1) << "SetState:" << AsString(state_) << " -> " << AsString(new_state);
- state_ = new_state;
+ // We use the parameters that come with this callback every time we call
+ // Configure(). This is possible only if the MediaCrypto object remains valid
+ // and the surface requirement does not change until new SetCdm() is called.
+
+ DCHECK(media_crypto);
+ DCHECK(!media_crypto->is_null());
+
+ media_crypto_ = media_crypto.Pass();
+
+ if (audio_decoder_) {
+ audio_decoder_->SetNeedsReconfigure();
+ }
+
+ if (video_decoder_) {
+ video_decoder_->SetNeedsReconfigure();
+ video_decoder_->SetProtectedSurfaceRequired(needs_protected_surface);
+ }
+
+ if (state_ == kStateWaitingForMediaCrypto) {
+ // Resume start sequence (configure, etc.)
+ SetState(kStatePlaying);
+ StartPlaybackOrBrowserSeek();
+ }
+
+ DVLOG(1) << __FUNCTION__ << " end";
}
-void MediaCodecPlayer::SetPendingSurface(gfx::ScopedJavaSurface surface) {
+void MediaCodecPlayer::OnKeyAdded() {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
DVLOG(1) << __FUNCTION__;
- video_decoder_->SetPendingSurface(surface.Pass());
+ key_is_added_ = true;
+
+ if (state_ == kStateWaitingForKey) {
+ SetState(kStatePlaying);
+ StartPlaybackOrBrowserSeek();
+ }
+}
+
+void MediaCodecPlayer::OnCdmUnset() {
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+ DVLOG(1) << __FUNCTION__;
+
+ // This comment is copied from MediaSourcePlayer::OnCdmUnset().
+ // TODO(xhwang): Currently this is only called during teardown. Support full
+ // detachment of CDM during playback. This will be needed when we start to
+ // support setMediaKeys(0) (see http://crbug.com/330324), or when we release
+ // MediaDrm when the video is paused, or when the device goes to sleep (see
+ // http://crbug.com/272421).
+
+ if (audio_decoder_) {
+ audio_decoder_->SetNeedsReconfigure();
+ }
+
+ if (video_decoder_) {
+ video_decoder_->SetProtectedSurfaceRequired(false);
+ video_decoder_->SetNeedsReconfigure();
+ }
+
+ cdm_registration_id_ = 0;
+ drm_bridge_ = nullptr;
+ media_crypto_.reset();
}
-bool MediaCodecPlayer::HasPendingSurface() {
- return video_decoder_->HasPendingSurface();
+// State machine operations, called on Media thread
+
+void MediaCodecPlayer::SetState(PlayerState new_state) {
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+
+ DVLOG(1) << "SetState:" << AsString(state_) << " -> " << AsString(new_state);
+ state_ = new_state;
}
void MediaCodecPlayer::SetPendingStart(bool need_to_start) {
@@ -499,17 +1005,28 @@ void MediaCodecPlayer::SetPendingStart(bool need_to_start) {
pending_start_ = need_to_start;
}
-bool MediaCodecPlayer::HasPendingStart() {
+bool MediaCodecPlayer::HasPendingStart() const {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
return pending_start_;
}
-bool MediaCodecPlayer::HasAudio() {
+void MediaCodecPlayer::SetPendingSeek(base::TimeDelta timestamp) {
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+ DVLOG(1) << __FUNCTION__ << ": " << timestamp;
+ pending_seek_ = timestamp;
+}
+
+base::TimeDelta MediaCodecPlayer::GetPendingSeek() const {
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+ return pending_seek_;
+}
+
+bool MediaCodecPlayer::HasAudio() const {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
return audio_decoder_->HasStream();
}
-bool MediaCodecPlayer::HasVideo() {
+bool MediaCodecPlayer::HasVideo() const {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
return video_decoder_->HasStream();
}
@@ -531,12 +1048,24 @@ void MediaCodecPlayer::SetDemuxerConfigs(const DemuxerConfigs& configs) {
if (configs.video_codec != kUnknownVideoCodec)
video_decoder_->SetDemuxerConfigs(configs);
- if (state_ == STATE_WAITING_FOR_CONFIG) {
- SetState(STATE_PREFETCHING);
- StartPrefetchDecoders();
+ if (state_ == kStateWaitingForConfig) {
+ SetState(kStateWaitingForPermission);
+ RequestPlayPermission();
}
}
+void MediaCodecPlayer::RequestPlayPermission() {
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+ DVLOG(1) << __FUNCTION__;
+
+ // Check that we have received demuxer config hence we know the duration.
+ DCHECK(HasAudio() || HasVideo());
+
+ ui_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&MediaPlayerAndroid::RequestPermissionAndPostResult,
+ WeakPtrForUIThread(), duration_));
+}
+
void MediaCodecPlayer::StartPrefetchDecoders() {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
DVLOG(1) << __FUNCTION__;
@@ -565,66 +1094,192 @@ void MediaCodecPlayer::StartPrefetchDecoders() {
video_decoder_->Prefetch(prefetch_cb);
}
-void MediaCodecPlayer::StartPlaybackDecoders() {
+void MediaCodecPlayer::StartPlaybackOrBrowserSeek() {
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+ DVLOG(1) << __FUNCTION__;
+
+ // TODO(timav): consider replacing this method with posting a
+ // browser seek task (i.e. generate an event) from StartPlaybackDecoders().
+
+ // Clear encryption key related flags.
+ key_is_required_ = false;
+ key_is_added_ = false;
+
+ StartStatus status = StartPlaybackDecoders();
+
+ switch (status) {
+ case kStartBrowserSeekRequired:
+ // Browser seek
+ SetState(kStateWaitingForSeek);
+ SetPendingStart(true);
+ StopDecoders();
+ RequestDemuxerSeek(GetInterpolatedTime(), true);
+ break;
+ case kStartCryptoRequired:
+ SetState(kStateWaitingForMediaCrypto);
+ break;
+ case kStartFailed:
+ GetMediaTaskRunner()->PostTask(FROM_HERE, internal_error_cb_);
+ break;
+ case kStartOk:
+ break;
+ }
+}
+
+MediaCodecPlayer::StartStatus MediaCodecPlayer::StartPlaybackDecoders() {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
DVLOG(1) << __FUNCTION__;
- // Configure all streams before the start since
- // we may discover that browser seek is required.
+ // Configure all streams before the start since we may discover that browser
+ // seek is required.
+ MediaCodecPlayer::StartStatus status = ConfigureDecoders();
+ if (status != kStartOk)
+ return status;
+
+ bool preroll_required = false;
+ status = MaybePrerollDecoders(&preroll_required);
+ if (preroll_required)
+ return status;
+
+ return StartDecoders();
+}
+
+MediaCodecPlayer::StartStatus MediaCodecPlayer::ConfigureDecoders() {
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+ DVLOG(1) << __FUNCTION__;
- bool do_audio = !AudioFinished();
- bool do_video = !VideoFinished();
+ const bool do_audio = !AudioFinished();
+ const bool do_video = !VideoFinished();
- // If there is nothing to play, the state machine should determine
- // this at the prefetch state and never call this method.
+ // If there is nothing to play, the state machine should determine this at the
+ // prefetch state and never call this method.
DCHECK(do_audio || do_video);
- if (do_audio) {
- MediaCodecDecoder::ConfigStatus status = audio_decoder_->Configure();
- if (status != MediaCodecDecoder::CONFIG_OK) {
- GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
- return;
+ const bool need_audio_crypto =
+ do_audio && audio_decoder_->IsContentEncrypted();
+ const bool need_video_crypto =
+ do_video && video_decoder_->IsContentEncrypted();
+
+ // Do we need to create a local ref from the global ref?
+ jobject media_crypto = media_crypto_ ? media_crypto_->obj() : nullptr;
+
+ if (need_audio_crypto || need_video_crypto) {
+ DVLOG(1) << (need_audio_crypto ? " audio" : "")
+ << (need_video_crypto ? " video" : "") << " need(s) encryption";
+ if (!media_crypto) {
+ DVLOG(1) << __FUNCTION__ << ": MediaCrypto is not found, returning";
+ return kStartCryptoRequired;
}
}
- if (do_video) {
- MediaCodecDecoder::ConfigStatus status = video_decoder_->Configure();
- if (status != MediaCodecDecoder::CONFIG_OK) {
- GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
- return;
- }
+ // Start with video: if browser seek is required it would not make sense to
+ // configure audio.
+
+ MediaCodecDecoder::ConfigStatus status = MediaCodecDecoder::kConfigOk;
+ if (do_video)
+ status = video_decoder_->Configure(media_crypto);
+
+ if (status == MediaCodecDecoder::kConfigOk && do_audio)
+ status = audio_decoder_->Configure(media_crypto);
+
+ switch (status) {
+ case MediaCodecDecoder::kConfigOk:
+ break;
+ case MediaCodecDecoder::kConfigKeyFrameRequired:
+ return kStartBrowserSeekRequired;
+ case MediaCodecDecoder::kConfigFailure:
+ return kStartFailed;
+ }
+ return kStartOk;
+}
+
+MediaCodecPlayer::StartStatus MediaCodecPlayer::MaybePrerollDecoders(
+ bool* preroll_required) {
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+
+ DVLOG(1) << __FUNCTION__ << " current_time:" << GetInterpolatedTime();
+
+ // If requested, preroll is always done in the beginning of the playback,
+ // after prefetch. The request might not happen at all though, in which case
+ // we won't have prerolling phase. We need the prerolling when we (re)create
+ // the decoder, because its configuration and initialization (getting input,
+ // but not making output) can take time, and after the seek because there
+ // could be some data to be skipped and there is again initialization after
+ // the flush.
+
+ *preroll_required = false;
+
+ int count = 0;
+ const bool do_audio = audio_decoder_->NotCompletedAndNeedsPreroll();
+ if (do_audio)
+ ++count;
+
+ const bool do_video = video_decoder_->NotCompletedAndNeedsPreroll();
+ if (do_video)
+ ++count;
+
+ if (count == 0) {
+ DVLOG(1) << __FUNCTION__ << ": preroll is not required, skipping";
+ return kStartOk;
}
- // At this point decoder threads should not be running.
+ *preroll_required = true;
+
+ DCHECK(count > 0);
+ DCHECK(do_audio || do_video);
+
+ DVLOG(1) << __FUNCTION__ << ": preroll for " << count << " stream(s)";
+
+ base::Closure preroll_cb = base::BarrierClosure(
+ count, base::Bind(&MediaCodecPlayer::OnPrerollDone, media_weak_this_));
+
+ if (do_audio && !audio_decoder_->Preroll(preroll_cb))
+ return kStartFailed;
+
+ if (do_video && !video_decoder_->Preroll(preroll_cb))
+ return kStartFailed;
+
+ return kStartOk;
+}
+
+MediaCodecPlayer::StartStatus MediaCodecPlayer::StartDecoders() {
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+
if (!interpolator_.interpolating())
interpolator_.StartInterpolating();
base::TimeDelta current_time = GetInterpolatedTime();
- if (do_audio) {
- if (!audio_decoder_->Start(current_time)) {
- GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
- return;
- }
+ DVLOG(1) << __FUNCTION__ << " current_time:" << current_time;
+
+ // At this point decoder threads are either not running at all or their
+ // message pumps are in the idle state after the preroll is done.
+ media_stat_->Start(current_time);
+
+ if (!AudioFinished()) {
+ if (!audio_decoder_->Start(current_time))
+ return kStartFailed;
// Attach listener on UI thread
ui_task_runner_->PostTask(FROM_HERE, attach_listener_cb_);
}
- if (do_video) {
- if (!video_decoder_->Start(current_time)) {
- GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
- return;
- }
+ if (!VideoFinished()) {
+ if (!video_decoder_->Start(current_time))
+ return kStartFailed;
}
+
+ return kStartOk;
}
void MediaCodecPlayer::StopDecoders() {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
DVLOG(1) << __FUNCTION__;
- audio_decoder_->SyncStop();
video_decoder_->SyncStop();
+ audio_decoder_->SyncStop();
+
+ media_stat_->StopAndReport(GetInterpolatedTime());
}
void MediaCodecPlayer::RequestToStopDecoders() {
@@ -641,7 +1296,8 @@ void MediaCodecPlayer::RequestToStopDecoders() {
if (!do_audio && !do_video) {
GetMediaTaskRunner()->PostTask(
- FROM_HERE, base::Bind(&MediaCodecPlayer::OnStopDone, media_weak_this_));
+ FROM_HERE, base::Bind(&MediaCodecPlayer::OnStopDone, media_weak_this_,
+ DemuxerStream::UNKNOWN));
return;
}
@@ -651,6 +1307,23 @@ void MediaCodecPlayer::RequestToStopDecoders() {
video_decoder_->RequestToStop();
}
+void MediaCodecPlayer::RequestDemuxerSeek(base::TimeDelta seek_time,
+ bool is_browser_seek) {
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+ DVLOG(1) << __FUNCTION__ << " " << seek_time
+ << (is_browser_seek ? " BROWSER_SEEK" : "");
+
+ // Flush decoders before requesting demuxer.
+ audio_decoder_->Flush();
+ video_decoder_->Flush();
+
+ // Save active seek data. Logically it is attached to kStateWaitingForSeek.
+ DCHECK_EQ(kStateWaitingForSeek, state_);
+ seek_info_.reset(new SeekInfo(seek_time, is_browser_seek));
+
+ demuxer_->RequestDemuxerSeek(seek_time, is_browser_seek);
+}
+
void MediaCodecPlayer::ReleaseDecoderResources() {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
DVLOG(1) << __FUNCTION__;
@@ -664,39 +1337,58 @@ void MediaCodecPlayer::ReleaseDecoderResources() {
// At this point decoder threads should not be running
if (interpolator_.interpolating())
interpolator_.StopInterpolating();
+
+ media_stat_->StopAndReport(GetInterpolatedTime());
}
void MediaCodecPlayer::CreateDecoders() {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
DVLOG(1) << __FUNCTION__;
- error_cb_ = base::Bind(&MediaCodecPlayer::OnError, media_weak_this_);
+ internal_error_cb_ = base::Bind(&MediaCodecPlayer::OnError, media_weak_this_);
+
+ media_stat_.reset(new MediaStatistics());
audio_decoder_.reset(new MediaCodecAudioDecoder(
- GetMediaTaskRunner(), base::Bind(&MediaCodecPlayer::RequestDemuxerData,
- media_weak_this_, DemuxerStream::AUDIO),
+ GetMediaTaskRunner(), &media_stat_->audio_frame_stats(),
+ base::Bind(&MediaCodecPlayer::RequestDemuxerData, media_weak_this_,
+ DemuxerStream::AUDIO),
base::Bind(&MediaCodecPlayer::OnStarvation, media_weak_this_,
DemuxerStream::AUDIO),
- base::Bind(&MediaCodecPlayer::OnStopDone, media_weak_this_), error_cb_,
+ base::Bind(&MediaCodecPlayer::OnDecoderDrained, media_weak_this_,
+ DemuxerStream::AUDIO),
+ base::Bind(&MediaCodecPlayer::OnStopDone, media_weak_this_,
+ DemuxerStream::AUDIO),
+ base::Bind(&MediaCodecPlayer::OnMissingKeyReported, media_weak_this_,
+ DemuxerStream::AUDIO),
+ internal_error_cb_,
base::Bind(&MediaCodecPlayer::OnTimeIntervalUpdate, media_weak_this_,
DemuxerStream::AUDIO)));
video_decoder_.reset(new MediaCodecVideoDecoder(
- GetMediaTaskRunner(), base::Bind(&MediaCodecPlayer::RequestDemuxerData,
- media_weak_this_, DemuxerStream::VIDEO),
+ GetMediaTaskRunner(), &media_stat_->video_frame_stats(),
+ base::Bind(&MediaCodecPlayer::RequestDemuxerData, media_weak_this_,
+ DemuxerStream::VIDEO),
base::Bind(&MediaCodecPlayer::OnStarvation, media_weak_this_,
DemuxerStream::VIDEO),
- base::Bind(&MediaCodecPlayer::OnStopDone, media_weak_this_), error_cb_,
- MediaCodecDecoder::SetTimeCallback(), // null callback
- base::Bind(&MediaCodecPlayer::OnVideoResolutionChanged, media_weak_this_),
- base::Bind(&MediaCodecPlayer::OnVideoCodecCreated, media_weak_this_)));
+ base::Bind(&MediaCodecPlayer::OnDecoderDrained, media_weak_this_,
+ DemuxerStream::VIDEO),
+ base::Bind(&MediaCodecPlayer::OnStopDone, media_weak_this_,
+ DemuxerStream::VIDEO),
+ base::Bind(&MediaCodecPlayer::OnMissingKeyReported, media_weak_this_,
+ DemuxerStream::VIDEO),
+ internal_error_cb_,
+ base::Bind(&MediaCodecPlayer::OnTimeIntervalUpdate, media_weak_this_,
+ DemuxerStream::VIDEO),
+ base::Bind(&MediaCodecPlayer::OnVideoResolutionChanged,
+ media_weak_this_)));
}
-bool MediaCodecPlayer::AudioFinished() {
+bool MediaCodecPlayer::AudioFinished() const {
return audio_decoder_->IsCompleted() || !audio_decoder_->HasStream();
}
-bool MediaCodecPlayer::VideoFinished() {
+bool MediaCodecPlayer::VideoFinished() const {
return video_decoder_->IsCompleted() || !video_decoder_->HasStream();
}
@@ -714,13 +1406,17 @@ base::TimeDelta MediaCodecPlayer::GetInterpolatedTime() {
const char* MediaCodecPlayer::AsString(PlayerState state) {
switch (state) {
- RETURN_STRING(STATE_PAUSED);
- RETURN_STRING(STATE_WAITING_FOR_CONFIG);
- RETURN_STRING(STATE_PREFETCHING);
- RETURN_STRING(STATE_PLAYING);
- RETURN_STRING(STATE_STOPPING);
- RETURN_STRING(STATE_WAITING_FOR_SURFACE);
- RETURN_STRING(STATE_ERROR);
+ RETURN_STRING(kStatePaused);
+ RETURN_STRING(kStateWaitingForConfig);
+ RETURN_STRING(kStateWaitingForPermission);
+ RETURN_STRING(kStatePrefetching);
+ RETURN_STRING(kStatePlaying);
+ RETURN_STRING(kStateStopping);
+ RETURN_STRING(kStateWaitingForSurface);
+ RETURN_STRING(kStateWaitingForKey);
+ RETURN_STRING(kStateWaitingForMediaCrypto);
+ RETURN_STRING(kStateWaitingForSeek);
+ RETURN_STRING(kStateError);
}
return nullptr; // crash early
}