Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(502)

Side by Side Diff: media/base/android/media_codec_player.cc

Issue 1128383003: Implementation of MediaCodecPlayer stage 1 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Removed unused media_codec_player_state.h and .cc Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/base/android/media_codec_player.h" 5 #include "media/base/android/media_codec_player.h"
6 6
7 #include "base/barrier_closure.h"
7 #include "base/bind.h" 8 #include "base/bind.h"
8 #include "base/lazy_instance.h" 9 #include "base/lazy_instance.h"
9 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/threading/thread.h"
12
wolenetz 2015/05/19 23:05:23 nit: drop extra line
Tima Vaisburd 2015/05/22 22:48:55 Done.
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"
10 17
11 #define RUN_ON_MEDIA_THREAD(METHOD, ...) \ 18 #define RUN_ON_MEDIA_THREAD(METHOD, ...) \
12 do { \ 19 do { \
13 if (!GetMediaTaskRunner()->BelongsToCurrentThread()) { \ 20 if (!GetMediaTaskRunner()->BelongsToCurrentThread()) { \
14 GetMediaTaskRunner()->PostTask( \ 21 GetMediaTaskRunner()->PostTask( \
15 FROM_HERE, \ 22 FROM_HERE, \
16 base::Bind(&MediaCodecPlayer:: METHOD, weak_this_, ##__VA_ARGS__)); \ 23 base::Bind(&MediaCodecPlayer:: METHOD, weak_this_, ##__VA_ARGS__)); \
17 return; \ 24 return; \
18 } \ 25 } \
19 } while(0) 26 } while(0)
(...skipping 10 matching lines...) Expand all
30 37
31 // Create media thread 38 // Create media thread
32 base::LazyInstance<MediaThread>::Leaky 39 base::LazyInstance<MediaThread>::Leaky
33 g_media_thread = LAZY_INSTANCE_INITIALIZER; 40 g_media_thread = LAZY_INSTANCE_INITIALIZER;
34 41
35 42
36 scoped_refptr<base::SingleThreadTaskRunner> GetMediaTaskRunner() { 43 scoped_refptr<base::SingleThreadTaskRunner> GetMediaTaskRunner() {
37 return g_media_thread.Pointer()->task_runner(); 44 return g_media_thread.Pointer()->task_runner();
38 } 45 }
39 46
47
qinmin 2015/05/19 19:26:25 nit: remove the extra line.
Tima Vaisburd 2015/05/20 05:19:58 Done.
40 // MediaCodecPlayer implementation. 48 // MediaCodecPlayer implementation.
41 49
50 /*
wolenetz 2015/05/19 23:05:23 nit: please move the state machine diagram to the
51 State machine diagram.
52
53
54 --------------------------> Paused
55 | StopDone:
56 | ^ |
57 | | |
58 | Pause: | | Start: dec.Prefetch
59 | | |
60 | | |
61 | | |
62 | | v PrefetchDone,
63 | no surface:
64 | ------------------> Prefetching --------------> Waiting
65 | | for surface
66 | | | /
67 | | | /
68 | | StopDone w/ | /
69 | | pending start: | PrefetchDone: /
70 | | dec.Prefetch | dec.Start /
71 | | | / SetSurface:
72 | | | / dec.Start
73 | | | /
74 | | v /
75 | | /
76 | | Playing <----------/
77 | |
78 | | |
79 | | |
80 | | | Pause: dec.RequestToStop
81 | | |
82 | | |
83 | | v
84 | |
85 -------------------------- Stopping
86
87 */
88
42 MediaCodecPlayer::MediaCodecPlayer( 89 MediaCodecPlayer::MediaCodecPlayer(
43 int player_id, 90 int player_id,
44 MediaPlayerManager* manager, 91 base::WeakPtr<MediaPlayerManager> manager,
45 const RequestMediaResourcesCB& request_media_resources_cb, 92 const RequestMediaResourcesCB& request_media_resources_cb,
46 scoped_ptr<DemuxerAndroid> demuxer, 93 scoped_ptr<DemuxerAndroid> demuxer,
47 const GURL& frame_url) 94 const GURL& frame_url)
48 : MediaPlayerAndroid(player_id, 95 : MediaPlayerAndroid(player_id,
49 manager, 96 manager.get(),
50 request_media_resources_cb, 97 request_media_resources_cb,
51 frame_url), 98 frame_url),
52 ui_task_runner_(base::MessageLoopProxy::current()), 99 ui_task_runner_(base::MessageLoopProxy::current()),
53 demuxer_(demuxer.Pass()), 100 demuxer_(demuxer.Pass()),
101 state_(STATE_PAUSED),
102 interpolator_(&default_tick_clock_),
103 pending_start_(false),
54 weak_factory_(this) { 104 weak_factory_(this) {
55 // UI thread 105 // UI thread
56 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 106 DCHECK(ui_task_runner_->BelongsToCurrentThread());
57 107
58 DVLOG(1) << "MediaCodecPlayer::MediaCodecPlayer: player_id:" << player_id; 108 DVLOG(1) << "MediaCodecPlayer::MediaCodecPlayer: player_id:" << player_id;
59 109
110 request_resources_cb_ = base::Bind(request_media_resources_cb_, player_id);
111
112 metadata_changed_cb_ = base::Bind(
113 &MediaPlayerManager::OnMediaMetadataChanged, manager, player_id);
114 time_update_cb_ = base::Bind(
115 &MediaPlayerManager::OnTimeUpdate, manager, player_id);
116 completion_cb_ = base::Bind(
117 &MediaPlayerManager::OnPlaybackComplete, manager, player_id);
118 attach_listener_cb_ = base::Bind(
119 &MediaPlayerAndroid::AttachListener, WeakPtrForUIThread(), nullptr);
120 detach_listener_cb_ = base::Bind(
121 &MediaPlayerAndroid::DetachListener, WeakPtrForUIThread());
122
60 weak_this_ = weak_factory_.GetWeakPtr(); 123 weak_this_ = weak_factory_.GetWeakPtr();
61 124
62 // Finish initializaton on Media thread 125 // Finish initializaton on Media thread
63 GetMediaTaskRunner()->PostTask( 126 GetMediaTaskRunner()->PostTask(
64 FROM_HERE, base::Bind(&MediaCodecPlayer::Initialize, weak_this_)); 127 FROM_HERE, base::Bind(&MediaCodecPlayer::Initialize, weak_this_));
65 } 128 }
66 129
67 MediaCodecPlayer::~MediaCodecPlayer() 130 MediaCodecPlayer::~MediaCodecPlayer()
68 { 131 {
69 // Media thread 132 // Media thread
70 DVLOG(1) << "MediaCodecPlayer::~MediaCodecPlayer"; 133 DVLOG(1) << "MediaCodecPlayer::~MediaCodecPlayer";
71 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread()); 134 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
135
136 // Stop decoder threads before destroying decoders
137 ReleaseDecoderResources();
72 } 138 }
73 139
74 void MediaCodecPlayer::Initialize() { 140 void MediaCodecPlayer::Initialize() {
75 // Media thread 141 // Media thread
76 DVLOG(1) << __FUNCTION__; 142 DVLOG(1) << __FUNCTION__;
77 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread()); 143 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
78 144
79 demuxer_->Initialize(this); 145 demuxer_->Initialize(this);
146
147 interpolator_.SetUpperBound(base::TimeDelta());
qinmin 2015/05/19 19:26:25 Can you call this without calling startInterpolati
148
149 CreateDecoders();
80 } 150 }
81 151
82 // MediaPlayerAndroid implementation. 152 // The implementation of MediaPlayerAndroid interface.
83 153
84 void MediaCodecPlayer::DeleteOnCorrectThread() { 154 void MediaCodecPlayer::DeleteOnCorrectThread() {
85 // UI thread 155 // UI thread
86 DVLOG(1) << __FUNCTION__; 156 DVLOG(1) << __FUNCTION__;
87 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 157 DCHECK(ui_task_runner_->BelongsToCurrentThread());
88 158
89 // The listener-related portion of the base class has to be 159 DetachListener();
90 // destroyed on UI thread. 160
161 // The base class part that deals with MediaPlayerListener
162 // has to be destroyed on UI thread.
91 DestroyListenerOnUIThread(); 163 DestroyListenerOnUIThread();
92 164
93 // Post deletion onto Media thread 165 // Post deletion onto Media thread
94 GetMediaTaskRunner()->DeleteSoon(FROM_HERE, this); 166 GetMediaTaskRunner()->DeleteSoon(FROM_HERE, this);
95 } 167 }
96 168
97 void MediaCodecPlayer::SetVideoSurface(gfx::ScopedJavaSurface surface) { 169 void MediaCodecPlayer::SetVideoSurface(gfx::ScopedJavaSurface surface) {
98 RUN_ON_MEDIA_THREAD(SetVideoSurface, base::Passed(&surface)); 170 RUN_ON_MEDIA_THREAD(SetVideoSurface, base::Passed(&surface));
99 171
100 // Media thread 172 // Media thread
101 DVLOG(1) << __FUNCTION__; 173 DVLOG(1) << __FUNCTION__ << (surface.IsEmpty() ? " empty" : " non-empty");
102 174
103 NOTIMPLEMENTED(); 175 // I assume that if video decoder already has the surface,
176 // there will be two calls:
177 // (1) SetVideoSurface(0)
178 // (2) SetVideoSurface(new_surface)
179 video_decoder_->SetPendingSurface(surface.Pass());
180
181 if (video_decoder_->HasPendingSurface() &&
qinmin 2015/05/19 19:26:25 nit: extra space after "if"
Tima Vaisburd 2015/05/20 05:19:59 Done.
182 state_ == STATE_WAITING_FOR_SURFACE) {
183 SetState(STATE_PLAYING);
184 StartPlaybackDecoders();
185 }
104 } 186 }
105 187
106 void MediaCodecPlayer::Start() { 188 void MediaCodecPlayer::Start() {
107 RUN_ON_MEDIA_THREAD(Start); 189 RUN_ON_MEDIA_THREAD(Start);
108 190
109 // Media thread 191 // Media thread
110 DVLOG(1) << __FUNCTION__; 192 DVLOG(1) << __FUNCTION__;
111 193
112 NOTIMPLEMENTED(); 194 switch (state_) {
195 case STATE_PAUSED:
196 SetState(STATE_PREFETCHING);
197 StartPrefetchDecoders();
198 break;
199 case STATE_STOPPING:
200 SetPendingStart(true);
201 break;
202 default:
203 // Ignore
204 break;
205 }
113 } 206 }
114 207
115 void MediaCodecPlayer::Pause(bool is_media_related_action) { 208 void MediaCodecPlayer::Pause(bool is_media_related_action) {
116 RUN_ON_MEDIA_THREAD(Pause, is_media_related_action); 209 RUN_ON_MEDIA_THREAD(Pause, is_media_related_action);
117 210
118 // Media thread 211 // Media thread
119 DVLOG(1) << __FUNCTION__; 212 DVLOG(1) << __FUNCTION__;
120 213
121 NOTIMPLEMENTED(); 214 switch (state_) {
215 case STATE_PREFETCHING:
216 SetState(STATE_PAUSED);
217 StopDecoders();
218 break;
219 case STATE_WAITING_FOR_CONFIG:
220 SetState(STATE_PAUSED);
221 break;
222 case STATE_WAITING_FOR_SURFACE:
223 SetState(STATE_PAUSED);
224 StopDecoders();
225 break;
226 case STATE_PLAYING:
227 SetState(STATE_STOPPING);
228 RequestToStopDecoders();
229 break;
230 default:
231 // Ignore
232 break;
233 }
122 } 234 }
123 235
124 void MediaCodecPlayer::SeekTo(base::TimeDelta timestamp) { 236 void MediaCodecPlayer::SeekTo(base::TimeDelta timestamp) {
125 RUN_ON_MEDIA_THREAD(SeekTo, timestamp); 237 RUN_ON_MEDIA_THREAD(SeekTo, timestamp);
126 238
127 // Media thread 239 // Media thread
128 DVLOG(1) << __FUNCTION__ << " " << timestamp; 240 DVLOG(1) << __FUNCTION__ << " " << timestamp;
129
130 NOTIMPLEMENTED(); 241 NOTIMPLEMENTED();
131 } 242 }
132 243
133 void MediaCodecPlayer::Release() { 244 void MediaCodecPlayer::Release() {
134 RUN_ON_MEDIA_THREAD(Release); 245 RUN_ON_MEDIA_THREAD(Release);
135 246
136 // Media thread 247 // Media thread
137 DVLOG(1) << __FUNCTION__; 248 DVLOG(1) << __FUNCTION__;
138 249
139 NOTIMPLEMENTED(); 250 SetState(STATE_PAUSED);
251 ReleaseDecoderResources();
140 } 252 }
141 253
142 void MediaCodecPlayer::SetVolume(double volume) { 254 void MediaCodecPlayer::SetVolume(double volume) {
143 RUN_ON_MEDIA_THREAD(SetVolume, volume); 255 RUN_ON_MEDIA_THREAD(SetVolume, volume);
144 256
145 // Media thread 257 // Media thread
146 DVLOG(1) << __FUNCTION__ << " " << volume; 258 DVLOG(1) << __FUNCTION__ << " " << volume;
147 259 audio_decoder_->SetVolume(volume);
148 NOTIMPLEMENTED();
149 } 260 }
150 261
151 int MediaCodecPlayer::GetVideoWidth() { 262 int MediaCodecPlayer::GetVideoWidth() {
152 // UI thread 263 // UI thread, Media thread
qinmin 2015/05/19 19:26:25 It is bad that these calls can span multiple threa
Tima Vaisburd 2015/05/20 05:19:58 I did the caching but I'm not sure I followed the
153 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 264 return video_decoder_->GetVideoWidth();
154
155 NOTIMPLEMENTED();
156 return 320;
157 } 265 }
158 266
159 int MediaCodecPlayer::GetVideoHeight() { 267 int MediaCodecPlayer::GetVideoHeight() {
160 // UI thread 268 // UI thread, Media thread
qinmin 2015/05/19 19:26:26 ditto
Tima Vaisburd 2015/05/20 05:19:59 Same as above.
161 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 269 return video_decoder_->GetVideoHeight();
162
163 NOTIMPLEMENTED();
164 return 240;
165 } 270 }
166 271
167 base::TimeDelta MediaCodecPlayer::GetCurrentTime() { 272 base::TimeDelta MediaCodecPlayer::GetCurrentTime() {
168 // UI thread, Media thread 273 // UI thread, Media thread
169 NOTIMPLEMENTED(); 274 base::TimeDelta interpolator_result;
170 return base::TimeDelta(); 275 {
276 base::AutoLock lock(interpolator_lock_);
277 interpolator_result = interpolator_.GetInterpolatedTime();
278 }
279 return std::min(interpolator_result, duration_);
171 } 280 }
172 281
173 base::TimeDelta MediaCodecPlayer::GetDuration() { 282 base::TimeDelta MediaCodecPlayer::GetDuration() {
174 // UI thread 283 // UI thread
175 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 284 DCHECK(ui_task_runner_->BelongsToCurrentThread());
176 285 return duration_;
177 NOTIMPLEMENTED();
178 return base::TimeDelta();
179 } 286 }
180 287
181 bool MediaCodecPlayer::IsPlaying() { 288 bool MediaCodecPlayer::IsPlaying() {
182 // UI thread 289 // UI thread
183 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 290 DCHECK(ui_task_runner_->BelongsToCurrentThread());
184 NOTIMPLEMENTED(); 291 return state_ == STATE_PLAYING;
185 return false;
186 } 292 }
187 293
188 bool MediaCodecPlayer::CanPause() { 294 bool MediaCodecPlayer::CanPause() {
189 // UI thread 295 // UI thread
190 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 296 DCHECK(ui_task_runner_->BelongsToCurrentThread());
191 NOTIMPLEMENTED(); 297 NOTIMPLEMENTED();
192 return false; 298 return false;
193 } 299 }
194 300
195 bool MediaCodecPlayer::CanSeekForward() { 301 bool MediaCodecPlayer::CanSeekForward() {
196 // UI thread 302 // UI thread
197 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 303 DCHECK(ui_task_runner_->BelongsToCurrentThread());
198 NOTIMPLEMENTED(); 304 NOTIMPLEMENTED();
199 return false; 305 return false;
200 } 306 }
201 307
202 bool MediaCodecPlayer::CanSeekBackward() { 308 bool MediaCodecPlayer::CanSeekBackward() {
203 // UI thread 309 // UI thread
204 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 310 DCHECK(ui_task_runner_->BelongsToCurrentThread());
205 NOTIMPLEMENTED(); 311 NOTIMPLEMENTED();
206 return false; 312 return false;
207 } 313 }
208 314
209 bool MediaCodecPlayer::IsPlayerReady() { 315 bool MediaCodecPlayer::IsPlayerReady() {
210 // UI thread 316 // UI thread
211 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 317 DCHECK(ui_task_runner_->BelongsToCurrentThread());
212 NOTIMPLEMENTED(); 318 // This method is called to check whether it' save to release
319 // this player if the OS needs more resources.
320 // For this player Release() can be called at any time.
213 return true; 321 return true;
214 } 322 }
215 323
216 void MediaCodecPlayer::SetCdm(BrowserCdm* cdm) { 324 void MediaCodecPlayer::SetCdm(BrowserCdm* cdm) {
217 // UI thread 325 // UI thread
218 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 326 DCHECK(ui_task_runner_->BelongsToCurrentThread());
219 NOTIMPLEMENTED(); 327 NOTIMPLEMENTED();
220 } 328 }
221 329
222 // Callbacks from Demuxer. 330 // Callbacks from Demuxer.
223 331
224 void MediaCodecPlayer::OnDemuxerConfigsAvailable( 332 void MediaCodecPlayer::OnDemuxerConfigsAvailable(
225 const DemuxerConfigs& configs) { 333 const DemuxerConfigs& configs) {
226 // Media thread 334 // Media thread
227 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread()); 335 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
228 336
229 NOTIMPLEMENTED(); 337 duration_ = configs.duration;
338
339 SetDemuxerConfigs(configs);
340
341 switch (state_) {
342 case STATE_WAITING_FOR_CONFIG:
343 if (HasVideo() && !HasPendingSurface()) {
344 SetState(STATE_WAITING_FOR_SURFACE);
345 } else {
346 SetState(STATE_PLAYING);
qinmin 2015/05/19 19:26:25 No idea why the player has to start playing here.
Tima Vaisburd 2015/05/20 05:19:58 But below you confirmed that this state is impossi
qinmin 2015/05/20 15:39:25 Prefetching and OnDemuxerConfigsAvailable are 2 di
Tima Vaisburd 2015/05/20 17:34:56 Please take a look at PatchSet 9 where I changed t
347 StartPlaybackDecoders();
348 }
349 break;
350 default:
351 // Do nothing
352 break;
353 }
354
355 // Post on UI thread
356 ui_task_runner_->PostTask(
357 FROM_HERE,
358 base::Bind(metadata_changed_cb_, duration_,
359 GetVideoWidth(), GetVideoHeight(), true));
230 } 360 }
231 361
232 void MediaCodecPlayer::OnDemuxerDataAvailable(const DemuxerData& data) { 362 void MediaCodecPlayer::OnDemuxerDataAvailable(const DemuxerData& data) {
233 // Media thread 363 // Media thread
234 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread()); 364 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
235 NOTIMPLEMENTED(); 365
366 DCHECK_LT(0u, data.access_units.size());
367 CHECK_GE(1u, data.demuxer_configs.size());
368
369 DVLOG(2) << "Player::" << __FUNCTION__;
370
371 if (data.type == DemuxerStream::AUDIO)
372 audio_decoder_->OnDemuxerDataAvailable(data);
373
374 if (data.type == DemuxerStream::VIDEO)
375 video_decoder_->OnDemuxerDataAvailable(data);
236 } 376 }
237 377
238 void MediaCodecPlayer::OnDemuxerSeekDone( 378 void MediaCodecPlayer::OnDemuxerSeekDone(
239 base::TimeDelta actual_browser_seek_time) { 379 base::TimeDelta actual_browser_seek_time) {
240 // Media thread 380 // Media thread
241 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread()); 381 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
382
383 DVLOG(1) << __FUNCTION__ << " actual_time:" << actual_browser_seek_time;
384
242 NOTIMPLEMENTED(); 385 NOTIMPLEMENTED();
243 } 386 }
244 387
245 void MediaCodecPlayer::OnDemuxerDurationChanged( 388 void MediaCodecPlayer::OnDemuxerDurationChanged(
246 base::TimeDelta duration) { 389 base::TimeDelta duration) {
247 // Media thread 390 // Media thread
248 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread()); 391 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
249 NOTIMPLEMENTED(); 392 DVLOG(1) << __FUNCTION__ << " duration:" << duration;
393 duration_ = duration;
394 }
395
qinmin 2015/05/19 19:26:25 nit: extra line
Tima Vaisburd 2015/05/20 05:19:58 Done.
396
397 // Events from Decoders, called on Media thread
398
399 void MediaCodecPlayer::RequestDemuxerData(DemuxerStream::Type stream_type) {
400 DVLOG(1) << __FUNCTION__ << " streamType:" << stream_type;
401
402 // Use this method instead of directly binding with
403 // DemuxerAndroid::RequestDemuxerData() to avoid the race condition
wolenetz 2015/05/19 23:05:23 nit: remove extra space after 'the'
Tima Vaisburd 2015/05/22 22:48:55 Done.
404 // on deletion:
405 // 1. DestroySelf is posted from UI to Media thread.
wolenetz 2015/05/19 23:05:23 What is DestroySelf? It's not in existence in this
Tima Vaisburd 2015/05/22 22:48:54 Yes, exactly. Fixed and reformulated slightly.
406 // 2. RequestDemuxerData callback is posted from Decoder to Media thread.
407 // 3. DestroySelf arrives, we delete the player and detach from
408 // BrowserDemuxerAndroid.
409 // 4. RequestDemuxerData is sent to the player, but weak_ptr blocks it.
410 // If we used DemuxerAndroid::RequestDemuxerData() it would arrive,
411 // but the client (i.e. this player) would not exist.
412 demuxer_->RequestDemuxerData(stream_type);
413 }
414
415 void MediaCodecPlayer::OnPrefetchDone() {
416 // Media thread
wolenetz 2015/05/19 23:05:23 Here, and elsewhere, a DCHECK ( I am on the right
Tima Vaisburd 2015/05/22 22:48:55 I found it easier to grasp a short comment written
417 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
418 DVLOG(1) << __FUNCTION__;
419
420 if (state_ != STATE_PREFETCHING)
wolenetz 2015/05/19 23:05:23 When might this happen with this new player/decode
Tima Vaisburd 2015/05/22 22:48:54 Release() may be called on Media thread before we
421 return; // Ignore
422
423 if (!HasAudio() && !HasVideo()) {
424 // No configuration at all, let's wait for it.
qinmin 2015/05/19 19:26:25 This shouldn't happen. I would just put NOT_REACHE
Tima Vaisburd 2015/05/20 05:19:59 I raised an error
425 // Although it probably should never happen here,
426 // after the data has come from demuxer.
427 SetState(STATE_WAITING_FOR_CONFIG);
428 return;
429 }
430
431 if (HasVideo() && !HasPendingSurface()) {
432 SetState(STATE_WAITING_FOR_SURFACE);
wolenetz 2015/05/19 23:05:23 I'm not sure how this happens given the current co
Tima Vaisburd 2015/05/22 22:48:54 Yes, the surface can be removed at any time, but c
433 return;
434 }
435
436 SetState(STATE_PLAYING);
437 StartPlaybackDecoders();
438 }
439
440 void MediaCodecPlayer::OnStarvation() {
441 // Media thread
442 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
443 DVLOG(1) << __FUNCTION__;
444
445 if (state_ != STATE_PLAYING)
446 return; // Ignore
447
448 SetState(STATE_STOPPING);
449 RequestToStopDecoders();
wolenetz 2015/05/19 23:05:23 10,000' view: why does starvation stop decoders?
Tima Vaisburd 2015/05/22 22:48:54 I think we need to stop the other channel... Upon
450 SetPendingStart(true);
451 }
452
453 void MediaCodecPlayer::OnStopDone() {
454 // Media thread
455 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
456 DVLOG(1) << __FUNCTION__;
457
458 if (!(audio_decoder_->IsStopped() && video_decoder_->IsStopped()))
459 return; // Wait until other stream is stopped
460
461 // At this point decoder threads should not be running
462 if (interpolator_.interpolating())
463 interpolator_.StopInterpolating();
464
465 switch (state_) {
466 case STATE_STOPPING:
467 if (HasPendingStart()) {
468 SetState(STATE_PREFETCHING);
469 StartPrefetchDecoders();
470 } else {
471 SetState(STATE_PAUSED);
472 }
473 break;
474 case STATE_PLAYING:
475 // Unexpected stop means completion
476 SetState(STATE_PAUSED);
477 break;
478 default:
479 NOTREACHED();
480 break;
481 }
482
483 // DetachListener to UI thread
484 ui_task_runner_->PostTask(FROM_HERE, detach_listener_cb_);
485
486 if (AudioFinished() && VideoFinished())
487 ui_task_runner_->PostTask(FROM_HERE, completion_cb_);
488 }
489
490 void MediaCodecPlayer::OnError() {
491 // Media thread
492 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
493 DVLOG(1) << __FUNCTION__;
494
495 // STATE_ERROR blocks all events
496 SetState(STATE_ERROR);
497
498 ReleaseDecoderResources();
499 }
500
501 // Event from Decoders, called on Decoder thread
502
503 void MediaCodecPlayer::OnTimeIntervalUpdate(base::TimeDelta now_playing,
504 base::TimeDelta last_buffered) {
505 // Decoder thread
506 base::TimeDelta current_time;
507 {
508 base::AutoLock lock(interpolator_lock_);
509 interpolator_.SetBounds(now_playing, last_buffered);
510
511 current_time = std::min(interpolator_.GetInterpolatedTime(), duration_);
512 }
513
514 // Post on UI thread
515 ui_task_runner_->PostTask(
516 FROM_HERE,
517 base::Bind(time_update_cb_, current_time, base::TimeTicks::Now()));
518 }
519
520 // State machine operations, called on Media thread
521
522 void MediaCodecPlayer::SetState(PlayerState new_state) {
523 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
524
525 DVLOG(1) << "SetState:" << AsString(state_) << " -> " << AsString(new_state);
526 state_ = new_state;
527 }
528
529 #undef ENTRY
530 #define ENTRY(x) case x: return #x;
531
532 const char* MediaCodecPlayer::AsString(PlayerState state) {
533 switch (state) {
534 ENTRY(STATE_PAUSED);
535 ENTRY(STATE_PREFETCHING);
536 ENTRY(STATE_PLAYING);
537 ENTRY(STATE_STOPPING);
538 ENTRY(STATE_WAITING_FOR_CONFIG);
539 ENTRY(STATE_WAITING_FOR_SURFACE);
540 ENTRY(STATE_WAITING_FOR_SEEK);
541 ENTRY(STATE_ERROR);
542 default: return "Unknown PlayerState";
543 }
544 }
545 #undef ENTRY
546
547 void MediaCodecPlayer::SetPendingSurface(gfx::ScopedJavaSurface surface) {
548 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
549 DVLOG(1) << __FUNCTION__;
550
551 video_decoder_->SetPendingSurface(surface.Pass());
552 }
553
554 bool MediaCodecPlayer::HasPendingSurface() {
555 return video_decoder_->HasPendingSurface();
556 }
557
558 void MediaCodecPlayer::SetPendingStart(bool need_to_start) {
559 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
560 DVLOG(1) << __FUNCTION__ << ": " << need_to_start;
561 pending_start_ = need_to_start;
562 }
563
564 bool MediaCodecPlayer::HasPendingStart() {
qinmin 2015/05/19 19:26:25 The function name just don't match what it does. H
Tima Vaisburd 2015/05/20 05:19:58 Right now called two methods since so far HasPendi
565 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
566 // Return and reset.
567 bool ret = pending_start_;
568 pending_start_ = false;
569 return ret;
570 }
571
572 bool MediaCodecPlayer::HasAudio() {
573 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
574 return audio_decoder_->HasStream();
575 }
576
577 bool MediaCodecPlayer::HasVideo() {
578 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
579 return video_decoder_->HasStream();
580 }
581
582 void MediaCodecPlayer::CreateDecoders() {
583 // Media thread
584 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
585 DVLOG(1) << __FUNCTION__;
586
587 error_cb_ = base::Bind(&MediaCodecPlayer::OnError, weak_this_);
588
589 audio_decoder_.reset(new MediaCodecAudioDecoder(
590 GetMediaTaskRunner(),
591 ui_task_runner_,
592 base::Bind(&MediaCodecPlayer::RequestDemuxerData, weak_this_,
593 DemuxerStream::AUDIO),
594 base::Bind(&MediaCodecPlayer::OnStarvation, weak_this_),
595 base::Bind(&MediaCodecPlayer::OnStopDone, weak_this_),
596 error_cb_,
597 base::Bind(&MediaCodecPlayer::OnTimeIntervalUpdate,
598 base::Unretained(this))));
599
600 video_decoder_.reset(new MediaCodecVideoDecoder(
601 GetMediaTaskRunner(),
602 ui_task_runner_,
603 base::Bind(&MediaCodecPlayer::RequestDemuxerData, weak_this_,
604 DemuxerStream::VIDEO),
605 base::Bind(&MediaCodecPlayer::OnStarvation, weak_this_),
606 base::Bind(&MediaCodecPlayer::OnStopDone, weak_this_),
607 error_cb_,
608 request_resources_cb_));
qinmin 2015/05/19 19:26:25 request_resources_cb_ is binded to the UI thread,
Tima Vaisburd 2015/05/20 05:19:58 ui_task_runner_ is accessible from VideoDecoder vi
qinmin 2015/05/20 15:48:16 I don't feel it proper for Decoder to know ui_task
Tima Vaisburd 2015/05/22 23:37:52 We have two callbacks now, both from the video dec
qinmin 2015/05/26 00:49:19 No, i don't that's logically sound. And I don't th
609 }
610
611 bool MediaCodecPlayer::AudioFinished() {
612 return audio_decoder_->IsCompleted() || !audio_decoder_->HasStream();
613 }
614
615 bool MediaCodecPlayer::VideoFinished() {
616 return video_decoder_->IsCompleted() || !video_decoder_->HasStream();
617 }
618
619 void MediaCodecPlayer::SetDemuxerConfigs(const DemuxerConfigs& configs) {
620 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
621 DVLOG(1) << __FUNCTION__;
622
623 if (configs.audio_codec != kUnknownAudioCodec)
624 audio_decoder_->SetDemuxerConfigs(configs);
625
626 if (configs.video_codec != kUnknownVideoCodec)
627 video_decoder_->SetDemuxerConfigs(configs);
628 }
629
630 void MediaCodecPlayer::StartPrefetchDecoders() {
631 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
632 DVLOG(1) << __FUNCTION__;
633
634 bool do_audio = false;
635 bool do_video = false;
636 int count = 0;
637 if (!AudioFinished()) {
638 do_audio = true;
639 ++count;
640 }
641 if (!VideoFinished()) {
642 do_video = true;
643 ++count;
644 }
645
646 DCHECK_LT(0, count); // at least one decoder should be active
647
648 base::Closure prefetch_cb = base::BarrierClosure(
649 count, base::Bind(&MediaCodecPlayer::OnPrefetchDone, weak_this_));
650
651 if (do_audio)
652 audio_decoder_->Prefetch(prefetch_cb);
653
654 if (do_video)
655 video_decoder_->Prefetch(prefetch_cb);
656 }
657
658 void MediaCodecPlayer::StartPlaybackDecoders() {
659 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
660 DVLOG(1) << __FUNCTION__;
661
662 // Configure all streams before the start since
663 // we may discover that browser seek is required.
664
665 bool do_audio = !AudioFinished();
666 bool do_video = !VideoFinished();
667
668 // If there is nothing to play, the state machine should determine
669 // this at the prefetch state and never call this method.
670 DCHECK(do_audio || do_video);
671
672 if (do_audio) {
673 MediaCodecDecoder::ConfigStatus status = audio_decoder_->Configure();
674 if (status != MediaCodecDecoder::CONFIG_OK) {
675 GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
676 return;
677 }
678 }
679
680 if (do_video) {
681 MediaCodecDecoder::ConfigStatus status = video_decoder_->Configure();
682 if (status != MediaCodecDecoder::CONFIG_OK) {
683 GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
684 return;
685 }
686 }
687
688 // At this point decoder threads should not be running
689 if (!interpolator_.interpolating())
690 interpolator_.StartInterpolating();
691
692 if (do_audio) {
693 if (!audio_decoder_->Start(GetCurrentTime())) {
694 GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
695 return;
696 }
697
698 // Attach listener on UI thread
699 ui_task_runner_->PostTask(FROM_HERE, attach_listener_cb_);
700 }
701
702 if (do_video) {
703 if (!video_decoder_->Start(GetCurrentTime())) {
704 GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
705 return;
706 }
707 }
708 }
709
710 void MediaCodecPlayer::StopDecoders() {
711 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
712 DVLOG(1) << __FUNCTION__;
713
714 audio_decoder_->SyncStop();
715 video_decoder_->SyncStop();
716 }
717
718 void MediaCodecPlayer::RequestToStopDecoders() {
719 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
720 DVLOG(1) << __FUNCTION__;
721
722 bool do_audio = false;
723 bool do_video = false;
724 int count = 0;
725 if (audio_decoder_->IsPrefetchingOrPlaying()) {
726 do_audio = true;
727 ++count;
728 }
729 if (video_decoder_->IsPrefetchingOrPlaying()) {
730 do_video = true;
731 ++count;
732 }
733
734 if (count == 0) {
qinmin 2015/05/19 19:26:26 No barrier closure is used here, why not simply re
Tima Vaisburd 2015/05/20 05:19:59 Yes, it was a silly copy-pasting.
735 GetMediaTaskRunner()->PostTask(
736 FROM_HERE, base::Bind(&MediaCodecPlayer::OnStopDone, weak_this_));
737 return;
738 }
739
740 if (do_audio)
741 audio_decoder_->RequestToStop();
742
743 if (do_video)
744 video_decoder_->RequestToStop();
745 }
746
747 void MediaCodecPlayer::ReleaseDecoderResources() {
748 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
749 DVLOG(1) << __FUNCTION__;
750
751 audio_decoder_->ReleaseDecoderResources();
752 video_decoder_->ReleaseDecoderResources();
753
754 // At this point decoder threads should not be running
755 if (interpolator_.interpolating())
756 interpolator_.StopInterpolating();
250 } 757 }
251 758
252 } // namespace media 759 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698