Bug 1199193: Ensure DrainComplete() is called once all decoded frames have been output. r=rillian a=ritu
--- a/dom/media/platforms/apple/AppleVDADecoder.cpp
+++ b/dom/media/platforms/apple/AppleVDADecoder.cpp
@@ -260,58 +260,53 @@ PlatformCallback(void* decompressionOutp
AppleVDADecoder::AppleFrameRef frameRef(
media::TimeUnit::FromMicroseconds(dts),
media::TimeUnit::FromMicroseconds(pts),
media::TimeUnit::FromMicroseconds(duration),
byte_offset,
is_sync_point == 1);
- // Forward the data back to an object method which can access
- // the correct reader's callback.
- nsCOMPtr<nsIRunnable> task =
- NS_NewRunnableMethodWithArgs<CFRefPtr<CVPixelBufferRef>, AppleVDADecoder::AppleFrameRef>(
- decoder, &AppleVDADecoder::OutputFrame, image, frameRef);
- decoder->DispatchOutputTask(task.forget());
+ decoder->OutputFrame(image, frameRef);
}
AppleVDADecoder::AppleFrameRef*
AppleVDADecoder::CreateAppleFrameRef(const MediaRawData* aSample)
{
MOZ_ASSERT(aSample);
return new AppleFrameRef(*aSample);
}
void
AppleVDADecoder::DrainReorderedFrames()
{
+ MonitorAutoLock mon(mMonitor);
while (!mReorderQueue.IsEmpty()) {
mCallback->Output(mReorderQueue.Pop());
}
mQueuedSamples = 0;
}
void
AppleVDADecoder::ClearReorderedFrames()
{
+ MonitorAutoLock mon(mMonitor);
while (!mReorderQueue.IsEmpty()) {
mReorderQueue.Pop();
}
mQueuedSamples = 0;
}
// Copy and return a decoded frame.
nsresult
-AppleVDADecoder::OutputFrame(CFRefPtr<CVPixelBufferRef> aImage,
+AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
AppleVDADecoder::AppleFrameRef aFrameRef)
{
- MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
-
- if (mIsFlushing) {
- // We are in the process of flushing; ignore frame.
+ if (mIsShutDown || mIsFlushing) {
+ // We are in the process of flushing or shutting down; 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(),
@@ -422,16 +417,17 @@ AppleVDADecoder::OutputFrame(CFRefPtr<CV
if (!data) {
NS_ERROR("Couldn't create VideoData for frame");
mCallback->Error();
return NS_ERROR_FAILURE;
}
// Frames come out in DTS order but we need to output them
// in composition order.
+ MonitorAutoLock mon(mMonitor);
mReorderQueue.Push(data);
while (mReorderQueue.Length() > mMaxRefFrames) {
mCallback->Output(mReorderQueue.Pop());
}
LOG("%llu decoded frames queued",
static_cast<unsigned long long>(mReorderQueue.Length()));
return NS_OK;
--- a/dom/media/platforms/apple/AppleVDADecoder.h
+++ b/dom/media/platforms/apple/AppleVDADecoder.h
@@ -76,26 +76,19 @@ public:
virtual nsresult Flush() override;
virtual nsresult Drain() override;
virtual nsresult Shutdown() override;
virtual bool IsHardwareAccelerated() const override
{
return true;
}
- void DispatchOutputTask(already_AddRefed<nsIRunnable> aTask)
- {
- nsCOMPtr<nsIRunnable> task = aTask;
- if (mIsShutDown || mIsFlushing) {
- return;
- }
- mTaskQueue->Dispatch(task.forget(), AbstractThread::DontAssertDispatchSuccess);
- }
-
- nsresult OutputFrame(CFRefPtr<CVPixelBufferRef> aImage,
+ // Access from the taskqueue and the decoder's thread.
+ // OutputFrame is thread-safe.
+ nsresult OutputFrame(CVPixelBufferRef aImage,
AppleFrameRef aFrameRef);
protected:
// Flush and Drain operation, always run
virtual void ProcessFlush();
virtual void ProcessDrain();
virtual void ProcessShutdown();
@@ -103,41 +96,43 @@ protected:
void DrainReorderedFrames();
void ClearReorderedFrames();
CFDictionaryRef CreateOutputConfiguration();
nsRefPtr<MediaByteBuffer> mExtraData;
nsRefPtr<FlushableTaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
nsRefPtr<layers::ImageContainer> mImageContainer;
- ReorderQueue mReorderQueue;
uint32_t mPictureWidth;
uint32_t mPictureHeight;
uint32_t mDisplayWidth;
uint32_t mDisplayHeight;
+ // Accessed on multiple threads, but only set in constructor.
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;
+ const bool mUseSoftwareImages;
+ const bool mIs106;
// Number of times a sample was queued via Input(). Will be decreased upon
// the decoder's callback being invoked.
// This is used to calculate how many frames has been buffered by the decoder.
- uint32_t mQueuedSamples;
+ Atomic<uint32_t> mQueuedSamples;
// For wait on mIsFlushing during Shutdown() process.
+ // Protects mReorderQueue.
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;
+ ReorderQueue mReorderQueue;
private:
VDADecoder mDecoder;
// Method to pass a frame to VideoToolbox for decoding.
nsresult SubmitFrame(MediaRawData* aSample);
// Method to set up the decompression session.
nsresult InitializeSession();
--- a/dom/media/platforms/apple/AppleVTDecoder.cpp
+++ b/dom/media/platforms/apple/AppleVTDecoder.cpp
@@ -164,20 +164,17 @@ PlatformCallback(void* decompressionOutp
NS_WARNING("VideoToolbox decoder returned no data");
image = nullptr;
} else if (flags & kVTDecodeInfo_FrameDropped) {
NS_WARNING(" ...frame tagged as dropped...");
} else {
MOZ_ASSERT(CFGetTypeID(image) == CVPixelBufferGetTypeID(),
"VideoToolbox returned an unexpected image type");
}
- nsCOMPtr<nsIRunnable> task =
- NS_NewRunnableMethodWithArgs<CFRefPtr<CVPixelBufferRef>, AppleVTDecoder::AppleFrameRef>(
- decoder, &AppleVTDecoder::OutputFrame, image, *frameRef);
- decoder->DispatchOutputTask(task.forget());
+ decoder->OutputFrame(image, *frameRef);
}
nsresult
AppleVTDecoder::WaitForAsynchronousFrames()
{
OSStatus rv = VTDecompressionSessionWaitForAsynchronousFrames(mSession);
if (rv != noErr) {
LOG("AppleVTDecoder: Error %d waiting for asynchronous frames", rv);