getid3_quicktime::FixedPoint16_16()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
// +----------------------------------------------------------------------+
3
// | PHP version 5                                                        |
4
// +----------------------------------------------------------------------+
5
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen                 |
6
// +----------------------------------------------------------------------+
7
// | This source file is subject to version 2 of the GPL license,         |
8
// | that is bundled with this package in the file license.txt and is     |
9
// | available through the world-wide-web at the following url:           |
10
// | http://www.gnu.org/copyleft/gpl.html                                 |
11
// +----------------------------------------------------------------------+
12
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org    |
13
// +----------------------------------------------------------------------+
14
// | Authors: James Heinrich <info�getid3*org>                            |
15
// |          Allan Hansen <ah�artemis*dk>                                |
16
// +----------------------------------------------------------------------+
17
// | module.audio-video.quicktime.php                                     |
18
// | Module for analyzing Quicktime, MP3-in-MP4 and Apple Lossless files. |
19
// | dependencies: module.audio.mp3.php                                   |
20
// |               zlib support in PHP (optional)                         |
21
// +----------------------------------------------------------------------+
22
//
23
// $Id: module.audio-video.quicktime.php,v 1.7 2006/11/02 16:03:28 ah Exp $
24
25
class getid3_quicktime extends getid3_handler
26
{
27
28
    public function Analyze()
29
    {
30
        $getid3 = $this->getid3;
31
32
        $info = &$getid3->info;
33
34
        $getid3->include_module('audio.mp3');
35
36
        $info['quicktime'] = [];
37
        $info_quicktime    = &$info['quicktime'];
38
39
        $info['fileformat']        = 'quicktime';
40
        $info_quicktime['hinting'] = false;
41
42
        fseek($getid3->fp, $info['avdataoffset'], SEEK_SET);
43
44
        $offset = $atom_counter = 0;
45
46
        while ($offset < $info['avdataend']) {
47
            fseek($getid3->fp, $offset, SEEK_SET);
48
            $atom_header = fread($getid3->fp, 8);
49
50
            $atom_size = getid3_lib::BigEndian2Int(substr($atom_header, 0, 4));
51
            $atom_name = substr($atom_header, 4, 4);
52
53
            $info_quicktime[$atom_name]['name']   = $atom_name;
54
            $info_quicktime[$atom_name]['size']   = $atom_size;
55
            $info_quicktime[$atom_name]['offset'] = $offset;
56
57
            if (($offset + $atom_size) > $info['avdataend']) {
58
                throw new getid3_exception('Atom at offset ' . $offset . ' claims to go beyond end-of-file (length: ' . $atom_size . ' bytes)');
59
            }
60
61
            if (0 == $atom_size) {
62
                // Furthermore, for historical reasons the list of atoms is optionally
63
                // terminated by a 32-bit integer set to 0. If you are writing a program
64
                // to read user data atoms, you should allow for the terminating 0.
65
                break;
66
            }
67
68
            switch ($atom_name) {
69
                case 'mdat': // Media DATa atom
70
                    // 'mdat' contains the actual data for the audio/video
71
                    if (($atom_size > 8) && (!isset($info['avdataend_tmp']) || ($info_quicktime[$atom_name]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) {
72
                        $info['avdataoffset'] = $info_quicktime[$atom_name]['offset'] + 8;
73
                        $old_av_data_end      = $info['avdataend'];
74
                        $info['avdataend']    = $info_quicktime[$atom_name]['offset'] + $info_quicktime[$atom_name]['size'];
75
76
                        //// MP3
77
78
                        if (!$getid3->include_module_optional('audio.mp3')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $getid3->include_module_optional('audio.mp3') of type null|true is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
79
                            $getid3->warning('MP3 skipped because mpeg module is missing.');
80
                        } else {
81
                            // Clone getid3 - messing with offsets - better safe than sorry
82
                            $clone = clone $getid3;
83
84
                            if (getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode(fread($clone->fp, 4)))) {
85
                                $mp3 = new getid3_mp3($clone);
86
                                $mp3->AnalyzeMPEGaudioInfo();
87
88
                                // Import from clone and destroy
89
                                if (isset($clone->info['mpeg']['audio'])) {
90
                                    $info['mpeg']['audio'] = $clone->info['mpeg']['audio'];
91
92
                                    $info['audio']['dataformat']   = 'mp3';
93
                                    $info['audio']['codec']        = (!empty($info['mpeg']['audio']['encoder']) ? $info['mpeg']['audio']['encoder'] : (!empty($info['mpeg']['audio']['codec']) ? $info['mpeg']['audio']['codec'] : (!empty($info['mpeg']['audio']['LAME']) ? 'LAME' : 'mp3')));
94
                                    $info['audio']['sample_rate']  = $info['mpeg']['audio']['sample_rate'];
95
                                    $info['audio']['channels']     = $info['mpeg']['audio']['channels'];
96
                                    $info['audio']['bitrate']      = $info['mpeg']['audio']['bitrate'];
97
                                    $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
98
                                    $info['bitrate']               = $info['audio']['bitrate'];
99
100
                                    $getid3->warning($clone->warnings());
101
                                    unset($clone);
102
                                }
103
                            }
104
                        }
105
106
                        $info['avdataend'] = $old_av_data_end;
107
                        unset($old_av_data_end);
108
                    }
109
                    break;
110
111
                case 'free': // FREE space atom
112
                case 'skip': // SKIP atom
113
                case 'wide': // 64-bit expansion placeholder atom
114
                    // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
115
                    break;
116
117
                default:
118
                    $atom_hierarchy             = [];
119
                    $info_quicktime[$atom_name] = $this->QuicktimeParseAtom($atom_name, $atom_size, fread($getid3->fp, $atom_size), $offset, $atom_hierarchy);
120
                    break;
121
            }
122
123
            $offset += $atom_size;
124
            $atom_counter++;
125
        }
126
127
        if (!empty($info['avdataend_tmp'])) {
128
            // this value is assigned to a temp value and then erased because
129
            // otherwise any atoms beyond the 'mdat' atom would not get parsed
130
            $info['avdataend'] = $info['avdataend_tmp'];
131
            unset($info['avdataend_tmp']);
132
        }
133
134
        if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) {
135
            $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
136
        }
137
138
        if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info_quicktime['video'])) {
139
            $info['audio']['bitrate'] = $info['bitrate'];
140
        }
141
142
        if (('mp4' == @$info['audio']['dataformat']) && empty($info['video']['resolution_x'])) {
143
            $info['fileformat'] = 'mp4';
144
            $info['mime_type']  = 'audio/mp4';
145
            unset($info['video']['dataformat']);
146
        }
147
148
        if (!$getid3->option_extra_info) {
149
            unset($info_quicktime['moov']);
150
        }
151
152
        if (empty($info['audio']['dataformat']) && !empty($info_quicktime['audio'])) {
153
            $info['audio']['dataformat'] = 'quicktime';
154
        }
155
156
        if (empty($info['video']['dataformat']) && !empty($info_quicktime['video'])) {
157
            $info['video']['dataformat'] = 'quicktime';
158
        }
159
160
        return true;
161
    }
162
163
    private function QuicktimeParseAtom($atom_name, $atom_size, $atom_data, $base_offset, &$atom_hierarchy)
164
    {
165
        // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm
166
167
        $getid3 = $this->getid3;
168
169
        $info           = &$getid3->info;
170
        $info_quicktime = &$info['quicktime'];
171
172
        array_push($atom_hierarchy, $atom_name);
173
        $atom_structure['hierarchy'] = implode(' ', $atom_hierarchy);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$atom_structure was never initialized. Although not strictly required by PHP, it is generally a good practice to add $atom_structure = array(); before regardless.
Loading history...
174
        $atom_structure['name']      = $atom_name;
175
        $atom_structure['size']      = $atom_size;
176
        $atom_structure['offset']    = $base_offset;
177
178
        switch ($atom_name) {
179
            case 'moov': // MOVie container atom
180
            case 'trak': // TRAcK container atom
181
            case 'clip': // CLIPping container atom
182
            case 'matt': // track MATTe container atom
183
            case 'edts': // EDiTS container atom
184
            case 'tref': // Track REFerence container atom
185
            case 'mdia': // MeDIA container atom
186
            case 'minf': // Media INFormation container atom
187
            case 'dinf': // Data INFormation container atom
188
            case 'udta': // User DaTA container atom
189
            case 'stbl': // Sample TaBLe container atom
190
            case 'cmov': // Compressed MOVie container atom
191
            case 'rmra': // Reference Movie Record Atom
192
            case 'rmda': // Reference Movie Descriptor Atom
193
            case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR)
194
                $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $base_offset + 8, $atom_hierarchy);
