Completed
Push — vendor/getid3 ( 2a5d73...44d59b )
by Pauli
03:12
created

getid3_quicktime::QuicktimeDCOMLookup()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 4
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
/////////////////////////////////////////////////////////////////
4
/// getID3() by James Heinrich <[email protected]>               //
5
//  available at https://github.com/JamesHeinrich/getID3       //
6
//            or https://www.getid3.org                        //
7
//            or http://getid3.sourceforge.net                 //
8
//  see readme.txt for more details                            //
9
/////////////////////////////////////////////////////////////////
10
//                                                             //
11
// module.audio-video.quicktime.php                            //
12
// module for analyzing Quicktime and MP3-in-MP4 files         //
13
// dependencies: module.audio.mp3.php                          //
14
// dependencies: module.tag.id3v2.php                          //
15
//                                                            ///
16
/////////////////////////////////////////////////////////////////
17
18
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
19
	exit;
20
}
21
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
22
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); // needed for ISO 639-2 language code lookup
23
24
class getid3_quicktime extends getid3_handler
25
{
26
27
	/** audio-video.quicktime
28
	 * return all parsed data from all atoms if true, otherwise just returned parsed metadata
29
	 *
30
	 * @var bool
31
	 */
32
	public $ReturnAtomData        = true;
33
34
	/** audio-video.quicktime
35
	 * return all parsed data from all atoms if true, otherwise just returned parsed metadata
36
	 *
37
	 * @var bool
38
	 */
39
	public $ParseAllPossibleAtoms = false;
40
41
	/**
42
	 * @return bool
43
	 */
44
	public function Analyze() {
45
		$info = &$this->getid3->info;
46
47
		$info['fileformat'] = 'quicktime';
48
		$info['quicktime']['hinting']    = false;
49
		$info['quicktime']['controller'] = 'standard'; // may be overridden if 'ctyp' atom is present
50
51
		$this->fseek($info['avdataoffset']);
52
53
		$offset      = 0;
54
		$atomcounter = 0;
55
		$atom_data_read_buffer_size = $info['php_memory_limit'] ? round($info['php_memory_limit'] / 4) : $this->getid3->option_fread_buffer_size * 1024; // set read buffer to 25% of PHP memory limit (if one is specified), otherwise use option_fread_buffer_size [default: 32MB]
56
		while ($offset < $info['avdataend']) {
57
			if (!getid3_lib::intValueSupported($offset)) {
58
				$this->error('Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions');
59
				break;
60
			}
61
			$this->fseek($offset);
62
			$AtomHeader = $this->fread(8);
63
64
			$atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4));
65
			$atomname = substr($AtomHeader, 4, 4);
66
67
			// 64-bit MOV patch by jlegateØktnc*com
68
			if ($atomsize == 1) {
69
				$atomsize = getid3_lib::BigEndian2Int($this->fread(8));
0 ignored issues
show
Security Bug introduced by
It seems like $this->fread(8) targeting getid3_handler::fread() can also be of type false; however, getid3_lib::BigEndian2Int() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
70
			}
71
72
			if (($offset + $atomsize) > $info['avdataend']) {
73
				$info['quicktime'][$atomname]['name']   = $atomname;
74
				$info['quicktime'][$atomname]['size']   = $atomsize;
75
				$info['quicktime'][$atomname]['offset'] = $offset;
76
				$this->error('Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)');
77
				return false;
78
			}
79
			if ($atomsize == 0) {
80
				// Furthermore, for historical reasons the list of atoms is optionally
81
				// terminated by a 32-bit integer set to 0. If you are writing a program
82
				// to read user data atoms, you should allow for the terminating 0.
83
				$info['quicktime'][$atomname]['name']   = $atomname;
84
				$info['quicktime'][$atomname]['size']   = $atomsize;
85
				$info['quicktime'][$atomname]['offset'] = $offset;
86
				break;
87
			}
88
89
			$atomHierarchy = array();
90
			$parsedAtomData = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, $atom_data_read_buffer_size)), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
0 ignored issues
show
Bug introduced by
It seems like $atomsize can also be of type double or false; however, getid3_quicktime::QuicktimeParseAtom() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
It seems like min($atomsize, $atom_data_read_buffer_size) targeting min() can also be of type double or false; however, getid3_handler::fread() does only seem to accept integer, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Security Bug introduced by
It seems like $this->fread(min($atomsi...data_read_buffer_size)) targeting getid3_handler::fread() can also be of type false; however, getid3_quicktime::QuicktimeParseAtom() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
91
			$parsedAtomData['name']   = $atomname;
92
			$parsedAtomData['size']   = $atomsize;
93
			$parsedAtomData['offset'] = $offset;
94
			if (in_array($atomname, array('uuid'))) {
95
				@$info['quicktime'][$atomname][] = $parsedAtomData;
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
96
			} else {
97
				$info['quicktime'][$atomname] = $parsedAtomData;
98
			}
99
100
			$offset += $atomsize;
101
			$atomcounter++;
102
		}
103
104
		if (!empty($info['avdataend_tmp'])) {
105
			// this value is assigned to a temp value and then erased because
106
			// otherwise any atoms beyond the 'mdat' atom would not get parsed
107
			$info['avdataend'] = $info['avdataend_tmp'];
108
			unset($info['avdataend_tmp']);
109
		}
110
111
		if (!empty($info['quicktime']['comments']['chapters']) && is_array($info['quicktime']['comments']['chapters']) && (count($info['quicktime']['comments']['chapters']) > 0)) {
112
			$durations = $this->quicktime_time_to_sample_table($info);
113
			for ($i = 0; $i < count($info['quicktime']['comments']['chapters']); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
114
				$bookmark = array();
115
				$bookmark['title'] = $info['quicktime']['comments']['chapters'][$i];
116
				if (isset($durations[$i])) {
117
					$bookmark['duration_sample'] = $durations[$i]['sample_duration'];
118
					if ($i > 0) {
119
						$bookmark['start_sample'] = $info['quicktime']['bookmarks'][($i - 1)]['start_sample'] + $info['quicktime']['bookmarks'][($i - 1)]['duration_sample'];
120
					} else {
121
						$bookmark['start_sample'] = 0;
122
					}
123
					if ($time_scale = $this->quicktime_bookmark_time_scale($info)) {
124
						$bookmark['duration_seconds'] = $bookmark['duration_sample'] / $time_scale;
125
						$bookmark['start_seconds']    = $bookmark['start_sample']    / $time_scale;
126
					}
127
				}
128
				$info['quicktime']['bookmarks'][] = $bookmark;
129
			}
130
		}
131
132
		if (isset($info['quicktime']['temp_meta_key_names'])) {
133
			unset($info['quicktime']['temp_meta_key_names']);
134
		}
135
136
		if (!empty($info['quicktime']['comments']['location.ISO6709'])) {
137
			// https://en.wikipedia.org/wiki/ISO_6709
138
			foreach ($info['quicktime']['comments']['location.ISO6709'] as $ISO6709string) {
139
				$ISO6709parsed = array('latitude'=>false, 'longitude'=>false, 'altitude'=>false);
140
				if (preg_match('#^([\\+\\-])([0-9]{2}|[0-9]{4}|[0-9]{6})(\\.[0-9]+)?([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?(([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?)?/$#', $ISO6709string, $matches)) {
141
					@list($dummy, $lat_sign, $lat_deg, $lat_deg_dec, $lon_sign, $lon_deg, $lon_deg_dec, $dummy, $alt_sign, $alt_deg, $alt_deg_dec) = $matches;
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
142
143 View Code Duplication
					if (strlen($lat_deg) == 2) {        // [+-]DD.D
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
144
						$ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * floatval(ltrim($lat_deg, '0').$lat_deg_dec);
145
					} elseif (strlen($lat_deg) == 4) {  // [+-]DDMM.M
146
						$ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0').$lat_deg_dec / 60);
147
					} elseif (strlen($lat_deg) == 6) {  // [+-]DDMMSS.S
148
						$ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lat_deg, 4, 2), '0').$lat_deg_dec / 3600);
149
					}
150
151 View Code Duplication
					if (strlen($lon_deg) == 3) {        // [+-]DDD.D
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
152
						$ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * floatval(ltrim($lon_deg, '0').$lon_deg_dec);
153
					} elseif (strlen($lon_deg) == 5) {  // [+-]DDDMM.M
154
						$ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0').$lon_deg_dec / 60);
155
					} elseif (strlen($lon_deg) == 7) {  // [+-]DDDMMSS.S
156
						$ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lon_deg, 4, 2), '0').$lon_deg_dec / 3600);
157
					}
158
159 View Code Duplication
					if (strlen($alt_deg) == 3) {        // [+-]DDD.D
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
160
						$ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * floatval(ltrim($alt_deg, '0').$alt_deg_dec);
161
					} elseif (strlen($alt_deg) == 5) {  // [+-]DDDMM.M
162
						$ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0').$alt_deg_dec / 60);
163
					} elseif (strlen($alt_deg) == 7) {  // [+-]DDDMMSS.S
164
						$ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($alt_deg, 4, 2), '0').$alt_deg_dec / 3600);
165
					}
166
167
					foreach (array('latitude', 'longitude', 'altitude') as $key) {
168
						if ($ISO6709parsed[$key] !== false) {
169
							$value = (($lat_sign == '-') ? -1 : 1) * floatval($ISO6709parsed[$key]);
170
							if (!isset($info['quicktime']['comments']['gps_'.$key]) || !in_array($value, $info['quicktime']['comments']['gps_'.$key])) {
171
								@$info['quicktime']['comments']['gps_'.$key][] = (($lat_sign == '-') ? -1 : 1) * floatval($ISO6709parsed[$key]);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
172
							}
173
						}
174
					}
175
				}
176
				if ($ISO6709parsed['latitude'] === false) {
177
					$this->warning('location.ISO6709 string not parsed correctly: "'.$ISO6709string.'", please submit as a bug');
178
				}
179
				break;
180
			}
181
		}
182
183 View Code Duplication
		if (!isset($info['bitrate']) && !empty($info['playtime_seconds'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
184
			$info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
185
		}
186
		if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info['quicktime']['video'])) {
187
			$info['audio']['bitrate'] = $info['bitrate'];
188
		}
189
		if (!empty($info['bitrate']) && !empty($info['audio']['bitrate']) && empty($info['video']['bitrate']) && !empty($info['video']['frame_rate']) && !empty($info['video']['resolution_x']) && ($info['bitrate'] > $info['audio']['bitrate'])) {
190
			$info['video']['bitrate'] = $info['bitrate'] - $info['audio']['bitrate'];
191
		}
192
		if (!empty($info['playtime_seconds']) && !isset($info['video']['frame_rate']) && !empty($info['quicktime']['stts_framecount'])) {
193
			foreach ($info['quicktime']['stts_framecount'] as $key => $samples_count) {
194
				$samples_per_second = $samples_count / $info['playtime_seconds'];
195
				if ($samples_per_second > 240) {
196
					// has to be audio samples
197
				} else {
198
					$info['video']['frame_rate'] = $samples_per_second;
199
					break;
200
				}
201
			}
202
		}
203
		if ($info['audio']['dataformat'] == 'mp4') {
204
			$info['fileformat'] = 'mp4';
205
			if (empty($info['video']['resolution_x'])) {
206
				$info['mime_type']  = 'audio/mp4';
207
				unset($info['video']['dataformat']);
208
			} else {
209
				$info['mime_type']  = 'video/mp4';
210
			}
211
		}
212
213
		if (!$this->ReturnAtomData) {
214
			unset($info['quicktime']['moov']);
215
		}
216
217 View Code Duplication
		if (empty($info['audio']['dataformat']) && !empty($info['quicktime']['audio'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
218
			$info['audio']['dataformat'] = 'quicktime';
219
		}
220 View Code Duplication
		if (empty($info['video']['dataformat']) && !empty($info['quicktime']['video'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
221
			$info['video']['dataformat'] = 'quicktime';
222
		}
223
		if (isset($info['video']) && ($info['mime_type'] == 'audio/mp4') && empty($info['video']['resolution_x']) && empty($info['video']['resolution_y']))  {
224
			unset($info['video']);
225
		}
226
227
		return true;
228
	}
229
230
	/**
231
	 * @param string $atomname
232
	 * @param int    $atomsize
233
	 * @param string $atom_data
234
	 * @param int    $baseoffset
235
	 * @param array  $atomHierarchy
236
	 * @param bool   $ParseAllPossibleAtoms
237
	 *
238
	 * @return array|false
239
	 */
240
	public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
241
		// http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm
242
		// https://code.google.com/p/mp4v2/wiki/iTunesMetadata
243
244
		$info = &$this->getid3->info;
245
246
		$atom_parent = end($atomHierarchy); // not array_pop($atomHierarchy); see https://www.getid3.org/phpBB3/viewtopic.php?t=1717
247
		array_push($atomHierarchy, $atomname);
248
		$atom_structure              = array();
249
		$atom_structure['hierarchy'] = implode(' ', $atomHierarchy);
250
		$atom_structure['name']      = $atomname;
251
		$atom_structure['size']      = $atomsize;
252
		$atom_structure['offset']    = $baseoffset;
253
		if (substr($atomname, 0, 3) == "\x00\x00\x00") {
254
			// https://github.com/JamesHeinrich/getID3/issues/139
255
			$atomname = getid3_lib::BigEndian2Int($atomname);
256
			$atom_structure['name'] = $atomname;
257
			$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
258
		} else {
259
			switch ($atomname) {
260
				case 'moov': // MOVie container atom
261
				case 'trak': // TRAcK container atom
262
				case 'clip': // CLIPping container atom
263
				case 'matt': // track MATTe container atom
264
				case 'edts': // EDiTS container atom
265
				case 'tref': // Track REFerence container atom
266
				case 'mdia': // MeDIA container atom
267
				case 'minf': // Media INFormation container atom
268
				case 'dinf': // Data INFormation container atom
269
				case 'nmhd': // Null Media HeaDer container atom
270
				case 'udta': // User DaTA container atom
271
				case 'cmov': // Compressed MOVie container atom
272
				case 'rmra': // Reference Movie Record Atom
273
				case 'rmda': // Reference Movie Descriptor Atom
274
				case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR)
275
					$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
276
					break;
277
278
				case 'ilst': // Item LiST container atom
279
					if ($atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms)) {
280
						// some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted
281
						$allnumericnames = true;
282
						foreach ($atom_structure['subatoms'] as $subatomarray) {
0 ignored issues
show
Bug introduced by
The expression $atom_structure['subatoms'] of type array|false is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
283
							if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) {
284
								$allnumericnames = false;
285
								break;
286
							}
287
						}
288
						if ($allnumericnames) {
289
							$newData = array();
290
							foreach ($atom_structure['subatoms'] as $subatomarray) {
0 ignored issues
show
Bug introduced by
The expression $atom_structure['subatoms'] of type array|false is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
291
								foreach ($subatomarray['subatoms'] as $newData_subatomarray) {
292
									unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']);
293
									$newData[$subatomarray['name']] = $newData_subatomarray;
294
									break;
295
								}
296
							}
297
							$atom_structure['data'] = $newData;
298
							unset($atom_structure['subatoms']);
299
						}
300
					}
301
					break;
302
303
				case 'stbl': // Sample TaBLe container atom
304
					$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
305
					$isVideo = false;
306
					$framerate  = 0;
307
					$framecount = 0;
308
					foreach ($atom_structure['subatoms'] as $key => $value_array) {
0 ignored issues
show
Bug introduced by
The expression $atom_structure['subatoms'] of type array|false is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
309
						if (isset($value_array['sample_description_table'])) {
310
							foreach ($value_array['sample_description_table'] as $key2 => $value_array2) {
311
								if (isset($value_array2['data_format'])) {
312
									switch ($value_array2['data_format']) {
313
										case 'avc1':
314
										case 'mp4v':
315
											// video data
316
											$isVideo = true;
317
											break;
318
										case 'mp4a':
319
											// audio data
320
											break;
321
									}
322
								}
323
							}
324
						} elseif (isset($value_array['time_to_sample_table'])) {
325
							foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
326
								if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) {
327
									$framerate  = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3);
328
									$framecount = $value_array2['sample_count'];
329
								}
330
							}
331
						}
332
					}
333
					if ($isVideo && $framerate) {
334
						$info['quicktime']['video']['frame_rate'] = $framerate;
335
						$info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate'];
336
					}
337
					if ($isVideo && $framecount) {
338
						$info['quicktime']['video']['frame_count'] = $framecount;
339
					}
340
					break;
341
342
343
				case "\xA9".'alb': // ALBum
344
				case "\xA9".'ART': //
345
				case "\xA9".'art': // ARTist
346
				case "\xA9".'aut': //
347
				case "\xA9".'cmt': // CoMmenT
348
				case "\xA9".'com': // COMposer
349
				case "\xA9".'cpy': //
350
				case "\xA9".'day': // content created year
351
				case "\xA9".'dir': //
352
				case "\xA9".'ed1': //
353
				case "\xA9".'ed2': //
354
				case "\xA9".'ed3': //
355
				case "\xA9".'ed4': //
356
				case "\xA9".'ed5': //
357
				case "\xA9".'ed6': //
358
				case "\xA9".'ed7': //
359
				case "\xA9".'ed8': //
360
				case "\xA9".'ed9': //
361
				case "\xA9".'enc': //
362
				case "\xA9".'fmt': //
363
				case "\xA9".'gen': // GENre
364
				case "\xA9".'grp': // GRouPing
365
				case "\xA9".'hst': //
366
				case "\xA9".'inf': //
367
				case "\xA9".'lyr': // LYRics
368
				case "\xA9".'mak': //
369
				case "\xA9".'mod': //
370
				case "\xA9".'nam': // full NAMe
371
				case "\xA9".'ope': //
372
				case "\xA9".'PRD': //
373
				case "\xA9".'prf': //
374
				case "\xA9".'req': //
375
				case "\xA9".'src': //
376
				case "\xA9".'swr': //
377
				case "\xA9".'too': // encoder
378
				case "\xA9".'trk': // TRacK
379
				case "\xA9".'url': //
380
				case "\xA9".'wrn': //
381
				case "\xA9".'wrt': // WRiTer
382
				case '----': // itunes specific
383
				case 'aART': // Album ARTist
384
				case 'akID': // iTunes store account type
385
				case 'apID': // Purchase Account
386
				case 'atID': //
387
				case 'catg': // CaTeGory
388
				case 'cmID': //
389
				case 'cnID': //
390
				case 'covr': // COVeR artwork
391
				case 'cpil': // ComPILation
392
				case 'cprt': // CoPyRighT
393
				case 'desc': // DESCription
394
				case 'disk': // DISK number
395
				case 'egid': // Episode Global ID
396
				case 'geID': //
397
				case 'gnre': // GeNRE
398
				case 'hdvd': // HD ViDeo
399
				case 'keyw': // KEYWord
400
				case 'ldes': // Long DEScription
401
				case 'pcst': // PodCaST
402
				case 'pgap': // GAPless Playback
403
				case 'plID': //
404
				case 'purd': // PURchase Date
405
				case 'purl': // Podcast URL
406
				case 'rati': //
407
				case 'rndu': //
408
				case 'rpdu': //
409
				case 'rtng': // RaTiNG
410
				case 'sfID': // iTunes store country
411
				case 'soaa': // SOrt Album Artist
412
				case 'soal': // SOrt ALbum
413
				case 'soar': // SOrt ARtist
414
				case 'soco': // SOrt COmposer
415
				case 'sonm': // SOrt NaMe
416
				case 'sosn': // SOrt Show Name
417
				case 'stik': //
418
				case 'tmpo': // TeMPO (BPM)
419
				case 'trkn': // TRacK Number
420
				case 'tven': // tvEpisodeID
421
				case 'tves': // TV EpiSode
422
				case 'tvnn': // TV Network Name
423
				case 'tvsh': // TV SHow Name
424
				case 'tvsn': // TV SeasoN
425
					if ($atom_parent == 'udta') {
426
						// User data atom handler
427
						$atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
428
						$atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
429
						$atom_structure['data']        =                           substr($atom_data, 4);
430
431
						$atom_structure['language']    = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
432 View Code Duplication
						if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
433
							$info['comments']['language'][] = $atom_structure['language'];
434
						}
