Files
palemoon27/dom/media/directshow/DirectShowReader.cpp
T
roytam1 147c5fa2eb import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1150437 - Make gmp-clearkey consistently Apache2 licensed. r=edwin,jwwang,kentuckyfriedtakahe (3c954c73dd)
- Bug 1223486 - Ensure WrapTexCoord returns 0.0 instead of 1.0 when given a negative whole number. r=Bas (b4af8eb524)
- Bug 1160914 - Make gmp-clearkey's decoders threadsafe refcounted, to handle DecodingComplete while GMPVideoHost::CreateFrame() is waiting. r=edwin (03359c7730)
- Bug 1206763 - Enable SkiaGL canvas on gonk r=mattwoodrow (1588079b23)
- Decouple CairoImage from ImageContainer. (bug 1222910, r=roc) (a93a4dad82)
- Decouple D3D11ShareHandleImage from ImageContainer. (bug 1222910, r=mattwoodrow) (9642ef1c53)
- Bug 1194753 - Wait longer when decoding the first frame of D3D9 DXVA video before deciding it is invalid. r=cpearce (865fe1d2f3)
- Decouple D3D9SurfaceImage from ImageContainer. (bug 1222910, r=mattwoodrow) (55bc5abffc)
- Bug 1191534 - Implement BlitImageToFramebuffer for IOSurface. r=jgilbert (0da6aff77f)
- Decouple EGLImageImage from ImageContainer. (bug 1222910, r=snorp) (cffb7aadcb)
- Decouple SurfaceTextureImage from ImageContainer. (bug 1222910, r=snorp) (5b920014d0)
- Decouple MacIOSurfaceImage from ImageContainer. (bug 1222910, r=mstange) (ffab69155c)
- Decouple SharedRGBImage and PlanarYCbCrImage from ImageContainer. (bug 1222910, r=mattwoodrow) (a9f6e23788)
- Remove Gonk usage of ImageContainer::CreateImage. (bug 1222910, r=sotaro) (3f460b901d)
- Remove CreateImage() from ImageContainer, ImageFactory, and ImageClient. (bug 1222910, r=roc) (873d5d5e47)
- Bug 1212795: P1. Ensure the check if HW acceleration is allowed is performed on the main thread. r=jwwang (7cac257e5a)
- Bug 1137529: Prefer Apple's VDA hardware acceleration for Hi-Def videos. r=rillian (a3ffc48e5e)
- Bug 1212795: P2. Ensure all frameworks required for video decoding on mac are loaded. r=jwwang (b903b94c60)
- Bug 1212795: P3. Make AppleDecoderModule detects if the required modules are loaded. r=jwwang (fb132e36b0)
- Bug 1221991 - [1.3] Make SupportsMimeType a const function. r=jya (2450030808)
- Bug 1221991 - [2.3] Split DecoderLoop into functions, simplify decoding state and clean up style. r=snorp (7c0e665cf7)
- Bug 1221991 - [3.1] Manage module state via accessor functions. r=snorp (491b00f74b)
- Bug 1221991 - [4.2] Add AndroidDecoderModule logging. r=snorp (7213505016)
- Bug 1153110: Don't prefer VDA for 720p and above. r=rillian (50d5fbfa7c)
- remove PR_LOGGING (44f4efbaa2)
- Bug 1169653 - Limit use of the speech recognition API in JS to certified apps or apps with the proper flags set. r=smaug (c93a8949b8)
- Bug 1133633: Part2. Enable async decoding on mac. r=mattmoodrow (c9740168bd)
- Bug 1154896: Report decoding errors back to MP4Reader. r=rillian (26291f72e1)
- Bug 1154896: Part2. Ignore kVTDecodeInfo_FrameDropped flag. r=rillian (bfca4c264e)
- Bug 1198094: P1. Limit rate at which InputExhausted could be called by mac decoder. r=rillian (e5819c1666)
- Bug 1214678 - Allow Apple media decoders to work on iOS r=jya (c475eff0be)
- Bug 1219140 - Remove AskMediaCodecAndWait() r=bwu (dbe8d364ec)
- fix includes (432c3419b0)
- adapted Bug 1159509 - Support audio AMR-NB for Gonk in MP4Reader. r=jya (adaddc5c21)
- Bug 1154512 - Remove MediaTaskQueue::SyncDispatch() from PDM. r=cpearce (5bddd082bd)
- Bug 1204622 - release codec listener at reader task queue. r=jya (72b062b5d7)
- Bug 1097498 - Wait fence for the graphic buffer. r=sotaro (c72f301939)
- Bug 1198664 - Refactor: move common behaviors to base class. r=bwu,jya (a754813c5b)
- Bug 1198664 - Use looper to process decoder tasks. r=bwu (768315de13)
- Bug 1174721 - Use AudioCompactor for GonkAudioDecoderManager. r=sotaro (768e68c946)
- Bug 1199809 - Refactor: use RAII to help manage output buffer lifecycle. r=sotaro (9a6e0dceb7)
- Bug 1133955 - Record the last seek time to decide the direction to seek. r=sotaro (d0bcdbe68b)
- Bug 1210045 - Fix GonkVideoDecoderManager shutdown during initialization r=bwu (b379248ff1)
- Bug 1214997 - Use MozPromise in MediaCodecProxy and OMXCodecProxy r=bwu (ab9a92d640)
- Bug 1199809 - Make a copy of output buffer after flush(). r=sotaro (17465ecaea)
- Bug 1216895 - assert that decoder methods are run on correct thread. r=jya (b8c106c2ca)
- Bug 1185018 - Part 1 of 1 - Made speech recognition services language dependent and removed assumption of a single service. r=smaug (f83acf82b4)
- Bug 1207416: Add Telemetry for time spent in different Load states in WebRTC r=gcp (e3959b6760)
- Bug 1219480 - Replace PRLogModuleInfo with LazyLogModule in the media directory. r=rillian (a9b13ef334)
- Bug 1196558: Don't assert should output task not be dispatched. r=edwin (a2569bca73)
- Bug 1199193: Ensure DrainComplete() is called once all decoded frames have been output. r=rillian (760fb42753)
- Bug 1079621 - Change non-fatal errors to warnings. r=rillian (20c85e7e56)
- Bug 1185234 - Implement SpeechRecognition::interimResults. r=smaug (b3d635d8c0)
- Bug 1171850 - Remove 'models' dir from MODELSPS_DEST as the dir 'models' now comes from MODELSPS_FILES. r=gps (5515511e3a)
- Bug 1183503 - Rename model and dictionary files for recognition in preparation for localization. r=smaug (32c78610af)
- Bug 1185235 - Implement SpeechRecognition::maxAlternatives. r=smaug (91eac7f30f)
- Bug 1177514 - Remove final text of 'ERROR' on recognition error, should be signaled by SpeechRecognitionError. r=smaug (c8d7b198e0)
- Bug 1187791 - Part 1 of 1 - SpeechRecognition::maxAlternatives can't throw; so, rm webidl throws specifier. r=smaug (e295b2bde5)
- Bug 1213131: [vpx] Configure libvpx decoder to use multi-threads decoding. r=kentuckyfriedtakahe (f38ba0ef82)
- Bug 1206845: Prevent overflows in MediaCache. r=roc (e8a228dcd3)
- Bug 1205825 - part 1 - don't reacquire the media cache's monitor in MediaCacheStream::FlushPartialBlockInternal; r=roc (6342592be3)
- Bug 1205825 - part 2 - call Get*CachedData*Internal variants from MediaCacheStream::GetCachedRanges; r=roc (f229c8f757)
2023-01-10 13:45:00 +08:00

