Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/platforms/wrappers/H264Converter.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim:set ts=2 sw=2 sts=2 et cindent: */
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
7
#include "H264Converter.h"
8
9
#include "DecoderDoctorDiagnostics.h"
10
#include "ImageContainer.h"
11
#include "MediaInfo.h"
12
#include "PDMFactory.h"
13
#include "mozilla/StaticPrefs.h"
14
#include "mozilla/TaskQueue.h"
15
#include "AnnexB.h"
16
#include "H264.h"
17
18
namespace mozilla
19
{
20
21
H264Converter::H264Converter(PlatformDecoderModule* aPDM,
22
                             const CreateDecoderParams& aParams)
23
  : mPDM(aPDM)
24
  , mOriginalConfig(aParams.VideoConfig())
25
  , mCurrentConfig(aParams.VideoConfig())
26
  , mKnowsCompositor(aParams.mKnowsCompositor)
27
  , mImageContainer(aParams.mImageContainer)
28
  , mTaskQueue(aParams.mTaskQueue)
29
  , mDecoder(nullptr)
30
  , mGMPCrashHelper(aParams.mCrashHelper)
31
  , mLastError(NS_OK)
32
  , mType(aParams.mType)
33
  , mOnWaitingForKeyEvent(aParams.mOnWaitingForKeyEvent)
34
  , mDecoderOptions(aParams.mOptions)
35
  , mRate(aParams.mRate)
36
0
{
37
0
  mLastError = CreateDecoder(mOriginalConfig, aParams.mDiagnostics);
38
0
  if (mDecoder) {
39
0
    MOZ_ASSERT(H264::HasSPS(mOriginalConfig.mExtraData));
40
0
    // The video metadata contains out of band SPS/PPS (AVC1) store it.
41
0
    mOriginalExtraData = mOriginalConfig.mExtraData;
42
0
  }
43
0
}
44
45
H264Converter::~H264Converter()
46
0
{
47
0
}
48
49
RefPtr<MediaDataDecoder::InitPromise>
50
H264Converter::Init()
51
0
{
52
0
  if (mDecoder) {
53
0
    return mDecoder->Init();
54
0
  }
55
0
56
0
  // We haven't been able to initialize a decoder due to a missing SPS/PPS.
57
0
  return MediaDataDecoder::InitPromise::CreateAndResolve(
58
0
           TrackType::kVideoTrack, __func__);
59
0
}
60
61
RefPtr<MediaDataDecoder::DecodePromise>
62
H264Converter::Decode(MediaRawData* aSample)
63
0
{
64
0
  MOZ_RELEASE_ASSERT(mFlushPromise.IsEmpty(), "Flush operatin didn't complete");
65
0
66
0
  MOZ_RELEASE_ASSERT(!mDecodePromiseRequest.Exists() &&
67
0
                       !mInitPromiseRequest.Exists(),
68
0
                     "Can't request a new decode until previous one completed");
69
0
70
0
  if (!AnnexB::ConvertSampleToAVCC(aSample)) {
71
0
    // We need AVCC content to be able to later parse the SPS.
72
0
    // This is a no-op if the data is already AVCC.
73
0
    return DecodePromise::CreateAndReject(
74
0
      MediaResult(NS_ERROR_OUT_OF_MEMORY, RESULT_DETAIL("ConvertSampleToAVCC")),
75
0
      __func__);
76
0
  }
77
0
78
0
  if (!AnnexB::IsAVCC(aSample)) {
79
0
    return DecodePromise::CreateAndReject(
80
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
81
0
                  RESULT_DETAIL("Invalid H264 content")),
82
0
      __func__);
83
0
  }
84
0
85
0
  MediaResult rv(NS_OK);
86
0
  if (!mDecoder) {
87
0
    // It is not possible to create an AVCC H264 decoder without SPS.
88
0
    // As such, creation will fail if the extra_data just extracted doesn't
89
0
    // contain a SPS.
90
0
    rv = CreateDecoderAndInit(aSample);
91
0
    if (rv == NS_ERROR_NOT_INITIALIZED) {
92
0
      // We are missing the required SPS to create the decoder.
93
0
      // Ignore for the time being, the MediaRawData will be dropped.
94
0
      return DecodePromise::CreateAndResolve(DecodedData(), __func__);
95
0
    }
96
0
  } else {
97
0
    // Initialize the members that we couldn't if the extradata was given during
98
0
    // H264Converter's construction.
99
0
    if (!mNeedAVCC) {
100
0
      mNeedAVCC =
101
0
        Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC);
102
0
    }
103
0
    if (!mCanRecycleDecoder) {
104
0
      mCanRecycleDecoder = Some(CanRecycleDecoder());
105
0
    }
106
0
    rv = CheckForSPSChange(aSample);
107
0
  }
