Bug 871485 - Share hw codec between applications/tasks. r=mwu, r=doublec, r=roc

This commit is contained in:
Sotaro Ikeda 2013-06-10 08:22:05 -04:00
parent 235fd18774
commit 357c151486
30 changed files with 1564 additions and 155 deletions

View File

@ -3244,6 +3244,11 @@ void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendE
void HTMLMediaElement::NotifyOwnerDocumentActivityChanged()
{
nsIDocument* ownerDoc = OwnerDoc();
if (mDecoder) {
mDecoder->SetDormantIfNecessary(ownerDoc->Hidden());
}
// SetVisibilityState will update mMuted with MUTED_BY_AUDIO_CHANNEL via the
// CanPlayChanged callback.
if (UseAudioChannelService() && mPlayingThroughTheAudioChannel &&

View File

@ -112,11 +112,47 @@ public:
NS_IMPL_THREADSAFE_ISUPPORTS1(MediaDecoder, nsIObserver)
void MediaDecoder::SetDormantIfNecessary(bool aDormant)
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (!mDecoderStateMachine || !mDecoderStateMachine->IsDormantNeeded() || (mPlayState == PLAY_STATE_SHUTDOWN)) {
return;
}
if (mIsDormant == aDormant) {
// no change to dormant state
return;
}
if(aDormant) {
// enter dormant state
StopProgress();
DestroyDecodedStream();
mDecoderStateMachine->SetDormant(true);
mRequestedSeekTime = mCurrentTime;
if (mPlayState == PLAY_STATE_PLAYING){
mNextState = PLAY_STATE_PLAYING;
} else {
mNextState = PLAY_STATE_PAUSED;
}
mNextState = mPlayState;
mIsDormant = aDormant;
ChangeState(PLAY_STATE_LOADING);
} else if ((aDormant != true) && (mPlayState == PLAY_STATE_LOADING)) {
// exit dormant state
// just trigger to state machine.
mDecoderStateMachine->SetDormant(false);
}
}
void MediaDecoder::Pause()
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (mPlayState == PLAY_STATE_SEEKING || mPlayState == PLAY_STATE_ENDED) {
if ((mPlayState == PLAY_STATE_LOADING && mIsDormant) || mPlayState == PLAY_STATE_SEEKING || mPlayState == PLAY_STATE_ENDED) {
mNextState = PLAY_STATE_PAUSED;
return;
}
@ -333,6 +369,7 @@ MediaDecoder::MediaDecoder() :
mTransportSeekable(true),
mMediaSeekable(true),
mReentrantMonitor("media.decoder"),
mIsDormant(false),
mPlayState(PLAY_STATE_PAUSED),
mNextState(PLAY_STATE_PAUSED),
mCalledResourceLoaded(false),
@ -519,7 +556,7 @@ nsresult MediaDecoder::Play()
NS_ASSERTION(mDecoderStateMachine != nullptr, "Should have state machine.");
nsresult res = ScheduleStateMachineThread();
NS_ENSURE_SUCCESS(res,res);
if (mPlayState == PLAY_STATE_SEEKING) {
if ((mPlayState == PLAY_STATE_LOADING && mIsDormant) || mPlayState == PLAY_STATE_SEEKING) {
mNextState = PLAY_STATE_PLAYING;
return NS_OK;
}
@ -619,7 +656,7 @@ nsresult MediaDecoder::Seek(double aTime)
// If we are already in the seeking state, then setting mRequestedSeekTime
// above will result in the new seek occurring when the current seek
// completes.
if (mPlayState != PLAY_STATE_SEEKING) {
if ((mPlayState != PLAY_STATE_LOADING || !mIsDormant) && mPlayState != PLAY_STATE_SEEKING) {
bool paused = false;
if (mOwner) {
paused = mOwner->GetPaused();
@ -1160,6 +1197,11 @@ void MediaDecoder::ChangeState(PlayState aState)
break;
}
}
if (aState!= PLAY_STATE_LOADING) {
mIsDormant = false;
}
GetReentrantMonitor().NotifyAll();
}

View File

@ -332,6 +332,12 @@ public:
// called.
virtual nsresult Play();
// Set/Unset dormant state if necessary.
// Dormant state is a state to free all scarce media resources
// (like hw video codec), did not decoding and stay dormant.
// It is used to share scarece media resources in system.
virtual void SetDormantIfNecessary(bool aDormant);
// Pause video playback.
virtual void Pause();
// Adjust the speed of the playback, optionally with pitch correction,
@ -1000,6 +1006,10 @@ public:
// without holding the monitor.
nsAutoPtr<DecodedStreamData> mDecodedStream;
// True if this decoder is in dormant state.
// Should be true only when PlayState is PLAY_STATE_LOADING.
bool mIsDormant;
// Set to one of the valid play states.
// This can only be changed on the main thread while holding the decoder
// monitor. Thus, it can be safely read while holding the decoder monitor

View File

@ -407,6 +407,13 @@ public:
// on failure.
virtual nsresult Init(MediaDecoderReader* aCloneDonor) = 0;
// True if this reader is waiting media resource allocation
virtual bool IsWaitingMediaResources() { return false; }
// True when this reader need to become dormant state
virtual bool IsDormantNeeded() { return false; }
// Release media resources they should be released in dormant state
virtual void ReleaseMediaResources() {};
// Resets all state related to decoding, emptying all buffers etc.
virtual nsresult ResetDecode();

View File

@ -503,12 +503,28 @@ void MediaDecoderStateMachine::DecodeThreadRun()
while (mState != DECODER_STATE_SHUTDOWN &&
mState != DECODER_STATE_COMPLETED &&
mState != DECODER_STATE_DORMANT &&
!mStopDecodeThread)
{
if (mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING) {
DecodeLoop();
} else if (mState == DECODER_STATE_SEEKING) {
DecodeSeek();
} else if (mState == DECODER_STATE_DECODING_METADATA) {
if (NS_FAILED(DecodeMetadata())) {
NS_ASSERTION(mState == DECODER_STATE_SHUTDOWN,
"Should be in shutdown state if metadata loading fails.");
LOG(PR_LOG_DEBUG, ("Decode metadata failed, shutting down decode thread"));
}
} else if (mState == DECODER_STATE_WAIT_FOR_RESOURCES) {
mDecoder->GetReentrantMonitor().Wait();
if (!mReader->IsWaitingMediaResources()) {
// change state to DECODER_STATE_WAIT_FOR_RESOURCES
StartDecodeMetadata();
}
} else if (mState == DECODER_STATE_DORMANT) {
mDecoder->GetReentrantMonitor().Wait();
}
}
@ -953,6 +969,7 @@ void MediaDecoderStateMachine::DecodeLoop()
if (!mStopDecodeThread &&
mState != DECODER_STATE_SHUTDOWN &&
mState != DECODER_STATE_DORMANT &&
mState != DECODER_STATE_SEEKING)
{
mState = DECODER_STATE_COMPLETED;
@ -1457,6 +1474,33 @@ void MediaDecoderStateMachine::SetMediaSeekable(bool aMediaSeekable)
mMediaSeekable = aMediaSeekable;
}
bool MediaDecoderStateMachine::IsDormantNeeded()
{
return mReader->IsDormantNeeded();
}
void MediaDecoderStateMachine::SetDormant(bool aDormant)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
if (!mReader) {
return;
}
if (aDormant) {
ScheduleStateMachine();
mState = DECODER_STATE_DORMANT;
mDecoder->GetReentrantMonitor().NotifyAll();
} else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) {
ScheduleStateMachine();
mStartTime = 0;
mCurrentFrameTime = 0;
mState = DECODER_STATE_DECODING_METADATA;
mDecoder->GetReentrantMonitor().NotifyAll();
}
}
void MediaDecoderStateMachine::Shutdown()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
@ -1484,6 +1528,22 @@ void MediaDecoderStateMachine::StartDecoding()
ScheduleStateMachine();
}
void MediaDecoderStateMachine::StartWaitForResources()
{
NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
"Should be on state machine or decode thread.");
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mState = DECODER_STATE_WAIT_FOR_RESOURCES;
}
void MediaDecoderStateMachine::StartDecodeMetadata()
{
NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
"Should be on state machine or decode thread.");
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mState = DECODER_STATE_DECODING_METADATA;
}
void MediaDecoderStateMachine::Play()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
@ -1815,6 +1875,12 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
res = mReader->ReadMetadata(&info, &tags);
}
if (NS_SUCCEEDED(res) && (mState == DECODER_STATE_DECODING_METADATA) && (mReader->IsWaitingMediaResources())) {
// change state to DECODER_STATE_WAIT_FOR_RESOURCES
StartWaitForResources();
return NS_OK;
}
mInfo = info;
if (NS_FAILED(res) || (!info.mHasVideo && !info.mHasAudio)) {
@ -2081,6 +2147,10 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
// Now that those threads are stopped, there's no possibility of
// mPendingWakeDecoder being needed again. Revoke it.
mPendingWakeDecoder = nullptr;
{
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
mReader->ReleaseMediaResources();
}
NS_ASSERTION(mState == DECODER_STATE_SHUTDOWN,
"How did we escape from the shutdown state?");
// We must daisy-chain these events to destroy the decoder. We must
@ -2100,6 +2170,26 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
return NS_OK;
}
case DECODER_STATE_DORMANT: {
if (IsPlaying()) {
StopPlayback();
}
StopAudioThread();
StopDecodeThread();
// Now that those threads are stopped, there's no possibility of
// mPendingWakeDecoder being needed again. Revoke it.
mPendingWakeDecoder = nullptr;
{
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
mReader->ReleaseMediaResources();
}
return NS_OK;
}
case DECODER_STATE_WAIT_FOR_RESOURCES: {
return NS_OK;
}
case DECODER_STATE_DECODING_METADATA: {
// Ensure we have a decode thread to decode metadata.
return ScheduleDecodeThread();

View File

@ -116,6 +116,8 @@ public:
// Enumeration for the valid decoding states
enum State {
DECODER_STATE_DECODING_METADATA,
DECODER_STATE_WAIT_FOR_RESOURCES,
DECODER_STATE_DORMANT,
DECODER_STATE_DECODING,
DECODER_STATE_SEEKING,
DECODER_STATE_BUFFERING,
@ -132,6 +134,11 @@ public:
// calling this.
void SetVolume(double aVolume);
void SetAudioCaptured(bool aCapture);
// Check if the decoder needs to become dormant state.
bool IsDormantNeeded();
// Set/Unset dormant state.
void SetDormant(bool aDormant);
void Shutdown();
// Called from the main thread to get the duration. The decoder monitor
@ -493,6 +500,10 @@ private:
// thread. The decoder monitor must be held.
void StartDecoding();
void StartWaitForResources();
void StartDecodeMetadata();
// Returns true if we're currently playing. The decoder monitor must
// be held.
bool IsPlaying();

View File

@ -34,6 +34,7 @@ PARALLEL_DIRS += ['webrtc']
if CONFIG['MOZ_OMX_DECODER']:
PARALLEL_DIRS += ['omx']
PARALLEL_DIRS += ['omx/mediaresourcemanager']
if CONFIG['MOZ_WEBSPEECH']:
PARALLEL_DIRS += ['webspeech']

View File

@ -18,6 +18,7 @@ include $(topsrcdir)/config/rules.mk
include $(topsrcdir)/ipc/chromium/chromium-config.mk
INCLUDES += \
-I$(srcdir)/mediaresourcemanager \
-I$(topsrcdir)/ipc/chromium/src \
-I$(srcdir)/../../base/src \
-I$(srcdir)/../../html/content/src \

View File

@ -35,6 +35,7 @@ MediaOmxReader::MediaOmxReader(AbstractMediaDecoder *aDecoder) :
MediaOmxReader::~MediaOmxReader()
{
ResetDecode();
mOmxDecoder.clear();
}
nsresult MediaOmxReader::Init(MediaDecoderReader* aCloneDonor)
@ -42,6 +43,25 @@ nsresult MediaOmxReader::Init(MediaDecoderReader* aCloneDonor)
return NS_OK;
}
bool MediaOmxReader::IsWaitingMediaResources()
{
return mOmxDecoder->IsWaitingMediaResources();
}
bool MediaOmxReader::IsDormantNeeded()
{
if (!mOmxDecoder.get()) {
return false;
}
return mOmxDecoder->IsDormantNeeded();
}
void MediaOmxReader::ReleaseMediaResources()
{
ResetDecode();
mOmxDecoder->ReleaseMediaResources();
}
nsresult MediaOmxReader::ReadMetadata(VideoInfo* aInfo,
MetadataTags** aTags)
{
@ -56,6 +76,14 @@ nsresult MediaOmxReader::ReadMetadata(VideoInfo* aInfo,
}
}
if (!mOmxDecoder->TryLoad()) {
return NS_ERROR_FAILURE;
}
if (IsWaitingMediaResources()) {
return NS_OK;
}
// Set the total duration (the max of the audio and video track).
int64_t durationUs;
mOmxDecoder->GetDuration(&durationUs);
@ -112,8 +140,6 @@ nsresult MediaOmxReader::ResetDecode()
if (container) {
container->ClearCurrentFrame();
}
mOmxDecoder.clear();
return NS_OK;
}

