зеркало из https://github.com/mozilla/gecko-dev.git
Bug 941302 - Part 5: Add Gonk Decoder Module. r=cpearce
From 6832d7208fc72eb8ff6782c87f5831ab3ff5a9bb Mon Sep 17 00:00:00 2001
This commit is contained in:
Родитель
b4b38b1643
Коммит
b66d0b3c5d
|
@ -0,0 +1,201 @@
|
|||
/* -*- 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 "MediaCodecProxy.h"
|
||||
#include <OMX_IVCommon.h>
|
||||
#include <gui/Surface.h>
|
||||
#include <ICrypto.h>
|
||||
#include "GonkAudioDecoderManager.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "nsTArray.h"
|
||||
#include "prlog.h"
|
||||
#include "stagefright/MediaBuffer.h"
|
||||
#include "stagefright/MetaData.h"
|
||||
#include "stagefright/MediaErrors.h"
|
||||
#include <stagefright/foundation/AMessage.h>
|
||||
#include <stagefright/foundation/ALooper.h>
|
||||
#include "media/openmax/OMX_Audio.h"
|
||||
|
||||
#define LOG_TAG "GonkAudioDecoderManager"
|
||||
#include <android/log.h>
|
||||
#define ALOG(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo* GetDemuxerLog();
|
||||
#define LOG(...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#else
|
||||
#define LOG(...)
|
||||
#endif
|
||||
#define READ_OUTPUT_BUFFER_TIMEOUT_US 3000
|
||||
|
||||
using namespace android;
|
||||
typedef android::MediaCodecProxy MediaCodecProxy;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
GonkAudioDecoderManager::GonkAudioDecoderManager(
|
||||
const mp4_demuxer::AudioDecoderConfig& aConfig)
|
||||
: mAudioChannels(aConfig.channel_count)
|
||||
, mAudioRate(aConfig.samples_per_second)
|
||||
, mAudioProfile(aConfig.aac_profile)
|
||||
, mAudioBuffer(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(GonkAudioDecoderManager);
|
||||
MOZ_ASSERT(mAudioChannels);
|
||||
mUserData.AppendElements(&aConfig.audio_specific_config[0],
|
||||
aConfig.audio_specific_config.length());
|
||||
}
|
||||
|
||||
GonkAudioDecoderManager::~GonkAudioDecoderManager()
|
||||
{
|
||||
MOZ_COUNT_DTOR(GonkAudioDecoderManager);
|
||||
}
|
||||
|
||||
android::sp<MediaCodecProxy>
|
||||
GonkAudioDecoderManager::Init(MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
if (mLooper != nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
// Create ALooper
|
||||
mLooper = new ALooper;
|
||||
mLooper->setName("GonkAudioDecoderManager");
|
||||
mLooper->start();
|
||||
|
||||
mDecoder = MediaCodecProxy::CreateByType(mLooper, "audio/mp4a-latm", false, false, nullptr);
|
||||
if (!mDecoder.get()) {
|
||||
return nullptr;
|
||||
}
|
||||
sp<AMessage> format = new AMessage;
|
||||
// Fixed values
|
||||
ALOG("Init Audio channel no:%d, sample-rate:%d", mAudioChannels, mAudioRate);
|
||||
format->setString("mime", "audio/mp4a-latm");
|
||||
format->setInt32("channel-count", mAudioChannels);
|
||||
format->setInt32("sample-rate", mAudioRate);
|
||||
format->setInt32("aac-profile", mAudioProfile);
|
||||
format->setInt32("is-adts", true);
|
||||
status_t err = mDecoder->configure(format, nullptr, nullptr, 0);
|
||||
if (err != OK || !mDecoder->Prepare()) {
|
||||
return nullptr;
|
||||
}
|
||||
status_t rv = mDecoder->Input(mUserData.Elements(), mUserData.Length(), 0,
|
||||
android::MediaCodec::BUFFER_FLAG_CODECCONFIG);
|
||||
|
||||
if (rv == OK) {
|
||||
return mDecoder;
|
||||
} else {
|
||||
ALOG("Failed to input codec specific data!");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkAudioDecoderManager::CreateAudioData(int64_t aStreamOffset, AudioData **v) {
|
||||
|
||||
void *data;
|
||||
size_t dataOffset;
|
||||
size_t size;
|
||||
int64_t timeUs;
|
||||
|
||||
if (!mAudioBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
data = mAudioBuffer->data();
|
||||
dataOffset = mAudioBuffer->range_offset();
|
||||
size = mAudioBuffer->range_length();
|
||||
|
||||
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[size/2] );
|
||||
memcpy(buffer.get(), data+dataOffset, size);
|
||||
uint32_t frames = size / (2 * mAudioChannels);
|
||||
|
||||
CheckedInt64 duration = FramesToUsecs(frames, mAudioRate);
|
||||
if (!duration.isValid()) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
*v = new AudioData(aStreamOffset, timeUs, duration.value(), frames, buffer.forget(),
|
||||
mAudioChannels);
|
||||
ReleaseAudioBuffer();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkAudioDecoderManager::Output(int64_t aStreamOffset,
|
||||
nsAutoPtr<MediaData>& aOutData)
|
||||
{
|
||||
aOutData = nullptr;
|
||||
status_t err;
|
||||
err = mDecoder->Output(&mAudioBuffer, READ_OUTPUT_BUFFER_TIMEOUT_US);
|
||||
|
||||
switch (err) {
|
||||
case OK:
|
||||
{
|
||||
if (mAudioBuffer && mAudioBuffer->range_length() != 0) {
|
||||
int64_t timeUs;
|
||||
if (!mAudioBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
AudioData* data = nullptr;
|
||||
nsresult rv = CreateAudioData(aStreamOffset, &data);
|
||||
// Frame should be non null only when we succeeded.
|
||||
if (rv != NS_OK) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
aOutData = data;
|
||||
return NS_OK;
|
||||
}
|
||||
case android::INFO_FORMAT_CHANGED:
|
||||
case android::INFO_OUTPUT_BUFFERS_CHANGED:
|
||||
{
|
||||
// If the format changed, update our cached info.
|
||||
ALOG("Decoder format changed");
|
||||
return Output(aStreamOffset, aOutData);
|
||||
}
|
||||
case -EAGAIN:
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
case android::ERROR_END_OF_STREAM:
|
||||
{
|
||||
ALOG("End of Stream");
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
case -ETIMEDOUT:
|
||||
{
|
||||
ALOG("Timeout. can try again next time");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
default:
|
||||
{
|
||||
ALOG("Decoder failed, err=%d", err);
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void GonkAudioDecoderManager::ReleaseAudioBuffer() {
|
||||
if (mAudioBuffer) {
|
||||
sp<MetaData> metaData = mAudioBuffer->meta_data();
|
||||
int32_t index;
|
||||
metaData->findInt32(android::MediaCodecProxy::kKeyBufferIndex, &index);
|
||||
mAudioBuffer->release();
|
||||
mAudioBuffer = nullptr;
|
||||
mDecoder->releaseOutputBuffer(index);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkAudioDecoderManager::Input(mp4_demuxer::MP4Sample* aSample)
|
||||
{
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
|
||||
uint32_t length = aSample->size;
|
||||
status_t rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0);
|
||||
return rv == OK ? NS_OK : NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,56 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#if !defined(GonkAudioDecoderManager_h_)
|
||||
#define GonkAudioDecoderManager_h_
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "MP4Reader.h"
|
||||
#include "GonkMediaDataDecoder.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
namespace android {
|
||||
struct MOZ_EXPORT ALooper;
|
||||
class MOZ_EXPORT MediaBuffer;
|
||||
} // namespace android
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class GonkAudioDecoderManager : public GonkDecoderManager {
|
||||
typedef android::MediaCodecProxy MediaCodecProxy;
|
||||
public:
|
||||
GonkAudioDecoderManager(const mp4_demuxer::AudioDecoderConfig& aConfig);
|
||||
~GonkAudioDecoderManager();
|
||||
|
||||
virtual android::sp<MediaCodecProxy> Init(MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult Output(int64_t aStreamOffset,
|
||||
nsAutoPtr<MediaData>& aOutput) MOZ_OVERRIDE;
|
||||
private:
|
||||
|
||||
nsresult CreateAudioData(int64_t aStreamOffset,
|
||||
AudioData** aOutData);
|
||||
|
||||
void ReleaseAudioBuffer();
|
||||
// MediaCodedc's wrapper that performs the decoding.
|
||||
android::sp<MediaCodecProxy> mDecoder;
|
||||
|
||||
const uint32_t mAudioChannels;
|
||||
const uint32_t mAudioRate;
|
||||
const uint32_t mAudioProfile;
|
||||
nsTArray<uint8_t> mUserData;
|
||||
|
||||
MediaDataDecoderCallback* mReaderCallback;
|
||||
android::MediaBuffer* mAudioBuffer;
|
||||
android::sp<ALooper> mLooper;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GonkAudioDecoderManager_h_
|
|
@ -0,0 +1,61 @@
|
|||
/* -*- 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 "GonkDecoderModule.h"
|
||||
#include "GonkVideoDecoderManager.h"
|
||||
#include "GonkAudioDecoderManager.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "GonkMediaDataDecoder.h"
|
||||
|
||||
namespace mozilla {
|
||||
GonkDecoderModule::GonkDecoderModule()
|
||||
{
|
||||
}
|
||||
|
||||
GonkDecoderModule::~GonkDecoderModule()
|
||||
{
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
GonkDecoderModule::Init()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkDecoderModule::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
GonkDecoderModule::CreateH264Decoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
|
||||
mozilla::layers::LayersBackend aLayersBackend,
|
||||
mozilla::layers::ImageContainer* aImageContainer,
|
||||
MediaTaskQueue* aVideoTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
nsRefPtr<MediaDataDecoder> decoder =
|
||||
new GonkMediaDataDecoder(new GonkVideoDecoderManager(aImageContainer,aConfig),
|
||||
aVideoTaskQueue, aCallback);
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
GonkDecoderModule::CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
|
||||
MediaTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
nsRefPtr<MediaDataDecoder> decoder =
|
||||
new GonkMediaDataDecoder(new GonkAudioDecoderManager(aConfig), aAudioTaskQueue,
|
||||
aCallback);
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,41 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#if !defined(GonkPlatformDecoderModule_h_)
|
||||
#define GonkPlatformDecoderModule_h_
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class GonkDecoderModule : public PlatformDecoderModule {
|
||||
public:
|
||||
GonkDecoderModule();
|
||||
virtual ~GonkDecoderModule();
|
||||
|
||||
// Called when the decoders have shutdown.
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
// Decode thread.
|
||||
virtual already_AddRefed<MediaDataDecoder>
|
||||
CreateH264Decoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
|
||||
mozilla::layers::LayersBackend aLayersBackend,
|
||||
mozilla::layers::ImageContainer* aImageContainer,
|
||||
MediaTaskQueue* aVideoTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
|
||||
|
||||
// Decode thread.
|
||||
virtual already_AddRefed<MediaDataDecoder>
|
||||
CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
|
||||
MediaTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
|
||||
|
||||
static void Init();
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -0,0 +1,155 @@
|
|||
/* -*- 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"
|
||||
#define LOG_TAG "GonkMediaDataDecoder(blake)"
|
||||
#include <android/log.h>
|
||||
#define ALOG(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __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 {
|
||||
|
||||
GonkMediaDataDecoder::GonkMediaDataDecoder(GonkDecoderManager* aManager,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
: mTaskQueue(aTaskQueue)
|
||||
, mCallback(aCallback)
|
||||
, mManager(aManager)
|
||||
{
|
||||
MOZ_COUNT_CTOR(GonkMediaDataDecoder);
|
||||
}
|
||||
|
||||
GonkMediaDataDecoder::~GonkMediaDataDecoder()
|
||||
{
|
||||
MOZ_COUNT_DTOR(GonkMediaDataDecoder);
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkMediaDataDecoder::Init()
|
||||
{
|
||||
mDecoder = mManager->Init(mCallback);
|
||||
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");
|
||||
ALOG("Failed to input data err: %d",rv);
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
|
||||
mLastStreamOffset = aSample->byte_offset;
|
||||
ProcessOutput();
|
||||
}
|
||||
|
||||
void
|
||||
GonkMediaDataDecoder::ProcessOutput()
|
||||
{
|
||||
nsAutoPtr<MediaData> output;
|
||||
nsresult rv;
|
||||
while (true) {
|
||||
rv = mManager->Output(mLastStreamOffset, output);
|
||||
if (rv == NS_OK) {
|
||||
mCallback->Output(output.forget());
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
mCallback->InputExhausted();
|
||||
return;
|
||||
}
|
||||
if (rv != NS_OK) {
|
||||
NS_WARNING("GonkMediaDataDecoder failed to output data");
|
||||
ALOG("Failed to output data");
|
||||
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();
|
||||
|
||||
status_t err = mDecoder->flush();
|
||||
return err == OK ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
void
|
||||
GonkMediaDataDecoder::ProcessDrain()
|
||||
{
|
||||
// Then extract all available output.
|
||||
ProcessOutput();
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkMediaDataDecoder::Drain()
|
||||
{
|
||||
mTaskQueue->Dispatch(NS_NewRunnableMethod(this, &GonkMediaDataDecoder::ProcessDrain));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
GonkMediaDataDecoder::IsWaitingMediaResources() {
|
||||
return mDecoder->IsWaitingResources();
|
||||
}
|
||||
|
||||
bool
|
||||
GonkMediaDataDecoder::IsDormantNeeded() {
|
||||
return mDecoder->IsDormantNeeded();
|
||||
}
|
||||
|
||||
void
|
||||
GonkMediaDataDecoder::ReleaseMediaResources() {
|
||||
mDecoder->ReleaseMediaResources();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,95 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#if !defined(GonkMediaDataDecoder_h_)
|
||||
#define GonkMediaDataDecoder_h_
|
||||
#include "mp4_demuxer/mp4_demuxer.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "MP4Reader.h"
|
||||
|
||||
namespace android {
|
||||
class MediaCodecProxy;
|
||||
} // namespace android
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Manage the data flow from inputting encoded data and outputting decode data.
|
||||
class GonkDecoderManager {
|
||||
public:
|
||||
virtual ~GonkDecoderManager() {}
|
||||
|
||||
// Creates and initializs the GonkDecoder.
|
||||
// Returns nullptr on failure.
|
||||
virtual android::sp<android::MediaCodecProxy> Init(MediaDataDecoderCallback* aCallback) = 0;
|
||||
|
||||
// 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.
|
||||
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) = 0;
|
||||
virtual nsresult Output(int64_t aStreamOffset,
|
||||
nsAutoPtr<MediaData>& aOutput) = 0;
|
||||
|
||||
};
|
||||
|
||||
// Samples are decoded using the GonkDecoder (MediaCodec)
|
||||
// created by the GonkDecoderManager. This class implements
|
||||
// the higher-level logic that drives mapping the Gonk to the async
|
||||
// MediaDataDecoder interface. The specifics of decoding the exact stream
|
||||
// type are handled by GonkDecoderManager and the GonkDecoder it creates.
|
||||
class GonkMediaDataDecoder : public MediaDataDecoder {
|
||||
public:
|
||||
GonkMediaDataDecoder(GonkDecoderManager* aDecoderManager,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback);
|
||||
|
||||
~GonkMediaDataDecoder();
|
||||
|
||||
virtual nsresult Init() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample);
|
||||
|
||||
virtual nsresult Flush() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult Drain() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool IsWaitingMediaResources() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool IsDormantNeeded() MOZ_OVERRIDE;
|
||||
|
||||
virtual void ReleaseMediaResources() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
// Called on the task queue. Inserts the sample into the decoder, and
|
||||
// extracts output if available.
|
||||
void ProcessDecode(mp4_demuxer::MP4Sample* 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<MediaTaskQueue> mTaskQueue;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
|
||||
android::sp<android::MediaCodecProxy> mDecoder;
|
||||
nsAutoPtr<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;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GonkMediaDataDecoder_h_
|
|
@ -0,0 +1,459 @@
|
|||
/* -*- 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 "MediaCodecProxy.h"
|
||||
#include <OMX_IVCommon.h>
|
||||
#include <gui/Surface.h>
|
||||
#include <ICrypto.h>
|
||||
#include "GonkVideoDecoderManager.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "Layers.h"
|
||||
#include "prlog.h"
|
||||
#include "stagefright/MediaBuffer.h"
|
||||
#include "stagefright/MetaData.h"
|
||||
#include "stagefright/MediaErrors.h"
|
||||
#include <stagefright/foundation/ADebug.h>
|
||||
#include <stagefright/foundation/AMessage.h>
|
||||
#include <stagefright/foundation/AString.h>
|
||||
#include <stagefright/foundation/ALooper.h>
|
||||
#include "mp4_demuxer/AnnexB.h"
|
||||
|
||||
#define READ_OUTPUT_BUFFER_TIMEOUT_US 3000
|
||||
|
||||
#define LOG_TAG "GonkVideoDecoderManager"
|
||||
#include <android/log.h>
|
||||
#define ALOG(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo* GetDemuxerLog();
|
||||
#define LOG(...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#else
|
||||
#define LOG(...)
|
||||
#endif
|
||||
using namespace mozilla::layers;
|
||||
using namespace android;
|
||||
typedef android::MediaCodecProxy MediaCodecProxy;
|
||||
|
||||
namespace mozilla {
|
||||
enum {
|
||||
kNotifyCodecReserved = 'core',
|
||||
kNotifyCodecCanceled = 'coca',
|
||||
};
|
||||
|
||||
GonkVideoDecoderManager::GonkVideoDecoderManager(
|
||||
mozilla::layers::ImageContainer* aImageContainer,
|
||||
const mp4_demuxer::VideoDecoderConfig& aConfig)
|
||||
: mImageContainer(aImageContainer)
|
||||
, mConfig(aConfig)
|
||||
, mReaderCallback(nullptr)
|
||||
, mColorConverterBufferSize(0)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Should not be on main thread.");
|
||||
MOZ_ASSERT(mImageContainer);
|
||||
MOZ_COUNT_CTOR(GonkVideoDecoderManager);
|
||||
mVideoWidth = aConfig.display_width;
|
||||
mVideoHeight = aConfig.display_height;
|
||||
mDisplayWidth = aConfig.display_width;
|
||||
mDisplayHeight = aConfig.display_width;
|
||||
mInfo.mVideo.mHasVideo = true;
|
||||
nsIntSize displaySize(mDisplayWidth, mDisplayHeight);
|
||||
mInfo.mVideo.mDisplay = displaySize;
|
||||
|
||||
nsIntRect pictureRect(0, 0, mVideoWidth, mVideoHeight);
|
||||
nsIntSize frameSize(mVideoWidth, mVideoHeight);
|
||||
mPicture = pictureRect;
|
||||
mInitialFrame = frameSize;
|
||||
mHandler = new MessageHandler(this);
|
||||
mVideoListener = new VideoResourceListener(this);
|
||||
|
||||
}
|
||||
|
||||
GonkVideoDecoderManager::~GonkVideoDecoderManager()
|
||||
{
|
||||
MOZ_COUNT_DTOR(GonkVideoDecoderManager);
|
||||
}
|
||||
|
||||
android::sp<MediaCodecProxy>
|
||||
GonkVideoDecoderManager::Init(MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
nsIntSize displaySize(mDisplayWidth, mDisplayHeight);
|
||||
nsIntRect pictureRect(0, 0, mVideoWidth, mVideoHeight);
|
||||
// Validate the container-reported frame and pictureRect sizes. This ensures
|
||||
// that our video frame creation code doesn't overflow.
|
||||
nsIntSize frameSize(mVideoWidth, mVideoHeight);
|
||||
if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
|
||||
ALOG("It is not a valid region");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mReaderCallback = aCallback;
|
||||
|
||||
if (mLooper.get() != nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
// Create ALooper
|
||||
mLooper = new ALooper;
|
||||
mLooper->setName("GonkVideoDecoderManager");
|
||||
// Register AMessage handler to ALooper.
|
||||
mLooper->registerHandler(mHandler);
|
||||
// Start ALooper thread.
|
||||
if (mLooper->start() != OK) {
|
||||
return nullptr;
|
||||
}
|
||||
mDecoder = MediaCodecProxy::CreateByType(mLooper, "video/avc", false, true, mVideoListener);
|
||||
return mDecoder;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkVideoDecoderManager::CreateVideoData(int64_t aStreamOffset, VideoData **v)
|
||||
{
|
||||
*v = nullptr;
|
||||
int64_t timeUs;
|
||||
int32_t keyFrame;
|
||||
if (!mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
|
||||
ALOG("Decoder did not return frame time");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (!mVideoBuffer->meta_data()->findInt32(kKeyIsSyncFrame, &keyFrame)) {
|
||||
keyFrame = 0;
|
||||
}
|
||||
|
||||
gfx::IntRect picture = ToIntRect(mPicture);
|
||||
if (mFrameInfo.mWidth != mInitialFrame.width ||
|
||||
mFrameInfo.mHeight != mInitialFrame.height) {
|
||||
|
||||
// Frame size is different from what the container reports. This is legal,
|
||||
// and we will preserve the ratio of the crop rectangle as it
|
||||
// was reported relative to the picture size reported by the container.
|
||||
picture.x = (mPicture.x * mFrameInfo.mWidth) / mInitialFrame.width;
|
||||
picture.y = (mPicture.y * mFrameInfo.mHeight) / mInitialFrame.height;
|
||||
picture.width = (mFrameInfo.mWidth * mPicture.width) / mInitialFrame.width;
|
||||
picture.height = (mFrameInfo.mHeight * mPicture.height) / mInitialFrame.height;
|
||||
}
|
||||
|
||||
if (!(mVideoBuffer != nullptr && mVideoBuffer->size() > 0 && mVideoBuffer->data() != nullptr)) {
|
||||
ALOG("mVideoBuffer is not valid!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
uint8_t *yuv420p_buffer = (uint8_t *)mVideoBuffer->data();
|
||||
int32_t stride = mFrameInfo.mStride;
|
||||
int32_t slice_height = mFrameInfo.mSliceHeight;
|
||||
|
||||
// Converts to OMX_COLOR_FormatYUV420Planar
|
||||
if (mFrameInfo.mColorFormat != OMX_COLOR_FormatYUV420Planar) {
|
||||
ARect crop;
|
||||
crop.top = 0;
|
||||
crop.bottom = mFrameInfo.mHeight;
|
||||
crop.left = 0;
|
||||
crop.right = mFrameInfo.mWidth;
|
||||
yuv420p_buffer = GetColorConverterBuffer(mFrameInfo.mWidth, mFrameInfo.mHeight);
|
||||
if (mColorConverter.convertDecoderOutputToI420(mVideoBuffer->data(),
|
||||
mFrameInfo.mWidth, mFrameInfo.mHeight, crop, yuv420p_buffer) != OK) {
|
||||
ReleaseVideoBuffer();
|
||||
ALOG("Color conversion failed!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
stride = mFrameInfo.mWidth;
|
||||
slice_height = mFrameInfo.mHeight;
|
||||
}
|
||||
|
||||
size_t yuv420p_y_size = stride * slice_height;
|
||||
size_t yuv420p_u_size = ((stride + 1) / 2) * ((slice_height + 1) / 2);
|
||||
uint8_t *yuv420p_y = yuv420p_buffer;
|
||||
uint8_t *yuv420p_u = yuv420p_y + yuv420p_y_size;
|
||||
uint8_t *yuv420p_v = yuv420p_u + yuv420p_u_size;
|
||||
|
||||
// This is the approximate byte position in the stream.
|
||||
int64_t pos = aStreamOffset;
|
||||
|
||||
VideoData::YCbCrBuffer b;
|
||||
b.mPlanes[0].mData = yuv420p_y;
|
||||
b.mPlanes[0].mWidth = mFrameInfo.mWidth;
|
||||
b.mPlanes[0].mHeight = mFrameInfo.mHeight;
|
||||
b.mPlanes[0].mStride = stride;
|
||||
b.mPlanes[0].mOffset = 0;
|
||||
b.mPlanes[0].mSkip = 0;
|
||||
|
||||
b.mPlanes[1].mData = yuv420p_u;
|
||||
b.mPlanes[1].mWidth = (mFrameInfo.mWidth + 1) / 2;
|
||||
b.mPlanes[1].mHeight = (mFrameInfo.mHeight + 1) / 2;
|
||||
b.mPlanes[1].mStride = (stride + 1) / 2;
|
||||
b.mPlanes[1].mOffset = 0;
|
||||
b.mPlanes[1].mSkip = 0;
|
||||
|
||||
b.mPlanes[2].mData = yuv420p_v;
|
||||
b.mPlanes[2].mWidth =(mFrameInfo.mWidth + 1) / 2;
|
||||
b.mPlanes[2].mHeight = (mFrameInfo.mHeight + 1) / 2;
|
||||
b.mPlanes[2].mStride = (stride + 1) / 2;
|
||||
b.mPlanes[2].mOffset = 0;
|
||||
b.mPlanes[2].mSkip = 0;
|
||||
|
||||
*v = VideoData::Create(
|
||||
mInfo.mVideo,
|
||||
mImageContainer,
|
||||
pos,
|
||||
timeUs,
|
||||
1, // We don't know the duration.
|
||||
b,
|
||||
keyFrame,
|
||||
-1,
|
||||
picture);
|
||||
ReleaseVideoBuffer();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
GonkVideoDecoderManager::SetVideoFormat()
|
||||
{
|
||||
// read video metadata from MediaCodec
|
||||
sp<AMessage> codecFormat;
|
||||
if (mDecoder->getOutputFormat(&codecFormat) == OK) {
|
||||
AString mime;
|
||||
int32_t width = 0;
|
||||
int32_t height = 0;
|
||||
int32_t stride = 0;
|
||||
int32_t slice_height = 0;
|
||||
int32_t color_format = 0;
|
||||
int32_t crop_left = 0;
|
||||
int32_t crop_top = 0;
|
||||
int32_t crop_right = 0;
|
||||
int32_t crop_bottom = 0;
|
||||
if (!codecFormat->findString("mime", &mime) ||
|
||||
!codecFormat->findInt32("width", &width) ||
|
||||
!codecFormat->findInt32("height", &height) ||
|
||||
!codecFormat->findInt32("stride", &stride) ||
|
||||
!codecFormat->findInt32("slice-height", &slice_height) ||
|
||||
!codecFormat->findInt32("color-format", &color_format) ||
|
||||
!codecFormat->findRect("crop", &crop_left, &crop_top, &crop_right, &crop_bottom)) {
|
||||
ALOG("Failed to find values");
|
||||
return false;
|
||||
}
|
||||
mFrameInfo.mWidth = width;
|
||||
mFrameInfo.mHeight = height;
|
||||
mFrameInfo.mStride = stride;
|
||||
mFrameInfo.mSliceHeight = slice_height;
|
||||
mFrameInfo.mColorFormat = color_format;
|
||||
|
||||
nsIntSize displaySize(width, height);
|
||||
if (!IsValidVideoRegion(mInitialFrame, mPicture, displaySize)) {
|
||||
ALOG("It is not a valid region");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Blocks until decoded sample is produced by the deoder.
|
||||
nsresult
|
||||
GonkVideoDecoderManager::Output(int64_t aStreamOffset,
|
||||
nsAutoPtr<MediaData>& aOutData)
|
||||
{
|
||||
aOutData = nullptr;
|
||||
status_t err;
|
||||
if (mDecoder == nullptr) {
|
||||
ALOG("Decoder is not inited");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
err = mDecoder->Output(&mVideoBuffer, READ_OUTPUT_BUFFER_TIMEOUT_US);
|
||||
|
||||
switch (err) {
|
||||
case OK:
|
||||
{
|
||||
VideoData* data = nullptr;
|
||||
nsresult rv = CreateVideoData(aStreamOffset, &data);
|
||||
// Frame should be non null only when we succeeded.
|
||||
if (rv != NS_OK || data == nullptr){
|
||||
ALOG("Error unexpected in CreateVideoData");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
aOutData = data;
|
||||
return NS_OK;
|
||||
}
|
||||
case android::INFO_FORMAT_CHANGED:
|
||||
case android::INFO_OUTPUT_BUFFERS_CHANGED:
|
||||
{
|
||||
// If the format changed, update our cached info.
|
||||
ALOG("Decoder format changed");
|
||||
if (!SetVideoFormat()) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
else
|
||||
return Output(aStreamOffset, aOutData);
|
||||
}
|
||||
case -EAGAIN:
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
case android::ERROR_END_OF_STREAM:
|
||||
{
|
||||
ALOG("End of Stream");
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
case -ETIMEDOUT:
|
||||
{
|
||||
ALOG("Timeout. can try again next time");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
default:
|
||||
{
|
||||
ALOG("Decoder failed, err=%d", err);
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void GonkVideoDecoderManager::ReleaseVideoBuffer() {
|
||||
if (mVideoBuffer) {
|
||||
sp<MetaData> metaData = mVideoBuffer->meta_data();
|
||||
int32_t index;
|
||||
metaData->findInt32(android::MediaCodecProxy::kKeyBufferIndex, &index);
|
||||
mVideoBuffer->release();
|
||||
mVideoBuffer = nullptr;
|
||||
mDecoder->releaseOutputBuffer(index);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkVideoDecoderManager::Input(mp4_demuxer::MP4Sample* aSample)
|
||||
{
|
||||
// We must prepare samples in AVC Annex B.
|
||||
mp4_demuxer::AnnexB::ConvertSample(aSample, mConfig.annex_b);
|
||||
// Forward sample data to the decoder.
|
||||
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
|
||||
uint32_t length = aSample->size;
|
||||
if (mDecoder == nullptr) {
|
||||
ALOG("Decoder is not inited");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
status_t rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0);
|
||||
return (rv == OK) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
void
|
||||
GonkVideoDecoderManager::codecReserved()
|
||||
{
|
||||
sp<AMessage> format = new AMessage;
|
||||
// Fixed values
|
||||
format->setString("mime", "video/avc");
|
||||
format->setInt32("width", mVideoWidth);
|
||||
format->setInt32("height", mVideoHeight);
|
||||
|
||||
mDecoder->configure(format, nullptr, nullptr, 0);
|
||||
mDecoder->Prepare();
|
||||
SetVideoFormat();
|
||||
|
||||
if (mHandler != nullptr) {
|
||||
// post kNotifyCodecReserved to Looper thread.
|
||||
sp<AMessage> notify = new AMessage(kNotifyCodecReserved, mHandler->id());
|
||||
notify->post();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GonkVideoDecoderManager::codecCanceled()
|
||||
{
|
||||
mDecoder = nullptr;
|
||||
if (mHandler != nullptr) {
|
||||
// post kNotifyCodecCanceled to Looper thread.
|
||||
sp<AMessage> notify = new AMessage(kNotifyCodecCanceled, mHandler->id());
|
||||
notify->post();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Called on GonkVideoDecoderManager::mLooper thread.
|
||||
void
|
||||
GonkVideoDecoderManager::onMessageReceived(const sp<AMessage> &aMessage)
|
||||
{
|
||||
switch (aMessage->what()) {
|
||||
case kNotifyCodecReserved:
|
||||
{
|
||||
// Our decode may have acquired the hardware resource that it needs
|
||||
// to start. Notify the state machine to resume loading metadata.
|
||||
mReaderCallback->NotifyResourcesStatusChanged();
|
||||
break;
|
||||
}
|
||||
|
||||
case kNotifyCodecCanceled:
|
||||
{
|
||||
mReaderCallback->ReleaseMediaResources();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
TRESPASS();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GonkVideoDecoderManager::MessageHandler::MessageHandler(GonkVideoDecoderManager *aManager)
|
||||
: mManager(aManager)
|
||||
{
|
||||
}
|
||||
|
||||
GonkVideoDecoderManager::MessageHandler::~MessageHandler()
|
||||
{
|
||||
mManager = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
GonkVideoDecoderManager::MessageHandler::onMessageReceived(const android::sp<android::AMessage> &aMessage)
|
||||
{
|
||||
if (mManager != nullptr) {
|
||||
mManager->onMessageReceived(aMessage);
|
||||
}
|
||||
}
|
||||
|
||||
GonkVideoDecoderManager::VideoResourceListener::VideoResourceListener(GonkVideoDecoderManager *aManager)
|
||||
: mManager(aManager)
|
||||
{
|
||||
}
|
||||
|
||||
GonkVideoDecoderManager::VideoResourceListener::~VideoResourceListener()
|
||||
{
|
||||
mManager = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
GonkVideoDecoderManager::VideoResourceListener::codecReserved()
|
||||
{
|
||||
if (mManager != nullptr) {
|
||||
mManager->codecReserved();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GonkVideoDecoderManager::VideoResourceListener::codecCanceled()
|
||||
{
|
||||
if (mManager != nullptr) {
|
||||
mManager->codecCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
GonkVideoDecoderManager::GetColorConverterBuffer(int32_t aWidth, int32_t aHeight)
|
||||
{
|
||||
// Allocate a temporary YUV420Planer buffer.
|
||||
size_t yuv420p_y_size = aWidth * aHeight;
|
||||
size_t yuv420p_u_size = ((aWidth + 1) / 2) * ((aHeight + 1) / 2);
|
||||
size_t yuv420p_v_size = yuv420p_u_size;
|
||||
size_t yuv420p_size = yuv420p_y_size + yuv420p_u_size + yuv420p_v_size;
|
||||
if (mColorConverterBufferSize != yuv420p_size) {
|
||||
mColorConverterBuffer = nullptr; // release the previous buffer first
|
||||
mColorConverterBuffer = new uint8_t[yuv420p_size];
|
||||
mColorConverterBufferSize = yuv420p_size;
|
||||
}
|
||||
return mColorConverterBuffer.get();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,134 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#if !defined(GonkVideoDecoderManager_h_)
|
||||
#define GonkVideoDecoderManager_h_
|
||||
|
||||
#include "MP4Reader.h"
|
||||
#include "nsRect.h"
|
||||
#include "GonkMediaDataDecoder.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "I420ColorConverterHelper.h"
|
||||
#include "MediaCodecProxy.h"
|
||||
#include <stagefright/foundation/AHandler.h>
|
||||
|
||||
using namespace android;
|
||||
|
||||
namespace android {
|
||||
struct MOZ_EXPORT ALooper;
|
||||
class MOZ_EXPORT MediaBuffer;
|
||||
struct MOZ_EXPORT AString;
|
||||
} // namespace android
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class GonkVideoDecoderManager : public GonkDecoderManager {
|
||||
typedef android::MediaCodecProxy MediaCodecProxy;
|
||||
|
||||
public:
|
||||
GonkVideoDecoderManager(mozilla::layers::ImageContainer* aImageContainer,
|
||||
const mp4_demuxer::VideoDecoderConfig& aConfig);
|
||||
|
||||
~GonkVideoDecoderManager();
|
||||
|
||||
virtual android::sp<MediaCodecProxy> Init(MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult Output(int64_t aStreamOffset,
|
||||
nsAutoPtr<MediaData>& aOutput) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
struct FrameInfo
|
||||
{
|
||||
int32_t mWidth = 0;
|
||||
int32_t mHeight = 0;
|
||||
int32_t mStride = 0;
|
||||
int32_t mSliceHeight = 0;
|
||||
int32_t mColorFormat = 0;
|
||||
int32_t mCropLeft = 0;
|
||||
int32_t mCropTop = 0;
|
||||
int32_t mCropRight = 0;
|
||||
int32_t mCropBottom = 0;
|
||||
};
|
||||
class MessageHandler : public android::AHandler
|
||||
{
|
||||
public:
|
||||
MessageHandler(GonkVideoDecoderManager *aManager);
|
||||
~MessageHandler();
|
||||
|
||||
virtual void onMessageReceived(const android::sp<android::AMessage> &aMessage);
|
||||
|
||||
private:
|
||||
// Forbidden
|
||||
MessageHandler() MOZ_DELETE;
|
||||
MessageHandler(const MessageHandler &rhs) MOZ_DELETE;
|
||||
const MessageHandler &operator=(const MessageHandler &rhs) MOZ_DELETE;
|
||||
|
||||
GonkVideoDecoderManager *mManager;
|
||||
};
|
||||
friend class MessageHandler;
|
||||
|
||||
class VideoResourceListener : public android::MediaCodecProxy::CodecResourceListener
|
||||
{
|
||||
public:
|
||||
VideoResourceListener(GonkVideoDecoderManager *aManager);
|
||||
~VideoResourceListener();
|
||||
|
||||
virtual void codecReserved() MOZ_OVERRIDE;
|
||||
virtual void codecCanceled() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
// Forbidden
|
||||
VideoResourceListener() MOZ_DELETE;
|
||||
VideoResourceListener(const VideoResourceListener &rhs) MOZ_DELETE;
|
||||
const VideoResourceListener &operator=(const VideoResourceListener &rhs) MOZ_DELETE;
|
||||
|
||||
GonkVideoDecoderManager *mManager;
|
||||
};
|
||||
friend class VideoResourceListener;
|
||||
|
||||
bool SetVideoFormat();
|
||||
|
||||
nsresult CreateVideoData(int64_t aStreamOffset, VideoData** aOutData);
|
||||
void ReleaseVideoBuffer();
|
||||
uint8_t* GetColorConverterBuffer(int32_t aWidth, int32_t aHeight);
|
||||
|
||||
// For codec resource management
|
||||
void codecReserved();
|
||||
void codecCanceled();
|
||||
void onMessageReceived(const sp<AMessage> &aMessage);
|
||||
|
||||
const mp4_demuxer::VideoDecoderConfig& mConfig;
|
||||
uint32_t mVideoWidth;
|
||||
uint32_t mVideoHeight;
|
||||
uint32_t mDisplayWidth;
|
||||
uint32_t mDisplayHeight;
|
||||
nsIntRect mPicture;
|
||||
nsIntSize mInitialFrame;
|
||||
|
||||
android::sp<MediaCodecProxy> mDecoder;
|
||||
nsRefPtr<layers::ImageContainer> mImageContainer;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
|
||||
android::MediaBuffer* mVideoBuffer;
|
||||
|
||||
MediaDataDecoderCallback* mReaderCallback;
|
||||
MediaInfo mInfo;
|
||||
android::sp<VideoResourceListener> mVideoListener;
|
||||
android::sp<MessageHandler> mHandler;
|
||||
android::sp<ALooper> mLooper;
|
||||
FrameInfo mFrameInfo;
|
||||
|
||||
// color converter
|
||||
android::I420ColorConverterHelper mColorConverter;
|
||||
nsAutoArrayPtr<uint8_t> mColorConverterBuffer;
|
||||
size_t mColorConverterBufferSize;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GonkVideoDecoderManager_h_
|
|
@ -0,0 +1,32 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
EXPORTS += [
|
||||
'GonkAudioDecoderManager.h',
|
||||
'GonkDecoderModule.h',
|
||||
'GonkMediaDataDecoder.h',
|
||||
'GonkVideoDecoderManager.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'GonkAudioDecoderManager.cpp',
|
||||
'GonkDecoderModule.cpp',
|
||||
'GonkMediaDataDecoder.cpp',
|
||||
'GonkVideoDecoderManager.cpp',
|
||||
]
|
||||
LOCAL_INCLUDES += [
|
||||
'/content/media/omx/',
|
||||
'/content/media/omx/mediaresourcemanager',
|
||||
]
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
CXXFLAGS += [
|
||||
'-I%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
|
||||
'frameworks/native/opengl/include',]
|
||||
]
|
Загрузка…
Ссылка в новой задаче