html5mediaview.lzx

<!---
    @copyright Copyright 2001-2011 Laszlo Systems, Inc.  All Rights Reserved.
               Use is subject to license terms.

    @access public
    @topic Extensions
    @subtopic Audio-Video
  -->
<library>
<class name="html5mediaview">
    <doc>
        <tag name="shortdesc"><text>Abstract base class to support HTML5 video and audio playback.</text></tag>
        <text>
        <p>See <sgmltag class="element" role="LzView"><html5videoview></sgmltag> and  <sgmltag class="element" role="LzView"><html5audioview></sgmltag></p>
        </text>

    </doc>  
  <!--- the HTML5 native media playback object @keywords private -->
  <attribute name="__media" value="null"/>

  <!--- Sent when the browser stops fetching the media data before the media resource was completely downloaded. -->
  <event name="onabort"/>

  <!--- Sent when the browser can resume playback of the media data,  -->
  <event name="oncanplay"/>
  
  <!--- Sent when the browser estimates that if playback is started now, the media resource could be rendered -->
  <event name="oncanplaythrough"/>
  
  <!--- Sent when the media element network state changes to the NETWORK_EMPTY state. -->
  <event name="onemptied"/>
  
  <!--- Sent when playback has stopped at the end of the media resource and the ended property is set to true. -->
  <event name="onended"/>
  
  <!--- Sent when an error occurs while fetching the media data. Use the error property to get the current error. -->
  <event name="onerror"/>
  
  <!--- Sent when the browser can render the media data at the current playback position for the first time. -->
  <event name="onloadeddata"/>
  
  <!--- Sent when the browser knows the duration and dimensions of the media resource. -->
  <event name="onloadedmetadata"/>
  
  <!--- Sent when the browser begins loading the media data. -->
  <event name="onloadstart"/>
  
  <!--- Sent when playback pauses after the pause method returns. -->
  <event name="onpause"/>
  
  <!--- Sent when playback starts after the play method returns. -->
  <event name="onplay"/>
  
  <!--- Sent when playback starts. -->
  <event name="onplaying"/>
  
  <!--- Sent when the browser is fetching the media data. -->
  <event name="onprogress"/>
  
  <!--- Sent when either the defaultPlaybackRate or the playbackRate property changes. -->
  <event name="onratechange"/>
  
  <!--- Sent when the seeking property is set to false -->
  <event name="onseeked"/>
  
  <!--- Sent when the seeking property is set to true and there is time to send this event. -->
  <event name="onseeking"/>
  
  <!--- Sent when the browser is fetching media data but it has stopped arriving. -->
  <event name="onstalled"/>
  
  <!--- Sent when the browser suspends loading the media data and does not have the entire media resource downloaded. -->
  <event name="onsuspend"/>
  
  <!--- Sent when the currentTime property changes as part of normal playback or because of some other condition. -->
  <event name="ontimeupdate"/>
  
  <!--- Sent when either the volume property or the muted property changes. -->
  <event name="onvolumechange"/>
  
  <!--- Sent when the browser stops playback because it is waiting for the next frame. -->
  <event name="onwaiting"/>
  
  <!--- Sent when the ready-state changes -->
  <event name="onreadystatechange"/>

  <!--- Sent when the media element is ready to accept commands; On iOS, you
       need to wait for this event before setting the media sources and
       other attributes, due to a delay in rendering the element in
       the browser -->
  <attribute name="ready" type="boolean" value="false"/>

  <!--- A Boolean value that gives a hint to the browser that it should automatically buffer media when the webpage is loaded.
      If true (the default), the media data is automatically buffered; otherwise, it is not.
      Availability
  -->
  
  <attribute name="autobuffer" type="boolean" value="true"/>
  <setter name="autobuffer" args="val">
     this.autobuffer = this.__media.autobuffer = val;
  </setter>
  
  <!--- A Boolean value that determines whether the media resource plays automatically when available. -->
  <attribute name="autoplay" type="boolean" value="false"/>
  <setter name="autoplay" args="val">
     this.autoplay = this.__media.autoplay = val;
     if (this.onautoplay) this.onautoplay.sendEvent(val);
  </setter>
  
  <!--- The time ranges of the media resource that have been downloaded. (read-only)
      readonly
  -->
  <attribute name="buffered"/>
  
  <!--- A Boolean value that determines whether the playback controls appear. -->
  <attribute name="controls" type="boolean" value="true"/>
  
  <setter name="controls" args="val"> 
    this.controls = this.__media.controls = val;
    if (this.oncontrols) this.oncontrols.sendEvent(val);
  </setter>
  
  
  <!--- The absolute URL of the media resource. (read-only) -->
  <method name="getCurrentSrc"> return this.__media.currentSrc; </method>
  
  <!--- The current playback position in seconds. -->
  <attribute name="currentTime" type="number" value="0"/>
  <setter name="currentTime" args="val"> 
    
         if (!this.__media.readyState == 0)  {
             this.__media.currentTime = val;
         }
    
  </setter>
  
  <!--- The default rate used to play the media resource.
      The value of this property is a multiple of the media resource’s intrinsic speed. The default value is 1.0. -->
  <attribute name="defaultPlaybackRate" type="number" value="1.0"/>
  
  <!--- The length of the media resource in seconds. (read-only)
      
      This property is 0 if there is no media data available. This property is NaN if the duration is unknown.
      The value is positive infinity if the duration is known but is indefinite—for example, a live stream.
  -->
  <attribute name="duration" type="number" value="0"/>
  
  <!--- A Boolean value that indicates whether the media played to the end. (read-only)
      If true, the video played to the end; otherwise, it did not.
  -->
  <attribute name="ended" type="boolean"/>
  

  <!--- Specifies the time at which an audio or video element will
       stop playing. If this attribute is not specified, the media
       element will play to completion.  -->
  <attribute name="end" type="number"/>  

  <!--- The last error that occurred for this element. (read-only) -->
  <attribute name="error"/>
  
  <!--- A Boolean value that determines whether the playback should loop. -->
  <attribute name="loop" type="boolean"/>

  <!--- A Boolean value that determines whether the audio content should be muted. -->
  <attribute name="muted" type="boolean"/>
  <setter name="muted" args="val"> 
    this.muted = val;
    if (this.__media) {
        this.__media.muted = val;
    } else {
        Debug.error("trying to set muted to ", muted, "but __media is null");
    }
    if (this.onmuted) { this.onmuted.sendEvent(val); }
  </setter>
  
  <!--- The state of downloading the media resource. (read-only) -->
  <attribute name="networkState"/>
  
  <!--- A Boolean value that indicates whether the media is paused. (read-only) -->
  <attribute name="paused"/>
  
  <!--- The speed that the media resource is playing.
      
      The value of this property is a multiple of the media resource’s intrinsic speed. If set to 0.0, a NOT_SUPPORTED_ERR DOM exception is raised. The default value is 1.0.
  -->
  <attribute name="playbackRate" type="number" value="1.0"/>
  
  
  <!--- The ranges of the media resource that was played. (read-only) -->
  <attribute name="played"/>
  
  <!--- The ready state of the media resource. (read-only) -->
  <attribute name="readyState"/>
  
  <!--- HAVE_NOTHING No media data is available for playback at the current time. @keywords private -->
  <attribute name="HAVE_NOTHING" allocation="class" value="0"/>
  
  <!---HAVE_METADATA Enough of the media resource has been loaded to know the duration, and in the case of a video element, the dimensions. 
      @keywords private -->
  <attribute name="HAVE_METADATA" allocation="class" value="1"/>
  
  <!--- HAVE_CURRENT_DATA Data for the current playback position is available, but not enough to begin playback. @keywords private -->
  <attribute name="HAVE_CURRENT_DATA" allocation="class" value="2"/>
  
  <!--- HAVE_FUTURE_DATA Enough data is available to begin playback. @keywords private -->
  <attribute name="HAVE_FUTURE_DATA" allocation="class" value="3"/>
  
  <!--- HAVE_ENOUGH_DATA Enough data is available to play at the default playback rate to the end of the media resource without having to pause to rebuffer. 
      @keywords private -->
  <attribute name="HAVE_ENOUGH_DATA" allocation="class" value="4"/>
  
  <!--- The ranges that can be played in a nonlinear fashion. (read-only) -->
  <method name="seekable">
    return this.__media.seekable;
  </method>
  
  <!--- A Boolean value that indicates whether the element is seeking. (read-only) -->
  <method name="seeking">
    return this.__media.seeking;
  </method>
  
  <!--- The URI address of the media resource to play. -->
  <attribute name="src" type="string"/>
  
  <setter name="src" args="val"> 
    this.src = val;
    if (this.__media) {
        this.__media.src = val;
    } else {
        Debug.error("trying to set src to ", src, "but __media is null");
    }
    if (this.onsrc) { this.onsrc.sendEvent(val); }
  </setter>
  