View File

@ -54,6 +54,11 @@ public:
return mHasVideo;
}
virtual bool IsWaitingMediaResources();
virtual bool IsDormantNeeded();
virtual void ReleaseMediaResources();
virtual nsresult ReadMetadata(VideoInfo* aInfo,
MetadataTags** aTags);
virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);

View File

@ -0,0 +1,241 @@
/* -*- 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/. */
//#define LOG_NDEBUG 0
#define LOG_TAG "OMXCodecProxy"
#include <binder/IPCThreadState.h>
#include <cutils/properties.h>
#include <stagefright/MetaData.h>
#include <stagefright/OMXCodec.h>
#include <utils/Log.h>
#include "nsDebug.h"
#include "IMediaResourceManagerService.h"
#include "OMXCodecProxy.h"
namespace android {
// static
sp<OMXCodecProxy> OMXCodecProxy::Create(
const sp<IOMX> &omx,
const sp<MetaData> &meta, bool createEncoder,
const sp<MediaSource> &source,
const char *matchComponentName,
uint32_t flags,
const sp<ANativeWindow> &nativeWindow)
{
sp<OMXCodecProxy> proxy;
const char *mime;
if (!meta->findCString(kKeyMIMEType, &mime)) {
return NULL;
}
if (!strncasecmp(mime, "video/", 6)) {
proxy = new OMXCodecProxy(omx, meta, createEncoder, source, matchComponentName, flags, nativeWindow);
}
return proxy;
}
OMXCodecProxy::OMXCodecProxy(
const sp<IOMX> &omx,
const sp<MetaData> &meta,
bool createEncoder,
const sp<MediaSource> &source,
const char *matchComponentName,
uint32_t flags,
const sp<ANativeWindow> &nativeWindow)
: mOMX(omx),
mSrcMeta(meta),
mIsEncoder(createEncoder),
mSource(source),
mComponentName(NULL),
mFlags(flags),
mNativeWindow(nativeWindow),
mState(MediaResourceManagerClient::CLIENT_STATE_WAIT_FOR_RESOURCE)
{
}
OMXCodecProxy::~OMXCodecProxy()
{
if (mOMXCodec.get()) {
wp<MediaSource> tmp = mOMXCodec;
mOMXCodec.clear();
while (tmp.promote() != NULL) {
// this value come from stagefrigh's AwesomePlayer.
usleep(1000);
}
}
// Complete all pending Binder ipc transactions
IPCThreadState::self()->flushCommands();
if (mManagerService.get() && mClient.get()) {
mManagerService->cancelClient(mClient);
}
mSource.clear();
free(mComponentName);
mComponentName = NULL;
}
MediaResourceManagerClient::State OMXCodecProxy::getState()
{
Mutex::Autolock autoLock(mLock);
return mState;
}
void OMXCodecProxy::setEventListener(const wp<OMXCodecProxy::EventListener>& listener)
{
Mutex::Autolock autoLock(mLock);
mEventListener = listener;
}
void OMXCodecProxy::notifyStatusChangedLocked()
{
if (mEventListener != NULL) {
sp<EventListener> listener = mEventListener.promote();
if (listener != NULL) {
listener->statusChanged();
}
}
}
void OMXCodecProxy::requestResource()
{
Mutex::Autolock autoLock(mLock);
if (mClient.get()) {
return;
}
sp<MediaResourceManagerClient::EventListener> listener = this;
mClient = new MediaResourceManagerClient(listener);
mManagerService = mClient->getMediaResourceManagerService();
if (!mManagerService.get()) {
mClient = NULL;
return;
}
mManagerService->requestMediaResource(mClient, MediaResourceManagerClient::HW_VIDEO_DECODER);
}
bool OMXCodecProxy::IsWaitingResources()
{
Mutex::Autolock autoLock(mLock);
return mState == MediaResourceManagerClient::CLIENT_STATE_WAIT_FOR_RESOURCE;
}
// called on Binder ipc thread
void OMXCodecProxy::statusChanged(int event)
{
Mutex::Autolock autoLock(mLock);
if (mState != MediaResourceManagerClient::CLIENT_STATE_WAIT_FOR_RESOURCE) {
return;
}
mState = (MediaResourceManagerClient::State) event;
const char *mime;
if (!mSrcMeta->findCString(kKeyMIMEType, &mime)) {
return;
}
if (!strncasecmp(mime, "video/", 6)) {
sp<MediaSource> codec;
mOMXCodec = OMXCodec::Create(mOMX, mSrcMeta, mIsEncoder, mSource, mComponentName, mFlags, mNativeWindow);
if (mOMXCodec == NULL) {
mState = MediaResourceManagerClient::CLIENT_STATE_SHUTDOWN;
notifyStatusChangedLocked();
return;
}
// Check if this video is sized such that we're comfortable
// possibly using an OMX decoder.
int32_t maxWidth, maxHeight;
char propValue[PROPERTY_VALUE_MAX];
property_get("ro.moz.omx.hw.max_width", propValue, "-1");
maxWidth = atoi(propValue);
property_get("ro.moz.omx.hw.max_height", propValue, "-1");
maxHeight = atoi(propValue);
int32_t width = -1, height = -1;
if (maxWidth > 0 && maxHeight > 0 &&
!(mOMXCodec->getFormat()->findInt32(kKeyWidth, &width) &&
mOMXCodec->getFormat()->findInt32(kKeyHeight, &height) &&
width * height <= maxWidth * maxHeight)) {
printf_stderr("Failed to get video size, or it was too large for HW decoder (<w=%d, h=%d> but <maxW=%d, maxH=%d>)",
width, height, maxWidth, maxHeight);
mState = MediaResourceManagerClient::CLIENT_STATE_SHUTDOWN;
notifyStatusChangedLocked();
return;
}
if (mOMXCodec->start() != OK) {
NS_WARNING("Couldn't start OMX video source");
mState = MediaResourceManagerClient::CLIENT_STATE_SHUTDOWN;
notifyStatusChangedLocked();
return;
}
}
notifyStatusChangedLocked();
}
status_t OMXCodecProxy::start(MetaData *params)
{
Mutex::Autolock autoLock(mLock);
if (!mOMXCodec.get()) {
return NO_INIT;
}
return mOMXCodec->start();
}
status_t OMXCodecProxy::stop()
{
Mutex::Autolock autoLock(mLock);
if (!mOMXCodec.get()) {
return NO_INIT;
}
return mOMXCodec->stop();
}
sp<MetaData> OMXCodecProxy::getFormat()
{
Mutex::Autolock autoLock(mLock);
if (!mOMXCodec.get()) {
sp<MetaData> meta = new MetaData;
return meta;
}
return mOMXCodec->getFormat();
}
status_t OMXCodecProxy::read(MediaBuffer **buffer, const ReadOptions *options)
{
Mutex::Autolock autoLock(mLock);
if (!mOMXCodec.get()) {
return NO_INIT;
}
return mOMXCodec->read(buffer, options);
}
status_t OMXCodecProxy::pause()
{
Mutex::Autolock autoLock(mLock);
if (!mOMXCodec.get()) {
return NO_INIT;
}
return mOMXCodec->pause();
}
} // namespace android

