Bug 976172 - Part 1 Changes in MediaOmxDecoder to enable audio offloading r=roc

This adds support in MediaOmxDecoder to dynamically switch between
MediaDecoderStateMachine and AudioOffloadPlayer
This commit is contained in:
Vasanthakumar Pandurangan 2014-04-08 20:59:07 +05:30
Родитель 92bcfe6282
Коммит 0f755350a7
13 изменённых файлов: 453 добавлений и 17 удалений

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

@ -232,7 +232,7 @@ if test -n "$gonkdir" ; then
case "$ANDROID_VERSION" in
15)
GONK_INCLUDES="-I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/frameworks/base/include -I$gonkdir/frameworks/base/services/camera -I$gonkdir/frameworks/base/include/media/stagefright -I$gonkdir/frameworks/base/include/media/stagefright/openmax -I$gonkdir/frameworks/base/media/libstagefright/rtsp -I$gonkdir/frameworks/base/media/libstagefright/include -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib -I$gonkdir/dalvik/libnativehelper/include/nativehelper"
GONK_INCLUDES="-I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/frameworks/base/include -I$gonkdir/frameworks/base/services/camera -I$gonkdir/frameworks/base/include/media/ -I$gonkdir/frameworks/base/include/media/stagefright -I$gonkdir/frameworks/base/include/media/stagefright/openmax -I$gonkdir/frameworks/base/media/libstagefright/rtsp -I$gonkdir/frameworks/base/media/libstagefright/include -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib -I$gonkdir/dalvik/libnativehelper/include/nativehelper"
MOZ_B2G_BT=1
MOZ_B2G_BT_BLUEZ=1
MOZ_NFC=1

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

