Bug 1288618 - Part 14: Add PVideoDecoder protocol for individual decoders. r=cpearce,dvander

This commit is contained in:
Matt Woodrow 2016-09-15 23:18:00 +12:00
Родитель 5fd458fdfa
Коммит 637309d62d
11 изменённых файлов: 706 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "mozilla/dom/MediaIPCUtils.h";
include protocol PVideoDecoderManager;
include LayersSurfaces;
using VideoInfo from "MediaInfo.h";
using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
namespace mozilla {
namespace dom {
struct MediaDataIPDL
{
int64_t offset;
int64_t time;
int64_t timecode;
int64_t duration;
uint32_t frames;
bool keyframe;
};
struct VideoDataIPDL
{
MediaDataIPDL base;
IntSize display;
SurfaceDescriptorGPUVideo sd;
int32_t frameID;
};
struct MediaRawDataIPDL
{
MediaDataIPDL base;
Shmem buffer;
};
// This protocol provides a way to use MediaDataDecoder/MediaDataDecoderCallback
// across processes. The parent side currently is only implemented to work with
// Window Media Foundation, but can be extended easily to support other backends.
// The child side runs in the content process, and the parent side runs in the
// GPU process. We run a separate IPDL thread for both sides.
async protocol PVideoDecoder
{
manager PVideoDecoderManager;
parent:
async Init(VideoInfo info, LayersBackend backend);
async Input(MediaRawDataIPDL data);
async Flush();
async Drain();
async Shutdown();
async __delete__();
child:
async InitComplete();
async InitFailed(nsresult reason);
// Each output includes a SurfaceDescriptorGPUVideo that represents the decoded
// frame. This SurfaceDescriptor can be used on the Layers IPDL protocol, but
// must be released explicitly using DeallocateSurfaceDescriptorGPUVideo
// on the manager protocol.
async Output(VideoDataIPDL data);
async InputExhausted();
async DrainComplete();
async Error(nsresult error);
};
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -3,6 +3,7 @@
* 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 protocol PVideoDecoder;
include LayersSurfaces;
include "mozilla/dom/MediaIPCUtils.h";
@ -11,7 +12,9 @@ namespace dom {
async protocol PVideoDecoderManager
{
manages PVideoDecoder;
parent:
async PVideoDecoder();
async DeallocateSurfaceDescriptorGPUVideo(SurfaceDescriptorGPUVideo sd);
};

Просмотреть файл

@ -0,0 +1,208 @@
/* -*- 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 "VideoDecoderChild.h"
#include "VideoDecoderManagerChild.h"
#include "mozilla/layers/TextureClient.h"
#include "base/thread.h"
#include "MediaInfo.h"
#include "ImageContainer.h"
#include "GPUVideoImage.h"
namespace mozilla {
namespace dom {
using base::Thread;
using namespace ipc;
using namespace layers;
using namespace gfx;
VideoDecoderChild::VideoDecoderChild()
: mThread(VideoDecoderManagerChild::GetManagerThread())
, mCanSend(true)
{
}
VideoDecoderChild::~VideoDecoderChild()
{
AssertOnManagerThread();
mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
}
bool
VideoDecoderChild::RecvOutput(const VideoDataIPDL& aData)
{
AssertOnManagerThread();
VideoInfo info(aData.display().width, aData.display().height);
// The Image here creates a TextureData object that takes ownership
// of the SurfaceDescriptor, and is responsible for making sure that
// it gets deallocated.
RefPtr<Image> image = new GPUVideoImage(aData.sd(), aData.display());
RefPtr<VideoData> video = VideoData::CreateFromImage(info,
aData.base().offset(),
aData.base().time(),
aData.base().duration(),
image,
aData.base().keyframe(),
aData.base().timecode(),
IntRect());
mCallback->Output(video);
return true;
}
bool
VideoDecoderChild::RecvInputExhausted()
{
AssertOnManagerThread();
mCallback->InputExhausted();
return true;
}
bool
VideoDecoderChild::RecvDrainComplete()
{
AssertOnManagerThread();
mCallback->DrainComplete();
return true;
}
bool
VideoDecoderChild::RecvError(const nsresult& aError)
{
AssertOnManagerThread();
mCallback->Error(aError);
return true;
}
bool
VideoDecoderChild::RecvInitComplete()
{
AssertOnManagerThread();
mInitPromise.Resolve(TrackInfo::kVideoTrack, __func__);
return true;
}
bool
VideoDecoderChild::RecvInitFailed(const nsresult& aReason)
{
AssertOnManagerThread();
mInitPromise.Reject(aReason, __func__);
return true;
}
void
VideoDecoderChild::ActorDestroy(ActorDestroyReason aWhy)
{
mCanSend = false;
}
void
VideoDecoderChild::InitIPDL(MediaDataDecoderCallback* aCallback,
const VideoInfo& aVideoInfo,
layers::LayersBackend aLayersBackend)
{
VideoDecoderManagerChild::GetSingleton()->SendPVideoDecoderConstructor(this);
mIPDLSelfRef = this;
mCallback = aCallback;
mVideoInfo = aVideoInfo;
mLayersBackend = aLayersBackend;
}
void
VideoDecoderChild::DestroyIPDL()
{
if (mCanSend) {
PVideoDecoderChild::Send__delete__(this);
}
}
void
VideoDecoderChild::IPDLActorDestroyed()
{
mIPDLSelfRef = nullptr;
}
// MediaDataDecoder methods
RefPtr<MediaDataDecoder::InitPromise>
VideoDecoderChild::Init()
{
AssertOnManagerThread();
if (!mCanSend || !SendInit(mVideoInfo, mLayersBackend)) {
return MediaDataDecoder::InitPromise::CreateAndReject(
NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
}
return mInitPromise.Ensure(__func__);
}
void
VideoDecoderChild::Input(MediaRawData* aSample)
{
AssertOnManagerThread();
if (!mCanSend) {
mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
return;
}
// TODO: It would be nice to add an allocator method to
// MediaDataDecoder so that the demuxer could write directly
// into shmem rather than requiring a copy here.
Shmem buffer;
if (!AllocShmem(aSample->Size(), Shmem::SharedMemory::TYPE_BASIC, &buffer)) {
mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
return;
}
memcpy(buffer.get<uint8_t>(), aSample->Data(), aSample->Size());
MediaRawDataIPDL sample(MediaDataIPDL(aSample->mOffset,
aSample->mTime,
aSample->mTimecode,
aSample->mDuration,
aSample->mFrames,
aSample->mKeyframe),
buffer);
if (!SendInput(sample)) {
mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
}
}
void
VideoDecoderChild::Flush()
{
AssertOnManagerThread();
if (!mCanSend || !SendFlush()) {
mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
}
}
void
VideoDecoderChild::Drain()
{
AssertOnManagerThread();
if (!mCanSend || !SendDrain()) {
mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
}
}
void
VideoDecoderChild::Shutdown()
{
AssertOnManagerThread();
if (!mCanSend || !SendShutdown()) {
mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
}
}
void
VideoDecoderChild::AssertOnManagerThread()
{
MOZ_ASSERT(NS_GetCurrentThread() == mThread);
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,71 @@
/* -*- 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/. */
#ifndef include_dom_ipc_VideoDecoderChild_h
#define include_dom_ipc_VideoDecoderChild_h
#include "mozilla/RefPtr.h"
#include "mozilla/dom/PVideoDecoderChild.h"
#include "MediaData.h"
#include "PlatformDecoderModule.h"
namespace mozilla {
namespace dom {
class RemoteVideoDecoder;
class RemoteDecoderModule;
class VideoDecoderChild final : public PVideoDecoderChild
{
public:
explicit VideoDecoderChild();
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderChild)
// PVideoDecoderChild
bool RecvOutput(const VideoDataIPDL& aData) override;
bool RecvInputExhausted() override;
bool RecvDrainComplete() override;
bool RecvError(const nsresult& aError) override;
bool RecvInitComplete() override;
bool RecvInitFailed(const nsresult& aReason) override;
void ActorDestroy(ActorDestroyReason aWhy) override;
RefPtr<MediaDataDecoder::InitPromise> Init();
void Input(MediaRawData* aSample);
void Flush();
void Drain();
void Shutdown();
void InitIPDL(MediaDataDecoderCallback* aCallback,
const VideoInfo& aVideoInfo,
layers::LayersBackend aLayersBackend);
void DestroyIPDL();
// Called from IPDL when our actor has been destroyed
void IPDLActorDestroyed();
private:
~VideoDecoderChild();
void AssertOnManagerThread();
RefPtr<VideoDecoderChild> mIPDLSelfRef;
RefPtr<nsIThread> mThread;
MediaDataDecoderCallback* mCallback;
MozPromiseHolder<MediaDataDecoder::InitPromise> mInitPromise;
VideoInfo mVideoInfo;
layers::LayersBackend mLayersBackend;
bool mCanSend;
};
} // namespace dom
} // namespace mozilla
#endif // include_dom_ipc_VideoDecoderChild_h

Просмотреть файл

@ -4,6 +4,7 @@
* 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 "VideoDecoderManagerChild.h"
#include "VideoDecoderChild.h"
#include "mozilla/dom/ContentChild.h"
#include "MediaPrefs.h"
#include "nsThreadUtils.h"
@ -87,6 +88,26 @@ VideoDecoderManagerChild::GetSingleton()
return sDecoderManager;
}
/* static */ nsIThread*
VideoDecoderManagerChild::GetManagerThread()
{
return sVideoDecoderChildThread;
}
PVideoDecoderChild*
VideoDecoderManagerChild::AllocPVideoDecoderChild()
{
return new VideoDecoderChild();
}
bool
VideoDecoderManagerChild::DeallocPVideoDecoderChild(PVideoDecoderChild* actor)
{
VideoDecoderChild* child = static_cast<VideoDecoderChild*>(actor);
child->IPDLActorDestroyed();
return true;
}
void
VideoDecoderManagerChild::Open(Endpoint<PVideoDecoderManagerChild>&& aEndpoint)
{

Просмотреть файл

@ -18,6 +18,7 @@ public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderManagerChild)
static VideoDecoderManagerChild* GetSingleton();
static nsIThread* GetManagerThread();
// Can be called from any thread, dispatches the request to the IPDL thread internally.
void DeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD);
@ -28,6 +29,10 @@ public:
static void Initialize();
static void Shutdown();
protected:
PVideoDecoderChild* AllocPVideoDecoderChild() override;
bool DeallocPVideoDecoderChild(PVideoDecoderChild* actor) override;
private:
VideoDecoderManagerChild()
{}

