offloading_video_decoder.cc 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "media/filters/offloading_video_decoder.h"
  5. #include <memory>
  6. #include "base/bind.h"
  7. #include "base/callback_helpers.h"
  8. #include "base/synchronization/atomic_flag.h"
  9. #include "base/task/sequenced_task_runner.h"
  10. #include "base/task/thread_pool.h"
  11. #include "media/base/bind_to_current_loop.h"
  12. #include "media/base/decoder_buffer.h"
  13. #include "media/base/video_frame.h"
  14. namespace media {
  15. // Helper class which manages cancellation of Decode() after Reset() and makes
  16. // it easier to destruct on the proper thread.
  17. class CancellationHelper {
  18. public:
  19. CancellationHelper(std::unique_ptr<OffloadableVideoDecoder> decoder)
  20. : cancellation_flag_(std::make_unique<base::AtomicFlag>()),
  21. decoder_(std::move(decoder)) {}
  22. CancellationHelper(const CancellationHelper&) = delete;
  23. CancellationHelper& operator=(const CancellationHelper&) = delete;
  24. // Safe to call from any thread.
  25. void Cancel() { cancellation_flag_->Set(); }
  26. void Decode(scoped_refptr<DecoderBuffer> buffer,
  27. VideoDecoder::DecodeCB decode_cb) {
  28. if (cancellation_flag_->IsSet()) {
  29. std::move(decode_cb).Run(DecoderStatus::Codes::kAborted);
  30. return;
  31. }
  32. decoder_->Decode(std::move(buffer), std::move(decode_cb));
  33. }
  34. void Reset(base::OnceClosure reset_cb) {
  35. // OffloadableVideoDecoders are required to have a synchronous Reset(), so
  36. // we don't need to wait for the Reset to complete. Despite this, we don't
  37. // want to run |reset_cb| before we've reset the cancellation flag or the
  38. // client may end up issuing another Reset() before this code runs.
  39. decoder_->Reset(base::DoNothing());
  40. cancellation_flag_ = std::make_unique<base::AtomicFlag>();
  41. std::move(reset_cb).Run();
  42. }
  43. OffloadableVideoDecoder* decoder() const { return decoder_.get(); }
  44. private:
  45. std::unique_ptr<base::AtomicFlag> cancellation_flag_;
  46. std::unique_ptr<OffloadableVideoDecoder> decoder_;
  47. };
  48. OffloadingVideoDecoder::OffloadingVideoDecoder(
  49. int min_offloading_width,
  50. std::vector<VideoCodec> supported_codecs,
  51. std::unique_ptr<OffloadableVideoDecoder> decoder)
  52. : min_offloading_width_(min_offloading_width),
  53. supported_codecs_(std::move(supported_codecs)),
  54. helper_(std::make_unique<CancellationHelper>(std::move(decoder))) {
  55. DETACH_FROM_SEQUENCE(sequence_checker_);
  56. }
  57. OffloadingVideoDecoder::~OffloadingVideoDecoder() {
  58. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  59. // The |helper_| must always be destroyed on the |offload_task_runner_| since
  60. // we may still have tasks posted to it.
  61. if (offload_task_runner_)
  62. offload_task_runner_->DeleteSoon(FROM_HERE, std::move(helper_));
  63. }
  64. VideoDecoderType OffloadingVideoDecoder::GetDecoderType() const {
  65. // This call is expected to be static and safe to call from any thread.
  66. return helper_->decoder()->GetDecoderType();
  67. }
  68. void OffloadingVideoDecoder::Initialize(const VideoDecoderConfig& config,
  69. bool low_delay,
  70. CdmContext* cdm_context,
  71. InitCB init_cb,
  72. const OutputCB& output_cb,
  73. const WaitingCB& waiting_cb) {
  74. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  75. DCHECK(config.IsValidConfig());
  76. const bool disable_offloading =
  77. config.is_encrypted() ||
  78. config.coded_size().width() < min_offloading_width_ ||
  79. std::find(supported_codecs_.begin(), supported_codecs_.end(),
  80. config.codec()) == supported_codecs_.end();
  81. if (initialized_) {
  82. initialized_ = false;
  83. // We're transitioning from offloading to no offloading, so detach from the
  84. // offloading thread so we can run on the media thread.
  85. if (disable_offloading && offload_task_runner_) {
  86. offload_task_runner_->PostTaskAndReply(
  87. FROM_HERE,
  88. base::BindOnce(&OffloadableVideoDecoder::Detach,
  89. base::Unretained(helper_->decoder())),
  90. // We must trampoline back trough OffloadingVideoDecoder because it's
  91. // possible for this class to be destroyed during Initialize().
  92. base::BindOnce(&OffloadingVideoDecoder::Initialize,
  93. weak_factory_.GetWeakPtr(), config, low_delay,
  94. cdm_context, std::move(init_cb), output_cb,
  95. waiting_cb));
  96. return;
  97. }
  98. // We're transitioning from no offloading to offloading, so detach from the
  99. // media thread so we can run on the offloading thread.
  100. if (!disable_offloading && !offload_task_runner_)
  101. helper_->decoder()->Detach();
  102. }
  103. DCHECK(!initialized_);
  104. initialized_ = true;
  105. // Offloaded decoders expect asynchronous execution of callbacks; even if we
  106. // aren't currently using the offload thread.
  107. InitCB bound_init_cb = BindToCurrentLoop(std::move(init_cb));
  108. OutputCB bound_output_cb = BindToCurrentLoop(output_cb);
  109. // If we're not offloading just pass through to the wrapped decoder.
  110. if (disable_offloading) {
  111. offload_task_runner_ = nullptr;
  112. helper_->decoder()->Initialize(config, low_delay, cdm_context,
  113. std::move(bound_init_cb), bound_output_cb,
  114. waiting_cb);
  115. return;
  116. }
  117. if (!offload_task_runner_) {
  118. offload_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
  119. {base::TaskPriority::USER_BLOCKING});
  120. }
  121. offload_task_runner_->PostTask(
  122. FROM_HERE,
  123. base::BindOnce(&OffloadableVideoDecoder::Initialize,
  124. base::Unretained(helper_->decoder()), config, low_delay,
  125. cdm_context, std::move(bound_init_cb), bound_output_cb,
  126. waiting_cb));
  127. }
  128. void OffloadingVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
  129. DecodeCB decode_cb) {
  130. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  131. DCHECK(buffer);
  132. DCHECK(decode_cb);
  133. DecodeCB bound_decode_cb = BindToCurrentLoop(std::move(decode_cb));
  134. if (!offload_task_runner_) {
  135. helper_->decoder()->Decode(std::move(buffer), std::move(bound_decode_cb));
  136. return;
  137. }
  138. offload_task_runner_->PostTask(
  139. FROM_HERE, base::BindOnce(&CancellationHelper::Decode,
  140. base::Unretained(helper_.get()),
  141. std::move(buffer), std::move(bound_decode_cb)));
  142. }
  143. void OffloadingVideoDecoder::Reset(base::OnceClosure reset_cb) {
  144. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  145. base::OnceClosure bound_reset_cb = BindToCurrentLoop(std::move(reset_cb));
  146. if (!offload_task_runner_) {
  147. helper_->Reset(std::move(bound_reset_cb));
  148. } else {
  149. helper_->Cancel();
  150. offload_task_runner_->PostTask(
  151. FROM_HERE, base::BindOnce(&CancellationHelper::Reset,
  152. base::Unretained(helper_.get()),
  153. std::move(bound_reset_cb)));
  154. }
  155. }
  156. int OffloadingVideoDecoder::GetMaxDecodeRequests() const {
  157. // If we're offloading, try to parallelize decodes as well. Take care when
  158. // adjusting this number as it may dramatically increase memory usage and
  159. // reduce seek times. See http://crbug.com/731841.
  160. //
  161. // The current value of 2 was determined via experimental adjustment until a
  162. // 4K60 VP9 playback dropped zero frames.
  163. return offload_task_runner_ ? 2 : 1;
  164. }
  165. } // namespace media