Bug 1198664 - Part 2 - Use looper to process decoder tasks. r=bwu

This commit is contained in:
John Lin 2015-10-06 02:20:00 +02:00
Родитель 3e0637f43f
Коммит 993994a080
6 изменённых файлов: 195 добавлений и 147 удалений

Просмотреть файл

@ -14,7 +14,6 @@
#include <android/log.h>
#define MCP_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "MediaCodecProxy", __VA_ARGS__)
#define TIMEOUT_DEQUEUE_INPUTBUFFER_MS 1000000ll
namespace android {
@ -571,7 +570,8 @@ bool MediaCodecProxy::UpdateOutputBuffers()
}
status_t MediaCodecProxy::Input(const uint8_t* aData, uint32_t aDataSize,
int64_t aTimestampUsecs, uint64_t aflags)
int64_t aTimestampUsecs, uint64_t aflags,
int64_t aTimeoutUs)
{
// Read Lock for mCodec
{
@ -583,9 +583,11 @@ status_t MediaCodecProxy::Input(const uint8_t* aData, uint32_t aDataSize,
}
size_t index;
status_t err = dequeueInputBuffer(&index, TIMEOUT_DEQUEUE_INPUTBUFFER_MS);
status_t err = dequeueInputBuffer(&index, aTimeoutUs);
if (err != OK) {
MCP_LOG("dequeueInputBuffer returned %d", err);
if (err != -EAGAIN) {
MCP_LOG("dequeueInputBuffer returned %d", err);
}
return err;
}

Просмотреть файл

@ -129,7 +129,7 @@ public:
// If aData is null, will notify decoder input EOS
status_t Input(const uint8_t* aData, uint32_t aDataSize,
int64_t aTimestampUsecs, uint64_t flags);
int64_t aTimestampUsecs, uint64_t flags, int64_t aTimeoutUs = 0);
status_t Output(MediaBuffer** aBuffer, int64_t aTimeoutUs);
bool Prepare();
void ReleaseMediaResources();

Просмотреть файл

@ -21,12 +21,14 @@
#include "MediaData.h"
#include "MediaInfo.h"
#define CODECCONFIG_TIMEOUT_US 10000LL
#define READ_OUTPUT_BUFFER_TIMEOUT_US 0LL
#include <android/log.h>
#define GADM_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkAudioDecoderManager", __VA_ARGS__)
extern PRLogModuleInfo* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
#define READ_OUTPUT_BUFFER_TIMEOUT_US 3000
using namespace android;
typedef android::MediaCodecProxy MediaCodecProxy;
@ -92,7 +94,8 @@ GonkAudioDecoderManager::InitMediaCodecProxy()
if (mMimeType.EqualsLiteral("audio/mp4a-latm")) {
rv = mDecoder->Input(mCodecSpecificData->Elements(), mCodecSpecificData->Length(), 0,
android::MediaCodec::BUFFER_FLAG_CODECCONFIG);
android::MediaCodec::BUFFER_FLAG_CODECCONFIG,
CODECCONFIG_TIMEOUT_US);
}
if (rv == OK) {

Просмотреть файл

@ -13,6 +13,8 @@
#include "mozilla/Logging.h"
#include <android/log.h>
#define GMDD_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkMediaDataDecoder", __VA_ARGS__)
#define INPUT_TIMEOUT_US 0LL // Don't wait for buffer if none is available.
#define MIN_QUEUED_SAMPLES 2
extern PRLogModuleInfo* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
@ -45,40 +47,53 @@ GonkDecoderManager::InitLoopers(MediaData::Type aType)
nsresult
GonkDecoderManager::Input(MediaRawData* aSample)
{
MutexAutoLock lock(mMutex);
nsRefPtr<MediaRawData> sample;
if (!aSample) {
if (aSample) {
sample = aSample;
} else {
// It means EOS with empty sample.
sample = new MediaRawData();
} else {
sample = aSample;
}
{
MutexAutoLock lock(mMutex);
mQueuedSamples.AppendElement(sample);
}
mQueuedSamples.AppendElement(sample);
sp<AMessage> input = new AMessage(kNotifyProcessInput, id());
if (!aSample) {
input->setInt32("input-eos", 1);
}
input->post();
return NS_OK;
}
int32_t
GonkDecoderManager::ProcessQueuedSample()
{
MutexAutoLock lock(mMutex);
status_t rv;
while (mQueuedSamples.Length()) {
nsRefPtr<MediaRawData> data = mQueuedSamples.ElementAt(0);
{
MutexAutoUnlock unlock(mMutex);
rv = mDecoder->Input(reinterpret_cast<const uint8_t*>(data->Data()),
data->Size(),
data->mTime,
0);
0,
INPUT_TIMEOUT_US);
}
if (rv == OK) {
mQueuedSamples.RemoveElementAt(0);
mWaitOutput.AppendElement(data->mOffset);
} else if (rv == -EAGAIN || rv == -ETIMEDOUT) {
// In most cases, EAGAIN or ETIMEOUT are safe because OMX can't fill
// buffer on time.
return NS_OK;
break;
} else {
return NS_ERROR_UNEXPECTED;
return rv;
}
}
return NS_OK;
return mQueuedSamples.Length();
}
nsresult
@ -95,10 +110,13 @@ GonkDecoderManager::Flush()
mLastTime = 0;
if (mDecoder->flush() != OK) {
return NS_ERROR_FAILURE;
MonitorAutoLock lock(mFlushMonitor);
mIsFlushing = true;
sp<AMessage> flush = new AMessage(kNotifyProcessFlush, id());
flush->post();
while (mIsFlushing) {
lock.Wait();
}
return NS_OK;
}
@ -123,10 +141,112 @@ GonkDecoderManager::HasQueuedSample()
return mQueuedSamples.Length();
}
void
GonkDecoderManager::ProcessInput(bool aEndOfStream)
{
status_t rv = ProcessQueuedSamples();
if (rv >= 0) {
if (!aEndOfStream && rv <= MIN_QUEUED_SAMPLES) {
mDecodeCallback->InputExhausted();
}
if (mToDo.get() == nullptr) {
mToDo = new AMessage(kNotifyDecoderActivity, id());
if (aEndOfStream) {
mToDo->setInt32("input-eos", 1);
}
mDecoder->requestActivityNotification(mToDo);
}
} else {
GMDD_LOG("input processed: error#%d", rv);
mDecodeCallback->Error();
}
}
void
GonkDecoderManager::ProcessFlush()
{
MonitorAutoLock lock(mFlushMonitor);
mWaitOutput.Clear();
if (mDecoder->flush() != OK) {
GMDD_LOG("flush error");
mDecodeCallback->Error();
}
mIsFlushing = false;
lock.NotifyAll();
}
void
GonkDecoderManager::ProcessToDo(bool aEndOfStream)
{
MOZ_ASSERT(mToDo.get() != nullptr);
mToDo.clear();
if (HasQueuedSample()) {
status_t pendingInput = ProcessQueuedSamples();
if (pendingInput < 0) {
mDecodeCallback->Error();
return;
}
if (!aEndOfStream && pendingInput <= MIN_QUEUED_SAMPLES) {
mDecodeCallback->InputExhausted();
}
}
nsresult rv = NS_OK;
while (mWaitOutput.Length() > 0) {
nsRefPtr<MediaData> output;
int64_t offset = mWaitOutput.ElementAt(0);
rv = Output(offset, output);
if (rv == NS_OK) {
mWaitOutput.RemoveElementAt(0);
mDecodeCallback->Output(output);
} else if (rv == NS_ERROR_ABORT) {
GMDD_LOG("eos output");
mWaitOutput.RemoveElementAt(0);
MOZ_ASSERT(mQueuedSamples.IsEmpty());
MOZ_ASSERT(mWaitOutput.IsEmpty());
// EOS
if (output) {
mDecodeCallback->Output(output);
}
mDecodeCallback->DrainComplete();
return;
} else if (rv == NS_ERROR_NOT_AVAILABLE) {
break;
} else {
mDecodeCallback->Error();
return;
}
}
if (HasQueuedSample() || mWaitOutput.Length() > 0) {
mToDo = new AMessage(kNotifyDecoderActivity, id());
mDecoder->requestActivityNotification(mToDo);
}
}
void
GonkDecoderManager::onMessageReceived(const sp<AMessage> &aMessage)
{
switch (aMessage->what()) {
case kNotifyProcessInput:
{
int32_t eos = 0;
ProcessInput(aMessage->findInt32("input-eos", &eos) && eos);
break;
}
case kNotifyProcessFlush:
{
ProcessFlush();
break;
}
case kNotifyDecoderActivity:
{
int32_t eos = 0;
ProcessToDo(aMessage->findInt32("input-eos", &eos) && eos);
break;
}
default:
{
TRESPASS();
@ -139,10 +259,7 @@ GonkMediaDataDecoder::GonkMediaDataDecoder(GonkDecoderManager* aManager,
FlushableTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback)
: mTaskQueue(aTaskQueue)
, mCallback(aCallback)
, mManager(aManager)
, mSignaledEOS(false)
, mDrainComplete(false)
{
MOZ_COUNT_CTOR(GonkMediaDataDecoder);
mManager->SetDecodeCallback(aCallback);
@ -156,8 +273,6 @@ GonkMediaDataDecoder::~GonkMediaDataDecoder()
nsRefPtr<MediaDataDecoder::InitPromise>
GonkMediaDataDecoder::Init()
{
mDrainComplete = false;
return mManager->Init();
}
@ -176,79 +291,10 @@ GonkMediaDataDecoder::Shutdown()
nsresult
GonkMediaDataDecoder::Input(MediaRawData* aSample)
{
nsCOMPtr<nsIRunnable> runnable(
NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
this,
&GonkMediaDataDecoder::ProcessDecode,
nsRefPtr<MediaRawData>(aSample)));
mTaskQueue->Dispatch(runnable.forget());
mManager->Input(aSample);
return NS_OK;
}
void
GonkMediaDataDecoder::ProcessDecode(MediaRawData* aSample)
{
nsresult rv = mManager->Input(aSample);
if (rv != NS_OK) {
NS_WARNING("GonkMediaDataDecoder failed to input data");
GMDD_LOG("Failed to input data err: %d",int(rv));
mCallback->Error();
return;
}
if (aSample) {
mLastStreamOffset = aSample->mOffset;
}
ProcessOutput();
}
void
GonkMediaDataDecoder::ProcessOutput()
{
nsRefPtr<MediaData> output;
nsresult rv = NS_ERROR_ABORT;
while (!mDrainComplete) {
// There are samples in queue, try to send them into decoder when EOS.
if (mSignaledEOS && mManager->HasQueuedSample()) {
GMDD_LOG("ProcessOutput: drain all input samples");
rv = mManager->Input(nullptr);
}
rv = mManager->Output(mLastStreamOffset, output);
if (rv == NS_OK) {
mCallback->Output(output);
continue;
} else if (rv == NS_ERROR_NOT_AVAILABLE && mSignaledEOS) {
// Try to get more frames before getting EOS frame
continue;
}
else {
break;
}
}
if (rv == NS_ERROR_NOT_AVAILABLE && !mSignaledEOS) {
mCallback->InputExhausted();
return;
}
if (rv != NS_OK) {
NS_WARNING("GonkMediaDataDecoder failed to output data");
GMDD_LOG("Failed to output data");
// GonkDecoderManangers report NS_ERROR_ABORT when EOS is reached.
if (rv == NS_ERROR_ABORT) {
if (output) {
mCallback->Output(output);
}
mCallback->DrainComplete();
MOZ_ASSERT_IF(mSignaledEOS, !mManager->HasQueuedSample());
mSignaledEOS = false;
mDrainComplete = true;
return;
}
GMDD_LOG("Callback error!");
mCallback->Error();
}
}
nsresult
GonkMediaDataDecoder::Flush()
{
@ -257,25 +303,13 @@ GonkMediaDataDecoder::Flush()
// it's executing at all. Note the MP4Reader ignores all output while
// flushing.
mTaskQueue->Flush();
mDrainComplete = false;
return mManager->Flush();
}
void
GonkMediaDataDecoder::ProcessDrain()
{
// Notify decoder input EOS by sending a null data.
ProcessDecode(nullptr);
mSignaledEOS = true;
ProcessOutput();
}
nsresult
GonkMediaDataDecoder::Drain()
{
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &GonkMediaDataDecoder::ProcessDrain);
mTaskQueue->Dispatch(runnable.forget());
mManager->Input(nullptr);
return NS_OK;
}

Просмотреть файл

@ -26,19 +26,12 @@ public:
virtual ~GonkDecoderManager() {}
// Add samples into OMX decoder or queue them if decoder is out of input buffer.
nsresult Input(MediaRawData* aSample);
// Produces decoded output, it blocks until output can be produced or a timeout
// is expired or until EOS. Returns NS_OK on success, or NS_ERROR_NOT_AVAILABLE
// if there's not enough data to produce more output. If this returns a failure
// code other than NS_ERROR_NOT_AVAILABLE, an error will be reported to the
// MP4Reader.
// The overrided class should follow the same behaviour.
virtual nsresult Output(int64_t aStreamOffset,
nsRefPtr<MediaData>& aOutput) = 0;
virtual nsRefPtr<InitPromise> Init() = 0;
// Asynchronously send sample into mDecoder. If out of input buffer, aSample
// will be queued for later re-send.
nsresult Input(MediaRawData* aSample);
// Flush the queued sample.
nsresult Flush();
@ -59,6 +52,8 @@ protected:
GonkDecoderManager()
: mMutex("GonkDecoderManager")
, mLastTime(0)
, mFlushMonitor("GonkDecoderManager::Flush")
, mIsFlushing(false)
, mDecodeCallback(nullptr)
{}
@ -66,6 +61,21 @@ protected:
void onMessageReceived(const android::sp<android::AMessage> &aMessage) override;
// Produces decoded output. It returns NS_OK on success, or NS_ERROR_NOT_AVAILABLE
// when output is not produced yet.
// If this returns a failure code other than NS_ERROR_NOT_AVAILABLE, an error
// will be reported through mDecodeCallback.
virtual nsresult Output(int64_t aStreamOffset,
nsRefPtr<MediaData>& aOutput) = 0;
// Send queued samples to OMX. It returns how many samples are still in
// queue after processing, or negtive error code if failed.
int32_t ProcessQueuedSamples();
void ProcessInput(bool aEndOfStream);
void ProcessFlush();
void ProcessToDo(bool aEndOfStream);
nsRefPtr<MediaByteBuffer> mCodecSpecificData;
nsAutoCString mMimeType;
@ -74,8 +84,18 @@ protected:
android::sp<android::MediaCodecProxy> mDecoder;
// Looper for mDecoder to run on.
android::sp<android::ALooper> mDecodeLooper;
// Looper to run decode tasks such as recycling output buffers.
// Looper to run decode tasks such as processing input, output, flush, and
// recycling output buffers.
android::sp<android::ALooper> mTaskLooper;
enum {
// Decoder will send this to indicate internal state change such as input or
// output buffers availability. Used to run pending input & output tasks.
kNotifyDecoderActivity = 'nda ',
// Signal the decoder to flush.
kNotifyProcessFlush = 'npf ',
// Used to process queued samples when there is new input.
kNotifyProcessInput = 'npi ',
};
MozPromiseHolder<InitPromise> mInitPromise;
@ -87,6 +107,17 @@ protected:
int64_t mLastTime; // The last decoded frame presentation time.
Monitor mFlushMonitor; // Waits for flushing to complete.
bool mIsFlushing;
// Remembers the notification that is currently waiting for the decoder event
// to avoid requesting more than one notification at the time, which is
// forbidden by mDecoder.
android::sp<android::AMessage> mToDo;
// Stores the offset of every output that needs to be read from mDecoder.
nsTArray<int64_t> mWaitOutput;
MediaDataDecoderCallback* mDecodeCallback; // Reports decoder output or error.
};
@ -114,33 +145,9 @@ public:
nsresult Shutdown() override;
private:
// Called on the task queue. Inserts the sample into the decoder, and
// extracts output if available, if aSample is null, it means there is
// no data from source, it will notify the decoder EOS and flush all the
// decoded frames.
void ProcessDecode(MediaRawData* aSample);
// Called on the task queue. Extracts output if available, and delivers
// it to the reader. Called after ProcessDecode() and ProcessDrain().
void ProcessOutput();
// Called on the task queue. Orders the Gonk to drain, and then extracts
// all available output.
void ProcessDrain();
RefPtr<FlushableTaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
android::sp<GonkDecoderManager> mManager;
// 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;
// Set it ture when there is no input data
bool mSignaledEOS;
// Set if there is no more output data from decoder
bool mDrainComplete;
};
} // namespace mozilla

Просмотреть файл

@ -24,7 +24,8 @@
#include "mozilla/layers/TextureClient.h"
#include <cutils/properties.h>
#define READ_OUTPUT_BUFFER_TIMEOUT_US 3000
#define CODECCONFIG_TIMEOUT_US 10000LL
#define READ_OUTPUT_BUFFER_TIMEOUT_US 0LL
#include <android/log.h>
#define GVDM_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkVideoDecoderManager", __VA_ARGS__)
@ -416,7 +417,8 @@ GonkVideoDecoderManager::codecReserved()
if (mMimeType.EqualsLiteral("video/mp4v-es")) {
rv = mDecoder->Input(mCodecSpecificData->Elements(),
mCodecSpecificData->Length(), 0,
android::MediaCodec::BUFFER_FLAG_CODECCONFIG);
android::MediaCodec::BUFFER_FLAG_CODECCONFIG,
CODECCONFIG_TIMEOUT_US);
}
if (rv != OK) {