435
					} else {
436
						// Apple item list box atom handler
437
						$atomoffset = 0;
438
						if (substr($atom_data, 2, 2) == "\x10\xB5") {
439
							// not sure what it means, but observed on iPhone4 data.
440
							// Each $atom_data has 2 bytes of datasize, plus 0x10B5, then data
441
							while ($atomoffset < strlen($atom_data)) {
442
								$boxsmallsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset,     2));
443
								$boxsmalltype =                           substr($atom_data, $atomoffset + 2, 2);
444
								$boxsmalldata =                           substr($atom_data, $atomoffset + 4, $boxsmallsize);
445 View Code Duplication
								if ($boxsmallsize <= 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
446
									$this->warning('Invalid QuickTime atom smallbox size "'.$boxsmallsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset));
447
									$atom_structure['data'] = null;
448
									$atomoffset = strlen($atom_data);
0 ignored issues
show
Unused Code introduced by
$atomoffset is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
449
									break;
450
								}
451
								switch ($boxsmalltype) {
452
									case "\x10\xB5":
453
										$atom_structure['data'] = $boxsmalldata;
454
										break;
455 View Code Duplication
									default:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
456
										$this->warning('Unknown QuickTime smallbox type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxsmalltype).'" ('.trim(getid3_lib::PrintHexBytes($boxsmalltype)).') at offset '.$baseoffset);
457
										$atom_structure['data'] = $atom_data;
458
										break;
459
								}
460
								$atomoffset += (4 + $boxsmallsize);
461
							}
462
						} else {
463
							while ($atomoffset < strlen($atom_data)) {
464
								$boxsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 4));
465
								$boxtype =                           substr($atom_data, $atomoffset + 4, 4);
466
								$boxdata =                           substr($atom_data, $atomoffset + 8, $boxsize - 8);
467 View Code Duplication
								if ($boxsize <= 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
468
									$this->warning('Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset));
469
									$atom_structure['data'] = null;
470
									$atomoffset = strlen($atom_data);
0 ignored issues
show
Unused Code introduced by
$atomoffset is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
471
									break;
472
								}
473
								$atomoffset += $boxsize;
474
475
								switch ($boxtype) {
476
									case 'mean':
477
									case 'name':
478
										$atom_structure[$boxtype] = substr($boxdata, 4);
479
										break;
480
481
									case 'data':
482
										$atom_structure['version']   = getid3_lib::BigEndian2Int(substr($boxdata,  0, 1));
483
										$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata,  1, 3));
484
										switch ($atom_structure['flags_raw']) {
485
											case  0: // data flag
486
											case 21: // tmpo/cpil flag
487
												switch ($atomname) {
488
													case 'cpil':
489
													case 'hdvd':
490
													case 'pcst':
491
													case 'pgap':
492
														// 8-bit integer (boolean)
493
														$atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
494
														break;
495
496
													case 'tmpo':
497
														// 16-bit integer
498
														$atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2));
499
														break;
500
501
													case 'disk':
502
													case 'trkn':
503
														// binary
504
														$num       = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2));
505
														$num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2));
506
														$atom_structure['data']  = empty($num) ? '' : $num;
507
														$atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total;
508
														break;
509
510
													case 'gnre':
511
														// enum
512
														$GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
513
														$atom_structure['data']    = getid3_id3v1::LookupGenreName($GenreID - 1);
514
														break;
515
516 View Code Duplication
													case 'rtng':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
517
														// 8-bit integer
518
														$atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
519
														$atom_structure['data']    = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]);
520
														break;
521
522 View Code Duplication
													case 'stik':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
523
														// 8-bit integer (enum)
524
														$atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
525
														$atom_structure['data']    = $this->QuicktimeSTIKLookup($atom_structure[$atomname]);
526
														break;
527
528 View Code Duplication
													case 'sfID':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
529
														// 32-bit integer
530
														$atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
531
														$atom_structure['data']    = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]);
532
														break;
533
534
													case 'egid':
535
													case 'purl':
536
														$atom_structure['data'] = substr($boxdata, 8);
537
														break;
538
539
													case 'plID':
540
														// 64-bit integer
541
														$atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 8));
542
														break;
543
544
													case 'covr':
545
														$atom_structure['data'] = substr($boxdata, 8);
546
														// not a foolproof check, but better than nothing
547
														if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) {
548
															$atom_structure['image_mime'] = 'image/jpeg';
549
														} elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) {
550
															$atom_structure['image_mime'] = 'image/png';
551
														} elseif (preg_match('#^GIF#', $atom_structure['data'])) {
552
															$atom_structure['image_mime'] = 'image/gif';
553
														}
554
														$info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_structure['data'], 'description'=>'cover');
555
														break;
556
557
													case 'atID':
558
													case 'cnID':
559
													case 'geID':
560
													case 'tves':
561
													case 'tvsn':
562
													default:
563
														// 32-bit integer
564
														$atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
565
												}
566
												break;
567
568
											case  1: // text flag
569
											case 13: // image flag
570
											default:
571
												$atom_structure['data'] = substr($boxdata, 8);
572
												if ($atomname == 'covr') {
573
													if (!empty($atom_structure['data'])) {
574
														$atom_structure['image_mime'] = 'image/unknown'; // provide default MIME type to ensure array keys exist
575
														if (function_exists('getimagesizefromstring') && ($getimagesize = getimagesizefromstring($atom_structure['data'])) && !empty($getimagesize['mime'])) {
576
															$atom_structure['image_mime'] = $getimagesize['mime'];
577
														} else {
578
															// if getimagesizefromstring is not available, or fails for some reason, fall back to simple detection of common image formats
579
															$ImageFormatSignatures = array(
580
																'image/jpeg' => "\xFF\xD8\xFF",
581
																'image/png'  => "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A",
582
																'image/gif'  => 'GIF',
583
															);
584
															foreach ($ImageFormatSignatures as $mime => $image_format_signature) {
585
																if (substr($atom_structure['data'], 0, strlen($image_format_signature)) == $image_format_signature) {
586
																	$atom_structure['image_mime'] = $mime;
587
																	break;
588
																}
589
															}
590
														}
591
														$info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_structure['data'], 'description'=>'cover');
592
													} else {
593
														$this->warning('Unknown empty "covr" image at offset '.$baseoffset);
594
													}
595
												}
596
												break;
597
598
										}
599
										break;
600
601 View Code Duplication
									default:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
602
										$this->warning('Unknown QuickTime box type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxtype).'" ('.trim(getid3_lib::PrintHexBytes($boxtype)).') at offset '.$baseoffset);
603
										$atom_structure['data'] = $atom_data;
604
605
								}
606
							}
607
						}
608
					}
609
					$this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']);
610
					break;
611
612
613
				case 'play': // auto-PLAY atom
614
					$atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
615
616
					$info['quicktime']['autoplay'] = $atom_structure['autoplay'];
617
					break;
618
619
620 View Code Duplication
				case 'WLOC': // Window LOCation atom
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
621
					$atom_structure['location_x']  = getid3_lib::BigEndian2Int(substr($atom_data,  0, 2));
622
					$atom_structure['location_y']  = getid3_lib::BigEndian2Int(substr($atom_data,  2, 2));
623
					break;
624
625
626
				case 'LOOP': // LOOPing atom
627
				case 'SelO': // play SELection Only atom
628
				case 'AllF': // play ALL Frames atom
629
					$atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data);
630
					break;
631
632
633
				case 'name': //
634
				case 'MCPS': // Media Cleaner PRo
635
				case '@PRM': // adobe PReMiere version
636
				case '@PRQ': // adobe PRemiere Quicktime version
637
					$atom_structure['data'] = $atom_data;
638
					break;
639
640
641
				case 'cmvd': // Compressed MooV Data atom
642
					// Code by ubergeekØubergeek*tv based on information from
643
					// http://developer.apple.com/quicktime/icefloe/dispatch012.html
644
					$atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
645
646
					$CompressedFileData = substr($atom_data, 4);
647
					if ($UncompressedHeader = @gzuncompress($CompressedFileData)) {
648
						$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms);
649
					} else {
650
						$this->warning('Error decompressing compressed MOV atom at offset '.$atom_structure['offset']);
651
					}
652
					break;
653
654
655
				case 'dcom': // Data COMpression atom
656
					$atom_structure['compression_id']   = $atom_data;
657
					$atom_structure['compression_text'] = $this->QuicktimeDCOMLookup($atom_data);
658
					break;
659
660
661
				case 'rdrf': // Reference movie Data ReFerence atom
662
					$atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
663
					$atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3));
664
					$atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x000001);
665
666
					$atom_structure['reference_type_name']    =                           substr($atom_data,  4, 4);
667
					$atom_structure['reference_length']       = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
668
					switch ($atom_structure['reference_type_name']) {
669
						case 'url ':
670
							$atom_structure['url']            =       $this->NoNullString(substr($atom_data, 12));
671
							break;
672
673
						case 'alis':
674
							$atom_structure['file_alias']     =                           substr($atom_data, 12);
675
							break;
676
677
						case 'rsrc':
678
							$atom_structure['resource_alias'] =                           substr($atom_data, 12);
679
							break;
680
681
						default:
682
							$atom_structure['data']           =                           substr($atom_data, 12);
683
							break;
684
					}
685
					break;
686
687
688
				case 'rmqu': // Reference Movie QUality atom
689
					$atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data);
690
					break;
691
692
693 View Code Duplication
				case 'rmcs': // Reference Movie Cpu Speed atom
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
694
					$atom_structure['version']          = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
695
					$atom_structure['flags_raw']        = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
696
					$atom_structure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
697
					break;
698
699
700
				case 'rmvc': // Reference Movie Version Check atom
701
					$atom_structure['version']            = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
702
					$atom_structure['flags_raw']          = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
703
					$atom_structure['gestalt_selector']   =                           substr($atom_data,  4, 4);
704
					$atom_structure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
705
					$atom_structure['gestalt_value']      = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
706
					$atom_structure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
707
					break;
708
709
710
				case 'rmcd': // Reference Movie Component check atom
711
					$atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
712
					$atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
713
					$atom_structure['component_type']         =                           substr($atom_data,  4, 4);
714
					$atom_structure['component_subtype']      =                           substr($atom_data,  8, 4);
715
					$atom_structure['component_manufacturer'] =                           substr($atom_data, 12, 4);
716
					$atom_structure['component_flags_raw']    = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
717
					$atom_structure['component_flags_mask']   = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
718
					$atom_structure['component_min_version']  = getid3_lib::BigEndian2Int(substr($atom_data, 24, 4));
719
					break;
720
721
722 View Code Duplication
				case 'rmdr': // Reference Movie Data Rate atom
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
723
					$atom_structure['version']       = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
724
					$atom_structure['flags_raw']     = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
725
					$atom_structure['data_rate']     = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
726
727
					$atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10;
728
					break;
729
730
731
				case 'rmla': // Reference Movie Language Atom
732
					$atom_structure['version']     = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
733
					$atom_structure['flags_raw']   = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
734
					$atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
735
736
					$atom_structure['language']    = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
0 ignored issues
show
Bug introduced by
It seems like $atom_structure['language_id'] can also be of type double or false; however, getid3_quicktime::QuicktimeLanguageLookup() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
737 View Code Duplication
					if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
738
						$info['comments']['language'][] = $atom_structure['language'];
739
					}
740
					break;
741
742
743
				case 'ptv ': // Print To Video - defines a movie's full screen mode
744
					// http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm
745
					$atom_structure['display_size_raw']  = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
746
					$atom_structure['reserved_1']        = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); // hardcoded: 0x0000
747
					$atom_structure['reserved_2']        = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x0000
748
					$atom_structure['slide_show_flag']   = getid3_lib::BigEndian2Int(substr($atom_data, 6, 1));
749
					$atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1));
750
751
					$atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag'];
752
					$atom_structure['flags']['slide_show']   = (bool) $atom_structure['slide_show_flag'];
753
754
					$ptv_lookup = array(
755
						0 => 'normal',
756
						1 => 'double',
757
						2 => 'half',
758
						3 => 'full',
759
						4 => 'current'
760
					);
761
					if (isset($ptv_lookup[$atom_structure['display_size_raw']])) {
762
						$atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
763
					} else {
764
						$this->warning('unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')');
765
					}
766
					break;
767
768
769
				case 'stsd': // Sample Table Sample Description atom
770
					$atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
771
					$atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
772
					$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
773
774
					// see: https://github.com/JamesHeinrich/getID3/issues/111
775
					// Some corrupt files have been known to have high bits set in the number_entries field
776
					// This field shouldn't really need to be 32-bits, values stores are likely in the range 1-100000
777
					// Workaround: mask off the upper byte and throw a warning if it's nonzero
778
					if ($atom_structure['number_entries'] > 0x000FFFFF) {
779
						if ($atom_structure['number_entries'] > 0x00FFFFFF) {
780
							$this->warning('"stsd" atom contains improbably large number_entries (0x'.getid3_lib::PrintHexBytes(substr($atom_data, 4, 4), true, false).' = '.$atom_structure['number_entries'].'), probably in error. Ignoring upper byte and interpreting this as 0x'.getid3_lib::PrintHexBytes(substr($atom_data, 5, 3), true, false).' = '.($atom_structure['number_entries'] & 0x00FFFFFF));
781
							$atom_structure['number_entries'] = ($atom_structure['number_entries'] & 0x00FFFFFF);
782
						} else {
783
							$this->warning('"stsd" atom contains improbably large number_entries (0x'.getid3_lib::PrintHexBytes(substr($atom_data, 4, 4), true, false).' = '.$atom_structure['number_entries'].'), probably in error. Please report this to [email protected] referencing bug report #111');
784
						}
785
					}
786
787
					$stsdEntriesDataOffset = 8;
788
					for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
789
						$atom_structure['sample_description_table'][$i]['size']             = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4));
790
						$stsdEntriesDataOffset += 4;
791
						$atom_structure['sample_description_table'][$i]['data_format']      =                           substr($atom_data, $stsdEntriesDataOffset, 4);
792
						$stsdEntriesDataOffset += 4;
793
						$atom_structure['sample_description_table'][$i]['reserved']         = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6));
794
						$stsdEntriesDataOffset += 6;
795
						$atom_structure['sample_description_table'][$i]['reference_index']  = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2));
796
						$stsdEntriesDataOffset += 2;
797
						$atom_structure['sample_description_table'][$i]['data']             =                           substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
798
						$stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
799
800
						if (substr($atom_structure['sample_description_table'][$i]['data'],  1, 54) == 'application/octet-stream;type=com.parrot.videometadata') {
801
							// special handling for apparently-malformed (TextMetaDataSampleEntry?) data for some version of Parrot drones
802
							$atom_structure['sample_description_table'][$i]['parrot_frame_metadata']['mime_type']        =       substr($atom_structure['sample_description_table'][$i]['data'],  1, 55);
803
							$atom_structure['sample_description_table'][$i]['parrot_frame_metadata']['metadata_version'] = (int) substr($atom_structure['sample_description_table'][$i]['data'], 55,  1);
804
							unset($atom_structure['sample_description_table'][$i]['data']);
805
$this->warning('incomplete/incorrect handling of "stsd" with Parrot metadata in this version of getID3() ['.$this->getid3->version().']');
806
							continue;
807
						}
808
809
						$atom_structure['sample_description_table'][$i]['encoder_version']  = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'],  0, 2));
810
						$atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'],  2, 2));
811
						$atom_structure['sample_description_table'][$i]['encoder_vendor']   =                           substr($atom_structure['sample_description_table'][$i]['data'],  4, 4);
812
813
						switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) {
814
815
							case "\x00\x00\x00\x00":
816
								// audio tracks
817
								$atom_structure['sample_description_table'][$i]['audio_channels']       =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'],  8,  2));
818
								$atom_structure['sample_description_table'][$i]['audio_bit_depth']      =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 10,  2));
819
								$atom_structure['sample_description_table'][$i]['audio_compression_id'] =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12,  2));
820
								$atom_structure['sample_description_table'][$i]['audio_packet_size']    =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 14,  2));
821
								$atom_structure['sample_description_table'][$i]['audio_sample_rate']    = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16,  4));
822
823
								// video tracks
824
								// http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
825
								$atom_structure['sample_description_table'][$i]['temporal_quality'] =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'],  8,  4));
826
								$atom_structure['sample_description_table'][$i]['spatial_quality']  =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12,  4));
827
								$atom_structure['sample_description_table'][$i]['width']            =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16,  2));
828
								$atom_structure['sample_description_table'][$i]['height']           =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18,  2));
829
								$atom_structure['sample_description_table'][$i]['resolution_x']     = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24,  4));
830
								$atom_structure['sample_description_table'][$i]['resolution_y']     = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 28,  4));
831
								$atom_structure['sample_description_table'][$i]['data_size']        =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32,  4));
832
								$atom_structure['sample_description_table'][$i]['frame_count']      =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 36,  2));
833
								$atom_structure['sample_description_table'][$i]['compressor_name']  =                             substr($atom_structure['sample_description_table'][$i]['data'], 38,  4);
834
								$atom_structure['sample_description_table'][$i]['pixel_depth']      =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 42,  2));
835
								$atom_structure['sample_description_table'][$i]['color_table_id']   =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 44,  2));
836
837
								switch ($atom_structure['sample_description_table'][$i]['data_format']) {
838
									case '2vuY':
839
									case 'avc1':
840
									case 'cvid':
841
									case 'dvc ':
842
									case 'dvcp':
843
									case 'gif ':
844
									case 'h263':
845
									case 'jpeg':
846
									case 'kpcd':
847
									case 'mjpa':
848
									case 'mjpb':
849
									case 'mp4v':
850
									case 'png ':
851
									case 'raw ':
852
									case 'rle ':
853
									case 'rpza':
854
									case 'smc ':
855
									case 'SVQ1':
856
									case 'SVQ3':
857
									case 'tiff':
858
									case 'v210':
859
									case 'v216':
860
									case 'v308':
861
									case 'v408':
862
									case 'v410':
863
									case 'yuv2':
864
										$info['fileformat'] = 'mp4';
865
										$info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
866
										if ($this->QuicktimeVideoCodecLookup($info['video']['fourcc'])) {
867
											$info['video']['fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($info['video']['fourcc']);
868
										}
869
870
										// https://www.getid3.org/phpBB3/viewtopic.php?t=1550
871
										//if ((!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['width'])) && (empty($info['video']['resolution_x']) || empty($info['video']['resolution_y']) || (number_format($info['video']['resolution_x'], 6) != number_format(round($info['video']['resolution_x']), 6)) || (number_format($info['video']['resolution_y'], 6) != number_format(round($info['video']['resolution_y']), 6)))) { // ugly check for floating point numbers
872
										if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['height'])) {
873
											// assume that values stored here are more important than values stored in [tkhd] atom
874
											$info['video']['resolution_x'] = $atom_structure['sample_description_table'][$i]['width'];
875
											$info['video']['resolution_y'] = $atom_structure['sample_description_table'][$i]['height'];
876
											$info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
877
											$info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
878
										}
879
										break;
880
881
									case 'qtvr':
882
										$info['video']['dataformat'] = 'quicktimevr';
883
										break;
884
885
									case 'mp4a':
886
									default:
887
										$info['quicktime']['audio']['codec']       = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
888
										$info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate'];
889
										$info['quicktime']['audio']['channels']    = $atom_structure['sample_description_table'][$i]['audio_channels'];
890
										$info['quicktime']['audio']['bit_depth']   = $atom_structure['sample_description_table'][$i]['audio_bit_depth'];
891
										$info['audio']['codec']                    = $info['quicktime']['audio']['codec'];
892
										$info['audio']['sample_rate']              = $info['quicktime']['audio']['sample_rate'];
893
										$info['audio']['channels']                 = $info['quicktime']['audio']['channels'];
894
										$info['audio']['bits_per_sample']          = $info['quicktime']['audio']['bit_depth'];
895
										switch ($atom_structure['sample_description_table'][$i]['data_format']) {
896
											case 'raw ': // PCM
897
											case 'alac': // Apple Lossless Audio Codec
898
											case 'sowt': // signed/two's complement (Little Endian)
899
											case 'twos': // signed/two's complement (Big Endian)
900
											case 'in24': // 24-bit Integer
901
											case 'in32': // 32-bit Integer
902
											case 'fl32': // 32-bit Floating Point
903
											case 'fl64': // 64-bit Floating Point
904
												$info['audio']['lossless'] = $info['quicktime']['audio']['lossless'] = true;
905
												$info['audio']['bitrate']  = $info['quicktime']['audio']['bitrate']  = $info['audio']['channels'] * $info['audio']['bits_per_sample'] * $info['audio']['sample_rate'];
906
												break;
907
											default:
908
												$info['audio']['lossless'] = false;
909
												break;
910
										}
911
										break;
912
								}
913
								break;
914
915
							default:
