html5mediaview.lzx
<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>
<attribute name="__media" value="null
"/>
<event name="onabort"/>
<event name="oncanplay"/>
<event name="oncanplaythrough"/>
<event name="onemptied"/>
<event name="onended"/>
<event name="onerror"/>
<event name="onloadeddata"/>
<event name="onloadedmetadata"/>
<event name="onloadstart"/>
<event name="onpause"/>
<event name="onplay"/>
<event name="onplaying"/>
<event name="onprogress"/>
<event name="onratechange"/>
<event name="onseeked"/>
<event name="onseeking"/>
<event name="onstalled"/>
<event name="onsuspend"/>
<event name="ontimeupdate"/>
<event name="onvolumechange"/>
<event name="onwaiting"/>
<event name="onreadystatechange"/>
<attribute name="ready" type="boolean" value="false
"/>
<attribute name="autobuffer" type="boolean" value="true
"/>
<setter name="autobuffer" args="val">
this.autobuffer = this.__media.autobuffer = val;
</setter>
<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>
<attribute name="buffered"/>
<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>
<method name="getCurrentSrc"> return this.__media.currentSrc; </method>
<attribute name="currentTime" type="number" value="0
"/>
<setter name="currentTime" args="val">
if (!this.__media.readyState == 0) {
this.__media.currentTime = val;
}
</setter>
<attribute name="defaultPlaybackRate" type="number" value="1.0
"/>
<attribute name="duration" type="number" value="0
"/>
<attribute name="ended" type="boolean"/>
<attribute name="end" type="number"/>
<attribute name="error"/>
<attribute name="loop" type="boolean"/>
<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>
<attribute name="networkState"/>
<attribute name="paused"/>
<attribute name="playbackRate" type="number" value="1.0
"/>
<attribute name="played"/>
<attribute name="readyState"/>
<attribute name="HAVE_NOTHING" allocation="class" value="0
"/>
<attribute name="HAVE_METADATA" allocation="class" value="1
"/>
<attribute name="HAVE_CURRENT_DATA" allocation="class" value="2
"/>
<attribute name="HAVE_FUTURE_DATA" allocation="class" value="3
"/>
<attribute name="HAVE_ENOUGH_DATA" allocation="class" value="4
"/>
<method name="seekable">
return this.__media.seekable;
</method>
<method name="seeking">
return this.__media.seeking;
</method>
<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>
<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>
<method name="removeSources" args="">
var node = this.__media;
while (node.firstChild) {
node.removeChild(node.firstChild);
}
</method>
<attribute name="startTime"/>
<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>
<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>
<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>
<method name="canPlayType" args="mtype">
return this.__media.canPlayType(mtype);
</method>
<method name="load">
this.__media.load();
</method>
<method name="pause">
this.__media.pause();
</method>
<method name="play">
this.__media.play();
</method>
<method name="construct" args="parent, args">
super.construct(parent, args);
this.makeNativePlayer(args);
</method>
<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>
<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>
<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>
<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>
<event name="onbeginfullscreen"/>
<event name="onendfullscreen"/>
<attribute name="poster" type="string"/>
<setter name="poster" args="val">
this.__media.poster = val;
</setter>
<method name="getVideoWidth">
return this.__media.videoWidth;
</method>
<method name="getVideoHeight">
return this.__media.videoHeight;
</method>
<method name="getDisplayingFullscreen">
return this.__media.webkitDisplayingFullscreen();
</method>
<method name="getSupportsFullscreen">
return this.__media.webkitSupportsFullscreen();
</method>
<method name="enterFullscreen">
if (this.__media.webkitSupportsFullscreen) {
this.__media.webkitEnterFullscreen();
} else {
Debug.warn('enterFullscreen: webkitSupportsFullscreen not supported');
}
</method>
<method name="exitFullscreen">
if (this.__media.webkitSupportsFullscreen) {
this.__media.webkitExitFullscreen();
} else {
Debug.warn('exitFullscreen: webkitSupportsFullscreen not supported');
}
</method>
</class>
</library>