195
                break;
196
197
            case '�cpy':
198
            case '�day':
199
            case '�dir':
200
            case '�ed1':
201
            case '�ed2':
202
            case '�ed3':
203
            case '�ed4':
204
            case '�ed5':
205
            case '�ed6':
206
            case '�ed7':
207
            case '�ed8':
208
            case '�ed9':
209
            case '�fmt':
210
            case '�inf':
211
            case '�prd':
212
            case '�prf':
213
            case '�req':
214
            case '�src':
215
            case '�wrt':
216
            case '�nam':
217
            case '�cmt':
218
            case '�wrn':
219
            case '�hst':
220
            case '�mak':
221
            case '�mod':
222
            case '�PRD':
223
            case '�swr':
224
            case '�aut':
225
            case '�ART':
226
            case '�trk':
227
            case '�alb':
228
            case '�com':
229
            case '�gen':
230
            case '�ope':
231
            case '�url':
232
            case '�enc':
233
                $atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
234
                $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
235
                $atom_structure['data']        = substr($atom_data, 4);
236
237
                $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
238
                if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
239
                    $info['comments']['language'][] = $atom_structure['language'];
240
                }
241
                $this->CopyToAppropriateCommentsSection($atom_name, $atom_structure['data']);
242
                break;
243
244
            case 'play': // auto-PLAY atom
245
                $atom_structure['autoplay'] = (bool)getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
246
247
                $info_quicktime['autoplay'] = $atom_structure['autoplay'];
248
                break;
249
250
            case 'WLOC': // Window LOCation atom
251
                $atom_structure['location_x'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
252
                $atom_structure['location_y'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
253
                break;
254
255
            case 'LOOP': // LOOPing atom
256
            case 'SelO': // play SELection Only atom
257
            case 'AllF': // play ALL Frames atom
258
                $atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data);
259
                break;
260
261
            case 'name': //
262
            case 'MCPS': // Media Cleaner PRo
263
            case '@PRM': // adobe PReMiere version
264
            case '@PRQ': // adobe PRemiere Quicktime version
265
                $atom_structure['data'] = $atom_data;
266
                break;
267
268
            case 'cmvd': // Compressed MooV Data atom
269
                // Code by ubergeek�ubergeek*tv based on information from
270
                // http://developer.apple.com/quicktime/icefloe/dispatch012.html
271
                $atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
272
273
                $compressed_file_data = substr($atom_data, 4);
274
                if (!function_exists('gzuncompress')) {
275
                    $getid3->warning('PHP does not have zlib support - cannot decompress MOV atom at offset ' . $atom_structure['offset']);
276
                } elseif ($uncompressed_header = @gzuncompress($compressed_file_data)) {
277
                    $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($uncompressed_header, 0, $atom_hierarchy);
278
                } else {
279
                    $getid3->warning('Error decompressing compressed MOV atom at offset ' . $atom_structure['offset']);
280
                }
281
                break;
282
283
            case 'dcom': // Data COMpression atom
284
                $atom_structure['compression_id']   = $atom_data;
285
                $atom_structure['compression_text'] = getid3_quicktime::QuicktimeDCOMLookup($atom_data);
286
                break;
287
288
            case 'rdrf': // Reference movie Data ReFerence atom
289
                getid3_lib::ReadSequence(
290
                    'BigEndian2Int',
291
                    $atom_structure,
292
                    $atom_data,
293
                    0,
294
                    [
295
                        'version'             => 1,
296
                        'flags_raw'           => 3,
297
                        'reference_type_name' => -4,
298
                        'reference_length'    => 4,
299
                    ]
300
                );
301
302
                $atom_structure['flags']['internal_data'] = (bool)($atom_structure['flags_raw'] & 0x000001);
303
304
                switch ($atom_structure['reference_type_name']) {
305
                    case 'url ':
306
                        $atom_structure['url'] = $this->NoNullString(substr($atom_data, 12));
307
                        break;
308
309
                    case 'alis':
310
                        $atom_structure['file_alias'] = substr($atom_data, 12);
311
                        break;
312
313
                    case 'rsrc':
314
                        $atom_structure['resource_alias'] = substr($atom_data, 12);
315
                        break;
316
317
                    default:
318
                        $atom_structure['data'] = substr($atom_data, 12);
319
                        break;
320
                }
321
                break;
322
323
            case 'rmqu': // Reference Movie QUality atom
324
                $atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data);
325
                break;
326
327
            case 'rmcs': // Reference Movie Cpu Speed atom
328
                getid3_lib::ReadSequence(
329
                    'BigEndian2Int',
330
                    $atom_structure,
331
                    $atom_data,
332
                    0,
333
                    [
334
                        'version'          => 1,
335
                        'flags_raw'        => 3, // hardcoded: 0x0000
336
                        'cpu_speed_rating' => 2
337
                    ]
338
                );
339
                break;
340
341
            case 'rmvc': // Reference Movie Version Check atom
342
                getid3_lib::ReadSequence(
343
                    'BigEndian2Int',
344
                    $atom_structure,
345
                    $atom_data,
346
                    0,
347
                    [
348
                        'version'            => 1,
349
                        'flags_raw'          => 3, // hardcoded: 0x0000
350
                        'gestalt_selector'   => -4,
351
                        'gestalt_value_mask' => 4,
352
                        'gestalt_value'      => 4,
353
                        'gestalt_check_type' => 2
354
                    ]
355
                );
356
                break;
357
358
            case 'rmcd': // Reference Movie Component check atom
359
                getid3_lib::ReadSequence(
360
                    'BigEndian2Int',
361
                    $atom_structure,
362
                    $atom_data,
363
                    0,
364
                    [
365
                        'version'                => 1,
366
                        'flags_raw'              => 3, // hardcoded: 0x0000
367
                        'component_type'         => -4,
368
                        'component_subtype'      => -4,
369
                        'component_manufacturer' => -4,
370
                        'component_flags_raw'    => 4,
371
                        'component_flags_mask'   => 4,
372
                        'component_min_version'  => 4
373
                    ]
374
                );
375
                break;
376
377
            case 'rmdr': // Reference Movie Data Rate atom
378
                getid3_lib::ReadSequence(
379
                    'BigEndian2Int',
380
                    $atom_structure,
381
                    $atom_data,
382
                    0,
383
                    [
384
                        'version'   => 1,
385
                        'flags_raw' => 3, // hardcoded: 0x0000
386
                        'data_rate' => 4
387
                    ]
388
                );
389
390
                $atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10;
391
                break;
392
393
            case 'rmla': // Reference Movie Language Atom
394
                getid3_lib::ReadSequence(
395
                    'BigEndian2Int',
396
                    $atom_structure,
397
                    $atom_data,
398
                    0,
399
                    [
400
                        'version'     => 1,
401
                        'flags_raw'   => 3, // hardcoded: 0x0000
402
                        'language_id' => 2
403
                    ]
404
                );
405
406
                $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
407
                if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
408
                    $info['comments']['language'][] = $atom_structure['language'];
409
                }
410
                break;
411
412
            case 'rmla': // Reference Movie Language Atom
413
                getid3_lib::ReadSequence(
414
                    'BigEndian2Int',
415
                    $atom_structure,
416
                    $atom_data,
417
                    0,
418
                    [
419
                        'version'   => 1,
420
                        'flags_raw' => 3, // hardcoded: 0x0000
421
                        'track_id'  => 2
422
                    ]
423
                );
424
                break;
425
426
            case 'ptv ': // Print To Video - defines a movie's full screen mode
427
                // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm
