Bug 1311214 - Remove b2g OMX encoder/decoder; r=cpearce

This commit is contained in:
Ehsan Akhgari 2016-10-18 18:10:07 -04:00
parent be86e8df54
commit d5d66e16c9
49 changed files with 1 additions and 8373 deletions

View File

@ -542,7 +542,6 @@ pref("dom.webapps.useCurrentProfile", true);
// Enable system message
pref("dom.sysmsg.enabled", true);
pref("media.plugins.enabled", false);
pref("media.omx.enabled", true);
pref("media.rtsp.enabled", true);
pref("media.rtsp.video.enabled", true);

View File

@ -24,12 +24,6 @@
#include "AndroidMediaReader.h"
#include "AndroidMediaPluginHost.h"
#endif
#ifdef MOZ_OMX_DECODER
#include "MediaOmxDecoder.h"
#include "MediaOmxReader.h"
#include "nsIPrincipal.h"
#include "mozilla/dom/HTMLMediaElement.h"
#endif
#ifdef MOZ_DIRECTSHOW
#include "DirectShowDecoder.h"
#include "DirectShowReader.h"
@ -146,89 +140,6 @@ IsHttpLiveStreamingType(const nsACString& aType)
return CodecListContains(gHttpLiveStreamingTypes, aType);
}
#ifdef MOZ_OMX_DECODER
static const char* const gOmxTypes[] = {
"audio/mpeg",
"audio/mp4",
"audio/amr",
"audio/3gpp",
"audio/flac",
"video/mp4",
"video/x-m4v",
"video/3gpp",
"video/3gpp2",
"video/quicktime",
#ifdef MOZ_OMX_WEBM_DECODER
"video/webm",
"audio/webm",
#endif
"audio/x-matroska",
"video/mp2t",
"video/avi",
"video/x-matroska",
nullptr
};
static const char* const gB2GOnlyTypes[] = {
"audio/3gpp",
"audio/amr",
"audio/x-matroska",
"video/mp2t",
"video/avi",
"video/x-matroska",
nullptr
};
static bool
IsOmxSupportedType(const nsACString& aType)
{
if (!MediaDecoder::IsOmxEnabled()) {
return false;
}
return CodecListContains(gOmxTypes, aType);
}
static bool
IsB2GSupportOnlyType(const nsACString& aType)
{
return CodecListContains(gB2GOnlyTypes, aType);
}
static char const *const gH264Codecs[9] = {
"avc1.42E01E", // H.264 Constrained Baseline Profile Level 3.0
"avc1.42001E", // H.264 Baseline Profile Level 3.0
"avc1.58A01E", // H.264 Extended Profile Level 3.0
"avc1.4D401E", // H.264 Main Profile Level 3.0
"avc1.64001E", // H.264 High Profile Level 3.0
"avc1.64001F", // H.264 High Profile Level 3.1
"mp4v.20.3", // 3GPP
"mp4a.40.2", // AAC-LC
nullptr
};
static char const *const gMpegAudioCodecs[2] = {
"mp3", // MP3
nullptr
};
#ifdef MOZ_OMX_WEBM_DECODER
static char const *const gOMXWebMCodecs[] = {
"vorbis",
"vp8",
"vp8.0",
// Since Android KK, VP9 SW decoder is supported.
// http://developer.android.com/guide/appendix/media-formats.html
#if ANDROID_VERSION > 18
"vp9",
"vp9.0",
#endif
nullptr
};
#endif //MOZ_OMX_WEBM_DECODER
#endif
#ifdef MOZ_ANDROID_OMX
static bool
IsAndroidMediaType(const nsACString& aType)
@ -283,11 +194,7 @@ static bool
IsMP3SupportedType(const nsACString& aType,
const nsAString& aCodecs = EmptyString())
{
#ifdef MOZ_OMX_DECODER
return false;
#else
return MP3Decoder::CanHandleMediaType(aType, aCodecs);
#endif
}
static bool
@ -364,20 +271,6 @@ CanHandleCodecsType(const MediaContentType& aType,
if (IsFlacSupportedType(aType.GetMIMEType(), aType.GetCodecs())) {
return CANPLAY_YES;
}
#ifdef MOZ_OMX_DECODER
if (IsOmxSupportedType(aType.GetMIMEType())) {
if (aType.GetMIMEType().EqualsASCII("audio/mpeg")) {
codecList = gMpegAudioCodecs;
#ifdef MOZ_OMX_WEBM_DECODER
} else if (aType.GetMIMEType().EqualsASCII("audio/webm") ||
aType.GetMIMEType().EqualsASCII("video/webm")) {
codecList = gOMXWebMCodecs;
#endif
} else {
codecList = gH264Codecs;
}
}
#endif
#ifdef MOZ_DIRECTSHOW
DirectShowDecoder::GetSupportedCodecs(aType.GetMIMEType(), &codecList);
#endif
@ -451,11 +344,6 @@ CanHandleMediaType(const MediaContentType& aType,
if (IsFlacSupportedType(aType.GetMIMEType())) {
return CANPLAY_MAYBE;
}
#ifdef MOZ_OMX_DECODER
if (IsOmxSupportedType(aType.GetMIMEType())) {
return CANPLAY_MAYBE;
}
#endif
#ifdef MOZ_DIRECTSHOW
if (DirectShowDecoder::GetSupportedCodecs(aType.GetMIMEType(), nullptr)) {
return CANPLAY_MAYBE;
@ -547,27 +435,6 @@ InstantiateDecoder(const nsACString& aType,
decoder = new FlacDecoder(aOwner);
return decoder.forget();
}
#ifdef MOZ_OMX_DECODER
if (IsOmxSupportedType(aType)) {
// we are discouraging Web and App developers from using those formats in
// gB2GOnlyTypes, thus we only allow them to be played on WebApps.
if (IsB2GSupportOnlyType(aType)) {
dom::HTMLMediaElement* element = aOwner->GetMediaElement();
if (!element) {
return nullptr;
}
nsIPrincipal* principal = element->NodePrincipal();
if (!principal) {
return nullptr;
}
if (principal->GetAppStatus() < nsIPrincipal::APP_STATUS_PRIVILEGED) {
return nullptr;
}
}
decoder = new MediaOmxDecoder(aOwner);
return decoder.forget();
}
#endif
#ifdef MOZ_ANDROID_OMX
if (MediaDecoder::IsAndroidMediaPluginEnabled() &&
EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
@ -642,11 +509,6 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac
if (IsWaveType(aType)) {
decoderReader = new WaveReader(aDecoder);
} else
#ifdef MOZ_OMX_DECODER
if (IsOmxSupportedType(aType)) {
decoderReader = new MediaOmxReader(aDecoder);
} else
#endif
#ifdef MOZ_ANDROID_OMX
if (MediaDecoder::IsAndroidMediaPluginEnabled() &&
EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
@ -680,13 +542,6 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType)
return
IsOggSupportedType(aType) ||
#ifdef MOZ_OMX_DECODER
// We support the formats in gB2GOnlyTypes only inside WebApps on firefoxOS
// but not in general web content. Ensure we dont create a VideoDocument
// when accessing those format URLs directly.
(IsOmxSupportedType(aType) &&
!IsB2GSupportOnlyType(aType)) ||
#endif
IsWebMSupportedType(aType) ||
#ifdef MOZ_ANDROID_OMX
(MediaDecoder::IsAndroidMediaPluginEnabled() && IsAndroidMediaType(aType)) ||

View File

@ -6,10 +6,6 @@
#include "MediaData.h"
#include "MediaInfo.h"
#ifdef MOZ_OMX_DECODER
#include "GrallocImages.h"
#include "mozilla/layers/TextureClient.h"
#endif
#include "VideoUtils.h"
#include "ImageContainer.h"
@ -384,52 +380,6 @@ VideoData::CreateFromImage(const VideoInfo& aInfo,
return v.forget();
}
#ifdef MOZ_OMX_DECODER
/* static */
already_AddRefed<VideoData>
VideoData::CreateAndCopyIntoTextureClient(const VideoInfo& aInfo,
int64_t aOffset,
int64_t aTime,
int64_t aDuration,
mozilla::layers::TextureClient* aBuffer,
bool aKeyframe,
int64_t aTimecode,
const IntRect& aPicture)
{
// The following situations could be triggered by invalid input
if (aPicture.width <= 0 || aPicture.height <= 0) {
NS_WARNING("Empty picture rect");
return nullptr;
}
// Ensure the picture size specified in the headers can be extracted out of
// the frame we've been supplied without indexing out of bounds.
CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height);
if (!xLimit.isValid() || !yLimit.isValid())
{
// The specified picture dimensions can't be contained inside the video
// frame, we'll stomp memory if we try to copy it. Fail.
NS_WARNING("Overflowing picture rect");
return nullptr;
}
RefPtr<VideoData> v(new VideoData(aOffset,
aTime,
aDuration,
aKeyframe,
aTimecode,
aInfo.mDisplay,
0));
RefPtr<layers::GrallocImage> image = new layers::GrallocImage();
image->AdoptData(aBuffer, aPicture.Size());
v->mImage = image;
return v.forget();
}
#endif // MOZ_OMX_DECODER
MediaRawData::MediaRawData()
: MediaData(RAW_DATA, 0)
, mCrypto(mCryptoInternal)

View File

@ -1736,14 +1736,6 @@ MediaDecoder::IsWebMEnabled()
return Preferences::GetBool("media.webm.enabled");
}
#ifdef MOZ_OMX_DECODER
bool
MediaDecoder::IsOmxEnabled()
{
return Preferences::GetBool("media.omx.enabled", false);
}
#endif
#ifdef MOZ_ANDROID_OMX
bool
MediaDecoder::IsAndroidMediaPluginEnabled()

View File

@ -456,10 +456,6 @@ private:
static bool IsWaveEnabled();
static bool IsWebMEnabled();
#ifdef MOZ_OMX_DECODER
static bool IsOmxEnabled();
#endif
#ifdef MOZ_ANDROID_OMX
static bool IsAndroidMediaPluginEnabled();
#endif

View File

@ -1328,17 +1328,6 @@ MediaRecorder::IsTypeSupported(const nsAString& aMIMEType)
codeclist = gWebMVideoEncoderCodecs;
}
#endif
#ifdef MOZ_OMX_ENCODER
// We're working on MP4 encoder support for desktop
else if (mimeType.EqualsLiteral(VIDEO_MP4) ||
mimeType.EqualsLiteral(AUDIO_3GPP) ||
mimeType.EqualsLiteral(AUDIO_3GPP2)) {
if (MediaEncoder::IsOMXEncoderEnabled()) {
// XXX check codecs for MP4/3GPP
return true;
}
}
#endif
// codecs don't matter if we don't support the container
if (!codeclist) {

View File

@ -19,10 +19,6 @@
#include "VP8TrackEncoder.h"
#include "WebMWriter.h"
#endif
#ifdef MOZ_OMX_ENCODER
#include "OmxTrackEncoder.h"
#include "ISOMediaWriter.h"
#endif
#ifdef LOG
#undef LOG
@ -180,37 +176,6 @@ MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint32_t aAudioBitrate,
mimeType = NS_LITERAL_STRING(VIDEO_WEBM);
}
#endif //MOZ_WEBM_ENCODER
#ifdef MOZ_OMX_ENCODER
else if (MediaEncoder::IsOMXEncoderEnabled() &&
(aMIMEType.EqualsLiteral(VIDEO_MP4) ||
(aTrackTypes & ContainerWriter::CREATE_VIDEO_TRACK))) {
if (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK) {
audioEncoder = new OmxAACAudioTrackEncoder();
NS_ENSURE_TRUE(audioEncoder, nullptr);
}
videoEncoder = new OmxVideoTrackEncoder(aTrackRate);
writer = new ISOMediaWriter(aTrackTypes);
NS_ENSURE_TRUE(writer, nullptr);
NS_ENSURE_TRUE(videoEncoder, nullptr);
mimeType = NS_LITERAL_STRING(VIDEO_MP4);
} else if (MediaEncoder::IsOMXEncoderEnabled() &&
(aMIMEType.EqualsLiteral(AUDIO_3GPP))) {
audioEncoder = new OmxAMRAudioTrackEncoder();
NS_ENSURE_TRUE(audioEncoder, nullptr);
writer = new ISOMediaWriter(aTrackTypes, ISOMediaWriter::TYPE_FRAG_3GP);
NS_ENSURE_TRUE(writer, nullptr);
mimeType = NS_LITERAL_STRING(AUDIO_3GPP);
} else if (MediaEncoder::IsOMXEncoderEnabled() &&
(aMIMEType.EqualsLiteral(AUDIO_3GPP2))) {
audioEncoder = new OmxEVRCAudioTrackEncoder();
NS_ENSURE_TRUE(audioEncoder, nullptr);
writer = new ISOMediaWriter(aTrackTypes, ISOMediaWriter::TYPE_FRAG_3G2);
NS_ENSURE_TRUE(writer, nullptr);
mimeType = NS_LITERAL_STRING(AUDIO_3GPP2) ;
}
#endif // MOZ_OMX_ENCODER
else if (MediaDecoder::IsOggEnabled() && MediaDecoder::IsOpusEnabled() &&
(aMIMEType.EqualsLiteral(AUDIO_OGG) ||
(aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK))) {
@ -419,14 +384,6 @@ MediaEncoder::IsWebMEncoderEnabled()
}
#endif
#ifdef MOZ_OMX_ENCODER
bool
MediaEncoder::IsOMXEncoderEnabled()
{
return Preferences::GetBool("media.encoder.omx.enabled");
}
#endif
/*
* SizeOfExcludingThis measures memory being used by the Media Encoder.
* Currently it measures the size of the Encoder buffer and memory occupied

View File

@ -217,10 +217,6 @@ public :
static bool IsWebMEncoderEnabled();
#endif
#ifdef MOZ_OMX_ENCODER
static bool IsOMXEncoderEnabled();
#endif
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
/*
* Measure the size of the buffer, and memory occupied by mAudioEncoder

View File

@ -1,384 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* 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 "OmxTrackEncoder.h"
#include "OMXCodecWrapper.h"
#include "VideoUtils.h"
#include "ISOTrackMetadata.h"
#include "GeckoProfiler.h"
#ifdef MOZ_WIDGET_GONK
#include <android/log.h>
#define OMX_LOG(args...) \
do { \
__android_log_print(ANDROID_LOG_INFO, "OmxTrackEncoder", ##args); \
} while (0)
#else
#define OMX_LOG(args, ...)
#endif
using namespace android;
namespace mozilla {
#define ENCODER_CONFIG_FRAME_RATE 30 // fps
#define GET_ENCODED_VIDEO_FRAME_TIMEOUT 100000 // microseconds
OmxVideoTrackEncoder::OmxVideoTrackEncoder(TrackRate aTrackRate)
: VideoTrackEncoder(aTrackRate)
{}
OmxVideoTrackEncoder::~OmxVideoTrackEncoder()
{}
nsresult
OmxVideoTrackEncoder::Init(int aWidth, int aHeight, int aDisplayWidth,
int aDisplayHeight)
{
mFrameWidth = aWidth;
mFrameHeight = aHeight;
mDisplayWidth = aDisplayWidth;
mDisplayHeight = aDisplayHeight;
mEncoder = OMXCodecWrapper::CreateAVCEncoder();
NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
nsresult rv = mEncoder->Configure(mFrameWidth, mFrameHeight,
ENCODER_CONFIG_FRAME_RATE);
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mInitialized = (rv == NS_OK);
mReentrantMonitor.NotifyAll();
return rv;
}
already_AddRefed<TrackMetadataBase>
OmxVideoTrackEncoder::GetMetadata()
{
PROFILER_LABEL("OmxVideoTrackEncoder", "GetMetadata",
js::ProfileEntry::Category::OTHER);
{
// Wait if mEncoder is not initialized nor is being canceled.
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
while (!mCanceled && !mInitialized) {
mReentrantMonitor.Wait();
}
}
if (mCanceled || mEncodingComplete) {
return nullptr;
}
RefPtr<AVCTrackMetadata> meta = new AVCTrackMetadata();
meta->mWidth = mFrameWidth;
meta->mHeight = mFrameHeight;
meta->mDisplayWidth = mDisplayWidth;
meta->mDisplayHeight = mDisplayHeight;
meta->mFrameRate = ENCODER_CONFIG_FRAME_RATE;
return meta.forget();
}
nsresult
OmxVideoTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
{
PROFILER_LABEL("OmxVideoTrackEncoder", "GetEncodedTrack",
js::ProfileEntry::Category::OTHER);
VideoSegment segment;
{
// Move all the samples from mRawSegment to segment. We only hold the
// monitor in this block.
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
// Wait if mEncoder is not initialized nor is being canceled.
while (!mCanceled && (!mInitialized ||
(mRawSegment.GetDuration() == 0 && !mEndOfStream))) {
mReentrantMonitor.Wait();
}
if (mCanceled || mEncodingComplete) {
return NS_ERROR_FAILURE;
}
segment.AppendFrom(&mRawSegment);
}
nsresult rv;
// Start queuing raw frames to the input buffers of OMXCodecWrapper.
VideoSegment::ChunkIterator iter(segment);
while (!iter.IsEnded()) {
VideoChunk chunk = *iter;
uint64_t totalDurationUs = mTotalFrameDuration * USECS_PER_S / mTrackRate;
layers::Image* img = (chunk.IsNull() || chunk.mFrame.GetForceBlack()) ?
nullptr : chunk.mFrame.GetImage();
rv = mEncoder->Encode(img, mFrameWidth, mFrameHeight, totalDurationUs);
NS_ENSURE_SUCCESS(rv, rv);
mTotalFrameDuration += chunk.GetDuration();
iter.Next();
}
// Send the EOS signal to OMXCodecWrapper.
if (mEndOfStream && iter.IsEnded() && !mEosSetInEncoder) {
uint64_t totalDurationUs = mTotalFrameDuration * USECS_PER_S / mTrackRate;
layers::Image* img = (!mLastFrame.GetImage() || mLastFrame.GetForceBlack())
? nullptr : mLastFrame.GetImage();
rv = mEncoder->Encode(img, mFrameWidth, mFrameHeight, totalDurationUs,
OMXCodecWrapper::BUFFER_EOS, &mEosSetInEncoder);
NS_ENSURE_SUCCESS(rv, rv);
}
// Dequeue an encoded frame from the output buffers of OMXCodecWrapper.
nsTArray<uint8_t> buffer;
int outFlags = 0;
int64_t outTimeStampUs = 0;
rv = mEncoder->GetNextEncodedFrame(&buffer, &outTimeStampUs, &outFlags,
GET_ENCODED_VIDEO_FRAME_TIMEOUT);
NS_ENSURE_SUCCESS(rv, rv);
if (!buffer.IsEmpty()) {
RefPtr<EncodedFrame> videoData = new EncodedFrame();
if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) {
videoData->SetFrameType(EncodedFrame::AVC_CSD);
} else {
videoData->SetFrameType((outFlags & OMXCodecWrapper::BUFFER_SYNC_FRAME) ?
EncodedFrame::AVC_I_FRAME : EncodedFrame::AVC_P_FRAME);
}
videoData->SwapInFrameData(buffer);
videoData->SetTimeStamp(outTimeStampUs);
aData.AppendEncodedFrame(videoData);
}
if (outFlags & OMXCodecWrapper::BUFFER_EOS) {
mEncodingComplete = true;
OMX_LOG("Done encoding video.");
}
return NS_OK;
}
OmxAudioTrackEncoder::OmxAudioTrackEncoder()
: AudioTrackEncoder()
{}
OmxAudioTrackEncoder::~OmxAudioTrackEncoder()
{}
nsresult
OmxAudioTrackEncoder::AppendEncodedFrames(EncodedFrameContainer& aContainer)
{
nsTArray<uint8_t> frameData;
int outFlags = 0;
int64_t outTimeUs = -1;
nsresult rv = mEncoder->GetNextEncodedFrame(&frameData, &outTimeUs, &outFlags,
3000); // wait up to 3ms
NS_ENSURE_SUCCESS(rv, rv);
if (!frameData.IsEmpty() || outFlags & OMXCodecWrapper::BUFFER_EOS) { // Some hw codec may send out EOS with an empty frame
bool isCSD = false;
if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) { // codec specific data
isCSD = true;
} else if (outFlags & OMXCodecWrapper::BUFFER_EOS) { // last frame
mEncodingComplete = true;
}
RefPtr<EncodedFrame> audiodata = new EncodedFrame();
if (mEncoder->GetCodecType() == OMXCodecWrapper::AAC_ENC) {
audiodata->SetFrameType(isCSD ?
EncodedFrame::AAC_CSD : EncodedFrame::AAC_AUDIO_FRAME);
} else if (mEncoder->GetCodecType() == OMXCodecWrapper::AMR_NB_ENC){
audiodata->SetFrameType(isCSD ?
EncodedFrame::AMR_AUDIO_CSD : EncodedFrame::AMR_AUDIO_FRAME);
} else if (mEncoder->GetCodecType() == OMXCodecWrapper::EVRC_ENC){
audiodata->SetFrameType(isCSD ?
EncodedFrame::EVRC_AUDIO_CSD : EncodedFrame::EVRC_AUDIO_FRAME);
} else {
MOZ_ASSERT(false, "audio codec not supported");
}
audiodata->SetTimeStamp(outTimeUs);
audiodata->SwapInFrameData(frameData);
aContainer.AppendEncodedFrame(audiodata);
}
return NS_OK;
}
nsresult
OmxAudioTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
{
PROFILER_LABEL("OmxAACAudioTrackEncoder", "GetEncodedTrack",
js::ProfileEntry::Category::OTHER);
AudioSegment segment;
bool EOS;
// Move all the samples from mRawSegment to segment. We only hold
// the monitor in this block.
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
// Wait if mEncoder is not initialized nor canceled.
while (!mInitialized && !mCanceled) {
mReentrantMonitor.Wait();
}
if (mCanceled || mEncodingComplete) {
return NS_ERROR_FAILURE;
}
segment.AppendFrom(&mRawSegment);
EOS = mEndOfStream;
}
nsresult rv;
if (segment.GetDuration() == 0) {
// Notify EOS at least once, even if segment is empty.
if (EOS && !mEosSetInEncoder) {
rv = mEncoder->Encode(segment, OMXCodecWrapper::BUFFER_EOS,
&mEosSetInEncoder);
NS_ENSURE_SUCCESS(rv, rv);
}
// Nothing to encode but encoder could still have encoded data for earlier
// input.
return AppendEncodedFrames(aData);
}
// OMX encoder has limited input buffers only so we have to feed input and get
// output more than once if there are too many samples pending in segment.
while (segment.GetDuration() > 0) {
rv = mEncoder->Encode(segment, 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = AppendEncodedFrames(aData);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
nsresult
OmxAACAudioTrackEncoder::Init(int aChannels, int aSamplingRate)
{
mChannels = aChannels;
mSamplingRate = aSamplingRate;
mEncoder = OMXCodecWrapper::CreateAACEncoder();
NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
nsresult rv = mEncoder->Configure(mChannels, mSamplingRate, mSamplingRate);
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mInitialized = (rv == NS_OK);
mReentrantMonitor.NotifyAll();
return NS_OK;
}
already_AddRefed<TrackMetadataBase>
OmxAACAudioTrackEncoder::GetMetadata()
{
PROFILER_LABEL("OmxAACAudioTrackEncoder", "GetMetadata",
js::ProfileEntry::Category::OTHER);
{
// Wait if mEncoder is not initialized nor is being canceled.
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
while (!mCanceled && !mInitialized) {
mReentrantMonitor.Wait();
}
}
if (mCanceled || mEncodingComplete) {
return nullptr;
}
RefPtr<AACTrackMetadata> meta = new AACTrackMetadata();
meta->mChannels = mChannels;
meta->mSampleRate = mSamplingRate;
meta->mFrameSize = OMXCodecWrapper::kAACFrameSize;
meta->mFrameDuration = OMXCodecWrapper::kAACFrameDuration;
return meta.forget();
}
nsresult
OmxAMRAudioTrackEncoder::Init(int aChannels, int aSamplingRate)
{
mChannels = aChannels;
mSamplingRate = aSamplingRate;
mEncoder = OMXCodecWrapper::CreateAMRNBEncoder();
NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
nsresult rv = mEncoder->Configure(mChannels, mSamplingRate, AMR_NB_SAMPLERATE);
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mInitialized = (rv == NS_OK);
mReentrantMonitor.NotifyAll();
return NS_OK;
}
already_AddRefed<TrackMetadataBase>
OmxAMRAudioTrackEncoder::GetMetadata()
{
PROFILER_LABEL("OmxAMRAudioTrackEncoder", "GetMetadata",
js::ProfileEntry::Category::OTHER);
{
// Wait if mEncoder is not initialized nor is being canceled.
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
while (!mCanceled && !mInitialized) {
mReentrantMonitor.Wait();
}
}
if (mCanceled || mEncodingComplete) {
return nullptr;
}
RefPtr<AMRTrackMetadata> meta = new AMRTrackMetadata();
return meta.forget();
}
nsresult
OmxEVRCAudioTrackEncoder::Init(int aChannels, int aSamplingRate)
{
mChannels = aChannels;
mSamplingRate = aSamplingRate;
mEncoder = OMXCodecWrapper::CreateEVRCEncoder();
NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
nsresult rv = mEncoder->Configure(mChannels, mSamplingRate, EVRC_SAMPLERATE);
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mInitialized = (rv == NS_OK);
mReentrantMonitor.NotifyAll();
return NS_OK;
}
already_AddRefed<TrackMetadataBase>
OmxEVRCAudioTrackEncoder::GetMetadata()
{
PROFILER_LABEL("OmxEVRCAudioTrackEncoder", "GetMetadata",
js::ProfileEntry::Category::OTHER);
{
// Wait if mEncoder is not initialized nor is being canceled.
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
while (!mCanceled && !mInitialized) {
mReentrantMonitor.Wait();
}
}
if (mCanceled || mEncodingComplete) {
return nullptr;
}
RefPtr<EVRCTrackMetadata> meta = new EVRCTrackMetadata();
meta->mChannels = mChannels;
return meta.forget();
}
}

View File

@ -1,110 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* 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 OmxTrackEncoder_h_
#define OmxTrackEncoder_h_
#include "nsAutoPtr.h"
#include "TrackEncoder.h"
namespace android {
class OMXVideoEncoder;
class OMXAudioEncoder;
}
/**
* There are two major classes defined in file OmxTrackEncoder;
* OmxVideoTrackEncoder and OmxAudioTrackEncoder, the video and audio track
* encoder for media type AVC/H.264 and AAC. OMXCodecWrapper wraps and controls
* an instance of MediaCodec, defined in libstagefright, runs on Android Jelly
* Bean platform.
*/
namespace mozilla {
class OmxVideoTrackEncoder: public VideoTrackEncoder
{
public:
explicit OmxVideoTrackEncoder(TrackRate aTrackRate);
~OmxVideoTrackEncoder();
already_AddRefed<TrackMetadataBase> GetMetadata() override;
nsresult GetEncodedTrack(EncodedFrameContainer& aData) override;
protected:
nsresult Init(int aWidth, int aHeight,
int aDisplayWidth, int aDisplayHeight) override;
private:
nsAutoPtr<android::OMXVideoEncoder> mEncoder;
};
class OmxAudioTrackEncoder : public AudioTrackEncoder
{
public:
OmxAudioTrackEncoder();
~OmxAudioTrackEncoder();
already_AddRefed<TrackMetadataBase> GetMetadata() = 0;
nsresult GetEncodedTrack(EncodedFrameContainer& aData) override;
protected:
nsresult Init(int aChannels, int aSamplingRate) = 0;
// Append encoded frames to aContainer.
nsresult AppendEncodedFrames(EncodedFrameContainer& aContainer);
nsAutoPtr<android::OMXAudioEncoder> mEncoder;
};
class OmxAACAudioTrackEncoder final : public OmxAudioTrackEncoder
{
public:
OmxAACAudioTrackEncoder()
: OmxAudioTrackEncoder()
{}
already_AddRefed<TrackMetadataBase> GetMetadata() override;
protected:
nsresult Init(int aChannels, int aSamplingRate) override;
};
class OmxAMRAudioTrackEncoder final : public OmxAudioTrackEncoder
{
public:
OmxAMRAudioTrackEncoder()
: OmxAudioTrackEncoder()
{}
enum {
AMR_NB_SAMPLERATE = 8000,
};
already_AddRefed<TrackMetadataBase> GetMetadata() override;
protected:
nsresult Init(int aChannels, int aSamplingRate) override;
};
class OmxEVRCAudioTrackEncoder final : public OmxAudioTrackEncoder
{
public:
OmxEVRCAudioTrackEncoder()
: OmxAudioTrackEncoder()
{}
enum {
EVRC_SAMPLERATE = 8000,
};
already_AddRefed<TrackMetadataBase> GetMetadata() override;
protected:
nsresult Init(int aChannels, int aSamplingRate) override;
};
}
#endif

View File

@ -25,10 +25,6 @@ UNIFIED_SOURCES += [
'TrackEncoder.cpp',
]
if CONFIG['MOZ_OMX_ENCODER']:
EXPORTS += ['OmxTrackEncoder.h']
UNIFIED_SOURCES += ['OmxTrackEncoder.cpp']
if CONFIG['MOZ_WEBM_ENCODER']:
EXPORTS += ['VP8TrackEncoder.h',
]

View File

@ -54,9 +54,6 @@ if CONFIG['MOZ_FMP4']:
if CONFIG['MOZ_WEBRTC']:
DIRS += ['bridge']
if CONFIG['MOZ_OMX_DECODER']:
DIRS += ['omx']
TEST_DIRS += [
'compiledtest',
'gtest',
@ -323,9 +320,6 @@ if CONFIG['OS_TARGET'] == 'WINNT':
else:
DEFINES['WEBRTC_POSIX'] = True
if CONFIG['MOZ_OMX_DECODER']:
DEFINES['MOZ_OMX_DECODER'] = True
if CONFIG['ANDROID_VERSION'] > '15':
DEFINES['MOZ_OMX_WEBM_DECODER'] = True

View File

@ -1,758 +0,0 @@
/* -*- 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.
*/
#include "AudioOffloadPlayer.h"
#include "nsComponentManagerUtils.h"
#include "nsITimer.h"
#include "MediaOmxCommonDecoder.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "VideoUtils.h"
#include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/WakeLock.h"
#include <binder/IPCThreadState.h>
#include <stagefright/foundation/ADebug.h>
#include <stagefright/foundation/ALooper.h>
#include <stagefright/MediaDefs.h>
#include <stagefright/MediaErrors.h>
#include <stagefright/MediaSource.h>
#include <stagefright/MetaData.h>
#include <stagefright/Utils.h>
#include <AudioTrack.h>
#include <AudioSystem.h>
#include <AudioParameter.h>
#include <hardware/audio.h>
using namespace android;
namespace mozilla {
LazyLogModule gAudioOffloadPlayerLog("AudioOffloadPlayer");
#define AUDIO_OFFLOAD_LOG(type, msg) \
MOZ_LOG(gAudioOffloadPlayerLog, type, msg)
// maximum time in paused state when offloading audio decompression.
// When elapsed, the GonkAudioSink is destroyed to allow the audio DSP to power down.
static const uint64_t OFFLOAD_PAUSE_MAX_MSECS = 60000ll;
AudioOffloadPlayer::AudioOffloadPlayer(MediaOmxCommonDecoder* aObserver) :
mStarted(false),
mPlaying(false),
mReachedEOS(false),
mIsElementVisible(true),
mSampleRate(0),
mStartPosUs(0),
mPositionTimeMediaUs(-1),
mInputBuffer(nullptr)
{
MOZ_ASSERT(NS_IsMainThread());
CHECK(aObserver);
#if ANDROID_VERSION >= 21
mSessionId = AudioSystem::newAudioUniqueId();
AudioSystem::acquireAudioSessionId(mSessionId, -1);
#else
mSessionId = AudioSystem::newAudioSessionId();
AudioSystem::acquireAudioSessionId(mSessionId);
#endif
mAudioSink = new AudioOutput(mSessionId,
IPCThreadState::self()->getCallingUid());
nsCOMPtr<nsIThread> thread;
MOZ_ALWAYS_SUCCEEDS(NS_GetMainThread(getter_AddRefs(thread)));
mPositionChanged = mOnPositionChanged.Connect(
thread, aObserver, &MediaOmxCommonDecoder::NotifyOffloadPlayerPositionChanged);
mPlaybackEnded = mOnPlaybackEnded.Connect(
thread, aObserver, &MediaDecoder::PlaybackEnded);
mPlayerTearDown = mOnPlayerTearDown.Connect(
thread, aObserver, &MediaOmxCommonDecoder::AudioOffloadTearDown);
mSeekingStarted = mOnSeekingStarted.Connect(
thread, aObserver, &MediaDecoder::SeekingStarted);
}
AudioOffloadPlayer::~AudioOffloadPlayer()
{
Reset();
#if ANDROID_VERSION >= 21
AudioSystem::releaseAudioSessionId(mSessionId, -1);
#else
AudioSystem::releaseAudioSessionId(mSessionId);
#endif
// Disconnect the listeners to prevent notifications from reaching
// the MediaOmxCommonDecoder object after shutdown.
mPositionChanged.Disconnect();
mPlaybackEnded.Disconnect();
mPlayerTearDown.Disconnect();
mSeekingStarted.Disconnect();
}
void AudioOffloadPlayer::SetSource(const sp<MediaSource> &aSource)
{
MOZ_ASSERT(NS_IsMainThread());
CHECK(!mSource.get());
mSource = aSource;
}
status_t AudioOffloadPlayer::Start(bool aSourceAlreadyStarted)
{
MOZ_ASSERT(NS_IsMainThread());
CHECK(!mStarted);
CHECK(mSource.get());
status_t err;
CHECK(mAudioSink.get());
if (!aSourceAlreadyStarted) {
err = mSource->start();
if (err != OK) {
return err;
}
}
sp<MetaData> format = mSource->getFormat();
const char* mime;
int avgBitRate = -1;
int32_t channelMask;
int32_t numChannels;
int64_t durationUs = -1;
audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT;
uint32_t flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER;
CHECK(format->findCString(kKeyMIMEType, &mime));
CHECK(format->findInt32(kKeySampleRate, &mSampleRate));
CHECK(format->findInt32(kKeyChannelCount, &numChannels));
format->findInt32(kKeyBitRate, &avgBitRate);
format->findInt64(kKeyDuration, &durationUs);
if(!format->findInt32(kKeyChannelMask, &channelMask)) {
channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER;
}
if (mapMimeToAudioFormat(audioFormat, mime) != OK) {
AUDIO_OFFLOAD_LOG(LogLevel::Error, ("Couldn't map mime type \"%s\" to a valid "
"AudioSystem::audio_format", mime));
audioFormat = AUDIO_FORMAT_INVALID;
}
offloadInfo.duration_us = durationUs;
offloadInfo.sample_rate = mSampleRate;
offloadInfo.channel_mask = channelMask;
offloadInfo.format = audioFormat;
offloadInfo.stream_type = AUDIO_STREAM_MUSIC;
offloadInfo.bit_rate = avgBitRate;
offloadInfo.has_video = false;
offloadInfo.is_streaming = false;
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("isOffloadSupported: SR=%u, CM=0x%x, "
"Format=0x%x, StreamType=%d, BitRate=%u, duration=%lld us, has_video=%d",
offloadInfo.sample_rate, offloadInfo.channel_mask, offloadInfo.format,
offloadInfo.stream_type, offloadInfo.bit_rate, offloadInfo.duration_us,
offloadInfo.has_video));
err = mAudioSink->Open(mSampleRate,
numChannels,
channelMask,
audioFormat,
&AudioOffloadPlayer::AudioSinkCallback,
this,
(audio_output_flags_t) flags,
&offloadInfo);
if (err == OK) {
// If the playback is offloaded to h/w we pass the
// HAL some metadata information
// We don't want to do this for PCM because it will be going
// through the AudioFlinger mixer before reaching the hardware
SendMetaDataToHal(mAudioSink, format);
}
mStarted = true;
mPlaying = false;
return err;
}
status_t AudioOffloadPlayer::ChangeState(MediaDecoder::PlayState aState)
{
MOZ_ASSERT(NS_IsMainThread());
mPlayState = aState;
switch (mPlayState) {
case MediaDecoder::PLAY_STATE_PLAYING: {
status_t err = Play();
if (err != OK) {
return err;
}
StartTimeUpdate();
} break;
case MediaDecoder::PLAY_STATE_PAUSED:
case MediaDecoder::PLAY_STATE_SHUTDOWN:
// Just pause here during play state shutdown as well to stop playing
// offload track immediately. Resources will be freed by
// MediaOmxCommonDecoder
Pause();
break;
case MediaDecoder::PLAY_STATE_ENDED:
Pause(true);
break;
default:
break;
}
return OK;
}
static void ResetCallback(nsITimer* aTimer, void* aClosure)
{
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("%s", __FUNCTION__));
AudioOffloadPlayer* player = static_cast<AudioOffloadPlayer*>(aClosure);
if (player) {
player->Reset();
}
}
void AudioOffloadPlayer::Pause(bool aPlayPendingSamples)
{
MOZ_ASSERT(NS_IsMainThread());
if (mStarted) {
CHECK(mAudioSink.get());
WakeLockCreate();
if (aPlayPendingSamples) {
mAudioSink->Stop();
} else {
mAudioSink->Pause();
}
mPlaying = false;
}
if (mResetTimer) {
return;
}
mResetTimer = do_CreateInstance("@mozilla.org/timer;1");
mResetTimer->InitWithFuncCallback(ResetCallback,
this,
OFFLOAD_PAUSE_MAX_MSECS,
nsITimer::TYPE_ONE_SHOT);
}
status_t AudioOffloadPlayer::Play()
{
MOZ_ASSERT(NS_IsMainThread());
if (mResetTimer) {
mResetTimer->Cancel();
mResetTimer = nullptr;
WakeLockRelease();
}
status_t err = OK;
if (!mStarted) {
// Last pause timed out and offloaded audio sink was reset. Start it again
err = Start(false);
if (err != OK) {
return err;
}
// Seek to last play position only when there was no seek during last pause
android::Mutex::Autolock autoLock(mLock);
if (!mSeekTarget.IsValid()) {
mSeekTarget = SeekTarget(mPositionTimeMediaUs,
SeekTarget::Accurate,
MediaDecoderEventVisibility::Suppressed);
DoSeek();
}
}
if (!mPlaying) {
CHECK(mAudioSink.get());
err = mAudioSink->Start();
if (err == OK) {
mPlaying = true;
}
}
return err;
}
void AudioOffloadPlayer::Reset()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mStarted) {
return;
}
CHECK(mAudioSink.get());
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("reset: mPlaying=%d mReachedEOS=%d",
mPlaying, mReachedEOS));
mAudioSink->Stop();
// If we're closing and have reached EOS, we don't want to flush
// the track because if it is offloaded there could be a small
// amount of residual data in the hardware buffer which we must
// play to give gapless playback.
// But if we're resetting when paused or before we've reached EOS
// we can't be doing a gapless playback and there could be a large
// amount of data queued in the hardware if the track is offloaded,
// so we must flush to prevent a track switch being delayed playing
// the buffered data that we don't want now
if (!mPlaying || !mReachedEOS) {
mAudioSink->Flush();
}
mAudioSink->Close();
// Make sure to release any buffer we hold onto so that the
// source is able to stop().
if (mInputBuffer) {
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("Releasing input buffer"));
mInputBuffer->release();
mInputBuffer = nullptr;
}
mSource->stop();
IPCThreadState::self()->flushCommands();
StopTimeUpdate();
mReachedEOS = false;
mStarted = false;
mPlaying = false;
mStartPosUs = 0;
WakeLockRelease();
}
RefPtr<MediaDecoder::SeekPromise> AudioOffloadPlayer::Seek(SeekTarget aTarget)
{
MOZ_ASSERT(NS_IsMainThread());
android::Mutex::Autolock autoLock(mLock);
mSeekPromise.RejectIfExists(true, __func__);
mSeekTarget = aTarget;
RefPtr<MediaDecoder::SeekPromise> p = mSeekPromise.Ensure(__func__);
DoSeek();
return p;
}
status_t AudioOffloadPlayer::DoSeek()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mSeekTarget.IsValid());
CHECK(mAudioSink.get());
AUDIO_OFFLOAD_LOG(LogLevel::Debug,
("DoSeek ( %lld )", mSeekTarget.GetTime().ToMicroseconds()));
mReachedEOS = false;
mPositionTimeMediaUs = -1;
mStartPosUs = mSeekTarget.GetTime().ToMicroseconds();
if (!mSeekPromise.IsEmpty() &&
mSeekTarget.mEventVisibility == MediaDecoderEventVisibility::Observable) {
mOnSeekingStarted.Notify();
}
if (mPlaying) {
mAudioSink->Pause();
mAudioSink->Flush();
mAudioSink->Start();
} else {
if (mStarted) {
mAudioSink->Flush();
}
if (!mSeekPromise.IsEmpty()) {
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("Fake seek complete during pause"));
// We do not reset mSeekTarget here.
MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
mSeekPromise.Resolve(val, __func__);
}
}
return OK;
}
int64_t AudioOffloadPlayer::GetMediaTimeUs()
{
android::Mutex::Autolock autoLock(mLock);
int64_t playPosition = 0;
if (mSeekTarget.IsValid()) {
return mSeekTarget.GetTime().ToMicroseconds();
}
if (!mStarted) {
return mPositionTimeMediaUs;
}
playPosition = GetOutputPlayPositionUs_l();
if (!mReachedEOS) {
mPositionTimeMediaUs = playPosition;
}
return mPositionTimeMediaUs;
}
int64_t AudioOffloadPlayer::GetOutputPlayPositionUs_l() const
{
CHECK(mAudioSink.get());
uint32_t playedSamples = 0;
mAudioSink->GetPosition(&playedSamples);
const int64_t playedUs = (static_cast<int64_t>(playedSamples) * 1000000 ) /
mSampleRate;
// HAL position is relative to the first buffer we sent at mStartPosUs
const int64_t renderedDuration = mStartPosUs + playedUs;
return renderedDuration;
}
void AudioOffloadPlayer::NotifyAudioEOS()
{
android::Mutex::Autolock autoLock(mLock);
// We do not reset mSeekTarget here.
if (!mSeekPromise.IsEmpty()) {
MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
mSeekPromise.Resolve(val, __func__);
}
mOnPlaybackEnded.Notify();
}
void AudioOffloadPlayer::NotifyPositionChanged()
{
mOnPositionChanged.Notify();
}
void AudioOffloadPlayer::NotifyAudioTearDown()
{
// Fallback to state machine.
// state machine's seeks will be done with
// MediaDecoderEventVisibility::Suppressed.
android::Mutex::Autolock autoLock(mLock);
// We do not reset mSeekTarget here.
if (!mSeekPromise.IsEmpty()) {
MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
mSeekPromise.Resolve(val, __func__);
}
mOnPlayerTearDown.Notify();
}
// static
size_t AudioOffloadPlayer::AudioSinkCallback(GonkAudioSink* aAudioSink,
void* aBuffer,
size_t aSize,
void* aCookie,
GonkAudioSink::cb_event_t aEvent)
{
AudioOffloadPlayer* me = (AudioOffloadPlayer*) aCookie;
switch (aEvent) {
case GonkAudioSink::CB_EVENT_FILL_BUFFER:
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("Notify Audio position changed"));
me->NotifyPositionChanged();
return me->FillBuffer(aBuffer, aSize);
case GonkAudioSink::CB_EVENT_STREAM_END:
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("Notify Audio EOS"));
me->mReachedEOS = true;
me->NotifyAudioEOS();
break;
case GonkAudioSink::CB_EVENT_TEAR_DOWN:
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("Notify Tear down event"));
me->NotifyAudioTearDown();
break;
default:
AUDIO_OFFLOAD_LOG(LogLevel::Error, ("Unknown event %d from audio sink",
aEvent));
break;
}
return 0;
}
size_t AudioOffloadPlayer::FillBuffer(void* aData, size_t aSize)
{
CHECK(mAudioSink.get());
if (mReachedEOS) {
return 0;
}
size_t sizeDone = 0;
size_t sizeRemaining = aSize;
int64_t seekTimeUs = -1;
while (sizeRemaining > 0) {
MediaSource::ReadOptions options;
bool refreshSeekTime = false;
{
android::Mutex::Autolock autoLock(mLock);
if (mSeekTarget.IsValid()) {
seekTimeUs = mSeekTarget.GetTime().ToMicroseconds();
options.setSeekTo(seekTimeUs);
refreshSeekTime = true;
if (mInputBuffer) {
mInputBuffer->release();
mInputBuffer = nullptr;
}
}
}
if (!mInputBuffer) {
status_t err;
err = mSource->read(&mInputBuffer, &options);
CHECK((!err && mInputBuffer) || (err && !mInputBuffer));
android::Mutex::Autolock autoLock(mLock);
if (err != OK) {
if (mSeekTarget.IsValid()) {
mSeekTarget.Reset();
}
AUDIO_OFFLOAD_LOG(LogLevel::Error, ("Error while reading media source %d "
"Ok to receive EOS error at end", err));
if (!mReachedEOS) {
// After seek there is a possible race condition if
// OffloadThread is observing state_stopping_1 before
// framesReady() > 0. Ensure sink stop is called
// after last buffer is released. This ensures the
// partial buffer is written to the driver before
// stopping one is observed.The drawback is that
// there will be an unnecessary call to the parser
// after parser signalled EOS.
if (sizeDone > 0) {
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("send Partial buffer down"));
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("skip calling stop till next"
" fillBuffer"));
break;
}
// no more buffers to push - stop() and wait for STREAM_END
// don't set mReachedEOS until stream end received
mAudioSink->Stop();
}
break;
}
if(mInputBuffer->range_length() != 0) {
CHECK(mInputBuffer->meta_data()->findInt64(
kKeyTime, &mPositionTimeMediaUs));
}
if (mSeekTarget.IsValid() &&
seekTimeUs == mSeekTarget.GetTime().ToMicroseconds()) {
MOZ_ASSERT(mSeekTarget.IsValid());
mSeekTarget.Reset();
if (!mSeekPromise.IsEmpty()) {
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("FillBuffer posting SEEK_COMPLETE"));
MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
mSeekPromise.Resolve(val, __func__);
}
} else if (mSeekTarget.IsValid()) {
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("seek is updated during unlocking mLock"));
}
if (refreshSeekTime) {
NotifyPositionChanged();
// need to adjust the mStartPosUs for offload decoding since parser
// might not be able to get the exact seek time requested.
mStartPosUs = mPositionTimeMediaUs;
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("Adjust seek time to: %.2f",
mStartPosUs / 1E6));
}
}
if (mInputBuffer->range_length() == 0) {
mInputBuffer->release();
mInputBuffer = nullptr;
continue;
}
size_t copy = sizeRemaining;
if (copy > mInputBuffer->range_length()) {
copy = mInputBuffer->range_length();
}
memcpy((char *)aData + sizeDone,
(const char *)mInputBuffer->data() + mInputBuffer->range_offset(),
copy);
mInputBuffer->set_range(mInputBuffer->range_offset() + copy,
mInputBuffer->range_length() - copy);
sizeDone += copy;
sizeRemaining -= copy;
}
return sizeDone;
}
void AudioOffloadPlayer::SetElementVisibility(bool aIsVisible)
{
MOZ_ASSERT(NS_IsMainThread());
mIsElementVisible = aIsVisible;
if (mIsElementVisible) {
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("Element is visible. Start time update"));
StartTimeUpdate();
}
}
static void TimeUpdateCallback(nsITimer* aTimer, void* aClosure)
{
AudioOffloadPlayer* player = static_cast<AudioOffloadPlayer*>(aClosure);
player->TimeUpdate();
}
void AudioOffloadPlayer::TimeUpdate()
{
MOZ_ASSERT(NS_IsMainThread());
TimeStamp now = TimeStamp::Now();
// If TIMEUPDATE_MS has passed since the last fire update event fired, fire
// another timeupdate event.
if ((mLastFireUpdateTime.IsNull() ||
now - mLastFireUpdateTime >=
TimeDuration::FromMilliseconds(TIMEUPDATE_MS))) {
mLastFireUpdateTime = now;
NotifyPositionChanged();
}
if (mPlayState != MediaDecoder::PLAY_STATE_PLAYING || !mIsElementVisible) {
StopTimeUpdate();
}
}
nsresult AudioOffloadPlayer::StartTimeUpdate()
{
MOZ_ASSERT(NS_IsMainThread());
if (mTimeUpdateTimer) {
return NS_OK;
}
mTimeUpdateTimer = do_CreateInstance("@mozilla.org/timer;1");
return mTimeUpdateTimer->InitWithFuncCallback(TimeUpdateCallback,
this,
TIMEUPDATE_MS,
nsITimer::TYPE_REPEATING_SLACK);
}
nsresult AudioOffloadPlayer::StopTimeUpdate()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mTimeUpdateTimer) {
return NS_OK;
}
nsresult rv = mTimeUpdateTimer->Cancel();
mTimeUpdateTimer = nullptr;
return rv;
}
MediaDecoderOwner::NextFrameStatus AudioOffloadPlayer::GetNextFrameStatus()
{
MOZ_ASSERT(NS_IsMainThread());
if (mSeekTarget.IsValid()) {
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING;
} else if (mPlayState == MediaDecoder::PLAY_STATE_ENDED) {
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
} else {
return MediaDecoderOwner::NEXT_FRAME_AVAILABLE;
}
}
void AudioOffloadPlayer::SendMetaDataToHal(sp<GonkAudioSink>& aSink,
const sp<MetaData>& aMeta)
{
int32_t sampleRate = 0;
int32_t bitRate = 0;
int32_t channelMask = 0;
int32_t delaySamples = 0;
int32_t paddingSamples = 0;
CHECK(aSink.get());
AudioParameter param = AudioParameter();
if (aMeta->findInt32(kKeySampleRate, &sampleRate)) {
param.addInt(String8(AUDIO_OFFLOAD_CODEC_SAMPLE_RATE), sampleRate);
}
if (aMeta->findInt32(kKeyChannelMask, &channelMask)) {
param.addInt(String8(AUDIO_OFFLOAD_CODEC_NUM_CHANNEL), channelMask);
}
if (aMeta->findInt32(kKeyBitRate, &bitRate)) {
param.addInt(String8(AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE), bitRate);
}
if (aMeta->findInt32(kKeyEncoderDelay, &delaySamples)) {
param.addInt(String8(AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES), delaySamples);
}
if (aMeta->findInt32(kKeyEncoderPadding, &paddingSamples)) {
param.addInt(String8(AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES), paddingSamples);
}
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("SendMetaDataToHal: bitRate %d,"
" sampleRate %d, chanMask %d, delaySample %d, paddingSample %d", bitRate,
sampleRate, channelMask, delaySamples, paddingSamples));
aSink->SetParameters(param.toString());
return;
}
void AudioOffloadPlayer::SetVolume(double aVolume)
{
MOZ_ASSERT(NS_IsMainThread());
CHECK(mAudioSink.get());
mAudioSink->SetVolume((float) aVolume);
}
void AudioOffloadPlayer::WakeLockCreate()
{
MOZ_ASSERT(NS_IsMainThread());
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("%s", __FUNCTION__));
if (!mWakeLock) {
RefPtr<dom::power::PowerManagerService> pmService =
dom::power::PowerManagerService::GetInstance();
NS_ENSURE_TRUE_VOID(pmService);
ErrorResult rv;
mWakeLock = pmService->NewWakeLock(NS_LITERAL_STRING("cpu"), nullptr, rv);
}
}
void AudioOffloadPlayer::WakeLockRelease()
{
MOZ_ASSERT(NS_IsMainThread());
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("%s", __FUNCTION__));
if (mWakeLock) {
ErrorResult rv;
mWakeLock->Unlock(rv);
mWakeLock = nullptr;
}
}
} // namespace mozilla

View File

@ -1,273 +0,0 @@
/* -*- 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_H_
#define AUDIO_OFFLOAD_PLAYER_H_
#include <stagefright/MediaBuffer.h>
#include <stagefright/MediaSource.h>
#include <stagefright/TimeSource.h>
#include <utils/threads.h>
#include <utils/RefBase.h>
#include "AudioOutput.h"
#include "AudioOffloadPlayerBase.h"
#include "MediaDecoderOwner.h"
#include "MediaEventSource.h"
namespace mozilla {
namespace dom {
class WakeLock;
}
/**
* AudioOffloadPlayer adds support for audio tunneling to a digital signal
* processor (DSP) in the device chipset. With tunneling, audio decoding is
* off-loaded to the DSP, waking the application processor less often and using
* less battery
*
* This depends on offloading capability provided by Android KK AudioTrack class
*
* Audio playback is based on pull mechanism, whenever audio sink needs
* data, FillBuffer() will read data from compressed audio source and provide
* it to the sink
*
* Also this class passes state changes (play/pause/seek) from
* MediaOmxCommonDecoder to GonkAudioSink as well as provide GonkAudioSink status
* (position changed, playback ended, seek complete, audio tear down) back to
* MediaOmxCommonDecoder
*
* It acts as a bridge between MediaOmxCommonDecoder and GonkAudioSink during
* offload playback
*/
class MediaOmxCommonDecoder;
class AudioOffloadPlayer : public AudioOffloadPlayerBase
{
typedef android::Mutex Mutex;
typedef android::MetaData MetaData;
typedef android::status_t status_t;
typedef android::AudioTrack AudioTrack;
typedef android::MediaBuffer MediaBuffer;
typedef android::MediaSource MediaSource;
public:
enum {
REACHED_EOS,
SEEK_COMPLETE
};
AudioOffloadPlayer(MediaOmxCommonDecoder* aDecoder);
~AudioOffloadPlayer();
// Caller retains ownership of "aSource".
void SetSource(const android::sp<MediaSource> &aSource) override;
// Start the source if it's not already started and open the GonkAudioSink to
// create an offloaded audio track
status_t Start(bool aSourceAlreadyStarted = false) override;
status_t ChangeState(MediaDecoder::PlayState aState) override;
void SetVolume(double aVolume) override;
int64_t GetMediaTimeUs() override;
// To update progress bar when the element is visible
void SetElementVisibility(bool aIsVisible) override;
// Update ready state based on current play state. Not checking data
// availability since offloading is currently done only when whole compressed
// data is available
MediaDecoderOwner::NextFrameStatus GetNextFrameStatus() override;
RefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget) override;
void TimeUpdate();
// Close the audio sink, stop time updates, frees the input buffers
void Reset();
private:
// Set when audio source is started and GonkAudioSink is initialized
// Used only in main thread
bool mStarted;
// Set when audio sink is started. i.e. playback started
// Used only in main thread
bool mPlaying;
// Once playback reached end of stream (last ~100ms), position provided by DSP
// may be reset/corrupted. This bool is used to avoid that.
// Used in main thread and offload callback thread, protected by Mutex
// mLock
bool mReachedEOS;
// Set when the HTML Audio Element is visible to the user.
// Used only in main thread
bool mIsElementVisible;
// Session id given by Android::AudioSystem and used while creating audio sink
// Used only in main thread
int mSessionId;
// Sample rate of current audio track. Used only in main thread
int mSampleRate;
// After seeking, positions returned by offlaoded tracks (DSP) will be
// relative to the seeked position. And seeked position may be slightly
// different than given mSeekTimeUs, if audio source cannot find a frame at
// that position. Store seeked position in mStartPosUs and provide
// mStartPosUs + GetPosition() (i.e. absolute position) to
// MediaOmxCommonDecoder
// Used in main thread and offload callback thread, protected by Mutex
// mLock
int64_t mStartPosUs;
// The target of current seek when there is a request to seek
// Used in main thread and offload callback thread, protected by Mutex
// mLock
SeekTarget mSeekTarget;
// MozPromise of current seek.
// Used in main thread and offload callback thread, protected by Mutex
// mLock
MozPromiseHolder<MediaDecoder::SeekPromise> mSeekPromise;
// Positions obtained from offlaoded tracks (DSP)
// Used in main thread and offload callback thread, protected by Mutex
// mLock
int64_t mPositionTimeMediaUs;
// State obtained from MediaOmxCommonDecoder. Used only in main thread
MediaDecoder::PlayState mPlayState;
// Protect accessing audio position related variables between main thread and
// offload callback thread
Mutex mLock;
// Compressed audio source.
// Used in main thread first and later in offload callback thread
android::sp<MediaSource> mSource;
// Audio sink wrapper to access offloaded audio tracks
// Used in main thread and offload callback thread
// Race conditions are protected in underlying Android::AudioTrack class
android::sp<GonkAudioSink> mAudioSink;
// Buffer used to get date from audio source. Used in offload callback thread
MediaBuffer* mInputBuffer;
TimeStamp mLastFireUpdateTime;
// Timer to trigger position changed events
nsCOMPtr<nsITimer> mTimeUpdateTimer;
// Timer to reset GonkAudioSink when audio is paused for OFFLOAD_PAUSE_MAX_USECS.
// It is triggered in Pause() and canceled when there is a Play() within
// OFFLOAD_PAUSE_MAX_USECS. Used only from main thread so no lock is needed.
nsCOMPtr<nsITimer> mResetTimer;
// To avoid device suspend when mResetTimer is going to be triggered.
// Used only from main thread so no lock is needed.
RefPtr<mozilla::dom::WakeLock> mWakeLock;
MediaEventProducer<void> mOnPositionChanged;
MediaEventProducer<void> mOnPlaybackEnded;
MediaEventProducer<void> mOnPlayerTearDown;
MediaEventProducer<void> mOnSeekingStarted;
MediaEventListener mPositionChanged;
MediaEventListener mPlaybackEnded;
MediaEventListener mPlayerTearDown;
MediaEventListener mSeekingStarted;
// Provide the playback position in microseconds from total number of
// frames played by audio track
int64_t GetOutputPlayPositionUs_l() const;
// Fill the buffer given by audio sink with data from compressed audio
// source. Also handles the seek by seeking audio source and stop the sink in
// case of error
size_t FillBuffer(void *aData, size_t aSize);
// Called by GonkAudioSink when it needs data, to notify EOS or tear down event
static size_t AudioSinkCallback(GonkAudioSink *aAudioSink,
void *aData,
size_t aSize,
void *aMe,
GonkAudioSink::cb_event_t aEvent);
bool IsSeeking();
// Set mSeekTarget to the given position and restart the sink. Actual seek
// happens in FillBuffer(). If mSeekPromise is not empty, send
// SeekingStarted event always and SeekingStopped event when the play state is
// paused to MediaDecoder.
// When decoding and playing happens separately, if there is a seek during
// pause, we can decode and keep data ready.
// In case of offload player, no way to seek during pause. So just fake that
// seek is done.
status_t DoSeek();
// Start/Resume the audio sink so that callback will start being called to get
// compressed data
status_t Play();
// Stop the audio sink if we need to play till we drain the current buffer.
// or Pause the sink in case we should stop playing immediately
void Pause(bool aPlayPendingSamples = false);
// When audio is offloaded, application processor wakes up less frequently
// (>1sec) But when Player UI is visible we need to update progress bar
// atleast once in 250ms. Start a timer when player UI becomes visible or
// audio starts playing to send UpdateLogicalPosition events once in 250ms.
// Stop the timer when UI goes invisible or play state is not playing.
// Also make sure timer functions are always called from main thread
nsresult StartTimeUpdate();
nsresult StopTimeUpdate();
void WakeLockCreate();
void WakeLockRelease();
// Notify end of stream by sending PlaybackEnded event to observer
// (i.e.MediaDecoder)
void NotifyAudioEOS();
// Notify position changed event by sending UpdateLogicalPosition event to
// observer
void NotifyPositionChanged();
// Offloaded audio track is invalidated due to usecase change. Notify
// MediaDecoder to re-evaluate offloading options
void NotifyAudioTearDown();
// Send information from MetaData to the HAL via GonkAudioSink
void SendMetaDataToHal(android::sp<GonkAudioSink>& aSink,
const android::sp<MetaData>& aMeta);
AudioOffloadPlayer(const AudioOffloadPlayer &);
AudioOffloadPlayer &operator=(const AudioOffloadPlayer &);
};
} // namespace mozilla
#endif // AUDIO_OFFLOAD_PLAYER_H_

