1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133

media / filters / offloading_video_decoder.h [blame]

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_FILTERS_OFFLOADING_VIDEO_DECODER_H_
#define MEDIA_FILTERS_OFFLOADING_VIDEO_DECODER_H_

#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "media/base/video_codecs.h"
#include "media/base/video_decoder.h"
#include "media/base/video_decoder_config.h"

namespace base {
class SequencedTaskRunner;
}  // namespace base

namespace media {
class CancellationHelper;

// OffloadableVideoDecoder implementations must have synchronous execution of
// Reset() and Decode() (true for all current software decoders); this allows
// for serializing these operations on the offloading sequence. With
// serializing, multiple Decode() events can be queued on the offload thread,
// and Reset() does not need to wait for |reset_cb| to return.
class MEDIA_EXPORT OffloadableVideoDecoder : public VideoDecoder {
 public:
  enum class OffloadState {
    kOffloaded,  // Indicates the VideoDecoder is being used with
                 // OffloadingVideoDecoder and that callbacks provided to
                 // VideoDecoder methods should not be bound to the current
                 // loop.

    kNormal,  // Indicates the VideoDecoder is being used as a normal
              // VideoDecoder, meaning callbacks should always be asynchronous.
  };

  ~OffloadableVideoDecoder() override {}

  // Called by the OffloadingVideoDecoder when closing the decoder and switching
  // task runners. Will be called on the task runner that Initialize() was
  // called on. Upon completion of this method implementing decoders must be
  // ready to be Initialized() on another thread.
  virtual void Detach() = 0;
};

// Wrapper for OffloadableVideoDecoder implementations that runs the wrapped
// decoder on a task pool other than the caller's thread.
//
// Offloading allows us to avoid blocking the media sequence for Decode() when
// it's known that decoding may take a long time; e.g., high-resolution VP9
// decodes may occasionally take upwards of > 100ms per frame, which is enough
// to exhaust the audio buffer and lead to underflow in some circumstances.
//
// Offloading also allows better pipelining of Decode() calls. The normal decode
// sequence is Decode(buffer) -> DecodeComplete() -> WaitFor(buffer)-> (repeat);
// this sequence generally involves thread hops as well. When offloading we can
// take advantage of the serialization of operations on the offloading sequence
// to make this Decode(buffer) -> DecodeComplete() -> Decode(buffer) by queuing
// the next Decode(buffer) before the previous one completes.
//
// I.e., we are no longer wasting cycles waiting for the recipient of the
// decoded frame to acknowledge that receipt, request the next muxed buffer, and
// then queue the next decode. Those operations now happen in parallel with the
// decoding of the previous buffer on the offloading sequence. Improving the
// total throughput that a decode can achieve.
//
// E.g., without parallel offloading, over 4000 frames, a 4K60 VP9 clip spent
// ~11.7 seconds of aggregate time just waiting for frames. With parallel
// offloading the same clip spent only ~3.4 seconds.
//
// Optionally decoders which are aware of the wrapping may choose to not rebind
// callbacks to the offloaded thread since they will already be bound by the
// OffloadingVideoDecoder; this simply avoids extra hops for completed tasks.
class MEDIA_EXPORT OffloadingVideoDecoder : public VideoDecoder {
 public:
  // Offloads |decoder| for VideoDecoderConfigs provided to Initialize() using
  // |supported_codecs| with a coded width >= |min_offloading_width|.
  //
  // E.g. if a width of 1024 is specified, and VideoDecoderConfig has a coded
  // size of 1280x720 we will use offloading. Conversely if the width was
  // 640x480, we would not use offloading.
  OffloadingVideoDecoder(int min_offloading_width,
                         std::vector<VideoCodec> supported_codecs,
                         std::unique_ptr<OffloadableVideoDecoder> decoder);

  OffloadingVideoDecoder(const OffloadingVideoDecoder&) = delete;
  OffloadingVideoDecoder& operator=(const OffloadingVideoDecoder&) = delete;

  ~OffloadingVideoDecoder() override;

  // VideoDecoder implementation.
  VideoDecoderType GetDecoderType() const override;
  void Initialize(const VideoDecoderConfig& config,
                  bool low_delay,
                  CdmContext* cdm_context,
                  InitCB init_cb,
                  const OutputCB& output_cb,
                  const WaitingCB& waiting_cb) override;
  void Decode(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb) override;
  void Reset(base::OnceClosure reset_cb) override;
  int GetMaxDecodeRequests() const override;

 private:
  // VideoDecoderConfigs given to Initialize() with a coded size that has width
  // greater than or equal to this value will be offloaded.
  const int min_offloading_width_;

  // Codecs supported for offloading.
  const std::vector<VideoCodec> supported_codecs_;

  // Indicates if Initialize() has been called.
  bool initialized_ = false;

  SEQUENCE_CHECKER(sequence_checker_);

  // A helper class for managing Decode() and Reset() calls to the offloaded
  // decoder; it owns the given OffloadableVideoDecoder and is always destructed
  // on |offload_task_runner_| when used.
  std::unique_ptr<CancellationHelper> helper_;

  // High resolution decodes may block the media thread for too long, in such
  // cases offload the decoding to a task pool.
  scoped_refptr<base::SequencedTaskRunner> offload_task_runner_;

  // NOTE: Weak pointers must be invalidated before all other member variables.
  base::WeakPtrFactory<OffloadingVideoDecoder> weak_factory_{this};
};

}  // namespace media

#endif  // MEDIA_FILTERS_OFFLOADING_VIDEO_DECODER_H_