108
0
109
0
  if (rv == NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER) {
110
0
    // The decoder is pending initialization.
111
0
    RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
112
0
    return p;
113
0
  }
114
0
115
0
  if (NS_FAILED(rv)) {
116
0
    return DecodePromise::CreateAndReject(rv, __func__);
117
0
  }
118
0
119
0
  if (mNeedKeyframe && !aSample->mKeyframe) {
120
0
    return DecodePromise::CreateAndResolve(DecodedData(), __func__);
121
0
  }
122
0
123
0
  auto res = !*mNeedAVCC
124
0
             ? AnnexB::ConvertSampleToAnnexB(aSample, mNeedKeyframe)
125
0
             : Ok();
126
0
  if (res.isErr()) {
127
0
    return DecodePromise::CreateAndReject(
128
0
      MediaResult(res.unwrapErr(), RESULT_DETAIL("ConvertSampleToAnnexB")),
129
0
      __func__);
130
0
  }
131
0
132
0
  mNeedKeyframe = false;
133
0
134
0
  aSample->mExtraData = mCurrentConfig.mExtraData;
135
0
136
0
  return mDecoder->Decode(aSample);
137
0
}
138
139
RefPtr<MediaDataDecoder::FlushPromise>
140
H264Converter::Flush()
141
0
{
142
0
  mDecodePromiseRequest.DisconnectIfExists();
143
0
  mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
144
0
  mNeedKeyframe = true;
145
0
  mPendingFrames.Clear();
146
0
147
0
  MOZ_RELEASE_ASSERT(mFlushPromise.IsEmpty(), "Previous flush didn't complete");
148
0
149
0
  /*
150
0
    When we detect a change of content in the H264 stream, we first drain the
151
0
    current decoder (1), flush (2), shut it down (3) create a new decoder and
152
0
    initialize it (4). It is possible for H264Converter::Flush to be called
153
0
    during any of those times.
154
0
    If during (1):
155
0
      - mDrainRequest will not be empty.
156
0
      - The old decoder can still be used, with the current extradata as stored
157
0
        in mCurrentConfig.mExtraData.
158
0
159
0
    If during (2):
160
0
      - mFlushRequest will not be empty.
161
0
      - The old decoder can still be used, with the current extradata as stored
162
0
        in mCurrentConfig.mExtraData.
163
0
164
0
    If during (3):
165
0
      - mShutdownRequest won't be empty.
166
0
      - mDecoder is empty.
167
0
      - The old decoder is no longer referenced by the H264Converter.
168
0
169
0
    If during (4):
170
0
      - mInitPromiseRequest won't be empty.
171
0
      - mDecoder is set but not usable yet.
172
0
  */
173
0
174
0
  if (mDrainRequest.Exists() || mFlushRequest.Exists() ||
175
0
      mShutdownRequest.Exists() || mInitPromiseRequest.Exists()) {
176
0
    // We let the current decoder complete and will resume after.
177
0
    return mFlushPromise.Ensure(__func__);
178
0
  }
179
0
  if (mDecoder) {
180
0
    return mDecoder->Flush();
181
0
  }
182
0
  return FlushPromise::CreateAndResolve(true, __func__);
183
0
}
184
185
RefPtr<MediaDataDecoder::DecodePromise>
186
H264Converter::Drain()
187
0
{
188
0
  MOZ_RELEASE_ASSERT(!mDrainRequest.Exists());
189
0
  mNeedKeyframe = true;
190
0
  if (mDecoder) {
191
0
    return mDecoder->Drain();
192
0
  }
193
0
  return DecodePromise::CreateAndResolve(DecodedData(), __func__);
194
0
}
195
196
RefPtr<ShutdownPromise>
197
H264Converter::Shutdown()
198
0
{
199
0
  mInitPromiseRequest.DisconnectIfExists();
200
0
  mDecodePromiseRequest.DisconnectIfExists();
201
0
  mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
202
0
  mDrainRequest.DisconnectIfExists();
203
0
  mFlushRequest.DisconnectIfExists();
204
0
  mFlushPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
205
0
  mShutdownRequest.DisconnectIfExists();
206
0
207
0
  if (mShutdownPromise) {
208
0
    // We have a shutdown in progress, return that promise instead as we can't
209
0
    // shutdown a decoder twice.
210
0
    return mShutdownPromise.forget();
211
0
  }
212
0
  return ShutdownDecoder();
213
0
}
214
215
RefPtr<ShutdownPromise>
216
H264Converter::ShutdownDecoder()
217
0
{
218
0
  mNeedAVCC.reset();
219
0
  if (mDecoder) {
220
0
    RefPtr<MediaDataDecoder> decoder = mDecoder.forget();
221
0
    return decoder->Shutdown();
222
0
  }
223
0
  return ShutdownPromise::CreateAndResolve(true, __func__);
224
0
}
225
226
bool
227
H264Converter::IsHardwareAccelerated(nsACString& aFailureReason) const
228
0
{
229
0
  if (mDecoder) {
230
0
    return mDecoder->IsHardwareAccelerated(aFailureReason);
231
0
  }
232
#ifdef MOZ_APPLEMEDIA
233
  // On mac, we can assume H264 is hardware accelerated for now.
234
  // This allows MediaCapabilities to report that playback will be smooth.
235
  // Which will always be.
236
  return true;
237
#else
238
0
  return MediaDataDecoder::IsHardwareAccelerated(aFailureReason);
239
0
#endif
240
0
}
241
242
void
243
H264Converter::SetSeekThreshold(const media::TimeUnit& aTime)
244
0
{
245
0
  if (mDecoder) {
246
0
    mDecoder->SetSeekThreshold(aTime);
247
0
  } else {
248
0
    MediaDataDecoder::SetSeekThreshold(aTime);
249
0
  }
250
0
}
251
252
MediaResult
253
H264Converter::CreateDecoder(const VideoInfo& aConfig,
254
                             DecoderDoctorDiagnostics* aDiagnostics)