View File

@ -1,75 +0,0 @@
/* -*- 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 "MediaDecoder.h"
#include "MediaDecoderOwner.h"
namespace mozilla {
/**
* 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 status_t ChangeState(MediaDecoder::PlayState aState)
{
return android::NO_INIT;
}
virtual void SetVolume(double aVolume) {}
virtual int64_t GetMediaTimeUs() { 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;
}
virtual RefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget) = 0;
};
} // namespace mozilla
#endif // AUDIO_OFFLOAD_PLAYER_BASE_H_

View File

@ -1,235 +0,0 @@
/* -*- 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) 2008 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.
*/
#include <stagefright/foundation/ADebug.h>
#include "AudioOutput.h"
#include "mozilla/Logging.h"
namespace mozilla {
extern LazyLogModule gAudioOffloadPlayerLog;
#define AUDIO_OFFLOAD_LOG(type, msg) \
MOZ_LOG(gAudioOffloadPlayerLog, type, msg)
using namespace android;
AudioOutput::AudioOutput(int aSessionId, int aUid) :
mCallbackCookie(nullptr),
mCallback(nullptr),
mCallbackData(nullptr),
mUid(aUid),
mSessionId(aSessionId)
{
}
AudioOutput::~AudioOutput()
{
Close();
}
ssize_t AudioOutput::FrameSize() const
{
if (!mTrack.get()) {
return NO_INIT;
}
return mTrack->frameSize();
}
status_t AudioOutput::GetPosition(uint32_t *aPosition) const
{
if (!mTrack.get()) {
return NO_INIT;
}
return mTrack->getPosition(aPosition);
}
status_t AudioOutput::SetVolume(float aVolume) const
{
if (!mTrack.get()) {
return NO_INIT;
}
return mTrack->setVolume(aVolume);
}
status_t AudioOutput::SetParameters(const String8& aKeyValuePairs)
{
if (!mTrack.get()) {
return NO_INIT;
}
return mTrack->setParameters(aKeyValuePairs);
}
status_t AudioOutput::Open(uint32_t aSampleRate,
int aChannelCount,
audio_channel_mask_t aChannelMask,
audio_format_t aFormat,
AudioCallback aCb,
void* aCookie,
audio_output_flags_t aFlags,
const audio_offload_info_t *aOffloadInfo)
{
mCallback = aCb;
mCallbackCookie = aCookie;
if (((aFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) || !aCb ||
!aOffloadInfo) {
return BAD_VALUE;
}
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("open(%u, %d, 0x%x, 0x%x, %d 0x%x)",
aSampleRate, aChannelCount, aChannelMask, aFormat, mSessionId, aFlags));
if (aChannelMask == CHANNEL_MASK_USE_CHANNEL_ORDER) {
aChannelMask = audio_channel_out_mask_from_count(aChannelCount);
if (0 == aChannelMask) {
AUDIO_OFFLOAD_LOG(LogLevel::Error, ("open() error, can\'t derive mask for"
" %d audio channels", aChannelCount));
return NO_INIT;
}
}
sp<AudioTrack> t;
CallbackData* newcbd = new CallbackData(this);
t = new AudioTrack(
AUDIO_STREAM_MUSIC,
aSampleRate,
aFormat,
aChannelMask,
0, // Offloaded tracks will get frame count from AudioFlinger
aFlags,
CallbackWrapper,
newcbd,
0, // notification frames
mSessionId,
AudioTrack::TRANSFER_CALLBACK,
aOffloadInfo,
mUid);
if ((!t.get()) || (t->initCheck() != NO_ERROR)) {
AUDIO_OFFLOAD_LOG(LogLevel::Error, ("Unable to create audio track"));
delete newcbd;
return NO_INIT;
}
mCallbackData = newcbd;
t->setVolume(1.0);
mTrack = t;
return NO_ERROR;
}
status_t AudioOutput::Start()
{
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("%s", __PRETTY_FUNCTION__));
if (!mTrack.get()) {
return NO_INIT;
}
mTrack->setVolume(1.0);
return mTrack->start();
}
void AudioOutput::Stop()
{
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("%s", __PRETTY_FUNCTION__));
if (mTrack.get()) {
mTrack->stop();
}
}
void AudioOutput::Flush()
{
if (mTrack.get()) {
mTrack->flush();
}
}
void AudioOutput::Pause()
{
if (mTrack.get()) {
mTrack->pause();
}
}
void AudioOutput::Close()
{
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("%s", __PRETTY_FUNCTION__));
mTrack.clear();
delete mCallbackData;
mCallbackData = nullptr;
}
// static
void AudioOutput::CallbackWrapper(int aEvent, void* aCookie, void* aInfo)
{
CallbackData* data = (CallbackData*) aCookie;
data->Lock();
AudioOutput* me = data->GetOutput();
AudioTrack::Buffer* buffer = (AudioTrack::Buffer*) aInfo;
if (!me) {
// no output set, likely because the track was scheduled to be reused
// by another player, but the format turned out to be incompatible.
data->Unlock();
if (buffer) {
buffer->size = 0;
}
return;
}
switch(aEvent) {
case AudioTrack::EVENT_MORE_DATA: {
size_t actualSize = (*me->mCallback)(me, buffer->raw, buffer->size,
me->mCallbackCookie, CB_EVENT_FILL_BUFFER);
if (actualSize == 0 && buffer->size > 0) {
// We've reached EOS but the audio track is not stopped yet,
// keep playing silence.
memset(buffer->raw, 0, buffer->size);
actualSize = buffer->size;
}
buffer->size = actualSize;
} break;
case AudioTrack::EVENT_STREAM_END:
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("Callback wrapper: EVENT_STREAM_END"));
(*me->mCallback)(me, nullptr /* buffer */, 0 /* size */,
me->mCallbackCookie, CB_EVENT_STREAM_END);
break;
case AudioTrack::EVENT_NEW_IAUDIOTRACK :
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("Callback wrapper: EVENT_TEAR_DOWN"));
(*me->mCallback)(me, nullptr /* buffer */, 0 /* size */,
me->mCallbackCookie, CB_EVENT_TEAR_DOWN);
break;
default:
AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("received unknown event type: %d in"
" Callback wrapper!", aEvent));
break;
}
data->Unlock();
}
} // namespace mozilla

View File

@ -1,112 +0,0 @@
/* -*- 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) 2008 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 AUDIOOUTPUT_H_
#define AUDIOOUTPUT_H_
#include <stagefright/foundation/ABase.h>
#include <utils/Mutex.h>
#include <AudioTrack.h>
#include "GonkAudioSink.h"
namespace mozilla {
/**
* Stripped version of Android KK MediaPlayerService::AudioOutput class
* Android MediaPlayer uses AudioOutput as a wrapper to handle
* Android::AudioTrack
* Similarly to ease handling offloaded tracks, part of AudioOutput is used here
*/
class AudioOutput : public GonkAudioSink
{
typedef android::Mutex Mutex;
typedef android::String8 String8;
typedef android::status_t status_t;
typedef android::AudioTrack AudioTrack;
class CallbackData;
public:
AudioOutput(int aSessionId, int aUid);
virtual ~AudioOutput();
ssize_t FrameSize() const override;
status_t GetPosition(uint32_t* aPosition) const override;
status_t SetVolume(float aVolume) const override;
status_t SetParameters(const String8& aKeyValuePairs) override;
// Creates an offloaded audio track with the given parameters
// TODO: Try to recycle audio tracks instead of creating new audio tracks
// every time
status_t Open(uint32_t aSampleRate,
int aChannelCount,
audio_channel_mask_t aChannelMask,
audio_format_t aFormat,
AudioCallback aCb,
void* aCookie,
audio_output_flags_t aFlags = AUDIO_OUTPUT_FLAG_NONE,
const audio_offload_info_t* aOffloadInfo = nullptr) override;
status_t Start() override;
void Stop() override;
void Flush() override;
void Pause() override;
void Close() override;
private:
static void CallbackWrapper(int aEvent, void* aMe, void* aInfo);
android::sp<AudioTrack> mTrack;
void* mCallbackCookie;
AudioCallback mCallback;
CallbackData* mCallbackData;
// Uid of the current process, need to create audio track
int mUid;
// Session id given by Android::AudioSystem and used to create audio track
int mSessionId;
// CallbackData is what is passed to the AudioTrack as the "user" data.
// We need to be able to target this to a different Output on the fly,
// so we can't use the Output itself for this.
class CallbackData
{
public:
CallbackData(AudioOutput* aCookie)
{
mData = aCookie;
}
AudioOutput* GetOutput() { return mData;}
void SetOutput(AudioOutput* aNewcookie) { mData = aNewcookie; }
// Lock/Unlock are used by the callback before accessing the payload of
// this object
void Lock() { mLock.lock(); }
void Unlock() { mLock.unlock(); }
private:
AudioOutput* mData;
mutable Mutex mLock;
DISALLOW_EVIL_CONSTRUCTORS(CallbackData);
};
}; // AudioOutput
} // namespace mozilla
#endif /* AUDIOOUTPUT_H_ */

View File

@ -1,89 +0,0 @@
/* -*- 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) 2007 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 GONK_AUDIO_SINK_H_
#define GONK_AUDIO_SINK_H_
#include <utils/Errors.h>
#include <utils/String8.h>
#include <system/audio.h>
#define DEFAULT_AUDIOSINK_BUFFERCOUNT 4
#define DEFAULT_AUDIOSINK_BUFFERSIZE 1200
#define DEFAULT_AUDIOSINK_SAMPLERATE 44100
// when the channel mask isn't known, use the channel count to derive a mask in
// AudioSink::open()
#define CHANNEL_MASK_USE_CHANNEL_ORDER 0
namespace mozilla {
/**
* AudioSink: abstraction layer for audio output
* Stripped version of Android KK MediaPlayerBase::AudioSink class
*/
class GonkAudioSink : public android::RefBase
{
typedef android::String8 String8;
typedef android::status_t status_t;
public:
enum cb_event_t {
CB_EVENT_FILL_BUFFER, // Request to write more data to buffer.
CB_EVENT_STREAM_END, // Sent after all the buffers queued in AF and HW
// are played back (after stop is called)
CB_EVENT_TEAR_DOWN // The AudioTrack was invalidated due to usecase
// change. Need to re-evaluate offloading options
};
// Callback returns the number of bytes actually written to the buffer.
typedef size_t (*AudioCallback)(GonkAudioSink* aAudioSink,
void* aBuffer,
size_t aSize,
void* aCookie,
cb_event_t aEvent);
virtual ~GonkAudioSink() {}
virtual ssize_t FrameSize() const = 0;
virtual status_t GetPosition(uint32_t* aPosition) const = 0;
virtual status_t SetVolume(float aVolume) const = 0;
virtual status_t SetParameters(const String8& aKeyValuePairs)
{
return android::NO_ERROR;
}
virtual status_t Open(uint32_t aSampleRate,
int aChannelCount,
audio_channel_mask_t aChannelMask,
audio_format_t aFormat=AUDIO_FORMAT_PCM_16_BIT,
AudioCallback aCb = nullptr,
void* aCookie = nullptr,
audio_output_flags_t aFlags = AUDIO_OUTPUT_FLAG_NONE,
const audio_offload_info_t* aOffloadInfo = nullptr) = 0;
virtual status_t Start() = 0;
virtual void Stop() = 0;
virtual void Flush() = 0;
virtual void Pause() = 0;
virtual void Close() = 0;
};
} // namespace mozilla
#endif // GONK_AUDIO_SINK_H_

View File

