Bug 448603. Support direct loading of Ogg audio and video files by creating a synthetic <video> document. r=doublec,sr=bzbarsky

This commit is contained in:
Robert O'Callahan 2008-10-29 22:20:08 -07:00
parent d0fa1eee37
commit c98e2689e7
26 changed files with 548 additions and 140 deletions

View File

@ -281,6 +281,15 @@
#endif // MOZ_SVG
#ifdef MOZ_MEDIA
// {d899a152-9412-46b2-b651-2e71c5c2f05f}
#define NS_VIDEODOCUMENT_CID \
{ 0xd899a152, 0x9412, 0x46b2, \
{ 0xb6, 0x51, 0x2e, 0x71, 0xc5, 0xc2, 0xf0, 0x5f } }
#endif
#define NS_SYNCLOADDOMSERVICE_CID \
{ /* 0e4e7d00-f71a-439f-9178-1a71ff11b55f */ \
0x0e4e7d00, 0xf71a, 0x439f, \

View File

@ -1262,6 +1262,11 @@ NS_NewSVGDocument(nsIDocument** aInstancePtrResult);
nsresult
NS_NewImageDocument(nsIDocument** aInstancePtrResult);
#ifdef MOZ_MEDIA
nsresult
NS_NewVideoDocument(nsIDocument** aInstancePtrResult);
#endif
nsresult
NS_NewDocumentFragment(nsIDOMDocumentFragment** aInstancePtrResult,
nsNodeInfoManager *aNodeInfoManager);

View File

@ -72,6 +72,4 @@ public:
PRBool aCompileEventHandlers);
virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
PRBool aNullParent = PR_TRUE);
protected:
virtual nsresult InitializeDecoder(nsAString& aChosenMediaResource);
};

View File

@ -38,6 +38,7 @@
#include "nsIDOMHTMLMediaElement.h"
#include "nsGenericHTMLElement.h"
#include "nsMediaDecoder.h"
#include "nsIChannel.h"
// Define to output information on decoding and painting framerate
/* #define DEBUG_FRAME_RATE 1 */
@ -51,6 +52,16 @@ public:
nsHTMLMediaElement(nsINodeInfo *aNodeInfo, PRBool aFromParser = PR_FALSE);
virtual ~nsHTMLMediaElement();
/**
* This is used when the browser is constructing a video element to play
* a channel that we've already started loading. The src attribute and
* <source> children are ignored.
* @param aChannel the channel to use
* @param aListener returns a stream listener that should receive
* notifications for the stream
*/
nsresult LoadWithChannel(nsIChannel *aChannel, nsIStreamListener **aListener);
// nsIDOMHTMLMediaElement
NS_DECL_NSIDOMHTMLMEDIAELEMENT
@ -147,9 +158,40 @@ public:
void Freeze();
void Thaw();
// Returns true if we can handle this MIME type in a <video> or <audio>
// element
static PRBool CanHandleMediaType(const char* aMIMEType);
/**
* Initialize data for available media types
*/
static void InitMediaTypes();
/**
* Shutdown data for available media types
*/
static void ShutdownMediaTypes();
protected:
nsresult PickMediaElement(nsAString& aChosenMediaResource);
virtual nsresult InitializeDecoder(nsAString& aChosenMediaResource);
/**
* Figure out which resource to load (either the 'src' attribute or
* a <source> child) and create the decoder for it.
*/
nsresult PickMediaElement();
/**
* Create a decoder for the given aMIMEType. Returns false if we
* were unable to create the decoder.
*/
PRBool CreateDecoder(const nsACString& aMIMEType);
/**
* Initialize a decoder to load the given URI.
*/
nsresult InitializeDecoder(const nsAString& aURISpec);
/**
* Initialize a decoder to load the given channel. The decoder's stream
* listener is returned via aListener.
*/
nsresult InitializeDecoderForChannel(nsIChannel *aChannel,
nsIStreamListener **aListener);
nsRefPtr<nsMediaDecoder> mDecoder;

View File

@ -73,8 +73,4 @@ public:
// Returns the current video frame width and height.
// If there is no video frame, returns the given default size.
nsIntSize GetVideoSize(nsIntSize defaultSize);
protected:
virtual nsresult InitializeDecoder(nsAString& aChosenMediaResource);
};

View File

@ -110,11 +110,3 @@ void nsHTMLAudioElement::UnbindFromTree(PRBool aDeep,
nsHTMLMediaElement::UnbindFromTree(aDeep, aNullParent);
}
nsresult nsHTMLAudioElement::InitializeDecoder(nsAString& aChosenMediaResource)
{
if (mDecoder)
mDecoder->ElementAvailable(this);
return nsHTMLMediaElement::InitializeDecoder(aChosenMediaResource);
}

