Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/pipeline_impl.h" | 5 #include "media/base/pipeline_impl.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 112 void RemoveTextStreamTask(DemuxerStream* text_stream); | 112 void RemoveTextStreamTask(DemuxerStream* text_stream); |
| 113 | 113 |
| 114 // Common handlers for notifications from renderers and demuxer. | 114 // Common handlers for notifications from renderers and demuxer. |
| 115 void OnPipelineError(PipelineStatus error); | 115 void OnPipelineError(PipelineStatus error); |
| 116 void OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, | 116 void OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, |
| 117 CdmContext* cdm_context, | 117 CdmContext* cdm_context, |
| 118 bool success); | 118 bool success); |
| 119 void CheckPlaybackEnded(); | 119 void CheckPlaybackEnded(); |
| 120 | 120 |
| 121 // State transition tasks. | 121 // State transition tasks. |
| 122 void DoSeek(base::TimeDelta seek_timestamp, const PipelineStatusCB& done_cb); | |
| 123 void DoStop(const base::Closure& done_cb); | |
| 124 void SetState(State next_state); | 122 void SetState(State next_state); |
| 125 State GetNextState() const; | 123 void CompleteSeek(base::TimeDelta seek_time, PipelineStatus status); |
| 126 void StateTransitionTask(PipelineStatus status); | 124 void CompleteSuspend(PipelineStatus status); |
| 127 void InitializeDemuxer(const PipelineStatusCB& done_cb); | 125 void InitializeDemuxer(const PipelineStatusCB& done_cb); |
| 128 void InitializeRenderer(const PipelineStatusCB& done_cb); | 126 void InitializeRenderer(const PipelineStatusCB& done_cb); |
| 129 void DestroyRenderer(); | 127 void DestroyRenderer(); |
| 130 void ReportMetadata(); | 128 void ReportMetadata(); |
| 131 | 129 |
| 132 const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; | 130 const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; |
| 133 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; | 131 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; |
| 134 const scoped_refptr<MediaLog> media_log_; | 132 const scoped_refptr<MediaLog> media_log_; |
| 135 | 133 |
| 136 base::WeakPtr<PipelineImpl> weak_pipeline_; | 134 base::WeakPtr<PipelineImpl> weak_pipeline_; |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 148 | 146 |
| 149 // Current state of the pipeline. | 147 // Current state of the pipeline. |
| 150 State state_; | 148 State state_; |
| 151 | 149 |
| 152 // Status of the pipeline. Initialized to PIPELINE_OK which indicates that | 150 // Status of the pipeline. Initialized to PIPELINE_OK which indicates that |
| 153 // the pipeline is operating correctly. Any other value indicates that the | 151 // the pipeline is operating correctly. Any other value indicates that the |
| 154 // pipeline is stopped or is stopping. Clients can call the Stop() method to | 152 // pipeline is stopped or is stopping. Clients can call the Stop() method to |
| 155 // reset the pipeline state, and restore this to PIPELINE_OK. | 153 // reset the pipeline state, and restore this to PIPELINE_OK. |
| 156 PipelineStatus status_; | 154 PipelineStatus status_; |
| 157 | 155 |
| 158 // The timestamp to start playback from after starting/seeking/resuming has | |
| 159 // completed. | |
| 160 base::TimeDelta start_timestamp_; | |
| 161 | |
| 162 // Whether we've received the audio/video/text ended events. | 156 // Whether we've received the audio/video/text ended events. |
| 163 bool renderer_ended_; | 157 bool renderer_ended_; |
| 164 bool text_renderer_ended_; | 158 bool text_renderer_ended_; |
| 165 | 159 |
| 166 // Series of tasks to Start(), Seek(), and Resume(). | 160 // Series of tasks to Start(), Seek(), and Resume(). |
| 167 std::unique_ptr<SerialRunner> pending_callbacks_; | 161 std::unique_ptr<SerialRunner> pending_callbacks_; |
| 168 | 162 |
| 169 base::WeakPtr<RendererWrapper> weak_this_; | 163 base::WeakPtr<RendererWrapper> weak_this_; |
| 170 base::WeakPtrFactory<RendererWrapper> weak_factory_; | 164 base::WeakPtrFactory<RendererWrapper> weak_factory_; |
| 171 DISALLOW_COPY_AND_ASSIGN(RendererWrapper); | 165 DISALLOW_COPY_AND_ASSIGN(RendererWrapper); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 188 weak_factory_(this) { | 182 weak_factory_(this) { |
| 189 weak_this_ = weak_factory_.GetWeakPtr(); | 183 weak_this_ = weak_factory_.GetWeakPtr(); |
| 190 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); | 184 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); |
| 191 } | 185 } |
| 192 | 186 |
| 193 PipelineImpl::RendererWrapper::~RendererWrapper() { | 187 PipelineImpl::RendererWrapper::~RendererWrapper() { |
| 194 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 188 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 195 DCHECK(state_ == kCreated || state_ == kStopped); | 189 DCHECK(state_ == kCreated || state_ == kStopped); |
| 196 } | 190 } |
| 197 | 191 |
| 192 // Note that the usage of base::Unretained() with the renderers is considered | |
| 193 // safe as they are owned by |pending_callbacks_| and share the same lifetime. | |
| 194 // | |
| 195 // That being said, deleting the renderers while keeping |pending_callbacks_| | |
| 196 // running on the media thread would result in crashes. | |
| 197 | |
| 198 void PipelineImpl::RendererWrapper::Start( | 198 void PipelineImpl::RendererWrapper::Start( |
| 199 Demuxer* demuxer, | 199 Demuxer* demuxer, |
| 200 std::unique_ptr<Renderer> renderer, | 200 std::unique_ptr<Renderer> renderer, |
| 201 std::unique_ptr<TextRenderer> text_renderer, | 201 std::unique_ptr<TextRenderer> text_renderer, |
| 202 base::WeakPtr<PipelineImpl> weak_pipeline) { | 202 base::WeakPtr<PipelineImpl> weak_pipeline) { |
| 203 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 203 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 204 DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: " | 204 DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: " |
| 205 << state_; | 205 << state_; |
| 206 | 206 |
| 207 SetState(kStarting); | |
| 208 | |
| 207 DCHECK(!demuxer_); | 209 DCHECK(!demuxer_); |
| 208 DCHECK(!shared_state_.renderer); | 210 DCHECK(!shared_state_.renderer); |
| 209 DCHECK(!text_renderer_); | 211 DCHECK(!text_renderer_); |
| 210 DCHECK(!renderer_ended_); | 212 DCHECK(!renderer_ended_); |
| 211 DCHECK(!text_renderer_ended_); | 213 DCHECK(!text_renderer_ended_); |
| 212 DCHECK(!weak_pipeline_); | 214 DCHECK(!weak_pipeline_); |
| 213 demuxer_ = demuxer; | 215 demuxer_ = demuxer; |
| 214 { | 216 { |
| 215 base::AutoLock auto_lock(shared_state_lock_); | 217 base::AutoLock auto_lock(shared_state_lock_); |
| 216 shared_state_.renderer = std::move(renderer); | 218 shared_state_.renderer = std::move(renderer); |
| 217 } | 219 } |
| 218 text_renderer_ = std::move(text_renderer); | 220 text_renderer_ = std::move(text_renderer); |
| 219 if (text_renderer_) { | 221 if (text_renderer_) { |
| 220 text_renderer_->Initialize( | 222 text_renderer_->Initialize( |
| 221 base::Bind(&RendererWrapper::OnTextRendererEnded, weak_this_)); | 223 base::Bind(&RendererWrapper::OnTextRendererEnded, weak_this_)); |
| 222 } | 224 } |
| 223 weak_pipeline_ = weak_pipeline; | 225 weak_pipeline_ = weak_pipeline; |
| 224 | 226 |
| 225 StateTransitionTask(PIPELINE_OK); | 227 // Queue asynchronous actions required to start. |
| 228 DCHECK(!pending_callbacks_); | |
| 229 SerialRunner::Queue fns; | |
| 230 | |
| 231 // Initialize demuxer. | |
| 232 fns.Push(base::Bind(&RendererWrapper::InitializeDemuxer, weak_this_)); | |
| 233 | |
| 234 // Once the demuxer is initialized successfully, media metadata must be | |
| 235 // available - report the metadata to client. | |
| 236 fns.Push(base::Bind(&RendererWrapper::ReportMetadata, weak_this_)); | |
| 237 | |
| 238 // Initialize renderer. | |
| 239 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_)); | |
| 240 | |
| 241 // Run tasks. | |
| 242 pending_callbacks_ = | |
| 243 SerialRunner::Run(fns, base::Bind(&RendererWrapper::CompleteSeek, | |
| 244 weak_this_, base::TimeDelta())); | |
| 226 } | 245 } |
| 227 | 246 |
| 228 void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) { | 247 void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) { |
| 229 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 248 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 230 DCHECK(state_ != kStopping && state_ != kStopped); | 249 DCHECK(state_ != kStopping && state_ != kStopped); |
| 231 | 250 |
| 232 SetState(kStopping); | 251 SetState(kStopping); |
| 233 | 252 |
| 234 if (shared_state_.statistics.video_frames_decoded > 0) { | 253 if (shared_state_.statistics.video_frames_decoded > 0) { |
| 235 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount", | 254 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount", |
| 236 shared_state_.statistics.video_frames_dropped); | 255 shared_state_.statistics.video_frames_dropped); |
| 237 } | 256 } |
| 238 | 257 |
| 239 // If we stop during starting/seeking/suspending/resuming we don't want to | 258 // If we stop during starting/seeking/suspending/resuming we don't want to |
| 240 // leave outstanding callbacks around. The callbacks also do not get run if | 259 // leave outstanding callbacks around. The callbacks also do not get run if |
| 241 // the pipeline is stopped before it had a chance to complete outstanding | 260 // the pipeline is stopped before it had a chance to complete outstanding |
| 242 // tasks. | 261 // tasks. |
| 243 pending_callbacks_.reset(); | 262 pending_callbacks_.reset(); |
| 244 | 263 |
| 245 DoStop(stop_cb); | 264 DestroyRenderer(); |
|
alokp
2016/06/24 06:15:06
DoStop() moved here.
| |
| 265 text_renderer_.reset(); | |
| 266 | |
| 267 if (demuxer_) { | |
| 268 demuxer_->Stop(); | |
| 269 demuxer_ = NULL; | |
| 270 } | |
| 271 | |
| 272 SetState(kStopped); | |
| 273 | |
| 274 // Post the stop callback to enqueue it after the tasks that may have been | |
| 275 // posted by Demuxer and Renderer during stopping. Note that in theory the | |
| 276 // tasks posted by Demuxer/Renderer may post even more tasks that will get | |
| 277 // enqueued after |stop_cb|. This may be problematic because Demuxer may | |
| 278 // get destroyed as soon as |stop_cb| is run. In practice this is not a | |
| 279 // problem, but ideally Demuxer should be destroyed on the media thread. | |
| 280 media_task_runner_->PostTask(FROM_HERE, stop_cb); | |
| 246 } | 281 } |
| 247 | 282 |
| 248 void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) { | 283 void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) { |
| 249 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 284 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 250 | 285 |
| 251 // Suppress seeking if we're not fully started. | 286 // Suppress seeking if we're not fully started. |
| 252 if (state_ != kPlaying) { | 287 if (state_ != kPlaying) { |
| 253 DCHECK(state_ == kStopping || state_ == kStopped) | 288 DCHECK(state_ == kStopping || state_ == kStopped) |
| 254 << "Receive seek in unexpected state: " << state_; | 289 << "Receive seek in unexpected state: " << state_; |
| 255 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); | 290 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
| 256 return; | 291 return; |
| 257 } | 292 } |
| 258 | 293 |
| 259 const base::TimeDelta seek_timestamp = | 294 base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime()); |
| 260 std::max(time, demuxer_->GetStartTime()); | |
| 261 | 295 |
| 262 SetState(kSeeking); | 296 SetState(kSeeking); |
| 263 renderer_ended_ = false; | 297 renderer_ended_ = false; |
| 264 text_renderer_ended_ = false; | 298 text_renderer_ended_ = false; |
| 265 start_timestamp_ = seek_timestamp; | |
| 266 | 299 |
| 267 DoSeek(seek_timestamp, | 300 // Queue asynchronous actions required to start. |
|
alokp
2016/06/24 06:15:07
DoSeek() moved here.
| |
| 268 base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); | 301 DCHECK(!pending_callbacks_); |
| 302 SerialRunner::Queue bound_fns; | |
| 303 | |
| 304 // Pause. | |
| 305 if (text_renderer_) { | |
| 306 bound_fns.Push(base::Bind(&TextRenderer::Pause, | |
| 307 base::Unretained(text_renderer_.get()))); | |
| 308 } | |
| 309 | |
| 310 // Flush. | |
| 311 DCHECK(shared_state_.renderer); | |
| 312 bound_fns.Push(base::Bind(&Renderer::Flush, | |
| 313 base::Unretained(shared_state_.renderer.get()))); | |
| 314 | |
| 315 if (text_renderer_) { | |
| 316 bound_fns.Push(base::Bind(&TextRenderer::Flush, | |
| 317 base::Unretained(text_renderer_.get()))); | |
| 318 } | |
| 319 | |
| 320 // Seek demuxer. | |
| 321 bound_fns.Push( | |
| 322 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); | |
| 323 | |
| 324 // Run tasks. | |
| 325 pending_callbacks_ = SerialRunner::Run( | |
| 326 bound_fns, | |
| 327 base::Bind(&RendererWrapper::CompleteSeek, weak_this_, seek_timestamp)); | |
| 269 } | 328 } |
| 270 | 329 |
| 271 void PipelineImpl::RendererWrapper::Suspend() { | 330 void PipelineImpl::RendererWrapper::Suspend() { |
| 272 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 331 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 273 | 332 |
| 274 // Suppress suspending if we're not playing. | 333 // Suppress suspending if we're not playing. |
| 275 if (state_ != kPlaying) { | 334 if (state_ != kPlaying) { |
| 276 DCHECK(state_ == kStopping || state_ == kStopped) | 335 DCHECK(state_ == kStopping || state_ == kStopped) |
| 277 << "Receive suspend in unexpected state: " << state_; | 336 << "Receive suspend in unexpected state: " << state_; |
| 278 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); | 337 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
| 279 return; | 338 return; |
| 280 } | 339 } |
| 281 DCHECK(shared_state_.renderer); | 340 DCHECK(shared_state_.renderer); |
| 282 DCHECK(!pending_callbacks_.get()); | 341 DCHECK(!pending_callbacks_.get()); |
| 283 | 342 |
| 284 SetState(kSuspending); | 343 SetState(kSuspending); |
| 285 | 344 |
| 286 // Freeze playback and record the media time before flushing. (Flushing clears | 345 // Freeze playback and record the media time before flushing. (Flushing clears |
| 287 // the value.) | 346 // the value.) |
| 288 shared_state_.renderer->SetPlaybackRate(0.0); | 347 shared_state_.renderer->SetPlaybackRate(0.0); |
| 289 { | 348 { |
| 290 base::AutoLock auto_lock(shared_state_lock_); | 349 base::AutoLock auto_lock(shared_state_lock_); |
| 291 shared_state_.suspend_timestamp = shared_state_.renderer->GetMediaTime(); | 350 shared_state_.suspend_timestamp = shared_state_.renderer->GetMediaTime(); |
| 292 DCHECK(shared_state_.suspend_timestamp != kNoTimestamp()); | 351 DCHECK(shared_state_.suspend_timestamp != kNoTimestamp()); |
| 293 } | 352 } |
| 294 | 353 |
| 295 // Queue the asynchronous actions required to stop playback. (Matches setup in | 354 // Queue the asynchronous actions required to stop playback. |
| 296 // DoSeek().) | |
| 297 // TODO(sandersd): Share implementation with DoSeek(). | |
| 298 SerialRunner::Queue fns; | 355 SerialRunner::Queue fns; |
| 299 | 356 |
| 300 if (text_renderer_) { | 357 if (text_renderer_) { |
| 301 fns.Push(base::Bind(&TextRenderer::Pause, | 358 fns.Push(base::Bind(&TextRenderer::Pause, |
| 302 base::Unretained(text_renderer_.get()))); | 359 base::Unretained(text_renderer_.get()))); |
| 303 } | 360 } |
| 304 | 361 |
| 305 fns.Push(base::Bind(&Renderer::Flush, | 362 fns.Push(base::Bind(&Renderer::Flush, |
| 306 base::Unretained(shared_state_.renderer.get()))); | 363 base::Unretained(shared_state_.renderer.get()))); |
| 307 | 364 |
| 308 if (text_renderer_) { | 365 if (text_renderer_) { |
| 309 fns.Push(base::Bind(&TextRenderer::Flush, | 366 fns.Push(base::Bind(&TextRenderer::Flush, |
| 310 base::Unretained(text_renderer_.get()))); | 367 base::Unretained(text_renderer_.get()))); |
| 311 } | 368 } |
| 312 | 369 |
| 313 pending_callbacks_ = SerialRunner::Run( | 370 pending_callbacks_ = SerialRunner::Run( |
| 314 fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); | 371 fns, base::Bind(&RendererWrapper::CompleteSuspend, weak_this_)); |
| 315 } | 372 } |
| 316 | 373 |
| 317 void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer, | 374 void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer, |
| 318 base::TimeDelta timestamp) { | 375 base::TimeDelta timestamp) { |
| 319 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 376 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 320 | 377 |
| 321 // Suppress resuming if we're not suspended. | 378 // Suppress resuming if we're not suspended. |
| 322 if (state_ != kSuspended) { | 379 if (state_ != kSuspended) { |
| 323 DCHECK(state_ == kStopping || state_ == kStopped) | 380 DCHECK(state_ == kStopping || state_ == kStopped) |
| 324 << "Receive resume in unexpected state: " << state_; | 381 << "Receive resume in unexpected state: " << state_; |
| 325 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); | 382 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
| 326 return; | 383 return; |
| 327 } | 384 } |
| 328 DCHECK(!shared_state_.renderer); | 385 DCHECK(!shared_state_.renderer); |
| 329 DCHECK(!pending_callbacks_.get()); | 386 DCHECK(!pending_callbacks_.get()); |
| 330 | 387 |
| 331 SetState(kResuming); | 388 SetState(kResuming); |
| 332 | 389 |
| 333 { | 390 { |
| 334 base::AutoLock auto_lock(shared_state_lock_); | 391 base::AutoLock auto_lock(shared_state_lock_); |
| 335 shared_state_.renderer = std::move(renderer); | 392 shared_state_.renderer = std::move(renderer); |
| 336 } | 393 } |
| 337 | 394 |
| 338 // Set up for a seek. (Matches setup in SeekTask().) | |
| 339 // TODO(sandersd): Share implementation with SeekTask(). | |
| 340 renderer_ended_ = false; | 395 renderer_ended_ = false; |
| 341 text_renderer_ended_ = false; | 396 text_renderer_ended_ = false; |
| 342 start_timestamp_ = std::max(timestamp, demuxer_->GetStartTime()); | 397 base::TimeDelta start_timestamp = |
| 398 std::max(timestamp, demuxer_->GetStartTime()); | |
| 343 | 399 |
| 344 // Queue the asynchronous actions required to start playback. Unlike DoSeek(), | 400 // Queue the asynchronous actions required to start playback. |
| 345 // we need to initialize the renderer ourselves (we don't want to enter state | |
| 346 // kInitDemuxer, and even if we did the current code would seek to the start | |
| 347 // instead of |timestamp|). | |
| 348 SerialRunner::Queue fns; | 401 SerialRunner::Queue fns; |
| 349 | 402 |
| 350 fns.Push( | 403 fns.Push( |
| 351 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp_)); | 404 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp)); |
| 352 | 405 |
| 353 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_)); | 406 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_)); |
| 354 | 407 |
| 355 pending_callbacks_ = SerialRunner::Run( | 408 pending_callbacks_ = SerialRunner::Run( |
| 356 fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); | 409 fns, |
| 410 base::Bind(&RendererWrapper::CompleteSeek, weak_this_, start_timestamp)); | |
| 357 } | 411 } |
| 358 | 412 |
| 359 void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) { | 413 void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) { |
| 360 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 414 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 361 | 415 |
| 362 playback_rate_ = playback_rate; | 416 playback_rate_ = playback_rate; |
| 363 if (state_ == kPlaying) | 417 if (state_ == kPlaying) |
| 364 shared_state_.renderer->SetPlaybackRate(playback_rate_); | 418 shared_state_.renderer->SetPlaybackRate(playback_rate_); |
| 365 } | 419 } |
| 366 | 420 |
| (...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 612 return; | 666 return; |
| 613 | 667 |
| 614 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_) | 668 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_) |
| 615 return; | 669 return; |
| 616 | 670 |
| 617 DCHECK_EQ(status_, PIPELINE_OK); | 671 DCHECK_EQ(status_, PIPELINE_OK); |
| 618 main_task_runner_->PostTask( | 672 main_task_runner_->PostTask( |
| 619 FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_)); | 673 FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_)); |
| 620 } | 674 } |
| 621 | 675 |
| 622 // Note that the usage of base::Unretained() with the renderers is considered | |
| 623 // safe as they are owned by |pending_callbacks_| and share the same lifetime. | |
| 624 // | |
| 625 // That being said, deleting the renderers while keeping |pending_callbacks_| | |
| 626 // running on the media thread would result in crashes. | |
| 627 void PipelineImpl::RendererWrapper::DoSeek(base::TimeDelta seek_timestamp, | |
| 628 const PipelineStatusCB& done_cb) { | |
| 629 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
| 630 DCHECK(!pending_callbacks_.get()); | |
| 631 DCHECK_EQ(state_, kSeeking); | |
| 632 SerialRunner::Queue bound_fns; | |
| 633 | |
| 634 // Pause. | |
| 635 if (text_renderer_) { | |
| 636 bound_fns.Push(base::Bind(&TextRenderer::Pause, | |
| 637 base::Unretained(text_renderer_.get()))); | |
| 638 } | |
| 639 | |
| 640 // Flush. | |
| 641 DCHECK(shared_state_.renderer); | |
| 642 bound_fns.Push(base::Bind(&Renderer::Flush, | |
| 643 base::Unretained(shared_state_.renderer.get()))); | |
| 644 | |
| 645 if (text_renderer_) { | |
| 646 bound_fns.Push(base::Bind(&TextRenderer::Flush, | |
| 647 base::Unretained(text_renderer_.get()))); | |
| 648 } | |
| 649 | |
| 650 // Seek demuxer. | |
| 651 bound_fns.Push( | |
| 652 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); | |
| 653 | |
| 654 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); | |
| 655 } | |
| 656 | |
| 657 void PipelineImpl::RendererWrapper::DoStop(const base::Closure& done_cb) { | |
| 658 DVLOG(2) << __FUNCTION__; | |
| 659 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
| 660 DCHECK_EQ(state_, kStopping); | |
| 661 DCHECK(!pending_callbacks_.get()); | |
| 662 | |
| 663 DestroyRenderer(); | |
| 664 text_renderer_.reset(); | |
| 665 | |
| 666 if (demuxer_) { | |
| 667 demuxer_->Stop(); | |
| 668 demuxer_ = NULL; | |
| 669 } | |
| 670 | |
| 671 SetState(kStopped); | |
| 672 | |
| 673 // Post the stop callback to enqueue it after the tasks that may have been | |
| 674 // posted by Demuxer and Renderer during stopping. Note that in theory the | |
| 675 // tasks posted by Demuxer/Renderer may post even more tasks that will get | |
| 676 // enqueued after |done_cb|. This may be problematic because Demuxer may | |
| 677 // get destroyed as soon as |done_cb| is run. In practice this is not a | |
| 678 // problem, but ideally Demuxer should be destroyed on the media thread. | |
| 679 media_task_runner_->PostTask(FROM_HERE, done_cb); | |
| 680 } | |
| 681 | |
| 682 void PipelineImpl::RendererWrapper::SetState(State next_state) { | 676 void PipelineImpl::RendererWrapper::SetState(State next_state) { |
| 683 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 677 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 684 DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> " | 678 DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> " |
| 685 << PipelineImpl::GetStateString(next_state); | 679 << PipelineImpl::GetStateString(next_state); |
| 686 | 680 |
| 687 state_ = next_state; | 681 state_ = next_state; |
| 688 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); | 682 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); |
| 689 } | 683 } |
| 690 | 684 |
| 691 PipelineImpl::State PipelineImpl::RendererWrapper::GetNextState() const { | 685 void PipelineImpl::RendererWrapper::CompleteSeek(base::TimeDelta seek_time, |
| 686 PipelineStatus status) { | |
| 692 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 687 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 693 DCHECK_EQ(status_, PIPELINE_OK) | 688 DCHECK(state_ == kStarting || state_ == kSeeking || state_ == kResuming); |
| 694 << "State transitions don't happen when there's an error: " << status_; | |
| 695 | 689 |
| 696 switch (state_) { | 690 DCHECK(pending_callbacks_); |
| 697 case kCreated: | 691 pending_callbacks_.reset(); |
| 698 return kInitDemuxer; | |
| 699 | 692 |
| 700 case kInitDemuxer: | |
| 701 return kInitRenderer; | |
| 702 | |
| 703 case kInitRenderer: | |
| 704 case kSeeking: | |
| 705 return kPlaying; | |
| 706 | |
| 707 case kSuspending: | |
| 708 return kSuspended; | |
| 709 | |
| 710 case kSuspended: | |
| 711 return kResuming; | |
| 712 | |
| 713 case kResuming: | |
| 714 return kPlaying; | |
| 715 | |
| 716 case kPlaying: | |
| 717 case kStopping: | |
| 718 case kStopped: | |
| 719 break; | |
| 720 } | |
| 721 NOTREACHED() << "State has no transition: " << state_; | |
| 722 return state_; | |
| 723 } | |
| 724 void PipelineImpl::RendererWrapper::StateTransitionTask(PipelineStatus status) { | |
| 725 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
| 726 | |
| 727 // No-op any state transitions if we're stopping or already encountered error. | |
| 728 if (state_ == kStopping || state_ == kStopped || status_ != PIPELINE_OK) | |
| 729 return; | |
| 730 | |
| 731 // Report error from the previous operation. | |
| 732 if (status != PIPELINE_OK) { | 693 if (status != PIPELINE_OK) { |
| 733 OnPipelineError(status); | 694 OnPipelineError(status); |
| 734 return; | 695 return; |
| 735 } | 696 } |
| 736 | 697 |
| 737 // Guard against accidentally clearing |pending_callbacks_| for states that | 698 shared_state_.renderer->StartPlayingFrom( |
|
alokp
2016/06/24 06:15:07
copied from PipelineImpl::RendererWrapper::StateTr
| |
| 738 // use it as well as states that should not be using it. | 699 std::max(seek_time, demuxer_->GetStartTime())); |
| 739 DCHECK_EQ(pending_callbacks_.get() != NULL, | 700 { |
| 740 state_ == kSeeking || state_ == kSuspending || state_ == kResuming); | 701 base::AutoLock auto_lock(shared_state_lock_); |
| 702 shared_state_.suspend_timestamp = kNoTimestamp(); | |
| 703 } | |
| 741 | 704 |
| 705 if (text_renderer_) | |
| 706 text_renderer_->StartPlaying(); | |
| 707 | |
| 708 shared_state_.renderer->SetPlaybackRate(playback_rate_); | |
| 709 shared_state_.renderer->SetVolume(volume_); | |
| 710 | |
| 711 SetState(kPlaying); | |
| 712 main_task_runner_->PostTask( | |
| 713 FROM_HERE, base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_)); | |
| 714 } | |
| 715 | |
| 716 void PipelineImpl::RendererWrapper::CompleteSuspend(PipelineStatus status) { | |
| 717 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
| 718 DCHECK_EQ(kSuspending, state_); | |
| 719 | |
| 720 DCHECK(pending_callbacks_); | |
| 742 pending_callbacks_.reset(); | 721 pending_callbacks_.reset(); |
| 743 | 722 |
| 744 PipelineStatusCB done_cb = | 723 // In case we are suspending or suspended, the error may be recoverable, |
| 745 base::Bind(&RendererWrapper::StateTransitionTask, weak_this_); | 724 // so don't propagate it now, instead let the subsequent seek during resume |
| 725 // propagate it if it's unrecoverable. | |
| 726 LOG_IF(WARNING, status != PIPELINE_OK) | |
| 727 << "Encountered pipeline error while suspending: " << status; | |
| 746 | 728 |
| 747 // Switch states, performing any entrance actions for the new state as well. | 729 DestroyRenderer(); |
|
alokp
2016/06/24 06:15:07
copied from PipelineImpl::RendererWrapper::StateTr
| |
| 748 SetState(GetNextState()); | 730 { |
| 749 switch (state_) { | 731 base::AutoLock auto_lock(shared_state_lock_); |
| 750 case kInitDemuxer: | 732 shared_state_.statistics.audio_memory_usage = 0; |
| 751 return InitializeDemuxer(done_cb); | 733 shared_state_.statistics.video_memory_usage = 0; |
| 734 } | |
| 752 | 735 |
| 753 case kInitRenderer: | 736 SetState(kSuspended); |
| 754 // When the state_ transfers to kInitRenderer, it means the demuxer has | 737 main_task_runner_->PostTask( |
| 755 // finished parsing the init info. It should call ReportMetadata in case | 738 FROM_HERE, base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_)); |
| 756 // meeting 'decode' error when passing media segment but WebMediaPlayer's | |
| 757 // ready_state_ is still ReadyStateHaveNothing. In that case, it will | |
| 758 // treat it as NetworkStateFormatError not NetworkStateDecodeError. | |
| 759 ReportMetadata(); | |
| 760 start_timestamp_ = demuxer_->GetStartTime(); | |
| 761 | |
| 762 return InitializeRenderer(done_cb); | |
| 763 | |
| 764 case kPlaying: | |
| 765 DCHECK(start_timestamp_ >= base::TimeDelta()); | |
| 766 shared_state_.renderer->StartPlayingFrom(start_timestamp_); | |
| 767 { | |
| 768 base::AutoLock auto_lock(shared_state_lock_); | |
| 769 shared_state_.suspend_timestamp = kNoTimestamp(); | |
| 770 } | |
| 771 | |
| 772 if (text_renderer_) | |
| 773 text_renderer_->StartPlaying(); | |
| 774 | |
| 775 main_task_runner_->PostTask( | |
| 776 FROM_HERE, base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_, | |
| 777 start_timestamp_)); | |
| 778 | |
| 779 shared_state_.renderer->SetPlaybackRate(playback_rate_); | |
| 780 shared_state_.renderer->SetVolume(volume_); | |
| 781 return; | |
| 782 | |
| 783 case kSuspended: | |
| 784 DestroyRenderer(); | |
| 785 { | |
| 786 base::AutoLock auto_lock(shared_state_lock_); | |
| 787 shared_state_.statistics.audio_memory_usage = 0; | |
| 788 shared_state_.statistics.video_memory_usage = 0; | |
| 789 } | |
| 790 main_task_runner_->PostTask( | |
| 791 FROM_HERE, base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_, | |
| 792 shared_state_.suspend_timestamp)); | |
| 793 return; | |
| 794 | |
| 795 case kStopping: | |
| 796 case kStopped: | |
| 797 case kCreated: | |
| 798 case kSeeking: | |
| 799 case kSuspending: | |
| 800 case kResuming: | |
| 801 NOTREACHED() << "State has no transition: " << state_; | |
| 802 return; | |
| 803 } | |
| 804 } | 739 } |
| 805 | 740 |
| 806 void PipelineImpl::RendererWrapper::InitializeDemuxer( | 741 void PipelineImpl::RendererWrapper::InitializeDemuxer( |
| 807 const PipelineStatusCB& done_cb) { | 742 const PipelineStatusCB& done_cb) { |
| 808 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 743 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 809 | 744 |
| 810 demuxer_->Initialize(this, done_cb, !!text_renderer_); | 745 demuxer_->Initialize(this, done_cb, !!text_renderer_); |
| 811 } | 746 } |
| 812 | 747 |
| 813 void PipelineImpl::RendererWrapper::InitializeRenderer( | 748 void PipelineImpl::RendererWrapper::InitializeRenderer( |
| (...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1098 } | 1033 } |
| 1099 | 1034 |
| 1100 #define RETURN_STRING(state) \ | 1035 #define RETURN_STRING(state) \ |
| 1101 case state: \ | 1036 case state: \ |
| 1102 return #state; | 1037 return #state; |
| 1103 | 1038 |
| 1104 // static | 1039 // static |
| 1105 const char* PipelineImpl::GetStateString(State state) { | 1040 const char* PipelineImpl::GetStateString(State state) { |
| 1106 switch (state) { | 1041 switch (state) { |
| 1107 RETURN_STRING(kCreated); | 1042 RETURN_STRING(kCreated); |
| 1108 RETURN_STRING(kInitDemuxer); | 1043 RETURN_STRING(kStarting); |
| 1109 RETURN_STRING(kInitRenderer); | |
| 1110 RETURN_STRING(kSeeking); | 1044 RETURN_STRING(kSeeking); |
| 1111 RETURN_STRING(kPlaying); | 1045 RETURN_STRING(kPlaying); |
| 1112 RETURN_STRING(kStopping); | 1046 RETURN_STRING(kStopping); |
| 1113 RETURN_STRING(kStopped); | 1047 RETURN_STRING(kStopped); |
| 1114 RETURN_STRING(kSuspending); | 1048 RETURN_STRING(kSuspending); |
| 1115 RETURN_STRING(kSuspended); | 1049 RETURN_STRING(kSuspended); |
| 1116 RETURN_STRING(kResuming); | 1050 RETURN_STRING(kResuming); |
| 1117 } | 1051 } |
| 1118 NOTREACHED(); | 1052 NOTREACHED(); |
| 1119 return "INVALID"; | 1053 return "INVALID"; |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1211 | 1145 |
| 1212 void PipelineImpl::OnVideoOpacityChange(bool opaque) { | 1146 void PipelineImpl::OnVideoOpacityChange(bool opaque) { |
| 1213 DVLOG(2) << __FUNCTION__; | 1147 DVLOG(2) << __FUNCTION__; |
| 1214 DCHECK(thread_checker_.CalledOnValidThread()); | 1148 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1215 DCHECK(IsRunning()); | 1149 DCHECK(IsRunning()); |
| 1216 | 1150 |
| 1217 DCHECK(client_); | 1151 DCHECK(client_); |
| 1218 client_->OnVideoOpacityChange(opaque); | 1152 client_->OnVideoOpacityChange(opaque); |
| 1219 } | 1153 } |
| 1220 | 1154 |
| 1221 void PipelineImpl::OnSeekDone(base::TimeDelta start_time) { | 1155 void PipelineImpl::OnSeekDone() { |
| 1222 DVLOG(3) << __FUNCTION__ << "(" << start_time.InMicroseconds() << ")"; | 1156 DVLOG(3) << __FUNCTION__; |
| 1223 DCHECK(thread_checker_.CalledOnValidThread()); | 1157 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1224 DCHECK(IsRunning()); | 1158 DCHECK(IsRunning()); |
| 1225 | 1159 |
| 1226 DCHECK(!seek_cb_.is_null()); | 1160 DCHECK(!seek_cb_.is_null()); |
| 1227 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); | 1161 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); |
| 1228 } | 1162 } |
| 1229 | 1163 |
| 1230 void PipelineImpl::OnSuspendDone(base::TimeDelta suspend_time) { | 1164 void PipelineImpl::OnSuspendDone() { |
| 1231 DVLOG(3) << __FUNCTION__ << "(" << suspend_time.InMicroseconds() << ")"; | 1165 DVLOG(3) << __FUNCTION__; |
| 1232 DCHECK(thread_checker_.CalledOnValidThread()); | 1166 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1233 DCHECK(IsRunning()); | 1167 DCHECK(IsRunning()); |
| 1234 | 1168 |
| 1235 DCHECK(!suspend_cb_.is_null()); | 1169 DCHECK(!suspend_cb_.is_null()); |
| 1236 base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK); | 1170 base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK); |
| 1237 } | 1171 } |
| 1238 | 1172 |
| 1239 } // namespace media | 1173 } // namespace media |
| OLD | NEW |