@ -1,220 +0,0 @@
/* -*- 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 "I420ColorConverterHelper.h"
#include <dlfcn.h>
#include "mozilla/Logging.h"
mozilla::LazyLogModule gI420ColorConverterHelperLog("I420ColorConverterHelper");
#define LOG(msg...) MOZ_LOG(gI420ColorConverterHelperLog, mozilla::LogLevel::Warning, (msg))
namespace android {
I420ColorConverterHelper::I420ColorConverterHelper()
: mHandle(nullptr)
, mConverter({nullptr, nullptr, nullptr, nullptr, nullptr})
{
}
I420ColorConverterHelper::~I420ColorConverterHelper()
{
RWLock::AutoWLock awl(mLock);
unloadLocked();
}
// Prerequisite: a writer-lock should be held
bool
I420ColorConverterHelper::loadLocked()
{
if (loadedLocked()) {
return true;
}
unloadLocked();
// Open the shared library
mHandle = dlopen("libI420colorconvert.so", RTLD_NOW);
if (mHandle == nullptr) {
LOG("libI420colorconvert.so not found");
return false;
}
// Find the entry point
// Pointer to function with signature
// void getI420ColorConverter(II420ColorConverter *converter)
typedef int (* getConverterFn)(II420ColorConverter *converter);
getConverterFn getI420ColorConverter =
(getConverterFn) dlsym(mHandle, "getI420ColorConverter");
if (getI420ColorConverter == nullptr) {
LOG("Cannot load getI420ColorConverter from libI420colorconvert.so");
unloadLocked();
return false;
}
// Fill the function pointers.
getI420ColorConverter(&mConverter);
if (mConverter.getDecoderOutputFormat == nullptr ||
mConverter.convertDecoderOutputToI420 == nullptr ||
mConverter.getEncoderInputFormat == nullptr ||
mConverter.convertI420ToEncoderInput == nullptr ||
mConverter.getEncoderInputBufferInfo == nullptr) {
LOG("Failed to initialize I420 color converter");
unloadLocked();
return false;
}
return true;
}
// Prerequisite: a reader-lock or a writer-lock should be held
bool
I420ColorConverterHelper::loadedLocked() const
{
if (mHandle == nullptr ||
mConverter.getDecoderOutputFormat == nullptr ||
mConverter.convertDecoderOutputToI420 == nullptr ||
mConverter.getEncoderInputFormat == nullptr ||
mConverter.convertI420ToEncoderInput == nullptr ||
mConverter.getEncoderInputBufferInfo == nullptr) {
return false;
}
return true;
}
// Prerequisite: a writer-lock should be held
void
I420ColorConverterHelper::unloadLocked()
{
if (mHandle != nullptr) {
dlclose(mHandle);
}
mHandle = nullptr;
mConverter.getDecoderOutputFormat = nullptr;
mConverter.convertDecoderOutputToI420 = nullptr;
mConverter.getEncoderInputFormat = nullptr;
mConverter.convertI420ToEncoderInput = nullptr;
mConverter.getEncoderInputBufferInfo = nullptr;
}
bool
I420ColorConverterHelper::ensureLoaded()
{
{
RWLock::AutoRLock arl(mLock);
// Check whether the library has been loaded or not.
if (loadedLocked()) {
return true;
}
}
{
RWLock::AutoWLock awl(mLock);
// Check whether the library has been loaded or not on other threads.
if (loadedLocked()) {
return true;
}
// Reload the library
unloadLocked();
if (loadLocked()) {
return true;
}
// Reset the library
unloadLocked();
}
return false;
}
int
I420ColorConverterHelper::getDecoderOutputFormat()
{
if (!ensureLoaded()) {
return -1;
}
RWLock::AutoRLock arl(mLock);
if (mConverter.getDecoderOutputFormat != nullptr) {
return mConverter.getDecoderOutputFormat();
}
return -1;
}
int
I420ColorConverterHelper::convertDecoderOutputToI420(
void* decoderBits, int decoderWidth, int decoderHeight,
ARect decoderRect, void* dstBits)
{
if (!ensureLoaded()) {
return -1;
}
RWLock::AutoRLock arl(mLock);
if (mConverter.convertDecoderOutputToI420 != nullptr) {
return mConverter.convertDecoderOutputToI420(decoderBits,
decoderWidth, decoderHeight, decoderRect, dstBits);
}
return -1;
}
int
I420ColorConverterHelper::getEncoderInputFormat()
{
if (!ensureLoaded()) {
return -1;
}
RWLock::AutoRLock arl(mLock);
if (mConverter.getEncoderInputFormat != nullptr) {
return mConverter.getEncoderInputFormat();
}
return -1;
}
int
I420ColorConverterHelper::convertI420ToEncoderInput(void* aSrcBits,
int aSrcWidth,
int aSrcHeight,
int aEncoderWidth,
int aEncoderHeight,
ARect aEncoderRect,
void* aEncoderBits)
{
if (!ensureLoaded()) {
return -1;
}
RWLock::AutoRLock arl(mLock);
if (mConverter.convertI420ToEncoderInput != nullptr) {
return mConverter.convertI420ToEncoderInput(aSrcBits, aSrcWidth, aSrcHeight,
aEncoderWidth, aEncoderHeight, aEncoderRect, aEncoderBits);
}
return -1;
}
int
I420ColorConverterHelper::getEncoderInputBufferInfo(int aSrcWidth,
int aSrcHeight,
int* aEncoderWidth,
int* aEncoderHeight,
ARect* aEncoderRect,
int* aEncoderBufferSize)
{
if (!ensureLoaded()) {
return -1;
}
RWLock::AutoRLock arl(mLock);
if (mConverter.getEncoderInputBufferInfo != nullptr) {
return mConverter.getEncoderInputBufferInfo(aSrcWidth, aSrcHeight,
aEncoderWidth, aEncoderHeight, aEncoderRect, aEncoderBufferSize);
}
return -1;
}
} // namespace android

View File

@ -1,64 +0,0 @@
/* -*- 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 I420_COLOR_CONVERTER_HELPER_H
#define I420_COLOR_CONVERTER_HELPER_H
#include <utils/RWLock.h>
#include <media/editor/II420ColorConverter.h>
#include <mozilla/Attributes.h>
namespace android {
class I420ColorConverterHelper {
public:
I420ColorConverterHelper();
~I420ColorConverterHelper();
int getDecoderOutputFormat();
int convertDecoderOutputToI420(void* aDecoderBits,
int aDecoderWidth,
int aDecoderHeight,
ARect aDecoderRect,
void* aDstBits);
int getEncoderInputFormat();
int convertI420ToEncoderInput(void* aSrcBits,
int aSrcWidth,
int aSrcHeight,
int aEncoderWidth,
int aEncoderHeight,
ARect aEncoderRect,
void* aEncoderBits);
int getEncoderInputBufferInfo(int aSrcWidth,
int aSrcHeight,
int* aEncoderWidth,
int* aEncoderHeight,
ARect* aEncoderRect,
int* aEncoderBufferSize);
private:
mutable RWLock mLock;
void *mHandle;
II420ColorConverter mConverter;
bool loadLocked();
bool loadedLocked() const;
void unloadLocked();
bool ensureLoaded();
I420ColorConverterHelper(const I420ColorConverterHelper &) = delete;
const I420ColorConverterHelper &operator=(const I420ColorConverterHelper &) = delete;
};
} // namespace android
#endif // I420_COLOR_CONVERTER_HELPER_H

View File

@ -1,153 +0,0 @@
/* -*- 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(MPAPI_h_)
#define MPAPI_h_
#include <stdint.h>
#include "mozilla/layers/TextureClient.h"
namespace MPAPI {
struct VideoPlane {
VideoPlane() :
mData(nullptr),
mStride(0),
mWidth(0),
mHeight(0),
mOffset(0),
mSkip(0)
{}
void *mData;
int32_t mStride;
int32_t mWidth;
int32_t mHeight;
int32_t mOffset;
int32_t mSkip;
};
struct VideoFrame {
int64_t mTimeUs;
bool mKeyFrame;
bool mShouldSkip;
void *mData;
size_t mSize;
int32_t mStride;
int32_t mSliceHeight;
int32_t mRotation;
VideoPlane Y;
VideoPlane Cb;
VideoPlane Cr;
RefPtr<mozilla::layers::TextureClient> mGraphicBuffer;
VideoFrame() :
mTimeUs(0),
mKeyFrame(false),
mShouldSkip(false),
mData(nullptr),
mSize(0),
mStride(0),
mSliceHeight(0),
mRotation(0)
{}
void Set(int64_t aTimeUs, bool aKeyFrame,
void *aData, size_t aSize, int32_t aStride, int32_t aSliceHeight, int32_t aRotation,
void *aYData, int32_t aYStride, int32_t aYWidth, int32_t aYHeight, int32_t aYOffset, int32_t aYSkip,
void *aCbData, int32_t aCbStride, int32_t aCbWidth, int32_t aCbHeight, int32_t aCbOffset, int32_t aCbSkip,
void *aCrData, int32_t aCrStride, int32_t aCrWidth, int32_t aCrHeight, int32_t aCrOffset, int32_t aCrSkip)
{
mTimeUs = aTimeUs;
mKeyFrame = aKeyFrame;
mData = aData;
mSize = aSize;
mStride = aStride;
mSliceHeight = aSliceHeight;
mRotation = aRotation;
mGraphicBuffer = nullptr;
Y.mData = aYData;
Y.mStride = aYStride;
Y.mWidth = aYWidth;
Y.mHeight = aYHeight;
Y.mOffset = aYOffset;
Y.mSkip = aYSkip;
Cb.mData = aCbData;
Cb.mStride = aCbStride;
Cb.mWidth = aCbWidth;
Cb.mHeight = aCbHeight;
Cb.mOffset = aCbOffset;
Cb.mSkip = aCbSkip;
Cr.mData = aCrData;
Cr.mStride = aCrStride;
Cr.mWidth = aCrWidth;
Cr.mHeight = aCrHeight;
Cr.mOffset = aCrOffset;
Cr.mSkip = aCrSkip;
}
};
struct AudioFrame {
int64_t mTimeUs;
void *mData; // 16PCM interleaved
size_t mSize; // Size of mData in bytes
int32_t mAudioChannels;
int32_t mAudioSampleRate;
AudioFrame() :
mTimeUs(0),
mData(0),
mSize(0),
mAudioChannels(0),
mAudioSampleRate(0)
{
}
void Set(int64_t aTimeUs,
void *aData, size_t aSize,
int32_t aAudioChannels, int32_t aAudioSampleRate)
{
mTimeUs = aTimeUs;
mData = aData;
mSize = aSize;
mAudioChannels = aAudioChannels;
mAudioSampleRate = aAudioSampleRate;
}
};
struct Decoder;
struct PluginHost {
bool (*Read)(Decoder *aDecoder, char *aBuffer, int64_t aOffset, uint32_t aCount, uint32_t* aBytes);
uint64_t (*GetLength)(Decoder *aDecoder);
void (*SetMetaDataReadMode)(Decoder *aDecoder);
void (*SetPlaybackReadMode)(Decoder *aDecoder);
};
struct Decoder {
void *mResource;
void *mPrivate;
Decoder();
void (*GetDuration)(Decoder *aDecoder, int64_t *durationUs);
void (*GetVideoParameters)(Decoder *aDecoder, int32_t *aWidth, int32_t *aHeight);
void (*GetAudioParameters)(Decoder *aDecoder, int32_t *aNumChannels, int32_t *aSampleRate);
bool (*HasVideo)(Decoder *aDecoder);
bool (*HasAudio)(Decoder *aDecoder);
bool (*ReadVideo)(Decoder *aDecoder, VideoFrame *aFrame, int64_t aSeekTimeUs);
bool (*ReadAudio)(Decoder *aDecoder, AudioFrame *aFrame, int64_t aSeekTimeUs);
void (*DestroyDecoder)(Decoder *);
};
struct Manifest {
bool (*CanDecode)(const char *aMimeChars, size_t aMimeLen, const char* const**aCodecs);
bool (*CreateDecoder)(PluginHost *aPluginHost, Decoder *aDecoder,
const char *aMimeChars, size_t aMimeLen);
};
}
#endif

View File

@ -1,626 +0,0 @@
/* -*- 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 <string.h>
#include <binder/IPCThreadState.h>
#include <stagefright/foundation/ABuffer.h>
#include <stagefright/foundation/ADebug.h>
#include <stagefright/MetaData.h>
#include "stagefright/MediaErrors.h"
#include <android/log.h>
#define MCP_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "MediaCodecProxy", __VA_ARGS__)
namespace android {
// General Template: MediaCodec::getOutputGraphicBufferFromIndex(...)
template <typename T, bool InterfaceSupported>
struct OutputGraphicBufferStub
{
static status_t GetOutputGraphicBuffer(T *aMediaCodec,
size_t aIndex,
sp<GraphicBuffer> *aGraphicBuffer)
{
return ERROR_UNSUPPORTED;
}
};
// Class Template Specialization: MediaCodec::getOutputGraphicBufferFromIndex(...)
template <typename T>
struct OutputGraphicBufferStub<T, true>
{
static status_t GetOutputGraphicBuffer(T *aMediaCodec,
size_t aIndex,
sp<GraphicBuffer> *aGraphicBuffer)
{
if (aMediaCodec == nullptr || aGraphicBuffer == nullptr) {
return BAD_VALUE;
}
*aGraphicBuffer = aMediaCodec->getOutputGraphicBufferFromIndex(aIndex);
return OK;
}
};
// Wrapper class to handle interface-difference of MediaCodec.
struct MediaCodecInterfaceWrapper
{
typedef int8_t Supported;
typedef int16_t Unsupported;
template <typename T>
static auto TestOutputGraphicBuffer(T *aMediaCodec) -> decltype(aMediaCodec->getOutputGraphicBufferFromIndex(0), Supported());
template <typename T>
static auto TestOutputGraphicBuffer(...) -> Unsupported;
// SFINAE: Substitution Failure Is Not An Error
static const bool OutputGraphicBufferSupported = sizeof(TestOutputGraphicBuffer<MediaCodec>(nullptr)) == sizeof(Supported);
// Class Template Specialization
static OutputGraphicBufferStub<MediaCodec, OutputGraphicBufferSupported> sOutputGraphicBufferStub;
// Wrapper Function
static status_t GetOutputGraphicBuffer(MediaCodec *aMediaCodec,
size_t aIndex,
sp<GraphicBuffer> *aGraphicBuffer)
{
return sOutputGraphicBufferStub.GetOutputGraphicBuffer(aMediaCodec, aIndex, aGraphicBuffer);
}
};
sp<MediaCodecProxy>
MediaCodecProxy::CreateByType(sp<ALooper> aLooper,
const char *aMime,
bool aEncoder)
{
sp<MediaCodecProxy> codec = new MediaCodecProxy(aLooper,
aMime,
aEncoder);
return codec;
}
MediaCodecProxy::MediaCodecProxy(sp<ALooper> aLooper,
const char *aMime,
bool aEncoder)
: mCodecLooper(aLooper)
, mCodecMime(aMime)
, mCodecEncoder(aEncoder)
, mPromiseMonitor("MediaCodecProxy::mPromiseMonitor")
{
MOZ_ASSERT(mCodecLooper != nullptr, "ALooper should not be nullptr.");
mCodecPromise.SetMonitor(&mPromiseMonitor);
}
MediaCodecProxy::~MediaCodecProxy()
{
ReleaseMediaCodec();
}
bool
MediaCodecProxy::AllocateAudioMediaCodec()
{
if (mResourceClient || mCodec.get()) {
return false;
}
if (strncasecmp(mCodecMime.get(), "audio/", 6) == 0) {
if (allocateCodec()) {
return true;
}
}
return false;
}
RefPtr<MediaCodecProxy::CodecPromise>
MediaCodecProxy::AsyncAllocateVideoMediaCodec()
{
if (mResourceClient || mCodec.get()) {
return CodecPromise::CreateAndReject(true, __func__);
}
if (strncasecmp(mCodecMime.get(), "video/", 6) != 0) {
return CodecPromise::CreateAndReject(true, __func__);
}
// request video codec
mozilla::MediaSystemResourceType type =
mCodecEncoder ? mozilla::MediaSystemResourceType::VIDEO_ENCODER :
mozilla::MediaSystemResourceType::VIDEO_DECODER;
mResourceClient = new mozilla::MediaSystemResourceClient(type);
mResourceClient->SetListener(this);
mResourceClient->Acquire();
mozilla::MonitorAutoLock lock(mPromiseMonitor);
RefPtr<CodecPromise> p = mCodecPromise.Ensure(__func__);
return p.forget();
}
void
MediaCodecProxy::ReleaseMediaCodec()
{
// At first, release mResourceClient's resource to prevent a conflict with
// mResourceClient's callback.
if (mResourceClient) {
mResourceClient->ReleaseResource();
mResourceClient = nullptr;
}
mozilla::MonitorAutoLock lock(mPromiseMonitor);
mCodecPromise.RejectIfExists(true, __func__);
releaseCodec();
}
bool
MediaCodecProxy::allocateCodec()
{
if (mCodecLooper == nullptr) {
return false;
}
// Write Lock for mCodec
RWLock::AutoWLock awl(mCodecLock);
// Create MediaCodec
mCodec = MediaCodec::CreateByType(mCodecLooper, mCodecMime.get(), mCodecEncoder);
if (mCodec == nullptr) {
return false;
}
return true;
}
void
MediaCodecProxy::releaseCodec()
{
wp<MediaCodec> codec;
{
// Write Lock for mCodec
RWLock::AutoWLock awl(mCodecLock);
codec = mCodec;
// Release MediaCodec
if (mCodec != nullptr) {
mCodec->stop();
mCodec->release();
mCodec = nullptr;
}
}
while (codec.promote() != nullptr) {
// this value come from stagefright's AwesomePlayer.
usleep(1000);
}
// Complete all pending Binder ipc transactions
IPCThreadState::self()->flushCommands();
}
bool
MediaCodecProxy::allocated() const
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
return mCodec != nullptr;
}
status_t
MediaCodecProxy::configure(const sp<AMessage> &aFormat,
const sp<Surface> &aNativeWindow,
const sp<ICrypto> &aCrypto,
uint32_t aFlags)
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return mCodec->configure(aFormat, aNativeWindow, aCrypto, aFlags);
}
status_t
MediaCodecProxy::start()
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return mCodec->start();
}
status_t
MediaCodecProxy::stop()
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return mCodec->stop();
}
status_t
MediaCodecProxy::release()
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return mCodec->release();
}
status_t
MediaCodecProxy::flush()
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return mCodec->flush();
}
status_t
MediaCodecProxy::queueInputBuffer(size_t aIndex,
size_t aOffset,
size_t aSize,
int64_t aPresentationTimeUs,
uint32_t aFlags,
AString *aErrorDetailMessage)
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return mCodec->queueInputBuffer(aIndex, aOffset, aSize,
aPresentationTimeUs, aFlags, aErrorDetailMessage);
}
status_t
MediaCodecProxy::queueSecureInputBuffer(size_t aIndex,
size_t aOffset,
const CryptoPlugin::SubSample *aSubSamples,
size_t aNumSubSamples,
const uint8_t aKey[16],
const uint8_t aIV[16],
CryptoPlugin::Mode aMode,
int64_t aPresentationTimeUs,
uint32_t aFlags,
AString *aErrorDetailMessage)
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return mCodec->queueSecureInputBuffer(aIndex, aOffset,
aSubSamples, aNumSubSamples, aKey, aIV, aMode,
aPresentationTimeUs, aFlags, aErrorDetailMessage);
}
status_t
MediaCodecProxy::dequeueInputBuffer(size_t *aIndex,
int64_t aTimeoutUs)
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return mCodec->dequeueInputBuffer(aIndex, aTimeoutUs);
}
status_t
MediaCodecProxy::dequeueOutputBuffer(size_t *aIndex,
size_t *aOffset,
size_t *aSize,
int64_t *aPresentationTimeUs,
uint32_t *aFlags,
int64_t aTimeoutUs)
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return mCodec->dequeueOutputBuffer(aIndex, aOffset, aSize,
aPresentationTimeUs, aFlags, aTimeoutUs);
}
status_t
MediaCodecProxy::renderOutputBufferAndRelease(size_t aIndex)
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return mCodec->renderOutputBufferAndRelease(aIndex);
}
status_t
MediaCodecProxy::releaseOutputBuffer(size_t aIndex)
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return mCodec->releaseOutputBuffer(aIndex);
}
status_t
MediaCodecProxy::signalEndOfInputStream()
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return mCodec->signalEndOfInputStream();
}
status_t
MediaCodecProxy::getOutputFormat(sp<AMessage> *aFormat) const
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return mCodec->getOutputFormat(aFormat);
}
status_t
MediaCodecProxy::getInputBuffers(Vector<sp<ABuffer>> *aBuffers) const
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return mCodec->getInputBuffers(aBuffers);
}
status_t
MediaCodecProxy::getOutputBuffers(Vector<sp<ABuffer>> *aBuffers) const
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return mCodec->getOutputBuffers(aBuffers);
}
void
MediaCodecProxy::requestActivityNotification(const sp<AMessage> &aNotify)
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return;
}
mCodec->requestActivityNotification(aNotify);
}
status_t
MediaCodecProxy::getOutputGraphicBufferFromIndex(size_t aIndex,
sp<GraphicBuffer> *aGraphicBuffer)
{
// Read Lock for mCodec
RWLock::AutoRLock arl(mCodecLock);
if (mCodec == nullptr) {
return NO_INIT;
}
return MediaCodecInterfaceWrapper::GetOutputGraphicBuffer(mCodec.get(), aIndex, aGraphicBuffer);
}
status_t
MediaCodecProxy::getCapability(uint32_t *aCapability)
{
if (aCapability == nullptr) {
return BAD_VALUE;
}
uint32_t capability = kEmptyCapability;
if (MediaCodecInterfaceWrapper::OutputGraphicBufferSupported) {
capability |= kCanExposeGraphicBuffer;
}
*aCapability = capability;
return OK;
}
// Called on ImageBridge thread
void
MediaCodecProxy::ResourceReserved()
{
MCP_LOG("resourceReserved");
mozilla::MonitorAutoLock lock(mPromiseMonitor);
// Create MediaCodec
if (!allocateCodec()) {
mCodecPromise.RejectIfExists(true, __func__);
return;
}
mCodecPromise.ResolveIfExists(true, __func__);
}
// Called on ImageBridge thread
void
MediaCodecProxy::ResourceReserveFailed()
{
mozilla::MonitorAutoLock lock(mPromiseMonitor);
mCodecPromise.RejectIfExists(true, __func__);
}
bool MediaCodecProxy::Prepare()
{
if (start() != OK) {
MCP_LOG("Couldn't start MediaCodec");
return false;
}
if (getInputBuffers(&mInputBuffers) != OK) {
MCP_LOG("Couldn't get input buffers from MediaCodec");
return false;
}
if (getOutputBuffers(&mOutputBuffers) != OK) {
MCP_LOG("Couldn't get output buffers from MediaCodec");
return false;
}
return true;
}
bool MediaCodecProxy::UpdateOutputBuffers()
{
// Read Lock for mCodec
{
RWLock::AutoRLock autolock(mCodecLock);
if (mCodec == nullptr) {
MCP_LOG("MediaCodec has not been inited from UpdateOutputBuffers");
return false;
}
}
status_t err = getOutputBuffers(&mOutputBuffers);
if (err != OK){
MCP_LOG("Couldn't update output buffers from MediaCodec");
return false;
}
return true;
}
status_t MediaCodecProxy::Input(const uint8_t* aData, uint32_t aDataSize,
int64_t aTimestampUsecs, uint64_t aflags,
int64_t aTimeoutUs)
{
// Read Lock for mCodec
{
RWLock::AutoRLock autolock(mCodecLock);
if (mCodec == nullptr) {
MCP_LOG("MediaCodec has not been inited from input!");
return NO_INIT;
}
}
size_t index;
status_t err = dequeueInputBuffer(&index, aTimeoutUs);
if (err != OK) {
if (err != -EAGAIN) {
MCP_LOG("dequeueInputBuffer returned %d", err);
}
return err;
}
if (aData) {
const sp<ABuffer> &dstBuffer = mInputBuffers.itemAt(index);
CHECK_LE(aDataSize, dstBuffer->capacity());
dstBuffer->setRange(0, aDataSize);
memcpy(dstBuffer->data(), aData, aDataSize);
err = queueInputBuffer(index, 0, dstBuffer->size(), aTimestampUsecs, aflags);
} else {
err = queueInputBuffer(index, 0, 0, 0ll, MediaCodec::BUFFER_FLAG_EOS);
}
if (err != OK) {
MCP_LOG("queueInputBuffer returned %d", err);
return err;
}
return err;
}
status_t MediaCodecProxy::Output(MediaBuffer** aBuffer, int64_t aTimeoutUs)
{
// Read Lock for mCodec
{
RWLock::AutoRLock autolock(mCodecLock);
if (mCodec == nullptr) {
MCP_LOG("MediaCodec has not been inited from output!");
return NO_INIT;
}
}
size_t index = 0;
size_t offset = 0;
size_t size = 0;
int64_t timeUs = 0;
uint32_t flags = 0;
*aBuffer = nullptr;
status_t err = dequeueOutputBuffer(&index, &offset, &size,
&timeUs, &flags, aTimeoutUs);
if (err != OK) {
return err;
}
MediaBuffer *buffer;
sp<GraphicBuffer> graphicBuffer;
if (getOutputGraphicBufferFromIndex(index, &graphicBuffer) == OK &&
graphicBuffer != nullptr) {
buffer = new MediaBuffer(graphicBuffer);
} else {
buffer = new MediaBuffer(mOutputBuffers.itemAt(index));
}
sp<MetaData> metaData = buffer->meta_data();
metaData->setInt32(kKeyBufferIndex, index);
metaData->setInt64(kKeyTime, timeUs);
buffer->set_range(buffer->range_offset(), size);
*aBuffer = buffer;
if (flags & MediaCodec::BUFFER_FLAG_EOS) {
return ERROR_END_OF_STREAM;
}
return err;
}
void MediaCodecProxy::ReleaseMediaResources()
{
ReleaseMediaCodec();
}
void MediaCodecProxy::ReleaseMediaBuffer(MediaBuffer* aBuffer) {
if (aBuffer) {
sp<MetaData> metaData = aBuffer->meta_data();
int32_t index;
metaData->findInt32(kKeyBufferIndex, &index);
aBuffer->release();
releaseOutputBuffer(index);
}
}
} // namespace android

View File

@ -1,183 +0,0 @@
/* -*- 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 MEDIA_CODEC_PROXY_H
#define MEDIA_CODEC_PROXY_H
#include <nsString.h>
#include <stagefright/MediaCodec.h>
#include <stagefright/MediaBuffer.h>
#include <utils/threads.h>
#include "mozilla/media/MediaSystemResourceClient.h"
#include "mozilla/Monitor.h"
#include "mozilla/MozPromise.h"
#include "mozilla/RefPtr.h"
namespace android {
// This class is intended to be a proxy for MediaCodec with codec resource
// management. Basically user can use it like MediaCodec, but need to handle
// the listener when Codec is reserved for Async case. A good example is
// MediaCodecReader.cpp. Another useage is to use configure(), Prepare(),
// Input(), and Output(). It is used in GonkVideoDecoderManager.cpp which
// doesn't need to handle the buffers for codec.
class MediaCodecProxy : public RefBase
, public mozilla::MediaSystemResourceReservationListener
{
public:
typedef mozilla::MozPromise<bool /* aIgnored */, bool /* aIgnored */, /* IsExclusive = */ true> CodecPromise;
enum Capability {
kEmptyCapability = 0x00000000,
kCanExposeGraphicBuffer = 0x00000001,
};
enum {
kKeyBufferIndex = 'bfin',
};
// Check whether MediaCodec has been allocated.
bool allocated() const;
// Static MediaCodec methods
// Only support MediaCodec::CreateByType()
static sp<MediaCodecProxy> CreateByType(sp<ALooper> aLooper,
const char *aMime,
bool aEncoder);
// MediaCodec methods
status_t configure(const sp<AMessage> &aFormat,
const sp<Surface> &aNativeWindow,
const sp<ICrypto> &aCrypto,
uint32_t aFlags);
status_t start();
status_t stop();
status_t release();
status_t flush();
status_t queueInputBuffer(size_t aIndex,
size_t aOffset,
size_t aSize,
int64_t aPresentationTimeUs,
uint32_t aFlags,
AString *aErrorDetailMessage=nullptr);
status_t queueSecureInputBuffer(size_t aIndex,
size_t aOffset,
const CryptoPlugin::SubSample *aSubSamples,
size_t aNumSubSamples,
const uint8_t aKey[16],
const uint8_t aIV[16],
CryptoPlugin::Mode aMode,
int64_t aPresentationTimeUs,
uint32_t aFlags,
AString *aErrorDetailMessage=nullptr);
status_t dequeueInputBuffer(size_t *aIndex,
int64_t aTimeoutUs=INT64_C(0));
status_t dequeueOutputBuffer(size_t *aIndex,
size_t *aOffset,
size_t *aSize,
int64_t *aPresentationTimeUs,
uint32_t *aFlags,
int64_t aTimeoutUs=INT64_C(0));
status_t renderOutputBufferAndRelease(size_t aIndex);
status_t releaseOutputBuffer(size_t aIndex);
status_t signalEndOfInputStream();
status_t getOutputFormat(sp<AMessage> *aFormat) const;
status_t getInputBuffers(Vector<sp<ABuffer>> *aBuffers) const;
status_t getOutputBuffers(Vector<sp<ABuffer>> *aBuffers) const;
// Notification will be posted once there "is something to do", i.e.
// an input/output buffer has become available, a format change is
// pending, an error is pending.
void requestActivityNotification(const sp<AMessage> &aNotify);
status_t getOutputGraphicBufferFromIndex(size_t aIndex,
sp<GraphicBuffer> *aGraphicBuffer);
status_t getCapability(uint32_t *aCapability);
// Utility functions
// If aData is null, will notify decoder input EOS
status_t Input(const uint8_t* aData, uint32_t aDataSize,
int64_t aTimestampUsecs, uint64_t flags, int64_t aTimeoutUs = 0);
status_t Output(MediaBuffer** aBuffer, int64_t aTimeoutUs);
bool Prepare();
void ReleaseMediaResources();
// This updates mOutputBuffer when receiving INFO_OUTPUT_BUFFERS_CHANGED event.
bool UpdateOutputBuffers();
void ReleaseMediaBuffer(MediaBuffer* abuffer);
// It allocates audio MediaCodec synchronously.
bool AllocateAudioMediaCodec();
// It allocates video MediaCodec asynchronously.
RefPtr<CodecPromise> AsyncAllocateVideoMediaCodec();
// Free the OMX codec so others can allocate it.
void ReleaseMediaCodec();
protected:
virtual ~MediaCodecProxy();
// MediaResourceReservationListener
void ResourceReserved() override;
void ResourceReserveFailed() override;
private:
// Forbidden
MediaCodecProxy() = delete;
MediaCodecProxy(const MediaCodecProxy &) = delete;
const MediaCodecProxy &operator=(const MediaCodecProxy &) = delete;
// Constructor for MediaCodecProxy::CreateByType
MediaCodecProxy(sp<ALooper> aLooper,
const char *aMime,
bool aEncoder);
// Allocate Codec Resource
bool allocateCodec();
// Release Codec Resource
void releaseCodec();
// MediaCodec Parameter
sp<ALooper> mCodecLooper;
nsCString mCodecMime;
bool mCodecEncoder;
mozilla::MozPromiseHolder<CodecPromise> mCodecPromise;
// When mPromiseMonitor is held, mResourceClient's functions should not be called.
mozilla::Monitor mPromiseMonitor;
// Media Resource Management
RefPtr<mozilla::MediaSystemResourceClient> mResourceClient;
// MediaCodec instance
mutable RWLock mCodecLock;
sp<MediaCodec> mCodec;
//MediaCodec buffers to hold input/output data.
Vector<sp<ABuffer> > mInputBuffers;
Vector<sp<ABuffer> > mOutputBuffers;
};
} // namespace android
#endif // MEDIA_CODEC_PROXY_H

View File

