Bumping manifests a=b2g-bump
[gecko.git] / media / gmp-clearkey / 0.1 / AudioDecoder.cpp
blob84e3df4c323a81acb709a9278a40ed84ba61c0fd
1 /*
2 * Copyright 2013, Mozilla Foundation and contributors
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <cstdint>
18 #include <limits>
20 #include "AudioDecoder.h"
21 #include "ClearKeyDecryptionManager.h"
22 #include "ClearKeyUtils.h"
23 #include "gmp-task-utils.h"
25 using namespace wmf;
27 AudioDecoder::AudioDecoder(GMPAudioHost *aHostAPI)
28 : mHostAPI(aHostAPI)
29 , mCallback(nullptr)
30 , mWorkerThread(nullptr)
31 , mMutex(nullptr)
32 , mNumInputTasks(0)
36 AudioDecoder::~AudioDecoder()
38 mMutex->Destroy();
41 void
42 AudioDecoder::InitDecode(const GMPAudioCodec& aConfig,
43 GMPAudioDecoderCallback* aCallback)
45 mCallback = aCallback;
46 assert(mCallback);
47 mDecoder = new WMFAACDecoder();
48 HRESULT hr = mDecoder->Init(aConfig.mChannelCount,
49 aConfig.mSamplesPerSecond,
50 (BYTE*)aConfig.mExtraData,
51 aConfig.mExtraDataLen);
52 LOG("[%p] AudioDecoder::InitializeAudioDecoder() hr=0x%x\n", this, hr);
53 if (FAILED(hr)) {
54 mCallback->Error(GMPGenericErr);
55 return;
57 auto err = GetPlatform()->createmutex(&mMutex);
58 if (GMP_FAILED(err)) {
59 mCallback->Error(GMPGenericErr);
60 return;
64 void
65 AudioDecoder::EnsureWorker()
67 if (!mWorkerThread) {
68 GetPlatform()->createthread(&mWorkerThread);
69 if (!mWorkerThread) {
70 mCallback->Error(GMPAllocErr);
71 return;
76 void
77 AudioDecoder::Decode(GMPAudioSamples* aInput)
79 EnsureWorker();
81 AutoLock lock(mMutex);
82 mNumInputTasks++;
84 mWorkerThread->Post(WrapTask(this,
85 &AudioDecoder::DecodeTask,
86 aInput));
89 void
90 AudioDecoder::DecodeTask(GMPAudioSamples* aInput)
92 HRESULT hr;
95 AutoLock lock(mMutex);
96 mNumInputTasks--;
97 assert(mNumInputTasks >= 0);
100 if (!aInput || !mHostAPI || !mDecoder) {
101 LOG("Decode job not set up correctly!");
102 return;
105 const uint8_t* inBuffer = aInput->Buffer();
106 if (!inBuffer) {
107 LOG("No buffer for encoded samples!\n");
108 return;
111 const GMPEncryptedBufferMetadata* crypto = aInput->GetDecryptionData();
112 std::vector<uint8_t> buffer(inBuffer, inBuffer + aInput->Size());
113 if (crypto) {
114 // Plugin host should have set up its decryptor/key sessions
115 // before trying to decode!
116 GMPErr rv =
117 ClearKeyDecryptionManager::Get()->Decrypt(&buffer[0], buffer.size(), crypto);
119 if (GMP_FAILED(rv)) {
120 CK_LOGE("Failed to decrypt with key id %08x...", *(uint32_t*)crypto->KeyId());
121 GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::Error, rv));
122 return;
126 hr = mDecoder->Input(&buffer[0],
127 buffer.size(),
128 aInput->TimeStamp());
130 // We must delete the input sample!
131 GetPlatform()->runonmainthread(WrapTask(aInput, &GMPAudioSamples::Destroy));
133 SAMPLE_LOG("AudioDecoder::DecodeTask() Input ret hr=0x%x\n", hr);
134 if (FAILED(hr)) {
135 LOG("AudioDecoder::DecodeTask() decode failed ret=0x%x%s\n",
137 ((hr == MF_E_NOTACCEPTING) ? " (MF_E_NOTACCEPTING)" : ""));
138 return;
141 while (hr == S_OK) {
142 CComPtr<IMFSample> output;
143 hr = mDecoder->Output(&output);
144 SAMPLE_LOG("AudioDecoder::DecodeTask() output ret=0x%x\n", hr);
145 if (hr == S_OK) {
146 ReturnOutput(output);
148 if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
149 AutoLock lock(mMutex);
150 if (mNumInputTasks == 0) {
151 // We have run all input tasks. We *must* notify Gecko so that it will
152 // send us more data.
153 GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::InputDataExhausted));
155 } else if (FAILED(hr)) {
156 LOG("AudioDecoder::DecodeTask() output failed hr=0x%x\n", hr);
161 void
162 AudioDecoder::ReturnOutput(IMFSample* aSample)
164 SAMPLE_LOG("[%p] AudioDecoder::ReturnOutput()\n", this);
165 assert(aSample);
167 HRESULT hr;
169 GMPAudioSamples* samples = nullptr;
170 mHostAPI->CreateSamples(kGMPAudioIS16Samples, &samples);
171 if (!samples) {
172 LOG("Failed to create i420 frame!\n");
173 return;
176 hr = MFToGMPSample(aSample, samples);
177 if (FAILED(hr)) {
178 samples->Destroy();
179 LOG("Failed to prepare output sample!");
180 return;
182 ENSURE(SUCCEEDED(hr), /*void*/);
184 GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::Decoded, samples));
187 HRESULT
188 AudioDecoder::MFToGMPSample(IMFSample* aInput,
189 GMPAudioSamples* aOutput)
191 ENSURE(aInput != nullptr, E_POINTER);
192 ENSURE(aOutput != nullptr, E_POINTER);
194 HRESULT hr;
195 CComPtr<IMFMediaBuffer> mediaBuffer;
197 hr = aInput->ConvertToContiguousBuffer(&mediaBuffer);
198 ENSURE(SUCCEEDED(hr), hr);
200 BYTE* data = nullptr; // Note: *data will be owned by the IMFMediaBuffer, we don't need to free it.
201 DWORD maxLength = 0, currentLength = 0;
202 hr = mediaBuffer->Lock(&data, &maxLength, &currentLength);
203 ENSURE(SUCCEEDED(hr), hr);
205 auto err = aOutput->SetBufferSize(currentLength);
206 ENSURE(GMP_SUCCEEDED(err), E_FAIL);
208 memcpy(aOutput->Buffer(), data, currentLength);
210 mediaBuffer->Unlock();
212 LONGLONG hns = 0;
213 hr = aInput->GetSampleTime(&hns);
214 ENSURE(SUCCEEDED(hr), hr);
215 aOutput->SetTimeStamp(HNsToUsecs(hns));
216 aOutput->SetChannels(mDecoder->Channels());
217 aOutput->SetRate(mDecoder->Rate());
219 return S_OK;
222 void
223 AudioDecoder::Reset()
225 mDecoder->Reset();
226 mCallback->ResetComplete();
229 void
230 AudioDecoder::DrainTask()
232 if (FAILED(mDecoder->Drain())) {
233 GetPlatform()->syncrunonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete));
236 // Return any pending output.
237 HRESULT hr = S_OK;
238 while (hr == S_OK) {
239 CComPtr<IMFSample> output;
240 hr = mDecoder->Output(&output);
241 SAMPLE_LOG("AudioDecoder::DrainTask() output ret=0x%x\n", hr);
242 if (hr == S_OK) {
243 ReturnOutput(output);
246 GetPlatform()->syncrunonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete));
249 void
250 AudioDecoder::Drain()
252 EnsureWorker();
253 mWorkerThread->Post(WrapTask(this,
254 &AudioDecoder::DrainTask));
257 void
258 AudioDecoder::DecodingComplete()
260 if (mWorkerThread) {
261 mWorkerThread->Join();
263 delete this;