<!-- 
Add a media source to the media view. This is used to provide a list
of alternate media files; when the play command is issued, the browser will look at the sources in order, playing the
first one whose encoding format it supports.

-->
  <method name="addSource" args="url,mimetype=null,codecs=null">
    
     /*
     http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#the-source-element

     Dynamically modifying a source element and its attribute when the
     element is already inserted in a video or audio element will have no
     effect. To change what is playing, either just use the src attribute
     on the media element directly, or call the load() method on the media
     element after manipulating the source elements.  The type attribute
     gives the type of the media resource, to help the user agent determine
     if it can play this media resource before fetching it. If specified,
     its value must be a valid MIME type. The codecs parameter, which
     certain MIME types define, might be necessary to specify exactly how
     the resource is encoded. [RFC4281]

     The following list shows some examples of how to use the codecs= MIME parameter in the type attribute.

     H.264 Constrained baseline profile video (main and extended video compatible) level 3 and Low-Complexity AAC audio in MP4 container

         <source src='video.mp4' type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'>

     H.264 Extended profile video (baseline-compatible) level 3 and Low-Complexity AAC audio in MP4 container

         <source src='video.mp4' type='video/mp4; codecs="avc1.58A01E, mp4a.40.2"'>

     H.264 Main profile video level 3 and Low-Complexity AAC audio in MP4 container

         <source src='video.mp4' type='video/mp4; codecs="avc1.4D401E, mp4a.40.2"'>

     H.264 'High' profile video (incompatible with main, baseline, or extended profiles) level 3 and Low-Complexity AAC audio in MP4 container

         <source src='video.mp4' type='video/mp4; codecs="avc1.64001E, mp4a.40.2"'>

     MPEG-4 Visual Simple Profile Level 0 video and Low-Complexity AAC audio in MP4 container

         <source src='video.mp4' type='video/mp4; codecs="mp4v.20.8, mp4a.40.2"'>

     MPEG-4 Advanced Simple Profile Level 0 video and Low-Complexity AAC audio in MP4 container

         <source src='video.mp4' type='video/mp4; codecs="mp4v.20.240, mp4a.40.2"'>

     MPEG-4 Visual Simple Profile Level 0 video and AMR audio in 3GPP container

         <source src='video.3gp' type='video/3gpp; codecs="mp4v.20.8, samr"'>

     Theora video and Vorbis audio in Ogg container

         <source src='video.ogv' type='video/ogg; codecs="theora, vorbis"'>

     Theora video and Speex audio in Ogg container

         <source src='video.ogv' type='video/ogg; codecs="theora, speex"'>

     Vorbis audio alone in Ogg container

         <source src='audio.ogg' type='audio/ogg; codecs=vorbis'>

     Speex audio alone in Ogg container

         <source src='audio.spx' type='audio/ogg; codecs=speex'>

     FLAC audio alone in Ogg container

         <source src='audio.oga' type='audio/ogg; codecs=flac'>

     Dirac video and Vorbis audio in Ogg container

         <source src='video.ogv' type='video/ogg; codecs="dirac, vorbis"'>

     Theora video and Vorbis audio in Matroska container

         <source src='video.mkv' type='video/x-matroska; codecs="theora, vorbis"'>

     The media attribute gives the intended media type of the media
     resource, to help the user agent determine if this media resource is
     useful to the user before fetching it. Its value must be a valid media
     query.

     The default, if the media attribute is omitted, is "all", meaning that
     by default the media resource is suitable for all media.

     If a source element is inserted as a child of a media element that has
     no src attribute and whose networkState has the value NETWORK_EMPTY,
     the user agent must invoke the media element's resource selection
     algorithm.

     The IDL attributes src, type, and media must reflect the respective
     content attributes of the same name.

     If the author isn't sure if the user agents will all be able to render
     the media resources provided, the author can listen to the error event
     on the last source element and trigger fallback behavior:

     <script>
      function fallback(video) {
        // replace <video> with its contents
        while (video.hasChildNodes()) {
          if (video.firstChild instanceof HTMLSourceElement)
            video.removeChild(video.firstChild);
          else
            video.parentNode.insertBefore(video.firstChild, video);
        }
        video.parentNode.removeChild(video);
      }
     </script>


     <video controls autoplay>
      <source src='video.mp4' type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'>
      <source src='video.ogv' type='video/ogg; codecs="theora, vorbis"'
              onerror="fallback(parentNode)">
      ...
     </video>


     */

    var source = document.createElement('source');
    source.src = url;
    if (mimetype != null) source.type = mimetype;
    if (codecs != null) source.codecs = codecs;
    this.__media.appendChild(source);
    
  </method>

  <!--- removes all video sources -->
  <method name="removeSources" args="">
    var node = this.__media;
    while (node.firstChild) {
        node.removeChild(node.firstChild);
    }
  </method>
  <!--- The earliest possible time in seconds to start playback. (read-only) -->
  <attribute name="startTime"/>
  
  <!--- The volume of the audio portion of the media element.
      
      The value of this property must be in the range from 0.0 (silent) to
      1.0 (loudest); otherwise, a INDEX_SIZE_ERR DOM exception is
      raised. The default value is 1.0.
  -->
  <attribute name="volume" type="number"/>
  
  <setter name="volume" args="val">
    this.volume = val;
    if (this.__media) {
         this.__media.volume = val;
    }
    if (this.onvolumechange) { this.onvolumechange.sendEvent(val); }
    if (this.onvolume) { this.onvolume.sendEvent(val); }
  </setter>
  
  
  <!--- @keywords private -->
  <method name="_addlisteners">
    /* add all event listeners for HTML5 media element events */  
    var media = this.__media;
    var self = this;
    
    media.addEventListener('loadstart', function(evt) { self.onloadstart.sendEvent() }, false);  
    
    media.addEventListener('canplaythrough',function(evt) {  self.oncanplaythrough.sendEvent() }, false);
    
    media.addEventListener('canplay', function(evt) { self.oncanplay.sendEvent() }, false);
    
    media.addEventListener('loadeddata', function(evt) { self.onloadeddata.sendEvent() }, false); 
    
    media.addEventListener('loadedmetadata', function(evt) { self.onloadedmetadata.sendEvent() }, false);
    
    media.addEventListener('abort', function(evt) { self.onabort.sendEvent() }, false);
    
    media.addEventListener('emptied', function(evt) { self.onemptied.sendEvent() }, false);
    
    media.addEventListener('error', function(evt) { self.onerror.sendEvent() }, false);
    
    media.addEventListener('stalled', function(evt) { self.onstalled.sendEvent() }, false);
    
    media.addEventListener('suspend', function(evt) { self.onsuspend.sendEvent() }, false);
    
    media.addEventListener('waiting', function(evt) { self.onwaiting.sendEvent() }, false);
    
    media.addEventListener('pause', function(evt) { self.onpause.sendEvent() }, false);
    
    media.addEventListener('play', function(evt) { self.onplay.sendEvent() }, false);
    
    media.addEventListener('volumechange', function(evt) { self.volume = evt.target.volume; self.onvolumechange.sendEvent(evt.target.volume) }, false);
    
    media.addEventListener('playing', function(evt) { self.playing = evt.target.playing; self.onplaying.sendEvent() }, false);
    
    media.addEventListener('seeked', function(evt) { self.seeked = evt.target.seeked; self.onseeked.sendEvent() }, false);    
    
    media.addEventListener('seeking', function(evt) { self.onseeking.sendEvent() }, false);    
    
    media.addEventListener('durationchange', function(evt) { self._updateDuration() }, false);
    
    media.addEventListener('progress', function(evt) { self.onprogress.sendEvent() }, false);   
    
    media.addEventListener('ratechange', function(evt) { self.onratechange.sendEvent() }, false);   
    
    
    
    media.addEventListener('timeupdate', function(evt) {
            self.currentTime = evt.target.currentTime;
            if (self.oncurrentTime) {
                self.oncurrentTime.sendEvent(evt.target.currentTime);
            }
            if (self.ontimeupdate) {
                self.ontimeupdate.sendEvent(self.currentTime); }
        }, false); 
    
    media.addEventListener('ended', function(evt) { self.onended.sendEvent() }, false); 
    
  </method>
  
  
  <!--- @keywords private -->
  <method name="_updateDuration" args="val">
    //Debug.info("_updateDuration", this.__media.duration, val);
    this.duration = this.__media.duration;
    if (this.onduration) { this.onduration.sendEvent(this.duration); }
  </method>
  
  <setter name="height" args="h"> 
    super.setAttribute ('height', h);
    if (this.__media) {
        this.__media.height = h;
        this.__media.style.height = h + "px";
    } 
  </setter> 
  
  
  <setter name="width" args="w"> 
    super.setAttribute ('width', w);
    if (this.__media) {
        this.__media.width = w;
        this.__media.style.width = w + "px";
    }
  </setter> 
  
  
  <!--- Returns whether the media element supports the specified MIME type. 
      DOMString canPlayType (in DOMString type);
      Return Value
      
      The possible string values are: “no”, “probably” and “maybe”.
  -->
  
  <method name="canPlayType" args="mtype">
    return this.__media.canPlayType(mtype);
  </method>
  
  <!--- Starts loading the media resource. -->
  <method name="load">
    this.__media.load();
  </method>
  
  <!--- Pauses the media playback if in progress. -->
  <method name="pause">
    this.__media.pause();
  </method>
  
  <!--- Begins playing the media resource. -->
  <method name="play">
    this.__media.play();
  </method>
  
  <method name="construct" args="parent, args">
    super.construct(parent, args);
    this.makeNativePlayer(args);
  </method>


  
  <!--- @keywords private -->
  <method name="init">
    super.init();
    this._readyDelay();
  </method>
  
  <method name="_readyDelay">
    // In iOS, it seems that it takes some time for the video
    // element to actually be rendered into the document and able to
    // accept new media sources, so we use a callback to send an
    // 'onready' event when it's safe to operate on the video or audio
    // view.
    var self = this;
    setTimeout(function () {self.setAttribute('ready', true);}, 0);
  </method>