@ -1,297 +0,0 @@
/* -*- 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 "MediaOmxCommonDecoder.h"
#include <stagefright/MediaSource.h>
#include "AudioOffloadPlayerBase.h"
#include "MediaDecoderStateMachine.h"
#include "MediaOmxCommonReader.h"
#ifdef MOZ_AUDIO_OFFLOAD
#include "AudioOffloadPlayer.h"
#endif
using namespace android;
namespace mozilla {
extern LazyLogModule gMediaDecoderLog;
#define DECODER_LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
MediaOmxCommonDecoder::MediaOmxCommonDecoder(MediaDecoderOwner* aOwner)
: MediaDecoder(aOwner)
, mReader(nullptr)
, mCanOffloadAudio(false)
, mFallbackToStateMachine(false)
, mIsCaptured(false)
{
mDormantSupported = true;
}
MediaOmxCommonDecoder::~MediaOmxCommonDecoder() {}
void
MediaOmxCommonDecoder::SetPlatformCanOffloadAudio(bool aCanOffloadAudio)
{
if (!aCanOffloadAudio) {
return;
}
// Stop MDSM from playing to avoid startup glitch (bug 1053186).
GetStateMachine()->DispatchAudioOffloading(true);
// Modify mCanOffloadAudio in the main thread.
RefPtr<MediaOmxCommonDecoder> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
self->mCanOffloadAudio = true;
});
AbstractThread::MainThread()->Dispatch(r.forget());
}
void
MediaOmxCommonDecoder::DisableStateMachineAudioOffloading()
{
MOZ_ASSERT(NS_IsMainThread());
if (mCanOffloadAudio) {
// mCanOffloadAudio is true implies we've called
// |GetStateMachine()->DispatchAudioOffloading(true)| in
// SetPlatformCanOffloadAudio(). We need to turn off audio offloading
// for MDSM so it can start playback.
GetStateMachine()->DispatchAudioOffloading(false);
}
}
bool
MediaOmxCommonDecoder::CheckDecoderCanOffloadAudio()
{
MOZ_ASSERT(NS_IsMainThread());
return (mCanOffloadAudio && !mFallbackToStateMachine &&
!mIsCaptured && mPlaybackRate == 1.0);
}
void
MediaOmxCommonDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
MediaDecoderEventVisibility aEventVisibility)
{
MOZ_ASSERT(NS_IsMainThread());
MediaDecoder::FirstFrameLoaded(aInfo, aEventVisibility);
if (!CheckDecoderCanOffloadAudio()) {
DECODER_LOG(LogLevel::Debug, ("In %s Offload Audio check failed",
__PRETTY_FUNCTION__));
DisableStateMachineAudioOffloading();
return;
}
#ifdef MOZ_AUDIO_OFFLOAD
mAudioOffloadPlayer = new AudioOffloadPlayer(this);
#endif
if (!mAudioOffloadPlayer) {
DisableStateMachineAudioOffloading();
return;
}
mAudioOffloadPlayer->SetSource(mReader->GetAudioOffloadTrack());
status_t err = mAudioOffloadPlayer->Start(false);
if (err != OK) {
mAudioOffloadPlayer = nullptr;
mFallbackToStateMachine = true;
DECODER_LOG(LogLevel::Debug, ("In %s Unable to start offload audio %d."
"Switching to normal mode", __PRETTY_FUNCTION__, err));
DisableStateMachineAudioOffloading();
return;
}
PauseStateMachine();
if (mLogicallySeeking) {
SeekTarget target = SeekTarget(mLogicalPosition,
SeekTarget::Accurate,
MediaDecoderEventVisibility::Observable);
mSeekRequest.DisconnectIfExists();
mSeekRequest.Begin(mAudioOffloadPlayer->Seek(target)
->Then(AbstractThread::MainThread(), __func__, static_cast<MediaDecoder*>(this),
&MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
}
// Call ChangeState() to run AudioOffloadPlayer since offload state enabled
ChangeState(mPlayState);
}
void
MediaOmxCommonDecoder::PauseStateMachine()
{
MOZ_ASSERT(NS_IsMainThread());
DECODER_LOG(LogLevel::Debug, ("%s", __PRETTY_FUNCTION__));
MOZ_ASSERT(GetStateMachine());
// enter dormant state
GetStateMachine()->DispatchSetDormant(true);
}
void
MediaOmxCommonDecoder::ResumeStateMachine()
{
MOZ_ASSERT(NS_IsMainThread());
DECODER_LOG(LogLevel::Debug, ("%s current time %f", __PRETTY_FUNCTION__, mLogicalPosition));
if (IsShutdown()) {
return;
}
if (!GetStateMachine()) {
return;
}
GetStateMachine()->DispatchAudioOffloading(false);
mFallbackToStateMachine = true;
mAudioOffloadPlayer = nullptr;
SeekTarget target = SeekTarget(mLogicalPosition,
SeekTarget::Accurate,
MediaDecoderEventVisibility::Suppressed);
// Call Seek of MediaDecoderStateMachine to suppress seek events.
GetStateMachine()->InvokeSeek(target);
// exit dormant state
GetStateMachine()->DispatchSetDormant(false);
UpdateLogicalPosition();
}
void
MediaOmxCommonDecoder::AudioOffloadTearDown()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IsShutdown());
DECODER_LOG(LogLevel::Debug, ("%s", __PRETTY_FUNCTION__));
// mAudioOffloadPlayer can be null here if ResumeStateMachine was called
// just before because of some other error.
if (mAudioOffloadPlayer) {
ResumeStateMachine();
}
}
void
MediaOmxCommonDecoder::AddOutputStream(ProcessedMediaStream* aStream,
bool aFinishWhenEnded)
{
MOZ_ASSERT(NS_IsMainThread());
mIsCaptured = true;
if (mAudioOffloadPlayer) {
ResumeStateMachine();
}
MediaDecoder::AddOutputStream(aStream, aFinishWhenEnded);
}
void
MediaOmxCommonDecoder::SetPlaybackRate(double aPlaybackRate)
{
MOZ_ASSERT(NS_IsMainThread());
if (mAudioOffloadPlayer &&
((aPlaybackRate != 0.0) || (aPlaybackRate != 1.0))) {
ResumeStateMachine();
}
MediaDecoder::SetPlaybackRate(aPlaybackRate);
}
void
MediaOmxCommonDecoder::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) {
return;
}
status_t err = mAudioOffloadPlayer->ChangeState(aState);
if (err != OK) {
ResumeStateMachine();
return;
}
}
void
MediaOmxCommonDecoder::CallSeek(const SeekTarget& aTarget, dom::Promise* aPromise)
{
if (!mAudioOffloadPlayer) {
MediaDecoder::CallSeek(aTarget, aPromise);
return;
}
DiscardOngoingSeekIfExists();
mSeekDOMPromise = aPromise;
mSeekRequest.Begin(mAudioOffloadPlayer->Seek(aTarget)
->Then(AbstractThread::MainThread(), __func__, static_cast<MediaDecoder*>(this),
&MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
}
int64_t
MediaOmxCommonDecoder::CurrentPosition()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mAudioOffloadPlayer) {
return MediaDecoder::CurrentPosition();
}
return mAudioOffloadPlayer->GetMediaTimeUs();
}
void
MediaOmxCommonDecoder::SetElementVisibility(bool aIsVisible)
{
MOZ_ASSERT(NS_IsMainThread());
MediaDecoder::SetElementVisibility(aIsVisible);
if (mAudioOffloadPlayer) {
mAudioOffloadPlayer->SetElementVisibility(aIsVisible);
}
}
MediaDecoderOwner::NextFrameStatus
MediaOmxCommonDecoder::NextFrameStatus()
{
MOZ_ASSERT(NS_IsMainThread());
return mAudioOffloadPlayer ? mAudioOffloadPlayer->GetNextFrameStatus()
: MediaDecoder::NextFrameStatus();
}
void
MediaOmxCommonDecoder::SetVolume(double aVolume)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mAudioOffloadPlayer) {
MediaDecoder::SetVolume(aVolume);
return;
}
mAudioOffloadPlayer->SetVolume(aVolume);
}
MediaDecoderStateMachine*
MediaOmxCommonDecoder::CreateStateMachine()
{
mReader = CreateReader();
if (mReader != nullptr) {
mReader->SetAudioChannel(GetAudioChannel());
}
return CreateStateMachineFromReader(mReader);
}
void
MediaOmxCommonDecoder::Shutdown()
{
mAudioOffloadPlayer = nullptr;
MediaDecoder::Shutdown();
}
} // namespace mozilla

View File

@ -1,78 +0,0 @@
/* -*- 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 MEDIA_OMX_COMMON_DECODER_H
#define MEDIA_OMX_COMMON_DECODER_H
#include "MediaDecoder.h"
#include "nsAutoPtr.h"
namespace android {
struct MOZ_EXPORT MediaSource;
} // namespace android
namespace mozilla {
class AudioOffloadPlayerBase;
class MediaOmxCommonReader;
class MediaOmxCommonDecoder : public MediaDecoder
{
public:
explicit MediaOmxCommonDecoder(MediaDecoderOwner* aOwner);
void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
MediaDecoderEventVisibility aEventVisibility) override;
void ChangeState(PlayState aState) override;
void CallSeek(const SeekTarget& aTarget, dom::Promise* aPromise) override;
void SetVolume(double aVolume) override;
int64_t CurrentPosition() override;
MediaDecoderOwner::NextFrameStatus NextFrameStatus() override;
void SetElementVisibility(bool aIsVisible) override;
void SetPlatformCanOffloadAudio(bool aCanOffloadAudio) override;
void AddOutputStream(ProcessedMediaStream* aStream,
bool aFinishWhenEnded) override;
void SetPlaybackRate(double aPlaybackRate) override;
void AudioOffloadTearDown();
MediaDecoderStateMachine* CreateStateMachine() override;
virtual MediaOmxCommonReader* CreateReader() = 0;
virtual MediaDecoderStateMachine* CreateStateMachineFromReader(MediaOmxCommonReader* aReader) = 0;
void NotifyOffloadPlayerPositionChanged() { UpdateLogicalPosition(); }
void Shutdown() override;
protected:
virtual ~MediaOmxCommonDecoder();
void PauseStateMachine();
void ResumeStateMachine();
bool CheckDecoderCanOffloadAudio();
void DisableStateMachineAudioOffloading();
MediaOmxCommonReader* mReader;
// Offloaded audio track
android::sp<android::MediaSource> mAudioTrack;
nsAutoPtr<AudioOffloadPlayerBase> mAudioOffloadPlayer;
// Set by Media*Reader 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;
// True if the media element is captured.
bool mIsCaptured;
};
} // namespace mozilla
#endif // MEDIA_OMX_COMMON_DECODER_H

View File

@ -1,79 +0,0 @@
/* -*- 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 "MediaOmxCommonReader.h"
#include <stagefright/MediaSource.h>
#include "AbstractMediaDecoder.h"
#include "AudioChannelService.h"
#include "MediaStreamSource.h"
#include "MediaPrefs.h"
#ifdef MOZ_AUDIO_OFFLOAD
#include <stagefright/Utils.h>
#include <cutils/properties.h>
#include <stagefright/MetaData.h>
#endif
using namespace android;
namespace mozilla {
extern LazyLogModule gMediaDecoderLog;
#define DECODER_LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
MediaOmxCommonReader::MediaOmxCommonReader(AbstractMediaDecoder *aDecoder)
: MediaDecoderReader(aDecoder)
, mStreamSource(nullptr)
{
mAudioChannel = dom::AudioChannelService::GetDefaultAudioChannel();
}
bool MediaOmxCommonReader::IsMonoAudioEnabled()
{
return MediaPrefs::MonoAudio();
}
#ifdef MOZ_AUDIO_OFFLOAD
void MediaOmxCommonReader::CheckAudioOffload()
{
MOZ_ASSERT(OnTaskQueue());
char offloadProp[128];
property_get("audio.offload.disable", offloadProp, "0");
bool offloadDisable = atoi(offloadProp) != 0;
if (offloadDisable) {
return;
}
sp<MediaSource> audioOffloadTrack = GetAudioOffloadTrack();
sp<MetaData> meta = audioOffloadTrack.get()
? audioOffloadTrack->getFormat() : nullptr;
// Supporting audio offload only when there is no video, no streaming
bool hasNoVideo = !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 = mAudioChannel == dom::AudioChannel::Content;
DECODER_LOG(LogLevel::Debug, ("%s meta %p, no video %d, no streaming %d,"
" channel type %d", __FUNCTION__, meta.get(), hasNoVideo,
isNotStreaming, mAudioChannel));
if ((meta.get()) && hasNoVideo && isNotStreaming && isTypeMusic &&
canOffloadStream(meta, false, false, AUDIO_STREAM_MUSIC) &&
!IsMonoAudioEnabled()) {
DECODER_LOG(LogLevel::Debug, ("Can offload this audio stream"));
mDecoder->SetPlatformCanOffloadAudio(true);
}
}
#endif
} // namespace mozilla

View File

@ -1,60 +0,0 @@
/* -*- 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 MEDIA_OMX_COMMON_READER_H
#define MEDIA_OMX_COMMON_READER_H
#include "MediaDecoderReader.h"
#include <utils/RefBase.h>
#include "mozilla/dom/AudioChannelBinding.h"
namespace android {
struct MOZ_EXPORT MediaSource;
class MediaStreamSource;
} // namespace android
namespace mozilla {
class AbstractMediaDecoder;
class MediaOmxCommonReader : public MediaDecoderReader
{
public:
typedef MozPromise<bool /* aIgnored */, bool /* aIgnored */, /* IsExclusive = */ true> MediaResourcePromise;
MediaOmxCommonReader(AbstractMediaDecoder* aDecoder);
void SetAudioChannel(dom::AudioChannel aAudioChannel) {
mAudioChannel = aAudioChannel;
}
virtual android::sp<android::MediaSource> GetAudioOffloadTrack() = 0;
#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
protected:
dom::AudioChannel mAudioChannel;
// Weak reference to the MediaStreamSource that will be created by either
// MediaOmxReader or MediaCodecReader.
android::MediaStreamSource* mStreamSource;
// Get value from the preferece, if true, we stop the audio offload.
bool IsMonoAudioEnabled();
private:
virtual bool HasAudio() = 0;
virtual bool HasVideo() = 0;
};
} // namespace mozilla
#endif // MEDIA_OMX_COMMON_READER_H

View File