255
0
{
256
0
  if (!H264::HasSPS(aConfig.mExtraData)) {
257
0
    // nothing found yet, will try again later
258
0
    return NS_ERROR_NOT_INITIALIZED;
259
0
  }
260
0
  UpdateConfigFromExtraData(aConfig.mExtraData);
261
0
262
0
  SPSData spsdata;
263
0
  if (H264::DecodeSPSFromExtraData(aConfig.mExtraData, spsdata)) {
264
0
    // Do some format check here.
265
0
    // WMF H.264 Video Decoder and Apple ATDecoder do not support YUV444 format.
266
0
    if (spsdata.profile_idc == 244 /* Hi444PP */ ||
267
0
        spsdata.chroma_format_idc == PDMFactory::kYUV444) {
268
0
      if (aDiagnostics) {
269
0
        aDiagnostics->SetVideoNotSupported();
270
0
      }
271
0
      return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
272
0
                         RESULT_DETAIL("No support for YUV444 format."));
273
0
    }
274
0
  } else {
275
0
    return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
276
0
                       RESULT_DETAIL("Invalid SPS NAL."));
277
0
  }
278
0
279
0
  MediaResult error = NS_OK;
280
0
  mDecoder = mPDM->CreateVideoDecoder({
281
0
    aConfig,
282
0
    mTaskQueue,
283
0
    aDiagnostics,
284
0
    mImageContainer,
285
0
    mKnowsCompositor,
286
0
    mGMPCrashHelper,
287
0
    mType,
288
0
    mOnWaitingForKeyEvent,
289
0
    mDecoderOptions,
290
0
    mRate,
291
0
    &error
292
0
  });
