gecko-dev/dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp

275 lines
6.7 KiB
C++

/* -*- 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 "mp4_demuxer/mp4_demuxer.h"
#include "GonkMediaDataDecoder.h"
#include "VideoUtils.h"
#include "nsTArray.h"
#include "MediaCodecProxy.h"
#include "prlog.h"
#include <android/log.h>
#define GMDD_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkMediaDataDecoder", __VA_ARGS__)
#ifdef PR_LOGGING
PRLogModuleInfo* GetDemuxerLog();
#define LOG(...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG, (__VA_ARGS__))
#else
#define LOG(...)
#endif
using namespace android;
namespace mozilla {
GonkDecoderManager::GonkDecoderManager(MediaTaskQueue* aTaskQueue)
: mTaskQueue(aTaskQueue)
{
}
nsresult
GonkDecoderManager::Input(mp4_demuxer::MP4Sample* aSample)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
// To maintain the order of the MP4Sample, it needs to send the queued samples
// to OMX first. And then the current input aSample.
// If it fails to input sample to OMX, it needs to add current into queue
// for next round.
uint32_t len = mQueueSample.Length();
status_t rv = OK;
for (uint32_t i = 0; i < len; i++) {
rv = SendSampleToOMX(mQueueSample.ElementAt(0));
if (rv != OK) {
break;
}
mQueueSample.RemoveElementAt(0);
}
// When EOS, aSample will be null and sends this empty MP4Sample to nofity
// OMX it reachs EOS.
nsAutoPtr<mp4_demuxer::MP4Sample> sample;
if (!aSample) {
sample = new mp4_demuxer::MP4Sample();
}
// If rv is OK, that means mQueueSample is empty, now try to queue current input
// aSample.
if (rv == OK) {
MOZ_ASSERT(!mQueueSample.Length());
mp4_demuxer::MP4Sample* tmp;
if (aSample) {
tmp = aSample;
if (!PerformFormatSpecificProcess(aSample)) {
return NS_ERROR_FAILURE;
}
} else {
tmp = sample;
}
rv = SendSampleToOMX(tmp);
if (rv == OK) {
return NS_OK;
}
}
// Current valid sample can't be sent into OMX, adding the clone one into queue
// for next round.
if (!sample) {
sample = aSample->Clone();
if (!sample) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
mQueueSample.AppendElement(sample);
// In most cases, EAGAIN or ETIMEOUT safe due to OMX can't process the
// filled buffer on time. It should be gone When requeuing sample next time.
if (rv == -EAGAIN || rv == -ETIMEDOUT) {
return NS_OK;
}
return NS_ERROR_UNEXPECTED;
}
nsresult
GonkDecoderManager::Flush()
{
class ClearQueueRunnable : public nsRunnable
{
public:
explicit ClearQueueRunnable(GonkDecoderManager* aManager)
: mManager(aManager) {}
NS_IMETHOD Run()
{
mManager->ClearQueuedSample();
return NS_OK;
}
GonkDecoderManager* mManager;
};
mTaskQueue->SyncDispatch(new ClearQueueRunnable(this));
return NS_OK;
}
GonkMediaDataDecoder::GonkMediaDataDecoder(GonkDecoderManager* aManager,
FlushableMediaTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback)
: mTaskQueue(aTaskQueue)
, mCallback(aCallback)
, mManager(aManager)
, mSignaledEOS(false)
, mDrainComplete(false)
{
MOZ_COUNT_CTOR(GonkMediaDataDecoder);
}
GonkMediaDataDecoder::~GonkMediaDataDecoder()
{
MOZ_COUNT_DTOR(GonkMediaDataDecoder);
}
nsresult
GonkMediaDataDecoder::Init()
{
mDecoder = mManager->Init(mCallback);
mDrainComplete = false;
return mDecoder.get() ? NS_OK : NS_ERROR_UNEXPECTED;
}
nsresult
GonkMediaDataDecoder::Shutdown()
{
mDecoder->stop();
mDecoder = nullptr;
return NS_OK;
}
// Inserts data into the decoder's pipeline.
nsresult
GonkMediaDataDecoder::Input(mp4_demuxer::MP4Sample* aSample)
{
mTaskQueue->Dispatch(
NS_NewRunnableMethodWithArg<nsAutoPtr<mp4_demuxer::MP4Sample>>(
this,
&GonkMediaDataDecoder::ProcessDecode,
nsAutoPtr<mp4_demuxer::MP4Sample>(aSample)));
return NS_OK;
}
void
GonkMediaDataDecoder::ProcessDecode(mp4_demuxer::MP4Sample* aSample)
{
nsresult rv = mManager->Input(aSample);
if (rv != NS_OK) {
NS_WARNING("GonkAudioDecoder failed to input data");
GMDD_LOG("Failed to input data err: %d",rv);
mCallback->Error();
return;
}
if (aSample) {
mLastStreamOffset = aSample->byte_offset;
}
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;
}
}
MOZ_ASSERT_IF(mSignaledEOS, !mManager->HasQueuedSample());
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();
mSignaledEOS = false;
mDrainComplete = true;
return;
}
GMDD_LOG("Callback error!");
mCallback->Error();
}
}
nsresult
GonkMediaDataDecoder::Flush()
{
// Flush the input task queue. This cancels all pending Decode() calls.
// Note this blocks until the task queue finishes its current job, if
// 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()
{
mTaskQueue->Dispatch(NS_NewRunnableMethod(this, &GonkMediaDataDecoder::ProcessDrain));
return NS_OK;
}
bool
GonkMediaDataDecoder::IsWaitingMediaResources() {
return mDecoder->IsWaitingResources();
}
void
GonkMediaDataDecoder::AllocateMediaResources()
{
mManager->AllocateMediaResources();
}
void
GonkMediaDataDecoder::ReleaseMediaResources()
{
mManager->ReleaseMediaResources();
}
} // namespace mozilla