916
								switch ($atom_structure['sample_description_table'][$i]['data_format']) {
917
									case 'mp4s':
918
										$info['fileformat'] = 'mp4';
919
										break;
920
921
									default:
922
										// video atom
923
										$atom_structure['sample_description_table'][$i]['video_temporal_quality']  =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'],  8,  4));
924
										$atom_structure['sample_description_table'][$i]['video_spatial_quality']   =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12,  4));
925
										$atom_structure['sample_description_table'][$i]['video_frame_width']       =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16,  2));
926
										$atom_structure['sample_description_table'][$i]['video_frame_height']      =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18,  2));
927
										$atom_structure['sample_description_table'][$i]['video_resolution_x']      = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20,  4));
928
										$atom_structure['sample_description_table'][$i]['video_resolution_y']      = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24,  4));
929
										$atom_structure['sample_description_table'][$i]['video_data_size']         =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 28,  4));
930
										$atom_structure['sample_description_table'][$i]['video_frame_count']       =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32,  2));
931
										$atom_structure['sample_description_table'][$i]['video_encoder_name_len']  =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34,  1));
932
										$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']);
933
										$atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66,  2));
934
										$atom_structure['sample_description_table'][$i]['video_color_table_id']    =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68,  2));
935
936
										$atom_structure['sample_description_table'][$i]['video_pixel_color_type']  = (((int) $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color');
937
										$atom_structure['sample_description_table'][$i]['video_pixel_color_name']  = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']);
938
939
										if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') {
940
											$info['quicktime']['video']['codec_fourcc']        = $atom_structure['sample_description_table'][$i]['data_format'];
941
											$info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
942
											$info['quicktime']['video']['codec']               = (((int) $atom_structure['sample_description_table'][$i]['video_encoder_name_len'] > 0) ? $atom_structure['sample_description_table'][$i]['video_encoder_name'] : $atom_structure['sample_description_table'][$i]['data_format']);
943
											$info['quicktime']['video']['color_depth']         = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'];
944
											$info['quicktime']['video']['color_depth_name']    = $atom_structure['sample_description_table'][$i]['video_pixel_color_name'];
945
946
											$info['video']['codec']           = $info['quicktime']['video']['codec'];
947
											$info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth'];
948
										}
949
										$info['video']['lossless']           = false;
950
										$info['video']['pixel_aspect_ratio'] = (float) 1;
951
										break;
952
								}
953
								break;
954
						}
955
						switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) {
956
							case 'mp4a':
957
								$info['audio']['dataformat']         = 'mp4';
958
								$info['quicktime']['audio']['codec'] = 'mp4';
959
								break;
960
961
							case '3ivx':
962
							case '3iv1':
963
							case '3iv2':
964
								$info['video']['dataformat'] = '3ivx';
965
								break;
966
967
							case 'xvid':
968
								$info['video']['dataformat'] = 'xvid';
969
								break;
970
971
							case 'mp4v':
972
								$info['video']['dataformat'] = 'mpeg4';
973
								break;
974
975
							case 'divx':
976
							case 'div1':
977
							case 'div2':
978
							case 'div3':
979
							case 'div4':
980
							case 'div5':
981
							case 'div6':
982
								$info['video']['dataformat'] = 'divx';
983
								break;
984
985
							default:
986
								// do nothing
987
								break;
988
						}
989
						unset($atom_structure['sample_description_table'][$i]['data']);
990
					}
991
					break;
992
993
994
				case 'stts': // Sample Table Time-to-Sample atom
995
					$atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
996
					$atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
997
					$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
998
					$sttsEntriesDataOffset = 8;
999
					//$FrameRateCalculatorArray = array();
1000
					$frames_count = 0;
1001
1002
					$max_stts_entries_to_scan = ($info['php_memory_limit'] ? min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']) : $atom_structure['number_entries']);
1003
					if ($max_stts_entries_to_scan < $atom_structure['number_entries']) {
1004
						$this->warning('QuickTime atom "stts" has '.$atom_structure['number_entries'].' but only scanning the first '.$max_stts_entries_to_scan.' entries due to limited PHP memory available ('.floor($this->getid3->memory_limit / 1048576).'MB).');
1005
					}
1006 View Code Duplication
					for ($i = 0; $i < $max_stts_entries_to_scan; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1007
						$atom_structure['time_to_sample_table'][$i]['sample_count']    = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
1008
						$sttsEntriesDataOffset += 4;
1009
						$atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
1010
						$sttsEntriesDataOffset += 4;
1011
1012
						$frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count'];
1013
1014
						// THIS SECTION REPLACED WITH CODE IN "stbl" ATOM
1015
						//if (!empty($info['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) {
1016
						//	$stts_new_framerate = $info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'];
1017
						//	if ($stts_new_framerate <= 60) {
1018
						//		// some atoms have durations of "1" giving a very large framerate, which probably is not right
1019
						//		$info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate);
1020
						//	}
1021
						//}
1022
						//
1023
						//$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count'];
1024
					}
1025
					$info['quicktime']['stts_framecount'][] = $frames_count;
1026
					//$sttsFramesTotal  = 0;
1027
					//$sttsSecondsTotal = 0;
1028
					//foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) {
1029
					//	if (($frames_per_second > 60) || ($frames_per_second < 1)) {
1030
					//		// not video FPS information, probably audio information
1031
					//		$sttsFramesTotal  = 0;
1032
					//		$sttsSecondsTotal = 0;
1033
					//		break;
1034
					//	}
1035
					//	$sttsFramesTotal  += $frame_count;
1036
					//	$sttsSecondsTotal += $frame_count / $frames_per_second;
1037
					//}
1038
					//if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) {
1039
					//	if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) {
1040
					//		$info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal;
1041
					//	}
1042
					//}
1043
					break;
1044
1045
1046 View Code Duplication
				case 'stss': // Sample Table Sync Sample (key frames) atom
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1047
					if ($ParseAllPossibleAtoms) {
1048
						$atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1049
						$atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
1050
						$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
1051
						$stssEntriesDataOffset = 8;
1052
						for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
1053
							$atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4));
1054
							$stssEntriesDataOffset += 4;
1055
						}
1056
					}
1057
					break;
1058
1059
1060
				case 'stsc': // Sample Table Sample-to-Chunk atom
1061
					if ($ParseAllPossibleAtoms) {
1062
						$atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1063
						$atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
1064
						$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
1065
						$stscEntriesDataOffset = 8;
1066 View Code Duplication
						for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1067
							$atom_structure['sample_to_chunk_table'][$i]['first_chunk']        = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
1068
							$stscEntriesDataOffset += 4;
1069
							$atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk']  = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
1070
							$stscEntriesDataOffset += 4;
1071
							$atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
1072
							$stscEntriesDataOffset += 4;
1073
						}
1074
					}
1075
					break;
1076
1077
1078
				case 'stsz': // Sample Table SiZe atom
1079
					if ($ParseAllPossibleAtoms) {
1080
						$atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1081
						$atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
1082
						$atom_structure['sample_size']    = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
1083
						$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
1084
						$stszEntriesDataOffset = 12;
1085
						if ($atom_structure['sample_size'] == 0) {
1086
							for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
1087
								$atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4));
1088
								$stszEntriesDataOffset += 4;
1089
							}
1090
						}
1091
					}
1092
					break;
1093
1094
1095 View Code Duplication
				case 'stco': // Sample Table Chunk Offset atom
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1096
//					if (true) {
1097
					if ($ParseAllPossibleAtoms) {
1098
						$atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1099
						$atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
1100
						$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
1101
						$stcoEntriesDataOffset = 8;
1102
						for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
1103
							$atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4));
1104
							$stcoEntriesDataOffset += 4;
1105
						}
1106
					}
1107
					break;
1108
1109
1110 View Code Duplication
				case 'co64': // Chunk Offset 64-bit (version of "stco" that supports > 2GB files)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1111
					if ($ParseAllPossibleAtoms) {
1112
						$atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1113
						$atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
1114
						$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
1115
						$stcoEntriesDataOffset = 8;
1116
						for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
1117
							$atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8));
1118
							$stcoEntriesDataOffset += 8;
1119
						}
1120
					}
1121
					break;
1122
1123
1124
				case 'dref': // Data REFerence atom
1125
					$atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1126
					$atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
1127
					$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
1128
					$drefDataOffset = 8;
1129
					for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
1130
						$atom_structure['data_references'][$i]['size']                    = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4));
1131
						$drefDataOffset += 4;
1132
						$atom_structure['data_references'][$i]['type']                    =                           substr($atom_data, $drefDataOffset, 4);
1133
						$drefDataOffset += 4;
1134
						$atom_structure['data_references'][$i]['version']                 = getid3_lib::BigEndian2Int(substr($atom_data,  $drefDataOffset, 1));
1135
						$drefDataOffset += 1;
1136
						$atom_structure['data_references'][$i]['flags_raw']               = getid3_lib::BigEndian2Int(substr($atom_data,  $drefDataOffset, 3)); // hardcoded: 0x0000
1137
						$drefDataOffset += 3;
1138
						$atom_structure['data_references'][$i]['data']                    =                           substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3));
1139
						$drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3);
1140
1141
						$atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001);
1142
					}
1143
					break;
1144
1145
1146
				case 'gmin': // base Media INformation atom
1147
					$atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1148
					$atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
1149
					$atom_structure['graphics_mode']          = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
1150
					$atom_structure['opcolor_red']            = getid3_lib::BigEndian2Int(substr($atom_data,  6, 2));
1151
					$atom_structure['opcolor_green']          = getid3_lib::BigEndian2Int(substr($atom_data,  8, 2));
1152
					$atom_structure['opcolor_blue']           = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
1153
					$atom_structure['balance']                = getid3_lib::BigEndian2Int(substr($atom_data, 12, 2));
1154
					$atom_structure['reserved']               = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
1155
					break;
1156
1157
1158 View Code Duplication
				case 'smhd': // Sound Media information HeaDer atom
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1159
					$atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1160
					$atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
1161
					$atom_structure['balance']                = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
1162
					$atom_structure['reserved']               = getid3_lib::BigEndian2Int(substr($atom_data,  6, 2));
1163
					break;
1164
1165
1166
				case 'vmhd': // Video Media information HeaDer atom
1167
					$atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1168
					$atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3));
1169
					$atom_structure['graphics_mode']          = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
1170
					$atom_structure['opcolor_red']            = getid3_lib::BigEndian2Int(substr($atom_data,  6, 2));
1171
					$atom_structure['opcolor_green']          = getid3_lib::BigEndian2Int(substr($atom_data,  8, 2));
1172
					$atom_structure['opcolor_blue']           = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
1173
1174
					$atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x001);
1175
					break;
1176
1177
1178
				case 'hdlr': // HanDLeR reference atom
1179
					$atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1180
					$atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
1181
					$atom_structure['component_type']         =                           substr($atom_data,  4, 4);
1182
					$atom_structure['component_subtype']      =                           substr($atom_data,  8, 4);
1183
					$atom_structure['component_manufacturer'] =                           substr($atom_data, 12, 4);
1184
					$atom_structure['component_flags_raw']    = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1185
					$atom_structure['component_flags_mask']   = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
1186
					$atom_structure['component_name']         = $this->MaybePascal2String(substr($atom_data, 24));
1187
1188
					if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) {
1189
						$info['video']['dataformat'] = 'quicktimevr';
1190
					}
1191
					break;
1192
1193
1194
				case 'mdhd': // MeDia HeaDer atom
1195
					$atom_structure['version']               = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1196
					$atom_structure['flags_raw']             = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
1197
					$atom_structure['creation_time']         = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
1198
					$atom_structure['modify_time']           = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
1199
					$atom_structure['time_scale']            = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1200
					$atom_structure['duration']              = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1201
					$atom_structure['language_id']           = getid3_lib::BigEndian2Int(substr($atom_data, 20, 2));
1202
					$atom_structure['quality']               = getid3_lib::BigEndian2Int(substr($atom_data, 22, 2));
1203
1204
					if ($atom_structure['time_scale'] == 0) {
1205
						$this->error('Corrupt Quicktime file: mdhd.time_scale == zero');
1206
						return false;
1207
					}
1208
					$info['quicktime']['time_scale'] = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
1209
1210
					$atom_structure['creation_time_unix']    = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
1211
					$atom_structure['modify_time_unix']      = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
1212
					$atom_structure['playtime_seconds']      = $atom_structure['duration'] / $atom_structure['time_scale'];
1213
					$atom_structure['language']              = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
1214 View Code Duplication
					if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1215
						$info['comments']['language'][] = $atom_structure['language'];
1216
					}
1217
					$info['quicktime']['timestamps_unix']['create'][$atom_structure['hierarchy']] = $atom_structure['creation_time_unix'];
1218
					$info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modify_time_unix'];
1219
					break;
1220
1221
1222
				case 'pnot': // Preview atom
1223
					$atom_structure['modification_date']      = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4)); // "standard Macintosh format"
1224
					$atom_structure['version_number']         = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2)); // hardcoded: 0x00
1225
					$atom_structure['atom_type']              =                           substr($atom_data,  6, 4);        // usually: 'PICT'
1226
					$atom_structure['atom_index']             = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01
1227
1228
					$atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']);
1229
					$info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modification_date_unix'];
1230
					break;
1231
1232
1233 View Code Duplication
				case 'crgn': // Clipping ReGioN atom
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1234
					$atom_structure['region_size']   = getid3_lib::BigEndian2Int(substr($atom_data,  0, 2)); // The Region size, Region boundary box,
1235
					$atom_structure['boundary_box']  = getid3_lib::BigEndian2Int(substr($atom_data,  2, 8)); // and Clipping region data fields
1236
					$atom_structure['clipping_data'] =                           substr($atom_data, 10);           // constitute a QuickDraw region.
1237
					break;
1238
1239
1240
				case 'load': // track LOAD settings atom
1241
					$atom_structure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4));
1242
					$atom_structure['preload_duration']   = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
1243
					$atom_structure['preload_flags_raw']  = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
1244
					$atom_structure['default_hints_raw']  = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1245
1246
					$atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x0020);
1247
					$atom_structure['default_hints']['high_quality']  = (bool) ($atom_structure['default_hints_raw'] & 0x0100);
1248
					break;
1249
1250
1251
				case 'tmcd': // TiMe CoDe atom
1252
				case 'chap': // CHAPter list atom
1253
				case 'sync': // SYNChronization atom
1254
				case 'scpt': // tranSCriPT atom
1255 View Code Duplication
				case 'ssrc': // non-primary SouRCe atom
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1256
					for ($i = 0; $i < strlen($atom_data); $i += 4) {
1257
						@$atom_structure['track_id'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1258
					}
1259
					break;
1260
1261
1262
				case 'elst': // Edit LiST atom
1263
					$atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1264
					$atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
1265
					$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
1266
					for ($i = 0; $i < $atom_structure['number_entries']; $i++ ) {
1267
						$atom_structure['edit_list'][$i]['track_duration'] =   getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4));
1268
						$atom_structure['edit_list'][$i]['media_time']     =   getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4));
1269
						$atom_structure['edit_list'][$i]['media_rate']     = getid3_lib::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4));
1270
					}
1271
					break;
1272
1273
1274 View Code Duplication
				case 'kmat': // compressed MATte atom
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1275
					$atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1276
					$atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
1277
					$atom_structure['matte_data_raw'] =               substr($atom_data,  4);
1278
					break;
1279
1280
1281
				case 'ctab': // Color TABle atom
1282
					$atom_structure['color_table_seed']   = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4)); // hardcoded: 0x00000000
1283
					$atom_structure['color_table_flags']  = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2)); // hardcoded: 0x8000
1284
					$atom_structure['color_table_size']   = getid3_lib::BigEndian2Int(substr($atom_data,  6, 2)) + 1;
1285
					for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) {
1286
						$atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2));
1287
						$atom_structure['color_table'][$colortableentry]['red']   = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2));
1288
						$atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2));
1289
						$atom_structure['color_table'][$colortableentry]['blue']  = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2));
1290
					}
1291
					break;
1292
1293
1294
				case 'mvhd': // MoVie HeaDer atom
1295
					$atom_structure['version']            =   getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1296
					$atom_structure['flags_raw']          =   getid3_lib::BigEndian2Int(substr($atom_data,  1, 3));
1297
					$atom_structure['creation_time']      =   getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
1298
					$atom_structure['modify_time']        =   getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
1299
					$atom_structure['time_scale']         =   getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1300
					$atom_structure['duration']           =   getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1301
					$atom_structure['preferred_rate']     = getid3_lib::FixedPoint16_16(substr($atom_data, 20, 4));
1302
					$atom_structure['preferred_volume']   =   getid3_lib::FixedPoint8_8(substr($atom_data, 24, 2));
1303
					$atom_structure['reserved']           =                             substr($atom_data, 26, 10);
1304
					$atom_structure['matrix_a']           = getid3_lib::FixedPoint16_16(substr($atom_data, 36, 4));
1305
					$atom_structure['matrix_b']           = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
1306
					$atom_structure['matrix_u']           =  getid3_lib::FixedPoint2_30(substr($atom_data, 44, 4));
1307
					$atom_structure['matrix_c']           = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4));
1308
					$atom_structure['matrix_d']           = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
1309
					$atom_structure['matrix_v']           =  getid3_lib::FixedPoint2_30(substr($atom_data, 56, 4));
1310
					$atom_structure['matrix_x']           = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4));
1311
					$atom_structure['matrix_y']           = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
1312
					$atom_structure['matrix_w']           =  getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4));
1313
					$atom_structure['preview_time']       =   getid3_lib::BigEndian2Int(substr($atom_data, 72, 4));
1314
					$atom_structure['preview_duration']   =   getid3_lib::BigEndian2Int(substr($atom_data, 76, 4));
1315
					$atom_structure['poster_time']        =   getid3_lib::BigEndian2Int(substr($atom_data, 80, 4));
1316
					$atom_structure['selection_time']     =   getid3_lib::BigEndian2Int(substr($atom_data, 84, 4));
1317
					$atom_structure['selection_duration'] =   getid3_lib::BigEndian2Int(substr($atom_data, 88, 4));
1318
					$atom_structure['current_time']       =   getid3_lib::BigEndian2Int(substr($atom_data, 92, 4));
1319
					$atom_structure['next_track_id']      =   getid3_lib::BigEndian2Int(substr($atom_data, 96, 4));
1320
1321
					if ($atom_structure['time_scale'] == 0) {
1322
						$this->error('Corrupt Quicktime file: mvhd.time_scale == zero');
1323
						return false;
1324
					}
1325
					$atom_structure['creation_time_unix']        = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
1326
					$atom_structure['modify_time_unix']          = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
1327
					$info['quicktime']['timestamps_unix']['create'][$atom_structure['hierarchy']] = $atom_structure['creation_time_unix'];
1328
					$info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modify_time_unix'];
1329
					$info['quicktime']['time_scale']    = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
1330
					$info['quicktime']['display_scale'] = $atom_structure['matrix_a'];
1331
					$info['playtime_seconds']           = $atom_structure['duration'] / $atom_structure['time_scale'];
1332
					break;
1333
1334
1335
				case 'tkhd': // TracK HeaDer atom
1336
					$atom_structure['version']             =   getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1337
					$atom_structure['flags_raw']           =   getid3_lib::BigEndian2Int(substr($atom_data,  1, 3));
1338
					$atom_structure['creation_time']       =   getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
1339
					$atom_structure['modify_time']         =   getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
1340
					$atom_structure['trackid']             =   getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1341
					$atom_structure['reserved1']           =   getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1342
					$atom_structure['duration']            =   getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
1343
					$atom_structure['reserved2']           =   getid3_lib::BigEndian2Int(substr($atom_data, 24, 8));
1344
					$atom_structure['layer']               =   getid3_lib::BigEndian2Int(substr($atom_data, 32, 2));
1345
					$atom_structure['alternate_group']     =   getid3_lib::BigEndian2Int(substr($atom_data, 34, 2));
1346
					$atom_structure['volume']              =   getid3_lib::FixedPoint8_8(substr($atom_data, 36, 2));
1347
					$atom_structure['reserved3']           =   getid3_lib::BigEndian2Int(substr($atom_data, 38, 2));
1348
					// http://developer.apple.com/library/mac/#documentation/QuickTime/RM/MovieBasics/MTEditing/K-Chapter/11MatrixFunctions.html
1349
					// http://developer.apple.com/library/mac/#documentation/QuickTime/qtff/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-18737
1350
					$atom_structure['matrix_a']            = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
1351
					$atom_structure['matrix_b']            = getid3_lib::FixedPoint16_16(substr($atom_data, 44, 4));
1352
					$atom_structure['matrix_u']            =  getid3_lib::FixedPoint2_30(substr($atom_data, 48, 4));
1353
					$atom_structure['matrix_c']            = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