293
0
294
0
  if (!mDecoder) {
295
0
    if (NS_FAILED(error)) {
296
0
      // The decoder supports CreateDecoderParam::mError, returns the value.
297
0
      return error;
298
0
    } else {
299
0
      return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
300
0
                         RESULT_DETAIL("Unable to create H264 decoder"));
301
0
    }
302
0
  }
303
0
304
0
  DDLINKCHILD("decoder", mDecoder.get());
305
0
306
0
  mNeedKeyframe = true;
307
0
308
0
  return NS_OK;
309
0
}
310
311
MediaResult
312
H264Converter::CreateDecoderAndInit(MediaRawData* aSample)
313
0
{
314
0
  RefPtr<MediaByteBuffer> extra_data =
315
0
    H264::ExtractExtraData(aSample);
316
0
  bool inbandExtradata = H264::HasSPS(extra_data);
317
0
  if (!inbandExtradata &&
318
0
      !H264::HasSPS(mCurrentConfig.mExtraData)) {
319
0
    return NS_ERROR_NOT_INITIALIZED;
320
0
  }
321
0
322
0
  if (inbandExtradata) {
323
0
    UpdateConfigFromExtraData(extra_data);
324
0
  }
325
0
326
0
  MediaResult rv =
327
0
    CreateDecoder(mCurrentConfig, /* DecoderDoctorDiagnostics* */ nullptr);
328
0
329
0
  if (NS_SUCCEEDED(rv)) {
330
0
    RefPtr<H264Converter> self = this;
331
0
    RefPtr<MediaRawData> sample = aSample;
332
0
    mDecoder->Init()
333
0
      ->Then(
334
0
        AbstractThread::GetCurrent()->AsTaskQueue(),
335
0
        __func__,
336
0
        [self, sample, this](const TrackType aTrackType) {
337
0
          mInitPromiseRequest.Complete();
338
0
          mNeedAVCC =
339
0
            Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC);
340
0
          mCanRecycleDecoder = Some(CanRecycleDecoder());
341
0
342
0
          if (!mFlushPromise.IsEmpty()) {
343
0
            // A Flush is pending, abort the current operation.
344
0
            mFlushPromise.Resolve(true, __func__);
345
0
            return;
346
0
          }
347
0
348
0
          DecodeFirstSample(sample);
349
0
        },
350
0
        [self, this](const MediaResult& aError) {
351
0
          mInitPromiseRequest.Complete();
352
0
353
0
          if (!mFlushPromise.IsEmpty()) {
354
0
            // A Flush is pending, abort the current operation.
355
0
            mFlushPromise.Reject(aError, __func__);
356
0
            return;
357
0
          }
358
0
359
0
          mDecodePromise.Reject(
360
0
            MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
361
0
                        RESULT_DETAIL("Unable to initialize H264 decoder")),
362
0
            __func__);
363
0
        })
364
0
      ->Track(mInitPromiseRequest);
365
0
    return NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER;
366
0
  }
367
0
  return rv;
