1 /*
2  * Copyright 2015, Mozilla Foundation and contributors
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 #include <cstdint>
18 #include <limits>
19 
20 #include "AudioDecoder.h"
21 #include "ClearKeyDecryptionManager.h"
22 #include "ClearKeyUtils.h"
23 #include "gmp-task-utils.h"
24 
25 using namespace wmf;
26 
AudioDecoder(GMPAudioHost * aHostAPI)27 AudioDecoder::AudioDecoder(GMPAudioHost *aHostAPI)
28   : mHostAPI(aHostAPI)
29   , mCallback(nullptr)
30   , mWorkerThread(nullptr)
31   , mMutex(nullptr)
32   , mNumInputTasks(0)
33   , mHasShutdown(false)
34 {
35   // We drop the ref in DecodingComplete().
36   AddRef();
37 }
38 
~AudioDecoder()39 AudioDecoder::~AudioDecoder()
40 {
41   if (mMutex) {
42     mMutex->Destroy();
43   }
44 }
45 
46 void
InitDecode(const GMPAudioCodec & aConfig,GMPAudioDecoderCallback * aCallback)47 AudioDecoder::InitDecode(const GMPAudioCodec& aConfig,
48                          GMPAudioDecoderCallback* aCallback)
49 {
50   mCallback = aCallback;
51   assert(mCallback);
52   mDecoder = new WMFAACDecoder();
53   HRESULT hr = mDecoder->Init(aConfig.mChannelCount,
54                               aConfig.mSamplesPerSecond,
55                               (BYTE*)aConfig.mExtraData,
56                               aConfig.mExtraDataLen);
57   LOG("[%p] AudioDecoder::InitializeAudioDecoder() hr=0x%x\n", this, hr);
58   if (FAILED(hr)) {
59     mCallback->Error(GMPGenericErr);
60     return;
61   }
62   auto err = GetPlatform()->createmutex(&mMutex);
63   if (GMP_FAILED(err)) {
64     mCallback->Error(GMPGenericErr);
65     return;
66   }
67 }
68 
69 void
EnsureWorker()70 AudioDecoder::EnsureWorker()
71 {
72   if (!mWorkerThread) {
73     GetPlatform()->createthread(&mWorkerThread);
74     if (!mWorkerThread) {
75       mCallback->Error(GMPAllocErr);
76       return;
77     }
78   }
79 }
80 
81 void
Decode(GMPAudioSamples * aInput)82 AudioDecoder::Decode(GMPAudioSamples* aInput)
83 {
84   EnsureWorker();
85   {
86     AutoLock lock(mMutex);
87     mNumInputTasks++;
88   }
89   mWorkerThread->Post(WrapTaskRefCounted(this,
90                                          &AudioDecoder::DecodeTask,
91                                          aInput));
92 }
93 
94 void
DecodeTask(GMPAudioSamples * aInput)95 AudioDecoder::DecodeTask(GMPAudioSamples* aInput)
96 {
97   HRESULT hr;
98 
99   {
100     AutoLock lock(mMutex);
101     mNumInputTasks--;
102     assert(mNumInputTasks >= 0);
103   }
104 
105   if (!aInput || !mHostAPI || !mDecoder) {
106     LOG("Decode job not set up correctly!");
107     return;
108   }
109 
110   const uint8_t* inBuffer = aInput->Buffer();
111   if (!inBuffer) {
112     LOG("No buffer for encoded samples!\n");
113     return;
114   }
115 
116   const GMPEncryptedBufferMetadata* crypto = aInput->GetDecryptionData();
117   std::vector<uint8_t> buffer(inBuffer, inBuffer + aInput->Size());
118   if (crypto) {
119     // Plugin host should have set up its decryptor/key sessions
120     // before trying to decode!
121     GMPErr rv =
122       ClearKeyDecryptionManager::Get()->Decrypt(buffer, CryptoMetaData(crypto));
123 
124     if (GMP_FAILED(rv)) {
125       CK_LOGE("Failed to decrypt with key id %08x...", *(uint32_t*)crypto->KeyId());
126       MaybeRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::Error, rv));
127       return;
128     }
129   }
130 
131   hr = mDecoder->Input(&buffer[0],
132                        buffer.size(),
133                        aInput->TimeStamp());
134 
135   // We must delete the input sample!
136   GetPlatform()->runonmainthread(WrapTask(aInput, &GMPAudioSamples::Destroy));
137 
138   SAMPLE_LOG("AudioDecoder::DecodeTask() Input ret hr=0x%x\n", hr);
139   if (FAILED(hr)) {
140     LOG("AudioDecoder::DecodeTask() decode failed ret=0x%x%s\n",
141         hr,
142         ((hr == MF_E_NOTACCEPTING) ? " (MF_E_NOTACCEPTING)" : ""));
143     return;
144   }
145 
146   while (hr == S_OK) {
147     CComPtr<IMFSample> output;
148     hr = mDecoder->Output(&output);
149     SAMPLE_LOG("AudioDecoder::DecodeTask() output ret=0x%x\n", hr);
150     if (hr == S_OK) {
151       ReturnOutput(output);
152     }
153     if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
154       AutoLock lock(mMutex);
155       if (mNumInputTasks == 0) {
156         // We have run all input tasks. We *must* notify Gecko so that it will
157         // send us more data.
158         MaybeRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::InputDataExhausted));
159       }
160     } else if (FAILED(hr)) {
161       LOG("AudioDecoder::DecodeTask() output failed hr=0x%x\n", hr);
162     }
163   }
164 }
165 
166 void
ReturnOutput(IMFSample * aSample)167 AudioDecoder::ReturnOutput(IMFSample* aSample)
168 {
169   SAMPLE_LOG("[%p] AudioDecoder::ReturnOutput()\n", this);
170   assert(aSample);
171 
172   HRESULT hr;
173 
174   GMPAudioSamples* samples = nullptr;
175   mHostAPI->CreateSamples(kGMPAudioIS16Samples, &samples);
176   if (!samples) {
177     LOG("Failed to create i420 frame!\n");
178     return;
179   }
180 
181   hr = MFToGMPSample(aSample, samples);
182   if (FAILED(hr)) {
183     samples->Destroy();
184     LOG("Failed to prepare output sample!");
185     return;
186   }
187   ENSURE(SUCCEEDED(hr), /*void*/);
188 
189   MaybeRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::Decoded, samples));
190 }
191 
192 HRESULT
MFToGMPSample(IMFSample * aInput,GMPAudioSamples * aOutput)193 AudioDecoder::MFToGMPSample(IMFSample* aInput,
194                             GMPAudioSamples* aOutput)
195 {
196   ENSURE(aInput != nullptr, E_POINTER);
197   ENSURE(aOutput != nullptr, E_POINTER);
198 
199   HRESULT hr;
200   CComPtr<IMFMediaBuffer> mediaBuffer;
201 
202   hr = aInput->ConvertToContiguousBuffer(&mediaBuffer);
203   ENSURE(SUCCEEDED(hr), hr);
204 
205   BYTE* data = nullptr; // Note: *data will be owned by the IMFMediaBuffer, we don't need to free it.
206   DWORD maxLength = 0, currentLength = 0;
207   hr = mediaBuffer->Lock(&data, &maxLength, &currentLength);
208   ENSURE(SUCCEEDED(hr), hr);
209 
210   auto err = aOutput->SetBufferSize(currentLength);
211   ENSURE(GMP_SUCCEEDED(err), E_FAIL);
212 
213   memcpy(aOutput->Buffer(), data, currentLength);
214 
215   mediaBuffer->Unlock();
216 
217   LONGLONG hns = 0;
218   hr = aInput->GetSampleTime(&hns);
219   ENSURE(SUCCEEDED(hr), hr);
220   aOutput->SetTimeStamp(HNsToUsecs(hns));
221   aOutput->SetChannels(mDecoder->Channels());
222   aOutput->SetRate(mDecoder->Rate());
223 
224   return S_OK;
225 }
226 
227 void
Reset()228 AudioDecoder::Reset()
229 {
230   if (mDecoder) {
231     mDecoder->Reset();
232   }
233   if (mCallback) {
234     mCallback->ResetComplete();
235   }
236 }
237 
238 void
DrainTask()239 AudioDecoder::DrainTask()
240 {
241   mDecoder->Drain();
242 
243   // Return any pending output.
244   HRESULT hr = S_OK;
245   while (hr == S_OK) {
246     CComPtr<IMFSample> output;
247     hr = mDecoder->Output(&output);
248     SAMPLE_LOG("AudioDecoder::DrainTask() output ret=0x%x\n", hr);
249     if (hr == S_OK) {
250       ReturnOutput(output);
251     }
252   }
253   MaybeRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete));
254 }
255 
256 void
Drain()257 AudioDecoder::Drain()
258 {
259   if (!mDecoder) {
260     return;
261   }
262   EnsureWorker();
263   mWorkerThread->Post(WrapTaskRefCounted(this,
264                                          &AudioDecoder::DrainTask));
265 }
266 
267 void
DecodingComplete()268 AudioDecoder::DecodingComplete()
269 {
270   if (mWorkerThread) {
271     mWorkerThread->Join();
272   }
273   mHasShutdown = true;
274 
275   // Release the reference we added in the constructor. There may be
276   // WrapRefCounted tasks that also hold references to us, and keep
277   // us alive a little longer.
278   Release();
279 }
280 
281 void
MaybeRunOnMainThread(GMPTask * aTask)282 AudioDecoder::MaybeRunOnMainThread(GMPTask* aTask)
283 {
284   class MaybeRunTask : public GMPTask
285   {
286   public:
287     MaybeRunTask(AudioDecoder* aDecoder, GMPTask* aTask)
288       : mDecoder(aDecoder), mTask(aTask)
289     { }
290 
291     virtual void Run(void) {
292       if (mDecoder->HasShutdown()) {
293         CK_LOGD("Trying to dispatch to main thread after AudioDecoder has shut down");
294         return;
295       }
296 
297       mTask->Run();
298     }
299 
300     virtual void Destroy()
301     {
302       mTask->Destroy();
303       delete this;
304     }
305 
306   private:
307     RefPtr<AudioDecoder> mDecoder;
308     GMPTask* mTask;
309   };
310 
311   GetPlatform()->runonmainthread(new MaybeRunTask(this, aTask));
312 }
313