@ -50,6 +50,9 @@ class nsIRunnable;
namespace mozilla {
namespace dom {
// Number of milliseconds between timeupdate events as defined by spec
#define TIMEUPDATE_MS 250
class MediaError;
class MediaSource;
class TextTrackList;

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

@ -107,9 +107,6 @@ using mozilla::net::nsMediaFragmentURIParser;
namespace mozilla {
namespace dom {
// Number of milliseconds between timeupdate events as defined by spec
#define TIMEUPDATE_MS 250
// Used by AudioChannel for suppresssing the volume to this ratio.
#define FADED_VOLUME_RATIO 0.25
@ -3385,6 +3382,7 @@ void HTMLMediaElement::NotifyOwnerDocumentActivityChanged()
nsIDocument* ownerDoc = OwnerDoc();
if (mDecoder) {
mDecoder->SetElementVisibility(!ownerDoc->Hidden());
mDecoder->SetDormantIfNecessary(ownerDoc->Hidden());
}

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

@ -116,6 +116,12 @@ public:
// required to begin playback have been acquired. Can be called on any thread.
virtual void NotifyWaitingForResourcesStatusChanged() = 0;
// Called by Reader if the current audio track can be offloaded
virtual void SetCanOffloadAudio(bool aCanOffloadAudio) {}
// Called from HTMLMediaElement when owner document activity changes
virtual void SetElementVisibility(bool aIsVisible) {}
// Stack based class to assist in notifying the frame statistics of
// parsed and decoded frames. Use inside video demux & decode functions
// to ensure all parsed and decoded frames are reported on all return paths.

12
content/media/MediaDecoder.h Normal file → Executable file
Просмотреть файл

@ -368,7 +368,7 @@ public:
virtual void NotifyWaitingForResourcesStatusChanged() MOZ_OVERRIDE;
void SetPlaybackRate(double aPlaybackRate);
virtual void SetPlaybackRate(double aPlaybackRate);
void SetPreservesPitch(bool aPreservesPitch);
// Directs the decoder to not preroll extra samples until the media is
@ -760,7 +760,7 @@ public:
// Change to a new play state. This updates the mState variable and
// notifies any thread blocking on this object's monitor of the
// change. Call on the main thread only.
void ChangeState(PlayState aState);
virtual void ChangeState(PlayState aState);
// Called by |ChangeState|, to update the state machine.
// Call on the main thread only and the lock must be obtained.
@ -772,7 +772,11 @@ public:
// Called when the metadata from the media file has been loaded by the
// state machine. Call on the main thread only.
void MetadataLoaded(int aChannels, int aRate, bool aHasAudio, bool aHasVideo, MetadataTags* aTags);
virtual void MetadataLoaded(int aChannels,
int aRate,
bool aHasAudio,
bool aHasVideo,
MetadataTags* aTags);
// Called when the first frame has been loaded.
// Call on the main thread only.
@ -805,7 +809,7 @@ public:
// Calls mElement->UpdateReadyStateForData, telling it whether we have
// data for the next frame and if we're buffering. Main thread only.
void UpdateReadyStateForData();
virtual void UpdateReadyStateForData();
// Find the end of the cached data starting at the current decoder
// position.

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

@ -0,0 +1,72 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/*
* Copyright (c) 2014 The Linux Foundation. All rights reserved.
* Copyright (C) 2009 The Android Open Source Project
*
* 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 AUDIO_OFFLOAD_PLAYER_BASE_H_
#define AUDIO_OFFLOAD_PLAYER_BASE_H_
#include "MediaDecoderOwner.h"
#include "MediaOmxDecoder.h"
namespace mozilla {
class MediaOmxDecoder;
/**
* AudioOffloadPlayer interface class which has funtions used by MediaOmxDecoder
* This is to reduce the dependency of AudioOffloadPlayer in MediaOmxDecoder
*/
class AudioOffloadPlayerBase
{
typedef android::status_t status_t;
typedef android::MediaSource MediaSource;
public:
virtual ~AudioOffloadPlayerBase() {};
// Caller retains ownership of "aSource".
virtual void SetSource(const android::sp<MediaSource> &aSource) {}
// Start the source if it's not already started and open the AudioSink to
// create an offloaded audio track
virtual status_t Start(bool aSourceAlreadyStarted = false)
{
return android::NO_INIT;
}
virtual void ChangeState(MediaDecoder::PlayState aState) {}
virtual void SetVolume(double aVolume) {}
virtual double GetMediaTimeSecs() { return 0; }
// To update progress bar when the element is visible
virtual void SetElementVisibility(bool aIsVisible) {}
// Update ready state based on current play state. Not checking data
// availability since offloading is currently done only when whole compressed
// data is available
virtual MediaDecoderOwner::NextFrameStatus GetNextFrameStatus()
{
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
}
};
} // namespace mozilla
#endif // AUDIO_OFFLOAD_PLAYER_BASE_H_

223
content/media/omx/MediaOmxDecoder.cpp Normal file → Executable file
Просмотреть файл

@ -8,11 +8,33 @@
#include "MediaOmxReader.h"
#include "MediaDecoderStateMachine.h"
#include "OmxDecoder.h"
#ifdef MOZ_AUDIO_OFFLOAD
#include "AudioOffloadPlayer.h"
#endif
using namespace android;
namespace mozilla {
#ifdef PR_LOGGING
extern PRLogModuleInfo* gMediaDecoderLog;
#define DECODER_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
#else
#define DECODER_LOG(type, msg)
#endif
MediaOmxDecoder::MediaOmxDecoder() :
MediaDecoder()
MediaDecoder(),
mCanOffloadAudio(false),
mFallbackToStateMachine(false)
{
#ifdef PR_LOGGING
if (!gMediaDecoderLog) {
gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
}
#endif
}
MediaDecoder* MediaOmxDecoder::Clone()
@ -22,12 +44,205 @@ MediaDecoder* MediaOmxDecoder::Clone()
MediaDecoderStateMachine* MediaOmxDecoder::CreateStateMachine()
{
return new MediaDecoderStateMachine(this, new MediaOmxReader(this));
mReader = new MediaOmxReader(this);
mReader->SetAudioChannelType(GetAudioChannelType());
return new MediaDecoderStateMachine(this, mReader);
}
MediaOmxDecoder::~MediaOmxDecoder()
void MediaOmxDecoder::SetCanOffloadAudio(bool aCanOffloadAudio)
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mCanOffloadAudio = aCanOffloadAudio;
}
void MediaOmxDecoder::MetadataLoaded(int aChannels,
int aRate,
bool aHasAudio,
bool aHasVideo,
MetadataTags* aTags)
{
MOZ_ASSERT(NS_IsMainThread());
MediaDecoder::MetadataLoaded(aChannels, aRate, aHasAudio, aHasVideo, aTags);
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (!mCanOffloadAudio || mFallbackToStateMachine || mOutputStreams.Length() ||
mInitialPlaybackRate != 1.0) {
DECODER_LOG(PR_LOG_DEBUG, ("In %s Offload Audio check failed",
__PRETTY_FUNCTION__));
return;
}
#ifdef MOZ_AUDIO_OFFLOAD
mAudioOffloadPlayer = new AudioOffloadPlayer(this);
#endif
mAudioOffloadPlayer->SetSource(mReader->GetAudioOffloadTrack());
status_t err = mAudioOffloadPlayer->Start(false);
if (err == OK) {
PauseStateMachine();
// Call ChangeState() to run AudioOffloadPlayer since offload state enabled
ChangeState(mPlayState);
return;
}
mAudioOffloadPlayer = nullptr;
DECODER_LOG(PR_LOG_DEBUG, ("In %s Unable to start offload audio %d."
"Switching to normal mode", __PRETTY_FUNCTION__, err));
}
void MediaOmxDecoder::PauseStateMachine()
{
MOZ_ASSERT(NS_IsMainThread());
GetReentrantMonitor().AssertCurrentThreadIn();
DECODER_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__));
if (!mDecoderStateMachine) {
return;
}
StopProgress();
mDecoderStateMachine->SetDormant(true);
}
void MediaOmxDecoder::ResumeStateMachine()
{
MOZ_ASSERT(NS_IsMainThread());
GetReentrantMonitor().AssertCurrentThreadIn();
DECODER_LOG(PR_LOG_DEBUG, ("%s current time %f", __PRETTY_FUNCTION__,
mCurrentTime));
if (!mDecoderStateMachine) {
return;
}
mFallbackToStateMachine = true;
mAudioOffloadPlayer = nullptr;
mRequestedSeekTarget = SeekTarget(mCurrentTime, SeekTarget::Accurate);
mNextState = mPlayState;
ChangeState(PLAY_STATE_LOADING);
mDecoderStateMachine->SetDormant(false);
}
void MediaOmxDecoder::AudioOffloadTearDown()
{
MOZ_ASSERT(NS_IsMainThread());
PlaybackPositionChanged();
DECODER_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__));
{
// Audio offload player sent tear down event. Fallback to state machine
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
ResumeStateMachine();
}
}
void MediaOmxDecoder::AddOutputStream(ProcessedMediaStream* aStream,
bool aFinishWhenEnded)
{
MOZ_ASSERT(NS_IsMainThread());
PlaybackPositionChanged();
if (mAudioOffloadPlayer) {
// Offload player cannot handle MediaStream. Fallback
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
ResumeStateMachine();
}
MediaDecoder::AddOutputStream(aStream, aFinishWhenEnded);
}
void MediaOmxDecoder::SetPlaybackRate(double aPlaybackRate)
{
MOZ_ASSERT(NS_IsMainThread());
PlaybackPositionChanged();
if (mAudioOffloadPlayer &&
((aPlaybackRate != 0.0) || (aPlaybackRate != 1.0))) {
// Offload player cannot handle playback rate other than 1/0. Fallback
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
ResumeStateMachine();
}
MediaDecoder::SetPlaybackRate(aPlaybackRate);
}
void MediaOmxDecoder::ChangeState(PlayState aState)
{
MOZ_ASSERT(NS_IsMainThread());
// Keep MediaDecoder state in sync with MediaElement irrespective of offload
// playback so it will continue to work in normal mode when offloading fails
// in between
MediaDecoder::ChangeState(aState);
if (mAudioOffloadPlayer) {
mAudioOffloadPlayer->ChangeState(aState);
}
}
void MediaOmxDecoder::ApplyStateToStateMachine(PlayState aState)
{
MOZ_ASSERT(NS_IsMainThread());
// During offload playback, state machine should be in dormant state.
// ApplyStateToStateMachine() can change state machine state to
// something else or reset the seek time. So don't call this when audio is
// offloaded
if (!mAudioOffloadPlayer) {
MediaDecoder::ApplyStateToStateMachine(aState);
}
}
void MediaOmxDecoder::PlaybackPositionChanged()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mAudioOffloadPlayer) {
MediaDecoder::PlaybackPositionChanged();
return;
}
if (!mOwner || mShuttingDown) {
return;
}
double lastTime = mCurrentTime;
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mCurrentTime = mAudioOffloadPlayer->GetMediaTimeSecs();
}
if (mOwner && lastTime != mCurrentTime) {
FireTimeUpdate();
}
}
void MediaOmxDecoder::SetElementVisibility(bool aIsVisible)
{
MOZ_ASSERT(NS_IsMainThread());
if (mAudioOffloadPlayer) {
mAudioOffloadPlayer->SetElementVisibility(aIsVisible);
}
}
void MediaOmxDecoder::UpdateReadyStateForData()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mAudioOffloadPlayer) {
MediaDecoder::UpdateReadyStateForData();
return;
}
if (!mOwner || mShuttingDown)
return;
mOwner->UpdateReadyStateForData(mAudioOffloadPlayer->GetNextFrameStatus());
}
void MediaOmxDecoder::SetVolume(double aVolume)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mAudioOffloadPlayer) {
MediaDecoder::SetVolume(aVolume);
return;
}
mAudioOffloadPlayer->SetVolume(aVolume);
}
} // namespace mozilla

