Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=99: */
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 : #include "VideoDecoderParent.h"
7 : #include "mozilla/Unused.h"
8 : #include "mozilla/layers/CompositorThread.h"
9 : #include "base/thread.h"
10 : #include "mozilla/layers/TextureClient.h"
11 : #include "mozilla/layers/VideoBridgeChild.h"
12 : #include "mozilla/layers/ImageClient.h"
13 : #include "MediaInfo.h"
14 : #include "VideoDecoderManagerParent.h"
15 : #ifdef XP_WIN
16 : #include "WMFDecoderModule.h"
17 : #endif
18 :
19 : namespace mozilla {
20 : namespace dom {
21 :
22 : using base::Thread;
23 : using media::TimeUnit;
24 : using namespace ipc;
25 : using namespace layers;
26 : using namespace gfx;
27 :
28 0 : class KnowsCompositorVideo : public layers::KnowsCompositor
29 : {
30 : public:
31 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(KnowsCompositorVideo, override)
32 :
33 0 : layers::TextureForwarder* GetTextureForwarder() override
34 : {
35 0 : return VideoBridgeChild::GetSingleton();
36 : }
37 0 : layers::LayersIPCActor* GetLayersIPCActor() override
38 : {
39 0 : return VideoBridgeChild::GetSingleton();
40 : }
41 : private:
42 0 : virtual ~KnowsCompositorVideo() = default;
43 : };
44 :
45 0 : VideoDecoderParent::VideoDecoderParent(VideoDecoderManagerParent* aParent,
46 : const VideoInfo& aVideoInfo,
47 : const layers::TextureFactoryIdentifier& aIdentifier,
48 : TaskQueue* aManagerTaskQueue,
49 : TaskQueue* aDecodeTaskQueue,
50 0 : bool* aSuccess)
51 : : mParent(aParent)
52 : , mManagerTaskQueue(aManagerTaskQueue)
53 : , mDecodeTaskQueue(aDecodeTaskQueue)
54 0 : , mKnowsCompositor(new KnowsCompositorVideo)
55 0 : , mDestroyed(false)
56 : {
57 0 : MOZ_COUNT_CTOR(VideoDecoderParent);
58 0 : MOZ_ASSERT(OnManagerThread());
59 : // We hold a reference to ourselves to keep us alive until IPDL
60 : // explictly destroys us. There may still be refs held by
61 : // tasks, but no new ones should be added after we're
62 : // destroyed.
63 0 : mIPDLSelfRef = this;
64 :
65 0 : mKnowsCompositor->IdentifyTextureHost(aIdentifier);
66 :
67 : #ifdef XP_WIN
68 : // TODO: Ideally we wouldn't hardcode the WMF PDM, and we'd use the normal PDM
69 : // factory logic for picking a decoder.
70 : WMFDecoderModule::Init();
71 : RefPtr<WMFDecoderModule> pdm(new WMFDecoderModule());
72 : pdm->Startup();
73 :
74 : CreateDecoderParams params(aVideoInfo);
75 : params.mTaskQueue = mDecodeTaskQueue;
76 : params.mKnowsCompositor = mKnowsCompositor;
77 : params.mImageContainer = new layers::ImageContainer();
78 :
79 : mDecoder = pdm->CreateVideoDecoder(params);
80 : #else
81 0 : MOZ_ASSERT(false,
82 : "Can't use RemoteVideoDecoder on non-Windows platforms yet");
83 : #endif
84 :
85 : *aSuccess = !!mDecoder;
86 : }
87 :
88 0 : VideoDecoderParent::~VideoDecoderParent()
89 : {
90 0 : MOZ_COUNT_DTOR(VideoDecoderParent);
91 0 : }
92 :
93 : void
94 0 : VideoDecoderParent::Destroy()
95 : {
96 0 : MOZ_ASSERT(OnManagerThread());
97 0 : mDecodeTaskQueue->AwaitShutdownAndIdle();
98 0 : mDestroyed = true;
99 0 : mIPDLSelfRef = nullptr;
100 0 : }
101 :
102 : mozilla::ipc::IPCResult
103 0 : VideoDecoderParent::RecvInit()
104 : {
105 0 : MOZ_ASSERT(OnManagerThread());
106 0 : RefPtr<VideoDecoderParent> self = this;
107 0 : mDecoder->Init()->Then(mManagerTaskQueue, __func__,
108 0 : [self] (TrackInfo::TrackType aTrack) {
109 0 : if (self->mDecoder) {
110 0 : nsCString hardwareReason;
111 : bool hardwareAccelerated =
112 0 : self->mDecoder->IsHardwareAccelerated(hardwareReason);
113 : uint32_t conversion =
114 0 : static_cast<uint32_t>(self->mDecoder->NeedsConversion());
115 0 : Unused << self->SendInitComplete(
116 : hardwareAccelerated, hardwareReason, conversion);
117 : }
118 0 : },
119 0 : [self] (MediaResult aReason) {
120 0 : if (!self->mDestroyed) {
121 0 : Unused << self->SendInitFailed(aReason);
122 : }
123 0 : });
124 0 : return IPC_OK();
125 : }
126 :
127 : mozilla::ipc::IPCResult
128 0 : VideoDecoderParent::RecvInput(const MediaRawDataIPDL& aData)
129 : {
130 0 : MOZ_ASSERT(OnManagerThread());
131 : // XXX: This copies the data into a buffer owned by the MediaRawData. Ideally
132 : // we'd just take ownership of the shmem.
133 0 : RefPtr<MediaRawData> data = new MediaRawData(aData.buffer().get<uint8_t>(),
134 0 : aData.buffer().Size<uint8_t>());
135 0 : if (aData.buffer().Size<uint8_t>() && !data->Data()) {
136 : // OOM
137 0 : Error(NS_ERROR_OUT_OF_MEMORY);
138 0 : return IPC_OK();
139 : }
140 0 : data->mOffset = aData.base().offset();
141 0 : data->mTime = TimeUnit::FromMicroseconds(aData.base().time());
142 0 : data->mTimecode = TimeUnit::FromMicroseconds(aData.base().timecode());
143 0 : data->mDuration = TimeUnit::FromMicroseconds(aData.base().duration());
144 0 : data->mKeyframe = aData.base().keyframe();
145 :
146 0 : DeallocShmem(aData.buffer());
147 :
148 0 : RefPtr<VideoDecoderParent> self = this;
149 0 : mDecoder->Decode(data)->Then(
150 : mManagerTaskQueue, __func__,
151 0 : [self, this](const MediaDataDecoder::DecodedData& aResults) {
152 0 : if (mDestroyed) {
153 0 : return;
154 : }
155 0 : ProcessDecodedData(aResults);
156 0 : Unused << SendInputExhausted();
157 : },
158 0 : [self, this](const MediaResult& aError) { Error(aError); });
159 0 : return IPC_OK();
160 : }
161 :
162 : void
163 0 : VideoDecoderParent::ProcessDecodedData(
164 : const MediaDataDecoder::DecodedData& aData)
165 : {
166 0 : MOZ_ASSERT(OnManagerThread());
167 :
168 : // If the video decoder bridge has shut down, stop.
169 0 : if (!mKnowsCompositor->GetTextureForwarder()) {
170 0 : return;
171 : }
172 :
173 0 : for (const auto& data : aData) {
174 0 : MOZ_ASSERT(data->mType == MediaData::VIDEO_DATA,
175 : "Can only decode videos using VideoDecoderParent!");
176 0 : VideoData* video = static_cast<VideoData*>(data.get());
177 :
178 0 : MOZ_ASSERT(video->mImage, "Decoded video must output a layer::Image to "
179 : "be used with VideoDecoderParent");
180 :
181 : RefPtr<TextureClient> texture =
182 0 : video->mImage->GetTextureClient(mKnowsCompositor);
183 :
184 0 : if (!texture) {
185 0 : texture = ImageClient::CreateTextureClientForImage(video->mImage,
186 0 : mKnowsCompositor);
187 : }
188 :
189 0 : if (texture && !texture->IsAddedToCompositableClient()) {
190 0 : texture->InitIPDLActor(mKnowsCompositor);
191 0 : texture->SetAddedToCompositableClient();
192 : }
193 :
194 : VideoDataIPDL output(
195 0 : MediaDataIPDL(data->mOffset, data->mTime.ToMicroseconds(),
196 0 : data->mTimecode.ToMicroseconds(),
197 0 : data->mDuration.ToMicroseconds(),
198 0 : data->mFrames, data->mKeyframe),
199 : video->mDisplay,
200 0 : texture ? texture->GetSize() : IntSize(),
201 0 : texture ? mParent->StoreImage(video->mImage, texture)
202 0 : : SurfaceDescriptorGPUVideo(0),
203 0 : video->mFrameID);
204 0 : Unused << SendOutput(output);
205 : }
206 : }
207 :
208 : mozilla::ipc::IPCResult
209 0 : VideoDecoderParent::RecvFlush()
210 : {
211 0 : MOZ_ASSERT(!mDestroyed);
212 0 : MOZ_ASSERT(OnManagerThread());
213 0 : RefPtr<VideoDecoderParent> self = this;
214 0 : mDecoder->Flush()->Then(
215 : mManagerTaskQueue, __func__,
216 0 : [self, this]() {
217 0 : if (!mDestroyed) {
218 0 : Unused << SendFlushComplete();
219 : }
220 0 : },
221 0 : [self, this](const MediaResult& aError) { Error(aError); });
222 :
223 0 : return IPC_OK();
224 : }
225 :
226 : mozilla::ipc::IPCResult
227 0 : VideoDecoderParent::RecvDrain()
228 : {
229 0 : MOZ_ASSERT(!mDestroyed);
230 0 : MOZ_ASSERT(OnManagerThread());
231 0 : RefPtr<VideoDecoderParent> self = this;
232 0 : mDecoder->Drain()->Then(
233 : mManagerTaskQueue, __func__,
234 0 : [self, this](const MediaDataDecoder::DecodedData& aResults) {
235 0 : if (!mDestroyed) {
236 0 : ProcessDecodedData(aResults);
237 0 : Unused << SendDrainComplete();
238 : }
239 0 : },
240 0 : [self, this](const MediaResult& aError) { Error(aError); });
241 0 : return IPC_OK();
242 : }
243 :
244 : mozilla::ipc::IPCResult
245 0 : VideoDecoderParent::RecvShutdown()
246 : {
247 0 : MOZ_ASSERT(!mDestroyed);
248 0 : MOZ_ASSERT(OnManagerThread());
249 0 : if (mDecoder) {
250 0 : mDecoder->Shutdown();
251 : }
252 0 : mDecoder = nullptr;
253 0 : return IPC_OK();
254 : }
255 :
256 : mozilla::ipc::IPCResult
257 0 : VideoDecoderParent::RecvSetSeekThreshold(const int64_t& aTime)
258 : {
259 0 : MOZ_ASSERT(!mDestroyed);
260 0 : MOZ_ASSERT(OnManagerThread());
261 0 : mDecoder->SetSeekThreshold(TimeUnit::FromMicroseconds(aTime));
262 0 : return IPC_OK();
263 : }
264 :
265 : void
266 0 : VideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy)
267 : {
268 0 : MOZ_ASSERT(!mDestroyed);
269 0 : MOZ_ASSERT(OnManagerThread());
270 0 : if (mDecoder) {
271 0 : mDecoder->Shutdown();
272 0 : mDecoder = nullptr;
273 : }
274 0 : if (mDecodeTaskQueue) {
275 0 : mDecodeTaskQueue->BeginShutdown();
276 : }
277 0 : }
278 :
279 : void
280 0 : VideoDecoderParent::Error(const MediaResult& aError)
281 : {
282 0 : MOZ_ASSERT(OnManagerThread());
283 0 : if (!mDestroyed) {
284 0 : Unused << SendError(aError);
285 : }
286 0 : }
287 :
288 : bool
289 0 : VideoDecoderParent::OnManagerThread()
290 : {
291 0 : return mParent->OnManagerThread();
292 : }
293 :
294 : } // namespace dom
295 : } // namespace mozilla
|