| 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 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/bind_helpers.h" | 11 #include "base/bind_helpers.h" |
| 12 #include "base/callback.h" | 12 #include "base/callback.h" |
| 13 #include "base/callback_helpers.h" | 13 #include "base/callback_helpers.h" |
| 14 #include "base/command_line.h" | 14 #include "base/command_line.h" |
| 15 #include "base/compiler_specific.h" | 15 #include "base/compiler_specific.h" |
| 16 #include "base/location.h" | 16 #include "base/location.h" |
| 17 #include "base/memory/ptr_util.h" | 17 #include "base/memory/ptr_util.h" |
| 18 #include "base/metrics/histogram.h" | 18 #include "base/metrics/histogram.h" |
| 19 #include "base/single_thread_task_runner.h" | 19 #include "base/single_thread_task_runner.h" |
| 20 #include "base/stl_util.h" | 20 #include "base/stl_util.h" |
| 21 #include "base/strings/string_number_conversions.h" | 21 #include "base/strings/string_number_conversions.h" |
| 22 #include "base/strings/string_util.h" | 22 #include "base/strings/string_util.h" |
| 23 #include "base/synchronization/condition_variable.h" | 23 #include "base/synchronization/waitable_event.h" |
| 24 #include "base/thread_task_runner_handle.h" |
| 25 #include "media/base/bind_to_current_loop.h" |
| 24 #include "media/base/media_log.h" | 26 #include "media/base/media_log.h" |
| 25 #include "media/base/media_switches.h" | 27 #include "media/base/media_switches.h" |
| 26 #include "media/base/renderer.h" | 28 #include "media/base/renderer.h" |
| 27 #include "media/base/text_renderer.h" | 29 #include "media/base/text_renderer.h" |
| 28 #include "media/base/text_track_config.h" | 30 #include "media/base/text_track_config.h" |
| 29 #include "media/base/timestamp_constants.h" | 31 #include "media/base/timestamp_constants.h" |
| 30 #include "media/base/video_decoder_config.h" | 32 #include "media/base/video_decoder_config.h" |
| 31 | 33 |
| 32 using base::TimeDelta; | 34 using base::TimeDelta; |
| 33 | 35 |
| 34 namespace media { | 36 namespace media { |
| 35 | 37 |
| 36 PipelineImpl::PipelineImpl( | 38 PipelineImpl::PipelineImpl( |
| 37 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | 39 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, |
| 38 MediaLog* media_log) | 40 MediaLog* media_log) |
| 39 : task_runner_(task_runner), | 41 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| 42 media_task_runner_(media_task_runner), |
| 40 media_log_(media_log), | 43 media_log_(media_log), |
| 41 running_(false), | 44 running_(false), |
| 42 did_loading_progress_(false), | 45 did_loading_progress_(false), |
| 43 volume_(1.0f), | 46 volume_(1.0f), |
| 44 playback_rate_(0.0), | 47 playback_rate_(0.0), |
| 45 status_(PIPELINE_OK), | 48 status_(PIPELINE_OK), |
| 46 state_(kCreated), | 49 state_(kCreated), |
| 47 suspend_timestamp_(kNoTimestamp()), | 50 suspend_timestamp_(kNoTimestamp()), |
| 48 renderer_ended_(false), | 51 renderer_ended_(false), |
| 49 text_renderer_ended_(false), | 52 text_renderer_ended_(false), |
| 50 demuxer_(NULL), | 53 demuxer_(NULL), |
| 51 cdm_context_(nullptr), | 54 cdm_context_(nullptr), |
| 52 weak_factory_(this) { | 55 weak_factory_(this) { |
| 53 weak_this_ = weak_factory_.GetWeakPtr(); | 56 weak_this_ = weak_factory_.GetWeakPtr(); |
| 54 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); | 57 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); |
| 55 } | 58 } |
| 56 | 59 |
| 57 PipelineImpl::~PipelineImpl() { | 60 PipelineImpl::~PipelineImpl() { |
| 58 DCHECK(thread_checker_.CalledOnValidThread()) | 61 DCHECK(main_task_runner_->BelongsToCurrentThread()) |
| 59 << "Pipeline must be destroyed on same thread that created it"; | 62 << "Pipeline must be destroyed on same thread that created it"; |
| 60 DCHECK(!running_) << "Stop() must complete before destroying object"; | 63 DCHECK(!running_) << "Stop() must complete before destroying object"; |
| 61 DCHECK(stop_cb_.is_null()); | |
| 62 DCHECK(seek_cb_.is_null()); | 64 DCHECK(seek_cb_.is_null()); |
| 63 } | 65 } |
| 64 | 66 |
| 65 void PipelineImpl::Start(Demuxer* demuxer, | 67 void PipelineImpl::Start(Demuxer* demuxer, |
| 66 std::unique_ptr<Renderer> renderer, | 68 std::unique_ptr<Renderer> renderer, |
| 67 const base::Closure& ended_cb, | 69 Client* client, |
| 68 const PipelineStatusCB& error_cb, | 70 const PipelineStatusCB& seek_cb) { |
| 69 const PipelineStatusCB& seek_cb, | 71 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 70 const PipelineMetadataCB& metadata_cb, | 72 DCHECK(client); |
| 71 const BufferingStateCB& buffering_state_cb, | |
| 72 const base::Closure& duration_change_cb, | |
| 73 const AddTextTrackCB& add_text_track_cb, | |
| 74 const base::Closure& waiting_for_decryption_key_cb) { | |
| 75 DCHECK(!ended_cb.is_null()); | |
| 76 DCHECK(!error_cb.is_null()); | |
| 77 DCHECK(!seek_cb.is_null()); | 73 DCHECK(!seek_cb.is_null()); |
| 78 DCHECK(!metadata_cb.is_null()); | |
| 79 DCHECK(!buffering_state_cb.is_null()); | |
| 80 | 74 |
| 81 base::AutoLock auto_lock(lock_); | 75 base::AutoLock auto_lock(lock_); |
| 82 CHECK(!running_) << "Media pipeline is already running"; | 76 CHECK(!running_) << "Media pipeline is already running"; |
| 83 running_ = true; | 77 running_ = true; |
| 84 | 78 |
| 85 demuxer_ = demuxer; | 79 demuxer_ = demuxer; |
| 86 renderer_ = std::move(renderer); | 80 renderer_ = std::move(renderer); |
| 87 ended_cb_ = ended_cb; | 81 client_weak_factory_.reset(new base::WeakPtrFactory<Client>(client)); |
| 88 error_cb_ = error_cb; | 82 weak_client_ = client_weak_factory_->GetWeakPtr(); |
| 89 seek_cb_ = seek_cb; | 83 seek_cb_ = media::BindToCurrentLoop(seek_cb); |
| 90 metadata_cb_ = metadata_cb; | 84 media_task_runner_->PostTask( |
| 91 buffering_state_cb_ = buffering_state_cb; | 85 FROM_HERE, base::Bind(&PipelineImpl::StartTask, weak_this_)); |
| 92 duration_change_cb_ = duration_change_cb; | |
| 93 add_text_track_cb_ = add_text_track_cb; | |
| 94 waiting_for_decryption_key_cb_ = waiting_for_decryption_key_cb; | |
| 95 | |
| 96 task_runner_->PostTask(FROM_HERE, | |
| 97 base::Bind(&PipelineImpl::StartTask, weak_this_)); | |
| 98 } | 86 } |
| 99 | 87 |
| 100 void PipelineImpl::Stop(const base::Closure& stop_cb) { | 88 void PipelineImpl::Stop() { |
| 89 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 101 DVLOG(2) << __FUNCTION__; | 90 DVLOG(2) << __FUNCTION__; |
| 102 task_runner_->PostTask( | 91 |
| 103 FROM_HERE, base::Bind(&PipelineImpl::StopTask, weak_this_, stop_cb)); | 92 if (media_task_runner_ != main_task_runner_) { |
| 93 // This path is executed by production code where the two task runners - |
| 94 // main and media - live on different threads. |
| 95 // TODO(alokp): It may be possible to not have to wait for StopTask by |
| 96 // moving the members accessed on media thread into a class/struct and |
| 97 // DeleteSoon the instance on the media thread. |
| 98 base::WaitableEvent waiter(false, false); |
| 99 base::Closure stop_cb = |
| 100 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&waiter)); |
| 101 // If posting the task fails or the posted task fails to run, |
| 102 // we will wait here forever. So add a CHECK to make sure we do not run |
| 103 // into those situations. |
| 104 CHECK(weak_factory_.HasWeakPtrs()); |
| 105 CHECK(media_task_runner_->PostTask( |
| 106 FROM_HERE, base::Bind(&PipelineImpl::StopTask, weak_this_, stop_cb))); |
| 107 waiter.Wait(); |
| 108 } else { |
| 109 // This path is executed by unittests that share media and main threads. |
| 110 StopTask(base::Bind(&base::DoNothing)); |
| 111 } |
| 112 // Invalidate client weak pointer effectively canceling all pending client |
| 113 // notifications in the message queue. |
| 114 client_weak_factory_.reset(); |
| 104 } | 115 } |
| 105 | 116 |
| 106 void PipelineImpl::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) { | 117 void PipelineImpl::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) { |
| 107 base::AutoLock auto_lock(lock_); | 118 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 108 if (!running_) { | 119 |
| 120 if (!IsRunning()) { |
| 109 DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek()."; | 121 DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek()."; |
| 110 return; | 122 return; |
| 111 } | 123 } |
| 112 | 124 |
| 113 task_runner_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::SeekTask, | 125 media_task_runner_->PostTask( |
| 114 weak_this_, time, seek_cb)); | 126 FROM_HERE, base::Bind(&PipelineImpl::SeekTask, weak_this_, time, |
| 127 media::BindToCurrentLoop(seek_cb))); |
| 115 } | 128 } |
| 116 | 129 |
| 117 bool PipelineImpl::IsRunning() const { | 130 bool PipelineImpl::IsRunning() const { |
| 131 // TODO(alokp): Add thread DCHECK after removing the internal usage on |
| 132 // media thread. |
| 118 base::AutoLock auto_lock(lock_); | 133 base::AutoLock auto_lock(lock_); |
| 119 return running_; | 134 return running_; |
| 120 } | 135 } |
| 121 | 136 |
| 122 double PipelineImpl::GetPlaybackRate() const { | 137 double PipelineImpl::GetPlaybackRate() const { |
| 138 // TODO(alokp): Add thread DCHECK after removing the internal usage on |
| 139 // media thread. |
| 123 base::AutoLock auto_lock(lock_); | 140 base::AutoLock auto_lock(lock_); |
| 124 return playback_rate_; | 141 return playback_rate_; |
| 125 } | 142 } |
| 126 | 143 |
| 127 void PipelineImpl::SetPlaybackRate(double playback_rate) { | 144 void PipelineImpl::SetPlaybackRate(double playback_rate) { |
| 145 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 146 |
| 128 if (playback_rate < 0.0) | 147 if (playback_rate < 0.0) |
| 129 return; | 148 return; |
| 130 | 149 |
| 131 base::AutoLock auto_lock(lock_); | 150 base::AutoLock auto_lock(lock_); |
| 132 playback_rate_ = playback_rate; | 151 playback_rate_ = playback_rate; |
| 133 if (running_) { | 152 if (running_) { |
| 134 task_runner_->PostTask(FROM_HERE, | 153 media_task_runner_->PostTask( |
| 135 base::Bind(&PipelineImpl::PlaybackRateChangedTask, | 154 FROM_HERE, base::Bind(&PipelineImpl::PlaybackRateChangedTask, |
| 136 weak_this_, playback_rate)); | 155 weak_this_, playback_rate)); |
| 137 } | 156 } |
| 138 } | 157 } |
| 139 | 158 |
| 140 void PipelineImpl::Suspend(const PipelineStatusCB& suspend_cb) { | 159 void PipelineImpl::Suspend(const PipelineStatusCB& suspend_cb) { |
| 141 task_runner_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::SuspendTask, | 160 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 142 weak_this_, suspend_cb)); | 161 |
| 162 media_task_runner_->PostTask( |
| 163 FROM_HERE, base::Bind(&PipelineImpl::SuspendTask, weak_this_, |
| 164 media::BindToCurrentLoop(suspend_cb))); |
| 143 } | 165 } |
| 144 | 166 |
| 145 void PipelineImpl::Resume(std::unique_ptr<Renderer> renderer, | 167 void PipelineImpl::Resume(std::unique_ptr<Renderer> renderer, |
| 146 base::TimeDelta timestamp, | 168 base::TimeDelta timestamp, |
| 147 const PipelineStatusCB& seek_cb) { | 169 const PipelineStatusCB& seek_cb) { |
| 148 task_runner_->PostTask( | 170 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 149 FROM_HERE, base::Bind(&PipelineImpl::ResumeTask, weak_this_, | 171 |
| 150 base::Passed(&renderer), timestamp, seek_cb)); | 172 media_task_runner_->PostTask( |
| 173 FROM_HERE, |
| 174 base::Bind(&PipelineImpl::ResumeTask, weak_this_, base::Passed(&renderer), |
| 175 timestamp, media::BindToCurrentLoop(seek_cb))); |
| 151 } | 176 } |
| 152 | 177 |
| 153 float PipelineImpl::GetVolume() const { | 178 float PipelineImpl::GetVolume() const { |
| 179 // TODO(alokp): Add thread DCHECK after removing the internal usage on |
| 180 // media thread. |
| 154 base::AutoLock auto_lock(lock_); | 181 base::AutoLock auto_lock(lock_); |
| 155 return volume_; | 182 return volume_; |
| 156 } | 183 } |
| 157 | 184 |
| 158 void PipelineImpl::SetVolume(float volume) { | 185 void PipelineImpl::SetVolume(float volume) { |
| 186 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 187 |
| 159 if (volume < 0.0f || volume > 1.0f) | 188 if (volume < 0.0f || volume > 1.0f) |
| 160 return; | 189 return; |
| 161 | 190 |
| 162 base::AutoLock auto_lock(lock_); | 191 base::AutoLock auto_lock(lock_); |
| 163 volume_ = volume; | 192 volume_ = volume; |
| 164 if (running_) { | 193 if (running_) { |
| 165 task_runner_->PostTask( | 194 media_task_runner_->PostTask( |
| 166 FROM_HERE, | 195 FROM_HERE, |
| 167 base::Bind(&PipelineImpl::VolumeChangedTask, weak_this_, volume)); | 196 base::Bind(&PipelineImpl::VolumeChangedTask, weak_this_, volume)); |
| 168 } | 197 } |
| 169 } | 198 } |
| 170 | 199 |
| 171 TimeDelta PipelineImpl::GetMediaTime() const { | 200 TimeDelta PipelineImpl::GetMediaTime() const { |
| 201 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 202 |
| 172 base::AutoLock auto_lock(lock_); | 203 base::AutoLock auto_lock(lock_); |
| 173 if (suspend_timestamp_ != kNoTimestamp()) | 204 if (suspend_timestamp_ != kNoTimestamp()) |
| 174 return suspend_timestamp_; | 205 return suspend_timestamp_; |
| 175 return renderer_ ? std::min(renderer_->GetMediaTime(), duration_) | 206 return renderer_ ? std::min(renderer_->GetMediaTime(), duration_) |
| 176 : TimeDelta(); | 207 : TimeDelta(); |
| 177 } | 208 } |
| 178 | 209 |
| 179 Ranges<TimeDelta> PipelineImpl::GetBufferedTimeRanges() const { | 210 Ranges<TimeDelta> PipelineImpl::GetBufferedTimeRanges() const { |
| 211 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 212 |
| 180 base::AutoLock auto_lock(lock_); | 213 base::AutoLock auto_lock(lock_); |
| 181 return buffered_time_ranges_; | 214 return buffered_time_ranges_; |
| 182 } | 215 } |
| 183 | 216 |
| 184 TimeDelta PipelineImpl::GetMediaDuration() const { | 217 TimeDelta PipelineImpl::GetMediaDuration() const { |
| 218 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 219 |
| 185 base::AutoLock auto_lock(lock_); | 220 base::AutoLock auto_lock(lock_); |
| 186 return duration_; | 221 return duration_; |
| 187 } | 222 } |
| 188 | 223 |
| 189 bool PipelineImpl::DidLoadingProgress() { | 224 bool PipelineImpl::DidLoadingProgress() { |
| 225 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 226 |
| 190 base::AutoLock auto_lock(lock_); | 227 base::AutoLock auto_lock(lock_); |
| 191 bool ret = did_loading_progress_; | 228 bool ret = did_loading_progress_; |
| 192 did_loading_progress_ = false; | 229 did_loading_progress_ = false; |
| 193 return ret; | 230 return ret; |
| 194 } | 231 } |
| 195 | 232 |
| 196 PipelineStatistics PipelineImpl::GetStatistics() const { | 233 PipelineStatistics PipelineImpl::GetStatistics() const { |
| 234 // TODO(alokp): Add thread DCHECK after removing the internal usage on |
| 235 // media thread. |
| 197 base::AutoLock auto_lock(lock_); | 236 base::AutoLock auto_lock(lock_); |
| 198 return statistics_; | 237 return statistics_; |
| 199 } | 238 } |
| 200 | 239 |
| 201 void PipelineImpl::SetCdm(CdmContext* cdm_context, | 240 void PipelineImpl::SetCdm(CdmContext* cdm_context, |
| 202 const CdmAttachedCB& cdm_attached_cb) { | 241 const CdmAttachedCB& cdm_attached_cb) { |
| 203 task_runner_->PostTask( | 242 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 243 |
| 244 media_task_runner_->PostTask( |
| 204 FROM_HERE, base::Bind(&PipelineImpl::SetCdmTask, weak_this_, cdm_context, | 245 FROM_HERE, base::Bind(&PipelineImpl::SetCdmTask, weak_this_, cdm_context, |
| 205 cdm_attached_cb)); | 246 cdm_attached_cb)); |
| 206 } | 247 } |
| 207 | 248 |
| 208 void PipelineImpl::SetErrorForTesting(PipelineStatus status) { | 249 void PipelineImpl::SetErrorForTesting(PipelineStatus status) { |
| 209 OnError(status); | 250 OnError(status); |
| 210 } | 251 } |
| 211 | 252 |
| 212 bool PipelineImpl::HasWeakPtrsForTesting() const { | 253 bool PipelineImpl::HasWeakPtrsForTesting() const { |
| 213 DCHECK(task_runner_->BelongsToCurrentThread()); | 254 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 214 return weak_factory_.HasWeakPtrs(); | 255 return weak_factory_.HasWeakPtrs(); |
| 215 } | 256 } |
| 216 | 257 |
| 217 void PipelineImpl::SetState(State next_state) { | 258 void PipelineImpl::SetState(State next_state) { |
| 259 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 218 DVLOG(1) << GetStateString(state_) << " -> " << GetStateString(next_state); | 260 DVLOG(1) << GetStateString(state_) << " -> " << GetStateString(next_state); |
| 219 | 261 |
| 220 state_ = next_state; | 262 state_ = next_state; |
| 221 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); | 263 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); |
| 222 } | 264 } |
| 223 | 265 |
| 224 #define RETURN_STRING(state) \ | 266 #define RETURN_STRING(state) \ |
| 225 case state: \ | 267 case state: \ |
| 226 return #state; | 268 return #state; |
| 227 | 269 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 238 RETURN_STRING(kSuspended); | 280 RETURN_STRING(kSuspended); |
| 239 RETURN_STRING(kResuming); | 281 RETURN_STRING(kResuming); |
| 240 } | 282 } |
| 241 NOTREACHED(); | 283 NOTREACHED(); |
| 242 return "INVALID"; | 284 return "INVALID"; |
| 243 } | 285 } |
| 244 | 286 |
| 245 #undef RETURN_STRING | 287 #undef RETURN_STRING |
| 246 | 288 |
| 247 PipelineImpl::State PipelineImpl::GetNextState() const { | 289 PipelineImpl::State PipelineImpl::GetNextState() const { |
| 248 DCHECK(task_runner_->BelongsToCurrentThread()); | 290 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 249 DCHECK(stop_cb_.is_null()) << "State transitions don't happen when stopping"; | 291 DCHECK(stop_cb_.is_null()) << "State transitions don't happen when stopping"; |
| 250 DCHECK_EQ(status_, PIPELINE_OK) | 292 DCHECK_EQ(status_, PIPELINE_OK) |
| 251 << "State transitions don't happen when there's an error: " << status_; | 293 << "State transitions don't happen when there's an error: " << status_; |
| 252 | 294 |
| 253 switch (state_) { | 295 switch (state_) { |
| 254 case kCreated: | 296 case kCreated: |
| 255 return kInitDemuxer; | 297 return kInitDemuxer; |
| 256 | 298 |
| 257 case kInitDemuxer: | 299 case kInitDemuxer: |
| 258 return kInitRenderer; | 300 return kInitRenderer; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 273 case kPlaying: | 315 case kPlaying: |
| 274 case kStopping: | 316 case kStopping: |
| 275 case kStopped: | 317 case kStopped: |
| 276 break; | 318 break; |
| 277 } | 319 } |
| 278 NOTREACHED() << "State has no transition: " << state_; | 320 NOTREACHED() << "State has no transition: " << state_; |
| 279 return state_; | 321 return state_; |
| 280 } | 322 } |
| 281 | 323 |
| 282 void PipelineImpl::OnDemuxerError(PipelineStatus error) { | 324 void PipelineImpl::OnDemuxerError(PipelineStatus error) { |
| 283 task_runner_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::ErrorChangedTask, | 325 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
| 284 weak_this_, error)); | 326 // implementations call DemuxerHost on the media thread. |
| 327 media_task_runner_->PostTask( |
| 328 FROM_HERE, |
| 329 base::Bind(&PipelineImpl::ErrorChangedTask, weak_this_, error)); |
| 285 } | 330 } |
| 286 | 331 |
| 287 void PipelineImpl::AddTextStream(DemuxerStream* text_stream, | 332 void PipelineImpl::AddTextStream(DemuxerStream* text_stream, |
| 288 const TextTrackConfig& config) { | 333 const TextTrackConfig& config) { |
| 289 task_runner_->PostTask( | 334 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
| 335 // implementations call DemuxerHost on the media thread. |
| 336 media_task_runner_->PostTask( |
| 290 FROM_HERE, base::Bind(&PipelineImpl::AddTextStreamTask, weak_this_, | 337 FROM_HERE, base::Bind(&PipelineImpl::AddTextStreamTask, weak_this_, |
| 291 text_stream, config)); | 338 text_stream, config)); |
| 292 } | 339 } |
| 293 | 340 |
| 294 void PipelineImpl::RemoveTextStream(DemuxerStream* text_stream) { | 341 void PipelineImpl::RemoveTextStream(DemuxerStream* text_stream) { |
| 295 task_runner_->PostTask( | 342 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
| 343 // implementations call DemuxerHost on the media thread. |
| 344 media_task_runner_->PostTask( |
| 296 FROM_HERE, | 345 FROM_HERE, |
| 297 base::Bind(&PipelineImpl::RemoveTextStreamTask, weak_this_, text_stream)); | 346 base::Bind(&PipelineImpl::RemoveTextStreamTask, weak_this_, text_stream)); |
| 298 } | 347 } |
| 299 | 348 |
| 300 void PipelineImpl::OnError(PipelineStatus error) { | 349 void PipelineImpl::OnError(PipelineStatus error) { |
| 301 DCHECK(task_runner_->BelongsToCurrentThread()); | 350 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 302 DCHECK(IsRunning()); | 351 DCHECK(IsRunning()); |
| 303 DCHECK_NE(PIPELINE_OK, error); | 352 DCHECK_NE(PIPELINE_OK, error); |
| 304 VLOG(1) << "Media pipeline error: " << error; | 353 VLOG(1) << "Media pipeline error: " << error; |
| 305 | 354 |
| 306 task_runner_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::ErrorChangedTask, | 355 media_task_runner_->PostTask( |
| 307 weak_this_, error)); | 356 FROM_HERE, |
| 357 base::Bind(&PipelineImpl::ErrorChangedTask, weak_this_, error)); |
| 308 } | 358 } |
| 309 | 359 |
| 310 void PipelineImpl::SetDuration(TimeDelta duration) { | 360 void PipelineImpl::SetDuration(TimeDelta duration) { |
| 361 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
| 362 // implementations call DemuxerHost on the media thread. |
| 311 DCHECK(IsRunning()); | 363 DCHECK(IsRunning()); |
| 312 media_log_->AddEvent(media_log_->CreateTimeEvent(MediaLogEvent::DURATION_SET, | 364 media_log_->AddEvent(media_log_->CreateTimeEvent(MediaLogEvent::DURATION_SET, |
| 313 "duration", duration)); | 365 "duration", duration)); |
| 314 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration); | 366 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration); |
| 315 | 367 |
| 316 base::AutoLock auto_lock(lock_); | 368 base::AutoLock auto_lock(lock_); |
| 317 duration_ = duration; | 369 duration_ = duration; |
| 318 if (!duration_change_cb_.is_null()) | 370 main_task_runner_->PostTask( |
| 319 duration_change_cb_.Run(); | 371 FROM_HERE, base::Bind(&Pipeline::Client::OnDurationChange, weak_client_)); |
| 320 } | 372 } |
| 321 | 373 |
| 322 void PipelineImpl::StateTransitionTask(PipelineStatus status) { | 374 void PipelineImpl::StateTransitionTask(PipelineStatus status) { |
| 323 DCHECK(task_runner_->BelongsToCurrentThread()); | 375 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 324 | 376 |
| 325 // No-op any state transitions if we're stopping. | 377 // No-op any state transitions if we're stopping. |
| 326 if (state_ == kStopping || state_ == kStopped) | 378 if (state_ == kStopping || state_ == kStopped) |
| 327 return; | 379 return; |
| 328 | 380 |
| 329 // Preserve existing abnormal status, otherwise update based on the result of | 381 // Report error from the previous operation. |
| 330 // the previous operation. | 382 if (status != PIPELINE_OK) { |
| 331 status_ = (status_ != PIPELINE_OK ? status_ : status); | 383 ErrorChangedTask(status); |
| 332 | |
| 333 if (status_ != PIPELINE_OK) { | |
| 334 ErrorChangedTask(status_); | |
| 335 return; | 384 return; |
| 336 } | 385 } |
| 337 | 386 |
| 338 // Guard against accidentally clearing |pending_callbacks_| for states that | 387 // Guard against accidentally clearing |pending_callbacks_| for states that |
| 339 // use it as well as states that should not be using it. | 388 // use it as well as states that should not be using it. |
| 340 DCHECK_EQ(pending_callbacks_.get() != NULL, | 389 DCHECK_EQ(pending_callbacks_.get() != NULL, |
| 341 state_ == kSeeking || state_ == kSuspending || state_ == kResuming); | 390 state_ == kSeeking || state_ == kSuspending || state_ == kResuming); |
| 342 | 391 |
| 343 pending_callbacks_.reset(); | 392 pending_callbacks_.reset(); |
| 344 | 393 |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 400 } | 449 } |
| 401 } | 450 } |
| 402 | 451 |
| 403 // Note that the usage of base::Unretained() with the renderers is considered | 452 // Note that the usage of base::Unretained() with the renderers is considered |
| 404 // safe as they are owned by |pending_callbacks_| and share the same lifetime. | 453 // safe as they are owned by |pending_callbacks_| and share the same lifetime. |
| 405 // | 454 // |
| 406 // That being said, deleting the renderers while keeping |pending_callbacks_| | 455 // That being said, deleting the renderers while keeping |pending_callbacks_| |
| 407 // running on the media thread would result in crashes. | 456 // running on the media thread would result in crashes. |
| 408 void PipelineImpl::DoSeek(TimeDelta seek_timestamp, | 457 void PipelineImpl::DoSeek(TimeDelta seek_timestamp, |
| 409 const PipelineStatusCB& done_cb) { | 458 const PipelineStatusCB& done_cb) { |
| 410 DCHECK(task_runner_->BelongsToCurrentThread()); | 459 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 411 DCHECK(!pending_callbacks_.get()); | 460 DCHECK(!pending_callbacks_.get()); |
| 412 DCHECK_EQ(state_, kSeeking); | 461 DCHECK_EQ(state_, kSeeking); |
| 413 SerialRunner::Queue bound_fns; | 462 SerialRunner::Queue bound_fns; |
| 414 | 463 |
| 415 // Pause. | 464 // Pause. |
| 416 if (text_renderer_) { | 465 if (text_renderer_) { |
| 417 bound_fns.Push(base::Bind(&TextRenderer::Pause, | 466 bound_fns.Push(base::Bind(&TextRenderer::Pause, |
| 418 base::Unretained(text_renderer_.get()))); | 467 base::Unretained(text_renderer_.get()))); |
| 419 } | 468 } |
| 420 | 469 |
| 421 // Flush. | 470 // Flush. |
| 422 DCHECK(renderer_); | 471 DCHECK(renderer_); |
| 423 bound_fns.Push( | 472 bound_fns.Push( |
| 424 base::Bind(&Renderer::Flush, base::Unretained(renderer_.get()))); | 473 base::Bind(&Renderer::Flush, base::Unretained(renderer_.get()))); |
| 425 | 474 |
| 426 if (text_renderer_) { | 475 if (text_renderer_) { |
| 427 bound_fns.Push(base::Bind(&TextRenderer::Flush, | 476 bound_fns.Push(base::Bind(&TextRenderer::Flush, |
| 428 base::Unretained(text_renderer_.get()))); | 477 base::Unretained(text_renderer_.get()))); |
| 429 } | 478 } |
| 430 | 479 |
| 431 // Seek demuxer. | 480 // Seek demuxer. |
| 432 bound_fns.Push( | 481 bound_fns.Push( |
| 433 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); | 482 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); |
| 434 | 483 |
| 435 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); | 484 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); |
| 436 } | 485 } |
| 437 | 486 |
| 438 void PipelineImpl::DoStop(const PipelineStatusCB& done_cb) { | 487 void PipelineImpl::DoStop() { |
| 439 DVLOG(2) << __FUNCTION__; | 488 DVLOG(2) << __FUNCTION__; |
| 440 DCHECK(task_runner_->BelongsToCurrentThread()); | 489 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 490 DCHECK_EQ(state_, kStopping); |
| 441 DCHECK(!pending_callbacks_.get()); | 491 DCHECK(!pending_callbacks_.get()); |
| 442 | 492 |
| 443 // TODO(scherkus): Enforce that Renderer is only called on a single thread, | 493 // TODO(scherkus): Enforce that Renderer is only called on a single thread, |
| 444 // even for accessing media time http://crbug.com/370634 | 494 // even for accessing media time http://crbug.com/370634 |
| 445 std::unique_ptr<Renderer> renderer; | 495 std::unique_ptr<Renderer> renderer; |
| 446 { | 496 { |
| 447 base::AutoLock auto_lock(lock_); | 497 base::AutoLock auto_lock(lock_); |
| 448 renderer.swap(renderer_); | 498 renderer.swap(renderer_); |
| 449 } | 499 } |
| 450 renderer.reset(); | 500 renderer.reset(); |
| 451 text_renderer_.reset(); | 501 text_renderer_.reset(); |
| 452 | 502 |
| 453 if (demuxer_) { | 503 if (demuxer_) { |
| 454 demuxer_->Stop(); | 504 demuxer_->Stop(); |
| 455 demuxer_ = NULL; | 505 demuxer_ = NULL; |
| 456 } | 506 } |
| 457 | 507 |
| 458 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK)); | |
| 459 } | |
| 460 | |
| 461 void PipelineImpl::OnStopCompleted(PipelineStatus status) { | |
| 462 DVLOG(2) << __FUNCTION__; | |
| 463 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 464 DCHECK_EQ(state_, kStopping); | |
| 465 DCHECK(!renderer_); | |
| 466 DCHECK(!text_renderer_); | |
| 467 | |
| 468 { | 508 { |
| 469 base::AutoLock auto_lock(lock_); | 509 base::AutoLock auto_lock(lock_); |
| 470 running_ = false; | 510 running_ = false; |
| 471 } | 511 } |
| 472 | |
| 473 SetState(kStopped); | 512 SetState(kStopped); |
| 474 demuxer_ = NULL; | |
| 475 | 513 |
| 476 // If we stop during initialization/seeking/suspending we don't want to leave | 514 // If we stop during initialization/seeking/suspending we don't want to leave |
| 477 // outstanding callbacks around. | 515 // outstanding callbacks around. The callbacks also do not get run if the |
| 478 if (!seek_cb_.is_null()) { | 516 // pipeline is stopped before it had a chance to complete outstanding tasks. |
| 479 base::ResetAndReturn(&seek_cb_).Run(status_); | 517 seek_cb_.Reset(); |
| 480 error_cb_.Reset(); | 518 suspend_cb_.Reset(); |
| 481 } | 519 |
| 482 if (!suspend_cb_.is_null()) { | |
| 483 base::ResetAndReturn(&suspend_cb_).Run(status_); | |
| 484 error_cb_.Reset(); | |
| 485 } | |
| 486 if (!stop_cb_.is_null()) { | 520 if (!stop_cb_.is_null()) { |
| 487 error_cb_.Reset(); | |
| 488 | |
| 489 // Invalid all weak pointers so it's safe to destroy |this| on the render | 521 // Invalid all weak pointers so it's safe to destroy |this| on the render |
| 490 // main thread. | 522 // main thread. |
| 491 weak_factory_.InvalidateWeakPtrs(); | 523 weak_factory_.InvalidateWeakPtrs(); |
| 492 | 524 |
| 493 base::ResetAndReturn(&stop_cb_).Run(); | 525 // Post the stop callback to enqueue it after the tasks that may have been |
| 494 | 526 // Demuxer and Renderer during stopping. |
| 495 // NOTE: pipeline may be deleted at this point in time as a result of | 527 media_task_runner_->PostTask(FROM_HERE, base::ResetAndReturn(&stop_cb_)); |
| 496 // executing |stop_cb_|. | |
| 497 return; | |
| 498 } | |
| 499 if (!error_cb_.is_null()) { | |
| 500 DCHECK_NE(status_, PIPELINE_OK); | |
| 501 base::ResetAndReturn(&error_cb_).Run(status_); | |
| 502 } | 528 } |
| 503 } | 529 } |
| 504 | 530 |
| 505 void PipelineImpl::OnBufferedTimeRangesChanged( | 531 void PipelineImpl::OnBufferedTimeRangesChanged( |
| 506 const Ranges<base::TimeDelta>& ranges) { | 532 const Ranges<base::TimeDelta>& ranges) { |
| 533 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
| 534 // implementations call DemuxerHost on the media thread. |
| 507 base::AutoLock auto_lock(lock_); | 535 base::AutoLock auto_lock(lock_); |
| 508 buffered_time_ranges_ = ranges; | 536 buffered_time_ranges_ = ranges; |
| 509 did_loading_progress_ = true; | 537 did_loading_progress_ = true; |
| 510 } | 538 } |
| 511 | 539 |
| 512 // Called from any thread. | 540 // Called from any thread. |
| 513 void PipelineImpl::OnUpdateStatistics(const PipelineStatistics& stats_delta) { | 541 void PipelineImpl::OnUpdateStatistics(const PipelineStatistics& stats_delta) { |
| 514 base::AutoLock auto_lock(lock_); | 542 base::AutoLock auto_lock(lock_); |
| 515 statistics_.audio_bytes_decoded += stats_delta.audio_bytes_decoded; | 543 statistics_.audio_bytes_decoded += stats_delta.audio_bytes_decoded; |
| 516 statistics_.video_bytes_decoded += stats_delta.video_bytes_decoded; | 544 statistics_.video_bytes_decoded += stats_delta.video_bytes_decoded; |
| 517 statistics_.video_frames_decoded += stats_delta.video_frames_decoded; | 545 statistics_.video_frames_decoded += stats_delta.video_frames_decoded; |
| 518 statistics_.video_frames_dropped += stats_delta.video_frames_dropped; | 546 statistics_.video_frames_dropped += stats_delta.video_frames_dropped; |
| 519 statistics_.audio_memory_usage += stats_delta.audio_memory_usage; | 547 statistics_.audio_memory_usage += stats_delta.audio_memory_usage; |
| 520 statistics_.video_memory_usage += stats_delta.video_memory_usage; | 548 statistics_.video_memory_usage += stats_delta.video_memory_usage; |
| 521 } | 549 } |
| 522 | 550 |
| 551 void PipelineImpl::OnWaitingForDecryptionKey() { |
| 552 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 553 |
| 554 main_task_runner_->PostTask( |
| 555 FROM_HERE, |
| 556 base::Bind(&Pipeline::Client::OnWaitingForDecryptionKey, weak_client_)); |
| 557 } |
| 558 |
| 523 void PipelineImpl::StartTask() { | 559 void PipelineImpl::StartTask() { |
| 524 DCHECK(task_runner_->BelongsToCurrentThread()); | 560 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 525 | 561 |
| 526 CHECK_EQ(kCreated, state_) | 562 CHECK_EQ(kCreated, state_) |
| 527 << "Media pipeline cannot be started more than once"; | 563 << "Media pipeline cannot be started more than once"; |
| 528 | 564 |
| 529 text_renderer_ = CreateTextRenderer(); | 565 text_renderer_ = CreateTextRenderer(); |
| 530 if (text_renderer_) { | 566 if (text_renderer_) { |
| 531 text_renderer_->Initialize( | 567 text_renderer_->Initialize( |
| 532 base::Bind(&PipelineImpl::OnTextRendererEnded, weak_this_)); | 568 base::Bind(&PipelineImpl::OnTextRendererEnded, weak_this_)); |
| 533 } | 569 } |
| 534 | 570 |
| 535 StateTransitionTask(PIPELINE_OK); | 571 StateTransitionTask(PIPELINE_OK); |
| 536 } | 572 } |
| 537 | 573 |
| 538 void PipelineImpl::StopTask(const base::Closure& stop_cb) { | 574 void PipelineImpl::StopTask(const base::Closure& stop_cb) { |
| 539 DCHECK(task_runner_->BelongsToCurrentThread()); | 575 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 540 DCHECK(stop_cb_.is_null()); | 576 DCHECK(stop_cb_.is_null()); |
| 541 | 577 |
| 542 if (state_ == kStopped) { | 578 if (state_ == kStopped) { |
| 543 // Invalid all weak pointers so it's safe to destroy |this| on the render | 579 // Invalid all weak pointers so it's safe to destroy |this| on the render |
| 544 // main thread. | 580 // main thread. |
| 545 weak_factory_.InvalidateWeakPtrs(); | 581 weak_factory_.InvalidateWeakPtrs(); |
| 546 | 582 |
| 547 // NOTE: pipeline may be deleted at this point in time as a result of | 583 // NOTE: pipeline may be deleted at this point in time as a result of |
| 548 // executing |stop_cb|. | 584 // executing |stop_cb|. |
| 549 stop_cb.Run(); | 585 stop_cb.Run(); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 562 state_ == kSuspended || state_ == kResuming) { | 598 state_ == kSuspended || state_ == kResuming) { |
| 563 PipelineStatistics stats = GetStatistics(); | 599 PipelineStatistics stats = GetStatistics(); |
| 564 if (stats.video_frames_decoded > 0) { | 600 if (stats.video_frames_decoded > 0) { |
| 565 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount", | 601 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount", |
| 566 stats.video_frames_dropped); | 602 stats.video_frames_dropped); |
| 567 } | 603 } |
| 568 } | 604 } |
| 569 | 605 |
| 570 SetState(kStopping); | 606 SetState(kStopping); |
| 571 pending_callbacks_.reset(); | 607 pending_callbacks_.reset(); |
| 572 DoStop(base::Bind(&PipelineImpl::OnStopCompleted, weak_this_)); | 608 DoStop(); |
| 573 } | 609 } |
| 574 | 610 |
| 575 void PipelineImpl::ErrorChangedTask(PipelineStatus error) { | 611 void PipelineImpl::ErrorChangedTask(PipelineStatus error) { |
| 576 DCHECK(task_runner_->BelongsToCurrentThread()); | 612 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 577 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; | 613 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; |
| 578 | 614 |
| 615 // Preserve existing abnormal status. |
| 616 if (status_ != PIPELINE_OK) |
| 617 return; |
| 618 |
| 579 // Don't report pipeline error events to the media log here. The embedder will | 619 // Don't report pipeline error events to the media log here. The embedder will |
| 580 // log this when |error_cb_| is called. If the pipeline is already stopped or | 620 // log this when Client::OnError is called. If the pipeline is already stopped |
| 581 // stopping we also don't want to log any event. In case we are suspending or | 621 // or stopping we also don't want to log any event. In case we are suspending |
| 582 // suspended, the error may be recoverable, so don't propagate it now, instead | 622 // or suspended, the error may be recoverable, so don't propagate it now, |
| 583 // let the subsequent seek during resume propagate it if it's unrecoverable. | 623 // instead let the subsequent seek during resume propagate it if it's |
| 584 | 624 // unrecoverable. |
| 585 if (state_ == kStopping || state_ == kStopped || state_ == kSuspending || | 625 if (state_ == kStopping || state_ == kStopped || state_ == kSuspending || |
| 586 state_ == kSuspended) { | 626 state_ == kSuspended) { |
| 587 return; | 627 return; |
| 588 } | 628 } |
| 589 | 629 |
| 630 // Once we enter |kStopping| state, nothing is reported back to the client. |
| 631 // If we encounter an error during initialization/seeking/suspending, |
| 632 // report the error using the completion callbacks for those tasks. |
| 633 status_ = error; |
| 634 bool error_reported = false; |
| 635 if (!seek_cb_.is_null()) { |
| 636 base::ResetAndReturn(&seek_cb_).Run(status_); |
| 637 error_reported = true; |
| 638 } |
| 639 if (!suspend_cb_.is_null()) { |
| 640 base::ResetAndReturn(&suspend_cb_).Run(status_); |
| 641 error_reported = true; |
| 642 } |
| 643 if (!error_reported) { |
| 644 DCHECK_NE(status_, PIPELINE_OK); |
| 645 main_task_runner_->PostTask( |
| 646 FROM_HERE, |
| 647 base::Bind(&Pipeline::Client::OnError, weak_client_, status_)); |
| 648 } |
| 649 |
| 590 SetState(kStopping); | 650 SetState(kStopping); |
| 591 pending_callbacks_.reset(); | 651 pending_callbacks_.reset(); |
| 592 status_ = error; | 652 DoStop(); |
| 593 | |
| 594 DoStop(base::Bind(&PipelineImpl::OnStopCompleted, weak_this_)); | |
| 595 } | 653 } |
| 596 | 654 |
| 597 void PipelineImpl::PlaybackRateChangedTask(double playback_rate) { | 655 void PipelineImpl::PlaybackRateChangedTask(double playback_rate) { |
| 598 DCHECK(task_runner_->BelongsToCurrentThread()); | 656 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 599 | 657 |
| 600 // Playback rate changes are only carried out while playing. | 658 // Playback rate changes are only carried out while playing. |
| 601 if (state_ != kPlaying) | 659 if (state_ != kPlaying) |
| 602 return; | 660 return; |
| 603 | 661 |
| 604 renderer_->SetPlaybackRate(playback_rate); | 662 renderer_->SetPlaybackRate(playback_rate); |
| 605 } | 663 } |
| 606 | 664 |
| 607 void PipelineImpl::VolumeChangedTask(float volume) { | 665 void PipelineImpl::VolumeChangedTask(float volume) { |
| 608 DCHECK(task_runner_->BelongsToCurrentThread()); | 666 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 609 | 667 |
| 610 // Volume changes are only carried out while playing. | 668 // Volume changes are only carried out while playing. |
| 611 if (state_ != kPlaying) | 669 if (state_ != kPlaying) |
| 612 return; | 670 return; |
| 613 | 671 |
| 614 renderer_->SetVolume(volume); | 672 renderer_->SetVolume(volume); |
| 615 } | 673 } |
| 616 | 674 |
| 617 void PipelineImpl::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { | 675 void PipelineImpl::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { |
| 618 DCHECK(task_runner_->BelongsToCurrentThread()); | 676 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 619 DCHECK(stop_cb_.is_null()); | 677 DCHECK(stop_cb_.is_null()); |
| 620 | 678 |
| 621 // Suppress seeking if we're not fully started. | 679 // Suppress seeking if we're not fully started. |
| 622 if (state_ != kPlaying) { | 680 if (state_ != kPlaying) { |
| 623 DCHECK(state_ == kStopping || state_ == kStopped) | 681 DCHECK(state_ == kStopping || state_ == kStopped) |
| 624 << "Receive seek in unexpected state: " << state_; | 682 << "Receive seek in unexpected state: " << state_; |
| 625 seek_cb.Run(PIPELINE_ERROR_INVALID_STATE); | 683 seek_cb.Run(PIPELINE_ERROR_INVALID_STATE); |
| 626 return; | 684 return; |
| 627 } | 685 } |
| 628 | 686 |
| 629 DCHECK(seek_cb_.is_null()); | 687 DCHECK(seek_cb_.is_null()); |
| 630 | 688 |
| 631 const base::TimeDelta seek_timestamp = | 689 const base::TimeDelta seek_timestamp = |
| 632 std::max(time, demuxer_->GetStartTime()); | 690 std::max(time, demuxer_->GetStartTime()); |
| 633 | 691 |
| 634 SetState(kSeeking); | 692 SetState(kSeeking); |
| 635 seek_cb_ = seek_cb; | 693 seek_cb_ = seek_cb; |
| 636 renderer_ended_ = false; | 694 renderer_ended_ = false; |
| 637 text_renderer_ended_ = false; | 695 text_renderer_ended_ = false; |
| 638 start_timestamp_ = seek_timestamp; | 696 start_timestamp_ = seek_timestamp; |
| 639 | 697 |
| 640 DoSeek(seek_timestamp, | 698 DoSeek(seek_timestamp, |
| 641 base::Bind(&PipelineImpl::StateTransitionTask, weak_this_)); | 699 base::Bind(&PipelineImpl::StateTransitionTask, weak_this_)); |
| 642 } | 700 } |
| 643 | 701 |
| 644 void PipelineImpl::SuspendTask(const PipelineStatusCB& suspend_cb) { | 702 void PipelineImpl::SuspendTask(const PipelineStatusCB& suspend_cb) { |
| 645 DCHECK(task_runner_->BelongsToCurrentThread()); | 703 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 646 | 704 |
| 647 // Suppress suspending if we're not playing. | 705 // Suppress suspending if we're not playing. |
| 648 if (state_ != kPlaying) { | 706 if (state_ != kPlaying) { |
| 649 DCHECK(state_ == kStopping || state_ == kStopped) | 707 DCHECK(state_ == kStopping || state_ == kStopped) |
| 650 << "Receive suspend in unexpected state: " << state_; | 708 << "Receive suspend in unexpected state: " << state_; |
| 651 suspend_cb.Run(PIPELINE_ERROR_INVALID_STATE); | 709 suspend_cb.Run(PIPELINE_ERROR_INVALID_STATE); |
| 652 return; | 710 return; |
| 653 } | 711 } |
| 654 DCHECK(renderer_); | 712 DCHECK(renderer_); |
| 655 DCHECK(!pending_callbacks_.get()); | 713 DCHECK(!pending_callbacks_.get()); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 683 base::Unretained(text_renderer_.get()))); | 741 base::Unretained(text_renderer_.get()))); |
| 684 } | 742 } |
| 685 | 743 |
| 686 pending_callbacks_ = SerialRunner::Run( | 744 pending_callbacks_ = SerialRunner::Run( |
| 687 fns, base::Bind(&PipelineImpl::StateTransitionTask, weak_this_)); | 745 fns, base::Bind(&PipelineImpl::StateTransitionTask, weak_this_)); |
| 688 } | 746 } |
| 689 | 747 |
| 690 void PipelineImpl::ResumeTask(std::unique_ptr<Renderer> renderer, | 748 void PipelineImpl::ResumeTask(std::unique_ptr<Renderer> renderer, |
| 691 base::TimeDelta timestamp, | 749 base::TimeDelta timestamp, |
| 692 const PipelineStatusCB& seek_cb) { | 750 const PipelineStatusCB& seek_cb) { |
| 693 DCHECK(task_runner_->BelongsToCurrentThread()); | 751 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 694 | 752 |
| 695 // Suppress resuming if we're not suspended. | 753 // Suppress resuming if we're not suspended. |
| 696 if (state_ != kSuspended) { | 754 if (state_ != kSuspended) { |
| 697 DCHECK(state_ == kStopping || state_ == kStopped) | 755 DCHECK(state_ == kStopping || state_ == kStopped) |
| 698 << "Receive resume in unexpected state: " << state_; | 756 << "Receive resume in unexpected state: " << state_; |
| 699 seek_cb.Run(PIPELINE_ERROR_INVALID_STATE); | 757 seek_cb.Run(PIPELINE_ERROR_INVALID_STATE); |
| 700 return; | 758 return; |
| 701 } | 759 } |
| 702 DCHECK(!renderer_); | 760 DCHECK(!renderer_); |
| 703 DCHECK(!pending_callbacks_.get()); | 761 DCHECK(!pending_callbacks_.get()); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 737 } | 795 } |
| 738 | 796 |
| 739 renderer_->SetCdm(cdm_context, | 797 renderer_->SetCdm(cdm_context, |
| 740 base::Bind(&PipelineImpl::OnCdmAttached, weak_this_, | 798 base::Bind(&PipelineImpl::OnCdmAttached, weak_this_, |
| 741 cdm_attached_cb, cdm_context)); | 799 cdm_attached_cb, cdm_context)); |
| 742 } | 800 } |
| 743 | 801 |
| 744 void PipelineImpl::OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, | 802 void PipelineImpl::OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, |
| 745 CdmContext* cdm_context, | 803 CdmContext* cdm_context, |
| 746 bool success) { | 804 bool success) { |
| 747 DCHECK(task_runner_->BelongsToCurrentThread()); | 805 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 748 if (success) | 806 if (success) |
| 749 cdm_context_ = cdm_context; | 807 cdm_context_ = cdm_context; |
| 750 cdm_attached_cb.Run(success); | 808 cdm_attached_cb.Run(success); |
| 751 } | 809 } |
| 752 | 810 |
| 753 void PipelineImpl::OnRendererEnded() { | 811 void PipelineImpl::OnRendererEnded() { |
| 754 DCHECK(task_runner_->BelongsToCurrentThread()); | 812 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 755 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED)); | 813 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED)); |
| 756 | 814 |
| 757 if (state_ != kPlaying) | 815 if (state_ != kPlaying) |
| 758 return; | 816 return; |
| 759 | 817 |
| 760 DCHECK(!renderer_ended_); | 818 DCHECK(!renderer_ended_); |
| 761 renderer_ended_ = true; | 819 renderer_ended_ = true; |
| 762 | 820 |
| 763 RunEndedCallbackIfNeeded(); | 821 RunEndedCallbackIfNeeded(); |
| 764 } | 822 } |
| 765 | 823 |
| 766 void PipelineImpl::OnTextRendererEnded() { | 824 void PipelineImpl::OnTextRendererEnded() { |
| 767 DCHECK(task_runner_->BelongsToCurrentThread()); | 825 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 768 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED)); | 826 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED)); |
| 769 | 827 |
| 770 if (state_ != kPlaying) | 828 if (state_ != kPlaying) |
| 771 return; | 829 return; |
| 772 | 830 |
| 773 DCHECK(!text_renderer_ended_); | 831 DCHECK(!text_renderer_ended_); |
| 774 text_renderer_ended_ = true; | 832 text_renderer_ended_ = true; |
| 775 | 833 |
| 776 RunEndedCallbackIfNeeded(); | 834 RunEndedCallbackIfNeeded(); |
| 777 } | 835 } |
| 778 | 836 |
| 779 void PipelineImpl::RunEndedCallbackIfNeeded() { | 837 void PipelineImpl::RunEndedCallbackIfNeeded() { |
| 780 DCHECK(task_runner_->BelongsToCurrentThread()); | 838 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 781 | 839 |
| 782 if (renderer_ && !renderer_ended_) | 840 if (renderer_ && !renderer_ended_) |
| 783 return; | 841 return; |
| 784 | 842 |
| 785 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_) | 843 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_) |
| 786 return; | 844 return; |
| 787 | 845 |
| 788 DCHECK_EQ(status_, PIPELINE_OK); | 846 DCHECK_EQ(status_, PIPELINE_OK); |
| 789 ended_cb_.Run(); | 847 main_task_runner_->PostTask( |
| 848 FROM_HERE, base::Bind(&Pipeline::Client::OnEnded, weak_client_)); |
| 790 } | 849 } |
| 791 | 850 |
| 792 std::unique_ptr<TextRenderer> PipelineImpl::CreateTextRenderer() { | 851 std::unique_ptr<TextRenderer> PipelineImpl::CreateTextRenderer() { |
| 793 DCHECK(task_runner_->BelongsToCurrentThread()); | 852 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 794 | 853 |
| 795 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); | 854 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); |
| 796 if (!cmd_line->HasSwitch(switches::kEnableInbandTextTracks)) | 855 if (!cmd_line->HasSwitch(switches::kEnableInbandTextTracks)) |
| 797 return nullptr; | 856 return nullptr; |
| 798 | 857 |
| 799 return base::WrapUnique(new media::TextRenderer( | 858 return base::WrapUnique(new media::TextRenderer( |
| 800 task_runner_, base::Bind(&PipelineImpl::OnAddTextTrack, weak_this_))); | 859 media_task_runner_, |
| 860 base::Bind(&PipelineImpl::OnAddTextTrack, weak_this_))); |
| 801 } | 861 } |
| 802 | 862 |
| 803 void PipelineImpl::AddTextStreamTask(DemuxerStream* text_stream, | 863 void PipelineImpl::AddTextStreamTask(DemuxerStream* text_stream, |
| 804 const TextTrackConfig& config) { | 864 const TextTrackConfig& config) { |
| 805 DCHECK(task_runner_->BelongsToCurrentThread()); | 865 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 806 // TODO(matthewjheaney): fix up text_ended_ when text stream | 866 // TODO(matthewjheaney): fix up text_ended_ when text stream |
| 807 // is added (http://crbug.com/321446). | 867 // is added (http://crbug.com/321446). |
| 808 if (text_renderer_) | 868 if (text_renderer_) |
| 809 text_renderer_->AddTextStream(text_stream, config); | 869 text_renderer_->AddTextStream(text_stream, config); |
| 810 } | 870 } |
| 811 | 871 |
| 812 void PipelineImpl::RemoveTextStreamTask(DemuxerStream* text_stream) { | 872 void PipelineImpl::RemoveTextStreamTask(DemuxerStream* text_stream) { |
| 813 DCHECK(task_runner_->BelongsToCurrentThread()); | 873 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 874 |
| 814 if (text_renderer_) | 875 if (text_renderer_) |
| 815 text_renderer_->RemoveTextStream(text_stream); | 876 text_renderer_->RemoveTextStream(text_stream); |
| 816 } | 877 } |
| 817 | 878 |
| 818 void PipelineImpl::OnAddTextTrack(const TextTrackConfig& config, | 879 void PipelineImpl::OnAddTextTrack(const TextTrackConfig& config, |
| 819 const AddTextTrackDoneCB& done_cb) { | 880 const AddTextTrackDoneCB& done_cb) { |
| 820 DCHECK(task_runner_->BelongsToCurrentThread()); | 881 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 821 add_text_track_cb_.Run(config, done_cb); | 882 |
| 883 main_task_runner_->PostTask( |
| 884 FROM_HERE, base::Bind(&Pipeline::Client::OnAddTextTrack, weak_client_, |
| 885 config, done_cb)); |
| 822 } | 886 } |
| 823 | 887 |
| 824 void PipelineImpl::InitializeDemuxer(const PipelineStatusCB& done_cb) { | 888 void PipelineImpl::InitializeDemuxer(const PipelineStatusCB& done_cb) { |
| 825 DCHECK(task_runner_->BelongsToCurrentThread()); | 889 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 890 |
| 826 demuxer_->Initialize(this, done_cb, !!text_renderer_); | 891 demuxer_->Initialize(this, done_cb, !!text_renderer_); |
| 827 } | 892 } |
| 828 | 893 |
| 829 void PipelineImpl::InitializeRenderer(const PipelineStatusCB& done_cb) { | 894 void PipelineImpl::InitializeRenderer(const PipelineStatusCB& done_cb) { |
| 830 DCHECK(task_runner_->BelongsToCurrentThread()); | 895 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 831 | 896 |
| 832 if (!demuxer_->GetStream(DemuxerStream::AUDIO) && | 897 if (!demuxer_->GetStream(DemuxerStream::AUDIO) && |
| 833 !demuxer_->GetStream(DemuxerStream::VIDEO)) { | 898 !demuxer_->GetStream(DemuxerStream::VIDEO)) { |
| 834 { | 899 { |
| 835 base::AutoLock auto_lock(lock_); | 900 base::AutoLock auto_lock(lock_); |
| 836 renderer_.reset(); | 901 renderer_.reset(); |
| 837 } | 902 } |
| 838 OnError(PIPELINE_ERROR_COULD_NOT_RENDER); | 903 OnError(PIPELINE_ERROR_COULD_NOT_RENDER); |
| 839 return; | 904 return; |
| 840 } | 905 } |
| 841 | 906 |
| 842 if (cdm_context_) | 907 if (cdm_context_) |
| 843 renderer_->SetCdm(cdm_context_, base::Bind(&IgnoreCdmAttached)); | 908 renderer_->SetCdm(cdm_context_, base::Bind(&IgnoreCdmAttached)); |
| 844 | 909 |
| 845 renderer_->Initialize( | 910 renderer_->Initialize( |
| 846 demuxer_, done_cb, | 911 demuxer_, done_cb, |
| 847 base::Bind(&PipelineImpl::OnUpdateStatistics, weak_this_), | 912 base::Bind(&PipelineImpl::OnUpdateStatistics, weak_this_), |
| 848 base::Bind(&PipelineImpl::BufferingStateChanged, weak_this_), | 913 base::Bind(&PipelineImpl::BufferingStateChanged, weak_this_), |
| 849 base::Bind(&PipelineImpl::OnRendererEnded, weak_this_), | 914 base::Bind(&PipelineImpl::OnRendererEnded, weak_this_), |
| 850 base::Bind(&PipelineImpl::OnError, weak_this_), | 915 base::Bind(&PipelineImpl::OnError, weak_this_), |
| 851 waiting_for_decryption_key_cb_); | 916 base::Bind(&PipelineImpl::OnWaitingForDecryptionKey, weak_this_)); |
| 852 } | 917 } |
| 853 | 918 |
| 854 void PipelineImpl::ReportMetadata() { | 919 void PipelineImpl::ReportMetadata() { |
| 855 DCHECK(task_runner_->BelongsToCurrentThread()); | 920 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 921 |
| 856 PipelineMetadata metadata; | 922 PipelineMetadata metadata; |
| 857 metadata.timeline_offset = demuxer_->GetTimelineOffset(); | 923 metadata.timeline_offset = demuxer_->GetTimelineOffset(); |
| 858 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO); | 924 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO); |
| 859 if (stream) { | 925 if (stream) { |
| 860 metadata.has_video = true; | 926 metadata.has_video = true; |
| 861 metadata.natural_size = stream->video_decoder_config().natural_size(); | 927 metadata.natural_size = stream->video_decoder_config().natural_size(); |
| 862 metadata.video_rotation = stream->video_rotation(); | 928 metadata.video_rotation = stream->video_rotation(); |
| 863 } | 929 } |
| 864 if (demuxer_->GetStream(DemuxerStream::AUDIO)) { | 930 if (demuxer_->GetStream(DemuxerStream::AUDIO)) { |
| 865 metadata.has_audio = true; | 931 metadata.has_audio = true; |
| 866 } | 932 } |
| 867 metadata_cb_.Run(metadata); | 933 |
| 934 main_task_runner_->PostTask( |
| 935 FROM_HERE, |
| 936 base::Bind(&Pipeline::Client::OnMetadata, weak_client_, metadata)); |
| 868 } | 937 } |
| 869 | 938 |
| 870 void PipelineImpl::BufferingStateChanged(BufferingState new_buffering_state) { | 939 void PipelineImpl::BufferingStateChanged(BufferingState new_buffering_state) { |
| 871 DVLOG(1) << __FUNCTION__ << "(" << new_buffering_state << ") "; | 940 DVLOG(1) << __FUNCTION__ << "(" << new_buffering_state << ") "; |
| 872 DCHECK(task_runner_->BelongsToCurrentThread()); | 941 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 873 buffering_state_cb_.Run(new_buffering_state); | 942 |
| 943 main_task_runner_->PostTask( |
| 944 FROM_HERE, base::Bind(&Pipeline::Client::OnBufferingStateChange, |
| 945 weak_client_, new_buffering_state)); |
| 874 } | 946 } |
| 875 | 947 |
| 876 } // namespace media | 948 } // namespace media |
| OLD | NEW |