43
content/media/omx/MediaOmxDecoder.h Normal file → Executable file
Просмотреть файл

@ -8,17 +8,56 @@
#include "base/basictypes.h"
#include "MediaDecoder.h"
#include "MediaOmxReader.h"
#include "AudioOffloadPlayerBase.h"
namespace mozilla {
class MediaOmxDecoder : public MediaDecoder
{
typedef android::MediaSource MediaSource;
public:
MediaOmxDecoder();
~MediaOmxDecoder();
virtual MediaDecoder* Clone();
virtual MediaDecoderStateMachine* CreateStateMachine();
virtual void MetadataLoaded(int aChannels,
int aRate,
bool aHasAudio,
bool aHasVideo,
MetadataTags* aTags);
virtual void ChangeState(PlayState aState);
virtual void ApplyStateToStateMachine(PlayState aState);
virtual void SetVolume(double aVolume);
virtual void PlaybackPositionChanged();
virtual void UpdateReadyStateForData();
virtual void SetElementVisibility(bool aIsVisible);
virtual void SetCanOffloadAudio(bool aCanOffloadAudio);
virtual void AddOutputStream(ProcessedMediaStream* aStream,
bool aFinishWhenEnded);
virtual void SetPlaybackRate(double aPlaybackRate);
void AudioOffloadTearDown();
int64_t GetSeekTime() { return mRequestedSeekTarget.mTime; }
void ResetSeekTime() { mRequestedSeekTarget.Reset(); }
private:
void PauseStateMachine();
void ResumeStateMachine();
MediaOmxReader* mReader;
// Offloaded audio track
android::sp<MediaSource> mAudioTrack;
nsAutoPtr<AudioOffloadPlayerBase> mAudioOffloadPlayer;
// Set by MediaOmxReader to denote current track can be offloaded
bool mCanOffloadAudio;
// Set when offload playback of current track fails in the middle and need to
// fallback to state machine
bool mFallbackToStateMachine;
};
} // namespace mozilla

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