428
                getid3_lib::ReadSequence(
429
                    'BigEndian2Int',
430
                    $atom_structure,
431
                    $atom_data,
432
                    0,
433
                    [
434
                        'display_size_raw'  => 2,
435
                        'reserved_1'        => 2, // hardcoded: 0x0000
436
                        'reserved_2'        => 2, // hardcoded: 0x0000
437
                        'slide_show_flag'   => 1,
438
                        'play_on_open_flag' => 1
439
                    ]
440
                );
441
442
                $atom_structure['flags']['play_on_open'] = (bool)$atom_structure['play_on_open_flag'];
443
                $atom_structure['flags']['slide_show']   = (bool)$atom_structure['slide_show_flag'];
444
445
                $ptv_lookup[0] = 'normal';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$ptv_lookup was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ptv_lookup = array(); before regardless.
Loading history...
446
                $ptv_lookup[1] = 'double';
447
                $ptv_lookup[2] = 'half';
448
                $ptv_lookup[3] = 'full';
449
                $ptv_lookup[4] = 'current';
450
                if (isset($ptv_lookup[$atom_structure['display_size_raw']])) {
451
                    $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
452
                } else {
453
                    $getid3->warning('unknown "ptv " display constant (' . $atom_structure['display_size_raw'] . ')');
454
                }
455
                break;
456
457
            case 'stsd': // Sample Table Sample Description atom
458
                getid3_lib::ReadSequence(
459
                    'BigEndian2Int',
460
                    $atom_structure,
461
                    $atom_data,
462
                    0,
463
                    [
464
                        'version'        => 1,
465
                        'flags_raw'      => 3, // hardcoded: 0x0000
466
                        'number_entries' => 4
467
                    ]
468
                );
469
                $stsd_entries_data_offset = 8;
470
                for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
471
                    getid3_lib::ReadSequence(
472
                        'BigEndian2Int',
473
                        $atom_structure['sample_description_table'][$i],
474
                        $atom_data,
475
                        $stsd_entries_data_offset,
476
                        [
477
                            'size'            => 4,
478
                            'data_format'     => -4,
479
                            'reserved'        => 6,
480
                            'reference_index' => 2
481
                        ]
482
                    );
483
484
                    $atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, 16 + $stsd_entries_data_offset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
485
                    $stsd_entries_data_offset                               += 16 + ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
486
487
                    getid3_lib::ReadSequence(
488
                        'BigEndian2Int',
489
                        $atom_structure['sample_description_table'][$i],
490
                        $atom_structure['sample_description_table'][$i]['data'],
491
                        0,
492
                        [
493
                            'encoder_version'  => 2,
494
                            'encoder_revision' => 2,
495
                            'encoder_vendor'   => -4
496
                        ]
497
                    );
498
499
                    switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) {
500
                        case "\x00\x00\x00\x00":
501
                            // audio atom
502
                            getid3_lib::ReadSequence(
503
                                'BigEndian2Int',
504
                                $atom_structure['sample_description_table'][$i],
505
                                $atom_structure['sample_description_table'][$i]['data'],
506
                                8,
507
                                [
508
                                    'audio_channels'       => 2,
509
                                    'audio_bit_depth'      => 2,
510
                                    'audio_compression_id' => 2,
511
                                    'audio_packet_size'    => 2
512
                                ]
513
                            );
514
515
                            $atom_structure['sample_description_table'][$i]['audio_sample_rate'] = getid3_quicktime::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16, 4));
516
517
                            switch ($atom_structure['sample_description_table'][$i]['data_format']) {
518
                                case 'mp4v':
519
                                    $info['fileformat'] = 'mp4';
520
                                    throw new getid3_exception('This version of getID3() does not fully support MPEG-4 audio/video streams');
521
522
                                case 'qtvr':
523
                                    $info['video']['dataformat'] = 'quicktimevr';
524
                                    break;
525
526
                                case 'mp4a':
527
                                default:
528
                                    $info_quicktime['audio']['codec']       = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
529
                                    $info_quicktime['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate'];
530
                                    $info_quicktime['audio']['channels']    = $atom_structure['sample_description_table'][$i]['audio_channels'];
531
                                    $info_quicktime['audio']['bit_depth']   = $atom_structure['sample_description_table'][$i]['audio_bit_depth'];
532
                                    $info['audio']['codec']                 = $info_quicktime['audio']['codec'];
533
                                    $info['audio']['sample_rate']           = $info_quicktime['audio']['sample_rate'];
534
                                    $info['audio']['channels']              = $info_quicktime['audio']['channels'];
535
                                    $info['audio']['bits_per_sample']       = $info_quicktime['audio']['bit_depth'];
536
                                    switch ($atom_structure['sample_description_table'][$i]['data_format']) {
537
                                        case 'raw ': // PCM
538
                                        case 'alac': // Apple Lossless Audio Codec
539
                                            $info['audio']['lossless'] = true;
540
                                            break;
541
                                        default:
542
                                            $info['audio']['lossless'] = false;
543
                                            break;
544
                                    }
545
                                    break;
546
                            }
547
                            break;
548
549
                        default:
550
                            switch ($atom_structure['sample_description_table'][$i]['data_format']) {
551
                                case 'mp4s':
552
                                    $info['fileformat'] = 'mp4';
553
                                    break;
554
555
                                default:
556
                                    // video atom
557
                                    getid3_lib::ReadSequence(
558
                                        'BigEndian2Int',
559
                                        $atom_structure['sample_description_table'][$i],
560
                                        $atom_structure['sample_description_table'][$i]['data'],
561
                                        8,
562
                                        [
563
                                            'video_temporal_quality' => 4,
564
                                            'video_spatial_quality'  => 4,
565
                                            'video_frame_width'      => 2,
566
                                            'video_frame_height'     => 2
567
                                        ]
568
                                    );
569
                                    $atom_structure['sample_description_table'][$i]['video_resolution_x'] = getid3_quicktime::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20, 4));
570
                                    $atom_structure['sample_description_table'][$i]['video_resolution_y'] = getid3_quicktime::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4));
571
                                    getid3_lib::ReadSequence(
572
                                        'BigEndian2Int',
573
                                        $atom_structure['sample_description_table'][$i],
574
                                        $atom_structure['sample_description_table'][$i]['data'],
575
                                        28,
576
                                        [
577
                                            'video_data_size'        => 4,
578
                                            'video_frame_count'      => 2,
579
                                            'video_encoder_name_len' => 1
580
                                        ]
581
                                    );
582
                                    $atom_structure['sample_description_table'][$i]['video_encoder_name']      = substr($atom_structure['sample_description_table'][$i]['data'], 35, $atom_structure['sample_description_table'][$i]['video_encoder_name_len']);
583
                                    $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66, 2));
584
                                    $atom_structure['sample_description_table'][$i]['video_color_table_id']    = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68, 2));
585
586
                                    $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color');
587
                                    $atom_structure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']);
588
589
                                    if ('invalid' != $atom_structure['sample_description_table'][$i]['video_pixel_color_name']) {
590
                                        $info_quicktime['video']['codec_fourcc']        = $atom_structure['sample_description_table'][$i]['data_format'];
591
                                        $info_quicktime['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
592
                                        $info_quicktime['video']['codec']               = $atom_structure['sample_description_table'][$i]['video_encoder_name'];
593
                                        $info_quicktime['video']['color_depth']         = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'];
594
                                        $info_quicktime['video']['color_depth_name']    = $atom_structure['sample_description_table'][$i]['video_pixel_color_name'];
595
596
                                        $info['video']['codec']           = $info_quicktime['video']['codec'];
597
                                        $info['video']['bits_per_sample'] = $info_quicktime['video']['color_depth'];
598
                                    }
599
                                    $info['video']['lossless']           = false;
600
                                    $info['video']['pixel_aspect_ratio'] = (float)1;
601
                                    break;
602
                            }
603
                            break;
604
                    }