1354
					$atom_structure['matrix_d']            = getid3_lib::FixedPoint16_16(substr($atom_data, 56, 4));
1355
					$atom_structure['matrix_v']            =  getid3_lib::FixedPoint2_30(substr($atom_data, 60, 4));
1356
					$atom_structure['matrix_x']            = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
1357
					$atom_structure['matrix_y']            = getid3_lib::FixedPoint16_16(substr($atom_data, 68, 4));
1358
					$atom_structure['matrix_w']            =  getid3_lib::FixedPoint2_30(substr($atom_data, 72, 4));
1359
					$atom_structure['width']               = getid3_lib::FixedPoint16_16(substr($atom_data, 76, 4));
1360
					$atom_structure['height']              = getid3_lib::FixedPoint16_16(substr($atom_data, 80, 4));
1361
					$atom_structure['flags']['enabled']    = (bool) ($atom_structure['flags_raw'] & 0x0001);
1362
					$atom_structure['flags']['in_movie']   = (bool) ($atom_structure['flags_raw'] & 0x0002);
1363
					$atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x0004);
1364
					$atom_structure['flags']['in_poster']  = (bool) ($atom_structure['flags_raw'] & 0x0008);
1365
					$atom_structure['creation_time_unix']  = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
1366
					$atom_structure['modify_time_unix']    = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
1367
					$info['quicktime']['timestamps_unix']['create'][$atom_structure['hierarchy']] = $atom_structure['creation_time_unix'];
1368
					$info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modify_time_unix'];
1369
1370
					// https://www.getid3.org/phpBB3/viewtopic.php?t=1908
1371
					// attempt to compute rotation from matrix values
1372
					// 2017-Dec-28: uncertain if 90/270 are correctly oriented; values returned by FixedPoint16_16 should perhaps be -1 instead of 65535(?)
1373
					$matrixRotation = 0;
1374
					switch ($atom_structure['matrix_a'].':'.$atom_structure['matrix_b'].':'.$atom_structure['matrix_c'].':'.$atom_structure['matrix_d']) {
1375
						case '1:0:0:1':         $matrixRotation =   0; break;
1376
						case '0:1:65535:0':     $matrixRotation =  90; break;
1377
						case '65535:0:0:65535': $matrixRotation = 180; break;
1378
						case '0:65535:1:0':     $matrixRotation = 270; break;
1379
						default: break;
1380
					}
1381
1382
					// https://www.getid3.org/phpBB3/viewtopic.php?t=2468
1383
					// The rotation matrix can appear in the Quicktime file multiple times, at least once for each track,
1384
					// and it's possible that only the video track (or, in theory, one of the video tracks) is flagged as
1385
					// rotated while the other tracks (e.g. audio) is tagged as rotation=0 (behavior noted on iPhone 8 Plus)
1386
					// The correct solution would be to check if the TrackID associated with the rotation matrix is indeed
1387
					// a video track (or the main video track) and only set the rotation then, but since information about
1388
					// what track is what is not trivially there to be examined, the lazy solution is to set the rotation
1389
					// if it is found to be nonzero, on the assumption that tracks that don't need it will have rotation set
1390
					// to zero (and be effectively ignored) and the video track will have rotation set correctly, which will
1391
					// either be zero and automatically correct, or nonzero and be set correctly.
1392
					if (!isset($info['video']['rotate']) || (($info['video']['rotate'] == 0) && ($matrixRotation > 0))) {
1393
						$info['quicktime']['video']['rotate'] = $info['video']['rotate'] = $matrixRotation;
1394
					}
1395
1396
					if ($atom_structure['flags']['enabled'] == 1) {
1397
						if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) {
1398
							$info['video']['resolution_x'] = $atom_structure['width'];
1399
							$info['video']['resolution_y'] = $atom_structure['height'];
1400
						}
1401
						$info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']);
1402
						$info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']);
1403
						$info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
1404
						$info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
1405
					} else {
1406
						// see: https://www.getid3.org/phpBB3/viewtopic.php?t=1295
1407
						//if (isset($info['video']['resolution_x'])) { unset($info['video']['resolution_x']); }
1408
						//if (isset($info['video']['resolution_y'])) { unset($info['video']['resolution_y']); }
1409
						//if (isset($info['quicktime']['video']))    { unset($info['quicktime']['video']);    }
1410
					}
1411
					break;
1412
1413
1414
				case 'iods': // Initial Object DeScriptor atom
1415
					// http://www.koders.com/c/fid1FAB3E762903DC482D8A246D4A4BF9F28E049594.aspx?s=windows.h
1416
					// http://libquicktime.sourcearchive.com/documentation/1.0.2plus-pdebian/iods_8c-source.html
1417
					$offset = 0;
1418
					$atom_structure['version']                =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1419
					$offset += 1;
1420
					$atom_structure['flags_raw']              =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 3));
1421
					$offset += 3;
1422
					$atom_structure['mp4_iod_tag']            =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1423
					$offset += 1;
1424
					$atom_structure['length']                 = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
1425
					//$offset already adjusted by quicktime_read_mp4_descr_length()
1426
					$atom_structure['object_descriptor_id']   =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2));
1427
					$offset += 2;
1428
					$atom_structure['od_profile_level']       =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1429
					$offset += 1;
1430
					$atom_structure['scene_profile_level']    =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1431
					$offset += 1;
1432
					$atom_structure['audio_profile_id']       =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1433
					$offset += 1;
1434
					$atom_structure['video_profile_id']       =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1435
					$offset += 1;
1436
					$atom_structure['graphics_profile_level'] =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1437
					$offset += 1;
1438
1439
					$atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6; // 6 bytes would only be right if all tracks use 1-byte length fields
1440 View Code Duplication
					for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1441
						$atom_structure['track'][$i]['ES_ID_IncTag'] =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1442
						$offset += 1;
1443
						$atom_structure['track'][$i]['length']       = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
1444
						//$offset already adjusted by quicktime_read_mp4_descr_length()
1445
						$atom_structure['track'][$i]['track_id']     =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4));
1446
						$offset += 4;
1447
					}
1448
1449
					$atom_structure['audio_profile_name'] = $this->QuicktimeIODSaudioProfileName($atom_structure['audio_profile_id']);
1450
					$atom_structure['video_profile_name'] = $this->QuicktimeIODSvideoProfileName($atom_structure['video_profile_id']);
1451
					break;
1452
1453 View Code Duplication
				case 'ftyp': // FileTYPe (?) atom (for MP4 it seems)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1454
					$atom_structure['signature'] =                           substr($atom_data,  0, 4);
1455
					$atom_structure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
1456
					$atom_structure['fourcc']    =                           substr($atom_data,  8, 4);
1457
					break;
1458
1459
				case 'mdat': // Media DATa atom
1460
					// 'mdat' contains the actual data for the audio/video, possibly also subtitles
1461
1462
	/* due to lack of known documentation, this is a kludge implementation. If you know of documentation on how mdat is properly structed, please send it to [email protected] */
1463
1464
					// first, skip any 'wide' padding, and second 'mdat' header (with specified size of zero?)
1465
					$mdat_offset = 0;
1466
					while (true) {
1467
						if (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x08".'wide') {
1468
							$mdat_offset += 8;
1469
						} elseif (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x00".'mdat') {
1470
							$mdat_offset += 8;
1471
						} else {
1472
							break;
1473
						}
1474
					}
1475
					if (substr($atom_data, $mdat_offset, 4) == 'GPRO') {
1476
						$GOPRO_chunk_length = getid3_lib::LittleEndian2Int(substr($atom_data, $mdat_offset + 4, 4));
1477
						$GOPRO_offset = 8;
0 ignored issues
show
Unused Code introduced by
$GOPRO_offset is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1478
						$atom_structure['GPRO']['raw'] = substr($atom_data, $mdat_offset + 8, $GOPRO_chunk_length - 8);
1479
						$atom_structure['GPRO']['firmware'] = substr($atom_structure['GPRO']['raw'],  0, 15);
1480
						$atom_structure['GPRO']['unknown1'] = substr($atom_structure['GPRO']['raw'], 15, 16);
1481
						$atom_structure['GPRO']['unknown2'] = substr($atom_structure['GPRO']['raw'], 31, 32);
1482
						$atom_structure['GPRO']['unknown3'] = substr($atom_structure['GPRO']['raw'], 63, 16);
1483
						$atom_structure['GPRO']['camera']   = substr($atom_structure['GPRO']['raw'], 79, 32);
1484
						$info['quicktime']['camera']['model'] = rtrim($atom_structure['GPRO']['camera'], "\x00");
1485
					}
1486
1487
					// check to see if it looks like chapter titles, in the form of unterminated strings with a leading 16-bit size field
1488
					while (($mdat_offset < (strlen($atom_data) - 8))
1489
						&& ($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2)))
1490
						&& ($chapter_string_length < 1000)
1491
						&& ($chapter_string_length <= (strlen($atom_data) - $mdat_offset - 2))
1492
						&& preg_match('#^([\x00-\xFF]{2})([\x20-\xFF]+)$#', substr($atom_data, $mdat_offset, $chapter_string_length + 2), $chapter_matches)) {
1493
							list($dummy, $chapter_string_length_hex, $chapter_string) = $chapter_matches;
0 ignored issues
show
Unused Code introduced by
The assignment to $chapter_string_length_hex is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
Unused Code introduced by
The assignment to $dummy is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1494
							$mdat_offset += (2 + $chapter_string_length);
1495
							@$info['quicktime']['comments']['chapters'][] = $chapter_string;
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1496
1497
							// "encd" atom specifies encoding. In theory could be anything, almost always UTF-8, but may be UTF-16 with BOM (not currently handled)
1498
							if (substr($atom_data, $mdat_offset, 12) == "\x00\x00\x00\x0C\x65\x6E\x63\x64\x00\x00\x01\x00") { // UTF-8
1499
								$mdat_offset += 12;
1500
							}
1501
					}
1502
1503
					if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) {
1504
1505
						$info['avdataoffset'] = $atom_structure['offset'] + 8;                       // $info['quicktime'][$atomname]['offset'] + 8;
1506
						$OldAVDataEnd         = $info['avdataend'];
1507
						$info['avdataend']    = $atom_structure['offset'] + $atom_structure['size']; // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size'];
1508
1509
						$getid3_temp = new getID3();
1510
						$getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
1511
						$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1512
						$getid3_temp->info['avdataend']    = $info['avdataend'];
1513
						$getid3_mp3 = new getid3_mp3($getid3_temp);
1514
						if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode($this->fread(4)))) {
0 ignored issues
show
Security Bug introduced by
It seems like $getid3_mp3->MPEGaudioHe...Decode($this->fread(4)) targeting getid3_mp3::MPEGaudioHeaderDecode() can also be of type false; however, getid3_mp3::MPEGaudioHeaderValid() does only seem to accept array, did you maybe forget to handle an error condition?
Loading history...
Security Bug introduced by
It seems like $this->fread(4) targeting getid3_handler::fread() can also be of type false; however, getid3_mp3::MPEGaudioHeaderDecode() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
1515
							$getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
1516
							if (!empty($getid3_temp->info['warning'])) {
1517
								foreach ($getid3_temp->info['warning'] as $value) {
1518
									$this->warning($value);
1519
								}
1520
							}
1521
							if (!empty($getid3_temp->info['mpeg'])) {
1522
								$info['mpeg'] = $getid3_temp->info['mpeg'];
1523
								if (isset($info['mpeg']['audio'])) {
1524
									$info['audio']['dataformat']   = 'mp3';
1525
									$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')));
1526
									$info['audio']['sample_rate']  = $info['mpeg']['audio']['sample_rate'];
1527
									$info['audio']['channels']     = $info['mpeg']['audio']['channels'];
1528
									$info['audio']['bitrate']      = $info['mpeg']['audio']['bitrate'];
1529
									$info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1530
									$info['bitrate']               = $info['audio']['bitrate'];
1531
								}
1532
							}
1533
						}
1534
						unset($getid3_mp3, $getid3_temp);
1535
						$info['avdataend'] = $OldAVDataEnd;
1536
						unset($OldAVDataEnd);
1537
1538
					}
1539
1540
					unset($mdat_offset, $chapter_string_length, $chapter_matches);
1541
					break;
1542
1543
				case 'free': // FREE space atom
1544
				case 'skip': // SKIP atom
1545
				case 'wide': // 64-bit expansion placeholder atom
1546
					// 'free', 'skip' and 'wide' are just padding, contains no useful data at all
1547
1548
					// When writing QuickTime files, it is sometimes necessary to update an atom's size.
1549
					// It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom
1550
					// is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime
1551
					// puts an 8-byte placeholder atom before any atoms it may have to update the size of.
1552
					// In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the
1553
					// placeholder atom can be overwritten to obtain the necessary 8 extra bytes.
1554
					// The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ).
1555
					break;
1556
1557
1558
				case 'nsav': // NoSAVe atom
1559
					// http://developer.apple.com/technotes/tn/tn2038.html
1560
					$atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4));
1561
					break;
1562
1563
				case 'ctyp': // Controller TYPe atom (seen on QTVR)
1564
					// http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
1565
					// some controller names are:
1566
					//   0x00 + 'std' for linear movie
1567
					//   'none' for no controls
1568
					$atom_structure['ctyp'] = substr($atom_data, 0, 4);
1569
					$info['quicktime']['controller'] = $atom_structure['ctyp'];
1570
					switch ($atom_structure['ctyp']) {
1571
						case 'qtvr':
1572
							$info['video']['dataformat'] = 'quicktimevr';
1573
							break;
1574
					}
1575
					break;
1576
1577
				case 'pano': // PANOrama track (seen on QTVR)
1578
					$atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4));
1579
					break;
1580
1581
				case 'hint': // HINT track
1582
				case 'hinf': //
1583
				case 'hinv': //
1584
				case 'hnti': //
1585
					$info['quicktime']['hinting'] = true;
1586
					break;
1587
1588
				case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR)
1589
					for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) {
1590
						$atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
1591
					}
1592
					break;
1593
1594
1595
				// Observed-but-not-handled atom types are just listed here to prevent warnings being generated
1596
				case 'FXTC': // Something to do with Adobe After Effects (?)
1597
				case 'PrmA':
1598
				case 'code':
1599
				case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html
1600
				case 'tapt': // TrackApertureModeDimensionsAID - http://developer.apple.com/documentation/QuickTime/Reference/QT7-1_Update_Reference/Constants/Constants.html
1601
							// tapt seems to be used to compute the video size [https://www.getid3.org/phpBB3/viewtopic.php?t=838]
1602
							// * http://lists.apple.com/archives/quicktime-api/2006/Aug/msg00014.html
1603
							// * http://handbrake.fr/irclogs/handbrake-dev/handbrake-dev20080128_pg2.html
1604
				case 'ctts'://  STCompositionOffsetAID             - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1605
				case 'cslg'://  STCompositionShiftLeastGreatestAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1606
				case 'sdtp'://  STSampleDependencyAID              - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1607
				case 'stps'://  STPartialSyncSampleAID             - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1608
					//$atom_structure['data'] = $atom_data;
1609
					break;
1610
1611
				case "\xA9".'xyz':  // GPS latitude+longitude+altitude
1612
					$atom_structure['data'] = $atom_data;
1613
					if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) {
1614
						@list($all, $latitude, $longitude, $altitude) = $matches;
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1615
						$info['quicktime']['comments']['gps_latitude'][]  = floatval($latitude);
1616
						$info['quicktime']['comments']['gps_longitude'][] = floatval($longitude);
1617
						if (!empty($altitude)) {
1618
							$info['quicktime']['comments']['gps_altitude'][] = floatval($altitude);
1619
						}
1620
					} else {
1621
						$this->warning('QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.');
1622
					}
1623
					break;
1624
1625
				case 'NCDT':
1626
					// https://exiftool.org/TagNames/Nikon.html
1627
					// Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
1628
					$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms);
1629
					break;
1630
				case 'NCTH': // Nikon Camera THumbnail image
1631
				case 'NCVW': // Nikon Camera preVieW image
1632
				case 'NCM1': // Nikon Camera preview iMage 1
1633
				case 'NCM2': // Nikon Camera preview iMage 2
1634
					// https://exiftool.org/TagNames/Nikon.html
1635
					if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) {
1636
						$descriptions = array(
1637
							'NCTH' => 'Nikon Camera Thumbnail Image',
1638
							'NCVW' => 'Nikon Camera Preview Image',
1639
							'NCM1' => 'Nikon Camera Preview Image 1',
1640
							'NCM2' => 'Nikon Camera Preview Image 2',
1641
						);
1642
						$atom_structure['data'] = $atom_data;
1643
						$atom_structure['image_mime'] = 'image/jpeg';
1644
						$atom_structure['description'] = isset($descriptions[$atomname]) ? $descriptions[$atomname] : 'Nikon preview image';
1645
						$info['quicktime']['comments']['picture'][] = array(
1646
							'image_mime' => $atom_structure['image_mime'],
1647
							'data' => $atom_data,
1648
							'description' => $atom_structure['description']
1649
						);
1650
					}
1651
					break;
1652
				case 'NCTG': // Nikon - https://exiftool.org/TagNames/Nikon.html#NCTG
1653
					getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.nikon-nctg.php', __FILE__, true);
1654
					$nikonNCTG = new getid3_tag_nikon_nctg($this->getid3);
1655
1656
					$atom_structure['data'] = $nikonNCTG->parse($atom_data);
1657
					break;
1658
				case 'NCHD': // Nikon:MakerNoteVersion  - https://exiftool.org/TagNames/Nikon.html
1659
					$makerNoteVersion = '';
1660
					for ($i = 0, $iMax = strlen($atom_data); $i < $iMax; ++$i) {
1661
						if (ord($atom_data[$i]) >= 0x00 && ord($atom_data[$i]) <= 0x1F) {
1662
							$makerNoteVersion .= ' '.ord($atom_data[$i]);
1663
						} else {
1664
							$makerNoteVersion .= $atom_data[$i];
1665
						}
1666
					}
1667
					$makerNoteVersion = rtrim($makerNoteVersion, "\x00");
1668
					$atom_structure['data'] = array(
1669
						'MakerNoteVersion' => $makerNoteVersion
1670
					);
1671
					break;
1672
				case 'NCDB': // Nikon                   - https://exiftool.org/TagNames/Nikon.html
1673
				case 'CNCV': // Canon:CompressorVersion - https://exiftool.org/TagNames/Canon.html
1674
					$atom_structure['data'] = $atom_data;
1675
					break;
1676
1677 View Code Duplication
				case "\x00\x00\x00\x00":
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1678
					// some kind of metacontainer, may contain a big data dump such as:
1679
					// mdta keys \005 mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst \01D \001 \015data \001DE\010Apple 0 \002 (data \001DE\0102011-05-11T17:54:04+0200 2 \003 *data \001DE\010+52.4936+013.3897+040.247/ \01D \004 \015data \001DE\0104.3.1 \005 \018data \001DE\010iPhone 4
1680
					// https://xhelmboyx.tripod.com/formats/qti-layout.txt
1681
1682
					$atom_structure['version']   =          getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1683
					$atom_structure['flags_raw'] =          getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1684
					$atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
1685
					//$atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
1686
					break;
1687
1688 View Code Duplication
				case 'meta': // METAdata atom
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1689
					// https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html
1690
1691
					$atom_structure['version']   =          getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1692
					$atom_structure['flags_raw'] =          getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1693
					$atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
1694
					break;
1695
1696
				case 'data': // metaDATA atom
1697
					static $metaDATAkey = 1; // real ugly, but so is the QuickTime structure that stores keys and values in different multinested locations that are hard to relate to each other
1698
					// seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data
1699
					$atom_structure['language'] =                           substr($atom_data, 4 + 0, 2);
1700
					$atom_structure['unknown']  = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2));
1701
					$atom_structure['data']     =                           substr($atom_data, 4 + 4);
1702
					$atom_structure['key_name'] = @$info['quicktime']['temp_meta_key_names'][$metaDATAkey++];
1703
1704
					if ($atom_structure['key_name'] && $atom_structure['data']) {
1705
						@$info['quicktime']['comments'][str_replace('com.apple.quicktime.', '', $atom_structure['key_name'])][] = $atom_structure['data'];
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1706
					}
1707
					break;
1708
1709
				case 'keys': // KEYS that may be present in the metadata atom.
1710
					// https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW21
1711
					// The metadata item keys atom holds a list of the metadata keys that may be present in the metadata atom.
1712
					// This list is indexed starting with 1; 0 is a reserved index value. The metadata item keys atom is a full atom with an atom type of "keys".
1713
					$atom_structure['version']       = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
1714
					$atom_structure['flags_raw']     = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3));
