Extension syncing: Introduce a NeedsSync pref
[chromium-blink-merge.git] / media / base / android / media_codec_player.cc
blobda1fff17cec50b61a2952154019a911af8305aa5
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/base/android/media_codec_player.h"
7 #include "base/barrier_closure.h"
8 #include "base/bind.h"
9 #include "base/lazy_instance.h"
10 #include "base/logging.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/threading/thread.h"
13 #include "media/base/android/media_codec_audio_decoder.h"
14 #include "media/base/android/media_codec_video_decoder.h"
15 #include "media/base/android/media_player_manager.h"
16 #include "media/base/buffers.h"
18 #define RUN_ON_MEDIA_THREAD(METHOD, ...) \
19 do { \
20 if (!GetMediaTaskRunner()->BelongsToCurrentThread()) { \
21 DCHECK(ui_task_runner_->BelongsToCurrentThread()); \
22 GetMediaTaskRunner()->PostTask( \
23 FROM_HERE, base::Bind(&MediaCodecPlayer::METHOD, media_weak_this_, \
24 ##__VA_ARGS__)); \
25 return; \
26 } \
27 } while (0)
29 namespace media {
31 class MediaThread : public base::Thread {
32 public:
33 MediaThread() : base::Thread("BrowserMediaThread") {
34 Start();
38 // Create media thread
39 base::LazyInstance<MediaThread>::Leaky
40 g_media_thread = LAZY_INSTANCE_INITIALIZER;
43 scoped_refptr<base::SingleThreadTaskRunner> GetMediaTaskRunner() {
44 return g_media_thread.Pointer()->task_runner();
47 // MediaCodecPlayer implementation.
49 MediaCodecPlayer::MediaCodecPlayer(
50 int player_id,
51 base::WeakPtr<MediaPlayerManager> manager,
52 const RequestMediaResourcesCB& request_media_resources_cb,
53 scoped_ptr<DemuxerAndroid> demuxer,
54 const GURL& frame_url)
55 : MediaPlayerAndroid(player_id,
56 manager.get(),
57 request_media_resources_cb,
58 frame_url),
59 ui_task_runner_(base::ThreadTaskRunnerHandle::Get()),
60 demuxer_(demuxer.Pass()),
61 state_(STATE_PAUSED),
62 interpolator_(&default_tick_clock_),
63 pending_start_(false),
64 media_weak_factory_(this) {
65 DCHECK(ui_task_runner_->BelongsToCurrentThread());
67 DVLOG(1) << "MediaCodecPlayer::MediaCodecPlayer: player_id:" << player_id;
69 request_resources_cb_ = base::Bind(request_media_resources_cb_, player_id);
71 completion_cb_ =
72 base::Bind(&MediaPlayerManager::OnPlaybackComplete, manager, player_id);
73 attach_listener_cb_ = base::Bind(&MediaPlayerAndroid::AttachListener,
74 WeakPtrForUIThread(), nullptr);
75 detach_listener_cb_ =
76 base::Bind(&MediaPlayerAndroid::DetachListener, WeakPtrForUIThread());
77 metadata_changed_cb_ = base::Bind(&MediaPlayerAndroid::OnMediaMetadataChanged,
78 WeakPtrForUIThread());
79 time_update_cb_ =
80 base::Bind(&MediaPlayerAndroid::OnTimeUpdate, WeakPtrForUIThread());
82 media_weak_this_ = media_weak_factory_.GetWeakPtr();
84 // Finish initializaton on Media thread
85 GetMediaTaskRunner()->PostTask(
86 FROM_HERE, base::Bind(&MediaCodecPlayer::Initialize, media_weak_this_));
89 MediaCodecPlayer::~MediaCodecPlayer()
91 DVLOG(1) << "MediaCodecPlayer::~MediaCodecPlayer";
92 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
95 void MediaCodecPlayer::Initialize() {
96 DVLOG(1) << __FUNCTION__;
97 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
99 interpolator_.SetUpperBound(base::TimeDelta());
101 CreateDecoders();
103 // This call might in turn call MediaCodecPlayer::OnDemuxerConfigsAvailable()
104 // which propagates configs into decoders. Therefore CreateDecoders() should
105 // be called first.
106 demuxer_->Initialize(this);
109 // The implementation of MediaPlayerAndroid interface.
111 void MediaCodecPlayer::DeleteOnCorrectThread() {
112 DVLOG(1) << __FUNCTION__;
113 DCHECK(ui_task_runner_->BelongsToCurrentThread());
115 DetachListener();
117 // The base class part that deals with MediaPlayerListener
118 // has to be destroyed on UI thread.
119 DestroyListenerOnUIThread();
121 // Post deletion onto Media thread
122 GetMediaTaskRunner()->DeleteSoon(FROM_HERE, this);
125 void MediaCodecPlayer::SetVideoSurface(gfx::ScopedJavaSurface surface) {
126 RUN_ON_MEDIA_THREAD(SetVideoSurface, base::Passed(&surface));
128 DVLOG(1) << __FUNCTION__ << (surface.IsEmpty() ? " empty" : " non-empty");
130 // I assume that if video decoder already has the surface,
131 // there will be two calls:
132 // (1) SetVideoSurface(0)
133 // (2) SetVideoSurface(new_surface)
134 video_decoder_->SetPendingSurface(surface.Pass());
136 if (video_decoder_->HasPendingSurface() &&
137 state_ == STATE_WAITING_FOR_SURFACE) {
138 SetState(STATE_PLAYING);
139 StartPlaybackDecoders();
143 void MediaCodecPlayer::Start() {
144 RUN_ON_MEDIA_THREAD(Start);
146 DVLOG(1) << __FUNCTION__;
148 switch (state_) {
149 case STATE_PAUSED:
150 if (HasAudio() || HasVideo()) {
151 SetState(STATE_PREFETCHING);
152 StartPrefetchDecoders();
153 } else {
154 SetState(STATE_WAITING_FOR_CONFIG);
156 break;
157 case STATE_STOPPING:
158 SetPendingStart(true);
159 break;
160 default:
161 // Ignore
162 break;
166 void MediaCodecPlayer::Pause(bool is_media_related_action) {
167 RUN_ON_MEDIA_THREAD(Pause, is_media_related_action);
169 DVLOG(1) << __FUNCTION__;
171 switch (state_) {
172 case STATE_PREFETCHING:
173 SetState(STATE_PAUSED);
174 StopDecoders();
175 break;
176 case STATE_WAITING_FOR_SURFACE:
177 SetState(STATE_PAUSED);
178 StopDecoders();
179 break;
180 case STATE_PLAYING:
181 SetState(STATE_STOPPING);
182 RequestToStopDecoders();
183 break;
184 default:
185 // Ignore
186 break;
190 void MediaCodecPlayer::SeekTo(base::TimeDelta timestamp) {
191 RUN_ON_MEDIA_THREAD(SeekTo, timestamp);
193 DVLOG(1) << __FUNCTION__ << " " << timestamp;
194 NOTIMPLEMENTED();
197 void MediaCodecPlayer::Release() {
198 RUN_ON_MEDIA_THREAD(Release);
200 DVLOG(1) << __FUNCTION__;
202 SetState(STATE_PAUSED);
203 ReleaseDecoderResources();
206 void MediaCodecPlayer::SetVolume(double volume) {
207 RUN_ON_MEDIA_THREAD(SetVolume, volume);
209 DVLOG(1) << __FUNCTION__ << " " << volume;
210 audio_decoder_->SetVolume(volume);
213 int MediaCodecPlayer::GetVideoWidth() {
214 DCHECK(ui_task_runner_->BelongsToCurrentThread());
215 return metadata_cache_.video_size.width();
218 int MediaCodecPlayer::GetVideoHeight() {
219 DCHECK(ui_task_runner_->BelongsToCurrentThread());
220 return metadata_cache_.video_size.height();
223 base::TimeDelta MediaCodecPlayer::GetCurrentTime() {
224 DCHECK(ui_task_runner_->BelongsToCurrentThread());
225 return current_time_cache_;
228 base::TimeDelta MediaCodecPlayer::GetDuration() {
229 DCHECK(ui_task_runner_->BelongsToCurrentThread());
230 return metadata_cache_.duration;
233 bool MediaCodecPlayer::IsPlaying() {
234 DCHECK(ui_task_runner_->BelongsToCurrentThread());
235 return state_ == STATE_PLAYING;
238 bool MediaCodecPlayer::CanPause() {
239 DCHECK(ui_task_runner_->BelongsToCurrentThread());
240 NOTIMPLEMENTED();
241 return false;
244 bool MediaCodecPlayer::CanSeekForward() {
245 DCHECK(ui_task_runner_->BelongsToCurrentThread());
246 NOTIMPLEMENTED();
247 return false;
250 bool MediaCodecPlayer::CanSeekBackward() {
251 DCHECK(ui_task_runner_->BelongsToCurrentThread());
252 NOTIMPLEMENTED();
253 return false;
256 bool MediaCodecPlayer::IsPlayerReady() {
257 DCHECK(ui_task_runner_->BelongsToCurrentThread());
258 // This method is called to check whether it's safe to release the player when
259 // the OS needs more resources. This class can be released at any time.
260 return true;
263 void MediaCodecPlayer::SetCdm(BrowserCdm* cdm) {
264 DCHECK(ui_task_runner_->BelongsToCurrentThread());
265 NOTIMPLEMENTED();
268 // Callbacks from Demuxer.
270 void MediaCodecPlayer::OnDemuxerConfigsAvailable(
271 const DemuxerConfigs& configs) {
272 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
274 DVLOG(1) << __FUNCTION__;
276 duration_ = configs.duration;
278 SetDemuxerConfigs(configs);
280 // Update cache and notify manager on UI thread
281 gfx::Size video_size = HasVideo() ? configs.video_size : gfx::Size();
282 ui_task_runner_->PostTask(
283 FROM_HERE, base::Bind(metadata_changed_cb_, duration_, video_size));
286 void MediaCodecPlayer::OnDemuxerDataAvailable(const DemuxerData& data) {
287 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
289 DCHECK_LT(0u, data.access_units.size());
290 CHECK_GE(1u, data.demuxer_configs.size());
292 DVLOG(2) << "Player::" << __FUNCTION__;
294 if (data.type == DemuxerStream::AUDIO)
295 audio_decoder_->OnDemuxerDataAvailable(data);
297 if (data.type == DemuxerStream::VIDEO)
298 video_decoder_->OnDemuxerDataAvailable(data);
301 void MediaCodecPlayer::OnDemuxerSeekDone(
302 base::TimeDelta actual_browser_seek_time) {
303 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
305 DVLOG(1) << __FUNCTION__ << " actual_time:" << actual_browser_seek_time;
307 NOTIMPLEMENTED();
310 void MediaCodecPlayer::OnDemuxerDurationChanged(
311 base::TimeDelta duration) {
312 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
313 DVLOG(1) << __FUNCTION__ << " duration:" << duration;
315 duration_ = duration;
318 // Events from Player, called on UI thread
320 void MediaCodecPlayer::OnMediaMetadataChanged(base::TimeDelta duration,
321 const gfx::Size& video_size) {
322 DCHECK(ui_task_runner_->BelongsToCurrentThread());
324 if (duration != kNoTimestamp())
325 metadata_cache_.duration = duration;
327 if (!video_size.IsEmpty())
328 metadata_cache_.video_size = video_size;
330 manager()->OnMediaMetadataChanged(player_id(), metadata_cache_.duration,
331 metadata_cache_.video_size.width(),
332 metadata_cache_.video_size.height(), true);
335 void MediaCodecPlayer::OnTimeUpdate(base::TimeDelta current_timestamp,
336 base::TimeTicks current_time_ticks) {
337 DCHECK(ui_task_runner_->BelongsToCurrentThread());
339 current_time_cache_ = current_timestamp;
340 manager()->OnTimeUpdate(player_id(), current_timestamp, current_time_ticks);
343 // Events from Decoders, called on Media thread
345 void MediaCodecPlayer::RequestDemuxerData(DemuxerStream::Type stream_type) {
346 DVLOG(2) << __FUNCTION__ << " streamType:" << stream_type;
348 // Use this method instead of directly binding with
349 // DemuxerAndroid::RequestDemuxerData() to avoid the race condition on
350 // deletion:
351 // 1. DeleteSoon is posted from UI to Media thread.
352 // 2. RequestDemuxerData callback is posted from Decoder to Media thread.
353 // 3. DeleteSoon arrives, we delete the player and detach from
354 // BrowserDemuxerAndroid.
355 // 4. RequestDemuxerData is processed by the media thread queue. Since the
356 // weak_ptr was invalidated in (3), this is a no-op. If we used
357 // DemuxerAndroid::RequestDemuxerData() it would arrive and will try to
358 // call the client, but the client (i.e. this player) would not exist.
359 demuxer_->RequestDemuxerData(stream_type);
362 void MediaCodecPlayer::OnPrefetchDone() {
363 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
364 DVLOG(1) << __FUNCTION__;
366 if (state_ != STATE_PREFETCHING)
367 return; // Ignore
369 if (!HasAudio() && !HasVideo()) {
370 // No configuration at all after prefetching.
371 // This is an error, initial configuration is expected
372 // before the first data chunk.
373 GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
374 return;
377 if (HasVideo() && !HasPendingSurface()) {
378 SetState(STATE_WAITING_FOR_SURFACE);
379 return;
382 SetState(STATE_PLAYING);
383 StartPlaybackDecoders();
386 void MediaCodecPlayer::OnStopDone() {
387 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
388 DVLOG(1) << __FUNCTION__;
390 if (!(audio_decoder_->IsStopped() && video_decoder_->IsStopped()))
391 return; // Wait until other stream is stopped
393 // At this point decoder threads should not be running
394 if (interpolator_.interpolating())
395 interpolator_.StopInterpolating();
397 switch (state_) {
398 case STATE_STOPPING:
399 if (HasPendingStart()) {
400 SetPendingStart(false);
401 SetState(STATE_PREFETCHING);
402 StartPrefetchDecoders();
403 } else {
404 SetState(STATE_PAUSED);
406 break;
407 case STATE_PLAYING:
408 // Unexpected stop means completion
409 SetState(STATE_PAUSED);
410 break;
411 default:
412 DVLOG(0) << __FUNCTION__ << " illegal state: " << AsString(state_);
413 NOTREACHED();
414 break;
417 // DetachListener to UI thread
418 ui_task_runner_->PostTask(FROM_HERE, detach_listener_cb_);
420 if (AudioFinished() && VideoFinished())
421 ui_task_runner_->PostTask(FROM_HERE, completion_cb_);
424 void MediaCodecPlayer::OnError() {
425 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
426 DVLOG(1) << __FUNCTION__;
428 // STATE_ERROR blocks all events
429 SetState(STATE_ERROR);
431 ReleaseDecoderResources();
434 void MediaCodecPlayer::OnStarvation(DemuxerStream::Type type) {
435 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
436 DVLOG(1) << __FUNCTION__ << " stream type:" << type;
438 if (state_ != STATE_PLAYING)
439 return; // Ignore
441 SetState(STATE_STOPPING);
442 RequestToStopDecoders();
443 SetPendingStart(true);
446 void MediaCodecPlayer::OnTimeIntervalUpdate(DemuxerStream::Type type,
447 base::TimeDelta now_playing,
448 base::TimeDelta last_buffered) {
449 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
451 DVLOG(2) << __FUNCTION__ << ": stream type:" << type << " [" << now_playing
452 << "," << last_buffered << "]";
454 // I assume that audio stream cannot be added after we get configs by
455 // OnDemuxerConfigsAvailable(), but that audio can finish early.
457 if (type == DemuxerStream::VIDEO) {
458 // Ignore video PTS if there is audio stream or if it's behind current
459 // time as set by audio stream.
460 if (!AudioFinished() || now_playing < interpolator_.GetInterpolatedTime())
461 return;
464 interpolator_.SetBounds(now_playing, last_buffered);
466 // Post to UI thread
467 ui_task_runner_->PostTask(FROM_HERE,
468 base::Bind(time_update_cb_, GetInterpolatedTime(),
469 base::TimeTicks::Now()));
472 void MediaCodecPlayer::OnVideoCodecCreated() {
473 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
475 // This callback requests resources by releasing other players.
476 ui_task_runner_->PostTask(FROM_HERE, request_resources_cb_);
479 void MediaCodecPlayer::OnVideoResolutionChanged(const gfx::Size& size) {
480 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
482 DVLOG(1) << __FUNCTION__ << " " << size.width() << "x" << size.height();
484 // Update cache and notify manager on UI thread
485 ui_task_runner_->PostTask(
486 FROM_HERE, base::Bind(metadata_changed_cb_, kNoTimestamp(), size));
489 // State machine operations, called on Media thread
491 void MediaCodecPlayer::SetState(PlayerState new_state) {
492 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
494 DVLOG(1) << "SetState:" << AsString(state_) << " -> " << AsString(new_state);
495 state_ = new_state;
498 void MediaCodecPlayer::SetPendingSurface(gfx::ScopedJavaSurface surface) {
499 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
500 DVLOG(1) << __FUNCTION__;
502 video_decoder_->SetPendingSurface(surface.Pass());
505 bool MediaCodecPlayer::HasPendingSurface() {
506 return video_decoder_->HasPendingSurface();
509 void MediaCodecPlayer::SetPendingStart(bool need_to_start) {
510 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
511 DVLOG(1) << __FUNCTION__ << ": " << need_to_start;
512 pending_start_ = need_to_start;
515 bool MediaCodecPlayer::HasPendingStart() {
516 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
517 return pending_start_;
520 bool MediaCodecPlayer::HasAudio() {
521 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
522 return audio_decoder_->HasStream();
525 bool MediaCodecPlayer::HasVideo() {
526 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
527 return video_decoder_->HasStream();
530 void MediaCodecPlayer::SetDemuxerConfigs(const DemuxerConfigs& configs) {
531 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
532 DVLOG(1) << __FUNCTION__ << " " << configs;
534 DCHECK(audio_decoder_);
535 DCHECK(video_decoder_);
537 // At least one valid codec must be present.
538 DCHECK(configs.audio_codec != kUnknownAudioCodec ||
539 configs.video_codec != kUnknownVideoCodec);
541 if (configs.audio_codec != kUnknownAudioCodec)
542 audio_decoder_->SetDemuxerConfigs(configs);
544 if (configs.video_codec != kUnknownVideoCodec)
545 video_decoder_->SetDemuxerConfigs(configs);
547 if (state_ == STATE_WAITING_FOR_CONFIG) {
548 SetState(STATE_PREFETCHING);
549 StartPrefetchDecoders();
553 void MediaCodecPlayer::StartPrefetchDecoders() {
554 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
555 DVLOG(1) << __FUNCTION__;
557 bool do_audio = false;
558 bool do_video = false;
559 int count = 0;
560 if (!AudioFinished()) {
561 do_audio = true;
562 ++count;
564 if (!VideoFinished()) {
565 do_video = true;
566 ++count;
569 DCHECK_LT(0, count); // at least one decoder should be active
571 base::Closure prefetch_cb = base::BarrierClosure(
572 count, base::Bind(&MediaCodecPlayer::OnPrefetchDone, media_weak_this_));
574 if (do_audio)
575 audio_decoder_->Prefetch(prefetch_cb);
577 if (do_video)
578 video_decoder_->Prefetch(prefetch_cb);
581 void MediaCodecPlayer::StartPlaybackDecoders() {
582 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
583 DVLOG(1) << __FUNCTION__;
585 // Configure all streams before the start since
586 // we may discover that browser seek is required.
588 bool do_audio = !AudioFinished();
589 bool do_video = !VideoFinished();
591 // If there is nothing to play, the state machine should determine
592 // this at the prefetch state and never call this method.
593 DCHECK(do_audio || do_video);
595 if (do_audio) {
596 MediaCodecDecoder::ConfigStatus status = audio_decoder_->Configure();
597 if (status != MediaCodecDecoder::CONFIG_OK) {
598 GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
599 return;
603 if (do_video) {
604 MediaCodecDecoder::ConfigStatus status = video_decoder_->Configure();
605 if (status != MediaCodecDecoder::CONFIG_OK) {
606 GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
607 return;
611 // At this point decoder threads should not be running.
612 if (!interpolator_.interpolating())
613 interpolator_.StartInterpolating();
615 base::TimeDelta current_time = GetInterpolatedTime();
617 if (do_audio) {
618 if (!audio_decoder_->Start(current_time)) {
619 GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
620 return;
623 // Attach listener on UI thread
624 ui_task_runner_->PostTask(FROM_HERE, attach_listener_cb_);
627 if (do_video) {
628 if (!video_decoder_->Start(current_time)) {
629 GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
630 return;
635 void MediaCodecPlayer::StopDecoders() {
636 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
637 DVLOG(1) << __FUNCTION__;
639 audio_decoder_->SyncStop();
640 video_decoder_->SyncStop();
643 void MediaCodecPlayer::RequestToStopDecoders() {
644 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
645 DVLOG(1) << __FUNCTION__;
647 bool do_audio = false;
648 bool do_video = false;
650 if (audio_decoder_->IsPrefetchingOrPlaying())
651 do_audio = true;
652 if (video_decoder_->IsPrefetchingOrPlaying())
653 do_video = true;
655 if (!do_audio && !do_video) {
656 GetMediaTaskRunner()->PostTask(
657 FROM_HERE, base::Bind(&MediaCodecPlayer::OnStopDone, media_weak_this_));
658 return;
661 if (do_audio)
662 audio_decoder_->RequestToStop();
663 if (do_video)
664 video_decoder_->RequestToStop();
667 void MediaCodecPlayer::ReleaseDecoderResources() {
668 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
669 DVLOG(1) << __FUNCTION__;
671 if (audio_decoder_)
672 audio_decoder_->ReleaseDecoderResources();
674 if (video_decoder_)
675 video_decoder_->ReleaseDecoderResources();
677 // At this point decoder threads should not be running
678 if (interpolator_.interpolating())
679 interpolator_.StopInterpolating();
682 void MediaCodecPlayer::CreateDecoders() {
683 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
684 DVLOG(1) << __FUNCTION__;
686 error_cb_ = base::Bind(&MediaCodecPlayer::OnError, media_weak_this_);
688 audio_decoder_.reset(new MediaCodecAudioDecoder(
689 GetMediaTaskRunner(), base::Bind(&MediaCodecPlayer::RequestDemuxerData,
690 media_weak_this_, DemuxerStream::AUDIO),
691 base::Bind(&MediaCodecPlayer::OnStarvation, media_weak_this_,
692 DemuxerStream::AUDIO),
693 base::Bind(&MediaCodecPlayer::OnStopDone, media_weak_this_), error_cb_,
694 base::Bind(&MediaCodecPlayer::OnTimeIntervalUpdate, media_weak_this_,
695 DemuxerStream::AUDIO)));
697 video_decoder_.reset(new MediaCodecVideoDecoder(
698 GetMediaTaskRunner(), base::Bind(&MediaCodecPlayer::RequestDemuxerData,
699 media_weak_this_, DemuxerStream::VIDEO),
700 base::Bind(&MediaCodecPlayer::OnStarvation, media_weak_this_,
701 DemuxerStream::VIDEO),
702 base::Bind(&MediaCodecPlayer::OnStopDone, media_weak_this_), error_cb_,
703 base::Bind(&MediaCodecPlayer::OnTimeIntervalUpdate, media_weak_this_,
704 DemuxerStream::VIDEO),
705 base::Bind(&MediaCodecPlayer::OnVideoResolutionChanged, media_weak_this_),
706 base::Bind(&MediaCodecPlayer::OnVideoCodecCreated, media_weak_this_)));
709 bool MediaCodecPlayer::AudioFinished() {
710 return audio_decoder_->IsCompleted() || !audio_decoder_->HasStream();
713 bool MediaCodecPlayer::VideoFinished() {
714 return video_decoder_->IsCompleted() || !video_decoder_->HasStream();
717 base::TimeDelta MediaCodecPlayer::GetInterpolatedTime() {
718 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
720 base::TimeDelta interpolated_time = interpolator_.GetInterpolatedTime();
721 return std::min(interpolated_time, duration_);
724 #undef RETURN_STRING
725 #define RETURN_STRING(x) \
726 case x: \
727 return #x;
729 const char* MediaCodecPlayer::AsString(PlayerState state) {
730 switch (state) {
731 RETURN_STRING(STATE_PAUSED);
732 RETURN_STRING(STATE_WAITING_FOR_CONFIG);
733 RETURN_STRING(STATE_PREFETCHING);
734 RETURN_STRING(STATE_PLAYING);
735 RETURN_STRING(STATE_STOPPING);
736 RETURN_STRING(STATE_WAITING_FOR_SURFACE);
737 RETURN_STRING(STATE_ERROR);
739 return nullptr; // crash early
742 #undef RETURN_STRING
744 } // namespace media