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> | |
9 | 8 |
10 #include "base/bind.h" | 9 #include "base/bind.h" |
11 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
12 #include "base/callback.h" | 11 #include "base/callback.h" |
13 #include "base/callback_helpers.h" | 12 #include "base/callback_helpers.h" |
14 #include "base/command_line.h" | 13 #include "base/command_line.h" |
15 #include "base/compiler_specific.h" | |
16 #include "base/location.h" | 14 #include "base/location.h" |
17 #include "base/memory/ptr_util.h" | |
18 #include "base/metrics/histogram.h" | 15 #include "base/metrics/histogram.h" |
19 #include "base/single_thread_task_runner.h" | 16 #include "base/single_thread_task_runner.h" |
20 #include "base/stl_util.h" | 17 #include "base/synchronization/lock.h" |
21 #include "base/strings/string_number_conversions.h" | |
22 #include "base/strings/string_util.h" | |
23 #include "base/synchronization/waitable_event.h" | 18 #include "base/synchronization/waitable_event.h" |
24 #include "base/threading/thread_task_runner_handle.h" | 19 #include "base/threading/thread_task_runner_handle.h" |
25 #include "media/base/bind_to_current_loop.h" | 20 #include "media/base/bind_to_current_loop.h" |
| 21 #include "media/base/demuxer.h" |
26 #include "media/base/media_log.h" | 22 #include "media/base/media_log.h" |
27 #include "media/base/media_switches.h" | 23 #include "media/base/media_switches.h" |
28 #include "media/base/renderer.h" | 24 #include "media/base/renderer.h" |
| 25 #include "media/base/renderer_client.h" |
| 26 #include "media/base/serial_runner.h" |
29 #include "media/base/text_renderer.h" | 27 #include "media/base/text_renderer.h" |
30 #include "media/base/text_track_config.h" | 28 #include "media/base/text_track_config.h" |
31 #include "media/base/timestamp_constants.h" | 29 #include "media/base/timestamp_constants.h" |
32 #include "media/base/video_decoder_config.h" | 30 #include "media/base/video_decoder_config.h" |
33 | 31 |
34 using base::TimeDelta; | 32 static const double kDefaultPlaybackRate = 0.0; |
| 33 static const float kDefaultVolume = 1.0f; |
35 | 34 |
36 namespace media { | 35 namespace media { |
37 | 36 |
| 37 class PipelineImpl::RendererWrapper : public DemuxerHost, |
| 38 public RendererClient { |
| 39 public: |
| 40 RendererWrapper(base::WeakPtr<PipelineImpl> weak_pipeline, |
| 41 scoped_refptr<base::SingleThreadTaskRunner> media_task_runner, |
| 42 scoped_refptr<MediaLog> media_log); |
| 43 ~RendererWrapper() final; |
| 44 |
| 45 void Start(Demuxer* demuxer, |
| 46 std::unique_ptr<Renderer> renderer, |
| 47 std::unique_ptr<TextRenderer> text_renderer); |
| 48 void Stop(const base::Closure& stop_cb); |
| 49 void Seek(base::TimeDelta time); |
| 50 void Suspend(); |
| 51 void Resume(std::unique_ptr<Renderer> renderer, base::TimeDelta time); |
| 52 void SetPlaybackRate(double playback_rate); |
| 53 void SetVolume(float volume); |
| 54 base::TimeDelta GetMediaTime(); |
| 55 void SetCdm(CdmContext* cdm_context, const CdmAttachedCB& cdm_attached_cb); |
| 56 |
| 57 private: |
| 58 // DemuxerHost implementaion. |
| 59 void OnBufferedTimeRangesChanged(const Ranges<base::TimeDelta>& ranges) final; |
| 60 void SetDuration(base::TimeDelta duration) final; |
| 61 void OnDemuxerError(PipelineStatus error) final; |
| 62 void AddTextStream(DemuxerStream* text_stream, |
| 63 const TextTrackConfig& config) final; |
| 64 void RemoveTextStream(DemuxerStream* text_stream) final; |
| 65 |
| 66 // RendererClient implementation. |
| 67 void OnError(PipelineStatus error) final; |
| 68 void OnEnded() final; |
| 69 void OnStatisticsUpdate(const PipelineStatistics& stats) final; |
| 70 void OnBufferingStateChange(BufferingState state) final; |
| 71 void OnWaitingForDecryptionKey() final; |
| 72 void OnVideoNaturalSizeChange(const gfx::Size& size) final; |
| 73 void OnVideoOpacityChange(bool opaque) final; |
| 74 |
| 75 void DoSeek(base::TimeDelta seek_timestamp, const PipelineStatusCB& done_cb); |
| 76 void DoStop(const base::Closure& done_cb); |
| 77 void OnPipelineError(PipelineStatus error); |
| 78 void OnTextRendererEnded(); |
| 79 void RunEndedCallbackIfNeeded(); |
| 80 void SetState(State next_state); |
| 81 State GetNextState() const; |
| 82 void StateTransitionTask(PipelineStatus status); |
| 83 void OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, |
| 84 CdmContext* cdm_context, |
| 85 bool success); |
| 86 void AddTextStreamTask(DemuxerStream* text_stream, |
| 87 const TextTrackConfig& config); |
| 88 void RemoveTextStreamTask(DemuxerStream* text_stream); |
| 89 void InitializeDemuxer(const PipelineStatusCB& done_cb); |
| 90 void InitializeRenderer(const PipelineStatusCB& done_cb); |
| 91 void ReportMetadata(); |
| 92 |
| 93 base::WeakPtr<PipelineImpl> weak_pipeline_; |
| 94 const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; |
| 95 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; |
| 96 const scoped_refptr<MediaLog> media_log_; |
| 97 |
| 98 Demuxer* demuxer_; |
| 99 std::unique_ptr<Renderer> renderer_; |
| 100 std::unique_ptr<TextRenderer> text_renderer_; |
| 101 double playback_rate_; |
| 102 float volume_; |
| 103 CdmContext* cdm_context_; |
| 104 |
| 105 // Lock used to serialize members - |renderer_| and |suspend_timestamp_| - |
| 106 // accessed in GetMediaTime(), which is the only member function called on |
| 107 // the main thread. |
| 108 mutable base::Lock media_time_lock_; |
| 109 |
| 110 // Current state of the pipeline. |
| 111 State state_; |
| 112 |
| 113 // Status of the pipeline. Initialized to PIPELINE_OK which indicates that |
| 114 // the pipeline is operating correctly. Any other value indicates that the |
| 115 // pipeline is stopped or is stopping. Clients can call the Stop() method to |
| 116 // reset the pipeline state, and restore this to PIPELINE_OK. |
| 117 PipelineStatus status_; |
| 118 |
| 119 // The timestamp to start playback from after starting/seeking/resuming has |
| 120 // completed. |
| 121 base::TimeDelta start_timestamp_; |
| 122 |
| 123 // The media timestamp to return while the pipeline is suspended. |
| 124 // Otherwise set to kNoTimestamp(). |
| 125 base::TimeDelta suspend_timestamp_; |
| 126 |
| 127 // Whether we've received the audio/video/text ended events. |
| 128 bool renderer_ended_; |
| 129 bool text_renderer_ended_; |
| 130 |
| 131 // Series of tasks to Start(), Seek(), and Resume(). |
| 132 std::unique_ptr<SerialRunner> pending_callbacks_; |
| 133 |
| 134 base::WeakPtr<RendererWrapper> weak_this_; |
| 135 base::WeakPtrFactory<RendererWrapper> weak_factory_; |
| 136 DISALLOW_COPY_AND_ASSIGN(RendererWrapper); |
| 137 }; |
| 138 |
38 PipelineImpl::PipelineImpl( | 139 PipelineImpl::PipelineImpl( |
39 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, | 140 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, |
40 MediaLog* media_log) | 141 MediaLog* media_log) |
41 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()), | 142 : media_task_runner_(media_task_runner), |
42 media_task_runner_(media_task_runner), | |
43 media_log_(media_log), | 143 media_log_(media_log), |
44 running_(false), | 144 client_(nullptr), |
| 145 playback_rate_(kDefaultPlaybackRate), |
| 146 volume_(kDefaultVolume), |
45 did_loading_progress_(false), | 147 did_loading_progress_(false), |
46 volume_(1.0f), | |
47 playback_rate_(0.0), | |
48 status_(PIPELINE_OK), | |
49 state_(kCreated), | |
50 suspend_timestamp_(kNoTimestamp()), | |
51 renderer_ended_(false), | |
52 text_renderer_ended_(false), | |
53 demuxer_(NULL), | |
54 cdm_context_(nullptr), | |
55 weak_factory_(this) { | 148 weak_factory_(this) { |
56 weak_this_ = weak_factory_.GetWeakPtr(); | 149 DVLOG(2) << __FUNCTION__; |
57 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); | 150 renderer_wrapper_.reset(new RendererWrapper(weak_factory_.GetWeakPtr(), |
| 151 media_task_runner_, media_log_)); |
58 } | 152 } |
59 | 153 |
60 PipelineImpl::~PipelineImpl() { | 154 PipelineImpl::~PipelineImpl() { |
61 DCHECK(main_task_runner_->BelongsToCurrentThread()) | 155 DVLOG(2) << __FUNCTION__; |
62 << "Pipeline must be destroyed on same thread that created it"; | 156 DCHECK(thread_checker_.CalledOnValidThread()); |
63 DCHECK(!running_) << "Stop() must complete before destroying object"; | 157 DCHECK(!client_) << "Stop() must complete before destroying object"; |
64 DCHECK(seek_cb_.is_null()); | 158 DCHECK(seek_cb_.is_null()); |
| 159 DCHECK(suspend_cb_.is_null()); |
| 160 |
| 161 // Invalidate self weak pointers effectively canceling all pending |
| 162 // notifications in the message queue. |
| 163 // Do this before posting the DeleteSoon task to the media thread to |
| 164 // guarantee that the WeakPtr<PipelineImpl> passed to the RendererWrapper |
| 165 // is not invalidated on the media thread. |
| 166 weak_factory_.InvalidateWeakPtrs(); |
| 167 |
| 168 // RendererWrapper is deleted on the media thread. |
| 169 media_task_runner_->DeleteSoon(FROM_HERE, renderer_wrapper_.release()); |
65 } | 170 } |
66 | 171 |
67 void PipelineImpl::Start(Demuxer* demuxer, | 172 void PipelineImpl::Start(Demuxer* demuxer, |
68 std::unique_ptr<Renderer> renderer, | 173 std::unique_ptr<Renderer> renderer, |
69 Client* client, | 174 Client* client, |
70 const PipelineStatusCB& seek_cb) { | 175 const PipelineStatusCB& seek_cb) { |
71 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 176 DVLOG(2) << __FUNCTION__; |
| 177 DCHECK(thread_checker_.CalledOnValidThread()); |
| 178 DCHECK(demuxer); |
| 179 DCHECK(renderer); |
72 DCHECK(client); | 180 DCHECK(client); |
73 DCHECK(!seek_cb.is_null()); | 181 DCHECK(!seek_cb.is_null()); |
74 | 182 |
75 base::AutoLock auto_lock(lock_); | 183 DCHECK(!client_); |
76 CHECK(!running_) << "Media pipeline is already running"; | 184 DCHECK(seek_cb_.is_null()); |
77 running_ = true; | 185 client_ = client; |
78 | 186 seek_cb_ = seek_cb; |
79 demuxer_ = demuxer; | 187 |
80 renderer_ = std::move(renderer); | 188 std::unique_ptr<TextRenderer> text_renderer; |
81 client_weak_factory_.reset(new base::WeakPtrFactory<Client>(client)); | 189 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
82 weak_client_ = client_weak_factory_->GetWeakPtr(); | 190 switches::kEnableInbandTextTracks)) { |
83 seek_cb_ = media::BindToCurrentLoop(seek_cb); | 191 text_renderer.reset(new TextRenderer( |
| 192 media_task_runner_, |
| 193 BindToCurrentLoop(base::Bind(&PipelineImpl::OnAddTextTrack, |
| 194 weak_factory_.GetWeakPtr())))); |
| 195 } |
| 196 |
84 media_task_runner_->PostTask( | 197 media_task_runner_->PostTask( |
85 FROM_HERE, base::Bind(&PipelineImpl::StartTask, weak_this_)); | 198 FROM_HERE, |
| 199 base::Bind(&RendererWrapper::Start, |
| 200 base::Unretained(renderer_wrapper_.get()), demuxer, |
| 201 base::Passed(&renderer), base::Passed(&text_renderer))); |
86 } | 202 } |
87 | 203 |
88 void PipelineImpl::Stop() { | 204 void PipelineImpl::Stop() { |
89 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 205 DVLOG(2) << __FUNCTION__; |
90 DVLOG(2) << __FUNCTION__; | 206 DCHECK(thread_checker_.CalledOnValidThread()); |
91 | 207 |
92 if (media_task_runner_ != main_task_runner_) { | 208 if (!IsRunning()) { |
| 209 DVLOG(2) << "Media pipeline isn't running. Ignoring Stop()"; |
| 210 return; |
| 211 } |
| 212 |
| 213 if (statistics_.video_frames_decoded > 0) { |
| 214 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount", |
| 215 statistics_.video_frames_dropped); |
| 216 } |
| 217 |
| 218 if (media_task_runner_->BelongsToCurrentThread()) { |
| 219 // This path is executed by unittests that share media and main threads. |
| 220 base::Closure stop_cb = base::Bind(&base::DoNothing); |
| 221 media_task_runner_->PostTask( |
| 222 FROM_HERE, |
| 223 base::Bind(&RendererWrapper::Stop, |
| 224 base::Unretained(renderer_wrapper_.get()), stop_cb)); |
| 225 } else { |
93 // This path is executed by production code where the two task runners - | 226 // This path is executed by production code where the two task runners - |
94 // main and media - live on different threads. | 227 // main and media - live on different threads. |
95 // TODO(alokp): It may be possible to not have to wait for StopTask by | 228 // |
96 // moving the members accessed on media thread into a class/struct and | 229 // TODO(alokp): We should not have to wait for the RendererWrapper::Stop. |
97 // DeleteSoon the instance on the media thread. | 230 // RendererWrapper holds a raw reference to Demuxer, which in turn holds a |
| 231 // raw reference to DataSource. Both Demuxer and DataSource need to live |
| 232 // until RendererWrapper is stopped. If RendererWrapper owned Demuxer and |
| 233 // Demuxer owned DataSource, we could simply let RendererWrapper get lazily |
| 234 // destroyed on the media thread. |
98 base::WaitableEvent waiter(base::WaitableEvent::ResetPolicy::AUTOMATIC, | 235 base::WaitableEvent waiter(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
99 base::WaitableEvent::InitialState::NOT_SIGNALED); | 236 base::WaitableEvent::InitialState::NOT_SIGNALED); |
100 base::Closure stop_cb = | 237 base::Closure stop_cb = |
101 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&waiter)); | 238 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&waiter)); |
102 // If posting the task fails or the posted task fails to run, | 239 // If posting the task fails or the posted task fails to run, |
103 // we will wait here forever. So add a CHECK to make sure we do not run | 240 // we will wait here forever. So add a CHECK to make sure we do not run |
104 // into those situations. | 241 // into those situations. |
105 CHECK(weak_factory_.HasWeakPtrs()); | |
106 CHECK(media_task_runner_->PostTask( | 242 CHECK(media_task_runner_->PostTask( |
107 FROM_HERE, base::Bind(&PipelineImpl::StopTask, weak_this_, stop_cb))); | 243 FROM_HERE, |
| 244 base::Bind(&RendererWrapper::Stop, |
| 245 base::Unretained(renderer_wrapper_.get()), stop_cb))); |
108 waiter.Wait(); | 246 waiter.Wait(); |
109 } else { | |
110 // This path is executed by unittests that share media and main threads. | |
111 StopTask(base::Bind(&base::DoNothing)); | |
112 } | 247 } |
113 // Invalidate client weak pointer effectively canceling all pending client | 248 |
114 // notifications in the message queue. | 249 // Once the pipeline is stopped, nothing is reported back to the client. |
115 client_weak_factory_.reset(); | 250 // Reset all callbacks and client handle. |
| 251 seek_cb_.Reset(); |
| 252 suspend_cb_.Reset(); |
| 253 client_ = nullptr; |
116 } | 254 } |
117 | 255 |
118 void PipelineImpl::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) { | 256 void PipelineImpl::Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb) { |
119 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 257 DVLOG(2) << __FUNCTION__; |
| 258 DCHECK(thread_checker_.CalledOnValidThread()); |
| 259 DCHECK(!seek_cb.is_null()); |
120 | 260 |
121 if (!IsRunning()) { | 261 if (!IsRunning()) { |
122 DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek()."; | 262 DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek()."; |
123 return; | 263 return; |
124 } | 264 } |
125 | 265 |
| 266 DCHECK(seek_cb_.is_null()); |
| 267 seek_cb_ = seek_cb; |
126 media_task_runner_->PostTask( | 268 media_task_runner_->PostTask( |
127 FROM_HERE, base::Bind(&PipelineImpl::SeekTask, weak_this_, time, | 269 FROM_HERE, base::Bind(&RendererWrapper::Seek, |
128 media::BindToCurrentLoop(seek_cb))); | 270 base::Unretained(renderer_wrapper_.get()), time)); |
129 } | 271 } |
130 | 272 |
131 bool PipelineImpl::IsRunning() const { | 273 bool PipelineImpl::IsRunning() const { |
132 // TODO(alokp): Add thread DCHECK after removing the internal usage on | 274 DCHECK(thread_checker_.CalledOnValidThread()); |
133 // media thread. | 275 return !!client_; |
134 base::AutoLock auto_lock(lock_); | |
135 return running_; | |
136 } | 276 } |
137 | 277 |
138 double PipelineImpl::GetPlaybackRate() const { | 278 double PipelineImpl::GetPlaybackRate() const { |
139 // TODO(alokp): Add thread DCHECK after removing the internal usage on | 279 DCHECK(thread_checker_.CalledOnValidThread()); |
140 // media thread. | |
141 base::AutoLock auto_lock(lock_); | |
142 return playback_rate_; | 280 return playback_rate_; |
143 } | 281 } |
144 | 282 |
145 void PipelineImpl::SetPlaybackRate(double playback_rate) { | 283 void PipelineImpl::SetPlaybackRate(double playback_rate) { |
146 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 284 DVLOG(2) << __FUNCTION__ << "(" << playback_rate << ")"; |
| 285 DCHECK(thread_checker_.CalledOnValidThread()); |
147 | 286 |
148 if (playback_rate < 0.0) | 287 if (playback_rate < 0.0) |
149 return; | 288 return; |
150 | 289 |
151 base::AutoLock auto_lock(lock_); | |
152 playback_rate_ = playback_rate; | 290 playback_rate_ = playback_rate; |
153 if (running_) { | 291 media_task_runner_->PostTask( |
154 media_task_runner_->PostTask( | 292 FROM_HERE, |
155 FROM_HERE, base::Bind(&PipelineImpl::PlaybackRateChangedTask, | 293 base::Bind(&RendererWrapper::SetPlaybackRate, |
156 weak_this_, playback_rate)); | 294 base::Unretained(renderer_wrapper_.get()), playback_rate_)); |
157 } | |
158 } | 295 } |
159 | 296 |
160 void PipelineImpl::Suspend(const PipelineStatusCB& suspend_cb) { | 297 void PipelineImpl::Suspend(const PipelineStatusCB& suspend_cb) { |
161 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 298 DVLOG(2) << __FUNCTION__; |
| 299 DCHECK(!suspend_cb.is_null()); |
| 300 |
| 301 DCHECK(IsRunning()); |
| 302 DCHECK(suspend_cb_.is_null()); |
| 303 suspend_cb_ = suspend_cb; |
162 | 304 |
163 media_task_runner_->PostTask( | 305 media_task_runner_->PostTask( |
164 FROM_HERE, base::Bind(&PipelineImpl::SuspendTask, weak_this_, | 306 FROM_HERE, base::Bind(&RendererWrapper::Suspend, |
165 media::BindToCurrentLoop(suspend_cb))); | 307 base::Unretained(renderer_wrapper_.get()))); |
166 } | 308 } |
167 | 309 |
168 void PipelineImpl::Resume(std::unique_ptr<Renderer> renderer, | 310 void PipelineImpl::Resume(std::unique_ptr<Renderer> renderer, |
169 base::TimeDelta timestamp, | 311 base::TimeDelta time, |
170 const PipelineStatusCB& seek_cb) { | 312 const PipelineStatusCB& seek_cb) { |
171 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 313 DVLOG(2) << __FUNCTION__; |
| 314 DCHECK(thread_checker_.CalledOnValidThread()); |
| 315 DCHECK(renderer); |
| 316 DCHECK(!seek_cb.is_null()); |
| 317 |
| 318 DCHECK(IsRunning()); |
| 319 DCHECK(seek_cb_.is_null()); |
| 320 seek_cb_ = seek_cb; |
172 | 321 |
173 media_task_runner_->PostTask( | 322 media_task_runner_->PostTask( |
174 FROM_HERE, | 323 FROM_HERE, base::Bind(&RendererWrapper::Resume, |
175 base::Bind(&PipelineImpl::ResumeTask, weak_this_, base::Passed(&renderer), | 324 base::Unretained(renderer_wrapper_.get()), |
176 timestamp, media::BindToCurrentLoop(seek_cb))); | 325 base::Passed(&renderer), time)); |
177 } | 326 } |
178 | 327 |
179 float PipelineImpl::GetVolume() const { | 328 float PipelineImpl::GetVolume() const { |
180 // TODO(alokp): Add thread DCHECK after removing the internal usage on | 329 DCHECK(thread_checker_.CalledOnValidThread()); |
181 // media thread. | |
182 base::AutoLock auto_lock(lock_); | |
183 return volume_; | 330 return volume_; |
184 } | 331 } |
185 | 332 |
186 void PipelineImpl::SetVolume(float volume) { | 333 void PipelineImpl::SetVolume(float volume) { |
187 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 334 DVLOG(2) << __FUNCTION__ << "(" << volume << ")"; |
| 335 DCHECK(thread_checker_.CalledOnValidThread()); |
188 | 336 |
189 if (volume < 0.0f || volume > 1.0f) | 337 if (volume < 0.0f || volume > 1.0f) |
190 return; | 338 return; |
191 | 339 |
192 base::AutoLock auto_lock(lock_); | |
193 volume_ = volume; | 340 volume_ = volume; |
194 if (running_) { | 341 media_task_runner_->PostTask( |
195 media_task_runner_->PostTask( | 342 FROM_HERE, |
196 FROM_HERE, | 343 base::Bind(&RendererWrapper::SetVolume, |
197 base::Bind(&PipelineImpl::VolumeChangedTask, weak_this_, volume)); | 344 base::Unretained(renderer_wrapper_.get()), volume_)); |
198 } | |
199 } | 345 } |
200 | 346 |
201 TimeDelta PipelineImpl::GetMediaTime() const { | 347 base::TimeDelta PipelineImpl::GetMediaTime() const { |
202 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 348 DCHECK(thread_checker_.CalledOnValidThread()); |
203 | 349 return renderer_wrapper_->GetMediaTime(); |
204 base::AutoLock auto_lock(lock_); | |
205 if (suspend_timestamp_ != kNoTimestamp()) | |
206 return suspend_timestamp_; | |
207 return renderer_ ? std::min(renderer_->GetMediaTime(), duration_) | |
208 : TimeDelta(); | |
209 } | 350 } |
210 | 351 |
211 Ranges<TimeDelta> PipelineImpl::GetBufferedTimeRanges() const { | 352 Ranges<base::TimeDelta> PipelineImpl::GetBufferedTimeRanges() const { |
212 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 353 DCHECK(thread_checker_.CalledOnValidThread()); |
213 | |
214 base::AutoLock auto_lock(lock_); | |
215 return buffered_time_ranges_; | 354 return buffered_time_ranges_; |
216 } | 355 } |
217 | 356 |
218 TimeDelta PipelineImpl::GetMediaDuration() const { | 357 base::TimeDelta PipelineImpl::GetMediaDuration() const { |
219 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 358 DCHECK(thread_checker_.CalledOnValidThread()); |
220 | |
221 base::AutoLock auto_lock(lock_); | |
222 return duration_; | 359 return duration_; |
223 } | 360 } |
224 | 361 |
225 bool PipelineImpl::DidLoadingProgress() { | 362 bool PipelineImpl::DidLoadingProgress() { |
226 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 363 DCHECK(thread_checker_.CalledOnValidThread()); |
227 | |
228 base::AutoLock auto_lock(lock_); | |
229 bool ret = did_loading_progress_; | 364 bool ret = did_loading_progress_; |
230 did_loading_progress_ = false; | 365 did_loading_progress_ = false; |
231 return ret; | 366 return ret; |
232 } | 367 } |
233 | 368 |
234 PipelineStatistics PipelineImpl::GetStatistics() const { | 369 PipelineStatistics PipelineImpl::GetStatistics() const { |
235 // TODO(alokp): Add thread DCHECK after removing the internal usage on | 370 DCHECK(thread_checker_.CalledOnValidThread()); |
236 // media thread. | |
237 base::AutoLock auto_lock(lock_); | |
238 return statistics_; | 371 return statistics_; |
239 } | 372 } |
240 | 373 |
241 void PipelineImpl::SetCdm(CdmContext* cdm_context, | 374 void PipelineImpl::SetCdm(CdmContext* cdm_context, |
242 const CdmAttachedCB& cdm_attached_cb) { | 375 const CdmAttachedCB& cdm_attached_cb) { |
243 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 376 DVLOG(2) << __FUNCTION__; |
| 377 DCHECK(thread_checker_.CalledOnValidThread()); |
| 378 DCHECK(cdm_context); |
| 379 DCHECK(!cdm_attached_cb.is_null()); |
244 | 380 |
245 media_task_runner_->PostTask( | 381 media_task_runner_->PostTask( |
246 FROM_HERE, base::Bind(&PipelineImpl::SetCdmTask, weak_this_, cdm_context, | 382 FROM_HERE, |
247 cdm_attached_cb)); | 383 base::Bind(&RendererWrapper::SetCdm, |
| 384 base::Unretained(renderer_wrapper_.get()), cdm_context, |
| 385 media::BindToCurrentLoop(cdm_attached_cb))); |
248 } | 386 } |
249 | 387 |
250 void PipelineImpl::SetErrorForTesting(PipelineStatus status) { | 388 void PipelineImpl::RendererWrapper::SetState(State next_state) { |
251 OnError(status); | |
252 } | |
253 | |
254 bool PipelineImpl::HasWeakPtrsForTesting() const { | |
255 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 389 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
256 return weak_factory_.HasWeakPtrs(); | 390 DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> " |
257 } | 391 << PipelineImpl::GetStateString(next_state); |
258 | |
259 void PipelineImpl::SetState(State next_state) { | |
260 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
261 DVLOG(1) << GetStateString(state_) << " -> " << GetStateString(next_state); | |
262 | 392 |
263 state_ = next_state; | 393 state_ = next_state; |
264 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); | 394 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); |
265 } | 395 } |
266 | 396 |
267 #define RETURN_STRING(state) \ | 397 #define RETURN_STRING(state) \ |
268 case state: \ | 398 case state: \ |
269 return #state; | 399 return #state; |
270 | 400 |
| 401 // static |
271 const char* PipelineImpl::GetStateString(State state) { | 402 const char* PipelineImpl::GetStateString(State state) { |
272 switch (state) { | 403 switch (state) { |
273 RETURN_STRING(kCreated); | 404 RETURN_STRING(kCreated); |
274 RETURN_STRING(kInitDemuxer); | 405 RETURN_STRING(kInitDemuxer); |
275 RETURN_STRING(kInitRenderer); | 406 RETURN_STRING(kInitRenderer); |
276 RETURN_STRING(kSeeking); | 407 RETURN_STRING(kSeeking); |
277 RETURN_STRING(kPlaying); | 408 RETURN_STRING(kPlaying); |
278 RETURN_STRING(kStopping); | 409 RETURN_STRING(kStopping); |
279 RETURN_STRING(kStopped); | 410 RETURN_STRING(kStopped); |
280 RETURN_STRING(kSuspending); | 411 RETURN_STRING(kSuspending); |
281 RETURN_STRING(kSuspended); | 412 RETURN_STRING(kSuspended); |
282 RETURN_STRING(kResuming); | 413 RETURN_STRING(kResuming); |
283 } | 414 } |
284 NOTREACHED(); | 415 NOTREACHED(); |
285 return "INVALID"; | 416 return "INVALID"; |
286 } | 417 } |
287 | 418 |
288 #undef RETURN_STRING | 419 #undef RETURN_STRING |
289 | 420 |
290 PipelineImpl::State PipelineImpl::GetNextState() const { | 421 PipelineImpl::State PipelineImpl::RendererWrapper::GetNextState() const { |
291 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 422 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
292 DCHECK(stop_cb_.is_null()) << "State transitions don't happen when stopping"; | |
293 DCHECK_EQ(status_, PIPELINE_OK) | 423 DCHECK_EQ(status_, PIPELINE_OK) |
294 << "State transitions don't happen when there's an error: " << status_; | 424 << "State transitions don't happen when there's an error: " << status_; |
295 | 425 |
296 switch (state_) { | 426 switch (state_) { |
297 case kCreated: | 427 case kCreated: |
298 return kInitDemuxer; | 428 return kInitDemuxer; |
299 | 429 |
300 case kInitDemuxer: | 430 case kInitDemuxer: |
301 return kInitRenderer; | 431 return kInitRenderer; |
302 | 432 |
(...skipping 12 matching lines...) Expand all Loading... |
315 | 445 |
316 case kPlaying: | 446 case kPlaying: |
317 case kStopping: | 447 case kStopping: |
318 case kStopped: | 448 case kStopped: |
319 break; | 449 break; |
320 } | 450 } |
321 NOTREACHED() << "State has no transition: " << state_; | 451 NOTREACHED() << "State has no transition: " << state_; |
322 return state_; | 452 return state_; |
323 } | 453 } |
324 | 454 |
325 void PipelineImpl::OnDemuxerError(PipelineStatus error) { | 455 void PipelineImpl::RendererWrapper::OnDemuxerError(PipelineStatus error) { |
326 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer | 456 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
327 // implementations call DemuxerHost on the media thread. | 457 // implementations call DemuxerHost on the media thread. |
328 media_task_runner_->PostTask( | 458 media_task_runner_->PostTask( |
329 FROM_HERE, | 459 FROM_HERE, |
330 base::Bind(&PipelineImpl::ErrorChangedTask, weak_this_, error)); | 460 base::Bind(&RendererWrapper::OnPipelineError, weak_this_, error)); |
331 } | 461 } |
332 | 462 |
333 void PipelineImpl::AddTextStream(DemuxerStream* text_stream, | 463 void PipelineImpl::RendererWrapper::AddTextStream( |
334 const TextTrackConfig& config) { | 464 DemuxerStream* text_stream, |
| 465 const TextTrackConfig& config) { |
335 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer | 466 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
336 // implementations call DemuxerHost on the media thread. | 467 // implementations call DemuxerHost on the media thread. |
337 media_task_runner_->PostTask( | 468 media_task_runner_->PostTask( |
338 FROM_HERE, base::Bind(&PipelineImpl::AddTextStreamTask, weak_this_, | 469 FROM_HERE, base::Bind(&RendererWrapper::AddTextStreamTask, weak_this_, |
339 text_stream, config)); | 470 text_stream, config)); |
340 } | 471 } |
341 | 472 |
342 void PipelineImpl::RemoveTextStream(DemuxerStream* text_stream) { | 473 void PipelineImpl::RendererWrapper::RemoveTextStream( |
| 474 DemuxerStream* text_stream) { |
343 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer | 475 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
344 // implementations call DemuxerHost on the media thread. | 476 // implementations call DemuxerHost on the media thread. |
345 media_task_runner_->PostTask( | 477 media_task_runner_->PostTask( |
346 FROM_HERE, | 478 FROM_HERE, base::Bind(&RendererWrapper::RemoveTextStreamTask, weak_this_, |
347 base::Bind(&PipelineImpl::RemoveTextStreamTask, weak_this_, text_stream)); | 479 text_stream)); |
348 } | 480 } |
349 | 481 |
350 void PipelineImpl::OnError(PipelineStatus error) { | 482 void PipelineImpl::RendererWrapper::OnError(PipelineStatus error) { |
351 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 483 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
352 DCHECK(IsRunning()); | |
353 DCHECK_NE(PIPELINE_OK, error); | |
354 VLOG(1) << "Media pipeline error: " << error; | |
355 | 484 |
356 media_task_runner_->PostTask( | 485 media_task_runner_->PostTask( |
357 FROM_HERE, | 486 FROM_HERE, |
358 base::Bind(&PipelineImpl::ErrorChangedTask, weak_this_, error)); | 487 base::Bind(&RendererWrapper::OnPipelineError, weak_this_, error)); |
359 } | 488 } |
360 | 489 |
361 void PipelineImpl::OnEnded() { | 490 void PipelineImpl::RendererWrapper::OnEnded() { |
362 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 491 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
363 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED)); | 492 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED)); |
364 | 493 |
365 if (state_ != kPlaying) | 494 if (state_ != kPlaying) |
366 return; | 495 return; |
367 | 496 |
368 DCHECK(!renderer_ended_); | 497 DCHECK(!renderer_ended_); |
369 renderer_ended_ = true; | 498 renderer_ended_ = true; |
370 | |
371 RunEndedCallbackIfNeeded(); | 499 RunEndedCallbackIfNeeded(); |
372 } | 500 } |
373 | 501 |
374 void PipelineImpl::OnStatisticsUpdate(const PipelineStatistics& stats) { | 502 void PipelineImpl::RendererWrapper::OnStatisticsUpdate( |
375 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 503 const PipelineStatistics& stats) { |
376 | |
377 base::AutoLock auto_lock(lock_); | |
378 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; | |
379 statistics_.video_bytes_decoded += stats.video_bytes_decoded; | |
380 statistics_.video_frames_decoded += stats.video_frames_decoded; | |
381 statistics_.video_frames_dropped += stats.video_frames_dropped; | |
382 statistics_.audio_memory_usage += stats.audio_memory_usage; | |
383 statistics_.video_memory_usage += stats.video_memory_usage; | |
384 } | |
385 | |
386 void PipelineImpl::OnBufferingStateChange(BufferingState state) { | |
387 DVLOG(1) << __FUNCTION__ << "(" << state << ") "; | |
388 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
389 | |
390 main_task_runner_->PostTask( | |
391 FROM_HERE, base::Bind(&Pipeline::Client::OnBufferingStateChange, | |
392 weak_client_, state)); | |
393 } | |
394 | |
395 void PipelineImpl::OnWaitingForDecryptionKey() { | |
396 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 504 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
397 | 505 |
398 main_task_runner_->PostTask( | 506 main_task_runner_->PostTask( |
399 FROM_HERE, | 507 FROM_HERE, |
400 base::Bind(&Pipeline::Client::OnWaitingForDecryptionKey, weak_client_)); | 508 base::Bind(&PipelineImpl::OnStatisticsUpdate, weak_pipeline_, stats)); |
401 } | 509 } |
402 | 510 |
403 void PipelineImpl::OnVideoNaturalSizeChange(const gfx::Size& size) { | 511 void PipelineImpl::RendererWrapper::OnBufferingStateChange( |
| 512 BufferingState state) { |
| 513 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 514 DVLOG(2) << __FUNCTION__ << "(" << state << ") "; |
| 515 |
| 516 main_task_runner_->PostTask( |
| 517 FROM_HERE, |
| 518 base::Bind(&PipelineImpl::OnBufferingStateChange, weak_pipeline_, state)); |
| 519 } |
| 520 |
| 521 void PipelineImpl::RendererWrapper::OnWaitingForDecryptionKey() { |
404 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 522 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
405 | 523 |
406 main_task_runner_->PostTask( | 524 main_task_runner_->PostTask( |
407 FROM_HERE, base::Bind(&Pipeline::Client::OnVideoNaturalSizeChange, | 525 FROM_HERE, |
408 weak_client_, size)); | 526 base::Bind(&PipelineImpl::OnWaitingForDecryptionKey, weak_pipeline_)); |
409 } | 527 } |
410 | 528 |
411 void PipelineImpl::OnVideoOpacityChange(bool opaque) { | 529 void PipelineImpl::RendererWrapper::OnVideoNaturalSizeChange( |
| 530 const gfx::Size& size) { |
412 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 531 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
413 | 532 |
414 main_task_runner_->PostTask( | 533 main_task_runner_->PostTask( |
415 FROM_HERE, base::Bind(&Pipeline::Client::OnVideoOpacityChange, | 534 FROM_HERE, base::Bind(&PipelineImpl::OnVideoNaturalSizeChange, |
416 weak_client_, opaque)); | 535 weak_pipeline_, size)); |
417 } | 536 } |
418 | 537 |
419 void PipelineImpl::SetDuration(TimeDelta duration) { | 538 void PipelineImpl::RendererWrapper::OnVideoOpacityChange(bool opaque) { |
| 539 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 540 |
| 541 main_task_runner_->PostTask( |
| 542 FROM_HERE, |
| 543 base::Bind(&PipelineImpl::OnVideoOpacityChange, weak_pipeline_, opaque)); |
| 544 } |
| 545 |
| 546 void PipelineImpl::RendererWrapper::SetDuration(base::TimeDelta duration) { |
420 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer | 547 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
421 // implementations call DemuxerHost on the media thread. | 548 // implementations call DemuxerHost on the media thread. |
422 DCHECK(IsRunning()); | |
423 media_log_->AddEvent(media_log_->CreateTimeEvent(MediaLogEvent::DURATION_SET, | 549 media_log_->AddEvent(media_log_->CreateTimeEvent(MediaLogEvent::DURATION_SET, |
424 "duration", duration)); | 550 "duration", duration)); |
425 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration); | 551 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration); |
426 | 552 |
427 base::AutoLock auto_lock(lock_); | |
428 duration_ = duration; | |
429 main_task_runner_->PostTask( | 553 main_task_runner_->PostTask( |
430 FROM_HERE, base::Bind(&Pipeline::Client::OnDurationChange, weak_client_)); | 554 FROM_HERE, |
| 555 base::Bind(&PipelineImpl::OnDurationChange, weak_pipeline_, duration)); |
431 } | 556 } |
432 | 557 |
433 void PipelineImpl::StateTransitionTask(PipelineStatus status) { | 558 void PipelineImpl::RendererWrapper::StateTransitionTask(PipelineStatus status) { |
434 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 559 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
435 | 560 |
436 // No-op any state transitions if we're stopping. | 561 // No-op any state transitions if we're stopping or already encountered error. |
437 if (state_ == kStopping || state_ == kStopped) | 562 if (state_ == kStopping || state_ == kStopped || status_ != PIPELINE_OK) |
438 return; | 563 return; |
439 | 564 |
440 // Report error from the previous operation. | 565 // Report error from the previous operation. |
441 if (status != PIPELINE_OK) { | 566 if (status != PIPELINE_OK) { |
442 ErrorChangedTask(status); | 567 OnPipelineError(status); |
443 return; | 568 return; |
444 } | 569 } |
445 | 570 |
446 // Guard against accidentally clearing |pending_callbacks_| for states that | 571 // Guard against accidentally clearing |pending_callbacks_| for states that |
447 // use it as well as states that should not be using it. | 572 // use it as well as states that should not be using it. |
448 DCHECK_EQ(pending_callbacks_.get() != NULL, | 573 DCHECK_EQ(pending_callbacks_.get() != NULL, |
449 state_ == kSeeking || state_ == kSuspending || state_ == kResuming); | 574 state_ == kSeeking || state_ == kSuspending || state_ == kResuming); |
450 | 575 |
451 pending_callbacks_.reset(); | 576 pending_callbacks_.reset(); |
452 | 577 |
453 PipelineStatusCB done_cb = | 578 PipelineStatusCB done_cb = |
454 base::Bind(&PipelineImpl::StateTransitionTask, weak_this_); | 579 base::Bind(&RendererWrapper::StateTransitionTask, weak_this_); |
455 | 580 |
456 // Switch states, performing any entrance actions for the new state as well. | 581 // Switch states, performing any entrance actions for the new state as well. |
457 SetState(GetNextState()); | 582 SetState(GetNextState()); |
458 switch (state_) { | 583 switch (state_) { |
459 case kInitDemuxer: | 584 case kInitDemuxer: |
460 return InitializeDemuxer(done_cb); | 585 return InitializeDemuxer(done_cb); |
461 | 586 |
462 case kInitRenderer: | 587 case kInitRenderer: |
463 // When the state_ transfers to kInitRenderer, it means the demuxer has | 588 // When the state_ transfers to kInitRenderer, it means the demuxer has |
464 // finished parsing the init info. It should call ReportMetadata in case | 589 // finished parsing the init info. It should call ReportMetadata in case |
465 // meeting 'decode' error when passing media segment but WebMediaPlayer's | 590 // meeting 'decode' error when passing media segment but WebMediaPlayer's |
466 // ready_state_ is still ReadyStateHaveNothing. In that case, it will | 591 // ready_state_ is still ReadyStateHaveNothing. In that case, it will |
467 // treat it as NetworkStateFormatError not NetworkStateDecodeError. | 592 // treat it as NetworkStateFormatError not NetworkStateDecodeError. |
468 ReportMetadata(); | 593 ReportMetadata(); |
469 start_timestamp_ = demuxer_->GetStartTime(); | 594 start_timestamp_ = demuxer_->GetStartTime(); |
470 | 595 |
471 return InitializeRenderer(done_cb); | 596 return InitializeRenderer(done_cb); |
472 | 597 |
473 case kPlaying: | 598 case kPlaying: |
474 DCHECK(start_timestamp_ >= base::TimeDelta()); | 599 DCHECK(start_timestamp_ >= base::TimeDelta()); |
475 renderer_->StartPlayingFrom(start_timestamp_); | 600 renderer_->StartPlayingFrom(start_timestamp_); |
476 { | 601 { |
477 base::AutoLock auto_lock(lock_); | 602 base::AutoLock auto_lock(media_time_lock_); |
478 suspend_timestamp_ = kNoTimestamp(); | 603 suspend_timestamp_ = kNoTimestamp(); |
479 } | 604 } |
480 | 605 |
481 if (text_renderer_) | 606 if (text_renderer_) |
482 text_renderer_->StartPlaying(); | 607 text_renderer_->StartPlaying(); |
483 | 608 |
484 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); | 609 main_task_runner_->PostTask( |
| 610 FROM_HERE, base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_, |
| 611 start_timestamp_)); |
485 | 612 |
486 PlaybackRateChangedTask(GetPlaybackRate()); | 613 renderer_->SetPlaybackRate(playback_rate_); |
487 VolumeChangedTask(GetVolume()); | 614 renderer_->SetVolume(volume_); |
488 return; | 615 return; |
489 | 616 |
490 case kSuspended: | 617 case kSuspended: |
491 renderer_.reset(); | 618 renderer_.reset(); |
492 { | 619 main_task_runner_->PostTask( |
493 base::AutoLock auto_lock(lock_); | 620 FROM_HERE, base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_, |
494 statistics_.audio_memory_usage = 0; | 621 suspend_timestamp_)); |
495 statistics_.video_memory_usage = 0; | |
496 } | |
497 base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK); | |
498 return; | 622 return; |
499 | 623 |
500 case kStopping: | 624 case kStopping: |
501 case kStopped: | 625 case kStopped: |
502 case kCreated: | 626 case kCreated: |
503 case kSeeking: | 627 case kSeeking: |
504 case kSuspending: | 628 case kSuspending: |
505 case kResuming: | 629 case kResuming: |
506 NOTREACHED() << "State has no transition: " << state_; | 630 NOTREACHED() << "State has no transition: " << state_; |
507 return; | 631 return; |
508 } | 632 } |
509 } | 633 } |
510 | 634 |
511 // Note that the usage of base::Unretained() with the renderers is considered | 635 // Note that the usage of base::Unretained() with the renderers is considered |
512 // safe as they are owned by |pending_callbacks_| and share the same lifetime. | 636 // safe as they are owned by |pending_callbacks_| and share the same lifetime. |
513 // | 637 // |
514 // That being said, deleting the renderers while keeping |pending_callbacks_| | 638 // That being said, deleting the renderers while keeping |pending_callbacks_| |
515 // running on the media thread would result in crashes. | 639 // running on the media thread would result in crashes. |
516 void PipelineImpl::DoSeek(TimeDelta seek_timestamp, | 640 void PipelineImpl::RendererWrapper::DoSeek(base::TimeDelta seek_timestamp, |
517 const PipelineStatusCB& done_cb) { | 641 const PipelineStatusCB& done_cb) { |
518 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 642 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
519 DCHECK(!pending_callbacks_.get()); | 643 DCHECK(!pending_callbacks_.get()); |
520 DCHECK_EQ(state_, kSeeking); | 644 DCHECK_EQ(state_, kSeeking); |
521 SerialRunner::Queue bound_fns; | 645 SerialRunner::Queue bound_fns; |
522 | 646 |
523 // Pause. | 647 // Pause. |
524 if (text_renderer_) { | 648 if (text_renderer_) { |
525 bound_fns.Push(base::Bind(&TextRenderer::Pause, | 649 bound_fns.Push(base::Bind(&TextRenderer::Pause, |
526 base::Unretained(text_renderer_.get()))); | 650 base::Unretained(text_renderer_.get()))); |
527 } | 651 } |
528 | 652 |
529 // Flush. | 653 // Flush. |
530 DCHECK(renderer_); | 654 DCHECK(renderer_); |
531 bound_fns.Push( | 655 bound_fns.Push( |
532 base::Bind(&Renderer::Flush, base::Unretained(renderer_.get()))); | 656 base::Bind(&Renderer::Flush, base::Unretained(renderer_.get()))); |
533 | 657 |
534 if (text_renderer_) { | 658 if (text_renderer_) { |
535 bound_fns.Push(base::Bind(&TextRenderer::Flush, | 659 bound_fns.Push(base::Bind(&TextRenderer::Flush, |
536 base::Unretained(text_renderer_.get()))); | 660 base::Unretained(text_renderer_.get()))); |
537 } | 661 } |
538 | 662 |
539 // Seek demuxer. | 663 // Seek demuxer. |
540 bound_fns.Push( | 664 bound_fns.Push( |
541 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); | 665 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); |
542 | 666 |
543 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); | 667 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); |
544 } | 668 } |
545 | 669 |
546 void PipelineImpl::DoStop() { | 670 void PipelineImpl::RendererWrapper::DoStop(const base::Closure& done_cb) { |
547 DVLOG(2) << __FUNCTION__; | 671 DVLOG(2) << __FUNCTION__; |
548 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 672 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
549 DCHECK_EQ(state_, kStopping); | 673 DCHECK_EQ(state_, kStopping); |
550 DCHECK(!pending_callbacks_.get()); | 674 DCHECK(!pending_callbacks_.get()); |
551 | 675 |
552 // TODO(scherkus): Enforce that Renderer is only called on a single thread, | 676 // TODO(scherkus): Enforce that Renderer is only called on a single thread, |
553 // even for accessing media time http://crbug.com/370634 | 677 // even for accessing media time http://crbug.com/370634 |
554 std::unique_ptr<Renderer> renderer; | 678 std::unique_ptr<Renderer> renderer; |
555 { | 679 { |
556 base::AutoLock auto_lock(lock_); | 680 base::AutoLock auto_lock(media_time_lock_); |
557 renderer.swap(renderer_); | 681 renderer.swap(renderer_); |
558 } | 682 } |
559 renderer.reset(); | 683 renderer.reset(); |
560 text_renderer_.reset(); | 684 text_renderer_.reset(); |
561 | 685 |
562 if (demuxer_) { | 686 if (demuxer_) { |
563 demuxer_->Stop(); | 687 demuxer_->Stop(); |
564 demuxer_ = NULL; | 688 demuxer_ = NULL; |
565 } | 689 } |
566 | 690 |
567 { | |
568 base::AutoLock auto_lock(lock_); | |
569 running_ = false; | |
570 } | |
571 SetState(kStopped); | 691 SetState(kStopped); |
572 | 692 |
573 // If we stop during initialization/seeking/suspending we don't want to leave | 693 // Post the stop callback to enqueue it after the tasks that may have been |
574 // outstanding callbacks around. The callbacks also do not get run if the | 694 // posted by Demuxer and Renderer during stopping. Note that in theory the |
575 // pipeline is stopped before it had a chance to complete outstanding tasks. | 695 // tasks posted by Demuxer/Renderer may post even more tasks that will get |
576 seek_cb_.Reset(); | 696 // enqueued after |done_cb|. This may be problematic because Demuxer may |
577 suspend_cb_.Reset(); | 697 // get destroyed as soon as |done_cb| is run. In practice this is not a |
578 | 698 // problem, but ideally Demuxer should be destroyed on the media thread. |
579 if (!stop_cb_.is_null()) { | 699 media_task_runner_->PostTask(FROM_HERE, done_cb); |
580 // Invalid all weak pointers so it's safe to destroy |this| on the render | |
581 // main thread. | |
582 weak_factory_.InvalidateWeakPtrs(); | |
583 | |
584 // Post the stop callback to enqueue it after the tasks that may have been | |
585 // Demuxer and Renderer during stopping. | |
586 media_task_runner_->PostTask(FROM_HERE, base::ResetAndReturn(&stop_cb_)); | |
587 } | |
588 } | 700 } |
589 | 701 |
590 void PipelineImpl::OnBufferedTimeRangesChanged( | 702 void PipelineImpl::RendererWrapper::OnBufferedTimeRangesChanged( |
591 const Ranges<base::TimeDelta>& ranges) { | 703 const Ranges<base::TimeDelta>& ranges) { |
592 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer | 704 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
593 // implementations call DemuxerHost on the media thread. | 705 // implementations call DemuxerHost on the media thread. |
594 base::AutoLock auto_lock(lock_); | 706 main_task_runner_->PostTask( |
595 buffered_time_ranges_ = ranges; | 707 FROM_HERE, base::Bind(&PipelineImpl::OnBufferedTimeRangesChange, |
596 did_loading_progress_ = true; | 708 weak_pipeline_, ranges)); |
597 } | 709 } |
598 | 710 |
599 void PipelineImpl::StartTask() { | 711 void PipelineImpl::RendererWrapper::Start( |
| 712 Demuxer* demuxer, |
| 713 std::unique_ptr<Renderer> renderer, |
| 714 std::unique_ptr<TextRenderer> text_renderer) { |
600 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 715 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 716 DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: " |
| 717 << state_; |
601 | 718 |
602 CHECK_EQ(kCreated, state_) | 719 DCHECK(!demuxer_); |
603 << "Media pipeline cannot be started more than once"; | 720 DCHECK(!renderer_); |
604 | 721 DCHECK(!text_renderer_); |
605 text_renderer_ = CreateTextRenderer(); | 722 DCHECK(!renderer_ended_); |
| 723 DCHECK(!text_renderer_ended_); |
| 724 demuxer_ = demuxer; |
| 725 { |
| 726 base::AutoLock auto_lock(media_time_lock_); |
| 727 renderer_ = std::move(renderer); |
| 728 } |
| 729 text_renderer_ = std::move(text_renderer); |
606 if (text_renderer_) { | 730 if (text_renderer_) { |
607 text_renderer_->Initialize( | 731 text_renderer_->Initialize( |
608 base::Bind(&PipelineImpl::OnTextRendererEnded, weak_this_)); | 732 base::Bind(&RendererWrapper::OnTextRendererEnded, weak_this_)); |
609 } | 733 } |
610 | 734 |
611 StateTransitionTask(PIPELINE_OK); | 735 StateTransitionTask(PIPELINE_OK); |
612 } | 736 } |
613 | 737 |
614 void PipelineImpl::StopTask(const base::Closure& stop_cb) { | 738 void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) { |
615 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 739 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
616 DCHECK(stop_cb_.is_null()); | 740 DCHECK(state_ != kStopping && state_ != kStopped); |
617 | |
618 if (state_ == kStopped) { | |
619 // Invalid all weak pointers so it's safe to destroy |this| on the render | |
620 // main thread. | |
621 weak_factory_.InvalidateWeakPtrs(); | |
622 | |
623 // NOTE: pipeline may be deleted at this point in time as a result of | |
624 // executing |stop_cb|. | |
625 stop_cb.Run(); | |
626 | |
627 return; | |
628 } | |
629 | |
630 stop_cb_ = stop_cb; | |
631 | |
632 // We may already be stopping due to a runtime error. | |
633 if (state_ == kStopping) | |
634 return; | |
635 | |
636 // Do not report statistics if the pipeline is not fully initialized. | |
637 if (state_ == kSeeking || state_ == kPlaying || state_ == kSuspending || | |
638 state_ == kSuspended || state_ == kResuming) { | |
639 PipelineStatistics stats = GetStatistics(); | |
640 if (stats.video_frames_decoded > 0) { | |
641 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount", | |
642 stats.video_frames_dropped); | |
643 } | |
644 } | |
645 | 741 |
646 SetState(kStopping); | 742 SetState(kStopping); |
| 743 |
| 744 // If we stop during starting/seeking/suspending/resuming we don't want to |
| 745 // leave outstanding callbacks around. The callbacks also do not get run if |
| 746 // the pipeline is stopped before it had a chance to complete outstanding |
| 747 // tasks. |
647 pending_callbacks_.reset(); | 748 pending_callbacks_.reset(); |
648 DoStop(); | 749 |
| 750 DoStop(stop_cb); |
649 } | 751 } |
650 | 752 |
651 void PipelineImpl::ErrorChangedTask(PipelineStatus error) { | 753 void PipelineImpl::RendererWrapper::OnPipelineError(PipelineStatus error) { |
652 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 754 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
653 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; | 755 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; |
654 | 756 |
655 // Preserve existing abnormal status. | 757 // Preserve existing abnormal status. |
656 if (status_ != PIPELINE_OK) | 758 if (status_ != PIPELINE_OK) |
657 return; | 759 return; |
658 | 760 |
659 // Don't report pipeline error events to the media log here. The embedder will | 761 // Don't report pipeline error events to the media log here. The embedder |
660 // log this when Client::OnError is called. If the pipeline is already stopped | 762 // will log this when Client::OnError is called. If the pipeline is already |
661 // or stopping we also don't want to log any event. In case we are suspending | 763 // stopped or stopping we also don't want to log any event. In case we are |
662 // or suspended, the error may be recoverable, so don't propagate it now, | 764 // suspending or suspended, the error may be recoverable, so don't propagate |
663 // instead let the subsequent seek during resume propagate it if it's | 765 // it now, instead let the subsequent seek during resume propagate it if |
664 // unrecoverable. | 766 // it's unrecoverable. |
665 if (state_ == kStopping || state_ == kStopped || state_ == kSuspending || | 767 if (state_ == kStopping || state_ == kStopped || state_ == kSuspending || |
666 state_ == kSuspended) { | 768 state_ == kSuspended) { |
667 return; | 769 return; |
668 } | 770 } |
669 | 771 |
670 // Once we enter |kStopping| state, nothing is reported back to the client. | |
671 // If we encounter an error during initialization/seeking/suspending, | |
672 // report the error using the completion callbacks for those tasks. | |
673 status_ = error; | 772 status_ = error; |
674 bool error_reported = false; | 773 main_task_runner_->PostTask( |
675 if (!seek_cb_.is_null()) { | 774 FROM_HERE, base::Bind(&PipelineImpl::OnError, weak_pipeline_, error)); |
676 base::ResetAndReturn(&seek_cb_).Run(status_); | |
677 error_reported = true; | |
678 } | |
679 if (!suspend_cb_.is_null()) { | |
680 base::ResetAndReturn(&suspend_cb_).Run(status_); | |
681 error_reported = true; | |
682 } | |
683 if (!error_reported) { | |
684 DCHECK_NE(status_, PIPELINE_OK); | |
685 main_task_runner_->PostTask( | |
686 FROM_HERE, | |
687 base::Bind(&Pipeline::Client::OnError, weak_client_, status_)); | |
688 } | |
689 | |
690 SetState(kStopping); | |
691 pending_callbacks_.reset(); | |
692 DoStop(); | |
693 } | 775 } |
694 | 776 |
695 void PipelineImpl::PlaybackRateChangedTask(double playback_rate) { | 777 void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) { |
696 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 778 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
697 | 779 |
698 // Playback rate changes are only carried out while playing. | 780 playback_rate_ = playback_rate; |
699 if (state_ != kPlaying) | 781 if (state_ == kPlaying) |
700 return; | 782 renderer_->SetPlaybackRate(playback_rate_); |
701 | |
702 renderer_->SetPlaybackRate(playback_rate); | |
703 } | 783 } |
704 | 784 |
705 void PipelineImpl::VolumeChangedTask(float volume) { | 785 void PipelineImpl::RendererWrapper::SetVolume(float volume) { |
706 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 786 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
707 | 787 |
708 // Volume changes are only carried out while playing. | 788 volume_ = volume; |
709 if (state_ != kPlaying) | 789 if (state_ == kPlaying) |
710 return; | 790 renderer_->SetVolume(volume_); |
711 | |
712 renderer_->SetVolume(volume); | |
713 } | 791 } |
714 | 792 |
715 void PipelineImpl::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { | 793 void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) { |
716 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 794 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
717 DCHECK(stop_cb_.is_null()); | |
718 | 795 |
719 // Suppress seeking if we're not fully started. | 796 // Suppress seeking if we're not fully started. |
720 if (state_ != kPlaying) { | 797 if (state_ != kPlaying) { |
721 DCHECK(state_ == kStopping || state_ == kStopped) | 798 DCHECK(state_ == kStopping || state_ == kStopped) |
722 << "Receive seek in unexpected state: " << state_; | 799 << "Receive seek in unexpected state: " << state_; |
723 seek_cb.Run(PIPELINE_ERROR_INVALID_STATE); | 800 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
724 return; | 801 return; |
725 } | 802 } |
726 | 803 |
727 DCHECK(seek_cb_.is_null()); | |
728 | |
729 const base::TimeDelta seek_timestamp = | 804 const base::TimeDelta seek_timestamp = |
730 std::max(time, demuxer_->GetStartTime()); | 805 std::max(time, demuxer_->GetStartTime()); |
731 | 806 |
732 SetState(kSeeking); | 807 SetState(kSeeking); |
733 seek_cb_ = seek_cb; | |
734 renderer_ended_ = false; | 808 renderer_ended_ = false; |
735 text_renderer_ended_ = false; | 809 text_renderer_ended_ = false; |
736 start_timestamp_ = seek_timestamp; | 810 start_timestamp_ = seek_timestamp; |
737 | 811 |
738 DoSeek(seek_timestamp, | 812 DoSeek(seek_timestamp, |
739 base::Bind(&PipelineImpl::StateTransitionTask, weak_this_)); | 813 base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); |
740 } | 814 } |
741 | 815 |
742 void PipelineImpl::SuspendTask(const PipelineStatusCB& suspend_cb) { | 816 void PipelineImpl::RendererWrapper::Suspend() { |
743 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 817 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
744 | 818 |
745 // Suppress suspending if we're not playing. | 819 // Suppress suspending if we're not playing. |
746 if (state_ != kPlaying) { | 820 if (state_ != kPlaying) { |
747 DCHECK(state_ == kStopping || state_ == kStopped) | 821 DCHECK(state_ == kStopping || state_ == kStopped) |
748 << "Receive suspend in unexpected state: " << state_; | 822 << "Receive suspend in unexpected state: " << state_; |
749 suspend_cb.Run(PIPELINE_ERROR_INVALID_STATE); | 823 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
750 return; | 824 return; |
751 } | 825 } |
752 DCHECK(renderer_); | 826 DCHECK(renderer_); |
753 DCHECK(!pending_callbacks_.get()); | 827 DCHECK(!pending_callbacks_.get()); |
754 | 828 |
755 SetState(kSuspending); | 829 SetState(kSuspending); |
756 suspend_cb_ = suspend_cb; | |
757 | 830 |
758 // Freeze playback and record the media time before flushing. (Flushing clears | 831 // Freeze playback and record the media time before flushing. (Flushing clears |
759 // the value.) | 832 // the value.) |
760 renderer_->SetPlaybackRate(0.0); | 833 renderer_->SetPlaybackRate(0.0); |
761 { | 834 { |
762 base::AutoLock auto_lock(lock_); | 835 base::AutoLock auto_lock(media_time_lock_); |
763 suspend_timestamp_ = renderer_->GetMediaTime(); | 836 suspend_timestamp_ = renderer_->GetMediaTime(); |
764 DCHECK(suspend_timestamp_ != kNoTimestamp()); | 837 DCHECK(suspend_timestamp_ != kNoTimestamp()); |
765 } | 838 } |
766 | 839 |
767 // Queue the asynchronous actions required to stop playback. (Matches setup in | 840 // Queue the asynchronous actions required to stop playback. (Matches setup in |
768 // DoSeek().) | 841 // DoSeek().) |
769 // TODO(sandersd): Share implementation with DoSeek(). | 842 // TODO(sandersd): Share implementation with DoSeek(). |
770 SerialRunner::Queue fns; | 843 SerialRunner::Queue fns; |
771 | 844 |
772 if (text_renderer_) { | 845 if (text_renderer_) { |
773 fns.Push(base::Bind(&TextRenderer::Pause, | 846 fns.Push(base::Bind(&TextRenderer::Pause, |
774 base::Unretained(text_renderer_.get()))); | 847 base::Unretained(text_renderer_.get()))); |
775 } | 848 } |
776 | 849 |
777 fns.Push(base::Bind(&Renderer::Flush, base::Unretained(renderer_.get()))); | 850 fns.Push(base::Bind(&Renderer::Flush, base::Unretained(renderer_.get()))); |
778 | 851 |
779 if (text_renderer_) { | 852 if (text_renderer_) { |
780 fns.Push(base::Bind(&TextRenderer::Flush, | 853 fns.Push(base::Bind(&TextRenderer::Flush, |
781 base::Unretained(text_renderer_.get()))); | 854 base::Unretained(text_renderer_.get()))); |
782 } | 855 } |
783 | 856 |
784 pending_callbacks_ = SerialRunner::Run( | 857 pending_callbacks_ = SerialRunner::Run( |
785 fns, base::Bind(&PipelineImpl::StateTransitionTask, weak_this_)); | 858 fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); |
786 } | 859 } |
787 | 860 |
788 void PipelineImpl::ResumeTask(std::unique_ptr<Renderer> renderer, | 861 void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer, |
789 base::TimeDelta timestamp, | 862 base::TimeDelta timestamp) { |
790 const PipelineStatusCB& seek_cb) { | |
791 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 863 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
792 | 864 |
793 // Suppress resuming if we're not suspended. | 865 // Suppress resuming if we're not suspended. |
794 if (state_ != kSuspended) { | 866 if (state_ != kSuspended) { |
795 DCHECK(state_ == kStopping || state_ == kStopped) | 867 DCHECK(state_ == kStopping || state_ == kStopped) |
796 << "Receive resume in unexpected state: " << state_; | 868 << "Receive resume in unexpected state: " << state_; |
797 seek_cb.Run(PIPELINE_ERROR_INVALID_STATE); | 869 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
798 return; | 870 return; |
799 } | 871 } |
800 DCHECK(!renderer_); | 872 DCHECK(!renderer_); |
801 DCHECK(!pending_callbacks_.get()); | 873 DCHECK(!pending_callbacks_.get()); |
802 | 874 |
803 SetState(kResuming); | 875 SetState(kResuming); |
804 renderer_ = std::move(renderer); | 876 |
| 877 { |
| 878 base::AutoLock auto_lock(media_time_lock_); |
| 879 renderer_ = std::move(renderer); |
| 880 } |
805 | 881 |
806 // Set up for a seek. (Matches setup in SeekTask().) | 882 // Set up for a seek. (Matches setup in SeekTask().) |
807 // TODO(sandersd): Share implementation with SeekTask(). | 883 // TODO(sandersd): Share implementation with SeekTask(). |
808 seek_cb_ = seek_cb; | |
809 renderer_ended_ = false; | 884 renderer_ended_ = false; |
810 text_renderer_ended_ = false; | 885 text_renderer_ended_ = false; |
811 start_timestamp_ = std::max(timestamp, demuxer_->GetStartTime()); | 886 start_timestamp_ = std::max(timestamp, demuxer_->GetStartTime()); |
812 | 887 |
813 // Queue the asynchronous actions required to start playback. Unlike DoSeek(), | 888 // Queue the asynchronous actions required to start playback. Unlike DoSeek(), |
814 // we need to initialize the renderer ourselves (we don't want to enter state | 889 // we need to initialize the renderer ourselves (we don't want to enter state |
815 // kInitDemuxer, and even if we did the current code would seek to the start | 890 // kInitDemuxer, and even if we did the current code would seek to the start |
816 // instead of |timestamp|). | 891 // instead of |timestamp|). |
817 SerialRunner::Queue fns; | 892 SerialRunner::Queue fns; |
818 | 893 |
819 fns.Push( | 894 fns.Push( |
820 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp_)); | 895 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp_)); |
821 | 896 |
822 fns.Push(base::Bind(&PipelineImpl::InitializeRenderer, weak_this_)); | 897 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_)); |
823 | 898 |
824 pending_callbacks_ = SerialRunner::Run( | 899 pending_callbacks_ = SerialRunner::Run( |
825 fns, base::Bind(&PipelineImpl::StateTransitionTask, weak_this_)); | 900 fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); |
826 } | 901 } |
827 | 902 |
828 void PipelineImpl::SetCdmTask(CdmContext* cdm_context, | 903 void PipelineImpl::RendererWrapper::SetCdm( |
829 const CdmAttachedCB& cdm_attached_cb) { | 904 CdmContext* cdm_context, |
830 base::AutoLock auto_lock(lock_); | 905 const CdmAttachedCB& cdm_attached_cb) { |
| 906 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 907 |
831 if (!renderer_) { | 908 if (!renderer_) { |
832 cdm_context_ = cdm_context; | 909 cdm_context_ = cdm_context; |
833 cdm_attached_cb.Run(true); | 910 cdm_attached_cb.Run(true); |
834 return; | 911 return; |
835 } | 912 } |
836 | 913 |
837 renderer_->SetCdm(cdm_context, | 914 renderer_->SetCdm(cdm_context, |
838 base::Bind(&PipelineImpl::OnCdmAttached, weak_this_, | 915 base::Bind(&RendererWrapper::OnCdmAttached, weak_this_, |
839 cdm_attached_cb, cdm_context)); | 916 cdm_attached_cb, cdm_context)); |
840 } | 917 } |
841 | 918 |
842 void PipelineImpl::OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, | 919 void PipelineImpl::RendererWrapper::OnCdmAttached( |
843 CdmContext* cdm_context, | 920 const CdmAttachedCB& cdm_attached_cb, |
844 bool success) { | 921 CdmContext* cdm_context, |
| 922 bool success) { |
845 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 923 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 924 |
846 if (success) | 925 if (success) |
847 cdm_context_ = cdm_context; | 926 cdm_context_ = cdm_context; |
848 cdm_attached_cb.Run(success); | 927 cdm_attached_cb.Run(success); |
849 } | 928 } |
850 | 929 |
851 void PipelineImpl::OnTextRendererEnded() { | 930 void PipelineImpl::RendererWrapper::OnTextRendererEnded() { |
852 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 931 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
853 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED)); | 932 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED)); |
854 | 933 |
855 if (state_ != kPlaying) | 934 if (state_ != kPlaying) |
856 return; | 935 return; |
857 | 936 |
858 DCHECK(!text_renderer_ended_); | 937 DCHECK(!text_renderer_ended_); |
859 text_renderer_ended_ = true; | 938 text_renderer_ended_ = true; |
860 | 939 |
861 RunEndedCallbackIfNeeded(); | 940 RunEndedCallbackIfNeeded(); |
862 } | 941 } |
863 | 942 |
864 void PipelineImpl::RunEndedCallbackIfNeeded() { | 943 void PipelineImpl::RendererWrapper::RunEndedCallbackIfNeeded() { |
865 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 944 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
866 | 945 |
867 if (renderer_ && !renderer_ended_) | 946 if (renderer_ && !renderer_ended_) |
868 return; | 947 return; |
869 | 948 |
870 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_) | 949 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_) |
871 return; | 950 return; |
872 | 951 |
873 DCHECK_EQ(status_, PIPELINE_OK); | 952 DCHECK_EQ(status_, PIPELINE_OK); |
874 main_task_runner_->PostTask( | 953 main_task_runner_->PostTask( |
875 FROM_HERE, base::Bind(&Pipeline::Client::OnEnded, weak_client_)); | 954 FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_)); |
876 } | 955 } |
877 | 956 |
878 std::unique_ptr<TextRenderer> PipelineImpl::CreateTextRenderer() { | 957 void PipelineImpl::RendererWrapper::AddTextStreamTask( |
| 958 DemuxerStream* text_stream, |
| 959 const TextTrackConfig& config) { |
879 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 960 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
880 | 961 |
881 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); | |
882 if (!cmd_line->HasSwitch(switches::kEnableInbandTextTracks)) | |
883 return nullptr; | |
884 | |
885 return base::WrapUnique(new media::TextRenderer( | |
886 media_task_runner_, | |
887 base::Bind(&PipelineImpl::OnAddTextTrack, weak_this_))); | |
888 } | |
889 | |
890 void PipelineImpl::AddTextStreamTask(DemuxerStream* text_stream, | |
891 const TextTrackConfig& config) { | |
892 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
893 // TODO(matthewjheaney): fix up text_ended_ when text stream | 962 // TODO(matthewjheaney): fix up text_ended_ when text stream |
894 // is added (http://crbug.com/321446). | 963 // is added (http://crbug.com/321446). |
895 if (text_renderer_) | 964 if (text_renderer_) |
896 text_renderer_->AddTextStream(text_stream, config); | 965 text_renderer_->AddTextStream(text_stream, config); |
897 } | 966 } |
898 | 967 |
899 void PipelineImpl::RemoveTextStreamTask(DemuxerStream* text_stream) { | 968 void PipelineImpl::RendererWrapper::RemoveTextStreamTask( |
| 969 DemuxerStream* text_stream) { |
900 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 970 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
901 | 971 |
902 if (text_renderer_) | 972 if (text_renderer_) |
903 text_renderer_->RemoveTextStream(text_stream); | 973 text_renderer_->RemoveTextStream(text_stream); |
904 } | 974 } |
905 | 975 |
906 void PipelineImpl::OnAddTextTrack(const TextTrackConfig& config, | 976 void PipelineImpl::RendererWrapper::InitializeDemuxer( |
907 const AddTextTrackDoneCB& done_cb) { | 977 const PipelineStatusCB& done_cb) { |
908 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
909 | |
910 main_task_runner_->PostTask( | |
911 FROM_HERE, base::Bind(&Pipeline::Client::OnAddTextTrack, weak_client_, | |
912 config, done_cb)); | |
913 } | |
914 | |
915 void PipelineImpl::InitializeDemuxer(const PipelineStatusCB& done_cb) { | |
916 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 978 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
917 | 979 |
918 demuxer_->Initialize(this, done_cb, !!text_renderer_); | 980 demuxer_->Initialize(this, done_cb, !!text_renderer_); |
919 } | 981 } |
920 | 982 |
921 void PipelineImpl::InitializeRenderer(const PipelineStatusCB& done_cb) { | 983 void PipelineImpl::RendererWrapper::InitializeRenderer( |
| 984 const PipelineStatusCB& done_cb) { |
922 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 985 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
923 | 986 |
924 if (!demuxer_->GetStream(DemuxerStream::AUDIO) && | 987 if (!demuxer_->GetStream(DemuxerStream::AUDIO) && |
925 !demuxer_->GetStream(DemuxerStream::VIDEO)) { | 988 !demuxer_->GetStream(DemuxerStream::VIDEO)) { |
926 { | 989 done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER); |
927 base::AutoLock auto_lock(lock_); | |
928 renderer_.reset(); | |
929 } | |
930 OnError(PIPELINE_ERROR_COULD_NOT_RENDER); | |
931 return; | 990 return; |
932 } | 991 } |
933 | 992 |
934 if (cdm_context_) | 993 if (cdm_context_) |
935 renderer_->SetCdm(cdm_context_, base::Bind(&IgnoreCdmAttached)); | 994 renderer_->SetCdm(cdm_context_, base::Bind(&IgnoreCdmAttached)); |
936 | 995 |
937 renderer_->Initialize(demuxer_, this, done_cb); | 996 renderer_->Initialize(demuxer_, this, done_cb); |
938 } | 997 } |
939 | 998 |
940 void PipelineImpl::ReportMetadata() { | 999 void PipelineImpl::RendererWrapper::ReportMetadata() { |
941 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 1000 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
942 | 1001 |
943 PipelineMetadata metadata; | 1002 PipelineMetadata metadata; |
944 metadata.timeline_offset = demuxer_->GetTimelineOffset(); | 1003 metadata.timeline_offset = demuxer_->GetTimelineOffset(); |
945 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO); | 1004 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO); |
946 if (stream) { | 1005 if (stream) { |
947 metadata.has_video = true; | 1006 metadata.has_video = true; |
948 metadata.natural_size = stream->video_decoder_config().natural_size(); | 1007 metadata.natural_size = stream->video_decoder_config().natural_size(); |
949 metadata.video_rotation = stream->video_rotation(); | 1008 metadata.video_rotation = stream->video_rotation(); |
950 } | 1009 } |
951 if (demuxer_->GetStream(DemuxerStream::AUDIO)) { | 1010 if (demuxer_->GetStream(DemuxerStream::AUDIO)) { |
952 metadata.has_audio = true; | 1011 metadata.has_audio = true; |
953 } | 1012 } |
954 | 1013 |
955 main_task_runner_->PostTask( | 1014 main_task_runner_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::OnMetadata, |
956 FROM_HERE, | 1015 weak_pipeline_, metadata)); |
957 base::Bind(&Pipeline::Client::OnMetadata, weak_client_, metadata)); | 1016 } |
| 1017 |
| 1018 void PipelineImpl::OnError(PipelineStatus error) { |
| 1019 DVLOG(2) << __FUNCTION__; |
| 1020 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1021 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; |
| 1022 |
| 1023 if (!IsRunning()) |
| 1024 return; |
| 1025 |
| 1026 // If the error happens during starting/seeking/suspending/resuming, |
| 1027 // report the error via the completion callback for those tasks. |
| 1028 // Else report error via the client interface. |
| 1029 if (!seek_cb_.is_null()) { |
| 1030 base::ResetAndReturn(&seek_cb_).Run(error); |
| 1031 } else if (!suspend_cb_.is_null()) { |
| 1032 base::ResetAndReturn(&suspend_cb_).Run(error); |
| 1033 } else { |
| 1034 DCHECK(client_); |
| 1035 client_->OnError(error); |
| 1036 } |
| 1037 |
| 1038 // Any kind of error stops the pipeline. |
| 1039 Stop(); |
| 1040 } |
| 1041 |
| 1042 void PipelineImpl::OnEnded() { |
| 1043 DVLOG(2) << __FUNCTION__; |
| 1044 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1045 |
| 1046 if (IsRunning()) { |
| 1047 DCHECK(client_); |
| 1048 client_->OnEnded(); |
| 1049 } |
| 1050 } |
| 1051 |
| 1052 void PipelineImpl::OnMetadata(PipelineMetadata metadata) { |
| 1053 DVLOG(2) << __FUNCTION__; |
| 1054 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1055 |
| 1056 if (IsRunning()) { |
| 1057 DCHECK(client_); |
| 1058 client_->OnMetadata(metadata); |
| 1059 } |
| 1060 } |
| 1061 |
| 1062 void PipelineImpl::OnBufferingStateChange(BufferingState state) { |
| 1063 DVLOG(2) << __FUNCTION__ << "(" << state << ")"; |
| 1064 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1065 |
| 1066 if (IsRunning()) { |
| 1067 DCHECK(client_); |
| 1068 client_->OnBufferingStateChange(state); |
| 1069 } |
| 1070 } |
| 1071 |
| 1072 void PipelineImpl::OnDurationChange(base::TimeDelta duration) { |
| 1073 DVLOG(2) << __FUNCTION__; |
| 1074 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1075 |
| 1076 duration_ = duration; |
| 1077 |
| 1078 if (IsRunning()) { |
| 1079 DCHECK(client_); |
| 1080 client_->OnDurationChange(); |
| 1081 } |
| 1082 } |
| 1083 |
| 1084 void PipelineImpl::OnAddTextTrack(const TextTrackConfig& config, |
| 1085 const AddTextTrackDoneCB& done_cb) { |
| 1086 DVLOG(2) << __FUNCTION__; |
| 1087 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1088 |
| 1089 if (IsRunning()) { |
| 1090 DCHECK(client_); |
| 1091 client_->OnAddTextTrack(config, done_cb); |
| 1092 } |
| 1093 } |
| 1094 |
| 1095 void PipelineImpl::OnWaitingForDecryptionKey() { |
| 1096 DVLOG(2) << __FUNCTION__; |
| 1097 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1098 |
| 1099 if (IsRunning()) { |
| 1100 DCHECK(client_); |
| 1101 client_->OnWaitingForDecryptionKey(); |
| 1102 } |
| 1103 } |
| 1104 |
| 1105 void PipelineImpl::OnVideoNaturalSizeChange(const gfx::Size& size) { |
| 1106 DVLOG(2) << __FUNCTION__; |
| 1107 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1108 |
| 1109 if (IsRunning()) { |
| 1110 DCHECK(client_); |
| 1111 client_->OnVideoNaturalSizeChange(size); |
| 1112 } |
| 1113 } |
| 1114 |
| 1115 void PipelineImpl::OnVideoOpacityChange(bool opaque) { |
| 1116 DVLOG(2) << __FUNCTION__; |
| 1117 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1118 |
| 1119 if (IsRunning()) { |
| 1120 DCHECK(client_); |
| 1121 client_->OnVideoOpacityChange(opaque); |
| 1122 } |
| 1123 } |
| 1124 |
| 1125 void PipelineImpl::OnBufferedTimeRangesChange( |
| 1126 const Ranges<base::TimeDelta>& ranges) { |
| 1127 DVLOG(3) << __FUNCTION__; |
| 1128 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1129 |
| 1130 buffered_time_ranges_ = ranges; |
| 1131 did_loading_progress_ = true; |
| 1132 } |
| 1133 |
| 1134 void PipelineImpl::OnStatisticsUpdate(const PipelineStatistics& stats) { |
| 1135 DVLOG(3) << __FUNCTION__; |
| 1136 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1137 |
| 1138 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; |
| 1139 statistics_.video_bytes_decoded += stats.video_bytes_decoded; |
| 1140 statistics_.video_frames_decoded += stats.video_frames_decoded; |
| 1141 statistics_.video_frames_dropped += stats.video_frames_dropped; |
| 1142 statistics_.audio_memory_usage += stats.audio_memory_usage; |
| 1143 statistics_.video_memory_usage += stats.video_memory_usage; |
| 1144 } |
| 1145 |
| 1146 void PipelineImpl::OnSeekDone(base::TimeDelta start_time) { |
| 1147 DVLOG(3) << __FUNCTION__ << "(" << start_time.InMicroseconds() << ")"; |
| 1148 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1149 |
| 1150 if (IsRunning()) { |
| 1151 DCHECK(!seek_cb_.is_null()); |
| 1152 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); |
| 1153 } |
| 1154 } |
| 1155 |
| 1156 void PipelineImpl::OnSuspendDone(base::TimeDelta suspend_time) { |
| 1157 DVLOG(3) << __FUNCTION__ << "(" << suspend_time.InMicroseconds() << ")"; |
| 1158 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1159 |
| 1160 // Reset audio-video memory usage since renderer has been destroyed. |
| 1161 statistics_.audio_memory_usage = 0; |
| 1162 statistics_.video_memory_usage = 0; |
| 1163 |
| 1164 if (IsRunning()) { |
| 1165 DCHECK(!suspend_cb_.is_null()); |
| 1166 base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK); |
| 1167 } |
| 1168 } |
| 1169 |
| 1170 PipelineImpl::RendererWrapper::RendererWrapper( |
| 1171 base::WeakPtr<PipelineImpl> weak_pipeline, |
| 1172 scoped_refptr<base::SingleThreadTaskRunner> media_task_runner, |
| 1173 scoped_refptr<MediaLog> media_log) |
| 1174 : weak_pipeline_(weak_pipeline), |
| 1175 media_task_runner_(std::move(media_task_runner)), |
| 1176 main_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| 1177 media_log_(std::move(media_log)), |
| 1178 demuxer_(nullptr), |
| 1179 playback_rate_(kDefaultPlaybackRate), |
| 1180 volume_(kDefaultVolume), |
| 1181 cdm_context_(nullptr), |
| 1182 state_(kCreated), |
| 1183 status_(PIPELINE_OK), |
| 1184 suspend_timestamp_(kNoTimestamp()), |
| 1185 renderer_ended_(false), |
| 1186 text_renderer_ended_(false), |
| 1187 weak_factory_(this) { |
| 1188 weak_this_ = weak_factory_.GetWeakPtr(); |
| 1189 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); |
| 1190 } |
| 1191 |
| 1192 PipelineImpl::RendererWrapper::~RendererWrapper() { |
| 1193 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 1194 DCHECK(state_ == kCreated || state_ == kStopped); |
| 1195 } |
| 1196 |
| 1197 base::TimeDelta PipelineImpl::RendererWrapper::GetMediaTime() { |
| 1198 // This is the only member function that gets called on the main thread. |
| 1199 // TODO(alokp): Enforce that Renderer is only called on a single thread, |
| 1200 // even for accessing media time http://crbug.com/370634. |
| 1201 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 1202 |
| 1203 base::AutoLock auto_lock(media_time_lock_); |
| 1204 if (suspend_timestamp_ != kNoTimestamp()) |
| 1205 return suspend_timestamp_; |
| 1206 return renderer_ ? renderer_->GetMediaTime() : base::TimeDelta(); |
958 } | 1207 } |
959 | 1208 |
960 } // namespace media | 1209 } // namespace media |
OLD | NEW |