0d1d88bb by jrivera

Expanded mp4 test page

Added the ability to view both the transmuxed video and audio playback automatically
Supports segments with only one of the two tracks
1 parent ac2b2759
...@@ -45,6 +45,8 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio. ...@@ -45,6 +45,8 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio.
45 </pre> 45 </pre>
46 <small>Looking for the <a href="index.html">FLV tool</a>?</small> 46 <small>Looking for the <a href="index.html">FLV tool</a>?</small>
47 </header> 47 </header>
48 <section id="video-place">
49 </section>
48 <section> 50 <section>
49 <h2>Inputs</h2> 51 <h2>Inputs</h2>
50 <form id="inputs"> 52 <form id="inputs">
...@@ -116,18 +118,132 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio. ...@@ -116,18 +118,132 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio.
116 Hls: {} 118 Hls: {}
117 }; 119 };
118 </script> 120 </script>
119 <script src="../../src/stream.js"></script> 121 <script src="../../node_modules/videojs-contrib-media-sources/node_modules/mux.js/lib/stream.js"></script>
120 <script src="../../src/mp4-generator.js"></script> 122 <script src="../../node_modules/videojs-contrib-media-sources/node_modules/mux.js/lib/mp4-generator.js"></script>
121 <script src="../../src/transmuxer.js"></script> 123 <script src="../../node_modules/videojs-contrib-media-sources/node_modules/mux.js/lib/transmuxer.js"></script>
122 <script src="../../src/flv-tag.js"></script> 124 <script src="../../node_modules/videojs-contrib-media-sources/node_modules/mux.js/lib/mp4-inspector.js"></script>
123 <script src="../../src/exp-golomb.js"></script> 125 <script src="../../node_modules/videojs-contrib-media-sources/node_modules/mux.js/legacy/flv-tag.js"></script>
124 <script src="js/mp4-inspector.js"></script> 126 <script src="../../node_modules/videojs-contrib-media-sources/node_modules/mux.js/lib/exp-golomb.js"></script>
125 127
126 <script src="../../src/bin-utils.js"></script> 128 <script src="../../src/bin-utils.js"></script>
127 129
128 <!-- Include QUnit for object diffs --> 130 <!-- Include QUnit for object diffs -->
129 <script src="../../node_modules/qunitjs/qunit/qunit.js"></script> 131 <script src="../../node_modules/qunitjs/qunit/qunit.js"></script>
130 <script> 132 <script>
133 /*
134 MOSTLY STOLEN FROM https://w3c.github.io/media-source/#examples
135 */
136 function setupMSE (videoElement, getNextVideoSegment, getNextAudioSegment) {
137 function onSourceOpen(videoTag, e) {
138 var
139 initVideoSegment = getNextVideoSegment(),
140 initAudioSegment = getNextAudioSegment(),
141 numberInited = 0,
142 videoBuffer, audioBuffer,
143 mediaSource = e.target;
144
145 if (mediaSource.sourceBuffers.length > 0)
146 return;
147
148 if (initVideoSegment) {
149 videoBuffer = mediaSource.addSourceBuffer('video/mp4;codecs=avc1.4d401f');
150 }
151 if (initAudioSegment) {
152 audioBuffer = mediaSource.addSourceBuffer('audio/mp4;codecs=mp4a.40.2');
153 }
154
155 videoTag.addEventListener('progress', onProgress.bind(videoTag, mediaSource));
156
157 if (initVideoSegment == null && initAudioSegment == null) {
158 // Error fetching the initialization segment. Signal end of stream with an error.
159 mediaSource.endOfStream("network");
160 return;
161 }
162
163 // Append the initialization segment.
164 var firstAppendHandler = function(e) {
165 var sourceBuffer = e.target;
166 sourceBuffer.removeEventListener('updateend', firstAppendHandler);
167
168 // Append some initial media data.
169 if (++numberInited === 2) {
170 onProgress(mediaSource, e);
171 }
172 };
173
174 if (videoBuffer) {
175 videoBuffer.addEventListener('updateend', firstAppendHandler);
176 }
177 if (audioBuffer) {
178 audioBuffer.addEventListener('updateend', firstAppendHandler);
179 }
180
181 if (initVideoSegment) {
182 videoBuffer.appendBuffer(initVideoSegment);
183 }
184 if (initAudioSegment) {
185 audioBuffer.appendBuffer(initAudioSegment);
186 }
187 }
188
189 function appendNextMediaSegment(getNextMediaSegment, mediaSource, sourceBuffer) {
190 if (mediaSource.readyState == "closed") {
191 return;
192 }
193
194 var mediaSegment = getNextMediaSegment();
195 // If we have run out of stream data, then signal end of stream.
196 if (mediaSegment == null) {
197 // mediaSource.endOfStream("network");
198 return false;
199 }
200
201 // Make sure the previous append is not still pending.
202 if (sourceBuffer.updating) {
203 return false;
204 }
205
206 // NOTE: If mediaSource.readyState == “ended”, this appendBuffer() call will
207 // cause mediaSource.readyState to transition to "open". The web application
208 // should be prepared to handle multiple “sourceopen” events.
209 sourceBuffer.appendBuffer(mediaSegment);
210 return true;
211 }
212 /*
213 function onSeeking(mediaSource, e) {
214 var video = e.target;
215
216 if (mediaSource.readyState == "open") {
217 // Abort current segment append.
218 mediaSource.sourceBuffers[0].abort();
219 }
220
221 // Notify the media segment loading code to start fetching data at the
222 // new playback position.
223 SeekToMediaSegmentAt(video.currentTime);
224
225 // Append a media segment from the new playback position.
226 appendNextMediaSegment(mediaSource);
227 }
228 */
229 function onProgress(mediaSource, e) {
230 (appendNextMediaSegment(getNextVideoSegment, mediaSource, mediaSource.sourceBuffers[0]) &&
231 appendNextMediaSegment(getNextAudioSegment, mediaSource, mediaSource.sourceBuffers[1]));
232 }
233
234 var mediaSource = new MediaSource();
235 mediaSource.addEventListener('sourceopen', onSourceOpen.bind(this, videoElement));
236 videoElement.src = window.URL.createObjectURL(mediaSource);
237 }
238 function getSegment (segmentArray) {
239 var segment = segmentArray.shift();
240 if (segment) {
241 return segment.data;
242 }
243 return null;
244 }
245 </script>
246 <script>
131 var inputs = document.getElementById('inputs'), 247 var inputs = document.getElementById('inputs'),
132 original = document.getElementById('original'), 248 original = document.getElementById('original'),
133 working = document.getElementById('working'), 249 working = document.getElementById('working'),
...@@ -145,8 +261,9 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio. ...@@ -145,8 +261,9 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio.
145 workingOutput = document.querySelector('.working-output'), 261 workingOutput = document.querySelector('.working-output'),
146 262
147 video = document.createElement('video'), 263 video = document.createElement('video'),
148 mediaSource = new MediaSource(), 264 mediaSource = new MediaSource();
149 FlvTag = videojs.Hls.FlvTag; 265
266 document.querySelector('#video-place').appendChild(video);
150 267
151 logevent = function(event) { 268 logevent = function(event) {
152 console.log(event.type); 269 console.log(event.type);
...@@ -176,8 +293,8 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio. ...@@ -176,8 +293,8 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio.
176 'properties present in the working version but missing in the ' + 293 'properties present in the working version but missing in the ' +
177 'transmuxed output.</p>'; 294 'transmuxed output.</p>';
178 diff += '<pre class="mp4-diff">' + 295 diff += '<pre class="mp4-diff">' +
179 QUnit.diff(videojs.textifyMp4(transmuxed, null, ' '), 296 QUnit.diff(muxjs.textifyMp4(transmuxed, null, ' '),
180 videojs.textifyMp4(workingParsed, null, ' ')) + 297 muxjs.textifyMp4(workingParsed, null, ' ')) +
181 '</pre>'; 298 '</pre>';
182 299
183 comparison.innerHTML = diff; 300 comparison.innerHTML = diff;
...@@ -185,8 +302,8 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio. ...@@ -185,8 +302,8 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio.
185 302
186 mediaSource.addEventListener('sourceopen', function() { 303 mediaSource.addEventListener('sourceopen', function() {
187 var 304 var
188 // buffer = mediaSource.addSourceBuffer('video/mp4;codecs=avc1.4d400d'); 305 buffer = mediaSource.addSourceBuffer('video/mp4;codecs=avc1.4d400d');
189 buffer = mediaSource.addSourceBuffer('audio/mp4;codecs=mp4a.40.2'); 306 //buffer = mediaSource.addSourceBuffer('audio/mp4;codecs=mp4a.40.2');
190 buffer.addEventListener('updatestart', logevent); 307 buffer.addEventListener('updatestart', logevent);
191 buffer.addEventListener('updateend', logevent); 308 buffer.addEventListener('updateend', logevent);
192 buffer.addEventListener('error', logevent); 309 buffer.addEventListener('error', logevent);
...@@ -202,46 +319,61 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio. ...@@ -202,46 +319,61 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio.
202 video.addEventListener('error', console.log.bind(console)); 319 video.addEventListener('error', console.log.bind(console));
203 320
204 321
205 videojs.log = console.log.bind(console); 322 // muxjs.log = console.log.bind(console);
206 323
207 original.addEventListener('change', function() { 324 original.addEventListener('change', function() {
208 var reader = new FileReader(), 325 var reader = new FileReader(),
209 videoSegments= [],
210 audioSegments = [],
211 videoBuffer = [], 326 videoBuffer = [],
212 audioBuffer = []; 327 audioBuffer = [];
213 328
214 reader.addEventListener('loadend', function() { 329 reader.addEventListener('loadend', function() {
215 var segment = new Uint8Array(reader.result), 330 var segment = new Uint8Array(reader.result),
216 transmuxer = new videojs.mp2t.Transmuxer(), 331 transmuxer = new muxjs.mp2t.Transmuxer(),
217 events = [], 332 videoSegments = [],
218 bytesLength = 0, 333 audioSegments = [],
334 videoBytesLength = 0,
335 audioBytesLength = 0,
336 decodeMe,
219 bytes, 337 bytes,
220 i, j, 338 i, j,
221 hex = ''; 339 hex = '';
222 340
223 transmuxer.on('data', function(data) { 341 // transmux the MPEG-TS data to BMFF segments
224 if (data && data.type === 'audio') { 342 transmuxer.on('data', function(segment) {
225 events.push(data.data); 343 if (segment.type === 'video') {
226 bytesLength += data.data.byteLength; 344 videoSegments.push(segment);
345 videoBytesLength += segment.data.byteLength;
346 } else {
347 audioSegments.push(segment);
348 audioBytesLength += segment.data.byteLength;
227 } 349 }
228 }); 350 });
351
229 transmuxer.push(segment); 352 transmuxer.push(segment);
230 transmuxer.end(); 353 transmuxer.end();
354 // XXX - switch to select video/audio to show
355 decodeMe = videoSegments;
356 bytes = new Uint8Array(videoBytesLength);
231 357
232 bytes = new Uint8Array(bytesLength); 358 for (j = 0, i = 0; j < decodeMe.length; j++) {
233 for (j = 0, i = 0; j < events.length; j++) { 359 bytes.set(decodeMe[j].data, i);
234 bytes.set(events[j], i); 360 i += decodeMe[j].byteLength;
235 i += events[j].byteLength;
236 } 361 }
237 362
238 vjsBytes = bytes; 363 vjsBytes = bytes;
239 vjsParsed = videojs.inspectMp4(bytes); 364 vjsParsed = muxjs.inspectMp4(bytes);
240 console.log('transmuxed', vjsParsed); 365 console.log('transmuxed', vjsParsed);
241 diffParsed(); 366 diffParsed();
242 367
368 // XXX - set one of videoSegments or audioSegments below to an
369 // empty array to only test one stream
370
371 setupMSE(video,
372 getSegment.bind(null, videoSegments),
373 getSegment.bind(null, audioSegments));
374
243 // clear old box info 375 // clear old box info
244 vjsBoxes.innerHTML = videojs.textifyMp4(vjsParsed, null, ' '); 376 vjsBoxes.innerHTML = muxjs.textifyMp4(vjsParsed, null, ' ');
245 377
246 // write out the result 378 // write out the result
247 hex += '<pre>'; 379 hex += '<pre>';
...@@ -249,9 +381,9 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio. ...@@ -249,9 +381,9 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio.
249 hex += '</pre>'; 381 hex += '</pre>';
250 vjsOutput.innerHTML = hex; 382 vjsOutput.innerHTML = hex;
251 383
252 // XXX Media Sources Testing 384 video.play();
253 //window.vjsSourceBuffer.appendBuffer(bytes);
254 }); 385 });
386
255 reader.readAsArrayBuffer(this.files[0]); 387 reader.readAsArrayBuffer(this.files[0]);
256 }, false); 388 }, false);
257 389
...@@ -263,12 +395,12 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio. ...@@ -263,12 +395,12 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio.
263 395
264 396
265 workingBytes = bytes; 397 workingBytes = bytes;
266 workingParsed = videojs.inspectMp4(bytes); 398 workingParsed = muxjs.inspectMp4(bytes);
267 console.log('working', workingParsed); 399 console.log('working', workingParsed);
268 diffParsed(); 400 diffParsed();
269 401
270 // clear old box info 402 // clear old box info
271 workingBoxes.innerHTML = videojs.textifyMp4(workingParsed, null, ' '); 403 workingBoxes.innerHTML = muxjs.textifyMp4(workingParsed, null, ' ');
272 404
273 // output the hex dump 405 // output the hex dump
274 hex += '<pre>'; 406 hex += '<pre>';
...@@ -277,7 +409,10 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio. ...@@ -277,7 +409,10 @@ mp4fragment --track audio --fragment-duration 11000 movie-audio.mp4 movie-audio.
277 workingOutput.innerHTML = hex; 409 workingOutput.innerHTML = hex;
278 410
279 // XXX Media Sources Testing 411 // XXX Media Sources Testing
280 window.vjsSourceBuffer.appendBuffer(bytes); 412 /* setupMSE(video,
413 getSegment.bind(null, []),
414 getSegment.bind(null, [{data: bytes}]));*/
415 //window.vjsSourceBuffer.appendBuffer(bytes);
281 }); 416 });
282 reader.readAsArrayBuffer(this.files[0]); 417 reader.readAsArrayBuffer(this.files[0]);
283 }, false); 418 }, false);
......