605
                    switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) {
606
                        case 'mp4a':
607
                            $info['audio']['dataformat'] = $info_quicktime['audio']['codec'] = 'mp4';
608
                            break;
609
610
                        case '3ivx':
611
                        case '3iv1':
612
                        case '3iv2':
613
                            $info['video']['dataformat'] = '3ivx';
614
                            break;
615
616
                        case 'xvid':
617
                            $info['video']['dataformat'] = 'xvid';
618
                            break;
619
620
                        case 'mp4v':
621
                            $info['video']['dataformat'] = 'mpeg4';
622
                            break;
623
624
                        case 'divx':
625
                        case 'div1':
626
                        case 'div2':
627
                        case 'div3':
628
                        case 'div4':
629
                        case 'div5':
630
                        case 'div6':
631
                            //$TDIVXileInfo['video']['dataformat'] = 'divx';
632
                            break;
633
634
                        default:
635
                            // do nothing
636
                            break;
637
                    }
638
                    unset($atom_structure['sample_description_table'][$i]['data']);
639
                }
640
                break;
641
642
            case 'stts': // Sample Table Time-to-Sample atom
643
                getid3_lib::ReadSequence(
644
                    'BigEndian2Int',
645
                    $atom_structure,
646
                    $atom_data,
647
                    0,
648
                    [
649
                        'version'        => 1,
650
                        'flags_raw'      => 3, // hardcoded: 0x0000
651
                        'number_entries' => 4
652
                    ]
653
                );
654
655
                $stts_entries_data_offset    = 8;
656
                $frame_rate_calculator_array = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $frame_rate_calculator_array is dead and can be removed.
Loading history...
657
                for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
658
                    $atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $stts_entries_data_offset, 4));
659
                    $stts_entries_data_offset                                   += 4;
660
661
                    $atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $stts_entries_data_offset, 4));
662
                    $stts_entries_data_offset                                      += 4;
663
664
                    if (!empty($info_quicktime['time_scale']) && (@$atoms_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $atoms_structure does not exist. Did you maybe mean $atom_structure?
Loading history...
665
                        $stts_new_framerate = $info_quicktime['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'];
666
                        if ($stts_new_framerate <= 60) {
667
                            // some atoms have durations of "1" giving a very large framerate, which probably is not right
668
                            $info['video']['frame_rate'] = max(@$info['video']['frame_rate'], $stts_new_framerate);
669
                        }
670
                    }
671
                    //@$frame_rate_calculator_array[($info_quicktime['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count'];
672
                }
673
                /*
674
                $stts_frames_total  = 0;
675
                $stts_seconds_total = 0;
676
                foreach ($frame_rate_calculator_array as $frames_per_second => $frame_count) {
677
                    if (($frames_per_second > 60) || ($frames_per_second < 1)) {
678
                        // not video FPS information, probably audio information
679
                        $stts_frames_total  = 0;
680
                        $stts_seconds_total = 0;
681
                        break;
682
                    }
683
                    $stts_frames_total  += $frame_count;
684
                    $stts_seconds_total += $frame_count / $frames_per_second;
685
                }
686
                if (($stts_frames_total > 0) && ($stts_seconds_total > 0)) {
687
                    if (($stts_frames_total / $stts_seconds_total) > @$info['video']['frame_rate']) {
688
                        $info['video']['frame_rate'] = $stts_frames_total / $stts_seconds_total;
689
                    }
690
                }
691
                */
692
                break;
693
694
            case 'stss': // Sample Table Sync Sample (key frames) atom
695
                /*
696
                $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
697
                $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
698
                $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
699
                $stss_entries_data_offset = 8;
700
                for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
701
                    $atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stss_entries_data_offset, 4));
702
                    $stss_entries_data_offset += 4;
703
                }
704
                */ break;
705
706
            case 'stsc': // Sample Table Sample-to-Chunk atom
707
                /*
708
                $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
709
                $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
710
                $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
711
                $stsc_entries_data_offset = 8;
712
                for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
713
                    $atom_structure['sample_to_chunk_table'][$i]['first_chunk']        = getid3_lib::BigEndian2Int(substr($atom_data, $stsc_entries_data_offset, 4));
714
                    $stsc_entries_data_offset += 4;
715
                    $atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk']  = getid3_lib::BigEndian2Int(substr($atom_data, $stsc_entries_data_offset, 4));
716
                    $stsc_entries_data_offset += 4;
717
                    $atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsc_entries_data_offset, 4));
718
                    $stsc_entries_data_offset += 4;
719
                }
720
                */ break;
721
722
            case 'stsz': // Sample Table SiZe atom
723
                /*
724
                $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
725
                $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
726
                $atom_structure['sample_size']    = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
727
                $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
728
                $stsz_entries_data_offset = 12;
729
                if ($atom_structure['sample_size'] == 0) {
730
                    for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
731
                        $atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stsz_entries_data_offset, 4));
732
                        $stsz_entries_data_offset += 4;
733
                    }
734
                }
735
                */ break;
736
737
            case 'stco': // Sample Table Chunk Offset atom
738
                /*
739
                $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
740
                $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
741
                $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
742
                $stco_entries_data_offset = 8;
743
                for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
744
                    $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stco_entries_data_offset, 4));
745
                    $stco_entries_data_offset += 4;
746
                }
747
                */ break;
748
749
            case 'dref': // Data REFerence atom
750
                getid3_lib::ReadSequence(
751
                    'BigEndian2Int',
752
                    $atom_structure,
753
                    $atom_data,
754
                    0,
755
                    [
756
                        'version'        => 1,
757
                        'flags_raw'      => 3, // hardcoded: 0x0000
758
                        'number_entries' => 4
759
                    ]
760
                );
761
762
                $dref_data_offset = 8;
763
                for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
764
                    getid3_lib::ReadSequence(
765
                        'BigEndian2Int',
766
                        $atom_structure['data_references'][$i],
767
                        $atom_data,
768
                        $dref_data_offset,
769
                        [
770
                            'size'      => 4,
771
                            'type'      => -4,
772
                            'version'   => 1,
773
                            'flags_raw' => 3  // hardcoded: 0x0000
774
                        ]
775
                    );
776
                    $dref_data_offset += 12;
777
778
                    $atom_structure['data_references'][$i]['data'] = substr($atom_data, $dref_data_offset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3));
779
                    $dref_data_offset                              += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3);
780
781
                    $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool)($atom_structure['data_references'][$i]['flags_raw'] & 0x001);
782
                }
783
                break;
784
785
            case 'gmin': // base Media INformation atom
786
                getid3_lib::ReadSequence(
787
                    'BigEndian2Int',
788
                    $atom_structure,
789
                    $atom_data,
790
                    0,
791
                    [
792
                        'version'       => 1,
793
                        'flags_raw'     => 3, // hardcoded: 0x0000
794
                        'graphics_mode' => 2,
795
                        'opcolor_red'   => 2,
796
                        'opcolor_green' => 2,
797
                        'opcolor_blue'  => 2,
798
                        'balance'       => 2,
799
                        'reserved'      => 2
800
                    ]
801
                );
802
                break;
803
804
            case 'smhd': // Sound Media information HeaDer atom
805
                getid3_lib::ReadSequence(
806
                    'BigEndian2Int',
807
                    $atom_structure,
808
                    $atom_data,
809
                    0,
810
                    [
811
                        'version'   => 1,
812
                        'flags_raw' => 3, // hardcoded: 0x0000
813
                        'balance'   => 2,
814
                        'reserved'  => 2
815
                    ]
816
                );
817
                break;
818
819
            case 'vmhd': // Video Media information HeaDer atom
820
                getid3_lib::ReadSequence(
821
                    'BigEndian2Int',
822
                    $atom_structure,
823
                    $atom_data,
824
                    0,
825
                    [
826
                        'version'       => 1,
827
                        'flags_raw'     => 3,
828
                        'graphics_mode' => 2,
829
                        'opcolor_red'   => 2,
830
                        'opcolor_green' => 2,
831
                        'opcolor_blue'  => 2
832
                    ]
833
                );
834
                $atom_structure['flags']['no_lean_ahead'] = (bool)($atom_structure['flags_raw'] & 0x001);
835
                break;
836
837
            case 'hdlr': // HanDLeR reference atom