@ -17,6 +17,12 @@
#include "MPAPI.h"
#include "gfx2DGlue.h"
#ifdef MOZ_AUDIO_OFFLOAD
#include <stagefright/Utils.h>
#include <cutils/properties.h>
#include <stagefright/MetaData.h>
#endif
#define MAX_DROPPED_FRAMES 25
// Try not to spend more than this much time in a single call to DecodeVideoFrame.
#define MAX_VIDEO_DECODE_SECONDS 3.0
@ -26,14 +32,27 @@ using namespace android;
namespace mozilla {
#ifdef PR_LOGGING
extern PRLogModuleInfo* gMediaDecoderLog;
#define DECODER_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
#else
#define DECODER_LOG(type, msg)
#endif
MediaOmxReader::MediaOmxReader(AbstractMediaDecoder *aDecoder) :
MediaDecoderReader(aDecoder),
mHasVideo(false),
mHasAudio(false),
mVideoSeekTimeUs(-1),
mAudioSeekTimeUs(-1),
mSkipCount(0)
mSkipCount(0),
mAudioChannelType(dom::AUDIO_CHANNEL_DEFAULT)
{
#ifdef PR_LOGGING
if (!gMediaDecoderLog) {
gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
}
#endif
}
MediaOmxReader::~MediaOmxReader()
@ -124,6 +143,10 @@ nsresult MediaOmxReader::ReadMetadata(MediaInfo* aInfo,
return NS_ERROR_FAILURE;
}
#ifdef MOZ_AUDIO_OFFLOAD
CheckAudioOffload();
#endif
if (IsWaitingMediaResources()) {
return NS_OK;
}
@ -391,5 +414,41 @@ void MediaOmxReader::SetActive() {
NS_ASSERTION(result == NS_OK, "OmxDecoder should be in play state to continue decoding");
}
} // namespace mozilla
#ifdef MOZ_AUDIO_OFFLOAD
void MediaOmxReader::CheckAudioOffload()
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
char offloadProp[128];
property_get("audio.offload.disable", offloadProp, "0");
bool offloadDisable = atoi(offloadProp) != 0;
if (offloadDisable) {
return;
}
mAudioOffloadTrack = mOmxDecoder->GetAudioOffloadTrack();
sp<MetaData> meta = (mAudioOffloadTrack.get()) ?
mAudioOffloadTrack->getFormat() : nullptr;
// Supporting audio offload only when there is no video, no streaming
bool hasNoVideo = !mOmxDecoder->HasVideo();
bool isNotStreaming
= mDecoder->GetResource()->IsDataCachedToEndOfResource(0);
// Not much benefit in trying to offload other channel types. Most of them
// aren't supported and also duration would be less than a minute
bool isTypeMusic = mAudioChannelType == dom::AUDIO_CHANNEL_CONTENT;
DECODER_LOG(PR_LOG_DEBUG, ("%s meta %p, no video %d, no streaming %d,"
" channel type %d", __FUNCTION__, meta.get(), hasNoVideo,
isNotStreaming, mAudioChannelType));
if ((meta.get()) && hasNoVideo && isNotStreaming && isTypeMusic &&
canOffloadStream(meta, false, false, AUDIO_STREAM_MUSIC)) {
DECODER_LOG(PR_LOG_DEBUG, ("Can offload this audio stream"));
mDecoder->SetCanOffloadAudio(true);
}
}
#endif
} // namespace mozilla

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