1715
					$atom_structure['entry_count']   = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
1716
					$keys_atom_offset = 8;
1717
					for ($i = 1; $i <= $atom_structure['entry_count']; $i++) {
1718
						$atom_structure['keys'][$i]['key_size']      = getid3_lib::BigEndian2Int(substr($atom_data, $keys_atom_offset + 0, 4));
1719
						$atom_structure['keys'][$i]['key_namespace'] =                           substr($atom_data, $keys_atom_offset + 4, 4);
1720
						$atom_structure['keys'][$i]['key_value']     =                           substr($atom_data, $keys_atom_offset + 8, $atom_structure['keys'][$i]['key_size'] - 8);
1721
						$keys_atom_offset += $atom_structure['keys'][$i]['key_size']; // key_size includes the 4+4 bytes for key_size and key_namespace
1722
1723
						$info['quicktime']['temp_meta_key_names'][$i] = $atom_structure['keys'][$i]['key_value'];
1724
					}
1725
					break;
1726
1727
				case 'uuid': // user-defined atom often seen containing XML data, also used for potentially many other purposes, only a few specifically handled by getID3 (e.g. 360fly spatial data)
1728
					//Get the UUID ID in first 16 bytes
1729
					$uuid_bytes_read = unpack('H8time_low/H4time_mid/H4time_hi/H4clock_seq_hi/H12clock_seq_low', substr($atom_data, 0, 16));
1730
					$atom_structure['uuid_field_id'] = implode('-', $uuid_bytes_read);
1731
1732
					switch ($atom_structure['uuid_field_id']) {   // http://fileformats.archiveteam.org/wiki/Boxes/atoms_format#UUID_boxes
1733
1734
						case '0537cdab-9d0c-4431-a72a-fa561f2a113e': // Exif                                       - http://fileformats.archiveteam.org/wiki/Exif
1735
						case '2c4c0100-8504-40b9-a03e-562148d6dfeb': // Photoshop Image Resources                  - http://fileformats.archiveteam.org/wiki/Photoshop_Image_Resources
1736
						case '33c7a4d2-b81d-4723-a0ba-f1a3e097ad38': // IPTC-IIM                                   - http://fileformats.archiveteam.org/wiki/IPTC-IIM
1737
						case '8974dbce-7be7-4c51-84f9-7148f9882554': // PIFF Track Encryption Box                  - http://fileformats.archiveteam.org/wiki/Protected_Interoperable_File_Format
1738
						case '96a9f1f1-dc98-402d-a7ae-d68e34451809': // GeoJP2 World File Box                      - http://fileformats.archiveteam.org/wiki/GeoJP2
1739
						case 'a2394f52-5a9b-4f14-a244-6c427c648df4': // PIFF Sample Encryption Box                 - http://fileformats.archiveteam.org/wiki/Protected_Interoperable_File_Format
1740
						case 'b14bf8bd-083d-4b43-a5ae-8cd7d5a6ce03': // GeoJP2 GeoTIFF Box                         - http://fileformats.archiveteam.org/wiki/GeoJP2
1741 View Code Duplication
						case 'd08a4f18-10f3-4a82-b6c8-32d8aba183d3': // PIFF Protection System Specific Header Box - http://fileformats.archiveteam.org/wiki/Protected_Interoperable_File_Format
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1742
							$this->warning('Unhandled (but recognized) "uuid" atom identified by "'.$atom_structure['uuid_field_id'].'" at offset '.$atom_structure['offset'].' ('.strlen($atom_data).' bytes)');
1743
							break;
1744
1745
						case 'be7acfcb-97a9-42e8-9c71-999491e3afac': // XMP data (in XML format)
1746
							$atom_structure['xml'] = substr($atom_data, 16, strlen($atom_data) - 16 - 8); // 16 bytes for UUID, 8 bytes header(?)
1747
							break;
1748
1749
						case 'efe1589a-bb77-49ef-8095-27759eb1dc6f': // 360fly data
1750
							/* 360fly code in this block by Paul Lewis 2019-Oct-31 */
1751
							/*	Sensor Timestamps need to be calculated using the recordings base time at ['quicktime']['moov']['subatoms'][0]['creation_time_unix']. */
1752
							$atom_structure['title'] = '360Fly Sensor Data';
1753
1754
							//Get the UUID HEADER data
1755
							$uuid_bytes_read = unpack('vheader_size/vheader_version/vtimescale/vhardware_version/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/', substr($atom_data, 16, 32));
1756
							$atom_structure['uuid_header'] = $uuid_bytes_read;
1757
1758
							$start_byte = 48;
1759
							$atom_SENSOR_data = substr($atom_data, $start_byte);
1760
							$atom_structure['sensor_data']['data_type'] = array(
1761
									'fusion_count'   => 0,       // ID 250
1762
									'fusion_data'    => array(),
1763
									'accel_count'    => 0,       // ID 1
1764
									'accel_data'     => array(),
1765
									'gyro_count'     => 0,       // ID 2
1766
									'gyro_data'      => array(),
1767
									'magno_count'    => 0,       // ID 3
1768
									'magno_data'     => array(),
1769
									'gps_count'      => 0,       // ID 5
1770
									'gps_data'       => array(),
1771
									'rotation_count' => 0,       // ID 6
1772
									'rotation_data'  => array(),
1773
									'unknown_count'  => 0,       // ID ??
1774
									'unknown_data'   => array(),
1775
									'debug_list'     => '',      // Used to debug variables stored as comma delimited strings
1776
							);
1777
							$debug_structure = array();
1778
							$debug_structure['debug_items'] = array();
1779
							// Can start loop here to decode all sensor data in 32 Byte chunks:
1780
							foreach (str_split($atom_SENSOR_data, 32) as $sensor_key => $sensor_data) {
1781
								// This gets me a data_type code to work out what data is in the next 31 bytes.
1782
								$sensor_data_type = substr($sensor_data, 0, 1);
1783
								$sensor_data_content = substr($sensor_data, 1);
1784
								$uuid_bytes_read = unpack('C*', $sensor_data_type);
1785
								$sensor_data_array = array();
1786
								switch ($uuid_bytes_read[1]) {
1787 View Code Duplication
									case 250:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1788
										$atom_structure['sensor_data']['data_type']['fusion_count']++;
1789
										$uuid_bytes_read = unpack('cmode/Jtimestamp/Gyaw/Gpitch/Groll/x*', $sensor_data_content);
1790
										$sensor_data_array['mode']      = $uuid_bytes_read['mode'];
1791
										$sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp'];
1792
										$sensor_data_array['yaw']       = $uuid_bytes_read['yaw'];
1793
										$sensor_data_array['pitch']     = $uuid_bytes_read['pitch'];
1794
										$sensor_data_array['roll']      = $uuid_bytes_read['roll'];
1795
										array_push($atom_structure['sensor_data']['data_type']['fusion_data'], $sensor_data_array);
1796
										break;
1797 View Code Duplication
									case 1:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1798
										$atom_structure['sensor_data']['data_type']['accel_count']++;
1799
										$uuid_bytes_read = unpack('cmode/Jtimestamp/Gyaw/Gpitch/Groll/x*', $sensor_data_content);
1800
										$sensor_data_array['mode']      = $uuid_bytes_read['mode'];
1801
										$sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp'];
1802
										$sensor_data_array['yaw']       = $uuid_bytes_read['yaw'];
1803
										$sensor_data_array['pitch']     = $uuid_bytes_read['pitch'];
1804
										$sensor_data_array['roll']      = $uuid_bytes_read['roll'];
1805
										array_push($atom_structure['sensor_data']['data_type']['accel_data'], $sensor_data_array);
1806
										break;
1807 View Code Duplication
									case 2:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1808
										$atom_structure['sensor_data']['data_type']['gyro_count']++;
1809
										$uuid_bytes_read = unpack('cmode/Jtimestamp/Gyaw/Gpitch/Groll/x*', $sensor_data_content);
1810
										$sensor_data_array['mode']      = $uuid_bytes_read['mode'];
1811
										$sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp'];
1812
										$sensor_data_array['yaw']       = $uuid_bytes_read['yaw'];
1813
										$sensor_data_array['pitch']     = $uuid_bytes_read['pitch'];
1814
										$sensor_data_array['roll']      = $uuid_bytes_read['roll'];
1815
										array_push($atom_structure['sensor_data']['data_type']['gyro_data'], $sensor_data_array);
1816
										break;
1817 View Code Duplication
									case 3:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1818
										$atom_structure['sensor_data']['data_type']['magno_count']++;
1819
										$uuid_bytes_read = unpack('cmode/Jtimestamp/Gmagx/Gmagy/Gmagz/x*', $sensor_data_content);
1820
										$sensor_data_array['mode']      = $uuid_bytes_read['mode'];
1821
										$sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp'];
1822
										$sensor_data_array['magx']      = $uuid_bytes_read['magx'];
1823
										$sensor_data_array['magy']      = $uuid_bytes_read['magy'];
1824
										$sensor_data_array['magz']      = $uuid_bytes_read['magz'];
1825
										array_push($atom_structure['sensor_data']['data_type']['magno_data'], $sensor_data_array);
1826
										break;
1827
									case 5:
1828
										$atom_structure['sensor_data']['data_type']['gps_count']++;
1829
										$uuid_bytes_read = unpack('cmode/Jtimestamp/Glat/Glon/Galt/Gspeed/nbearing/nacc/x*', $sensor_data_content);
1830
										$sensor_data_array['mode']      = $uuid_bytes_read['mode'];
1831
										$sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp'];
1832
										$sensor_data_array['lat']       = $uuid_bytes_read['lat'];
1833
										$sensor_data_array['lon']       = $uuid_bytes_read['lon'];
1834
										$sensor_data_array['alt']       = $uuid_bytes_read['alt'];
1835
										$sensor_data_array['speed']     = $uuid_bytes_read['speed'];
1836
										$sensor_data_array['bearing']   = $uuid_bytes_read['bearing'];
1837
										$sensor_data_array['acc']       = $uuid_bytes_read['acc'];
1838
										array_push($atom_structure['sensor_data']['data_type']['gps_data'], $sensor_data_array);
1839
										//array_push($debug_structure['debug_items'], $uuid_bytes_read['timestamp']);
1840
										break;
1841 View Code Duplication
									case 6:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1842
										$atom_structure['sensor_data']['data_type']['rotation_count']++;
1843
										$uuid_bytes_read = unpack('cmode/Jtimestamp/Grotx/Groty/Grotz/x*', $sensor_data_content);
1844
										$sensor_data_array['mode']      = $uuid_bytes_read['mode'];
1845
										$sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp'];
1846
										$sensor_data_array['rotx']      = $uuid_bytes_read['rotx'];
1847
										$sensor_data_array['roty']      = $uuid_bytes_read['roty'];
1848
										$sensor_data_array['rotz']      = $uuid_bytes_read['rotz'];
1849
										array_push($atom_structure['sensor_data']['data_type']['rotation_data'], $sensor_data_array);
1850
										break;
1851
									default:
1852
										$atom_structure['sensor_data']['data_type']['unknown_count']++;
1853
										break;
1854
								}
1855
							}
1856
							//if (isset($debug_structure['debug_items']) && count($debug_structure['debug_items']) > 0) {
1857
							//	$atom_structure['sensor_data']['data_type']['debug_list'] = implode(',', $debug_structure['debug_items']);
1858
							//} else {
1859
								$atom_structure['sensor_data']['data_type']['debug_list'] = 'No debug items in list!';
1860
							//}
1861
							break;
1862
1863 View Code Duplication
						default:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1864
							$this->warning('Unhandled "uuid" atom identified by "'.$atom_structure['uuid_field_id'].'" at offset '.$atom_structure['offset'].' ('.strlen($atom_data).' bytes)');
1865
					}
1866
					break;
1867
1868
				case 'gps ':
1869
					// https://dashcamtalk.com/forum/threads/script-to-extract-gps-data-from-novatek-mp4.20808/page-2#post-291730
1870
					// The 'gps ' contains simple look up table made up of 8byte rows, that point to the 'free' atoms that contains the actual GPS data.
1871
					// The first row is version/metadata/notsure, I skip that.
1872
					// The following rows consist of 4byte address (absolute) and 4byte size (0x1000), these point to the GPS data in the file.
1873
1874
					$GPS_rowsize = 8; // 4 bytes for offset, 4 bytes for size
1875
					if (strlen($atom_data) > 0) {
1876
						if ((strlen($atom_data) % $GPS_rowsize) == 0) {
1877
							$atom_structure['gps_toc'] = array();
1878
							foreach (str_split($atom_data, $GPS_rowsize) as $counter => $datapair) {
1879
								$atom_structure['gps_toc'][] = unpack('Noffset/Nsize', substr($atom_data, $counter * $GPS_rowsize, $GPS_rowsize));
1880
							}
1881
1882
							$atom_structure['gps_entries'] = array();
1883
							$previous_offset = $this->ftell();
1884
							foreach ($atom_structure['gps_toc'] as $key => $gps_pointer) {
1885
								if ($key == 0) {
1886
									// "The first row is version/metadata/notsure, I skip that."
1887
									continue;
1888
								}
1889
								$this->fseek($gps_pointer['offset']);
1890
								$GPS_free_data = $this->fread($gps_pointer['size']);
1891
1892
								/*
1893
								// 2017-05-10: I see some of the data, notably the Hour-Minute-Second, but cannot reconcile the rest of the data. However, the NMEA "GPRMC" line is there and relatively easy to parse, so I'm using that instead
1894
1895
								// https://dashcamtalk.com/forum/threads/script-to-extract-gps-data-from-novatek-mp4.20808/page-2#post-291730
1896
								// The structure of the GPS data atom (the 'free' atoms mentioned above) is following:
1897
								// hour,minute,second,year,month,day,active,latitude_b,longitude_b,unknown2,latitude,longitude,speed = struct.unpack_from('<IIIIIIssssfff',data, 48)
1898
								// For those unfamiliar with python struct:
1899
								// I = int
1900
								// s = is string (size 1, in this case)
1901
								// f = float
1902
1903
								//$atom_structure['gps_entries'][$key] = unpack('Vhour/Vminute/Vsecond/Vyear/Vmonth/Vday/Vactive/Vlatitude_b/Vlongitude_b/Vunknown2/flatitude/flongitude/fspeed', substr($GPS_free_data, 48));
1904
								*/
1905
1906
								// $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62
1907
								// $GPRMC,183731,A,3907.482,N,12102.436,W,000.0,360.0,080301,015.5,E*67
1908
								// $GPRMC,002454,A,3553.5295,N,13938.6570,E,0.0,43.1,180700,7.1,W,A*3F
1909
								// $GPRMC,094347.000,A,5342.0061,N,00737.9908,W,0.01,156.75,140217,,,A*7D
1910
								if (preg_match('#\\$GPRMC,([0-9\\.]*),([AV]),([0-9\\.]*),([NS]),([0-9\\.]*),([EW]),([0-9\\.]*),([0-9\\.]*),([0-9]*),([0-9\\.]*),([EW]?)(,[A])?(\\*[0-9A-F]{2})#', $GPS_free_data, $matches)) {
1911
									$GPS_this_GPRMC = array();
1912
									$GPS_this_GPRMC_raw = array();
1913
									list(
1914
										$GPS_this_GPRMC_raw['gprmc'],
1915
										$GPS_this_GPRMC_raw['timestamp'],
1916
										$GPS_this_GPRMC_raw['status'],
1917
										$GPS_this_GPRMC_raw['latitude'],
1918
										$GPS_this_GPRMC_raw['latitude_direction'],
1919
										$GPS_this_GPRMC_raw['longitude'],
1920
										$GPS_this_GPRMC_raw['longitude_direction'],
1921
										$GPS_this_GPRMC_raw['knots'],
1922
										$GPS_this_GPRMC_raw['angle'],
1923
										$GPS_this_GPRMC_raw['datestamp'],
1924
										$GPS_this_GPRMC_raw['variation'],
1925
										$GPS_this_GPRMC_raw['variation_direction'],
1926
										$dummy,
0 ignored issues
show
Unused Code introduced by
The assignment to $dummy is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1927
										$GPS_this_GPRMC_raw['checksum'],
1928
									) = $matches;
1929
									$GPS_this_GPRMC['raw'] = $GPS_this_GPRMC_raw;
1930
1931
									$hour   = substr($GPS_this_GPRMC['raw']['timestamp'], 0, 2);
1932
									$minute = substr($GPS_this_GPRMC['raw']['timestamp'], 2, 2);
1933
									$second = substr($GPS_this_GPRMC['raw']['timestamp'], 4, 2);
1934
									$ms     = substr($GPS_this_GPRMC['raw']['timestamp'], 6);    // may contain decimal seconds
1935
									$day    = substr($GPS_this_GPRMC['raw']['datestamp'], 0, 2);
1936
									$month  = substr($GPS_this_GPRMC['raw']['datestamp'], 2, 2);
1937
									$year   = (int) substr($GPS_this_GPRMC['raw']['datestamp'], 4, 2);
1938
									$year += (($year > 90) ? 1900 : 2000); // complete lack of foresight: datestamps are stored with 2-digit years, take best guess
1939
									$GPS_this_GPRMC['timestamp'] = $year.'-'.$month.'-'.$day.' '.$hour.':'.$minute.':'.$second.$ms;
1940
1941
									$GPS_this_GPRMC['active'] = ($GPS_this_GPRMC['raw']['status'] == 'A'); // A=Active,V=Void
1942
1943
									foreach (array('latitude','longitude') as $latlon) {
1944
										preg_match('#^([0-9]{1,3})([0-9]{2}\\.[0-9]+)$#', $GPS_this_GPRMC['raw'][$latlon], $matches);
1945
										list($dummy, $deg, $min) = $matches;
0 ignored issues
show
Unused Code introduced by
The assignment to $dummy is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1946
										$GPS_this_GPRMC[$latlon] = $deg + ($min / 60);
1947
									}
1948
									$GPS_this_GPRMC['latitude']  *= (($GPS_this_GPRMC['raw']['latitude_direction']  == 'S') ? -1 : 1);
1949
									$GPS_this_GPRMC['longitude'] *= (($GPS_this_GPRMC['raw']['longitude_direction'] == 'W') ? -1 : 1);
1950
1951
									$GPS_this_GPRMC['heading']    = $GPS_this_GPRMC['raw']['angle'];
1952
									$GPS_this_GPRMC['speed_knot'] = $GPS_this_GPRMC['raw']['knots'];
1953
									$GPS_this_GPRMC['speed_kmh']  = $GPS_this_GPRMC['raw']['knots'] * 1.852;
1954
									if ($GPS_this_GPRMC['raw']['variation']) {
1955
										$GPS_this_GPRMC['variation']  = $GPS_this_GPRMC['raw']['variation'];
1956
										$GPS_this_GPRMC['variation'] *= (($GPS_this_GPRMC['raw']['variation_direction'] == 'W') ? -1 : 1);
1957
									}
1958
1959
									$atom_structure['gps_entries'][$key] = $GPS_this_GPRMC;
1960
1961
									@$info['quicktime']['gps_track'][$GPS_this_GPRMC['timestamp']] = array(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1962
										'latitude'  => (float) $GPS_this_GPRMC['latitude'],
1963
										'longitude' => (float) $GPS_this_GPRMC['longitude'],
1964
										'speed_kmh' => (float) $GPS_this_GPRMC['speed_kmh'],
1965
										'heading'   => (float) $GPS_this_GPRMC['heading'],
1966
									);
1967
1968
								} else {
1969
									$this->warning('Unhandled GPS format in "free" atom at offset '.$gps_pointer['offset']);
1970
								}
1971
							}
1972
							$this->fseek($previous_offset);
0 ignored issues
show
Bug introduced by
It seems like $previous_offset defined by $this->ftell() on line 1883 can also be of type boolean; however, getid3_handler::fseek() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1973
1974
						} else {
1975
							$this->warning('QuickTime atom "'.$atomname.'" is not mod-8 bytes long ('.$atomsize.' bytes) at offset '.$baseoffset);
1976
						}
1977
					} else {
1978
						$this->warning('QuickTime atom "'.$atomname.'" is zero bytes long at offset '.$baseoffset);
1979
					}
1980
					break;
1981
1982
				case 'loci':// 3GP location (El Loco)
1983
					$loffset = 0;
1984
					$info['quicktime']['comments']['gps_flags']     = array(  getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)));
1985
					$info['quicktime']['comments']['gps_lang']      = array(  getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)));
1986
					$info['quicktime']['comments']['gps_location']  = array(          $this->LociString(substr($atom_data, 6), $loffset));
1987
					$loci_data = substr($atom_data, 6 + $loffset);