838
                getid3_lib::ReadSequence(
839
                    'BigEndian2Int',
840
                    $atom_structure,
841
                    $atom_data,
842
                    0,
843
                    [
844
                        'version'                => 1,
845
                        'flags_raw'              => 3, // hardcoded: 0x0000
846
                        'component_type'         => -4,
847
                        'component_subtype'      => -4,
848
                        'component_manufacturer' => -4,
849
                        'component_flags_raw'    => 4,
850
                        'component_flags_mask'   => 4
851
                    ]
852
                );
853
854
                $atom_structure['component_name'] = substr(substr($atom_data, 24), 1);       /// Pascal2String
855
856
                if (('STpn' == $atom_structure['component_subtype']) && ('zzzz' == $atom_structure['component_manufacturer'])) {
857
                    $info['video']['dataformat'] = 'quicktimevr';
858
                }
859
                break;
860
861
            case 'mdhd': // MeDia HeaDer atom
862
                getid3_lib::ReadSequence(
863
                    'BigEndian2Int',
864
                    $atom_structure,
865
                    $atom_data,
866
                    0,
867
                    [
868
                        'version'       => 1,
869
                        'flags_raw'     => 3, // hardcoded: 0x0000
870
                        'creation_time' => 4,
871
                        'modify_time'   => 4,
872
                        'time_scale'    => 4,
873
                        'duration'      => 4,
874
                        'language_id'   => 2,
875
                        'quality'       => 2
876
                    ]
877
                );
878
879
                if (0 == $atom_structure['time_scale']) {
880
                    throw new getid3_exception('Corrupt Quicktime file: mdhd.time_scale == zero');
881
                }
882
                $info_quicktime['time_scale'] = max(@$info['quicktime']['time_scale'], $atom_structure['time_scale']);
883
884
                $atom_structure['creation_time_unix'] = (int)($atom_structure['creation_time'] - 2082844800); // DateMac2Unix()
885
                $atom_structure['modify_time_unix']   = (int)($atom_structure['modify_time'] - 2082844800); // DateMac2Unix()
886
                $atom_structure['playtime_seconds']   = $atom_structure['duration'] / $atom_structure['time_scale'];
887
                $atom_structure['language']           = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
888
                if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
889
                    $info['comments']['language'][] = $atom_structure['language'];
890
                }
891
                break;
892
893
            case 'pnot': // Preview atom
894
                getid3_lib::ReadSequence(
895
                    'BigEndian2Int',
896
                    $atom_structure,
897
                    $atom_data,
898
                    0,
899
                    [
900
                        'modification_date' => 4,   // "standard Macintosh format"
901
                        'version_number'    => 2,   // hardcoded: 0x00
902
                        'atom_type'         => -4,  // usually: 'PICT'
903
                        'atom_index'        => 2    // usually: 0x01
904
                    ]
905
                );
906
                $atom_structure['modification_date_unix'] = (int)($atom_structure['modification_date'] - 2082844800); // DateMac2Unix()
907
                break;
908
909
            case 'crgn': // Clipping ReGioN atom
910
                $atom_structure['region_size']   = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); // The Region size, Region boundary box,
911
                $atom_structure['boundary_box']  = getid3_lib::BigEndian2Int(substr($atom_data, 2, 8)); // and Clipping region data fields
912
                $atom_structure['clipping_data'] = substr($atom_data, 10);           // constitute a QuickDraw region.
913
                break;
914
915
            case 'load': // track LOAD settings atom
916
                getid3_lib::ReadSequence(
917
                    'BigEndian2Int',
918
                    $atom_structure,
919
                    $atom_data,
920
                    0,
921
                    [
922
                        'preload_start_time' => 4,
923
                        'preload_duration'   => 4,
924
                        'preload_flags_raw'  => 4,
925
                        'default_hints_raw'  => 4
926
                    ]
927
                );
928
929
                $atom_structure['default_hints']['double_buffer'] = (bool)($atom_structure['default_hints_raw'] & 0x0020);
930
                $atom_structure['default_hints']['high_quality']  = (bool)($atom_structure['default_hints_raw'] & 0x0100);
931
                break;
932
933
            case 'tmcd': // TiMe CoDe atom
934
            case 'chap': // CHAPter list atom
935
            case 'sync': // SYNChronization atom
936
            case 'scpt': // tranSCriPT atom
937
            case 'ssrc': // non-primary SouRCe atom
938
                for ($i = 0; $i < (strlen($atom_data) % 4); $i++) {
939
                    $atom_structure['track_id'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $i * 4, 4));
940
                }
941
                break;
942
943
            case 'elst': // Edit LiST atom
944
                getid3_lib::ReadSequence(
945
                    'BigEndian2Int',
946
                    $atom_structure,
947
                    $atom_data,
948
                    0,
949
                    [
950
                        'version'        => 1,
951
                        'flags_raw'      => 3, // hardcoded: 0x0000
952
                        'number_entries' => 4
953
                    ]
954
                );
955
956
                for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
957
                    $atom_structure['edit_list'][$i]['track_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4));
958
                    $atom_structure['edit_list'][$i]['media_time']     = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4));
959
                    $atom_structure['edit_list'][$i]['media_rate']     = getid3_quicktime::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4));
960
                }
961
                break;
962
963
            case 'kmat': // compressed MATte atom
964
                $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
965
                $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
966
                $atom_structure['matte_data_raw'] = substr($atom_data, 4);
967
                break;
968
969
            case 'ctab': // Color TABle atom
970
                $atom_structure['color_table_seed']  = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // hardcoded: 0x00000000
971
                $atom_structure['color_table_flags'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x8000
972
                $atom_structure['color_table_size']  = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)) + 1;
973
                for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) {
974
                    $atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2));
975
                    $atom_structure['color_table'][$colortableentry]['red']   = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2));
976
                    $atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2));
977
                    $atom_structure['color_table'][$colortableentry]['blue']  = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2));
978
                }
979
                break;
980
981
            case 'mvhd': // MoVie HeaDer atom
982
                getid3_lib::ReadSequence(
983
                    'BigEndian2Int',
984
                    $atom_structure,
985
                    $atom_data,
986
                    0,
987
                    [
988
                        'version'       => 1,
989
                        'flags_raw'     => 3,
990
                        'creation_time' => 4,
991
                        'modify_time'   => 4,
992
                        'time_scale'    => 4,
993
                        'duration'      => 4
994
                    ]
995
                );
996
997
                $atom_structure['preferred_rate']   = getid3_quicktime::FixedPoint16_16(substr($atom_data, 20, 4));
998
                $atom_structure['preferred_volume'] = getid3_quicktime::FixedPoint8_8(substr($atom_data, 24, 2));
999
                $atom_structure['reserved']         = substr($atom_data, 26, 10);
1000
                $atom_structure['matrix_a']         = getid3_quicktime::FixedPoint16_16(substr($atom_data, 36, 4));
1001
                $atom_structure['matrix_b']         = getid3_quicktime::FixedPoint16_16(substr($atom_data, 40, 4));
1002
                $atom_structure['matrix_u']         = getid3_quicktime::FixedPoint2_30(substr($atom_data, 44, 4));
1003
                $atom_structure['matrix_c']         = getid3_quicktime::FixedPoint16_16(substr($atom_data, 48, 4));
1004
                $atom_structure['matrix_d']         = getid3_quicktime::FixedPoint16_16(substr($atom_data, 52, 4));
1005
                $atom_structure['matrix_v']         = getid3_quicktime::FixedPoint2_30(substr($atom_data, 56, 4));
1006
                $atom_structure['matrix_x']         = getid3_quicktime::FixedPoint16_16(substr($atom_data, 60, 4));
1007
                $atom_structure['matrix_y']         = getid3_quicktime::FixedPoint16_16(substr($atom_data, 64, 4));
1008
                $atom_structure['matrix_w']         = getid3_quicktime::FixedPoint2_30(substr($atom_data, 68, 4));
1009
1010
                getid3_lib::ReadSequence(
1011
                    'BigEndian2Int',
1012
                    $atom_structure,
1013
                    $atom_data,
1014
                    72,
1015
                    [
1016
                        'preview_time'       => 4,
1017
                        'preview_duration'   => 4,
1018
                        'poster_time'        => 4,
1019
                        'selection_time'     => 4,
1020
                        'selection_duration' => 4,
1021
                        'current_time'       => 4,
1022
                        'next_track_id'      => 4
1023
                    ]
1024
                );