View File

@ -67,6 +67,7 @@
#include "nsIDOMDocumentEvent.h"
#include "nsIDOMProgressEvent.h"
#include "nsHTMLMediaError.h"
#include "nsICategoryManager.h"
#ifdef MOZ_OGG
#include "nsOggDecoder.h"
@ -220,6 +221,19 @@ NS_IMETHODIMP nsHTMLMediaElement::GetTotalBytes(PRUint32 *aTotalBytes)
/* void load (); */
NS_IMETHODIMP nsHTMLMediaElement::Load()
{
return LoadWithChannel(nsnull, nsnull);
}
nsresult nsHTMLMediaElement::LoadWithChannel(nsIChannel *aChannel,
nsIStreamListener **aListener)
{
NS_ASSERTION((aChannel == nsnull) == (aListener == nsnull),
"channel and listener should both be null or both non-null");
if (aListener) {
*aListener = nsnull;
}
if (mBegun) {
mBegun = PR_FALSE;
@ -245,14 +259,12 @@ NS_IMETHODIMP nsHTMLMediaElement::Load()
DispatchSimpleEvent(NS_LITERAL_STRING("emptied"));
}
nsAutoString chosenMediaResource;
nsresult rv = PickMediaElement(chosenMediaResource);
NS_ENSURE_SUCCESS(rv, rv);
mNetworkState = nsIDOMHTMLMediaElement::LOADING;
// This causes the currentSrc attribute to become valid
rv = InitializeDecoder(chosenMediaResource);
nsresult rv;
if (aChannel) {
rv = InitializeDecoderForChannel(aChannel, aListener);
} else {
rv = PickMediaElement();
}
NS_ENSURE_SUCCESS(rv, rv);
mBegun = PR_TRUE;
@ -564,7 +576,8 @@ nsresult nsHTMLMediaElement::BindToTree(nsIDocument* aDocument, nsIContent* aPar
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
if (mIsDoneAddingChildren && mNetworkState == nsIDOMHTMLMediaElement::EMPTY) {
if (mIsDoneAddingChildren &&
mNetworkState == nsIDOMHTMLMediaElement::EMPTY) {
Load();
}
@ -580,16 +593,98 @@ void nsHTMLMediaElement::UnbindFromTree(PRBool aDeep,
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
}
#ifdef MOZ_OGG
static const char gOggTypes[][16] = {
"video/ogg",
"audio/ogg",
"application/ogg"
};
nsresult nsHTMLMediaElement::PickMediaElement(nsAString& aChosenMediaResource)
static PRBool IsOggType(const nsACString& aType)
{
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(gOggTypes); ++i) {
if (aType.EqualsASCII(gOggTypes[i]))
return PR_TRUE;
}
return PR_FALSE;
}
#endif
/* static */
PRBool nsHTMLMediaElement::CanHandleMediaType(const char* aMIMEType)
{
#ifdef MOZ_OGG
if (IsOggType(nsDependentCString(aMIMEType)))
return PR_TRUE;
#endif
return PR_FALSE;
}
/* static */
void nsHTMLMediaElement::InitMediaTypes()
{
nsresult rv;
nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
if (NS_SUCCEEDED(rv)) {
#ifdef MOZ_OGG
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(gOggTypes); i++) {
catMan->AddCategoryEntry("Gecko-Content-Viewers", gOggTypes[i],
"@mozilla.org/content/document-loader-factory;1",
PR_FALSE, PR_TRUE, nsnull);
}
#endif
}
}
/* static */
void nsHTMLMediaElement::ShutdownMediaTypes()
{
nsresult rv;
nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
if (NS_SUCCEEDED(rv)) {
#ifdef MOZ_OGG
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(gOggTypes); i++) {
catMan->DeleteCategoryEntry("Gecko-Content-Viewers", gOggTypes[i], PR_FALSE);
}
#endif
}
}
PRBool nsHTMLMediaElement::CreateDecoder(const nsACString& aType)
{
#ifdef MOZ_OGG
if (IsOggType(aType)) {
mDecoder = new nsOggDecoder();
if (mDecoder && !mDecoder->Init()) {
mDecoder = nsnull;
}
}
#endif
return mDecoder != nsnull;
}
nsresult nsHTMLMediaElement::InitializeDecoderForChannel(nsIChannel *aChannel,
nsIStreamListener **aListener)
{
nsCAutoString mimeType;
aChannel->GetContentType(mimeType);
if (!CreateDecoder(mimeType))
return NS_ERROR_FAILURE;
mNetworkState = nsIDOMHTMLMediaElement::LOADING;
mDecoder->ElementAvailable(this);
return mDecoder->Load(nsnull, aChannel, aListener);
}
nsresult nsHTMLMediaElement::PickMediaElement()
{
// Implements:
// http://www.whatwg.org/specs/web-apps/current-work/#pick-a
nsAutoString src;
if (HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
aChosenMediaResource = src;
#ifdef MOZ_OGG
// Currently assuming an Ogg file
// TODO: Instantiate decoder based on type
@ -604,7 +699,7 @@ nsresult nsHTMLMediaElement::PickMediaElement(nsAString& aChosenMediaResource)
mDecoder = nsnull;
}
#endif
return NS_OK;
return InitializeDecoder(src);
}
}
@ -619,31 +714,22 @@ nsresult nsHTMLMediaElement::PickMediaElement(nsAString& aChosenMediaResource)
if (source) {
if (source->HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
nsAutoString type;
if (source->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type)) {
#if MOZ_OGG
if (type.EqualsLiteral("video/ogg") || type.EqualsLiteral("application/ogg")) {
nsAutoString src;
if (source->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
mDecoder = new nsOggDecoder();
if (mDecoder && !mDecoder->Init()) {
mDecoder = nsnull;
}
aChosenMediaResource = src;
return NS_OK;
}
}
#endif
}
nsAutoString src;
if (source->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) &&
source->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src) &&
CreateDecoder(NS_ConvertUTF16toUTF8(type)))
return InitializeDecoder(src);
}
}
}
}
}
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
nsresult nsHTMLMediaElement::InitializeDecoder(nsAString& aChosenMediaResource)
nsresult nsHTMLMediaElement::InitializeDecoder(const nsAString& aURISpec)
{
mNetworkState = nsIDOMHTMLMediaElement::LOADING;
nsCOMPtr<nsIDocument> doc = GetOwnerDoc();
if (!doc) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
@ -653,15 +739,15 @@ nsresult nsHTMLMediaElement::InitializeDecoder(nsAString& aChosenMediaResource)
nsCOMPtr<nsIURI> uri;
nsCOMPtr<nsIURI> baseURL = GetBaseURI();
const nsAFlatCString &charset = doc->GetDocumentCharacterSet();
rv = NS_NewURI(getter_AddRefs(uri),
aChosenMediaResource,
rv = NS_NewURI(getter_AddRefs(uri), aURISpec,
charset.IsEmpty() ? nsnull : charset.get(),
baseURL,
nsContentUtils::GetIOService());
NS_ENSURE_SUCCESS(rv, rv);
if (mDecoder) {
rv = mDecoder->Load(uri);
mDecoder->ElementAvailable(this);
rv = mDecoder->Load(uri, nsnull, nsnull);
if (NS_FAILED(rv)) {
mDecoder = nsnull;
}

View File

@ -134,12 +134,3 @@ void nsHTMLVideoElement::UnbindFromTree(PRBool aDeep,
if (mDecoder)
mDecoder->ElementUnavailable();
}
nsresult nsHTMLVideoElement::InitializeDecoder(nsAString& aChosenMediaResource)
{
if (mDecoder)
mDecoder->ElementAvailable(this);
return nsHTMLMediaElement::InitializeDecoder(aChosenMediaResource);
}

View File

@ -81,13 +81,17 @@ CPPSRCS = \
nsHTMLContentSink.cpp \
nsHTMLFragmentContentSink.cpp \
nsHTMLDocument.cpp \
nsImageDocument.cpp \
nsMediaDocument.cpp \
nsPluginDocument.cpp \
nsImageDocument.cpp \
nsWyciwygChannel.cpp \
nsWyciwygProtocolHandler.cpp \
$(NULL)
ifdef MOZ_MEDIA
CPPSRCS += nsVideoDocument.cpp
endif
EXPORTS = \
nsIHTMLDocument.h \
$(NULL)

View File

@ -120,7 +120,7 @@ public:
friend class ImageListener;
protected:
nsresult CreateSyntheticDocument();
virtual nsresult CreateSyntheticDocument();
nsresult CheckOverflowing(PRBool changeState);

View File

@ -105,7 +105,6 @@ public:
NS_DECL_NSISTREAMLISTENER
protected:
nsRefPtr<nsMediaDocument> mDocument;
nsCOMPtr<nsIStreamListener> mNextStream;
};

View File

@ -0,0 +1,140 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsMediaDocument.h"
#include "nsGkAtoms.h"
#include "nsNodeInfoManager.h"
#include "nsContentCreatorFunctions.h"
#include "nsHTMLMediaElement.h"
class nsVideoDocument : public nsMediaDocument
{
public:
virtual nsresult StartDocumentLoad(const char* aCommand,
nsIChannel* aChannel,
nsILoadGroup* aLoadGroup,
nsISupports* aContainer,
nsIStreamListener** aDocListener,
PRBool aReset = PR_TRUE,
nsIContentSink* aSink = nsnull);
protected:
nsresult CreateSyntheticVideoDocument(nsIChannel* aChannel,
nsIStreamListener** aListener);
nsRefPtr<nsMediaDocumentStreamListener> mStreamListener;
};
nsresult
nsVideoDocument::StartDocumentLoad(const char* aCommand,
nsIChannel* aChannel,
nsILoadGroup* aLoadGroup,
nsISupports* aContainer,
nsIStreamListener** aDocListener,
PRBool aReset,
nsIContentSink* aSink)
{
nsresult rv =
nsMediaDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup,
aContainer, aDocListener, aReset,
aSink);
NS_ENSURE_SUCCESS(rv, rv);
mStreamListener = new nsMediaDocumentStreamListener(this);
if (!mStreamListener)
return NS_ERROR_OUT_OF_MEMORY;
// Create synthetic document
rv = CreateSyntheticVideoDocument(aChannel,
getter_AddRefs(mStreamListener->mNextStream));
NS_ENSURE_SUCCESS(rv, rv);
NS_ADDREF(*aDocListener = mStreamListener);
return rv;
}
nsresult
nsVideoDocument::CreateSyntheticVideoDocument(nsIChannel* aChannel,
nsIStreamListener** aListener)
{
// make our generic document
nsresult rv = nsMediaDocument::CreateSyntheticDocument();
NS_ENSURE_SUCCESS(rv, rv);
nsIContent* body = GetBodyContent();
if (!body) {
NS_WARNING("no body on video document!");
return NS_ERROR_FAILURE;
}
// make content
nsCOMPtr<nsINodeInfo> nodeInfo;
nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::video, nsnull,
kNameSpaceID_None);
NS_ENSURE_TRUE(nodeInfo, NS_ERROR_FAILURE);
nsRefPtr<nsHTMLMediaElement> element =
static_cast<nsHTMLMediaElement*>(NS_NewHTMLVideoElement(nodeInfo, PR_FALSE));
if (!element)
return NS_ERROR_OUT_OF_MEMORY;
element->SetAutoplay(PR_TRUE);
element->SetControls(PR_TRUE);
element->LoadWithChannel(aChannel, aListener);
return body->AppendChildTo(element, PR_FALSE);
}
nsresult
NS_NewVideoDocument(nsIDocument** aResult)
{
nsVideoDocument* doc = new nsVideoDocument();
if (!doc) {
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ADDREF(doc);
nsresult rv = doc->Init();
if (NS_FAILED(rv)) {
NS_RELEASE(doc);
}
*aResult = doc;
return rv;
}

View File

@ -44,14 +44,24 @@
#include "oggplay/oggplay.h"
class nsIChannel;
class nsIStreamListener;
class nsChannelReader : public OggPlayReader
{
public:
nsChannelReader();
~nsChannelReader();
// Initialize the reader with the given decoder and URI.
nsresult Init(nsMediaDecoder* aDecoder, nsIURI* aURI);
/**
* Initialize the reader with the given decoder, URI, and
* optional channel.
* @param aChannel may be null
* @param aStreamListener if aChannel is non-null, this will return
* a stream listener which should be attached to the channel.
*/
nsresult Init(nsMediaDecoder* aDecoder, nsIURI* aURI, nsIChannel* aChannel,
nsIStreamListener** aStreamListener);
// Cancel any blocking request currently in progress and cause that
// request to return an error. Call on main thread only.

View File

@ -109,10 +109,13 @@ class nsMediaDecoder : public nsIObserver
// Stop playback of a video, and stop download of video stream.
virtual void Stop() = 0;
// Start downloading the video at the given URI. Decode
// the downloaded data up to the point of the first frame
// of data.
virtual nsresult Load(nsIURI* aURI) = 0;
// Start downloading the video. Decode the downloaded data up to the
// point of the first frame of data.
// Exactly one of aURI and aChannel must be null. aListener must be
// null if and only if aChannel is.
virtual nsresult Load(nsIURI* aURI,
nsIChannel* aChannel,
nsIStreamListener **aListener) = 0;
// Draw the latest video data. This is done
// here instead of in nsVideoFrame so that the lock around the

View File

@ -70,7 +70,14 @@ public:
// These methods have the same thread calling requirements
// as those with the same name in nsMediaStream
virtual nsresult Open() = 0;
/**
* @param aStreamListener if null, the strategy should open mChannel
* itself. Otherwise, mChannel is already open and the strategy
* should just return its stream listener in aStreamListener (or set
* *aStreamListener to null, if it doesn't need a listener).
*/
virtual nsresult Open(nsIStreamListener** aStreamListener) = 0;
virtual nsresult Close() = 0;
virtual nsresult Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes) = 0;
virtual nsresult Seek(PRInt32 aWhence, PRInt64 aOffset) = 0;
@ -121,9 +128,15 @@ class nsMediaStream
nsMediaStream();
~nsMediaStream();
// Create a channel for the stream, reading data from the
// media resource at the URI. Call on main thread only.
nsresult Open(nsMediaDecoder *aDecoder, nsIURI* aURI);
/**
* Create a channel for the stream, reading data from the
* media resource at the URI. Call on main thread only.
* @param aChannel if non-null, this channel is used and aListener
* is set to the listener we want for the channel. aURI must
* be the URI for the channel, obtained via NS_GetFinalChannelURI.
*/
nsresult Open(nsMediaDecoder* aDecoder, nsIURI* aURI,
nsIChannel* aChannel, nsIStreamListener** aListener);
// Close the stream, stop any listeners, channels, etc.
// Call on main thread only.

View File

@ -297,34 +297,33 @@ class nsOggDecoder : public nsMediaDecoder
// This method must be called by the owning object before that
// object disposes of this decoder object.
void Shutdown();
virtual void Shutdown();
float GetCurrentTime();
virtual float GetCurrentTime();
// Start downloading the video at the given URI. Decode
// the downloaded data up to the point of the first frame
// of data.
nsresult Load(nsIURI* aURI);
virtual nsresult Load(nsIURI* aURI,
nsIChannel* aChannel,
nsIStreamListener **aListener);
// Start playback of a video. 'Load' must have previously been
// called.
nsresult Play();
virtual nsresult Play();
// Stop playback of a video, and stop download of video stream.
virtual void Stop();
// Seek to the time position in (seconds) from the start of the video.
nsresult Seek(float time);
virtual nsresult Seek(float time);
nsresult PlaybackRateChanged();
virtual nsresult PlaybackRateChanged();
void Pause();
float GetVolume();
void SetVolume(float volume);
float GetDuration();
virtual void Pause();
virtual float GetVolume();
virtual void SetVolume(float volume);
virtual float GetDuration();
void GetCurrentURI(nsIURI** aURI);
nsIPrincipal* GetCurrentPrincipal();
virtual void GetCurrentURI(nsIURI** aURI);
virtual nsIPrincipal* GetCurrentPrincipal();
virtual void UpdateBytesDownloaded(PRUint64 aBytes);

View File

@ -147,10 +147,12 @@ static int oggplay_channel_reader_duration(OggPlayReader* aReader)
return me->duration();
}
nsresult nsChannelReader::Init(nsMediaDecoder* aDecoder, nsIURI* aURI)
nsresult nsChannelReader::Init(nsMediaDecoder* aDecoder, nsIURI* aURI,
nsIChannel* aChannel,
nsIStreamListener** aStreamListener)
{
mCurrentPosition = 0;
return mStream.Open(aDecoder, aURI);
return mStream.Open(aDecoder, aURI, aChannel, aStreamListener);
}
nsChannelReader::~nsChannelReader()