1988
					$info['quicktime']['comments']['gps_role']      = array(  getid3_lib::BigEndian2Int(substr($loci_data, 0, 1)));
1989
					$info['quicktime']['comments']['gps_longitude'] = array(getid3_lib::FixedPoint16_16(substr($loci_data, 1, 4)));
1990
					$info['quicktime']['comments']['gps_latitude']  = array(getid3_lib::FixedPoint16_16(substr($loci_data, 5, 4)));
1991
					$info['quicktime']['comments']['gps_altitude']  = array(getid3_lib::FixedPoint16_16(substr($loci_data, 9, 4)));
1992
					$info['quicktime']['comments']['gps_body']      = array(          $this->LociString(substr($loci_data, 13           ), $loffset));
1993
					$info['quicktime']['comments']['gps_notes']     = array(          $this->LociString(substr($loci_data, 13 + $loffset), $loffset));
1994
					break;
1995
1996
				case 'chpl': // CHaPter List
1997
					// https://www.adobe.com/content/dam/Adobe/en/devnet/flv/pdfs/video_file_format_spec_v10.pdf
1998
					$chpl_version = getid3_lib::BigEndian2Int(substr($atom_data, 4, 1)); // Expected to be 0
0 ignored issues
show
Unused Code introduced by
$chpl_version is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1999
					$chpl_flags   = getid3_lib::BigEndian2Int(substr($atom_data, 5, 3)); // Reserved, set to 0
0 ignored issues
show
Unused Code introduced by
$chpl_flags is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2000
					$chpl_count   = getid3_lib::BigEndian2Int(substr($atom_data, 8, 1));
2001
					$chpl_offset = 9;
2002
					for ($i = 0; $i < $chpl_count; $i++) {
2003
						if (($chpl_offset + 9) >= strlen($atom_data)) {
2004
							$this->warning('QuickTime chapter '.$i.' extends beyond end of "chpl" atom');
2005
							break;
2006
						}
2007
						$info['quicktime']['chapters'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($atom_data, $chpl_offset, 8)) / 10000000; // timestamps are stored as 100-nanosecond units
2008
						$chpl_offset += 8;
2009
						$chpl_title_size = getid3_lib::BigEndian2Int(substr($atom_data, $chpl_offset, 1));
2010
						$chpl_offset += 1;
2011
						$info['quicktime']['chapters'][$i]['title']     =                           substr($atom_data, $chpl_offset, $chpl_title_size);
2012
						$chpl_offset += $chpl_title_size;
2013
					}
2014
					break;
2015
2016
				case 'FIRM': // FIRMware version(?), seen on GoPro Hero4
2017
					$info['quicktime']['camera']['firmware'] = $atom_data;
2018
					break;
2019
2020
				case 'CAME': // FIRMware version(?), seen on GoPro Hero4
2021
					$info['quicktime']['camera']['serial_hash'] = unpack('H*', $atom_data);
2022
					break;
2023
2024
				case 'dscp':
2025
				case 'rcif':
2026
					// https://www.getid3.org/phpBB3/viewtopic.php?t=1908
2027
					if (substr($atom_data, 0, 7) == "\x00\x00\x00\x00\x55\xC4".'{') {
2028
						if ($json_decoded = @json_decode(rtrim(substr($atom_data, 6), "\x00"), true)) {
2029
							$info['quicktime']['camera'][$atomname] = $json_decoded;
2030
							if (($atomname == 'rcif') && isset($info['quicktime']['camera']['rcif']['wxcamera']['rotate'])) {
2031
								$info['video']['rotate'] = $info['quicktime']['video']['rotate'] = $info['quicktime']['camera']['rcif']['wxcamera']['rotate'];
2032
							}
2033
						} else {
2034
							$this->warning('Failed to JSON decode atom "'.$atomname.'"');
2035
							$atom_structure['data'] = $atom_data;
2036
						}
2037
						unset($json_decoded);
2038
					} else {
2039
						$this->warning('Expecting 55 C4 7B at start of atom "'.$atomname.'", found '.getid3_lib::PrintHexBytes(substr($atom_data, 4, 3)).' instead');
2040
						$atom_structure['data'] = $atom_data;
2041
					}
2042
					break;
2043
2044
				case 'frea':
2045
					// https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea
2046
					// may contain "scra" (PreviewImage) and/or "thma" (ThumbnailImage)
2047
					$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms);
2048
					break;
2049
				case 'tima': // subatom to "frea"
2050
					// no idea what this does, the one sample file I've seen has a value of 0x00000027
2051
					$atom_structure['data'] = $atom_data;
2052
					break;
2053
				case 'ver ': // subatom to "frea"
2054
					// some kind of version number, the one sample file I've seen has a value of "3.00.073"
2055
					$atom_structure['data'] = $atom_data;
2056
					break;
2057 View Code Duplication
				case 'thma': // subatom to "frea" -- "ThumbnailImage"
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2058
					// https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea
2059
					if (strlen($atom_data) > 0) {
2060
						$info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg', 'description'=>'ThumbnailImage');
2061
					}
2062
					break;
2063 View Code Duplication
				case 'scra': // subatom to "frea" -- "PreviewImage"
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2064
					// https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea
2065
					// but the only sample file I've seen has no useful data here
2066
					if (strlen($atom_data) > 0) {
2067
						$info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg', 'description'=>'PreviewImage');
2068
					}
2069
					break;
2070
2071
				case 'cdsc': // timed metadata reference
2072
					// A QuickTime movie can contain none, one, or several timed metadata tracks. Timed metadata tracks can refer to multiple tracks.
2073
					// Metadata tracks are linked to the tracks they describe using a track-reference of type 'cdsc'. The metadata track holds the 'cdsc' track reference.
2074
					$atom_structure['track_number'] = getid3_lib::BigEndian2Int($atom_data);
2075
					break;
2076
2077 View Code Duplication
				default:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2078
					$this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).'), '.$atomsize.' bytes at offset '.$baseoffset);
2079
					$atom_structure['data'] = $atom_data;
2080
					break;
2081
			}
2082
		}
2083
		array_pop($atomHierarchy);
2084
		return $atom_structure;
2085
	}
2086
2087
	/**
2088
	 * @param string $atom_data
2089
	 * @param int    $baseoffset
2090
	 * @param array  $atomHierarchy
2091
	 * @param bool   $ParseAllPossibleAtoms
2092
	 *
2093
	 * @return array|false
2094
	 */
2095
	public function QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
2096
		$atom_structure  = false;
2097
		$subatomoffset  = 0;
2098
		$subatomcounter = 0;
2099
		if ((strlen($atom_data) == 4) && (getid3_lib::BigEndian2Int($atom_data) == 0x00000000)) {
2100
			return false;
2101
		}
2102
		while ($subatomoffset < strlen($atom_data)) {
2103
			$subatomsize = getid3_lib::BigEndian2Int(substr($atom_data, $subatomoffset + 0, 4));
2104
			$subatomname =                           substr($atom_data, $subatomoffset + 4, 4);
2105
			$subatomdata =                           substr($atom_data, $subatomoffset + 8, $subatomsize - 8);
2106
			if ($subatomsize == 0) {
2107
				// Furthermore, for historical reasons the list of atoms is optionally
2108
				// terminated by a 32-bit integer set to 0. If you are writing a program
2109
				// to read user data atoms, you should allow for the terminating 0.
2110
				if (strlen($atom_data) > 12) {
2111
					$subatomoffset += 4;
2112
					continue;
2113
				}
2114
				return $atom_structure;
2115
			}
2116
			if (strlen($subatomdata) < ($subatomsize - 8)) {
2117
			    // we don't have enough data to decode the subatom.
2118
			    // this may be because we are refusing to parse large subatoms, or it may be because this atom had its size set too large
2119
			    // so we passed in the start of a following atom incorrectly?
2120
			    return $atom_structure;
2121
			}
2122
			$atom_structure[$subatomcounter++] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms);
2123
			$subatomoffset += $subatomsize;
2124
		}
2125
		return $atom_structure;
2126
	}
2127
2128
	/**
2129
	 * @param string $data
2130
	 * @param int    $offset
2131
	 *
2132
	 * @return int
2133
	 */
2134
	public function quicktime_read_mp4_descr_length($data, &$offset) {
2135
		// http://libquicktime.sourcearchive.com/documentation/2:1.0.2plus-pdebian-2build1/esds_8c-source.html
2136
		$num_bytes = 0;
2137
		$length    = 0;
2138
		do {
2139
			$b = ord(substr($data, $offset++, 1));
2140
			$length = ($length << 7) | ($b & 0x7F);
2141
		} while (($b & 0x80) && ($num_bytes++ < 4));
2142
		return $length;
2143
	}
2144
2145
	/**
2146
	 * @param int $languageid
2147
	 *
2148
	 * @return string
2149
	 */
2150
	public function QuicktimeLanguageLookup($languageid) {
2151
		// http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-34353
2152
		static $QuicktimeLanguageLookup = array();
2153
		if (empty($QuicktimeLanguageLookup)) {
2154
			$QuicktimeLanguageLookup[0]     = 'English';
2155
			$QuicktimeLanguageLookup[1]     = 'French';
2156
			$QuicktimeLanguageLookup[2]     = 'German';
2157
			$QuicktimeLanguageLookup[3]     = 'Italian';
2158
			$QuicktimeLanguageLookup[4]     = 'Dutch';
2159
			$QuicktimeLanguageLookup[5]     = 'Swedish';
2160
			$QuicktimeLanguageLookup[6]     = 'Spanish';
2161
			$QuicktimeLanguageLookup[7]     = 'Danish';
2162
			$QuicktimeLanguageLookup[8]     = 'Portuguese';
2163
			$QuicktimeLanguageLookup[9]     = 'Norwegian';
2164
			$QuicktimeLanguageLookup[10]    = 'Hebrew';
2165
			$QuicktimeLanguageLookup[11]    = 'Japanese';
2166
			$QuicktimeLanguageLookup[12]    = 'Arabic';
2167
			$QuicktimeLanguageLookup[13]    = 'Finnish';
2168
			$QuicktimeLanguageLookup[14]    = 'Greek';
2169
			$QuicktimeLanguageLookup[15]    = 'Icelandic';
2170
			$QuicktimeLanguageLookup[16]    = 'Maltese';
2171
			$QuicktimeLanguageLookup[17]    = 'Turkish';
2172
			$QuicktimeLanguageLookup[18]    = 'Croatian';
2173
			$QuicktimeLanguageLookup[19]    = 'Chinese (Traditional)';
2174
			$QuicktimeLanguageLookup[20]    = 'Urdu';
2175
			$QuicktimeLanguageLookup[21]    = 'Hindi';
2176
			$QuicktimeLanguageLookup[22]    = 'Thai';
2177
			$QuicktimeLanguageLookup[23]    = 'Korean';
2178
			$QuicktimeLanguageLookup[24]    = 'Lithuanian';
2179
			$QuicktimeLanguageLookup[25]    = 'Polish';
2180
			$QuicktimeLanguageLookup[26]    = 'Hungarian';
2181
			$QuicktimeLanguageLookup[27]    = 'Estonian';
2182
			$QuicktimeLanguageLookup[28]    = 'Lettish';
2183
			$QuicktimeLanguageLookup[28]    = 'Latvian';
2184
			$QuicktimeLanguageLookup[29]    = 'Saamisk';
2185
			$QuicktimeLanguageLookup[29]    = 'Lappish';
2186
			$QuicktimeLanguageLookup[30]    = 'Faeroese';
2187
			$QuicktimeLanguageLookup[31]    = 'Farsi';
2188
			$QuicktimeLanguageLookup[31]    = 'Persian';
2189
			$QuicktimeLanguageLookup[32]    = 'Russian';
2190
			$QuicktimeLanguageLookup[33]    = 'Chinese (Simplified)';
2191
			$QuicktimeLanguageLookup[34]    = 'Flemish';
2192
			$QuicktimeLanguageLookup[35]    = 'Irish';
2193
			$QuicktimeLanguageLookup[36]    = 'Albanian';
2194
			$QuicktimeLanguageLookup[37]    = 'Romanian';
2195
			$QuicktimeLanguageLookup[38]    = 'Czech';
2196
			$QuicktimeLanguageLookup[39]    = 'Slovak';
2197
			$QuicktimeLanguageLookup[40]    = 'Slovenian';
2198
			$QuicktimeLanguageLookup[41]    = 'Yiddish';
2199
			$QuicktimeLanguageLookup[42]    = 'Serbian';
2200
			$QuicktimeLanguageLookup[43]    = 'Macedonian';
2201
			$QuicktimeLanguageLookup[44]    = 'Bulgarian';
2202
			$QuicktimeLanguageLookup[45]    = 'Ukrainian';
2203
			$QuicktimeLanguageLookup[46]    = 'Byelorussian';
2204
			$QuicktimeLanguageLookup[47]    = 'Uzbek';
2205
			$QuicktimeLanguageLookup[48]    = 'Kazakh';
2206
			$QuicktimeLanguageLookup[49]    = 'Azerbaijani';
2207
			$QuicktimeLanguageLookup[50]    = 'AzerbaijanAr';
2208
			$QuicktimeLanguageLookup[51]    = 'Armenian';
2209
			$QuicktimeLanguageLookup[52]    = 'Georgian';
2210
			$QuicktimeLanguageLookup[53]    = 'Moldavian';
2211
			$QuicktimeLanguageLookup[54]    = 'Kirghiz';
2212
			$QuicktimeLanguageLookup[55]    = 'Tajiki';
2213
			$QuicktimeLanguageLookup[56]    = 'Turkmen';
2214
			$QuicktimeLanguageLookup[57]    = 'Mongolian';
2215
			$QuicktimeLanguageLookup[58]    = 'MongolianCyr';
2216
			$QuicktimeLanguageLookup[59]    = 'Pashto';
2217
			$QuicktimeLanguageLookup[60]    = 'Kurdish';
2218
			$QuicktimeLanguageLookup[61]    = 'Kashmiri';
2219
			$QuicktimeLanguageLookup[62]    = 'Sindhi';
2220
			$QuicktimeLanguageLookup[63]    = 'Tibetan';
2221
			$QuicktimeLanguageLookup[64]    = 'Nepali';
2222
			$QuicktimeLanguageLookup[65]    = 'Sanskrit';
2223
			$QuicktimeLanguageLookup[66]    = 'Marathi';
2224
			$QuicktimeLanguageLookup[67]    = 'Bengali';
2225
			$QuicktimeLanguageLookup[68]    = 'Assamese';
2226
			$QuicktimeLanguageLookup[69]    = 'Gujarati';
2227
			$QuicktimeLanguageLookup[70]    = 'Punjabi';
2228
			$QuicktimeLanguageLookup[71]    = 'Oriya';
2229
			$QuicktimeLanguageLookup[72]    = 'Malayalam';
2230
			$QuicktimeLanguageLookup[73]    = 'Kannada';
2231
			$QuicktimeLanguageLookup[74]    = 'Tamil';
2232
			$QuicktimeLanguageLookup[75]    = 'Telugu';
2233
			$QuicktimeLanguageLookup[76]    = 'Sinhalese';
2234
			$QuicktimeLanguageLookup[77]    = 'Burmese';
2235
			$QuicktimeLanguageLookup[78]    = 'Khmer';
2236
			$QuicktimeLanguageLookup[79]    = 'Lao';
2237
			$QuicktimeLanguageLookup[80]    = 'Vietnamese';
2238
			$QuicktimeLanguageLookup[81]    = 'Indonesian';
2239
			$QuicktimeLanguageLookup[82]    = 'Tagalog';
2240
			$QuicktimeLanguageLookup[83]    = 'MalayRoman';
2241
			$QuicktimeLanguageLookup[84]    = 'MalayArabic';
2242
			$QuicktimeLanguageLookup[85]    = 'Amharic';
2243
			$QuicktimeLanguageLookup[86]    = 'Tigrinya';
2244
			$QuicktimeLanguageLookup[87]    = 'Galla';
2245
			$QuicktimeLanguageLookup[87]    = 'Oromo';
2246
			$QuicktimeLanguageLookup[88]    = 'Somali';
2247
			$QuicktimeLanguageLookup[89]    = 'Swahili';
2248
			$QuicktimeLanguageLookup[90]    = 'Ruanda';
2249
			$QuicktimeLanguageLookup[91]    = 'Rundi';
2250
			$QuicktimeLanguageLookup[92]    = 'Chewa';
2251
			$QuicktimeLanguageLookup[93]    = 'Malagasy';
2252
			$QuicktimeLanguageLookup[94]    = 'Esperanto';
2253
			$QuicktimeLanguageLookup[128]   = 'Welsh';
2254
			$QuicktimeLanguageLookup[129]   = 'Basque';
2255
			$QuicktimeLanguageLookup[130]   = 'Catalan';
2256
			$QuicktimeLanguageLookup[131]   = 'Latin';
2257
			$QuicktimeLanguageLookup[132]   = 'Quechua';
2258
			$QuicktimeLanguageLookup[133]   = 'Guarani';
2259
			$QuicktimeLanguageLookup[134]   = 'Aymara';
2260
			$QuicktimeLanguageLookup[135]   = 'Tatar';
2261
			$QuicktimeLanguageLookup[136]   = 'Uighur';
2262
			$QuicktimeLanguageLookup[137]   = 'Dzongkha';
2263
			$QuicktimeLanguageLookup[138]   = 'JavaneseRom';
2264
			$QuicktimeLanguageLookup[32767] = 'Unspecified';
2265
		}
2266
		if (($languageid > 138) && ($languageid < 32767)) {
2267
			/*
2268
			ISO Language Codes - http://www.loc.gov/standards/iso639-2/php/code_list.php
2269
			Because the language codes specified by ISO 639-2/T are three characters long, they must be packed to fit into a 16-bit field.
2270
			The packing algorithm must map each of the three characters, which are always lowercase, into a 5-bit integer and then concatenate
2271
			these integers into the least significant 15 bits of a 16-bit integer, leaving the 16-bit integer's most significant bit set to zero.
2272
2273
			One algorithm for performing this packing is to treat each ISO character as a 16-bit integer. Subtract 0x60 from the first character
2274
			and multiply by 2^10 (0x400), subtract 0x60 from the second character and multiply by 2^5 (0x20), subtract 0x60 from the third character,
2275
			and add the three 16-bit values. This will result in a single 16-bit value with the three codes correctly packed into the 15 least
2276
			significant bits and the most significant bit set to zero.
2277
			*/
2278
			$iso_language_id  = '';
2279
			$iso_language_id .= chr((($languageid & 0x7C00) >> 10) + 0x60);
2280
			$iso_language_id .= chr((($languageid & 0x03E0) >>  5) + 0x60);
2281
			$iso_language_id .= chr((($languageid & 0x001F) >>  0) + 0x60);
2282
			$QuicktimeLanguageLookup[$languageid] = getid3_id3v2::LanguageLookup($iso_language_id);
2283
		}
2284
		return (isset($QuicktimeLanguageLookup[$languageid]) ? $QuicktimeLanguageLookup[$languageid] : 'invalid');
2285
	}
2286
2287
	/**
2288
	 * @param string $codecid
2289
	 *
2290
	 * @return string
2291
	 */