@ -1,33 +0,0 @@
/* -*- 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 "MediaOmxDecoder.h"
#include "MediaOmxReader.h"
#include "MediaDecoderStateMachine.h"
using namespace android;
namespace mozilla {
MediaDecoder*
MediaOmxDecoder::Clone(MediaDecoderOwner* aOwner)
{
return new MediaOmxDecoder(aOwner);
}
MediaOmxCommonReader*
MediaOmxDecoder::CreateReader()
{
return new MediaOmxReader(this);
}
MediaDecoderStateMachine*
MediaOmxDecoder::CreateStateMachineFromReader(MediaOmxCommonReader* aReader)
{
return new MediaDecoderStateMachine(this, aReader);
}
} // namespace mozilla

View File

@ -1,25 +0,0 @@
/* -*- 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(MediaOmxDecoder_h_)
#define MediaOmxDecoder_h_
#include "MediaOmxCommonDecoder.h"
namespace mozilla {
class MediaOmxDecoder : public MediaOmxCommonDecoder
{
public:
explicit MediaOmxDecoder(MediaDecoderOwner* aOwner)
: MediaOmxCommonDecoder(aOwner) {}
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner);
virtual MediaOmxCommonReader* CreateReader();
virtual MediaDecoderStateMachine* CreateStateMachineFromReader(MediaOmxCommonReader* aReader);
};
} // namespace mozilla
#endif

View File

@ -1,622 +0,0 @@
/* -*- 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 "MediaOmxReader.h"
#include "MediaDecoderStateMachine.h"
#include "mozilla/TimeStamp.h"
#include "MediaResource.h"
#include "VideoUtils.h"
#include "MediaOmxDecoder.h"
#include "AbstractMediaDecoder.h"
#include "AudioChannelService.h"
#include "OmxDecoder.h"
#include "MPAPI.h"
#include "gfx2DGlue.h"
#include "MediaStreamSource.h"
#include "VideoFrameContainer.h"
#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 0.1
using namespace mozilla::gfx;
using namespace mozilla::media;
using namespace android;
namespace mozilla {
extern LazyLogModule gMediaDecoderLog;
#define DECODER_LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
class MediaOmxReader::ProcessCachedDataTask : public Runnable
{
public:
ProcessCachedDataTask(MediaOmxReader* aOmxReader, int64_t aOffset)
: mOmxReader(aOmxReader),
mOffset(aOffset)
{ }
NS_IMETHOD Run() override
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(mOmxReader.get());
mOmxReader->ProcessCachedData(mOffset);
return NS_OK;
}
private:
RefPtr<MediaOmxReader> mOmxReader;
int64_t mOffset;
};
// When loading an MP3 stream from a file, we need to parse the file's
// content to find its duration. Reading files of 100 MiB or more can
// delay the player app noticably, so the file is read and decoded in
// smaller chunks.
//
// We first read on the decode thread, but parsing must be done on the
// main thread. After we read the file's initial MiBs in the decode
// thread, an instance of this class is scheduled to the main thread for
// parsing the MP3 stream. The decode thread waits until it has finished.
//
// If there is more data available from the file, the runnable dispatches
// a task to the IO thread for retrieving the next chunk of data, and
// the IO task dispatches a runnable to the main thread for parsing the
// data. This goes on until all of the MP3 file has been parsed.
class MediaOmxReader::NotifyDataArrivedRunnable : public Runnable
{
public:
NotifyDataArrivedRunnable(MediaOmxReader* aOmxReader,
uint64_t aLength,
int64_t aOffset, uint64_t aFullLength)
: mOmxReader(aOmxReader),
mLength(aLength),
mOffset(aOffset),
mFullLength(aFullLength)
{
MOZ_ASSERT(mOmxReader.get());
}
NS_IMETHOD Run() override
{
MOZ_ASSERT(mOmxReader->OnTaskQueue());
NotifyDataArrived();
return NS_OK;
}
private:
void NotifyDataArrived()
{
if (mOmxReader->IsShutdown()) {
return;
}
while (mLength) {
uint32_t length = std::min<uint64_t>(mLength, UINT32_MAX);
mOmxReader->NotifyDataArrived();
mLength -= length;
mOffset += length;
}
if (static_cast<uint64_t>(mOffset) < mFullLength) {
// We cannot read data in the main thread because it
// might block for too long. Instead we post an IO task
// to the IO thread if there is more data available.
RefPtr<Runnable> task = new ProcessCachedDataTask(mOmxReader.get(), mOffset);
XRE_GetIOMessageLoop()->PostTask(task.forget());
}
}
RefPtr<MediaOmxReader> mOmxReader;
uint64_t mLength;
int64_t mOffset;
uint64_t mFullLength;
};
MediaOmxReader::MediaOmxReader(AbstractMediaDecoder *aDecoder)
: MediaOmxCommonReader(aDecoder)
, mShutdownMutex("MediaOmxReader.Shutdown")
, mHasVideo(false)
, mHasAudio(false)
, mVideoSeekTimeUs(-1)
, mAudioSeekTimeUs(-1)
, mLastParserDuration(-1)
, mSkipCount(0)
, mIsShutdown(false)
, mMP3FrameParser(-1)
{
mAudioChannel = dom::AudioChannelService::GetDefaultAudioChannel();
}
MediaOmxReader::~MediaOmxReader()
{
}
already_AddRefed<AbstractMediaDecoder>
MediaOmxReader::SafeGetDecoder() {
RefPtr<AbstractMediaDecoder> decoder;
MutexAutoLock lock(mShutdownMutex);
if (!mIsShutdown) {
decoder = mDecoder;
}
return decoder.forget();
}
void MediaOmxReader::ReleaseDecoder()
{
if (mOmxDecoder.get()) {
mOmxDecoder->ReleaseDecoder();
}
mOmxDecoder.clear();
}
RefPtr<ShutdownPromise>
MediaOmxReader::Shutdown()
{
{
MutexAutoLock lock(mShutdownMutex);
mIsShutdown = true;
}
RefPtr<ShutdownPromise> p = MediaDecoderReader::Shutdown();
// Wait for the superclass to finish tearing things down before releasing
// the decoder on the main thread.
p->Then(AbstractThread::MainThread(), __func__, this, &MediaOmxReader::ReleaseDecoder, &MediaOmxReader::ReleaseDecoder);
return p;
}
void MediaOmxReader::ReleaseResources()
{
mMediaResourceRequest.DisconnectIfExists();
mMetadataPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
ResetDecode();
// Before freeing a video codec, all video buffers needed to be released
// even from graphics pipeline.
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
if (container) {
container->ClearCurrentFrame();
}
if (mOmxDecoder.get()) {
mOmxDecoder->ReleaseMediaResources();
}
}
nsresult MediaOmxReader::InitOmxDecoder()
{
if (!mOmxDecoder.get()) {
//register sniffers, if they are not registered in this process.
DataSource::RegisterDefaultSniffers();
sp<DataSource> dataSource = new MediaStreamSource(mDecoder->GetResource());
dataSource->initCheck();
mExtractor = MediaExtractor::Create(dataSource);
if (!mExtractor.get()) {
return NS_ERROR_FAILURE;
}
mOmxDecoder = new OmxDecoder(mDecoder, OwnerThread());
if (!mOmxDecoder->Init(mExtractor)) {
return NS_ERROR_FAILURE;
}
mStreamSource = static_cast<MediaStreamSource*>(dataSource.get());
}
return NS_OK;
}
RefPtr<MediaDecoderReader::MetadataPromise>
MediaOmxReader::AsyncReadMetadata()
{
MOZ_ASSERT(OnTaskQueue());
EnsureActive();
// Initialize the internal OMX Decoder.
nsresult rv = InitOmxDecoder();
if (NS_FAILED(rv)) {
return MediaDecoderReader::MetadataPromise::CreateAndReject(
NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
}
bool isMP3 = mDecoder->GetResource()->GetContentType().EqualsASCII(AUDIO_MP3);
if (isMP3) {
// When read sdcard's file on b2g platform at constructor,
// the mDecoder->GetResource()->GetLength() would return -1.
// Delay set the total duration on this function.
mMP3FrameParser.SetLength(mDecoder->GetResource()->GetLength());
ProcessCachedData(0);
}
RefPtr<MediaDecoderReader::MetadataPromise> p = mMetadataPromise.Ensure(__func__);
RefPtr<MediaOmxReader> self = this;
mMediaResourceRequest.Begin(mOmxDecoder->AllocateMediaResources()
->Then(OwnerThread(), __func__,
[self] (bool) -> void {
self->mMediaResourceRequest.Complete();
self->HandleResourceAllocated();
}, [self] (bool) -> void {
self->mMediaResourceRequest.Complete();
self->mMetadataPromise.Reject(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
}));
return p;
}
void MediaOmxReader::HandleResourceAllocated()
{
EnsureActive();
// After resources are available, set the metadata.
if (!mOmxDecoder->EnsureMetadata()) {
mMetadataPromise.Reject(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
return;
}
bool isMP3 = mDecoder->GetResource()->GetContentType().EqualsASCII(AUDIO_MP3);
if (isMP3 && mMP3FrameParser.IsMP3()) {
// Check if the MP3 frame parser found a duration.
mLastParserDuration = mMP3FrameParser.GetDuration();
}
if (mLastParserDuration >= 0) {
// Prefer the parser duration if we have it.
mInfo.mMetadataDuration = Some(TimeUnit::FromMicroseconds(mLastParserDuration));
} else {
// MP3 parser failed to find a duration.
// Set the total duration (the max of the audio and video track).
int64_t durationUs;
mOmxDecoder->GetDuration(&durationUs);
if (durationUs) {
mInfo.mMetadataDuration = Some(TimeUnit::FromMicroseconds(durationUs));
}
}
if (mOmxDecoder->HasVideo()) {
int32_t displayWidth, displayHeight, width, height;
mOmxDecoder->GetVideoParameters(&displayWidth, &displayHeight,
&width, &height);
nsIntRect pictureRect(0, 0, width, height);
// Validate the container-reported frame and pictureRect sizes. This ensures
// that our video frame creation code doesn't overflow.
nsIntSize displaySize(displayWidth, displayHeight);
nsIntSize frameSize(width, height);
if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
mMetadataPromise.Reject(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
return;
}
// Video track's frame sizes will not overflow. Activate the video track.
mHasVideo = true;
mInfo.mVideo.mDisplay = displaySize;
mPicture = pictureRect;
mInitialFrame = frameSize;
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
if (container) {
container->ClearCurrentFrame(IntSize(displaySize.width, displaySize.height));
}
}
if (mOmxDecoder->HasAudio()) {
int32_t numChannels, sampleRate;
mOmxDecoder->GetAudioParameters(&numChannels, &sampleRate);
mHasAudio = true;
mInfo.mAudio.mChannels = numChannels;
mInfo.mAudio.mRate = sampleRate;
}
mInfo.mMediaSeekable = mExtractor->flags() & MediaExtractor::CAN_SEEK;
RefPtr<MetadataHolder> metadata = new MetadataHolder();
metadata->mInfo = mInfo;
metadata->mTags = nullptr;
#ifdef MOZ_AUDIO_OFFLOAD
CheckAudioOffload();
#endif
mMetadataPromise.Resolve(metadata, __func__);
}
bool MediaOmxReader::DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold)
{
MOZ_ASSERT(OnTaskQueue());
EnsureActive();
// Record number of frames decoded and parsed. Automatically update the
// stats counters using the AutoNotifyDecoded stack-based class.
AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder);
bool doSeek = mVideoSeekTimeUs != -1;
if (doSeek) {
aTimeThreshold = mVideoSeekTimeUs;
}
TimeStamp start = TimeStamp::Now();
// Read next frame. Don't let this loop run for too long.
while ((TimeStamp::Now() - start) < TimeDuration::FromSeconds(MAX_VIDEO_DECODE_SECONDS)) {
MPAPI::VideoFrame frame;
frame.mGraphicBuffer = nullptr;
frame.mShouldSkip = false;
if (!mOmxDecoder->ReadVideo(&frame, aTimeThreshold, aKeyframeSkip, doSeek)) {
return false;
}
doSeek = false;
mVideoSeekTimeUs = -1;
// Ignore empty buffer which stagefright media read will sporadically return
if (frame.mSize == 0 && !frame.mGraphicBuffer) {
continue;
}
a.mStats.mParsedFrames++;
if (frame.mShouldSkip && mSkipCount < MAX_DROPPED_FRAMES) {
mSkipCount++;
continue;
}
mSkipCount = 0;
aKeyframeSkip = false;
IntRect picture = mPicture;
if (frame.Y.mWidth != mInitialFrame.width ||
frame.Y.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 * frame.Y.mWidth) / mInitialFrame.width;
picture.y = (mPicture.y * frame.Y.mHeight) / mInitialFrame.height;
picture.width = (frame.Y.mWidth * mPicture.width) / mInitialFrame.width;
picture.height = (frame.Y.mHeight * mPicture.height) / mInitialFrame.height;
}
// This is the approximate byte position in the stream.
int64_t pos = mStreamSource ? mStreamSource->Tell() : 0;
RefPtr<VideoData> v;
if (!frame.mGraphicBuffer) {
VideoData::YCbCrBuffer b;
b.mPlanes[0].mData = static_cast<uint8_t *>(frame.Y.mData);
b.mPlanes[0].mStride = frame.Y.mStride;
b.mPlanes[0].mHeight = frame.Y.mHeight;
b.mPlanes[0].mWidth = frame.Y.mWidth;
b.mPlanes[0].mOffset = frame.Y.mOffset;
b.mPlanes[0].mSkip = frame.Y.mSkip;
b.mPlanes[1].mData = static_cast<uint8_t *>(frame.Cb.mData);
b.mPlanes[1].mStride = frame.Cb.mStride;
b.mPlanes[1].mHeight = frame.Cb.mHeight;
b.mPlanes[1].mWidth = frame.Cb.mWidth;
b.mPlanes[1].mOffset = frame.Cb.mOffset;
b.mPlanes[1].mSkip = frame.Cb.mSkip;
b.mPlanes[2].mData = static_cast<uint8_t *>(frame.Cr.mData);
b.mPlanes[2].mStride = frame.Cr.mStride;
b.mPlanes[2].mHeight = frame.Cr.mHeight;
b.mPlanes[2].mWidth = frame.Cr.mWidth;
b.mPlanes[2].mOffset = frame.Cr.mOffset;
b.mPlanes[2].mSkip = frame.Cr.mSkip;
v = VideoData::CreateAndCopyData(mInfo.mVideo,
mDecoder->GetImageContainer(),
pos,
frame.mTimeUs,
1, // We don't know the duration.
b,
frame.mKeyFrame,
-1,
picture);
} else {
v = VideoData::CreateAndCopyIntoTextureClient(
mInfo.mVideo,
pos,
frame.mTimeUs,
1, // We don't know the duration.
frame.mGraphicBuffer,
frame.mKeyFrame,
-1,
picture);
}
if (!v) {
NS_WARNING("Unable to create VideoData");
return false;
}
a.mStats.mDecodedFrames++;
NS_ASSERTION(a.mStats.mDecodedFrames <= a.mStats.mParsedFrames, "Expect to decode fewer frames than parsed in OMX decoder...");
mVideoQueue.Push(v);
break;
}
return true;
}
void MediaOmxReader::NotifyDataArrivedInternal()
{
MOZ_ASSERT(OnTaskQueue());
RefPtr<AbstractMediaDecoder> decoder = SafeGetDecoder();
if (!decoder) { // reader has shut down
return;
}
if (HasVideo()) {
return;
}
if (!mMP3FrameParser.NeedsData()) {
return;
}
AutoPinned<MediaResource> resource(mDecoder->GetResource());
MediaByteRangeSet byteRanges;
nsresult rv = resource->GetCachedRanges(byteRanges);
if (NS_FAILED(rv)) {
return;
}
if (byteRanges == mLastCachedRanges) {
return;
}
MediaByteRangeSet intervals = byteRanges - mLastCachedRanges;
mLastCachedRanges = byteRanges;
for (const auto& interval : intervals) {
RefPtr<MediaByteBuffer> bytes =
resource->MediaReadAt(interval.mStart, interval.Length());
NS_ENSURE_TRUE_VOID(bytes);
mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart);
if (!mMP3FrameParser.IsMP3()) {
return;
}
}
int64_t duration = mMP3FrameParser.GetDuration();
if (duration != mLastParserDuration) {
mLastParserDuration = duration;
decoder->DispatchUpdateEstimatedMediaDuration(mLastParserDuration);
}
}
bool MediaOmxReader::DecodeAudioData()
{
MOZ_ASSERT(OnTaskQueue());
EnsureActive();
// This is the approximate byte position in the stream.
int64_t pos = mStreamSource ? mStreamSource->Tell() : 0;
// Read next frame
MPAPI::AudioFrame source;
if (!mOmxDecoder->ReadAudio(&source, mAudioSeekTimeUs)) {
return false;
}
mAudioSeekTimeUs = -1;
// Ignore empty buffer which stagefright media read will sporadically return
if (source.mSize == 0) {
return true;
}
uint32_t frames = source.mSize / (source.mAudioChannels *
sizeof(AudioDataValue));
typedef AudioCompactor::NativeCopy OmxCopy;
return mAudioCompactor.Push(pos,
source.mTimeUs,
source.mAudioSampleRate,
frames,
source.mAudioChannels,
OmxCopy(static_cast<uint8_t *>(source.mData),
source.mSize,
source.mAudioChannels));
}
RefPtr<MediaDecoderReader::SeekPromise>
MediaOmxReader::Seek(SeekTarget aTarget, int64_t aEndTime)
{
MOZ_ASSERT(OnTaskQueue());
EnsureActive();
RefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__);
if (mHasAudio && mHasVideo) {
// The OMXDecoder seeks/demuxes audio and video streams separately. So if
// we seek both audio and video to aTarget, the audio stream can typically
// seek closer to the seek target, since typically every audio block is
// a sync point, whereas for video there are only keyframes once every few
// seconds. So if we have both audio and video, we must seek the video
// stream to the preceeding keyframe first, get the stream time, and then
// seek the audio stream to match the video stream's time. Otherwise, the
// audio and video streams won't be in sync after the seek.
mVideoSeekTimeUs = aTarget.GetTime().ToMicroseconds();
RefPtr<MediaOmxReader> self = this;
mSeekRequest.Begin(DecodeToFirstVideoData()->Then(OwnerThread(), __func__, [self] (MediaData* v) {
self->mSeekRequest.Complete();
self->mAudioSeekTimeUs = v->mTime;
self->mSeekPromise.Resolve(media::TimeUnit::FromMicroseconds(self->mAudioSeekTimeUs), __func__);
}, [self, aTarget] () {
self->mSeekRequest.Complete();
self->mAudioSeekTimeUs = aTarget.GetTime().ToMicroseconds();
self->mSeekPromise.Resolve(aTarget.GetTime(), __func__);
}));
} else {
mAudioSeekTimeUs = mVideoSeekTimeUs = aTarget.GetTime().ToMicroseconds();
mSeekPromise.Resolve(aTarget.GetTime(), __func__);
}
return p;
}
void MediaOmxReader::SetIdle() {
if (!mOmxDecoder.get()) {
return;
}
mOmxDecoder->Pause();
}
void MediaOmxReader::EnsureActive() {
if (!mOmxDecoder.get()) {
return;
}
DebugOnly<nsresult> result = mOmxDecoder->Play();
NS_ASSERTION(result == NS_OK, "OmxDecoder should be in play state to continue decoding");
}
int64_t MediaOmxReader::ProcessCachedData(int64_t aOffset)
{
// Could run on decoder thread or IO thread.
RefPtr<AbstractMediaDecoder> decoder = SafeGetDecoder();
if (!decoder) { // reader has shut down
return -1;
}
// We read data in chunks of 32 KiB. We can reduce this
// value if media, such as sdcards, is too slow.
// Because of SD card's slowness, need to keep sReadSize to small size.
// See Bug 914870.
static const int64_t sReadSize = 32 * 1024;
NS_ASSERTION(!NS_IsMainThread(), "Should not be on main thread.");
MOZ_ASSERT(decoder->GetResource());
int64_t resourceLength = decoder->GetResource()->GetCachedDataEnd(0);
NS_ENSURE_TRUE(resourceLength >= 0, -1);
if (aOffset >= resourceLength) {
return 0; // Cache is empty, nothing to do
}
int64_t bufferLength = std::min<int64_t>(resourceLength-aOffset, sReadSize);
RefPtr<NotifyDataArrivedRunnable> runnable(
new NotifyDataArrivedRunnable(this, bufferLength, aOffset, resourceLength));
if (OnTaskQueue()) {
runnable->Run();
} else {
OwnerThread()->Dispatch(runnable.forget());
}
return resourceLength - aOffset - bufferLength;
}
android::sp<android::MediaSource> MediaOmxReader::GetAudioOffloadTrack()
{
if (!mOmxDecoder.get()) {
return nullptr;
}
return mOmxDecoder->GetAudioOffloadTrack();
}
} // namespace mozilla

View File

@ -1,127 +0,0 @@
/* -*- 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(MediaOmxReader_h_)
#define MediaOmxReader_h_
#include "MediaOmxCommonReader.h"
#include "MediaResource.h"
#include "MediaDecoderReader.h"
#include "nsMimeTypes.h"
#include "MP3FrameParser.h"
#include "nsRect.h"
#include <ui/GraphicBuffer.h>
#include <stagefright/MediaSource.h>
namespace android {
class OmxDecoder;
class MOZ_EXPORT MediaExtractor;
}
namespace mozilla {
class AbstractMediaDecoder;
class MediaOmxReader : public MediaOmxCommonReader
{
typedef MediaOmxCommonReader::MediaResourcePromise MediaResourcePromise;
// This mutex is held when accessing the mIsShutdown variable, which is
// modified on the decode task queue and read on main and IO threads.
Mutex mShutdownMutex;
nsCString mType;
bool mHasVideo;
bool mHasAudio;
nsIntRect mPicture;
nsIntSize mInitialFrame;
int64_t mVideoSeekTimeUs;
int64_t mAudioSeekTimeUs;
int64_t mLastParserDuration;
int32_t mSkipCount;
// If mIsShutdown is false, and mShutdownMutex is held, then
// AbstractMediaDecoder::mDecoder will be non-null.
bool mIsShutdown;
MozPromiseHolder<MediaDecoderReader::MetadataPromise> mMetadataPromise;
MozPromiseRequestHolder<MediaResourcePromise> mMediaResourceRequest;
MozPromiseHolder<MediaDecoderReader::SeekPromise> mSeekPromise;
MozPromiseRequestHolder<MediaDecoderReader::MediaDataPromise> mSeekRequest;
protected:
android::sp<android::OmxDecoder> mOmxDecoder;
android::sp<android::MediaExtractor> mExtractor;
MP3FrameParser mMP3FrameParser;
// Called by ReadMetadata() during MediaDecoderStateMachine::DecodeMetadata()
// on decode thread. It create and initialize the OMX decoder including
// setting up custom extractor. The extractor provide the essential
// information used for creating OMX decoder such as video/audio codec.
virtual nsresult InitOmxDecoder();
// Called inside DecodeVideoFrame, DecodeAudioData, ReadMetadata and Seek
// to activate the decoder automatically.
virtual void EnsureActive();
virtual void HandleResourceAllocated();
public:
MediaOmxReader(AbstractMediaDecoder* aDecoder);
~MediaOmxReader();
protected:
void NotifyDataArrivedInternal() override;
public:
nsresult ResetDecode(
TrackSet aTracks = TrackSet(TrackInfo::kAudioTrack,
TrackInfo::kVideoTrack)) override
{
mSeekRequest.DisconnectIfExists();
mSeekPromise.RejectIfExists(NS_OK, __func__);
return MediaDecoderReader::ResetDecode(aTracks);
}
bool DecodeAudioData() override;
bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold) override;
void ReleaseResources() override;
RefPtr<MediaDecoderReader::MetadataPromise> AsyncReadMetadata() override;
RefPtr<SeekPromise>
Seek(SeekTarget aTarget, int64_t aEndTime) override;
void SetIdle() override;
RefPtr<ShutdownPromise> Shutdown() override;
android::sp<android::MediaSource> GetAudioOffloadTrack();
// This method is intended only for private use but public only for
// MozPromise::InvokeCallbackMethod().
void ReleaseDecoder();
private:
class ProcessCachedDataTask;
class NotifyDataArrivedRunnable;
bool HasAudio() override { return mHasAudio; }
bool HasVideo() override { return mHasVideo; }
bool IsShutdown() {
MutexAutoLock lock(mShutdownMutex);
return mIsShutdown;
}
int64_t ProcessCachedData(int64_t aOffset);
already_AddRefed<AbstractMediaDecoder> SafeGetDecoder();
MediaByteRangeSet mLastCachedRanges;
};
} // namespace mozilla
#endif

View File

@ -1,71 +0,0 @@
/* -*- 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 "MediaStreamSource.h"
#include <utils/threads.h>
#include "nsISeekableStream.h"
namespace android {
MediaStreamSource::MediaStreamSource(MediaResource *aResource)
: mResource(aResource)
{
}
MediaStreamSource::~MediaStreamSource()
{
}
status_t MediaStreamSource::initCheck() const
{
return OK;
}
ssize_t MediaStreamSource::readAt(off64_t offset, void *data, size_t size)
{
char *ptr = static_cast<char *>(data);
size_t todo = size;
while (todo > 0) {
Mutex::Autolock autoLock(mLock);
uint32_t bytesRead;
if ((offset != mResource.Tell() &&
NS_FAILED(mResource.Seek(nsISeekableStream::NS_SEEK_SET, offset))) ||
NS_FAILED(mResource.Read(ptr, todo, &bytesRead))) {
return ERROR_IO;
}
if (bytesRead == 0) {
return size - todo;
}
offset += bytesRead;
todo -= bytesRead;
ptr += bytesRead;
}
return size;
}
status_t MediaStreamSource::getSize(off64_t *size)
{
uint64_t length = mResource.GetLength();
if (length == static_cast<uint64_t>(-1))
return ERROR_UNSUPPORTED;
*size = length;
return OK;
}
int64_t
MediaStreamSource::Tell()
{
Mutex::Autolock autoLock(mLock);
return mResource.Tell();
}
} // namespace android

View File

@ -1,47 +0,0 @@
/* -*- 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 MEDIA_STREAM_SOURCE_H
#define MEDIA_STREAM_SOURCE_H
#include <stdint.h>
#include <stagefright/DataSource.h>
#include <stagefright/MediaSource.h>
#include "MediaResource.h"
namespace android {
// MediaStreamSource is a DataSource that reads from a MPAPI media stream.
class MediaStreamSource : public DataSource {
typedef mozilla::MediaResource MediaResource;
typedef mozilla::MediaResourceIndex MediaResourceIndex;
Mutex mLock;
MediaResourceIndex mResource;
public:
MediaStreamSource(MediaResource* aResource);
status_t initCheck() const override;
ssize_t readAt(off64_t offset, void *data, size_t size) override;
status_t getSize(off64_t *size) override;
uint32_t flags() override {
return kWantsPrefetching;
}
int64_t Tell();
virtual ~MediaStreamSource();
private:
MediaStreamSource(const MediaStreamSource &);
MediaStreamSource &operator=(const MediaStreamSource &);
};
} // namespace android
#endif // MEDIA_STREAM_SOURCE_H

View File

@ -1,204 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* 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 "OMXCodecDescriptorUtil.h"
namespace android {
// The utility functions in this file concatenate two AVC/H.264 parameter sets,
// sequence parameter set(SPS) and picture parameter set(PPS), into byte stream
// format or construct AVC decoder config descriptor blob from them.
//
// * NAL unit defined in ISO/IEC 14496-10 7.3.1
// * SPS defined ISO/IEC 14496-10 7.3.2.1.1
// * PPS defined in ISO/IEC 14496-10 7.3.2.2
//
// Byte stream format:
// Start code <0x00 0x00 0x00 0x01> (4 bytes)
// --- (SPS) NAL unit ---
// ... (3 bits)
// NAL unit type <0x07> (5 bits)
// SPS (3+ bytes)
// Profile (1 byte)
// Compatible profiles (1 byte)
// Level (1 byte)
// ...
// --- End ---
// Start code <0x00 0x00 0x00 0x01> (4 bytes)
// --- (PPS) NAL unit ---
// ... (3 bits)
// NAL unit type <0x08> (5 bits)
// PPS (1+ bytes)
// ...
// --- End ---
//
// Descriptor format: (defined in ISO/IEC 14496-15 5.2.4.1.1)
// --- Header (5 bytes) ---
// Version <0x01> (1 byte)
// Profile (1 byte)
// Compatible profiles (1 byte)
// Level (1 byte)
// Reserved <111111> (6 bits)
// NAL length type (2 bits)
// --- Parameter sets ---
// Reserved <111> (3 bits)
// Number of SPS (5 bits)
// SPS (3+ bytes)
// Length (2 bytes)
// SPS NAL unit (1+ bytes)
// ...
// Number of PPS (1 byte)
// PPS (3+ bytes)
// Length (2 bytes)
// PPS NAL unit (1+ bytes)
// ...
// --- End ---
// NAL unit start code.
static const uint8_t kNALUnitStartCode[] = { 0x00, 0x00, 0x00, 0x01 };
// NAL unit types.
enum {
kNALUnitTypeSPS = 0x07, // Value for sequence parameter set.
kNALUnitTypePPS = 0x08, // Value for picture parameter set.
kNALUnitTypeBad = -1, // Malformed data.
};
// Sequence parameter set or picture parameter set.
struct AVCParamSet {
AVCParamSet(const uint8_t* aPtr, const size_t aSize)
: mPtr(aPtr)
, mSize(aSize)
{
MOZ_ASSERT(mPtr && mSize > 0);
}
size_t Size() {
return mSize + 2; // 2 more bytes for length value.
}
// Append 2 bytes length value and NAL unit bitstream to aOutputBuf.
void AppendTo(nsTArray<uint8_t>* aOutputBuf)
{
// 2 bytes length value.
uint8_t size[] = {
uint8_t((mSize & 0xFF00) >> 8), // MSB.
uint8_t(mSize & 0x00FF), // LSB.
};
aOutputBuf->AppendElements(size, sizeof(size));
aOutputBuf->AppendElements(mPtr, mSize);
}
const uint8_t* mPtr; // Pointer to NAL unit.
const size_t mSize; // NAL unit length in bytes.
};
// Convert SPS and PPS data into decoder config descriptor blob. The generated
// blob will be appended to aOutputBuf.
static status_t
ConvertParamSetsToDescriptorBlob(sp<ABuffer>& aSPS, sp<ABuffer>& aPPS,
nsTArray<uint8_t>* aOutputBuf)
{
// Strip start code in the input.
AVCParamSet sps(aSPS->data() + sizeof(kNALUnitStartCode),
aSPS->size() - sizeof(kNALUnitStartCode));
AVCParamSet pps(aPPS->data() + sizeof(kNALUnitStartCode),
aPPS->size() - sizeof(kNALUnitStartCode));
size_t paramSetsSize = sps.Size() + pps.Size();
// Profile/level info in SPS.
uint8_t* info = aSPS->data() + 5;
uint8_t header[] = {
0x01, // Version.
info[0], // Profile.
info[1], // Compatible profiles.
info[2], // Level.
0xFF, // 6 bits reserved value <111111> + 2 bits NAL length type <11>
};
// Reserve 1 byte for number of SPS & another 1 for number of PPS.
aOutputBuf->SetCapacity(sizeof(header) + paramSetsSize + 2);
// Build the blob.
aOutputBuf->AppendElements(header, sizeof(header)); // 5 bytes Header.
aOutputBuf->AppendElement(0xE0 | 1); // 3 bits <111> + 5 bits number of SPS.
sps.AppendTo(aOutputBuf); // SPS NALU data.
aOutputBuf->AppendElement(1); // 1 byte number of PPS.
pps.AppendTo(aOutputBuf); // PPS NALU data.
return OK;
}
static int
NALType(sp<ABuffer>& aBuffer)
{
if (aBuffer == nullptr) {
return kNALUnitTypeBad;
}
// Start code?
uint8_t* data = aBuffer->data();
if (aBuffer->size() <= 4 ||
memcmp(data, kNALUnitStartCode, sizeof(kNALUnitStartCode))) {
return kNALUnitTypeBad;
}
return data[4] & 0x1F;
}
// Generate AVC/H.264 decoder config blob.
// See MPEG4Writer::Track::makeAVCCodecSpecificData() and
// MPEG4Writer::Track::writeAvccBox() implementation in libstagefright.
status_t
GenerateAVCDescriptorBlob(sp<AMessage>& aConfigData,
nsTArray<uint8_t>* aOutputBuf,
OMXVideoEncoder::BlobFormat aFormat)
{
// Search for parameter sets using key "csd-0" and "csd-1".
char key[6] = "csd-";
sp<ABuffer> sps;
sp<ABuffer> pps;
for (int i = 0; i < 2; i++) {
snprintf(key + 4, 2, "%d", i);
sp<ABuffer> paramSet;
bool found = aConfigData->findBuffer(key, &paramSet);
int type = NALType(paramSet);
bool valid = ((type == kNALUnitTypeSPS) || (type == kNALUnitTypePPS));
MOZ_ASSERT(found && valid);
if (!found || !valid) {
return ERROR_MALFORMED;
}
switch (type) {
case kNALUnitTypeSPS:
sps = paramSet;
break;
case kNALUnitTypePPS:
pps = paramSet;
break;
default:
NS_NOTREACHED("Should not get here!");
}
}
MOZ_ASSERT(sps != nullptr && pps != nullptr);
if (sps == nullptr || pps == nullptr) {
return ERROR_MALFORMED;
}
if (aFormat == OMXVideoEncoder::BlobFormat::AVC_NAL) {
// SPS + PPS.
aOutputBuf->AppendElements(sps->data(), sps->size());
aOutputBuf->AppendElements(pps->data(), pps->size());
return OK;
} else {
status_t result = ConvertParamSetsToDescriptorBlob(sps, pps, aOutputBuf);
MOZ_ASSERT(result == OK);
return result;
}
}
} // namespace android

View File

@ -1,27 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* 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 OMXCodecDescriptorUtil_h_
#define OMXCodecDescriptorUtil_h_
#include <stagefright/foundation/AMessage.h>
#include <stagefright/MediaErrors.h>
#include <nsTArray.h>
#include "OMXCodecWrapper.h"
namespace android {
// Generate decoder config blob using aConfigData provided by encoder.
// The output will be stored in aOutputBuf.
// aFormat specifies the output format: AVC_MP4 is for MP4 file, and AVC_NAL is
// for RTP packet used by WebRTC.
status_t GenerateAVCDescriptorBlob(sp<AMessage>& aConfigData,
nsTArray<uint8_t>* aOutputBuf,
OMXVideoEncoder::BlobFormat aFormat);
}
#endif // OMXCodecDescriptorUtil_h_

View File

@ -1,244 +0,0 @@
/* -*- 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/foundation/ADebug.h>
#include <stagefright/MetaData.h>
#include <stagefright/OMXCodec.h>
#include <utils/Log.h>
#include "nsDebug.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 nullptr;
}
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),
mComponentName(nullptr),
mIsEncoder(createEncoder),
mFlags(flags),
mNativeWindow(nativeWindow),
mSource(source),
mState(ResourceState::START)
{
}
OMXCodecProxy::~OMXCodecProxy()
{
// At first, release mResourceClient's resource to prevent a conflict with
// mResourceClient's callback.
if (mResourceClient) {
mResourceClient->ReleaseResource();
mResourceClient = nullptr;
}
mState = ResourceState::END;
mCodecPromise.RejectIfExists(true, __func__);
if (mOMXCodec.get()) {
wp<MediaSource> tmp = mOMXCodec;
mOMXCodec.clear();
while (tmp.promote() != nullptr) {
// this value come from stagefrigh's AwesomePlayer.
usleep(1000);
}
}
// Complete all pending Binder ipc transactions
IPCThreadState::self()->flushCommands();
mSource.clear();
free(mComponentName);
mComponentName = nullptr;
}
RefPtr<OMXCodecProxy::CodecPromise>
OMXCodecProxy::requestResource()
{
Mutex::Autolock autoLock(mLock);
if (mResourceClient) {
return CodecPromise::CreateAndReject(true, __func__);
}
mState = ResourceState::WAITING;
mozilla::MediaSystemResourceType type = mIsEncoder ? mozilla::MediaSystemResourceType::VIDEO_ENCODER :
mozilla::MediaSystemResourceType::VIDEO_DECODER;
mResourceClient = new mozilla::MediaSystemResourceClient(type);
mResourceClient->SetListener(this);
mResourceClient->Acquire();
RefPtr<CodecPromise> p = mCodecPromise.Ensure(__func__);
return p.forget();
}
// Called on ImageBridge thread
void
OMXCodecProxy::ResourceReserved()
{
Mutex::Autolock autoLock(mLock);
if (mState != ResourceState::WAITING) {
mCodecPromise.RejectIfExists(true, __func__);
return;
}
const char *mime;
if (!mSrcMeta->findCString(kKeyMIMEType, &mime)) {
mState = ResourceState::END;
mCodecPromise.RejectIfExists(true, __func__);
return;
}
if (!strncasecmp(mime, "video/", 6)) {
sp<MediaSource> codec;
mOMXCodec = OMXCodec::Create(mOMX, mSrcMeta, mIsEncoder, mSource, mComponentName, mFlags, mNativeWindow);
if (mOMXCodec == nullptr) {
mState = ResourceState::END;
mCodecPromise.RejectIfExists(true, __func__);
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);
mOMXCodec.clear();
mState = ResourceState::END;
mCodecPromise.RejectIfExists(true, __func__);
return;
}
if (mOMXCodec->start() != OK) {
NS_WARNING("Couldn't start OMX video source");
mOMXCodec.clear();
mState = ResourceState::END;
mCodecPromise.RejectIfExists(true, __func__);
return;
}
}
mState = ResourceState::ACQUIRED;
mCodecPromise.ResolveIfExists(true, __func__);
}
// Called on ImageBridge thread
void
OMXCodecProxy::ResourceReserveFailed()
{
Mutex::Autolock autoLock(mLock);
mState = ResourceState::NOT_ACQUIRED;
mCodecPromise.RejectIfExists(true, __func__);
}
status_t
OMXCodecProxy::start(MetaData *params)
{
Mutex::Autolock autoLock(mLock);
if (mState != ResourceState::ACQUIRED) {
return NO_INIT;
}
CHECK(mOMXCodec.get() != nullptr);
return mOMXCodec->start();
}
status_t
OMXCodecProxy::stop()
{
Mutex::Autolock autoLock(mLock);
if (mState != ResourceState::ACQUIRED) {
return NO_INIT;
}
CHECK(mOMXCodec.get() != nullptr);
return mOMXCodec->stop();
}
sp<MetaData>
OMXCodecProxy::getFormat()
{
Mutex::Autolock autoLock(mLock);
if (mState != ResourceState::ACQUIRED) {
sp<MetaData> meta = new MetaData;
return meta;
}
CHECK(mOMXCodec.get() != nullptr);
return mOMXCodec->getFormat();
}
status_t
OMXCodecProxy::read(MediaBuffer **buffer, const ReadOptions *options)
{
Mutex::Autolock autoLock(mLock);
if (mState != ResourceState::ACQUIRED) {
return NO_INIT;
}
CHECK(mOMXCodec.get() != nullptr);
return mOMXCodec->read(buffer, options);
}
status_t
OMXCodecProxy::pause()
{
Mutex::Autolock autoLock(mLock);
if (mState != ResourceState::ACQUIRED) {
return NO_INIT;
}
CHECK(mOMXCodec.get() != nullptr);
return mOMXCodec->pause();
}
} // namespace android

View File

@ -1,101 +0,0 @@
/* -*- 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 <media/IOMX.h>
#include <stagefright/MediaBuffer.h>
#include <stagefright/MediaSource.h>
#include <utils/threads.h>
#include "mozilla/media/MediaSystemResourceClient.h"
#include "mozilla/MozPromise.h"
#include "mozilla/RefPtr.h"
namespace android {
struct MetaData;
class OMXCodecProxy : public MediaSource
, public mozilla::MediaSystemResourceReservationListener
{
public:
typedef mozilla::MozPromise<bool /* aIgnored */, bool /* aIgnored */, /* IsExclusive = */ true> CodecPromise;
// Enumeration for the valid resource allcoation states
enum class ResourceState : int8_t {
START,
WAITING,
ACQUIRED,
NOT_ACQUIRED,
END
};
static sp<OMXCodecProxy> Create(
const sp<IOMX> &omx,
const sp<MetaData> &meta, bool createEncoder,
const sp<MediaSource> &source,
const char *matchComponentName = nullptr,
uint32_t flags = 0,
const sp<ANativeWindow> &nativeWindow = nullptr);
RefPtr<CodecPromise> requestResource();
// MediaSystemResourceReservationListener
void ResourceReserved() override;
void ResourceReserveFailed() override;
// MediaSource
status_t start(MetaData *params = nullptr) override;
status_t stop() override;
sp<MetaData> getFormat() override;
status_t read(
MediaBuffer **buffer, const ReadOptions *options = nullptr) override;
status_t pause() override;
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();
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;
RefPtr<mozilla::MediaSystemResourceClient> mResourceClient;
ResourceState mState;
mozilla::MozPromiseHolder<CodecPromise> mCodecPromise;
};
} // namespace android
#endif // OMX_CODEC_PROXY_DECODER_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,368 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* 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 OMXCodecWrapper_h_
#define OMXCodecWrapper_h_
#include <gui/Surface.h>
#include <utils/RefBase.h>
#include <stagefright/foundation/ABuffer.h>
#include <stagefright/foundation/AMessage.h>
#include <stagefright/MediaCodec.h>
#include "AudioSegment.h"
#include "GonkNativeWindow.h"
#include "mozilla/media/MediaSystemResourceClient.h"
#include "mozilla/RefPtr.h"
#include <speex/speex_resampler.h>
namespace android {
// Wrapper class for managing HW codec reservations
class OMXCodecReservation : public RefBase
{
public:
OMXCodecReservation(bool aEncoder) : mOwned(false)
{
mType = aEncoder ? mozilla::MediaSystemResourceType::VIDEO_ENCODER :
mozilla::MediaSystemResourceType::VIDEO_DECODER;
}
virtual ~OMXCodecReservation()
{
ReleaseOMXCodec();
}
/** Reserve the Encode or Decode resource for this instance */
virtual bool ReserveOMXCodec();
/** Release the Encode or Decode resource for this instance */
virtual void ReleaseOMXCodec();
private:
mozilla::MediaSystemResourceType mType;
bool mOwned; // We already own this resource
RefPtr<mozilla::MediaSystemResourceClient> mClient;
};
class OMXAudioEncoder;
class OMXVideoEncoder;
/**
* This class (and its subclasses) wraps the video and audio codec from
* MediaCodec API in libstagefright. Currently only AVC/H.264 video encoder and
* AAC audio encoder are supported.
*
* OMXCodecWrapper has static creator functions that returns actual codec
* instances for different types of codec supported and serves as superclass to
* provide a function to read encoded data as byte array from codec. Two
* subclasses, OMXAudioEncoder and OMXVideoEncoder, respectively provides
* functions for encoding data from audio and video track.
*
* A typical usage is as follows:
* - Call one of the creator function Create...() to get either a
* OMXAudioEncoder or OMXVideoEncoder object.
* - Configure codec by providing characteristics of input raw data, such as
* video frame width and height, using Configure().
* - Send raw data (and notify end of stream) with Encode().
* - Get encoded data through GetNextEncodedFrame().
* - Repeat previous 2 steps until end of stream.
* - Destroy the object.
*
* The lifecycle of underlying OMX codec is binded with construction and
* destruction of OMXCodecWrapper and subclass objects. For some types of
* codecs, such as HW accelerated AVC/H.264 encoder, there can be only one
* instance system-wise at a time, attempting to create another instance will
* fail.
*/
class OMXCodecWrapper
{
public:
// Codec types.
enum CodecType {
AAC_ENC, // AAC encoder.
AMR_NB_ENC, // AMR_NB encoder.
AVC_ENC, // AVC/H.264 encoder.
EVRC_ENC, // EVRC encoder
TYPE_COUNT
};
// Input and output flags.
enum {
// For Encode() and Encode, it indicates the end of input stream;
// For GetNextEncodedFrame(), it indicates the end of output
// stream.
BUFFER_EOS = MediaCodec::BUFFER_FLAG_EOS,
// For GetNextEncodedFrame(). It indicates the output buffer is an I-frame.
BUFFER_SYNC_FRAME = MediaCodec::BUFFER_FLAG_SYNCFRAME,
// For GetNextEncodedFrame(). It indicates that the output buffer contains
// codec specific configuration info. (SPS & PPS for AVC/H.264;
// DecoderSpecificInfo for AAC)
BUFFER_CODEC_CONFIG = MediaCodec::BUFFER_FLAG_CODECCONFIG,
};
// Hard-coded values for AAC DecoderConfigDescriptor in libstagefright.
// See MPEG4Writer::Track::writeMp4aEsdsBox()
// Exposed for the need of MP4 container writer.
enum {
kAACBitrate = 96000, // kbps
kAACFrameSize = 768, // bytes
kAACFrameDuration = 1024, // How many samples per AAC frame.
};
/** Create a AAC audio encoder. Returns nullptr when failed. */
static OMXAudioEncoder* CreateAACEncoder();
/** Create a AMR audio encoder. Returns nullptr when failed. */
static OMXAudioEncoder* CreateAMRNBEncoder();
/** Create a EVRC audio encoder. Returns nullptr when failed. */
static OMXAudioEncoder* CreateEVRCEncoder();
/** Create a AVC/H.264 video encoder. Returns nullptr when failed. */
static OMXVideoEncoder* CreateAVCEncoder();
virtual ~OMXCodecWrapper();
/**
* Get the next available encoded data from MediaCodec. The data will be
* copied into aOutputBuf array, with its timestamp (in microseconds) in
* aOutputTimestamp.
* Wait at most aTimeout microseconds to dequeue a output buffer.
*/
nsresult GetNextEncodedFrame(nsTArray<uint8_t>* aOutputBuf,
int64_t* aOutputTimestamp, int* aOutputFlags,
int64_t aTimeOut);
/*
* Get the codec type
*/
int GetCodecType() { return mCodecType; }
protected:
/**
* See whether the object has been initialized successfully and is ready to
* use.
*/
virtual bool IsValid() { return mCodec != nullptr; }
/**
* Construct codec specific configuration blob with given data aData generated
* by media codec and append it into aOutputBuf. Needed by MP4 container
* writer for generating decoder config box, or WebRTC for generating RTP
* packets. Returns OK if succeed.
*/
virtual status_t AppendDecoderConfig(nsTArray<uint8_t>* aOutputBuf,
ABuffer* aData) = 0;
/**
* Append encoded frame data generated by media codec (stored in aData and
* is aSize bytes long) into aOutputBuf. Subclasses can override this function
* to process the data for specific container writer.
*/
virtual void AppendFrame(nsTArray<uint8_t>* aOutputBuf,
const uint8_t* aData, size_t aSize)
{
aOutputBuf->AppendElements(aData, aSize);
}
private:
// Hide these. User should always use creator functions to get a media codec.
OMXCodecWrapper() = delete;
OMXCodecWrapper(const OMXCodecWrapper&) = delete;
OMXCodecWrapper& operator=(const OMXCodecWrapper&) = delete;
/**
* Create a media codec of given type. It will be a AVC/H.264 video encoder if
* aCodecType is CODEC_AVC_ENC, or AAC audio encoder if aCodecType is
* CODEC_AAC_ENC.
*/
OMXCodecWrapper(CodecType aCodecType);
// For subclasses to access hidden constructor and implementation details.
friend class OMXAudioEncoder;
friend class OMXVideoEncoder;
/**
* Start the media codec.
*/
status_t Start();
/**
* Stop the media codec.
*/
status_t Stop();
// The actual codec instance provided by libstagefright.
sp<MediaCodec> mCodec;
// A dedicate message loop with its own thread used by MediaCodec.
sp<ALooper> mLooper;
Vector<sp<ABuffer> > mInputBufs; // MediaCodec buffers to hold input data.
Vector<sp<ABuffer> > mOutputBufs; // MediaCodec buffers to hold output data.
int mCodecType;
bool mStarted; // Has MediaCodec been started?
bool mAMRCSDProvided;
bool mEVRCCSDProvided;
};
/**
* Audio encoder.
*/
class OMXAudioEncoder final : public OMXCodecWrapper
{
public:
/**
* Configure audio codec parameters and start media codec. It must be called
* before calling Encode() and GetNextEncodedFrame().
* aReSamplingRate = 0 means no resampler required
*/
nsresult Configure(int aChannelCount, int aInputSampleRate, int aEncodedSampleRate);
/**
* Encode 16-bit PCM audio samples stored in aSegment. To notify end of
* stream, set aInputFlags to BUFFER_EOS. Since encoder has limited buffers,
* this function might not be able to encode all chunks in one call, however
* it will remove chunks it consumes from aSegment.
* aSendEOS is the output to tell the caller EOS signal sent into MediaCodec
* because the signal might not be sent due to the dequeueInputBuffer timeout.
* And the value of aSendEOS won't be set to any default value, only set to
* true when EOS signal sent into MediaCodec.
*/
nsresult Encode(mozilla::AudioSegment& aSegment, int aInputFlags = 0,
bool* aSendEOS = nullptr);
~OMXAudioEncoder();
protected:
virtual status_t AppendDecoderConfig(nsTArray<uint8_t>* aOutputBuf,
ABuffer* aData) override;
private:
// Hide these. User should always use creator functions to get a media codec.
OMXAudioEncoder() = delete;
OMXAudioEncoder(const OMXAudioEncoder&) = delete;
OMXAudioEncoder& operator=(const OMXAudioEncoder&) = delete;
/**
* Create a audio codec. It will be a AAC encoder if aCodecType is
* CODEC_AAC_ENC.
*/
OMXAudioEncoder(CodecType aCodecType)
: OMXCodecWrapper(aCodecType)
, mResampler(nullptr)
, mChannels(0)
, mResamplingRatio(0)
, mTimestamp(0)
, mSampleDuration(0) {}
// For creator function to access hidden constructor.
friend class OMXCodecWrapper;
friend class InputBufferHelper;
/**
* If the input sample rate does not divide 48kHz evenly, the input data are
* resampled.
*/
SpeexResamplerState* mResampler;
// Number of audio channels.
size_t mChannels;
float mResamplingRatio;
// The total duration of audio samples that have been encoded in microseconds.
int64_t mTimestamp;
// Time per audio sample in microseconds.
int64_t mSampleDuration;
};
/**
* Video encoder.
*/
class OMXVideoEncoder final : public OMXCodecWrapper
{
public:
// Types of output blob format.
enum BlobFormat {
AVC_MP4, // MP4 file config descripter (defined in ISO/IEC 14496-15 5.2.4.1.1)
AVC_NAL // NAL (Network Abstract Layer) (defined in ITU-T H.264 7.4.1)
};
/**
* Configure video codec parameters and start media codec. It must be called
* before calling Encode() and GetNextEncodedFrame().
* aBlobFormat specifies output blob format provided by encoder. It can be
* AVC_MP4 or AVC_NAL.
* Configure sets up most format value to values appropriate for camera use.
* ConfigureDirect lets the caller determine all the defaults.
*/
nsresult Configure(int aWidth, int aHeight, int aFrameRate,
BlobFormat aBlobFormat = BlobFormat::AVC_MP4);
nsresult ConfigureDirect(sp<AMessage>& aFormat,
BlobFormat aBlobFormat = BlobFormat::AVC_MP4);
/**
* Encode a aWidth pixels wide and aHeight pixels tall video frame of
* semi-planar YUV420 format stored in the buffer of aImage. aTimestamp gives
* the frame timestamp/presentation time (in microseconds). To notify end of
* stream, set aInputFlags to BUFFER_EOS.
* aSendEOS is the output to tell the caller EOS signal sent into MediaCodec
* because the signal might not be sent due to the dequeueInputBuffer timeout.
* And the value of aSendEOS won't be set to any default value, only set to
* true when EOS signal sent into MediaCodec.
*/
nsresult Encode(const mozilla::layers::Image* aImage, int aWidth, int aHeight,
int64_t aTimestamp, int aInputFlags = 0,
bool* aSendEOS = nullptr);
#if ANDROID_VERSION >= 18
/** Set encoding bitrate (in kbps). */
nsresult SetBitrate(int32_t aKbps);
#endif
/**
* Ask codec to generate an instantaneous decoding refresh (IDR) frame
* (defined in ISO/IEC 14496-10).
*/
nsresult RequestIDRFrame();
protected:
virtual status_t AppendDecoderConfig(nsTArray<uint8_t>* aOutputBuf,
ABuffer* aData) override;
// If configured to output MP4 format blob, AVC/H.264 encoder has to replace
// NAL unit start code with the unit length as specified in
// ISO/IEC 14496-15 5.2.3.
virtual void AppendFrame(nsTArray<uint8_t>* aOutputBuf,
const uint8_t* aData, size_t aSize) override;
private:
// Hide these. User should always use creator functions to get a media codec.
OMXVideoEncoder() = delete;
OMXVideoEncoder(const OMXVideoEncoder&) = delete;
OMXVideoEncoder& operator=(const OMXVideoEncoder&) = delete;
/**
* Create a video codec. It will be a AVC/H.264 encoder if aCodecType is
* CODEC_AVC_ENC.
*/
OMXVideoEncoder(CodecType aCodecType)
: OMXCodecWrapper(aCodecType)
, mWidth(0)
, mHeight(0)
, mBlobFormat(BlobFormat::AVC_MP4)
{}
// For creator function to access hidden constructor.
friend class OMXCodecWrapper;
int mWidth;
int mHeight;
BlobFormat mBlobFormat;
};
} // namespace android
#endif // OMXCodecWrapper_h_