Просмотреть файл

@ -4,6 +4,7 @@
* 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 "VideoDecoderManagerParent.h"
#include "VideoDecoderParent.h"
#include "base/thread.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/UniquePtr.h"
@ -159,6 +160,12 @@ VideoDecoderManagerParent::ShutdownThreads()
sVideoDecoderManagerThread = nullptr;
}
bool
VideoDecoderManagerParent::OnManagerThread()
{
return NS_GetCurrentThread() == sVideoDecoderManagerThread;
}
bool
VideoDecoderManagerParent::CreateForContent(Endpoint<PVideoDecoderManagerParent>&& aEndpoint)
{
@ -190,6 +197,21 @@ VideoDecoderManagerParent::~VideoDecoderManagerParent()
ClearAllOwnedImages();
}
PVideoDecoderParent*
VideoDecoderManagerParent::AllocPVideoDecoderParent()
{
RefPtr<nsIEventTarget> target = sVideoDecoderTaskThread;;
return new VideoDecoderParent(this, sManagerTaskQueue, new TaskQueue(target.forget()));
}
bool
VideoDecoderManagerParent::DeallocPVideoDecoderParent(PVideoDecoderParent* actor)
{
VideoDecoderParent* parent = static_cast<VideoDecoderParent*>(actor);
parent->Destroy();
return true;
}
void
VideoDecoderManagerParent::Open(Endpoint<PVideoDecoderManagerParent>&& aEndpoint)
{

Просмотреть файл

@ -25,7 +25,12 @@ public:
static void StartupThreads();
static void ShutdownThreads();
bool OnManagerThread();
protected:
PVideoDecoderParent* AllocPVideoDecoderParent() override;
bool DeallocPVideoDecoderParent(PVideoDecoderParent* actor) override;
bool RecvDeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD) override;
void ActorDestroy(mozilla::ipc::IProtocolManager<mozilla::ipc::IProtocol>::ActorDestroyReason) override {}

Просмотреть файл

@ -0,0 +1,227 @@
/* -*- 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/TextureHost.h"
#include "mozilla/layers/PTextureParent.h"
#include "MediaInfo.h"
#include "VideoDecoderManagerParent.h"
#ifdef XP_WIN
#include "WMFDecoderModule.h"
#endif
namespace mozilla {
namespace dom {
using base::Thread;
using namespace ipc;
using namespace layers;
using namespace gfx;
VideoDecoderParent::VideoDecoderParent(VideoDecoderManagerParent* aParent,
TaskQueue* aManagerTaskQueue,
TaskQueue* aDecodeTaskQueue)
: mParent(aParent)
, mManagerTaskQueue(aManagerTaskQueue)
, mDecodeTaskQueue(aDecodeTaskQueue)
, mDestroyed(false)
{
MOZ_COUNT_CTOR(VideoDecoderParent);
// 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;
}
VideoDecoderParent::~VideoDecoderParent()
{
MOZ_COUNT_DTOR(VideoDecoderParent);
}
void
VideoDecoderParent::Destroy()
{
mDecodeTaskQueue->AwaitShutdownAndIdle();
mDestroyed = true;
mIPDLSelfRef = nullptr;
}
bool
VideoDecoderParent::RecvInit(const VideoInfo& aInfo, const layers::LayersBackend& aBackend)
{
CreateDecoderParams params(aInfo);
params.mTaskQueue = mDecodeTaskQueue;
params.mCallback = this;
params.mLayersBackend = aBackend;
params.mImageContainer = new layers::ImageContainer();
#ifdef XP_WIN
// 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();
mDecoder = pdm->CreateVideoDecoder(params);
if (!mDecoder) {
Unused << SendInitFailed(NS_ERROR_DOM_MEDIA_FATAL_ERR);
return true;
}
#else
MOZ_ASSERT(false, "Can't use RemoteVideoDecoder on non-Windows platforms yet");
#endif
RefPtr<VideoDecoderParent> self = this;
mDecoder->Init()->Then(mManagerTaskQueue, __func__,
[self] (TrackInfo::TrackType aTrack) {
if (!self->mDestroyed) {
Unused << self->SendInitComplete();
}
},
[self] (MediaResult aReason) {
if (!self->mDestroyed) {
Unused << self->SendInitFailed(aReason);
}
});
return true;
}
bool
VideoDecoderParent::RecvInput(const MediaRawDataIPDL& aData)
{
// 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>());
data->mOffset = aData.base().offset();
data->mTime = aData.base().time();
data->mTimecode = aData.base().timecode();
data->mDuration = aData.base().duration();
data->mKeyframe = aData.base().keyframe();
DeallocShmem(aData.buffer());
mDecoder->Input(data);
return true;
}
bool
VideoDecoderParent::RecvFlush()
{
MOZ_ASSERT(!mDestroyed);
mDecoder->Flush();
return true;
}
bool
VideoDecoderParent::RecvDrain()
{
MOZ_ASSERT(!mDestroyed);
mDecoder->Drain();
return true;
}
bool
VideoDecoderParent::RecvShutdown()
{
MOZ_ASSERT(!mDestroyed);
mDecoder->Shutdown();
mDecoder = nullptr;
return true;
}
void
VideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy)
{
MOZ_ASSERT(!mDestroyed);
if (mDecoder) {
mDecoder->Shutdown();
mDecoder = nullptr;
}
if (mDecodeTaskQueue) {
mDecodeTaskQueue->BeginShutdown();
}
}
void
VideoDecoderParent::Output(MediaData* aData)
{
MOZ_ASSERT(mDecodeTaskQueue->IsCurrentThreadIn());
RefPtr<VideoDecoderParent> self = this;
RefPtr<MediaData> data = aData;
mManagerTaskQueue->Dispatch(NS_NewRunnableFunction([self, data]() {
if (self->mDestroyed) {
return;
}
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");
VideoDataIPDL output(MediaDataIPDL(data->mOffset,
data->mTime,
data->mTimecode,
data->mDuration,
data->mFrames,
data->mKeyframe),
video->mDisplay,
self->GetManager()->StoreImage(video->mImage),
video->mFrameID);
Unused << self->SendOutput(output);
}));
}
void
VideoDecoderParent::Error(const MediaResult& aError)
{
MOZ_ASSERT(mDecodeTaskQueue->IsCurrentThreadIn());
RefPtr<VideoDecoderParent> self = this;
MediaResult error = aError;
mManagerTaskQueue->Dispatch(NS_NewRunnableFunction([self, error]() {
if (!self->mDestroyed) {
Unused << self->SendError(error);
}
}));
}
void
VideoDecoderParent::InputExhausted()
{
MOZ_ASSERT(mDecodeTaskQueue->IsCurrentThreadIn());
RefPtr<VideoDecoderParent> self = this;
mManagerTaskQueue->Dispatch(NS_NewRunnableFunction([self]() {
if (!self->mDestroyed) {
Unused << self->SendInputExhausted();
}
}));
}
void
VideoDecoderParent::DrainComplete()
{
MOZ_ASSERT(mDecodeTaskQueue->IsCurrentThreadIn());
RefPtr<VideoDecoderParent> self = this;
mManagerTaskQueue->Dispatch(NS_NewRunnableFunction([self]() {
if (!self->mDestroyed) {
Unused << self->SendDrainComplete();
}
}));
}
bool
VideoDecoderParent::OnReaderTaskQueue()
{
// Most of our calls into mDecoder come directly from IPDL so are on
// the right thread, but not actually on the task queue. We only ever
// run a single thread, not a pool, so this should work fine.
return mParent->OnManagerThread();
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,66 @@
/* -*- 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/. */
#ifndef include_dom_ipc_VideoDecoderParent_h
#define include_dom_ipc_VideoDecoderParent_h
#include "mozilla/RefPtr.h"
#include "mozilla/dom/PVideoDecoderParent.h"
#include "VideoDecoderManagerParent.h"
#include "MediaData.h"
#include "ImageContainer.h"
namespace mozilla {
namespace dom {
class VideoDecoderParent final : public PVideoDecoderParent,
public MediaDataDecoderCallback
{
public:
// We refcount this class since the task queue can have runnables
// that reference us.
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderParent)
VideoDecoderParent(VideoDecoderManagerParent* aParent,
TaskQueue* aManagerTaskQueue,
TaskQueue* aDecodeTaskQueue);
void Destroy();
// PVideoDecoderParent
bool RecvInit(const VideoInfo& aVideoInfo, const layers::LayersBackend& aBackend) override;
bool RecvInput(const MediaRawDataIPDL& aData) override;
bool RecvFlush() override;
bool RecvDrain() override;
bool RecvShutdown() override;
void ActorDestroy(ActorDestroyReason aWhy) override;
// MediaDataDecoderCallback
void Output(MediaData* aData) override;
void Error(const MediaResult& aError) override;
void InputExhausted() override;
void DrainComplete() override;
bool OnReaderTaskQueue() override;
private:
~VideoDecoderParent();
VideoDecoderManagerParent* GetManager() { return static_cast<VideoDecoderManagerParent*>(Manager()); }
RefPtr<VideoDecoderManagerParent> mParent;
RefPtr<VideoDecoderParent> mIPDLSelfRef;
RefPtr<TaskQueue> mManagerTaskQueue;
RefPtr<TaskQueue> mDecodeTaskQueue;
RefPtr<MediaDataDecoder> mDecoder;
// Can only be accessed from the manager thread
bool mDestroyed;
};
} // namespace dom
} // namespace mozilla
#endif // include_dom_ipc_VideoDecoderParent_h

Просмотреть файл

@ -6,6 +6,7 @@
IPDL_SOURCES += [
'PVideoDecoder.ipdl',
'PVideoDecoderManager.ipdl',
]
@ -16,8 +17,10 @@ EXPORTS.mozilla.dom += [
]
SOURCES += [
'VideoDecoderChild.cpp',
'VideoDecoderManagerChild.cpp',
'VideoDecoderManagerParent.cpp',
'VideoDecoderParent.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')