1025
1026
                if (0 == $atom_structure['time_scale']) {
1027
                    throw new getid3_exception('Corrupt Quicktime file: mvhd.time_scale == zero');
1028
                }
1029
1030
                $atom_structure['creation_time_unix'] = (int)($atom_structure['creation_time'] - 2082844800); // DateMac2Unix()
1031
                $atom_structure['modify_time_unix']   = (int)($atom_structure['modify_time'] - 2082844800); // DateMac2Unix()
1032
                $info_quicktime['time_scale']         = max(@$info['quicktime']['time_scale'], $atom_structure['time_scale']);
1033
                $info_quicktime['display_scale']      = $atom_structure['matrix_a'];
1034
                $info['playtime_seconds']             = $atom_structure['duration'] / $atom_structure['time_scale'];
1035
                break;
1036
1037
            case 'tkhd': // TracK HeaDer atom
1038
                getid3_lib::ReadSequence(
1039
                    'BigEndian2Int',
1040
                    $atom_structure,
1041
                    $atom_data,
1042
                    0,
1043
                    [
1044
                        'version'         => 1,
1045
                        'flags_raw'       => 3,
1046
                        'creation_time'   => 4,
1047
                        'modify_time'     => 4,
1048
                        'trackid'         => 4,
1049
                        'reserved1'       => 4,
1050
                        'duration'        => 4,
1051
                        'reserved2'       => 8,
1052
                        'layer'           => 2,
1053
                        'alternate_group' => 2
1054
                    ]
1055
                );
1056
1057
                $atom_structure['volume']    = getid3_quicktime::FixedPoint8_8(substr($atom_data, 36, 2));
1058
                $atom_structure['reserved3'] = getid3_lib::BigEndian2Int(substr($atom_data, 38, 2));
1059
                $atom_structure['matrix_a']  = getid3_quicktime::FixedPoint16_16(substr($atom_data, 40, 4));
1060
                $atom_structure['matrix_b']  = getid3_quicktime::FixedPoint16_16(substr($atom_data, 44, 4));
1061
                $atom_structure['matrix_u']  = getid3_quicktime::FixedPoint16_16(substr($atom_data, 48, 4));
1062
                $atom_structure['matrix_c']  = getid3_quicktime::FixedPoint16_16(substr($atom_data, 52, 4));
1063
                $atom_structure['matrix_v']  = getid3_quicktime::FixedPoint16_16(substr($atom_data, 56, 4));
1064
                $atom_structure['matrix_d']  = getid3_quicktime::FixedPoint16_16(substr($atom_data, 60, 4));
1065
                $atom_structure['matrix_x']  = getid3_quicktime::FixedPoint2_30(substr($atom_data, 64, 4));
1066
                $atom_structure['matrix_y']  = getid3_quicktime::FixedPoint2_30(substr($atom_data, 68, 4));
1067
                $atom_structure['matrix_w']  = getid3_quicktime::FixedPoint2_30(substr($atom_data, 72, 4));
1068
                $atom_structure['width']     = getid3_quicktime::FixedPoint16_16(substr($atom_data, 76, 4));
1069
                $atom_structure['height']    = getid3_quicktime::FixedPoint16_16(substr($atom_data, 80, 4));
1070
1071
                $atom_structure['flags']['enabled']    = (bool)($atom_structure['flags_raw'] & 0x0001);
1072
                $atom_structure['flags']['in_movie']   = (bool)($atom_structure['flags_raw'] & 0x0002);
1073
                $atom_structure['flags']['in_preview'] = (bool)($atom_structure['flags_raw'] & 0x0004);
1074
                $atom_structure['flags']['in_poster']  = (bool)($atom_structure['flags_raw'] & 0x0008);
1075
                $atom_structure['creation_time_unix']  = (int)($atom_structure['creation_time'] - 2082844800); // DateMac2Unix()
1076
                $atom_structure['modify_time_unix']    = (int)($atom_structure['modify_time'] - 2082844800); // DateMac2Unix()
1077
1078
                if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) {
1079
                    $info['video']['resolution_x'] = $atom_structure['width'];
1080
                    $info['video']['resolution_y'] = $atom_structure['height'];
1081
                }
1082
1083
                if (1 == $atom_structure['flags']['enabled']) {
1084
                    $info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']);
1085
                    $info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']);
1086
                }
1087
1088
                if (!empty($info['video']['resolution_x']) && !empty($info['video']['resolution_y'])) {
1089
                    $info_quicktime['video']['resolution_x'] = $info['video']['resolution_x'];
1090
                    $info_quicktime['video']['resolution_y'] = $info['video']['resolution_y'];
1091
                } else {
1092
                    unset($info['video']['resolution_x']);
1093
                    unset($info['video']['resolution_y']);
1094
                    unset($info_quicktime['video']);
1095
                }
1096
                break;
1097
1098
            case 'meta': // METAdata atom
1099
                // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
1100
                $next_tag_position = strpos($atom_data, '�');
1101
                while ($next_tag_position < strlen($atom_data)) {
1102
                    $meta_item_size = getid3_lib::BigEndian2Int(substr($atom_data, $next_tag_position - 4, 4)) - 4;
1103
                    if (-4 == $meta_item_size) {
1104
                        break;
1105
                    }
1106
                    $meta_item_raw     = substr($atom_data, $next_tag_position, $meta_item_size);
1107
                    $meta_item_key     = substr($meta_item_raw, 0, 4);
1108
                    $meta_item_data    = substr($meta_item_raw, 20);
1109
                    $next_tag_position += $meta_item_size + 4;
1110
1111
                    $this->CopyToAppropriateCommentsSection($meta_item_key, $meta_item_data);
1112
                }
1113
                break;
1114
1115
            case 'ftyp': // FileTYPe (?) atom (for MP4 it seems)
1116
                getid3_lib::ReadSequence(
1117
                    'BigEndian2Int',
1118
                    $atom_structure,
1119
                    $atom_data,
1120
                    0,
1121
                    [
1122
                        'signature' => -4,
1123
                        'unknown_1' => 4,
1124
                        'fourcc'    => -4,
1125
                    ]
1126
                );
1127
                break;
1128
1129
            case 'mdat': // Media DATa atom
1130
            case 'free': // FREE space atom
1131
            case 'skip': // SKIP atom
1132
            case 'wide': // 64-bit expansion placeholder atom
1133
                // 'mdat' data is too big to deal with, contains no useful metadata
1134
                // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
1135
1136
                // When writing QuickTime files, it is sometimes necessary to update an atom's size.
1137
                // It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom
1138
                // is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime
1139
                // puts an 8-byte placeholder atom before any atoms it may have to update the size of.
1140
                // In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the
1141
                // placeholder atom can be overwritten to obtain the necessary 8 extra bytes.
1142
                // The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ).
1143
                break;
1144
1145
            case 'nsav': // NoSAVe atom
1146
                // http://developer.apple.com/technotes/tn/tn2038.html
1147
                $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
1148
                break;
1149
1150
            case 'ctyp': // Controller TYPe atom (seen on QTVR)
1151
                // http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
1152
                // some controller names are:
1153
                //   0x00 + 'std' for linear movie
1154
                //   'none' for no controls
1155
                $atom_structure['ctyp'] = substr($atom_data, 0, 4);
1156
                switch ($atom_structure['ctyp']) {
1157
                    case 'qtvr':
1158
                        $info['video']['dataformat'] = 'quicktimevr';
1159
                        break;
1160
                }
1161
                break;
1162
1163
            case 'pano': // PANOrama track (seen on QTVR)
1164
                $atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
1165
                break;
1166
1167
            case 'hint': // HINT track
1168
            case 'hinf': //
1169
            case 'hinv': //
1170
            case 'hnti': //
1171
                $info['quicktime']['hinting'] = true;
1172
                break;
1173
1174
            case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR)
1175
                for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) {
1176
                    $atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
1177
                }
1178
                break;
