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 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 121 void RemoveTextStreamTask(DemuxerStream* text_stream); | 121 void RemoveTextStreamTask(DemuxerStream* text_stream); |
| 122 | 122 |
| 123 // Common handlers for notifications from renderers and demuxer. | 123 // Common handlers for notifications from renderers and demuxer. |
| 124 void OnPipelineError(PipelineStatus error); | 124 void OnPipelineError(PipelineStatus error); |
| 125 void OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, | 125 void OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, |
| 126 CdmContext* cdm_context, | 126 CdmContext* cdm_context, |
| 127 bool success); | 127 bool success); |
| 128 void CheckPlaybackEnded(); | 128 void CheckPlaybackEnded(); |
| 129 | 129 |
| 130 // State transition tasks. | 130 // State transition tasks. |
| 131 void DoSeek(base::TimeDelta seek_timestamp, const PipelineStatusCB& done_cb); | |
| 132 void DoStop(const base::Closure& done_cb); | |
| 133 void SetState(State next_state); | 131 void SetState(State next_state); |
| 134 State GetNextState() const; | 132 void CompleteSeek(base::TimeDelta seek_time, PipelineStatus status); |
| 135 void StateTransitionTask(PipelineStatus status); | 133 void CompleteSuspend(PipelineStatus status); |
| 136 void InitializeDemuxer(const PipelineStatusCB& done_cb); | 134 void InitializeDemuxer(const PipelineStatusCB& done_cb); |
| 137 void InitializeRenderer(const PipelineStatusCB& done_cb); | 135 void InitializeRenderer(const PipelineStatusCB& done_cb); |
| 138 void DestroyRenderer(); | 136 void DestroyRenderer(); |
| 139 void ReportMetadata(); | 137 void ReportMetadata(); |
| 140 | 138 |
| 141 const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; | 139 const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; |
| 142 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; | 140 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; |
| 143 const scoped_refptr<MediaLog> media_log_; | 141 const scoped_refptr<MediaLog> media_log_; |
| 144 | 142 |
| 145 base::WeakPtr<PipelineImpl> weak_pipeline_; | 143 base::WeakPtr<PipelineImpl> weak_pipeline_; |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 157 | 155 |
| 158 // Current state of the pipeline. | 156 // Current state of the pipeline. |
| 159 State state_; | 157 State state_; |
| 160 | 158 |
| 161 // Status of the pipeline. Initialized to PIPELINE_OK which indicates that | 159 // Status of the pipeline. Initialized to PIPELINE_OK which indicates that |
| 162 // the pipeline is operating correctly. Any other value indicates that the | 160 // the pipeline is operating correctly. Any other value indicates that the |
| 163 // pipeline is stopped or is stopping. Clients can call the Stop() method to | 161 // pipeline is stopped or is stopping. Clients can call the Stop() method to |
| 164 // reset the pipeline state, and restore this to PIPELINE_OK. | 162 // reset the pipeline state, and restore this to PIPELINE_OK. |
| 165 PipelineStatus status_; | 163 PipelineStatus status_; |
| 166 | 164 |
| 167 // The timestamp to start playback from after starting/seeking/resuming has | |
| 168 // completed. | |
| 169 base::TimeDelta start_timestamp_; | |
| 170 | |
| 171 // Whether we've received the audio/video/text ended events. | 165 // Whether we've received the audio/video/text ended events. |
| 172 bool renderer_ended_; | 166 bool renderer_ended_; |
| 173 bool text_renderer_ended_; | 167 bool text_renderer_ended_; |
| 174 | 168 |
| 175 // Series of tasks to Start(), Seek(), and Resume(). | 169 // Series of tasks to Start(), Seek(), and Resume(). |
| 176 std::unique_ptr<SerialRunner> pending_callbacks_; | 170 std::unique_ptr<SerialRunner> pending_callbacks_; |
| 177 | 171 |
| 178 base::WeakPtr<RendererWrapper> weak_this_; | 172 base::WeakPtr<RendererWrapper> weak_this_; |
| 179 base::WeakPtrFactory<RendererWrapper> weak_factory_; | 173 base::WeakPtrFactory<RendererWrapper> weak_factory_; |
| 180 DISALLOW_COPY_AND_ASSIGN(RendererWrapper); | 174 DISALLOW_COPY_AND_ASSIGN(RendererWrapper); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 197 weak_factory_(this) { | 191 weak_factory_(this) { |
| 198 weak_this_ = weak_factory_.GetWeakPtr(); | 192 weak_this_ = weak_factory_.GetWeakPtr(); |
| 199 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); | 193 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); |
| 200 } | 194 } |
| 201 | 195 |
| 202 PipelineImpl::RendererWrapper::~RendererWrapper() { | 196 PipelineImpl::RendererWrapper::~RendererWrapper() { |
| 203 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 197 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 204 DCHECK(state_ == kCreated || state_ == kStopped); | 198 DCHECK(state_ == kCreated || state_ == kStopped); |
| 205 } | 199 } |
| 206 | 200 |
| 201 // Note that the usage of base::Unretained() with the renderers is considered | |
| 202 // safe as they are owned by |pending_callbacks_| and share the same lifetime. | |
| 203 // | |
| 204 // That being said, deleting the renderers while keeping |pending_callbacks_| | |
| 205 // running on the media thread would result in crashes. | |
| 206 | |
| 207 void PipelineImpl::RendererWrapper::Start( | 207 void PipelineImpl::RendererWrapper::Start( |
| 208 Demuxer* demuxer, | 208 Demuxer* demuxer, |
| 209 std::unique_ptr<Renderer> renderer, | 209 std::unique_ptr<Renderer> renderer, |
| 210 std::unique_ptr<TextRenderer> text_renderer, | 210 std::unique_ptr<TextRenderer> text_renderer, |
| 211 base::WeakPtr<PipelineImpl> weak_pipeline) { | 211 base::WeakPtr<PipelineImpl> weak_pipeline) { |
| 212 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 212 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 213 DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: " | 213 DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: " |
| 214 << state_; | 214 << state_; |
| 215 | 215 |
| 216 SetState(kStarting); | |
| 217 | |
| 216 DCHECK(!demuxer_); | 218 DCHECK(!demuxer_); |
| 217 DCHECK(!shared_state_.renderer); | 219 DCHECK(!shared_state_.renderer); |
| 218 DCHECK(!text_renderer_); | 220 DCHECK(!text_renderer_); |
| 219 DCHECK(!renderer_ended_); | 221 DCHECK(!renderer_ended_); |
| 220 DCHECK(!text_renderer_ended_); | 222 DCHECK(!text_renderer_ended_); |
| 221 DCHECK(!weak_pipeline_); | 223 DCHECK(!weak_pipeline_); |
| 222 demuxer_ = demuxer; | 224 demuxer_ = demuxer; |
| 223 { | 225 { |
| 224 base::AutoLock auto_lock(shared_state_lock_); | 226 base::AutoLock auto_lock(shared_state_lock_); |
| 225 shared_state_.renderer = std::move(renderer); | 227 shared_state_.renderer = std::move(renderer); |
| 226 } | 228 } |
| 227 text_renderer_ = std::move(text_renderer); | 229 text_renderer_ = std::move(text_renderer); |
| 228 if (text_renderer_) { | 230 if (text_renderer_) { |
| 229 text_renderer_->Initialize( | 231 text_renderer_->Initialize( |
| 230 base::Bind(&RendererWrapper::OnTextRendererEnded, weak_this_)); | 232 base::Bind(&RendererWrapper::OnTextRendererEnded, weak_this_)); |
| 231 } | 233 } |
| 232 weak_pipeline_ = weak_pipeline; | 234 weak_pipeline_ = weak_pipeline; |
| 233 | 235 |
| 234 StateTransitionTask(PIPELINE_OK); | 236 // Queue asynchronous actions required to start. |
| 237 DCHECK(!pending_callbacks_); | |
| 238 SerialRunner::Queue fns; | |
| 239 | |
| 240 // Initialize demuxer. | |
| 241 fns.Push(base::Bind(&RendererWrapper::InitializeDemuxer, weak_this_)); | |
| 242 | |
| 243 // Once the demuxer is initialized successfully, media metadata must be | |
| 244 // available - report the metadata to client. | |
| 245 fns.Push(base::Bind(&RendererWrapper::ReportMetadata, weak_this_)); | |
| 246 | |
| 247 // Initialize renderer. | |
| 248 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_)); | |
| 249 | |
| 250 // Run tasks. | |
| 251 pending_callbacks_ = | |
| 252 SerialRunner::Run(fns, base::Bind(&RendererWrapper::CompleteSeek, | |
| 253 weak_this_, base::TimeDelta())); | |
| 235 } | 254 } |
| 236 | 255 |
| 237 void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) { | 256 void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) { |
| 238 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 257 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 239 DCHECK(state_ != kStopping && state_ != kStopped); | 258 DCHECK(state_ != kStopping && state_ != kStopped); |
| 240 | 259 |
| 241 SetState(kStopping); | 260 SetState(kStopping); |
| 242 | 261 |
| 243 if (shared_state_.statistics.video_frames_decoded > 0) { | 262 if (shared_state_.statistics.video_frames_decoded > 0) { |
| 244 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount", | 263 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount", |
| 245 shared_state_.statistics.video_frames_dropped); | 264 shared_state_.statistics.video_frames_dropped); |
| 246 } | 265 } |
| 247 | 266 |
| 248 // If we stop during starting/seeking/suspending/resuming we don't want to | 267 // If we stop during starting/seeking/suspending/resuming we don't want to |
| 249 // leave outstanding callbacks around. The callbacks also do not get run if | 268 // leave outstanding callbacks around. The callbacks also do not get run if |
| 250 // the pipeline is stopped before it had a chance to complete outstanding | 269 // the pipeline is stopped before it had a chance to complete outstanding |
| 251 // tasks. | 270 // tasks. |
| 252 pending_callbacks_.reset(); | 271 pending_callbacks_.reset(); |
| 253 | 272 |
| 254 DoStop(stop_cb); | 273 DestroyRenderer(); |
| 274 text_renderer_.reset(); | |
| 275 | |
| 276 if (demuxer_) { | |
| 277 demuxer_->Stop(); | |
| 278 demuxer_ = NULL; | |
| 279 } | |
| 280 | |
| 281 SetState(kStopped); | |
| 282 | |
| 283 // Post the stop callback to enqueue it after the tasks that may have been | |
| 284 // posted by Demuxer and Renderer during stopping. Note that in theory the | |
| 285 // tasks posted by Demuxer/Renderer may post even more tasks that will get | |
| 286 // enqueued after |stop_cb|. This may be problematic because Demuxer may | |
| 287 // get destroyed as soon as |stop_cb| is run. In practice this is not a | |
| 288 // problem, but ideally Demuxer should be destroyed on the media thread. | |
| 289 media_task_runner_->PostTask(FROM_HERE, stop_cb); | |
| 255 } | 290 } |
| 256 | 291 |
| 257 void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) { | 292 void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) { |
| 258 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 293 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 259 | 294 |
| 260 // Suppress seeking if we're not fully started. | 295 // Suppress seeking if we're not fully started. |
| 261 if (state_ != kPlaying) { | 296 if (state_ != kPlaying) { |
| 262 DCHECK(state_ == kStopping || state_ == kStopped) | 297 DCHECK(state_ == kStopping || state_ == kStopped) |
| 263 << "Receive seek in unexpected state: " << state_; | 298 << "Receive seek in unexpected state: " << state_; |
| 264 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); | 299 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
| 265 return; | 300 return; |
| 266 } | 301 } |
| 267 | 302 |
| 268 const base::TimeDelta seek_timestamp = | 303 base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime()); |
| 269 std::max(time, demuxer_->GetStartTime()); | |
| 270 | 304 |
| 271 SetState(kSeeking); | 305 SetState(kSeeking); |
| 272 renderer_ended_ = false; | 306 renderer_ended_ = false; |
| 273 text_renderer_ended_ = false; | 307 text_renderer_ended_ = false; |
| 274 start_timestamp_ = seek_timestamp; | |
| 275 | 308 |
| 276 DoSeek(seek_timestamp, | 309 // Queue asynchronous actions required to start. |
| 277 base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); | 310 DCHECK(!pending_callbacks_); |
| 311 SerialRunner::Queue bound_fns; | |
| 312 | |
| 313 // Pause. | |
| 314 if (text_renderer_) { | |
| 315 bound_fns.Push(base::Bind(&TextRenderer::Pause, | |
| 316 base::Unretained(text_renderer_.get()))); | |
| 317 } | |
| 318 | |
| 319 // Flush. | |
| 320 DCHECK(shared_state_.renderer); | |
| 321 bound_fns.Push(base::Bind(&Renderer::Flush, | |
| 322 base::Unretained(shared_state_.renderer.get()))); | |
| 323 | |
| 324 if (text_renderer_) { | |
| 325 bound_fns.Push(base::Bind(&TextRenderer::Flush, | |
| 326 base::Unretained(text_renderer_.get()))); | |
| 327 } | |
| 328 | |
| 329 // Seek demuxer. | |
| 330 bound_fns.Push( | |
| 331 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); | |
| 332 | |
| 333 // Run tasks. | |
| 334 pending_callbacks_ = SerialRunner::Run( | |
| 335 bound_fns, | |
| 336 base::Bind(&RendererWrapper::CompleteSeek, weak_this_, seek_timestamp)); | |
| 278 } | 337 } |
| 279 | 338 |
| 280 void PipelineImpl::RendererWrapper::Suspend() { | 339 void PipelineImpl::RendererWrapper::Suspend() { |
| 281 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 340 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 282 | 341 |
| 283 // Suppress suspending if we're not playing. | 342 // Suppress suspending if we're not playing. |
| 284 if (state_ != kPlaying) { | 343 if (state_ != kPlaying) { |
| 285 DCHECK(state_ == kStopping || state_ == kStopped) | 344 DCHECK(state_ == kStopping || state_ == kStopped) |
| 286 << "Receive suspend in unexpected state: " << state_; | 345 << "Receive suspend in unexpected state: " << state_; |
| 287 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); | 346 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
| 288 return; | 347 return; |
| 289 } | 348 } |
| 290 DCHECK(shared_state_.renderer); | 349 DCHECK(shared_state_.renderer); |
| 291 DCHECK(!pending_callbacks_.get()); | 350 DCHECK(!pending_callbacks_.get()); |
| 292 | 351 |
| 293 SetState(kSuspending); | 352 SetState(kSuspending); |
| 294 | 353 |
| 295 // Freeze playback and record the media time before flushing. (Flushing clears | 354 // Freeze playback and record the media time before flushing. (Flushing clears |
| 296 // the value.) | 355 // the value.) |
| 297 shared_state_.renderer->SetPlaybackRate(0.0); | 356 shared_state_.renderer->SetPlaybackRate(0.0); |
| 298 { | 357 { |
| 299 base::AutoLock auto_lock(shared_state_lock_); | 358 base::AutoLock auto_lock(shared_state_lock_); |
| 300 shared_state_.suspend_timestamp = shared_state_.renderer->GetMediaTime(); | 359 shared_state_.suspend_timestamp = shared_state_.renderer->GetMediaTime(); |
| 301 DCHECK(shared_state_.suspend_timestamp != kNoTimestamp); | 360 DCHECK(shared_state_.suspend_timestamp != kNoTimestamp); |
| 302 } | 361 } |
| 303 | 362 |
| 304 // Queue the asynchronous actions required to stop playback. (Matches setup in | 363 // Queue the asynchronous actions required to stop playback. |
| 305 // DoSeek().) | |
| 306 // TODO(sandersd): Share implementation with DoSeek(). | |
| 307 SerialRunner::Queue fns; | 364 SerialRunner::Queue fns; |
| 308 | 365 |
| 309 if (text_renderer_) { | 366 if (text_renderer_) { |
| 310 fns.Push(base::Bind(&TextRenderer::Pause, | 367 fns.Push(base::Bind(&TextRenderer::Pause, |
| 311 base::Unretained(text_renderer_.get()))); | 368 base::Unretained(text_renderer_.get()))); |
| 312 } | 369 } |
| 313 | 370 |
| 314 fns.Push(base::Bind(&Renderer::Flush, | 371 fns.Push(base::Bind(&Renderer::Flush, |
| 315 base::Unretained(shared_state_.renderer.get()))); | 372 base::Unretained(shared_state_.renderer.get()))); |
| 316 | 373 |
| 317 if (text_renderer_) { | 374 if (text_renderer_) { |
| 318 fns.Push(base::Bind(&TextRenderer::Flush, | 375 fns.Push(base::Bind(&TextRenderer::Flush, |
| 319 base::Unretained(text_renderer_.get()))); | 376 base::Unretained(text_renderer_.get()))); |
| 320 } | 377 } |
| 321 | 378 |
| 322 pending_callbacks_ = SerialRunner::Run( | 379 pending_callbacks_ = SerialRunner::Run( |
| 323 fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); | 380 fns, base::Bind(&RendererWrapper::CompleteSuspend, weak_this_)); |
| 324 } | 381 } |
| 325 | 382 |
| 326 void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer, | 383 void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer, |
| 327 base::TimeDelta timestamp) { | 384 base::TimeDelta timestamp) { |
| 328 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 385 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 329 | 386 |
| 330 // Suppress resuming if we're not suspended. | 387 // Suppress resuming if we're not suspended. |
| 331 if (state_ != kSuspended) { | 388 if (state_ != kSuspended) { |
| 332 DCHECK(state_ == kStopping || state_ == kStopped) | 389 DCHECK(state_ == kStopping || state_ == kStopped) |
| 333 << "Receive resume in unexpected state: " << state_; | 390 << "Receive resume in unexpected state: " << state_; |
| 334 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); | 391 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
| 335 return; | 392 return; |
| 336 } | 393 } |
| 337 DCHECK(!shared_state_.renderer); | 394 DCHECK(!shared_state_.renderer); |
| 338 DCHECK(!pending_callbacks_.get()); | 395 DCHECK(!pending_callbacks_.get()); |
| 339 | 396 |
| 340 SetState(kResuming); | 397 SetState(kResuming); |
| 341 | 398 |
| 342 { | 399 { |
| 343 base::AutoLock auto_lock(shared_state_lock_); | 400 base::AutoLock auto_lock(shared_state_lock_); |
| 344 shared_state_.renderer = std::move(renderer); | 401 shared_state_.renderer = std::move(renderer); |
| 345 } | 402 } |
| 346 | 403 |
| 347 // Set up for a seek. (Matches setup in SeekTask().) | |
| 348 // TODO(sandersd): Share implementation with SeekTask(). | |
| 349 renderer_ended_ = false; | 404 renderer_ended_ = false; |
| 350 text_renderer_ended_ = false; | 405 text_renderer_ended_ = false; |
| 351 start_timestamp_ = std::max(timestamp, demuxer_->GetStartTime()); | 406 base::TimeDelta start_timestamp = |
| 407 std::max(timestamp, demuxer_->GetStartTime()); | |
| 352 | 408 |
| 353 // Queue the asynchronous actions required to start playback. Unlike DoSeek(), | 409 // Queue the asynchronous actions required to start playback. |
| 354 // we need to initialize the renderer ourselves (we don't want to enter state | |
| 355 // kInitDemuxer, and even if we did the current code would seek to the start | |
| 356 // instead of |timestamp|). | |
| 357 SerialRunner::Queue fns; | 410 SerialRunner::Queue fns; |
| 358 | 411 |
| 359 fns.Push( | 412 fns.Push( |
| 360 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp_)); | 413 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp)); |
| 361 | 414 |
| 362 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_)); | 415 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_)); |
| 363 | 416 |
| 364 pending_callbacks_ = SerialRunner::Run( | 417 pending_callbacks_ = SerialRunner::Run( |
| 365 fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); | 418 fns, |
| 419 base::Bind(&RendererWrapper::CompleteSeek, weak_this_, start_timestamp)); | |
| 366 } | 420 } |
| 367 | 421 |
| 368 void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) { | 422 void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) { |
| 369 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 423 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 370 | 424 |
| 371 playback_rate_ = playback_rate; | 425 playback_rate_ = playback_rate; |
| 372 if (state_ == kPlaying) | 426 if (state_ == kPlaying) |
| 373 shared_state_.renderer->SetPlaybackRate(playback_rate_); | 427 shared_state_.renderer->SetPlaybackRate(playback_rate_); |
| 374 } | 428 } |
| 375 | 429 |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 525 // a notification when pipeline is stopped/shut down, it's safe to ignore it. | 579 // a notification when pipeline is stopped/shut down, it's safe to ignore it. |
| 526 if (state_ == kStopping || state_ == kStopped) { | 580 if (state_ == kStopping || state_ == kStopped) { |
| 527 return; | 581 return; |
| 528 } | 582 } |
| 529 | 583 |
| 530 DCHECK(demuxer_); | 584 DCHECK(demuxer_); |
| 531 DCHECK(shared_state_.renderer); | 585 DCHECK(shared_state_.renderer); |
| 532 | 586 |
| 533 base::TimeDelta currTime = (state_ == kPlaying) | 587 base::TimeDelta currTime = (state_ == kPlaying) |
| 534 ? shared_state_.renderer->GetMediaTime() | 588 ? shared_state_.renderer->GetMediaTime() |
| 535 : start_timestamp_; | 589 : demuxer_->GetStartTime(); |
|
alokp
2016/07/29 18:30:52
I cannot call RendererWrapper::GetMediaTime here b
servolk
2016/07/29 18:37:36
Yes, I believe this should be fine.
Indeed OnEnabl
| |
| 536 demuxer_->OnEnabledAudioTracksChanged(enabledTrackIds, currTime); | 590 demuxer_->OnEnabledAudioTracksChanged(enabledTrackIds, currTime); |
| 537 } | 591 } |
| 538 | 592 |
| 539 void PipelineImpl::RendererWrapper::OnSelectedVideoTrackChanged( | 593 void PipelineImpl::RendererWrapper::OnSelectedVideoTrackChanged( |
| 540 const std::vector<MediaTrack::Id>& selectedTrackId) { | 594 const std::vector<MediaTrack::Id>& selectedTrackId) { |
| 541 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 595 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 542 | 596 |
| 543 // Track status notifications might be delivered asynchronously. If we receive | 597 // Track status notifications might be delivered asynchronously. If we receive |
| 544 // a notification when pipeline is stopped/shut down, it's safe to ignore it. | 598 // a notification when pipeline is stopped/shut down, it's safe to ignore it. |
| 545 if (state_ == kStopping || state_ == kStopped) { | 599 if (state_ == kStopping || state_ == kStopped) { |
| 546 return; | 600 return; |
| 547 } | 601 } |
| 548 | 602 |
| 549 DCHECK(demuxer_); | 603 DCHECK(demuxer_); |
| 550 DCHECK(shared_state_.renderer); | 604 DCHECK(shared_state_.renderer); |
| 551 | 605 |
| 552 base::TimeDelta currTime = (state_ == kPlaying) | 606 base::TimeDelta currTime = (state_ == kPlaying) |
| 553 ? shared_state_.renderer->GetMediaTime() | 607 ? shared_state_.renderer->GetMediaTime() |
| 554 : start_timestamp_; | 608 : demuxer_->GetStartTime(); |
| 555 demuxer_->OnSelectedVideoTrackChanged(selectedTrackId, currTime); | 609 demuxer_->OnSelectedVideoTrackChanged(selectedTrackId, currTime); |
| 556 } | 610 } |
| 557 | 611 |
| 558 void PipelineImpl::RendererWrapper::OnStatisticsUpdate( | 612 void PipelineImpl::RendererWrapper::OnStatisticsUpdate( |
| 559 const PipelineStatistics& stats) { | 613 const PipelineStatistics& stats) { |
| 560 DVLOG(3) << __func__; | 614 DVLOG(3) << __func__; |
| 561 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 615 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 562 | 616 |
| 563 base::AutoLock auto_lock(shared_state_lock_); | 617 base::AutoLock auto_lock(shared_state_lock_); |
| 564 shared_state_.statistics.audio_bytes_decoded += stats.audio_bytes_decoded; | 618 shared_state_.statistics.audio_bytes_decoded += stats.audio_bytes_decoded; |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 677 return; | 731 return; |
| 678 | 732 |
| 679 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_) | 733 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_) |
| 680 return; | 734 return; |
| 681 | 735 |
| 682 DCHECK_EQ(status_, PIPELINE_OK); | 736 DCHECK_EQ(status_, PIPELINE_OK); |
| 683 main_task_runner_->PostTask( | 737 main_task_runner_->PostTask( |
| 684 FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_)); | 738 FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_)); |
| 685 } | 739 } |
| 686 | 740 |
| 687 // Note that the usage of base::Unretained() with the renderers is considered | |
| 688 // safe as they are owned by |pending_callbacks_| and share the same lifetime. | |
| 689 // | |
| 690 // That being said, deleting the renderers while keeping |pending_callbacks_| | |
| 691 // running on the media thread would result in crashes. | |
| 692 void PipelineImpl::RendererWrapper::DoSeek(base::TimeDelta seek_timestamp, | |
| 693 const PipelineStatusCB& done_cb) { | |
| 694 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
| 695 DCHECK(!pending_callbacks_.get()); | |
| 696 DCHECK_EQ(state_, kSeeking); | |
| 697 SerialRunner::Queue bound_fns; | |
| 698 | |
| 699 // Pause. | |
| 700 if (text_renderer_) { | |
| 701 bound_fns.Push(base::Bind(&TextRenderer::Pause, | |
| 702 base::Unretained(text_renderer_.get()))); | |
| 703 } | |
| 704 | |
| 705 // Flush. | |
| 706 DCHECK(shared_state_.renderer); | |
| 707 bound_fns.Push(base::Bind(&Renderer::Flush, | |
| 708 base::Unretained(shared_state_.renderer.get()))); | |
| 709 | |
| 710 if (text_renderer_) { | |
| 711 bound_fns.Push(base::Bind(&TextRenderer::Flush, | |
| 712 base::Unretained(text_renderer_.get()))); | |
| 713 } | |
| 714 | |
| 715 // Seek demuxer. | |
| 716 bound_fns.Push( | |
| 717 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); | |
| 718 | |
| 719 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); | |
| 720 } | |
| 721 | |
| 722 void PipelineImpl::RendererWrapper::DoStop(const base::Closure& done_cb) { | |
| 723 DVLOG(2) << __func__; | |
| 724 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
| 725 DCHECK_EQ(state_, kStopping); | |
| 726 DCHECK(!pending_callbacks_.get()); | |
| 727 | |
| 728 DestroyRenderer(); | |
| 729 text_renderer_.reset(); | |
| 730 | |
| 731 if (demuxer_) { | |
| 732 demuxer_->Stop(); | |
| 733 demuxer_ = NULL; | |
| 734 } | |
| 735 | |
| 736 SetState(kStopped); | |
| 737 | |
| 738 // Post the stop callback to enqueue it after the tasks that may have been | |
| 739 // posted by Demuxer and Renderer during stopping. Note that in theory the | |
| 740 // tasks posted by Demuxer/Renderer may post even more tasks that will get | |
| 741 // enqueued after |done_cb|. This may be problematic because Demuxer may | |
| 742 // get destroyed as soon as |done_cb| is run. In practice this is not a | |
| 743 // problem, but ideally Demuxer should be destroyed on the media thread. | |
| 744 media_task_runner_->PostTask(FROM_HERE, done_cb); | |
| 745 } | |
| 746 | |
| 747 void PipelineImpl::RendererWrapper::SetState(State next_state) { | 741 void PipelineImpl::RendererWrapper::SetState(State next_state) { |
| 748 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 742 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 749 DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> " | 743 DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> " |
| 750 << PipelineImpl::GetStateString(next_state); | 744 << PipelineImpl::GetStateString(next_state); |
| 751 | 745 |
| 752 state_ = next_state; | 746 state_ = next_state; |
| 753 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); | 747 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); |
| 754 } | 748 } |
| 755 | 749 |
| 756 PipelineImpl::State PipelineImpl::RendererWrapper::GetNextState() const { | 750 void PipelineImpl::RendererWrapper::CompleteSeek(base::TimeDelta seek_time, |
| 751 PipelineStatus status) { | |
| 757 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 752 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 758 DCHECK_EQ(status_, PIPELINE_OK) | 753 DCHECK(state_ == kStarting || state_ == kSeeking || state_ == kResuming); |
| 759 << "State transitions don't happen when there's an error: " << status_; | |
| 760 | 754 |
| 761 switch (state_) { | 755 DCHECK(pending_callbacks_); |
| 762 case kCreated: | 756 pending_callbacks_.reset(); |
| 763 return kInitDemuxer; | |
| 764 | 757 |
| 765 case kInitDemuxer: | |
| 766 return kInitRenderer; | |
| 767 | |
| 768 case kInitRenderer: | |
| 769 case kSeeking: | |
| 770 return kPlaying; | |
| 771 | |
| 772 case kSuspending: | |
| 773 return kSuspended; | |
| 774 | |
| 775 case kSuspended: | |
| 776 return kResuming; | |
| 777 | |
| 778 case kResuming: | |
| 779 return kPlaying; | |
| 780 | |
| 781 case kPlaying: | |
| 782 case kStopping: | |
| 783 case kStopped: | |
| 784 break; | |
| 785 } | |
| 786 NOTREACHED() << "State has no transition: " << state_; | |
| 787 return state_; | |
| 788 } | |
| 789 void PipelineImpl::RendererWrapper::StateTransitionTask(PipelineStatus status) { | |
| 790 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
| 791 | |
| 792 // No-op any state transitions if we're stopping or already encountered error. | |
| 793 if (state_ == kStopping || state_ == kStopped || status_ != PIPELINE_OK) | |
| 794 return; | |
| 795 | |
| 796 // Report error from the previous operation. | |
| 797 if (status != PIPELINE_OK) { | 758 if (status != PIPELINE_OK) { |
| 798 OnPipelineError(status); | 759 OnPipelineError(status); |
| 799 return; | 760 return; |
| 800 } | 761 } |
| 801 | 762 |
| 802 // Guard against accidentally clearing |pending_callbacks_| for states that | 763 shared_state_.renderer->StartPlayingFrom( |
| 803 // use it as well as states that should not be using it. | 764 std::max(seek_time, demuxer_->GetStartTime())); |
| 804 DCHECK_EQ(pending_callbacks_.get() != NULL, | 765 { |
| 805 state_ == kSeeking || state_ == kSuspending || state_ == kResuming); | 766 base::AutoLock auto_lock(shared_state_lock_); |
| 767 shared_state_.suspend_timestamp = kNoTimestamp; | |
| 768 } | |
| 806 | 769 |
| 770 if (text_renderer_) | |
| 771 text_renderer_->StartPlaying(); | |
| 772 | |
| 773 shared_state_.renderer->SetPlaybackRate(playback_rate_); | |
| 774 shared_state_.renderer->SetVolume(volume_); | |
| 775 | |
| 776 SetState(kPlaying); | |
| 777 main_task_runner_->PostTask( | |
| 778 FROM_HERE, base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_)); | |
| 779 } | |
| 780 | |
| 781 void PipelineImpl::RendererWrapper::CompleteSuspend(PipelineStatus status) { | |
| 782 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
| 783 DCHECK_EQ(kSuspending, state_); | |
| 784 | |
| 785 DCHECK(pending_callbacks_); | |
| 807 pending_callbacks_.reset(); | 786 pending_callbacks_.reset(); |
| 808 | 787 |
| 809 PipelineStatusCB done_cb = | 788 // In case we are suspending or suspended, the error may be recoverable, |
| 810 base::Bind(&RendererWrapper::StateTransitionTask, weak_this_); | 789 // so don't propagate it now, instead let the subsequent seek during resume |
| 790 // propagate it if it's unrecoverable. | |
| 791 LOG_IF(WARNING, status != PIPELINE_OK) | |
| 792 << "Encountered pipeline error while suspending: " << status; | |
| 811 | 793 |
| 812 // Switch states, performing any entrance actions for the new state as well. | 794 DestroyRenderer(); |
| 813 SetState(GetNextState()); | 795 { |
| 814 switch (state_) { | 796 base::AutoLock auto_lock(shared_state_lock_); |
| 815 case kInitDemuxer: | 797 shared_state_.statistics.audio_memory_usage = 0; |
| 816 return InitializeDemuxer(done_cb); | 798 shared_state_.statistics.video_memory_usage = 0; |
| 799 } | |
| 817 | 800 |
| 818 case kInitRenderer: | 801 SetState(kSuspended); |
| 819 // When the state_ transfers to kInitRenderer, it means the demuxer has | 802 main_task_runner_->PostTask( |
| 820 // finished parsing the init info. It should call ReportMetadata in case | 803 FROM_HERE, base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_)); |
| 821 // meeting 'decode' error when passing media segment but WebMediaPlayer's | |
| 822 // ready_state_ is still ReadyStateHaveNothing. In that case, it will | |
| 823 // treat it as NetworkStateFormatError not NetworkStateDecodeError. | |
| 824 ReportMetadata(); | |
| 825 start_timestamp_ = demuxer_->GetStartTime(); | |
| 826 | |
| 827 return InitializeRenderer(done_cb); | |
| 828 | |
| 829 case kPlaying: | |
| 830 DCHECK(start_timestamp_ >= base::TimeDelta()); | |
| 831 shared_state_.renderer->StartPlayingFrom(start_timestamp_); | |
| 832 { | |
| 833 base::AutoLock auto_lock(shared_state_lock_); | |
| 834 shared_state_.suspend_timestamp = kNoTimestamp; | |
| 835 } | |
| 836 | |
| 837 if (text_renderer_) | |
| 838 text_renderer_->StartPlaying(); | |
| 839 | |
| 840 main_task_runner_->PostTask( | |
| 841 FROM_HERE, base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_, | |
| 842 start_timestamp_)); | |
| 843 | |
| 844 shared_state_.renderer->SetPlaybackRate(playback_rate_); | |
| 845 shared_state_.renderer->SetVolume(volume_); | |
| 846 return; | |
| 847 | |
| 848 case kSuspended: | |
| 849 DestroyRenderer(); | |
| 850 { | |
| 851 base::AutoLock auto_lock(shared_state_lock_); | |
| 852 shared_state_.statistics.audio_memory_usage = 0; | |
| 853 shared_state_.statistics.video_memory_usage = 0; | |
| 854 } | |
| 855 main_task_runner_->PostTask( | |
| 856 FROM_HERE, base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_, | |
| 857 shared_state_.suspend_timestamp)); | |
| 858 return; | |
| 859 | |
| 860 case kStopping: | |
| 861 case kStopped: | |
| 862 case kCreated: | |
| 863 case kSeeking: | |
| 864 case kSuspending: | |
| 865 case kResuming: | |
| 866 NOTREACHED() << "State has no transition: " << state_; | |
| 867 return; | |
| 868 } | |
| 869 } | 804 } |
| 870 | 805 |
| 871 void PipelineImpl::RendererWrapper::InitializeDemuxer( | 806 void PipelineImpl::RendererWrapper::InitializeDemuxer( |
| 872 const PipelineStatusCB& done_cb) { | 807 const PipelineStatusCB& done_cb) { |
| 873 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 808 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 874 | 809 |
| 875 demuxer_->Initialize(this, done_cb, !!text_renderer_); | 810 demuxer_->Initialize(this, done_cb, !!text_renderer_); |
| 876 } | 811 } |
| 877 | 812 |
| 878 void PipelineImpl::RendererWrapper::InitializeRenderer( | 813 void PipelineImpl::RendererWrapper::InitializeRenderer( |
| (...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1163 } | 1098 } |
| 1164 | 1099 |
| 1165 #define RETURN_STRING(state) \ | 1100 #define RETURN_STRING(state) \ |
| 1166 case state: \ | 1101 case state: \ |
| 1167 return #state; | 1102 return #state; |
| 1168 | 1103 |
| 1169 // static | 1104 // static |
| 1170 const char* PipelineImpl::GetStateString(State state) { | 1105 const char* PipelineImpl::GetStateString(State state) { |
| 1171 switch (state) { | 1106 switch (state) { |
| 1172 RETURN_STRING(kCreated); | 1107 RETURN_STRING(kCreated); |
| 1173 RETURN_STRING(kInitDemuxer); | 1108 RETURN_STRING(kStarting); |
| 1174 RETURN_STRING(kInitRenderer); | |
| 1175 RETURN_STRING(kSeeking); | 1109 RETURN_STRING(kSeeking); |
| 1176 RETURN_STRING(kPlaying); | 1110 RETURN_STRING(kPlaying); |
| 1177 RETURN_STRING(kStopping); | 1111 RETURN_STRING(kStopping); |
| 1178 RETURN_STRING(kStopped); | 1112 RETURN_STRING(kStopped); |
| 1179 RETURN_STRING(kSuspending); | 1113 RETURN_STRING(kSuspending); |
| 1180 RETURN_STRING(kSuspended); | 1114 RETURN_STRING(kSuspended); |
| 1181 RETURN_STRING(kResuming); | 1115 RETURN_STRING(kResuming); |
| 1182 } | 1116 } |
| 1183 NOTREACHED(); | 1117 NOTREACHED(); |
| 1184 return "INVALID"; | 1118 return "INVALID"; |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1276 | 1210 |
| 1277 void PipelineImpl::OnVideoOpacityChange(bool opaque) { | 1211 void PipelineImpl::OnVideoOpacityChange(bool opaque) { |
| 1278 DVLOG(2) << __func__; | 1212 DVLOG(2) << __func__; |
| 1279 DCHECK(thread_checker_.CalledOnValidThread()); | 1213 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1280 DCHECK(IsRunning()); | 1214 DCHECK(IsRunning()); |
| 1281 | 1215 |
| 1282 DCHECK(client_); | 1216 DCHECK(client_); |
| 1283 client_->OnVideoOpacityChange(opaque); | 1217 client_->OnVideoOpacityChange(opaque); |
| 1284 } | 1218 } |
| 1285 | 1219 |
| 1286 void PipelineImpl::OnSeekDone(base::TimeDelta start_time) { | 1220 void PipelineImpl::OnSeekDone() { |
| 1287 DVLOG(3) << __func__ << "(" << start_time.InMicroseconds() << ")"; | 1221 DVLOG(3) << __func__; |
| 1288 DCHECK(thread_checker_.CalledOnValidThread()); | 1222 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1289 DCHECK(IsRunning()); | 1223 DCHECK(IsRunning()); |
| 1290 | 1224 |
| 1291 DCHECK(!seek_cb_.is_null()); | 1225 DCHECK(!seek_cb_.is_null()); |
| 1292 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); | 1226 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); |
| 1293 } | 1227 } |
| 1294 | 1228 |
| 1295 void PipelineImpl::OnSuspendDone(base::TimeDelta suspend_time) { | 1229 void PipelineImpl::OnSuspendDone() { |
| 1296 DVLOG(3) << __func__ << "(" << suspend_time.InMicroseconds() << ")"; | 1230 DVLOG(3) << __func__; |
| 1297 DCHECK(thread_checker_.CalledOnValidThread()); | 1231 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1298 DCHECK(IsRunning()); | 1232 DCHECK(IsRunning()); |
| 1299 | 1233 |
| 1300 DCHECK(!suspend_cb_.is_null()); | 1234 DCHECK(!suspend_cb_.is_null()); |
| 1301 base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK); | 1235 base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK); |
| 1302 } | 1236 } |
| 1303 | 1237 |
| 1304 } // namespace media | 1238 } // namespace media |
| OLD | NEW |