mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
364 lines
9.0 KiB
C++
364 lines
9.0 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "EMEH264Decoder.h"
|
|
#include "gmp-video-host.h"
|
|
#include "gmp-video-decode.h"
|
|
#include "gmp-video-frame-i420.h"
|
|
#include "gmp-video-frame-encoded.h"
|
|
#include "GMPVideoEncodedFrameImpl.h"
|
|
#include "mp4_demuxer/AnnexB.h"
|
|
#include "mozilla/CDMProxy.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "prsystem.h"
|
|
#include "gfx2DGlue.h"
|
|
#include "mozilla/EMELog.h"
|
|
#include "mozilla/Move.h"
|
|
|
|
namespace mozilla {
|
|
|
|
EMEH264Decoder::EMEH264Decoder(CDMProxy* aProxy,
|
|
const mp4_demuxer::VideoDecoderConfig& aConfig,
|
|
layers::LayersBackend aLayersBackend,
|
|
layers::ImageContainer* aImageContainer,
|
|
MediaTaskQueue* aTaskQueue,
|
|
MediaDataDecoderCallback* aCallback)
|
|
: mProxy(aProxy)
|
|
, mGMP(nullptr)
|
|
, mHost(nullptr)
|
|
, mConfig(aConfig)
|
|
, mImageContainer(aImageContainer)
|
|
, mTaskQueue(aTaskQueue)
|
|
, mCallback(aCallback)
|
|
, mLastStreamOffset(0)
|
|
, mMonitor("EMEH264Decoder")
|
|
, mFlushComplete(false)
|
|
{
|
|
}
|
|
|
|
EMEH264Decoder::~EMEH264Decoder() {
|
|
}
|
|
|
|
nsresult
|
|
EMEH264Decoder::Init()
|
|
{
|
|
// Note: this runs on the decode task queue.
|
|
|
|
mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
|
|
MOZ_ASSERT(mMPS);
|
|
|
|
nsresult rv = mMPS->GetThread(getter_AddRefs(mGMPThread));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsRefPtr<InitTask> task(new InitTask(this));
|
|
rv = mGMPThread->Dispatch(task, NS_DISPATCH_SYNC);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
NS_ENSURE_SUCCESS(task->mResult, task->mResult);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
EMEH264Decoder::Input(MP4Sample* aSample)
|
|
{
|
|
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
|
|
|
nsRefPtr<nsIRunnable> task(new DeliverSample(this, aSample));
|
|
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
EMEH264Decoder::Flush()
|
|
{
|
|
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
|
|
|
{
|
|
MonitorAutoLock mon(mMonitor);
|
|
mFlushComplete = false;
|
|
}
|
|
|
|
nsRefPtr<nsIRunnable> task;
|
|
task = NS_NewRunnableMethod(this, &EMEH264Decoder::GmpFlush);
|
|
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
{
|
|
MonitorAutoLock mon(mMonitor);
|
|
while (!mFlushComplete) {
|
|
mon.Wait();
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
EMEH264Decoder::Drain()
|
|
{
|
|
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
|
|
|
nsRefPtr<nsIRunnable> task;
|
|
task = NS_NewRunnableMethod(this, &EMEH264Decoder::GmpDrain);
|
|
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
EMEH264Decoder::Shutdown()
|
|
{
|
|
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
|
|
|
nsRefPtr<nsIRunnable> task;
|
|
task = NS_NewRunnableMethod(this, &EMEH264Decoder::GmpShutdown);
|
|
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_SYNC);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
EMEH264Decoder::Decoded(GMPVideoi420Frame* aDecodedFrame)
|
|
{
|
|
MOZ_ASSERT(IsOnGMPThread());
|
|
|
|
VideoData::YCbCrBuffer b;
|
|
|
|
auto height = aDecodedFrame->Height();
|
|
auto width = aDecodedFrame->Width();
|
|
|
|
// Y (Y') plane
|
|
b.mPlanes[0].mData = aDecodedFrame->Buffer(kGMPYPlane);
|
|
b.mPlanes[0].mStride = aDecodedFrame->Stride(kGMPYPlane);
|
|
b.mPlanes[0].mHeight = height;
|
|
b.mPlanes[0].mWidth = width;
|
|
b.mPlanes[0].mOffset = 0;
|
|
b.mPlanes[0].mSkip = 0;
|
|
|
|
// U plane (Cb)
|
|
b.mPlanes[1].mData = aDecodedFrame->Buffer(kGMPUPlane);
|
|
b.mPlanes[1].mStride = aDecodedFrame->Stride(kGMPUPlane);
|
|
b.mPlanes[1].mHeight = height / 2;
|
|
b.mPlanes[1].mWidth = width / 2;
|
|
b.mPlanes[1].mOffset = 0;
|
|
b.mPlanes[1].mSkip = 0;
|
|
|
|
// V plane (Cr)
|
|
b.mPlanes[2].mData = aDecodedFrame->Buffer(kGMPVPlane);
|
|
b.mPlanes[2].mStride = aDecodedFrame->Stride(kGMPVPlane);
|
|
b.mPlanes[2].mHeight = height / 2;
|
|
b.mPlanes[2].mWidth = width / 2;
|
|
b.mPlanes[2].mOffset = 0;
|
|
b.mPlanes[2].mSkip = 0;
|
|
|
|
VideoData *v = VideoData::Create(mVideoInfo,
|
|
mImageContainer,
|
|
mLastStreamOffset,
|
|
aDecodedFrame->Timestamp(),
|
|
aDecodedFrame->Duration(),
|
|
b,
|
|
false,
|
|
-1,
|
|
ToIntRect(mPictureRegion));
|
|
aDecodedFrame->Destroy();
|
|
mCallback->Output(v);
|
|
}
|
|
|
|
void
|
|
EMEH264Decoder::ReceivedDecodedReferenceFrame(const uint64_t aPictureId)
|
|
{
|
|
// Ignore.
|
|
}
|
|
|
|
void
|
|
EMEH264Decoder::ReceivedDecodedFrame(const uint64_t aPictureId)
|
|
{
|
|
// Ignore.
|
|
}
|
|
|
|
void
|
|
EMEH264Decoder::InputDataExhausted()
|
|
{
|
|
MOZ_ASSERT(IsOnGMPThread());
|
|
mCallback->InputExhausted();
|
|
}
|
|
|
|
void
|
|
EMEH264Decoder::DrainComplete()
|
|
{
|
|
MOZ_ASSERT(IsOnGMPThread());
|
|
mCallback->DrainComplete();
|
|
}
|
|
|
|
void
|
|
EMEH264Decoder::ResetComplete()
|
|
{
|
|
MOZ_ASSERT(IsOnGMPThread());
|
|
{
|
|
MonitorAutoLock mon(mMonitor);
|
|
mFlushComplete = true;
|
|
mon.NotifyAll();
|
|
}
|
|
}
|
|
|
|
void
|
|
EMEH264Decoder::Error(GMPErr aErr)
|
|
{
|
|
MOZ_ASSERT(IsOnGMPThread());
|
|
EME_LOG("EMEH264Decoder::Error");
|
|
mCallback->Error();
|
|
GmpShutdown();
|
|
}
|
|
|
|
void
|
|
EMEH264Decoder::Terminated()
|
|
{
|
|
MOZ_ASSERT(IsOnGMPThread());
|
|
|
|
NS_WARNING("H.264 GMP decoder terminated.");
|
|
GmpShutdown();
|
|
}
|
|
|
|
nsresult
|
|
EMEH264Decoder::GmpInit()
|
|
{
|
|
MOZ_ASSERT(IsOnGMPThread());
|
|
|
|
nsTArray<nsCString> tags;
|
|
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
|
|
tags.AppendElement(NS_ConvertUTF16toUTF8(mProxy->KeySystem()));
|
|
nsresult rv = mMPS->GetGMPVideoDecoder(&tags,
|
|
mProxy->GetOrigin(),
|
|
&mHost,
|
|
&mGMP);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
MOZ_ASSERT(mHost && mGMP);
|
|
|
|
GMPVideoCodec codec;
|
|
memset(&codec, 0, sizeof(codec));
|
|
|
|
codec.mGMPApiVersion = kGMPVersion33;
|
|
|
|
codec.mCodecType = kGMPVideoCodecH264;
|
|
codec.mWidth = mConfig.display_width;
|
|
codec.mHeight = mConfig.display_height;
|
|
|
|
nsTArray<uint8_t> codecSpecific;
|
|
codecSpecific.AppendElement(0); // mPacketizationMode.
|
|
codecSpecific.AppendElements(mConfig.extra_data.begin(),
|
|
mConfig.extra_data.length());
|
|
|
|
rv = mGMP->InitDecode(codec,
|
|
codecSpecific,
|
|
this,
|
|
PR_GetNumberOfProcessors());
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mVideoInfo.mDisplay = nsIntSize(mConfig.display_width, mConfig.display_height);
|
|
mVideoInfo.mHasVideo = true;
|
|
mPictureRegion = nsIntRect(0, 0, mConfig.display_width, mConfig.display_height);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
EMEH264Decoder::GmpInput(MP4Sample* aSample)
|
|
{
|
|
MOZ_ASSERT(IsOnGMPThread());
|
|
|
|
nsAutoPtr<MP4Sample> sample(aSample);
|
|
if (!mGMP) {
|
|
mCallback->Error();
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (sample->crypto.valid) {
|
|
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
|
MOZ_ASSERT(caps.CanDecryptAndDecodeVideo());
|
|
const auto& keyid = sample->crypto.key;
|
|
if (!caps.IsKeyUsable(keyid)) {
|
|
nsRefPtr<nsIRunnable> task(new DeliverSample(this, sample.forget()));
|
|
caps.CallWhenKeyUsable(keyid, task, mGMPThread);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
|
|
mLastStreamOffset = sample->byte_offset;
|
|
|
|
GMPVideoFrame* ftmp = nullptr;
|
|
GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
|
|
if (GMP_FAILED(err)) {
|
|
mCallback->Error();
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
UniquePtr<gmp::GMPVideoEncodedFrameImpl> frame(static_cast<gmp::GMPVideoEncodedFrameImpl*>(ftmp));
|
|
err = frame->CreateEmptyFrame(sample->size);
|
|
if (GMP_FAILED(err)) {
|
|
mCallback->Error();
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
memcpy(frame->Buffer(), sample->data, frame->Size());
|
|
|
|
frame->SetEncodedWidth(mConfig.display_width);
|
|
frame->SetEncodedHeight(mConfig.display_height);
|
|
frame->SetTimeStamp(sample->composition_timestamp);
|
|
frame->SetCompleteFrame(true);
|
|
frame->SetDuration(sample->duration);
|
|
if (sample->crypto.valid) {
|
|
frame->InitCrypto(sample->crypto);
|
|
}
|
|
frame->SetFrameType(sample->is_sync_point ? kGMPKeyFrame : kGMPDeltaFrame);
|
|
frame->SetBufferType(GMP_BufferLength32);
|
|
|
|
nsTArray<uint8_t> info; // No codec specific per-frame info to pass.
|
|
nsresult rv = mGMP->Decode(UniquePtr<GMPVideoEncodedFrame>(frame.release()), false, info, 0);
|
|
if (NS_FAILED(rv)) {
|
|
mCallback->Error();
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
EMEH264Decoder::GmpFlush()
|
|
{
|
|
MOZ_ASSERT(IsOnGMPThread());
|
|
if (!mGMP || NS_FAILED(mGMP->Reset())) {
|
|
// Abort the flush...
|
|
MonitorAutoLock mon(mMonitor);
|
|
mFlushComplete = true;
|
|
mon.NotifyAll();
|
|
}
|
|
}
|
|
|
|
void
|
|
EMEH264Decoder::GmpDrain()
|
|
{
|
|
MOZ_ASSERT(IsOnGMPThread());
|
|
if (!mGMP || NS_FAILED(mGMP->Drain())) {
|
|
mCallback->DrainComplete();
|
|
}
|
|
}
|
|
|
|
void
|
|
EMEH264Decoder::GmpShutdown()
|
|
{
|
|
MOZ_ASSERT(IsOnGMPThread());
|
|
if (!mGMP) {
|
|
return;
|
|
}
|
|
mGMP->Close();
|
|
mGMP = nullptr;
|
|
}
|
|
|
|
} // namespace mozilla
|