1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #if !defined(WMFMediaDataDecoder_h_)
8 #  define WMFMediaDataDecoder_h_
9 
10 #  include <set>
11 
12 #  include "MFTDecoder.h"
13 #  include "PlatformDecoderModule.h"
14 #  include "WMF.h"
15 #  include "mozilla/RefPtr.h"
16 
17 namespace mozilla {
18 
19 // Encapsulates the initialization of the MFTDecoder appropriate for decoding
20 // a given stream, and the process of converting the IMFSample produced
21 // by the MFT into a MediaData object.
22 class MFTManager {
23  public:
~MFTManager()24   virtual ~MFTManager() {}
25 
26   // Submit a compressed sample for decoding.
27   // This should forward to the MFTDecoder after performing
28   // any required sample formatting.
29   virtual HRESULT Input(MediaRawData* aSample) = 0;
30 
31   // Produces decoded output, if possible. Blocks until output can be produced,
32   // or until no more is able to be produced.
33   // Returns S_OK on success, or MF_E_TRANSFORM_NEED_MORE_INPUT if there's not
34   // enough data to produce more output. If this returns a failure code other
35   // than MF_E_TRANSFORM_NEED_MORE_INPUT, an error will be reported to the
36   // MP4Reader.
37   virtual HRESULT Output(int64_t aStreamOffset, RefPtr<MediaData>& aOutput) = 0;
38 
Flush()39   void Flush() {
40     mDecoder->Flush();
41     mSeekTargetThreshold.reset();
42   }
43 
Drain()44   void Drain() {
45     if (FAILED(mDecoder->SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0))) {
46       NS_WARNING("Failed to send DRAIN command to MFT");
47     }
48   }
49 
50   // Destroys all resources.
51   virtual void Shutdown() = 0;
52 
IsHardwareAccelerated(nsACString & aFailureReason)53   virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const {
54     return false;
55   }
56 
57   virtual TrackInfo::TrackType GetType() = 0;
58 
59   virtual nsCString GetDescriptionName() const = 0;
60 
SetSeekThreshold(const media::TimeUnit & aTime)61   virtual void SetSeekThreshold(const media::TimeUnit& aTime) {
62     if (aTime.IsValid()) {
63       mSeekTargetThreshold = Some(aTime);
64     } else {
65       mSeekTargetThreshold.reset();
66     }
67   }
68 
HasSeekThreshold()69   virtual bool HasSeekThreshold() const {
70     return mSeekTargetThreshold.isSome();
71   }
72 
NeedsConversion()73   virtual MediaDataDecoder::ConversionRequired NeedsConversion() const {
74     return MediaDataDecoder::ConversionRequired::kNeedNone;
75   }
76 
77  protected:
78   // IMFTransform wrapper that performs the decoding.
79   RefPtr<MFTDecoder> mDecoder;
80 
81   Maybe<media::TimeUnit> mSeekTargetThreshold;
82 };
83 
84 DDLoggedTypeDeclNameAndBase(WMFMediaDataDecoder, MediaDataDecoder);
85 
86 // Decodes audio and video using Windows Media Foundation. Samples are decoded
87 // using the MFTDecoder created by the MFTManager. This class implements
88 // the higher-level logic that drives mapping the MFT to the async
89 // MediaDataDecoder interface. The specifics of decoding the exact stream
90 // type are handled by MFTManager and the MFTDecoder it creates.
91 class WMFMediaDataDecoder
92     : public MediaDataDecoder,
93       public DecoderDoctorLifeLogger<WMFMediaDataDecoder> {
94  public:
95   explicit WMFMediaDataDecoder(MFTManager* aOutputSource);
96   ~WMFMediaDataDecoder();
97 
98   RefPtr<MediaDataDecoder::InitPromise> Init() override;
99 
100   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
101 
102   RefPtr<DecodePromise> Drain() override;
103 
104   RefPtr<FlushPromise> Flush() override;
105 
106   RefPtr<ShutdownPromise> Shutdown() override;
107 
108   bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
109 
GetDescriptionName()110   nsCString GetDescriptionName() const override {
111     return mMFTManager ? mMFTManager->GetDescriptionName() : ""_ns;
112   }
113 
NeedsConversion()114   ConversionRequired NeedsConversion() const override {
115     MOZ_ASSERT(mMFTManager);
116     return mMFTManager->NeedsConversion();
117   }
118 
119   virtual void SetSeekThreshold(const media::TimeUnit& aTime) override;
120 
121  private:
122   RefPtr<DecodePromise> ProcessError(HRESULT aError, const char* aReason);
123 
124   // Called on the task queue. Inserts the sample into the decoder, and
125   // extracts output if available.
126   RefPtr<DecodePromise> ProcessDecode(MediaRawData* aSample);
127 
128   // Called on the task queue. Extracts output if available, and delivers
129   // it to the reader. Called after ProcessDecode() and ProcessDrain().
130   HRESULT ProcessOutput(DecodedData& aResults);
131 
132   // Called on the task queue. Orders the MFT to flush.  There is no output to
133   // extract.
134   RefPtr<FlushPromise> ProcessFlush();
135 
136   // Called on the task queue. Orders the MFT to drain, and then extracts
137   // all available output.
138   RefPtr<DecodePromise> ProcessDrain();
139 
140   // Checks if `aOutput` should be discarded (guarded against) because its a
141   // potentially invalid output from the decoder. This is done because the
142   // Windows decoder appears to produce invalid outputs under certain
143   // conditions.
144   bool ShouldGuardAgaintIncorrectFirstSample(MediaData* aOutput) const;
145 
146   const RefPtr<TaskQueue> mTaskQueue;
147 
148   UniquePtr<MFTManager> mMFTManager;
149 
150   // The last offset into the media resource that was passed into Input().
151   // This is used to approximate the decoder's position in the media resource.
152   int64_t mLastStreamOffset;
153   Maybe<media::TimeUnit> mLastTime;
154   media::TimeUnit mLastDuration;
155   // Before we get the first sample, this records the times of all samples we
156   // send to the decoder which is used to validate if the first sample is valid.
157   std::set<int64_t> mInputTimesSet;
158   int64_t mSamplesCount = 0;
159   int64_t mOutputsCount = 0;
160 
161   bool mIsShutDown = false;
162 
163   enum class DrainStatus {
164     DRAINED,
165     DRAINABLE,
166     DRAINING,
167   };
168   DrainStatus mDrainStatus = DrainStatus::DRAINED;
169 
170   // For telemetry
171   bool mHasSuccessfulOutput = false;
172   bool mRecordedError = false;
173 };
174 
175 }  // namespace mozilla
176 
177 #endif  // WMFMediaDataDecoder_h_
178