</class>

<class name="html5audioview" extends="html5mediaview">
    <doc>
        <text>
            <p><tagname>html5audioview</tagname> allows applications to play audio from HTTP servers.</p> 

            <example><programlisting class="code">
            <canvas>
            <html5audioview src="http://www.beartronics.com/music/figleafrag.mp3" autoplay="true" width="320" height="240"/>
            </canvas>
            </programlisting></example>
        </text>  
        </doc>
  <!--- @keywords private -->
  <method name="construct" args="parent, args">
    super.construct(parent, args);
  </method>

  
  <method name="makeNativePlayer" args="args">
    var media = document.createElement('audio');
    media.style.height = this.height;
    media.style.width = this.width;
    // Have to set this early for iPad for some reason
    media.controls = args.controls;
    media.autoplay = args.autoplay;
    this.__media = media;
    this.sprite.__LZdiv.appendChild(media);
    this._addlisteners();
  </method>



</class>

<class name="html5videoview" extends="html5mediaview">
      <doc>
        <text>
            <p><tagname>html5videoview</tagname> allows applications to play video from HTTP servers.</p> 

            <example><programlisting class="code">
            <canvas>
            <html5videoview src="http://www.archive.org/download/SoundieF/SoundieF_512kb.mp4" autoplay="true" width="320" height="240"/>
            </canvas>
            </programlisting></example>
        </text>  
        </doc>

  <!--- @keywords private -->
  <method name="makeNativePlayer" args="args">
    var vid = document.createElement('video');
    vid.style.height = this.height;
    vid.style.width = this.width;
    vid.controls = args.controls;
    vid.autoplay = args.autoplay;
    this.__media = vid;
    this.sprite.__LZdiv.appendChild(vid);
    this._addlisteners();
  </method>
  



  
  <!--- @keywords private -->
  <method name="_addlisteners">
    super._addlisteners();
    this.__media.addEventListener('webkitbeginfullscreen', function(evt) { if (this.onbeginfullscreen) this.onbeginfullscreen.sendEvent() }, false); 
    this.__media.addEventListener('webkitendfullscreen', function(evt) { if (this.onendfullscreen) this.onendfullscreen.sendEvent() }, false); 
  </method>
  
  
  <!--- Sent when the video begins displaying as fullscreen -->
  <event name="onbeginfullscreen"/>
  
  <!--- Sent when the video stops displaying as fullscreen -->
  <event name="onendfullscreen"/>
  
  
  <attribute name="poster" type="string"/>
  
  <setter name="poster" args="val"> 
    this.__media.poster = val;
  </setter>
  
  
  
  <!--- The native width of the video in CSS pixels. (read-only) -->
  <method name="getVideoWidth">
    return this.__media.videoWidth;
  </method>
  
  <!--- The native height of the video in CSS pixels. (read-only) -->
  <method name="getVideoHeight">
    return this.__media.videoHeight;
  </method>
  
  <!--- Is video element displaying fullscreen mode? -->
  <method name="getDisplayingFullscreen">
    return this.__media.webkitDisplayingFullscreen();
  </method>
  
  <!--- Does video element support fullscreen mode? -->
  <method name="getSupportsFullscreen">
    return this.__media.webkitSupportsFullscreen();
  </method>
  
  <!--- Enters fullscreen mode. -->
  <method name="enterFullscreen">
    if (this.__media.webkitSupportsFullscreen) {
        this.__media.webkitEnterFullscreen();
    } else {
       Debug.warn('enterFullscreen: webkitSupportsFullscreen not supported');
    }
  </method>
  
  <!--- Exits fullscreen mode. -->
  <method name="exitFullscreen">
    if (this.__media.webkitSupportsFullscreen) {
        this.__media.webkitExitFullscreen();
    } else {
       Debug.warn('exitFullscreen: webkitSupportsFullscreen not supported');
    }
  </method>
  
  
</class>


</library>

Cross References

Classes