mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 886196 - Implement WMF platform decoder for FMP4 demuxer. r=padenot
This commit is contained in:
parent
c1e402fac2
commit
e02a05ef89
@ -466,12 +466,14 @@ VideoData* MediaDecoderReader::FindStartTime(int64_t& aOutStartTime)
|
||||
videoData = DecodeToFirstVideoData();
|
||||
if (videoData) {
|
||||
videoStartTime = videoData->mTime;
|
||||
LOG(PR_LOG_DEBUG, ("MediaDecoderReader::FindStartTime() video=%lld", videoStartTime));
|
||||
}
|
||||
}
|
||||
if (HasAudio()) {
|
||||
AudioData* audioData = DecodeToFirstAudioData();
|
||||
if (audioData) {
|
||||
audioStartTime = audioData->mTime;
|
||||
LOG(PR_LOG_DEBUG, ("MediaDecoderReader::FindStartTime() audio=%lld", audioStartTime));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,11 @@
|
||||
#include "MediaDecoderStateMachine.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "WinUtils.h"
|
||||
using namespace mozilla::widget;
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
MediaDecoderStateMachine* MP4Decoder::CreateStateMachine()
|
||||
@ -63,7 +68,13 @@ MP4Decoder::GetSupportedCodecs(const nsACString& aType,
|
||||
static bool
|
||||
HavePlatformMPEGDecoders()
|
||||
{
|
||||
return false;
|
||||
return
|
||||
#ifdef XP_WIN
|
||||
// We have H.264/AAC platform decoders on Windows Vista and up.
|
||||
WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION ||
|
||||
#endif
|
||||
// TODO: Other platforms...
|
||||
false;
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -6,13 +6,22 @@
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "WMFDecoderModule.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/* static */
|
||||
PlatformDecoderModule*
|
||||
PlatformDecoderModule::Create()
|
||||
{
|
||||
// TODO: Create appropriate PlatformDecoderModule...
|
||||
#ifdef XP_WIN
|
||||
nsAutoPtr<WMFDecoderModule> m(new WMFDecoderModule());
|
||||
if (NS_SUCCEEDED(m->Init())) {
|
||||
return m.forget();
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,10 @@ EXPORTS += [
|
||||
'MP4Decoder.h',
|
||||
'MP4Reader.h',
|
||||
'PlatformDecoderModule.h',
|
||||
'wmf/MFTDecoder.h',
|
||||
'wmf/WMFAudioDecoder.h',
|
||||
'wmf/WMFDecoderModule.h',
|
||||
'wmf/WMFVideoDecoder.h',
|
||||
]
|
||||
|
||||
EXPORTS.mp4_demuxer += [
|
||||
@ -48,6 +52,10 @@ SOURCES += [
|
||||
'MP4Decoder.cpp',
|
||||
'MP4Reader.cpp',
|
||||
'PlatformDecoderModule.cpp',
|
||||
'wmf/MFTDecoder.cpp',
|
||||
'wmf/WMFAudioDecoder.cpp',
|
||||
'wmf/WMFDecoderModule.cpp',
|
||||
'wmf/WMFVideoDecoder.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'gklayout'
|
||||
|
290
content/media/fmp4/wmf/MFTDecoder.cpp
Normal file
290
content/media/fmp4/wmf/MFTDecoder.cpp
Normal file
@ -0,0 +1,290 @@
|
||||
/* -*- 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 "MFTDecoder.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "WMFUtils.h"
|
||||
#include "prlog.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo* GetDemuxerLog();
|
||||
#define LOG(...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#else
|
||||
#define LOG(...)
|
||||
#endif
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
MFTDecoder::MFTDecoder()
|
||||
: mMFTProvidesOutputSamples(false)
|
||||
{
|
||||
memset(&mInputStreamInfo, 0, sizeof(MFT_INPUT_STREAM_INFO));
|
||||
memset(&mOutputStreamInfo, 0, sizeof(MFT_OUTPUT_STREAM_INFO));
|
||||
}
|
||||
|
||||
MFTDecoder::~MFTDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MFTDecoder::Create(const GUID& aMFTClsID)
|
||||
{
|
||||
// Create the IMFTransform to do the decoding.
|
||||
HRESULT hr;
|
||||
hr = CoCreateInstance(aMFTClsID,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IMFTransform,
|
||||
reinterpret_cast<void**>(static_cast<IMFTransform**>(byRef(mDecoder))));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MFTDecoder::SetMediaTypes(IMFMediaType* aInputType,
|
||||
const GUID& aOutputSubType)
|
||||
{
|
||||
mOutputSubtype = aOutputSubType;
|
||||
|
||||
// Set the input type to the one the caller gave us...
|
||||
HRESULT hr = mDecoder->SetInputType(0, aInputType, 0);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = SetDecoderOutputType();
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = mDecoder->GetInputStreamInfo(0, &mInputStreamInfo);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = mDecoder->GetOutputStreamInfo(0, &mOutputStreamInfo);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
mMFTProvidesOutputSamples = IsFlagSet(mOutputStreamInfo.dwFlags, MFT_OUTPUT_STREAM_PROVIDES_SAMPLES);
|
||||
|
||||
hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
TemporaryRef<IMFAttributes>
|
||||
MFTDecoder::GetAttributes()
|
||||
{
|
||||
RefPtr<IMFAttributes> attr;
|
||||
HRESULT hr = mDecoder->GetAttributes(byRef(attr));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
|
||||
return attr;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MFTDecoder::SetDecoderOutputType()
|
||||
{
|
||||
NS_ENSURE_TRUE(mDecoder != nullptr, E_POINTER);
|
||||
|
||||
// Iterate the enumerate the output types, until we find one compatible
|
||||
// with what we need.
|
||||
HRESULT hr;
|
||||
RefPtr<IMFMediaType> outputType;
|
||||
UINT32 typeIndex = 0;
|
||||
while (SUCCEEDED(mDecoder->GetOutputAvailableType(0, typeIndex++, byRef(outputType)))) {
|
||||
GUID subtype;
|
||||
hr = outputType->GetGUID(MF_MT_SUBTYPE, &subtype);
|
||||
if (FAILED(hr)) {
|
||||
continue;
|
||||
}
|
||||
if (subtype == mOutputSubtype) {
|
||||
hr = mDecoder->SetOutputType(0, outputType, 0);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MFTDecoder::SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData)
|
||||
{
|
||||
NS_ENSURE_TRUE(mDecoder != nullptr, E_POINTER);
|
||||
HRESULT hr = mDecoder->ProcessMessage(aMsg, aData);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MFTDecoder::CreateInputSample(const uint8_t* aData,
|
||||
uint32_t aDataSize,
|
||||
int64_t aTimestamp,
|
||||
RefPtr<IMFSample>* aOutSample)
|
||||
{
|
||||
NS_ENSURE_TRUE(mDecoder != nullptr, E_POINTER);
|
||||
|
||||
HRESULT hr;
|
||||
RefPtr<IMFSample> sample;
|
||||
hr = wmf::MFCreateSample(byRef(sample));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
RefPtr<IMFMediaBuffer> buffer;
|
||||
int32_t bufferSize = std::max(uint32_t(mInputStreamInfo.cbSize), aDataSize);
|
||||
UINT32 alignment = (mInputStreamInfo.cbAlignment > 1) ? mInputStreamInfo.cbAlignment - 1 : 0;
|
||||
hr = wmf::MFCreateAlignedMemoryBuffer(bufferSize, alignment, byRef(buffer));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
DWORD maxLength = 0;
|
||||
DWORD currentLength = 0;
|
||||
BYTE* dst = nullptr;
|
||||
hr = buffer->Lock(&dst, &maxLength, ¤tLength);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
// Copy data into sample's buffer.
|
||||
memcpy(dst, aData, aDataSize);
|
||||
|
||||
hr = buffer->Unlock();
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = buffer->SetCurrentLength(aDataSize);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = sample->AddBuffer(buffer);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = sample->SetSampleTime(UsecsToHNs(aTimestamp));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
*aOutSample = sample.forget();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MFTDecoder::CreateOutputSample(RefPtr<IMFSample>* aOutSample)
|
||||
{
|
||||
NS_ENSURE_TRUE(mDecoder != nullptr, E_POINTER);
|
||||
|
||||
HRESULT hr;
|
||||
RefPtr<IMFSample> sample;
|
||||
hr = wmf::MFCreateSample(byRef(sample));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
RefPtr<IMFMediaBuffer> buffer;
|
||||
int32_t bufferSize = mOutputStreamInfo.cbSize;
|
||||
UINT32 alignment = (mOutputStreamInfo.cbAlignment > 1) ? mOutputStreamInfo.cbAlignment - 1 : 0;
|
||||
hr = wmf::MFCreateAlignedMemoryBuffer(bufferSize, alignment, byRef(buffer));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
DWORD maxLength = 0;
|
||||
DWORD currentLength = 0;
|
||||
BYTE* dst = nullptr;
|
||||
|
||||
hr = sample->AddBuffer(buffer);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
*aOutSample = sample.forget();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MFTDecoder::Output(RefPtr<IMFSample>* aOutput)
|
||||
{
|
||||
NS_ENSURE_TRUE(mDecoder != nullptr, E_POINTER);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
MFT_OUTPUT_DATA_BUFFER output = {0};
|
||||
|
||||
RefPtr<IMFSample> sample;
|
||||
if (!mMFTProvidesOutputSamples) {
|
||||
hr = CreateOutputSample(&sample);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
output.pSample = sample;
|
||||
}
|
||||
|
||||
DWORD status = 0;
|
||||
hr = mDecoder->ProcessOutput(0, 1, &output, &status);
|
||||
if (output.pEvents) {
|
||||
// We must release this, as per the IMFTransform::ProcessOutput()
|
||||
// MSDN documentation.
|
||||
output.pEvents->Release();
|
||||
output.pEvents = nullptr;
|
||||
}
|
||||
|
||||
if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
|
||||
// Type change, probably geometric aperature change.
|
||||
// Reconfigure decoder output type, so that GetOutputMediaType()
|
||||
// returns the new type, and return the error code to caller.
|
||||
// This is an expected failure, so don't warn on encountering it.
|
||||
hr = SetDecoderOutputType();
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
// Return the error, so that the caller knows to retry.
|
||||
return MF_E_TRANSFORM_STREAM_CHANGE;
|
||||
}
|
||||
|
||||
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
|
||||
// Not enough input to produce output. This is an expected failure,
|
||||
// so don't warn on encountering it.
|
||||
return hr;
|
||||
}
|
||||
// Treat other errors as unexpected, and warn.
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
MOZ_ASSERT(output.pSample);
|
||||
|
||||
*aOutput = output.pSample; // AddRefs
|
||||
if (mMFTProvidesOutputSamples) {
|
||||
// If the MFT is providing samples, we must release the sample here.
|
||||
// Typically only the H.264 MFT provides samples when using DXVA,
|
||||
// and it always re-uses the same sample, so if we don't release it
|
||||
// MFT::ProcessOutput() deadlocks waiting for the sample to be released.
|
||||
output.pSample->Release();
|
||||
output.pSample = nullptr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MFTDecoder::Input(const uint8_t* aData,
|
||||
uint32_t aDataSize,
|
||||
int64_t aTimestamp)
|
||||
{
|
||||
NS_ENSURE_TRUE(mDecoder != nullptr, E_POINTER);
|
||||
|
||||
RefPtr<IMFSample> input;
|
||||
HRESULT hr = CreateInputSample(aData, aDataSize, aTimestamp, &input);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr) && input!=nullptr, hr);
|
||||
|
||||
hr = mDecoder->ProcessInput(0, input, 0);
|
||||
if (hr == MF_E_NOTACCEPTING) {
|
||||
// MFT *already* has enough data to produce a sample. Retrieve it.
|
||||
return MF_E_NOTACCEPTING;
|
||||
}
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MFTDecoder::Flush()
|
||||
{
|
||||
HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MFTDecoder::GetOutputMediaType(RefPtr<IMFMediaType>& aMediaType)
|
||||
{
|
||||
NS_ENSURE_TRUE(mDecoder, E_POINTER);
|
||||
return mDecoder->GetOutputCurrentType(0, byRef(aMediaType));
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
100
content/media/fmp4/wmf/MFTDecoder.h
Normal file
100
content/media/fmp4/wmf/MFTDecoder.h
Normal file
@ -0,0 +1,100 @@
|
||||
/* -*- 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(MFTDecoder_h_)
|
||||
#define MFTDecoder_h_
|
||||
|
||||
#include "wmf.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "nsIThread.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MFTDecoder {
|
||||
public:
|
||||
MFTDecoder();
|
||||
~MFTDecoder();
|
||||
|
||||
// Creates the MFT. First thing to do as part of setup.
|
||||
//
|
||||
// Params:
|
||||
// - aMFTClsID the clsid used by CoCreateInstance to instantiate the
|
||||
// decoder MFT.
|
||||
HRESULT Create(const GUID& aMFTClsID);
|
||||
|
||||
// Sets the input and output media types. Call after Init().
|
||||
//
|
||||
// Params:
|
||||
// - aInputType needs at least major and minor types set.
|
||||
// - aOutputSubType is the minor type of the same major type e.g.
|
||||
// MFVideoFormat_H264. This is used to select the output type out
|
||||
// of all the available output types of the MFT.
|
||||
HRESULT SetMediaTypes(IMFMediaType* aInputType,
|
||||
const GUID& aOutputSubType);
|
||||
|
||||
// Returns the MFT's IMFAttributes object.
|
||||
TemporaryRef<IMFAttributes> GetAttributes();
|
||||
|
||||
// Retrieves the media type being output. This may not be valid until
|
||||
// the first sample is decoded.
|
||||
HRESULT GetOutputMediaType(RefPtr<IMFMediaType>& aMediaType);
|
||||
|
||||
// Submits data into the MFT for processing.
|
||||
//
|
||||
// Returns:
|
||||
// - MF_E_NOTACCEPTING if the decoder can't accept input. The data
|
||||
// must be resubmitted after Output() stops producing output.
|
||||
HRESULT Input(const uint8_t* aData,
|
||||
uint32_t aDataSize,
|
||||
int64_t aTimestampUsecs);
|
||||
|
||||
// Retrieves output from the MFT. Call this once Input() returns
|
||||
// MF_E_NOTACCEPTING. Some MFTs with hardware acceleration (the H.264
|
||||
// decoder MFT in particular) can't handle it if clients hold onto
|
||||
// references to the output IMFSample, so don't do that.
|
||||
//
|
||||
// Returns:
|
||||
// - MF_E_TRANSFORM_STREAM_CHANGE if the underlying stream output
|
||||
// type changed. Retrieve the output media type and reconfig client,
|
||||
// else you may misinterpret the MFT's output.
|
||||
// - MF_E_TRANSFORM_NEED_MORE_INPUT if no output can be produced
|
||||
// due to lack of input.
|
||||
// - S_OK if an output frame is produced.
|
||||
HRESULT Output(RefPtr<IMFSample>* aOutput);
|
||||
|
||||
// Sends a flush message to the MFT. This causes it to discard all
|
||||
// input data. Use before seeking.
|
||||
HRESULT Flush();
|
||||
|
||||
// Sends a message to the MFT.
|
||||
HRESULT SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData);
|
||||
|
||||
private:
|
||||
|
||||
HRESULT SetDecoderOutputType();
|
||||
|
||||
HRESULT CreateInputSample(const uint8_t* aData,
|
||||
uint32_t aDataSize,
|
||||
int64_t aTimestampUsecs,
|
||||
RefPtr<IMFSample>* aOutSample);
|
||||
|
||||
HRESULT CreateOutputSample(RefPtr<IMFSample>* aOutSample);
|
||||
|
||||
MFT_INPUT_STREAM_INFO mInputStreamInfo;
|
||||
MFT_OUTPUT_STREAM_INFO mOutputStreamInfo;
|
||||
|
||||
RefPtr<IMFTransform> mDecoder;
|
||||
|
||||
GUID mOutputSubtype;
|
||||
|
||||
// True if the IMFTransform allocates the samples that it returns.
|
||||
bool mMFTProvidesOutputSamples;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
283
content/media/fmp4/wmf/WMFAudioDecoder.cpp
Normal file
283
content/media/fmp4/wmf/WMFAudioDecoder.cpp
Normal file
@ -0,0 +1,283 @@
|
||||
/* -*- 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 "WMFAudioDecoder.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "WMFUtils.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include "prlog.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo* GetDemuxerLog();
|
||||
#define LOG(...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#else
|
||||
#define LOG(...)
|
||||
#endif
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
WMFAudioDecoder::WMFAudioDecoder()
|
||||
: mAudioChannels(0),
|
||||
mAudioBytesPerSample(0),
|
||||
mAudioRate(0),
|
||||
mLastStreamOffset(0),
|
||||
mAudioFrameOffset(0),
|
||||
mAudioFrameSum(0),
|
||||
mMustRecaptureAudioPosition(true)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
AACAudioSpecificConfigToUserData(const uint8_t* aAudioSpecConfig,
|
||||
uint32_t aConfigLength,
|
||||
nsTArray<BYTE>& aOutUserData)
|
||||
{
|
||||
MOZ_ASSERT(aOutUserData.IsEmpty());
|
||||
|
||||
// The MF_MT_USER_DATA for AAC is defined here:
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd742784%28v=vs.85%29.aspx
|
||||
//
|
||||
// For MFAudioFormat_AAC, MF_MT_USER_DATA contains the portion of
|
||||
// the HEAACWAVEINFO structure that appears after the WAVEFORMATEX
|
||||
// structure (that is, after the wfx member). This is followed by
|
||||
// the AudioSpecificConfig() data, as defined by ISO/IEC 14496-3.
|
||||
// [...]
|
||||
// The length of the AudioSpecificConfig() data is 2 bytes for AAC-LC
|
||||
// or HE-AAC with implicit signaling of SBR/PS. It is more than 2 bytes
|
||||
// for HE-AAC with explicit signaling of SBR/PS.
|
||||
//
|
||||
// The value of audioObjectType as defined in AudioSpecificConfig()
|
||||
// must be 2, indicating AAC-LC. The value of extensionAudioObjectType
|
||||
// must be 5 for SBR or 29 for PS.
|
||||
//
|
||||
// HEAACWAVEINFO structure:
|
||||
// typedef struct heaacwaveinfo_tag {
|
||||
// WAVEFORMATEX wfx;
|
||||
// WORD wPayloadType;
|
||||
// WORD wAudioProfileLevelIndication;
|
||||
// WORD wStructType;
|
||||
// WORD wReserved1;
|
||||
// DWORD dwReserved2;
|
||||
// }
|
||||
const UINT32 heeInfoLen = 4 * sizeof(WORD) + sizeof(DWORD);
|
||||
|
||||
// The HEAACWAVEINFO must have payload and profile set,
|
||||
// the rest can be all 0x00.
|
||||
BYTE heeInfo[heeInfoLen] = {0};
|
||||
WORD* w = (WORD*)heeInfo;
|
||||
w[0] = 0x1; // Payload type ADTS
|
||||
w[1] = 0xFE; // Profile level indication, none specified.
|
||||
|
||||
aOutUserData.AppendElements(heeInfo, heeInfoLen);
|
||||
aOutUserData.AppendElements(aAudioSpecConfig, aConfigLength);
|
||||
}
|
||||
|
||||
nsresult
|
||||
WMFAudioDecoder::Init(uint32_t aChannelCount,
|
||||
uint32_t aSampleRate,
|
||||
uint16_t aBitsPerSample,
|
||||
const uint8_t* aAudioSpecConfig,
|
||||
uint32_t aConfigLength)
|
||||
{
|
||||
mDecoder = new MFTDecoder();
|
||||
|
||||
HRESULT hr = mDecoder->Create(CLSID_CMSAACDecMFT);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
// Setup input/output media types
|
||||
RefPtr<IMFMediaType> type;
|
||||
|
||||
hr = wmf::MFCreateMediaType(byRef(type));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
hr = type->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_AAC);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
hr = type->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, aSampleRate);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
hr = type->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, aChannelCount);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
hr = type->SetUINT32(MF_MT_AAC_PAYLOAD_TYPE, 0x1); // ADTS
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
nsTArray<BYTE> userData;
|
||||
AACAudioSpecificConfigToUserData(aAudioSpecConfig,
|
||||
aConfigLength,
|
||||
userData);
|
||||
|
||||
hr = type->SetBlob(MF_MT_USER_DATA,
|
||||
userData.Elements(),
|
||||
userData.Length());
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
hr = mDecoder->SetMediaTypes(type, MFAudioFormat_PCM);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
mAudioChannels = aChannelCount;
|
||||
mAudioBytesPerSample = aBitsPerSample / 8;
|
||||
mAudioRate = aSampleRate;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WMFAudioDecoder::Shutdown()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
DecoderStatus
|
||||
WMFAudioDecoder::Input(const uint8_t* aData,
|
||||
uint32_t aLength,
|
||||
Microseconds aDTS,
|
||||
Microseconds aPTS,
|
||||
int64_t aOffsetInStream)
|
||||
{
|
||||
mLastStreamOffset = aOffsetInStream;
|
||||
HRESULT hr = mDecoder->Input(aData, aLength, aPTS);
|
||||
if (hr == MF_E_NOTACCEPTING) {
|
||||
return DECODE_STATUS_NOT_ACCEPTING;
|
||||
}
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
|
||||
|
||||
return DECODE_STATUS_OK;
|
||||
}
|
||||
|
||||
DecoderStatus
|
||||
WMFAudioDecoder::Output(nsAutoPtr<MediaData>& aOutData)
|
||||
{
|
||||
DecoderStatus status;
|
||||
do {
|
||||
status = OutputNonNegativeTimeSamples(aOutData);
|
||||
} while (status == DECODE_STATUS_OK && !aOutData);
|
||||
return status;
|
||||
}
|
||||
|
||||
DecoderStatus
|
||||
WMFAudioDecoder::OutputNonNegativeTimeSamples(nsAutoPtr<MediaData>& aOutData)
|
||||
{
|
||||
|
||||
aOutData = nullptr;
|
||||
RefPtr<IMFSample> sample;
|
||||
HRESULT hr = mDecoder->Output(&sample);
|
||||
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
|
||||
return DECODE_STATUS_NEED_MORE_INPUT;
|
||||
}
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
|
||||
|
||||
RefPtr<IMFMediaBuffer> buffer;
|
||||
hr = sample->ConvertToContiguousBuffer(byRef(buffer));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
|
||||
|
||||
BYTE* data = nullptr; // Note: *data will be owned by the IMFMediaBuffer, we don't need to free it.
|
||||
DWORD maxLength = 0, currentLength = 0;
|
||||
hr = buffer->Lock(&data, &maxLength, ¤tLength);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
|
||||
|
||||
int32_t numSamples = currentLength / mAudioBytesPerSample;
|
||||
int32_t numFrames = numSamples / mAudioChannels;
|
||||
|
||||
// Sometimes when starting decoding, the AAC decoder gives us samples
|
||||
// with a negative timestamp. AAC does usually have preroll (or encoder
|
||||
// delay) encoded into its bitstream, but the amount encoded to the stream
|
||||
// is variable, and it not signalled in-bitstream. There is sometimes
|
||||
// signalling in the MP4 container what the preroll amount, but it's
|
||||
// inconsistent. It looks like WMF's AAC encoder may take this into
|
||||
// account, so strip off samples with a negative timestamp to get us
|
||||
// to a 0-timestamp start. This seems to maintain A/V sync, so we can run
|
||||
// with this until someone complains...
|
||||
|
||||
// We calculate the timestamp and the duration based on the number of audio
|
||||
// frames we've already played. We don't trust the timestamp stored on the
|
||||
// IMFSample, as sometimes it's wrong, possibly due to buggy encoders?
|
||||
|
||||
// If this sample block comes after a discontinuity (i.e. a gap or seek)
|
||||
// reset the frame counters, and capture the timestamp. Future timestamps
|
||||
// will be offset from this block's timestamp.
|
||||
UINT32 discontinuity = false;
|
||||
int32_t numFramesToStrip = 0;
|
||||
sample->GetUINT32(MFSampleExtension_Discontinuity, &discontinuity);
|
||||
if (mMustRecaptureAudioPosition || discontinuity) {
|
||||
mAudioFrameSum = 0;
|
||||
LONGLONG timestampHns = 0;
|
||||
hr = sample->GetSampleTime(×tampHns);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
|
||||
hr = HNsToFrames(timestampHns, mAudioRate, &mAudioFrameOffset);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
|
||||
if (mAudioFrameOffset < 0) {
|
||||
// First sample has a negative timestamp. Strip off the samples until
|
||||
// we reach positive territory.
|
||||
numFramesToStrip = -mAudioFrameOffset;
|
||||
mAudioFrameOffset = 0;
|
||||
}
|
||||
mMustRecaptureAudioPosition = false;
|
||||
}
|
||||
MOZ_ASSERT(numFramesToStrip >= 0);
|
||||
int32_t offset = std::min<int32_t>(numFramesToStrip, numFrames);
|
||||
numFrames -= offset;
|
||||
numSamples -= offset * mAudioChannels;
|
||||
MOZ_ASSERT(numFrames >= 0);
|
||||
MOZ_ASSERT(numSamples >= 0);
|
||||
if (numFrames == 0) {
|
||||
// All data from this chunk stripped, loop back and try to output the next
|
||||
// frame, if possible.
|
||||
return DECODE_STATUS_OK;
|
||||
}
|
||||
|
||||
nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[numSamples]);
|
||||
|
||||
// Just assume PCM output for now...
|
||||
MOZ_ASSERT(mAudioBytesPerSample == 2);
|
||||
int16_t* pcm = ((int16_t*)data) + (offset * mAudioChannels);
|
||||
MOZ_ASSERT(pcm >= (int16_t*)data);
|
||||
MOZ_ASSERT(pcm <= (int16_t*)(data + currentLength));
|
||||
MOZ_ASSERT(pcm+numSamples <= (int16_t*)(data + currentLength));
|
||||
for (int32_t i = 0; i < numSamples; ++i) {
|
||||
audioData[i] = AudioSampleToFloat(pcm[i]);
|
||||
}
|
||||
|
||||
buffer->Unlock();
|
||||
int64_t timestamp;
|
||||
hr = FramesToUsecs(mAudioFrameOffset + mAudioFrameSum, mAudioRate, ×tamp);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
|
||||
|
||||
mAudioFrameSum += numFrames;
|
||||
|
||||
int64_t duration;
|
||||
hr = FramesToUsecs(numFrames, mAudioRate, &duration);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
|
||||
|
||||
aOutData = new AudioData(mLastStreamOffset,
|
||||
timestamp,
|
||||
duration,
|
||||
numFrames,
|
||||
audioData.forget(),
|
||||
mAudioChannels);
|
||||
|
||||
#ifdef LOG_SAMPLE_DECODE
|
||||
LOG("Decoded audio sample! timestamp=%lld duration=%lld currentLength=%u",
|
||||
timestamp, duration, currentLength);
|
||||
#endif
|
||||
|
||||
return DECODE_STATUS_OK;
|
||||
}
|
||||
|
||||
DecoderStatus
|
||||
WMFAudioDecoder::Flush()
|
||||
{
|
||||
NS_ENSURE_TRUE(mDecoder, DECODE_STATUS_ERROR);
|
||||
HRESULT hr = mDecoder->Flush();
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
|
||||
return DECODE_STATUS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
77
content/media/fmp4/wmf/WMFAudioDecoder.h
Normal file
77
content/media/fmp4/wmf/WMFAudioDecoder.h
Normal file
@ -0,0 +1,77 @@
|
||||
/* -*- 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(WMFAudioDecoder_h_)
|
||||
#define WMFAudioDecoder_h_
|
||||
|
||||
#include "wmf.h"
|
||||
#include "MP4Reader.h"
|
||||
#include "MFTDecoder.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class WMFAudioDecoder : public MediaDataDecoder {
|
||||
public:
|
||||
WMFAudioDecoder();
|
||||
|
||||
nsresult Init(uint32_t aChannelCount,
|
||||
uint32_t aSampleRate,
|
||||
uint16_t aBitsPerSample,
|
||||
const uint8_t* aUserData,
|
||||
uint32_t aUserDataLength);
|
||||
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
// Inserts data into the decoder's pipeline.
|
||||
virtual DecoderStatus Input(const uint8_t* aData,
|
||||
uint32_t aLength,
|
||||
Microseconds aDTS,
|
||||
Microseconds aPTS,
|
||||
int64_t aOffsetInStream);
|
||||
|
||||
// Blocks until a decoded sample is produced by the decoder.
|
||||
virtual DecoderStatus Output(nsAutoPtr<MediaData>& aOutData);
|
||||
|
||||
virtual DecoderStatus Flush() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
// A helper for Output() above. This has the same interface as Output()
|
||||
// above, except that it returns DECODE_STATUS_OK and sets aOutData to
|
||||
// nullptr when all the output samples have been stripped due to having
|
||||
// negative timestamps. WMF's AAC decoder sometimes output negatively
|
||||
// timestamped samples, presumably they're the preroll samples, and we
|
||||
// strip them.
|
||||
DecoderStatus OutputNonNegativeTimeSamples(nsAutoPtr<MediaData>& aOutData);
|
||||
|
||||
nsAutoPtr<MFTDecoder> mDecoder;
|
||||
|
||||
uint32_t mAudioChannels;
|
||||
uint32_t mAudioBytesPerSample;
|
||||
uint32_t mAudioRate;
|
||||
|
||||
// The last offset into the media resource that was passed into Input().
|
||||
// This is used to approximate the decoder's position in the media resource.
|
||||
int64_t mLastStreamOffset;
|
||||
|
||||
// The offset, in audio frames, at which playback started since the
|
||||
// last discontinuity.
|
||||
int64_t mAudioFrameOffset;
|
||||
// The number of audio frames that we've played since the last
|
||||
// discontinuity.
|
||||
int64_t mAudioFrameSum;
|
||||
// True if we need to re-initialize mAudioFrameOffset and mAudioFrameSum
|
||||
// from the next audio packet we decode. This happens after a seek, since
|
||||
// WMF doesn't mark a stream as having a discontinuity after a seek(0).
|
||||
bool mMustRecaptureAudioPosition;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
104
content/media/fmp4/wmf/WMFDecoderModule.cpp
Normal file
104
content/media/fmp4/wmf/WMFDecoderModule.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
/* -*- 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 "wmf.h"
|
||||
#include "WMFDecoderModule.h"
|
||||
#include "WMFDecoder.h"
|
||||
#include "WMFVideoDecoder.h"
|
||||
#include "WMFAudioDecoder.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
WMFDecoderModule::WMFDecoderModule()
|
||||
: mDXVAEnabled(Preferences::GetBool("media.windows-media-foundation.use-dxva", false))
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
}
|
||||
|
||||
WMFDecoderModule::~WMFDecoderModule()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
}
|
||||
|
||||
nsresult
|
||||
WMFDecoderModule::Init()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
if (!Preferences::GetBool("media.windows-media-foundation.enabled", false)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult rv = WMFDecoder::LoadDLLs();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (FAILED(wmf::MFStartup())) {
|
||||
NS_WARNING("Failed to initialize Windows Media Foundation");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WMFDecoderModule::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
|
||||
DebugOnly<HRESULT> hr = wmf::MFShutdown();
|
||||
NS_ASSERTION(SUCCEEDED(hr), "MFShutdown failed");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MediaDataDecoder*
|
||||
WMFDecoderModule::CreateVideoDecoder(mozilla::layers::LayersBackend aLayersBackend,
|
||||
mozilla::layers::ImageContainer* aImageContainer)
|
||||
{
|
||||
nsAutoPtr<WMFVideoDecoder> decoder(new WMFVideoDecoder(mDXVAEnabled));
|
||||
nsresult rv = decoder->Init(aLayersBackend, aImageContainer);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
MediaDataDecoder*
|
||||
WMFDecoderModule::CreateAudioDecoder(uint32_t aChannelCount,
|
||||
uint32_t aSampleRate,
|
||||
uint16_t aBitsPerSample,
|
||||
const uint8_t* aUserData,
|
||||
uint32_t aUserDataLength)
|
||||
{
|
||||
nsAutoPtr<WMFAudioDecoder> decoder(new WMFAudioDecoder());
|
||||
nsresult rv = decoder->Init(aChannelCount,
|
||||
aSampleRate,
|
||||
aBitsPerSample,
|
||||
aUserData,
|
||||
aUserDataLength);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
void
|
||||
WMFDecoderModule::OnDecodeThreadStart()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "Must not be on main thread.");
|
||||
// XXX WebAudio can call this on the main thread when using deprecated APIs.
|
||||
// That should not happen. You cannot change the concurrency model once already set.
|
||||
// The main thread will continue to be STA, which seems to work, but MSDN
|
||||
// recommends that MTA be used.
|
||||
// TODO: enforce that WebAudio stops doing that!
|
||||
CoInitializeEx(0, COINIT_MULTITHREADED);
|
||||
}
|
||||
|
||||
void
|
||||
WMFDecoderModule::OnDecodeThreadFinish()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "Must be on main thread.");
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
48
content/media/fmp4/wmf/WMFDecoderModule.h
Normal file
48
content/media/fmp4/wmf/WMFDecoderModule.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* -*- 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(WMFPlatformDecoderModule_h_)
|
||||
#define WMFPlatformDecoderModule_h_
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class WMFDecoderModule : public PlatformDecoderModule {
|
||||
public:
|
||||
WMFDecoderModule();
|
||||
virtual ~WMFDecoderModule();
|
||||
|
||||
// Initializes the module, loads required dynamic libraries, etc.
|
||||
// Main thread only.
|
||||
nsresult Init();
|
||||
|
||||
// Called when the decoders have shutdown. Main thread only.
|
||||
// Does this really need to be main thread only????
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
// Decode thread.
|
||||
virtual MediaDataDecoder*
|
||||
CreateVideoDecoder(mozilla::layers::LayersBackend aLayersBackend,
|
||||
mozilla::layers::ImageContainer* aImageContainer) MOZ_OVERRIDE;
|
||||
|
||||
// Decode thread.
|
||||
virtual MediaDataDecoder* CreateAudioDecoder(uint32_t aChannelCount,
|
||||
uint32_t aSampleRate,
|
||||
uint16_t aBitsPerSample,
|
||||
const uint8_t* aUserData,
|
||||
uint32_t aUserDataLength) MOZ_OVERRIDE;
|
||||
|
||||
// Platform decoders can override these. Base implementation does nothing.
|
||||
virtual void OnDecodeThreadStart() MOZ_OVERRIDE;
|
||||
virtual void OnDecodeThreadFinish() MOZ_OVERRIDE;
|
||||
private:
|
||||
const bool mDXVAEnabled;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
412
content/media/fmp4/wmf/WMFVideoDecoder.cpp
Normal file
412
content/media/fmp4/wmf/WMFVideoDecoder.cpp
Normal file
@ -0,0 +1,412 @@
|
||||
/* -*- 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 "WMFVideoDecoder.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "WMFUtils.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "DXVA2Manager.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "WinUtils.h"
|
||||
#include "Layers.h"
|
||||
#include "mozilla/layers/LayersTypes.h"
|
||||
#include "prlog.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo* GetDemuxerLog();
|
||||
#define LOG(...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#else
|
||||
#define LOG(...)
|
||||
#endif
|
||||
|
||||
using mozilla::layers::Image;
|
||||
using mozilla::layers::LayerManager;
|
||||
using mozilla::layers::LayersBackend;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
WMFVideoDecoder::WMFVideoDecoder(bool aDXVAEnabled)
|
||||
: mVideoStride(0),
|
||||
mVideoWidth(0),
|
||||
mVideoHeight(0),
|
||||
mLastStreamOffset(0),
|
||||
mDXVAEnabled(aDXVAEnabled),
|
||||
mIsRunningOnVista(widget::WinUtils::GetWindowsVersion() == widget::WinUtils::WIN7_VERSION),
|
||||
mUseHwAccel(false)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Must be on main thread.");
|
||||
MOZ_COUNT_CTOR(WMFVideoDecoder);
|
||||
}
|
||||
|
||||
WMFVideoDecoder::~WMFVideoDecoder()
|
||||
{
|
||||
MOZ_COUNT_DTOR(WMFVideoDecoder);
|
||||
}
|
||||
|
||||
class CreateDXVAManagerEvent : public nsRunnable {
|
||||
public:
|
||||
NS_IMETHOD Run() {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
|
||||
mDXVA2Manager = DXVA2Manager::Create();
|
||||
return NS_OK;
|
||||
}
|
||||
nsAutoPtr<DXVA2Manager> mDXVA2Manager;
|
||||
};
|
||||
|
||||
bool
|
||||
WMFVideoDecoder::InitializeDXVA(mozilla::layers::LayersBackend aLayersBackend)
|
||||
{
|
||||
// If we use DXVA but aren't running with a D3D layer manager then the
|
||||
// readback of decoded video frames from GPU to CPU memory grinds painting
|
||||
// to a halt, and makes playback performance *worse*.
|
||||
if (!mDXVAEnabled ||
|
||||
(aLayersBackend != LayersBackend::LAYERS_D3D9 &&
|
||||
aLayersBackend != LayersBackend::LAYERS_D3D10)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DXVA manager must be created on the main thread.
|
||||
nsRefPtr<CreateDXVAManagerEvent> event(new CreateDXVAManagerEvent());
|
||||
NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
|
||||
mDXVA2Manager = event->mDXVA2Manager;
|
||||
|
||||
return mDXVA2Manager != nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WMFVideoDecoder::Init(mozilla::layers::LayersBackend aLayersBackend,
|
||||
mozilla::layers::ImageContainer* aImageContainer)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aImageContainer);
|
||||
|
||||
bool useDxva= InitializeDXVA(aLayersBackend);
|
||||
|
||||
mDecoder = new MFTDecoder();
|
||||
|
||||
HRESULT hr = mDecoder->Create(CLSID_CMSH264DecoderMFT);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
if (useDxva) {
|
||||
RefPtr<IMFAttributes> attr(mDecoder->GetAttributes());
|
||||
|
||||
UINT32 aware = 0;
|
||||
if (attr) {
|
||||
attr->GetUINT32(MF_SA_D3D_AWARE, &aware);
|
||||
}
|
||||
if (aware) {
|
||||
// TODO: Test if I need this anywhere... Maybe on Vista?
|
||||
//hr = attr->SetUINT32(CODECAPI_AVDecVideoAcceleration_H264, TRUE);
|
||||
//NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
MOZ_ASSERT(mDXVA2Manager);
|
||||
ULONG_PTR manager = ULONG_PTR(mDXVA2Manager->GetDXVADeviceManager());
|
||||
hr = mDecoder->SendMFTMessage(MFT_MESSAGE_SET_D3D_MANAGER, manager);
|
||||
if (SUCCEEDED(hr)) {
|
||||
mUseHwAccel = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the input/output media types.
|
||||
RefPtr<IMFMediaType> type;
|
||||
hr = wmf::MFCreateMediaType(byRef(type));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
hr = type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
hr = type->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_MixedInterlaceOrProgressive);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
GUID outputType = mUseHwAccel ? MFVideoFormat_NV12 : MFVideoFormat_YV12;
|
||||
hr = mDecoder->SetMediaTypes(type, outputType);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
mImageContainer = aImageContainer;
|
||||
|
||||
LOG("Video Decoder initialized, Using DXVA: %s", (mUseHwAccel ? "Yes" : "No"));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFVideoDecoder::ConfigureVideoFrameGeometry()
|
||||
{
|
||||
RefPtr<IMFMediaType> mediaType;
|
||||
HRESULT hr = mDecoder->GetOutputMediaType(mediaType);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
// Verify that the video subtype is what we expect it to be.
|
||||
// When using hardware acceleration/DXVA2 the video format should
|
||||
// be NV12, which is DXVA2's preferred format. For software decoding
|
||||
// we use YV12, as that's easier for us to stick into our rendering
|
||||
// pipeline than NV12. NV12 has interleaved UV samples, whereas YV12
|
||||
// is a planar format.
|
||||
GUID videoFormat;
|
||||
hr = mediaType->GetGUID(MF_MT_SUBTYPE, &videoFormat);
|
||||
NS_ENSURE_TRUE(videoFormat == MFVideoFormat_NV12 || !mUseHwAccel, E_FAIL);
|
||||
NS_ENSURE_TRUE(videoFormat == MFVideoFormat_YV12 || mUseHwAccel, E_FAIL);
|
||||
|
||||
nsIntRect pictureRegion;
|
||||
hr = GetPictureRegion(mediaType, pictureRegion);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
UINT32 width = 0, height = 0;
|
||||
hr = MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &width, &height);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
uint32_t aspectNum = 0, aspectDenom = 0;
|
||||
hr = MFGetAttributeRatio(mediaType,
|
||||
MF_MT_PIXEL_ASPECT_RATIO,
|
||||
&aspectNum,
|
||||
&aspectDenom);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
// Calculate and validate the picture region and frame dimensions after
|
||||
// scaling by the pixel aspect ratio.
|
||||
nsIntSize frameSize = nsIntSize(width, height);
|
||||
nsIntSize displaySize = nsIntSize(pictureRegion.width, pictureRegion.height);
|
||||
ScaleDisplayByAspectRatio(displaySize, float(aspectNum) / float(aspectDenom));
|
||||
if (!VideoInfo::ValidateVideoRegion(frameSize, pictureRegion, displaySize)) {
|
||||
// Video track's frame sizes will overflow. Ignore the video track.
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Success! Save state.
|
||||
mVideoInfo.mDisplay = displaySize;
|
||||
mVideoInfo.mHasVideo = true;
|
||||
GetDefaultStride(mediaType, &mVideoStride);
|
||||
mVideoWidth = width;
|
||||
mVideoHeight = height;
|
||||
mPictureRegion = pictureRegion;
|
||||
|
||||
LOG("WMFReader frame geometry frame=(%u,%u) stride=%u picture=(%d, %d, %d, %d) display=(%d,%d) PAR=%d:%d",
|
||||
width, height,
|
||||
mVideoStride,
|
||||
mPictureRegion.x, mPictureRegion.y, mPictureRegion.width, mPictureRegion.height,
|
||||
displaySize.width, displaySize.height,
|
||||
aspectNum, aspectDenom);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WMFVideoDecoder::Shutdown()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Inserts data into the decoder's pipeline.
|
||||
DecoderStatus
|
||||
WMFVideoDecoder::Input(const uint8_t* aData,
|
||||
uint32_t aLength,
|
||||
Microseconds aDTS,
|
||||
Microseconds aPTS,
|
||||
int64_t aOffsetInStream)
|
||||
{
|
||||
mLastStreamOffset = aOffsetInStream;
|
||||
HRESULT hr = mDecoder->Input(aData, aLength, aPTS);
|
||||
if (hr == MF_E_NOTACCEPTING) {
|
||||
return DECODE_STATUS_NOT_ACCEPTING;
|
||||
}
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
|
||||
|
||||
return DECODE_STATUS_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFVideoDecoder::CreateBasicVideoFrame(IMFSample* aSample,
|
||||
VideoData** aOutVideoData)
|
||||
{
|
||||
NS_ENSURE_TRUE(aSample, E_POINTER);
|
||||
NS_ENSURE_TRUE(aOutVideoData, E_POINTER);
|
||||
|
||||
*aOutVideoData = nullptr;
|
||||
|
||||
HRESULT hr;
|
||||
RefPtr<IMFMediaBuffer> buffer;
|
||||
|
||||
// Must convert to contiguous buffer to use IMD2DBuffer interface.
|
||||
hr = aSample->ConvertToContiguousBuffer(byRef(buffer));
|
||||
NS_ENSURE_TRUE(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;
|
||||
RefPtr<IMF2DBuffer> twoDBuffer;
|
||||
hr = buffer->QueryInterface(static_cast<IMF2DBuffer**>(byRef(twoDBuffer)));
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = twoDBuffer->Lock2D(&data, &stride);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
} else {
|
||||
hr = buffer->Lock(&data, NULL, NULL);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
stride = mVideoStride;
|
||||
}
|
||||
|
||||
// YV12, planar format: [YYYY....][VVVV....][UUUU....]
|
||||
// i.e., Y, then V, then U.
|
||||
VideoData::YCbCrBuffer b;
|
||||
|
||||
// Y (Y') plane
|
||||
b.mPlanes[0].mData = data;
|
||||
b.mPlanes[0].mStride = stride;
|
||||
b.mPlanes[0].mHeight = mVideoHeight;
|
||||
b.mPlanes[0].mWidth = mVideoWidth;
|
||||
b.mPlanes[0].mOffset = 0;
|
||||
b.mPlanes[0].mSkip = 0;
|
||||
|
||||
// The V and U planes are stored 16-row-aligned, so we need to add padding
|
||||
// to the row heights to ensure the Y'CbCr planes are referenced properly.
|
||||
uint32_t padding = 0;
|
||||
if (mVideoHeight % 16 != 0) {
|
||||
padding = 16 - (mVideoHeight % 16);
|
||||
}
|
||||
uint32_t y_size = stride * (mVideoHeight + padding);
|
||||
uint32_t v_size = stride * (mVideoHeight + padding) / 4;
|
||||
uint32_t halfStride = (stride + 1) / 2;
|
||||
uint32_t halfHeight = (mVideoHeight + 1) / 2;
|
||||
uint32_t halfWidth = (mVideoWidth + 1) / 2;
|
||||
|
||||
// U plane (Cb)
|
||||
b.mPlanes[1].mData = data + y_size + v_size;
|
||||
b.mPlanes[1].mStride = halfStride;
|
||||
b.mPlanes[1].mHeight = halfHeight;
|
||||
b.mPlanes[1].mWidth = halfWidth;
|
||||
b.mPlanes[1].mOffset = 0;
|
||||
b.mPlanes[1].mSkip = 0;
|
||||
|
||||
// V plane (Cr)
|
||||
b.mPlanes[2].mData = data + y_size;
|
||||
b.mPlanes[2].mStride = halfStride;
|
||||
b.mPlanes[2].mHeight = halfHeight;
|
||||
b.mPlanes[2].mWidth = halfWidth;
|
||||
b.mPlanes[2].mOffset = 0;
|
||||
b.mPlanes[2].mSkip = 0;
|
||||
|
||||
Microseconds pts = GetSampleTime(aSample);
|
||||
Microseconds duration = GetSampleDuration(aSample);
|
||||
VideoData *v = VideoData::Create(mVideoInfo,
|
||||
mImageContainer,
|
||||
mLastStreamOffset,
|
||||
pts,
|
||||
duration,
|
||||
b,
|
||||
false,
|
||||
-1,
|
||||
mPictureRegion);
|
||||
if (twoDBuffer) {
|
||||
twoDBuffer->Unlock2D();
|
||||
} else {
|
||||
buffer->Unlock();
|
||||
}
|
||||
|
||||
*aOutVideoData = v;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFVideoDecoder::CreateD3DVideoFrame(IMFSample* aSample,
|
||||
VideoData** aOutVideoData)
|
||||
{
|
||||
NS_ENSURE_TRUE(aSample, E_POINTER);
|
||||
NS_ENSURE_TRUE(aOutVideoData, E_POINTER);
|
||||
NS_ENSURE_TRUE(mDXVA2Manager, E_ABORT);
|
||||
NS_ENSURE_TRUE(mUseHwAccel, E_ABORT);
|
||||
|
||||
*aOutVideoData = nullptr;
|
||||
HRESULT hr;
|
||||
|
||||
nsRefPtr<Image> image;
|
||||
hr = mDXVA2Manager->CopyToImage(aSample,
|
||||
mPictureRegion,
|
||||
mImageContainer,
|
||||
getter_AddRefs(image));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
NS_ENSURE_TRUE(image, E_FAIL);
|
||||
|
||||
Microseconds pts = GetSampleTime(aSample);
|
||||
Microseconds duration = GetSampleDuration(aSample);
|
||||
VideoData *v = VideoData::CreateFromImage(mVideoInfo,
|
||||
mImageContainer,
|
||||
mLastStreamOffset,
|
||||
pts,
|
||||
duration,
|
||||
image.forget(),
|
||||
false,
|
||||
-1,
|
||||
mPictureRegion);
|
||||
|
||||
NS_ENSURE_TRUE(v, E_FAIL);
|
||||
*aOutVideoData = v;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Blocks until decoded sample is produced by the deoder.
|
||||
DecoderStatus
|
||||
WMFVideoDecoder::Output(nsAutoPtr<MediaData>& aOutData)
|
||||
{
|
||||
RefPtr<IMFSample> sample;
|
||||
HRESULT hr;
|
||||
|
||||
// Loop until we decode a sample, or an unexpected error that we can't
|
||||
// handle occurs.
|
||||
while (true) {
|
||||
hr = mDecoder->Output(&sample);
|
||||
if (SUCCEEDED(hr)) {
|
||||
NS_ENSURE_TRUE(sample, DECODE_STATUS_ERROR);
|
||||
break;
|
||||
}
|
||||
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
|
||||
return DECODE_STATUS_NEED_MORE_INPUT;
|
||||
}
|
||||
if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
|
||||
// Video stream output type change. Probably a geometric apperature
|
||||
// change. Reconfigure the video geometry, so that we output the
|
||||
// correct size frames.
|
||||
MOZ_ASSERT(!sample);
|
||||
hr = ConfigureVideoFrameGeometry();
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
|
||||
// Loop back and try decoding again...
|
||||
continue;
|
||||
}
|
||||
// Else unexpected error, assert, and bail.
|
||||
NS_WARNING("WMFVideoDecoder::Output() unexpected error");
|
||||
return DECODE_STATUS_ERROR;
|
||||
}
|
||||
|
||||
VideoData* frame = nullptr;
|
||||
if (mUseHwAccel) {
|
||||
hr = CreateD3DVideoFrame(sample, &frame);
|
||||
} else {
|
||||
hr = CreateBasicVideoFrame(sample, &frame);
|
||||
}
|
||||
// Frame should be non null only when we succeeded.
|
||||
MOZ_ASSERT((frame != nullptr) == SUCCEEDED(hr));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr) && frame, DECODE_STATUS_ERROR);
|
||||
|
||||
aOutData = frame;
|
||||
|
||||
return DECODE_STATUS_OK;
|
||||
}
|
||||
|
||||
DecoderStatus
|
||||
WMFVideoDecoder::Flush()
|
||||
{
|
||||
NS_ENSURE_TRUE(mDecoder, DECODE_STATUS_ERROR);
|
||||
HRESULT hr = mDecoder->Flush();
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
|
||||
return DECODE_STATUS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
79
content/media/fmp4/wmf/WMFVideoDecoder.h
Normal file
79
content/media/fmp4/wmf/WMFVideoDecoder.h
Normal file
@ -0,0 +1,79 @@
|
||||
/* -*- 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(WMFVideoDecoder_h_)
|
||||
#define WMFVideoDecoder_h_
|
||||
|
||||
#include "wmf.h"
|
||||
#include "MP4Reader.h"
|
||||
#include "MFTDecoder.h"
|
||||
#include "nsRect.h"
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class DXVA2Manager;
|
||||
|
||||
class WMFVideoDecoder : public MediaDataDecoder {
|
||||
public:
|
||||
WMFVideoDecoder(bool aDXVAEnabled);
|
||||
~WMFVideoDecoder();
|
||||
|
||||
// Decode thread.
|
||||
nsresult Init(mozilla::layers::LayersBackend aLayersBackend,
|
||||
mozilla::layers::ImageContainer* aImageContainer);
|
||||
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
// Inserts data into the decoder's pipeline.
|
||||
virtual DecoderStatus Input(const uint8_t* aData,
|
||||
uint32_t aLength,
|
||||
Microseconds aDTS,
|
||||
Microseconds aPTS,
|
||||
int64_t aOffsetInStream) MOZ_OVERRIDE;
|
||||
|
||||
// Blocks until a decoded sample is produced by the decoder.
|
||||
virtual DecoderStatus Output(nsAutoPtr<MediaData>& aOutData) MOZ_OVERRIDE;
|
||||
|
||||
virtual DecoderStatus Flush() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
bool InitializeDXVA(mozilla::layers::LayersBackend aLayersBackend);
|
||||
|
||||
HRESULT ConfigureVideoFrameGeometry();
|
||||
|
||||
HRESULT CreateBasicVideoFrame(IMFSample* aSample,
|
||||
VideoData** aOutVideoData);
|
||||
|
||||
HRESULT CreateD3DVideoFrame(IMFSample* aSample,
|
||||
VideoData** aOutVideoData);
|
||||
// Video frame geometry.
|
||||
VideoInfo mVideoInfo;
|
||||
uint32_t mVideoStride;
|
||||
uint32_t mVideoWidth;
|
||||
uint32_t mVideoHeight;
|
||||
nsIntRect mPictureRegion;
|
||||
|
||||
// The last offset into the media resource that was passed into Input().
|
||||
// This is used to approximate the decoder's position in the media resource.
|
||||
int64_t mLastStreamOffset;
|
||||
|
||||
nsAutoPtr<MFTDecoder> mDecoder;
|
||||
RefPtr<layers::ImageContainer> mImageContainer;
|
||||
nsAutoPtr<DXVA2Manager> mDXVA2Manager;
|
||||
|
||||
const bool mDXVAEnabled;
|
||||
const bool mIsRunningOnVista;
|
||||
bool mUseHwAccel;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -385,9 +385,12 @@ WMFReader::GetSupportedAudioCodecs(const GUID** aCodecs, uint32_t* aNumCodecs)
|
||||
MOZ_ASSERT(aNumCodecs);
|
||||
|
||||
if (mIsMP3Enabled) {
|
||||
GUID aacOrMp3 = MFMPEG4Format_Base;
|
||||
aacOrMp3.Data1 = 0x6D703461;// FOURCC('m','p','4','a');
|
||||
static const GUID codecs[] = {
|
||||
MFAudioFormat_AAC,
|
||||
MFAudioFormat_MP3
|
||||
MFAudioFormat_MP3,
|
||||
aacOrMp3
|
||||
};
|
||||
*aCodecs = codecs;
|
||||
*aNumCodecs = NS_ARRAY_LENGTH(codecs);
|
||||
|
@ -93,4 +93,9 @@ GetSampleDuration(IMFSample* aSample);
|
||||
int64_t
|
||||
GetSampleTime(IMFSample* aSample);
|
||||
|
||||
inline bool
|
||||
IsFlagSet(DWORD flags, DWORD pattern) {
|
||||
return (flags & pattern) == pattern;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -5,6 +5,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS += [
|
||||
'DXVA2Manager.h',
|
||||
'WMF.h',
|
||||
'WMFDecoder.h',
|
||||
'WMFReader.h',
|
||||
|
Loading…
Reference in New Issue
Block a user