dom/media/ipc/VideoDecoderParent.cpp
author Sylvestre Ledru <sledru@mozilla.com>
Mon, 19 Nov 2018 13:25:37 +0000
changeset 446960 0ceae9db9ec0be18daa1a279511ad305723185d4
parent 445434 23849b2015ce4cd60c7123c742bc0a0ce8512f07
child 449028 e4712449ba4303cef134ba0b3f1bea13fbd50c4a
permissions -rw-r--r--
Bug 1204606 - Reformat of dom/media r=jya # skip-blame Differential Revision: https://phabricator.services.mozilla.com/D12251

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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/. */
#include "VideoDecoderParent.h"
#include "mozilla/Unused.h"
#include "mozilla/layers/CompositorThread.h"
#include "base/thread.h"
#include "mozilla/layers/TextureClient.h"
#include "mozilla/layers/VideoBridgeChild.h"
#include "mozilla/layers/ImageClient.h"
#include "MediaInfo.h"
#include "VideoDecoderManagerParent.h"
#ifdef XP_WIN
#include "WMFDecoderModule.h"
#endif

namespace mozilla {
namespace dom {

using base::Thread;
using media::TimeUnit;
using namespace ipc;
using namespace layers;
using namespace gfx;

class KnowsCompositorVideo : public layers::KnowsCompositor {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(KnowsCompositorVideo, override)

  layers::TextureForwarder* GetTextureForwarder() override {
    return VideoBridgeChild::GetSingleton();
  }
  layers::LayersIPCActor* GetLayersIPCActor() override {
    return VideoBridgeChild::GetSingleton();
  }