368
0
}
369
370
bool
371
H264Converter::CanRecycleDecoder() const
372
0
{
373
0
  MOZ_ASSERT(mDecoder);
374
0
  return StaticPrefs::MediaDecoderRecycleEnabled() &&
375
0
         mDecoder->SupportDecoderRecycling();
376
0
}
377
378
void
379
H264Converter::DecodeFirstSample(MediaRawData* aSample)
380
0
{
381
0
  if (mNeedKeyframe && !aSample->mKeyframe) {
382
0
    mDecodePromise.Resolve(mPendingFrames, __func__);
383
0
    mPendingFrames.Clear();
384
0
    return;
385
0
  }
386
0
387
0
  auto res = !*mNeedAVCC
388
0
             ? AnnexB::ConvertSampleToAnnexB(aSample, mNeedKeyframe)
389
0
             : Ok();
390
0
  if (res.isErr()) {
391
0
    mDecodePromise.Reject(
392
0
      MediaResult(res.unwrapErr(), RESULT_DETAIL("ConvertSampleToAnnexB")),
393
0
      __func__);
394
0
    return;
395
0
  }
396
0
397
0
  mNeedKeyframe = false;
398
0
399
0
  RefPtr<H264Converter> self = this;
400
0
  mDecoder->Decode(aSample)
401
0
    ->Then(AbstractThread::GetCurrent()->AsTaskQueue(), __func__,
402
0
           [self, this](const MediaDataDecoder::DecodedData& aResults) {
403
0
             mDecodePromiseRequest.Complete();
404
0
             mPendingFrames.AppendElements(aResults);
405
0
             mDecodePromise.Resolve(mPendingFrames, __func__);
406
0
             mPendingFrames.Clear();
407
0
           },
408
0
           [self, this](const MediaResult& aError) {
409
0
             mDecodePromiseRequest.Complete();
410
0
             mDecodePromise.Reject(aError, __func__);
411
0
           })
412
0
    ->Track(mDecodePromiseRequest);
413
0
}
414
415
MediaResult
416
H264Converter::CheckForSPSChange(MediaRawData* aSample)
417
0
{
418
0
  RefPtr<MediaByteBuffer> extra_data =
419
0
    aSample->mKeyframe ? H264::ExtractExtraData(aSample) : nullptr;
420
0
  if (!H264::HasSPS(extra_data)) {
421
0
    MOZ_ASSERT(mCanRecycleDecoder.isSome());
422
0
    if (!*mCanRecycleDecoder) {
423
0
      // If the decoder can't be recycled, the out of band extradata will never
424
0
      // change as the H264Converter will be recreated by the MediaFormatReader
425
0
      // instead. So there's no point in testing for changes.
426
0
      return NS_OK;
427
0
    }
428
0
    // This sample doesn't contain inband SPS/PPS
429
0
    // We now check if the out of band one has changed.
430
0
    // This scenario can only occur on Android with devices that can recycle a
431
0
    // decoder.
432
0
    if (!H264::HasSPS(aSample->mExtraData) ||
433
0
        H264::CompareExtraData(aSample->mExtraData, mOriginalExtraData)) {
434
0
      return NS_OK;
435
0
    }
436
0
    extra_data = mOriginalExtraData = aSample->mExtraData;
437
0
  }
438
0
  if (H264::CompareExtraData(extra_data, mCurrentConfig.mExtraData)) {
439
0
    return NS_OK;
440
0
  }
441
0
442
0
  MOZ_ASSERT(mCanRecycleDecoder.isSome());
443
0
  if (*mCanRecycleDecoder) {
444
0
    // Do not recreate the decoder, reuse it.
445
0
    UpdateConfigFromExtraData(extra_data);
446
0
    if (!aSample->mTrackInfo) {
447
0
      aSample->mTrackInfo = new TrackInfoSharedPtr(mCurrentConfig, 0);
448
0
    }
449
0
    mNeedKeyframe = true;
450
0
    return NS_OK;
451
0
  }
452
0
453
0
  // The SPS has changed, signal to drain the current decoder and once done
454
0
  // create a new one.
455
0
  DrainThenFlushDecoder(aSample);
456
0
  return NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER;
457
0
}
458
459
void
460
H264Converter::DrainThenFlushDecoder(MediaRawData* aPendingSample)
461
0
{
462
0
  RefPtr<MediaRawData> sample = aPendingSample;
463
0
  RefPtr<H264Converter> self = this;
464
0
  mDecoder->Drain()
465
0
    ->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
466
0
           __func__,
467
0
           [self, sample, this](const MediaDataDecoder::DecodedData& aResults) {
468
0
             mDrainRequest.Complete();
469
0
             if (!mFlushPromise.IsEmpty()) {
470
0
               // A Flush is pending, abort the current operation.
471
0
               mFlushPromise.Resolve(true, __func__);
472
0
               return;
473
0
             }
474
0
             if (aResults.Length() > 0) {
475
0
               mPendingFrames.AppendElements(aResults);
476
0
               DrainThenFlushDecoder(sample);
477
0
               return;
478
0
             }
479
0
             // We've completed the draining operation, we can now flush the
480
0
             // decoder.
481
0
             FlushThenShutdownDecoder(sample);
482
0
           },
483
0
           [self, this](const MediaResult& aError) {
484
0
             mDrainRequest.Complete();
485
0
             if (!mFlushPromise.IsEmpty()) {
486
0
               // A Flush is pending, abort the current operation.
487
0
               mFlushPromise.Reject(aError, __func__);
488
0
               return;
489
0
             }
490
0
             mDecodePromise.Reject(aError, __func__);
491
0
           })
