Bug 941302 - Part 5: Add Gonk Decoder Module. r=cpearce

From 6832d7208fc72eb8ff6782c87f5831ab3ff5a9bb Mon Sep 17 00:00:00 2001
This commit is contained in:
Blake Wu 2014-08-07 17:46:11 +08:00
Родитель b4b38b1643
Коммит b66d0b3c5d
9 изменённых файлов: 1234 добавлений и 0 удалений

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

@ -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',]
]