2015-01-16 00:37:54 +03:00
|
|
|
/*
|
|
|
|
* Copyright 2013, Mozilla Foundation and contributors
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
#include <algorithm>
|
2015-01-16 00:37:54 +03:00
|
|
|
#include <cstdint>
|
|
|
|
#include <limits>
|
2015-01-16 00:37:54 +03:00
|
|
|
|
2016-06-05 20:24:51 +03:00
|
|
|
#include "BigEndian.h"
|
2015-01-16 00:37:54 +03:00
|
|
|
#include "ClearKeyDecryptionManager.h"
|
|
|
|
#include "ClearKeyUtils.h"
|
|
|
|
#include "VideoDecoder.h"
|
|
|
|
|
|
|
|
using namespace wmf;
|
2017-01-11 23:52:05 +03:00
|
|
|
using namespace cdm;
|
2015-01-16 00:37:54 +03:00
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
VideoDecoder::VideoDecoder(Host_8 *aHost)
|
|
|
|
: mHost(aHost)
|
2015-03-22 21:59:42 +03:00
|
|
|
, mHasShutdown(false)
|
2015-01-16 00:37:54 +03:00
|
|
|
{
|
2015-05-06 02:40:40 +03:00
|
|
|
// We drop the ref in DecodingComplete().
|
|
|
|
AddRef();
|
2017-01-11 23:52:05 +03:00
|
|
|
|
|
|
|
mDecoder = new WMFH264Decoder();
|
|
|
|
|
|
|
|
uint32_t cores = std::max(1u, std::thread::hardware_concurrency());
|
|
|
|
HRESULT hr = mDecoder->Init(cores);
|
2015-01-16 00:37:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
VideoDecoder::~VideoDecoder()
|
|
|
|
{
|
2017-01-11 23:52:05 +03:00
|
|
|
|
2015-01-16 00:37:54 +03:00
|
|
|
}
|
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
Status
|
|
|
|
VideoDecoder::InitDecode(const VideoDecoderConfig& aConfig)
|
2015-01-16 00:37:54 +03:00
|
|
|
{
|
2017-01-11 23:52:05 +03:00
|
|
|
if (!mDecoder) {
|
2015-04-20 11:07:24 +03:00
|
|
|
CK_LOGD("VideoDecoder::InitDecode failed to init WMFH264Decoder");
|
2015-01-16 00:37:54 +03:00
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
return Status::kDecodeError;
|
2015-01-16 00:37:54 +03:00
|
|
|
}
|
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
return Status::kSuccess;
|
2015-01-16 00:37:54 +03:00
|
|
|
}
|
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
Status
|
|
|
|
VideoDecoder::Decode(const InputBuffer& aInputBuffer, VideoFrame* aVideoFrame)
|
2015-01-16 00:37:54 +03:00
|
|
|
{
|
2017-01-11 23:52:05 +03:00
|
|
|
// If the input buffer we have been passed has a null buffer, it means we
|
|
|
|
// should drain.
|
|
|
|
if (!aInputBuffer.data) {
|
|
|
|
// This will drain the decoder until there are no frames left to drain,
|
|
|
|
// whereupon it will return 'NeedsMoreData'.
|
|
|
|
CK_LOGD("Input buffer null: Draining");
|
|
|
|
return Drain(aVideoFrame);
|
2015-01-16 00:37:54 +03:00
|
|
|
}
|
|
|
|
|
2015-12-01 08:13:58 +03:00
|
|
|
DecodeData* data = new DecodeData();
|
2017-01-11 23:52:05 +03:00
|
|
|
Assign(data->mBuffer, aInputBuffer.data, aInputBuffer.data_size);
|
|
|
|
data->mTimestamp = aInputBuffer.timestamp;
|
|
|
|
data->mCrypto = CryptoMetaData(&aInputBuffer);
|
2015-01-16 00:37:54 +03:00
|
|
|
|
2015-01-16 00:37:54 +03:00
|
|
|
CK_LOGD("VideoDecoder::DecodeTask");
|
2017-01-11 23:52:05 +03:00
|
|
|
AutoPtr<DecodeData> d(data);
|
2015-01-16 00:37:54 +03:00
|
|
|
HRESULT hr;
|
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
if (!data || !mDecoder) {
|
2017-01-14 00:23:31 +03:00
|
|
|
CK_LOGE("Decode job not set up correctly!");
|
2017-01-11 23:52:05 +03:00
|
|
|
return Status::kDecodeError;
|
2017-01-14 00:23:31 +03:00
|
|
|
}
|
2015-01-16 00:37:54 +03:00
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
std::vector<uint8_t>& buffer = data->mBuffer;
|
2015-01-16 00:37:54 +03:00
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
if (data->mCrypto.IsValid()) {
|
|
|
|
Status rv =
|
|
|
|
ClearKeyDecryptionManager::Get()->Decrypt(buffer, data->mCrypto);
|
2015-01-16 00:37:54 +03:00
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
if (STATUS_FAILED(rv)) {
|
|
|
|
CK_LOGARRAY("Failed to decrypt video using key ",
|
|
|
|
aInputBuffer.key_id,
|
|
|
|
aInputBuffer.key_id_size);
|
|
|
|
return rv;
|
|
|
|
}
|
2017-01-14 00:23:31 +03:00
|
|
|
}
|
|
|
|
|
2015-01-16 00:37:54 +03:00
|
|
|
hr = mDecoder->Input(buffer.data(),
|
|
|
|
buffer.size(),
|
2017-01-11 23:52:05 +03:00
|
|
|
data->mTimestamp);
|
2015-01-16 00:37:54 +03:00
|
|
|
|
2015-01-16 00:37:54 +03:00
|
|
|
CK_LOGD("VideoDecoder::DecodeTask() Input ret hr=0x%x\n", hr);
|
2017-01-11 23:52:05 +03:00
|
|
|
|
|
|
|
|
2015-01-16 00:37:54 +03:00
|
|
|
if (FAILED(hr)) {
|
2017-01-11 23:52:05 +03:00
|
|
|
assert(hr != MF_E_TRANSFORM_NEED_MORE_INPUT);
|
|
|
|
|
2015-01-16 00:37:54 +03:00
|
|
|
CK_LOGE("VideoDecoder::DecodeTask() decode failed ret=0x%x%s\n",
|
2017-01-11 23:52:05 +03:00
|
|
|
hr,
|
|
|
|
((hr == MF_E_NOTACCEPTING) ? " (MF_E_NOTACCEPTING)" : ""));
|
|
|
|
CK_LOGD("Decode failed. The decoder is not accepting input");
|
|
|
|
return Status::kDecodeError;
|
2015-01-16 00:37:54 +03:00
|
|
|
}
|
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
return OutputFrame(aVideoFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
Status VideoDecoder::OutputFrame(VideoFrame* aVideoFrame) {
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
|
|
// Read all the output from the decoder. Ideally, this would be a while loop
|
|
|
|
// where we read the output and check the result as the condition. However,
|
|
|
|
// this produces a memory leak connected to assigning a new CComPtr to the
|
|
|
|
// address of the old one, which avoids the CComPtr cleaning up.
|
|
|
|
while (true) {
|
2015-01-16 00:37:54 +03:00
|
|
|
CComPtr<IMFSample> output;
|
|
|
|
hr = mDecoder->Output(&output);
|
2017-01-11 23:52:05 +03:00
|
|
|
|
|
|
|
if (hr != S_OK) {
|
|
|
|
break;
|
2017-01-14 00:23:31 +03:00
|
|
|
}
|
2015-01-16 00:37:54 +03:00
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
CK_LOGD("VideoDecoder::DecodeTask() output ret=0x%x\n", hr);
|
2015-01-16 00:37:54 +03:00
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
mOutputQueue.push(output);
|
|
|
|
CK_LOGD("Queue size: %u", mOutputQueue.size());
|
|
|
|
}
|
2015-01-16 00:37:54 +03:00
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
// If we don't have any inputs, we need more data.
|
|
|
|
if (mOutputQueue.empty()) {
|
|
|
|
CK_LOGD("Decode failed. Not enought data; Requesting more input");
|
|
|
|
return Status::kNeedMoreData;
|
2015-01-16 00:37:54 +03:00
|
|
|
}
|
2017-01-11 23:52:05 +03:00
|
|
|
|
|
|
|
// We will get a MF_E_TRANSFORM_NEED_MORE_INPUT every time, as we always
|
|
|
|
// consume everything in the buffer.
|
|
|
|
if (hr != MF_E_TRANSFORM_NEED_MORE_INPUT && FAILED(hr)) {
|
|
|
|
CK_LOGD("Decode failed output ret=0x%x\n", hr);
|
|
|
|
return Status::kDecodeError;
|
2015-05-06 02:40:40 +03:00
|
|
|
}
|
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
CComPtr<IMFSample> result = mOutputQueue.front();
|
|
|
|
mOutputQueue.pop();
|
2015-01-16 00:37:54 +03:00
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
// The Chromium CDM API doesn't have support for negative strides, though
|
|
|
|
// they are theoretically possible in real world data.
|
|
|
|
if (mDecoder->GetStride() <= 0) {
|
|
|
|
return Status::kDecodeError;
|
|
|
|
}
|
2015-01-16 00:37:54 +03:00
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
hr = SampleToVideoFrame(result,
|
|
|
|
mDecoder->GetFrameWidth(),
|
|
|
|
mDecoder->GetFrameHeight(),
|
|
|
|
mDecoder->GetStride(),
|
|
|
|
aVideoFrame);
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
return Status::kDecodeError;
|
|
|
|
}
|
|
|
|
|
|
|
|
CK_LOGD("Decode succeeded.");
|
|
|
|
return Status::kSuccess;
|
2015-01-16 00:37:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
VideoDecoder::SampleToVideoFrame(IMFSample* aSample,
|
2015-02-28 00:23:33 +03:00
|
|
|
int32_t aWidth,
|
|
|
|
int32_t aHeight,
|
|
|
|
int32_t aStride,
|
2017-01-11 23:52:05 +03:00
|
|
|
VideoFrame* aVideoFrame)
|
2015-01-16 00:37:54 +03:00
|
|
|
{
|
2017-01-11 23:52:05 +03:00
|
|
|
CK_LOGD("[%p] VideoDecoder::SampleToVideoFrame()\n", this);
|
|
|
|
assert(aSample);
|
|
|
|
|
2015-01-16 00:37:54 +03:00
|
|
|
ENSURE(aSample != nullptr, E_POINTER);
|
|
|
|
ENSURE(aVideoFrame != nullptr, E_POINTER);
|
|
|
|
|
|
|
|
HRESULT hr;
|
|
|
|
CComPtr<IMFMediaBuffer> mediaBuffer;
|
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
aVideoFrame->SetFormat(kI420);
|
|
|
|
|
2015-01-16 00:37:54 +03:00
|
|
|
// Must convert to contiguous mediaBuffer to use IMD2DBuffer interface.
|
|
|
|
hr = aSample->ConvertToContiguousBuffer(&mediaBuffer);
|
|
|
|
ENSURE(SUCCEEDED(hr), hr);
|
|
|
|
|
|
|
|
// Try and use the IMF2DBuffer interface if available, otherwise fallback
|
|
|
|
// to the IMFMediaBuffer interface. Apparently IMF2DBuffer is more efficient,
|
|
|
|
// but only some systems (Windows 8?) support it.
|
|
|
|
BYTE* data = nullptr;
|
|
|
|
LONG stride = 0;
|
|
|
|
CComPtr<IMF2DBuffer> twoDBuffer;
|
|
|
|
hr = mediaBuffer->QueryInterface(static_cast<IMF2DBuffer**>(&twoDBuffer));
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
hr = twoDBuffer->Lock2D(&data, &stride);
|
|
|
|
ENSURE(SUCCEEDED(hr), hr);
|
|
|
|
} else {
|
2017-01-11 23:52:05 +03:00
|
|
|
hr = mediaBuffer->Lock(&data, nullptr, nullptr);
|
2015-01-16 00:37:54 +03:00
|
|
|
ENSURE(SUCCEEDED(hr), hr);
|
2015-02-28 00:23:33 +03:00
|
|
|
stride = aStride;
|
2015-01-16 00:37:54 +03:00
|
|
|
}
|
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
// The U and V planes are stored 16-row-aligned, so we need to add padding
|
2015-01-16 00:37:54 +03:00
|
|
|
// to the row heights to ensure the Y'CbCr planes are referenced properly.
|
2017-01-11 23:52:05 +03:00
|
|
|
// YV12, planar format: [YYYY....][UUUU....][VVVV....]
|
|
|
|
// i.e., Y, then U, then V.
|
2015-01-16 00:37:54 +03:00
|
|
|
uint32_t padding = 0;
|
2015-02-28 00:23:33 +03:00
|
|
|
if (aHeight % 16 != 0) {
|
|
|
|
padding = 16 - (aHeight % 16);
|
2015-01-16 00:37:54 +03:00
|
|
|
}
|
2017-01-11 23:52:05 +03:00
|
|
|
uint32_t ySize = stride * (aHeight + padding);
|
|
|
|
uint32_t uSize = stride * (aHeight + padding) / 4;
|
|
|
|
uint32_t halfStride = (stride + 1) / 2;
|
|
|
|
uint32_t halfHeight = (aHeight + 1) / 2;
|
|
|
|
|
|
|
|
aVideoFrame->SetStride(VideoFrame::kYPlane, stride);
|
|
|
|
aVideoFrame->SetStride(VideoFrame::kUPlane, halfStride);
|
|
|
|
aVideoFrame->SetStride(VideoFrame::kVPlane, halfStride);
|
|
|
|
|
|
|
|
aVideoFrame->SetSize(Size(aWidth, aHeight));
|
|
|
|
|
|
|
|
uint64_t bufferSize = ySize + 2 * uSize;
|
|
|
|
|
|
|
|
// If the buffer is bigger than the max for a 32 bit, fail to avoid buffer
|
|
|
|
// overflows.
|
|
|
|
if (bufferSize > UINT32_MAX) {
|
|
|
|
return Status::kDecodeError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the buffer from the host.
|
|
|
|
Buffer* buffer = mHost->Allocate(bufferSize);
|
|
|
|
aVideoFrame->SetFrameBuffer(buffer);
|
|
|
|
|
|
|
|
// Make sure the buffer is non-null (allocate guarantees it will be of
|
|
|
|
// sufficient size).
|
|
|
|
if (!buffer) {
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t* outBuffer = buffer->Data();
|
|
|
|
|
|
|
|
aVideoFrame->SetPlaneOffset(VideoFrame::kYPlane, 0);
|
|
|
|
|
|
|
|
// Offset is the size of the copied y data.
|
|
|
|
aVideoFrame->SetPlaneOffset(VideoFrame::kUPlane, ySize);
|
|
|
|
|
|
|
|
// Offset is the size of the copied y data + the size of the copied u data.
|
|
|
|
aVideoFrame->SetPlaneOffset(VideoFrame::kVPlane, ySize + uSize);
|
|
|
|
|
|
|
|
// Copy the data.
|
|
|
|
memcpy(outBuffer, data, ySize + uSize * 2);
|
2015-01-16 00:37:54 +03:00
|
|
|
|
|
|
|
if (twoDBuffer) {
|
|
|
|
twoDBuffer->Unlock2D();
|
|
|
|
} else {
|
|
|
|
mediaBuffer->Unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
LONGLONG hns = 0;
|
|
|
|
hr = aSample->GetSampleTime(&hns);
|
|
|
|
ENSURE(SUCCEEDED(hr), hr);
|
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
aVideoFrame->SetTimestamp(HNsToUsecs(hns));
|
2017-01-14 00:23:31 +03:00
|
|
|
|
2015-01-16 00:37:54 +03:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2017-01-14 00:23:31 +03:00
|
|
|
void
|
|
|
|
VideoDecoder::Reset()
|
|
|
|
{
|
2017-01-11 23:52:05 +03:00
|
|
|
CK_LOGD("VideoDecoder::Reset");
|
|
|
|
|
2015-04-13 04:39:49 +03:00
|
|
|
if (mDecoder) {
|
|
|
|
mDecoder->Reset();
|
|
|
|
}
|
2015-08-20 01:06:36 +03:00
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
// Remove all the frames from the output queue.
|
|
|
|
while (!mOutputQueue.empty()) {
|
|
|
|
mOutputQueue.pop();
|
2017-01-14 00:23:31 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-11 23:52:05 +03:00
|
|
|
Status
|
|
|
|
VideoDecoder::Drain(VideoFrame* aVideoFrame)
|
2017-01-14 00:23:31 +03:00
|
|
|
{
|
2017-01-11 23:52:05 +03:00
|
|
|
CK_LOGD("VideoDecoder::Drain()");
|
|
|
|
|
2017-01-14 00:23:31 +03:00
|
|
|
if (!mDecoder) {
|
2017-01-11 23:52:05 +03:00
|
|
|
CK_LOGD("Drain failed! Decoder was not initialized");
|
|
|
|
return Status::kDecodeError;
|
2017-01-14 00:23:31 +03:00
|
|
|
}
|
2017-01-11 23:52:05 +03:00
|
|
|
|
|
|
|
mDecoder->Drain();
|
|
|
|
|
|
|
|
// Return any pending output.
|
|
|
|
return OutputFrame(aVideoFrame);
|
2015-01-16 00:37:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
VideoDecoder::DecodingComplete()
|
|
|
|
{
|
2015-03-22 21:59:42 +03:00
|
|
|
mHasShutdown = true;
|
|
|
|
|
2015-05-06 02:40:40 +03:00
|
|
|
// Release the reference we added in the constructor. There may be
|
|
|
|
// WrapRefCounted tasks that also hold references to us, and keep
|
|
|
|
// us alive a little longer.
|
|
|
|
Release();
|
2015-01-16 00:37:54 +03:00
|
|
|
}
|