View File

@ -1,940 +0,0 @@
/* -*- 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 <unistd.h>
#include <fcntl.h>
#include "base/basictypes.h"
#include <cutils/properties.h>
#include <stagefright/foundation/ADebug.h>
#include <stagefright/foundation/AMessage.h>
#include <stagefright/MediaExtractor.h>
#include <stagefright/MetaData.h>
#include <stagefright/OMXClient.h>
#include <stagefright/OMXCodec.h>
#include <OMX.h>
#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
#include <ui/Fence.h>
#endif
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
#include <gui/Surface.h>
#else
#include <gui/SurfaceTextureClient.h>
#endif
#include "mozilla/layers/GrallocTextureClient.h"
#include "mozilla/layers/TextureClient.h"
#include "mozilla/Types.h"
#include "mozilla/Monitor.h"
#include "nsMimeTypes.h"
#include "MPAPI.h"
#include "mozilla/Logging.h"
#include "GonkNativeWindow.h"
#include "OMXCodecProxy.h"
#include "OmxDecoder.h"
#include <android/log.h>
#define OD_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "OmxDecoder", __VA_ARGS__)
#undef LOG
mozilla::LazyLogModule gOmxDecoderLog("OmxDecoder");
#define LOG(type, msg...) MOZ_LOG(gOmxDecoderLog, type, (msg))
using namespace MPAPI;
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::layers;
using namespace android;
OmxDecoder::OmxDecoder(AbstractMediaDecoder *aDecoder,
mozilla::TaskQueue* aTaskQueue) :
mDecoder(aDecoder),
mDisplayWidth(0),
mDisplayHeight(0),
mVideoWidth(0),
mVideoHeight(0),
mVideoColorFormat(0),
mVideoStride(0),
mVideoSliceHeight(0),
mVideoRotation(0),
mAudioChannels(-1),
mAudioSampleRate(-1),
mDurationUs(-1),
mLastSeekTime(-1),
mVideoBuffer(nullptr),
mAudioBuffer(nullptr),
mIsVideoSeeking(false),
mAudioMetadataRead(false),
mTaskQueue(aTaskQueue),
mAudioPaused(false),
mVideoPaused(false)
{
mLooper = new ALooper;
mLooper->setName("OmxDecoder");
mReflector = new AHandlerReflector<OmxDecoder>(this);
// Register AMessage handler to ALooper.
mLooper->registerHandler(mReflector);
// Start ALooper thread.
mLooper->start();
}
OmxDecoder::~OmxDecoder()
{
MOZ_ASSERT(NS_IsMainThread());
ReleaseMediaResources();
// unregister AMessage handler from ALooper.
mLooper->unregisterHandler(mReflector->id());
// Stop ALooper thread.
mLooper->stop();
}
static sp<IOMX> sOMX = nullptr;
static sp<IOMX> GetOMX()
{
if(sOMX.get() == nullptr) {
sOMX = new OMX;
}
return sOMX;
}
bool
OmxDecoder::Init(sp<MediaExtractor>& extractor) {
sp<MetaData> meta = extractor->getMetaData();
ssize_t audioTrackIndex = -1;
ssize_t videoTrackIndex = -1;
for (size_t i = 0; i < extractor->countTracks(); ++i) {
sp<MetaData> meta = extractor->getTrackMetaData(i);
int32_t bitRate;
if (!meta->findInt32(kKeyBitRate, &bitRate))
bitRate = 0;
const char *mime;
if (!meta->findCString(kKeyMIMEType, &mime)) {
continue;
}
if (videoTrackIndex == -1 && !strncasecmp(mime, "video/", 6)) {
videoTrackIndex = i;
} else if (audioTrackIndex == -1 && !strncasecmp(mime, "audio/", 6)) {
audioTrackIndex = i;
}
}
if (videoTrackIndex == -1 && audioTrackIndex == -1) {
NS_WARNING("OMX decoder could not find video or audio tracks");
return false;
}
if (videoTrackIndex != -1 && mDecoder->GetImageContainer()) {
mVideoTrack = extractor->getTrack(videoTrackIndex);
}
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;
}
bool
OmxDecoder::EnsureMetadata() {
// calculate duration
int64_t totalDurationUs = 0;
int64_t durationUs = 0;
if (mVideoTrack.get() && mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
if (durationUs > totalDurationUs)
totalDurationUs = durationUs;
}
if (mAudioTrack.get()) {
durationUs = -1;
sp<MetaData> meta = mAudioTrack->getFormat();
if ((durationUs == -1) && meta->findInt64(kKeyDuration, &durationUs)) {
if (durationUs > totalDurationUs) {
totalDurationUs = durationUs;
}
}
}
mDurationUs = totalDurationUs;
// read video metadata
if (mVideoSource.get() && !SetVideoFormat()) {
NS_WARNING("Couldn't set OMX video format");
return false;
}
// 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) {
NS_WARNING("Couldn't read audio buffer from OMX decoder");
return false;
}
sp<MetaData> meta = mAudioSource->getFormat();
if (!meta->findInt32(kKeyChannelCount, &mAudioChannels) ||
!meta->findInt32(kKeySampleRate, &mAudioSampleRate)) {
NS_WARNING("Couldn't get audio metadata from OMX decoder");
return false;
}
mAudioMetadataRead = true;
}
else if (!SetAudioFormat()) {
NS_WARNING("Couldn't set audio format");
return false;
}
}
return true;
}
static bool isInEmulator()
{
char propQemu[PROPERTY_VALUE_MAX];
property_get("ro.kernel.qemu", propQemu, "");
return !strncmp(propQemu, "1", 1);
}
RefPtr<mozilla::MediaOmxCommonReader::MediaResourcePromise>
OmxDecoder::AllocateMediaResources()
{
RefPtr<MediaResourcePromise> p = mMediaResourcePromise.Ensure(__func__);
if ((mVideoTrack != nullptr) && (mVideoSource == nullptr)) {
// 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 defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
sp<IGraphicBufferProducer> producer;
sp<IGonkGraphicBufferConsumer> consumer;
GonkBufferQueue::createBufferQueue(&producer, &consumer);
mNativeWindow = new GonkNativeWindow(consumer);
#else
mNativeWindow = new GonkNativeWindow();
#endif
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
mNativeWindowClient = new Surface(producer);
#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
mNativeWindowClient = new Surface(mNativeWindow->getBufferQueue());
#else
mNativeWindowClient = new SurfaceTextureClient(mNativeWindow);
#endif
// 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.
#ifdef MOZ_OMX_WEBM_DECODER
int flags = 0;//fallback to omx sw decoder if there is no hw decoder
#else
int flags = kHardwareCodecsOnly;
#endif//MOZ_OMX_WEBM_DECODER
if (isInEmulator()) {
// 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");
mMediaResourcePromise.Reject(true, __func__);
return p;
} else {
sp<OmxDecoder> self = this;
mVideoCodecRequest.Begin(mVideoSource->requestResource()
->Then(OwnerThread(), __func__,
[self] (bool) -> void {
self->mVideoCodecRequest.Complete();
self->mMediaResourcePromise.ResolveIfExists(true, __func__);
}, [self] (bool) -> void {
self->mVideoCodecRequest.Complete();
self->mMediaResourcePromise.RejectIfExists(true, __func__);
}));
}
}
if ((mAudioTrack != nullptr) && (mAudioSource == nullptr)) {
// 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();
const char *audioMime = nullptr;
sp<MetaData> meta = mAudioTrack->getFormat();
if (!meta->findCString(kKeyMIMEType, &audioMime)) {
mMediaResourcePromise.Reject(true, __func__);
return p;
}
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");
mMediaResourcePromise.Reject(true, __func__);
return p;
}
}
if (mAudioSource->start() != OK) {
NS_WARNING("Couldn't start OMX audio source");
mAudioSource.clear();
mMediaResourcePromise.Reject(true, __func__);
return p;
}
}
if (!mVideoSource.get()) {
// No resource allocation wait.
mMediaResourcePromise.Resolve(true, __func__);
}
return p;
}
void
OmxDecoder::ReleaseMediaResources() {
mVideoCodecRequest.DisconnectIfExists();
mMediaResourcePromise.RejectIfExists(true, __func__);
ReleaseVideoBuffer();
ReleaseAudioBuffer();
{
Mutex::Autolock autoLock(mPendingVideoBuffersLock);
MOZ_ASSERT(mPendingRecycleTexutreClients.empty());
// Release all pending recycle TextureClients, if they are not recycled yet.
// This should not happen. See Bug 1042308.
if (!mPendingRecycleTexutreClients.empty()) {
printf_stderr("OmxDecoder::ReleaseMediaResources -- TextureClients are not recycled yet\n");
for (std::set<TextureClient*>::iterator it=mPendingRecycleTexutreClients.begin();
it!=mPendingRecycleTexutreClients.end(); it++)
{
GrallocTextureData* client = static_cast<GrallocTextureData*>((*it)->GetInternalData());
(*it)->ClearRecycleCallback();
if (client->GetMediaBuffer()) {
mPendingVideoBuffers.push(BufferItem(client->GetMediaBuffer(), (*it)->GetAndResetReleaseFenceHandle()));
}
}
mPendingRecycleTexutreClients.clear();
}
}
{
// Free all pending video buffers.
Mutex::Autolock autoLock(mSeekLock);
ReleaseAllPendingVideoBuffersLocked();
}
if (mVideoSource.get()) {
mVideoSource->stop();
mVideoSource.clear();
}
if (mAudioSource.get()) {
mAudioSource->stop();
mAudioSource.clear();
}
mNativeWindowClient.clear();
mNativeWindow.clear();
// Reset this variable to make the first seek go to the previous keyframe
// when resuming
mLastSeekTime = -1;
}
bool
OmxDecoder::SetVideoFormat() {
const char *componentName;
if (!mVideoSource->getFormat()->findInt32(kKeyWidth, &mVideoWidth) ||
!mVideoSource->getFormat()->findInt32(kKeyHeight, &mVideoHeight) ||
!mVideoSource->getFormat()->findCString(kKeyDecoderComponent, &componentName) ||
!mVideoSource->getFormat()->findInt32(kKeyColorFormat, &mVideoColorFormat) ) {
return false;
}
if (!mVideoTrack.get() || !mVideoTrack->getFormat()->findInt32(kKeyDisplayWidth, &mDisplayWidth)) {
mDisplayWidth = mVideoWidth;
NS_WARNING("display width not available, assuming width");
}
if (!mVideoTrack.get() || !mVideoTrack->getFormat()->findInt32(kKeyDisplayHeight, &mDisplayHeight)) {
mDisplayHeight = mVideoHeight;
NS_WARNING("display height not available, assuming height");
}
if (!mVideoSource->getFormat()->findInt32(kKeyStride, &mVideoStride)) {
mVideoStride = mVideoWidth;
NS_WARNING("stride not available, assuming width");
}
if (!mVideoSource->getFormat()->findInt32(kKeySliceHeight, &mVideoSliceHeight)) {
mVideoSliceHeight = mVideoHeight;
NS_WARNING("slice height not available, assuming height");
}
// Since ICS, valid video side is caluculated from kKeyCropRect.
// kKeyWidth means decoded video buffer width.
// kKeyHeight means decoded video buffer height.
// On some hardwares, decoded video buffer and valid video size are different.
int32_t crop_left, crop_top, crop_right, crop_bottom;
if (mVideoSource->getFormat()->findRect(kKeyCropRect,
&crop_left,
&crop_top,
&crop_right,
&crop_bottom)) {
mVideoWidth = crop_right - crop_left + 1;
mVideoHeight = crop_bottom - crop_top + 1;
}
if (!mVideoSource->getFormat()->findInt32(kKeyRotation, &mVideoRotation)) {
mVideoRotation = 0;
NS_WARNING("rotation not available, assuming 0");
}
LOG(LogLevel::Debug, "display width: %d display height %d width: %d height: %d component: %s format: %d stride: %d sliceHeight: %d rotation: %d",
mDisplayWidth, mDisplayHeight, mVideoWidth, mVideoHeight, componentName,
mVideoColorFormat, mVideoStride, mVideoSliceHeight, mVideoRotation);
return true;
}
bool
OmxDecoder::SetAudioFormat() {
// If the format changed, update our cached info.
if (!mAudioSource->getFormat()->findInt32(kKeyChannelCount, &mAudioChannels) ||
!mAudioSource->getFormat()->findInt32(kKeySampleRate, &mAudioSampleRate)) {
return false;
}
LOG(LogLevel::Debug, "channelCount: %d sampleRate: %d",
mAudioChannels, mAudioSampleRate);
return true;
}
void
OmxDecoder::ReleaseDecoder()
{
mDecoder = nullptr;
}
void
OmxDecoder::ReleaseVideoBuffer() {
if (mVideoBuffer) {
mVideoBuffer->release();
mVideoBuffer = nullptr;
}
}
void
OmxDecoder::ReleaseAudioBuffer() {
if (mAudioBuffer) {
mAudioBuffer->release();
mAudioBuffer = nullptr;
}
}
void
OmxDecoder::PlanarYUV420Frame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) {
void *y = aData;
void *u = static_cast<uint8_t *>(y) + mVideoStride * mVideoSliceHeight;
void *v = static_cast<uint8_t *>(u) + mVideoStride/2 * mVideoSliceHeight/2;
aFrame->Set(aTimeUs, aKeyFrame,
aData, aSize, mVideoStride, mVideoSliceHeight, mVideoRotation,
y, mVideoStride, mVideoWidth, mVideoHeight, 0, 0,
u, mVideoStride/2, mVideoWidth/2, mVideoHeight/2, 0, 0,
v, mVideoStride/2, mVideoWidth/2, mVideoHeight/2, 0, 0);
}
void
OmxDecoder::CbYCrYFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) {
aFrame->Set(aTimeUs, aKeyFrame,
aData, aSize, mVideoStride, mVideoSliceHeight, mVideoRotation,
aData, mVideoStride, mVideoWidth, mVideoHeight, 1, 1,
aData, mVideoStride, mVideoWidth/2, mVideoHeight/2, 0, 3,
aData, mVideoStride, mVideoWidth/2, mVideoHeight/2, 2, 3);
}
void
OmxDecoder::SemiPlanarYUV420Frame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) {
void *y = aData;
void *uv = static_cast<uint8_t *>(y) + (mVideoStride * mVideoSliceHeight);
aFrame->Set(aTimeUs, aKeyFrame,
aData, aSize, mVideoStride, mVideoSliceHeight, mVideoRotation,
y, mVideoStride, mVideoWidth, mVideoHeight, 0, 0,
uv, mVideoStride, mVideoWidth/2, mVideoHeight/2, 0, 1,
uv, mVideoStride, mVideoWidth/2, mVideoHeight/2, 1, 1);
}
void
OmxDecoder::SemiPlanarYVU420Frame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) {
SemiPlanarYUV420Frame(aFrame, aTimeUs, aData, aSize, aKeyFrame);
aFrame->Cb.mOffset = 1;
aFrame->Cr.mOffset = 0;
}
bool
OmxDecoder::ToVideoFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) {
const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
aFrame->mGraphicBuffer = nullptr;
switch (mVideoColorFormat) {
case OMX_COLOR_FormatYUV420Planar:
PlanarYUV420Frame(aFrame, aTimeUs, aData, aSize, aKeyFrame);
break;
case OMX_COLOR_FormatCbYCrY:
CbYCrYFrame(aFrame, aTimeUs, aData, aSize, aKeyFrame);
break;
case OMX_COLOR_FormatYUV420SemiPlanar:
SemiPlanarYUV420Frame(aFrame, aTimeUs, aData, aSize, aKeyFrame);
break;
case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
SemiPlanarYVU420Frame(aFrame, aTimeUs, aData, aSize, aKeyFrame);
break;
default:
LOG(LogLevel::Debug, "Unknown video color format %08x", mVideoColorFormat);
return false;
}
return true;
}
bool
OmxDecoder::ToAudioFrame(AudioFrame *aFrame, int64_t aTimeUs, void *aData, size_t aDataOffset, size_t aSize, int32_t aAudioChannels, int32_t aAudioSampleRate)
{
aFrame->Set(aTimeUs, static_cast<char *>(aData) + aDataOffset, aSize, aAudioChannels, aAudioSampleRate);
return true;
}
bool
OmxDecoder::ReadVideo(VideoFrame *aFrame, int64_t aTimeUs,
bool aKeyframeSkip, bool aDoSeek)
{
if (!mVideoSource.get())
return false;
ReleaseVideoBuffer();
status_t err;
if (aDoSeek) {
{
Mutex::Autolock autoLock(mSeekLock);
ReleaseAllPendingVideoBuffersLocked();
mIsVideoSeeking = true;
}
MediaSource::ReadOptions options;
MediaSource::ReadOptions::SeekMode seekMode;
// If the last timestamp of decoded frame is smaller than seekTime,
// seek to next key frame. Otherwise seek to the previos one.
OD_LOG("SeekTime: %lld, mLastSeekTime:%lld", aTimeUs, mLastSeekTime);
if (mLastSeekTime == -1 || mLastSeekTime > aTimeUs) {
seekMode = MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC;
} else {
seekMode = MediaSource::ReadOptions::SEEK_NEXT_SYNC;
}
mLastSeekTime = aTimeUs;
bool findNextBuffer = true;
while (findNextBuffer) {
options.setSeekTo(aTimeUs, seekMode);
findNextBuffer = false;
if (mIsVideoSeeking) {
err = mVideoSource->read(&mVideoBuffer, &options);
Mutex::Autolock autoLock(mSeekLock);
mIsVideoSeeking = false;
PostReleaseVideoBuffer(nullptr, FenceHandle());
}
else {
err = mVideoSource->read(&mVideoBuffer);
}
// If there is no next Keyframe, jump to the previous key frame.
if (err == ERROR_END_OF_STREAM && seekMode == MediaSource::ReadOptions::SEEK_NEXT_SYNC) {
seekMode = MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC;
findNextBuffer = true;
{
Mutex::Autolock autoLock(mSeekLock);
mIsVideoSeeking = true;
}
continue;
} else if (err != OK) {
OD_LOG("Unexpected error when seeking to %lld", aTimeUs);
break;
}
// For some codecs, the length of first decoded frame after seek is 0.
// Need to ignore it and continue to find the next one
if (mVideoBuffer->range_length() == 0) {
PostReleaseVideoBuffer(mVideoBuffer, FenceHandle());
findNextBuffer = true;
}
}
aDoSeek = false;
} else {
err = mVideoSource->read(&mVideoBuffer);
}
aFrame->mSize = 0;
if (err == OK) {
int64_t timeUs;
int32_t unreadable;
int32_t keyFrame;
size_t length = mVideoBuffer->range_length();
if (!mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs) ) {
NS_WARNING("OMX decoder did not return frame time");
return false;
}
if (!mVideoBuffer->meta_data()->findInt32(kKeyIsSyncFrame, &keyFrame)) {
keyFrame = 0;
}
if (!mVideoBuffer->meta_data()->findInt32(kKeyIsUnreadable, &unreadable)) {
unreadable = 0;
}
RefPtr<mozilla::layers::TextureClient> textureClient;
if ((mVideoBuffer->graphicBuffer().get())) {
textureClient = mNativeWindow->getTextureClientFromBuffer(mVideoBuffer->graphicBuffer().get());
}
if (textureClient) {
// Manually increment reference count to keep MediaBuffer alive
// during TextureClient is in use.
mVideoBuffer->add_ref();
static_cast<GrallocTextureData*>(textureClient->GetInternalData())->SetMediaBuffer(mVideoBuffer);
// Set recycle callback for TextureClient
textureClient->SetRecycleCallback(OmxDecoder::RecycleCallback, this);
{
Mutex::Autolock autoLock(mPendingVideoBuffersLock);
// Store pending recycle TextureClient.
MOZ_ASSERT(mPendingRecycleTexutreClients.find(textureClient) == mPendingRecycleTexutreClients.end());
mPendingRecycleTexutreClients.insert(textureClient);
}
aFrame->mGraphicBuffer = textureClient;
aFrame->mRotation = mVideoRotation;
aFrame->mTimeUs = timeUs;
aFrame->mKeyFrame = keyFrame;
aFrame->Y.mWidth = mVideoWidth;
aFrame->Y.mHeight = mVideoHeight;
// Release to hold video buffer in OmxDecoder more.
// MediaBuffer's ref count is changed from 2 to 1.
ReleaseVideoBuffer();
} else if (length > 0) {
char *data = static_cast<char *>(mVideoBuffer->data()) + mVideoBuffer->range_offset();
if (unreadable) {
LOG(LogLevel::Debug, "video frame is unreadable");
}
if (!ToVideoFrame(aFrame, timeUs, data, length, keyFrame)) {
return false;
}
}
// Check if this frame is valid or not. If not, skip it.
if ((aKeyframeSkip && timeUs < aTimeUs) || length == 0) {
aFrame->mShouldSkip = true;
}
}
else if (err == INFO_FORMAT_CHANGED) {
// If the format changed, update our cached info.
if (!SetVideoFormat()) {
return false;
} else {
return ReadVideo(aFrame, aTimeUs, aKeyframeSkip, aDoSeek);
}
}
else if (err == ERROR_END_OF_STREAM) {
return false;
}
else if (err == -ETIMEDOUT) {
LOG(LogLevel::Debug, "OmxDecoder::ReadVideo timed out, will retry");
return true;
}
else {
// UNKNOWN_ERROR is sometimes is used to mean "out of memory", but
// regardless, don't keep trying to decode if the decoder doesn't want to.
LOG(LogLevel::Debug, "OmxDecoder::ReadVideo failed, err=%d", err);
return false;
}
return true;
}
bool
OmxDecoder::ReadAudio(AudioFrame *aFrame, int64_t aSeekTimeUs)
{
status_t err;
if (mAudioMetadataRead && aSeekTimeUs == -1) {
// Use the data read into the buffer during metadata time
err = OK;
}
else {
ReleaseAudioBuffer();
if (aSeekTimeUs != -1) {
MediaSource::ReadOptions options;
options.setSeekTo(aSeekTimeUs);
err = mAudioSource->read(&mAudioBuffer, &options);
} else {
err = mAudioSource->read(&mAudioBuffer);
}
}
mAudioMetadataRead = false;
aSeekTimeUs = -1;
aFrame->mSize = 0;
if (err == OK && mAudioBuffer && mAudioBuffer->range_length() != 0) {
int64_t timeUs;
if (!mAudioBuffer->meta_data()->findInt64(kKeyTime, &timeUs))
return false;
return ToAudioFrame(aFrame, timeUs,
mAudioBuffer->data(),
mAudioBuffer->range_offset(),
mAudioBuffer->range_length(),
mAudioChannels, mAudioSampleRate);
}
else if (err == INFO_FORMAT_CHANGED) {
// If the format changed, update our cached info.
if (!SetAudioFormat()) {
return false;
} else {
return ReadAudio(aFrame, aSeekTimeUs);
}
}
else if (err == ERROR_END_OF_STREAM) {
if (aFrame->mSize == 0) {
return false;
}
}
else if (err == -ETIMEDOUT) {
LOG(LogLevel::Debug, "OmxDecoder::ReadAudio timed out, will retry");
return true;
}
else if (err != OK) {
LOG(LogLevel::Debug, "OmxDecoder::ReadAudio failed, err=%d", err);
return false;
}
return true;
}
nsresult
OmxDecoder::Play()
{
if (!mVideoPaused && !mAudioPaused) {
return NS_OK;
}
if (mVideoPaused && mVideoSource.get() && mVideoSource->start() != OK) {
return NS_ERROR_UNEXPECTED;
}
mVideoPaused = false;
if (mAudioPaused && mAudioSource.get() && mAudioSource->start() != OK) {
return NS_ERROR_UNEXPECTED;
}
mAudioPaused = false;
return NS_OK;
}
// AOSP didn't give implementation on OMXCodec::Pause() and not define
// OMXCodec::Start() should be called for resuming the decoding. Currently
// it is customized by a specific open source repository only.
// ToDo The one not supported OMXCodec::Pause() should return error code here,
// so OMXCodec::Start() doesn't be called again for resuming. But if someone
// implement the OMXCodec::Pause() and need a following OMXCodec::Read() with
// seek option (define in MediaSource.h) then it is still not supported here.
// We need to fix it until it is really happened.
void
OmxDecoder::Pause()
{
/* The implementation of OMXCodec::pause is flawed.
* OMXCodec::start will not restore from the paused state and result in
* buffer timeout which causes timeouts in mochitests.
* Since there is not power consumption problem in emulator, we will just
* return when running in emulator to fix timeouts in mochitests.
*/
if (isInEmulator()) {
return;
}
if (mVideoPaused || mAudioPaused) {
return;
}
if (mVideoSource.get() && mVideoSource->pause() == OK) {
mVideoPaused = true;
}
if (mAudioSource.get() && mAudioSource->pause() == OK) {
mAudioPaused = true;
}
}
// Called on ALooper thread.
void
OmxDecoder::onMessageReceived(const sp<AMessage> &msg)
{
switch (msg->what()) {
case kNotifyPostReleaseVideoBuffer:
{
Mutex::Autolock autoLock(mSeekLock);
// Free pending video buffers when OmxDecoder is not seeking video.
// If OmxDecoder is seeking video, the buffers are freed on seek exit.
if (!mIsVideoSeeking) {
ReleaseAllPendingVideoBuffersLocked();
}
break;
}
default:
TRESPASS();
break;
}
}
void
OmxDecoder::PostReleaseVideoBuffer(MediaBuffer *aBuffer, const FenceHandle& aReleaseFenceHandle)
{
{
Mutex::Autolock autoLock(mPendingVideoBuffersLock);
if (aBuffer) {
mPendingVideoBuffers.push(BufferItem(aBuffer, aReleaseFenceHandle));
}
}
sp<AMessage> notify =
new AMessage(kNotifyPostReleaseVideoBuffer, mReflector->id());
// post AMessage to OmxDecoder via ALooper.
notify->post();
}
void
OmxDecoder::ReleaseAllPendingVideoBuffersLocked()
{
Vector<BufferItem> releasingVideoBuffers;
{
Mutex::Autolock autoLock(mPendingVideoBuffersLock);
int size = mPendingVideoBuffers.size();
for (int i = 0; i < size; i++) {
releasingVideoBuffers.push(mPendingVideoBuffers[i]);
}
mPendingVideoBuffers.clear();
}
// Free all pending video buffers without holding mPendingVideoBuffersLock.
int size = releasingVideoBuffers.size();
for (int i = 0; i < size; i++) {
MediaBuffer *buffer;
buffer = releasingVideoBuffers[i].mMediaBuffer;
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
RefPtr<FenceHandle::FdObj> fdObj = releasingVideoBuffers.editItemAt(i).mReleaseFenceHandle.GetAndResetFdObj();
int fenceFd = fdObj->GetAndResetFd();
MOZ_ASSERT(buffer->refcount() == 1);
// This code expect MediaBuffer's ref count is 1.
// Return gralloc buffer to ANativeWindow
ANativeWindow* window = static_cast<ANativeWindow*>(mNativeWindowClient.get());
window->cancelBuffer(window,
buffer->graphicBuffer().get(),
fenceFd);
// Mark MediaBuffer as rendered.
// When gralloc buffer is directly returned to ANativeWindow,
// this mark is necesary.
sp<MetaData> metaData = buffer->meta_data();
metaData->setInt32(kKeyRendered, 1);
#endif
// Return MediaBuffer to OMXCodec.
buffer->release();
}
releasingVideoBuffers.clear();
}
void
OmxDecoder::RecycleCallbackImp(TextureClient* aClient)
{
aClient->ClearRecycleCallback();
{
Mutex::Autolock autoLock(mPendingVideoBuffersLock);
if (mPendingRecycleTexutreClients.find(aClient) == mPendingRecycleTexutreClients.end()) {
printf_stderr("OmxDecoder::RecycleCallbackImp -- TextureClient is not pending recycle\n");
return;
}
mPendingRecycleTexutreClients.erase(aClient);
GrallocTextureData* grallocData = static_cast<GrallocTextureData*>(aClient->GetInternalData());
if (grallocData->GetMediaBuffer()) {
mPendingVideoBuffers.push(BufferItem(grallocData->GetMediaBuffer(), aClient->GetAndResetReleaseFenceHandle()));
}
}
sp<AMessage> notify =
new AMessage(kNotifyPostReleaseVideoBuffer, mReflector->id());
// post AMessage to OmxDecoder via ALooper.
notify->post();
}
/* static */ void
OmxDecoder::RecycleCallback(TextureClient* aClient, void* aClosure)
{
MOZ_ASSERT(aClient && !aClient->IsDead());
OmxDecoder* decoder = static_cast<OmxDecoder*>(aClosure);
decoder->RecycleCallbackImp(aClient);
}

