зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1168674: [ogg] P3. Add theora MediaDataDecoder. r=me
MozReview-Commit-ID: 7ZJD21JMXBY --HG-- extra : rebase_source : 5108040c8de69e52bdd96842315e5a5e70c65ff7
This commit is contained in:
Родитель
847a1df7ea
Коммит
7f2fa5c20a
|
@ -10,6 +10,7 @@
|
||||||
#include "VorbisDecoder.h"
|
#include "VorbisDecoder.h"
|
||||||
#include "VPXDecoder.h"
|
#include "VPXDecoder.h"
|
||||||
#include "WAVDecoder.h"
|
#include "WAVDecoder.h"
|
||||||
|
#include "TheoraDecoder.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
@ -21,7 +22,8 @@ AgnosticDecoderModule::SupportsMimeType(const nsACString& aMimeType,
|
||||||
VPXDecoder::IsVPX(aMimeType) ||
|
VPXDecoder::IsVPX(aMimeType) ||
|
||||||
OpusDataDecoder::IsOpus(aMimeType) ||
|
OpusDataDecoder::IsOpus(aMimeType) ||
|
||||||
VorbisDataDecoder::IsVorbis(aMimeType) ||
|
VorbisDataDecoder::IsVorbis(aMimeType) ||
|
||||||
WaveDataDecoder::IsWave(aMimeType);
|
WaveDataDecoder::IsWave(aMimeType) ||
|
||||||
|
TheoraDecoder::IsTheora(aMimeType);
|
||||||
MOZ_LOG(sPDMLog, LogLevel::Debug, ("Agnostic decoder %s requested type",
|
MOZ_LOG(sPDMLog, LogLevel::Debug, ("Agnostic decoder %s requested type",
|
||||||
supports ? "supports" : "rejects"));
|
supports ? "supports" : "rejects"));
|
||||||
return supports;
|
return supports;
|
||||||
|
@ -34,6 +36,8 @@ AgnosticDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
|
||||||
|
|
||||||
if (VPXDecoder::IsVPX(aParams.mConfig.mMimeType)) {
|
if (VPXDecoder::IsVPX(aParams.mConfig.mMimeType)) {
|
||||||
m = new VPXDecoder(aParams);
|
m = new VPXDecoder(aParams);
|
||||||
|
} else if (TheoraDecoder::IsTheora(aParams.mConfig.mMimeType)) {
|
||||||
|
m = new TheoraDecoder(aParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.forget();
|
return m.forget();
|
||||||
|
|
|
@ -0,0 +1,243 @@
|
||||||
|
/* -*- 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 "TheoraDecoder.h"
|
||||||
|
#include "XiphExtradata.h"
|
||||||
|
#include "gfx2DGlue.h"
|
||||||
|
#include "nsError.h"
|
||||||
|
#include "TimeUnits.h"
|
||||||
|
#include "mozilla/PodOperations.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#undef LOG
|
||||||
|
#define LOG(arg, ...) MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Debug, ("TheoraDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
using namespace gfx;
|
||||||
|
using namespace layers;
|
||||||
|
|
||||||
|
extern LazyLogModule gMediaDecoderLog;
|
||||||
|
|
||||||
|
ogg_packet InitTheoraPacket(const unsigned char* aData, size_t aLength,
|
||||||
|
bool aBOS, bool aEOS,
|
||||||
|
int64_t aGranulepos, int64_t aPacketNo)
|
||||||
|
{
|
||||||
|
ogg_packet packet;
|
||||||
|
packet.packet = const_cast<unsigned char*>(aData);
|
||||||
|
packet.bytes = aLength;
|
||||||
|
packet.b_o_s = aBOS;
|
||||||
|
packet.e_o_s = aEOS;
|
||||||
|
packet.granulepos = aGranulepos;
|
||||||
|
packet.packetno = aPacketNo;
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
TheoraDecoder::TheoraDecoder(const CreateDecoderParams& aParams)
|
||||||
|
: mImageContainer(aParams.mImageContainer)
|
||||||
|
, mTaskQueue(aParams.mTaskQueue)
|
||||||
|
, mCallback(aParams.mCallback)
|
||||||
|
, mIsFlushing(false)
|
||||||
|
, mTheoraSetupInfo(nullptr)
|
||||||
|
, mTheoraDecoderContext(nullptr)
|
||||||
|
, mPacketCount(0)
|
||||||
|
, mInfo(aParams.VideoConfig())
|
||||||
|
{
|
||||||
|
MOZ_COUNT_CTOR(TheoraDecoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
TheoraDecoder::~TheoraDecoder()
|
||||||
|
{
|
||||||
|
MOZ_COUNT_DTOR(TheoraDecoder);
|
||||||
|
th_setup_free(mTheoraSetupInfo);
|
||||||
|
th_comment_clear(&mTheoraComment);
|
||||||
|
th_info_clear(&mTheoraInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
TheoraDecoder::Shutdown()
|
||||||
|
{
|
||||||
|
if (mTheoraDecoderContext) {
|
||||||
|
th_decode_free(mTheoraDecoderContext);
|
||||||
|
mTheoraDecoderContext = nullptr;
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<MediaDataDecoder::InitPromise>
|
||||||
|
TheoraDecoder::Init()
|
||||||
|
{
|
||||||
|
th_comment_init(&mTheoraComment);
|
||||||
|
th_info_init(&mTheoraInfo);
|
||||||
|
|
||||||
|
nsTArray<unsigned char*> headers;
|
||||||
|
nsTArray<size_t> headerLens;
|
||||||
|
if (!XiphExtradataToHeaders(headers, headerLens,
|
||||||
|
mInfo.mCodecSpecificConfig->Elements(),
|
||||||
|
mInfo.mCodecSpecificConfig->Length())) {
|
||||||
|
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < headers.Length(); i++) {
|
||||||
|
if (NS_FAILED(DoDecodeHeader(headers[i], headerLens[i]))) {
|
||||||
|
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mPacketCount != 3) {
|
||||||
|
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
mTheoraDecoderContext = th_decode_alloc(&mTheoraInfo, mTheoraSetupInfo);
|
||||||
|
if (mTheoraDecoderContext) {
|
||||||
|
return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
|
||||||
|
} else {
|
||||||
|
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
TheoraDecoder::Flush()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||||
|
mIsFlushing = true;
|
||||||
|
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([this] () {
|
||||||
|
// nothing to do for now.
|
||||||
|
});
|
||||||
|
SyncRunnable::DispatchToThread(mTaskQueue, r);
|
||||||
|
mIsFlushing = false;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
TheoraDecoder::DoDecodeHeader(const unsigned char* aData, size_t aLength)
|
||||||
|
{
|
||||||
|
bool bos = mPacketCount == 0;
|
||||||
|
ogg_packet pkt = InitTheoraPacket(aData, aLength, bos, false, 0, mPacketCount++);
|
||||||
|
|
||||||
|
int r = th_decode_headerin(&mTheoraInfo,
|
||||||
|
&mTheoraComment,
|
||||||
|
&mTheoraSetupInfo,
|
||||||
|
&pkt);
|
||||||
|
return r > 0 ? NS_OK : NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
TheoraDecoder::DoDecode(MediaRawData* aSample)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||||
|
|
||||||
|
const unsigned char* aData = aSample->Data();
|
||||||
|
size_t aLength = aSample->Size();
|
||||||
|
|
||||||
|
bool bos = mPacketCount == 0;
|
||||||
|
ogg_packet pkt = InitTheoraPacket(aData, aLength, bos, false, aSample->mTimecode, mPacketCount++);
|
||||||
|
|
||||||
|
int ret = th_decode_packetin(mTheoraDecoderContext, &pkt, nullptr);
|
||||||
|
if (ret == 0 || ret == TH_DUPFRAME) {
|
||||||
|
th_ycbcr_buffer ycbcr;
|
||||||
|
th_decode_ycbcr_out(mTheoraDecoderContext, ycbcr);
|
||||||
|
|
||||||
|
int hdec = !(mTheoraInfo.pixel_fmt & 1);
|
||||||
|
int vdec = !(mTheoraInfo.pixel_fmt & 2);
|
||||||
|
|
||||||
|
VideoData::YCbCrBuffer b;
|
||||||
|
b.mPlanes[0].mData = ycbcr[0].data;
|
||||||
|
b.mPlanes[0].mStride = ycbcr[0].stride;
|
||||||
|
b.mPlanes[0].mHeight = mTheoraInfo.frame_height;
|
||||||
|
b.mPlanes[0].mWidth = mTheoraInfo.frame_width;
|
||||||
|
b.mPlanes[0].mOffset = b.mPlanes[0].mSkip = 0;
|
||||||
|
|
||||||
|
b.mPlanes[1].mData = ycbcr[1].data;
|
||||||
|
b.mPlanes[1].mStride = ycbcr[1].stride;
|
||||||
|
b.mPlanes[1].mHeight = mTheoraInfo.frame_height >> vdec;
|
||||||
|
b.mPlanes[1].mWidth = mTheoraInfo.frame_width >> hdec;
|
||||||
|
b.mPlanes[1].mOffset = b.mPlanes[1].mSkip = 0;
|
||||||
|
|
||||||
|
b.mPlanes[2].mData = ycbcr[2].data;
|
||||||
|
b.mPlanes[2].mStride = ycbcr[2].stride;
|
||||||
|
b.mPlanes[2].mHeight = mTheoraInfo.frame_height >> vdec;
|
||||||
|
b.mPlanes[2].mWidth = mTheoraInfo.frame_width >> hdec;
|
||||||
|
b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0;
|
||||||
|
|
||||||
|
IntRect pictureArea(mTheoraInfo.pic_x, mTheoraInfo.pic_y,
|
||||||
|
mTheoraInfo.pic_width, mTheoraInfo.pic_height);
|
||||||
|
|
||||||
|
VideoInfo info;
|
||||||
|
info.mDisplay = mInfo.mDisplay;
|
||||||
|
RefPtr<VideoData> v = VideoData::Create(info,
|
||||||
|
mImageContainer,
|
||||||
|
aSample->mOffset,
|
||||||
|
aSample->mTime,
|
||||||
|
aSample->mDuration,
|
||||||
|
b,
|
||||||
|
aSample->mKeyframe,
|
||||||
|
aSample->mTimecode,
|
||||||
|
mInfo.ScaledImageRect(mTheoraInfo.frame_width,
|
||||||
|
mTheoraInfo.frame_height));
|
||||||
|
if (!v) {
|
||||||
|
LOG("Image allocation error source %ldx%ld display %ldx%ld picture %ldx%ld",
|
||||||
|
mTheoraInfo.frame_width, mTheoraInfo.frame_height, mInfo.mDisplay.width, mInfo.mDisplay.height,
|
||||||
|
mInfo.mImage.width, mInfo.mImage.height);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
mCallback->Output(v);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
LOG("Theora Decode error: %d", ret);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TheoraDecoder::ProcessDecode(MediaRawData* aSample)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||||
|
if (mIsFlushing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (DoDecode(aSample) == -1) {
|
||||||
|
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
|
||||||
|
} else if (mTaskQueue->IsEmpty()) {
|
||||||
|
mCallback->InputExhausted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
TheoraDecoder::Input(MediaRawData* aSample)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||||
|
mTaskQueue->Dispatch(NewRunnableMethod<RefPtr<MediaRawData>>(
|
||||||
|
this, &TheoraDecoder::ProcessDecode, aSample));
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TheoraDecoder::ProcessDrain()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||||
|
mCallback->DrainComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
TheoraDecoder::Drain()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||||
|
mTaskQueue->Dispatch(NewRunnableMethod(this, &TheoraDecoder::ProcessDrain));
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
bool
|
||||||
|
TheoraDecoder::IsTheora(const nsACString& aMimeType)
|
||||||
|
{
|
||||||
|
return aMimeType.EqualsLiteral("video/ogg; codecs=theora");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mozilla
|
||||||
|
#undef LOG
|
|
@ -0,0 +1,64 @@
|
||||||
|
/* -*- 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/. */
|
||||||
|
#if !defined(TheoraDecoder_h_)
|
||||||
|
#define TheoraDecoder_h_
|
||||||
|
|
||||||
|
#include "PlatformDecoderModule.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "ogg/ogg.h"
|
||||||
|
#include "theora/theoradec.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
using namespace layers;
|
||||||
|
|
||||||
|
class TheoraDecoder : public MediaDataDecoder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit TheoraDecoder(const CreateDecoderParams& aParams);
|
||||||
|
|
||||||
|
~TheoraDecoder();
|
||||||
|
|
||||||
|
RefPtr<InitPromise> Init() override;
|
||||||
|
nsresult Input(MediaRawData* aSample) override;
|
||||||
|
nsresult Flush() override;
|
||||||
|
nsresult Drain() override;
|
||||||
|
nsresult Shutdown() override;
|
||||||
|
|
||||||
|
// Return true if mimetype is a Theora codec
|
||||||
|
static bool IsTheora(const nsACString& aMimeType);
|
||||||
|
|
||||||
|
const char* GetDescriptionName() const override
|
||||||
|
{
|
||||||
|
return "theora video decoder";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsresult DoDecodeHeader(const unsigned char* aData, size_t aLength);
|
||||||
|
|
||||||
|
void ProcessDecode(MediaRawData* aSample);
|
||||||
|
int DoDecode(MediaRawData* aSample);
|
||||||
|
void ProcessDrain();
|
||||||
|
|
||||||
|
RefPtr<ImageContainer> mImageContainer;
|
||||||
|
RefPtr<TaskQueue> mTaskQueue;
|
||||||
|
MediaDataDecoderCallback* mCallback;
|
||||||
|
Atomic<bool> mIsFlushing;
|
||||||
|
|
||||||
|
// Theora header & decoder state
|
||||||
|
th_info mTheoraInfo;
|
||||||
|
th_comment mTheoraComment;
|
||||||
|
th_setup_info *mTheoraSetupInfo;
|
||||||
|
th_dec_ctx *mTheoraDecoderContext;
|
||||||
|
int mPacketCount;
|
||||||
|
|
||||||
|
const VideoInfo& mInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif
|
|
@ -7,6 +7,7 @@
|
||||||
EXPORTS += [
|
EXPORTS += [
|
||||||
'agnostic/AgnosticDecoderModule.h',
|
'agnostic/AgnosticDecoderModule.h',
|
||||||
'agnostic/OpusDecoder.h',
|
'agnostic/OpusDecoder.h',
|
||||||
|
'agnostic/TheoraDecoder.h',
|
||||||
'agnostic/VorbisDecoder.h',
|
'agnostic/VorbisDecoder.h',
|
||||||
'agnostic/VPXDecoder.h',
|
'agnostic/VPXDecoder.h',
|
||||||
'MediaTelemetryConstants.h',
|
'MediaTelemetryConstants.h',
|
||||||
|
@ -20,6 +21,7 @@ UNIFIED_SOURCES += [
|
||||||
'agnostic/AgnosticDecoderModule.cpp',
|
'agnostic/AgnosticDecoderModule.cpp',
|
||||||
'agnostic/BlankDecoderModule.cpp',
|
'agnostic/BlankDecoderModule.cpp',
|
||||||
'agnostic/OpusDecoder.cpp',
|
'agnostic/OpusDecoder.cpp',
|
||||||
|
'agnostic/TheoraDecoder.cpp',
|
||||||
'agnostic/VorbisDecoder.cpp',
|
'agnostic/VorbisDecoder.cpp',
|
||||||
'agnostic/VPXDecoder.cpp',
|
'agnostic/VPXDecoder.cpp',
|
||||||
'agnostic/WAVDecoder.cpp',
|
'agnostic/WAVDecoder.cpp',
|
||||||
|
|
Загрузка…
Ссылка в новой задаче