492
0
    ->Track(mDrainRequest);
493
0
}
494
495
void H264Converter::FlushThenShutdownDecoder(MediaRawData* aPendingSample)
496
0
{
497
0
  RefPtr<MediaRawData> sample = aPendingSample;
498
0
  RefPtr<H264Converter> self = this;
499
0
  mDecoder->Flush()
500
0
    ->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
501
0
           __func__,
502
0
           [self, sample, this]() {
503
0
             mFlushRequest.Complete();
504
0
505
0
             if (!mFlushPromise.IsEmpty()) {
506
0
               // A Flush is pending, abort the current operation.
507
0
               mFlushPromise.Resolve(true, __func__);
508
0
               return;
509
0
             }
510
0
511
0
             mShutdownPromise = ShutdownDecoder();
512
0
             mShutdownPromise
513
0
               ->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
514
0
                      __func__,
515
0
                      [self, sample, this]() {
516
0
                        mShutdownRequest.Complete();
517
0
                        mShutdownPromise = nullptr;
518
0
519
0
                        if (!mFlushPromise.IsEmpty()) {
520
0
                          // A Flush is pending, abort the current operation.
521
0
                          mFlushPromise.Resolve(true, __func__);
522
0
                          return;
523
0
                        }
524
0
525
0
                        MediaResult rv = CreateDecoderAndInit(sample);
526
0
                        if (rv == NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER) {
527
0
                          // All good so far, will continue later.
528
0
                          return;
529
0
                        }
530
0
                        MOZ_ASSERT(NS_FAILED(rv));
531
0
                        mDecodePromise.Reject(rv, __func__);
532
0
                        return;
533
0
                      },
534
0
                      [] { MOZ_CRASH("Can't reach here'"); })
535
0
               ->Track(mShutdownRequest);
536
0
           },
537
0
           [self, this](const MediaResult& aError) {
538
0
             mFlushRequest.Complete();
539
0
             if (!mFlushPromise.IsEmpty()) {
540
0
               // A Flush is pending, abort the current operation.
541
0
               mFlushPromise.Reject(aError, __func__);
542
0
               return;
543
0
             }
544
0
             mDecodePromise.Reject(aError, __func__);
545
0
           })
546
0
    ->Track(mFlushRequest);
547
0
}
548
549
void
550
H264Converter::UpdateConfigFromExtraData(MediaByteBuffer* aExtraData)
551
0
{
552
0
  SPSData spsdata;
553
0
  if (H264::DecodeSPSFromExtraData(aExtraData, spsdata) &&
554
0
      spsdata.pic_width > 0 && spsdata.pic_height > 0) {
555
0
    H264::EnsureSPSIsSane(spsdata);
556
0
    mCurrentConfig.mImage.width = spsdata.pic_width;
557
0
    mCurrentConfig.mImage.height = spsdata.pic_height;
558
0
    mCurrentConfig.mDisplay.width = spsdata.display_width;
559
0
    mCurrentConfig.mDisplay.height = spsdata.display_height;
560
0
  }
561
0
  mCurrentConfig.mExtraData = aExtraData;
562
0
}
563
564
} // namespace mozilla