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 &lt;source&gt; element.
+   *
+   * @return the newly created element
+   */
+  public final SourceElement createSourceElement() {
+ return (SourceElement) DOMImpl.impl.createElement(this, SourceElement.TAG);
+  }

   /**
    * Creates a &lt;span&gt; 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

Reply via email to