1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/base/pipeline_impl.h"
11 #include "base/command_line.h"
12 #include "base/functional/bind.h"
13 #include "base/functional/callback.h"
14 #include "base/functional/callback_helpers.h"
15 #include "base/location.h"
16 #include "base/memory/raw_ptr.h"
17 #include "base/metrics/histogram_functions.h"
18 #include "base/metrics/histogram_macros.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/synchronization/lock.h"
21 #include "base/synchronization/waitable_event.h"
22 #include "base/task/bind_post_task.h"
23 #include "base/task/sequenced_task_runner.h"
24 #include "base/task/single_thread_task_runner.h"
25 #include "build/build_config.h"
26 #include "media/base/callback_timeout_helpers.h"
27 #include "media/base/cdm_context.h"
28 #include "media/base/decoder.h"
29 #include "media/base/demuxer.h"
30 #include "media/base/media_log.h"
31 #include "media/base/media_switches.h"
32 #include "media/base/renderer.h"
33 #include "media/base/renderer_client.h"
34 #include "media/base/serial_runner.h"
35 #include "media/base/timestamp_constants.h"
36 #include "media/base/video_decoder_config.h"
39 #include "media/base/win/mf_feature_checks.h"
40 #endif // BUILDFLAG(IS_WIN)
42 #if BUILDFLAG(IS_TIZEN_TV)
43 #include "media/filters/decrypting_media_resource.h"
46 static const double kDefaultPlaybackRate = 0.0;
47 static const float kDefaultVolume = 1.0f;
53 gfx::Size GetRotatedVideoSize(VideoRotation rotation, gfx::Size natural_size) {
54 if (rotation == VIDEO_ROTATION_90 || rotation == VIDEO_ROTATION_270)
55 return gfx::Size(natural_size.height(), natural_size.width());
59 void OnCallbackTimeout(const std::string& uma_name,
60 bool called_on_destruction) {
61 DVLOG(1) << "Callback Timeout: " << uma_name
62 << ", called_on_destruction=" << called_on_destruction;
63 base::UmaHistogramEnumeration(
64 uma_name, called_on_destruction
65 ? CallbackTimeoutStatus::kDestructedBeforeTimeout
66 : CallbackTimeoutStatus::kTimeout);
71 // A wrapper of Renderer that runs on the |media_task_runner|.
72 // |default_renderer| in Start() and Resume() helps avoid a round trip to the
73 // render main task runner for Renderer creation in most cases which could add
74 // latency to start-to-play time.
75 class PipelineImpl::RendererWrapper final : public DemuxerHost,
76 public RendererClient {
78 RendererWrapper(scoped_refptr<base::SequencedTaskRunner> media_task_runner,
79 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
80 #if defined(TIZEN_VIDEO_HOLE)
85 RendererWrapper(const RendererWrapper&) = delete;
86 RendererWrapper& operator=(const RendererWrapper&) = delete;
88 ~RendererWrapper() final;
90 void Start(StartType start_type,
92 std::unique_ptr<Renderer> default_renderer,
93 base::WeakPtr<PipelineImpl> weak_pipeline);
95 void Seek(base::TimeDelta time);
97 void Resume(std::unique_ptr<Renderer> default_renderer, base::TimeDelta time);
98 void SetPlaybackRate(double playback_rate);
99 void SetVolume(float volume);
100 void SetLatencyHint(absl::optional<base::TimeDelta> latency_hint);
101 void SetPreservesPitch(bool preserves_pitch);
102 void SetWasPlayedWithUserActivation(bool was_played_with_user_activation);
103 base::TimeDelta GetMediaTime() const;
104 Ranges<base::TimeDelta> GetBufferedTimeRanges() const;
105 bool DidLoadingProgress();
106 PipelineStatistics GetStatistics() const;
107 void SetCdm(CdmContext* cdm_context, CdmAttachedCB cdm_attached_cb);
109 #if defined(TIZEN_MULTIMEDIA)
110 void ToggleFullscreenMode(bool is_fullscreen, ToggledFullscreenCB cb);
111 bool IsRendererSuspended() { return renderer_suspended_; }
114 #if defined(TIZEN_VIDEO_HOLE)
115 void SetMediaGeometry(const gfx::RectF rect_f);
117 #if BUILDFLAG(IS_TIZEN_TV)
118 void SetContentMimeType(const std::string& mime_type);
119 void SetParentalRatingResult(bool is_pass);
122 // |enabled_track_ids| contains track ids of enabled audio tracks.
123 void OnEnabledAudioTracksChanged(
124 const std::vector<MediaTrack::Id>& enabled_track_ids,
125 base::OnceClosure change_completed_cb);
127 // |selected_track_id| is either empty, which means no video track is
128 // selected, or contains the selected video track id.
129 void OnSelectedVideoTrackChanged(
130 absl::optional<MediaTrack::Id> selected_track_id,
131 base::OnceClosure change_completed_cb);
133 void OnExternalVideoFrameRequest();
136 // Contains state shared between main and media thread. On the media thread
137 // each member can be read without locking, but writing requires locking. On
138 // the main thread reading requires a lock and writing is prohibited.
140 // This struct should only contain state that is not immediately needed by
141 // PipelineClient and can be cached on the media thread until queried.
142 // Alternatively we could cache it on the main thread by posting the
143 // notification to the main thread. But some of the state change notifications
144 // (OnStatisticsUpdate and OnBufferedTimeRangesChanged) arrive much more
145 // frequently than needed. Posting all those notifications to the main thread
146 // causes performance issues: crbug.com/619975.
148 // TODO(scherkus): Enforce that Renderer is only called on a single thread,
149 // even for accessing media time http://crbug.com/370634
151 // Note: Renderer implementations must support GetMediaTime() being called
152 // on both the main and media threads. RendererWrapper::GetMediaTime() calls
153 // it from the main thread (locked).
154 std::unique_ptr<Renderer> renderer;
156 // True when OnBufferedTimeRangesChanged() has been called more recently
157 // than DidLoadingProgress().
158 bool did_loading_progress = false;
160 // Amount of available buffered data as reported by Demuxer.
161 Ranges<base::TimeDelta> buffered_time_ranges;
163 // Accumulated statistics reported by the renderer.
164 PipelineStatistics statistics;
166 // The media timestamp to return while the pipeline is suspended.
167 // Otherwise set to kNoTimestamp.
168 base::TimeDelta suspend_timestamp = kNoTimestamp;
171 base::TimeDelta GetCurrentTimestamp();
173 void OnDemuxerCompletedTrackChange(
174 base::OnceClosure change_completed_cb,
175 DemuxerStream::Type stream_type,
176 const std::vector<DemuxerStream*>& streams);
178 // DemuxerHost implementaion.
179 void OnBufferedTimeRangesChanged(const Ranges<base::TimeDelta>& ranges) final;
180 void SetDuration(base::TimeDelta duration) final;
181 void OnDemuxerError(PipelineStatus error) final;
183 // RendererClient implementation.
184 void OnError(PipelineStatus error) final;
185 void OnFallback(PipelineStatus status) final;
186 void OnEnded() final;
187 void OnStatisticsUpdate(const PipelineStatistics& stats) final;
188 void OnBufferingStateChange(BufferingState state,
189 BufferingStateChangeReason reason) final;
190 void OnWaiting(WaitingReason reason) final;
191 void OnAudioConfigChange(const AudioDecoderConfig& config) final;
192 void OnVideoConfigChange(const VideoDecoderConfig& config) final;
193 void OnVideoNaturalSizeChange(const gfx::Size& size) final;
194 void OnVideoOpacityChange(bool opaque) final;
195 void OnVideoFrameRateChange(absl::optional<int> fps) final;
196 #if defined(TIZEN_MULTIMEDIA)
197 void OnRequestSuspend(bool resource_conflict) final;
198 void OnRequestSeek(base::TimeDelta time) final;
199 void OnSeekableTimeChange(base::TimeDelta min_time,
200 base::TimeDelta max_time,
202 void OnLivePlaybackComplete() final;
205 // Common handlers for notifications from renderers and demuxer.
206 void OnPipelineError(PipelineStatus error);
207 void OnCdmAttached(CdmAttachedCB cdm_attached_cb,
208 CdmContext* cdm_context,
210 void CheckPlaybackEnded();
212 // State transition tasks.
213 void SetState(State next_state);
214 void CompleteSeek(base::TimeDelta seek_time, PipelineStatus status);
215 void CompleteSuspend(PipelineStatus status);
216 void InitializeDemuxer(PipelineStatusCallback done_cb);
217 void CreateRenderer(PipelineStatusCallback done_cb);
218 void OnRendererCreated(PipelineStatusCallback done_cb,
219 std::unique_ptr<Renderer> renderer);
220 void InitializeRenderer(PipelineStatusCallback done_cb);
221 void DestroyRenderer();
222 void ReportMetadata(StartType start_type);
224 #if BUILDFLAG(IS_TIZEN_TV)
225 // Create decrypting media resource
226 void CreateAndInitializeDecryptingMediaResource();
227 void OnDecryptInitDone(bool success);
230 // Returns whether there's any encrypted stream in the demuxer.
231 bool HasEncryptedStream();
233 // Uses |default_renderer_| as the Renderer or asynchronously creates a new
234 // one by calling back to PipelineImpl. Fires |done_cb| with the result.
235 void CreateRendererInternal(PipelineStatusCallback done_cb);
237 const scoped_refptr<base::SequencedTaskRunner> media_task_runner_;
238 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
239 const raw_ptr<MediaLog, AcrossTasksDanglingUntriaged> media_log_;
241 // A weak pointer to PipelineImpl. Must only use on the main task runner.
242 base::WeakPtr<PipelineImpl> weak_pipeline_;
244 raw_ptr<Demuxer> demuxer_;
246 // Optional default renderer to be used during Start() and Resume(). If not
247 // available, or if a different Renderer is needed,
248 // PipelineImpl::AsyncCreateRenderer() will be called to create a new one.
249 std::unique_ptr<Renderer> default_renderer_;
251 double playback_rate_;
253 absl::optional<base::TimeDelta> latency_hint_;
254 raw_ptr<CdmContext> cdm_context_;
256 // By default, apply pitch adjustments.
257 bool preserves_pitch_ = true;
259 bool was_played_with_user_activation_ = false;
261 // Lock used to serialize |shared_state_|.
262 // TODO(crbug.com/893739): Add GUARDED_BY annotations.
263 mutable base::Lock shared_state_lock_;
265 // State shared between main and media thread.
266 SharedState shared_state_;
268 // Current state of the pipeline.
271 // Status of the pipeline. Initialized to PIPELINE_OK which indicates that
272 // the pipeline is operating correctly. Any other value indicates that the
273 // pipeline is stopped or is stopping. Clients can call the Stop() method to
274 // reset the pipeline state, and restore this to PIPELINE_OK.
275 PipelineStatus status_;
277 #if defined(TIZEN_MULTIMEDIA)
278 bool renderer_suspended_ = false;
279 base::DelayedTaskHandle request_suspend_task_handle_;
282 #if defined(TIZEN_VIDEO_HOLE)
286 #if BUILDFLAG(IS_TIZEN_TV)
287 std::string mime_type_;
288 std::unique_ptr<DecryptingMediaResource> decrypting_media_resource_{nullptr};
291 // Whether we've received the audio/video ended events.
292 bool renderer_ended_;
294 // Series of tasks to Start(), Seek(), and Resume().
295 std::unique_ptr<SerialRunner> pending_callbacks_;
297 // Callback to store the |done_cb| when CreateRenderer() needs to wait for a
298 // CDM to be set. Should only be set in kStarting or kResuming states.
299 PipelineStatusCallback create_renderer_done_cb_;
301 // Called from non-media threads when an error occurs.
302 PipelineStatusCB error_cb_;
304 base::WeakPtrFactory<RendererWrapper> weak_factory_{this};
307 PipelineImpl::RendererWrapper::RendererWrapper(
308 scoped_refptr<base::SequencedTaskRunner> media_task_runner,
309 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
310 #if defined(TIZEN_VIDEO_HOLE)
314 : media_task_runner_(std::move(media_task_runner)),
315 main_task_runner_(std::move(main_task_runner)),
316 media_log_(media_log),
318 playback_rate_(kDefaultPlaybackRate),
319 volume_(kDefaultVolume),
320 cdm_context_(nullptr),
322 status_(PIPELINE_OK),
323 #if defined(TIZEN_VIDEO_HOLE)
324 is_video_hole_(is_video_hole),
326 renderer_ended_(false) {}
328 PipelineImpl::RendererWrapper::~RendererWrapper() {
329 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
330 DCHECK(state_ == kCreated || state_ == kStopped);
333 // Note that the usage of base::Unretained() with the renderers is considered
334 // safe as they are owned by |pending_callbacks_| and share the same lifetime.
336 // That being said, deleting the renderers while keeping |pending_callbacks_|
337 // running on the media thread would result in crashes.
339 void PipelineImpl::RendererWrapper::Start(
340 StartType start_type,
342 std::unique_ptr<Renderer> default_renderer,
343 base::WeakPtr<PipelineImpl> weak_pipeline) {
344 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
345 DCHECK(state_ == kCreated || state_ == kStopped)
346 << "Received start in unexpected state: " << state_;
348 DCHECK(!renderer_ended_);
352 default_renderer_ = std::move(default_renderer);
353 weak_pipeline_ = weak_pipeline;
355 // Setup |error_cb_| on the media thread.
356 error_cb_ = base::BindRepeating(&RendererWrapper::OnPipelineError,
357 weak_factory_.GetWeakPtr());
359 // Queue asynchronous actions required to start.
360 DCHECK(!pending_callbacks_);
361 SerialRunner::Queue fns;
363 // Initialize demuxer.
364 fns.Push(base::BindOnce(&RendererWrapper::InitializeDemuxer,
365 weak_factory_.GetWeakPtr()));
367 // Once the demuxer is initialized successfully, media metadata must be
368 // available - report the metadata to client. If starting without a renderer
369 // we'll complete initialization at this point.
370 fns.Push(base::BindOnce(&RendererWrapper::ReportMetadata,
371 weak_factory_.GetWeakPtr(), start_type));
374 fns.Push(base::BindOnce(&RendererWrapper::CreateRenderer,
375 weak_factory_.GetWeakPtr()));
377 // Initialize renderer.
378 fns.Push(base::BindOnce(&RendererWrapper::InitializeRenderer,
379 weak_factory_.GetWeakPtr()));
382 pending_callbacks_ = SerialRunner::Run(
384 base::BindOnce(&RendererWrapper::CompleteSeek, weak_factory_.GetWeakPtr(),
388 void PipelineImpl::RendererWrapper::Stop() {
389 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
390 DCHECK(state_ != kStopping && state_ != kStopped);
394 if (shared_state_.statistics.video_frames_decoded > 0) {
395 UMA_HISTOGRAM_COUNTS_1M("Media.DroppedFrameCount",
396 shared_state_.statistics.video_frames_dropped);
399 // If we stop during starting/seeking/suspending/resuming we don't want to
400 // leave outstanding callbacks around. The callbacks also do not get run if
401 // the pipeline is stopped before it had a chance to complete outstanding
403 pending_callbacks_.reset();
404 weak_factory_.InvalidateWeakPtrs();
415 // Reset the status. Otherwise, if we encountered an error, new errors will
416 // never be propagated. See https://crbug.com/812465.
417 status_ = PIPELINE_OK;
420 void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) {
421 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
423 // Suppress seeking if we're not fully started.
424 if (state_ != kPlaying) {
425 DCHECK(state_ == kStopping || state_ == kStopped)
426 << "Receive seek in unexpected state: " << state_;
427 OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
431 base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime());
434 renderer_ended_ = false;
436 // Queue asynchronous actions required to start.
437 DCHECK(!pending_callbacks_);
438 SerialRunner::Queue bound_fns;
440 // Abort any reads the renderer may be blocked on.
441 demuxer_->AbortPendingReads();
444 DCHECK(shared_state_.renderer);
445 bound_fns.Push(base::BindOnce(
446 &Renderer::Flush, base::Unretained(shared_state_.renderer.get())));
449 bound_fns.Push(base::BindOnce(&Demuxer::Seek, base::Unretained(demuxer_),
452 #if defined(TIZEN_MULTIMEDIA)
453 // Calling seek is completely redundant, because upstream StartPlayingFrom()
454 // will do the same thing any moment now. Skip this call for EMSS && URL
455 // demuxer, to avoid unnecessary slowdown of the pipeline
456 bool skip_unnecessary_double_seek =
457 demuxer_->GetType() == MediaResource::Type::KUrl;
458 if (!skip_unnecessary_double_seek) {
459 bound_fns.Push(base::BindOnce(
460 &Renderer::Seek, base::Unretained(shared_state_.renderer.get()),
463 #endif // defined(TIZEN_MULTIMEDIA)
466 pending_callbacks_ = SerialRunner::Run(
467 std::move(bound_fns),
468 base::BindOnce(&RendererWrapper::CompleteSeek, weak_factory_.GetWeakPtr(),
472 void PipelineImpl::RendererWrapper::Suspend() {
473 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
475 // Suppress suspending if we're not playing.
476 if (state_ != kPlaying) {
477 DCHECK(state_ == kStopping || state_ == kStopped)
478 << "Receive suspend in unexpected state: " << state_;
479 OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
482 DCHECK(!pending_callbacks_.get());
484 SetState(kSuspending);
486 #if defined(TIZEN_MULTIMEDIA)
487 if (request_suspend_task_handle_.IsValid()) {
488 LOG(INFO) << " Cancel posted request suspend task if pended.";
489 request_suspend_task_handle_.CancelTask();
492 shared_state_.renderer->Suspend();
495 // Freeze playback and record the media time before destroying the renderer.
496 shared_state_.renderer->SetPlaybackRate(0.0);
498 base::AutoLock auto_lock(shared_state_lock_);
499 DCHECK(shared_state_.renderer);
500 shared_state_.suspend_timestamp = shared_state_.renderer->GetMediaTime();
501 DCHECK(shared_state_.suspend_timestamp != kNoTimestamp);
504 // Queue the asynchronous actions required to stop playback.
505 SerialRunner::Queue fns;
507 // No need to flush the renderer since it's going to be destroyed.
508 pending_callbacks_ = SerialRunner::Run(
509 std::move(fns), base::BindOnce(&RendererWrapper::CompleteSuspend,
510 weak_factory_.GetWeakPtr()));
513 void PipelineImpl::RendererWrapper::Resume(
514 std::unique_ptr<Renderer> default_renderer,
515 base::TimeDelta timestamp) {
516 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
518 // Suppress resuming if we're not suspended.
519 if (state_ != kSuspended) {
520 DCHECK(state_ == kStopping || state_ == kStopped)
521 << "Receive resume in unexpected state: " << state_;
522 OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
525 DCHECK(!pending_callbacks_.get());
527 if (!default_renderer) {
528 OnPipelineError({PIPELINE_ERROR_INITIALIZATION_FAILED,
529 "Media Renderer creation failed during resume!"});
536 base::AutoLock auto_lock(shared_state_lock_);
537 DCHECK(!shared_state_.renderer);
540 #if defined(TIZEN_MULTIMEDIA)
541 if (request_suspend_task_handle_.IsValid()) {
542 LOG(INFO) << " Cancel posted request suspend task if pended.";
543 request_suspend_task_handle_.CancelTask();
546 renderer_suspended_ = false;
548 bool default_renderer_created = default_renderer.get() ? true : false;
549 if (default_renderer_created)
551 default_renderer_ = std::move(default_renderer);
552 renderer_ended_ = false;
553 base::TimeDelta start_timestamp =
554 std::max(timestamp, demuxer_->GetStartTime());
556 // Queue the asynchronous actions required to start playback.
557 SerialRunner::Queue fns;
559 fns.Push(base::BindOnce(&Demuxer::Seek, base::Unretained(demuxer_),
562 #if defined(TIZEN_MULTIMEDIA)
563 if (default_renderer_created)
566 fns.Push(base::BindOnce(&RendererWrapper::CreateRenderer,
567 weak_factory_.GetWeakPtr()));
569 fns.Push(base::BindOnce(&RendererWrapper::InitializeRenderer,
570 weak_factory_.GetWeakPtr()));
573 pending_callbacks_ = SerialRunner::Run(
575 base::BindOnce(&RendererWrapper::CompleteSeek, weak_factory_.GetWeakPtr(),
579 void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) {
580 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
582 playback_rate_ = playback_rate;
583 if (state_ == kPlaying)
584 shared_state_.renderer->SetPlaybackRate(playback_rate_);
586 if (state_ != kCreated && state_ != kStopping && state_ != kStopped) {
588 demuxer_->SetPlaybackRate(playback_rate);
592 void PipelineImpl::RendererWrapper::SetVolume(float volume) {
593 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
596 if (shared_state_.renderer)
597 shared_state_.renderer->SetVolume(volume_);
600 void PipelineImpl::RendererWrapper::SetLatencyHint(
601 absl::optional<base::TimeDelta> latency_hint) {
602 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
604 if (latency_hint_ == latency_hint)
607 latency_hint_ = latency_hint;
608 if (shared_state_.renderer)
609 shared_state_.renderer->SetLatencyHint(latency_hint_);
612 void PipelineImpl::RendererWrapper::SetPreservesPitch(bool preserves_pitch) {
613 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
615 if (preserves_pitch_ == preserves_pitch)
618 preserves_pitch_ = preserves_pitch;
619 if (shared_state_.renderer)
620 shared_state_.renderer->SetPreservesPitch(preserves_pitch_);
623 void PipelineImpl::RendererWrapper::SetWasPlayedWithUserActivation(
624 bool was_played_with_user_activation) {
625 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
627 was_played_with_user_activation_ = was_played_with_user_activation;
628 if (shared_state_.renderer) {
629 shared_state_.renderer->SetWasPlayedWithUserActivation(
630 was_played_with_user_activation_);
634 base::TimeDelta PipelineImpl::RendererWrapper::GetMediaTime() const {
635 DCHECK(main_task_runner_->BelongsToCurrentThread());
637 base::AutoLock auto_lock(shared_state_lock_);
638 if (shared_state_.suspend_timestamp != kNoTimestamp)
639 return shared_state_.suspend_timestamp;
640 return shared_state_.renderer ? shared_state_.renderer->GetMediaTime()
644 Ranges<base::TimeDelta> PipelineImpl::RendererWrapper::GetBufferedTimeRanges()
646 DCHECK(main_task_runner_->BelongsToCurrentThread());
648 base::AutoLock auto_lock(shared_state_lock_);
649 return shared_state_.buffered_time_ranges;
652 bool PipelineImpl::RendererWrapper::DidLoadingProgress() {
653 DCHECK(main_task_runner_->BelongsToCurrentThread());
655 base::AutoLock auto_lock(shared_state_lock_);
656 bool did_progress = shared_state_.did_loading_progress;
657 shared_state_.did_loading_progress = false;
661 PipelineStatistics PipelineImpl::RendererWrapper::GetStatistics() const {
662 DCHECK(main_task_runner_->BelongsToCurrentThread());
664 base::AutoLock auto_lock(shared_state_lock_);
665 return shared_state_.statistics;
668 void PipelineImpl::RendererWrapper::SetCdm(CdmContext* cdm_context,
669 CdmAttachedCB cdm_attached_cb) {
670 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
673 // If there's already a renderer, set the CDM on the renderer directly.
674 if (shared_state_.renderer) {
675 shared_state_.renderer->SetCdm(
676 cdm_context, base::BindOnce(&RendererWrapper::OnCdmAttached,
677 weak_factory_.GetWeakPtr(),
678 std::move(cdm_attached_cb), cdm_context));
682 // Otherwise, wait for the Renderer to be created and the CDM will be set
683 // in InitializeRenderer().
684 cdm_context_ = cdm_context;
685 std::move(cdm_attached_cb).Run(true);
687 // Continue Renderer creation if it's waiting for the CDM to be set.
688 if (create_renderer_done_cb_)
689 CreateRendererInternal(std::move(create_renderer_done_cb_));
692 #if defined(TIZEN_VIDEO_HOLE)
693 void PipelineImpl::RendererWrapper::SetMediaGeometry(const gfx::RectF rect_f) {
694 LOG(INFO) << __func__;
695 if (shared_state_.renderer)
696 shared_state_.renderer->SetMediaGeometry(rect_f);
700 #if BUILDFLAG(IS_TIZEN_TV)
701 void PipelineImpl::RendererWrapper::SetContentMimeType(
702 const std::string& mime_type) {
703 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
704 mime_type_ = mime_type;
705 if (shared_state_.renderer)
706 shared_state_.renderer->SetContentMimeType(mime_type_);
709 void PipelineImpl::RendererWrapper::SetParentalRatingResult(bool is_pass) {
710 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
712 if (shared_state_.renderer)
713 shared_state_.renderer->SetParentalRatingResult(is_pass);
715 LOG(ERROR) << "renderer is null";
719 #if BUILDFLAG(IS_TIZEN_TV)
720 void PipelineImpl::RendererWrapper::OnDecryptInitDone(bool success) {
721 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
722 LOG(INFO) << "OnDecryptInitDone,this:" << (void*)this;
725 void PipelineImpl::RendererWrapper::
726 CreateAndInitializeDecryptingMediaResource() {
727 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
728 DCHECK(cdm_context_);
729 decrypting_media_resource_ = std::make_unique<DecryptingMediaResource>(
730 demuxer_, cdm_context_, media_log_, media_task_runner_);
732 LOG(INFO) << "(" << static_cast<void*>(this) << __func__
733 << ") decrypting_media_resource_:"
734 << decrypting_media_resource_.get();
736 decrypting_media_resource_->Initialize(
737 base::BindOnce(&RendererWrapper::OnDecryptInitDone,
738 weak_factory_.GetWeakPtr()),
739 base::BindRepeating(&RendererWrapper::OnWaiting,
740 weak_factory_.GetWeakPtr()));
744 void PipelineImpl::RendererWrapper::CreateRendererInternal(
745 PipelineStatusCallback done_cb) {
746 DVLOG(1) << __func__;
748 DCHECK(state_ == kStarting || state_ == kResuming);
749 DCHECK(cdm_context_ || !HasEncryptedStream())
750 << "CDM should be available now if has encrypted stream";
752 absl::optional<RendererType> renderer_type;
754 #if BUILDFLAG(IS_WIN)
756 if (cdm_context_->RequiresMediaFoundationRenderer()) {
757 renderer_type = RendererType::kMediaFoundation;
758 } else if (media::SupportMediaFoundationClearPlayback()) {
759 // When MediaFoundation for Clear is enabled, the base renderer
760 // type is set to MediaFoundation. In order to ensure DRM systems
761 // built on non-Media Foundation pipelines continue to work we
762 // explicitly set renderer_type to Default.
763 renderer_type = RendererType::kRendererImpl;
766 #endif // BUILDFLAG(IS_WIN)
768 // TODO(xhwang): During Resume(), the |default_renderer_| might already match
769 // the |renderer_type|, in which case we shouldn't need to create a new one.
770 if (!default_renderer_ || renderer_type) {
771 // Create the Renderer asynchronously on the main task runner. Use
772 // base::BindPostTaskToCurrentDefault to call OnRendererCreated() on the
773 // media task runner.
774 auto renderer_created_cb = base::BindPostTaskToCurrentDefault(
775 base::BindOnce(&RendererWrapper::OnRendererCreated,
776 weak_factory_.GetWeakPtr(), std::move(done_cb)));
777 main_task_runner_->PostTask(
779 base::BindOnce(&PipelineImpl::AsyncCreateRenderer, weak_pipeline_,
780 renderer_type, std::move(renderer_created_cb)));
784 // Just use the default one.
785 OnRendererCreated(std::move(done_cb), std::move(default_renderer_));
788 void PipelineImpl::RendererWrapper::OnBufferedTimeRangesChanged(
789 const Ranges<base::TimeDelta>& ranges) {
790 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer
791 // implementations call DemuxerHost on the media thread.
792 base::AutoLock auto_lock(shared_state_lock_);
793 shared_state_.did_loading_progress = true;
794 shared_state_.buffered_time_ranges = ranges;
797 void PipelineImpl::RendererWrapper::SetDuration(base::TimeDelta duration) {
798 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer
799 // implementations call DemuxerHost on the media thread.
800 media_log_->AddEvent<MediaLogEvent::kDurationChanged>(duration);
801 main_task_runner_->PostTask(
802 FROM_HERE, base::BindOnce(&PipelineImpl::OnDurationChange, weak_pipeline_,
806 void PipelineImpl::RendererWrapper::OnDemuxerError(PipelineStatus error) {
807 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer
808 // implementations call DemuxerHost on the media thread.
810 media_task_runner_->PostTask(FROM_HERE, base::BindOnce(error_cb_, error));
813 void PipelineImpl::RendererWrapper::OnError(PipelineStatus error) {
814 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
816 media_task_runner_->PostTask(FROM_HERE, base::BindOnce(error_cb_, error));
819 void PipelineImpl::RendererWrapper::OnFallback(PipelineStatus fallback) {
820 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
821 main_task_runner_->PostTask(
822 FROM_HERE, base::BindOnce(&PipelineImpl::OnFallback, weak_pipeline_,
823 std::move(fallback).AddHere()));
826 void PipelineImpl::RendererWrapper::OnEnded() {
827 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
828 media_log_->AddEvent<MediaLogEvent::kEnded>();
830 if (state_ != kPlaying)
833 DCHECK(!renderer_ended_);
834 renderer_ended_ = true;
835 CheckPlaybackEnded();
838 // TODO(crbug/817089): Combine this functionality into renderer->GetMediaTime().
839 base::TimeDelta PipelineImpl::RendererWrapper::GetCurrentTimestamp() {
840 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
842 DCHECK(shared_state_.renderer || state_ != kPlaying);
844 return state_ == kPlaying ? shared_state_.renderer->GetMediaTime()
845 : demuxer_->GetStartTime();
848 void PipelineImpl::OnEnabledAudioTracksChanged(
849 const std::vector<MediaTrack::Id>& enabled_track_ids,
850 base::OnceClosure change_completed_cb) {
851 DCHECK(thread_checker_.CalledOnValidThread());
852 media_task_runner_->PostTask(
855 &RendererWrapper::OnEnabledAudioTracksChanged,
856 base::Unretained(renderer_wrapper_.get()), enabled_track_ids,
857 base::BindPostTaskToCurrentDefault(std::move(change_completed_cb))));
860 void PipelineImpl::RendererWrapper::OnEnabledAudioTracksChanged(
861 const std::vector<MediaTrack::Id>& enabled_track_ids,
862 base::OnceClosure change_completed_cb) {
863 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
865 // If the pipeline has been created, but not started yet, we may still receive
866 // track notifications from blink level (e.g. when video track gets deselected
867 // due to player/pipeline belonging to a background tab). We can safely ignore
868 // these, since WebMediaPlayerImpl will ensure that demuxer stream / track
869 // status is in sync with blink after pipeline is started.
870 if (state_ == kCreated) {
872 std::move(change_completed_cb).Run();
876 // Track status notifications might be delivered asynchronously. If we receive
877 // a notification when pipeline is stopped/shut down, it's safe to ignore it.
878 if (state_ == kStopping || state_ == kStopped) {
879 std::move(change_completed_cb).Run();
882 demuxer_->OnEnabledAudioTracksChanged(
883 enabled_track_ids, GetCurrentTimestamp(),
884 base::BindOnce(&RendererWrapper::OnDemuxerCompletedTrackChange,
885 weak_factory_.GetWeakPtr(),
886 std::move(change_completed_cb)));
889 void PipelineImpl::OnSelectedVideoTrackChanged(
890 absl::optional<MediaTrack::Id> selected_track_id,
891 base::OnceClosure change_completed_cb) {
892 DCHECK(thread_checker_.CalledOnValidThread());
893 media_task_runner_->PostTask(
896 &RendererWrapper::OnSelectedVideoTrackChanged,
897 base::Unretained(renderer_wrapper_.get()), selected_track_id,
898 base::BindPostTaskToCurrentDefault(std::move(change_completed_cb))));
901 void PipelineImpl::RendererWrapper::OnSelectedVideoTrackChanged(
902 absl::optional<MediaTrack::Id> selected_track_id,
903 base::OnceClosure change_completed_cb) {
904 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
906 // See RenderWrapper::OnEnabledAudioTracksChanged.
907 if (state_ == kCreated) {
909 std::move(change_completed_cb).Run();
913 if (state_ == kStopping || state_ == kStopped) {
914 std::move(change_completed_cb).Run();
918 std::vector<MediaTrack::Id> tracks;
919 if (selected_track_id)
920 tracks.push_back(*selected_track_id);
922 demuxer_->OnSelectedVideoTrackChanged(
923 tracks, GetCurrentTimestamp(),
924 base::BindOnce(&RendererWrapper::OnDemuxerCompletedTrackChange,
925 weak_factory_.GetWeakPtr(),
926 std::move(change_completed_cb)));
929 void PipelineImpl::OnExternalVideoFrameRequest() {
930 // This function is currently a no-op unless we're on a Windows build with
931 // Media Foundation for Clear running.
932 DCHECK(thread_checker_.CalledOnValidThread());
933 if (!external_video_frame_request_signaled_) {
934 external_video_frame_request_signaled_ = true;
935 media_task_runner_->PostTask(
936 FROM_HERE, base::BindOnce(&RendererWrapper::OnExternalVideoFrameRequest,
937 base::Unretained(renderer_wrapper_.get())));
941 void PipelineImpl::RendererWrapper::OnExternalVideoFrameRequest() {
942 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
943 if (!shared_state_.renderer) {
947 shared_state_.renderer->OnExternalVideoFrameRequest();
950 void PipelineImpl::RendererWrapper::OnDemuxerCompletedTrackChange(
951 base::OnceClosure change_completed_cb,
952 DemuxerStream::Type stream_type,
953 const std::vector<DemuxerStream*>& streams) {
954 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
955 if (!shared_state_.renderer) {
956 // This can happen if the pipeline has been suspended.
957 std::move(change_completed_cb).Run();
961 switch (stream_type) {
962 case DemuxerStream::AUDIO:
963 shared_state_.renderer->OnEnabledAudioTracksChanged(
964 streams, std::move(change_completed_cb));
966 case DemuxerStream::VIDEO:
967 shared_state_.renderer->OnSelectedVideoTracksChanged(
968 streams, std::move(change_completed_cb));
970 case DemuxerStream::UNKNOWN: // Fail on unknown type.
971 NOTREACHED_NORETURN();
975 void PipelineImpl::RendererWrapper::OnStatisticsUpdate(
976 const PipelineStatistics& stats) {
977 DVLOG(3) << __func__;
978 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
980 base::AutoLock auto_lock(shared_state_lock_);
981 shared_state_.statistics.audio_bytes_decoded += stats.audio_bytes_decoded;
982 shared_state_.statistics.video_bytes_decoded += stats.video_bytes_decoded;
983 shared_state_.statistics.video_frames_decoded += stats.video_frames_decoded;
984 shared_state_.statistics.video_frames_decoded_power_efficient +=
985 stats.video_frames_decoded_power_efficient;
986 shared_state_.statistics.video_frames_dropped += stats.video_frames_dropped;
987 shared_state_.statistics.audio_memory_usage += stats.audio_memory_usage;
988 shared_state_.statistics.video_memory_usage += stats.video_memory_usage;
990 if (stats.audio_pipeline_info.decoder_type != AudioDecoderType::kUnknown &&
991 stats.audio_pipeline_info !=
992 shared_state_.statistics.audio_pipeline_info) {
993 shared_state_.statistics.audio_pipeline_info = stats.audio_pipeline_info;
994 main_task_runner_->PostTask(
995 FROM_HERE, base::BindOnce(&PipelineImpl::OnAudioPipelineInfoChange,
996 weak_pipeline_, stats.audio_pipeline_info));
999 if (stats.video_pipeline_info.decoder_type != VideoDecoderType::kUnknown &&
1000 stats.video_pipeline_info !=
1001 shared_state_.statistics.video_pipeline_info) {
1002 shared_state_.statistics.video_pipeline_info = stats.video_pipeline_info;
1003 main_task_runner_->PostTask(
1004 FROM_HERE, base::BindOnce(&PipelineImpl::OnVideoPipelineInfoChange,
1005 weak_pipeline_, stats.video_pipeline_info));
1008 if (stats.video_frame_duration_average != kNoTimestamp) {
1009 shared_state_.statistics.video_frame_duration_average =
1010 stats.video_frame_duration_average;
1013 base::TimeDelta old_key_frame_distance_average =
1014 shared_state_.statistics.video_keyframe_distance_average;
1015 if (stats.video_keyframe_distance_average != kNoTimestamp) {
1016 shared_state_.statistics.video_keyframe_distance_average =
1017 stats.video_keyframe_distance_average;
1020 if (shared_state_.statistics.video_keyframe_distance_average !=
1021 old_key_frame_distance_average) {
1022 main_task_runner_->PostTask(
1024 base::BindOnce(&PipelineImpl::OnVideoAverageKeyframeDistanceUpdate,
1029 void PipelineImpl::RendererWrapper::OnBufferingStateChange(
1030 BufferingState state,
1031 BufferingStateChangeReason reason) {
1032 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1033 DVLOG(2) << __func__ << "(" << state << ", " << reason << ") ";
1035 main_task_runner_->PostTask(
1036 FROM_HERE, base::BindOnce(&PipelineImpl::OnBufferingStateChange,
1037 weak_pipeline_, state, reason));
1040 void PipelineImpl::RendererWrapper::OnWaiting(WaitingReason reason) {
1041 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1043 main_task_runner_->PostTask(
1045 base::BindOnce(&PipelineImpl::OnWaiting, weak_pipeline_, reason));
1048 void PipelineImpl::RendererWrapper::OnVideoNaturalSizeChange(
1049 const gfx::Size& size) {
1050 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1052 main_task_runner_->PostTask(
1053 FROM_HERE, base::BindOnce(&PipelineImpl::OnVideoNaturalSizeChange,
1054 weak_pipeline_, size));
1057 void PipelineImpl::RendererWrapper::OnVideoOpacityChange(bool opaque) {
1058 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1060 main_task_runner_->PostTask(
1061 FROM_HERE, base::BindOnce(&PipelineImpl::OnVideoOpacityChange,
1062 weak_pipeline_, opaque));
1065 void PipelineImpl::RendererWrapper::OnVideoFrameRateChange(
1066 absl::optional<int> fps) {
1067 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1069 main_task_runner_->PostTask(
1070 FROM_HERE, base::BindOnce(&PipelineImpl::OnVideoFrameRateChange,
1071 weak_pipeline_, fps));
1074 void PipelineImpl::RendererWrapper::OnAudioConfigChange(
1075 const AudioDecoderConfig& config) {
1076 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1078 main_task_runner_->PostTask(FROM_HERE,
1079 base::BindOnce(&PipelineImpl::OnAudioConfigChange,
1080 weak_pipeline_, config));
1083 void PipelineImpl::RendererWrapper::OnVideoConfigChange(
1084 const VideoDecoderConfig& config) {
1085 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1087 main_task_runner_->PostTask(FROM_HERE,
1088 base::BindOnce(&PipelineImpl::OnVideoConfigChange,
1089 weak_pipeline_, config));
1092 #if defined(TIZEN_MULTIMEDIA)
1093 void PipelineImpl::RendererWrapper::ToggleFullscreenMode(
1095 ToggledFullscreenCB cb) {
1096 LOG(INFO) << __func__;
1097 if (shared_state_.renderer)
1098 shared_state_.renderer->ToggleFullscreenMode(is_fullscreen, std::move(cb));
1101 void PipelineImpl::RendererWrapper::OnRequestSuspend(bool resource_conflict) {
1102 if (state_ == kSuspending || state_ == kSuspended)
1105 LOG(INFO) << __func__;
1106 request_suspend_task_handle_ = main_task_runner_->PostCancelableDelayedTask(
1107 base::subtle::PostDelayedTaskPassKey(), FROM_HERE,
1108 base::BindOnce(&PipelineImpl::OnRequestSuspend, weak_pipeline_,
1113 void PipelineImpl::RendererWrapper::OnRequestSeek(base::TimeDelta time) {
1114 LOG(INFO) << __func__ << " time:" << time;
1116 if (!media_task_runner_->RunsTasksInCurrentSequence()) {
1117 media_task_runner_->PostTask(
1119 base::BindOnce(&media::PipelineImpl::RendererWrapper::OnRequestSeek,
1120 weak_factory_.GetWeakPtr(), time));
1125 SerialRunner::Queue bound_fns;
1126 demuxer_->AbortPendingReads();
1128 bound_fns.Push(base::BindOnce(
1129 &Renderer::Flush, base::Unretained(shared_state_.renderer.get())));
1131 base::BindOnce(&Demuxer::Seek, base::Unretained(demuxer_), time));
1132 pending_callbacks_ = SerialRunner::Run(
1133 std::move(bound_fns), base::BindOnce(&RendererWrapper::CompleteSeek,
1134 weak_factory_.GetWeakPtr(), time));
1137 void PipelineImpl::RendererWrapper::OnSeekableTimeChange(
1138 base::TimeDelta min_time,
1139 base::TimeDelta max_time,
1141 main_task_runner_->PostTask(
1142 FROM_HERE, base::BindOnce(&PipelineImpl::OnSeekableTimeChange,
1143 weak_pipeline_, min_time, max_time, is_live));
1146 void PipelineImpl::RendererWrapper::OnLivePlaybackComplete() {
1147 main_task_runner_->PostTask(
1149 base::BindOnce(&PipelineImpl::OnLivePlaybackComplete, weak_pipeline_));
1153 void PipelineImpl::RendererWrapper::OnPipelineError(PipelineStatus error) {
1154 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1155 DCHECK(!error.is_ok()) << "PIPELINE_OK isn't an error!";
1157 // Preserve existing abnormal status.
1158 if (status_ != PIPELINE_OK)
1161 // If the pipeline is already stopping or stopped we don't need to report an
1162 // error. Similarly if the pipeline is suspending or suspended, the error may
1163 // be recoverable, so don't propagate it now, instead let the subsequent seek
1164 // during resume propagate it if it's unrecoverable.
1165 if (state_ == kStopping || state_ == kStopped || state_ == kSuspending ||
1166 state_ == kSuspended) {
1170 // PIPELINE_ERROR_HARDWARE_CONTEXT_RESET and DEMUXER_ERROR_DETECTED_HLS are
1171 // not fatal errors. They are just signals to restart or reconfig the
1173 if (error != PIPELINE_ERROR_HARDWARE_CONTEXT_RESET &&
1174 error != DEMUXER_ERROR_DETECTED_HLS) {
1178 main_task_runner_->PostTask(
1179 FROM_HERE, base::BindOnce(&PipelineImpl::OnError, weak_pipeline_, error));
1182 void PipelineImpl::RendererWrapper::OnCdmAttached(CdmAttachedCB cdm_attached_cb,
1183 CdmContext* cdm_context,
1185 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1188 cdm_context_ = cdm_context;
1189 std::move(cdm_attached_cb).Run(success);
1192 void PipelineImpl::RendererWrapper::CheckPlaybackEnded() {
1193 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1195 if (shared_state_.renderer && !renderer_ended_)
1198 // Don't fire an ended event if we're already in an error state.
1199 if (status_ != PIPELINE_OK)
1202 main_task_runner_->PostTask(
1203 FROM_HERE, base::BindOnce(&PipelineImpl::OnEnded, weak_pipeline_));
1206 void PipelineImpl::RendererWrapper::SetState(State next_state) {
1207 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1208 DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> "
1209 << PipelineImpl::GetStateString(next_state);
1211 state_ = next_state;
1213 // TODO(tmathmeyer) Make State serializable so GetStateString won't need
1214 // to be called here.
1215 media_log_->AddEvent<MediaLogEvent::kPipelineStateChange>(
1216 std::string(PipelineImpl::GetStateString(next_state)));
1219 void PipelineImpl::RendererWrapper::CompleteSeek(base::TimeDelta seek_time,
1220 PipelineStatus status) {
1221 DVLOG(1) << __func__ << ": seek_time=" << seek_time << ", status=" << status;
1222 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1223 DCHECK(state_ == kStarting || state_ == kSeeking || state_ == kResuming);
1225 if (state_ == kStarting) {
1226 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.Start", status.code(),
1227 PIPELINE_STATUS_MAX + 1);
1230 DCHECK(pending_callbacks_);
1231 pending_callbacks_.reset();
1233 if (status != PIPELINE_OK) {
1234 OnPipelineError(status);
1238 #if BUILDFLAG(IS_TIZEN_TV)
1239 shared_state_.renderer->SetContentMimeType(mime_type_);
1242 shared_state_.renderer->StartPlayingFrom(
1243 std::max(seek_time, demuxer_->GetStartTime()));
1245 base::AutoLock auto_lock(shared_state_lock_);
1246 shared_state_.suspend_timestamp = kNoTimestamp;
1249 shared_state_.renderer->SetPlaybackRate(playback_rate_);
1252 main_task_runner_->PostTask(
1254 base::BindOnce(&PipelineImpl::OnSeekDone, weak_pipeline_, false));
1257 void PipelineImpl::RendererWrapper::CompleteSuspend(PipelineStatus status) {
1258 DVLOG(1) << __func__ << ": status=" << status;
1259 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1260 DCHECK_EQ(kSuspending, state_);
1262 DCHECK(pending_callbacks_);
1263 pending_callbacks_.reset();
1265 // In case we are suspending or suspended, the error may be recoverable,
1266 // so don't propagate it now, instead let the subsequent seek during resume
1267 // propagate it if it's unrecoverable.
1268 LOG_IF(WARNING, status != PIPELINE_OK)
1269 << "Encountered pipeline error while suspending: " << status;
1271 #if defined(TIZEN_MULTIMEDIA)
1272 // Keep the renderer without destroying it in tizen.
1273 renderer_suspended_ = true;
1278 base::AutoLock auto_lock(shared_state_lock_);
1279 shared_state_.statistics.audio_memory_usage = 0;
1280 shared_state_.statistics.video_memory_usage = 0;
1283 // Abort any reads the renderer may have kicked off.
1284 demuxer_->AbortPendingReads();
1286 SetState(kSuspended);
1287 main_task_runner_->PostTask(
1288 FROM_HERE, base::BindOnce(&PipelineImpl::OnSuspendDone, weak_pipeline_));
1291 void PipelineImpl::RendererWrapper::InitializeDemuxer(
1292 PipelineStatusCallback done_cb) {
1293 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1295 demuxer_->Initialize(this, std::move(done_cb));
1298 void PipelineImpl::RendererWrapper::CreateRenderer(
1299 PipelineStatusCallback done_cb) {
1300 DVLOG(1) << __func__;
1301 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1302 DCHECK(state_ == kStarting || state_ == kResuming);
1304 if (HasEncryptedStream() && !cdm_context_) {
1305 LOG(INFO) << __func__ << ": Has encrypted stream but CDM is not set.";
1306 create_renderer_done_cb_ = std::move(done_cb);
1307 OnWaiting(WaitingReason::kNoCdm);
1311 CreateRendererInternal(std::move(done_cb));
1314 void PipelineImpl::RendererWrapper::OnRendererCreated(
1315 PipelineStatusCallback done_cb,
1316 std::unique_ptr<Renderer> renderer) {
1317 DVLOG(1) << __func__ << ": renderer=" << renderer.get();
1318 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1321 std::move(done_cb).Run({PIPELINE_ERROR_INITIALIZATION_FAILED,
1322 "Media Renderer creation failed!"});
1327 base::AutoLock auto_lock(shared_state_lock_);
1328 DCHECK(!shared_state_.renderer);
1329 shared_state_.renderer = std::move(renderer);
1331 std::move(done_cb).Run(PIPELINE_OK);
1334 void PipelineImpl::RendererWrapper::InitializeRenderer(
1335 PipelineStatusCallback done_cb) {
1336 DVLOG(1) << __func__;
1337 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1339 switch (demuxer_->GetType()) {
1340 case MediaResource::Type::kStream:
1341 if (demuxer_->GetAllStreams().empty()) {
1342 DVLOG(1) << "Error: demuxer does not have an audio or a video stream.";
1343 std::move(done_cb).Run(PIPELINE_ERROR_COULD_NOT_RENDER);
1348 case MediaResource::Type::KUrl:
1349 // NOTE: Empty GURL are not valid.
1350 if (!demuxer_->GetMediaUrlParams().media_url.is_valid()) {
1351 DVLOG(1) << "Error: demuxer does not have a valid URL.";
1352 std::move(done_cb).Run(PIPELINE_ERROR_COULD_NOT_RENDER);
1359 shared_state_.renderer->SetCdm(cdm_context_, base::DoNothing());
1361 #if BUILDFLAG(IS_TIZEN_TV)
1362 // Create decrypt media resource for eme
1363 CreateAndInitializeDecryptingMediaResource();
1368 shared_state_.renderer->SetLatencyHint(latency_hint_);
1370 shared_state_.renderer->SetPreservesPitch(preserves_pitch_);
1372 // Calling SetVolume() before Initialize() allows renderers to optimize for
1373 // power by avoiding initialization of audio output until necessary.
1374 shared_state_.renderer->SetVolume(volume_);
1376 shared_state_.renderer->SetWasPlayedWithUserActivation(
1377 was_played_with_user_activation_);
1379 #if defined(TIZEN_VIDEO_HOLE)
1380 LOG(INFO) << __func__ << " call SetVideoHole : " << is_video_hole_;
1381 shared_state_.renderer->SetVideoHole(is_video_hole_);
1383 #if BUILDFLAG(IS_TIZEN_TV)
1384 // We need to set the mime type before Initialize() in the case of URL streams
1385 // where the content is dash, because the Initialize() calls prepare in this
1386 // case, and the player needs the mime type to configure itself correctly.
1387 shared_state_.renderer->SetContentMimeType(mime_type_);
1390 // Initialize Renderer and report timeout UMA.
1391 std::string uma_name = "Media.InitializeRendererTimeout";
1392 base::UmaHistogramEnumeration(uma_name, CallbackTimeoutStatus::kCreate);
1394 #if BUILDFLAG(IS_TIZEN_TV)
1395 if ((MediaResource::Type::kStream == demuxer_->GetType()) && (cdm_context_)) {
1396 if (!decrypting_media_resource_.get()) {
1397 LOG(ERROR) << "decrypting_media_resource is not created yet";
1400 shared_state_.renderer->Initialize(decrypting_media_resource_.get(), this,
1401 std::move(done_cb));
1404 shared_state_.renderer->Initialize(
1406 WrapCallbackWithTimeoutHandler(
1407 std::move(done_cb), /*timeout_delay=*/base::Seconds(10),
1408 base::BindOnce(&OnCallbackTimeout, uma_name)));
1411 void PipelineImpl::RendererWrapper::DestroyRenderer() {
1412 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1414 // Destroy the renderer outside the lock scope to avoid holding the lock
1415 // while renderer is being destroyed (in case Renderer destructor is costly).
1416 std::unique_ptr<Renderer> renderer;
1418 base::AutoLock auto_lock(shared_state_lock_);
1419 renderer.swap(shared_state_.renderer);
1423 void PipelineImpl::RendererWrapper::ReportMetadata(StartType start_type) {
1424 DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
1426 PipelineMetadata metadata;
1427 std::vector<DemuxerStream*> streams;
1429 switch (demuxer_->GetType()) {
1430 case MediaResource::Type::kStream:
1431 metadata.timeline_offset = demuxer_->GetTimelineOffset();
1432 // TODO(servolk): What should we do about metadata for multiple streams?
1433 streams = demuxer_->GetAllStreams();
1434 for (auto* stream : streams) {
1435 if (stream->type() == DemuxerStream::VIDEO && !metadata.has_video) {
1436 metadata.has_video = true;
1437 metadata.natural_size = GetRotatedVideoSize(
1438 stream->video_decoder_config().video_transformation().rotation,
1439 stream->video_decoder_config().natural_size());
1440 metadata.video_decoder_config = stream->video_decoder_config();
1442 if (stream->type() == DemuxerStream::AUDIO && !metadata.has_audio) {
1443 metadata.has_audio = true;
1444 metadata.audio_decoder_config = stream->audio_decoder_config();
1449 case MediaResource::Type::KUrl:
1450 // We don't know if the MediaPlayerRender has Audio/Video until we start
1451 // playing. Conservatively assume that they do.
1452 metadata.has_video = true;
1453 metadata.has_audio = true;
1457 main_task_runner_->PostTask(
1459 base::BindOnce(&PipelineImpl::OnMetadata, weak_pipeline_, metadata));
1461 // If suspended start has not been requested, or is not allowed given the
1462 // metadata, continue the normal renderer initialization path.
1463 if (start_type == StartType::kNormal ||
1464 (start_type == StartType::kSuspendAfterMetadataForAudioOnly &&
1465 metadata.has_video)) {
1469 // Abort pending render initialization tasks and suspend the pipeline.
1470 pending_callbacks_.reset();
1472 shared_state_.suspend_timestamp =
1473 std::max(base::TimeDelta(), demuxer_->GetStartTime());
1474 SetState(kSuspended);
1475 main_task_runner_->PostTask(
1477 base::BindOnce(&PipelineImpl::OnSeekDone, weak_pipeline_, true));
1480 bool PipelineImpl::RendererWrapper::HasEncryptedStream() {
1481 // Encrypted streams are only handled explicitly for STREAM type.
1482 if (demuxer_->GetType() != MediaResource::Type::kStream)
1485 auto streams = demuxer_->GetAllStreams();
1487 for (auto* stream : streams) {
1488 if (stream->type() == DemuxerStream::AUDIO &&
1489 stream->audio_decoder_config().is_encrypted())
1491 if (stream->type() == DemuxerStream::VIDEO &&
1492 stream->video_decoder_config().is_encrypted())
1499 PipelineImpl::PipelineImpl(
1500 scoped_refptr<base::SequencedTaskRunner> media_task_runner,
1501 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
1502 CreateRendererCB create_renderer_cb,
1503 #if defined(TIZEN_VIDEO_HOLE)
1506 MediaLog* media_log)
1507 : media_task_runner_(media_task_runner),
1508 create_renderer_cb_(create_renderer_cb),
1509 media_log_(media_log),
1511 playback_rate_(kDefaultPlaybackRate),
1512 volume_(kDefaultVolume),
1513 is_suspended_(false) {
1514 DVLOG(2) << __func__;
1515 DCHECK(create_renderer_cb_);
1517 renderer_wrapper_ = std::make_unique<RendererWrapper>(
1518 media_task_runner_, std::move(main_task_runner),
1519 #if defined(TIZEN_VIDEO_HOLE)
1525 PipelineImpl::~PipelineImpl() {
1526 DVLOG(2) << __func__;
1527 DCHECK(thread_checker_.CalledOnValidThread());
1528 DCHECK(!client_) << "Stop() must complete before destroying object";
1530 DCHECK(!suspend_cb_);
1531 DCHECK(!weak_factory_.HasWeakPtrs())
1532 << "Stop() should have invalidated all weak pointers";
1534 // RendererWrapper is deleted on the media thread.
1535 media_task_runner_->DeleteSoon(FROM_HERE, renderer_wrapper_.release());
1538 void PipelineImpl::Start(StartType start_type,
1541 PipelineStatusCallback seek_cb) {
1542 DVLOG(2) << __func__ << ": start_type=" << static_cast<int>(start_type);
1543 DCHECK(thread_checker_.CalledOnValidThread());
1551 seek_cb_ = std::move(seek_cb);
1552 last_media_time_ = base::TimeDelta();
1553 seek_time_ = kNoTimestamp;
1554 external_video_frame_request_signaled_ = false;
1556 // By default, create a default renderer to avoid additional start-to-play
1557 // latency caused by asynchronous Renderer creation. When |start_type| is
1558 // kSuspendAfterMetadata, latency is not important and the video may never
1559 // play. In this case, not creating a default renderer to reduce memory usage.
1560 std::unique_ptr<Renderer> default_renderer;
1561 if (start_type != StartType::kSuspendAfterMetadata)
1562 default_renderer = create_renderer_cb_.Run(absl::nullopt);
1564 media_task_runner_->PostTask(
1566 base::BindOnce(&RendererWrapper::Start,
1567 base::Unretained(renderer_wrapper_.get()), start_type,
1568 demuxer, std::move(default_renderer),
1569 weak_factory_.GetWeakPtr()));
1572 void PipelineImpl::Stop() {
1573 DVLOG(2) << __func__;
1574 DCHECK(thread_checker_.CalledOnValidThread());
1577 DVLOG(2) << "Media pipeline isn't running. Ignoring Stop()";
1581 if (media_task_runner_->RunsTasksInCurrentSequence()) {
1582 // This path is executed by unittests that share media and main threads.
1583 renderer_wrapper_->Stop();
1585 // This path is executed by production code where the two task runners -
1586 // main and media - live on different threads.
1587 media_task_runner_->PostTask(
1588 FROM_HERE, base::BindOnce(&RendererWrapper::Stop,
1589 base::Unretained(renderer_wrapper_.get())));
1592 // Once the pipeline is stopped, nothing is reported back to the client.
1593 // Reset all callbacks and client handle.
1595 suspend_cb_.Reset();
1598 // Invalidate self weak pointers effectively canceling all pending
1599 // notifications in the message queue.
1600 weak_factory_.InvalidateWeakPtrs();
1603 void PipelineImpl::Seek(base::TimeDelta time, PipelineStatusCallback seek_cb) {
1604 DVLOG(2) << __func__ << " to " << time;
1605 DCHECK(thread_checker_.CalledOnValidThread());
1609 DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek().";
1610 std::move(seek_cb).Run(PIPELINE_ERROR_INVALID_STATE);
1615 seek_cb_ = std::move(seek_cb);
1617 last_media_time_ = base::TimeDelta();
1618 media_task_runner_->PostTask(
1620 base::BindOnce(&RendererWrapper::Seek,
1621 base::Unretained(renderer_wrapper_.get()), time));
1624 void PipelineImpl::Suspend(PipelineStatusCallback suspend_cb) {
1625 DVLOG(2) << __func__;
1628 DCHECK(IsRunning());
1629 DCHECK(!suspend_cb_);
1630 suspend_cb_ = std::move(suspend_cb);
1632 media_task_runner_->PostTask(
1633 FROM_HERE, base::BindOnce(&RendererWrapper::Suspend,
1634 base::Unretained(renderer_wrapper_.get())));
1637 void PipelineImpl::Resume(base::TimeDelta time,
1638 PipelineStatusCallback seek_cb) {
1639 DVLOG(2) << __func__;
1640 DCHECK(thread_checker_.CalledOnValidThread());
1643 DCHECK(IsRunning());
1645 seek_cb_ = std::move(seek_cb);
1647 last_media_time_ = base::TimeDelta();
1648 external_video_frame_request_signaled_ = false;
1650 std::unique_ptr<Renderer> default_renderer;
1651 #if defined(TIZEN_MULTIMEDIA)
1652 // Do not need to create the renderer if suspended in tizen.
1653 if (!renderer_wrapper_->IsRendererSuspended())
1656 // Always create a default renderer for Resume(). Creation error is handled in
1657 // `RendererWrapper::Resume()`.
1658 default_renderer = create_renderer_cb_.Run(absl::nullopt);
1661 media_task_runner_->PostTask(
1662 FROM_HERE, base::BindOnce(&RendererWrapper::Resume,
1663 base::Unretained(renderer_wrapper_.get()),
1664 std::move(default_renderer), time));
1667 bool PipelineImpl::IsRunning() const {
1668 DCHECK(thread_checker_.CalledOnValidThread());
1672 bool PipelineImpl::IsSuspended() const {
1673 DVLOG(2) << __func__ << "(" << is_suspended_ << ")";
1674 DCHECK(thread_checker_.CalledOnValidThread());
1675 return is_suspended_;
1678 double PipelineImpl::GetPlaybackRate() const {
1679 DCHECK(thread_checker_.CalledOnValidThread());
1680 return playback_rate_;
1683 void PipelineImpl::SetPlaybackRate(double playback_rate) {
1684 DVLOG(2) << __func__ << "(" << playback_rate << ")";
1685 DCHECK(thread_checker_.CalledOnValidThread());
1687 // Not checking IsRunning() so we can set the playback rate before Start().
1689 if (playback_rate < 0.0) {
1690 DVLOG(1) << __func__ << ": Invalid playback rate " << playback_rate;
1694 playback_rate_ = playback_rate;
1695 media_task_runner_->PostTask(
1696 FROM_HERE, base::BindOnce(&RendererWrapper::SetPlaybackRate,
1697 base::Unretained(renderer_wrapper_.get()),
1701 float PipelineImpl::GetVolume() const {
1702 DCHECK(thread_checker_.CalledOnValidThread());
1706 void PipelineImpl::SetVolume(float volume) {
1707 DVLOG(2) << __func__ << "(" << volume << ")";
1708 DCHECK(thread_checker_.CalledOnValidThread());
1710 // Not checking IsRunning() so we can set the volume before Start().
1712 if (volume < 0.0f) {
1713 DVLOG(1) << __func__ << ": Invalid volume " << volume;
1718 media_task_runner_->PostTask(
1720 base::BindOnce(&RendererWrapper::SetVolume,
1721 base::Unretained(renderer_wrapper_.get()), volume_));
1724 void PipelineImpl::SetLatencyHint(
1725 absl::optional<base::TimeDelta> latency_hint) {
1726 DVLOG(1) << __func__ << "("
1728 ? base::NumberToString(latency_hint->InMilliseconds()) + "ms"
1731 DCHECK(!latency_hint || (*latency_hint >= base::TimeDelta()));
1732 DCHECK(thread_checker_.CalledOnValidThread());
1734 // Not checking IsRunning() so we can set the latency hint before Start().
1735 media_task_runner_->PostTask(
1737 base::BindOnce(&RendererWrapper::SetLatencyHint,
1738 base::Unretained(renderer_wrapper_.get()), latency_hint));
1741 void PipelineImpl::SetPreservesPitch(bool preserves_pitch) {
1742 DCHECK(thread_checker_.CalledOnValidThread());
1744 media_task_runner_->PostTask(
1745 FROM_HERE, base::BindOnce(&RendererWrapper::SetPreservesPitch,
1746 base::Unretained(renderer_wrapper_.get()),
1750 void PipelineImpl::SetWasPlayedWithUserActivation(
1751 bool was_played_with_user_activation) {
1752 DCHECK(thread_checker_.CalledOnValidThread());
1754 media_task_runner_->PostTask(
1756 base::BindOnce(&RendererWrapper::SetWasPlayedWithUserActivation,
1757 base::Unretained(renderer_wrapper_.get()),
1758 was_played_with_user_activation));
1761 base::TimeDelta PipelineImpl::GetMediaTime() const {
1762 DCHECK(thread_checker_.CalledOnValidThread());
1764 // Don't trust renderer time during a pending seek. Renderer may return
1765 // pre-seek time which may corrupt |last_media_time_| used for clamping.
1766 if (seek_time_ != kNoTimestamp) {
1767 DVLOG(3) << __func__ << ": (seeking) " << seek_time_.InMilliseconds()
1772 base::TimeDelta media_time = renderer_wrapper_->GetMediaTime();
1774 // Clamp current media time to the last reported value, this prevents higher
1775 // level clients from seeing time go backwards based on inaccurate or spurious
1776 // delay values reported to the AudioClock.
1778 // It is expected that such events are transient and will be recovered as
1779 // rendering continues over time.
1780 if (media_time < last_media_time_) {
1781 DVLOG(2) << __func__ << ": actual=" << media_time
1782 << " clamped=" << last_media_time_;
1783 return last_media_time_;
1786 DVLOG(3) << __func__ << ": " << media_time.InMilliseconds() << " ms";
1787 last_media_time_ = media_time;
1788 return last_media_time_;
1791 Ranges<base::TimeDelta> PipelineImpl::GetBufferedTimeRanges() const {
1792 DCHECK(thread_checker_.CalledOnValidThread());
1793 return renderer_wrapper_->GetBufferedTimeRanges();
1796 base::TimeDelta PipelineImpl::GetMediaDuration() const {
1797 DCHECK(thread_checker_.CalledOnValidThread());
1801 bool PipelineImpl::DidLoadingProgress() {
1802 DCHECK(thread_checker_.CalledOnValidThread());
1803 return renderer_wrapper_->DidLoadingProgress();
1806 PipelineStatistics PipelineImpl::GetStatistics() const {
1807 DCHECK(thread_checker_.CalledOnValidThread());
1808 return renderer_wrapper_->GetStatistics();
1811 void PipelineImpl::SetCdm(CdmContext* cdm_context,
1812 CdmAttachedCB cdm_attached_cb) {
1813 DVLOG(2) << __func__;
1814 DCHECK(thread_checker_.CalledOnValidThread());
1815 DCHECK(cdm_context);
1816 DCHECK(cdm_attached_cb);
1818 // Not checking IsRunning() so we can set the CDM before Start().
1820 media_task_runner_->PostTask(
1823 &RendererWrapper::SetCdm, base::Unretained(renderer_wrapper_.get()),
1825 base::BindPostTaskToCurrentDefault(std::move(cdm_attached_cb))));
1828 #if defined(TIZEN_MULTIMEDIA)
1829 void PipelineImpl::ToggleFullscreenMode(bool is_fullscreen,
1830 ToggledFullscreenCB cb) {
1831 media_task_runner_->PostTask(
1832 FROM_HERE, base::BindOnce(&RendererWrapper::ToggleFullscreenMode,
1833 base::Unretained(renderer_wrapper_.get()),
1834 is_fullscreen, std::move(cb)));
1838 #if defined(TIZEN_VIDEO_HOLE)
1839 void PipelineImpl::SetMediaGeometry(const gfx::RectF rect_f) {
1840 media_task_runner_->PostTask(
1842 base::BindOnce(&RendererWrapper::SetMediaGeometry,
1843 base::Unretained(renderer_wrapper_.get()), rect_f));
1847 #define RETURN_STRING(state) \
1852 const char* PipelineImpl::GetStateString(State state) {
1854 RETURN_STRING(kCreated);
1855 RETURN_STRING(kStarting);
1856 RETURN_STRING(kSeeking);
1857 RETURN_STRING(kPlaying);
1858 RETURN_STRING(kStopping);
1859 RETURN_STRING(kStopped);
1860 RETURN_STRING(kSuspending);
1861 RETURN_STRING(kSuspended);
1862 RETURN_STRING(kResuming);
1864 NOTREACHED_NORETURN();
1867 #undef RETURN_STRING
1869 void PipelineImpl::AsyncCreateRenderer(
1870 absl::optional<RendererType> renderer_type,
1871 RendererCreatedCB renderer_created_cb) {
1872 DVLOG(2) << __func__;
1873 DCHECK(thread_checker_.CalledOnValidThread());
1875 std::move(renderer_created_cb).Run(create_renderer_cb_.Run(renderer_type));
1878 void PipelineImpl::OnError(PipelineStatus error) {
1879 DVLOG(2) << __func__;
1880 DCHECK(thread_checker_.CalledOnValidThread());
1881 DCHECK(!error.is_ok()) << "PIPELINE_OK isn't an error!";
1882 DCHECK(IsRunning());
1884 // If the error happens during starting/seeking/suspending/resuming,
1885 // report the error via the completion callback for those tasks.
1886 // Else report error via the client interface.
1888 std::move(seek_cb_).Run(error);
1893 std::move(suspend_cb_).Run(error);
1898 client_->OnError(error);
1901 void PipelineImpl::OnFallback(PipelineStatus status) {
1902 client_->OnFallback(std::move(status).AddHere());
1905 void PipelineImpl::OnEnded() {
1906 DVLOG(2) << __func__;
1907 DCHECK(thread_checker_.CalledOnValidThread());
1908 DCHECK(IsRunning());
1914 void PipelineImpl::OnMetadata(const PipelineMetadata& metadata) {
1915 DVLOG(2) << __func__;
1916 DCHECK(thread_checker_.CalledOnValidThread());
1917 DCHECK(IsRunning());
1920 client_->OnMetadata(metadata);
1923 void PipelineImpl::OnBufferingStateChange(BufferingState state,
1924 BufferingStateChangeReason reason) {
1925 DCHECK(thread_checker_.CalledOnValidThread());
1926 DCHECK(IsRunning());
1929 client_->OnBufferingStateChange(state, reason);
1932 #if BUILDFLAG(IS_TIZEN_TV)
1933 void PipelineImpl::SetContentMimeType(const std::string& mime_type) {
1934 DVLOG(2) << __func__ << "(" << mime_type << ")";
1935 DCHECK(thread_checker_.CalledOnValidThread());
1936 media_task_runner_->PostTask(
1938 base::BindOnce(&RendererWrapper::SetContentMimeType,
1939 base::Unretained(renderer_wrapper_.get()), mime_type));
1942 void PipelineImpl::SetParentalRatingResult(bool is_pass) {
1943 DVLOG(2) << __func__;
1944 DCHECK(thread_checker_.CalledOnValidThread());
1945 renderer_wrapper_->SetParentalRatingResult(is_pass);
1949 void PipelineImpl::OnDurationChange(base::TimeDelta duration) {
1950 DVLOG(2) << __func__;
1951 DCHECK(thread_checker_.CalledOnValidThread());
1952 DCHECK(IsRunning());
1954 duration_ = duration;
1957 client_->OnDurationChange();
1960 void PipelineImpl::OnWaiting(WaitingReason reason) {
1961 DVLOG(2) << __func__;
1962 DCHECK(thread_checker_.CalledOnValidThread());
1963 DCHECK(IsRunning());
1966 client_->OnWaiting(reason);
1969 void PipelineImpl::OnVideoNaturalSizeChange(const gfx::Size& size) {
1970 DVLOG(2) << __func__;
1971 DCHECK(thread_checker_.CalledOnValidThread());
1972 DCHECK(IsRunning());
1975 client_->OnVideoNaturalSizeChange(size);
1978 void PipelineImpl::OnVideoOpacityChange(bool opaque) {
1979 DVLOG(2) << __func__;
1980 DCHECK(thread_checker_.CalledOnValidThread());
1981 DCHECK(IsRunning());
1984 client_->OnVideoOpacityChange(opaque);
1987 void PipelineImpl::OnVideoFrameRateChange(absl::optional<int> fps) {
1988 DVLOG(2) << __func__;
1989 DCHECK(thread_checker_.CalledOnValidThread());
1990 DCHECK(IsRunning());
1993 client_->OnVideoFrameRateChange(fps);
1996 void PipelineImpl::OnAudioConfigChange(const AudioDecoderConfig& config) {
1997 DVLOG(2) << __func__;
1998 DCHECK(thread_checker_.CalledOnValidThread());
1999 DCHECK(IsRunning());
2002 client_->OnAudioConfigChange(config);
2005 void PipelineImpl::OnVideoConfigChange(const VideoDecoderConfig& config) {
2006 DVLOG(2) << __func__;
2007 DCHECK(thread_checker_.CalledOnValidThread());
2008 DCHECK(IsRunning());
2011 client_->OnVideoConfigChange(config);
2014 void PipelineImpl::OnVideoAverageKeyframeDistanceUpdate() {
2015 DVLOG(2) << __func__;
2016 DCHECK(thread_checker_.CalledOnValidThread());
2017 DCHECK(IsRunning());
2020 client_->OnVideoAverageKeyframeDistanceUpdate();
2023 void PipelineImpl::OnAudioPipelineInfoChange(const AudioPipelineInfo& info) {
2024 DVLOG(2) << __func__ << ": info=" << info;
2025 DCHECK(thread_checker_.CalledOnValidThread());
2026 DCHECK(IsRunning());
2029 client_->OnAudioPipelineInfoChange(info);
2032 void PipelineImpl::OnVideoPipelineInfoChange(const VideoPipelineInfo& info) {
2033 DVLOG(2) << __func__ << ": info=" << info;
2034 DCHECK(thread_checker_.CalledOnValidThread());
2035 DCHECK(IsRunning());
2038 client_->OnVideoPipelineInfoChange(info);
2041 #if defined(TIZEN_MULTIMEDIA)
2042 void PipelineImpl::OnRequestSuspend(bool resource_conflicted) {
2043 DVLOG(3) << __func__;
2044 client_->OnRequestSuspend(resource_conflicted);
2047 void PipelineImpl::OnSeekableTimeChange(base::TimeDelta min_time,
2048 base::TimeDelta max_time,
2050 DVLOG(3) << __func__;
2051 client_->OnSeekableTimeChange(min_time, max_time, is_live);
2054 void PipelineImpl::OnLivePlaybackComplete() {
2055 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
2056 client_->OnLivePlaybackComplete();
2060 void PipelineImpl::OnSeekDone(bool is_suspended) {
2061 DVLOG(3) << __func__ << ": is_suspended=" << is_suspended;
2062 DCHECK(thread_checker_.CalledOnValidThread());
2063 DCHECK(IsRunning());
2065 seek_time_ = kNoTimestamp;
2066 is_suspended_ = is_suspended;
2068 // `seek_cb_` could have been reset in OnError().
2070 std::move(seek_cb_).Run(PIPELINE_OK);
2073 void PipelineImpl::OnSuspendDone() {
2074 DVLOG(3) << __func__;
2075 DCHECK(thread_checker_.CalledOnValidThread());
2076 DCHECK(IsRunning());
2078 is_suspended_ = true;
2080 // `suspend_cb_` could have been reset in OnError().
2082 std::move(suspend_cb_).Run(PIPELINE_OK);
2085 } // namespace media