Inspect and generate avcC, btrt, stsz, tkhd, and vmhd boxes
Add support for a number of new boxes in the generator and inspector. Bring some existing generated boxes more closely into line with a working init segment.
Showing
5 changed files
with
440 additions
and
111 deletions
... | @@ -2,7 +2,8 @@ | ... | @@ -2,7 +2,8 @@ |
2 | 'use strict'; | 2 | 'use strict'; |
3 | 3 | ||
4 | var box, dinf, ftyp, minf, moov, mvex, mvhd, trak, tkhd, mdia, mdhd, hdlr, stbl, | 4 | var box, dinf, ftyp, minf, moov, mvex, mvhd, trak, tkhd, mdia, mdhd, hdlr, stbl, |
5 | stsd, types, MAJOR_BRAND, MINOR_VERSION, VIDEO_HDLR, DREF, TREX, Uint8Array, DataView; | 5 | stsd, types, MAJOR_BRAND, MINOR_VERSION, VIDEO_HDLR, VMHD, DREF, STCO, STSC, STSZ, STTS, TREX, |
6 | Uint8Array, DataView; | ||
6 | 7 | ||
7 | Uint8Array = window.Uint8Array; | 8 | Uint8Array = window.Uint8Array; |
8 | DataView = window.DataView; | 9 | DataView = window.DataView; |
... | @@ -12,6 +13,8 @@ DataView = window.DataView; | ... | @@ -12,6 +13,8 @@ DataView = window.DataView; |
12 | var i; | 13 | var i; |
13 | types = { | 14 | types = { |
14 | avc1: [], // codingname | 15 | avc1: [], // codingname |
16 | avcC: [], | ||
17 | btrt: [], | ||
15 | dinf: [], | 18 | dinf: [], |
16 | dref: [], | 19 | dref: [], |
17 | ftyp: [], | 20 | ftyp: [], |
... | @@ -26,10 +29,12 @@ DataView = window.DataView; | ... | @@ -26,10 +29,12 @@ DataView = window.DataView; |
26 | stco: [], | 29 | stco: [], |
27 | stsc: [], | 30 | stsc: [], |
28 | stsd: [], | 31 | stsd: [], |
32 | stsz: [], | ||
29 | stts: [], | 33 | stts: [], |
30 | trak: [], | 34 | trak: [], |
31 | trex: [], | 35 | trex: [], |
32 | tkhd: [] | 36 | tkhd: [], |
37 | vmhd: [] | ||
33 | }; | 38 | }; |
34 | 39 | ||
35 | for (i in types) { | 40 | for (i in types) { |
... | @@ -65,7 +70,11 @@ DataView = window.DataView; | ... | @@ -65,7 +70,11 @@ DataView = window.DataView; |
65 | DREF = new Uint8Array([ | 70 | DREF = new Uint8Array([ |
66 | 0x00, // version 0 | 71 | 0x00, // version 0 |
67 | 0x00, 0x00, 0x00, // flags | 72 | 0x00, 0x00, 0x00, // flags |
68 | 0x00, 0x00, 0x00, 0x00 // entry_count | 73 | 0x00, 0x00, 0x00, 0x01, // entry_count |
74 | 0x00, 0x00, 0x00, 0x0c, // entry_size | ||
75 | 0x75, 0x72, 0x6c, 0x20, // 'url' type | ||
76 | 0x00, // version 0 | ||
77 | 0x00, 0x00, 0x01 // entry_flags | ||
69 | ]); | 78 | ]); |
70 | TREX = new Uint8Array([ | 79 | TREX = new Uint8Array([ |
71 | 0x00, // version 0 | 80 | 0x00, // version 0 |
... | @@ -76,6 +85,27 @@ DataView = window.DataView; | ... | @@ -76,6 +85,27 @@ DataView = window.DataView; |
76 | 0x00, 0x00, 0x00, 0x00, // default_sample_size | 85 | 0x00, 0x00, 0x00, 0x00, // default_sample_size |
77 | 0x00, 0x01, 0x00, 0x01 // default_sample_flags | 86 | 0x00, 0x01, 0x00, 0x01 // default_sample_flags |
78 | ]); | 87 | ]); |
88 | STCO = new Uint8Array([ | ||
89 | 0x00, // version | ||
90 | 0x00, 0x00, 0x00, // flags | ||
91 | 0x00, 0x00, 0x00, 0x00 // entry_count | ||
92 | ]); | ||
93 | STSC = STCO; | ||
94 | STSZ = new Uint8Array([ | ||
95 | 0x00, // version | ||
96 | 0x00, 0x00, 0x00, // flags | ||
97 | 0x00, 0x00, 0x00, 0x00, // sample_size | ||
98 | 0x00, 0x00, 0x00, 0x00, // sample_count | ||
99 | ]); | ||
100 | STTS = STCO; | ||
101 | VMHD = new Uint8Array([ | ||
102 | 0x00, // version | ||
103 | 0x00, 0x00, 0x00, // flags | ||
104 | 0x00, 0x00, // graphicsmode | ||
105 | 0x00, 0x00, | ||
106 | 0x00, 0x00, | ||
107 | 0x00, 0x00 // opcolor | ||
108 | ]); | ||
79 | })(); | 109 | })(); |
80 | 110 | ||
81 | box = function(type) { | 111 | box = function(type) { |
... | @@ -120,7 +150,8 @@ mdhd = function(duration) { | ... | @@ -120,7 +150,8 @@ mdhd = function(duration) { |
120 | 0x00, 0x00, 0x00, // flags | 150 | 0x00, 0x00, 0x00, // flags |
121 | 0x00, 0x00, 0x00, 0x02, // creation_time | 151 | 0x00, 0x00, 0x00, 0x02, // creation_time |
122 | 0x00, 0x00, 0x00, 0x03, // modification_time | 152 | 0x00, 0x00, 0x00, 0x03, // modification_time |
123 | 0x00, 0x00, 0x00, 0x3c, // timescale | 153 | 0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second |
154 | |||
124 | (duration & 0xFF000000) >> 24, | 155 | (duration & 0xFF000000) >> 24, |
125 | (duration & 0xFF0000) >> 16, | 156 | (duration & 0xFF0000) >> 16, |
126 | (duration & 0xFF00) >> 8, | 157 | (duration & 0xFF00) >> 8, |
... | @@ -133,7 +164,7 @@ mdia = function(duration, width, height) { | ... | @@ -133,7 +164,7 @@ mdia = function(duration, width, height) { |
133 | return box(types.mdia, mdhd(duration), hdlr(), minf(width, height)); | 164 | return box(types.mdia, mdhd(duration), hdlr(), minf(width, height)); |
134 | }; | 165 | }; |
135 | minf = function(width, height) { | 166 | minf = function(width, height) { |
136 | return box(types.minf, dinf(), stbl(width, height)); | 167 | return box(types.minf, box(types.vmhd, VMHD), dinf(), stbl(width, height)); |
137 | }; | 168 | }; |
138 | moov = function(duration, width, height) { | 169 | moov = function(duration, width, height) { |
139 | return box(types.moov, mvhd(duration), trak(duration, width, height), mvex()); | 170 | return box(types.moov, mvhd(duration), trak(duration, width, height), mvex()); |
... | @@ -148,7 +179,7 @@ mvhd = function(duration) { | ... | @@ -148,7 +179,7 @@ mvhd = function(duration) { |
148 | 0x00, 0x00, 0x00, // flags | 179 | 0x00, 0x00, 0x00, // flags |
149 | 0x00, 0x00, 0x00, 0x01, // creation_time | 180 | 0x00, 0x00, 0x00, 0x01, // creation_time |
150 | 0x00, 0x00, 0x00, 0x02, // modification_time | 181 | 0x00, 0x00, 0x00, 0x02, // modification_time |
151 | 0x00, 0x00, 0x00, 0x01, // timescale, 1 "tick" per second | 182 | 0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second |
152 | (duration & 0xFF000000) >> 24, | 183 | (duration & 0xFF000000) >> 24, |
153 | (duration & 0xFF0000) >> 16, | 184 | (duration & 0xFF0000) >> 16, |
154 | (duration & 0xFF00) >> 8, | 185 | (duration & 0xFF00) >> 8, |
... | @@ -181,9 +212,10 @@ mvhd = function(duration) { | ... | @@ -181,9 +212,10 @@ mvhd = function(duration) { |
181 | stbl = function(width, height) { | 212 | stbl = function(width, height) { |
182 | return box(types.stbl, | 213 | return box(types.stbl, |
183 | stsd(width, height), | 214 | stsd(width, height), |
184 | box(types.stts), | 215 | box(types.stts, STTS), |
185 | box(types.stsc), | 216 | box(types.stsc, STSC), |
186 | box(types.stco)); | 217 | box(types.stsz, STSZ), |
218 | box(types.stco, STCO)); | ||
187 | }; | 219 | }; |
188 | 220 | ||
189 | stsd = function(width, height) { | 221 | stsd = function(width, height) { |
... | @@ -218,7 +250,26 @@ stsd = function(width, height) { | ... | @@ -218,7 +250,26 @@ stsd = function(width, height) { |
218 | 0x00, 0x00, 0x00, 0x00, | 250 | 0x00, 0x00, 0x00, 0x00, |
219 | 0x00, 0x00, 0x00, // compressorname | 251 | 0x00, 0x00, 0x00, // compressorname |
220 | 0x00, 0x18, // depth = 24 | 252 | 0x00, 0x18, // depth = 24 |
221 | 0x11, 0x11]))); // pre_defined = -1 | 253 | 0x11, 0x11]), // pre_defined = -1 |
254 | box(types.avcC, new Uint8Array([ | ||
255 | 0x01, // configurationVersion | ||
256 | 0x4d, // AVCProfileIndication?? | ||
257 | 0x40, // profile_compatibility | ||
258 | 0x20, // AVCLevelIndication | ||
259 | 0xff, // lengthSizeMinusOne | ||
260 | 0xe1, // numOfSequenceParameterSets | ||
261 | 0x00, 0x0c, // sequenceParameterSetLength | ||
262 | 0x67, 0x4d, 0x40, 0x20, | ||
263 | 0x96, 0x52, 0x80, 0xa0, | ||
264 | 0x0b, 0x76, 0x02, 0x05, // SPS | ||
265 | 0x01, // numOfPictureParameterSets | ||
266 | 0x00, 0x04, // pictureParameterSetLength | ||
267 | 0x68, 0xef, 0x38, 0x80])), // "PPS" | ||
268 | box(types.btrt, new Uint8Array([ | ||
269 | 0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB | ||
270 | 0x00, 0x2d, 0xc6, 0xc0, // maxBitrate | ||
271 | 0x00, 0x2d, 0xc6, 0xc0])) // avgBitrate | ||
272 | )); | ||
222 | }; | 273 | }; |
223 | 274 | ||
224 | tkhd = function(duration, width, height) { | 275 | tkhd = function(duration, width, height) { |
... | @@ -248,14 +299,12 @@ tkhd = function(duration, width, height) { | ... | @@ -248,14 +299,12 @@ tkhd = function(duration, width, height) { |
248 | 0x00, 0x00, 0x00, 0x00, | 299 | 0x00, 0x00, 0x00, 0x00, |
249 | 0x00, 0x00, 0x00, 0x00, | 300 | 0x00, 0x00, 0x00, 0x00, |
250 | 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix | 301 | 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix |
251 | (width & 0xFF000000) >> 24, | ||
252 | (width & 0xFF0000) >> 16, | ||
253 | (width & 0xFF00) >> 8, | 302 | (width & 0xFF00) >> 8, |
254 | width & 0xFF, // width | 303 | width & 0xFF, |
255 | (height & 0xFF000000) >> 24, | 304 | 0x00, 0x00, // width |
256 | (height & 0xFF0000) >> 16, | ||
257 | (height & 0xFF00) >> 8, | 305 | (height & 0xFF00) >> 8, |
258 | height & 0xFF // height | 306 | height & 0xFF, |
307 | 0x00, 0x00 // height | ||
259 | ])); | 308 | ])); |
260 | }; | 309 | }; |
261 | 310 | ||
... | @@ -269,7 +318,7 @@ window.videojs.mp4 = { | ... | @@ -269,7 +318,7 @@ window.videojs.mp4 = { |
269 | initSegment: function() { | 318 | initSegment: function() { |
270 | var | 319 | var |
271 | fileType = ftyp(), | 320 | fileType = ftyp(), |
272 | movie = moov(1, 1280, 720), | 321 | movie = moov(0xffffffff, 1280, 720), |
273 | result = new Uint8Array(fileType.byteLength + movie.byteLength); | 322 | result = new Uint8Array(fileType.byteLength + movie.byteLength); |
274 | 323 | ||
275 | result.set(fileType); | 324 | result.set(fileType); | ... | ... |
... | @@ -78,29 +78,48 @@ test('generates a moov', function() { | ... | @@ -78,29 +78,48 @@ test('generates a moov', function() { |
78 | 78 | ||
79 | minf = boxes[0].boxes[1].boxes[1].boxes[2]; | 79 | minf = boxes[0].boxes[1].boxes[1].boxes[2]; |
80 | equal(minf.type, 'minf', 'generate an minf type'); | 80 | equal(minf.type, 'minf', 'generate an minf type'); |
81 | equal(minf.boxes.length, 2, 'generates two minf sub boxes'); | 81 | equal(minf.boxes.length, 3, 'generates three minf sub boxes'); |
82 | |||
83 | equal(minf.boxes[0].type, 'vmhd', 'generates a vmhd type'); | ||
84 | deepEqual({ | ||
85 | type: 'vmhd', | ||
86 | size: 20, | ||
87 | version: 0, | ||
88 | flags: new Uint8Array([0, 0, 0]), | ||
89 | graphicsmode: 0, | ||
90 | opcolor: new Uint16Array([0, 0, 0]) | ||
91 | }, minf.boxes[0], 'generates a vhmd'); | ||
92 | |||
93 | equal(minf.boxes[1].type, 'dinf', 'generates a dinf type'); | ||
82 | deepEqual({ | 94 | deepEqual({ |
83 | type: 'dinf', | 95 | type: 'dinf', |
84 | size: 24, | 96 | size: 36, |
85 | boxes: [{ | 97 | boxes: [{ |
86 | type: 'dref', | 98 | type: 'dref', |
87 | size: 16, | 99 | size: 28, |
88 | version: 0, | 100 | version: 0, |
89 | flags: new Uint8Array([0, 0, 0]), | 101 | flags: new Uint8Array([0, 0, 0]), |
90 | dataReferences: [] | 102 | dataReferences: [{ |
103 | type: 'url ', | ||
104 | size: 12, | ||
105 | version: 0, | ||
106 | flags: new Uint8Array([0, 0, 1]) | ||
107 | }] | ||
91 | }] | 108 | }] |
92 | }, minf.boxes[0], 'generates a dinf'); | 109 | }, minf.boxes[1], 'generates a dinf'); |
93 | 110 | ||
94 | equal(minf.boxes[1].type, 'stbl', 'generates an stbl type'); | 111 | equal(minf.boxes[2].type, 'stbl', 'generates an stbl type'); |
95 | deepEqual({ | 112 | deepEqual({ |
96 | type: 'stbl', | 113 | type: 'stbl', |
97 | size: 134, | 114 | size: 233, |
98 | boxes: [{ | 115 | boxes: [{ |
99 | type: 'stsd', | 116 | type: 'stsd', |
100 | size: 102, | 117 | size: 157, |
101 | version: 0, | 118 | version: 0, |
102 | flags: new Uint8Array([0, 0, 0]), | 119 | flags: new Uint8Array([0, 0, 0]), |
103 | sampleDescriptions: [{ | 120 | sampleDescriptions: [{ |
121 | type: 'avc1', | ||
122 | size: 141, | ||
104 | dataReferenceIndex: 1, | 123 | dataReferenceIndex: 1, |
105 | width: 600, | 124 | width: 600, |
106 | height: 300, | 125 | height: 300, |
... | @@ -108,23 +127,57 @@ test('generates a moov', function() { | ... | @@ -108,23 +127,57 @@ test('generates a moov', function() { |
108 | vertresolution: 72, | 127 | vertresolution: 72, |
109 | frameCount: 1, | 128 | frameCount: 1, |
110 | depth: 24, | 129 | depth: 24, |
111 | size: 86, | 130 | config: [{ |
112 | type: 'avc1' | 131 | type: 'avcC', |
132 | size: 35, | ||
133 | configurationVersion: 1, | ||
134 | avcProfileIndication: 0x4d, | ||
135 | profileCompatibility: 0x40, | ||
136 | avcLevelIndication: 0x20, | ||
137 | lengthSizeMinusOne: 3, | ||
138 | sps: [new Uint8Array([ | ||
139 | 0x67, 0x4d, 0x40, 0x20, | ||
140 | 0x96, 0x52, 0x80, 0xa0, | ||
141 | 0x0b, 0x76, 0x02, 0x05 | ||
142 | ])], | ||
143 | pps: [new Uint8Array([ | ||
144 | 0x68, 0xef, 0x38, 0x80 | ||
145 | ])] | ||
146 | }, { | ||
147 | type: 'btrt', | ||
148 | size: 20, | ||
149 | bufferSizeDB: 1875072, | ||
150 | maxBitrate: 3000000, | ||
151 | avgBitrate: 3000000 | ||
152 | }] | ||
113 | }] | 153 | }] |
114 | }, { | 154 | }, { |
115 | type: 'stts', | 155 | type: 'stts', |
116 | size: 8, | 156 | size: 16, |
157 | version: 0, | ||
158 | flags: new Uint8Array([0, 0, 0]), | ||
117 | timeToSamples: [] | 159 | timeToSamples: [] |
118 | }, { | 160 | }, { |
119 | type: 'stsc', | 161 | type: 'stsc', |
120 | size: 8, | 162 | size: 16, |
163 | version: 0, | ||
164 | flags: new Uint8Array([0, 0, 0]), | ||
121 | sampleToChunks: [] | 165 | sampleToChunks: [] |
122 | }, { | 166 | }, { |
167 | type: 'stsz', | ||
168 | version: 0, | ||
169 | size: 20, | ||
170 | flags: new Uint8Array([0, 0, 0]), | ||
171 | sampleSize: 0, | ||
172 | entries: [] | ||
173 | }, { | ||
123 | type: 'stco', | 174 | type: 'stco', |
124 | size: 8, | 175 | size: 16, |
176 | version: 0, | ||
177 | flags: new Uint8Array([0, 0, 0]), | ||
125 | chunkOffsets: [] | 178 | chunkOffsets: [] |
126 | }] | 179 | }] |
127 | }, minf.boxes[1], 'generates a stbl'); | 180 | }, minf.boxes[2], 'generates a stbl'); |
128 | 181 | ||
129 | 182 | ||
130 | mvex = boxes[0].boxes[2]; | 183 | mvex = boxes[0].boxes[2]; |
... | @@ -160,6 +213,7 @@ test('generates an initialization segment', function() { | ... | @@ -160,6 +213,7 @@ test('generates an initialization segment', function() { |
160 | equal(init.length, 2, 'generated two boxes'); | 213 | equal(init.length, 2, 'generated two boxes'); |
161 | equal(init[0].type, 'ftyp', 'generated a ftyp box'); | 214 | equal(init[0].type, 'ftyp', 'generated a ftyp box'); |
162 | equal(init[1].type, 'moov', 'generated a moov box'); | 215 | equal(init[1].type, 'moov', 'generated a moov box'); |
216 | equal(init[1].boxes[0].duration, 0xffffffff, 'wrote a maximum duration'); | ||
163 | }); | 217 | }); |
164 | 218 | ||
165 | 219 | ... | ... |
... | @@ -105,8 +105,8 @@ var | ... | @@ -105,8 +105,8 @@ var |
105 | 0x00, 0x00, // non-audio track volume | 105 | 0x00, 0x00, // non-audio track volume |
106 | 0x00, 0x00, // reserved | 106 | 0x00, 0x00, // reserved |
107 | unityMatrix, | 107 | unityMatrix, |
108 | 0x00, 0x00, 0x01, 0x2c, // 300 = 0x12c width | 108 | 0x01, 0x2c, 0x00, 0x00, // 300 in 16.16 fixed point |
109 | 0x00, 0x00, 0x00, 0x96), // 150 = 0x96 height | 109 | 0x00, 0x96, 0x00, 0x00), // 150 in 16.16 fixed point |
110 | mdhd0 = box('mdhd', | 110 | mdhd0 = box('mdhd', |
111 | 0x00, // version 0 | 111 | 0x00, // version 0 |
112 | 0x00, 0x00, 0x00, // flags | 112 | 0x00, 0x00, 0x00, // flags |
... | @@ -201,8 +201,8 @@ test('can parse a version 0 mvhd', function() { | ... | @@ -201,8 +201,8 @@ test('can parse a version 0 mvhd', function() { |
201 | type: 'mvhd', | 201 | type: 'mvhd', |
202 | version: 0, | 202 | version: 0, |
203 | flags: new Uint8Array([0, 0, 0]), | 203 | flags: new Uint8Array([0, 0, 0]), |
204 | creationTime: 1, | 204 | creationTime: new Date(1000 - 2082844800000), |
205 | modificationTime: 2, | 205 | modificationTime: new Date(2000 - 2082844800000), |
206 | timescale: 60, | 206 | timescale: 60, |
207 | duration: 600, | 207 | duration: 600, |
208 | rate: 1, | 208 | rate: 1, |
... | @@ -218,8 +218,8 @@ test('can parse a version 0 tkhd', function() { | ... | @@ -218,8 +218,8 @@ test('can parse a version 0 tkhd', function() { |
218 | type: 'tkhd', | 218 | type: 'tkhd', |
219 | version: 0, | 219 | version: 0, |
220 | flags: new Uint8Array([0, 0, 0]), | 220 | flags: new Uint8Array([0, 0, 0]), |
221 | creationTime: 2, | 221 | creationTime: new Date(2000 - 2082844800000), |
222 | modificationTime: 3, | 222 | modificationTime: new Date(3000 - 2082844800000), |
223 | size: 92, | 223 | size: 92, |
224 | trackId: 1, | 224 | trackId: 1, |
225 | duration: 600, | 225 | duration: 600, |
... | @@ -237,8 +237,8 @@ test('can parse a version 0 mdhd', function() { | ... | @@ -237,8 +237,8 @@ test('can parse a version 0 mdhd', function() { |
237 | type: 'mdhd', | 237 | type: 'mdhd', |
238 | version: 0, | 238 | version: 0, |
239 | flags: new Uint8Array([0, 0, 0]), | 239 | flags: new Uint8Array([0, 0, 0]), |
240 | creationTime: 2, | 240 | creationTime: new Date(2000 - 2082844800000), |
241 | modificationTime: 3, | 241 | modificationTime: new Date(3000 - 2082844800000), |
242 | size: 32, | 242 | size: 32, |
243 | timescale: 60, | 243 | timescale: 60, |
244 | duration: 600, | 244 | duration: 600, |
... | @@ -291,8 +291,8 @@ test('can parse a moov', function() { | ... | @@ -291,8 +291,8 @@ test('can parse a moov', function() { |
291 | 0x00, 0x00, // non-audio track volume | 291 | 0x00, 0x00, // non-audio track volume |
292 | 0x00, 0x00, // reserved | 292 | 0x00, 0x00, // reserved |
293 | unityMatrix, | 293 | unityMatrix, |
294 | 0x00, 0x00, 0x01, 0x2c, // 300 = 0x12c width | 294 | 0x01, 0x2c, 0x00, 0x00, // 300 in 16.16 fixed-point |
295 | 0x00, 0x00, 0x00, 0x96), // 150 = 0x96 height | 295 | 0x00, 0x96, 0x00, 0x00), // 150 in 16.16 fixed-point |
296 | box('mdia', | 296 | box('mdia', |
297 | box('mdhd', | 297 | box('mdhd', |
298 | 0x01, // version 1 | 298 | 0x01, // version 1 |
... | @@ -320,7 +320,10 @@ test('can parse a moov', function() { | ... | @@ -320,7 +320,10 @@ test('can parse a moov', function() { |
320 | box('dref', | 320 | box('dref', |
321 | 0x01, // version 1 | 321 | 0x01, // version 1 |
322 | 0x00, 0x00, 0x00, // flags | 322 | 0x00, 0x00, 0x00, // flags |
323 | 0x00, 0x00, 0x00, 0x00)), // entry_count | 323 | 0x00, 0x00, 0x00, 0x01, // entry_count |
324 | box('url ', | ||
325 | 0x00, // version | ||
326 | 0x00, 0x00, 0x01))), // flags | ||
324 | box('stbl', | 327 | box('stbl', |
325 | box('stsd', | 328 | box('stsd', |
326 | 0x01, // version 1 | 329 | 0x01, // version 1 |
... | @@ -329,25 +332,32 @@ test('can parse a moov', function() { | ... | @@ -329,25 +332,32 @@ test('can parse a moov', function() { |
329 | box('stts', | 332 | box('stts', |
330 | 0x01, // version 1 | 333 | 0x01, // version 1 |
331 | 0x00, 0x00, 0x00, // flags | 334 | 0x00, 0x00, 0x00, // flags |
332 | 0x00, 0x00, 0x00, 0x00), // entry_count | 335 | 0x00, 0x00, 0x00, 0x01, // entry_count |
336 | 0x00, 0x00, 0x00, 0x01, // sample_count | ||
337 | 0x00, 0x00, 0x00, 0x01), // sample_delta | ||
333 | box('stsc', | 338 | box('stsc', |
334 | 0x01, // version 1 | 339 | 0x01, // version 1 |
335 | 0x00, 0x00, 0x00, // flags | 340 | 0x00, 0x00, 0x00, // flags |
336 | 0x00, 0x00, 0x00, 0x00), // entry_count | 341 | 0x00, 0x00, 0x00, 0x01, // entry_count |
342 | 0x00, 0x00, 0x00, 0x02, // first_chunk | ||
343 | 0x00, 0x00, 0x00, 0x03, // samples_per_chunk | ||
344 | 0x00, 0x00, 0x00, 0x01), // sample_description_index | ||
337 | box('stco', | 345 | box('stco', |
338 | 0x01, // version 1 | 346 | 0x01, // version 1 |
339 | 0x00, 0x00, 0x00, // flags | 347 | 0x00, 0x00, 0x00, // flags |
340 | 0x00, 0x00, 0x00, 0x00)))))); // entry_count; | 348 | 0x00, 0x00, 0x00, 0x01, // entry_count |
349 | 0x00, 0x00, 0x00, 0x01)))))); // chunk_offset | ||
350 | |||
341 | 351 | ||
342 | deepEqual(videojs.inspectMp4(new Uint8Array(data)), [{ | 352 | deepEqual(videojs.inspectMp4(new Uint8Array(data)), [{ |
343 | size: 433, | ||
344 | type: 'moov', | 353 | type: 'moov', |
354 | size: 469, | ||
345 | boxes: [{ | 355 | boxes: [{ |
346 | type: 'mvhd', | 356 | type: 'mvhd', |
347 | version: 1, | 357 | version: 1, |
348 | flags: new Uint8Array([0, 0, 0]), | 358 | flags: new Uint8Array([0, 0, 0]), |
349 | creationTime: 1, | 359 | creationTime: new Date(1000 - 2082844800000), |
350 | modificationTime: 2, | 360 | modificationTime: new Date(2000 - 2082844800000), |
351 | timescale: 60, | 361 | timescale: 60, |
352 | duration: 600, | 362 | duration: 600, |
353 | rate: 1, | 363 | rate: 1, |
... | @@ -357,13 +367,13 @@ test('can parse a moov', function() { | ... | @@ -357,13 +367,13 @@ test('can parse a moov', function() { |
357 | nextTrackId: 2 | 367 | nextTrackId: 2 |
358 | }, { | 368 | }, { |
359 | type: 'trak', | 369 | type: 'trak', |
360 | size: 305, | 370 | size: 341, |
361 | boxes: [{ | 371 | boxes: [{ |
362 | type: 'tkhd', | 372 | type: 'tkhd', |
363 | flags: new Uint8Array([0, 0, 0]), | 373 | flags: new Uint8Array([0, 0, 0]), |
364 | version: 1, | 374 | version: 1, |
365 | creationTime: 2, | 375 | creationTime: new Date(2000 - 2082844800000), |
366 | modificationTime: 3, | 376 | modificationTime: new Date(3000 - 2082844800000), |
367 | size: 104, | 377 | size: 104, |
368 | trackId: 1, | 378 | trackId: 1, |
369 | duration: 600, | 379 | duration: 600, |
... | @@ -375,13 +385,13 @@ test('can parse a moov', function() { | ... | @@ -375,13 +385,13 @@ test('can parse a moov', function() { |
375 | height: 150 | 385 | height: 150 |
376 | }, { | 386 | }, { |
377 | type: 'mdia', | 387 | type: 'mdia', |
378 | size: 193, | 388 | size: 229, |
379 | boxes: [{ | 389 | boxes: [{ |
380 | type: 'mdhd', | 390 | type: 'mdhd', |
381 | version: 1, | 391 | version: 1, |
382 | flags: new Uint8Array([0, 0, 0]), | 392 | flags: new Uint8Array([0, 0, 0]), |
383 | creationTime: 2, | 393 | creationTime: new Date(2000 - 2082844800000), |
384 | modificationTime: 3, | 394 | modificationTime: new Date(3000 - 2082844800000), |
385 | timescale: 60, | 395 | timescale: 60, |
386 | duration: 600, | 396 | duration: 600, |
387 | language: 'eng', | 397 | language: 'eng', |
... | @@ -395,20 +405,25 @@ test('can parse a moov', function() { | ... | @@ -395,20 +405,25 @@ test('can parse a moov', function() { |
395 | size: 37 | 405 | size: 37 |
396 | }, { | 406 | }, { |
397 | type: 'minf', | 407 | type: 'minf', |
398 | size: 104, | 408 | size: 140, |
399 | boxes: [{ | 409 | boxes: [{ |
400 | type: 'dinf', | 410 | type: 'dinf', |
401 | size: 24, | 411 | size: 36, |
402 | boxes: [{ | 412 | boxes: [{ |
403 | type: 'dref', | 413 | type: 'dref', |
414 | size: 28, | ||
404 | version: 1, | 415 | version: 1, |
405 | flags: new Uint8Array([0, 0, 0]), | 416 | flags: new Uint8Array([0, 0, 0]), |
406 | dataReferences: [], | 417 | dataReferences: [{ |
407 | size: 16 | 418 | type: 'url ', |
419 | size: 12, | ||
420 | version: 0, | ||
421 | flags: new Uint8Array([0, 0, 1]) | ||
422 | }], | ||
408 | }] | 423 | }] |
409 | }, { | 424 | }, { |
410 | type: 'stbl', | 425 | type: 'stbl', |
411 | size: 72, | 426 | size: 96, |
412 | boxes: [{ | 427 | boxes: [{ |
413 | type: 'stsd', | 428 | type: 'stsd', |
414 | size: 16, | 429 | size: 16, |
... | @@ -417,16 +432,29 @@ test('can parse a moov', function() { | ... | @@ -417,16 +432,29 @@ test('can parse a moov', function() { |
417 | sampleDescriptions: [], | 432 | sampleDescriptions: [], |
418 | }, { | 433 | }, { |
419 | type: 'stts', | 434 | type: 'stts', |
420 | timeToSamples: [], | 435 | size: 24, |
421 | size: 16 | 436 | version: 1, |
437 | flags: new Uint8Array([0, 0, 0]), | ||
438 | timeToSamples: [{ | ||
439 | sampleCount: 1, | ||
440 | sampleDelta: 1 | ||
441 | }] | ||
422 | }, { | 442 | }, { |
423 | type: 'stsc', | 443 | type: 'stsc', |
424 | sampleToChunks: [], | 444 | version: 1, |
425 | size: 16 | 445 | flags: new Uint8Array([0, 0, 0]), |
446 | sampleToChunks: [{ | ||
447 | firstChunk: 2, | ||
448 | samplesPerChunk: 3, | ||
449 | sampleDescriptionIndex: 1 | ||
450 | }], | ||
451 | size: 28 | ||
426 | }, { | 452 | }, { |
427 | type: 'stco', | 453 | type: 'stco', |
428 | chunkOffsets: [], | 454 | size: 20, |
429 | size: 16 | 455 | version: 1, |
456 | flags: new Uint8Array([0, 0, 0]), | ||
457 | chunkOffsets: [1] | ||
430 | }] | 458 | }] |
431 | }] | 459 | }] |
432 | }] | 460 | }] |
... | @@ -498,23 +526,99 @@ test('can parse a video stsd', function() { | ... | @@ -498,23 +526,99 @@ test('can parse a video stsd', function() { |
498 | 0x00, 0x00, 0x00, 0x00, | 526 | 0x00, 0x00, 0x00, 0x00, |
499 | 0x00, 0x00, 0x00, // compressorname | 527 | 0x00, 0x00, 0x00, // compressorname |
500 | 0x00, 0x18, // depth = 24 | 528 | 0x00, 0x18, // depth = 24 |
501 | 0x11, 0x11)); // pre_defined | 529 | 0x11, 0x11, // pre_defined |
530 | box('avcC', | ||
531 | 0x01, // configurationVersion | ||
532 | 0x00, // AVCProfileIndication?? | ||
533 | 0x00, // profile_compatibility | ||
534 | 0x00, // AVCLevelIndication | ||
535 | 0x1c, // lengthSizeMinusOne | ||
536 | 0xe1, // numOfSequenceParameterSets | ||
537 | 0x00, 0x01, // sequenceParameterSetLength | ||
538 | 0x00, // "SPS" | ||
539 | 0x02, // numOfPictureParameterSets | ||
540 | 0x00, 0x02, // pictureParameterSetLength | ||
541 | 0x01, 0x02, // "PPS" | ||
542 | 0x00, 0x01, // pictureParameterSetLength | ||
543 | 0xff), // "PPS" | ||
544 | box('btrt', | ||
545 | 0x00, 0x00, 0x00, 0x00, // bufferSizeDB | ||
546 | 0x00, 0x00, 0x00, 0x01, // maxBitrate | ||
547 | 0x00, 0x00, 0x00, 0x01))); // avgBitrate | ||
502 | deepEqual(videojs.inspectMp4(new Uint8Array(data)), [{ | 548 | deepEqual(videojs.inspectMp4(new Uint8Array(data)), [{ |
503 | type: 'stsd', | 549 | type: 'stsd', |
504 | size: 102, | 550 | size: 147, |
505 | version: 0, | 551 | version: 0, |
506 | flags: new Uint8Array([0, 0, 0]), | 552 | flags: new Uint8Array([0, 0, 0]), |
507 | sampleDescriptions: [{ | 553 | sampleDescriptions: [{ |
508 | type: 'avc1', | 554 | type: 'avc1', |
509 | size: 86, | 555 | size: 131, |
510 | dataReferenceIndex: 1, | 556 | dataReferenceIndex: 1, |
511 | width: 300, | 557 | width: 300, |
512 | height: 150, | 558 | height: 150, |
513 | horizresolution: 72, | 559 | horizresolution: 72, |
514 | vertresolution: 72, | 560 | vertresolution: 72, |
515 | frameCount: 1, | 561 | frameCount: 1, |
516 | depth: 24 | 562 | depth: 24, |
563 | config: [{ | ||
564 | type: 'avcC', | ||
565 | size: 25, | ||
566 | configurationVersion: 1, | ||
567 | avcProfileIndication: 0, | ||
568 | profileCompatibility: 0, | ||
569 | avcLevelIndication: 0, | ||
570 | lengthSizeMinusOne: 0, | ||
571 | sps: [new Uint8Array(1)], | ||
572 | pps: [new Uint8Array([1, 2]), | ||
573 | new Uint8Array([0xff])] | ||
574 | }, { | ||
575 | type: 'btrt', | ||
576 | size: 20, | ||
577 | bufferSizeDB: 0, | ||
578 | maxBitrate: 1, | ||
579 | avgBitrate: 1 | ||
517 | }] | 580 | }] |
581 | }] | ||
582 | }]); | ||
583 | }); | ||
584 | |||
585 | test('can parse a vmhd', function() { | ||
586 | var data = box('vmhd', | ||
587 | 0x00, // version | ||
588 | 0x00, 0x00, 0x00, // flags | ||
589 | 0x00, 0x00, // graphicsmode | ||
590 | 0x00, 0x00, | ||
591 | 0x00, 0x00, | ||
592 | 0x00, 0x00); // opcolor | ||
593 | |||
594 | deepEqual(videojs.inspectMp4(new Uint8Array(data)), | ||
595 | [{ | ||
596 | type: 'vmhd', | ||
597 | size: 20, | ||
598 | version: 0, | ||
599 | flags: new Uint8Array([0, 0, 0]), | ||
600 | graphicsmode: 0, | ||
601 | opcolor: new Uint16Array([0, 0, 0]) | ||
602 | }]); | ||
603 | }); | ||
604 | |||
605 | test('can parse an stsz', function() { | ||
606 | var data = box('stsz', | ||
607 | 0x00, // version | ||
608 | 0x00, 0x00, 0x00, // flags | ||
609 | 0x00, 0x00, 0x00, 0x00, // sample_size | ||
610 | 0x00, 0x00, 0x00, 0x03, // sample_count | ||
611 | 0x00, 0x00, 0x00, 0x01, // entry_size | ||
612 | 0x00, 0x00, 0x00, 0x02, // entry_size | ||
613 | 0x00, 0x00, 0x00, 0x03); // entry_size | ||
614 | deepEqual(videojs.inspectMp4(new Uint8Array(data)), | ||
615 | [{ | ||
616 | type: 'stsz', | ||
617 | size: 32, | ||
618 | version: 0, | ||
619 | flags: new Uint8Array([0, 0, 0]), | ||
620 | sampleSize: 0, | ||
621 | entries: [1, 2, 3] | ||
518 | }]); | 622 | }]); |
519 | }); | 623 | }); |
520 | 624 | ... | ... |
... | @@ -16,6 +16,9 @@ var | ... | @@ -16,6 +16,9 @@ var |
16 | result += String.fromCharCode(buffer[3]); | 16 | result += String.fromCharCode(buffer[3]); |
17 | return result; | 17 | return result; |
18 | }, | 18 | }, |
19 | parseMp4Date = function(seconds) { | ||
20 | return new Date(seconds * 1000 - 2082844800000); | ||
21 | }, | ||
19 | 22 | ||
20 | // registry of handlers for individual mp4 box types | 23 | // registry of handlers for individual mp4 box types |
21 | parse = { | 24 | parse = { |
... | @@ -31,7 +34,53 @@ var | ... | @@ -31,7 +34,53 @@ var |
31 | horizresolution: view.getUint16(28) + (view.getUint16(30) / 16), | 34 | horizresolution: view.getUint16(28) + (view.getUint16(30) / 16), |
32 | vertresolution: view.getUint16(32) + (view.getUint16(34) / 16), | 35 | vertresolution: view.getUint16(32) + (view.getUint16(34) / 16), |
33 | frameCount: view.getUint16(40), | 36 | frameCount: view.getUint16(40), |
34 | depth: view.getUint16(74) | 37 | depth: view.getUint16(74), |
38 | config: videojs.inspectMp4(data.subarray(78, data.byteLength)) | ||
39 | }; | ||
40 | }, | ||
41 | avcC: function(data) { | ||
42 | var | ||
43 | view = new DataView(data.buffer, data.byteOffset, data.byteLength), | ||
44 | result = { | ||
45 | configurationVersion: data[0], | ||
46 | avcProfileIndication: data[1], | ||
47 | profileCompatibility: data[2], | ||
48 | avcLevelIndication: data[3], | ||
49 | lengthSizeMinusOne: data[4] & 0x03, | ||
50 | sps: [], | ||
51 | pps: [] | ||
52 | }, | ||
53 | numOfSequenceParameterSets = data[5] & 0x1f, | ||
54 | numOfPictureParameterSets, | ||
55 | nalSize, | ||
56 | offset, | ||
57 | i; | ||
58 | |||
59 | // iterate past any SPSs | ||
60 | offset = 6; | ||
61 | for (i = 0; i < numOfSequenceParameterSets; i++) { | ||
62 | nalSize = view.getUint16(offset); | ||
63 | offset += 2; | ||
64 | result.sps.push(data.subarray(offset, offset + nalSize)); | ||
65 | offset += nalSize; | ||
66 | } | ||
67 | // iterate past any PPSs | ||
68 | numOfPictureParameterSets = data[offset]; | ||
69 | offset++; | ||
70 | for (i = 0; i < numOfPictureParameterSets; i++) { | ||
71 | nalSize = view.getUint16(offset); | ||
72 | offset += 2; | ||
73 | result.pps.push(data.subarray(offset, offset + nalSize)); | ||
74 | offset += nalSize; | ||
75 | } | ||
76 | return result; | ||
77 | }, | ||
78 | btrt: function(data) { | ||
79 | var view = new DataView(data.buffer, data.byteOffset, data.byteLength); | ||
80 | return { | ||
81 | bufferSizeDB: view.getUint32(0), | ||
82 | maxBitrate: view.getUint32(4), | ||
83 | avgBitrate: view.getUint32(8) | ||
35 | }; | 84 | }; |
36 | }, | 85 | }, |
37 | ftyp: function(data) { | 86 | ftyp: function(data) { |
... | @@ -58,7 +107,7 @@ var | ... | @@ -58,7 +107,7 @@ var |
58 | return { | 107 | return { |
59 | version: data[0], | 108 | version: data[0], |
60 | flags: new Uint8Array(data.subarray(1, 4)), | 109 | flags: new Uint8Array(data.subarray(1, 4)), |
61 | dataReferences: [] | 110 | dataReferences: videojs.inspectMp4(data.subarray(8)) |
62 | }; | 111 | }; |
63 | }, | 112 | }, |
64 | hdlr: function(data) { | 113 | hdlr: function(data) { |
... | @@ -105,17 +154,17 @@ var | ... | @@ -105,17 +154,17 @@ var |
105 | }; | 154 | }; |
106 | if (result.version === 1) { | 155 | if (result.version === 1) { |
107 | i += 4; | 156 | i += 4; |
108 | result.creationTime = view.getUint32(i); // truncating top 4 bytes | 157 | result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes |
109 | i += 8; | 158 | i += 8; |
110 | result.modificationTime = view.getUint32(i); // truncating top 4 bytes | 159 | result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes |
111 | i += 4; | 160 | i += 4; |
112 | result.timescale = view.getUint32(i); | 161 | result.timescale = view.getUint32(i); |
113 | i += 8; | 162 | i += 8; |
114 | result.duration = view.getUint32(i); // truncating top 4 bytes | 163 | result.duration = view.getUint32(i); // truncating top 4 bytes |
115 | } else { | 164 | } else { |
116 | result.creationTime = view.getUint32(i); | 165 | result.creationTime = parseMp4Date(view.getUint32(i)); |
117 | i += 4; | 166 | i += 4; |
118 | result.modificationTime = view.getUint32(i); | 167 | result.modificationTime = parseMp4Date(view.getUint32(i)); |
119 | i += 4; | 168 | i += 4; |
120 | result.timescale = view.getUint32(i); | 169 | result.timescale = view.getUint32(i); |
121 | i += 4; | 170 | i += 4; |
... | @@ -162,17 +211,17 @@ var | ... | @@ -162,17 +211,17 @@ var |
162 | 211 | ||
163 | if (result.version === 1) { | 212 | if (result.version === 1) { |
164 | i += 4; | 213 | i += 4; |
165 | result.creationTime = view.getUint32(i); // truncating top 4 bytes | 214 | result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes |
166 | i += 8; | 215 | i += 8; |
167 | result.modificationTime = view.getUint32(i); // truncating top 4 bytes | 216 | result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes |
168 | i += 4; | 217 | i += 4; |
169 | result.timescale = view.getUint32(i); | 218 | result.timescale = view.getUint32(i); |
170 | i += 8 | 219 | i += 8 |
171 | result.duration = view.getUint32(i); // truncating top 4 bytes | 220 | result.duration = view.getUint32(i); // truncating top 4 bytes |
172 | } else { | 221 | } else { |
173 | result.creationTime = view.getUint32(i); | 222 | result.creationTime = parseMp4Date(view.getUint32(i)); |
174 | i += 4; | 223 | i += 4; |
175 | result.modificationTime = view.getUint32(i); | 224 | result.modificationTime = parseMp4Date(view.getUint32(i)); |
176 | i += 4; | 225 | i += 4; |
177 | result.timescale = view.getUint32(i); | 226 | result.timescale = view.getUint32(i); |
178 | i += 4; | 227 | i += 4; |
... | @@ -230,14 +279,38 @@ var | ... | @@ -230,14 +279,38 @@ var |
230 | }; | 279 | }; |
231 | }, | 280 | }, |
232 | stco: function(data) { | 281 | stco: function(data) { |
233 | return { | 282 | var |
283 | view = new DataView(data.buffer, data.byteOffset, data.byteLength), | ||
284 | result = { | ||
285 | version: data[0], | ||
286 | flags: new Uint8Array(data.subarray(1, 4)), | ||
234 | chunkOffsets: [] | 287 | chunkOffsets: [] |
235 | }; | 288 | }, |
289 | entryCount = view.getUint32(4), | ||
290 | i; | ||
291 | for (i = 8; entryCount; i += 4, entryCount--) { | ||
292 | result.chunkOffsets.push(view.getUint32(i)); | ||
293 | } | ||
294 | return result; | ||
236 | }, | 295 | }, |
237 | stsc: function(data) { | 296 | stsc: function(data) { |
238 | return { | 297 | var |
298 | view = new DataView(data.buffer, data.byteOffset, data.byteLength), | ||
299 | entryCount = view.getUint32(4), | ||
300 | result = { | ||
301 | version: data[0], | ||
302 | flags: new Uint8Array(data.subarray(1, 4)), | ||
239 | sampleToChunks: [] | 303 | sampleToChunks: [] |
240 | }; | 304 | }, |
305 | i; | ||
306 | for (i = 8; entryCount; i += 12, entryCount--) { | ||
307 | result.sampleToChunks.push({ | ||
308 | firstChunk: view.getUint32(i), | ||
309 | samplesPerChunk: view.getUint32(i + 4), | ||
310 | sampleDescriptionIndex: view.getUint32(i + 8) | ||
311 | }); | ||
312 | } | ||
313 | return result; | ||
241 | }, | 314 | }, |
242 | stsd: function(data) { | 315 | stsd: function(data) { |
243 | return { | 316 | return { |
... | @@ -246,10 +319,39 @@ var | ... | @@ -246,10 +319,39 @@ var |
246 | sampleDescriptions: videojs.inspectMp4(data.subarray(8)) | 319 | sampleDescriptions: videojs.inspectMp4(data.subarray(8)) |
247 | }; | 320 | }; |
248 | }, | 321 | }, |
322 | stsz: function(data) { | ||
323 | var | ||
324 | view = new DataView(data.buffer, data.byteOffset, data.byteLength), | ||
325 | result = { | ||
326 | version: data[0], | ||
327 | flags: new Uint8Array(data.subarray(1, 4)), | ||
328 | sampleSize: view.getUint32(4), | ||
329 | entries: [] | ||
330 | }, | ||
331 | i; | ||
332 | for (i = 12; i < data.byteLength; i += 4) { | ||
333 | result.entries.push(view.getUint32(i)); | ||
334 | } | ||
335 | return result; | ||
336 | }, | ||
249 | stts: function(data) { | 337 | stts: function(data) { |
250 | return { | 338 | var |
339 | view = new DataView(data.buffer, data.byteOffset, data.byteLength), | ||
340 | result = { | ||
341 | version: data[0], | ||
342 | flags: new Uint8Array(data.subarray(1, 4)), | ||
251 | timeToSamples: [] | 343 | timeToSamples: [] |
252 | }; | 344 | }, |
345 | entryCount = view.getUint32(4), | ||
346 | i; | ||
347 | |||
348 | for (i = 8; entryCount; i += 8, entryCount--) { | ||
349 | result.timeToSamples.push({ | ||
350 | sampleCount: view.getUint32(i), | ||
351 | sampleDelta: view.getUint32(i + 4) | ||
352 | }); | ||
353 | } | ||
354 | return result; | ||
253 | }, | 355 | }, |
254 | tkhd: function(data) { | 356 | tkhd: function(data) { |
255 | var | 357 | var |
... | @@ -261,18 +363,18 @@ var | ... | @@ -261,18 +363,18 @@ var |
261 | }; | 363 | }; |
262 | if (result.version === 1) { | 364 | if (result.version === 1) { |
263 | i += 4; | 365 | i += 4; |
264 | result.creationTime = view.getUint32(i); // truncating top 4 bytes | 366 | result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes |
265 | i += 8; | 367 | i += 8; |
266 | result.modificationTime = view.getUint32(i); // truncating top 4 bytes | 368 | result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes |
267 | i += 4; | 369 | i += 4; |
268 | result.trackId = view.getUint32(i); | 370 | result.trackId = view.getUint32(i); |
269 | i += 4; | 371 | i += 4; |
270 | i += 8; | 372 | i += 8; |
271 | result.duration = view.getUint32(i); // truncating top 4 bytes | 373 | result.duration = view.getUint32(i); // truncating top 4 bytes |
272 | } else { | 374 | } else { |
273 | result.creationTime = view.getUint32(i); | 375 | result.creationTime = parseMp4Date(view.getUint32(i)); |
274 | i += 4; | 376 | i += 4; |
275 | result.modificationTime = view.getUint32(i); | 377 | result.modificationTime = parseMp4Date(view.getUint32(i)); |
276 | i += 4; | 378 | i += 4; |
277 | result.trackId = view.getUint32(i); | 379 | result.trackId = view.getUint32(i); |
278 | i += 4; | 380 | i += 4; |
... | @@ -291,10 +393,27 @@ var | ... | @@ -291,10 +393,27 @@ var |
291 | i += 2; | 393 | i += 2; |
292 | result.matrix = new Uint32Array(data.subarray(i, i + (9 * 4))); | 394 | result.matrix = new Uint32Array(data.subarray(i, i + (9 * 4))); |
293 | i += 9 * 4; | 395 | i += 9 * 4; |
294 | result.width = view.getUint32(i); | 396 | result.width = view.getUint16(i) + (view.getUint16(i + 2) / 16); |
295 | i += 4; | 397 | i += 4; |
296 | result.height = view.getUint32(i); | 398 | result.height = view.getUint16(i) + (view.getUint16(i + 2) / 16); |
297 | return result; | 399 | return result; |
400 | }, | ||
401 | 'url ': function(data) { | ||
402 | return { | ||
403 | version: data[0], | ||
404 | flags: new Uint8Array(data.subarray(1, 4)) | ||
405 | }; | ||
406 | }, | ||
407 | vmhd: function(data) { | ||
408 | var view = new DataView(data.buffer, data.byteOffset, data.byteLength); | ||
409 | return { | ||
410 | version: data[0], | ||
411 | flags: new Uint8Array(data.subarray(1, 4)), | ||
412 | graphicsmode: view.getUint16(4), | ||
413 | opcolor: new Uint16Array([view.getUint16(6), | ||
414 | view.getUint16(8), | ||
415 | view.getUint16(10)]) | ||
416 | }; | ||
298 | } | 417 | } |
299 | }; | 418 | }; |
300 | 419 | ... | ... |
... | @@ -140,32 +140,13 @@ | ... | @@ -140,32 +140,13 @@ |
140 | 140 | ||
141 | // write out the result | 141 | // write out the result |
142 | hex += '<pre>'; | 142 | hex += '<pre>'; |
143 | // hex += videojs.Hls.utils.hexDump(transmuxer.mp4); | 143 | hex += videojs.Hls.utils.hexDump(videojs.mp4.initSegment()); |
144 | hex += '</pre>'; | 144 | hex += '</pre>'; |
145 | vjsOutput.innerHTML = hex; | 145 | vjsOutput.innerHTML = hex; |
146 | 146 | ||
147 | // XXX media source testing | 147 | // XXX media source testing |
148 | 148 | ||
149 | vjsBoxes.innerHTML = JSON.stringify(videojs.inspectMp4(videojs.mp4.initSegment()), null, ' '); | 149 | vjsBoxes.innerHTML = JSON.stringify(videojs.inspectMp4(videojs.mp4.initSegment()), null, ' '); |
150 | |||
151 | var video = document.createElement('video'); | ||
152 | var mediaSource = new MediaSource(); | ||
153 | mediaSource.addEventListener('sourceopen', function() { | ||
154 | var buffer = mediaSource.addSourceBuffer('video/mp4;codecs=avc1.4d0020,mp4a.40.2'); | ||
155 | buffer.addEventListener('updatestart', console.log.bind(console)); | ||
156 | buffer.addEventListener('updateend', console.log.bind(console)); | ||
157 | buffer.addEventListener('error', console.log.bind(console)); | ||
158 | buffer.appendBuffer(videojs.mp4.initSegment()); | ||
159 | console.log('done', mediaSource, buffer, video.error); | ||
160 | window.vjsMediaSource = mediaSource; | ||
161 | window.vjsSourceBuffer = buffer; | ||
162 | window.vjsVideo = video; | ||
163 | }); | ||
164 | mediaSource.addEventListener('error', console.log.bind(console)); | ||
165 | mediaSource.addEventListener('opened', console.log.bind(console)); | ||
166 | mediaSource.addEventListener('closed', console.log.bind(console)); | ||
167 | mediaSource.addEventListener('sourceended', console.log.bind(console)); | ||
168 | video.src = URL.createObjectURL(mediaSource); | ||
169 | }); | 150 | }); |
170 | reader.readAsArrayBuffer(this.files[0]); | 151 | reader.readAsArrayBuffer(this.files[0]); |
171 | }, false); | 152 | }, false); |
... | @@ -184,6 +165,28 @@ | ... | @@ -184,6 +165,28 @@ |
184 | hex += videojs.Hls.utils.hexDump(bytes); | 165 | hex += videojs.Hls.utils.hexDump(bytes); |
185 | hex += '</pre>'; | 166 | hex += '</pre>'; |
186 | workingOutput.innerHTML = hex; | 167 | workingOutput.innerHTML = hex; |
168 | |||
169 | // XXX Media Sources Testing | ||
170 | |||
171 | var video = document.createElement('video'); | ||
172 | var mediaSource = new MediaSource(); | ||
173 | mediaSource.addEventListener('sourceopen', function() { | ||
174 | var buffer = mediaSource.addSourceBuffer('video/mp4;codecs=avc1.4d0020,mp4a.40.2'); | ||
175 | buffer.addEventListener('updatestart', console.log.bind(console)); | ||
176 | buffer.addEventListener('updateend', console.log.bind(console)); | ||
177 | buffer.addEventListener('error', console.log.bind(console)); | ||
178 | buffer.appendBuffer(bytes); | ||
179 | console.log('done'); | ||
180 | window.vjsMediaSource = mediaSource; | ||
181 | window.vjsSourceBuffer = buffer; | ||
182 | window.vjsVideo = video; | ||
183 | }); | ||
184 | mediaSource.addEventListener('error', console.log.bind(console)); | ||
185 | mediaSource.addEventListener('opened', console.log.bind(console)); | ||
186 | mediaSource.addEventListener('closed', console.log.bind(console)); | ||
187 | mediaSource.addEventListener('sourceended', console.log.bind(console)); | ||
188 | video.src = URL.createObjectURL(mediaSource); | ||
189 | video.addEventListener('error', console.log.bind(console)); | ||
187 | }); | 190 | }); |
188 | reader.readAsArrayBuffer(this.files[0]); | 191 | reader.readAsArrayBuffer(this.files[0]); |
189 | }, false); | 192 | }, false); | ... | ... |
-
Please register or sign in to post a comment