tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MFMediaEngineStream.cpp (23424B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "MFMediaEngineStream.h"
      6 
      7 #include <vcruntime.h>
      8 
      9 #include "AudioConverter.h"
     10 #include "MFMediaEngineUtils.h"
     11 #include "MFMediaSource.h"
     12 #include "TimeUnits.h"
     13 #include "WMF.h"
     14 #include "WMFUtils.h"
     15 #include "mozilla/ProfilerLabels.h"
     16 #include "mozilla/ProfilerMarkerTypes.h"
     17 #include "mozilla/ScopeExit.h"
     18 
     19 namespace mozilla {
     20 
     21 // Don't use this log on the task queue, because it would be racy for `mStream`.
     22 #define WLOGV(msg, ...)                                                   \
     23  MOZ_LOG(gMFMediaEngineLog, LogLevel::Verbose,                           \
     24          ("MFMediaEngineStreamWrapper for stream %p (%s, id=%lu), " msg, \
     25           mStream.Get(), mStream->GetDescriptionName().get(),            \
     26           mStream->DescriptorId(), ##__VA_ARGS__))
     27 
     28 #define SLOG(msg, ...)                              \
     29  MOZ_LOG(                                          \
     30      gMFMediaEngineLog, LogLevel::Debug,           \
     31      ("MFMediaStream=%p (%s, id=%lu), " msg, this, \
     32       this->GetDescriptionName().get(), this->DescriptorId(), ##__VA_ARGS__))
     33 
     34 #define SLOGV(msg, ...)                             \
     35  MOZ_LOG(                                          \
     36      gMFMediaEngineLog, LogLevel::Verbose,         \
     37      ("MFMediaStream=%p (%s, id=%lu), " msg, this, \
     38       this->GetDescriptionName().get(), this->DescriptorId(), ##__VA_ARGS__))
     39 
     40 using Microsoft::WRL::ComPtr;
     41 
     42 RefPtr<MediaDataDecoder::InitPromise> MFMediaEngineStreamWrapper::Init() {
     43  MOZ_ASSERT(mStream->DescriptorId(), "Stream hasn't been initialized!");
     44  WLOGV("Init");
     45  return InitPromise::CreateAndResolve(mStream->TrackType(), __func__);
     46 }
     47 
     48 RefPtr<MediaDataDecoder::DecodePromise> MFMediaEngineStreamWrapper::Decode(
     49    MediaRawData* aSample) {
     50  WLOGV("Decode");
     51  if (!mStream || mStream->IsShutdown()) {
     52    return DecodePromise::CreateAndReject(
     53        MediaResult(NS_ERROR_FAILURE, "MFMediaEngineStreamWrapper is shutdown"),
     54        __func__);
     55  }
     56  RefPtr<MediaRawData> sample = aSample;
     57  return InvokeAsync(mTaskQueue, mStream.Get(), __func__,
     58                     &MFMediaEngineStream::OutputData, std::move(sample));
     59 }
     60 
     61 RefPtr<MediaDataDecoder::DecodePromise> MFMediaEngineStreamWrapper::Drain() {
     62  WLOGV("Drain");
     63  if (!mStream || mStream->IsShutdown()) {
     64    return DecodePromise::CreateAndReject(
     65        MediaResult(NS_ERROR_FAILURE, "MFMediaEngineStreamWrapper is shutdown"),
     66        __func__);
     67  }
     68  return InvokeAsync(mTaskQueue, mStream.Get(), __func__,
     69                     &MFMediaEngineStream::Drain);
     70 }
     71 
     72 RefPtr<MediaDataDecoder::FlushPromise> MFMediaEngineStreamWrapper::Flush() {
     73  WLOGV("Flush");
     74  if (!mStream || mStream->IsShutdown()) {
     75    return FlushPromise::CreateAndReject(
     76        MediaResult(NS_ERROR_FAILURE, "MFMediaEngineStreamWrapper is shutdown"),
     77        __func__);
     78  }
     79  return InvokeAsync(mTaskQueue, mStream.Get(), __func__,
     80                     &MFMediaEngineStream::Flush);
     81 }
     82 
     83 RefPtr<ShutdownPromise> MFMediaEngineStreamWrapper::Shutdown() {
     84  // Stream shutdown is controlled by the media source, so we don't need to call
     85  // its shutdown.
     86  WLOGV("Disconnect wrapper");
     87  if (!mStream) {
     88    // This promise must only ever be resolved. See the definition of the
     89    // original abstract function.
     90    return ShutdownPromise::CreateAndResolve(false, __func__);
     91  }
     92  mStream = nullptr;
     93  mTaskQueue = nullptr;
     94  return ShutdownPromise::CreateAndResolve(true, __func__);
     95 }
     96 
     97 nsCString MFMediaEngineStreamWrapper::GetDescriptionName() const {
     98  return mStream ? mStream->GetDescriptionName() : nsLiteralCString("none");
     99 }
    100 
    101 nsCString MFMediaEngineStreamWrapper::GetCodecName() const {
    102  return mStream ? mStream->GetCodecName() : nsLiteralCString("none");
    103 }
    104 
    105 MediaDataDecoder::ConversionRequired
    106 MFMediaEngineStreamWrapper::NeedsConversion() const {
    107  return mStream ? mStream->NeedsConversion()
    108                 : MediaDataDecoder::ConversionRequired::kNeedNone;
    109 }
    110 
    111 bool MFMediaEngineStreamWrapper::ShouldDecoderAlwaysBeRecycled() const {
    112  return true;
    113 }
    114 
    115 bool MFMediaEngineStreamWrapper::IsHardwareAccelerated(
    116    nsACString& aFailureReason) const {
    117  if (!mStream) {
    118    return false;
    119  }
    120  // Video is always hardware accelerated.
    121  return mStream->AsVideoStream() != nullptr;
    122 }
    123 
    124 MFMediaEngineStream::MFMediaEngineStream()
    125    : mIsShutdown(false),
    126      mIsSelected(false),
    127      mRawDataQueueForFeedingEngine(true /* aEnablePreciseDuration */),
    128      mRawDataQueueForGeneratingOutput(true /* aEnablePreciseDuration */),
    129      mReceivedEOS(false) {
    130  MOZ_COUNT_CTOR(MFMediaEngineStream);
    131 }
    132 
    133 MFMediaEngineStream::~MFMediaEngineStream() {
    134  MOZ_ASSERT(IsShutdown());
    135  MOZ_COUNT_DTOR(MFMediaEngineStream);
    136 }
    137 
    138 HRESULT MFMediaEngineStream::RuntimeClassInitialize(
    139    uint64_t aStreamId, const TrackInfo& aInfo, bool aIsEncryptedCustomInit,
    140    MFMediaSource* aParentSource) {
    141  mParentSource = aParentSource;
    142  mTaskQueue = aParentSource->GetTaskQueue();
    143  MOZ_ASSERT(mTaskQueue);
    144  mStreamId = aStreamId;
    145  mIsEncryptedCustomInit = aIsEncryptedCustomInit;
    146 
    147  auto errorExit = MakeScopeExit([&] {
    148    SLOG("Failed to initialize media stream (id=%" PRIu64 ")", aStreamId);
    149    mIsShutdown = true;
    150    (void)mMediaEventQueue->Shutdown();
    151  });
    152 
    153  RETURN_IF_FAILED(wmf::MFCreateEventQueue(&mMediaEventQueue));
    154 
    155  ComPtr<IMFMediaType> mediaType;
    156  // The inherited stream would return different type based on their media info.
    157  RETURN_IF_FAILED(CreateMediaType(aInfo, mediaType.GetAddressOf()));
    158  RETURN_IF_FAILED(GenerateStreamDescriptor(mediaType));
    159  SLOG("Initialized %s (id=%" PRIu64 ", descriptorId=%lu)",
    160       GetDescriptionName().get(), aStreamId, mStreamDescriptorId);
    161  errorExit.release();
    162  return S_OK;
    163 }
    164 
    165 HRESULT MFMediaEngineStream::GenerateStreamDescriptor(
    166    ComPtr<IMFMediaType>& aMediaType) {
    167  RETURN_IF_FAILED(wmf::MFCreateStreamDescriptor(
    168      mStreamId, 1 /* stream amount */, aMediaType.GetAddressOf(),
    169      &mStreamDescriptor));
    170  RETURN_IF_FAILED(
    171      mStreamDescriptor->GetStreamIdentifier(&mStreamDescriptorId));
    172  if (IsEncrypted()) {
    173    RETURN_IF_FAILED(mStreamDescriptor->SetUINT32(MF_SD_PROTECTED, 1));
    174  }
    175  return S_OK;
    176 }
    177 
    178 HRESULT MFMediaEngineStream::Start(const PROPVARIANT* aPosition) {
    179  AssertOnMFThreadPool();
    180  if (!IsSelected()) {
    181    SLOG("No need to start non-selected stream");
    182    return S_OK;
    183  }
    184  if (IsShutdown()) {
    185    return MF_E_SHUTDOWN;
    186  }
    187  SLOG("Start");
    188  const bool isFromCurrentPosition = aPosition->vt == VT_EMPTY;
    189  RETURN_IF_FAILED(QueueEvent(MEStreamStarted, GUID_NULL, S_OK, aPosition));
    190  MOZ_ASSERT(mTaskQueue);
    191  (void)mTaskQueue->Dispatch(NS_NewRunnableFunction(
    192      "MFMediaEngineStream::Start",
    193      [self = RefPtr{this}, isFromCurrentPosition, this]() {
    194        if (!isFromCurrentPosition && IsEnded()) {
    195          SLOG("Stream restarts again from a new position, reset EOS");
    196          mReceivedEOS = false;
    197        }
    198        // Process pending requests (if any) which happened when the stream
    199        // wasn't allowed to serve samples. Eg. stream is paused. Or resend the
    200        // ended event if the stream is ended already.
    201        ReplySampleRequestIfPossible();
    202      }));
    203  return S_OK;
    204 }
    205 
    206 HRESULT MFMediaEngineStream::Seek(const PROPVARIANT* aPosition) {
    207  AssertOnMFThreadPool();
    208  if (!IsSelected()) {
    209    SLOG("No need to seek non-selected stream");
    210    return S_OK;
    211  }
    212  SLOG("Seek");
    213  RETURN_IF_FAILED(QueueEvent(MEStreamSeeked, GUID_NULL, S_OK, aPosition));
    214  return S_OK;
    215 }
    216 
    217 HRESULT MFMediaEngineStream::Stop() {
    218  AssertOnMFThreadPool();
    219  if (!IsSelected()) {
    220    SLOG("No need to stop non-selected stream");
    221    return S_OK;
    222  }
    223  SLOG("Stop");
    224  RETURN_IF_FAILED(QueueEvent(MEStreamStopped, GUID_NULL, S_OK, nullptr));
    225  return S_OK;
    226 }
    227 
    228 HRESULT MFMediaEngineStream::Pause() {
    229  AssertOnMFThreadPool();
    230  if (!IsSelected()) {
    231    SLOG("No need to pause non-selected stream");
    232    return S_OK;
    233  }
    234  SLOG("Pause");
    235  RETURN_IF_FAILED(QueueEvent(MEStreamPaused, GUID_NULL, S_OK, nullptr));
    236  return S_OK;
    237 }
    238 
    239 void MFMediaEngineStream::Shutdown() {
    240  AssertOnMFThreadPool();
    241  if (IsShutdown()) {
    242    return;
    243  }
    244  SLOG("Shutdown");
    245  mIsShutdown = true;
    246  // After this method is called, all IMFMediaEventQueue methods return
    247  // MF_E_SHUTDOWN.
    248  RETURN_VOID_IF_FAILED(mMediaEventQueue->Shutdown());
    249  ComPtr<MFMediaEngineStream> self = this;
    250  MOZ_ASSERT(mTaskQueue);
    251  (void)mTaskQueue->Dispatch(
    252      NS_NewRunnableFunction("MFMediaEngineStream::Shutdown", [self]() {
    253        self->mParentSource = nullptr;
    254        self->mRawDataQueueForFeedingEngine.Reset();
    255        self->mRawDataQueueForGeneratingOutput.Reset();
    256        self->ShutdownCleanUpOnTaskQueue();
    257        self->mTaskQueue = nullptr;
    258      }));
    259 }
    260 
    261 IFACEMETHODIMP
    262 MFMediaEngineStream::GetMediaSource(IMFMediaSource** aMediaSource) {
    263  AssertOnMFThreadPool();
    264  if (IsShutdown()) {
    265    return MF_E_SHUTDOWN;
    266  }
    267  RETURN_IF_FAILED(mParentSource.CopyTo(aMediaSource));
    268  return S_OK;
    269 }
    270 
    271 IFACEMETHODIMP MFMediaEngineStream::GetStreamDescriptor(
    272    IMFStreamDescriptor** aStreamDescriptor) {
    273  AssertOnMFThreadPool();
    274  if (IsShutdown()) {
    275    return MF_E_SHUTDOWN;
    276  }
    277  if (!mStreamDescriptor) {
    278    SLOG("Hasn't initialized stream descriptor");
    279    return MF_E_NOT_INITIALIZED;
    280  }
    281  RETURN_IF_FAILED(mStreamDescriptor.CopyTo(aStreamDescriptor));
    282  return S_OK;
    283 }
    284 
    285 IFACEMETHODIMP MFMediaEngineStream::RequestSample(IUnknown* aToken) {
    286  AssertOnMFThreadPool();
    287  if (IsShutdown()) {
    288    return MF_E_SHUTDOWN;
    289  }
    290 
    291  ComPtr<IUnknown> token = aToken;
    292  ComPtr<MFMediaEngineStream> self = this;
    293  MOZ_ASSERT(mTaskQueue);
    294  (void)mTaskQueue->Dispatch(NS_NewRunnableFunction(
    295      "MFMediaEngineStream::RequestSample", [token, self, this]() {
    296        AssertOnTaskQueue();
    297        mSampleRequestTokens.push(token);
    298        SLOGV("RequestSample, token amount=%zu", mSampleRequestTokens.size());
    299        ReplySampleRequestIfPossible();
    300        if (!HasEnoughRawData() && mParentSource && !IsEnded()) {
    301          SendRequestSampleEvent(false /* isEnough */);
    302        }
    303      }));
    304  return S_OK;
    305 }
    306 
    307 void MFMediaEngineStream::ReplySampleRequestIfPossible() {
    308  AssertOnTaskQueue();
    309  if (IsEnded()) {
    310    // We have no more sample to return, clean all pending requests.
    311    while (!mSampleRequestTokens.empty()) {
    312      mSampleRequestTokens.pop();
    313    }
    314    MOZ_ASSERT(mSampleRequestTokens.empty());
    315    NotifyEndEvent();
    316    return;
    317  }
    318 
    319  if (mSampleRequestTokens.empty() ||
    320      mRawDataQueueForFeedingEngine.GetSize() == 0) {
    321    return;
    322  }
    323 
    324  if (!ShouldServeSamples()) {
    325    SLOGV("Not deliver samples if the stream is not started");
    326    return;
    327  }
    328 
    329  // Push data into the mf media event queue if the media engine is already
    330  // waiting for data.
    331  ComPtr<IMFSample> inputSample;
    332  RETURN_VOID_IF_FAILED(CreateInputSample(inputSample.GetAddressOf()));
    333  ComPtr<IUnknown> token = mSampleRequestTokens.front();
    334  RETURN_VOID_IF_FAILED(
    335      inputSample->SetUnknown(MFSampleExtension_Token, token.Get()));
    336  mSampleRequestTokens.pop();
    337  RETURN_VOID_IF_FAILED(mMediaEventQueue->QueueEventParamUnk(
    338      MEMediaSample, GUID_NULL, S_OK, inputSample.Get()));
    339 }
    340 
    341 void MFMediaEngineStream::NotifyEndEvent() {
    342  AssertOnTaskQueue();
    343  SLOG("Notify end event");
    344  MOZ_ASSERT(mRawDataQueueForFeedingEngine.GetSize() == 0);
    345  RETURN_VOID_IF_FAILED(mMediaEventQueue->QueueEventParamUnk(
    346      MEEndOfStream, GUID_NULL, S_OK, nullptr));
    347  mEndedEvent.Notify(TrackType());
    348  PROFILER_MARKER_TEXT("MFMediaEngineStream:NotifyEnd", MEDIA_PLAYBACK, {},
    349                       nsPrintfCString("stream=%s, id=%" PRIu64,
    350                                       GetDescriptionName().get(), mStreamId));
    351 }
    352 
    353 bool MFMediaEngineStream::ShouldServeSamples() const {
    354  AssertOnTaskQueue();
    355  return mParentSource &&
    356         mParentSource->GetState() == MFMediaSource::State::Started &&
    357         mIsSelected;
    358 }
    359 
    360 HRESULT MFMediaEngineStream::CreateInputSample(IMFSample** aSample) {
    361  AssertOnTaskQueue();
    362 
    363  ComPtr<IMFSample> sample;
    364  RETURN_IF_FAILED(wmf::MFCreateSample(&sample));
    365 
    366  MOZ_ASSERT(mRawDataQueueForFeedingEngine.GetSize() != 0);
    367  RefPtr<MediaRawData> data = mRawDataQueueForFeedingEngine.PopFront();
    368  SLOGV("CreateInputSample, pop data [%" PRId64 ", %" PRId64
    369        "] (duration=%" PRId64 ", kf=%d, encrypted=%d), queue size=%zu",
    370        data->mTime.ToMicroseconds(), data->GetEndTime().ToMicroseconds(),
    371        data->mDuration.ToMicroseconds(), data->mKeyframe,
    372        data->mCrypto.IsEncrypted(), mRawDataQueueForFeedingEngine.GetSize());
    373  PROFILER_MARKER(
    374      nsPrintfCString(
    375          "pop %s (stream=%" PRIu64 ")",
    376          TrackType() == TrackInfo::TrackType::kVideoTrack ? "video" : "audio",
    377          mStreamId),
    378      MEDIA_PLAYBACK, {}, MediaSampleMarker, data->mTime.ToMicroseconds(),
    379      data->GetEndTime().ToMicroseconds(),
    380      mRawDataQueueForFeedingEngine.GetSize());
    381 
    382  // Copy data into IMFMediaBuffer
    383  ComPtr<IMFMediaBuffer> buffer;
    384  BYTE* dst = nullptr;
    385  DWORD maxLength = 0;
    386  RETURN_IF_FAILED(
    387      wmf::MFCreateMemoryBuffer(data->Size(), buffer.GetAddressOf()));
    388  RETURN_IF_FAILED(buffer->Lock(&dst, &maxLength, 0));
    389  memcpy(dst, data->Data(), data->Size());
    390  RETURN_IF_FAILED(buffer->Unlock());
    391  RETURN_IF_FAILED(buffer->SetCurrentLength(data->Size()));
    392 
    393  // Setup sample attributes
    394  RETURN_IF_FAILED(sample->AddBuffer(buffer.Get()));
    395  RETURN_IF_FAILED(
    396      sample->SetSampleTime(UsecsToHNs(data->mTime.ToMicroseconds())));
    397  RETURN_IF_FAILED(
    398      sample->SetSampleDuration(UsecsToHNs(data->mDuration.ToMicroseconds())));
    399  if (data->mKeyframe) {
    400    RETURN_IF_FAILED(sample->SetUINT32(MFSampleExtension_CleanPoint, 1));
    401  }
    402 
    403  // Setup encrypt attributes
    404  if (data->mCrypto.IsEncrypted()) {
    405    RETURN_IF_FAILED(AddEncryptAttributes(sample.Get(), data->mCrypto));
    406  }
    407 
    408  *aSample = sample.Detach();
    409  return S_OK;
    410 }
    411 
    412 HRESULT MFMediaEngineStream::AddEncryptAttributes(
    413    IMFSample* aSample, const CryptoSample& aCryptoConfig) {
    414  // Scheme
    415  MFSampleEncryptionProtectionScheme protectionScheme;
    416  if (aCryptoConfig.mCryptoScheme == CryptoScheme::Cenc) {
    417    SLOG("Set CENC encryption");
    418    protectionScheme = MFSampleEncryptionProtectionScheme::
    419        MF_SAMPLE_ENCRYPTION_PROTECTION_SCHEME_AES_CTR;
    420  } else if (aCryptoConfig.mCryptoScheme == CryptoScheme::Cbcs ||
    421             aCryptoConfig.mCryptoScheme == CryptoScheme::Cbcs_1_9) {
    422    protectionScheme = MFSampleEncryptionProtectionScheme::
    423        MF_SAMPLE_ENCRYPTION_PROTECTION_SCHEME_AES_CBC;
    424    SLOG("Set CBC pattern encryption, crypt=%u, skip=%u",
    425         aCryptoConfig.mCryptByteBlock, aCryptoConfig.mSkipByteBlock);
    426    // Only need to set them when they are non-zero. See
    427    // https://learn.microsoft.com/en-us/windows/win32/medfound/mfsampleextension-encryption-cryptbyteblock
    428    // https://learn.microsoft.com/en-us/windows/win32/medfound/mfsampleextension-encryption-skipbyteblock
    429    if (aCryptoConfig.mCryptByteBlock > 0 && aCryptoConfig.mSkipByteBlock > 0) {
    430      RETURN_IF_FAILED(
    431          aSample->SetUINT32(MFSampleExtension_Encryption_CryptByteBlock,
    432                             aCryptoConfig.mCryptByteBlock));
    433      RETURN_IF_FAILED(
    434          aSample->SetUINT32(MFSampleExtension_Encryption_SkipByteBlock,
    435                             aCryptoConfig.mSkipByteBlock));
    436    }
    437  } else {
    438    SLOG("Unexpected encryption scheme");
    439    return MF_E_UNEXPECTED;
    440  }
    441  RETURN_IF_FAILED(aSample->SetUINT32(
    442      MFSampleExtension_Encryption_ProtectionScheme, protectionScheme));
    443 
    444  // KID
    445  if (aCryptoConfig.mKeyId.Length() != sizeof(GUID)) {
    446    SLOG("Unsupported key ID size (%zu)", aCryptoConfig.mKeyId.Length());
    447    return MF_E_UNEXPECTED;
    448  }
    449  GUID keyId;
    450  GUIDFromByteArray(aCryptoConfig.mKeyId, keyId);
    451  RETURN_IF_FAILED(aSample->SetGUID(MFSampleExtension_Content_KeyID, keyId));
    452  // TODO : if we want to suspend/resume the media engine, then we can consider
    453  // to store last key id and set it in CDM to refresh the decryptor.
    454 
    455  // IV
    456  if (aCryptoConfig.mIVSize != 0) {
    457    // Per-sample IV, usually seen in CENC.
    458    SLOG("Use sample IV for decryption, IV size=%u", aCryptoConfig.mIVSize);
    459    RETURN_IF_FAILED(aSample->SetBlob(
    460        MFSampleExtension_Encryption_SampleID,
    461        reinterpret_cast<const uint8_t*>(aCryptoConfig.mIV.Elements()),
    462        aCryptoConfig.mIVSize));
    463  } else {
    464    // A constant IV for all samples, usually seen in CBCS.
    465    SLOG("Use constant IV for decryption, constantIV length=%zu",
    466         aCryptoConfig.mConstantIV.Length());
    467    RETURN_IF_FAILED(aSample->SetBlob(
    468        MFSampleExtension_Encryption_SampleID,
    469        reinterpret_cast<const uint8_t*>(aCryptoConfig.mConstantIV.Elements()),
    470        aCryptoConfig.mConstantIV.Length()));
    471  }
    472 
    473  // Subsample entries.
    474  MOZ_ASSERT(aCryptoConfig.mEncryptedSizes.Length() ==
    475             aCryptoConfig.mPlainSizes.Length());
    476  size_t numSubsamples = aCryptoConfig.mEncryptedSizes.Length();
    477  if (numSubsamples != 0) {
    478    std::vector<MediaFoundationSubsampleEntry> subsampleEntries;
    479    for (size_t idx = 0; idx < numSubsamples; idx++) {
    480      subsampleEntries.push_back(MediaFoundationSubsampleEntry{
    481          aCryptoConfig.mPlainSizes[idx], aCryptoConfig.mEncryptedSizes[idx]});
    482    }
    483    const uint32_t entriesSize =
    484        sizeof(MediaFoundationSubsampleEntry) * numSubsamples;
    485    RETURN_IF_FAILED(aSample->SetBlob(
    486        MFSampleExtension_Encryption_SubSample_Mapping,
    487        reinterpret_cast<const uint8_t*>(subsampleEntries.data()),
    488        entriesSize));
    489  }
    490 
    491  return S_OK;
    492 }
    493 
    494 IFACEMETHODIMP MFMediaEngineStream::GetEvent(DWORD aFlags,
    495                                             IMFMediaEvent** aEvent) {
    496  AssertOnMFThreadPool();
    497  MOZ_ASSERT(mMediaEventQueue);
    498  RETURN_IF_FAILED(mMediaEventQueue->GetEvent(aFlags, aEvent));
    499  return S_OK;
    500 }
    501 
    502 IFACEMETHODIMP MFMediaEngineStream::BeginGetEvent(IMFAsyncCallback* aCallback,
    503                                                  IUnknown* aState) {
    504  AssertOnMFThreadPool();
    505  MOZ_ASSERT(mMediaEventQueue);
    506  RETURN_IF_FAILED(mMediaEventQueue->BeginGetEvent(aCallback, aState));
    507  return S_OK;
    508 }
    509 
    510 IFACEMETHODIMP MFMediaEngineStream::EndGetEvent(IMFAsyncResult* aResult,
    511                                                IMFMediaEvent** aEvent) {
    512  AssertOnMFThreadPool();
    513  MOZ_ASSERT(mMediaEventQueue);
    514  RETURN_IF_FAILED(mMediaEventQueue->EndGetEvent(aResult, aEvent));
    515  return S_OK;
    516 }
    517 
    518 IFACEMETHODIMP MFMediaEngineStream::QueueEvent(MediaEventType aType,
    519                                               REFGUID aExtendedType,
    520                                               HRESULT aStatus,
    521                                               const PROPVARIANT* aValue) {
    522  AssertOnMFThreadPool();
    523  MOZ_ASSERT(mMediaEventQueue);
    524  RETURN_IF_FAILED(mMediaEventQueue->QueueEventParamVar(aType, aExtendedType,
    525                                                        aStatus, aValue));
    526  SLOG("Queued event %s", MediaEventTypeToStr(aType));
    527  return S_OK;
    528 }
    529 
    530 void MFMediaEngineStream::SetSelected(bool aSelected) {
    531  AssertOnMFThreadPool();
    532  SLOG("Select=%d", aSelected);
    533  mIsSelected = aSelected;
    534 }
    535 
    536 void MFMediaEngineStream::NotifyNewData(MediaRawData* aSample) {
    537  AssertOnTaskQueue();
    538  if (IsShutdown()) {
    539    return;
    540  }
    541  const bool wasEnough = HasEnoughRawData();
    542  mRawDataQueueForFeedingEngine.Push(aSample);
    543  mRawDataQueueForGeneratingOutput.Push(aSample);
    544  SLOGV("NotifyNewData, push data [%" PRId64 ", %" PRId64
    545        "], queue size=%zu, queue duration=%" PRId64,
    546        aSample->mTime.ToMicroseconds(), aSample->GetEndTime().ToMicroseconds(),
    547        mRawDataQueueForFeedingEngine.GetSize(),
    548        mRawDataQueueForFeedingEngine.PreciseDuration());
    549  if (mReceivedEOS) {
    550    SLOG("Receive a new data, cancel old EOS flag");
    551    mReceivedEOS = false;
    552  }
    553  ReplySampleRequestIfPossible();
    554  if (!wasEnough && HasEnoughRawData()) {
    555    SendRequestSampleEvent(true /* isEnough */);
    556  }
    557 }
    558 
    559 void MFMediaEngineStream::SendRequestSampleEvent(bool aIsEnough) {
    560  AssertOnTaskQueue();
    561  SLOGV("data is %s, queue duration=%" PRId64,
    562        aIsEnough ? "enough" : "not enough",
    563        mRawDataQueueForFeedingEngine.PreciseDuration());
    564  mParentSource->mRequestSampleEvent.Notify(
    565      SampleRequest{TrackType(), aIsEnough});
    566 }
    567 
    568 void MFMediaEngineStream::NotifyEndOfStreamInternal() {
    569  AssertOnTaskQueue();
    570  if (mReceivedEOS) {
    571    return;
    572  }
    573  SLOG("EOS");
    574  mReceivedEOS = true;
    575  ReplySampleRequestIfPossible();
    576 }
    577 
    578 bool MFMediaEngineStream::IsEnded() const {
    579  AssertOnTaskQueue();
    580  return mReceivedEOS && mRawDataQueueForFeedingEngine.GetSize() == 0;
    581 }
    582 
    583 RefPtr<MediaDataDecoder::FlushPromise> MFMediaEngineStream::Flush() {
    584  if (IsShutdown()) {
    585    return MediaDataDecoder::FlushPromise::CreateAndReject(
    586        MediaResult(NS_ERROR_FAILURE,
    587                    RESULT_DETAIL("MFMediaEngineStream is shutdown")),
    588        __func__);
    589  }
    590  AssertOnTaskQueue();
    591  SLOG("Flush");
    592  mRawDataQueueForFeedingEngine.Reset();
    593  mRawDataQueueForGeneratingOutput.Reset();
    594  mReceivedEOS = false;
    595  return MediaDataDecoder::FlushPromise::CreateAndResolve(true, __func__);
    596 }
    597 
    598 RefPtr<MediaDataDecoder::DecodePromise> MFMediaEngineStream::OutputData(
    599    RefPtr<MediaRawData> aSample) {
    600  if (IsShutdown()) {
    601    return MediaDataDecoder::DecodePromise::CreateAndReject(
    602        MediaResult(NS_ERROR_FAILURE,
    603                    RESULT_DETAIL("MFMediaEngineStream is shutdown")),
    604        __func__);
    605  }
    606  AssertOnTaskQueue();
    607  NotifyNewData(aSample);
    608  MediaDataDecoder::DecodedData outputs;
    609  if (RefPtr<MediaData> outputData = OutputDataInternal()) {
    610    outputs.AppendElement(outputData);
    611    SLOGV("Output data [%" PRId64 ",%" PRId64 "]",
    612          outputData->mTime.ToMicroseconds(),
    613          outputData->GetEndTime().ToMicroseconds());
    614  }
    615  return MediaDataDecoder::DecodePromise::CreateAndResolve(std::move(outputs),
    616                                                           __func__);
    617 };
    618 
    619 RefPtr<MediaDataDecoder::DecodePromise> MFMediaEngineStream::Drain() {
    620  if (IsShutdown()) {
    621    return MediaDataDecoder::DecodePromise::CreateAndReject(
    622        MediaResult(NS_ERROR_FAILURE,
    623                    RESULT_DETAIL("MFMediaEngineStream is shutdown")),
    624        __func__);
    625  }
    626  AssertOnTaskQueue();
    627  MediaDataDecoder::DecodedData outputs;
    628  while (RefPtr<MediaData> outputData = OutputDataInternal()) {
    629    outputs.AppendElement(outputData);
    630    SLOGV("Output data [%" PRId64 ",%" PRId64 "]",
    631          outputData->mTime.ToMicroseconds(),
    632          outputData->GetEndTime().ToMicroseconds());
    633  }
    634  return MediaDataDecoder::DecodePromise::CreateAndResolve(std::move(outputs),
    635                                                           __func__);
    636 }
    637 
    638 void MFMediaEngineStream::AssertOnTaskQueue() const {
    639  MOZ_ASSERT(mTaskQueue && mTaskQueue->IsCurrentThreadIn());
    640 }
    641 
    642 void MFMediaEngineStream::AssertOnMFThreadPool() const {
    643  // We can't really assert the thread id from thread pool, because it would
    644  // change any time. So we just assert this is not the task queue, and use the
    645  // explicit function name to indicate what thread we should run on.
    646  // TODO : this assertion is not precise, because the running thread could be
    647  // the stream wrapper thread as well,
    648  MOZ_ASSERT(!mTaskQueue || !mTaskQueue->IsCurrentThreadIn());
    649 }
    650 
    651 #undef WLOGV
    652 #undef SLOG
    653 #undef SLOGV
    654 
    655 }  // namespace mozilla