View File

@ -0,0 +1,100 @@
/* -*- 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/. */
#ifndef OMX_CODEC_PROXY_DECODER_H_
#define OMX_CODEC_PROXY_DECODER_H_
#include <android/native_window.h>
#include <IOMX.h>
#include <stagefright/MediaBuffer.h>
#include <stagefright/MediaSource.h>
#include <utils/threads.h>
#include "MediaResourceManagerClient.h"
namespace android {
struct MediaBufferGroup;
struct MetaData;
class OMXCodecProxy : public MediaSource,
public MediaResourceManagerClient::EventListener
{
public:
struct EventListener : public virtual RefBase {
virtual void statusChanged() = 0;
};
static sp<OMXCodecProxy> Create(
const sp<IOMX> &omx,
const sp<MetaData> &meta, bool createEncoder,
const sp<MediaSource> &source,
const char *matchComponentName = NULL,
uint32_t flags = 0,
const sp<ANativeWindow> &nativeWindow = NULL);
MediaResourceManagerClient::State getState();
void setEventListener(const wp<EventListener>& listener);
void requestResource();
bool IsWaitingResources();
// MediaResourceManagerClient::EventListener
virtual void statusChanged(int event);
// MediaSource
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
virtual sp<MetaData> getFormat();
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
virtual status_t pause();
protected:
OMXCodecProxy(
const sp<IOMX> &omx,
const sp<MetaData> &meta,
bool createEncoder,
const sp<MediaSource> &source,
const char *matchComponentName,
uint32_t flags,
const sp<ANativeWindow> &nativeWindow);
virtual ~OMXCodecProxy();
void notifyStatusChangedLocked();
private:
OMXCodecProxy(const OMXCodecProxy &);
OMXCodecProxy &operator=(const OMXCodecProxy &);
Mutex mLock;
sp<IOMX> mOMX;
sp<MetaData> mSrcMeta;
char *mComponentName;
bool mIsEncoder;
// Flags specified in the creation of the codec.
uint32_t mFlags;
sp<ANativeWindow> mNativeWindow;
sp<MediaSource> mSource;
sp<MediaSource> mOMXCodec;
sp<MediaResourceManagerClient> mClient;
MediaResourceManagerClient::State mState;
sp<IMediaResourceManagerService> mManagerService;
wp<OMXCodecProxy::EventListener> mEventListener;
};
} // namespace android
#endif // OMX_CODEC_PROXY_DECODER_H_