 private:
  virtual ~KnowsCompositorVideo() = default;
};

VideoDecoderParent::VideoDecoderParent(
    VideoDecoderManagerParent* aParent, const VideoInfo& aVideoInfo,
    float aFramerate, const CreateDecoderParams::OptionSet& aOptions,
    const layers::TextureFactoryIdentifier& aIdentifier,
    TaskQueue* aManagerTaskQueue, TaskQueue* aDecodeTaskQueue, bool* aSuccess,
    nsCString* aErrorDescription)
    : mParent(aParent),
      mManagerTaskQueue(aManagerTaskQueue),
      mDecodeTaskQueue(aDecodeTaskQueue),
      mKnowsCompositor(new KnowsCompositorVideo),
      mDestroyed(false) {
  MOZ_COUNT_CTOR(VideoDecoderParent);
  MOZ_ASSERT(OnManagerThread());
  // We hold a reference to ourselves to keep us alive until IPDL
  // explictly destroys us. There may still be refs held by
  // tasks, but no new ones should be added after we're
  // destroyed.
  mIPDLSelfRef = this;

  mKnowsCompositor->IdentifyTextureHost(aIdentifier);

#ifdef XP_WIN
  using Option = CreateDecoderParams::Option;
  using OptionSet = CreateDecoderParams::OptionSet;

  // TODO: Ideally we wouldn't hardcode the WMF PDM, and we'd use the normal PDM
  // factory logic for picking a decoder.
  WMFDecoderModule::Init();
  RefPtr<WMFDecoderModule> pdm(new WMFDecoderModule());
  pdm->Startup();

  CreateDecoderParams params(aVideoInfo);
  params.mTaskQueue = mDecodeTaskQueue;
  params.mKnowsCompositor = mKnowsCompositor;
  params.mImageContainer = new layers::ImageContainer();
  params.mRate = CreateDecoderParams::VideoFrameRate(aFramerate);
  params.mOptions = aOptions;
  MediaResult error(NS_OK);
  params.mError = &error;

  mDecoder = pdm->CreateVideoDecoder(params);

  if (NS_FAILED(error)) {
    MOZ_ASSERT(aErrorDescription);
    *aErrorDescription = error.Description();
  }
#else
  MOZ_ASSERT(false,
             "Can't use RemoteVideoDecoder on non-Windows platforms yet");
#endif
  *aSuccess = !!mDecoder;
}

VideoDecoderParent::~VideoDecoderParent() {
  MOZ_COUNT_DTOR(VideoDecoderParent);
}

void VideoDecoderParent::Destroy() {
  MOZ_ASSERT(OnManagerThread());
  mDecodeTaskQueue->AwaitShutdownAndIdle();
  mDestroyed = true;
  mIPDLSelfRef = nullptr;
}

mozilla::ipc::IPCResult VideoDecoderParent::RecvInit() {
  MOZ_ASSERT(OnManagerThread());
  RefPtr<VideoDecoderParent> self = this;
  mDecoder->Init()->Then(
      mManagerTaskQueue, __func__,
      [self](TrackInfo::TrackType aTrack) {
        if (self->mDecoder) {
          nsCString hardwareReason;
          bool hardwareAccelerated =
              self->mDecoder->IsHardwareAccelerated(hardwareReason);
          uint32_t conversion =
              static_cast<uint32_t>(self->mDecoder->NeedsConversion());
          Unused << self->SendInitComplete(self->mDecoder->GetDescriptionName(),
                                           hardwareAccelerated, hardwareReason,
                                           conversion);
        }
      },
      [self](MediaResult aReason) {
        if (!self->mDestroyed) {
          Unused << self->SendInitFailed(aReason);
        }
      });
  return IPC_OK();
}

mozilla::ipc::IPCResult VideoDecoderParent::RecvInput(
    const MediaRawDataIPDL& aData) {
  MOZ_ASSERT(OnManagerThread());
  // XXX: This copies the data into a buffer owned by the MediaRawData. Ideally
  // we'd just take ownership of the shmem.
  RefPtr<MediaRawData> data = new MediaRawData(aData.buffer().get<uint8_t>(),
                                               aData.buffer().Size<uint8_t>());
  if (aData.buffer().Size<uint8_t>() && !data->Data()) {
    // OOM
    Error(NS_ERROR_OUT_OF_MEMORY);
    return IPC_OK();
  }
  data->mOffset = aData.base().offset();
  data->mTime = TimeUnit::FromMicroseconds(aData.base().time());
  data->mTimecode = TimeUnit::FromMicroseconds(aData.base().timecode());
  data->mDuration = TimeUnit::FromMicroseconds(aData.base().duration());
  data->mKeyframe = aData.base().keyframe();

  DeallocShmem(aData.buffer());

  RefPtr<VideoDecoderParent> self = this;
  mDecoder->Decode(data)->Then(
      mManagerTaskQueue, __func__,
      [self, this](MediaDataDecoder::DecodedData&& aResults) {
        if (mDestroyed) {
          return;
        }
        ProcessDecodedData(std::move(aResults));
        Unused << SendInputExhausted();
      },
      [self](const MediaResult& aError) { self->Error(aError); });
  return IPC_OK();
}

void VideoDecoderParent::ProcessDecodedData(
    MediaDataDecoder::DecodedData&& aData) {
  MOZ_ASSERT(OnManagerThread());

  // If the video decoder bridge has shut down, stop.
  if (!mKnowsCompositor->GetTextureForwarder()) {
    return;
  }

  for (auto&& data : aData) {
    MOZ_ASSERT(data->mType == MediaData::VIDEO_DATA,
               "Can only decode videos using VideoDecoderParent!");
    VideoData* video = static_cast<VideoData*>(data.get());

    MOZ_ASSERT(video->mImage,
               "Decoded video must output a layer::Image to "
               "be used with VideoDecoderParent");

    RefPtr<TextureClient> texture =
        video->mImage->GetTextureClient(mKnowsCompositor);

    if (!texture) {
      texture = ImageClient::CreateTextureClientForImage(video->mImage,
                                                         mKnowsCompositor);
    }

    if (texture && !texture->IsAddedToCompositableClient()) {
      texture->InitIPDLActor(mKnowsCompositor);
      texture->SetAddedToCompositableClient();
    }

    VideoDataIPDL output(
        MediaDataIPDL(data->mOffset, data->mTime.ToMicroseconds(),
                      data->mTimecode.ToMicroseconds(),
                      data->mDuration.ToMicroseconds(), data->mFrames,
                      data->mKeyframe),
        video->mDisplay, texture ? texture->GetSize() : IntSize(),
        texture ? mParent->StoreImage(video->mImage, texture)
                : SurfaceDescriptorGPUVideo(0, null_t()),
        video->mFrameID);
    Unused << SendOutput(output);
  }
}

mozilla::ipc::IPCResult VideoDecoderParent::RecvFlush() {
  MOZ_ASSERT(!mDestroyed);
  MOZ_ASSERT(OnManagerThread());
  RefPtr<VideoDecoderParent> self = this;
  mDecoder->Flush()->Then(
      mManagerTaskQueue, __func__,
      [self]() {
        if (!self->mDestroyed) {
          Unused << self->SendFlushComplete();
        }
      },
      [self](const MediaResult& aError) { self->Error(aError); });

  return IPC_OK();
}

mozilla::ipc::IPCResult VideoDecoderParent::RecvDrain() {
  MOZ_ASSERT(!mDestroyed);
  MOZ_ASSERT(OnManagerThread());
  RefPtr<VideoDecoderParent> self = this;
  mDecoder->Drain()->Then(
      mManagerTaskQueue, __func__,
      [self, this](MediaDataDecoder::DecodedData&& aResults) {
        if (!mDestroyed) {
          ProcessDecodedData(std::move(aResults));
          Unused << SendDrainComplete();
        }
      },
      [self](const MediaResult& aError) { self->Error(aError); });
  return IPC_OK();
}

mozilla::ipc::IPCResult VideoDecoderParent::RecvShutdown() {
  MOZ_ASSERT(!mDestroyed);
  MOZ_ASSERT(OnManagerThread());
  if (mDecoder) {
    mDecoder->Shutdown();
  }
  mDecoder = nullptr;
  return IPC_OK();
}

mozilla::ipc::IPCResult VideoDecoderParent::RecvSetSeekThreshold(
    const int64_t& aTime) {
  MOZ_ASSERT(!mDestroyed);
  MOZ_ASSERT(OnManagerThread());
  mDecoder->SetSeekThreshold(TimeUnit::FromMicroseconds(aTime));
  return IPC_OK();
}

void VideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy) {
  MOZ_ASSERT(!mDestroyed);
  MOZ_ASSERT(OnManagerThread());
  if (mDecoder) {
    mDecoder->Shutdown();
    mDecoder = nullptr;
  }
  if (mDecodeTaskQueue) {
    mDecodeTaskQueue->BeginShutdown();
  }
}

void VideoDecoderParent::Error(const MediaResult& aError) {
  MOZ_ASSERT(OnManagerThread());
  if (!mDestroyed) {
    Unused << SendError(aError);
  }
}

bool VideoDecoderParent::OnManagerThread() {
  return mParent->OnManagerThread();
}

}  // namespace dom
}  // namespace mozilla