Merge mozilla-central and inbound

This commit is contained in:
Ed Morley 2014-08-15 17:35:20 +01:00
commit 2d07117081
221 changed files with 2250 additions and 1363 deletions

View File

@ -657,9 +657,6 @@
; [Layout Engine Resources]
; Style Sheets, Graphics and other Resources used by the layout engine.
@BINPATH@/res/EditorOverride.css
@BINPATH@/res/caret_left.svg
@BINPATH@/res/caret_middle.svg
@BINPATH@/res/caret_right.svg
@BINPATH@/res/contenteditable.css
@BINPATH@/res/designmode.css
@BINPATH@/res/ImageDocument.css
@ -683,6 +680,21 @@
@BINPATH@/res/table-remove-row-active.gif
@BINPATH@/res/table-remove-row-hover.gif
@BINPATH@/res/table-remove-row.gif
@BINPATH@/res/text_caret.png
@BINPATH@/res/text_caret@1.5x.png
@BINPATH@/res/text_caret@2.25x.png
@BINPATH@/res/text_caret@2x.png
@BINPATH@/res/text_caret_tilt_left.png
@BINPATH@/res/text_caret_tilt_left@1.5x.png
@BINPATH@/res/text_caret_tilt_left@2.25x.png
@BINPATH@/res/text_caret_tilt_left@2x.png
@BINPATH@/res/text_caret_tilt_right.png
@BINPATH@/res/text_caret_tilt_right@1.5x.png
@BINPATH@/res/text_caret_tilt_right@2.25x.png
@BINPATH@/res/text_caret_tilt_right@2x.png
@BINPATH@/res/text_selection_handle.png
@BINPATH@/res/text_selection_handle@1.5.png
@BINPATH@/res/text_selection_handle@2.png
@BINPATH@/res/grabber.gif
#ifdef XP_MACOSX
@BINPATH@/res/cursors/*

View File

@ -30,7 +30,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
const nsIWebNavigation = Ci.nsIWebNavigation;
var gLastBrowserCharset = null;
var gPrevCharset = null;
var gProxyFavIcon = null;
var gLastValidURLStr = "";
var gInPrintPreviewMode = false;
@ -242,7 +241,6 @@ XPCOMUtils.defineLazyGetter(this, "PageMenu", function() {
* one listener that calls all real handlers.
*/
function pageShowEventHandlers(persisted) {
charsetLoadListener();
XULBrowserWindow.asyncUpdateUI();
// The PluginClickToPlay events are not fired when navigating using the
@ -2588,7 +2586,7 @@ let BrowserOnClick = {
anchorTarget.classList.contains("newtab-link")) {
event.preventDefault();
let where = whereToOpenLink(event, false, false);
openUILinkIn(anchorTarget.href, where);
openLinkIn(anchorTarget.href, where, { charset: ownerDoc.characterSet });
}
},
@ -3511,9 +3509,7 @@ function updateCharacterEncodingMenuState()
// gBrowser is null on Mac when the menubar shows in the context of
// non-browser windows. The above elements may be null depending on
// what parts of the menubar are present. E.g. no app menu on Mac.
if (gBrowser &&
gBrowser.docShell &&
gBrowser.docShell.mayEnableCharacterEncodingMenu) {
if (gBrowser && gBrowser.selectedBrowser.mayEnableCharacterEncodingMenu) {
if (charsetMenu) {
charsetMenu.removeAttribute("disabled");
}
@ -5358,8 +5354,7 @@ function handleDroppedLink(event, url, name)
function BrowserSetForcedCharacterSet(aCharset)
{
if (aCharset) {
gBrowser.docShell.gatherCharsetMenuTelemetry();
gBrowser.docShell.charset = aCharset;
gBrowser.selectedBrowser.characterSet = aCharset;
// Save the forced character-set
if (!PrivateBrowsingUtils.isWindowPrivate(window))
PlacesUtils.setCharsetForURI(getWebNavigation().currentURI, aCharset);
@ -5372,35 +5367,11 @@ function BrowserCharsetReload()
BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
}
function charsetMenuGetElement(parent, charset) {
return parent.getElementsByAttribute("charset", charset)[0];
}
function UpdateCurrentCharset(target) {
// extract the charset from DOM
var wnd = document.commandDispatcher.focusedWindow;
if ((window == wnd) || (wnd == null)) wnd = window.content;
// Uncheck previous item
if (gPrevCharset) {
var pref_item = charsetMenuGetElement(target, gPrevCharset);
if (pref_item)
pref_item.setAttribute('checked', 'false');
}
var menuitem = charsetMenuGetElement(target, CharsetMenu.foldCharset(wnd.document.characterSet));
if (menuitem) {
menuitem.setAttribute('checked', 'true');
}
}
function charsetLoadListener() {
let currCharset = gBrowser.selectedBrowser.characterSet;
let charset = CharsetMenu.foldCharset(currCharset);
if (charset.length > 0 && (charset != gLastBrowserCharset)) {
gPrevCharset = gLastBrowserCharset;
gLastBrowserCharset = charset;
for (let menuItem of target.getElementsByTagName("menuitem")) {
let isSelected = menuItem.getAttribute("charset") ===
CharsetMenu.foldCharset(gBrowser.selectedBrowser.characterSet);
menuItem.setAttribute("checked", isSelected);
}
}

View File

@ -735,8 +735,7 @@ const CustomizableWidgets = [{
maybeDisableMenu: function(aDocument) {
let window = aDocument.defaultView;
return !(window.gBrowser &&
window.gBrowser.docShell &&
window.gBrowser.docShell.mayEnableCharacterEncodingMenu);
window.gBrowser.selectedBrowser.mayEnableCharacterEncodingMenu);
},
populateList: function(aDocument, aContainerId, aSection) {
let containerElem = aDocument.getElementById(aContainerId);
@ -756,8 +755,7 @@ const CustomizableWidgets = [{
}
},
updateCurrentCharset: function(aDocument) {
let content = aDocument.defaultView.content;
let currentCharset = content && content.document && content.document.characterSet;
let currentCharset = aDocument.defaultView.gBrowser.selectedBrowser.characterSet;
currentCharset = CharsetMenu.foldCharset(currentCharset);
let pinnedContainer = aDocument.getElementById("PanelUI-characterEncodingView-pinned");

View File

@ -64,6 +64,8 @@ const PREF_TELEMETRY_ENABLED = "enabled";
const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
const STRING_TYPE_NAME = "type.%ID%.name";
const CACHE_WRITE_RETRY_DELAY_SEC = 60 * 3;
const TELEMETRY_LOG = {
// log(key, [kind, experimentId, details])
ACTIVATION_KEY: "EXPERIMENT_ACTIVATION",
@ -331,10 +333,19 @@ function AlreadyShutdownError(message="already shut down") {
this.message = message;
this.stack = error.stack;
}
AlreadyShutdownError.prototype = Object.create(Error.prototype);
AlreadyShutdownError.prototype.constructor = AlreadyShutdownError;
function CacheWriteError(message="Error writing cache file") {
Error.call(this, message);
let error = new Error();
this.name = "CacheWriteError";
this.message = message;
this.stack = error.stack;
}
CacheWriteError.prototype = Object.create(Error.prototype);
CacheWriteError.prototype.constructor = CacheWriteError;
/**
* Manages the experiments and provides an interface to control them.
*/
@ -690,6 +701,7 @@ Experiments.Experiments.prototype = {
throw new Error("Experiment not found");
}
e.branch = String(branchstr);
this._log.trace("setExperimentBranch(" + id + ", " + e.branch + ") _dirty=" + this._dirty);
this._dirty = true;
Services.obs.notifyObservers(null, EXPERIMENTS_CHANGED_TOPIC, null);
yield this._run();
@ -766,6 +778,8 @@ Experiments.Experiments.prototype = {
this._mainTask = Task.spawn(function*() {
try {
yield this._main();
} catch (e if e instanceof CacheWriteError) {
// In this case we want to reschedule
} catch (e) {
this._log.error("_main caught error: " + e);
return;
@ -801,7 +815,7 @@ Experiments.Experiments.prototype = {
// If somebody called .updateManifest() or disableExperiment()
// while we were running, go again right now.
}
while (this._refresh || this._terminateReason);
while (this._refresh || this._terminateReason || this._dirty);
},
_loadManifest: function*() {
@ -992,7 +1006,7 @@ Experiments.Experiments.prototype = {
// We failed to write the cache, it's still dirty.
this._dirty = true;
this._log.error("_saveToCache failed and caught error: " + e);
return;
throw new CacheWriteError();
}
this._log.debug("_saveToCache saved to " + path);
@ -1307,6 +1321,10 @@ Experiments.Experiments.prototype = {
let time = null;
let now = this._policy.now().getTime();
if (this._dirty) {
// If we failed to write the cache, we should try again periodically
time = now + 1000 * CACHE_WRITE_RETRY_DELAY_SEC;
}
for (let [id, experiment] of this._experiments) {
let scheduleTime = experiment.getScheduleTime();

View File

@ -311,7 +311,7 @@ def print_command(out, args):
print >>out, "".join([" " + l for l in file.readlines()])
out.flush()
def main():
def main(args, proc_callback=None):
parser = OptionParser()
parser.add_option("--extract", action="store_true", dest="extract",
help="when a library has no descriptor file, extract it first, when possible")
@ -322,7 +322,7 @@ def main():
parser.add_option("--symbol-order", dest="symbol_order", metavar="FILE",
help="use the given list of symbols to order symbols in the resulting binary when using with a linker")
(options, args) = parser.parse_args()
(options, args) = parser.parse_args(args)
with ExpandArgsMore(args) as args:
if options.extract:
@ -336,6 +336,8 @@ def main():
print_command(sys.stderr, args)
try:
proc = subprocess.Popen(args, stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
if proc_callback:
proc_callback(proc)
except Exception, e:
print >>sys.stderr, 'error: Launching', args, ':', e
raise e
@ -345,7 +347,8 @@ def main():
sys.stderr.write(stdout)
sys.stderr.flush()
if proc.returncode:
exit(proc.returncode)
return proc.returncode
return 0
if __name__ == '__main__':
main()
exit(main(sys.argv[1:]))

View File

@ -2,8 +2,11 @@
# 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/.
from __future__ import with_statement
import os, subprocess, sys, threading, time
import expandlibs_exec
import sys
import threading
import time
from win32 import procmem
def measure_vsize_threadfunc(proc, output_file):
@ -33,14 +36,17 @@ def measure_link_vsize(output_file, args):
Execute |args|, and measure the maximum virtual memory usage of the process,
printing it to stdout when finished.
"""
proc = subprocess.Popen(args)
t = threading.Thread(target=measure_vsize_threadfunc,
args=(proc, output_file))
t.start()
# Wait for the linker to finish.
exitcode = proc.wait()
# ...and then wait for the background thread to finish.
t.join()
# This needs to be a list in order for the callback to set the
# variable properly with python-2's scoping rules.
t = [None]
def callback(proc):
t[0] = threading.Thread(target=measure_vsize_threadfunc,
args=(proc, output_file))
t[0].start()
exitcode = expandlibs_exec.main(args, proc_callback=callback)
# Wait for the background thread to finish.
t[0].join()
return exitcode
if __name__ == "__main__":
@ -50,4 +56,5 @@ if __name__ == "__main__":
if len(sys.argv) < 3:
print >>sys.stderr, "Usage: link.py <output filename> <commandline>"
sys.exit(1)
sys.exit(measure_link_vsize(sys.argv[1], sys.argv[2:]))
output_file = sys.argv.pop(1)
sys.exit(measure_link_vsize(output_file, sys.argv[1:]))

View File

@ -178,7 +178,7 @@ LOCAL_INCLUDES += [
'/dom/base',
'/dom/canvas',
'/dom/xbl',
'/editor/libeditor/base',
'/editor/libeditor',
'/editor/libeditor/text',
'/editor/txmgr',
'/layout/forms',

View File

@ -113,12 +113,14 @@ ImageListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
return NS_OK;
}
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(imgDoc->mImageContent);
NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED);
if (!imgDoc->mObservingImageLoader) {
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(imgDoc->mImageContent);
NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED);
imageLoader->AddObserver(imgDoc);
imgDoc->mObservingImageLoader = true;
imageLoader->LoadImageWithChannel(channel, getter_AddRefs(mNextStream));
imageLoader->AddObserver(imgDoc);
imgDoc->mObservingImageLoader = true;
imageLoader->LoadImageWithChannel(channel, getter_AddRefs(mNextStream));
}
return MediaDocumentStreamListener::OnStartRequest(request, ctxt);
}
@ -384,12 +386,14 @@ ImageDocument::ScrollImageTo(int32_t aX, int32_t aY, bool restoreImage)
}
nsCOMPtr<nsIPresShell> shell = GetShell();
if (!shell)
if (!shell) {
return;
}
nsIScrollableFrame* sf = shell->GetRootScrollFrameAsScrollable();
if (!sf)
if (!sf) {
return;
}
nsRect portRect = sf->GetScrollPortRect();
sf->ScrollTo(nsPoint(nsPresContext::CSSPixelsToAppUnits(aX/ratio) - portRect.width/2,

View File

@ -21,6 +21,7 @@
#include "mozilla/Services.h"
#include "nsServiceManagerUtils.h"
#include "nsIPrincipal.h"
#include "nsIMultiPartChannel.h"
namespace mozilla {
namespace dom {
@ -70,9 +71,16 @@ MediaDocumentStreamListener::OnStopRequest(nsIRequest* request,
rv = mNextStream->OnStopRequest(request, ctxt, status);
}
// No more need for our document so clear our reference and prevent leaks
mDocument = nullptr;
// Don't release mDocument here if we're in the middle of a multipart response.
bool lastPart = true;
nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(request));
if (mpchan) {
mpchan->GetIsLastPart(&lastPart);
}
if (lastPart) {
mDocument = nullptr;
}
return rv;
}

View File

@ -441,7 +441,8 @@ DecoderTraits::CanHandleMediaType(const char* aMIMEType,
}
#endif
#ifdef MOZ_WMF
if (IsWMFSupportedType(nsDependentCString(aMIMEType))) {
if (Preferences::GetBool("media.fragmented-mp4.exposed", false) &&
IsWMFSupportedType(nsDependentCString(aMIMEType))) {
if (!aHaveRequestedCodecs) {
return CANPLAY_MAYBE;
}

View File

@ -758,6 +758,9 @@ public:
MediaInfo* aInfo,
MetadataTags* aTags);
int64_t GetSeekTime() { return mRequestedSeekTarget.mTime; }
void ResetSeekTime() { mRequestedSeekTarget.Reset(); }
/******
* The following methods must only be called on the main
* thread.

View File

@ -1518,17 +1518,16 @@ MediaDecoderStateMachine::EnqueueDecodeMetadataTask()
mDispatchedDecodeMetadataTask) {
return NS_OK;
}
mDispatchedDecodeMetadataTask = true;
RefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::CallDecodeMetadata));
nsresult rv = mDecodeTaskQueue->Dispatch(task);
if (NS_SUCCEEDED(rv)) {
mDispatchedDecodeMetadataTask = true;
} else {
if (NS_FAILED(rv)) {
NS_WARNING("Dispatch ReadMetadata task failed.");
return rv;
mDispatchedDecodeMetadataTask = false;
}
return NS_OK;
return rv;
}
void
@ -1842,7 +1841,6 @@ void
MediaDecoderStateMachine::CallDecodeMetadata()
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
AutoSetOnScopeExit<bool> unsetOnExit(mDispatchedDecodeMetadataTask, false);
if (mState != DECODER_STATE_DECODING_METADATA) {
return;
}
@ -1874,6 +1872,7 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
// change state to DECODER_STATE_WAIT_FOR_RESOURCES
StartWaitForResources();
// affect values only if ReadMetadata succeeds
mDispatchedDecodeMetadataTask = false;
return NS_OK;
}
}
@ -1996,6 +1995,7 @@ MediaDecoderStateMachine::FinishDecodeMetadata()
StartPlayback();
}
mDispatchedDecodeMetadataTask = false;
return NS_OK;
}
@ -2443,23 +2443,23 @@ MediaDecoderStateMachine::FlushDecoding()
"Should be on state machine or decode thread.");
mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
// Put a task in the decode queue to abort any decoding operations.
// The reader is not supposed to put any tasks to deliver samples into
// the queue after we call this (unless we request another sample from it).
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(mReader, &MediaDecoderReader::ResetDecode);
mDecodeTaskQueue->Dispatch(task);
{
// Wait for the thread decoding to abort decoding operations and run
// any pending callbacks. This is important, as we don't want any
// pending tasks posted to the task queue by the reader to deliver
// any samples after we've posted the reader Shutdown() task below,
// as the sample-delivery tasks will keep video frames alive until
// after we've called Reader::Shutdown(), and shutdown on B2G will
// fail as there are outstanding video frames alive.
// Put a task in the decode queue to abort any decoding operations.
// The reader is not supposed to put any tasks to deliver samples into
// the queue after this runs (unless we request another sample from it).
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(mReader, &MediaDecoderReader::ResetDecode);
// Wait for the ResetDecode to run and for the decoder to abort
// decoding operations and run any pending callbacks. This is
// important, as we don't want any pending tasks posted to the task
// queue by the reader to deliver any samples after we've posted the
// reader Shutdown() task below, as the sample-delivery tasks will
// keep video frames alive until after we've called Reader::Shutdown(),
// and shutdown on B2G will fail as there are outstanding video frames
// alive.
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
mDecodeTaskQueue->Flush();
mDecodeTaskQueue->FlushAndDispatch(task);
}
// We must reset playback so that all references to frames queued

View File

@ -15,6 +15,7 @@ MediaTaskQueue::MediaTaskQueue(TemporaryRef<SharedThreadPool> aPool)
, mQueueMonitor("MediaTaskQueue::Queue")
, mIsRunning(false)
, mIsShutdown(false)
, mIsFlushing(false)
{
MOZ_COUNT_CTOR(MediaTaskQueue);
}
@ -30,6 +31,17 @@ nsresult
MediaTaskQueue::Dispatch(TemporaryRef<nsIRunnable> aRunnable)
{
MonitorAutoLock mon(mQueueMonitor);
return DispatchLocked(aRunnable, AbortIfFlushing);
}
nsresult
MediaTaskQueue::DispatchLocked(TemporaryRef<nsIRunnable> aRunnable,
DispatchMode aMode)
{
mQueueMonitor.AssertCurrentThreadOwns();
if (mIsFlushing && aMode == AbortIfFlushing) {
return NS_ERROR_ABORT;
}
if (mIsShutdown) {
return NS_ERROR_FAILURE;
}
@ -113,10 +125,25 @@ MediaTaskQueue::Shutdown()
AwaitIdleLocked();
}
nsresult
MediaTaskQueue::FlushAndDispatch(TemporaryRef<nsIRunnable> aRunnable)
{
MonitorAutoLock mon(mQueueMonitor);
AutoSetFlushing autoFlush(this);
while (!mTasks.empty()) {
mTasks.pop();
}
nsresult rv = DispatchLocked(aRunnable, IgnoreFlushing);
NS_ENSURE_SUCCESS(rv, rv);
AwaitIdleLocked();
return NS_OK;
}
void
MediaTaskQueue::Flush()
{
MonitorAutoLock mon(mQueueMonitor);
AutoSetFlushing autoFlush(this);
while (!mTasks.empty()) {
mTasks.pop();
}

View File

@ -35,6 +35,8 @@ public:
nsresult SyncDispatch(TemporaryRef<nsIRunnable> aRunnable);
nsresult FlushAndDispatch(TemporaryRef<nsIRunnable> aRunnable);
// Removes all pending tasks from the task queue, and blocks until
// the currently running task (if any) finishes.
void Flush();
@ -59,6 +61,11 @@ private:
// mQueueMonitor must be held.
void AwaitIdleLocked();
enum DispatchMode { AbortIfFlushing, IgnoreFlushing };
nsresult DispatchLocked(TemporaryRef<nsIRunnable> aRunnable,
DispatchMode aMode);
RefPtr<SharedThreadPool> mPool;
// Monitor that protects the queue and mIsRunning;
@ -79,6 +86,27 @@ private:
// True if we've started our shutdown process.
bool mIsShutdown;
class MOZ_STACK_CLASS AutoSetFlushing
{
public:
AutoSetFlushing(MediaTaskQueue* aTaskQueue) : mTaskQueue(aTaskQueue)
{
mTaskQueue->mQueueMonitor.AssertCurrentThreadOwns();
mTaskQueue->mIsFlushing = true;
}
~AutoSetFlushing()
{
mTaskQueue->mQueueMonitor.AssertCurrentThreadOwns();
mTaskQueue->mIsFlushing = false;
}
private:
MediaTaskQueue* mTaskQueue;
};
// True if we're flushing; we reject new tasks if we're flushing.
bool mIsFlushing;
class Runner : public nsRunnable {
public:
Runner(MediaTaskQueue* aQueue)

View File

@ -229,9 +229,9 @@ public:
// Decode thread.
virtual already_AddRefed<MediaDataDecoder>
CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE {
CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE {
BlankAudioDataCreator* creator = new BlankAudioDataCreator(
aConfig.channel_count, aConfig.samples_per_second);
@ -241,6 +241,13 @@ public:
aCallback);
return decoder.forget();
}
virtual bool
SupportsAudioMimeType(const char* aMimeType) MOZ_OVERRIDE
{
return true;
}
};
PlatformDecoderModule* CreateBlankDecoderModule()

View File

@ -288,6 +288,14 @@ MP4Reader::ExtractCryptoInitData(nsTArray<uint8_t>& aInitData)
}
}
bool
MP4Reader::IsSupportedAudioMimeType(const char* aMimeType)
{
return (!strcmp(aMimeType, "audio/mpeg") ||
!strcmp(aMimeType, "audio/mp4a-latm")) &&
mPlatform->SupportsAudioMimeType(aMimeType);
}
nsresult
MP4Reader::ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags)
@ -296,13 +304,6 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
bool ok = mDemuxer->Init();
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
mInfo.mAudio.mHasAudio = mAudio.mActive = mDemuxer->HasValidAudio();
const AudioDecoderConfig& audio = mDemuxer->AudioConfig();
// If we have audio, we *only* allow AAC to be decoded.
if (mInfo.mAudio.mHasAudio && strcmp(audio.mime_type, "audio/mp4a-latm")) {
return NS_ERROR_FAILURE;
}
mInfo.mVideo.mHasVideo = mVideo.mActive = mDemuxer->HasValidVideo();
const VideoDecoderConfig& video = mDemuxer->VideoConfig();
// If we have video, we *only* allow H.264 to be decoded.
@ -370,14 +371,18 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
}
if (HasAudio()) {
if (mDemuxer->HasValidAudio()) {
const AudioDecoderConfig& audio = mDemuxer->AudioConfig();
mInfo.mAudio.mHasAudio = mAudio.mActive = true;
if (mInfo.mAudio.mHasAudio && !IsSupportedAudioMimeType(audio.mime_type)) {
return NS_ERROR_FAILURE;
}
mInfo.mAudio.mRate = audio.samples_per_second;
mInfo.mAudio.mChannels = audio.channel_count;
mAudio.mCallback = new DecoderCallback(this, kAudio);
mAudio.mDecoder = mPlatform->CreateAACDecoder(audio,
mAudio.mTaskQueue,
mAudio.mCallback);
mAudio.mDecoder = mPlatform->CreateAudioDecoder(audio,
mAudio.mTaskQueue,
mAudio.mCallback);
NS_ENSURE_TRUE(mAudio.mDecoder != nullptr, NS_ERROR_FAILURE);
nsresult rv = mAudio.mDecoder->Init();
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -87,6 +87,7 @@ private:
void Flush(mp4_demuxer::TrackType aTrack);
void DrainComplete(mp4_demuxer::TrackType aTrack);
void UpdateIndex();
bool IsSupportedAudioMimeType(const char* aMimeType);
void NotifyResourcesStatusChanged();
bool IsWaitingOnCodecResource();
bool IsWaitingOnCDMResource();

View File

@ -153,4 +153,10 @@ PlatformDecoderModule::Create()
return nullptr;
}
bool
PlatformDecoderModule::SupportsAudioMimeType(const char* aMimeType)
{
return !strcmp(aMimeType, "audio/mp4a-latm");
}
} // namespace mozilla

View File

@ -35,8 +35,8 @@ class CDMProxy;
typedef int64_t Microseconds;
// The PlatformDecoderModule interface is used by the MP4Reader to abstract
// access to the H264 and AAC decoders provided by various platforms. It
// may be extended to support other codecs in future. Each platform (Windows,
// access to the H264 and Audio (AAC/MP3) decoders provided by various platforms.
// It may be extended to support other codecs in future. Each platform (Windows,
// MacOSX, Linux, B2G etc) must implement a PlatformDecoderModule to provide
// access to its decoders in order to get decompressed H.264/AAC from the
// MP4Reader.
@ -103,7 +103,7 @@ public:
MediaTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback) = 0;
// Creates an AAC decoder with the specified properties.
// Creates an Audio decoder with the specified properties.
// Asynchronous decoding of audio should be done in runnables dispatched to
// aAudioTaskQueue. If the task queue isn't needed, the decoder should
// not hold a reference to it.
@ -114,9 +114,14 @@ public:
// It is safe to store a reference to aConfig.
// This is called on the decode task queue.
virtual already_AddRefed<MediaDataDecoder>
CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) = 0;
CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) = 0;
// An audio decoder module must support AAC by default.
// If more audio codec is to be supported, SupportsAudioMimeType will have
// to be extended
virtual bool SupportsAudioMimeType(const char* aMimeType);
virtual ~PlatformDecoderModule() {}

View File

@ -37,13 +37,20 @@ AppleATDecoder::AppleATDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
, mHaveOutput(false)
{
MOZ_COUNT_CTOR(AppleATDecoder);
LOG("Creating Apple AudioToolbox AAC decoder");
LOG("Creating Apple AudioToolbox Audio decoder");
LOG("Audio Decoder configuration: %s %d Hz %d channels %d bits per channel",
mConfig.mime_type,
mConfig.samples_per_second,
mConfig.channel_count,
mConfig.bits_per_sample);
// TODO: Verify aConfig.mime_type.
if (!strcmp(aConfig.mime_type, "audio/mpeg")) {
mFileType = kAudioFileMP3Type;
} else if (!strcmp(aConfig.mime_type, "audio/mp4a-latm")) {
mFileType = kAudioFileAAC_ADTSType;
} else {
mFileType = 0;
}
}
AppleATDecoder::~AppleATDecoder()
@ -54,10 +61,10 @@ AppleATDecoder::~AppleATDecoder()
}
static void
_MetadataCallback(void *aDecoder,
_MetadataCallback(void* aDecoder,
AudioFileStreamID aStream,
AudioFileStreamPropertyID aProperty,
UInt32 *aFlags)
UInt32* aFlags)
{
LOG("AppleATDecoder metadata callback");
AppleATDecoder* decoder = static_cast<AppleATDecoder*>(aDecoder);
@ -65,10 +72,11 @@ _MetadataCallback(void *aDecoder,
}
static void
_SampleCallback(void *aDecoder,
UInt32 aNumBytes, UInt32 aNumPackets,
const void *aData,
AudioStreamPacketDescription *aPackets)
_SampleCallback(void* aDecoder,
UInt32 aNumBytes,
UInt32 aNumPackets,
const void* aData,
AudioStreamPacketDescription* aPackets)
{
LOG("AppleATDecoder sample callback %u bytes %u packets",
aNumBytes, aNumPackets);
@ -79,12 +87,15 @@ _SampleCallback(void *aDecoder,
nsresult
AppleATDecoder::Init()
{
LOG("Initializing Apple AudioToolbox AAC decoder");
AudioFileTypeID fileType = kAudioFileAAC_ADTSType;
if (!mFileType) {
NS_ERROR("Non recognised format");
return NS_ERROR_FAILURE;
}
LOG("Initializing Apple AudioToolbox Audio decoder");
OSStatus rv = AudioFileStreamOpen(this,
_MetadataCallback,
_SampleCallback,
fileType,
mFileType,
&mStream);
if (rv) {
NS_ERROR("Couldn't open AudioFileStream");
@ -170,8 +181,8 @@ struct PassthroughUserData {
AppleATDecoder* mDecoder;
UInt32 mNumPackets;
UInt32 mDataSize;
const void *mData;
AudioStreamPacketDescription *mPacketDesc;
const void* mData;
AudioStreamPacketDescription* mPacketDesc;
bool mDone;
};
@ -181,12 +192,12 @@ const uint32_t kNeedMoreData = 'MOAR';
static OSStatus
_PassthroughInputDataCallback(AudioConverterRef aAudioConverter,
UInt32 *aNumDataPackets /* in/out */,
AudioBufferList *aData /* in/out */,
AudioStreamPacketDescription **aPacketDesc,
void *aUserData)
UInt32* aNumDataPackets /* in/out */,
AudioBufferList* aData /* in/out */,
AudioStreamPacketDescription** aPacketDesc,
void* aUserData)
{
PassthroughUserData *userData = (PassthroughUserData *)aUserData;
PassthroughUserData* userData = (PassthroughUserData*)aUserData;
if (userData->mDone) {
// We make sure this callback is run _once_, with all the data we received
// from |AudioFileStreamParseBytes|. When we return an error, the decoder
@ -207,7 +218,7 @@ _PassthroughInputDataCallback(AudioConverterRef aAudioConverter,
aData->mBuffers[0].mNumberChannels = userData->mDecoder->mConfig.channel_count;
aData->mBuffers[0].mDataByteSize = userData->mDataSize;
aData->mBuffers[0].mData = const_cast<void *>(userData->mData);
aData->mBuffers[0].mData = const_cast<void*>(userData->mData);
return noErr;
}
@ -280,9 +291,9 @@ AppleATDecoder::SampleCallback(uint32_t aNumBytes,
LOG("pushed audio at time %lfs; duration %lfs\n",
(double)time / USECS_PER_S, (double)duration / USECS_PER_S);
AudioData *audio = new AudioData(mSamplePosition,
AudioData* audio = new AudioData(mSamplePosition,
time, duration, numFrames,
reinterpret_cast<AudioDataValue *>(decoded.forget()),
reinterpret_cast<AudioDataValue*>(decoded.forget()),
channels, rate);
mCallback->Output(audio);
mHaveOutput = true;
@ -302,9 +313,14 @@ void
AppleATDecoder::SetupDecoder()
{
AudioStreamBasicDescription inputFormat;
// Fill in the input format description from the stream.
AppleUtils::GetProperty(mStream,
kAudioFileStreamProperty_DataFormat, &inputFormat);
mHaveOutput = false;
nsresult rv = AppleUtils::GetRichestDecodableFormat(mStream, inputFormat);
if (NS_FAILED(rv)) {
mCallback->Error();
return;
}
// Fill in the output format manually.
PodZero(&mOutputFormat);
@ -324,13 +340,13 @@ AppleATDecoder::SetupDecoder()
mOutputFormat.mBytesPerPacket = mOutputFormat.mBytesPerFrame
= mOutputFormat.mChannelsPerFrame * mOutputFormat.mBitsPerChannel / 8;
OSStatus rv = AudioConverterNew(&inputFormat, &mOutputFormat, &mConverter);
if (rv) {
OSStatus status =
AudioConverterNew(&inputFormat, &mOutputFormat, &mConverter);
if (status) {
LOG("Error %d constructing AudioConverter", rv);
mConverter = nullptr;
mCallback->Error();
}
mHaveOutput = false;
}
void

View File

@ -53,6 +53,7 @@ private:
int64_t mSamplePosition;
bool mHaveOutput;
AudioStreamBasicDescription mOutputFormat;
AudioFileTypeID mFileType;
void SetupDecoder();
void SubmitSample(nsAutoPtr<mp4_demuxer::MP4Sample> aSample);

View File

@ -87,13 +87,19 @@ AppleDecoderModule::CreateH264Decoder(const mp4_demuxer::VideoDecoderConfig& aCo
}
already_AddRefed<MediaDataDecoder>
AppleDecoderModule::CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback)
AppleDecoderModule::CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback)
{
nsRefPtr<MediaDataDecoder> decoder =
new AppleATDecoder(aConfig, aAudioTaskQueue, aCallback);
return decoder.forget();
}
bool
AppleDecoderModule::SupportsAudioMimeType(const char* aMimeType)
{
return !strcmp(aMimeType, "audio/mp4a-latm") || !strcmp(aMimeType, "audio/mpeg");
}
} // namespace mozilla

View File

@ -34,9 +34,11 @@ public:
// Decode thread.
virtual already_AddRefed<MediaDataDecoder>
CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
virtual bool SupportsAudioMimeType(const char* aMimeType) MOZ_OVERRIDE;
static void Init();
private:

View File

@ -4,9 +4,9 @@
// Utility functions to help with Apple API calls.
#include <AudioToolbox/AudioToolbox.h>
#include "AppleUtils.h"
#include "prlog.h"
#include "nsAutoPtr.h"
#ifdef PR_LOGGING
PRLogModuleInfo* GetDemuxerLog();
@ -26,12 +26,12 @@ namespace mozilla {
nsresult
AppleUtils::GetProperty(AudioFileStreamID aAudioFileStream,
AudioFileStreamPropertyID aPropertyID,
void *aData)
void* aData)
{
UInt32 size;
Boolean writeable;
OSStatus rv = AudioFileStreamGetPropertyInfo(aAudioFileStream, aPropertyID,
&size, &writeable);
&size, &writeable);
if (rv) {
WARN("Couldn't get property " PROPERTY_ID_FORMAT "\n",
@ -80,5 +80,52 @@ AppleUtils::SetCFDict(CFMutableDictionaryRef dict,
CFDictionarySetValue(dict, keyRef, value ? kCFBooleanTrue : kCFBooleanFalse);
}
nsresult
AppleUtils::GetRichestDecodableFormat(AudioFileStreamID aAudioFileStream,
AudioStreamBasicDescription& aFormat)
{
// Fill in the default format description from the stream.
nsresult rv = GetProperty(aAudioFileStream,
kAudioFileStreamProperty_DataFormat, &aFormat);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
UInt32 propertySize;
OSStatus status = AudioFileStreamGetPropertyInfo(
aAudioFileStream, kAudioFileStreamProperty_FormatList, &propertySize, NULL);
if (NS_WARN_IF(status)) {
// Return the default format description.
return NS_OK;
}
MOZ_ASSERT(propertySize % sizeof(AudioFormatListItem) == 0);
uint32_t sizeList = propertySize / sizeof(AudioFormatListItem);
nsAutoArrayPtr<AudioFormatListItem> formatListPtr(
new AudioFormatListItem[sizeList]);
rv = GetProperty(aAudioFileStream, kAudioFileStreamProperty_FormatList,
formatListPtr);
if (NS_WARN_IF(NS_FAILED(rv))) {
// Return the default format description.
return NS_OK;
}
// Get the index number of the first playable format.
// This index number will be for the highest quality layer the platform
// is capable of playing.
UInt32 itemIndex;
UInt32 indexSize = sizeof(itemIndex);
status =
AudioFormatGetProperty(kAudioFormatProperty_FirstPlayableFormatFromList,
propertySize, formatListPtr, &indexSize, &itemIndex);
if (NS_WARN_IF(status)) {
// Return the default format description.
return NS_OK;
}
aFormat = formatListPtr[itemIndex].mASBD;
return NS_OK;
}
} // namespace mozilla

View File

@ -16,7 +16,7 @@ struct AppleUtils {
// Helper to retrieve properties from AudioFileStream objects.
static nsresult GetProperty(AudioFileStreamID aAudioFileStream,
AudioFileStreamPropertyID aPropertyID,
void *aData);
void* aData);
// Helper to set a string, string pair on a CFMutableDictionaryRef.
static void SetCFDict(CFMutableDictionaryRef dict,
@ -30,6 +30,12 @@ struct AppleUtils {
static void SetCFDict(CFMutableDictionaryRef dict,
const char* key,
bool value);
// Helper to retrieve the best audio format available in the given
// audio stream.
// The basic format will be returned by default should an error occur.
static nsresult GetRichestDecodableFormat(
AudioFileStreamID aAudioFileStream, AudioStreamBasicDescription& aFormat);
};
// Wrapper class to call CFRelease on reference types

View File

@ -4,7 +4,7 @@
* 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 "EMEAACDecoder.h"
#include "EMEAudioDecoder.h"
#include "mp4_demuxer/DecoderData.h"
#include "mozilla/EMELog.h"
#include "gmp-audio-host.h"
@ -18,10 +18,10 @@
namespace mozilla {
EMEAACDecoder::EMEAACDecoder(CDMProxy* aProxy,
const AudioDecoderConfig& aConfig,
MediaTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback)
EMEAudioDecoder::EMEAudioDecoder(CDMProxy* aProxy,
const AudioDecoderConfig& aConfig,
MediaTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback)
: mAudioRate(0)
, mAudioBytesPerSample(0)
, mAudioChannels(0)
@ -34,17 +34,17 @@ EMEAACDecoder::EMEAACDecoder(CDMProxy* aProxy,
, mConfig(aConfig)
, mTaskQueue(aTaskQueue)
, mCallback(aCallback)
, mMonitor("EMEAACDecoder")
, mMonitor("EMEAudioDecoder")
, mFlushComplete(false)
{
}
EMEAACDecoder::~EMEAACDecoder()
EMEAudioDecoder::~EMEAudioDecoder()
{
}
nsresult
EMEAACDecoder::Init()
EMEAudioDecoder::Init()
{
// Note: this runs on the decode task queue.
@ -65,7 +65,7 @@ EMEAACDecoder::Init()
}
nsresult
EMEAACDecoder::Input(MP4Sample* aSample)
EMEAudioDecoder::Input(MP4Sample* aSample)
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
@ -77,7 +77,7 @@ EMEAACDecoder::Input(MP4Sample* aSample)
}
nsresult
EMEAACDecoder::Flush()
EMEAudioDecoder::Flush()
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
@ -87,7 +87,7 @@ EMEAACDecoder::Flush()
}
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(this, &EMEAACDecoder::GmpFlush);
task = NS_NewRunnableMethod(this, &EMEAudioDecoder::GmpFlush);
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
@ -102,34 +102,34 @@ EMEAACDecoder::Flush()
}
nsresult
EMEAACDecoder::Drain()
EMEAudioDecoder::Drain()
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(this, &EMEAACDecoder::GmpDrain);
task = NS_NewRunnableMethod(this, &EMEAudioDecoder::GmpDrain);
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
EMEAACDecoder::Shutdown()
EMEAudioDecoder::Shutdown()
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(this, &EMEAACDecoder::GmpShutdown);
task = NS_NewRunnableMethod(this, &EMEAudioDecoder::GmpShutdown);
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_SYNC);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
void
EMEAACDecoder::Decoded(const nsTArray<int16_t>& aPCM,
uint64_t aTimeStamp,
uint32_t aChannels,
uint32_t aRate)
EMEAudioDecoder::Decoded(const nsTArray<int16_t>& aPCM,
uint64_t aTimeStamp,
uint32_t aChannels,
uint32_t aRate)
{
MOZ_ASSERT(IsOnGMPThread());
@ -192,21 +192,21 @@ EMEAACDecoder::Decoded(const nsTArray<int16_t>& aPCM,
}
void
EMEAACDecoder::InputDataExhausted()
EMEAudioDecoder::InputDataExhausted()
{
MOZ_ASSERT(IsOnGMPThread());
mCallback->InputExhausted();
}
void
EMEAACDecoder::DrainComplete()
EMEAudioDecoder::DrainComplete()
{
MOZ_ASSERT(IsOnGMPThread());
mCallback->DrainComplete();
}
void
EMEAACDecoder::ResetComplete()
EMEAudioDecoder::ResetComplete()
{
MOZ_ASSERT(IsOnGMPThread());
mMustRecaptureAudioPosition = true;
@ -218,23 +218,23 @@ EMEAACDecoder::ResetComplete()
}
void
EMEAACDecoder::Error(GMPErr aErr)
EMEAudioDecoder::Error(GMPErr aErr)
{
MOZ_ASSERT(IsOnGMPThread());
EME_LOG("EMEAACDecoder::Error");
EME_LOG("EMEAudioDecoder::Error");
mCallback->Error();
GmpShutdown();
}
void
EMEAACDecoder::Terminated()
EMEAudioDecoder::Terminated()
{
MOZ_ASSERT(IsOnGMPThread());
GmpShutdown();
}
nsresult
EMEAACDecoder::GmpInit()
EMEAudioDecoder::GmpInit()
{
MOZ_ASSERT(IsOnGMPThread());
@ -266,7 +266,7 @@ EMEAACDecoder::GmpInit()
}
nsresult
EMEAACDecoder::GmpInput(MP4Sample* aSample)
EMEAudioDecoder::GmpInput(MP4Sample* aSample)
{
MOZ_ASSERT(IsOnGMPThread());
nsAutoPtr<MP4Sample> sample(aSample);
@ -296,7 +296,7 @@ EMEAACDecoder::GmpInput(MP4Sample* aSample)
}
void
EMEAACDecoder::GmpFlush()
EMEAudioDecoder::GmpFlush()
{
MOZ_ASSERT(IsOnGMPThread());
if (!mGMP || NS_FAILED(mGMP->Reset())) {
@ -308,7 +308,7 @@ EMEAACDecoder::GmpFlush()
}
void
EMEAACDecoder::GmpDrain()
EMEAudioDecoder::GmpDrain()
{
MOZ_ASSERT(IsOnGMPThread());
if (!mGMP || NS_FAILED(mGMP->Drain())) {
@ -317,7 +317,7 @@ EMEAACDecoder::GmpDrain()
}
void
EMEAACDecoder::GmpShutdown()
EMEAudioDecoder::GmpShutdown()
{
MOZ_ASSERT(IsOnGMPThread());
if (!mGMP) {

View File

@ -16,18 +16,18 @@
namespace mozilla {
class EMEAACDecoder : public MediaDataDecoder
, public GMPAudioDecoderProxyCallback
class EMEAudioDecoder : public MediaDataDecoder
, public GMPAudioDecoderProxyCallback
{
typedef mp4_demuxer::MP4Sample MP4Sample;
typedef mp4_demuxer::AudioDecoderConfig AudioDecoderConfig;
public:
EMEAACDecoder(CDMProxy* aProxy,
const AudioDecoderConfig& aConfig,
MediaTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback);
EMEAudioDecoder(CDMProxy* aProxy,
const AudioDecoderConfig& aConfig,
MediaTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback);
~EMEAACDecoder();
~EMEAudioDecoder();
// MediaDataDecoder implementation.
virtual nsresult Init() MOZ_OVERRIDE;
@ -51,7 +51,7 @@ private:
class DeliverSample : public nsRunnable {
public:
DeliverSample(EMEAACDecoder* aDecoder,
DeliverSample(EMEAudioDecoder* aDecoder,
mp4_demuxer::MP4Sample* aSample)
: mDecoder(aDecoder)
, mSample(aSample)
@ -62,13 +62,13 @@ private:
return NS_OK;
}
private:
nsRefPtr<EMEAACDecoder> mDecoder;
nsRefPtr<EMEAudioDecoder> mDecoder;
nsAutoPtr<mp4_demuxer::MP4Sample> mSample;
};
class InitTask : public nsRunnable {
public:
InitTask(EMEAACDecoder* aDecoder)
InitTask(EMEAudioDecoder* aDecoder)
: mDecoder(aDecoder)
{}
NS_IMETHOD Run() {
@ -76,7 +76,7 @@ private:
return NS_OK;
}
nsresult mResult;
EMEAACDecoder* mDecoder;
EMEAudioDecoder* mDecoder;
};
nsresult GmpInit();
@ -114,4 +114,4 @@ private:
} // namespace mozilla
#endif
#endif

View File

@ -19,7 +19,7 @@
#include "SharedThreadPool.h"
#include "mozilla/EMELog.h"
#include "EMEH264Decoder.h"
#include "EMEAACDecoder.h"
#include "EMEAudioDecoder.h"
#include <string>
namespace mozilla {
@ -227,21 +227,21 @@ EMEDecoderModule::CreateH264Decoder(const VideoDecoderConfig& aConfig,
}
already_AddRefed<MediaDataDecoder>
EMEDecoderModule::CreateAACDecoder(const AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback)
EMEDecoderModule::CreateAudioDecoder(const AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback)
{
if (mCDMDecodesAudio) {
nsRefPtr<MediaDataDecoder> decoder(new EMEAACDecoder(mProxy,
aConfig,
aAudioTaskQueue,
aCallback));
nsRefPtr<MediaDataDecoder> decoder(new EMEAudioDecoder(mProxy,
aConfig,
aAudioTaskQueue,
aCallback));
return decoder.forget();
}
nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateAACDecoder(aConfig,
aAudioTaskQueue,
aCallback));
nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateAudioDecoder(aConfig,
aAudioTaskQueue,
aCallback));
if (!decoder) {
return nullptr;
}

View File

@ -42,9 +42,9 @@ public:
// Decode thread.
virtual already_AddRefed<MediaDataDecoder>
CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
private:
nsRefPtr<CDMProxy> mProxy;

View File

@ -5,13 +5,13 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS += [
'EMEAACDecoder.h',
'EMEAudioDecoder.h',
'EMEDecoderModule.h',
'EMEH264Decoder.h',
]
UNIFIED_SOURCES += [
'EMEAACDecoder.cpp',
'EMEAudioDecoder.cpp',
'EMEDecoderModule.cpp',
'EMEH264Decoder.cpp',
]

View File

@ -7,7 +7,7 @@
#include "MediaTaskQueue.h"
#include "FFmpegRuntimeLinker.h"
#include "FFmpegAACDecoder.h"
#include "FFmpegAudioDecoder.h"
#define MAX_CHANNELS 16
@ -16,16 +16,17 @@ typedef mp4_demuxer::MP4Sample MP4Sample;
namespace mozilla
{
FFmpegAACDecoder<LIBAV_VER>::FFmpegAACDecoder(
FFmpegAudioDecoder<LIBAV_VER>::FFmpegAudioDecoder(
MediaTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
const mp4_demuxer::AudioDecoderConfig& aConfig)
: FFmpegDataDecoder(aTaskQueue, AV_CODEC_ID_AAC), mCallback(aCallback)
: FFmpegDataDecoder(aTaskQueue, GetCodecId(aConfig.mime_type))
, mCallback(aCallback)
{
MOZ_COUNT_CTOR(FFmpegAACDecoder);
MOZ_COUNT_CTOR(FFmpegAudioDecoder);
}
nsresult
FFmpegAACDecoder<LIBAV_VER>::Init()
FFmpegAudioDecoder<LIBAV_VER>::Init()
{
nsresult rv = FFmpegDataDecoder::Init();
NS_ENSURE_SUCCESS(rv, rv);
@ -34,24 +35,44 @@ FFmpegAACDecoder<LIBAV_VER>::Init()
}
static AudioDataValue*
CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumSamples)
CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumAFrames)
{
MOZ_ASSERT(aNumChannels <= MAX_CHANNELS);
nsAutoArrayPtr<AudioDataValue> audio(
new AudioDataValue[aNumChannels * aNumSamples]);
AudioDataValue** data = reinterpret_cast<AudioDataValue**>(aFrame->data);
new AudioDataValue[aNumChannels * aNumAFrames]);
if (aFrame->format == AV_SAMPLE_FMT_FLT) {
// Audio data already packed. No need to do anything other than copy it
// into a buffer we own.
memcpy(audio, data[0], aNumChannels * aNumSamples * sizeof(AudioDataValue));
memcpy(audio, aFrame->data[0],
aNumChannels * aNumAFrames * sizeof(AudioDataValue));
} else if (aFrame->format == AV_SAMPLE_FMT_FLTP) {
// Planar audio data. Pack it into something we can understand.
for (uint32_t channel = 0; channel < aNumChannels; channel++) {
for (uint32_t sample = 0; sample < aNumSamples; sample++) {
audio[sample * aNumChannels + channel] = data[channel][sample];
AudioDataValue* tmp = audio;
AudioDataValue** data = reinterpret_cast<AudioDataValue**>(aFrame->data);
for (uint32_t frame = 0; frame < aNumAFrames; frame++) {
for (uint32_t channel = 0; channel < aNumChannels; channel++) {
*tmp++ = data[channel][frame];
}
}
} else if (aFrame->format == AV_SAMPLE_FMT_S16) {
// Audio data already packed. Need to convert from S16 to 32 bits Float
AudioDataValue* tmp = audio;
int16_t* data = reinterpret_cast<int16_t**>(aFrame->data)[0];
for (uint32_t frame = 0; frame < aNumAFrames; frame++) {
for (uint32_t channel = 0; channel < aNumChannels; channel++) {
*tmp++ = AudioSampleToFloat(*data++);
}
}
} else if (aFrame->format == AV_SAMPLE_FMT_S16P) {
// Planar audio data. Convert it from S16 to 32 bits float
// and pack it into something we can understand.
AudioDataValue* tmp = audio;
int16_t** data = reinterpret_cast<int16_t**>(aFrame->data);
for (uint32_t frame = 0; frame < aNumAFrames; frame++) {
for (uint32_t channel = 0; channel < aNumChannels; channel++) {
*tmp++ = AudioSampleToFloat(data[channel][frame]);
}
}
}
@ -60,7 +81,7 @@ CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumSamples)
}
void
FFmpegAACDecoder<LIBAV_VER>::DecodePacket(MP4Sample* aSample)
FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MP4Sample* aSample)
{
AVPacket packet;
av_init_packet(&packet);
@ -107,24 +128,38 @@ FFmpegAACDecoder<LIBAV_VER>::DecodePacket(MP4Sample* aSample)
}
nsresult
FFmpegAACDecoder<LIBAV_VER>::Input(MP4Sample* aSample)
FFmpegAudioDecoder<LIBAV_VER>::Input(MP4Sample* aSample)
{
mTaskQueue->Dispatch(NS_NewRunnableMethodWithArg<nsAutoPtr<MP4Sample> >(
this, &FFmpegAACDecoder::DecodePacket, nsAutoPtr<MP4Sample>(aSample)));
this, &FFmpegAudioDecoder::DecodePacket, nsAutoPtr<MP4Sample>(aSample)));
return NS_OK;
}
nsresult
FFmpegAACDecoder<LIBAV_VER>::Drain()
FFmpegAudioDecoder<LIBAV_VER>::Drain()
{
mCallback->DrainComplete();
return NS_OK;
}
FFmpegAACDecoder<LIBAV_VER>::~FFmpegAACDecoder()
AVCodecID
FFmpegAudioDecoder<LIBAV_VER>::GetCodecId(const char* aMimeType)
{
MOZ_COUNT_DTOR(FFmpegAACDecoder);
if (!strcmp(aMimeType, "audio/mpeg")) {
return AV_CODEC_ID_MP3;
}
if (!strcmp(aMimeType, "audio/mp4a-latm")) {
return AV_CODEC_ID_AAC;
}
return AV_CODEC_ID_NONE;
}
FFmpegAudioDecoder<LIBAV_VER>::~FFmpegAudioDecoder()
{
MOZ_COUNT_DTOR(FFmpegAudioDecoder);
}
} // namespace mozilla

View File

@ -13,22 +13,23 @@
namespace mozilla
{
template <int V> class FFmpegAACDecoder
template <int V> class FFmpegAudioDecoder
{
};
template <>
class FFmpegAACDecoder<LIBAV_VER> : public FFmpegDataDecoder<LIBAV_VER>
class FFmpegAudioDecoder<LIBAV_VER> : public FFmpegDataDecoder<LIBAV_VER>
{
public:
FFmpegAACDecoder(MediaTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
const mp4_demuxer::AudioDecoderConfig& aConfig);
virtual ~FFmpegAACDecoder();
FFmpegAudioDecoder(MediaTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
const mp4_demuxer::AudioDecoderConfig& aConfig);
virtual ~FFmpegAudioDecoder();
virtual nsresult Init() MOZ_OVERRIDE;
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
virtual nsresult Drain() MOZ_OVERRIDE;
static AVCodecID GetCodecId(const char* aMimeType);
private:
void DecodePacket(mp4_demuxer::MP4Sample* aSample);

View File

@ -108,8 +108,10 @@ FFmpegDataDecoder<LIBAV_VER>::Init()
if (mCodecContext->codec_type == AVMEDIA_TYPE_AUDIO &&
mCodecContext->sample_fmt != AV_SAMPLE_FMT_FLT &&
mCodecContext->sample_fmt != AV_SAMPLE_FMT_FLTP) {
NS_WARNING("FFmpeg AAC decoder outputs unsupported audio format.");
mCodecContext->sample_fmt != AV_SAMPLE_FMT_FLTP &&
mCodecContext->sample_fmt != AV_SAMPLE_FMT_S16 &&
mCodecContext->sample_fmt != AV_SAMPLE_FMT_S16P) {
NS_WARNING("FFmpeg audio decoder outputs unsupported audio format.");
return NS_ERROR_FAILURE;
}

View File

@ -8,7 +8,7 @@
#define __FFmpegDecoderModule_h__
#include "PlatformDecoderModule.h"
#include "FFmpegAACDecoder.h"
#include "FFmpegAudioDecoder.h"
#include "FFmpegH264Decoder.h"
namespace mozilla
@ -39,14 +39,19 @@ public:
}
virtual already_AddRefed<MediaDataDecoder>
CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE
CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE
{
nsRefPtr<MediaDataDecoder> decoder =
new FFmpegAACDecoder<V>(aAudioTaskQueue, aCallback, aConfig);
new FFmpegAudioDecoder<V>(aAudioTaskQueue, aCallback, aConfig);
return decoder.forget();
}
virtual bool SupportsAudioMimeType(const char* aMimeType) MOZ_OVERRIDE
{
return FFmpegAudioDecoder<V>::GetCodecId(aMimeType) != AV_CODEC_ID_NONE;
}
};
} // namespace mozilla

View File

@ -18,6 +18,8 @@ extern "C" {
#if LIBAVCODEC_VERSION_MAJOR < 55
#define AV_CODEC_ID_H264 CODEC_ID_H264
#define AV_CODEC_ID_AAC CODEC_ID_AAC
#define AV_CODEC_ID_MP3 CODEC_ID_MP3
#define AV_CODEC_ID_NONE CODEC_ID_NONE
typedef CodecID AVCodecID;
#endif

View File

@ -5,7 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
UNIFIED_SOURCES += [
'../FFmpegAACDecoder.cpp',
'../FFmpegAudioDecoder.cpp',
'../FFmpegDataDecoder.cpp',
'../FFmpegDecoderModule.cpp',
'../FFmpegH264Decoder.cpp',

View File

@ -5,7 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
UNIFIED_SOURCES += [
'../FFmpegAACDecoder.cpp',
'../FFmpegAudioDecoder.cpp',
'../FFmpegDataDecoder.cpp',
'../FFmpegDecoderModule.cpp',
'../FFmpegH264Decoder.cpp',

View File

@ -5,7 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
UNIFIED_SOURCES += [
'../FFmpegAACDecoder.cpp',
'../FFmpegAudioDecoder.cpp',
'../FFmpegDataDecoder.cpp',
'../FFmpegDecoderModule.cpp',
'../FFmpegH264Decoder.cpp',

View File

@ -48,9 +48,9 @@ GonkDecoderModule::CreateH264Decoder(const mp4_demuxer::VideoDecoderConfig& aCon
}
already_AddRefed<MediaDataDecoder>
GonkDecoderModule::CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback)
GonkDecoderModule::CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback)
{
nsRefPtr<MediaDataDecoder> decoder =
new GonkMediaDataDecoder(new GonkAudioDecoderManager(aConfig), aAudioTaskQueue,

View File

@ -29,9 +29,9 @@ public:
// Decode thread.
virtual already_AddRefed<MediaDataDecoder>
CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
static void Init();
};

View File

@ -81,9 +81,9 @@ WMFDecoderModule::CreateH264Decoder(const mp4_demuxer::VideoDecoderConfig& aConf
}
already_AddRefed<MediaDataDecoder>
WMFDecoderModule::CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback)
WMFDecoderModule::CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback)
{
nsRefPtr<MediaDataDecoder> decoder =
new WMFMediaDataDecoder(new WMFAudioMFTManager(aConfig),

View File

@ -30,9 +30,9 @@ public:
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
virtual already_AddRefed<MediaDataDecoder>
CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
// Called on main thread.
static void Init();

10
content/media/omx/AudioOffloadPlayer.cpp Executable file → Normal file
View File

@ -21,6 +21,7 @@
#include "nsComponentManagerUtils.h"
#include "nsITimer.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "VideoUtils.h"
#include <binder/IPCThreadState.h>
#include <stagefright/foundation/ADebug.h>
@ -51,7 +52,7 @@ PRLogModuleInfo* gAudioOffloadPlayerLog;
// When elapsed, the AudioSink is destroyed to allow the audio DSP to power down.
static const uint64_t OFFLOAD_PAUSE_MAX_MSECS = 60000ll;
AudioOffloadPlayer::AudioOffloadPlayer(MediaOmxDecoder* aObserver) :
AudioOffloadPlayer::AudioOffloadPlayer(MediaOmxCommonDecoder* aObserver) :
mObserver(aObserver),
mInputBuffer(nullptr),
mSampleRate(0),
@ -197,7 +198,8 @@ status_t AudioOffloadPlayer::ChangeState(MediaDecoder::PlayState aState)
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 MediaOmxDecoder
// offload track immediately. Resources will be freed by
// MediaOmxCommonDecoder
Pause();
break;
@ -421,14 +423,14 @@ void AudioOffloadPlayer::NotifyAudioEOS()
void AudioOffloadPlayer::NotifyPositionChanged()
{
nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver,
&MediaOmxDecoder::PlaybackPositionChanged);
&MediaOmxCommonDecoder::PlaybackPositionChanged);
NS_DispatchToMainThread(nsEvent);
}
void AudioOffloadPlayer::NotifyAudioTearDown()
{
nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver,
&MediaOmxDecoder::AudioOffloadTearDown);
&MediaOmxCommonDecoder::AudioOffloadTearDown);
NS_DispatchToMainThread(nsEvent);
}

26
content/media/omx/AudioOffloadPlayer.h Executable file → Normal file
View File

@ -27,14 +27,12 @@
#include <utils/RefBase.h>
#include "AudioOutput.h"
#include "AudioOffloadPlayerBase.h"
#include "MediaDecoderOwner.h"
#include "MediaOmxDecoder.h"
#include "MediaOmxCommonDecoder.h"
namespace mozilla {
class MediaOmxDecoder;
/**
* AudioOffloadPlayer adds support for audio tunneling to a digital signal
* processor (DSP) in the device chipset. With tunneling, audio decoding is
@ -47,11 +45,12 @@ class MediaOmxDecoder;
* 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 MediaOmxDecoder
* to AudioSink as well as provide AudioSink status (position changed,
* playback ended, seek complete, audio tear down) back to MediaOmxDecoder
* Also this class passes state changes (play/pause/seek) from
* MediaOmxCommonDecoder to AudioSink as well as provide AudioSink status
* (position changed, playback ended, seek complete, audio tear down) back to
* MediaOmxCommonDecoder
*
* It acts as a bridge between MediaOmxDecoder and AudioSink during
* It acts as a bridge between MediaOmxCommonDecoder and AudioSink during
* offload playback
*/
@ -70,7 +69,7 @@ public:
SEEK_COMPLETE
};
AudioOffloadPlayer(MediaOmxDecoder* aDecoder = nullptr);
AudioOffloadPlayer(MediaOmxCommonDecoder* aDecoder = nullptr);
~AudioOffloadPlayer();
@ -146,7 +145,8 @@ private:
// 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 MediaOmxDecoder
// mStartPosUs + GetPosition() (i.e. absolute position) to
// MediaOmxCommonDecoder
// Used in main thread and offload callback thread, protected by Mutex
// mLock
int64_t mStartPosUs;
@ -161,7 +161,7 @@ private:
// mLock
int64_t mPositionTimeMediaUs;
// State obtained from MediaOmxDecoder. Used only in main thread
// State obtained from MediaOmxCommonDecoder. Used only in main thread
MediaDecoder::PlayState mPlayState;
// Protect accessing audio position related variables between main thread and
@ -180,8 +180,8 @@ private:
// Buffer used to get date from audio source. Used in offload callback thread
MediaBuffer* mInputBuffer;
// MediaOmxDecoder object used mainly to notify the audio sink status
MediaOmxDecoder* mObserver;
// MediaOmxCommonDecoder object used mainly to notify the audio sink status
MediaOmxCommonDecoder* mObserver;
TimeStamp mLastFireUpdateTime;

View File

@ -20,13 +20,11 @@
#ifndef AUDIO_OFFLOAD_PLAYER_BASE_H_
#define AUDIO_OFFLOAD_PLAYER_BASE_H_
#include "MediaDecoder.h"
#include "MediaDecoderOwner.h"
#include "MediaOmxDecoder.h"
namespace mozilla {
class MediaOmxDecoder;
/**
* AudioOffloadPlayer interface class which has funtions used by MediaOmxDecoder
* This is to reduce the dependency of AudioOffloadPlayer in MediaOmxDecoder

View File

@ -5,6 +5,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MediaCodecDecoder.h"
#include <stagefright/MediaSource.h>
#include "MediaCodecReader.h"
#include "MediaDecoderStateMachine.h"
@ -16,10 +19,16 @@ MediaCodecDecoder::Clone()
return new MediaCodecDecoder();
}
MediaDecoderStateMachine*
MediaCodecDecoder::CreateStateMachine()
MediaOmxCommonReader*
MediaCodecDecoder::CreateReader()
{
return new MediaDecoderStateMachine(this, new MediaCodecReader(this));
return new MediaCodecReader(this);
}
MediaDecoderStateMachine*
MediaCodecDecoder::CreateStateMachine(MediaOmxCommonReader* aReader)
{
return new MediaDecoderStateMachine(this, aReader);
}
} // namespace mozilla

View File

@ -7,18 +7,20 @@
#ifndef MEDIA_CODEC_DECODER_H
#define MEDIA_CODEC_DECODER_H
#include "MediaDecoder.h"
#include "MediaOmxCommonDecoder.h"
namespace mozilla {
// MediaDecoder that uses MediaCodecReader.
class MediaCodecDecoder : public MediaDecoder
class MediaCodecDecoder : public MediaOmxCommonDecoder
{
public:
virtual MediaDecoder* Clone();
virtual MediaDecoderStateMachine* CreateStateMachine();
virtual MediaOmxCommonReader* CreateReader();
virtual MediaDecoderStateMachine* CreateStateMachine(MediaOmxCommonReader* aReader);
};
} // namespace mozilla

View File

@ -106,7 +106,8 @@ MediaCodecReader::VideoResourceListener::codecCanceled()
}
}
bool MediaCodecReader::TrackInputCopier::Copy(MediaBuffer* aSourceBuffer, sp<ABuffer> aCodecBuffer)
bool
MediaCodecReader::TrackInputCopier::Copy(MediaBuffer* aSourceBuffer, sp<ABuffer> aCodecBuffer)
{
if (aSourceBuffer == nullptr ||
aCodecBuffer == nullptr ||
@ -132,7 +133,8 @@ MediaCodecReader::Track::Track()
// Append the value of |kKeyValidSamples| to the end of each vorbis buffer.
// https://github.com/mozilla-b2g/platform_frameworks_av/blob/master/media/libstagefright/OMXCodec.cpp#L3128
// https://github.com/mozilla-b2g/platform_frameworks_av/blob/master/media/libstagefright/NuMediaExtractor.cpp#L472
bool MediaCodecReader::VorbisInputCopier::Copy(MediaBuffer* aSourceBuffer, sp<ABuffer> aCodecBuffer)
bool
MediaCodecReader::VorbisInputCopier::Copy(MediaBuffer* aSourceBuffer, sp<ABuffer> aCodecBuffer)
{
if (aSourceBuffer == nullptr ||
aCodecBuffer == nullptr ||
@ -176,7 +178,7 @@ MediaCodecReader::CodecBufferInfo::CodecBufferInfo()
}
MediaCodecReader::MediaCodecReader(AbstractMediaDecoder* aDecoder)
: MediaDecoderReader(aDecoder)
: MediaOmxCommonReader(aDecoder)
, mColorConverterBufferSize(0)
{
mHandler = new MessageHandler(this);
@ -427,6 +429,10 @@ MediaCodecReader::ReadMetadata(MediaInfo* aInfo,
return NS_ERROR_FAILURE;
}
#ifdef MOZ_AUDIO_OFFLOAD
CheckAudioOffload();
#endif
if (IsWaitingMediaResources()) {
return NS_OK;
}
@ -524,6 +530,12 @@ MediaCodecReader::IsMediaSeekable()
return (mExtractor != nullptr) && (mExtractor->flags() & MediaExtractor::CAN_SEEK);
}
android::sp<android::MediaSource>
MediaCodecReader::GetAudioOffloadTrack()
{
return mAudioOffloadTrack.mSource;
}
bool
MediaCodecReader::ReallocateResources()
{
@ -675,6 +687,8 @@ MediaCodecReader::CreateMediaSources()
if (audioSource != nullptr && audioSource->start() == OK) {
mAudioTrack.mSource = audioSource;
}
// Get one another track instance for audio offload playback.
mAudioOffloadTrack.mSource = mExtractor->getTrack(audioTrackIndex);
}
if (videoTrackIndex != invalidTrackIndex && mVideoTrack.mSource == nullptr) {
@ -694,6 +708,7 @@ MediaCodecReader::DestroyMediaSources()
{
mAudioTrack.mSource = nullptr;
mVideoTrack.mSource = nullptr;
mAudioOffloadTrack.mSource = nullptr;
}
bool
@ -1227,8 +1242,6 @@ MediaCodecReader::onMessageReceived(const sp<AMessage> &aMessage)
break;
}
// TODO
default:
TRESPASS();
break;

View File

@ -15,7 +15,7 @@
#include "I420ColorConverterHelper.h"
#include "MediaCodecProxy.h"
#include "MediaDecoderReader.h"
#include "MediaOmxCommonReader.h"
namespace android {
struct ALooper;
@ -29,7 +29,7 @@ struct MediaCodec;
namespace mozilla {
class MediaCodecReader : public MediaDecoderReader
class MediaCodecReader : public MediaOmxCommonReader
{
public:
MediaCodecReader(AbstractMediaDecoder* aDecoder);
@ -85,6 +85,8 @@ public:
virtual bool IsMediaSeekable() MOZ_OVERRIDE;
virtual android::sp<android::MediaSource> GetAudioOffloadTrack();
protected:
struct TrackInputCopier
{
@ -249,9 +251,10 @@ private:
android::sp<android::ALooper> mLooper;
android::sp<android::MediaExtractor> mExtractor;
// media elements
// media tracks
AudioTrack mAudioTrack;
VideoTrack mVideoTrack;
AudioTrack mAudioOffloadTrack; // only Track::mSource is valid
// color converter
android::I420ColorConverterHelper mColorConverter;

View File

@ -0,0 +1,264 @@
/* -*- 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 {
#ifdef PR_LOGGING
extern PRLogModuleInfo* gMediaDecoderLog;
#define DECODER_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
#else
#define DECODER_LOG(type, msg)
#endif
MediaOmxCommonDecoder::MediaOmxCommonDecoder()
: MediaDecoder()
, mReader(nullptr)
, mCanOffloadAudio(false)
, mFallbackToStateMachine(false)
{
#ifdef PR_LOGGING
if (!gMediaDecoderLog) {
gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
}
#endif
}
void
MediaOmxCommonDecoder::SetCanOffloadAudio(bool aCanOffloadAudio)
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mCanOffloadAudio = aCanOffloadAudio;
}
void
MediaOmxCommonDecoder::MetadataLoaded(MediaInfo* aInfo,
MetadataTags* aTags)
{
MOZ_ASSERT(NS_IsMainThread());
MediaDecoder::MetadataLoaded(aInfo, aTags);
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (!mCanOffloadAudio || mFallbackToStateMachine || mOutputStreams.Length() ||
mInitialPlaybackRate != 1.0) {
DECODER_LOG(PR_LOG_DEBUG, ("In %s Offload Audio check failed",
__PRETTY_FUNCTION__));
return;
}
#ifdef MOZ_AUDIO_OFFLOAD
mAudioOffloadPlayer = new AudioOffloadPlayer(this);
#endif
if (!mAudioOffloadPlayer) {
return;
}
mAudioOffloadPlayer->SetSource(mReader->GetAudioOffloadTrack());
status_t err = mAudioOffloadPlayer->Start(false);
if (err == OK) {
PauseStateMachine();
// Call ChangeState() to run AudioOffloadPlayer since offload state enabled
ChangeState(mPlayState);
return;
}
mAudioOffloadPlayer = nullptr;
DECODER_LOG(PR_LOG_DEBUG, ("In %s Unable to start offload audio %d."
"Switching to normal mode", __PRETTY_FUNCTION__, err));
}
void
MediaOmxCommonDecoder::PauseStateMachine()
{
MOZ_ASSERT(NS_IsMainThread());
GetReentrantMonitor().AssertCurrentThreadIn();
DECODER_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__));
if (!mDecoderStateMachine) {
return;
}
StopProgress();
mDecoderStateMachine->SetDormant(true);
}
void
MediaOmxCommonDecoder::ResumeStateMachine()
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
DECODER_LOG(PR_LOG_DEBUG, ("%s current time %f", __PRETTY_FUNCTION__,
mCurrentTime));
if (!mDecoderStateMachine) {
return;
}
mFallbackToStateMachine = true;
mAudioOffloadPlayer = nullptr;
int64_t timeUsecs = 0;
SecondsToUsecs(mCurrentTime, timeUsecs);
mRequestedSeekTarget = SeekTarget(timeUsecs, SeekTarget::Accurate);
mNextState = mPlayState;
ChangeState(PLAY_STATE_LOADING);
mDecoderStateMachine->SetDormant(false);
}
void
MediaOmxCommonDecoder::AudioOffloadTearDown()
{
MOZ_ASSERT(NS_IsMainThread());
DECODER_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__));
// mAudioOffloadPlayer can be null here if ResumeStateMachine was called
// just before because of some other error.
if (mAudioOffloadPlayer) {
// Audio offload player sent tear down event. Fallback to state machine
PlaybackPositionChanged();
ResumeStateMachine();
}
}
void
MediaOmxCommonDecoder::AddOutputStream(ProcessedMediaStream* aStream,
bool aFinishWhenEnded)
{
MOZ_ASSERT(NS_IsMainThread());
if (mAudioOffloadPlayer) {
// Offload player cannot handle MediaStream. Fallback
PlaybackPositionChanged();
ResumeStateMachine();
}
MediaDecoder::AddOutputStream(aStream, aFinishWhenEnded);
}
void
MediaOmxCommonDecoder::SetPlaybackRate(double aPlaybackRate)
{
MOZ_ASSERT(NS_IsMainThread());
if (mAudioOffloadPlayer &&
((aPlaybackRate != 0.0) || (aPlaybackRate != 1.0))) {
// Offload player cannot handle playback rate other than 1/0. Fallback
PlaybackPositionChanged();
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) {
status_t err = mAudioOffloadPlayer->ChangeState(aState);
if (err != OK) {
ResumeStateMachine();
}
}
}
void
MediaOmxCommonDecoder::ApplyStateToStateMachine(PlayState aState)
{
MOZ_ASSERT(NS_IsMainThread());
// During offload playback, state machine should be in dormant state.
// ApplyStateToStateMachine() can change state machine state to
// something else or reset the seek time. So don't call this when audio is
// offloaded
if (!mAudioOffloadPlayer) {
MediaDecoder::ApplyStateToStateMachine(aState);
}
}
void
MediaOmxCommonDecoder::PlaybackPositionChanged()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mAudioOffloadPlayer) {
MediaDecoder::PlaybackPositionChanged();
return;
}
if (!mOwner || mShuttingDown) {
return;
}
double lastTime = mCurrentTime;
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mCurrentTime = mAudioOffloadPlayer->GetMediaTimeSecs();
}
if (mOwner && lastTime != mCurrentTime) {
FireTimeUpdate();
}
}
void
MediaOmxCommonDecoder::SetElementVisibility(bool aIsVisible)
{
MOZ_ASSERT(NS_IsMainThread());
if (mAudioOffloadPlayer) {
mAudioOffloadPlayer->SetElementVisibility(aIsVisible);
}
}
void
MediaOmxCommonDecoder::UpdateReadyStateForData()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mAudioOffloadPlayer) {
MediaDecoder::UpdateReadyStateForData();
return;
}
if (!mOwner || mShuttingDown)
return;
mOwner->UpdateReadyStateForData(mAudioOffloadPlayer->GetNextFrameStatus());
}
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 CreateStateMachine(mReader);
}
} // namespace mozilla

View File

@ -0,0 +1,67 @@
/* -*- 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"
namespace android {
struct MOZ_EXPORT MediaSource;
} // namespace android
namespace mozilla {
class AudioOffloadPlayerBase;
class MediaOmxCommonReader;
class MediaOmxCommonDecoder : public MediaDecoder
{
public:
MediaOmxCommonDecoder();
virtual void MetadataLoaded(MediaInfo* aInfo,
MetadataTags* aTags);
virtual void ChangeState(PlayState aState);
virtual void ApplyStateToStateMachine(PlayState aState);
virtual void SetVolume(double aVolume);
virtual void PlaybackPositionChanged();
virtual void UpdateReadyStateForData();
virtual void SetElementVisibility(bool aIsVisible);
virtual void SetCanOffloadAudio(bool aCanOffloadAudio);
virtual void AddOutputStream(ProcessedMediaStream* aStream,
bool aFinishWhenEnded);
virtual void SetPlaybackRate(double aPlaybackRate);
void AudioOffloadTearDown();
virtual MediaDecoderStateMachine* CreateStateMachine();
virtual MediaOmxCommonReader* CreateReader() = 0;
virtual MediaDecoderStateMachine* CreateStateMachine(MediaOmxCommonReader* aReader) = 0;
protected:
void PauseStateMachine();
void ResumeStateMachine();
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;
};
} // namespace mozilla
#endif // MEDIA_OMX_COMMON_DECODER_H

View File

@ -0,0 +1,81 @@
/* -*- 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"
#ifdef MOZ_AUDIO_OFFLOAD
#include <stagefright/Utils.h>
#include <cutils/properties.h>
#include <stagefright/MetaData.h>
#endif
using namespace android;
namespace mozilla {
#ifdef PR_LOGGING
extern PRLogModuleInfo* gMediaDecoderLog;
#define DECODER_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
#else
#define DECODER_LOG(type, msg)
#endif
MediaOmxCommonReader::MediaOmxCommonReader(AbstractMediaDecoder *aDecoder)
: MediaDecoderReader(aDecoder)
{
#ifdef PR_LOGGING
if (!gMediaDecoderLog) {
gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
}
#endif
mAudioChannel = dom::AudioChannelService::GetDefaultAudioChannel();
}
#ifdef MOZ_AUDIO_OFFLOAD
void MediaOmxCommonReader::CheckAudioOffload()
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
char offloadProp[128];
property_get("audio.offload.disable", offloadProp, "0");
bool offloadDisable = atoi(offloadProp) != 0;
if (offloadDisable) {
return;
}
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(PR_LOG_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)) {
DECODER_LOG(PR_LOG_DEBUG, ("Can offload this audio stream"));
mDecoder->SetCanOffloadAudio(true);
}
}
#endif
} // namespace mozilla

View File

@ -0,0 +1,48 @@
/* -*- 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;
} // namespace android
namespace mozilla {
class AbstractMediaDecoder;
class MediaOmxCommonReader : public MediaDecoderReader
{
public:
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;
};
} // namespace mozilla
#endif // MEDIA_OMX_COMMON_READER_H

230
content/media/omx/MediaOmxDecoder.cpp Executable file → Normal file
View File

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

47
content/media/omx/MediaOmxDecoder.h Executable file → Normal file
View File

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

View File

@ -19,12 +19,6 @@
#include "gfx2DGlue.h"
#include "MediaStreamSource.h"
#ifdef MOZ_AUDIO_OFFLOAD
#include <stagefright/Utils.h>
#include <cutils/properties.h>
#include <stagefright/MetaData.h>
#endif
#define MAX_DROPPED_FRAMES 25
// Try not to spend more than this much time in a single call to DecodeVideoFrame.
#define MAX_VIDEO_DECODE_SECONDS 0.1
@ -42,7 +36,7 @@ extern PRLogModuleInfo* gMediaDecoderLog;
#endif
MediaOmxReader::MediaOmxReader(AbstractMediaDecoder *aDecoder)
: MediaDecoderReader(aDecoder)
: MediaOmxCommonReader(aDecoder)
, mHasVideo(false)
, mHasAudio(false)
, mVideoSeekTimeUs(-1)
@ -425,41 +419,12 @@ void MediaOmxReader::EnsureActive() {
NS_ASSERTION(result == NS_OK, "OmxDecoder should be in play state to continue decoding");
}
#ifdef MOZ_AUDIO_OFFLOAD
void MediaOmxReader::CheckAudioOffload()
android::sp<android::MediaSource> MediaOmxReader::GetAudioOffloadTrack()
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
char offloadProp[128];
property_get("audio.offload.disable", offloadProp, "0");
bool offloadDisable = atoi(offloadProp) != 0;
if (offloadDisable) {
return;
}
mAudioOffloadTrack = mOmxDecoder->GetAudioOffloadTrack();
sp<MetaData> meta = (mAudioOffloadTrack.get()) ?
mAudioOffloadTrack->getFormat() : nullptr;
// Supporting audio offload only when there is no video, no streaming
bool hasNoVideo = !mOmxDecoder->HasVideo();
bool isNotStreaming
= mDecoder->GetResource()->IsDataCachedToEndOfResource(0);
// Not much benefit in trying to offload other channel types. Most of them
// aren't supported and also duration would be less than a minute
bool isTypeMusic = mAudioChannel == dom::AudioChannel::Content;
DECODER_LOG(PR_LOG_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)) {
DECODER_LOG(PR_LOG_DEBUG, ("Can offload this audio stream"));
mDecoder->SetCanOffloadAudio(true);
if (!mOmxDecoder.get()) {
return nullptr;
}
return mOmxDecoder->GetAudioOffloadTrack();
}
#endif
} // namespace mozilla

View File

@ -6,10 +6,10 @@
#if !defined(MediaOmxReader_h_)
#define MediaOmxReader_h_
#include "MediaOmxCommonReader.h"
#include "MediaResource.h"
#include "MediaDecoderReader.h"
#include "nsRect.h"
#include "mozilla/dom/AudioChannelBinding.h"
#include <ui/GraphicBuffer.h>
#include <stagefright/MediaSource.h>
@ -26,7 +26,7 @@ namespace dom {
class AbstractMediaDecoder;
class MediaOmxReader : public MediaDecoderReader
class MediaOmxReader : public MediaOmxCommonReader
{
nsCString mType;
bool mHasVideo;
@ -36,8 +36,6 @@ class MediaOmxReader : public MediaDecoderReader
int64_t mVideoSeekTimeUs;
int64_t mAudioSeekTimeUs;
int32_t mSkipCount;
dom::AudioChannel mAudioChannel;
android::sp<android::MediaSource> mAudioOffloadTrack;
protected:
android::sp<android::OmxDecoder> mOmxDecoder;
@ -90,22 +88,9 @@ public:
virtual void Shutdown() MOZ_OVERRIDE;
void SetAudioChannel(dom::AudioChannel aAudioChannel) {
mAudioChannel = aAudioChannel;
}
android::sp<android::MediaSource> GetAudioOffloadTrack() {
return mAudioOffloadTrack;
}
#ifdef MOZ_AUDIO_OFFLOAD
// Check whether it is possible to offload current audio track. This access
// canOffloadStream() from libStageFright Utils.cpp, which is not there in
// ANDROID_VERSION < 19
void CheckAudioOffload();
#endif
void ReleaseDecoder();
android::sp<android::MediaSource> GetAudioOffloadTrack();
};
} // namespace mozilla

View File

@ -6,11 +6,15 @@
EXPORTS += [
'AudioOffloadPlayerBase.h',
'MediaOmxCommonDecoder.h',
'MediaOmxCommonReader.h',
'MediaOmxDecoder.h',
'MediaOmxReader.h',
]
SOURCES += [
'MediaOmxCommonDecoder.cpp',
'MediaOmxCommonReader.cpp',
'MediaOmxDecoder.cpp',
'MediaOmxReader.cpp',
'MediaStreamSource.cpp',

View File

@ -20,6 +20,7 @@
#include "nsIConsoleService.h"
#include "nsIScriptError.h"
#include "nsDocShellLoadTypes.h"
#include "nsIMultiPartChannel.h"
using namespace mozilla;
@ -28,8 +29,9 @@ using namespace mozilla;
//*****************************************************************************
nsDSURIContentListener::nsDSURIContentListener(nsDocShell* aDocShell)
: mDocShell(aDocShell),
mParentContentListener(nullptr)
: mDocShell(aDocShell)
, mExistingJPEGRequest(nullptr)
, mParentContentListener(nullptr)
{
}
@ -118,8 +120,33 @@ nsDSURIContentListener::DoContent(const char* aContentType,
mDocShell->SetLoadType(aIsContentPreferred ? LOAD_LINK : LOAD_NORMAL);
}
// In case of multipart jpeg request (mjpeg) we don't really want to
// create new viewer since the one we already have is capable of
// rendering multipart jpeg correctly (see bug 625012)
nsCOMPtr<nsIChannel> baseChannel;
if (nsCOMPtr<nsIMultiPartChannel> mpchan = do_QueryInterface(request)) {
mpchan->GetBaseChannel(getter_AddRefs(baseChannel));
}
bool reuseCV = baseChannel
&& baseChannel == mExistingJPEGRequest
&& nsDependentCString(aContentType).EqualsLiteral("image/jpeg");
if (mExistingJPEGStreamListener && reuseCV) {
nsRefPtr<nsIStreamListener> copy(mExistingJPEGStreamListener);
copy.forget(aContentHandler);
rv = NS_OK;
} else {
rv = mDocShell->CreateContentViewer(aContentType, request, aContentHandler);
if (NS_SUCCEEDED(rv) && reuseCV) {
mExistingJPEGStreamListener = *aContentHandler;
} else {
mExistingJPEGStreamListener = nullptr;
}
mExistingJPEGRequest = baseChannel;
}
rv = mDocShell->CreateContentViewer(aContentType, request, aContentHandler);
if (rv == NS_ERROR_REMOTE_XUL) {
request->Cancel(rv);

View File

@ -34,6 +34,8 @@ protected:
void DropDocShellreference() {
mDocShell = nullptr;
mExistingJPEGRequest = nullptr;
mExistingJPEGStreamListener = nullptr;
}
// Determine if X-Frame-Options allows content to be framed
@ -53,6 +55,9 @@ protected:
XFOHeader aHeader);
protected:
nsDocShell* mDocShell;
// Hack to handle multipart images without creating a new viewer
nsCOMPtr<nsIStreamListener> mExistingJPEGStreamListener;
nsCOMPtr<nsIChannel> mExistingJPEGRequest;
// Store the parent listener in either of these depending on
// if supports weak references or not. Proper weak refs are

View File

@ -58,7 +58,7 @@ GonkCameraHardware::OnNewFrame()
}
RefPtr<TextureClient> buffer = mNativeWindow->getCurrentBuffer();
if (!buffer) {
DOM_CAMERA_LOGW("received null frame");
DOM_CAMERA_LOGE("received null frame");
return;
}
OnNewPreviewFrame(mTarget, buffer);
@ -185,9 +185,13 @@ GonkCameraHardware::Init()
#if ANDROID_VERSION >= 19
mNativeWindow = new GonkNativeWindow(GonkCameraHardware::MIN_UNDEQUEUED_BUFFERS);
sp<GonkBufferQueue> bq = mNativeWindow->getBufferQueue();
bq->setSynchronousMode(false);
mCamera->setPreviewTarget(mNativeWindow->getBufferQueue());
#elif ANDROID_VERSION >= 17
mNativeWindow = new GonkNativeWindow(GonkCameraHardware::MIN_UNDEQUEUED_BUFFERS);
sp<GonkBufferQueue> bq = mNativeWindow->getBufferQueue();
bq->setSynchronousMode(false);
mCamera->setPreviewTexture(mNativeWindow->getBufferQueue());
#else
mNativeWindow = new GonkNativeWindow();

View File

@ -27,9 +27,6 @@ FAIL_ON_WARNINGS = True
FINAL_LIBRARY = 'xul'
RESOURCE_FILES += [
'res/caret_left.svg',
'res/caret_middle.svg',
'res/caret_right.svg',
'res/EditorOverride.css',
'res/grabber.gif',
'res/table-add-column-after-active.gif',
@ -50,4 +47,19 @@ RESOURCE_FILES += [
'res/table-remove-row-active.gif',
'res/table-remove-row-hover.gif',
'res/table-remove-row.gif',
'res/text_caret.png',
'res/text_caret@1.5x.png',
'res/text_caret@2.25x.png',
'res/text_caret@2x.png',
'res/text_caret_tilt_left.png',
'res/text_caret_tilt_left@1.5x.png',
'res/text_caret_tilt_left@2.25x.png',
'res/text_caret_tilt_left@2x.png',
'res/text_caret_tilt_right.png',
'res/text_caret_tilt_right@1.5x.png',
'res/text_caret_tilt_right@2.25x.png',
'res/text_caret_tilt_right@2x.png',
'res/text_selection_handle.png',
'res/text_selection_handle@1.5.png',
'res/text_selection_handle@2.png',
]

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="29px" height="31px" viewBox="0 0 29 31" style="enable-background:new 0 0 29 31;" xml:space="preserve">
<!-- TODO: Enable shadow after bug 1015575 is resolved.
<defs>
<filter id="caretFilter">
<feOffset result="offsetOut" in="SourceAlpha" dx="1" dy="1" />
<feGaussianBlur result="blurOut" in="offsetOut" stdDeviation="0.5" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
</defs>
<g fill="#2da9e3" filter="url(#caretFilter)">
-->
<g fill="#2da9e3">
<path d="M25.368,2.674c-0.049,0.104-0.09,0.209-0.134,0.314C25.304,2.893,25.347,2.786,25.368,2.674z"/>
<path d="M24.27,1.734c0.003-0.001,0.008-0.003,0.013-0.004C24.277,1.73,24.272,1.733,24.27,1.734z"/>
<path d="M24.583,8.574C24.25,6.7,24.478,4.755,25.234,2.989c0.044-0.105,0.085-0.21,0.134-0.314
c0.053-0.254-0.016-0.528-0.204-0.73c-0.232-0.249-0.581-0.322-0.882-0.215c-0.005,0.001-0.01,0.003-0.013,0.004
c-1.915,0.71-4.001,0.798-5.954,0.277C15.015,0.898,11.222,1.587,8.5,4.134c-3.947,3.691-4.155,9.882-0.464,13.828
c3.691,3.947,9.881,4.154,13.828,0.462C24.64,15.828,25.562,11.994,24.583,8.574z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="29px" height="31px" style="enable-background:new 0 0 29 31;" xml:space="preserve">
<!-- TODO: Enable shadow after bug 1015575 is resolved.
<defs>
<filter id="caretFilter">
<feOffset result="offsetOut" in="SourceAlpha" dx="1" dy="1" />
<feGaussianBlur result="blurOut" in="offsetOut" stdDeviation="0.5" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
</defs>
<g fill="#2da9e3" filter="url(#caretFilter)">
-->
<g fill="#2da9e3">
<path d="M15.174,1.374c0.042,0.106,0.091,0.208,0.138,0.312C15.288,1.57,15.239,1.466,15.174,1.374z"/>
<path d="M13.735,1.534c0.002-0.003,0.004-0.009,0.006-0.013C13.739,1.525,13.737,1.531,13.735,1.534z"/>
<path d="M18.945,5.978c-1.596-1.038-2.861-2.532-3.634-4.292c-0.047-0.104-0.096-0.206-0.138-0.312
c-0.15-0.212-0.396-0.349-0.674-0.349c-0.34,0-0.631,0.204-0.759,0.497c-0.002,0.004-0.004,0.009-0.006,0.013
c-0.789,1.883-2.149,3.467-3.864,4.538c-3.068,1.651-5.155,4.892-5.155,8.62c0,5.404,4.379,9.784,9.783,9.784
c5.403,0,9.783-4.38,9.783-9.784C24.283,10.891,22.113,7.598,18.945,5.978z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="29px" height="31px" viewBox="0 0 29 31" style="enable-background:new 0 0 29 31;" xml:space="preserve">
<!-- TODO: Enable shadow after bug 1015575 is resolved.
<defs>
<filter id="caretFilter">
<feOffset result="offsetOut" in="SourceAlpha" dx="1" dy="1" />
<feGaussianBlur result="blurOut" in="offsetOut" stdDeviation="0.5" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
</defs>
<g fill="#2da9e3" filter="url(#caretFilter)">
-->
<g fill="#2da9e3">
<path fill="#2da9e3" d="M27.296,2.674c-0.049,0.104-0.09,0.209-0.134,0.314C27.231,2.893,27.274,2.786,27.296,2.674z"/>
<path fill="#2da9e3" d="M26.197,1.734C26.2,1.733,26.205,1.73,26.21,1.729C26.205,1.73,26.2,1.733,26.197,1.734z"/>
<path fill="#2da9e3" d="M4.299,8.574C4.632,6.7,4.404,4.755,3.647,2.989c-0.044-0.105-0.085-0.21-0.134-0.314C3.461,2.42,3.529,2.146,3.718,1.944
C3.95,1.696,4.299,1.623,4.6,1.729c0.005,0.001,0.01,0.003,0.013,0.004c1.915,0.71,4.001,0.798,5.954,0.277
c3.301-1.113,7.094-0.423,9.815,2.123c3.947,3.691,4.156,9.882,0.465,13.828c-3.691,3.947-9.881,4.154-13.828,0.462
C4.242,15.828,3.319,11.994,4.299,8.574z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -3,7 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include libeditor/html/crashtests/crashtests.list
include libeditor/base/crashtests/crashtests.list
include libeditor/crashtests/crashtests.list
include libeditor/text/crashtests/crashtests.list
include composer/crashtests/crashtests.list
include txmgr/tests/crashtests/crashtests.list

View File

@ -1,44 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
TEST_DIRS += ['tests']
UNIFIED_SOURCES += [
'ChangeAttributeTxn.cpp',
'ChangeCSSInlineStyleTxn.cpp',
'CreateElementTxn.cpp',
'DeleteNodeTxn.cpp',
'DeleteRangeTxn.cpp',
'DeleteTextTxn.cpp',
'EditAggregateTxn.cpp',
'EditTxn.cpp',
'IMETextTxn.cpp',
'InsertElementTxn.cpp',
'InsertTextTxn.cpp',
'JoinElementTxn.cpp',
'nsEditor.cpp',
'nsEditorCommands.cpp',
'nsEditorController.cpp',
'nsEditorEventListener.cpp',
'nsEditorUtils.cpp',
'nsSelectionState.cpp',
'nsStyleSheetTxns.cpp',
'PlaceholderTxn.cpp',
'SetDocTitleTxn.cpp',
'SplitElementTxn.cpp',
]
FAIL_ON_WARNINGS = True
LOCAL_INCLUDES += [
'../text',
'/content/base/src',
'/editor/txmgr',
'/extensions/spellcheck/src',
'/layout/style',
]
FINAL_LIBRARY = 'xul'

Some files were not shown because too many files have changed in this diff Show More