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.
20 #include "AudioDecoder.h"
21 #include "ClearKeyDecryptionManager.h"
22 #include "ClearKeyUtils.h"
23 #include "gmp-task-utils.h"
27 AudioDecoder::AudioDecoder(GMPAudioHost
*aHostAPI
)
30 , mWorkerThread(nullptr)
36 AudioDecoder::~AudioDecoder()
42 AudioDecoder::InitDecode(const GMPAudioCodec
& aConfig
,
43 GMPAudioDecoderCallback
* aCallback
)
45 mCallback
= aCallback
;
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
);
54 mCallback
->Error(GMPGenericErr
);
57 auto err
= GetPlatform()->createmutex(&mMutex
);
58 if (GMP_FAILED(err
)) {
59 mCallback
->Error(GMPGenericErr
);
65 AudioDecoder::EnsureWorker()
68 GetPlatform()->createthread(&mWorkerThread
);
70 mCallback
->Error(GMPAllocErr
);
77 AudioDecoder::Decode(GMPAudioSamples
* aInput
)
81 AutoLock
lock(mMutex
);
84 mWorkerThread
->Post(WrapTask(this,
85 &AudioDecoder::DecodeTask
,
90 AudioDecoder::DecodeTask(GMPAudioSamples
* aInput
)
95 AutoLock
lock(mMutex
);
97 assert(mNumInputTasks
>= 0);
100 if (!aInput
|| !mHostAPI
|| !mDecoder
) {
101 LOG("Decode job not set up correctly!");
105 const uint8_t* inBuffer
= aInput
->Buffer();
107 LOG("No buffer for encoded samples!\n");
111 const GMPEncryptedBufferMetadata
* crypto
= aInput
->GetDecryptionData();
112 std::vector
<uint8_t> buffer(inBuffer
, inBuffer
+ aInput
->Size());
114 // Plugin host should have set up its decryptor/key sessions
115 // before trying to decode!
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
));
126 hr
= mDecoder
->Input(&buffer
[0],
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
);
135 LOG("AudioDecoder::DecodeTask() decode failed ret=0x%x%s\n",
137 ((hr
== MF_E_NOTACCEPTING
) ? " (MF_E_NOTACCEPTING)" : ""));
142 CComPtr
<IMFSample
> output
;
143 hr
= mDecoder
->Output(&output
);
144 SAMPLE_LOG("AudioDecoder::DecodeTask() output ret=0x%x\n", hr
);
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
);
162 AudioDecoder::ReturnOutput(IMFSample
* aSample
)
164 SAMPLE_LOG("[%p] AudioDecoder::ReturnOutput()\n", this);
169 GMPAudioSamples
* samples
= nullptr;
170 mHostAPI
->CreateSamples(kGMPAudioIS16Samples
, &samples
);
172 LOG("Failed to create i420 frame!\n");
176 hr
= MFToGMPSample(aSample
, samples
);
179 LOG("Failed to prepare output sample!");
182 ENSURE(SUCCEEDED(hr
), /*void*/);
184 GetPlatform()->runonmainthread(WrapTask(mCallback
, &GMPAudioDecoderCallback::Decoded
, samples
));
188 AudioDecoder::MFToGMPSample(IMFSample
* aInput
,
189 GMPAudioSamples
* aOutput
)
191 ENSURE(aInput
!= nullptr, E_POINTER
);
192 ENSURE(aOutput
!= nullptr, E_POINTER
);
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
, ¤tLength
);
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();
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());
223 AudioDecoder::Reset()
226 mCallback
->ResetComplete();
230 AudioDecoder::DrainTask()
232 if (FAILED(mDecoder
->Drain())) {
233 GetPlatform()->syncrunonmainthread(WrapTask(mCallback
, &GMPAudioDecoderCallback::DrainComplete
));
236 // Return any pending output.
239 CComPtr
<IMFSample
> output
;
240 hr
= mDecoder
->Output(&output
);
241 SAMPLE_LOG("AudioDecoder::DrainTask() output ret=0x%x\n", hr
);
243 ReturnOutput(output
);
246 GetPlatform()->syncrunonmainthread(WrapTask(mCallback
, &GMPAudioDecoderCallback::DrainComplete
));
250 AudioDecoder::Drain()
253 mWorkerThread
->Post(WrapTask(this,
254 &AudioDecoder::DrainTask
));
258 AudioDecoder::DecodingComplete()
261 mWorkerThread
->Join();