1179
1180
            case 'FXTC': // Something to do with Adobe After Effects (?)
1181
            case 'PrmA':
1182
            case 'code':
1183
            case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html
1184
                // Observed-but-not-handled atom types are just listed here
1185
                // to prevent warnings being generated
1186
                $atom_structure['data'] = $atom_data;
1187
                break;
1188
1189
            default:
1190
                $getid3->warning('Unknown QuickTime atom type: "' . $atom_name . '" at offset ' . $base_offset);
1191
                $atom_structure['data'] = $atom_data;
1192
                break;
1193
        }
1194
        array_pop($atom_hierarchy);
1195
        return $atom_structure;
1196
    }
1197
1198
    private function QuicktimeParseContainerAtom($atom_data, $base_offset, &$atom_hierarchy)
1199
    {
1200
        if ((4 == strlen($atom_data)) && (0x00000000 == getid3_lib::BigEndian2Int($atom_data))) {
1201
            return false;
1202
        }
1203
1204
        $atom_structure = false;
1205
        $subatom_offset = 0;
1206
1207
        while ($subatom_offset < strlen($atom_data)) {
1208
            $subatom_size = getid3_lib::BigEndian2Int(substr($atom_data, $subatom_offset + 0, 4));
1209
            $subatom_name = substr($atom_data, $subatom_offset + 4, 4);
1210
            $subatom_data = substr($atom_data, $subatom_offset + 8, $subatom_size - 8);
1211
1212
            if (0 == $subatom_size) {
1213
                // Furthermore, for historical reasons the list of atoms is optionally
1214
                // terminated by a 32-bit integer set to 0. If you are writing a program
1215
                // to read user data atoms, you should allow for the terminating 0.
1216
                return $atom_structure;
1217
            }
1218
1219
            $atom_structure[] = $this->QuicktimeParseAtom($subatom_name, $subatom_size, $subatom_data, $base_offset + $subatom_offset, $atom_hierarchy);
1220
1221
            $subatom_offset += $subatom_size;
1222
        }
1223
        return $atom_structure;
1224
    }
1225
1226
    private function CopyToAppropriateCommentsSection($key_name, $data)
1227
    {
1228
        // http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
1229
1230
        static $translator = [
1231
            '�cpy' => 'copyright',
1232
            '�day' => 'creation_date',
1233
            '�dir' => 'director',
1234
            '�ed1' => 'edit1',
1235
            '�ed2' => 'edit2',
1236
            '�ed3' => 'edit3',
1237
            '�ed4' => 'edit4',
1238
            '�ed5' => 'edit5',
1239
            '�ed6' => 'edit6',
1240
            '�ed7' => 'edit7',
1241
            '�ed8' => 'edit8',
1242
            '�ed9' => 'edit9',
1243
            '�fmt' => 'format',
1244
            '�inf' => 'information',
1245
            '�prd' => 'producer',
1246
            '�prf' => 'performers',
1247
            '�req' => 'system_requirements',
1248
            '�src' => 'source_credit',
1249
            '�wrt' => 'writer',
1250
            '�nam' => 'title',
1251
            '�cmt' => 'comment',
1252
            '�wrn' => 'warning',
1253
            '�hst' => 'host_computer',
1254
            '�mak' => 'make',
1255
            '�mod' => 'model',
1256
            '�PRD' => 'product',
1257
            '�swr' => 'software',
1258
            '�aut' => 'author',
1259
            '�ART' => 'artist',
1260
            '�trk' => 'track',
1261
            '�alb' => 'album',
1262
            '�com' => 'comment',
1263
            '�gen' => 'genre',
1264
            '�ope' => 'composer',
1265
            '�url' => 'url',
1266
            '�enc' => 'encoder'
1267
        ];
1268
1269
        if (isset($translator[$key_name])) {
1270
            $this->getid3->info['quicktime']['comments'][$translator[$key_name]][] = $data;
1271
        }
1272
1273
        return true;
1274
    }
1275
1276
    public static function QuicktimeLanguageLookup($language_id)
1277
    {
1278
        static $lookup = [
1279
            0   => 'English',
1280
            1   => 'French',
1281
            2   => 'German',
1282
            3   => 'Italian',
1283
            4   => 'Dutch',
1284
            5   => 'Swedish',
1285
            6   => 'Spanish',
1286
            7   => 'Danish',
1287
            8   => 'Portuguese',
1288
            9   => 'Norwegian',
1289
            10  => 'Hebrew',
1290
            11  => 'Japanese',
1291
            12  => 'Arabic',
1292
            13  => 'Finnish',
1293
            14  => 'Greek',
1294
            15  => 'Icelandic',
1295
            16  => 'Maltese',
1296
            17  => 'Turkish',
1297
            18  => 'Croatian',
1298
            19  => 'Chinese (Traditional)',
1299
            20  => 'Urdu',
1300
            21  => 'Hindi',
1301
            22  => 'Thai',
1302
            23  => 'Korean',
1303
            24  => 'Lithuanian',
1304
            25  => 'Polish',
1305
            26  => 'Hungarian',
1306
            27  => 'Estonian',
1307
            28  => 'Lettish',
1308
            28  => 'Latvian',
1309
            29  => 'Saamisk',
1310
            29  => 'Lappish',
1311
            30  => 'Faeroese',
1312
            31  => 'Farsi',
1313
            31  => 'Persian',
1314
            32  => 'Russian',
1315
            33  => 'Chinese (Simplified)',
1316
            34  => 'Flemish',
1317
            35  => 'Irish',
1318
            36  => 'Albanian',
1319
            37  => 'Romanian',
1320
            38  => 'Czech',
1321
            39  => 'Slovak',
1322
            40  => 'Slovenian',
1323
            41  => 'Yiddish',
1324
            42  => 'Serbian',
1325
            43  => 'Macedonian',
1326
            44  => 'Bulgarian',
1327
            45  => 'Ukrainian',
1328
            46  => 'Byelorussian',
1329
            47  => 'Uzbek',
1330
            48  => 'Kazakh',
1331
            49  => 'Azerbaijani',
1332
            50  => 'AzerbaijanAr',
1333
            51  => 'Armenian',
1334
            52  => 'Georgian',
1335
            53  => 'Moldavian',
1336
            54  => 'Kirghiz',
1337
            55  => 'Tajiki',
1338
            56  => 'Turkmen',
1339
            57  => 'Mongolian',
1340
            58  => 'MongolianCyr',
1341
            59  => 'Pashto',
1342
            60  => 'Kurdish',
1343
            61  => 'Kashmiri',
1344
            62  => 'Sindhi',
1345
            63  => 'Tibetan',
1346
            64  => 'Nepali',
1347
            65  => 'Sanskrit',
1348
            66  => 'Marathi',
1349
            67  => 'Bengali',
1350
            68  => 'Assamese',
1351
            69  => 'Gujarati',
1352
            70  => 'Punjabi',
1353
            71  => 'Oriya',
1354
            72  => 'Malayalam',
1355
            73  => 'Kannada',
1356
            74  => 'Tamil',
1357
            75  => 'Telugu',
1358
            76  => 'Sinhalese',
1359
            77  => 'Burmese',
1360
            78  => 'Khmer',
1361
            79  => 'Lao',
1362
            80  => 'Vietnamese',
1363
            81  => 'Indonesian',
1364
            82  => 'Tagalog',
1365
            83  => 'MalayRoman',
1366
            84  => 'MalayArabic',
1367
            85  => 'Amharic',
1368
            86  => 'Tigrinya',
1369
            87  => 'Galla',
1370
            87  => 'Oromo',
1371
            88  => 'Somali',
1372
            89  => 'Swahili',
1373
            90  => 'Ruanda',
1374
            91  => 'Rundi',
1375
            92  => 'Chewa',
1376
            93  => 'Malagasy',
1377
            94  => 'Esperanto',
1378
            128 => 'Welsh',
1379
            129 => 'Basque',
1380
            130 => 'Catalan',
1381
            131 => 'Latin',
1382
            132 => 'Quechua',
1383
            133 => 'Guarani',
1384
            134 => 'Aymara',
1385
            135 => 'Tatar',
1386
            136 => 'Uighur',
1387
            137 => 'Dzongkha',
1388
            138 => 'JavaneseRom'
1389
        ];
1390
1391
        return (isset($lookup[$language_id]) ? $lookup[$language_id] : 'invalid');
1392
    }