View File

@ -67,7 +67,7 @@ public:
// These methods have the same thread calling requirements
// as those with the same name in nsMediaStream
virtual nsresult Open();
virtual nsresult Open(nsIStreamListener** aStreamListener);
virtual nsresult Close();
virtual nsresult Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes);
virtual nsresult Seek(PRInt32 aWhence, PRInt64 aOffset);
@ -89,19 +89,26 @@ private:
nsCOMPtr<nsIInputStream> mPipeInput;
};
nsresult nsDefaultStreamStrategy::Open()
nsresult nsDefaultStreamStrategy::Open(nsIStreamListener** aStreamListener)
{
nsresult rv;
if (aStreamListener) {
*aStreamListener = nsnull;
}
mListener = new nsChannelToPipeListener(mDecoder);
NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY);
rv = mListener->Init();
nsresult rv = mListener->Init();
NS_ENSURE_SUCCESS(rv, rv);
rv = mChannel->AsyncOpen(mListener, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
if (aStreamListener) {
*aStreamListener = mListener;
NS_ADDREF(mListener);
} else {
rv = mChannel->AsyncOpen(mListener, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = mListener->GetInputStream(getter_AddRefs(mPipeInput));
NS_ENSURE_SUCCESS(rv, rv);
@ -189,7 +196,7 @@ public:
// These methods have the same thread calling requirements
// as those with the same name in nsMediaStream
virtual nsresult Open();
virtual nsresult Open(nsIStreamListener** aStreamListener);
virtual nsresult Close();
virtual nsresult Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes);
virtual nsresult Seek(PRInt32 aWhence, PRInt64 aOffset);
@ -211,35 +218,54 @@ private:
nsCOMPtr<nsIPrincipal> mPrincipal;
};
nsresult nsFileStreamStrategy::Open()
nsresult nsFileStreamStrategy::Open(nsIStreamListener** aStreamListener)
{
nsresult rv;
if (aStreamListener) {
*aStreamListener = nsnull;
}
rv = mChannel->Open(getter_AddRefs(mInput));
nsresult rv;
if (aStreamListener) {
// The channel is already open. We need a synchronous stream that
// implements nsISeekableStream, so we have to find the underlying
// file and reopen it
nsCOMPtr<nsIFileChannel> fc(do_QueryInterface(mChannel));
if (!fc)
return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIFile> file;
rv = fc->GetFile(getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_NewLocalFileInputStream(getter_AddRefs(mInput), file);
} else {
rv = mChannel->Open(getter_AddRefs(mInput));
}
NS_ENSURE_SUCCESS(rv, rv);
mSeekable = do_QueryInterface(mInput);
if (!mSeekable) {
// XXX The file may just be a .url or similar
// shortcut that points to a Web site. We need to fix this by
// doing an async open and waiting until we locate the real resource,
// then using that (if it's still a file!).
return NS_ERROR_FAILURE;
}
// Get the file size and inform the decoder
nsCOMPtr<nsIFileChannel> fc(do_QueryInterface(mChannel));
if (fc) {
nsCOMPtr<nsIFile> file;
rv = fc->GetFile(getter_AddRefs(file));
if (NS_SUCCEEDED(rv)) {
PRInt64 size = 0;
rv = file->GetFileSize(&size);
if (NS_SUCCEEDED(rv)) {
mDecoder->SetTotalBytes(size);
}
}
// Get the file size and inform the decoder. Only files up to 4GB are
// supported here.
PRUint32 size;
rv = mInput->Available(&size);
if (NS_SUCCEEDED(rv)) {
mDecoder->SetTotalBytes(size);
}
/* Get our principal */
nsCOMPtr<nsIScriptSecurityManager> secMan =
do_GetService("@mozilla.org/scriptsecuritymanager;1");
if (secMan) {
nsresult rv = secMan->GetChannelPrincipal(mChannel,
getter_AddRefs(mPrincipal));
rv = secMan->GetChannelPrincipal(mChannel,
getter_AddRefs(mPrincipal));
if (NS_FAILED(rv)) {
return rv;
}
@ -251,7 +277,7 @@ nsresult nsFileStreamStrategy::Open()
NS_NEW_RUNNABLE_METHOD(nsMediaDecoder, mDecoder, ResourceLoaded);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
return mSeekable ? NS_OK : NS_ERROR_FAILURE;
return NS_OK;
}
nsresult nsFileStreamStrategy::Close()
@ -324,7 +350,7 @@ public:
// These methods have the same thread calling requirements
// as those with the same name in nsMediaStream
virtual nsresult Open();
virtual nsresult Open(nsIStreamListener** aListener);
virtual nsresult Close();
virtual nsresult Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes);
virtual nsresult Seek(PRInt32 aWhence, PRInt64 aOffset);
@ -383,18 +409,25 @@ void nsHttpStreamStrategy::Reset(nsIChannel* aChannel,
mPipeInput = aStream;
}
nsresult nsHttpStreamStrategy::Open()
nsresult nsHttpStreamStrategy::Open(nsIStreamListener **aStreamListener)
{
nsresult rv;
if (aStreamListener) {
*aStreamListener = nsnull;
}
mListener = new nsChannelToPipeListener(mDecoder);
NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY);
rv = mListener->Init();
nsresult rv = mListener->Init();
NS_ENSURE_SUCCESS(rv, rv);
rv = mChannel->AsyncOpen(mListener, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
if (aStreamListener) {
*aStreamListener = mListener;
NS_ADDREF(*aStreamListener);
} else {
rv = mChannel->AsyncOpen(mListener, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = mListener->GetInputStream(getter_AddRefs(mPipeInput));
NS_ENSURE_SUCCESS(rv, rv);
@ -684,21 +717,24 @@ nsMediaStream::~nsMediaStream()
MOZ_COUNT_DTOR(nsMediaStream);
}
nsresult nsMediaStream::Open(nsMediaDecoder* aDecoder, nsIURI* aURI)
nsresult nsMediaStream::Open(nsMediaDecoder* aDecoder, nsIURI* aURI,
nsIChannel* aChannel, nsIStreamListener** aListener)
{
NS_ASSERTION(NS_IsMainThread(),
"nsMediaStream::Open called on non-main thread");
nsresult rv;
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel),
aURI,
nsnull,
nsnull,
nsnull,
nsIRequest::LOAD_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
if (aChannel) {
channel = aChannel;
} else {
nsresult rv = NS_NewChannel(getter_AddRefs(channel),
aURI,
nsnull,
nsnull,
nsnull,
nsIRequest::LOAD_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(channel);
nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(channel);
@ -712,7 +748,7 @@ nsresult nsMediaStream::Open(nsMediaDecoder* aDecoder, nsIURI* aURI)
mPlaybackRateCount = 0;
mPlaybackRateStart = PR_IntervalNow();
return mStreamStrategy->Open();
return mStreamStrategy->Open(aListener);
}
nsresult nsMediaStream::Close()

View File

@ -49,6 +49,7 @@
#include "nsIObserverService.h"
#include "nsAutoLock.h"
#include "nsTArray.h"
#include "nsNetUtil.h"
#include "nsOggDecoder.h"
/*
@ -1165,10 +1166,26 @@ nsOggDecoder::~nsOggDecoder()
nsAutoMonitor::DestroyMonitor(mMonitor);
}
nsresult nsOggDecoder::Load(nsIURI* aURI)
nsresult nsOggDecoder::Load(nsIURI* aURI, nsIChannel* aChannel,
nsIStreamListener** aStreamListener)
{
nsresult rv;
mURI = aURI;
if (aStreamListener) {
*aStreamListener = nsnull;
}
if (aURI) {
NS_ASSERTION(!aStreamListener, "No listener should be requested here");
mURI = aURI;
} else {
NS_ASSERTION(aChannel, "Either a URI or a channel is required");
NS_ASSERTION(aStreamListener, "A listener should be requested here");
// If the channel was redirected, we want the post-redirect URI;
// but if the URI scheme was expanded, say from chrome: to jar:file:,
// we want the original URI.
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(mURI));
NS_ENSURE_SUCCESS(rv, rv);
}
StartProgress();
@ -1177,7 +1194,7 @@ nsresult nsOggDecoder::Load(nsIURI* aURI)
mReader = new nsChannelReader();
NS_ENSURE_TRUE(mReader, NS_ERROR_OUT_OF_MEMORY);
rv = mReader->Init(this, aURI);
nsresult rv = mReader->Init(this, mURI, aChannel, aStreamListener);
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_NewThread(getter_AddRefs(mDecodeThread));
@ -1457,9 +1474,12 @@ void nsOggDecoder::ChangeState(PlayState aState)
// If we've completed playback then the decode and display threads
// have been shutdown. To honor the state change request we need
// to reload the resource and restart the threads.
// Like seeking, this will require opening a new channel, which means
// we may not actually get the same resource --- a server may send
// us something different.
mNextState = aState;
mPlayState = PLAY_STATE_LOADING;
Load(mURI);
Load(mURI, nsnull, nsnull);
return;
}

View File

@ -64,6 +64,7 @@ _TEST_FILES = test_autoplay.html \
test_seek6.html \
test_seek7.html \
test_seek8.html \
test_standalone.html \
test_timeupdate1.html \
test_timeupdate2.html \
test_timeupdate3.html \

View File

@ -0,0 +1,29 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Media test: standalone video documents</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="doTest()">
<iframe id="i" src="320x240.ogg"></iframe>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
function doTest()
{
var elem = document.getElementById("i").contentDocument.body.firstChild;
is(elem.tagName.toLowerCase(), "video", "Is video element");
is(elem.currentSrc.substring(elem.currentSrc.length - 11), "320x240.ogg", "currentSrc");
is(elem.controls, true, "Controls set");
is(elem.autoplay, true, "Autoplay set");
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

View File

@ -160,6 +160,7 @@ SHARED_LIBRARY_LIBS += \
$(DEPTH)/media/libtheora/lib/$(LIB_PREFIX)theora.$(LIB_SUFFIX) \
$(DEPTH)/media/libvorbis/lib/$(LIB_PREFIX)vorbis.$(LIB_SUFFIX) \
$(NULL)
LOCAL_INCLUDES += -I$(DEPTH)/content/html/content/src
endif
ifdef NS_PRINTING

View File

@ -56,6 +56,9 @@
#include "nsICSSLoader.h"
#include "nsCRT.h"
#include "nsIViewSourceChannel.h"
#ifdef MOZ_MEDIA
#include "nsHTMLMediaElement.h"
#endif
#include "imgILoader.h"
#include "nsIParser.h"
@ -78,6 +81,9 @@ static NS_DEFINE_IID(kXMLDocumentCID, NS_XMLDOCUMENT_CID);
#ifdef MOZ_SVG
static NS_DEFINE_IID(kSVGDocumentCID, NS_SVGDOCUMENT_CID);
#endif
#ifdef MOZ_MEDIA
static NS_DEFINE_IID(kVideoDocumentCID, NS_VIDEODOCUMENT_CID);
#endif
static NS_DEFINE_IID(kImageDocumentCID, NS_IMAGEDOCUMENT_CID);
static NS_DEFINE_IID(kXULDocumentCID, NS_XULDOCUMENT_CID);
@ -267,6 +273,15 @@ nsContentDLF::CreateInstance(const char* aCommand,
}
}
#ifdef MOZ_MEDIA
if (nsHTMLMediaElement::CanHandleMediaType(aContentType)) {
return CreateDocument(aCommand,
aChannel, aLoadGroup,
aContainer, kVideoDocumentCID,
aDocListener, aDocViewer);
}
#endif
// Try image types
nsCOMPtr<imgILoader> loader(do_GetService("@mozilla.org/image/loader;1"));
PRBool isReg = PR_FALSE;
@ -286,7 +301,6 @@ nsContentDLF::CreateInstance(const char* aCommand,
aDocListener, aDocViewer);
}
// If we get here, then we weren't able to create anything. Sorry!
return NS_ERROR_FAILURE;
}

View File

@ -530,6 +530,9 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsDataDocumentContentPolicy)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsNoDataProtocolContentPolicy)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSyncLoadService)
MAKE_CTOR(CreatePluginDocument, nsIDocument, NS_NewPluginDocument)
#ifdef MOZ_MEDIA
MAKE_CTOR(CreateVideoDocument, nsIDocument, NS_NewVideoDocument)
#endif
#ifdef MOZ_ENABLE_CANVAS
MAKE_CTOR(CreateCanvasRenderingContext2D, nsIDOMCanvasRenderingContext2D, NS_NewCanvasRenderingContext2D)
@ -1282,6 +1285,13 @@ static const nsModuleComponentInfo gComponents[] = {
nsnull,
CreatePluginDocument },
#ifdef MOZ_MEDIA
{ "Video Document",
NS_VIDEODOCUMENT_CID,
nsnull,
CreateVideoDocument },
#endif
{ "Style sheet service",
NS_STYLESHEETSERVICE_CID,
NS_STYLESHEETSERVICE_CONTRACTID,

View File

@ -112,6 +112,7 @@ PRBool NS_SVGEnabled();
#ifdef MOZ_MEDIA
#include "nsMediaDecoder.h"
#include "nsHTMLMediaElement.h"
#endif
#ifdef MOZ_OGG
@ -253,6 +254,7 @@ nsLayoutStatics::Initialize()
return rv;
}
nsHTMLMediaElement::InitMediaTypes();
#endif
#ifdef MOZ_OGG
@ -339,6 +341,9 @@ nsLayoutStatics::Shutdown()
NS_ShutdownFocusSuppressor();
#ifdef MOZ_MEDIA
nsHTMLMediaElement::ShutdownMediaTypes();
#endif
#ifdef MOZ_OGG
nsAudioStream::ShutdownLibrary();
#endif

View File

@ -535,6 +535,9 @@ static nsExtraMimeTypeEntry extraMimeEntries [] =
{ TEXT_XUL, "xul", "XML-Based User Interface Language", MAC_TYPE('TEXT'), MAC_TYPE('ttxt') },
{ TEXT_XML, "xml,xsl,xbl", "Extensible Markup Language", MAC_TYPE('TEXT'), MAC_TYPE('ttxt') },
{ TEXT_CSS, "css", "Style Sheet", MAC_TYPE('TEXT'), MAC_TYPE('ttxt') },
{ "audio/ogg", "oga", "Ogg Audio", 0, 0 },
{ "video/ogg", "ogv", "Ogg Video", 0, 0 },
{ "audio/ogg", "ogg", "Ogg Audio", 0, 0 }
};
#undef MAC_TYPE