View File

@ -1,224 +0,0 @@
#include <set>
#include <stagefright/foundation/ABase.h>
#include <stagefright/foundation/AHandlerReflector.h>
#include <stagefright/foundation/ALooper.h>
#include <utils/RefBase.h>
#include <stagefright/MediaExtractor.h>
#include "GonkNativeWindow.h"
#include "mozilla/layers/FenceUtils.h"
#include "MP3FrameParser.h"
#include "MPAPI.h"
#include "MediaOmxCommonReader.h"
#include "AbstractMediaDecoder.h"
#include "OMXCodecProxy.h"
namespace android {
class OmxDecoder;
};
namespace android {
class OmxDecoder : public RefBase {
typedef MPAPI::AudioFrame AudioFrame;
typedef MPAPI::VideoFrame VideoFrame;
typedef mozilla::MP3FrameParser MP3FrameParser;
typedef mozilla::MediaResource MediaResource;
typedef mozilla::AbstractMediaDecoder AbstractMediaDecoder;
typedef mozilla::layers::FenceHandle FenceHandle;
typedef mozilla::layers::TextureClient TextureClient;
typedef mozilla::MediaOmxCommonReader::MediaResourcePromise MediaResourcePromise;
enum {
kPreferSoftwareCodecs = 1,
kSoftwareCodecsOnly = 8,
kHardwareCodecsOnly = 16,
};
enum {
kNotifyPostReleaseVideoBuffer = 'noti',
};
AbstractMediaDecoder *mDecoder;
sp<GonkNativeWindow> mNativeWindow;
sp<ANativeWindow> mNativeWindowClient;
sp<MediaSource> mVideoTrack;
sp<OMXCodecProxy> mVideoSource;
sp<MediaSource> mAudioOffloadTrack;
sp<MediaSource> mAudioTrack;
sp<MediaSource> mAudioSource;
int32_t mDisplayWidth;
int32_t mDisplayHeight;
int32_t mVideoWidth;
int32_t mVideoHeight;
int32_t mVideoColorFormat;
int32_t mVideoStride;
int32_t mVideoSliceHeight;
int32_t mVideoRotation;
int32_t mAudioChannels;
int32_t mAudioSampleRate;
int64_t mDurationUs;
int64_t mLastSeekTime;
VideoFrame mVideoFrame;
AudioFrame mAudioFrame;
MP3FrameParser mMP3FrameParser;
bool mIsMp3;
// Lifetime of these should be handled by OMXCodec, as long as we release
// them after use: see ReleaseVideoBuffer(), ReleaseAudioBuffer()
MediaBuffer *mVideoBuffer;
MediaBuffer *mAudioBuffer;
struct BufferItem {
BufferItem()
: mMediaBuffer(nullptr)
{
}
BufferItem(MediaBuffer* aMediaBuffer, const FenceHandle& aReleaseFenceHandle)
: mMediaBuffer(aMediaBuffer)
, mReleaseFenceHandle(aReleaseFenceHandle) {
}
MediaBuffer* mMediaBuffer;
// a fence will signal when the current buffer is no longer being read.
FenceHandle mReleaseFenceHandle;
};
// Hold video's MediaBuffers that are released during video seeking.
// The holded MediaBuffers are released soon after seek completion.
// OMXCodec does not accept MediaBuffer during seeking. If MediaBuffer is
// returned to OMXCodec during seeking, OMXCodec calls assert.
Vector<BufferItem> mPendingVideoBuffers;
// Hold TextureClients that are waiting to be recycled.
std::set<TextureClient*> mPendingRecycleTexutreClients;
// The lock protects mPendingVideoBuffers and mPendingRecycleTexutreClients.
Mutex mPendingVideoBuffersLock;
// Show if OMXCodec is seeking.
bool mIsVideoSeeking;
// The lock protects video MediaBuffer release()'s pending operations called
// from multiple threads. The pending operations happen only during video
// seeking. Holding mSeekLock long time could affect to video rendering.
// Holding time should be minimum.
Mutex mSeekLock;
// 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<OmxDecoder> > mReflector;
// 'true' if a read from the audio stream was done while reading the metadata
bool mAudioMetadataRead;
RefPtr<mozilla::TaskQueue> mTaskQueue;
mozilla::MozPromiseRequestHolder<OMXCodecProxy::CodecPromise> mVideoCodecRequest;
mozilla::MozPromiseHolder<MediaResourcePromise> mMediaResourcePromise;
void ReleaseVideoBuffer();
void ReleaseAudioBuffer();
// Call with mSeekLock held.
void ReleaseAllPendingVideoBuffersLocked();
void PlanarYUV420Frame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame);
void CbYCrYFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame);
void SemiPlanarYUV420Frame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame);
void SemiPlanarYVU420Frame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame);
bool ToVideoFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame);
bool ToAudioFrame(AudioFrame *aFrame, int64_t aTimeUs, void *aData, size_t aDataOffset, size_t aSize,
int32_t aAudioChannels, int32_t aAudioSampleRate);
//True if decoder is in a paused state
bool mAudioPaused;
bool mVideoPaused;
mozilla::TaskQueue* OwnerThread() const
{
return mTaskQueue;
}
public:
explicit OmxDecoder(AbstractMediaDecoder *aDecoder, mozilla::TaskQueue* aTaskQueue);
~OmxDecoder();
// The MediaExtractor provides essential information for creating OMXCodec
// instance. Such as video/audio codec, we can retrieve them through the
// MediaExtractor::getTrackMetaData().
// In general cases, the extractor is created by a sp<DataSource> which
// connect to a MediaResource like ChannelMediaResource.
// Data is read from the MediaResource to create a suitable extractor which
// extracts data from a container.
// Note: RTSP requires a custom extractor because it doesn't have a container.
bool Init(sp<MediaExtractor>& extractor);
// Called after resources(video/audio codec) are allocated, set the
// mDurationUs and video/audio metadata.
bool EnsureMetadata();
RefPtr<MediaResourcePromise> AllocateMediaResources();
void ReleaseMediaResources();
bool SetVideoFormat();
bool SetAudioFormat();
void ReleaseDecoder();
void GetDuration(int64_t *durationUs) {
*durationUs = mDurationUs;
}
void GetVideoParameters(int32_t* aDisplayWidth, int32_t* aDisplayHeight,
int32_t* aWidth, int32_t* aHeight) {
*aDisplayWidth = mDisplayWidth;
*aDisplayHeight = mDisplayHeight;
*aWidth = mVideoWidth;
*aHeight = mVideoHeight;
}
void GetAudioParameters(int32_t *numChannels, int32_t *sampleRate) {
*numChannels = mAudioChannels;
*sampleRate = mAudioSampleRate;
}
bool HasVideo() {
return mVideoSource != nullptr;
}
bool HasAudio() {
return mAudioSource != nullptr;
}
bool ReadVideo(VideoFrame *aFrame, int64_t aSeekTimeUs,
bool aKeyframeSkip = false,
bool aDoSeek = false);
bool ReadAudio(AudioFrame *aFrame, int64_t aSeekTimeUs);
//Change decoder into a playing state
nsresult Play();
//Change decoder into a paused state
void Pause();
// Post kNotifyPostReleaseVideoBuffer message to OmxDecoder via ALooper.
void PostReleaseVideoBuffer(MediaBuffer *aBuffer, const FenceHandle& aReleaseFenceHandle);
// Receive a message from AHandlerReflector.
// Called on ALooper thread.
void onMessageReceived(const sp<AMessage> &msg);
sp<MediaSource> GetAudioOffloadTrack() { return mAudioOffloadTrack; }
void RecycleCallbackImp(TextureClient* aClient);
static void RecycleCallback(TextureClient* aClient, void* aClosure);
};
}

View File

@ -1,111 +0,0 @@
# -*- Mode: python; 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 += [
'AudioOffloadPlayerBase.h',
'MediaOmxCommonDecoder.h',
'MediaOmxCommonReader.h',
'MediaOmxDecoder.h',
'MediaOmxReader.h',
]
SOURCES += [
'MediaOmxCommonDecoder.cpp',
'MediaOmxCommonReader.cpp',
'MediaOmxDecoder.cpp',
'MediaOmxReader.cpp',
'MediaStreamSource.cpp',
'OMXCodecProxy.cpp',
'OmxDecoder.cpp',
]
if CONFIG['MOZ_AUDIO_OFFLOAD']:
EXPORTS += [
'AudioOffloadPlayer.h',
'AudioOutput.h',
'GonkAudioSink.h',
]
SOURCES += [
'AudioOffloadPlayer.cpp',
'AudioOutput.cpp',
]
if CONFIG['MOZ_OMX_ENCODER']:
EXPORTS += [
'OMXCodecWrapper.h',
]
SOURCES += [
'OMXCodecDescriptorUtil.cpp',
'OMXCodecWrapper.cpp',
]
LOCAL_INCLUDES += ['/media/libyuv/include']
if 'rtsp' in CONFIG['NECKO_PROTOCOLS']:
EXPORTS += [
'RtspExtractor.h',
'RtspOmxDecoder.h',
'RtspOmxReader.h',
]
SOURCES += [
'RtspExtractor.cpp',
'RtspOmxDecoder.cpp',
'RtspOmxReader.cpp',
]
if CONFIG['ANDROID_VERSION'] >= '18':
EXPORTS += [
'I420ColorConverterHelper.h',
'MediaCodecProxy.h',
]
SOURCES += [
'I420ColorConverterHelper.cpp',
'MediaCodecProxy.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
# Suppress some GCC/clang warnings being treated as errors:
# - about attributes on forward declarations for types that are already
# defined, which complains about an important MOZ_EXPORT for android::AString
# - about multi-character constants which are used in codec-related code
# and are part of Android's libstagefright API style.
if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
CXXFLAGS += [
'-Wno-error=attributes',
'-Wno-multichar'
]
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'/dom/base',
'/dom/html',
'/ipc/chromium/src',
]
if CONFIG['ANDROID_VERSION'] == '15':
LOCAL_INCLUDES += [
'%' + '%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
'dalvik/libnativehelper/include/nativehelper',
'frameworks/base/include',
'frameworks/base/include/binder',
'frameworks/base/include/media',
'frameworks/base/include/media/stagefright/openmax',
'frameworks/base/include/utils',
'frameworks/base/media/libstagefright/include',
'hardware/libhardware/include',
]
]
else:
LOCAL_INCLUDES += [
'%' + '%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
'frameworks/av/include/media',
'frameworks/native/include',
'frameworks/native/opengl/include',
]
]
if CONFIG['ANDROID_VERSION'] > '15':
DEFINES['MOZ_OMX_WEBM_DECODER'] = True

View File

@ -137,7 +137,6 @@ var haveMp4 = (getPref("media.wmf.enabled") && IsWindowsVistaOrLater()) ||
IsMacOSSnowLeopardOrLater() ||
(IsSupportedAndroid() &&
(IsJellyBeanOrLater() || getPref("media.plugins.enabled"))) ||
getPref("media.omx.enabled") ||
(IsLinux() && getPref("media.ffmpeg.enabled"));
check_mp4(document.getElementById('v'), haveMp4);

View File

@ -91,7 +91,6 @@ SpecialPowers.pushPrefEnv(
{
"set": [
["media.encoder.webm.enabled", false],
["media.encoder.omx.enabled", false]
]
}, startTest);

View File

@ -584,9 +584,6 @@ pref("media.webspeech.synth.enabled", false);
#ifdef MOZ_WEBM_ENCODER
pref("media.encoder.webm.enabled", true);
#endif
#ifdef MOZ_OMX_ENCODER
pref("media.encoder.omx.enabled", true);
#endif
// Whether to autostart a media element with an |autoplay| attribute
pref("media.autoplay.enabled", true);

View File

@ -108,26 +108,16 @@ if test -n "$gonkdir"; then
15)
CPPFLAGS="-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 $CPPFLAGS"
MOZ_NFC=1
MOZ_OMX_DECODER=1
AC_SUBST(MOZ_OMX_DECODER)
MOZ_SECUREELEMENT=1
;;
17|18)
CPPFLAGS="-I$gonkdir/frameworks/native/include -I$gonkdir/frameworks/av/include -I$gonkdir/frameworks/av/include/media -I$gonkdir/frameworks/av/include/camera -I$gonkdir/frameworks/native/include/media/openmax -I$gonkdir/frameworks/av/media/libstagefright/include $CPPFLAGS"
MOZ_NFC=1
MOZ_OMX_DECODER=1
AC_SUBST(MOZ_OMX_DECODER)
MOZ_OMX_ENCODER=1
AC_SUBST(MOZ_OMX_ENCODER)
AC_DEFINE(MOZ_OMX_ENCODER)
MOZ_SECUREELEMENT=1
;;
19)
CPPFLAGS="-I$gonkdir/frameworks/native/include -I$gonkdir/frameworks/av/include -I$gonkdir/frameworks/av/include/media -I$gonkdir/frameworks/av/include/camera -I$gonkdir/frameworks/native/include/media/openmax -I$gonkdir/frameworks/av/media/libstagefright/include $CPPFLAGS"
MOZ_NFC=1
MOZ_OMX_DECODER=1
MOZ_OMX_ENCODER=1
AC_DEFINE(MOZ_OMX_ENCODER)
MOZ_AUDIO_OFFLOAD=1
MOZ_SECUREELEMENT=1
AC_SUBST(MOZ_AUDIO_OFFLOAD)
@ -136,9 +126,6 @@ if test -n "$gonkdir"; then
21|22)
CPPFLAGS="-I$gonkdir/frameworks/native/include -I$gonkdir/frameworks/av/include -I$gonkdir/frameworks/av/include/media -I$gonkdir/frameworks/av/include/camera -I$gonkdir/frameworks/native/include/media/openmax -I$gonkdir/frameworks/av/media/libstagefright/include $CPPFLAGS"
MOZ_AUDIO_OFFLOAD=1
MOZ_OMX_DECODER=1
MOZ_OMX_ENCODER=1
AC_DEFINE(MOZ_OMX_ENCODER)
AC_SUBST(MOZ_AUDIO_OFFLOAD)
AC_DEFINE(MOZ_AUDIO_OFFLOAD)
MOZ_NFC=1

View File

@ -87,9 +87,6 @@ DEFINES['HAVE_OFF64_T'] = True
DEFINES['SK_BUILD_FOR_ANDROID_NDK'] = True
DEFINES['HAVE_POSIX_CLOCKS'] = True
if CONFIG['MOZ_OMX_DECODER']:
DEFINES['MOZ_OMX_DECODER'] = True
LOCAL_INCLUDES += [
'%' + '%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
'frameworks/native/opengl/include',

View File

@ -48,7 +48,7 @@ elif CONFIG['ANDROID_VERSION'] in ('17', '18'):
'GonkNativeWindowJB.h',
]
if CONFIG['MOZ_OMX_DECODER'] or CONFIG['MOZ_WEBRTC']:
if CONFIG['MOZ_WEBRTC']:
if CONFIG['ANDROID_VERSION'] >= '21':
SOURCES += [
'GonkBufferQueueLL/GonkBufferItem.cpp',

View File

@ -889,9 +889,6 @@ nsAppShell::Init()
printf("*** This is stdout. Most of the useful output will be in logcat.\n");
printf("***\n");
printf("*****************************************************************\n");
#if ANDROID_VERSION >= 18 && defined(MOZ_OMX_DECODER)
android::FakeSurfaceComposer::instantiate();
#endif
GonkPermissionService::instantiate();
// Causes the kernel timezone to be set, which in turn causes the