View File

@ -22,6 +22,7 @@
#include "GonkNativeWindow.h"
#include "GonkNativeWindowClient.h"
#include "OMXCodecProxy.h"
#include "OmxDecoder.h"
#ifdef PR_LOGGING
@ -163,22 +164,7 @@ OmxDecoder::OmxDecoder(MediaResource *aResource,
OmxDecoder::~OmxDecoder()
{
{
// Free all pending video buffers.
Mutex::Autolock autoLock(mSeekLock);
ReleaseAllPendingVideoBuffersLocked();
}
ReleaseVideoBuffer();
ReleaseAudioBuffer();
if (mVideoSource.get()) {
mVideoSource->stop();
}
if (mAudioSource.get()) {
mAudioSource->stop();
}
ReleaseMediaResources();
// unregister AMessage handler from ALooper.
mLooper->unregisterHandler(mReflector->id());
@ -186,19 +172,15 @@ OmxDecoder::~OmxDecoder()
mLooper->stop();
}
class AutoStopMediaSource {
sp<MediaSource> mMediaSource;
public:
AutoStopMediaSource(const sp<MediaSource>& aMediaSource) : mMediaSource(aMediaSource) {
}
~AutoStopMediaSource() {
mMediaSource->stop();
}
};
void OmxDecoder::statusChanged()
{
mozilla::ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDecoder->GetReentrantMonitor().NotifyAll();
}
static sp<IOMX> sOMX = nullptr;
static sp<IOMX> GetOMX() {
static sp<IOMX> GetOMX()
{
if(sOMX.get() == nullptr) {
sOMX = new OMX;
}
@ -231,7 +213,6 @@ bool OmxDecoder::Init() {
ssize_t audioTrackIndex = -1;
ssize_t videoTrackIndex = -1;
const char *audioMime = nullptr;
for (size_t i = 0; i < extractor->countTracks(); ++i) {
sp<MetaData> meta = extractor->getTrackMetaData(i);
@ -249,7 +230,6 @@ bool OmxDecoder::Init() {
videoTrackIndex = i;
} else if (audioTrackIndex == -1 && !strncasecmp(mime, "audio/", 6)) {
audioTrackIndex = i;
audioMime = mime;
}
}
@ -260,134 +240,52 @@ bool OmxDecoder::Init() {
mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
if (videoTrackIndex != -1) {
mVideoTrack = extractor->getTrack(videoTrackIndex);
}
if (audioTrackIndex != -1) {
mAudioTrack = extractor->getTrack(audioTrackIndex);
}
return true;
}
bool OmxDecoder::TryLoad() {
if (!AllocateMediaResources()) {
return false;
}
//check if video is waiting resources
if (mVideoSource.get()) {
if (mVideoSource->IsWaitingResources()) {
return true;
}
}
// calculate duration
int64_t totalDurationUs = 0;
mNativeWindow = new GonkNativeWindow();
mNativeWindowClient = new GonkNativeWindowClient(mNativeWindow);
// OMXClient::connect() always returns OK and abort's fatally if
// it can't connect.
OMXClient client;
DebugOnly<status_t> err = client.connect();
NS_ASSERTION(err == OK, "Failed to connect to OMX in mediaserver.");
sp<IOMX> omx = client.interface();
sp<MediaSource> videoTrack;
sp<MediaSource> videoSource;
if (videoTrackIndex != -1 && (videoTrack = extractor->getTrack(videoTrackIndex)) != nullptr) {
// Experience with OMX codecs is that only the HW decoders are
// worth bothering with, at least on the platforms where this code
// is currently used, and for formats this code is currently used
// for (h.264). So if we don't get a hardware decoder, just give
// up.
int flags = kHardwareCodecsOnly;
char propQemu[PROPERTY_VALUE_MAX];
property_get("ro.kernel.qemu", propQemu, "");
if (!strncmp(propQemu, "1", 1)) {
// If we are in emulator, allow to fall back to software.
flags = 0;
}
videoSource = OMXCodec::Create(omx,
videoTrack->getFormat(),
false, // decoder
videoTrack,
nullptr,
flags,
mNativeWindowClient);
if (videoSource == nullptr) {
NS_WARNING("Couldn't create OMX video source");
return false;
}
// Check if this video is sized such that we're comfortable
// possibly using an OMX decoder.
int32_t maxWidth, maxHeight;
char propValue[PROPERTY_VALUE_MAX];
property_get("ro.moz.omx.hw.max_width", propValue, "-1");
maxWidth = atoi(propValue);
property_get("ro.moz.omx.hw.max_height", propValue, "-1");
maxHeight = atoi(propValue);
int32_t width = -1, height = -1;
if (maxWidth > 0 && maxHeight > 0 &&
!(videoSource->getFormat()->findInt32(kKeyWidth, &width) &&
videoSource->getFormat()->findInt32(kKeyHeight, &height) &&
width * height <= maxWidth * maxHeight)) {
printf_stderr("Failed to get video size, or it was too large for HW decoder (<w=%d, h=%d> but <maxW=%d, maxH=%d>)",
width, height, maxWidth, maxHeight);
return false;
}
if (videoSource->start() != OK) {
NS_WARNING("Couldn't start OMX video source");
return false;
}
int64_t durationUs;
if (videoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
if (durationUs > totalDurationUs)
totalDurationUs = durationUs;
}
int64_t durationUs = 0;
if (mVideoTrack.get() && mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
if (durationUs > totalDurationUs)
totalDurationUs = durationUs;
}
sp<MediaSource> audioTrack;
sp<MediaSource> audioSource;
if (audioTrackIndex != -1 && (audioTrack = extractor->getTrack(audioTrackIndex)) != nullptr)
{
if (!strcasecmp(audioMime, "audio/raw")) {
audioSource = audioTrack;
} else {
// try to load hardware codec in mediaserver process.
int flags = kHardwareCodecsOnly;
audioSource = OMXCodec::Create(omx,
audioTrack->getFormat(),
false, // decoder
audioTrack,
nullptr,
flags);
}
if (audioSource == nullptr) {
// try to load software codec in this process.
int flags = kSoftwareCodecsOnly;
audioSource = OMXCodec::Create(GetOMX(),
audioTrack->getFormat(),
false, // decoder
audioTrack,
nullptr,
flags);
if (audioSource == nullptr) {
NS_WARNING("Couldn't create OMX audio source");
return false;
}
}
if (audioSource->start() != OK) {
NS_WARNING("Couldn't start OMX audio source");
return false;
}
int64_t durationUs;
if (audioTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
if (durationUs > totalDurationUs)
totalDurationUs = durationUs;
}
if (mAudioTrack.get() && mAudioTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
if (durationUs > totalDurationUs)
totalDurationUs = durationUs;
}
// set decoder state
mVideoTrack = videoTrack;
mVideoSource = videoSource;
mAudioTrack = audioTrack;
mAudioSource = audioSource;
mDurationUs = totalDurationUs;
// read video metadata
if (mVideoSource.get() && !SetVideoFormat()) {
NS_WARNING("Couldn't set OMX video format");
return false;
}
// To reliably get the channel and sample rate data we need to read from the
// audio source until we get a INFO_FORMAT_CHANGE status
// read audio metadata
if (mAudioSource.get()) {
// To reliably get the channel and sample rate data we need to read from the
// audio source until we get a INFO_FORMAT_CHANGE status
status_t err = mAudioSource->read(&mAudioBuffer);
if (err != INFO_FORMAT_CHANGED) {
if (err != OK) {
@ -411,6 +309,132 @@ bool OmxDecoder::Init() {
return true;
}
bool OmxDecoder::IsDormantNeeded()
{
if (mVideoTrack.get()) {
return true;
}
return false;
}
bool OmxDecoder::IsWaitingMediaResources()
{
if (mVideoSource.get()) {
return mVideoSource->IsWaitingResources();
}
return false;
}
bool OmxDecoder::AllocateMediaResources()
{
// OMXClient::connect() always returns OK and abort's fatally if
// it can't connect.
OMXClient client;
DebugOnly<status_t> err = client.connect();
NS_ASSERTION(err == OK, "Failed to connect to OMX in mediaserver.");
sp<IOMX> omx = client.interface();
if ((mVideoTrack != nullptr) && (mVideoSource == nullptr)) {
mNativeWindow = new GonkNativeWindow();
mNativeWindowClient = new GonkNativeWindowClient(mNativeWindow);
// Experience with OMX codecs is that only the HW decoders are
// worth bothering with, at least on the platforms where this code
// is currently used, and for formats this code is currently used
// for (h.264). So if we don't get a hardware decoder, just give
// up.
int flags = kHardwareCodecsOnly;
char propQemu[PROPERTY_VALUE_MAX];
property_get("ro.kernel.qemu", propQemu, "");
if (!strncmp(propQemu, "1", 1)) {
// If we are in emulator, allow to fall back to software.
flags = 0;
}
mVideoSource =
OMXCodecProxy::Create(omx,
mVideoTrack->getFormat(),
false, // decoder
mVideoTrack,
nullptr,
flags,
mNativeWindowClient);
if (mVideoSource == nullptr) {
NS_WARNING("Couldn't create OMX video source");
return false;
} else {
sp<OMXCodecProxy::EventListener> listener = this;
mVideoSource->setEventListener(listener);
mVideoSource->requestResource();
}
}
if ((mAudioTrack != nullptr) && (mAudioSource == nullptr)) {
const char *audioMime = nullptr;
sp<MetaData> meta = mAudioTrack->getFormat();
if (!meta->findCString(kKeyMIMEType, &audioMime)) {
return false;
}
if (!strcasecmp(audioMime, "audio/raw")) {
mAudioSource = mAudioTrack;
} else {
// try to load hardware codec in mediaserver process.
int flags = kHardwareCodecsOnly;
mAudioSource = OMXCodec::Create(omx,
mAudioTrack->getFormat(),
false, // decoder
mAudioTrack,
nullptr,
flags);
}
if (mAudioSource == nullptr) {
// try to load software codec in this process.
int flags = kSoftwareCodecsOnly;
mAudioSource = OMXCodec::Create(GetOMX(),
mAudioTrack->getFormat(),
false, // decoder
mAudioTrack,
nullptr,
flags);
if (mAudioSource == nullptr) {
NS_WARNING("Couldn't create OMX audio source");
return false;
}
}
if (mAudioSource->start() != OK) {
NS_WARNING("Couldn't start OMX audio source");
return false;
}
}
return true;
}
void OmxDecoder::ReleaseMediaResources() {
{
// Free all pending video buffers.
Mutex::Autolock autoLock(mSeekLock);
ReleaseAllPendingVideoBuffersLocked();
}
ReleaseVideoBuffer();
ReleaseAudioBuffer();
if (mVideoSource.get()) {
mVideoSource->stop();
mVideoSource.clear();
}
if (mAudioSource.get()) {
mAudioSource->stop();
mAudioSource.clear();
}
mNativeWindowClient.clear();
mNativeWindow.clear();
}
bool OmxDecoder::SetVideoFormat() {
const char *componentName;
@ -707,7 +731,8 @@ bool OmxDecoder::ReadAudio(AudioFrame *aFrame, int64_t aSeekTimeUs)
return true;
}
nsresult OmxDecoder::Play() {
nsresult OmxDecoder::Play()
{
if (!mPaused) {
return NS_OK;
}
@ -722,7 +747,8 @@ nsresult OmxDecoder::Play() {
return NS_OK;
}
void OmxDecoder::Pause() {
void OmxDecoder::Pause()
{
if (mPaused) {
return;
}

View File

@ -12,6 +12,7 @@
#include "MPAPI.h"
#include "MediaResource.h"
#include "AbstractMediaDecoder.h"
#include "OMXCodecProxy.h"
namespace android {
class OmxDecoder;
@ -72,7 +73,7 @@ private:
MediaStreamSource &operator=(const MediaStreamSource &);
};
class OmxDecoder : public RefBase {
class OmxDecoder : public OMXCodecProxy::EventListener {
typedef MPAPI::AudioFrame AudioFrame;
typedef MPAPI::VideoFrame VideoFrame;
typedef mozilla::MediaResource MediaResource;
@ -93,7 +94,7 @@ class OmxDecoder : public RefBase {
sp<GonkNativeWindow> mNativeWindow;
sp<GonkNativeWindowClient> mNativeWindowClient;
sp<MediaSource> mVideoTrack;
sp<MediaSource> mVideoSource;
sp<OMXCodecProxy> mVideoSource;
sp<MediaSource> mAudioTrack;
sp<MediaSource> mAudioSource;
int32_t mVideoWidth;
@ -162,7 +163,15 @@ public:
OmxDecoder(MediaResource *aResource, AbstractMediaDecoder *aDecoder);
~OmxDecoder();
// MediaResourceManagerClient::EventListener
virtual void statusChanged();
bool Init();
bool TryLoad();
bool IsDormantNeeded();
bool IsWaitingMediaResources();
bool AllocateMediaResources();
void ReleaseMediaResources();
bool SetVideoFormat();
bool SetAudioFormat();

View File

@ -0,0 +1,62 @@
/* -*- 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/. */
//#define LOG_NDEBUG 0
#define LOG_TAG "IMediaResourceManagerClient"
#include <utils/Log.h>
#include <stdint.h>
#include <sys/types.h>
#include <binder/Parcel.h>
#include "IMediaResourceManagerClient.h"
namespace android {
enum {
STATUS_CHANGED = IBinder::FIRST_CALL_TRANSACTION
};
class BpMediaResourceManagerClient : public BpInterface<IMediaResourceManagerClient>
{
public:
BpMediaResourceManagerClient(const sp<IBinder>& impl)
: BpInterface<IMediaResourceManagerClient>(impl)
{
}
void statusChanged(int event)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaResourceManagerClient::getInterfaceDescriptor());
data.writeInt32(event);
remote()->transact(STATUS_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
}
};
IMPLEMENT_META_INTERFACE(MediaResourceManagerClient, "android.media.IMediaResourceManagerClient");
// ----------------------------------------------------------------------
status_t BnMediaResourceManagerClient::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case STATUS_CHANGED: {
CHECK_INTERFACE(IMediaResourceManagerClient, data, reply);
int event = data.readInt32();
statusChanged(event);
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
// ----------------------------------------------------------------------------
}; // namespace android

View File

@ -0,0 +1,43 @@
/* -*- 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/. */
#ifndef ANDROID_IMEDIARESOURCEMANAGERCLIENT_H
#define ANDROID_IMEDIARESOURCEMANAGERCLIENT_H
#include <utils/RefBase.h>
#include <binder/IInterface.h>
namespace android {
// ----------------------------------------------------------------------------
class IMediaResourceManagerClient : public IInterface
{
public:
DECLARE_META_INTERFACE(MediaResourceManagerClient);
// Notifies a change of media resource request status.
virtual void statusChanged(int event) = 0;
};
// ----------------------------------------------------------------------------
class BnMediaResourceManagerClient : public BnInterface<IMediaResourceManagerClient>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
// ----------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_IMEDIARESOURCEMANAGERCLIENT_H

View File

@ -0,0 +1,114 @@
/*
** Copyright 2010, The Android Open Source Project
** Copyright 2013, Mozilla Foundation
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "IMediaResourceManagerDeathNotifier"
#include <utils/Log.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include "IMediaResourceManagerDeathNotifier.h"
namespace android {
// client singleton for binder interface to services
Mutex IMediaResourceManagerDeathNotifier::sServiceLock;
sp<IMediaResourceManagerService> IMediaResourceManagerDeathNotifier::sMediaResourceManagerService;
sp<IMediaResourceManagerDeathNotifier::DeathNotifier> IMediaResourceManagerDeathNotifier::sDeathNotifier;
SortedVector< wp<IMediaResourceManagerDeathNotifier> > IMediaResourceManagerDeathNotifier::sObitRecipients;
// establish binder interface to MediaResourceManagerService
/*static*/const sp<IMediaResourceManagerService>&
IMediaResourceManagerDeathNotifier::getMediaResourceManagerService()
{
LOGV("getMediaResourceManagerService");
Mutex::Autolock _l(sServiceLock);
if (sMediaResourceManagerService.get() == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16("media.resource_manager"));
if (binder != 0) {
break;
}
LOGW("Media resource manager service not published, waiting...");
usleep(500000); // 0.5 s
} while(true);
if (sDeathNotifier == NULL) {
sDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(sDeathNotifier);
sMediaResourceManagerService = interface_cast<IMediaResourceManagerService>(binder);
}
LOGE_IF(sMediaResourceManagerService == 0, "no media player service!?");
return sMediaResourceManagerService;
}
/*static*/ void
IMediaResourceManagerDeathNotifier::addObitRecipient(const wp<IMediaResourceManagerDeathNotifier>& recipient)
{
LOGV("addObitRecipient");
Mutex::Autolock _l(sServiceLock);
sObitRecipients.add(recipient);
}
/*static*/ void
IMediaResourceManagerDeathNotifier::removeObitRecipient(const wp<IMediaResourceManagerDeathNotifier>& recipient)
{
LOGV("removeObitRecipient");
Mutex::Autolock _l(sServiceLock);
sObitRecipients.remove(recipient);
}
void
IMediaResourceManagerDeathNotifier::DeathNotifier::binderDied(const wp<IBinder>& who)
{
LOGW("media server died");
// Need to do this with the lock held
SortedVector< wp<IMediaResourceManagerDeathNotifier> > list;
{
Mutex::Autolock _l(sServiceLock);
sMediaResourceManagerService.clear();
list = sObitRecipients;
}
// Notify application when media server dies.
// Don't hold the static lock during callback in case app
// makes a call that needs the lock.
size_t count = list.size();
for (size_t iter = 0; iter < count; ++iter) {
sp<IMediaResourceManagerDeathNotifier> notifier = list[iter].promote();
if (notifier != 0) {
notifier->died();
}
}
}
IMediaResourceManagerDeathNotifier::DeathNotifier::~DeathNotifier()
{
LOGV("DeathNotifier::~DeathNotifier");
Mutex::Autolock _l(sServiceLock);
sObitRecipients.clear();
if (sMediaResourceManagerService != 0) {
sMediaResourceManagerService->asBinder()->unlinkToDeath(this);
}
}
}; // namespace android

View File

@ -0,0 +1,67 @@
/*
** Copyright 2010, The Android Open Source Project
** Copyright 2013, Mozilla Foundation
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#ifndef ANDROID_IMEDIARESOURCEMANAGERDEATHNOTIFIER_H
#define ANDROID_IMEDIARESOURCEMANAGERDEATHNOTIFIER_H
#include <utils/threads.h>
#include <utils/SortedVector.h>
#include "IMediaResourceManagerService.h"
namespace android {
/**
* Handle MediaResourceManagerService's death notification.
* Made from android's IMediaDeathNotifier class.
*/
class IMediaResourceManagerDeathNotifier: virtual public RefBase
{
public:
IMediaResourceManagerDeathNotifier() { addObitRecipient(this); }
virtual ~IMediaResourceManagerDeathNotifier() { removeObitRecipient(this); }
virtual void died() = 0;
static const sp<IMediaResourceManagerService>& getMediaResourceManagerService();
private:
IMediaResourceManagerDeathNotifier &operator=(const IMediaResourceManagerDeathNotifier &);
IMediaResourceManagerDeathNotifier(const IMediaResourceManagerDeathNotifier &);
static void addObitRecipient(const wp<IMediaResourceManagerDeathNotifier>& recipient);
static void removeObitRecipient(const wp<IMediaResourceManagerDeathNotifier>& recipient);
class DeathNotifier: public IBinder::DeathRecipient
{
public:
DeathNotifier() {}
virtual ~DeathNotifier();
virtual void binderDied(const wp<IBinder>& who);
};
friend class DeathNotifier;
static Mutex sServiceLock;
static sp<IMediaResourceManagerService> sMediaResourceManagerService;
static sp<DeathNotifier> sDeathNotifier;
static SortedVector< wp<IMediaResourceManagerDeathNotifier> > sObitRecipients;
};
}; // namespace android
#endif // ANDROID_IMEDIARESOURCEMANAGERDEATHNOTIFIER_H

View File

@ -0,0 +1,86 @@
/* -*- 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/. */
//#define LOG_NDEBUG 0
#define LOG_TAG "IMediaResourceManagerService"
#include <stdint.h>
#include <sys/types.h>
#include <binder/Parcel.h>
#include <utils/Log.h>
#include "IMediaResourceManagerService.h"
namespace android {
/**
* Function ID used between BpMediaResourceManagerService and
* BnMediaResourceManagerService by using Binder ipc.
*/
enum {
REQUEST_MEDIA_RESOURCE = IBinder::FIRST_CALL_TRANSACTION,
DEREGISTER_CLIENT
};
class BpMediaResourceManagerService : public BpInterface<IMediaResourceManagerService>
{
public:
BpMediaResourceManagerService(const sp<IBinder>& impl)
: BpInterface<IMediaResourceManagerService>(impl)
{
}
virtual void requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaResourceManagerService::getInterfaceDescriptor());
data.writeStrongBinder(client->asBinder());
data.writeInt32(resourceType);
remote()->transact(REQUEST_MEDIA_RESOURCE, data, &reply);
}
virtual status_t cancelClient(const sp<IMediaResourceManagerClient>& client)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaResourceManagerService::getInterfaceDescriptor());
data.writeStrongBinder(client->asBinder());
remote()->transact(DEREGISTER_CLIENT, data, &reply);
return reply.readInt32();
}
};
IMPLEMENT_META_INTERFACE(MediaResourceManagerService, "android.media.IMediaResourceManagerService");
// ----------------------------------------------------------------------
status_t BnMediaResourceManagerService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case REQUEST_MEDIA_RESOURCE: {
CHECK_INTERFACE(IMediaResourceManagerService, data, reply);
sp<IMediaResourceManagerClient> client = interface_cast<IMediaResourceManagerClient>(data.readStrongBinder());
int resourceType = data.readInt32();
requestMediaResource(client, resourceType);
return NO_ERROR;
} break;
case DEREGISTER_CLIENT: {
CHECK_INTERFACE(IMediaResourceManagerService, data, reply);
sp<IMediaResourceManagerClient> client = interface_cast<IMediaResourceManagerClient>(data.readStrongBinder());
cancelClient(client);
reply->writeInt32(NO_ERROR);
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
// ----------------------------------------------------------------------------
}; // namespace android

View File

@ -0,0 +1,47 @@
/* -*- 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/. */
#ifndef ANDROID_IMEDIARESOURCEMANAGERSERVICE_H
#define ANDROID_IMEDIARESOURCEMANAGERSERVICE_H
#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include "IMediaResourceManagerClient.h"
namespace android {
// ----------------------------------------------------------------------------
class IMediaResourceManagerService : public IInterface
{
public:
DECLARE_META_INTERFACE(MediaResourceManagerService);
// Request a media resource for IMediaResourceManagerClient.
virtual void requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType) = 0;
// Cancel a media resource request and a resource allocated to IMediaResourceManagerClient.
virtual status_t cancelClient(const sp<IMediaResourceManagerClient>& client) = 0;
};
// ----------------------------------------------------------------------------
class BnMediaResourceManagerService : public BnInterface<IMediaResourceManagerService>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
// ----------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_IMEDIARESOURCEMANAGERSERVICE_H

View File

@ -0,0 +1,27 @@
# 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/.
DEPTH = @DEPTH@
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
LIBRARY_NAME = mediaresourcemanager
FORCE_STATIC_LIB = 1
include $(topsrcdir)/config/rules.mk
include $(topsrcdir)/ipc/chromium/chromium-config.mk
INCLUDES += \
-I$(srcdir)/ \
-I$(ANDROID_SOURCE)/frameworks/base/include/ \
-I$(ANDROID_SOURCE)/frameworks/base/include/binder/ \
-I$(ANDROID_SOURCE)/frameworks/base/include/utils/ \
-I$(ANDROID_SOURCE)/frameworks/base/include/media/ \
-I$(ANDROID_SOURCE)/frameworks/base/include/media/stagefright/openmax \
-I$(ANDROID_SOURCE)/frameworks/base/media/libstagefright/include/ \
$(NULL)

View File

@ -0,0 +1,40 @@
/* -*- 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/. */
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaResourceManagerClient"
#include <utils/Log.h>
#include "MediaResourceManagerClient.h"
namespace android {
MediaResourceManagerClient::MediaResourceManagerClient(const wp<EventListener>& listener)
: mEventListener(listener)
{
}
void MediaResourceManagerClient::statusChanged(int event)
{
if (mEventListener != NULL) {
sp<EventListener> listener = mEventListener.promote();
if (listener != NULL) {
listener->statusChanged(event);
}
}
}
void MediaResourceManagerClient::died()
{
sp<EventListener> listener = mEventListener.promote();
if (listener != NULL) {
listener->statusChanged(CLIENT_STATE_SHUTDOWN);
}
}
}; // namespace android

View File

@ -0,0 +1,51 @@
/* -*- 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/. */
#ifndef ANDROID_MEDIARESOURCEMANAGERCLIENT_H
#define ANDROID_MEDIARESOURCEMANAGERCLIENT_H
#include "IMediaResourceManagerClient.h"
#include "IMediaResourceManagerDeathNotifier.h"
namespace android {
class MediaResourceManagerClient: public BnMediaResourceManagerClient,
public virtual IMediaResourceManagerDeathNotifier
{
public:
// Enumeration for the valid decoding states
enum State {
CLIENT_STATE_WAIT_FOR_RESOURCE,
CLIENT_STATE_RESOURCE_ASSIGNED,
CLIENT_STATE_SHUTDOWN
};
// Enumeration for the resource types
enum ResourceType {
HW_VIDEO_DECODER,
HW_AUDIO_DECODER,
HW_CAMERA
};
struct EventListener : public virtual RefBase {
// Notifies a change of media resource request status.
virtual void statusChanged(int event) = 0;
};
MediaResourceManagerClient(const wp<EventListener>& listener);
// DeathRecipient
void died();
// IMediaResourceManagerClient
virtual void statusChanged(int event);
private:
wp<EventListener> mEventListener;
};
}; // namespace android
#endif // ANDROID_IMEDIARESOURCEMANAGERCLIENT_H

View File

@ -0,0 +1,179 @@
/* -*- 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/. */
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaResourceManagerService"
#include <binder/IServiceManager.h>
#include <media/stagefright/foundation/AMessage.h>
#include <utils/Log.h>
#include "MediaResourceManagerClient.h"
#include "MediaResourceManagerService.h"
namespace android {
int waitBeforeAdding(const android::String16& serviceName)
{
android::sp<android::IServiceManager> sm = android::defaultServiceManager();
for ( int i = 0 ; i < 5; i++ ) {
if ( sm->checkService ( serviceName ) != NULL ) {
sleep(1);
}
else {
//good to go;
return 0;
}
}
// time out failure
return -1;
}
// Wait until service manager is started
void
waitServiceManager()
{
android::sp<android::IServiceManager> sm;
do {
sm = android::defaultServiceManager();
if (sm.get()) {
break;
}
usleep(50000); // 0.05 s
} while(true);
}
/* static */
void MediaResourceManagerService::instantiate() {
waitServiceManager();
waitBeforeAdding( android::String16("media.resource_manager") );
defaultServiceManager()->addService(
String16("media.resource_manager"), new MediaResourceManagerService());
}
MediaResourceManagerService::MediaResourceManagerService()
: mVideoDecoderCount(VIDEO_DECODER_COUNT)
{
mLooper = new ALooper;
mLooper->setName("MediaResourceManagerService");
mReflector = new AHandlerReflector<MediaResourceManagerService>(this);
// Register AMessage handler to ALooper.
mLooper->registerHandler(mReflector);
// Start ALooper thread.
mLooper->start();
}
MediaResourceManagerService::~MediaResourceManagerService()
{
// Unregister AMessage handler from ALooper.
mLooper->unregisterHandler(mReflector->id());
// Stop ALooper thread.
mLooper->stop();
}
void MediaResourceManagerService::binderDied(const wp<IBinder>& who)
{
if (who != NULL) {
sp<IBinder> binder = who.promote();
if (binder != NULL) {
cancelClientLocked(binder);
}
}
}
void MediaResourceManagerService::requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType)
{
if (resourceType != MediaResourceManagerClient::HW_VIDEO_DECODER) {
// Support only HW_VIDEO_DECODER
return;
}
{
Mutex::Autolock autoLock(mLock);
sp<IBinder> binder = client->asBinder();
mVideoCodecRequestQueue.push_back(binder);
binder->linkToDeath(this);
}
sp<AMessage> notify =
new AMessage(kNotifyRequest, mReflector->id());
// Post AMessage to MediaResourceManagerService via ALooper.
notify->post();
}
status_t MediaResourceManagerService::cancelClient(const sp<IMediaResourceManagerClient>& client)
{
{
Mutex::Autolock autoLock(mLock);
sp<IBinder> binder = client->asBinder();
cancelClientLocked(binder);
}
sp<AMessage> notify =
new AMessage(kNotifyRequest, mReflector->id());
// Post AMessage to MediaResourceManagerService via ALooper.
notify->post();
return NO_ERROR;
}
// Called on ALooper thread.
void MediaResourceManagerService::onMessageReceived(const sp<AMessage> &msg)
{
Mutex::Autolock autoLock(mLock);
// Exit if no request.
if (mVideoCodecRequestQueue.empty()) {
return;
}
// Check if resource is available
int found = -1;
for (int i=0 ; i<mVideoDecoderCount ; i++) {
if (!mVideoDecoderSlots[i].mClient.get()) {
found = i;
}
}
// Exit if no resource is available.
if (found == -1) {
return;
}
// Assign resource to IMediaResourceManagerClient
Fifo::iterator front(mVideoCodecRequestQueue.begin());
mVideoDecoderSlots[found].mClient = *front;
mVideoCodecRequestQueue.erase(front);
// Notify resource assignment to the client.
sp<IMediaResourceManagerClient> client = interface_cast<IMediaResourceManagerClient>(mVideoDecoderSlots[found].mClient);
client->statusChanged(MediaResourceManagerClient::CLIENT_STATE_RESOURCE_ASSIGNED);
}
void MediaResourceManagerService::cancelClientLocked(const sp<IBinder>& binder)
{
// Clear the request from request queue.
Fifo::iterator it(mVideoCodecRequestQueue.begin());
while (it != mVideoCodecRequestQueue.end()) {
if (*it == binder) {
*it = NULL;
continue;
}
it++;
}
// Clear the client from the resource
for (int i=0 ; i<mVideoDecoderCount ; i++) {
if (mVideoDecoderSlots[i].mClient == binder) {
mVideoDecoderSlots[i].mClient = NULL;
}
}
binder->unlinkToDeath(this);
}
}; // namespace android

View File

@ -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/. */
#ifndef ANDROID_MEDIARESOURCEMANAGERSERVICE_H
#define ANDROID_MEDIARESOURCEMANAGERSERVICE_H
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AHandlerReflector.h>
#include <media/stagefright/foundation/ALooper.h>
#include <utils/List.h>
#include <utils/RefBase.h>
#include "IMediaResourceManagerClient.h"
#include "IMediaResourceManagerService.h"
namespace android {
/**
* Manage permissions of using media resources(hw decoder, hw encoder, camera)
* XXX Current implementaion support only one hw video decoder.
* Need to extend to support multiple instance and other resources.
*/
class MediaResourceManagerService: public BnMediaResourceManagerService,
public IBinder::DeathRecipient
{
public:
// The maximum number of hardware decoders available.
enum { VIDEO_DECODER_COUNT = 1 };
enum {
kNotifyRequest = 'noti'
};
// Instantiate MediaResourceManagerService and register to service manager.
// If service manager is not present, wait until service manager becomes present.
static void instantiate();
// DeathRecipient
virtual void binderDied(const wp<IBinder>& who);
// derived from IMediaResourceManagerService
virtual void requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType);
virtual status_t cancelClient(const sp<IMediaResourceManagerClient>& client);
// Receive a message from AHandlerReflector.
// Called on ALooper thread.
void onMessageReceived(const sp<AMessage> &msg);
protected:
MediaResourceManagerService();
virtual ~MediaResourceManagerService();
protected:
// Represent a media resouce.
// Hold a IMediaResourceManagerClient that got a media resource as IBinder.
struct ResourceSlot {
ResourceSlot ()
{
}
sp<IBinder> mClient;
};
void cancelClientLocked(const sp<IBinder>& binder);
// mVideoDecoderSlots is the array of slots that represent a media resource.
ResourceSlot mVideoDecoderSlots[VIDEO_DECODER_COUNT];
// The maximum number of hardware decoders available on the device.
int mVideoDecoderCount;
// The lock protects mVideoDecoderSlots and mVideoCodecRequestQueue called
// from multiple threads.
Mutex mLock;
typedef Vector<sp<IBinder> > Fifo;
// Queue of media resource requests.
// Hold IMediaResourceManagerClient that requesting a media resource as IBinder.
Fifo mVideoCodecRequestQueue;
// ALooper is a message loop used in stagefright.
// It creates a thread for messages and handles messages in the thread.
// ALooper is a clone of Looper in android Java.
// http://developer.android.com/reference/android/os/Looper.html
sp<ALooper> mLooper;
// deliver a message to a wrapped object(OmxDecoder).
// AHandlerReflector is similar to Handler in android Java.
// http://developer.android.com/reference/android/os/Handler.html
sp<AHandlerReflector<MediaResourceManagerService> > mReflector;
};
}; // namespace android
#endif // ANDROID_MEDIARESOURCEMANAGERSERVICE_H

View File

@ -0,0 +1,16 @@
# -*- 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/.
MODULE = 'content'
CPP_SOURCES += [
'IMediaResourceManagerClient.cpp',
'IMediaResourceManagerDeathNotifier.cpp',
'IMediaResourceManagerService.cpp',
'MediaResourceManagerClient.cpp',
'MediaResourceManagerService.cpp',
]

View File

@ -15,5 +15,6 @@ CPP_SOURCES += [
'MediaOmxDecoder.cpp',
'MediaOmxReader.cpp',
'OmxDecoder.cpp',
'OMXCodecProxy.cpp',
]

View File

@ -115,6 +115,7 @@ ifdef MOZ_OMX_DECODER #{
# include OMX decoder
SHARED_LIBRARY_LIBS += \
$(DEPTH)/content/media/omx/$(LIB_PREFIX)gkconomx_s.$(LIB_SUFFIX) \
$(DEPTH)/content/media/omx/mediaresourcemanager/$(LIB_PREFIX)mediaresourcemanager.$(LIB_SUFFIX) \
$(NULL)
endif #}

View File

@ -43,6 +43,7 @@ LOCAL_INCLUDES += \
-I$(topsrcdir)/content/events/src \
-I$(topsrcdir)/gfx/skia/include/core \
-I$(topsrcdir)/gfx/skia/include/config \
-I$(topsrcdir)/content/media/omx/mediaresourcemanager \
-I$(srcdir) \
$(NULL)

View File

@ -30,6 +30,7 @@
#include "base/basictypes.h"
#include "nscore.h"
#include "MediaResourceManagerService.h"
#include "mozilla/FileUtils.h"
#include "mozilla/Hal.h"
#include "mozilla/Mutex.h"
@ -639,6 +640,10 @@ nsAppShell::Init()
InitGonkMemoryPressureMonitoring();
if (XRE_GetProcessType() == GeckoProcessType_Default) {
android::MediaResourceManagerService::instantiate();
}
nsCOMPtr<nsIObserverService> obsServ = GetObserverService();
if (obsServ) {
obsServ->AddObserver(this, "browser-ui-startup-complete", false);