1393
1394
    public static function QuicktimeVideoCodecLookup($codec_id)
1395
    {
1396
        static $lookup = [
1397
            '3IVX' => '3ivx MPEG-4',
1398
            '3IV1' => '3ivx MPEG-4 v1',
1399
            '3IV2' => '3ivx MPEG-4 v2',
1400
            'avr ' => 'AVR-JPEG',
1401
            'base' => 'Base',
1402
            'WRLE' => 'BMP',
1403
            'cvid' => 'Cinepak',
1404
            'clou' => 'Cloud',
1405
            'cmyk' => 'CMYK',
1406
            'yuv2' => 'ComponentVideo',
1407
            'yuvu' => 'ComponentVideoSigned',
1408
            'yuvs' => 'ComponentVideoUnsigned',
1409
            'dvc ' => 'DVC-NTSC',
1410
            'dvcp' => 'DVC-PAL',
1411
            'dvpn' => 'DVCPro-NTSC',
1412
            'dvpp' => 'DVCPro-PAL',
1413
            'fire' => 'Fire',
1414
            'flic' => 'FLC',
1415
            'b48r' => '48RGB',
1416
            'gif ' => 'GIF',
1417
            'smc ' => 'Graphics',
1418
            'h261' => 'H261',
1419
            'h263' => 'H263',
1420
            'IV41' => 'Indeo4',
1421
            'jpeg' => 'JPEG',
1422
            'PNTG' => 'MacPaint',
1423
            'msvc' => 'Microsoft Video1',
1424
            'mjpa' => 'Motion JPEG-A',
1425
            'mjpb' => 'Motion JPEG-B',
1426
            'myuv' => 'MPEG YUV420',
1427
            'dmb1' => 'OpenDML JPEG',
1428
            'kpcd' => 'PhotoCD',
1429
            '8BPS' => 'Planar RGB',
1430
            'png ' => 'PNG',
1431
            'qdrw' => 'QuickDraw',
1432
            'qdgx' => 'QuickDrawGX',
1433
            'raw ' => 'RAW',
1434
            '.SGI' => 'SGI',
1435
            'b16g' => '16Gray',
1436
            'b64a' => '64ARGB',
1437
            'SVQ1' => 'Sorenson Video 1',
1438
            'SVQ1' => 'Sorenson Video 3',
1439
            'syv9' => 'Sorenson YUV9',
1440
            'tga ' => 'Targa',
1441
            'b32a' => '32AlphaGray',
1442
            'tiff' => 'TIFF',
1443
            'path' => 'Vector',
1444
            'rpza' => 'Video',
1445
            'ripl' => 'WaterRipple',
1446
            'WRAW' => 'Windows RAW',
1447
            'y420' => 'YUV420'
1448
        ];
1449
1450
        return (isset($lookup[$codec_id]) ? $lookup[$codec_id] : '');
1451
    }
1452
1453
    public static function QuicktimeAudioCodecLookup($codec_id)
1454
    {
1455
        static $lookup = [
1456
            '.mp3'        => 'Fraunhofer MPEG Layer-III alias',
1457
            'aac '        => 'ISO/IEC 14496-3 AAC',
1458
            'agsm'        => 'Apple GSM 10:1',
1459
            'alac'        => 'Apple Lossless Audio Codec',
1460
            'alaw'        => 'A-law 2:1',
1461
            'conv'        => 'Sample Format',
1462
            'dvca'        => 'DV',
1463
            'dvi '        => 'DV 4:1',
1464
            'eqal'        => 'Frequency Equalizer',
1465
            'fl32'        => '32-bit Floating Point',
1466
            'fl64'        => '64-bit Floating Point',
1467
            'ima4'        => 'Interactive Multimedia Association 4:1',
1468
            'in24'        => '24-bit Integer',
1469
            'in32'        => '32-bit Integer',
1470
            'lpc '        => 'LPC 23:1',
1471
            'MAC3'        => 'Macintosh Audio Compression/Expansion (MACE) 3:1',
1472
            'MAC6'        => 'Macintosh Audio Compression/Expansion (MACE) 6:1',
1473
            'mixb'        => '8-bit Mixer',
1474
            'mixw'        => '16-bit Mixer',
1475
            'mp4a'        => 'ISO/IEC 14496-3 AAC',
1476
            "MS'\x00\x02" => 'Microsoft ADPCM',
1477
            "MS'\x00\x11" => 'DV IMA',
1478
            "MS\x00\x55"  => 'Fraunhofer MPEG Layer III',
1479
            'NONE'        => 'No Encoding',
1480
            'Qclp'        => 'Qualcomm PureVoice',
1481
            'QDM2'        => 'QDesign Music 2',
1482
            'QDMC'        => 'QDesign Music 1',
1483
            'ratb'        => '8-bit Rate',
1484
            'ratw'        => '16-bit Rate',
1485
            'raw '        => 'raw PCM',
1486
            'sour'        => 'Sound Source',
1487
            'sowt'        => 'signed/two\'s complement (Little Endian)',
1488
            'str1'        => 'Iomega MPEG layer II',
1489
            'str2'        => 'Iomega MPEG *layer II',
1490
            'str3'        => 'Iomega MPEG **layer II',
1491
            'str4'        => 'Iomega MPEG ***layer II',
1492
            'twos'        => 'signed/two\'s complement (Big Endian)',
1493
            'ulaw'        => 'mu-law 2:1',
1494
        ];
1495
1496
        return (isset($lookup[$codec_id]) ? $lookup[$codec_id] : '');
1497
    }
1498
1499
    public static function QuicktimeDCOMLookup($compression_id)
1500
    {
1501
        static $lookup = [
1502
            'zlib' => 'ZLib Deflate',
1503
            'adec' => 'Apple Compression'
1504
        ];
1505
1506
        return (isset($lookup[$compression_id]) ? $lookup[$compression_id] : '');
1507
    }
1508
1509
    public static function QuicktimeColorNameLookup($color_depth_id)
1510
    {
1511
        static $lookup = [
1512
            1  => '2-color (monochrome)',
1513
            2  => '4-color',
1514
            4  => '16-color',
1515
            8  => '256-color',
1516
            16 => 'thousands (16-bit color)',
1517
            24 => 'millions (24-bit color)',
1518
            32 => 'millions+ (32-bit color)',
1519
            33 => 'black & white',
1520
            34 => '4-gray',
1521
            36 => '16-gray',
1522
            40 => '256-gray',
1523
        ];
1524
1525
        return (isset($lookup[$color_depth_id]) ? $lookup[$color_depth_id] : 'invalid');
1526
    }
1527
1528
    public static function NoNullString($null_terminated_string)
1529
    {
1530
        // remove the single null terminator on null terminated strings
1531
        if ("\x00" === substr($null_terminated_string, strlen($null_terminated_string) - 1, 1)) {
1532
            return substr($null_terminated_string, 0, strlen($null_terminated_string) - 1);
1533
        }
1534
1535
        return $null_terminated_string;
1536
    }
1537
1538
    public static function FixedPoint8_8($raw_data)
1539
    {
1540
        return getid3_lib::BigEndian2Int($raw_data{0}) + (float)(getid3_lib::BigEndian2Int($raw_data{1}) / 256);
1541
    }
1542
1543
    public static function FixedPoint16_16($raw_data)
1544
    {
1545
        return getid3_lib::BigEndian2Int(substr($raw_data, 0, 2)) + (float)(getid3_lib::BigEndian2Int(substr($raw_data, 2, 2)) / 65536);
1546
    }
1547
1548
    public static function FixedPoint2_30($raw_data)
1549
    {
1550
        $binary_string = getid3_lib::BigEndian2Bin($raw_data);
1551
        return bindec(substr($binary_string, 0, 2)) + (float)(bindec(substr($binary_string, 2, 30)) / 1073741824);
1552
    }
1553
1554
}
1555
1556
1557