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 "VideoDecoderChild.h"
7 : #include "VideoDecoderManagerChild.h"
8 : #include "mozilla/layers/TextureClient.h"
9 : #include "base/thread.h"
10 : #include "MediaInfo.h"
11 : #include "ImageContainer.h"
12 : #include "GPUVideoImage.h"
13 :
14 : namespace mozilla {
15 : namespace dom {
16 :
17 : using base::Thread;
18 : using namespace ipc;
19 : using namespace layers;
20 : using namespace gfx;
21 :
22 0 : VideoDecoderChild::VideoDecoderChild()
23 : : mThread(VideoDecoderManagerChild::GetManagerThread())
24 : , mCanSend(false)
25 : , mInitialized(false)
26 : , mIsHardwareAccelerated(false)
27 : , mConversion(MediaDataDecoder::ConversionRequired::kNeedNone)
28 0 : , mNeedNewDecoder(false)
29 : {
30 0 : }
31 :
32 0 : VideoDecoderChild::~VideoDecoderChild()
33 : {
34 0 : AssertOnManagerThread();
35 0 : mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
36 0 : }
37 :
38 : mozilla::ipc::IPCResult
39 0 : VideoDecoderChild::RecvOutput(const VideoDataIPDL& aData)
40 : {
41 0 : AssertOnManagerThread();
42 :
43 : // The Image here creates a TextureData object that takes ownership
44 : // of the SurfaceDescriptor, and is responsible for making sure that
45 : // it gets deallocated.
46 0 : RefPtr<Image> image = new GPUVideoImage(GetManager(), aData.sd(), aData.frameSize());
47 :
48 0 : RefPtr<VideoData> video = VideoData::CreateFromImage(
49 0 : aData.display(),
50 0 : aData.base().offset(),
51 0 : media::TimeUnit::FromMicroseconds(aData.base().time()),
52 0 : media::TimeUnit::FromMicroseconds(aData.base().duration()),
53 : image,
54 0 : aData.base().keyframe(),
55 0 : media::TimeUnit::FromMicroseconds(aData.base().timecode()));
56 :
57 0 : mDecodedData.AppendElement(Move(video));
58 0 : return IPC_OK();
59 : }
60 :
61 : mozilla::ipc::IPCResult
62 0 : VideoDecoderChild::RecvInputExhausted()
63 : {
64 0 : AssertOnManagerThread();
65 0 : mDecodePromise.ResolveIfExists(mDecodedData, __func__);
66 0 : mDecodedData.Clear();
67 0 : return IPC_OK();
68 : }
69 :
70 : mozilla::ipc::IPCResult
71 0 : VideoDecoderChild::RecvDrainComplete()
72 : {
73 0 : AssertOnManagerThread();
74 0 : mDrainPromise.ResolveIfExists(mDecodedData, __func__);
75 0 : mDecodedData.Clear();
76 0 : return IPC_OK();
77 : }
78 :
79 : mozilla::ipc::IPCResult
80 0 : VideoDecoderChild::RecvError(const nsresult& aError)
81 : {
82 0 : AssertOnManagerThread();
83 0 : mDecodedData.Clear();
84 0 : mDecodePromise.RejectIfExists(aError, __func__);
85 0 : mDrainPromise.RejectIfExists(aError, __func__);
86 0 : mFlushPromise.RejectIfExists(aError, __func__);
87 0 : return IPC_OK();
88 : }
89 :
90 : mozilla::ipc::IPCResult
91 0 : VideoDecoderChild::RecvInitComplete(const bool& aHardware,
92 : const nsCString& aHardwareReason,
93 : const uint32_t& aConversion)
94 : {
95 0 : AssertOnManagerThread();
96 0 : mInitPromise.ResolveIfExists(TrackInfo::kVideoTrack, __func__);
97 0 : mInitialized = true;
98 0 : mIsHardwareAccelerated = aHardware;
99 0 : mHardwareAcceleratedReason = aHardwareReason;
100 0 : mConversion = static_cast<MediaDataDecoder::ConversionRequired>(aConversion);
101 0 : return IPC_OK();
102 : }
103 :
104 : mozilla::ipc::IPCResult
105 0 : VideoDecoderChild::RecvInitFailed(const nsresult& aReason)
106 : {
107 0 : AssertOnManagerThread();
108 0 : mInitPromise.RejectIfExists(aReason, __func__);
109 0 : return IPC_OK();
110 : }
111 :
112 : mozilla::ipc::IPCResult
113 0 : VideoDecoderChild::RecvFlushComplete()
114 : {
115 0 : AssertOnManagerThread();
116 0 : mFlushPromise.ResolveIfExists(true, __func__);
117 0 : return IPC_OK();
118 : }
119 :
120 : void
121 0 : VideoDecoderChild::ActorDestroy(ActorDestroyReason aWhy)
122 : {
123 0 : if (aWhy == AbnormalShutdown) {
124 : // Defer reporting an error until we've recreated the manager so that
125 : // it'll be safe for MediaFormatReader to recreate decoders
126 0 : RefPtr<VideoDecoderChild> ref = this;
127 0 : GetManager()->RunWhenRecreated(
128 0 : NS_NewRunnableFunction("dom::VideoDecoderChild::ActorDestroy", [=]() {
129 0 : if (ref->mInitialized) {
130 0 : mDecodedData.Clear();
131 0 : mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER,
132 0 : __func__);
133 0 : mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER,
134 0 : __func__);
135 0 : mFlushPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER,
136 0 : __func__);
137 : // Make sure the next request will be rejected accordingly if ever
138 : // called.
139 0 : mNeedNewDecoder = true;
140 : } else {
141 0 : ref->mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER,
142 0 : __func__);
143 : }
144 0 : }));
145 : }
146 0 : mCanSend = false;
147 0 : }
148 :
149 : bool
150 0 : VideoDecoderChild::InitIPDL(const VideoInfo& aVideoInfo,
151 : const layers::TextureFactoryIdentifier& aIdentifier)
152 : {
153 : RefPtr<VideoDecoderManagerChild> manager =
154 0 : VideoDecoderManagerChild::GetSingleton();
155 : // If the manager isn't available, then don't initialize mIPDLSelfRef and
156 : // leave us in an error state. We'll then immediately reject the promise when
157 : // Init() is called and the caller can try again. Hopefully by then the new
158 : // manager is ready, or we've notified the caller of it being no longer
159 : // available. If not, then the cycle repeats until we're ready.
160 0 : if (!manager || !manager->CanSend()) {
161 0 : return true;
162 : }
163 :
164 0 : mIPDLSelfRef = this;
165 0 : bool success = false;
166 0 : if (manager->SendPVideoDecoderConstructor(this, aVideoInfo, aIdentifier,
167 : &success)) {
168 0 : mCanSend = true;
169 : }
170 0 : return success;
171 : }
172 :
173 : void
174 0 : VideoDecoderChild::DestroyIPDL()
175 : {
176 0 : if (mCanSend) {
177 0 : PVideoDecoderChild::Send__delete__(this);
178 : }
179 0 : }
180 :
181 : void
182 0 : VideoDecoderChild::IPDLActorDestroyed()
183 : {
184 0 : mIPDLSelfRef = nullptr;
185 0 : }
186 :
187 : // MediaDataDecoder methods
188 :
189 : RefPtr<MediaDataDecoder::InitPromise>
190 0 : VideoDecoderChild::Init()
191 : {
192 0 : AssertOnManagerThread();
193 :
194 0 : if (!mIPDLSelfRef) {
195 : return MediaDataDecoder::InitPromise::CreateAndReject(
196 0 : NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
197 : }
198 : // If we failed to send this, then we'll still resolve the Init promise
199 : // as ActorDestroy handles it.
200 0 : if (mCanSend) {
201 0 : SendInit();
202 : }
203 0 : return mInitPromise.Ensure(__func__);
204 : }
205 :
206 : RefPtr<MediaDataDecoder::DecodePromise>
207 0 : VideoDecoderChild::Decode(MediaRawData* aSample)
208 : {
209 0 : AssertOnManagerThread();
210 :
211 0 : if (mNeedNewDecoder) {
212 : return MediaDataDecoder::DecodePromise::CreateAndReject(
213 0 : NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER, __func__);
214 : }
215 0 : if (!mCanSend) {
216 : // We're here if the IPC channel has died but we're still waiting for the
217 : // RunWhenRecreated task to complete. The decode promise will be rejected
218 : // when that task is run.
219 0 : return mDecodePromise.Ensure(__func__);
220 : }
221 :
222 : // TODO: It would be nice to add an allocator method to
223 : // MediaDataDecoder so that the demuxer could write directly
224 : // into shmem rather than requiring a copy here.
225 0 : Shmem buffer;
226 0 : if (!AllocShmem(aSample->Size(), Shmem::SharedMemory::TYPE_BASIC, &buffer)) {
227 : return MediaDataDecoder::DecodePromise::CreateAndReject(
228 0 : NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
229 : }
230 :
231 0 : memcpy(buffer.get<uint8_t>(), aSample->Data(), aSample->Size());
232 :
233 0 : MediaRawDataIPDL sample(MediaDataIPDL(aSample->mOffset,
234 0 : aSample->mTime.ToMicroseconds(),
235 0 : aSample->mTimecode.ToMicroseconds(),
236 0 : aSample->mDuration.ToMicroseconds(),
237 : aSample->mFrames,
238 : aSample->mKeyframe),
239 0 : buffer);
240 0 : SendInput(sample);
241 0 : return mDecodePromise.Ensure(__func__);
242 : }
243 :
244 : RefPtr<MediaDataDecoder::FlushPromise>
245 0 : VideoDecoderChild::Flush()
246 : {
247 0 : AssertOnManagerThread();
248 0 : mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
249 0 : mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
250 0 : if (mNeedNewDecoder) {
251 : return MediaDataDecoder::FlushPromise::CreateAndReject(
252 0 : NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER, __func__);
253 : }
254 0 : if (mCanSend) {
255 0 : SendFlush();
256 : }
257 0 : return mFlushPromise.Ensure(__func__);
258 : }
259 :
260 : RefPtr<MediaDataDecoder::DecodePromise>
261 0 : VideoDecoderChild::Drain()
262 : {
263 0 : AssertOnManagerThread();
264 0 : if (mNeedNewDecoder) {
265 : return MediaDataDecoder::DecodePromise::CreateAndReject(
266 0 : NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER, __func__);
267 : }
268 0 : if (mCanSend) {
269 0 : SendDrain();
270 : }
271 0 : return mDrainPromise.Ensure(__func__);
272 : }
273 :
274 : void
275 0 : VideoDecoderChild::Shutdown()
276 : {
277 0 : AssertOnManagerThread();
278 0 : mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
279 0 : if (mCanSend) {
280 0 : SendShutdown();
281 : }
282 0 : mInitialized = false;
283 0 : }
284 :
285 : bool
286 0 : VideoDecoderChild::IsHardwareAccelerated(nsACString& aFailureReason) const
287 : {
288 0 : aFailureReason = mHardwareAcceleratedReason;
289 0 : return mIsHardwareAccelerated;
290 : }
291 :
292 : void
293 0 : VideoDecoderChild::SetSeekThreshold(const media::TimeUnit& aTime)
294 : {
295 0 : AssertOnManagerThread();
296 0 : if (mCanSend) {
297 0 : SendSetSeekThreshold(aTime.ToMicroseconds());
298 : }
299 0 : }
300 :
301 : MediaDataDecoder::ConversionRequired
302 0 : VideoDecoderChild::NeedsConversion() const
303 : {
304 0 : return mConversion;
305 : }
306 :
307 : void
308 0 : VideoDecoderChild::AssertOnManagerThread() const
309 : {
310 0 : MOZ_ASSERT(NS_GetCurrentThread() == mThread);
311 0 : }
312 :
313 : VideoDecoderManagerChild*
314 0 : VideoDecoderChild::GetManager()
315 : {
316 0 : if (!mCanSend) {
317 0 : return nullptr;
318 : }
319 0 : return static_cast<VideoDecoderManagerChild*>(Manager());
320 : }
321 :
322 : } // namespace dom
323 : } // namespace mozilla
|