Revision: 10117
Author: jlaba...@google.com
Date: Mon May 2 06:45:06 2011
Log: Adding the SourceElement for use with Audio and Video, and adding
convenience methods in those widgets to use the element. Multiple
SourceElements can be specified for an AudioElement/VideoElement widget,
and the browser will choose and download one of the sources that it can
play. This is convenient and easier than doing a runtime check in user
code to figure out the best source file.
Review at http://gwt-code-reviews.appspot.com/1423810
Review by: p...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=10117
Added:
/trunk/user/src/com/google/gwt/dom/client/SourceElement.java
Modified:
/trunk/samples/mobilewebapp/src/com/google/gwt/sample/mobilewebapp/client/ui/SoundEffects.java
/trunk/user/src/com/google/gwt/dom/client/AudioElement.java
/trunk/user/src/com/google/gwt/dom/client/Document.java
/trunk/user/src/com/google/gwt/dom/client/VideoElement.java
/trunk/user/src/com/google/gwt/media/client/MediaBase.java
/trunk/user/test/com/google/gwt/media/client/MediaTest.java
=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/dom/client/SourceElement.java Mon May 2
06:45:06 2011
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package com.google.gwt.dom.client;
+
+/**
+ * The SOURCE element specifies one of potentially multiple source file in
a
+ * media element.
+ *
+ * @see <a
href="http://www.w3.org/TR/html5/video.html#the-source-element">W3C
+ * HTML Specification</a>
+ */
+@TagName(SourceElement.TAG)
+public class SourceElement extends Element {
+
+ static final String TAG = "source";
+
+ /**
+ * Assert that the given {@link Element} is compatible with this class
and
+ * automatically typecast it.
+ */
+ public static SourceElement as(Element elem) {
+ assert elem.getTagName().equalsIgnoreCase(TAG);
+ return (SourceElement) elem;
+ }
+
+ protected SourceElement() {
+ }
+
+ /**
+ * Returns the source URL for the media, or {@code null} if none is set.
+ *
+ * @return a String URL or {@code null}
+ *
+ * @see #setSrc(String)
+ */
+ public final native String getSrc() /*-{
+ return this.src;
+ }-*/;
+
+ /**
+ * Returns the type of media represented by the src, or {@code null} if
none
+ * is set.
+ *
+ * @return a String type, or {@code null}
+ *
+ * @see #setType(String)
+ */
+ public final native String getType() /*-{
+ return this.type;
+ }-*/;
+
+ /**
+ * Sets the source URL for the media.
+ *
+ * @param url a String URL
+ *
+ * @see #getSrc()
+ */
+ public final native void setSrc(String url) /*-{
+ this.src = url;
+ }-*/;
+
+ /**
+ * Sets the type of media represented by the src. The browser will look
at the
+ * type when deciding which source files to request from the server.
+ *
+ * <p>
+ * The type is the format or encoding of the media represented by the
source
+ * element. For example, the type of an {@link AudioElement} could be
one of
+ * {@value AudioElement#TYPE_OGG}, {@link AudioElement#TYPE_MP3}, or
+ * {@link AudioElement#TYPE_WAV}.
+ * </p>
+ *
+ * <p>
+ * You can also add the codec information to the type, giving the
browser even
+ * more information about whether or not it can play the file (Example: "
+ * <code>audio/ogg; codec=vorbis</code>");
+ * </p>
+ *
+ * @param type the media type
+ *
+ * @see #getType()
+ */
+ public final native void setType(String type) /*-{
+ this.type = type;
+ }-*/;
+}
=======================================
---
/trunk/samples/mobilewebapp/src/com/google/gwt/sample/mobilewebapp/client/ui/SoundEffects.java
Wed Apr 27 05:15:56 2011
+++
/trunk/samples/mobilewebapp/src/com/google/gwt/sample/mobilewebapp/client/ui/SoundEffects.java
Mon May 2 06:45:06 2011
@@ -16,49 +16,13 @@
package com.google.gwt.sample.mobilewebapp.client.ui;
import com.google.gwt.dom.client.AudioElement;
-import com.google.gwt.dom.client.Document;
import com.google.gwt.media.client.Audio;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* A bundle of sound effects that can be used in the application.
*/
public class SoundEffects {
- /**
- * The source path and type of an audio source file.
- */
- private static class AudioSource {
- private final String source;
- private final AudioType type;
-
- public AudioSource(String source, AudioType type) {
- this.source = source;
- this.type = type;
- }
- }
-
- /**
- * The supported audio types.
- */
- private static enum AudioType {
- OGG("audio/ogg"), MP3("audio/mp3"), WAV("audio/wav");
-
- private final String mimeType;
-
- private AudioType(String mimeType) {
- this.mimeType = mimeType;
- }
-
- public String getMimeType() {
- return mimeType;
- }
- }
-
- private static List<AudioType> typePreference = new
ArrayList<AudioType>();
-
private static SoundEffects instance;
private static boolean isSupported;
@@ -71,37 +35,11 @@
if (instance == null) {
isSupported = Audio.isSupported();
instance = new SoundEffects();
-
- // Detect which audio types we support.
- if (isSupported) {
- AudioElement elem = Document.get().createAudioElement();
-
- // Prefer "can play probably" to "can play maybe".
- for (AudioType audioType : AudioType.values()) {
- if
(AudioElement.CAN_PLAY_PROBABLY.equals(elem.canPlayType(audioType.getMimeType())))
{
- typePreference.add(audioType);
- }
- }
-
- // Use "can play maybe" if its the only thing available.
- for (AudioType audioType : AudioType.values()) {
- if
(AudioElement.CAN_PLAY_MAYBE.equals(elem.canPlayType(audioType.getMimeType())))
{
- typePreference.add(audioType);
- }
- }
- }
}
return instance;
}
- /**
- * Create and return an audio source.
- */
- private static AudioSource audioSource(String source, AudioType type) {
- return new AudioSource(source, type);
- }
-
- private AudioElement error;
+ private Audio error;
/**
* Construct using {@link #get()}.
@@ -122,79 +60,43 @@
*/
public void prefetchError() {
if (isSupported && error == null) {
- error =
- createAudioElement(audioSource("audio/error.ogg",
AudioType.OGG), audioSource(
- "audio/error.mp3", AudioType.MP3),
audioSource("audio/error.wav", AudioType.WAV));
+ error = Audio.createIfSupported();
+ error.addSource("audio/error.ogg", AudioElement.TYPE_OGG);
+ error.addSource("audio/error.mp3", AudioElement.TYPE_MP3);
+ error.addSource("audio/error.wav", AudioElement.TYPE_WAV);
+ prefetchAudio(error);
}
}
/**
- * Create an {@link Audio} that will play one of the specified source
media
- * files. The sources will be tried in the order they are added until a
- * supported format is found.
+ * Play an audio.
*
- * <p>
- * This method will attempt to prefetch the audio sources by playing the
file
- * muted.
- * </p>
- *
- * @param sources the source files, of which one will be chosen
- * @return a new {@link AudioElement}, or null if not supported
+ * @param audio the audio to play, or null if not supported
*/
- private AudioElement createAudioElement(AudioSource... sources) {
- if (!isSupported) {
- return null;
- }
-
- AudioSource bestSource = null;
- for (int i = 0; i < typePreference.size() && bestSource == null; i++) {
- AudioType type = typePreference.get(i);
- for (AudioSource source : sources) {
- if (source.type == type) {
- bestSource = source;
- break;
- }
- }
- }
-
- // None of the source files are supported.
- if (bestSource == null) {
- return null;
- }
-
- // Create the audio element.
- AudioElement audio = Document.get().createAudioElement();
- audio.setSrc(bestSource.source);
-
- // Force the browser to fetch the source files.
- audio.setVolume(0.0);
+ private void playAudio(Audio audio) {
+ if (audio == null) {
+ return;
+ }
+
+ // Pause current progress.
+ audio.pause();
+
+ // Reset the source.
+ // TODO(jlabanca): Is cache-control=private making the source
unseekable?
+ audio.setSrc(audio.getCurrentSrc());
+
+ // Unmute because we muted in createAudioElement.
audio.play();
-
- return audio;
}
/**
- * Play an audio element.
+ * Prefetch an audio.
*
- * @param audio the audio element to play, or null if not supported
+ * @param audio the audio to prefetch, or null if not supported
*/
- private void playAudio(AudioElement audio) {
- if (audio == null) {
- return;
- }
-
- // Pause current progress.
- audio.pause();
-
- /*
- * Some browsers throw an error when we try to seek back to time 0, so
reset
- * the source instead. The audio file should be loaded from the browser
- * cache.
- */
- audio.setSrc(audio.getSrc());
-
- // Unmute because we muted in createAudioElement.
- audio.setVolume(1.0);
- audio.play();
+ private void prefetchAudio(Audio audio) {
+ if (audio != null) {
+ audio.load();
+ }
}
}
=======================================
--- /trunk/user/src/com/google/gwt/dom/client/AudioElement.java Fri Jan 28
12:43:16 2011
+++ /trunk/user/src/com/google/gwt/dom/client/AudioElement.java Mon May 2
06:45:06 2011
@@ -34,6 +34,21 @@
*/
public static final String TAG = "audio";
+ /**
+ * The audio type of MP3 encoded audio.
+ */
+ public static final String TYPE_MP3 = "audio/mpeg";
+
+ /**
+ * The audio type of Ogg encoded audio.
+ */
+ public static final String TYPE_OGG = "audio/ogg";
+
+ /**
+ * The audio type of WAV encoded audio.
+ */
+ public static final String TYPE_WAV = "audio/wav";
+
protected AudioElement() {
}
}
=======================================
--- /trunk/user/src/com/google/gwt/dom/client/Document.java Wed Mar 30
05:37:55 2011
+++ /trunk/user/src/com/google/gwt/dom/client/Document.java Mon May 2
06:45:06 2011
@@ -1048,6 +1048,15 @@
public final SelectElement createSelectElement(boolean multiple) {
return DOMImpl.impl.createSelectElement(this, multiple);
}
+
+ /**
+ * Creates an <source> element.
+ *
+ * @return the newly created element
+ */
+ public final SourceElement createSourceElement() {
+ return (SourceElement) DOMImpl.impl.createElement(this,
SourceElement.TAG);
+ }
/**
* Creates a <span> element.
=======================================
--- /trunk/user/src/com/google/gwt/dom/client/VideoElement.java Fri Jan 28
12:43:16 2011
+++ /trunk/user/src/com/google/gwt/dom/client/VideoElement.java Mon May 2
06:45:06 2011
@@ -34,6 +34,21 @@
*/
public static final String TAG = "video";
+ /**
+ * The audio type of MP4 encoded video.
+ */
+ public static final String TYPE_MP4 = "video/mp4";
+
+ /**
+ * The audio type of Ogg encoded video.
+ */
+ public static final String TYPE_OGG = "video/ogg";
+
+ /**
+ * The audio type of WebM encoded audio.
+ */
+ public static final String TYPE_WEBM = "video/webm";
+
protected VideoElement() {
}
=======================================
--- /trunk/user/src/com/google/gwt/media/client/MediaBase.java Fri Apr 15
06:58:00 2011
+++ /trunk/user/src/com/google/gwt/media/client/MediaBase.java Mon May 2
06:45:06 2011
@@ -15,7 +15,9 @@
*/
package com.google.gwt.media.client;
+import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.MediaElement;
+import com.google.gwt.dom.client.SourceElement;
import com.google.gwt.event.dom.client.CanPlayThroughEvent;
import com.google.gwt.event.dom.client.CanPlayThroughHandler;
import com.google.gwt.event.dom.client.EndedEvent;
@@ -60,6 +62,57 @@
return addDomHandler(handler, ProgressEvent.getType());
}
+ /**
+ * Add a source element to this media. The browser will request source
files
+ * from the server until it finds one it can play.
+ *
+ * <p>
+ * Only use this method if you do not know the type of the source file,
as the
+ * browser cannot determine the format from the filename and must
download
+ * each source until a compatible one is found. Instead, you should
specify
+ * the type for the media using {@link #addSource(String, String)} so the
+ * browser can choose a source file without downloading the file.
+ * </p>
+ *
+ * @param url a String URL
+ * @see #addSource(String, String)
+ */
+ public SourceElement addSource(String url) {
+ SourceElement elem = Document.get().createSourceElement();
+ elem.setSrc(url);
+ getElement().appendChild(elem);
+ return elem;
+ }
+
+ /**
+ * Add a source element to this media, specifying the type (format) of
the
+ * media. The browser will choose a supported source file and download
it.
+ *
+ * <p>
+ * The type is the format or encoding of the media represented by the
source
+ * element. For example, the type of an
+ * {@link com.google.gwt.dom.client.AudioElement} could be one of
+ * {@value com.google.gwt.dom.client.AudioElement#TYPE_OGG},
+ * {@link com.google.gwt.dom.client.AudioElement#TYPE_MP3}, or
+ * {@link com.google.gwt.dom.client.AudioElement#TYPE_WAV}.
+ * </p>
+ *
+ * <p>
+ * You can also add the codec information to the type, giving the
browser even
+ * more information about whether or not it can play the file (Example: "
+ * <code>audio/ogg; codec=vorbis</code>");
+ * </p>
+ *
+ * @param url a String URL
+ * @param type the type (format) of the media
+ * @see #getSrc()
+ */
+ public SourceElement addSource(String url, String type) {
+ SourceElement elem = addSource(url);
+ elem.setType(type);
+ return elem;
+ }
+
/**
* Returns {@code true} if the native player is capable of playing
content of
* the given MIME type.
@@ -382,6 +435,17 @@
public void play() {
getMediaElement().play();
}
+
+ /**
+ * Remove the specified {@link SourceElement} from this media. If the
source
+ * element is not a child of this widget, it will not be removed.
+ *
+ * @param source the source element to remove
+ * @see #addSource(String, String)
+ */
+ public void removeSource(SourceElement source) {
+ getElement().removeChild(source);
+ }
/**
* Enables or disables autoplay of the resource.
@@ -479,10 +543,18 @@
/**
* Sets the source URL for the media.
- *
+ *
+ * <p>
+ * Support for different media types varies between browsers. Instead of
using
+ * this method, you should encode your media in multiple formats and add
all
+ * of them using {@link #addSource(String, String)} so the browser can
choose
+ * a source that it supports.
+ * </p>
+ *
* @param url a String URL
- *
+ *
* @see #getSrc()
+ * @see #addSource(String, String)
*/
public void setSrc(String url) {
getMediaElement().setSrc(url);
=======================================
--- /trunk/user/test/com/google/gwt/media/client/MediaTest.java Fri Apr 15
06:58:00 2011
+++ /trunk/user/test/com/google/gwt/media/client/MediaTest.java Mon May 2
06:45:06 2011
@@ -16,6 +16,7 @@
package com.google.gwt.media.client;
import com.google.gwt.dom.client.MediaElement;
+import com.google.gwt.dom.client.SourceElement;
import com.google.gwt.junit.DoNotRunWith;
import com.google.gwt.junit.Platform;
import com.google.gwt.junit.client.GWTTestCase;
@@ -87,6 +88,30 @@
public String getModuleName() {
return "com.google.gwt.media.MediaTest";
}
+
+ public void testAddSource() {
+ final MediaBase media = getMedia();
+ if (media == null) {
+ return; // don't continue if not supported
+ }
+
+ // Add some source elements.
+ SourceElement source0 = media.addSource("file.ogg", "audio/ogg");
+ assertEquals("file.ogg", source0.getSrc());
+ assertEquals("audio/ogg", source0.getType());
+ SourceElement source1 = media.addSource("file.ogv", "video/ogg");
+ assertEquals("file.ogv", source1.getSrc());
+ assertEquals("video/ogg", source1.getType());
+
+ // Add a source without a type.
+ SourceElement source2 = media.addSource("file.mp3");
+ assertEquals("file.mp3", source2.getSrc());
+
+ // Check that the sources are a children of the media.
+ assertEquals(media.getElement(), source0.getParentElement());
+ assertEquals(media.getElement(), source1.getParentElement());
+ assertEquals(media.getElement(), source2.getParentElement());
+ }
public void testAutoPlay() {
final MediaBase media = getMedia();
@@ -309,6 +334,36 @@
|| state == MediaElement.HAVE_METADATA
|| state == MediaElement.HAVE_NOTHING);
}
+
+ public void testRemoveSource() {
+ final MediaBase media = getMedia();
+ if (media == null) {
+ return; // don't continue if not supported
+ }
+
+ // Add some source elements.
+ SourceElement source0 = media.addSource("file.ogg", "audio/ogg");
+ SourceElement source1 = media.addSource("file.ogv", "video/ogg");
+ SourceElement source2 = media.addSource("file.mp3");
+ assertEquals(media.getElement(), source0.getParentElement());
+ assertEquals(media.getElement(), source1.getParentElement());
+ assertEquals(media.getElement(), source2.getParentElement());
+
+ // Remove a source.
+ media.removeSource(source1);
+ assertEquals(media.getElement(), source0.getParentElement());
+ assertNull(source1.getParentElement());
+ assertEquals(media.getElement(), source2.getParentElement());
+
+ // Let a source remove itself.
+ source2.removeFromParent();
+ assertEquals(media.getElement(), source0.getParentElement());
+ assertNull(source1.getParentElement());
+ assertNull(source2.getParentElement());
+
+ // Remove a source that is not a child.
+ media.removeSource(source0);
+ }
public void testSupported() {
// test the isxxxSupported() call if running known sup or not sup
browsers.
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors