mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-13 05:15:45 +00:00
Bug 1192675: P1. Ensure VDA/VT APIs are only ever accessed from the same thread. r=cpearce
This commit is contained in:
parent
3a39ae3f5f
commit
22bbf0d187
@ -43,6 +43,56 @@ private:
|
||||
T mRef;
|
||||
};
|
||||
|
||||
// CFRefPtr: A CoreFoundation smart pointer.
|
||||
template <class T>
|
||||
class CFRefPtr {
|
||||
public:
|
||||
explicit CFRefPtr(T aRef)
|
||||
: mRef(aRef)
|
||||
{
|
||||
if (mRef) {
|
||||
CFRetain(mRef);
|
||||
}
|
||||
}
|
||||
// Copy constructor.
|
||||
CFRefPtr(const CFRefPtr<T>& aCFRefPtr)
|
||||
: mRef(aCFRefPtr.mRef)
|
||||
{
|
||||
if (mRef) {
|
||||
CFRetain(mRef);
|
||||
}
|
||||
}
|
||||
// Copy operator
|
||||
CFRefPtr<T>& operator=(const CFRefPtr<T>& aCFRefPtr)
|
||||
{
|
||||
if (mRef == aCFRefPtr.mRef) {
|
||||
return;
|
||||
}
|
||||
if (mRef) {
|
||||
CFRelease(mRef);
|
||||
}
|
||||
mRef = aCFRefPtr.mRef;
|
||||
if (mRef) {
|
||||
CFRetain(mRef);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
~CFRefPtr()
|
||||
{
|
||||
if (mRef) {
|
||||
CFRelease(mRef);
|
||||
}
|
||||
}
|
||||
// Return the wrapped ref so it can be used as an in parameter.
|
||||
operator T()
|
||||
{
|
||||
return mRef;
|
||||
}
|
||||
|
||||
private:
|
||||
T mRef;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_AppleUtils_h
|
||||
|
@ -40,8 +40,12 @@ AppleVDADecoder::AppleVDADecoder(const VideoInfo& aConfig,
|
||||
, mPictureHeight(aConfig.mImage.height)
|
||||
, mDisplayWidth(aConfig.mDisplay.width)
|
||||
, mDisplayHeight(aConfig.mDisplay.height)
|
||||
, mInputIncoming(0)
|
||||
, mIsShutDown(false)
|
||||
, mUseSoftwareImages(false)
|
||||
, mIs106(!nsCocoaFeatures::OnLionOrLater())
|
||||
, mMonitor("AppleVideoDecoder")
|
||||
, mIsFlushing(false)
|
||||
, mDecoder(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(AppleVDADecoder);
|
||||
@ -81,18 +85,34 @@ AppleVDADecoder::Init()
|
||||
|
||||
nsresult
|
||||
AppleVDADecoder::Shutdown()
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mIsShutDown);
|
||||
mIsShutDown = true;
|
||||
if (mTaskQueue) {
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &AppleVDADecoder::ProcessShutdown);
|
||||
mTaskQueue->Dispatch(runnable.forget());
|
||||
} else {
|
||||
ProcessShutdown();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
AppleVDADecoder::ProcessShutdown()
|
||||
{
|
||||
if (mDecoder) {
|
||||
LOG("%s: cleaning up decoder %p", __func__, mDecoder);
|
||||
VDADecoderDestroy(mDecoder);
|
||||
mDecoder = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleVDADecoder::Input(MediaRawData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
|
||||
LOG("mp4 input sample %p pts %lld duration %lld us%s %d bytes",
|
||||
aSample,
|
||||
aSample->mTime,
|
||||
@ -100,6 +120,8 @@ AppleVDADecoder::Input(MediaRawData* aSample)
|
||||
aSample->mKeyframe ? " keyframe" : "",
|
||||
aSample->Size());
|
||||
|
||||
mInputIncoming++;
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
|
||||
this,
|
||||
@ -112,21 +134,51 @@ AppleVDADecoder::Input(MediaRawData* aSample)
|
||||
nsresult
|
||||
AppleVDADecoder::Flush()
|
||||
{
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
mIsFlushing = true;
|
||||
mTaskQueue->Flush();
|
||||
OSStatus rv = VDADecoderFlush(mDecoder, 0 /*dont emit*/);
|
||||
if (rv != noErr) {
|
||||
LOG("AppleVDADecoder::Flush failed waiting for platform decoder "
|
||||
"with error:%d.", rv);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &AppleVDADecoder::ProcessFlush);
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mTaskQueue->Dispatch(runnable.forget());
|
||||
while (mIsFlushing) {
|
||||
mon.Wait();
|
||||
}
|
||||
ClearReorderedFrames();
|
||||
|
||||
mInputIncoming = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleVDADecoder::Drain()
|
||||
{
|
||||
mTaskQueue->AwaitIdle();
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &AppleVDADecoder::ProcessDrain);
|
||||
mTaskQueue->Dispatch(runnable.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
AppleVDADecoder::ProcessFlush()
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
|
||||
OSStatus rv = VDADecoderFlush(mDecoder, 0 /*dont emit*/);
|
||||
if (rv != noErr) {
|
||||
LOG("AppleVDADecoder::Flush failed waiting for platform decoder "
|
||||
"with error:%d.", rv);
|
||||
}
|
||||
ClearReorderedFrames();
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mIsFlushing = false;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
void
|
||||
AppleVDADecoder::ProcessDrain()
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
|
||||
OSStatus rv = VDADecoderFlush(mDecoder, kVDADecoderFlush_EmitFrames);
|
||||
if (rv != noErr) {
|
||||
LOG("AppleVDADecoder::Drain failed waiting for platform decoder "
|
||||
@ -134,7 +186,6 @@ AppleVDADecoder::Drain()
|
||||
}
|
||||
DrainReorderedFrames();
|
||||
mCallback->DrainComplete();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
@ -199,17 +250,19 @@ PlatformCallback(void* decompressionOutputRefCon,
|
||||
CFNumberGetValue(boref, kCFNumberSInt64Type, &byte_offset);
|
||||
CFNumberGetValue(kfref, kCFNumberSInt8Type, &is_sync_point);
|
||||
|
||||
nsAutoPtr<AppleVDADecoder::AppleFrameRef> frameRef(
|
||||
new AppleVDADecoder::AppleFrameRef(
|
||||
AppleVDADecoder::AppleFrameRef frameRef(
|
||||
media::TimeUnit::FromMicroseconds(dts),
|
||||
media::TimeUnit::FromMicroseconds(pts),
|
||||
media::TimeUnit::FromMicroseconds(duration),
|
||||
byte_offset,
|
||||
is_sync_point == 1));
|
||||
is_sync_point == 1);
|
||||
|
||||
// Forward the data back to an object method which can access
|
||||
// the correct MP4Reader callback.
|
||||
decoder->OutputFrame(image, frameRef);
|
||||
// the correct reader's callback.
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethodWithArgs<CFRefPtr<CVPixelBufferRef>, AppleVDADecoder::AppleFrameRef>(
|
||||
decoder, &AppleVDADecoder::OutputFrame, image, frameRef);
|
||||
decoder->DispatchOutputTask(task.forget());
|
||||
}
|
||||
|
||||
AppleVDADecoder::AppleFrameRef*
|
||||
@ -237,15 +290,22 @@ AppleVDADecoder::ClearReorderedFrames()
|
||||
|
||||
// Copy and return a decoded frame.
|
||||
nsresult
|
||||
AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
|
||||
nsAutoPtr<AppleVDADecoder::AppleFrameRef> aFrameRef)
|
||||
AppleVDADecoder::OutputFrame(CFRefPtr<CVPixelBufferRef> aImage,
|
||||
AppleVDADecoder::AppleFrameRef aFrameRef)
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
|
||||
if (mIsFlushing) {
|
||||
// We are in the process of flushing; ignore frame.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
LOG("mp4 output frame %lld dts %lld pts %lld duration %lld us%s",
|
||||
aFrameRef->byte_offset,
|
||||
aFrameRef->decode_timestamp.ToMicroseconds(),
|
||||
aFrameRef->composition_timestamp.ToMicroseconds(),
|
||||
aFrameRef->duration.ToMicroseconds(),
|
||||
aFrameRef->is_sync_point ? " keyframe" : ""
|
||||
aFrameRef.byte_offset,
|
||||
aFrameRef.decode_timestamp.ToMicroseconds(),
|
||||
aFrameRef.composition_timestamp.ToMicroseconds(),
|
||||
aFrameRef.duration.ToMicroseconds(),
|
||||
aFrameRef.is_sync_point ? " keyframe" : ""
|
||||
);
|
||||
|
||||
// Where our resulting image will end up.
|
||||
@ -303,12 +363,12 @@ AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
|
||||
VideoData::Create(info,
|
||||
mImageContainer,
|
||||
nullptr,
|
||||
aFrameRef->byte_offset,
|
||||
aFrameRef->composition_timestamp.ToMicroseconds(),
|
||||
aFrameRef->duration.ToMicroseconds(),
|
||||
aFrameRef.byte_offset,
|
||||
aFrameRef.composition_timestamp.ToMicroseconds(),
|
||||
aFrameRef.duration.ToMicroseconds(),
|
||||
buffer,
|
||||
aFrameRef->is_sync_point,
|
||||
aFrameRef->decode_timestamp.ToMicroseconds(),
|
||||
aFrameRef.is_sync_point,
|
||||
aFrameRef.decode_timestamp.ToMicroseconds(),
|
||||
visible);
|
||||
// Unlock the returned image data.
|
||||
CVPixelBufferUnlockBaseAddress(aImage, kCVPixelBufferLock_ReadOnly);
|
||||
@ -327,12 +387,12 @@ AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
|
||||
data =
|
||||
VideoData::CreateFromImage(info,
|
||||
mImageContainer,
|
||||
aFrameRef->byte_offset,
|
||||
aFrameRef->composition_timestamp.ToMicroseconds(),
|
||||
aFrameRef->duration.ToMicroseconds(),
|
||||
aFrameRef.byte_offset,
|
||||
aFrameRef.composition_timestamp.ToMicroseconds(),
|
||||
aFrameRef.duration.ToMicroseconds(),
|
||||
image.forget(),
|
||||
aFrameRef->is_sync_point,
|
||||
aFrameRef->decode_timestamp.ToMicroseconds(),
|
||||
aFrameRef.is_sync_point,
|
||||
aFrameRef.decode_timestamp.ToMicroseconds(),
|
||||
visible);
|
||||
}
|
||||
|
||||
@ -357,6 +417,10 @@ AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
|
||||
nsresult
|
||||
AppleVDADecoder::SubmitFrame(MediaRawData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
|
||||
mInputIncoming--;
|
||||
|
||||
AutoCFRelease<CFDataRef> block =
|
||||
CFDataCreate(kCFAllocatorDefault, aSample->Data(), aSample->Size());
|
||||
if (!block) {
|
||||
@ -430,7 +494,7 @@ AppleVDADecoder::SubmitFrame(MediaRawData* aSample)
|
||||
}
|
||||
|
||||
// Ask for more data.
|
||||
if (mTaskQueue->IsEmpty()) {
|
||||
if (!mInputIncoming) {
|
||||
LOG("AppleVDADecoder task queue empty; requesting more data");
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define mozilla_AppleVDADecoder_h
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "MP4Decoder.h"
|
||||
#include "nsIThread.h"
|
||||
@ -80,18 +81,28 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult OutputFrame(CVPixelBufferRef aImage,
|
||||
nsAutoPtr<AppleFrameRef> aFrameRef);
|
||||
void DispatchOutputTask(already_AddRefed<nsIRunnable> aTask)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> task = aTask;
|
||||
if (mIsShutDown || mIsFlushing) {
|
||||
return;
|
||||
}
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
}
|
||||
|
||||
// Method to set up the decompression session.
|
||||
nsresult InitializeSession();
|
||||
nsresult OutputFrame(CFRefPtr<CVPixelBufferRef> aImage,
|
||||
AppleFrameRef aFrameRef);
|
||||
|
||||
protected:
|
||||
// Flush and Drain operation, always run
|
||||
virtual void ProcessFlush();
|
||||
virtual void ProcessDrain();
|
||||
virtual void ProcessShutdown();
|
||||
|
||||
protected:
|
||||
AppleFrameRef* CreateAppleFrameRef(const MediaRawData* aSample);
|
||||
void DrainReorderedFrames();
|
||||
void ClearReorderedFrames();
|
||||
CFDictionaryRef CreateOutputConfiguration();
|
||||
nsresult InitDecoder();
|
||||
|
||||
nsRefPtr<MediaByteBuffer> mExtraData;
|
||||
nsRefPtr<FlushableTaskQueue> mTaskQueue;
|
||||
@ -103,12 +114,27 @@ public:
|
||||
uint32_t mDisplayWidth;
|
||||
uint32_t mDisplayHeight;
|
||||
uint32_t mMaxRefFrames;
|
||||
// Increased when Input is called, and decreased when ProcessFrame runs.
|
||||
// Reaching 0 indicates that there's no pending Input.
|
||||
Atomic<uint32_t> mInputIncoming;
|
||||
Atomic<bool> mIsShutDown;
|
||||
|
||||
bool mUseSoftwareImages;
|
||||
bool mIs106;
|
||||
|
||||
// For wait on mIsFlushing during Shutdown() process.
|
||||
Monitor mMonitor;
|
||||
// Set on reader/decode thread calling Flush() to indicate that output is
|
||||
// not required and so input samples on mTaskQueue need not be processed.
|
||||
// Cleared on mTaskQueue in ProcessDrain().
|
||||
Atomic<bool> mIsFlushing;
|
||||
|
||||
private:
|
||||
VDADecoder mDecoder;
|
||||
|
||||
// Method to set up the decompression session.
|
||||
nsresult InitializeSession();
|
||||
|
||||
// Method to pass a frame to VideoToolbox for decoding.
|
||||
nsresult SubmitFrame(MediaRawData* aSample);
|
||||
CFDictionaryRef CreateDecoderSpecification();
|
||||
|
@ -63,8 +63,8 @@ AppleVTDecoder::Init()
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleVTDecoder::Shutdown()
|
||||
void
|
||||
AppleVTDecoder::ProcessShutdown()
|
||||
{
|
||||
if (mSession) {
|
||||
LOG("%s: cleaning up session %p", __func__, mSession);
|
||||
@ -77,12 +77,13 @@ AppleVTDecoder::Shutdown()
|
||||
CFRelease(mFormat);
|
||||
mFormat = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleVTDecoder::Input(MediaRawData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
|
||||
LOG("mp4 input sample %p pts %lld duration %lld us%s %d bytes",
|
||||
aSample,
|
||||
aSample->mTime,
|
||||
@ -102,33 +103,34 @@ AppleVTDecoder::Input(MediaRawData* aSample)
|
||||
LOG(" sha1 %s", digest.get());
|
||||
#endif // LOG_MEDIA_SHA1
|
||||
|
||||
mInputIncoming++;
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
|
||||
this,
|
||||
&AppleVTDecoder::SubmitFrame,
|
||||
nsRefPtr<MediaRawData>(aSample));
|
||||
this, &AppleVTDecoder::SubmitFrame, aSample);
|
||||
mTaskQueue->Dispatch(runnable.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleVTDecoder::Flush()
|
||||
void
|
||||
AppleVTDecoder::ProcessFlush()
|
||||
{
|
||||
mTaskQueue->Flush();
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
nsresult rv = WaitForAsynchronousFrames();
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("AppleVTDecoder::Flush failed waiting for platform decoder "
|
||||
"with error:%d.", rv);
|
||||
}
|
||||
ClearReorderedFrames();
|
||||
|
||||
return rv;
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mIsFlushing = false;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleVTDecoder::Drain()
|
||||
void
|
||||
AppleVTDecoder::ProcessDrain()
|
||||
{
|
||||
mTaskQueue->AwaitIdle();
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
nsresult rv = WaitForAsynchronousFrames();
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("AppleVTDecoder::Drain failed waiting for platform decoder "
|
||||
@ -136,7 +138,6 @@ AppleVTDecoder::Drain()
|
||||
}
|
||||
DrainReorderedFrames();
|
||||
mCallback->DrainComplete();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
@ -174,9 +175,10 @@ PlatformCallback(void* decompressionOutputRefCon,
|
||||
MOZ_ASSERT(CFGetTypeID(image) == CVPixelBufferGetTypeID(),
|
||||
"VideoToolbox returned an unexpected image type");
|
||||
|
||||
// Forward the data back to an object method which can access
|
||||
// the correct MP4Reader callback.
|
||||
decoder->OutputFrame(image, frameRef);
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethodWithArgs<CFRefPtr<CVPixelBufferRef>, AppleVTDecoder::AppleFrameRef>(
|
||||
decoder, &AppleVTDecoder::OutputFrame, image, *frameRef);
|
||||
decoder->DispatchOutputTask(task.forget());
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -208,6 +210,8 @@ TimingInfoFromSample(MediaRawData* aSample)
|
||||
nsresult
|
||||
AppleVTDecoder::SubmitFrame(MediaRawData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
mInputIncoming--;
|
||||
// For some reason this gives me a double-free error with stagefright.
|
||||
AutoCFRelease<CMBlockBufferRef> block = nullptr;
|
||||
AutoCFRelease<CMSampleBufferRef> sample = nullptr;
|
||||
@ -253,7 +257,7 @@ AppleVTDecoder::SubmitFrame(MediaRawData* aSample)
|
||||
}
|
||||
|
||||
// Ask for more data.
|
||||
if (mTaskQueue->IsEmpty()) {
|
||||
if (!mInputIncoming) {
|
||||
LOG("AppleVTDecoder task queue empty; requesting more data");
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
|
@ -22,14 +22,16 @@ public:
|
||||
virtual ~AppleVTDecoder();
|
||||
virtual nsRefPtr<InitPromise> Init() override;
|
||||
virtual nsresult Input(MediaRawData* aSample) override;
|
||||
virtual nsresult Flush() override;
|
||||
virtual nsresult Drain() override;
|
||||
virtual nsresult Shutdown() override;
|
||||
virtual bool IsHardwareAccelerated() const override
|
||||
{
|
||||
return mIsHardwareAccelerated;
|
||||
}
|
||||
|
||||
protected:
|
||||
void ProcessFlush() override;
|
||||
void ProcessDrain() override;
|
||||
void ProcessShutdown() override;
|
||||
|
||||
private:
|
||||
CMVideoFormatDescriptionRef mFormat;
|
||||
VTDecompressionSessionRef mSession;
|
||||
|
Loading…
Reference in New Issue
Block a user