416 lines
13 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "DirectShowReader.h"
#include "MediaDecoderReader.h"
#include "mozilla/RefPtr.h"
#include "DirectShowUtils.h"
#include "AudioSinkFilter.h"
#include "SourceFilter.h"
#include "SampleSink.h"
#include "MediaResource.h"
#include "VideoUtils.h"
using namespace mozilla::media;
namespace mozilla {
LogModule*
GetDirectShowLog() {
static LazyLogModule log("DirectShowDecoder");
return log;
}
#define LOG(...) MOZ_LOG(GetDirectShowLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
DirectShowReader::DirectShowReader(AbstractMediaDecoder* aDecoder)
: MediaDecoderReader(aDecoder),
mMP3FrameParser(aDecoder->GetResource()->GetLength()),
#ifdef DEBUG
mRotRegister(0),
#endif
mNumChannels(0),
mAudioRate(0),
mBytesPerSample(0),
mDuration(0)
{
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
MOZ_COUNT_CTOR(DirectShowReader);
}
DirectShowReader::~DirectShowReader()
{
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
MOZ_COUNT_DTOR(DirectShowReader);
#ifdef DEBUG
if (mRotRegister) {
RemoveGraphFromRunningObjectTable(mRotRegister);
}
#endif
}
// Try to parse the MP3 stream to make sure this is indeed an MP3, get the
// estimated duration of the stream, and find the offset of the actual MP3
// frames in the stream, as DirectShow doesn't like large ID3 sections.
static nsresult
ParseMP3Headers(MP3FrameParser *aParser, MediaResource *aResource)
{
const uint32_t MAX_READ_SIZE = 4096;
uint64_t offset = 0;
while (aParser->NeedsData() && !aParser->ParsedHeaders()) {
uint32_t bytesRead;
char buffer[MAX_READ_SIZE];
nsresult rv = aResource->ReadAt(offset, buffer,
MAX_READ_SIZE, &bytesRead);
NS_ENSURE_SUCCESS(rv, rv);
if (!bytesRead) {
// End of stream.
return NS_ERROR_FAILURE;
}
aParser->Parse(reinterpret_cast<uint8_t*>(buffer), bytesRead, offset);
offset += bytesRead;
}
return aParser->IsMP3() ? NS_OK : NS_ERROR_FAILURE;
}
// Windows XP's MP3 decoder filter. This is available on XP only, on Vista
// and later we can use the DMO Wrapper filter and MP3 decoder DMO.
static const GUID CLSID_MPEG_LAYER_3_DECODER_FILTER =
{ 0x38BE3000, 0xDBF4, 0x11D0, {0x86, 0x0E, 0x00, 0xA0, 0x24, 0xCF, 0xEF, 0x6D} };
nsresult
DirectShowReader::ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags)
{
MOZ_ASSERT(OnTaskQueue());
HRESULT hr;
nsresult rv;
// Create the filter graph, reference it by the GraphBuilder interface,
// to make graph building more convenient.
hr = CoCreateInstance(CLSID_FilterGraph,
nullptr,
CLSCTX_INPROC_SERVER,
IID_IGraphBuilder,
reinterpret_cast<void**>(static_cast<IGraphBuilder**>(getter_AddRefs(mGraph))));
NS_ENSURE_TRUE(SUCCEEDED(hr) && mGraph, NS_ERROR_FAILURE);
rv = ParseMP3Headers(&mMP3FrameParser, mDecoder->GetResource());
NS_ENSURE_SUCCESS(rv, rv);
#ifdef DEBUG
// Add the graph to the Running Object Table so that we can connect
// to this graph with GraphEdit/GraphStudio. Note: on Vista and up you must
// also regsvr32 proppage.dll from the Windows SDK.
// See: http://msdn.microsoft.com/en-us/library/ms787252(VS.85).aspx
hr = AddGraphToRunningObjectTable(mGraph, &mRotRegister);
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
#endif
// Extract the interface pointers we'll need from the filter graph.
hr = mGraph->QueryInterface(static_cast<IMediaControl**>(getter_AddRefs(mControl)));
NS_ENSURE_TRUE(SUCCEEDED(hr) && mControl, NS_ERROR_FAILURE);
hr = mGraph->QueryInterface(static_cast<IMediaSeeking**>(getter_AddRefs(mMediaSeeking)));
NS_ENSURE_TRUE(SUCCEEDED(hr) && mMediaSeeking, NS_ERROR_FAILURE);
// Build the graph. Create the filters we need, and connect them. We
// build the entire graph ourselves to prevent other decoders installed
// on the system being created and used.
// Our source filters, wraps the MediaResource.
mSourceFilter = new SourceFilter(MEDIATYPE_Stream, MEDIASUBTYPE_MPEG1Audio);
NS_ENSURE_TRUE(mSourceFilter, NS_ERROR_FAILURE);
rv = mSourceFilter->Init(mDecoder->GetResource(), mMP3FrameParser.GetMP3Offset());
NS_ENSURE_SUCCESS(rv, rv);
hr = mGraph->AddFilter(mSourceFilter, L"MozillaDirectShowSource");
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
// The MPEG demuxer.
RefPtr<IBaseFilter> demuxer;
hr = CreateAndAddFilter(mGraph,
CLSID_MPEG1Splitter,
L"MPEG1Splitter",
getter_AddRefs(demuxer));
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
// Platform MP3 decoder.
RefPtr<IBaseFilter> decoder;
// Firstly try to create the MP3 decoder filter that ships with WinXP
// directly. This filter doesn't normally exist on later versions of
// Windows.
hr = CreateAndAddFilter(mGraph,
CLSID_MPEG_LAYER_3_DECODER_FILTER,
L"MPEG Layer 3 Decoder",
getter_AddRefs(decoder));
if (FAILED(hr)) {
// Failed to create MP3 decoder filter. Try to instantiate
// the MP3 decoder DMO.
hr = AddMP3DMOWrapperFilter(mGraph, getter_AddRefs(decoder));
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
}
// Sink, captures audio samples and inserts them into our pipeline.
static const wchar_t* AudioSinkFilterName = L"MozAudioSinkFilter";
mAudioSinkFilter = new AudioSinkFilter(AudioSinkFilterName, &hr);
NS_ENSURE_TRUE(mAudioSinkFilter && SUCCEEDED(hr), NS_ERROR_FAILURE);
hr = mGraph->AddFilter(mAudioSinkFilter, AudioSinkFilterName);
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
// Join the filters.
hr = ConnectFilters(mGraph, mSourceFilter, demuxer);
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
hr = ConnectFilters(mGraph, demuxer, decoder);
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
hr = ConnectFilters(mGraph, decoder, mAudioSinkFilter);
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
WAVEFORMATEX format;
mAudioSinkFilter->GetSampleSink()->GetAudioFormat(&format);
NS_ENSURE_TRUE(format.wFormatTag == WAVE_FORMAT_PCM, NS_ERROR_FAILURE);
mInfo.mAudio.mChannels = mNumChannels = format.nChannels;
mInfo.mAudio.mRate = mAudioRate = format.nSamplesPerSec;
mInfo.mAudio.mBitDepth = format.wBitsPerSample;
mBytesPerSample = format.wBitsPerSample / 8;
// Begin decoding!
hr = mControl->Run();
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
DWORD seekCaps = 0;
hr = mMediaSeeking->GetCapabilities(&seekCaps);
int64_t duration = mMP3FrameParser.GetDuration();
if (SUCCEEDED(hr)) {
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(duration));
}
LOG("Successfully initialized DirectShow MP3 decoder.");
LOG("Channels=%u Hz=%u duration=%lld bytesPerSample=%d",
mInfo.mAudio.mChannels,
mInfo.mAudio.mRate,
RefTimeToUsecs(duration),
mBytesPerSample);
*aInfo = mInfo;
// Note: The SourceFilter strips ID3v2 tags out of the stream.
*aTags = nullptr;
return NS_OK;
}
bool
DirectShowReader::IsMediaSeekable()
{
DWORD seekCaps = 0;
HRESULT hr = mMediaSeeking->GetCapabilities(&seekCaps);
return ((AM_SEEKING_CanSeekAbsolute & seekCaps) ==
AM_SEEKING_CanSeekAbsolute);
}
inline float
UnsignedByteToAudioSample(uint8_t aValue)
{
return aValue * (2.0f / UINT8_MAX) - 1.0f;
}
bool
DirectShowReader::Finish(HRESULT aStatus)
{
MOZ_ASSERT(OnTaskQueue());
LOG("DirectShowReader::Finish(0x%x)", aStatus);
// Notify the filter graph of end of stream.
RefPtr<IMediaEventSink> eventSink;
HRESULT hr = mGraph->QueryInterface(static_cast<IMediaEventSink**>(getter_AddRefs(eventSink)));
if (SUCCEEDED(hr) && eventSink) {
eventSink->Notify(EC_COMPLETE, aStatus, 0);
}
return false;
}
class DirectShowCopy
{
public:
DirectShowCopy(uint8_t *aSource, uint32_t aBytesPerSample,
uint32_t aSamples, uint32_t aChannels)
: mSource(aSource)
, mBytesPerSample(aBytesPerSample)
, mSamples(aSamples)
, mChannels(aChannels)
, mNextSample(0)
{ }
uint32_t operator()(AudioDataValue *aBuffer, uint32_t aSamples)
{
uint32_t maxSamples = std::min(aSamples, mSamples - mNextSample);
uint32_t frames = maxSamples / mChannels;
size_t byteOffset = mNextSample * mBytesPerSample;
if (mBytesPerSample == 1) {
for (uint32_t i = 0; i < maxSamples; ++i) {
uint8_t *sample = mSource + byteOffset;
aBuffer[i] = UnsignedByteToAudioSample(*sample);
byteOffset += mBytesPerSample;
}
} else if (mBytesPerSample == 2) {
for (uint32_t i = 0; i < maxSamples; ++i) {
int16_t *sample = reinterpret_cast<int16_t *>(mSource + byteOffset);
aBuffer[i] = AudioSampleToFloat(*sample);
byteOffset += mBytesPerSample;
}
}
mNextSample += maxSamples;
return frames;
}
private:
uint8_t * const mSource;
const uint32_t mBytesPerSample;
const uint32_t mSamples;
const uint32_t mChannels;
uint32_t mNextSample;
};
bool
DirectShowReader::DecodeAudioData()
{
MOZ_ASSERT(OnTaskQueue());
HRESULT hr;
SampleSink* sink = mAudioSinkFilter->GetSampleSink();
if (sink->AtEOS()) {
// End of stream.
return Finish(S_OK);
}
// Get the next chunk of audio samples. This blocks until the sample
// arrives, or an error occurs (like the stream is shutdown).
RefPtr<IMediaSample> sample;
hr = sink->Extract(sample);
if (FAILED(hr) || hr == S_FALSE) {
return Finish(hr);
}
int64_t start = 0, end = 0;
sample->GetMediaTime(&start, &end);
LOG("DirectShowReader::DecodeAudioData [%4.2lf-%4.2lf]",
RefTimeToSeconds(start),
RefTimeToSeconds(end));
LONG length = sample->GetActualDataLength();
LONG numSamples = length / mBytesPerSample;
LONG numFrames = length / mBytesPerSample / mNumChannels;
BYTE* data = nullptr;
hr = sample->GetPointer(&data);
NS_ENSURE_TRUE(SUCCEEDED(hr), Finish(hr));
mAudioCompactor.Push(mDecoder->GetResource()->Tell(),
RefTimeToUsecs(start),
mInfo.mAudio.mRate,
numFrames,
mNumChannels,
DirectShowCopy(reinterpret_cast<uint8_t *>(data),
mBytesPerSample,
numSamples,
mNumChannels));
return true;
}
bool
DirectShowReader::DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold)
{
MOZ_ASSERT(OnTaskQueue());
return false;
}
RefPtr<MediaDecoderReader::SeekPromise>
DirectShowReader::Seek(int64_t aTargetUs, int64_t aEndTime)
{
nsresult res = SeekInternal(aTargetUs);
if (NS_FAILED(res)) {
return SeekPromise::CreateAndReject(res, __func__);
} else {
return SeekPromise::CreateAndResolve(aTargetUs, __func__);
}
}
nsresult
DirectShowReader::SeekInternal(int64_t aTargetUs)
{
HRESULT hr;
MOZ_ASSERT(OnTaskQueue());
LOG("DirectShowReader::Seek() target=%lld", aTargetUs);
hr = mControl->Pause();
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
nsresult rv = ResetDecode();
NS_ENSURE_SUCCESS(rv, rv);
LONGLONG seekPosition = UsecsToRefTime(aTargetUs);
hr = mMediaSeeking->SetPositions(&seekPosition,
AM_SEEKING_AbsolutePositioning,
nullptr,
AM_SEEKING_NoPositioning);
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
hr = mControl->Run();
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
return NS_OK;
}
void
DirectShowReader::NotifyDataArrivedInternal()
{
MOZ_ASSERT(OnTaskQueue());
if (!mMP3FrameParser.NeedsData()) {
return;
}
AutoPinned<MediaResource> resource(mDecoder->GetResource());
nsTArray<MediaByteRange> byteRanges;
nsresult rv = resource->GetCachedRanges(byteRanges);
if (NS_FAILED(rv)) {
return;
}
IntervalSet<int64_t> intervals;
for (auto& range : byteRanges) {
intervals += mFilter.NotifyDataArrived(range.Length(), range.mStart);
}
for (const auto& interval : intervals) {
RefPtr<MediaByteBuffer> bytes =
resource->MediaReadAt(interval.mStart, interval.Length());
NS_ENSURE_TRUE_VOID(bytes);
mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart);
if (!mMP3FrameParser.IsMP3()) {
return;
}
}
int64_t duration = mMP3FrameParser.GetDuration();
if (duration != mDuration) {
MOZ_ASSERT(mDecoder);
mDuration = duration;
mDecoder->DispatchUpdateEstimatedMediaDuration(mDuration);
}
}
} // namespace mozilla