format_parser 2.3.0 → 2.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,632 @@
1
+ require 'matrix'
2
+ require 'spec_helper'
3
+
4
+ describe FormatParser::ISOBaseMediaFileFormat::Utils do
5
+ include FormatParser::ISOBaseMediaFileFormat::Utils
6
+ Box = FormatParser::ISOBaseMediaFileFormat::Box
7
+
8
+ describe '.dimensions' do
9
+ context 'when no moov box' do
10
+ let(:box_tree) { [Box.new('ftyp', 0, 16)] }
11
+
12
+ it 'returns nil' do
13
+ expect(dimensions(box_tree)).to eq(nil)
14
+ end
15
+ end
16
+
17
+ context 'when no video trak boxes' do
18
+ let(:box_tree) do
19
+ [
20
+ Box.new('moov', 0, 0, nil, [
21
+ Box.new('trak', 0, 0),
22
+ Box.new('trak', 0, 0, nil, [
23
+ Box.new('hdlr', 0, 0, { handler_type: 'soun' }),
24
+ ]),
25
+ Box.new('trak', 0, 0, nil, [
26
+ Box.new('hdlr', 0, 0, { component_type: 'mhlr', component_subtype: 'soun' }),
27
+ ])
28
+ ])
29
+ ]
30
+ end
31
+
32
+ it 'returns nil' do
33
+ expect(dimensions(box_tree)).to eq(nil)
34
+ end
35
+ end
36
+
37
+ context 'when no video tkhd boxes' do
38
+ let(:box_tree) do
39
+ [
40
+ Box.new('moov', 0, 0, nil, [
41
+ Box.new('trak', 0, 0, nil, [
42
+ Box.new('hdlr', 0, 0, { handler_type: 'soun' }),
43
+ Box.new('tkhd', 0, 0),
44
+ ]),
45
+ Box.new('trak', 0, 0, nil, [
46
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
47
+ ]),
48
+ Box.new('trak', 0, 0, nil, [
49
+ Box.new('hdlr', 0, 0, { component_type: 'mhlr', component_subtype: 'vide' }),
50
+ ])
51
+ ])
52
+ ]
53
+ end
54
+
55
+ it 'returns nil' do
56
+ expect(dimensions(box_tree)).to eq(nil)
57
+ end
58
+ end
59
+
60
+ context 'when dimensions missing' do
61
+ let(:box_tree) do
62
+ [
63
+ Box.new('moov', 0, 0, nil, [
64
+ Box.new('trak', 0, 0, nil, [
65
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
66
+ Box.new('tkhd', 0, 0, { height: 100 })
67
+ ]),
68
+ ])
69
+ ]
70
+ end
71
+
72
+ it 'returns nil' do
73
+ expect(dimensions(box_tree)).to eq(nil)
74
+ end
75
+ end
76
+
77
+ context 'when movie matrix missing' do
78
+ let(:box_tree) do
79
+ [
80
+ Box.new('moov', 0, 0, nil, [
81
+ Box.new('trak', 0, 0, nil, [
82
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
83
+ Box.new('tkhd', 0, 0, {
84
+ height: 100,
85
+ width: 200,
86
+ matrix: Matrix[[2, 0, 0], [0, 2, 0], [0, 0, 2]]
87
+ })
88
+ ]),
89
+ ])
90
+ ]
91
+ end
92
+
93
+ it 'defaults to identity matrix' do
94
+ expect(dimensions(box_tree)).to eq([400, 200])
95
+ end
96
+ end
97
+
98
+ context 'when track matrix missing' do
99
+ let(:box_tree) do
100
+ [
101
+ Box.new('moov', 0, 0, nil, [
102
+ Box.new('mvhd', 0, 0, { matrix: Matrix[[2, 0, 0], [0, 2, 0], [0, 0, 2]] }),
103
+ Box.new('trak', 0, 0, nil, [
104
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
105
+ Box.new('tkhd', 0, 0, { height: 100, width: 200 })
106
+ ]),
107
+ ])
108
+ ]
109
+ end
110
+
111
+ it 'defaults to identity matrix' do
112
+ expect(dimensions(box_tree)).to eq([400, 200])
113
+ end
114
+ end
115
+
116
+ context 'when multiple tracks' do
117
+ let(:box_tree) do
118
+ [
119
+ Box.new('moov', 0, 0, nil, [
120
+ Box.new('mvhd', 0, 0, { matrix: Matrix[[2, 0, 0], [0, 2, 0], [0, 0, 2]] }),
121
+ Box.new('trak', 0, 0, nil, [
122
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
123
+ Box.new('tkhd', 0, 0, {
124
+ height: 1000,
125
+ })
126
+ ]),
127
+ Box.new('trak', 0, 0, nil, [
128
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
129
+ Box.new('tkhd', 0, 0, {
130
+ height: 200,
131
+ width: 100,
132
+ matrix: Matrix[[0, 3, 0], [3, 0, 0], [0, 0, 3]]
133
+ })
134
+ ]),
135
+ Box.new('trak', 0, 0, nil, [
136
+ Box.new('hdlr', 0, 0, { component_type: 'mhlr', component_subtype: 'vide' }),
137
+ Box.new('tkhd', 0, 0, {
138
+ height: 200,
139
+ width: 100,
140
+ matrix: Matrix[[2, 0, 0], [0, 2, 0], [0, 0, 2]]
141
+ })
142
+ ]),
143
+ ])
144
+ ]
145
+ end
146
+
147
+ it 'correctly calculates dimensions' do
148
+ expect(dimensions(box_tree)).to eq([1200, 800])
149
+ end
150
+ end
151
+ end
152
+
153
+ describe '.duration' do
154
+ context 'when no moov box' do
155
+ let(:box_tree) { [Box.new('ftyp', 0, 16)] }
156
+
157
+ it 'returns nil' do
158
+ expect(duration(box_tree)).to eq(nil)
159
+ end
160
+ end
161
+
162
+ context 'when no mvhd box' do
163
+ let(:box_tree) { [Box.new('moov', 0, 0)] }
164
+
165
+ it 'returns nil' do
166
+ expect(duration(box_tree)).to eq(nil)
167
+ end
168
+ end
169
+
170
+ context 'when no duration' do
171
+ let(:box_tree) do
172
+ [
173
+ Box.new('moov', 0, 0, nil, [
174
+ Box.new('mvhd', 0, 0, { timescale: 1 })
175
+ ])
176
+ ]
177
+ end
178
+
179
+ it 'returns nil' do
180
+ expect(duration(box_tree)).to eq(nil)
181
+ end
182
+ end
183
+
184
+ context 'when no timescale' do
185
+ let(:box_tree) do
186
+ [
187
+ Box.new('moov', 0, 0, nil, [
188
+ Box.new('mvhd', 0, 0, { duration: 1 })
189
+ ])
190
+ ]
191
+ end
192
+
193
+ it 'returns nil' do
194
+ expect(duration(box_tree)).to eq(nil)
195
+ end
196
+ end
197
+
198
+ context 'when timescale and duration' do
199
+ let(:box_tree) do
200
+ [
201
+ Box.new('moov', 0, 0, nil, [
202
+ Box.new('mvhd', 0, 0, { duration: 10, timescale: 2 })
203
+ ])
204
+ ]
205
+ end
206
+
207
+ it 'correctly calculates duration' do
208
+ expect(duration(box_tree)).to eq(5)
209
+ end
210
+ end
211
+ end
212
+
213
+ describe '.frame_rate' do
214
+ context 'when no moov box' do
215
+ let(:box_tree) { [Box.new('ftyp', 0, 16)] }
216
+
217
+ it 'returns nil' do
218
+ expect(frame_rate(box_tree)).to eq(nil)
219
+ end
220
+ end
221
+
222
+ context 'when no video trak boxes' do
223
+ let(:box_tree) do
224
+ [
225
+ Box.new('moov', 0, 0, nil, [
226
+ Box.new('trak', 0, 0),
227
+ Box.new('trak', 0, 0, nil, [
228
+ Box.new('hdlr', 0, 0, { handler_type: 'soun' }), # Audio - should be ignored
229
+ Box.new('mdia', 0, 0, nil, [
230
+ Box.new('mdhd', 0, 0, { timescale: 30 }),
231
+ Box.new('minf', 0, 0, nil, [
232
+ Box.new('stbl', 0, 0, nil, [
233
+ Box.new('stts', 0, 0, {
234
+ entry_count: 1,
235
+ entries: [
236
+ {
237
+ sample_count: 10,
238
+ sample_delta: 1
239
+ }
240
+ ]
241
+ })
242
+ ])
243
+ ])
244
+ ])
245
+ ]),
246
+ ])
247
+ ]
248
+ end
249
+
250
+ it 'returns nil' do
251
+ expect(frame_rate(box_tree)).to eq(nil)
252
+ end
253
+ end
254
+
255
+ context 'when no mdhd box' do
256
+ let(:box_tree) do
257
+ [
258
+ Box.new('moov', 0, 0, nil, [
259
+ Box.new('trak', 0, 0, nil, [
260
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
261
+ Box.new('mdia', 0, 0, nil, [
262
+ Box.new('minf', 0, 0, nil, [
263
+ Box.new('stbl', 0, 0, nil, [
264
+ Box.new('stts', 0, 0, {
265
+ entry_count: 1,
266
+ entries: [
267
+ {
268
+ sample_count: 10,
269
+ sample_delta: 1
270
+ }
271
+ ]
272
+ })
273
+ ])
274
+ ])
275
+ ])
276
+ ]),
277
+ ])
278
+ ]
279
+ end
280
+
281
+ it 'returns nil' do
282
+ expect(frame_rate(box_tree)).to eq(nil)
283
+ end
284
+ end
285
+
286
+ context 'when no stts box' do
287
+ let(:box_tree) do
288
+ [
289
+ Box.new('moov', 0, 0, nil, [
290
+ Box.new('trak', 0, 0, nil, [
291
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
292
+ Box.new('mdia', 0, 0, nil, [
293
+ Box.new('mdhd', 0, 0, { timescale: 30 }),
294
+ Box.new('minf', 0, 0, nil, [
295
+ Box.new('stbl', 0, 0)
296
+ ])
297
+ ])
298
+ ]),
299
+ ])
300
+ ]
301
+ end
302
+
303
+ it 'returns nil' do
304
+ expect(frame_rate(box_tree)).to eq(nil)
305
+ end
306
+ end
307
+
308
+ context 'when multiple entries' do
309
+ let(:box_tree) do
310
+ [
311
+ Box.new('moov', 0, 0, nil, [
312
+ Box.new('trak', 0, 0),
313
+ Box.new('trak', 0, 0, nil, [
314
+ Box.new('hdlr', 0, 0, { handler_type: 'soun' }), # Audio - should be ignored
315
+ Box.new('mdia', 0, 0, nil, [
316
+ Box.new('mdhd', 0, 0, { timescale: 30 }),
317
+ Box.new('minf', 0, 0, nil, [
318
+ Box.new('stbl', 0, 0, nil, [
319
+ Box.new('stts', 0, 0, {
320
+ entry_count: 1,
321
+ entries: [
322
+ {
323
+ sample_count: 10,
324
+ sample_delta: 1
325
+ }
326
+ ]
327
+ })
328
+ ])
329
+ ])
330
+ ])
331
+ ]),
332
+ Box.new('trak', 0, 0, nil, [
333
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
334
+ Box.new('mdia', 0, 0, nil, [
335
+ Box.new('mdhd', 0, 0, { timescale: 30 }),
336
+ Box.new('minf', 0, 0, nil, [
337
+ Box.new('stbl', 0, 0)
338
+ ])
339
+ ])
340
+ ]),
341
+ Box.new('trak', 0, 0, nil, [
342
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
343
+ Box.new('mdia', 0, 0, nil, [
344
+ Box.new('minf', 0, 0, nil, [
345
+ Box.new('stbl', 0, 0, nil, [
346
+ Box.new('stts', 0, 0, {
347
+ entry_count: 1,
348
+ entries: [
349
+ {
350
+ sample_count: 10,
351
+ sample_delta: 1
352
+ }
353
+ ]
354
+ })
355
+ ])
356
+ ])
357
+ ])
358
+ ]),
359
+ Box.new('trak', 0, 0, nil, [
360
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
361
+ Box.new('mdia', 0, 0, nil, [
362
+ Box.new('mdhd', 0, 0, { timescale: 60 }),
363
+ Box.new('minf', 0, 0, nil, [
364
+ Box.new('stbl', 0, 0, nil, [
365
+ Box.new('stts', 0, 0, {
366
+ entry_count: 2,
367
+ entries: [
368
+ {
369
+ sample_count: 10,
370
+ sample_delta: 2
371
+ },
372
+ {
373
+ sample_count: 10,
374
+ sample_delta: 1
375
+ }
376
+ ]
377
+ })
378
+ ])
379
+ ])
380
+ ])
381
+ ]),
382
+ Box.new('trak', 0, 0, nil, [
383
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
384
+ Box.new('mdia', 0, 0, nil, [
385
+ Box.new('mdhd', 0, 0, { timescale: 120 }),
386
+ Box.new('minf', 0, 0, nil, [
387
+ Box.new('stbl', 0, 0, nil, [
388
+ Box.new('stts', 0, 0, {
389
+ entry_count: 1,
390
+ entries: [
391
+ {
392
+ sample_count: 10,
393
+ sample_delta: 3
394
+ }
395
+ ]
396
+ })
397
+ ])
398
+ ])
399
+ ])
400
+ ]),
401
+ ])
402
+ ]
403
+ end
404
+
405
+ it 'correctly calculates frame rate' do
406
+ expect(frame_rate(box_tree)).to eq(30)
407
+ end
408
+ end
409
+
410
+ describe '.video_codecs' do
411
+ context 'when no moov box' do
412
+ let(:box_tree) { [Box.new('ftyp', 0, 16)] }
413
+
414
+ it 'returns empty array' do
415
+ expect(video_codecs(box_tree)).to eq([])
416
+ end
417
+ end
418
+
419
+ context 'when no video trak boxes' do
420
+ let(:box_tree) do
421
+ [
422
+ Box.new('moov', 0, 0, nil, [
423
+ Box.new('trak', 0, 0),
424
+ Box.new('trak', 0, 0, nil, [
425
+ Box.new('hdlr', 0, 0, { handler_type: 'soun' }),
426
+ Box.new('mdia', 0, 0, nil, [
427
+ Box.new('minf', 0, 0, nil, [
428
+ Box.new('stbl', 0, 0, nil, [
429
+ Box.new('stsd', 0, 0, nil, [
430
+ Box.new('flac', 0, 0) # Audio codec - should be ignored
431
+ ])
432
+ ])
433
+ ])
434
+ ])
435
+ ]),
436
+ Box.new('trak', 0, 0, nil, [
437
+ Box.new('hdlr', 0, 0, { component_type: 'mhlr', component_subtype: 'soun' }),
438
+ Box.new('mdia', 0, 0, nil, [
439
+ Box.new('minf', 0, 0, nil, [
440
+ Box.new('stbl', 0, 0, nil, [
441
+ Box.new('stsd', 0, 0, nil, [
442
+ Box.new('flac', 0, 0) # Audio codec - should be ignored
443
+ ])
444
+ ])
445
+ ])
446
+ ])
447
+ ])
448
+ ])
449
+ ]
450
+ end
451
+
452
+ it 'returns empty array' do
453
+ expect(video_codecs(box_tree)).to eq([])
454
+ end
455
+ end
456
+
457
+ context 'when no stsd boxes' do
458
+ let(:box_tree) do
459
+ [
460
+ Box.new('moov', 0, 0, nil, [
461
+ Box.new('trak', 0, 0, nil, [
462
+ Box.new('hdlr', 0, 0, { handler_type: 'soun' }),
463
+ Box.new('mdia', 0, 0, nil, [
464
+ Box.new('minf', 0, 0, nil, [
465
+ Box.new('stbl', 0, 0, nil, [
466
+ Box.new('stsd', 0, 0, nil, [
467
+ Box.new('flac', 0, 0) # Audio codec - should be ignored
468
+ ])
469
+ ])
470
+ ])
471
+ ])
472
+ ]),
473
+ Box.new('trak', 0, 0, nil, [
474
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' })
475
+ ]),
476
+ Box.new('trak', 0, 0, nil, [
477
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
478
+ Box.new('mdia', 0, 0)
479
+ ]),
480
+ Box.new('trak', 0, 0, nil, [
481
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
482
+ Box.new('mdia', 0, 0, nil, [
483
+ Box.new('minf', 0, 0)
484
+ ])
485
+ ]),
486
+ Box.new('trak', 0, 0, nil, [
487
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
488
+ Box.new('mdia', 0, 0, nil, [
489
+ Box.new('minf', 0, 0, nil, [
490
+ Box.new('stbl', 0, 0)
491
+ ])
492
+ ])
493
+ ]),
494
+ Box.new('trak', 0, 0, nil, [
495
+ Box.new('hdlr', 0, 0, { component_type: 'mhlr', component_subtype: 'vide' }),
496
+ Box.new('mdia', 0, 0, nil, [
497
+ Box.new('minf', 0, 0, nil, [
498
+ Box.new('stbl', 0, 0)
499
+ ])
500
+ ])
501
+ ]),
502
+ ])
503
+ ]
504
+ end
505
+
506
+ it 'returns empty array' do
507
+ expect(video_codecs(box_tree)).to eq([])
508
+ end
509
+ end
510
+
511
+ context 'when no codecs' do
512
+ let(:box_tree) do
513
+ [
514
+ Box.new('moov', 0, 0, nil, [
515
+ Box.new('trak', 0, 0, nil, [
516
+ Box.new('hdlr', 0, 0, { handler_type: 'soun' }),
517
+ Box.new('mdia', 0, 0, nil, [
518
+ Box.new('minf', 0, 0, nil, [
519
+ Box.new('stbl', 0, 0, nil, [
520
+ Box.new('stsd', 0, 0, nil, [
521
+ Box.new('flac', 0, 0) # Audio codec - should be ignored
522
+ ])
523
+ ])
524
+ ])
525
+ ])
526
+ ]),
527
+ Box.new('trak', 0, 0, nil, [
528
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
529
+ Box.new('mdia', 0, 0, nil, [
530
+ Box.new('minf', 0, 0, nil, [
531
+ Box.new('stbl', 0, 0, nil, [
532
+ Box.new('stsd', 0, 0)
533
+ ])
534
+ ])
535
+ ])
536
+ ]),
537
+ Box.new('trak', 0, 0, nil, [
538
+ Box.new('hdlr', 0, 0, { component_type: 'mhlr', component_subtype: 'vide' }),
539
+ Box.new('mdia', 0, 0, nil, [
540
+ Box.new('minf', 0, 0, nil, [
541
+ Box.new('stbl', 0, 0, nil, [
542
+ Box.new('stsd', 0, 0)
543
+ ])
544
+ ])
545
+ ])
546
+ ]),
547
+ ])
548
+ ]
549
+ end
550
+
551
+ it 'returns empty array' do
552
+ expect(video_codecs(box_tree)).to eq([])
553
+ end
554
+ end
555
+
556
+ context 'when multiple codecs' do
557
+ let(:box_tree) do
558
+ [
559
+ Box.new('moov', 0, 0, nil, [
560
+ Box.new('trak', 0, 0, nil, [
561
+ Box.new('hdlr', 0, 0, { handler_type: 'soun' }),
562
+ Box.new('mdia', 0, 0, nil, [
563
+ Box.new('minf', 0, 0, nil, [
564
+ Box.new('stbl', 0, 0, nil, [
565
+ Box.new('stsd', 0, 0, nil, [
566
+ Box.new('flac', 0, 0) # Audio codec - should be ignored
567
+ ])
568
+ ])
569
+ ])
570
+ ])
571
+ ]),
572
+ Box.new('trak', 0, 0, nil, [
573
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
574
+ Box.new('mdia', 0, 0, nil, [
575
+ Box.new('minf', 0, 0, nil, [
576
+ Box.new('stbl', 0, 0, nil, [
577
+ Box.new('stsd', 0, 0, nil, [
578
+ Box.new('avc1', 0, 0),
579
+ Box.new('raw ', 0, 0)
580
+ ])
581
+ ])
582
+ ])
583
+ ])
584
+ ]),
585
+ Box.new('trak', 0, 0, nil, [
586
+ Box.new('hdlr', 0, 0, { component_type: 'mhlr', component_subtype: 'vide' }),
587
+ Box.new('mdia', 0, 0, nil, [
588
+ Box.new('minf', 0, 0, nil, [
589
+ Box.new('stbl', 0, 0, nil, [
590
+ Box.new('stsd', 0, 0, nil, [
591
+ Box.new('avc1', 0, 0),
592
+ ]),
593
+ Box.new('stsd', 0, 0, nil, [
594
+ Box.new('rpza', 0, 0),
595
+ ])
596
+ ])
597
+ ])
598
+ ])
599
+ ]),
600
+ Box.new('trak', 0, 0, nil, [
601
+ Box.new('hdlr', 0, 0, { handler_type: 'vide' }),
602
+ Box.new('mdia', 0, 0, nil, [
603
+ Box.new('minf', 0, 0, nil, [
604
+ Box.new('stbl', 0, 0, nil, [
605
+ Box.new('stsd', 0, 0, nil, [
606
+ Box.new('avc1', 0, 0),
607
+ ])
608
+ ])
609
+ ])
610
+ ]),
611
+ Box.new('mdia', 0, 0, nil, [
612
+ Box.new('minf', 0, 0, nil, [
613
+ Box.new('stbl', 0, 0, nil, [
614
+ Box.new('stsd', 0, 0, nil, [
615
+ Box.new('avc1', 0, 0),
616
+ Box.new('mp4v', 0, 0)
617
+ ])
618
+ ])
619
+ ])
620
+ ])
621
+ ]),
622
+ ])
623
+ ]
624
+ end
625
+
626
+ it 'returns all distinct codecs' do
627
+ expect(video_codecs(box_tree)).to match_array(['avc1', 'mp4v', 'raw ', 'rpza'])
628
+ end
629
+ end
630
+ end
631
+ end
632
+ end