2292
	public function QuicktimeVideoCodecLookup($codecid) {
2293
		static $QuicktimeVideoCodecLookup = array();
2294
		if (empty($QuicktimeVideoCodecLookup)) {
2295
			$QuicktimeVideoCodecLookup['.SGI'] = 'SGI';
2296
			$QuicktimeVideoCodecLookup['3IV1'] = '3ivx MPEG-4 v1';
2297
			$QuicktimeVideoCodecLookup['3IV2'] = '3ivx MPEG-4 v2';
2298
			$QuicktimeVideoCodecLookup['3IVX'] = '3ivx MPEG-4';
2299
			$QuicktimeVideoCodecLookup['8BPS'] = 'Planar RGB';
2300
			$QuicktimeVideoCodecLookup['avc1'] = 'H.264/MPEG-4 AVC';
2301
			$QuicktimeVideoCodecLookup['avr '] = 'AVR-JPEG';
2302
			$QuicktimeVideoCodecLookup['b16g'] = '16Gray';
2303
			$QuicktimeVideoCodecLookup['b32a'] = '32AlphaGray';
2304
			$QuicktimeVideoCodecLookup['b48r'] = '48RGB';
2305
			$QuicktimeVideoCodecLookup['b64a'] = '64ARGB';
2306
			$QuicktimeVideoCodecLookup['base'] = 'Base';
2307
			$QuicktimeVideoCodecLookup['clou'] = 'Cloud';
2308
			$QuicktimeVideoCodecLookup['cmyk'] = 'CMYK';
2309
			$QuicktimeVideoCodecLookup['cvid'] = 'Cinepak';
2310
			$QuicktimeVideoCodecLookup['dmb1'] = 'OpenDML JPEG';
2311
			$QuicktimeVideoCodecLookup['dvc '] = 'DVC-NTSC';
2312
			$QuicktimeVideoCodecLookup['dvcp'] = 'DVC-PAL';
2313
			$QuicktimeVideoCodecLookup['dvpn'] = 'DVCPro-NTSC';
2314
			$QuicktimeVideoCodecLookup['dvpp'] = 'DVCPro-PAL';
2315
			$QuicktimeVideoCodecLookup['fire'] = 'Fire';
2316
			$QuicktimeVideoCodecLookup['flic'] = 'FLC';
2317
			$QuicktimeVideoCodecLookup['gif '] = 'GIF';
2318
			$QuicktimeVideoCodecLookup['h261'] = 'H261';
2319
			$QuicktimeVideoCodecLookup['h263'] = 'H263';
2320
			$QuicktimeVideoCodecLookup['IV41'] = 'Indeo4';
2321
			$QuicktimeVideoCodecLookup['jpeg'] = 'JPEG';
2322
			$QuicktimeVideoCodecLookup['kpcd'] = 'PhotoCD';
2323
			$QuicktimeVideoCodecLookup['mjpa'] = 'Motion JPEG-A';
2324
			$QuicktimeVideoCodecLookup['mjpb'] = 'Motion JPEG-B';
2325
			$QuicktimeVideoCodecLookup['msvc'] = 'Microsoft Video1';
2326
			$QuicktimeVideoCodecLookup['myuv'] = 'MPEG YUV420';
2327
			$QuicktimeVideoCodecLookup['path'] = 'Vector';
2328
			$QuicktimeVideoCodecLookup['png '] = 'PNG';
2329
			$QuicktimeVideoCodecLookup['PNTG'] = 'MacPaint';
2330
			$QuicktimeVideoCodecLookup['qdgx'] = 'QuickDrawGX';
2331
			$QuicktimeVideoCodecLookup['qdrw'] = 'QuickDraw';
2332
			$QuicktimeVideoCodecLookup['raw '] = 'RAW';
2333
			$QuicktimeVideoCodecLookup['ripl'] = 'WaterRipple';
2334
			$QuicktimeVideoCodecLookup['rpza'] = 'Video';
2335
			$QuicktimeVideoCodecLookup['smc '] = 'Graphics';
2336
			$QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 1';
2337
			$QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 3';
2338
			$QuicktimeVideoCodecLookup['syv9'] = 'Sorenson YUV9';
2339
			$QuicktimeVideoCodecLookup['tga '] = 'Targa';
2340
			$QuicktimeVideoCodecLookup['tiff'] = 'TIFF';
2341
			$QuicktimeVideoCodecLookup['WRAW'] = 'Windows RAW';
2342
			$QuicktimeVideoCodecLookup['WRLE'] = 'BMP';
2343
			$QuicktimeVideoCodecLookup['y420'] = 'YUV420';
2344
			$QuicktimeVideoCodecLookup['yuv2'] = 'ComponentVideo';
2345
			$QuicktimeVideoCodecLookup['yuvs'] = 'ComponentVideoUnsigned';
2346
			$QuicktimeVideoCodecLookup['yuvu'] = 'ComponentVideoSigned';
2347
		}
2348
		return (isset($QuicktimeVideoCodecLookup[$codecid]) ? $QuicktimeVideoCodecLookup[$codecid] : '');
2349
	}
2350
2351
	/**
2352
	 * @param string $codecid
2353
	 *
2354
	 * @return mixed|string
2355
	 */
2356
	public function QuicktimeAudioCodecLookup($codecid) {
2357
		static $QuicktimeAudioCodecLookup = array();
2358
		if (empty($QuicktimeAudioCodecLookup)) {
2359
			$QuicktimeAudioCodecLookup['.mp3']          = 'Fraunhofer MPEG Layer-III alias';
2360
			$QuicktimeAudioCodecLookup['aac ']          = 'ISO/IEC 14496-3 AAC';
2361
			$QuicktimeAudioCodecLookup['agsm']          = 'Apple GSM 10:1';
2362
			$QuicktimeAudioCodecLookup['alac']          = 'Apple Lossless Audio Codec';
2363
			$QuicktimeAudioCodecLookup['alaw']          = 'A-law 2:1';
2364
			$QuicktimeAudioCodecLookup['conv']          = 'Sample Format';
2365
			$QuicktimeAudioCodecLookup['dvca']          = 'DV';
2366
			$QuicktimeAudioCodecLookup['dvi ']          = 'DV 4:1';
2367
			$QuicktimeAudioCodecLookup['eqal']          = 'Frequency Equalizer';
2368
			$QuicktimeAudioCodecLookup['fl32']          = '32-bit Floating Point';
2369
			$QuicktimeAudioCodecLookup['fl64']          = '64-bit Floating Point';
2370
			$QuicktimeAudioCodecLookup['ima4']          = 'Interactive Multimedia Association 4:1';
2371
			$QuicktimeAudioCodecLookup['in24']          = '24-bit Integer';
2372
			$QuicktimeAudioCodecLookup['in32']          = '32-bit Integer';
2373
			$QuicktimeAudioCodecLookup['lpc ']          = 'LPC 23:1';
2374
			$QuicktimeAudioCodecLookup['MAC3']          = 'Macintosh Audio Compression/Expansion (MACE) 3:1';
2375
			$QuicktimeAudioCodecLookup['MAC6']          = 'Macintosh Audio Compression/Expansion (MACE) 6:1';
2376
			$QuicktimeAudioCodecLookup['mixb']          = '8-bit Mixer';
2377
			$QuicktimeAudioCodecLookup['mixw']          = '16-bit Mixer';
2378
			$QuicktimeAudioCodecLookup['mp4a']          = 'ISO/IEC 14496-3 AAC';
2379
			$QuicktimeAudioCodecLookup['MS'."\x00\x02"] = 'Microsoft ADPCM';
2380
			$QuicktimeAudioCodecLookup['MS'."\x00\x11"] = 'DV IMA';
2381
			$QuicktimeAudioCodecLookup['MS'."\x00\x55"] = 'Fraunhofer MPEG Layer III';
2382
			$QuicktimeAudioCodecLookup['NONE']          = 'No Encoding';
2383
			$QuicktimeAudioCodecLookup['Qclp']          = 'Qualcomm PureVoice';
2384
			$QuicktimeAudioCodecLookup['QDM2']          = 'QDesign Music 2';
2385
			$QuicktimeAudioCodecLookup['QDMC']          = 'QDesign Music 1';
2386
			$QuicktimeAudioCodecLookup['ratb']          = '8-bit Rate';
2387
			$QuicktimeAudioCodecLookup['ratw']          = '16-bit Rate';
2388
			$QuicktimeAudioCodecLookup['raw ']          = 'raw PCM';
2389
			$QuicktimeAudioCodecLookup['sour']          = 'Sound Source';
2390
			$QuicktimeAudioCodecLookup['sowt']          = 'signed/two\'s complement (Little Endian)';
2391
			$QuicktimeAudioCodecLookup['str1']          = 'Iomega MPEG layer II';
2392
			$QuicktimeAudioCodecLookup['str2']          = 'Iomega MPEG *layer II';
2393
			$QuicktimeAudioCodecLookup['str3']          = 'Iomega MPEG **layer II';
2394
			$QuicktimeAudioCodecLookup['str4']          = 'Iomega MPEG ***layer II';
2395
			$QuicktimeAudioCodecLookup['twos']          = 'signed/two\'s complement (Big Endian)';
2396
			$QuicktimeAudioCodecLookup['ulaw']          = 'mu-law 2:1';
2397
		}
2398
		return (isset($QuicktimeAudioCodecLookup[$codecid]) ? $QuicktimeAudioCodecLookup[$codecid] : '');
2399
	}
2400
2401
	/**
2402
	 * @param string $compressionid
2403
	 *
2404
	 * @return string
2405
	 */
2406
	public function QuicktimeDCOMLookup($compressionid) {
2407
		static $QuicktimeDCOMLookup = array();
2408
		if (empty($QuicktimeDCOMLookup)) {
2409
			$QuicktimeDCOMLookup['zlib'] = 'ZLib Deflate';
2410
			$QuicktimeDCOMLookup['adec'] = 'Apple Compression';
2411
		}
2412
		return (isset($QuicktimeDCOMLookup[$compressionid]) ? $QuicktimeDCOMLookup[$compressionid] : '');
2413
	}
2414
2415
	/**
2416
	 * @param int $colordepthid
2417
	 *
2418
	 * @return string
2419
	 */
2420
	public function QuicktimeColorNameLookup($colordepthid) {
2421
		static $QuicktimeColorNameLookup = array();
2422
		if (empty($QuicktimeColorNameLookup)) {
2423
			$QuicktimeColorNameLookup[1]  = '2-color (monochrome)';
2424
			$QuicktimeColorNameLookup[2]  = '4-color';
2425
			$QuicktimeColorNameLookup[4]  = '16-color';
2426
			$QuicktimeColorNameLookup[8]  = '256-color';
2427
			$QuicktimeColorNameLookup[16] = 'thousands (16-bit color)';
2428
			$QuicktimeColorNameLookup[24] = 'millions (24-bit color)';
2429
			$QuicktimeColorNameLookup[32] = 'millions+ (32-bit color)';
2430
			$QuicktimeColorNameLookup[33] = 'black & white';
2431
			$QuicktimeColorNameLookup[34] = '4-gray';
2432
			$QuicktimeColorNameLookup[36] = '16-gray';
2433
			$QuicktimeColorNameLookup[40] = '256-gray';
2434
		}
2435
		return (isset($QuicktimeColorNameLookup[$colordepthid]) ? $QuicktimeColorNameLookup[$colordepthid] : 'invalid');
2436
	}
2437
2438
	/**
2439
	 * @param int $stik
2440
	 *
2441
	 * @return string
2442
	 */
2443
	public function QuicktimeSTIKLookup($stik) {
2444
		static $QuicktimeSTIKLookup = array();
2445
		if (empty($QuicktimeSTIKLookup)) {
2446
			$QuicktimeSTIKLookup[0]  = 'Movie';
2447
			$QuicktimeSTIKLookup[1]  = 'Normal';
2448
			$QuicktimeSTIKLookup[2]  = 'Audiobook';
2449
			$QuicktimeSTIKLookup[5]  = 'Whacked Bookmark';
2450
			$QuicktimeSTIKLookup[6]  = 'Music Video';
2451
			$QuicktimeSTIKLookup[9]  = 'Short Film';
2452
			$QuicktimeSTIKLookup[10] = 'TV Show';
2453
			$QuicktimeSTIKLookup[11] = 'Booklet';
2454
			$QuicktimeSTIKLookup[14] = 'Ringtone';
2455
			$QuicktimeSTIKLookup[21] = 'Podcast';
2456
		}
2457
		return (isset($QuicktimeSTIKLookup[$stik]) ? $QuicktimeSTIKLookup[$stik] : 'invalid');
2458
	}
2459
2460
	/**
2461
	 * @param int $audio_profile_id
2462
	 *
2463
	 * @return string
2464
	 */
2465
	public function QuicktimeIODSaudioProfileName($audio_profile_id) {
2466
		static $QuicktimeIODSaudioProfileNameLookup = array();
2467
		if (empty($QuicktimeIODSaudioProfileNameLookup)) {
2468
			$QuicktimeIODSaudioProfileNameLookup = array(
2469
				0x00 => 'ISO Reserved (0x00)',
2470
				0x01 => 'Main Audio Profile @ Level 1',
2471
				0x02 => 'Main Audio Profile @ Level 2',
2472
				0x03 => 'Main Audio Profile @ Level 3',
2473
				0x04 => 'Main Audio Profile @ Level 4',
2474
				0x05 => 'Scalable Audio Profile @ Level 1',
2475
				0x06 => 'Scalable Audio Profile @ Level 2',
2476
				0x07 => 'Scalable Audio Profile @ Level 3',
2477
				0x08 => 'Scalable Audio Profile @ Level 4',
2478
				0x09 => 'Speech Audio Profile @ Level 1',
2479
				0x0A => 'Speech Audio Profile @ Level 2',
2480
				0x0B => 'Synthetic Audio Profile @ Level 1',
2481
				0x0C => 'Synthetic Audio Profile @ Level 2',
2482
				0x0D => 'Synthetic Audio Profile @ Level 3',
2483
				0x0E => 'High Quality Audio Profile @ Level 1',
2484
				0x0F => 'High Quality Audio Profile @ Level 2',
2485
				0x10 => 'High Quality Audio Profile @ Level 3',
2486
				0x11 => 'High Quality Audio Profile @ Level 4',
2487
				0x12 => 'High Quality Audio Profile @ Level 5',
2488
				0x13 => 'High Quality Audio Profile @ Level 6',
2489
				0x14 => 'High Quality Audio Profile @ Level 7',
2490
				0x15 => 'High Quality Audio Profile @ Level 8',
2491
				0x16 => 'Low Delay Audio Profile @ Level 1',
2492
				0x17 => 'Low Delay Audio Profile @ Level 2',
2493
				0x18 => 'Low Delay Audio Profile @ Level 3',
2494
				0x19 => 'Low Delay Audio Profile @ Level 4',
2495
				0x1A => 'Low Delay Audio Profile @ Level 5',
2496
				0x1B => 'Low Delay Audio Profile @ Level 6',
2497
				0x1C => 'Low Delay Audio Profile @ Level 7',
2498
				0x1D => 'Low Delay Audio Profile @ Level 8',
2499
				0x1E => 'Natural Audio Profile @ Level 1',
2500
				0x1F => 'Natural Audio Profile @ Level 2',
2501
				0x20 => 'Natural Audio Profile @ Level 3',
2502
				0x21 => 'Natural Audio Profile @ Level 4',
2503
				0x22 => 'Mobile Audio Internetworking Profile @ Level 1',
2504
				0x23 => 'Mobile Audio Internetworking Profile @ Level 2',
2505
				0x24 => 'Mobile Audio Internetworking Profile @ Level 3',
2506
				0x25 => 'Mobile Audio Internetworking Profile @ Level 4',
2507
				0x26 => 'Mobile Audio Internetworking Profile @ Level 5',
2508
				0x27 => 'Mobile Audio Internetworking Profile @ Level 6',
2509
				0x28 => 'AAC Profile @ Level 1',
2510
				0x29 => 'AAC Profile @ Level 2',
2511
				0x2A => 'AAC Profile @ Level 4',
2512
				0x2B => 'AAC Profile @ Level 5',
2513
				0x2C => 'High Efficiency AAC Profile @ Level 2',
2514
				0x2D => 'High Efficiency AAC Profile @ Level 3',
2515
				0x2E => 'High Efficiency AAC Profile @ Level 4',
2516
				0x2F => 'High Efficiency AAC Profile @ Level 5',
2517
				0xFE => 'Not part of MPEG-4 audio profiles',
2518
				0xFF => 'No audio capability required',
2519
			);
2520
		}
2521
		return (isset($QuicktimeIODSaudioProfileNameLookup[$audio_profile_id]) ? $QuicktimeIODSaudioProfileNameLookup[$audio_profile_id] : 'ISO Reserved / User Private');
2522
	}
2523
2524
	/**
2525
	 * @param int $video_profile_id
2526
	 *
2527
	 * @return string
2528
	 */
2529
	public function QuicktimeIODSvideoProfileName($video_profile_id) {
2530
		static $QuicktimeIODSvideoProfileNameLookup = array();
2531
		if (empty($QuicktimeIODSvideoProfileNameLookup)) {
2532
			$QuicktimeIODSvideoProfileNameLookup = array(
2533
				0x00 => 'Reserved (0x00) Profile',
2534
				0x01 => 'Simple Profile @ Level 1',
2535
				0x02 => 'Simple Profile @ Level 2',
2536
				0x03 => 'Simple Profile @ Level 3',
2537
				0x08 => 'Simple Profile @ Level 0',
2538
				0x10 => 'Simple Scalable Profile @ Level 0',
2539
				0x11 => 'Simple Scalable Profile @ Level 1',
2540
				0x12 => 'Simple Scalable Profile @ Level 2',
2541
				0x15 => 'AVC/H264 Profile',
2542
				0x21 => 'Core Profile @ Level 1',
2543
				0x22 => 'Core Profile @ Level 2',
2544
				0x32 => 'Main Profile @ Level 2',
2545
				0x33 => 'Main Profile @ Level 3',
2546
				0x34 => 'Main Profile @ Level 4',
2547
				0x42 => 'N-bit Profile @ Level 2',
2548
				0x51 => 'Scalable Texture Profile @ Level 1',
2549
				0x61 => 'Simple Face Animation Profile @ Level 1',
2550
				0x62 => 'Simple Face Animation Profile @ Level 2',
2551
				0x63 => 'Simple FBA Profile @ Level 1',
2552
				0x64 => 'Simple FBA Profile @ Level 2',
2553
				0x71 => 'Basic Animated Texture Profile @ Level 1',
2554
				0x72 => 'Basic Animated Texture Profile @ Level 2',
2555
				0x81 => 'Hybrid Profile @ Level 1',
2556
				0x82 => 'Hybrid Profile @ Level 2',
2557
				0x91 => 'Advanced Real Time Simple Profile @ Level 1',
2558
				0x92 => 'Advanced Real Time Simple Profile @ Level 2',
2559
				0x93 => 'Advanced Real Time Simple Profile @ Level 3',
2560
				0x94 => 'Advanced Real Time Simple Profile @ Level 4',
2561
				0xA1 => 'Core Scalable Profile @ Level1',
2562
				0xA2 => 'Core Scalable Profile @ Level2',
2563
				0xA3 => 'Core Scalable Profile @ Level3',
2564
				0xB1 => 'Advanced Coding Efficiency Profile @ Level 1',
2565
				0xB2 => 'Advanced Coding Efficiency Profile @ Level 2',
2566
				0xB3 => 'Advanced Coding Efficiency Profile @ Level 3',
2567
				0xB4 => 'Advanced Coding Efficiency Profile @ Level 4',
2568
				0xC1 => 'Advanced Core Profile @ Level 1',
2569
				0xC2 => 'Advanced Core Profile @ Level 2',
2570
				0xD1 => 'Advanced Scalable Texture @ Level1',
2571
				0xD2 => 'Advanced Scalable Texture @ Level2',
2572
				0xE1 => 'Simple Studio Profile @ Level 1',
2573
				0xE2 => 'Simple Studio Profile @ Level 2',
2574
				0xE3 => 'Simple Studio Profile @ Level 3',
2575
				0xE4 => 'Simple Studio Profile @ Level 4',
2576
				0xE5 => 'Core Studio Profile @ Level 1',
2577
				0xE6 => 'Core Studio Profile @ Level 2',
2578
				0xE7 => 'Core Studio Profile @ Level 3',
2579
				0xE8 => 'Core Studio Profile @ Level 4',
2580
				0xF0 => 'Advanced Simple Profile @ Level 0',
2581
				0xF1 => 'Advanced Simple Profile @ Level 1',
2582
				0xF2 => 'Advanced Simple Profile @ Level 2',
2583
				0xF3 => 'Advanced Simple Profile @ Level 3',
2584
				0xF4 => 'Advanced Simple Profile @ Level 4',
2585
				0xF5 => 'Advanced Simple Profile @ Level 5',
2586
				0xF7 => 'Advanced Simple Profile @ Level 3b',
2587
				0xF8 => 'Fine Granularity Scalable Profile @ Level 0',
2588
				0xF9 => 'Fine Granularity Scalable Profile @ Level 1',
2589
				0xFA => 'Fine Granularity Scalable Profile @ Level 2',
2590
				0xFB => 'Fine Granularity Scalable Profile @ Level 3',
2591
				0xFC => 'Fine Granularity Scalable Profile @ Level 4',
2592
				0xFD => 'Fine Granularity Scalable Profile @ Level 5',
2593
				0xFE => 'Not part of MPEG-4 Visual profiles',
2594
				0xFF => 'No visual capability required',
2595
			);
2596
		}
2597
		return (isset($QuicktimeIODSvideoProfileNameLookup[$video_profile_id]) ? $QuicktimeIODSvideoProfileNameLookup[$video_profile_id] : 'ISO Reserved Profile');
2598
	}
2599
2600
	/**
2601
	 * @param int $rtng
2602
	 *
2603
	 * @return string
2604
	 */
