PowerPress Podcasting plugin by Blubrry - Version 7.0

Version Description

  • Released on 6/6/2016
  • Added Google Play Music to subscribe page shortcode embed and sidebar widget.
  • Added Stitcher to subscribe page shortcode embed and sidebar widget.
  • New: Strict Category Podcasting. Select a specific category to each episode for statistics tracking and subscription links.
  • New: Podcast only category feeds. You can now mix blog posts with podcast episodes in your podast categories.
  • New: Blubrry Audio Player. Modern podcast audio player complete with subscribe and share tools. For Blubrry Hosting customers only.
  • Updated the getid3 library to latest version.
  • Embed link enhanced to use provided embed. If an embed is placed into an episode, the embed link below the player will use that embed as well.
  • Fixed bug where post type podcast feed with slug 'podcast' will also get the default podcast feed settings (Thanks steveportigal for bringing to our attention).
  • Fixed bug with WP 4.5 dropping the rss-http content type "text/xml".
  • Added shortcode attribute ids="" so you can specify post IDs for the playlist player to display exclusively. (Thanks John Haspel for the feature suggestion!)
  • RSS import now sets the blog post content from the iTunes summary if no description or content:encoded was found in the feed. (Thanks @synthtony for letting us know about the situation)
  • RSS import function calls to mb_stripos() will fallback to stripos() if the PHP mb (Multibyte String) library not available. (Thanks Glen Donohu for bringing to our attention!)
  • Import to podcast category option added.
  • Added audio ac3 and dts to media detection getid3 library (these are not podcast safe formats though we can detect their meta information).
  • Improved logic for setting the iTunes subtitle and iTunes summary from blog post excerpts and blog post content if no custom iTunes summary and/or subtitle is set.
  • Improved feed logic to minimize SQL queries
  • Feed Maximizer option now applies to category podcast only feeds.
  • Renamed "Appearance" settings to "Website" settings
  • Added "Destination" settings for podcast directories and apps
  • You can now select from pages when setting your Subscribe Page.
  • Changed labels for Podcasting SEO guidance to include word "Podcasting" so not to confuse with "Web SEO".
  • Updated Podcasting SEO guidance to reflect how search works in Google Play Music (Thanks Daniel Lewis for confirming what is indexed!)
  • Changed wording for Podcasting SEO guidance with Author field, replaced "slogans" with "nicknames" so it is more clear that we mean slogans for talent on the show.
  • Removed logic to fallback to the HTML5 player for mobile devices. This was only needed when Flash based players were used.
  • Quicktime Embed option for m4a audio removed due to security concerns with Quicktime.
  • SWF embed support dropped.
  • Embed link now toggles embed box when clicked multiple times.
  • ATTENTION: The 1 pixel out audio player will be removed from PowerPress 7.1.
Download this release

Release Info

Developer amandato
Plugin Icon 128x128 PowerPress Podcasting plugin by Blubrry
Version 7.0
Comparing to
See all releases

Code changes from version 6.3.3 to 7.0

class.powerpress-subscribe-widget.php CHANGED
@@ -93,6 +93,12 @@ body .pp-ssb-widget a.pp-ssb-btn:hover {
93
  .pp-ssb-widget-modern a.pp-ssb-email {
94
  background-color: #337EC9;
95
  }
 
 
 
 
 
 
96
  .pp-ssb-widget-modern a.pp-ssb-android {
97
  background-color: #6AB344;
98
  }
@@ -122,6 +128,13 @@ body .pp-ssb-widget a.pp-ssb-btn:hover {
122
  .pp-ssb-android .pp-ssb-ic {
123
  background-position: -98px -98px;
124
  }
 
 
 
 
 
 
 
125
  .pp-ssb-more .pp-ssb-ic {
126
  background-position: -49px -49px;
127
  }
@@ -231,8 +244,10 @@ body .pp-ssb-widget a.pp-ssb-btn:hover {
231
  $ExtraData = array('subscribe_type'=>'general', 'feed'=>'', 'taxonomy_term_id'=>'', 'cat_id'=>'', 'post_type'=>'');
232
  if( !empty($instance['subscribe_type']) )
233
  $ExtraData['subscribe_type'] = $instance['subscribe_type'];
 
 
234
 
235
- switch( $instance['subscribe_type'] )
236
  {
237
  case 'post_type': {
238
 
@@ -269,14 +284,18 @@ body .pp-ssb-widget a.pp-ssb-btn:hover {
269
  }
270
  }; break;
271
  default: {
272
- // Doesn't matter, we'r using the default podcast channel
273
 
274
  };
275
  }
276
 
277
- $Settings = powerpresssubscribe_get_settings( $ExtraData );
278
  if( empty($Settings) )
279
  return;
 
 
 
 
280
 
281
  echo $args['before_widget'];
282
  if ( ! empty( $instance['title'] ) ) {
93
  .pp-ssb-widget-modern a.pp-ssb-email {
94
  background-color: #337EC9;
95
  }
96
+ .pp-ssb-widget-modern a.pp-ssb-stitcher {
97
+ background-color: #197195;
98
+ }
99
+ .pp-ssb-widget-modern a.pp-ssb-gp {
100
+ background-color: #F15832;
101
+ }
102
  .pp-ssb-widget-modern a.pp-ssb-android {
103
  background-color: #6AB344;
104
  }
128
  .pp-ssb-android .pp-ssb-ic {
129
  background-position: -98px -98px;
130
  }
131
+ .pp-ssb-stitcher .pp-ssb-ic {
132
+ background-position: -147px -98px;
133
+ }
134
+ .pp-ssb-gp .pp-ssb-ic {
135
+ background-position: -196px -98px;
136
+ }
137
+
138
  .pp-ssb-more .pp-ssb-ic {
139
  background-position: -49px -49px;
140
  }
244
  $ExtraData = array('subscribe_type'=>'general', 'feed'=>'', 'taxonomy_term_id'=>'', 'cat_id'=>'', 'post_type'=>'');
245
  if( !empty($instance['subscribe_type']) )
246
  $ExtraData['subscribe_type'] = $instance['subscribe_type'];
247
+ else
248
+ $ExtraData['subscribe_type'] = '';
249
 
250
+ switch( $ExtraData['subscribe_type'] )
251
  {
252
  case 'post_type': {
253
 
284
  }
285
  }; break;
286
  default: {
287
+ // Doesn't matter, we're using the default podcast channel
288
 
289
  };
290
  }
291
 
292
+ $Settings = powerpresssubscribe_get_settings( $ExtraData, false );
293
  if( empty($Settings) )
294
  return;
295
+
296
+ if( empty($instance['title']) )
297
+ $instance['title'] = __( 'Subscribe to Podcast' , 'powerpress');
298
+ $instance['title'] = trim($instance['title']);
299
 
300
  echo $args['before_widget'];
301
  if ( ! empty( $instance['title'] ) ) {
css/admin.css CHANGED
@@ -21,7 +21,7 @@ div.powerpress-error,
21
  /* background-color: #ffebe8; /* red */
22
  }
23
  #powerpress_settings {
24
- background-image:url(//images.blubrry.com/powerpress/blubrry_logo5.png);
25
  background-repeat: no-repeat;
26
  background-position: bottom right;
27
  }
@@ -36,12 +36,22 @@ div.powerpress-error,
36
  #powerpress_settings label {
37
  /* font-size: 110%; */
38
  }
 
 
 
 
 
 
39
  .bpp_input_sm {
40
  width: 120px;
41
  }
42
  .bpp_input_med {
43
  width: 250px;
44
  }
 
 
 
 
45
  .powerpress_color_box {
46
  float: left;
47
  width: 16px;
@@ -379,3 +389,64 @@ padding-bottom: 15px;
379
  border-color: #DFDFDF;
380
  border-color: #B3B3B3;
381
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  /* background-color: #ffebe8; /* red */
22
  }
23
  #powerpress_settings {
24
+ background-image:url(//images.blubrry.com/powerpress/blubrry_logo7.png);
25
  background-repeat: no-repeat;
26
  background-position: bottom right;
27
  }
36
  #powerpress_settings label {
37
  /* font-size: 110%; */
38
  }
39
+ #powerpress_settings td {
40
+ padding-top: 12px;
41
+ }
42
+ #powerpress_settings td p {
43
+ padding-bottom: 10px;
44
+ }
45
  .bpp_input_sm {
46
  width: 120px;
47
  }
48
  .bpp_input_med {
49
  width: 250px;
50
  }
51
+ .bpp-input-normal {
52
+ width: 80%;
53
+ }
54
+
55
  .powerpress_color_box {
56
  float: left;
57
  width: 16px;
389
  border-color: #DFDFDF;
390
  border-color: #B3B3B3;
391
  }
392
+
393
+
394
+ /* sections */
395
+ .pp-section {
396
+ clear: both;
397
+ padding: 0px;
398
+ margin: 0px;
399
+ }
400
+ .pp-section dt {
401
+ font-weight: bold;
402
+ }
403
+
404
+ .pp-expand-section {
405
+ text-decoration: none;
406
+ }
407
+ a.pp-expand-section,
408
+ a.pp-expand-section:hover,
409
+ a.pp-expand-section:focus,
410
+ a.pp-expand-section:visited {
411
+ color: #1976D2;
412
+ }
413
+ .pp-expand-section:before,
414
+ .pp-expand-section-expanded:before {
415
+ background-color: #1976D2;
416
+ border: 1px solid #1976D2;
417
+ border-radius: 3px;
418
+ font-weight: bold;
419
+ color: #ffffff;
420
+ height: 20px;
421
+ width: 20px;
422
+ display: inline-block;
423
+ margin-right: 10px;
424
+ text-align: center;
425
+ font-size: 20px;
426
+ }
427
+ .pp-expand-section:before {
428
+ content: ' \25C4'; /* ◄ */
429
+ content: '+';
430
+ }
431
+ .pp-expand-section-expanded:before {
432
+ content: ' \25BC';
433
+ content: '-';
434
+ }
435
+
436
+ /* buttons */
437
+ .wp-core-ui .button-blubrry {
438
+ background: #1976D2;
439
+ border-color: #1976D2;
440
+ color: #FFFFFF;
441
+ }
442
+ .wp-core-ui .button-blubrry-gray {
443
+ background: #f7f7f7;
444
+ border-color: #ccc;
445
+ color: #555;
446
+ }
447
+ .wp-core-ui .button-blubrry:hover,
448
+ .wp-core-ui .button-blubrry:focus {
449
+ background: #0D87E9;
450
+ border-color: #0D87E9;
451
+ color: #FFFFFF;
452
+ }
css/steps.css CHANGED
@@ -88,12 +88,15 @@
88
  }
89
  #powerpress-steps h3.pp-step-h-completed:before
90
  {
91
- content: '\2713';
 
 
 
 
92
  font-weight: 400;
93
  font-size: 32px;
94
  position: absolute;
95
- left: -25px;
96
- top: -5px;
97
  }
98
 
99
  @media screen and (max-width: 400px) {
88
  }
89
  #powerpress-steps h3.pp-step-h-completed:before
90
  {
91
+ content: url(../images/check.svg); /*'\2713';*/
92
+ color: #54A954;
93
+ width: 24px;
94
+ height: 24px;
95
+ padding-right: 5px;
96
  font-weight: 400;
97
  font-size: 32px;
98
  position: absolute;
99
+ left: -30px;
 
100
  }
101
 
102
  @media screen and (max-width: 400px) {
css/subscribe.css CHANGED
@@ -131,6 +131,15 @@ body .pp-sub-widget a:hover {
131
  .pp-sub-widget-modern a.pp-sub-rss {
132
  background-color: #FF8800;
133
  }
 
 
 
 
 
 
 
 
 
134
  .pp-sub-widget-modern a.pp-sub-android {
135
  background-color: #6AB344;
136
  }
@@ -171,6 +180,12 @@ body .pp-sub-widget a:hover {
171
  .pp-sub-android .pp-sub-ic {
172
  background-position: -98px -98px;
173
  }
 
 
 
 
 
 
174
 
175
  /* Retina-specific stuff here */
176
  @media only screen and (-webkit-min-device-pixel-ratio: 2.0),
131
  .pp-sub-widget-modern a.pp-sub-rss {
132
  background-color: #FF8800;
133
  }
134
+
135
+ .pp-sub-widget-modern a.pp-sub-stitcher {
136
+ background-color: #222222;
137
+ background-color: #3ec6ff; /* stitcher light blue */
138
+ background-color: #197195; /* stitcher dark blue */
139
+ }
140
+ .pp-sub-widget-modern a.pp-sub-gp {
141
+ background-color: #F15832;
142
+ }
143
  .pp-sub-widget-modern a.pp-sub-android {
144
  background-color: #6AB344;
145
  }
180
  .pp-sub-android .pp-sub-ic {
181
  background-position: -98px -98px;
182
  }
183
+ .pp-sub-stitcher .pp-sub-ic {
184
+ background-position: -147px -98px;
185
+ }
186
+ .pp-sub-gp .pp-sub-ic {
187
+ background-position: -196px -98px;
188
+ }
189
 
190
  /* Retina-specific stuff here */
191
  @media only screen and (-webkit-min-device-pixel-ratio: 2.0),
feed-podcast.php CHANGED
@@ -22,25 +22,29 @@
22
  return apply_filters('the_excerpt_rss', $output);
23
  }
24
 
25
- $iTunesOrderNumber = 0;
26
- $FeaturedPodcastID = 0;
27
- $iTunesFeatured = get_option('powerpress_itunes_featured');
28
- $feed_slug = get_query_var('feed');
29
- if( !empty($iTunesFeatured[ $feed_slug ]) )
30
- {
31
- if( get_post_type() == 'post' )
 
32
  {
33
- $FeaturedPodcastID = $iTunesFeatured[ $feed_slug ];
34
- $GLOBALS['powerpress_feed']['itunes_feature'] = true; // So any custom order value is not used when looping through the feeds.
35
- $iTunesOrderNumber = 2; // One reserved for featured episode
 
 
 
36
  }
37
  }
38
 
39
 
40
- header('Content-Type: ' . feed_content_type('rss-http') . '; charset=' . get_option('blog_charset'), true);
41
  $more = 1;
42
 
43
- $GeneralSettings = get_option('powerpress_general');
44
  $FeedActionHook = '';
45
  if( !empty($GeneralSettings['feed_action_hook']) )
46
  $FeedActionHook = '_powerpress';
@@ -69,7 +73,13 @@ echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>'."\n";
69
 
70
  $ItemCount = 0;
71
  ?>
72
- <?php while( have_posts()) : the_post(); ?>
 
 
 
 
 
 
73
  <item>
74
  <title><?php the_title_rss() ?></title>
75
  <link><?php the_permalink_rss() ?></link>
@@ -78,22 +88,27 @@ echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>'."\n";
78
  <?php
79
  if( empty($GLOBALS['powerpress_feed']['feed_maximizer_on']) ) // If feed maximizer off
80
  {
 
 
81
  ?>
82
  <comments><?php comments_link_feed(); ?></comments>
83
- <dc:creator><?php the_author() ?></dc:creator>
84
- <?php the_category_rss('rss2') ?>
85
- <?php if (get_option('rss_use_excerpt')) : ?>
 
 
 
 
 
86
  <description><?php echo powerpress_format_itunes_value( powerpress_get_the_excerpt_rss(), 'description' ); ?></description>
87
- <?php else : ?>
88
  <description><?php echo powerpress_format_itunes_value( powerpress_get_the_excerpt_rss(), 'description' ); ?></description>
89
- <?php if ( strlen( $post->post_content ) > 0 ) : ?>
90
  <content:encoded><![CDATA[<?php the_content_feed('rss2') ?>]]></content:encoded>
91
- <?php else : ?>
92
  <content:encoded><![CDATA[<?php the_excerpt_rss() ?>]]></content:encoded>
93
- <?php endif; ?>
94
- <?php endif; ?>
95
- <wfw:commentRss><?php echo esc_url( get_post_comments_feed_link(null, 'rss2') ); ?></wfw:commentRss>
96
- <slash:comments><?php echo get_comments_number(); ?></slash:comments>
97
  <?php
98
  }
99
  else // If feed maximizer on
@@ -140,7 +155,10 @@ echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>'."\n";
140
  query_posts( array('p'=>$FeaturedPodcastID) );
141
  if( have_posts())
142
  {
143
- the_post();
 
 
 
144
  // Featured podcast epiosde, give it the highest itunes:order value...
145
  ?>
146
  <item>
22
  return apply_filters('the_excerpt_rss', $output);
23
  }
24
 
25
+ $GeneralSettings = get_option('powerpress_general');
26
+ $iTunesOrderNumber = 0;
27
+ $FeaturedPodcastID = 0;
28
+
29
+ if( !empty($GeneralSettings['episode_box_feature_in_itunes']) ) {
30
+ $iTunesFeatured = get_option('powerpress_itunes_featured');
31
+ $feed_slug = get_query_var('feed');
32
+ if( !empty($iTunesFeatured[ $feed_slug ]) )
33
  {
34
+ if( get_post_type() == 'post' )
35
+ {
36
+ $FeaturedPodcastID = $iTunesFeatured[ $feed_slug ];
37
+ $GLOBALS['powerpress_feed']['itunes_feature'] = true; // So any custom order value is not used when looping through the feeds.
38
+ $iTunesOrderNumber = 2; // One reserved for featured episode
39
+ }
40
  }
41
  }
42
 
43
 
44
+ header('Content-Type: application/rss+xml; charset=' . get_option('blog_charset'), true);
45
  $more = 1;
46
 
47
+
48
  $FeedActionHook = '';
49
  if( !empty($GeneralSettings['feed_action_hook']) )
50
  $FeedActionHook = '_powerpress';
73
 
74
  $ItemCount = 0;
75
  ?>
76
+ <?php while( have_posts()) :
77
+
78
+ if( empty($GeneralSettings['feed_accel']) )
79
+ the_post();
80
+ else
81
+ $GLOBALS['post'] = $GLOBALS['wp_query']->next_post(); // Use this rather than the_post() that way we do not add additional queries to the database
82
+ ?>
83
  <item>
84
  <title><?php the_title_rss() ?></title>
85
  <link><?php the_permalink_rss() ?></link>
88
  <?php
89
  if( empty($GLOBALS['powerpress_feed']['feed_maximizer_on']) ) // If feed maximizer off
90
  {
91
+
92
+ if( empty($GeneralSettings['feed_accel']) ) {
93
  ?>
94
  <comments><?php comments_link_feed(); ?></comments>
95
+ <wfw:commentRss><?php echo esc_url( get_post_comments_feed_link(null, 'rss2') ); ?></wfw:commentRss>
96
+ <slash:comments><?php echo get_comments_number(); ?></slash:comments>
97
+ <?php } // end powerpress feed comments
98
+
99
+ if( empty($GeneralSettings['feed_accel']) ) {
100
+ the_category_rss('rss2');
101
+ }
102
+ if (get_option('rss_use_excerpt')) { ?>
103
  <description><?php echo powerpress_format_itunes_value( powerpress_get_the_excerpt_rss(), 'description' ); ?></description>
104
+ <?php } else { // else no rss_use_excerpt ?>
105
  <description><?php echo powerpress_format_itunes_value( powerpress_get_the_excerpt_rss(), 'description' ); ?></description>
106
+ <?php if ( strlen( $post->post_content ) > 0 ) { ?>
107
  <content:encoded><![CDATA[<?php the_content_feed('rss2') ?>]]></content:encoded>
108
+ <?php } else { // else strlen( $post->post_content ) <= 0 ?>
109
  <content:encoded><![CDATA[<?php the_excerpt_rss() ?>]]></content:encoded>
110
+ <?php } // end else strlen( $post->post_content ) <= 0 ?>
111
+ <?php } // end else no rss_use_excerpt ?>
 
 
112
  <?php
113
  }
114
  else // If feed maximizer on
155
  query_posts( array('p'=>$FeaturedPodcastID) );
156
  if( have_posts())
157
  {
158
+ if( empty($GeneralSettings['feed_accel']) )
159
+ the_post();
160
+ else
161
+ $GLOBALS['post'] = $GLOBALS['wp_query']->next_post(); // Use this rather than the_post() that way we do not add additional queries to the database
162
  // Featured podcast epiosde, give it the highest itunes:order value...
163
  ?>
164
  <item>
getid3/getid3.lib.php CHANGED
@@ -1,1376 +1,1405 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- // also https://github.com/JamesHeinrich/getID3 //
7
- /////////////////////////////////////////////////////////////////
8
- // //
9
- // getid3.lib.php - part of getID3() //
10
- // See readme.txt for more details //
11
- // ///
12
- /////////////////////////////////////////////////////////////////
13
-
14
-
15
- class getid3_lib
16
- {
17
-
18
- public static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8') {
19
- $returnstring = '';
20
- for ($i = 0; $i < strlen($string); $i++) {
21
- if ($hex) {
22
- $returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
23
- } else {
24
- $returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string{$i}) ? $string{$i} : '¤');
25
- }
26
- if ($spaces) {
27
- $returnstring .= ' ';
28
- }
29
- }
30
- if (!empty($htmlencoding)) {
31
- if ($htmlencoding === true) {
32
- $htmlencoding = 'UTF-8'; // prior to getID3 v1.9.0 the function's 4th parameter was boolean
33
- }
34
- $returnstring = htmlentities($returnstring, ENT_QUOTES, $htmlencoding);
35
- }
36
- return $returnstring;
37
- }
38
-
39
- public static function trunc($floatnumber) {
40
- // truncates a floating-point number at the decimal point
41
- // returns int (if possible, otherwise float)
42
- if ($floatnumber >= 1) {
43
- $truncatednumber = floor($floatnumber);
44
- } elseif ($floatnumber <= -1) {
45
- $truncatednumber = ceil($floatnumber);
46
- } else {
47
- $truncatednumber = 0;
48
- }
49
- if (self::intValueSupported($truncatednumber)) {
50
- $truncatednumber = (int) $truncatednumber;
51
- }
52
- return $truncatednumber;
53
- }
54
-
55
-
56
- public static function safe_inc(&$variable, $increment=1) {
57
- if (isset($variable)) {
58
- $variable += $increment;
59
- } else {
60
- $variable = $increment;
61
- }
62
- return true;
63
- }
64
-
65
- public static function CastAsInt($floatnum) {
66
- // convert to float if not already
67
- $floatnum = (float) $floatnum;
68
-
69
- // convert a float to type int, only if possible
70
- if (self::trunc($floatnum) == $floatnum) {
71
- // it's not floating point
72
- if (self::intValueSupported($floatnum)) {
73
- // it's within int range
74
- $floatnum = (int) $floatnum;
75
- }
76
- }
77
- return $floatnum;
78
- }
79
-
80
- public static function intValueSupported($num) {
81
- // check if integers are 64-bit
82
- static $hasINT64 = null;
83
- if ($hasINT64 === null) { // 10x faster than is_null()
84
- $hasINT64 = is_int(pow(2, 31)); // 32-bit int are limited to (2^31)-1
85
- if (!$hasINT64 && !defined('PHP_INT_MIN')) {
86
- define('PHP_INT_MIN', ~PHP_INT_MAX);
87
- }
88
- }
89
- // if integers are 64-bit - no other check required
90
- if ($hasINT64 || (($num <= PHP_INT_MAX) && ($num >= PHP_INT_MIN))) {
91
- return true;
92
- }
93
- return false;
94
- }
95
-
96
- public static function DecimalizeFraction($fraction) {
97
- list($numerator, $denominator) = explode('/', $fraction);
98
- return $numerator / ($denominator ? $denominator : 1);
99
- }
100
-
101
-
102
- public static function DecimalBinary2Float($binarynumerator) {
103
- $numerator = self::Bin2Dec($binarynumerator);
104
- $denominator = self::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator)));
105
- return ($numerator / $denominator);
106
- }
107
-
108
-
109
- public static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) {
110
- // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
111
- if (strpos($binarypointnumber, '.') === false) {
112
- $binarypointnumber = '0.'.$binarypointnumber;
113
- } elseif ($binarypointnumber{0} == '.') {
114
- $binarypointnumber = '0'.$binarypointnumber;
115
- }
116
- $exponent = 0;
117
- while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
118
- if (substr($binarypointnumber, 1, 1) == '.') {
119
- $exponent--;
120
- $binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3);
121
- } else {
122
- $pointpos = strpos($binarypointnumber, '.');
123
- $exponent += ($pointpos - 1);
124
- $binarypointnumber = str_replace('.', '', $binarypointnumber);
125
- $binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1);
126
- }
127
- }
128
- $binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT);
129
- return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent);
130
- }
131
-
132
-
133
- public static function Float2BinaryDecimal($floatvalue) {
134
- // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
135
- $maxbits = 128; // to how many bits of precision should the calculations be taken?
136
- $intpart = self::trunc($floatvalue);
137
- $floatpart = abs($floatvalue - $intpart);
138
- $pointbitstring = '';
139
- while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) {
140
- $floatpart *= 2;
141
- $pointbitstring .= (string) self::trunc($floatpart);
142
- $floatpart -= self::trunc($floatpart);
143
- }
144
- $binarypointnumber = decbin($intpart).'.'.$pointbitstring;
145
- return $binarypointnumber;
146
- }
147
-
148
-
149
- public static function Float2String($floatvalue, $bits) {
150
- // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
151
- switch ($bits) {
152
- case 32:
153
- $exponentbits = 8;
154
- $fractionbits = 23;
155
- break;
156
-
157
- case 64:
158
- $exponentbits = 11;
159
- $fractionbits = 52;
160
- break;
161
-
162
- default:
163
- return false;
164
- break;
165
- }
166
- if ($floatvalue >= 0) {
167
- $signbit = '0';
168
- } else {
169
- $signbit = '1';
170
- }
171
- $normalizedbinary = self::NormalizeBinaryPoint(self::Float2BinaryDecimal($floatvalue), $fractionbits);
172
- $biasedexponent = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent
173
- $exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT);
174
- $fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT);
175
-
176
- return self::BigEndian2String(self::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false);
177
- }
178
-
179
-
180
- public static function LittleEndian2Float($byteword) {
181
- return self::BigEndian2Float(strrev($byteword));
182
- }
183
-
184
-
185
- public static function BigEndian2Float($byteword) {
186
- // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
187
- // http://www.psc.edu/general/software/packages/ieee/ieee.html
188
- // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
189
-
190
- $bitword = self::BigEndian2Bin($byteword);
191
- if (!$bitword) {
192
- return 0;
193
- }
194
- $signbit = $bitword{0};
195
-
196
- switch (strlen($byteword) * 8) {
197
- case 32:
198
- $exponentbits = 8;
199
- $fractionbits = 23;
200
- break;
201
-
202
- case 64:
203
- $exponentbits = 11;
204
- $fractionbits = 52;
205
- break;
206
-
207
- case 80:
208
- // 80-bit Apple SANE format
209
- // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
210
- $exponentstring = substr($bitword, 1, 15);
211
- $isnormalized = intval($bitword{16});
212
- $fractionstring = substr($bitword, 17, 63);
213
- $exponent = pow(2, self::Bin2Dec($exponentstring) - 16383);
214
- $fraction = $isnormalized + self::DecimalBinary2Float($fractionstring);
215
- $floatvalue = $exponent * $fraction;
216
- if ($signbit == '1') {
217
- $floatvalue *= -1;
218
- }
219
- return $floatvalue;
220
- break;
221
-
222
- default:
223
- return false;
224
- break;
225
- }
226
- $exponentstring = substr($bitword, 1, $exponentbits);
227
- $fractionstring = substr($bitword, $exponentbits + 1, $fractionbits);
228
- $exponent = self::Bin2Dec($exponentstring);
229
- $fraction = self::Bin2Dec($fractionstring);
230
-
231
- if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) {
232
- // Not a Number
233
- $floatvalue = false;
234
- } elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) {
235
- if ($signbit == '1') {
236
- $floatvalue = '-infinity';
237
- } else {
238
- $floatvalue = '+infinity';
239
- }
240
- } elseif (($exponent == 0) && ($fraction == 0)) {
241
- if ($signbit == '1') {
242
- $floatvalue = -0;
243
- } else {
244
- $floatvalue = 0;
245
- }
246
- $floatvalue = ($signbit ? 0 : -0);
247
- } elseif (($exponent == 0) && ($fraction != 0)) {
248
- // These are 'unnormalized' values
249
- $floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * self::DecimalBinary2Float($fractionstring);
250
- if ($signbit == '1') {
251
- $floatvalue *= -1;
252
- }
253
- } elseif ($exponent != 0) {
254
- $floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + self::DecimalBinary2Float($fractionstring));
255
- if ($signbit == '1') {
256
- $floatvalue *= -1;
257
- }
258
- }
259
- return (float) $floatvalue;
260
- }
261
-
262
-
263
- public static function BigEndian2Int($byteword, $synchsafe=false, $signed=false) {
264
- $intvalue = 0;
265
- $bytewordlen = strlen($byteword);
266
- if ($bytewordlen == 0) {
267
- return false;
268
- }
269
- for ($i = 0; $i < $bytewordlen; $i++) {
270
- if ($synchsafe) { // disregard MSB, effectively 7-bit bytes
271
- //$intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7); // faster, but runs into problems past 2^31 on 32-bit systems
272
- $intvalue += (ord($byteword{$i}) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7);
273
- } else {
274
- $intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
275
- }
276
- }
277
- if ($signed && !$synchsafe) {
278
- // synchsafe ints are not allowed to be signed
279
- if ($bytewordlen <= PHP_INT_SIZE) {
280
- $signMaskBit = 0x80 << (8 * ($bytewordlen - 1));
281
- if ($intvalue & $signMaskBit) {
282
- $intvalue = 0 - ($intvalue & ($signMaskBit - 1));
283
- }
284
- } else {
285
- throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits ('.strlen($byteword).') in self::BigEndian2Int()');
286
- }
287
- }
288
- return self::CastAsInt($intvalue);
289
- }
290
-
291
-
292
- public static function LittleEndian2Int($byteword, $signed=false) {
293
- return self::BigEndian2Int(strrev($byteword), false, $signed);
294
- }
295
-
296
-
297
- public static function BigEndian2Bin($byteword) {
298
- $binvalue = '';
299
- $bytewordlen = strlen($byteword);
300
- for ($i = 0; $i < $bytewordlen; $i++) {
301
- $binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT);
302
- }
303
- return $binvalue;
304
- }
305
-
306
-
307
- public static function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) {
308
- if ($number < 0) {
309
- throw new Exception('ERROR: self::BigEndian2String() does not support negative numbers');
310
- }
311
- $maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF);
312
- $intstring = '';
313
- if ($signed) {
314
- if ($minbytes > PHP_INT_SIZE) {
315
- throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits in self::BigEndian2String()');
316
- }
317
- $number = $number & (0x80 << (8 * ($minbytes - 1)));
318
- }
319
- while ($number != 0) {
320
- $quotient = ($number / ($maskbyte + 1));
321
- $intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring;
322
- $number = floor($quotient);
323
- }
324
- return str_pad($intstring, $minbytes, "\x00", STR_PAD_LEFT);
325
- }
326
-
327
-
328
- public static function Dec2Bin($number) {
329
- while ($number >= 256) {
330
- $bytes[] = (($number / 256) - (floor($number / 256))) * 256;
331
- $number = floor($number / 256);
332
- }
333
- $bytes[] = $number;
334
- $binstring = '';
335
- for ($i = 0; $i < count($bytes); $i++) {
336
- $binstring = (($i == count($bytes) - 1) ? decbin($bytes[$i]) : str_pad(decbin($bytes[$i]), 8, '0', STR_PAD_LEFT)).$binstring;
337
- }
338
- return $binstring;
339
- }
340
-
341
-
342
- public static function Bin2Dec($binstring, $signed=false) {
343
- $signmult = 1;
344
- if ($signed) {
345
- if ($binstring{0} == '1') {
346
- $signmult = -1;
347
- }
348
- $binstring = substr($binstring, 1);
349
- }
350
- $decvalue = 0;
351
- for ($i = 0; $i < strlen($binstring); $i++) {
352
- $decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i);
353
- }
354
- return self::CastAsInt($decvalue * $signmult);
355
- }
356
-
357
-
358
- public static function Bin2String($binstring) {
359
- // return 'hi' for input of '0110100001101001'
360
- $string = '';
361
- $binstringreversed = strrev($binstring);
362
- for ($i = 0; $i < strlen($binstringreversed); $i += 8) {
363
- $string = chr(self::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string;
364
- }
365
- return $string;
366
- }
367
-
368
-
369
- public static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) {
370
- $intstring = '';
371
- while ($number > 0) {
372
- if ($synchsafe) {
373
- $intstring = $intstring.chr($number & 127);
374
- $number >>= 7;
375
- } else {
376
- $intstring = $intstring.chr($number & 255);
377
- $number >>= 8;
378
- }
379
- }
380
- return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
381
- }
382
-
383
-
384
- public static function array_merge_clobber($array1, $array2) {
385
- // written by kcØhireability*com
386
- // taken from http://www.php.net/manual/en/function.array-merge-recursive.php
387
- if (!is_array($array1) || !is_array($array2)) {
388
- return false;
389
- }
390
- $newarray = $array1;
391
- foreach ($array2 as $key => $val) {
392
- if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
393
- $newarray[$key] = self::array_merge_clobber($newarray[$key], $val);
394
- } else {
395
- $newarray[$key] = $val;
396
- }
397
- }
398
- return $newarray;
399
- }
400
-
401
-
402
- public static function array_merge_noclobber($array1, $array2) {
403
- if (!is_array($array1) || !is_array($array2)) {
404
- return false;
405
- }
406
- $newarray = $array1;
407
- foreach ($array2 as $key => $val) {
408
- if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
409
- $newarray[$key] = self::array_merge_noclobber($newarray[$key], $val);
410
- } elseif (!isset($newarray[$key])) {
411
- $newarray[$key] = $val;
412
- }
413
- }
414
- return $newarray;
415
- }
416
-
417
-
418
- public static function ksort_recursive(&$theArray) {
419
- ksort($theArray);
420
- foreach ($theArray as $key => $value) {
421
- if (is_array($value)) {
422
- self::ksort_recursive($theArray[$key]);
423
- }
424
- }
425
- return true;
426
- }
427
-
428
- public static function fileextension($filename, $numextensions=1) {
429
- if (strstr($filename, '.')) {
430
- $reversedfilename = strrev($filename);
431
- $offset = 0;
432
- for ($i = 0; $i < $numextensions; $i++) {
433
- $offset = strpos($reversedfilename, '.', $offset + 1);
434
- if ($offset === false) {
435
- return '';
436
- }
437
- }
438
- return strrev(substr($reversedfilename, 0, $offset));
439
- }
440
- return '';
441
- }
442
-
443
-
444
- public static function PlaytimeString($seconds) {
445
- $sign = (($seconds < 0) ? '-' : '');
446
- $seconds = round(abs($seconds));
447
- $H = (int) floor( $seconds / 3600);
448
- $M = (int) floor(($seconds - (3600 * $H) ) / 60);
449
- $S = (int) round( $seconds - (3600 * $H) - (60 * $M) );
450
- return $sign.($H ? $H.':' : '').($H ? str_pad($M, 2, '0', STR_PAD_LEFT) : intval($M)).':'.str_pad($S, 2, 0, STR_PAD_LEFT);
451
- }
452
-
453
-
454
- public static function DateMac2Unix($macdate) {
455
- // Macintosh timestamp: seconds since 00:00h January 1, 1904
456
- // UNIX timestamp: seconds since 00:00h January 1, 1970
457
- return self::CastAsInt($macdate - 2082844800);
458
- }
459
-
460
-
461
- public static function FixedPoint8_8($rawdata) {
462
- return self::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (self::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8));
463
- }
464
-
465
-
466
- public static function FixedPoint16_16($rawdata) {
467
- return self::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (self::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16));
468
- }
469
-
470
-
471
- public static function FixedPoint2_30($rawdata) {
472
- $binarystring = self::BigEndian2Bin($rawdata);
473
- return self::Bin2Dec(substr($binarystring, 0, 2)) + (float) (self::Bin2Dec(substr($binarystring, 2, 30)) / pow(2, 30));
474
- }
475
-
476
-
477
- public static function CreateDeepArray($ArrayPath, $Separator, $Value) {
478
- // assigns $Value to a nested array path:
479
- // $foo = self::CreateDeepArray('/path/to/my', '/', 'file.txt')
480
- // is the same as:
481
- // $foo = array('path'=>array('to'=>'array('my'=>array('file.txt'))));
482
- // or
483
- // $foo['path']['to']['my'] = 'file.txt';
484
- $ArrayPath = ltrim($ArrayPath, $Separator);
485
- if (($pos = strpos($ArrayPath, $Separator)) !== false) {
486
- $ReturnedArray[substr($ArrayPath, 0, $pos)] = self::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value);
487
- } else {
488
- $ReturnedArray[$ArrayPath] = $Value;
489
- }
490
- return $ReturnedArray;
491
- }
492
-
493
- public static function array_max($arraydata, $returnkey=false) {
494
- $maxvalue = false;
495
- $maxkey = false;
496
- foreach ($arraydata as $key => $value) {
497
- if (!is_array($value)) {
498
- if ($value > $maxvalue) {
499
- $maxvalue = $value;
500
- $maxkey = $key;
501
- }
502
- }
503
- }
504
- return ($returnkey ? $maxkey : $maxvalue);
505
- }
506
-
507
- public static function array_min($arraydata, $returnkey=false) {
508
- $minvalue = false;
509
- $minkey = false;
510
- foreach ($arraydata as $key => $value) {
511
- if (!is_array($value)) {
512
- if ($value > $minvalue) {
513
- $minvalue = $value;
514
- $minkey = $key;
515
- }
516
- }
517
- }
518
- return ($returnkey ? $minkey : $minvalue);
519
- }
520
-
521
- public static function XML2array($XMLstring) {
522
- if ( function_exists( 'simplexml_load_string' ) && function_exists( 'libxml_disable_entity_loader' ) ) {
523
- $loader = libxml_disable_entity_loader( true );
524
- $XMLobject = simplexml_load_string( $XMLstring, 'SimpleXMLElement', LIBXML_NOENT );
525
- $return = self::SimpleXMLelement2array( $XMLobject );
526
- libxml_disable_entity_loader( $loader );
527
- return $return;
528
- }
529
- return false;
530
- }
531
-
532
- public static function SimpleXMLelement2array($XMLobject) {
533
- if (!is_object($XMLobject) && !is_array($XMLobject)) {
534
- return $XMLobject;
535
- }
536
- $XMLarray = (is_object($XMLobject) ? get_object_vars($XMLobject) : $XMLobject);
537
- foreach ($XMLarray as $key => $value) {
538
- $XMLarray[$key] = self::SimpleXMLelement2array($value);
539
- }
540
- return $XMLarray;
541
- }
542
-
543
-
544
- // Allan Hansen <ahØartemis*dk>
545
- // self::md5_data() - returns md5sum for a file from startuing position to absolute end position
546
- public static function hash_data($file, $offset, $end, $algorithm) {
547
- static $tempdir = '';
548
- if (!self::intValueSupported($end)) {
549
- return false;
550
- }
551
- switch ($algorithm) {
552
- case 'md5':
553
- $hash_function = 'md5_file';
554
- $unix_call = 'md5sum';
555
- $windows_call = 'md5sum.exe';
556
- $hash_length = 32;
557
- break;
558
-
559
- case 'sha1':
560
- $hash_function = 'sha1_file';
561
- $unix_call = 'sha1sum';
562
- $windows_call = 'sha1sum.exe';
563
- $hash_length = 40;
564
- break;
565
-
566
- default:
567
- throw new Exception('Invalid algorithm ('.$algorithm.') in self::hash_data()');
568
- break;
569
- }
570
- $size = $end - $offset;
571
- while (true) {
572
- if (GETID3_OS_ISWINDOWS) {
573
-
574
- // It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data
575
- // Fall back to create-temp-file method:
576
- if ($algorithm == 'sha1') {
577
- break;
578
- }
579
-
580
- $RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call);
581
- foreach ($RequiredFiles as $required_file) {
582
- if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
583
- // helper apps not available - fall back to old method
584
- break 2;
585
- }
586
- }
587
- $commandline = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' '.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).' | ';
588
- $commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | ';
589
- $commandline .= GETID3_HELPERAPPSDIR.$windows_call;
590
-
591
- } else {
592
-
593
- $commandline = 'head -c'.$end.' '.escapeshellarg($file).' | ';
594
- $commandline .= 'tail -c'.$size.' | ';
595
- $commandline .= $unix_call;
596
-
597
- }
598
- if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
599
- //throw new Exception('PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm');
600
- break;
601
- }
602
- return substr(`$commandline`, 0, $hash_length);
603
- }
604
-
605
- if (empty($tempdir)) {
606
- // yes this is ugly, feel free to suggest a better way
607
- require_once(dirname(__FILE__).'/getid3.php');
608
- $getid3_temp = new getID3();
609
- $tempdir = $getid3_temp->tempdir;
610
- unset($getid3_temp);
611
- }
612
- // try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir
613
- if (($data_filename = tempnam($tempdir, 'gI3')) === false) {
614
- // can't find anywhere to create a temp file, just fail
615
- return false;
616
- }
617
-
618
- // Init
619
- $result = false;
620
-
621
- // copy parts of file
622
- try {
623
- self::CopyFileParts($file, $data_filename, $offset, $end - $offset);
624
- $result = $hash_function($data_filename);
625
- } catch (Exception $e) {
626
- throw new Exception('self::CopyFileParts() failed in getid_lib::hash_data(): '.$e->getMessage());
627
- }
628
- unlink($data_filename);
629
- return $result;
630
- }
631
-
632
- public static function CopyFileParts($filename_source, $filename_dest, $offset, $length) {
633
- if (!self::intValueSupported($offset + $length)) {
634
- throw new Exception('cannot copy file portion, it extends beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit');
635
- }
636
- if (is_readable($filename_source) && is_file($filename_source) && ($fp_src = fopen($filename_source, 'rb'))) {
637
- if (($fp_dest = fopen($filename_dest, 'wb'))) {
638
- if (fseek($fp_src, $offset) == 0) {
639
- $byteslefttowrite = $length;
640
- while (($byteslefttowrite > 0) && ($buffer = fread($fp_src, min($byteslefttowrite, getID3::FREAD_BUFFER_SIZE)))) {
641
- $byteswritten = fwrite($fp_dest, $buffer, $byteslefttowrite);
642
- $byteslefttowrite -= $byteswritten;
643
- }
644
- return true;
645
- } else {
646
- throw new Exception('failed to seek to offset '.$offset.' in '.$filename_source);
647
- }
648
- fclose($fp_dest);
649
- } else {
650
- throw new Exception('failed to create file for writing '.$filename_dest);
651
- }
652
- fclose($fp_src);
653
- } else {
654
- throw new Exception('failed to open file for reading '.$filename_source);
655
- }
656
- return false;
657
- }
658
-
659
- public static function iconv_fallback_int_utf8($charval) {
660
- if ($charval < 128) {
661
- // 0bbbbbbb
662
- $newcharstring = chr($charval);
663
- } elseif ($charval < 2048) {
664
- // 110bbbbb 10bbbbbb
665
- $newcharstring = chr(($charval >> 6) | 0xC0);
666
- $newcharstring .= chr(($charval & 0x3F) | 0x80);
667
- } elseif ($charval < 65536) {
668
- // 1110bbbb 10bbbbbb 10bbbbbb
669
- $newcharstring = chr(($charval >> 12) | 0xE0);
670
- $newcharstring .= chr(($charval >> 6) | 0xC0);
671
- $newcharstring .= chr(($charval & 0x3F) | 0x80);
672
- } else {
673
- // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
674
- $newcharstring = chr(($charval >> 18) | 0xF0);
675
- $newcharstring .= chr(($charval >> 12) | 0xC0);
676
- $newcharstring .= chr(($charval >> 6) | 0xC0);
677
- $newcharstring .= chr(($charval & 0x3F) | 0x80);
678
- }
679
- return $newcharstring;
680
- }
681
-
682
- // ISO-8859-1 => UTF-8
683
- public static function iconv_fallback_iso88591_utf8($string, $bom=false) {
684
- if (function_exists('utf8_encode')) {
685
- return utf8_encode($string);
686
- }
687
- // utf8_encode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
688
- $newcharstring = '';
689
- if ($bom) {
690
- $newcharstring .= "\xEF\xBB\xBF";
691
- }
692
- for ($i = 0; $i < strlen($string); $i++) {
693
- $charval = ord($string{$i});
694
- $newcharstring .= self::iconv_fallback_int_utf8($charval);
695
- }
696
- return $newcharstring;
697
- }
698
-
699
- // ISO-8859-1 => UTF-16BE
700
- public static function iconv_fallback_iso88591_utf16be($string, $bom=false) {
701
- $newcharstring = '';
702
- if ($bom) {
703
- $newcharstring .= "\xFE\xFF";
704
- }
705
- for ($i = 0; $i < strlen($string); $i++) {
706
- $newcharstring .= "\x00".$string{$i};
707
- }
708
- return $newcharstring;
709
- }
710
-
711
- // ISO-8859-1 => UTF-16LE
712
- public static function iconv_fallback_iso88591_utf16le($string, $bom=false) {
713
- $newcharstring = '';
714
- if ($bom) {
715
- $newcharstring .= "\xFF\xFE";
716
- }
717
- for ($i = 0; $i < strlen($string); $i++) {
718
- $newcharstring .= $string{$i}."\x00";
719
- }
720
- return $newcharstring;
721
- }
722
-
723
- // ISO-8859-1 => UTF-16LE (BOM)
724
- public static function iconv_fallback_iso88591_utf16($string) {
725
- return self::iconv_fallback_iso88591_utf16le($string, true);
726
- }
727
-
728
- // UTF-8 => ISO-8859-1
729
- public static function iconv_fallback_utf8_iso88591($string) {
730
- if (function_exists('utf8_decode')) {
731
- return utf8_decode($string);
732
- }
733
- // utf8_decode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
734
- $newcharstring = '';
735
- $offset = 0;
736
- $stringlength = strlen($string);
737
- while ($offset < $stringlength) {
738
- if ((ord($string{$offset}) | 0x07) == 0xF7) {
739
- // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
740
- $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
741
- ((ord($string{($offset + 1)}) & 0x3F) << 12) &
742
- ((ord($string{($offset + 2)}) & 0x3F) << 6) &
743
- (ord($string{($offset + 3)}) & 0x3F);
744
- $offset += 4;
745
- } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
746
- // 1110bbbb 10bbbbbb 10bbbbbb
747
- $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
748
- ((ord($string{($offset + 1)}) & 0x3F) << 6) &
749
- (ord($string{($offset + 2)}) & 0x3F);
750
- $offset += 3;
751
- } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
752
- // 110bbbbb 10bbbbbb
753
- $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
754
- (ord($string{($offset + 1)}) & 0x3F);
755
- $offset += 2;
756
- } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
757
- // 0bbbbbbb
758
- $charval = ord($string{$offset});
759
- $offset += 1;
760
- } else {
761
- // error? throw some kind of warning here?
762
- $charval = false;
763
- $offset += 1;
764
- }
765
- if ($charval !== false) {
766
- $newcharstring .= (($charval < 256) ? chr($charval) : '?');
767
- }
768
- }
769
- return $newcharstring;
770
- }
771
-
772
- // UTF-8 => UTF-16BE
773
- public static function iconv_fallback_utf8_utf16be($string, $bom=false) {
774
- $newcharstring = '';
775
- if ($bom) {
776
- $newcharstring .= "\xFE\xFF";
777
- }
778
- $offset = 0;
779
- $stringlength = strlen($string);
780
- while ($offset < $stringlength) {
781
- if ((ord($string{$offset}) | 0x07) == 0xF7) {
782
- // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
783
- $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
784
- ((ord($string{($offset + 1)}) & 0x3F) << 12) &
785
- ((ord($string{($offset + 2)}) & 0x3F) << 6) &
786
- (ord($string{($offset + 3)}) & 0x3F);
787
- $offset += 4;
788
- } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
789
- // 1110bbbb 10bbbbbb 10bbbbbb
790
- $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
791
- ((ord($string{($offset + 1)}) & 0x3F) << 6) &
792
- (ord($string{($offset + 2)}) & 0x3F);
793
- $offset += 3;
794
- } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
795
- // 110bbbbb 10bbbbbb
796
- $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
797
- (ord($string{($offset + 1)}) & 0x3F);
798
- $offset += 2;
799
- } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
800
- // 0bbbbbbb
801
- $charval = ord($string{$offset});
802
- $offset += 1;
803
- } else {
804
- // error? throw some kind of warning here?
805
- $charval = false;
806
- $offset += 1;
807
- }
808
- if ($charval !== false) {
809
- $newcharstring .= (($charval < 65536) ? self::BigEndian2String($charval, 2) : "\x00".'?');
810
- }
811
- }
812
- return $newcharstring;
813
- }
814
-
815
- // UTF-8 => UTF-16LE
816
- public static function iconv_fallback_utf8_utf16le($string, $bom=false) {
817
- $newcharstring = '';
818
- if ($bom) {
819
- $newcharstring .= "\xFF\xFE";
820
- }
821
- $offset = 0;
822
- $stringlength = strlen($string);
823
- while ($offset < $stringlength) {
824
- if ((ord($string{$offset}) | 0x07) == 0xF7) {
825
- // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
826
- $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
827
- ((ord($string{($offset + 1)}) & 0x3F) << 12) &
828
- ((ord($string{($offset + 2)}) & 0x3F) << 6) &
829
- (ord($string{($offset + 3)}) & 0x3F);
830
- $offset += 4;
831
- } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
832
- // 1110bbbb 10bbbbbb 10bbbbbb
833
- $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
834
- ((ord($string{($offset + 1)}) & 0x3F) << 6) &
835
- (ord($string{($offset + 2)}) & 0x3F);
836
- $offset += 3;
837
- } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
838
- // 110bbbbb 10bbbbbb
839
- $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
840
- (ord($string{($offset + 1)}) & 0x3F);
841
- $offset += 2;
842
- } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
843
- // 0bbbbbbb
844
- $charval = ord($string{$offset});
845
- $offset += 1;
846
- } else {
847
- // error? maybe throw some warning here?
848
- $charval = false;
849
- $offset += 1;
850
- }
851
- if ($charval !== false) {
852
- $newcharstring .= (($charval < 65536) ? self::LittleEndian2String($charval, 2) : '?'."\x00");
853
- }
854
- }
855
- return $newcharstring;
856
- }
857
-
858
- // UTF-8 => UTF-16LE (BOM)
859
- public static function iconv_fallback_utf8_utf16($string) {
860
- return self::iconv_fallback_utf8_utf16le($string, true);
861
- }
862
-
863
- // UTF-16BE => UTF-8
864
- public static function iconv_fallback_utf16be_utf8($string) {
865
- if (substr($string, 0, 2) == "\xFE\xFF") {
866
- // strip BOM
867
- $string = substr($string, 2);
868
- }
869
- $newcharstring = '';
870
- for ($i = 0; $i < strlen($string); $i += 2) {
871
- $charval = self::BigEndian2Int(substr($string, $i, 2));
872
- $newcharstring .= self::iconv_fallback_int_utf8($charval);
873
- }
874
- return $newcharstring;
875
- }
876
-
877
- // UTF-16LE => UTF-8
878
- public static function iconv_fallback_utf16le_utf8($string) {
879
- if (substr($string, 0, 2) == "\xFF\xFE") {
880
- // strip BOM
881
- $string = substr($string, 2);
882
- }
883
- $newcharstring = '';
884
- for ($i = 0; $i < strlen($string); $i += 2) {
885
- $charval = self::LittleEndian2Int(substr($string, $i, 2));
886
- $newcharstring .= self::iconv_fallback_int_utf8($charval);
887
- }
888
- return $newcharstring;
889
- }
890
-
891
- // UTF-16BE => ISO-8859-1
892
- public static function iconv_fallback_utf16be_iso88591($string) {
893
- if (substr($string, 0, 2) == "\xFE\xFF") {
894
- // strip BOM
895
- $string = substr($string, 2);
896
- }
897
- $newcharstring = '';
898
- for ($i = 0; $i < strlen($string); $i += 2) {
899
- $charval = self::BigEndian2Int(substr($string, $i, 2));
900
- $newcharstring .= (($charval < 256) ? chr($charval) : '?');
901
- }
902
- return $newcharstring;
903
- }
904
-
905
- // UTF-16LE => ISO-8859-1
906
- public static function iconv_fallback_utf16le_iso88591($string) {
907
- if (substr($string, 0, 2) == "\xFF\xFE") {
908
- // strip BOM
909
- $string = substr($string, 2);
910
- }
911
- $newcharstring = '';
912
- for ($i = 0; $i < strlen($string); $i += 2) {
913
- $charval = self::LittleEndian2Int(substr($string, $i, 2));
914
- $newcharstring .= (($charval < 256) ? chr($charval) : '?');
915
- }
916
- return $newcharstring;
917
- }
918
-
919
- // UTF-16 (BOM) => ISO-8859-1
920
- public static function iconv_fallback_utf16_iso88591($string) {
921
- $bom = substr($string, 0, 2);
922
- if ($bom == "\xFE\xFF") {
923
- return self::iconv_fallback_utf16be_iso88591(substr($string, 2));
924
- } elseif ($bom == "\xFF\xFE") {
925
- return self::iconv_fallback_utf16le_iso88591(substr($string, 2));
926
- }
927
- return $string;
928
- }
929
-
930
- // UTF-16 (BOM) => UTF-8
931
- public static function iconv_fallback_utf16_utf8($string) {
932
- $bom = substr($string, 0, 2);
933
- if ($bom == "\xFE\xFF") {
934
- return self::iconv_fallback_utf16be_utf8(substr($string, 2));
935
- } elseif ($bom == "\xFF\xFE") {
936
- return self::iconv_fallback_utf16le_utf8(substr($string, 2));
937
- }
938
- return $string;
939
- }
940
-
941
- public static function iconv_fallback($in_charset, $out_charset, $string) {
942
-
943
- if ($in_charset == $out_charset) {
944
- return $string;
945
- }
946
-
947
- // iconv() availble
948
- if (function_exists('iconv')) {
949
- if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) {
950
- switch ($out_charset) {
951
- case 'ISO-8859-1':
952
- $converted_string = rtrim($converted_string, "\x00");
953
- break;
954
- }
955
- return $converted_string;
956
- }
957
-
958
- // iconv() may sometimes fail with "illegal character in input string" error message
959
- // and return an empty string, but returning the unconverted string is more useful
960
- return $string;
961
- }
962
-
963
-
964
- // iconv() not available
965
- static $ConversionFunctionList = array();
966
- if (empty($ConversionFunctionList)) {
967
- $ConversionFunctionList['ISO-8859-1']['UTF-8'] = 'iconv_fallback_iso88591_utf8';
968
- $ConversionFunctionList['ISO-8859-1']['UTF-16'] = 'iconv_fallback_iso88591_utf16';
969
- $ConversionFunctionList['ISO-8859-1']['UTF-16BE'] = 'iconv_fallback_iso88591_utf16be';
970
- $ConversionFunctionList['ISO-8859-1']['UTF-16LE'] = 'iconv_fallback_iso88591_utf16le';
971
- $ConversionFunctionList['UTF-8']['ISO-8859-1'] = 'iconv_fallback_utf8_iso88591';
972
- $ConversionFunctionList['UTF-8']['UTF-16'] = 'iconv_fallback_utf8_utf16';
973
- $ConversionFunctionList['UTF-8']['UTF-16BE'] = 'iconv_fallback_utf8_utf16be';
974
- $ConversionFunctionList['UTF-8']['UTF-16LE'] = 'iconv_fallback_utf8_utf16le';
975
- $ConversionFunctionList['UTF-16']['ISO-8859-1'] = 'iconv_fallback_utf16_iso88591';
976
- $ConversionFunctionList['UTF-16']['UTF-8'] = 'iconv_fallback_utf16_utf8';
977
- $ConversionFunctionList['UTF-16LE']['ISO-8859-1'] = 'iconv_fallback_utf16le_iso88591';
978
- $ConversionFunctionList['UTF-16LE']['UTF-8'] = 'iconv_fallback_utf16le_utf8';
979
- $ConversionFunctionList['UTF-16BE']['ISO-8859-1'] = 'iconv_fallback_utf16be_iso88591';
980
- $ConversionFunctionList['UTF-16BE']['UTF-8'] = 'iconv_fallback_utf16be_utf8';
981
- }
982
- if (isset($ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)])) {
983
- $ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)];
984
- return self::$ConversionFunction($string);
985
- }
986
- throw new Exception('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset);
987
- }
988
-
989
- public static function recursiveMultiByteCharString2HTML($data, $charset='ISO-8859-1') {
990
- if (is_string($data)) {
991
- return self::MultiByteCharString2HTML($data, $charset);
992
- } elseif (is_array($data)) {
993
- $return_data = array();
994
- foreach ($data as $key => $value) {
995
- $return_data[$key] = self::recursiveMultiByteCharString2HTML($value, $charset);
996
- }
997
- return $return_data;
998
- }
999
- // integer, float, objects, resources, etc
1000
- return $data;
1001
- }
1002
-
1003
- public static function MultiByteCharString2HTML($string, $charset='ISO-8859-1') {
1004
- $string = (string) $string; // in case trying to pass a numeric (float, int) string, would otherwise return an empty string
1005
- $HTMLstring = '';
1006
-
1007
- switch ($charset) {
1008
- case '1251':
1009
- case '1252':
1010
- case '866':
1011
- case '932':
1012
- case '936':
1013
- case '950':
1014
- case 'BIG5':
1015
- case 'BIG5-HKSCS':
1016
- case 'cp1251':
1017
- case 'cp1252':
1018
- case 'cp866':
1019
- case 'EUC-JP':
1020
- case 'EUCJP':
1021
- case 'GB2312':
1022
- case 'ibm866':
1023
- case 'ISO-8859-1':
1024
- case 'ISO-8859-15':
1025
- case 'ISO8859-1':
1026
- case 'ISO8859-15':
1027
- case 'KOI8-R':
1028
- case 'koi8-ru':
1029
- case 'koi8r':
1030
- case 'Shift_JIS':
1031
- case 'SJIS':
1032
- case 'win-1251':
1033
- case 'Windows-1251':
1034
- case 'Windows-1252':
1035
- $HTMLstring = htmlentities($string, ENT_COMPAT, $charset);
1036
- break;
1037
-
1038
- case 'UTF-8':
1039
- $strlen = strlen($string);
1040
- for ($i = 0; $i < $strlen; $i++) {
1041
- $char_ord_val = ord($string{$i});
1042
- $charval = 0;
1043
- if ($char_ord_val < 0x80) {
1044
- $charval = $char_ord_val;
1045
- } elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F && $i+3 < $strlen) {
1046
- $charval = (($char_ord_val & 0x07) << 18);
1047
- $charval += ((ord($string{++$i}) & 0x3F) << 12);
1048
- $charval += ((ord($string{++$i}) & 0x3F) << 6);
1049
- $charval += (ord($string{++$i}) & 0x3F);
1050
- } elseif ((($char_ord_val & 0xE0) >> 5) == 0x07 && $i+2 < $strlen) {
1051
- $charval = (($char_ord_val & 0x0F) << 12);
1052
- $charval += ((ord($string{++$i}) & 0x3F) << 6);
1053
- $charval += (ord($string{++$i}) & 0x3F);
1054
- } elseif ((($char_ord_val & 0xC0) >> 6) == 0x03 && $i+1 < $strlen) {
1055
- $charval = (($char_ord_val & 0x1F) << 6);
1056
- $charval += (ord($string{++$i}) & 0x3F);
1057
- }
1058
- if (($charval >= 32) && ($charval <= 127)) {
1059
- $HTMLstring .= htmlentities(chr($charval));
1060
- } else {
1061
- $HTMLstring .= '&#'.$charval.';';
1062
- }
1063
- }
1064
- break;
1065
-
1066
- case 'UTF-16LE':
1067
- for ($i = 0; $i < strlen($string); $i += 2) {
1068
- $charval = self::LittleEndian2Int(substr($string, $i, 2));
1069
- if (($charval >= 32) && ($charval <= 127)) {
1070
- $HTMLstring .= chr($charval);
1071
- } else {
1072
- $HTMLstring .= '&#'.$charval.';';
1073
- }
1074
- }
1075
- break;
1076
-
1077
- case 'UTF-16BE':
1078
- for ($i = 0; $i < strlen($string); $i += 2) {
1079
- $charval = self::BigEndian2Int(substr($string, $i, 2));
1080
- if (($charval >= 32) && ($charval <= 127)) {
1081
- $HTMLstring .= chr($charval);
1082
- } else {
1083
- $HTMLstring .= '&#'.$charval.';';
1084
- }
1085
- }
1086
- break;
1087
-
1088
- default:
1089
- $HTMLstring = 'ERROR: Character set "'.$charset.'" not supported in MultiByteCharString2HTML()';
1090
- break;
1091
- }
1092
- return $HTMLstring;
1093
- }
1094
-
1095
-
1096
-
1097
- public static function RGADnameLookup($namecode) {
1098
- static $RGADname = array();
1099
- if (empty($RGADname)) {
1100
- $RGADname[0] = 'not set';
1101
- $RGADname[1] = 'Track Gain Adjustment';
1102
- $RGADname[2] = 'Album Gain Adjustment';
1103
- }
1104
-
1105
- return (isset($RGADname[$namecode]) ? $RGADname[$namecode] : '');
1106
- }
1107
-
1108
-
1109
- public static function RGADoriginatorLookup($originatorcode) {
1110
- static $RGADoriginator = array();
1111
- if (empty($RGADoriginator)) {
1112
- $RGADoriginator[0] = 'unspecified';
1113
- $RGADoriginator[1] = 'pre-set by artist/producer/mastering engineer';
1114
- $RGADoriginator[2] = 'set by user';
1115
- $RGADoriginator[3] = 'determined automatically';
1116
- }
1117
-
1118
- return (isset($RGADoriginator[$originatorcode]) ? $RGADoriginator[$originatorcode] : '');
1119
- }
1120
-
1121
-
1122
- public static function RGADadjustmentLookup($rawadjustment, $signbit) {
1123
- $adjustment = $rawadjustment / 10;
1124
- if ($signbit == 1) {
1125
- $adjustment *= -1;
1126
- }
1127
- return (float) $adjustment;
1128
- }
1129
-
1130
-
1131
- public static function RGADgainString($namecode, $originatorcode, $replaygain) {
1132
- if ($replaygain < 0) {
1133
- $signbit = '1';
1134
- } else {
1135
- $signbit = '0';
1136
- }
1137
- $storedreplaygain = intval(round($replaygain * 10));
1138
- $gainstring = str_pad(decbin($namecode), 3, '0', STR_PAD_LEFT);
1139
- $gainstring .= str_pad(decbin($originatorcode), 3, '0', STR_PAD_LEFT);
1140
- $gainstring .= $signbit;
1141
- $gainstring .= str_pad(decbin($storedreplaygain), 9, '0', STR_PAD_LEFT);
1142
-
1143
- return $gainstring;
1144
- }
1145
-
1146
- public static function RGADamplitude2dB($amplitude) {
1147
- return 20 * log10($amplitude);
1148
- }
1149
-
1150
-
1151
- public static function GetDataImageSize($imgData, &$imageinfo=array()) {
1152
- static $tempdir = '';
1153
- if (empty($tempdir)) {
1154
- // yes this is ugly, feel free to suggest a better way
1155
- require_once(dirname(__FILE__).'/getid3.php');
1156
- $getid3_temp = new getID3();
1157
- $tempdir = $getid3_temp->tempdir;
1158
- unset($getid3_temp);
1159
- }
1160
- $GetDataImageSize = false;
1161
- if ($tempfilename = tempnam($tempdir, 'gI3')) {
1162
- if (is_writable($tempfilename) && is_file($tempfilename) && ($tmp = fopen($tempfilename, 'wb'))) {
1163
- fwrite($tmp, $imgData);
1164
- fclose($tmp);
1165
- $GetDataImageSize = @getimagesize($tempfilename, $imageinfo);
1166
- }
1167
- unlink($tempfilename);
1168
- }
1169
- return $GetDataImageSize;
1170
- }
1171
-
1172
- public static function ImageExtFromMime($mime_type) {
1173
- // temporary way, works OK for now, but should be reworked in the future
1174
- return str_replace(array('image/', 'x-', 'jpeg'), array('', '', 'jpg'), $mime_type);
1175
- }
1176
-
1177
- public static function ImageTypesLookup($imagetypeid) {
1178
- static $ImageTypesLookup = array();
1179
- if (empty($ImageTypesLookup)) {
1180
- $ImageTypesLookup[1] = 'gif';
1181
- $ImageTypesLookup[2] = 'jpeg';
1182
- $ImageTypesLookup[3] = 'png';
1183
- $ImageTypesLookup[4] = 'swf';
1184
- $ImageTypesLookup[5] = 'psd';
1185
- $ImageTypesLookup[6] = 'bmp';
1186
- $ImageTypesLookup[7] = 'tiff (little-endian)';
1187
- $ImageTypesLookup[8] = 'tiff (big-endian)';
1188
- $ImageTypesLookup[9] = 'jpc';
1189
- $ImageTypesLookup[10] = 'jp2';
1190
- $ImageTypesLookup[11] = 'jpx';
1191
- $ImageTypesLookup[12] = 'jb2';
1192
- $ImageTypesLookup[13] = 'swc';
1193
- $ImageTypesLookup[14] = 'iff';
1194
- }
1195
- return (isset($ImageTypesLookup[$imagetypeid]) ? $ImageTypesLookup[$imagetypeid] : '');
1196
- }
1197
-
1198
- public static function CopyTagsToComments(&$ThisFileInfo) {
1199
-
1200
- // Copy all entries from ['tags'] into common ['comments']
1201
- if (!empty($ThisFileInfo['tags'])) {
1202
- foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) {
1203
- foreach ($tagarray as $tagname => $tagdata) {
1204
- foreach ($tagdata as $key => $value) {
1205
- if (!empty($value)) {
1206
- if (empty($ThisFileInfo['comments'][$tagname])) {
1207
-
1208
- // fall through and append value
1209
-
1210
- } elseif ($tagtype == 'id3v1') {
1211
-
1212
- $newvaluelength = strlen(trim($value));
1213
- foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
1214
- $oldvaluelength = strlen(trim($existingvalue));
1215
- if (($newvaluelength <= $oldvaluelength) && (substr($existingvalue, 0, $newvaluelength) == trim($value))) {
1216
- // new value is identical but shorter-than (or equal-length to) one already in comments - skip
1217
- break 2;
1218
- }
1219
- }
1220
-
1221
- } elseif (!is_array($value)) {
1222
-
1223
- $newvaluelength = strlen(trim($value));
1224
- foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
1225
- $oldvaluelength = strlen(trim($existingvalue));
1226
- if ((strlen($existingvalue) > 10) && ($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) {
1227
- $ThisFileInfo['comments'][$tagname][$existingkey] = trim($value);
1228
- //break 2;
1229
- break;
1230
- }
1231
- }
1232
-
1233
- }
1234
- if (is_array($value) || empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) {
1235
- $value = (is_string($value) ? trim($value) : $value);
1236
- if (!is_numeric($key)) {
1237
- $ThisFileInfo['comments'][$tagname][$key] = $value;
1238
- } else {
1239
- $ThisFileInfo['comments'][$tagname][] = $value;
1240
- }
1241
- }
1242
- }
1243
- }
1244
- }
1245
- }
1246
-
1247
- // Copy to ['comments_html']
1248
- if (!empty($ThisFileInfo['comments'])) {
1249
- foreach ($ThisFileInfo['comments'] as $field => $values) {
1250
- if ($field == 'picture') {
1251
- // pictures can take up a lot of space, and we don't need multiple copies of them
1252
- // let there be a single copy in [comments][picture], and not elsewhere
1253
- continue;
1254
- }
1255
- foreach ($values as $index => $value) {
1256
- if (is_array($value)) {
1257
- $ThisFileInfo['comments_html'][$field][$index] = $value;
1258
- } else {
1259
- $ThisFileInfo['comments_html'][$field][$index] = str_replace('&#0;', '', self::MultiByteCharString2HTML($value, $ThisFileInfo['encoding']));
1260
- }
1261
- }
1262
- }
1263
- }
1264
-
1265
- }
1266
- return true;
1267
- }
1268
-
1269
-
1270
- public static function EmbeddedLookup($key, $begin, $end, $file, $name) {
1271
-
1272
- // Cached
1273
- static $cache;
1274
- if (isset($cache[$file][$name])) {
1275
- return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : '');
1276
- }
1277
-
1278
- // Init
1279
- $keylength = strlen($key);
1280
- $line_count = $end - $begin - 7;
1281
-
1282
- // Open php file
1283
- $fp = fopen($file, 'r');
1284
-
1285
- // Discard $begin lines
1286
- for ($i = 0; $i < ($begin + 3); $i++) {
1287
- fgets($fp, 1024);
1288
- }
1289
-
1290
- // Loop thru line
1291
- while (0 < $line_count--) {
1292
-
1293
- // Read line
1294
- $line = ltrim(fgets($fp, 1024), "\t ");
1295
-
1296
- // METHOD A: only cache the matching key - less memory but slower on next lookup of not-previously-looked-up key
1297
- //$keycheck = substr($line, 0, $keylength);
1298
- //if ($key == $keycheck) {
1299
- // $cache[$file][$name][$keycheck] = substr($line, $keylength + 1);
1300
- // break;
1301
- //}
1302
-
1303
- // METHOD B: cache all keys in this lookup - more memory but faster on next lookup of not-previously-looked-up key
1304
- //$cache[$file][$name][substr($line, 0, $keylength)] = trim(substr($line, $keylength + 1));
1305
- $explodedLine = explode("\t", $line, 2);
1306
- $ThisKey = (isset($explodedLine[0]) ? $explodedLine[0] : '');
1307
- $ThisValue = (isset($explodedLine[1]) ? $explodedLine[1] : '');
1308
- $cache[$file][$name][$ThisKey] = trim($ThisValue);
1309
- }
1310
-
1311
- // Close and return
1312
- fclose($fp);
1313
- return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : '');
1314
- }
1315
-
1316
- public static function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) {
1317
- global $GETID3_ERRORARRAY;
1318
-
1319
- if (file_exists($filename)) {
1320
- if (include_once($filename)) {
1321
- return true;
1322
- } else {
1323
- $diemessage = basename($sourcefile).' depends on '.$filename.', which has errors';
1324
- }
1325
- } else {
1326
- $diemessage = basename($sourcefile).' depends on '.$filename.', which is missing';
1327
- }
1328
- if ($DieOnFailure) {
1329
- throw new Exception($diemessage);
1330
- } else {
1331
- $GETID3_ERRORARRAY[] = $diemessage;
1332
- }
1333
- return false;
1334
- }
1335
-
1336
- public static function trimNullByte($string) {
1337
- return trim($string, "\x00");
1338
- }
1339
-
1340
- public static function getFileSizeSyscall($path) {
1341
- $filesize = false;
1342
-
1343
- if (GETID3_OS_ISWINDOWS) {
1344
- if (class_exists('COM')) { // From PHP 5.3.15 and 5.4.5, COM and DOTNET is no longer built into the php core.you have to add COM support in php.ini:
1345
- $filesystem = new COM('Scripting.FileSystemObject');
1346
- $file = $filesystem->GetFile($path);
1347
- $filesize = $file->Size();
1348
- unset($filesystem, $file);
1349
- } else {
1350
- $commandline = 'for %I in ('.escapeshellarg($path).') do @echo %~zI';
1351
- }
1352
- } else {
1353
- $commandline = 'ls -l '.escapeshellarg($path).' | awk \'{print $5}\'';
1354
- }
1355
- if (isset($commandline)) {
1356
- $output = trim(`$commandline`);
1357
- if (ctype_digit($output)) {
1358
- $filesize = (float) $output;
1359
- }
1360
- }
1361
- return $filesize;
1362
- }
1363
-
1364
-
1365
- /**
1366
- * Workaround for Bug #37268 (https://bugs.php.net/bug.php?id=37268)
1367
- * @param string $path A path.
1368
- * @param string $suffix If the name component ends in suffix this will also be cut off.
1369
- * @return string
1370
- */
1371
- public static function mb_basename($path, $suffix = null) {
1372
- $splited = preg_split('#/#', rtrim($path, '/ '));
1373
- return substr(basename('X'.$splited[count($splited) - 1], $suffix), 1);
1374
- }
1375
-
1376
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /////////////////////////////////////////////////////////////////
3
+ /// getID3() by James Heinrich <info@getid3.org> //
4
+ // available at http://getid3.sourceforge.net //
5
+ // or http://www.getid3.org //
6
+ // also https://github.com/JamesHeinrich/getID3 //
7
+ /////////////////////////////////////////////////////////////////
8
+ // //
9
+ // getid3.lib.php - part of getID3() //
10
+ // See readme.txt for more details //
11
+ // ///
12
+ /////////////////////////////////////////////////////////////////
13
+
14
+
15
+ class getid3_lib
16
+ {
17
+
18
+ public static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8') {
19
+ $returnstring = '';
20
+ for ($i = 0; $i < strlen($string); $i++) {
21
+ if ($hex) {
22
+ $returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
23
+ } else {
24
+ $returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string{$i}) ? $string{$i} : '¤');
25
+ }
26
+ if ($spaces) {
27
+ $returnstring .= ' ';
28
+ }
29
+ }
30
+ if (!empty($htmlencoding)) {
31
+ if ($htmlencoding === true) {
32
+ $htmlencoding = 'UTF-8'; // prior to getID3 v1.9.0 the function's 4th parameter was boolean
33
+ }
34
+ $returnstring = htmlentities($returnstring, ENT_QUOTES, $htmlencoding);
35
+ }
36
+ return $returnstring;
37
+ }
38
+
39
+ public static function trunc($floatnumber) {
40
+ // truncates a floating-point number at the decimal point
41
+ // returns int (if possible, otherwise float)
42
+ if ($floatnumber >= 1) {
43
+ $truncatednumber = floor($floatnumber);
44
+ } elseif ($floatnumber <= -1) {
45
+ $truncatednumber = ceil($floatnumber);
46
+ } else {
47
+ $truncatednumber = 0;
48
+ }
49
+ if (self::intValueSupported($truncatednumber)) {
50
+ $truncatednumber = (int) $truncatednumber;
51
+ }
52
+ return $truncatednumber;
53
+ }
54
+
55
+
56
+ public static function safe_inc(&$variable, $increment=1) {
57
+ if (isset($variable)) {
58
+ $variable += $increment;
59
+ } else {
60
+ $variable = $increment;
61
+ }
62
+ return true;
63
+ }
64
+
65
+ public static function CastAsInt($floatnum) {
66
+ // convert to float if not already
67
+ $floatnum = (float) $floatnum;
68
+
69
+ // convert a float to type int, only if possible
70
+ if (self::trunc($floatnum) == $floatnum) {
71
+ // it's not floating point
72
+ if (self::intValueSupported($floatnum)) {
73
+ // it's within int range
74
+ $floatnum = (int) $floatnum;
75
+ }
76
+ }
77
+ return $floatnum;
78
+ }
79
+
80
+ public static function intValueSupported($num) {
81
+ // check if integers are 64-bit
82
+ static $hasINT64 = null;
83
+ if ($hasINT64 === null) { // 10x faster than is_null()
84
+ $hasINT64 = is_int(pow(2, 31)); // 32-bit int are limited to (2^31)-1
85
+ if (!$hasINT64 && !defined('PHP_INT_MIN')) {
86
+ define('PHP_INT_MIN', ~PHP_INT_MAX);
87
+ }
88
+ }
89
+ // if integers are 64-bit - no other check required
90
+ if ($hasINT64 || (($num <= PHP_INT_MAX) && ($num >= PHP_INT_MIN))) {
91
+ return true;
92
+ }
93
+ return false;
94
+ }
95
+
96
+ public static function DecimalizeFraction($fraction) {
97
+ list($numerator, $denominator) = explode('/', $fraction);
98
+ return $numerator / ($denominator ? $denominator : 1);
99
+ }
100
+
101
+
102
+ public static function DecimalBinary2Float($binarynumerator) {
103
+ $numerator = self::Bin2Dec($binarynumerator);
104
+ $denominator = self::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator)));
105
+ return ($numerator / $denominator);
106
+ }
107
+
108
+
109
+ public static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) {
110
+ // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
111
+ if (strpos($binarypointnumber, '.') === false) {
112
+ $binarypointnumber = '0.'.$binarypointnumber;
113
+ } elseif ($binarypointnumber{0} == '.') {
114
+ $binarypointnumber = '0'.$binarypointnumber;
115
+ }
116
+ $exponent = 0;
117
+ while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
118
+ if (substr($binarypointnumber, 1, 1) == '.') {
119
+ $exponent--;
120
+ $binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3);
121
+ } else {
122
+ $pointpos = strpos($binarypointnumber, '.');
123
+ $exponent += ($pointpos - 1);
124
+ $binarypointnumber = str_replace('.', '', $binarypointnumber);
125
+ $binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1);
126
+ }
127
+ }
128
+ $binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT);
129
+ return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent);
130
+ }
131
+
132
+
133
+ public static function Float2BinaryDecimal($floatvalue) {
134
+ // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
135
+ $maxbits = 128; // to how many bits of precision should the calculations be taken?
136
+ $intpart = self::trunc($floatvalue);
137
+ $floatpart = abs($floatvalue - $intpart);
138
+ $pointbitstring = '';
139
+ while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) {
140
+ $floatpart *= 2;
141
+ $pointbitstring .= (string) self::trunc($floatpart);
142
+ $floatpart -= self::trunc($floatpart);
143
+ }
144
+ $binarypointnumber = decbin($intpart).'.'.$pointbitstring;
145
+ return $binarypointnumber;
146
+ }
147
+
148
+
149
+ public static function Float2String($floatvalue, $bits) {
150
+ // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
151
+ switch ($bits) {
152
+ case 32:
153
+ $exponentbits = 8;
154
+ $fractionbits = 23;
155
+ break;
156
+
157
+ case 64:
158
+ $exponentbits = 11;
159
+ $fractionbits = 52;
160
+ break;
161
+
162
+ default:
163
+ return false;
164
+ break;
165
+ }
166
+ if ($floatvalue >= 0) {
167
+ $signbit = '0';
168
+ } else {
169
+ $signbit = '1';
170
+ }
171
+ $normalizedbinary = self::NormalizeBinaryPoint(self::Float2BinaryDecimal($floatvalue), $fractionbits);
172
+ $biasedexponent = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent
173
+ $exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT);
174
+ $fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT);
175
+
176
+ return self::BigEndian2String(self::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false);
177
+ }
178
+
179
+
180
+ public static function LittleEndian2Float($byteword) {
181
+ return self::BigEndian2Float(strrev($byteword));
182
+ }
183
+
184
+
185
+ public static function BigEndian2Float($byteword) {
186
+ // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
187
+ // http://www.psc.edu/general/software/packages/ieee/ieee.html
188
+ // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
189
+
190
+ $bitword = self::BigEndian2Bin($byteword);
191
+ if (!$bitword) {
192
+ return 0;
193
+ }
194
+ $signbit = $bitword{0};
195
+
196
+ switch (strlen($byteword) * 8) {
197
+ case 32:
198
+ $exponentbits = 8;
199
+ $fractionbits = 23;
200
+ break;
201
+
202
+ case 64:
203
+ $exponentbits = 11;
204
+ $fractionbits = 52;
205
+ break;
206
+
207
+ case 80:
208
+ // 80-bit Apple SANE format
209
+ // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
210
+ $exponentstring = substr($bitword, 1, 15);
211
+ $isnormalized = intval($bitword{16});
212
+ $fractionstring = substr($bitword, 17, 63);
213
+ $exponent = pow(2, self::Bin2Dec($exponentstring) - 16383);
214
+ $fraction = $isnormalized + self::DecimalBinary2Float($fractionstring);
215
+ $floatvalue = $exponent * $fraction;
216
+ if ($signbit == '1') {
217
+ $floatvalue *= -1;
218
+ }
219
+ return $floatvalue;
220
+ break;
221
+
222
+ default:
223
+ return false;
224
+ break;
225
+ }
226
+ $exponentstring = substr($bitword, 1, $exponentbits);
227
+ $fractionstring = substr($bitword, $exponentbits + 1, $fractionbits);
228
+ $exponent = self::Bin2Dec($exponentstring);
229
+ $fraction = self::Bin2Dec($fractionstring);
230
+
231
+ if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) {
232
+ // Not a Number
233
+ $floatvalue = false;
234
+ } elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) {
235
+ if ($signbit == '1') {
236
+ $floatvalue = '-infinity';
237
+ } else {
238
+ $floatvalue = '+infinity';
239
+ }
240
+ } elseif (($exponent == 0) && ($fraction == 0)) {
241
+ if ($signbit == '1') {
242
+ $floatvalue = -0;
243
+ } else {
244
+ $floatvalue = 0;
245
+ }
246
+ $floatvalue = ($signbit ? 0 : -0);
247
+ } elseif (($exponent == 0) && ($fraction != 0)) {
248
+ // These are 'unnormalized' values
249
+ $floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * self::DecimalBinary2Float($fractionstring);
250
+ if ($signbit == '1') {
251
+ $floatvalue *= -1;
252
+ }
253
+ } elseif ($exponent != 0) {
254
+ $floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + self::DecimalBinary2Float($fractionstring));
255
+ if ($signbit == '1') {
256
+ $floatvalue *= -1;
257
+ }
258
+ }
259
+ return (float) $floatvalue;
260
+ }
261
+
262
+
263
+ public static function BigEndian2Int($byteword, $synchsafe=false, $signed=false) {
264
+ $intvalue = 0;
265
+ $bytewordlen = strlen($byteword);
266
+ if ($bytewordlen == 0) {
267
+ return false;
268
+ }
269
+ for ($i = 0; $i < $bytewordlen; $i++) {
270
+ if ($synchsafe) { // disregard MSB, effectively 7-bit bytes
271
+ //$intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7); // faster, but runs into problems past 2^31 on 32-bit systems
272
+ $intvalue += (ord($byteword{$i}) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7);
273
+ } else {
274
+ $intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
275
+ }
276
+ }
277
+ if ($signed && !$synchsafe) {
278
+ // synchsafe ints are not allowed to be signed
279
+ if ($bytewordlen <= PHP_INT_SIZE) {
280
+ $signMaskBit = 0x80 << (8 * ($bytewordlen - 1));
281
+ if ($intvalue & $signMaskBit) {
282
+ $intvalue = 0 - ($intvalue & ($signMaskBit - 1));
283
+ }
284
+ } else {
285
+ throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits ('.strlen($byteword).') in self::BigEndian2Int()');
286
+ }
287
+ }
288
+ return self::CastAsInt($intvalue);
289
+ }
290
+
291
+
292
+ public static function LittleEndian2Int($byteword, $signed=false) {
293
+ return self::BigEndian2Int(strrev($byteword), false, $signed);
294
+ }
295
+
296
+
297
+ public static function BigEndian2Bin($byteword) {
298
+ $binvalue = '';
299
+ $bytewordlen = strlen($byteword);
300
+ for ($i = 0; $i < $bytewordlen; $i++) {
301
+ $binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT);
302
+ }
303
+ return $binvalue;
304
+ }
305
+
306
+
307
+ public static function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) {
308
+ if ($number < 0) {
309
+ throw new Exception('ERROR: self::BigEndian2String() does not support negative numbers');
310
+ }
311
+ $maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF);
312
+ $intstring = '';
313
+ if ($signed) {
314
+ if ($minbytes > PHP_INT_SIZE) {
315
+ throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits in self::BigEndian2String()');
316
+ }
317
+ $number = $number & (0x80 << (8 * ($minbytes - 1)));
318
+ }
319
+ while ($number != 0) {
320
+ $quotient = ($number / ($maskbyte + 1));
321
+ $intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring;
322
+ $number = floor($quotient);
323
+ }
324
+ return str_pad($intstring, $minbytes, "\x00", STR_PAD_LEFT);
325
+ }
326
+
327
+
328
+ public static function Dec2Bin($number) {
329
+ while ($number >= 256) {
330
+ $bytes[] = (($number / 256) - (floor($number / 256))) * 256;
331
+ $number = floor($number / 256);
332
+ }
333
+ $bytes[] = $number;
334
+ $binstring = '';
335
+ for ($i = 0; $i < count($bytes); $i++) {
336
+ $binstring = (($i == count($bytes) - 1) ? decbin($bytes[$i]) : str_pad(decbin($bytes[$i]), 8, '0', STR_PAD_LEFT)).$binstring;
337
+ }
338
+ return $binstring;
339
+ }
340
+
341
+
342
+ public static function Bin2Dec($binstring, $signed=false) {
343
+ $signmult = 1;
344
+ if ($signed) {
345
+ if ($binstring{0} == '1') {
346
+ $signmult = -1;
347
+ }
348
+ $binstring = substr($binstring, 1);
349
+ }
350
+ $decvalue = 0;
351
+ for ($i = 0; $i < strlen($binstring); $i++) {
352
+ $decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i);
353
+ }
354
+ return self::CastAsInt($decvalue * $signmult);
355
+ }
356
+
357
+
358
+ public static function Bin2String($binstring) {
359
+ // return 'hi' for input of '0110100001101001'
360
+ $string = '';
361
+ $binstringreversed = strrev($binstring);
362
+ for ($i = 0; $i < strlen($binstringreversed); $i += 8) {
363
+ $string = chr(self::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string;
364
+ }
365
+ return $string;
366
+ }
367
+
368
+
369
+ public static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) {
370
+ $intstring = '';
371
+ while ($number > 0) {
372
+ if ($synchsafe) {
373
+ $intstring = $intstring.chr($number & 127);
374
+ $number >>= 7;
375
+ } else {
376
+ $intstring = $intstring.chr($number & 255);
377
+ $number >>= 8;
378
+ }
379
+ }
380
+ return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
381
+ }
382
+
383
+
384
+ public static function array_merge_clobber($array1, $array2) {
385
+ // written by kcØhireability*com
386
+ // taken from http://www.php.net/manual/en/function.array-merge-recursive.php
387
+ if (!is_array($array1) || !is_array($array2)) {
388
+ return false;
389
+ }
390
+ $newarray = $array1;
391
+ foreach ($array2 as $key => $val) {
392
+ if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
393
+ $newarray[$key] = self::array_merge_clobber($newarray[$key], $val);
394
+ } else {
395
+ $newarray[$key] = $val;
396
+ }
397
+ }
398
+ return $newarray;
399
+ }
400
+
401
+
402
+ public static function array_merge_noclobber($array1, $array2) {
403
+ if (!is_array($array1) || !is_array($array2)) {
404
+ return false;
405
+ }
406
+ $newarray = $array1;
407
+ foreach ($array2 as $key => $val) {
408
+ if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
409
+ $newarray[$key] = self::array_merge_noclobber($newarray[$key], $val);
410
+ } elseif (!isset($newarray[$key])) {
411
+ $newarray[$key] = $val;
412
+ }
413
+ }
414
+ return $newarray;
415
+ }
416
+
417
+ public static function flipped_array_merge_noclobber($array1, $array2) {
418
+ if (!is_array($array1) || !is_array($array2)) {
419
+ return false;
420
+ }
421
+ # naturally, this only works non-recursively
422
+ $newarray = array_flip($array1);
423
+ foreach (array_flip($array2) as $key => $val) {
424
+ if (!isset($newarray[$key])) {
425
+ $newarray[$key] = count($newarray);
426
+ }
427
+ }
428
+ return array_flip($newarray);
429
+ }
430
+
431
+
432
+ public static function ksort_recursive(&$theArray) {
433
+ ksort($theArray);
434
+ foreach ($theArray as $key => $value) {
435
+ if (is_array($value)) {
436
+ self::ksort_recursive($theArray[$key]);
437
+ }
438
+ }
439
+ return true;
440
+ }
441
+
442
+ public static function fileextension($filename, $numextensions=1) {
443
+ if (strstr($filename, '.')) {
444
+ $reversedfilename = strrev($filename);
445
+ $offset = 0;
446
+ for ($i = 0; $i < $numextensions; $i++) {
447
+ $offset = strpos($reversedfilename, '.', $offset + 1);
448
+ if ($offset === false) {
449
+ return '';
450
+ }
451
+ }
452
+ return strrev(substr($reversedfilename, 0, $offset));
453
+ }
454
+ return '';
455
+ }
456
+
457
+
458
+ public static function PlaytimeString($seconds) {
459
+ $sign = (($seconds < 0) ? '-' : '');
460
+ $seconds = round(abs($seconds));
461
+ $H = (int) floor( $seconds / 3600);
462
+ $M = (int) floor(($seconds - (3600 * $H) ) / 60);
463
+ $S = (int) round( $seconds - (3600 * $H) - (60 * $M) );
464
+ return $sign.($H ? $H.':' : '').($H ? str_pad($M, 2, '0', STR_PAD_LEFT) : intval($M)).':'.str_pad($S, 2, 0, STR_PAD_LEFT);
465
+ }
466
+
467
+
468
+ public static function DateMac2Unix($macdate) {
469
+ // Macintosh timestamp: seconds since 00:00h January 1, 1904
470
+ // UNIX timestamp: seconds since 00:00h January 1, 1970
471
+ return self::CastAsInt($macdate - 2082844800);
472
+ }
473
+
474
+
475
+ public static function FixedPoint8_8($rawdata) {
476
+ return self::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (self::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8));
477
+ }
478
+
479
+
480
+ public static function FixedPoint16_16($rawdata) {
481
+ return self::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (self::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16));
482
+ }
483
+
484
+
485
+ public static function FixedPoint2_30($rawdata) {
486
+ $binarystring = self::BigEndian2Bin($rawdata);
487
+ return self::Bin2Dec(substr($binarystring, 0, 2)) + (float) (self::Bin2Dec(substr($binarystring, 2, 30)) / pow(2, 30));
488
+ }
489
+
490
+
491
+ public static function CreateDeepArray($ArrayPath, $Separator, $Value) {
492
+ // assigns $Value to a nested array path:
493
+ // $foo = self::CreateDeepArray('/path/to/my', '/', 'file.txt')
494
+ // is the same as:
495
+ // $foo = array('path'=>array('to'=>'array('my'=>array('file.txt'))));
496
+ // or
497
+ // $foo['path']['to']['my'] = 'file.txt';
498
+ $ArrayPath = ltrim($ArrayPath, $Separator);
499
+ if (($pos = strpos($ArrayPath, $Separator)) !== false) {
500
+ $ReturnedArray[substr($ArrayPath, 0, $pos)] = self::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value);
501
+ } else {
502
+ $ReturnedArray[$ArrayPath] = $Value;
503
+ }
504
+ return $ReturnedArray;
505
+ }
506
+
507
+ public static function array_max($arraydata, $returnkey=false) {
508
+ $maxvalue = false;
509
+ $maxkey = false;
510
+ foreach ($arraydata as $key => $value) {
511
+ if (!is_array($value)) {
512
+ if ($value > $maxvalue) {
513
+ $maxvalue = $value;
514
+ $maxkey = $key;
515
+ }
516
+ }
517
+ }
518
+ return ($returnkey ? $maxkey : $maxvalue);
519
+ }
520
+
521
+ public static function array_min($arraydata, $returnkey=false) {
522
+ $minvalue = false;
523
+ $minkey = false;
524
+ foreach ($arraydata as $key => $value) {
525
+ if (!is_array($value)) {
526
+ if ($value > $minvalue) {
527
+ $minvalue = $value;
528
+ $minkey = $key;
529
+ }
530
+ }
531
+ }
532
+ return ($returnkey ? $minkey : $minvalue);
533
+ }
534
+
535
+ public static function XML2array($XMLstring) {
536
+ if (function_exists('simplexml_load_string') && function_exists('libxml_disable_entity_loader')) {
537
+ // http://websec.io/2012/08/27/Preventing-XEE-in-PHP.html
538
+ // https://core.trac.wordpress.org/changeset/29378
539
+ $loader = libxml_disable_entity_loader(true);
540
+ $XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', LIBXML_NOENT);
541
+ $return = self::SimpleXMLelement2array($XMLobject);
542
+ libxml_disable_entity_loader($loader);
543
+ return $return;
544
+ }
545
+ return false;
546
+ }
547
+
548
+ public static function SimpleXMLelement2array($XMLobject) {
549
+ if (!is_object($XMLobject) && !is_array($XMLobject)) {
550
+ return $XMLobject;
551
+ }
552
+ $XMLarray = (is_object($XMLobject) ? get_object_vars($XMLobject) : $XMLobject);
553
+ foreach ($XMLarray as $key => $value) {
554
+ $XMLarray[$key] = self::SimpleXMLelement2array($value);
555
+ }
556
+ return $XMLarray;
557
+ }
558
+
559
+
560
+ // Allan Hansen <ahØartemis*dk>
561
+ // self::md5_data() - returns md5sum for a file from startuing position to absolute end position
562
+ public static function hash_data($file, $offset, $end, $algorithm) {
563
+ static $tempdir = '';
564
+ if (!self::intValueSupported($end)) {
565
+ return false;
566
+ }
567
+ switch ($algorithm) {
568
+ case 'md5':
569
+ $hash_function = 'md5_file';
570
+ $unix_call = 'md5sum';
571
+ $windows_call = 'md5sum.exe';
572
+ $hash_length = 32;
573
+ break;
574
+
575
+ case 'sha1':
576
+ $hash_function = 'sha1_file';
577
+ $unix_call = 'sha1sum';
578
+ $windows_call = 'sha1sum.exe';
579
+ $hash_length = 40;
580
+ break;
581
+
582
+ default:
583
+ throw new Exception('Invalid algorithm ('.$algorithm.') in self::hash_data()');
584
+ break;
585
+ }
586
+ $size = $end - $offset;
587
+ while (true) {
588
+ if (GETID3_OS_ISWINDOWS) {
589
+
590
+ // It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data
591
+ // Fall back to create-temp-file method:
592
+ if ($algorithm == 'sha1') {
593
+ break;
594
+ }
595
+
596
+ $RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call);
597
+ foreach ($RequiredFiles as $required_file) {
598
+ if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
599
+ // helper apps not available - fall back to old method
600
+ break 2;
601
+ }
602
+ }
603
+ $commandline = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' '.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).' | ';
604
+ $commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | ';
605
+ $commandline .= GETID3_HELPERAPPSDIR.$windows_call;
606
+
607
+ } else {
608
+
609
+ $commandline = 'head -c'.$end.' '.escapeshellarg($file).' | ';
610
+ $commandline .= 'tail -c'.$size.' | ';
611
+ $commandline .= $unix_call;
612
+
613
+ }
614
+ if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
615
+ //throw new Exception('PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm');
616
+ break;
617
+ }
618
+ return substr(`$commandline`, 0, $hash_length);
619
+ }
620
+
621
+ if (empty($tempdir)) {
622
+ // yes this is ugly, feel free to suggest a better way
623
+ require_once(dirname(__FILE__).'/getid3.php');
624
+ $getid3_temp = new getID3();
625
+ $tempdir = $getid3_temp->tempdir;
626
+ unset($getid3_temp);
627
+ }
628
+ // try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir
629
+ if (($data_filename = tempnam($tempdir, 'gI3')) === false) {
630
+ // can't find anywhere to create a temp file, just fail
631
+ return false;
632
+ }
633
+
634
+ // Init
635
+ $result = false;
636
+
637
+ // copy parts of file
638
+ try {
639
+ self::CopyFileParts($file, $data_filename, $offset, $end - $offset);
640
+ $result = $hash_function($data_filename);
641
+ } catch (Exception $e) {
642
+ throw new Exception('self::CopyFileParts() failed in getid_lib::hash_data(): '.$e->getMessage());
643
+ }
644
+ unlink($data_filename);
645
+ return $result;
646
+ }
647
+
648
+ public static function CopyFileParts($filename_source, $filename_dest, $offset, $length) {
649
+ if (!self::intValueSupported($offset + $length)) {
650
+ throw new Exception('cannot copy file portion, it extends beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit');
651
+ }
652
+ if (is_readable($filename_source) && is_file($filename_source) && ($fp_src = fopen($filename_source, 'rb'))) {
653
+ if (($fp_dest = fopen($filename_dest, 'wb'))) {
654
+ if (fseek($fp_src, $offset) == 0) {
655
+ $byteslefttowrite = $length;
656
+ while (($byteslefttowrite > 0) && ($buffer = fread($fp_src, min($byteslefttowrite, getID3::FREAD_BUFFER_SIZE)))) {
657
+ $byteswritten = fwrite($fp_dest, $buffer, $byteslefttowrite);
658
+ $byteslefttowrite -= $byteswritten;
659
+ }
660
+ return true;
661
+ } else {
662
+ throw new Exception('failed to seek to offset '.$offset.' in '.$filename_source);
663
+ }
664
+ fclose($fp_dest);
665
+ } else {
666
+ throw new Exception('failed to create file for writing '.$filename_dest);
667
+ }
668
+ fclose($fp_src);
669
+ } else {
670
+ throw new Exception('failed to open file for reading '.$filename_source);
671
+ }
672
+ return false;
673
+ }
674
+
675
+ public static function iconv_fallback_int_utf8($charval) {
676
+ if ($charval < 128) {
677
+ // 0bbbbbbb
678
+ $newcharstring = chr($charval);
679
+ } elseif ($charval < 2048) {
680
+ // 110bbbbb 10bbbbbb
681
+ $newcharstring = chr(($charval >> 6) | 0xC0);
682
+ $newcharstring .= chr(($charval & 0x3F) | 0x80);
683
+ } elseif ($charval < 65536) {
684
+ // 1110bbbb 10bbbbbb 10bbbbbb
685
+ $newcharstring = chr(($charval >> 12) | 0xE0);
686
+ $newcharstring .= chr(($charval >> 6) | 0xC0);
687
+ $newcharstring .= chr(($charval & 0x3F) | 0x80);
688
+ } else {
689
+ // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
690
+ $newcharstring = chr(($charval >> 18) | 0xF0);
691
+ $newcharstring .= chr(($charval >> 12) | 0xC0);
692
+ $newcharstring .= chr(($charval >> 6) | 0xC0);
693
+ $newcharstring .= chr(($charval & 0x3F) | 0x80);
694
+ }
695
+ return $newcharstring;
696
+ }
697
+
698
+ // ISO-8859-1 => UTF-8
699
+ public static function iconv_fallback_iso88591_utf8($string, $bom=false) {
700
+ if (function_exists('utf8_encode')) {
701
+ return utf8_encode($string);
702
+ }
703
+ // utf8_encode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
704
+ $newcharstring = '';
705
+ if ($bom) {
706
+ $newcharstring .= "\xEF\xBB\xBF";
707
+ }
708
+ for ($i = 0; $i < strlen($string); $i++) {
709
+ $charval = ord($string{$i});
710
+ $newcharstring .= self::iconv_fallback_int_utf8($charval);
711
+ }
712
+ return $newcharstring;
713
+ }
714
+
715
+ // ISO-8859-1 => UTF-16BE
716
+ public static function iconv_fallback_iso88591_utf16be($string, $bom=false) {
717
+ $newcharstring = '';
718
+ if ($bom) {
719
+ $newcharstring .= "\xFE\xFF";
720
+ }
721
+ for ($i = 0; $i < strlen($string); $i++) {
722
+ $newcharstring .= "\x00".$string{$i};
723
+ }
724
+ return $newcharstring;
725
+ }
726
+
727
+ // ISO-8859-1 => UTF-16LE
728
+ public static function iconv_fallback_iso88591_utf16le($string, $bom=false) {
729
+ $newcharstring = '';
730
+ if ($bom) {
731
+ $newcharstring .= "\xFF\xFE";
732
+ }
733
+ for ($i = 0; $i < strlen($string); $i++) {
734
+ $newcharstring .= $string{$i}."\x00";
735
+ }
736
+ return $newcharstring;
737
+ }
738
+
739
+ // ISO-8859-1 => UTF-16LE (BOM)
740
+ public static function iconv_fallback_iso88591_utf16($string) {
741
+ return self::iconv_fallback_iso88591_utf16le($string, true);
742
+ }
743
+
744
+ // UTF-8 => ISO-8859-1
745
+ public static function iconv_fallback_utf8_iso88591($string) {
746
+ if (function_exists('utf8_decode')) {
747
+ return utf8_decode($string);
748
+ }
749
+ // utf8_decode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
750
+ $newcharstring = '';
751
+ $offset = 0;
752
+ $stringlength = strlen($string);
753
+ while ($offset < $stringlength) {
754
+ if ((ord($string{$offset}) | 0x07) == 0xF7) {
755
+ // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
756
+ $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
757
+ ((ord($string{($offset + 1)}) & 0x3F) << 12) &
758
+ ((ord($string{($offset + 2)}) & 0x3F) << 6) &
759
+ (ord($string{($offset + 3)}) & 0x3F);
760
+ $offset += 4;
761
+ } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
762
+ // 1110bbbb 10bbbbbb 10bbbbbb
763
+ $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
764
+ ((ord($string{($offset + 1)}) & 0x3F) << 6) &
765
+ (ord($string{($offset + 2)}) & 0x3F);
766
+ $offset += 3;
767
+ } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
768
+ // 110bbbbb 10bbbbbb
769
+ $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
770
+ (ord($string{($offset + 1)}) & 0x3F);
771
+ $offset += 2;
772
+ } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
773
+ // 0bbbbbbb
774
+ $charval = ord($string{$offset});
775
+ $offset += 1;
776
+ } else {
777
+ // error? throw some kind of warning here?
778
+ $charval = false;
779
+ $offset += 1;
780
+ }
781
+ if ($charval !== false) {
782
+ $newcharstring .= (($charval < 256) ? chr($charval) : '?');
783
+ }
784
+ }
785
+ return $newcharstring;
786
+ }
787
+
788
+ // UTF-8 => UTF-16BE
789
+ public static function iconv_fallback_utf8_utf16be($string, $bom=false) {
790
+ $newcharstring = '';
791
+ if ($bom) {
792
+ $newcharstring .= "\xFE\xFF";
793
+ }
794
+ $offset = 0;
795
+ $stringlength = strlen($string);
796
+ while ($offset < $stringlength) {
797
+ if ((ord($string{$offset}) | 0x07) == 0xF7) {
798
+ // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
799
+ $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
800
+ ((ord($string{($offset + 1)}) & 0x3F) << 12) &
801
+ ((ord($string{($offset + 2)}) & 0x3F) << 6) &
802
+ (ord($string{($offset + 3)}) & 0x3F);
803
+ $offset += 4;
804
+ } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
805
+ // 1110bbbb 10bbbbbb 10bbbbbb
806
+ $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
807
+ ((ord($string{($offset + 1)}) & 0x3F) << 6) &
808
+ (ord($string{($offset + 2)}) & 0x3F);
809
+ $offset += 3;
810
+ } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
811
+ // 110bbbbb 10bbbbbb
812
+ $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
813
+ (ord($string{($offset + 1)}) & 0x3F);
814
+ $offset += 2;
815
+ } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
816
+ // 0bbbbbbb
817
+ $charval = ord($string{$offset});
818
+ $offset += 1;
819
+ } else {
820
+ // error? throw some kind of warning here?
821
+ $charval = false;
822
+ $offset += 1;
823
+ }
824
+ if ($charval !== false) {
825
+ $newcharstring .= (($charval < 65536) ? self::BigEndian2String($charval, 2) : "\x00".'?');
826
+ }
827
+ }
828
+ return $newcharstring;
829
+ }
830
+
831
+ // UTF-8 => UTF-16LE
832
+ public static function iconv_fallback_utf8_utf16le($string, $bom=false) {
833
+ $newcharstring = '';
834
+ if ($bom) {
835
+ $newcharstring .= "\xFF\xFE";
836
+ }
837
+ $offset = 0;
838
+ $stringlength = strlen($string);
839
+ while ($offset < $stringlength) {
840
+ if ((ord($string{$offset}) | 0x07) == 0xF7) {
841
+ // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
842
+ $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
843
+ ((ord($string{($offset + 1)}) & 0x3F) << 12) &
844
+ ((ord($string{($offset + 2)}) & 0x3F) << 6) &
845
+ (ord($string{($offset + 3)}) & 0x3F);
846
+ $offset += 4;
847
+ } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
848
+ // 1110bbbb 10bbbbbb 10bbbbbb
849
+ $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
850
+ ((ord($string{($offset + 1)}) & 0x3F) << 6) &
851
+ (ord($string{($offset + 2)}) & 0x3F);
852
+ $offset += 3;
853
+ } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
854
+ // 110bbbbb 10bbbbbb
855
+ $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
856
+ (ord($string{($offset + 1)}) & 0x3F);
857
+ $offset += 2;
858
+ } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
859
+ // 0bbbbbbb
860
+ $charval = ord($string{$offset});
861
+ $offset += 1;
862
+ } else {
863
+ // error? maybe throw some warning here?
864
+ $charval = false;
865
+ $offset += 1;
866
+ }
867
+ if ($charval !== false) {
868
+ $newcharstring .= (($charval < 65536) ? self::LittleEndian2String($charval, 2) : '?'."\x00");
869
+ }
870
+ }
871
+ return $newcharstring;
872
+ }
873
+
874
+ // UTF-8 => UTF-16LE (BOM)
875
+ public static function iconv_fallback_utf8_utf16($string) {
876
+ return self::iconv_fallback_utf8_utf16le($string, true);
877
+ }
878
+
879
+ // UTF-16BE => UTF-8
880
+ public static function iconv_fallback_utf16be_utf8($string) {
881
+ if (substr($string, 0, 2) == "\xFE\xFF") {
882
+ // strip BOM
883
+ $string = substr($string, 2);
884
+ }
885
+ $newcharstring = '';
886
+ for ($i = 0; $i < strlen($string); $i += 2) {
887
+ $charval = self::BigEndian2Int(substr($string, $i, 2));
888
+ $newcharstring .= self::iconv_fallback_int_utf8($charval);
889
+ }
890
+ return $newcharstring;
891
+ }
892
+
893
+ // UTF-16LE => UTF-8
894
+ public static function iconv_fallback_utf16le_utf8($string) {
895
+ if (substr($string, 0, 2) == "\xFF\xFE") {
896
+ // strip BOM
897
+ $string = substr($string, 2);
898
+ }
899
+ $newcharstring = '';
900
+ for ($i = 0; $i < strlen($string); $i += 2) {
901
+ $charval = self::LittleEndian2Int(substr($string, $i, 2));
902
+ $newcharstring .= self::iconv_fallback_int_utf8($charval);
903
+ }
904
+ return $newcharstring;
905
+ }
906
+
907
+ // UTF-16BE => ISO-8859-1
908
+ public static function iconv_fallback_utf16be_iso88591($string) {
909
+ if (substr($string, 0, 2) == "\xFE\xFF") {
910
+ // strip BOM
911
+ $string = substr($string, 2);
912
+ }
913
+ $newcharstring = '';
914
+ for ($i = 0; $i < strlen($string); $i += 2) {
915
+ $charval = self::BigEndian2Int(substr($string, $i, 2));
916
+ $newcharstring .= (($charval < 256) ? chr($charval) : '?');
917
+ }
918
+ return $newcharstring;
919
+ }
920
+
921
+ // UTF-16LE => ISO-8859-1
922
+ public static function iconv_fallback_utf16le_iso88591($string) {
923
+ if (substr($string, 0, 2) == "\xFF\xFE") {
924
+ // strip BOM
925
+ $string = substr($string, 2);
926
+ }
927
+ $newcharstring = '';
928
+ for ($i = 0; $i < strlen($string); $i += 2) {
929
+ $charval = self::LittleEndian2Int(substr($string, $i, 2));
930
+ $newcharstring .= (($charval < 256) ? chr($charval) : '?');
931
+ }
932
+ return $newcharstring;
933
+ }
934
+
935
+ // UTF-16 (BOM) => ISO-8859-1
936
+ public static function iconv_fallback_utf16_iso88591($string) {
937
+ $bom = substr($string, 0, 2);
938
+ if ($bom == "\xFE\xFF") {
939
+ return self::iconv_fallback_utf16be_iso88591(substr($string, 2));
940
+ } elseif ($bom == "\xFF\xFE") {
941
+ return self::iconv_fallback_utf16le_iso88591(substr($string, 2));
942
+ }
943
+ return $string;
944
+ }
945
+
946
+ // UTF-16 (BOM) => UTF-8
947
+ public static function iconv_fallback_utf16_utf8($string) {
948
+ $bom = substr($string, 0, 2);
949
+ if ($bom == "\xFE\xFF") {
950
+ return self::iconv_fallback_utf16be_utf8(substr($string, 2));
951
+ } elseif ($bom == "\xFF\xFE") {
952
+ return self::iconv_fallback_utf16le_utf8(substr($string, 2));
953
+ }
954
+ return $string;
955
+ }
956
+
957
+ public static function iconv_fallback($in_charset, $out_charset, $string) {
958
+
959
+ if ($in_charset == $out_charset) {
960
+ return $string;
961
+ }
962
+
963
+ // iconv() availble
964
+ if (function_exists('iconv')) {
965
+ if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) {
966
+ switch ($out_charset) {
967
+ case 'ISO-8859-1':
968
+ $converted_string = rtrim($converted_string, "\x00");
969
+ break;
970
+ }
971
+ return $converted_string;
972
+ }
973
+
974
+ // iconv() may sometimes fail with "illegal character in input string" error message
975
+ // and return an empty string, but returning the unconverted string is more useful
976
+ return $string;
977
+ }
978
+
979
+
980
+ // iconv() not available
981
+ static $ConversionFunctionList = array();
982
+ if (empty($ConversionFunctionList)) {
983
+ $ConversionFunctionList['ISO-8859-1']['UTF-8'] = 'iconv_fallback_iso88591_utf8';
984
+ $ConversionFunctionList['ISO-8859-1']['UTF-16'] = 'iconv_fallback_iso88591_utf16';
985
+ $ConversionFunctionList['ISO-8859-1']['UTF-16BE'] = 'iconv_fallback_iso88591_utf16be';
986
+ $ConversionFunctionList['ISO-8859-1']['UTF-16LE'] = 'iconv_fallback_iso88591_utf16le';
987
+ $ConversionFunctionList['UTF-8']['ISO-8859-1'] = 'iconv_fallback_utf8_iso88591';
988
+ $ConversionFunctionList['UTF-8']['UTF-16'] = 'iconv_fallback_utf8_utf16';
989
+ $ConversionFunctionList['UTF-8']['UTF-16BE'] = 'iconv_fallback_utf8_utf16be';
990
+ $ConversionFunctionList['UTF-8']['UTF-16LE'] = 'iconv_fallback_utf8_utf16le';
991
+ $ConversionFunctionList['UTF-16']['ISO-8859-1'] = 'iconv_fallback_utf16_iso88591';
992
+ $ConversionFunctionList['UTF-16']['UTF-8'] = 'iconv_fallback_utf16_utf8';
993
+ $ConversionFunctionList['UTF-16LE']['ISO-8859-1'] = 'iconv_fallback_utf16le_iso88591';
994
+ $ConversionFunctionList['UTF-16LE']['UTF-8'] = 'iconv_fallback_utf16le_utf8';
995
+ $ConversionFunctionList['UTF-16BE']['ISO-8859-1'] = 'iconv_fallback_utf16be_iso88591';
996
+ $ConversionFunctionList['UTF-16BE']['UTF-8'] = 'iconv_fallback_utf16be_utf8';
997
+ }
998
+ if (isset($ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)])) {
999
+ $ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)];
1000
+ return self::$ConversionFunction($string);
1001
+ }
1002
+ throw new Exception('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset);
1003
+ }
1004
+
1005
+ public static function recursiveMultiByteCharString2HTML($data, $charset='ISO-8859-1') {
1006
+ if (is_string($data)) {
1007
+ return self::MultiByteCharString2HTML($data, $charset);
1008
+ } elseif (is_array($data)) {
1009
+ $return_data = array();
1010
+ foreach ($data as $key => $value) {
1011
+ $return_data[$key] = self::recursiveMultiByteCharString2HTML($value, $charset);
1012
+ }
1013
+ return $return_data;
1014
+ }
1015
+ // integer, float, objects, resources, etc
1016
+ return $data;
1017
+ }
1018
+
1019
+ public static function MultiByteCharString2HTML($string, $charset='ISO-8859-1') {
1020
+ $string = (string) $string; // in case trying to pass a numeric (float, int) string, would otherwise return an empty string
1021
+ $HTMLstring = '';
1022
+
1023
+ switch ($charset) {
1024
+ case '1251':
1025
+ case '1252':
1026
+ case '866':
1027
+ case '932':
1028
+ case '936':
1029
+ case '950':
1030
+ case 'BIG5':
1031
+ case 'BIG5-HKSCS':
1032
+ case 'cp1251':
1033
+ case 'cp1252':
1034
+ case 'cp866':
1035
+ case 'EUC-JP':
1036
+ case 'EUCJP':
1037
+ case 'GB2312':
1038
+ case 'ibm866':
1039
+ case 'ISO-8859-1':
1040
+ case 'ISO-8859-15':
1041
+ case 'ISO8859-1':
1042
+ case 'ISO8859-15':
1043
+ case 'KOI8-R':
1044
+ case 'koi8-ru':
1045
+ case 'koi8r':
1046
+ case 'Shift_JIS':
1047
+ case 'SJIS':
1048
+ case 'win-1251':
1049
+ case 'Windows-1251':
1050
+ case 'Windows-1252':
1051
+ $HTMLstring = htmlentities($string, ENT_COMPAT, $charset);
1052
+ break;
1053
+
1054
+ case 'UTF-8':
1055
+ $strlen = strlen($string);
1056
+ for ($i = 0; $i < $strlen; $i++) {
1057
+ $char_ord_val = ord($string{$i});
1058
+ $charval = 0;
1059
+ if ($char_ord_val < 0x80) {
1060
+ $charval = $char_ord_val;
1061
+ } elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F && $i+3 < $strlen) {
1062
+ $charval = (($char_ord_val & 0x07) << 18);
1063
+ $charval += ((ord($string{++$i}) & 0x3F) << 12);
1064
+ $charval += ((ord($string{++$i}) & 0x3F) << 6);
1065
+ $charval += (ord($string{++$i}) & 0x3F);
1066
+ } elseif ((($char_ord_val & 0xE0) >> 5) == 0x07 && $i+2 < $strlen) {
1067
+ $charval = (($char_ord_val & 0x0F) << 12);
1068
+ $charval += ((ord($string{++$i}) & 0x3F) << 6);
1069
+ $charval += (ord($string{++$i}) & 0x3F);
1070
+ } elseif ((($char_ord_val & 0xC0) >> 6) == 0x03 && $i+1 < $strlen) {
1071
+ $charval = (($char_ord_val & 0x1F) << 6);
1072
+ $charval += (ord($string{++$i}) & 0x3F);
1073
+ }
1074
+ if (($charval >= 32) && ($charval <= 127)) {
1075
+ $HTMLstring .= htmlentities(chr($charval));
1076
+ } else {
1077
+ $HTMLstring .= '&#'.$charval.';';
1078
+ }
1079
+ }
1080
+ break;
1081
+
1082
+ case 'UTF-16LE':
1083
+ for ($i = 0; $i < strlen($string); $i += 2) {
1084
+ $charval = self::LittleEndian2Int(substr($string, $i, 2));
1085
+ if (($charval >= 32) && ($charval <= 127)) {
1086
+ $HTMLstring .= chr($charval);
1087
+ } else {
1088
+ $HTMLstring .= '&#'.$charval.';';
1089
+ }
1090
+ }
1091
+ break;
1092
+
1093
+ case 'UTF-16BE':
1094
+ for ($i = 0; $i < strlen($string); $i += 2) {
1095
+ $charval = self::BigEndian2Int(substr($string, $i, 2));
1096
+ if (($charval >= 32) && ($charval <= 127)) {
1097
+ $HTMLstring .= chr($charval);
1098
+ } else {
1099
+ $HTMLstring .= '&#'.$charval.';';
1100
+ }
1101
+ }
1102
+ break;
1103
+
1104
+ default:
1105
+ $HTMLstring = 'ERROR: Character set "'.$charset.'" not supported in MultiByteCharString2HTML()';
1106
+ break;
1107
+ }
1108
+ return $HTMLstring;
1109
+ }
1110
+
1111
+
1112
+
1113
+ public static function RGADnameLookup($namecode) {
1114
+ static $RGADname = array();
1115
+ if (empty($RGADname)) {
1116
+ $RGADname[0] = 'not set';
1117
+ $RGADname[1] = 'Track Gain Adjustment';
1118
+ $RGADname[2] = 'Album Gain Adjustment';
1119
+ }
1120
+
1121
+ return (isset($RGADname[$namecode]) ? $RGADname[$namecode] : '');
1122
+ }
1123
+
1124
+
1125
+ public static function RGADoriginatorLookup($originatorcode) {
1126
+ static $RGADoriginator = array();
1127
+ if (empty($RGADoriginator)) {
1128
+ $RGADoriginator[0] = 'unspecified';
1129
+ $RGADoriginator[1] = 'pre-set by artist/producer/mastering engineer';
1130
+ $RGADoriginator[2] = 'set by user';
1131
+ $RGADoriginator[3] = 'determined automatically';
1132
+ }
1133
+
1134
+ return (isset($RGADoriginator[$originatorcode]) ? $RGADoriginator[$originatorcode] : '');
1135
+ }
1136
+
1137
+
1138
+ public static function RGADadjustmentLookup($rawadjustment, $signbit) {
1139
+ $adjustment = $rawadjustment / 10;
1140
+ if ($signbit == 1) {
1141
+ $adjustment *= -1;
1142
+ }
1143
+ return (float) $adjustment;
1144
+ }
1145
+
1146
+
1147
+ public static function RGADgainString($namecode, $originatorcode, $replaygain) {
1148
+ if ($replaygain < 0) {
1149
+ $signbit = '1';
1150
+ } else {
1151
+ $signbit = '0';
1152
+ }
1153
+ $storedreplaygain = intval(round($replaygain * 10));
1154
+ $gainstring = str_pad(decbin($namecode), 3, '0', STR_PAD_LEFT);
1155
+ $gainstring .= str_pad(decbin($originatorcode), 3, '0', STR_PAD_LEFT);
1156
+ $gainstring .= $signbit;
1157
+ $gainstring .= str_pad(decbin($storedreplaygain), 9, '0', STR_PAD_LEFT);
1158
+
1159
+ return $gainstring;
1160
+ }
1161
+
1162
+ public static function RGADamplitude2dB($amplitude) {
1163
+ return 20 * log10($amplitude);
1164
+ }
1165
+
1166
+
1167
+ public static function GetDataImageSize($imgData, &$imageinfo=array()) {
1168
+ static $tempdir = '';
1169
+ if (empty($tempdir)) {
1170
+ if (function_exists('sys_get_temp_dir')) {
1171
+ $tempdir = sys_get_temp_dir(); // https://github.com/JamesHeinrich/getID3/issues/52
1172
+ }
1173
+
1174
+ // yes this is ugly, feel free to suggest a better way
1175
+ if (include_once(dirname(__FILE__).'/getid3.php')) {
1176
+ if ($getid3_temp = new getID3()) {
1177
+ if ($getid3_temp_tempdir = $getid3_temp->tempdir) {
1178
+ $tempdir = $getid3_temp_tempdir;
1179
+ }
1180
+ unset($getid3_temp, $getid3_temp_tempdir);
1181
+ }
1182
+ }
1183
+ }
1184
+ $GetDataImageSize = false;
1185
+ if ($tempfilename = tempnam($tempdir, 'gI3')) {
1186
+ if (is_writable($tempfilename) && is_file($tempfilename) && ($tmp = fopen($tempfilename, 'wb'))) {
1187
+ fwrite($tmp, $imgData);
1188
+ fclose($tmp);
1189
+ $GetDataImageSize = @getimagesize($tempfilename, $imageinfo);
1190
+ if (($GetDataImageSize === false) || !isset($GetDataImageSize[0]) || !isset($GetDataImageSize[1])) {
1191
+ return false;
1192
+ }
1193
+ $GetDataImageSize['height'] = $GetDataImageSize[0];
1194
+ $GetDataImageSize['width'] = $GetDataImageSize[1];
1195
+ }
1196
+ unlink($tempfilename);
1197
+ }
1198
+ return $GetDataImageSize;
1199
+ }
1200
+
1201
+ public static function ImageExtFromMime($mime_type) {
1202
+ // temporary way, works OK for now, but should be reworked in the future
1203
+ return str_replace(array('image/', 'x-', 'jpeg'), array('', '', 'jpg'), $mime_type);
1204
+ }
1205
+
1206
+ public static function ImageTypesLookup($imagetypeid) {
1207
+ static $ImageTypesLookup = array();
1208
+ if (empty($ImageTypesLookup)) {
1209
+ $ImageTypesLookup[1] = 'gif';
1210
+ $ImageTypesLookup[2] = 'jpeg';
1211
+ $ImageTypesLookup[3] = 'png';
1212
+ $ImageTypesLookup[4] = 'swf';
1213
+ $ImageTypesLookup[5] = 'psd';
1214
+ $ImageTypesLookup[6] = 'bmp';
1215
+ $ImageTypesLookup[7] = 'tiff (little-endian)';
1216
+ $ImageTypesLookup[8] = 'tiff (big-endian)';
1217
+ $ImageTypesLookup[9] = 'jpc';
1218
+ $ImageTypesLookup[10] = 'jp2';
1219
+ $ImageTypesLookup[11] = 'jpx';
1220
+ $ImageTypesLookup[12] = 'jb2';
1221
+ $ImageTypesLookup[13] = 'swc';
1222
+ $ImageTypesLookup[14] = 'iff';
1223
+ }
1224
+ return (isset($ImageTypesLookup[$imagetypeid]) ? $ImageTypesLookup[$imagetypeid] : '');
1225
+ }
1226
+
1227
+ public static function CopyTagsToComments(&$ThisFileInfo) {
1228
+
1229
+ // Copy all entries from ['tags'] into common ['comments']
1230
+ if (!empty($ThisFileInfo['tags'])) {
1231
+ foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) {
1232
+ foreach ($tagarray as $tagname => $tagdata) {
1233
+ foreach ($tagdata as $key => $value) {
1234
+ if (!empty($value)) {
1235
+ if (empty($ThisFileInfo['comments'][$tagname])) {
1236
+
1237
+ // fall through and append value
1238
+
1239
+ } elseif ($tagtype == 'id3v1') {
1240
+
1241
+ $newvaluelength = strlen(trim($value));
1242
+ foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
1243
+ $oldvaluelength = strlen(trim($existingvalue));
1244
+ if (($newvaluelength <= $oldvaluelength) && (substr($existingvalue, 0, $newvaluelength) == trim($value))) {
1245
+ // new value is identical but shorter-than (or equal-length to) one already in comments - skip
1246
+ break 2;
1247
+ }
1248
+ }
1249
+
1250
+ } elseif (!is_array($value)) {
1251
+
1252
+ $newvaluelength = strlen(trim($value));
1253
+ foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
1254
+ $oldvaluelength = strlen(trim($existingvalue));
1255
+ if ((strlen($existingvalue) > 10) && ($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) {
1256
+ $ThisFileInfo['comments'][$tagname][$existingkey] = trim($value);
1257
+ //break 2;
1258
+ break;
1259
+ }
1260
+ }
1261
+
1262
+ }
1263
+ if (is_array($value) || empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) {
1264
+ $value = (is_string($value) ? trim($value) : $value);
1265
+ if (!is_numeric($key)) {
1266
+ $ThisFileInfo['comments'][$tagname][$key] = $value;
1267
+ } else {
1268
+ $ThisFileInfo['comments'][$tagname][] = $value;
1269
+ }
1270
+ }
1271
+ }
1272
+ }
1273
+ }
1274
+ }
1275
+
1276
+ // Copy to ['comments_html']
1277
+ if (!empty($ThisFileInfo['comments'])) {
1278
+ foreach ($ThisFileInfo['comments'] as $field => $values) {
1279
+ if ($field == 'picture') {
1280
+ // pictures can take up a lot of space, and we don't need multiple copies of them
1281
+ // let there be a single copy in [comments][picture], and not elsewhere
1282
+ continue;
1283
+ }
1284
+ foreach ($values as $index => $value) {
1285
+ if (is_array($value)) {
1286
+ $ThisFileInfo['comments_html'][$field][$index] = $value;
1287
+ } else {
1288
+ $ThisFileInfo['comments_html'][$field][$index] = str_replace('&#0;', '', self::MultiByteCharString2HTML($value, $ThisFileInfo['encoding']));
1289
+ }
1290
+ }
1291
+ }
1292
+ }
1293
+
1294
+ }
1295
+ return true;
1296
+ }
1297
+
1298
+
1299
+ public static function EmbeddedLookup($key, $begin, $end, $file, $name) {
1300
+
1301
+ // Cached
1302
+ static $cache;
1303
+ if (isset($cache[$file][$name])) {
1304
+ return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : '');
1305
+ }
1306
+
1307
+ // Init
1308
+ $keylength = strlen($key);
1309
+ $line_count = $end - $begin - 7;
1310
+
1311
+ // Open php file
1312
+ $fp = fopen($file, 'r');
1313
+
1314
+ // Discard $begin lines
1315
+ for ($i = 0; $i < ($begin + 3); $i++) {
1316
+ fgets($fp, 1024);
1317
+ }
1318
+
1319
+ // Loop thru line
1320
+ while (0 < $line_count--) {
1321
+
1322
+ // Read line
1323
+ $line = ltrim(fgets($fp, 1024), "\t ");
1324
+
1325
+ // METHOD A: only cache the matching key - less memory but slower on next lookup of not-previously-looked-up key
1326
+ //$keycheck = substr($line, 0, $keylength);
1327
+ //if ($key == $keycheck) {
1328
+ // $cache[$file][$name][$keycheck] = substr($line, $keylength + 1);
1329
+ // break;
1330
+ //}
1331
+
1332
+ // METHOD B: cache all keys in this lookup - more memory but faster on next lookup of not-previously-looked-up key
1333
+ //$cache[$file][$name][substr($line, 0, $keylength)] = trim(substr($line, $keylength + 1));
1334
+ $explodedLine = explode("\t", $line, 2);
1335
+ $ThisKey = (isset($explodedLine[0]) ? $explodedLine[0] : '');
1336
+ $ThisValue = (isset($explodedLine[1]) ? $explodedLine[1] : '');
1337
+ $cache[$file][$name][$ThisKey] = trim($ThisValue);
1338
+ }
1339
+
1340
+ // Close and return
1341
+ fclose($fp);
1342
+ return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : '');
1343
+ }
1344
+
1345
+ public static function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) {
1346
+ global $GETID3_ERRORARRAY;
1347
+
1348
+ if (file_exists($filename)) {
1349
+ if (include_once($filename)) {
1350
+ return true;
1351
+ } else {
1352
+ $diemessage = basename($sourcefile).' depends on '.$filename.', which has errors';
1353
+ }
1354
+ } else {
1355
+ $diemessage = basename($sourcefile).' depends on '.$filename.', which is missing';
1356
+ }
1357
+ if ($DieOnFailure) {
1358
+ throw new Exception($diemessage);
1359
+ } else {
1360
+ $GETID3_ERRORARRAY[] = $diemessage;
1361
+ }
1362
+ return false;
1363
+ }
1364
+
1365
+ public static function trimNullByte($string) {
1366
+ return trim($string, "\x00");
1367
+ }
1368
+
1369
+ public static function getFileSizeSyscall($path) {
1370
+ $filesize = false;
1371
+
1372
+ if (GETID3_OS_ISWINDOWS) {
1373
+ if (class_exists('COM')) { // From PHP 5.3.15 and 5.4.5, COM and DOTNET is no longer built into the php core.you have to add COM support in php.ini:
1374
+ $filesystem = new COM('Scripting.FileSystemObject');
1375
+ $file = $filesystem->GetFile($path);
1376
+ $filesize = $file->Size();
1377
+ unset($filesystem, $file);
1378
+ } else {
1379
+ $commandline = 'for %I in ('.escapeshellarg($path).') do @echo %~zI';
1380
+ }
1381
+ } else {
1382
+ $commandline = 'ls -l '.escapeshellarg($path).' | awk \'{print $5}\'';
1383
+ }
1384
+ if (isset($commandline)) {
1385
+ $output = trim(`$commandline`);
1386
+ if (ctype_digit($output)) {
1387
+ $filesize = (float) $output;
1388
+ }
1389
+ }
1390
+ return $filesize;
1391
+ }
1392
+
1393
+
1394
+ /**
1395
+ * Workaround for Bug #37268 (https://bugs.php.net/bug.php?id=37268)
1396
+ * @param string $path A path.
1397
+ * @param string $suffix If the name component ends in suffix this will also be cut off.
1398
+ * @return string
1399
+ */
1400
+ public static function mb_basename($path, $suffix = null) {
1401
+ $splited = preg_split('#/#', rtrim($path, '/ '));
1402
+ return substr(basename('X'.$splited[count($splited) - 1], $suffix), 1);
1403
+ }
1404
+
1405
+ }
getid3/getid3.php CHANGED
@@ -1,1812 +1,1834 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- // also https://github.com/JamesHeinrich/getID3 //
7
- /////////////////////////////////////////////////////////////////
8
- // //
9
- // Please see readme.txt for more information //
10
- // ///
11
- /////////////////////////////////////////////////////////////////
12
-
13
- // define a constant rather than looking up every time it is needed
14
- if (!defined('GETID3_OS_ISWINDOWS')) {
15
- define('GETID3_OS_ISWINDOWS', (stripos(PHP_OS, 'WIN') === 0));
16
- }
17
- // Get base path of getID3() - ONCE
18
- if (!defined('GETID3_INCLUDEPATH')) {
19
- define('GETID3_INCLUDEPATH', dirname(__FILE__).DIRECTORY_SEPARATOR);
20
- }
21
- // Workaround Bug #39923 (https://bugs.php.net/bug.php?id=39923)
22
- if (!defined('IMG_JPG') && defined('IMAGETYPE_JPEG')) {
23
- define('IMG_JPG', IMAGETYPE_JPEG);
24
- }
25
-
26
- // attempt to define temp dir as something flexible but reliable
27
- $temp_dir = ini_get('upload_tmp_dir');
28
- if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) {
29
- $temp_dir = '';
30
- }
31
- if (!$temp_dir) {
32
- // sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts
33
- $temp_dir = sys_get_temp_dir();
34
- }
35
- $temp_dir = @realpath($temp_dir); // see https://github.com/JamesHeinrich/getID3/pull/10
36
- $open_basedir = ini_get('open_basedir');
37
- if ($open_basedir) {
38
- // e.g. "/var/www/vhosts/getid3.org/httpdocs/:/tmp/"
39
- $temp_dir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $temp_dir);
40
- $open_basedir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $open_basedir);
41
- if (substr($temp_dir, -1, 1) != DIRECTORY_SEPARATOR) {
42
- $temp_dir .= DIRECTORY_SEPARATOR;
43
- }
44
- $found_valid_tempdir = false;
45
- $open_basedirs = explode(PATH_SEPARATOR, $open_basedir);
46
- foreach ($open_basedirs as $basedir) {
47
- if (substr($basedir, -1, 1) != DIRECTORY_SEPARATOR) {
48
- $basedir .= DIRECTORY_SEPARATOR;
49
- }
50
- if (preg_match('#^'.preg_quote($basedir).'#', $temp_dir)) {
51
- $found_valid_tempdir = true;
52
- break;
53
- }
54
- }
55
- if (!$found_valid_tempdir) {
56
- $temp_dir = '';
57
- }
58
- unset($open_basedirs, $found_valid_tempdir, $basedir);
59
- }
60
- if (!$temp_dir) {
61
- $temp_dir = '*'; // invalid directory name should force tempnam() to use system default temp dir
62
- }
63
- // $temp_dir = '/something/else/'; // feel free to override temp dir here if it works better for your system
64
- if (!defined('GETID3_TEMP_DIR')) {
65
- define('GETID3_TEMP_DIR', $temp_dir);
66
- }
67
- unset($open_basedir, $temp_dir);
68
-
69
- // End: Defines
70
-
71
-
72
- class getID3
73
- {
74
- // public: Settings
75
- public $encoding = 'UTF-8'; // CASE SENSITIVE! - i.e. (must be supported by iconv()). Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE
76
- public $encoding_id3v1 = 'ISO-8859-1'; // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' or 'CP1252'
77
-
78
- // public: Optional tag checks - disable for speed.
79
- public $option_tag_id3v1 = true; // Read and process ID3v1 tags
80
- public $option_tag_id3v2 = true; // Read and process ID3v2 tags
81
- public $option_tag_lyrics3 = true; // Read and process Lyrics3 tags
82
- public $option_tag_apetag = true; // Read and process APE tags
83
- public $option_tags_process = true; // Copy tags to root key 'tags' and encode to $this->encoding
84
- public $option_tags_html = true; // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
85
-
86
- // public: Optional tag/comment calucations
87
- public $option_extra_info = true; // Calculate additional info such as bitrate, channelmode etc
88
-
89
- // public: Optional handling of embedded attachments (e.g. images)
90
- public $option_save_attachments = true; // defaults to true (ATTACHMENTS_INLINE) for backward compatibility
91
-
92
- // public: Optional calculations
93
- public $option_md5_data = false; // Get MD5 sum of data part - slow
94
- public $option_md5_data_source = false; // Use MD5 of source file if availble - only FLAC and OptimFROG
95
- public $option_sha1_data = false; // Get SHA1 sum of data part - slow
96
- public $option_max_2gb_check = null; // Check whether file is larger than 2GB and thus not supported by 32-bit PHP (null: auto-detect based on PHP_INT_MAX)
97
-
98
- // public: Read buffer size in bytes
99
- public $option_fread_buffer_size = 32768;
100
-
101
- // Public variables
102
- public $filename; // Filename of file being analysed.
103
- public $fp; // Filepointer to file being analysed.
104
- public $info; // Result array.
105
- public $tempdir = GETID3_TEMP_DIR;
106
- public $memory_limit = 0;
107
-
108
- // Protected variables
109
- protected $startup_error = '';
110
- protected $startup_warning = '';
111
-
112
- const VERSION = '1.9.8-20140511';
113
- const FREAD_BUFFER_SIZE = 32768;
114
-
115
- const ATTACHMENTS_NONE = false;
116
- const ATTACHMENTS_INLINE = true;
117
-
118
- // public: constructor
119
- public function __construct() {
120
-
121
- // Check for PHP version
122
- /*
123
- // not necessary if logic added to getid3.lib.php using the 5.2.11 libxml_disable_entity_loader() function
124
- $required_php_version = '5.3.0';
125
- if (version_compare(PHP_VERSION, $required_php_version, '<')) {
126
- $this->startup_error .= 'getID3() requires PHP v'.$required_php_version.' or higher - you are running v'.PHP_VERSION;
127
- return false;
128
- }
129
- */
130
-
131
- // Check memory
132
- $this->memory_limit = ini_get('memory_limit');
133
- if (preg_match('#([0-9]+)M#i', $this->memory_limit, $matches)) {
134
- // could be stored as "16M" rather than 16777216 for example
135
- $this->memory_limit = $matches[1] * 1048576;
136
- } elseif (preg_match('#([0-9]+)G#i', $this->memory_limit, $matches)) { // The 'G' modifier is available since PHP 5.1.0
137
- // could be stored as "2G" rather than 2147483648 for example
138
- $this->memory_limit = $matches[1] * 1073741824;
139
- }
140
- if ($this->memory_limit <= 0) {
141
- // memory limits probably disabled
142
- } elseif ($this->memory_limit <= 4194304) {
143
- $this->startup_error .= 'PHP has less than 4MB available memory and will very likely run out. Increase memory_limit in php.ini';
144
- } elseif ($this->memory_limit <= 12582912) {
145
- $this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini';
146
- }
147
-
148
- // Check safe_mode off
149
- if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
150
- $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.');
151
- }
152
-
153
- if (intval(ini_get('mbstring.func_overload')) > 0) {
154
- $this->warning('WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", this may break things.');
155
- }
156
-
157
- // Check for magic_quotes_runtime
158
- if (function_exists('get_magic_quotes_runtime')) {
159
- if (get_magic_quotes_runtime()) {
160
- return $this->startup_error('magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).');
161
- }
162
- }
163
-
164
- // Check for magic_quotes_gpc
165
- if (function_exists('magic_quotes_gpc')) {
166
- if (get_magic_quotes_gpc()) {
167
- return $this->startup_error('magic_quotes_gpc must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_gpc(0) and set_magic_quotes_gpc(1).');
168
- }
169
- }
170
-
171
- // Load support library
172
- if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
173
- $this->startup_error .= 'getid3.lib.php is missing or corrupt';
174
- }
175
-
176
- if ($this->option_max_2gb_check === null) {
177
- $this->option_max_2gb_check = (PHP_INT_MAX <= 2147483647);
178
- }
179
-
180
-
181
- // Needed for Windows only:
182
- // Define locations of helper applications for Shorten, VorbisComment, MetaFLAC
183
- // as well as other helper functions such as head, tail, md5sum, etc
184
- // This path cannot contain spaces, but the below code will attempt to get the
185
- // 8.3-equivalent path automatically
186
- // IMPORTANT: This path must include the trailing slash
187
- if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) {
188
-
189
- $helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path
190
-
191
- if (!is_dir($helperappsdir)) {
192
- $this->startup_warning .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist';
193
- } elseif (strpos(realpath($helperappsdir), ' ') !== false) {
194
- $DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir));
195
- $path_so_far = array();
196
- foreach ($DirPieces as $key => $value) {
197
- if (strpos($value, ' ') !== false) {
198
- if (!empty($path_so_far)) {
199
- $commandline = 'dir /x '.escapeshellarg(implode(DIRECTORY_SEPARATOR, $path_so_far));
200
- $dir_listing = `$commandline`;
201
- $lines = explode("\n", $dir_listing);
202
- foreach ($lines as $line) {
203
- $line = trim($line);
204
- if (preg_match('#^([0-9/]{10}) +([0-9:]{4,5}( [AP]M)?) +(<DIR>|[0-9,]+) +([^ ]{0,11}) +(.+)$#', $line, $matches)) {
205
- list($dummy, $date, $time, $ampm, $filesize, $shortname, $filename) = $matches;
206
- if ((strtoupper($filesize) == '<DIR>') && (strtolower($filename) == strtolower($value))) {
207
- $value = $shortname;
208
- }
209
- }
210
- }
211
- } else {
212
- $this->startup_warning .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary. You can run "dir /x" from the commandline to see the correct 8.3-style names.';
213
- }
214
- }
215
- $path_so_far[] = $value;
216
- }
217
- $helperappsdir = implode(DIRECTORY_SEPARATOR, $path_so_far);
218
- }
219
- define('GETID3_HELPERAPPSDIR', $helperappsdir.DIRECTORY_SEPARATOR);
220
- }
221
-
222
- return true;
223
- }
224
-
225
- public function version() {
226
- return self::VERSION;
227
- }
228
-
229
- public function fread_buffer_size() {
230
- return $this->option_fread_buffer_size;
231
- }
232
-
233
-
234
- // public: setOption
235
- public function setOption($optArray) {
236
- if (!is_array($optArray) || empty($optArray)) {
237
- return false;
238
- }
239
- foreach ($optArray as $opt => $val) {
240
- if (isset($this->$opt) === false) {
241
- continue;
242
- }
243
- $this->$opt = $val;
244
- }
245
- return true;
246
- }
247
-
248
-
249
- public function openfile($filename, $filesize = false) { // , $filesize=false added by POWERPRESS
250
- try {
251
- if (!empty($this->startup_error)) {
252
- throw new getid3_exception($this->startup_error);
253
- }
254
- if (!empty($this->startup_warning)) {
255
- $this->warning($this->startup_warning);
256
- }
257
-
258
- // init result array and set parameters
259
- $this->filename = $filename;
260
- $this->info = array();
261
- $this->info['GETID3_VERSION'] = $this->version();
262
- $this->info['php_memory_limit'] = $this->memory_limit;
263
-
264
- // remote files not supported
265
- if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
266
- throw new getid3_exception('Remote files are not supported - please copy the file locally first');
267
- }
268
-
269
- $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
270
- $filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename);
271
-
272
- // open local file
273
- //if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { // see http://www.getid3.org/phpBB3/viewtopic.php?t=1720
274
- if ((is_readable($filename) || file_exists($filename)) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
275
- // great
276
- } else {
277
- $errormessagelist = array();
278
- if (!is_readable($filename)) {
279
- $errormessagelist[] = '!is_readable';
280
- }
281
- if (!is_file($filename)) {
282
- $errormessagelist[] = '!is_file';
283
- }
284
- if (!file_exists($filename)) {
285
- $errormessagelist[] = '!file_exists';
286
- }
287
- if (empty($errormessagelist)) {
288
- $errormessagelist[] = 'fopen failed';
289
- }
290
- throw new getid3_exception('Could not open "'.$filename.'" ('.implode('; ', $errormessagelist).')');
291
- }
292
-
293
- // START ADDED BY POWERPRESS
294
- if( $filesize )
295
- $this->info['filesize'] = $filesize;
296
- else // END ADDED BY POWERPRESS
297
- $this->info['filesize'] = filesize($filename);
298
- // set redundant parameters - might be needed in some include file
299
- // filenames / filepaths in getID3 are always expressed with forward slashes (unix-style) for both Windows and other to try and minimize confusion
300
- $filename = str_replace('\\', '/', $filename);
301
- $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename)));
302
- $this->info['filename'] = getid3_lib::mb_basename($filename);
303
- $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename'];
304
-
305
-
306
- // option_max_2gb_check
307
- if ($this->option_max_2gb_check) {
308
- // PHP (32-bit all, and 64-bit Windows) doesn't support integers larger than 2^31 (~2GB)
309
- // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize
310
- // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer
311
- $fseek = fseek($this->fp, 0, SEEK_END);
312
- if (($fseek < 0) || (($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) ||
313
- ($this->info['filesize'] < 0) ||
314
- (ftell($this->fp) < 0)) {
315
- $real_filesize = getid3_lib::getFileSizeSyscall($this->info['filenamepath']);
316
-
317
- if ($real_filesize === false) {
318
- unset($this->info['filesize']);
319
- fclose($this->fp);
320
- throw new getid3_exception('Unable to determine actual filesize. File is most likely larger than '.round(PHP_INT_MAX / 1073741824).'GB and is not supported by PHP.');
321
- } elseif (getid3_lib::intValueSupported($real_filesize)) {
322
- unset($this->info['filesize']);
323
- fclose($this->fp);
324
- throw new getid3_exception('PHP seems to think the file is larger than '.round(PHP_INT_MAX / 1073741824).'GB, but filesystem reports it as '.number_format($real_filesize, 3).'GB, please report to info@getid3.org');
325
- }
326
- $this->info['filesize'] = $real_filesize;
327
- $this->warning('File is larger than '.round(PHP_INT_MAX / 1073741824).'GB (filesystem reports it as '.number_format($real_filesize, 3).'GB) and is not properly supported by PHP.');
328
- }
329
- }
330
-
331
- // set more parameters
332
- $this->info['avdataoffset'] = 0;
333
- $this->info['avdataend'] = $this->info['filesize'];
334
- $this->info['fileformat'] = ''; // filled in later
335
- $this->info['audio']['dataformat'] = ''; // filled in later, unset if not used
336
- $this->info['video']['dataformat'] = ''; // filled in later, unset if not used
337
- $this->info['tags'] = array(); // filled in later, unset if not used
338
- $this->info['error'] = array(); // filled in later, unset if not used
339
- $this->info['warning'] = array(); // filled in later, unset if not used
340
- $this->info['comments'] = array(); // filled in later, unset if not used
341
- $this->info['encoding'] = $this->encoding; // required by id3v2 and iso modules - can be unset at the end if desired
342
-
343
- return true;
344
-
345
- } catch (Exception $e) {
346
- $this->error($e->getMessage());
347
- }
348
- return false;
349
- }
350
-
351
- // public: analyze file
352
- public function analyze($filename, $filesize=false, $orig_filename='file.mp3') { // 2nd AND 3rd PARAMTERS ADDED BY POWERPRESS
353
- try {
354
- if (!$this->openfile($filename, $filesize)) { // 2nd PARAMTER ADDED BY POWERPRESS
355
- return $this->info;
356
- }
357
-
358
- // Handle tags
359
- foreach (array('id3v2'=>'id3v2', 'id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) {
360
- $option_tag = 'option_tag_'.$tag_name;
361
- if ($this->$option_tag) {
362
- $this->include_module('tag.'.$tag_name);
363
- try {
364
- $tag_class = 'getid3_'.$tag_name;
365
- $tag = new $tag_class($this);
366
- $tag->Analyze();
367
- }
368
- catch (getid3_exception $e) {
369
- throw $e;
370
- }
371
- }
372
- }
373
- if (isset($this->info['id3v2']['tag_offset_start'])) {
374
- $this->info['avdataoffset'] = max($this->info['avdataoffset'], $this->info['id3v2']['tag_offset_end']);
375
- }
376
- foreach (array('id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) {
377
- if (isset($this->info[$tag_key]['tag_offset_start'])) {
378
- $this->info['avdataend'] = min($this->info['avdataend'], $this->info[$tag_key]['tag_offset_start']);
379
- }
380
- }
381
-
382
- // ID3v2 detection (NOT parsing), even if ($this->option_tag_id3v2 == false) done to make fileformat easier
383
- if (!$this->option_tag_id3v2) {
384
- fseek($this->fp, 0);
385
- $header = fread($this->fp, 10);
386
- if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) {
387
- $this->info['id3v2']['header'] = true;
388
- $this->info['id3v2']['majorversion'] = ord($header{3});
389
- $this->info['id3v2']['minorversion'] = ord($header{4});
390
- $this->info['avdataoffset'] += getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
391
- }
392
- }
393
-
394
- // read 32 kb file data
395
- fseek($this->fp, $this->info['avdataoffset']);
396
- $formattest = fread($this->fp, 32774);
397
-
398
- // determine format
399
- // MODIFIED BY POWERPRESS
400
- $determined_format = $this->GetFileFormat($formattest, $orig_filename);
401
- // MODIFIED BY POWERPRESS
402
-
403
- // unable to determine file format
404
- if (!$determined_format) {
405
- fclose($this->fp);
406
- return $this->error('unable to determine file format');
407
- }
408
-
409
- // check for illegal ID3 tags
410
- if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
411
- if ($determined_format['fail_id3'] === 'ERROR') {
412
- fclose($this->fp);
413
- return $this->error('ID3 tags not allowed on this file type.');
414
- } elseif ($determined_format['fail_id3'] === 'WARNING') {
415
- $this->warning('ID3 tags not allowed on this file type.');
416
- }
417
- }
418
-
419
- // check for illegal APE tags
420
- if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
421
- if ($determined_format['fail_ape'] === 'ERROR') {
422
- fclose($this->fp);
423
- return $this->error('APE tags not allowed on this file type.');
424
- } elseif ($determined_format['fail_ape'] === 'WARNING') {
425
- $this->warning('APE tags not allowed on this file type.');
426
- }
427
- }
428
-
429
- // set mime type
430
- $this->info['mime_type'] = $determined_format['mime_type'];
431
-
432
- // supported format signature pattern detected, but module deleted
433
- if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) {
434
- fclose($this->fp);
435
- return $this->error('Format not supported, module "'.$determined_format['include'].'" was removed.');
436
- }
437
-
438
- // module requires iconv support
439
- // Check encoding/iconv support
440
- if (!empty($determined_format['iconv_req']) && !function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
441
- $errormessage = 'iconv() support is required for this module ('.$determined_format['include'].') for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
442
- if (GETID3_OS_ISWINDOWS) {
443
- $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32';
444
- } else {
445
- $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
446
- }
447
- return $this->error($errormessage);
448
- }
449
-
450
- // include module
451
- include_once(GETID3_INCLUDEPATH.$determined_format['include']);
452
-
453
- // instantiate module class
454
- $class_name = 'getid3_'.$determined_format['module'];
455
- if (!class_exists($class_name)) {
456
- return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.');
457
- }
458
- $class = new $class_name($this);
459
- $class->Analyze();
460
- unset($class);
461
-
462
- // close file
463
- fclose($this->fp);
464
-
465
- // process all tags - copy to 'tags' and convert charsets
466
- if ($this->option_tags_process) {
467
- $this->HandleAllTags();
468
- }
469
-
470
- // perform more calculations
471
- if ($this->option_extra_info) {
472
- $this->ChannelsBitratePlaytimeCalculations();
473
- $this->CalculateCompressionRatioVideo();
474
- $this->CalculateCompressionRatioAudio();
475
- $this->CalculateReplayGain();
476
- $this->ProcessAudioStreams();
477
- }
478
-
479
- // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
480
- if ($this->option_md5_data) {
481
- // do not calc md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
482
- if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
483
- $this->getHashdata('md5');
484
- }
485
- }
486
-
487
- // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
488
- if ($this->option_sha1_data) {
489
- $this->getHashdata('sha1');
490
- }
491
-
492
- // remove undesired keys
493
- $this->CleanUp();
494
-
495
- } catch (Exception $e) {
496
- $this->error('Caught exception: '.$e->getMessage());
497
- }
498
-
499
- // return info array
500
- return $this->info;
501
- }
502
-
503
-
504
- // private: error handling
505
- public function error($message) {
506
- $this->CleanUp();
507
- if (!isset($this->info['error'])) {
508
- $this->info['error'] = array();
509
- }
510
- $this->info['error'][] = $message;
511
- return $this->info;
512
- }
513
-
514
-
515
- // private: warning handling
516
- public function warning($message) {
517
- $this->info['warning'][] = $message;
518
- return true;
519
- }
520
-
521
-
522
- // private: CleanUp
523
- private function CleanUp() {
524
-
525
- // remove possible empty keys
526
- $AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate');
527
- foreach ($AVpossibleEmptyKeys as $dummy => $key) {
528
- if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) {
529
- unset($this->info['audio'][$key]);
530
- }
531
- if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) {
532
- unset($this->info['video'][$key]);
533
- }
534
- }
535
-
536
- // remove empty root keys
537
- if (!empty($this->info)) {
538
- foreach ($this->info as $key => $value) {
539
- if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) {
540
- unset($this->info[$key]);
541
- }
542
- }
543
- }
544
-
545
- // remove meaningless entries from unknown-format files
546
- if (empty($this->info['fileformat'])) {
547
- if (isset($this->info['avdataoffset'])) {
548
- unset($this->info['avdataoffset']);
549
- }
550
- if (isset($this->info['avdataend'])) {
551
- unset($this->info['avdataend']);
552
- }
553
- }
554
-
555
- // remove possible duplicated identical entries
556
- if (!empty($this->info['error'])) {
557
- $this->info['error'] = array_values(array_unique($this->info['error']));
558
- }
559
- if (!empty($this->info['warning'])) {
560
- $this->info['warning'] = array_values(array_unique($this->info['warning']));
561
- }
562
-
563
- // remove "global variable" type keys
564
- unset($this->info['php_memory_limit']);
565
-
566
- return true;
567
- }
568
-
569
-
570
- // return array containing information about all supported formats
571
- public function GetFileFormatArray() {
572
- static $format_info = array();
573
- if (empty($format_info)) {
574
- $format_info = array(
575
-
576
- // Audio formats
577
-
578
- // AC-3 - audio - Dolby AC-3 / Dolby Digital
579
- 'ac3' => array(
580
- 'pattern' => '^\x0B\x77',
581
- 'group' => 'audio',
582
- 'module' => 'ac3',
583
- 'mime_type' => 'audio/ac3',
584
- ),
585
-
586
- // AAC - audio - Advanced Audio Coding (AAC) - ADIF format
587
- 'adif' => array(
588
- 'pattern' => '^ADIF',
589
- 'group' => 'audio',
590
- 'module' => 'aac',
591
- 'mime_type' => 'application/octet-stream',
592
- 'fail_ape' => 'WARNING',
593
- ),
594
-
595
- /*
596
- // AA - audio - Audible Audiobook
597
- 'aa' => array(
598
- 'pattern' => '^.{4}\x57\x90\x75\x36',
599
- 'group' => 'audio',
600
- 'module' => 'aa',
601
- 'mime_type' => 'audio/audible',
602
- ),
603
- */
604
- // AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
605
- 'adts' => array(
606
- 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]',
607
- 'group' => 'audio',
608
- 'module' => 'aac',
609
- 'mime_type' => 'application/octet-stream',
610
- 'fail_ape' => 'WARNING',
611
- ),
612
-
613
-
614
- // AU - audio - NeXT/Sun AUdio (AU)
615
- 'au' => array(
616
- 'pattern' => '^\.snd',
617
- 'group' => 'audio',
618
- 'module' => 'au',
619
- 'mime_type' => 'audio/basic',
620
- ),
621
-
622
- // AMR - audio - Adaptive Multi Rate
623
- 'amr' => array(
624
- 'pattern' => '^\x23\x21AMR\x0A', // #!AMR[0A]
625
- 'group' => 'audio',
626
- 'module' => 'amr',
627
- 'mime_type' => 'audio/amr',
628
- ),
629
-
630
- // AVR - audio - Audio Visual Research
631
- 'avr' => array(
632
- 'pattern' => '^2BIT',
633
- 'group' => 'audio',
634
- 'module' => 'avr',
635
- 'mime_type' => 'application/octet-stream',
636
- ),
637
-
638
- // BONK - audio - Bonk v0.9+
639
- 'bonk' => array(
640
- 'pattern' => '^\x00(BONK|INFO|META| ID3)',
641
- 'group' => 'audio',
642
- 'module' => 'bonk',
643
- 'mime_type' => 'audio/xmms-bonk',
644
- ),
645
-
646
- // DSS - audio - Digital Speech Standard
647
- 'dss' => array(
648
- 'pattern' => '^[\x02-\x03]ds[s2]',
649
- 'group' => 'audio',
650
- 'module' => 'dss',
651
- 'mime_type' => 'application/octet-stream',
652
- ),
653
-
654
- // DTS - audio - Dolby Theatre System
655
- 'dts' => array(
656
- 'pattern' => '^\x7F\xFE\x80\x01',
657
- 'group' => 'audio',
658
- 'module' => 'dts',
659
- 'mime_type' => 'audio/dts',
660
- ),
661
-
662
- // FLAC - audio - Free Lossless Audio Codec
663
- 'flac' => array(
664
- 'pattern' => '^fLaC',
665
- 'group' => 'audio',
666
- 'module' => 'flac',
667
- 'mime_type' => 'audio/x-flac',
668
- ),
669
-
670
- // LA - audio - Lossless Audio (LA)
671
- 'la' => array(
672
- 'pattern' => '^LA0[2-4]',
673
- 'group' => 'audio',
674
- 'module' => 'la',
675
- 'mime_type' => 'application/octet-stream',
676
- ),
677
-
678
- // LPAC - audio - Lossless Predictive Audio Compression (LPAC)
679
- 'lpac' => array(
680
- 'pattern' => '^LPAC',
681
- 'group' => 'audio',
682
- 'module' => 'lpac',
683
- 'mime_type' => 'application/octet-stream',
684
- ),
685
-
686
- // MIDI - audio - MIDI (Musical Instrument Digital Interface)
687
- 'midi' => array(
688
- 'pattern' => '^MThd',
689
- 'group' => 'audio',
690
- 'module' => 'midi',
691
- 'mime_type' => 'audio/midi',
692
- ),
693
-
694
- // MAC - audio - Monkey's Audio Compressor
695
- 'mac' => array(
696
- 'pattern' => '^MAC ',
697
- 'group' => 'audio',
698
- 'module' => 'monkey',
699
- 'mime_type' => 'application/octet-stream',
700
- ),
701
-
702
- // has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available
703
- // // MOD - audio - MODule (assorted sub-formats)
704
- // 'mod' => array(
705
- // 'pattern' => '^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)',
706
- // 'group' => 'audio',
707
- // 'module' => 'mod',
708
- // 'option' => 'mod',
709
- // 'mime_type' => 'audio/mod',
710
- // ),
711
-
712
- // MOD - audio - MODule (Impulse Tracker)
713
- 'it' => array(
714
- 'pattern' => '^IMPM',
715
- 'group' => 'audio',
716
- 'module' => 'mod',
717
- //'option' => 'it',
718
- 'mime_type' => 'audio/it',
719
- ),
720
-
721
- // MOD - audio - MODule (eXtended Module, various sub-formats)
722
- 'xm' => array(
723
- 'pattern' => '^Extended Module',
724
- 'group' => 'audio',
725
- 'module' => 'mod',
726
- //'option' => 'xm',
727
- 'mime_type' => 'audio/xm',
728
- ),
729
-
730
- // MOD - audio - MODule (ScreamTracker)
731
- 's3m' => array(
732
- 'pattern' => '^.{44}SCRM',
733
- 'group' => 'audio',
734
- 'module' => 'mod',
735
- //'option' => 's3m',
736
- 'mime_type' => 'audio/s3m',
737
- ),
738
-
739
- // MPC - audio - Musepack / MPEGplus
740
- 'mpc' => array(
741
- 'pattern' => '^(MPCK|MP\+|[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])',
742
- 'group' => 'audio',
743
- 'module' => 'mpc',
744
- 'mime_type' => 'audio/x-musepack',
745
- ),
746
-
747
- // MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS)
748
- 'mp3' => array(
749
- 'pattern' => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\x0B\x10-\x1B\x20-\x2B\x30-\x3B\x40-\x4B\x50-\x5B\x60-\x6B\x70-\x7B\x80-\x8B\x90-\x9B\xA0-\xAB\xB0-\xBB\xC0-\xCB\xD0-\xDB\xE0-\xEB\xF0-\xFB]',
750
- 'group' => 'audio',
751
- 'module' => 'mp3',
752
- 'mime_type' => 'audio/mpeg',
753
- ),
754
-
755
- // OFR - audio - OptimFROG
756
- 'ofr' => array(
757
- 'pattern' => '^(\*RIFF|OFR)',
758
- 'group' => 'audio',
759
- 'module' => 'optimfrog',
760
- 'mime_type' => 'application/octet-stream',
761
- ),
762
-
763
- // RKAU - audio - RKive AUdio compressor
764
- 'rkau' => array(
765
- 'pattern' => '^RKA',
766
- 'group' => 'audio',
767
- 'module' => 'rkau',
768
- 'mime_type' => 'application/octet-stream',
769
- ),
770
-
771
- // SHN - audio - Shorten
772
- 'shn' => array(
773
- 'pattern' => '^ajkg',
774
- 'group' => 'audio',
775
- 'module' => 'shorten',
776
- 'mime_type' => 'audio/xmms-shn',
777
- 'fail_id3' => 'ERROR',
778
- 'fail_ape' => 'ERROR',
779
- ),
780
-
781
- // TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org)
782
- 'tta' => array(
783
- 'pattern' => '^TTA', // could also be '^TTA(\x01|\x02|\x03|2|1)'
784
- 'group' => 'audio',
785
- 'module' => 'tta',
786
- 'mime_type' => 'application/octet-stream',
787
- ),
788
-
789
- // VOC - audio - Creative Voice (VOC)
790
- 'voc' => array(
791
- 'pattern' => '^Creative Voice File',
792
- 'group' => 'audio',
793
- 'module' => 'voc',
794
- 'mime_type' => 'audio/voc',
795
- ),
796
-
797
- // VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF)
798
- 'vqf' => array(
799
- 'pattern' => '^TWIN',
800
- 'group' => 'audio',
801
- 'module' => 'vqf',
802
- 'mime_type' => 'application/octet-stream',
803
- ),
804
-
805
- // WV - audio - WavPack (v4.0+)
806
- 'wv' => array(
807
- 'pattern' => '^wvpk',
808
- 'group' => 'audio',
809
- 'module' => 'wavpack',
810
- 'mime_type' => 'application/octet-stream',
811
- ),
812
-
813
-
814
- // Audio-Video formats
815
-
816
- // ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio
817
- 'asf' => array(
818
- 'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C',
819
- 'group' => 'audio-video',
820
- 'module' => 'asf',
821
- 'mime_type' => 'video/x-ms-asf',
822
- 'iconv_req' => false,
823
- ),
824
-
825
- // BINK - audio/video - Bink / Smacker
826
- 'bink' => array(
827
- 'pattern' => '^(BIK|SMK)',
828
- 'group' => 'audio-video',
829
- 'module' => 'bink',
830
- 'mime_type' => 'application/octet-stream',
831
- ),
832
-
833
- // FLV - audio/video - FLash Video
834
- 'flv' => array(
835
- 'pattern' => '^FLV\x01',
836
- 'group' => 'audio-video',
837
- 'module' => 'flv',
838
- 'mime_type' => 'video/x-flv',
839
- ),
840
-
841
- // MKAV - audio/video - Mastroka
842
- 'matroska' => array(
843
- 'pattern' => '^\x1A\x45\xDF\xA3',
844
- 'group' => 'audio-video',
845
- 'module' => 'matroska',
846
- 'mime_type' => 'video/x-matroska', // may also be audio/x-matroska
847
- ),
848
-
849
- // MPEG - audio/video - MPEG (Moving Pictures Experts Group)
850
- 'mpeg' => array(
851
- 'pattern' => '^\x00\x00\x01(\xBA|\xB3)',
852
- 'group' => 'audio-video',
853
- 'module' => 'mpeg',
854
- 'mime_type' => 'video/mpeg',
855
- ),
856
-
857
- // NSV - audio/video - Nullsoft Streaming Video (NSV)
858
- 'nsv' => array(
859
- 'pattern' => '^NSV[sf]',
860
- 'group' => 'audio-video',
861
- 'module' => 'nsv',
862
- 'mime_type' => 'application/octet-stream',
863
- ),
864
-
865
- // Ogg - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*))
866
- 'ogg' => array(
867
- 'pattern' => '^OggS',
868
- 'group' => 'audio',
869
- 'module' => 'ogg',
870
- 'mime_type' => 'application/ogg',
871
- 'fail_id3' => 'WARNING',
872
- 'fail_ape' => 'WARNING',
873
- ),
874
-
875
- // QT - audio/video - Quicktime
876
- 'quicktime' => array(
877
- 'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)',
878
- 'group' => 'audio-video',
879
- 'module' => 'quicktime',
880
- 'mime_type' => 'video/quicktime',
881
- ),
882
-
883
- // RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF)
884
- 'riff' => array(
885
- 'pattern' => '^(RIFF|SDSS|FORM)',
886
- 'group' => 'audio-video',
887
- 'module' => 'riff',
888
- 'mime_type' => 'audio/x-wave',
889
- 'fail_ape' => 'WARNING',
890
- ),
891
-
892
- // Real - audio/video - RealAudio, RealVideo
893
- 'real' => array(
894
- 'pattern' => '^(\\.RMF|\\.ra)',
895
- 'group' => 'audio-video',
896
- 'module' => 'real',
897
- 'mime_type' => 'audio/x-realaudio',
898
- ),
899
-
900
- // SWF - audio/video - ShockWave Flash
901
- 'swf' => array(
902
- 'pattern' => '^(F|C)WS',
903
- 'group' => 'audio-video',
904
- 'module' => 'swf',
905
- 'mime_type' => 'application/x-shockwave-flash',
906
- ),
907
-
908
- // TS - audio/video - MPEG-2 Transport Stream
909
- 'ts' => array(
910
- 'pattern' => '^(\x47.{187}){10,}', // packets are 188 bytes long and start with 0x47 "G". Check for at least 10 packets matching this pattern
911
- 'group' => 'audio-video',
912
- 'module' => 'ts',
913
- 'mime_type' => 'video/MP2T',
914
- ),
915
-
916
-
917
- // Still-Image formats
918
-
919
- // BMP - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4)
920
- 'bmp' => array(
921
- 'pattern' => '^BM',
922
- 'group' => 'graphic',
923
- 'module' => 'bmp',
924
- 'mime_type' => 'image/bmp',
925
- 'fail_id3' => 'ERROR',
926
- 'fail_ape' => 'ERROR',
927
- ),
928
-
929
- // GIF - still image - Graphics Interchange Format
930
- 'gif' => array(
931
- 'pattern' => '^GIF',
932
- 'group' => 'graphic',
933
- 'module' => 'gif',
934
- 'mime_type' => 'image/gif',
935
- 'fail_id3' => 'ERROR',
936
- 'fail_ape' => 'ERROR',
937
- ),
938
-
939
- // JPEG - still image - Joint Photographic Experts Group (JPEG)
940
- 'jpg' => array(
941
- 'pattern' => '^\xFF\xD8\xFF',
942
- 'group' => 'graphic',
943
- 'module' => 'jpg',
944
- 'mime_type' => 'image/jpeg',
945
- 'fail_id3' => 'ERROR',
946
- 'fail_ape' => 'ERROR',
947
- ),
948
-
949
- // PCD - still image - Kodak Photo CD
950
- 'pcd' => array(
951
- 'pattern' => '^.{2048}PCD_IPI\x00',
952
- 'group' => 'graphic',
953
- 'module' => 'pcd',
954
- 'mime_type' => 'image/x-photo-cd',
955
- 'fail_id3' => 'ERROR',
956
- 'fail_ape' => 'ERROR',
957
- ),
958
-
959
-
960
- // PNG - still image - Portable Network Graphics (PNG)
961
- 'png' => array(
962
- 'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A',
963
- 'group' => 'graphic',
964
- 'module' => 'png',
965
- 'mime_type' => 'image/png',
966
- 'fail_id3' => 'ERROR',
967
- 'fail_ape' => 'ERROR',
968
- ),
969
-
970
-
971
- // SVG - still image - Scalable Vector Graphics (SVG)
972
- 'svg' => array(
973
- 'pattern' => '(<!DOCTYPE svg PUBLIC |xmlns="http:\/\/www\.w3\.org\/2000\/svg")',
974
- 'group' => 'graphic',
975
- 'module' => 'svg',
976
- 'mime_type' => 'image/svg+xml',
977
- 'fail_id3' => 'ERROR',
978
- 'fail_ape' => 'ERROR',
979
- ),
980
-
981
-
982
- // TIFF - still image - Tagged Information File Format (TIFF)
983
- 'tiff' => array(
984
- 'pattern' => '^(II\x2A\x00|MM\x00\x2A)',
985
- 'group' => 'graphic',
986
- 'module' => 'tiff',
987
- 'mime_type' => 'image/tiff',
988
- 'fail_id3' => 'ERROR',
989
- 'fail_ape' => 'ERROR',
990
- ),
991
-
992
-
993
- // EFAX - still image - eFax (TIFF derivative)
994
- 'efax' => array(
995
- 'pattern' => '^\xDC\xFE',
996
- 'group' => 'graphic',
997
- 'module' => 'efax',
998
- 'mime_type' => 'image/efax',
999
- 'fail_id3' => 'ERROR',
1000
- 'fail_ape' => 'ERROR',
1001
- ),
1002
-
1003
-
1004
- // Data formats
1005
-
1006
- // ISO - data - International Standards Organization (ISO) CD-ROM Image
1007
- 'iso' => array(
1008
- 'pattern' => '^.{32769}CD001',
1009
- 'group' => 'misc',
1010
- 'module' => 'iso',
1011
- 'mime_type' => 'application/octet-stream',
1012
- 'fail_id3' => 'ERROR',
1013
- 'fail_ape' => 'ERROR',
1014
- 'iconv_req' => false,
1015
- ),
1016
-
1017
- // RAR - data - RAR compressed data
1018
- 'rar' => array(
1019
- 'pattern' => '^Rar\!',
1020
- 'group' => 'archive',
1021
- 'module' => 'rar',
1022
- 'mime_type' => 'application/octet-stream',
1023
- 'fail_id3' => 'ERROR',
1024
- 'fail_ape' => 'ERROR',
1025
- ),
1026
-
1027
- // SZIP - audio/data - SZIP compressed data
1028
- 'szip' => array(
1029
- 'pattern' => '^SZ\x0A\x04',
1030
- 'group' => 'archive',
1031
- 'module' => 'szip',
1032
- 'mime_type' => 'application/octet-stream',
1033
- 'fail_id3' => 'ERROR',
1034
- 'fail_ape' => 'ERROR',
1035
- ),
1036
-
1037
- // TAR - data - TAR compressed data
1038
- 'tar' => array(
1039
- 'pattern' => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}',
1040
- 'group' => 'archive',
1041
- 'module' => 'tar',
1042
- 'mime_type' => 'application/x-tar',
1043
- 'fail_id3' => 'ERROR',
1044
- 'fail_ape' => 'ERROR',
1045
- ),
1046
-
1047
- // GZIP - data - GZIP compressed data
1048
- 'gz' => array(
1049
- 'pattern' => '^\x1F\x8B\x08',
1050
- 'group' => 'archive',
1051
- 'module' => 'gzip',
1052
- 'mime_type' => 'application/x-gzip',
1053
- 'fail_id3' => 'ERROR',
1054
- 'fail_ape' => 'ERROR',
1055
- ),
1056
-
1057
- // ZIP - data - ZIP compressed data
1058
- 'zip' => array(
1059
- 'pattern' => '^PK\x03\x04',
1060
- 'group' => 'archive',
1061
- 'module' => 'zip',
1062
- 'mime_type' => 'application/zip',
1063
- 'fail_id3' => 'ERROR',
1064
- 'fail_ape' => 'ERROR',
1065
- ),
1066
-
1067
-
1068
- // Misc other formats
1069
-
1070
- // PAR2 - data - Parity Volume Set Specification 2.0
1071
- 'par2' => array (
1072
- 'pattern' => '^PAR2\x00PKT',
1073
- 'group' => 'misc',
1074
- 'module' => 'par2',
1075
- 'mime_type' => 'application/octet-stream',
1076
- 'fail_id3' => 'ERROR',
1077
- 'fail_ape' => 'ERROR',
1078
- ),
1079
-
1080
- // PDF - data - Portable Document Format
1081
- 'pdf' => array(
1082
- 'pattern' => '^\x25PDF',
1083
- 'group' => 'misc',
1084
- 'module' => 'pdf',
1085
- 'mime_type' => 'application/pdf',
1086
- 'fail_id3' => 'ERROR',
1087
- 'fail_ape' => 'ERROR',
1088
- ),
1089
-
1090
- // MSOFFICE - data - ZIP compressed data
1091
- 'msoffice' => array(
1092
- 'pattern' => '^\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1', // D0CF11E == DOCFILE == Microsoft Office Document
1093
- 'group' => 'misc',
1094
- 'module' => 'msoffice',
1095
- 'mime_type' => 'application/octet-stream',
1096
- 'fail_id3' => 'ERROR',
1097
- 'fail_ape' => 'ERROR',
1098
- ),
1099
-
1100
- // CUE - data - CUEsheet (index to single-file disc images)
1101
- 'cue' => array(
1102
- 'pattern' => '', // empty pattern means cannot be automatically detected, will fall through all other formats and match based on filename and very basic file contents
1103
- 'group' => 'misc',
1104
- 'module' => 'cue',
1105
- 'mime_type' => 'application/octet-stream',
1106
- ),
1107
-
1108
- );
1109
- }
1110
-
1111
- return $format_info;
1112
- }
1113
-
1114
-
1115
-
1116
- public function GetFileFormat(&$filedata, $filename='') {
1117
- // this function will determine the format of a file based on usually
1118
- // the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG,
1119
- // and in the case of ISO CD image, 6 bytes offset 32kb from the start
1120
- // of the file).
1121
-
1122
- // Identify file format - loop through $format_info and detect with reg expr
1123
- foreach ($this->GetFileFormatArray() as $format_name => $info) {
1124
- // The /s switch on preg_match() forces preg_match() NOT to treat
1125
- // newline (0x0A) characters as special chars but do a binary match
1126
- if (!empty($info['pattern']) && preg_match('#'.$info['pattern'].'#s', $filedata)) {
1127
- $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1128
- return $info;
1129
- }
1130
- }
1131
-
1132
-
1133
- if (preg_match('#\.mp[123a]$#i', $filename)) {
1134
- // Too many mp3 encoders on the market put gabage in front of mpeg files
1135
- // use assume format on these if format detection failed
1136
- $GetFileFormatArray = $this->GetFileFormatArray();
1137
- $info = $GetFileFormatArray['mp3'];
1138
- $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1139
- return $info;
1140
- } elseif (preg_match('/\.cue$/i', $filename) && preg_match('#FILE "[^"]+" (BINARY|MOTOROLA|AIFF|WAVE|MP3)#', $filedata)) {
1141
- // there's not really a useful consistent "magic" at the beginning of .cue files to identify them
1142
- // so until I think of something better, just go by filename if all other format checks fail
1143
- // and verify there's at least one instance of "TRACK xx AUDIO" in the file
1144
- $GetFileFormatArray = $this->GetFileFormatArray();
1145
- $info = $GetFileFormatArray['cue'];
1146
- $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1147
- return $info;
1148
- }
1149
-
1150
- return false;
1151
- }
1152
-
1153
-
1154
- // converts array to $encoding charset from $this->encoding
1155
- public function CharConvert(&$array, $encoding) {
1156
-
1157
- // identical encoding - end here
1158
- if ($encoding == $this->encoding) {
1159
- return;
1160
- }
1161
-
1162
- // loop thru array
1163
- foreach ($array as $key => $value) {
1164
-
1165
- // go recursive
1166
- if (is_array($value)) {
1167
- $this->CharConvert($array[$key], $encoding);
1168
- }
1169
-
1170
- // convert string
1171
- elseif (is_string($value)) {
1172
- $array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value));
1173
- }
1174
- }
1175
- }
1176
-
1177
-
1178
- public function HandleAllTags() {
1179
-
1180
- // key name => array (tag name, character encoding)
1181
- static $tags;
1182
- if (empty($tags)) {
1183
- $tags = array(
1184
- 'asf' => array('asf' , 'UTF-16LE'),
1185
- 'midi' => array('midi' , 'ISO-8859-1'),
1186
- 'nsv' => array('nsv' , 'ISO-8859-1'),
1187
- 'ogg' => array('vorbiscomment' , 'UTF-8'),
1188
- 'png' => array('png' , 'UTF-8'),
1189
- 'tiff' => array('tiff' , 'ISO-8859-1'),
1190
- 'quicktime' => array('quicktime' , 'UTF-8'),
1191
- 'real' => array('real' , 'ISO-8859-1'),
1192
- 'vqf' => array('vqf' , 'ISO-8859-1'),
1193
- 'zip' => array('zip' , 'ISO-8859-1'),
1194
- 'riff' => array('riff' , 'ISO-8859-1'),
1195
- 'lyrics3' => array('lyrics3' , 'ISO-8859-1'),
1196
- 'id3v1' => array('id3v1' , $this->encoding_id3v1),
1197
- 'id3v2' => array('id3v2' , 'UTF-8'), // not according to the specs (every frame can have a different encoding), but getID3() force-converts all encodings to UTF-8
1198
- 'ape' => array('ape' , 'UTF-8'),
1199
- 'cue' => array('cue' , 'ISO-8859-1'),
1200
- 'matroska' => array('matroska' , 'UTF-8'),
1201
- 'flac' => array('vorbiscomment' , 'UTF-8'),
1202
- 'divxtag' => array('divx' , 'ISO-8859-1'),
1203
- 'iptc' => array('iptc' , 'ISO-8859-1'),
1204
- );
1205
- }
1206
-
1207
- // loop through comments array
1208
- foreach ($tags as $comment_name => $tagname_encoding_array) {
1209
- list($tag_name, $encoding) = $tagname_encoding_array;
1210
-
1211
- // fill in default encoding type if not already present
1212
- if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) {
1213
- $this->info[$comment_name]['encoding'] = $encoding;
1214
- }
1215
-
1216
- // copy comments if key name set
1217
- if (!empty($this->info[$comment_name]['comments'])) {
1218
- foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) {
1219
- foreach ($valuearray as $key => $value) {
1220
- if (is_string($value)) {
1221
- $value = trim($value, " \r\n\t"); // do not trim nulls from $value!! Unicode characters will get mangled if trailing nulls are removed!
1222
- }
1223
- if ($value) {
1224
- if (!is_numeric($key)) {
1225
- $this->info['tags'][trim($tag_name)][trim($tag_key)][$key] = $value;
1226
- } else {
1227
- $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value;
1228
- }
1229
- }
1230
- }
1231
- if ($tag_key == 'picture') {
1232
- unset($this->info[$comment_name]['comments'][$tag_key]);
1233
- }
1234
- }
1235
-
1236
- if (!isset($this->info['tags'][$tag_name])) {
1237
- // comments are set but contain nothing but empty strings, so skip
1238
- continue;
1239
- }
1240
-
1241
- if ($this->option_tags_html) {
1242
- foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
1243
- $this->info['tags_html'][$tag_name][$tag_key] = getid3_lib::recursiveMultiByteCharString2HTML($valuearray, $encoding);
1244
- }
1245
- }
1246
-
1247
- $this->CharConvert($this->info['tags'][$tag_name], $encoding); // only copy gets converted!
1248
- }
1249
-
1250
- }
1251
-
1252
- // pictures can take up a lot of space, and we don't need multiple copies of them
1253
- // let there be a single copy in [comments][picture], and not elsewhere
1254
- if (!empty($this->info['tags'])) {
1255
- $unset_keys = array('tags', 'tags_html');
1256
- foreach ($this->info['tags'] as $tagtype => $tagarray) {
1257
- foreach ($tagarray as $tagname => $tagdata) {
1258
- if ($tagname == 'picture') {
1259
- foreach ($tagdata as $key => $tagarray) {
1260
- $this->info['comments']['picture'][] = $tagarray;
1261
- if (isset($tagarray['data']) && isset($tagarray['image_mime'])) {
1262
- if (isset($this->info['tags'][$tagtype][$tagname][$key])) {
1263
- unset($this->info['tags'][$tagtype][$tagname][$key]);
1264
- }
1265
- if (isset($this->info['tags_html'][$tagtype][$tagname][$key])) {
1266
- unset($this->info['tags_html'][$tagtype][$tagname][$key]);
1267
- }
1268
- }
1269
- }
1270
- }
1271
- }
1272
- foreach ($unset_keys as $unset_key) {
1273
- // remove possible empty keys from (e.g. [tags][id3v2][picture])
1274
- if (empty($this->info[$unset_key][$tagtype]['picture'])) {
1275
- unset($this->info[$unset_key][$tagtype]['picture']);
1276
- }
1277
- if (empty($this->info[$unset_key][$tagtype])) {
1278
- unset($this->info[$unset_key][$tagtype]);
1279
- }
1280
- if (empty($this->info[$unset_key])) {
1281
- unset($this->info[$unset_key]);
1282
- }
1283
- }
1284
- // remove duplicate copy of picture data from (e.g. [id3v2][comments][picture])
1285
- if (isset($this->info[$tagtype]['comments']['picture'])) {
1286
- unset($this->info[$tagtype]['comments']['picture']);
1287
- }
1288
- if (empty($this->info[$tagtype]['comments'])) {
1289
- unset($this->info[$tagtype]['comments']);
1290
- }
1291
- if (empty($this->info[$tagtype])) {
1292
- unset($this->info[$tagtype]);
1293
- }
1294
- }
1295
- }
1296
- return true;
1297
- }
1298
-
1299
- public function getHashdata($algorithm) {
1300
- switch ($algorithm) {
1301
- case 'md5':
1302
- case 'sha1':
1303
- break;
1304
-
1305
- default:
1306
- return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()');
1307
- break;
1308
- }
1309
-
1310
- if (!empty($this->info['fileformat']) && !empty($this->info['dataformat']) && ($this->info['fileformat'] == 'ogg') && ($this->info['audio']['dataformat'] == 'vorbis')) {
1311
-
1312
- // We cannot get an identical md5_data value for Ogg files where the comments
1313
- // span more than 1 Ogg page (compared to the same audio data with smaller
1314
- // comments) using the normal getID3() method of MD5'ing the data between the
1315
- // end of the comments and the end of the file (minus any trailing tags),
1316
- // because the page sequence numbers of the pages that the audio data is on
1317
- // do not match. Under normal circumstances, where comments are smaller than
1318
- // the nominal 4-8kB page size, then this is not a problem, but if there are
1319
- // very large comments, the only way around it is to strip off the comment
1320
- // tags with vorbiscomment and MD5 that file.
1321
- // This procedure must be applied to ALL Ogg files, not just the ones with
1322
- // comments larger than 1 page, because the below method simply MD5's the
1323
- // whole file with the comments stripped, not just the portion after the
1324
- // comments block (which is the standard getID3() method.
1325
-
1326
- // The above-mentioned problem of comments spanning multiple pages and changing
1327
- // page sequence numbers likely happens for OggSpeex and OggFLAC as well, but
1328
- // currently vorbiscomment only works on OggVorbis files.
1329
-
1330
- if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
1331
-
1332
- $this->warning('Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)');
1333
- $this->info[$algorithm.'_data'] = false;
1334
-
1335
- } else {
1336
-
1337
- // Prevent user from aborting script
1338
- $old_abort = ignore_user_abort(true);
1339
-
1340
- // Create empty file
1341
- $empty = tempnam(GETID3_TEMP_DIR, 'getID3');
1342
- touch($empty);
1343
-
1344
- // Use vorbiscomment to make temp file without comments
1345
- $temp = tempnam(GETID3_TEMP_DIR, 'getID3');
1346
- $file = $this->info['filenamepath'];
1347
-
1348
- if (GETID3_OS_ISWINDOWS) {
1349
-
1350
- if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
1351
-
1352
- $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"';
1353
- $VorbisCommentError = `$commandline`;
1354
-
1355
- } else {
1356
-
1357
- $VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
1358
-
1359
- }
1360
-
1361
- } else {
1362
-
1363
- $commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1';
1364
- $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1';
1365
- $VorbisCommentError = `$commandline`;
1366
-
1367
- }
1368
-
1369
- if (!empty($VorbisCommentError)) {
1370
-
1371
- $this->info['warning'][] = 'Failed making system call to vorbiscomment(.exe) - '.$algorithm.'_data will be incorrect. If vorbiscomment is unavailable, please download from http://www.vorbis.com/download.psp and put in the getID3() directory. Error returned: '.$VorbisCommentError;
1372
- $this->info[$algorithm.'_data'] = false;
1373
-
1374
- } else {
1375
-
1376
- // Get hash of newly created file
1377
- switch ($algorithm) {
1378
- case 'md5':
1379
- $this->info[$algorithm.'_data'] = md5_file($temp);
1380
- break;
1381
-
1382
- case 'sha1':
1383
- $this->info[$algorithm.'_data'] = sha1_file($temp);
1384
- break;
1385
- }
1386
- }
1387
-
1388
- // Clean up
1389
- unlink($empty);
1390
- unlink($temp);
1391
-
1392
- // Reset abort setting
1393
- ignore_user_abort($old_abort);
1394
-
1395
- }
1396
-
1397
- } else {
1398
-
1399
- if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) {
1400
-
1401
- // get hash from part of file
1402
- $this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm);
1403
-
1404
- } else {
1405
-
1406
- // get hash from whole file
1407
- switch ($algorithm) {
1408
- case 'md5':
1409
- $this->info[$algorithm.'_data'] = md5_file($this->info['filenamepath']);
1410
- break;
1411
-
1412
- case 'sha1':
1413
- $this->info[$algorithm.'_data'] = sha1_file($this->info['filenamepath']);
1414
- break;
1415
- }
1416
- }
1417
-
1418
- }
1419
- return true;
1420
- }
1421
-
1422
-
1423
- public function ChannelsBitratePlaytimeCalculations() {
1424
-
1425
- // set channelmode on audio
1426
- if (!empty($this->info['audio']['channelmode']) || !isset($this->info['audio']['channels'])) {
1427
- // ignore
1428
- } elseif ($this->info['audio']['channels'] == 1) {
1429
- $this->info['audio']['channelmode'] = 'mono';
1430
- } elseif ($this->info['audio']['channels'] == 2) {
1431
- $this->info['audio']['channelmode'] = 'stereo';
1432
- }
1433
-
1434
- // Calculate combined bitrate - audio + video
1435
- $CombinedBitrate = 0;
1436
- $CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0);
1437
- $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0);
1438
- if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) {
1439
- $this->info['bitrate'] = $CombinedBitrate;
1440
- }
1441
- //if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) {
1442
- // // for example, VBR MPEG video files cannot determine video bitrate:
1443
- // // should not set overall bitrate and playtime from audio bitrate only
1444
- // unset($this->info['bitrate']);
1445
- //}
1446
-
1447
- // video bitrate undetermined, but calculable
1448
- if (isset($this->info['video']['dataformat']) && $this->info['video']['dataformat'] && (!isset($this->info['video']['bitrate']) || ($this->info['video']['bitrate'] == 0))) {
1449
- // if video bitrate not set
1450
- if (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] > 0) && ($this->info['audio']['bitrate'] == $this->info['bitrate'])) {
1451
- // AND if audio bitrate is set to same as overall bitrate
1452
- if (isset($this->info['playtime_seconds']) && ($this->info['playtime_seconds'] > 0)) {
1453
- // AND if playtime is set
1454
- if (isset($this->info['avdataend']) && isset($this->info['avdataoffset'])) {
1455
- // AND if AV data offset start/end is known
1456
- // THEN we can calculate the video bitrate
1457
- $this->info['bitrate'] = round((($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']);
1458
- $this->info['video']['bitrate'] = $this->info['bitrate'] - $this->info['audio']['bitrate'];
1459
- }
1460
- }
1461
- }
1462
- }
1463
-
1464
- if ((!isset($this->info['playtime_seconds']) || ($this->info['playtime_seconds'] <= 0)) && !empty($this->info['bitrate'])) {
1465
- $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate'];
1466
- }
1467
-
1468
- if (!isset($this->info['bitrate']) && !empty($this->info['playtime_seconds'])) {
1469
- $this->info['bitrate'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds'];
1470
- }
1471
- if (isset($this->info['bitrate']) && empty($this->info['audio']['bitrate']) && empty($this->info['video']['bitrate'])) {
1472
- if (isset($this->info['audio']['dataformat']) && empty($this->info['video']['resolution_x'])) {
1473
- // audio only
1474
- $this->info['audio']['bitrate'] = $this->info['bitrate'];
1475
- } elseif (isset($this->info['video']['resolution_x']) && empty($this->info['audio']['dataformat'])) {
1476
- // video only
1477
- $this->info['video']['bitrate'] = $this->info['bitrate'];
1478
- }
1479
- }
1480
-
1481
- // Set playtime string
1482
- if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) {
1483
- $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']);
1484
- }
1485
- }
1486
-
1487
-
1488
- public function CalculateCompressionRatioVideo() {
1489
- if (empty($this->info['video'])) {
1490
- return false;
1491
- }
1492
- if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) {
1493
- return false;
1494
- }
1495
- if (empty($this->info['video']['bits_per_sample'])) {
1496
- return false;
1497
- }
1498
-
1499
- switch ($this->info['video']['dataformat']) {
1500
- case 'bmp':
1501
- case 'gif':
1502
- case 'jpeg':
1503
- case 'jpg':
1504
- case 'png':
1505
- case 'tiff':
1506
- $FrameRate = 1;
1507
- $PlaytimeSeconds = 1;
1508
- $BitrateCompressed = $this->info['filesize'] * 8;
1509
- break;
1510
-
1511
- default:
1512
- if (!empty($this->info['video']['frame_rate'])) {
1513
- $FrameRate = $this->info['video']['frame_rate'];
1514
- } else {
1515
- return false;
1516
- }
1517
- if (!empty($this->info['playtime_seconds'])) {
1518
- $PlaytimeSeconds = $this->info['playtime_seconds'];
1519
- } else {
1520
- return false;
1521
- }
1522
- if (!empty($this->info['video']['bitrate'])) {
1523
- $BitrateCompressed = $this->info['video']['bitrate'];
1524
- } else {
1525
- return false;
1526
- }
1527
- break;
1528
- }
1529
- $BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate;
1530
-
1531
- $this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed;
1532
- return true;
1533
- }
1534
-
1535
-
1536
- public function CalculateCompressionRatioAudio() {
1537
- if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate']) || !is_numeric($this->info['audio']['sample_rate'])) {
1538
- return false;
1539
- }
1540
- $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (!empty($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : 16));
1541
-
1542
- if (!empty($this->info['audio']['streams'])) {
1543
- foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) {
1544
- if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) {
1545
- $this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16));
1546
- }
1547
- }
1548
- }
1549
- return true;
1550
- }
1551
-
1552
-
1553
- public function CalculateReplayGain() {
1554
- if (isset($this->info['replay_gain'])) {
1555
- if (!isset($this->info['replay_gain']['reference_volume'])) {
1556
- $this->info['replay_gain']['reference_volume'] = (double) 89.0;
1557
- }
1558
- if (isset($this->info['replay_gain']['track']['adjustment'])) {
1559
- $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment'];
1560
- }
1561
- if (isset($this->info['replay_gain']['album']['adjustment'])) {
1562
- $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment'];
1563
- }
1564
-
1565
- if (isset($this->info['replay_gain']['track']['peak'])) {
1566
- $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']);
1567
- }
1568
- if (isset($this->info['replay_gain']['album']['peak'])) {
1569
- $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']);
1570
- }
1571
- }
1572
- return true;
1573
- }
1574
-
1575
- public function ProcessAudioStreams() {
1576
- if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) {
1577
- if (!isset($this->info['audio']['streams'])) {
1578
- foreach ($this->info['audio'] as $key => $value) {
1579
- if ($key != 'streams') {
1580
- $this->info['audio']['streams'][0][$key] = $value;
1581
- }
1582
- }
1583
- }
1584
- }
1585
- return true;
1586
- }
1587
-
1588
- public function getid3_tempnam() {
1589
- return tempnam($this->tempdir, 'gI3');
1590
- }
1591
-
1592
- public function include_module($name) {
1593
- //if (!file_exists($this->include_path.'module.'.$name.'.php')) {
1594
- if (!file_exists(GETID3_INCLUDEPATH.'module.'.$name.'.php')) {
1595
- throw new getid3_exception('Required module.'.$name.'.php is missing.');
1596
- }
1597
- include_once(GETID3_INCLUDEPATH.'module.'.$name.'.php');
1598
- return true;
1599
- }
1600
-
1601
- }
1602
-
1603
-
1604
- abstract class getid3_handler {
1605
-
1606
- /**
1607
- * @var getID3
1608
- */
1609
- protected $getid3; // pointer
1610
-
1611
- protected $data_string_flag = false; // analyzing filepointer or string
1612
- protected $data_string = ''; // string to analyze
1613
- protected $data_string_position = 0; // seek position in string
1614
- protected $data_string_length = 0; // string length
1615
-
1616
- private $dependency_to = null;
1617
-
1618
-
1619
- public function __construct(getID3 $getid3, $call_module=null) {
1620
- $this->getid3 = $getid3;
1621
-
1622
- if ($call_module) {
1623
- $this->dependency_to = str_replace('getid3_', '', $call_module);
1624
- }
1625
- }
1626
-
1627
-
1628
- // Analyze from file pointer
1629
- abstract public function Analyze();
1630
-
1631
-
1632
- // Analyze from string instead
1633
- public function AnalyzeString($string) {
1634
- // Enter string mode
1635
- $this->setStringMode($string);
1636
-
1637
- // Save info
1638
- $saved_avdataoffset = $this->getid3->info['avdataoffset'];
1639
- $saved_avdataend = $this->getid3->info['avdataend'];
1640
- $saved_filesize = (isset($this->getid3->info['filesize']) ? $this->getid3->info['filesize'] : null); // may be not set if called as dependency without openfile() call
1641
-
1642
- // Reset some info
1643
- $this->getid3->info['avdataoffset'] = 0;
1644
- $this->getid3->info['avdataend'] = $this->getid3->info['filesize'] = $this->data_string_length;
1645
-
1646
- // Analyze
1647
- $this->Analyze();
1648
-
1649
- // Restore some info
1650
- $this->getid3->info['avdataoffset'] = $saved_avdataoffset;
1651
- $this->getid3->info['avdataend'] = $saved_avdataend;
1652
- $this->getid3->info['filesize'] = $saved_filesize;
1653
-
1654
- // Exit string mode
1655
- $this->data_string_flag = false;
1656
- }
1657
-
1658
- public function setStringMode($string) {
1659
- $this->data_string_flag = true;
1660
- $this->data_string = $string;
1661
- $this->data_string_length = strlen($string);
1662
- }
1663
-
1664
- protected function ftell() {
1665
- if ($this->data_string_flag) {
1666
- return $this->data_string_position;
1667
- }
1668
- return ftell($this->getid3->fp);
1669
- }
1670
-
1671
- protected function fread($bytes) {
1672
- if ($this->data_string_flag) {
1673
- $this->data_string_position += $bytes;
1674
- return substr($this->data_string, $this->data_string_position - $bytes, $bytes);
1675
- }
1676
- $pos = $this->ftell() + $bytes;
1677
- if (!getid3_lib::intValueSupported($pos)) {
1678
- throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') because beyond PHP filesystem limit', 10);
1679
- }
1680
- return fread($this->getid3->fp, $bytes);
1681
- }
1682
-
1683
- protected function fseek($bytes, $whence=SEEK_SET) {
1684
- if ($this->data_string_flag) {
1685
- switch ($whence) {
1686
- case SEEK_SET:
1687
- $this->data_string_position = $bytes;
1688
- break;
1689
-
1690
- case SEEK_CUR:
1691
- $this->data_string_position += $bytes;
1692
- break;
1693
-
1694
- case SEEK_END:
1695
- $this->data_string_position = $this->data_string_length + $bytes;
1696
- break;
1697
- }
1698
- return 0;
1699
- } else {
1700
- $pos = $bytes;
1701
- if ($whence == SEEK_CUR) {
1702
- $pos = $this->ftell() + $bytes;
1703
- } elseif ($whence == SEEK_END) {
1704
- $pos = $this->getid3->info['filesize'] + $bytes;
1705
- }
1706
- if (!getid3_lib::intValueSupported($pos)) {
1707
- throw new getid3_exception('cannot fseek('.$pos.') because beyond PHP filesystem limit', 10);
1708
- }
1709
- }
1710
- return fseek($this->getid3->fp, $bytes, $whence);
1711
- }
1712
-
1713
- protected function feof() {
1714
- if ($this->data_string_flag) {
1715
- return $this->data_string_position >= $this->data_string_length;
1716
- }
1717
- return feof($this->getid3->fp);
1718
- }
1719
-
1720
- final protected function isDependencyFor($module) {
1721
- return $this->dependency_to == $module;
1722
- }
1723
-
1724
- protected function error($text) {
1725
- $this->getid3->info['error'][] = $text;
1726
-
1727
- return false;
1728
- }
1729
-
1730
- protected function warning($text) {
1731
- return $this->getid3->warning($text);
1732
- }
1733
-
1734
- protected function notice($text) {
1735
- // does nothing for now
1736
- }
1737
-
1738
- public function saveAttachment($name, $offset, $length, $image_mime=null) {
1739
- try {
1740
-
1741
- // do not extract at all
1742
- if ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_NONE) {
1743
-
1744
- $attachment = null; // do not set any
1745
-
1746
- // extract to return array
1747
- } elseif ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_INLINE) {
1748
-
1749
- $this->fseek($offset);
1750
- $attachment = $this->fread($length); // get whole data in one pass, till it is anyway stored in memory
1751
- if ($attachment === false || strlen($attachment) != $length) {
1752
- throw new Exception('failed to read attachment data');
1753
- }
1754
-
1755
- // assume directory path is given
1756
- } else {
1757
-
1758
- // set up destination path
1759
- $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR);
1760
- if (!is_dir($dir) || !is_writable($dir)) { // check supplied directory
1761
- throw new Exception('supplied path ('.$dir.') does not exist, or is not writable');
1762
- }
1763
- $dest = $dir.DIRECTORY_SEPARATOR.$name.($image_mime ? '.'.getid3_lib::ImageExtFromMime($image_mime) : '');
1764
-
1765
- // create dest file
1766
- if (($fp_dest = fopen($dest, 'wb')) == false) {
1767
- throw new Exception('failed to create file '.$dest);
1768
- }
1769
-
1770
- // copy data
1771
- $this->fseek($offset);
1772
- $buffersize = ($this->data_string_flag ? $length : $this->getid3->fread_buffer_size());
1773
- $bytesleft = $length;
1774
- while ($bytesleft > 0) {
1775
- if (($buffer = $this->fread(min($buffersize, $bytesleft))) === false || ($byteswritten = fwrite($fp_dest, $buffer)) === false || ($byteswritten === 0)) {
1776
- throw new Exception($buffer === false ? 'not enough data to read' : 'failed to write to destination file, may be not enough disk space');
1777
- }
1778
- $bytesleft -= $byteswritten;
1779
- }
1780
-
1781
- fclose($fp_dest);
1782
- $attachment = $dest;
1783
-
1784
- }
1785
-
1786
- } catch (Exception $e) {
1787
-
1788
- // close and remove dest file if created
1789
- if (isset($fp_dest) && is_resource($fp_dest)) {
1790
- fclose($fp_dest);
1791
- unlink($dest);
1792
- }
1793
-
1794
- // do not set any is case of error
1795
- $attachment = null;
1796
- $this->warning('Failed to extract attachment '.$name.': '.$e->getMessage());
1797
-
1798
- }
1799
-
1800
- // seek to the end of attachment
1801
- $this->fseek($offset + $length);
1802
-
1803
- return $attachment;
1804
- }
1805
-
1806
- }
1807
-
1808
-
1809
- class getid3_exception extends Exception
1810
- {
1811
- public $message;
1812
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /////////////////////////////////////////////////////////////////
3
+ /// getID3() by James Heinrich <info@getid3.org> //
4
+ // available at http://getid3.sourceforge.net //
5
+ // or http://www.getid3.org //
6
+ // also https://github.com/JamesHeinrich/getID3 //
7
+ /////////////////////////////////////////////////////////////////
8
+ // //
9
+ // Please see readme.txt for more information //
10
+ // ///
11
+ /////////////////////////////////////////////////////////////////
12
+
13
+ // define a constant rather than looking up every time it is needed
14
+ if (!defined('GETID3_OS_ISWINDOWS')) {
15
+ define('GETID3_OS_ISWINDOWS', (stripos(PHP_OS, 'WIN') === 0));
16
+ }
17
+ // Get base path of getID3() - ONCE
18
+ if (!defined('GETID3_INCLUDEPATH')) {
19
+ define('GETID3_INCLUDEPATH', dirname(__FILE__).DIRECTORY_SEPARATOR);
20
+ }
21
+ // Workaround Bug #39923 (https://bugs.php.net/bug.php?id=39923)
22
+ if (!defined('IMG_JPG') && defined('IMAGETYPE_JPEG')) {
23
+ define('IMG_JPG', IMAGETYPE_JPEG);
24
+ }
25
+
26
+ // attempt to define temp dir as something flexible but reliable
27
+ $temp_dir = ini_get('upload_tmp_dir');
28
+ if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) {
29
+ $temp_dir = '';
30
+ }
31
+ if (!$temp_dir && function_exists('sys_get_temp_dir')) { // sys_get_temp_dir added in PHP v5.2.1
32
+ // sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts
33
+ $temp_dir = sys_get_temp_dir();
34
+ }
35
+ $temp_dir = @realpath($temp_dir); // see https://github.com/JamesHeinrich/getID3/pull/10
36
+ $open_basedir = ini_get('open_basedir');
37
+ if ($open_basedir) {
38
+ // e.g. "/var/www/vhosts/getid3.org/httpdocs/:/tmp/"
39
+ $temp_dir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $temp_dir);
40
+ $open_basedir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $open_basedir);
41
+ if (substr($temp_dir, -1, 1) != DIRECTORY_SEPARATOR) {
42
+ $temp_dir .= DIRECTORY_SEPARATOR;
43
+ }
44
+ $found_valid_tempdir = false;
45
+ $open_basedirs = explode(PATH_SEPARATOR, $open_basedir);
46
+ foreach ($open_basedirs as $basedir) {
47
+ if (substr($basedir, -1, 1) != DIRECTORY_SEPARATOR) {
48
+ $basedir .= DIRECTORY_SEPARATOR;
49
+ }
50
+ if (preg_match('#^'.preg_quote($basedir).'#', $temp_dir)) {
51
+ $found_valid_tempdir = true;
52
+ break;
53
+ }
54
+ }
55
+ if (!$found_valid_tempdir) {
56
+ $temp_dir = '';
57
+ }
58
+ unset($open_basedirs, $found_valid_tempdir, $basedir);
59
+ }
60
+ if (!$temp_dir) {
61
+ $temp_dir = '*'; // invalid directory name should force tempnam() to use system default temp dir
62
+ }
63
+ // $temp_dir = '/something/else/'; // feel free to override temp dir here if it works better for your system
64
+ if (!defined('GETID3_TEMP_DIR')) {
65
+ define('GETID3_TEMP_DIR', $temp_dir);
66
+ }
67
+ unset($open_basedir, $temp_dir);
68
+
69
+ // End: Defines
70
+
71
+
72
+ class getID3
73
+ {
74
+ // public: Settings
75
+ public $encoding = 'UTF-8'; // CASE SENSITIVE! - i.e. (must be supported by iconv()). Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE
76
+ public $encoding_id3v1 = 'ISO-8859-1'; // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' or 'CP1252'
77
+
78
+ // public: Optional tag checks - disable for speed.
79
+ public $option_tag_id3v1 = true; // Read and process ID3v1 tags
80
+ public $option_tag_id3v2 = true; // Read and process ID3v2 tags
81
+ public $option_tag_lyrics3 = true; // Read and process Lyrics3 tags
82
+ public $option_tag_apetag = true; // Read and process APE tags
83
+ public $option_tags_process = true; // Copy tags to root key 'tags' and encode to $this->encoding
84
+ public $option_tags_html = true; // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
85
+
86
+ // public: Optional tag/comment calucations
87
+ public $option_extra_info = true; // Calculate additional info such as bitrate, channelmode etc
88
+
89
+ // public: Optional handling of embedded attachments (e.g. images)
90
+ public $option_save_attachments = true; // defaults to true (ATTACHMENTS_INLINE) for backward compatibility
91
+
92
+ // public: Optional calculations
93
+ public $option_md5_data = false; // Get MD5 sum of data part - slow
94
+ public $option_md5_data_source = false; // Use MD5 of source file if availble - only FLAC and OptimFROG
95
+ public $option_sha1_data = false; // Get SHA1 sum of data part - slow
96
+ public $option_max_2gb_check = null; // Check whether file is larger than 2GB and thus not supported by 32-bit PHP (null: auto-detect based on PHP_INT_MAX)
97
+
98
+ // public: Read buffer size in bytes
99
+ public $option_fread_buffer_size = 32768;
100
+
101
+ // Public variables
102
+ public $filename; // Filename of file being analysed.
103
+ public $fp; // Filepointer to file being analysed.
104
+ public $info; // Result array.
105
+ public $tempdir = GETID3_TEMP_DIR;
106
+ public $memory_limit = 0;
107
+
108
+ // Protected variables
109
+ protected $startup_error = '';
110
+ protected $startup_warning = '';
111
+
112
+ const VERSION = '1.9.12-201602240818';
113
+ const FREAD_BUFFER_SIZE = 32768;
114
+
115
+ const ATTACHMENTS_NONE = false;
116
+ const ATTACHMENTS_INLINE = true;
117
+
118
+ // public: constructor
119
+ public function __construct() {
120
+
121
+ // Check for PHP version
122
+ $required_php_version = '5.3.0';
123
+ if (version_compare(PHP_VERSION, $required_php_version, '<')) {
124
+ $this->startup_error .= 'getID3() requires PHP v'.$required_php_version.' or higher - you are running v'.PHP_VERSION;
125
+ return false;
126
+ }
127
+
128
+ // Check memory
129
+ $this->memory_limit = ini_get('memory_limit');
130
+ if (preg_match('#([0-9]+)M#i', $this->memory_limit, $matches)) {
131
+ // could be stored as "16M" rather than 16777216 for example
132
+ $this->memory_limit = $matches[1] * 1048576;
133
+ } elseif (preg_match('#([0-9]+)G#i', $this->memory_limit, $matches)) { // The 'G' modifier is available since PHP 5.1.0
134
+ // could be stored as "2G" rather than 2147483648 for example
135
+ $this->memory_limit = $matches[1] * 1073741824;
136
+ }
137
+ if ($this->memory_limit <= 0) {
138
+ // memory limits probably disabled
139
+ } elseif ($this->memory_limit <= 4194304) {
140
+ $this->startup_error .= 'PHP has less than 4MB available memory and will very likely run out. Increase memory_limit in php.ini';
141
+ } elseif ($this->memory_limit <= 12582912) {
142
+ $this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini';
143
+ }
144
+
145
+ // Check safe_mode off
146
+ if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
147
+ $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.');
148
+ }
149
+
150
+ if (intval(ini_get('mbstring.func_overload')) > 0) {
151
+ $this->warning('WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", this may break things.');
152
+ }
153
+
154
+ // Check for magic_quotes_runtime
155
+ if (function_exists('get_magic_quotes_runtime')) {
156
+ if (get_magic_quotes_runtime()) {
157
+ return $this->startup_error('magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).');
158
+ }
159
+ }
160
+
161
+ // Check for magic_quotes_gpc
162
+ if (function_exists('magic_quotes_gpc')) {
163
+ if (get_magic_quotes_gpc()) {
164
+ return $this->startup_error('magic_quotes_gpc must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_gpc(0) and set_magic_quotes_gpc(1).');
165
+ }
166
+ }
167
+
168
+ // Load support library
169
+ if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
170
+ $this->startup_error .= 'getid3.lib.php is missing or corrupt';
171
+ }
172
+
173
+ if ($this->option_max_2gb_check === null) {
174
+ $this->option_max_2gb_check = (PHP_INT_MAX <= 2147483647);
175
+ }
176
+
177
+
178
+ // Needed for Windows only:
179
+ // Define locations of helper applications for Shorten, VorbisComment, MetaFLAC
180
+ // as well as other helper functions such as head, tail, md5sum, etc
181
+ // This path cannot contain spaces, but the below code will attempt to get the
182
+ // 8.3-equivalent path automatically
183
+ // IMPORTANT: This path must include the trailing slash
184
+ if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) {
185
+
186
+ $helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path
187
+
188
+ if (!is_dir($helperappsdir)) {
189
+ $this->startup_warning .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist';
190
+ } elseif (strpos(realpath($helperappsdir), ' ') !== false) {
191
+ $DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir));
192
+ $path_so_far = array();
193
+ foreach ($DirPieces as $key => $value) {
194
+ if (strpos($value, ' ') !== false) {
195
+ if (!empty($path_so_far)) {
196
+ $commandline = 'dir /x '.escapeshellarg(implode(DIRECTORY_SEPARATOR, $path_so_far));
197
+ $dir_listing = `$commandline`;
198
+ $lines = explode("\n", $dir_listing);
199
+ foreach ($lines as $line) {
200
+ $line = trim($line);
201
+ if (preg_match('#^([0-9/]{10}) +([0-9:]{4,5}( [AP]M)?) +(<DIR>|[0-9,]+) +([^ ]{0,11}) +(.+)$#', $line, $matches)) {
202
+ list($dummy, $date, $time, $ampm, $filesize, $shortname, $filename) = $matches;
203
+ if ((strtoupper($filesize) == '<DIR>') && (strtolower($filename) == strtolower($value))) {
204
+ $value = $shortname;
205
+ }
206
+ }
207
+ }
208
+ } else {
209
+ $this->startup_warning .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary. You can run "dir /x" from the commandline to see the correct 8.3-style names.';
210
+ }
211
+ }
212
+ $path_so_far[] = $value;
213
+ }
214
+ $helperappsdir = implode(DIRECTORY_SEPARATOR, $path_so_far);
215
+ }
216
+ define('GETID3_HELPERAPPSDIR', $helperappsdir.DIRECTORY_SEPARATOR);
217
+ }
218
+
219
+ return true;
220
+ }
221
+
222
+ public function version() {
223
+ return self::VERSION;
224
+ }
225
+
226
+ public function fread_buffer_size() {
227
+ return $this->option_fread_buffer_size;
228
+ }
229
+
230
+
231
+ // public: setOption
232
+ public function setOption($optArray) {
233
+ if (!is_array($optArray) || empty($optArray)) {
234
+ return false;
235
+ }
236
+ foreach ($optArray as $opt => $val) {
237
+ if (isset($this->$opt) === false) {
238
+ continue;
239
+ }
240
+ $this->$opt = $val;
241
+ }
242
+ return true;
243
+ }
244
+
245
+
246
+ public function openfile($filename, $filesize=null) {
247
+ try {
248
+ if (!empty($this->startup_error)) {
249
+ throw new getid3_exception($this->startup_error);
250
+ }
251
+ if (!empty($this->startup_warning)) {
252
+ $this->warning($this->startup_warning);
253
+ }
254
+
255
+ // init result array and set parameters
256
+ $this->filename = $filename;
257
+ $this->info = array();
258
+ $this->info['GETID3_VERSION'] = $this->version();
259
+ $this->info['php_memory_limit'] = (($this->memory_limit > 0) ? $this->memory_limit : false);
260
+
261
+ // remote files not supported
262
+ if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
263
+ throw new getid3_exception('Remote files are not supported - please copy the file locally first');
264
+ }
265
+
266
+ $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
267
+ $filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename);
268
+
269
+ // open local file
270
+ //if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { // see http://www.getid3.org/phpBB3/viewtopic.php?t=1720
271
+ if ((is_readable($filename) || file_exists($filename)) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
272
+ // great
273
+ } else {
274
+ $errormessagelist = array();
275
+ if (!is_readable($filename)) {
276
+ $errormessagelist[] = '!is_readable';
277
+ }
278
+ if (!is_file($filename)) {
279
+ $errormessagelist[] = '!is_file';
280
+ }
281
+ if (!file_exists($filename)) {
282
+ $errormessagelist[] = '!file_exists';
283
+ }
284
+ if (empty($errormessagelist)) {
285
+ $errormessagelist[] = 'fopen failed';
286
+ }
287
+ throw new getid3_exception('Could not open "'.$filename.'" ('.implode('; ', $errormessagelist).')');
288
+ }
289
+
290
+ $this->info['filesize'] = (!is_null($filesize) ? $filesize : filesize($filename));
291
+ // set redundant parameters - might be needed in some include file
292
+ // filenames / filepaths in getID3 are always expressed with forward slashes (unix-style) for both Windows and other to try and minimize confusion
293
+ $filename = str_replace('\\', '/', $filename);
294
+ $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename)));
295
+ $this->info['filename'] = getid3_lib::mb_basename($filename);
296
+ $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename'];
297
+
298
+
299
+ // option_max_2gb_check
300
+ if ($this->option_max_2gb_check) {
301
+ // PHP (32-bit all, and 64-bit Windows) doesn't support integers larger than 2^31 (~2GB)
302
+ // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize
303
+ // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer
304
+ $fseek = fseek($this->fp, 0, SEEK_END);
305
+ if (($fseek < 0) || (($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) ||
306
+ ($this->info['filesize'] < 0) ||
307
+ (ftell($this->fp) < 0)) {
308
+ $real_filesize = getid3_lib::getFileSizeSyscall($this->info['filenamepath']);
309
+
310
+ if ($real_filesize === false) {
311
+ unset($this->info['filesize']);
312
+ fclose($this->fp);
313
+ throw new getid3_exception('Unable to determine actual filesize. File is most likely larger than '.round(PHP_INT_MAX / 1073741824).'GB and is not supported by PHP.');
314
+ } elseif (getid3_lib::intValueSupported($real_filesize)) {
315
+ unset($this->info['filesize']);
316
+ fclose($this->fp);
317
+ throw new getid3_exception('PHP seems to think the file is larger than '.round(PHP_INT_MAX / 1073741824).'GB, but filesystem reports it as '.number_format($real_filesize, 3).'GB, please report to info@getid3.org');
318
+ }
319
+ $this->info['filesize'] = $real_filesize;
320
+ $this->warning('File is larger than '.round(PHP_INT_MAX / 1073741824).'GB (filesystem reports it as '.number_format($real_filesize, 3).'GB) and is not properly supported by PHP.');
321
+ }
322
+ }
323
+
324
+ // set more parameters
325
+ $this->info['avdataoffset'] = 0;
326
+ $this->info['avdataend'] = $this->info['filesize'];
327
+ $this->info['fileformat'] = ''; // filled in later
328
+ $this->info['audio']['dataformat'] = ''; // filled in later, unset if not used
329
+ $this->info['video']['dataformat'] = ''; // filled in later, unset if not used
330
+ $this->info['tags'] = array(); // filled in later, unset if not used
331
+ $this->info['error'] = array(); // filled in later, unset if not used
332
+ $this->info['warning'] = array(); // filled in later, unset if not used
333
+ $this->info['comments'] = array(); // filled in later, unset if not used
334
+ $this->info['encoding'] = $this->encoding; // required by id3v2 and iso modules - can be unset at the end if desired
335
+
336
+ return true;
337
+
338
+ } catch (Exception $e) {
339
+ $this->error($e->getMessage());
340
+ }
341
+ return false;
342
+ }
343
+
344
+ // public: analyze file
345
+ public function analyze($filename, $filesize=null, $original_filename='') {
346
+ try {
347
+ if (!$this->openfile($filename, $filesize)) {
348
+ return $this->info;
349
+ }
350
+
351
+ // Handle tags
352
+ foreach (array('id3v2'=>'id3v2', 'id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) {
353
+ $option_tag = 'option_tag_'.$tag_name;
354
+ if ($this->$option_tag) {
355
+ $this->include_module('tag.'.$tag_name);
356
+ try {
357
+ $tag_class = 'getid3_'.$tag_name;
358
+ $tag = new $tag_class($this);
359
+ $tag->Analyze();
360
+ }
361
+ catch (getid3_exception $e) {
362
+ throw $e;
363
+ }
364
+ }
365
+ }
366
+ if (isset($this->info['id3v2']['tag_offset_start'])) {
367
+ $this->info['avdataoffset'] = max($this->info['avdataoffset'], $this->info['id3v2']['tag_offset_end']);
368
+ }
369
+ foreach (array('id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) {
370
+ if (isset($this->info[$tag_key]['tag_offset_start'])) {
371
+ $this->info['avdataend'] = min($this->info['avdataend'], $this->info[$tag_key]['tag_offset_start']);
372
+ }
373
+ }
374
+
375
+ // ID3v2 detection (NOT parsing), even if ($this->option_tag_id3v2 == false) done to make fileformat easier
376
+ if (!$this->option_tag_id3v2) {
377
+ fseek($this->fp, 0);
378
+ $header = fread($this->fp, 10);
379
+ if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) {
380
+ $this->info['id3v2']['header'] = true;
381
+ $this->info['id3v2']['majorversion'] = ord($header{3});
382
+ $this->info['id3v2']['minorversion'] = ord($header{4});
383
+ $this->info['avdataoffset'] += getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
384
+ }
385
+ }
386
+
387
+ // read 32 kb file data
388
+ fseek($this->fp, $this->info['avdataoffset']);
389
+ $formattest = fread($this->fp, 32774);
390
+
391
+ // determine format
392
+ $determined_format = $this->GetFileFormat($formattest, ($original_filename ? $original_filename : $filename));
393
+
394
+ // unable to determine file format
395
+ if (!$determined_format) {
396
+ fclose($this->fp);
397
+ return $this->error('unable to determine file format');
398
+ }
399
+
400
+ // check for illegal ID3 tags
401
+ if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
402
+ if ($determined_format['fail_id3'] === 'ERROR') {
403
+ fclose($this->fp);
404
+ return $this->error('ID3 tags not allowed on this file type.');
405
+ } elseif ($determined_format['fail_id3'] === 'WARNING') {
406
+ $this->warning('ID3 tags not allowed on this file type.');
407
+ }
408
+ }
409
+
410
+ // check for illegal APE tags
411
+ if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
412
+ if ($determined_format['fail_ape'] === 'ERROR') {
413
+ fclose($this->fp);
414
+ return $this->error('APE tags not allowed on this file type.');
415
+ } elseif ($determined_format['fail_ape'] === 'WARNING') {
416
+ $this->warning('APE tags not allowed on this file type.');
417
+ }
418
+ }
419
+
420
+ // set mime type
421
+ $this->info['mime_type'] = $determined_format['mime_type'];
422
+
423
+ // supported format signature pattern detected, but module deleted
424
+ if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) {
425
+ fclose($this->fp);
426
+ return $this->error('Format not supported, module "'.$determined_format['include'].'" was removed.');
427
+ }
428
+
429
+ // module requires iconv support
430
+ // Check encoding/iconv support
431
+ if (!empty($determined_format['iconv_req']) && !function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
432
+ $errormessage = 'iconv() support is required for this module ('.$determined_format['include'].') for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
433
+ if (GETID3_OS_ISWINDOWS) {
434
+ $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32';
435
+ } else {
436
+ $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
437
+ }
438
+ return $this->error($errormessage);
439
+ }
440
+
441
+ // include module
442
+ include_once(GETID3_INCLUDEPATH.$determined_format['include']);
443
+
444
+ // instantiate module class
445
+ $class_name = 'getid3_'.$determined_format['module'];
446
+ if (!class_exists($class_name)) {
447
+ return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.');
448
+ }
449
+ $class = new $class_name($this);
450
+ $class->Analyze();
451
+ unset($class);
452
+
453
+ // close file
454
+ fclose($this->fp);
455
+
456
+ // process all tags - copy to 'tags' and convert charsets
457
+ if ($this->option_tags_process) {
458
+ $this->HandleAllTags();
459
+ }
460
+
461
+ // perform more calculations
462
+ if ($this->option_extra_info) {
463
+ $this->ChannelsBitratePlaytimeCalculations();
464
+ $this->CalculateCompressionRatioVideo();
465
+ $this->CalculateCompressionRatioAudio();
466
+ $this->CalculateReplayGain();
467
+ $this->ProcessAudioStreams();
468
+ }
469
+
470
+ // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
471
+ if ($this->option_md5_data) {
472
+ // do not calc md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
473
+ if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
474
+ $this->getHashdata('md5');
475
+ }
476
+ }
477
+
478
+ // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
479
+ if ($this->option_sha1_data) {
480
+ $this->getHashdata('sha1');
481
+ }
482
+
483
+ // remove undesired keys
484
+ $this->CleanUp();
485
+
486
+ } catch (Exception $e) {
487
+ $this->error('Caught exception: '.$e->getMessage());
488
+ }
489
+
490
+ // return info array
491
+ return $this->info;
492
+ }
493
+
494
+
495
+ // private: error handling
496
+ public function error($message) {
497
+ $this->CleanUp();
498
+ if (!isset($this->info['error'])) {
499
+ $this->info['error'] = array();
500
+ }
501
+ $this->info['error'][] = $message;
502
+ return $this->info;
503
+ }
504
+
505
+
506
+ // private: warning handling
507
+ public function warning($message) {
508
+ $this->info['warning'][] = $message;
509
+ return true;
510
+ }
511
+
512
+
513
+ // private: CleanUp
514
+ private function CleanUp() {
515
+
516
+ // remove possible empty keys
517
+ $AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate');
518
+ foreach ($AVpossibleEmptyKeys as $dummy => $key) {
519
+ if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) {
520
+ unset($this->info['audio'][$key]);
521
+ }
522
+ if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) {
523
+ unset($this->info['video'][$key]);
524
+ }
525
+ }
526
+
527
+ // remove empty root keys
528
+ if (!empty($this->info)) {
529
+ foreach ($this->info as $key => $value) {
530
+ if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) {
531
+ unset($this->info[$key]);
532
+ }
533
+ }
534
+ }
535
+
536
+ // remove meaningless entries from unknown-format files
537
+ if (empty($this->info['fileformat'])) {
538
+ if (isset($this->info['avdataoffset'])) {
539
+ unset($this->info['avdataoffset']);
540
+ }
541
+ if (isset($this->info['avdataend'])) {
542
+ unset($this->info['avdataend']);
543
+ }
544
+ }
545
+
546
+ // remove possible duplicated identical entries
547
+ if (!empty($this->info['error'])) {
548
+ $this->info['error'] = array_values(array_unique($this->info['error']));
549
+ }
550
+ if (!empty($this->info['warning'])) {
551
+ $this->info['warning'] = array_values(array_unique($this->info['warning']));
552
+ }
553
+
554
+ // remove "global variable" type keys
555
+ unset($this->info['php_memory_limit']);
556
+
557
+ return true;
558
+ }
559
+
560
+
561
+ // return array containing information about all supported formats
562
+ public function GetFileFormatArray() {
563
+ static $format_info = array();
564
+ if (empty($format_info)) {
565
+ $format_info = array(
566
+
567
+ // Audio formats
568
+
569
+ // AC-3 - audio - Dolby AC-3 / Dolby Digital
570
+ 'ac3' => array(
571
+ 'pattern' => '^\x0B\x77',
572
+ 'group' => 'audio',
573
+ 'module' => 'ac3',
574
+ 'mime_type' => 'audio/ac3',
575
+ ),
576
+
577
+ // AAC - audio - Advanced Audio Coding (AAC) - ADIF format
578
+ 'adif' => array(
579
+ 'pattern' => '^ADIF',
580
+ 'group' => 'audio',
581
+ 'module' => 'aac',
582
+ 'mime_type' => 'application/octet-stream',
583
+ 'fail_ape' => 'WARNING',
584
+ ),
585
+
586
+ /*
587
+ // AA - audio - Audible Audiobook
588
+ 'aa' => array(
589
+ 'pattern' => '^.{4}\x57\x90\x75\x36',
590
+ 'group' => 'audio',
591
+ 'module' => 'aa',
592
+ 'mime_type' => 'audio/audible',
593
+ ),
594
+ */
595
+ // AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
596
+ 'adts' => array(
597
+ 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]',
598
+ 'group' => 'audio',
599
+ 'module' => 'aac',
600
+ 'mime_type' => 'application/octet-stream',
601
+ 'fail_ape' => 'WARNING',
602
+ ),
603
+
604
+
605
+ // AU - audio - NeXT/Sun AUdio (AU)
606
+ 'au' => array(
607
+ 'pattern' => '^\.snd',
608
+ 'group' => 'audio',
609
+ 'module' => 'au',
610
+ 'mime_type' => 'audio/basic',
611
+ ),
612
+
613
+ // AMR - audio - Adaptive Multi Rate
614
+ 'amr' => array(
615
+ 'pattern' => '^\x23\x21AMR\x0A', // #!AMR[0A]
616
+ 'group' => 'audio',
617
+ 'module' => 'amr',
618
+ 'mime_type' => 'audio/amr',
619
+ ),
620
+
621
+ // AVR - audio - Audio Visual Research
622
+ 'avr' => array(
623
+ 'pattern' => '^2BIT',
624
+ 'group' => 'audio',
625
+ 'module' => 'avr',
626
+ 'mime_type' => 'application/octet-stream',
627
+ ),
628
+
629
+ // BONK - audio - Bonk v0.9+
630
+ 'bonk' => array(
631
+ 'pattern' => '^\x00(BONK|INFO|META| ID3)',
632
+ 'group' => 'audio',
633
+ 'module' => 'bonk',
634
+ 'mime_type' => 'audio/xmms-bonk',
635
+ ),
636
+
637
+ // DSF - audio - Direct Stream Digital (DSD) Storage Facility files (DSF) - https://en.wikipedia.org/wiki/Direct_Stream_Digital
638
+ 'dsf' => array(
639
+ 'pattern' => '^DSD ', // including trailing space: 44 53 44 20
640
+ 'group' => 'audio',
641
+ 'module' => 'dsf',
642
+ 'mime_type' => 'audio/dsd',
643
+ ),
644
+
645
+ // DSS - audio - Digital Speech Standard
646
+ 'dss' => array(
647
+ 'pattern' => '^[\x02-\x03]ds[s2]',
648
+ 'group' => 'audio',
649
+ 'module' => 'dss',
650
+ 'mime_type' => 'application/octet-stream',
651
+ ),
652
+
653
+ // DTS - audio - Dolby Theatre System
654
+ 'dts' => array(
655
+ 'pattern' => '^\x7F\xFE\x80\x01',
656
+ 'group' => 'audio',
657
+ 'module' => 'dts',
658
+ 'mime_type' => 'audio/dts',
659
+ ),
660
+
661
+ // FLAC - audio - Free Lossless Audio Codec
662
+ 'flac' => array(
663
+ 'pattern' => '^fLaC',
664
+ 'group' => 'audio',
665
+ 'module' => 'flac',
666
+ 'mime_type' => 'audio/x-flac',
667
+ ),
668
+
669
+ // LA - audio - Lossless Audio (LA)
670
+ 'la' => array(
671
+ 'pattern' => '^LA0[2-4]',
672
+ 'group' => 'audio',
673
+ 'module' => 'la',
674
+ 'mime_type' => 'application/octet-stream',
675
+ ),
676
+
677
+ // LPAC - audio - Lossless Predictive Audio Compression (LPAC)
678
+ 'lpac' => array(
679
+ 'pattern' => '^LPAC',
680
+ 'group' => 'audio',
681
+ 'module' => 'lpac',
682
+ 'mime_type' => 'application/octet-stream',
683
+ ),
684
+
685
+ // MIDI - audio - MIDI (Musical Instrument Digital Interface)
686
+ 'midi' => array(
687
+ 'pattern' => '^MThd',
688
+ 'group' => 'audio',
689
+ 'module' => 'midi',
690
+ 'mime_type' => 'audio/midi',
691
+ ),
692
+
693
+ // MAC - audio - Monkey's Audio Compressor
694
+ 'mac' => array(
695
+ 'pattern' => '^MAC ',
696
+ 'group' => 'audio',
697
+ 'module' => 'monkey',
698
+ 'mime_type' => 'application/octet-stream',
699
+ ),
700
+
701
+ // has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available
702
+ // // MOD - audio - MODule (assorted sub-formats)
703
+ // 'mod' => array(
704
+ // 'pattern' => '^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)',
705
+ // 'group' => 'audio',
706
+ // 'module' => 'mod',
707
+ // 'option' => 'mod',
708
+ // 'mime_type' => 'audio/mod',
709
+ // ),
710
+
711
+ // MOD - audio - MODule (Impulse Tracker)
712
+ 'it' => array(
713
+ 'pattern' => '^IMPM',
714
+ 'group' => 'audio',
715
+ 'module' => 'mod',
716
+ //'option' => 'it',
717
+ 'mime_type' => 'audio/it',
718
+ ),
719
+
720
+ // MOD - audio - MODule (eXtended Module, various sub-formats)
721
+ 'xm' => array(
722
+ 'pattern' => '^Extended Module',
723
+ 'group' => 'audio',
724
+ 'module' => 'mod',
725
+ //'option' => 'xm',
726
+ 'mime_type' => 'audio/xm',
727
+ ),
728
+
729
+ // MOD - audio - MODule (ScreamTracker)
730
+ 's3m' => array(
731
+ 'pattern' => '^.{44}SCRM',
732
+ 'group' => 'audio',
733
+ 'module' => 'mod',
734
+ //'option' => 's3m',
735
+ 'mime_type' => 'audio/s3m',
736
+ ),
737
+
738
+ // MPC - audio - Musepack / MPEGplus
739
+ 'mpc' => array(
740
+ 'pattern' => '^(MPCK|MP\+|[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])',
741
+ 'group' => 'audio',
742
+ 'module' => 'mpc',
743
+ 'mime_type' => 'audio/x-musepack',
744
+ ),
745
+
746
+ // MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS)
747
+ 'mp3' => array(
748
+ 'pattern' => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\x0B\x10-\x1B\x20-\x2B\x30-\x3B\x40-\x4B\x50-\x5B\x60-\x6B\x70-\x7B\x80-\x8B\x90-\x9B\xA0-\xAB\xB0-\xBB\xC0-\xCB\xD0-\xDB\xE0-\xEB\xF0-\xFB]',
749
+ 'group' => 'audio',
750
+ 'module' => 'mp3',
751
+ 'mime_type' => 'audio/mpeg',
752
+ ),
753
+
754
+ // OFR - audio - OptimFROG
755
+ 'ofr' => array(
756
+ 'pattern' => '^(\*RIFF|OFR)',
757
+ 'group' => 'audio',
758
+ 'module' => 'optimfrog',
759
+ 'mime_type' => 'application/octet-stream',
760
+ ),
761
+
762
+ // RKAU - audio - RKive AUdio compressor
763
+ 'rkau' => array(
764
+ 'pattern' => '^RKA',
765
+ 'group' => 'audio',
766
+ 'module' => 'rkau',
767
+ 'mime_type' => 'application/octet-stream',
768
+ ),
769
+
770
+ // SHN - audio - Shorten
771
+ 'shn' => array(
772
+ 'pattern' => '^ajkg',
773
+ 'group' => 'audio',
774
+ 'module' => 'shorten',
775
+ 'mime_type' => 'audio/xmms-shn',
776
+ 'fail_id3' => 'ERROR',
777
+ 'fail_ape' => 'ERROR',
778
+ ),
779
+
780
+ // TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org)
781
+ 'tta' => array(
782
+ 'pattern' => '^TTA', // could also be '^TTA(\x01|\x02|\x03|2|1)'
783
+ 'group' => 'audio',
784
+ 'module' => 'tta',
785
+ 'mime_type' => 'application/octet-stream',
786
+ ),
787
+
788
+ // VOC - audio - Creative Voice (VOC)
789
+ 'voc' => array(
790
+ 'pattern' => '^Creative Voice File',
791
+ 'group' => 'audio',
792
+ 'module' => 'voc',
793
+ 'mime_type' => 'audio/voc',
794
+ ),
795
+
796
+ // VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF)
797
+ 'vqf' => array(
798
+ 'pattern' => '^TWIN',
799
+ 'group' => 'audio',
800
+ 'module' => 'vqf',
801
+ 'mime_type' => 'application/octet-stream',
802
+ ),
803
+
804
+ // WV - audio - WavPack (v4.0+)
805
+ 'wv' => array(
806
+ 'pattern' => '^wvpk',
807
+ 'group' => 'audio',
808
+ 'module' => 'wavpack',
809
+ 'mime_type' => 'application/octet-stream',
810
+ ),
811
+
812
+
813
+ // Audio-Video formats
814
+
815
+ // ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio
816
+ 'asf' => array(
817
+ 'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C',
818
+ 'group' => 'audio-video',
819
+ 'module' => 'asf',
820
+ 'mime_type' => 'video/x-ms-asf',
821
+ 'iconv_req' => false,
822
+ ),
823
+
824
+ // BINK - audio/video - Bink / Smacker
825
+ 'bink' => array(
826
+ 'pattern' => '^(BIK|SMK)',
827
+ 'group' => 'audio-video',
828
+ 'module' => 'bink',
829
+ 'mime_type' => 'application/octet-stream',
830
+ ),
831
+
832
+ // FLV - audio/video - FLash Video
833
+ 'flv' => array(
834
+ 'pattern' => '^FLV\x01',
835
+ 'group' => 'audio-video',
836
+ 'module' => 'flv',
837
+ 'mime_type' => 'video/x-flv',
838
+ ),
839
+
840
+ // MKAV - audio/video - Mastroka
841
+ 'matroska' => array(
842
+ 'pattern' => '^\x1A\x45\xDF\xA3',
843
+ 'group' => 'audio-video',
844
+ 'module' => 'matroska',
845
+ 'mime_type' => 'video/x-matroska', // may also be audio/x-matroska
846
+ ),
847
+
848
+ // MPEG - audio/video - MPEG (Moving Pictures Experts Group)
849
+ 'mpeg' => array(
850
+ 'pattern' => '^\x00\x00\x01(\xBA|\xB3)',
851
+ 'group' => 'audio-video',
852
+ 'module' => 'mpeg',
853
+ 'mime_type' => 'video/mpeg',
854
+ ),
855
+
856
+ // NSV - audio/video - Nullsoft Streaming Video (NSV)
857
+ 'nsv' => array(
858
+ 'pattern' => '^NSV[sf]',
859
+ 'group' => 'audio-video',
860
+ 'module' => 'nsv',
861
+ 'mime_type' => 'application/octet-stream',
862
+ ),
863
+
864
+ // Ogg - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*))
865
+ 'ogg' => array(
866
+ 'pattern' => '^OggS',
867
+ 'group' => 'audio',
868
+ 'module' => 'ogg',
869
+ 'mime_type' => 'application/ogg',
870
+ 'fail_id3' => 'WARNING',
871
+ 'fail_ape' => 'WARNING',
872
+ ),
873
+
874
+ // QT - audio/video - Quicktime
875
+ 'quicktime' => array(
876
+ 'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)',
877
+ 'group' => 'audio-video',
878
+ 'module' => 'quicktime',
879
+ 'mime_type' => 'video/quicktime',
880
+ ),
881
+
882
+ // RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF)
883
+ 'riff' => array(
884
+ 'pattern' => '^(RIFF|SDSS|FORM)',
885
+ 'group' => 'audio-video',
886
+ 'module' => 'riff',
887
+ 'mime_type' => 'audio/x-wav',
888
+ 'fail_ape' => 'WARNING',
889
+ ),
890
+
891
+ // Real - audio/video - RealAudio, RealVideo
892
+ 'real' => array(
893
+ 'pattern' => '^(\\.RMF|\\.ra)',
894
+ 'group' => 'audio-video',
895
+ 'module' => 'real',
896
+ 'mime_type' => 'audio/x-realaudio',
897
+ ),
898
+
899
+ // SWF - audio/video - ShockWave Flash
900
+ 'swf' => array(
901
+ 'pattern' => '^(F|C)WS',
902
+ 'group' => 'audio-video',
903
+ 'module' => 'swf',
904
+ 'mime_type' => 'application/x-shockwave-flash',
905
+ ),
906
+
907
+ // TS - audio/video - MPEG-2 Transport Stream
908
+ 'ts' => array(
909
+ 'pattern' => '^(\x47.{187}){10,}', // packets are 188 bytes long and start with 0x47 "G". Check for at least 10 packets matching this pattern
910
+ 'group' => 'audio-video',
911
+ 'module' => 'ts',
912
+ 'mime_type' => 'video/MP2T',
913
+ ),
914
+
915
+
916
+ // Still-Image formats
917
+
918
+ // BMP - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4)
919
+ 'bmp' => array(
920
+ 'pattern' => '^BM',
921
+ 'group' => 'graphic',
922
+ 'module' => 'bmp',
923
+ 'mime_type' => 'image/bmp',
924
+ 'fail_id3' => 'ERROR',
925
+ 'fail_ape' => 'ERROR',
926
+ ),
927
+
928
+ // GIF - still image - Graphics Interchange Format
929
+ 'gif' => array(
930
+ 'pattern' => '^GIF',
931
+ 'group' => 'graphic',
932
+ 'module' => 'gif',
933
+ 'mime_type' => 'image/gif',
934
+ 'fail_id3' => 'ERROR',
935
+ 'fail_ape' => 'ERROR',
936
+ ),
937
+
938
+ // JPEG - still image - Joint Photographic Experts Group (JPEG)
939
+ 'jpg' => array(
940
+ 'pattern' => '^\xFF\xD8\xFF',
941
+ 'group' => 'graphic',
942
+ 'module' => 'jpg',
943
+ 'mime_type' => 'image/jpeg',
944
+ 'fail_id3' => 'ERROR',
945
+ 'fail_ape' => 'ERROR',
946
+ ),
947
+
948
+ // PCD - still image - Kodak Photo CD
949
+ 'pcd' => array(
950
+ 'pattern' => '^.{2048}PCD_IPI\x00',
951
+ 'group' => 'graphic',
952
+ 'module' => 'pcd',
953
+ 'mime_type' => 'image/x-photo-cd',
954
+ 'fail_id3' => 'ERROR',
955
+ 'fail_ape' => 'ERROR',
956
+ ),
957
+
958
+
959
+ // PNG - still image - Portable Network Graphics (PNG)
960
+ 'png' => array(
961
+ 'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A',
962
+ 'group' => 'graphic',
963
+ 'module' => 'png',
964
+ 'mime_type' => 'image/png',
965
+ 'fail_id3' => 'ERROR',
966
+ 'fail_ape' => 'ERROR',
967
+ ),
968
+
969
+
970
+ // SVG - still image - Scalable Vector Graphics (SVG)
971
+ 'svg' => array(
972
+ 'pattern' => '(<!DOCTYPE svg PUBLIC |xmlns="http:\/\/www\.w3\.org\/2000\/svg")',
973
+ 'group' => 'graphic',
974
+ 'module' => 'svg',
975
+ 'mime_type' => 'image/svg+xml',
976
+ 'fail_id3' => 'ERROR',
977
+ 'fail_ape' => 'ERROR',
978
+ ),
979
+
980
+
981
+ // TIFF - still image - Tagged Information File Format (TIFF)
982
+ 'tiff' => array(
983
+ 'pattern' => '^(II\x2A\x00|MM\x00\x2A)',
984
+ 'group' => 'graphic',
985
+ 'module' => 'tiff',
986
+ 'mime_type' => 'image/tiff',
987
+ 'fail_id3' => 'ERROR',
988
+ 'fail_ape' => 'ERROR',
989
+ ),
990
+
991
+
992
+ // EFAX - still image - eFax (TIFF derivative)
993
+ 'efax' => array(
994
+ 'pattern' => '^\xDC\xFE',
995
+ 'group' => 'graphic',
996
+ 'module' => 'efax',
997
+ 'mime_type' => 'image/efax',
998
+ 'fail_id3' => 'ERROR',
999
+ 'fail_ape' => 'ERROR',
1000
+ ),
1001
+
1002
+
1003
+ // Data formats
1004
+
1005
+ // ISO - data - International Standards Organization (ISO) CD-ROM Image
1006
+ 'iso' => array(
1007
+ 'pattern' => '^.{32769}CD001',
1008
+ 'group' => 'misc',
1009
+ 'module' => 'iso',
1010
+ 'mime_type' => 'application/octet-stream',
1011
+ 'fail_id3' => 'ERROR',
1012
+ 'fail_ape' => 'ERROR',
1013
+ 'iconv_req' => false,
1014
+ ),
1015
+
1016
+ // RAR - data - RAR compressed data
1017
+ 'rar' => array(
1018
+ 'pattern' => '^Rar\!',
1019
+ 'group' => 'archive',
1020
+ 'module' => 'rar',
1021
+ 'mime_type' => 'application/octet-stream',
1022
+ 'fail_id3' => 'ERROR',
1023
+ 'fail_ape' => 'ERROR',
1024
+ ),
1025
+
1026
+ // SZIP - audio/data - SZIP compressed data
1027
+ 'szip' => array(
1028
+ 'pattern' => '^SZ\x0A\x04',
1029
+ 'group' => 'archive',
1030
+ 'module' => 'szip',
1031
+ 'mime_type' => 'application/octet-stream',
1032
+ 'fail_id3' => 'ERROR',
1033
+ 'fail_ape' => 'ERROR',
1034
+ ),
1035
+
1036
+ // TAR - data - TAR compressed data
1037
+ 'tar' => array(
1038
+ 'pattern' => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}',
1039
+ 'group' => 'archive',
1040
+ 'module' => 'tar',
1041
+ 'mime_type' => 'application/x-tar',
1042
+ 'fail_id3' => 'ERROR',
1043
+ 'fail_ape' => 'ERROR',
1044
+ ),
1045
+
1046
+ // GZIP - data - GZIP compressed data
1047
+ 'gz' => array(
1048
+ 'pattern' => '^\x1F\x8B\x08',
1049
+ 'group' => 'archive',
1050
+ 'module' => 'gzip',
1051
+ 'mime_type' => 'application/x-gzip',
1052
+ 'fail_id3' => 'ERROR',
1053
+ 'fail_ape' => 'ERROR',
1054
+ ),
1055
+
1056
+ // ZIP - data - ZIP compressed data
1057
+ 'zip' => array(
1058
+ 'pattern' => '^PK\x03\x04',
1059
+ 'group' => 'archive',
1060
+ 'module' => 'zip',
1061
+ 'mime_type' => 'application/zip',
1062
+ 'fail_id3' => 'ERROR',
1063
+ 'fail_ape' => 'ERROR',
1064
+ ),
1065
+
1066
+
1067
+ // Misc other formats
1068
+
1069
+ // PAR2 - data - Parity Volume Set Specification 2.0
1070
+ 'par2' => array (
1071
+ 'pattern' => '^PAR2\x00PKT',
1072
+ 'group' => 'misc',
1073
+ 'module' => 'par2',
1074
+ 'mime_type' => 'application/octet-stream',
1075
+ 'fail_id3' => 'ERROR',
1076
+ 'fail_ape' => 'ERROR',
1077
+ ),
1078
+
1079
+ // PDF - data - Portable Document Format
1080
+ 'pdf' => array(
1081
+ 'pattern' => '^\x25PDF',
1082
+ 'group' => 'misc',
1083
+ 'module' => 'pdf',
1084
+ 'mime_type' => 'application/pdf',
1085
+ 'fail_id3' => 'ERROR',
1086
+ 'fail_ape' => 'ERROR',
1087
+ ),
1088
+
1089
+ // MSOFFICE - data - ZIP compressed data
1090
+ 'msoffice' => array(
1091
+ 'pattern' => '^\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1', // D0CF11E == DOCFILE == Microsoft Office Document
1092
+ 'group' => 'misc',
1093
+ 'module' => 'msoffice',
1094
+ 'mime_type' => 'application/octet-stream',
1095
+ 'fail_id3' => 'ERROR',
1096
+ 'fail_ape' => 'ERROR',
1097
+ ),
1098
+
1099
+ // CUE - data - CUEsheet (index to single-file disc images)
1100
+ 'cue' => array(
1101
+ 'pattern' => '', // empty pattern means cannot be automatically detected, will fall through all other formats and match based on filename and very basic file contents
1102
+ 'group' => 'misc',
1103
+ 'module' => 'cue',
1104
+ 'mime_type' => 'application/octet-stream',
1105
+ ),
1106
+
1107
+ );
1108
+ }
1109
+
1110
+ return $format_info;
1111
+ }
1112
+
1113
+
1114
+
1115
+ public function GetFileFormat(&$filedata, $filename='') {
1116
+ // this function will determine the format of a file based on usually
1117
+ // the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG,
1118
+ // and in the case of ISO CD image, 6 bytes offset 32kb from the start
1119
+ // of the file).
1120
+
1121
+ // Identify file format - loop through $format_info and detect with reg expr
1122
+ foreach ($this->GetFileFormatArray() as $format_name => $info) {
1123
+ // The /s switch on preg_match() forces preg_match() NOT to treat
1124
+ // newline (0x0A) characters as special chars but do a binary match
1125
+ if (!empty($info['pattern']) && preg_match('#'.$info['pattern'].'#s', $filedata)) {
1126
+ $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1127
+ return $info;
1128
+ }
1129
+ }
1130
+
1131
+
1132
+ if (preg_match('#\.mp[123a]$#i', $filename)) {
1133
+ // Too many mp3 encoders on the market put gabage in front of mpeg files
1134
+ // use assume format on these if format detection failed
1135
+ $GetFileFormatArray = $this->GetFileFormatArray();
1136
+ $info = $GetFileFormatArray['mp3'];
1137
+ $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1138
+ return $info;
1139
+ } elseif (preg_match('/\.cue$/i', $filename) && preg_match('#FILE "[^"]+" (BINARY|MOTOROLA|AIFF|WAVE|MP3)#', $filedata)) {
1140
+ // there's not really a useful consistent "magic" at the beginning of .cue files to identify them
1141
+ // so until I think of something better, just go by filename if all other format checks fail
1142
+ // and verify there's at least one instance of "TRACK xx AUDIO" in the file
1143
+ $GetFileFormatArray = $this->GetFileFormatArray();
1144
+ $info = $GetFileFormatArray['cue'];
1145
+ $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1146
+ return $info;
1147
+ }
1148
+
1149
+ return false;
1150
+ }
1151
+
1152
+
1153
+ // converts array to $encoding charset from $this->encoding
1154
+ public function CharConvert(&$array, $encoding) {
1155
+
1156
+ // identical encoding - end here
1157
+ if ($encoding == $this->encoding) {
1158
+ return;
1159
+ }
1160
+
1161
+ // loop thru array
1162
+ foreach ($array as $key => $value) {
1163
+
1164
+ // go recursive
1165
+ if (is_array($value)) {
1166
+ $this->CharConvert($array[$key], $encoding);
1167
+ }
1168
+
1169
+ // convert string
1170
+ elseif (is_string($value)) {
1171
+ $array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value));
1172
+ }
1173
+ }
1174
+ }
1175
+
1176
+
1177
+ public function HandleAllTags() {
1178
+
1179
+ // key name => array (tag name, character encoding)
1180
+ static $tags;
1181
+ if (empty($tags)) {
1182
+ $tags = array(
1183
+ 'asf' => array('asf' , 'UTF-16LE'),
1184
+ 'midi' => array('midi' , 'ISO-8859-1'),
1185
+ 'nsv' => array('nsv' , 'ISO-8859-1'),
1186
+ 'ogg' => array('vorbiscomment' , 'UTF-8'),
1187
+ 'png' => array('png' , 'UTF-8'),
1188
+ 'tiff' => array('tiff' , 'ISO-8859-1'),
1189
+ 'quicktime' => array('quicktime' , 'UTF-8'),
1190
+ 'real' => array('real' , 'ISO-8859-1'),
1191
+ 'vqf' => array('vqf' , 'ISO-8859-1'),
1192
+ 'zip' => array('zip' , 'ISO-8859-1'),
1193
+ 'riff' => array('riff' , 'ISO-8859-1'),
1194
+ 'lyrics3' => array('lyrics3' , 'ISO-8859-1'),
1195
+ 'id3v1' => array('id3v1' , $this->encoding_id3v1),
1196
+ 'id3v2' => array('id3v2' , 'UTF-8'), // not according to the specs (every frame can have a different encoding), but getID3() force-converts all encodings to UTF-8
1197
+ 'ape' => array('ape' , 'UTF-8'),
1198
+ 'cue' => array('cue' , 'ISO-8859-1'),
1199
+ 'matroska' => array('matroska' , 'UTF-8'),
1200
+ 'flac' => array('vorbiscomment' , 'UTF-8'),
1201
+ 'divxtag' => array('divx' , 'ISO-8859-1'),
1202
+ 'iptc' => array('iptc' , 'ISO-8859-1'),
1203
+ );
1204
+ }
1205
+
1206
+ // loop through comments array
1207
+ foreach ($tags as $comment_name => $tagname_encoding_array) {
1208
+ list($tag_name, $encoding) = $tagname_encoding_array;
1209
+
1210
+ // fill in default encoding type if not already present
1211
+ if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) {
1212
+ $this->info[$comment_name]['encoding'] = $encoding;
1213
+ }
1214
+
1215
+ // copy comments if key name set
1216
+ if (!empty($this->info[$comment_name]['comments'])) {
1217
+ foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) {
1218
+ foreach ($valuearray as $key => $value) {
1219
+ if (is_string($value)) {
1220
+ $value = trim($value, " \r\n\t"); // do not trim nulls from $value!! Unicode characters will get mangled if trailing nulls are removed!
1221
+ }
1222
+ if ($value) {
1223
+ if (!is_numeric($key)) {
1224
+ $this->info['tags'][trim($tag_name)][trim($tag_key)][$key] = $value;
1225
+ } else {
1226
+ $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value;
1227
+ }
1228
+ }
1229
+ }
1230
+ if ($tag_key == 'picture') {
1231
+ unset($this->info[$comment_name]['comments'][$tag_key]);
1232
+ }
1233
+ }
1234
+
1235
+ if (!isset($this->info['tags'][$tag_name])) {
1236
+ // comments are set but contain nothing but empty strings, so skip
1237
+ continue;
1238
+ }
1239
+
1240
+ if ($this->option_tags_html) {
1241
+ foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
1242
+ $this->info['tags_html'][$tag_name][$tag_key] = getid3_lib::recursiveMultiByteCharString2HTML($valuearray, $encoding);
1243
+ }
1244
+ }
1245
+
1246
+ // ID3v1 encoding detection hack start
1247
+ // ID3v1 is defined as always using ISO-8859-1 encoding, but it is not uncommon to find files tagged with ID3v1 using Windows-1251 or other character sets
1248
+ // Since ID3v1 has no concept of character sets there is no certain way to know we have the correct non-ISO-8859-1 character set, but we can guess
1249
+ if ($comment_name == 'id3v1') {
1250
+ if ($encoding == 'ISO-8859-1') {
1251
+ if (function_exists('iconv')) {
1252
+ foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
1253
+ foreach ($valuearray as $key => $value) {
1254
+ if (preg_match('#^[\\x80-\\xFF]+$#', $value)) {
1255
+ foreach (array('windows-1251', 'KOI8-R') as $id3v1_bad_encoding) {
1256
+ if (@iconv($id3v1_bad_encoding, $id3v1_bad_encoding, $value) === $value) {
1257
+ $encoding = $id3v1_bad_encoding;
1258
+ break 3;
1259
+ }
1260
+ }
1261
+ }
1262
+ }
1263
+ }
1264
+ }
1265
+ }
1266
+ }
1267
+ // ID3v1 encoding detection hack end
1268
+
1269
+ $this->CharConvert($this->info['tags'][$tag_name], $encoding); // only copy gets converted!
1270
+ }
1271
+
1272
+ }
1273
+
1274
+ // pictures can take up a lot of space, and we don't need multiple copies of them
1275
+ // let there be a single copy in [comments][picture], and not elsewhere
1276
+ if (!empty($this->info['tags'])) {
1277
+ $unset_keys = array('tags', 'tags_html');
1278
+ foreach ($this->info['tags'] as $tagtype => $tagarray) {
1279
+ foreach ($tagarray as $tagname => $tagdata) {
1280
+ if ($tagname == 'picture') {
1281
+ foreach ($tagdata as $key => $tagarray) {
1282
+ $this->info['comments']['picture'][] = $tagarray;
1283
+ if (isset($tagarray['data']) && isset($tagarray['image_mime'])) {
1284
+ if (isset($this->info['tags'][$tagtype][$tagname][$key])) {
1285
+ unset($this->info['tags'][$tagtype][$tagname][$key]);
1286
+ }
1287
+ if (isset($this->info['tags_html'][$tagtype][$tagname][$key])) {
1288
+ unset($this->info['tags_html'][$tagtype][$tagname][$key]);
1289
+ }
1290
+ }
1291
+ }
1292
+ }
1293
+ }
1294
+ foreach ($unset_keys as $unset_key) {
1295
+ // remove possible empty keys from (e.g. [tags][id3v2][picture])
1296
+ if (empty($this->info[$unset_key][$tagtype]['picture'])) {
1297
+ unset($this->info[$unset_key][$tagtype]['picture']);
1298
+ }
1299
+ if (empty($this->info[$unset_key][$tagtype])) {
1300
+ unset($this->info[$unset_key][$tagtype]);
1301
+ }
1302
+ if (empty($this->info[$unset_key])) {
1303
+ unset($this->info[$unset_key]);
1304
+ }
1305
+ }
1306
+ // remove duplicate copy of picture data from (e.g. [id3v2][comments][picture])
1307
+ if (isset($this->info[$tagtype]['comments']['picture'])) {
1308
+ unset($this->info[$tagtype]['comments']['picture']);
1309
+ }
1310
+ if (empty($this->info[$tagtype]['comments'])) {
1311
+ unset($this->info[$tagtype]['comments']);
1312
+ }
1313
+ if (empty($this->info[$tagtype])) {
1314
+ unset($this->info[$tagtype]);
1315
+ }
1316
+ }
1317
+ }
1318
+ return true;
1319
+ }
1320
+
1321
+ public function getHashdata($algorithm) {
1322
+ switch ($algorithm) {
1323
+ case 'md5':
1324
+ case 'sha1':
1325
+ break;
1326
+
1327
+ default:
1328
+ return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()');
1329
+ break;
1330
+ }
1331
+
1332
+ if (!empty($this->info['fileformat']) && !empty($this->info['dataformat']) && ($this->info['fileformat'] == 'ogg') && ($this->info['audio']['dataformat'] == 'vorbis')) {
1333
+
1334
+ // We cannot get an identical md5_data value for Ogg files where the comments
1335
+ // span more than 1 Ogg page (compared to the same audio data with smaller
1336
+ // comments) using the normal getID3() method of MD5'ing the data between the
1337
+ // end of the comments and the end of the file (minus any trailing tags),
1338
+ // because the page sequence numbers of the pages that the audio data is on
1339
+ // do not match. Under normal circumstances, where comments are smaller than
1340
+ // the nominal 4-8kB page size, then this is not a problem, but if there are
1341
+ // very large comments, the only way around it is to strip off the comment
1342
+ // tags with vorbiscomment and MD5 that file.
1343
+ // This procedure must be applied to ALL Ogg files, not just the ones with
1344
+ // comments larger than 1 page, because the below method simply MD5's the
1345
+ // whole file with the comments stripped, not just the portion after the
1346
+ // comments block (which is the standard getID3() method.
1347
+
1348
+ // The above-mentioned problem of comments spanning multiple pages and changing
1349
+ // page sequence numbers likely happens for OggSpeex and OggFLAC as well, but
1350
+ // currently vorbiscomment only works on OggVorbis files.
1351
+
1352
+ if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
1353
+
1354
+ $this->warning('Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)');
1355
+ $this->info[$algorithm.'_data'] = false;
1356
+
1357
+ } else {
1358
+
1359
+ // Prevent user from aborting script
1360
+ $old_abort = ignore_user_abort(true);
1361
+
1362
+ // Create empty file
1363
+ $empty = tempnam(GETID3_TEMP_DIR, 'getID3');
1364
+ touch($empty);
1365
+
1366
+ // Use vorbiscomment to make temp file without comments
1367
+ $temp = tempnam(GETID3_TEMP_DIR, 'getID3');
1368
+ $file = $this->info['filenamepath'];
1369
+
1370
+ if (GETID3_OS_ISWINDOWS) {
1371
+
1372
+ if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
1373
+
1374
+ $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"';
1375
+ $VorbisCommentError = `$commandline`;
1376
+
1377
+ } else {
1378
+
1379
+ $VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
1380
+
1381
+ }
1382
+
1383
+ } else {
1384
+
1385
+ $commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1';
1386
+ $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1';
1387
+ $VorbisCommentError = `$commandline`;
1388
+
1389
+ }
1390
+
1391
+ if (!empty($VorbisCommentError)) {
1392
+
1393
+ $this->info['warning'][] = 'Failed making system call to vorbiscomment(.exe) - '.$algorithm.'_data will be incorrect. If vorbiscomment is unavailable, please download from http://www.vorbis.com/download.psp and put in the getID3() directory. Error returned: '.$VorbisCommentError;
1394
+ $this->info[$algorithm.'_data'] = false;
1395
+
1396
+ } else {
1397
+
1398
+ // Get hash of newly created file
1399
+ switch ($algorithm) {
1400
+ case 'md5':
1401
+ $this->info[$algorithm.'_data'] = md5_file($temp);
1402
+ break;
1403
+
1404
+ case 'sha1':
1405
+ $this->info[$algorithm.'_data'] = sha1_file($temp);
1406
+ break;
1407
+ }
1408
+ }
1409
+
1410
+ // Clean up
1411
+ unlink($empty);
1412
+ unlink($temp);
1413
+
1414
+ // Reset abort setting
1415
+ ignore_user_abort($old_abort);
1416
+
1417
+ }
1418
+
1419
+ } else {
1420
+
1421
+ if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) {
1422
+
1423
+ // get hash from part of file
1424
+ $this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm);
1425
+
1426
+ } else {
1427
+
1428
+ // get hash from whole file
1429
+ switch ($algorithm) {
1430
+ case 'md5':
1431
+ $this->info[$algorithm.'_data'] = md5_file($this->info['filenamepath']);
1432
+ break;
1433
+
1434
+ case 'sha1':
1435
+ $this->info[$algorithm.'_data'] = sha1_file($this->info['filenamepath']);
1436
+ break;
1437
+ }
1438
+ }
1439
+
1440
+ }
1441
+ return true;
1442
+ }
1443
+
1444
+
1445
+ public function ChannelsBitratePlaytimeCalculations() {
1446
+
1447
+ // set channelmode on audio
1448
+ if (!empty($this->info['audio']['channelmode']) || !isset($this->info['audio']['channels'])) {
1449
+ // ignore
1450
+ } elseif ($this->info['audio']['channels'] == 1) {
1451
+ $this->info['audio']['channelmode'] = 'mono';
1452
+ } elseif ($this->info['audio']['channels'] == 2) {
1453
+ $this->info['audio']['channelmode'] = 'stereo';
1454
+ }
1455
+
1456
+ // Calculate combined bitrate - audio + video
1457
+ $CombinedBitrate = 0;
1458
+ $CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0);
1459
+ $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0);
1460
+ if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) {
1461
+ $this->info['bitrate'] = $CombinedBitrate;
1462
+ }
1463
+ //if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) {
1464
+ // // for example, VBR MPEG video files cannot determine video bitrate:
1465
+ // // should not set overall bitrate and playtime from audio bitrate only
1466
+ // unset($this->info['bitrate']);
1467
+ //}
1468
+
1469
+ // video bitrate undetermined, but calculable
1470
+ if (isset($this->info['video']['dataformat']) && $this->info['video']['dataformat'] && (!isset($this->info['video']['bitrate']) || ($this->info['video']['bitrate'] == 0))) {
1471
+ // if video bitrate not set
1472
+ if (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] > 0) && ($this->info['audio']['bitrate'] == $this->info['bitrate'])) {
1473
+ // AND if audio bitrate is set to same as overall bitrate
1474
+ if (isset($this->info['playtime_seconds']) && ($this->info['playtime_seconds'] > 0)) {
1475
+ // AND if playtime is set
1476
+ if (isset($this->info['avdataend']) && isset($this->info['avdataoffset'])) {
1477
+ // AND if AV data offset start/end is known
1478
+ // THEN we can calculate the video bitrate
1479
+ $this->info['bitrate'] = round((($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']);
1480
+ $this->info['video']['bitrate'] = $this->info['bitrate'] - $this->info['audio']['bitrate'];
1481
+ }
1482
+ }
1483
+ }
1484
+ }
1485
+
1486
+ if ((!isset($this->info['playtime_seconds']) || ($this->info['playtime_seconds'] <= 0)) && !empty($this->info['bitrate'])) {
1487
+ $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate'];
1488
+ }
1489
+
1490
+ if (!isset($this->info['bitrate']) && !empty($this->info['playtime_seconds'])) {
1491
+ $this->info['bitrate'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds'];
1492
+ }
1493
+ if (isset($this->info['bitrate']) && empty($this->info['audio']['bitrate']) && empty($this->info['video']['bitrate'])) {
1494
+ if (isset($this->info['audio']['dataformat']) && empty($this->info['video']['resolution_x'])) {
1495
+ // audio only
1496
+ $this->info['audio']['bitrate'] = $this->info['bitrate'];
1497
+ } elseif (isset($this->info['video']['resolution_x']) && empty($this->info['audio']['dataformat'])) {
1498
+ // video only
1499
+ $this->info['video']['bitrate'] = $this->info['bitrate'];
1500
+ }
1501
+ }
1502
+
1503
+ // Set playtime string
1504
+ if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) {
1505
+ $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']);
1506
+ }
1507
+ }
1508
+
1509
+
1510
+ public function CalculateCompressionRatioVideo() {
1511
+ if (empty($this->info['video'])) {
1512
+ return false;
1513
+ }
1514
+ if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) {
1515
+ return false;
1516
+ }
1517
+ if (empty($this->info['video']['bits_per_sample'])) {
1518
+ return false;
1519
+ }
1520
+
1521
+ switch ($this->info['video']['dataformat']) {
1522
+ case 'bmp':
1523
+ case 'gif':
1524
+ case 'jpeg':
1525
+ case 'jpg':
1526
+ case 'png':
1527
+ case 'tiff':
1528
+ $FrameRate = 1;
1529
+ $PlaytimeSeconds = 1;
1530
+ $BitrateCompressed = $this->info['filesize'] * 8;
1531
+ break;
1532
+
1533
+ default:
1534
+ if (!empty($this->info['video']['frame_rate'])) {
1535
+ $FrameRate = $this->info['video']['frame_rate'];
1536
+ } else {
1537
+ return false;
1538
+ }
1539
+ if (!empty($this->info['playtime_seconds'])) {
1540
+ $PlaytimeSeconds = $this->info['playtime_seconds'];
1541
+ } else {
1542
+ return false;
1543
+ }
1544
+ if (!empty($this->info['video']['bitrate'])) {
1545
+ $BitrateCompressed = $this->info['video']['bitrate'];
1546
+ } else {
1547
+ return false;
1548
+ }
1549
+ break;
1550
+ }
1551
+ $BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate;
1552
+
1553
+ $this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed;
1554
+ return true;
1555
+ }
1556
+
1557
+
1558
+ public function CalculateCompressionRatioAudio() {
1559
+ if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate']) || !is_numeric($this->info['audio']['sample_rate'])) {
1560
+ return false;
1561
+ }
1562
+ $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (!empty($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : 16));
1563
+
1564
+ if (!empty($this->info['audio']['streams'])) {
1565
+ foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) {
1566
+ if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) {
1567
+ $this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16));
1568
+ }
1569
+ }
1570
+ }
1571
+ return true;
1572
+ }
1573
+
1574
+
1575
+ public function CalculateReplayGain() {
1576
+ if (isset($this->info['replay_gain'])) {
1577
+ if (!isset($this->info['replay_gain']['reference_volume'])) {
1578
+ $this->info['replay_gain']['reference_volume'] = (double) 89.0;
1579
+ }
1580
+ if (isset($this->info['replay_gain']['track']['adjustment'])) {
1581
+ $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment'];
1582
+ }
1583
+ if (isset($this->info['replay_gain']['album']['adjustment'])) {
1584
+ $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment'];
1585
+ }
1586
+
1587
+ if (isset($this->info['replay_gain']['track']['peak'])) {
1588
+ $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']);
1589
+ }
1590
+ if (isset($this->info['replay_gain']['album']['peak'])) {
1591
+ $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']);
1592
+ }
1593
+ }
1594
+ return true;
1595
+ }
1596
+
1597
+ public function ProcessAudioStreams() {
1598
+ if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) {
1599
+ if (!isset($this->info['audio']['streams'])) {
1600
+ foreach ($this->info['audio'] as $key => $value) {
1601
+ if ($key != 'streams') {
1602
+ $this->info['audio']['streams'][0][$key] = $value;
1603
+ }
1604
+ }
1605
+ }
1606
+ }
1607
+ return true;
1608
+ }
1609
+
1610
+ public function getid3_tempnam() {
1611
+ return tempnam($this->tempdir, 'gI3');
1612
+ }
1613
+
1614
+ public function include_module($name) {
1615
+ //if (!file_exists($this->include_path.'module.'.$name.'.php')) {
1616
+ if (!file_exists(GETID3_INCLUDEPATH.'module.'.$name.'.php')) {
1617
+ throw new getid3_exception('Required module.'.$name.'.php is missing.');
1618
+ }
1619
+ include_once(GETID3_INCLUDEPATH.'module.'.$name.'.php');
1620
+ return true;
1621
+ }
1622
+
1623
+ }
1624
+
1625
+
1626
+ abstract class getid3_handler {
1627
+
1628
+ /**
1629
+ * @var getID3
1630
+ */
1631
+ protected $getid3; // pointer
1632
+
1633
+ protected $data_string_flag = false; // analyzing filepointer or string
1634
+ protected $data_string = ''; // string to analyze
1635
+ protected $data_string_position = 0; // seek position in string
1636
+ protected $data_string_length = 0; // string length
1637
+
1638
+ private $dependency_to = null;
1639
+
1640
+
1641
+ public function __construct(getID3 $getid3, $call_module=null) {
1642
+ $this->getid3 = $getid3;
1643
+
1644
+ if ($call_module) {
1645
+ $this->dependency_to = str_replace('getid3_', '', $call_module);
1646
+ }
1647
+ }
1648
+
1649
+
1650
+ // Analyze from file pointer
1651
+ abstract public function Analyze();
1652
+
1653
+
1654
+ // Analyze from string instead
1655
+ public function AnalyzeString($string) {
1656
+ // Enter string mode
1657
+ $this->setStringMode($string);
1658
+
1659
+ // Save info
1660
+ $saved_avdataoffset = $this->getid3->info['avdataoffset'];
1661
+ $saved_avdataend = $this->getid3->info['avdataend'];
1662
+ $saved_filesize = (isset($this->getid3->info['filesize']) ? $this->getid3->info['filesize'] : null); // may be not set if called as dependency without openfile() call
1663
+
1664
+ // Reset some info
1665
+ $this->getid3->info['avdataoffset'] = 0;
1666
+ $this->getid3->info['avdataend'] = $this->getid3->info['filesize'] = $this->data_string_length;
1667
+
1668
+ // Analyze
1669
+ $this->Analyze();
1670
+
1671
+ // Restore some info
1672
+ $this->getid3->info['avdataoffset'] = $saved_avdataoffset;
1673
+ $this->getid3->info['avdataend'] = $saved_avdataend;
1674
+ $this->getid3->info['filesize'] = $saved_filesize;
1675
+
1676
+ // Exit string mode
1677
+ $this->data_string_flag = false;
1678
+ }
1679
+
1680
+ public function setStringMode($string) {
1681
+ $this->data_string_flag = true;
1682
+ $this->data_string = $string;
1683
+ $this->data_string_length = strlen($string);
1684
+ }
1685
+
1686
+ protected function ftell() {
1687
+ if ($this->data_string_flag) {
1688
+ return $this->data_string_position;
1689
+ }
1690
+ return ftell($this->getid3->fp);
1691
+ }
1692
+
1693
+ protected function fread($bytes) {
1694
+ if ($this->data_string_flag) {
1695
+ $this->data_string_position += $bytes;
1696
+ return substr($this->data_string, $this->data_string_position - $bytes, $bytes);
1697
+ }
1698
+ $pos = $this->ftell() + $bytes;
1699
+ if (!getid3_lib::intValueSupported($pos)) {
1700
+ throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') because beyond PHP filesystem limit', 10);
1701
+ }
1702
+ return fread($this->getid3->fp, $bytes);
1703
+ }
1704
+
1705
+ protected function fseek($bytes, $whence=SEEK_SET) {
1706
+ if ($this->data_string_flag) {
1707
+ switch ($whence) {
1708
+ case SEEK_SET:
1709
+ $this->data_string_position = $bytes;
1710
+ break;
1711
+
1712
+ case SEEK_CUR:
1713
+ $this->data_string_position += $bytes;
1714
+ break;
1715
+
1716
+ case SEEK_END:
1717
+ $this->data_string_position = $this->data_string_length + $bytes;
1718
+ break;
1719
+ }
1720
+ return 0;
1721
+ } else {
1722
+ $pos = $bytes;
1723
+ if ($whence == SEEK_CUR) {
1724
+ $pos = $this->ftell() + $bytes;
1725
+ } elseif ($whence == SEEK_END) {
1726
+ $pos = $this->getid3->info['filesize'] + $bytes;
1727
+ }
1728
+ if (!getid3_lib::intValueSupported($pos)) {
1729
+ throw new getid3_exception('cannot fseek('.$pos.') because beyond PHP filesystem limit', 10);
1730
+ }
1731
+ }
1732
+ return fseek($this->getid3->fp, $bytes, $whence);
1733
+ }
1734
+
1735
+ protected function feof() {
1736
+ if ($this->data_string_flag) {
1737
+ return $this->data_string_position >= $this->data_string_length;
1738
+ }
1739
+ return feof($this->getid3->fp);
1740
+ }
1741
+
1742
+ final protected function isDependencyFor($module) {
1743
+ return $this->dependency_to == $module;
1744
+ }
1745
+
1746
+ protected function error($text) {
1747
+ $this->getid3->info['error'][] = $text;
1748
+
1749
+ return false;
1750
+ }
1751
+
1752
+ protected function warning($text) {
1753
+ return $this->getid3->warning($text);
1754
+ }
1755
+
1756
+ protected function notice($text) {
1757
+ // does nothing for now
1758
+ }
1759
+
1760
+ public function saveAttachment($name, $offset, $length, $image_mime=null) {
1761
+ try {
1762
+
1763
+ // do not extract at all
1764
+ if ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_NONE) {
1765
+
1766
+ $attachment = null; // do not set any
1767
+
1768
+ // extract to return array
1769
+ } elseif ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_INLINE) {
1770
+
1771
+ $this->fseek($offset);
1772
+ $attachment = $this->fread($length); // get whole data in one pass, till it is anyway stored in memory
1773
+ if ($attachment === false || strlen($attachment) != $length) {
1774
+ throw new Exception('failed to read attachment data');
1775
+ }
1776
+
1777
+ // assume directory path is given
1778
+ } else {
1779
+
1780
+ // set up destination path
1781
+ $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR);
1782
+ if (!is_dir($dir) || !is_writable($dir)) { // check supplied directory
1783
+ throw new Exception('supplied path ('.$dir.') does not exist, or is not writable');
1784
+ }
1785
+ $dest = $dir.DIRECTORY_SEPARATOR.$name.($image_mime ? '.'.getid3_lib::ImageExtFromMime($image_mime) : '');
1786
+
1787
+ // create dest file
1788
+ if (($fp_dest = fopen($dest, 'wb')) == false) {
1789
+ throw new Exception('failed to create file '.$dest);
1790
+ }
1791
+
1792
+ // copy data
1793
+ $this->fseek($offset);
1794
+ $buffersize = ($this->data_string_flag ? $length : $this->getid3->fread_buffer_size());
1795
+ $bytesleft = $length;
1796
+ while ($bytesleft > 0) {
1797
+ if (($buffer = $this->fread(min($buffersize, $bytesleft))) === false || ($byteswritten = fwrite($fp_dest, $buffer)) === false || ($byteswritten === 0)) {
1798
+ throw new Exception($buffer === false ? 'not enough data to read' : 'failed to write to destination file, may be not enough disk space');
1799
+ }
1800
+ $bytesleft -= $byteswritten;
1801
+ }
1802
+
1803
+ fclose($fp_dest);
1804
+ $attachment = $dest;
1805
+
1806
+ }
1807
+
1808
+ } catch (Exception $e) {
1809
+
1810
+ // close and remove dest file if created
1811
+ if (isset($fp_dest) && is_resource($fp_dest)) {
1812
+ fclose($fp_dest);
1813
+ unlink($dest);
1814
+ }
1815
+
1816
+ // do not set any is case of error
1817
+ $attachment = null;
1818
+ $this->warning('Failed to extract attachment '.$name.': '.$e->getMessage());
1819
+
1820
+ }
1821
+
1822
+ // seek to the end of attachment
1823
+ $this->fseek($offset + $length);
1824
+
1825
+ return $attachment;
1826
+ }
1827
+
1828
+ }
1829
+
1830
+
1831
+ class getid3_exception extends Exception
1832
+ {
1833
+ public $message;
1834
+ }
getid3/module.audio-video.quicktime.php CHANGED
@@ -1,2246 +1,2489 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- // also https://github.com/JamesHeinrich/getID3 //
7
- /////////////////////////////////////////////////////////////////
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
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
19
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); // needed for ISO 639-2 language code lookup
20
-
21
- class getid3_quicktime extends getid3_handler
22
- {
23
-
24
- public $ReturnAtomData = true;
25
- public $ParseAllPossibleAtoms = false;
26
-
27
- public function Analyze() {
28
- $info = &$this->getid3->info;
29
-
30
- $info['fileformat'] = 'quicktime';
31
- $info['quicktime']['hinting'] = false;
32
- $info['quicktime']['controller'] = 'standard'; // may be overridden if 'ctyp' atom is present
33
-
34
- $this->fseek($info['avdataoffset']);
35
-
36
- $offset = 0;
37
- $atomcounter = 0;
38
-
39
- while ($offset < $info['avdataend']) {
40
- if (!getid3_lib::intValueSupported($offset)) {
41
- $info['error'][] = 'Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions';
42
- break;
43
- }
44
- $this->fseek($offset);
45
- $AtomHeader = $this->fread(8);
46
-
47
- $atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4));
48
- $atomname = substr($AtomHeader, 4, 4);
49
-
50
- // 64-bit MOV patch by jlegateØktnc*com
51
- if ($atomsize == 1) {
52
- $atomsize = getid3_lib::BigEndian2Int($this->fread(8));
53
- }
54
-
55
- $info['quicktime'][$atomname]['name'] = $atomname;
56
- $info['quicktime'][$atomname]['size'] = $atomsize;
57
- $info['quicktime'][$atomname]['offset'] = $offset;
58
-
59
- if (($offset + $atomsize) > $info['avdataend']) {
60
- $info['error'][] = 'Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)';
61
- return false;
62
- }
63
-
64
- if ($atomsize == 0) {
65
- // Furthermore, for historical reasons the list of atoms is optionally
66
- // terminated by a 32-bit integer set to 0. If you are writing a program
67
- // to read user data atoms, you should allow for the terminating 0.
68
- break;
69
- }
70
- $atomHierarchy = array();
71
- $info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, round($this->getid3->memory_limit / 2))), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
72
-
73
- $offset += $atomsize;
74
- $atomcounter++;
75
- }
76
-
77
- if (!empty($info['avdataend_tmp'])) {
78
- // this value is assigned to a temp value and then erased because
79
- // otherwise any atoms beyond the 'mdat' atom would not get parsed
80
- $info['avdataend'] = $info['avdataend_tmp'];
81
- unset($info['avdataend_tmp']);
82
- }
83
-
84
- if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) {
85
- $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
86
- }
87
- if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info['quicktime']['video'])) {
88
- $info['audio']['bitrate'] = $info['bitrate'];
89
- }
90
- if (!empty($info['playtime_seconds']) && !isset($info['video']['frame_rate']) && !empty($info['quicktime']['stts_framecount'])) {
91
- foreach ($info['quicktime']['stts_framecount'] as $key => $samples_count) {
92
- $samples_per_second = $samples_count / $info['playtime_seconds'];
93
- if ($samples_per_second > 240) {
94
- // has to be audio samples
95
- } else {
96
- $info['video']['frame_rate'] = $samples_per_second;
97
- break;
98
- }
99
- }
100
- }
101
- if (($info['audio']['dataformat'] == 'mp4') && empty($info['video']['resolution_x'])) {
102
- $info['fileformat'] = 'mp4';
103
- $info['mime_type'] = 'audio/mp4';
104
- unset($info['video']['dataformat']);
105
- }
106
-
107
- if (!$this->ReturnAtomData) {
108
- unset($info['quicktime']['moov']);
109
- }
110
-
111
- if (empty($info['audio']['dataformat']) && !empty($info['quicktime']['audio'])) {
112
- $info['audio']['dataformat'] = 'quicktime';
113
- }
114
- if (empty($info['video']['dataformat']) && !empty($info['quicktime']['video'])) {
115
- $info['video']['dataformat'] = 'quicktime';
116
- }
117
-
118
- return true;
119
- }
120
-
121
- public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
122
- // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm
123
-
124
- $info = &$this->getid3->info;
125
-
126
- $atom_parent = end($atomHierarchy); // not array_pop($atomHierarchy); see http://www.getid3.org/phpBB3/viewtopic.php?t=1717
127
- array_push($atomHierarchy, $atomname);
128
- $atom_structure['hierarchy'] = implode(' ', $atomHierarchy);
129
- $atom_structure['name'] = $atomname;
130
- $atom_structure['size'] = $atomsize;
131
- $atom_structure['offset'] = $baseoffset;
132
- switch ($atomname) {
133
- case 'moov': // MOVie container atom
134
- case 'trak': // TRAcK container atom
135
- case 'clip': // CLIPping container atom
136
- case 'matt': // track MATTe container atom
137
- case 'edts': // EDiTS container atom
138
- case 'tref': // Track REFerence container atom
139
- case 'mdia': // MeDIA container atom
140
- case 'minf': // Media INFormation container atom
141
- case 'dinf': // Data INFormation container atom
142
- case 'udta': // User DaTA container atom
143
- case 'cmov': // Compressed MOVie container atom
144
- case 'rmra': // Reference Movie Record Atom
145
- case 'rmda': // Reference Movie Descriptor Atom
146
- case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR)
147
- $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
148
- break;
149
-
150
- case 'ilst': // Item LiST container atom
151
- if ($atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms)) {
152
- // some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted
153
- $allnumericnames = true;
154
- foreach ($atom_structure['subatoms'] as $subatomarray) {
155
- if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) {
156
- $allnumericnames = false;
157
- break;
158
- }
159
- }
160
- if ($allnumericnames) {
161
- $newData = array();
162
- foreach ($atom_structure['subatoms'] as $subatomarray) {
163
- foreach ($subatomarray['subatoms'] as $newData_subatomarray) {
164
- unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']);
165
- $newData[$subatomarray['name']] = $newData_subatomarray;
166
- break;
167
- }
168
- }
169
- $atom_structure['data'] = $newData;
170
- unset($atom_structure['subatoms']);
171
- }
172
- }
173
- break;
174
-
175
- case "\x00\x00\x00\x01":
176
- case "\x00\x00\x00\x02":
177
- case "\x00\x00\x00\x03":
178
- case "\x00\x00\x00\x04":
179
- case "\x00\x00\x00\x05":
180
- $atomname = getid3_lib::BigEndian2Int($atomname);
181
- $atom_structure['name'] = $atomname;
182
- $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
183
- break;
184
-
185
- case 'stbl': // Sample TaBLe container atom
186
- $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
187
- $isVideo = false;
188
- $framerate = 0;
189
- $framecount = 0;
190
- foreach ($atom_structure['subatoms'] as $key => $value_array) {
191
- if (isset($value_array['sample_description_table'])) {
192
- foreach ($value_array['sample_description_table'] as $key2 => $value_array2) {
193
- if (isset($value_array2['data_format'])) {
194
- switch ($value_array2['data_format']) {
195
- case 'avc1':
196
- case 'mp4v':
197
- // video data
198
- $isVideo = true;
199
- break;
200
- case 'mp4a':
201
- // audio data
202
- break;
203
- }
204
- }
205
- }
206
- } elseif (isset($value_array['time_to_sample_table'])) {
207
- foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
208
- if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) {
209
- $framerate = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3);
210
- $framecount = $value_array2['sample_count'];
211
- }
212
- }
213
- }
214
- }
215
- if ($isVideo && $framerate) {
216
- $info['quicktime']['video']['frame_rate'] = $framerate;
217
- $info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate'];
218
- }
219
- if ($isVideo && $framecount) {
220
- $info['quicktime']['video']['frame_count'] = $framecount;
221
- }
222
- break;
223
-
224
-
225
- case 'aART': // Album ARTist
226
- case 'catg': // CaTeGory
227
- case 'covr': // COVeR artwork
228
- case 'cpil': // ComPILation
229
- case 'cprt': // CoPyRighT
230
- case 'desc': // DESCription
231
- case 'disk': // DISK number
232
- case 'egid': // Episode Global ID
233
- case 'gnre': // GeNRE
234
- case 'keyw': // KEYWord
235
- case 'ldes':
236
- case 'pcst': // PodCaST
237
- case 'pgap': // GAPless Playback
238
- case 'purd': // PURchase Date
239
- case 'purl': // Podcast URL
240
- case 'rati':
241
- case 'rndu':
242
- case 'rpdu':
243
- case 'rtng': // RaTiNG
244
- case 'stik':
245
- case 'tmpo': // TeMPO (BPM)
246
- case 'trkn': // TRacK Number
247
- case 'tves': // TV EpiSode
248
- case 'tvnn': // TV Network Name
249
- case 'tvsh': // TV SHow Name
250
- case 'tvsn': // TV SeasoN
251
- case 'akID': // iTunes store account type
252
- case 'apID':
253
- case 'atID':
254
- case 'cmID':
255
- case 'cnID':
256
- case 'geID':
257
- case 'plID':
258
- case 'sfID': // iTunes store country
259
- case "\xA9".'alb': // ALBum
260
- case "\xA9".'art': // ARTist
261
- case "\xA9".'ART':
262
- case "\xA9".'aut':
263
- case "\xA9".'cmt': // CoMmenT
264
- case "\xA9".'com': // COMposer
265
- case "\xA9".'cpy':
266
- case "\xA9".'day': // content created year
267
- case "\xA9".'dir':
268
- case "\xA9".'ed1':
269
- case "\xA9".'ed2':
270
- case "\xA9".'ed3':
271
- case "\xA9".'ed4':
272
- case "\xA9".'ed5':
273
- case "\xA9".'ed6':
274
- case "\xA9".'ed7':
275
- case "\xA9".'ed8':
276
- case "\xA9".'ed9':
277
- case "\xA9".'enc':
278
- case "\xA9".'fmt':
279
- case "\xA9".'gen': // GENre
280
- case "\xA9".'grp': // GRouPing
281
- case "\xA9".'hst':
282
- case "\xA9".'inf':
283
- case "\xA9".'lyr': // LYRics
284
- case "\xA9".'mak':
285
- case "\xA9".'mod':
286
- case "\xA9".'nam': // full NAMe
287
- case "\xA9".'ope':
288
- case "\xA9".'PRD':
289
- case "\xA9".'prd':
290
- case "\xA9".'prf':
291
- case "\xA9".'req':
292
- case "\xA9".'src':
293
- case "\xA9".'swr':
294
- case "\xA9".'too': // encoder
295
- case "\xA9".'trk': // TRacK
296
- case "\xA9".'url':
297
- case "\xA9".'wrn':
298
- case "\xA9".'wrt': // WRiTer
299
- case '----': // itunes specific
300
- if ($atom_parent == 'udta') {
301
- // User data atom handler
302
- $atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
303
- $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
304
- $atom_structure['data'] = substr($atom_data, 4);
305
-
306
- $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
307
- if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
308
- $info['comments']['language'][] = $atom_structure['language'];
309
- }
310
- } else {
311
- // Apple item list box atom handler
312
- $atomoffset = 0;
313
- if (substr($atom_data, 2, 2) == "\x10\xB5") {
314
- // not sure what it means, but observed on iPhone4 data.
315
- // Each $atom_data has 2 bytes of datasize, plus 0x10B5, then data
316
- while ($atomoffset < strlen($atom_data)) {
317
- $boxsmallsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 2));
318
- $boxsmalltype = substr($atom_data, $atomoffset + 2, 2);
319
- $boxsmalldata = substr($atom_data, $atomoffset + 4, $boxsmallsize);
320
- if ($boxsmallsize <= 1) {
321
- $info['warning'][] = 'Invalid QuickTime atom smallbox size "'.$boxsmallsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset);
322
- $atom_structure['data'] = null;
323
- $atomoffset = strlen($atom_data);
324
- break;
325
- }
326
- switch ($boxsmalltype) {
327
- case "\x10\xB5":
328
- $atom_structure['data'] = $boxsmalldata;
329
- break;
330
- default:
331
- $info['warning'][] = 'Unknown QuickTime smallbox type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxsmalltype).'" ('.trim(getid3_lib::PrintHexBytes($boxsmalltype)).') at offset '.$baseoffset;
332
- $atom_structure['data'] = $atom_data;
333
- break;
334
- }
335
- $atomoffset += (4 + $boxsmallsize);
336
- }
337
- } else {
338
- while ($atomoffset < strlen($atom_data)) {
339
- $boxsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 4));
340
- $boxtype = substr($atom_data, $atomoffset + 4, 4);
341
- $boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8);
342
- if ($boxsize <= 1) {
343
- $info['warning'][] = 'Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset);
344
- $atom_structure['data'] = null;
345
- $atomoffset = strlen($atom_data);
346
- break;
347
- }
348
- $atomoffset += $boxsize;
349
-
350
- switch ($boxtype) {
351
- case 'mean':
352
- case 'name':
353
- $atom_structure[$boxtype] = substr($boxdata, 4);
354
- break;
355
-
356
- case 'data':
357
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($boxdata, 0, 1));
358
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata, 1, 3));
359
- switch ($atom_structure['flags_raw']) {
360
- case 0: // data flag
361
- case 21: // tmpo/cpil flag
362
- switch ($atomname) {
363
- case 'cpil':
364
- case 'pcst':
365
- case 'pgap':
366
- $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
367
- break;
368
-
369
- case 'tmpo':
370
- $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2));
371
- break;
372
-
373
- case 'disk':
374
- case 'trkn':
375
- $num = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2));
376
- $num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2));
377
- $atom_structure['data'] = empty($num) ? '' : $num;
378
- $atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total;
379
- break;
380
-
381
- case 'gnre':
382
- $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
383
- $atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1);
384
- break;
385
-
386
- case 'rtng':
387
- $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
388
- $atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]);
389
- break;
390
-
391
- case 'stik':
392
- $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
393
- $atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]);
394
- break;
395
-
396
- case 'sfID':
397
- $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
398
- $atom_structure['data'] = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]);
399
- break;
400
-
401
- case 'egid':
402
- case 'purl':
403
- $atom_structure['data'] = substr($boxdata, 8);
404
- break;
405
-
406
- default:
407
- $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
408
- }
409
- break;
410
-
411
- case 1: // text flag
412
- case 13: // image flag
413
- default:
414
- $atom_structure['data'] = substr($boxdata, 8);
415
- if ($atomname == 'covr') {
416
- // not a foolproof check, but better than nothing
417
- if (preg_match('#^\xFF\xD8\xFF#', $atom_structure['data'])) {
418
- $atom_structure['image_mime'] = 'image/jpeg';
419
- } elseif (preg_match('#^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A#', $atom_structure['data'])) {
420
- $atom_structure['image_mime'] = 'image/png';
421
- } elseif (preg_match('#^GIF#', $atom_structure['data'])) {
422
- $atom_structure['image_mime'] = 'image/gif';
423
- }
424
- }
425
- break;
426
-
427
- }
428
- break;
429
-
430
- default:
431
- $info['warning'][] = 'Unknown QuickTime box type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxtype).'" ('.trim(getid3_lib::PrintHexBytes($boxtype)).') at offset '.$baseoffset;
432
- $atom_structure['data'] = $atom_data;
433
-
434
- }
435
- }
436
- }
437
- }
438
- $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']);
439
- break;
440
-
441
-
442
- case 'play': // auto-PLAY atom
443
- $atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
444
-
445
- $info['quicktime']['autoplay'] = $atom_structure['autoplay'];
446
- break;
447
-
448
-
449
- case 'WLOC': // Window LOCation atom
450
- $atom_structure['location_x'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
451
- $atom_structure['location_y'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
452
- break;
453
-
454
-
455
- case 'LOOP': // LOOPing atom
456
- case 'SelO': // play SELection Only atom
457
- case 'AllF': // play ALL Frames atom
458
- $atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data);
459
- break;
460
-
461
-
462
- case 'name': //
463
- case 'MCPS': // Media Cleaner PRo
464
- case '@PRM': // adobe PReMiere version
465
- case '@PRQ': // adobe PRemiere Quicktime version
466
- $atom_structure['data'] = $atom_data;
467
- break;
468
-
469
-
470
- case 'cmvd': // Compressed MooV Data atom
471
- // Code by ubergeekØubergeek*tv based on information from
472
- // http://developer.apple.com/quicktime/icefloe/dispatch012.html
473
- $atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
474
-
475
- $CompressedFileData = substr($atom_data, 4);
476
- if ($UncompressedHeader = @gzuncompress($CompressedFileData)) {
477
- $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms);
478
- } else {
479
- $info['warning'][] = 'Error decompressing compressed MOV atom at offset '.$atom_structure['offset'];
480
- }
481
- break;
482
-
483
-
484
- case 'dcom': // Data COMpression atom
485
- $atom_structure['compression_id'] = $atom_data;
486
- $atom_structure['compression_text'] = $this->QuicktimeDCOMLookup($atom_data);
487
- break;
488
-
489
-
490
- case 'rdrf': // Reference movie Data ReFerence atom
491
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
492
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
493
- $atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x000001);
494
-
495
- $atom_structure['reference_type_name'] = substr($atom_data, 4, 4);
496
- $atom_structure['reference_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
497
- switch ($atom_structure['reference_type_name']) {
498
- case 'url ':
499
- $atom_structure['url'] = $this->NoNullString(substr($atom_data, 12));
500
- break;
501
-
502
- case 'alis':
503
- $atom_structure['file_alias'] = substr($atom_data, 12);
504
- break;
505
-
506
- case 'rsrc':
507
- $atom_structure['resource_alias'] = substr($atom_data, 12);
508
- break;
509
-
510
- default:
511
- $atom_structure['data'] = substr($atom_data, 12);
512
- break;
513
- }
514
- break;
515
-
516
-
517
- case 'rmqu': // Reference Movie QUality atom
518
- $atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data);
519
- break;
520
-
521
-
522
- case 'rmcs': // Reference Movie Cpu Speed atom
523
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
524
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
525
- $atom_structure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
526
- break;
527
-
528
-
529
- case 'rmvc': // Reference Movie Version Check atom
530
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
531
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
532
- $atom_structure['gestalt_selector'] = substr($atom_data, 4, 4);
533
- $atom_structure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
534
- $atom_structure['gestalt_value'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
535
- $atom_structure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
536
- break;
537
-
538
-
539
- case 'rmcd': // Reference Movie Component check atom
540
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
541
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
542
- $atom_structure['component_type'] = substr($atom_data, 4, 4);
543
- $atom_structure['component_subtype'] = substr($atom_data, 8, 4);
544
- $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
545
- $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
546
- $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
547
- $atom_structure['component_min_version'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 4));
548
- break;
549
-
550
-
551
- case 'rmdr': // Reference Movie Data Rate atom
552
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
553
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
554
- $atom_structure['data_rate'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
555
-
556
- $atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10;
557
- break;
558
-
559
-
560
- case 'rmla': // Reference Movie Language Atom
561
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
562
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
563
- $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
564
-
565
- $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
566
- if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
567
- $info['comments']['language'][] = $atom_structure['language'];
568
- }
569
- break;
570
-
571
-
572
- case 'rmla': // Reference Movie Language Atom
573
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
574
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
575
- $atom_structure['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
576
- break;
577
-
578
-
579
- case 'ptv ': // Print To Video - defines a movie's full screen mode
580
- // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm
581
- $atom_structure['display_size_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
582
- $atom_structure['reserved_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); // hardcoded: 0x0000
583
- $atom_structure['reserved_2'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x0000
584
- $atom_structure['slide_show_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 1));
585
- $atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1));
586
-
587
- $atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag'];
588
- $atom_structure['flags']['slide_show'] = (bool) $atom_structure['slide_show_flag'];
589
-
590
- $ptv_lookup[0] = 'normal';
591
- $ptv_lookup[1] = 'double';
592
- $ptv_lookup[2] = 'half';
593
- $ptv_lookup[3] = 'full';
594
- $ptv_lookup[4] = 'current';
595
- if (isset($ptv_lookup[$atom_structure['display_size_raw']])) {
596
- $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
597
- } else {
598
- $info['warning'][] = 'unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')';
599
- }
600
- break;
601
-
602
-
603
- case 'stsd': // Sample Table Sample Description atom
604
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
605
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
606
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
607
- $stsdEntriesDataOffset = 8;
608
- for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
609
- $atom_structure['sample_description_table'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4));
610
- $stsdEntriesDataOffset += 4;
611
- $atom_structure['sample_description_table'][$i]['data_format'] = substr($atom_data, $stsdEntriesDataOffset, 4);
612
- $stsdEntriesDataOffset += 4;
613
- $atom_structure['sample_description_table'][$i]['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6));
614
- $stsdEntriesDataOffset += 6;
615
- $atom_structure['sample_description_table'][$i]['reference_index'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2));
616
- $stsdEntriesDataOffset += 2;
617
- $atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
618
- $stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
619
-
620
- $atom_structure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 0, 2));
621
- $atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 2, 2));
622
- $atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4);
623
-
624
- switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) {
625
-
626
- case "\x00\x00\x00\x00":
627
- // audio tracks
628
- $atom_structure['sample_description_table'][$i]['audio_channels'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 2));
629
- $atom_structure['sample_description_table'][$i]['audio_bit_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 10, 2));
630
- $atom_structure['sample_description_table'][$i]['audio_compression_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 2));
631
- $atom_structure['sample_description_table'][$i]['audio_packet_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 14, 2));
632
- $atom_structure['sample_description_table'][$i]['audio_sample_rate'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16, 4));
633
-
634
- // video tracks
635
- // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
636
- $atom_structure['sample_description_table'][$i]['temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4));
637
- $atom_structure['sample_description_table'][$i]['spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4));
638
- $atom_structure['sample_description_table'][$i]['width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2));
639
- $atom_structure['sample_description_table'][$i]['height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2));
640
- $atom_structure['sample_description_table'][$i]['resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4));
641
- $atom_structure['sample_description_table'][$i]['resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4));
642
- $atom_structure['sample_description_table'][$i]['data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 4));
643
- $atom_structure['sample_description_table'][$i]['frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 36, 2));
644
- $atom_structure['sample_description_table'][$i]['compressor_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 38, 4);
645
- $atom_structure['sample_description_table'][$i]['pixel_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 42, 2));
646
- $atom_structure['sample_description_table'][$i]['color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 44, 2));
647
-
648
- switch ($atom_structure['sample_description_table'][$i]['data_format']) {
649
- case '2vuY':
650
- case 'avc1':
651
- case 'cvid':
652
- case 'dvc ':
653
- case 'dvcp':
654
- case 'gif ':
655
- case 'h263':
656
- case 'jpeg':
657
- case 'kpcd':
658
- case 'mjpa':
659
- case 'mjpb':
660
- case 'mp4v':
661
- case 'png ':
662
- case 'raw ':
663
- case 'rle ':
664
- case 'rpza':
665
- case 'smc ':
666
- case 'SVQ1':
667
- case 'SVQ3':
668
- case 'tiff':
669
- case 'v210':
670
- case 'v216':
671
- case 'v308':
672
- case 'v408':
673
- case 'v410':
674
- case 'yuv2':
675
- $info['fileformat'] = 'mp4';
676
- $info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
677
- // http://www.getid3.org/phpBB3/viewtopic.php?t=1550
678
- //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
679
- if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['height'])) {
680
- // assume that values stored here are more important than values stored in [tkhd] atom
681
- $info['video']['resolution_x'] = $atom_structure['sample_description_table'][$i]['width'];
682
- $info['video']['resolution_y'] = $atom_structure['sample_description_table'][$i]['height'];
683
- $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
684
- $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
685
- }
686
- break;
687
-
688
- case 'qtvr':
689
- $info['video']['dataformat'] = 'quicktimevr';
690
- break;
691
-
692
- case 'mp4a':
693
- default:
694
- $info['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
695
- $info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate'];
696
- $info['quicktime']['audio']['channels'] = $atom_structure['sample_description_table'][$i]['audio_channels'];
697
- $info['quicktime']['audio']['bit_depth'] = $atom_structure['sample_description_table'][$i]['audio_bit_depth'];
698
- $info['audio']['codec'] = $info['quicktime']['audio']['codec'];
699
- $info['audio']['sample_rate'] = $info['quicktime']['audio']['sample_rate'];
700
- $info['audio']['channels'] = $info['quicktime']['audio']['channels'];
701
- $info['audio']['bits_per_sample'] = $info['quicktime']['audio']['bit_depth'];
702
- switch ($atom_structure['sample_description_table'][$i]['data_format']) {
703
- case 'raw ': // PCM
704
- case 'alac': // Apple Lossless Audio Codec
705
- $info['audio']['lossless'] = true;
706
- break;
707
- default:
708
- $info['audio']['lossless'] = false;
709
- break;
710
- }
711
- break;
712
- }
713
- break;
714
-
715
- default:
716
- switch ($atom_structure['sample_description_table'][$i]['data_format']) {
717
- case 'mp4s':
718
- $info['fileformat'] = 'mp4';
719
- break;
720
-
721
- default:
722
- // video atom
723
- $atom_structure['sample_description_table'][$i]['video_temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4));
724
- $atom_structure['sample_description_table'][$i]['video_spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4));
725
- $atom_structure['sample_description_table'][$i]['video_frame_width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2));
726
- $atom_structure['sample_description_table'][$i]['video_frame_height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2));
727
- $atom_structure['sample_description_table'][$i]['video_resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20, 4));
728
- $atom_structure['sample_description_table'][$i]['video_resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4));
729
- $atom_structure['sample_description_table'][$i]['video_data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4));
730
- $atom_structure['sample_description_table'][$i]['video_frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 2));
731
- $atom_structure['sample_description_table'][$i]['video_encoder_name_len'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34, 1));
732
- $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']);
733
- $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66, 2));
734
- $atom_structure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68, 2));
735
-
736
- $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color');
737
- $atom_structure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']);
738
-
739
- if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') {
740
- $info['quicktime']['video']['codec_fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
741
- $info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
742
- $info['quicktime']['video']['codec'] = (($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']);
743
- $info['quicktime']['video']['color_depth'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'];
744
- $info['quicktime']['video']['color_depth_name'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_name'];
745
-
746
- $info['video']['codec'] = $info['quicktime']['video']['codec'];
747
- $info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth'];
748
- }
749
- $info['video']['lossless'] = false;
750
- $info['video']['pixel_aspect_ratio'] = (float) 1;
751
- break;
752
- }
753
- break;
754
- }
755
- switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) {
756
- case 'mp4a':
757
- $info['audio']['dataformat'] = 'mp4';
758
- $info['quicktime']['audio']['codec'] = 'mp4';
759
- break;
760
-
761
- case '3ivx':
762
- case '3iv1':
763
- case '3iv2':
764
- $info['video']['dataformat'] = '3ivx';
765
- break;
766
-
767
- case 'xvid':
768
- $info['video']['dataformat'] = 'xvid';
769
- break;
770
-
771
- case 'mp4v':
772
- $info['video']['dataformat'] = 'mpeg4';
773
- break;
774
-
775
- case 'divx':
776
- case 'div1':
777
- case 'div2':
778
- case 'div3':
779
- case 'div4':
780
- case 'div5':
781
- case 'div6':
782
- $info['video']['dataformat'] = 'divx';
783
- break;
784
-
785
- default:
786
- // do nothing
787
- break;
788
- }
789
- unset($atom_structure['sample_description_table'][$i]['data']);
790
- }
791
- break;
792
-
793
-
794
- case 'stts': // Sample Table Time-to-Sample atom
795
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
796
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
797
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
798
- $sttsEntriesDataOffset = 8;
799
- //$FrameRateCalculatorArray = array();
800
- $frames_count = 0;
801
-
802
- $max_stts_entries_to_scan = min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']);
803
- if ($max_stts_entries_to_scan < $atom_structure['number_entries']) {
804
- $info['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).';
805
- }
806
- for ($i = 0; $i < $max_stts_entries_to_scan; $i++) {
807
- $atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
808
- $sttsEntriesDataOffset += 4;
809
- $atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
810
- $sttsEntriesDataOffset += 4;
811
-
812
- $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count'];
813
-
814
- // THIS SECTION REPLACED WITH CODE IN "stbl" ATOM
815
- //if (!empty($info['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) {
816
- // $stts_new_framerate = $info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'];
817
- // if ($stts_new_framerate <= 60) {
818
- // // some atoms have durations of "1" giving a very large framerate, which probably is not right
819
- // $info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate);
820
- // }
821
- //}
822
- //
823
- //$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count'];
824
- }
825
- $info['quicktime']['stts_framecount'][] = $frames_count;
826
- //$sttsFramesTotal = 0;
827
- //$sttsSecondsTotal = 0;
828
- //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) {
829
- // if (($frames_per_second > 60) || ($frames_per_second < 1)) {
830
- // // not video FPS information, probably audio information
831
- // $sttsFramesTotal = 0;
832
- // $sttsSecondsTotal = 0;
833
- // break;
834
- // }
835
- // $sttsFramesTotal += $frame_count;
836
- // $sttsSecondsTotal += $frame_count / $frames_per_second;
837
- //}
838
- //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) {
839
- // if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) {
840
- // $info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal;
841
- // }
842
- //}
843
- break;
844
-
845
-
846
- case 'stss': // Sample Table Sync Sample (key frames) atom
847
- if ($ParseAllPossibleAtoms) {
848
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
849
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
850
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
851
- $stssEntriesDataOffset = 8;
852
- for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
853
- $atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4));
854
- $stssEntriesDataOffset += 4;
855
- }
856
- }
857
- break;
858
-
859
-
860
- case 'stsc': // Sample Table Sample-to-Chunk atom
861
- if ($ParseAllPossibleAtoms) {
862
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
863
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
864
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
865
- $stscEntriesDataOffset = 8;
866
- for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
867
- $atom_structure['sample_to_chunk_table'][$i]['first_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
868
- $stscEntriesDataOffset += 4;
869
- $atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
870
- $stscEntriesDataOffset += 4;
871
- $atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
872
- $stscEntriesDataOffset += 4;
873
- }
874
- }
875
- break;
876
-
877
-
878
- case 'stsz': // Sample Table SiZe atom
879
- if ($ParseAllPossibleAtoms) {
880
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
881
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
882
- $atom_structure['sample_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
883
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
884
- $stszEntriesDataOffset = 12;
885
- if ($atom_structure['sample_size'] == 0) {
886
- for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
887
- $atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4));
888
- $stszEntriesDataOffset += 4;
889
- }
890
- }
891
- }
892
- break;
893
-
894
-
895
- case 'stco': // Sample Table Chunk Offset atom
896
- if ($ParseAllPossibleAtoms) {
897
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
898
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
899
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
900
- $stcoEntriesDataOffset = 8;
901
- for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
902
- $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4));
903
- $stcoEntriesDataOffset += 4;
904
- }
905
- }
906
- break;
907
-
908
-
909
- case 'co64': // Chunk Offset 64-bit (version of "stco" that supports > 2GB files)
910
- if ($ParseAllPossibleAtoms) {
911
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
912
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
913
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
914
- $stcoEntriesDataOffset = 8;
915
- for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
916
- $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8));
917
- $stcoEntriesDataOffset += 8;
918
- }
919
- }
920
- break;
921
-
922
-
923
- case 'dref': // Data REFerence atom
924
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
925
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
926
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
927
- $drefDataOffset = 8;
928
- for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
929
- $atom_structure['data_references'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4));
930
- $drefDataOffset += 4;
931
- $atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4);
932
- $drefDataOffset += 4;
933
- $atom_structure['data_references'][$i]['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 1));
934
- $drefDataOffset += 1;
935
- $atom_structure['data_references'][$i]['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 3)); // hardcoded: 0x0000
936
- $drefDataOffset += 3;
937
- $atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3));
938
- $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3);
939
-
940
- $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001);
941
- }
942
- break;
943
-
944
-
945
- case 'gmin': // base Media INformation atom
946
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
947
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
948
- $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
949
- $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
950
- $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2));
951
- $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
952
- $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 2));
953
- $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
954
- break;
955
-
956
-
957
- case 'smhd': // Sound Media information HeaDer atom
958
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
959
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
960
- $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
961
- $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
962
- break;
963
-
964
-
965
- case 'vmhd': // Video Media information HeaDer atom
966
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
967
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
968
- $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
969
- $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
970
- $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2));
971
- $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
972
-
973
- $atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x001);
974
- break;
975
-
976
-
977
- case 'hdlr': // HanDLeR reference atom
978
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
979
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
980
- $atom_structure['component_type'] = substr($atom_data, 4, 4);
981
- $atom_structure['component_subtype'] = substr($atom_data, 8, 4);
982
- $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
983
- $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
984
- $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
985
- $atom_structure['component_name'] = $this->Pascal2String(substr($atom_data, 24));
986
-
987
- if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) {
988
- $info['video']['dataformat'] = 'quicktimevr';
989
- }
990
- break;
991
-
992
-
993
- case 'mdhd': // MeDia HeaDer atom
994
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
995
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
996
- $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
997
- $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
998
- $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
999
- $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1000
- $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 2));
1001
- $atom_structure['quality'] = getid3_lib::BigEndian2Int(substr($atom_data, 22, 2));
1002
-
1003
- if ($atom_structure['time_scale'] == 0) {
1004
- $info['error'][] = 'Corrupt Quicktime file: mdhd.time_scale == zero';
1005
- return false;
1006
- }
1007
- $info['quicktime']['time_scale'] = (isset($info['quicktime']['time_scale']) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
1008
-
1009
- $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
1010
- $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
1011
- $atom_structure['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
1012
- $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
1013
- if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
1014
- $info['comments']['language'][] = $atom_structure['language'];
1015
- }
1016
- break;
1017
-
1018
-
1019
- case 'pnot': // Preview atom
1020
- $atom_structure['modification_date'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // "standard Macintosh format"
1021
- $atom_structure['version_number'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x00
1022
- $atom_structure['atom_type'] = substr($atom_data, 6, 4); // usually: 'PICT'
1023
- $atom_structure['atom_index'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01
1024
-
1025
- $atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']);
1026
- break;
1027
-
1028
-
1029
- case 'crgn': // Clipping ReGioN atom
1030
- $atom_structure['region_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); // The Region size, Region boundary box,
1031
- $atom_structure['boundary_box'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 8)); // and Clipping region data fields
1032
- $atom_structure['clipping_data'] = substr($atom_data, 10); // constitute a QuickDraw region.
1033
- break;
1034
-
1035
-
1036
- case 'load': // track LOAD settings atom
1037
- $atom_structure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
1038
- $atom_structure['preload_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1039
- $atom_structure['preload_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
1040
- $atom_structure['default_hints_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1041
-
1042
- $atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x0020);
1043
- $atom_structure['default_hints']['high_quality'] = (bool) ($atom_structure['default_hints_raw'] & 0x0100);
1044
- break;
1045
-
1046
-
1047
- case 'tmcd': // TiMe CoDe atom
1048
- case 'chap': // CHAPter list atom
1049
- case 'sync': // SYNChronization atom
1050
- case 'scpt': // tranSCriPT atom
1051
- case 'ssrc': // non-primary SouRCe atom
1052
- for ($i = 0; $i < strlen($atom_data); $i += 4) {
1053
- @$atom_structure['track_id'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
1054
- }
1055
- break;
1056
-
1057
-
1058
- case 'elst': // Edit LiST atom
1059
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1060
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1061
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1062
- for ($i = 0; $i < $atom_structure['number_entries']; $i++ ) {
1063
- $atom_structure['edit_list'][$i]['track_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4));
1064
- $atom_structure['edit_list'][$i]['media_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4));
1065
- $atom_structure['edit_list'][$i]['media_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4));
1066
- }
1067
- break;
1068
-
1069
-
1070
- case 'kmat': // compressed MATte atom
1071
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1072
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1073
- $atom_structure['matte_data_raw'] = substr($atom_data, 4);
1074
- break;
1075
-
1076
-
1077
- case 'ctab': // Color TABle atom
1078
- $atom_structure['color_table_seed'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // hardcoded: 0x00000000
1079
- $atom_structure['color_table_flags'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x8000
1080
- $atom_structure['color_table_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)) + 1;
1081
- for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) {
1082
- $atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2));
1083
- $atom_structure['color_table'][$colortableentry]['red'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2));
1084
- $atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2));
1085
- $atom_structure['color_table'][$colortableentry]['blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2));
1086
- }
1087
- break;
1088
-
1089
-
1090
- case 'mvhd': // MoVie HeaDer atom
1091
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1092
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1093
- $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1094
- $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
1095
- $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1096
- $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1097
- $atom_structure['preferred_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 20, 4));
1098
- $atom_structure['preferred_volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 24, 2));
1099
- $atom_structure['reserved'] = substr($atom_data, 26, 10);
1100
- $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 36, 4));
1101
- $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
1102
- $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 44, 4));
1103
- $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4));
1104
- $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
1105
- $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 56, 4));
1106
- $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4));
1107
- $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
1108
- $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4));
1109
- $atom_structure['preview_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 72, 4));
1110
- $atom_structure['preview_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 76, 4));
1111
- $atom_structure['poster_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 80, 4));
1112
- $atom_structure['selection_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 84, 4));
1113
- $atom_structure['selection_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 88, 4));
1114
- $atom_structure['current_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 92, 4));
1115
- $atom_structure['next_track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 96, 4));
1116
-
1117
- if ($atom_structure['time_scale'] == 0) {
1118
- $info['error'][] = 'Corrupt Quicktime file: mvhd.time_scale == zero';
1119
- return false;
1120
- }
1121
- $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
1122
- $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
1123
- $info['quicktime']['time_scale'] = (isset($info['quicktime']['time_scale']) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
1124
- $info['quicktime']['display_scale'] = $atom_structure['matrix_a'];
1125
- $info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
1126
- break;
1127
-
1128
-
1129
- case 'tkhd': // TracK HeaDer atom
1130
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1131
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1132
- $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1133
- $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
1134
- $atom_structure['trackid'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1135
- $atom_structure['reserved1'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1136
- $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
1137
- $atom_structure['reserved2'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 8));
1138
- $atom_structure['layer'] = getid3_lib::BigEndian2Int(substr($atom_data, 32, 2));
1139
- $atom_structure['alternate_group'] = getid3_lib::BigEndian2Int(substr($atom_data, 34, 2));
1140
- $atom_structure['volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 36, 2));
1141
- $atom_structure['reserved3'] = getid3_lib::BigEndian2Int(substr($atom_data, 38, 2));
1142
- // http://developer.apple.com/library/mac/#documentation/QuickTime/RM/MovieBasics/MTEditing/K-Chapter/11MatrixFunctions.html
1143
- // http://developer.apple.com/library/mac/#documentation/QuickTime/qtff/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-18737
1144
- $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
1145
- $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 44, 4));
1146
- $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 48, 4));
1147
- $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
1148
- $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 56, 4));
1149
- $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 60, 4));
1150
- $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
1151
- $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 68, 4));
1152
- $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 72, 4));
1153
- $atom_structure['width'] = getid3_lib::FixedPoint16_16(substr($atom_data, 76, 4));
1154
- $atom_structure['height'] = getid3_lib::FixedPoint16_16(substr($atom_data, 80, 4));
1155
- $atom_structure['flags']['enabled'] = (bool) ($atom_structure['flags_raw'] & 0x0001);
1156
- $atom_structure['flags']['in_movie'] = (bool) ($atom_structure['flags_raw'] & 0x0002);
1157
- $atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x0004);
1158
- $atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x0008);
1159
- $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
1160
- $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
1161
-
1162
- if ($atom_structure['flags']['enabled'] == 1) {
1163
- if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) {
1164
- $info['video']['resolution_x'] = $atom_structure['width'];
1165
- $info['video']['resolution_y'] = $atom_structure['height'];
1166
- }
1167
- $info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']);
1168
- $info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']);
1169
- $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
1170
- $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
1171
- } else {
1172
- // see: http://www.getid3.org/phpBB3/viewtopic.php?t=1295
1173
- //if (isset($info['video']['resolution_x'])) { unset($info['video']['resolution_x']); }
1174
- //if (isset($info['video']['resolution_y'])) { unset($info['video']['resolution_y']); }
1175
- //if (isset($info['quicktime']['video'])) { unset($info['quicktime']['video']); }
1176
- }
1177
- break;
1178
-
1179
-
1180
- case 'iods': // Initial Object DeScriptor atom
1181
- // http://www.koders.com/c/fid1FAB3E762903DC482D8A246D4A4BF9F28E049594.aspx?s=windows.h
1182
- // http://libquicktime.sourcearchive.com/documentation/1.0.2plus-pdebian/iods_8c-source.html
1183
- $offset = 0;
1184
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1185
- $offset += 1;
1186
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 3));
1187
- $offset += 3;
1188
- $atom_structure['mp4_iod_tag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1189
- $offset += 1;
1190
- $atom_structure['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
1191
- //$offset already adjusted by quicktime_read_mp4_descr_length()
1192
- $atom_structure['object_descriptor_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2));
1193
- $offset += 2;
1194
- $atom_structure['od_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1195
- $offset += 1;
1196
- $atom_structure['scene_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1197
- $offset += 1;
1198
- $atom_structure['audio_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1199
- $offset += 1;
1200
- $atom_structure['video_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1201
- $offset += 1;
1202
- $atom_structure['graphics_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1203
- $offset += 1;
1204
-
1205
- $atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6; // 6 bytes would only be right if all tracks use 1-byte length fields
1206
- for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) {
1207
- $atom_structure['track'][$i]['ES_ID_IncTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1208
- $offset += 1;
1209
- $atom_structure['track'][$i]['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
1210
- //$offset already adjusted by quicktime_read_mp4_descr_length()
1211
- $atom_structure['track'][$i]['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4));
1212
- $offset += 4;
1213
- }
1214
-
1215
- $atom_structure['audio_profile_name'] = $this->QuicktimeIODSaudioProfileName($atom_structure['audio_profile_id']);
1216
- $atom_structure['video_profile_name'] = $this->QuicktimeIODSvideoProfileName($atom_structure['video_profile_id']);
1217
- break;
1218
-
1219
- case 'ftyp': // FileTYPe (?) atom (for MP4 it seems)
1220
- $atom_structure['signature'] = substr($atom_data, 0, 4);
1221
- $atom_structure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1222
- $atom_structure['fourcc'] = substr($atom_data, 8, 4);
1223
- break;
1224
-
1225
- case 'mdat': // Media DATa atom
1226
- // 'mdat' contains the actual data for the audio/video, possibly also subtitles
1227
-
1228
- /* 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 info@getid3.org */
1229
-
1230
- // first, skip any 'wide' padding, and second 'mdat' header (with specified size of zero?)
1231
- $mdat_offset = 0;
1232
- while (true) {
1233
- if (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x08".'wide') {
1234
- $mdat_offset += 8;
1235
- } elseif (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x00".'mdat') {
1236
- $mdat_offset += 8;
1237
- } else {
1238
- break;
1239
- }
1240
- }
1241
-
1242
- // check to see if it looks like chapter titles, in the form of unterminated strings with a leading 16-bit size field
1243
- while (($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2)))
1244
- && ($chapter_string_length < 1000)
1245
- && ($chapter_string_length <= (strlen($atom_data) - $mdat_offset - 2))
1246
- && preg_match('#^[\x20-\xFF]+$#', substr($atom_data, $mdat_offset + 2, $chapter_string_length), $chapter_matches)) {
1247
- $mdat_offset += (2 + $chapter_string_length);
1248
- @$info['quicktime']['comments']['chapters'][] = $chapter_matches[0];
1249
- }
1250
-
1251
-
1252
-
1253
- if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) {
1254
-
1255
- $info['avdataoffset'] = $atom_structure['offset'] + 8; // $info['quicktime'][$atomname]['offset'] + 8;
1256
- $OldAVDataEnd = $info['avdataend'];
1257
- $info['avdataend'] = $atom_structure['offset'] + $atom_structure['size']; // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size'];
1258
-
1259
- $getid3_temp = new getID3();
1260
- $getid3_temp->openfile($this->getid3->filename);
1261
- $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1262
- $getid3_temp->info['avdataend'] = $info['avdataend'];
1263
- $getid3_mp3 = new getid3_mp3($getid3_temp);
1264
- if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode($this->fread(4)))) {
1265
- $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
1266
- if (!empty($getid3_temp->info['warning'])) {
1267
- foreach ($getid3_temp->info['warning'] as $value) {
1268
- $info['warning'][] = $value;
1269
- }
1270
- }
1271
- if (!empty($getid3_temp->info['mpeg'])) {
1272
- $info['mpeg'] = $getid3_temp->info['mpeg'];
1273
- if (isset($info['mpeg']['audio'])) {
1274
- $info['audio']['dataformat'] = 'mp3';
1275
- $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')));
1276
- $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1277
- $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
1278
- $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1279
- $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1280
- $info['bitrate'] = $info['audio']['bitrate'];
1281
- }
1282
- }
1283
- }
1284
- unset($getid3_mp3, $getid3_temp);
1285
- $info['avdataend'] = $OldAVDataEnd;
1286
- unset($OldAVDataEnd);
1287
-
1288
- }
1289
-
1290
- unset($mdat_offset, $chapter_string_length, $chapter_matches);
1291
- break;
1292
-
1293
- case 'free': // FREE space atom
1294
- case 'skip': // SKIP atom
1295
- case 'wide': // 64-bit expansion placeholder atom
1296
- // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
1297
-
1298
- // When writing QuickTime files, it is sometimes necessary to update an atom's size.
1299
- // It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom
1300
- // is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime
1301
- // puts an 8-byte placeholder atom before any atoms it may have to update the size of.
1302
- // In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the
1303
- // placeholder atom can be overwritten to obtain the necessary 8 extra bytes.
1304
- // The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ).
1305
- break;
1306
-
1307
-
1308
- case 'nsav': // NoSAVe atom
1309
- // http://developer.apple.com/technotes/tn/tn2038.html
1310
- $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
1311
- break;
1312
-
1313
- case 'ctyp': // Controller TYPe atom (seen on QTVR)
1314
- // http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
1315
- // some controller names are:
1316
- // 0x00 + 'std' for linear movie
1317
- // 'none' for no controls
1318
- $atom_structure['ctyp'] = substr($atom_data, 0, 4);
1319
- $info['quicktime']['controller'] = $atom_structure['ctyp'];
1320
- switch ($atom_structure['ctyp']) {
1321
- case 'qtvr':
1322
- $info['video']['dataformat'] = 'quicktimevr';
1323
- break;
1324
- }
1325
- break;
1326
-
1327
- case 'pano': // PANOrama track (seen on QTVR)
1328
- $atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
1329
- break;
1330
-
1331
- case 'hint': // HINT track
1332
- case 'hinf': //
1333
- case 'hinv': //
1334
- case 'hnti': //
1335
- $info['quicktime']['hinting'] = true;
1336
- break;
1337
-
1338
- case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR)
1339
- for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) {
1340
- $atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
1341
- }
1342
- break;
1343
-
1344
-
1345
- // Observed-but-not-handled atom types are just listed here to prevent warnings being generated
1346
- case 'FXTC': // Something to do with Adobe After Effects (?)
1347
- case 'PrmA':
1348
- case 'code':
1349
- case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html
1350
- case 'tapt': // TrackApertureModeDimensionsAID - http://developer.apple.com/documentation/QuickTime/Reference/QT7-1_Update_Reference/Constants/Constants.html
1351
- // tapt seems to be used to compute the video size [http://www.getid3.org/phpBB3/viewtopic.php?t=838]
1352
- // * http://lists.apple.com/archives/quicktime-api/2006/Aug/msg00014.html
1353
- // * http://handbrake.fr/irclogs/handbrake-dev/handbrake-dev20080128_pg2.html
1354
- case 'ctts':// STCompositionOffsetAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1355
- case 'cslg':// STCompositionShiftLeastGreatestAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1356
- case 'sdtp':// STSampleDependencyAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1357
- case 'stps':// STPartialSyncSampleAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1358
- //$atom_structure['data'] = $atom_data;
1359
- break;
1360
-
1361
- case "\xA9".'xyz': // GPS latitude+longitude+altitude
1362
- $atom_structure['data'] = $atom_data;
1363
- if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) {
1364
- @list($all, $latitude, $longitude, $altitude) = $matches;
1365
- $info['quicktime']['comments']['gps_latitude'][] = floatval($latitude);
1366
- $info['quicktime']['comments']['gps_longitude'][] = floatval($longitude);
1367
- if (!empty($altitude)) {
1368
- $info['quicktime']['comments']['gps_altitude'][] = floatval($altitude);
1369
- }
1370
- } else {
1371
- $info['warning'][] = 'QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.';
1372
- }
1373
- break;
1374
-
1375
- case 'NCDT':
1376
- // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1377
- // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
1378
- $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms);
1379
- break;
1380
- case 'NCTH': // Nikon Camera THumbnail image
1381
- case 'NCVW': // Nikon Camera preVieW image
1382
- // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1383
- if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) {
1384
- $atom_structure['data'] = $atom_data;
1385
- $atom_structure['image_mime'] = 'image/jpeg';
1386
- $atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image'));
1387
- $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']);
1388
- }
1389
- break;
1390
- case 'NCTG': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
1391
- $atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data);
1392
- break;
1393
- case 'NCHD': // Nikon:MakerNoteVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1394
- case 'NCDB': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1395
- case 'CNCV': // Canon:CompressorVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Canon.html
1396
- $atom_structure['data'] = $atom_data;
1397
- break;
1398
-
1399
- case "\x00\x00\x00\x00":
1400
- case 'meta': // METAdata atom
1401
- // some kind of metacontainer, may contain a big data dump such as:
1402
- // mdta keys mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst data DEApple 0 (data DE2011-05-11T17:54:04+0200 2 *data DE+52.4936+013.3897+040.247/ data DE4.3.1 data DEiPhone 4
1403
- // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
1404
-
1405
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1406
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1407
- $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
1408
- //$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
1409
- break;
1410
-
1411
- case 'data': // metaDATA atom
1412
- // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data
1413
- $atom_structure['language'] = substr($atom_data, 4 + 0, 2);
1414
- $atom_structure['unknown'] = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2));
1415
- $atom_structure['data'] = substr($atom_data, 4 + 4);
1416
- break;
1417
-
1418
- default:
1419
- $info['warning'][] = 'Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).') at offset '.$baseoffset;
1420
- $atom_structure['data'] = $atom_data;
1421
- break;
1422
- }
1423
- array_pop($atomHierarchy);
1424
- return $atom_structure;
1425
- }
1426
-
1427
- public function QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
1428
- //echo 'QuicktimeParseContainerAtom('.substr($atom_data, 4, 4).') @ '.$baseoffset.'<br><br>';
1429
- $atom_structure = false;
1430
- $subatomoffset = 0;
1431
- $subatomcounter = 0;
1432
- if ((strlen($atom_data) == 4) && (getid3_lib::BigEndian2Int($atom_data) == 0x00000000)) {
1433
- return false;
1434
- }
1435
- while ($subatomoffset < strlen($atom_data)) {
1436
- $subatomsize = getid3_lib::BigEndian2Int(substr($atom_data, $subatomoffset + 0, 4));
1437
- $subatomname = substr($atom_data, $subatomoffset + 4, 4);
1438
- $subatomdata = substr($atom_data, $subatomoffset + 8, $subatomsize - 8);
1439
- if ($subatomsize == 0) {
1440
- // Furthermore, for historical reasons the list of atoms is optionally
1441
- // terminated by a 32-bit integer set to 0. If you are writing a program
1442
- // to read user data atoms, you should allow for the terminating 0.
1443
- return $atom_structure;
1444
- }
1445
-
1446
- $atom_structure[$subatomcounter] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms);
1447
-
1448
- $subatomoffset += $subatomsize;
1449
- $subatomcounter++;
1450
- }
1451
- return $atom_structure;
1452
- }
1453
-
1454
-
1455
- public function quicktime_read_mp4_descr_length($data, &$offset) {
1456
- // http://libquicktime.sourcearchive.com/documentation/2:1.0.2plus-pdebian-2build1/esds_8c-source.html
1457
- $num_bytes = 0;
1458
- $length = 0;
1459
- do {
1460
- $b = ord(substr($data, $offset++, 1));
1461
- $length = ($length << 7) | ($b & 0x7F);
1462
- } while (($b & 0x80) && ($num_bytes++ < 4));
1463
- return $length;
1464
- }
1465
-
1466
-
1467
- public function QuicktimeLanguageLookup($languageid) {
1468
- // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-34353
1469
- static $QuicktimeLanguageLookup = array();
1470
- if (empty($QuicktimeLanguageLookup)) {
1471
- $QuicktimeLanguageLookup[0] = 'English';
1472
- $QuicktimeLanguageLookup[1] = 'French';
1473
- $QuicktimeLanguageLookup[2] = 'German';
1474
- $QuicktimeLanguageLookup[3] = 'Italian';
1475
- $QuicktimeLanguageLookup[4] = 'Dutch';
1476
- $QuicktimeLanguageLookup[5] = 'Swedish';
1477
- $QuicktimeLanguageLookup[6] = 'Spanish';
1478
- $QuicktimeLanguageLookup[7] = 'Danish';
1479
- $QuicktimeLanguageLookup[8] = 'Portuguese';
1480
- $QuicktimeLanguageLookup[9] = 'Norwegian';
1481
- $QuicktimeLanguageLookup[10] = 'Hebrew';
1482
- $QuicktimeLanguageLookup[11] = 'Japanese';
1483
- $QuicktimeLanguageLookup[12] = 'Arabic';
1484
- $QuicktimeLanguageLookup[13] = 'Finnish';
1485
- $QuicktimeLanguageLookup[14] = 'Greek';
1486
- $QuicktimeLanguageLookup[15] = 'Icelandic';
1487
- $QuicktimeLanguageLookup[16] = 'Maltese';
1488
- $QuicktimeLanguageLookup[17] = 'Turkish';
1489
- $QuicktimeLanguageLookup[18] = 'Croatian';
1490
- $QuicktimeLanguageLookup[19] = 'Chinese (Traditional)';
1491
- $QuicktimeLanguageLookup[20] = 'Urdu';
1492
- $QuicktimeLanguageLookup[21] = 'Hindi';
1493
- $QuicktimeLanguageLookup[22] = 'Thai';
1494
- $QuicktimeLanguageLookup[23] = 'Korean';
1495
- $QuicktimeLanguageLookup[24] = 'Lithuanian';
1496
- $QuicktimeLanguageLookup[25] = 'Polish';
1497
- $QuicktimeLanguageLookup[26] = 'Hungarian';
1498
- $QuicktimeLanguageLookup[27] = 'Estonian';
1499
- $QuicktimeLanguageLookup[28] = 'Lettish';
1500
- $QuicktimeLanguageLookup[28] = 'Latvian';
1501
- $QuicktimeLanguageLookup[29] = 'Saamisk';
1502
- $QuicktimeLanguageLookup[29] = 'Lappish';
1503
- $QuicktimeLanguageLookup[30] = 'Faeroese';
1504
- $QuicktimeLanguageLookup[31] = 'Farsi';
1505
- $QuicktimeLanguageLookup[31] = 'Persian';
1506
- $QuicktimeLanguageLookup[32] = 'Russian';
1507
- $QuicktimeLanguageLookup[33] = 'Chinese (Simplified)';
1508
- $QuicktimeLanguageLookup[34] = 'Flemish';
1509
- $QuicktimeLanguageLookup[35] = 'Irish';
1510
- $QuicktimeLanguageLookup[36] = 'Albanian';
1511
- $QuicktimeLanguageLookup[37] = 'Romanian';
1512
- $QuicktimeLanguageLookup[38] = 'Czech';
1513
- $QuicktimeLanguageLookup[39] = 'Slovak';
1514
- $QuicktimeLanguageLookup[40] = 'Slovenian';
1515
- $QuicktimeLanguageLookup[41] = 'Yiddish';
1516
- $QuicktimeLanguageLookup[42] = 'Serbian';
1517
- $QuicktimeLanguageLookup[43] = 'Macedonian';
1518
- $QuicktimeLanguageLookup[44] = 'Bulgarian';
1519
- $QuicktimeLanguageLookup[45] = 'Ukrainian';
1520
- $QuicktimeLanguageLookup[46] = 'Byelorussian';
1521
- $QuicktimeLanguageLookup[47] = 'Uzbek';
1522
- $QuicktimeLanguageLookup[48] = 'Kazakh';
1523
- $QuicktimeLanguageLookup[49] = 'Azerbaijani';
1524
- $QuicktimeLanguageLookup[50] = 'AzerbaijanAr';
1525
- $QuicktimeLanguageLookup[51] = 'Armenian';
1526
- $QuicktimeLanguageLookup[52] = 'Georgian';
1527
- $QuicktimeLanguageLookup[53] = 'Moldavian';
1528
- $QuicktimeLanguageLookup[54] = 'Kirghiz';
1529
- $QuicktimeLanguageLookup[55] = 'Tajiki';
1530
- $QuicktimeLanguageLookup[56] = 'Turkmen';
1531
- $QuicktimeLanguageLookup[57] = 'Mongolian';
1532
- $QuicktimeLanguageLookup[58] = 'MongolianCyr';
1533
- $QuicktimeLanguageLookup[59] = 'Pashto';
1534
- $QuicktimeLanguageLookup[60] = 'Kurdish';
1535
- $QuicktimeLanguageLookup[61] = 'Kashmiri';
1536
- $QuicktimeLanguageLookup[62] = 'Sindhi';
1537
- $QuicktimeLanguageLookup[63] = 'Tibetan';
1538
- $QuicktimeLanguageLookup[64] = 'Nepali';
1539
- $QuicktimeLanguageLookup[65] = 'Sanskrit';
1540
- $QuicktimeLanguageLookup[66] = 'Marathi';
1541
- $QuicktimeLanguageLookup[67] = 'Bengali';
1542
- $QuicktimeLanguageLookup[68] = 'Assamese';
1543
- $QuicktimeLanguageLookup[69] = 'Gujarati';
1544
- $QuicktimeLanguageLookup[70] = 'Punjabi';
1545
- $QuicktimeLanguageLookup[71] = 'Oriya';
1546
- $QuicktimeLanguageLookup[72] = 'Malayalam';
1547
- $QuicktimeLanguageLookup[73] = 'Kannada';
1548
- $QuicktimeLanguageLookup[74] = 'Tamil';
1549
- $QuicktimeLanguageLookup[75] = 'Telugu';
1550
- $QuicktimeLanguageLookup[76] = 'Sinhalese';
1551
- $QuicktimeLanguageLookup[77] = 'Burmese';
1552
- $QuicktimeLanguageLookup[78] = 'Khmer';
1553
- $QuicktimeLanguageLookup[79] = 'Lao';
1554
- $QuicktimeLanguageLookup[80] = 'Vietnamese';
1555
- $QuicktimeLanguageLookup[81] = 'Indonesian';
1556
- $QuicktimeLanguageLookup[82] = 'Tagalog';
1557
- $QuicktimeLanguageLookup[83] = 'MalayRoman';
1558
- $QuicktimeLanguageLookup[84] = 'MalayArabic';
1559
- $QuicktimeLanguageLookup[85] = 'Amharic';
1560
- $QuicktimeLanguageLookup[86] = 'Tigrinya';
1561
- $QuicktimeLanguageLookup[87] = 'Galla';
1562
- $QuicktimeLanguageLookup[87] = 'Oromo';
1563
- $QuicktimeLanguageLookup[88] = 'Somali';
1564
- $QuicktimeLanguageLookup[89] = 'Swahili';
1565
- $QuicktimeLanguageLookup[90] = 'Ruanda';
1566
- $QuicktimeLanguageLookup[91] = 'Rundi';
1567
- $QuicktimeLanguageLookup[92] = 'Chewa';
1568
- $QuicktimeLanguageLookup[93] = 'Malagasy';
1569
- $QuicktimeLanguageLookup[94] = 'Esperanto';
1570
- $QuicktimeLanguageLookup[128] = 'Welsh';
1571
- $QuicktimeLanguageLookup[129] = 'Basque';
1572
- $QuicktimeLanguageLookup[130] = 'Catalan';
1573
- $QuicktimeLanguageLookup[131] = 'Latin';
1574
- $QuicktimeLanguageLookup[132] = 'Quechua';
1575
- $QuicktimeLanguageLookup[133] = 'Guarani';
1576
- $QuicktimeLanguageLookup[134] = 'Aymara';
1577
- $QuicktimeLanguageLookup[135] = 'Tatar';
1578
- $QuicktimeLanguageLookup[136] = 'Uighur';
1579
- $QuicktimeLanguageLookup[137] = 'Dzongkha';
1580
- $QuicktimeLanguageLookup[138] = 'JavaneseRom';
1581
- $QuicktimeLanguageLookup[32767] = 'Unspecified';
1582
- }
1583
- if (($languageid > 138) && ($languageid < 32767)) {
1584
- /*
1585
- ISO Language Codes - http://www.loc.gov/standards/iso639-2/php/code_list.php
1586
- 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.
1587
- The packing algorithm must map each of the three characters, which are always lowercase, into a 5-bit integer and then concatenate
1588
- 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.
1589
-
1590
- One algorithm for performing this packing is to treat each ISO character as a 16-bit integer. Subtract 0x60 from the first character
1591
- and multiply by 2^10 (0x400), subtract 0x60 from the second character and multiply by 2^5 (0x20), subtract 0x60 from the third character,
1592
- 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
1593
- significant bits and the most significant bit set to zero.
1594
- */
1595
- $iso_language_id = '';
1596
- $iso_language_id .= chr((($languageid & 0x7C00) >> 10) + 0x60);
1597
- $iso_language_id .= chr((($languageid & 0x03E0) >> 5) + 0x60);
1598
- $iso_language_id .= chr((($languageid & 0x001F) >> 0) + 0x60);
1599
- $QuicktimeLanguageLookup[$languageid] = getid3_id3v2::LanguageLookup($iso_language_id);
1600
- }
1601
- return (isset($QuicktimeLanguageLookup[$languageid]) ? $QuicktimeLanguageLookup[$languageid] : 'invalid');
1602
- }
1603
-
1604
- public function QuicktimeVideoCodecLookup($codecid) {
1605
- static $QuicktimeVideoCodecLookup = array();
1606
- if (empty($QuicktimeVideoCodecLookup)) {
1607
- $QuicktimeVideoCodecLookup['.SGI'] = 'SGI';
1608
- $QuicktimeVideoCodecLookup['3IV1'] = '3ivx MPEG-4 v1';
1609
- $QuicktimeVideoCodecLookup['3IV2'] = '3ivx MPEG-4 v2';
1610
- $QuicktimeVideoCodecLookup['3IVX'] = '3ivx MPEG-4';
1611
- $QuicktimeVideoCodecLookup['8BPS'] = 'Planar RGB';
1612
- $QuicktimeVideoCodecLookup['avc1'] = 'H.264/MPEG-4 AVC';
1613
- $QuicktimeVideoCodecLookup['avr '] = 'AVR-JPEG';
1614
- $QuicktimeVideoCodecLookup['b16g'] = '16Gray';
1615
- $QuicktimeVideoCodecLookup['b32a'] = '32AlphaGray';
1616
- $QuicktimeVideoCodecLookup['b48r'] = '48RGB';
1617
- $QuicktimeVideoCodecLookup['b64a'] = '64ARGB';
1618
- $QuicktimeVideoCodecLookup['base'] = 'Base';
1619
- $QuicktimeVideoCodecLookup['clou'] = 'Cloud';
1620
- $QuicktimeVideoCodecLookup['cmyk'] = 'CMYK';
1621
- $QuicktimeVideoCodecLookup['cvid'] = 'Cinepak';
1622
- $QuicktimeVideoCodecLookup['dmb1'] = 'OpenDML JPEG';
1623
- $QuicktimeVideoCodecLookup['dvc '] = 'DVC-NTSC';
1624
- $QuicktimeVideoCodecLookup['dvcp'] = 'DVC-PAL';
1625
- $QuicktimeVideoCodecLookup['dvpn'] = 'DVCPro-NTSC';
1626
- $QuicktimeVideoCodecLookup['dvpp'] = 'DVCPro-PAL';
1627
- $QuicktimeVideoCodecLookup['fire'] = 'Fire';
1628
- $QuicktimeVideoCodecLookup['flic'] = 'FLC';
1629
- $QuicktimeVideoCodecLookup['gif '] = 'GIF';
1630
- $QuicktimeVideoCodecLookup['h261'] = 'H261';
1631
- $QuicktimeVideoCodecLookup['h263'] = 'H263';
1632
- $QuicktimeVideoCodecLookup['IV41'] = 'Indeo4';
1633
- $QuicktimeVideoCodecLookup['jpeg'] = 'JPEG';
1634
- $QuicktimeVideoCodecLookup['kpcd'] = 'PhotoCD';
1635
- $QuicktimeVideoCodecLookup['mjpa'] = 'Motion JPEG-A';
1636
- $QuicktimeVideoCodecLookup['mjpb'] = 'Motion JPEG-B';
1637
- $QuicktimeVideoCodecLookup['msvc'] = 'Microsoft Video1';
1638
- $QuicktimeVideoCodecLookup['myuv'] = 'MPEG YUV420';
1639
- $QuicktimeVideoCodecLookup['path'] = 'Vector';
1640
- $QuicktimeVideoCodecLookup['png '] = 'PNG';
1641
- $QuicktimeVideoCodecLookup['PNTG'] = 'MacPaint';
1642
- $QuicktimeVideoCodecLookup['qdgx'] = 'QuickDrawGX';
1643
- $QuicktimeVideoCodecLookup['qdrw'] = 'QuickDraw';
1644
- $QuicktimeVideoCodecLookup['raw '] = 'RAW';
1645
- $QuicktimeVideoCodecLookup['ripl'] = 'WaterRipple';
1646
- $QuicktimeVideoCodecLookup['rpza'] = 'Video';
1647
- $QuicktimeVideoCodecLookup['smc '] = 'Graphics';
1648
- $QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 1';
1649
- $QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 3';
1650
- $QuicktimeVideoCodecLookup['syv9'] = 'Sorenson YUV9';
1651
- $QuicktimeVideoCodecLookup['tga '] = 'Targa';
1652
- $QuicktimeVideoCodecLookup['tiff'] = 'TIFF';
1653
- $QuicktimeVideoCodecLookup['WRAW'] = 'Windows RAW';
1654
- $QuicktimeVideoCodecLookup['WRLE'] = 'BMP';
1655
- $QuicktimeVideoCodecLookup['y420'] = 'YUV420';
1656
- $QuicktimeVideoCodecLookup['yuv2'] = 'ComponentVideo';
1657
- $QuicktimeVideoCodecLookup['yuvs'] = 'ComponentVideoUnsigned';
1658
- $QuicktimeVideoCodecLookup['yuvu'] = 'ComponentVideoSigned';
1659
- }
1660
- return (isset($QuicktimeVideoCodecLookup[$codecid]) ? $QuicktimeVideoCodecLookup[$codecid] : '');
1661
- }
1662
-
1663
- public function QuicktimeAudioCodecLookup($codecid) {
1664
- static $QuicktimeAudioCodecLookup = array();
1665
- if (empty($QuicktimeAudioCodecLookup)) {
1666
- $QuicktimeAudioCodecLookup['.mp3'] = 'Fraunhofer MPEG Layer-III alias';
1667
- $QuicktimeAudioCodecLookup['aac '] = 'ISO/IEC 14496-3 AAC';
1668
- $QuicktimeAudioCodecLookup['agsm'] = 'Apple GSM 10:1';
1669
- $QuicktimeAudioCodecLookup['alac'] = 'Apple Lossless Audio Codec';
1670
- $QuicktimeAudioCodecLookup['alaw'] = 'A-law 2:1';
1671
- $QuicktimeAudioCodecLookup['conv'] = 'Sample Format';
1672
- $QuicktimeAudioCodecLookup['dvca'] = 'DV';
1673
- $QuicktimeAudioCodecLookup['dvi '] = 'DV 4:1';
1674
- $QuicktimeAudioCodecLookup['eqal'] = 'Frequency Equalizer';
1675
- $QuicktimeAudioCodecLookup['fl32'] = '32-bit Floating Point';
1676
- $QuicktimeAudioCodecLookup['fl64'] = '64-bit Floating Point';
1677
- $QuicktimeAudioCodecLookup['ima4'] = 'Interactive Multimedia Association 4:1';
1678
- $QuicktimeAudioCodecLookup['in24'] = '24-bit Integer';
1679
- $QuicktimeAudioCodecLookup['in32'] = '32-bit Integer';
1680
- $QuicktimeAudioCodecLookup['lpc '] = 'LPC 23:1';
1681
- $QuicktimeAudioCodecLookup['MAC3'] = 'Macintosh Audio Compression/Expansion (MACE) 3:1';
1682
- $QuicktimeAudioCodecLookup['MAC6'] = 'Macintosh Audio Compression/Expansion (MACE) 6:1';
1683
- $QuicktimeAudioCodecLookup['mixb'] = '8-bit Mixer';
1684
- $QuicktimeAudioCodecLookup['mixw'] = '16-bit Mixer';
1685
- $QuicktimeAudioCodecLookup['mp4a'] = 'ISO/IEC 14496-3 AAC';
1686
- $QuicktimeAudioCodecLookup['MS'."\x00\x02"] = 'Microsoft ADPCM';
1687
- $QuicktimeAudioCodecLookup['MS'."\x00\x11"] = 'DV IMA';
1688
- $QuicktimeAudioCodecLookup['MS'."\x00\x55"] = 'Fraunhofer MPEG Layer III';
1689
- $QuicktimeAudioCodecLookup['NONE'] = 'No Encoding';
1690
- $QuicktimeAudioCodecLookup['Qclp'] = 'Qualcomm PureVoice';
1691
- $QuicktimeAudioCodecLookup['QDM2'] = 'QDesign Music 2';
1692
- $QuicktimeAudioCodecLookup['QDMC'] = 'QDesign Music 1';
1693
- $QuicktimeAudioCodecLookup['ratb'] = '8-bit Rate';
1694
- $QuicktimeAudioCodecLookup['ratw'] = '16-bit Rate';
1695
- $QuicktimeAudioCodecLookup['raw '] = 'raw PCM';
1696
- $QuicktimeAudioCodecLookup['sour'] = 'Sound Source';
1697
- $QuicktimeAudioCodecLookup['sowt'] = 'signed/two\'s complement (Little Endian)';
1698
- $QuicktimeAudioCodecLookup['str1'] = 'Iomega MPEG layer II';
1699
- $QuicktimeAudioCodecLookup['str2'] = 'Iomega MPEG *layer II';
1700
- $QuicktimeAudioCodecLookup['str3'] = 'Iomega MPEG **layer II';
1701
- $QuicktimeAudioCodecLookup['str4'] = 'Iomega MPEG ***layer II';
1702
- $QuicktimeAudioCodecLookup['twos'] = 'signed/two\'s complement (Big Endian)';
1703
- $QuicktimeAudioCodecLookup['ulaw'] = 'mu-law 2:1';
1704
- }
1705
- return (isset($QuicktimeAudioCodecLookup[$codecid]) ? $QuicktimeAudioCodecLookup[$codecid] : '');
1706
- }
1707
-
1708
- public function QuicktimeDCOMLookup($compressionid) {
1709
- static $QuicktimeDCOMLookup = array();
1710
- if (empty($QuicktimeDCOMLookup)) {
1711
- $QuicktimeDCOMLookup['zlib'] = 'ZLib Deflate';
1712
- $QuicktimeDCOMLookup['adec'] = 'Apple Compression';
1713
- }
1714
- return (isset($QuicktimeDCOMLookup[$compressionid]) ? $QuicktimeDCOMLookup[$compressionid] : '');
1715
- }
1716
-
1717
- public function QuicktimeColorNameLookup($colordepthid) {
1718
- static $QuicktimeColorNameLookup = array();
1719
- if (empty($QuicktimeColorNameLookup)) {
1720
- $QuicktimeColorNameLookup[1] = '2-color (monochrome)';
1721
- $QuicktimeColorNameLookup[2] = '4-color';
1722
- $QuicktimeColorNameLookup[4] = '16-color';
1723
- $QuicktimeColorNameLookup[8] = '256-color';
1724
- $QuicktimeColorNameLookup[16] = 'thousands (16-bit color)';
1725
- $QuicktimeColorNameLookup[24] = 'millions (24-bit color)';
1726
- $QuicktimeColorNameLookup[32] = 'millions+ (32-bit color)';
1727
- $QuicktimeColorNameLookup[33] = 'black & white';
1728
- $QuicktimeColorNameLookup[34] = '4-gray';
1729
- $QuicktimeColorNameLookup[36] = '16-gray';
1730
- $QuicktimeColorNameLookup[40] = '256-gray';
1731
- }
1732
- return (isset($QuicktimeColorNameLookup[$colordepthid]) ? $QuicktimeColorNameLookup[$colordepthid] : 'invalid');
1733
- }
1734
-
1735
- public function QuicktimeSTIKLookup($stik) {
1736
- static $QuicktimeSTIKLookup = array();
1737
- if (empty($QuicktimeSTIKLookup)) {
1738
- $QuicktimeSTIKLookup[0] = 'Movie';
1739
- $QuicktimeSTIKLookup[1] = 'Normal';
1740
- $QuicktimeSTIKLookup[2] = 'Audiobook';
1741
- $QuicktimeSTIKLookup[5] = 'Whacked Bookmark';
1742
- $QuicktimeSTIKLookup[6] = 'Music Video';
1743
- $QuicktimeSTIKLookup[9] = 'Short Film';
1744
- $QuicktimeSTIKLookup[10] = 'TV Show';
1745
- $QuicktimeSTIKLookup[11] = 'Booklet';
1746
- $QuicktimeSTIKLookup[14] = 'Ringtone';
1747
- $QuicktimeSTIKLookup[21] = 'Podcast';
1748
- }
1749
- return (isset($QuicktimeSTIKLookup[$stik]) ? $QuicktimeSTIKLookup[$stik] : 'invalid');
1750
- }
1751
-
1752
- public function QuicktimeIODSaudioProfileName($audio_profile_id) {
1753
- static $QuicktimeIODSaudioProfileNameLookup = array();
1754
- if (empty($QuicktimeIODSaudioProfileNameLookup)) {
1755
- $QuicktimeIODSaudioProfileNameLookup = array(
1756
- 0x00 => 'ISO Reserved (0x00)',
1757
- 0x01 => 'Main Audio Profile @ Level 1',
1758
- 0x02 => 'Main Audio Profile @ Level 2',
1759
- 0x03 => 'Main Audio Profile @ Level 3',
1760
- 0x04 => 'Main Audio Profile @ Level 4',
1761
- 0x05 => 'Scalable Audio Profile @ Level 1',
1762
- 0x06 => 'Scalable Audio Profile @ Level 2',
1763
- 0x07 => 'Scalable Audio Profile @ Level 3',
1764
- 0x08 => 'Scalable Audio Profile @ Level 4',
1765
- 0x09 => 'Speech Audio Profile @ Level 1',
1766
- 0x0A => 'Speech Audio Profile @ Level 2',
1767
- 0x0B => 'Synthetic Audio Profile @ Level 1',
1768
- 0x0C => 'Synthetic Audio Profile @ Level 2',
1769
- 0x0D => 'Synthetic Audio Profile @ Level 3',
1770
- 0x0E => 'High Quality Audio Profile @ Level 1',
1771
- 0x0F => 'High Quality Audio Profile @ Level 2',
1772
- 0x10 => 'High Quality Audio Profile @ Level 3',
1773
- 0x11 => 'High Quality Audio Profile @ Level 4',
1774
- 0x12 => 'High Quality Audio Profile @ Level 5',
1775
- 0x13 => 'High Quality Audio Profile @ Level 6',
1776
- 0x14 => 'High Quality Audio Profile @ Level 7',
1777
- 0x15 => 'High Quality Audio Profile @ Level 8',
1778
- 0x16 => 'Low Delay Audio Profile @ Level 1',
1779
- 0x17 => 'Low Delay Audio Profile @ Level 2',
1780
- 0x18 => 'Low Delay Audio Profile @ Level 3',
1781
- 0x19 => 'Low Delay Audio Profile @ Level 4',
1782
- 0x1A => 'Low Delay Audio Profile @ Level 5',
1783
- 0x1B => 'Low Delay Audio Profile @ Level 6',
1784
- 0x1C => 'Low Delay Audio Profile @ Level 7',
1785
- 0x1D => 'Low Delay Audio Profile @ Level 8',
1786
- 0x1E => 'Natural Audio Profile @ Level 1',
1787
- 0x1F => 'Natural Audio Profile @ Level 2',
1788
- 0x20 => 'Natural Audio Profile @ Level 3',
1789
- 0x21 => 'Natural Audio Profile @ Level 4',
1790
- 0x22 => 'Mobile Audio Internetworking Profile @ Level 1',
1791
- 0x23 => 'Mobile Audio Internetworking Profile @ Level 2',
1792
- 0x24 => 'Mobile Audio Internetworking Profile @ Level 3',
1793
- 0x25 => 'Mobile Audio Internetworking Profile @ Level 4',
1794
- 0x26 => 'Mobile Audio Internetworking Profile @ Level 5',
1795
- 0x27 => 'Mobile Audio Internetworking Profile @ Level 6',
1796
- 0x28 => 'AAC Profile @ Level 1',
1797
- 0x29 => 'AAC Profile @ Level 2',
1798
- 0x2A => 'AAC Profile @ Level 4',
1799
- 0x2B => 'AAC Profile @ Level 5',
1800
- 0x2C => 'High Efficiency AAC Profile @ Level 2',
1801
- 0x2D => 'High Efficiency AAC Profile @ Level 3',
1802
- 0x2E => 'High Efficiency AAC Profile @ Level 4',
1803
- 0x2F => 'High Efficiency AAC Profile @ Level 5',
1804
- 0xFE => 'Not part of MPEG-4 audio profiles',
1805
- 0xFF => 'No audio capability required',
1806
- );
1807
- }
1808
- return (isset($QuicktimeIODSaudioProfileNameLookup[$audio_profile_id]) ? $QuicktimeIODSaudioProfileNameLookup[$audio_profile_id] : 'ISO Reserved / User Private');
1809
- }
1810
-
1811
-
1812
- public function QuicktimeIODSvideoProfileName($video_profile_id) {
1813
- static $QuicktimeIODSvideoProfileNameLookup = array();
1814
- if (empty($QuicktimeIODSvideoProfileNameLookup)) {
1815
- $QuicktimeIODSvideoProfileNameLookup = array(
1816
- 0x00 => 'Reserved (0x00) Profile',
1817
- 0x01 => 'Simple Profile @ Level 1',
1818
- 0x02 => 'Simple Profile @ Level 2',
1819
- 0x03 => 'Simple Profile @ Level 3',
1820
- 0x08 => 'Simple Profile @ Level 0',
1821
- 0x10 => 'Simple Scalable Profile @ Level 0',
1822
- 0x11 => 'Simple Scalable Profile @ Level 1',
1823
- 0x12 => 'Simple Scalable Profile @ Level 2',
1824
- 0x15 => 'AVC/H264 Profile',
1825
- 0x21 => 'Core Profile @ Level 1',
1826
- 0x22 => 'Core Profile @ Level 2',
1827
- 0x32 => 'Main Profile @ Level 2',
1828
- 0x33 => 'Main Profile @ Level 3',
1829
- 0x34 => 'Main Profile @ Level 4',
1830
- 0x42 => 'N-bit Profile @ Level 2',
1831
- 0x51 => 'Scalable Texture Profile @ Level 1',
1832
- 0x61 => 'Simple Face Animation Profile @ Level 1',
1833
- 0x62 => 'Simple Face Animation Profile @ Level 2',
1834
- 0x63 => 'Simple FBA Profile @ Level 1',
1835
- 0x64 => 'Simple FBA Profile @ Level 2',
1836
- 0x71 => 'Basic Animated Texture Profile @ Level 1',
1837
- 0x72 => 'Basic Animated Texture Profile @ Level 2',
1838
- 0x81 => 'Hybrid Profile @ Level 1',
1839
- 0x82 => 'Hybrid Profile @ Level 2',
1840
- 0x91 => 'Advanced Real Time Simple Profile @ Level 1',
1841
- 0x92 => 'Advanced Real Time Simple Profile @ Level 2',
1842
- 0x93 => 'Advanced Real Time Simple Profile @ Level 3',
1843
- 0x94 => 'Advanced Real Time Simple Profile @ Level 4',
1844
- 0xA1 => 'Core Scalable Profile @ Level1',
1845
- 0xA2 => 'Core Scalable Profile @ Level2',
1846
- 0xA3 => 'Core Scalable Profile @ Level3',
1847
- 0xB1 => 'Advanced Coding Efficiency Profile @ Level 1',
1848
- 0xB2 => 'Advanced Coding Efficiency Profile @ Level 2',
1849
- 0xB3 => 'Advanced Coding Efficiency Profile @ Level 3',
1850
- 0xB4 => 'Advanced Coding Efficiency Profile @ Level 4',
1851
- 0xC1 => 'Advanced Core Profile @ Level 1',
1852
- 0xC2 => 'Advanced Core Profile @ Level 2',
1853
- 0xD1 => 'Advanced Scalable Texture @ Level1',
1854
- 0xD2 => 'Advanced Scalable Texture @ Level2',
1855
- 0xE1 => 'Simple Studio Profile @ Level 1',
1856
- 0xE2 => 'Simple Studio Profile @ Level 2',
1857
- 0xE3 => 'Simple Studio Profile @ Level 3',
1858
- 0xE4 => 'Simple Studio Profile @ Level 4',
1859
- 0xE5 => 'Core Studio Profile @ Level 1',
1860
- 0xE6 => 'Core Studio Profile @ Level 2',
1861
- 0xE7 => 'Core Studio Profile @ Level 3',
1862
- 0xE8 => 'Core Studio Profile @ Level 4',
1863
- 0xF0 => 'Advanced Simple Profile @ Level 0',
1864
- 0xF1 => 'Advanced Simple Profile @ Level 1',
1865
- 0xF2 => 'Advanced Simple Profile @ Level 2',
1866
- 0xF3 => 'Advanced Simple Profile @ Level 3',
1867
- 0xF4 => 'Advanced Simple Profile @ Level 4',
1868
- 0xF5 => 'Advanced Simple Profile @ Level 5',
1869
- 0xF7 => 'Advanced Simple Profile @ Level 3b',
1870
- 0xF8 => 'Fine Granularity Scalable Profile @ Level 0',
1871
- 0xF9 => 'Fine Granularity Scalable Profile @ Level 1',
1872
- 0xFA => 'Fine Granularity Scalable Profile @ Level 2',
1873
- 0xFB => 'Fine Granularity Scalable Profile @ Level 3',
1874
- 0xFC => 'Fine Granularity Scalable Profile @ Level 4',
1875
- 0xFD => 'Fine Granularity Scalable Profile @ Level 5',
1876
- 0xFE => 'Not part of MPEG-4 Visual profiles',
1877
- 0xFF => 'No visual capability required',
1878
- );
1879
- }
1880
- return (isset($QuicktimeIODSvideoProfileNameLookup[$video_profile_id]) ? $QuicktimeIODSvideoProfileNameLookup[$video_profile_id] : 'ISO Reserved Profile');
1881
- }
1882
-
1883
-
1884
- public function QuicktimeContentRatingLookup($rtng) {
1885
- static $QuicktimeContentRatingLookup = array();
1886
- if (empty($QuicktimeContentRatingLookup)) {
1887
- $QuicktimeContentRatingLookup[0] = 'None';
1888
- $QuicktimeContentRatingLookup[2] = 'Clean';
1889
- $QuicktimeContentRatingLookup[4] = 'Explicit';
1890
- }
1891
- return (isset($QuicktimeContentRatingLookup[$rtng]) ? $QuicktimeContentRatingLookup[$rtng] : 'invalid');
1892
- }
1893
-
1894
- public function QuicktimeStoreAccountTypeLookup($akid) {
1895
- static $QuicktimeStoreAccountTypeLookup = array();
1896
- if (empty($QuicktimeStoreAccountTypeLookup)) {
1897
- $QuicktimeStoreAccountTypeLookup[0] = 'iTunes';
1898
- $QuicktimeStoreAccountTypeLookup[1] = 'AOL';
1899
- }
1900
- return (isset($QuicktimeStoreAccountTypeLookup[$akid]) ? $QuicktimeStoreAccountTypeLookup[$akid] : 'invalid');
1901
- }
1902
-
1903
- public function QuicktimeStoreFrontCodeLookup($sfid) {
1904
- static $QuicktimeStoreFrontCodeLookup = array();
1905
- if (empty($QuicktimeStoreFrontCodeLookup)) {
1906
- $QuicktimeStoreFrontCodeLookup[143460] = 'Australia';
1907
- $QuicktimeStoreFrontCodeLookup[143445] = 'Austria';
1908
- $QuicktimeStoreFrontCodeLookup[143446] = 'Belgium';
1909
- $QuicktimeStoreFrontCodeLookup[143455] = 'Canada';
1910
- $QuicktimeStoreFrontCodeLookup[143458] = 'Denmark';
1911
- $QuicktimeStoreFrontCodeLookup[143447] = 'Finland';
1912
- $QuicktimeStoreFrontCodeLookup[143442] = 'France';
1913
- $QuicktimeStoreFrontCodeLookup[143443] = 'Germany';
1914
- $QuicktimeStoreFrontCodeLookup[143448] = 'Greece';
1915
- $QuicktimeStoreFrontCodeLookup[143449] = 'Ireland';
1916
- $QuicktimeStoreFrontCodeLookup[143450] = 'Italy';
1917
- $QuicktimeStoreFrontCodeLookup[143462] = 'Japan';
1918
- $QuicktimeStoreFrontCodeLookup[143451] = 'Luxembourg';
1919
- $QuicktimeStoreFrontCodeLookup[143452] = 'Netherlands';
1920
- $QuicktimeStoreFrontCodeLookup[143461] = 'New Zealand';
1921
- $QuicktimeStoreFrontCodeLookup[143457] = 'Norway';
1922
- $QuicktimeStoreFrontCodeLookup[143453] = 'Portugal';
1923
- $QuicktimeStoreFrontCodeLookup[143454] = 'Spain';
1924
- $QuicktimeStoreFrontCodeLookup[143456] = 'Sweden';
1925
- $QuicktimeStoreFrontCodeLookup[143459] = 'Switzerland';
1926
- $QuicktimeStoreFrontCodeLookup[143444] = 'United Kingdom';
1927
- $QuicktimeStoreFrontCodeLookup[143441] = 'United States';
1928
- }
1929
- return (isset($QuicktimeStoreFrontCodeLookup[$sfid]) ? $QuicktimeStoreFrontCodeLookup[$sfid] : 'invalid');
1930
- }
1931
-
1932
- public function QuicktimeParseNikonNCTG($atom_data) {
1933
- // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
1934
- // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
1935
- // Data is stored as records of:
1936
- // * 4 bytes record type
1937
- // * 2 bytes size of data field type:
1938
- // 0x0001 = flag (size field *= 1-byte)
1939
- // 0x0002 = char (size field *= 1-byte)
1940
- // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
1941
- // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
1942
- // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
1943
- // 0x0007 = bytes (size field *= 1-byte), values are stored as ??????
1944
- // 0x0008 = ????? (size field *= 2-byte), values are stored as ??????
1945
- // * 2 bytes data size field
1946
- // * ? bytes data (string data may be null-padded; datestamp fields are in the format "2011:05:25 20:24:15")
1947
- // all integers are stored BigEndian
1948
-
1949
- $NCTGtagName = array(
1950
- 0x00000001 => 'Make',
1951
- 0x00000002 => 'Model',
1952
- 0x00000003 => 'Software',
1953
- 0x00000011 => 'CreateDate',
1954
- 0x00000012 => 'DateTimeOriginal',
1955
- 0x00000013 => 'FrameCount',
1956
- 0x00000016 => 'FrameRate',
1957
- 0x00000022 => 'FrameWidth',
1958
- 0x00000023 => 'FrameHeight',
1959
- 0x00000032 => 'AudioChannels',
1960
- 0x00000033 => 'AudioBitsPerSample',
1961
- 0x00000034 => 'AudioSampleRate',
1962
- 0x02000001 => 'MakerNoteVersion',
1963
- 0x02000005 => 'WhiteBalance',
1964
- 0x0200000b => 'WhiteBalanceFineTune',
1965
- 0x0200001e => 'ColorSpace',
1966
- 0x02000023 => 'PictureControlData',
1967
- 0x02000024 => 'WorldTime',
1968
- 0x02000032 => 'UnknownInfo',
1969
- 0x02000083 => 'LensType',
1970
- 0x02000084 => 'Lens',
1971
- );
1972
-
1973
- $offset = 0;
1974
- $datalength = strlen($atom_data);
1975
- $parsed = array();
1976
- while ($offset < $datalength) {
1977
- //echo getid3_lib::PrintHexBytes(substr($atom_data, $offset, 4)).'<br>';
1978
- $record_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4)); $offset += 4;
1979
- $data_size_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2;
1980
- $data_size = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2;
1981
- switch ($data_size_type) {
1982
- case 0x0001: // 0x0001 = flag (size field *= 1-byte)
1983
- $data = getid3_lib::BigEndian2Int(substr($atom_data, $offset, $data_size * 1));
1984
- $offset += ($data_size * 1);
1985
- break;
1986
- case 0x0002: // 0x0002 = char (size field *= 1-byte)
1987
- $data = substr($atom_data, $offset, $data_size * 1);
1988
- $offset += ($data_size * 1);
1989
- $data = rtrim($data, "\x00");
1990
- break;
1991
- case 0x0003: // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
1992
- $data = '';
1993
- for ($i = $data_size - 1; $i >= 0; $i--) {
1994
- $data .= substr($atom_data, $offset + ($i * 2), 2);
1995
- }
1996
- $data = getid3_lib::BigEndian2Int($data);
1997
- $offset += ($data_size * 2);
1998
- break;
1999
- case 0x0004: // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
2000
- $data = '';
2001
- for ($i = $data_size - 1; $i >= 0; $i--) {
2002
- $data .= substr($atom_data, $offset + ($i * 4), 4);
2003
- }
2004
- $data = getid3_lib::BigEndian2Int($data);
2005
- $offset += ($data_size * 4);
2006
- break;
2007
- case 0x0005: // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
2008
- $data = array();
2009
- for ($i = 0; $i < $data_size; $i++) {
2010
- $numerator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 0, 4));
2011
- $denomninator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 4, 4));
2012
- if ($denomninator == 0) {
2013
- $data[$i] = false;
2014
- } else {
2015
- $data[$i] = (double) $numerator / $denomninator;
2016
- }
2017
- }
2018
- $offset += (8 * $data_size);
2019
- if (count($data) == 1) {
2020
- $data = $data[0];
2021
- }
2022
- break;
2023
- case 0x0007: // 0x0007 = bytes (size field *= 1-byte), values are stored as ??????
2024
- $data = substr($atom_data, $offset, $data_size * 1);
2025
- $offset += ($data_size * 1);
2026
- break;
2027
- case 0x0008: // 0x0008 = ????? (size field *= 2-byte), values are stored as ??????
2028
- $data = substr($atom_data, $offset, $data_size * 2);
2029
- $offset += ($data_size * 2);
2030
- break;
2031
- default:
2032
- echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br>';
2033
- break 2;
2034
- }
2035
-
2036
- switch ($record_type) {
2037
- case 0x00000011: // CreateDate
2038
- case 0x00000012: // DateTimeOriginal
2039
- $data = strtotime($data);
2040
- break;
2041
- case 0x0200001e: // ColorSpace
2042
- switch ($data) {
2043
- case 1:
2044
- $data = 'sRGB';
2045
- break;
2046
- case 2:
2047
- $data = 'Adobe RGB';
2048
- break;
2049
- }
2050
- break;
2051
- case 0x02000023: // PictureControlData
2052
- $PictureControlAdjust = array(0=>'default', 1=>'quick', 2=>'full');
2053
- $FilterEffect = array(0x80=>'off', 0x81=>'yellow', 0x82=>'orange', 0x83=>'red', 0x84=>'green', 0xff=>'n/a');
2054
- $ToningEffect = array(0x80=>'b&w', 0x81=>'sepia', 0x82=>'cyanotype', 0x83=>'red', 0x84=>'yellow', 0x85=>'green', 0x86=>'blue-green', 0x87=>'blue', 0x88=>'purple-blue', 0x89=>'red-purple', 0xff=>'n/a');
2055
- $data = array(
2056
- 'PictureControlVersion' => substr($data, 0, 4),
2057
- 'PictureControlName' => rtrim(substr($data, 4, 20), "\x00"),
2058
- 'PictureControlBase' => rtrim(substr($data, 24, 20), "\x00"),
2059
- //'?' => substr($data, 44, 4),
2060
- 'PictureControlAdjust' => $PictureControlAdjust[ord(substr($data, 48, 1))],
2061
- 'PictureControlQuickAdjust' => ord(substr($data, 49, 1)),
2062
- 'Sharpness' => ord(substr($data, 50, 1)),
2063
- 'Contrast' => ord(substr($data, 51, 1)),
2064
- 'Brightness' => ord(substr($data, 52, 1)),
2065
- 'Saturation' => ord(substr($data, 53, 1)),
2066
- 'HueAdjustment' => ord(substr($data, 54, 1)),
2067
- 'FilterEffect' => $FilterEffect[ord(substr($data, 55, 1))],
2068
- 'ToningEffect' => $ToningEffect[ord(substr($data, 56, 1))],
2069
- 'ToningSaturation' => ord(substr($data, 57, 1)),
2070
- );
2071
- break;
2072
- case 0x02000024: // WorldTime
2073
- // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#WorldTime
2074
- // timezone is stored as offset from GMT in minutes
2075
- $timezone = getid3_lib::BigEndian2Int(substr($data, 0, 2));
2076
- if ($timezone & 0x8000) {
2077
- $timezone = 0 - (0x10000 - $timezone);
2078
- }
2079
- $timezone /= 60;
2080
-
2081
- $dst = (bool) getid3_lib::BigEndian2Int(substr($data, 2, 1));
2082
- switch (getid3_lib::BigEndian2Int(substr($data, 3, 1))) {
2083
- case 2:
2084
- $datedisplayformat = 'D/M/Y'; break;
2085
- case 1:
2086
- $datedisplayformat = 'M/D/Y'; break;
2087
- case 0:
2088
- default:
2089
- $datedisplayformat = 'Y/M/D'; break;
2090
- }
2091
-
2092
- $data = array('timezone'=>floatval($timezone), 'dst'=>$dst, 'display'=>$datedisplayformat);
2093
- break;
2094
- case 0x02000083: // LensType
2095
- $data = array(
2096
- //'_' => $data,
2097
- 'mf' => (bool) ($data & 0x01),
2098
- 'd' => (bool) ($data & 0x02),
2099
- 'g' => (bool) ($data & 0x04),
2100
- 'vr' => (bool) ($data & 0x08),
2101
- );
2102
- break;
2103
- }
2104
- $tag_name = (isset($NCTGtagName[$record_type]) ? $NCTGtagName[$record_type] : '0x'.str_pad(dechex($record_type), 8, '0', STR_PAD_LEFT));
2105
- $parsed[$tag_name] = $data;
2106
- }
2107
- return $parsed;
2108
- }
2109
-
2110
-
2111
- public function CopyToAppropriateCommentsSection($keyname, $data, $boxname='') {
2112
- static $handyatomtranslatorarray = array();
2113
- if (empty($handyatomtranslatorarray)) {
2114
- $handyatomtranslatorarray["\xA9".'cpy'] = 'copyright';
2115
- $handyatomtranslatorarray["\xA9".'day'] = 'creation_date'; // iTunes 4.0
2116
- $handyatomtranslatorarray["\xA9".'dir'] = 'director';
2117
- $handyatomtranslatorarray["\xA9".'ed1'] = 'edit1';
2118
- $handyatomtranslatorarray["\xA9".'ed2'] = 'edit2';
2119
- $handyatomtranslatorarray["\xA9".'ed3'] = 'edit3';
2120
- $handyatomtranslatorarray["\xA9".'ed4'] = 'edit4';
2121
- $handyatomtranslatorarray["\xA9".'ed5'] = 'edit5';
2122
- $handyatomtranslatorarray["\xA9".'ed6'] = 'edit6';
2123
- $handyatomtranslatorarray["\xA9".'ed7'] = 'edit7';
2124
- $handyatomtranslatorarray["\xA9".'ed8'] = 'edit8';
2125
- $handyatomtranslatorarray["\xA9".'ed9'] = 'edit9';
2126
- $handyatomtranslatorarray["\xA9".'fmt'] = 'format';
2127
- $handyatomtranslatorarray["\xA9".'inf'] = 'information';
2128
- $handyatomtranslatorarray["\xA9".'prd'] = 'producer';
2129
- $handyatomtranslatorarray["\xA9".'prf'] = 'performers';
2130
- $handyatomtranslatorarray["\xA9".'req'] = 'system_requirements';
2131
- $handyatomtranslatorarray["\xA9".'src'] = 'source_credit';
2132
- $handyatomtranslatorarray["\xA9".'wrt'] = 'writer';
2133
-
2134
- // http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
2135
- $handyatomtranslatorarray["\xA9".'nam'] = 'title'; // iTunes 4.0
2136
- $handyatomtranslatorarray["\xA9".'cmt'] = 'comment'; // iTunes 4.0
2137
- $handyatomtranslatorarray["\xA9".'wrn'] = 'warning';
2138
- $handyatomtranslatorarray["\xA9".'hst'] = 'host_computer';
2139
- $handyatomtranslatorarray["\xA9".'mak'] = 'make';
2140
- $handyatomtranslatorarray["\xA9".'mod'] = 'model';
2141
- $handyatomtranslatorarray["\xA9".'PRD'] = 'product';
2142
- $handyatomtranslatorarray["\xA9".'swr'] = 'software';
2143
- $handyatomtranslatorarray["\xA9".'aut'] = 'author';
2144
- $handyatomtranslatorarray["\xA9".'ART'] = 'artist';
2145
- $handyatomtranslatorarray["\xA9".'trk'] = 'track';
2146
- $handyatomtranslatorarray["\xA9".'alb'] = 'album'; // iTunes 4.0
2147
- $handyatomtranslatorarray["\xA9".'com'] = 'comment';
2148
- $handyatomtranslatorarray["\xA9".'gen'] = 'genre'; // iTunes 4.0
2149
- $handyatomtranslatorarray["\xA9".'ope'] = 'composer';
2150
- $handyatomtranslatorarray["\xA9".'url'] = 'url';
2151
- $handyatomtranslatorarray["\xA9".'enc'] = 'encoder';
2152
-
2153
- // http://atomicparsley.sourceforge.net/mpeg-4files.html
2154
- $handyatomtranslatorarray["\xA9".'art'] = 'artist'; // iTunes 4.0
2155
- $handyatomtranslatorarray['aART'] = 'album_artist';
2156
- $handyatomtranslatorarray['trkn'] = 'track_number'; // iTunes 4.0
2157
- $handyatomtranslatorarray['disk'] = 'disc_number'; // iTunes 4.0
2158
- $handyatomtranslatorarray['gnre'] = 'genre'; // iTunes 4.0
2159
- $handyatomtranslatorarray["\xA9".'too'] = 'encoder'; // iTunes 4.0
2160
- $handyatomtranslatorarray['tmpo'] = 'bpm'; // iTunes 4.0
2161
- $handyatomtranslatorarray['cprt'] = 'copyright'; // iTunes 4.0?
2162
- $handyatomtranslatorarray['cpil'] = 'compilation'; // iTunes 4.0
2163
- $handyatomtranslatorarray['covr'] = 'picture'; // iTunes 4.0
2164
- $handyatomtranslatorarray['rtng'] = 'rating'; // iTunes 4.0
2165
- $handyatomtranslatorarray["\xA9".'grp'] = 'grouping'; // iTunes 4.2
2166
- $handyatomtranslatorarray['stik'] = 'stik'; // iTunes 4.9
2167
- $handyatomtranslatorarray['pcst'] = 'podcast'; // iTunes 4.9
2168
- $handyatomtranslatorarray['catg'] = 'category'; // iTunes 4.9
2169
- $handyatomtranslatorarray['keyw'] = 'keyword'; // iTunes 4.9
2170
- $handyatomtranslatorarray['purl'] = 'podcast_url'; // iTunes 4.9
2171
- $handyatomtranslatorarray['egid'] = 'episode_guid'; // iTunes 4.9
2172
- $handyatomtranslatorarray['desc'] = 'description'; // iTunes 5.0
2173
- $handyatomtranslatorarray["\xA9".'lyr'] = 'lyrics'; // iTunes 5.0
2174
- $handyatomtranslatorarray['tvnn'] = 'tv_network_name'; // iTunes 6.0
2175
- $handyatomtranslatorarray['tvsh'] = 'tv_show_name'; // iTunes 6.0
2176
- $handyatomtranslatorarray['tvsn'] = 'tv_season'; // iTunes 6.0
2177
- $handyatomtranslatorarray['tves'] = 'tv_episode'; // iTunes 6.0
2178
- $handyatomtranslatorarray['purd'] = 'purchase_date'; // iTunes 6.0.2
2179
- $handyatomtranslatorarray['pgap'] = 'gapless_playback'; // iTunes 7.0
2180
-
2181
- // http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
2182
-
2183
-
2184
-
2185
- // boxnames:
2186
- /*
2187
- $handyatomtranslatorarray['iTunSMPB'] = 'iTunSMPB';
2188
- $handyatomtranslatorarray['iTunNORM'] = 'iTunNORM';
2189
- $handyatomtranslatorarray['Encoding Params'] = 'Encoding Params';
2190
- $handyatomtranslatorarray['replaygain_track_gain'] = 'replaygain_track_gain';
2191
- $handyatomtranslatorarray['replaygain_track_peak'] = 'replaygain_track_peak';
2192
- $handyatomtranslatorarray['replaygain_track_minmax'] = 'replaygain_track_minmax';
2193
- $handyatomtranslatorarray['MusicIP PUID'] = 'MusicIP PUID';
2194
- $handyatomtranslatorarray['MusicBrainz Artist Id'] = 'MusicBrainz Artist Id';
2195
- $handyatomtranslatorarray['MusicBrainz Album Id'] = 'MusicBrainz Album Id';
2196
- $handyatomtranslatorarray['MusicBrainz Album Artist Id'] = 'MusicBrainz Album Artist Id';
2197
- $handyatomtranslatorarray['MusicBrainz Track Id'] = 'MusicBrainz Track Id';
2198
- $handyatomtranslatorarray['MusicBrainz Disc Id'] = 'MusicBrainz Disc Id';
2199
-
2200
- // http://age.hobba.nl/audio/tag_frame_reference.html
2201
- $handyatomtranslatorarray['PLAY_COUNTER'] = 'play_counter'; // Foobar2000 - http://www.getid3.org/phpBB3/viewtopic.php?t=1355
2202
- $handyatomtranslatorarray['MEDIATYPE'] = 'mediatype'; // Foobar2000 - http://www.getid3.org/phpBB3/viewtopic.php?t=1355
2203
- */
2204
- }
2205
- $info = &$this->getid3->info;
2206
- $comment_key = '';
2207
- if ($boxname && ($boxname != $keyname)) {
2208
- $comment_key = (isset($handyatomtranslatorarray[$boxname]) ? $handyatomtranslatorarray[$boxname] : $boxname);
2209
- } elseif (isset($handyatomtranslatorarray[$keyname])) {
2210
- $comment_key = $handyatomtranslatorarray[$keyname];
2211
- }
2212
- if ($comment_key) {
2213
- if ($comment_key == 'picture') {
2214
- if (!is_array($data)) {
2215
- $image_mime = '';
2216
- if (preg_match('#^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A#', $data)) {
2217
- $image_mime = 'image/png';
2218
- } elseif (preg_match('#^\xFF\xD8\xFF#', $data)) {
2219
- $image_mime = 'image/jpeg';
2220
- } elseif (preg_match('#^GIF#', $data)) {
2221
- $image_mime = 'image/gif';
2222
- } elseif (preg_match('#^BM#', $data)) {
2223
- $image_mime = 'image/bmp';
2224
- }
2225
- $data = array('data'=>$data, 'image_mime'=>$image_mime);
2226
- }
2227
- }
2228
- $info['quicktime']['comments'][$comment_key][] = $data;
2229
- }
2230
- return true;
2231
- }
2232
-
2233
- public function NoNullString($nullterminatedstring) {
2234
- // remove the single null terminator on null terminated strings
2235
- if (substr($nullterminatedstring, strlen($nullterminatedstring) - 1, 1) === "\x00") {
2236
- return substr($nullterminatedstring, 0, strlen($nullterminatedstring) - 1);
2237
- }
2238
- return $nullterminatedstring;
2239
- }
2240
-
2241
- public function Pascal2String($pascalstring) {
2242
- // Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string
2243
- return substr($pascalstring, 1);
2244
- }
2245
-
2246
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /////////////////////////////////////////////////////////////////
3
+ /// getID3() by James Heinrich <info@getid3.org> //
4
+ // available at http://getid3.sourceforge.net //
5
+ // or http://www.getid3.org //
6
+ // also https://github.com/JamesHeinrich/getID3 //
7
+ /////////////////////////////////////////////////////////////////
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
+ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
19
+ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); // needed for ISO 639-2 language code lookup
20
+
21
+ class getid3_quicktime extends getid3_handler
22
+ {
23
+
24
+ public $ReturnAtomData = true;
25
+ public $ParseAllPossibleAtoms = false;
26
+
27
+ public function Analyze() {
28
+ $info = &$this->getid3->info;
29
+
30
+ $info['fileformat'] = 'quicktime';
31
+ $info['quicktime']['hinting'] = false;
32
+ $info['quicktime']['controller'] = 'standard'; // may be overridden if 'ctyp' atom is present
33
+
34
+ $this->fseek($info['avdataoffset']);
35
+
36
+ $offset = 0;
37
+ $atomcounter = 0;
38
+ $atom_data_read_buffer_size = ($info['php_memory_limit'] ? round($info['php_memory_limit'] / 2) : $this->getid3->option_fread_buffer_size * 1024); // allow [default: 32MB] if PHP configured with no memory_limit
39
+ while ($offset < $info['avdataend']) {
40
+ if (!getid3_lib::intValueSupported($offset)) {
41
+ $info['error'][] = 'Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions';
42
+ break;
43
+ }
44
+ $this->fseek($offset);
45
+ $AtomHeader = $this->fread(8);
46
+
47
+ $atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4));
48
+ $atomname = substr($AtomHeader, 4, 4);
49
+
50
+ // 64-bit MOV patch by jlegateØktnc*com
51
+ if ($atomsize == 1) {
52
+ $atomsize = getid3_lib::BigEndian2Int($this->fread(8));
53
+ }
54
+
55
+ $info['quicktime'][$atomname]['name'] = $atomname;
56
+ $info['quicktime'][$atomname]['size'] = $atomsize;
57
+ $info['quicktime'][$atomname]['offset'] = $offset;
58
+
59
+ if (($offset + $atomsize) > $info['avdataend']) {
60
+ $info['error'][] = 'Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)';
61
+ return false;
62
+ }
63
+
64
+ if ($atomsize == 0) {
65
+ // Furthermore, for historical reasons the list of atoms is optionally
66
+ // terminated by a 32-bit integer set to 0. If you are writing a program
67
+ // to read user data atoms, you should allow for the terminating 0.
68
+ break;
69
+ }
70
+ $atomHierarchy = array();
71
+ $info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, $atom_data_read_buffer_size)), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
72
+
73
+ $offset += $atomsize;
74
+ $atomcounter++;
75
+ }
76
+
77
+ if (!empty($info['avdataend_tmp'])) {
78
+ // this value is assigned to a temp value and then erased because
79
+ // otherwise any atoms beyond the 'mdat' atom would not get parsed
80
+ $info['avdataend'] = $info['avdataend_tmp'];
81
+ unset($info['avdataend_tmp']);
82
+ }
83
+
84
+ if (!empty($info['quicktime']['comments']['chapters']) && is_array($info['quicktime']['comments']['chapters']) && (count($info['quicktime']['comments']['chapters']) > 0)) {
85
+ $durations = $this->quicktime_time_to_sample_table($info);
86
+ for ($i = 0; $i < count($info['quicktime']['comments']['chapters']); $i++) {
87
+ $bookmark = array();
88
+ $bookmark['title'] = $info['quicktime']['comments']['chapters'][$i];
89
+ if (isset($durations[$i])) {
90
+ $bookmark['duration_sample'] = $durations[$i]['sample_duration'];
91
+ if ($i > 0) {
92
+ $bookmark['start_sample'] = $info['quicktime']['bookmarks'][($i - 1)]['start_sample'] + $info['quicktime']['bookmarks'][($i - 1)]['duration_sample'];
93
+ } else {
94
+ $bookmark['start_sample'] = 0;
95
+ }
96
+ if ($time_scale = $this->quicktime_bookmark_time_scale($info)) {
97
+ $bookmark['duration_seconds'] = $bookmark['duration_sample'] / $time_scale;
98
+ $bookmark['start_seconds'] = $bookmark['start_sample'] / $time_scale;
99
+ }
100
+ }
101
+ $info['quicktime']['bookmarks'][] = $bookmark;
102
+ }
103
+ }
104
+
105
+ if (isset($info['quicktime']['temp_meta_key_names'])) {
106
+ unset($info['quicktime']['temp_meta_key_names']);
107
+ }
108
+
109
+ if (!empty($info['quicktime']['comments']['location.ISO6709'])) {
110
+ // https://en.wikipedia.org/wiki/ISO_6709
111
+ foreach ($info['quicktime']['comments']['location.ISO6709'] as $ISO6709string) {
112
+ $latitude = false;
113
+ $longitude = false;
114
+ $altitude = false;
115
+ 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)) {
116
+ @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;
117
+
118
+ if (strlen($lat_deg) == 2) { // [+-]DD.D
119
+ $latitude = floatval(ltrim($lat_deg, '0').$lat_deg_dec);
120
+ } elseif (strlen($lat_deg) == 4) { // [+-]DDMM.M
121
+ $latitude = floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0').$lat_deg_dec / 60);
122
+ } elseif (strlen($lat_deg) == 6) { // [+-]DDMMSS.S
123
+ $latitude = 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);
124
+ }
125
+
126
+ if (strlen($lon_deg) == 3) { // [+-]DDD.D
127
+ $longitude = floatval(ltrim($lon_deg, '0').$lon_deg_dec);
128
+ } elseif (strlen($lon_deg) == 5) { // [+-]DDDMM.M
129
+ $longitude = floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0').$lon_deg_dec / 60);
130
+ } elseif (strlen($lon_deg) == 7) { // [+-]DDDMMSS.S
131
+ $longitude = 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);
132
+ }
133
+
134
+ if (strlen($alt_deg) == 3) { // [+-]DDD.D
135
+ $altitude = floatval(ltrim($alt_deg, '0').$alt_deg_dec);
136
+ } elseif (strlen($alt_deg) == 5) { // [+-]DDDMM.M
137
+ $altitude = floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0').$alt_deg_dec / 60);
138
+ } elseif (strlen($alt_deg) == 7) { // [+-]DDDMMSS.S
139
+ $altitude = 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);
140
+ }
141
+
142
+ if ($latitude !== false) {
143
+ $info['quicktime']['comments']['gps_latitude'][] = (($lat_sign == '-') ? -1 : 1) * floatval($latitude);
144
+ }
145
+ if ($longitude !== false) {
146
+ $info['quicktime']['comments']['gps_longitude'][] = (($lon_sign == '-') ? -1 : 1) * floatval($longitude);
147
+ }
148
+ if ($altitude !== false) {
149
+ $info['quicktime']['comments']['gps_altitude'][] = (($alt_sign == '-') ? -1 : 1) * floatval($altitude);
150
+ }
151
+ }
152
+ if ($latitude === false) {
153
+ $info['warning'][] = 'location.ISO6709 string not parsed correctly: "'.$ISO6709string.'", please submit as a bug';
154
+ }
155
+ break;
156
+ }
157
+ }
158
+
159
+ if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) {
160
+ $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
161
+ }
162
+ if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info['quicktime']['video'])) {
163
+ $info['audio']['bitrate'] = $info['bitrate'];
164
+ }
165
+ if (!empty($info['playtime_seconds']) && !isset($info['video']['frame_rate']) && !empty($info['quicktime']['stts_framecount'])) {
166
+ foreach ($info['quicktime']['stts_framecount'] as $key => $samples_count) {
167
+ $samples_per_second = $samples_count / $info['playtime_seconds'];
168
+ if ($samples_per_second > 240) {
169
+ // has to be audio samples
170
+ } else {
171
+ $info['video']['frame_rate'] = $samples_per_second;
172
+ break;
173
+ }
174
+ }
175
+ }
176
+ if (($info['audio']['dataformat'] == 'mp4') && empty($info['video']['resolution_x'])) {
177
+ $info['fileformat'] = 'mp4';
178
+ $info['mime_type'] = 'audio/mp4';
179
+ unset($info['video']['dataformat']);
180
+ }
181
+
182
+ if (!$this->ReturnAtomData) {
183
+ unset($info['quicktime']['moov']);
184
+ }
185
+
186
+ if (empty($info['audio']['dataformat']) && !empty($info['quicktime']['audio'])) {
187
+ $info['audio']['dataformat'] = 'quicktime';
188
+ }
189
+ if (empty($info['video']['dataformat']) && !empty($info['quicktime']['video'])) {
190
+ $info['video']['dataformat'] = 'quicktime';
191
+ }
192
+
193
+ return true;
194
+ }
195
+
196
+ public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
197
+ // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm
198
+ // https://code.google.com/p/mp4v2/wiki/iTunesMetadata
199
+
200
+ $info = &$this->getid3->info;
201
+
202
+ $atom_parent = end($atomHierarchy); // not array_pop($atomHierarchy); see http://www.getid3.org/phpBB3/viewtopic.php?t=1717
203
+ array_push($atomHierarchy, $atomname);
204
+ $atom_structure['hierarchy'] = implode(' ', $atomHierarchy);
205
+ $atom_structure['name'] = $atomname;
206
+ $atom_structure['size'] = $atomsize;
207
+ $atom_structure['offset'] = $baseoffset;
208
+ switch ($atomname) {
209
+ case 'moov': // MOVie container atom
210
+ case 'trak': // TRAcK container atom
211
+ case 'clip': // CLIPping container atom
212
+ case 'matt': // track MATTe container atom
213
+ case 'edts': // EDiTS container atom
214
+ case 'tref': // Track REFerence container atom
215
+ case 'mdia': // MeDIA container atom
216
+ case 'minf': // Media INFormation container atom
217
+ case 'dinf': // Data INFormation container atom
218
+ case 'udta': // User DaTA container atom
219
+ case 'cmov': // Compressed MOVie container atom
220
+ case 'rmra': // Reference Movie Record Atom
221
+ case 'rmda': // Reference Movie Descriptor Atom
222
+ case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR)
223
+ $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
224
+ break;
225
+
226
+ case 'ilst': // Item LiST container atom
227
+ if ($atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms)) {
228
+ // some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted
229
+ $allnumericnames = true;
230
+ foreach ($atom_structure['subatoms'] as $subatomarray) {
231
+ if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) {
232
+ $allnumericnames = false;
233
+ break;
234
+ }
235
+ }
236
+ if ($allnumericnames) {
237
+ $newData = array();
238
+ foreach ($atom_structure['subatoms'] as $subatomarray) {
239
+ foreach ($subatomarray['subatoms'] as $newData_subatomarray) {
240
+ unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']);
241
+ $newData[$subatomarray['name']] = $newData_subatomarray;
242
+ break;
243
+ }
244
+ }
245
+ $atom_structure['data'] = $newData;
246
+ unset($atom_structure['subatoms']);
247
+ }
248
+ }
249
+ break;
250
+
251
+ case "\x00\x00\x00\x01":
252
+ case "\x00\x00\x00\x02":
253
+ case "\x00\x00\x00\x03":
254
+ case "\x00\x00\x00\x04":
255
+ case "\x00\x00\x00\x05":
256
+ $atomname = getid3_lib::BigEndian2Int($atomname);
257
+ $atom_structure['name'] = $atomname;
258
+ $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
259
+ break;
260
+
261
+ case 'stbl': // Sample TaBLe container atom
262
+ $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
263
+ $isVideo = false;
264
+ $framerate = 0;
265
+ $framecount = 0;
266
+ foreach ($atom_structure['subatoms'] as $key => $value_array) {
267
+ if (isset($value_array['sample_description_table'])) {
268
+ foreach ($value_array['sample_description_table'] as $key2 => $value_array2) {
269
+ if (isset($value_array2['data_format'])) {
270
+ switch ($value_array2['data_format']) {
271
+ case 'avc1':
272
+ case 'mp4v':
273
+ // video data
274
+ $isVideo = true;
275
+ break;
276
+ case 'mp4a':
277
+ // audio data
278
+ break;
279
+ }
280
+ }
281
+ }
282
+ } elseif (isset($value_array['time_to_sample_table'])) {
283
+ foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
284
+ if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) {
285
+ $framerate = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3);
286
+ $framecount = $value_array2['sample_count'];
287
+ }
288
+ }
289
+ }
290
+ }
291
+ if ($isVideo && $framerate) {
292
+ $info['quicktime']['video']['frame_rate'] = $framerate;
293
+ $info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate'];
294
+ }
295
+ if ($isVideo && $framecount) {
296
+ $info['quicktime']['video']['frame_count'] = $framecount;
297
+ }
298
+ break;
299
+
300
+
301
+ case "\xA9".'alb': // ALBum
302
+ case "\xA9".'ART': //
303
+ case "\xA9".'art': // ARTist
304
+ case "\xA9".'aut': //
305
+ case "\xA9".'cmt': // CoMmenT
306
+ case "\xA9".'com': // COMposer
307
+ case "\xA9".'cpy': //
308
+ case "\xA9".'day': // content created year
309
+ case "\xA9".'dir': //
310
+ case "\xA9".'ed1': //
311
+ case "\xA9".'ed2': //
312
+ case "\xA9".'ed3': //
313
+ case "\xA9".'ed4': //
314
+ case "\xA9".'ed5': //
315
+ case "\xA9".'ed6': //
316
+ case "\xA9".'ed7': //
317
+ case "\xA9".'ed8': //
318
+ case "\xA9".'ed9': //
319
+ case "\xA9".'enc': //
320
+ case "\xA9".'fmt': //
321
+ case "\xA9".'gen': // GENre
322
+ case "\xA9".'grp': // GRouPing
323
+ case "\xA9".'hst': //
324
+ case "\xA9".'inf': //
325
+ case "\xA9".'lyr': // LYRics
326
+ case "\xA9".'mak': //
327
+ case "\xA9".'mod': //
328
+ case "\xA9".'nam': // full NAMe
329
+ case "\xA9".'ope': //
330
+ case "\xA9".'PRD': //
331
+ case "\xA9".'prf': //
332
+ case "\xA9".'req': //
333
+ case "\xA9".'src': //
334
+ case "\xA9".'swr': //
335
+ case "\xA9".'too': // encoder
336
+ case "\xA9".'trk': // TRacK
337
+ case "\xA9".'url': //
338
+ case "\xA9".'wrn': //
339
+ case "\xA9".'wrt': // WRiTer
340
+ case '----': // itunes specific
341
+ case 'aART': // Album ARTist
342
+ case 'akID': // iTunes store account type
343
+ case 'apID': // Purchase Account
344
+ case 'atID': //
345
+ case 'catg': // CaTeGory
346
+ case 'cmID': //
347
+ case 'cnID': //
348
+ case 'covr': // COVeR artwork
349
+ case 'cpil': // ComPILation
350
+ case 'cprt': // CoPyRighT
351
+ case 'desc': // DESCription
352
+ case 'disk': // DISK number
353
+ case 'egid': // Episode Global ID
354
+ case 'geID': //
355
+ case 'gnre': // GeNRE
356
+ case 'hdvd': // HD ViDeo
357
+ case 'keyw': // KEYWord
358
+ case 'ldes': // Long DEScription
359
+ case 'pcst': // PodCaST
360
+ case 'pgap': // GAPless Playback
361
+ case 'plID': //
362
+ case 'purd': // PURchase Date
363
+ case 'purl': // Podcast URL
364
+ case 'rati': //
365
+ case 'rndu': //
366
+ case 'rpdu': //
367
+ case 'rtng': // RaTiNG
368
+ case 'sfID': // iTunes store country
369
+ case 'soaa': // SOrt Album Artist
370
+ case 'soal': // SOrt ALbum
371
+ case 'soar': // SOrt ARtist
372
+ case 'soco': // SOrt COmposer
373
+ case 'sonm': // SOrt NaMe
374
+ case 'sosn': // SOrt Show Name
375
+ case 'stik': //
376
+ case 'tmpo': // TeMPO (BPM)
377
+ case 'trkn': // TRacK Number
378
+ case 'tven': // tvEpisodeID
379
+ case 'tves': // TV EpiSode
380
+ case 'tvnn': // TV Network Name
381
+ case 'tvsh': // TV SHow Name
382
+ case 'tvsn': // TV SeasoN
383
+ if ($atom_parent == 'udta') {
384
+ // User data atom handler
385
+ $atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
386
+ $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
387
+ $atom_structure['data'] = substr($atom_data, 4);
388
+
389
+ $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
390
+ if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
391
+ $info['comments']['language'][] = $atom_structure['language'];
392
+ }
393
+ } else {
394
+ // Apple item list box atom handler
395
+ $atomoffset = 0;
396
+ if (substr($atom_data, 2, 2) == "\x10\xB5") {
397
+ // not sure what it means, but observed on iPhone4 data.
398
+ // Each $atom_data has 2 bytes of datasize, plus 0x10B5, then data
399
+ while ($atomoffset < strlen($atom_data)) {
400
+ $boxsmallsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 2));
401
+ $boxsmalltype = substr($atom_data, $atomoffset + 2, 2);
402
+ $boxsmalldata = substr($atom_data, $atomoffset + 4, $boxsmallsize);
403
+ if ($boxsmallsize <= 1) {
404
+ $info['warning'][] = 'Invalid QuickTime atom smallbox size "'.$boxsmallsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset);
405
+ $atom_structure['data'] = null;
406
+ $atomoffset = strlen($atom_data);
407
+ break;
408
+ }
409
+ switch ($boxsmalltype) {
410
+ case "\x10\xB5":
411
+ $atom_structure['data'] = $boxsmalldata;
412
+ break;
413
+ default:
414
+ $info['warning'][] = 'Unknown QuickTime smallbox type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxsmalltype).'" ('.trim(getid3_lib::PrintHexBytes($boxsmalltype)).') at offset '.$baseoffset;
415
+ $atom_structure['data'] = $atom_data;
416
+ break;
417
+ }
418
+ $atomoffset += (4 + $boxsmallsize);
419
+ }
420
+ } else {
421
+ while ($atomoffset < strlen($atom_data)) {
422
+ $boxsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 4));
423
+ $boxtype = substr($atom_data, $atomoffset + 4, 4);
424
+ $boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8);
425
+ if ($boxsize <= 1) {
426
+ $info['warning'][] = 'Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset);
427
+ $atom_structure['data'] = null;
428
+ $atomoffset = strlen($atom_data);
429
+ break;
430
+ }
431
+ $atomoffset += $boxsize;
432
+
433
+ switch ($boxtype) {
434
+ case 'mean':
435
+ case 'name':
436
+ $atom_structure[$boxtype] = substr($boxdata, 4);
437
+ break;
438
+
439
+ case 'data':
440
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($boxdata, 0, 1));
441
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata, 1, 3));
442
+ switch ($atom_structure['flags_raw']) {
443
+ case 0: // data flag
444
+ case 21: // tmpo/cpil flag
445
+ switch ($atomname) {
446
+ case 'cpil':
447
+ case 'hdvd':
448
+ case 'pcst':
449
+ case 'pgap':
450
+ // 8-bit integer (boolean)
451
+ $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
452
+ break;
453
+
454
+ case 'tmpo':
455
+ // 16-bit integer
456
+ $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2));
457
+ break;
458
+
459
+ case 'disk':
460
+ case 'trkn':
461
+ // binary
462
+ $num = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2));
463
+ $num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2));
464
+ $atom_structure['data'] = empty($num) ? '' : $num;
465
+ $atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total;
466
+ break;
467
+
468
+ case 'gnre':
469
+ // enum
470
+ $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
471
+ $atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1);
472
+ break;
473
+
474
+ case 'rtng':
475
+ // 8-bit integer
476
+ $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
477
+ $atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]);
478
+ break;
479
+
480
+ case 'stik':
481
+ // 8-bit integer (enum)
482
+ $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
483
+ $atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]);
484
+ break;
485
+
486
+ case 'sfID':
487
+ // 32-bit integer
488
+ $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
489
+ $atom_structure['data'] = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]);
490
+ break;
491
+
492
+ case 'egid':
493
+ case 'purl':
494
+ $atom_structure['data'] = substr($boxdata, 8);
495
+ break;
496
+
497
+ case 'plID':
498
+ // 64-bit integer
499
+ $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 8));
500
+ break;
501
+
502
+ case 'covr':
503
+ $atom_structure['data'] = substr($boxdata, 8);
504
+ // not a foolproof check, but better than nothing
505
+ if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) {
506
+ $atom_structure['image_mime'] = 'image/jpeg';
507
+ } elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) {
508
+ $atom_structure['image_mime'] = 'image/png';
509
+ } elseif (preg_match('#^GIF#', $atom_structure['data'])) {
510
+ $atom_structure['image_mime'] = 'image/gif';
511
+ }
512
+ break;
513
+
514
+ case 'atID':
515
+ case 'cnID':
516
+ case 'geID':
517
+ case 'tves':
518
+ case 'tvsn':
519
+ default:
520
+ // 32-bit integer
521
+ $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
522
+ }
523
+ break;
524
+
525
+ case 1: // text flag
526
+ case 13: // image flag
527
+ default:
528
+ $atom_structure['data'] = substr($boxdata, 8);
529
+ if ($atomname == 'covr') {
530
+ // not a foolproof check, but better than nothing
531
+ if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) {
532
+ $atom_structure['image_mime'] = 'image/jpeg';
533
+ } elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) {
534
+ $atom_structure['image_mime'] = 'image/png';
535
+ } elseif (preg_match('#^GIF#', $atom_structure['data'])) {
536
+ $atom_structure['image_mime'] = 'image/gif';
537
+ }
538
+ }
539
+ break;
540
+
541
+ }
542
+ break;
543
+
544
+ default:
545
+ $info['warning'][] = 'Unknown QuickTime box type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxtype).'" ('.trim(getid3_lib::PrintHexBytes($boxtype)).') at offset '.$baseoffset;
546
+ $atom_structure['data'] = $atom_data;
547
+
548
+ }
549
+ }
550
+ }
551
+ }
552
+ $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']);
553
+ break;
554
+
555
+
556
+ case 'play': // auto-PLAY atom
557
+ $atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
558
+
559
+ $info['quicktime']['autoplay'] = $atom_structure['autoplay'];
560
+ break;
561
+
562
+
563
+ case 'WLOC': // Window LOCation atom
564
+ $atom_structure['location_x'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
565
+ $atom_structure['location_y'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
566
+ break;
567
+
568
+
569
+ case 'LOOP': // LOOPing atom
570
+ case 'SelO': // play SELection Only atom
571
+ case 'AllF': // play ALL Frames atom
572
+ $atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data);
573
+ break;
574
+
575
+
576
+ case 'name': //
577
+ case 'MCPS': // Media Cleaner PRo
578
+ case '@PRM': // adobe PReMiere version
579
+ case '@PRQ': // adobe PRemiere Quicktime version
580
+ $atom_structure['data'] = $atom_data;
581
+ break;
582
+
583
+
584
+ case 'cmvd': // Compressed MooV Data atom
585
+ // Code by ubergeekØubergeek*tv based on information from
586
+ // http://developer.apple.com/quicktime/icefloe/dispatch012.html
587
+ $atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
588
+
589
+ $CompressedFileData = substr($atom_data, 4);
590
+ if ($UncompressedHeader = @gzuncompress($CompressedFileData)) {
591
+ $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms);
592
+ } else {
593
+ $info['warning'][] = 'Error decompressing compressed MOV atom at offset '.$atom_structure['offset'];
594
+ }
595
+ break;
596
+
597
+
598
+ case 'dcom': // Data COMpression atom
599
+ $atom_structure['compression_id'] = $atom_data;
600
+ $atom_structure['compression_text'] = $this->QuicktimeDCOMLookup($atom_data);
601
+ break;
602
+
603
+
604
+ case 'rdrf': // Reference movie Data ReFerence atom
605
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
606
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
607
+ $atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x000001);
608
+
609
+ $atom_structure['reference_type_name'] = substr($atom_data, 4, 4);
610
+ $atom_structure['reference_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
611
+ switch ($atom_structure['reference_type_name']) {
612
+ case 'url ':
613
+ $atom_structure['url'] = $this->NoNullString(substr($atom_data, 12));
614
+ break;
615
+
616
+ case 'alis':
617
+ $atom_structure['file_alias'] = substr($atom_data, 12);
618
+ break;
619
+
620
+ case 'rsrc':
621
+ $atom_structure['resource_alias'] = substr($atom_data, 12);
622
+ break;
623
+
624
+ default:
625
+ $atom_structure['data'] = substr($atom_data, 12);
626
+ break;
627
+ }
628
+ break;
629
+
630
+
631
+ case 'rmqu': // Reference Movie QUality atom
632
+ $atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data);
633
+ break;
634
+
635
+
636
+ case 'rmcs': // Reference Movie Cpu Speed atom
637
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
638
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
639
+ $atom_structure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
640
+ break;
641
+
642
+
643
+ case 'rmvc': // Reference Movie Version Check atom
644
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
645
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
646
+ $atom_structure['gestalt_selector'] = substr($atom_data, 4, 4);
647
+ $atom_structure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
648
+ $atom_structure['gestalt_value'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
649
+ $atom_structure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
650
+ break;
651
+
652
+
653
+ case 'rmcd': // Reference Movie Component check atom
654
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
655
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
656
+ $atom_structure['component_type'] = substr($atom_data, 4, 4);
657
+ $atom_structure['component_subtype'] = substr($atom_data, 8, 4);
658
+ $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
659
+ $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
660
+ $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
661
+ $atom_structure['component_min_version'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 4));
662
+ break;
663
+
664
+
665
+ case 'rmdr': // Reference Movie Data Rate atom
666
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
667
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
668
+ $atom_structure['data_rate'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
669
+
670
+ $atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10;
671
+ break;
672
+
673
+
674
+ case 'rmla': // Reference Movie Language Atom
675
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
676
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
677
+ $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
678
+
679
+ $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
680
+ if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
681
+ $info['comments']['language'][] = $atom_structure['language'];
682
+ }
683
+ break;
684
+
685
+
686
+ case 'rmla': // Reference Movie Language Atom
687
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
688
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
689
+ $atom_structure['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
690
+ break;
691
+
692
+
693
+ case 'ptv ': // Print To Video - defines a movie's full screen mode
694
+ // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm
695
+ $atom_structure['display_size_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
696
+ $atom_structure['reserved_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); // hardcoded: 0x0000
697
+ $atom_structure['reserved_2'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x0000
698
+ $atom_structure['slide_show_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 1));
699
+ $atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1));
700
+
701
+ $atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag'];
702
+ $atom_structure['flags']['slide_show'] = (bool) $atom_structure['slide_show_flag'];
703
+
704
+ $ptv_lookup[0] = 'normal';
705
+ $ptv_lookup[1] = 'double';
706
+ $ptv_lookup[2] = 'half';
707
+ $ptv_lookup[3] = 'full';
708
+ $ptv_lookup[4] = 'current';
709
+ if (isset($ptv_lookup[$atom_structure['display_size_raw']])) {
710
+ $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
711
+ } else {
712
+ $info['warning'][] = 'unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')';
713
+ }
714
+ break;
715
+
716
+
717
+ case 'stsd': // Sample Table Sample Description atom
718
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
719
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
720
+ $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
721
+ $stsdEntriesDataOffset = 8;
722
+ for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
723
+ $atom_structure['sample_description_table'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4));
724
+ $stsdEntriesDataOffset += 4;
725
+ $atom_structure['sample_description_table'][$i]['data_format'] = substr($atom_data, $stsdEntriesDataOffset, 4);
726
+ $stsdEntriesDataOffset += 4;
727
+ $atom_structure['sample_description_table'][$i]['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6));
728
+ $stsdEntriesDataOffset += 6;
729
+ $atom_structure['sample_description_table'][$i]['reference_index'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2));
730
+ $stsdEntriesDataOffset += 2;
731
+ $atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
732
+ $stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
733
+
734
+ $atom_structure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 0, 2));
735
+ $atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 2, 2));
736
+ $atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4);
737
+
738
+ switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) {
739
+
740
+ case "\x00\x00\x00\x00":
741
+ // audio tracks
742
+ $atom_structure['sample_description_table'][$i]['audio_channels'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 2));
743
+ $atom_structure['sample_description_table'][$i]['audio_bit_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 10, 2));
744
+ $atom_structure['sample_description_table'][$i]['audio_compression_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 2));
745
+ $atom_structure['sample_description_table'][$i]['audio_packet_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 14, 2));
746
+ $atom_structure['sample_description_table'][$i]['audio_sample_rate'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16, 4));
747
+
748
+ // video tracks
749
+ // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
750
+ $atom_structure['sample_description_table'][$i]['temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4));
751
+ $atom_structure['sample_description_table'][$i]['spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4));
752
+ $atom_structure['sample_description_table'][$i]['width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2));
753
+ $atom_structure['sample_description_table'][$i]['height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2));
754
+ $atom_structure['sample_description_table'][$i]['resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4));
755
+ $atom_structure['sample_description_table'][$i]['resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4));
756
+ $atom_structure['sample_description_table'][$i]['data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 4));
757
+ $atom_structure['sample_description_table'][$i]['frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 36, 2));
758
+ $atom_structure['sample_description_table'][$i]['compressor_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 38, 4);
759
+ $atom_structure['sample_description_table'][$i]['pixel_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 42, 2));
760
+ $atom_structure['sample_description_table'][$i]['color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 44, 2));
761
+
762
+ switch ($atom_structure['sample_description_table'][$i]['data_format']) {
763
+ case '2vuY':
764
+ case 'avc1':
765
+ case 'cvid':
766
+ case 'dvc ':
767
+ case 'dvcp':
768
+ case 'gif ':
769
+ case 'h263':
770
+ case 'jpeg':
771
+ case 'kpcd':
772
+ case 'mjpa':
773
+ case 'mjpb':
774
+ case 'mp4v':
775
+ case 'png ':
776
+ case 'raw ':
777
+ case 'rle ':
778
+ case 'rpza':
779
+ case 'smc ':
780
+ case 'SVQ1':
781
+ case 'SVQ3':
782
+ case 'tiff':
783
+ case 'v210':
784
+ case 'v216':
785
+ case 'v308':
786
+ case 'v408':
787
+ case 'v410':
788
+ case 'yuv2':
789
+ $info['fileformat'] = 'mp4';
790
+ $info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
791
+ // http://www.getid3.org/phpBB3/viewtopic.php?t=1550
792
+ //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
793
+ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['height'])) {
794
+ // assume that values stored here are more important than values stored in [tkhd] atom
795
+ $info['video']['resolution_x'] = $atom_structure['sample_description_table'][$i]['width'];
796
+ $info['video']['resolution_y'] = $atom_structure['sample_description_table'][$i]['height'];
797
+ $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
798
+ $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
799
+ }
800
+ break;
801
+
802
+ case 'qtvr':
803
+ $info['video']['dataformat'] = 'quicktimevr';
804
+ break;
805
+
806
+ case 'mp4a':
807
+ default:
808
+ $info['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
809
+ $info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate'];
810
+ $info['quicktime']['audio']['channels'] = $atom_structure['sample_description_table'][$i]['audio_channels'];
811
+ $info['quicktime']['audio']['bit_depth'] = $atom_structure['sample_description_table'][$i]['audio_bit_depth'];
812
+ $info['audio']['codec'] = $info['quicktime']['audio']['codec'];
813
+ $info['audio']['sample_rate'] = $info['quicktime']['audio']['sample_rate'];
814
+ $info['audio']['channels'] = $info['quicktime']['audio']['channels'];
815
+ $info['audio']['bits_per_sample'] = $info['quicktime']['audio']['bit_depth'];
816
+ switch ($atom_structure['sample_description_table'][$i]['data_format']) {
817
+ case 'raw ': // PCM
818
+ case 'alac': // Apple Lossless Audio Codec
819
+ $info['audio']['lossless'] = true;
820
+ break;
821
+ default:
822
+ $info['audio']['lossless'] = false;
823
+ break;
824
+ }
825
+ break;
826
+ }
827
+ break;
828
+
829
+ default:
830
+ switch ($atom_structure['sample_description_table'][$i]['data_format']) {
831
+ case 'mp4s':
832
+ $info['fileformat'] = 'mp4';
833
+ break;
834
+
835
+ default:
836
+ // video atom
837
+ $atom_structure['sample_description_table'][$i]['video_temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4));
838
+ $atom_structure['sample_description_table'][$i]['video_spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4));
839
+ $atom_structure['sample_description_table'][$i]['video_frame_width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2));
840
+ $atom_structure['sample_description_table'][$i]['video_frame_height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2));
841
+ $atom_structure['sample_description_table'][$i]['video_resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20, 4));
842
+ $atom_structure['sample_description_table'][$i]['video_resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4));
843
+ $atom_structure['sample_description_table'][$i]['video_data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4));
844
+ $atom_structure['sample_description_table'][$i]['video_frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 2));
845
+ $atom_structure['sample_description_table'][$i]['video_encoder_name_len'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34, 1));
846
+ $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']);
847
+ $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66, 2));
848
+ $atom_structure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68, 2));
849
+
850
+ $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color');
851
+ $atom_structure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']);
852
+
853
+ if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') {
854
+ $info['quicktime']['video']['codec_fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
855
+ $info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
856
+ $info['quicktime']['video']['codec'] = (($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']);
857
+ $info['quicktime']['video']['color_depth'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'];
858
+ $info['quicktime']['video']['color_depth_name'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_name'];
859
+
860
+ $info['video']['codec'] = $info['quicktime']['video']['codec'];
861
+ $info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth'];
862
+ }
863
+ $info['video']['lossless'] = false;
864
+ $info['video']['pixel_aspect_ratio'] = (float) 1;
865
+ break;
866
+ }
867
+ break;
868
+ }
869
+ switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) {
870
+ case 'mp4a':
871
+ $info['audio']['dataformat'] = 'mp4';
872
+ $info['quicktime']['audio']['codec'] = 'mp4';
873
+ break;
874
+
875
+ case '3ivx':
876
+ case '3iv1':
877
+ case '3iv2':
878
+ $info['video']['dataformat'] = '3ivx';
879
+ break;
880
+
881
+ case 'xvid':
882
+ $info['video']['dataformat'] = 'xvid';
883
+ break;
884
+
885
+ case 'mp4v':
886
+ $info['video']['dataformat'] = 'mpeg4';
887
+ break;
888
+
889
+ case 'divx':
890
+ case 'div1':
891
+ case 'div2':
892
+ case 'div3':
893
+ case 'div4':
894
+ case 'div5':
895
+ case 'div6':
896
+ $info['video']['dataformat'] = 'divx';
897
+ break;
898
+
899
+ default:
900
+ // do nothing
901
+ break;
902
+ }
903
+ unset($atom_structure['sample_description_table'][$i]['data']);
904
+ }
905
+ break;
906
+
907
+
908
+ case 'stts': // Sample Table Time-to-Sample atom
909
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
910
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
911
+ $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
912
+ $sttsEntriesDataOffset = 8;
913
+ //$FrameRateCalculatorArray = array();
914
+ $frames_count = 0;
915
+
916
+ $max_stts_entries_to_scan = ($info['php_memory_limit'] ? min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']) : $atom_structure['number_entries']);
917
+ if ($max_stts_entries_to_scan < $atom_structure['number_entries']) {
918
+ $info['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($atom_structure['number_entries'] / 1048576).'MB).';
919
+ }
920
+ for ($i = 0; $i < $max_stts_entries_to_scan; $i++) {
921
+ $atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
922
+ $sttsEntriesDataOffset += 4;
923
+ $atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
924
+ $sttsEntriesDataOffset += 4;
925
+
926
+ $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count'];
927
+
928
+ // THIS SECTION REPLACED WITH CODE IN "stbl" ATOM
929
+ //if (!empty($info['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) {
930
+ // $stts_new_framerate = $info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'];
931
+ // if ($stts_new_framerate <= 60) {
932
+ // // some atoms have durations of "1" giving a very large framerate, which probably is not right
933
+ // $info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate);
934
+ // }
935
+ //}
936
+ //
937
+ //$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count'];
938
+ }
939
+ $info['quicktime']['stts_framecount'][] = $frames_count;
940
+ //$sttsFramesTotal = 0;
941
+ //$sttsSecondsTotal = 0;
942
+ //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) {
943
+ // if (($frames_per_second > 60) || ($frames_per_second < 1)) {
944
+ // // not video FPS information, probably audio information
945
+ // $sttsFramesTotal = 0;
946
+ // $sttsSecondsTotal = 0;
947
+ // break;
948
+ // }
949
+ // $sttsFramesTotal += $frame_count;
950
+ // $sttsSecondsTotal += $frame_count / $frames_per_second;
951
+ //}
952
+ //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) {
953
+ // if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) {
954
+ // $info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal;
955
+ // }
956
+ //}
957
+ break;
958
+
959
+
960
+ case 'stss': // Sample Table Sync Sample (key frames) atom
961
+ if ($ParseAllPossibleAtoms) {
962
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
963
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
964
+ $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
965
+ $stssEntriesDataOffset = 8;
966
+ for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
967
+ $atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4));
968
+ $stssEntriesDataOffset += 4;
969
+ }
970
+ }
971
+ break;
972
+
973
+
974
+ case 'stsc': // Sample Table Sample-to-Chunk atom
975
+ if ($ParseAllPossibleAtoms) {
976
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
977
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
978
+ $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
979
+ $stscEntriesDataOffset = 8;
980
+ for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
981
+ $atom_structure['sample_to_chunk_table'][$i]['first_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
982
+ $stscEntriesDataOffset += 4;
983
+ $atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
984
+ $stscEntriesDataOffset += 4;
985
+ $atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
986
+ $stscEntriesDataOffset += 4;
987
+ }
988
+ }
989
+ break;
990
+
991
+
992
+ case 'stsz': // Sample Table SiZe atom
993
+ if ($ParseAllPossibleAtoms) {
994
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
995
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
996
+ $atom_structure['sample_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
997
+ $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
998
+ $stszEntriesDataOffset = 12;
999
+ if ($atom_structure['sample_size'] == 0) {
1000
+ for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
1001
+ $atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4));
1002
+ $stszEntriesDataOffset += 4;
1003
+ }
1004
+ }
1005
+ }
1006
+ break;
1007
+
1008
+
1009
+ case 'stco': // Sample Table Chunk Offset atom
1010
+ if ($ParseAllPossibleAtoms) {
1011
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1012
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1013
+ $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1014
+ $stcoEntriesDataOffset = 8;
1015
+ for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
1016
+ $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4));
1017
+ $stcoEntriesDataOffset += 4;
1018
+ }
1019
+ }
1020
+ break;
1021
+
1022
+
1023
+ case 'co64': // Chunk Offset 64-bit (version of "stco" that supports > 2GB files)
1024
+ if ($ParseAllPossibleAtoms) {
1025
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1026
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1027
+ $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1028
+ $stcoEntriesDataOffset = 8;
1029
+ for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
1030
+ $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8));
1031
+ $stcoEntriesDataOffset += 8;
1032
+ }
1033
+ }
1034
+ break;
1035
+
1036
+
1037
+ case 'dref': // Data REFerence atom
1038
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1039
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1040
+ $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1041
+ $drefDataOffset = 8;
1042
+ for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
1043
+ $atom_structure['data_references'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4));
1044
+ $drefDataOffset += 4;
1045
+ $atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4);
1046
+ $drefDataOffset += 4;
1047
+ $atom_structure['data_references'][$i]['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 1));
1048
+ $drefDataOffset += 1;
1049
+ $atom_structure['data_references'][$i]['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 3)); // hardcoded: 0x0000
1050
+ $drefDataOffset += 3;
1051
+ $atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3));
1052
+ $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3);
1053
+
1054
+ $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001);
1055
+ }
1056
+ break;
1057
+
1058
+
1059
+ case 'gmin': // base Media INformation atom
1060
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1061
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1062
+ $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
1063
+ $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
1064
+ $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2));
1065
+ $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
1066
+ $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 2));
1067
+ $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
1068
+ break;
1069
+
1070
+
1071
+ case 'smhd': // Sound Media information HeaDer atom
1072
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1073
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1074
+ $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
1075
+ $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
1076
+ break;
1077
+
1078
+
1079
+ case 'vmhd': // Video Media information HeaDer atom
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));
1082
+ $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
1083
+ $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
1084
+ $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2));
1085
+ $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
1086
+
1087
+ $atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x001);
1088
+ break;
1089
+
1090
+
1091
+ case 'hdlr': // HanDLeR reference atom
1092
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1093
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1094
+ $atom_structure['component_type'] = substr($atom_data, 4, 4);
1095
+ $atom_structure['component_subtype'] = substr($atom_data, 8, 4);
1096
+ $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
1097
+ $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1098
+ $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
1099
+ $atom_structure['component_name'] = $this->Pascal2String(substr($atom_data, 24));
1100
+
1101
+ if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) {
1102
+ $info['video']['dataformat'] = 'quicktimevr';
1103
+ }
1104
+ break;
1105
+
1106
+
1107
+ case 'mdhd': // MeDia HeaDer atom
1108
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1109
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1110
+ $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1111
+ $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
1112
+ $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1113
+ $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1114
+ $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 2));
1115
+ $atom_structure['quality'] = getid3_lib::BigEndian2Int(substr($atom_data, 22, 2));
1116
+
1117
+ if ($atom_structure['time_scale'] == 0) {
1118
+ $info['error'][] = 'Corrupt Quicktime file: mdhd.time_scale == zero';
1119
+ return false;
1120
+ }
1121
+ $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']);
1122
+
1123
+ $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
1124
+ $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
1125
+ $atom_structure['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
1126
+ $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
1127
+ if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
1128
+ $info['comments']['language'][] = $atom_structure['language'];
1129
+ }
1130
+ break;
1131
+
1132
+
1133
+ case 'pnot': // Preview atom
1134
+ $atom_structure['modification_date'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // "standard Macintosh format"
1135
+ $atom_structure['version_number'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x00
1136
+ $atom_structure['atom_type'] = substr($atom_data, 6, 4); // usually: 'PICT'
1137
+ $atom_structure['atom_index'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01
1138
+
1139
+ $atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']);
1140
+ break;
1141
+
1142
+
1143
+ case 'crgn': // Clipping ReGioN atom
1144
+ $atom_structure['region_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); // The Region size, Region boundary box,
1145
+ $atom_structure['boundary_box'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 8)); // and Clipping region data fields
1146
+ $atom_structure['clipping_data'] = substr($atom_data, 10); // constitute a QuickDraw region.
1147
+ break;
1148
+
1149
+
1150
+ case 'load': // track LOAD settings atom
1151
+ $atom_structure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
1152
+ $atom_structure['preload_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1153
+ $atom_structure['preload_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
1154
+ $atom_structure['default_hints_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1155
+
1156
+ $atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x0020);
1157
+ $atom_structure['default_hints']['high_quality'] = (bool) ($atom_structure['default_hints_raw'] & 0x0100);
1158
+ break;
1159
+
1160
+
1161
+ case 'tmcd': // TiMe CoDe atom
1162
+ case 'chap': // CHAPter list atom
1163
+ case 'sync': // SYNChronization atom
1164
+ case 'scpt': // tranSCriPT atom
1165
+ case 'ssrc': // non-primary SouRCe atom
1166
+ for ($i = 0; $i < strlen($atom_data); $i += 4) {
1167
+ @$atom_structure['track_id'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
1168
+ }
1169
+ break;
1170
+
1171
+
1172
+ case 'elst': // Edit LiST atom
1173
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1174
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1175
+ $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1176
+ for ($i = 0; $i < $atom_structure['number_entries']; $i++ ) {
1177
+ $atom_structure['edit_list'][$i]['track_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4));
1178
+ $atom_structure['edit_list'][$i]['media_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4));
1179
+ $atom_structure['edit_list'][$i]['media_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4));
1180
+ }
1181
+ break;
1182
+
1183
+
1184
+ case 'kmat': // compressed MATte atom
1185
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1186
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1187
+ $atom_structure['matte_data_raw'] = substr($atom_data, 4);
1188
+ break;
1189
+
1190
+
1191
+ case 'ctab': // Color TABle atom
1192
+ $atom_structure['color_table_seed'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // hardcoded: 0x00000000
1193
+ $atom_structure['color_table_flags'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x8000
1194
+ $atom_structure['color_table_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)) + 1;
1195
+ for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) {
1196
+ $atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2));
1197
+ $atom_structure['color_table'][$colortableentry]['red'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2));
1198
+ $atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2));
1199
+ $atom_structure['color_table'][$colortableentry]['blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2));
1200
+ }
1201
+ break;
1202
+
1203
+
1204
+ case 'mvhd': // MoVie HeaDer atom
1205
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1206
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1207
+ $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1208
+ $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
1209
+ $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1210
+ $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1211
+ $atom_structure['preferred_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 20, 4));
1212
+ $atom_structure['preferred_volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 24, 2));
1213
+ $atom_structure['reserved'] = substr($atom_data, 26, 10);
1214
+ $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 36, 4));
1215
+ $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
1216
+ $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 44, 4));
1217
+ $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4));
1218
+ $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
1219
+ $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 56, 4));
1220
+ $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4));
1221
+ $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
1222
+ $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4));
1223
+ $atom_structure['preview_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 72, 4));
1224
+ $atom_structure['preview_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 76, 4));
1225
+ $atom_structure['poster_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 80, 4));
1226
+ $atom_structure['selection_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 84, 4));
1227
+ $atom_structure['selection_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 88, 4));
1228
+ $atom_structure['current_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 92, 4));
1229
+ $atom_structure['next_track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 96, 4));
1230
+
1231
+ if ($atom_structure['time_scale'] == 0) {
1232
+ $info['error'][] = 'Corrupt Quicktime file: mvhd.time_scale == zero';
1233
+ return false;
1234
+ }
1235
+ $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
1236
+ $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
1237
+ $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']);
1238
+ $info['quicktime']['display_scale'] = $atom_structure['matrix_a'];
1239
+ $info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
1240
+ break;
1241
+
1242
+
1243
+ case 'tkhd': // TracK HeaDer atom
1244
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1245
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1246
+ $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1247
+ $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
1248
+ $atom_structure['trackid'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1249
+ $atom_structure['reserved1'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1250
+ $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
1251
+ $atom_structure['reserved2'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 8));
1252
+ $atom_structure['layer'] = getid3_lib::BigEndian2Int(substr($atom_data, 32, 2));
1253
+ $atom_structure['alternate_group'] = getid3_lib::BigEndian2Int(substr($atom_data, 34, 2));
1254
+ $atom_structure['volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 36, 2));
1255
+ $atom_structure['reserved3'] = getid3_lib::BigEndian2Int(substr($atom_data, 38, 2));
1256
+ // http://developer.apple.com/library/mac/#documentation/QuickTime/RM/MovieBasics/MTEditing/K-Chapter/11MatrixFunctions.html
1257
+ // http://developer.apple.com/library/mac/#documentation/QuickTime/qtff/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-18737
1258
+ $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
1259
+ $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 44, 4));
1260
+ $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 48, 4));
1261
+ $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
1262
+ $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 56, 4));
1263
+ $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 60, 4));
1264
+ $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
1265
+ $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 68, 4));
1266
+ $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 72, 4));
1267
+ $atom_structure['width'] = getid3_lib::FixedPoint16_16(substr($atom_data, 76, 4));
1268
+ $atom_structure['height'] = getid3_lib::FixedPoint16_16(substr($atom_data, 80, 4));
1269
+ $atom_structure['flags']['enabled'] = (bool) ($atom_structure['flags_raw'] & 0x0001);
1270
+ $atom_structure['flags']['in_movie'] = (bool) ($atom_structure['flags_raw'] & 0x0002);
1271
+ $atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x0004);
1272
+ $atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x0008);
1273
+ $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
1274
+ $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
1275
+
1276
+ if ($atom_structure['flags']['enabled'] == 1) {
1277
+ if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) {
1278
+ $info['video']['resolution_x'] = $atom_structure['width'];
1279
+ $info['video']['resolution_y'] = $atom_structure['height'];
1280
+ }
1281
+ $info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']);
1282
+ $info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']);
1283
+ $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
1284
+ $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
1285
+ } else {
1286
+ // see: http://www.getid3.org/phpBB3/viewtopic.php?t=1295
1287
+ //if (isset($info['video']['resolution_x'])) { unset($info['video']['resolution_x']); }
1288
+ //if (isset($info['video']['resolution_y'])) { unset($info['video']['resolution_y']); }
1289
+ //if (isset($info['quicktime']['video'])) { unset($info['quicktime']['video']); }
1290
+ }
1291
+ break;
1292
+
1293
+
1294
+ case 'iods': // Initial Object DeScriptor atom
1295
+ // http://www.koders.com/c/fid1FAB3E762903DC482D8A246D4A4BF9F28E049594.aspx?s=windows.h
1296
+ // http://libquicktime.sourcearchive.com/documentation/1.0.2plus-pdebian/iods_8c-source.html
1297
+ $offset = 0;
1298
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1299
+ $offset += 1;
1300
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 3));
1301
+ $offset += 3;
1302
+ $atom_structure['mp4_iod_tag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1303
+ $offset += 1;
1304
+ $atom_structure['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
1305
+ //$offset already adjusted by quicktime_read_mp4_descr_length()
1306
+ $atom_structure['object_descriptor_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2));
1307
+ $offset += 2;
1308
+ $atom_structure['od_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1309
+ $offset += 1;
1310
+ $atom_structure['scene_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1311
+ $offset += 1;
1312
+ $atom_structure['audio_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1313
+ $offset += 1;
1314
+ $atom_structure['video_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1315
+ $offset += 1;
1316
+ $atom_structure['graphics_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1317
+ $offset += 1;
1318
+
1319
+ $atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6; // 6 bytes would only be right if all tracks use 1-byte length fields
1320
+ for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) {
1321
+ $atom_structure['track'][$i]['ES_ID_IncTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1322
+ $offset += 1;
1323
+ $atom_structure['track'][$i]['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
1324
+ //$offset already adjusted by quicktime_read_mp4_descr_length()
1325
+ $atom_structure['track'][$i]['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4));
1326
+ $offset += 4;
1327
+ }
1328
+
1329
+ $atom_structure['audio_profile_name'] = $this->QuicktimeIODSaudioProfileName($atom_structure['audio_profile_id']);
1330
+ $atom_structure['video_profile_name'] = $this->QuicktimeIODSvideoProfileName($atom_structure['video_profile_id']);
1331
+ break;
1332
+
1333
+ case 'ftyp': // FileTYPe (?) atom (for MP4 it seems)
1334
+ $atom_structure['signature'] = substr($atom_data, 0, 4);
1335
+ $atom_structure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1336
+ $atom_structure['fourcc'] = substr($atom_data, 8, 4);
1337
+ break;
1338
+
1339
+ case 'mdat': // Media DATa atom
1340
+ // 'mdat' contains the actual data for the audio/video, possibly also subtitles
1341
+
1342
+ /* 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 info@getid3.org */
1343
+
1344
+ // first, skip any 'wide' padding, and second 'mdat' header (with specified size of zero?)
1345
+ $mdat_offset = 0;
1346
+ while (true) {
1347
+ if (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x08".'wide') {
1348
+ $mdat_offset += 8;
1349
+ } elseif (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x00".'mdat') {
1350
+ $mdat_offset += 8;
1351
+ } else {
1352
+ break;
1353
+ }
1354
+ }
1355
+
1356
+ // check to see if it looks like chapter titles, in the form of unterminated strings with a leading 16-bit size field
1357
+ while (($mdat_offset < (strlen($atom_data) - 8))
1358
+ && ($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2)))
1359
+ && ($chapter_string_length < 1000)
1360
+ && ($chapter_string_length <= (strlen($atom_data) - $mdat_offset - 2))
1361
+ && preg_match('#^([\x00-\xFF]{2})([\x20-\xFF]+)$#', substr($atom_data, $mdat_offset, $chapter_string_length + 2), $chapter_matches)) {
1362
+ list($dummy, $chapter_string_length_hex, $chapter_string) = $chapter_matches;
1363
+ $mdat_offset += (2 + $chapter_string_length);
1364
+ @$info['quicktime']['comments']['chapters'][] = $chapter_string;
1365
+
1366
+ // "encd" atom specifies encoding. In theory could be anything, almost always UTF-8, but may be UTF-16 with BOM (not currently handled)
1367
+ if (substr($atom_data, $mdat_offset, 12) == "\x00\x00\x00\x0C\x65\x6E\x63\x64\x00\x00\x01\x00") { // UTF-8
1368
+ $mdat_offset += 12;
1369
+ }
1370
+ }
1371
+
1372
+
1373
+ if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) {
1374
+
1375
+ $info['avdataoffset'] = $atom_structure['offset'] + 8; // $info['quicktime'][$atomname]['offset'] + 8;
1376
+ $OldAVDataEnd = $info['avdataend'];
1377
+ $info['avdataend'] = $atom_structure['offset'] + $atom_structure['size']; // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size'];
1378
+
1379
+ $getid3_temp = new getID3();
1380
+ $getid3_temp->openfile($this->getid3->filename);
1381
+ $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1382
+ $getid3_temp->info['avdataend'] = $info['avdataend'];
1383
+ $getid3_mp3 = new getid3_mp3($getid3_temp);
1384
+ if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode($this->fread(4)))) {
1385
+ $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
1386
+ if (!empty($getid3_temp->info['warning'])) {
1387
+ foreach ($getid3_temp->info['warning'] as $value) {
1388
+ $info['warning'][] = $value;
1389
+ }
1390
+ }
1391
+ if (!empty($getid3_temp->info['mpeg'])) {
1392
+ $info['mpeg'] = $getid3_temp->info['mpeg'];
1393
+ if (isset($info['mpeg']['audio'])) {
1394
+ $info['audio']['dataformat'] = 'mp3';
1395
+ $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')));
1396
+ $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1397
+ $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
1398
+ $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1399
+ $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1400
+ $info['bitrate'] = $info['audio']['bitrate'];
1401
+ }
1402
+ }
1403
+ }
1404
+ unset($getid3_mp3, $getid3_temp);
1405
+ $info['avdataend'] = $OldAVDataEnd;
1406
+ unset($OldAVDataEnd);
1407
+
1408
+ }
1409
+
1410
+ unset($mdat_offset, $chapter_string_length, $chapter_matches);
1411
+ break;
1412
+
1413
+ case 'free': // FREE space atom
1414
+ case 'skip': // SKIP atom
1415
+ case 'wide': // 64-bit expansion placeholder atom
1416
+ // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
1417
+
1418
+ // When writing QuickTime files, it is sometimes necessary to update an atom's size.
1419
+ // It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom
1420
+ // is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime
1421
+ // puts an 8-byte placeholder atom before any atoms it may have to update the size of.
1422
+ // In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the
1423
+ // placeholder atom can be overwritten to obtain the necessary 8 extra bytes.
1424
+ // The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ).
1425
+ break;
1426
+
1427
+
1428
+ case 'nsav': // NoSAVe atom
1429
+ // http://developer.apple.com/technotes/tn/tn2038.html
1430
+ $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
1431
+ break;
1432
+
1433
+ case 'ctyp': // Controller TYPe atom (seen on QTVR)
1434
+ // http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
1435
+ // some controller names are:
1436
+ // 0x00 + 'std' for linear movie
1437
+ // 'none' for no controls
1438
+ $atom_structure['ctyp'] = substr($atom_data, 0, 4);
1439
+ $info['quicktime']['controller'] = $atom_structure['ctyp'];
1440
+ switch ($atom_structure['ctyp']) {
1441
+ case 'qtvr':
1442
+ $info['video']['dataformat'] = 'quicktimevr';
1443
+ break;
1444
+ }
1445
+ break;
1446
+
1447
+ case 'pano': // PANOrama track (seen on QTVR)
1448
+ $atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
1449
+ break;
1450
+
1451
+ case 'hint': // HINT track
1452
+ case 'hinf': //
1453
+ case 'hinv': //
1454
+ case 'hnti': //
1455
+ $info['quicktime']['hinting'] = true;
1456
+ break;
1457
+
1458
+ case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR)
1459
+ for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) {
1460
+ $atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
1461
+ }
1462
+ break;
1463
+
1464
+
1465
+ // Observed-but-not-handled atom types are just listed here to prevent warnings being generated
1466
+ case 'FXTC': // Something to do with Adobe After Effects (?)
1467
+ case 'PrmA':
1468
+ case 'code':
1469
+ case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html
1470
+ case 'tapt': // TrackApertureModeDimensionsAID - http://developer.apple.com/documentation/QuickTime/Reference/QT7-1_Update_Reference/Constants/Constants.html
1471
+ // tapt seems to be used to compute the video size [http://www.getid3.org/phpBB3/viewtopic.php?t=838]
1472
+ // * http://lists.apple.com/archives/quicktime-api/2006/Aug/msg00014.html
1473
+ // * http://handbrake.fr/irclogs/handbrake-dev/handbrake-dev20080128_pg2.html
1474
+ case 'ctts':// STCompositionOffsetAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1475
+ case 'cslg':// STCompositionShiftLeastGreatestAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1476
+ case 'sdtp':// STSampleDependencyAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1477
+ case 'stps':// STPartialSyncSampleAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1478
+ //$atom_structure['data'] = $atom_data;
1479
+ break;
1480
+
1481
+ case "\xA9".'xyz': // GPS latitude+longitude+altitude
1482
+ $atom_structure['data'] = $atom_data;
1483
+ if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) {
1484
+ @list($all, $latitude, $longitude, $altitude) = $matches;
1485
+ $info['quicktime']['comments']['gps_latitude'][] = floatval($latitude);
1486
+ $info['quicktime']['comments']['gps_longitude'][] = floatval($longitude);
1487
+ if (!empty($altitude)) {
1488
+ $info['quicktime']['comments']['gps_altitude'][] = floatval($altitude);
1489
+ }
1490
+ } else {
1491
+ $info['warning'][] = 'QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.';
1492
+ }
1493
+ break;
1494
+
1495
+ case 'NCDT':
1496
+ // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1497
+ // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
1498
+ $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms);
1499
+ break;
1500
+ case 'NCTH': // Nikon Camera THumbnail image
1501
+ case 'NCVW': // Nikon Camera preVieW image
1502
+ // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1503
+ if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) {
1504
+ $atom_structure['data'] = $atom_data;
1505
+ $atom_structure['image_mime'] = 'image/jpeg';
1506
+ $atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image'));
1507
+ $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']);
1508
+ }
1509
+ break;
1510
+ case 'NCTG': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
1511
+ $atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data);
1512
+ break;
1513
+ case 'NCHD': // Nikon:MakerNoteVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1514
+ case 'NCDB': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1515
+ case 'CNCV': // Canon:CompressorVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Canon.html
1516
+ $atom_structure['data'] = $atom_data;
1517
+ break;
1518
+
1519
+ case "\x00\x00\x00\x00":
1520
+ // some kind of metacontainer, may contain a big data dump such as:
1521
+ // 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
1522
+ // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
1523
+
1524
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1525
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1526
+ $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
1527
+ //$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
1528
+ break;
1529
+
1530
+ case 'meta': // METAdata atom
1531
+ // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html
1532
+
1533
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1534
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1535
+ $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
1536
+ break;
1537
+
1538
+ case 'data': // metaDATA atom
1539
+ 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
1540
+ // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data
1541
+ $atom_structure['language'] = substr($atom_data, 4 + 0, 2);
1542
+ $atom_structure['unknown'] = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2));
1543
+ $atom_structure['data'] = substr($atom_data, 4 + 4);
1544
+ $atom_structure['key_name'] = @$info['quicktime']['temp_meta_key_names'][$metaDATAkey++];
1545
+
1546
+ if ($atom_structure['key_name'] && $atom_structure['data']) {
1547
+ @$info['quicktime']['comments'][str_replace('com.apple.quicktime.', '', $atom_structure['key_name'])][] = $atom_structure['data'];
1548
+ }
1549
+ break;
1550
+
1551
+ case 'keys': // KEYS that may be present in the metadata atom.
1552
+ // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW21
1553
+ // The metadata item keys atom holds a list of the metadata keys that may be present in the metadata atom.
1554
+ // 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".
1555
+ $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1556
+ $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1557
+ $atom_structure['entry_count'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1558
+ $keys_atom_offset = 8;
1559
+ for ($i = 1; $i <= $atom_structure['entry_count']; $i++) {
1560
+ $atom_structure['keys'][$i]['key_size'] = getid3_lib::BigEndian2Int(substr($atom_data, $keys_atom_offset + 0, 4));
1561
+ $atom_structure['keys'][$i]['key_namespace'] = substr($atom_data, $keys_atom_offset + 4, 4);
1562
+ $atom_structure['keys'][$i]['key_value'] = substr($atom_data, $keys_atom_offset + 8, $atom_structure['keys'][$i]['key_size'] - 8);
1563
+ $keys_atom_offset += $atom_structure['keys'][$i]['key_size']; // key_size includes the 4+4 bytes for key_size and key_namespace
1564
+
1565
+ $info['quicktime']['temp_meta_key_names'][$i] = $atom_structure['keys'][$i]['key_value'];
1566
+ }
1567
+ break;
1568
+
1569
+ default:
1570
+ $info['warning'][] = 'Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).') at offset '.$baseoffset;
1571
+ $atom_structure['data'] = $atom_data;
1572
+ break;
1573
+ }
1574
+ array_pop($atomHierarchy);
1575
+ return $atom_structure;
1576
+ }
1577
+
1578
+ public function QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
1579
+ //echo 'QuicktimeParseContainerAtom('.substr($atom_data, 4, 4).') @ '.$baseoffset.'<br><br>';
1580
+ $atom_structure = false;
1581
+ $subatomoffset = 0;
1582
+ $subatomcounter = 0;
1583
+ if ((strlen($atom_data) == 4) && (getid3_lib::BigEndian2Int($atom_data) == 0x00000000)) {
1584
+ return false;
1585
+ }
1586
+ while ($subatomoffset < strlen($atom_data)) {
1587
+ $subatomsize = getid3_lib::BigEndian2Int(substr($atom_data, $subatomoffset + 0, 4));
1588
+ $subatomname = substr($atom_data, $subatomoffset + 4, 4);
1589
+ $subatomdata = substr($atom_data, $subatomoffset + 8, $subatomsize - 8);
1590
+ if ($subatomsize == 0) {
1591
+ // Furthermore, for historical reasons the list of atoms is optionally
1592
+ // terminated by a 32-bit integer set to 0. If you are writing a program
1593
+ // to read user data atoms, you should allow for the terminating 0.
1594
+ if (strlen($atom_data) > 12) {
1595
+ $subatomoffset += 4;
1596
+ continue;
1597
+ }
1598
+ return $atom_structure;
1599
+ }
1600
+
1601
+ $atom_structure[$subatomcounter] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms);
1602
+
1603
+ $subatomoffset += $subatomsize;
1604
+ $subatomcounter++;
1605
+ }
1606
+ return $atom_structure;
1607
+ }
1608
+
1609
+
1610
+ public function quicktime_read_mp4_descr_length($data, &$offset) {
1611
+ // http://libquicktime.sourcearchive.com/documentation/2:1.0.2plus-pdebian-2build1/esds_8c-source.html
1612
+ $num_bytes = 0;
1613
+ $length = 0;
1614
+ do {
1615
+ $b = ord(substr($data, $offset++, 1));
1616
+ $length = ($length << 7) | ($b & 0x7F);
1617
+ } while (($b & 0x80) && ($num_bytes++ < 4));
1618
+ return $length;
1619
+ }
1620
+
1621
+
1622
+ public function QuicktimeLanguageLookup($languageid) {
1623
+ // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-34353
1624
+ static $QuicktimeLanguageLookup = array();
1625
+ if (empty($QuicktimeLanguageLookup)) {
1626
+ $QuicktimeLanguageLookup[0] = 'English';
1627
+ $QuicktimeLanguageLookup[1] = 'French';
1628
+ $QuicktimeLanguageLookup[2] = 'German';
1629
+ $QuicktimeLanguageLookup[3] = 'Italian';
1630
+ $QuicktimeLanguageLookup[4] = 'Dutch';
1631
+ $QuicktimeLanguageLookup[5] = 'Swedish';
1632
+ $QuicktimeLanguageLookup[6] = 'Spanish';
1633
+ $QuicktimeLanguageLookup[7] = 'Danish';
1634
+ $QuicktimeLanguageLookup[8] = 'Portuguese';
1635
+ $QuicktimeLanguageLookup[9] = 'Norwegian';
1636
+ $QuicktimeLanguageLookup[10] = 'Hebrew';
1637
+ $QuicktimeLanguageLookup[11] = 'Japanese';
1638
+ $QuicktimeLanguageLookup[12] = 'Arabic';
1639
+ $QuicktimeLanguageLookup[13] = 'Finnish';
1640
+ $QuicktimeLanguageLookup[14] = 'Greek';
1641
+ $QuicktimeLanguageLookup[15] = 'Icelandic';
1642
+ $QuicktimeLanguageLookup[16] = 'Maltese';
1643
+ $QuicktimeLanguageLookup[17] = 'Turkish';
1644
+ $QuicktimeLanguageLookup[18] = 'Croatian';
1645
+ $QuicktimeLanguageLookup[19] = 'Chinese (Traditional)';
1646
+ $QuicktimeLanguageLookup[20] = 'Urdu';
1647
+ $QuicktimeLanguageLookup[21] = 'Hindi';
1648
+ $QuicktimeLanguageLookup[22] = 'Thai';
1649
+ $QuicktimeLanguageLookup[23] = 'Korean';
1650
+ $QuicktimeLanguageLookup[24] = 'Lithuanian';
1651
+ $QuicktimeLanguageLookup[25] = 'Polish';
1652
+ $QuicktimeLanguageLookup[26] = 'Hungarian';
1653
+ $QuicktimeLanguageLookup[27] = 'Estonian';
1654
+ $QuicktimeLanguageLookup[28] = 'Lettish';
1655
+ $QuicktimeLanguageLookup[28] = 'Latvian';
1656
+ $QuicktimeLanguageLookup[29] = 'Saamisk';
1657
+ $QuicktimeLanguageLookup[29] = 'Lappish';
1658
+ $QuicktimeLanguageLookup[30] = 'Faeroese';
1659
+ $QuicktimeLanguageLookup[31] = 'Farsi';
1660
+ $QuicktimeLanguageLookup[31] = 'Persian';
1661
+ $QuicktimeLanguageLookup[32] = 'Russian';
1662
+ $QuicktimeLanguageLookup[33] = 'Chinese (Simplified)';
1663
+ $QuicktimeLanguageLookup[34] = 'Flemish';
1664
+ $QuicktimeLanguageLookup[35] = 'Irish';
1665
+ $QuicktimeLanguageLookup[36] = 'Albanian';
1666
+ $QuicktimeLanguageLookup[37] = 'Romanian';
1667
+ $QuicktimeLanguageLookup[38] = 'Czech';
1668
+ $QuicktimeLanguageLookup[39] = 'Slovak';
1669
+ $QuicktimeLanguageLookup[40] = 'Slovenian';
1670
+ $QuicktimeLanguageLookup[41] = 'Yiddish';
1671
+ $QuicktimeLanguageLookup[42] = 'Serbian';
1672
+ $QuicktimeLanguageLookup[43] = 'Macedonian';
1673
+ $QuicktimeLanguageLookup[44] = 'Bulgarian';
1674
+ $QuicktimeLanguageLookup[45] = 'Ukrainian';
1675
+ $QuicktimeLanguageLookup[46] = 'Byelorussian';
1676
+ $QuicktimeLanguageLookup[47] = 'Uzbek';
1677
+ $QuicktimeLanguageLookup[48] = 'Kazakh';
1678
+ $QuicktimeLanguageLookup[49] = 'Azerbaijani';
1679
+ $QuicktimeLanguageLookup[50] = 'AzerbaijanAr';
1680
+ $QuicktimeLanguageLookup[51] = 'Armenian';
1681
+ $QuicktimeLanguageLookup[52] = 'Georgian';
1682
+ $QuicktimeLanguageLookup[53] = 'Moldavian';
1683
+ $QuicktimeLanguageLookup[54] = 'Kirghiz';
1684
+ $QuicktimeLanguageLookup[55] = 'Tajiki';
1685
+ $QuicktimeLanguageLookup[56] = 'Turkmen';
1686
+ $QuicktimeLanguageLookup[57] = 'Mongolian';
1687
+ $QuicktimeLanguageLookup[58] = 'MongolianCyr';
1688
+ $QuicktimeLanguageLookup[59] = 'Pashto';
1689
+ $QuicktimeLanguageLookup[60] = 'Kurdish';
1690
+ $QuicktimeLanguageLookup[61] = 'Kashmiri';
1691
+ $QuicktimeLanguageLookup[62] = 'Sindhi';
1692
+ $QuicktimeLanguageLookup[63] = 'Tibetan';
1693
+ $QuicktimeLanguageLookup[64] = 'Nepali';
1694
+ $QuicktimeLanguageLookup[65] = 'Sanskrit';
1695
+ $QuicktimeLanguageLookup[66] = 'Marathi';
1696
+ $QuicktimeLanguageLookup[67] = 'Bengali';
1697
+ $QuicktimeLanguageLookup[68] = 'Assamese';
1698
+ $QuicktimeLanguageLookup[69] = 'Gujarati';
1699
+ $QuicktimeLanguageLookup[70] = 'Punjabi';
1700
+ $QuicktimeLanguageLookup[71] = 'Oriya';
1701
+ $QuicktimeLanguageLookup[72] = 'Malayalam';
1702
+ $QuicktimeLanguageLookup[73] = 'Kannada';
1703
+ $QuicktimeLanguageLookup[74] = 'Tamil';
1704
+ $QuicktimeLanguageLookup[75] = 'Telugu';
1705
+ $QuicktimeLanguageLookup[76] = 'Sinhalese';
1706
+ $QuicktimeLanguageLookup[77] = 'Burmese';
1707
+ $QuicktimeLanguageLookup[78] = 'Khmer';
1708
+ $QuicktimeLanguageLookup[79] = 'Lao';
1709
+ $QuicktimeLanguageLookup[80] = 'Vietnamese';
1710
+ $QuicktimeLanguageLookup[81] = 'Indonesian';
1711
+ $QuicktimeLanguageLookup[82] = 'Tagalog';
1712
+ $QuicktimeLanguageLookup[83] = 'MalayRoman';
1713
+ $QuicktimeLanguageLookup[84] = 'MalayArabic';
1714
+ $QuicktimeLanguageLookup[85] = 'Amharic';
1715
+ $QuicktimeLanguageLookup[86] = 'Tigrinya';
1716
+ $QuicktimeLanguageLookup[87] = 'Galla';
1717
+ $QuicktimeLanguageLookup[87] = 'Oromo';
1718
+ $QuicktimeLanguageLookup[88] = 'Somali';
1719
+ $QuicktimeLanguageLookup[89] = 'Swahili';
1720
+ $QuicktimeLanguageLookup[90] = 'Ruanda';
1721
+ $QuicktimeLanguageLookup[91] = 'Rundi';
1722
+ $QuicktimeLanguageLookup[92] = 'Chewa';
1723
+ $QuicktimeLanguageLookup[93] = 'Malagasy';
1724
+ $QuicktimeLanguageLookup[94] = 'Esperanto';
1725
+ $QuicktimeLanguageLookup[128] = 'Welsh';
1726
+ $QuicktimeLanguageLookup[129] = 'Basque';
1727
+ $QuicktimeLanguageLookup[130] = 'Catalan';
1728
+ $QuicktimeLanguageLookup[131] = 'Latin';
1729
+ $QuicktimeLanguageLookup[132] = 'Quechua';
1730
+ $QuicktimeLanguageLookup[133] = 'Guarani';
1731
+ $QuicktimeLanguageLookup[134] = 'Aymara';
1732
+ $QuicktimeLanguageLookup[135] = 'Tatar';
1733
+ $QuicktimeLanguageLookup[136] = 'Uighur';
1734
+ $QuicktimeLanguageLookup[137] = 'Dzongkha';
1735
+ $QuicktimeLanguageLookup[138] = 'JavaneseRom';
1736
+ $QuicktimeLanguageLookup[32767] = 'Unspecified';
1737
+ }
1738
+ if (($languageid > 138) && ($languageid < 32767)) {
1739
+ /*
1740
+ ISO Language Codes - http://www.loc.gov/standards/iso639-2/php/code_list.php
1741
+ 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.
1742
+ The packing algorithm must map each of the three characters, which are always lowercase, into a 5-bit integer and then concatenate
1743
+ 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.
1744
+
1745
+ One algorithm for performing this packing is to treat each ISO character as a 16-bit integer. Subtract 0x60 from the first character
1746
+ and multiply by 2^10 (0x400), subtract 0x60 from the second character and multiply by 2^5 (0x20), subtract 0x60 from the third character,
1747
+ 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
1748
+ significant bits and the most significant bit set to zero.
1749
+ */
1750
+ $iso_language_id = '';
1751
+ $iso_language_id .= chr((($languageid & 0x7C00) >> 10) + 0x60);
1752
+ $iso_language_id .= chr((($languageid & 0x03E0) >> 5) + 0x60);
1753
+ $iso_language_id .= chr((($languageid & 0x001F) >> 0) + 0x60);
1754
+ $QuicktimeLanguageLookup[$languageid] = getid3_id3v2::LanguageLookup($iso_language_id);
1755
+ }
1756
+ return (isset($QuicktimeLanguageLookup[$languageid]) ? $QuicktimeLanguageLookup[$languageid] : 'invalid');
1757
+ }
1758
+
1759
+ public function QuicktimeVideoCodecLookup($codecid) {
1760
+ static $QuicktimeVideoCodecLookup = array();
1761
+ if (empty($QuicktimeVideoCodecLookup)) {
1762
+ $QuicktimeVideoCodecLookup['.SGI'] = 'SGI';
1763
+ $QuicktimeVideoCodecLookup['3IV1'] = '3ivx MPEG-4 v1';
1764
+ $QuicktimeVideoCodecLookup['3IV2'] = '3ivx MPEG-4 v2';
1765
+ $QuicktimeVideoCodecLookup['3IVX'] = '3ivx MPEG-4';
1766
+ $QuicktimeVideoCodecLookup['8BPS'] = 'Planar RGB';
1767
+ $QuicktimeVideoCodecLookup['avc1'] = 'H.264/MPEG-4 AVC';
1768
+ $QuicktimeVideoCodecLookup['avr '] = 'AVR-JPEG';
1769
+ $QuicktimeVideoCodecLookup['b16g'] = '16Gray';
1770
+ $QuicktimeVideoCodecLookup['b32a'] = '32AlphaGray';
1771
+ $QuicktimeVideoCodecLookup['b48r'] = '48RGB';
1772
+ $QuicktimeVideoCodecLookup['b64a'] = '64ARGB';
1773
+ $QuicktimeVideoCodecLookup['base'] = 'Base';
1774
+ $QuicktimeVideoCodecLookup['clou'] = 'Cloud';
1775
+ $QuicktimeVideoCodecLookup['cmyk'] = 'CMYK';
1776
+ $QuicktimeVideoCodecLookup['cvid'] = 'Cinepak';
1777
+ $QuicktimeVideoCodecLookup['dmb1'] = 'OpenDML JPEG';
1778
+ $QuicktimeVideoCodecLookup['dvc '] = 'DVC-NTSC';
1779
+ $QuicktimeVideoCodecLookup['dvcp'] = 'DVC-PAL';
1780
+ $QuicktimeVideoCodecLookup['dvpn'] = 'DVCPro-NTSC';
1781
+ $QuicktimeVideoCodecLookup['dvpp'] = 'DVCPro-PAL';
1782
+ $QuicktimeVideoCodecLookup['fire'] = 'Fire';
1783
+ $QuicktimeVideoCodecLookup['flic'] = 'FLC';
1784
+ $QuicktimeVideoCodecLookup['gif '] = 'GIF';
1785
+ $QuicktimeVideoCodecLookup['h261'] = 'H261';
1786
+ $QuicktimeVideoCodecLookup['h263'] = 'H263';
1787
+ $QuicktimeVideoCodecLookup['IV41'] = 'Indeo4';
1788
+ $QuicktimeVideoCodecLookup['jpeg'] = 'JPEG';
1789
+ $QuicktimeVideoCodecLookup['kpcd'] = 'PhotoCD';
1790
+ $QuicktimeVideoCodecLookup['mjpa'] = 'Motion JPEG-A';
1791
+ $QuicktimeVideoCodecLookup['mjpb'] = 'Motion JPEG-B';
1792
+ $QuicktimeVideoCodecLookup['msvc'] = 'Microsoft Video1';
1793
+ $QuicktimeVideoCodecLookup['myuv'] = 'MPEG YUV420';
1794
+ $QuicktimeVideoCodecLookup['path'] = 'Vector';
1795
+ $QuicktimeVideoCodecLookup['png '] = 'PNG';
1796
+ $QuicktimeVideoCodecLookup['PNTG'] = 'MacPaint';
1797
+ $QuicktimeVideoCodecLookup['qdgx'] = 'QuickDrawGX';
1798
+ $QuicktimeVideoCodecLookup['qdrw'] = 'QuickDraw';
1799
+ $QuicktimeVideoCodecLookup['raw '] = 'RAW';
1800
+ $QuicktimeVideoCodecLookup['ripl'] = 'WaterRipple';
1801
+ $QuicktimeVideoCodecLookup['rpza'] = 'Video';
1802
+ $QuicktimeVideoCodecLookup['smc '] = 'Graphics';
1803
+ $QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 1';
1804
+ $QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 3';
1805
+ $QuicktimeVideoCodecLookup['syv9'] = 'Sorenson YUV9';
1806
+ $QuicktimeVideoCodecLookup['tga '] = 'Targa';
1807
+ $QuicktimeVideoCodecLookup['tiff'] = 'TIFF';
1808
+ $QuicktimeVideoCodecLookup['WRAW'] = 'Windows RAW';
1809
+ $QuicktimeVideoCodecLookup['WRLE'] = 'BMP';
1810
+ $QuicktimeVideoCodecLookup['y420'] = 'YUV420';
1811
+ $QuicktimeVideoCodecLookup['yuv2'] = 'ComponentVideo';
1812
+ $QuicktimeVideoCodecLookup['yuvs'] = 'ComponentVideoUnsigned';
1813
+ $QuicktimeVideoCodecLookup['yuvu'] = 'ComponentVideoSigned';
1814
+ }
1815
+ return (isset($QuicktimeVideoCodecLookup[$codecid]) ? $QuicktimeVideoCodecLookup[$codecid] : '');
1816
+ }
1817
+
1818
+ public function QuicktimeAudioCodecLookup($codecid) {
1819
+ static $QuicktimeAudioCodecLookup = array();
1820
+ if (empty($QuicktimeAudioCodecLookup)) {
1821
+ $QuicktimeAudioCodecLookup['.mp3'] = 'Fraunhofer MPEG Layer-III alias';
1822
+ $QuicktimeAudioCodecLookup['aac '] = 'ISO/IEC 14496-3 AAC';
1823
+ $QuicktimeAudioCodecLookup['agsm'] = 'Apple GSM 10:1';
1824
+ $QuicktimeAudioCodecLookup['alac'] = 'Apple Lossless Audio Codec';
1825
+ $QuicktimeAudioCodecLookup['alaw'] = 'A-law 2:1';
1826
+ $QuicktimeAudioCodecLookup['conv'] = 'Sample Format';
1827
+ $QuicktimeAudioCodecLookup['dvca'] = 'DV';
1828
+ $QuicktimeAudioCodecLookup['dvi '] = 'DV 4:1';
1829
+ $QuicktimeAudioCodecLookup['eqal'] = 'Frequency Equalizer';
1830
+ $QuicktimeAudioCodecLookup['fl32'] = '32-bit Floating Point';
1831
+ $QuicktimeAudioCodecLookup['fl64'] = '64-bit Floating Point';
1832
+ $QuicktimeAudioCodecLookup['ima4'] = 'Interactive Multimedia Association 4:1';
1833
+ $QuicktimeAudioCodecLookup['in24'] = '24-bit Integer';
1834
+ $QuicktimeAudioCodecLookup['in32'] = '32-bit Integer';
1835
+ $QuicktimeAudioCodecLookup['lpc '] = 'LPC 23:1';
1836
+ $QuicktimeAudioCodecLookup['MAC3'] = 'Macintosh Audio Compression/Expansion (MACE) 3:1';
1837
+ $QuicktimeAudioCodecLookup['MAC6'] = 'Macintosh Audio Compression/Expansion (MACE) 6:1';
1838
+ $QuicktimeAudioCodecLookup['mixb'] = '8-bit Mixer';
1839
+ $QuicktimeAudioCodecLookup['mixw'] = '16-bit Mixer';
1840
+ $QuicktimeAudioCodecLookup['mp4a'] = 'ISO/IEC 14496-3 AAC';
1841
+ $QuicktimeAudioCodecLookup['MS'."\x00\x02"] = 'Microsoft ADPCM';
1842
+ $QuicktimeAudioCodecLookup['MS'."\x00\x11"] = 'DV IMA';
1843
+ $QuicktimeAudioCodecLookup['MS'."\x00\x55"] = 'Fraunhofer MPEG Layer III';
1844
+ $QuicktimeAudioCodecLookup['NONE'] = 'No Encoding';
1845
+ $QuicktimeAudioCodecLookup['Qclp'] = 'Qualcomm PureVoice';
1846
+ $QuicktimeAudioCodecLookup['QDM2'] = 'QDesign Music 2';
1847
+ $QuicktimeAudioCodecLookup['QDMC'] = 'QDesign Music 1';
1848
+ $QuicktimeAudioCodecLookup['ratb'] = '8-bit Rate';
1849
+ $QuicktimeAudioCodecLookup['ratw'] = '16-bit Rate';
1850
+ $QuicktimeAudioCodecLookup['raw '] = 'raw PCM';
1851
+ $QuicktimeAudioCodecLookup['sour'] = 'Sound Source';
1852
+ $QuicktimeAudioCodecLookup['sowt'] = 'signed/two\'s complement (Little Endian)';
1853
+ $QuicktimeAudioCodecLookup['str1'] = 'Iomega MPEG layer II';
1854
+ $QuicktimeAudioCodecLookup['str2'] = 'Iomega MPEG *layer II';
1855
+ $QuicktimeAudioCodecLookup['str3'] = 'Iomega MPEG **layer II';
1856
+ $QuicktimeAudioCodecLookup['str4'] = 'Iomega MPEG ***layer II';
1857
+ $QuicktimeAudioCodecLookup['twos'] = 'signed/two\'s complement (Big Endian)';
1858
+ $QuicktimeAudioCodecLookup['ulaw'] = 'mu-law 2:1';
1859
+ }
1860
+ return (isset($QuicktimeAudioCodecLookup[$codecid]) ? $QuicktimeAudioCodecLookup[$codecid] : '');
1861
+ }
1862
+
1863
+ public function QuicktimeDCOMLookup($compressionid) {
1864
+ static $QuicktimeDCOMLookup = array();
1865
+ if (empty($QuicktimeDCOMLookup)) {
1866
+ $QuicktimeDCOMLookup['zlib'] = 'ZLib Deflate';
1867
+ $QuicktimeDCOMLookup['adec'] = 'Apple Compression';
1868
+ }
1869
+ return (isset($QuicktimeDCOMLookup[$compressionid]) ? $QuicktimeDCOMLookup[$compressionid] : '');
1870
+ }
1871
+
1872
+ public function QuicktimeColorNameLookup($colordepthid) {
1873
+ static $QuicktimeColorNameLookup = array();
1874
+ if (empty($QuicktimeColorNameLookup)) {
1875
+ $QuicktimeColorNameLookup[1] = '2-color (monochrome)';
1876
+ $QuicktimeColorNameLookup[2] = '4-color';
1877
+ $QuicktimeColorNameLookup[4] = '16-color';
1878
+ $QuicktimeColorNameLookup[8] = '256-color';
1879
+ $QuicktimeColorNameLookup[16] = 'thousands (16-bit color)';
1880
+ $QuicktimeColorNameLookup[24] = 'millions (24-bit color)';
1881
+ $QuicktimeColorNameLookup[32] = 'millions+ (32-bit color)';
1882
+ $QuicktimeColorNameLookup[33] = 'black & white';
1883
+ $QuicktimeColorNameLookup[34] = '4-gray';
1884
+ $QuicktimeColorNameLookup[36] = '16-gray';
1885
+ $QuicktimeColorNameLookup[40] = '256-gray';
1886
+ }
1887
+ return (isset($QuicktimeColorNameLookup[$colordepthid]) ? $QuicktimeColorNameLookup[$colordepthid] : 'invalid');
1888
+ }
1889
+
1890
+ public function QuicktimeSTIKLookup($stik) {
1891
+ static $QuicktimeSTIKLookup = array();
1892
+ if (empty($QuicktimeSTIKLookup)) {
1893
+ $QuicktimeSTIKLookup[0] = 'Movie';
1894
+ $QuicktimeSTIKLookup[1] = 'Normal';
1895
+ $QuicktimeSTIKLookup[2] = 'Audiobook';
1896
+ $QuicktimeSTIKLookup[5] = 'Whacked Bookmark';
1897
+ $QuicktimeSTIKLookup[6] = 'Music Video';
1898
+ $QuicktimeSTIKLookup[9] = 'Short Film';
1899
+ $QuicktimeSTIKLookup[10] = 'TV Show';
1900
+ $QuicktimeSTIKLookup[11] = 'Booklet';
1901
+ $QuicktimeSTIKLookup[14] = 'Ringtone';
1902
+ $QuicktimeSTIKLookup[21] = 'Podcast';
1903
+ }
1904
+ return (isset($QuicktimeSTIKLookup[$stik]) ? $QuicktimeSTIKLookup[$stik] : 'invalid');
1905
+ }
1906
+
1907
+ public function QuicktimeIODSaudioProfileName($audio_profile_id) {
1908
+ static $QuicktimeIODSaudioProfileNameLookup = array();
1909
+ if (empty($QuicktimeIODSaudioProfileNameLookup)) {
1910
+ $QuicktimeIODSaudioProfileNameLookup = array(
1911
+ 0x00 => 'ISO Reserved (0x00)',
1912
+ 0x01 => 'Main Audio Profile @ Level 1',
1913
+ 0x02 => 'Main Audio Profile @ Level 2',
1914
+ 0x03 => 'Main Audio Profile @ Level 3',
1915
+ 0x04 => 'Main Audio Profile @ Level 4',
1916
+ 0x05 => 'Scalable Audio Profile @ Level 1',
1917
+ 0x06 => 'Scalable Audio Profile @ Level 2',
1918
+ 0x07 => 'Scalable Audio Profile @ Level 3',
1919
+ 0x08 => 'Scalable Audio Profile @ Level 4',
1920
+ 0x09 => 'Speech Audio Profile @ Level 1',
1921
+ 0x0A => 'Speech Audio Profile @ Level 2',
1922
+ 0x0B => 'Synthetic Audio Profile @ Level 1',
1923
+ 0x0C => 'Synthetic Audio Profile @ Level 2',
1924
+ 0x0D => 'Synthetic Audio Profile @ Level 3',
1925
+ 0x0E => 'High Quality Audio Profile @ Level 1',
1926
+ 0x0F => 'High Quality Audio Profile @ Level 2',
1927
+ 0x10 => 'High Quality Audio Profile @ Level 3',
1928
+ 0x11 => 'High Quality Audio Profile @ Level 4',
1929
+ 0x12 => 'High Quality Audio Profile @ Level 5',
1930
+ 0x13 => 'High Quality Audio Profile @ Level 6',
1931
+ 0x14 => 'High Quality Audio Profile @ Level 7',
1932
+ 0x15 => 'High Quality Audio Profile @ Level 8',
1933
+ 0x16 => 'Low Delay Audio Profile @ Level 1',
1934
+ 0x17 => 'Low Delay Audio Profile @ Level 2',
1935
+ 0x18 => 'Low Delay Audio Profile @ Level 3',
1936
+ 0x19 => 'Low Delay Audio Profile @ Level 4',
1937
+ 0x1A => 'Low Delay Audio Profile @ Level 5',
1938
+ 0x1B => 'Low Delay Audio Profile @ Level 6',
1939
+ 0x1C => 'Low Delay Audio Profile @ Level 7',
1940
+ 0x1D => 'Low Delay Audio Profile @ Level 8',
1941
+ 0x1E => 'Natural Audio Profile @ Level 1',
1942
+ 0x1F => 'Natural Audio Profile @ Level 2',
1943
+ 0x20 => 'Natural Audio Profile @ Level 3',
1944
+ 0x21 => 'Natural Audio Profile @ Level 4',
1945
+ 0x22 => 'Mobile Audio Internetworking Profile @ Level 1',
1946
+ 0x23 => 'Mobile Audio Internetworking Profile @ Level 2',
1947
+ 0x24 => 'Mobile Audio Internetworking Profile @ Level 3',
1948
+ 0x25 => 'Mobile Audio Internetworking Profile @ Level 4',
1949
+ 0x26 => 'Mobile Audio Internetworking Profile @ Level 5',
1950
+ 0x27 => 'Mobile Audio Internetworking Profile @ Level 6',
1951
+ 0x28 => 'AAC Profile @ Level 1',
1952
+ 0x29 => 'AAC Profile @ Level 2',
1953
+ 0x2A => 'AAC Profile @ Level 4',
1954
+ 0x2B => 'AAC Profile @ Level 5',
1955
+ 0x2C => 'High Efficiency AAC Profile @ Level 2',
1956
+ 0x2D => 'High Efficiency AAC Profile @ Level 3',
1957
+ 0x2E => 'High Efficiency AAC Profile @ Level 4',
1958
+ 0x2F => 'High Efficiency AAC Profile @ Level 5',
1959
+ 0xFE => 'Not part of MPEG-4 audio profiles',
1960
+ 0xFF => 'No audio capability required',
1961
+ );
1962
+ }
1963
+ return (isset($QuicktimeIODSaudioProfileNameLookup[$audio_profile_id]) ? $QuicktimeIODSaudioProfileNameLookup[$audio_profile_id] : 'ISO Reserved / User Private');
1964
+ }
1965
+
1966
+
1967
+ public function QuicktimeIODSvideoProfileName($video_profile_id) {
1968
+ static $QuicktimeIODSvideoProfileNameLookup = array();
1969
+ if (empty($QuicktimeIODSvideoProfileNameLookup)) {
1970
+ $QuicktimeIODSvideoProfileNameLookup = array(
1971
+ 0x00 => 'Reserved (0x00) Profile',
1972
+ 0x01 => 'Simple Profile @ Level 1',
1973
+ 0x02 => 'Simple Profile @ Level 2',
1974
+ 0x03 => 'Simple Profile @ Level 3',
1975
+ 0x08 => 'Simple Profile @ Level 0',
1976
+ 0x10 => 'Simple Scalable Profile @ Level 0',
1977
+ 0x11 => 'Simple Scalable Profile @ Level 1',
1978
+ 0x12 => 'Simple Scalable Profile @ Level 2',
1979
+ 0x15 => 'AVC/H264 Profile',
1980
+ 0x21 => 'Core Profile @ Level 1',
1981
+ 0x22 => 'Core Profile @ Level 2',
1982
+ 0x32 => 'Main Profile @ Level 2',
1983
+ 0x33 => 'Main Profile @ Level 3',
1984
+ 0x34 => 'Main Profile @ Level 4',
1985
+ 0x42 => 'N-bit Profile @ Level 2',
1986
+ 0x51 => 'Scalable Texture Profile @ Level 1',
1987
+ 0x61 => 'Simple Face Animation Profile @ Level 1',
1988
+ 0x62 => 'Simple Face Animation Profile @ Level 2',
1989
+ 0x63 => 'Simple FBA Profile @ Level 1',
1990
+ 0x64 => 'Simple FBA Profile @ Level 2',
1991
+ 0x71 => 'Basic Animated Texture Profile @ Level 1',
1992
+ 0x72 => 'Basic Animated Texture Profile @ Level 2',
1993
+ 0x81 => 'Hybrid Profile @ Level 1',
1994
+ 0x82 => 'Hybrid Profile @ Level 2',
1995
+ 0x91 => 'Advanced Real Time Simple Profile @ Level 1',
1996
+ 0x92 => 'Advanced Real Time Simple Profile @ Level 2',
1997
+ 0x93 => 'Advanced Real Time Simple Profile @ Level 3',
1998
+ 0x94 => 'Advanced Real Time Simple Profile @ Level 4',
1999
+ 0xA1 => 'Core Scalable Profile @ Level1',
2000
+ 0xA2 => 'Core Scalable Profile @ Level2',
2001
+ 0xA3 => 'Core Scalable Profile @ Level3',
2002
+ 0xB1 => 'Advanced Coding Efficiency Profile @ Level 1',
2003
+ 0xB2 => 'Advanced Coding Efficiency Profile @ Level 2',
2004
+ 0xB3 => 'Advanced Coding Efficiency Profile @ Level 3',
2005
+ 0xB4 => 'Advanced Coding Efficiency Profile @ Level 4',
2006
+ 0xC1 => 'Advanced Core Profile @ Level 1',
2007
+ 0xC2 => 'Advanced Core Profile @ Level 2',
2008
+ 0xD1 => 'Advanced Scalable Texture @ Level1',
2009
+ 0xD2 => 'Advanced Scalable Texture @ Level2',
2010
+ 0xE1 => 'Simple Studio Profile @ Level 1',
2011
+ 0xE2 => 'Simple Studio Profile @ Level 2',
2012
+ 0xE3 => 'Simple Studio Profile @ Level 3',
2013
+ 0xE4 => 'Simple Studio Profile @ Level 4',
2014
+ 0xE5 => 'Core Studio Profile @ Level 1',
2015
+ 0xE6 => 'Core Studio Profile @ Level 2',
2016
+ 0xE7 => 'Core Studio Profile @ Level 3',
2017
+ 0xE8 => 'Core Studio Profile @ Level 4',
2018
+ 0xF0 => 'Advanced Simple Profile @ Level 0',
2019
+ 0xF1 => 'Advanced Simple Profile @ Level 1',
2020
+ 0xF2 => 'Advanced Simple Profile @ Level 2',
2021
+ 0xF3 => 'Advanced Simple Profile @ Level 3',
2022
+ 0xF4 => 'Advanced Simple Profile @ Level 4',
2023
+ 0xF5 => 'Advanced Simple Profile @ Level 5',
2024
+ 0xF7 => 'Advanced Simple Profile @ Level 3b',
2025
+ 0xF8 => 'Fine Granularity Scalable Profile @ Level 0',
2026
+ 0xF9 => 'Fine Granularity Scalable Profile @ Level 1',
2027
+ 0xFA => 'Fine Granularity Scalable Profile @ Level 2',
2028
+ 0xFB => 'Fine Granularity Scalable Profile @ Level 3',
2029
+ 0xFC => 'Fine Granularity Scalable Profile @ Level 4',
2030
+ 0xFD => 'Fine Granularity Scalable Profile @ Level 5',
2031
+ 0xFE => 'Not part of MPEG-4 Visual profiles',
2032
+ 0xFF => 'No visual capability required',
2033
+ );
2034
+ }
2035
+ return (isset($QuicktimeIODSvideoProfileNameLookup[$video_profile_id]) ? $QuicktimeIODSvideoProfileNameLookup[$video_profile_id] : 'ISO Reserved Profile');
2036
+ }
2037
+
2038
+
2039
+ public function QuicktimeContentRatingLookup($rtng) {
2040
+ static $QuicktimeContentRatingLookup = array();
2041
+ if (empty($QuicktimeContentRatingLookup)) {
2042
+ $QuicktimeContentRatingLookup[0] = 'None';
2043
+ $QuicktimeContentRatingLookup[2] = 'Clean';
2044
+ $QuicktimeContentRatingLookup[4] = 'Explicit';
2045
+ }
2046
+ return (isset($QuicktimeContentRatingLookup[$rtng]) ? $QuicktimeContentRatingLookup[$rtng] : 'invalid');
2047
+ }
2048
+
2049
+ public function QuicktimeStoreAccountTypeLookup($akid) {
2050
+ static $QuicktimeStoreAccountTypeLookup = array();
2051
+ if (empty($QuicktimeStoreAccountTypeLookup)) {
2052
+ $QuicktimeStoreAccountTypeLookup[0] = 'iTunes';
2053
+ $QuicktimeStoreAccountTypeLookup[1] = 'AOL';
2054
+ }
2055
+ return (isset($QuicktimeStoreAccountTypeLookup[$akid]) ? $QuicktimeStoreAccountTypeLookup[$akid] : 'invalid');
2056
+ }
2057
+
2058
+ public function QuicktimeStoreFrontCodeLookup($sfid) {
2059
+ static $QuicktimeStoreFrontCodeLookup = array();
2060
+ if (empty($QuicktimeStoreFrontCodeLookup)) {
2061
+ $QuicktimeStoreFrontCodeLookup[143460] = 'Australia';
2062
+ $QuicktimeStoreFrontCodeLookup[143445] = 'Austria';
2063
+ $QuicktimeStoreFrontCodeLookup[143446] = 'Belgium';
2064
+ $QuicktimeStoreFrontCodeLookup[143455] = 'Canada';
2065
+ $QuicktimeStoreFrontCodeLookup[143458] = 'Denmark';
2066
+ $QuicktimeStoreFrontCodeLookup[143447] = 'Finland';
2067
+ $QuicktimeStoreFrontCodeLookup[143442] = 'France';
2068
+ $QuicktimeStoreFrontCodeLookup[143443] = 'Germany';
2069
+ $QuicktimeStoreFrontCodeLookup[143448] = 'Greece';
2070
+ $QuicktimeStoreFrontCodeLookup[143449] = 'Ireland';
2071
+ $QuicktimeStoreFrontCodeLookup[143450] = 'Italy';
2072
+ $QuicktimeStoreFrontCodeLookup[143462] = 'Japan';
2073
+ $QuicktimeStoreFrontCodeLookup[143451] = 'Luxembourg';
2074
+ $QuicktimeStoreFrontCodeLookup[143452] = 'Netherlands';
2075
+ $QuicktimeStoreFrontCodeLookup[143461] = 'New Zealand';
2076
+ $QuicktimeStoreFrontCodeLookup[143457] = 'Norway';
2077
+ $QuicktimeStoreFrontCodeLookup[143453] = 'Portugal';
2078
+ $QuicktimeStoreFrontCodeLookup[143454] = 'Spain';
2079
+ $QuicktimeStoreFrontCodeLookup[143456] = 'Sweden';
2080
+ $QuicktimeStoreFrontCodeLookup[143459] = 'Switzerland';
2081
+ $QuicktimeStoreFrontCodeLookup[143444] = 'United Kingdom';
2082
+ $QuicktimeStoreFrontCodeLookup[143441] = 'United States';
2083
+ }
2084
+ return (isset($QuicktimeStoreFrontCodeLookup[$sfid]) ? $QuicktimeStoreFrontCodeLookup[$sfid] : 'invalid');
2085
+ }
2086
+
2087
+ public function QuicktimeParseNikonNCTG($atom_data) {
2088
+ // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
2089
+ // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
2090
+ // Data is stored as records of:
2091
+ // * 4 bytes record type
2092
+ // * 2 bytes size of data field type:
2093
+ // 0x0001 = flag (size field *= 1-byte)
2094
+ // 0x0002 = char (size field *= 1-byte)
2095
+ // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
2096
+ // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
2097
+ // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
2098
+ // 0x0007 = bytes (size field *= 1-byte), values are stored as ??????
2099
+ // 0x0008 = ????? (size field *= 2-byte), values are stored as ??????
2100
+ // * 2 bytes data size field
2101
+ // * ? bytes data (string data may be null-padded; datestamp fields are in the format "2011:05:25 20:24:15")
2102
+ // all integers are stored BigEndian
2103
+
2104
+ $NCTGtagName = array(
2105
+ 0x00000001 => 'Make',
2106
+ 0x00000002 => 'Model',
2107
+ 0x00000003 => 'Software',
2108
+ 0x00000011 => 'CreateDate',
2109
+ 0x00000012 => 'DateTimeOriginal',
2110
+ 0x00000013 => 'FrameCount',
2111
+ 0x00000016 => 'FrameRate',
2112
+ 0x00000022 => 'FrameWidth',
2113
+ 0x00000023 => 'FrameHeight',
2114
+ 0x00000032 => 'AudioChannels',
2115
+ 0x00000033 => 'AudioBitsPerSample',
2116
+ 0x00000034 => 'AudioSampleRate',
2117
+ 0x02000001 => 'MakerNoteVersion',
2118
+ 0x02000005 => 'WhiteBalance',
2119
+ 0x0200000b => 'WhiteBalanceFineTune',
2120
+ 0x0200001e => 'ColorSpace',
2121
+ 0x02000023 => 'PictureControlData',
2122
+ 0x02000024 => 'WorldTime',
2123
+ 0x02000032 => 'UnknownInfo',
2124
+ 0x02000083 => 'LensType',
2125
+ 0x02000084 => 'Lens',
2126
+ );
2127
+
2128
+ $offset = 0;
2129
+ $datalength = strlen($atom_data);
2130
+ $parsed = array();
2131
+ while ($offset < $datalength) {
2132
+ //echo getid3_lib::PrintHexBytes(substr($atom_data, $offset, 4)).'<br>';
2133
+ $record_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4)); $offset += 4;
2134
+ $data_size_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2;
2135
+ $data_size = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2;
2136
+ switch ($data_size_type) {
2137
+ case 0x0001: // 0x0001 = flag (size field *= 1-byte)
2138
+ $data = getid3_lib::BigEndian2Int(substr($atom_data, $offset, $data_size * 1));
2139
+ $offset += ($data_size * 1);
2140
+ break;
2141
+ case 0x0002: // 0x0002 = char (size field *= 1-byte)
2142
+ $data = substr($atom_data, $offset, $data_size * 1);
2143
+ $offset += ($data_size * 1);
2144
+ $data = rtrim($data, "\x00");
2145
+ break;
2146
+ case 0x0003: // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
2147
+ $data = '';
2148
+ for ($i = $data_size - 1; $i >= 0; $i--) {
2149
+ $data .= substr($atom_data, $offset + ($i * 2), 2);
2150
+ }
2151
+ $data = getid3_lib::BigEndian2Int($data);
2152
+ $offset += ($data_size * 2);
2153
+ break;
2154
+ case 0x0004: // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
2155
+ $data = '';
2156
+ for ($i = $data_size - 1; $i >= 0; $i--) {
2157
+ $data .= substr($atom_data, $offset + ($i * 4), 4);
2158
+ }
2159
+ $data = getid3_lib::BigEndian2Int($data);
2160
+ $offset += ($data_size * 4);
2161
+ break;
2162
+ case 0x0005: // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
2163
+ $data = array();
2164
+ for ($i = 0; $i < $data_size; $i++) {
2165
+ $numerator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 0, 4));
2166
+ $denomninator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 4, 4));
2167
+ if ($denomninator == 0) {
2168
+ $data[$i] = false;
2169
+ } else {
2170
+ $data[$i] = (double) $numerator / $denomninator;
2171
+ }
2172
+ }
2173
+ $offset += (8 * $data_size);
2174
+ if (count($data) == 1) {
2175
+ $data = $data[0];
2176
+ }
2177
+ break;
2178
+ case 0x0007: // 0x0007 = bytes (size field *= 1-byte), values are stored as ??????
2179
+ $data = substr($atom_data, $offset, $data_size * 1);
2180
+ $offset += ($data_size * 1);
2181
+ break;
2182
+ case 0x0008: // 0x0008 = ????? (size field *= 2-byte), values are stored as ??????
2183
+ $data = substr($atom_data, $offset, $data_size * 2);
2184
+ $offset += ($data_size * 2);
2185
+ break;
2186
+ default:
2187
+ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br>';
2188
+ break 2;
2189
+ }
2190
+
2191
+ switch ($record_type) {
2192
+ case 0x00000011: // CreateDate
2193
+ case 0x00000012: // DateTimeOriginal
2194
+ $data = strtotime($data);
2195
+ break;
2196
+ case 0x0200001e: // ColorSpace
2197
+ switch ($data) {
2198
+ case 1:
2199
+ $data = 'sRGB';
2200
+ break;
2201
+ case 2:
2202
+ $data = 'Adobe RGB';
2203
+ break;
2204
+ }
2205
+ break;
2206
+ case 0x02000023: // PictureControlData
2207
+ $PictureControlAdjust = array(0=>'default', 1=>'quick', 2=>'full');
2208
+ $FilterEffect = array(0x80=>'off', 0x81=>'yellow', 0x82=>'orange', 0x83=>'red', 0x84=>'green', 0xff=>'n/a');
2209
+ $ToningEffect = array(0x80=>'b&w', 0x81=>'sepia', 0x82=>'cyanotype', 0x83=>'red', 0x84=>'yellow', 0x85=>'green', 0x86=>'blue-green', 0x87=>'blue', 0x88=>'purple-blue', 0x89=>'red-purple', 0xff=>'n/a');
2210
+ $data = array(
2211
+ 'PictureControlVersion' => substr($data, 0, 4),
2212
+ 'PictureControlName' => rtrim(substr($data, 4, 20), "\x00"),
2213
+ 'PictureControlBase' => rtrim(substr($data, 24, 20), "\x00"),
2214
+ //'?' => substr($data, 44, 4),
2215
+ 'PictureControlAdjust' => $PictureControlAdjust[ord(substr($data, 48, 1))],
2216
+ 'PictureControlQuickAdjust' => ord(substr($data, 49, 1)),
2217
+ 'Sharpness' => ord(substr($data, 50, 1)),
2218
+ 'Contrast' => ord(substr($data, 51, 1)),
2219
+ 'Brightness' => ord(substr($data, 52, 1)),
2220
+ 'Saturation' => ord(substr($data, 53, 1)),
2221
+ 'HueAdjustment' => ord(substr($data, 54, 1)),
2222
+ 'FilterEffect' => $FilterEffect[ord(substr($data, 55, 1))],
2223
+ 'ToningEffect' => $ToningEffect[ord(substr($data, 56, 1))],
2224
+ 'ToningSaturation' => ord(substr($data, 57, 1)),
2225
+ );
2226
+ break;
2227
+ case 0x02000024: // WorldTime
2228
+ // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#WorldTime
2229
+ // timezone is stored as offset from GMT in minutes
2230
+ $timezone = getid3_lib::BigEndian2Int(substr($data, 0, 2));
2231
+ if ($timezone & 0x8000) {
2232
+ $timezone = 0 - (0x10000 - $timezone);
2233
+ }
2234
+ $timezone /= 60;
2235
+
2236
+ $dst = (bool) getid3_lib::BigEndian2Int(substr($data, 2, 1));
2237
+ switch (getid3_lib::BigEndian2Int(substr($data, 3, 1))) {
2238
+ case 2:
2239
+ $datedisplayformat = 'D/M/Y'; break;
2240
+ case 1:
2241
+ $datedisplayformat = 'M/D/Y'; break;
2242
+ case 0:
2243
+ default:
2244
+ $datedisplayformat = 'Y/M/D'; break;
2245
+ }
2246
+
2247
+ $data = array('timezone'=>floatval($timezone), 'dst'=>$dst, 'display'=>$datedisplayformat);
2248
+ break;
2249
+ case 0x02000083: // LensType
2250
+ $data = array(
2251
+ //'_' => $data,
2252
+ 'mf' => (bool) ($data & 0x01),
2253
+ 'd' => (bool) ($data & 0x02),
2254
+ 'g' => (bool) ($data & 0x04),
2255
+ 'vr' => (bool) ($data & 0x08),
2256
+ );
2257
+ break;
2258
+ }
2259
+ $tag_name = (isset($NCTGtagName[$record_type]) ? $NCTGtagName[$record_type] : '0x'.str_pad(dechex($record_type), 8, '0', STR_PAD_LEFT));
2260
+ $parsed[$tag_name] = $data;
2261
+ }
2262
+ return $parsed;
2263
+ }
2264
+
2265
+
2266
+ public function CopyToAppropriateCommentsSection($keyname, $data, $boxname='') {
2267
+ static $handyatomtranslatorarray = array();
2268
+ if (empty($handyatomtranslatorarray)) {
2269
+ // http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
2270
+ // http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
2271
+ // http://atomicparsley.sourceforge.net/mpeg-4files.html
2272
+ // https://code.google.com/p/mp4v2/wiki/iTunesMetadata
2273
+ $handyatomtranslatorarray["\xA9".'alb'] = 'album'; // iTunes 4.0
2274
+ $handyatomtranslatorarray["\xA9".'ART'] = 'artist';
2275
+ $handyatomtranslatorarray["\xA9".'art'] = 'artist'; // iTunes 4.0
2276
+ $handyatomtranslatorarray["\xA9".'aut'] = 'author';
2277
+ $handyatomtranslatorarray["\xA9".'cmt'] = 'comment'; // iTunes 4.0
2278
+ $handyatomtranslatorarray["\xA9".'com'] = 'comment';
2279
+ $handyatomtranslatorarray["\xA9".'cpy'] = 'copyright';
2280
+ $handyatomtranslatorarray["\xA9".'day'] = 'creation_date'; // iTunes 4.0
2281
+ $handyatomtranslatorarray["\xA9".'dir'] = 'director';
2282
+ $handyatomtranslatorarray["\xA9".'ed1'] = 'edit1';
2283
+ $handyatomtranslatorarray["\xA9".'ed2'] = 'edit2';
2284
+ $handyatomtranslatorarray["\xA9".'ed3'] = 'edit3';
2285
+ $handyatomtranslatorarray["\xA9".'ed4'] = 'edit4';
2286
+ $handyatomtranslatorarray["\xA9".'ed5'] = 'edit5';
2287
+ $handyatomtranslatorarray["\xA9".'ed6'] = 'edit6';
2288
+ $handyatomtranslatorarray["\xA9".'ed7'] = 'edit7';
2289
+ $handyatomtranslatorarray["\xA9".'ed8'] = 'edit8';
2290
+ $handyatomtranslatorarray["\xA9".'ed9'] = 'edit9';
2291
+ $handyatomtranslatorarray["\xA9".'enc'] = 'encoded_by';
2292
+ $handyatomtranslatorarray["\xA9".'fmt'] = 'format';
2293
+ $handyatomtranslatorarray["\xA9".'gen'] = 'genre'; // iTunes 4.0
2294
+ $handyatomtranslatorarray["\xA9".'grp'] = 'grouping'; // iTunes 4.2
2295
+ $handyatomtranslatorarray["\xA9".'hst'] = 'host_computer';
2296
+ $handyatomtranslatorarray["\xA9".'inf'] = 'information';
2297
+ $handyatomtranslatorarray["\xA9".'lyr'] = 'lyrics'; // iTunes 5.0
2298
+ $handyatomtranslatorarray["\xA9".'mak'] = 'make';
2299
+ $handyatomtranslatorarray["\xA9".'mod'] = 'model';
2300
+ $handyatomtranslatorarray["\xA9".'nam'] = 'title'; // iTunes 4.0
2301
+ $handyatomtranslatorarray["\xA9".'ope'] = 'composer';
2302
+ $handyatomtranslatorarray["\xA9".'prd'] = 'producer';
2303
+ $handyatomtranslatorarray["\xA9".'PRD'] = 'product';
2304
+ $handyatomtranslatorarray["\xA9".'prf'] = 'performers';
2305
+ $handyatomtranslatorarray["\xA9".'req'] = 'system_requirements';
2306
+ $handyatomtranslatorarray["\xA9".'src'] = 'source_credit';
2307
+ $handyatomtranslatorarray["\xA9".'swr'] = 'software';
2308
+ $handyatomtranslatorarray["\xA9".'too'] = 'encoding_tool'; // iTunes 4.0
2309
+ $handyatomtranslatorarray["\xA9".'trk'] = 'track';
2310
+ $handyatomtranslatorarray["\xA9".'url'] = 'url';
2311
+ $handyatomtranslatorarray["\xA9".'wrn'] = 'warning';
2312
+ $handyatomtranslatorarray["\xA9".'wrt'] = 'composer';
2313
+ $handyatomtranslatorarray['aART'] = 'album_artist';
2314
+ $handyatomtranslatorarray['apID'] = 'purchase_account';
2315
+ $handyatomtranslatorarray['catg'] = 'category'; // iTunes 4.9
2316
+ $handyatomtranslatorarray['covr'] = 'picture'; // iTunes 4.0
2317
+ $handyatomtranslatorarray['cpil'] = 'compilation'; // iTunes 4.0
2318
+ $handyatomtranslatorarray['cprt'] = 'copyright'; // iTunes 4.0?
2319
+ $handyatomtranslatorarray['desc'] = 'description'; // iTunes 5.0
2320
+ $handyatomtranslatorarray['disk'] = 'disc_number'; // iTunes 4.0
2321
+ $handyatomtranslatorarray['egid'] = 'episode_guid'; // iTunes 4.9
2322
+ $handyatomtranslatorarray['gnre'] = 'genre'; // iTunes 4.0
2323
+ $handyatomtranslatorarray['hdvd'] = 'hd_video'; // iTunes 4.0
2324
+ $handyatomtranslatorarray['ldes'] = 'description_long'; //
2325
+ $handyatomtranslatorarray['keyw'] = 'keyword'; // iTunes 4.9
2326
+ $handyatomtranslatorarray['pcst'] = 'podcast'; // iTunes 4.9
2327
+ $handyatomtranslatorarray['pgap'] = 'gapless_playback'; // iTunes 7.0
2328
+ $handyatomtranslatorarray['purd'] = 'purchase_date'; // iTunes 6.0.2
2329
+ $handyatomtranslatorarray['purl'] = 'podcast_url'; // iTunes 4.9
2330
+ $handyatomtranslatorarray['rtng'] = 'rating'; // iTunes 4.0
2331
+ $handyatomtranslatorarray['soaa'] = 'sort_album_artist'; //
2332
+ $handyatomtranslatorarray['soal'] = 'sort_album'; //
2333
+ $handyatomtranslatorarray['soar'] = 'sort_artist'; //
2334
+ $handyatomtranslatorarray['soco'] = 'sort_composer'; //
2335
+ $handyatomtranslatorarray['sonm'] = 'sort_title'; //
2336
+ $handyatomtranslatorarray['sosn'] = 'sort_show'; //
2337
+ $handyatomtranslatorarray['stik'] = 'stik'; // iTunes 4.9
2338
+ $handyatomtranslatorarray['tmpo'] = 'bpm'; // iTunes 4.0
2339
+ $handyatomtranslatorarray['trkn'] = 'track_number'; // iTunes 4.0
2340
+ $handyatomtranslatorarray['tven'] = 'tv_episode_id'; //
2341
+ $handyatomtranslatorarray['tves'] = 'tv_episode'; // iTunes 6.0
2342
+ $handyatomtranslatorarray['tvnn'] = 'tv_network_name'; // iTunes 6.0
2343
+ $handyatomtranslatorarray['tvsh'] = 'tv_show_name'; // iTunes 6.0
2344
+ $handyatomtranslatorarray['tvsn'] = 'tv_season'; // iTunes 6.0
2345
+
2346
+ // boxnames:
2347
+ /*
2348
+ $handyatomtranslatorarray['iTunSMPB'] = 'iTunSMPB';
2349
+ $handyatomtranslatorarray['iTunNORM'] = 'iTunNORM';
2350
+ $handyatomtranslatorarray['Encoding Params'] = 'Encoding Params';
2351
+ $handyatomtranslatorarray['replaygain_track_gain'] = 'replaygain_track_gain';
2352
+ $handyatomtranslatorarray['replaygain_track_peak'] = 'replaygain_track_peak';
2353
+ $handyatomtranslatorarray['replaygain_track_minmax'] = 'replaygain_track_minmax';
2354
+ $handyatomtranslatorarray['MusicIP PUID'] = 'MusicIP PUID';
2355
+ $handyatomtranslatorarray['MusicBrainz Artist Id'] = 'MusicBrainz Artist Id';
2356
+ $handyatomtranslatorarray['MusicBrainz Album Id'] = 'MusicBrainz Album Id';
2357
+ $handyatomtranslatorarray['MusicBrainz Album Artist Id'] = 'MusicBrainz Album Artist Id';
2358
+ $handyatomtranslatorarray['MusicBrainz Track Id'] = 'MusicBrainz Track Id';
2359
+ $handyatomtranslatorarray['MusicBrainz Disc Id'] = 'MusicBrainz Disc Id';
2360
+
2361
+ // http://age.hobba.nl/audio/tag_frame_reference.html
2362
+ $handyatomtranslatorarray['PLAY_COUNTER'] = 'play_counter'; // Foobar2000 - http://www.getid3.org/phpBB3/viewtopic.php?t=1355
2363
+ $handyatomtranslatorarray['MEDIATYPE'] = 'mediatype'; // Foobar2000 - http://www.getid3.org/phpBB3/viewtopic.php?t=1355
2364
+ */
2365
+ }
2366
+ $info = &$this->getid3->info;
2367
+ $comment_key = '';
2368
+ if ($boxname && ($boxname != $keyname)) {
2369
+ $comment_key = (isset($handyatomtranslatorarray[$boxname]) ? $handyatomtranslatorarray[$boxname] : $boxname);
2370
+ } elseif (isset($handyatomtranslatorarray[$keyname])) {
2371
+ $comment_key = $handyatomtranslatorarray[$keyname];
2372
+ }
2373
+ if ($comment_key) {
2374
+ if ($comment_key == 'picture') {
2375
+ if (!is_array($data)) {
2376
+ $image_mime = '';
2377
+ if (preg_match('#^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A#', $data)) {
2378
+ $image_mime = 'image/png';
2379
+ } elseif (preg_match('#^\xFF\xD8\xFF#', $data)) {
2380
+ $image_mime = 'image/jpeg';
2381
+ } elseif (preg_match('#^GIF#', $data)) {
2382
+ $image_mime = 'image/gif';
2383
+ } elseif (preg_match('#^BM#', $data)) {
2384
+ $image_mime = 'image/bmp';
2385
+ }
2386
+ $data = array('data'=>$data, 'image_mime'=>$image_mime);
2387
+ }
2388
+ }
2389
+ $gooddata = array($data);
2390
+ if ($comment_key == 'genre') {
2391
+ // some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal"
2392
+ $gooddata = explode(';', $data);
2393
+ }
2394
+ foreach ($gooddata as $data) {
2395
+ $info['quicktime']['comments'][$comment_key][] = $data;
2396
+ }
2397
+ }
2398
+ return true;
2399
+ }
2400
+
2401
+ public function NoNullString($nullterminatedstring) {
2402
+ // remove the single null terminator on null terminated strings
2403
+ if (substr($nullterminatedstring, strlen($nullterminatedstring) - 1, 1) === "\x00") {
2404
+ return substr($nullterminatedstring, 0, strlen($nullterminatedstring) - 1);
2405
+ }
2406
+ return $nullterminatedstring;
2407
+ }
2408
+
2409
+ public function Pascal2String($pascalstring) {
2410
+ // Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string
2411
+ return substr($pascalstring, 1);
2412
+ }
2413
+
2414
+
2415
+ /*
2416
+ // helper functions for m4b audiobook chapters
2417
+ // code by Steffen Hartmann 2015-Nov-08
2418
+ */
2419
+ public function search_tag_by_key($info, $tag, $history, &$result) {
2420
+ foreach ($info as $key => $value) {
2421
+ $key_history = $history.'/'.$key;
2422
+ if ($key === $tag) {
2423
+ $result[] = array($key_history, $info);
2424
+ } else {
2425
+ if (is_array($value)) {
2426
+ $this->search_tag_by_key($value, $tag, $key_history, $result);
2427
+ }
2428
+ }
2429
+ }
2430
+ }
2431
+
2432
+ public function search_tag_by_pair($info, $k, $v, $history, &$result) {
2433
+ foreach ($info as $key => $value) {
2434
+ $key_history = $history.'/'.$key;
2435
+ if (($key === $k) && ($value === $v)) {
2436
+ $result[] = array($key_history, $info);
2437
+ } else {
2438
+ if (is_array($value)) {
2439
+ $this->search_tag_by_pair($value, $k, $v, $key_history, $result);
2440
+ }
2441
+ }
2442
+ }
2443
+ }
2444
+
2445
+ public function quicktime_time_to_sample_table($info) {
2446
+ $res = array();
2447
+ $this->search_tag_by_pair($info['quicktime']['moov'], 'name', 'stbl', 'quicktime/moov', $res);
2448
+ foreach ($res as $value) {
2449
+ $stbl_res = array();
2450
+ $this->search_tag_by_pair($value[1], 'data_format', 'text', $value[0], $stbl_res);
2451
+ if (count($stbl_res) > 0) {
2452
+ $stts_res = array();
2453
+ $this->search_tag_by_key($value[1], 'time_to_sample_table', $value[0], $stts_res);
2454
+ if (count($stts_res) > 0) {
2455
+ return $stts_res[0][1]['time_to_sample_table'];
2456
+ }
2457
+ }
2458
+ }
2459
+ return array();
2460
+ }
2461
+
2462
+ function quicktime_bookmark_time_scale($info) {
2463
+ $time_scale = '';
2464
+ $ts_prefix_len = 0;
2465
+ $res = array();
2466
+ $this->search_tag_by_pair($info['quicktime']['moov'], 'name', 'stbl', 'quicktime/moov', $res);
2467
+ foreach ($res as $value) {
2468
+ $stbl_res = array();
2469
+ $this->search_tag_by_pair($value[1], 'data_format', 'text', $value[0], $stbl_res);
2470
+ if (count($stbl_res) > 0) {
2471
+ $ts_res = array();
2472
+ $this->search_tag_by_key($info['quicktime']['moov'], 'time_scale', 'quicktime/moov', $ts_res);
2473
+ foreach ($ts_res as $value) {
2474
+ $prefix = substr($value[0], 0, -12);
2475
+ if ((substr($stbl_res[0][0], 0, strlen($prefix)) === $prefix) && ($ts_prefix_len < strlen($prefix))) {
2476
+ $time_scale = $value[1]['time_scale'];
2477
+ $ts_prefix_len = strlen($prefix);
2478
+ }
2479
+ }
2480
+ }
2481
+ }
2482
+ return $time_scale;
2483
+ }
2484
+ /*
2485
+ // END helper functions for m4b audiobook chapters
2486
+ */
2487
+
2488
+
2489
+ }
getid3/module.audio-video.riff.php CHANGED
@@ -1,2586 +1,2593 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- // also https://github.com/JamesHeinrich/getID3 //
7
- /////////////////////////////////////////////////////////////////
8
- // See readme.txt for more details //
9
- /////////////////////////////////////////////////////////////////
10
- // //
11
- // module.audio-video.riff.php //
12
- // module for analyzing RIFF files //
13
- // multiple formats supported by this module: //
14
- // Wave, AVI, AIFF/AIFC, (MP3,AC3)/RIFF, Wavpack v3, 8SVX //
15
- // dependencies: module.audio.mp3.php //
16
- // module.audio.ac3.php //
17
- // module.audio.dts.php //
18
- // ///
19
- /////////////////////////////////////////////////////////////////
20
-
21
- /**
22
- * @todo Parse AC-3/DTS audio inside WAVE correctly
23
- * @todo Rewrite RIFF parser totally
24
- */
25
-
26
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
27
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
28
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true);
29
-
30
- class getid3_riff extends getid3_handler {
31
-
32
- protected $container = 'riff'; // default
33
-
34
- public function Analyze() {
35
- $info = &$this->getid3->info;
36
-
37
- // initialize these values to an empty array, otherwise they default to NULL
38
- // and you can't append array values to a NULL value
39
- $info['riff'] = array('raw'=>array());
40
-
41
- // Shortcuts
42
- $thisfile_riff = &$info['riff'];
43
- $thisfile_riff_raw = &$thisfile_riff['raw'];
44
- $thisfile_audio = &$info['audio'];
45
- $thisfile_video = &$info['video'];
46
- $thisfile_audio_dataformat = &$thisfile_audio['dataformat'];
47
- $thisfile_riff_audio = &$thisfile_riff['audio'];
48
- $thisfile_riff_video = &$thisfile_riff['video'];
49
-
50
- $Original['avdataoffset'] = $info['avdataoffset'];
51
- $Original['avdataend'] = $info['avdataend'];
52
-
53
- $this->fseek($info['avdataoffset']);
54
- $RIFFheader = $this->fread(12);
55
- $offset = $this->ftell();
56
- $RIFFtype = substr($RIFFheader, 0, 4);
57
- $RIFFsize = substr($RIFFheader, 4, 4);
58
- $RIFFsubtype = substr($RIFFheader, 8, 4);
59
-
60
- switch ($RIFFtype) {
61
-
62
- case 'FORM': // AIFF, AIFC
63
- //$info['fileformat'] = 'aiff';
64
- $this->container = 'aiff';
65
- $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
66
- $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4));
67
- break;
68
-
69
- case 'RIFF': // AVI, WAV, etc
70
- case 'SDSS': // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com)
71
- case 'RMP3': // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s
72
- //$info['fileformat'] = 'riff';
73
- $this->container = 'riff';
74
- $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
75
- if ($RIFFsubtype == 'RMP3') {
76
- // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s
77
- $RIFFsubtype = 'WAVE';
78
- }
79
- if ($RIFFsubtype != 'AMV ') {
80
- // AMV files are RIFF-AVI files with parts of the spec deliberately broken, such as chunk size fields hardcoded to zero (because players known in hardware that these fields are always a certain size
81
- // Handled separately in ParseRIFFAMV()
82
- $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4));
83
- }
84
- if (($info['avdataend'] - $info['filesize']) == 1) {
85
- // LiteWave appears to incorrectly *not* pad actual output file
86
- // to nearest WORD boundary so may appear to be short by one
87
- // byte, in which case - skip warning
88
- $info['avdataend'] = $info['filesize'];
89
- }
90
-
91
- $nextRIFFoffset = $Original['avdataoffset'] + 8 + $thisfile_riff['header_size']; // 8 = "RIFF" + 32-bit offset
92
- while ($nextRIFFoffset < min($info['filesize'], $info['avdataend'])) {
93
- try {
94
- $this->fseek($nextRIFFoffset);
95
- } catch (getid3_exception $e) {
96
- if ($e->getCode() == 10) {
97
- //$this->warning('RIFF parser: '.$e->getMessage());
98
- $this->error('AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime may be wrong');
99
- $this->warning('[avdataend] value may be incorrect, multiple AVIX chunks may be present');
100
- break;
101
- } else {
102
- throw $e;
103
- }
104
- }
105
- $nextRIFFheader = $this->fread(12);
106
- if ($nextRIFFoffset == ($info['avdataend'] - 1)) {
107
- if (substr($nextRIFFheader, 0, 1) == "\x00") {
108
- // RIFF padded to WORD boundary, we're actually already at the end
109
- break;
110
- }
111
- }
112
- $nextRIFFheaderID = substr($nextRIFFheader, 0, 4);
113
- $nextRIFFsize = $this->EitherEndian2Int(substr($nextRIFFheader, 4, 4));
114
- $nextRIFFtype = substr($nextRIFFheader, 8, 4);
115
- $chunkdata = array();
116
- $chunkdata['offset'] = $nextRIFFoffset + 8;
117
- $chunkdata['size'] = $nextRIFFsize;
118
- $nextRIFFoffset = $chunkdata['offset'] + $chunkdata['size'];
119
-
120
- switch ($nextRIFFheaderID) {
121
- case 'RIFF':
122
- $chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $nextRIFFoffset);
123
- if (!isset($thisfile_riff[$nextRIFFtype])) {
124
- $thisfile_riff[$nextRIFFtype] = array();
125
- }
126
- $thisfile_riff[$nextRIFFtype][] = $chunkdata;
127
- break;
128
-
129
- case 'AMV ':
130
- unset($info['riff']);
131
- $info['amv'] = $this->ParseRIFFAMV($chunkdata['offset'] + 4, $nextRIFFoffset);
132
- break;
133
-
134
- case 'JUNK':
135
- // ignore
136
- $thisfile_riff[$nextRIFFheaderID][] = $chunkdata;
137
- break;
138
-
139
- case 'IDVX':
140
- $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunkdata['size']));
141
- break;
142
-
143
- default:
144
- if ($info['filesize'] == ($chunkdata['offset'] - 8 + 128)) {
145
- $DIVXTAG = $nextRIFFheader.$this->fread(128 - 12);
146
- if (substr($DIVXTAG, -7) == 'DIVXTAG') {
147
- // DIVXTAG is supposed to be inside an IDVX chunk in a LIST chunk, but some bad encoders just slap it on the end of a file
148
- $this->warning('Found wrongly-structured DIVXTAG at offset '.($this->ftell() - 128).', parsing anyway');
149
- $info['divxtag']['comments'] = self::ParseDIVXTAG($DIVXTAG);
150
- break 2;
151
- }
152
- }
153
- $this->warning('Expecting "RIFF|JUNK|IDVX" at '.$nextRIFFoffset.', found "'.$nextRIFFheaderID.'" ('.getid3_lib::PrintHexBytes($nextRIFFheaderID).') - skipping rest of file');
154
- break 2;
155
-
156
- }
157
-
158
- }
159
- if ($RIFFsubtype == 'WAVE') {
160
- $thisfile_riff_WAVE = &$thisfile_riff['WAVE'];
161
- }
162
- break;
163
-
164
- default:
165
- $this->error('Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "'.$RIFFsubtype.'" instead');
166
- //unset($info['fileformat']);
167
- return false;
168
- }
169
-
170
- $streamindex = 0;
171
- switch ($RIFFsubtype) {
172
-
173
- // http://en.wikipedia.org/wiki/Wav
174
- case 'WAVE':
175
- $info['fileformat'] = 'wav';
176
-
177
- if (empty($thisfile_audio['bitrate_mode'])) {
178
- $thisfile_audio['bitrate_mode'] = 'cbr';
179
- }
180
- if (empty($thisfile_audio_dataformat)) {
181
- $thisfile_audio_dataformat = 'wav';
182
- }
183
-
184
- if (isset($thisfile_riff_WAVE['data'][0]['offset'])) {
185
- $info['avdataoffset'] = $thisfile_riff_WAVE['data'][0]['offset'] + 8;
186
- $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff_WAVE['data'][0]['size'];
187
- }
188
- if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) {
189
-
190
- $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']);
191
- $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
192
- if (!isset($thisfile_riff_audio[$streamindex]['bitrate']) || ($thisfile_riff_audio[$streamindex]['bitrate'] == 0)) {
193
- $info['error'][] = 'Corrupt RIFF file: bitrate_audio == zero';
194
- return false;
195
- }
196
- $thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw'];
197
- unset($thisfile_riff_audio[$streamindex]['raw']);
198
- $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
199
-
200
- $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
201
- if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') {
202
- $info['warning'][] = 'Audio codec = '.$thisfile_audio['codec'];
203
- }
204
- $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
205
-
206
- if (empty($info['playtime_seconds'])) { // may already be set (e.g. DTS-WAV)
207
- $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
208
- }
209
-
210
- $thisfile_audio['lossless'] = false;
211
- if (isset($thisfile_riff_WAVE['data'][0]['offset']) && isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
212
- switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
213
-
214
- case 0x0001: // PCM
215
- $thisfile_audio['lossless'] = true;
216
- break;
217
-
218
- case 0x2000: // AC-3
219
- $thisfile_audio_dataformat = 'ac3';
220
- break;
221
-
222
- default:
223
- // do nothing
224
- break;
225
-
226
- }
227
- }
228
- $thisfile_audio['streams'][$streamindex]['wformattag'] = $thisfile_audio['wformattag'];
229
- $thisfile_audio['streams'][$streamindex]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
230
- $thisfile_audio['streams'][$streamindex]['lossless'] = $thisfile_audio['lossless'];
231
- $thisfile_audio['streams'][$streamindex]['dataformat'] = $thisfile_audio_dataformat;
232
- }
233
-
234
- if (isset($thisfile_riff_WAVE['rgad'][0]['data'])) {
235
-
236
- // shortcuts
237
- $rgadData = &$thisfile_riff_WAVE['rgad'][0]['data'];
238
- $thisfile_riff_raw['rgad'] = array('track'=>array(), 'album'=>array());
239
- $thisfile_riff_raw_rgad = &$thisfile_riff_raw['rgad'];
240
- $thisfile_riff_raw_rgad_track = &$thisfile_riff_raw_rgad['track'];
241
- $thisfile_riff_raw_rgad_album = &$thisfile_riff_raw_rgad['album'];
242
-
243
- $thisfile_riff_raw_rgad['fPeakAmplitude'] = getid3_lib::LittleEndian2Float(substr($rgadData, 0, 4));
244
- $thisfile_riff_raw_rgad['nRadioRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 4, 2));
245
- $thisfile_riff_raw_rgad['nAudiophileRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 6, 2));
246
-
247
- $nRadioRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT);
248
- $nAudiophileRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT);
249
- $thisfile_riff_raw_rgad_track['name'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3));
250
- $thisfile_riff_raw_rgad_track['originator'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3));
251
- $thisfile_riff_raw_rgad_track['signbit'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1));
252
- $thisfile_riff_raw_rgad_track['adjustment'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9));
253
- $thisfile_riff_raw_rgad_album['name'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3));
254
- $thisfile_riff_raw_rgad_album['originator'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3));
255
- $thisfile_riff_raw_rgad_album['signbit'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1));
256
- $thisfile_riff_raw_rgad_album['adjustment'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9));
257
-
258
- $thisfile_riff['rgad']['peakamplitude'] = $thisfile_riff_raw_rgad['fPeakAmplitude'];
259
- if (($thisfile_riff_raw_rgad_track['name'] != 0) && ($thisfile_riff_raw_rgad_track['originator'] != 0)) {
260
- $thisfile_riff['rgad']['track']['name'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_track['name']);
261
- $thisfile_riff['rgad']['track']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_track['originator']);
262
- $thisfile_riff['rgad']['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_track['adjustment'], $thisfile_riff_raw_rgad_track['signbit']);
263
- }
264
- if (($thisfile_riff_raw_rgad_album['name'] != 0) && ($thisfile_riff_raw_rgad_album['originator'] != 0)) {
265
- $thisfile_riff['rgad']['album']['name'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_album['name']);
266
- $thisfile_riff['rgad']['album']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_album['originator']);
267
- $thisfile_riff['rgad']['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_album['adjustment'], $thisfile_riff_raw_rgad_album['signbit']);
268
- }
269
- }
270
-
271
- if (isset($thisfile_riff_WAVE['fact'][0]['data'])) {
272
- $thisfile_riff_raw['fact']['NumberOfSamples'] = $this->EitherEndian2Int(substr($thisfile_riff_WAVE['fact'][0]['data'], 0, 4));
273
-
274
- // This should be a good way of calculating exact playtime,
275
- // but some sample files have had incorrect number of samples,
276
- // so cannot use this method
277
-
278
- // if (!empty($thisfile_riff_raw['fmt ']['nSamplesPerSec'])) {
279
- // $info['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec'];
280
- // }
281
- }
282
- if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) {
283
- $thisfile_audio['bitrate'] = getid3_lib::CastAsInt($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'] * 8);
284
- }
285
-
286
- if (isset($thisfile_riff_WAVE['bext'][0]['data'])) {
287
- // shortcut
288
- $thisfile_riff_WAVE_bext_0 = &$thisfile_riff_WAVE['bext'][0];
289
-
290
- $thisfile_riff_WAVE_bext_0['title'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 0, 256));
291
- $thisfile_riff_WAVE_bext_0['author'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 256, 32));
292
- $thisfile_riff_WAVE_bext_0['reference'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 288, 32));
293
- $thisfile_riff_WAVE_bext_0['origin_date'] = substr($thisfile_riff_WAVE_bext_0['data'], 320, 10);
294
- $thisfile_riff_WAVE_bext_0['origin_time'] = substr($thisfile_riff_WAVE_bext_0['data'], 330, 8);
295
- $thisfile_riff_WAVE_bext_0['time_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338, 8));
296
- $thisfile_riff_WAVE_bext_0['bwf_version'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 346, 1));
297
- $thisfile_riff_WAVE_bext_0['reserved'] = substr($thisfile_riff_WAVE_bext_0['data'], 347, 254);
298
- $thisfile_riff_WAVE_bext_0['coding_history'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_bext_0['data'], 601)));
299
- if (preg_match('#^([0-9]{4}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_date'], $matches_bext_date)) {
300
- if (preg_match('#^([0-9]{2}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_time'], $matches_bext_time)) {
301
- list($dummy, $bext_timestamp['year'], $bext_timestamp['month'], $bext_timestamp['day']) = $matches_bext_date;
302
- list($dummy, $bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second']) = $matches_bext_time;
303
- $thisfile_riff_WAVE_bext_0['origin_date_unix'] = gmmktime($bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second'], $bext_timestamp['month'], $bext_timestamp['day'], $bext_timestamp['year']);
304
- } else {
305
- $info['warning'][] = 'RIFF.WAVE.BEXT.origin_time is invalid';
306
- }
307
- } else {
308
- $info['warning'][] = 'RIFF.WAVE.BEXT.origin_date is invalid';
309
- }
310
- $thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author'];
311
- $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_bext_0['title'];
312
- }
313
-
314
- if (isset($thisfile_riff_WAVE['MEXT'][0]['data'])) {
315
- // shortcut
316
- $thisfile_riff_WAVE_MEXT_0 = &$thisfile_riff_WAVE['MEXT'][0];
317
-
318
- $thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 0, 2));
319
- $thisfile_riff_WAVE_MEXT_0['flags']['homogenous'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0001);
320
- if ($thisfile_riff_WAVE_MEXT_0['flags']['homogenous']) {
321
- $thisfile_riff_WAVE_MEXT_0['flags']['padding'] = ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0002) ? false : true;
322
- $thisfile_riff_WAVE_MEXT_0['flags']['22_or_44'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0004);
323
- $thisfile_riff_WAVE_MEXT_0['flags']['free_format'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0008);
324
-
325
- $thisfile_riff_WAVE_MEXT_0['nominal_frame_size'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2));
326
- }
327
- $thisfile_riff_WAVE_MEXT_0['anciliary_data_length'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 6, 2));
328
- $thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 8, 2));
329
- $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_left'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0001);
330
- $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_free'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0002);
331
- $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_right'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0004);
332
- }
333
-
334
- if (isset($thisfile_riff_WAVE['cart'][0]['data'])) {
335
- // shortcut
336
- $thisfile_riff_WAVE_cart_0 = &$thisfile_riff_WAVE['cart'][0];
337
-
338
- $thisfile_riff_WAVE_cart_0['version'] = substr($thisfile_riff_WAVE_cart_0['data'], 0, 4);
339
- $thisfile_riff_WAVE_cart_0['title'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 4, 64));
340
- $thisfile_riff_WAVE_cart_0['artist'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 68, 64));
341
- $thisfile_riff_WAVE_cart_0['cut_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 132, 64));
342
- $thisfile_riff_WAVE_cart_0['client_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 196, 64));
343
- $thisfile_riff_WAVE_cart_0['category'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 260, 64));
344
- $thisfile_riff_WAVE_cart_0['classification'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 324, 64));
345
- $thisfile_riff_WAVE_cart_0['out_cue'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 388, 64));
346
- $thisfile_riff_WAVE_cart_0['start_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 452, 10));
347
- $thisfile_riff_WAVE_cart_0['start_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 462, 8));
348
- $thisfile_riff_WAVE_cart_0['end_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 470, 10));
349
- $thisfile_riff_WAVE_cart_0['end_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 480, 8));
350
- $thisfile_riff_WAVE_cart_0['producer_app_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 488, 64));
351
- $thisfile_riff_WAVE_cart_0['producer_app_version'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 552, 64));
352
- $thisfile_riff_WAVE_cart_0['user_defined_text'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 616, 64));
353
- $thisfile_riff_WAVE_cart_0['zero_db_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 680, 4), true);
354
- for ($i = 0; $i < 8; $i++) {
355
- $thisfile_riff_WAVE_cart_0['post_time'][$i]['usage_fourcc'] = substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8), 4);
356
- $thisfile_riff_WAVE_cart_0['post_time'][$i]['timer_value'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8) + 4, 4));
357
- }
358
- $thisfile_riff_WAVE_cart_0['url'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 748, 1024));
359
- $thisfile_riff_WAVE_cart_0['tag_text'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772)));
360
-
361
- $thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist'];
362
- $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_cart_0['title'];
363
- }
364
-
365
- if (isset($thisfile_riff_WAVE['SNDM'][0]['data'])) {
366
- // SoundMiner metadata
367
-
368
- // shortcuts
369
- $thisfile_riff_WAVE_SNDM_0 = &$thisfile_riff_WAVE['SNDM'][0];
370
- $thisfile_riff_WAVE_SNDM_0_data = &$thisfile_riff_WAVE_SNDM_0['data'];
371
- $SNDM_startoffset = 0;
372
- $SNDM_endoffset = $thisfile_riff_WAVE_SNDM_0['size'];
373
-
374
- while ($SNDM_startoffset < $SNDM_endoffset) {
375
- $SNDM_thisTagOffset = 0;
376
- $SNDM_thisTagSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4));
377
- $SNDM_thisTagOffset += 4;
378
- $SNDM_thisTagKey = substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4);
379
- $SNDM_thisTagOffset += 4;
380
- $SNDM_thisTagDataSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
381
- $SNDM_thisTagOffset += 2;
382
- $SNDM_thisTagDataFlags = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
383
- $SNDM_thisTagOffset += 2;
384
- $SNDM_thisTagDataText = substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, $SNDM_thisTagDataSize);
385
- $SNDM_thisTagOffset += $SNDM_thisTagDataSize;
386
-
387
- if ($SNDM_thisTagSize != (4 + 4 + 2 + 2 + $SNDM_thisTagDataSize)) {
388
- $info['warning'][] = 'RIFF.WAVE.SNDM.data contains tag not expected length (expected: '.$SNDM_thisTagSize.', found: '.(4 + 4 + 2 + 2 + $SNDM_thisTagDataSize).') at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
389
- break;
390
- } elseif ($SNDM_thisTagSize <= 0) {
391
- $info['warning'][] = 'RIFF.WAVE.SNDM.data contains zero-size tag at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
392
- break;
393
- }
394
- $SNDM_startoffset += $SNDM_thisTagSize;
395
-
396
- $thisfile_riff_WAVE_SNDM_0['parsed_raw'][$SNDM_thisTagKey] = $SNDM_thisTagDataText;
397
- if ($parsedkey = self::waveSNDMtagLookup($SNDM_thisTagKey)) {
398
- $thisfile_riff_WAVE_SNDM_0['parsed'][$parsedkey] = $SNDM_thisTagDataText;
399
- } else {
400
- $info['warning'][] = 'RIFF.WAVE.SNDM contains unknown tag "'.$SNDM_thisTagKey.'" at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
401
- }
402
- }
403
-
404
- $tagmapping = array(
405
- 'tracktitle'=>'title',
406
- 'category' =>'genre',
407
- 'cdtitle' =>'album',
408
- 'tracktitle'=>'title',
409
- );
410
- foreach ($tagmapping as $fromkey => $tokey) {
411
- if (isset($thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey])) {
412
- $thisfile_riff['comments'][$tokey][] = $thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey];
413
- }
414
- }
415
- }
416
-
417
- if (isset($thisfile_riff_WAVE['iXML'][0]['data'])) {
418
- // requires functions simplexml_load_string and get_object_vars
419
- if ($parsedXML = getid3_lib::XML2array($thisfile_riff_WAVE['iXML'][0]['data'])) {
420
- $thisfile_riff_WAVE['iXML'][0]['parsed'] = $parsedXML;
421
- if (isset($parsedXML['SPEED']['MASTER_SPEED'])) {
422
- @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['MASTER_SPEED']);
423
- $thisfile_riff_WAVE['iXML'][0]['master_speed'] = $numerator / ($denominator ? $denominator : 1000);
424
- }
425
- if (isset($parsedXML['SPEED']['TIMECODE_RATE'])) {
426
- @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['TIMECODE_RATE']);
427
- $thisfile_riff_WAVE['iXML'][0]['timecode_rate'] = $numerator / ($denominator ? $denominator : 1000);
428
- }
429
- if (isset($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO']) && !empty($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) && !empty($thisfile_riff_WAVE['iXML'][0]['timecode_rate'])) {
430
- $samples_since_midnight = floatval(ltrim($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI'].$parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO'], '0'));
431
- $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] = $samples_since_midnight / $parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE'];
432
- $h = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] / 3600);
433
- $m = floor(($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600)) / 60);
434
- $s = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60));
435
- $f = ($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60) - $s) * $thisfile_riff_WAVE['iXML'][0]['timecode_rate'];
436
- $thisfile_riff_WAVE['iXML'][0]['timecode_string'] = sprintf('%02d:%02d:%02d:%05.2f', $h, $m, $s, $f);
437
- $thisfile_riff_WAVE['iXML'][0]['timecode_string_round'] = sprintf('%02d:%02d:%02d:%02d', $h, $m, $s, round($f));
438
- }
439
- unset($parsedXML);
440
- }
441
- }
442
-
443
-
444
-
445
- if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) {
446
- $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
447
- $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
448
- }
449
-
450
- if (!empty($info['wavpack'])) {
451
- $thisfile_audio_dataformat = 'wavpack';
452
- $thisfile_audio['bitrate_mode'] = 'vbr';
453
- $thisfile_audio['encoder'] = 'WavPack v'.$info['wavpack']['version'];
454
-
455
- // Reset to the way it was - RIFF parsing will have messed this up
456
- $info['avdataend'] = $Original['avdataend'];
457
- $thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
458
-
459
- $this->fseek($info['avdataoffset'] - 44);
460
- $RIFFdata = $this->fread(44);
461
- $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8;
462
- $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
463
-
464
- if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
465
- $info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
466
- $this->fseek($info['avdataend']);
467
- $RIFFdata .= $this->fread($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
468
- }
469
-
470
- // move the data chunk after all other chunks (if any)
471
- // so that the RIFF parser doesn't see EOF when trying
472
- // to skip over the data chunk
473
- $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
474
- $getid3_riff = new getid3_riff($this->getid3);
475
- $getid3_riff->ParseRIFFdata($RIFFdata);
476
- unset($getid3_riff);
477
- }
478
-
479
- if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
480
- switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
481
- case 0x0001: // PCM
482
- if (!empty($info['ac3'])) {
483
- // Dolby Digital WAV files masquerade as PCM-WAV, but they're not
484
- $thisfile_audio['wformattag'] = 0x2000;
485
- $thisfile_audio['codec'] = self::wFormatTagLookup($thisfile_audio['wformattag']);
486
- $thisfile_audio['lossless'] = false;
487
- $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
488
- $thisfile_audio['sample_rate'] = $info['ac3']['sample_rate'];
489
- }
490
- if (!empty($info['dts'])) {
491
- // Dolby DTS files masquerade as PCM-WAV, but they're not
492
- $thisfile_audio['wformattag'] = 0x2001;
493
- $thisfile_audio['codec'] = self::wFormatTagLookup($thisfile_audio['wformattag']);
494
- $thisfile_audio['lossless'] = false;
495
- $thisfile_audio['bitrate'] = $info['dts']['bitrate'];
496
- $thisfile_audio['sample_rate'] = $info['dts']['sample_rate'];
497
- }
498
- break;
499
- case 0x08AE: // ClearJump LiteWave
500
- $thisfile_audio['bitrate_mode'] = 'vbr';
501
- $thisfile_audio_dataformat = 'litewave';
502
-
503
- //typedef struct tagSLwFormat {
504
- // WORD m_wCompFormat; // low byte defines compression method, high byte is compression flags
505
- // DWORD m_dwScale; // scale factor for lossy compression
506
- // DWORD m_dwBlockSize; // number of samples in encoded blocks
507
- // WORD m_wQuality; // alias for the scale factor
508
- // WORD m_wMarkDistance; // distance between marks in bytes
509
- // WORD m_wReserved;
510
- //
511
- // //following paramters are ignored if CF_FILESRC is not set
512
- // DWORD m_dwOrgSize; // original file size in bytes
513
- // WORD m_bFactExists; // indicates if 'fact' chunk exists in the original file
514
- // DWORD m_dwRiffChunkSize; // riff chunk size in the original file
515
- //
516
- // PCMWAVEFORMAT m_OrgWf; // original wave format
517
- // }SLwFormat, *PSLwFormat;
518
-
519
- // shortcut
520
- $thisfile_riff['litewave']['raw'] = array();
521
- $riff_litewave = &$thisfile_riff['litewave'];
522
- $riff_litewave_raw = &$riff_litewave['raw'];
523
-
524
- $flags = array(
525
- 'compression_method' => 1,
526
- 'compression_flags' => 1,
527
- 'm_dwScale' => 4,
528
- 'm_dwBlockSize' => 4,
529
- 'm_wQuality' => 2,
530
- 'm_wMarkDistance' => 2,
531
- 'm_wReserved' => 2,
532
- 'm_dwOrgSize' => 4,
533
- 'm_bFactExists' => 2,
534
- 'm_dwRiffChunkSize' => 4,
535
- );
536
- $litewave_offset = 18;
537
- foreach ($flags as $flag => $length) {
538
- $riff_litewave_raw[$flag] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], $litewave_offset, $length));
539
- $litewave_offset += $length;
540
- }
541
-
542
- //$riff_litewave['quality_factor'] = intval(round((2000 - $riff_litewave_raw['m_dwScale']) / 20));
543
- $riff_litewave['quality_factor'] = $riff_litewave_raw['m_wQuality'];
544
-
545
- $riff_litewave['flags']['raw_source'] = ($riff_litewave_raw['compression_flags'] & 0x01) ? false : true;
546
- $riff_litewave['flags']['vbr_blocksize'] = ($riff_litewave_raw['compression_flags'] & 0x02) ? false : true;
547
- $riff_litewave['flags']['seekpoints'] = (bool) ($riff_litewave_raw['compression_flags'] & 0x04);
548
-
549
- $thisfile_audio['lossless'] = (($riff_litewave_raw['m_wQuality'] == 100) ? true : false);
550
- $thisfile_audio['encoder_options'] = '-q'.$riff_litewave['quality_factor'];
551
- break;
552
-
553
- default:
554
- break;
555
- }
556
- }
557
- if ($info['avdataend'] > $info['filesize']) {
558
- switch (!empty($thisfile_audio_dataformat) ? $thisfile_audio_dataformat : '') {
559
- case 'wavpack': // WavPack
560
- case 'lpac': // LPAC
561
- case 'ofr': // OptimFROG
562
- case 'ofs': // OptimFROG DualStream
563
- // lossless compressed audio formats that keep original RIFF headers - skip warning
564
- break;
565
-
566
- case 'litewave':
567
- if (($info['avdataend'] - $info['filesize']) == 1) {
568
- // LiteWave appears to incorrectly *not* pad actual output file
569
- // to nearest WORD boundary so may appear to be short by one
570
- // byte, in which case - skip warning
571
- } else {
572
- // Short by more than one byte, throw warning
573
- $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
574
- $info['avdataend'] = $info['filesize'];
575
- }
576
- break;
577
-
578
- default:
579
- if ((($info['avdataend'] - $info['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($info['filesize'] - $info['avdataoffset']) % 2) == 1)) {
580
- // output file appears to be incorrectly *not* padded to nearest WORD boundary
581
- // Output less severe warning
582
- $info['warning'][] = 'File should probably be padded to nearest WORD boundary, but it is not (expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' therefore short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
583
- $info['avdataend'] = $info['filesize'];
584
- } else {
585
- // Short by more than one byte, throw warning
586
- $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
587
- $info['avdataend'] = $info['filesize'];
588
- }
589
- break;
590
- }
591
- }
592
- if (!empty($info['mpeg']['audio']['LAME']['audio_bytes'])) {
593
- if ((($info['avdataend'] - $info['avdataoffset']) - $info['mpeg']['audio']['LAME']['audio_bytes']) == 1) {
594
- $info['avdataend']--;
595
- $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
596
- }
597
- }
598
- if (isset($thisfile_audio_dataformat) && ($thisfile_audio_dataformat == 'ac3')) {
599
- unset($thisfile_audio['bits_per_sample']);
600
- if (!empty($info['ac3']['bitrate']) && ($info['ac3']['bitrate'] != $thisfile_audio['bitrate'])) {
601
- $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
602
- }
603
- }
604
- break;
605
-
606
- // http://en.wikipedia.org/wiki/Audio_Video_Interleave
607
- case 'AVI ':
608
- $info['fileformat'] = 'avi';
609
- $info['mime_type'] = 'video/avi';
610
-
611
- $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably
612
- $thisfile_video['dataformat'] = 'avi';
613
-
614
- if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) {
615
- $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8;
616
- if (isset($thisfile_riff['AVIX'])) {
617
- $info['avdataend'] = $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['offset'] + $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['size'];
618
- } else {
619
- $info['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size'];
620
- }
621
- if ($info['avdataend'] > $info['filesize']) {
622
- $info['warning'][] = 'Probably truncated file - expecting '.($info['avdataend'] - $info['avdataoffset']).' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($info['avdataend'] - $info['filesize']).' bytes)';
623
- $info['avdataend'] = $info['filesize'];
624
- }
625
- }
626
-
627
- if (isset($thisfile_riff['AVI ']['hdrl']['strl']['indx'])) {
628
- //$bIndexType = array(
629
- // 0x00 => 'AVI_INDEX_OF_INDEXES',
630
- // 0x01 => 'AVI_INDEX_OF_CHUNKS',
631
- // 0x80 => 'AVI_INDEX_IS_DATA',
632
- //);
633
- //$bIndexSubtype = array(
634
- // 0x01 => array(
635
- // 0x01 => 'AVI_INDEX_2FIELD',
636
- // ),
637
- //);
638
- foreach ($thisfile_riff['AVI ']['hdrl']['strl']['indx'] as $streamnumber => $steamdataarray) {
639
- $ahsisd = &$thisfile_riff['AVI ']['hdrl']['strl']['indx'][$streamnumber]['data'];
640
-
641
- $thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = $this->EitherEndian2Int(substr($ahsisd, 0, 2));
642
- $thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType'] = $this->EitherEndian2Int(substr($ahsisd, 2, 1));
643
- $thisfile_riff_raw['indx'][$streamnumber]['bIndexType'] = $this->EitherEndian2Int(substr($ahsisd, 3, 1));
644
- $thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse'] = $this->EitherEndian2Int(substr($ahsisd, 4, 4));
645
- $thisfile_riff_raw['indx'][$streamnumber]['dwChunkId'] = substr($ahsisd, 8, 4);
646
- $thisfile_riff_raw['indx'][$streamnumber]['dwReserved'] = $this->EitherEndian2Int(substr($ahsisd, 12, 4));
647
-
648
- //$thisfile_riff_raw['indx'][$streamnumber]['bIndexType_name'] = $bIndexType[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']];
649
- //$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType_name'] = $bIndexSubtype[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']][$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']];
650
-
651
- unset($ahsisd);
652
- }
653
- }
654
- if (isset($thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'])) {
655
- $avihData = $thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'];
656
-
657
- // shortcut
658
- $thisfile_riff_raw['avih'] = array();
659
- $thisfile_riff_raw_avih = &$thisfile_riff_raw['avih'];
660
-
661
- $thisfile_riff_raw_avih['dwMicroSecPerFrame'] = $this->EitherEndian2Int(substr($avihData, 0, 4)); // frame display rate (or 0L)
662
- if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) {
663
- $info['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero';
664
- return false;
665
- }
666
-
667
- $flags = array(
668
- 'dwMaxBytesPerSec', // max. transfer rate
669
- 'dwPaddingGranularity', // pad to multiples of this size; normally 2K.
670
- 'dwFlags', // the ever-present flags
671
- 'dwTotalFrames', // # frames in file
672
- 'dwInitialFrames', //
673
- 'dwStreams', //
674
- 'dwSuggestedBufferSize', //
675
- 'dwWidth', //
676
- 'dwHeight', //
677
- 'dwScale', //
678
- 'dwRate', //
679
- 'dwStart', //
680
- 'dwLength', //
681
- );
682
- $avih_offset = 4;
683
- foreach ($flags as $flag) {
684
- $thisfile_riff_raw_avih[$flag] = $this->EitherEndian2Int(substr($avihData, $avih_offset, 4));
685
- $avih_offset += 4;
686
- }
687
-
688
- $flags = array(
689
- 'hasindex' => 0x00000010,
690
- 'mustuseindex' => 0x00000020,
691
- 'interleaved' => 0x00000100,
692
- 'trustcktype' => 0x00000800,
693
- 'capturedfile' => 0x00010000,
694
- 'copyrighted' => 0x00020010,
695
- );
696
- foreach ($flags as $flag => $value) {
697
- $thisfile_riff_raw_avih['flags'][$flag] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & $value);
698
- }
699
-
700
- // shortcut
701
- $thisfile_riff_video[$streamindex] = array();
702
- $thisfile_riff_video_current = &$thisfile_riff_video[$streamindex];
703
-
704
- if ($thisfile_riff_raw_avih['dwWidth'] > 0) {
705
- $thisfile_riff_video_current['frame_width'] = $thisfile_riff_raw_avih['dwWidth'];
706
- $thisfile_video['resolution_x'] = $thisfile_riff_video_current['frame_width'];
707
- }
708
- if ($thisfile_riff_raw_avih['dwHeight'] > 0) {
709
- $thisfile_riff_video_current['frame_height'] = $thisfile_riff_raw_avih['dwHeight'];
710
- $thisfile_video['resolution_y'] = $thisfile_riff_video_current['frame_height'];
711
- }
712
- if ($thisfile_riff_raw_avih['dwTotalFrames'] > 0) {
713
- $thisfile_riff_video_current['total_frames'] = $thisfile_riff_raw_avih['dwTotalFrames'];
714
- $thisfile_video['total_frames'] = $thisfile_riff_video_current['total_frames'];
715
- }
716
-
717
- $thisfile_riff_video_current['frame_rate'] = round(1000000 / $thisfile_riff_raw_avih['dwMicroSecPerFrame'], 3);
718
- $thisfile_video['frame_rate'] = $thisfile_riff_video_current['frame_rate'];
719
- }
720
- if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][0]['data'])) {
721
- if (is_array($thisfile_riff['AVI ']['hdrl']['strl']['strh'])) {
722
- for ($i = 0; $i < count($thisfile_riff['AVI ']['hdrl']['strl']['strh']); $i++) {
723
- if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'])) {
724
- $strhData = $thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'];
725
- $strhfccType = substr($strhData, 0, 4);
726
-
727
- if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'])) {
728
- $strfData = $thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'];
729
-
730
- // shortcut
731
- $thisfile_riff_raw_strf_strhfccType_streamindex = &$thisfile_riff_raw['strf'][$strhfccType][$streamindex];
732
-
733
- switch ($strhfccType) {
734
- case 'auds':
735
- $thisfile_audio['bitrate_mode'] = 'cbr';
736
- $thisfile_audio_dataformat = 'wav';
737
- if (isset($thisfile_riff_audio) && is_array($thisfile_riff_audio)) {
738
- $streamindex = count($thisfile_riff_audio);
739
- }
740
-
741
- $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($strfData);
742
- $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
743
-
744
- // shortcut
745
- $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
746
- $thisfile_audio_streams_currentstream = &$thisfile_audio['streams'][$streamindex];
747
-
748
- if ($thisfile_audio_streams_currentstream['bits_per_sample'] == 0) {
749
- unset($thisfile_audio_streams_currentstream['bits_per_sample']);
750
- }
751
- $thisfile_audio_streams_currentstream['wformattag'] = $thisfile_audio_streams_currentstream['raw']['wFormatTag'];
752
- unset($thisfile_audio_streams_currentstream['raw']);
753
-
754
- // shortcut
755
- $thisfile_riff_raw['strf'][$strhfccType][$streamindex] = $thisfile_riff_audio[$streamindex]['raw'];
756
-
757
- unset($thisfile_riff_audio[$streamindex]['raw']);
758
- $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
759
-
760
- $thisfile_audio['lossless'] = false;
761
- switch ($thisfile_riff_raw_strf_strhfccType_streamindex['wFormatTag']) {
762
- case 0x0001: // PCM
763
- $thisfile_audio_dataformat = 'wav';
764
- $thisfile_audio['lossless'] = true;
765
- break;
766
-
767
- case 0x0050: // MPEG Layer 2 or Layer 1
768
- $thisfile_audio_dataformat = 'mp2'; // Assume Layer-2
769
- break;
770
-
771
- case 0x0055: // MPEG Layer 3
772
- $thisfile_audio_dataformat = 'mp3';
773
- break;
774
-
775
- case 0x00FF: // AAC
776
- $thisfile_audio_dataformat = 'aac';
777
- break;
778
-
779
- case 0x0161: // Windows Media v7 / v8 / v9
780
- case 0x0162: // Windows Media Professional v9
781
- case 0x0163: // Windows Media Lossess v9
782
- $thisfile_audio_dataformat = 'wma';
783
- break;
784
-
785
- case 0x2000: // AC-3
786
- $thisfile_audio_dataformat = 'ac3';
787
- break;
788
-
789
- case 0x2001: // DTS
790
- $thisfile_audio_dataformat = 'dts';
791
- break;
792
-
793
- default:
794
- $thisfile_audio_dataformat = 'wav';
795
- break;
796
- }
797
- $thisfile_audio_streams_currentstream['dataformat'] = $thisfile_audio_dataformat;
798
- $thisfile_audio_streams_currentstream['lossless'] = $thisfile_audio['lossless'];
799
- $thisfile_audio_streams_currentstream['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
800
- break;
801
-
802
-
803
- case 'iavs':
804
- case 'vids':
805
- // shortcut
806
- $thisfile_riff_raw['strh'][$i] = array();
807
- $thisfile_riff_raw_strh_current = &$thisfile_riff_raw['strh'][$i];
808
-
809
- $thisfile_riff_raw_strh_current['fccType'] = substr($strhData, 0, 4); // same as $strhfccType;
810
- $thisfile_riff_raw_strh_current['fccHandler'] = substr($strhData, 4, 4);
811
- $thisfile_riff_raw_strh_current['dwFlags'] = $this->EitherEndian2Int(substr($strhData, 8, 4)); // Contains AVITF_* flags
812
- $thisfile_riff_raw_strh_current['wPriority'] = $this->EitherEndian2Int(substr($strhData, 12, 2));
813
- $thisfile_riff_raw_strh_current['wLanguage'] = $this->EitherEndian2Int(substr($strhData, 14, 2));
814
- $thisfile_riff_raw_strh_current['dwInitialFrames'] = $this->EitherEndian2Int(substr($strhData, 16, 4));
815
- $thisfile_riff_raw_strh_current['dwScale'] = $this->EitherEndian2Int(substr($strhData, 20, 4));
816
- $thisfile_riff_raw_strh_current['dwRate'] = $this->EitherEndian2Int(substr($strhData, 24, 4));
817
- $thisfile_riff_raw_strh_current['dwStart'] = $this->EitherEndian2Int(substr($strhData, 28, 4));
818
- $thisfile_riff_raw_strh_current['dwLength'] = $this->EitherEndian2Int(substr($strhData, 32, 4));
819
- $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($strhData, 36, 4));
820
- $thisfile_riff_raw_strh_current['dwQuality'] = $this->EitherEndian2Int(substr($strhData, 40, 4));
821
- $thisfile_riff_raw_strh_current['dwSampleSize'] = $this->EitherEndian2Int(substr($strhData, 44, 4));
822
- $thisfile_riff_raw_strh_current['rcFrame'] = $this->EitherEndian2Int(substr($strhData, 48, 4));
823
-
824
- $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strh_current['fccHandler']);
825
- $thisfile_video['fourcc'] = $thisfile_riff_raw_strh_current['fccHandler'];
826
- if (!$thisfile_riff_video_current['codec'] && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
827
- $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']);
828
- $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
829
- }
830
- $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
831
- $thisfile_video['pixel_aspect_ratio'] = (float) 1;
832
- switch ($thisfile_riff_raw_strh_current['fccHandler']) {
833
- case 'HFYU': // Huffman Lossless Codec
834
- case 'IRAW': // Intel YUV Uncompressed
835
- case 'YUY2': // Uncompressed YUV 4:2:2
836
- $thisfile_video['lossless'] = true;
837
- break;
838
-
839
- default:
840
- $thisfile_video['lossless'] = false;
841
- break;
842
- }
843
-
844
- switch ($strhfccType) {
845
- case 'vids':
846
- $thisfile_riff_raw_strf_strhfccType_streamindex = self::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), ($this->container == 'riff'));
847
- $thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'];
848
-
849
- if ($thisfile_riff_video_current['codec'] == 'DV') {
850
- $thisfile_riff_video_current['dv_type'] = 2;
851
- }
852
- break;
853
-
854
- case 'iavs':
855
- $thisfile_riff_video_current['dv_type'] = 1;
856
- break;
857
- }
858
- break;
859
-
860
- default:
861
- $info['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"';
862
- break;
863
-
864
- }
865
- }
866
- }
867
-
868
- if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
869
-
870
- $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
871
- if (self::fourccLookup($thisfile_video['fourcc'])) {
872
- $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_video['fourcc']);
873
- $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
874
- }
875
-
876
- switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) {
877
- case 'HFYU': // Huffman Lossless Codec
878
- case 'IRAW': // Intel YUV Uncompressed
879
- case 'YUY2': // Uncompressed YUV 4:2:2
880
- $thisfile_video['lossless'] = true;
881
- //$thisfile_video['bits_per_sample'] = 24;
882
- break;
883
-
884
- default:
885
- $thisfile_video['lossless'] = false;
886
- //$thisfile_video['bits_per_sample'] = 24;
887
- break;
888
- }
889
-
890
- }
891
- }
892
- }
893
- }
894
- break;
895
-
896
-
897
- case 'AMV ':
898
- $info['fileformat'] = 'amv';
899
- $info['mime_type'] = 'video/amv';
900
-
901
- $thisfile_video['bitrate_mode'] = 'vbr'; // it's MJPEG, presumably contant-quality encoding, thereby VBR
902
- $thisfile_video['dataformat'] = 'mjpeg';
903
- $thisfile_video['codec'] = 'mjpeg';
904
- $thisfile_video['lossless'] = false;
905
- $thisfile_video['bits_per_sample'] = 24;
906
-
907
- $thisfile_audio['dataformat'] = 'adpcm';
908
- $thisfile_audio['lossless'] = false;
909
- break;
910
-
911
-
912
- // http://en.wikipedia.org/wiki/CD-DA
913
- case 'CDDA':
914
- $info['fileformat'] = 'cda';
915
- unset($info['mime_type']);
916
-
917
- $thisfile_audio_dataformat = 'cda';
918
-
919
- $info['avdataoffset'] = 44;
920
-
921
- if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) {
922
- // shortcut
923
- $thisfile_riff_CDDA_fmt_0 = &$thisfile_riff['CDDA']['fmt '][0];
924
-
925
- $thisfile_riff_CDDA_fmt_0['unknown1'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 0, 2));
926
- $thisfile_riff_CDDA_fmt_0['track_num'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 2, 2));
927
- $thisfile_riff_CDDA_fmt_0['disc_id'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 4, 4));
928
- $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 8, 4));
929
- $thisfile_riff_CDDA_fmt_0['playtime_frames'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4));
930
- $thisfile_riff_CDDA_fmt_0['unknown6'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4));
931
- $thisfile_riff_CDDA_fmt_0['unknown7'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4));
932
-
933
- $thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75;
934
- $thisfile_riff_CDDA_fmt_0['playtime_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75;
935
- $info['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num'];
936
- $info['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
937
-
938
- // hardcoded data for CD-audio
939
- $thisfile_audio['lossless'] = true;
940
- $thisfile_audio['sample_rate'] = 44100;
941
- $thisfile_audio['channels'] = 2;
942
- $thisfile_audio['bits_per_sample'] = 16;
943
- $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $thisfile_audio['channels'] * $thisfile_audio['bits_per_sample'];
944
- $thisfile_audio['bitrate_mode'] = 'cbr';
945
- }
946
- break;
947
-
948
- // http://en.wikipedia.org/wiki/AIFF
949
- case 'AIFF':
950
- case 'AIFC':
951
- $info['fileformat'] = 'aiff';
952
- $info['mime_type'] = 'audio/x-aiff';
953
-
954
- $thisfile_audio['bitrate_mode'] = 'cbr';
955
- $thisfile_audio_dataformat = 'aiff';
956
- $thisfile_audio['lossless'] = true;
957
-
958
- if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) {
959
- $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8;
960
- $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size'];
961
- if ($info['avdataend'] > $info['filesize']) {
962
- if (($info['avdataend'] == ($info['filesize'] + 1)) && (($info['filesize'] % 2) == 1)) {
963
- // structures rounded to 2-byte boundary, but dumb encoders
964
- // forget to pad end of file to make this actually work
965
- } else {
966
- $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
967
- }
968
- $info['avdataend'] = $info['filesize'];
969
- }
970
- }
971
-
972
- if (isset($thisfile_riff[$RIFFsubtype]['COMM'][0]['data'])) {
973
-
974
- // shortcut
975
- $thisfile_riff_RIFFsubtype_COMM_0_data = &$thisfile_riff[$RIFFsubtype]['COMM'][0]['data'];
976
-
977
- $thisfile_riff_audio['channels'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 0, 2), true);
978
- $thisfile_riff_audio['total_samples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 2, 4), false);
979
- $thisfile_riff_audio['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 6, 2), true);
980
- $thisfile_riff_audio['sample_rate'] = (int) getid3_lib::BigEndian2Float(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 8, 10));
981
-
982
- if ($thisfile_riff[$RIFFsubtype]['COMM'][0]['size'] > 18) {
983
- $thisfile_riff_audio['codec_fourcc'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 18, 4);
984
- $CodecNameSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 22, 1), false);
985
- $thisfile_riff_audio['codec_name'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 23, $CodecNameSize);
986
- switch ($thisfile_riff_audio['codec_name']) {
987
- case 'NONE':
988
- $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
989
- $thisfile_audio['lossless'] = true;
990
- break;
991
-
992
- case '':
993
- switch ($thisfile_riff_audio['codec_fourcc']) {
994
- // http://developer.apple.com/qa/snd/snd07.html
995
- case 'sowt':
996
- $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Little-Endian PCM';
997
- $thisfile_audio['lossless'] = true;
998
- break;
999
-
1000
- case 'twos':
1001
- $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Big-Endian PCM';
1002
- $thisfile_audio['lossless'] = true;
1003
- break;
1004
-
1005
- default:
1006
- break;
1007
- }
1008
- break;
1009
-
1010
- default:
1011
- $thisfile_audio['codec'] = $thisfile_riff_audio['codec_name'];
1012
- $thisfile_audio['lossless'] = false;
1013
- break;
1014
- }
1015
- }
1016
-
1017
- $thisfile_audio['channels'] = $thisfile_riff_audio['channels'];
1018
- if ($thisfile_riff_audio['bits_per_sample'] > 0) {
1019
- $thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample'];
1020
- }
1021
- $thisfile_audio['sample_rate'] = $thisfile_riff_audio['sample_rate'];
1022
- if ($thisfile_audio['sample_rate'] == 0) {
1023
- $info['error'][] = 'Corrupted AIFF file: sample_rate == zero';
1024
- return false;
1025
- }
1026
- $info['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate'];
1027
- }
1028
-
1029
- if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) {
1030
- $offset = 0;
1031
- $CommentCount = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
1032
- $offset += 2;
1033
- for ($i = 0; $i < $CommentCount; $i++) {
1034
- $info['comments_raw'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false);
1035
- $offset += 4;
1036
- $info['comments_raw'][$i]['marker_id'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true);
1037
- $offset += 2;
1038
- $CommentLength = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
1039
- $offset += 2;
1040
- $info['comments_raw'][$i]['comment'] = substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength);
1041
- $offset += $CommentLength;
1042
-
1043
- $info['comments_raw'][$i]['timestamp_unix'] = getid3_lib::DateMac2Unix($info['comments_raw'][$i]['timestamp']);
1044
- $thisfile_riff['comments']['comment'][] = $info['comments_raw'][$i]['comment'];
1045
- }
1046
- }
1047
-
1048
- $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
1049
- foreach ($CommentsChunkNames as $key => $value) {
1050
- if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
1051
- $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
1052
- }
1053
- }
1054
- /*
1055
- if (isset($thisfile_riff[$RIFFsubtype]['ID3 '])) {
1056
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
1057
- $getid3_temp = new getID3();
1058
- $getid3_temp->openfile($this->getid3->filename);
1059
- $getid3_id3v2 = new getid3_id3v2($getid3_temp);
1060
- $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['ID3 '][0]['offset'] + 8;
1061
- if ($thisfile_riff[$RIFFsubtype]['ID3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
1062
- $info['id3v2'] = $getid3_temp->info['id3v2'];
1063
- }
1064
- unset($getid3_temp, $getid3_id3v2);
1065
- }
1066
- */
1067
- break;
1068
-
1069
- // http://en.wikipedia.org/wiki/8SVX
1070
- case '8SVX':
1071
- $info['fileformat'] = '8svx';
1072
- $info['mime_type'] = 'audio/8svx';
1073
-
1074
- $thisfile_audio['bitrate_mode'] = 'cbr';
1075
- $thisfile_audio_dataformat = '8svx';
1076
- $thisfile_audio['bits_per_sample'] = 8;
1077
- $thisfile_audio['channels'] = 1; // overridden below, if need be
1078
-
1079
- if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
1080
- $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8;
1081
- $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'];
1082
- if ($info['avdataend'] > $info['filesize']) {
1083
- $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
1084
- }
1085
- }
1086
-
1087
- if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) {
1088
- // shortcut
1089
- $thisfile_riff_RIFFsubtype_VHDR_0 = &$thisfile_riff[$RIFFsubtype]['VHDR'][0];
1090
-
1091
- $thisfile_riff_RIFFsubtype_VHDR_0['oneShotHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 0, 4));
1092
- $thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 4, 4));
1093
- $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 8, 4));
1094
- $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2));
1095
- $thisfile_riff_RIFFsubtype_VHDR_0['ctOctave'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1));
1096
- $thisfile_riff_RIFFsubtype_VHDR_0['sCompression'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1));
1097
- $thisfile_riff_RIFFsubtype_VHDR_0['Volume'] = getid3_lib::FixedPoint16_16(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 16, 4));
1098
-
1099
- $thisfile_audio['sample_rate'] = $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'];
1100
-
1101
- switch ($thisfile_riff_RIFFsubtype_VHDR_0['sCompression']) {
1102
- case 0:
1103
- $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
1104
- $thisfile_audio['lossless'] = true;
1105
- $ActualBitsPerSample = 8;
1106
- break;
1107
-
1108
- case 1:
1109
- $thisfile_audio['codec'] = 'Fibonacci-delta encoding';
1110
- $thisfile_audio['lossless'] = false;
1111
- $ActualBitsPerSample = 4;
1112
- break;
1113
-
1114
- default:
1115
- $info['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"';
1116
- break;
1117
- }
1118
- }
1119
-
1120
- if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) {
1121
- $ChannelsIndex = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'], 0, 4));
1122
- switch ($ChannelsIndex) {
1123
- case 6: // Stereo
1124
- $thisfile_audio['channels'] = 2;
1125
- break;
1126
-
1127
- case 2: // Left channel only
1128
- case 4: // Right channel only
1129
- $thisfile_audio['channels'] = 1;
1130
- break;
1131
-
1132
- default:
1133
- $info['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"';
1134
- break;
1135
- }
1136
-
1137
- }
1138
-
1139
- $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
1140
- foreach ($CommentsChunkNames as $key => $value) {
1141
- if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
1142
- $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
1143
- }
1144
- }
1145
-
1146
- $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $ActualBitsPerSample * $thisfile_audio['channels'];
1147
- if (!empty($thisfile_audio['bitrate'])) {
1148
- $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($thisfile_audio['bitrate'] / 8);
1149
- }
1150
- break;
1151
-
1152
- case 'CDXA':
1153
- $info['fileformat'] = 'vcd'; // Asume Video CD
1154
- $info['mime_type'] = 'video/mpeg';
1155
-
1156
- if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) {
1157
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, true);
1158
-
1159
- $getid3_temp = new getID3();
1160
- $getid3_temp->openfile($this->getid3->filename);
1161
- $getid3_mpeg = new getid3_mpeg($getid3_temp);
1162
- $getid3_mpeg->Analyze();
1163
- if (empty($getid3_temp->info['error'])) {
1164
- $info['audio'] = $getid3_temp->info['audio'];
1165
- $info['video'] = $getid3_temp->info['video'];
1166
- $info['mpeg'] = $getid3_temp->info['mpeg'];
1167
- $info['warning'] = $getid3_temp->info['warning'];
1168
- }
1169
- unset($getid3_temp, $getid3_mpeg);
1170
- }
1171
- break;
1172
-
1173
-
1174
- default:
1175
- $info['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead';
1176
- //unset($info['fileformat']);
1177
- }
1178
-
1179
- switch ($RIFFsubtype) {
1180
- case 'WAVE':
1181
- case 'AIFF':
1182
- case 'AIFC':
1183
- $ID3v2_key_good = 'id3 ';
1184
- $ID3v2_keys_bad = array('ID3 ', 'tag ');
1185
- foreach ($ID3v2_keys_bad as $ID3v2_key_bad) {
1186
- if (isset($thisfile_riff[$RIFFsubtype][$ID3v2_key_bad]) && !array_key_exists($ID3v2_key_good, $thisfile_riff[$RIFFsubtype])) {
1187
- $thisfile_riff[$RIFFsubtype][$ID3v2_key_good] = $thisfile_riff[$RIFFsubtype][$ID3v2_key_bad];
1188
- $info['warning'][] = 'mapping "'.$ID3v2_key_bad.'" chunk to "'.$ID3v2_key_good.'"';
1189
- }
1190
- }
1191
-
1192
- if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) {
1193
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
1194
-
1195
- $getid3_temp = new getID3();
1196
- $getid3_temp->openfile($this->getid3->filename);
1197
- $getid3_id3v2 = new getid3_id3v2($getid3_temp);
1198
- $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['id3 '][0]['offset'] + 8;
1199
- if ($thisfile_riff[$RIFFsubtype]['id3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
1200
- $info['id3v2'] = $getid3_temp->info['id3v2'];
1201
- }
1202
- unset($getid3_temp, $getid3_id3v2);
1203
- }
1204
- break;
1205
- }
1206
-
1207
- if (isset($thisfile_riff_WAVE['DISP']) && is_array($thisfile_riff_WAVE['DISP'])) {
1208
- $thisfile_riff['comments']['title'][] = trim(substr($thisfile_riff_WAVE['DISP'][count($thisfile_riff_WAVE['DISP']) - 1]['data'], 4));
1209
- }
1210
- if (isset($thisfile_riff_WAVE['INFO']) && is_array($thisfile_riff_WAVE['INFO'])) {
1211
- self::parseComments($thisfile_riff_WAVE['INFO'], $thisfile_riff['comments']);
1212
- }
1213
- if (isset($thisfile_riff['AVI ']['INFO']) && is_array($thisfile_riff['AVI ']['INFO'])) {
1214
- self::parseComments($thisfile_riff['AVI ']['INFO'], $thisfile_riff['comments']);
1215
- }
1216
-
1217
- if (empty($thisfile_audio['encoder']) && !empty($info['mpeg']['audio']['LAME']['short_version'])) {
1218
- $thisfile_audio['encoder'] = $info['mpeg']['audio']['LAME']['short_version'];
1219
- }
1220
-
1221
- if (!isset($info['playtime_seconds'])) {
1222
- $info['playtime_seconds'] = 0;
1223
- }
1224
- if (isset($thisfile_riff_raw['strh'][0]['dwLength']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1225
- // needed for >2GB AVIs where 'avih' chunk only lists number of frames in that chunk, not entire movie
1226
- $info['playtime_seconds'] = $thisfile_riff_raw['strh'][0]['dwLength'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1227
- } elseif (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1228
- $info['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1229
- }
1230
-
1231
- if ($info['playtime_seconds'] > 0) {
1232
- if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1233
-
1234
- if (!isset($info['bitrate'])) {
1235
- $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1236
- }
1237
-
1238
- } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) {
1239
-
1240
- if (!isset($thisfile_audio['bitrate'])) {
1241
- $thisfile_audio['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1242
- }
1243
-
1244
- } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1245
-
1246
- if (!isset($thisfile_video['bitrate'])) {
1247
- $thisfile_video['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1248
- }
1249
-
1250
- }
1251
- }
1252
-
1253
-
1254
- if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && ($thisfile_audio['bitrate'] > 0) && ($info['playtime_seconds'] > 0)) {
1255
-
1256
- $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1257
- $thisfile_audio['bitrate'] = 0;
1258
- $thisfile_video['bitrate'] = $info['bitrate'];
1259
- foreach ($thisfile_riff_audio as $channelnumber => $audioinfoarray) {
1260
- $thisfile_video['bitrate'] -= $audioinfoarray['bitrate'];
1261
- $thisfile_audio['bitrate'] += $audioinfoarray['bitrate'];
1262
- }
1263
- if ($thisfile_video['bitrate'] <= 0) {
1264
- unset($thisfile_video['bitrate']);
1265
- }
1266
- if ($thisfile_audio['bitrate'] <= 0) {
1267
- unset($thisfile_audio['bitrate']);
1268
- }
1269
- }
1270
-
1271
- if (isset($info['mpeg']['audio'])) {
1272
- $thisfile_audio_dataformat = 'mp'.$info['mpeg']['audio']['layer'];
1273
- $thisfile_audio['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1274
- $thisfile_audio['channels'] = $info['mpeg']['audio']['channels'];
1275
- $thisfile_audio['bitrate'] = $info['mpeg']['audio']['bitrate'];
1276
- $thisfile_audio['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1277
- if (!empty($info['mpeg']['audio']['codec'])) {
1278
- $thisfile_audio['codec'] = $info['mpeg']['audio']['codec'].' '.$thisfile_audio['codec'];
1279
- }
1280
- if (!empty($thisfile_audio['streams'])) {
1281
- foreach ($thisfile_audio['streams'] as $streamnumber => $streamdata) {
1282
- if ($streamdata['dataformat'] == $thisfile_audio_dataformat) {
1283
- $thisfile_audio['streams'][$streamnumber]['sample_rate'] = $thisfile_audio['sample_rate'];
1284
- $thisfile_audio['streams'][$streamnumber]['channels'] = $thisfile_audio['channels'];
1285
- $thisfile_audio['streams'][$streamnumber]['bitrate'] = $thisfile_audio['bitrate'];
1286
- $thisfile_audio['streams'][$streamnumber]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
1287
- $thisfile_audio['streams'][$streamnumber]['codec'] = $thisfile_audio['codec'];
1288
- }
1289
- }
1290
- }
1291
- $getid3_mp3 = new getid3_mp3($this->getid3);
1292
- $thisfile_audio['encoder_options'] = $getid3_mp3->GuessEncoderOptions();
1293
- unset($getid3_mp3);
1294
- }
1295
-
1296
-
1297
- if (!empty($thisfile_riff_raw['fmt ']['wBitsPerSample']) && ($thisfile_riff_raw['fmt ']['wBitsPerSample'] > 0)) {
1298
- switch ($thisfile_audio_dataformat) {
1299
- case 'ac3':
1300
- // ignore bits_per_sample
1301
- break;
1302
-
1303
- default:
1304
- $thisfile_audio['bits_per_sample'] = $thisfile_riff_raw['fmt ']['wBitsPerSample'];
1305
- break;
1306
- }
1307
- }
1308
-
1309
-
1310
- if (empty($thisfile_riff_raw)) {
1311
- unset($thisfile_riff['raw']);
1312
- }
1313
- if (empty($thisfile_riff_audio)) {
1314
- unset($thisfile_riff['audio']);
1315
- }
1316
- if (empty($thisfile_riff_video)) {
1317
- unset($thisfile_riff['video']);
1318
- }
1319
-
1320
- return true;
1321
- }
1322
-
1323
- public function ParseRIFFAMV($startoffset, $maxoffset) {
1324
- // AMV files are RIFF-AVI files with parts of the spec deliberately broken, such as chunk size fields hardcoded to zero (because players known in hardware that these fields are always a certain size
1325
-
1326
- // https://code.google.com/p/amv-codec-tools/wiki/AmvDocumentation
1327
- //typedef struct _amvmainheader {
1328
- //FOURCC fcc; // 'amvh'
1329
- //DWORD cb;
1330
- //DWORD dwMicroSecPerFrame;
1331
- //BYTE reserve[28];
1332
- //DWORD dwWidth;
1333
- //DWORD dwHeight;
1334
- //DWORD dwSpeed;
1335
- //DWORD reserve0;
1336
- //DWORD reserve1;
1337
- //BYTE bTimeSec;
1338
- //BYTE bTimeMin;
1339
- //WORD wTimeHour;
1340
- //} AMVMAINHEADER;
1341
-
1342
- $info = &$this->getid3->info;
1343
- $RIFFchunk = false;
1344
-
1345
- try {
1346
-
1347
- $this->fseek($startoffset);
1348
- $maxoffset = min($maxoffset, $info['avdataend']);
1349
- $AMVheader = $this->fread(284);
1350
- if (substr($AMVheader, 0, 8) != 'hdrlamvh') {
1351
- throw new Exception('expecting "hdrlamv" at offset '.($startoffset + 0).', found "'.substr($AMVheader, 0, 8).'"');
1352
- }
1353
- if (substr($AMVheader, 8, 4) != "\x38\x00\x00\x00") { // "amvh" chunk size, hardcoded to 0x38 = 56 bytes
1354
- throw new Exception('expecting "0x38000000" at offset '.($startoffset + 8).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 8, 4)).'"');
1355
- }
1356
- $RIFFchunk = array();
1357
- $RIFFchunk['amvh']['us_per_frame'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 12, 4));
1358
- $RIFFchunk['amvh']['reserved28'] = substr($AMVheader, 16, 28); // null? reserved?
1359
- $RIFFchunk['amvh']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 44, 4));
1360
- $RIFFchunk['amvh']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 48, 4));
1361
- $RIFFchunk['amvh']['frame_rate_int'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 52, 4));
1362
- $RIFFchunk['amvh']['reserved0'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 56, 4)); // 1? reserved?
1363
- $RIFFchunk['amvh']['reserved1'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 60, 4)); // 0? reserved?
1364
- $RIFFchunk['amvh']['runtime_sec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 64, 1));
1365
- $RIFFchunk['amvh']['runtime_min'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 65, 1));
1366
- $RIFFchunk['amvh']['runtime_hrs'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 66, 2));
1367
-
1368
- $info['video']['frame_rate'] = 1000000 / $RIFFchunk['amvh']['us_per_frame'];
1369
- $info['video']['resolution_x'] = $RIFFchunk['amvh']['resolution_x'];
1370
- $info['video']['resolution_y'] = $RIFFchunk['amvh']['resolution_y'];
1371
- $info['playtime_seconds'] = ($RIFFchunk['amvh']['runtime_hrs'] * 3600) + ($RIFFchunk['amvh']['runtime_min'] * 60) + $RIFFchunk['amvh']['runtime_sec'];
1372
-
1373
- // the rest is all hardcoded(?) and does not appear to be useful until you get to audio info at offset 256, even then everything is probably hardcoded
1374
-
1375
- if (substr($AMVheader, 68, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x38\x00\x00\x00") {
1376
- throw new Exception('expecting "LIST<0x00000000>strlstrh<0x38000000>" at offset '.($startoffset + 68).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 68, 20)).'"');
1377
- }
1378
- // followed by 56 bytes of null: substr($AMVheader, 88, 56) -> 144
1379
- if (substr($AMVheader, 144, 8) != 'strf'."\x24\x00\x00\x00") {
1380
- throw new Exception('expecting "strf<0x24000000>" at offset '.($startoffset + 144).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 144, 8)).'"');
1381
- }
1382
- // followed by 36 bytes of null: substr($AMVheader, 144, 36) -> 180
1383
-
1384
- if (substr($AMVheader, 188, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x30\x00\x00\x00") {
1385
- throw new Exception('expecting "LIST<0x00000000>strlstrh<0x30000000>" at offset '.($startoffset + 188).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 188, 20)).'"');
1386
- }
1387
- // followed by 48 bytes of null: substr($AMVheader, 208, 48) -> 256
1388
- if (substr($AMVheader, 256, 8) != 'strf'."\x14\x00\x00\x00") {
1389
- throw new Exception('expecting "strf<0x14000000>" at offset '.($startoffset + 256).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 256, 8)).'"');
1390
- }
1391
- // followed by 20 bytes of a modified WAVEFORMATEX:
1392
- // typedef struct {
1393
- // WORD wFormatTag; //(Fixme: this is equal to PCM's 0x01 format code)
1394
- // WORD nChannels; //(Fixme: this is always 1)
1395
- // DWORD nSamplesPerSec; //(Fixme: for all known sample files this is equal to 22050)
1396
- // DWORD nAvgBytesPerSec; //(Fixme: for all known sample files this is equal to 44100)
1397
- // WORD nBlockAlign; //(Fixme: this seems to be 2 in AMV files, is this correct ?)
1398
- // WORD wBitsPerSample; //(Fixme: this seems to be 16 in AMV files instead of the expected 4)
1399
- // WORD cbSize; //(Fixme: this seems to be 0 in AMV files)
1400
- // WORD reserved;
1401
- // } WAVEFORMATEX;
1402
- $RIFFchunk['strf']['wformattag'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 264, 2));
1403
- $RIFFchunk['strf']['nchannels'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 266, 2));
1404
- $RIFFchunk['strf']['nsamplespersec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 268, 4));
1405
- $RIFFchunk['strf']['navgbytespersec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 272, 4));
1406
- $RIFFchunk['strf']['nblockalign'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 276, 2));
1407
- $RIFFchunk['strf']['wbitspersample'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 278, 2));
1408
- $RIFFchunk['strf']['cbsize'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 280, 2));
1409
- $RIFFchunk['strf']['reserved'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 282, 2));
1410
-
1411
-
1412
- $info['audio']['lossless'] = false;
1413
- $info['audio']['sample_rate'] = $RIFFchunk['strf']['nsamplespersec'];
1414
- $info['audio']['channels'] = $RIFFchunk['strf']['nchannels'];
1415
- $info['audio']['bits_per_sample'] = $RIFFchunk['strf']['wbitspersample'];
1416
- $info['audio']['bitrate'] = $info['audio']['sample_rate'] * $info['audio']['channels'] * $info['audio']['bits_per_sample'];
1417
- $info['audio']['bitrate_mode'] = 'cbr';
1418
-
1419
-
1420
- } catch (getid3_exception $e) {
1421
- if ($e->getCode() == 10) {
1422
- $this->warning('RIFFAMV parser: '.$e->getMessage());
1423
- } else {
1424
- throw $e;
1425
- }
1426
- }
1427
-
1428
- return $RIFFchunk;
1429
- }
1430
-
1431
-
1432
- public function ParseRIFF($startoffset, $maxoffset) {
1433
- $info = &$this->getid3->info;
1434
-
1435
- $RIFFchunk = false;
1436
- $FoundAllChunksWeNeed = false;
1437
-
1438
- try {
1439
- $this->fseek($startoffset);
1440
- $maxoffset = min($maxoffset, $info['avdataend']);
1441
- while ($this->ftell() < $maxoffset) {
1442
- $chunknamesize = $this->fread(8);
1443
- //$chunkname = substr($chunknamesize, 0, 4);
1444
- $chunkname = str_replace("\x00", '_', substr($chunknamesize, 0, 4)); // note: chunk names of 4 null bytes do appear to be legal (has been observed inside INFO and PRMI chunks, for example), but makes traversing array keys more difficult
1445
- $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4));
1446
- //if (strlen(trim($chunkname, "\x00")) < 4) {
1447
- if (strlen($chunkname) < 4) {
1448
- $this->error('Expecting chunk name at offset '.($this->ftell() - 8).' but found nothing. Aborting RIFF parsing.');
1449
- break;
1450
- }
1451
- if (($chunksize == 0) && ($chunkname != 'JUNK')) {
1452
- $this->warning('Chunk ('.$chunkname.') size at offset '.($this->ftell() - 4).' is zero. Aborting RIFF parsing.');
1453
- break;
1454
- }
1455
- if (($chunksize % 2) != 0) {
1456
- // all structures are packed on word boundaries
1457
- $chunksize++;
1458
- }
1459
-
1460
- switch ($chunkname) {
1461
- case 'LIST':
1462
- $listname = $this->fread(4);
1463
- if (preg_match('#^(movi|rec )$#i', $listname)) {
1464
- $RIFFchunk[$listname]['offset'] = $this->ftell() - 4;
1465
- $RIFFchunk[$listname]['size'] = $chunksize;
1466
-
1467
- if (!$FoundAllChunksWeNeed) {
1468
- $WhereWeWere = $this->ftell();
1469
- $AudioChunkHeader = $this->fread(12);
1470
- $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2);
1471
- $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2);
1472
- $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
1473
-
1474
- if ($AudioChunkStreamType == 'wb') {
1475
- $FirstFourBytes = substr($AudioChunkHeader, 8, 4);
1476
- if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', $FirstFourBytes)) {
1477
- // MP3
1478
- if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
1479
- $getid3_temp = new getID3();
1480
- $getid3_temp->openfile($this->getid3->filename);
1481
- $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
1482
- $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
1483
- $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
1484
- $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
1485
- if (isset($getid3_temp->info['mpeg']['audio'])) {
1486
- $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio'];
1487
- $info['audio'] = $getid3_temp->info['audio'];
1488
- $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
1489
- $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1490
- $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
1491
- $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1492
- $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1493
- //$info['bitrate'] = $info['audio']['bitrate'];
1494
- }
1495
- unset($getid3_temp, $getid3_mp3);
1496
- }
1497
-
1498
- } elseif (strpos($FirstFourBytes, getid3_ac3::syncword) === 0) {
1499
-
1500
- // AC3
1501
- $getid3_temp = new getID3();
1502
- $getid3_temp->openfile($this->getid3->filename);
1503
- $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
1504
- $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
1505
- $getid3_ac3 = new getid3_ac3($getid3_temp);
1506
- $getid3_ac3->Analyze();
1507
- if (empty($getid3_temp->info['error'])) {
1508
- $info['audio'] = $getid3_temp->info['audio'];
1509
- $info['ac3'] = $getid3_temp->info['ac3'];
1510
- if (!empty($getid3_temp->info['warning'])) {
1511
- foreach ($getid3_temp->info['warning'] as $key => $value) {
1512
- $info['warning'][] = $value;
1513
- }
1514
- }
1515
- }
1516
- unset($getid3_temp, $getid3_ac3);
1517
- }
1518
- }
1519
- $FoundAllChunksWeNeed = true;
1520
- $this->fseek($WhereWeWere);
1521
- }
1522
- $this->fseek($chunksize - 4, SEEK_CUR);
1523
-
1524
- } else {
1525
-
1526
- if (!isset($RIFFchunk[$listname])) {
1527
- $RIFFchunk[$listname] = array();
1528
- }
1529
- $LISTchunkParent = $listname;
1530
- $LISTchunkMaxOffset = $this->ftell() - 4 + $chunksize;
1531
- if ($parsedChunk = $this->ParseRIFF($this->ftell(), $LISTchunkMaxOffset)) {
1532
- $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
1533
- }
1534
-
1535
- }
1536
- break;
1537
-
1538
- default:
1539
- if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) {
1540
- $this->fseek($chunksize, SEEK_CUR);
1541
- break;
1542
- }
1543
- $thisindex = 0;
1544
- if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
1545
- $thisindex = count($RIFFchunk[$chunkname]);
1546
- }
1547
- $RIFFchunk[$chunkname][$thisindex]['offset'] = $this->ftell() - 8;
1548
- $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize;
1549
- switch ($chunkname) {
1550
- case 'data':
1551
- $info['avdataoffset'] = $this->ftell();
1552
- $info['avdataend'] = $info['avdataoffset'] + $chunksize;
1553
-
1554
- $testData = $this->fread(36);
1555
- if ($testData === '') {
1556
- break;
1557
- }
1558
- if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', substr($testData, 0, 4))) {
1559
-
1560
- // Probably is MP3 data
1561
- if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($testData, 0, 4))) {
1562
- $getid3_temp = new getID3();
1563
- $getid3_temp->openfile($this->getid3->filename);
1564
- $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1565
- $getid3_temp->info['avdataend'] = $info['avdataend'];
1566
- $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
1567
- $getid3_mp3->getOnlyMPEGaudioInfo($info['avdataoffset'], false);
1568
- if (empty($getid3_temp->info['error'])) {
1569
- $info['audio'] = $getid3_temp->info['audio'];
1570
- $info['mpeg'] = $getid3_temp->info['mpeg'];
1571
- }
1572
- unset($getid3_temp, $getid3_mp3);
1573
- }
1574
-
1575
- } elseif (($isRegularAC3 = (substr($testData, 0, 2) == getid3_ac3::syncword)) || substr($testData, 8, 2) == strrev(getid3_ac3::syncword)) {
1576
-
1577
- // This is probably AC-3 data
1578
- $getid3_temp = new getID3();
1579
- if ($isRegularAC3) {
1580
- $getid3_temp->openfile($this->getid3->filename);
1581
- $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1582
- $getid3_temp->info['avdataend'] = $info['avdataend'];
1583
- }
1584
- $getid3_ac3 = new getid3_ac3($getid3_temp);
1585
- if ($isRegularAC3) {
1586
- $getid3_ac3->Analyze();
1587
- } else {
1588
- // Dolby Digital WAV
1589
- // AC-3 content, but not encoded in same format as normal AC-3 file
1590
- // For one thing, byte order is swapped
1591
- $ac3_data = '';
1592
- for ($i = 0; $i < 28; $i += 2) {
1593
- $ac3_data .= substr($testData, 8 + $i + 1, 1);
1594
- $ac3_data .= substr($testData, 8 + $i + 0, 1);
1595
- }
1596
- $getid3_ac3->AnalyzeString($ac3_data);
1597
- }
1598
-
1599
- if (empty($getid3_temp->info['error'])) {
1600
- $info['audio'] = $getid3_temp->info['audio'];
1601
- $info['ac3'] = $getid3_temp->info['ac3'];
1602
- if (!empty($getid3_temp->info['warning'])) {
1603
- foreach ($getid3_temp->info['warning'] as $newerror) {
1604
- $this->warning('getid3_ac3() says: ['.$newerror.']');
1605
- }
1606
- }
1607
- }
1608
- unset($getid3_temp, $getid3_ac3);
1609
-
1610
- } elseif (preg_match('/^('.implode('|', array_map('preg_quote', getid3_dts::$syncwords)).')/', $testData)) {
1611
-
1612
- // This is probably DTS data
1613
- $getid3_temp = new getID3();
1614
- $getid3_temp->openfile($this->getid3->filename);
1615
- $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1616
- $getid3_dts = new getid3_dts($getid3_temp);
1617
- $getid3_dts->Analyze();
1618
- if (empty($getid3_temp->info['error'])) {
1619
- $info['audio'] = $getid3_temp->info['audio'];
1620
- $info['dts'] = $getid3_temp->info['dts'];
1621
- $info['playtime_seconds'] = $getid3_temp->info['playtime_seconds']; // may not match RIFF calculations since DTS-WAV often used 14/16 bit-word packing
1622
- if (!empty($getid3_temp->info['warning'])) {
1623
- foreach ($getid3_temp->info['warning'] as $newerror) {
1624
- $this->warning('getid3_dts() says: ['.$newerror.']');
1625
- }
1626
- }
1627
- }
1628
-
1629
- unset($getid3_temp, $getid3_dts);
1630
-
1631
- } elseif (substr($testData, 0, 4) == 'wvpk') {
1632
-
1633
- // This is WavPack data
1634
- $info['wavpack']['offset'] = $info['avdataoffset'];
1635
- $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($testData, 4, 4));
1636
- $this->parseWavPackHeader(substr($testData, 8, 28));
1637
-
1638
- } else {
1639
- // This is some other kind of data (quite possibly just PCM)
1640
- // do nothing special, just skip it
1641
- }
1642
- $nextoffset = $info['avdataend'];
1643
- $this->fseek($nextoffset);
1644
- break;
1645
-
1646
- case 'iXML':
1647
- case 'bext':
1648
- case 'cart':
1649
- case 'fmt ':
1650
- case 'strh':
1651
- case 'strf':
1652
- case 'indx':
1653
- case 'MEXT':
1654
- case 'DISP':
1655
- // always read data in
1656
- case 'JUNK':
1657
- // should be: never read data in
1658
- // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc)
1659
- if ($chunksize < 1048576) {
1660
- if ($chunksize > 0) {
1661
- $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1662
- if ($chunkname == 'JUNK') {
1663
- if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) {
1664
- // only keep text characters [chr(32)-chr(127)]
1665
- $info['riff']['comments']['junk'][] = trim($matches[1]);
1666
- }
1667
- // but if nothing there, ignore
1668
- // remove the key in either case
1669
- unset($RIFFchunk[$chunkname][$thisindex]['data']);
1670
- }
1671
- }
1672
- } else {
1673
- $this->warning('Chunk "'.$chunkname.'" at offset '.$this->ftell().' is unexpectedly larger than 1MB (claims to be '.number_format($chunksize).' bytes), skipping data');
1674
- $this->fseek($chunksize, SEEK_CUR);
1675
- }
1676
- break;
1677
-
1678
- //case 'IDVX':
1679
- // $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize));
1680
- // break;
1681
-
1682
- default:
1683
- if (!empty($LISTchunkParent) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) {
1684
- $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
1685
- $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size'];
1686
- unset($RIFFchunk[$chunkname][$thisindex]['offset']);
1687
- unset($RIFFchunk[$chunkname][$thisindex]['size']);
1688
- if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
1689
- unset($RIFFchunk[$chunkname][$thisindex]);
1690
- }
1691
- if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
1692
- unset($RIFFchunk[$chunkname]);
1693
- }
1694
- $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1695
- } elseif ($chunksize < 2048) {
1696
- // only read data in if smaller than 2kB
1697
- $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1698
- } else {
1699
- $this->fseek($chunksize, SEEK_CUR);
1700
- }
1701
- break;
1702
- }
1703
- break;
1704
- }
1705
- }
1706
-
1707
- } catch (getid3_exception $e) {
1708
- if ($e->getCode() == 10) {
1709
- $this->warning('RIFF parser: '.$e->getMessage());
1710
- } else {
1711
- throw $e;
1712
- }
1713
- }
1714
-
1715
- return $RIFFchunk;
1716
- }
1717
-
1718
- public function ParseRIFFdata(&$RIFFdata) {
1719
- $info = &$this->getid3->info;
1720
- if ($RIFFdata) {
1721
- $tempfile = tempnam(GETID3_TEMP_DIR, 'getID3');
1722
- $fp_temp = fopen($tempfile, 'wb');
1723
- $RIFFdataLength = strlen($RIFFdata);
1724
- $NewLengthString = getid3_lib::LittleEndian2String($RIFFdataLength, 4);
1725
- for ($i = 0; $i < 4; $i++) {
1726
- $RIFFdata[($i + 4)] = $NewLengthString[$i];
1727
- }
1728
- fwrite($fp_temp, $RIFFdata);
1729
- fclose($fp_temp);
1730
-
1731
- $getid3_temp = new getID3();
1732
- $getid3_temp->openfile($tempfile);
1733
- $getid3_temp->info['filesize'] = $RIFFdataLength;
1734
- $getid3_temp->info['filenamepath'] = $info['filenamepath'];
1735
- $getid3_temp->info['tags'] = $info['tags'];
1736
- $getid3_temp->info['warning'] = $info['warning'];
1737
- $getid3_temp->info['error'] = $info['error'];
1738
- $getid3_temp->info['comments'] = $info['comments'];
1739
- $getid3_temp->info['audio'] = (isset($info['audio']) ? $info['audio'] : array());
1740
- $getid3_temp->info['video'] = (isset($info['video']) ? $info['video'] : array());
1741
- $getid3_riff = new getid3_riff($getid3_temp);
1742
- $getid3_riff->Analyze();
1743
-
1744
- $info['riff'] = $getid3_temp->info['riff'];
1745
- $info['warning'] = $getid3_temp->info['warning'];
1746
- $info['error'] = $getid3_temp->info['error'];
1747
- $info['tags'] = $getid3_temp->info['tags'];
1748
- $info['comments'] = $getid3_temp->info['comments'];
1749
- unset($getid3_riff, $getid3_temp);
1750
- unlink($tempfile);
1751
- }
1752
- return false;
1753
- }
1754
-
1755
- public static function parseComments(&$RIFFinfoArray, &$CommentsTargetArray) {
1756
- $RIFFinfoKeyLookup = array(
1757
- 'IARL'=>'archivallocation',
1758
- 'IART'=>'artist',
1759
- 'ICDS'=>'costumedesigner',
1760
- 'ICMS'=>'commissionedby',
1761
- 'ICMT'=>'comment',
1762
- 'ICNT'=>'country',
1763
- 'ICOP'=>'copyright',
1764
- 'ICRD'=>'creationdate',
1765
- 'IDIM'=>'dimensions',
1766
- 'IDIT'=>'digitizationdate',
1767
- 'IDPI'=>'resolution',
1768
- 'IDST'=>'distributor',
1769
- 'IEDT'=>'editor',
1770
- 'IENG'=>'engineers',
1771
- 'IFRM'=>'accountofparts',
1772
- 'IGNR'=>'genre',
1773
- 'IKEY'=>'keywords',
1774
- 'ILGT'=>'lightness',
1775
- 'ILNG'=>'language',
1776
- 'IMED'=>'orignalmedium',
1777
- 'IMUS'=>'composer',
1778
- 'INAM'=>'title',
1779
- 'IPDS'=>'productiondesigner',
1780
- 'IPLT'=>'palette',
1781
- 'IPRD'=>'product',
1782
- 'IPRO'=>'producer',
1783
- 'IPRT'=>'part',
1784
- 'IRTD'=>'rating',
1785
- 'ISBJ'=>'subject',
1786
- 'ISFT'=>'software',
1787
- 'ISGN'=>'secondarygenre',
1788
- 'ISHP'=>'sharpness',
1789
- 'ISRC'=>'sourcesupplier',
1790
- 'ISRF'=>'digitizationsource',
1791
- 'ISTD'=>'productionstudio',
1792
- 'ISTR'=>'starring',
1793
- 'ITCH'=>'encoded_by',
1794
- 'IWEB'=>'url',
1795
- 'IWRI'=>'writer',
1796
- '____'=>'comment',
1797
- );
1798
- foreach ($RIFFinfoKeyLookup as $key => $value) {
1799
- if (isset($RIFFinfoArray[$key])) {
1800
- foreach ($RIFFinfoArray[$key] as $commentid => $commentdata) {
1801
- if (trim($commentdata['data']) != '') {
1802
- if (isset($CommentsTargetArray[$value])) {
1803
- $CommentsTargetArray[$value][] = trim($commentdata['data']);
1804
- } else {
1805
- $CommentsTargetArray[$value] = array(trim($commentdata['data']));
1806
- }
1807
- }
1808
- }
1809
- }
1810
- }
1811
- return true;
1812
- }
1813
-
1814
- public static function parseWAVEFORMATex($WaveFormatExData) {
1815
- // shortcut
1816
- $WaveFormatEx['raw'] = array();
1817
- $WaveFormatEx_raw = &$WaveFormatEx['raw'];
1818
-
1819
- $WaveFormatEx_raw['wFormatTag'] = substr($WaveFormatExData, 0, 2);
1820
- $WaveFormatEx_raw['nChannels'] = substr($WaveFormatExData, 2, 2);
1821
- $WaveFormatEx_raw['nSamplesPerSec'] = substr($WaveFormatExData, 4, 4);
1822
- $WaveFormatEx_raw['nAvgBytesPerSec'] = substr($WaveFormatExData, 8, 4);
1823
- $WaveFormatEx_raw['nBlockAlign'] = substr($WaveFormatExData, 12, 2);
1824
- $WaveFormatEx_raw['wBitsPerSample'] = substr($WaveFormatExData, 14, 2);
1825
- if (strlen($WaveFormatExData) > 16) {
1826
- $WaveFormatEx_raw['cbSize'] = substr($WaveFormatExData, 16, 2);
1827
- }
1828
- $WaveFormatEx_raw = array_map('getid3_lib::LittleEndian2Int', $WaveFormatEx_raw);
1829
-
1830
- $WaveFormatEx['codec'] = self::wFormatTagLookup($WaveFormatEx_raw['wFormatTag']);
1831
- $WaveFormatEx['channels'] = $WaveFormatEx_raw['nChannels'];
1832
- $WaveFormatEx['sample_rate'] = $WaveFormatEx_raw['nSamplesPerSec'];
1833
- $WaveFormatEx['bitrate'] = $WaveFormatEx_raw['nAvgBytesPerSec'] * 8;
1834
- $WaveFormatEx['bits_per_sample'] = $WaveFormatEx_raw['wBitsPerSample'];
1835
-
1836
- return $WaveFormatEx;
1837
- }
1838
-
1839
- public function parseWavPackHeader($WavPackChunkData) {
1840
- // typedef struct {
1841
- // char ckID [4];
1842
- // long ckSize;
1843
- // short version;
1844
- // short bits; // added for version 2.00
1845
- // short flags, shift; // added for version 3.00
1846
- // long total_samples, crc, crc2;
1847
- // char extension [4], extra_bc, extras [3];
1848
- // } WavpackHeader;
1849
-
1850
- // shortcut
1851
- $info = &$this->getid3->info;
1852
- $info['wavpack'] = array();
1853
- $thisfile_wavpack = &$info['wavpack'];
1854
-
1855
- $thisfile_wavpack['version'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 0, 2));
1856
- if ($thisfile_wavpack['version'] >= 2) {
1857
- $thisfile_wavpack['bits'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 2, 2));
1858
- }
1859
- if ($thisfile_wavpack['version'] >= 3) {
1860
- $thisfile_wavpack['flags_raw'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 4, 2));
1861
- $thisfile_wavpack['shift'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 6, 2));
1862
- $thisfile_wavpack['total_samples'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 8, 4));
1863
- $thisfile_wavpack['crc1'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 12, 4));
1864
- $thisfile_wavpack['crc2'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 16, 4));
1865
- $thisfile_wavpack['extension'] = substr($WavPackChunkData, 20, 4);
1866
- $thisfile_wavpack['extra_bc'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 24, 1));
1867
- for ($i = 0; $i <= 2; $i++) {
1868
- $thisfile_wavpack['extras'][] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 25 + $i, 1));
1869
- }
1870
-
1871
- // shortcut
1872
- $thisfile_wavpack['flags'] = array();
1873
- $thisfile_wavpack_flags = &$thisfile_wavpack['flags'];
1874
-
1875
- $thisfile_wavpack_flags['mono'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000001);
1876
- $thisfile_wavpack_flags['fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000002);
1877
- $thisfile_wavpack_flags['raw_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000004);
1878
- $thisfile_wavpack_flags['calc_noise'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000008);
1879
- $thisfile_wavpack_flags['high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000010);
1880
- $thisfile_wavpack_flags['3_byte_samples'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000020);
1881
- $thisfile_wavpack_flags['over_20_bits'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000040);
1882
- $thisfile_wavpack_flags['use_wvc'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000080);
1883
- $thisfile_wavpack_flags['noiseshaping'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000100);
1884
- $thisfile_wavpack_flags['very_fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000200);
1885
- $thisfile_wavpack_flags['new_high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000400);
1886
- $thisfile_wavpack_flags['cancel_extreme'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000800);
1887
- $thisfile_wavpack_flags['cross_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x001000);
1888
- $thisfile_wavpack_flags['new_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x002000);
1889
- $thisfile_wavpack_flags['joint_stereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x004000);
1890
- $thisfile_wavpack_flags['extra_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x008000);
1891
- $thisfile_wavpack_flags['override_noiseshape'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x010000);
1892
- $thisfile_wavpack_flags['override_jointstereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x020000);
1893
- $thisfile_wavpack_flags['copy_source_filetime'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x040000);
1894
- $thisfile_wavpack_flags['create_exe'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x080000);
1895
- }
1896
-
1897
- return true;
1898
- }
1899
-
1900
- public static function ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true) {
1901
-
1902
- $parsed['biSize'] = substr($BITMAPINFOHEADER, 0, 4); // number of bytes required by the BITMAPINFOHEADER structure
1903
- $parsed['biWidth'] = substr($BITMAPINFOHEADER, 4, 4); // width of the bitmap in pixels
1904
- $parsed['biHeight'] = substr($BITMAPINFOHEADER, 8, 4); // height of the bitmap in pixels. If biHeight is positive, the bitmap is a 'bottom-up' DIB and its origin is the lower left corner. If biHeight is negative, the bitmap is a 'top-down' DIB and its origin is the upper left corner
1905
- $parsed['biPlanes'] = substr($BITMAPINFOHEADER, 12, 2); // number of color planes on the target device. In most cases this value must be set to 1
1906
- $parsed['biBitCount'] = substr($BITMAPINFOHEADER, 14, 2); // Specifies the number of bits per pixels
1907
- $parsed['biSizeImage'] = substr($BITMAPINFOHEADER, 20, 4); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures)
1908
- $parsed['biXPelsPerMeter'] = substr($BITMAPINFOHEADER, 24, 4); // horizontal resolution, in pixels per metre, of the target device
1909
- $parsed['biYPelsPerMeter'] = substr($BITMAPINFOHEADER, 28, 4); // vertical resolution, in pixels per metre, of the target device
1910
- $parsed['biClrUsed'] = substr($BITMAPINFOHEADER, 32, 4); // actual number of color indices in the color table used by the bitmap. If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression
1911
- $parsed['biClrImportant'] = substr($BITMAPINFOHEADER, 36, 4); // number of color indices that are considered important for displaying the bitmap. If this value is zero, all colors are important
1912
- $parsed = array_map('getid3_lib::'.($littleEndian ? 'Little' : 'Big').'Endian2Int', $parsed);
1913
-
1914
- $parsed['fourcc'] = substr($BITMAPINFOHEADER, 16, 4); // compression identifier
1915
-
1916
- return $parsed;
1917
- }
1918
-
1919
- public static function ParseDIVXTAG($DIVXTAG, $raw=false) {
1920
- // structure from "IDivX" source, Form1.frm, by "Greg Frazier of Daemonic Software Group", email: gfrazier@icestorm.net, web: http://dsg.cjb.net/
1921
- // source available at http://files.divx-digest.com/download/c663efe7ef8ad2e90bf4af4d3ea6188a/on0SWN2r/edit/IDivX.zip
1922
- // 'Byte Layout: '1111111111111111
1923
- // '32 for Movie - 1 '1111111111111111
1924
- // '28 for Author - 6 '6666666666666666
1925
- // '4 for year - 2 '6666666666662222
1926
- // '3 for genre - 3 '7777777777777777
1927
- // '48 for Comments - 7 '7777777777777777
1928
- // '1 for Rating - 4 '7777777777777777
1929
- // '5 for Future Additions - 0 '333400000DIVXTAG
1930
- // '128 bytes total
1931
-
1932
- static $DIVXTAGgenre = array(
1933
- 0 => 'Action',
1934
- 1 => 'Action/Adventure',
1935
- 2 => 'Adventure',
1936
- 3 => 'Adult',
1937
- 4 => 'Anime',
1938
- 5 => 'Cartoon',
1939
- 6 => 'Claymation',
1940
- 7 => 'Comedy',
1941
- 8 => 'Commercial',
1942
- 9 => 'Documentary',
1943
- 10 => 'Drama',
1944
- 11 => 'Home Video',
1945
- 12 => 'Horror',
1946
- 13 => 'Infomercial',
1947
- 14 => 'Interactive',
1948
- 15 => 'Mystery',
1949
- 16 => 'Music Video',
1950
- 17 => 'Other',
1951
- 18 => 'Religion',
1952
- 19 => 'Sci Fi',
1953
- 20 => 'Thriller',
1954
- 21 => 'Western',
1955
- ),
1956
- $DIVXTAGrating = array(
1957
- 0 => 'Unrated',
1958
- 1 => 'G',
1959
- 2 => 'PG',
1960
- 3 => 'PG-13',
1961
- 4 => 'R',
1962
- 5 => 'NC-17',
1963
- );
1964
-
1965
- $parsed['title'] = trim(substr($DIVXTAG, 0, 32));
1966
- $parsed['artist'] = trim(substr($DIVXTAG, 32, 28));
1967
- $parsed['year'] = intval(trim(substr($DIVXTAG, 60, 4)));
1968
- $parsed['comment'] = trim(substr($DIVXTAG, 64, 48));
1969
- $parsed['genre_id'] = intval(trim(substr($DIVXTAG, 112, 3)));
1970
- $parsed['rating_id'] = ord(substr($DIVXTAG, 115, 1));
1971
- //$parsed['padding'] = substr($DIVXTAG, 116, 5); // 5-byte null
1972
- //$parsed['magic'] = substr($DIVXTAG, 121, 7); // "DIVXTAG"
1973
-
1974
- $parsed['genre'] = (isset($DIVXTAGgenre[$parsed['genre_id']]) ? $DIVXTAGgenre[$parsed['genre_id']] : $parsed['genre_id']);
1975
- $parsed['rating'] = (isset($DIVXTAGrating[$parsed['rating_id']]) ? $DIVXTAGrating[$parsed['rating_id']] : $parsed['rating_id']);
1976
-
1977
- if (!$raw) {
1978
- unset($parsed['genre_id'], $parsed['rating_id']);
1979
- foreach ($parsed as $key => $value) {
1980
- if (!$value === '') {
1981
- unset($parsed['key']);
1982
- }
1983
- }
1984
- }
1985
-
1986
- foreach ($parsed as $tag => $value) {
1987
- $parsed[$tag] = array($value);
1988
- }
1989
-
1990
- return $parsed;
1991
- }
1992
-
1993
- public static function waveSNDMtagLookup($tagshortname) {
1994
- $begin = __LINE__;
1995
-
1996
- /** This is not a comment!
1997
-
1998
- ©kwd keywords
1999
- ©BPM bpm
2000
- ©trt tracktitle
2001
- ©des description
2002
- ©gen category
2003
- ©fin featuredinstrument
2004
- ©LID longid
2005
- ©bex bwdescription
2006
- ©pub publisher
2007
- ©cdt cdtitle
2008
- ©alb library
2009
- ©com composer
2010
-
2011
- */
2012
-
2013
- return getid3_lib::EmbeddedLookup($tagshortname, $begin, __LINE__, __FILE__, 'riff-sndm');
2014
- }
2015
-
2016
- public static function wFormatTagLookup($wFormatTag) {
2017
-
2018
- $begin = __LINE__;
2019
-
2020
- /** This is not a comment!
2021
-
2022
- 0x0000 Microsoft Unknown Wave Format
2023
- 0x0001 Pulse Code Modulation (PCM)
2024
- 0x0002 Microsoft ADPCM
2025
- 0x0003 IEEE Float
2026
- 0x0004 Compaq Computer VSELP
2027
- 0x0005 IBM CVSD
2028
- 0x0006 Microsoft A-Law
2029
- 0x0007 Microsoft mu-Law
2030
- 0x0008 Microsoft DTS
2031
- 0x0010 OKI ADPCM
2032
- 0x0011 Intel DVI/IMA ADPCM
2033
- 0x0012 Videologic MediaSpace ADPCM
2034
- 0x0013 Sierra Semiconductor ADPCM
2035
- 0x0014 Antex Electronics G.723 ADPCM
2036
- 0x0015 DSP Solutions DigiSTD
2037
- 0x0016 DSP Solutions DigiFIX
2038
- 0x0017 Dialogic OKI ADPCM
2039
- 0x0018 MediaVision ADPCM
2040
- 0x0019 Hewlett-Packard CU
2041
- 0x0020 Yamaha ADPCM
2042
- 0x0021 Speech Compression Sonarc
2043
- 0x0022 DSP Group TrueSpeech
2044
- 0x0023 Echo Speech EchoSC1
2045
- 0x0024 Audiofile AF36
2046
- 0x0025 Audio Processing Technology APTX
2047
- 0x0026 AudioFile AF10
2048
- 0x0027 Prosody 1612
2049
- 0x0028 LRC
2050
- 0x0030 Dolby AC2
2051
- 0x0031 Microsoft GSM 6.10
2052
- 0x0032 MSNAudio
2053
- 0x0033 Antex Electronics ADPCME
2054
- 0x0034 Control Resources VQLPC
2055
- 0x0035 DSP Solutions DigiREAL
2056
- 0x0036 DSP Solutions DigiADPCM
2057
- 0x0037 Control Resources CR10
2058
- 0x0038 Natural MicroSystems VBXADPCM
2059
- 0x0039 Crystal Semiconductor IMA ADPCM
2060
- 0x003A EchoSC3
2061
- 0x003B Rockwell ADPCM
2062
- 0x003C Rockwell Digit LK
2063
- 0x003D Xebec
2064
- 0x0040 Antex Electronics G.721 ADPCM
2065
- 0x0041 G.728 CELP
2066
- 0x0042 MSG723
2067
- 0x0050 MPEG Layer-2 or Layer-1
2068
- 0x0052 RT24
2069
- 0x0053 PAC
2070
- 0x0055 MPEG Layer-3
2071
- 0x0059 Lucent G.723
2072
- 0x0060 Cirrus
2073
- 0x0061 ESPCM
2074
- 0x0062 Voxware
2075
- 0x0063 Canopus Atrac
2076
- 0x0064 G.726 ADPCM
2077
- 0x0065 G.722 ADPCM
2078
- 0x0066 DSAT
2079
- 0x0067 DSAT Display
2080
- 0x0069 Voxware Byte Aligned
2081
- 0x0070 Voxware AC8
2082
- 0x0071 Voxware AC10
2083
- 0x0072 Voxware AC16
2084
- 0x0073 Voxware AC20
2085
- 0x0074 Voxware MetaVoice
2086
- 0x0075 Voxware MetaSound
2087
- 0x0076 Voxware RT29HW
2088
- 0x0077 Voxware VR12
2089
- 0x0078 Voxware VR18
2090
- 0x0079 Voxware TQ40
2091
- 0x0080 Softsound
2092
- 0x0081 Voxware TQ60
2093
- 0x0082 MSRT24
2094
- 0x0083 G.729A
2095
- 0x0084 MVI MV12
2096
- 0x0085 DF G.726
2097
- 0x0086 DF GSM610
2098
- 0x0088 ISIAudio
2099
- 0x0089 Onlive
2100
- 0x0091 SBC24
2101
- 0x0092 Dolby AC3 SPDIF
2102
- 0x0093 MediaSonic G.723
2103
- 0x0094 Aculab PLC Prosody 8kbps
2104
- 0x0097 ZyXEL ADPCM
2105
- 0x0098 Philips LPCBB
2106
- 0x0099 Packed
2107
- 0x00FF AAC
2108
- 0x0100 Rhetorex ADPCM
2109
- 0x0101 IBM mu-law
2110
- 0x0102 IBM A-law
2111
- 0x0103 IBM AVC Adaptive Differential Pulse Code Modulation (ADPCM)
2112
- 0x0111 Vivo G.723
2113
- 0x0112 Vivo Siren
2114
- 0x0123 Digital G.723
2115
- 0x0125 Sanyo LD ADPCM
2116
- 0x0130 Sipro Lab Telecom ACELP NET
2117
- 0x0131 Sipro Lab Telecom ACELP 4800
2118
- 0x0132 Sipro Lab Telecom ACELP 8V3
2119
- 0x0133 Sipro Lab Telecom G.729
2120
- 0x0134 Sipro Lab Telecom G.729A
2121
- 0x0135 Sipro Lab Telecom Kelvin
2122
- 0x0140 Windows Media Video V8
2123
- 0x0150 Qualcomm PureVoice
2124
- 0x0151 Qualcomm HalfRate
2125
- 0x0155 Ring Zero Systems TUB GSM
2126
- 0x0160 Microsoft Audio 1
2127
- 0x0161 Windows Media Audio V7 / V8 / V9
2128
- 0x0162 Windows Media Audio Professional V9
2129
- 0x0163 Windows Media Audio Lossless V9
2130
- 0x0200 Creative Labs ADPCM
2131
- 0x0202 Creative Labs Fastspeech8
2132
- 0x0203 Creative Labs Fastspeech10
2133
- 0x0210 UHER Informatic GmbH ADPCM
2134
- 0x0220 Quarterdeck
2135
- 0x0230 I-link Worldwide VC
2136
- 0x0240 Aureal RAW Sport
2137
- 0x0250 Interactive Products HSX
2138
- 0x0251 Interactive Products RPELP
2139
- 0x0260 Consistent Software CS2
2140
- 0x0270 Sony SCX
2141
- 0x0300 Fujitsu FM Towns Snd
2142
- 0x0400 BTV Digital
2143
- 0x0401 Intel Music Coder
2144
- 0x0450 QDesign Music
2145
- 0x0680 VME VMPCM
2146
- 0x0681 AT&T Labs TPC
2147
- 0x08AE ClearJump LiteWave
2148
- 0x1000 Olivetti GSM
2149
- 0x1001 Olivetti ADPCM
2150
- 0x1002 Olivetti CELP
2151
- 0x1003 Olivetti SBC
2152
- 0x1004 Olivetti OPR
2153
- 0x1100 Lernout & Hauspie Codec (0x1100)
2154
- 0x1101 Lernout & Hauspie CELP Codec (0x1101)
2155
- 0x1102 Lernout & Hauspie SBC Codec (0x1102)
2156
- 0x1103 Lernout & Hauspie SBC Codec (0x1103)
2157
- 0x1104 Lernout & Hauspie SBC Codec (0x1104)
2158
- 0x1400 Norris
2159
- 0x1401 AT&T ISIAudio
2160
- 0x1500 Soundspace Music Compression
2161
- 0x181C VoxWare RT24 Speech
2162
- 0x1FC4 NCT Soft ALF2CD (www.nctsoft.com)
2163
- 0x2000 Dolby AC3
2164
- 0x2001 Dolby DTS
2165
- 0x2002 WAVE_FORMAT_14_4
2166
- 0x2003 WAVE_FORMAT_28_8
2167
- 0x2004 WAVE_FORMAT_COOK
2168
- 0x2005 WAVE_FORMAT_DNET
2169
- 0x674F Ogg Vorbis 1
2170
- 0x6750 Ogg Vorbis 2
2171
- 0x6751 Ogg Vorbis 3
2172
- 0x676F Ogg Vorbis 1+
2173
- 0x6770 Ogg Vorbis 2+
2174
- 0x6771 Ogg Vorbis 3+
2175
- 0x7A21 GSM-AMR (CBR, no SID)
2176
- 0x7A22 GSM-AMR (VBR, including SID)
2177
- 0xFFFE WAVE_FORMAT_EXTENSIBLE
2178
- 0xFFFF WAVE_FORMAT_DEVELOPMENT
2179
-
2180
- */
2181
-
2182
- return getid3_lib::EmbeddedLookup('0x'.str_pad(strtoupper(dechex($wFormatTag)), 4, '0', STR_PAD_LEFT), $begin, __LINE__, __FILE__, 'riff-wFormatTag');
2183
- }
2184
-
2185
- public static function fourccLookup($fourcc) {
2186
-
2187
- $begin = __LINE__;
2188
-
2189
- /** This is not a comment!
2190
-
2191
- swot http://developer.apple.com/qa/snd/snd07.html
2192
- ____ No Codec (____)
2193
- _BIT BI_BITFIELDS (Raw RGB)
2194
- _JPG JPEG compressed
2195
- _PNG PNG compressed W3C/ISO/IEC (RFC-2083)
2196
- _RAW Full Frames (Uncompressed)
2197
- _RGB Raw RGB Bitmap
2198
- _RL4 RLE 4bpp RGB
2199
- _RL8 RLE 8bpp RGB
2200
- 3IV1 3ivx MPEG-4 v1
2201
- 3IV2 3ivx MPEG-4 v2
2202
- 3IVX 3ivx MPEG-4
2203
- AASC Autodesk Animator
2204
- ABYR Kensington ?ABYR?
2205
- AEMI Array Microsystems VideoONE MPEG1-I Capture
2206
- AFLC Autodesk Animator FLC
2207
- AFLI Autodesk Animator FLI
2208
- AMPG Array Microsystems VideoONE MPEG
2209
- ANIM Intel RDX (ANIM)
2210
- AP41 AngelPotion Definitive
2211
- ASV1 Asus Video v1
2212
- ASV2 Asus Video v2
2213
- ASVX Asus Video 2.0 (audio)
2214
- AUR2 AuraVision Aura 2 Codec - YUV 4:2:2
2215
- AURA AuraVision Aura 1 Codec - YUV 4:1:1
2216
- AVDJ Independent JPEG Group\'s codec (AVDJ)
2217
- AVRN Independent JPEG Group\'s codec (AVRN)
2218
- AYUV 4:4:4 YUV (AYUV)
2219
- AZPR Quicktime Apple Video (AZPR)
2220
- BGR Raw RGB32
2221
- BLZ0 Blizzard DivX MPEG-4
2222
- BTVC Conexant Composite Video
2223
- BINK RAD Game Tools Bink Video
2224
- BT20 Conexant Prosumer Video
2225
- BTCV Conexant Composite Video Codec
2226
- BW10 Data Translation Broadway MPEG Capture
2227
- CC12 Intel YUV12
2228
- CDVC Canopus DV
2229
- CFCC Digital Processing Systems DPS Perception
2230
- CGDI Microsoft Office 97 Camcorder Video
2231
- CHAM Winnov Caviara Champagne
2232
- CJPG Creative WebCam JPEG
2233
- CLJR Cirrus Logic YUV 4:1:1
2234
- CMYK Common Data Format in Printing (Colorgraph)
2235
- CPLA Weitek 4:2:0 YUV Planar
2236
- CRAM Microsoft Video 1 (CRAM)
2237
- cvid Radius Cinepak
2238
- CVID Radius Cinepak
2239
- CWLT Microsoft Color WLT DIB
2240
- CYUV Creative Labs YUV
2241
- CYUY ATI YUV
2242
- D261 H.261
2243
- D263 H.263
2244
- DIB Device Independent Bitmap
2245
- DIV1 FFmpeg OpenDivX
2246
- DIV2 Microsoft MPEG-4 v1/v2
2247
- DIV3 DivX ;-) MPEG-4 v3.x Low-Motion
2248
- DIV4 DivX ;-) MPEG-4 v3.x Fast-Motion
2249
- DIV5 DivX MPEG-4 v5.x
2250
- DIV6 DivX ;-) (MS MPEG-4 v3.x)
2251
- DIVX DivX MPEG-4 v4 (OpenDivX / Project Mayo)
2252
- divx DivX MPEG-4
2253
- DMB1 Matrox Rainbow Runner hardware MJPEG
2254
- DMB2 Paradigm MJPEG
2255
- DSVD ?DSVD?
2256
- DUCK Duck TrueMotion 1.0
2257
- DPS0 DPS/Leitch Reality Motion JPEG
2258
- DPSC DPS/Leitch PAR Motion JPEG
2259
- DV25 Matrox DVCPRO codec
2260
- DV50 Matrox DVCPRO50 codec
2261
- DVC IEC 61834 and SMPTE 314M (DVC/DV Video)
2262
- DVCP IEC 61834 and SMPTE 314M (DVC/DV Video)
2263
- DVHD IEC Standard DV 1125 lines @ 30fps / 1250 lines @ 25fps
2264
- DVMA Darim Vision DVMPEG (dummy for MPEG compressor) (www.darvision.com)
2265
- DVSL IEC Standard DV compressed in SD (SDL)
2266
- DVAN ?DVAN?
2267
- DVE2 InSoft DVE-2 Videoconferencing
2268
- dvsd IEC 61834 and SMPTE 314M DVC/DV Video
2269
- DVSD IEC 61834 and SMPTE 314M DVC/DV Video
2270
- DVX1 Lucent DVX1000SP Video Decoder
2271
- DVX2 Lucent DVX2000S Video Decoder
2272
- DVX3 Lucent DVX3000S Video Decoder
2273
- DX50 DivX v5
2274
- DXT1 Microsoft DirectX Compressed Texture (DXT1)
2275
- DXT2 Microsoft DirectX Compressed Texture (DXT2)
2276
- DXT3 Microsoft DirectX Compressed Texture (DXT3)
2277
- DXT4 Microsoft DirectX Compressed Texture (DXT4)
2278
- DXT5 Microsoft DirectX Compressed Texture (DXT5)
2279
- DXTC Microsoft DirectX Compressed Texture (DXTC)
2280
- DXTn Microsoft DirectX Compressed Texture (DXTn)
2281
- EM2V Etymonix MPEG-2 I-frame (www.etymonix.com)
2282
- EKQ0 Elsa ?EKQ0?
2283
- ELK0 Elsa ?ELK0?
2284
- ESCP Eidos Escape
2285
- ETV1 eTreppid Video ETV1
2286
- ETV2 eTreppid Video ETV2
2287
- ETVC eTreppid Video ETVC
2288
- FLIC Autodesk FLI/FLC Animation
2289
- FLV1 Sorenson Spark
2290
- FLV4 On2 TrueMotion VP6
2291
- FRWT Darim Vision Forward Motion JPEG (www.darvision.com)
2292
- FRWU Darim Vision Forward Uncompressed (www.darvision.com)
2293
- FLJP D-Vision Field Encoded Motion JPEG
2294
- FPS1 FRAPS v1
2295
- FRWA SoftLab-Nsk Forward Motion JPEG w/ alpha channel
2296
- FRWD SoftLab-Nsk Forward Motion JPEG
2297
- FVF1 Iterated Systems Fractal Video Frame
2298
- GLZW Motion LZW (gabest@freemail.hu)
2299
- GPEG Motion JPEG (gabest@freemail.hu)
2300
- GWLT Microsoft Greyscale WLT DIB
2301
- H260 Intel ITU H.260 Videoconferencing
2302
- H261 Intel ITU H.261 Videoconferencing
2303
- H262 Intel ITU H.262 Videoconferencing
2304
- H263 Intel ITU H.263 Videoconferencing
2305
- H264 Intel ITU H.264 Videoconferencing
2306
- H265 Intel ITU H.265 Videoconferencing
2307
- H266 Intel ITU H.266 Videoconferencing
2308
- H267 Intel ITU H.267 Videoconferencing
2309
- H268 Intel ITU H.268 Videoconferencing
2310
- H269 Intel ITU H.269 Videoconferencing
2311
- HFYU Huffman Lossless Codec
2312
- HMCR Rendition Motion Compensation Format (HMCR)
2313
- HMRR Rendition Motion Compensation Format (HMRR)
2314
- I263 FFmpeg I263 decoder
2315
- IF09 Indeo YVU9 ("YVU9 with additional delta-frame info after the U plane")
2316
- IUYV Interlaced version of UYVY (www.leadtools.com)
2317
- IY41 Interlaced version of Y41P (www.leadtools.com)
2318
- IYU1 12 bit format used in mode 2 of the IEEE 1394 Digital Camera 1.04 spec IEEE standard
2319
- IYU2 24 bit format used in mode 2 of the IEEE 1394 Digital Camera 1.04 spec IEEE standard
2320
- IYUV Planar YUV format (8-bpp Y plane, followed by 8-bpp 2×2 U and V planes)
2321
- i263 Intel ITU H.263 Videoconferencing (i263)
2322
- I420 Intel Indeo 4
2323
- IAN Intel Indeo 4 (RDX)
2324
- ICLB InSoft CellB Videoconferencing
2325
- IGOR Power DVD
2326
- IJPG Intergraph JPEG
2327
- ILVC Intel Layered Video
2328
- ILVR ITU-T H.263+
2329
- IPDV I-O Data Device Giga AVI DV Codec
2330
- IR21 Intel Indeo 2.1
2331
- IRAW Intel YUV Uncompressed
2332
- IV30 Intel Indeo 3.0
2333
- IV31 Intel Indeo 3.1
2334
- IV32 Ligos Indeo 3.2
2335
- IV33 Ligos Indeo 3.3
2336
- IV34 Ligos Indeo 3.4
2337
- IV35 Ligos Indeo 3.5
2338
- IV36 Ligos Indeo 3.6
2339
- IV37 Ligos Indeo 3.7
2340
- IV38 Ligos Indeo 3.8
2341
- IV39 Ligos Indeo 3.9
2342
- IV40 Ligos Indeo Interactive 4.0
2343
- IV41 Ligos Indeo Interactive 4.1
2344
- IV42 Ligos Indeo Interactive 4.2
2345
- IV43 Ligos Indeo Interactive 4.3
2346
- IV44 Ligos Indeo Interactive 4.4
2347
- IV45 Ligos Indeo Interactive 4.5
2348
- IV46 Ligos Indeo Interactive 4.6
2349
- IV47 Ligos Indeo Interactive 4.7
2350
- IV48 Ligos Indeo Interactive 4.8
2351
- IV49 Ligos Indeo Interactive 4.9
2352
- IV50 Ligos Indeo Interactive 5.0
2353
- JBYR Kensington ?JBYR?
2354
- JPEG Still Image JPEG DIB
2355
- JPGL Pegasus Lossless Motion JPEG
2356
- KMVC Team17 Software Karl Morton\'s Video Codec
2357
- LSVM Vianet Lighting Strike Vmail (Streaming) (www.vianet.com)
2358
- LEAD LEAD Video Codec
2359
- Ljpg LEAD MJPEG Codec
2360
- MDVD Alex MicroDVD Video (hacked MS MPEG-4) (www.tiasoft.de)
2361
- MJPA Morgan Motion JPEG (MJPA) (www.morgan-multimedia.com)
2362
- MJPB Morgan Motion JPEG (MJPB) (www.morgan-multimedia.com)
2363
- MMES Matrox MPEG-2 I-frame
2364
- MP2v Microsoft S-Mpeg 4 version 1 (MP2v)
2365
- MP42 Microsoft S-Mpeg 4 version 2 (MP42)
2366
- MP43 Microsoft S-Mpeg 4 version 3 (MP43)
2367
- MP4S Microsoft S-Mpeg 4 version 3 (MP4S)
2368
- MP4V FFmpeg MPEG-4
2369
- MPG1 FFmpeg MPEG 1/2
2370
- MPG2 FFmpeg MPEG 1/2
2371
- MPG3 FFmpeg DivX ;-) (MS MPEG-4 v3)
2372
- MPG4 Microsoft MPEG-4
2373
- MPGI Sigma Designs MPEG
2374
- MPNG PNG images decoder
2375
- MSS1 Microsoft Windows Screen Video
2376
- MSZH LCL (Lossless Codec Library) (www.geocities.co.jp/Playtown-Denei/2837/LRC.htm)
2377
- M261 Microsoft H.261
2378
- M263 Microsoft H.263
2379
- M4S2 Microsoft Fully Compliant MPEG-4 v2 simple profile (M4S2)
2380
- m4s2 Microsoft Fully Compliant MPEG-4 v2 simple profile (m4s2)
2381
- MC12 ATI Motion Compensation Format (MC12)
2382
- MCAM ATI Motion Compensation Format (MCAM)
2383
- MJ2C Morgan Multimedia Motion JPEG2000
2384
- mJPG IBM Motion JPEG w/ Huffman Tables
2385
- MJPG Microsoft Motion JPEG DIB
2386
- MP42 Microsoft MPEG-4 (low-motion)
2387
- MP43 Microsoft MPEG-4 (fast-motion)
2388
- MP4S Microsoft MPEG-4 (MP4S)
2389
- mp4s Microsoft MPEG-4 (mp4s)
2390
- MPEG Chromatic Research MPEG-1 Video I-Frame
2391
- MPG4 Microsoft MPEG-4 Video High Speed Compressor
2392
- MPGI Sigma Designs MPEG
2393
- MRCA FAST Multimedia Martin Regen Codec
2394
- MRLE Microsoft Run Length Encoding
2395
- MSVC Microsoft Video 1
2396
- MTX1 Matrox ?MTX1?
2397
- MTX2 Matrox ?MTX2?
2398
- MTX3 Matrox ?MTX3?
2399
- MTX4 Matrox ?MTX4?
2400
- MTX5 Matrox ?MTX5?
2401
- MTX6 Matrox ?MTX6?
2402
- MTX7 Matrox ?MTX7?
2403
- MTX8 Matrox ?MTX8?
2404
- MTX9 Matrox ?MTX9?
2405
- MV12 Motion Pixels Codec (old)
2406
- MWV1 Aware Motion Wavelets
2407
- nAVI SMR Codec (hack of Microsoft MPEG-4) (IRC #shadowrealm)
2408
- NT00 NewTek LightWave HDTV YUV w/ Alpha (www.newtek.com)
2409
- NUV1 NuppelVideo
2410
- NTN1 Nogatech Video Compression 1
2411
- NVS0 nVidia GeForce Texture (NVS0)
2412
- NVS1 nVidia GeForce Texture (NVS1)
2413
- NVS2 nVidia GeForce Texture (NVS2)
2414
- NVS3 nVidia GeForce Texture (NVS3)
2415
- NVS4 nVidia GeForce Texture (NVS4)
2416
- NVS5 nVidia GeForce Texture (NVS5)
2417
- NVT0 nVidia GeForce Texture (NVT0)
2418
- NVT1 nVidia GeForce Texture (NVT1)
2419
- NVT2 nVidia GeForce Texture (NVT2)
2420
- NVT3 nVidia GeForce Texture (NVT3)
2421
- NVT4 nVidia GeForce Texture (NVT4)
2422
- NVT5 nVidia GeForce Texture (NVT5)
2423
- PIXL MiroXL, Pinnacle PCTV
2424
- PDVC I-O Data Device Digital Video Capture DV codec
2425
- PGVV Radius Video Vision
2426
- PHMO IBM Photomotion
2427
- PIM1 MPEG Realtime (Pinnacle Cards)
2428
- PIM2 Pegasus Imaging ?PIM2?
2429
- PIMJ Pegasus Imaging Lossless JPEG
2430
- PVEZ Horizons Technology PowerEZ
2431
- PVMM PacketVideo Corporation MPEG-4
2432
- PVW2 Pegasus Imaging Wavelet Compression
2433
- Q1.0 Q-Team\'s QPEG 1.0 (www.q-team.de)
2434
- Q1.1 Q-Team\'s QPEG 1.1 (www.q-team.de)
2435
- QPEG Q-Team QPEG 1.0
2436
- qpeq Q-Team QPEG 1.1
2437
- RGB Raw BGR32
2438
- RGBA Raw RGB w/ Alpha
2439
- RMP4 REALmagic MPEG-4 (unauthorized XVID copy) (www.sigmadesigns.com)
2440
- ROQV Id RoQ File Video Decoder
2441
- RPZA Quicktime Apple Video (RPZA)
2442
- RUD0 Rududu video codec (http://rududu.ifrance.com/rududu/)
2443
- RV10 RealVideo 1.0 (aka RealVideo 5.0)
2444
- RV13 RealVideo 1.0 (RV13)
2445
- RV20 RealVideo G2
2446
- RV30 RealVideo 8
2447
- RV40 RealVideo 9
2448
- RGBT Raw RGB w/ Transparency
2449
- RLE Microsoft Run Length Encoder
2450
- RLE4 Run Length Encoded (4bpp, 16-color)
2451
- RLE8 Run Length Encoded (8bpp, 256-color)
2452
- RT21 Intel Indeo RealTime Video 2.1
2453
- rv20 RealVideo G2
2454
- rv30 RealVideo 8
2455
- RVX Intel RDX (RVX )
2456
- SMC Apple Graphics (SMC )
2457
- SP54 Logitech Sunplus Sp54 Codec for Mustek GSmart Mini 2
2458
- SPIG Radius Spigot
2459
- SVQ3 Sorenson Video 3 (Apple Quicktime 5)
2460
- s422 Tekram VideoCap C210 YUV 4:2:2
2461
- SDCC Sun Communication Digital Camera Codec
2462
- SFMC CrystalNet Surface Fitting Method
2463
- SMSC Radius SMSC
2464
- SMSD Radius SMSD
2465
- smsv WorldConnect Wavelet Video
2466
- SPIG Radius Spigot
2467
- SPLC Splash Studios ACM Audio Codec (www.splashstudios.net)
2468
- SQZ2 Microsoft VXTreme Video Codec V2
2469
- STVA ST Microelectronics CMOS Imager Data (Bayer)
2470
- STVB ST Microelectronics CMOS Imager Data (Nudged Bayer)
2471
- STVC ST Microelectronics CMOS Imager Data (Bunched)
2472
- STVX ST Microelectronics CMOS Imager Data (Extended CODEC Data Format)
2473
- STVY ST Microelectronics CMOS Imager Data (Extended CODEC Data Format with Correction Data)
2474
- SV10 Sorenson Video R1
2475
- SVQ1 Sorenson Video
2476
- T420 Toshiba YUV 4:2:0
2477
- TM2A Duck TrueMotion Archiver 2.0 (www.duck.com)
2478
- TVJP Pinnacle/Truevision Targa 2000 board (TVJP)
2479
- TVMJ Pinnacle/Truevision Targa 2000 board (TVMJ)
2480
- TY0N Tecomac Low-Bit Rate Codec (www.tecomac.com)
2481
- TY2C Trident Decompression Driver
2482
- TLMS TeraLogic Motion Intraframe Codec (TLMS)
2483
- TLST TeraLogic Motion Intraframe Codec (TLST)
2484
- TM20 Duck TrueMotion 2.0
2485
- TM2X Duck TrueMotion 2X
2486
- TMIC TeraLogic Motion Intraframe Codec (TMIC)
2487
- TMOT Horizons Technology TrueMotion S
2488
- tmot Horizons TrueMotion Video Compression
2489
- TR20 Duck TrueMotion RealTime 2.0
2490
- TSCC TechSmith Screen Capture Codec
2491
- TV10 Tecomac Low-Bit Rate Codec
2492
- TY2N Trident ?TY2N?
2493
- U263 UB Video H.263/H.263+/H.263++ Decoder
2494
- UMP4 UB Video MPEG 4 (www.ubvideo.com)
2495
- UYNV Nvidia UYVY packed 4:2:2
2496
- UYVP Evans & Sutherland YCbCr 4:2:2 extended precision
2497
- UCOD eMajix.com ClearVideo
2498
- ULTI IBM Ultimotion
2499
- UYVY UYVY packed 4:2:2
2500
- V261 Lucent VX2000S
2501
- VIFP VFAPI Reader Codec (www.yks.ne.jp/~hori/)
2502
- VIV1 FFmpeg H263+ decoder
2503
- VIV2 Vivo H.263
2504
- VQC2 Vector-quantised codec 2 (research) http://eprints.ecs.soton.ac.uk/archive/00001310/01/VTC97-js.pdf)
2505
- VTLP Alaris VideoGramPiX
2506
- VYU9 ATI YUV (VYU9)
2507
- VYUY ATI YUV (VYUY)
2508
- V261 Lucent VX2000S
2509
- V422 Vitec Multimedia 24-bit YUV 4:2:2 Format
2510
- V655 Vitec Multimedia 16-bit YUV 4:2:2 Format
2511
- VCR1 ATI Video Codec 1
2512
- VCR2 ATI Video Codec 2
2513
- VCR3 ATI VCR 3.0
2514
- VCR4 ATI VCR 4.0
2515
- VCR5 ATI VCR 5.0
2516
- VCR6 ATI VCR 6.0
2517
- VCR7 ATI VCR 7.0
2518
- VCR8 ATI VCR 8.0
2519
- VCR9 ATI VCR 9.0
2520
- VDCT Vitec Multimedia Video Maker Pro DIB
2521
- VDOM VDOnet VDOWave
2522
- VDOW VDOnet VDOLive (H.263)
2523
- VDTZ Darim Vison VideoTizer YUV
2524
- VGPX Alaris VideoGramPiX
2525
- VIDS Vitec Multimedia YUV 4:2:2 CCIR 601 for V422
2526
- VIVO Vivo H.263 v2.00
2527
- vivo Vivo H.263
2528
- VIXL Miro/Pinnacle Video XL
2529
- VLV1 VideoLogic/PURE Digital Videologic Capture
2530
- VP30 On2 VP3.0
2531
- VP31 On2 VP3.1
2532
- VP6F On2 TrueMotion VP6
2533
- VX1K Lucent VX1000S Video Codec
2534
- VX2K Lucent VX2000S Video Codec
2535
- VXSP Lucent VX1000SP Video Codec
2536
- WBVC Winbond W9960
2537
- WHAM Microsoft Video 1 (WHAM)
2538
- WINX Winnov Software Compression
2539
- WJPG AverMedia Winbond JPEG
2540
- WMV1 Windows Media Video V7
2541
- WMV2 Windows Media Video V8
2542
- WMV3 Windows Media Video V9
2543
- WNV1 Winnov Hardware Compression
2544
- XYZP Extended PAL format XYZ palette (www.riff.org)
2545
- x263 Xirlink H.263
2546
- XLV0 NetXL Video Decoder
2547
- XMPG Xing MPEG (I-Frame only)
2548
- XVID XviD MPEG-4 (www.xvid.org)
2549
- XXAN ?XXAN?
2550
- YU92 Intel YUV (YU92)
2551
- YUNV Nvidia Uncompressed YUV 4:2:2
2552
- YUVP Extended PAL format YUV palette (www.riff.org)
2553
- Y211 YUV 2:1:1 Packed
2554
- Y411 YUV 4:1:1 Packed
2555
- Y41B Weitek YUV 4:1:1 Planar
2556
- Y41P Brooktree PC1 YUV 4:1:1 Packed
2557
- Y41T Brooktree PC1 YUV 4:1:1 with transparency
2558
- Y42B Weitek YUV 4:2:2 Planar
2559
- Y42T Brooktree UYUV 4:2:2 with transparency
2560
- Y422 ADS Technologies Copy of UYVY used in Pyro WebCam firewire camera
2561
- Y800 Simple, single Y plane for monochrome images
2562
- Y8 Grayscale video
2563
- YC12 Intel YUV 12 codec
2564
- YUV8 Winnov Caviar YUV8
2565
- YUV9 Intel YUV9
2566
- YUY2 Uncompressed YUV 4:2:2
2567
- YUYV Canopus YUV
2568
- YV12 YVU12 Planar
2569
- YVU9 Intel YVU9 Planar (8-bpp Y plane, followed by 8-bpp 4x4 U and V planes)
2570
- YVYU YVYU 4:2:2 Packed
2571
- ZLIB Lossless Codec Library zlib compression (www.geocities.co.jp/Playtown-Denei/2837/LRC.htm)
2572
- ZPEG Metheus Video Zipper
2573
-
2574
- */
2575
-
2576
- return getid3_lib::EmbeddedLookup($fourcc, $begin, __LINE__, __FILE__, 'riff-fourcc');
2577
- }
2578
-
2579
- private function EitherEndian2Int($byteword, $signed=false) {
2580
- if ($this->container == 'riff') {
2581
- return getid3_lib::LittleEndian2Int($byteword, $signed);
2582
- }
2583
- return getid3_lib::BigEndian2Int($byteword, false, $signed);
2584
- }
2585
-
 
 
 
 
 
 
 
2586
  }
1
+ <?php
2
+ /////////////////////////////////////////////////////////////////
3
+ /// getID3() by James Heinrich <info@getid3.org> //
4
+ // available at http://getid3.sourceforge.net //
5
+ // or http://www.getid3.org //
6
+ // also https://github.com/JamesHeinrich/getID3 //
7
+ /////////////////////////////////////////////////////////////////
8
+ // See readme.txt for more details //
9
+ /////////////////////////////////////////////////////////////////
10
+ // //
11
+ // module.audio-video.riff.php //
12
+ // module for analyzing RIFF files //
13
+ // multiple formats supported by this module: //
14
+ // Wave, AVI, AIFF/AIFC, (MP3,AC3)/RIFF, Wavpack v3, 8SVX //
15
+ // dependencies: module.audio.mp3.php //
16
+ // module.audio.ac3.php //
17
+ // module.audio.dts.php //
18
+ // ///
19
+ /////////////////////////////////////////////////////////////////
20
+
21
+ /**
22
+ * @todo Parse AC-3/DTS audio inside WAVE correctly
23
+ * @todo Rewrite RIFF parser totally
24
+ */
25
+
26
+ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
27
+ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
28
+ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true);
29
+
30
+ class getid3_riff extends getid3_handler {
31
+
32
+ protected $container = 'riff'; // default
33
+
34
+ public function Analyze() {
35
+ $info = &$this->getid3->info;
36
+
37
+ // initialize these values to an empty array, otherwise they default to NULL
38
+ // and you can't append array values to a NULL value
39
+ $info['riff'] = array('raw'=>array());
40
+
41
+ // Shortcuts
42
+ $thisfile_riff = &$info['riff'];
43
+ $thisfile_riff_raw = &$thisfile_riff['raw'];
44
+ $thisfile_audio = &$info['audio'];
45
+ $thisfile_video = &$info['video'];
46
+ $thisfile_audio_dataformat = &$thisfile_audio['dataformat'];
47
+ $thisfile_riff_audio = &$thisfile_riff['audio'];
48
+ $thisfile_riff_video = &$thisfile_riff['video'];
49
+
50
+ $Original['avdataoffset'] = $info['avdataoffset'];
51
+ $Original['avdataend'] = $info['avdataend'];
52
+
53
+ $this->fseek($info['avdataoffset']);
54
+ $RIFFheader = $this->fread(12);
55
+ $offset = $this->ftell();
56
+ $RIFFtype = substr($RIFFheader, 0, 4);
57
+ $RIFFsize = substr($RIFFheader, 4, 4);
58
+ $RIFFsubtype = substr($RIFFheader, 8, 4);
59
+
60
+ switch ($RIFFtype) {
61
+
62
+ case 'FORM': // AIFF, AIFC
63
+ //$info['fileformat'] = 'aiff';
64
+ $this->container = 'aiff';
65
+ $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
66
+ $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4));
67
+ break;
68
+
69
+ case 'RIFF': // AVI, WAV, etc
70
+ case 'SDSS': // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com)
71
+ case 'RMP3': // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s
72
+ //$info['fileformat'] = 'riff';
73
+ $this->container = 'riff';
74
+ $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
75
+ if ($RIFFsubtype == 'RMP3') {
76
+ // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s
77
+ $RIFFsubtype = 'WAVE';
78
+ }
79
+ if ($RIFFsubtype != 'AMV ') {
80
+ // AMV files are RIFF-AVI files with parts of the spec deliberately broken, such as chunk size fields hardcoded to zero (because players known in hardware that these fields are always a certain size
81
+ // Handled separately in ParseRIFFAMV()
82
+ $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4));
83
+ }
84
+ if (($info['avdataend'] - $info['filesize']) == 1) {
85
+ // LiteWave appears to incorrectly *not* pad actual output file
86
+ // to nearest WORD boundary so may appear to be short by one
87
+ // byte, in which case - skip warning
88
+ $info['avdataend'] = $info['filesize'];
89
+ }
90
+
91
+ $nextRIFFoffset = $Original['avdataoffset'] + 8 + $thisfile_riff['header_size']; // 8 = "RIFF" + 32-bit offset
92
+ while ($nextRIFFoffset < min($info['filesize'], $info['avdataend'])) {
93
+ try {
94
+ $this->fseek($nextRIFFoffset);
95
+ } catch (getid3_exception $e) {
96
+ if ($e->getCode() == 10) {
97
+ //$this->warning('RIFF parser: '.$e->getMessage());
98
+ $this->error('AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime may be wrong');
99
+ $this->warning('[avdataend] value may be incorrect, multiple AVIX chunks may be present');
100
+ break;
101
+ } else {
102
+ throw $e;
103
+ }
104
+ }
105
+ $nextRIFFheader = $this->fread(12);
106
+ if ($nextRIFFoffset == ($info['avdataend'] - 1)) {
107
+ if (substr($nextRIFFheader, 0, 1) == "\x00") {
108
+ // RIFF padded to WORD boundary, we're actually already at the end
109
+ break;
110
+ }
111
+ }
112
+ $nextRIFFheaderID = substr($nextRIFFheader, 0, 4);
113
+ $nextRIFFsize = $this->EitherEndian2Int(substr($nextRIFFheader, 4, 4));
114
+ $nextRIFFtype = substr($nextRIFFheader, 8, 4);
115
+ $chunkdata = array();
116
+ $chunkdata['offset'] = $nextRIFFoffset + 8;
117
+ $chunkdata['size'] = $nextRIFFsize;
118
+ $nextRIFFoffset = $chunkdata['offset'] + $chunkdata['size'];
119
+
120
+ switch ($nextRIFFheaderID) {
121
+ case 'RIFF':
122
+ $chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $nextRIFFoffset);
123
+ if (!isset($thisfile_riff[$nextRIFFtype])) {
124
+ $thisfile_riff[$nextRIFFtype] = array();
125
+ }
126
+ $thisfile_riff[$nextRIFFtype][] = $chunkdata;
127
+ break;
128
+
129
+ case 'AMV ':
130
+ unset($info['riff']);
131
+ $info['amv'] = $this->ParseRIFFAMV($chunkdata['offset'] + 4, $nextRIFFoffset);
132
+ break;
133
+
134
+ case 'JUNK':
135
+ // ignore
136
+ $thisfile_riff[$nextRIFFheaderID][] = $chunkdata;
137
+ break;
138
+
139
+ case 'IDVX':
140
+ $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunkdata['size']));
141
+ break;
142
+
143
+ default:
144
+ if ($info['filesize'] == ($chunkdata['offset'] - 8 + 128)) {
145
+ $DIVXTAG = $nextRIFFheader.$this->fread(128 - 12);
146
+ if (substr($DIVXTAG, -7) == 'DIVXTAG') {
147
+ // DIVXTAG is supposed to be inside an IDVX chunk in a LIST chunk, but some bad encoders just slap it on the end of a file
148
+ $this->warning('Found wrongly-structured DIVXTAG at offset '.($this->ftell() - 128).', parsing anyway');
149
+ $info['divxtag']['comments'] = self::ParseDIVXTAG($DIVXTAG);
150
+ break 2;
151
+ }
152
+ }
153
+ $this->warning('Expecting "RIFF|JUNK|IDVX" at '.$nextRIFFoffset.', found "'.$nextRIFFheaderID.'" ('.getid3_lib::PrintHexBytes($nextRIFFheaderID).') - skipping rest of file');
154
+ break 2;
155
+
156
+ }
157
+
158
+ }
159
+ if ($RIFFsubtype == 'WAVE') {
160
+ $thisfile_riff_WAVE = &$thisfile_riff['WAVE'];
161
+ }
162
+ break;
163
+
164
+ default:
165
+ $this->error('Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "'.$RIFFsubtype.'" instead');
166
+ //unset($info['fileformat']);
167
+ return false;
168
+ }
169
+
170
+ $streamindex = 0;
171
+ switch ($RIFFsubtype) {
172
+
173
+ // http://en.wikipedia.org/wiki/Wav
174
+ case 'WAVE':
175
+ $info['fileformat'] = 'wav';
176
+
177
+ if (empty($thisfile_audio['bitrate_mode'])) {
178
+ $thisfile_audio['bitrate_mode'] = 'cbr';
179
+ }
180
+ if (empty($thisfile_audio_dataformat)) {
181
+ $thisfile_audio_dataformat = 'wav';
182
+ }
183
+
184
+ if (isset($thisfile_riff_WAVE['data'][0]['offset'])) {
185
+ $info['avdataoffset'] = $thisfile_riff_WAVE['data'][0]['offset'] + 8;
186
+ $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff_WAVE['data'][0]['size'];
187
+ }
188
+ if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) {
189
+
190
+ $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']);
191
+ $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
192
+ if (!isset($thisfile_riff_audio[$streamindex]['bitrate']) || ($thisfile_riff_audio[$streamindex]['bitrate'] == 0)) {
193
+ $info['error'][] = 'Corrupt RIFF file: bitrate_audio == zero';
194
+ return false;
195
+ }
196
+ $thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw'];
197
+ unset($thisfile_riff_audio[$streamindex]['raw']);
198
+ $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
199
+
200
+ $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
201
+ if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') {
202
+ $info['warning'][] = 'Audio codec = '.$thisfile_audio['codec'];
203
+ }
204
+ $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
205
+
206
+ if (empty($info['playtime_seconds'])) { // may already be set (e.g. DTS-WAV)
207
+ $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
208
+ }
209
+
210
+ $thisfile_audio['lossless'] = false;
211
+ if (isset($thisfile_riff_WAVE['data'][0]['offset']) && isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
212
+ switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
213
+
214
+ case 0x0001: // PCM
215
+ $thisfile_audio['lossless'] = true;
216
+ break;
217
+
218
+ case 0x2000: // AC-3
219
+ $thisfile_audio_dataformat = 'ac3';
220
+ break;
221
+
222
+ default:
223
+ // do nothing
224
+ break;
225
+
226
+ }
227
+ }
228
+ $thisfile_audio['streams'][$streamindex]['wformattag'] = $thisfile_audio['wformattag'];
229
+ $thisfile_audio['streams'][$streamindex]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
230
+ $thisfile_audio['streams'][$streamindex]['lossless'] = $thisfile_audio['lossless'];
231
+ $thisfile_audio['streams'][$streamindex]['dataformat'] = $thisfile_audio_dataformat;
232
+ }
233
+
234
+ if (isset($thisfile_riff_WAVE['rgad'][0]['data'])) {
235
+
236
+ // shortcuts
237
+ $rgadData = &$thisfile_riff_WAVE['rgad'][0]['data'];
238
+ $thisfile_riff_raw['rgad'] = array('track'=>array(), 'album'=>array());
239
+ $thisfile_riff_raw_rgad = &$thisfile_riff_raw['rgad'];
240
+ $thisfile_riff_raw_rgad_track = &$thisfile_riff_raw_rgad['track'];
241
+ $thisfile_riff_raw_rgad_album = &$thisfile_riff_raw_rgad['album'];
242
+
243
+ $thisfile_riff_raw_rgad['fPeakAmplitude'] = getid3_lib::LittleEndian2Float(substr($rgadData, 0, 4));
244
+ $thisfile_riff_raw_rgad['nRadioRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 4, 2));
245
+ $thisfile_riff_raw_rgad['nAudiophileRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 6, 2));
246
+
247
+ $nRadioRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT);
248
+ $nAudiophileRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT);
249
+ $thisfile_riff_raw_rgad_track['name'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3));
250
+ $thisfile_riff_raw_rgad_track['originator'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3));
251
+ $thisfile_riff_raw_rgad_track['signbit'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1));
252
+ $thisfile_riff_raw_rgad_track['adjustment'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9));
253
+ $thisfile_riff_raw_rgad_album['name'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3));
254
+ $thisfile_riff_raw_rgad_album['originator'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3));
255
+ $thisfile_riff_raw_rgad_album['signbit'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1));
256
+ $thisfile_riff_raw_rgad_album['adjustment'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9));
257
+
258
+ $thisfile_riff['rgad']['peakamplitude'] = $thisfile_riff_raw_rgad['fPeakAmplitude'];
259
+ if (($thisfile_riff_raw_rgad_track['name'] != 0) && ($thisfile_riff_raw_rgad_track['originator'] != 0)) {
260
+ $thisfile_riff['rgad']['track']['name'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_track['name']);
261
+ $thisfile_riff['rgad']['track']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_track['originator']);
262
+ $thisfile_riff['rgad']['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_track['adjustment'], $thisfile_riff_raw_rgad_track['signbit']);
263
+ }
264
+ if (($thisfile_riff_raw_rgad_album['name'] != 0) && ($thisfile_riff_raw_rgad_album['originator'] != 0)) {
265
+ $thisfile_riff['rgad']['album']['name'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_album['name']);
266
+ $thisfile_riff['rgad']['album']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_album['originator']);
267
+ $thisfile_riff['rgad']['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_album['adjustment'], $thisfile_riff_raw_rgad_album['signbit']);
268
+ }
269
+ }
270
+
271
+ if (isset($thisfile_riff_WAVE['fact'][0]['data'])) {
272
+ $thisfile_riff_raw['fact']['NumberOfSamples'] = $this->EitherEndian2Int(substr($thisfile_riff_WAVE['fact'][0]['data'], 0, 4));
273
+
274
+ // This should be a good way of calculating exact playtime,
275
+ // but some sample files have had incorrect number of samples,
276
+ // so cannot use this method
277
+
278
+ // if (!empty($thisfile_riff_raw['fmt ']['nSamplesPerSec'])) {
279
+ // $info['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec'];
280
+ // }
281
+ }
282
+ if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) {
283
+ $thisfile_audio['bitrate'] = getid3_lib::CastAsInt($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'] * 8);
284
+ }
285
+
286
+ if (isset($thisfile_riff_WAVE['bext'][0]['data'])) {
287
+ // shortcut
288
+ $thisfile_riff_WAVE_bext_0 = &$thisfile_riff_WAVE['bext'][0];
289
+
290
+ $thisfile_riff_WAVE_bext_0['title'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 0, 256));
291
+ $thisfile_riff_WAVE_bext_0['author'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 256, 32));
292
+ $thisfile_riff_WAVE_bext_0['reference'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 288, 32));
293
+ $thisfile_riff_WAVE_bext_0['origin_date'] = substr($thisfile_riff_WAVE_bext_0['data'], 320, 10);
294
+ $thisfile_riff_WAVE_bext_0['origin_time'] = substr($thisfile_riff_WAVE_bext_0['data'], 330, 8);
295
+ $thisfile_riff_WAVE_bext_0['time_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338, 8));
296
+ $thisfile_riff_WAVE_bext_0['bwf_version'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 346, 1));
297
+ $thisfile_riff_WAVE_bext_0['reserved'] = substr($thisfile_riff_WAVE_bext_0['data'], 347, 254);
298
+ $thisfile_riff_WAVE_bext_0['coding_history'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_bext_0['data'], 601)));
299
+ if (preg_match('#^([0-9]{4}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_date'], $matches_bext_date)) {
300
+ if (preg_match('#^([0-9]{2}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_time'], $matches_bext_time)) {
301
+ list($dummy, $bext_timestamp['year'], $bext_timestamp['month'], $bext_timestamp['day']) = $matches_bext_date;
302
+ list($dummy, $bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second']) = $matches_bext_time;
303
+ $thisfile_riff_WAVE_bext_0['origin_date_unix'] = gmmktime($bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second'], $bext_timestamp['month'], $bext_timestamp['day'], $bext_timestamp['year']);
304
+ } else {
305
+ $info['warning'][] = 'RIFF.WAVE.BEXT.origin_time is invalid';
306
+ }
307
+ } else {
308
+ $info['warning'][] = 'RIFF.WAVE.BEXT.origin_date is invalid';
309
+ }
310
+ $thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author'];
311
+ $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_bext_0['title'];
312
+ }
313
+
314
+ if (isset($thisfile_riff_WAVE['MEXT'][0]['data'])) {
315
+ // shortcut
316
+ $thisfile_riff_WAVE_MEXT_0 = &$thisfile_riff_WAVE['MEXT'][0];
317
+
318
+ $thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 0, 2));
319
+ $thisfile_riff_WAVE_MEXT_0['flags']['homogenous'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0001);
320
+ if ($thisfile_riff_WAVE_MEXT_0['flags']['homogenous']) {
321
+ $thisfile_riff_WAVE_MEXT_0['flags']['padding'] = ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0002) ? false : true;
322
+ $thisfile_riff_WAVE_MEXT_0['flags']['22_or_44'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0004);
323
+ $thisfile_riff_WAVE_MEXT_0['flags']['free_format'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0008);
324
+
325
+ $thisfile_riff_WAVE_MEXT_0['nominal_frame_size'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2));
326
+ }
327
+ $thisfile_riff_WAVE_MEXT_0['anciliary_data_length'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 6, 2));
328
+ $thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 8, 2));
329
+ $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_left'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0001);
330
+ $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_free'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0002);
331
+ $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_right'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0004);
332
+ }
333
+
334
+ if (isset($thisfile_riff_WAVE['cart'][0]['data'])) {
335
+ // shortcut
336
+ $thisfile_riff_WAVE_cart_0 = &$thisfile_riff_WAVE['cart'][0];
337
+
338
+ $thisfile_riff_WAVE_cart_0['version'] = substr($thisfile_riff_WAVE_cart_0['data'], 0, 4);
339
+ $thisfile_riff_WAVE_cart_0['title'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 4, 64));
340
+ $thisfile_riff_WAVE_cart_0['artist'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 68, 64));
341
+ $thisfile_riff_WAVE_cart_0['cut_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 132, 64));
342
+ $thisfile_riff_WAVE_cart_0['client_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 196, 64));
343
+ $thisfile_riff_WAVE_cart_0['category'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 260, 64));
344
+ $thisfile_riff_WAVE_cart_0['classification'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 324, 64));
345
+ $thisfile_riff_WAVE_cart_0['out_cue'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 388, 64));
346
+ $thisfile_riff_WAVE_cart_0['start_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 452, 10));
347
+ $thisfile_riff_WAVE_cart_0['start_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 462, 8));
348
+ $thisfile_riff_WAVE_cart_0['end_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 470, 10));
349
+ $thisfile_riff_WAVE_cart_0['end_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 480, 8));
350
+ $thisfile_riff_WAVE_cart_0['producer_app_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 488, 64));
351
+ $thisfile_riff_WAVE_cart_0['producer_app_version'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 552, 64));
352
+ $thisfile_riff_WAVE_cart_0['user_defined_text'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 616, 64));
353
+ $thisfile_riff_WAVE_cart_0['zero_db_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 680, 4), true);
354
+ for ($i = 0; $i < 8; $i++) {
355
+ $thisfile_riff_WAVE_cart_0['post_time'][$i]['usage_fourcc'] = substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8), 4);
356
+ $thisfile_riff_WAVE_cart_0['post_time'][$i]['timer_value'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8) + 4, 4));
357
+ }
358
+ $thisfile_riff_WAVE_cart_0['url'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 748, 1024));
359
+ $thisfile_riff_WAVE_cart_0['tag_text'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772)));
360
+
361
+ $thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist'];
362
+ $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_cart_0['title'];
363
+ }
364
+
365
+ if (isset($thisfile_riff_WAVE['SNDM'][0]['data'])) {
366
+ // SoundMiner metadata
367
+
368
+ // shortcuts
369
+ $thisfile_riff_WAVE_SNDM_0 = &$thisfile_riff_WAVE['SNDM'][0];
370
+ $thisfile_riff_WAVE_SNDM_0_data = &$thisfile_riff_WAVE_SNDM_0['data'];
371
+ $SNDM_startoffset = 0;
372
+ $SNDM_endoffset = $thisfile_riff_WAVE_SNDM_0['size'];
373
+
374
+ while ($SNDM_startoffset < $SNDM_endoffset) {
375
+ $SNDM_thisTagOffset = 0;
376
+ $SNDM_thisTagSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4));
377
+ $SNDM_thisTagOffset += 4;
378
+ $SNDM_thisTagKey = substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4);
379
+ $SNDM_thisTagOffset += 4;
380
+ $SNDM_thisTagDataSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
381
+ $SNDM_thisTagOffset += 2;
382
+ $SNDM_thisTagDataFlags = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
383
+ $SNDM_thisTagOffset += 2;
384
+ $SNDM_thisTagDataText = substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, $SNDM_thisTagDataSize);
385
+ $SNDM_thisTagOffset += $SNDM_thisTagDataSize;
386
+
387
+ if ($SNDM_thisTagSize != (4 + 4 + 2 + 2 + $SNDM_thisTagDataSize)) {
388
+ $info['warning'][] = 'RIFF.WAVE.SNDM.data contains tag not expected length (expected: '.$SNDM_thisTagSize.', found: '.(4 + 4 + 2 + 2 + $SNDM_thisTagDataSize).') at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
389
+ break;
390
+ } elseif ($SNDM_thisTagSize <= 0) {
391
+ $info['warning'][] = 'RIFF.WAVE.SNDM.data contains zero-size tag at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
392
+ break;
393
+ }
394
+ $SNDM_startoffset += $SNDM_thisTagSize;
395
+
396
+ $thisfile_riff_WAVE_SNDM_0['parsed_raw'][$SNDM_thisTagKey] = $SNDM_thisTagDataText;
397
+ if ($parsedkey = self::waveSNDMtagLookup($SNDM_thisTagKey)) {
398
+ $thisfile_riff_WAVE_SNDM_0['parsed'][$parsedkey] = $SNDM_thisTagDataText;
399
+ } else {
400
+ $info['warning'][] = 'RIFF.WAVE.SNDM contains unknown tag "'.$SNDM_thisTagKey.'" at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
401
+ }
402
+ }
403
+
404
+ $tagmapping = array(
405
+ 'tracktitle'=>'title',
406
+ 'category' =>'genre',
407
+ 'cdtitle' =>'album',
408
+ 'tracktitle'=>'title',
409
+ );
410
+ foreach ($tagmapping as $fromkey => $tokey) {
411
+ if (isset($thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey])) {
412
+ $thisfile_riff['comments'][$tokey][] = $thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey];
413
+ }
414
+ }
415
+ }
416
+
417
+ if (isset($thisfile_riff_WAVE['iXML'][0]['data'])) {
418
+ // requires functions simplexml_load_string and get_object_vars
419
+ if ($parsedXML = getid3_lib::XML2array($thisfile_riff_WAVE['iXML'][0]['data'])) {
420
+ $thisfile_riff_WAVE['iXML'][0]['parsed'] = $parsedXML;
421
+ if (isset($parsedXML['SPEED']['MASTER_SPEED'])) {
422
+ @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['MASTER_SPEED']);
423
+ $thisfile_riff_WAVE['iXML'][0]['master_speed'] = $numerator / ($denominator ? $denominator : 1000);
424
+ }
425
+ if (isset($parsedXML['SPEED']['TIMECODE_RATE'])) {
426
+ @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['TIMECODE_RATE']);
427
+ $thisfile_riff_WAVE['iXML'][0]['timecode_rate'] = $numerator / ($denominator ? $denominator : 1000);
428
+ }
429
+ if (isset($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO']) && !empty($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) && !empty($thisfile_riff_WAVE['iXML'][0]['timecode_rate'])) {
430
+ $samples_since_midnight = floatval(ltrim($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI'].$parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO'], '0'));
431
+ $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] = $samples_since_midnight / $parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE'];
432
+ $h = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] / 3600);
433
+ $m = floor(($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600)) / 60);
434
+ $s = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60));
435
+ $f = ($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60) - $s) * $thisfile_riff_WAVE['iXML'][0]['timecode_rate'];
436
+ $thisfile_riff_WAVE['iXML'][0]['timecode_string'] = sprintf('%02d:%02d:%02d:%05.2f', $h, $m, $s, $f);
437
+ $thisfile_riff_WAVE['iXML'][0]['timecode_string_round'] = sprintf('%02d:%02d:%02d:%02d', $h, $m, $s, round($f));
438
+ }
439
+ unset($parsedXML);
440
+ }
441
+ }
442
+
443
+
444
+
445
+ if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) {
446
+ $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
447
+ $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
448
+ }
449
+
450
+ if (!empty($info['wavpack'])) {
451
+ $thisfile_audio_dataformat = 'wavpack';
452
+ $thisfile_audio['bitrate_mode'] = 'vbr';
453
+ $thisfile_audio['encoder'] = 'WavPack v'.$info['wavpack']['version'];
454
+
455
+ // Reset to the way it was - RIFF parsing will have messed this up
456
+ $info['avdataend'] = $Original['avdataend'];
457
+ $thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
458
+
459
+ $this->fseek($info['avdataoffset'] - 44);
460
+ $RIFFdata = $this->fread(44);
461
+ $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8;
462
+ $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
463
+
464
+ if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
465
+ $info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
466
+ $this->fseek($info['avdataend']);
467
+ $RIFFdata .= $this->fread($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
468
+ }
469
+
470
+ // move the data chunk after all other chunks (if any)
471
+ // so that the RIFF parser doesn't see EOF when trying
472
+ // to skip over the data chunk
473
+ $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
474
+ $getid3_riff = new getid3_riff($this->getid3);
475
+ $getid3_riff->ParseRIFFdata($RIFFdata);
476
+ unset($getid3_riff);
477
+ }
478
+
479
+ if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
480
+ switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
481
+ case 0x0001: // PCM
482
+ if (!empty($info['ac3'])) {
483
+ // Dolby Digital WAV files masquerade as PCM-WAV, but they're not
484
+ $thisfile_audio['wformattag'] = 0x2000;
485
+ $thisfile_audio['codec'] = self::wFormatTagLookup($thisfile_audio['wformattag']);
486
+ $thisfile_audio['lossless'] = false;
487
+ $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
488
+ $thisfile_audio['sample_rate'] = $info['ac3']['sample_rate'];
489
+ }
490
+ if (!empty($info['dts'])) {
491
+ // Dolby DTS files masquerade as PCM-WAV, but they're not
492
+ $thisfile_audio['wformattag'] = 0x2001;
493
+ $thisfile_audio['codec'] = self::wFormatTagLookup($thisfile_audio['wformattag']);
494
+ $thisfile_audio['lossless'] = false;
495
+ $thisfile_audio['bitrate'] = $info['dts']['bitrate'];
496
+ $thisfile_audio['sample_rate'] = $info['dts']['sample_rate'];
497
+ }
498
+ break;
499
+ case 0x08AE: // ClearJump LiteWave
500
+ $thisfile_audio['bitrate_mode'] = 'vbr';
501
+ $thisfile_audio_dataformat = 'litewave';
502
+
503
+ //typedef struct tagSLwFormat {
504
+ // WORD m_wCompFormat; // low byte defines compression method, high byte is compression flags
505
+ // DWORD m_dwScale; // scale factor for lossy compression
506
+ // DWORD m_dwBlockSize; // number of samples in encoded blocks
507
+ // WORD m_wQuality; // alias for the scale factor
508
+ // WORD m_wMarkDistance; // distance between marks in bytes
509
+ // WORD m_wReserved;
510
+ //
511
+ // //following paramters are ignored if CF_FILESRC is not set
512
+ // DWORD m_dwOrgSize; // original file size in bytes
513
+ // WORD m_bFactExists; // indicates if 'fact' chunk exists in the original file
514
+ // DWORD m_dwRiffChunkSize; // riff chunk size in the original file
515
+ //
516
+ // PCMWAVEFORMAT m_OrgWf; // original wave format
517
+ // }SLwFormat, *PSLwFormat;
518
+
519
+ // shortcut
520
+ $thisfile_riff['litewave']['raw'] = array();
521
+ $riff_litewave = &$thisfile_riff['litewave'];
522
+ $riff_litewave_raw = &$riff_litewave['raw'];
523
+
524
+ $flags = array(
525
+ 'compression_method' => 1,
526
+ 'compression_flags' => 1,
527
+ 'm_dwScale' => 4,
528
+ 'm_dwBlockSize' => 4,
529
+ 'm_wQuality' => 2,
530
+ 'm_wMarkDistance' => 2,
531
+ 'm_wReserved' => 2,
532
+ 'm_dwOrgSize' => 4,
533
+ 'm_bFactExists' => 2,
534
+ 'm_dwRiffChunkSize' => 4,
535
+ );
536
+ $litewave_offset = 18;
537
+ foreach ($flags as $flag => $length) {
538
+ $riff_litewave_raw[$flag] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], $litewave_offset, $length));
539
+ $litewave_offset += $length;
540
+ }
541
+
542
+ //$riff_litewave['quality_factor'] = intval(round((2000 - $riff_litewave_raw['m_dwScale']) / 20));
543
+ $riff_litewave['quality_factor'] = $riff_litewave_raw['m_wQuality'];
544
+
545
+ $riff_litewave['flags']['raw_source'] = ($riff_litewave_raw['compression_flags'] & 0x01) ? false : true;
546
+ $riff_litewave['flags']['vbr_blocksize'] = ($riff_litewave_raw['compression_flags'] & 0x02) ? false : true;
547
+ $riff_litewave['flags']['seekpoints'] = (bool) ($riff_litewave_raw['compression_flags'] & 0x04);
548
+
549
+ $thisfile_audio['lossless'] = (($riff_litewave_raw['m_wQuality'] == 100) ? true : false);
550
+ $thisfile_audio['encoder_options'] = '-q'.$riff_litewave['quality_factor'];
551
+ break;
552
+
553
+ default:
554
+ break;
555
+ }
556
+ }
557
+ if ($info['avdataend'] > $info['filesize']) {
558
+ switch (!empty($thisfile_audio_dataformat) ? $thisfile_audio_dataformat : '') {
559
+ case 'wavpack': // WavPack
560
+ case 'lpac': // LPAC
561
+ case 'ofr': // OptimFROG
562
+ case 'ofs': // OptimFROG DualStream
563
+ // lossless compressed audio formats that keep original RIFF headers - skip warning
564
+ break;
565
+
566
+ case 'litewave':
567
+ if (($info['avdataend'] - $info['filesize']) == 1) {
568
+ // LiteWave appears to incorrectly *not* pad actual output file
569
+ // to nearest WORD boundary so may appear to be short by one
570
+ // byte, in which case - skip warning
571
+ } else {
572
+ // Short by more than one byte, throw warning
573
+ $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
574
+ $info['avdataend'] = $info['filesize'];
575
+ }
576
+ break;
577
+
578
+ default:
579
+ if ((($info['avdataend'] - $info['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($info['filesize'] - $info['avdataoffset']) % 2) == 1)) {
580
+ // output file appears to be incorrectly *not* padded to nearest WORD boundary
581
+ // Output less severe warning
582
+ $info['warning'][] = 'File should probably be padded to nearest WORD boundary, but it is not (expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' therefore short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
583
+ $info['avdataend'] = $info['filesize'];
584
+ } else {
585
+ // Short by more than one byte, throw warning
586
+ $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
587
+ $info['avdataend'] = $info['filesize'];
588
+ }
589
+ break;
590
+ }
591
+ }
592
+ if (!empty($info['mpeg']['audio']['LAME']['audio_bytes'])) {
593
+ if ((($info['avdataend'] - $info['avdataoffset']) - $info['mpeg']['audio']['LAME']['audio_bytes']) == 1) {
594
+ $info['avdataend']--;
595
+ $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
596
+ }
597
+ }
598
+ if (isset($thisfile_audio_dataformat) && ($thisfile_audio_dataformat == 'ac3')) {
599
+ unset($thisfile_audio['bits_per_sample']);
600
+ if (!empty($info['ac3']['bitrate']) && ($info['ac3']['bitrate'] != $thisfile_audio['bitrate'])) {
601
+ $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
602
+ }
603
+ }
604
+ break;
605
+
606
+ // http://en.wikipedia.org/wiki/Audio_Video_Interleave
607
+ case 'AVI ':
608
+ $info['fileformat'] = 'avi';
609
+ $info['mime_type'] = 'video/avi';
610
+
611
+ $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably
612
+ $thisfile_video['dataformat'] = 'avi';
613
+
614
+ if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) {
615
+ $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8;
616
+ if (isset($thisfile_riff['AVIX'])) {
617
+ $info['avdataend'] = $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['offset'] + $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['size'];
618
+ } else {
619
+ $info['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size'];
620
+ }
621
+ if ($info['avdataend'] > $info['filesize']) {
622
+ $info['warning'][] = 'Probably truncated file - expecting '.($info['avdataend'] - $info['avdataoffset']).' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($info['avdataend'] - $info['filesize']).' bytes)';
623
+ $info['avdataend'] = $info['filesize'];
624
+ }
625
+ }
626
+
627
+ if (isset($thisfile_riff['AVI ']['hdrl']['strl']['indx'])) {
628
+ //$bIndexType = array(
629
+ // 0x00 => 'AVI_INDEX_OF_INDEXES',
630
+ // 0x01 => 'AVI_INDEX_OF_CHUNKS',
631
+ // 0x80 => 'AVI_INDEX_IS_DATA',
632
+ //);
633
+ //$bIndexSubtype = array(
634
+ // 0x01 => array(
635
+ // 0x01 => 'AVI_INDEX_2FIELD',
636
+ // ),
637
+ //);
638
+ foreach ($thisfile_riff['AVI ']['hdrl']['strl']['indx'] as $streamnumber => $steamdataarray) {
639
+ $ahsisd = &$thisfile_riff['AVI ']['hdrl']['strl']['indx'][$streamnumber]['data'];
640
+
641
+ $thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = $this->EitherEndian2Int(substr($ahsisd, 0, 2));
642
+ $thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType'] = $this->EitherEndian2Int(substr($ahsisd, 2, 1));
643
+ $thisfile_riff_raw['indx'][$streamnumber]['bIndexType'] = $this->EitherEndian2Int(substr($ahsisd, 3, 1));
644
+ $thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse'] = $this->EitherEndian2Int(substr($ahsisd, 4, 4));
645
+ $thisfile_riff_raw['indx'][$streamnumber]['dwChunkId'] = substr($ahsisd, 8, 4);
646
+ $thisfile_riff_raw['indx'][$streamnumber]['dwReserved'] = $this->EitherEndian2Int(substr($ahsisd, 12, 4));
647
+
648
+ //$thisfile_riff_raw['indx'][$streamnumber]['bIndexType_name'] = $bIndexType[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']];
649
+ //$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType_name'] = $bIndexSubtype[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']][$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']];
650
+
651
+ unset($ahsisd);
652
+ }
653
+ }
654
+ if (isset($thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'])) {
655
+ $avihData = $thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'];
656
+
657
+ // shortcut
658
+ $thisfile_riff_raw['avih'] = array();
659
+ $thisfile_riff_raw_avih = &$thisfile_riff_raw['avih'];
660
+
661
+ $thisfile_riff_raw_avih['dwMicroSecPerFrame'] = $this->EitherEndian2Int(substr($avihData, 0, 4)); // frame display rate (or 0L)
662
+ if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) {
663
+ $info['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero';
664
+ return false;
665
+ }
666
+
667
+ $flags = array(
668
+ 'dwMaxBytesPerSec', // max. transfer rate
669
+ 'dwPaddingGranularity', // pad to multiples of this size; normally 2K.
670
+ 'dwFlags', // the ever-present flags
671
+ 'dwTotalFrames', // # frames in file
672
+ 'dwInitialFrames', //
673
+ 'dwStreams', //
674
+ 'dwSuggestedBufferSize', //
675
+ 'dwWidth', //
676
+ 'dwHeight', //
677
+ 'dwScale', //
678
+ 'dwRate', //
679
+ 'dwStart', //
680
+ 'dwLength', //
681
+ );
682
+ $avih_offset = 4;
683
+ foreach ($flags as $flag) {
684
+ $thisfile_riff_raw_avih[$flag] = $this->EitherEndian2Int(substr($avihData, $avih_offset, 4));
685
+ $avih_offset += 4;
686
+ }
687
+
688
+ $flags = array(
689
+ 'hasindex' => 0x00000010,
690
+ 'mustuseindex' => 0x00000020,
691
+ 'interleaved' => 0x00000100,
692
+ 'trustcktype' => 0x00000800,
693
+ 'capturedfile' => 0x00010000,
694
+ 'copyrighted' => 0x00020010,
695
+ );
696
+ foreach ($flags as $flag => $value) {
697
+ $thisfile_riff_raw_avih['flags'][$flag] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & $value);
698
+ }
699
+
700
+ // shortcut
701
+ $thisfile_riff_video[$streamindex] = array();
702
+ $thisfile_riff_video_current = &$thisfile_riff_video[$streamindex];
703
+
704
+ if ($thisfile_riff_raw_avih['dwWidth'] > 0) {
705
+ $thisfile_riff_video_current['frame_width'] = $thisfile_riff_raw_avih['dwWidth'];
706
+ $thisfile_video['resolution_x'] = $thisfile_riff_video_current['frame_width'];
707
+ }
708
+ if ($thisfile_riff_raw_avih['dwHeight'] > 0) {
709
+ $thisfile_riff_video_current['frame_height'] = $thisfile_riff_raw_avih['dwHeight'];
710
+ $thisfile_video['resolution_y'] = $thisfile_riff_video_current['frame_height'];
711
+ }
712
+ if ($thisfile_riff_raw_avih['dwTotalFrames'] > 0) {
713
+ $thisfile_riff_video_current['total_frames'] = $thisfile_riff_raw_avih['dwTotalFrames'];
714
+ $thisfile_video['total_frames'] = $thisfile_riff_video_current['total_frames'];
715
+ }
716
+
717
+ $thisfile_riff_video_current['frame_rate'] = round(1000000 / $thisfile_riff_raw_avih['dwMicroSecPerFrame'], 3);
718
+ $thisfile_video['frame_rate'] = $thisfile_riff_video_current['frame_rate'];
719
+ }
720
+ if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][0]['data'])) {
721
+ if (is_array($thisfile_riff['AVI ']['hdrl']['strl']['strh'])) {
722
+ for ($i = 0; $i < count($thisfile_riff['AVI ']['hdrl']['strl']['strh']); $i++) {
723
+ if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'])) {
724
+ $strhData = $thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'];
725
+ $strhfccType = substr($strhData, 0, 4);
726
+
727
+ if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'])) {
728
+ $strfData = $thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'];
729
+
730
+ // shortcut
731
+ $thisfile_riff_raw_strf_strhfccType_streamindex = &$thisfile_riff_raw['strf'][$strhfccType][$streamindex];
732
+
733
+ switch ($strhfccType) {
734
+ case 'auds':
735
+ $thisfile_audio['bitrate_mode'] = 'cbr';
736
+ $thisfile_audio_dataformat = 'wav';
737
+ if (isset($thisfile_riff_audio) && is_array($thisfile_riff_audio)) {
738
+ $streamindex = count($thisfile_riff_audio);
739
+ }
740
+
741
+ $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($strfData);
742
+ $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
743
+
744
+ // shortcut
745
+ $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
746
+ $thisfile_audio_streams_currentstream = &$thisfile_audio['streams'][$streamindex];
747
+
748
+ if ($thisfile_audio_streams_currentstream['bits_per_sample'] == 0) {
749
+ unset($thisfile_audio_streams_currentstream['bits_per_sample']);
750
+ }
751
+ $thisfile_audio_streams_currentstream['wformattag'] = $thisfile_audio_streams_currentstream['raw']['wFormatTag'];
752
+ unset($thisfile_audio_streams_currentstream['raw']);
753
+
754
+ // shortcut
755
+ $thisfile_riff_raw['strf'][$strhfccType][$streamindex] = $thisfile_riff_audio[$streamindex]['raw'];
756
+
757
+ unset($thisfile_riff_audio[$streamindex]['raw']);
758
+ $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
759
+
760
+ $thisfile_audio['lossless'] = false;
761
+ switch ($thisfile_riff_raw_strf_strhfccType_streamindex['wFormatTag']) {
762
+ case 0x0001: // PCM
763
+ $thisfile_audio_dataformat = 'wav';
764
+ $thisfile_audio['lossless'] = true;
765
+ break;
766
+
767
+ case 0x0050: // MPEG Layer 2 or Layer 1
768
+ $thisfile_audio_dataformat = 'mp2'; // Assume Layer-2
769
+ break;
770
+
771
+ case 0x0055: // MPEG Layer 3
772
+ $thisfile_audio_dataformat = 'mp3';
773
+ break;
774
+
775
+ case 0x00FF: // AAC
776
+ $thisfile_audio_dataformat = 'aac';
777
+ break;
778
+
779
+ case 0x0161: // Windows Media v7 / v8 / v9
780
+ case 0x0162: // Windows Media Professional v9
781
+ case 0x0163: // Windows Media Lossess v9
782
+ $thisfile_audio_dataformat = 'wma';
783
+ break;
784
+
785
+ case 0x2000: // AC-3
786
+ $thisfile_audio_dataformat = 'ac3';
787
+ break;
788
+
789
+ case 0x2001: // DTS
790
+ $thisfile_audio_dataformat = 'dts';
791
+ break;
792
+
793
+ default:
794
+ $thisfile_audio_dataformat = 'wav';
795
+ break;
796
+ }
797
+ $thisfile_audio_streams_currentstream['dataformat'] = $thisfile_audio_dataformat;
798
+ $thisfile_audio_streams_currentstream['lossless'] = $thisfile_audio['lossless'];
799
+ $thisfile_audio_streams_currentstream['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
800
+ break;
801
+
802
+
803
+ case 'iavs':
804
+ case 'vids':
805
+ // shortcut
806
+ $thisfile_riff_raw['strh'][$i] = array();
807
+ $thisfile_riff_raw_strh_current = &$thisfile_riff_raw['strh'][$i];
808
+
809
+ $thisfile_riff_raw_strh_current['fccType'] = substr($strhData, 0, 4); // same as $strhfccType;
810
+ $thisfile_riff_raw_strh_current['fccHandler'] = substr($strhData, 4, 4);
811
+ $thisfile_riff_raw_strh_current['dwFlags'] = $this->EitherEndian2Int(substr($strhData, 8, 4)); // Contains AVITF_* flags
812
+ $thisfile_riff_raw_strh_current['wPriority'] = $this->EitherEndian2Int(substr($strhData, 12, 2));
813
+ $thisfile_riff_raw_strh_current['wLanguage'] = $this->EitherEndian2Int(substr($strhData, 14, 2));
814
+ $thisfile_riff_raw_strh_current['dwInitialFrames'] = $this->EitherEndian2Int(substr($strhData, 16, 4));
815
+ $thisfile_riff_raw_strh_current['dwScale'] = $this->EitherEndian2Int(substr($strhData, 20, 4));
816
+ $thisfile_riff_raw_strh_current['dwRate'] = $this->EitherEndian2Int(substr($strhData, 24, 4));
817
+ $thisfile_riff_raw_strh_current['dwStart'] = $this->EitherEndian2Int(substr($strhData, 28, 4));
818
+ $thisfile_riff_raw_strh_current['dwLength'] = $this->EitherEndian2Int(substr($strhData, 32, 4));
819
+ $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($strhData, 36, 4));
820
+ $thisfile_riff_raw_strh_current['dwQuality'] = $this->EitherEndian2Int(substr($strhData, 40, 4));
821
+ $thisfile_riff_raw_strh_current['dwSampleSize'] = $this->EitherEndian2Int(substr($strhData, 44, 4));
822
+ $thisfile_riff_raw_strh_current['rcFrame'] = $this->EitherEndian2Int(substr($strhData, 48, 4));
823
+
824
+ $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strh_current['fccHandler']);
825
+ $thisfile_video['fourcc'] = $thisfile_riff_raw_strh_current['fccHandler'];
826
+ if (!$thisfile_riff_video_current['codec'] && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
827
+ $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']);
828
+ $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
829
+ }
830
+ $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
831
+ $thisfile_video['pixel_aspect_ratio'] = (float) 1;
832
+ switch ($thisfile_riff_raw_strh_current['fccHandler']) {
833
+ case 'HFYU': // Huffman Lossless Codec
834
+ case 'IRAW': // Intel YUV Uncompressed
835
+ case 'YUY2': // Uncompressed YUV 4:2:2
836
+ $thisfile_video['lossless'] = true;
837
+ break;
838
+
839
+ default:
840
+ $thisfile_video['lossless'] = false;
841
+ break;
842
+ }
843
+
844
+ switch ($strhfccType) {
845
+ case 'vids':
846
+ $thisfile_riff_raw_strf_strhfccType_streamindex = self::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), ($this->container == 'riff'));
847
+ $thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'];
848
+
849
+ if ($thisfile_riff_video_current['codec'] == 'DV') {
850
+ $thisfile_riff_video_current['dv_type'] = 2;
851
+ }
852
+ break;
853
+
854
+ case 'iavs':
855
+ $thisfile_riff_video_current['dv_type'] = 1;
856
+ break;
857
+ }
858
+ break;
859
+
860
+ default:
861
+ $info['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"';
862
+ break;
863
+
864
+ }
865
+ }
866
+ }
867
+
868
+ if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
869
+
870
+ $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
871
+ if (self::fourccLookup($thisfile_video['fourcc'])) {
872
+ $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_video['fourcc']);
873
+ $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
874
+ }
875
+
876
+ switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) {
877
+ case 'HFYU': // Huffman Lossless Codec
878
+ case 'IRAW': // Intel YUV Uncompressed
879
+ case 'YUY2': // Uncompressed YUV 4:2:2
880
+ $thisfile_video['lossless'] = true;
881
+ //$thisfile_video['bits_per_sample'] = 24;
882
+ break;
883
+
884
+ default:
885
+ $thisfile_video['lossless'] = false;
886
+ //$thisfile_video['bits_per_sample'] = 24;
887
+ break;
888
+ }
889
+
890
+ }
891
+ }
892
+ }
893
+ }
894
+ break;
895
+
896
+
897
+ case 'AMV ':
898
+ $info['fileformat'] = 'amv';
899
+ $info['mime_type'] = 'video/amv';
900
+
901
+ $thisfile_video['bitrate_mode'] = 'vbr'; // it's MJPEG, presumably contant-quality encoding, thereby VBR
902
+ $thisfile_video['dataformat'] = 'mjpeg';
903
+ $thisfile_video['codec'] = 'mjpeg';
904
+ $thisfile_video['lossless'] = false;
905
+ $thisfile_video['bits_per_sample'] = 24;
906
+
907
+ $thisfile_audio['dataformat'] = 'adpcm';
908
+ $thisfile_audio['lossless'] = false;
909
+ break;
910
+
911
+
912
+ // http://en.wikipedia.org/wiki/CD-DA
913
+ case 'CDDA':
914
+ $info['fileformat'] = 'cda';
915
+ unset($info['mime_type']);
916
+
917
+ $thisfile_audio_dataformat = 'cda';
918
+
919
+ $info['avdataoffset'] = 44;
920
+
921
+ if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) {
922
+ // shortcut
923
+ $thisfile_riff_CDDA_fmt_0 = &$thisfile_riff['CDDA']['fmt '][0];
924
+
925
+ $thisfile_riff_CDDA_fmt_0['unknown1'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 0, 2));
926
+ $thisfile_riff_CDDA_fmt_0['track_num'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 2, 2));
927
+ $thisfile_riff_CDDA_fmt_0['disc_id'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 4, 4));
928
+ $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 8, 4));
929
+ $thisfile_riff_CDDA_fmt_0['playtime_frames'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4));
930
+ $thisfile_riff_CDDA_fmt_0['unknown6'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4));
931
+ $thisfile_riff_CDDA_fmt_0['unknown7'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4));
932
+
933
+ $thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75;
934
+ $thisfile_riff_CDDA_fmt_0['playtime_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75;
935
+ $info['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num'];
936
+ $info['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
937
+
938
+ // hardcoded data for CD-audio
939
+ $thisfile_audio['lossless'] = true;
940
+ $thisfile_audio['sample_rate'] = 44100;
941
+ $thisfile_audio['channels'] = 2;
942
+ $thisfile_audio['bits_per_sample'] = 16;
943
+ $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $thisfile_audio['channels'] * $thisfile_audio['bits_per_sample'];
944
+ $thisfile_audio['bitrate_mode'] = 'cbr';
945
+ }
946
+ break;
947
+
948
+ // http://en.wikipedia.org/wiki/AIFF
949
+ case 'AIFF':
950
+ case 'AIFC':
951
+ $info['fileformat'] = 'aiff';
952
+ $info['mime_type'] = 'audio/x-aiff';
953
+
954
+ $thisfile_audio['bitrate_mode'] = 'cbr';
955
+ $thisfile_audio_dataformat = 'aiff';
956
+ $thisfile_audio['lossless'] = true;
957
+
958
+ if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) {
959
+ $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8;
960
+ $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size'];
961
+ if ($info['avdataend'] > $info['filesize']) {
962
+ if (($info['avdataend'] == ($info['filesize'] + 1)) && (($info['filesize'] % 2) == 1)) {
963
+ // structures rounded to 2-byte boundary, but dumb encoders
964
+ // forget to pad end of file to make this actually work
965
+ } else {
966
+ $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
967
+ }
968
+ $info['avdataend'] = $info['filesize'];
969
+ }
970
+ }
971
+
972
+ if (isset($thisfile_riff[$RIFFsubtype]['COMM'][0]['data'])) {
973
+
974
+ // shortcut
975
+ $thisfile_riff_RIFFsubtype_COMM_0_data = &$thisfile_riff[$RIFFsubtype]['COMM'][0]['data'];
976
+
977
+ $thisfile_riff_audio['channels'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 0, 2), true);
978
+ $thisfile_riff_audio['total_samples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 2, 4), false);
979
+ $thisfile_riff_audio['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 6, 2), true);
980
+ $thisfile_riff_audio['sample_rate'] = (int) getid3_lib::BigEndian2Float(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 8, 10));
981
+
982
+ if ($thisfile_riff[$RIFFsubtype]['COMM'][0]['size'] > 18) {
983
+ $thisfile_riff_audio['codec_fourcc'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 18, 4);
984
+ $CodecNameSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 22, 1), false);
985
+ $thisfile_riff_audio['codec_name'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 23, $CodecNameSize);
986
+ switch ($thisfile_riff_audio['codec_name']) {
987
+ case 'NONE':
988
+ $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
989
+ $thisfile_audio['lossless'] = true;
990
+ break;
991
+
992
+ case '':
993
+ switch ($thisfile_riff_audio['codec_fourcc']) {
994
+ // http://developer.apple.com/qa/snd/snd07.html
995
+ case 'sowt':
996
+ $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Little-Endian PCM';
997
+ $thisfile_audio['lossless'] = true;
998
+ break;
999
+
1000
+ case 'twos':
1001
+ $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Big-Endian PCM';
1002
+ $thisfile_audio['lossless'] = true;
1003
+ break;
1004
+
1005
+ default:
1006
+ break;
1007
+ }
1008
+ break;
1009
+
1010
+ default:
1011
+ $thisfile_audio['codec'] = $thisfile_riff_audio['codec_name'];
1012
+ $thisfile_audio['lossless'] = false;
1013
+ break;
1014
+ }
1015
+ }
1016
+
1017
+ $thisfile_audio['channels'] = $thisfile_riff_audio['channels'];
1018
+ if ($thisfile_riff_audio['bits_per_sample'] > 0) {
1019
+ $thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample'];
1020
+ }
1021
+ $thisfile_audio['sample_rate'] = $thisfile_riff_audio['sample_rate'];
1022
+ if ($thisfile_audio['sample_rate'] == 0) {
1023
+ $info['error'][] = 'Corrupted AIFF file: sample_rate == zero';
1024
+ return false;
1025
+ }
1026
+ $info['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate'];
1027
+ }
1028
+
1029
+ if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) {
1030
+ $offset = 0;
1031
+ $CommentCount = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
1032
+ $offset += 2;
1033
+ for ($i = 0; $i < $CommentCount; $i++) {
1034
+ $info['comments_raw'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false);
1035
+ $offset += 4;
1036
+ $info['comments_raw'][$i]['marker_id'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true);
1037
+ $offset += 2;
1038
+ $CommentLength = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
1039
+ $offset += 2;
1040
+ $info['comments_raw'][$i]['comment'] = substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength);
1041
+ $offset += $CommentLength;
1042
+
1043
+ $info['comments_raw'][$i]['timestamp_unix'] = getid3_lib::DateMac2Unix($info['comments_raw'][$i]['timestamp']);
1044
+ $thisfile_riff['comments']['comment'][] = $info['comments_raw'][$i]['comment'];
1045
+ }
1046
+ }
1047
+
1048
+ $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
1049
+ foreach ($CommentsChunkNames as $key => $value) {
1050
+ if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
1051
+ $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
1052
+ }
1053
+ }
1054
+ /*
1055
+ if (isset($thisfile_riff[$RIFFsubtype]['ID3 '])) {
1056
+ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
1057
+ $getid3_temp = new getID3();
1058
+ $getid3_temp->openfile($this->getid3->filename);
1059
+ $getid3_id3v2 = new getid3_id3v2($getid3_temp);
1060
+ $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['ID3 '][0]['offset'] + 8;
1061
+ if ($thisfile_riff[$RIFFsubtype]['ID3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
1062
+ $info['id3v2'] = $getid3_temp->info['id3v2'];
1063
+ }
1064
+ unset($getid3_temp, $getid3_id3v2);
1065
+ }
1066
+ */
1067
+ break;
1068
+
1069
+ // http://en.wikipedia.org/wiki/8SVX
1070
+ case '8SVX':
1071
+ $info['fileformat'] = '8svx';
1072
+ $info['mime_type'] = 'audio/8svx';
1073
+
1074
+ $thisfile_audio['bitrate_mode'] = 'cbr';
1075
+ $thisfile_audio_dataformat = '8svx';
1076
+ $thisfile_audio['bits_per_sample'] = 8;
1077
+ $thisfile_audio['channels'] = 1; // overridden below, if need be
1078
+
1079
+ if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
1080
+ $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8;
1081
+ $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'];
1082
+ if ($info['avdataend'] > $info['filesize']) {
1083
+ $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
1084
+ }
1085
+ }
1086
+
1087
+ if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) {
1088
+ // shortcut
1089
+ $thisfile_riff_RIFFsubtype_VHDR_0 = &$thisfile_riff[$RIFFsubtype]['VHDR'][0];
1090
+
1091
+ $thisfile_riff_RIFFsubtype_VHDR_0['oneShotHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 0, 4));
1092
+ $thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 4, 4));
1093
+ $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 8, 4));
1094
+ $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2));
1095
+ $thisfile_riff_RIFFsubtype_VHDR_0['ctOctave'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1));
1096
+ $thisfile_riff_RIFFsubtype_VHDR_0['sCompression'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1));
1097
+ $thisfile_riff_RIFFsubtype_VHDR_0['Volume'] = getid3_lib::FixedPoint16_16(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 16, 4));
1098
+
1099
+ $thisfile_audio['sample_rate'] = $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'];
1100
+
1101
+ switch ($thisfile_riff_RIFFsubtype_VHDR_0['sCompression']) {
1102
+ case 0:
1103
+ $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
1104
+ $thisfile_audio['lossless'] = true;
1105
+ $ActualBitsPerSample = 8;
1106
+ break;
1107
+
1108
+ case 1:
1109
+ $thisfile_audio['codec'] = 'Fibonacci-delta encoding';
1110
+ $thisfile_audio['lossless'] = false;
1111
+ $ActualBitsPerSample = 4;
1112
+ break;
1113
+
1114
+ default:
1115
+ $info['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"';
1116
+ break;
1117
+ }
1118
+ }
1119
+
1120
+ if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) {
1121
+ $ChannelsIndex = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'], 0, 4));
1122
+ switch ($ChannelsIndex) {
1123
+ case 6: // Stereo
1124
+ $thisfile_audio['channels'] = 2;
1125
+ break;
1126
+
1127
+ case 2: // Left channel only
1128
+ case 4: // Right channel only
1129
+ $thisfile_audio['channels'] = 1;
1130
+ break;
1131
+
1132
+ default:
1133
+ $info['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"';
1134
+ break;
1135
+ }
1136
+
1137
+ }
1138
+
1139
+ $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
1140
+ foreach ($CommentsChunkNames as $key => $value) {
1141
+ if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
1142
+ $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
1143
+ }
1144
+ }
1145
+
1146
+ $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $ActualBitsPerSample * $thisfile_audio['channels'];
1147
+ if (!empty($thisfile_audio['bitrate'])) {
1148
+ $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($thisfile_audio['bitrate'] / 8);
1149
+ }
1150
+ break;
1151
+
1152
+ case 'CDXA':
1153
+ $info['fileformat'] = 'vcd'; // Asume Video CD
1154
+ $info['mime_type'] = 'video/mpeg';
1155
+
1156
+ if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) {
1157
+ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, true);
1158
+
1159
+ $getid3_temp = new getID3();
1160
+ $getid3_temp->openfile($this->getid3->filename);
1161
+ $getid3_mpeg = new getid3_mpeg($getid3_temp);
1162
+ $getid3_mpeg->Analyze();
1163
+ if (empty($getid3_temp->info['error'])) {
1164
+ $info['audio'] = $getid3_temp->info['audio'];
1165
+ $info['video'] = $getid3_temp->info['video'];
1166
+ $info['mpeg'] = $getid3_temp->info['mpeg'];
1167
+ $info['warning'] = $getid3_temp->info['warning'];
1168
+ }
1169
+ unset($getid3_temp, $getid3_mpeg);
1170
+ }
1171
+ break;
1172
+
1173
+ case 'WEBP':
1174
+ // https://developers.google.com/speed/webp/docs/riff_container
1175
+ $info['fileformat'] = 'webp';
1176
+ $info['mime_type'] = 'image/webp';
1177
+
1178
+ $info['error'][] = 'WebP image parsing not supported in this version of getID3()';
1179
+ break;
1180
+
1181
+ default:
1182
+ $info['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA|WEBP), found "'.$RIFFsubtype.'" instead';
1183
+ //unset($info['fileformat']);
1184
+ }
1185
+
1186
+ switch ($RIFFsubtype) {
1187
+ case 'WAVE':
1188
+ case 'AIFF':
1189
+ case 'AIFC':
1190
+ $ID3v2_key_good = 'id3 ';
1191
+ $ID3v2_keys_bad = array('ID3 ', 'tag ');
1192
+ foreach ($ID3v2_keys_bad as $ID3v2_key_bad) {
1193
+ if (isset($thisfile_riff[$RIFFsubtype][$ID3v2_key_bad]) && !array_key_exists($ID3v2_key_good, $thisfile_riff[$RIFFsubtype])) {
1194
+ $thisfile_riff[$RIFFsubtype][$ID3v2_key_good] = $thisfile_riff[$RIFFsubtype][$ID3v2_key_bad];
1195
+ $info['warning'][] = 'mapping "'.$ID3v2_key_bad.'" chunk to "'.$ID3v2_key_good.'"';
1196
+ }
1197
+ }
1198
+
1199
+ if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) {
1200
+ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
1201
+
1202
+ $getid3_temp = new getID3();
1203
+ $getid3_temp->openfile($this->getid3->filename);
1204
+ $getid3_id3v2 = new getid3_id3v2($getid3_temp);
1205
+ $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['id3 '][0]['offset'] + 8;
1206
+ if ($thisfile_riff[$RIFFsubtype]['id3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
1207
+ $info['id3v2'] = $getid3_temp->info['id3v2'];
1208
+ }
1209
+ unset($getid3_temp, $getid3_id3v2);
1210
+ }
1211
+ break;
1212
+ }
1213
+
1214
+ if (isset($thisfile_riff_WAVE['DISP']) && is_array($thisfile_riff_WAVE['DISP'])) {
1215
+ $thisfile_riff['comments']['title'][] = trim(substr($thisfile_riff_WAVE['DISP'][count($thisfile_riff_WAVE['DISP']) - 1]['data'], 4));
1216
+ }
1217
+ if (isset($thisfile_riff_WAVE['INFO']) && is_array($thisfile_riff_WAVE['INFO'])) {
1218
+ self::parseComments($thisfile_riff_WAVE['INFO'], $thisfile_riff['comments']);
1219
+ }
1220
+ if (isset($thisfile_riff['AVI ']['INFO']) && is_array($thisfile_riff['AVI ']['INFO'])) {
1221
+ self::parseComments($thisfile_riff['AVI ']['INFO'], $thisfile_riff['comments']);
1222
+ }
1223
+
1224
+ if (empty($thisfile_audio['encoder']) && !empty($info['mpeg']['audio']['LAME']['short_version'])) {
1225
+ $thisfile_audio['encoder'] = $info['mpeg']['audio']['LAME']['short_version'];
1226
+ }
1227
+
1228
+ if (!isset($info['playtime_seconds'])) {
1229
+ $info['playtime_seconds'] = 0;
1230
+ }
1231
+ if (isset($thisfile_riff_raw['strh'][0]['dwLength']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1232
+ // needed for >2GB AVIs where 'avih' chunk only lists number of frames in that chunk, not entire movie
1233
+ $info['playtime_seconds'] = $thisfile_riff_raw['strh'][0]['dwLength'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1234
+ } elseif (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1235
+ $info['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1236
+ }
1237
+
1238
+ if ($info['playtime_seconds'] > 0) {
1239
+ if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1240
+
1241
+ if (!isset($info['bitrate'])) {
1242
+ $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1243
+ }
1244
+
1245
+ } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) {
1246
+
1247
+ if (!isset($thisfile_audio['bitrate'])) {
1248
+ $thisfile_audio['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1249
+ }
1250
+
1251
+ } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1252
+
1253
+ if (!isset($thisfile_video['bitrate'])) {
1254
+ $thisfile_video['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1255
+ }
1256
+
1257
+ }
1258
+ }
1259
+
1260
+
1261
+ if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && ($thisfile_audio['bitrate'] > 0) && ($info['playtime_seconds'] > 0)) {
1262
+
1263
+ $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1264
+ $thisfile_audio['bitrate'] = 0;
1265
+ $thisfile_video['bitrate'] = $info['bitrate'];
1266
+ foreach ($thisfile_riff_audio as $channelnumber => $audioinfoarray) {
1267
+ $thisfile_video['bitrate'] -= $audioinfoarray['bitrate'];
1268
+ $thisfile_audio['bitrate'] += $audioinfoarray['bitrate'];
1269
+ }
1270
+ if ($thisfile_video['bitrate'] <= 0) {
1271
+ unset($thisfile_video['bitrate']);
1272
+ }
1273
+ if ($thisfile_audio['bitrate'] <= 0) {
1274
+ unset($thisfile_audio['bitrate']);
1275
+ }
1276
+ }
1277
+
1278
+ if (isset($info['mpeg']['audio'])) {
1279
+ $thisfile_audio_dataformat = 'mp'.$info['mpeg']['audio']['layer'];
1280
+ $thisfile_audio['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1281
+ $thisfile_audio['channels'] = $info['mpeg']['audio']['channels'];
1282
+ $thisfile_audio['bitrate'] = $info['mpeg']['audio']['bitrate'];
1283
+ $thisfile_audio['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1284
+ if (!empty($info['mpeg']['audio']['codec'])) {
1285
+ $thisfile_audio['codec'] = $info['mpeg']['audio']['codec'].' '.$thisfile_audio['codec'];
1286
+ }
1287
+ if (!empty($thisfile_audio['streams'])) {
1288
+ foreach ($thisfile_audio['streams'] as $streamnumber => $streamdata) {
1289
+ if ($streamdata['dataformat'] == $thisfile_audio_dataformat) {
1290
+ $thisfile_audio['streams'][$streamnumber]['sample_rate'] = $thisfile_audio['sample_rate'];
1291
+ $thisfile_audio['streams'][$streamnumber]['channels'] = $thisfile_audio['channels'];
1292
+ $thisfile_audio['streams'][$streamnumber]['bitrate'] = $thisfile_audio['bitrate'];
1293
+ $thisfile_audio['streams'][$streamnumber]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
1294
+ $thisfile_audio['streams'][$streamnumber]['codec'] = $thisfile_audio['codec'];
1295
+ }
1296
+ }
1297
+ }
1298
+ $getid3_mp3 = new getid3_mp3($this->getid3);
1299
+ $thisfile_audio['encoder_options'] = $getid3_mp3->GuessEncoderOptions();
1300
+ unset($getid3_mp3);
1301
+ }
1302
+
1303
+
1304
+ if (!empty($thisfile_riff_raw['fmt ']['wBitsPerSample']) && ($thisfile_riff_raw['fmt ']['wBitsPerSample'] > 0)) {
1305
+ switch ($thisfile_audio_dataformat) {
1306
+ case 'ac3':
1307
+ // ignore bits_per_sample
1308
+ break;
1309
+
1310
+ default:
1311
+ $thisfile_audio['bits_per_sample'] = $thisfile_riff_raw['fmt ']['wBitsPerSample'];
1312
+ break;
1313
+ }
1314
+ }
1315
+
1316
+
1317
+ if (empty($thisfile_riff_raw)) {
1318
+ unset($thisfile_riff['raw']);
1319
+ }
1320
+ if (empty($thisfile_riff_audio)) {
1321
+ unset($thisfile_riff['audio']);
1322
+ }
1323
+ if (empty($thisfile_riff_video)) {
1324
+ unset($thisfile_riff['video']);
1325
+ }
1326
+
1327
+ return true;
1328
+ }
1329
+
1330
+ public function ParseRIFFAMV($startoffset, $maxoffset) {
1331
+ // AMV files are RIFF-AVI files with parts of the spec deliberately broken, such as chunk size fields hardcoded to zero (because players known in hardware that these fields are always a certain size
1332
+
1333
+ // https://code.google.com/p/amv-codec-tools/wiki/AmvDocumentation
1334
+ //typedef struct _amvmainheader {
1335
+ //FOURCC fcc; // 'amvh'
1336
+ //DWORD cb;
1337
+ //DWORD dwMicroSecPerFrame;
1338
+ //BYTE reserve[28];
1339
+ //DWORD dwWidth;
1340
+ //DWORD dwHeight;
1341
+ //DWORD dwSpeed;
1342
+ //DWORD reserve0;
1343
+ //DWORD reserve1;
1344
+ //BYTE bTimeSec;
1345
+ //BYTE bTimeMin;
1346
+ //WORD wTimeHour;
1347
+ //} AMVMAINHEADER;
1348
+
1349
+ $info = &$this->getid3->info;
1350
+ $RIFFchunk = false;
1351
+
1352
+ try {
1353
+
1354
+ $this->fseek($startoffset);
1355
+ $maxoffset = min($maxoffset, $info['avdataend']);
1356
+ $AMVheader = $this->fread(284);
1357
+ if (substr($AMVheader, 0, 8) != 'hdrlamvh') {
1358
+ throw new Exception('expecting "hdrlamv" at offset '.($startoffset + 0).', found "'.substr($AMVheader, 0, 8).'"');
1359
+ }
1360
+ if (substr($AMVheader, 8, 4) != "\x38\x00\x00\x00") { // "amvh" chunk size, hardcoded to 0x38 = 56 bytes
1361
+ throw new Exception('expecting "0x38000000" at offset '.($startoffset + 8).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 8, 4)).'"');
1362
+ }
1363
+ $RIFFchunk = array();
1364
+ $RIFFchunk['amvh']['us_per_frame'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 12, 4));
1365
+ $RIFFchunk['amvh']['reserved28'] = substr($AMVheader, 16, 28); // null? reserved?
1366
+ $RIFFchunk['amvh']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 44, 4));
1367
+ $RIFFchunk['amvh']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 48, 4));
1368
+ $RIFFchunk['amvh']['frame_rate_int'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 52, 4));
1369
+ $RIFFchunk['amvh']['reserved0'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 56, 4)); // 1? reserved?
1370
+ $RIFFchunk['amvh']['reserved1'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 60, 4)); // 0? reserved?
1371
+ $RIFFchunk['amvh']['runtime_sec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 64, 1));
1372
+ $RIFFchunk['amvh']['runtime_min'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 65, 1));
1373
+ $RIFFchunk['amvh']['runtime_hrs'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 66, 2));
1374
+
1375
+ $info['video']['frame_rate'] = 1000000 / $RIFFchunk['amvh']['us_per_frame'];
1376
+ $info['video']['resolution_x'] = $RIFFchunk['amvh']['resolution_x'];
1377
+ $info['video']['resolution_y'] = $RIFFchunk['amvh']['resolution_y'];
1378
+ $info['playtime_seconds'] = ($RIFFchunk['amvh']['runtime_hrs'] * 3600) + ($RIFFchunk['amvh']['runtime_min'] * 60) + $RIFFchunk['amvh']['runtime_sec'];
1379
+
1380
+ // the rest is all hardcoded(?) and does not appear to be useful until you get to audio info at offset 256, even then everything is probably hardcoded
1381
+
1382
+ if (substr($AMVheader, 68, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x38\x00\x00\x00") {
1383
+ throw new Exception('expecting "LIST<0x00000000>strlstrh<0x38000000>" at offset '.($startoffset + 68).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 68, 20)).'"');
1384
+ }
1385
+ // followed by 56 bytes of null: substr($AMVheader, 88, 56) -> 144
1386
+ if (substr($AMVheader, 144, 8) != 'strf'."\x24\x00\x00\x00") {
1387
+ throw new Exception('expecting "strf<0x24000000>" at offset '.($startoffset + 144).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 144, 8)).'"');
1388
+ }
1389
+ // followed by 36 bytes of null: substr($AMVheader, 144, 36) -> 180
1390
+
1391
+ if (substr($AMVheader, 188, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x30\x00\x00\x00") {
1392
+ throw new Exception('expecting "LIST<0x00000000>strlstrh<0x30000000>" at offset '.($startoffset + 188).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 188, 20)).'"');
1393
+ }
1394
+ // followed by 48 bytes of null: substr($AMVheader, 208, 48) -> 256
1395
+ if (substr($AMVheader, 256, 8) != 'strf'."\x14\x00\x00\x00") {
1396
+ throw new Exception('expecting "strf<0x14000000>" at offset '.($startoffset + 256).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 256, 8)).'"');
1397
+ }
1398
+ // followed by 20 bytes of a modified WAVEFORMATEX:
1399
+ // typedef struct {
1400
+ // WORD wFormatTag; //(Fixme: this is equal to PCM's 0x01 format code)
1401
+ // WORD nChannels; //(Fixme: this is always 1)
1402
+ // DWORD nSamplesPerSec; //(Fixme: for all known sample files this is equal to 22050)
1403
+ // DWORD nAvgBytesPerSec; //(Fixme: for all known sample files this is equal to 44100)
1404
+ // WORD nBlockAlign; //(Fixme: this seems to be 2 in AMV files, is this correct ?)
1405
+ // WORD wBitsPerSample; //(Fixme: this seems to be 16 in AMV files instead of the expected 4)
1406
+ // WORD cbSize; //(Fixme: this seems to be 0 in AMV files)
1407
+ // WORD reserved;
1408
+ // } WAVEFORMATEX;
1409
+ $RIFFchunk['strf']['wformattag'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 264, 2));
1410
+ $RIFFchunk['strf']['nchannels'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 266, 2));
1411
+ $RIFFchunk['strf']['nsamplespersec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 268, 4));
1412
+ $RIFFchunk['strf']['navgbytespersec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 272, 4));
1413
+ $RIFFchunk['strf']['nblockalign'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 276, 2));
1414
+ $RIFFchunk['strf']['wbitspersample'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 278, 2));
1415
+ $RIFFchunk['strf']['cbsize'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 280, 2));
1416
+ $RIFFchunk['strf']['reserved'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 282, 2));
1417
+
1418
+
1419
+ $info['audio']['lossless'] = false;
1420
+ $info['audio']['sample_rate'] = $RIFFchunk['strf']['nsamplespersec'];
1421
+ $info['audio']['channels'] = $RIFFchunk['strf']['nchannels'];
1422
+ $info['audio']['bits_per_sample'] = $RIFFchunk['strf']['wbitspersample'];
1423
+ $info['audio']['bitrate'] = $info['audio']['sample_rate'] * $info['audio']['channels'] * $info['audio']['bits_per_sample'];
1424
+ $info['audio']['bitrate_mode'] = 'cbr';
1425
+
1426
+
1427
+ } catch (getid3_exception $e) {
1428
+ if ($e->getCode() == 10) {
1429
+ $this->warning('RIFFAMV parser: '.$e->getMessage());
1430
+ } else {
1431
+ throw $e;
1432
+ }
1433
+ }
1434
+
1435
+ return $RIFFchunk;
1436
+ }
1437
+
1438
+
1439
+ public function ParseRIFF($startoffset, $maxoffset) {
1440
+ $info = &$this->getid3->info;
1441
+
1442
+ $RIFFchunk = false;
1443
+ $FoundAllChunksWeNeed = false;
1444
+
1445
+ try {
1446
+ $this->fseek($startoffset);
1447
+ $maxoffset = min($maxoffset, $info['avdataend']);
1448
+ while ($this->ftell() < $maxoffset) {
1449
+ $chunknamesize = $this->fread(8);
1450
+ //$chunkname = substr($chunknamesize, 0, 4);
1451
+ $chunkname = str_replace("\x00", '_', substr($chunknamesize, 0, 4)); // note: chunk names of 4 null bytes do appear to be legal (has been observed inside INFO and PRMI chunks, for example), but makes traversing array keys more difficult
1452
+ $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4));
1453
+ //if (strlen(trim($chunkname, "\x00")) < 4) {
1454
+ if (strlen($chunkname) < 4) {
1455
+ $this->error('Expecting chunk name at offset '.($this->ftell() - 8).' but found nothing. Aborting RIFF parsing.');
1456
+ break;
1457
+ }
1458
+ if (($chunksize == 0) && ($chunkname != 'JUNK')) {
1459
+ $this->warning('Chunk ('.$chunkname.') size at offset '.($this->ftell() - 4).' is zero. Aborting RIFF parsing.');
1460
+ break;
1461
+ }
1462
+ if (($chunksize % 2) != 0) {
1463
+ // all structures are packed on word boundaries
1464
+ $chunksize++;
1465
+ }
1466
+
1467
+ switch ($chunkname) {
1468
+ case 'LIST':
1469
+ $listname = $this->fread(4);
1470
+ if (preg_match('#^(movi|rec )$#i', $listname)) {
1471
+ $RIFFchunk[$listname]['offset'] = $this->ftell() - 4;
1472
+ $RIFFchunk[$listname]['size'] = $chunksize;
1473
+
1474
+ if (!$FoundAllChunksWeNeed) {
1475
+ $WhereWeWere = $this->ftell();
1476
+ $AudioChunkHeader = $this->fread(12);
1477
+ $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2);
1478
+ $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2);
1479
+ $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
1480
+
1481
+ if ($AudioChunkStreamType == 'wb') {
1482
+ $FirstFourBytes = substr($AudioChunkHeader, 8, 4);
1483
+ if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', $FirstFourBytes)) {
1484
+ // MP3
1485
+ if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
1486
+ $getid3_temp = new getID3();
1487
+ $getid3_temp->openfile($this->getid3->filename);
1488
+ $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
1489
+ $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
1490
+ $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
1491
+ $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
1492
+ if (isset($getid3_temp->info['mpeg']['audio'])) {
1493
+ $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio'];
1494
+ $info['audio'] = $getid3_temp->info['audio'];
1495
+ $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
1496
+ $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1497
+ $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
1498
+ $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1499
+ $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1500
+ //$info['bitrate'] = $info['audio']['bitrate'];
1501
+ }
1502
+ unset($getid3_temp, $getid3_mp3);
1503
+ }
1504
+
1505
+ } elseif (strpos($FirstFourBytes, getid3_ac3::syncword) === 0) {
1506
+
1507
+ // AC3
1508
+ $getid3_temp = new getID3();
1509
+ $getid3_temp->openfile($this->getid3->filename);
1510
+ $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
1511
+ $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
1512
+ $getid3_ac3 = new getid3_ac3($getid3_temp);
1513
+ $getid3_ac3->Analyze();
1514
+ if (empty($getid3_temp->info['error'])) {
1515
+ $info['audio'] = $getid3_temp->info['audio'];
1516
+ $info['ac3'] = $getid3_temp->info['ac3'];
1517
+ if (!empty($getid3_temp->info['warning'])) {
1518
+ foreach ($getid3_temp->info['warning'] as $key => $value) {
1519
+ $info['warning'][] = $value;
1520
+ }
1521
+ }
1522
+ }
1523
+ unset($getid3_temp, $getid3_ac3);
1524
+ }
1525
+ }
1526
+ $FoundAllChunksWeNeed = true;
1527
+ $this->fseek($WhereWeWere);
1528
+ }
1529
+ $this->fseek($chunksize - 4, SEEK_CUR);
1530
+
1531
+ } else {
1532
+
1533
+ if (!isset($RIFFchunk[$listname])) {
1534
+ $RIFFchunk[$listname] = array();
1535
+ }
1536
+ $LISTchunkParent = $listname;
1537
+ $LISTchunkMaxOffset = $this->ftell() - 4 + $chunksize;
1538
+ if ($parsedChunk = $this->ParseRIFF($this->ftell(), $LISTchunkMaxOffset)) {
1539
+ $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
1540
+ }
1541
+
1542
+ }
1543
+ break;
1544
+
1545
+ default:
1546
+ if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) {
1547
+ $this->fseek($chunksize, SEEK_CUR);
1548
+ break;
1549
+ }
1550
+ $thisindex = 0;
1551
+ if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
1552
+ $thisindex = count($RIFFchunk[$chunkname]);
1553
+ }
1554
+ $RIFFchunk[$chunkname][$thisindex]['offset'] = $this->ftell() - 8;
1555
+ $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize;
1556
+ switch ($chunkname) {
1557
+ case 'data':
1558
+ $info['avdataoffset'] = $this->ftell();
1559
+ $info['avdataend'] = $info['avdataoffset'] + $chunksize;
1560
+
1561
+ $testData = $this->fread(36);
1562
+ if ($testData === '') {
1563
+ break;
1564
+ }
1565
+ if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', substr($testData, 0, 4))) {
1566
+
1567
+ // Probably is MP3 data
1568
+ if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($testData, 0, 4))) {
1569
+ $getid3_temp = new getID3();
1570
+ $getid3_temp->openfile($this->getid3->filename);
1571
+ $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1572
+ $getid3_temp->info['avdataend'] = $info['avdataend'];
1573
+ $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
1574
+ $getid3_mp3->getOnlyMPEGaudioInfo($info['avdataoffset'], false);
1575
+ if (empty($getid3_temp->info['error'])) {
1576
+ $info['audio'] = $getid3_temp->info['audio'];
1577
+ $info['mpeg'] = $getid3_temp->info['mpeg'];
1578
+ }
1579
+ unset($getid3_temp, $getid3_mp3);
1580
+ }
1581
+
1582
+ } elseif (($isRegularAC3 = (substr($testData, 0, 2) == getid3_ac3::syncword)) || substr($testData, 8, 2) == strrev(getid3_ac3::syncword)) {
1583
+
1584
+ // This is probably AC-3 data
1585
+ $getid3_temp = new getID3();
1586
+ if ($isRegularAC3) {
1587
+ $getid3_temp->openfile($this->getid3->filename);
1588
+ $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1589
+ $getid3_temp->info['avdataend'] = $info['avdataend'];
1590
+ }
1591
+ $getid3_ac3 = new getid3_ac3($getid3_temp);
1592
+ if ($isRegularAC3) {
1593
+ $getid3_ac3->Analyze();
1594
+ } else {
1595
+ // Dolby Digital WAV
1596
+ // AC-3 content, but not encoded in same format as normal AC-3 file
1597
+ // For one thing, byte order is swapped
1598
+ $ac3_data = '';
1599
+ for ($i = 0; $i < 28; $i += 2) {
1600
+ $ac3_data .= substr($testData, 8 + $i + 1, 1);
1601
+ $ac3_data .= substr($testData, 8 + $i + 0, 1);
1602
+ }
1603
+ $getid3_ac3->AnalyzeString($ac3_data);
1604
+ }
1605
+
1606
+ if (empty($getid3_temp->info['error'])) {
1607
+ $info['audio'] = $getid3_temp->info['audio'];
1608
+ $info['ac3'] = $getid3_temp->info['ac3'];
1609
+ if (!empty($getid3_temp->info['warning'])) {
1610
+ foreach ($getid3_temp->info['warning'] as $newerror) {
1611
+ $this->warning('getid3_ac3() says: ['.$newerror.']');
1612
+ }
1613
+ }
1614
+ }
1615
+ unset($getid3_temp, $getid3_ac3);
1616
+
1617
+ } elseif (preg_match('/^('.implode('|', array_map('preg_quote', getid3_dts::$syncwords)).')/', $testData)) {
1618
+
1619
+ // This is probably DTS data
1620
+ $getid3_temp = new getID3();
1621
+ $getid3_temp->openfile($this->getid3->filename);
1622
+ $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1623
+ $getid3_dts = new getid3_dts($getid3_temp);
1624
+ $getid3_dts->Analyze();
1625
+ if (empty($getid3_temp->info['error'])) {
1626
+ $info['audio'] = $getid3_temp->info['audio'];
1627
+ $info['dts'] = $getid3_temp->info['dts'];
1628
+ $info['playtime_seconds'] = $getid3_temp->info['playtime_seconds']; // may not match RIFF calculations since DTS-WAV often used 14/16 bit-word packing
1629
+ if (!empty($getid3_temp->info['warning'])) {
1630
+ foreach ($getid3_temp->info['warning'] as $newerror) {
1631
+ $this->warning('getid3_dts() says: ['.$newerror.']');
1632
+ }
1633
+ }
1634
+ }
1635
+
1636
+ unset($getid3_temp, $getid3_dts);
1637
+
1638
+ } elseif (substr($testData, 0, 4) == 'wvpk') {
1639
+
1640
+ // This is WavPack data
1641
+ $info['wavpack']['offset'] = $info['avdataoffset'];
1642
+ $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($testData, 4, 4));
1643
+ $this->parseWavPackHeader(substr($testData, 8, 28));
1644
+
1645
+ } else {
1646
+ // This is some other kind of data (quite possibly just PCM)
1647
+ // do nothing special, just skip it
1648
+ }
1649
+ $nextoffset = $info['avdataend'];
1650
+ $this->fseek($nextoffset);
1651
+ break;
1652
+
1653
+ case 'iXML':
1654
+ case 'bext':
1655
+ case 'cart':
1656
+ case 'fmt ':
1657
+ case 'strh':
1658
+ case 'strf':
1659
+ case 'indx':
1660
+ case 'MEXT':
1661
+ case 'DISP':
1662
+ // always read data in
1663
+ case 'JUNK':
1664
+ // should be: never read data in
1665
+ // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc)
1666
+ if ($chunksize < 1048576) {
1667
+ if ($chunksize > 0) {
1668
+ $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1669
+ if ($chunkname == 'JUNK') {
1670
+ if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) {
1671
+ // only keep text characters [chr(32)-chr(127)]
1672
+ $info['riff']['comments']['junk'][] = trim($matches[1]);
1673
+ }
1674
+ // but if nothing there, ignore
1675
+ // remove the key in either case
1676
+ unset($RIFFchunk[$chunkname][$thisindex]['data']);
1677
+ }
1678
+ }
1679
+ } else {
1680
+ $this->warning('Chunk "'.$chunkname.'" at offset '.$this->ftell().' is unexpectedly larger than 1MB (claims to be '.number_format($chunksize).' bytes), skipping data');
1681
+ $this->fseek($chunksize, SEEK_CUR);
1682
+ }
1683
+ break;
1684
+
1685
+ //case 'IDVX':
1686
+ // $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize));
1687
+ // break;
1688
+
1689
+ default:
1690
+ if (!empty($LISTchunkParent) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) {
1691
+ $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
1692
+ $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size'];
1693
+ unset($RIFFchunk[$chunkname][$thisindex]['offset']);
1694
+ unset($RIFFchunk[$chunkname][$thisindex]['size']);
1695
+ if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
1696
+ unset($RIFFchunk[$chunkname][$thisindex]);
1697
+ }
1698
+ if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
1699
+ unset($RIFFchunk[$chunkname]);
1700
+ }
1701
+ $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1702
+ } elseif ($chunksize < 2048) {
1703
+ // only read data in if smaller than 2kB
1704
+ $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1705
+ } else {
1706
+ $this->fseek($chunksize, SEEK_CUR);
1707
+ }
1708
+ break;
1709
+ }
1710
+ break;
1711
+ }
1712
+ }
1713
+
1714
+ } catch (getid3_exception $e) {
1715
+ if ($e->getCode() == 10) {
1716
+ $this->warning('RIFF parser: '.$e->getMessage());
1717
+ } else {
1718
+ throw $e;
1719
+ }
1720
+ }
1721
+
1722
+ return $RIFFchunk;
1723
+ }
1724
+
1725
+ public function ParseRIFFdata(&$RIFFdata) {
1726
+ $info = &$this->getid3->info;
1727
+ if ($RIFFdata) {
1728
+ $tempfile = tempnam(GETID3_TEMP_DIR, 'getID3');
1729
+ $fp_temp = fopen($tempfile, 'wb');
1730
+ $RIFFdataLength = strlen($RIFFdata);
1731
+ $NewLengthString = getid3_lib::LittleEndian2String($RIFFdataLength, 4);
1732
+ for ($i = 0; $i < 4; $i++) {
1733
+ $RIFFdata[($i + 4)] = $NewLengthString[$i];
1734
+ }
1735
+ fwrite($fp_temp, $RIFFdata);
1736
+ fclose($fp_temp);
1737
+
1738
+ $getid3_temp = new getID3();
1739
+ $getid3_temp->openfile($tempfile);
1740
+ $getid3_temp->info['filesize'] = $RIFFdataLength;
1741
+ $getid3_temp->info['filenamepath'] = $info['filenamepath'];
1742
+ $getid3_temp->info['tags'] = $info['tags'];
1743
+ $getid3_temp->info['warning'] = $info['warning'];
1744
+ $getid3_temp->info['error'] = $info['error'];
1745
+ $getid3_temp->info['comments'] = $info['comments'];
1746
+ $getid3_temp->info['audio'] = (isset($info['audio']) ? $info['audio'] : array());
1747
+ $getid3_temp->info['video'] = (isset($info['video']) ? $info['video'] : array());
1748
+ $getid3_riff = new getid3_riff($getid3_temp);
1749
+ $getid3_riff->Analyze();
1750
+
1751
+ $info['riff'] = $getid3_temp->info['riff'];
1752
+ $info['warning'] = $getid3_temp->info['warning'];
1753
+ $info['error'] = $getid3_temp->info['error'];
1754
+ $info['tags'] = $getid3_temp->info['tags'];
1755
+ $info['comments'] = $getid3_temp->info['comments'];
1756
+ unset($getid3_riff, $getid3_temp);
1757
+ unlink($tempfile);
1758
+ }
1759
+ return false;
1760
+ }
1761
+
1762
+ public static function parseComments(&$RIFFinfoArray, &$CommentsTargetArray) {
1763
+ $RIFFinfoKeyLookup = array(
1764
+ 'IARL'=>'archivallocation',
1765
+ 'IART'=>'artist',
1766
+ 'ICDS'=>'costumedesigner',
1767
+ 'ICMS'=>'commissionedby',
1768
+ 'ICMT'=>'comment',
1769
+ 'ICNT'=>'country',
1770
+ 'ICOP'=>'copyright',
1771
+ 'ICRD'=>'creationdate',
1772
+ 'IDIM'=>'dimensions',
1773
+ 'IDIT'=>'digitizationdate',
1774
+ 'IDPI'=>'resolution',
1775
+ 'IDST'=>'distributor',
1776
+ 'IEDT'=>'editor',
1777
+ 'IENG'=>'engineers',
1778
+ 'IFRM'=>'accountofparts',
1779
+ 'IGNR'=>'genre',
1780
+ 'IKEY'=>'keywords',
1781
+ 'ILGT'=>'lightness',
1782
+ 'ILNG'=>'language',
1783
+ 'IMED'=>'orignalmedium',
1784
+ 'IMUS'=>'composer',
1785
+ 'INAM'=>'title',
1786
+ 'IPDS'=>'productiondesigner',
1787
+ 'IPLT'=>'palette',
1788
+ 'IPRD'=>'product',
1789
+ 'IPRO'=>'producer',
1790
+ 'IPRT'=>'part',
1791
+ 'IRTD'=>'rating',
1792
+ 'ISBJ'=>'subject',
1793
+ 'ISFT'=>'software',
1794
+ 'ISGN'=>'secondarygenre',
1795
+ 'ISHP'=>'sharpness',
1796
+ 'ISRC'=>'sourcesupplier',
1797
+ 'ISRF'=>'digitizationsource',
1798
+ 'ISTD'=>'productionstudio',
1799
+ 'ISTR'=>'starring',
1800
+ 'ITCH'=>'encoded_by',
1801
+ 'IWEB'=>'url',
1802
+ 'IWRI'=>'writer',
1803
+ '____'=>'comment',
1804
+ );
1805
+ foreach ($RIFFinfoKeyLookup as $key => $value) {
1806
+ if (isset($RIFFinfoArray[$key])) {
1807
+ foreach ($RIFFinfoArray[$key] as $commentid => $commentdata) {
1808
+ if (trim($commentdata['data']) != '') {
1809
+ if (isset($CommentsTargetArray[$value])) {
1810
+ $CommentsTargetArray[$value][] = trim($commentdata['data']);
1811
+ } else {
1812
+ $CommentsTargetArray[$value] = array(trim($commentdata['data']));
1813
+ }
1814
+ }
1815
+ }
1816
+ }
1817
+ }
1818
+ return true;
1819
+ }
1820
+
1821
+ public static function parseWAVEFORMATex($WaveFormatExData) {
1822
+ // shortcut
1823
+ $WaveFormatEx['raw'] = array();
1824
+ $WaveFormatEx_raw = &$WaveFormatEx['raw'];
1825
+
1826
+ $WaveFormatEx_raw['wFormatTag'] = substr($WaveFormatExData, 0, 2);
1827
+ $WaveFormatEx_raw['nChannels'] = substr($WaveFormatExData, 2, 2);
1828
+ $WaveFormatEx_raw['nSamplesPerSec'] = substr($WaveFormatExData, 4, 4);
1829
+ $WaveFormatEx_raw['nAvgBytesPerSec'] = substr($WaveFormatExData, 8, 4);
1830
+ $WaveFormatEx_raw['nBlockAlign'] = substr($WaveFormatExData, 12, 2);
1831
+ $WaveFormatEx_raw['wBitsPerSample'] = substr($WaveFormatExData, 14, 2);
1832
+ if (strlen($WaveFormatExData) > 16) {
1833
+ $WaveFormatEx_raw['cbSize'] = substr($WaveFormatExData, 16, 2);
1834
+ }
1835
+ $WaveFormatEx_raw = array_map('getid3_lib::LittleEndian2Int', $WaveFormatEx_raw);
1836
+
1837
+ $WaveFormatEx['codec'] = self::wFormatTagLookup($WaveFormatEx_raw['wFormatTag']);
1838
+ $WaveFormatEx['channels'] = $WaveFormatEx_raw['nChannels'];
1839
+ $WaveFormatEx['sample_rate'] = $WaveFormatEx_raw['nSamplesPerSec'];
1840
+ $WaveFormatEx['bitrate'] = $WaveFormatEx_raw['nAvgBytesPerSec'] * 8;
1841
+ $WaveFormatEx['bits_per_sample'] = $WaveFormatEx_raw['wBitsPerSample'];
1842
+
1843
+ return $WaveFormatEx;
1844
+ }
1845
+
1846
+ public function parseWavPackHeader($WavPackChunkData) {
1847
+ // typedef struct {
1848
+ // char ckID [4];
1849
+ // long ckSize;
1850
+ // short version;
1851
+ // short bits; // added for version 2.00
1852
+ // short flags, shift; // added for version 3.00
1853
+ // long total_samples, crc, crc2;
1854
+ // char extension [4], extra_bc, extras [3];
1855
+ // } WavpackHeader;
1856
+
1857
+ // shortcut
1858
+ $info = &$this->getid3->info;
1859
+ $info['wavpack'] = array();
1860
+ $thisfile_wavpack = &$info['wavpack'];
1861
+
1862
+ $thisfile_wavpack['version'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 0, 2));
1863
+ if ($thisfile_wavpack['version'] >= 2) {
1864
+ $thisfile_wavpack['bits'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 2, 2));
1865
+ }
1866
+ if ($thisfile_wavpack['version'] >= 3) {
1867
+ $thisfile_wavpack['flags_raw'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 4, 2));
1868
+ $thisfile_wavpack['shift'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 6, 2));
1869
+ $thisfile_wavpack['total_samples'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 8, 4));
1870
+ $thisfile_wavpack['crc1'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 12, 4));
1871
+ $thisfile_wavpack['crc2'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 16, 4));
1872
+ $thisfile_wavpack['extension'] = substr($WavPackChunkData, 20, 4);
1873
+ $thisfile_wavpack['extra_bc'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 24, 1));
1874
+ for ($i = 0; $i <= 2; $i++) {
1875
+ $thisfile_wavpack['extras'][] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 25 + $i, 1));
1876
+ }
1877
+
1878
+ // shortcut
1879
+ $thisfile_wavpack['flags'] = array();
1880
+ $thisfile_wavpack_flags = &$thisfile_wavpack['flags'];
1881
+
1882
+ $thisfile_wavpack_flags['mono'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000001);
1883
+ $thisfile_wavpack_flags['fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000002);
1884
+ $thisfile_wavpack_flags['raw_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000004);
1885
+ $thisfile_wavpack_flags['calc_noise'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000008);
1886
+ $thisfile_wavpack_flags['high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000010);
1887
+ $thisfile_wavpack_flags['3_byte_samples'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000020);
1888
+ $thisfile_wavpack_flags['over_20_bits'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000040);
1889
+ $thisfile_wavpack_flags['use_wvc'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000080);
1890
+ $thisfile_wavpack_flags['noiseshaping'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000100);
1891
+ $thisfile_wavpack_flags['very_fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000200);
1892
+ $thisfile_wavpack_flags['new_high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000400);
1893
+ $thisfile_wavpack_flags['cancel_extreme'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000800);
1894
+ $thisfile_wavpack_flags['cross_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x001000);
1895
+ $thisfile_wavpack_flags['new_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x002000);
1896
+ $thisfile_wavpack_flags['joint_stereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x004000);
1897
+ $thisfile_wavpack_flags['extra_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x008000);
1898
+ $thisfile_wavpack_flags['override_noiseshape'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x010000);
1899
+ $thisfile_wavpack_flags['override_jointstereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x020000);
1900
+ $thisfile_wavpack_flags['copy_source_filetime'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x040000);
1901
+ $thisfile_wavpack_flags['create_exe'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x080000);
1902
+ }
1903
+
1904
+ return true;
1905
+ }
1906
+
1907
+ public static function ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true) {
1908
+
1909
+ $parsed['biSize'] = substr($BITMAPINFOHEADER, 0, 4); // number of bytes required by the BITMAPINFOHEADER structure
1910
+ $parsed['biWidth'] = substr($BITMAPINFOHEADER, 4, 4); // width of the bitmap in pixels
1911
+ $parsed['biHeight'] = substr($BITMAPINFOHEADER, 8, 4); // height of the bitmap in pixels. If biHeight is positive, the bitmap is a 'bottom-up' DIB and its origin is the lower left corner. If biHeight is negative, the bitmap is a 'top-down' DIB and its origin is the upper left corner
1912
+ $parsed['biPlanes'] = substr($BITMAPINFOHEADER, 12, 2); // number of color planes on the target device. In most cases this value must be set to 1
1913
+ $parsed['biBitCount'] = substr($BITMAPINFOHEADER, 14, 2); // Specifies the number of bits per pixels
1914
+ $parsed['biSizeImage'] = substr($BITMAPINFOHEADER, 20, 4); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures)
1915
+ $parsed['biXPelsPerMeter'] = substr($BITMAPINFOHEADER, 24, 4); // horizontal resolution, in pixels per metre, of the target device
1916
+ $parsed['biYPelsPerMeter'] = substr($BITMAPINFOHEADER, 28, 4); // vertical resolution, in pixels per metre, of the target device
1917
+ $parsed['biClrUsed'] = substr($BITMAPINFOHEADER, 32, 4); // actual number of color indices in the color table used by the bitmap. If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression
1918
+ $parsed['biClrImportant'] = substr($BITMAPINFOHEADER, 36, 4); // number of color indices that are considered important for displaying the bitmap. If this value is zero, all colors are important
1919
+ $parsed = array_map('getid3_lib::'.($littleEndian ? 'Little' : 'Big').'Endian2Int', $parsed);
1920
+
1921
+ $parsed['fourcc'] = substr($BITMAPINFOHEADER, 16, 4); // compression identifier
1922
+
1923
+ return $parsed;
1924
+ }
1925
+
1926
+ public static function ParseDIVXTAG($DIVXTAG, $raw=false) {
1927
+ // structure from "IDivX" source, Form1.frm, by "Greg Frazier of Daemonic Software Group", email: gfrazier@icestorm.net, web: http://dsg.cjb.net/
1928
+ // source available at http://files.divx-digest.com/download/c663efe7ef8ad2e90bf4af4d3ea6188a/on0SWN2r/edit/IDivX.zip
1929
+ // 'Byte Layout: '1111111111111111
1930
+ // '32 for Movie - 1 '1111111111111111
1931
+ // '28 for Author - 6 '6666666666666666
1932
+ // '4 for year - 2 '6666666666662222
1933
+ // '3 for genre - 3 '7777777777777777
1934
+ // '48 for Comments - 7 '7777777777777777
1935
+ // '1 for Rating - 4 '7777777777777777
1936
+ // '5 for Future Additions - 0 '333400000DIVXTAG
1937
+ // '128 bytes total
1938
+
1939
+ static $DIVXTAGgenre = array(
1940
+ 0 => 'Action',
1941
+ 1 => 'Action/Adventure',
1942
+ 2 => 'Adventure',
1943
+ 3 => 'Adult',
1944
+ 4 => 'Anime',
1945
+ 5 => 'Cartoon',
1946
+ 6 => 'Claymation',
1947
+ 7 => 'Comedy',
1948
+ 8 => 'Commercial',
1949
+ 9 => 'Documentary',
1950
+ 10 => 'Drama',
1951
+ 11 => 'Home Video',
1952
+ 12 => 'Horror',
1953
+ 13 => 'Infomercial',
1954
+ 14 => 'Interactive',
1955
+ 15 => 'Mystery',
1956
+ 16 => 'Music Video',
1957
+ 17 => 'Other',
1958
+ 18 => 'Religion',
1959
+ 19 => 'Sci Fi',
1960
+ 20 => 'Thriller',
1961
+ 21 => 'Western',
1962
+ ),
1963
+ $DIVXTAGrating = array(
1964
+ 0 => 'Unrated',
1965
+ 1 => 'G',
1966
+ 2 => 'PG',
1967
+ 3 => 'PG-13',
1968
+ 4 => 'R',
1969
+ 5 => 'NC-17',
1970
+ );
1971
+
1972
+ $parsed['title'] = trim(substr($DIVXTAG, 0, 32));
1973
+ $parsed['artist'] = trim(substr($DIVXTAG, 32, 28));
1974
+ $parsed['year'] = intval(trim(substr($DIVXTAG, 60, 4)));
1975
+ $parsed['comment'] = trim(substr($DIVXTAG, 64, 48));
1976
+ $parsed['genre_id'] = intval(trim(substr($DIVXTAG, 112, 3)));
1977
+ $parsed['rating_id'] = ord(substr($DIVXTAG, 115, 1));
1978
+ //$parsed['padding'] = substr($DIVXTAG, 116, 5); // 5-byte null
1979
+ //$parsed['magic'] = substr($DIVXTAG, 121, 7); // "DIVXTAG"
1980
+
1981
+ $parsed['genre'] = (isset($DIVXTAGgenre[$parsed['genre_id']]) ? $DIVXTAGgenre[$parsed['genre_id']] : $parsed['genre_id']);
1982
+ $parsed['rating'] = (isset($DIVXTAGrating[$parsed['rating_id']]) ? $DIVXTAGrating[$parsed['rating_id']] : $parsed['rating_id']);
1983
+
1984
+ if (!$raw) {
1985
+ unset($parsed['genre_id'], $parsed['rating_id']);
1986
+ foreach ($parsed as $key => $value) {
1987
+ if (!$value === '') {
1988
+ unset($parsed['key']);
1989
+ }
1990
+ }
1991
+ }
1992
+
1993
+ foreach ($parsed as $tag => $value) {
1994
+ $parsed[$tag] = array($value);
1995
+ }
1996
+
1997
+ return $parsed;
1998
+ }
1999
+
2000
+ public static function waveSNDMtagLookup($tagshortname) {
2001
+ $begin = __LINE__;
2002
+
2003
+ /** This is not a comment!
2004
+
2005
+ ©kwd keywords
2006
+ ©BPM bpm
2007
+ ©trt tracktitle
2008
+ ©des description
2009
+ ©gen category
2010
+ ©fin featuredinstrument
2011
+ ©LID longid
2012
+ ©bex bwdescription
2013
+ ©pub publisher
2014
+ ©cdt cdtitle
2015
+ ©alb library
2016
+ ©com composer
2017
+
2018
+ */
2019
+
2020
+ return getid3_lib::EmbeddedLookup($tagshortname, $begin, __LINE__, __FILE__, 'riff-sndm');
2021
+ }
2022
+
2023
+ public static function wFormatTagLookup($wFormatTag) {
2024
+
2025
+ $begin = __LINE__;
2026
+
2027
+ /** This is not a comment!
2028
+
2029
+ 0x0000 Microsoft Unknown Wave Format
2030
+ 0x0001 Pulse Code Modulation (PCM)
2031
+ 0x0002 Microsoft ADPCM
2032
+ 0x0003 IEEE Float
2033
+ 0x0004 Compaq Computer VSELP
2034
+ 0x0005 IBM CVSD
2035
+ 0x0006 Microsoft A-Law
2036
+ 0x0007 Microsoft mu-Law
2037
+ 0x0008 Microsoft DTS
2038
+ 0x0010 OKI ADPCM
2039
+ 0x0011 Intel DVI/IMA ADPCM
2040
+ 0x0012 Videologic MediaSpace ADPCM
2041
+ 0x0013 Sierra Semiconductor ADPCM
2042
+ 0x0014 Antex Electronics G.723 ADPCM
2043
+ 0x0015 DSP Solutions DigiSTD
2044
+ 0x0016 DSP Solutions DigiFIX
2045
+ 0x0017 Dialogic OKI ADPCM
2046
+ 0x0018 MediaVision ADPCM
2047
+ 0x0019 Hewlett-Packard CU
2048
+ 0x0020 Yamaha ADPCM
2049
+ 0x0021 Speech Compression Sonarc
2050
+ 0x0022 DSP Group TrueSpeech
2051
+ 0x0023 Echo Speech EchoSC1
2052
+ 0x0024 Audiofile AF36
2053
+ 0x0025 Audio Processing Technology APTX
2054
+ 0x0026 AudioFile AF10
2055
+ 0x0027 Prosody 1612
2056
+ 0x0028 LRC
2057
+ 0x0030 Dolby AC2
2058
+ 0x0031 Microsoft GSM 6.10
2059
+ 0x0032 MSNAudio
2060
+ 0x0033 Antex Electronics ADPCME
2061
+ 0x0034 Control Resources VQLPC
2062
+ 0x0035 DSP Solutions DigiREAL
2063
+ 0x0036 DSP Solutions DigiADPCM
2064
+ 0x0037 Control Resources CR10
2065
+ 0x0038 Natural MicroSystems VBXADPCM
2066
+ 0x0039 Crystal Semiconductor IMA ADPCM
2067
+ 0x003A EchoSC3
2068
+ 0x003B Rockwell ADPCM
2069
+ 0x003C Rockwell Digit LK
2070
+ 0x003D Xebec
2071
+ 0x0040 Antex Electronics G.721 ADPCM
2072
+ 0x0041 G.728 CELP
2073
+ 0x0042 MSG723
2074
+ 0x0050 MPEG Layer-2 or Layer-1
2075
+ 0x0052 RT24
2076
+ 0x0053 PAC
2077
+ 0x0055 MPEG Layer-3
2078
+ 0x0059 Lucent G.723
2079
+ 0x0060 Cirrus
2080
+ 0x0061 ESPCM
2081
+ 0x0062 Voxware
2082
+ 0x0063 Canopus Atrac
2083
+ 0x0064 G.726 ADPCM
2084
+ 0x0065 G.722 ADPCM
2085
+ 0x0066 DSAT
2086
+ 0x0067 DSAT Display
2087
+ 0x0069 Voxware Byte Aligned
2088
+ 0x0070 Voxware AC8
2089
+ 0x0071 Voxware AC10
2090
+ 0x0072 Voxware AC16
2091
+ 0x0073 Voxware AC20
2092
+ 0x0074 Voxware MetaVoice
2093
+ 0x0075 Voxware MetaSound
2094
+ 0x0076 Voxware RT29HW
2095
+ 0x0077 Voxware VR12
2096
+ 0x0078 Voxware VR18
2097
+ 0x0079 Voxware TQ40
2098
+ 0x0080 Softsound
2099
+ 0x0081 Voxware TQ60
2100
+ 0x0082 MSRT24
2101
+ 0x0083 G.729A
2102
+ 0x0084 MVI MV12
2103
+ 0x0085 DF G.726
2104
+ 0x0086 DF GSM610
2105
+ 0x0088 ISIAudio
2106
+ 0x0089 Onlive
2107
+ 0x0091 SBC24
2108
+ 0x0092 Dolby AC3 SPDIF
2109
+ 0x0093 MediaSonic G.723
2110
+ 0x0094 Aculab PLC Prosody 8kbps
2111
+ 0x0097 ZyXEL ADPCM
2112
+ 0x0098 Philips LPCBB
2113
+ 0x0099 Packed
2114
+ 0x00FF AAC
2115
+ 0x0100 Rhetorex ADPCM
2116
+ 0x0101 IBM mu-law
2117
+ 0x0102 IBM A-law
2118
+ 0x0103 IBM AVC Adaptive Differential Pulse Code Modulation (ADPCM)
2119
+ 0x0111 Vivo G.723
2120
+ 0x0112 Vivo Siren
2121
+ 0x0123 Digital G.723
2122
+ 0x0125 Sanyo LD ADPCM
2123
+ 0x0130 Sipro Lab Telecom ACELP NET
2124
+ 0x0131 Sipro Lab Telecom ACELP 4800
2125
+ 0x0132 Sipro Lab Telecom ACELP 8V3
2126
+ 0x0133 Sipro Lab Telecom G.729
2127
+ 0x0134 Sipro Lab Telecom G.729A
2128
+ 0x0135 Sipro Lab Telecom Kelvin
2129
+ 0x0140 Windows Media Video V8
2130
+ 0x0150 Qualcomm PureVoice
2131
+ 0x0151 Qualcomm HalfRate
2132
+ 0x0155 Ring Zero Systems TUB GSM
2133
+ 0x0160 Microsoft Audio 1
2134
+ 0x0161 Windows Media Audio V7 / V8 / V9
2135
+ 0x0162 Windows Media Audio Professional V9
2136
+ 0x0163 Windows Media Audio Lossless V9
2137
+ 0x0200 Creative Labs ADPCM
2138
+ 0x0202 Creative Labs Fastspeech8
2139
+ 0x0203 Creative Labs Fastspeech10
2140
+ 0x0210 UHER Informatic GmbH ADPCM
2141
+ 0x0220 Quarterdeck
2142
+ 0x0230 I-link Worldwide VC
2143
+ 0x0240 Aureal RAW Sport
2144
+ 0x0250 Interactive Products HSX
2145
+ 0x0251 Interactive Products RPELP
2146
+ 0x0260 Consistent Software CS2
2147
+ 0x0270 Sony SCX
2148
+ 0x0300 Fujitsu FM Towns Snd
2149
+ 0x0400 BTV Digital
2150
+ 0x0401 Intel Music Coder
2151
+ 0x0450 QDesign Music
2152
+ 0x0680 VME VMPCM
2153
+ 0x0681 AT&T Labs TPC
2154
+ 0x08AE ClearJump LiteWave
2155
+ 0x1000 Olivetti GSM
2156
+ 0x1001 Olivetti ADPCM
2157
+ 0x1002 Olivetti CELP
2158
+ 0x1003 Olivetti SBC
2159
+ 0x1004 Olivetti OPR
2160
+ 0x1100 Lernout & Hauspie Codec (0x1100)
2161
+ 0x1101 Lernout & Hauspie CELP Codec (0x1101)
2162
+ 0x1102 Lernout & Hauspie SBC Codec (0x1102)
2163
+ 0x1103 Lernout & Hauspie SBC Codec (0x1103)
2164
+ 0x1104 Lernout & Hauspie SBC Codec (0x1104)
2165
+ 0x1400 Norris
2166
+ 0x1401 AT&T ISIAudio
2167
+ 0x1500 Soundspace Music Compression
2168
+ 0x181C VoxWare RT24 Speech
2169
+ 0x1FC4 NCT Soft ALF2CD (www.nctsoft.com)
2170
+ 0x2000 Dolby AC3
2171
+ 0x2001 Dolby DTS
2172
+ 0x2002 WAVE_FORMAT_14_4
2173
+ 0x2003 WAVE_FORMAT_28_8
2174
+ 0x2004 WAVE_FORMAT_COOK
2175
+ 0x2005 WAVE_FORMAT_DNET
2176
+ 0x674F Ogg Vorbis 1
2177
+ 0x6750 Ogg Vorbis 2
2178
+ 0x6751 Ogg Vorbis 3
2179
+ 0x676F Ogg Vorbis 1+
2180
+ 0x6770 Ogg Vorbis 2+
2181
+ 0x6771 Ogg Vorbis 3+
2182
+ 0x7A21 GSM-AMR (CBR, no SID)
2183
+ 0x7A22 GSM-AMR (VBR, including SID)
2184
+ 0xFFFE WAVE_FORMAT_EXTENSIBLE
2185
+ 0xFFFF WAVE_FORMAT_DEVELOPMENT
2186
+
2187
+ */
2188
+
2189
+ return getid3_lib::EmbeddedLookup('0x'.str_pad(strtoupper(dechex($wFormatTag)), 4, '0', STR_PAD_LEFT), $begin, __LINE__, __FILE__, 'riff-wFormatTag');
2190
+ }
2191
+
2192
+ public static function fourccLookup($fourcc) {
2193
+
2194
+ $begin = __LINE__;
2195
+
2196
+ /** This is not a comment!
2197
+
2198
+ swot http://developer.apple.com/qa/snd/snd07.html
2199
+ ____ No Codec (____)
2200
+ _BIT BI_BITFIELDS (Raw RGB)
2201
+ _JPG JPEG compressed
2202
+ _PNG PNG compressed W3C/ISO/IEC (RFC-2083)
2203
+ _RAW Full Frames (Uncompressed)
2204
+ _RGB Raw RGB Bitmap
2205
+ _RL4 RLE 4bpp RGB
2206
+ _RL8 RLE 8bpp RGB
2207
+ 3IV1 3ivx MPEG-4 v1
2208
+ 3IV2 3ivx MPEG-4 v2
2209
+ 3IVX 3ivx MPEG-4
2210
+ AASC Autodesk Animator
2211
+ ABYR Kensington ?ABYR?
2212
+ AEMI Array Microsystems VideoONE MPEG1-I Capture
2213
+ AFLC Autodesk Animator FLC
2214
+ AFLI Autodesk Animator FLI
2215
+ AMPG Array Microsystems VideoONE MPEG
2216
+ ANIM Intel RDX (ANIM)
2217
+ AP41 AngelPotion Definitive
2218
+ ASV1 Asus Video v1
2219
+ ASV2 Asus Video v2
2220
+ ASVX Asus Video 2.0 (audio)
2221
+ AUR2 AuraVision Aura 2 Codec - YUV 4:2:2
2222
+ AURA AuraVision Aura 1 Codec - YUV 4:1:1
2223
+ AVDJ Independent JPEG Group\'s codec (AVDJ)
2224
+ AVRN Independent JPEG Group\'s codec (AVRN)
2225
+ AYUV 4:4:4 YUV (AYUV)
2226
+ AZPR Quicktime Apple Video (AZPR)
2227
+ BGR Raw RGB32
2228
+ BLZ0 Blizzard DivX MPEG-4
2229
+ BTVC Conexant Composite Video
2230
+ BINK RAD Game Tools Bink Video
2231
+ BT20 Conexant Prosumer Video
2232
+ BTCV Conexant Composite Video Codec
2233
+ BW10 Data Translation Broadway MPEG Capture
2234
+ CC12 Intel YUV12
2235
+ CDVC Canopus DV
2236
+ CFCC Digital Processing Systems DPS Perception
2237
+ CGDI Microsoft Office 97 Camcorder Video
2238
+ CHAM Winnov Caviara Champagne
2239
+ CJPG Creative WebCam JPEG
2240
+ CLJR Cirrus Logic YUV 4:1:1
2241
+ CMYK Common Data Format in Printing (Colorgraph)
2242
+ CPLA Weitek 4:2:0 YUV Planar
2243
+ CRAM Microsoft Video 1 (CRAM)
2244
+ cvid Radius Cinepak
2245
+ CVID Radius Cinepak
2246
+ CWLT Microsoft Color WLT DIB
2247
+ CYUV Creative Labs YUV
2248
+ CYUY ATI YUV
2249
+ D261 H.261
2250
+ D263 H.263
2251
+ DIB Device Independent Bitmap
2252
+ DIV1 FFmpeg OpenDivX
2253
+ DIV2 Microsoft MPEG-4 v1/v2
2254
+ DIV3 DivX ;-) MPEG-4 v3.x Low-Motion
2255
+ DIV4 DivX ;-) MPEG-4 v3.x Fast-Motion
2256
+ DIV5 DivX MPEG-4 v5.x
2257
+ DIV6 DivX ;-) (MS MPEG-4 v3.x)
2258
+ DIVX DivX MPEG-4 v4 (OpenDivX / Project Mayo)
2259
+ divx DivX MPEG-4
2260
+ DMB1 Matrox Rainbow Runner hardware MJPEG
2261
+ DMB2 Paradigm MJPEG
2262
+ DSVD ?DSVD?
2263
+ DUCK Duck TrueMotion 1.0
2264
+ DPS0 DPS/Leitch Reality Motion JPEG
2265
+ DPSC DPS/Leitch PAR Motion JPEG
2266
+ DV25 Matrox DVCPRO codec
2267
+ DV50 Matrox DVCPRO50 codec
2268
+ DVC IEC 61834 and SMPTE 314M (DVC/DV Video)
2269
+ DVCP IEC 61834 and SMPTE 314M (DVC/DV Video)
2270
+ DVHD IEC Standard DV 1125 lines @ 30fps / 1250 lines @ 25fps
2271
+ DVMA Darim Vision DVMPEG (dummy for MPEG compressor) (www.darvision.com)
2272
+ DVSL IEC Standard DV compressed in SD (SDL)
2273
+ DVAN ?DVAN?
2274
+ DVE2 InSoft DVE-2 Videoconferencing
2275
+ dvsd IEC 61834 and SMPTE 314M DVC/DV Video
2276
+ DVSD IEC 61834 and SMPTE 314M DVC/DV Video
2277
+ DVX1 Lucent DVX1000SP Video Decoder
2278
+ DVX2 Lucent DVX2000S Video Decoder
2279
+ DVX3 Lucent DVX3000S Video Decoder
2280
+ DX50 DivX v5
2281
+ DXT1 Microsoft DirectX Compressed Texture (DXT1)
2282
+ DXT2 Microsoft DirectX Compressed Texture (DXT2)
2283
+ DXT3 Microsoft DirectX Compressed Texture (DXT3)
2284
+ DXT4 Microsoft DirectX Compressed Texture (DXT4)
2285
+ DXT5 Microsoft DirectX Compressed Texture (DXT5)
2286
+ DXTC Microsoft DirectX Compressed Texture (DXTC)
2287
+ DXTn Microsoft DirectX Compressed Texture (DXTn)
2288
+ EM2V Etymonix MPEG-2 I-frame (www.etymonix.com)
2289
+ EKQ0 Elsa ?EKQ0?
2290
+ ELK0 Elsa ?ELK0?
2291
+ ESCP Eidos Escape
2292
+ ETV1 eTreppid Video ETV1
2293
+ ETV2 eTreppid Video ETV2
2294
+ ETVC eTreppid Video ETVC
2295
+ FLIC Autodesk FLI/FLC Animation
2296
+ FLV1 Sorenson Spark
2297
+ FLV4 On2 TrueMotion VP6
2298
+ FRWT Darim Vision Forward Motion JPEG (www.darvision.com)
2299
+ FRWU Darim Vision Forward Uncompressed (www.darvision.com)
2300
+ FLJP D-Vision Field Encoded Motion JPEG
2301
+ FPS1 FRAPS v1
2302
+ FRWA SoftLab-Nsk Forward Motion JPEG w/ alpha channel
2303
+ FRWD SoftLab-Nsk Forward Motion JPEG
2304
+ FVF1 Iterated Systems Fractal Video Frame
2305
+ GLZW Motion LZW (gabest@freemail.hu)
2306
+ GPEG Motion JPEG (gabest@freemail.hu)
2307
+ GWLT Microsoft Greyscale WLT DIB
2308
+ H260 Intel ITU H.260 Videoconferencing
2309
+ H261 Intel ITU H.261 Videoconferencing
2310
+ H262 Intel ITU H.262 Videoconferencing
2311
+ H263 Intel ITU H.263 Videoconferencing
2312
+ H264 Intel ITU H.264 Videoconferencing
2313
+ H265 Intel ITU H.265 Videoconferencing
2314
+ H266 Intel ITU H.266 Videoconferencing
2315
+ H267 Intel ITU H.267 Videoconferencing
2316
+ H268 Intel ITU H.268 Videoconferencing
2317
+ H269 Intel ITU H.269 Videoconferencing
2318
+ HFYU Huffman Lossless Codec
2319
+ HMCR Rendition Motion Compensation Format (HMCR)
2320
+ HMRR Rendition Motion Compensation Format (HMRR)
2321
+ I263 FFmpeg I263 decoder
2322
+ IF09 Indeo YVU9 ("YVU9 with additional delta-frame info after the U plane")
2323
+ IUYV Interlaced version of UYVY (www.leadtools.com)
2324
+ IY41 Interlaced version of Y41P (www.leadtools.com)
2325
+ IYU1 12 bit format used in mode 2 of the IEEE 1394 Digital Camera 1.04 spec IEEE standard
2326
+ IYU2 24 bit format used in mode 2 of the IEEE 1394 Digital Camera 1.04 spec IEEE standard
2327
+ IYUV Planar YUV format (8-bpp Y plane, followed by 8-bpp 2×2 U and V planes)
2328
+ i263 Intel ITU H.263 Videoconferencing (i263)
2329
+ I420 Intel Indeo 4
2330
+ IAN Intel Indeo 4 (RDX)
2331
+ ICLB InSoft CellB Videoconferencing
2332
+ IGOR Power DVD
2333
+ IJPG Intergraph JPEG
2334
+ ILVC Intel Layered Video
2335
+ ILVR ITU-T H.263+
2336
+ IPDV I-O Data Device Giga AVI DV Codec
2337
+ IR21 Intel Indeo 2.1
2338
+ IRAW Intel YUV Uncompressed
2339
+ IV30 Intel Indeo 3.0
2340
+ IV31 Intel Indeo 3.1
2341
+ IV32 Ligos Indeo 3.2
2342
+ IV33 Ligos Indeo 3.3
2343
+ IV34 Ligos Indeo 3.4
2344
+ IV35 Ligos Indeo 3.5
2345
+ IV36 Ligos Indeo 3.6
2346
+ IV37 Ligos Indeo 3.7
2347
+ IV38 Ligos Indeo 3.8
2348
+ IV39 Ligos Indeo 3.9
2349
+ IV40 Ligos Indeo Interactive 4.0
2350
+ IV41 Ligos Indeo Interactive 4.1
2351
+ IV42 Ligos Indeo Interactive 4.2
2352
+ IV43 Ligos Indeo Interactive 4.3
2353
+ IV44 Ligos Indeo Interactive 4.4
2354
+ IV45 Ligos Indeo Interactive 4.5
2355
+ IV46 Ligos Indeo Interactive 4.6
2356
+ IV47 Ligos Indeo Interactive 4.7
2357
+ IV48 Ligos Indeo Interactive 4.8
2358
+ IV49 Ligos Indeo Interactive 4.9
2359
+ IV50 Ligos Indeo Interactive 5.0
2360
+ JBYR Kensington ?JBYR?
2361
+ JPEG Still Image JPEG DIB
2362
+ JPGL Pegasus Lossless Motion JPEG
2363
+ KMVC Team17 Software Karl Morton\'s Video Codec
2364
+ LSVM Vianet Lighting Strike Vmail (Streaming) (www.vianet.com)
2365
+ LEAD LEAD Video Codec
2366
+ Ljpg LEAD MJPEG Codec
2367
+ MDVD Alex MicroDVD Video (hacked MS MPEG-4) (www.tiasoft.de)
2368
+ MJPA Morgan Motion JPEG (MJPA) (www.morgan-multimedia.com)
2369
+ MJPB Morgan Motion JPEG (MJPB) (www.morgan-multimedia.com)
2370
+ MMES Matrox MPEG-2 I-frame
2371
+ MP2v Microsoft S-Mpeg 4 version 1 (MP2v)
2372
+ MP42 Microsoft S-Mpeg 4 version 2 (MP42)
2373
+ MP43 Microsoft S-Mpeg 4 version 3 (MP43)
2374
+ MP4S Microsoft S-Mpeg 4 version 3 (MP4S)
2375
+ MP4V FFmpeg MPEG-4
2376
+ MPG1 FFmpeg MPEG 1/2
2377
+ MPG2 FFmpeg MPEG 1/2
2378
+ MPG3 FFmpeg DivX ;-) (MS MPEG-4 v3)
2379
+ MPG4 Microsoft MPEG-4
2380
+ MPGI Sigma Designs MPEG
2381
+ MPNG PNG images decoder
2382
+ MSS1 Microsoft Windows Screen Video
2383
+ MSZH LCL (Lossless Codec Library) (www.geocities.co.jp/Playtown-Denei/2837/LRC.htm)
2384
+ M261 Microsoft H.261
2385
+ M263 Microsoft H.263
2386
+ M4S2 Microsoft Fully Compliant MPEG-4 v2 simple profile (M4S2)
2387
+ m4s2 Microsoft Fully Compliant MPEG-4 v2 simple profile (m4s2)
2388
+ MC12 ATI Motion Compensation Format (MC12)
2389
+ MCAM ATI Motion Compensation Format (MCAM)
2390
+ MJ2C Morgan Multimedia Motion JPEG2000
2391
+ mJPG IBM Motion JPEG w/ Huffman Tables
2392
+ MJPG Microsoft Motion JPEG DIB
2393
+ MP42 Microsoft MPEG-4 (low-motion)
2394
+ MP43 Microsoft MPEG-4 (fast-motion)
2395
+ MP4S Microsoft MPEG-4 (MP4S)
2396
+ mp4s Microsoft MPEG-4 (mp4s)
2397
+ MPEG Chromatic Research MPEG-1 Video I-Frame
2398
+ MPG4 Microsoft MPEG-4 Video High Speed Compressor
2399
+ MPGI Sigma Designs MPEG
2400
+ MRCA FAST Multimedia Martin Regen Codec
2401
+ MRLE Microsoft Run Length Encoding
2402
+ MSVC Microsoft Video 1
2403
+ MTX1 Matrox ?MTX1?
2404
+ MTX2 Matrox ?MTX2?
2405
+ MTX3 Matrox ?MTX3?
2406
+ MTX4 Matrox ?MTX4?
2407
+ MTX5 Matrox ?MTX5?
2408
+ MTX6 Matrox ?MTX6?
2409
+ MTX7 Matrox ?MTX7?
2410
+ MTX8 Matrox ?MTX8?
2411
+ MTX9 Matrox ?MTX9?
2412
+ MV12 Motion Pixels Codec (old)
2413
+ MWV1 Aware Motion Wavelets
2414
+ nAVI SMR Codec (hack of Microsoft MPEG-4) (IRC #shadowrealm)
2415
+ NT00 NewTek LightWave HDTV YUV w/ Alpha (www.newtek.com)
2416
+ NUV1 NuppelVideo
2417
+ NTN1 Nogatech Video Compression 1
2418
+ NVS0 nVidia GeForce Texture (NVS0)
2419
+ NVS1 nVidia GeForce Texture (NVS1)
2420
+ NVS2 nVidia GeForce Texture (NVS2)
2421
+ NVS3 nVidia GeForce Texture (NVS3)
2422
+ NVS4 nVidia GeForce Texture (NVS4)
2423
+ NVS5 nVidia GeForce Texture (NVS5)
2424
+ NVT0 nVidia GeForce Texture (NVT0)
2425
+ NVT1 nVidia GeForce Texture (NVT1)
2426
+ NVT2 nVidia GeForce Texture (NVT2)
2427
+ NVT3 nVidia GeForce Texture (NVT3)
2428
+ NVT4 nVidia GeForce Texture (NVT4)
2429
+ NVT5 nVidia GeForce Texture (NVT5)
2430
+ PIXL MiroXL, Pinnacle PCTV
2431
+ PDVC I-O Data Device Digital Video Capture DV codec
2432
+ PGVV Radius Video Vision
2433
+ PHMO IBM Photomotion
2434
+ PIM1 MPEG Realtime (Pinnacle Cards)
2435
+ PIM2 Pegasus Imaging ?PIM2?
2436
+ PIMJ Pegasus Imaging Lossless JPEG
2437
+ PVEZ Horizons Technology PowerEZ
2438
+ PVMM PacketVideo Corporation MPEG-4
2439
+ PVW2 Pegasus Imaging Wavelet Compression
2440
+ Q1.0 Q-Team\'s QPEG 1.0 (www.q-team.de)
2441
+ Q1.1 Q-Team\'s QPEG 1.1 (www.q-team.de)
2442
+ QPEG Q-Team QPEG 1.0
2443
+ qpeq Q-Team QPEG 1.1
2444
+ RGB Raw BGR32
2445
+ RGBA Raw RGB w/ Alpha
2446
+ RMP4 REALmagic MPEG-4 (unauthorized XVID copy) (www.sigmadesigns.com)
2447
+ ROQV Id RoQ File Video Decoder
2448
+ RPZA Quicktime Apple Video (RPZA)
2449
+ RUD0 Rududu video codec (http://rududu.ifrance.com/rududu/)
2450
+ RV10 RealVideo 1.0 (aka RealVideo 5.0)
2451
+ RV13 RealVideo 1.0 (RV13)
2452
+ RV20 RealVideo G2
2453
+ RV30 RealVideo 8
2454
+ RV40 RealVideo 9
2455
+ RGBT Raw RGB w/ Transparency
2456
+ RLE Microsoft Run Length Encoder
2457
+ RLE4 Run Length Encoded (4bpp, 16-color)
2458
+ RLE8 Run Length Encoded (8bpp, 256-color)
2459
+ RT21 Intel Indeo RealTime Video 2.1
2460
+ rv20 RealVideo G2
2461
+ rv30 RealVideo 8
2462
+ RVX Intel RDX (RVX )
2463
+ SMC Apple Graphics (SMC )
2464
+ SP54 Logitech Sunplus Sp54 Codec for Mustek GSmart Mini 2
2465
+ SPIG Radius Spigot
2466
+ SVQ3 Sorenson Video 3 (Apple Quicktime 5)
2467
+ s422 Tekram VideoCap C210 YUV 4:2:2
2468
+ SDCC Sun Communication Digital Camera Codec
2469
+ SFMC CrystalNet Surface Fitting Method
2470
+ SMSC Radius SMSC
2471
+ SMSD Radius SMSD
2472
+ smsv WorldConnect Wavelet Video
2473
+ SPIG Radius Spigot
2474
+ SPLC Splash Studios ACM Audio Codec (www.splashstudios.net)
2475
+ SQZ2 Microsoft VXTreme Video Codec V2
2476
+ STVA ST Microelectronics CMOS Imager Data (Bayer)
2477
+ STVB ST Microelectronics CMOS Imager Data (Nudged Bayer)
2478
+ STVC ST Microelectronics CMOS Imager Data (Bunched)
2479
+ STVX ST Microelectronics CMOS Imager Data (Extended CODEC Data Format)
2480
+ STVY ST Microelectronics CMOS Imager Data (Extended CODEC Data Format with Correction Data)
2481
+ SV10 Sorenson Video R1
2482
+ SVQ1 Sorenson Video
2483
+ T420 Toshiba YUV 4:2:0
2484
+ TM2A Duck TrueMotion Archiver 2.0 (www.duck.com)
2485
+ TVJP Pinnacle/Truevision Targa 2000 board (TVJP)
2486
+ TVMJ Pinnacle/Truevision Targa 2000 board (TVMJ)
2487
+ TY0N Tecomac Low-Bit Rate Codec (www.tecomac.com)
2488
+ TY2C Trident Decompression Driver
2489
+ TLMS TeraLogic Motion Intraframe Codec (TLMS)
2490
+ TLST TeraLogic Motion Intraframe Codec (TLST)
2491
+ TM20 Duck TrueMotion 2.0
2492
+ TM2X Duck TrueMotion 2X
2493
+ TMIC TeraLogic Motion Intraframe Codec (TMIC)
2494
+ TMOT Horizons Technology TrueMotion S
2495
+ tmot Horizons TrueMotion Video Compression
2496
+ TR20 Duck TrueMotion RealTime 2.0
2497
+ TSCC TechSmith Screen Capture Codec
2498
+ TV10 Tecomac Low-Bit Rate Codec
2499
+ TY2N Trident ?TY2N?
2500
+ U263 UB Video H.263/H.263+/H.263++ Decoder
2501
+ UMP4 UB Video MPEG 4 (www.ubvideo.com)
2502
+ UYNV Nvidia UYVY packed 4:2:2
2503
+ UYVP Evans & Sutherland YCbCr 4:2:2 extended precision
2504
+ UCOD eMajix.com ClearVideo
2505
+ ULTI IBM Ultimotion
2506
+ UYVY UYVY packed 4:2:2
2507
+ V261 Lucent VX2000S
2508
+ VIFP VFAPI Reader Codec (www.yks.ne.jp/~hori/)
2509
+ VIV1 FFmpeg H263+ decoder
2510
+ VIV2 Vivo H.263
2511
+ VQC2 Vector-quantised codec 2 (research) http://eprints.ecs.soton.ac.uk/archive/00001310/01/VTC97-js.pdf)
2512
+ VTLP Alaris VideoGramPiX
2513
+ VYU9 ATI YUV (VYU9)
2514
+ VYUY ATI YUV (VYUY)
2515
+ V261 Lucent VX2000S
2516
+ V422 Vitec Multimedia 24-bit YUV 4:2:2 Format
2517
+ V655 Vitec Multimedia 16-bit YUV 4:2:2 Format
2518
+ VCR1 ATI Video Codec 1
2519
+ VCR2 ATI Video Codec 2
2520
+ VCR3 ATI VCR 3.0
2521
+ VCR4 ATI VCR 4.0
2522
+ VCR5 ATI VCR 5.0
2523
+ VCR6 ATI VCR 6.0
2524
+ VCR7 ATI VCR 7.0
2525
+ VCR8 ATI VCR 8.0
2526
+ VCR9 ATI VCR 9.0
2527
+ VDCT Vitec Multimedia Video Maker Pro DIB
2528
+ VDOM VDOnet VDOWave
2529
+ VDOW VDOnet VDOLive (H.263)
2530
+ VDTZ Darim Vison VideoTizer YUV
2531
+ VGPX Alaris VideoGramPiX
2532
+ VIDS Vitec Multimedia YUV 4:2:2 CCIR 601 for V422
2533
+ VIVO Vivo H.263 v2.00
2534
+ vivo Vivo H.263
2535
+ VIXL Miro/Pinnacle Video XL
2536
+ VLV1 VideoLogic/PURE Digital Videologic Capture
2537
+ VP30 On2 VP3.0
2538
+ VP31 On2 VP3.1
2539
+ VP6F On2 TrueMotion VP6
2540
+ VX1K Lucent VX1000S Video Codec
2541
+ VX2K Lucent VX2000S Video Codec
2542
+ VXSP Lucent VX1000SP Video Codec
2543
+ WBVC Winbond W9960
2544
+ WHAM Microsoft Video 1 (WHAM)
2545
+ WINX Winnov Software Compression
2546
+ WJPG AverMedia Winbond JPEG
2547
+ WMV1 Windows Media Video V7
2548
+ WMV2 Windows Media Video V8
2549
+ WMV3 Windows Media Video V9
2550
+ WNV1 Winnov Hardware Compression
2551
+ XYZP Extended PAL format XYZ palette (www.riff.org)
2552
+ x263 Xirlink H.263
2553
+ XLV0 NetXL Video Decoder
2554
+ XMPG Xing MPEG (I-Frame only)
2555
+ XVID XviD MPEG-4 (www.xvid.org)
2556
+ XXAN ?XXAN?
2557
+ YU92 Intel YUV (YU92)
2558
+ YUNV Nvidia Uncompressed YUV 4:2:2
2559
+ YUVP Extended PAL format YUV palette (www.riff.org)
2560
+ Y211 YUV 2:1:1 Packed
2561
+ Y411 YUV 4:1:1 Packed
2562
+ Y41B Weitek YUV 4:1:1 Planar
2563
+ Y41P Brooktree PC1 YUV 4:1:1 Packed
2564
+ Y41T Brooktree PC1 YUV 4:1:1 with transparency
2565
+ Y42B Weitek YUV 4:2:2 Planar
2566
+ Y42T Brooktree UYUV 4:2:2 with transparency
2567
+ Y422 ADS Technologies Copy of UYVY used in Pyro WebCam firewire camera
2568
+ Y800 Simple, single Y plane for monochrome images
2569
+ Y8 Grayscale video
2570
+ YC12 Intel YUV 12 codec
2571
+ YUV8 Winnov Caviar YUV8
2572
+ YUV9 Intel YUV9
2573
+ YUY2 Uncompressed YUV 4:2:2
2574
+ YUYV Canopus YUV
2575
+ YV12 YVU12 Planar
2576
+ YVU9 Intel YVU9 Planar (8-bpp Y plane, followed by 8-bpp 4x4 U and V planes)
2577
+ YVYU YVYU 4:2:2 Packed
2578
+ ZLIB Lossless Codec Library zlib compression (www.geocities.co.jp/Playtown-Denei/2837/LRC.htm)
2579
+ ZPEG Metheus Video Zipper
2580
+
2581
+ */
2582
+
2583
+ return getid3_lib::EmbeddedLookup($fourcc, $begin, __LINE__, __FILE__, 'riff-fourcc');
2584
+ }
2585
+
2586
+ private function EitherEndian2Int($byteword, $signed=false) {
2587
+ if ($this->container == 'riff') {
2588
+ return getid3_lib::LittleEndian2Int($byteword, $signed);
2589
+ }
2590
+ return getid3_lib::BigEndian2Int($byteword, false, $signed);
2591
+ }
2592
+
2593
  }
getid3/module.audio.aac.php CHANGED
@@ -1,513 +1,513 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- // also https://github.com/JamesHeinrich/getID3 //
7
- /////////////////////////////////////////////////////////////////
8
- // See readme.txt for more details //
9
- /////////////////////////////////////////////////////////////////
10
- // //
11
- // module.audio.aac.php //
12
- // module for analyzing AAC Audio files //
13
- // dependencies: NONE //
14
- // ///
15
- /////////////////////////////////////////////////////////////////
16
-
17
-
18
- class getid3_aac extends getid3_handler
19
- {
20
- public function Analyze() {
21
- $info = &$this->getid3->info;
22
- $this->fseek($info['avdataoffset']);
23
- if ($this->fread(4) == 'ADIF') {
24
- $this->getAACADIFheaderFilepointer();
25
- } else {
26
- $this->getAACADTSheaderFilepointer();
27
- }
28
- return true;
29
- }
30
-
31
-
32
-
33
- public function getAACADIFheaderFilepointer() {
34
- $info = &$this->getid3->info;
35
- $info['fileformat'] = 'aac';
36
- $info['audio']['dataformat'] = 'aac';
37
- $info['audio']['lossless'] = false;
38
-
39
- $this->fseek($info['avdataoffset']);
40
- $AACheader = $this->fread(1024);
41
- $offset = 0;
42
-
43
- if (substr($AACheader, 0, 4) == 'ADIF') {
44
-
45
- // http://faac.sourceforge.net/wiki/index.php?page=ADIF
46
-
47
- // http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
48
- // adif_header() {
49
- // adif_id 32
50
- // copyright_id_present 1
51
- // if( copyright_id_present )
52
- // copyright_id 72
53
- // original_copy 1
54
- // home 1
55
- // bitstream_type 1
56
- // bitrate 23
57
- // num_program_config_elements 4
58
- // for (i = 0; i < num_program_config_elements + 1; i++ ) {
59
- // if( bitstream_type == '0' )
60
- // adif_buffer_fullness 20
61
- // program_config_element()
62
- // }
63
- // }
64
-
65
- $AACheaderBitstream = getid3_lib::BigEndian2Bin($AACheader);
66
- $bitoffset = 0;
67
-
68
- $info['aac']['header_type'] = 'ADIF';
69
- $bitoffset += 32;
70
- $info['aac']['header']['mpeg_version'] = 4;
71
-
72
- $info['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
73
- $bitoffset += 1;
74
- if ($info['aac']['header']['copyright']) {
75
- $info['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72));
76
- $bitoffset += 72;
77
- }
78
- $info['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
79
- $bitoffset += 1;
80
- $info['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
81
- $bitoffset += 1;
82
- $info['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
83
- $bitoffset += 1;
84
- if ($info['aac']['header']['is_vbr']) {
85
- $info['audio']['bitrate_mode'] = 'vbr';
86
- $info['aac']['header']['bitrate_max'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
87
- $bitoffset += 23;
88
- } else {
89
- $info['audio']['bitrate_mode'] = 'cbr';
90
- $info['aac']['header']['bitrate'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
91
- $bitoffset += 23;
92
- $info['audio']['bitrate'] = $info['aac']['header']['bitrate'];
93
- }
94
- if ($info['audio']['bitrate'] == 0) {
95
- $info['error'][] = 'Corrupt AAC file: bitrate_audio == zero';
96
- return false;
97
- }
98
- $info['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
99
- $bitoffset += 4;
100
-
101
- for ($i = 0; $i < $info['aac']['header']['num_program_configs']; $i++) {
102
- // http://www.audiocoding.com/wiki/index.php?page=program_config_element
103
-
104
- // buffer_fullness 20
105
-
106
- // element_instance_tag 4
107
- // object_type 2
108
- // sampling_frequency_index 4
109
- // num_front_channel_elements 4
110
- // num_side_channel_elements 4
111
- // num_back_channel_elements 4
112
- // num_lfe_channel_elements 2
113
- // num_assoc_data_elements 3
114
- // num_valid_cc_elements 4
115
- // mono_mixdown_present 1
116
- // mono_mixdown_element_number 4 if mono_mixdown_present == 1
117
- // stereo_mixdown_present 1
118
- // stereo_mixdown_element_number 4 if stereo_mixdown_present == 1
119
- // matrix_mixdown_idx_present 1
120
- // matrix_mixdown_idx 2 if matrix_mixdown_idx_present == 1
121
- // pseudo_surround_enable 1 if matrix_mixdown_idx_present == 1
122
- // for (i = 0; i < num_front_channel_elements; i++) {
123
- // front_element_is_cpe[i] 1
124
- // front_element_tag_select[i] 4
125
- // }
126
- // for (i = 0; i < num_side_channel_elements; i++) {
127
- // side_element_is_cpe[i] 1
128
- // side_element_tag_select[i] 4
129
- // }
130
- // for (i = 0; i < num_back_channel_elements; i++) {
131
- // back_element_is_cpe[i] 1
132
- // back_element_tag_select[i] 4
133
- // }
134
- // for (i = 0; i < num_lfe_channel_elements; i++) {
135
- // lfe_element_tag_select[i] 4
136
- // }
137
- // for (i = 0; i < num_assoc_data_elements; i++) {
138
- // assoc_data_element_tag_select[i] 4
139
- // }
140
- // for (i = 0; i < num_valid_cc_elements; i++) {
141
- // cc_element_is_ind_sw[i] 1
142
- // valid_cc_element_tag_select[i] 4
143
- // }
144
- // byte_alignment() VAR
145
- // comment_field_bytes 8
146
- // for (i = 0; i < comment_field_bytes; i++) {
147
- // comment_field_data[i] 8
148
- // }
149
-
150
- if (!$info['aac']['header']['is_vbr']) {
151
- $info['aac']['program_configs'][$i]['buffer_fullness'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20));
152
- $bitoffset += 20;
153
- }
154
- $info['aac']['program_configs'][$i]['element_instance_tag'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
155
- $bitoffset += 4;
156
- $info['aac']['program_configs'][$i]['object_type'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
157
- $bitoffset += 2;
158
- $info['aac']['program_configs'][$i]['sampling_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
159
- $bitoffset += 4;
160
- $info['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
161
- $bitoffset += 4;
162
- $info['aac']['program_configs'][$i]['num_side_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
163
- $bitoffset += 4;
164
- $info['aac']['program_configs'][$i]['num_back_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
165
- $bitoffset += 4;
166
- $info['aac']['program_configs'][$i]['num_lfe_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
167
- $bitoffset += 2;
168
- $info['aac']['program_configs'][$i]['num_assoc_data_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3));
169
- $bitoffset += 3;
170
- $info['aac']['program_configs'][$i]['num_valid_cc_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
171
- $bitoffset += 4;
172
- $info['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
173
- $bitoffset += 1;
174
- if ($info['aac']['program_configs'][$i]['mono_mixdown_present']) {
175
- $info['aac']['program_configs'][$i]['mono_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
176
- $bitoffset += 4;
177
- }
178
- $info['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
179
- $bitoffset += 1;
180
- if ($info['aac']['program_configs'][$i]['stereo_mixdown_present']) {
181
- $info['aac']['program_configs'][$i]['stereo_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
182
- $bitoffset += 4;
183
- }
184
- $info['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
185
- $bitoffset += 1;
186
- if ($info['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) {
187
- $info['aac']['program_configs'][$i]['matrix_mixdown_idx'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
188
- $bitoffset += 2;
189
- $info['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
190
- $bitoffset += 1;
191
- }
192
- for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) {
193
- $info['aac']['program_configs'][$i]['front_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
194
- $bitoffset += 1;
195
- $info['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
196
- $bitoffset += 4;
197
- }
198
- for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) {
199
- $info['aac']['program_configs'][$i]['side_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
200
- $bitoffset += 1;
201
- $info['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
202
- $bitoffset += 4;
203
- }
204
- for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) {
205
- $info['aac']['program_configs'][$i]['back_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
206
- $bitoffset += 1;
207
- $info['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
208
- $bitoffset += 4;
209
- }
210
- for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) {
211
- $info['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
212
- $bitoffset += 4;
213
- }
214
- for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) {
215
- $info['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
216
- $bitoffset += 4;
217
- }
218
- for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) {
219
- $info['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
220
- $bitoffset += 1;
221
- $info['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
222
- $bitoffset += 4;
223
- }
224
-
225
- $bitoffset = ceil($bitoffset / 8) * 8;
226
-
227
- $info['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8));
228
- $bitoffset += 8;
229
- $info['aac']['program_configs'][$i]['comment_field'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $info['aac']['program_configs'][$i]['comment_field_bytes']));
230
- $bitoffset += 8 * $info['aac']['program_configs'][$i]['comment_field_bytes'];
231
-
232
-
233
- $info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['program_configs'][$i]['object_type'], $info['aac']['header']['mpeg_version']);
234
- $info['aac']['program_configs'][$i]['sampling_frequency'] = self::AACsampleRateLookup($info['aac']['program_configs'][$i]['sampling_frequency_index']);
235
- $info['audio']['sample_rate'] = $info['aac']['program_configs'][$i]['sampling_frequency'];
236
- $info['audio']['channels'] = self::AACchannelCountCalculate($info['aac']['program_configs'][$i]);
237
- if ($info['aac']['program_configs'][$i]['comment_field']) {
238
- $info['aac']['comments'][] = $info['aac']['program_configs'][$i]['comment_field'];
239
- }
240
- }
241
- $info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate'];
242
-
243
- $info['audio']['encoder_options'] = $info['aac']['header_type'].' '.$info['aac']['header']['profile'];
244
-
245
-
246
-
247
- return true;
248
-
249
- } else {
250
-
251
- unset($info['fileformat']);
252
- unset($info['aac']);
253
- $info['error'][] = 'AAC-ADIF synch not found at offset '.$info['avdataoffset'].' (expected "ADIF", found "'.substr($AACheader, 0, 4).'" instead)';
254
- return false;
255
-
256
- }
257
-
258
- }
259
-
260
-
261
- public function getAACADTSheaderFilepointer($MaxFramesToScan=1000000, $ReturnExtendedInfo=false) {
262
- $info = &$this->getid3->info;
263
-
264
- // based loosely on code from AACfile by Jurgen Faul <jfaulØgmx.de>
265
- // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html
266
-
267
-
268
- // http://faac.sourceforge.net/wiki/index.php?page=ADTS // dead link
269
- // http://wiki.multimedia.cx/index.php?title=ADTS
270
-
271
- // * ADTS Fixed Header: these don't change from frame to frame
272
- // syncword 12 always: '111111111111'
273
- // ID 1 0: MPEG-4, 1: MPEG-2
274
- // MPEG layer 2 If you send AAC in MPEG-TS, set to 0
275
- // protection_absent 1 0: CRC present; 1: no CRC
276
- // profile 2 0: AAC Main; 1: AAC LC (Low Complexity); 2: AAC SSR (Scalable Sample Rate); 3: AAC LTP (Long Term Prediction)
277
- // sampling_frequency_index 4 15 not allowed
278
- // private_bit 1 usually 0
279
- // channel_configuration 3
280
- // original/copy 1 0: original; 1: copy
281
- // home 1 usually 0
282
- // emphasis 2 only if ID == 0 (ie MPEG-4) // not present in some documentation?
283
-
284
- // * ADTS Variable Header: these can change from frame to frame
285
- // copyright_identification_bit 1
286
- // copyright_identification_start 1
287
- // aac_frame_length 13 length of the frame including header (in bytes)
288
- // adts_buffer_fullness 11 0x7FF indicates VBR
289
- // no_raw_data_blocks_in_frame 2
290
-
291
- // * ADTS Error check
292
- // crc_check 16 only if protection_absent == 0
293
-
294
- $byteoffset = $info['avdataoffset'];
295
- $framenumber = 0;
296
-
297
- // Init bit pattern array
298
- static $decbin = array();
299
-
300
- // Populate $bindec
301
- for ($i = 0; $i < 256; $i++) {
302
- $decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT);
303
- }
304
-
305
- // used to calculate bitrate below
306
- $BitrateCache = array();
307
-
308
-
309
- while (true) {
310
- // breaks out when end-of-file encountered, or invalid data found,
311
- // or MaxFramesToScan frames have been scanned
312
-
313
- if (!getid3_lib::intValueSupported($byteoffset)) {
314
- $info['warning'][] = 'Unable to parse AAC file beyond '.$this->ftell().' (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)';
315
- return false;
316
- }
317
- $this->fseek($byteoffset);
318
-
319
- // First get substring
320
- $substring = $this->fread(9); // header is 7 bytes (or 9 if CRC is present)
321
- $substringlength = strlen($substring);
322
- if ($substringlength != 9) {
323
- $info['error'][] = 'Failed to read 7 bytes at offset '.($this->ftell() - $substringlength).' (only read '.$substringlength.' bytes)';
324
- return false;
325
- }
326
- // this would be easier with 64-bit math, but split it up to allow for 32-bit:
327
- $header1 = getid3_lib::BigEndian2Int(substr($substring, 0, 2));
328
- $header2 = getid3_lib::BigEndian2Int(substr($substring, 2, 4));
329
- $header3 = getid3_lib::BigEndian2Int(substr($substring, 6, 1));
330
-
331
- $info['aac']['header']['raw']['syncword'] = ($header1 & 0xFFF0) >> 4;
332
- if ($info['aac']['header']['raw']['syncword'] != 0x0FFF) {
333
- $info['error'][] = 'Synch pattern (0x0FFF) not found at offset '.($this->ftell() - $substringlength).' (found 0x0'.strtoupper(dechex($info['aac']['header']['raw']['syncword'])).' instead)';
334
- //if ($info['fileformat'] == 'aac') {
335
- // return true;
336
- //}
337
- unset($info['aac']);
338
- return false;
339
- }
340
-
341
- // Gather info for first frame only - this takes time to do 1000 times!
342
- if ($framenumber == 0) {
343
- $info['aac']['header_type'] = 'ADTS';
344
- $info['fileformat'] = 'aac';
345
- $info['audio']['dataformat'] = 'aac';
346
-
347
- $info['aac']['header']['raw']['mpeg_version'] = ($header1 & 0x0008) >> 3;
348
- $info['aac']['header']['raw']['mpeg_layer'] = ($header1 & 0x0006) >> 1;
349
- $info['aac']['header']['raw']['protection_absent'] = ($header1 & 0x0001) >> 0;
350
-
351
- $info['aac']['header']['raw']['profile_code'] = ($header2 & 0xC0000000) >> 30;
352
- $info['aac']['header']['raw']['sample_rate_code'] = ($header2 & 0x3C000000) >> 26;
353
- $info['aac']['header']['raw']['private_stream'] = ($header2 & 0x02000000) >> 25;
354
- $info['aac']['header']['raw']['channels_code'] = ($header2 & 0x01C00000) >> 22;
355
- $info['aac']['header']['raw']['original'] = ($header2 & 0x00200000) >> 21;
356
- $info['aac']['header']['raw']['home'] = ($header2 & 0x00100000) >> 20;
357
- $info['aac']['header']['raw']['copyright_stream'] = ($header2 & 0x00080000) >> 19;
358
- $info['aac']['header']['raw']['copyright_start'] = ($header2 & 0x00040000) >> 18;
359
- $info['aac']['header']['raw']['frame_length'] = ($header2 & 0x0003FFE0) >> 5;
360
-
361
- $info['aac']['header']['mpeg_version'] = ($info['aac']['header']['raw']['mpeg_version'] ? 2 : 4);
362
- $info['aac']['header']['crc_present'] = ($info['aac']['header']['raw']['protection_absent'] ? false: true);
363
- $info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['header']['raw']['profile_code'], $info['aac']['header']['mpeg_version']);
364
- $info['aac']['header']['sample_frequency'] = self::AACsampleRateLookup($info['aac']['header']['raw']['sample_rate_code']);
365
- $info['aac']['header']['private'] = (bool) $info['aac']['header']['raw']['private_stream'];
366
- $info['aac']['header']['original'] = (bool) $info['aac']['header']['raw']['original'];
367
- $info['aac']['header']['home'] = (bool) $info['aac']['header']['raw']['home'];
368
- $info['aac']['header']['channels'] = (($info['aac']['header']['raw']['channels_code'] == 7) ? 8 : $info['aac']['header']['raw']['channels_code']);
369
- if ($ReturnExtendedInfo) {
370
- $info['aac'][$framenumber]['copyright_id_bit'] = (bool) $info['aac']['header']['raw']['copyright_stream'];
371
- $info['aac'][$framenumber]['copyright_id_start'] = (bool) $info['aac']['header']['raw']['copyright_start'];
372
- }
373
-
374
- if ($info['aac']['header']['raw']['mpeg_layer'] != 0) {
375
- $info['warning'][] = 'Layer error - expected "0", found "'.$info['aac']['header']['raw']['mpeg_layer'].'" instead';
376
- }
377
- if ($info['aac']['header']['sample_frequency'] == 0) {
378
- $info['error'][] = 'Corrupt AAC file: sample_frequency == zero';
379
- return false;
380
- }
381
-
382
- $info['audio']['sample_rate'] = $info['aac']['header']['sample_frequency'];
383
- $info['audio']['channels'] = $info['aac']['header']['channels'];
384
- }
385
-
386
- $FrameLength = ($header2 & 0x0003FFE0) >> 5;
387
-
388
- if (!isset($BitrateCache[$FrameLength])) {
389
- $BitrateCache[$FrameLength] = ($info['aac']['header']['sample_frequency'] / 1024) * $FrameLength * 8;
390
- }
391
- getid3_lib::safe_inc($info['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1);
392
-
393
- $info['aac'][$framenumber]['aac_frame_length'] = $FrameLength;
394
-
395
- $info['aac'][$framenumber]['adts_buffer_fullness'] = (($header2 & 0x0000001F) << 6) & (($header3 & 0xFC) >> 2);
396
- if ($info['aac'][$framenumber]['adts_buffer_fullness'] == 0x07FF) {
397
- $info['audio']['bitrate_mode'] = 'vbr';
398
- } else {
399
- $info['audio']['bitrate_mode'] = 'cbr';
400
- }
401
- $info['aac'][$framenumber]['num_raw_data_blocks'] = (($header3 & 0x03) >> 0);
402
-
403
- if ($info['aac']['header']['crc_present']) {
404
- //$info['aac'][$framenumber]['crc'] = getid3_lib::BigEndian2Int(substr($substring, 7, 2);
405
- }
406
-
407
- if (!$ReturnExtendedInfo) {
408
- unset($info['aac'][$framenumber]);
409
- }
410
-
411
- /*
412
- $rounded_precision = 5000;
413
- $info['aac']['bitrate_distribution_rounded'] = array();
414
- foreach ($info['aac']['bitrate_distribution'] as $bitrate => $count) {
415
- $rounded_bitrate = round($bitrate / $rounded_precision) * $rounded_precision;
416
- getid3_lib::safe_inc($info['aac']['bitrate_distribution_rounded'][$rounded_bitrate], $count);
417
- }
418
- ksort($info['aac']['bitrate_distribution_rounded']);
419
- */
420
-
421
- $byteoffset += $FrameLength;
422
- if ((++$framenumber < $MaxFramesToScan) && (($byteoffset + 10) < $info['avdataend'])) {
423
-
424
- // keep scanning
425
-
426
- } else {
427
-
428
- $info['aac']['frames'] = $framenumber;
429
- $info['playtime_seconds'] = ($info['avdataend'] / $byteoffset) * (($framenumber * 1024) / $info['aac']['header']['sample_frequency']); // (1 / % of file scanned) * (samples / (samples/sec)) = seconds
430
- if ($info['playtime_seconds'] == 0) {
431
- $info['error'][] = 'Corrupt AAC file: playtime_seconds == zero';
432
- return false;
433
- }
434
- $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
435
- ksort($info['aac']['bitrate_distribution']);
436
-
437
- $info['audio']['encoder_options'] = $info['aac']['header_type'].' '.$info['aac']['header']['profile'];
438
-
439
- return true;
440
-
441
- }
442
- }
443
- // should never get here.
444
- }
445
-
446
- public static function AACsampleRateLookup($samplerateid) {
447
- static $AACsampleRateLookup = array();
448
- if (empty($AACsampleRateLookup)) {
449
- $AACsampleRateLookup[0] = 96000;
450
- $AACsampleRateLookup[1] = 88200;
451
- $AACsampleRateLookup[2] = 64000;
452
- $AACsampleRateLookup[3] = 48000;
453
- $AACsampleRateLookup[4] = 44100;
454
- $AACsampleRateLookup[5] = 32000;
455
- $AACsampleRateLookup[6] = 24000;
456
- $AACsampleRateLookup[7] = 22050;
457
- $AACsampleRateLookup[8] = 16000;
458
- $AACsampleRateLookup[9] = 12000;
459
- $AACsampleRateLookup[10] = 11025;
460
- $AACsampleRateLookup[11] = 8000;
461
- $AACsampleRateLookup[12] = 0;
462
- $AACsampleRateLookup[13] = 0;
463
- $AACsampleRateLookup[14] = 0;
464
- $AACsampleRateLookup[15] = 0;
465
- }
466
- return (isset($AACsampleRateLookup[$samplerateid]) ? $AACsampleRateLookup[$samplerateid] : 'invalid');
467
- }
468
-
469
- public static function AACprofileLookup($profileid, $mpegversion) {
470
- static $AACprofileLookup = array();
471
- if (empty($AACprofileLookup)) {
472
- $AACprofileLookup[2][0] = 'Main profile';
473
- $AACprofileLookup[2][1] = 'Low Complexity profile (LC)';
474
- $AACprofileLookup[2][2] = 'Scalable Sample Rate profile (SSR)';
475
- $AACprofileLookup[2][3] = '(reserved)';
476
- $AACprofileLookup[4][0] = 'AAC_MAIN';
477
- $AACprofileLookup[4][1] = 'AAC_LC';
478
- $AACprofileLookup[4][2] = 'AAC_SSR';
479
- $AACprofileLookup[4][3] = 'AAC_LTP';
480
- }
481
- return (isset($AACprofileLookup[$mpegversion][$profileid]) ? $AACprofileLookup[$mpegversion][$profileid] : 'invalid');
482
- }
483
-
484
- public static function AACchannelCountCalculate($program_configs) {
485
- $channels = 0;
486
- for ($i = 0; $i < $program_configs['num_front_channel_elements']; $i++) {
487
- $channels++;
488
- if ($program_configs['front_element_is_cpe'][$i]) {
489
- // each front element is channel pair (CPE = Channel Pair Element)
490
- $channels++;
491
- }
492
- }
493
- for ($i = 0; $i < $program_configs['num_side_channel_elements']; $i++) {
494
- $channels++;
495
- if ($program_configs['side_element_is_cpe'][$i]) {
496
- // each side element is channel pair (CPE = Channel Pair Element)
497
- $channels++;
498
- }
499
- }
500
- for ($i = 0; $i < $program_configs['num_back_channel_elements']; $i++) {
501
- $channels++;
502
- if ($program_configs['back_element_is_cpe'][$i]) {
503
- // each back element is channel pair (CPE = Channel Pair Element)
504
- $channels++;
505
- }
506
- }
507
- for ($i = 0; $i < $program_configs['num_lfe_channel_elements']; $i++) {
508
- $channels++;
509
- }
510
- return $channels;
511
- }
512
-
513
- }
1
+ <?php
2
+ /////////////////////////////////////////////////////////////////
3
+ /// getID3() by James Heinrich <info@getid3.org> //
4
+ // available at http://getid3.sourceforge.net //
5
+ // or http://www.getid3.org //
6
+ // also https://github.com/JamesHeinrich/getID3 //
7
+ /////////////////////////////////////////////////////////////////
8
+ // See readme.txt for more details //
9
+ /////////////////////////////////////////////////////////////////
10
+ // //
11
+ // module.audio.aac.php //
12
+ // module for analyzing AAC Audio files //
13
+ // dependencies: NONE //
14
+ // ///
15
+ /////////////////////////////////////////////////////////////////
16
+
17
+
18
+ class getid3_aac extends getid3_handler
19
+ {
20
+ public function Analyze() {
21
+ $info = &$this->getid3->info;
22
+ $this->fseek($info['avdataoffset']);
23
+ if ($this->fread(4) == 'ADIF') {
24
+ $this->getAACADIFheaderFilepointer();
25
+ } else {
26
+ $this->getAACADTSheaderFilepointer();
27
+ }
28
+ return true;
29
+ }
30
+
31
+
32
+
33
+ public function getAACADIFheaderFilepointer() {
34
+ $info = &$this->getid3->info;
35
+ $info['fileformat'] = 'aac';
36
+ $info['audio']['dataformat'] = 'aac';
37
+ $info['audio']['lossless'] = false;
38
+
39
+ $this->fseek($info['avdataoffset']);
40
+ $AACheader = $this->fread(1024);
41
+ $offset = 0;
42
+
43
+ if (substr($AACheader, 0, 4) == 'ADIF') {
44
+
45
+ // http://faac.sourceforge.net/wiki/index.php?page=ADIF
46
+
47
+ // http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
48
+ // adif_header() {
49
+ // adif_id 32
50
+ // copyright_id_present 1
51
+ // if( copyright_id_present )
52
+ // copyright_id 72
53
+ // original_copy 1
54
+ // home 1
55
+ // bitstream_type 1
56
+ // bitrate 23
57
+ // num_program_config_elements 4
58
+ // for (i = 0; i < num_program_config_elements + 1; i++ ) {
59
+ // if( bitstream_type == '0' )
60
+ // adif_buffer_fullness 20
61
+ // program_config_element()
62
+ // }
63
+ // }
64
+
65
+ $AACheaderBitstream = getid3_lib::BigEndian2Bin($AACheader);
66
+ $bitoffset = 0;
67
+
68
+ $info['aac']['header_type'] = 'ADIF';
69
+ $bitoffset += 32;
70
+ $info['aac']['header']['mpeg_version'] = 4;
71
+
72
+ $info['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
73
+ $bitoffset += 1;
74
+ if ($info['aac']['header']['copyright']) {
75
+ $info['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72));
76
+ $bitoffset += 72;
77
+ }
78
+ $info['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
79
+ $bitoffset += 1;
80
+ $info['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
81
+ $bitoffset += 1;
82
+ $info['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
83
+ $bitoffset += 1;
84
+ if ($info['aac']['header']['is_vbr']) {
85
+ $info['audio']['bitrate_mode'] = 'vbr';
86
+ $info['aac']['header']['bitrate_max'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
87
+ $bitoffset += 23;
88
+ } else {
89
+ $info['audio']['bitrate_mode'] = 'cbr';
90
+ $info['aac']['header']['bitrate'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
91
+ $bitoffset += 23;
92
+ $info['audio']['bitrate'] = $info['aac']['header']['bitrate'];
93
+ }
94
+ if ($info['audio']['bitrate'] == 0) {
95
+ $info['error'][] = 'Corrupt AAC file: bitrate_audio == zero';
96
+ return false;
97
+ }
98
+ $info['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
99
+ $bitoffset += 4;
100
+
101
+ for ($i = 0; $i < $info['aac']['header']['num_program_configs']; $i++) {
102
+ // http://www.audiocoding.com/wiki/index.php?page=program_config_element
103
+
104
+ // buffer_fullness 20
105
+
106
+ // element_instance_tag 4
107
+ // object_type 2
108
+ // sampling_frequency_index 4
109
+ // num_front_channel_elements 4
110
+ // num_side_channel_elements 4
111
+ // num_back_channel_elements 4
112
+ // num_lfe_channel_elements 2
113
+ // num_assoc_data_elements 3
114
+ // num_valid_cc_elements 4
115
+ // mono_mixdown_present 1
116
+ // mono_mixdown_element_number 4 if mono_mixdown_present == 1
117
+ // stereo_mixdown_present 1
118
+ // stereo_mixdown_element_number 4 if stereo_mixdown_present == 1
119
+ // matrix_mixdown_idx_present 1
120
+ // matrix_mixdown_idx 2 if matrix_mixdown_idx_present == 1
121
+ // pseudo_surround_enable 1 if matrix_mixdown_idx_present == 1
122
+ // for (i = 0; i < num_front_channel_elements; i++) {
123
+ // front_element_is_cpe[i] 1
124
+ // front_element_tag_select[i] 4
125
+ // }
126
+ // for (i = 0; i < num_side_channel_elements; i++) {
127
+ // side_element_is_cpe[i] 1
128
+ // side_element_tag_select[i] 4
129
+ // }
130
+ // for (i = 0; i < num_back_channel_elements; i++) {
131
+ // back_element_is_cpe[i] 1
132
+ // back_element_tag_select[i] 4
133
+ // }
134
+ // for (i = 0; i < num_lfe_channel_elements; i++) {
135
+ // lfe_element_tag_select[i] 4
136
+ // }
137
+ // for (i = 0; i < num_assoc_data_elements; i++) {
138
+ // assoc_data_element_tag_select[i] 4
139
+ // }
140
+ // for (i = 0; i < num_valid_cc_elements; i++) {
141
+ // cc_element_is_ind_sw[i] 1
142
+ // valid_cc_element_tag_select[i] 4
143
+ // }
144
+ // byte_alignment() VAR
145
+ // comment_field_bytes 8
146
+ // for (i = 0; i < comment_field_bytes; i++) {
147
+ // comment_field_data[i] 8
148
+ // }
149
+
150
+ if (!$info['aac']['header']['is_vbr']) {
151
+ $info['aac']['program_configs'][$i]['buffer_fullness'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20));
152
+ $bitoffset += 20;
153
+ }
154
+ $info['aac']['program_configs'][$i]['element_instance_tag'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
155
+ $bitoffset += 4;
156
+ $info['aac']['program_configs'][$i]['object_type'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
157
+ $bitoffset += 2;
158
+ $info['aac']['program_configs'][$i]['sampling_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
159
+ $bitoffset += 4;
160
+ $info['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
161
+ $bitoffset += 4;
162
+ $info['aac']['program_configs'][$i]['num_side_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
163
+ $bitoffset += 4;
164
+ $info['aac']['program_configs'][$i]['num_back_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
165
+ $bitoffset += 4;
166
+ $info['aac']['program_configs'][$i]['num_lfe_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
167
+ $bitoffset += 2;
168
+ $info['aac']['program_configs'][$i]['num_assoc_data_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3));
169
+ $bitoffset += 3;
170
+ $info['aac']['program_configs'][$i]['num_valid_cc_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
171
+ $bitoffset += 4;
172
+ $info['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
173
+ $bitoffset += 1;
174
+ if ($info['aac']['program_configs'][$i]['mono_mixdown_present']) {
175
+ $info['aac']['program_configs'][$i]['mono_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
176
+ $bitoffset += 4;
177
+ }
178
+ $info['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
179
+ $bitoffset += 1;
180
+ if ($info['aac']['program_configs'][$i]['stereo_mixdown_present']) {
181
+ $info['aac']['program_configs'][$i]['stereo_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
182
+ $bitoffset += 4;
183
+ }
184
+ $info['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
185
+ $bitoffset += 1;
186
+ if ($info['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) {
187
+ $info['aac']['program_configs'][$i]['matrix_mixdown_idx'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
188
+ $bitoffset += 2;
189
+ $info['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
190
+ $bitoffset += 1;
191
+ }
192
+ for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) {
193
+ $info['aac']['program_configs'][$i]['front_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
194
+ $bitoffset += 1;
195
+ $info['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
196
+ $bitoffset += 4;
197
+ }
198
+ for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) {
199
+ $info['aac']['program_configs'][$i]['side_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
200
+ $bitoffset += 1;
201
+ $info['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
202
+ $bitoffset += 4;
203
+ }
204
+ for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) {
205
+ $info['aac']['program_configs'][$i]['back_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
206
+ $bitoffset += 1;
207
+ $info['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
208
+ $bitoffset += 4;
209
+ }
210
+ for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) {
211
+ $info['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
212
+ $bitoffset += 4;
213
+ }
214
+ for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) {
215
+ $info['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
216
+ $bitoffset += 4;
217
+ }
218
+ for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) {
219
+ $info['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
220
+ $bitoffset += 1;
221
+ $info['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
222
+ $bitoffset += 4;
223
+ }
224
+
225
+ $bitoffset = ceil($bitoffset / 8) * 8;
226
+
227
+ $info['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8));
228
+ $bitoffset += 8;
229
+ $info['aac']['program_configs'][$i]['comment_field'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $info['aac']['program_configs'][$i]['comment_field_bytes']));
230
+ $bitoffset += 8 * $info['aac']['program_configs'][$i]['comment_field_bytes'];
231
+
232
+
233
+ $info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['program_configs'][$i]['object_type'], $info['aac']['header']['mpeg_version']);
234
+ $info['aac']['program_configs'][$i]['sampling_frequency'] = self::AACsampleRateLookup($info['aac']['program_configs'][$i]['sampling_frequency_index']);
235
+ $info['audio']['sample_rate'] = $info['aac']['program_configs'][$i]['sampling_frequency'];
236
+ $info['audio']['channels'] = self::AACchannelCountCalculate($info['aac']['program_configs'][$i]);
237
+ if ($info['aac']['program_configs'][$i]['comment_field']) {
238
+ $info['aac']['comments'][] = $info['aac']['program_configs'][$i]['comment_field'];
239
+ }
240
+ }
241
+ $info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate'];
242
+
243
+ $info['audio']['encoder_options'] = $info['aac']['header_type'].' '.$info['aac']['header']['profile'];
244
+
245
+
246
+
247
+ return true;
248
+
249
+ } else {
250
+
251
+ unset($info['fileformat']);
252
+ unset($info['aac']);
253
+ $info['error'][] = 'AAC-ADIF synch not found at offset '.$info['avdataoffset'].' (expected "ADIF", found "'.substr($AACheader, 0, 4).'" instead)';
254
+ return false;
255
+
256
+ }
257
+
258
+ }
259
+
260
+
261
+ public function getAACADTSheaderFilepointer($MaxFramesToScan=1000000, $ReturnExtendedInfo=false) {
262
+ $info = &$this->getid3->info;
263
+
264
+ // based loosely on code from AACfile by Jurgen Faul <jfaulØgmx.de>
265
+ // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html
266
+
267
+
268
+ // http://faac.sourceforge.net/wiki/index.php?page=ADTS // dead link
269
+ // http://wiki.multimedia.cx/index.php?title=ADTS
270
+
271
+ // * ADTS Fixed Header: these don't change from frame to frame
272
+ // syncword 12 always: '111111111111'
273
+ // ID 1 0: MPEG-4, 1: MPEG-2
274
+ // MPEG layer 2 If you send AAC in MPEG-TS, set to 0
275
+ // protection_absent 1 0: CRC present; 1: no CRC
276
+ // profile 2 0: AAC Main; 1: AAC LC (Low Complexity); 2: AAC SSR (Scalable Sample Rate); 3: AAC LTP (Long Term Prediction)
277
+ // sampling_frequency_index 4 15 not allowed
278
+ // private_bit 1 usually 0
279
+ // channel_configuration 3
280
+ // original/copy 1 0: original; 1: copy
281
+ // home 1 usually 0
282
+ // emphasis 2 only if ID == 0 (ie MPEG-4) // not present in some documentation?
283
+
284
+ // * ADTS Variable Header: these can change from frame to frame
285
+ // copyright_identification_bit 1
286
+ // copyright_identification_start 1
287
+ // aac_frame_length 13 length of the frame including header (in bytes)
288
+ // adts_buffer_fullness 11 0x7FF indicates VBR
289
+ // no_raw_data_blocks_in_frame 2
290
+
291
+ // * ADTS Error check
292
+ // crc_check 16 only if protection_absent == 0
293
+
294
+ $byteoffset = $info['avdataoffset'];
295
+ $framenumber = 0;
296
+
297
+ // Init bit pattern array
298
+ static $decbin = array();
299
+
300
+ // Populate $bindec
301
+ for ($i = 0; $i < 256; $i++) {
302
+ $decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT);
303
+ }
304
+
305
+ // used to calculate bitrate below
306
+ $BitrateCache = array();
307
+
308
+
309
+ while (true) {
310
+ // breaks out when end-of-file encountered, or invalid data found,
311
+ // or MaxFramesToScan frames have been scanned
312
+
313
+ if (!getid3_lib::intValueSupported($byteoffset)) {
314
+ $info['warning'][] = 'Unable to parse AAC file beyond '.$this->ftell().' (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)';
315
+ return false;
316
+ }
317
+ $this->fseek($byteoffset);
318
+
319
+ // First get substring
320
+ $substring = $this->fread(9); // header is 7 bytes (or 9 if CRC is present)
321
+ $substringlength = strlen($substring);
322
+ if ($substringlength != 9) {
323
+ $info['error'][] = 'Failed to read 7 bytes at offset '.($this->ftell() - $substringlength).' (only read '.$substringlength.' bytes)';
324
+ return false;
325
+ }
326
+ // this would be easier with 64-bit math, but split it up to allow for 32-bit:
327
+ $header1 = getid3_lib::BigEndian2Int(substr($substring, 0, 2));
328
+ $header2 = getid3_lib::BigEndian2Int(substr($substring, 2, 4));
329
+ $header3 = getid3_lib::BigEndian2Int(substr($substring, 6, 1));
330
+
331
+ $info['aac']['header']['raw']['syncword'] = ($header1 & 0xFFF0) >> 4;
332
+ if ($info['aac']['header']['raw']['syncword'] != 0x0FFF) {
333
+ $info['error'][] = 'Synch pattern (0x0FFF) not found at offset '.($this->ftell() - $substringlength).' (found 0x0'.strtoupper(dechex($info['aac']['header']['raw']['syncword'])).' instead)';
334
+ //if ($info['fileformat'] == 'aac') {
335
+ // return true;
336
+ //}
337
+ unset($info['aac']);
338
+ return false;
339
+ }
340
+
341
+ // Gather info for first frame only - this takes time to do 1000 times!
342
+ if ($framenumber == 0) {
343
+ $info['aac']['header_type'] = 'ADTS';
344
+ $info['fileformat'] = 'aac';
345
+ $info['audio']['dataformat'] = 'aac';
346
+
347
+ $info['aac']['header']['raw']['mpeg_version'] = ($header1 & 0x0008) >> 3;
348
+ $info['aac']['header']['raw']['mpeg_layer'] = ($header1 & 0x0006) >> 1;
349
+ $info['aac']['header']['raw']['protection_absent'] = ($header1 & 0x0001) >> 0;
350
+
351
+ $info['aac']['header']['raw']['profile_code'] = ($header2 & 0xC0000000) >> 30;
352
+ $info['aac']['header']['raw']['sample_rate_code'] = ($header2 & 0x3C000000) >> 26;
353
+ $info['aac']['header']['raw']['private_stream'] = ($header2 & 0x02000000) >> 25;
354
+ $info['aac']['header']['raw']['channels_code'] = ($header2 & 0x01C00000) >> 22;
355
+ $info['aac']['header']['raw']['original'] = ($header2 & 0x00200000) >> 21;
356
+ $info['aac']['header']['raw']['home'] = ($header2 & 0x00100000) >> 20;
357
+ $info['aac']['header']['raw']['copyright_stream'] = ($header2 & 0x00080000) >> 19;
358
+ $info['aac']['header']['raw']['copyright_start'] = ($header2 & 0x00040000) >> 18;
359
+ $info['aac']['header']['raw']['frame_length'] = ($header2 & 0x0003FFE0) >> 5;
360
+
361
+ $info['aac']['header']['mpeg_version'] = ($info['aac']['header']['raw']['mpeg_version'] ? 2 : 4);
362
+ $info['aac']['header']['crc_present'] = ($info['aac']['header']['raw']['protection_absent'] ? false: true);
363
+ $info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['header']['raw']['profile_code'], $info['aac']['header']['mpeg_version']);
364
+ $info['aac']['header']['sample_frequency'] = self::AACsampleRateLookup($info['aac']['header']['raw']['sample_rate_code']);
365
+ $info['aac']['header']['private'] = (bool) $info['aac']['header']['raw']['private_stream'];
366
+ $info['aac']['header']['original'] = (bool) $info['aac']['header']['raw']['original'];
367
+ $info['aac']['header']['home'] = (bool) $info['aac']['header']['raw']['home'];
368
+ $info['aac']['header']['channels'] = (($info['aac']['header']['raw']['channels_code'] == 7) ? 8 : $info['aac']['header']['raw']['channels_code']);
369
+ if ($ReturnExtendedInfo) {
370
+ $info['aac'][$framenumber]['copyright_id_bit'] = (bool) $info['aac']['header']['raw']['copyright_stream'];
371
+ $info['aac'][$framenumber]['copyright_id_start'] = (bool) $info['aac']['header']['raw']['copyright_start'];
372
+ }
373
+
374
+ if ($info['aac']['header']['raw']['mpeg_layer'] != 0) {
375
+ $info['warning'][] = 'Layer error - expected "0", found "'.$info['aac']['header']['raw']['mpeg_layer'].'" instead';
376
+ }
377
+ if ($info['aac']['header']['sample_frequency'] == 0) {
378
+ $info['error'][] = 'Corrupt AAC file: sample_frequency == zero';
379
+ return false;
380
+ }
381
+
382
+ $info['audio']['sample_rate'] = $info['aac']['header']['sample_frequency'];
383
+ $info['audio']['channels'] = $info['aac']['header']['channels'];
384
+ }
385
+
386
+ $FrameLength = ($header2 & 0x0003FFE0) >> 5;
387
+
388
+ if (!isset($BitrateCache[$FrameLength])) {
389
+ $BitrateCache[$FrameLength] = ($info['aac']['header']['sample_frequency'] / 1024) * $FrameLength * 8;
390
+ }
391
+ getid3_lib::safe_inc($info['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1);
392
+
393
+ $info['aac'][$framenumber]['aac_frame_length'] = $FrameLength;
394
+
395
+ $info['aac'][$framenumber]['adts_buffer_fullness'] = (($header2 & 0x0000001F) << 6) & (($header3 & 0xFC) >> 2);
396
+ if ($info['aac'][$framenumber]['adts_buffer_fullness'] == 0x07FF) {
397
+ $info['audio']['bitrate_mode'] = 'vbr';
398
+ } else {
399
+ $info['audio']['bitrate_mode'] = 'cbr';
400
+ }
401
+ $info['aac'][$framenumber]['num_raw_data_blocks'] = (($header3 & 0x03) >> 0);
402
+
403
+ if ($info['aac']['header']['crc_present']) {
404
+ //$info['aac'][$framenumber]['crc'] = getid3_lib::BigEndian2Int(substr($substring, 7, 2);
405
+ }
406
+
407
+ if (!$ReturnExtendedInfo) {
408
+ unset($info['aac'][$framenumber]);
409
+ }
410
+
411
+ /*
412
+ $rounded_precision = 5000;
413
+ $info['aac']['bitrate_distribution_rounded'] = array();
414
+ foreach ($info['aac']['bitrate_distribution'] as $bitrate => $count) {
415
+ $rounded_bitrate = round($bitrate / $rounded_precision) * $rounded_precision;
416
+ getid3_lib::safe_inc($info['aac']['bitrate_distribution_rounded'][$rounded_bitrate], $count);
417
+ }
418
+ ksort($info['aac']['bitrate_distribution_rounded']);
419
+ */
420
+
421
+ $byteoffset += $FrameLength;
422
+ if ((++$framenumber < $MaxFramesToScan) && (($byteoffset + 10) < $info['avdataend'])) {
423
+
424
+ // keep scanning
425
+
426
+ } else {
427
+
428
+ $info['aac']['frames'] = $framenumber;
429
+ $info['playtime_seconds'] = ($info['avdataend'] / $byteoffset) * (($framenumber * 1024) / $info['aac']['header']['sample_frequency']); // (1 / % of file scanned) * (samples / (samples/sec)) = seconds
430
+ if ($info['playtime_seconds'] == 0) {
431
+ $info['error'][] = 'Corrupt AAC file: playtime_seconds == zero';
432
+ return false;
433
+ }
434
+ $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
435
+ ksort($info['aac']['bitrate_distribution']);
436
+
437
+ $info['audio']['encoder_options'] = $info['aac']['header_type'].' '.$info['aac']['header']['profile'];
438
+
439
+ return true;
440
+
441
+ }
442
+ }
443
+ // should never get here.
444
+ }
445
+
446
+ public static function AACsampleRateLookup($samplerateid) {
447
+ static $AACsampleRateLookup = array();
448
+ if (empty($AACsampleRateLookup)) {
449
+ $AACsampleRateLookup[0] = 96000;
450
+ $AACsampleRateLookup[1] = 88200;
451
+ $AACsampleRateLookup[2] = 64000;
452
+ $AACsampleRateLookup[3] = 48000;
453
+ $AACsampleRateLookup[4] = 44100;
454
+ $AACsampleRateLookup[5] = 32000;
455
+ $AACsampleRateLookup[6] = 24000;
456
+ $AACsampleRateLookup[7] = 22050;
457
+ $AACsampleRateLookup[8] = 16000;
458
+ $AACsampleRateLookup[9] = 12000;
459
+ $AACsampleRateLookup[10] = 11025;
460
+ $AACsampleRateLookup[11] = 8000;
461
+ $AACsampleRateLookup[12] = 0;
462
+ $AACsampleRateLookup[13] = 0;
463
+ $AACsampleRateLookup[14] = 0;
464
+ $AACsampleRateLookup[15] = 0;
465
+ }
466
+ return (isset($AACsampleRateLookup[$samplerateid]) ? $AACsampleRateLookup[$samplerateid] : 'invalid');
467
+ }
468
+
469
+ public static function AACprofileLookup($profileid, $mpegversion) {
470
+ static $AACprofileLookup = array();
471
+ if (empty($AACprofileLookup)) {
472
+ $AACprofileLookup[2][0] = 'Main profile';
473
+ $AACprofileLookup[2][1] = 'Low Complexity profile (LC)';
474
+ $AACprofileLookup[2][2] = 'Scalable Sample Rate profile (SSR)';
475
+ $AACprofileLookup[2][3] = '(reserved)';
476
+ $AACprofileLookup[4][0] = 'AAC_MAIN';
477
+ $AACprofileLookup[4][1] = 'AAC_LC';
478
+ $AACprofileLookup[4][2] = 'AAC_SSR';
479
+ $AACprofileLookup[4][3] = 'AAC_LTP';
480
+ }
481
+ return (isset($AACprofileLookup[$mpegversion][$profileid]) ? $AACprofileLookup[$mpegversion][$profileid] : 'invalid');
482
+ }
483
+
484
+ public static function AACchannelCountCalculate($program_configs) {
485
+ $channels = 0;
486
+ for ($i = 0; $i < $program_configs['num_front_channel_elements']; $i++) {
487
+ $channels++;
488
+ if ($program_configs['front_element_is_cpe'][$i]) {
489
+ // each front element is channel pair (CPE = Channel Pair Element)
490
+ $channels++;
491
+ }
492
+ }
493
+ for ($i = 0; $i < $program_configs['num_side_channel_elements']; $i++) {
494
+ $channels++;
495
+ if ($program_configs['side_element_is_cpe'][$i]) {
496
+ // each side element is channel pair (CPE = Channel Pair Element)
497
+ $channels++;
498
+ }
499
+ }
500
+ for ($i = 0; $i < $program_configs['num_back_channel_elements']; $i++) {
501
+ $channels++;
502
+ if ($program_configs['back_element_is_cpe'][$i]) {
503
+ // each back element is channel pair (CPE = Channel Pair Element)
504
+ $channels++;
505
+ }
506
+ }
507
+ for ($i = 0; $i < $program_configs['num_lfe_channel_elements']; $i++) {
508
+ $channels++;
509
+ }
510
+ return $channels;
511
+ }
512
+
513
+ }
getid3/module.audio.ac3.php ADDED
@@ -0,0 +1,474 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /////////////////////////////////////////////////////////////////
3
+ /// getID3() by James Heinrich <info@getid3.org> //
4
+ // available at http://getid3.sourceforge.net //
5
+ // or http://www.getid3.org //
6
+ // also https://github.com/JamesHeinrich/getID3 //
7
+ /////////////////////////////////////////////////////////////////
8
+ // See readme.txt for more details //
9
+ /////////////////////////////////////////////////////////////////
10
+ // //
11
+ // module.audio.ac3.php //
12
+ // module for analyzing AC-3 (aka Dolby Digital) audio files //
13
+ // dependencies: NONE //
14
+ // ///
15
+ /////////////////////////////////////////////////////////////////
16
+
17
+
18
+ class getid3_ac3 extends getid3_handler
19
+ {
20
+ private $AC3header = array();
21
+ private $BSIoffset = 0;
22
+
23
+ const syncword = "\x0B\x77";
24
+
25
+ public function Analyze() {
26
+ $info = &$this->getid3->info;
27
+
28
+ ///AH
29
+ $info['ac3']['raw']['bsi'] = array();
30
+ $thisfile_ac3 = &$info['ac3'];
31
+ $thisfile_ac3_raw = &$thisfile_ac3['raw'];
32
+ $thisfile_ac3_raw_bsi = &$thisfile_ac3_raw['bsi'];
33
+
34
+
35
+ // http://www.atsc.org/standards/a_52a.pdf
36
+
37
+ $info['fileformat'] = 'ac3';
38
+
39
+ // An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames
40
+ // Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256
41
+ // new audio samples per channel. A synchronization information (SI) header at the beginning
42
+ // of each frame contains information needed to acquire and maintain synchronization. A
43
+ // bit stream information (BSI) header follows SI, and contains parameters describing the coded
44
+ // audio service. The coded audio blocks may be followed by an auxiliary data (Aux) field. At the
45
+ // end of each frame is an error check field that includes a CRC word for error detection. An
46
+ // additional CRC word is located in the SI header, the use of which, by a decoder, is optional.
47
+ //
48
+ // syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC
49
+
50
+ // syncinfo() {
51
+ // syncword 16
52
+ // crc1 16
53
+ // fscod 2
54
+ // frmsizecod 6
55
+ // } /* end of syncinfo */
56
+
57
+ $this->fseek($info['avdataoffset']);
58
+ $this->AC3header['syncinfo'] = $this->fread(5);
59
+
60
+ if (strpos($this->AC3header['syncinfo'], self::syncword) === 0) {
61
+ $thisfile_ac3_raw['synchinfo']['synchword'] = self::syncword;
62
+ $offset = 2;
63
+ } else {
64
+ if (!$this->isDependencyFor('matroska')) {
65
+ unset($info['fileformat'], $info['ac3']);
66
+ return $this->error('Expecting "'.getid3_lib::PrintHexBytes(self::syncword).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($this->AC3header['syncinfo'], 0, 2)).'"');
67
+ }
68
+ $offset = 0;
69
+ $this->fseek(-2, SEEK_CUR);
70
+ }
71
+
72
+ $info['audio']['dataformat'] = 'ac3';
73
+ $info['audio']['bitrate_mode'] = 'cbr';
74
+ $info['audio']['lossless'] = false;
75
+
76
+ $thisfile_ac3_raw['synchinfo']['crc1'] = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], $offset, 2));
77
+ $ac3_synchinfo_fscod_frmsizecod = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], ($offset + 2), 1));
78
+ $thisfile_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xC0) >> 6;
79
+ $thisfile_ac3_raw['synchinfo']['frmsizecod'] = ($ac3_synchinfo_fscod_frmsizecod & 0x3F);
80
+
81
+ $thisfile_ac3['sample_rate'] = self::sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']);
82
+ if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) {
83
+ $info['audio']['sample_rate'] = $thisfile_ac3['sample_rate'];
84
+ }
85
+
86
+ $thisfile_ac3['frame_length'] = self::frameSizeLookup($thisfile_ac3_raw['synchinfo']['frmsizecod'], $thisfile_ac3_raw['synchinfo']['fscod']);
87
+ $thisfile_ac3['bitrate'] = self::bitrateLookup($thisfile_ac3_raw['synchinfo']['frmsizecod']);
88
+ $info['audio']['bitrate'] = $thisfile_ac3['bitrate'];
89
+
90
+ $this->AC3header['bsi'] = getid3_lib::BigEndian2Bin($this->fread(15));
91
+ $ac3_bsi_offset = 0;
92
+
93
+ $thisfile_ac3_raw_bsi['bsid'] = $this->readHeaderBSI(5);
94
+ if ($thisfile_ac3_raw_bsi['bsid'] > 8) {
95
+ // Decoders which can decode version 8 will thus be able to decode version numbers less than 8.
96
+ // If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used.
97
+ // Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8.
98
+ $this->error('Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 8');
99
+ unset($info['ac3']);
100
+ return false;
101
+ }
102
+
103
+ $thisfile_ac3_raw_bsi['bsmod'] = $this->readHeaderBSI(3);
104
+ $thisfile_ac3_raw_bsi['acmod'] = $this->readHeaderBSI(3);
105
+
106
+ $thisfile_ac3['service_type'] = self::serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']);
107
+ $ac3_coding_mode = self::audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']);
108
+ foreach($ac3_coding_mode as $key => $value) {
109
+ $thisfile_ac3[$key] = $value;
110
+ }
111
+ switch ($thisfile_ac3_raw_bsi['acmod']) {
112
+ case 0:
113
+ case 1:
114
+ $info['audio']['channelmode'] = 'mono';
115
+ break;
116
+ case 3:
117
+ case 4:
118
+ $info['audio']['channelmode'] = 'stereo';
119
+ break;
120
+ default:
121
+ $info['audio']['channelmode'] = 'surround';
122
+ break;
123
+ }
124
+ $info['audio']['channels'] = $thisfile_ac3['num_channels'];
125
+
126
+ if ($thisfile_ac3_raw_bsi['acmod'] & 0x01) {
127
+ // If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream.
128
+ $thisfile_ac3_raw_bsi['cmixlev'] = $this->readHeaderBSI(2);
129
+ $thisfile_ac3['center_mix_level'] = self::centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']);
130
+ }
131
+
132
+ if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) {
133
+ // If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream.
134
+ $thisfile_ac3_raw_bsi['surmixlev'] = $this->readHeaderBSI(2);
135
+ $thisfile_ac3['surround_mix_level'] = self::surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']);
136
+ }
137
+
138
+ if ($thisfile_ac3_raw_bsi['acmod'] == 0x02) {
139
+ // When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround.
140
+ $thisfile_ac3_raw_bsi['dsurmod'] = $this->readHeaderBSI(2);
141
+ $thisfile_ac3['dolby_surround_mode'] = self::dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']);
142
+ }
143
+
144
+ $thisfile_ac3_raw_bsi['lfeon'] = (bool) $this->readHeaderBSI(1);
145
+ $thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['lfeon'];
146
+ if ($thisfile_ac3_raw_bsi['lfeon']) {
147
+ //$info['audio']['channels']++;
148
+ $info['audio']['channels'] .= '.1';
149
+ }
150
+
151
+ $thisfile_ac3['channels_enabled'] = self::channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']);
152
+
153
+ // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31.
154
+ // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
155
+ $thisfile_ac3_raw_bsi['dialnorm'] = $this->readHeaderBSI(5);
156
+ $thisfile_ac3['dialogue_normalization'] = '-'.$thisfile_ac3_raw_bsi['dialnorm'].'dB';
157
+
158
+ $thisfile_ac3_raw_bsi['compre_flag'] = (bool) $this->readHeaderBSI(1);
159
+ if ($thisfile_ac3_raw_bsi['compre_flag']) {
160
+ $thisfile_ac3_raw_bsi['compr'] = $this->readHeaderBSI(8);
161
+ $thisfile_ac3['heavy_compression'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr']);
162
+ }
163
+
164
+ $thisfile_ac3_raw_bsi['langcode_flag'] = (bool) $this->readHeaderBSI(1);
165
+ if ($thisfile_ac3_raw_bsi['langcode_flag']) {
166
+ $thisfile_ac3_raw_bsi['langcod'] = $this->readHeaderBSI(8);
167
+ }
168
+
169
+ $thisfile_ac3_raw_bsi['audprodie'] = (bool) $this->readHeaderBSI(1);
170
+ if ($thisfile_ac3_raw_bsi['audprodie']) {
171
+ $thisfile_ac3_raw_bsi['mixlevel'] = $this->readHeaderBSI(5);
172
+ $thisfile_ac3_raw_bsi['roomtyp'] = $this->readHeaderBSI(2);
173
+
174
+ $thisfile_ac3['mixing_level'] = (80 + $thisfile_ac3_raw_bsi['mixlevel']).'dB';
175
+ $thisfile_ac3['room_type'] = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']);
176
+ }
177
+
178
+ if ($thisfile_ac3_raw_bsi['acmod'] == 0x00) {
179
+ // If acmod is 0, then two completely independent program channels (dual mono)
180
+ // are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case,
181
+ // a number of additional items are present in BSI or audblk to fully describe Ch2.
182
+
183
+ // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31.
184
+ // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
185
+ $thisfile_ac3_raw_bsi['dialnorm2'] = $this->readHeaderBSI(5);
186
+ $thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB';
187
+
188
+ $thisfile_ac3_raw_bsi['compre_flag2'] = (bool) $this->readHeaderBSI(1);
189
+ if ($thisfile_ac3_raw_bsi['compre_flag2']) {
190
+ $thisfile_ac3_raw_bsi['compr2'] = $this->readHeaderBSI(8);
191
+ $thisfile_ac3['heavy_compression2'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr2']);
192
+ }
193
+
194
+ $thisfile_ac3_raw_bsi['langcode_flag2'] = (bool) $this->readHeaderBSI(1);
195
+ if ($thisfile_ac3_raw_bsi['langcode_flag2']) {
196
+ $thisfile_ac3_raw_bsi['langcod2'] = $this->readHeaderBSI(8);
197
+ }
198
+
199
+ $thisfile_ac3_raw_bsi['audprodie2'] = (bool) $this->readHeaderBSI(1);
200
+ if ($thisfile_ac3_raw_bsi['audprodie2']) {
201
+ $thisfile_ac3_raw_bsi['mixlevel2'] = $this->readHeaderBSI(5);
202
+ $thisfile_ac3_raw_bsi['roomtyp2'] = $this->readHeaderBSI(2);
203
+
204
+ $thisfile_ac3['mixing_level2'] = (80 + $thisfile_ac3_raw_bsi['mixlevel2']).'dB';
205
+ $thisfile_ac3['room_type2'] = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']);
206
+ }
207
+
208
+ }
209
+
210
+ $thisfile_ac3_raw_bsi['copyright'] = (bool) $this->readHeaderBSI(1);
211
+
212
+ $thisfile_ac3_raw_bsi['original'] = (bool) $this->readHeaderBSI(1);
213
+
214
+ $thisfile_ac3_raw_bsi['timecode1_flag'] = (bool) $this->readHeaderBSI(1);
215
+ if ($thisfile_ac3_raw_bsi['timecode1_flag']) {
216
+ $thisfile_ac3_raw_bsi['timecode1'] = $this->readHeaderBSI(14);
217
+ }
218
+
219
+ $thisfile_ac3_raw_bsi['timecode2_flag'] = (bool) $this->readHeaderBSI(1);
220
+ if ($thisfile_ac3_raw_bsi['timecode2_flag']) {
221
+ $thisfile_ac3_raw_bsi['timecode2'] = $this->readHeaderBSI(14);
222
+ }
223
+
224
+ $thisfile_ac3_raw_bsi['addbsi_flag'] = (bool) $this->readHeaderBSI(1);
225
+ if ($thisfile_ac3_raw_bsi['addbsi_flag']) {
226
+ $thisfile_ac3_raw_bsi['addbsi_length'] = $this->readHeaderBSI(6);
227
+
228
+ $this->AC3header['bsi'] .= getid3_lib::BigEndian2Bin($this->fread($thisfile_ac3_raw_bsi['addbsi_length']));
229
+
230
+ $thisfile_ac3_raw_bsi['addbsi_data'] = substr($this->AC3header['bsi'], $this->BSIoffset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8);
231
+ $this->BSIoffset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8;
232
+ }
233
+
234
+ return true;
235
+ }
236
+
237
+ private function readHeaderBSI($length) {
238
+ $data = substr($this->AC3header['bsi'], $this->BSIoffset, $length);
239
+ $this->BSIoffset += $length;
240
+
241
+ return bindec($data);
242
+ }
243
+
244
+ public static function sampleRateCodeLookup($fscod) {
245
+ static $sampleRateCodeLookup = array(
246
+ 0 => 48000,
247
+ 1 => 44100,
248
+ 2 => 32000,
249
+ 3 => 'reserved' // If the reserved code is indicated, the decoder should not attempt to decode audio and should mute.
250
+ );
251
+ return (isset($sampleRateCodeLookup[$fscod]) ? $sampleRateCodeLookup[$fscod] : false);
252
+ }
253
+
254
+ public static function serviceTypeLookup($bsmod, $acmod) {
255
+ static $serviceTypeLookup = array();
256
+ if (empty($serviceTypeLookup)) {
257
+ for ($i = 0; $i <= 7; $i++) {
258
+ $serviceTypeLookup[0][$i] = 'main audio service: complete main (CM)';
259
+ $serviceTypeLookup[1][$i] = 'main audio service: music and effects (ME)';
260
+ $serviceTypeLookup[2][$i] = 'associated service: visually impaired (VI)';
261
+ $serviceTypeLookup[3][$i] = 'associated service: hearing impaired (HI)';
262
+ $serviceTypeLookup[4][$i] = 'associated service: dialogue (D)';
263
+ $serviceTypeLookup[5][$i] = 'associated service: commentary (C)';
264
+ $serviceTypeLookup[6][$i] = 'associated service: emergency (E)';
265
+ }
266
+
267
+ $serviceTypeLookup[7][1] = 'associated service: voice over (VO)';
268
+ for ($i = 2; $i <= 7; $i++) {
269
+ $serviceTypeLookup[7][$i] = 'main audio service: karaoke';
270
+ }
271
+ }
272
+ return (isset($serviceTypeLookup[$bsmod][$acmod]) ? $serviceTypeLookup[$bsmod][$acmod] : false);
273
+ }
274
+
275
+ public static function audioCodingModeLookup($acmod) {
276
+ // array(channel configuration, # channels (not incl LFE), channel order)
277
+ static $audioCodingModeLookup = array (
278
+ 0 => array('channel_config'=>'1+1', 'num_channels'=>2, 'channel_order'=>'Ch1,Ch2'),
279
+ 1 => array('channel_config'=>'1/0', 'num_channels'=>1, 'channel_order'=>'C'),
280
+ 2 => array('channel_config'=>'2/0', 'num_channels'=>2, 'channel_order'=>'L,R'),
281
+ 3 => array('channel_config'=>'3/0', 'num_channels'=>3, 'channel_order'=>'L,C,R'),
282
+ 4 => array('channel_config'=>'2/1', 'num_channels'=>3, 'channel_order'=>'L,R,S'),
283
+ 5 => array('channel_config'=>'3/1', 'num_channels'=>4, 'channel_order'=>'L,C,R,S'),
284
+ 6 => array('channel_config'=>'2/2', 'num_channels'=>4, 'channel_order'=>'L,R,SL,SR'),
285
+ 7 => array('channel_config'=>'3/2', 'num_channels'=>5, 'channel_order'=>'L,C,R,SL,SR'),
286
+ );
287
+ return (isset($audioCodingModeLookup[$acmod]) ? $audioCodingModeLookup[$acmod] : false);
288
+ }
289
+
290
+ public static function centerMixLevelLookup($cmixlev) {
291
+ static $centerMixLevelLookup;
292
+ if (empty($centerMixLevelLookup)) {
293
+ $centerMixLevelLookup = array(
294
+ 0 => pow(2, -3.0 / 6), // 0.707 (-3.0 dB)
295
+ 1 => pow(2, -4.5 / 6), // 0.595 (-4.5 dB)
296
+ 2 => pow(2, -6.0 / 6), // 0.500 (-6.0 dB)
297
+ 3 => 'reserved'
298
+ );
299
+ }
300
+ return (isset($centerMixLevelLookup[$cmixlev]) ? $centerMixLevelLookup[$cmixlev] : false);
301
+ }
302
+
303
+ public static function surroundMixLevelLookup($surmixlev) {
304
+ static $surroundMixLevelLookup;
305
+ if (empty($surroundMixLevelLookup)) {
306
+ $surroundMixLevelLookup = array(
307
+ 0 => pow(2, -3.0 / 6),
308
+ 1 => pow(2, -6.0 / 6),
309
+ 2 => 0,
310
+ 3 => 'reserved'
311
+ );
312
+ }
313
+ return (isset($surroundMixLevelLookup[$surmixlev]) ? $surroundMixLevelLookup[$surmixlev] : false);
314
+ }
315
+
316
+ public static function dolbySurroundModeLookup($dsurmod) {
317
+ static $dolbySurroundModeLookup = array(
318
+ 0 => 'not indicated',
319
+ 1 => 'Not Dolby Surround encoded',
320
+ 2 => 'Dolby Surround encoded',
321
+ 3 => 'reserved'
322
+ );
323
+ return (isset($dolbySurroundModeLookup[$dsurmod]) ? $dolbySurroundModeLookup[$dsurmod] : false);
324
+ }
325
+
326
+ public static function channelsEnabledLookup($acmod, $lfeon) {
327
+ $lookup = array(
328
+ 'ch1'=>(bool) ($acmod == 0),
329
+ 'ch2'=>(bool) ($acmod == 0),
330
+ 'left'=>(bool) ($acmod > 1),
331
+ 'right'=>(bool) ($acmod > 1),
332
+ 'center'=>(bool) ($acmod & 0x01),
333
+ 'surround_mono'=>false,
334
+ 'surround_left'=>false,
335
+ 'surround_right'=>false,
336
+ 'lfe'=>$lfeon);
337
+ switch ($acmod) {
338
+ case 4:
339
+ case 5:
340
+ $lookup['surround_mono'] = true;
341
+ break;
342
+ case 6:
343
+ case 7:
344
+ $lookup['surround_left'] = true;
345
+ $lookup['surround_right'] = true;
346
+ break;
347
+ }
348
+ return $lookup;
349
+ }
350
+
351
+ public static function heavyCompression($compre) {
352
+ // The first four bits indicate gain changes in 6.02dB increments which can be
353
+ // implemented with an arithmetic shift operation. The following four bits
354
+ // indicate linear gain changes, and require a 5-bit multiply.
355
+ // We will represent the two 4-bit fields of compr as follows:
356
+ // X0 X1 X2 X3 . Y4 Y5 Y6 Y7
357
+ // The meaning of the X values is most simply described by considering X to represent a 4-bit
358
+ // signed integer with values from -8 to +7. The gain indicated by X is then (X + 1) * 6.02 dB. The
359
+ // following table shows this in detail.
360
+
361
+ // Meaning of 4 msb of compr
362
+ // 7 +48.16 dB
363
+ // 6 +42.14 dB
364
+ // 5 +36.12 dB
365
+ // 4 +30.10 dB
366
+ // 3 +24.08 dB
367
+ // 2 +18.06 dB
368
+ // 1 +12.04 dB
369
+ // 0 +6.02 dB
370
+ // -1 0 dB
371
+ // -2 -6.02 dB
372
+ // -3 -12.04 dB
373
+ // -4 -18.06 dB
374
+ // -5 -24.08 dB
375
+ // -6 -30.10 dB
376
+ // -7 -36.12 dB
377
+ // -8 -42.14 dB
378
+
379
+ $fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT);
380
+ if ($fourbit{0} == '1') {
381
+ $log_gain = -8 + bindec(substr($fourbit, 1));
382
+ } else {
383
+ $log_gain = bindec(substr($fourbit, 1));
384
+ }
385
+ $log_gain = ($log_gain + 1) * getid3_lib::RGADamplitude2dB(2);
386
+
387
+ // The value of Y is a linear representation of a gain change of up to -6 dB. Y is considered to
388
+ // be an unsigned fractional integer, with a leading value of 1, or: 0.1 Y4 Y5 Y6 Y7 (base 2). Y can
389
+ // represent values between 0.111112 (or 31/32) and 0.100002 (or 1/2). Thus, Y can represent gain
390
+ // changes from -0.28 dB to -6.02 dB.
391
+
392
+ $lin_gain = (16 + ($compre & 0x0F)) / 32;
393
+
394
+ // The combination of X and Y values allows compr to indicate gain changes from
395
+ // 48.16 - 0.28 = +47.89 dB, to
396
+ // -42.14 - 6.02 = -48.16 dB.
397
+
398
+ return $log_gain - $lin_gain;
399
+ }
400
+
401
+ public static function roomTypeLookup($roomtyp) {
402
+ static $roomTypeLookup = array(
403
+ 0 => 'not indicated',
404
+ 1 => 'large room, X curve monitor',
405
+ 2 => 'small room, flat monitor',
406
+ 3 => 'reserved'
407
+ );
408
+ return (isset($roomTypeLookup[$roomtyp]) ? $roomTypeLookup[$roomtyp] : false);
409
+ }
410
+
411
+ public static function frameSizeLookup($frmsizecod, $fscod) {
412
+ $padding = (bool) ($frmsizecod % 2);
413
+ $framesizeid = floor($frmsizecod / 2);
414
+
415
+ static $frameSizeLookup = array();
416
+ if (empty($frameSizeLookup)) {
417
+ $frameSizeLookup = array (
418
+ 0 => array(128, 138, 192),
419
+ 1 => array(40, 160, 174, 240),
420
+ 2 => array(48, 192, 208, 288),
421
+ 3 => array(56, 224, 242, 336),
422
+ 4 => array(64, 256, 278, 384),
423
+ 5 => array(80, 320, 348, 480),
424
+ 6 => array(96, 384, 416, 576),
425
+ 7 => array(112, 448, 486, 672),
426
+ 8 => array(128, 512, 556, 768),
427
+ 9 => array(160, 640, 696, 960),
428
+ 10 => array(192, 768, 834, 1152),
429
+ 11 => array(224, 896, 974, 1344),
430
+ 12 => array(256, 1024, 1114, 1536),
431
+ 13 => array(320, 1280, 1392, 1920),
432
+ 14 => array(384, 1536, 1670, 2304),
433
+ 15 => array(448, 1792, 1950, 2688),
434
+ 16 => array(512, 2048, 2228, 3072),
435
+ 17 => array(576, 2304, 2506, 3456),
436
+ 18 => array(640, 2560, 2786, 3840)
437
+ );
438
+ }
439
+ if (($fscod == 1) && $padding) {
440
+ // frame lengths are padded by 1 word (16 bits) at 44100
441
+ $frameSizeLookup[$frmsizecod] += 2;
442
+ }
443
+ return (isset($frameSizeLookup[$framesizeid][$fscod]) ? $frameSizeLookup[$framesizeid][$fscod] : false);
444
+ }
445
+
446
+ public static function bitrateLookup($frmsizecod) {
447
+ $framesizeid = floor($frmsizecod / 2);
448
+
449
+ static $bitrateLookup = array(
450
+ 0 => 32000,
451
+ 1 => 40000,
452
+ 2 => 48000,
453
+ 3 => 56000,
454
+ 4 => 64000,
455
+ 5 => 80000,
456
+ 6 => 96000,
457
+ 7 => 112000,
458
+ 8 => 128000,
459
+ 9 => 160000,
460
+ 10 => 192000,
461
+ 11 => 224000,
462
+ 12 => 256000,
463
+ 13 => 320000,
464
+ 14 => 384000,
465
+ 15 => 448000,
466
+ 16 => 512000,
467
+ 17 => 576000,
468
+ 18 => 640000
469
+ );
470
+ return (isset($bitrateLookup[$framesizeid]) ? $bitrateLookup[$framesizeid] : false);
471
+ }
472
+
473
+
474
+ }
getid3/module.audio.dts.php ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /////////////////////////////////////////////////////////////////
3
+ /// getID3() by James Heinrich <info@getid3.org> //
4
+ // available at http://getid3.sourceforge.net //
5
+ // or http://www.getid3.org //
6
+ // also https://github.com/JamesHeinrich/getID3 //
7
+ /////////////////////////////////////////////////////////////////
8
+ // See readme.txt for more details //
9
+ /////////////////////////////////////////////////////////////////
10
+ // //
11
+ // module.audio.dts.php //
12
+ // module for analyzing DTS Audio files //
13
+ // dependencies: NONE //
14
+ // //
15
+ /////////////////////////////////////////////////////////////////
16
+
17
+
18
+ /**
19
+ * @tutorial http://wiki.multimedia.cx/index.php?title=DTS
20
+ */
21
+ class getid3_dts extends getid3_handler
22
+ {
23
+ /**
24
+ * Default DTS syncword used in native .cpt or .dts formats
25
+ */
26
+ const syncword = "\x7F\xFE\x80\x01";
27
+
28
+ private $readBinDataOffset = 0;
29
+
30
+ /**
31
+ * Possible syncwords indicating bitstream encoding
32
+ */
33
+ public static $syncwords = array(
34
+ 0 => "\x7F\xFE\x80\x01", // raw big-endian
35
+ 1 => "\xFE\x7F\x01\x80", // raw little-endian
36
+ 2 => "\x1F\xFF\xE8\x00", // 14-bit big-endian
37
+ 3 => "\xFF\x1F\x00\xE8"); // 14-bit little-endian
38
+
39
+ public function Analyze() {
40
+ $info = &$this->getid3->info;
41
+ $info['fileformat'] = 'dts';
42
+
43
+ $this->fseek($info['avdataoffset']);
44
+ $DTSheader = $this->fread(20); // we only need 2 words magic + 6 words frame header, but these words may be normal 16-bit words OR 14-bit words with 2 highest bits set to zero, so 8 words can be either 8*16/8 = 16 bytes OR 8*16*(16/14)/8 = 18.3 bytes
45
+
46
+ // check syncword
47
+ $sync = substr($DTSheader, 0, 4);
48
+ if (($encoding = array_search($sync, self::$syncwords)) !== false) {
49
+
50
+ $info['dts']['raw']['magic'] = $sync;
51
+ $this->readBinDataOffset = 32;
52
+
53
+ } elseif ($this->isDependencyFor('matroska')) {
54
+
55
+ // Matroska contains DTS without syncword encoded as raw big-endian format
56
+ $encoding = 0;
57
+ $this->readBinDataOffset = 0;
58
+
59
+ } else {
60
+
61
+ unset($info['fileformat']);
62
+ return $this->error('Expecting "'.implode('| ', array_map('getid3_lib::PrintHexBytes', self::$syncwords)).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($sync).'"');
63
+
64
+ }
65
+
66
+ // decode header
67
+ $fhBS = '';
68
+ for ($word_offset = 0; $word_offset <= strlen($DTSheader); $word_offset += 2) {
69
+ switch ($encoding) {
70
+ case 0: // raw big-endian
71
+ $fhBS .= getid3_lib::BigEndian2Bin( substr($DTSheader, $word_offset, 2) );
72
+ break;
73
+ case 1: // raw little-endian
74
+ $fhBS .= getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2)));
75
+ break;
76
+ case 2: // 14-bit big-endian
77
+ $fhBS .= substr(getid3_lib::BigEndian2Bin( substr($DTSheader, $word_offset, 2) ), 2, 14);
78
+ break;
79
+ case 3: // 14-bit little-endian
80
+ $fhBS .= substr(getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2))), 2, 14);
81
+ break;
82
+ }
83
+ }
84
+
85
+ $info['dts']['raw']['frame_type'] = $this->readBinData($fhBS, 1);
86
+ $info['dts']['raw']['deficit_samples'] = $this->readBinData($fhBS, 5);
87
+ $info['dts']['flags']['crc_present'] = (bool) $this->readBinData($fhBS, 1);
88
+ $info['dts']['raw']['pcm_sample_blocks'] = $this->readBinData($fhBS, 7);
89
+ $info['dts']['raw']['frame_byte_size'] = $this->readBinData($fhBS, 14);
90
+ $info['dts']['raw']['channel_arrangement'] = $this->readBinData($fhBS, 6);
91
+ $info['dts']['raw']['sample_frequency'] = $this->readBinData($fhBS, 4);
92
+ $info['dts']['raw']['bitrate'] = $this->readBinData($fhBS, 5);
93
+ $info['dts']['flags']['embedded_downmix'] = (bool) $this->readBinData($fhBS, 1);
94
+ $info['dts']['flags']['dynamicrange'] = (bool) $this->readBinData($fhBS, 1);
95
+ $info['dts']['flags']['timestamp'] = (bool) $this->readBinData($fhBS, 1);
96
+ $info['dts']['flags']['auxdata'] = (bool) $this->readBinData($fhBS, 1);
97
+ $info['dts']['flags']['hdcd'] = (bool) $this->readBinData($fhBS, 1);
98
+ $info['dts']['raw']['extension_audio'] = $this->readBinData($fhBS, 3);
99
+ $info['dts']['flags']['extended_coding'] = (bool) $this->readBinData($fhBS, 1);
100
+ $info['dts']['flags']['audio_sync_insertion'] = (bool) $this->readBinData($fhBS, 1);
101
+ $info['dts']['raw']['lfe_effects'] = $this->readBinData($fhBS, 2);
102
+ $info['dts']['flags']['predictor_history'] = (bool) $this->readBinData($fhBS, 1);
103
+ if ($info['dts']['flags']['crc_present']) {
104
+ $info['dts']['raw']['crc16'] = $this->readBinData($fhBS, 16);
105
+ }
106
+ $info['dts']['flags']['mri_perfect_reconst'] = (bool) $this->readBinData($fhBS, 1);
107
+ $info['dts']['raw']['encoder_soft_version'] = $this->readBinData($fhBS, 4);
108
+ $info['dts']['raw']['copy_history'] = $this->readBinData($fhBS, 2);
109
+ $info['dts']['raw']['bits_per_sample'] = $this->readBinData($fhBS, 2);
110
+ $info['dts']['flags']['surround_es'] = (bool) $this->readBinData($fhBS, 1);
111
+ $info['dts']['flags']['front_sum_diff'] = (bool) $this->readBinData($fhBS, 1);
112
+ $info['dts']['flags']['surround_sum_diff'] = (bool) $this->readBinData($fhBS, 1);
113
+ $info['dts']['raw']['dialog_normalization'] = $this->readBinData($fhBS, 4);
114
+
115
+
116
+ $info['dts']['bitrate'] = self::bitrateLookup($info['dts']['raw']['bitrate']);
117
+ $info['dts']['bits_per_sample'] = self::bitPerSampleLookup($info['dts']['raw']['bits_per_sample']);
118
+ $info['dts']['sample_rate'] = self::sampleRateLookup($info['dts']['raw']['sample_frequency']);
119
+ $info['dts']['dialog_normalization'] = self::dialogNormalization($info['dts']['raw']['dialog_normalization'], $info['dts']['raw']['encoder_soft_version']);
120
+ $info['dts']['flags']['lossless'] = (($info['dts']['raw']['bitrate'] == 31) ? true : false);
121
+ $info['dts']['bitrate_mode'] = (($info['dts']['raw']['bitrate'] == 30) ? 'vbr' : 'cbr');
122
+ $info['dts']['channels'] = self::numChannelsLookup($info['dts']['raw']['channel_arrangement']);
123
+ $info['dts']['channel_arrangement'] = self::channelArrangementLookup($info['dts']['raw']['channel_arrangement']);
124
+
125
+ $info['audio']['dataformat'] = 'dts';
126
+ $info['audio']['lossless'] = $info['dts']['flags']['lossless'];
127
+ $info['audio']['bitrate_mode'] = $info['dts']['bitrate_mode'];
128
+ $info['audio']['bits_per_sample'] = $info['dts']['bits_per_sample'];
129
+ $info['audio']['sample_rate'] = $info['dts']['sample_rate'];
130
+ $info['audio']['channels'] = $info['dts']['channels'];
131
+ $info['audio']['bitrate'] = $info['dts']['bitrate'];
132
+ if (isset($info['avdataend']) && !empty($info['dts']['bitrate']) && is_numeric($info['dts']['bitrate'])) {
133
+ $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($info['dts']['bitrate'] / 8);
134
+ if (($encoding == 2) || ($encoding == 3)) {
135
+ // 14-bit data packed into 16-bit words, so the playtime is wrong because only (14/16) of the bytes in the data portion of the file are used at the specified bitrate
136
+ $info['playtime_seconds'] *= (14 / 16);
137
+ }
138
+ }
139
+ return true;
140
+ }
141
+
142
+ private function readBinData($bin, $length) {
143
+ $data = substr($bin, $this->readBinDataOffset, $length);
144
+ $this->readBinDataOffset += $length;
145
+
146
+ return bindec($data);
147
+ }
148
+
149
+ public static function bitrateLookup($index) {
150
+ static $lookup = array(
151
+ 0 => 32000,
152
+ 1 => 56000,
153
+ 2 => 64000,
154
+ 3 => 96000,
155
+ 4 => 112000,
156
+ 5 => 128000,
157
+ 6 => 192000,
158
+ 7 => 224000,
159
+ 8 => 256000,
160
+ 9 => 320000,
161
+ 10 => 384000,
162
+ 11 => 448000,
163
+ 12 => 512000,
164
+ 13 => 576000,
165
+ 14 => 640000,
166
+ 15 => 768000,
167
+ 16 => 960000,
168
+ 17 => 1024000,
169
+ 18 => 1152000,
170
+ 19 => 1280000,
171
+ 20 => 1344000,
172
+ 21 => 1408000,
173
+ 22 => 1411200,
174
+ 23 => 1472000,
175
+ 24 => 1536000,
176
+ 25 => 1920000,
177
+ 26 => 2048000,
178
+ 27 => 3072000,
179
+ 28 => 3840000,
180
+ 29 => 'open',
181
+ 30 => 'variable',
182
+ 31 => 'lossless',
183
+ );
184
+ return (isset($lookup[$index]) ? $lookup[$index] : false);
185
+ }
186
+
187
+ public static function sampleRateLookup($index) {
188
+ static $lookup = array(
189
+ 0 => 'invalid',
190
+ 1 => 8000,
191
+ 2 => 16000,
192
+ 3 => 32000,
193
+ 4 => 'invalid',
194
+ 5 => 'invalid',
195
+ 6 => 11025,
196
+ 7 => 22050,
197
+ 8 => 44100,
198
+ 9 => 'invalid',
199
+ 10 => 'invalid',
200
+ 11 => 12000,
201
+ 12 => 24000,
202
+ 13 => 48000,
203
+ 14 => 'invalid',
204
+ 15 => 'invalid',
205
+ );
206
+ return (isset($lookup[$index]) ? $lookup[$index] : false);
207
+ }
208
+
209
+ public static function bitPerSampleLookup($index) {
210
+ static $lookup = array(
211
+ 0 => 16,
212
+ 1 => 20,
213
+ 2 => 24,
214
+ 3 => 24,
215
+ );
216
+ return (isset($lookup[$index]) ? $lookup[$index] : false);
217
+ }
218
+
219
+ public static function numChannelsLookup($index) {
220
+ switch ($index) {
221
+ case 0:
222
+ return 1;
223
+ break;
224
+ case 1:
225
+ case 2:
226
+ case 3:
227
+ case 4:
228
+ return 2;
229
+ break;
230
+ case 5:
231
+ case 6:
232
+ return 3;
233
+ break;
234
+ case 7:
235
+ case 8:
236
+ return 4;
237
+ break;
238
+ case 9:
239
+ return 5;
240
+ break;
241
+ case 10:
242
+ case 11:
243
+ case 12:
244
+ return 6;
245
+ break;
246
+ case 13:
247
+ return 7;
248
+ break;
249
+ case 14:
250
+ case 15:
251
+ return 8;
252
+ break;
253
+ }
254
+ return false;
255
+ }
256
+
257
+ public static function channelArrangementLookup($index) {
258
+ static $lookup = array(
259
+ 0 => 'A',
260
+ 1 => 'A + B (dual mono)',
261
+ 2 => 'L + R (stereo)',
262
+ 3 => '(L+R) + (L-R) (sum-difference)',
263
+ 4 => 'LT + RT (left and right total)',
264
+ 5 => 'C + L + R',
265
+ 6 => 'L + R + S',
266
+ 7 => 'C + L + R + S',
267
+ 8 => 'L + R + SL + SR',
268
+ 9 => 'C + L + R + SL + SR',
269
+ 10 => 'CL + CR + L + R + SL + SR',
270
+ 11 => 'C + L + R+ LR + RR + OV',
271
+ 12 => 'CF + CR + LF + RF + LR + RR',
272
+ 13 => 'CL + C + CR + L + R + SL + SR',
273
+ 14 => 'CL + CR + L + R + SL1 + SL2 + SR1 + SR2',
274
+ 15 => 'CL + C+ CR + L + R + SL + S + SR',
275
+ );
276
+ return (isset($lookup[$index]) ? $lookup[$index] : 'user-defined');
277
+ }
278
+
279
+ public static function dialogNormalization($index, $version) {
280
+ switch ($version) {
281
+ case 7:
282
+ return 0 - $index;
283
+ break;
284
+ case 6:
285
+ return 0 - 16 - $index;
286
+ break;
287
+ }
288
+ return false;
289
+ }
290
+
291
+ }
getid3/module.audio.flac.php CHANGED
@@ -1,443 +1,453 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- // also https://github.com/JamesHeinrich/getID3 //
7
- /////////////////////////////////////////////////////////////////
8
- // See readme.txt for more details //
9
- /////////////////////////////////////////////////////////////////
10
- // //
11
- // module.audio.flac.php //
12
- // module for analyzing FLAC and OggFLAC audio files //
13
- // dependencies: module.audio.ogg.php //
14
- // ///
15
- /////////////////////////////////////////////////////////////////
16
-
17
-
18
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
19
-
20
- /**
21
- * @tutorial http://flac.sourceforge.net/format.html
22
- */
23
- class getid3_flac extends getid3_handler
24
- {
25
- const syncword = 'fLaC';
26
-
27
- public function Analyze() {
28
- $info = &$this->getid3->info;
29
-
30
- $this->fseek($info['avdataoffset']);
31
- $StreamMarker = $this->fread(4);
32
- if ($StreamMarker != self::syncword) {
33
- return $this->error('Expecting "'.getid3_lib::PrintHexBytes(self::syncword).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($StreamMarker).'"');
34
- }
35
- $info['fileformat'] = 'flac';
36
- $info['audio']['dataformat'] = 'flac';
37
- $info['audio']['bitrate_mode'] = 'vbr';
38
- $info['audio']['lossless'] = true;
39
-
40
- // parse flac container
41
- return $this->parseMETAdata();
42
- }
43
-
44
- public function parseMETAdata() {
45
- $info = &$this->getid3->info;
46
- do {
47
- $BlockOffset = $this->ftell();
48
- $BlockHeader = $this->fread(4);
49
- $LBFBT = getid3_lib::BigEndian2Int(substr($BlockHeader, 0, 1));
50
- $LastBlockFlag = (bool) ($LBFBT & 0x80);
51
- $BlockType = ($LBFBT & 0x7F);
52
- $BlockLength = getid3_lib::BigEndian2Int(substr($BlockHeader, 1, 3));
53
- $BlockTypeText = self::metaBlockTypeLookup($BlockType);
54
-
55
- if (($BlockOffset + 4 + $BlockLength) > $info['avdataend']) {
56
- $this->error('METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockTypeText.') at offset '.$BlockOffset.' extends beyond end of file');
57
- break;
58
- }
59
- if ($BlockLength < 1) {
60
- $this->error('METADATA_BLOCK_HEADER.BLOCK_LENGTH ('.$BlockLength.') at offset '.$BlockOffset.' is invalid');
61
- break;
62
- }
63
-
64
- $info['flac'][$BlockTypeText]['raw'] = array();
65
- $BlockTypeText_raw = &$info['flac'][$BlockTypeText]['raw'];
66
-
67
- $BlockTypeText_raw['offset'] = $BlockOffset;
68
- $BlockTypeText_raw['last_meta_block'] = $LastBlockFlag;
69
- $BlockTypeText_raw['block_type'] = $BlockType;
70
- $BlockTypeText_raw['block_type_text'] = $BlockTypeText;
71
- $BlockTypeText_raw['block_length'] = $BlockLength;
72
- if ($BlockTypeText_raw['block_type'] != 0x06) { // do not read attachment data automatically
73
- $BlockTypeText_raw['block_data'] = $this->fread($BlockLength);
74
- }
75
-
76
- switch ($BlockTypeText) {
77
- case 'STREAMINFO': // 0x00
78
- if (!$this->parseSTREAMINFO($BlockTypeText_raw['block_data'])) {
79
- return false;
80
- }
81
- break;
82
-
83
- case 'PADDING': // 0x01
84
- unset($info['flac']['PADDING']); // ignore
85
- break;
86
-
87
- case 'APPLICATION': // 0x02
88
- if (!$this->parseAPPLICATION($BlockTypeText_raw['block_data'])) {
89
- return false;
90
- }
91
- break;
92
-
93
- case 'SEEKTABLE': // 0x03
94
- if (!$this->parseSEEKTABLE($BlockTypeText_raw['block_data'])) {
95
- return false;
96
- }
97
- break;
98
-
99
- case 'VORBIS_COMMENT': // 0x04
100
- if (!$this->parseVORBIS_COMMENT($BlockTypeText_raw['block_data'])) {
101
- return false;
102
- }
103
- break;
104
-
105
- case 'CUESHEET': // 0x05
106
- if (!$this->parseCUESHEET($BlockTypeText_raw['block_data'])) {
107
- return false;
108
- }
109
- break;
110
-
111
- case 'PICTURE': // 0x06
112
- if (!$this->parsePICTURE()) {
113
- return false;
114
- }
115
- break;
116
-
117
- default:
118
- $this->warning('Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockType.') at offset '.$BlockOffset);
119
- }
120
-
121
- unset($info['flac'][$BlockTypeText]['raw']);
122
- $info['avdataoffset'] = $this->ftell();
123
- }
124
- while ($LastBlockFlag === false);
125
-
126
- // handle tags
127
- if (!empty($info['flac']['VORBIS_COMMENT']['comments'])) {
128
- $info['flac']['comments'] = $info['flac']['VORBIS_COMMENT']['comments'];
129
- }
130
- if (!empty($info['flac']['VORBIS_COMMENT']['vendor'])) {
131
- $info['audio']['encoder'] = str_replace('reference ', '', $info['flac']['VORBIS_COMMENT']['vendor']);
132
- }
133
-
134
- // copy attachments to 'comments' array if nesesary
135
- if (isset($info['flac']['PICTURE']) && ($this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE)) {
136
- foreach ($info['flac']['PICTURE'] as $entry) {
137
- if (!empty($entry['data'])) {
138
- $info['flac']['comments']['picture'][] = array('image_mime'=>$entry['image_mime'], 'data'=>$entry['data']);
139
- }
140
- }
141
- }
142
-
143
- if (isset($info['flac']['STREAMINFO'])) {
144
- if (!$this->isDependencyFor('matroska')) {
145
- $info['flac']['compressed_audio_bytes'] = $info['avdataend'] - $info['avdataoffset'];
146
- }
147
- $info['flac']['uncompressed_audio_bytes'] = $info['flac']['STREAMINFO']['samples_stream'] * $info['flac']['STREAMINFO']['channels'] * ($info['flac']['STREAMINFO']['bits_per_sample'] / 8);
148
- if ($info['flac']['uncompressed_audio_bytes'] == 0) {
149
- return $this->error('Corrupt FLAC file: uncompressed_audio_bytes == zero');
150
- }
151
- if (!empty($info['flac']['compressed_audio_bytes'])) {
152
- $info['flac']['compression_ratio'] = $info['flac']['compressed_audio_bytes'] / $info['flac']['uncompressed_audio_bytes'];
153
- }
154
- }
155
-
156
- // set md5_data_source - built into flac 0.5+
157
- if (isset($info['flac']['STREAMINFO']['audio_signature'])) {
158
-
159
- if ($info['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
160
- $this->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
161
- }
162
- else {
163
- $info['md5_data_source'] = '';
164
- $md5 = $info['flac']['STREAMINFO']['audio_signature'];
165
- for ($i = 0; $i < strlen($md5); $i++) {
166
- $info['md5_data_source'] .= str_pad(dechex(ord($md5[$i])), 2, '00', STR_PAD_LEFT);
167
- }
168
- if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) {
169
- unset($info['md5_data_source']);
170
- }
171
- }
172
- }
173
-
174
- if (isset($info['flac']['STREAMINFO']['bits_per_sample'])) {
175
- $info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample'];
176
- if ($info['audio']['bits_per_sample'] == 8) {
177
- // special case
178
- // must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value
179
- // MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed
180
- $this->warning('FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file');
181
- }
182
- }
183
-
184
- return true;
185
- }
186
-
187
- private function parseSTREAMINFO($BlockData) {
188
- $info = &$this->getid3->info;
189
-
190
- $info['flac']['STREAMINFO'] = array();
191
- $streaminfo = &$info['flac']['STREAMINFO'];
192
-
193
- $streaminfo['min_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 0, 2));
194
- $streaminfo['max_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 2, 2));
195
- $streaminfo['min_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 4, 3));
196
- $streaminfo['max_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 7, 3));
197
-
198
- $SRCSBSS = getid3_lib::BigEndian2Bin(substr($BlockData, 10, 8));
199
- $streaminfo['sample_rate'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 0, 20));
200
- $streaminfo['channels'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 20, 3)) + 1;
201
- $streaminfo['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 23, 5)) + 1;
202
- $streaminfo['samples_stream'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 28, 36));
203
-
204
- $streaminfo['audio_signature'] = substr($BlockData, 18, 16);
205
-
206
- if (!empty($streaminfo['sample_rate'])) {
207
-
208
- $info['audio']['bitrate_mode'] = 'vbr';
209
- $info['audio']['sample_rate'] = $streaminfo['sample_rate'];
210
- $info['audio']['channels'] = $streaminfo['channels'];
211
- $info['audio']['bits_per_sample'] = $streaminfo['bits_per_sample'];
212
- $info['playtime_seconds'] = $streaminfo['samples_stream'] / $streaminfo['sample_rate'];
213
- if ($info['playtime_seconds'] > 0) {
214
- if (!$this->isDependencyFor('matroska')) {
215
- $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
216
- }
217
- else {
218
- $this->warning('Cannot determine audio bitrate because total stream size is unknown');
219
- }
220
- }
221
-
222
- } else {
223
- return $this->error('Corrupt METAdata block: STREAMINFO');
224
- }
225
-
226
- return true;
227
- }
228
-
229
- private function parseAPPLICATION($BlockData) {
230
- $info = &$this->getid3->info;
231
-
232
- $ApplicationID = getid3_lib::BigEndian2Int(substr($BlockData, 0, 4));
233
- $info['flac']['APPLICATION'][$ApplicationID]['name'] = self::applicationIDLookup($ApplicationID);
234
- $info['flac']['APPLICATION'][$ApplicationID]['data'] = substr($BlockData, 4);
235
-
236
- return true;
237
- }
238
-
239
- private function parseSEEKTABLE($BlockData) {
240
- $info = &$this->getid3->info;
241
-
242
- $offset = 0;
243
- $BlockLength = strlen($BlockData);
244
- $placeholderpattern = str_repeat("\xFF", 8);
245
- while ($offset < $BlockLength) {
246
- $SampleNumberString = substr($BlockData, $offset, 8);
247
- $offset += 8;
248
- if ($SampleNumberString == $placeholderpattern) {
249
-
250
- // placeholder point
251
- getid3_lib::safe_inc($info['flac']['SEEKTABLE']['placeholders'], 1);
252
- $offset += 10;
253
-
254
- } else {
255
-
256
- $SampleNumber = getid3_lib::BigEndian2Int($SampleNumberString);
257
- $info['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
258
- $offset += 8;
259
- $info['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 2));
260
- $offset += 2;
261
-
262
- }
263
- }
264
-
265
- return true;
266
- }
267
-
268
- private function parseVORBIS_COMMENT($BlockData) {
269
- $info = &$this->getid3->info;
270
-
271
- $getid3_ogg = new getid3_ogg($this->getid3);
272
- if ($this->isDependencyFor('matroska')) {
273
- $getid3_ogg->setStringMode($this->data_string);
274
- }
275
- $getid3_ogg->ParseVorbisComments();
276
- if (isset($info['ogg'])) {
277
- unset($info['ogg']['comments_raw']);
278
- $info['flac']['VORBIS_COMMENT'] = $info['ogg'];
279
- unset($info['ogg']);
280
- }
281
-
282
- unset($getid3_ogg);
283
-
284
- return true;
285
- }
286
-
287
- private function parseCUESHEET($BlockData) {
288
- $info = &$this->getid3->info;
289
- $offset = 0;
290
- $info['flac']['CUESHEET']['media_catalog_number'] = trim(substr($BlockData, $offset, 128), "\0");
291
- $offset += 128;
292
- $info['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
293
- $offset += 8;
294
- $info['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1)) & 0x80);
295
- $offset += 1;
296
-
297
- $offset += 258; // reserved
298
-
299
- $info['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
300
- $offset += 1;
301
-
302
- for ($track = 0; $track < $info['flac']['CUESHEET']['number_tracks']; $track++) {
303
- $TrackSampleOffset = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
304
- $offset += 8;
305
- $TrackNumber = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
306
- $offset += 1;
307
-
308
- $info['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset;
309
-
310
- $info['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($BlockData, $offset, 12);
311
- $offset += 12;
312
-
313
- $TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
314
- $offset += 1;
315
- $info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80);
316
- $info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40);
317
-
318
- $offset += 13; // reserved
319
-
320
- $info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
321
- $offset += 1;
322
-
323
- for ($index = 0; $index < $info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) {
324
- $IndexSampleOffset = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
325
- $offset += 8;
326
- $IndexNumber = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
327
- $offset += 1;
328
-
329
- $offset += 3; // reserved
330
-
331
- $info['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset;
332
- }
333
- }
334
-
335
- return true;
336
- }
337
-
338
- /**
339
- * Parse METADATA_BLOCK_PICTURE flac structure and extract attachment
340
- * External usage: audio.ogg
341
- */
342
- public function parsePICTURE() {
343
- $info = &$this->getid3->info;
344
-
345
- $picture['typeid'] = getid3_lib::BigEndian2Int($this->fread(4));
346
- $picture['type'] = self::pictureTypeLookup($picture['typeid']);
347
- $picture['image_mime'] = $this->fread(getid3_lib::BigEndian2Int($this->fread(4)));
348
- $descr_length = getid3_lib::BigEndian2Int($this->fread(4));
349
- if ($descr_length) {
350
- $picture['description'] = $this->fread($descr_length);
351
- }
352
- $picture['width'] = getid3_lib::BigEndian2Int($this->fread(4));
353
- $picture['height'] = getid3_lib::BigEndian2Int($this->fread(4));
354
- $picture['color_depth'] = getid3_lib::BigEndian2Int($this->fread(4));
355
- $picture['colors_indexed'] = getid3_lib::BigEndian2Int($this->fread(4));
356
- $data_length = getid3_lib::BigEndian2Int($this->fread(4));
357
-
358
- if ($picture['image_mime'] == '-->') {
359
- $picture['data'] = $this->fread($data_length);
360
- } else {
361
- $picture['data'] = $this->saveAttachment(
362
- str_replace('/', '_', $picture['type']).'_'.$this->ftell(),
363
- $this->ftell(),
364
- $data_length,
365
- $picture['image_mime']);
366
- }
367
-
368
- $info['flac']['PICTURE'][] = $picture;
369
-
370
- return true;
371
- }
372
-
373
- public static function metaBlockTypeLookup($blocktype) {
374
- static $lookup = array(
375
- 0 => 'STREAMINFO',
376
- 1 => 'PADDING',
377
- 2 => 'APPLICATION',
378
- 3 => 'SEEKTABLE',
379
- 4 => 'VORBIS_COMMENT',
380
- 5 => 'CUESHEET',
381
- 6 => 'PICTURE',
382
- );
383
- return (isset($lookup[$blocktype]) ? $lookup[$blocktype] : 'reserved');
384
- }
385
-
386
- public static function applicationIDLookup($applicationid) {
387
- // http://flac.sourceforge.net/id.html
388
- static $lookup = array(
389
- 0x41544348 => 'FlacFile', // "ATCH"
390
- 0x42534F4C => 'beSolo', // "BSOL"
391
- 0x42554753 => 'Bugs Player', // "BUGS"
392
- 0x43756573 => 'GoldWave cue points (specification)', // "Cues"
393
- 0x46696361 => 'CUE Splitter', // "Fica"
394
- 0x46746F6C => 'flac-tools', // "Ftol"
395
- 0x4D4F5442 => 'MOTB MetaCzar', // "MOTB"
396
- 0x4D505345 => 'MP3 Stream Editor', // "MPSE"
397
- 0x4D754D4C => 'MusicML: Music Metadata Language', // "MuML"
398
- 0x52494646 => 'Sound Devices RIFF chunk storage', // "RIFF"
399
- 0x5346464C => 'Sound Font FLAC', // "SFFL"
400
- 0x534F4E59 => 'Sony Creative Software', // "SONY"
401
- 0x5351455A => 'flacsqueeze', // "SQEZ"
402
- 0x54745776 => 'TwistedWave', // "TtWv"
403
- 0x55495453 => 'UITS Embedding tools', // "UITS"
404
- 0x61696666 => 'FLAC AIFF chunk storage', // "aiff"
405
- 0x696D6167 => 'flac-image application for storing arbitrary files in APPLICATION metadata blocks', // "imag"
406
- 0x7065656D => 'Parseable Embedded Extensible Metadata (specification)', // "peem"
407
- 0x71667374 => 'QFLAC Studio', // "qfst"
408
- 0x72696666 => 'FLAC RIFF chunk storage', // "riff"
409
- 0x74756E65 => 'TagTuner', // "tune"
410
- 0x78626174 => 'XBAT', // "xbat"
411
- 0x786D6364 => 'xmcd', // "xmcd"
412
- );
413
- return (isset($lookup[$applicationid]) ? $lookup[$applicationid] : 'reserved');
414
- }
415
-
416
- public static function pictureTypeLookup($type_id) {
417
- static $lookup = array (
418
- 0 => 'Other',
419
- 1 => '32x32 pixels \'file icon\' (PNG only)',
420
- 2 => 'Other file icon',
421
- 3 => 'Cover (front)',
422
- 4 => 'Cover (back)',
423
- 5 => 'Leaflet page',
424
- 6 => 'Media (e.g. label side of CD)',
425
- 7 => 'Lead artist/lead performer/soloist',
426
- 8 => 'Artist/performer',
427
- 9 => 'Conductor',
428
- 10 => 'Band/Orchestra',
429
- 11 => 'Composer',
430
- 12 => 'Lyricist/text writer',
431
- 13 => 'Recording Location',
432
- 14 => 'During recording',
433
- 15 => 'During performance',
434
- 16 => 'Movie/video screen capture',
435
- 17 => 'A bright coloured fish',
436
- 18 => 'Illustration',
437
- 19 => 'Band/artist logotype',
438
- 20 => 'Publisher/Studio logotype',
439
- );
440
- return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved');
441
- }
442
-
443
- }
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /////////////////////////////////////////////////////////////////
3
+ /// getID3() by James Heinrich <info@getid3.org> //
4
+ // available at http://getid3.sourceforge.net //
5
+ // or http://www.getid3.org //
6
+ // also https://github.com/JamesHeinrich/getID3 //
7
+ /////////////////////////////////////////////////////////////////
8
+ // See readme.txt for more details //
9
+ /////////////////////////////////////////////////////////////////
10
+ // //
11
+ // module.audio.flac.php //
12
+ // module for analyzing FLAC and OggFLAC audio files //
13
+ // dependencies: module.audio.ogg.php //
14
+ // ///
15
+ /////////////////////////////////////////////////////////////////
16
+
17
+
18
+ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
19
+
20
+ /**
21
+ * @tutorial http://flac.sourceforge.net/format.html
22
+ */
23
+ class getid3_flac extends getid3_handler
24
+ {
25
+ const syncword = 'fLaC';
26
+
27
+ public function Analyze() {
28
+ $info = &$this->getid3->info;
29
+
30
+ $this->fseek($info['avdataoffset']);
31
+ $StreamMarker = $this->fread(4);
32
+ if ($StreamMarker != self::syncword) {
33
+ return $this->error('Expecting "'.getid3_lib::PrintHexBytes(self::syncword).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($StreamMarker).'"');
34
+ }
35
+ $info['fileformat'] = 'flac';
36
+ $info['audio']['dataformat'] = 'flac';
37
+ $info['audio']['bitrate_mode'] = 'vbr';
38
+ $info['audio']['lossless'] = true;
39
+
40
+ // parse flac container
41
+ return $this->parseMETAdata();
42
+ }
43
+
44
+ public function parseMETAdata() {
45
+ $info = &$this->getid3->info;
46
+ do {
47
+ $BlockOffset = $this->ftell();
48
+ $BlockHeader = $this->fread(4);
49
+ $LBFBT = getid3_lib::BigEndian2Int(substr($BlockHeader, 0, 1));
50
+ $LastBlockFlag = (bool) ($LBFBT & 0x80);
51
+ $BlockType = ($LBFBT & 0x7F);
52
+ $BlockLength = getid3_lib::BigEndian2Int(substr($BlockHeader, 1, 3));
53
+ $BlockTypeText = self::metaBlockTypeLookup($BlockType);
54
+
55
+ if (($BlockOffset + 4 + $BlockLength) > $info['avdataend']) {
56
+ $this->error('METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockTypeText.') at offset '.$BlockOffset.' extends beyond end of file');
57
+ break;
58
+ }
59
+ if ($BlockLength < 1) {
60
+ $this->error('METADATA_BLOCK_HEADER.BLOCK_LENGTH ('.$BlockLength.') at offset '.$BlockOffset.' is invalid');
61
+ break;
62
+ }
63
+
64
+ $info['flac'][$BlockTypeText]['raw'] = array();
65
+ $BlockTypeText_raw = &$info['flac'][$BlockTypeText]['raw'];
66
+
67
+ $BlockTypeText_raw['offset'] = $BlockOffset;
68
+ $BlockTypeText_raw['last_meta_block'] = $LastBlockFlag;
69
+ $BlockTypeText_raw['block_type'] = $BlockType;
70
+ $BlockTypeText_raw['block_type_text'] = $BlockTypeText;
71
+ $BlockTypeText_raw['block_length'] = $BlockLength;
72
+ if ($BlockTypeText_raw['block_type'] != 0x06) { // do not read attachment data automatically
73
+ $BlockTypeText_raw['block_data'] = $this->fread($BlockLength);
74
+ }
75
+
76
+ switch ($BlockTypeText) {
77
+ case 'STREAMINFO': // 0x00
78
+ if (!$this->parseSTREAMINFO($BlockTypeText_raw['block_data'])) {
79
+ return false;
80
+ }
81
+ break;
82
+
83
+ case 'PADDING': // 0x01
84
+ unset($info['flac']['PADDING']); // ignore
85
+ break;
86
+
87
+ case 'APPLICATION': // 0x02
88
+ if (!$this->parseAPPLICATION($BlockTypeText_raw['block_data'])) {
89
+ return false;
90
+ }
91
+ break;
92
+
93
+ case 'SEEKTABLE': // 0x03
94
+ if (!$this->parseSEEKTABLE($BlockTypeText_raw['block_data'])) {
95
+ return false;
96
+ }
97
+ break;
98
+
99
+ case 'VORBIS_COMMENT': // 0x04
100
+ if (!$this->parseVORBIS_COMMENT($BlockTypeText_raw['block_data'])) {
101
+ return false;
102
+ }
103
+ break;
104
+
105
+ case 'CUESHEET': // 0x05
106
+ if (!$this->parseCUESHEET($BlockTypeText_raw['block_data'])) {
107
+ return false;
108
+ }
109
+ break;
110
+
111
+ case 'PICTURE': // 0x06
112
+ if (!$this->parsePICTURE()) {
113
+ return false;
114
+ }
115
+ break;
116
+
117
+ default:
118
+ $this->warning('Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockType.') at offset '.$BlockOffset);
119
+ }
120
+
121
+ unset($info['flac'][$BlockTypeText]['raw']);
122
+ $info['avdataoffset'] = $this->ftell();
123
+ }
124
+ while ($LastBlockFlag === false);
125
+
126
+ // handle tags
127
+ if (!empty($info['flac']['VORBIS_COMMENT']['comments'])) {
128
+ $info['flac']['comments'] = $info['flac']['VORBIS_COMMENT']['comments'];
129
+ }
130
+ if (!empty($info['flac']['VORBIS_COMMENT']['vendor'])) {
131
+ $info['audio']['encoder'] = str_replace('reference ', '', $info['flac']['VORBIS_COMMENT']['vendor']);
132
+ }
133
+
134
+ // copy attachments to 'comments' array if nesesary
135
+ if (isset($info['flac']['PICTURE']) && ($this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE)) {
136
+ foreach ($info['flac']['PICTURE'] as $entry) {
137
+ if (!empty($entry['data'])) {
138
+ if (!isset($info['flac']['comments']['picture'])) {
139
+ $info['flac']['comments']['picture'] = array();
140
+ }
141
+ $comments_picture_data = array();
142
+ foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
143
+ if (isset($entry[$picture_key])) {
144
+ $comments_picture_data[$picture_key] = $entry[$picture_key];
145
+ }
146
+ }
147
+ $info['flac']['comments']['picture'][] = $comments_picture_data;
148
+ unset($comments_picture_data);
149
+ }
150
+ }
151
+ }
152
+
153
+ if (isset($info['flac']['STREAMINFO'])) {
154
+ if (!$this->isDependencyFor('matroska')) {
155
+ $info['flac']['compressed_audio_bytes'] = $info['avdataend'] - $info['avdataoffset'];
156
+ }
157
+ $info['flac']['uncompressed_audio_bytes'] = $info['flac']['STREAMINFO']['samples_stream'] * $info['flac']['STREAMINFO']['channels'] * ($info['flac']['STREAMINFO']['bits_per_sample'] / 8);
158
+ if ($info['flac']['uncompressed_audio_bytes'] == 0) {
159
+ return $this->error('Corrupt FLAC file: uncompressed_audio_bytes == zero');
160
+ }
161
+ if (!empty($info['flac']['compressed_audio_bytes'])) {
162
+ $info['flac']['compression_ratio'] = $info['flac']['compressed_audio_bytes'] / $info['flac']['uncompressed_audio_bytes'];
163
+ }
164
+ }
165
+
166
+ // set md5_data_source - built into flac 0.5+
167
+ if (isset($info['flac']['STREAMINFO']['audio_signature'])) {
168
+
169
+ if ($info['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
170
+ $this->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
171
+ }
172
+ else {
173
+ $info['md5_data_source'] = '';
174
+ $md5 = $info['flac']['STREAMINFO']['audio_signature'];
175
+ for ($i = 0; $i < strlen($md5); $i++) {
176
+ $info['md5_data_source'] .= str_pad(dechex(ord($md5[$i])), 2, '00', STR_PAD_LEFT);
177
+ }
178
+ if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) {
179
+ unset($info['md5_data_source']);
180
+ }
181
+ }
182
+ }
183
+
184
+ if (isset($info['flac']['STREAMINFO']['bits_per_sample'])) {
185
+ $info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample'];
186
+ if ($info['audio']['bits_per_sample'] == 8) {
187
+ // special case
188
+ // must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value
189
+ // MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed
190
+ $this->warning('FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file');
191
+ }
192
+ }
193
+
194
+ return true;
195
+ }
196
+
197
+ private function parseSTREAMINFO($BlockData) {
198
+ $info = &$this->getid3->info;
199
+
200
+ $info['flac']['STREAMINFO'] = array();
201
+ $streaminfo = &$info['flac']['STREAMINFO'];
202
+
203
+ $streaminfo['min_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 0, 2));
204
+ $streaminfo['max_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 2, 2));
205
+ $streaminfo['min_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 4, 3));
206
+ $streaminfo['max_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 7, 3));
207
+
208
+ $SRCSBSS = getid3_lib::BigEndian2Bin(substr($BlockData, 10, 8));
209
+ $streaminfo['sample_rate'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 0, 20));
210
+ $streaminfo['channels'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 20, 3)) + 1;
211
+ $streaminfo['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 23, 5)) + 1;
212
+ $streaminfo['samples_stream'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 28, 36));
213
+
214
+ $streaminfo['audio_signature'] = substr($BlockData, 18, 16);
215
+
216
+ if (!empty($streaminfo['sample_rate'])) {
217
+
218
+ $info['audio']['bitrate_mode'] = 'vbr';
219
+ $info['audio']['sample_rate'] = $streaminfo['sample_rate'];
220
+ $info['audio']['channels'] = $streaminfo['channels'];
221
+ $info['audio']['bits_per_sample'] = $streaminfo['bits_per_sample'];
222
+ $info['playtime_seconds'] = $streaminfo['samples_stream'] / $streaminfo['sample_rate'];
223
+ if ($info['playtime_seconds'] > 0) {
224
+ if (!$this->isDependencyFor('matroska')) {
225
+ $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
226
+ }
227
+ else {
228
+ $this->warning('Cannot determine audio bitrate because total stream size is unknown');
229
+ }
230
+ }
231
+
232
+ } else {
233
+ return $this->error('Corrupt METAdata block: STREAMINFO');
234
+ }
235
+
236
+ return true;
237
+ }
238
+
239
+ private function parseAPPLICATION($BlockData) {
240
+ $info = &$this->getid3->info;
241
+
242
+ $ApplicationID = getid3_lib::BigEndian2Int(substr($BlockData, 0, 4));
243
+ $info['flac']['APPLICATION'][$ApplicationID]['name'] = self::applicationIDLookup($ApplicationID);
244
+ $info['flac']['APPLICATION'][$ApplicationID]['data'] = substr($BlockData, 4);
245
+
246
+ return true;
247
+ }
248
+
249
+ private function parseSEEKTABLE($BlockData) {
250
+ $info = &$this->getid3->info;
251
+
252
+ $offset = 0;
253
+ $BlockLength = strlen($BlockData);
254
+ $placeholderpattern = str_repeat("\xFF", 8);
255
+ while ($offset < $BlockLength) {
256
+ $SampleNumberString = substr($BlockData, $offset, 8);
257
+ $offset += 8;
258
+ if ($SampleNumberString == $placeholderpattern) {
259
+
260
+ // placeholder point
261
+ getid3_lib::safe_inc($info['flac']['SEEKTABLE']['placeholders'], 1);
262
+ $offset += 10;
263
+
264
+ } else {
265
+
266
+ $SampleNumber = getid3_lib::BigEndian2Int($SampleNumberString);
267
+ $info['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
268
+ $offset += 8;
269
+ $info['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 2));
270
+ $offset += 2;
271
+
272
+ }
273
+ }
274
+
275
+ return true;
276
+ }
277
+
278
+ private function parseVORBIS_COMMENT($BlockData) {
279
+ $info = &$this->getid3->info;
280
+
281
+ $getid3_ogg = new getid3_ogg($this->getid3);
282
+ if ($this->isDependencyFor('matroska')) {
283
+ $getid3_ogg->setStringMode($this->data_string);
284
+ }
285
+ $getid3_ogg->ParseVorbisComments();
286
+ if (isset($info['ogg'])) {
287
+ unset($info['ogg']['comments_raw']);
288
+ $info['flac']['VORBIS_COMMENT'] = $info['ogg'];
289
+ unset($info['ogg']);
290
+ }
291
+
292
+ unset($getid3_ogg);
293
+
294
+ return true;
295
+ }
296
+
297
+ private function parseCUESHEET($BlockData) {
298
+ $info = &$this->getid3->info;
299
+ $offset = 0;
300
+ $info['flac']['CUESHEET']['media_catalog_number'] = trim(substr($BlockData, $offset, 128), "\0");
301
+ $offset += 128;
302
+ $info['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
303
+ $offset += 8;
304
+ $info['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1)) & 0x80);
305
+ $offset += 1;
306
+
307
+ $offset += 258; // reserved
308
+
309
+ $info['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
310
+ $offset += 1;
311
+
312
+ for ($track = 0; $track < $info['flac']['CUESHEET']['number_tracks']; $track++) {
313
+ $TrackSampleOffset = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
314
+ $offset += 8;
315
+ $TrackNumber = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
316
+ $offset += 1;
317
+
318
+ $info['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset;
319
+
320
+ $info['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($BlockData, $offset, 12);
321
+ $offset += 12;
322
+
323
+ $TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
324
+ $offset += 1;
325
+ $info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80);
326
+ $info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40);
327
+
328
+ $offset += 13; // reserved
329
+
330
+ $info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
331
+ $offset += 1;
332
+
333
+ for ($index = 0; $index < $info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) {
334
+ $IndexSampleOffset = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
335
+ $offset += 8;
336
+ $IndexNumber = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
337
+ $offset += 1;
338
+
339
+ $offset += 3; // reserved
340
+
341
+ $info['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset;
342
+ }
343
+ }
344
+
345
+ return true;
346
+ }
347
+
348
+ /**
349
+ * Parse METADATA_BLOCK_PICTURE flac structure and extract attachment
350
+ * External usage: audio.ogg
351
+ */
352
+ public function parsePICTURE() {
353
+ $info = &$this->getid3->info;
354
+
355
+ $picture['typeid'] = getid3_lib::BigEndian2Int($this->fread(4));
356
+ $picture['picturetype'] = self::pictureTypeLookup($picture['typeid']);
357
+ $picture['image_mime'] = $this->fread(getid3_lib::BigEndian2Int($this->fread(4)));
358
+ $descr_length = getid3_lib::BigEndian2Int($this->fread(4));
359
+ if ($descr_length) {
360
+ $picture['description'] = $this->fread($descr_length);
361
+ }
362
+ $picture['image_width'] = getid3_lib::BigEndian2Int($this->fread(4));
363
+ $picture['image_height'] = getid3_lib::BigEndian2Int($this->fread(4));
364
+ $picture['color_depth'] = getid3_lib::BigEndian2Int($this->fread(4));
365
+ $picture['colors_indexed'] = getid3_lib::BigEndian2Int($this->fread(4));
366
+ $picture['datalength'] = getid3_lib::BigEndian2Int($this->fread(4));
367
+
368
+ if ($picture['image_mime'] == '-->') {
369
+ $picture['data'] = $this->fread($picture['datalength']);
370
+ } else {
371
+ $picture['data'] = $this->saveAttachment(
372
+ str_replace('/', '_', $picture['picturetype']).'_'.$this->ftell(),
373
+ $this->ftell(),
374
+ $picture['datalength'],
375
+ $picture['image_mime']);
376
+ }
377
+
378
+ $info['flac']['PICTURE'][] = $picture;
379
+
380
+ return true;
381
+ }
382
+
383
+ public static function metaBlockTypeLookup($blocktype) {
384
+ static $lookup = array(
385
+ 0 => 'STREAMINFO',
386
+ 1 => 'PADDING',
387
+ 2 => 'APPLICATION',
388
+ 3 => 'SEEKTABLE',
389
+ 4 => 'VORBIS_COMMENT',
390
+ 5 => 'CUESHEET',
391
+ 6 => 'PICTURE',
392
+ );
393
+ return (isset($lookup[$blocktype]) ? $lookup[$blocktype] : 'reserved');
394
+ }
395
+
396
+ public static function applicationIDLookup($applicationid) {
397
+ // http://flac.sourceforge.net/id.html
398
+ static $lookup = array(
399
+ 0x41544348 => 'FlacFile', // "ATCH"
400
+ 0x42534F4C => 'beSolo', // "BSOL"
401
+ 0x42554753 => 'Bugs Player', // "BUGS"
402
+ 0x43756573 => 'GoldWave cue points (specification)', // "Cues"
403
+ 0x46696361 => 'CUE Splitter', // "Fica"
404
+ 0x46746F6C => 'flac-tools', // "Ftol"
405
+ 0x4D4F5442 => 'MOTB MetaCzar', // "MOTB"
406
+ 0x4D505345 => 'MP3 Stream Editor', // "MPSE"
407
+ 0x4D754D4C => 'MusicML: Music Metadata Language', // "MuML"
408
+ 0x52494646 => 'Sound Devices RIFF chunk storage', // "RIFF"
409
+ 0x5346464C => 'Sound Font FLAC', // "SFFL"
410
+ 0x534F4E59 => 'Sony Creative Software', // "SONY"
411
+ 0x5351455A => 'flacsqueeze', // "SQEZ"
412
+ 0x54745776 => 'TwistedWave', // "TtWv"
413
+ 0x55495453 => 'UITS Embedding tools', // "UITS"
414
+ 0x61696666 => 'FLAC AIFF chunk storage', // "aiff"
415
+ 0x696D6167 => 'flac-image application for storing arbitrary files in APPLICATION metadata blocks', // "imag"
416
+ 0x7065656D => 'Parseable Embedded Extensible Metadata (specification)', // "peem"
417
+ 0x71667374 => 'QFLAC Studio', // "qfst"
418
+ 0x72696666 => 'FLAC RIFF chunk storage', // "riff"
419
+ 0x74756E65 => 'TagTuner', // "tune"
420
+ 0x78626174 => 'XBAT', // "xbat"
421
+ 0x786D6364 => 'xmcd', // "xmcd"
422
+ );
423
+ return (isset($lookup[$applicationid]) ? $lookup[$applicationid] : 'reserved');
424
+ }
425
+
426
+ public static function pictureTypeLookup($type_id) {
427
+ static $lookup = array (
428
+ 0 => 'Other',
429
+ 1 => '32x32 pixels \'file icon\' (PNG only)',
430
+ 2 => 'Other file icon',
431
+ 3 => 'Cover (front)',
432
+ 4 => 'Cover (back)',
433
+ 5 => 'Leaflet page',
434
+ 6 => 'Media (e.g. label side of CD)',
435
+ 7 => 'Lead artist/lead performer/soloist',
436
+ 8 => 'Artist/performer',
437
+ 9 => 'Conductor',
438
+ 10 => 'Band/Orchestra',
439
+ 11 => 'Composer',
440
+ 12 => 'Lyricist/text writer',
441
+ 13 => 'Recording Location',
442
+ 14 => 'During recording',
443
+ 15 => 'During performance',
444
+ 16 => 'Movie/video screen capture',
445
+ 17 => 'A bright coloured fish',
446
+ 18 => 'Illustration',
447
+ 19 => 'Band/artist logotype',
448
+ 20 => 'Publisher/Studio logotype',
449
+ );
450
+ return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved');
451
+ }
452
+
453
+ }
getid3/module.audio.mp3.php CHANGED
@@ -1,2012 +1,2022 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- // also https://github.com/JamesHeinrich/getID3 //
7
- /////////////////////////////////////////////////////////////////
8
- // See readme.txt for more details //
9
- /////////////////////////////////////////////////////////////////
10
- // //
11
- // module.audio.mp3.php //
12
- // module for analyzing MP3 files //
13
- // dependencies: NONE //
14
- // ///
15
- /////////////////////////////////////////////////////////////////
16
-
17
-
18
- // number of frames to scan to determine if MPEG-audio sequence is valid
19
- // Lower this number to 5-20 for faster scanning
20
- // Increase this number to 50+ for most accurate detection of valid VBR/CBR
21
- // mpeg-audio streams
22
- define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
23
-
24
-
25
- class getid3_mp3 extends getid3_handler
26
- {
27
-
28
- public $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files
29
-
30
- public function Analyze() {
31
- $info = &$this->getid3->info;
32
-
33
- $initialOffset = $info['avdataoffset'];
34
-
35
- if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) {
36
- if ($this->allow_bruteforce) {
37
- $info['error'][] = 'Rescanning file in BruteForce mode';
38
- $this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info);
39
- }
40
- }
41
-
42
-
43
- if (isset($info['mpeg']['audio']['bitrate_mode'])) {
44
- $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
45
- }
46
-
47
- if (((isset($info['id3v2']['headerlength']) && ($info['avdataoffset'] > $info['id3v2']['headerlength'])) || (!isset($info['id3v2']) && ($info['avdataoffset'] > 0) && ($info['avdataoffset'] != $initialOffset)))) {
48
-
49
- $synchoffsetwarning = 'Unknown data before synch ';
50
- if (isset($info['id3v2']['headerlength'])) {
51
- $synchoffsetwarning .= '(ID3v2 header ends at '.$info['id3v2']['headerlength'].', then '.($info['avdataoffset'] - $info['id3v2']['headerlength']).' bytes garbage, ';
52
- } elseif ($initialOffset > 0) {
53
- $synchoffsetwarning .= '(should be at '.$initialOffset.', ';
54
- } else {
55
- $synchoffsetwarning .= '(should be at beginning of file, ';
56
- }
57
- $synchoffsetwarning .= 'synch detected at '.$info['avdataoffset'].')';
58
- if (isset($info['audio']['bitrate_mode']) && ($info['audio']['bitrate_mode'] == 'cbr')) {
59
-
60
- if (!empty($info['id3v2']['headerlength']) && (($info['avdataoffset'] - $info['id3v2']['headerlength']) == $info['mpeg']['audio']['framelength'])) {
61
-
62
- $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.';
63
- $info['audio']['codec'] = 'LAME';
64
- $CurrentDataLAMEversionString = 'LAME3.';
65
-
66
- } elseif (empty($info['id3v2']['headerlength']) && ($info['avdataoffset'] == $info['mpeg']['audio']['framelength'])) {
67
-
68
- $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.';
69
- $info['audio']['codec'] = 'LAME';
70
- $CurrentDataLAMEversionString = 'LAME3.';
71
-
72
- }
73
-
74
- }
75
- $info['warning'][] = $synchoffsetwarning;
76
-
77
- }
78
-
79
- if (isset($info['mpeg']['audio']['LAME'])) {
80
- $info['audio']['codec'] = 'LAME';
81
- if (!empty($info['mpeg']['audio']['LAME']['long_version'])) {
82
- $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['long_version'], "\x00");
83
- } elseif (!empty($info['mpeg']['audio']['LAME']['short_version'])) {
84
- $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['short_version'], "\x00");
85
- }
86
- }
87
-
88
- $CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : (isset($info['audio']['encoder']) ? $info['audio']['encoder'] : ''));
89
- if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) {
90
- // a version number of LAME that does not end with a number like "LAME3.92"
91
- // or with a closing parenthesis like "LAME3.88 (alpha)"
92
- // or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92)
93
-
94
- // not sure what the actual last frame length will be, but will be less than or equal to 1441
95
- $PossiblyLongerLAMEversion_FrameLength = 1441;
96
-
97
- // Not sure what version of LAME this is - look in padding of last frame for longer version string
98
- $PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
99
- $this->fseek($PossibleLAMEversionStringOffset);
100
- $PossiblyLongerLAMEversion_Data = $this->fread($PossiblyLongerLAMEversion_FrameLength);
101
- switch (substr($CurrentDataLAMEversionString, -1)) {
102
- case 'a':
103
- case 'b':
104
- // "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example
105
- // need to trim off "a" to match longer string
106
- $CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1);
107
- break;
108
- }
109
- if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) {
110
- if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) {
111
- $PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3" "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)"
112
- if (empty($info['audio']['encoder']) || (strlen($PossiblyLongerLAMEversion_NewString) > strlen($info['audio']['encoder']))) {
113
- $info['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString;
114
- }
115
- }
116
- }
117
- }
118
- if (!empty($info['audio']['encoder'])) {
119
- $info['audio']['encoder'] = rtrim($info['audio']['encoder'], "\x00 ");
120
- }
121
-
122
- switch (isset($info['mpeg']['audio']['layer']) ? $info['mpeg']['audio']['layer'] : '') {
123
- case 1:
124
- case 2:
125
- $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
126
- break;
127
- }
128
- if (isset($info['fileformat']) && ($info['fileformat'] == 'mp3')) {
129
- switch ($info['audio']['dataformat']) {
130
- case 'mp1':
131
- case 'mp2':
132
- case 'mp3':
133
- $info['fileformat'] = $info['audio']['dataformat'];
134
- break;
135
-
136
- default:
137
- $info['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"';
138
- break;
139
- }
140
- }
141
-
142
- if (empty($info['fileformat'])) {
143
- unset($info['fileformat']);
144
- unset($info['audio']['bitrate_mode']);
145
- unset($info['avdataoffset']);
146
- unset($info['avdataend']);
147
- return false;
148
- }
149
-
150
- $info['mime_type'] = 'audio/mpeg';
151
- $info['audio']['lossless'] = false;
152
-
153
- // Calculate playtime
154
- if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
155
- $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
156
- }
157
-
158
- $info['audio']['encoder_options'] = $this->GuessEncoderOptions();
159
-
160
- return true;
161
- }
162
-
163
-
164
- public function GuessEncoderOptions() {
165
- // shortcuts
166
- $info = &$this->getid3->info;
167
- if (!empty($info['mpeg']['audio'])) {
168
- $thisfile_mpeg_audio = &$info['mpeg']['audio'];
169
- if (!empty($thisfile_mpeg_audio['LAME'])) {
170
- $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
171
- }
172
- }
173
-
174
- $encoder_options = '';
175
- static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256);
176
-
177
- if (isset($thisfile_mpeg_audio['VBR_method']) && ($thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {
178
-
179
- $encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
180
-
181
- } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
182
-
183
- $encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
184
-
185
- } elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {
186
-
187
- static $KnownEncoderValues = array();
188
- if (empty($KnownEncoderValues)) {
189
-
190
- //$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name';
191
- $KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane'; // 3.90, 3.90.1, 3.92
192
- $KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane'; // 3.90.2, 3.90.3, 3.91
193
- $KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane'; // 3.94, 3.95
194
- $KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme'; // 3.90, 3.90.1, 3.92
195
- $KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme'; // 3.90.2, 3.91
196
- $KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme'; // 3.90.3
197
- $KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme'; // 3.90, 3.90.1, 3.92
198
- $KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme'; // 3.90.2, 3.90.3, 3.91
199
- $KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
200
- $KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard'; // 3.90.3
201
- $KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
202
- $KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3
203
- $KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix'; // 3.90, 3.90.1, 3.92
204
- $KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix'; // 3.90.2, 3.90.3, 3.91
205
- $KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix'; // 3.94, 3.95
206
- $KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium'; // 3.90.3
207
- $KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium'; // 3.90.3
208
-
209
- $KnownEncoderValues[0xFF][99][1][1][1][2][0] = '--preset studio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
210
- $KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio'; // 3.90.3, 3.93.1
211
- $KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio'; // 3.93
212
- $KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio'; // 3.94, 3.95
213
- $KnownEncoderValues[0xC0][88][1][1][1][2][0] = '--preset cd'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
214
- $KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd'; // 3.90.3, 3.93.1
215
- $KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd'; // 3.93
216
- $KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd'; // 3.94, 3.95
217
- $KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
218
- $KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi'; // 3.90.3, 3.93, 3.93.1
219
- $KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi'; // 3.94, 3.95
220
- $KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
221
- $KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
222
- $KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
223
- $KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm'; // 3.90.3, 3.93, 3.93.1
224
- $KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm'; // 3.94, 3.95
225
- $KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice'; // 3.90.3, 3.93, 3.93.1
226
- $KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice'; // 3.94, 3.95
227
- $KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice'; // 3.94a14
228
- $KnownEncoderValues[0x28][65][1][1][0][2][7500] = '--preset mw-us'; // 3.90, 3.90.1, 3.92
229
- $KnownEncoderValues[0x28][65][1][1][0][2][7600] = '--preset mw-us'; // 3.90.2, 3.91
230
- $KnownEncoderValues[0x28][58][2][2][0][2][7000] = '--preset mw-us'; // 3.90.3, 3.93, 3.93.1
231
- $KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us'; // 3.94, 3.95
232
- $KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us'; // 3.94a14
233
- $KnownEncoderValues[0x28][57][2][1][0][4][8800] = '--preset mw-us'; // 3.94a15
234
- $KnownEncoderValues[0x18][58][2][2][0][2][4000] = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1
235
- $KnownEncoderValues[0x18][58][2][2][0][2][3900] = '--preset phon+/lw/mw-eu/sw'; // 3.93
236
- $KnownEncoderValues[0x18][57][2][1][0][4][5900] = '--preset phon+/lw/mw-eu/sw'; // 3.94, 3.95
237
- $KnownEncoderValues[0x18][57][2][1][0][4][6200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a14
238
- $KnownEncoderValues[0x18][57][2][1][0][4][3200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a15
239
- $KnownEncoderValues[0x10][58][2][2][0][2][3800] = '--preset phone'; // 3.90.3, 3.93.1
240
- $KnownEncoderValues[0x10][58][2][2][0][2][3700] = '--preset phone'; // 3.93
241
- $KnownEncoderValues[0x10][57][2][1][0][4][5600] = '--preset phone'; // 3.94, 3.95
242
- }
243
-
244
- if (isset($KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
245
-
246
- $encoder_options = $KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
247
-
248
- } elseif (isset($KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
249
-
250
- $encoder_options = $KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
251
-
252
- } elseif ($info['audio']['bitrate_mode'] == 'vbr') {
253
-
254
- // http://gabriel.mp3-tech.org/mp3infotag.html
255
- // int Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h
256
-
257
-
258
- $LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10);
259
- $LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10);
260
- $encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value;
261
-
262
- } elseif ($info['audio']['bitrate_mode'] == 'cbr') {
263
-
264
- $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
265
-
266
- } else {
267
-
268
- $encoder_options = strtoupper($info['audio']['bitrate_mode']);
269
-
270
- }
271
-
272
- } elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) {
273
-
274
- $encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr'];
275
-
276
- } elseif (!empty($info['audio']['bitrate'])) {
277
-
278
- if ($info['audio']['bitrate_mode'] == 'cbr') {
279
- $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
280
- } else {
281
- $encoder_options = strtoupper($info['audio']['bitrate_mode']);
282
- }
283
-
284
- }
285
- if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) {
286
- $encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min'];
287
- }
288
-
289
- if (!empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']) || !empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'])) {
290
- $encoder_options .= ' --nogap';
291
- }
292
-
293
- if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) {
294
- $ExplodedOptions = explode(' ', $encoder_options, 4);
295
- if ($ExplodedOptions[0] == '--r3mix') {
296
- $ExplodedOptions[1] = 'r3mix';
297
- }
298
- switch ($ExplodedOptions[0]) {
299
- case '--preset':
300
- case '--alt-preset':
301
- case '--r3mix':
302
- if ($ExplodedOptions[1] == 'fast') {
303
- $ExplodedOptions[1] .= ' '.$ExplodedOptions[2];
304
- }
305
- switch ($ExplodedOptions[1]) {
306
- case 'portable':
307
- case 'medium':
308
- case 'standard':
309
- case 'extreme':
310
- case 'insane':
311
- case 'fast portable':
312
- case 'fast medium':
313
- case 'fast standard':
314
- case 'fast extreme':
315
- case 'fast insane':
316
- case 'r3mix':
317
- static $ExpectedLowpass = array(
318
- 'insane|20500' => 20500,
319
- 'insane|20600' => 20600, // 3.90.2, 3.90.3, 3.91
320
- 'medium|18000' => 18000,
321
- 'fast medium|18000' => 18000,
322
- 'extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95
323
- 'extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1
324
- 'fast extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95
325
- 'fast extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1
326
- 'standard|19000' => 19000,
327
- 'fast standard|19000' => 19000,
328
- 'r3mix|19500' => 19500, // 3.90, 3.90.1, 3.92
329
- 'r3mix|19600' => 19600, // 3.90.2, 3.90.3, 3.91
330
- 'r3mix|18000' => 18000, // 3.94, 3.95
331
- );
332
- if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) {
333
- $encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency'];
334
- }
335
- break;
336
-
337
- default:
338
- break;
339
- }
340
- break;
341
- }
342
- }
343
-
344
- if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) {
345
- if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) {
346
- $encoder_options .= ' --resample 44100';
347
- } elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) {
348
- $encoder_options .= ' --resample 48000';
349
- } elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) {
350
- switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) {
351
- case 0: // <= 32000
352
- // may or may not be same as source frequency - ignore
353
- break;
354
- case 1: // 44100
355
- case 2: // 48000
356
- case 3: // 48000+
357
- $ExplodedOptions = explode(' ', $encoder_options, 4);
358
- switch ($ExplodedOptions[0]) {
359
- case '--preset':
360
- case '--alt-preset':
361
- switch ($ExplodedOptions[1]) {
362
- case 'fast':
363
- case 'portable':
364
- case 'medium':
365
- case 'standard':
366
- case 'extreme':
367
- case 'insane':
368
- $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
369
- break;
370
-
371
- default:
372
- static $ExpectedResampledRate = array(
373
- 'phon+/lw/mw-eu/sw|16000' => 16000,
374
- 'mw-us|24000' => 24000, // 3.95
375
- 'mw-us|32000' => 32000, // 3.93
376
- 'mw-us|16000' => 16000, // 3.92
377
- 'phone|16000' => 16000,
378
- 'phone|11025' => 11025, // 3.94a15
379
- 'radio|32000' => 32000, // 3.94a15
380
- 'fm/radio|32000' => 32000, // 3.92
381
- 'fm|32000' => 32000, // 3.90
382
- 'voice|32000' => 32000);
383
- if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) {
384
- $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
385
- }
386
- break;
387
- }
388
- break;
389
-
390
- case '--r3mix':
391
- default:
392
- $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
393
- break;
394
- }
395
- break;
396
- }
397
- }
398
- }
399
- if (empty($encoder_options) && !empty($info['audio']['bitrate']) && !empty($info['audio']['bitrate_mode'])) {
400
- //$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
401
- $encoder_options = strtoupper($info['audio']['bitrate_mode']);
402
- }
403
-
404
- return $encoder_options;
405
- }
406
-
407
-
408
- public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
409
- static $MPEGaudioVersionLookup;
410
- static $MPEGaudioLayerLookup;
411
- static $MPEGaudioBitrateLookup;
412
- static $MPEGaudioFrequencyLookup;
413
- static $MPEGaudioChannelModeLookup;
414
- static $MPEGaudioModeExtensionLookup;
415
- static $MPEGaudioEmphasisLookup;
416
- if (empty($MPEGaudioVersionLookup)) {
417
- $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
418
- $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
419
- $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
420
- $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray();
421
- $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray();
422
- $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
423
- $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray();
424
- }
425
-
426
- if ($this->fseek($offset) != 0) {
427
- $info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset;
428
- return false;
429
- }
430
- //$headerstring = $this->fread(1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
431
- $headerstring = $this->fread(226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
432
-
433
- // MP3 audio frame structure:
434
- // $aa $aa $aa $aa [$bb $bb] $cc...
435
- // where $aa..$aa is the four-byte mpeg-audio header (below)
436
- // $bb $bb is the optional 2-byte CRC
437
- // and $cc... is the audio data
438
-
439
- $head4 = substr($headerstring, 0, 4);
440
-
441
- static $MPEGaudioHeaderDecodeCache = array();
442
- if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
443
- $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
444
- } else {
445
- $MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4);
446
- $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
447
- }
448
-
449
- static $MPEGaudioHeaderValidCache = array();
450
- if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache
451
- //$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
452
- $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false);
453
- }
454
-
455
- // shortcut
456
- if (!isset($info['mpeg']['audio'])) {
457
- $info['mpeg']['audio'] = array();
458
- }
459
- $thisfile_mpeg_audio = &$info['mpeg']['audio'];
460
-
461
-
462
- if ($MPEGaudioHeaderValidCache[$head4]) {
463
- $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
464
- } else {
465
- $info['error'][] = 'Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset;
466
- return false;
467
- }
468
-
469
- if (!$FastMPEGheaderScan) {
470
- $thisfile_mpeg_audio['version'] = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']];
471
- $thisfile_mpeg_audio['layer'] = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']];
472
-
473
- $thisfile_mpeg_audio['channelmode'] = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']];
474
- $thisfile_mpeg_audio['channels'] = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2);
475
- $thisfile_mpeg_audio['sample_rate'] = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']];
476
- $thisfile_mpeg_audio['protection'] = !$thisfile_mpeg_audio['raw']['protection'];
477
- $thisfile_mpeg_audio['private'] = (bool) $thisfile_mpeg_audio['raw']['private'];
478
- $thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']];
479
- $thisfile_mpeg_audio['copyright'] = (bool) $thisfile_mpeg_audio['raw']['copyright'];
480
- $thisfile_mpeg_audio['original'] = (bool) $thisfile_mpeg_audio['raw']['original'];
481
- $thisfile_mpeg_audio['emphasis'] = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']];
482
-
483
- $info['audio']['channels'] = $thisfile_mpeg_audio['channels'];
484
- $info['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate'];
485
-
486
- if ($thisfile_mpeg_audio['protection']) {
487
- $thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2));
488
- }
489
- }
490
-
491
- if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) {
492
- // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
493
- $info['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1';
494
- $thisfile_mpeg_audio['raw']['bitrate'] = 0;
495
- }
496
- $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding'];
497
- $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']];
498
-
499
- if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $info['avdataoffset'])) {
500
- // only skip multiple frame check if free-format bitstream found at beginning of file
501
- // otherwise is quite possibly simply corrupted data
502
- $recursivesearch = false;
503
- }
504
-
505
- // For Layer 2 there are some combinations of bitrate and mode which are not allowed.
506
- if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) {
507
-
508
- $info['audio']['dataformat'] = 'mp2';
509
- switch ($thisfile_mpeg_audio['channelmode']) {
510
-
511
- case 'mono':
512
- if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) {
513
- // these are ok
514
- } else {
515
- $info['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
516
- return false;
517
- }
518
- break;
519
-
520
- case 'stereo':
521
- case 'joint stereo':
522
- case 'dual channel':
523
- if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) {
524
- // these are ok
525
- } else {
526
- $info['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
527
- return false;
528
- }
529
- break;
530
-
531
- }
532
-
533
- }
534
-
535
-
536
- if ($info['audio']['sample_rate'] > 0) {
537
- $thisfile_mpeg_audio['framelength'] = self::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $info['audio']['sample_rate']);
538
- }
539
-
540
- $nextframetestoffset = $offset + 1;
541
- if ($thisfile_mpeg_audio['bitrate'] != 'free') {
542
-
543
- $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
544
-
545
- if (isset($thisfile_mpeg_audio['framelength'])) {
546
- $nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength'];
547
- } else {
548
- $info['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.';
549
- return false;
550
- }
551
-
552
- }
553
-
554
- $ExpectedNumberOfAudioBytes = 0;
555
-
556
- ////////////////////////////////////////////////////////////////////////////////////
557
- // Variable-bitrate headers
558
-
559
- if (substr($headerstring, 4 + 32, 4) == 'VBRI') {
560
- // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
561
- // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
562
-
563
- $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
564
- $thisfile_mpeg_audio['VBR_method'] = 'Fraunhofer';
565
- $info['audio']['codec'] = 'Fraunhofer';
566
-
567
- $SideInfoData = substr($headerstring, 4 + 2, 32);
568
-
569
- $FraunhoferVBROffset = 36;
570
-
571
- $thisfile_mpeg_audio['VBR_encoder_version'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 4, 2)); // VbriVersion
572
- $thisfile_mpeg_audio['VBR_encoder_delay'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 6, 2)); // VbriDelay
573
- $thisfile_mpeg_audio['VBR_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 8, 2)); // VbriQuality
574
- $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes
575
- $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames
576
- $thisfile_mpeg_audio['VBR_seek_offsets'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize
577
- $thisfile_mpeg_audio['VBR_seek_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale
578
- $thisfile_mpeg_audio['VBR_entry_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes
579
- $thisfile_mpeg_audio['VBR_entry_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames
580
-
581
- $ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes'];
582
-
583
- $previousbyteoffset = $offset;
584
- for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) {
585
- $Fraunhofer_OffsetN = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes']));
586
- $FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes'];
587
- $thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']);
588
- $thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset;
589
- $previousbyteoffset += $Fraunhofer_OffsetN;
590
- }
591
-
592
-
593
- } else {
594
-
595
- // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
596
- // depending on MPEG layer and number of channels
597
-
598
- $VBRidOffset = self::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']);
599
- $SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4);
600
-
601
- if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) {
602
- // 'Xing' is traditional Xing VBR frame
603
- // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
604
- // 'Info' *can* legally be used to specify a VBR file as well, however.
605
-
606
- // http://www.multiweb.cz/twoinches/MP3inside.htm
607
- //00..03 = "Xing" or "Info"
608
- //04..07 = Flags:
609
- // 0x01 Frames Flag set if value for number of frames in file is stored
610
- // 0x02 Bytes Flag set if value for filesize in bytes is stored
611
- // 0x04 TOC Flag set if values for TOC are stored
612
- // 0x08 VBR Scale Flag set if values for VBR scale is stored
613
- //08..11 Frames: Number of frames in file (including the first Xing/Info one)
614
- //12..15 Bytes: File length in Bytes
615
- //16..115 TOC (Table of Contents):
616
- // Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file.
617
- // Each Byte has a value according this formula:
618
- // (TOC[i] / 256) * fileLenInBytes
619
- // So if song lasts eg. 240 sec. and you want to jump to 60. sec. (and file is 5 000 000 Bytes length) you can use:
620
- // TOC[(60/240)*100] = TOC[25]
621
- // and corresponding Byte in file is then approximately at:
622
- // (TOC[25]/256) * 5000000
623
- //116..119 VBR Scale
624
-
625
-
626
- // should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME
627
- // if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') {
628
- $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
629
- $thisfile_mpeg_audio['VBR_method'] = 'Xing';
630
- // } else {
631
- // $ScanAsCBR = true;
632
- // $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
633
- // }
634
-
635
- $thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4));
636
-
637
- $thisfile_mpeg_audio['xing_flags']['frames'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001);
638
- $thisfile_mpeg_audio['xing_flags']['bytes'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002);
639
- $thisfile_mpeg_audio['xing_flags']['toc'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004);
640
- $thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008);
641
-
642
- if ($thisfile_mpeg_audio['xing_flags']['frames']) {
643
- $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 8, 4));
644
- //$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame
645
- }
646
- if ($thisfile_mpeg_audio['xing_flags']['bytes']) {
647
- $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4));
648
- }
649
-
650
- //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
651
- if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
652
-
653
- $framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames'];
654
-
655
- if ($thisfile_mpeg_audio['layer'] == '1') {
656
- // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
657
- //$info['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
658
- $info['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12;
659
- } else {
660
- // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
661
- //$info['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
662
- $info['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144;
663
- }
664
- $thisfile_mpeg_audio['framelength'] = floor($framelengthfloat);
665
- }
666
-
667
- if ($thisfile_mpeg_audio['xing_flags']['toc']) {
668
- $LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
669
- for ($i = 0; $i < 100; $i++) {
670
- $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
671
- }
672
- }
673
- if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
674
- $thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
675
- }
676
-
677
-
678
- // http://gabriel.mp3-tech.org/mp3infotag.html
679
- if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') {
680
-
681
- // shortcut
682
- $thisfile_mpeg_audio['LAME'] = array();
683
- $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
684
-
685
-
686
- $thisfile_mpeg_audio_lame['long_version'] = substr($headerstring, $VBRidOffset + 120, 20);
687
- $thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
688
-
689
- if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
690
-
691
- // extra 11 chars are not part of version string when LAMEtag present
692
- unset($thisfile_mpeg_audio_lame['long_version']);
693
-
694
- // It the LAME tag was only introduced in LAME v3.90
695
- // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
696
-
697
- // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
698
- // are assuming a 'Xing' identifier offset of 0x24, which is the case for
699
- // MPEG-1 non-mono, but not for other combinations
700
- $LAMEtagOffsetContant = $VBRidOffset - 0x24;
701
-
702
- // shortcuts
703
- $thisfile_mpeg_audio_lame['RGAD'] = array('track'=>array(), 'album'=>array());
704
- $thisfile_mpeg_audio_lame_RGAD = &$thisfile_mpeg_audio_lame['RGAD'];
705
- $thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track'];
706
- $thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album'];
707
- $thisfile_mpeg_audio_lame['raw'] = array();
708
- $thisfile_mpeg_audio_lame_raw = &$thisfile_mpeg_audio_lame['raw'];
709
-
710
- // byte $9B VBR Quality
711
- // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
712
- // Actually overwrites original Xing bytes
713
- unset($thisfile_mpeg_audio['VBR_scale']);
714
- $thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1));
715
-
716
- // bytes $9C-$A4 Encoder short VersionString
717
- $thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9);
718
-
719
- // byte $A5 Info Tag revision + VBR method
720
- $LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1));
721
-
722
- $thisfile_mpeg_audio_lame['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
723
- $thisfile_mpeg_audio_lame_raw['vbr_method'] = $LAMEtagRevisionVBRmethod & 0x0F;
724
- $thisfile_mpeg_audio_lame['vbr_method'] = self::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
725
- $thisfile_mpeg_audio['bitrate_mode'] = substr($thisfile_mpeg_audio_lame['vbr_method'], 0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr'
726
-
727
- // byte $A6 Lowpass filter value
728
- $thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100;
729
-
730
- // bytes $A7-$AE Replay Gain
731
- // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
732
- // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
733
- if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') {
734
- // LAME 3.94a16 and later - 9.23 fixed point
735
- // ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375
736
- $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608);
737
- } else {
738
- // LAME 3.94a15 and earlier - 32-bit floating point
739
- // Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15
740
- $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4));
741
- }
742
- if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) {
743
- unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
744
- } else {
745
- $thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
746
- }
747
-
748
- $thisfile_mpeg_audio_lame_raw['RGAD_track'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2));
749
- $thisfile_mpeg_audio_lame_raw['RGAD_album'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2));
750
-
751
-
752
- if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) {
753
-
754
- $thisfile_mpeg_audio_lame_RGAD_track['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13;
755
- $thisfile_mpeg_audio_lame_RGAD_track['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10;
756
- $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9;
757
- $thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF;
758
- $thisfile_mpeg_audio_lame_RGAD_track['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']);
759
- $thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']);
760
- $thisfile_mpeg_audio_lame_RGAD_track['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']);
761
-
762
- if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
763
- $info['replay_gain']['track']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
764
- }
765
- $info['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator'];
766
- $info['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db'];
767
- } else {
768
- unset($thisfile_mpeg_audio_lame_RGAD['track']);
769
- }
770
- if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) {
771
-
772
- $thisfile_mpeg_audio_lame_RGAD_album['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13;
773
- $thisfile_mpeg_audio_lame_RGAD_album['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10;
774
- $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9;
775
- $thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF;
776
- $thisfile_mpeg_audio_lame_RGAD_album['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']);
777
- $thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']);
778
- $thisfile_mpeg_audio_lame_RGAD_album['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']);
779
-
780
- if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
781
- $info['replay_gain']['album']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
782
- }
783
- $info['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator'];
784
- $info['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db'];
785
- } else {
786
- unset($thisfile_mpeg_audio_lame_RGAD['album']);
787
- }
788
- if (empty($thisfile_mpeg_audio_lame_RGAD)) {
789
- unset($thisfile_mpeg_audio_lame['RGAD']);
790
- }
791
-
792
-
793
- // byte $AF Encoding flags + ATH Type
794
- $EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1));
795
- $thisfile_mpeg_audio_lame['encoding_flags']['nspsytune'] = (bool) ($EncodingFlagsATHtype & 0x10);
796
- $thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20);
797
- $thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'] = (bool) ($EncodingFlagsATHtype & 0x40);
798
- $thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] = (bool) ($EncodingFlagsATHtype & 0x80);
799
- $thisfile_mpeg_audio_lame['ath_type'] = $EncodingFlagsATHtype & 0x0F;
800
-
801
- // byte $B0 if ABR {specified bitrate} else {minimal bitrate}
802
- $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1));
803
- if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR)
804
- $thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
805
- } elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR)
806
- // ignore
807
- } elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate
808
- $thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
809
- }
810
-
811
- // bytes $B1-$B3 Encoder delays
812
- $EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3));
813
- $thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12;
814
- $thisfile_mpeg_audio_lame['end_padding'] = $EncoderDelays & 0x000FFF;
815
-
816
- // byte $B4 Misc
817
- $MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1));
818
- $thisfile_mpeg_audio_lame_raw['noise_shaping'] = ($MiscByte & 0x03);
819
- $thisfile_mpeg_audio_lame_raw['stereo_mode'] = ($MiscByte & 0x1C) >> 2;
820
- $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
821
- $thisfile_mpeg_audio_lame_raw['source_sample_freq'] = ($MiscByte & 0xC0) >> 6;
822
- $thisfile_mpeg_audio_lame['noise_shaping'] = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
823
- $thisfile_mpeg_audio_lame['stereo_mode'] = self::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
824
- $thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
825
- $thisfile_mpeg_audio_lame['source_sample_freq'] = self::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);
826
-
827
- // byte $B5 MP3 Gain
828
- $thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true);
829
- $thisfile_mpeg_audio_lame['mp3_gain_db'] = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
830
- $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));
831
-
832
- // bytes $B6-$B7 Preset and surround info
833
- $PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2));
834
- // Reserved = ($PresetSurroundBytes & 0xC000);
835
- $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
836
- $thisfile_mpeg_audio_lame['surround_info'] = self::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
837
- $thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF);
838
- $thisfile_mpeg_audio_lame['preset_used'] = self::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
839
- if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
840
- $info['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org';
841
- }
842
- if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
843
- // this may change if 3.90.4 ever comes out
844
- $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
845
- }
846
-
847
- // bytes $B8-$BB MusicLength
848
- $thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4));
849
- $ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);
850
-
851
- // bytes $BC-$BD MusicCRC
852
- $thisfile_mpeg_audio_lame['music_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2));
853
-
854
- // bytes $BE-$BF CRC-16 of Info Tag
855
- $thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2));
856
-
857
-
858
- // LAME CBR
859
- if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) {
860
-
861
- $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
862
- $thisfile_mpeg_audio['bitrate'] = self::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
863
- $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
864
- //if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) {
865
- // $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min'];
866
- //}
867
-
868
- }
869
-
870
- }
871
- }
872
-
873
- } else {
874
-
875
- // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
876
- $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
877
- if ($recursivesearch) {
878
- $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
879
- if ($this->RecursiveFrameScanning($offset, $nextframetestoffset, true)) {
880
- $recursivesearch = false;
881
- $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
882
- }
883
- if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') {
884
- $info['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.';
885
- }
886
- }
887
-
888
- }
889
-
890
- }
891
-
892
- if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) {
893
- if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) {
894
- if ($this->isDependencyFor('matroska') || $this->isDependencyFor('riff')) {
895
- // ignore, audio data is broken into chunks so will always be data "missing"
896
- }
897
- elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) {
898
- $this->warning('Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)');
899
- }
900
- else {
901
- $this->warning('Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)');
902
- }
903
- } else {
904
- if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) {
905
- // $prenullbytefileoffset = $this->ftell();
906
- // $this->fseek($info['avdataend']);
907
- // $PossibleNullByte = $this->fread(1);
908
- // $this->fseek($prenullbytefileoffset);
909
- // if ($PossibleNullByte === "\x00") {
910
- $info['avdataend']--;
911
- // $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
912
- // } else {
913
- // $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
914
- // }
915
- } else {
916
- $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
917
- }
918
- }
919
- }
920
-
921
- if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) {
922
- if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) {
923
- $framebytelength = $this->FreeFormatFrameLength($offset, true);
924
- if ($framebytelength > 0) {
925
- $thisfile_mpeg_audio['framelength'] = $framebytelength;
926
- if ($thisfile_mpeg_audio['layer'] == '1') {
927
- // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
928
- $info['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
929
- } else {
930
- // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
931
- $info['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
932
- }
933
- } else {
934
- $info['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header';
935
- }
936
- }
937
- }
938
-
939
- if (isset($thisfile_mpeg_audio['VBR_frames']) ? $thisfile_mpeg_audio['VBR_frames'] : '') {
940
- switch ($thisfile_mpeg_audio['bitrate_mode']) {
941
- case 'vbr':
942
- case 'abr':
943
- $bytes_per_frame = 1152;
944
- if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) {
945
- $bytes_per_frame = 384;
946
- } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) {
947
- $bytes_per_frame = 576;
948
- }
949
- $thisfile_mpeg_audio['VBR_bitrate'] = (isset($thisfile_mpeg_audio['VBR_bytes']) ? (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / $bytes_per_frame) : 0);
950
- if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
951
- $info['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate'];
952
- $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
953
- }
954
- break;
955
- }
956
- }
957
-
958
- // End variable-bitrate headers
959
- ////////////////////////////////////////////////////////////////////////////////////
960
-
961
- if ($recursivesearch) {
962
-
963
- if (!$this->RecursiveFrameScanning($offset, $nextframetestoffset, $ScanAsCBR)) {
964
- return false;
965
- }
966
-
967
- }
968
-
969
-
970
- //if (false) {
971
- // // experimental side info parsing section - not returning anything useful yet
972
- //
973
- // $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData);
974
- // $SideInfoOffset = 0;
975
- //
976
- // if ($thisfile_mpeg_audio['version'] == '1') {
977
- // if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
978
- // // MPEG-1 (mono)
979
- // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
980
- // $SideInfoOffset += 9;
981
- // $SideInfoOffset += 5;
982
- // } else {
983
- // // MPEG-1 (stereo, joint-stereo, dual-channel)
984
- // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
985
- // $SideInfoOffset += 9;
986
- // $SideInfoOffset += 3;
987
- // }
988
- // } else { // 2 or 2.5
989
- // if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
990
- // // MPEG-2, MPEG-2.5 (mono)
991
- // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
992
- // $SideInfoOffset += 8;
993
- // $SideInfoOffset += 1;
994
- // } else {
995
- // // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
996
- // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
997
- // $SideInfoOffset += 8;
998
- // $SideInfoOffset += 2;
999
- // }
1000
- // }
1001
- //
1002
- // if ($thisfile_mpeg_audio['version'] == '1') {
1003
- // for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
1004
- // for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
1005
- // $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1006
- // $SideInfoOffset += 2;
1007
- // }
1008
- // }
1009
- // }
1010
- // for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) {
1011
- // for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
1012
- // $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
1013
- // $SideInfoOffset += 12;
1014
- // $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1015
- // $SideInfoOffset += 9;
1016
- // $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
1017
- // $SideInfoOffset += 8;
1018
- // if ($thisfile_mpeg_audio['version'] == '1') {
1019
- // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1020
- // $SideInfoOffset += 4;
1021
- // } else {
1022
- // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1023
- // $SideInfoOffset += 9;
1024
- // }
1025
- // $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1026
- // $SideInfoOffset += 1;
1027
- //
1028
- // if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') {
1029
- //
1030
- // $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
1031
- // $SideInfoOffset += 2;
1032
- // $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1033
- // $SideInfoOffset += 1;
1034
- //
1035
- // for ($region = 0; $region < 2; $region++) {
1036
- // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1037
- // $SideInfoOffset += 5;
1038
- // }
1039
- // $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0;
1040
- //
1041
- // for ($window = 0; $window < 3; $window++) {
1042
- // $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1043
- // $SideInfoOffset += 3;
1044
- // }
1045
- //
1046
- // } else {
1047
- //
1048
- // for ($region = 0; $region < 3; $region++) {
1049
- // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1050
- // $SideInfoOffset += 5;
1051
- // }
1052
- //
1053
- // $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1054
- // $SideInfoOffset += 4;
1055
- // $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1056
- // $SideInfoOffset += 3;
1057
- // $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0;
1058
- // }
1059
- //
1060
- // if ($thisfile_mpeg_audio['version'] == '1') {
1061
- // $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1062
- // $SideInfoOffset += 1;
1063
- // }
1064
- // $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1065
- // $SideInfoOffset += 1;
1066
- // $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1067
- // $SideInfoOffset += 1;
1068
- // }
1069
- // }
1070
- //}
1071
-
1072
- return true;
1073
- }
1074
-
1075
- public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
1076
- $info = &$this->getid3->info;
1077
- $firstframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1078
- $this->decodeMPEGaudioHeader($offset, $firstframetestarray, false);
1079
-
1080
- for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) {
1081
- // check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
1082
- if (($nextframetestoffset + 4) >= $info['avdataend']) {
1083
- // end of file
1084
- return true;
1085
- }
1086
-
1087
- $nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1088
- if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) {
1089
- if ($ScanAsCBR) {
1090
- // force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header
1091
- if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) {
1092
- return false;
1093
- }
1094
- }
1095
-
1096
-
1097
- // next frame is OK, get ready to check the one after that
1098
- if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) {
1099
- $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
1100
- } else {
1101
- $info['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.';
1102
- return false;
1103
- }
1104
-
1105
- } elseif (!empty($firstframetestarray['mpeg']['audio']['framelength']) && (($nextframetestoffset + $firstframetestarray['mpeg']['audio']['framelength']) > $info['avdataend'])) {
1106
-
1107
- // it's not the end of the file, but there's not enough data left for another frame, so assume it's garbage/padding and return OK
1108
- return true;
1109
-
1110
- } else {
1111
-
1112
- // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
1113
- $info['warning'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.';
1114
-
1115
- return false;
1116
- }
1117
- }
1118
- return true;
1119
- }
1120
-
1121
- public function FreeFormatFrameLength($offset, $deepscan=false) {
1122
- $info = &$this->getid3->info;
1123
-
1124
- $this->fseek($offset);
1125
- $MPEGaudioData = $this->fread(32768);
1126
-
1127
- $SyncPattern1 = substr($MPEGaudioData, 0, 4);
1128
- // may be different pattern due to padding
1129
- $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
1130
- if ($SyncPattern2 === $SyncPattern1) {
1131
- $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
1132
- }
1133
-
1134
- $framelength = false;
1135
- $framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
1136
- $framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
1137
- if ($framelength1 > 4) {
1138
- $framelength = $framelength1;
1139
- }
1140
- if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1141
- $framelength = $framelength2;
1142
- }
1143
- if (!$framelength) {
1144
-
1145
- // LAME 3.88 has a different value for modeextension on the first frame vs the rest
1146
- $framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4);
1147
- $framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4);
1148
-
1149
- if ($framelength1 > 4) {
1150
- $framelength = $framelength1;
1151
- }
1152
- if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1153
- $framelength = $framelength2;
1154
- }
1155
- if (!$framelength) {
1156
- $info['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset;
1157
- return false;
1158
- } else {
1159
- $info['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)';
1160
- $info['audio']['codec'] = 'LAME';
1161
- $info['audio']['encoder'] = 'LAME3.88';
1162
- $SyncPattern1 = substr($SyncPattern1, 0, 3);
1163
- $SyncPattern2 = substr($SyncPattern2, 0, 3);
1164
- }
1165
- }
1166
-
1167
- if ($deepscan) {
1168
-
1169
- $ActualFrameLengthValues = array();
1170
- $nextoffset = $offset + $framelength;
1171
- while ($nextoffset < ($info['avdataend'] - 6)) {
1172
- $this->fseek($nextoffset - 1);
1173
- $NextSyncPattern = $this->fread(6);
1174
- if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) {
1175
- // good - found where expected
1176
- $ActualFrameLengthValues[] = $framelength;
1177
- } elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) {
1178
- // ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
1179
- $ActualFrameLengthValues[] = ($framelength - 1);
1180
- $nextoffset--;
1181
- } elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) {
1182
- // ok - found one byte later than expected (last frame was padded, first frame wasn't)
1183
- $ActualFrameLengthValues[] = ($framelength + 1);
1184
- $nextoffset++;
1185
- } else {
1186
- $info['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset;
1187
- return false;
1188
- }
1189
- $nextoffset += $framelength;
1190
- }
1191
- if (count($ActualFrameLengthValues) > 0) {
1192
- $framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues)));
1193
- }
1194
- }
1195
- return $framelength;
1196
- }
1197
-
1198
- public function getOnlyMPEGaudioInfoBruteForce() {
1199
- $MPEGaudioHeaderDecodeCache = array();
1200
- $MPEGaudioHeaderValidCache = array();
1201
- $MPEGaudioHeaderLengthCache = array();
1202
- $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
1203
- $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
1204
- $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
1205
- $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray();
1206
- $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray();
1207
- $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
1208
- $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray();
1209
- $LongMPEGversionLookup = array();
1210
- $LongMPEGlayerLookup = array();
1211
- $LongMPEGbitrateLookup = array();
1212
- $LongMPEGpaddingLookup = array();
1213
- $LongMPEGfrequencyLookup = array();
1214
- $Distribution['bitrate'] = array();
1215
- $Distribution['frequency'] = array();
1216
- $Distribution['layer'] = array();
1217
- $Distribution['version'] = array();
1218
- $Distribution['padding'] = array();
1219
-
1220
- $info = &$this->getid3->info;
1221
- $this->fseek($info['avdataoffset']);
1222
-
1223
- $max_frames_scan = 5000;
1224
- $frames_scanned = 0;
1225
-
1226
- $previousvalidframe = $info['avdataoffset'];
1227
- while ($this->ftell() < $info['avdataend']) {
1228
- set_time_limit(30);
1229
- $head4 = $this->fread(4);
1230
- if (strlen($head4) < 4) {
1231
- break;
1232
- }
1233
- if ($head4{0} != "\xFF") {
1234
- for ($i = 1; $i < 4; $i++) {
1235
- if ($head4{$i} == "\xFF") {
1236
- $this->fseek($i - 4, SEEK_CUR);
1237
- continue 2;
1238
- }
1239
- }
1240
- continue;
1241
- }
1242
- if (!isset($MPEGaudioHeaderDecodeCache[$head4])) {
1243
- $MPEGaudioHeaderDecodeCache[$head4] = self::MPEGaudioHeaderDecode($head4);
1244
- }
1245
- if (!isset($MPEGaudioHeaderValidCache[$head4])) {
1246
- $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false);
1247
- }
1248
- if ($MPEGaudioHeaderValidCache[$head4]) {
1249
-
1250
- if (!isset($MPEGaudioHeaderLengthCache[$head4])) {
1251
- $LongMPEGversionLookup[$head4] = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']];
1252
- $LongMPEGlayerLookup[$head4] = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']];
1253
- $LongMPEGbitrateLookup[$head4] = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']];
1254
- $LongMPEGpaddingLookup[$head4] = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding'];
1255
- $LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']];
1256
- $MPEGaudioHeaderLengthCache[$head4] = self::MPEGaudioFrameLength(
1257
- $LongMPEGbitrateLookup[$head4],
1258
- $LongMPEGversionLookup[$head4],
1259
- $LongMPEGlayerLookup[$head4],
1260
- $LongMPEGpaddingLookup[$head4],
1261
- $LongMPEGfrequencyLookup[$head4]);
1262
- }
1263
- if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
1264
- $WhereWeWere = $this->ftell();
1265
- $this->fseek($MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
1266
- $next4 = $this->fread(4);
1267
- if ($next4{0} == "\xFF") {
1268
- if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
1269
- $MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
1270
- }
1271
- if (!isset($MPEGaudioHeaderValidCache[$next4])) {
1272
- $MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
1273
- }
1274
- if ($MPEGaudioHeaderValidCache[$next4]) {
1275
- $this->fseek(-4, SEEK_CUR);
1276
-
1277
- getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]);
1278
- getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]);
1279
- getid3_lib::safe_inc($Distribution['version'][$LongMPEGversionLookup[$head4]]);
1280
- getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]);
1281
- getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]);
1282
- if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
1283
- $pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
1284
- $info['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
1285
- foreach ($Distribution as $key1 => $value1) {
1286
- foreach ($value1 as $key2 => $value2) {
1287
- $Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
1288
- }
1289
- }
1290
- break;
1291
- }
1292
- continue;
1293
- }
1294
- }
1295
- unset($next4);
1296
- $this->fseek($WhereWeWere - 3);
1297
- }
1298
-
1299
- }
1300
- }
1301
- foreach ($Distribution as $key => $value) {
1302
- ksort($Distribution[$key], SORT_NUMERIC);
1303
- }
1304
- ksort($Distribution['version'], SORT_STRING);
1305
- $info['mpeg']['audio']['bitrate_distribution'] = $Distribution['bitrate'];
1306
- $info['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency'];
1307
- $info['mpeg']['audio']['layer_distribution'] = $Distribution['layer'];
1308
- $info['mpeg']['audio']['version_distribution'] = $Distribution['version'];
1309
- $info['mpeg']['audio']['padding_distribution'] = $Distribution['padding'];
1310
- if (count($Distribution['version']) > 1) {
1311
- $info['error'][] = 'Corrupt file - more than one MPEG version detected';
1312
- }
1313
- if (count($Distribution['layer']) > 1) {
1314
- $info['error'][] = 'Corrupt file - more than one MPEG layer detected';
1315
- }
1316
- if (count($Distribution['frequency']) > 1) {
1317
- $info['error'][] = 'Corrupt file - more than one MPEG sample rate detected';
1318
- }
1319
-
1320
-
1321
- $bittotal = 0;
1322
- foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) {
1323
- if ($bitratevalue != 'free') {
1324
- $bittotal += ($bitratevalue * $bitratecount);
1325
- }
1326
- }
1327
- $info['mpeg']['audio']['frame_count'] = array_sum($Distribution['bitrate']);
1328
- if ($info['mpeg']['audio']['frame_count'] == 0) {
1329
- $info['error'][] = 'no MPEG audio frames found';
1330
- return false;
1331
- }
1332
- $info['mpeg']['audio']['bitrate'] = ($bittotal / $info['mpeg']['audio']['frame_count']);
1333
- $info['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr');
1334
- $info['mpeg']['audio']['sample_rate'] = getid3_lib::array_max($Distribution['frequency'], true);
1335
-
1336
- $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1337
- $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1338
- $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1339
- $info['audio']['dataformat'] = 'mp'.getid3_lib::array_max($Distribution['layer'], true);
1340
- $info['fileformat'] = $info['audio']['dataformat'];
1341
-
1342
- return true;
1343
- }
1344
-
1345
-
1346
- public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) {
1347
- // looks for synch, decodes MPEG audio header
1348
-
1349
- $info = &$this->getid3->info;
1350
-
1351
- static $MPEGaudioVersionLookup;
1352
- static $MPEGaudioLayerLookup;
1353
- static $MPEGaudioBitrateLookup;
1354
- if (empty($MPEGaudioVersionLookup)) {
1355
- $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
1356
- $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
1357
- $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
1358
-
1359
- }
1360
-
1361
- $this->fseek($avdataoffset);
1362
- $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
1363
- if ($sync_seek_buffer_size <= 0) {
1364
- $info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset;
1365
- return false;
1366
- }
1367
- $header = $this->fread($sync_seek_buffer_size);
1368
- $sync_seek_buffer_size = strlen($header);
1369
- $SynchSeekOffset = 0;
1370
- while ($SynchSeekOffset < $sync_seek_buffer_size) {
1371
- if ((($avdataoffset + $SynchSeekOffset) < $info['avdataend']) && !feof($this->getid3->fp)) {
1372
-
1373
- if ($SynchSeekOffset > $sync_seek_buffer_size) {
1374
- // if a synch's not found within the first 128k bytes, then give up
1375
- $info['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB';
1376
- if (isset($info['audio']['bitrate'])) {
1377
- unset($info['audio']['bitrate']);
1378
- }
1379
- if (isset($info['mpeg']['audio'])) {
1380
- unset($info['mpeg']['audio']);
1381
- }
1382
- if (empty($info['mpeg'])) {
1383
- unset($info['mpeg']);
1384
- }
1385
- return false;
1386
-
1387
- } elseif (feof($this->getid3->fp)) {
1388
-
1389
- $info['error'][] = 'Could not find valid MPEG audio synch before end of file';
1390
- if (isset($info['audio']['bitrate'])) {
1391
- unset($info['audio']['bitrate']);
1392
- }
1393
- if (isset($info['mpeg']['audio'])) {
1394
- unset($info['mpeg']['audio']);
1395
- }
1396
- if (isset($info['mpeg']) && (!is_array($info['mpeg']) || (count($info['mpeg']) == 0))) {
1397
- unset($info['mpeg']);
1398
- }
1399
- return false;
1400
- }
1401
- }
1402
-
1403
- if (($SynchSeekOffset + 1) >= strlen($header)) {
1404
- $info['error'][] = 'Could not find valid MPEG synch before end of file';
1405
- return false;
1406
- }
1407
-
1408
- if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
1409
- if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
1410
- $FirstFrameThisfileInfo = $info;
1411
- $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
1412
- if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $FirstFrameThisfileInfo, false)) {
1413
- // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
1414
- // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
1415
- unset($FirstFrameThisfileInfo);
1416
- }
1417
- }
1418
-
1419
- $dummy = $info; // only overwrite real data if valid header found
1420
- if ($this->decodeMPEGaudioHeader($avdataoffset + $SynchSeekOffset, $dummy, true)) {
1421
- $info = $dummy;
1422
- $info['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
1423
- switch (isset($info['fileformat']) ? $info['fileformat'] : '') {
1424
- case '':
1425
- case 'id3':
1426
- case 'ape':
1427
- case 'mp3':
1428
- $info['fileformat'] = 'mp3';
1429
- $info['audio']['dataformat'] = 'mp3';
1430
- break;
1431
- }
1432
- if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
1433
- if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
1434
- // If there is garbage data between a valid VBR header frame and a sequence
1435
- // of valid MPEG-audio frames the VBR data is no longer discarded.
1436
- $info = $FirstFrameThisfileInfo;
1437
- $info['avdataoffset'] = $FirstFrameAVDataOffset;
1438
- $info['fileformat'] = 'mp3';
1439
- $info['audio']['dataformat'] = 'mp3';
1440
- $dummy = $info;
1441
- unset($dummy['mpeg']['audio']);
1442
- $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
1443
- $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset;
1444
- if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) {
1445
- $info = $dummy;
1446
- $info['avdataoffset'] = $GarbageOffsetEnd;
1447
- $info['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd;
1448
- } else {
1449
- $info['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')';
1450
- }
1451
- }
1452
- }
1453
- if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) {
1454
- // VBR file with no VBR header
1455
- $BitrateHistogram = true;
1456
- }
1457
-
1458
- if ($BitrateHistogram) {
1459
-
1460
- $info['mpeg']['audio']['stereo_distribution'] = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0);
1461
- $info['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0);
1462
-
1463
- if ($info['mpeg']['audio']['version'] == '1') {
1464
- if ($info['mpeg']['audio']['layer'] == 3) {
1465
- $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0);
1466
- } elseif ($info['mpeg']['audio']['layer'] == 2) {
1467
- $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0);
1468
- } elseif ($info['mpeg']['audio']['layer'] == 1) {
1469
- $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0);
1470
- }
1471
- } elseif ($info['mpeg']['audio']['layer'] == 1) {
1472
- $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0);
1473
- } else {
1474
- $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0);
1475
- }
1476
-
1477
- $dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1478
- $synchstartoffset = $info['avdataoffset'];
1479
- $this->fseek($info['avdataoffset']);
1480
-
1481
- // you can play with these numbers:
1482
- $max_frames_scan = 50000;
1483
- $max_scan_segments = 10;
1484
-
1485
- // don't play with these numbers:
1486
- $FastMode = false;
1487
- $SynchErrorsFound = 0;
1488
- $frames_scanned = 0;
1489
- $this_scan_segment = 0;
1490
- $frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments);
1491
- $pct_data_scanned = 0;
1492
- for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) {
1493
- $frames_scanned_this_segment = 0;
1494
- if ($this->ftell() >= $info['avdataend']) {
1495
- break;
1496
- }
1497
- $scan_start_offset[$current_segment] = max($this->ftell(), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
1498
- if ($current_segment > 0) {
1499
- $this->fseek($scan_start_offset[$current_segment]);
1500
- $buffer_4k = $this->fread(4096);
1501
- for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
1502
- if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
1503
- if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
1504
- $calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
1505
- if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
1506
- $scan_start_offset[$current_segment] += $j;
1507
- break;
1508
- }
1509
- }
1510
- }
1511
- }
1512
- }
1513
- $synchstartoffset = $scan_start_offset[$current_segment];
1514
- while ($this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
1515
- $FastMode = true;
1516
- $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
1517
-
1518
- if (empty($dummy['mpeg']['audio']['framelength'])) {
1519
- $SynchErrorsFound++;
1520
- $synchstartoffset++;
1521
- } else {
1522
- getid3_lib::safe_inc($info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]);
1523
- getid3_lib::safe_inc($info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]);
1524
- getid3_lib::safe_inc($info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]);
1525
- $synchstartoffset += $dummy['mpeg']['audio']['framelength'];
1526
- }
1527
- $frames_scanned++;
1528
- if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) {
1529
- $this_pct_scanned = ($this->ftell() - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']);
1530
- if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) {
1531
- // file likely contains < $max_frames_scan, just scan as one segment
1532
- $max_scan_segments = 1;
1533
- $frames_scan_per_segment = $max_frames_scan;
1534
- } else {
1535
- $pct_data_scanned += $this_pct_scanned;
1536
- break;
1537
- }
1538
- }
1539
- }
1540
- }
1541
- if ($pct_data_scanned > 0) {
1542
- $info['warning'][] = 'too many MPEG audio frames to scan, only scanned '.$frames_scanned.' frames in '.$max_scan_segments.' segments ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
1543
- foreach ($info['mpeg']['audio'] as $key1 => $value1) {
1544
- if (!preg_match('#_distribution$#i', $key1)) {
1545
- continue;
1546
- }
1547
- foreach ($value1 as $key2 => $value2) {
1548
- $info['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned);
1549
- }
1550
- }
1551
- }
1552
-
1553
- if ($SynchErrorsFound > 0) {
1554
- $info['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis';
1555
- //return false;
1556
- }
1557
-
1558
- $bittotal = 0;
1559
- $framecounter = 0;
1560
- foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) {
1561
- $framecounter += $bitratecount;
1562
- if ($bitratevalue != 'free') {
1563
- $bittotal += ($bitratevalue * $bitratecount);
1564
- }
1565
- }
1566
- if ($framecounter == 0) {
1567
- $info['error'][] = 'Corrupt MP3 file: framecounter == zero';
1568
- return false;
1569
- }
1570
- $info['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter);
1571
- $info['mpeg']['audio']['bitrate'] = ($bittotal / $framecounter);
1572
-
1573
- $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1574
-
1575
-
1576
- // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
1577
- $distinct_bitrates = 0;
1578
- foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) {
1579
- if ($bitrate_count > 0) {
1580
- $distinct_bitrates++;
1581
- }
1582
- }
1583
- if ($distinct_bitrates > 1) {
1584
- $info['mpeg']['audio']['bitrate_mode'] = 'vbr';
1585
- } else {
1586
- $info['mpeg']['audio']['bitrate_mode'] = 'cbr';
1587
- }
1588
- $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1589
-
1590
- }
1591
-
1592
- break; // exit while()
1593
- }
1594
- }
1595
-
1596
- $SynchSeekOffset++;
1597
- if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) {
1598
- // end of file/data
1599
-
1600
- if (empty($info['mpeg']['audio'])) {
1601
-
1602
- $info['error'][] = 'could not find valid MPEG synch before end of file';
1603
- if (isset($info['audio']['bitrate'])) {
1604
- unset($info['audio']['bitrate']);
1605
- }
1606
- if (isset($info['mpeg']['audio'])) {
1607
- unset($info['mpeg']['audio']);
1608
- }
1609
- if (isset($info['mpeg']) && (!is_array($info['mpeg']) || empty($info['mpeg']))) {
1610
- unset($info['mpeg']);
1611
- }
1612
- return false;
1613
-
1614
- }
1615
- break;
1616
- }
1617
-
1618
- }
1619
- $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
1620
- $info['audio']['channelmode'] = $info['mpeg']['audio']['channelmode'];
1621
- $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1622
- return true;
1623
- }
1624
-
1625
-
1626
- public static function MPEGaudioVersionArray() {
1627
- static $MPEGaudioVersion = array('2.5', false, '2', '1');
1628
- return $MPEGaudioVersion;
1629
- }
1630
-
1631
- public static function MPEGaudioLayerArray() {
1632
- static $MPEGaudioLayer = array(false, 3, 2, 1);
1633
- return $MPEGaudioLayer;
1634
- }
1635
-
1636
- public static function MPEGaudioBitrateArray() {
1637
- static $MPEGaudioBitrate;
1638
- if (empty($MPEGaudioBitrate)) {
1639
- $MPEGaudioBitrate = array (
1640
- '1' => array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000),
1641
- 2 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000),
1642
- 3 => array('free', 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000)
1643
- ),
1644
-
1645
- '2' => array (1 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000),
1646
- 2 => array('free', 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000),
1647
- )
1648
- );
1649
- $MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2];
1650
- $MPEGaudioBitrate['2.5'] = $MPEGaudioBitrate['2'];
1651
- }
1652
- return $MPEGaudioBitrate;
1653
- }
1654
-
1655
- public static function MPEGaudioFrequencyArray() {
1656
- static $MPEGaudioFrequency;
1657
- if (empty($MPEGaudioFrequency)) {
1658
- $MPEGaudioFrequency = array (
1659
- '1' => array(44100, 48000, 32000),
1660
- '2' => array(22050, 24000, 16000),
1661
- '2.5' => array(11025, 12000, 8000)
1662
- );
1663
- }
1664
- return $MPEGaudioFrequency;
1665
- }
1666
-
1667
- public static function MPEGaudioChannelModeArray() {
1668
- static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
1669
- return $MPEGaudioChannelMode;
1670
- }
1671
-
1672
- public static function MPEGaudioModeExtensionArray() {
1673
- static $MPEGaudioModeExtension;
1674
- if (empty($MPEGaudioModeExtension)) {
1675
- $MPEGaudioModeExtension = array (
1676
- 1 => array('4-31', '8-31', '12-31', '16-31'),
1677
- 2 => array('4-31', '8-31', '12-31', '16-31'),
1678
- 3 => array('', 'IS', 'MS', 'IS+MS')
1679
- );
1680
- }
1681
- return $MPEGaudioModeExtension;
1682
- }
1683
-
1684
- public static function MPEGaudioEmphasisArray() {
1685
- static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
1686
- return $MPEGaudioEmphasis;
1687
- }
1688
-
1689
- public static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
1690
- return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
1691
- }
1692
-
1693
- public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
1694
- if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
1695
- return false;
1696
- }
1697
-
1698
- static $MPEGaudioVersionLookup;
1699
- static $MPEGaudioLayerLookup;
1700
- static $MPEGaudioBitrateLookup;
1701
- static $MPEGaudioFrequencyLookup;
1702
- static $MPEGaudioChannelModeLookup;
1703
- static $MPEGaudioModeExtensionLookup;
1704
- static $MPEGaudioEmphasisLookup;
1705
- if (empty($MPEGaudioVersionLookup)) {
1706
- $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
1707
- $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
1708
- $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
1709
- $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray();
1710
- $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray();
1711
- $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
1712
- $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray();
1713
- }
1714
-
1715
- if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
1716
- $decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
1717
- } else {
1718
- echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : '');
1719
- return false;
1720
- }
1721
- if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
1722
- $decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
1723
- } else {
1724
- echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : '');
1725
- return false;
1726
- }
1727
- if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
1728
- echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : '');
1729
- if ($rawarray['bitrate'] == 15) {
1730
- // known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
1731
- // let it go through here otherwise file will not be identified
1732
- if (!$allowBitrate15) {
1733
- return false;
1734
- }
1735
- } else {
1736
- return false;
1737
- }
1738
- }
1739
- if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
1740
- echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : '');
1741
- return false;
1742
- }
1743
- if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
1744
- echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : '');
1745
- return false;
1746
- }
1747
- if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
1748
- echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : '');
1749
- return false;
1750
- }
1751
- if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
1752
- echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : '');
1753
- return false;
1754
- }
1755
- // These are just either set or not set, you can't mess that up :)
1756
- // $rawarray['protection'];
1757
- // $rawarray['padding'];
1758
- // $rawarray['private'];
1759
- // $rawarray['copyright'];
1760
- // $rawarray['original'];
1761
-
1762
- return true;
1763
- }
1764
-
1765
- public static function MPEGaudioHeaderDecode($Header4Bytes) {
1766
- // AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM
1767
- // A - Frame sync (all bits set)
1768
- // B - MPEG Audio version ID
1769
- // C - Layer description
1770
- // D - Protection bit
1771
- // E - Bitrate index
1772
- // F - Sampling rate frequency index
1773
- // G - Padding bit
1774
- // H - Private bit
1775
- // I - Channel Mode
1776
- // J - Mode extension (Only if Joint stereo)
1777
- // K - Copyright
1778
- // L - Original
1779
- // M - Emphasis
1780
-
1781
- if (strlen($Header4Bytes) != 4) {
1782
- return false;
1783
- }
1784
-
1785
- $MPEGrawHeader['synch'] = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
1786
- $MPEGrawHeader['version'] = (ord($Header4Bytes{1}) & 0x18) >> 3; // BB
1787
- $MPEGrawHeader['layer'] = (ord($Header4Bytes{1}) & 0x06) >> 1; // CC
1788
- $MPEGrawHeader['protection'] = (ord($Header4Bytes{1}) & 0x01); // D
1789
- $MPEGrawHeader['bitrate'] = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
1790
- $MPEGrawHeader['sample_rate'] = (ord($Header4Bytes{2}) & 0x0C) >> 2; // FF
1791
- $MPEGrawHeader['padding'] = (ord($Header4Bytes{2}) & 0x02) >> 1; // G
1792
- $MPEGrawHeader['private'] = (ord($Header4Bytes{2}) & 0x01); // H
1793
- $MPEGrawHeader['channelmode'] = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
1794
- $MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; // JJ
1795
- $MPEGrawHeader['copyright'] = (ord($Header4Bytes{3}) & 0x08) >> 3; // K
1796
- $MPEGrawHeader['original'] = (ord($Header4Bytes{3}) & 0x04) >> 2; // L
1797
- $MPEGrawHeader['emphasis'] = (ord($Header4Bytes{3}) & 0x03); // MM
1798
-
1799
- return $MPEGrawHeader;
1800
- }
1801
-
1802
- public static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
1803
- static $AudioFrameLengthCache = array();
1804
-
1805
- if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
1806
- $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
1807
- if ($bitrate != 'free') {
1808
-
1809
- if ($version == '1') {
1810
-
1811
- if ($layer == '1') {
1812
-
1813
- // For Layer I slot is 32 bits long
1814
- $FrameLengthCoefficient = 48;
1815
- $SlotLength = 4;
1816
-
1817
- } else { // Layer 2 / 3
1818
-
1819
- // for Layer 2 and Layer 3 slot is 8 bits long.
1820
- $FrameLengthCoefficient = 144;
1821
- $SlotLength = 1;
1822
-
1823
- }
1824
-
1825
- } else { // MPEG-2 / MPEG-2.5
1826
-
1827
- if ($layer == '1') {
1828
-
1829
- // For Layer I slot is 32 bits long
1830
- $FrameLengthCoefficient = 24;
1831
- $SlotLength = 4;
1832
-
1833
- } elseif ($layer == '2') {
1834
-
1835
- // for Layer 2 and Layer 3 slot is 8 bits long.
1836
- $FrameLengthCoefficient = 144;
1837
- $SlotLength = 1;
1838
-
1839
- } else { // layer 3
1840
-
1841
- // for Layer 2 and Layer 3 slot is 8 bits long.
1842
- $FrameLengthCoefficient = 72;
1843
- $SlotLength = 1;
1844
-
1845
- }
1846
-
1847
- }
1848
-
1849
- // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
1850
- if ($samplerate > 0) {
1851
- $NewFramelength = ($FrameLengthCoefficient * $bitrate) / $samplerate;
1852
- $NewFramelength = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I)
1853
- if ($padding) {
1854
- $NewFramelength += $SlotLength;
1855
- }
1856
- $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength;
1857
- }
1858
- }
1859
- }
1860
- return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
1861
- }
1862
-
1863
- public static function ClosestStandardMP3Bitrate($bit_rate) {
1864
- static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
1865
- static $bit_rate_table = array (0=>'-');
1866
- $round_bit_rate = intval(round($bit_rate, -3));
1867
- if (!isset($bit_rate_table[$round_bit_rate])) {
1868
- if ($round_bit_rate > max($standard_bit_rates)) {
1869
- $bit_rate_table[$round_bit_rate] = round($bit_rate, 2 - strlen($bit_rate));
1870
- } else {
1871
- $bit_rate_table[$round_bit_rate] = max($standard_bit_rates);
1872
- foreach ($standard_bit_rates as $standard_bit_rate) {
1873
- if ($round_bit_rate >= $standard_bit_rate + (($bit_rate_table[$round_bit_rate] - $standard_bit_rate) / 2)) {
1874
- break;
1875
- }
1876
- $bit_rate_table[$round_bit_rate] = $standard_bit_rate;
1877
- }
1878
- }
1879
- }
1880
- return $bit_rate_table[$round_bit_rate];
1881
- }
1882
-
1883
- public static function XingVBRidOffset($version, $channelmode) {
1884
- static $XingVBRidOffsetCache = array();
1885
- if (empty($XingVBRidOffset)) {
1886
- $XingVBRidOffset = array (
1887
- '1' => array ('mono' => 0x15, // 4 + 17 = 21
1888
- 'stereo' => 0x24, // 4 + 32 = 36
1889
- 'joint stereo' => 0x24,
1890
- 'dual channel' => 0x24
1891
- ),
1892
-
1893
- '2' => array ('mono' => 0x0D, // 4 + 9 = 13
1894
- 'stereo' => 0x15, // 4 + 17 = 21
1895
- 'joint stereo' => 0x15,
1896
- 'dual channel' => 0x15
1897
- ),
1898
-
1899
- '2.5' => array ('mono' => 0x15,
1900
- 'stereo' => 0x15,
1901
- 'joint stereo' => 0x15,
1902
- 'dual channel' => 0x15
1903
- )
1904
- );
1905
- }
1906
- return $XingVBRidOffset[$version][$channelmode];
1907
- }
1908
-
1909
- public static function LAMEvbrMethodLookup($VBRmethodID) {
1910
- static $LAMEvbrMethodLookup = array(
1911
- 0x00 => 'unknown',
1912
- 0x01 => 'cbr',
1913
- 0x02 => 'abr',
1914
- 0x03 => 'vbr-old / vbr-rh',
1915
- 0x04 => 'vbr-new / vbr-mtrh',
1916
- 0x05 => 'vbr-mt',
1917
- 0x06 => 'vbr (full vbr method 4)',
1918
- 0x08 => 'cbr (constant bitrate 2 pass)',
1919
- 0x09 => 'abr (2 pass)',
1920
- 0x0F => 'reserved'
1921
- );
1922
- return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
1923
- }
1924
-
1925
- public static function LAMEmiscStereoModeLookup($StereoModeID) {
1926
- static $LAMEmiscStereoModeLookup = array(
1927
- 0 => 'mono',
1928
- 1 => 'stereo',
1929
- 2 => 'dual mono',
1930
- 3 => 'joint stereo',
1931
- 4 => 'forced stereo',
1932
- 5 => 'auto',
1933
- 6 => 'intensity stereo',
1934
- 7 => 'other'
1935
- );
1936
- return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
1937
- }
1938
-
1939
- public static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
1940
- static $LAMEmiscSourceSampleFrequencyLookup = array(
1941
- 0 => '<= 32 kHz',
1942
- 1 => '44.1 kHz',
1943
- 2 => '48 kHz',
1944
- 3 => '> 48kHz'
1945
- );
1946
- return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
1947
- }
1948
-
1949
- public static function LAMEsurroundInfoLookup($SurroundInfoID) {
1950
- static $LAMEsurroundInfoLookup = array(
1951
- 0 => 'no surround info',
1952
- 1 => 'DPL encoding',
1953
- 2 => 'DPL2 encoding',
1954
- 3 => 'Ambisonic encoding'
1955
- );
1956
- return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
1957
- }
1958
-
1959
- public static function LAMEpresetUsedLookup($LAMEtag) {
1960
-
1961
- if ($LAMEtag['preset_used_id'] == 0) {
1962
- // no preset used (LAME >=3.93)
1963
- // no preset recorded (LAME <3.93)
1964
- return '';
1965
- }
1966
- $LAMEpresetUsedLookup = array();
1967
-
1968
- ///// THIS PART CANNOT BE STATIC .
1969
- for ($i = 8; $i <= 320; $i++) {
1970
- switch ($LAMEtag['vbr_method']) {
1971
- case 'cbr':
1972
- $LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i;
1973
- break;
1974
- case 'abr':
1975
- default: // other VBR modes shouldn't be here(?)
1976
- $LAMEpresetUsedLookup[$i] = '--alt-preset '.$i;
1977
- break;
1978
- }
1979
- }
1980
-
1981
- // named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
1982
-
1983
- // named alt-presets
1984
- $LAMEpresetUsedLookup[1000] = '--r3mix';
1985
- $LAMEpresetUsedLookup[1001] = '--alt-preset standard';
1986
- $LAMEpresetUsedLookup[1002] = '--alt-preset extreme';
1987
- $LAMEpresetUsedLookup[1003] = '--alt-preset insane';
1988
- $LAMEpresetUsedLookup[1004] = '--alt-preset fast standard';
1989
- $LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme';
1990
- $LAMEpresetUsedLookup[1006] = '--alt-preset medium';
1991
- $LAMEpresetUsedLookup[1007] = '--alt-preset fast medium';
1992
-
1993
- // LAME 3.94 additions/changes
1994
- $LAMEpresetUsedLookup[1010] = '--preset portable'; // 3.94a15 Oct 21 2003
1995
- $LAMEpresetUsedLookup[1015] = '--preset radio'; // 3.94a15 Oct 21 2003
1996
-
1997
- $LAMEpresetUsedLookup[320] = '--preset insane'; // 3.94a15 Nov 12 2003
1998
- $LAMEpresetUsedLookup[410] = '-V9';
1999
- $LAMEpresetUsedLookup[420] = '-V8';
2000
- $LAMEpresetUsedLookup[440] = '-V6';
2001
- $LAMEpresetUsedLookup[430] = '--preset radio'; // 3.94a15 Nov 12 2003
2002
- $LAMEpresetUsedLookup[450] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable'; // 3.94a15 Nov 12 2003
2003
- $LAMEpresetUsedLookup[460] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium'; // 3.94a15 Nov 12 2003
2004
- $LAMEpresetUsedLookup[470] = '--r3mix'; // 3.94b1 Dec 18 2003
2005
- $LAMEpresetUsedLookup[480] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard'; // 3.94a15 Nov 12 2003
2006
- $LAMEpresetUsedLookup[490] = '-V1';
2007
- $LAMEpresetUsedLookup[500] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme'; // 3.94a15 Nov 12 2003
2008
-
2009
- return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org');
2010
- }
2011
-
2012
- }
1
+ <?php
2
+ /////////////////////////////////////////////////////////////////
3
+ /// getID3() by James Heinrich <info@getid3.org> //
4
+ // available at http://getid3.sourceforge.net //
5
+ // or http://www.getid3.org //
6
+ // also https://github.com/JamesHeinrich/getID3 //
7
+ /////////////////////////////////////////////////////////////////
8
+ // See readme.txt for more details //
9
+ /////////////////////////////////////////////////////////////////
10
+ // //
11
+ // module.audio.mp3.php //
12
+ // module for analyzing MP3 files //
13
+ // dependencies: NONE //
14
+ // ///
15
+ /////////////////////////////////////////////////////////////////
16
+
17
+
18
+ // number of frames to scan to determine if MPEG-audio sequence is valid
19
+ // Lower this number to 5-20 for faster scanning
20
+ // Increase this number to 50+ for most accurate detection of valid VBR/CBR
21
+ // mpeg-audio streams
22
+ define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
23
+
24
+
25
+ class getid3_mp3 extends getid3_handler
26
+ {
27
+
28
+ public $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files
29
+
30
+ public function Analyze() {
31
+ $info = &$this->getid3->info;
32
+
33
+ $initialOffset = $info['avdataoffset'];
34
+
35
+ if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) {
36
+ if ($this->allow_bruteforce) {
37
+ $info['error'][] = 'Rescanning file in BruteForce mode';
38
+ $this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info);
39
+ }
40
+ }
41
+
42
+
43
+ if (isset($info['mpeg']['audio']['bitrate_mode'])) {
44
+ $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
45
+ }
46
+
47
+ if (((isset($info['id3v2']['headerlength']) && ($info['avdataoffset'] > $info['id3v2']['headerlength'])) || (!isset($info['id3v2']) && ($info['avdataoffset'] > 0) && ($info['avdataoffset'] != $initialOffset)))) {
48
+
49
+ $synchoffsetwarning = 'Unknown data before synch ';
50
+ if (isset($info['id3v2']['headerlength'])) {
51
+ $synchoffsetwarning .= '(ID3v2 header ends at '.$info['id3v2']['headerlength'].', then '.($info['avdataoffset'] - $info['id3v2']['headerlength']).' bytes garbage, ';
52
+ } elseif ($initialOffset > 0) {
53
+ $synchoffsetwarning .= '(should be at '.$initialOffset.', ';
54
+ } else {
55
+ $synchoffsetwarning .= '(should be at beginning of file, ';
56
+ }
57
+ $synchoffsetwarning .= 'synch detected at '.$info['avdataoffset'].')';
58
+ if (isset($info['audio']['bitrate_mode']) && ($info['audio']['bitrate_mode'] == 'cbr')) {
59
+
60
+ if (!empty($info['id3v2']['headerlength']) && (($info['avdataoffset'] - $info['id3v2']['headerlength']) == $info['mpeg']['audio']['framelength'])) {
61
+
62
+ $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.';
63
+ $info['audio']['codec'] = 'LAME';
64
+ $CurrentDataLAMEversionString = 'LAME3.';
65
+
66
+ } elseif (empty($info['id3v2']['headerlength']) && ($info['avdataoffset'] == $info['mpeg']['audio']['framelength'])) {
67
+
68
+ $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.';
69
+ $info['audio']['codec'] = 'LAME';
70
+ $CurrentDataLAMEversionString = 'LAME3.';
71
+
72
+ }
73
+
74
+ }
75
+ $info['warning'][] = $synchoffsetwarning;
76
+
77
+ }
78
+
79
+ if (isset($info['mpeg']['audio']['LAME'])) {
80
+ $info['audio']['codec'] = 'LAME';
81
+ if (!empty($info['mpeg']['audio']['LAME']['long_version'])) {
82
+ $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['long_version'], "\x00");
83
+ } elseif (!empty($info['mpeg']['audio']['LAME']['short_version'])) {
84
+ $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['short_version'], "\x00");
85
+ }
86
+ }
87
+
88
+ $CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : (isset($info['audio']['encoder']) ? $info['audio']['encoder'] : ''));
89
+ if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) {
90
+ // a version number of LAME that does not end with a number like "LAME3.92"
91
+ // or with a closing parenthesis like "LAME3.88 (alpha)"
92
+ // or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92)
93
+
94
+ // not sure what the actual last frame length will be, but will be less than or equal to 1441
95
+ $PossiblyLongerLAMEversion_FrameLength = 1441;
96
+
97
+ // Not sure what version of LAME this is - look in padding of last frame for longer version string
98
+ $PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
99
+ $this->fseek($PossibleLAMEversionStringOffset);
100
+ $PossiblyLongerLAMEversion_Data = $this->fread($PossiblyLongerLAMEversion_FrameLength);
101
+ switch (substr($CurrentDataLAMEversionString, -1)) {
102
+ case 'a':
103
+ case 'b':
104
+ // "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example
105
+ // need to trim off "a" to match longer string
106
+ $CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1);
107
+ break;
108
+ }
109
+ if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) {
110
+ if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) {
111
+ $PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3" "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)"
112
+ if (empty($info['audio']['encoder']) || (strlen($PossiblyLongerLAMEversion_NewString) > strlen($info['audio']['encoder']))) {
113
+ $info['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString;
114
+ }
115
+ }
116
+ }
117
+ }
118
+ if (!empty($info['audio']['encoder'])) {
119
+ $info['audio']['encoder'] = rtrim($info['audio']['encoder'], "\x00 ");
120
+ }
121
+
122
+ switch (isset($info['mpeg']['audio']['layer']) ? $info['mpeg']['audio']['layer'] : '') {
123
+ case 1:
124
+ case 2:
125
+ $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
126
+ break;
127
+ }
128
+ if (isset($info['fileformat']) && ($info['fileformat'] == 'mp3')) {
129
+ switch ($info['audio']['dataformat']) {
130
+ case 'mp1':
131
+ case 'mp2':
132
+ case 'mp3':
133
+ $info['fileformat'] = $info['audio']['dataformat'];
134
+ break;
135
+
136
+ default:
137
+ $info['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"';
138
+ break;
139
+ }
140
+ }
141
+
142
+ if (empty($info['fileformat'])) {
143
+ unset($info['fileformat']);
144
+ unset($info['audio']['bitrate_mode']);
145
+ unset($info['avdataoffset']);
146
+ unset($info['avdataend']);
147
+ return false;
148
+ }
149
+
150
+ $info['mime_type'] = 'audio/mpeg';
151
+ $info['audio']['lossless'] = false;
152
+
153
+ // Calculate playtime
154
+ if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
155
+ $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
156
+ }
157
+
158
+ $info['audio']['encoder_options'] = $this->GuessEncoderOptions();
159
+
160
+ return true;
161
+ }
162
+
163
+
164
+ public function GuessEncoderOptions() {
165
+ // shortcuts
166
+ $info = &$this->getid3->info;
167
+ if (!empty($info['mpeg']['audio'])) {
168
+ $thisfile_mpeg_audio = &$info['mpeg']['audio'];
169
+ if (!empty($thisfile_mpeg_audio['LAME'])) {
170
+ $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
171
+ }
172
+ }
173
+
174
+ $encoder_options = '';
175
+ static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256);
176
+
177
+ if (isset($thisfile_mpeg_audio['VBR_method']) && ($thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {
178
+
179
+ $encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
180
+
181
+ } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
182
+
183
+ $encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
184
+
185
+ } elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {
186
+
187
+ static $KnownEncoderValues = array();
188
+ if (empty($KnownEncoderValues)) {
189
+
190
+ //$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name';
191
+ $KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane'; // 3.90, 3.90.1, 3.92
192
+ $KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane'; // 3.90.2, 3.90.3, 3.91
193
+ $KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane'; // 3.94, 3.95
194
+ $KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme'; // 3.90, 3.90.1, 3.92
195
+ $KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme'; // 3.90.2, 3.91
196
+ $KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme'; // 3.90.3
197
+ $KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme'; // 3.90, 3.90.1, 3.92
198
+ $KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme'; // 3.90.2, 3.90.3, 3.91
199
+ $KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
200
+ $KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard'; // 3.90.3
201
+ $KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
202
+ $KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3
203
+ $KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix'; // 3.90, 3.90.1, 3.92
204
+ $KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix'; // 3.90.2, 3.90.3, 3.91
205
+ $KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix'; // 3.94, 3.95
206
+ $KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium'; // 3.90.3
207
+ $KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium'; // 3.90.3
208
+
209
+ $KnownEncoderValues[0xFF][99][1][1][1][2][0] = '--preset studio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
210
+ $KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio'; // 3.90.3, 3.93.1
211
+ $KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio'; // 3.93
212
+ $KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio'; // 3.94, 3.95
213
+ $KnownEncoderValues[0xC0][88][1][1][1][2][0] = '--preset cd'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
214
+ $KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd'; // 3.90.3, 3.93.1
215
+ $KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd'; // 3.93
216
+ $KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd'; // 3.94, 3.95
217
+ $KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
218
+ $KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi'; // 3.90.3, 3.93, 3.93.1
219
+ $KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi'; // 3.94, 3.95
220
+ $KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
221
+ $KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
222
+ $KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
223
+ $KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm'; // 3.90.3, 3.93, 3.93.1
224
+ $KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm'; // 3.94, 3.95
225
+ $KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice'; // 3.90.3, 3.93, 3.93.1
226
+ $KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice'; // 3.94, 3.95
227
+ $KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice'; // 3.94a14
228
+ $KnownEncoderValues[0x28][65][1][1][0][2][7500] = '--preset mw-us'; // 3.90, 3.90.1, 3.92
229
+ $KnownEncoderValues[0x28][65][1][1][0][2][7600] = '--preset mw-us'; // 3.90.2, 3.91
230
+ $KnownEncoderValues[0x28][58][2][2][0][2][7000] = '--preset mw-us'; // 3.90.3, 3.93, 3.93.1
231
+ $KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us'; // 3.94, 3.95
232
+ $KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us'; // 3.94a14
233
+ $KnownEncoderValues[0x28][57][2][1][0][4][8800] = '--preset mw-us'; // 3.94a15
234
+ $KnownEncoderValues[0x18][58][2][2][0][2][4000] = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1
235
+ $KnownEncoderValues[0x18][58][2][2][0][2][3900] = '--preset phon+/lw/mw-eu/sw'; // 3.93
236
+ $KnownEncoderValues[0x18][57][2][1][0][4][5900] = '--preset phon+/lw/mw-eu/sw'; // 3.94, 3.95
237
+ $KnownEncoderValues[0x18][57][2][1][0][4][6200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a14
238
+ $KnownEncoderValues[0x18][57][2][1][0][4][3200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a15
239
+ $KnownEncoderValues[0x10][58][2][2][0][2][3800] = '--preset phone'; // 3.90.3, 3.93.1
240
+ $KnownEncoderValues[0x10][58][2][2][0][2][3700] = '--preset phone'; // 3.93
241
+ $KnownEncoderValues[0x10][57][2][1][0][4][5600] = '--preset phone'; // 3.94, 3.95
242
+ }
243
+
244
+ if (isset($KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
245
+
246
+ $encoder_options = $KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
247
+
248
+ } elseif (isset($KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
249
+
250
+ $encoder_options = $KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
251
+
252
+ } elseif ($info['audio']['bitrate_mode'] == 'vbr') {
253
+
254
+ // http://gabriel.mp3-tech.org/mp3infotag.html
255
+ // int Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h
256
+
257
+
258
+ $LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10);
259
+ $LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10);
260
+ $encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value;
261
+
262
+ } elseif ($info['audio']['bitrate_mode'] == 'cbr') {
263
+
264
+ $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
265
+
266
+ } else {
267
+
268
+ $encoder_options = strtoupper($info['audio']['bitrate_mode']);
269
+
270
+ }
271
+
272
+ } elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) {
273
+
274
+ $encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr'];
275
+
276
+ } elseif (!empty($info['audio']['bitrate'])) {
277
+
278
+ if ($info['audio']['bitrate_mode'] == 'cbr') {
279
+ $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
280
+ } else {
281
+ $encoder_options = strtoupper($info['audio']['bitrate_mode']);
282
+ }
283
+
284
+ }
285
+ if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) {
286
+ $encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min'];
287
+ }
288
+
289
+ if (!empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']) || !empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'])) {
290
+ $encoder_options .= ' --nogap';
291
+ }
292
+
293
+ if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) {
294
+ $ExplodedOptions = explode(' ', $encoder_options, 4);
295
+ if ($ExplodedOptions[0] == '--r3mix') {
296
+ $ExplodedOptions[1] = 'r3mix';
297
+ }
298
+ switch ($ExplodedOptions[0]) {
299
+ case '--preset':
300
+ case '--alt-preset':
301
+ case '--r3mix':
302
+ if ($ExplodedOptions[1] == 'fast') {
303
+ $ExplodedOptions[1] .= ' '.$ExplodedOptions[2];
304
+ }
305
+ switch ($ExplodedOptions[1]) {
306
+ case 'portable':
307
+ case 'medium':
308
+ case 'standard':
309
+ case 'extreme':
310
+ case 'insane':
311
+ case 'fast portable':
312
+ case 'fast medium':
313
+ case 'fast standard':
314
+ case 'fast extreme':
315
+ case 'fast insane':
316
+ case 'r3mix':
317
+ static $ExpectedLowpass = array(
318
+ 'insane|20500' => 20500,
319
+ 'insane|20600' => 20600, // 3.90.2, 3.90.3, 3.91
320
+ 'medium|18000' => 18000,
321
+ 'fast medium|18000' => 18000,
322
+ 'extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95
323
+ 'extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1
324
+ 'fast extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95
325
+ 'fast extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1
326
+ 'standard|19000' => 19000,
327
+ 'fast standard|19000' => 19000,
328
+ 'r3mix|19500' => 19500, // 3.90, 3.90.1, 3.92
329
+ 'r3mix|19600' => 19600, // 3.90.2, 3.90.3, 3.91
330
+ 'r3mix|18000' => 18000, // 3.94, 3.95
331
+ );
332
+ if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) {
333
+ $encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency'];
334
+ }
335
+ break;
336
+
337
+ default:
338
+ break;
339
+ }
340
+ break;
341
+ }
342
+ }
343
+
344
+ if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) {
345
+ if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) {
346
+ $encoder_options .= ' --resample 44100';
347
+ } elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) {
348
+ $encoder_options .= ' --resample 48000';
349
+ } elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) {
350
+ switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) {
351
+ case 0: // <= 32000
352
+ // may or may not be same as source frequency - ignore
353
+ break;
354
+ case 1: // 44100
355
+ case 2: // 48000
356
+ case 3: // 48000+
357
+ $ExplodedOptions = explode(' ', $encoder_options, 4);
358
+ switch ($ExplodedOptions[0]) {
359
+ case '--preset':
360
+ case '--alt-preset':
361
+ switch ($ExplodedOptions[1]) {
362
+ case 'fast':
363
+ case 'portable':
364
+ case 'medium':
365
+ case 'standard':
366
+ case 'extreme':
367
+ case 'insane':
368
+ $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
369
+ break;
370
+
371
+ default:
372
+ static $ExpectedResampledRate = array(
373
+ 'phon+/lw/mw-eu/sw|16000' => 16000,
374
+ 'mw-us|24000' => 24000, // 3.95
375
+ 'mw-us|32000' => 32000, // 3.93
376
+ 'mw-us|16000' => 16000, // 3.92
377
+ 'phone|16000' => 16000,
378
+ 'phone|11025' => 11025, // 3.94a15
379
+ 'radio|32000' => 32000, // 3.94a15
380
+ 'fm/radio|32000' => 32000, // 3.92
381
+ 'fm|32000' => 32000, // 3.90
382
+ 'voice|32000' => 32000);
383
+ if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) {
384
+ $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
385
+ }
386
+ break;
387
+ }
388
+ break;
389
+
390
+ case '--r3mix':
391
+ default:
392
+ $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
393
+ break;
394
+ }
395
+ break;
396
+ }
397
+ }
398
+ }
399
+ if (empty($encoder_options) && !empty($info['audio']['bitrate']) && !empty($info['audio']['bitrate_mode'])) {
400
+ //$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
401
+ $encoder_options = strtoupper($info['audio']['bitrate_mode']);
402
+ }
403
+
404
+ return $encoder_options;
405
+ }
406
+
407
+
408
+ public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
409
+ static $MPEGaudioVersionLookup;
410
+ static $MPEGaudioLayerLookup;
411
+ static $MPEGaudioBitrateLookup;
412
+ static $MPEGaudioFrequencyLookup;
413
+ static $MPEGaudioChannelModeLookup;
414
+ static $MPEGaudioModeExtensionLookup;
415
+ static $MPEGaudioEmphasisLookup;
416
+ if (empty($MPEGaudioVersionLookup)) {
417
+ $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
418
+ $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
419
+ $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
420
+ $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray();
421
+ $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray();
422
+ $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
423
+ $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray();
424
+ }
425
+
426
+ if ($this->fseek($offset) != 0) {
427
+ $info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset;
428
+ return false;
429
+ }
430
+ //$headerstring = $this->fread(1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
431
+ $headerstring = $this->fread(226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
432
+
433
+ // MP3 audio frame structure:
434
+ // $aa $aa $aa $aa [$bb $bb] $cc...
435
+ // where $aa..$aa is the four-byte mpeg-audio header (below)
436
+ // $bb $bb is the optional 2-byte CRC
437
+ // and $cc... is the audio data
438
+
439
+ $head4 = substr($headerstring, 0, 4);
440
+ static $MPEGaudioHeaderDecodeCache = array();
441
+ if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
442
+ $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
443
+ } else {
444
+ $MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4);
445
+ $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
446
+ }
447
+
448
+ static $MPEGaudioHeaderValidCache = array();
449
+ if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache
450
+ //$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
451
+ $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false);
452
+ }
453
+
454
+ // shortcut
455
+ if (!isset($info['mpeg']['audio'])) {
456
+ $info['mpeg']['audio'] = array();
457
+ }
458
+ $thisfile_mpeg_audio = &$info['mpeg']['audio'];
459
+
460
+
461
+ if ($MPEGaudioHeaderValidCache[$head4]) {
462
+ $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
463
+ } else {
464
+ $info['error'][] = 'Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset;
465
+ return false;
466
+ }
467
+
468
+ if (!$FastMPEGheaderScan) {
469
+ $thisfile_mpeg_audio['version'] = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']];
470
+ $thisfile_mpeg_audio['layer'] = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']];
471
+
472
+ $thisfile_mpeg_audio['channelmode'] = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']];
473
+ $thisfile_mpeg_audio['channels'] = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2);
474
+ $thisfile_mpeg_audio['sample_rate'] = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']];
475
+ $thisfile_mpeg_audio['protection'] = !$thisfile_mpeg_audio['raw']['protection'];
476
+ $thisfile_mpeg_audio['private'] = (bool) $thisfile_mpeg_audio['raw']['private'];
477
+ $thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']];
478
+ $thisfile_mpeg_audio['copyright'] = (bool) $thisfile_mpeg_audio['raw']['copyright'];
479
+ $thisfile_mpeg_audio['original'] = (bool) $thisfile_mpeg_audio['raw']['original'];
480
+ $thisfile_mpeg_audio['emphasis'] = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']];
481
+
482
+ $info['audio']['channels'] = $thisfile_mpeg_audio['channels'];
483
+ $info['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate'];
484
+
485
+ if ($thisfile_mpeg_audio['protection']) {
486
+ $thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2));
487
+ }
488
+ }
489
+
490
+ if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) {
491
+ // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
492
+ $info['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1';
493
+ $thisfile_mpeg_audio['raw']['bitrate'] = 0;
494
+ }
495
+ $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding'];
496
+ $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']];
497
+
498
+ if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $info['avdataoffset'])) {
499
+ // only skip multiple frame check if free-format bitstream found at beginning of file
500
+ // otherwise is quite possibly simply corrupted data
501
+ $recursivesearch = false;
502
+ }
503
+
504
+ // For Layer 2 there are some combinations of bitrate and mode which are not allowed.
505
+ if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) {
506
+
507
+ $info['audio']['dataformat'] = 'mp2';
508
+ switch ($thisfile_mpeg_audio['channelmode']) {
509
+
510
+ case 'mono':
511
+ if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) {
512
+ // these are ok
513
+ } else {
514
+ $info['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
515
+ return false;
516
+ }
517
+ break;
518
+
519
+ case 'stereo':
520
+ case 'joint stereo':
521
+ case 'dual channel':
522
+ if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) {
523
+ // these are ok
524
+ } else {
525
+ $info['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
526
+ return false;
527
+ }
528
+ break;
529
+
530
+ }
531
+
532
+ }
533
+
534
+
535
+ if ($info['audio']['sample_rate'] > 0) {
536
+ $thisfile_mpeg_audio['framelength'] = self::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $info['audio']['sample_rate']);
537
+ }
538
+
539
+ $nextframetestoffset = $offset + 1;
540
+ if ($thisfile_mpeg_audio['bitrate'] != 'free') {
541
+
542
+ $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
543
+
544
+ if (isset($thisfile_mpeg_audio['framelength'])) {
545
+ $nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength'];
546
+ } else {
547
+ $info['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.';
548
+ return false;
549
+ }
550
+
551
+ }
552
+
553
+ $ExpectedNumberOfAudioBytes = 0;
554
+
555
+ ////////////////////////////////////////////////////////////////////////////////////
556
+ // Variable-bitrate headers
557
+
558
+ if (substr($headerstring, 4 + 32, 4) == 'VBRI') {
559
+ // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
560
+ // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
561
+
562
+ $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
563
+ $thisfile_mpeg_audio['VBR_method'] = 'Fraunhofer';
564
+ $info['audio']['codec'] = 'Fraunhofer';
565
+
566
+ $SideInfoData = substr($headerstring, 4 + 2, 32);
567
+
568
+ $FraunhoferVBROffset = 36;
569
+
570
+ $thisfile_mpeg_audio['VBR_encoder_version'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 4, 2)); // VbriVersion
571
+ $thisfile_mpeg_audio['VBR_encoder_delay'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 6, 2)); // VbriDelay
572
+ $thisfile_mpeg_audio['VBR_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 8, 2)); // VbriQuality
573
+ $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes
574
+ $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames
575
+ $thisfile_mpeg_audio['VBR_seek_offsets'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize
576
+ $thisfile_mpeg_audio['VBR_seek_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale
577
+ $thisfile_mpeg_audio['VBR_entry_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes
578
+ $thisfile_mpeg_audio['VBR_entry_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames
579
+
580
+ $ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes'];
581
+
582
+ $previousbyteoffset = $offset;
583
+ for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) {
584
+ $Fraunhofer_OffsetN = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes']));
585
+ $FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes'];
586
+ $thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']);
587
+ $thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset;
588
+ $previousbyteoffset += $Fraunhofer_OffsetN;
589
+ }
590
+
591
+
592
+ } else {
593
+
594
+ // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
595
+ // depending on MPEG layer and number of channels
596
+
597
+ $VBRidOffset = self::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']);
598
+ $SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4);
599
+
600
+ if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) {
601
+ // 'Xing' is traditional Xing VBR frame
602
+ // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
603
+ // 'Info' *can* legally be used to specify a VBR file as well, however.
604
+
605
+ // http://www.multiweb.cz/twoinches/MP3inside.htm
606
+ //00..03 = "Xing" or "Info"
607
+ //04..07 = Flags:
608
+ // 0x01 Frames Flag set if value for number of frames in file is stored
609
+ // 0x02 Bytes Flag set if value for filesize in bytes is stored
610
+ // 0x04 TOC Flag set if values for TOC are stored
611
+ // 0x08 VBR Scale Flag set if values for VBR scale is stored
612
+ //08..11 Frames: Number of frames in file (including the first Xing/Info one)
613
+ //12..15 Bytes: File length in Bytes
614
+ //16..115 TOC (Table of Contents):
615
+ // Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file.
616
+ // Each Byte has a value according this formula:
617
+ // (TOC[i] / 256) * fileLenInBytes
618
+ // So if song lasts eg. 240 sec. and you want to jump to 60. sec. (and file is 5 000 000 Bytes length) you can use:
619
+ // TOC[(60/240)*100] = TOC[25]
620
+ // and corresponding Byte in file is then approximately at:
621
+ // (TOC[25]/256) * 5000000
622
+ //116..119 VBR Scale
623
+
624
+
625
+ // should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME
626
+ // if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') {
627
+ $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
628
+ $thisfile_mpeg_audio['VBR_method'] = 'Xing';
629
+ // } else {
630
+ // $ScanAsCBR = true;
631
+ // $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
632
+ // }
633
+
634
+ $thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4));
635
+
636
+ $thisfile_mpeg_audio['xing_flags']['frames'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001);
637
+ $thisfile_mpeg_audio['xing_flags']['bytes'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002);
638
+ $thisfile_mpeg_audio['xing_flags']['toc'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004);
639
+ $thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008);
640
+
641
+ if ($thisfile_mpeg_audio['xing_flags']['frames']) {
642
+ $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 8, 4));
643
+ //$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame
644
+ }
645
+ if ($thisfile_mpeg_audio['xing_flags']['bytes']) {
646
+ $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4));
647
+ }
648
+
649
+ //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
650
+ //if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
651
+ if (!empty($thisfile_mpeg_audio['VBR_frames'])) {
652
+ $used_filesize = 0;
653
+ if (!empty($thisfile_mpeg_audio['VBR_bytes'])) {
654
+ $used_filesize = $thisfile_mpeg_audio['VBR_bytes'];
655
+ } elseif (!empty($info['filesize'])) {
656
+ $used_filesize = $info['filesize'];
657
+ $used_filesize -= intval(@$info['id3v2']['headerlength']);
658
+ $used_filesize -= (isset($info['id3v1']) ? 128 : 0);
659
+ $used_filesize -= (isset($info['tag_offset_end']) ? $info['tag_offset_end'] - $info['tag_offset_start'] : 0);
660
+ $info['warning'][] = 'MP3.Xing header missing VBR_bytes, assuming MPEG audio portion of file is '.number_format($used_filesize).' bytes';
661
+ }
662
+
663
+ $framelengthfloat = $used_filesize / $thisfile_mpeg_audio['VBR_frames'];
664
+
665
+ if ($thisfile_mpeg_audio['layer'] == '1') {
666
+ // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
667
+ //$info['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
668
+ $info['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12;
669
+ } else {
670
+ // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
671
+ //$info['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
672
+ $info['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144;
673
+ }
674
+ $thisfile_mpeg_audio['framelength'] = floor($framelengthfloat);
675
+ }
676
+
677
+ if ($thisfile_mpeg_audio['xing_flags']['toc']) {
678
+ $LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
679
+ for ($i = 0; $i < 100; $i++) {
680
+ $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
681
+ }
682
+ }
683
+ if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
684
+ $thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
685
+ }
686
+
687
+
688
+ // http://gabriel.mp3-tech.org/mp3infotag.html
689
+ if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') {
690
+
691
+ // shortcut
692
+ $thisfile_mpeg_audio['LAME'] = array();
693
+ $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
694
+
695
+
696
+ $thisfile_mpeg_audio_lame['long_version'] = substr($headerstring, $VBRidOffset + 120, 20);
697
+ $thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
698
+
699
+ if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
700
+
701
+ // extra 11 chars are not part of version string when LAMEtag present
702
+ unset($thisfile_mpeg_audio_lame['long_version']);
703
+
704
+ // It the LAME tag was only introduced in LAME v3.90
705
+ // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
706
+
707
+ // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
708
+ // are assuming a 'Xing' identifier offset of 0x24, which is the case for
709
+ // MPEG-1 non-mono, but not for other combinations
710
+ $LAMEtagOffsetContant = $VBRidOffset - 0x24;
711
+
712
+ // shortcuts
713
+ $thisfile_mpeg_audio_lame['RGAD'] = array('track'=>array(), 'album'=>array());
714
+ $thisfile_mpeg_audio_lame_RGAD = &$thisfile_mpeg_audio_lame['RGAD'];
715
+ $thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track'];
716
+ $thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album'];
717
+ $thisfile_mpeg_audio_lame['raw'] = array();
718
+ $thisfile_mpeg_audio_lame_raw = &$thisfile_mpeg_audio_lame['raw'];
719
+
720
+ // byte $9B VBR Quality
721
+ // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
722
+ // Actually overwrites original Xing bytes
723
+ unset($thisfile_mpeg_audio['VBR_scale']);
724
+ $thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1));
725
+
726
+ // bytes $9C-$A4 Encoder short VersionString
727
+ $thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9);
728
+
729
+ // byte $A5 Info Tag revision + VBR method
730
+ $LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1));
731
+
732
+ $thisfile_mpeg_audio_lame['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
733
+ $thisfile_mpeg_audio_lame_raw['vbr_method'] = $LAMEtagRevisionVBRmethod & 0x0F;
734
+ $thisfile_mpeg_audio_lame['vbr_method'] = self::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
735
+ $thisfile_mpeg_audio['bitrate_mode'] = substr($thisfile_mpeg_audio_lame['vbr_method'], 0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr'
736
+
737
+ // byte $A6 Lowpass filter value
738
+ $thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100;
739
+
740
+ // bytes $A7-$AE Replay Gain
741
+ // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
742
+ // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
743
+ if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') {
744
+ // LAME 3.94a16 and later - 9.23 fixed point
745
+ // ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375
746
+ $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608);
747
+ } else {
748
+ // LAME 3.94a15 and earlier - 32-bit floating point
749
+ // Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15
750
+ $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4));
751
+ }
752
+ if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) {
753
+ unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
754
+ } else {
755
+ $thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
756
+ }
757
+
758
+ $thisfile_mpeg_audio_lame_raw['RGAD_track'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2));
759
+ $thisfile_mpeg_audio_lame_raw['RGAD_album'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2));
760
+
761
+
762
+ if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) {
763
+
764
+ $thisfile_mpeg_audio_lame_RGAD_track['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13;
765
+ $thisfile_mpeg_audio_lame_RGAD_track['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10;
766
+ $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9;
767
+ $thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF;
768
+ $thisfile_mpeg_audio_lame_RGAD_track['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']);
769
+ $thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']);
770
+ $thisfile_mpeg_audio_lame_RGAD_track['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']);
771
+
772
+ if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
773
+ $info['replay_gain']['track']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
774
+ }
775
+ $info['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator'];
776
+ $info['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db'];
777
+ } else {
778
+ unset($thisfile_mpeg_audio_lame_RGAD['track']);
779
+ }
780
+ if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) {
781
+
782
+ $thisfile_mpeg_audio_lame_RGAD_album['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13;
783
+ $thisfile_mpeg_audio_lame_RGAD_album['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10;
784
+ $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9;
785
+ $thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF;
786
+ $thisfile_mpeg_audio_lame_RGAD_album['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']);
787
+ $thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']);
788
+ $thisfile_mpeg_audio_lame_RGAD_album['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']);
789
+
790
+ if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
791
+ $info['replay_gain']['album']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
792
+ }
793
+ $info['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator'];
794
+ $info['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db'];
795
+ } else {
796
+ unset($thisfile_mpeg_audio_lame_RGAD['album']);
797
+ }
798
+ if (empty($thisfile_mpeg_audio_lame_RGAD)) {
799
+ unset($thisfile_mpeg_audio_lame['RGAD']);
800
+ }
801
+
802
+
803
+ // byte $AF Encoding flags + ATH Type
804
+ $EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1));
805
+ $thisfile_mpeg_audio_lame['encoding_flags']['nspsytune'] = (bool) ($EncodingFlagsATHtype & 0x10);
806
+ $thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20);
807
+ $thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'] = (bool) ($EncodingFlagsATHtype & 0x40);
808
+ $thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] = (bool) ($EncodingFlagsATHtype & 0x80);
809
+ $thisfile_mpeg_audio_lame['ath_type'] = $EncodingFlagsATHtype & 0x0F;
810
+
811
+ // byte $B0 if ABR {specified bitrate} else {minimal bitrate}
812
+ $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1));
813
+ if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR)
814
+ $thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
815
+ } elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR)
816
+ // ignore
817
+ } elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate
818
+ $thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
819
+ }
820
+
821
+ // bytes $B1-$B3 Encoder delays
822
+ $EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3));
823
+ $thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12;
824
+ $thisfile_mpeg_audio_lame['end_padding'] = $EncoderDelays & 0x000FFF;
825
+
826
+ // byte $B4 Misc
827
+ $MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1));
828
+ $thisfile_mpeg_audio_lame_raw['noise_shaping'] = ($MiscByte & 0x03);
829
+ $thisfile_mpeg_audio_lame_raw['stereo_mode'] = ($MiscByte & 0x1C) >> 2;
830
+ $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
831
+ $thisfile_mpeg_audio_lame_raw['source_sample_freq'] = ($MiscByte & 0xC0) >> 6;
832
+ $thisfile_mpeg_audio_lame['noise_shaping'] = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
833
+ $thisfile_mpeg_audio_lame['stereo_mode'] = self::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
834
+ $thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
835
+ $thisfile_mpeg_audio_lame['source_sample_freq'] = self::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);
836
+
837
+ // byte $B5 MP3 Gain
838
+ $thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true);
839
+ $thisfile_mpeg_audio_lame['mp3_gain_db'] = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
840
+ $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));
841
+
842
+ // bytes $B6-$B7 Preset and surround info
843
+ $PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2));
844
+ // Reserved = ($PresetSurroundBytes & 0xC000);
845
+ $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
846
+ $thisfile_mpeg_audio_lame['surround_info'] = self::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
847
+ $thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF);
848
+ $thisfile_mpeg_audio_lame['preset_used'] = self::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
849
+ if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
850
+ $info['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org';
851
+ }
852
+ if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
853
+ // this may change if 3.90.4 ever comes out
854
+ $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
855
+ }
856
+
857
+ // bytes $B8-$BB MusicLength
858
+ $thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4));
859
+ $ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);
860
+
861
+ // bytes $BC-$BD MusicCRC
862
+ $thisfile_mpeg_audio_lame['music_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2));
863
+
864
+ // bytes $BE-$BF CRC-16 of Info Tag
865
+ $thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2));
866
+
867
+
868
+ // LAME CBR
869
+ if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) {
870
+
871
+ $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
872
+ $thisfile_mpeg_audio['bitrate'] = self::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
873
+ $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
874
+ //if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) {
875
+ // $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min'];
876
+ //}
877
+
878
+ }
879
+
880
+ }
881
+ }
882
+
883
+ } else {
884
+
885
+ // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
886
+ $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
887
+ if ($recursivesearch) {
888
+ $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
889
+ if ($this->RecursiveFrameScanning($offset, $nextframetestoffset, true)) {
890
+ $recursivesearch = false;
891
+ $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
892
+ }
893
+ if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') {
894
+ $info['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.';
895
+ }
896
+ }
897
+
898
+ }
899
+
900
+ }
901
+
902
+ if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) {
903
+ if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) {
904
+ if ($this->isDependencyFor('matroska') || $this->isDependencyFor('riff')) {
905
+ // ignore, audio data is broken into chunks so will always be data "missing"
906
+ }
907
+ elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) {
908
+ $this->warning('Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)');
909
+ }
910
+ else {
911
+ $this->warning('Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)');
912
+ }
913
+ } else {
914
+ if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) {
915
+ // $prenullbytefileoffset = $this->ftell();
916
+ // $this->fseek($info['avdataend']);
917
+ // $PossibleNullByte = $this->fread(1);
918
+ // $this->fseek($prenullbytefileoffset);
919
+ // if ($PossibleNullByte === "\x00") {
920
+ $info['avdataend']--;
921
+ // $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
922
+ // } else {
923
+ // $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
924
+ // }
925
+ } else {
926
+ $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
927
+ }
928
+ }
929
+ }
930
+
931
+ if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) {
932
+ if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) {
933
+ $framebytelength = $this->FreeFormatFrameLength($offset, true);
934
+ if ($framebytelength > 0) {
935
+ $thisfile_mpeg_audio['framelength'] = $framebytelength;
936
+ if ($thisfile_mpeg_audio['layer'] == '1') {
937
+ // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
938
+ $info['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
939
+ } else {
940
+ // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
941
+ $info['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
942
+ }
943
+ } else {
944
+ $info['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header';
945
+ }
946
+ }
947
+ }
948
+
949
+ if (isset($thisfile_mpeg_audio['VBR_frames']) ? $thisfile_mpeg_audio['VBR_frames'] : '') {
950
+ switch ($thisfile_mpeg_audio['bitrate_mode']) {
951
+ case 'vbr':
952
+ case 'abr':
953
+ $bytes_per_frame = 1152;
954
+ if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) {
955
+ $bytes_per_frame = 384;
956
+ } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) {
957
+ $bytes_per_frame = 576;
958
+ }
959
+ $thisfile_mpeg_audio['VBR_bitrate'] = (isset($thisfile_mpeg_audio['VBR_bytes']) ? (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / $bytes_per_frame) : 0);
960
+ if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
961
+ $info['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate'];
962
+ $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
963
+ }
964
+ break;
965
+ }
966
+ }
967
+
968
+ // End variable-bitrate headers
969
+ ////////////////////////////////////////////////////////////////////////////////////
970
+
971
+ if ($recursivesearch) {
972
+
973
+ if (!$this->RecursiveFrameScanning($offset, $nextframetestoffset, $ScanAsCBR)) {
974
+ return false;
975
+ }
976
+
977
+ }
978
+
979
+
980
+ //if (false) {
981
+ // // experimental side info parsing section - not returning anything useful yet
982
+ //
983
+ // $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData);
984
+ // $SideInfoOffset = 0;
985
+ //
986
+ // if ($thisfile_mpeg_audio['version'] == '1') {
987
+ // if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
988
+ // // MPEG-1 (mono)
989
+ // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
990
+ // $SideInfoOffset += 9;
991
+ // $SideInfoOffset += 5;
992
+ // } else {
993
+ // // MPEG-1 (stereo, joint-stereo, dual-channel)
994
+ // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
995
+ // $SideInfoOffset += 9;
996
+ // $SideInfoOffset += 3;
997
+ // }
998
+ // } else { // 2 or 2.5
999
+ // if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
1000
+ // // MPEG-2, MPEG-2.5 (mono)
1001
+ // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
1002
+ // $SideInfoOffset += 8;
1003
+ // $SideInfoOffset += 1;
1004
+ // } else {
1005
+ // // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
1006
+ // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
1007
+ // $SideInfoOffset += 8;
1008
+ // $SideInfoOffset += 2;
1009
+ // }
1010
+ // }
1011
+ //
1012
+ // if ($thisfile_mpeg_audio['version'] == '1') {
1013
+ // for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
1014
+ // for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
1015
+ // $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1016
+ // $SideInfoOffset += 2;
1017
+ // }
1018
+ // }
1019
+ // }
1020
+ // for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) {
1021
+ // for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
1022
+ // $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
1023
+ // $SideInfoOffset += 12;
1024
+ // $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1025
+ // $SideInfoOffset += 9;
1026
+ // $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
1027
+ // $SideInfoOffset += 8;
1028
+ // if ($thisfile_mpeg_audio['version'] == '1') {
1029
+ // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1030
+ // $SideInfoOffset += 4;
1031
+ // } else {
1032
+ // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1033
+ // $SideInfoOffset += 9;
1034
+ // }
1035
+ // $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1036
+ // $SideInfoOffset += 1;
1037
+ //
1038
+ // if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') {
1039
+ //
1040
+ // $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
1041
+ // $SideInfoOffset += 2;
1042
+ // $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1043
+ // $SideInfoOffset += 1;
1044
+ //
1045
+ // for ($region = 0; $region < 2; $region++) {
1046
+ // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1047
+ // $SideInfoOffset += 5;
1048
+ // }
1049
+ // $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0;
1050
+ //
1051
+ // for ($window = 0; $window < 3; $window++) {
1052
+ // $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1053
+ // $SideInfoOffset += 3;
1054
+ // }
1055
+ //
1056
+ // } else {
1057
+ //
1058
+ // for ($region = 0; $region < 3; $region++) {
1059
+ // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1060
+ // $SideInfoOffset += 5;
1061
+ // }
1062
+ //
1063
+ // $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1064
+ // $SideInfoOffset += 4;
1065
+ // $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1066
+ // $SideInfoOffset += 3;
1067
+ // $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0;
1068
+ // }
1069
+ //
1070
+ // if ($thisfile_mpeg_audio['version'] == '1') {
1071
+ // $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1072
+ // $SideInfoOffset += 1;
1073
+ // }
1074
+ // $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1075
+ // $SideInfoOffset += 1;
1076
+ // $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1077
+ // $SideInfoOffset += 1;
1078
+ // }
1079
+ // }
1080
+ //}
1081
+
1082
+ return true;
1083
+ }
1084
+
1085
+ public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
1086
+ $info = &$this->getid3->info;
1087
+ $firstframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1088
+ $this->decodeMPEGaudioHeader($offset, $firstframetestarray, false);
1089
+
1090
+ for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) {
1091
+ // check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
1092
+ if (($nextframetestoffset + 4) >= $info['avdataend']) {
1093
+ // end of file
1094
+ return true;
1095
+ }
1096
+
1097
+ $nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1098
+ if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) {
1099
+ if ($ScanAsCBR) {
1100
+ // force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header
1101
+ if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) {
1102
+ return false;
1103
+ }
1104
+ }
1105
+
1106
+
1107
+ // next frame is OK, get ready to check the one after that
1108
+ if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) {
1109
+ $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
1110
+ } else {
1111
+ $info['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.';
1112
+ return false;
1113
+ }
1114
+
1115
+ } elseif (!empty($firstframetestarray['mpeg']['audio']['framelength']) && (($nextframetestoffset + $firstframetestarray['mpeg']['audio']['framelength']) > $info['avdataend'])) {
1116
+
1117
+ // it's not the end of the file, but there's not enough data left for another frame, so assume it's garbage/padding and return OK
1118
+ return true;
1119
+
1120
+ } else {
1121
+
1122
+ // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
1123
+ $info['warning'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.';
1124
+
1125
+ return false;
1126
+ }
1127
+ }
1128
+ return true;
1129
+ }
1130
+
1131
+ public function FreeFormatFrameLength($offset, $deepscan=false) {
1132
+ $info = &$this->getid3->info;
1133
+
1134
+ $this->fseek($offset);
1135
+ $MPEGaudioData = $this->fread(32768);
1136
+
1137
+ $SyncPattern1 = substr($MPEGaudioData, 0, 4);
1138
+ // may be different pattern due to padding
1139
+ $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
1140
+ if ($SyncPattern2 === $SyncPattern1) {
1141
+ $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
1142
+ }
1143
+
1144
+ $framelength = false;
1145
+ $framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
1146
+ $framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
1147
+ if ($framelength1 > 4) {
1148
+ $framelength = $framelength1;
1149
+ }
1150
+ if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1151
+ $framelength = $framelength2;
1152
+ }
1153
+ if (!$framelength) {
1154
+
1155
+ // LAME 3.88 has a different value for modeextension on the first frame vs the rest
1156
+ $framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4);
1157
+ $framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4);
1158
+
1159
+ if ($framelength1 > 4) {
1160
+ $framelength = $framelength1;
1161
+ }
1162
+ if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1163
+ $framelength = $framelength2;
1164
+ }
1165
+ if (!$framelength) {
1166
+ $info['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset;
1167
+ return false;
1168
+ } else {
1169
+ $info['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)';
1170
+ $info['audio']['codec'] = 'LAME';
1171
+ $info['audio']['encoder'] = 'LAME3.88';
1172
+ $SyncPattern1 = substr($SyncPattern1, 0, 3);
1173
+ $SyncPattern2 = substr($SyncPattern2, 0, 3);
1174
+ }
1175
+ }
1176
+
1177
+ if ($deepscan) {
1178
+
1179
+ $ActualFrameLengthValues = array();
1180
+ $nextoffset = $offset + $framelength;
1181
+ while ($nextoffset < ($info['avdataend'] - 6)) {
1182
+ $this->fseek($nextoffset - 1);
1183
+ $NextSyncPattern = $this->fread(6);
1184
+ if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) {
1185
+ // good - found where expected
1186
+ $ActualFrameLengthValues[] = $framelength;
1187
+ } elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) {
1188
+ // ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
1189
+ $ActualFrameLengthValues[] = ($framelength - 1);
1190
+ $nextoffset--;
1191
+ } elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) {
1192
+ // ok - found one byte later than expected (last frame was padded, first frame wasn't)
1193
+ $ActualFrameLengthValues[] = ($framelength + 1);
1194
+ $nextoffset++;
1195
+ } else {
1196
+ $info['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset;
1197
+ return false;
1198
+ }
1199
+ $nextoffset += $framelength;
1200
+ }
1201
+ if (count($ActualFrameLengthValues) > 0) {
1202
+ $framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues)));
1203
+ }
1204
+ }
1205
+ return $framelength;
1206
+ }
1207
+
1208
+ public function getOnlyMPEGaudioInfoBruteForce() {
1209
+ $MPEGaudioHeaderDecodeCache = array();
1210
+ $MPEGaudioHeaderValidCache = array();
1211
+ $MPEGaudioHeaderLengthCache = array();
1212
+ $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
1213
+ $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
1214
+ $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
1215
+ $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray();
1216
+ $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray();
1217
+ $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
1218
+ $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray();
1219
+ $LongMPEGversionLookup = array();
1220
+ $LongMPEGlayerLookup = array();
1221
+ $LongMPEGbitrateLookup = array();
1222
+ $LongMPEGpaddingLookup = array();
1223
+ $LongMPEGfrequencyLookup = array();
1224
+ $Distribution['bitrate'] = array();
1225
+ $Distribution['frequency'] = array();
1226
+ $Distribution['layer'] = array();
1227
+ $Distribution['version'] = array();
1228
+ $Distribution['padding'] = array();
1229
+
1230
+ $info = &$this->getid3->info;
1231
+ $this->fseek($info['avdataoffset']);
1232
+
1233
+ $max_frames_scan = 5000;
1234
+ $frames_scanned = 0;
1235
+
1236
+ $previousvalidframe = $info['avdataoffset'];
1237
+ while ($this->ftell() < $info['avdataend']) {
1238
+ set_time_limit(30);
1239
+ $head4 = $this->fread(4);
1240
+ if (strlen($head4) < 4) {
1241
+ break;
1242
+ }
1243
+ if ($head4{0} != "\xFF") {
1244
+ for ($i = 1; $i < 4; $i++) {
1245
+ if ($head4{$i} == "\xFF") {
1246
+ $this->fseek($i - 4, SEEK_CUR);
1247
+ continue 2;
1248
+ }
1249
+ }
1250
+ continue;
1251
+ }
1252
+ if (!isset($MPEGaudioHeaderDecodeCache[$head4])) {
1253
+ $MPEGaudioHeaderDecodeCache[$head4] = self::MPEGaudioHeaderDecode($head4);
1254
+ }
1255
+ if (!isset($MPEGaudioHeaderValidCache[$head4])) {
1256
+ $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false);
1257
+ }
1258
+ if ($MPEGaudioHeaderValidCache[$head4]) {
1259
+
1260
+ if (!isset($MPEGaudioHeaderLengthCache[$head4])) {
1261
+ $LongMPEGversionLookup[$head4] = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']];
1262
+ $LongMPEGlayerLookup[$head4] = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']];
1263
+ $LongMPEGbitrateLookup[$head4] = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']];
1264
+ $LongMPEGpaddingLookup[$head4] = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding'];
1265
+ $LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']];
1266
+ $MPEGaudioHeaderLengthCache[$head4] = self::MPEGaudioFrameLength(
1267
+ $LongMPEGbitrateLookup[$head4],
1268
+ $LongMPEGversionLookup[$head4],
1269
+ $LongMPEGlayerLookup[$head4],
1270
+ $LongMPEGpaddingLookup[$head4],
1271
+ $LongMPEGfrequencyLookup[$head4]);
1272
+ }
1273
+ if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
1274
+ $WhereWeWere = $this->ftell();
1275
+ $this->fseek($MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
1276
+ $next4 = $this->fread(4);
1277
+ if ($next4{0} == "\xFF") {
1278
+ if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
1279
+ $MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
1280
+ }
1281
+ if (!isset($MPEGaudioHeaderValidCache[$next4])) {
1282
+ $MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
1283
+ }
1284
+ if ($MPEGaudioHeaderValidCache[$next4]) {
1285
+ $this->fseek(-4, SEEK_CUR);
1286
+
1287
+ getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]);
1288
+ getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]);
1289
+ getid3_lib::safe_inc($Distribution['version'][$LongMPEGversionLookup[$head4]]);
1290
+ getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]);
1291
+ getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]);
1292
+ if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
1293
+ $pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
1294
+ $info['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
1295
+ foreach ($Distribution as $key1 => $value1) {
1296
+ foreach ($value1 as $key2 => $value2) {
1297
+ $Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
1298
+ }
1299
+ }
1300
+ break;
1301
+ }
1302
+ continue;
1303
+ }
1304
+ }
1305
+ unset($next4);
1306
+ $this->fseek($WhereWeWere - 3);
1307
+ }
1308
+
1309
+ }
1310
+ }
1311
+ foreach ($Distribution as $key => $value) {
1312
+ ksort($Distribution[$key], SORT_NUMERIC);
1313
+ }
1314
+ ksort($Distribution['version'], SORT_STRING);
1315
+ $info['mpeg']['audio']['bitrate_distribution'] = $Distribution['bitrate'];
1316
+ $info['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency'];
1317
+ $info['mpeg']['audio']['layer_distribution'] = $Distribution['layer'];
1318
+ $info['mpeg']['audio']['version_distribution'] = $Distribution['version'];
1319
+ $info['mpeg']['audio']['padding_distribution'] = $Distribution['padding'];
1320
+ if (count($Distribution['version']) > 1) {
1321
+ $info['error'][] = 'Corrupt file - more than one MPEG version detected';
1322
+ }
1323
+ if (count($Distribution['layer']) > 1) {
1324
+ $info['error'][] = 'Corrupt file - more than one MPEG layer detected';
1325
+ }
1326
+ if (count($Distribution['frequency']) > 1) {
1327
+ $info['error'][] = 'Corrupt file - more than one MPEG sample rate detected';
1328
+ }
1329
+
1330
+
1331
+ $bittotal = 0;
1332
+ foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) {
1333
+ if ($bitratevalue != 'free') {
1334
+ $bittotal += ($bitratevalue * $bitratecount);
1335
+ }
1336
+ }
1337
+ $info['mpeg']['audio']['frame_count'] = array_sum($Distribution['bitrate']);
1338
+ if ($info['mpeg']['audio']['frame_count'] == 0) {
1339
+ $info['error'][] = 'no MPEG audio frames found';
1340
+ return false;
1341
+ }
1342
+ $info['mpeg']['audio']['bitrate'] = ($bittotal / $info['mpeg']['audio']['frame_count']);
1343
+ $info['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr');
1344
+ $info['mpeg']['audio']['sample_rate'] = getid3_lib::array_max($Distribution['frequency'], true);
1345
+
1346
+ $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1347
+ $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1348
+ $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1349
+ $info['audio']['dataformat'] = 'mp'.getid3_lib::array_max($Distribution['layer'], true);
1350
+ $info['fileformat'] = $info['audio']['dataformat'];
1351
+
1352
+ return true;
1353
+ }
1354
+
1355
+
1356
+ public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) {
1357
+ // looks for synch, decodes MPEG audio header
1358
+
1359
+ $info = &$this->getid3->info;
1360
+
1361
+ static $MPEGaudioVersionLookup;
1362
+ static $MPEGaudioLayerLookup;
1363
+ static $MPEGaudioBitrateLookup;
1364
+ if (empty($MPEGaudioVersionLookup)) {
1365
+ $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
1366
+ $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
1367
+ $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
1368
+
1369
+ }
1370
+
1371
+ $this->fseek($avdataoffset);
1372
+ $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
1373
+ if ($sync_seek_buffer_size <= 0) {
1374
+ $info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset;
1375
+ return false;
1376
+ }
1377
+ $header = $this->fread($sync_seek_buffer_size);
1378
+ $sync_seek_buffer_size = strlen($header);
1379
+ $SynchSeekOffset = 0;
1380
+ while ($SynchSeekOffset < $sync_seek_buffer_size) {
1381
+ if ((($avdataoffset + $SynchSeekOffset) < $info['avdataend']) && !feof($this->getid3->fp)) {
1382
+
1383
+ if ($SynchSeekOffset > $sync_seek_buffer_size) {
1384
+ // if a synch's not found within the first 128k bytes, then give up
1385
+ $info['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB';
1386
+ if (isset($info['audio']['bitrate'])) {
1387
+ unset($info['audio']['bitrate']);
1388
+ }
1389
+ if (isset($info['mpeg']['audio'])) {
1390
+ unset($info['mpeg']['audio']);
1391
+ }
1392
+ if (empty($info['mpeg'])) {
1393
+ unset($info['mpeg']);
1394
+ }
1395
+ return false;
1396
+
1397
+ } elseif (feof($this->getid3->fp)) {
1398
+
1399
+ $info['error'][] = 'Could not find valid MPEG audio synch before end of file';
1400
+ if (isset($info['audio']['bitrate'])) {
1401
+ unset($info['audio']['bitrate']);
1402
+ }
1403
+ if (isset($info['mpeg']['audio'])) {
1404
+ unset($info['mpeg']['audio']);
1405
+ }
1406
+ if (isset($info['mpeg']) && (!is_array($info['mpeg']) || (count($info['mpeg']) == 0))) {
1407
+ unset($info['mpeg']);
1408
+ }
1409
+ return false;
1410
+ }
1411
+ }
1412
+
1413
+ if (($SynchSeekOffset + 1) >= strlen($header)) {
1414
+ $info['error'][] = 'Could not find valid MPEG synch before end of file';
1415
+ return false;
1416
+ }
1417
+
1418
+ if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
1419
+ if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
1420
+ $FirstFrameThisfileInfo = $info;
1421
+ $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
1422
+ if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $FirstFrameThisfileInfo, false)) {
1423
+ // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
1424
+ // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
1425
+ unset($FirstFrameThisfileInfo);
1426
+ }
1427
+ }
1428
+
1429
+ $dummy = $info; // only overwrite real data if valid header found
1430
+ if ($this->decodeMPEGaudioHeader($avdataoffset + $SynchSeekOffset, $dummy, true)) {
1431
+ $info = $dummy;
1432
+ $info['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
1433
+ switch (isset($info['fileformat']) ? $info['fileformat'] : '') {
1434
+ case '':
1435
+ case 'id3':
1436
+ case 'ape':
1437
+ case 'mp3':
1438
+ $info['fileformat'] = 'mp3';
1439
+ $info['audio']['dataformat'] = 'mp3';
1440
+ break;
1441
+ }
1442
+ if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
1443
+ if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
1444
+ // If there is garbage data between a valid VBR header frame and a sequence
1445
+ // of valid MPEG-audio frames the VBR data is no longer discarded.
1446
+ $info = $FirstFrameThisfileInfo;
1447
+ $info['avdataoffset'] = $FirstFrameAVDataOffset;
1448
+ $info['fileformat'] = 'mp3';
1449
+ $info['audio']['dataformat'] = 'mp3';
1450
+ $dummy = $info;
1451
+ unset($dummy['mpeg']['audio']);
1452
+ $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
1453
+ $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset;
1454
+ if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) {
1455
+ $info = $dummy;
1456
+ $info['avdataoffset'] = $GarbageOffsetEnd;
1457
+ $info['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd;
1458
+ } else {
1459
+ $info['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')';
1460
+ }
1461
+ }
1462
+ }
1463
+ if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) {
1464
+ // VBR file with no VBR header
1465
+ $BitrateHistogram = true;
1466
+ }
1467
+
1468
+ if ($BitrateHistogram) {
1469
+
1470
+ $info['mpeg']['audio']['stereo_distribution'] = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0);
1471
+ $info['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0);
1472
+
1473
+ if ($info['mpeg']['audio']['version'] == '1') {
1474
+ if ($info['mpeg']['audio']['layer'] == 3) {
1475
+ $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0);
1476
+ } elseif ($info['mpeg']['audio']['layer'] == 2) {
1477
+ $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0);
1478
+ } elseif ($info['mpeg']['audio']['layer'] == 1) {
1479
+ $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0);
1480
+ }
1481
+ } elseif ($info['mpeg']['audio']['layer'] == 1) {
1482
+ $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0);
1483
+ } else {
1484
+ $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0);
1485
+ }
1486
+
1487
+ $dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1488
+ $synchstartoffset = $info['avdataoffset'];
1489
+ $this->fseek($info['avdataoffset']);
1490
+
1491
+ // you can play with these numbers:
1492
+ $max_frames_scan = 50000;
1493
+ $max_scan_segments = 10;
1494
+
1495
+ // don't play with these numbers:
1496
+ $FastMode = false;
1497
+ $SynchErrorsFound = 0;
1498
+ $frames_scanned = 0;
1499
+ $this_scan_segment = 0;
1500
+ $frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments);
1501
+ $pct_data_scanned = 0;
1502
+ for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) {
1503
+ $frames_scanned_this_segment = 0;
1504
+ if ($this->ftell() >= $info['avdataend']) {
1505
+ break;
1506
+ }
1507
+ $scan_start_offset[$current_segment] = max($this->ftell(), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
1508
+ if ($current_segment > 0) {
1509
+ $this->fseek($scan_start_offset[$current_segment]);
1510
+ $buffer_4k = $this->fread(4096);
1511
+ for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
1512
+ if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
1513
+ if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
1514
+ $calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
1515
+ if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
1516
+ $scan_start_offset[$current_segment] += $j;
1517
+ break;
1518
+ }
1519
+ }
1520
+ }
1521
+ }
1522
+ }
1523
+ $synchstartoffset = $scan_start_offset[$current_segment];
1524
+ while ($this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
1525
+ $FastMode = true;
1526
+ $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
1527
+
1528
+ if (empty($dummy['mpeg']['audio']['framelength'])) {
1529
+ $SynchErrorsFound++;
1530
+ $synchstartoffset++;
1531
+ } else {
1532
+ getid3_lib::safe_inc($info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]);
1533
+ getid3_lib::safe_inc($info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]);
1534
+ getid3_lib::safe_inc($info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]);
1535
+ $synchstartoffset += $dummy['mpeg']['audio']['framelength'];
1536
+ }
1537
+ $frames_scanned++;
1538
+ if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) {
1539
+ $this_pct_scanned = ($this->ftell() - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']);
1540
+ if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) {
1541
+ // file likely contains < $max_frames_scan, just scan as one segment
1542
+ $max_scan_segments = 1;
1543
+ $frames_scan_per_segment = $max_frames_scan;
1544
+ } else {
1545
+ $pct_data_scanned += $this_pct_scanned;
1546
+ break;
1547
+ }
1548
+ }
1549
+ }
1550
+ }
1551
+ if ($pct_data_scanned > 0) {
1552
+ $info['warning'][] = 'too many MPEG audio frames to scan, only scanned '.$frames_scanned.' frames in '.$max_scan_segments.' segments ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
1553
+ foreach ($info['mpeg']['audio'] as $key1 => $value1) {
1554
+ if (!preg_match('#_distribution$#i', $key1)) {
1555
+ continue;
1556
+ }
1557
+ foreach ($value1 as $key2 => $value2) {
1558
+ $info['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned);
1559
+ }
1560
+ }
1561
+ }
1562
+
1563
+ if ($SynchErrorsFound > 0) {
1564
+ $info['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis';
1565
+ //return false;
1566
+ }
1567
+
1568
+ $bittotal = 0;
1569
+ $framecounter = 0;
1570
+ foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) {
1571
+ $framecounter += $bitratecount;
1572
+ if ($bitratevalue != 'free') {
1573
+ $bittotal += ($bitratevalue * $bitratecount);
1574
+ }
1575
+ }
1576
+ if ($framecounter == 0) {
1577
+ $info['error'][] = 'Corrupt MP3 file: framecounter == zero';
1578
+ return false;
1579
+ }
1580
+ $info['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter);
1581
+ $info['mpeg']['audio']['bitrate'] = ($bittotal / $framecounter);
1582
+
1583
+ $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1584
+
1585
+
1586
+ // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
1587
+ $distinct_bitrates = 0;
1588
+ foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) {
1589
+ if ($bitrate_count > 0) {
1590
+ $distinct_bitrates++;
1591
+ }
1592
+ }
1593
+ if ($distinct_bitrates > 1) {
1594
+ $info['mpeg']['audio']['bitrate_mode'] = 'vbr';
1595
+ } else {
1596
+ $info['mpeg']['audio']['bitrate_mode'] = 'cbr';
1597
+ }
1598
+ $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1599
+
1600
+ }
1601
+
1602
+ break; // exit while()
1603
+ }
1604
+ }
1605
+
1606
+ $SynchSeekOffset++;
1607
+ if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) {
1608
+ // end of file/data
1609
+
1610
+ if (empty($info['mpeg']['audio'])) {
1611
+
1612
+ $info['error'][] = 'could not find valid MPEG synch before end of file';
1613
+ if (isset($info['audio']['bitrate'])) {
1614
+ unset($info['audio']['bitrate']);
1615
+ }
1616
+ if (isset($info['mpeg']['audio'])) {
1617
+ unset($info['mpeg']['audio']);
1618
+ }
1619
+ if (isset($info['mpeg']) && (!is_array($info['mpeg']) || empty($info['mpeg']))) {
1620
+ unset(