@ -9,7 +9,9 @@
#include "MediaResource.h"
#include "MediaDecoderReader.h"
#include "nsRect.h"
#include "AudioChannelCommon.h"
#include <ui/GraphicBuffer.h>
#include <stagefright/MediaSource.h>
namespace android {
class OmxDecoder;
@ -34,10 +36,11 @@ class MediaOmxReader : public MediaDecoderReader
int64_t mVideoSeekTimeUs;
int64_t mAudioSeekTimeUs;
int32_t mSkipCount;
dom::AudioChannelType mAudioChannelType;
android::sp<android::MediaSource> mAudioOffloadTrack;
protected:
android::sp<android::OmxDecoder> mOmxDecoder;
android::sp<android::MediaExtractor> mExtractor;
// Called by ReadMetadata() during MediaDecoderStateMachine::DecodeMetadata()
@ -82,6 +85,21 @@ public:
virtual void SetIdle() MOZ_OVERRIDE;
virtual void SetActive() MOZ_OVERRIDE;
void SetAudioChannelType(dom::AudioChannelType aAudioChannelType) {
mAudioChannelType = aAudioChannelType;
}
android::sp<android::MediaSource> GetAudioOffloadTrack() {
return mAudioOffloadTrack;
}
#ifdef MOZ_AUDIO_OFFLOAD
// Check whether it is possible to offload current audio track. This access
// canOffloadStream() from libStageFright Utils.cpp, which is not there in
// ANDROID_VERSION < 19
void CheckAudioOffload();
#endif
};
} // namespace mozilla

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

@ -362,6 +362,12 @@ bool OmxDecoder::Init(sp<MediaExtractor>& extractor) {
if (audioTrackIndex != -1) {
mAudioTrack = extractor->getTrack(audioTrackIndex);
#ifdef MOZ_AUDIO_OFFLOAD
// mAudioTrack is be used by OMXCodec. For offloaded audio track, using same
// object gives undetermined behavior. So get a new track
mAudioOffloadTrack = extractor->getTrack(audioTrackIndex);
#endif
}
return true;
}

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

@ -83,6 +83,7 @@ class OmxDecoder : public OMXCodecProxy::EventListener {
sp<GonkNativeWindowClient> mNativeWindowClient;
sp<MediaSource> mVideoTrack;
sp<OMXCodecProxy> mVideoSource;
sp<MediaSource> mAudioOffloadTrack;
sp<MediaSource> mAudioTrack;
sp<MediaSource> mAudioSource;
int32_t mDisplayWidth;
@ -244,6 +245,8 @@ public:
int64_t ProcessCachedData(int64_t aOffset, bool aWaitForCompletion);
sp<MediaSource> GetAudioOffloadTrack() { return mAudioOffloadTrack; }
static void RecycleCallback(TextureClient* aClient, void* aClosure);
};

13
content/media/omx/moz.build Normal file → Executable file
Просмотреть файл

@ -5,6 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS += [
'AudioOffloadPlayerBase.h',
'MediaOmxDecoder.h',
'MediaOmxReader.h',
]
@ -16,6 +17,17 @@ SOURCES += [
'OmxDecoder.cpp',
]
if CONFIG['MOZ_AUDIO_OFFLOAD']:
EXPORTS += [
'AudioOffloadPlayer.h',
'AudioOutput.h',
'AudioSink.h',
]
SOURCES += [
'AudioOffloadPlayer.cpp',
'AudioOutput.cpp',
]
if CONFIG['MOZ_OMX_ENCODER']:
EXPORTS += [
'OMXCodecWrapper.h',
@ -57,6 +69,7 @@ CXXFLAGS += [
'frameworks/base/media/libstagefright/include',
'frameworks/native/opengl/include',
'frameworks/native/include',
'hardware/libhardware/include/',
]
]