void
WidevineVideoDecoder::InitDecode(const GMPVideoCodec& aCodecSettings,
                                 const uint8_t* aCodecSpecific,
                                 uint32_t aCodecSpecificLength,
                                 GMPVideoDecoderCallback* aCallback,
                                 int32_t aCoreCount)
{
  mCallback = aCallback;
  VideoDecoderConfig config;
  config.codec = VideoDecoderConfig::kCodecH264; // TODO: others.
  const GMPVideoCodecH264* h264 = (const GMPVideoCodecH264*)(aCodecSpecific);
  config.profile = ToCDMH264Profile(h264->mAVCC.mProfile);
  config.format = kYv12;
  config.coded_size = Size(aCodecSettings.mWidth, aCodecSettings.mHeight);
  mExtraData->AppendElements(aCodecSpecific + 1, aCodecSpecificLength);
  config.extra_data = mExtraData->Elements();
  config.extra_data_size = mExtraData->Length();
  Status rv = CDM()->InitializeVideoDecoder(config);
  if (rv != kSuccess) {
    mCallback->Error(ToGMPErr(rv));
    return;
  }
  Log("WidevineVideoDecoder::InitDecode() rv=%d", rv);
  mAnnexB = mp4_demuxer::AnnexB::ConvertExtraDataToAnnexB(mExtraData);
}
void
WidevineDecryptor::Decrypt(GMPBuffer* aBuffer,
                           GMPEncryptedBufferMetadata* aMetadata)
{
  if (!mCallback) {
    Log("WidevineDecryptor::Decrypt() this=%p FAIL; !mCallback", this);
    return;
  }
  const GMPEncryptedBufferMetadata* crypto = aMetadata;
  InputBuffer sample;
  nsTArray<SubsampleEntry> subsamples;
  InitInputBuffer(crypto, aBuffer->Id(), aBuffer->Data(), aBuffer->Size(), sample, subsamples);
  WidevineDecryptedBlock decrypted;
  Status rv = CDM()->Decrypt(sample, &decrypted);
  Log("Decryptor::Decrypt(timestamp=%lld) rv=%d sz=%d",
      sample.timestamp, rv, decrypted.DecryptedBuffer()->Size());
  if (rv == kSuccess) {
    aBuffer->Resize(decrypted.DecryptedBuffer()->Size());
    memcpy(aBuffer->Data(),
           decrypted.DecryptedBuffer()->Data(),
           decrypted.DecryptedBuffer()->Size());
  }
  mCallback->Decrypted(aBuffer, ToGMPErr(rv));
}
void
WidevineVideoDecoder::Decode(GMPVideoEncodedFrame* aInputFrame,
                             bool aMissingFrames,
                             const uint8_t* aCodecSpecificInfo,
                             uint32_t aCodecSpecificInfoLength,
                             int64_t aRenderTimeMs)
{
  // We should not be given new input if a drain has been initiated
  MOZ_ASSERT(!mDrainPending);
  // We may not get the same out of the CDM decoder as we put in, and there
  // may be some latency, i.e. we may need to input (say) 30 frames before
  // we receive output. So we need to store the durations of the frames input,
  // and retrieve them on output.
  mFrameDurations[aInputFrame->TimeStamp()] = aInputFrame->Duration();

  mSentInput = true;
  InputBuffer sample;

  RefPtr<MediaRawData> raw(new MediaRawData(aInputFrame->Buffer(), aInputFrame->Size()));
  raw->mExtraData = mExtraData;
  raw->mKeyframe = (aInputFrame->FrameType() == kGMPKeyFrame);
  // Convert input from AVCC, which GMPAPI passes in, to AnnexB, which
  // Chromium uses internally.
  mp4_demuxer::AnnexB::ConvertSampleToAnnexB(raw);

  const GMPEncryptedBufferMetadata* crypto = aInputFrame->GetDecryptionData();
  nsTArray<SubsampleEntry> subsamples;
  InitInputBuffer(crypto, aInputFrame->TimeStamp(), raw->Data(), raw->Size(), sample, subsamples);

  // For keyframes, ConvertSampleToAnnexB will stick the AnnexB extra data
  // at the start of the input. So we need to account for that as clear data
  // in the subsamples.
  if (raw->mKeyframe && !subsamples.IsEmpty()) {
    subsamples[0].clear_bytes += mAnnexB->Length();
  }

  WidevineVideoFrame frame;
  Status rv = CDM()->DecryptAndDecodeFrame(sample, &frame);
  Log("WidevineVideoDecoder::Decode(timestamp=%lld) rv=%d", sample.timestamp, rv);

  // Destroy frame, so that the shmem is now free to be used to return
  // output to the Gecko process.
  aInputFrame->Destroy();
  aInputFrame = nullptr;

  if (rv == kSuccess) {
    if (!ReturnOutput(frame)) {
      Log("WidevineVideoDecoder::Decode() Failed in ReturnOutput()");
      mCallback->Error(GMPDecodeErr);
      return;
    }
    // A reset should only be started at most at level mReturnOutputCallDepth 1,
    // and if it's started it should be finished by that call by the time
    // the it returns, so it should always be false by this point.
    MOZ_ASSERT(!mResetInProgress);
    // Only request more data if we don't have pending samples.
    if (mFrameAllocationQueue.empty()) {
      MOZ_ASSERT(mCDMWrapper);
      mCallback->InputDataExhausted();
    }
  } else if (rv == kNeedMoreData) {
    MOZ_ASSERT(mCDMWrapper);
    mCallback->InputDataExhausted();
  } else {
    mCallback->Error(ToGMPErr(rv));
  }
  // Finish a drain if pending and we have no pending ReturnOutput calls on the stack.
  if (mDrainPending && mReturnOutputCallDepth == 0) {
    Drain();
  }
}