summaryrefslogtreecommitdiffstats
path: root/dom/media/platforms/wmf/MFTEncoder.h
blob: e2eaec3476dcc7086edcde1e61f2ebd02c8da013 (plain)
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
134
135
136
137
138
139
140
141
142
143
144
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#if !defined(MFTEncoder_h_)
#  define MFTEncoder_h_

#  include <functional>
#  include <queue>
#  include "mozilla/RefPtr.h"
#  include "mozilla/ResultVariant.h"
#  include "nsISupportsImpl.h"
#  include "nsDeque.h"
#  include "nsTArray.h"
#  include "WMF.h"

namespace mozilla {

class MFTEncoder final {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MFTEncoder)

  explicit MFTEncoder(const bool aHardwareNotAllowed)
      : mHardwareNotAllowed(aHardwareNotAllowed) {}

  HRESULT Create(const GUID& aSubtype);
  HRESULT Destroy();
  HRESULT SetMediaTypes(IMFMediaType* aInputType, IMFMediaType* aOutputType);
  HRESULT SetModes(UINT32 aBitsPerSec);
  HRESULT SetBitrate(UINT32 aBitsPerSec);

  HRESULT CreateInputSample(RefPtr<IMFSample>* aSample, size_t aSize);
  HRESULT PushInput(RefPtr<IMFSample>&& aInput);
  HRESULT TakeOutput(nsTArray<RefPtr<IMFSample>>& aOutput);
  HRESULT Drain(nsTArray<RefPtr<IMFSample>>& aOutput);

  HRESULT GetMPEGSequenceHeader(nsTArray<UINT8>& aHeader);

  static nsCString GetFriendlyName(const GUID& aSubtype);

  struct Info final {
    GUID mSubtype;
    nsCString mName;
  };

 private:
  // Abstractions to support sync MFTs using the same logic for async MFTs.
  // When the MFT is async and a real event generator is available, simply
  // forward the calls. For sync MFTs, use the synchronous processing model
  // described in
  // https://docs.microsoft.com/en-us/windows/win32/medfound/basic-mft-processing-model#process-data
  // to generate events of the asynchronous processing model.
  using Event = Result<MediaEventType, HRESULT>;
  using EventQueue = std::queue<MediaEventType>;
  class EventSource final {
   public:
    EventSource() : mImpl(Nothing{}) {}

    void SetAsyncEventGenerator(
        already_AddRefed<IMFMediaEventGenerator>&& aAsyncEventGenerator) {
      MOZ_ASSERT(mImpl.is<Nothing>());
      mImpl.emplace<RefPtr<IMFMediaEventGenerator>>(aAsyncEventGenerator);
    }

    void InitSyncMFTEventQueue() {
      MOZ_ASSERT(mImpl.is<Nothing>());
      mImpl.emplace<UniquePtr<EventQueue>>(MakeUnique<EventQueue>());
    }

    bool IsSync() const { return mImpl.is<UniquePtr<EventQueue>>(); }

    Event GetEvent();
    // Push an event when sync MFT is used.
    HRESULT QueueSyncMFTEvent(MediaEventType aEventType);

   private:
    // Pop an event from the queue when sync MFT is used.
    Event GetSyncMFTEvent();

    Variant<
        // Uninitialized.
        Nothing,
        // For async MFT events. See
        // https://docs.microsoft.com/en-us/windows/win32/medfound/asynchronous-mfts#events
        RefPtr<IMFMediaEventGenerator>,
        // Event queue for a sync MFT. Storing EventQueue directly breaks the
        // code so a pointer is introduced.
        UniquePtr<EventQueue>>
        mImpl;
#  ifdef DEBUG
    bool IsOnCurrentThread();
    nsCOMPtr<nsISerialEventTarget> mThread;
#  endif
  };

  ~MFTEncoder() { Destroy(); };

  static nsTArray<Info>& Infos();
  static nsTArray<Info> Enumerate();
  static Maybe<Info> GetInfo(const GUID& aSubtype);

  already_AddRefed<IMFActivate> CreateFactory(const GUID& aSubtype);
  // Return true when successfully enabled, false for MFT that doesn't support
  // async processing model, and error otherwise.
  using AsyncMFTResult = Result<bool, HRESULT>;
  AsyncMFTResult AttemptEnableAsync();
  HRESULT GetStreamIDs();
  GUID MatchInputSubtype(IMFMediaType* aInputType);
  HRESULT SendMFTMessage(MFT_MESSAGE_TYPE aMsg, ULONG_PTR aData);

  HRESULT ProcessEvents();
  HRESULT ProcessInput();
  HRESULT ProcessOutput();

  const bool mHardwareNotAllowed;
  RefPtr<IMFTransform> mEncoder;
  // For MFT object creation. See
  // https://docs.microsoft.com/en-us/windows/win32/medfound/activation-objects
  RefPtr<IMFActivate> mFactory;
  // For encoder configuration. See
  // https://docs.microsoft.com/en-us/windows/win32/directshow/encoder-api
  RefPtr<ICodecAPI> mConfig;

  DWORD mInputStreamID;
  DWORD mOutputStreamID;
  MFT_INPUT_STREAM_INFO mInputStreamInfo;
  MFT_OUTPUT_STREAM_INFO mOutputStreamInfo;
  bool mOutputStreamProvidesSample;

  size_t mNumNeedInput;
  enum class DrainState { DRAINED, DRAINABLE, DRAINING };
  DrainState mDrainState = DrainState::DRAINABLE;

  nsRefPtrDeque<IMFSample> mPendingInputs;
  nsTArray<RefPtr<IMFSample>> mOutputs;

  EventSource mEventSource;
};

}  // namespace mozilla

#endif