video.js
  1 /*{# Copyright (c) 2012 Turbulenz Limited #}*/
  2 /*
  3 * @title: Video playback
  4 * @description:
  5 * This sample shows how to play a video into a texture.
  6 */
  7 /*{{ javascript("jslib/observer.js") }}*/
  8 /*{{ javascript("jslib/requesthandler.js") }}*/
  9 /*{{ javascript("jslib/utilities.js") }}*/
 10 /*{{ javascript("jslib/services/turbulenzservices.js") }}*/
 11 /*{{ javascript("jslib/services/turbulenzbridge.js") }}*/
 12 /*{{ javascript("jslib/services/gamesession.js") }}*/
 13 /*{{ javascript("jslib/services/mappingtable.js") }}*/
 14 /*global TurbulenzEngine: true */
 15 /*global TurbulenzServices: false */
 16 /*global RequestHandler: false */
 17 TurbulenzEngine.onload = function onloadFn() {
 18     var graphicsDevice = TurbulenzEngine.createGraphicsDevice({});
 19 
 20     if (graphicsDevice && graphicsDevice.renderer === "Internet Explorer") {
 21         window.alert("The video sample is not supported on Internet Explorer");
 22         return;
 23     }
 24     var soundDevice = TurbulenzEngine.createSoundDevice({});
 25     var mathDevice = TurbulenzEngine.createMathDevice({});
 26     var requestHandler = RequestHandler.create({});
 27 
 28     var video;
 29     var videoPosition = -1;
 30 
 31     var shader, technique;
 32     var texture;
 33 
 34     var clearColor = mathDevice.v4Build(0, 0, 0, 1);
 35     var clipSpace = mathDevice.v4Build(1, -1, 0, 0);
 36     var videoColor = mathDevice.v4Build(1, 1, 1, 1);
 37 
 38     var primitive = graphicsDevice.PRIMITIVE_TRIANGLE_STRIP;
 39     var semantics = graphicsDevice.createSemantics(['POSITION', 'TEXCOORD0']);
 40     var vertexBuffer = graphicsDevice.createVertexBuffer({
 41         numVertices: 4,
 42         attributes: [
 43             graphicsDevice.VERTEXFORMAT_FLOAT2,
 44             graphicsDevice.VERTEXFORMAT_FLOAT2
 45         ],
 46         dynamic: false,
 47         data: [
 48             -1.0,
 49             1.0,
 50             0.0,
 51             1.0,
 52             1.0,
 53             1.0,
 54             1.0,
 55             1.0,
 56             -1.0,
 57             -1.0,
 58             0.0,
 59             0.0,
 60             1.0,
 61             -1.0,
 62             1.0,
 63             0.0
 64         ]
 65     });
 66 
 67     var source = soundDevice.createSource({
 68         position: [0, 0, 0],
 69         relative: true,
 70         looping: true
 71     });
 72     var sound;
 73 
 74     var assetsToLoad = 3;
 75 
 76     function mappingTableReceived(mappingTable) {
 77         var videoURL;
 78         if (graphicsDevice.isSupported("FILEFORMAT_WEBM")) {
 79             videoURL = mappingTable.getURL("videos/turbulenzanimation.webm");
 80         } else {
 81             videoURL = mappingTable.getURL("videos/turbulenzanimation.mp4");
 82         }
 83 
 84         graphicsDevice.createVideo({
 85             src: videoURL,
 86             looping: true,
 87             onload: function (v) {
 88                 if (v) {
 89                     video = v;
 90 
 91                     assetsToLoad -= 1;
 92                 } else {
 93                     window.alert("Failed to load video!");
 94                 }
 95             }
 96         });
 97 
 98         var soundURL;
 99         if (soundDevice.isSupported("FILEFORMAT_OGG")) {
100             soundURL = mappingTable.getURL("sounds/turbulenzanimation.ogg");
101         } else {
102             soundURL = mappingTable.getURL("sounds/turbulenzanimation.mp3");
103         }
104 
105         soundDevice.createSound({
106             src: soundURL,
107             onload: function (s) {
108                 if (s) {
109                     sound = s;
110 
111                     assetsToLoad -= 1;
112                 } else {
113                     window.alert('Failed to load sound!');
114                 }
115             }
116         });
117 
118         function shaderLoaded(shaderText) {
119             if (shaderText) {
120                 var shaderParameters = JSON.parse(shaderText);
121                 shader = graphicsDevice.createShader(shaderParameters);
122 
123                 technique = shader.getTechnique("video");
124 
125                 assetsToLoad -= 1;
126             } else {
127                 window.alert("Failed to load shader!");
128             }
129         }
130 
131         requestHandler.request({
132             src: mappingTable.getURL("shaders/video.cgfx"),
133             onload: shaderLoaded
134         });
135     }
136 
137     var gameSession;
138     function sessionCreated() {
139         TurbulenzServices.createMappingTable(requestHandler, gameSession, mappingTableReceived);
140     }
141 
142     gameSession = TurbulenzServices.createGameSession(requestHandler, sessionCreated);
143 
144     //==========================================================================
145     // Main loop.
146     //==========================================================================
147     var fpsElement = document.getElementById("fpscounter");
148     var lastFPS = "";
149     var nextUpdate = 0;
150     function displayPerformance() {
151         var currentTime = TurbulenzEngine.time;
152         if (currentTime > nextUpdate) {
153             nextUpdate = (currentTime + 0.1);
154 
155             var fpsText = (graphicsDevice.fps).toFixed(2);
156             if (lastFPS !== fpsText) {
157                 lastFPS = fpsText;
158                 fpsElement.innerHTML = fpsText + " fps";
159             }
160         }
161     }
162 
163     function mainLoop() {
164         soundDevice.update();
165 
166         if (graphicsDevice.beginFrame()) {
167             var deviceWidth = graphicsDevice.width;
168             var deviceHeight = graphicsDevice.height;
169             var aspectRatio = (deviceWidth / deviceHeight);
170 
171             var videoWidth = video.width;
172             var videoHeight = video.height;
173             var videoAspectRatio = (videoWidth / videoHeight);
174 
175             var x, y;
176             if (aspectRatio < videoAspectRatio) {
177                 x = 1;
178                 y = aspectRatio / videoAspectRatio;
179             } else {
180                 x = videoAspectRatio / aspectRatio;
181                 y = 1;
182             }
183 
184             var currentVideoPosition = video.tell;
185             if (currentVideoPosition && videoPosition !== currentVideoPosition) {
186                 if (currentVideoPosition < videoPosition) {
187                     // looped, sync
188                     source.seek(videoPosition);
189                 }
190 
191                 videoPosition = currentVideoPosition;
192                 texture.setData(video);
193             }
194 
195             graphicsDevice.clear(clearColor);
196 
197             graphicsDevice.setTechnique(technique);
198             technique.texture = texture;
199             technique.clipSpace = mathDevice.v4Build(x, -y, 0, 0, clipSpace);
200             technique.color = videoColor;
201 
202             graphicsDevice.setStream(vertexBuffer, semantics);
203             graphicsDevice.draw(primitive, 4);
204 
205             graphicsDevice.endFrame();
206 
207             if (fpsElement) {
208                 displayPerformance();
209             }
210         }
211     }
212 
213     var intervalID;
214     function loadingLoop() {
215         if (assetsToLoad === 0) {
216             TurbulenzEngine.clearInterval(intervalID);
217 
218             source.play(sound);
219 
220             video.play();
221 
222             texture = graphicsDevice.createTexture({
223                 width: video.width,
224                 height: video.height,
225                 mipmaps: false,
226                 format: 'R8G8B8',
227                 dynamic: true,
228                 data: video
229             });
230 
231             videoPosition = video.tell;
232 
233             intervalID = TurbulenzEngine.setInterval(mainLoop, 1000 / 60);
234         }
235     }
236 
237     intervalID = TurbulenzEngine.setInterval(loadingLoop, 100);
238 
239     // Create a scene destroy callback to run when the window is closed
240     TurbulenzEngine.onunload = function destroyScene() {
241         TurbulenzEngine.clearInterval(intervalID);
242 
243         if (texture) {
244             texture.destroy();
245             texture = null;
246         }
247 
248         if (shader) {
249             shader.destroy();
250             technique = null;
251             shader = null;
252         }
253 
254         if (video) {
255             video.destroy();
256             video = null;
257         }
258 
259         if (vertexBuffer) {
260             vertexBuffer.destroy();
261             vertexBuffer = null;
262         }
263 
264         if (source) {
265             source.destroy();
266             source = null;
267         }
268 
269         if (sound) {
270             sound.destroy();
271             sound = null;
272         }
273 
274         fpsElement = null;
275 
276         if (gameSession) {
277             gameSession.destroy();
278             gameSession = null;
279         }
280     };
281 };