2605 View Code Duplication
	public function QuicktimeContentRatingLookup($rtng) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2606
		static $QuicktimeContentRatingLookup = array();
2607
		if (empty($QuicktimeContentRatingLookup)) {
2608
			$QuicktimeContentRatingLookup[0]  = 'None';
2609
			$QuicktimeContentRatingLookup[2]  = 'Clean';
2610
			$QuicktimeContentRatingLookup[4]  = 'Explicit';
2611
		}
2612
		return (isset($QuicktimeContentRatingLookup[$rtng]) ? $QuicktimeContentRatingLookup[$rtng] : 'invalid');
2613
	}
2614
2615
	/**
2616
	 * @param int $akid
2617
	 *
2618
	 * @return string
2619
	 */
2620
	public function QuicktimeStoreAccountTypeLookup($akid) {
2621
		static $QuicktimeStoreAccountTypeLookup = array();
2622
		if (empty($QuicktimeStoreAccountTypeLookup)) {
2623
			$QuicktimeStoreAccountTypeLookup[0] = 'iTunes';
2624
			$QuicktimeStoreAccountTypeLookup[1] = 'AOL';
2625
		}
2626
		return (isset($QuicktimeStoreAccountTypeLookup[$akid]) ? $QuicktimeStoreAccountTypeLookup[$akid] : 'invalid');
2627
	}
2628
2629
	/**
2630
	 * @param int $sfid
2631
	 *
2632
	 * @return string
2633
	 */
2634
	public function QuicktimeStoreFrontCodeLookup($sfid) {
2635
		static $QuicktimeStoreFrontCodeLookup = array();
2636
		if (empty($QuicktimeStoreFrontCodeLookup)) {
2637
			$QuicktimeStoreFrontCodeLookup[143460] = 'Australia';
2638
			$QuicktimeStoreFrontCodeLookup[143445] = 'Austria';
2639
			$QuicktimeStoreFrontCodeLookup[143446] = 'Belgium';
2640
			$QuicktimeStoreFrontCodeLookup[143455] = 'Canada';
2641
			$QuicktimeStoreFrontCodeLookup[143458] = 'Denmark';
2642
			$QuicktimeStoreFrontCodeLookup[143447] = 'Finland';
2643
			$QuicktimeStoreFrontCodeLookup[143442] = 'France';
2644
			$QuicktimeStoreFrontCodeLookup[143443] = 'Germany';
2645
			$QuicktimeStoreFrontCodeLookup[143448] = 'Greece';
2646
			$QuicktimeStoreFrontCodeLookup[143449] = 'Ireland';
2647
			$QuicktimeStoreFrontCodeLookup[143450] = 'Italy';
2648
			$QuicktimeStoreFrontCodeLookup[143462] = 'Japan';
2649
			$QuicktimeStoreFrontCodeLookup[143451] = 'Luxembourg';
2650
			$QuicktimeStoreFrontCodeLookup[143452] = 'Netherlands';
2651
			$QuicktimeStoreFrontCodeLookup[143461] = 'New Zealand';
2652
			$QuicktimeStoreFrontCodeLookup[143457] = 'Norway';
2653
			$QuicktimeStoreFrontCodeLookup[143453] = 'Portugal';
2654
			$QuicktimeStoreFrontCodeLookup[143454] = 'Spain';
2655
			$QuicktimeStoreFrontCodeLookup[143456] = 'Sweden';
2656
			$QuicktimeStoreFrontCodeLookup[143459] = 'Switzerland';
2657
			$QuicktimeStoreFrontCodeLookup[143444] = 'United Kingdom';
2658
			$QuicktimeStoreFrontCodeLookup[143441] = 'United States';
2659
		}
2660
		return (isset($QuicktimeStoreFrontCodeLookup[$sfid]) ? $QuicktimeStoreFrontCodeLookup[$sfid] : 'invalid');
2661
	}
2662
2663
	/**
2664
	 * @param string $keyname
2665
	 * @param string|array $data
2666
	 * @param string $boxname
2667
	 *
2668
	 * @return bool
2669
	 */
2670
	public function CopyToAppropriateCommentsSection($keyname, $data, $boxname='') {
2671
		static $handyatomtranslatorarray = array();
2672
		if (empty($handyatomtranslatorarray)) {
2673
			// http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
2674
			// http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
2675
			// http://atomicparsley.sourceforge.net/mpeg-4files.html
2676
			// https://code.google.com/p/mp4v2/wiki/iTunesMetadata
2677
			$handyatomtranslatorarray["\xA9".'alb'] = 'album';               // iTunes 4.0
2678
			$handyatomtranslatorarray["\xA9".'ART'] = 'artist';
2679
			$handyatomtranslatorarray["\xA9".'art'] = 'artist';              // iTunes 4.0
2680
			$handyatomtranslatorarray["\xA9".'aut'] = 'author';
2681
			$handyatomtranslatorarray["\xA9".'cmt'] = 'comment';             // iTunes 4.0
2682
			$handyatomtranslatorarray["\xA9".'com'] = 'comment';
2683
			$handyatomtranslatorarray["\xA9".'cpy'] = 'copyright';
2684
			$handyatomtranslatorarray["\xA9".'day'] = 'creation_date';       // iTunes 4.0
2685
			$handyatomtranslatorarray["\xA9".'dir'] = 'director';
2686
			$handyatomtranslatorarray["\xA9".'ed1'] = 'edit1';
2687
			$handyatomtranslatorarray["\xA9".'ed2'] = 'edit2';
2688
			$handyatomtranslatorarray["\xA9".'ed3'] = 'edit3';
2689
			$handyatomtranslatorarray["\xA9".'ed4'] = 'edit4';
2690
			$handyatomtranslatorarray["\xA9".'ed5'] = 'edit5';
2691
			$handyatomtranslatorarray["\xA9".'ed6'] = 'edit6';
2692
			$handyatomtranslatorarray["\xA9".'ed7'] = 'edit7';
2693
			$handyatomtranslatorarray["\xA9".'ed8'] = 'edit8';
2694
			$handyatomtranslatorarray["\xA9".'ed9'] = 'edit9';
2695
			$handyatomtranslatorarray["\xA9".'enc'] = 'encoded_by';
2696
			$handyatomtranslatorarray["\xA9".'fmt'] = 'format';
2697
			$handyatomtranslatorarray["\xA9".'gen'] = 'genre';               // iTunes 4.0
2698
			$handyatomtranslatorarray["\xA9".'grp'] = 'grouping';            // iTunes 4.2
2699
			$handyatomtranslatorarray["\xA9".'hst'] = 'host_computer';
2700
			$handyatomtranslatorarray["\xA9".'inf'] = 'information';
2701
			$handyatomtranslatorarray["\xA9".'lyr'] = 'lyrics';              // iTunes 5.0
2702
			$handyatomtranslatorarray["\xA9".'mak'] = 'make';
2703
			$handyatomtranslatorarray["\xA9".'mod'] = 'model';
2704
			$handyatomtranslatorarray["\xA9".'nam'] = 'title';               // iTunes 4.0
2705
			$handyatomtranslatorarray["\xA9".'ope'] = 'composer';
2706
			$handyatomtranslatorarray["\xA9".'prd'] = 'producer';
2707
			$handyatomtranslatorarray["\xA9".'PRD'] = 'product';
2708
			$handyatomtranslatorarray["\xA9".'prf'] = 'performers';
2709
			$handyatomtranslatorarray["\xA9".'req'] = 'system_requirements';
2710
			$handyatomtranslatorarray["\xA9".'src'] = 'source_credit';
2711
			$handyatomtranslatorarray["\xA9".'swr'] = 'software';
2712
			$handyatomtranslatorarray["\xA9".'too'] = 'encoding_tool';       // iTunes 4.0
2713
			$handyatomtranslatorarray["\xA9".'trk'] = 'track_number';
2714
			$handyatomtranslatorarray["\xA9".'url'] = 'url';
2715
			$handyatomtranslatorarray["\xA9".'wrn'] = 'warning';
2716
			$handyatomtranslatorarray["\xA9".'wrt'] = 'composer';
2717
			$handyatomtranslatorarray['aART'] = 'album_artist';
2718
			$handyatomtranslatorarray['apID'] = 'purchase_account';
2719
			$handyatomtranslatorarray['catg'] = 'category';            // iTunes 4.9
2720
			$handyatomtranslatorarray['covr'] = 'picture';             // iTunes 4.0
2721
			$handyatomtranslatorarray['cpil'] = 'compilation';         // iTunes 4.0
2722
			$handyatomtranslatorarray['cprt'] = 'copyright';           // iTunes 4.0?
2723
			$handyatomtranslatorarray['desc'] = 'description';         // iTunes 5.0
2724
			$handyatomtranslatorarray['disk'] = 'disc_number';         // iTunes 4.0
2725
			$handyatomtranslatorarray['egid'] = 'episode_guid';        // iTunes 4.9
2726
			$handyatomtranslatorarray['gnre'] = 'genre';               // iTunes 4.0
2727
			$handyatomtranslatorarray['hdvd'] = 'hd_video';            // iTunes 4.0
2728
			$handyatomtranslatorarray['ldes'] = 'description_long';    //
2729
			$handyatomtranslatorarray['keyw'] = 'keyword';             // iTunes 4.9
2730
			$handyatomtranslatorarray['pcst'] = 'podcast';             // iTunes 4.9
2731
			$handyatomtranslatorarray['pgap'] = 'gapless_playback';    // iTunes 7.0
2732
			$handyatomtranslatorarray['purd'] = 'purchase_date';       // iTunes 6.0.2
2733
			$handyatomtranslatorarray['purl'] = 'podcast_url';         // iTunes 4.9
2734
			$handyatomtranslatorarray['rtng'] = 'rating';              // iTunes 4.0
2735
			$handyatomtranslatorarray['soaa'] = 'sort_album_artist';   //
2736
			$handyatomtranslatorarray['soal'] = 'sort_album';          //
2737
			$handyatomtranslatorarray['soar'] = 'sort_artist';         //
2738
			$handyatomtranslatorarray['soco'] = 'sort_composer';       //
2739
			$handyatomtranslatorarray['sonm'] = 'sort_title';          //
2740
			$handyatomtranslatorarray['sosn'] = 'sort_show';           //
2741
			$handyatomtranslatorarray['stik'] = 'stik';                // iTunes 4.9
2742
			$handyatomtranslatorarray['tmpo'] = 'bpm';                 // iTunes 4.0
2743
			$handyatomtranslatorarray['trkn'] = 'track_number';        // iTunes 4.0
2744
			$handyatomtranslatorarray['tven'] = 'tv_episode_id';       //
2745
			$handyatomtranslatorarray['tves'] = 'tv_episode';          // iTunes 6.0
2746
			$handyatomtranslatorarray['tvnn'] = 'tv_network_name';     // iTunes 6.0
2747
			$handyatomtranslatorarray['tvsh'] = 'tv_show_name';        // iTunes 6.0
2748
			$handyatomtranslatorarray['tvsn'] = 'tv_season';           // iTunes 6.0
2749
2750
			// boxnames:
2751
			/*
2752
			$handyatomtranslatorarray['iTunSMPB']                    = 'iTunSMPB';
2753
			$handyatomtranslatorarray['iTunNORM']                    = 'iTunNORM';
2754
			$handyatomtranslatorarray['Encoding Params']             = 'Encoding Params';
2755
			$handyatomtranslatorarray['replaygain_track_gain']       = 'replaygain_track_gain';
2756
			$handyatomtranslatorarray['replaygain_track_peak']       = 'replaygain_track_peak';
2757
			$handyatomtranslatorarray['replaygain_track_minmax']     = 'replaygain_track_minmax';
2758
			$handyatomtranslatorarray['MusicIP PUID']                = 'MusicIP PUID';
2759
			$handyatomtranslatorarray['MusicBrainz Artist Id']       = 'MusicBrainz Artist Id';
2760
			$handyatomtranslatorarray['MusicBrainz Album Id']        = 'MusicBrainz Album Id';
2761
			$handyatomtranslatorarray['MusicBrainz Album Artist Id'] = 'MusicBrainz Album Artist Id';
2762
			$handyatomtranslatorarray['MusicBrainz Track Id']        = 'MusicBrainz Track Id';
2763
			$handyatomtranslatorarray['MusicBrainz Disc Id']         = 'MusicBrainz Disc Id';
2764
2765
			// http://age.hobba.nl/audio/tag_frame_reference.html
2766
			$handyatomtranslatorarray['PLAY_COUNTER']                = 'play_counter'; // Foobar2000 - https://www.getid3.org/phpBB3/viewtopic.php?t=1355
2767
			$handyatomtranslatorarray['MEDIATYPE']                   = 'mediatype';    // Foobar2000 - https://www.getid3.org/phpBB3/viewtopic.php?t=1355
2768
			*/
2769
		}
2770
		$info = &$this->getid3->info;
2771
		$comment_key = '';
2772
		if ($boxname && ($boxname != $keyname)) {
2773
			$comment_key = (isset($handyatomtranslatorarray[$boxname]) ? $handyatomtranslatorarray[$boxname] : $boxname);
2774
		} elseif (isset($handyatomtranslatorarray[$keyname])) {
2775
			$comment_key = $handyatomtranslatorarray[$keyname];
2776
		}
2777
		if ($comment_key) {
2778
			if ($comment_key == 'picture') {
2779
				// already copied directly into [comments][picture] elsewhere, do not re-copy here
2780
				return true;
2781
			}
2782
			$gooddata = array($data);
2783
			if ($comment_key == 'genre') {
2784
				// some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal"
2785
				$gooddata = explode(';', $data);
2786
			}
2787
			foreach ($gooddata as $data) {
2788
				if (!empty($info['quicktime']['comments'][$comment_key]) && in_array($data, $info['quicktime']['comments'][$comment_key], true)) {
2789
					// avoid duplicate copies of identical data
2790
					continue;
2791
				}
2792
				$info['quicktime']['comments'][$comment_key][] = $data;
2793
			}
2794
		}
2795
		return true;
2796
	}
2797
2798
	/**
2799
	 * @param string $lstring
2800
	 * @param int    $count
2801
	 *
2802
	 * @return string
2803
	 */
2804
	public function LociString($lstring, &$count) {
2805
		// Loci strings are UTF-8 or UTF-16 and null (x00/x0000) terminated. UTF-16 has a BOM
2806
		// Also need to return the number of bytes the string occupied so additional fields can be extracted
2807
		$len = strlen($lstring);
2808
		if ($len == 0) {
2809
			$count = 0;
2810
			return '';
2811
		}
2812
		if ($lstring[0] == "\x00") {
2813
			$count = 1;
2814
			return '';
2815
		}
2816
		// check for BOM
2817
		if (($len > 2) && ((($lstring[0] == "\xFE") && ($lstring[1] == "\xFF")) || (($lstring[0] == "\xFF") && ($lstring[1] == "\xFE")))) {
2818
			// UTF-16
2819 View Code Duplication
			if (preg_match('/(.*)\x00/', $lstring, $lmatches)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2820
				$count = strlen($lmatches[1]) * 2 + 2; //account for 2 byte characters and trailing \x0000
2821
				return getid3_lib::iconv_fallback_utf16_utf8($lmatches[1]);
2822
			} else {
2823
				return '';
2824
			}
2825
		}
2826
		// UTF-8
2827 View Code Duplication
		if (preg_match('/(.*)\x00/', $lstring, $lmatches)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2828
			$count = strlen($lmatches[1]) + 1; //account for trailing \x00
2829
			return $lmatches[1];
2830
		}
2831
		return '';
2832
	}
2833
2834
	/**
2835
	 * @param string $nullterminatedstring
2836
	 *
2837
	 * @return string
2838
	 */
2839 View Code Duplication
	public function NoNullString($nullterminatedstring) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2840
		// remove the single null terminator on null terminated strings
2841
		if (substr($nullterminatedstring, strlen($nullterminatedstring) - 1, 1) === "\x00") {
2842
			return substr($nullterminatedstring, 0, strlen($nullterminatedstring) - 1);
2843
		}
2844
		return $nullterminatedstring;
2845
	}
2846
2847
	/**
2848
	 * @param string $pascalstring
2849
	 *
2850
	 * @return string
2851
	 */
2852
	public function Pascal2String($pascalstring) {
2853
		// Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string
2854
		return substr($pascalstring, 1);
2855
	}
2856
2857
	/**
2858
	 * @param string $pascalstring
2859
	 *
2860
	 * @return string
2861
	 */
2862
	public function MaybePascal2String($pascalstring) {
2863
		// Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string
2864
		// Check if string actually is in this format or written incorrectly, straight string, or null-terminated string
2865
		if (ord(substr($pascalstring, 0, 1)) == (strlen($pascalstring) - 1)) {
2866
			return substr($pascalstring, 1);
2867
		} elseif (substr($pascalstring, -1, 1) == "\x00") {
2868
			// appears to be null-terminated instead of Pascal-style
2869
			return substr($pascalstring, 0, -1);
2870
		}
2871
		return $pascalstring;
2872
	}
2873
2874
2875
	/**
2876
	 * Helper functions for m4b audiobook chapters
2877
	 * code by Steffen Hartmann 2015-Nov-08.
2878
	 *
2879
	 * @param array  $info
2880
	 * @param string $tag
2881
	 * @param string $history
2882
	 * @param array  $result
2883
	 */
2884
	public function search_tag_by_key($info, $tag, $history, &$result) {
2885
		foreach ($info as $key => $value) {
2886
			$key_history = $history.'/'.$key;
2887
			if ($key === $tag) {
2888
				$result[] = array($key_history, $info);
2889
			} else {
2890
				if (is_array($value)) {
2891
					$this->search_tag_by_key($value, $tag, $key_history, $result);
2892
				}
2893
			}
2894
		}
2895
	}
2896
2897
	/**
2898
	 * @param array  $info
2899
	 * @param string $k
2900
	 * @param string $v
2901
	 * @param string $history
2902
	 * @param array  $result
2903
	 */
2904
	public function search_tag_by_pair($info, $k, $v, $history, &$result) {
2905
		foreach ($info as $key => $value) {
2906
			$key_history = $history.'/'.$key;
2907
			if (($key === $k) && ($value === $v)) {
2908
				$result[] = array($key_history, $info);
2909
			} else {
2910
				if (is_array($value)) {
2911
					$this->search_tag_by_pair($value, $k, $v, $key_history, $result);
2912
				}
2913
			}
2914
		}
2915
	}
2916
2917
	/**
2918
	 * @param array $info
2919
	 *
2920
	 * @return array
2921
	 */
2922
	public function quicktime_time_to_sample_table($info) {
2923
		$res = array();
2924
		$this->search_tag_by_pair($info['quicktime']['moov'], 'name', 'stbl', 'quicktime/moov', $res);
2925
		foreach ($res as $value) {
2926
			$stbl_res = array();
2927
			$this->search_tag_by_pair($value[1], 'data_format', 'text', $value[0], $stbl_res);
2928
			if (count($stbl_res) > 0) {
2929
				$stts_res = array();
2930
				$this->search_tag_by_key($value[1], 'time_to_sample_table', $value[0], $stts_res);
2931
				if (count($stts_res) > 0) {
2932
					return $stts_res[0][1]['time_to_sample_table'];
2933
				}
2934
			}
2935
		}
2936
		return array();
2937
	}
2938
2939
	/**
2940
	 * @param array $info
2941
	 *
2942
	 * @return int
2943
	 */
2944
	public function quicktime_bookmark_time_scale($info) {
2945
		$time_scale = '';
2946
		$ts_prefix_len = 0;
2947
		$res = array();
2948
		$this->search_tag_by_pair($info['quicktime']['moov'], 'name', 'stbl', 'quicktime/moov', $res);
2949
		foreach ($res as $value) {
2950
			$stbl_res = array();
2951
			$this->search_tag_by_pair($value[1], 'data_format', 'text', $value[0], $stbl_res);
2952
			if (count($stbl_res) > 0) {
2953
				$ts_res = array();
2954
				$this->search_tag_by_key($info['quicktime']['moov'], 'time_scale', 'quicktime/moov', $ts_res);
2955
				foreach ($ts_res as $sub_value) {
2956
					$prefix = substr($sub_value[0], 0, -12);
2957
					if ((substr($stbl_res[0][0], 0, strlen($prefix)) === $prefix) && ($ts_prefix_len < strlen($prefix))) {
2958
						$time_scale = $sub_value[1]['time_scale'];
2959
						$ts_prefix_len = strlen($prefix);
2960
					}
2961
				}
2962
			}
2963
		}
2964
		return $time_scale;
2965
	}
2966
	/*
2967
	// END helper functions for m4b audiobook chapters
2968
	*/
2969
2970
2971
}
2972