Version Description
- Replaced codec finding library
- Fixed warning on activities page
Download this release
Release Info
Developer | rtcamp |
Plugin | rtMedia for WordPress, BuddyPress and bbPress |
Version | 2.0.1 |
Comparing to | |
See all releases |
Code changes from version 2.0 to 2.0.1
- includes/bp-media-class-wordpress.php +7 -11
- includes/bp-media-filters.php +7 -8
- includes/bp-media-functions.php +2 -2
- includes/lib/MP4Info.php +0 -257
- includes/lib/MP4Info/Box.php +0 -446
- includes/lib/MP4Info/Box/Container.php +0 -99
- includes/lib/MP4Info/Box/ftyp.php +0 -231
- includes/lib/MP4Info/Box/hdlr.php +0 -140
- includes/lib/MP4Info/Box/ilst.php +0 -122
- includes/lib/MP4Info/Box/ilst_sub.php +0 -148
- includes/lib/MP4Info/Box/mdhd.php +0 -146
- includes/lib/MP4Info/Box/meta.php +0 -69
- includes/lib/MP4Info/Box/mvhd.php +0 -233
- includes/lib/MP4Info/Box/stsd.php +0 -139
- includes/lib/MP4Info/Box/tkhd.php +0 -215
- includes/lib/MP4Info/Box/uuid.php +0 -117
- includes/lib/MP4Info/Exception.php +0 -61
- includes/lib/MP4Info/Helper.php +0 -72
- includes/lib/getid3/getid3.lib.php +1317 -0
- includes/lib/getid3/getid3.php +1744 -0
- includes/lib/getid3/module.audio-video.mpeg.php +299 -0
- includes/lib/getid3/module.audio-video.quicktime.php +2134 -0
- includes/lib/getid3/module.audio.aac.php +515 -0
- includes/lib/getid3/module.audio.ac3.php +473 -0
- includes/lib/getid3/module.audio.mp3.php +2011 -0
- includes/lib/getid3/module.tag.apetag.php +372 -0
- includes/lib/getid3/module.tag.id3v1.php +362 -0
- includes/lib/getid3/module.tag.id3v2.php +3327 -0
- includes/lib/getid3/module.tag.lyrics3.php +297 -0
- loader.php +9 -9
- readme.txt +48 -40
includes/bp-media-class-wordpress.php
CHANGED
@@ -100,23 +100,19 @@ class BP_Media_Host_Wordpress {
|
|
100 |
switch ($type) {
|
101 |
case 'video/mp4' :
|
102 |
$type = 'video';
|
103 |
-
include_once(trailingslashit(BP_MEDIA_PLUGIN_DIR) . 'includes/lib/
|
104 |
try {
|
105 |
-
$
|
106 |
-
|
107 |
-
wp_delete_post($post_id, true);
|
108 |
-
unlink($file);
|
109 |
-
$activity_content = false;
|
110 |
-
throw new Exception(__('MP4 file you have uploaded is currupt.', 'bp-media'));
|
111 |
} catch (Exception $e) {
|
112 |
wp_delete_post($post_id, true);
|
113 |
unlink($file);
|
114 |
$activity_content = false;
|
115 |
throw new Exception(__('MP4 file you have uploaded is currupt.', 'bp-media'));
|
116 |
}
|
117 |
-
if (
|
118 |
-
if (
|
119 |
-
if (!(
|
120 |
wp_delete_post($post_id, true);
|
121 |
unlink($file);
|
122 |
$activity_content = false;
|
@@ -126,7 +122,7 @@ class BP_Media_Host_Wordpress {
|
|
126 |
wp_delete_post($post_id, true);
|
127 |
unlink($file);
|
128 |
$activity_content = false;
|
129 |
-
throw new Exception(__('The MP4 file you have uploaded
|
130 |
}
|
131 |
} else {
|
132 |
wp_delete_post($post_id, true);
|
100 |
switch ($type) {
|
101 |
case 'video/mp4' :
|
102 |
$type = 'video';
|
103 |
+
include_once(trailingslashit(BP_MEDIA_PLUGIN_DIR) . 'includes/lib/getid3/getid3.php');
|
104 |
try {
|
105 |
+
$getID3 = new getID3;
|
106 |
+
$vid_info = $getID3->analyze($file);
|
|
|
|
|
|
|
|
|
107 |
} catch (Exception $e) {
|
108 |
wp_delete_post($post_id, true);
|
109 |
unlink($file);
|
110 |
$activity_content = false;
|
111 |
throw new Exception(__('MP4 file you have uploaded is currupt.', 'bp-media'));
|
112 |
}
|
113 |
+
if (is_array($vid_info)) {
|
114 |
+
if (!array_key_exists('error',$vid_info)&& array_key_exists('fileformat', $vid_info) && array_key_exists('video', $vid_info)&&array_key_exists('fourcc',$vid_info['video'])) {
|
115 |
+
if (!($vid_info['fileformat']=='mp4'&&$vid_info['video']['fourcc']=='avc1')) {
|
116 |
wp_delete_post($post_id, true);
|
117 |
unlink($file);
|
118 |
$activity_content = false;
|
122 |
wp_delete_post($post_id, true);
|
123 |
unlink($file);
|
124 |
$activity_content = false;
|
125 |
+
throw new Exception(__('The MP4 file you have uploaded is using an unsupported video codec. Supported video codec is H.264.', 'bp-media'));
|
126 |
}
|
127 |
} else {
|
128 |
wp_delete_post($post_id, true);
|
includes/bp-media-filters.php
CHANGED
@@ -1,11 +1,11 @@
|
|
1 |
<?php
|
2 |
-
function bp_media_activity_permalink_filter($link, $activity_obj) {
|
3 |
-
if ('media_upload' == $activity_obj->type) {
|
4 |
add_shortcode('bp_media_url', 'bp_media_shortcode_url');
|
5 |
$link = do_shortcode($activity_obj->primary_link);
|
6 |
remove_shortcode('bp_media_url');
|
7 |
}
|
8 |
-
if ('activity_comment' == $activity_obj->type) {
|
9 |
$parent = bp_activity_get_meta($activity_obj->item_id, 'bp_media_parent_post');
|
10 |
if ($parent) {
|
11 |
$parent = new BP_Media_Host_Wordpress($parent);
|
@@ -16,8 +16,8 @@ function bp_media_activity_permalink_filter($link, $activity_obj) {
|
|
16 |
}
|
17 |
add_filter('bp_activity_get_permalink', 'bp_media_activity_permalink_filter', 10, 2);
|
18 |
|
19 |
-
function bp_media_activity_action_filter($activity_action, $activity_obj) {
|
20 |
-
if ('media_upload' == $activity_obj->type) {
|
21 |
add_shortcode('bp_media_action', 'bp_media_shortcode_action');
|
22 |
$activity_action = do_shortcode($activity_action);
|
23 |
remove_shortcode('bp_media_action');
|
@@ -26,8 +26,8 @@ function bp_media_activity_action_filter($activity_action, $activity_obj) {
|
|
26 |
}
|
27 |
add_filter('bp_get_activity_action', 'bp_media_activity_action_filter', 10, 2);
|
28 |
|
29 |
-
function bp_media_activity_content_filter($activity_content, $activity_obj) {
|
30 |
-
if ('media_upload' == $activity_obj->type) {
|
31 |
add_shortcode('bp_media_content', 'bp_media_shortcode_content');
|
32 |
$activity_content = do_shortcode($activity_content);
|
33 |
remove_shortcode('bp_media_content');
|
@@ -44,7 +44,6 @@ function bp_media_activity_parent_content_filter($content) {
|
|
44 |
remove_shortcode('bp_media_content');
|
45 |
return $content;
|
46 |
}
|
47 |
-
|
48 |
add_filter('bp_get_activity_parent_content', 'bp_media_activity_parent_content_filter');
|
49 |
|
50 |
function bp_media_delete_button_handler($link) {
|
1 |
<?php
|
2 |
+
function bp_media_activity_permalink_filter($link, $activity_obj = null) {
|
3 |
+
if ($activity_obj != null && 'media_upload' == $activity_obj->type) {
|
4 |
add_shortcode('bp_media_url', 'bp_media_shortcode_url');
|
5 |
$link = do_shortcode($activity_obj->primary_link);
|
6 |
remove_shortcode('bp_media_url');
|
7 |
}
|
8 |
+
if ($activity_obj != null && 'activity_comment' == $activity_obj->type) {
|
9 |
$parent = bp_activity_get_meta($activity_obj->item_id, 'bp_media_parent_post');
|
10 |
if ($parent) {
|
11 |
$parent = new BP_Media_Host_Wordpress($parent);
|
16 |
}
|
17 |
add_filter('bp_activity_get_permalink', 'bp_media_activity_permalink_filter', 10, 2);
|
18 |
|
19 |
+
function bp_media_activity_action_filter($activity_action, $activity_obj = null) {
|
20 |
+
if ($activity_obj != null && 'media_upload' == $activity_obj->type) {
|
21 |
add_shortcode('bp_media_action', 'bp_media_shortcode_action');
|
22 |
$activity_action = do_shortcode($activity_action);
|
23 |
remove_shortcode('bp_media_action');
|
26 |
}
|
27 |
add_filter('bp_get_activity_action', 'bp_media_activity_action_filter', 10, 2);
|
28 |
|
29 |
+
function bp_media_activity_content_filter($activity_content, $activity_obj = null ) {
|
30 |
+
if ($activity_obj != null && 'media_upload' == $activity_obj->type) {
|
31 |
add_shortcode('bp_media_content', 'bp_media_shortcode_content');
|
32 |
$activity_content = do_shortcode($activity_content);
|
33 |
remove_shortcode('bp_media_content');
|
44 |
remove_shortcode('bp_media_content');
|
45 |
return $content;
|
46 |
}
|
|
|
47 |
add_filter('bp_get_activity_parent_content', 'bp_media_activity_parent_content_filter');
|
48 |
|
49 |
function bp_media_delete_button_handler($link) {
|
includes/bp-media-functions.php
CHANGED
@@ -69,8 +69,8 @@ function bp_media_show_formatted_error_message($messages, $type) {
|
|
69 |
echo '</div>';
|
70 |
}
|
71 |
|
72 |
-
function bp_media_conditional_override_allowed_tags($content, $activity) {
|
73 |
-
if ($activity->type == 'media_upload') {
|
74 |
add_filter('bp_activity_allowed_tags', 'bp_media_override_allowed_tags', 1);
|
75 |
}
|
76 |
return bp_activity_filter_kses($content);
|
69 |
echo '</div>';
|
70 |
}
|
71 |
|
72 |
+
function bp_media_conditional_override_allowed_tags($content, $activity=null) {
|
73 |
+
if ($activity != null && $activity->type == 'media_upload') {
|
74 |
add_filter('bp_activity_allowed_tags', 'bp_media_override_allowed_tags', 1);
|
75 |
}
|
76 |
return bp_activity_filter_kses($content);
|
includes/lib/MP4Info.php
DELETED
@@ -1,257 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* MP4Info
|
4 |
-
*
|
5 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
6 |
-
* @copyright Copyright (c) 2006-2009 Tommy Lacroix
|
7 |
-
* @license LGPL version 3, http://www.gnu.org/licenses/lgpl.html
|
8 |
-
* @package php-mp4info
|
9 |
-
* @link $HeadURL: https://php-mp4info.googlecode.com/svn/trunk/MP4Info.php $
|
10 |
-
*/
|
11 |
-
|
12 |
-
// ---
|
13 |
-
|
14 |
-
/**
|
15 |
-
* MP4Info main class
|
16 |
-
*
|
17 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
18 |
-
* @version 1.1.20090611 $Id: MP4Info.php 2 2009-06-11 14:12:31Z lacroix.tommy@gmail.com $
|
19 |
-
*/
|
20 |
-
class MP4Info {
|
21 |
-
// {{{ Audio codec types
|
22 |
-
const MP4_AUDIO_CODEC_UNCOMPRESSED = 0x00;
|
23 |
-
const MP4_AUDIO_CODEC_MP3 = 0x02;
|
24 |
-
const MP4_AUDIO_CODEC_AAC = 0xe0;
|
25 |
-
// }}}
|
26 |
-
|
27 |
-
// {{{ Video codec types
|
28 |
-
const MP4_VIDEO_CODEC_H264 = 0xe0;
|
29 |
-
// }}}
|
30 |
-
|
31 |
-
|
32 |
-
/**
|
33 |
-
* Get information from MP4 file
|
34 |
-
*
|
35 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
36 |
-
* @param string $file
|
37 |
-
* @return array
|
38 |
-
* @access public
|
39 |
-
* @static
|
40 |
-
*/
|
41 |
-
public static function getInfo($file) {
|
42 |
-
// Open file
|
43 |
-
$f = fopen($file,'rb');
|
44 |
-
if (!$f) {
|
45 |
-
throw new Exception('Cannot open file: '.$file);
|
46 |
-
}
|
47 |
-
|
48 |
-
// Get all boxes
|
49 |
-
try {
|
50 |
-
while (($box = MP4Info_Box::fromStream($f))) {
|
51 |
-
$boxes[] = $box;
|
52 |
-
}
|
53 |
-
} catch (Exception $e) { }
|
54 |
-
|
55 |
-
// Close
|
56 |
-
fclose($f);
|
57 |
-
|
58 |
-
// Return info
|
59 |
-
return self::getInfoFromBoxes($boxes);
|
60 |
-
} // getInfo method
|
61 |
-
|
62 |
-
|
63 |
-
/**
|
64 |
-
* Get information from MP4 boxes
|
65 |
-
*
|
66 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
67 |
-
* @param string $file
|
68 |
-
* @return array
|
69 |
-
* @access public
|
70 |
-
* @static
|
71 |
-
*/
|
72 |
-
public static function getInfoFromBoxes($boxes, &$context=null) {
|
73 |
-
if ($context === null) {
|
74 |
-
$context = new stdClass();
|
75 |
-
$context->hasVideo = false;
|
76 |
-
$context->hasAudio = false;
|
77 |
-
$context->video = new stdClass();
|
78 |
-
$context->audio = new stdClass();
|
79 |
-
}
|
80 |
-
|
81 |
-
foreach ($boxes as &$box) {
|
82 |
-
// Interpret box
|
83 |
-
switch ($box->getBoxTypeStr()) {
|
84 |
-
case 'hdlr':
|
85 |
-
switch ($box->getHandlerType()) {
|
86 |
-
case MP4Info_Box_hdlr::HANDLER_VIDEO:
|
87 |
-
$context->hasVideo = true;
|
88 |
-
break;
|
89 |
-
case MP4Info_Box_hdlr::HANDLER_SOUND:
|
90 |
-
$context->hasAudio = true;
|
91 |
-
break;
|
92 |
-
}
|
93 |
-
break;
|
94 |
-
case 'mvhd':
|
95 |
-
$context->duration = $box->getRealDuration();
|
96 |
-
break;
|
97 |
-
case 'ilst':
|
98 |
-
if ($box->hasValue('©too')) {
|
99 |
-
$context->encoder = $box->getValue('©too');
|
100 |
-
}
|
101 |
-
break;
|
102 |
-
case 'uuid':
|
103 |
-
$meta = $box->getXMPMetaData();
|
104 |
-
if ($meta !== false) {
|
105 |
-
// Try to get duration
|
106 |
-
if (!isset($context->duration)) {
|
107 |
-
if (preg_match('/<(|[a-z]+:)duration[\s\n\r]([^>]*)>/im',$meta,$m)) {
|
108 |
-
if (preg_match_all('/xmpDM:([a-z]+)="([^"]+)"/',$m[2],$mm)) {
|
109 |
-
$value = $scale = false;
|
110 |
-
foreach ($mm[1] as $k=>$v) {
|
111 |
-
if (($v == 'value') || ($v == 'scale')) {
|
112 |
-
if (preg_match('/^1\/([0-9]+)$/',$mm[2][$k],$mmm)) {
|
113 |
-
$mm[2][$k] = 1/$mmm[1];
|
114 |
-
}
|
115 |
-
$$v = $mm[2][$k];
|
116 |
-
}
|
117 |
-
}
|
118 |
-
if (($value !== false) && ($scale !== false)) {
|
119 |
-
$context->duration = $value*$scale;
|
120 |
-
}
|
121 |
-
}
|
122 |
-
}
|
123 |
-
}
|
124 |
-
|
125 |
-
// Try to get size
|
126 |
-
if ((!isset($context->width)) || (!isset($context->height))) {
|
127 |
-
if (preg_match('/<(|[a-z]+:)videoFrameSize[\s\n\r]([^>]*)>/im',$meta,$m)) {
|
128 |
-
if (preg_match_all('/[a-z]:([a-z]+)="([^"]+)"/',$m[2],$mm)) {
|
129 |
-
$w = $h = false;
|
130 |
-
foreach ($mm[1] as $k=>$v) {
|
131 |
-
if (($v == 'w') || ($v == 'h')) {
|
132 |
-
$$v = $mm[2][$k];
|
133 |
-
}
|
134 |
-
}
|
135 |
-
if ($w != false) {
|
136 |
-
$context->video->width = $w;
|
137 |
-
$context->hasVideo = true;
|
138 |
-
}
|
139 |
-
if ($h != false) {
|
140 |
-
$context->video->height = $h;
|
141 |
-
$context->hasVideo = true;
|
142 |
-
}
|
143 |
-
}
|
144 |
-
}
|
145 |
-
}
|
146 |
-
|
147 |
-
// Try to get encoder
|
148 |
-
if (preg_match('/softwareAgent="([^"]+)"/i',$meta,$m)) {
|
149 |
-
$context->encoder = $m[1];
|
150 |
-
}
|
151 |
-
|
152 |
-
// Try to get audio channels
|
153 |
-
if (preg_match('/audioChannelType="([^"]+)"/i',$meta,$m)) {
|
154 |
-
switch (strtolower($m[1])) {
|
155 |
-
case 'stereo':
|
156 |
-
case '2':
|
157 |
-
$context->audio->channels = 2;
|
158 |
-
$context->hasAudio = true;
|
159 |
-
break;
|
160 |
-
case 'mono':
|
161 |
-
case '1':
|
162 |
-
$context->audio->channels = 1;
|
163 |
-
$context->hasAudio = true;
|
164 |
-
break;
|
165 |
-
case '5.1':
|
166 |
-
case '5':
|
167 |
-
$context->audio->channels = 5;
|
168 |
-
$context->hasAudio = true;
|
169 |
-
break;
|
170 |
-
}
|
171 |
-
}
|
172 |
-
|
173 |
-
// Try to get audio frequency
|
174 |
-
if (preg_match('/audioSampleRate="([^"]+)"/i',$meta,$m)) {
|
175 |
-
$context->audio->frequency = $m[1]/1000;
|
176 |
-
$context->hasAudio = true;
|
177 |
-
}
|
178 |
-
|
179 |
-
// Try to get video frame rate
|
180 |
-
if (preg_match('/videoFrameRate="([^"]+)"/i',$meta,$m)) {
|
181 |
-
$context->video->fps = $m[1];
|
182 |
-
$context->hasVideo = true;
|
183 |
-
}
|
184 |
-
|
185 |
-
//print htmlentities($meta);
|
186 |
-
}
|
187 |
-
break;
|
188 |
-
case 'stsd':
|
189 |
-
$values = $box->getValues();
|
190 |
-
foreach (array_keys($values) as $codec) {
|
191 |
-
switch ($codec) {
|
192 |
-
case '.mp3':
|
193 |
-
$context->audio->codec = self::MP4_AUDIO_CODEC_MP3;
|
194 |
-
$context->audio->codecStr = 'MP3';
|
195 |
-
$context->hasAudio = true;
|
196 |
-
break;
|
197 |
-
case 'mp4a':
|
198 |
-
case 'mp4s':
|
199 |
-
$context->audio->codec = self::MP4_AUDIO_CODEC_AAC;
|
200 |
-
$context->audio->codecStr = 'AAC';
|
201 |
-
$context->hasAudio = true;
|
202 |
-
break;
|
203 |
-
case 'avc1':
|
204 |
-
case 'h264':
|
205 |
-
case 'H264':
|
206 |
-
$context->video->codec = self::MP4_VIDEO_CODEC_H264;
|
207 |
-
$context->video->codecStr = 'H.264';
|
208 |
-
$context->hasVideo = true;
|
209 |
-
break;
|
210 |
-
}
|
211 |
-
}
|
212 |
-
break;
|
213 |
-
case 'tkhd':
|
214 |
-
if ($box->getWidth() > 0) {
|
215 |
-
$context->hasVideo = true;
|
216 |
-
$context->video->width = $box->getWidth();
|
217 |
-
$context->video->height = $box->getHeight();
|
218 |
-
$context->hasVideo = true;
|
219 |
-
}
|
220 |
-
break;
|
221 |
-
}
|
222 |
-
|
223 |
-
// Process children
|
224 |
-
if ($box->hasChildren()) {
|
225 |
-
self::getInfoFromBoxes($box->children(), $context);
|
226 |
-
}
|
227 |
-
}
|
228 |
-
|
229 |
-
return $context;
|
230 |
-
} // getInfoFromBoxes method
|
231 |
-
|
232 |
-
|
233 |
-
/**
|
234 |
-
* Display boxes for debugging
|
235 |
-
*
|
236 |
-
* @param MP4Info_Box[] $boxes
|
237 |
-
* @param int $level
|
238 |
-
* @access public
|
239 |
-
* @static
|
240 |
-
*/
|
241 |
-
public static function displayBoxes($boxes,$level=0) {
|
242 |
-
foreach ($boxes as $box) {
|
243 |
-
print str_repeat(' ',$level*4) . $box->toString() . '<br>';
|
244 |
-
if ($box->hasChildren()) {
|
245 |
-
$this->displayBoxes($box->children(), $level+1);
|
246 |
-
}
|
247 |
-
}
|
248 |
-
} // displayBoxes method
|
249 |
-
} // MP4Info class
|
250 |
-
|
251 |
-
// ---
|
252 |
-
|
253 |
-
// {{{ Dependencies
|
254 |
-
include "MP4Info/Helper.php";
|
255 |
-
include "MP4Info/Exception.php";
|
256 |
-
include "MP4Info/Box.php";
|
257 |
-
// }}} Dependencies
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/lib/MP4Info/Box.php
DELETED
@@ -1,446 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* MP4Info
|
4 |
-
*
|
5 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
6 |
-
* @copyright Copyright (c) 2006-2009 Tommy Lacroix
|
7 |
-
* @license LGPL version 3, http://www.gnu.org/licenses/lgpl.html
|
8 |
-
* @package php-mp4info
|
9 |
-
* @link $HeadURL: https://php-mp4info.googlecode.com/svn/trunk/MP4Info/Box.php $
|
10 |
-
*
|
11 |
-
* Based on:
|
12 |
-
* - http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
|
13 |
-
* - http://neuron2.net/library/avc/c041828_ISO_IEC_14496-12_2005(E).pdf
|
14 |
-
* - http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf
|
15 |
-
*/
|
16 |
-
|
17 |
-
// ---
|
18 |
-
|
19 |
-
/**
|
20 |
-
* MP4Info General Box
|
21 |
-
*
|
22 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
23 |
-
* @version 1.1.20090611 $Id: Box.php 2 2009-06-11 14:12:31Z lacroix.tommy@gmail.com $
|
24 |
-
*/
|
25 |
-
class MP4Info_Box {
|
26 |
-
/**
|
27 |
-
* Total box size, including box header (8 bytes)
|
28 |
-
*
|
29 |
-
* @var int
|
30 |
-
*/
|
31 |
-
protected $totalSize;
|
32 |
-
|
33 |
-
/**
|
34 |
-
* Box type, numeric
|
35 |
-
*
|
36 |
-
* @var int
|
37 |
-
*/
|
38 |
-
protected $boxType;
|
39 |
-
|
40 |
-
/**
|
41 |
-
* Box type, string
|
42 |
-
*
|
43 |
-
* @var string
|
44 |
-
*/
|
45 |
-
protected $boxTypeStr;
|
46 |
-
|
47 |
-
/**
|
48 |
-
* Box data
|
49 |
-
*
|
50 |
-
* @var string(binary)
|
51 |
-
*/
|
52 |
-
protected $data;
|
53 |
-
|
54 |
-
/**
|
55 |
-
* Parent
|
56 |
-
*
|
57 |
-
* @var MP4Info_Box|false
|
58 |
-
*/
|
59 |
-
protected $parent;
|
60 |
-
|
61 |
-
/**
|
62 |
-
* Children
|
63 |
-
*
|
64 |
-
* @var MP4Info_Box[]
|
65 |
-
*/
|
66 |
-
protected $children = array();
|
67 |
-
|
68 |
-
/**
|
69 |
-
* Constructor
|
70 |
-
*
|
71 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
72 |
-
* @param int $totalSize
|
73 |
-
* @param int $boxType
|
74 |
-
* @param file|string $f
|
75 |
-
* @param MP4Info_Box $parent
|
76 |
-
* @access public
|
77 |
-
*/
|
78 |
-
public function __construct($totalSize, $boxType, $f, $parent=false) {
|
79 |
-
$this->totalSize = $totalSize;
|
80 |
-
$this->boxType = $boxType;
|
81 |
-
$this->boxTypeStr = pack('N',$boxType);
|
82 |
-
$this->data = self::getDataFrom3rd($f,$totalSize);
|
83 |
-
$this->parent = $parent;
|
84 |
-
if ($parent != false) {
|
85 |
-
$parent->addChild($this);
|
86 |
-
}
|
87 |
-
} // Constructor
|
88 |
-
|
89 |
-
/**
|
90 |
-
* Add a child to this box
|
91 |
-
*
|
92 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
93 |
-
* @param MP4Info_Box $child
|
94 |
-
* @access public
|
95 |
-
*/
|
96 |
-
public function addChild(&$child) {
|
97 |
-
if (!$child instanceof MP4Info_Box) {
|
98 |
-
throw new Exception('Child is not MP4Info_Box');
|
99 |
-
}
|
100 |
-
$this->children[] = &$child;
|
101 |
-
}
|
102 |
-
|
103 |
-
/**
|
104 |
-
* Check if the box has children
|
105 |
-
*
|
106 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
107 |
-
* @return bool
|
108 |
-
* @access public
|
109 |
-
*/
|
110 |
-
public function hasChildren() {
|
111 |
-
return count($this->children) > 0;
|
112 |
-
} // hasChildren method
|
113 |
-
|
114 |
-
/**
|
115 |
-
* Get boxes' children
|
116 |
-
*
|
117 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
118 |
-
* @return MP4Info_Box[]
|
119 |
-
* @access public
|
120 |
-
*/
|
121 |
-
public function children() {
|
122 |
-
return $this->children;
|
123 |
-
} // children method
|
124 |
-
|
125 |
-
|
126 |
-
/**
|
127 |
-
* Get data from 3rd argument (file or string)
|
128 |
-
*
|
129 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
130 |
-
* @param file|string $f
|
131 |
-
* @param int $totalSize
|
132 |
-
* @return string
|
133 |
-
* @access public
|
134 |
-
* @static
|
135 |
-
*/
|
136 |
-
public static function getDataFrom3rd($f, $totalSize) {
|
137 |
-
// Get data
|
138 |
-
if ($f === false) {
|
139 |
-
return '';
|
140 |
-
} else if (is_string($f)) {
|
141 |
-
$data = substr($f,0,$totalSize-8);
|
142 |
-
} else {
|
143 |
-
$data = fread($f,$totalSize-8);
|
144 |
-
}
|
145 |
-
|
146 |
-
return $data;
|
147 |
-
} // getDataFrom3rd method
|
148 |
-
|
149 |
-
|
150 |
-
/**
|
151 |
-
* Create an MP4Info_Box object from data
|
152 |
-
*
|
153 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
154 |
-
* @param int $totalSize
|
155 |
-
* @param int $boxType
|
156 |
-
* @param file|string $f
|
157 |
-
* @param MP4Info_Box|false $parent
|
158 |
-
* @return MP4Info_Box
|
159 |
-
* @access public
|
160 |
-
* @static
|
161 |
-
*/
|
162 |
-
public static function factory($totalSize, $boxType, $f, $parent=false) {
|
163 |
-
if (MP4Info_Box_Container::isCompatible($boxType,$parent)) {
|
164 |
-
$box = new MP4Info_Box_Container($totalSize, $boxType, $f, $parent);
|
165 |
-
} else if (MP4Info_Box_ftyp::isCompatible($boxType,$parent)) {
|
166 |
-
$box = new MP4Info_Box_ftyp($totalSize, $boxType, $f, $parent);
|
167 |
-
} else if (MP4Info_Box_uuid::isCompatible($boxType,$parent)) {
|
168 |
-
$box = new MP4Info_Box_uuid($totalSize, $boxType, $f, $parent);
|
169 |
-
} else if (MP4Info_Box_hdlr::isCompatible($boxType,$parent)) {
|
170 |
-
$box = new MP4Info_Box_hdlr($totalSize, $boxType, $f, $parent);
|
171 |
-
} else if (MP4Info_Box_mvhd::isCompatible($boxType,$parent)) {
|
172 |
-
$box = new MP4Info_Box_mvhd($totalSize, $boxType, $f, $parent);
|
173 |
-
} else if (MP4Info_Box_tkhd::isCompatible($boxType,$parent)) {
|
174 |
-
$box = new MP4Info_Box_tkhd($totalSize, $boxType, $f, $parent);
|
175 |
-
} else if (MP4Info_Box_mdhd::isCompatible($boxType,$parent)) {
|
176 |
-
$box = new MP4Info_Box_mdhd($totalSize, $boxType, $f, $parent);
|
177 |
-
} else if (MP4Info_Box_meta::isCompatible($boxType,$parent)) {
|
178 |
-
$box = new MP4Info_Box_meta($totalSize, $boxType, $f, $parent);
|
179 |
-
} else if (MP4Info_Box_stsd::isCompatible($boxType,$parent)) {
|
180 |
-
$box = new MP4Info_Box_stsd($totalSize, $boxType, $f, $parent);
|
181 |
-
} else if (MP4Info_Box_ilst::isCompatible($boxType,$parent)) {
|
182 |
-
$box = new MP4Info_Box_ilst($totalSize, $boxType, $f, $parent);
|
183 |
-
} else if (MP4Info_Box_ilst_sub::isCompatible($boxType,$parent)) {
|
184 |
-
$box = new MP4Info_Box_ilst_sub($totalSize, $boxType, $f, $parent);
|
185 |
-
} else {
|
186 |
-
throw new Exception('Media type error');
|
187 |
-
}
|
188 |
-
|
189 |
-
// Return box
|
190 |
-
return $box;
|
191 |
-
} // factory method
|
192 |
-
|
193 |
-
|
194 |
-
/**
|
195 |
-
* Create a box from string
|
196 |
-
*
|
197 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
198 |
-
* @param string $data
|
199 |
-
* @param MP4Info_Box|false $parent
|
200 |
-
* @return MP4Info_Box
|
201 |
-
* @access public
|
202 |
-
* @static
|
203 |
-
*/
|
204 |
-
public static function fromString(&$data,$parent=false) {
|
205 |
-
if (strlen($data) < 8) {
|
206 |
-
throw new Exception('Not enough data, need at least 8 bytes!');
|
207 |
-
}
|
208 |
-
|
209 |
-
$ar = unpack('NtotalSize/NboxType',$data);
|
210 |
-
if ($ar['totalSize'] == 1) {
|
211 |
-
// Size is bigger than 4GB :-O die
|
212 |
-
// Skip ExtendedSize(UI64) and try to decode anyway
|
213 |
-
$ar2 = unpack('N2extSize',substr($data,8));
|
214 |
-
if ($ar2['extSize1'] > 0) {
|
215 |
-
throw new Exception('Extended size not supported');
|
216 |
-
} else {
|
217 |
-
$ar['totalSize'] = $ar2['extSize2'];
|
218 |
-
}
|
219 |
-
$skip = 8;
|
220 |
-
} else {
|
221 |
-
$skip = 0;
|
222 |
-
}
|
223 |
-
|
224 |
-
// Check if we need to skip
|
225 |
-
if (self::skipBoxType($ar['boxType'])) {
|
226 |
-
//print '+++ Skipping box '.pack('N',$ar['boxType']).'<br>';
|
227 |
-
$data = substr($data,$ar['totalSize']);
|
228 |
-
return self::fromString($data,$parent);
|
229 |
-
}
|
230 |
-
|
231 |
-
// Check if box is a container, and skip to content if so
|
232 |
-
if (self::ignoreBoxType($ar['boxType'])) {
|
233 |
-
//print '+++ Ignoring box '.pack('N',$ar['boxType']).'<br>';
|
234 |
-
$data = substr($data,8+$skip);
|
235 |
-
return self::fromString($data,$parent);
|
236 |
-
}
|
237 |
-
|
238 |
-
// Create box
|
239 |
-
$box = self::factory($ar['totalSize'],$ar['boxType'],substr($data,8+$skip),$parent);
|
240 |
-
if ($box instanceof MP4Info_Box) {
|
241 |
-
$data = substr($data,$box->getTotalSize());
|
242 |
-
}
|
243 |
-
|
244 |
-
return $box;
|
245 |
-
} // fromString method
|
246 |
-
|
247 |
-
|
248 |
-
/**
|
249 |
-
* Create a box from file stream
|
250 |
-
*
|
251 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
252 |
-
* @param file $f
|
253 |
-
* @param MP4Info_Box|false $parent
|
254 |
-
* @return MP4Info_Box
|
255 |
-
* @access public
|
256 |
-
* @static
|
257 |
-
*/
|
258 |
-
public static function fromStream($f,$parent=false) {
|
259 |
-
// Get box header
|
260 |
-
$buf = fread($f,8);
|
261 |
-
if (strlen($buf) < 8) {
|
262 |
-
return false;
|
263 |
-
}
|
264 |
-
$ar = unpack('NtotalSize/NboxType',$buf);
|
265 |
-
if ($ar['totalSize'] == 1) {
|
266 |
-
// Size is bigger than 4GB :-O die
|
267 |
-
// Skip ExtendedSize(UI64) and try to decode anyway
|
268 |
-
$buf = fread($f,8);
|
269 |
-
$ar2 = unpack('N2extSize',$buf);
|
270 |
-
if ($ar2['extSize1'] > 0) {
|
271 |
-
throw new Exception('Extended size not supported');
|
272 |
-
} else {
|
273 |
-
$ar['totalSize'] = $ar2['extSize2'];
|
274 |
-
}
|
275 |
-
}
|
276 |
-
|
277 |
-
// Check if we need to skip
|
278 |
-
if (self::skipBoxType($ar['boxType'])) {
|
279 |
-
//print '+++ Skipping box '.pack('N',$ar['boxType']).'<br>';
|
280 |
-
fseek($f,$ar['totalSize']-8,SEEK_CUR);
|
281 |
-
return self::fromStream($f,$parent);
|
282 |
-
}
|
283 |
-
|
284 |
-
// Check if box is a container, and skip it if so
|
285 |
-
if (self::ignoreBoxType($ar['boxType'])) {
|
286 |
-
//print '+++ Ignoring box '.pack('N',$ar['boxType']).' of size '.$ar['totalSize'].'<br>';
|
287 |
-
return self::fromStream($f,$parent);
|
288 |
-
}
|
289 |
-
|
290 |
-
// Get box content
|
291 |
-
if ($ar['totalSize'] > 0) {
|
292 |
-
if ($ar['totalSize'] < 256*1024) {
|
293 |
-
$data = fread($f,$ar['totalSize']-8);
|
294 |
-
} else {
|
295 |
-
$data = $f;
|
296 |
-
}
|
297 |
-
} else {
|
298 |
-
$data = '';
|
299 |
-
}
|
300 |
-
|
301 |
-
// Create box object
|
302 |
-
$box = MP4Info_Box::factory($ar['totalSize'], $ar['boxType'], $data, $parent);
|
303 |
-
//print 'Got box from stream of type 0x'.dechex($ar['boxType']).'('.pack('N',$ar['boxType']).') and size '.$ar['totalSize'].' bytes: '.$box->toString().'<br>';
|
304 |
-
return $box;
|
305 |
-
} // fromStream method
|
306 |
-
|
307 |
-
|
308 |
-
/**
|
309 |
-
* Check if we need to ignore that box, based on type
|
310 |
-
*
|
311 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
312 |
-
* @param int $boxType
|
313 |
-
* @return bool
|
314 |
-
* @access public
|
315 |
-
* @static
|
316 |
-
* @todo Cleanup, legacy stuff
|
317 |
-
*/
|
318 |
-
public static function ignoreBoxType($boxType) {
|
319 |
-
return false;
|
320 |
-
} // ignoreBoxType method
|
321 |
-
|
322 |
-
|
323 |
-
/**
|
324 |
-
* Check if we need to skip a box based on type
|
325 |
-
*
|
326 |
-
* @param int $boxType
|
327 |
-
* @return bool
|
328 |
-
* @access public
|
329 |
-
* @static
|
330 |
-
*/
|
331 |
-
public static function skipBoxType($boxType) {
|
332 |
-
switch ($boxType) {
|
333 |
-
case 0x696f6473: // iods 5.1 Initial Object Descriptor Box
|
334 |
-
case 0x55c40000: // ??? ??
|
335 |
-
case 0x6d646174: // mdat ?? Movie Data
|
336 |
-
case 0x736d6864: // smhd 8.11.3 Sound Media Header Box
|
337 |
-
case 0x766d6864: // vmhd 8.11.2 Video Media Header Box
|
338 |
-
case 0x6e6d6864: // nmhd 8.11.5 Null Media Header Box
|
339 |
-
case 0x64696e66: // dinf ??
|
340 |
-
case 0x73747473: // stts 8.15.2 Decoding Time to Sample Box
|
341 |
-
case 0x73747363: // stsc 8.18 Sample To Chunk Box
|
342 |
-
case 0x7374737a: // stsz 8.17 Sample Size Boxes
|
343 |
-
case 0x7374636f: // stco 8.19 Chunk Offset Box
|
344 |
-
case 0x636f3634: // co64 8.19 Chunk Offset Box
|
345 |
-
case 0x63747473: // ctts 8.15.3 Composition Time to Sample Box
|
346 |
-
case 0x73747373: // stss 8.20 Sync Sample Box
|
347 |
-
case 0x74726566: // tref 8.6 Track Reference Box
|
348 |
-
return true;
|
349 |
-
default:
|
350 |
-
return false;
|
351 |
-
}
|
352 |
-
} // skipBoxType method
|
353 |
-
|
354 |
-
|
355 |
-
/**
|
356 |
-
* Total size getter
|
357 |
-
*
|
358 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
359 |
-
* @return int
|
360 |
-
* @access public
|
361 |
-
*/
|
362 |
-
public function getTotalSize() {
|
363 |
-
return $this->totalSize;
|
364 |
-
} // getTotalSize method
|
365 |
-
|
366 |
-
|
367 |
-
/**
|
368 |
-
* Box type getter
|
369 |
-
*
|
370 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
371 |
-
* @return int
|
372 |
-
* @access public
|
373 |
-
*/
|
374 |
-
public function getBoxType() {
|
375 |
-
return $this->boxType;
|
376 |
-
} // getBoxType method
|
377 |
-
|
378 |
-
|
379 |
-
/**
|
380 |
-
* Box type string getter
|
381 |
-
*
|
382 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
383 |
-
* @return string
|
384 |
-
* @access public
|
385 |
-
*/
|
386 |
-
public function getBoxTypeStr() {
|
387 |
-
return $this->boxTypeStr;
|
388 |
-
} // getBoxTypeStr method
|
389 |
-
|
390 |
-
|
391 |
-
/**
|
392 |
-
* Data getter
|
393 |
-
*
|
394 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
395 |
-
* @return string(binary)
|
396 |
-
* @access public
|
397 |
-
*/
|
398 |
-
public function getData() {
|
399 |
-
return $this->data;
|
400 |
-
} // getData method
|
401 |
-
|
402 |
-
|
403 |
-
/**
|
404 |
-
* stdClass converter
|
405 |
-
*
|
406 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
407 |
-
* @return stdClass
|
408 |
-
* @access public
|
409 |
-
*/
|
410 |
-
public function toStdClass() {
|
411 |
-
$a = new stdClass();
|
412 |
-
foreach ($this as $propName=>$prop) {
|
413 |
-
if (($propName != 'children') && ($propName != 'parent') && ($propName != 'boxes')) {
|
414 |
-
$a->{$propName} = $prop;
|
415 |
-
}
|
416 |
-
}
|
417 |
-
return $a;
|
418 |
-
} // toStdClass method
|
419 |
-
|
420 |
-
|
421 |
-
/**
|
422 |
-
* String converter
|
423 |
-
*
|
424 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
425 |
-
* @return string
|
426 |
-
* @access public
|
427 |
-
*/
|
428 |
-
public function toString() {
|
429 |
-
return '[MP4Info_Box[0x'.dechex($this->boxType).'('.pack('N',$this->boxType).']]';
|
430 |
-
} // toString method
|
431 |
-
} // MP4Info_Box class
|
432 |
-
|
433 |
-
|
434 |
-
// {{{ Dependencies
|
435 |
-
include "Box/Container.php";
|
436 |
-
include "Box/ftyp.php";
|
437 |
-
include "Box/uuid.php";
|
438 |
-
include "Box/hdlr.php";
|
439 |
-
include "Box/tkhd.php";
|
440 |
-
include "Box/mvhd.php";
|
441 |
-
include "Box/mdhd.php";
|
442 |
-
include "Box/meta.php";
|
443 |
-
include "Box/stsd.php";
|
444 |
-
include "Box/ilst.php";
|
445 |
-
include "Box/ilst_sub.php";
|
446 |
-
// }}} Dependencies
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/lib/MP4Info/Box/Container.php
DELETED
@@ -1,99 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* MP4Info
|
4 |
-
*
|
5 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
6 |
-
* @copyright Copyright (c) 2006-2009 Tommy Lacroix
|
7 |
-
* @license LGPL version 3, http://www.gnu.org/licenses/lgpl.html
|
8 |
-
* @package php-mp4info
|
9 |
-
* @link $HeadURL: https://php-mp4info.googlecode.com/svn/trunk/MP4Info/Box/Container.php $
|
10 |
-
*/
|
11 |
-
|
12 |
-
// ---
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Generic container box
|
16 |
-
*
|
17 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
18 |
-
* @version 1.1.20090611 $Id: Container.php 2 2009-06-11 14:12:31Z lacroix.tommy@gmail.com $
|
19 |
-
*/
|
20 |
-
class MP4Info_Box_Container extends MP4Info_Box {
|
21 |
-
/**
|
22 |
-
* Constructor
|
23 |
-
*
|
24 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
25 |
-
* @param int $totalSize
|
26 |
-
* @param int $boxType
|
27 |
-
* @param file|string $data
|
28 |
-
* @param MP4Info_Box $parent
|
29 |
-
* @return MP4Info_Box_Container
|
30 |
-
* @access public
|
31 |
-
* @throws MP4Info_Exception
|
32 |
-
*/
|
33 |
-
public function __construct($totalSize, $boxType, $data, $parent) {
|
34 |
-
if (!self::isCompatible($boxType, $parent)) {
|
35 |
-
throw new MP4Info_Exception('This box isn\'t a container',MP4Info_Exception::CODE_INCOMPATIBLE,$boxType);
|
36 |
-
}
|
37 |
-
|
38 |
-
// Call ancestor
|
39 |
-
parent::__construct($totalSize, $boxType, false, $parent);
|
40 |
-
|
41 |
-
// Unpack
|
42 |
-
if (is_string($data)) {
|
43 |
-
while ($data != '') {
|
44 |
-
try {
|
45 |
-
$box = MP4Info_Box::fromString($data, $this);
|
46 |
-
if (!$box instanceof MP4Info_Box) {
|
47 |
-
break;
|
48 |
-
}
|
49 |
-
} catch (Exception $e) {
|
50 |
-
break;
|
51 |
-
}
|
52 |
-
}
|
53 |
-
} else {
|
54 |
-
do {
|
55 |
-
try {
|
56 |
-
$box = MP4Info_Box::fromStream($data, $this);
|
57 |
-
if (!$box instanceof MP4Info_Box) {
|
58 |
-
break;
|
59 |
-
}
|
60 |
-
} catch (Exception $e) {
|
61 |
-
break;
|
62 |
-
}
|
63 |
-
} while ($box !== false);
|
64 |
-
}
|
65 |
-
} // Constructor
|
66 |
-
|
67 |
-
|
68 |
-
/**
|
69 |
-
* Check if block is compatible with class
|
70 |
-
*
|
71 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
72 |
-
* @param int $boxType
|
73 |
-
* @param MP4Info_Box $parent
|
74 |
-
* @return bool
|
75 |
-
* @access public
|
76 |
-
* @static
|
77 |
-
*/
|
78 |
-
public static function isCompatible($boxType, $parent) {
|
79 |
-
return ($boxType == 0x6D6F6F76) || // moov
|
80 |
-
($boxType == 0x7472616B) || // trak
|
81 |
-
($boxType == 0x6d646961) || // mdia
|
82 |
-
($boxType == 0x6D696E66) || // minf
|
83 |
-
($boxType == 0x7374626c) || // stbl
|
84 |
-
($boxType == 0x75647461) || // udta
|
85 |
-
false;
|
86 |
-
} // isCompatible method
|
87 |
-
|
88 |
-
|
89 |
-
/**
|
90 |
-
* String converter
|
91 |
-
*
|
92 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
93 |
-
* @return string
|
94 |
-
* @access public
|
95 |
-
*/
|
96 |
-
public function toString() {
|
97 |
-
return '[MP4Info_Box_Container['.$this->boxTypeStr.']:'.count($this->children).']';
|
98 |
-
} // toString method
|
99 |
-
} // MP4Info_Box_Container method
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/lib/MP4Info/Box/ftyp.php
DELETED
@@ -1,231 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* MP4Info
|
4 |
-
*
|
5 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
6 |
-
* @copyright Copyright (c) 2006-2009 Tommy Lacroix
|
7 |
-
* @license LGPL version 3, http://www.gnu.org/licenses/lgpl.html
|
8 |
-
* @package php-mp4info
|
9 |
-
* @link $HeadURL: https://php-mp4info.googlecode.com/svn/trunk/MP4Info/Box/ftyp.php $
|
10 |
-
*/
|
11 |
-
|
12 |
-
// ---
|
13 |
-
|
14 |
-
/**
|
15 |
-
* 4.3 File Type Box (FTYP)
|
16 |
-
*
|
17 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
18 |
-
* @version 1.1.20090611 $Id: ftyp.php 2 2009-06-11 14:12:31Z lacroix.tommy@gmail.com $
|
19 |
-
*/
|
20 |
-
class MP4Info_Box_ftyp extends MP4Info_Box {
|
21 |
-
/**
|
22 |
-
* Major brand
|
23 |
-
*
|
24 |
-
* @var int
|
25 |
-
*/
|
26 |
-
protected $majorBrand;
|
27 |
-
|
28 |
-
/**
|
29 |
-
* Minor brand
|
30 |
-
*
|
31 |
-
* @var int
|
32 |
-
*/
|
33 |
-
protected $minorBrand;
|
34 |
-
|
35 |
-
/**
|
36 |
-
* Compatible brands
|
37 |
-
*
|
38 |
-
* @var int[]
|
39 |
-
*/
|
40 |
-
protected $compatibleBrands;
|
41 |
-
|
42 |
-
/**
|
43 |
-
* Major Brands' names
|
44 |
-
*
|
45 |
-
* @var {n:Str,n:Str,...}
|
46 |
-
* @static
|
47 |
-
*/
|
48 |
-
protected static $brandNames = array(
|
49 |
-
'3g2a' => '3GPP2 Media (.3G2)',
|
50 |
-
'3ge6' => '3GPP (.3GP) Release 6 MBMS Extended Presentations',
|
51 |
-
'3ge7' => '3GPP (.3GP) Release 7 MBMS Extended Presentations',
|
52 |
-
'3gg6' => '3GPP Release 6 General Profile',
|
53 |
-
'3gp1' => '3GPP Media (.3GP) Release 1 ? (non-existent)',
|
54 |
-
'3gp2' => '3GPP Media (.3GP) Release 2 ? (non-existent)',
|
55 |
-
'3gp3' => '3GPP Media (.3GP) Release 3 ? (non-existent)',
|
56 |
-
'3gp4' => '3GPP Media (.3GP) Release 4',
|
57 |
-
'3gp5' => '3GPP Media (.3GP) Release 5',
|
58 |
-
'3gp6' => '3GPP Media (.3GP) Release 6 Basic Profile',
|
59 |
-
'3gr6' => '3GPP Media (.3GP) Release 6 Progressive Download',
|
60 |
-
'3gs6' => '3GPP Media (.3GP) Release 6 Streaming Servers',
|
61 |
-
'3gs7' => '3GPP Media (.3GP) Release 7 Streaming Servers',
|
62 |
-
'avc1' => 'MP4 Base w/ AVC ext [ISO 14496-12:2005]',
|
63 |
-
'caep' => 'Canon Digital Camera',
|
64 |
-
'caqv' => 'Casio Digital Camera',
|
65 |
-
'cdes' => 'Convergent Design',
|
66 |
-
'f4v' => 'Video for Adobe Flash Player 9+ (.F4V)',
|
67 |
-
'f4p' => 'Protected Video for Adobe Flash Player 9+ (.F4P)',
|
68 |
-
'f4a' => 'Audio for Adobe Flash Player 9+ (.F4A)',
|
69 |
-
'f4b' => 'Audio Book for Adobe Flash Player 9+ (.F4B)',
|
70 |
-
'isc2' => 'ISMACryp 2.0 Encrypted File',
|
71 |
-
'iso2' => 'MP4 Base Media v2 [ISO 14496-12:2005]',
|
72 |
-
'isom' => 'MP4 Base Media v1 [IS0 14496-12:2003]',
|
73 |
-
'jp2' => 'JPEG 2000 Image (.JP2) [ISO 15444-1 ?]',
|
74 |
-
'jp20' => 'Unknown, from GPAC samples (prob non-existent)',
|
75 |
-
'jpm' => 'JPEG 2000 Compound Image (.JPM) [ISO 15444-6]',
|
76 |
-
'jpx' => 'JPEG 2000 w/ extensions (.JPX) [ISO 15444-2]',
|
77 |
-
'kddi' => '3GPP2 EZmovie for KDDI 3G Cellphones',
|
78 |
-
'm4a ' => 'Apple iTunes AAC-LC (.M4A) Audio',
|
79 |
-
'm4b ' => 'Apple iTunes AAC-LC (.M4B) Audio Book',
|
80 |
-
'm4p ' => 'Apple iTunes AAC-LC (.M4P) AES Protected Audio',
|
81 |
-
'm4v ' => 'Apple iTunes Video (.M4V) Video',
|
82 |
-
'm4vh' => 'Apple TV (.M4V)',
|
83 |
-
'm4vp' => 'Apple iPhone (.M4V)',
|
84 |
-
'mj2s' => 'Motion JPEG 2000 [ISO 15444-3] Simple Profile',
|
85 |
-
'mjp2' => 'Motion JPEG 2000 [ISO 15444-3] General Profile',
|
86 |
-
'mmp4' => 'MPEG-4/3GPP Mobile Profile (.MP4 / .3GP) (for NTT)',
|
87 |
-
'mp21' => 'MPEG-21 [ISO/IEC 21000-9]',
|
88 |
-
'mp41' => 'MP4 v1 [ISO 14496-1:ch13]',
|
89 |
-
'mp42' => 'MP4 v2 [ISO 14496-14]',
|
90 |
-
'mp71' => 'MP4 w/ MPEG-7 Metadata [per ISO 14496-12]',
|
91 |
-
'mppi' => 'Photo Player, MAF [ISO/IEC 23000-3]',
|
92 |
-
'mqt' => 'Sony / Mobile QuickTime (.MQV)',
|
93 |
-
'msnv' => 'MPEG-4 (.MP4) for SonyPSP',
|
94 |
-
'ndas' => 'MP4 v2 [ISO 14496-14] Nero Digital AAC Audio',
|
95 |
-
'ndsc' => 'MPEG-4 (.MP4) Nero Cinema Profile',
|
96 |
-
'ndsh' => 'MPEG-4 (.MP4) Nero HDTV Profile',
|
97 |
-
'ndsm' => 'MPEG-4 (.MP4) Nero Mobile Profile',
|
98 |
-
'ndsp' => 'MPEG-4 (.MP4) Nero Portable Profile',
|
99 |
-
'ndss' => 'MPEG-4 (.MP4) Nero Standard Profile',
|
100 |
-
'ndxc' => 'H.264/MPEG-4 AVC (.MP4) Nero Cinema Profile',
|
101 |
-
'ndxh' => 'H.264/MPEG-4 AVC (.MP4) Nero HDTV Profile',
|
102 |
-
'ndxm' => 'H.264/MPEG-4 AVC (.MP4) Nero Mobile Profile',
|
103 |
-
'ndxp' => 'H.264/MPEG-4 AVC (.MP4) Nero Portable Profile',
|
104 |
-
'ndxs' => 'H.264/MPEG-4 AVC (.MP4) Nero Standard Profile',
|
105 |
-
'odcf ' => 'OMA DCF DRM Format 2.0 (OMA-TS-DRM-DCF-V2_0-20060303-A)',
|
106 |
-
'opf2 ' => 'OMA PDCF DRM Format 2.1 (OMA-TS-DRM-DCF-V2_1-20070724-C)',
|
107 |
-
'opx2 ' => 'OMA PDCF DRM + XBS extensions (OMA-TS-DRM_XBS-V1_0-20070529-C)',
|
108 |
-
'qt ' => 'Apple QuickTime (.MOV/QT)',
|
109 |
-
'sdv' => 'SD Memory Card Video',
|
110 |
-
);
|
111 |
-
|
112 |
-
|
113 |
-
/**
|
114 |
-
* Constructor
|
115 |
-
*
|
116 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
117 |
-
* @param int $totalSize
|
118 |
-
* @param int $boxType
|
119 |
-
* @param file|string $data
|
120 |
-
* @param MP4Info_Box $parent
|
121 |
-
* @return MP4Info_Box_ftyp
|
122 |
-
* @access public
|
123 |
-
* @throws MP4Info_Exception
|
124 |
-
*/
|
125 |
-
public function __construct($totalSize, $boxType, $data, $parent) {
|
126 |
-
if (!self::isCompatible($boxType, $parent)) {
|
127 |
-
throw new MP4Info_Exception('This box isn\'t "ftyp"',MP4Info_Exception::CODE_INCOMPATIBLE,$boxType);
|
128 |
-
}
|
129 |
-
|
130 |
-
// Call ancestor
|
131 |
-
parent::__construct($totalSize, $boxType, false, $parent);
|
132 |
-
|
133 |
-
// Get data
|
134 |
-
$data = self::getDataFrom3rd($data, $totalSize);
|
135 |
-
|
136 |
-
// Unpack
|
137 |
-
$ar = unpack('NmajorBrand/NminorVersion/N*compatibleBrands',$data);
|
138 |
-
$compatibleBrands = array();
|
139 |
-
foreach ($ar as $k=>$v) {
|
140 |
-
if (substr($k,0,16) == 'compatibleBrands') {
|
141 |
-
$compatibleBrands[] = $v;
|
142 |
-
}
|
143 |
-
}
|
144 |
-
|
145 |
-
// Save properties
|
146 |
-
$this->majorBrand = $ar['majorBrand'];
|
147 |
-
$this->minorVersion = $ar['minorVersion'];
|
148 |
-
$this->compatibleBrands = $compatibleBrands;
|
149 |
-
} // Constructor
|
150 |
-
|
151 |
-
|
152 |
-
/**
|
153 |
-
* Check if block is compatible with class
|
154 |
-
*
|
155 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
156 |
-
* @param int $boxType
|
157 |
-
* @param MP4Info_Box $parent
|
158 |
-
* @return bool
|
159 |
-
* @access public
|
160 |
-
* @static
|
161 |
-
*/
|
162 |
-
static function isCompatible($boxType, $parent) {
|
163 |
-
return $boxType == 0x66747970;
|
164 |
-
} // isCompatible method
|
165 |
-
|
166 |
-
|
167 |
-
/**
|
168 |
-
* Major brand getter
|
169 |
-
*
|
170 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
171 |
-
* @return int
|
172 |
-
* @access public
|
173 |
-
*/
|
174 |
-
public function getMajorBrand() {
|
175 |
-
return $this->majorBrand;
|
176 |
-
} // getMajorBrand method
|
177 |
-
|
178 |
-
|
179 |
-
/**
|
180 |
-
* Minor version getter
|
181 |
-
*
|
182 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
183 |
-
* @return int
|
184 |
-
* @access public
|
185 |
-
*/
|
186 |
-
public function getMinorVersion() {
|
187 |
-
return $this->minorVersion;
|
188 |
-
} // getMinorVersion method
|
189 |
-
|
190 |
-
|
191 |
-
/**
|
192 |
-
* Compatible brands getter
|
193 |
-
*
|
194 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
195 |
-
* @return int[]
|
196 |
-
* @access public
|
197 |
-
*/
|
198 |
-
public function getCompatibleBrands() {
|
199 |
-
return $this->compatibleBrands;
|
200 |
-
} // getCompatibleBrands method
|
201 |
-
|
202 |
-
|
203 |
-
/**
|
204 |
-
* Convert a brand 32bit code to a string
|
205 |
-
*
|
206 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
207 |
-
* @param int $brand
|
208 |
-
* @return string
|
209 |
-
* @access public
|
210 |
-
* @static
|
211 |
-
*/
|
212 |
-
public static function brandToString($brand) {
|
213 |
-
if (isset(self::$$brandNames[$brand])) {
|
214 |
-
return self::$$brandNames[$brand];
|
215 |
-
} else {
|
216 |
-
return $brand;
|
217 |
-
}
|
218 |
-
} // brandToString method
|
219 |
-
|
220 |
-
|
221 |
-
/**
|
222 |
-
* String converter
|
223 |
-
*
|
224 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
225 |
-
* @return string
|
226 |
-
* @access public
|
227 |
-
*/
|
228 |
-
public function toString() {
|
229 |
-
return '[MP4Info_Box_ftyp]';
|
230 |
-
} // toString method
|
231 |
-
} // MP4Info_Box_ftyp method
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/lib/MP4Info/Box/hdlr.php
DELETED
@@ -1,140 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* MP4Info
|
4 |
-
*
|
5 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
6 |
-
* @copyright Copyright (c) 2006-2009 Tommy Lacroix
|
7 |
-
* @license LGPL version 3, http://www.gnu.org/licenses/lgpl.html
|
8 |
-
* @package php-mp4info
|
9 |
-
* @link $HeadURL: https://php-mp4info.googlecode.com/svn/trunk/MP4Info/Box/hdlr.php $
|
10 |
-
*/
|
11 |
-
|
12 |
-
// ---
|
13 |
-
|
14 |
-
/**
|
15 |
-
* 8.9 Handler Reference Box (HDLR)
|
16 |
-
*
|
17 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
18 |
-
* @version 1.0.20090601 $Id: hdlr.php 2 2009-06-11 14:12:31Z lacroix.tommy@gmail.com $
|
19 |
-
* @todo Factor this into a fullbox
|
20 |
-
*/
|
21 |
-
class MP4Info_Box_hdlr extends MP4Info_Box {
|
22 |
-
// {{{ Constants
|
23 |
-
const HANDLER_VIDEO = 'vide';
|
24 |
-
const HANDLER_SOUND = 'soun';
|
25 |
-
// }}} Constants
|
26 |
-
|
27 |
-
/**
|
28 |
-
* Handler type
|
29 |
-
*
|
30 |
-
* @var uint32
|
31 |
-
*/
|
32 |
-
protected $handlerType;
|
33 |
-
|
34 |
-
/**
|
35 |
-
* Name
|
36 |
-
*
|
37 |
-
* @var string
|
38 |
-
*/
|
39 |
-
protected $name;
|
40 |
-
|
41 |
-
/**
|
42 |
-
* Timezone
|
43 |
-
*
|
44 |
-
* @var int
|
45 |
-
* @static
|
46 |
-
*/
|
47 |
-
protected static $timezone = false;
|
48 |
-
|
49 |
-
/**
|
50 |
-
* Constructor
|
51 |
-
*
|
52 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
53 |
-
* @param int $totalSize
|
54 |
-
* @param int $boxType
|
55 |
-
* @param file|string $data
|
56 |
-
* @param MP4Info_Box $parent
|
57 |
-
* @return MP4Info_Box_hdlr
|
58 |
-
* @access public
|
59 |
-
* @throws MP4Info_Exception
|
60 |
-
*/
|
61 |
-
public function __construct($totalSize, $boxType, $data, $parent) {
|
62 |
-
if (!self::isCompatible($boxType, $parent)) {
|
63 |
-
throw new Exception('This box isn\'t "ftyp"');
|
64 |
-
}
|
65 |
-
|
66 |
-
// Get timezone
|
67 |
-
if (self::$timezone === false) {
|
68 |
-
self::$timezone = date('Z');
|
69 |
-
}
|
70 |
-
|
71 |
-
// Call ancestor
|
72 |
-
parent::__construct($totalSize,$boxType,'',$parent);
|
73 |
-
|
74 |
-
// Get data
|
75 |
-
$data = self::getDataFrom3rd($data,$totalSize);
|
76 |
-
|
77 |
-
// Unpack
|
78 |
-
$ar = unpack('Cversion/C3flags',$data);
|
79 |
-
if ($ar['version'] == 0) {
|
80 |
-
// 32 bit
|
81 |
-
$ar2 = unpack('Nctime/Nmtime/NtimeScale/Nduration',substr($data,4));
|
82 |
-
$len = 6*4;
|
83 |
-
} else if ($ar['version'] == 1) {
|
84 |
-
// 64 bit
|
85 |
-
$ar2 = unpack('N2ctime/N2mtime/NtimeScale/N2duration',substr($data,4));
|
86 |
-
$len = 9*4;
|
87 |
-
} else {
|
88 |
-
throw new Exception('Unhandled version: '.$ar['version']);
|
89 |
-
}
|
90 |
-
|
91 |
-
// Save
|
92 |
-
$this->version = $ar['version'];
|
93 |
-
$this->flags = $ar['flags1']*65536+$ar['flags1']*256+$ar['flags1']*1;
|
94 |
-
$this->ctime = date('r',(isset($ar2['ctime']) ? $ar2['ctime'] : $ar2['ctime1'])-2082826800-self::$timezone);
|
95 |
-
$this->mtime = date('r',(isset($ar2['mtime']) ? $ar2['mtime'] : $ar2['mtime1'])-2082826800-self::$timezone);
|
96 |
-
$this->timeScale = $ar2['timeScale'];
|
97 |
-
$this->duration = (isset($ar2['duration']) ? $ar2['duration'] : $ar2['duration1']);
|
98 |
-
$this->handlerType = substr($data,$len,4);
|
99 |
-
$this->name = substr($data,$len+8,-1);
|
100 |
-
} // Constructor
|
101 |
-
|
102 |
-
|
103 |
-
/**
|
104 |
-
* Check if block is compatible with class
|
105 |
-
*
|
106 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
107 |
-
* @param int $boxType
|
108 |
-
* @param MP4Info_Box $parent
|
109 |
-
* @return bool
|
110 |
-
* @access public
|
111 |
-
* @static
|
112 |
-
*/
|
113 |
-
static public function isCompatible($boxType, $parent) {
|
114 |
-
return $boxType == 0x68646c72;
|
115 |
-
} // isCompatible method
|
116 |
-
|
117 |
-
|
118 |
-
/**
|
119 |
-
* Handler type getter
|
120 |
-
*
|
121 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
122 |
-
* @return int
|
123 |
-
* @access public
|
124 |
-
*/
|
125 |
-
public function getHandlerType() {
|
126 |
-
return $this->handlerType;
|
127 |
-
} // getHandlerType method
|
128 |
-
|
129 |
-
|
130 |
-
/**
|
131 |
-
* String converter
|
132 |
-
*
|
133 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
134 |
-
* @return string
|
135 |
-
* @access public
|
136 |
-
*/
|
137 |
-
public function toString() {
|
138 |
-
return '[MP4Info_Box_hdlr:'.$this->handlerType.']';
|
139 |
-
} // toString method
|
140 |
-
} // MP4Info_Box_hdlr class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/lib/MP4Info/Box/ilst.php
DELETED
@@ -1,122 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* MP4Info
|
4 |
-
*
|
5 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
6 |
-
* @copyright Copyright (c) 2006-2009 Tommy Lacroix
|
7 |
-
* @license LGPL version 3, http://www.gnu.org/licenses/lgpl.html
|
8 |
-
* @package php-mp4info
|
9 |
-
* @link $HeadURL: https://php-mp4info.googlecode.com/svn/trunk/MP4Info/Box/ilst.php $
|
10 |
-
*/
|
11 |
-
|
12 |
-
// ---
|
13 |
-
|
14 |
-
/**
|
15 |
-
* x.x ??? (ILST)
|
16 |
-
*
|
17 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
18 |
-
* @version 1.0.20090601 $Id: ilst.php 2 2009-06-11 14:12:31Z lacroix.tommy@gmail.com $
|
19 |
-
*/
|
20 |
-
class MP4Info_Box_ilst extends MP4Info_Box_Container {
|
21 |
-
/**
|
22 |
-
* Values
|
23 |
-
*
|
24 |
-
* @var {}
|
25 |
-
* @access protected
|
26 |
-
*/
|
27 |
-
protected $values = array();
|
28 |
-
|
29 |
-
|
30 |
-
/**
|
31 |
-
* Constructor
|
32 |
-
*
|
33 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
34 |
-
* @param int $totalSize
|
35 |
-
* @param int $boxType
|
36 |
-
* @param file|string $data
|
37 |
-
* @param MP4Info_Box $parent
|
38 |
-
* @return MP4Info_Box_ilst
|
39 |
-
* @access public
|
40 |
-
* @throws MP4Info_Exception
|
41 |
-
*/
|
42 |
-
public function __construct($totalSize, $boxType, $data, $parent) {
|
43 |
-
if (!self::isCompatible($boxType, $parent)) {
|
44 |
-
throw new MP4Info_Exception('This box isn\'t "ilst"', MP4Info_Exception::CODE_INCOMPATIBLE, $boxType);
|
45 |
-
}
|
46 |
-
|
47 |
-
// Call ancestor
|
48 |
-
parent::__construct($totalSize, $boxType, $data, $parent);
|
49 |
-
} // Constructor
|
50 |
-
|
51 |
-
|
52 |
-
/**
|
53 |
-
* Check if block is compatible with class
|
54 |
-
*
|
55 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
56 |
-
* @param int $boxType
|
57 |
-
* @param MP4Info_Box $parent
|
58 |
-
* @return bool
|
59 |
-
* @access public
|
60 |
-
* @static
|
61 |
-
*/
|
62 |
-
static public function isCompatible($boxType, $parent) {
|
63 |
-
return $boxType == 0x696C7374;
|
64 |
-
} // isCompatible method
|
65 |
-
|
66 |
-
|
67 |
-
/**
|
68 |
-
* Check if a given key has a value
|
69 |
-
*
|
70 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
71 |
-
* @param string $k
|
72 |
-
* @return bool
|
73 |
-
* @access public
|
74 |
-
*/
|
75 |
-
public function hasValue($k) {
|
76 |
-
return (isset($this->values[$k])) || (isset($this->values[utf8_decode($k)]));
|
77 |
-
} // hasValue method
|
78 |
-
|
79 |
-
|
80 |
-
/**
|
81 |
-
* Get the value of a given key
|
82 |
-
*
|
83 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
84 |
-
* @param string $k
|
85 |
-
* @return mixed
|
86 |
-
* @access public
|
87 |
-
*/
|
88 |
-
public function getValue($k) {
|
89 |
-
if (isset($this->values[$k])) {
|
90 |
-
return $this->values[$k];
|
91 |
-
} else if (isset($this->values[utf8_decode($k)])) {
|
92 |
-
return $this->values[utf8_decode($k)];
|
93 |
-
} else {
|
94 |
-
return false;
|
95 |
-
}
|
96 |
-
} // getValue method
|
97 |
-
|
98 |
-
|
99 |
-
/**
|
100 |
-
* Set a value for a given key
|
101 |
-
*
|
102 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
103 |
-
* @param string $k
|
104 |
-
* @param mixed $v
|
105 |
-
* @access public
|
106 |
-
*/
|
107 |
-
public function setKeyValue($k,$v) {
|
108 |
-
$this->values[$k] = $v;
|
109 |
-
} // setKeyValue method
|
110 |
-
|
111 |
-
|
112 |
-
/**
|
113 |
-
* String converter
|
114 |
-
*
|
115 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
116 |
-
* @return string
|
117 |
-
* @access public
|
118 |
-
*/
|
119 |
-
public function toString() {
|
120 |
-
return '[MP4Info_Box_ilst:'.count($this->boxes).']';
|
121 |
-
} // toString method
|
122 |
-
} // MP4Info_Box_ilst class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/lib/MP4Info/Box/ilst_sub.php
DELETED
@@ -1,148 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* MP4Info
|
4 |
-
*
|
5 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
6 |
-
* @copyright Copyright (c) 2006-2009 Tommy Lacroix
|
7 |
-
* @license LGPL version 3, http://www.gnu.org/licenses/lgpl.html
|
8 |
-
* @package php-mp4info
|
9 |
-
* @link $HeadURL: https://php-mp4info.googlecode.com/svn/trunk/MP4Info/Box/ilst_sub.php $
|
10 |
-
*/
|
11 |
-
|
12 |
-
// ---
|
13 |
-
|
14 |
-
/**
|
15 |
-
* x.x ILST sub blocks (numerous)
|
16 |
-
*
|
17 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
18 |
-
* @version 1.1.20090611 $Id: ilst_sub.php 2 2009-06-11 14:12:31Z lacroix.tommy@gmail.com $
|
19 |
-
*/
|
20 |
-
class MP4Info_Box_ilst_sub extends MP4Info_Box {
|
21 |
-
/**
|
22 |
-
* Constructor
|
23 |
-
*
|
24 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
25 |
-
* @param int $totalSize
|
26 |
-
* @param int $boxType
|
27 |
-
* @param file|string $f
|
28 |
-
* @param MP4Info_Box $parent
|
29 |
-
* @return MP4Info_Box_ilst_sub
|
30 |
-
* @access public
|
31 |
-
* @throws MP4Info_Exception
|
32 |
-
*/
|
33 |
-
public function __construct($totalSize, $boxType, $data, $parent) {
|
34 |
-
if (!$parent instanceof MP4Info_Box_ilst) {
|
35 |
-
throw new MP4Info_Exception('This box isn\'t "islt" child', MP4Info_Exception::CODE_INCOMPATIBLE, $boxType);
|
36 |
-
}
|
37 |
-
|
38 |
-
// Call ancestor
|
39 |
-
parent::__construct($totalSize,$boxType,$data,$parent);
|
40 |
-
|
41 |
-
// Get data
|
42 |
-
$data = $this->data;
|
43 |
-
|
44 |
-
// Unpack
|
45 |
-
$type = self::getType($this->boxType);
|
46 |
-
$ar = unpack('Nlen',$data);
|
47 |
-
if (substr($data,4,4) == 'data') {
|
48 |
-
$info = substr($data,8,$ar['len']-8);
|
49 |
-
switch ($type) {
|
50 |
-
case 'uint8':
|
51 |
-
$info = reset(unpack('C',$info));
|
52 |
-
break;
|
53 |
-
case 'uint16':
|
54 |
-
$info = reset(unpack('n',$info));
|
55 |
-
break;
|
56 |
-
case 'uint32':
|
57 |
-
$info = reset(unpack('N',$info));
|
58 |
-
break;
|
59 |
-
case 'text':
|
60 |
-
break;
|
61 |
-
}
|
62 |
-
$this->data = $info;
|
63 |
-
$parent->setKeyValue($this->boxTypeStr, $info);
|
64 |
-
} else {
|
65 |
-
throw new MP4Info_Exception('Didn\'t get the "data" code');
|
66 |
-
}
|
67 |
-
} // Constructor
|
68 |
-
|
69 |
-
|
70 |
-
/**
|
71 |
-
* Check if block is compatible with class
|
72 |
-
*
|
73 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
74 |
-
* @param int $boxType
|
75 |
-
* @param MP4Info_Box $parent
|
76 |
-
* @return bool
|
77 |
-
* @access public
|
78 |
-
* @static
|
79 |
-
*/
|
80 |
-
static public function isCompatible($boxType, $parent) {
|
81 |
-
return ($parent instanceof MP4Info_Box_ilst);
|
82 |
-
} // isCompatible method
|
83 |
-
|
84 |
-
|
85 |
-
/**
|
86 |
-
* Get sub type
|
87 |
-
* http://atomicparsley.sourceforge.net/mpeg-4files.html
|
88 |
-
*
|
89 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
90 |
-
* @param int $boxType
|
91 |
-
* @return string
|
92 |
-
* @access protected
|
93 |
-
* @static
|
94 |
-
* @todo The © codes should be chr(...), as utf8 encoding messes things up
|
95 |
-
*/
|
96 |
-
static protected function getType($boxType) {
|
97 |
-
switch (pack('N',$boxType)) {
|
98 |
-
case '©alb':
|
99 |
-
case '©art':
|
100 |
-
case 'aART':
|
101 |
-
case '©cmt':
|
102 |
-
case '©day':
|
103 |
-
case '©nam':
|
104 |
-
case '©gen':
|
105 |
-
case '©wrt':
|
106 |
-
case '©too':
|
107 |
-
case 'cprt':
|
108 |
-
case '©grp':
|
109 |
-
case 'catg':
|
110 |
-
case 'desc':
|
111 |
-
case '©lyr':
|
112 |
-
case 'tvnn':
|
113 |
-
case 'tvsh':
|
114 |
-
case 'tven':
|
115 |
-
case 'purd':
|
116 |
-
return 'text';
|
117 |
-
|
118 |
-
case 'gnre':
|
119 |
-
case 'trkn':
|
120 |
-
case 'disk':
|
121 |
-
case 'tmpo':
|
122 |
-
case 'cpil':
|
123 |
-
case 'rtng':
|
124 |
-
case 'stik':
|
125 |
-
case 'pcst':
|
126 |
-
case 'purl':
|
127 |
-
case 'egid':
|
128 |
-
case 'tvsn':
|
129 |
-
case 'tves':
|
130 |
-
case 'pgap':
|
131 |
-
return 'uint8';
|
132 |
-
default:
|
133 |
-
return '';
|
134 |
-
}
|
135 |
-
} // getType method
|
136 |
-
|
137 |
-
|
138 |
-
/**
|
139 |
-
* String converter
|
140 |
-
*
|
141 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
142 |
-
* @return string
|
143 |
-
* @access public
|
144 |
-
*/
|
145 |
-
public function toString() {
|
146 |
-
return '[MP4Info_Box_ilst_sub['.$this->boxTypeStr.']:'.$this->getData().']';
|
147 |
-
} // toString converter
|
148 |
-
} // MP4Info_Box_ilst_sub class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/lib/MP4Info/Box/mdhd.php
DELETED
@@ -1,146 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* MP4Info
|
4 |
-
*
|
5 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
6 |
-
* @copyright Copyright (c) 2006-2009 Tommy Lacroix
|
7 |
-
* @license LGPL version 3, http://www.gnu.org/licenses/lgpl.html
|
8 |
-
* @package php-mp4info
|
9 |
-
* @link $HeadURL: https://php-mp4info.googlecode.com/svn/trunk/MP4Info/Box/mdhd.php $
|
10 |
-
*/
|
11 |
-
|
12 |
-
// ---
|
13 |
-
|
14 |
-
/**
|
15 |
-
* 8.8 Media Header Box (MDHD)
|
16 |
-
*
|
17 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
18 |
-
* @version 1.0.20090611 $Id: mdhd.php 2 2009-06-11 14:12:31Z lacroix.tommy@gmail.com $
|
19 |
-
* @todo Factor this into a fullbox
|
20 |
-
*/
|
21 |
-
class MP4Info_Box_mdhd extends MP4Info_Box {
|
22 |
-
/**
|
23 |
-
* Version
|
24 |
-
*
|
25 |
-
* @var int
|
26 |
-
*/
|
27 |
-
protected $version;
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Flags
|
31 |
-
*
|
32 |
-
* @var int
|
33 |
-
*/
|
34 |
-
protected $flags;
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Creation time
|
38 |
-
*
|
39 |
-
* @var string
|
40 |
-
*/
|
41 |
-
protected $ctime;
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Modification time
|
45 |
-
*
|
46 |
-
* @var unknown_type
|
47 |
-
*/
|
48 |
-
protected $mtime;
|
49 |
-
|
50 |
-
/**
|
51 |
-
* Time scale
|
52 |
-
*
|
53 |
-
* @var int
|
54 |
-
*/
|
55 |
-
protected $timeScale;
|
56 |
-
|
57 |
-
/**
|
58 |
-
* Duration
|
59 |
-
*
|
60 |
-
* @var int
|
61 |
-
*/
|
62 |
-
protected $duration;
|
63 |
-
|
64 |
-
/**
|
65 |
-
* Time zone
|
66 |
-
*
|
67 |
-
* @var int
|
68 |
-
* @static
|
69 |
-
*/
|
70 |
-
protected static $timezone = false;
|
71 |
-
|
72 |
-
|
73 |
-
/**
|
74 |
-
* Constructor
|
75 |
-
*
|
76 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
77 |
-
* @param int $totalSize
|
78 |
-
* @param int $boxType
|
79 |
-
* @param file|string $data
|
80 |
-
* @param MP4Info_Box $parent
|
81 |
-
* @return MP4Info_Box_mdhd
|
82 |
-
* @access public
|
83 |
-
* @throws MP4Info_Exception
|
84 |
-
*/
|
85 |
-
public function __construct($totalSize, $boxType, $data, $parent) {
|
86 |
-
if (!self::isCompatible($boxType, $parent)) {
|
87 |
-
throw new Exception('This box isn\'t "mdhd"');
|
88 |
-
}
|
89 |
-
|
90 |
-
// Get timezone
|
91 |
-
if (self::$timezone === false) {
|
92 |
-
self::$timezone = date('Z');
|
93 |
-
}
|
94 |
-
|
95 |
-
// Call ancestor
|
96 |
-
parent::__construct($totalSize, $boxType, '', $parent);
|
97 |
-
|
98 |
-
// Unpack
|
99 |
-
$ar = unpack('Cversion/C3flags',$data);
|
100 |
-
if ($ar['version'] == 0) {
|
101 |
-
// 32 bit
|
102 |
-
$ar2 = unpack('Nctime/Nmtime/NtimeScale/Nduration/nlanguage/ndummy',substr($data,4));
|
103 |
-
} else if ($ar['version'] == 1) {
|
104 |
-
// 64 bit
|
105 |
-
$ar2 = unpack('N2ctime/N2mtime/NtimeScale/N2duration/nlanguage/ndummy',substr($data,4));
|
106 |
-
} else {
|
107 |
-
throw new Exception('Unhandled version: '.$ar['version']);
|
108 |
-
}
|
109 |
-
|
110 |
-
// Save
|
111 |
-
$this->version = $ar['version'];
|
112 |
-
$this->flags = $ar['flags1']*65536+$ar['flags1']*256+$ar['flags1']*1;
|
113 |
-
$this->ctime = date('r',(isset($ar2['ctime']) ? $ar2['ctime'] : $ar2['ctime1'])-2082826800-self::$timezone);
|
114 |
-
$this->mtime = date('r',(isset($ar2['mtime']) ? $ar2['mtime'] : $ar2['mtime1'])-2082826800-self::$timezone);
|
115 |
-
$this->timeScale = $ar2['timeScale'];
|
116 |
-
$this->duration = (isset($ar2['duration']) ? $ar2['duration'] : $ar2['duration1']);
|
117 |
-
$this->language = MP4Info_Helper::fromPackedLetters($ar2['language'],1);
|
118 |
-
} // Constructor
|
119 |
-
|
120 |
-
|
121 |
-
/**
|
122 |
-
* Check if block is compatible with class
|
123 |
-
*
|
124 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
125 |
-
* @param int $boxType
|
126 |
-
* @param MP4Info_Box $parent
|
127 |
-
* @return bool
|
128 |
-
* @access public
|
129 |
-
* @static
|
130 |
-
*/
|
131 |
-
static function isCompatible($boxType, $parent) {
|
132 |
-
return $boxType == 0x6d646864;
|
133 |
-
} // isCompatible method
|
134 |
-
|
135 |
-
|
136 |
-
/**
|
137 |
-
* String converter
|
138 |
-
*
|
139 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
140 |
-
* @return string
|
141 |
-
* @access public
|
142 |
-
*/
|
143 |
-
public function toString() {
|
144 |
-
return '[MP4Info_Box_mdhd]';
|
145 |
-
} // toString method
|
146 |
-
} // MP4Info_Box_mdhd class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/lib/MP4Info/Box/meta.php
DELETED
@@ -1,69 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* MP4Info
|
4 |
-
*
|
5 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
6 |
-
* @copyright Copyright (c) 2006-2009 Tommy Lacroix
|
7 |
-
* @license LGPL version 3, http://www.gnu.org/licenses/lgpl.html
|
8 |
-
* @package php-mp4info
|
9 |
-
* @link $HeadURL: https://php-mp4info.googlecode.com/svn/trunk/MP4Info/Box/meta.php $
|
10 |
-
*/
|
11 |
-
|
12 |
-
// ---
|
13 |
-
|
14 |
-
/**
|
15 |
-
* x.x Meta (META)
|
16 |
-
*
|
17 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
18 |
-
* @version 1.0.20090601 $Id: meta.php 2 2009-06-11 14:12:31Z lacroix.tommy@gmail.com $
|
19 |
-
*/
|
20 |
-
class MP4Info_Box_meta extends MP4Info_Box_container {
|
21 |
-
|
22 |
-
/**
|
23 |
-
* Constructor
|
24 |
-
*
|
25 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
26 |
-
* @param int $totalSize
|
27 |
-
* @param int $boxType
|
28 |
-
* @param file|string $f
|
29 |
-
* @param MP4Info_Box_meta $parent
|
30 |
-
* @access public
|
31 |
-
* @throws MP4Info_Exception
|
32 |
-
*/
|
33 |
-
public function __construct($totalSize, $boxType, $data, $parent=false) {
|
34 |
-
if (!self::isCompatible($boxType, $parent)) {
|
35 |
-
throw new MP4Info_Exception('This box isn\'t "meta"',MP4Info_Exception::CODE_INCOMPATIBLE,false,$boxType);
|
36 |
-
}
|
37 |
-
|
38 |
-
$ar = unpack('Nlen',$data);
|
39 |
-
|
40 |
-
parent::__construct($totalSize, $boxType, substr($data,4,$ar['len']), $parent);
|
41 |
-
} // Constructor
|
42 |
-
|
43 |
-
|
44 |
-
/**
|
45 |
-
* Check if block is compatible with class
|
46 |
-
*
|
47 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
48 |
-
* @param int $boxType
|
49 |
-
* @param MP4Info_Box $parent
|
50 |
-
* @return bool
|
51 |
-
* @access public
|
52 |
-
* @static
|
53 |
-
*/
|
54 |
-
static public function isCompatible($boxType, $parent) {
|
55 |
-
return $boxType == 0x6D657461;
|
56 |
-
} // isCompatible method
|
57 |
-
|
58 |
-
|
59 |
-
/**
|
60 |
-
* String converter
|
61 |
-
*
|
62 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
63 |
-
* @return string
|
64 |
-
* @access public
|
65 |
-
*/
|
66 |
-
public function toString() {
|
67 |
-
return '[MP4Info_Box_meta:'.count($this->boxes).']';
|
68 |
-
} // toString method
|
69 |
-
} // MP4Info_Box_meta class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/lib/MP4Info/Box/mvhd.php
DELETED
@@ -1,233 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* MP4Info
|
4 |
-
*
|
5 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
6 |
-
* @copyright Copyright (c) 2006-2009 Tommy Lacroix
|
7 |
-
* @license LGPL version 3, http://www.gnu.org/licenses/lgpl.html
|
8 |
-
* @package php-mp4info
|
9 |
-
* @link $HeadURL: https://php-mp4info.googlecode.com/svn/trunk/MP4Info/Box/mvhd.php $
|
10 |
-
*/
|
11 |
-
|
12 |
-
// ---
|
13 |
-
|
14 |
-
/**
|
15 |
-
* x.x Movie Header (MVHD)
|
16 |
-
*
|
17 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
18 |
-
* @version 1.0.20090601 $Id: mvhd.php 2 2009-06-11 14:12:31Z lacroix.tommy@gmail.com $
|
19 |
-
* @todo Factor this into a fullbox
|
20 |
-
*/
|
21 |
-
class MP4Info_Box_mvhd extends MP4Info_Box {
|
22 |
-
/**
|
23 |
-
* Version
|
24 |
-
*
|
25 |
-
* @var int
|
26 |
-
*/
|
27 |
-
protected $version;
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Flags
|
31 |
-
*
|
32 |
-
* @var int
|
33 |
-
*/
|
34 |
-
protected $flags;
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Creation time
|
38 |
-
*
|
39 |
-
* @var string
|
40 |
-
*/
|
41 |
-
protected $ctime;
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Modification time
|
45 |
-
*
|
46 |
-
* @var unknown_type
|
47 |
-
*/
|
48 |
-
protected $mtime;
|
49 |
-
|
50 |
-
/**
|
51 |
-
* Time scale
|
52 |
-
*
|
53 |
-
* @var int
|
54 |
-
*/
|
55 |
-
protected $timeScale;
|
56 |
-
|
57 |
-
/**
|
58 |
-
* Duration
|
59 |
-
*
|
60 |
-
* @var int
|
61 |
-
*/
|
62 |
-
protected $duration;
|
63 |
-
|
64 |
-
/**
|
65 |
-
* Rate
|
66 |
-
*
|
67 |
-
* @var int
|
68 |
-
*/
|
69 |
-
protected $rate;
|
70 |
-
|
71 |
-
/**
|
72 |
-
* Volume
|
73 |
-
*
|
74 |
-
* @var int
|
75 |
-
*/
|
76 |
-
protected $volume;
|
77 |
-
|
78 |
-
/**
|
79 |
-
* Time zone
|
80 |
-
*
|
81 |
-
* @var int
|
82 |
-
* @static
|
83 |
-
*/
|
84 |
-
protected static $timezone = false;
|
85 |
-
|
86 |
-
|
87 |
-
/**
|
88 |
-
* Constructor
|
89 |
-
*
|
90 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
91 |
-
* @param int $totalSize
|
92 |
-
* @param int $boxType
|
93 |
-
* @param file|string $data
|
94 |
-
* @param MP4Info_Box $parent
|
95 |
-
* @return MP4Info_Box_mvhd
|
96 |
-
* @access public
|
97 |
-
* @throws MP4Info_Exception
|
98 |
-
*/
|
99 |
-
public function __construct($totalSize, $boxType, $data, $parent) {
|
100 |
-
if (!self::isCompatible($boxType)) {
|
101 |
-
throw new Exception('This box isn\'t "mvhd"');
|
102 |
-
}
|
103 |
-
|
104 |
-
// Get timezone
|
105 |
-
if (self::$timezone === false) {
|
106 |
-
self::$timezone = date('Z');
|
107 |
-
}
|
108 |
-
|
109 |
-
// Call ancestor's constructor
|
110 |
-
parent::__construct($totalSize,$boxType,'',$parent);
|
111 |
-
|
112 |
-
// Unpack
|
113 |
-
$ar = unpack('Cversion/C3flags',$data);
|
114 |
-
if ($ar['version'] == 0) {
|
115 |
-
// 32 bit
|
116 |
-
$ar2 = unpack('Nctime/Nmtime/NtimeScale/Nduration/Nrate/nvolume/ndummy/N2dummy2/N9matrix/N3dummy3/NnextTrack',substr($data,4));
|
117 |
-
} else if ($ar['version'] == 1) {
|
118 |
-
// 64 bit
|
119 |
-
$ar2 = unpack('N2ctime/N2mtime/NtimeScale/N2duration/Nrate/nvolume/ndummy/N2dummy2/N9matrix/N3dummy3/NnextTrack',substr($data,4));
|
120 |
-
} else {
|
121 |
-
throw new Exception('Unhandled version: '.$ar['version']);
|
122 |
-
}
|
123 |
-
|
124 |
-
// Save
|
125 |
-
$this->version = $ar['version'];
|
126 |
-
$this->flags = $ar['flags1']*65536+$ar['flags1']*256+$ar['flags1']*1;
|
127 |
-
$this->ctime = date('r',(isset($ar2['ctime']) ? $ar2['ctime'] : $ar2['ctime1'])-2082826800-self::$timezone);
|
128 |
-
$this->mtime = date('r',(isset($ar2['mtime']) ? $ar2['mtime'] : $ar2['mtime1'])-2082826800-self::$timezone);
|
129 |
-
$this->timeScale = $ar2['timeScale'];
|
130 |
-
$this->duration = (isset($ar2['duration']) ? $ar2['duration'] : $ar2['duration1']);
|
131 |
-
$this->rate = MP4Info_Helper::fromFixed16($ar2['rate']);
|
132 |
-
$this->volume = MP4Info_Helper::fromFixed8($ar2['volume']);
|
133 |
-
} // Constructor
|
134 |
-
|
135 |
-
|
136 |
-
/**
|
137 |
-
* Check if block is compatible with class
|
138 |
-
*
|
139 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
140 |
-
* @param int $boxType
|
141 |
-
* @param MP4Info_Box $parent
|
142 |
-
* @return bool
|
143 |
-
* @access public
|
144 |
-
* @static
|
145 |
-
*/
|
146 |
-
static public function isCompatible($boxType) {
|
147 |
-
return $boxType == 0x6D766864;
|
148 |
-
} // isCompatible method
|
149 |
-
|
150 |
-
|
151 |
-
/**
|
152 |
-
* Creation time getter
|
153 |
-
*
|
154 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
155 |
-
* @return int
|
156 |
-
* @access public
|
157 |
-
*/
|
158 |
-
public function getCreationTime() {
|
159 |
-
return $this->ctime;
|
160 |
-
} // getCreationTime method
|
161 |
-
|
162 |
-
|
163 |
-
/**
|
164 |
-
* Time scale getter
|
165 |
-
*
|
166 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
167 |
-
* @return int
|
168 |
-
* @access public
|
169 |
-
*/
|
170 |
-
public function getTimeScale() {
|
171 |
-
return $this->timeScale;
|
172 |
-
} // getTimeScale method
|
173 |
-
|
174 |
-
|
175 |
-
/**
|
176 |
-
* Duration getter
|
177 |
-
*
|
178 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
179 |
-
* @return int
|
180 |
-
* @access public
|
181 |
-
*/
|
182 |
-
public function getDuration() {
|
183 |
-
return $this->duration;
|
184 |
-
} // getDuration method
|
185 |
-
|
186 |
-
|
187 |
-
/**
|
188 |
-
* Real duration getter
|
189 |
-
*
|
190 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
191 |
-
* @return float
|
192 |
-
* @access public
|
193 |
-
*/
|
194 |
-
public function getRealDuration() {
|
195 |
-
return $this->duration/$this->timeScale;
|
196 |
-
} // getRealDuration method
|
197 |
-
|
198 |
-
|
199 |
-
/**
|
200 |
-
* Rate getter
|
201 |
-
*
|
202 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
203 |
-
* @return int
|
204 |
-
* @access public
|
205 |
-
*/
|
206 |
-
public function getRate() {
|
207 |
-
return $this->rate();
|
208 |
-
} // getRate method
|
209 |
-
|
210 |
-
|
211 |
-
/**
|
212 |
-
* Volume getter
|
213 |
-
*
|
214 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
215 |
-
* @return int
|
216 |
-
* @access public
|
217 |
-
*/
|
218 |
-
public function getVolume() {
|
219 |
-
return $this->volume();
|
220 |
-
} // getVolume method
|
221 |
-
|
222 |
-
|
223 |
-
/**
|
224 |
-
* String converter
|
225 |
-
*
|
226 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
227 |
-
* @return string
|
228 |
-
* @access public
|
229 |
-
*/
|
230 |
-
public function toString() {
|
231 |
-
return '[MP4Info_Box_mvhd]';
|
232 |
-
} // toString method
|
233 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/lib/MP4Info/Box/stsd.php
DELETED
@@ -1,139 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* MP4Info
|
4 |
-
*
|
5 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
6 |
-
* @copyright Copyright (c) 2006-2009 Tommy Lacroix
|
7 |
-
* @license LGPL version 3, http://www.gnu.org/licenses/lgpl.html
|
8 |
-
* @package php-mp4info
|
9 |
-
* @link $HeadURL: https://php-mp4info.googlecode.com/svn/trunk/MP4Info/Box/stsd.php $
|
10 |
-
*/
|
11 |
-
|
12 |
-
// ---
|
13 |
-
|
14 |
-
/**
|
15 |
-
* x.x ??? (STSD)
|
16 |
-
*
|
17 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
18 |
-
* @version 1.0.20090601 $Id: stsd.php 2 2009-06-11 14:12:31Z lacroix.tommy@gmail.com $
|
19 |
-
* @todo Factor this into a fullbox
|
20 |
-
*/
|
21 |
-
class MP4Info_Box_stsd extends MP4Info_Box {
|
22 |
-
/**
|
23 |
-
* Version
|
24 |
-
*
|
25 |
-
* @var int
|
26 |
-
*/
|
27 |
-
protected $version;
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Flags
|
31 |
-
*
|
32 |
-
* @var int
|
33 |
-
*/
|
34 |
-
protected $flags;
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Count
|
38 |
-
*
|
39 |
-
* @var int
|
40 |
-
*/
|
41 |
-
protected $count;
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Values
|
45 |
-
*
|
46 |
-
* @var string{}
|
47 |
-
*/
|
48 |
-
protected $values = array();
|
49 |
-
|
50 |
-
|
51 |
-
/**
|
52 |
-
* Constructor
|
53 |
-
*
|
54 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
55 |
-
* @param int $totalSize
|
56 |
-
* @param int $boxType
|
57 |
-
* @param file|string $data
|
58 |
-
* @param MP4Info_Box $parent
|
59 |
-
* @return MP4Info_Box_stsd
|
60 |
-
* @access public
|
61 |
-
* @throws MP4Info_Exception
|
62 |
-
*/
|
63 |
-
public function __construct($totalSize, $boxType, $data, $parent) {
|
64 |
-
if (!self::isCompatible($boxType, $parent)) {
|
65 |
-
throw new Exception('This box isn\'t "stsd"');
|
66 |
-
}
|
67 |
-
|
68 |
-
// Call ancestor
|
69 |
-
parent::__construct($totalSize,$boxType,'',$parent);
|
70 |
-
|
71 |
-
// Get data
|
72 |
-
$data = self::getDataFrom3rd($data, $totalSize);
|
73 |
-
|
74 |
-
// Unpack
|
75 |
-
$ar = unpack('Cversion/C3flags/Ncount',$data);
|
76 |
-
$this->version = $ar['version'];
|
77 |
-
$this->flags = $ar['flags1']*65536+$ar['flags1']*256+$ar['flags1']*1;
|
78 |
-
$this->count = $ar['count'];
|
79 |
-
|
80 |
-
// Unpack SAMPLEDESCRIPTION
|
81 |
-
$desc = substr($data,8);
|
82 |
-
for ($i=0;$i<$this->count;$i++) {
|
83 |
-
$ar = unpack('Nlen',$desc);
|
84 |
-
$type = substr($desc,4,4);
|
85 |
-
$info = substr($desc,8,$ar['len']-8);
|
86 |
-
$desc = substr($desc,$ar['len']);
|
87 |
-
$this->values[$type] = $info;
|
88 |
-
}
|
89 |
-
} // Constructor
|
90 |
-
|
91 |
-
|
92 |
-
/**
|
93 |
-
* Check if block is compatible with class
|
94 |
-
*
|
95 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
96 |
-
* @param int $boxType
|
97 |
-
* @param MP4Info_Box $parent
|
98 |
-
* @return bool
|
99 |
-
* @access public
|
100 |
-
* @static
|
101 |
-
*/
|
102 |
-
static public function isCompatible($boxType, $parent) {
|
103 |
-
return $boxType == 0x73747364;
|
104 |
-
} // isCompatible method
|
105 |
-
|
106 |
-
/**
|
107 |
-
* Values getter
|
108 |
-
*
|
109 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
110 |
-
* @return string{}
|
111 |
-
* @access public
|
112 |
-
*/
|
113 |
-
public function getValues() {
|
114 |
-
return $this->values;
|
115 |
-
} // getValues method
|
116 |
-
|
117 |
-
/**
|
118 |
-
* Value getter
|
119 |
-
*
|
120 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
121 |
-
* @param string $key
|
122 |
-
* @return string
|
123 |
-
* @access public
|
124 |
-
*/
|
125 |
-
public function getValue($key) {
|
126 |
-
return isset($this->values[$key]) ? $this->values[$key] : false;
|
127 |
-
} // getValue method
|
128 |
-
|
129 |
-
/**
|
130 |
-
* String converter
|
131 |
-
*
|
132 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
133 |
-
* @return string
|
134 |
-
* @access public
|
135 |
-
*/
|
136 |
-
public function toString() {
|
137 |
-
return '[MP4Info_Box_stsd:'.implode(',',array_keys($this->values)).']';
|
138 |
-
} // toString method
|
139 |
-
} // MP4Info_Box_stsd class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/lib/MP4Info/Box/tkhd.php
DELETED
@@ -1,215 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* MP4Info
|
4 |
-
*
|
5 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
6 |
-
* @copyright Copyright (c) 2006-2009 Tommy Lacroix
|
7 |
-
* @license LGPL version 3, http://www.gnu.org/licenses/lgpl.html
|
8 |
-
* @package php-mp4info
|
9 |
-
* @link $HeadURL: https://php-mp4info.googlecode.com/svn/trunk/MP4Info/Box/tkhd.php $
|
10 |
-
*/
|
11 |
-
|
12 |
-
// ---
|
13 |
-
|
14 |
-
/**
|
15 |
-
* x.x Track Header (TKHD)
|
16 |
-
*
|
17 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
18 |
-
* @version 1.1.20090611 $Id: tkhd.php 2 2009-06-11 14:12:31Z lacroix.tommy@gmail.com $
|
19 |
-
* @todo Factor this into a fullbox
|
20 |
-
*/
|
21 |
-
class MP4Info_Box_tkhd extends MP4Info_Box {
|
22 |
-
/**
|
23 |
-
* Version
|
24 |
-
*
|
25 |
-
* @var int
|
26 |
-
*/
|
27 |
-
protected $version;
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Flags
|
31 |
-
*
|
32 |
-
* @var int
|
33 |
-
*/
|
34 |
-
protected $flags;
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Creation time
|
38 |
-
*
|
39 |
-
* @var string
|
40 |
-
*/
|
41 |
-
protected $ctime;
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Modification time
|
45 |
-
*
|
46 |
-
* @var unknown_type
|
47 |
-
*/
|
48 |
-
protected $mtime;
|
49 |
-
|
50 |
-
/**
|
51 |
-
* Time scale
|
52 |
-
*
|
53 |
-
* @var int
|
54 |
-
*/
|
55 |
-
protected $timeScale;
|
56 |
-
|
57 |
-
/**
|
58 |
-
* Duration
|
59 |
-
*
|
60 |
-
* @var int
|
61 |
-
*/
|
62 |
-
protected $duration;
|
63 |
-
|
64 |
-
/**
|
65 |
-
* Layer
|
66 |
-
*
|
67 |
-
* @var int
|
68 |
-
*/
|
69 |
-
protected $layer;
|
70 |
-
|
71 |
-
/**
|
72 |
-
* Volume
|
73 |
-
*
|
74 |
-
* @var float
|
75 |
-
*/
|
76 |
-
protected $volume;
|
77 |
-
|
78 |
-
/**
|
79 |
-
* Width
|
80 |
-
*
|
81 |
-
* @var float
|
82 |
-
*/
|
83 |
-
protected $width;
|
84 |
-
|
85 |
-
/**
|
86 |
-
* Height
|
87 |
-
*
|
88 |
-
* @var float
|
89 |
-
*/
|
90 |
-
protected $height;
|
91 |
-
|
92 |
-
/**
|
93 |
-
* Time zone
|
94 |
-
*
|
95 |
-
* @var int
|
96 |
-
* @static
|
97 |
-
*/
|
98 |
-
protected static $timezone = false;
|
99 |
-
|
100 |
-
/**
|
101 |
-
* Constructor
|
102 |
-
*
|
103 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
104 |
-
* @param int $totalSize
|
105 |
-
* @param int $boxType
|
106 |
-
* @param file|string $data
|
107 |
-
* @param MP4Info_Box $parent
|
108 |
-
* @return MP4Info_Box_tkhd
|
109 |
-
* @access public
|
110 |
-
* @throws MP4Info_Exception
|
111 |
-
*/
|
112 |
-
public function __construct($totalSize, $boxType, $data, $parent=false) {
|
113 |
-
if (!self::isCompatible($boxType,$parent)) {
|
114 |
-
throw new Exception('This box isn\'t "tkhd"');
|
115 |
-
}
|
116 |
-
|
117 |
-
// Get timezone
|
118 |
-
if (self::$timezone === false) {
|
119 |
-
self::$timezone = date('Z');
|
120 |
-
}
|
121 |
-
|
122 |
-
// Call ancestor's constructor
|
123 |
-
parent::__construct($totalSize,$boxType,'',$parent);
|
124 |
-
|
125 |
-
// Get data
|
126 |
-
$data = self::getDataFrom3rd($data,$totalSize);
|
127 |
-
|
128 |
-
// Unpack
|
129 |
-
$ar = unpack('Cversion/C3flags',$data);
|
130 |
-
if ($ar['version'] == 0) {
|
131 |
-
// 32 bit
|
132 |
-
$ar2 = unpack('Nctime/Nmtime/NtrackId/Ndummy/Nduration/N2dummy1/nlayer/naltGroup/nvolume/ndummy2/N9matrix/Nwidth/Nheight',substr($data,4));
|
133 |
-
} else if ($ar['version'] == 1) {
|
134 |
-
// 64 bit
|
135 |
-
$ar2 = unpack('N2ctime/N2mtime/NtrackId/Ndummy/N2duration/N2dummy1/nlayer/naltGroup/nvolume/ndummy2/N9matrix/Nwidth/Nheight',substr($data,4));
|
136 |
-
} else {
|
137 |
-
throw new Exception('Unhandled version: '.$ar['version']);
|
138 |
-
}
|
139 |
-
|
140 |
-
// Save
|
141 |
-
$this->version = $ar['version'];
|
142 |
-
$this->flags = $ar['flags1']*65536+$ar['flags1']*256+$ar['flags1']*1;
|
143 |
-
$this->ctime = date('r',(isset($ar2['ctime']) ? $ar2['ctime'] : $ar2['ctime1'])-2082826800-self::$timezone);
|
144 |
-
$this->mtime = date('r',(isset($ar2['mtime']) ? $ar2['mtime'] : $ar2['mtime1'])-2082826800-self::$timezone);
|
145 |
-
$this->trackId = $ar2['trackId'];
|
146 |
-
$this->duration = (isset($ar2['duration']) ? $ar2['duration'] : $ar2['duration1']);
|
147 |
-
$this->layer = ($ar2['layer']>32767 ? $ar2['layer']-65536 : $ar2['layer']);
|
148 |
-
$this->volume = MP4Info_Helper::fromFixed8($ar2['volume']);
|
149 |
-
$this->width = MP4Info_Helper::fromFixed16($ar2['width']);
|
150 |
-
$this->height = MP4Info_Helper::fromFixed16($ar2['height']);
|
151 |
-
} // Constructor
|
152 |
-
|
153 |
-
|
154 |
-
/**
|
155 |
-
* Check if block is compatible with class
|
156 |
-
*
|
157 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
158 |
-
* @param int $boxType
|
159 |
-
* @param MP4Info_Box $parent
|
160 |
-
* @return bool
|
161 |
-
* @access public
|
162 |
-
* @static
|
163 |
-
*/
|
164 |
-
static public function isCompatible($boxType, $parent) {
|
165 |
-
return $boxType == 0x746b6864;
|
166 |
-
} // isCompatible method
|
167 |
-
|
168 |
-
|
169 |
-
/**
|
170 |
-
* Width getter
|
171 |
-
*
|
172 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
173 |
-
* @return float
|
174 |
-
* @access public
|
175 |
-
*/
|
176 |
-
public function getWidth() {
|
177 |
-
return $this->width;
|
178 |
-
} // getWidth method
|
179 |
-
|
180 |
-
|
181 |
-
/**
|
182 |
-
* Height getter
|
183 |
-
*
|
184 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
185 |
-
* @return float
|
186 |
-
* @access public
|
187 |
-
*/
|
188 |
-
public function getHeight() {
|
189 |
-
return $this->height;
|
190 |
-
} // getHeight method
|
191 |
-
|
192 |
-
|
193 |
-
/**
|
194 |
-
* Duration getter
|
195 |
-
*
|
196 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
197 |
-
* @return int
|
198 |
-
* @access public
|
199 |
-
*/
|
200 |
-
public function getDuration() {
|
201 |
-
return $this->duration;
|
202 |
-
} // getDuration method
|
203 |
-
|
204 |
-
|
205 |
-
/**
|
206 |
-
* String converter
|
207 |
-
*
|
208 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
209 |
-
* @return string
|
210 |
-
* @access public
|
211 |
-
*/
|
212 |
-
public function toString() {
|
213 |
-
return '[MP4Info_Box_tkhd]';
|
214 |
-
} // toString method
|
215 |
-
} // MP4Info_Box_tkhd class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/lib/MP4Info/Box/uuid.php
DELETED
@@ -1,117 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* MP4Info
|
4 |
-
*
|
5 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
6 |
-
* @copyright Copyright (c) 2006-2009 Tommy Lacroix
|
7 |
-
* @license LGPL version 3, http://www.gnu.org/licenses/lgpl.html
|
8 |
-
* @package php-mp4info
|
9 |
-
* @link $HeadURL: https://php-mp4info.googlecode.com/svn/trunk/MP4Info/Box/uuid.php $
|
10 |
-
*/
|
11 |
-
|
12 |
-
// ---
|
13 |
-
|
14 |
-
/**
|
15 |
-
* x.x ??? (UUID)
|
16 |
-
*
|
17 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
18 |
-
* @version 1.0.20090601 $Id: uuid.php 2 2009-06-11 14:12:31Z lacroix.tommy@gmail.com $
|
19 |
-
* @todo Limited to XMP meta data... extend
|
20 |
-
*/
|
21 |
-
class MP4Info_Box_uuid extends MP4Info_Box {
|
22 |
-
// {{{ Constants
|
23 |
-
const UUID_XMP_METADATA = 'BE7ACFCB97A942';
|
24 |
-
// }}} Constants
|
25 |
-
|
26 |
-
/**
|
27 |
-
* UUID
|
28 |
-
*
|
29 |
-
* @var string
|
30 |
-
*/
|
31 |
-
protected $uuid;
|
32 |
-
|
33 |
-
/**
|
34 |
-
* XMP data
|
35 |
-
*
|
36 |
-
* @var string
|
37 |
-
*/
|
38 |
-
protected $xmp;
|
39 |
-
|
40 |
-
|
41 |
-
/**
|
42 |
-
* Constructor
|
43 |
-
*
|
44 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
45 |
-
* @param int $totalSize
|
46 |
-
* @param int $boxType
|
47 |
-
* @param file|string $data
|
48 |
-
* @param MP4Info_Box $parent
|
49 |
-
* @return MP4Info_Box_uuid
|
50 |
-
* @access public
|
51 |
-
* @throws MP4Info_Exception
|
52 |
-
*/
|
53 |
-
public function __construct($totalSize, $boxType, $data, $parent) {
|
54 |
-
if (!self::isCompatible($boxType, $parent)) {
|
55 |
-
throw new Exception('This box isn\'t "uuid"');
|
56 |
-
}
|
57 |
-
|
58 |
-
// Call ancestor
|
59 |
-
parent::__construct($totalSize,$boxType,'',$parent);
|
60 |
-
|
61 |
-
// Unpack
|
62 |
-
$data = self::getDataFrom3rd($data,$totalSize);
|
63 |
-
$this->uuid = bin2hex(substr($data,0,16));
|
64 |
-
$this->xmp = substr($data,16);
|
65 |
-
} // Constructor
|
66 |
-
|
67 |
-
|
68 |
-
/**
|
69 |
-
* Check if block is compatible with class
|
70 |
-
*
|
71 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
72 |
-
* @param int $boxType
|
73 |
-
* @param MP4Info_Box $parent
|
74 |
-
* @return bool
|
75 |
-
* @access public
|
76 |
-
* @static
|
77 |
-
*/
|
78 |
-
static public function isCompatible($boxType, $parent) {
|
79 |
-
return $boxType == 0x75756964;
|
80 |
-
} // isCompatible method
|
81 |
-
|
82 |
-
|
83 |
-
/**
|
84 |
-
* UUID getter
|
85 |
-
*
|
86 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
87 |
-
* @return string
|
88 |
-
* @access public
|
89 |
-
*/
|
90 |
-
public function getUUID() {
|
91 |
-
return strtoupper($this->uuid);
|
92 |
-
} // getUUID method
|
93 |
-
|
94 |
-
|
95 |
-
/**
|
96 |
-
* XMP Meta data getter
|
97 |
-
*
|
98 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
99 |
-
* @return string
|
100 |
-
* @access public
|
101 |
-
*/
|
102 |
-
public function getXMPMetaData() {
|
103 |
-
return (substr(strtoupper($this->uuid),0,14) == self::UUID_XMP_METADATA) ? $this->xmp : false;
|
104 |
-
} // getXMPMetaData method
|
105 |
-
|
106 |
-
|
107 |
-
/**
|
108 |
-
* String converter
|
109 |
-
*
|
110 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
111 |
-
* @return string
|
112 |
-
* @access public
|
113 |
-
*/
|
114 |
-
public function toString() {
|
115 |
-
return '[MP4Info_Box_uuid]';
|
116 |
-
} // toString method
|
117 |
-
} // MP4Info_Box_uuid class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/lib/MP4Info/Exception.php
DELETED
@@ -1,61 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* MP4Info
|
4 |
-
*
|
5 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
6 |
-
* @copyright Copyright (c) 2006-2009 Tommy Lacroix
|
7 |
-
* @license LGPL version 3, http://www.gnu.org/licenses/lgpl.html
|
8 |
-
* @package php-mp4info
|
9 |
-
* @link $HeadURL: https://php-mp4info.googlecode.com/svn/trunk/MP4Info/Exception.php $
|
10 |
-
*/
|
11 |
-
|
12 |
-
// ---
|
13 |
-
|
14 |
-
/**
|
15 |
-
* MP4Info Exception Class
|
16 |
-
*
|
17 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
18 |
-
* @version 1.1.20090611 $Id: Exception.php 2 2009-06-11 14:12:31Z lacroix.tommy@gmail.com $
|
19 |
-
*/
|
20 |
-
|
21 |
-
class MP4Info_Exception extends Exception {
|
22 |
-
// {{{ Constants
|
23 |
-
const CODE_UNKNOWN = 0x00;
|
24 |
-
const CODE_INCOMPATIBLE = 0x01;
|
25 |
-
// }}} Constants
|
26 |
-
|
27 |
-
/**
|
28 |
-
* Box type
|
29 |
-
*
|
30 |
-
* @var int
|
31 |
-
*/
|
32 |
-
protected $boxType;
|
33 |
-
|
34 |
-
|
35 |
-
/**
|
36 |
-
* Constructor
|
37 |
-
*
|
38 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
39 |
-
* @param string $message
|
40 |
-
* @param int $code
|
41 |
-
* @param int $boxType
|
42 |
-
* @return MP4Info_Exception
|
43 |
-
* @access public
|
44 |
-
*/
|
45 |
-
public function __construct($message, $code = 0, $boxType=false) {
|
46 |
-
parent::__construct($message, $code);
|
47 |
-
$this->boxType = $boxType;
|
48 |
-
} // Constructor
|
49 |
-
|
50 |
-
|
51 |
-
/**
|
52 |
-
* Box type getter
|
53 |
-
*
|
54 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
55 |
-
* @return int
|
56 |
-
* @access public
|
57 |
-
*/
|
58 |
-
public function getBoxType() {
|
59 |
-
return $this->boxType;
|
60 |
-
} // getBoxType method
|
61 |
-
} // MP4Info_Exception class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/lib/MP4Info/Helper.php
DELETED
@@ -1,72 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* MP4Info
|
4 |
-
*
|
5 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
6 |
-
* @copyright Copyright (c) 2006-2009 Tommy Lacroix
|
7 |
-
* @license LGPL version 3, http://www.gnu.org/licenses/lgpl.html
|
8 |
-
* @package php-mp4info
|
9 |
-
* @link $HeadURL: https://php-mp4info.googlecode.com/svn/trunk/MP4Info/Helper.php $
|
10 |
-
*/
|
11 |
-
|
12 |
-
// ---
|
13 |
-
|
14 |
-
/**
|
15 |
-
* MP4Info helper functions
|
16 |
-
*
|
17 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
18 |
-
* @version 1.0.20090601 $Id: Helper.php 2 2009-06-11 14:12:31Z lacroix.tommy@gmail.com $
|
19 |
-
*/
|
20 |
-
class MP4Info_Helper {
|
21 |
-
/**
|
22 |
-
* Convert a short to a float
|
23 |
-
*
|
24 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
25 |
-
* @param int $n
|
26 |
-
* @return float
|
27 |
-
* @access public
|
28 |
-
* @static
|
29 |
-
*/
|
30 |
-
public static function fromFixed8($n) {
|
31 |
-
return $n / 256;
|
32 |
-
} // fromFixed8 method
|
33 |
-
|
34 |
-
|
35 |
-
/**
|
36 |
-
* Convert a long to a float
|
37 |
-
*
|
38 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
39 |
-
* @param int $n
|
40 |
-
* @return float
|
41 |
-
* @access public
|
42 |
-
* @static
|
43 |
-
*/
|
44 |
-
public static function fromFixed16($n) {
|
45 |
-
return $n / 65536;
|
46 |
-
} // fromFixed16 method
|
47 |
-
|
48 |
-
|
49 |
-
/**
|
50 |
-
* Convert binary packed (5bit) letters to string
|
51 |
-
*
|
52 |
-
* @author Tommy Lacroix <lacroix.tommy@gmail.com>
|
53 |
-
* @param int $n
|
54 |
-
* @param int $pad
|
55 |
-
* @return string
|
56 |
-
* @access public
|
57 |
-
* @static
|
58 |
-
*/
|
59 |
-
public static function fromPackedLetters($n, $pad=1) {
|
60 |
-
$s = decbin($n);
|
61 |
-
$s .= str_repeat('0',8-(strlen($s)%8));
|
62 |
-
$s = substr($s,0,-$pad);
|
63 |
-
$out = '';
|
64 |
-
while (strlen($s)>=5) {
|
65 |
-
$letter = substr($s,0,5);
|
66 |
-
$nl = bindec($letter) + 0x60;
|
67 |
-
$out .= chr($nl);
|
68 |
-
$s = substr($s,5);
|
69 |
-
}
|
70 |
-
return $out;
|
71 |
-
} // fromPackedLetters method
|
72 |
-
} // MP4Info_Helper class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/lib/getid3/getid3.lib.php
ADDED
@@ -0,0 +1,1317 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
/////////////////////////////////////////////////////////////////
|
7 |
+
// //
|
8 |
+
// getid3.lib.php - part of getID3() //
|
9 |
+
// See readme.txt for more details //
|
10 |
+
// ///
|
11 |
+
/////////////////////////////////////////////////////////////////
|
12 |
+
|
13 |
+
|
14 |
+
class getid3_lib
|
15 |
+
{
|
16 |
+
|
17 |
+
static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8') {
|
18 |
+
$returnstring = '';
|
19 |
+
for ($i = 0; $i < strlen($string); $i++) {
|
20 |
+
if ($hex) {
|
21 |
+
$returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
|
22 |
+
} else {
|
23 |
+
$returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string{$i}) ? $string{$i} : '�');
|
24 |
+
}
|
25 |
+
if ($spaces) {
|
26 |
+
$returnstring .= ' ';
|
27 |
+
}
|
28 |
+
}
|
29 |
+
if (!empty($htmlencoding)) {
|
30 |
+
if ($htmlencoding === true) {
|
31 |
+
$htmlencoding = 'UTF-8'; // prior to getID3 v1.9.0 the function's 4th parameter was boolean
|
32 |
+
}
|
33 |
+
$returnstring = htmlentities($returnstring, ENT_QUOTES, $htmlencoding);
|
34 |
+
}
|
35 |
+
return $returnstring;
|
36 |
+
}
|
37 |
+
|
38 |
+
static function trunc($floatnumber) {
|
39 |
+
// truncates a floating-point number at the decimal point
|
40 |
+
// returns int (if possible, otherwise float)
|
41 |
+
if ($floatnumber >= 1) {
|
42 |
+
$truncatednumber = floor($floatnumber);
|
43 |
+
} elseif ($floatnumber <= -1) {
|
44 |
+
$truncatednumber = ceil($floatnumber);
|
45 |
+
} else {
|
46 |
+
$truncatednumber = 0;
|
47 |
+
}
|
48 |
+
if (getid3_lib::intValueSupported($truncatednumber)) {
|
49 |
+
$truncatednumber = (int) $truncatednumber;
|
50 |
+
}
|
51 |
+
return $truncatednumber;
|
52 |
+
}
|
53 |
+
|
54 |
+
|
55 |
+
static function safe_inc(&$variable, $increment=1) {
|
56 |
+
if (isset($variable)) {
|
57 |
+
$variable += $increment;
|
58 |
+
} else {
|
59 |
+
$variable = $increment;
|
60 |
+
}
|
61 |
+
return true;
|
62 |
+
}
|
63 |
+
|
64 |
+
static function CastAsInt($floatnum) {
|
65 |
+
// convert to float if not already
|
66 |
+
$floatnum = (float) $floatnum;
|
67 |
+
|
68 |
+
// convert a float to type int, only if possible
|
69 |
+
if (getid3_lib::trunc($floatnum) == $floatnum) {
|
70 |
+
// it's not floating point
|
71 |
+
if (getid3_lib::intValueSupported($floatnum)) {
|
72 |
+
// it's within int range
|
73 |
+
$floatnum = (int) $floatnum;
|
74 |
+
}
|
75 |
+
}
|
76 |
+
return $floatnum;
|
77 |
+
}
|
78 |
+
|
79 |
+
public static function intValueSupported($num) {
|
80 |
+
// check if integers are 64-bit
|
81 |
+
static $hasINT64 = null;
|
82 |
+
if ($hasINT64 === null) { // 10x faster than is_null()
|
83 |
+
$hasINT64 = is_int(pow(2, 31)); // 32-bit int are limited to (2^31)-1
|
84 |
+
if (!$hasINT64 && !defined('PHP_INT_MIN')) {
|
85 |
+
define('PHP_INT_MIN', ~PHP_INT_MAX);
|
86 |
+
}
|
87 |
+
}
|
88 |
+
// if integers are 64-bit - no other check required
|
89 |
+
if ($hasINT64 || (($num <= PHP_INT_MAX) && ($num >= PHP_INT_MIN))) {
|
90 |
+
return true;
|
91 |
+
}
|
92 |
+
return false;
|
93 |
+
}
|
94 |
+
|
95 |
+
static function DecimalizeFraction($fraction) {
|
96 |
+
list($numerator, $denominator) = explode('/', $fraction);
|
97 |
+
return $numerator / ($denominator ? $denominator : 1);
|
98 |
+
}
|
99 |
+
|
100 |
+
|
101 |
+
static function DecimalBinary2Float($binarynumerator) {
|
102 |
+
$numerator = getid3_lib::Bin2Dec($binarynumerator);
|
103 |
+
$denominator = getid3_lib::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator)));
|
104 |
+
return ($numerator / $denominator);
|
105 |
+
}
|
106 |
+
|
107 |
+
|
108 |
+
static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) {
|
109 |
+
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
|
110 |
+
if (strpos($binarypointnumber, '.') === false) {
|
111 |
+
$binarypointnumber = '0.'.$binarypointnumber;
|
112 |
+
} elseif ($binarypointnumber{0} == '.') {
|
113 |
+
$binarypointnumber = '0'.$binarypointnumber;
|
114 |
+
}
|
115 |
+
$exponent = 0;
|
116 |
+
while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
|
117 |
+
if (substr($binarypointnumber, 1, 1) == '.') {
|
118 |
+
$exponent--;
|
119 |
+
$binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3);
|
120 |
+
} else {
|
121 |
+
$pointpos = strpos($binarypointnumber, '.');
|
122 |
+
$exponent += ($pointpos - 1);
|
123 |
+
$binarypointnumber = str_replace('.', '', $binarypointnumber);
|
124 |
+
$binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1);
|
125 |
+
}
|
126 |
+
}
|
127 |
+
$binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT);
|
128 |
+
return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent);
|
129 |
+
}
|
130 |
+
|
131 |
+
|
132 |
+
static function Float2BinaryDecimal($floatvalue) {
|
133 |
+
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
|
134 |
+
$maxbits = 128; // to how many bits of precision should the calculations be taken?
|
135 |
+
$intpart = getid3_lib::trunc($floatvalue);
|
136 |
+
$floatpart = abs($floatvalue - $intpart);
|
137 |
+
$pointbitstring = '';
|
138 |
+
while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) {
|
139 |
+
$floatpart *= 2;
|
140 |
+
$pointbitstring .= (string) getid3_lib::trunc($floatpart);
|
141 |
+
$floatpart -= getid3_lib::trunc($floatpart);
|
142 |
+
}
|
143 |
+
$binarypointnumber = decbin($intpart).'.'.$pointbitstring;
|
144 |
+
return $binarypointnumber;
|
145 |
+
}
|
146 |
+
|
147 |
+
|
148 |
+
static function Float2String($floatvalue, $bits) {
|
149 |
+
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
|
150 |
+
switch ($bits) {
|
151 |
+
case 32:
|
152 |
+
$exponentbits = 8;
|
153 |
+
$fractionbits = 23;
|
154 |
+
break;
|
155 |
+
|
156 |
+
case 64:
|
157 |
+
$exponentbits = 11;
|
158 |
+
$fractionbits = 52;
|
159 |
+
break;
|
160 |
+
|
161 |
+
default:
|
162 |
+
return false;
|
163 |
+
break;
|
164 |
+
}
|
165 |
+
if ($floatvalue >= 0) {
|
166 |
+
$signbit = '0';
|
167 |
+
} else {
|
168 |
+
$signbit = '1';
|
169 |
+
}
|
170 |
+
$normalizedbinary = getid3_lib::NormalizeBinaryPoint(getid3_lib::Float2BinaryDecimal($floatvalue), $fractionbits);
|
171 |
+
$biasedexponent = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent
|
172 |
+
$exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT);
|
173 |
+
$fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT);
|
174 |
+
|
175 |
+
return getid3_lib::BigEndian2String(getid3_lib::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false);
|
176 |
+
}
|
177 |
+
|
178 |
+
|
179 |
+
static function LittleEndian2Float($byteword) {
|
180 |
+
return getid3_lib::BigEndian2Float(strrev($byteword));
|
181 |
+
}
|
182 |
+
|
183 |
+
|
184 |
+
static function BigEndian2Float($byteword) {
|
185 |
+
// ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
|
186 |
+
// http://www.psc.edu/general/software/packages/ieee/ieee.html
|
187 |
+
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
|
188 |
+
|
189 |
+
$bitword = getid3_lib::BigEndian2Bin($byteword);
|
190 |
+
if (!$bitword) {
|
191 |
+
return 0;
|
192 |
+
}
|
193 |
+
$signbit = $bitword{0};
|
194 |
+
|
195 |
+
switch (strlen($byteword) * 8) {
|
196 |
+
case 32:
|
197 |
+
$exponentbits = 8;
|
198 |
+
$fractionbits = 23;
|
199 |
+
break;
|
200 |
+
|
201 |
+
case 64:
|
202 |
+
$exponentbits = 11;
|
203 |
+
$fractionbits = 52;
|
204 |
+
break;
|
205 |
+
|
206 |
+
case 80:
|
207 |
+
// 80-bit Apple SANE format
|
208 |
+
// http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
|
209 |
+
$exponentstring = substr($bitword, 1, 15);
|
210 |
+
$isnormalized = intval($bitword{16});
|
211 |
+
$fractionstring = substr($bitword, 17, 63);
|
212 |
+
$exponent = pow(2, getid3_lib::Bin2Dec($exponentstring) - 16383);
|
213 |
+
$fraction = $isnormalized + getid3_lib::DecimalBinary2Float($fractionstring);
|
214 |
+
$floatvalue = $exponent * $fraction;
|
215 |
+
if ($signbit == '1') {
|
216 |
+
$floatvalue *= -1;
|
217 |
+
}
|
218 |
+
return $floatvalue;
|
219 |
+
break;
|
220 |
+
|
221 |
+
default:
|
222 |
+
return false;
|
223 |
+
break;
|
224 |
+
}
|
225 |
+
$exponentstring = substr($bitword, 1, $exponentbits);
|
226 |
+
$fractionstring = substr($bitword, $exponentbits + 1, $fractionbits);
|
227 |
+
$exponent = getid3_lib::Bin2Dec($exponentstring);
|
228 |
+
$fraction = getid3_lib::Bin2Dec($fractionstring);
|
229 |
+
|
230 |
+
if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) {
|
231 |
+
// Not a Number
|
232 |
+
$floatvalue = false;
|
233 |
+
} elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) {
|
234 |
+
if ($signbit == '1') {
|
235 |
+
$floatvalue = '-infinity';
|
236 |
+
} else {
|
237 |
+
$floatvalue = '+infinity';
|
238 |
+
}
|
239 |
+
} elseif (($exponent == 0) && ($fraction == 0)) {
|
240 |
+
if ($signbit == '1') {
|
241 |
+
$floatvalue = -0;
|
242 |
+
} else {
|
243 |
+
$floatvalue = 0;
|
244 |
+
}
|
245 |
+
$floatvalue = ($signbit ? 0 : -0);
|
246 |
+
} elseif (($exponent == 0) && ($fraction != 0)) {
|
247 |
+
// These are 'unnormalized' values
|
248 |
+
$floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * getid3_lib::DecimalBinary2Float($fractionstring);
|
249 |
+
if ($signbit == '1') {
|
250 |
+
$floatvalue *= -1;
|
251 |
+
}
|
252 |
+
} elseif ($exponent != 0) {
|
253 |
+
$floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + getid3_lib::DecimalBinary2Float($fractionstring));
|
254 |
+
if ($signbit == '1') {
|
255 |
+
$floatvalue *= -1;
|
256 |
+
}
|
257 |
+
}
|
258 |
+
return (float) $floatvalue;
|
259 |
+
}
|
260 |
+
|
261 |
+
|
262 |
+
static function BigEndian2Int($byteword, $synchsafe=false, $signed=false) {
|
263 |
+
$intvalue = 0;
|
264 |
+
$bytewordlen = strlen($byteword);
|
265 |
+
if ($bytewordlen == 0) {
|
266 |
+
return false;
|
267 |
+
}
|
268 |
+
for ($i = 0; $i < $bytewordlen; $i++) {
|
269 |
+
if ($synchsafe) { // disregard MSB, effectively 7-bit bytes
|
270 |
+
//$intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7); // faster, but runs into problems past 2^31 on 32-bit systems
|
271 |
+
$intvalue += (ord($byteword{$i}) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7);
|
272 |
+
} else {
|
273 |
+
$intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
|
274 |
+
}
|
275 |
+
}
|
276 |
+
if ($signed && !$synchsafe) {
|
277 |
+
// synchsafe ints are not allowed to be signed
|
278 |
+
if ($bytewordlen <= PHP_INT_SIZE) {
|
279 |
+
$signMaskBit = 0x80 << (8 * ($bytewordlen - 1));
|
280 |
+
if ($intvalue & $signMaskBit) {
|
281 |
+
$intvalue = 0 - ($intvalue & ($signMaskBit - 1));
|
282 |
+
}
|
283 |
+
} else {
|
284 |
+
throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits ('.strlen($byteword).') in getid3_lib::BigEndian2Int()');
|
285 |
+
break;
|
286 |
+
}
|
287 |
+
}
|
288 |
+
return getid3_lib::CastAsInt($intvalue);
|
289 |
+
}
|
290 |
+
|
291 |
+
|
292 |
+
static function LittleEndian2Int($byteword, $signed=false) {
|
293 |
+
return getid3_lib::BigEndian2Int(strrev($byteword), false, $signed);
|
294 |
+
}
|
295 |
+
|
296 |
+
|
297 |
+
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 |
+
static function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) {
|
308 |
+
if ($number < 0) {
|
309 |
+
throw new Exception('ERROR: getid3_lib::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 getid3_lib::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 |
+
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 |
+
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 getid3_lib::CastAsInt($decvalue * $signmult);
|
355 |
+
}
|
356 |
+
|
357 |
+
|
358 |
+
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(getid3_lib::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string;
|
364 |
+
}
|
365 |
+
return $string;
|
366 |
+
}
|
367 |
+
|
368 |
+
|
369 |
+
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 |
+
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] = getid3_lib::array_merge_clobber($newarray[$key], $val);
|
394 |
+
} else {
|
395 |
+
$newarray[$key] = $val;
|
396 |
+
}
|
397 |
+
}
|
398 |
+
return $newarray;
|
399 |
+
}
|
400 |
+
|
401 |
+
|
402 |
+
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] = getid3_lib::array_merge_noclobber($newarray[$key], $val);
|
410 |
+
} elseif (!isset($newarray[$key])) {
|
411 |
+
$newarray[$key] = $val;
|
412 |
+
}
|
413 |
+
}
|
414 |
+
return $newarray;
|
415 |
+
}
|
416 |
+
|
417 |
+
|
418 |
+
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 |
+
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 |
+
static function PlaytimeString($seconds) {
|
445 |
+
$sign = (($seconds < 0) ? '-' : '');
|
446 |
+
$seconds = abs($seconds);
|
447 |
+
$H = floor( $seconds / 3600);
|
448 |
+
$M = floor(($seconds - (3600 * $H) ) / 60);
|
449 |
+
$S = 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 |
+
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 getid3_lib::CastAsInt($macdate - 2082844800);
|
458 |
+
}
|
459 |
+
|
460 |
+
|
461 |
+
static function FixedPoint8_8($rawdata) {
|
462 |
+
return getid3_lib::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (getid3_lib::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8));
|
463 |
+
}
|
464 |
+
|
465 |
+
|
466 |
+
static function FixedPoint16_16($rawdata) {
|
467 |
+
return getid3_lib::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (getid3_lib::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16));
|
468 |
+
}
|
469 |
+
|
470 |
+
|
471 |
+
static function FixedPoint2_30($rawdata) {
|
472 |
+
$binarystring = getid3_lib::BigEndian2Bin($rawdata);
|
473 |
+
return getid3_lib::Bin2Dec(substr($binarystring, 0, 2)) + (float) (getid3_lib::Bin2Dec(substr($binarystring, 2, 30)) / pow(2, 30));
|
474 |
+
}
|
475 |
+
|
476 |
+
|
477 |
+
static function CreateDeepArray($ArrayPath, $Separator, $Value) {
|
478 |
+
// assigns $Value to a nested array path:
|
479 |
+
// $foo = getid3_lib::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 |
+
while ($ArrayPath && ($ArrayPath{0} == $Separator)) {
|
485 |
+
$ArrayPath = substr($ArrayPath, 1);
|
486 |
+
}
|
487 |
+
if (($pos = strpos($ArrayPath, $Separator)) !== false) {
|
488 |
+
$ReturnedArray[substr($ArrayPath, 0, $pos)] = getid3_lib::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value);
|
489 |
+
} else {
|
490 |
+
$ReturnedArray[$ArrayPath] = $Value;
|
491 |
+
}
|
492 |
+
return $ReturnedArray;
|
493 |
+
}
|
494 |
+
|
495 |
+
static function array_max($arraydata, $returnkey=false) {
|
496 |
+
$maxvalue = false;
|
497 |
+
$maxkey = false;
|
498 |
+
foreach ($arraydata as $key => $value) {
|
499 |
+
if (!is_array($value)) {
|
500 |
+
if ($value > $maxvalue) {
|
501 |
+
$maxvalue = $value;
|
502 |
+
$maxkey = $key;
|
503 |
+
}
|
504 |
+
}
|
505 |
+
}
|
506 |
+
return ($returnkey ? $maxkey : $maxvalue);
|
507 |
+
}
|
508 |
+
|
509 |
+
static function array_min($arraydata, $returnkey=false) {
|
510 |
+
$minvalue = false;
|
511 |
+
$minkey = false;
|
512 |
+
foreach ($arraydata as $key => $value) {
|
513 |
+
if (!is_array($value)) {
|
514 |
+
if ($value > $minvalue) {
|
515 |
+
$minvalue = $value;
|
516 |
+
$minkey = $key;
|
517 |
+
}
|
518 |
+
}
|
519 |
+
}
|
520 |
+
return ($returnkey ? $minkey : $minvalue);
|
521 |
+
}
|
522 |
+
|
523 |
+
static function XML2array($XMLstring) {
|
524 |
+
if (function_exists('simplexml_load_string')) {
|
525 |
+
if (function_exists('get_object_vars')) {
|
526 |
+
$XMLobject = simplexml_load_string($XMLstring);
|
527 |
+
return self::SimpleXMLelement2array($XMLobject);
|
528 |
+
}
|
529 |
+
}
|
530 |
+
return false;
|
531 |
+
}
|
532 |
+
|
533 |
+
static function SimpleXMLelement2array($XMLobject) {
|
534 |
+
if (!is_object($XMLobject) && !is_array($XMLobject)) {
|
535 |
+
return $XMLobject;
|
536 |
+
}
|
537 |
+
$XMLarray = (is_object($XMLobject) ? get_object_vars($XMLobject) : $XMLobject);
|
538 |
+
foreach ($XMLarray as $key => $value) {
|
539 |
+
$XMLarray[$key] = self::SimpleXMLelement2array($value);
|
540 |
+
}
|
541 |
+
return $XMLarray;
|
542 |
+
}
|
543 |
+
|
544 |
+
|
545 |
+
// Allan Hansen <ah�artemis*dk>
|
546 |
+
// getid3_lib::md5_data() - returns md5sum for a file from startuing position to absolute end position
|
547 |
+
static function hash_data($file, $offset, $end, $algorithm) {
|
548 |
+
static $tempdir = '';
|
549 |
+
if (!getid3_lib::intValueSupported($end)) {
|
550 |
+
return false;
|
551 |
+
}
|
552 |
+
switch ($algorithm) {
|
553 |
+
case 'md5':
|
554 |
+
$hash_function = 'md5_file';
|
555 |
+
$unix_call = 'md5sum';
|
556 |
+
$windows_call = 'md5sum.exe';
|
557 |
+
$hash_length = 32;
|
558 |
+
break;
|
559 |
+
|
560 |
+
case 'sha1':
|
561 |
+
$hash_function = 'sha1_file';
|
562 |
+
$unix_call = 'sha1sum';
|
563 |
+
$windows_call = 'sha1sum.exe';
|
564 |
+
$hash_length = 40;
|
565 |
+
break;
|
566 |
+
|
567 |
+
default:
|
568 |
+
throw new Exception('Invalid algorithm ('.$algorithm.') in getid3_lib::hash_data()');
|
569 |
+
break;
|
570 |
+
}
|
571 |
+
$size = $end - $offset;
|
572 |
+
while (true) {
|
573 |
+
if (GETID3_OS_ISWINDOWS) {
|
574 |
+
|
575 |
+
// It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data
|
576 |
+
// Fall back to create-temp-file method:
|
577 |
+
if ($algorithm == 'sha1') {
|
578 |
+
break;
|
579 |
+
}
|
580 |
+
|
581 |
+
$RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call);
|
582 |
+
foreach ($RequiredFiles as $required_file) {
|
583 |
+
if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
|
584 |
+
// helper apps not available - fall back to old method
|
585 |
+
break;
|
586 |
+
}
|
587 |
+
}
|
588 |
+
$commandline = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' "'.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).'" | ';
|
589 |
+
$commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | ';
|
590 |
+
$commandline .= GETID3_HELPERAPPSDIR.$windows_call;
|
591 |
+
|
592 |
+
} else {
|
593 |
+
|
594 |
+
$commandline = 'head -c'.$end.' '.escapeshellarg($file).' | ';
|
595 |
+
$commandline .= 'tail -c'.$size.' | ';
|
596 |
+
$commandline .= $unix_call;
|
597 |
+
|
598 |
+
}
|
599 |
+
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
|
600 |
+
//throw new Exception('PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm');
|
601 |
+
break;
|
602 |
+
}
|
603 |
+
return substr(`$commandline`, 0, $hash_length);
|
604 |
+
}
|
605 |
+
|
606 |
+
if (empty($tempdir)) {
|
607 |
+
// yes this is ugly, feel free to suggest a better way
|
608 |
+
require_once(dirname(__FILE__).'/getid3.php');
|
609 |
+
$getid3_temp = new getID3();
|
610 |
+
$tempdir = $getid3_temp->tempdir;
|
611 |
+
unset($getid3_temp);
|
612 |
+
}
|
613 |
+
// try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir
|
614 |
+
if (($data_filename = tempnam($tempdir, 'gI3')) === false) {
|
615 |
+
// can't find anywhere to create a temp file, just fail
|
616 |
+
return false;
|
617 |
+
}
|
618 |
+
|
619 |
+
// Init
|
620 |
+
$result = false;
|
621 |
+
|
622 |
+
// copy parts of file
|
623 |
+
try {
|
624 |
+
getid3_lib::CopyFileParts($file, $data_filename, $offset, $end - $offset);
|
625 |
+
$result = $hash_function($data_filename);
|
626 |
+
} catch (Exception $e) {
|
627 |
+
throw new Exception('getid3_lib::CopyFileParts() failed in getid_lib::hash_data(): '.$e->getMessage());
|
628 |
+
}
|
629 |
+
unlink($data_filename);
|
630 |
+
return $result;
|
631 |
+
}
|
632 |
+
|
633 |
+
static function CopyFileParts($filename_source, $filename_dest, $offset, $length) {
|
634 |
+
if (!getid3_lib::intValueSupported($offset + $length)) {
|
635 |
+
throw new Exception('cannot copy file portion, it extends beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit');
|
636 |
+
}
|
637 |
+
if (is_readable($filename_source) && is_file($filename_source) && ($fp_src = fopen($filename_source, 'rb'))) {
|
638 |
+
if (($fp_dest = fopen($filename_dest, 'wb'))) {
|
639 |
+
if (fseek($fp_src, $offset, SEEK_SET) == 0) {
|
640 |
+
$byteslefttowrite = $length;
|
641 |
+
while (($byteslefttowrite > 0) && ($buffer = fread($fp_src, min($byteslefttowrite, getID3::FREAD_BUFFER_SIZE)))) {
|
642 |
+
$byteswritten = fwrite($fp_dest, $buffer, $byteslefttowrite);
|
643 |
+
$byteslefttowrite -= $byteswritten;
|
644 |
+
}
|
645 |
+
return true;
|
646 |
+
} else {
|
647 |
+
throw new Exception('failed to seek to offset '.$offset.' in '.$filename_source);
|
648 |
+
}
|
649 |
+
fclose($fp_dest);
|
650 |
+
} else {
|
651 |
+
throw new Exception('failed to create file for writing '.$filename_dest);
|
652 |
+
}
|
653 |
+
fclose($fp_src);
|
654 |
+
} else {
|
655 |
+
throw new Exception('failed to open file for reading '.$filename_source);
|
656 |
+
}
|
657 |
+
return false;
|
658 |
+
}
|
659 |
+
|
660 |
+
static function iconv_fallback_int_utf8($charval) {
|
661 |
+
if ($charval < 128) {
|
662 |
+
// 0bbbbbbb
|
663 |
+
$newcharstring = chr($charval);
|
664 |
+
} elseif ($charval < 2048) {
|
665 |
+
// 110bbbbb 10bbbbbb
|
666 |
+
$newcharstring = chr(($charval >> 6) | 0xC0);
|
667 |
+
$newcharstring .= chr(($charval & 0x3F) | 0x80);
|
668 |
+
} elseif ($charval < 65536) {
|
669 |
+
// 1110bbbb 10bbbbbb 10bbbbbb
|
670 |
+
$newcharstring = chr(($charval >> 12) | 0xE0);
|
671 |
+
$newcharstring .= chr(($charval >> 6) | 0xC0);
|
672 |
+
$newcharstring .= chr(($charval & 0x3F) | 0x80);
|
673 |
+
} else {
|
674 |
+
// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
|
675 |
+
$newcharstring = chr(($charval >> 18) | 0xF0);
|
676 |
+
$newcharstring .= chr(($charval >> 12) | 0xC0);
|
677 |
+
$newcharstring .= chr(($charval >> 6) | 0xC0);
|
678 |
+
$newcharstring .= chr(($charval & 0x3F) | 0x80);
|
679 |
+
}
|
680 |
+
return $newcharstring;
|
681 |
+
}
|
682 |
+
|
683 |
+
// ISO-8859-1 => UTF-8
|
684 |
+
static function iconv_fallback_iso88591_utf8($string, $bom=false) {
|
685 |
+
if (function_exists('utf8_encode')) {
|
686 |
+
return utf8_encode($string);
|
687 |
+
}
|
688 |
+
// utf8_encode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
|
689 |
+
$newcharstring = '';
|
690 |
+
if ($bom) {
|
691 |
+
$newcharstring .= "\xEF\xBB\xBF";
|
692 |
+
}
|
693 |
+
for ($i = 0; $i < strlen($string); $i++) {
|
694 |
+
$charval = ord($string{$i});
|
695 |
+
$newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval);
|
696 |
+
}
|
697 |
+
return $newcharstring;
|
698 |
+
}
|
699 |
+
|
700 |
+
// ISO-8859-1 => UTF-16BE
|
701 |
+
static function iconv_fallback_iso88591_utf16be($string, $bom=false) {
|
702 |
+
$newcharstring = '';
|
703 |
+
if ($bom) {
|
704 |
+
$newcharstring .= "\xFE\xFF";
|
705 |
+
}
|
706 |
+
for ($i = 0; $i < strlen($string); $i++) {
|
707 |
+
$newcharstring .= "\x00".$string{$i};
|
708 |
+
}
|
709 |
+
return $newcharstring;
|
710 |
+
}
|
711 |
+
|
712 |
+
// ISO-8859-1 => UTF-16LE
|
713 |
+
static function iconv_fallback_iso88591_utf16le($string, $bom=false) {
|
714 |
+
$newcharstring = '';
|
715 |
+
if ($bom) {
|
716 |
+
$newcharstring .= "\xFF\xFE";
|
717 |
+
}
|
718 |
+
for ($i = 0; $i < strlen($string); $i++) {
|
719 |
+
$newcharstring .= $string{$i}."\x00";
|
720 |
+
}
|
721 |
+
return $newcharstring;
|
722 |
+
}
|
723 |
+
|
724 |
+
// ISO-8859-1 => UTF-16LE (BOM)
|
725 |
+
static function iconv_fallback_iso88591_utf16($string) {
|
726 |
+
return getid3_lib::iconv_fallback_iso88591_utf16le($string, true);
|
727 |
+
}
|
728 |
+
|
729 |
+
// UTF-8 => ISO-8859-1
|
730 |
+
static function iconv_fallback_utf8_iso88591($string) {
|
731 |
+
if (function_exists('utf8_decode')) {
|
732 |
+
return utf8_decode($string);
|
733 |
+
}
|
734 |
+
// utf8_decode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
|
735 |
+
$newcharstring = '';
|
736 |
+
$offset = 0;
|
737 |
+
$stringlength = strlen($string);
|
738 |
+
while ($offset < $stringlength) {
|
739 |
+
if ((ord($string{$offset}) | 0x07) == 0xF7) {
|
740 |
+
// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
|
741 |
+
$charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
|
742 |
+
((ord($string{($offset + 1)}) & 0x3F) << 12) &
|
743 |
+
((ord($string{($offset + 2)}) & 0x3F) << 6) &
|
744 |
+
(ord($string{($offset + 3)}) & 0x3F);
|
745 |
+
$offset += 4;
|
746 |
+
} elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
|
747 |
+
// 1110bbbb 10bbbbbb 10bbbbbb
|
748 |
+
$charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
|
749 |
+
((ord($string{($offset + 1)}) & 0x3F) << 6) &
|
750 |
+
(ord($string{($offset + 2)}) & 0x3F);
|
751 |
+
$offset += 3;
|
752 |
+
} elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
|
753 |
+
// 110bbbbb 10bbbbbb
|
754 |
+
$charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
|
755 |
+
(ord($string{($offset + 1)}) & 0x3F);
|
756 |
+
$offset += 2;
|
757 |
+
} elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
|
758 |
+
// 0bbbbbbb
|
759 |
+
$charval = ord($string{$offset});
|
760 |
+
$offset += 1;
|
761 |
+
} else {
|
762 |
+
// error? throw some kind of warning here?
|
763 |
+
$charval = false;
|
764 |
+
$offset += 1;
|
765 |
+
}
|
766 |
+
if ($charval !== false) {
|
767 |
+
$newcharstring .= (($charval < 256) ? chr($charval) : '?');
|
768 |
+
}
|
769 |
+
}
|
770 |
+
return $newcharstring;
|
771 |
+
}
|
772 |
+
|
773 |
+
// UTF-8 => UTF-16BE
|
774 |
+
static function iconv_fallback_utf8_utf16be($string, $bom=false) {
|
775 |
+
$newcharstring = '';
|
776 |
+
if ($bom) {
|
777 |
+
$newcharstring .= "\xFE\xFF";
|
778 |
+
}
|
779 |
+
$offset = 0;
|
780 |
+
$stringlength = strlen($string);
|
781 |
+
while ($offset < $stringlength) {
|
782 |
+
if ((ord($string{$offset}) | 0x07) == 0xF7) {
|
783 |
+
// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
|
784 |
+
$charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
|
785 |
+
((ord($string{($offset + 1)}) & 0x3F) << 12) &
|
786 |
+
((ord($string{($offset + 2)}) & 0x3F) << 6) &
|
787 |
+
(ord($string{($offset + 3)}) & 0x3F);
|
788 |
+
$offset += 4;
|
789 |
+
} elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
|
790 |
+
// 1110bbbb 10bbbbbb 10bbbbbb
|
791 |
+
$charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
|
792 |
+
((ord($string{($offset + 1)}) & 0x3F) << 6) &
|
793 |
+
(ord($string{($offset + 2)}) & 0x3F);
|
794 |
+
$offset += 3;
|
795 |
+
} elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
|
796 |
+
// 110bbbbb 10bbbbbb
|
797 |
+
$charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
|
798 |
+
(ord($string{($offset + 1)}) & 0x3F);
|
799 |
+
$offset += 2;
|
800 |
+
} elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
|
801 |
+
// 0bbbbbbb
|
802 |
+
$charval = ord($string{$offset});
|
803 |
+
$offset += 1;
|
804 |
+
} else {
|
805 |
+
// error? throw some kind of warning here?
|
806 |
+
$charval = false;
|
807 |
+
$offset += 1;
|
808 |
+
}
|
809 |
+
if ($charval !== false) {
|
810 |
+
$newcharstring .= (($charval < 65536) ? getid3_lib::BigEndian2String($charval, 2) : "\x00".'?');
|
811 |
+
}
|
812 |
+
}
|
813 |
+
return $newcharstring;
|
814 |
+
}
|
815 |
+
|
816 |
+
// UTF-8 => UTF-16LE
|
817 |
+
static function iconv_fallback_utf8_utf16le($string, $bom=false) {
|
818 |
+
$newcharstring = '';
|
819 |
+
if ($bom) {
|
820 |
+
$newcharstring .= "\xFF\xFE";
|
821 |
+
}
|
822 |
+
$offset = 0;
|
823 |
+
$stringlength = strlen($string);
|
824 |
+
while ($offset < $stringlength) {
|
825 |
+
if ((ord($string{$offset}) | 0x07) == 0xF7) {
|
826 |
+
// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
|
827 |
+
$charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
|
828 |
+
((ord($string{($offset + 1)}) & 0x3F) << 12) &
|
829 |
+
((ord($string{($offset + 2)}) & 0x3F) << 6) &
|
830 |
+
(ord($string{($offset + 3)}) & 0x3F);
|
831 |
+
$offset += 4;
|
832 |
+
} elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
|
833 |
+
// 1110bbbb 10bbbbbb 10bbbbbb
|
834 |
+
$charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
|
835 |
+
((ord($string{($offset + 1)}) & 0x3F) << 6) &
|
836 |
+
(ord($string{($offset + 2)}) & 0x3F);
|
837 |
+
$offset += 3;
|
838 |
+
} elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
|
839 |
+
// 110bbbbb 10bbbbbb
|
840 |
+
$charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
|
841 |
+
(ord($string{($offset + 1)}) & 0x3F);
|
842 |
+
$offset += 2;
|
843 |
+
} elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
|
844 |
+
// 0bbbbbbb
|
845 |
+
$charval = ord($string{$offset});
|
846 |
+
$offset += 1;
|
847 |
+
} else {
|
848 |
+
// error? maybe throw some warning here?
|
849 |
+
$charval = false;
|
850 |
+
$offset += 1;
|
851 |
+
}
|
852 |
+
if ($charval !== false) {
|
853 |
+
$newcharstring .= (($charval < 65536) ? getid3_lib::LittleEndian2String($charval, 2) : '?'."\x00");
|
854 |
+
}
|
855 |
+
}
|
856 |
+
return $newcharstring;
|
857 |
+
}
|
858 |
+
|
859 |
+
// UTF-8 => UTF-16LE (BOM)
|
860 |
+
static function iconv_fallback_utf8_utf16($string) {
|
861 |
+
return getid3_lib::iconv_fallback_utf8_utf16le($string, true);
|
862 |
+
}
|
863 |
+
|
864 |
+
// UTF-16BE => UTF-8
|
865 |
+
static function iconv_fallback_utf16be_utf8($string) {
|
866 |
+
if (substr($string, 0, 2) == "\xFE\xFF") {
|
867 |
+
// strip BOM
|
868 |
+
$string = substr($string, 2);
|
869 |
+
}
|
870 |
+
$newcharstring = '';
|
871 |
+
for ($i = 0; $i < strlen($string); $i += 2) {
|
872 |
+
$charval = getid3_lib::BigEndian2Int(substr($string, $i, 2));
|
873 |
+
$newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval);
|
874 |
+
}
|
875 |
+
return $newcharstring;
|
876 |
+
}
|
877 |
+
|
878 |
+
// UTF-16LE => UTF-8
|
879 |
+
static function iconv_fallback_utf16le_utf8($string) {
|
880 |
+
if (substr($string, 0, 2) == "\xFF\xFE") {
|
881 |
+
// strip BOM
|
882 |
+
$string = substr($string, 2);
|
883 |
+
}
|
884 |
+
$newcharstring = '';
|
885 |
+
for ($i = 0; $i < strlen($string); $i += 2) {
|
886 |
+
$charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2));
|
887 |
+
$newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval);
|
888 |
+
}
|
889 |
+
return $newcharstring;
|
890 |
+
}
|
891 |
+
|
892 |
+
// UTF-16BE => ISO-8859-1
|
893 |
+
static function iconv_fallback_utf16be_iso88591($string) {
|
894 |
+
if (substr($string, 0, 2) == "\xFE\xFF") {
|
895 |
+
// strip BOM
|
896 |
+
$string = substr($string, 2);
|
897 |
+
}
|
898 |
+
$newcharstring = '';
|
899 |
+
for ($i = 0; $i < strlen($string); $i += 2) {
|
900 |
+
$charval = getid3_lib::BigEndian2Int(substr($string, $i, 2));
|
901 |
+
$newcharstring .= (($charval < 256) ? chr($charval) : '?');
|
902 |
+
}
|
903 |
+
return $newcharstring;
|
904 |
+
}
|
905 |
+
|
906 |
+
// UTF-16LE => ISO-8859-1
|
907 |
+
static function iconv_fallback_utf16le_iso88591($string) {
|
908 |
+
if (substr($string, 0, 2) == "\xFF\xFE") {
|
909 |
+
// strip BOM
|
910 |
+
$string = substr($string, 2);
|
911 |
+
}
|
912 |
+
$newcharstring = '';
|
913 |
+
for ($i = 0; $i < strlen($string); $i += 2) {
|
914 |
+
$charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2));
|
915 |
+
$newcharstring .= (($charval < 256) ? chr($charval) : '?');
|
916 |
+
}
|
917 |
+
return $newcharstring;
|
918 |
+
}
|
919 |
+
|
920 |
+
// UTF-16 (BOM) => ISO-8859-1
|
921 |
+
static function iconv_fallback_utf16_iso88591($string) {
|
922 |
+
$bom = substr($string, 0, 2);
|
923 |
+
if ($bom == "\xFE\xFF") {
|
924 |
+
return getid3_lib::iconv_fallback_utf16be_iso88591(substr($string, 2));
|
925 |
+
} elseif ($bom == "\xFF\xFE") {
|
926 |
+
return getid3_lib::iconv_fallback_utf16le_iso88591(substr($string, 2));
|
927 |
+
}
|
928 |
+
return $string;
|
929 |
+
}
|
930 |
+
|
931 |
+
// UTF-16 (BOM) => UTF-8
|
932 |
+
static function iconv_fallback_utf16_utf8($string) {
|
933 |
+
$bom = substr($string, 0, 2);
|
934 |
+
if ($bom == "\xFE\xFF") {
|
935 |
+
return getid3_lib::iconv_fallback_utf16be_utf8(substr($string, 2));
|
936 |
+
} elseif ($bom == "\xFF\xFE") {
|
937 |
+
return getid3_lib::iconv_fallback_utf16le_utf8(substr($string, 2));
|
938 |
+
}
|
939 |
+
return $string;
|
940 |
+
}
|
941 |
+
|
942 |
+
static function iconv_fallback($in_charset, $out_charset, $string) {
|
943 |
+
|
944 |
+
if ($in_charset == $out_charset) {
|
945 |
+
return $string;
|
946 |
+
}
|
947 |
+
|
948 |
+
// iconv() availble
|
949 |
+
if (function_exists('iconv')) {
|
950 |
+
if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) {
|
951 |
+
switch ($out_charset) {
|
952 |
+
case 'ISO-8859-1':
|
953 |
+
$converted_string = rtrim($converted_string, "\x00");
|
954 |
+
break;
|
955 |
+
}
|
956 |
+
return $converted_string;
|
957 |
+
}
|
958 |
+
|
959 |
+
// iconv() may sometimes fail with "illegal character in input string" error message
|
960 |
+
// and return an empty string, but returning the unconverted string is more useful
|
961 |
+
return $string;
|
962 |
+
}
|
963 |
+
|
964 |
+
|
965 |
+
// iconv() not available
|
966 |
+
static $ConversionFunctionList = array();
|
967 |
+
if (empty($ConversionFunctionList)) {
|
968 |
+
$ConversionFunctionList['ISO-8859-1']['UTF-8'] = 'iconv_fallback_iso88591_utf8';
|
969 |
+
$ConversionFunctionList['ISO-8859-1']['UTF-16'] = 'iconv_fallback_iso88591_utf16';
|
970 |
+
$ConversionFunctionList['ISO-8859-1']['UTF-16BE'] = 'iconv_fallback_iso88591_utf16be';
|
971 |
+
$ConversionFunctionList['ISO-8859-1']['UTF-16LE'] = 'iconv_fallback_iso88591_utf16le';
|
972 |
+
$ConversionFunctionList['UTF-8']['ISO-8859-1'] = 'iconv_fallback_utf8_iso88591';
|
973 |
+
$ConversionFunctionList['UTF-8']['UTF-16'] = 'iconv_fallback_utf8_utf16';
|
974 |
+
$ConversionFunctionList['UTF-8']['UTF-16BE'] = 'iconv_fallback_utf8_utf16be';
|
975 |
+
$ConversionFunctionList['UTF-8']['UTF-16LE'] = 'iconv_fallback_utf8_utf16le';
|
976 |
+
$ConversionFunctionList['UTF-16']['ISO-8859-1'] = 'iconv_fallback_utf16_iso88591';
|
977 |
+
$ConversionFunctionList['UTF-16']['UTF-8'] = 'iconv_fallback_utf16_utf8';
|
978 |
+
$ConversionFunctionList['UTF-16LE']['ISO-8859-1'] = 'iconv_fallback_utf16le_iso88591';
|
979 |
+
$ConversionFunctionList['UTF-16LE']['UTF-8'] = 'iconv_fallback_utf16le_utf8';
|
980 |
+
$ConversionFunctionList['UTF-16BE']['ISO-8859-1'] = 'iconv_fallback_utf16be_iso88591';
|
981 |
+
$ConversionFunctionList['UTF-16BE']['UTF-8'] = 'iconv_fallback_utf16be_utf8';
|
982 |
+
}
|
983 |
+
if (isset($ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)])) {
|
984 |
+
$ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)];
|
985 |
+
return getid3_lib::$ConversionFunction($string);
|
986 |
+
}
|
987 |
+
throw new Exception('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset);
|
988 |
+
}
|
989 |
+
|
990 |
+
|
991 |
+
static function MultiByteCharString2HTML($string, $charset='ISO-8859-1') {
|
992 |
+
$string = (string) $string; // in case trying to pass a numeric (float, int) string, would otherwise return an empty string
|
993 |
+
$HTMLstring = '';
|
994 |
+
|
995 |
+
switch ($charset) {
|
996 |
+
case '1251':
|
997 |
+
case '1252':
|
998 |
+
case '866':
|
999 |
+
case '932':
|
1000 |
+
case '936':
|
1001 |
+
case '950':
|
1002 |
+
case 'BIG5':
|
1003 |
+
case 'BIG5-HKSCS':
|
1004 |
+
case 'cp1251':
|
1005 |
+
case 'cp1252':
|
1006 |
+
case 'cp866':
|
1007 |
+
case 'EUC-JP':
|
1008 |
+
case 'EUCJP':
|
1009 |
+
case 'GB2312':
|
1010 |
+
case 'ibm866':
|
1011 |
+
case 'ISO-8859-1':
|
1012 |
+
case 'ISO-8859-15':
|
1013 |
+
case 'ISO8859-1':
|
1014 |
+
case 'ISO8859-15':
|
1015 |
+
case 'KOI8-R':
|
1016 |
+
case 'koi8-ru':
|
1017 |
+
case 'koi8r':
|
1018 |
+
case 'Shift_JIS':
|
1019 |
+
case 'SJIS':
|
1020 |
+
case 'win-1251':
|
1021 |
+
case 'Windows-1251':
|
1022 |
+
case 'Windows-1252':
|
1023 |
+
$HTMLstring = htmlentities($string, ENT_COMPAT, $charset);
|
1024 |
+
break;
|
1025 |
+
|
1026 |
+
case 'UTF-8':
|
1027 |
+
$strlen = strlen($string);
|
1028 |
+
for ($i = 0; $i < $strlen; $i++) {
|
1029 |
+
$char_ord_val = ord($string{$i});
|
1030 |
+
$charval = 0;
|
1031 |
+
if ($char_ord_val < 0x80) {
|
1032 |
+
$charval = $char_ord_val;
|
1033 |
+
} elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F && $i+3 < $strlen) {
|
1034 |
+
$charval = (($char_ord_val & 0x07) << 18);
|
1035 |
+
$charval += ((ord($string{++$i}) & 0x3F) << 12);
|
1036 |
+
$charval += ((ord($string{++$i}) & 0x3F) << 6);
|
1037 |
+
$charval += (ord($string{++$i}) & 0x3F);
|
1038 |
+
} elseif ((($char_ord_val & 0xE0) >> 5) == 0x07 && $i+2 < $strlen) {
|
1039 |
+
$charval = (($char_ord_val & 0x0F) << 12);
|
1040 |
+
$charval += ((ord($string{++$i}) & 0x3F) << 6);
|
1041 |
+
$charval += (ord($string{++$i}) & 0x3F);
|
1042 |
+
} elseif ((($char_ord_val & 0xC0) >> 6) == 0x03 && $i+1 < $strlen) {
|
1043 |
+
$charval = (($char_ord_val & 0x1F) << 6);
|
1044 |
+
$charval += (ord($string{++$i}) & 0x3F);
|
1045 |
+
}
|
1046 |
+
if (($charval >= 32) && ($charval <= 127)) {
|
1047 |
+
$HTMLstring .= htmlentities(chr($charval));
|
1048 |
+
} else {
|
1049 |
+
$HTMLstring .= '&#'.$charval.';';
|
1050 |
+
}
|
1051 |
+
}
|
1052 |
+
break;
|
1053 |
+
|
1054 |
+
case 'UTF-16LE':
|
1055 |
+
for ($i = 0; $i < strlen($string); $i += 2) {
|
1056 |
+
$charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2));
|
1057 |
+
if (($charval >= 32) && ($charval <= 127)) {
|
1058 |
+
$HTMLstring .= chr($charval);
|
1059 |
+
} else {
|
1060 |
+
$HTMLstring .= '&#'.$charval.';';
|
1061 |
+
}
|
1062 |
+
}
|
1063 |
+
break;
|
1064 |
+
|
1065 |
+
case 'UTF-16BE':
|
1066 |
+
for ($i = 0; $i < strlen($string); $i += 2) {
|
1067 |
+
$charval = getid3_lib::BigEndian2Int(substr($string, $i, 2));
|
1068 |
+
if (($charval >= 32) && ($charval <= 127)) {
|
1069 |
+
$HTMLstring .= chr($charval);
|
1070 |
+
} else {
|
1071 |
+
$HTMLstring .= '&#'.$charval.';';
|
1072 |
+
}
|
1073 |
+
}
|
1074 |
+
break;
|
1075 |
+
|
1076 |
+
default:
|
1077 |
+
$HTMLstring = 'ERROR: Character set "'.$charset.'" not supported in MultiByteCharString2HTML()';
|
1078 |
+
break;
|
1079 |
+
}
|
1080 |
+
return $HTMLstring;
|
1081 |
+
}
|
1082 |
+
|
1083 |
+
|
1084 |
+
|
1085 |
+
static function RGADnameLookup($namecode) {
|
1086 |
+
static $RGADname = array();
|
1087 |
+
if (empty($RGADname)) {
|
1088 |
+
$RGADname[0] = 'not set';
|
1089 |
+
$RGADname[1] = 'Track Gain Adjustment';
|
1090 |
+
$RGADname[2] = 'Album Gain Adjustment';
|
1091 |
+
}
|
1092 |
+
|
1093 |
+
return (isset($RGADname[$namecode]) ? $RGADname[$namecode] : '');
|
1094 |
+
}
|
1095 |
+
|
1096 |
+
|
1097 |
+
static function RGADoriginatorLookup($originatorcode) {
|
1098 |
+
static $RGADoriginator = array();
|
1099 |
+
if (empty($RGADoriginator)) {
|
1100 |
+
$RGADoriginator[0] = 'unspecified';
|
1101 |
+
$RGADoriginator[1] = 'pre-set by artist/producer/mastering engineer';
|
1102 |
+
$RGADoriginator[2] = 'set by user';
|
1103 |
+
$RGADoriginator[3] = 'determined automatically';
|
1104 |
+
}
|
1105 |
+
|
1106 |
+
return (isset($RGADoriginator[$originatorcode]) ? $RGADoriginator[$originatorcode] : '');
|
1107 |
+
}
|
1108 |
+
|
1109 |
+
|
1110 |
+
static function RGADadjustmentLookup($rawadjustment, $signbit) {
|
1111 |
+
$adjustment = $rawadjustment / 10;
|
1112 |
+
if ($signbit == 1) {
|
1113 |
+
$adjustment *= -1;
|
1114 |
+
}
|
1115 |
+
return (float) $adjustment;
|
1116 |
+
}
|
1117 |
+
|
1118 |
+
|
1119 |
+
static function RGADgainString($namecode, $originatorcode, $replaygain) {
|
1120 |
+
if ($replaygain < 0) {
|
1121 |
+
$signbit = '1';
|
1122 |
+
} else {
|
1123 |
+
$signbit = '0';
|
1124 |
+
}
|
1125 |
+
$storedreplaygain = intval(round($replaygain * 10));
|
1126 |
+
$gainstring = str_pad(decbin($namecode), 3, '0', STR_PAD_LEFT);
|
1127 |
+
$gainstring .= str_pad(decbin($originatorcode), 3, '0', STR_PAD_LEFT);
|
1128 |
+
$gainstring .= $signbit;
|
1129 |
+
$gainstring .= str_pad(decbin($storedreplaygain), 9, '0', STR_PAD_LEFT);
|
1130 |
+
|
1131 |
+
return $gainstring;
|
1132 |
+
}
|
1133 |
+
|
1134 |
+
static function RGADamplitude2dB($amplitude) {
|
1135 |
+
return 20 * log10($amplitude);
|
1136 |
+
}
|
1137 |
+
|
1138 |
+
|
1139 |
+
static function GetDataImageSize($imgData, &$imageinfo) {
|
1140 |
+
static $tempdir = '';
|
1141 |
+
if (empty($tempdir)) {
|
1142 |
+
// yes this is ugly, feel free to suggest a better way
|
1143 |
+
require_once(dirname(__FILE__).'/getid3.php');
|
1144 |
+
$getid3_temp = new getID3();
|
1145 |
+
$tempdir = $getid3_temp->tempdir;
|
1146 |
+
unset($getid3_temp);
|
1147 |
+
}
|
1148 |
+
$GetDataImageSize = false;
|
1149 |
+
if ($tempfilename = tempnam($tempdir, 'gI3')) {
|
1150 |
+
if (is_writable($tempfilename) && is_file($tempfilename) && ($tmp = fopen($tempfilename, 'wb'))) {
|
1151 |
+
fwrite($tmp, $imgData);
|
1152 |
+
fclose($tmp);
|
1153 |
+
$GetDataImageSize = @GetImageSize($tempfilename, $imageinfo);
|
1154 |
+
}
|
1155 |
+
unlink($tempfilename);
|
1156 |
+
}
|
1157 |
+
return $GetDataImageSize;
|
1158 |
+
}
|
1159 |
+
|
1160 |
+
static function ImageTypesLookup($imagetypeid) {
|
1161 |
+
static $ImageTypesLookup = array();
|
1162 |
+
if (empty($ImageTypesLookup)) {
|
1163 |
+
$ImageTypesLookup[1] = 'gif';
|
1164 |
+
$ImageTypesLookup[2] = 'jpeg';
|
1165 |
+
$ImageTypesLookup[3] = 'png';
|
1166 |
+
$ImageTypesLookup[4] = 'swf';
|
1167 |
+
$ImageTypesLookup[5] = 'psd';
|
1168 |
+
$ImageTypesLookup[6] = 'bmp';
|
1169 |
+
$ImageTypesLookup[7] = 'tiff (little-endian)';
|
1170 |
+
$ImageTypesLookup[8] = 'tiff (big-endian)';
|
1171 |
+
$ImageTypesLookup[9] = 'jpc';
|
1172 |
+
$ImageTypesLookup[10] = 'jp2';
|
1173 |
+
$ImageTypesLookup[11] = 'jpx';
|
1174 |
+
$ImageTypesLookup[12] = 'jb2';
|
1175 |
+
$ImageTypesLookup[13] = 'swc';
|
1176 |
+
$ImageTypesLookup[14] = 'iff';
|
1177 |
+
}
|
1178 |
+
return (isset($ImageTypesLookup[$imagetypeid]) ? $ImageTypesLookup[$imagetypeid] : '');
|
1179 |
+
}
|
1180 |
+
|
1181 |
+
static function CopyTagsToComments(&$ThisFileInfo) {
|
1182 |
+
|
1183 |
+
// Copy all entries from ['tags'] into common ['comments']
|
1184 |
+
if (!empty($ThisFileInfo['tags'])) {
|
1185 |
+
foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) {
|
1186 |
+
foreach ($tagarray as $tagname => $tagdata) {
|
1187 |
+
foreach ($tagdata as $key => $value) {
|
1188 |
+
if (!empty($value)) {
|
1189 |
+
if (empty($ThisFileInfo['comments'][$tagname])) {
|
1190 |
+
|
1191 |
+
// fall through and append value
|
1192 |
+
|
1193 |
+
} elseif ($tagtype == 'id3v1') {
|
1194 |
+
|
1195 |
+
$newvaluelength = strlen(trim($value));
|
1196 |
+
foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
|
1197 |
+
$oldvaluelength = strlen(trim($existingvalue));
|
1198 |
+
if (($newvaluelength <= $oldvaluelength) && (substr($existingvalue, 0, $newvaluelength) == trim($value))) {
|
1199 |
+
// new value is identical but shorter-than (or equal-length to) one already in comments - skip
|
1200 |
+
break 2;
|
1201 |
+
}
|
1202 |
+
}
|
1203 |
+
|
1204 |
+
} elseif (!is_array($value)) {
|
1205 |
+
|
1206 |
+
$newvaluelength = strlen(trim($value));
|
1207 |
+
foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
|
1208 |
+
$oldvaluelength = strlen(trim($existingvalue));
|
1209 |
+
if (($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) {
|
1210 |
+
$ThisFileInfo['comments'][$tagname][$existingkey] = trim($value);
|
1211 |
+
break 2;
|
1212 |
+
}
|
1213 |
+
}
|
1214 |
+
|
1215 |
+
}
|
1216 |
+
if (is_array($value) || empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) {
|
1217 |
+
$value = (is_string($value) ? trim($value) : $value);
|
1218 |
+
$ThisFileInfo['comments'][$tagname][] = $value;
|
1219 |
+
}
|
1220 |
+
}
|
1221 |
+
}
|
1222 |
+
}
|
1223 |
+
}
|
1224 |
+
|
1225 |
+
// Copy to ['comments_html']
|
1226 |
+
foreach ($ThisFileInfo['comments'] as $field => $values) {
|
1227 |
+
if ($field == 'picture') {
|
1228 |
+
// pictures can take up a lot of space, and we don't need multiple copies of them
|
1229 |
+
// let there be a single copy in [comments][picture], and not elsewhere
|
1230 |
+
continue;
|
1231 |
+
}
|
1232 |
+
foreach ($values as $index => $value) {
|
1233 |
+
if (is_array($value)) {
|
1234 |
+
$ThisFileInfo['comments_html'][$field][$index] = $value;
|
1235 |
+
} else {
|
1236 |
+
$ThisFileInfo['comments_html'][$field][$index] = str_replace('�', '', getid3_lib::MultiByteCharString2HTML($value, $ThisFileInfo['encoding']));
|
1237 |
+
}
|
1238 |
+
}
|
1239 |
+
}
|
1240 |
+
}
|
1241 |
+
return true;
|
1242 |
+
}
|
1243 |
+
|
1244 |
+
|
1245 |
+
static function EmbeddedLookup($key, $begin, $end, $file, $name) {
|
1246 |
+
|
1247 |
+
// Cached
|
1248 |
+
static $cache;
|
1249 |
+
if (isset($cache[$file][$name])) {
|
1250 |
+
return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : '');
|
1251 |
+
}
|
1252 |
+
|
1253 |
+
// Init
|
1254 |
+
$keylength = strlen($key);
|
1255 |
+
$line_count = $end - $begin - 7;
|
1256 |
+
|
1257 |
+
// Open php file
|
1258 |
+
$fp = fopen($file, 'r');
|
1259 |
+
|
1260 |
+
// Discard $begin lines
|
1261 |
+
for ($i = 0; $i < ($begin + 3); $i++) {
|
1262 |
+
fgets($fp, 1024);
|
1263 |
+
}
|
1264 |
+
|
1265 |
+
// Loop thru line
|
1266 |
+
while (0 < $line_count--) {
|
1267 |
+
|
1268 |
+
// Read line
|
1269 |
+
$line = ltrim(fgets($fp, 1024), "\t ");
|
1270 |
+
|
1271 |
+
// METHOD A: only cache the matching key - less memory but slower on next lookup of not-previously-looked-up key
|
1272 |
+
//$keycheck = substr($line, 0, $keylength);
|
1273 |
+
//if ($key == $keycheck) {
|
1274 |
+
// $cache[$file][$name][$keycheck] = substr($line, $keylength + 1);
|
1275 |
+
// break;
|
1276 |
+
//}
|
1277 |
+
|
1278 |
+
// METHOD B: cache all keys in this lookup - more memory but faster on next lookup of not-previously-looked-up key
|
1279 |
+
//$cache[$file][$name][substr($line, 0, $keylength)] = trim(substr($line, $keylength + 1));
|
1280 |
+
$explodedLine = explode("\t", $line, 2);
|
1281 |
+
$ThisKey = (isset($explodedLine[0]) ? $explodedLine[0] : '');
|
1282 |
+
$ThisValue = (isset($explodedLine[1]) ? $explodedLine[1] : '');
|
1283 |
+
$cache[$file][$name][$ThisKey] = trim($ThisValue);
|
1284 |
+
}
|
1285 |
+
|
1286 |
+
// Close and return
|
1287 |
+
fclose($fp);
|
1288 |
+
return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : '');
|
1289 |
+
}
|
1290 |
+
|
1291 |
+
static function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) {
|
1292 |
+
global $GETID3_ERRORARRAY;
|
1293 |
+
|
1294 |
+
if (file_exists($filename)) {
|
1295 |
+
if (include_once($filename)) {
|
1296 |
+
return true;
|
1297 |
+
} else {
|
1298 |
+
$diemessage = basename($sourcefile).' depends on '.$filename.', which has errors';
|
1299 |
+
}
|
1300 |
+
} else {
|
1301 |
+
$diemessage = basename($sourcefile).' depends on '.$filename.', which is missing';
|
1302 |
+
}
|
1303 |
+
if ($DieOnFailure) {
|
1304 |
+
throw new Exception($diemessage);
|
1305 |
+
} else {
|
1306 |
+
$GETID3_ERRORARRAY[] = $diemessage;
|
1307 |
+
}
|
1308 |
+
return false;
|
1309 |
+
}
|
1310 |
+
|
1311 |
+
public static function trimNullByte($string) {
|
1312 |
+
return trim($string, "\x00");
|
1313 |
+
}
|
1314 |
+
|
1315 |
+
}
|
1316 |
+
|
1317 |
+
?>
|
includes/lib/getid3/getid3.php
ADDED
@@ -0,0 +1,1744 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
/////////////////////////////////////////////////////////////////
|
7 |
+
// //
|
8 |
+
// Please see readme.txt for more information //
|
9 |
+
// ///
|
10 |
+
/////////////////////////////////////////////////////////////////
|
11 |
+
|
12 |
+
// attempt to define temp dir as something flexible but reliable
|
13 |
+
$temp_dir = ini_get('upload_tmp_dir');
|
14 |
+
if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) {
|
15 |
+
$temp_dir = '';
|
16 |
+
}
|
17 |
+
if (!$temp_dir && function_exists('sys_get_temp_dir')) {
|
18 |
+
// PHP v5.2.1+
|
19 |
+
// sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts
|
20 |
+
$temp_dir = sys_get_temp_dir();
|
21 |
+
}
|
22 |
+
$temp_dir = realpath($temp_dir);
|
23 |
+
$open_basedir = ini_get('open_basedir');
|
24 |
+
if ($open_basedir) {
|
25 |
+
// e.g. "/var/www/vhosts/getid3.org/httpdocs/:/tmp/"
|
26 |
+
$temp_dir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $temp_dir);
|
27 |
+
$open_basedir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $open_basedir);
|
28 |
+
if (substr($temp_dir, -1, 1) != DIRECTORY_SEPARATOR) {
|
29 |
+
$temp_dir .= DIRECTORY_SEPARATOR;
|
30 |
+
}
|
31 |
+
$found_valid_tempdir = false;
|
32 |
+
$open_basedirs = explode(':', $open_basedir);
|
33 |
+
foreach ($open_basedirs as $basedir) {
|
34 |
+
if (substr($basedir, -1, 1) != DIRECTORY_SEPARATOR) {
|
35 |
+
$basedir .= DIRECTORY_SEPARATOR;
|
36 |
+
}
|
37 |
+
if (preg_match('#^'.preg_quote($basedir).'#', $temp_dir)) {
|
38 |
+
$found_valid_tempdir = true;
|
39 |
+
break;
|
40 |
+
}
|
41 |
+
}
|
42 |
+
if (!$found_valid_tempdir) {
|
43 |
+
$temp_dir = '';
|
44 |
+
}
|
45 |
+
unset($open_basedirs, $found_valid_tempdir, $basedir);
|
46 |
+
}
|
47 |
+
if (!$temp_dir) {
|
48 |
+
$temp_dir = '*'; // invalid directory name should force tempnam() to use system default temp dir
|
49 |
+
}
|
50 |
+
// $temp_dir = '/something/else/'; // feel free to override temp dir here if it works better for your system
|
51 |
+
define('GETID3_TEMP_DIR', $temp_dir);
|
52 |
+
unset($open_basedir, $temp_dir);
|
53 |
+
|
54 |
+
|
55 |
+
// define a constant rather than looking up every time it is needed
|
56 |
+
if (!defined('GETID3_OS_ISWINDOWS')) {
|
57 |
+
if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
|
58 |
+
define('GETID3_OS_ISWINDOWS', true);
|
59 |
+
} else {
|
60 |
+
define('GETID3_OS_ISWINDOWS', false);
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
// Get base path of getID3() - ONCE
|
65 |
+
if (!defined('GETID3_INCLUDEPATH')) {
|
66 |
+
foreach (get_included_files() as $key => $val) {
|
67 |
+
if (basename($val) == 'getid3.php') {
|
68 |
+
define('GETID3_INCLUDEPATH', dirname($val).DIRECTORY_SEPARATOR);
|
69 |
+
break;
|
70 |
+
}
|
71 |
+
}
|
72 |
+
}
|
73 |
+
|
74 |
+
// End: Defines
|
75 |
+
|
76 |
+
|
77 |
+
class getID3
|
78 |
+
{
|
79 |
+
// public: Settings
|
80 |
+
public $encoding = 'UTF-8'; // CASE SENSITIVE! - i.e. (must be supported by iconv()). Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE
|
81 |
+
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'
|
82 |
+
|
83 |
+
// public: Optional tag checks - disable for speed.
|
84 |
+
public $option_tag_id3v1 = true; // Read and process ID3v1 tags
|
85 |
+
public $option_tag_id3v2 = true; // Read and process ID3v2 tags
|
86 |
+
public $option_tag_lyrics3 = true; // Read and process Lyrics3 tags
|
87 |
+
public $option_tag_apetag = true; // Read and process APE tags
|
88 |
+
public $option_tags_process = true; // Copy tags to root key 'tags' and encode to $this->encoding
|
89 |
+
public $option_tags_html = true; // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
|
90 |
+
|
91 |
+
// public: Optional tag/comment calucations
|
92 |
+
public $option_extra_info = true; // Calculate additional info such as bitrate, channelmode etc
|
93 |
+
|
94 |
+
// public: Optional handling of embedded attachments (e.g. images)
|
95 |
+
public $option_save_attachments = true; // defaults to true (ATTACHMENTS_INLINE) for backward compatibility
|
96 |
+
|
97 |
+
// public: Optional calculations
|
98 |
+
public $option_md5_data = false; // Get MD5 sum of data part - slow
|
99 |
+
public $option_md5_data_source = false; // Use MD5 of source file if availble - only FLAC and OptimFROG
|
100 |
+
public $option_sha1_data = false; // Get SHA1 sum of data part - slow
|
101 |
+
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)
|
102 |
+
|
103 |
+
// public: Read buffer size in bytes
|
104 |
+
public $option_fread_buffer_size = 32768;
|
105 |
+
|
106 |
+
// Public variables
|
107 |
+
public $filename; // Filename of file being analysed.
|
108 |
+
public $fp; // Filepointer to file being analysed.
|
109 |
+
public $info; // Result array.
|
110 |
+
|
111 |
+
// Protected variables
|
112 |
+
protected $startup_error = '';
|
113 |
+
protected $startup_warning = '';
|
114 |
+
protected $memory_limit = 0;
|
115 |
+
|
116 |
+
const VERSION = '1.9.3-20111213';
|
117 |
+
const FREAD_BUFFER_SIZE = 32768;
|
118 |
+
var $tempdir = GETID3_TEMP_DIR;
|
119 |
+
|
120 |
+
const ATTACHMENTS_NONE = false;
|
121 |
+
const ATTACHMENTS_INLINE = true;
|
122 |
+
|
123 |
+
// public: constructor
|
124 |
+
public function __construct() {
|
125 |
+
|
126 |
+
// Check for PHP version
|
127 |
+
$required_php_version = '5.0.5';
|
128 |
+
if (version_compare(PHP_VERSION, $required_php_version, '<')) {
|
129 |
+
$this->startup_error .= 'getID3() requires PHP v'.$required_php_version.' or higher - you are running v'.PHP_VERSION;
|
130 |
+
return false;
|
131 |
+
}
|
132 |
+
|
133 |
+
// Check memory
|
134 |
+
$this->memory_limit = ini_get('memory_limit');
|
135 |
+
if (preg_match('#([0-9]+)M#i', $this->memory_limit, $matches)) {
|
136 |
+
// could be stored as "16M" rather than 16777216 for example
|
137 |
+
$this->memory_limit = $matches[1] * 1048576;
|
138 |
+
} elseif (preg_match('#([0-9]+)G#i', $this->memory_limit, $matches)) { // The 'G' modifier is available since PHP 5.1.0
|
139 |
+
// could be stored as "2G" rather than 2147483648 for example
|
140 |
+
$this->memory_limit = $matches[1] * 1073741824;
|
141 |
+
}
|
142 |
+
if ($this->memory_limit <= 0) {
|
143 |
+
// memory limits probably disabled
|
144 |
+
} elseif ($this->memory_limit <= 4194304) {
|
145 |
+
$this->startup_error .= 'PHP has less than 4MB available memory and will very likely run out. Increase memory_limit in php.ini';
|
146 |
+
} elseif ($this->memory_limit <= 12582912) {
|
147 |
+
$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';
|
148 |
+
}
|
149 |
+
|
150 |
+
// Check safe_mode off
|
151 |
+
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
|
152 |
+
$this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.');
|
153 |
+
}
|
154 |
+
|
155 |
+
if (intval(ini_get('mbstring.func_overload')) > 0) {
|
156 |
+
$this->warning('WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", this may break things.');
|
157 |
+
}
|
158 |
+
|
159 |
+
// Check for magic_quotes_runtime
|
160 |
+
if (function_exists('get_magic_quotes_runtime')) {
|
161 |
+
if (get_magic_quotes_runtime()) {
|
162 |
+
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).');
|
163 |
+
}
|
164 |
+
}
|
165 |
+
|
166 |
+
// Check for magic_quotes_gpc
|
167 |
+
if (function_exists('magic_quotes_gpc')) {
|
168 |
+
if (get_magic_quotes_gpc()) {
|
169 |
+
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).');
|
170 |
+
}
|
171 |
+
}
|
172 |
+
|
173 |
+
// Load support library
|
174 |
+
if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
|
175 |
+
$this->startup_error .= 'getid3.lib.php is missing or corrupt';
|
176 |
+
}
|
177 |
+
|
178 |
+
if ($this->option_max_2gb_check === null) {
|
179 |
+
$this->option_max_2gb_check = (PHP_INT_MAX <= 2147483647);
|
180 |
+
}
|
181 |
+
|
182 |
+
|
183 |
+
// Needed for Windows only:
|
184 |
+
// Define locations of helper applications for Shorten, VorbisComment, MetaFLAC
|
185 |
+
// as well as other helper functions such as head, tail, md5sum, etc
|
186 |
+
// This path cannot contain spaces, but the below code will attempt to get the
|
187 |
+
// 8.3-equivalent path automatically
|
188 |
+
// IMPORTANT: This path must include the trailing slash
|
189 |
+
if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) {
|
190 |
+
|
191 |
+
$helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path
|
192 |
+
|
193 |
+
if (!is_dir($helperappsdir)) {
|
194 |
+
$this->startup_warning .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist';
|
195 |
+
} elseif (strpos(realpath($helperappsdir), ' ') !== false) {
|
196 |
+
$DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir));
|
197 |
+
$path_so_far = array();
|
198 |
+
foreach ($DirPieces as $key => $value) {
|
199 |
+
if (strpos($value, ' ') !== false) {
|
200 |
+
if (!empty($path_so_far)) {
|
201 |
+
$commandline = 'dir /x '.escapeshellarg(implode(DIRECTORY_SEPARATOR, $path_so_far));
|
202 |
+
$dir_listing = `$commandline`;
|
203 |
+
$lines = explode("\n", $dir_listing);
|
204 |
+
foreach ($lines as $line) {
|
205 |
+
$line = trim($line);
|
206 |
+
if (preg_match('#^([0-9/]{10}) +([0-9:]{4,5}( [AP]M)?) +(<DIR>|[0-9,]+) +([^ ]{0,11}) +(.+)$#', $line, $matches)) {
|
207 |
+
list($dummy, $date, $time, $ampm, $filesize, $shortname, $filename) = $matches;
|
208 |
+
if ((strtoupper($filesize) == '<DIR>') && (strtolower($filename) == strtolower($value))) {
|
209 |
+
$value = $shortname;
|
210 |
+
}
|
211 |
+
}
|
212 |
+
}
|
213 |
+
} else {
|
214 |
+
$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.';
|
215 |
+
}
|
216 |
+
}
|
217 |
+
$path_so_far[] = $value;
|
218 |
+
}
|
219 |
+
$helperappsdir = implode(DIRECTORY_SEPARATOR, $path_so_far);
|
220 |
+
}
|
221 |
+
define('GETID3_HELPERAPPSDIR', $helperappsdir.DIRECTORY_SEPARATOR);
|
222 |
+
}
|
223 |
+
|
224 |
+
return true;
|
225 |
+
}
|
226 |
+
|
227 |
+
public function version() {
|
228 |
+
return self::VERSION;
|
229 |
+
}
|
230 |
+
|
231 |
+
public function fread_buffer_size() {
|
232 |
+
return $this->option_fread_buffer_size;
|
233 |
+
}
|
234 |
+
|
235 |
+
|
236 |
+
// public: setOption
|
237 |
+
function setOption($optArray) {
|
238 |
+
if (!is_array($optArray) || empty($optArray)) {
|
239 |
+
return false;
|
240 |
+
}
|
241 |
+
foreach ($optArray as $opt => $val) {
|
242 |
+
if (isset($this->$opt) === false) {
|
243 |
+
continue;
|
244 |
+
}
|
245 |
+
$this->$opt = $val;
|
246 |
+
}
|
247 |
+
return true;
|
248 |
+
}
|
249 |
+
|
250 |
+
|
251 |
+
public function openfile($filename) {
|
252 |
+
try {
|
253 |
+
if (!empty($this->startup_error)) {
|
254 |
+
throw new getid3_exception($this->startup_error);
|
255 |
+
}
|
256 |
+
if (!empty($this->startup_warning)) {
|
257 |
+
$this->warning($this->startup_warning);
|
258 |
+
}
|
259 |
+
|
260 |
+
// init result array and set parameters
|
261 |
+
$this->filename = $filename;
|
262 |
+
$this->info = array();
|
263 |
+
$this->info['GETID3_VERSION'] = $this->version();
|
264 |
+
$this->info['php_memory_limit'] = $this->memory_limit;
|
265 |
+
|
266 |
+
// remote files not supported
|
267 |
+
if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
|
268 |
+
throw new getid3_exception('Remote files are not supported - please copy the file locally first');
|
269 |
+
}
|
270 |
+
|
271 |
+
$filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
|
272 |
+
$filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename);
|
273 |
+
|
274 |
+
// open local file
|
275 |
+
if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
|
276 |
+
// great
|
277 |
+
} else {
|
278 |
+
throw new getid3_exception('Could not open "'.$filename.'" (does not exist, or is not a file)');
|
279 |
+
}
|
280 |
+
|
281 |
+
$this->info['filesize'] = filesize($filename);
|
282 |
+
// set redundant parameters - might be needed in some include file
|
283 |
+
$this->info['filename'] = basename($filename);
|
284 |
+
$this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename)));
|
285 |
+
$this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename'];
|
286 |
+
|
287 |
+
|
288 |
+
// option_max_2gb_check
|
289 |
+
if ($this->option_max_2gb_check) {
|
290 |
+
// PHP (32-bit all, and 64-bit Windows) doesn't support integers larger than 2^31 (~2GB)
|
291 |
+
// filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize
|
292 |
+
// ftell() returns 0 if seeking to the end is beyond the range of unsigned integer
|
293 |
+
$fseek = fseek($this->fp, 0, SEEK_END);
|
294 |
+
if (($fseek < 0) || (($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) ||
|
295 |
+
($this->info['filesize'] < 0) ||
|
296 |
+
(ftell($this->fp) < 0)) {
|
297 |
+
$real_filesize = false;
|
298 |
+
if (GETID3_OS_ISWINDOWS) {
|
299 |
+
$commandline = 'dir /-C "'.str_replace('/', DIRECTORY_SEPARATOR, $filename).'"';
|
300 |
+
$dir_output = `$commandline`;
|
301 |
+
if (preg_match('#1 File\(s\)[ ]+([0-9]+) bytes#i', $dir_output, $matches)) {
|
302 |
+
$real_filesize = (float) $matches[1];
|
303 |
+
}
|
304 |
+
} else {
|
305 |
+
$commandline = 'ls -o -g -G --time-style=long-iso '.escapeshellarg($filename);
|
306 |
+
$dir_output = `$commandline`;
|
307 |
+
if (preg_match('#([0-9]+) ([0-9]{4}-[0-9]{2}\-[0-9]{2} [0-9]{2}:[0-9]{2}) '.str_replace('#', '\\#', preg_quote($filename)).'$#', $dir_output, $matches)) {
|
308 |
+
$real_filesize = (float) $matches[1];
|
309 |
+
}
|
310 |
+
}
|
311 |
+
if ($real_filesize === false) {
|
312 |
+
unset($this->info['filesize']);
|
313 |
+
fclose($this->fp);
|
314 |
+
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.');
|
315 |
+
} elseif (getid3_lib::intValueSupported($real_filesize)) {
|
316 |
+
unset($this->info['filesize']);
|
317 |
+
fclose($this->fp);
|
318 |
+
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');
|
319 |
+
}
|
320 |
+
$this->info['filesize'] = $real_filesize;
|
321 |
+
$this->error('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.');
|
322 |
+
}
|
323 |
+
}
|
324 |
+
|
325 |
+
// set more parameters
|
326 |
+
$this->info['avdataoffset'] = 0;
|
327 |
+
$this->info['avdataend'] = $this->info['filesize'];
|
328 |
+
$this->info['fileformat'] = ''; // filled in later
|
329 |
+
$this->info['audio']['dataformat'] = ''; // filled in later, unset if not used
|
330 |
+
$this->info['video']['dataformat'] = ''; // filled in later, unset if not used
|
331 |
+
$this->info['tags'] = array(); // filled in later, unset if not used
|
332 |
+
$this->info['error'] = array(); // filled in later, unset if not used
|
333 |
+
$this->info['warning'] = array(); // filled in later, unset if not used
|
334 |
+
$this->info['comments'] = array(); // filled in later, unset if not used
|
335 |
+
$this->info['encoding'] = $this->encoding; // required by id3v2 and iso modules - can be unset at the end if desired
|
336 |
+
|
337 |
+
return true;
|
338 |
+
|
339 |
+
} catch (Exception $e) {
|
340 |
+
$this->error($e->getMessage());
|
341 |
+
}
|
342 |
+
return false;
|
343 |
+
}
|
344 |
+
|
345 |
+
// public: analyze file
|
346 |
+
function analyze($filename) {
|
347 |
+
try {
|
348 |
+
if (!$this->openfile($filename)) {
|
349 |
+
return $this->info;
|
350 |
+
}
|
351 |
+
|
352 |
+
// Handle tags
|
353 |
+
foreach (array('id3v2'=>'id3v2', 'id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) {
|
354 |
+
$option_tag = 'option_tag_'.$tag_name;
|
355 |
+
if ($this->$option_tag) {
|
356 |
+
$this->include_module('tag.'.$tag_name);
|
357 |
+
try {
|
358 |
+
$tag_class = 'getid3_'.$tag_name;
|
359 |
+
$tag = new $tag_class($this);
|
360 |
+
$tag->Analyze();
|
361 |
+
}
|
362 |
+
catch (getid3_exception $e) {
|
363 |
+
throw $e;
|
364 |
+
}
|
365 |
+
}
|
366 |
+
}
|
367 |
+
if (isset($this->info['id3v2']['tag_offset_start'])) {
|
368 |
+
$this->info['avdataoffset'] = max($this->info['avdataoffset'], $this->info['id3v2']['tag_offset_end']);
|
369 |
+
}
|
370 |
+
foreach (array('id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) {
|
371 |
+
if (isset($this->info[$tag_key]['tag_offset_start'])) {
|
372 |
+
$this->info['avdataend'] = min($this->info['avdataend'], $this->info[$tag_key]['tag_offset_start']);
|
373 |
+
}
|
374 |
+
}
|
375 |
+
|
376 |
+
// ID3v2 detection (NOT parsing), even if ($this->option_tag_id3v2 == false) done to make fileformat easier
|
377 |
+
if (!$this->option_tag_id3v2) {
|
378 |
+
fseek($this->fp, 0, SEEK_SET);
|
379 |
+
$header = fread($this->fp, 10);
|
380 |
+
if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) {
|
381 |
+
$this->info['id3v2']['header'] = true;
|
382 |
+
$this->info['id3v2']['majorversion'] = ord($header{3});
|
383 |
+
$this->info['id3v2']['minorversion'] = ord($header{4});
|
384 |
+
$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
|
385 |
+
}
|
386 |
+
}
|
387 |
+
|
388 |
+
// read 32 kb file data
|
389 |
+
fseek($this->fp, $this->info['avdataoffset'], SEEK_SET);
|
390 |
+
$formattest = fread($this->fp, 32774);
|
391 |
+
|
392 |
+
// determine format
|
393 |
+
$determined_format = $this->GetFileFormat($formattest, $filename);
|
394 |
+
|
395 |
+
// unable to determine file format
|
396 |
+
if (!$determined_format) {
|
397 |
+
fclose($this->fp);
|
398 |
+
return $this->error('unable to determine file format');
|
399 |
+
}
|
400 |
+
|
401 |
+
// check for illegal ID3 tags
|
402 |
+
if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
|
403 |
+
if ($determined_format['fail_id3'] === 'ERROR') {
|
404 |
+
fclose($this->fp);
|
405 |
+
return $this->error('ID3 tags not allowed on this file type.');
|
406 |
+
} elseif ($determined_format['fail_id3'] === 'WARNING') {
|
407 |
+
$this->warning('ID3 tags not allowed on this file type.');
|
408 |
+
}
|
409 |
+
}
|
410 |
+
|
411 |
+
// check for illegal APE tags
|
412 |
+
if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
|
413 |
+
if ($determined_format['fail_ape'] === 'ERROR') {
|
414 |
+
fclose($this->fp);
|
415 |
+
return $this->error('APE tags not allowed on this file type.');
|
416 |
+
} elseif ($determined_format['fail_ape'] === 'WARNING') {
|
417 |
+
$this->warning('APE tags not allowed on this file type.');
|
418 |
+
}
|
419 |
+
}
|
420 |
+
|
421 |
+
// set mime type
|
422 |
+
$this->info['mime_type'] = $determined_format['mime_type'];
|
423 |
+
|
424 |
+
// supported format signature pattern detected, but module deleted
|
425 |
+
if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) {
|
426 |
+
fclose($this->fp);
|
427 |
+
return $this->error('Format not supported, module "'.$determined_format['include'].'" was removed.');
|
428 |
+
}
|
429 |
+
|
430 |
+
// module requires iconv support
|
431 |
+
// Check encoding/iconv support
|
432 |
+
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'))) {
|
433 |
+
$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. ';
|
434 |
+
if (GETID3_OS_ISWINDOWS) {
|
435 |
+
$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';
|
436 |
+
} else {
|
437 |
+
$errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
|
438 |
+
}
|
439 |
+
return $this->error($errormessage);
|
440 |
+
}
|
441 |
+
|
442 |
+
// include module
|
443 |
+
include_once(GETID3_INCLUDEPATH.$determined_format['include']);
|
444 |
+
|
445 |
+
// instantiate module class
|
446 |
+
$class_name = 'getid3_'.$determined_format['module'];
|
447 |
+
if (!class_exists($class_name)) {
|
448 |
+
return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.');
|
449 |
+
}
|
450 |
+
//if (isset($determined_format['option'])) {
|
451 |
+
// //$class = new $class_name($this->fp, $this->info, $determined_format['option']);
|
452 |
+
//} else {
|
453 |
+
//$class = new $class_name($this->fp, $this->info);
|
454 |
+
$class = new $class_name($this);
|
455 |
+
//}
|
456 |
+
|
457 |
+
if (!empty($determined_format['set_inline_attachments'])) {
|
458 |
+
$class->inline_attachments = $this->option_save_attachments;
|
459 |
+
}
|
460 |
+
$class->Analyze();
|
461 |
+
|
462 |
+
unset($class);
|
463 |
+
|
464 |
+
// close file
|
465 |
+
fclose($this->fp);
|
466 |
+
|
467 |
+
// process all tags - copy to 'tags' and convert charsets
|
468 |
+
if ($this->option_tags_process) {
|
469 |
+
$this->HandleAllTags();
|
470 |
+
}
|
471 |
+
|
472 |
+
// perform more calculations
|
473 |
+
if ($this->option_extra_info) {
|
474 |
+
$this->ChannelsBitratePlaytimeCalculations();
|
475 |
+
$this->CalculateCompressionRatioVideo();
|
476 |
+
$this->CalculateCompressionRatioAudio();
|
477 |
+
$this->CalculateReplayGain();
|
478 |
+
$this->ProcessAudioStreams();
|
479 |
+
}
|
480 |
+
|
481 |
+
// get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
|
482 |
+
if ($this->option_md5_data) {
|
483 |
+
// do not cald md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
|
484 |
+
if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
|
485 |
+
$this->getHashdata('md5');
|
486 |
+
}
|
487 |
+
}
|
488 |
+
|
489 |
+
// get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
|
490 |
+
if ($this->option_sha1_data) {
|
491 |
+
$this->getHashdata('sha1');
|
492 |
+
}
|
493 |
+
|
494 |
+
// remove undesired keys
|
495 |
+
$this->CleanUp();
|
496 |
+
|
497 |
+
} catch (Exception $e) {
|
498 |
+
$this->error('Caught exception: '.$e->getMessage());
|
499 |
+
}
|
500 |
+
|
501 |
+
// return info array
|
502 |
+
return $this->info;
|
503 |
+
}
|
504 |
+
|
505 |
+
|
506 |
+
// private: error handling
|
507 |
+
function error($message) {
|
508 |
+
$this->CleanUp();
|
509 |
+
if (!isset($this->info['error'])) {
|
510 |
+
$this->info['error'] = array();
|
511 |
+
}
|
512 |
+
$this->info['error'][] = $message;
|
513 |
+
return $this->info;
|
514 |
+
}
|
515 |
+
|
516 |
+
|
517 |
+
// private: warning handling
|
518 |
+
function warning($message) {
|
519 |
+
$this->info['warning'][] = $message;
|
520 |
+
return true;
|
521 |
+
}
|
522 |
+
|
523 |
+
|
524 |
+
// private: CleanUp
|
525 |
+
function CleanUp() {
|
526 |
+
|
527 |
+
// remove possible empty keys
|
528 |
+
$AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate');
|
529 |
+
foreach ($AVpossibleEmptyKeys as $dummy => $key) {
|
530 |
+
if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) {
|
531 |
+
unset($this->info['audio'][$key]);
|
532 |
+
}
|
533 |
+
if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) {
|
534 |
+
unset($this->info['video'][$key]);
|
535 |
+
}
|
536 |
+
}
|
537 |
+
|
538 |
+
// remove empty root keys
|
539 |
+
if (!empty($this->info)) {
|
540 |
+
foreach ($this->info as $key => $value) {
|
541 |
+
if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) {
|
542 |
+
unset($this->info[$key]);
|
543 |
+
}
|
544 |
+
}
|
545 |
+
}
|
546 |
+
|
547 |
+
// remove meaningless entries from unknown-format files
|
548 |
+
if (empty($this->info['fileformat'])) {
|
549 |
+
if (isset($this->info['avdataoffset'])) {
|
550 |
+
unset($this->info['avdataoffset']);
|
551 |
+
}
|
552 |
+
if (isset($this->info['avdataend'])) {
|
553 |
+
unset($this->info['avdataend']);
|
554 |
+
}
|
555 |
+
}
|
556 |
+
|
557 |
+
// remove possible duplicated identical entries
|
558 |
+
if (!empty($this->info['error'])) {
|
559 |
+
$this->info['error'] = array_values(array_unique($this->info['error']));
|
560 |
+
}
|
561 |
+
if (!empty($this->info['warning'])) {
|
562 |
+
$this->info['warning'] = array_values(array_unique($this->info['warning']));
|
563 |
+
}
|
564 |
+
|
565 |
+
// remove "global variable" type keys
|
566 |
+
unset($this->info['php_memory_limit']);
|
567 |
+
|
568 |
+
return true;
|
569 |
+
}
|
570 |
+
|
571 |
+
|
572 |
+
// return array containing information about all supported formats
|
573 |
+
function GetFileFormatArray() {
|
574 |
+
static $format_info = array();
|
575 |
+
if (empty($format_info)) {
|
576 |
+
$format_info = array(
|
577 |
+
|
578 |
+
// Audio formats
|
579 |
+
|
580 |
+
// AC-3 - audio - Dolby AC-3 / Dolby Digital
|
581 |
+
'ac3' => array(
|
582 |
+
'pattern' => '^\x0B\x77',
|
583 |
+
'group' => 'audio',
|
584 |
+
'module' => 'ac3',
|
585 |
+
'mime_type' => 'audio/ac3',
|
586 |
+
),
|
587 |
+
|
588 |
+
// AAC - audio - Advanced Audio Coding (AAC) - ADIF format
|
589 |
+
'adif' => array(
|
590 |
+
'pattern' => '^ADIF',
|
591 |
+
'group' => 'audio',
|
592 |
+
'module' => 'aac',
|
593 |
+
'mime_type' => 'application/octet-stream',
|
594 |
+
'fail_ape' => 'WARNING',
|
595 |
+
),
|
596 |
+
|
597 |
+
|
598 |
+
// AA - audio - Audible Audiobook
|
599 |
+
'adts' => array(
|
600 |
+
'pattern' => '^.{4}\x57\x90\x75\x36',
|
601 |
+
'group' => 'audio',
|
602 |
+
'module' => 'aa',
|
603 |
+
'mime_type' => 'audio/audible ',
|
604 |
+
),
|
605 |
+
|
606 |
+
// AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
|
607 |
+
'adts' => array(
|
608 |
+
'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]',
|
609 |
+
'group' => 'audio',
|
610 |
+
'module' => 'aac',
|
611 |
+
'mime_type' => 'application/octet-stream',
|
612 |
+
'fail_ape' => 'WARNING',
|
613 |
+
),
|
614 |
+
|
615 |
+
|
616 |
+
// AU - audio - NeXT/Sun AUdio (AU)
|
617 |
+
'au' => array(
|
618 |
+
'pattern' => '^\.snd',
|
619 |
+
'group' => 'audio',
|
620 |
+
'module' => 'au',
|
621 |
+
'mime_type' => 'audio/basic',
|
622 |
+
),
|
623 |
+
|
624 |
+
// AVR - audio - Audio Visual Research
|
625 |
+
'avr' => array(
|
626 |
+
'pattern' => '^2BIT',
|
627 |
+
'group' => 'audio',
|
628 |
+
'module' => 'avr',
|
629 |
+
'mime_type' => 'application/octet-stream',
|
630 |
+
),
|
631 |
+
|
632 |
+
// BONK - audio - Bonk v0.9+
|
633 |
+
'bonk' => array(
|
634 |
+
'pattern' => '^\x00(BONK|INFO|META| ID3)',
|
635 |
+
'group' => 'audio',
|
636 |
+
'module' => 'bonk',
|
637 |
+
'mime_type' => 'audio/xmms-bonk',
|
638 |
+
),
|
639 |
+
|
640 |
+
// DSS - audio - Digital Speech Standard
|
641 |
+
'dss' => array(
|
642 |
+
'pattern' => '^[\x02-\x03]dss',
|
643 |
+
'group' => 'audio',
|
644 |
+
'module' => 'dss',
|
645 |
+
'mime_type' => 'application/octet-stream',
|
646 |
+
),
|
647 |
+
|
648 |
+
// DTS - audio - Dolby Theatre System
|
649 |
+
'dts' => array(
|
650 |
+
'pattern' => '^\x7F\xFE\x80\x01',
|
651 |
+
'group' => 'audio',
|
652 |
+
'module' => 'dts',
|
653 |
+
'mime_type' => 'audio/dts',
|
654 |
+
),
|
655 |
+
|
656 |
+
// FLAC - audio - Free Lossless Audio Codec
|
657 |
+
'flac' => array(
|
658 |
+
'pattern' => '^fLaC',
|
659 |
+
'group' => 'audio',
|
660 |
+
'module' => 'flac',
|
661 |
+
'mime_type' => 'audio/x-flac',
|
662 |
+
'set_inline_attachments' => true,
|
663 |
+
),
|
664 |
+
|
665 |
+
// LA - audio - Lossless Audio (LA)
|
666 |
+
'la' => array(
|
667 |
+
'pattern' => '^LA0[2-4]',
|
668 |
+
'group' => 'audio',
|
669 |
+
'module' => 'la',
|
670 |
+
'mime_type' => 'application/octet-stream',
|
671 |
+
),
|
672 |
+
|
673 |
+
// LPAC - audio - Lossless Predictive Audio Compression (LPAC)
|
674 |
+
'lpac' => array(
|
675 |
+
'pattern' => '^LPAC',
|
676 |
+
'group' => 'audio',
|
677 |
+
'module' => 'lpac',
|
678 |
+
'mime_type' => 'application/octet-stream',
|
679 |
+
),
|
680 |
+
|
681 |
+
// MIDI - audio - MIDI (Musical Instrument Digital Interface)
|
682 |
+
'midi' => array(
|
683 |
+
'pattern' => '^MThd',
|
684 |
+
'group' => 'audio',
|
685 |
+
'module' => 'midi',
|
686 |
+
'mime_type' => 'audio/midi',
|
687 |
+
),
|
688 |
+
|
689 |
+
// MAC - audio - Monkey's Audio Compressor
|
690 |
+
'mac' => array(
|
691 |
+
'pattern' => '^MAC ',
|
692 |
+
'group' => 'audio',
|
693 |
+
'module' => 'monkey',
|
694 |
+
'mime_type' => 'application/octet-stream',
|
695 |
+
),
|
696 |
+
|
697 |
+
// has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available
|
698 |
+
// // MOD - audio - MODule (assorted sub-formats)
|
699 |
+
// 'mod' => array(
|
700 |
+
// 'pattern' => '^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)',
|
701 |
+
// 'group' => 'audio',
|
702 |
+
// 'module' => 'mod',
|
703 |
+
// 'option' => 'mod',
|
704 |
+
// 'mime_type' => 'audio/mod',
|
705 |
+
// ),
|
706 |
+
|
707 |
+
// MOD - audio - MODule (Impulse Tracker)
|
708 |
+
'it' => array(
|
709 |
+
'pattern' => '^IMPM',
|
710 |
+
'group' => 'audio',
|
711 |
+
'module' => 'mod',
|
712 |
+
//'option' => 'it',
|
713 |
+
'mime_type' => 'audio/it',
|
714 |
+
),
|
715 |
+
|
716 |
+
// MOD - audio - MODule (eXtended Module, various sub-formats)
|
717 |
+
'xm' => array(
|
718 |
+
'pattern' => '^Extended Module',
|
719 |
+
'group' => 'audio',
|
720 |
+
'module' => 'mod',
|
721 |
+
//'option' => 'xm',
|
722 |
+
'mime_type' => 'audio/xm',
|
723 |
+
),
|
724 |
+
|
725 |
+
// MOD - audio - MODule (ScreamTracker)
|
726 |
+
's3m' => array(
|
727 |
+
'pattern' => '^.{44}SCRM',
|
728 |
+
'group' => 'audio',
|
729 |
+
'module' => 'mod',
|
730 |
+
//'option' => 's3m',
|
731 |
+
'mime_type' => 'audio/s3m',
|
732 |
+
),
|
733 |
+
|
734 |
+
// MPC - audio - Musepack / MPEGplus
|
735 |
+
'mpc' => array(
|
736 |
+
'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])',
|
737 |
+
'group' => 'audio',
|
738 |
+
'module' => 'mpc',
|
739 |
+
'mime_type' => 'audio/x-musepack',
|
740 |
+
),
|
741 |
+
|
742 |
+
// MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS)
|
743 |
+
'mp3' => array(
|
744 |
+
'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]',
|
745 |
+
'group' => 'audio',
|
746 |
+
'module' => 'mp3',
|
747 |
+
'mime_type' => 'audio/mpeg',
|
748 |
+
),
|
749 |
+
|
750 |
+
// OFR - audio - OptimFROG
|
751 |
+
'ofr' => array(
|
752 |
+
'pattern' => '^(\*RIFF|OFR)',
|
753 |
+
'group' => 'audio',
|
754 |
+
'module' => 'optimfrog',
|
755 |
+
'mime_type' => 'application/octet-stream',
|
756 |
+
),
|
757 |
+
|
758 |
+
// RKAU - audio - RKive AUdio compressor
|
759 |
+
'rkau' => array(
|
760 |
+
'pattern' => '^RKA',
|
761 |
+
'group' => 'audio',
|
762 |
+
'module' => 'rkau',
|
763 |
+
'mime_type' => 'application/octet-stream',
|
764 |
+
),
|
765 |
+
|
766 |
+
// SHN - audio - Shorten
|
767 |
+
'shn' => array(
|
768 |
+
'pattern' => '^ajkg',
|
769 |
+
'group' => 'audio',
|
770 |
+
'module' => 'shorten',
|
771 |
+
'mime_type' => 'audio/xmms-shn',
|
772 |
+
'fail_id3' => 'ERROR',
|
773 |
+
'fail_ape' => 'ERROR',
|
774 |
+
),
|
775 |
+
|
776 |
+
// TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org)
|
777 |
+
'tta' => array(
|
778 |
+
'pattern' => '^TTA', // could also be '^TTA(\x01|\x02|\x03|2|1)'
|
779 |
+
'group' => 'audio',
|
780 |
+
'module' => 'tta',
|
781 |
+
'mime_type' => 'application/octet-stream',
|
782 |
+
),
|
783 |
+
|
784 |
+
// VOC - audio - Creative Voice (VOC)
|
785 |
+
'voc' => array(
|
786 |
+
'pattern' => '^Creative Voice File',
|
787 |
+
'group' => 'audio',
|
788 |
+
'module' => 'voc',
|
789 |
+
'mime_type' => 'audio/voc',
|
790 |
+
),
|
791 |
+
|
792 |
+
// VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF)
|
793 |
+
'vqf' => array(
|
794 |
+
'pattern' => '^TWIN',
|
795 |
+
'group' => 'audio',
|
796 |
+
'module' => 'vqf',
|
797 |
+
'mime_type' => 'application/octet-stream',
|
798 |
+
),
|
799 |
+
|
800 |
+
// WV - audio - WavPack (v4.0+)
|
801 |
+
'wv' => array(
|
802 |
+
'pattern' => '^wvpk',
|
803 |
+
'group' => 'audio',
|
804 |
+
'module' => 'wavpack',
|
805 |
+
'mime_type' => 'application/octet-stream',
|
806 |
+
),
|
807 |
+
|
808 |
+
|
809 |
+
// Audio-Video formats
|
810 |
+
|
811 |
+
// ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio
|
812 |
+
'asf' => array(
|
813 |
+
'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C',
|
814 |
+
'group' => 'audio-video',
|
815 |
+
'module' => 'asf',
|
816 |
+
'mime_type' => 'video/x-ms-asf',
|
817 |
+
'iconv_req' => false,
|
818 |
+
),
|
819 |
+
|
820 |
+
// BINK - audio/video - Bink / Smacker
|
821 |
+
'bink' => array(
|
822 |
+
'pattern' => '^(BIK|SMK)',
|
823 |
+
'group' => 'audio-video',
|
824 |
+
'module' => 'bink',
|
825 |
+
'mime_type' => 'application/octet-stream',
|
826 |
+
),
|
827 |
+
|
828 |
+
// FLV - audio/video - FLash Video
|
829 |
+
'flv' => array(
|
830 |
+
'pattern' => '^FLV\x01',
|
831 |
+
'group' => 'audio-video',
|
832 |
+
'module' => 'flv',
|
833 |
+
'mime_type' => 'video/x-flv',
|
834 |
+
),
|
835 |
+
|
836 |
+
// MKAV - audio/video - Mastroka
|
837 |
+
'matroska' => array(
|
838 |
+
'pattern' => '^\x1A\x45\xDF\xA3',
|
839 |
+
'group' => 'audio-video',
|
840 |
+
'module' => 'matroska',
|
841 |
+
'mime_type' => 'video/x-matroska', // may also be audio/x-matroska
|
842 |
+
'set_inline_attachments' => true,
|
843 |
+
),
|
844 |
+
|
845 |
+
// MPEG - audio/video - MPEG (Moving Pictures Experts Group)
|
846 |
+
'mpeg' => array(
|
847 |
+
'pattern' => '^\x00\x00\x01(\xBA|\xB3)',
|
848 |
+
'group' => 'audio-video',
|
849 |
+
'module' => 'mpeg',
|
850 |
+
'mime_type' => 'video/mpeg',
|
851 |
+
),
|
852 |
+
|
853 |
+
// NSV - audio/video - Nullsoft Streaming Video (NSV)
|
854 |
+
'nsv' => array(
|
855 |
+
'pattern' => '^NSV[sf]',
|
856 |
+
'group' => 'audio-video',
|
857 |
+
'module' => 'nsv',
|
858 |
+
'mime_type' => 'application/octet-stream',
|
859 |
+
),
|
860 |
+
|
861 |
+
// Ogg - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*))
|
862 |
+
'ogg' => array(
|
863 |
+
'pattern' => '^OggS',
|
864 |
+
'group' => 'audio',
|
865 |
+
'module' => 'ogg',
|
866 |
+
'mime_type' => 'application/ogg',
|
867 |
+
'fail_id3' => 'WARNING',
|
868 |
+
'fail_ape' => 'WARNING',
|
869 |
+
'set_inline_attachments' => true,
|
870 |
+
),
|
871 |
+
|
872 |
+
// QT - audio/video - Quicktime
|
873 |
+
'quicktime' => array(
|
874 |
+
'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)',
|
875 |
+
'group' => 'audio-video',
|
876 |
+
'module' => 'quicktime',
|
877 |
+
'mime_type' => 'video/quicktime',
|
878 |
+
),
|
879 |
+
|
880 |
+
// 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)
|
881 |
+
'riff' => array(
|
882 |
+
'pattern' => '^(RIFF|SDSS|FORM)',
|
883 |
+
'group' => 'audio-video',
|
884 |
+
'module' => 'riff',
|
885 |
+
'mime_type' => 'audio/x-wave',
|
886 |
+
'fail_ape' => 'WARNING',
|
887 |
+
),
|
888 |
+
|
889 |
+
// Real - audio/video - RealAudio, RealVideo
|
890 |
+
'real' => array(
|
891 |
+
'pattern' => '^(\\.RMF|\\.ra)',
|
892 |
+
'group' => 'audio-video',
|
893 |
+
'module' => 'real',
|
894 |
+
'mime_type' => 'audio/x-realaudio',
|
895 |
+
),
|
896 |
+
|
897 |
+
// SWF - audio/video - ShockWave Flash
|
898 |
+
'swf' => array(
|
899 |
+
'pattern' => '^(F|C)WS',
|
900 |
+
'group' => 'audio-video',
|
901 |
+
'module' => 'swf',
|
902 |
+
'mime_type' => 'application/x-shockwave-flash',
|
903 |
+
),
|
904 |
+
|
905 |
+
|
906 |
+
// Still-Image formats
|
907 |
+
|
908 |
+
// BMP - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4)
|
909 |
+
'bmp' => array(
|
910 |
+
'pattern' => '^BM',
|
911 |
+
'group' => 'graphic',
|
912 |
+
'module' => 'bmp',
|
913 |
+
'mime_type' => 'image/bmp',
|
914 |
+
'fail_id3' => 'ERROR',
|
915 |
+
'fail_ape' => 'ERROR',
|
916 |
+
),
|
917 |
+
|
918 |
+
// GIF - still image - Graphics Interchange Format
|
919 |
+
'gif' => array(
|
920 |
+
'pattern' => '^GIF',
|
921 |
+
'group' => 'graphic',
|
922 |
+
'module' => 'gif',
|
923 |
+
'mime_type' => 'image/gif',
|
924 |
+
'fail_id3' => 'ERROR',
|
925 |
+
'fail_ape' => 'ERROR',
|
926 |
+
),
|
927 |
+
|
928 |
+
// JPEG - still image - Joint Photographic Experts Group (JPEG)
|
929 |
+
'jpg' => array(
|
930 |
+
'pattern' => '^\xFF\xD8\xFF',
|
931 |
+
'group' => 'graphic',
|
932 |
+
'module' => 'jpg',
|
933 |
+
'mime_type' => 'image/jpeg',
|
934 |
+
'fail_id3' => 'ERROR',
|
935 |
+
'fail_ape' => 'ERROR',
|
936 |
+
),
|
937 |
+
|
938 |
+
// PCD - still image - Kodak Photo CD
|
939 |
+
'pcd' => array(
|
940 |
+
'pattern' => '^.{2048}PCD_IPI\x00',
|
941 |
+
'group' => 'graphic',
|
942 |
+
'module' => 'pcd',
|
943 |
+
'mime_type' => 'image/x-photo-cd',
|
944 |
+
'fail_id3' => 'ERROR',
|
945 |
+
'fail_ape' => 'ERROR',
|
946 |
+
),
|
947 |
+
|
948 |
+
|
949 |
+
// PNG - still image - Portable Network Graphics (PNG)
|
950 |
+
'png' => array(
|
951 |
+
'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A',
|
952 |
+
'group' => 'graphic',
|
953 |
+
'module' => 'png',
|
954 |
+
'mime_type' => 'image/png',
|
955 |
+
'fail_id3' => 'ERROR',
|
956 |
+
'fail_ape' => 'ERROR',
|
957 |
+
),
|
958 |
+
|
959 |
+
|
960 |
+
// SVG - still image - Scalable Vector Graphics (SVG)
|
961 |
+
'svg' => array(
|
962 |
+
'pattern' => '(<!DOCTYPE svg PUBLIC |xmlns="http:\/\/www\.w3\.org\/2000\/svg")',
|
963 |
+
'group' => 'graphic',
|
964 |
+
'module' => 'svg',
|
965 |
+
'mime_type' => 'image/svg+xml',
|
966 |
+
'fail_id3' => 'ERROR',
|
967 |
+
'fail_ape' => 'ERROR',
|
968 |
+
),
|
969 |
+
|
970 |
+
|
971 |
+
// TIFF - still image - Tagged Information File Format (TIFF)
|
972 |
+
'tiff' => array(
|
973 |
+
'pattern' => '^(II\x2A\x00|MM\x00\x2A)',
|
974 |
+
'group' => 'graphic',
|
975 |
+
'module' => 'tiff',
|
976 |
+
'mime_type' => 'image/tiff',
|
977 |
+
'fail_id3' => 'ERROR',
|
978 |
+
'fail_ape' => 'ERROR',
|
979 |
+
),
|
980 |
+
|
981 |
+
|
982 |
+
// EFAX - still image - eFax (TIFF derivative)
|
983 |
+
'bmp' => array(
|
984 |
+
'pattern' => '^\xDC\xFE',
|
985 |
+
'group' => 'graphic',
|
986 |
+
'module' => 'efax',
|
987 |
+
'mime_type' => 'image/efax',
|
988 |
+
'fail_id3' => 'ERROR',
|
989 |
+
'fail_ape' => 'ERROR',
|
990 |
+
),
|
991 |
+
|
992 |
+
|
993 |
+
// Data formats
|
994 |
+
|
995 |
+
// ISO - data - International Standards Organization (ISO) CD-ROM Image
|
996 |
+
'iso' => array(
|
997 |
+
'pattern' => '^.{32769}CD001',
|
998 |
+
'group' => 'misc',
|
999 |
+
'module' => 'iso',
|
1000 |
+
'mime_type' => 'application/octet-stream',
|
1001 |
+
'fail_id3' => 'ERROR',
|
1002 |
+
'fail_ape' => 'ERROR',
|
1003 |
+
'iconv_req' => false,
|
1004 |
+
),
|
1005 |
+
|
1006 |
+
// RAR - data - RAR compressed data
|
1007 |
+
'rar' => array(
|
1008 |
+
'pattern' => '^Rar\!',
|
1009 |
+
'group' => 'archive',
|
1010 |
+
'module' => 'rar',
|
1011 |
+
'mime_type' => 'application/octet-stream',
|
1012 |
+
'fail_id3' => 'ERROR',
|
1013 |
+
'fail_ape' => 'ERROR',
|
1014 |
+
),
|
1015 |
+
|
1016 |
+
// SZIP - audio/data - SZIP compressed data
|
1017 |
+
'szip' => array(
|
1018 |
+
'pattern' => '^SZ\x0A\x04',
|
1019 |
+
'group' => 'archive',
|
1020 |
+
'module' => 'szip',
|
1021 |
+
'mime_type' => 'application/octet-stream',
|
1022 |
+
'fail_id3' => 'ERROR',
|
1023 |
+
'fail_ape' => 'ERROR',
|
1024 |
+
),
|
1025 |
+
|
1026 |
+
// TAR - data - TAR compressed data
|
1027 |
+
'tar' => array(
|
1028 |
+
'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}',
|
1029 |
+
'group' => 'archive',
|
1030 |
+
'module' => 'tar',
|
1031 |
+
'mime_type' => 'application/x-tar',
|
1032 |
+
'fail_id3' => 'ERROR',
|
1033 |
+
'fail_ape' => 'ERROR',
|
1034 |
+
),
|
1035 |
+
|
1036 |
+
// GZIP - data - GZIP compressed data
|
1037 |
+
'gz' => array(
|
1038 |
+
'pattern' => '^\x1F\x8B\x08',
|
1039 |
+
'group' => 'archive',
|
1040 |
+
'module' => 'gzip',
|
1041 |
+
'mime_type' => 'application/x-gzip',
|
1042 |
+
'fail_id3' => 'ERROR',
|
1043 |
+
'fail_ape' => 'ERROR',
|
1044 |
+
),
|
1045 |
+
|
1046 |
+
// ZIP - data - ZIP compressed data
|
1047 |
+
'zip' => array(
|
1048 |
+
'pattern' => '^PK\x03\x04',
|
1049 |
+
'group' => 'archive',
|
1050 |
+
'module' => 'zip',
|
1051 |
+
'mime_type' => 'application/zip',
|
1052 |
+
'fail_id3' => 'ERROR',
|
1053 |
+
'fail_ape' => 'ERROR',
|
1054 |
+
),
|
1055 |
+
|
1056 |
+
|
1057 |
+
// Misc other formats
|
1058 |
+
|
1059 |
+
// PAR2 - data - Parity Volume Set Specification 2.0
|
1060 |
+
'par2' => array (
|
1061 |
+
'pattern' => '^PAR2\x00PKT',
|
1062 |
+
'group' => 'misc',
|
1063 |
+
'module' => 'par2',
|
1064 |
+
'mime_type' => 'application/octet-stream',
|
1065 |
+
'fail_id3' => 'ERROR',
|
1066 |
+
'fail_ape' => 'ERROR',
|
1067 |
+
),
|
1068 |
+
|
1069 |
+
// PDF - data - Portable Document Format
|
1070 |
+
'pdf' => array(
|
1071 |
+
'pattern' => '^\x25PDF',
|
1072 |
+
'group' => 'misc',
|
1073 |
+
'module' => 'pdf',
|
1074 |
+
'mime_type' => 'application/pdf',
|
1075 |
+
'fail_id3' => 'ERROR',
|
1076 |
+
'fail_ape' => 'ERROR',
|
1077 |
+
),
|
1078 |
+
|
1079 |
+
// MSOFFICE - data - ZIP compressed data
|
1080 |
+
'msoffice' => array(
|
1081 |
+
'pattern' => '^\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1', // D0CF11E == DOCFILE == Microsoft Office Document
|
1082 |
+
'group' => 'misc',
|
1083 |
+
'module' => 'msoffice',
|
1084 |
+
'mime_type' => 'application/octet-stream',
|
1085 |
+
'fail_id3' => 'ERROR',
|
1086 |
+
'fail_ape' => 'ERROR',
|
1087 |
+
),
|
1088 |
+
|
1089 |
+
// CUE - data - CUEsheet (index to single-file disc images)
|
1090 |
+
'cue' => array(
|
1091 |
+
'pattern' => '', // empty pattern means cannot be automatically detected, will fall through all other formats and match based on filename and very basic file contents
|
1092 |
+
'group' => 'misc',
|
1093 |
+
'module' => 'cue',
|
1094 |
+
'mime_type' => 'application/octet-stream',
|
1095 |
+
),
|
1096 |
+
|
1097 |
+
);
|
1098 |
+
}
|
1099 |
+
|
1100 |
+
return $format_info;
|
1101 |
+
}
|
1102 |
+
|
1103 |
+
|
1104 |
+
|
1105 |
+
function GetFileFormat(&$filedata, $filename='') {
|
1106 |
+
// this function will determine the format of a file based on usually
|
1107 |
+
// the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG,
|
1108 |
+
// and in the case of ISO CD image, 6 bytes offset 32kb from the start
|
1109 |
+
// of the file).
|
1110 |
+
|
1111 |
+
// Identify file format - loop through $format_info and detect with reg expr
|
1112 |
+
foreach ($this->GetFileFormatArray() as $format_name => $info) {
|
1113 |
+
// The /s switch on preg_match() forces preg_match() NOT to treat
|
1114 |
+
// newline (0x0A) characters as special chars but do a binary match
|
1115 |
+
if (!empty($info['pattern']) && preg_match('#'.$info['pattern'].'#s', $filedata)) {
|
1116 |
+
$info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
|
1117 |
+
return $info;
|
1118 |
+
}
|
1119 |
+
}
|
1120 |
+
|
1121 |
+
|
1122 |
+
if (preg_match('#\.mp[123a]$#i', $filename)) {
|
1123 |
+
// Too many mp3 encoders on the market put gabage in front of mpeg files
|
1124 |
+
// use assume format on these if format detection failed
|
1125 |
+
$GetFileFormatArray = $this->GetFileFormatArray();
|
1126 |
+
$info = $GetFileFormatArray['mp3'];
|
1127 |
+
$info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
|
1128 |
+
return $info;
|
1129 |
+
} elseif (preg_match('/\.cue$/i', $filename) && preg_match('#FILE "[^"]+" (BINARY|MOTOROLA|AIFF|WAVE|MP3)#', $filedata)) {
|
1130 |
+
// there's not really a useful consistent "magic" at the beginning of .cue files to identify them
|
1131 |
+
// so until I think of something better, just go by filename if all other format checks fail
|
1132 |
+
// and verify there's at least one instance of "TRACK xx AUDIO" in the file
|
1133 |
+
$GetFileFormatArray = $this->GetFileFormatArray();
|
1134 |
+
$info = $GetFileFormatArray['cue'];
|
1135 |
+
$info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
|
1136 |
+
return $info;
|
1137 |
+
}
|
1138 |
+
|
1139 |
+
return false;
|
1140 |
+
}
|
1141 |
+
|
1142 |
+
|
1143 |
+
// converts array to $encoding charset from $this->encoding
|
1144 |
+
function CharConvert(&$array, $encoding) {
|
1145 |
+
|
1146 |
+
// identical encoding - end here
|
1147 |
+
if ($encoding == $this->encoding) {
|
1148 |
+
return;
|
1149 |
+
}
|
1150 |
+
|
1151 |
+
// loop thru array
|
1152 |
+
foreach ($array as $key => $value) {
|
1153 |
+
|
1154 |
+
// go recursive
|
1155 |
+
if (is_array($value)) {
|
1156 |
+
$this->CharConvert($array[$key], $encoding);
|
1157 |
+
}
|
1158 |
+
|
1159 |
+
// convert string
|
1160 |
+
elseif (is_string($value)) {
|
1161 |
+
$array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value));
|
1162 |
+
}
|
1163 |
+
}
|
1164 |
+
}
|
1165 |
+
|
1166 |
+
|
1167 |
+
function HandleAllTags() {
|
1168 |
+
|
1169 |
+
// key name => array (tag name, character encoding)
|
1170 |
+
static $tags;
|
1171 |
+
if (empty($tags)) {
|
1172 |
+
$tags = array(
|
1173 |
+
'asf' => array('asf' , 'UTF-16LE'),
|
1174 |
+
'midi' => array('midi' , 'ISO-8859-1'),
|
1175 |
+
'nsv' => array('nsv' , 'ISO-8859-1'),
|
1176 |
+
'ogg' => array('vorbiscomment' , 'UTF-8'),
|
1177 |
+
'png' => array('png' , 'UTF-8'),
|
1178 |
+
'tiff' => array('tiff' , 'ISO-8859-1'),
|
1179 |
+
'quicktime' => array('quicktime' , 'UTF-8'),
|
1180 |
+
'real' => array('real' , 'ISO-8859-1'),
|
1181 |
+
'vqf' => array('vqf' , 'ISO-8859-1'),
|
1182 |
+
'zip' => array('zip' , 'ISO-8859-1'),
|
1183 |
+
'riff' => array('riff' , 'ISO-8859-1'),
|
1184 |
+
'lyrics3' => array('lyrics3' , 'ISO-8859-1'),
|
1185 |
+
'id3v1' => array('id3v1' , $this->encoding_id3v1),
|
1186 |
+
'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
|
1187 |
+
'ape' => array('ape' , 'UTF-8'),
|
1188 |
+
'cue' => array('cue' , 'ISO-8859-1'),
|
1189 |
+
'matroska' => array('matroska' , 'UTF-8'),
|
1190 |
+
);
|
1191 |
+
}
|
1192 |
+
|
1193 |
+
// loop through comments array
|
1194 |
+
foreach ($tags as $comment_name => $tagname_encoding_array) {
|
1195 |
+
list($tag_name, $encoding) = $tagname_encoding_array;
|
1196 |
+
|
1197 |
+
// fill in default encoding type if not already present
|
1198 |
+
if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) {
|
1199 |
+
$this->info[$comment_name]['encoding'] = $encoding;
|
1200 |
+
}
|
1201 |
+
|
1202 |
+
// copy comments if key name set
|
1203 |
+
if (!empty($this->info[$comment_name]['comments'])) {
|
1204 |
+
|
1205 |
+
foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) {
|
1206 |
+
foreach ($valuearray as $key => $value) {
|
1207 |
+
if (is_string($value)) {
|
1208 |
+
$value = trim($value, " \r\n\t"); // do not trim nulls from $value!! Unicode characters will get mangled if trailing nulls are removed!
|
1209 |
+
}
|
1210 |
+
if ($value) {
|
1211 |
+
$this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value;
|
1212 |
+
}
|
1213 |
+
}
|
1214 |
+
}
|
1215 |
+
|
1216 |
+
if (!isset($this->info['tags'][$tag_name])) {
|
1217 |
+
// comments are set but contain nothing but empty strings, so skip
|
1218 |
+
continue;
|
1219 |
+
}
|
1220 |
+
|
1221 |
+
if ($this->option_tags_html) {
|
1222 |
+
foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
|
1223 |
+
foreach ($valuearray as $key => $value) {
|
1224 |
+
if (is_string($value)) {
|
1225 |
+
//$this->info['tags_html'][$tag_name][$tag_key][$key] = getid3_lib::MultiByteCharString2HTML($value, $encoding);
|
1226 |
+
$this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('�', '', trim(getid3_lib::MultiByteCharString2HTML($value, $encoding)));
|
1227 |
+
} else {
|
1228 |
+
$this->info['tags_html'][$tag_name][$tag_key][$key] = $value;
|
1229 |
+
}
|
1230 |
+
}
|
1231 |
+
}
|
1232 |
+
}
|
1233 |
+
|
1234 |
+
$this->CharConvert($this->info['tags'][$tag_name], $encoding); // only copy gets converted!
|
1235 |
+
}
|
1236 |
+
|
1237 |
+
}
|
1238 |
+
|
1239 |
+
// pictures can take up a lot of space, and we don't need multiple copies of them
|
1240 |
+
// let there be a single copy in [comments][picture], and not elsewhere
|
1241 |
+
if (!empty($this->info['tags'])) {
|
1242 |
+
$unset_keys = array('tags', 'tags_html');
|
1243 |
+
foreach ($this->info['tags'] as $tagtype => $tagarray) {
|
1244 |
+
foreach ($tagarray as $tagname => $tagdata) {
|
1245 |
+
if ($tagname == 'picture') {
|
1246 |
+
foreach ($tagdata as $key => $tagarray) {
|
1247 |
+
$this->info['comments']['picture'][] = $tagarray;
|
1248 |
+
if (isset($tagarray['data']) && isset($tagarray['image_mime'])) {
|
1249 |
+
if (isset($this->info['tags'][$tagtype][$tagname][$key])) {
|
1250 |
+
unset($this->info['tags'][$tagtype][$tagname][$key]);
|
1251 |
+
}
|
1252 |
+
if (isset($this->info['tags_html'][$tagtype][$tagname][$key])) {
|
1253 |
+
unset($this->info['tags_html'][$tagtype][$tagname][$key]);
|
1254 |
+
}
|
1255 |
+
}
|
1256 |
+
}
|
1257 |
+
}
|
1258 |
+
}
|
1259 |
+
foreach ($unset_keys as $unset_key) {
|
1260 |
+
// remove possible empty keys from (e.g. [tags][id3v2][picture])
|
1261 |
+
if (empty($this->info[$unset_key][$tagtype]['picture'])) {
|
1262 |
+
unset($this->info[$unset_key][$tagtype]['picture']);
|
1263 |
+
}
|
1264 |
+
if (empty($this->info[$unset_key][$tagtype])) {
|
1265 |
+
unset($this->info[$unset_key][$tagtype]);
|
1266 |
+
}
|
1267 |
+
if (empty($this->info[$unset_key])) {
|
1268 |
+
unset($this->info[$unset_key]);
|
1269 |
+
}
|
1270 |
+
}
|
1271 |
+
// remove duplicate copy of picture data from (e.g. [id3v2][comments][picture])
|
1272 |
+
if (isset($this->info[$tagtype]['comments']['picture'])) {
|
1273 |
+
unset($this->info[$tagtype]['comments']['picture']);
|
1274 |
+
}
|
1275 |
+
if (empty($this->info[$tagtype]['comments'])) {
|
1276 |
+
unset($this->info[$tagtype]['comments']);
|
1277 |
+
}
|
1278 |
+
if (empty($this->info[$tagtype])) {
|
1279 |
+
unset($this->info[$tagtype]);
|
1280 |
+
}
|
1281 |
+
}
|
1282 |
+
}
|
1283 |
+
return true;
|
1284 |
+
}
|
1285 |
+
|
1286 |
+
|
1287 |
+
function getHashdata($algorithm) {
|
1288 |
+
switch ($algorithm) {
|
1289 |
+
case 'md5':
|
1290 |
+
case 'sha1':
|
1291 |
+
break;
|
1292 |
+
|
1293 |
+
default:
|
1294 |
+
return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()');
|
1295 |
+
break;
|
1296 |
+
}
|
1297 |
+
|
1298 |
+
if (!empty($this->info['fileformat']) && !empty($this->info['dataformat']) && ($this->info['fileformat'] == 'ogg') && ($this->info['audio']['dataformat'] == 'vorbis')) {
|
1299 |
+
|
1300 |
+
// We cannot get an identical md5_data value for Ogg files where the comments
|
1301 |
+
// span more than 1 Ogg page (compared to the same audio data with smaller
|
1302 |
+
// comments) using the normal getID3() method of MD5'ing the data between the
|
1303 |
+
// end of the comments and the end of the file (minus any trailing tags),
|
1304 |
+
// because the page sequence numbers of the pages that the audio data is on
|
1305 |
+
// do not match. Under normal circumstances, where comments are smaller than
|
1306 |
+
// the nominal 4-8kB page size, then this is not a problem, but if there are
|
1307 |
+
// very large comments, the only way around it is to strip off the comment
|
1308 |
+
// tags with vorbiscomment and MD5 that file.
|
1309 |
+
// This procedure must be applied to ALL Ogg files, not just the ones with
|
1310 |
+
// comments larger than 1 page, because the below method simply MD5's the
|
1311 |
+
// whole file with the comments stripped, not just the portion after the
|
1312 |
+
// comments block (which is the standard getID3() method.
|
1313 |
+
|
1314 |
+
// The above-mentioned problem of comments spanning multiple pages and changing
|
1315 |
+
// page sequence numbers likely happens for OggSpeex and OggFLAC as well, but
|
1316 |
+
// currently vorbiscomment only works on OggVorbis files.
|
1317 |
+
|
1318 |
+
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
|
1319 |
+
|
1320 |
+
$this->warning('Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)');
|
1321 |
+
$this->info[$algorithm.'_data'] = false;
|
1322 |
+
|
1323 |
+
} else {
|
1324 |
+
|
1325 |
+
// Prevent user from aborting script
|
1326 |
+
$old_abort = ignore_user_abort(true);
|
1327 |
+
|
1328 |
+
// Create empty file
|
1329 |
+
$empty = tempnam(GETID3_TEMP_DIR, 'getID3');
|
1330 |
+
touch($empty);
|
1331 |
+
|
1332 |
+
// Use vorbiscomment to make temp file without comments
|
1333 |
+
$temp = tempnam(GETID3_TEMP_DIR, 'getID3');
|
1334 |
+
$file = $this->info['filenamepath'];
|
1335 |
+
|
1336 |
+
if (GETID3_OS_ISWINDOWS) {
|
1337 |
+
|
1338 |
+
if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
|
1339 |
+
|
1340 |
+
$commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"';
|
1341 |
+
$VorbisCommentError = `$commandline`;
|
1342 |
+
|
1343 |
+
} else {
|
1344 |
+
|
1345 |
+
$VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
|
1346 |
+
|
1347 |
+
}
|
1348 |
+
|
1349 |
+
} else {
|
1350 |
+
|
1351 |
+
$commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1';
|
1352 |
+
$commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1';
|
1353 |
+
$VorbisCommentError = `$commandline`;
|
1354 |
+
|
1355 |
+
}
|
1356 |
+
|
1357 |
+
if (!empty($VorbisCommentError)) {
|
1358 |
+
|
1359 |
+
$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;
|
1360 |
+
$this->info[$algorithm.'_data'] = false;
|
1361 |
+
|
1362 |
+
} else {
|
1363 |
+
|
1364 |
+
// Get hash of newly created file
|
1365 |
+
switch ($algorithm) {
|
1366 |
+
case 'md5':
|
1367 |
+
$this->info[$algorithm.'_data'] = md5_file($temp);
|
1368 |
+
break;
|
1369 |
+
|
1370 |
+
case 'sha1':
|
1371 |
+
$this->info[$algorithm.'_data'] = sha1_file($temp);
|
1372 |
+
break;
|
1373 |
+
}
|
1374 |
+
}
|
1375 |
+
|
1376 |
+
// Clean up
|
1377 |
+
unlink($empty);
|
1378 |
+
unlink($temp);
|
1379 |
+
|
1380 |
+
// Reset abort setting
|
1381 |
+
ignore_user_abort($old_abort);
|
1382 |
+
|
1383 |
+
}
|
1384 |
+
|
1385 |
+
} else {
|
1386 |
+
|
1387 |
+
if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) {
|
1388 |
+
|
1389 |
+
// get hash from part of file
|
1390 |
+
$this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm);
|
1391 |
+
|
1392 |
+
} else {
|
1393 |
+
|
1394 |
+
// get hash from whole file
|
1395 |
+
switch ($algorithm) {
|
1396 |
+
case 'md5':
|
1397 |
+
$this->info[$algorithm.'_data'] = md5_file($this->info['filenamepath']);
|
1398 |
+
break;
|
1399 |
+
|
1400 |
+
case 'sha1':
|
1401 |
+
$this->info[$algorithm.'_data'] = sha1_file($this->info['filenamepath']);
|
1402 |
+
break;
|
1403 |
+
}
|
1404 |
+
}
|
1405 |
+
|
1406 |
+
}
|
1407 |
+
return true;
|
1408 |
+
}
|
1409 |
+
|
1410 |
+
|
1411 |
+
function ChannelsBitratePlaytimeCalculations() {
|
1412 |
+
|
1413 |
+
// set channelmode on audio
|
1414 |
+
if (!empty($this->info['audio']['channelmode']) || !isset($this->info['audio']['channels'])) {
|
1415 |
+
// ignore
|
1416 |
+
} elseif ($this->info['audio']['channels'] == 1) {
|
1417 |
+
$this->info['audio']['channelmode'] = 'mono';
|
1418 |
+
} elseif ($this->info['audio']['channels'] == 2) {
|
1419 |
+
$this->info['audio']['channelmode'] = 'stereo';
|
1420 |
+
}
|
1421 |
+
|
1422 |
+
// Calculate combined bitrate - audio + video
|
1423 |
+
$CombinedBitrate = 0;
|
1424 |
+
$CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0);
|
1425 |
+
$CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0);
|
1426 |
+
if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) {
|
1427 |
+
$this->info['bitrate'] = $CombinedBitrate;
|
1428 |
+
}
|
1429 |
+
//if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) {
|
1430 |
+
// // for example, VBR MPEG video files cannot determine video bitrate:
|
1431 |
+
// // should not set overall bitrate and playtime from audio bitrate only
|
1432 |
+
// unset($this->info['bitrate']);
|
1433 |
+
//}
|
1434 |
+
|
1435 |
+
// video bitrate undetermined, but calculable
|
1436 |
+
if (isset($this->info['video']['dataformat']) && $this->info['video']['dataformat'] && (!isset($this->info['video']['bitrate']) || ($this->info['video']['bitrate'] == 0))) {
|
1437 |
+
// if video bitrate not set
|
1438 |
+
if (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] > 0) && ($this->info['audio']['bitrate'] == $this->info['bitrate'])) {
|
1439 |
+
// AND if audio bitrate is set to same as overall bitrate
|
1440 |
+
if (isset($this->info['playtime_seconds']) && ($this->info['playtime_seconds'] > 0)) {
|
1441 |
+
// AND if playtime is set
|
1442 |
+
if (isset($this->info['avdataend']) && isset($this->info['avdataoffset'])) {
|
1443 |
+
// AND if AV data offset start/end is known
|
1444 |
+
// THEN we can calculate the video bitrate
|
1445 |
+
$this->info['bitrate'] = round((($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']);
|
1446 |
+
$this->info['video']['bitrate'] = $this->info['bitrate'] - $this->info['audio']['bitrate'];
|
1447 |
+
}
|
1448 |
+
}
|
1449 |
+
}
|
1450 |
+
}
|
1451 |
+
|
1452 |
+
if ((!isset($this->info['playtime_seconds']) || ($this->info['playtime_seconds'] <= 0)) && !empty($this->info['bitrate'])) {
|
1453 |
+
$this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate'];
|
1454 |
+
}
|
1455 |
+
|
1456 |
+
if (!isset($this->info['bitrate']) && !empty($this->info['playtime_seconds'])) {
|
1457 |
+
$this->info['bitrate'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds'];
|
1458 |
+
}
|
1459 |
+
if (isset($this->info['bitrate']) && empty($this->info['audio']['bitrate']) && empty($this->info['video']['bitrate'])) {
|
1460 |
+
if (isset($this->info['audio']['dataformat']) && empty($this->info['video']['resolution_x'])) {
|
1461 |
+
// audio only
|
1462 |
+
$this->info['audio']['bitrate'] = $this->info['bitrate'];
|
1463 |
+
} elseif (isset($this->info['video']['resolution_x']) && empty($this->info['audio']['dataformat'])) {
|
1464 |
+
// video only
|
1465 |
+
$this->info['video']['bitrate'] = $this->info['bitrate'];
|
1466 |
+
}
|
1467 |
+
}
|
1468 |
+
|
1469 |
+
// Set playtime string
|
1470 |
+
if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) {
|
1471 |
+
$this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']);
|
1472 |
+
}
|
1473 |
+
}
|
1474 |
+
|
1475 |
+
|
1476 |
+
function CalculateCompressionRatioVideo() {
|
1477 |
+
if (empty($this->info['video'])) {
|
1478 |
+
return false;
|
1479 |
+
}
|
1480 |
+
if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) {
|
1481 |
+
return false;
|
1482 |
+
}
|
1483 |
+
if (empty($this->info['video']['bits_per_sample'])) {
|
1484 |
+
return false;
|
1485 |
+
}
|
1486 |
+
|
1487 |
+
switch ($this->info['video']['dataformat']) {
|
1488 |
+
case 'bmp':
|
1489 |
+
case 'gif':
|
1490 |
+
case 'jpeg':
|
1491 |
+
case 'jpg':
|
1492 |
+
case 'png':
|
1493 |
+
case 'tiff':
|
1494 |
+
$FrameRate = 1;
|
1495 |
+
$PlaytimeSeconds = 1;
|
1496 |
+
$BitrateCompressed = $this->info['filesize'] * 8;
|
1497 |
+
break;
|
1498 |
+
|
1499 |
+
default:
|
1500 |
+
if (!empty($this->info['video']['frame_rate'])) {
|
1501 |
+
$FrameRate = $this->info['video']['frame_rate'];
|
1502 |
+
} else {
|
1503 |
+
return false;
|
1504 |
+
}
|
1505 |
+
if (!empty($this->info['playtime_seconds'])) {
|
1506 |
+
$PlaytimeSeconds = $this->info['playtime_seconds'];
|
1507 |
+
} else {
|
1508 |
+
return false;
|
1509 |
+
}
|
1510 |
+
if (!empty($this->info['video']['bitrate'])) {
|
1511 |
+
$BitrateCompressed = $this->info['video']['bitrate'];
|
1512 |
+
} else {
|
1513 |
+
return false;
|
1514 |
+
}
|
1515 |
+
break;
|
1516 |
+
}
|
1517 |
+
$BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate;
|
1518 |
+
|
1519 |
+
$this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed;
|
1520 |
+
return true;
|
1521 |
+
}
|
1522 |
+
|
1523 |
+
|
1524 |
+
function CalculateCompressionRatioAudio() {
|
1525 |
+
if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate'])) {
|
1526 |
+
return false;
|
1527 |
+
}
|
1528 |
+
$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));
|
1529 |
+
|
1530 |
+
if (!empty($this->info['audio']['streams'])) {
|
1531 |
+
foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) {
|
1532 |
+
if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) {
|
1533 |
+
$this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16));
|
1534 |
+
}
|
1535 |
+
}
|
1536 |
+
}
|
1537 |
+
return true;
|
1538 |
+
}
|
1539 |
+
|
1540 |
+
|
1541 |
+
function CalculateReplayGain() {
|
1542 |
+
if (isset($this->info['replay_gain'])) {
|
1543 |
+
if (!isset($this->info['replay_gain']['reference_volume'])) {
|
1544 |
+
$this->info['replay_gain']['reference_volume'] = (double) 89.0;
|
1545 |
+
}
|
1546 |
+
if (isset($this->info['replay_gain']['track']['adjustment'])) {
|
1547 |
+
$this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment'];
|
1548 |
+
}
|
1549 |
+
if (isset($this->info['replay_gain']['album']['adjustment'])) {
|
1550 |
+
$this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment'];
|
1551 |
+
}
|
1552 |
+
|
1553 |
+
if (isset($this->info['replay_gain']['track']['peak'])) {
|
1554 |
+
$this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']);
|
1555 |
+
}
|
1556 |
+
if (isset($this->info['replay_gain']['album']['peak'])) {
|
1557 |
+
$this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']);
|
1558 |
+
}
|
1559 |
+
}
|
1560 |
+
return true;
|
1561 |
+
}
|
1562 |
+
|
1563 |
+
function ProcessAudioStreams() {
|
1564 |
+
if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) {
|
1565 |
+
if (!isset($this->info['audio']['streams'])) {
|
1566 |
+
foreach ($this->info['audio'] as $key => $value) {
|
1567 |
+
if ($key != 'streams') {
|
1568 |
+
$this->info['audio']['streams'][0][$key] = $value;
|
1569 |
+
}
|
1570 |
+
}
|
1571 |
+
}
|
1572 |
+
}
|
1573 |
+
return true;
|
1574 |
+
}
|
1575 |
+
|
1576 |
+
function getid3_tempnam() {
|
1577 |
+
return tempnam($this->tempdir, 'gI3');
|
1578 |
+
}
|
1579 |
+
|
1580 |
+
|
1581 |
+
public function saveAttachment(&$ThisFileInfoIndex, $filename, $offset, $length) {
|
1582 |
+
try {
|
1583 |
+
if (!getid3_lib::intValueSupported($offset + $length)) {
|
1584 |
+
throw new Exception('cannot extract attachment, it extends beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit');
|
1585 |
+
}
|
1586 |
+
|
1587 |
+
// do not extract at all
|
1588 |
+
if ($this->option_save_attachments === getID3::ATTACHMENTS_NONE) {
|
1589 |
+
|
1590 |
+
unset($ThisFileInfoIndex); // do not set any
|
1591 |
+
|
1592 |
+
// extract to return array
|
1593 |
+
} elseif ($this->option_save_attachments === getID3::ATTACHMENTS_INLINE) {
|
1594 |
+
|
1595 |
+
// get whole data in one pass, till it is anyway stored in memory
|
1596 |
+
$ThisFileInfoIndex = file_get_contents($this->info['filenamepath'], false, null, $offset, $length);
|
1597 |
+
if (($ThisFileInfoIndex === false) || (strlen($ThisFileInfoIndex) != $length)) { // verify
|
1598 |
+
throw new Exception('failed to read attachment data');
|
1599 |
+
}
|
1600 |
+
|
1601 |
+
// assume directory path is given
|
1602 |
+
} else {
|
1603 |
+
|
1604 |
+
$dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->option_save_attachments), DIRECTORY_SEPARATOR);
|
1605 |
+
// check supplied directory
|
1606 |
+
if (!is_dir($dir) || !is_writable($dir)) {
|
1607 |
+
throw new Exception('getID3::saveAttachment() -- supplied path ('.$dir.') does not exist, or is not writable');
|
1608 |
+
}
|
1609 |
+
|
1610 |
+
// set up destination path
|
1611 |
+
$dest = $dir.DIRECTORY_SEPARATOR.$filename;
|
1612 |
+
|
1613 |
+
// optimize speed if read buffer size is configured to be large enough
|
1614 |
+
// here stream_copy_to_stream() may also be used. need to do speed-compare tests
|
1615 |
+
if ($length <= $this->fread_buffer_size()) {
|
1616 |
+
$data = file_get_contents($this->info['filenamepath'], false, null, $offset, $length);
|
1617 |
+
if (($data === false) || (strlen($data) != $length)) { // verify
|
1618 |
+
throw new Exception('failed to read attachment data');
|
1619 |
+
}
|
1620 |
+
if (!file_put_contents($dest, $data)) {
|
1621 |
+
throw new Exception('failed to create file '.$dest);
|
1622 |
+
}
|
1623 |
+
} else {
|
1624 |
+
// optimization not available - copy data in loop
|
1625 |
+
// here stream_copy_to_stream() shouldn't be used because it's internal read buffer may be larger than ours!
|
1626 |
+
getid3_lib::CopyFileParts($this->info['filenamepath'], $dest, $offset, $length);
|
1627 |
+
}
|
1628 |
+
$ThisFileInfoIndex = $dest;
|
1629 |
+
}
|
1630 |
+
|
1631 |
+
} catch (Exception $e) {
|
1632 |
+
|
1633 |
+
unset($ThisFileInfoIndex); // do not set any is case of error
|
1634 |
+
$this->warning('Failed to extract attachment '.$filename.': '.$e->getMessage());
|
1635 |
+
return false;
|
1636 |
+
|
1637 |
+
}
|
1638 |
+
return true;
|
1639 |
+
}
|
1640 |
+
|
1641 |
+
|
1642 |
+
public function include_module($name) {
|
1643 |
+
//if (!file_exists($this->include_path.'module.'.$name.'.php')) {
|
1644 |
+
if (!file_exists(GETID3_INCLUDEPATH.'module.'.$name.'.php')) {
|
1645 |
+
throw new getid3_exception('Required module.'.$name.'.php is missing.');
|
1646 |
+
}
|
1647 |
+
include_once(GETID3_INCLUDEPATH.'module.'.$name.'.php');
|
1648 |
+
return true;
|
1649 |
+
}
|
1650 |
+
|
1651 |
+
}
|
1652 |
+
|
1653 |
+
|
1654 |
+
abstract class getid3_handler
|
1655 |
+
{
|
1656 |
+
protected $getid3; // pointer
|
1657 |
+
|
1658 |
+
protected $data_string_flag = false; // analyzing filepointer or string
|
1659 |
+
protected $data_string; // string to analyze
|
1660 |
+
protected $data_string_position = 0; // seek position in string
|
1661 |
+
|
1662 |
+
|
1663 |
+
public function __construct(getID3 $getid3) {
|
1664 |
+
$this->getid3 = $getid3;
|
1665 |
+
}
|
1666 |
+
|
1667 |
+
|
1668 |
+
// Analyze from file pointer
|
1669 |
+
abstract public function Analyze();
|
1670 |
+
|
1671 |
+
|
1672 |
+
// Analyze from string instead
|
1673 |
+
public function AnalyzeString(&$string) {
|
1674 |
+
// Enter string mode
|
1675 |
+
$this->data_string_flag = true;
|
1676 |
+
$this->data_string = $string;
|
1677 |
+
|
1678 |
+
// Save info
|
1679 |
+
$saved_avdataoffset = $this->getid3->info['avdataoffset'];
|
1680 |
+
$saved_avdataend = $this->getid3->info['avdataend'];
|
1681 |
+
$saved_filesize = $this->getid3->info['filesize'];
|
1682 |
+
|
1683 |
+
// Reset some info
|
1684 |
+
$this->getid3->info['avdataoffset'] = 0;
|
1685 |
+
$this->getid3->info['avdataend'] = $this->getid3->info['filesize'] = strlen($string);
|
1686 |
+
|
1687 |
+
// Analyze
|
1688 |
+
$this->Analyze();
|
1689 |
+
|
1690 |
+
// Restore some info
|
1691 |
+
$this->getid3->info['avdataoffset'] = $saved_avdataoffset;
|
1692 |
+
$this->getid3->info['avdataend'] = $saved_avdataend;
|
1693 |
+
$this->getid3->info['filesize'] = $saved_filesize;
|
1694 |
+
|
1695 |
+
// Exit string mode
|
1696 |
+
$this->data_string_flag = false;
|
1697 |
+
}
|
1698 |
+
|
1699 |
+
|
1700 |
+
protected function ftell() {
|
1701 |
+
if ($this->data_string_flag) {
|
1702 |
+
return $this->data_string_position;
|
1703 |
+
}
|
1704 |
+
return ftell($this->getid3->fp);
|
1705 |
+
}
|
1706 |
+
|
1707 |
+
|
1708 |
+
protected function fread($bytes) {
|
1709 |
+
if ($this->data_string_flag) {
|
1710 |
+
$this->data_string_position += $bytes;
|
1711 |
+
return substr($this->data_string, $this->data_string_position - $bytes, $bytes);
|
1712 |
+
}
|
1713 |
+
return fread($this->getid3->fp, $bytes);
|
1714 |
+
}
|
1715 |
+
|
1716 |
+
|
1717 |
+
protected function fseek($bytes, $whence = SEEK_SET) {
|
1718 |
+
if ($this->data_string_flag) {
|
1719 |
+
switch ($whence) {
|
1720 |
+
case SEEK_SET:
|
1721 |
+
$this->data_string_position = $bytes;
|
1722 |
+
return;
|
1723 |
+
|
1724 |
+
case SEEK_CUR:
|
1725 |
+
$this->data_string_position += $bytes;
|
1726 |
+
return;
|
1727 |
+
|
1728 |
+
case SEEK_END:
|
1729 |
+
$this->data_string_position = strlen($this->data_string) + $bytes;
|
1730 |
+
return;
|
1731 |
+
}
|
1732 |
+
}
|
1733 |
+
return fseek($this->getid3->fp, $bytes, $whence);
|
1734 |
+
}
|
1735 |
+
|
1736 |
+
}
|
1737 |
+
|
1738 |
+
|
1739 |
+
class getid3_exception extends Exception
|
1740 |
+
{
|
1741 |
+
public $message;
|
1742 |
+
}
|
1743 |
+
|
1744 |
+
?>
|
includes/lib/getid3/module.audio-video.mpeg.php
ADDED
@@ -0,0 +1,299 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
/////////////////////////////////////////////////////////////////
|
7 |
+
// See readme.txt for more details //
|
8 |
+
/////////////////////////////////////////////////////////////////
|
9 |
+
// //
|
10 |
+
// module.audio-video.mpeg.php //
|
11 |
+
// module for analyzing MPEG files //
|
12 |
+
// dependencies: module.audio.mp3.php //
|
13 |
+
// ///
|
14 |
+
/////////////////////////////////////////////////////////////////
|
15 |
+
|
16 |
+
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
|
17 |
+
|
18 |
+
define('GETID3_MPEG_VIDEO_PICTURE_START', "\x00\x00\x01\x00");
|
19 |
+
define('GETID3_MPEG_VIDEO_USER_DATA_START', "\x00\x00\x01\xB2");
|
20 |
+
define('GETID3_MPEG_VIDEO_SEQUENCE_HEADER', "\x00\x00\x01\xB3");
|
21 |
+
define('GETID3_MPEG_VIDEO_SEQUENCE_ERROR', "\x00\x00\x01\xB4");
|
22 |
+
define('GETID3_MPEG_VIDEO_EXTENSION_START', "\x00\x00\x01\xB5");
|
23 |
+
define('GETID3_MPEG_VIDEO_SEQUENCE_END', "\x00\x00\x01\xB7");
|
24 |
+
define('GETID3_MPEG_VIDEO_GROUP_START', "\x00\x00\x01\xB8");
|
25 |
+
define('GETID3_MPEG_AUDIO_START', "\x00\x00\x01\xC0");
|
26 |
+
|
27 |
+
|
28 |
+
class getid3_mpeg extends getid3_handler
|
29 |
+
{
|
30 |
+
|
31 |
+
function Analyze() {
|
32 |
+
$info = &$this->getid3->info;
|
33 |
+
|
34 |
+
if ($info['avdataend'] <= $info['avdataoffset']) {
|
35 |
+
$info['error'][] = '"avdataend" ('.$info['avdataend'].') is unexpectedly less-than-or-equal-to "avdataoffset" ('.$info['avdataoffset'].')';
|
36 |
+
return false;
|
37 |
+
}
|
38 |
+
$info['fileformat'] = 'mpeg';
|
39 |
+
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
40 |
+
$MPEGstreamData = fread($this->getid3->fp, min(100000, $info['avdataend'] - $info['avdataoffset']));
|
41 |
+
$MPEGstreamDataLength = strlen($MPEGstreamData);
|
42 |
+
|
43 |
+
$foundVideo = true;
|
44 |
+
$VideoChunkOffset = 0;
|
45 |
+
while (substr($MPEGstreamData, $VideoChunkOffset++, 4) !== GETID3_MPEG_VIDEO_SEQUENCE_HEADER) {
|
46 |
+
if ($VideoChunkOffset >= $MPEGstreamDataLength) {
|
47 |
+
$foundVideo = false;
|
48 |
+
break;
|
49 |
+
}
|
50 |
+
}
|
51 |
+
if ($foundVideo) {
|
52 |
+
|
53 |
+
// Start code 32 bits
|
54 |
+
// horizontal frame size 12 bits
|
55 |
+
// vertical frame size 12 bits
|
56 |
+
// pixel aspect ratio 4 bits
|
57 |
+
// frame rate 4 bits
|
58 |
+
// bitrate 18 bits
|
59 |
+
// marker bit 1 bit
|
60 |
+
// VBV buffer size 10 bits
|
61 |
+
// constrained parameter flag 1 bit
|
62 |
+
// intra quant. matrix flag 1 bit
|
63 |
+
// intra quant. matrix values 512 bits (present if matrix flag == 1)
|
64 |
+
// non-intra quant. matrix flag 1 bit
|
65 |
+
// non-intra quant. matrix values 512 bits (present if matrix flag == 1)
|
66 |
+
|
67 |
+
$info['video']['dataformat'] = 'mpeg';
|
68 |
+
|
69 |
+
$VideoChunkOffset += (strlen(GETID3_MPEG_VIDEO_SEQUENCE_HEADER) - 1);
|
70 |
+
|
71 |
+
$FrameSizeDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 3));
|
72 |
+
$VideoChunkOffset += 3;
|
73 |
+
|
74 |
+
$AspectRatioFrameRateDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 1));
|
75 |
+
$VideoChunkOffset += 1;
|
76 |
+
|
77 |
+
$assortedinformation = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 4));
|
78 |
+
$VideoChunkOffset += 4;
|
79 |
+
|
80 |
+
$info['mpeg']['video']['raw']['framesize_horizontal'] = ($FrameSizeDWORD & 0xFFF000) >> 12; // 12 bits for horizontal frame size
|
81 |
+
$info['mpeg']['video']['raw']['framesize_vertical'] = ($FrameSizeDWORD & 0x000FFF); // 12 bits for vertical frame size
|
82 |
+
$info['mpeg']['video']['raw']['pixel_aspect_ratio'] = ($AspectRatioFrameRateDWORD & 0xF0) >> 4;
|
83 |
+
$info['mpeg']['video']['raw']['frame_rate'] = ($AspectRatioFrameRateDWORD & 0x0F);
|
84 |
+
|
85 |
+
$info['mpeg']['video']['framesize_horizontal'] = $info['mpeg']['video']['raw']['framesize_horizontal'];
|
86 |
+
$info['mpeg']['video']['framesize_vertical'] = $info['mpeg']['video']['raw']['framesize_vertical'];
|
87 |
+
|
88 |
+
$info['mpeg']['video']['pixel_aspect_ratio'] = $this->MPEGvideoAspectRatioLookup($info['mpeg']['video']['raw']['pixel_aspect_ratio']);
|
89 |
+
$info['mpeg']['video']['pixel_aspect_ratio_text'] = $this->MPEGvideoAspectRatioTextLookup($info['mpeg']['video']['raw']['pixel_aspect_ratio']);
|
90 |
+
$info['mpeg']['video']['frame_rate'] = $this->MPEGvideoFramerateLookup($info['mpeg']['video']['raw']['frame_rate']);
|
91 |
+
|
92 |
+
$info['mpeg']['video']['raw']['bitrate'] = getid3_lib::Bin2Dec(substr($assortedinformation, 0, 18));
|
93 |
+
$info['mpeg']['video']['raw']['marker_bit'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 18, 1));
|
94 |
+
$info['mpeg']['video']['raw']['vbv_buffer_size'] = getid3_lib::Bin2Dec(substr($assortedinformation, 19, 10));
|
95 |
+
$info['mpeg']['video']['raw']['constrained_param_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 29, 1));
|
96 |
+
$info['mpeg']['video']['raw']['intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 30, 1));
|
97 |
+
if ($info['mpeg']['video']['raw']['intra_quant_flag']) {
|
98 |
+
|
99 |
+
// read 512 bits
|
100 |
+
$info['mpeg']['video']['raw']['intra_quant'] = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64));
|
101 |
+
$VideoChunkOffset += 64;
|
102 |
+
|
103 |
+
$info['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($info['mpeg']['video']['raw']['intra_quant'], 511, 1));
|
104 |
+
$info['mpeg']['video']['raw']['intra_quant'] = getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1)).substr(getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64)), 0, 511);
|
105 |
+
|
106 |
+
if ($info['mpeg']['video']['raw']['non_intra_quant_flag']) {
|
107 |
+
$info['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64);
|
108 |
+
$VideoChunkOffset += 64;
|
109 |
+
}
|
110 |
+
|
111 |
+
} else {
|
112 |
+
|
113 |
+
$info['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1));
|
114 |
+
if ($info['mpeg']['video']['raw']['non_intra_quant_flag']) {
|
115 |
+
$info['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64);
|
116 |
+
$VideoChunkOffset += 64;
|
117 |
+
}
|
118 |
+
|
119 |
+
}
|
120 |
+
|
121 |
+
if ($info['mpeg']['video']['raw']['bitrate'] == 0x3FFFF) { // 18 set bits
|
122 |
+
|
123 |
+
$info['warning'][] = 'This version of getID3() ['.$this->getid3->version().'] cannot determine average bitrate of VBR MPEG video files';
|
124 |
+
$info['mpeg']['video']['bitrate_mode'] = 'vbr';
|
125 |
+
|
126 |
+
} else {
|
127 |
+
|
128 |
+
$info['mpeg']['video']['bitrate'] = $info['mpeg']['video']['raw']['bitrate'] * 400;
|
129 |
+
$info['mpeg']['video']['bitrate_mode'] = 'cbr';
|
130 |
+
$info['video']['bitrate'] = $info['mpeg']['video']['bitrate'];
|
131 |
+
|
132 |
+
}
|
133 |
+
|
134 |
+
$info['video']['resolution_x'] = $info['mpeg']['video']['framesize_horizontal'];
|
135 |
+
$info['video']['resolution_y'] = $info['mpeg']['video']['framesize_vertical'];
|
136 |
+
$info['video']['frame_rate'] = $info['mpeg']['video']['frame_rate'];
|
137 |
+
$info['video']['bitrate_mode'] = $info['mpeg']['video']['bitrate_mode'];
|
138 |
+
$info['video']['pixel_aspect_ratio'] = $info['mpeg']['video']['pixel_aspect_ratio'];
|
139 |
+
$info['video']['lossless'] = false;
|
140 |
+
$info['video']['bits_per_sample'] = 24;
|
141 |
+
|
142 |
+
} else {
|
143 |
+
|
144 |
+
$info['error'][] = 'Could not find start of video block in the first 100,000 bytes (or before end of file) - this might not be an MPEG-video file?';
|
145 |
+
|
146 |
+
}
|
147 |
+
|
148 |
+
//0x000001B3 begins the sequence_header of every MPEG video stream.
|
149 |
+
//But in MPEG-2, this header must immediately be followed by an
|
150 |
+
//extension_start_code (0x000001B5) with a sequence_extension ID (1).
|
151 |
+
//(This extension contains all the additional MPEG-2 stuff.)
|
152 |
+
//MPEG-1 doesn't have this extension, so that's a sure way to tell the
|
153 |
+
//difference between MPEG-1 and MPEG-2 video streams.
|
154 |
+
|
155 |
+
if (substr($MPEGstreamData, $VideoChunkOffset, 4) == GETID3_MPEG_VIDEO_EXTENSION_START) {
|
156 |
+
$info['video']['codec'] = 'MPEG-2';
|
157 |
+
} else {
|
158 |
+
$info['video']['codec'] = 'MPEG-1';
|
159 |
+
}
|
160 |
+
|
161 |
+
|
162 |
+
$AudioChunkOffset = 0;
|
163 |
+
while (true) {
|
164 |
+
while (substr($MPEGstreamData, $AudioChunkOffset++, 4) !== GETID3_MPEG_AUDIO_START) {
|
165 |
+
if ($AudioChunkOffset >= $MPEGstreamDataLength) {
|
166 |
+
break 2;
|
167 |
+
}
|
168 |
+
}
|
169 |
+
|
170 |
+
$getid3_temp = new getID3();
|
171 |
+
$getid3_temp->openfile($this->getid3->filename);
|
172 |
+
$getid3_temp->info = $info;
|
173 |
+
$getid3_mp3 = new getid3_mp3($getid3_temp);
|
174 |
+
for ($i = 0; $i <= 7; $i++) {
|
175 |
+
// some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after
|
176 |
+
// I have no idea why or what the difference is, so this is a stupid hack.
|
177 |
+
// If anybody has any better idea of what's going on, please let me know - info@getid3.org
|
178 |
+
fseek($getid3_temp->fp, ftell($this->getid3->fp), SEEK_SET);
|
179 |
+
$getid3_temp->info = $info; // only overwrite real data if valid header found
|
180 |
+
if ($getid3_mp3->decodeMPEGaudioHeader(($AudioChunkOffset + 3) + 8 + $i, $getid3_temp->info, false)) {
|
181 |
+
$info = $getid3_temp->info;
|
182 |
+
$info['audio']['bitrate_mode'] = 'cbr';
|
183 |
+
$info['audio']['lossless'] = false;
|
184 |
+
unset($getid3_temp, $getid3_mp3);
|
185 |
+
break 2;
|
186 |
+
}
|
187 |
+
}
|
188 |
+
unset($getid3_temp, $getid3_mp3);
|
189 |
+
}
|
190 |
+
|
191 |
+
// Temporary hack to account for interleaving overhead:
|
192 |
+
if (!empty($info['video']['bitrate']) && !empty($info['audio']['bitrate'])) {
|
193 |
+
$info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($info['video']['bitrate'] + $info['audio']['bitrate']);
|
194 |
+
|
195 |
+
// Interleaved MPEG audio/video files have a certain amount of overhead that varies
|
196 |
+
// by both video and audio bitrates, and not in any sensible, linear/logarithmic patter
|
197 |
+
// Use interpolated lookup tables to approximately guess how much is overhead, because
|
198 |
+
// playtime is calculated as filesize / total-bitrate
|
199 |
+
$info['playtime_seconds'] *= $this->MPEGsystemNonOverheadPercentage($info['video']['bitrate'], $info['audio']['bitrate']);
|
200 |
+
|
201 |
+
//switch ($info['video']['bitrate']) {
|
202 |
+
// case('5000000'):
|
203 |
+
// $multiplier = 0.93292642112380355828048824319889;
|
204 |
+
// break;
|
205 |
+
// case('5500000'):
|
206 |
+
// $multiplier = 0.93582895375200989965359777343219;
|
207 |
+
// break;
|
208 |
+
// case('6000000'):
|
209 |
+
// $multiplier = 0.93796247714820932532911373859139;
|
210 |
+
// break;
|
211 |
+
// case('7000000'):
|
212 |
+
// $multiplier = 0.9413264083635103463010117778776;
|
213 |
+
// break;
|
214 |
+
// default:
|
215 |
+
// $multiplier = 1;
|
216 |
+
// break;
|
217 |
+
//}
|
218 |
+
//$info['playtime_seconds'] *= $multiplier;
|
219 |
+
//$info['warning'][] = 'Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.';
|
220 |
+
if ($info['video']['bitrate'] < 50000) {
|
221 |
+
$info['warning'][] = 'Interleaved MPEG audio/video playtime may be slightly inaccurate for video bitrates below 100kbps. Except in extreme low-bitrate situations, error should be less than 1%. Report to info@getid3.org if greater than this.';
|
222 |
+
}
|
223 |
+
}
|
224 |
+
|
225 |
+
return true;
|
226 |
+
}
|
227 |
+
|
228 |
+
|
229 |
+
function MPEGsystemNonOverheadPercentage($VideoBitrate, $AudioBitrate) {
|
230 |
+
$OverheadPercentage = 0;
|
231 |
+
|
232 |
+
$AudioBitrate = max(min($AudioBitrate / 1000, 384), 32); // limit to range of 32kbps - 384kbps (should be only legal bitrates, but maybe VBR?)
|
233 |
+
$VideoBitrate = max(min($VideoBitrate / 1000, 10000), 10); // limit to range of 10kbps - 10Mbps (beyond that curves flatten anyways, no big loss)
|
234 |
+
|
235 |
+
|
236 |
+
//OMBB[audiobitrate] = array(video-10kbps, video-100kbps, video-1000kbps, video-10000kbps)
|
237 |
+
$OverheadMultiplierByBitrate[32] = array(0, 0.9676287944368530, 0.9802276264360310, 0.9844916183244460, 0.9852821845179940);
|
238 |
+
$OverheadMultiplierByBitrate[48] = array(0, 0.9779100089209830, 0.9787770035359320, 0.9846738664076130, 0.9852683013799960);
|
239 |
+
$OverheadMultiplierByBitrate[56] = array(0, 0.9731249855367600, 0.9776624308938040, 0.9832606361852130, 0.9843922606633340);
|
240 |
+
$OverheadMultiplierByBitrate[64] = array(0, 0.9755642683275760, 0.9795256705493390, 0.9836573009193170, 0.9851122539404470);
|
241 |
+
$OverheadMultiplierByBitrate[96] = array(0, 0.9788025247497290, 0.9798553314148700, 0.9822956869792560, 0.9834815119124690);
|
242 |
+
$OverheadMultiplierByBitrate[128] = array(0, 0.9816940050925480, 0.9821675936072120, 0.9829756927470870, 0.9839763420152050);
|
243 |
+
$OverheadMultiplierByBitrate[160] = array(0, 0.9825894094561180, 0.9820913399073960, 0.9823907143253970, 0.9832821783651570);
|
244 |
+
$OverheadMultiplierByBitrate[192] = array(0, 0.9832038474336260, 0.9825731694317960, 0.9821028622712400, 0.9828262076447620);
|
245 |
+
$OverheadMultiplierByBitrate[224] = array(0, 0.9836516298538770, 0.9824718601823890, 0.9818302180625380, 0.9823735101626480);
|
246 |
+
$OverheadMultiplierByBitrate[256] = array(0, 0.9845863022094920, 0.9837229411967540, 0.9824521662210830, 0.9828645172100790);
|
247 |
+
$OverheadMultiplierByBitrate[320] = array(0, 0.9849565280263180, 0.9837683142805110, 0.9822885275960400, 0.9824424382727190);
|
248 |
+
$OverheadMultiplierByBitrate[384] = array(0, 0.9856094774357600, 0.9844573394432720, 0.9825970399837330, 0.9824673808303890);
|
249 |
+
|
250 |
+
$BitrateToUseMin = 32;
|
251 |
+
$BitrateToUseMax = 32;
|
252 |
+
$previousBitrate = 32;
|
253 |
+
foreach ($OverheadMultiplierByBitrate as $key => $value) {
|
254 |
+
if ($AudioBitrate >= $previousBitrate) {
|
255 |
+
$BitrateToUseMin = $previousBitrate;
|
256 |
+
}
|
257 |
+
if ($AudioBitrate < $key) {
|
258 |
+
$BitrateToUseMax = $key;
|
259 |
+
break;
|
260 |
+
}
|
261 |
+
$previousBitrate = $key;
|
262 |
+
}
|
263 |
+
$FactorA = ($BitrateToUseMax - $AudioBitrate) / ($BitrateToUseMax - $BitrateToUseMin);
|
264 |
+
|
265 |
+
$VideoBitrateLog10 = log10($VideoBitrate);
|
266 |
+
$VideoFactorMin1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][floor($VideoBitrateLog10)];
|
267 |
+
$VideoFactorMin2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][floor($VideoBitrateLog10)];
|
268 |
+
$VideoFactorMax1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][ceil($VideoBitrateLog10)];
|
269 |
+
$VideoFactorMax2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][ceil($VideoBitrateLog10)];
|
270 |
+
$FactorV = $VideoBitrateLog10 - floor($VideoBitrateLog10);
|
271 |
+
|
272 |
+
$OverheadPercentage = $VideoFactorMin1 * $FactorA * $FactorV;
|
273 |
+
$OverheadPercentage += $VideoFactorMin2 * (1 - $FactorA) * $FactorV;
|
274 |
+
$OverheadPercentage += $VideoFactorMax1 * $FactorA * (1 - $FactorV);
|
275 |
+
$OverheadPercentage += $VideoFactorMax2 * (1 - $FactorA) * (1 - $FactorV);
|
276 |
+
|
277 |
+
return $OverheadPercentage;
|
278 |
+
}
|
279 |
+
|
280 |
+
|
281 |
+
function MPEGvideoFramerateLookup($rawframerate) {
|
282 |
+
$MPEGvideoFramerateLookup = array(0, 23.976, 24, 25, 29.97, 30, 50, 59.94, 60);
|
283 |
+
return (isset($MPEGvideoFramerateLookup[$rawframerate]) ? (float) $MPEGvideoFramerateLookup[$rawframerate] : (float) 0);
|
284 |
+
}
|
285 |
+
|
286 |
+
function MPEGvideoAspectRatioLookup($rawaspectratio) {
|
287 |
+
$MPEGvideoAspectRatioLookup = array(0, 1, 0.6735, 0.7031, 0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695, 1.0950, 1.1575, 1.2015, 0);
|
288 |
+
return (isset($MPEGvideoAspectRatioLookup[$rawaspectratio]) ? (float) $MPEGvideoAspectRatioLookup[$rawaspectratio] : (float) 0);
|
289 |
+
}
|
290 |
+
|
291 |
+
function MPEGvideoAspectRatioTextLookup($rawaspectratio) {
|
292 |
+
$MPEGvideoAspectRatioTextLookup = array('forbidden', 'square pixels', '0.6735', '16:9, 625 line, PAL', '0.7615', '0.8055', '16:9, 525 line, NTSC', '0.8935', '4:3, 625 line, PAL, CCIR601', '0.9815', '1.0255', '1.0695', '4:3, 525 line, NTSC, CCIR601', '1.1575', '1.2015', 'reserved');
|
293 |
+
return (isset($MPEGvideoAspectRatioTextLookup[$rawaspectratio]) ? $MPEGvideoAspectRatioTextLookup[$rawaspectratio] : '');
|
294 |
+
}
|
295 |
+
|
296 |
+
}
|
297 |
+
|
298 |
+
|
299 |
+
?>
|
includes/lib/getid3/module.audio-video.quicktime.php
ADDED
@@ -0,0 +1,2134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
/////////////////////////////////////////////////////////////////
|
7 |
+
// See readme.txt for more details //
|
8 |
+
/////////////////////////////////////////////////////////////////
|
9 |
+
// //
|
10 |
+
// module.audio-video.quicktime.php //
|
11 |
+
// module for analyzing Quicktime and MP3-in-MP4 files //
|
12 |
+
// dependencies: module.audio.mp3.php //
|
13 |
+
// ///
|
14 |
+
/////////////////////////////////////////////////////////////////
|
15 |
+
|
16 |
+
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
|
17 |
+
|
18 |
+
class getid3_quicktime extends getid3_handler
|
19 |
+
{
|
20 |
+
|
21 |
+
var $ReturnAtomData = true;
|
22 |
+
var $ParseAllPossibleAtoms = false;
|
23 |
+
|
24 |
+
function Analyze() {
|
25 |
+
$info = &$this->getid3->info;
|
26 |
+
|
27 |
+
$info['fileformat'] = 'quicktime';
|
28 |
+
$info['quicktime']['hinting'] = false;
|
29 |
+
$info['quicktime']['controller'] = 'standard'; // may be overridden if 'ctyp' atom is present
|
30 |
+
|
31 |
+
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
32 |
+
|
33 |
+
$offset = 0;
|
34 |
+
$atomcounter = 0;
|
35 |
+
|
36 |
+
while ($offset < $info['avdataend']) {
|
37 |
+
if (!getid3_lib::intValueSupported($offset)) {
|
38 |
+
$info['error'][] = 'Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions';
|
39 |
+
break;
|
40 |
+
}
|
41 |
+
fseek($this->getid3->fp, $offset, SEEK_SET);
|
42 |
+
$AtomHeader = fread($this->getid3->fp, 8);
|
43 |
+
|
44 |
+
$atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4));
|
45 |
+
$atomname = substr($AtomHeader, 4, 4);
|
46 |
+
|
47 |
+
// 64-bit MOV patch by jlegate�ktnc*com
|
48 |
+
if ($atomsize == 1) {
|
49 |
+
$atomsize = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 8));
|
50 |
+
}
|
51 |
+
|
52 |
+
$info['quicktime'][$atomname]['name'] = $atomname;
|
53 |
+
$info['quicktime'][$atomname]['size'] = $atomsize;
|
54 |
+
$info['quicktime'][$atomname]['offset'] = $offset;
|
55 |
+
|
56 |
+
if (($offset + $atomsize) > $info['avdataend']) {
|
57 |
+
$info['error'][] = 'Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)';
|
58 |
+
return false;
|
59 |
+
}
|
60 |
+
|
61 |
+
if ($atomsize == 0) {
|
62 |
+
// Furthermore, for historical reasons the list of atoms is optionally
|
63 |
+
// terminated by a 32-bit integer set to 0. If you are writing a program
|
64 |
+
// to read user data atoms, you should allow for the terminating 0.
|
65 |
+
break;
|
66 |
+
}
|
67 |
+
switch ($atomname) {
|
68 |
+
case 'mdat': // Media DATa atom
|
69 |
+
// 'mdat' contains the actual data for the audio/video
|
70 |
+
if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) {
|
71 |
+
|
72 |
+
$info['avdataoffset'] = $info['quicktime'][$atomname]['offset'] + 8;
|
73 |
+
$OldAVDataEnd = $info['avdataend'];
|
74 |
+
$info['avdataend'] = $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size'];
|
75 |
+
|
76 |
+
$getid3_temp = new getID3();
|
77 |
+
$getid3_temp->openfile($this->getid3->filename);
|
78 |
+
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
|
79 |
+
$getid3_temp->info['avdataend'] = $info['avdataend'];
|
80 |
+
$getid3_mp3 = new getid3_mp3($getid3_temp);
|
81 |
+
if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode(fread($this->getid3->fp, 4)))) {
|
82 |
+
$getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
|
83 |
+
if (!empty($getid3_temp->info['warning'])) {
|
84 |
+
foreach ($getid3_temp->info['warning'] as $value) {
|
85 |
+
$info['warning'][] = $value;
|
86 |
+
}
|
87 |
+
}
|
88 |
+
if (!empty($getid3_temp->info['mpeg'])) {
|
89 |
+
$info['mpeg'] = $getid3_temp->info['mpeg'];
|
90 |
+
if (isset($info['mpeg']['audio'])) {
|
91 |
+
$info['audio']['dataformat'] = 'mp3';
|
92 |
+
$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')));
|
93 |
+
$info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
|
94 |
+
$info['audio']['channels'] = $info['mpeg']['audio']['channels'];
|
95 |
+
$info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
|
96 |
+
$info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
|
97 |
+
$info['bitrate'] = $info['audio']['bitrate'];
|
98 |
+
}
|
99 |
+
}
|
100 |
+
}
|
101 |
+
unset($getid3_mp3, $getid3_temp);
|
102 |
+
$info['avdataend'] = $OldAVDataEnd;
|
103 |
+
unset($OldAVDataEnd);
|
104 |
+
|
105 |
+
}
|
106 |
+
break;
|
107 |
+
|
108 |
+
case 'free': // FREE space atom
|
109 |
+
case 'skip': // SKIP atom
|
110 |
+
case 'wide': // 64-bit expansion placeholder atom
|
111 |
+
// 'free', 'skip' and 'wide' are just padding, contains no useful data at all
|
112 |
+
break;
|
113 |
+
|
114 |
+
default:
|
115 |
+
$atomHierarchy = array();
|
116 |
+
$info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, fread($this->getid3->fp, $atomsize), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
|
117 |
+
break;
|
118 |
+
}
|
119 |
+
|
120 |
+
$offset += $atomsize;
|
121 |
+
$atomcounter++;
|
122 |
+
}
|
123 |
+
|
124 |
+
if (!empty($info['avdataend_tmp'])) {
|
125 |
+
// this value is assigned to a temp value and then erased because
|
126 |
+
// otherwise any atoms beyond the 'mdat' atom would not get parsed
|
127 |
+
$info['avdataend'] = $info['avdataend_tmp'];
|
128 |
+
unset($info['avdataend_tmp']);
|
129 |
+
}
|
130 |
+
|
131 |
+
if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) {
|
132 |
+
$info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
133 |
+
}
|
134 |
+
if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info['quicktime']['video'])) {
|
135 |
+
$info['audio']['bitrate'] = $info['bitrate'];
|
136 |
+
}
|
137 |
+
if (!empty($info['playtime_seconds']) && !isset($info['video']['frame_rate']) && !empty($info['quicktime']['stts_framecount'])) {
|
138 |
+
foreach ($info['quicktime']['stts_framecount'] as $key => $samples_count) {
|
139 |
+
$samples_per_second = $samples_count / $info['playtime_seconds'];
|
140 |
+
if ($samples_per_second > 240) {
|
141 |
+
// has to be audio samples
|
142 |
+
} else {
|
143 |
+
$info['video']['frame_rate'] = $samples_per_second;
|
144 |
+
break;
|
145 |
+
}
|
146 |
+
}
|
147 |
+
}
|
148 |
+
if (($info['audio']['dataformat'] == 'mp4') && empty($info['video']['resolution_x'])) {
|
149 |
+
$info['fileformat'] = 'mp4';
|
150 |
+
$info['mime_type'] = 'audio/mp4';
|
151 |
+
unset($info['video']['dataformat']);
|
152 |
+
}
|
153 |
+
|
154 |
+
if (!$this->ReturnAtomData) {
|
155 |
+
unset($info['quicktime']['moov']);
|
156 |
+
}
|
157 |
+
|
158 |
+
if (empty($info['audio']['dataformat']) && !empty($info['quicktime']['audio'])) {
|
159 |
+
$info['audio']['dataformat'] = 'quicktime';
|
160 |
+
}
|
161 |
+
if (empty($info['video']['dataformat']) && !empty($info['quicktime']['video'])) {
|
162 |
+
$info['video']['dataformat'] = 'quicktime';
|
163 |
+
}
|
164 |
+
|
165 |
+
return true;
|
166 |
+
}
|
167 |
+
|
168 |
+
function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
|
169 |
+
// http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm
|
170 |
+
|
171 |
+
$info = &$this->getid3->info;
|
172 |
+
|
173 |
+
$atom_parent = array_pop($atomHierarchy);
|
174 |
+
array_push($atomHierarchy, $atomname);
|
175 |
+
$atom_structure['hierarchy'] = implode(' ', $atomHierarchy);
|
176 |
+
$atom_structure['name'] = $atomname;
|
177 |
+
$atom_structure['size'] = $atomsize;
|
178 |
+
$atom_structure['offset'] = $baseoffset;
|
179 |
+
//echo getid3_lib::PrintHexBytes(substr($atom_data, 0, 8)).'<br>';
|
180 |
+
//echo getid3_lib::PrintHexBytes(substr($atom_data, 0, 8), false).'<br><br>';
|
181 |
+
switch ($atomname) {
|
182 |
+
case 'moov': // MOVie container atom
|
183 |
+
case 'trak': // TRAcK container atom
|
184 |
+
case 'clip': // CLIPping container atom
|
185 |
+
case 'matt': // track MATTe container atom
|
186 |
+
case 'edts': // EDiTS container atom
|
187 |
+
case 'tref': // Track REFerence container atom
|
188 |
+
case 'mdia': // MeDIA container atom
|
189 |
+
case 'minf': // Media INFormation container atom
|
190 |
+
case 'dinf': // Data INFormation container atom
|
191 |
+
case 'udta': // User DaTA container atom
|
192 |
+
case 'cmov': // Compressed MOVie container atom
|
193 |
+
case 'rmra': // Reference Movie Record Atom
|
194 |
+
case 'rmda': // Reference Movie Descriptor Atom
|
195 |
+
case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR)
|
196 |
+
$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
|
197 |
+
break;
|
198 |
+
|
199 |
+
case 'ilst': // Item LiST container atom
|
200 |
+
$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
|
201 |
+
|
202 |
+
// some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted
|
203 |
+
$allnumericnames = true;
|
204 |
+
foreach ($atom_structure['subatoms'] as $subatomarray) {
|
205 |
+
if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) {
|
206 |
+
$allnumericnames = false;
|
207 |
+
break;
|
208 |
+
}
|
209 |
+
}
|
210 |
+
if ($allnumericnames) {
|
211 |
+
$newData = array();
|
212 |
+
foreach ($atom_structure['subatoms'] as $subatomarray) {
|
213 |
+
foreach ($subatomarray['subatoms'] as $newData_subatomarray) {
|
214 |
+
unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']);
|
215 |
+
$newData[$subatomarray['name']] = $newData_subatomarray;
|
216 |
+
break;
|
217 |
+
}
|
218 |
+
}
|
219 |
+
$atom_structure['data'] = $newData;
|
220 |
+
unset($atom_structure['subatoms']);
|
221 |
+
}
|
222 |
+
break;
|
223 |
+
|
224 |
+
case "\x00\x00\x00\x01":
|
225 |
+
case "\x00\x00\x00\x02":
|
226 |
+
case "\x00\x00\x00\x03":
|
227 |
+
case "\x00\x00\x00\x04":
|
228 |
+
case "\x00\x00\x00\x05":
|
229 |
+
$atomname = getid3_lib::BigEndian2Int($atomname);
|
230 |
+
$atom_structure['name'] = $atomname;
|
231 |
+
$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
|
232 |
+
break;
|
233 |
+
|
234 |
+
case 'stbl': // Sample TaBLe container atom
|
235 |
+
$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
|
236 |
+
$isVideo = false;
|
237 |
+
$framerate = 0;
|
238 |
+
$framecount = 0;
|
239 |
+
foreach ($atom_structure['subatoms'] as $key => $value_array) {
|
240 |
+
if (isset($value_array['sample_description_table'])) {
|
241 |
+
foreach ($value_array['sample_description_table'] as $key2 => $value_array2) {
|
242 |
+
if (isset($value_array2['data_format'])) {
|
243 |
+
switch ($value_array2['data_format']) {
|
244 |
+
case 'avc1':
|
245 |
+
case 'mp4v':
|
246 |
+
// video data
|
247 |
+
$isVideo = true;
|
248 |
+
break;
|
249 |
+
case 'mp4a':
|
250 |
+
// audio data
|
251 |
+
break;
|
252 |
+
}
|
253 |
+
}
|
254 |
+
}
|
255 |
+
} elseif (isset($value_array['time_to_sample_table'])) {
|
256 |
+
foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
|
257 |
+
if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) {
|
258 |
+
$framerate = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3);
|
259 |
+
$framecount = $value_array2['sample_count'];
|
260 |
+
}
|
261 |
+
}
|
262 |
+
}
|
263 |
+
}
|
264 |
+
if ($isVideo && $framerate) {
|
265 |
+
$info['quicktime']['video']['frame_rate'] = $framerate;
|
266 |
+
$info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate'];
|
267 |
+
}
|
268 |
+
if ($isVideo && $framecount) {
|
269 |
+
$info['quicktime']['video']['frame_count'] = $framecount;
|
270 |
+
}
|
271 |
+
break;
|
272 |
+
|
273 |
+
|
274 |
+
case 'aART': // Album ARTist
|
275 |
+
case 'catg': // CaTeGory
|
276 |
+
case 'covr': // COVeR artwork
|
277 |
+
case 'cpil': // ComPILation
|
278 |
+
case 'cprt': // CoPyRighT
|
279 |
+
case 'desc': // DESCription
|
280 |
+
case 'disk': // DISK number
|
281 |
+
case 'egid': // Episode Global ID
|
282 |
+
case 'gnre': // GeNRE
|
283 |
+
case 'keyw': // KEYWord
|
284 |
+
case 'ldes':
|
285 |
+
case 'pcst': // PodCaST
|
286 |
+
case 'pgap': // GAPless Playback
|
287 |
+
case 'purd': // PURchase Date
|
288 |
+
case 'purl': // Podcast URL
|
289 |
+
case 'rati':
|
290 |
+
case 'rndu':
|
291 |
+
case 'rpdu':
|
292 |
+
case 'rtng': // RaTiNG
|
293 |
+
case 'stik':
|
294 |
+
case 'tmpo': // TeMPO (BPM)
|
295 |
+
case 'trkn': // TRacK Number
|
296 |
+
case 'tves': // TV EpiSode
|
297 |
+
case 'tvnn': // TV Network Name
|
298 |
+
case 'tvsh': // TV SHow Name
|
299 |
+
case 'tvsn': // TV SeasoN
|
300 |
+
case 'akID': // iTunes store account type
|
301 |
+
case 'apID':
|
302 |
+
case 'atID':
|
303 |
+
case 'cmID':
|
304 |
+
case 'cnID':
|
305 |
+
case 'geID':
|
306 |
+
case 'plID':
|
307 |
+
case 'sfID': // iTunes store country
|
308 |
+
case '�alb': // ALBum
|
309 |
+
case '�art': // ARTist
|
310 |
+
case '�ART':
|
311 |
+
case '�aut':
|
312 |
+
case '�cmt': // CoMmenT
|
313 |
+
case '�com': // COMposer
|
314 |
+
case '�cpy':
|
315 |
+
case '�day': // content created year
|
316 |
+
case '�dir':
|
317 |
+
case '�ed1':
|
318 |
+
case '�ed2':
|
319 |
+
case '�ed3':
|
320 |
+
case '�ed4':
|
321 |
+
case '�ed5':
|
322 |
+
case '�ed6':
|
323 |
+
case '�ed7':
|
324 |
+
case '�ed8':
|
325 |
+
case '�ed9':
|
326 |
+
case '�enc':
|
327 |
+
case '�fmt':
|
328 |
+
case '�gen': // GENre
|
329 |
+
case '�grp': // GRouPing
|
330 |
+
case '�hst':
|
331 |
+
case '�inf':
|
332 |
+
case '�lyr': // LYRics
|
333 |
+
case '�mak':
|
334 |
+
case '�mod':
|
335 |
+
case '�nam': // full NAMe
|
336 |
+
case '�ope':
|
337 |
+
case '�PRD':
|
338 |
+
case '�prd':
|
339 |
+
case '�prf':
|
340 |
+
case '�req':
|
341 |
+
case '�src':
|
342 |
+
case '�swr':
|
343 |
+
case '�too': // encoder
|
344 |
+
case '�trk': // TRacK
|
345 |
+
case '�url':
|
346 |
+
case '�wrn':
|
347 |
+
case '�wrt': // WRiTer
|
348 |
+
case '----': // itunes specific
|
349 |
+
if ($atom_parent == 'udta') {
|
350 |
+
// User data atom handler
|
351 |
+
$atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
|
352 |
+
$atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
|
353 |
+
$atom_structure['data'] = substr($atom_data, 4);
|
354 |
+
|
355 |
+
$atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
|
356 |
+
if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
|
357 |
+
$info['comments']['language'][] = $atom_structure['language'];
|
358 |
+
}
|
359 |
+
} else {
|
360 |
+
// Apple item list box atom handler
|
361 |
+
$atomoffset = 0;
|
362 |
+
if (substr($atom_data, 2, 2) == "\x10\xB5") {
|
363 |
+
// not sure what it means, but observed on iPhone4 data.
|
364 |
+
// Each $atom_data has 2 bytes of datasize, plus 0x10B5, then data
|
365 |
+
while ($atomoffset < strlen($atom_data)) {
|
366 |
+
$boxsmallsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 2));
|
367 |
+
$boxsmalltype = substr($atom_data, $atomoffset + 2, 2);
|
368 |
+
$boxsmalldata = substr($atom_data, $atomoffset + 4, $boxsmallsize);
|
369 |
+
switch ($boxsmalltype) {
|
370 |
+
case "\x10\xB5":
|
371 |
+
$atom_structure['data'] = $boxsmalldata;
|
372 |
+
break;
|
373 |
+
default:
|
374 |
+
$info['warning'][] = 'Unknown QuickTime smallbox type: "'.getid3_lib::PrintHexBytes($boxsmalltype).'" at offset '.$baseoffset;
|
375 |
+
$atom_structure['data'] = $atom_data;
|
376 |
+
break;
|
377 |
+
}
|
378 |
+
$atomoffset += (4 + $boxsmallsize);
|
379 |
+
}
|
380 |
+
} else {
|
381 |
+
while ($atomoffset < strlen($atom_data)) {
|
382 |
+
$boxsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 4));
|
383 |
+
$boxtype = substr($atom_data, $atomoffset + 4, 4);
|
384 |
+
$boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8);
|
385 |
+
|
386 |
+
switch ($boxtype) {
|
387 |
+
case 'mean':
|
388 |
+
case 'name':
|
389 |
+
$atom_structure[$boxtype] = substr($boxdata, 4);
|
390 |
+
break;
|
391 |
+
|
392 |
+
case 'data':
|
393 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($boxdata, 0, 1));
|
394 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata, 1, 3));
|
395 |
+
switch ($atom_structure['flags_raw']) {
|
396 |
+
case 0: // data flag
|
397 |
+
case 21: // tmpo/cpil flag
|
398 |
+
switch ($atomname) {
|
399 |
+
case 'cpil':
|
400 |
+
case 'pcst':
|
401 |
+
case 'pgap':
|
402 |
+
$atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
|
403 |
+
break;
|
404 |
+
|
405 |
+
case 'tmpo':
|
406 |
+
$atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2));
|
407 |
+
break;
|
408 |
+
|
409 |
+
case 'disk':
|
410 |
+
case 'trkn':
|
411 |
+
$num = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2));
|
412 |
+
$num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2));
|
413 |
+
$atom_structure['data'] = empty($num) ? '' : $num;
|
414 |
+
$atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total;
|
415 |
+
break;
|
416 |
+
|
417 |
+
case 'gnre':
|
418 |
+
$GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
|
419 |
+
$atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1);
|
420 |
+
break;
|
421 |
+
|
422 |
+
case 'rtng':
|
423 |
+
$atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
|
424 |
+
$atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]);
|
425 |
+
break;
|
426 |
+
|
427 |
+
case 'stik':
|
428 |
+
$atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
|
429 |
+
$atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]);
|
430 |
+
break;
|
431 |
+
|
432 |
+
case 'sfID':
|
433 |
+
$atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
|
434 |
+
$atom_structure['data'] = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]);
|
435 |
+
break;
|
436 |
+
|
437 |
+
case 'egid':
|
438 |
+
case 'purl':
|
439 |
+
$atom_structure['data'] = substr($boxdata, 8);
|
440 |
+
break;
|
441 |
+
|
442 |
+
default:
|
443 |
+
$atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
|
444 |
+
}
|
445 |
+
break;
|
446 |
+
|
447 |
+
case 1: // text flag
|
448 |
+
case 13: // image flag
|
449 |
+
default:
|
450 |
+
$atom_structure['data'] = substr($boxdata, 8);
|
451 |
+
break;
|
452 |
+
|
453 |
+
}
|
454 |
+
break;
|
455 |
+
|
456 |
+
default:
|
457 |
+
$info['warning'][] = 'Unknown QuickTime box type: "'.getid3_lib::PrintHexBytes($boxtype).'" at offset '.$baseoffset;
|
458 |
+
$atom_structure['data'] = $atom_data;
|
459 |
+
|
460 |
+
}
|
461 |
+
$atomoffset += $boxsize;
|
462 |
+
}
|
463 |
+
}
|
464 |
+
}
|
465 |
+
$this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']);
|
466 |
+
break;
|
467 |
+
|
468 |
+
|
469 |
+
case 'play': // auto-PLAY atom
|
470 |
+
$atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
471 |
+
|
472 |
+
$info['quicktime']['autoplay'] = $atom_structure['autoplay'];
|
473 |
+
break;
|
474 |
+
|
475 |
+
|
476 |
+
case 'WLOC': // Window LOCation atom
|
477 |
+
$atom_structure['location_x'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
|
478 |
+
$atom_structure['location_y'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
|
479 |
+
break;
|
480 |
+
|
481 |
+
|
482 |
+
case 'LOOP': // LOOPing atom
|
483 |
+
case 'SelO': // play SELection Only atom
|
484 |
+
case 'AllF': // play ALL Frames atom
|
485 |
+
$atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data);
|
486 |
+
break;
|
487 |
+
|
488 |
+
|
489 |
+
case 'name': //
|
490 |
+
case 'MCPS': // Media Cleaner PRo
|
491 |
+
case '@PRM': // adobe PReMiere version
|
492 |
+
case '@PRQ': // adobe PRemiere Quicktime version
|
493 |
+
$atom_structure['data'] = $atom_data;
|
494 |
+
break;
|
495 |
+
|
496 |
+
|
497 |
+
case 'cmvd': // Compressed MooV Data atom
|
498 |
+
// Code by ubergeek�ubergeek*tv based on information from
|
499 |
+
// http://developer.apple.com/quicktime/icefloe/dispatch012.html
|
500 |
+
$atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
|
501 |
+
|
502 |
+
$CompressedFileData = substr($atom_data, 4);
|
503 |
+
if ($UncompressedHeader = @gzuncompress($CompressedFileData)) {
|
504 |
+
$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms);
|
505 |
+
} else {
|
506 |
+
$info['warning'][] = 'Error decompressing compressed MOV atom at offset '.$atom_structure['offset'];
|
507 |
+
}
|
508 |
+
break;
|
509 |
+
|
510 |
+
|
511 |
+
case 'dcom': // Data COMpression atom
|
512 |
+
$atom_structure['compression_id'] = $atom_data;
|
513 |
+
$atom_structure['compression_text'] = $this->QuicktimeDCOMLookup($atom_data);
|
514 |
+
break;
|
515 |
+
|
516 |
+
|
517 |
+
case 'rdrf': // Reference movie Data ReFerence atom
|
518 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
519 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
|
520 |
+
$atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x000001);
|
521 |
+
|
522 |
+
$atom_structure['reference_type_name'] = substr($atom_data, 4, 4);
|
523 |
+
$atom_structure['reference_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
|
524 |
+
switch ($atom_structure['reference_type_name']) {
|
525 |
+
case 'url ':
|
526 |
+
$atom_structure['url'] = $this->NoNullString(substr($atom_data, 12));
|
527 |
+
break;
|
528 |
+
|
529 |
+
case 'alis':
|
530 |
+
$atom_structure['file_alias'] = substr($atom_data, 12);
|
531 |
+
break;
|
532 |
+
|
533 |
+
case 'rsrc':
|
534 |
+
$atom_structure['resource_alias'] = substr($atom_data, 12);
|
535 |
+
break;
|
536 |
+
|
537 |
+
default:
|
538 |
+
$atom_structure['data'] = substr($atom_data, 12);
|
539 |
+
break;
|
540 |
+
}
|
541 |
+
break;
|
542 |
+
|
543 |
+
|
544 |
+
case 'rmqu': // Reference Movie QUality atom
|
545 |
+
$atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data);
|
546 |
+
break;
|
547 |
+
|
548 |
+
|
549 |
+
case 'rmcs': // Reference Movie Cpu Speed atom
|
550 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
551 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
552 |
+
$atom_structure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
|
553 |
+
break;
|
554 |
+
|
555 |
+
|
556 |
+
case 'rmvc': // Reference Movie Version Check atom
|
557 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
558 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
559 |
+
$atom_structure['gestalt_selector'] = substr($atom_data, 4, 4);
|
560 |
+
$atom_structure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
|
561 |
+
$atom_structure['gestalt_value'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
|
562 |
+
$atom_structure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
|
563 |
+
break;
|
564 |
+
|
565 |
+
|
566 |
+
case 'rmcd': // Reference Movie Component check atom
|
567 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
568 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
569 |
+
$atom_structure['component_type'] = substr($atom_data, 4, 4);
|
570 |
+
$atom_structure['component_subtype'] = substr($atom_data, 8, 4);
|
571 |
+
$atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
|
572 |
+
$atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
|
573 |
+
$atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
|
574 |
+
$atom_structure['component_min_version'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 4));
|
575 |
+
break;
|
576 |
+
|
577 |
+
|
578 |
+
case 'rmdr': // Reference Movie Data Rate atom
|
579 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
580 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
581 |
+
$atom_structure['data_rate'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
|
582 |
+
|
583 |
+
$atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10;
|
584 |
+
break;
|
585 |
+
|
586 |
+
|
587 |
+
case 'rmla': // Reference Movie Language Atom
|
588 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
589 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
590 |
+
$atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
|
591 |
+
|
592 |
+
$atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
|
593 |
+
if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
|
594 |
+
$info['comments']['language'][] = $atom_structure['language'];
|
595 |
+
}
|
596 |
+
break;
|
597 |
+
|
598 |
+
|
599 |
+
case 'rmla': // Reference Movie Language Atom
|
600 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
601 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
602 |
+
$atom_structure['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
|
603 |
+
break;
|
604 |
+
|
605 |
+
|
606 |
+
case 'ptv ': // Print To Video - defines a movie's full screen mode
|
607 |
+
// http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm
|
608 |
+
$atom_structure['display_size_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
|
609 |
+
$atom_structure['reserved_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); // hardcoded: 0x0000
|
610 |
+
$atom_structure['reserved_2'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x0000
|
611 |
+
$atom_structure['slide_show_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 1));
|
612 |
+
$atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1));
|
613 |
+
|
614 |
+
$atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag'];
|
615 |
+
$atom_structure['flags']['slide_show'] = (bool) $atom_structure['slide_show_flag'];
|
616 |
+
|
617 |
+
$ptv_lookup[0] = 'normal';
|
618 |
+
$ptv_lookup[1] = 'double';
|
619 |
+
$ptv_lookup[2] = 'half';
|
620 |
+
$ptv_lookup[3] = 'full';
|
621 |
+
$ptv_lookup[4] = 'current';
|
622 |
+
if (isset($ptv_lookup[$atom_structure['display_size_raw']])) {
|
623 |
+
$atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
|
624 |
+
} else {
|
625 |
+
$info['warning'][] = 'unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')';
|
626 |
+
}
|
627 |
+
break;
|
628 |
+
|
629 |
+
|
630 |
+
case 'stsd': // Sample Table Sample Description atom
|
631 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
632 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
633 |
+
$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
|
634 |
+
$stsdEntriesDataOffset = 8;
|
635 |
+
for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
|
636 |
+
$atom_structure['sample_description_table'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4));
|
637 |
+
$stsdEntriesDataOffset += 4;
|
638 |
+
$atom_structure['sample_description_table'][$i]['data_format'] = substr($atom_data, $stsdEntriesDataOffset, 4);
|
639 |
+
$stsdEntriesDataOffset += 4;
|
640 |
+
$atom_structure['sample_description_table'][$i]['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6));
|
641 |
+
$stsdEntriesDataOffset += 6;
|
642 |
+
$atom_structure['sample_description_table'][$i]['reference_index'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2));
|
643 |
+
$stsdEntriesDataOffset += 2;
|
644 |
+
$atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
|
645 |
+
$stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
|
646 |
+
|
647 |
+
$atom_structure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 0, 2));
|
648 |
+
$atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 2, 2));
|
649 |
+
$atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4);
|
650 |
+
|
651 |
+
switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) {
|
652 |
+
|
653 |
+
case "\x00\x00\x00\x00":
|
654 |
+
// audio atom
|
655 |
+
$atom_structure['sample_description_table'][$i]['audio_channels'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 2));
|
656 |
+
$atom_structure['sample_description_table'][$i]['audio_bit_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 10, 2));
|
657 |
+
$atom_structure['sample_description_table'][$i]['audio_compression_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 2));
|
658 |
+
$atom_structure['sample_description_table'][$i]['audio_packet_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 14, 2));
|
659 |
+
$atom_structure['sample_description_table'][$i]['audio_sample_rate'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16, 4));
|
660 |
+
|
661 |
+
switch ($atom_structure['sample_description_table'][$i]['data_format']) {
|
662 |
+
case 'avc1':
|
663 |
+
case 'mp4v':
|
664 |
+
$info['fileformat'] = 'mp4';
|
665 |
+
$info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
|
666 |
+
//$info['warning'][] = 'This version of getID3() ['.$this->getid3->version().'] does not fully support MPEG-4 audio/video streams'; // 2011-02-18: why am I warning about this again? What's not supported?
|
667 |
+
break;
|
668 |
+
|
669 |
+
case 'qtvr':
|
670 |
+
$info['video']['dataformat'] = 'quicktimevr';
|
671 |
+
break;
|
672 |
+
|
673 |
+
case 'mp4a':
|
674 |
+
default:
|
675 |
+
$info['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
|
676 |
+
$info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate'];
|
677 |
+
$info['quicktime']['audio']['channels'] = $atom_structure['sample_description_table'][$i]['audio_channels'];
|
678 |
+
$info['quicktime']['audio']['bit_depth'] = $atom_structure['sample_description_table'][$i]['audio_bit_depth'];
|
679 |
+
$info['audio']['codec'] = $info['quicktime']['audio']['codec'];
|
680 |
+
$info['audio']['sample_rate'] = $info['quicktime']['audio']['sample_rate'];
|
681 |
+
$info['audio']['channels'] = $info['quicktime']['audio']['channels'];
|
682 |
+
$info['audio']['bits_per_sample'] = $info['quicktime']['audio']['bit_depth'];
|
683 |
+
switch ($atom_structure['sample_description_table'][$i]['data_format']) {
|
684 |
+
case 'raw ': // PCM
|
685 |
+
case 'alac': // Apple Lossless Audio Codec
|
686 |
+
$info['audio']['lossless'] = true;
|
687 |
+
break;
|
688 |
+
default:
|
689 |
+
$info['audio']['lossless'] = false;
|
690 |
+
break;
|
691 |
+
}
|
692 |
+
break;
|
693 |
+
}
|
694 |
+
break;
|
695 |
+
|
696 |
+
default:
|
697 |
+
switch ($atom_structure['sample_description_table'][$i]['data_format']) {
|
698 |
+
case 'mp4s':
|
699 |
+
$info['fileformat'] = 'mp4';
|
700 |
+
break;
|
701 |
+
|
702 |
+
default:
|
703 |
+
// video atom
|
704 |
+
$atom_structure['sample_description_table'][$i]['video_temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4));
|
705 |
+
$atom_structure['sample_description_table'][$i]['video_spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4));
|
706 |
+
$atom_structure['sample_description_table'][$i]['video_frame_width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2));
|
707 |
+
$atom_structure['sample_description_table'][$i]['video_frame_height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2));
|
708 |
+
$atom_structure['sample_description_table'][$i]['video_resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20, 4));
|
709 |
+
$atom_structure['sample_description_table'][$i]['video_resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4));
|
710 |
+
$atom_structure['sample_description_table'][$i]['video_data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4));
|
711 |
+
$atom_structure['sample_description_table'][$i]['video_frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 2));
|
712 |
+
$atom_structure['sample_description_table'][$i]['video_encoder_name_len'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34, 1));
|
713 |
+
$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']);
|
714 |
+
$atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66, 2));
|
715 |
+
$atom_structure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68, 2));
|
716 |
+
|
717 |
+
$atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color');
|
718 |
+
$atom_structure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']);
|
719 |
+
|
720 |
+
if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') {
|
721 |
+
$info['quicktime']['video']['codec_fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
|
722 |
+
$info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
|
723 |
+
$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']);
|
724 |
+
$info['quicktime']['video']['color_depth'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'];
|
725 |
+
$info['quicktime']['video']['color_depth_name'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_name'];
|
726 |
+
|
727 |
+
$info['video']['codec'] = $info['quicktime']['video']['codec'];
|
728 |
+
$info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth'];
|
729 |
+
}
|
730 |
+
$info['video']['lossless'] = false;
|
731 |
+
$info['video']['pixel_aspect_ratio'] = (float) 1;
|
732 |
+
break;
|
733 |
+
}
|
734 |
+
break;
|
735 |
+
}
|
736 |
+
switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) {
|
737 |
+
case 'mp4a':
|
738 |
+
$info['audio']['dataformat'] = 'mp4';
|
739 |
+
$info['quicktime']['audio']['codec'] = 'mp4';
|
740 |
+
break;
|
741 |
+
|
742 |
+
case '3ivx':
|
743 |
+
case '3iv1':
|
744 |
+
case '3iv2':
|
745 |
+
$info['video']['dataformat'] = '3ivx';
|
746 |
+
break;
|
747 |
+
|
748 |
+
case 'xvid':
|
749 |
+
$info['video']['dataformat'] = 'xvid';
|
750 |
+
break;
|
751 |
+
|
752 |
+
case 'mp4v':
|
753 |
+
$info['video']['dataformat'] = 'mpeg4';
|
754 |
+
break;
|
755 |
+
|
756 |
+
case 'divx':
|
757 |
+
case 'div1':
|
758 |
+
case 'div2':
|
759 |
+
case 'div3':
|
760 |
+
case 'div4':
|
761 |
+
case 'div5':
|
762 |
+
case 'div6':
|
763 |
+
$info['video']['dataformat'] = 'divx';
|
764 |
+
break;
|
765 |
+
|
766 |
+
default:
|
767 |
+
// do nothing
|
768 |
+
break;
|
769 |
+
}
|
770 |
+
unset($atom_structure['sample_description_table'][$i]['data']);
|
771 |
+
}
|
772 |
+
break;
|
773 |
+
|
774 |
+
|
775 |
+
case 'stts': // Sample Table Time-to-Sample atom
|
776 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
777 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
778 |
+
$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
|
779 |
+
$sttsEntriesDataOffset = 8;
|
780 |
+
//$FrameRateCalculatorArray = array();
|
781 |
+
$frames_count = 0;
|
782 |
+
for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
|
783 |
+
$atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
|
784 |
+
$sttsEntriesDataOffset += 4;
|
785 |
+
$atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
|
786 |
+
$sttsEntriesDataOffset += 4;
|
787 |
+
|
788 |
+
$frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count'];
|
789 |
+
|
790 |
+
// THIS SECTION REPLACED WITH CODE IN "stbl" ATOM
|
791 |
+
//if (!empty($info['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) {
|
792 |
+
// $stts_new_framerate = $info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'];
|
793 |
+
// if ($stts_new_framerate <= 60) {
|
794 |
+
// // some atoms have durations of "1" giving a very large framerate, which probably is not right
|
795 |
+
// $info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate);
|
796 |
+
// }
|
797 |
+
//}
|
798 |
+
//
|
799 |
+
//$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count'];
|
800 |
+
}
|
801 |
+
$info['quicktime']['stts_framecount'][] = $frames_count;
|
802 |
+
//$sttsFramesTotal = 0;
|
803 |
+
//$sttsSecondsTotal = 0;
|
804 |
+
//foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) {
|
805 |
+
// if (($frames_per_second > 60) || ($frames_per_second < 1)) {
|
806 |
+
// // not video FPS information, probably audio information
|
807 |
+
// $sttsFramesTotal = 0;
|
808 |
+
// $sttsSecondsTotal = 0;
|
809 |
+
// break;
|
810 |
+
// }
|
811 |
+
// $sttsFramesTotal += $frame_count;
|
812 |
+
// $sttsSecondsTotal += $frame_count / $frames_per_second;
|
813 |
+
//}
|
814 |
+
//if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) {
|
815 |
+
// if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) {
|
816 |
+
// $info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal;
|
817 |
+
// }
|
818 |
+
//}
|
819 |
+
break;
|
820 |
+
|
821 |
+
|
822 |
+
case 'stss': // Sample Table Sync Sample (key frames) atom
|
823 |
+
if ($ParseAllPossibleAtoms) {
|
824 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
825 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
826 |
+
$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
|
827 |
+
$stssEntriesDataOffset = 8;
|
828 |
+
for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
|
829 |
+
$atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4));
|
830 |
+
$stssEntriesDataOffset += 4;
|
831 |
+
}
|
832 |
+
}
|
833 |
+
break;
|
834 |
+
|
835 |
+
|
836 |
+
case 'stsc': // Sample Table Sample-to-Chunk atom
|
837 |
+
if ($ParseAllPossibleAtoms) {
|
838 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
839 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
840 |
+
$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
|
841 |
+
$stscEntriesDataOffset = 8;
|
842 |
+
for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
|
843 |
+
$atom_structure['sample_to_chunk_table'][$i]['first_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
|
844 |
+
$stscEntriesDataOffset += 4;
|
845 |
+
$atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
|
846 |
+
$stscEntriesDataOffset += 4;
|
847 |
+
$atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
|
848 |
+
$stscEntriesDataOffset += 4;
|
849 |
+
}
|
850 |
+
}
|
851 |
+
break;
|
852 |
+
|
853 |
+
|
854 |
+
case 'stsz': // Sample Table SiZe atom
|
855 |
+
if ($ParseAllPossibleAtoms) {
|
856 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
857 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
858 |
+
$atom_structure['sample_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
|
859 |
+
$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
|
860 |
+
$stszEntriesDataOffset = 12;
|
861 |
+
if ($atom_structure['sample_size'] == 0) {
|
862 |
+
for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
|
863 |
+
$atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4));
|
864 |
+
$stszEntriesDataOffset += 4;
|
865 |
+
}
|
866 |
+
}
|
867 |
+
}
|
868 |
+
break;
|
869 |
+
|
870 |
+
|
871 |
+
case 'stco': // Sample Table Chunk Offset atom
|
872 |
+
if ($ParseAllPossibleAtoms) {
|
873 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
874 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
875 |
+
$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
|
876 |
+
$stcoEntriesDataOffset = 8;
|
877 |
+
for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
|
878 |
+
$atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4));
|
879 |
+
$stcoEntriesDataOffset += 4;
|
880 |
+
}
|
881 |
+
}
|
882 |
+
break;
|
883 |
+
|
884 |
+
|
885 |
+
case 'co64': // Chunk Offset 64-bit (version of "stco" that supports > 2GB files)
|
886 |
+
if ($ParseAllPossibleAtoms) {
|
887 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
888 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
889 |
+
$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
|
890 |
+
$stcoEntriesDataOffset = 8;
|
891 |
+
for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
|
892 |
+
$atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8));
|
893 |
+
$stcoEntriesDataOffset += 8;
|
894 |
+
}
|
895 |
+
}
|
896 |
+
break;
|
897 |
+
|
898 |
+
|
899 |
+
case 'dref': // Data REFerence atom
|
900 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
901 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
902 |
+
$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
|
903 |
+
$drefDataOffset = 8;
|
904 |
+
for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
|
905 |
+
$atom_structure['data_references'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4));
|
906 |
+
$drefDataOffset += 4;
|
907 |
+
$atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4);
|
908 |
+
$drefDataOffset += 4;
|
909 |
+
$atom_structure['data_references'][$i]['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 1));
|
910 |
+
$drefDataOffset += 1;
|
911 |
+
$atom_structure['data_references'][$i]['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 3)); // hardcoded: 0x0000
|
912 |
+
$drefDataOffset += 3;
|
913 |
+
$atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3));
|
914 |
+
$drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3);
|
915 |
+
|
916 |
+
$atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001);
|
917 |
+
}
|
918 |
+
break;
|
919 |
+
|
920 |
+
|
921 |
+
case 'gmin': // base Media INformation atom
|
922 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
923 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
924 |
+
$atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
|
925 |
+
$atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
|
926 |
+
$atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2));
|
927 |
+
$atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
|
928 |
+
$atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 2));
|
929 |
+
$atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
|
930 |
+
break;
|
931 |
+
|
932 |
+
|
933 |
+
case 'smhd': // Sound Media information HeaDer atom
|
934 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
935 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
936 |
+
$atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
|
937 |
+
$atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
|
938 |
+
break;
|
939 |
+
|
940 |
+
|
941 |
+
case 'vmhd': // Video Media information HeaDer atom
|
942 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
943 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
|
944 |
+
$atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
|
945 |
+
$atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
|
946 |
+
$atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2));
|
947 |
+
$atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
|
948 |
+
|
949 |
+
$atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x001);
|
950 |
+
break;
|
951 |
+
|
952 |
+
|
953 |
+
case 'hdlr': // HanDLeR reference atom
|
954 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
955 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
956 |
+
$atom_structure['component_type'] = substr($atom_data, 4, 4);
|
957 |
+
$atom_structure['component_subtype'] = substr($atom_data, 8, 4);
|
958 |
+
$atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
|
959 |
+
$atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
|
960 |
+
$atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
|
961 |
+
$atom_structure['component_name'] = $this->Pascal2String(substr($atom_data, 24));
|
962 |
+
|
963 |
+
if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) {
|
964 |
+
$info['video']['dataformat'] = 'quicktimevr';
|
965 |
+
}
|
966 |
+
break;
|
967 |
+
|
968 |
+
|
969 |
+
case 'mdhd': // MeDia HeaDer atom
|
970 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
971 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
972 |
+
$atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
|
973 |
+
$atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
|
974 |
+
$atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
|
975 |
+
$atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
|
976 |
+
$atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 2));
|
977 |
+
$atom_structure['quality'] = getid3_lib::BigEndian2Int(substr($atom_data, 22, 2));
|
978 |
+
|
979 |
+
if ($atom_structure['time_scale'] == 0) {
|
980 |
+
$info['error'][] = 'Corrupt Quicktime file: mdhd.time_scale == zero';
|
981 |
+
return false;
|
982 |
+
}
|
983 |
+
$info['quicktime']['time_scale'] = (isset($info['quicktime']['time_scale']) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
|
984 |
+
|
985 |
+
$atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
|
986 |
+
$atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
|
987 |
+
$atom_structure['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
|
988 |
+
$atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
|
989 |
+
if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
|
990 |
+
$info['comments']['language'][] = $atom_structure['language'];
|
991 |
+
}
|
992 |
+
break;
|
993 |
+
|
994 |
+
|
995 |
+
case 'pnot': // Preview atom
|
996 |
+
$atom_structure['modification_date'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // "standard Macintosh format"
|
997 |
+
$atom_structure['version_number'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x00
|
998 |
+
$atom_structure['atom_type'] = substr($atom_data, 6, 4); // usually: 'PICT'
|
999 |
+
$atom_structure['atom_index'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01
|
1000 |
+
|
1001 |
+
$atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']);
|
1002 |
+
break;
|
1003 |
+
|
1004 |
+
|
1005 |
+
case 'crgn': // Clipping ReGioN atom
|
1006 |
+
$atom_structure['region_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); // The Region size, Region boundary box,
|
1007 |
+
$atom_structure['boundary_box'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 8)); // and Clipping region data fields
|
1008 |
+
$atom_structure['clipping_data'] = substr($atom_data, 10); // constitute a QuickDraw region.
|
1009 |
+
break;
|
1010 |
+
|
1011 |
+
|
1012 |
+
case 'load': // track LOAD settings atom
|
1013 |
+
$atom_structure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
|
1014 |
+
$atom_structure['preload_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
|
1015 |
+
$atom_structure['preload_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
|
1016 |
+
$atom_structure['default_hints_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
|
1017 |
+
|
1018 |
+
$atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x0020);
|
1019 |
+
$atom_structure['default_hints']['high_quality'] = (bool) ($atom_structure['default_hints_raw'] & 0x0100);
|
1020 |
+
break;
|
1021 |
+
|
1022 |
+
|
1023 |
+
case 'tmcd': // TiMe CoDe atom
|
1024 |
+
case 'chap': // CHAPter list atom
|
1025 |
+
case 'sync': // SYNChronization atom
|
1026 |
+
case 'scpt': // tranSCriPT atom
|
1027 |
+
case 'ssrc': // non-primary SouRCe atom
|
1028 |
+
for ($i = 0; $i < (strlen($atom_data) % 4); $i++) {
|
1029 |
+
$atom_structure['track_id'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $i * 4, 4));
|
1030 |
+
}
|
1031 |
+
break;
|
1032 |
+
|
1033 |
+
|
1034 |
+
case 'elst': // Edit LiST atom
|
1035 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
1036 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
1037 |
+
$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
|
1038 |
+
for ($i = 0; $i < $atom_structure['number_entries']; $i++ ) {
|
1039 |
+
$atom_structure['edit_list'][$i]['track_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4));
|
1040 |
+
$atom_structure['edit_list'][$i]['media_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4));
|
1041 |
+
$atom_structure['edit_list'][$i]['media_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4));
|
1042 |
+
}
|
1043 |
+
break;
|
1044 |
+
|
1045 |
+
|
1046 |
+
case 'kmat': // compressed MATte atom
|
1047 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
1048 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
|
1049 |
+
$atom_structure['matte_data_raw'] = substr($atom_data, 4);
|
1050 |
+
break;
|
1051 |
+
|
1052 |
+
|
1053 |
+
case 'ctab': // Color TABle atom
|
1054 |
+
$atom_structure['color_table_seed'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // hardcoded: 0x00000000
|
1055 |
+
$atom_structure['color_table_flags'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x8000
|
1056 |
+
$atom_structure['color_table_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)) + 1;
|
1057 |
+
for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) {
|
1058 |
+
$atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2));
|
1059 |
+
$atom_structure['color_table'][$colortableentry]['red'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2));
|
1060 |
+
$atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2));
|
1061 |
+
$atom_structure['color_table'][$colortableentry]['blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2));
|
1062 |
+
}
|
1063 |
+
break;
|
1064 |
+
|
1065 |
+
|
1066 |
+
case 'mvhd': // MoVie HeaDer atom
|
1067 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
1068 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
|
1069 |
+
$atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
|
1070 |
+
$atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
|
1071 |
+
$atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
|
1072 |
+
$atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
|
1073 |
+
$atom_structure['preferred_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 20, 4));
|
1074 |
+
$atom_structure['preferred_volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 24, 2));
|
1075 |
+
$atom_structure['reserved'] = substr($atom_data, 26, 10);
|
1076 |
+
$atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 36, 4));
|
1077 |
+
$atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
|
1078 |
+
$atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 44, 4));
|
1079 |
+
$atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4));
|
1080 |
+
$atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
|
1081 |
+
$atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 56, 4));
|
1082 |
+
$atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4));
|
1083 |
+
$atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
|
1084 |
+
$atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4));
|
1085 |
+
$atom_structure['preview_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 72, 4));
|
1086 |
+
$atom_structure['preview_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 76, 4));
|
1087 |
+
$atom_structure['poster_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 80, 4));
|
1088 |
+
$atom_structure['selection_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 84, 4));
|
1089 |
+
$atom_structure['selection_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 88, 4));
|
1090 |
+
$atom_structure['current_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 92, 4));
|
1091 |
+
$atom_structure['next_track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 96, 4));
|
1092 |
+
|
1093 |
+
if ($atom_structure['time_scale'] == 0) {
|
1094 |
+
$info['error'][] = 'Corrupt Quicktime file: mvhd.time_scale == zero';
|
1095 |
+
return false;
|
1096 |
+
}
|
1097 |
+
$atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
|
1098 |
+
$atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
|
1099 |
+
$info['quicktime']['time_scale'] = (isset($info['quicktime']['time_scale']) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
|
1100 |
+
$info['quicktime']['display_scale'] = $atom_structure['matrix_a'];
|
1101 |
+
$info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
|
1102 |
+
break;
|
1103 |
+
|
1104 |
+
|
1105 |
+
case 'tkhd': // TracK HeaDer atom
|
1106 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
1107 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
|
1108 |
+
$atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
|
1109 |
+
$atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
|
1110 |
+
$atom_structure['trackid'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
|
1111 |
+
$atom_structure['reserved1'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
|
1112 |
+
$atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
|
1113 |
+
$atom_structure['reserved2'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 8));
|
1114 |
+
$atom_structure['layer'] = getid3_lib::BigEndian2Int(substr($atom_data, 32, 2));
|
1115 |
+
$atom_structure['alternate_group'] = getid3_lib::BigEndian2Int(substr($atom_data, 34, 2));
|
1116 |
+
$atom_structure['volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 36, 2));
|
1117 |
+
$atom_structure['reserved3'] = getid3_lib::BigEndian2Int(substr($atom_data, 38, 2));
|
1118 |
+
$atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
|
1119 |
+
$atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 44, 4));
|
1120 |
+
$atom_structure['matrix_u'] = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4));
|
1121 |
+
$atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
|
1122 |
+
$atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 56, 4));
|
1123 |
+
$atom_structure['matrix_v'] = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4));
|
1124 |
+
$atom_structure['matrix_x'] = getid3_lib::FixedPoint2_30(substr($atom_data, 64, 4));
|
1125 |
+
$atom_structure['matrix_y'] = getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4));
|
1126 |
+
$atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 72, 4));
|
1127 |
+
$atom_structure['width'] = getid3_lib::FixedPoint16_16(substr($atom_data, 76, 4));
|
1128 |
+
$atom_structure['height'] = getid3_lib::FixedPoint16_16(substr($atom_data, 80, 4));
|
1129 |
+
|
1130 |
+
$atom_structure['flags']['enabled'] = (bool) ($atom_structure['flags_raw'] & 0x0001);
|
1131 |
+
$atom_structure['flags']['in_movie'] = (bool) ($atom_structure['flags_raw'] & 0x0002);
|
1132 |
+
$atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x0004);
|
1133 |
+
$atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x0008);
|
1134 |
+
$atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
|
1135 |
+
$atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
|
1136 |
+
|
1137 |
+
if ($atom_structure['flags']['enabled'] == 1) {
|
1138 |
+
if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) {
|
1139 |
+
$info['video']['resolution_x'] = $atom_structure['width'];
|
1140 |
+
$info['video']['resolution_y'] = $atom_structure['height'];
|
1141 |
+
}
|
1142 |
+
$info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']);
|
1143 |
+
$info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']);
|
1144 |
+
$info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
|
1145 |
+
$info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
|
1146 |
+
} else {
|
1147 |
+
if (isset($info['video']['resolution_x'])) { unset($info['video']['resolution_x']); }
|
1148 |
+
if (isset($info['video']['resolution_y'])) { unset($info['video']['resolution_y']); }
|
1149 |
+
if (isset($info['quicktime']['video'])) { unset($info['quicktime']['video']); }
|
1150 |
+
}
|
1151 |
+
break;
|
1152 |
+
|
1153 |
+
|
1154 |
+
case 'iods': // Initial Object DeScriptor atom
|
1155 |
+
// http://www.koders.com/c/fid1FAB3E762903DC482D8A246D4A4BF9F28E049594.aspx?s=windows.h
|
1156 |
+
// http://libquicktime.sourcearchive.com/documentation/1.0.2plus-pdebian/iods_8c-source.html
|
1157 |
+
$offset = 0;
|
1158 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
|
1159 |
+
$offset += 1;
|
1160 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 3));
|
1161 |
+
$offset += 3;
|
1162 |
+
$atom_structure['mp4_iod_tag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
|
1163 |
+
$offset += 1;
|
1164 |
+
$atom_structure['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
|
1165 |
+
//$offset already adjusted by quicktime_read_mp4_descr_length()
|
1166 |
+
$atom_structure['object_descriptor_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2));
|
1167 |
+
$offset += 2;
|
1168 |
+
$atom_structure['od_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
|
1169 |
+
$offset += 1;
|
1170 |
+
$atom_structure['scene_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
|
1171 |
+
$offset += 1;
|
1172 |
+
$atom_structure['audio_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
|
1173 |
+
$offset += 1;
|
1174 |
+
$atom_structure['video_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
|
1175 |
+
$offset += 1;
|
1176 |
+
$atom_structure['graphics_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
|
1177 |
+
$offset += 1;
|
1178 |
+
|
1179 |
+
$atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6; // 6 bytes would only be right if all tracks use 1-byte length fields
|
1180 |
+
for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) {
|
1181 |
+
$atom_structure['track'][$i]['ES_ID_IncTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
|
1182 |
+
$offset += 1;
|
1183 |
+
$atom_structure['track'][$i]['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
|
1184 |
+
//$offset already adjusted by quicktime_read_mp4_descr_length()
|
1185 |
+
$atom_structure['track'][$i]['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4));
|
1186 |
+
$offset += 4;
|
1187 |
+
}
|
1188 |
+
|
1189 |
+
$atom_structure['audio_profile_name'] = $this->QuicktimeIODSaudioProfileName($atom_structure['audio_profile_id']);
|
1190 |
+
$atom_structure['video_profile_name'] = $this->QuicktimeIODSvideoProfileName($atom_structure['video_profile_id']);
|
1191 |
+
break;
|
1192 |
+
|
1193 |
+
case 'ftyp': // FileTYPe (?) atom (for MP4 it seems)
|
1194 |
+
$atom_structure['signature'] = substr($atom_data, 0, 4);
|
1195 |
+
$atom_structure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
|
1196 |
+
$atom_structure['fourcc'] = substr($atom_data, 8, 4);
|
1197 |
+
break;
|
1198 |
+
|
1199 |
+
case 'mdat': // Media DATa atom
|
1200 |
+
case 'free': // FREE space atom
|
1201 |
+
case 'skip': // SKIP atom
|
1202 |
+
case 'wide': // 64-bit expansion placeholder atom
|
1203 |
+
// 'mdat' data is too big to deal with, contains no useful metadata
|
1204 |
+
// 'free', 'skip' and 'wide' are just padding, contains no useful data at all
|
1205 |
+
|
1206 |
+
// When writing QuickTime files, it is sometimes necessary to update an atom's size.
|
1207 |
+
// It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom
|
1208 |
+
// is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime
|
1209 |
+
// puts an 8-byte placeholder atom before any atoms it may have to update the size of.
|
1210 |
+
// In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the
|
1211 |
+
// placeholder atom can be overwritten to obtain the necessary 8 extra bytes.
|
1212 |
+
// The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ).
|
1213 |
+
break;
|
1214 |
+
|
1215 |
+
|
1216 |
+
case 'nsav': // NoSAVe atom
|
1217 |
+
// http://developer.apple.com/technotes/tn/tn2038.html
|
1218 |
+
$atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
|
1219 |
+
break;
|
1220 |
+
|
1221 |
+
case 'ctyp': // Controller TYPe atom (seen on QTVR)
|
1222 |
+
// http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
|
1223 |
+
// some controller names are:
|
1224 |
+
// 0x00 + 'std' for linear movie
|
1225 |
+
// 'none' for no controls
|
1226 |
+
$atom_structure['ctyp'] = substr($atom_data, 0, 4);
|
1227 |
+
$info['quicktime']['controller'] = $atom_structure['ctyp'];
|
1228 |
+
switch ($atom_structure['ctyp']) {
|
1229 |
+
case 'qtvr':
|
1230 |
+
$info['video']['dataformat'] = 'quicktimevr';
|
1231 |
+
break;
|
1232 |
+
}
|
1233 |
+
break;
|
1234 |
+
|
1235 |
+
case 'pano': // PANOrama track (seen on QTVR)
|
1236 |
+
$atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
|
1237 |
+
break;
|
1238 |
+
|
1239 |
+
case 'hint': // HINT track
|
1240 |
+
case 'hinf': //
|
1241 |
+
case 'hinv': //
|
1242 |
+
case 'hnti': //
|
1243 |
+
$info['quicktime']['hinting'] = true;
|
1244 |
+
break;
|
1245 |
+
|
1246 |
+
case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR)
|
1247 |
+
for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) {
|
1248 |
+
$atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
|
1249 |
+
}
|
1250 |
+
break;
|
1251 |
+
|
1252 |
+
|
1253 |
+
// Observed-but-not-handled atom types are just listed here to prevent warnings being generated
|
1254 |
+
case 'FXTC': // Something to do with Adobe After Effects (?)
|
1255 |
+
case 'PrmA':
|
1256 |
+
case 'code':
|
1257 |
+
case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html
|
1258 |
+
case 'tapt': // TrackApertureModeDimensionsAID - http://developer.apple.com/documentation/QuickTime/Reference/QT7-1_Update_Reference/Constants/Constants.html
|
1259 |
+
// tapt seems to be used to compute the video size [http://www.getid3.org/phpBB3/viewtopic.php?t=838]
|
1260 |
+
// * http://lists.apple.com/archives/quicktime-api/2006/Aug/msg00014.html
|
1261 |
+
// * http://handbrake.fr/irclogs/handbrake-dev/handbrake-dev20080128_pg2.html
|
1262 |
+
case 'ctts':// STCompositionOffsetAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
|
1263 |
+
case 'cslg':// STCompositionShiftLeastGreatestAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
|
1264 |
+
case 'sdtp':// STSampleDependencyAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
|
1265 |
+
case 'stps':// STPartialSyncSampleAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
|
1266 |
+
//$atom_structure['data'] = $atom_data;
|
1267 |
+
break;
|
1268 |
+
|
1269 |
+
case '�xyz': // GPS latitude+longitude+altitude
|
1270 |
+
$atom_structure['data'] = $atom_data;
|
1271 |
+
if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) {
|
1272 |
+
@list($all, $latitude, $longitude, $altitude) = $matches;
|
1273 |
+
$info['quicktime']['comments']['gps_latitude'][] = floatval($latitude);
|
1274 |
+
$info['quicktime']['comments']['gps_longitude'][] = floatval($longitude);
|
1275 |
+
if (!empty($altitude)) {
|
1276 |
+
$info['quicktime']['comments']['gps_altitude'][] = floatval($altitude);
|
1277 |
+
}
|
1278 |
+
} else {
|
1279 |
+
$info['warning'][] = 'QuickTime atom "�xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.';
|
1280 |
+
}
|
1281 |
+
break;
|
1282 |
+
|
1283 |
+
case 'NCDT':
|
1284 |
+
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
|
1285 |
+
// Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
|
1286 |
+
$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms);
|
1287 |
+
break;
|
1288 |
+
case 'NCTH': // Nikon Camera THumbnail image
|
1289 |
+
case 'NCVW': // Nikon Camera preVieW image
|
1290 |
+
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
|
1291 |
+
if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) {
|
1292 |
+
$atom_structure['data'] = $atom_data;
|
1293 |
+
$atom_structure['image_mime'] = 'image/jpeg';
|
1294 |
+
$atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image'));
|
1295 |
+
$info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']);
|
1296 |
+
}
|
1297 |
+
break;
|
1298 |
+
case 'NCHD': // MakerNoteVersion
|
1299 |
+
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
|
1300 |
+
$atom_structure['data'] = $atom_data;
|
1301 |
+
break;
|
1302 |
+
case 'NCTG': // NikonTags
|
1303 |
+
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
|
1304 |
+
$atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data);
|
1305 |
+
break;
|
1306 |
+
case 'NCDB': // NikonTags
|
1307 |
+
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
|
1308 |
+
$atom_structure['data'] = $atom_data;
|
1309 |
+
break;
|
1310 |
+
|
1311 |
+
case "\x00\x00\x00\x00":
|
1312 |
+
case 'meta': // METAdata atom
|
1313 |
+
// some kind of metacontainer, may contain a big data dump such as:
|
1314 |
+
// 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
|
1315 |
+
// http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
|
1316 |
+
|
1317 |
+
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
|
1318 |
+
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
|
1319 |
+
$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
|
1320 |
+
//$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
|
1321 |
+
break;
|
1322 |
+
|
1323 |
+
case 'data': // metaDATA atom
|
1324 |
+
// seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data
|
1325 |
+
$atom_structure['language'] = substr($atom_data, 4 + 0, 2);
|
1326 |
+
$atom_structure['unknown'] = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2));
|
1327 |
+
$atom_structure['data'] = substr($atom_data, 4 + 4);
|
1328 |
+
break;
|
1329 |
+
|
1330 |
+
default:
|
1331 |
+
$info['warning'][] = 'Unknown QuickTime atom type: "'.getid3_lib::PrintHexBytes($atomname).'" at offset '.$baseoffset;
|
1332 |
+
$atom_structure['data'] = $atom_data;
|
1333 |
+
break;
|
1334 |
+
}
|
1335 |
+
array_pop($atomHierarchy);
|
1336 |
+
return $atom_structure;
|
1337 |
+
}
|
1338 |
+
|
1339 |
+
function QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
|
1340 |
+
//echo 'QuicktimeParseContainerAtom('.substr($atom_data, 4, 4).') @ '.$baseoffset.'<br><br>';
|
1341 |
+
$atom_structure = false;
|
1342 |
+
$subatomoffset = 0;
|
1343 |
+
$subatomcounter = 0;
|
1344 |
+
if ((strlen($atom_data) == 4) && (getid3_lib::BigEndian2Int($atom_data) == 0x00000000)) {
|
1345 |
+
return false;
|
1346 |
+
}
|
1347 |
+
while ($subatomoffset < strlen($atom_data)) {
|
1348 |
+
$subatomsize = getid3_lib::BigEndian2Int(substr($atom_data, $subatomoffset + 0, 4));
|
1349 |
+
$subatomname = substr($atom_data, $subatomoffset + 4, 4);
|
1350 |
+
$subatomdata = substr($atom_data, $subatomoffset + 8, $subatomsize - 8);
|
1351 |
+
if ($subatomsize == 0) {
|
1352 |
+
// Furthermore, for historical reasons the list of atoms is optionally
|
1353 |
+
// terminated by a 32-bit integer set to 0. If you are writing a program
|
1354 |
+
// to read user data atoms, you should allow for the terminating 0.
|
1355 |
+
return $atom_structure;
|
1356 |
+
}
|
1357 |
+
|
1358 |
+
$atom_structure[$subatomcounter] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms);
|
1359 |
+
|
1360 |
+
$subatomoffset += $subatomsize;
|
1361 |
+
$subatomcounter++;
|
1362 |
+
}
|
1363 |
+
return $atom_structure;
|
1364 |
+
}
|
1365 |
+
|
1366 |
+
|
1367 |
+
function quicktime_read_mp4_descr_length($data, &$offset) {
|
1368 |
+
// http://libquicktime.sourcearchive.com/documentation/2:1.0.2plus-pdebian-2build1/esds_8c-source.html
|
1369 |
+
$num_bytes = 0;
|
1370 |
+
$length = 0;
|
1371 |
+
do {
|
1372 |
+
$b = ord(substr($data, $offset++, 1));
|
1373 |
+
$length = ($length << 7) | ($b & 0x7F);
|
1374 |
+
} while (($b & 0x80) && ($num_bytes++ < 4));
|
1375 |
+
return $length;
|
1376 |
+
}
|
1377 |
+
|
1378 |
+
|
1379 |
+
function QuicktimeLanguageLookup($languageid) {
|
1380 |
+
static $QuicktimeLanguageLookup = array();
|
1381 |
+
if (empty($QuicktimeLanguageLookup)) {
|
1382 |
+
$QuicktimeLanguageLookup[0] = 'English';
|
1383 |
+
$QuicktimeLanguageLookup[1] = 'French';
|
1384 |
+
$QuicktimeLanguageLookup[2] = 'German';
|
1385 |
+
$QuicktimeLanguageLookup[3] = 'Italian';
|
1386 |
+
$QuicktimeLanguageLookup[4] = 'Dutch';
|
1387 |
+
$QuicktimeLanguageLookup[5] = 'Swedish';
|
1388 |
+
$QuicktimeLanguageLookup[6] = 'Spanish';
|
1389 |
+
$QuicktimeLanguageLookup[7] = 'Danish';
|
1390 |
+
$QuicktimeLanguageLookup[8] = 'Portuguese';
|
1391 |
+
$QuicktimeLanguageLookup[9] = 'Norwegian';
|
1392 |
+
$QuicktimeLanguageLookup[10] = 'Hebrew';
|
1393 |
+
$QuicktimeLanguageLookup[11] = 'Japanese';
|
1394 |
+
$QuicktimeLanguageLookup[12] = 'Arabic';
|
1395 |
+
$QuicktimeLanguageLookup[13] = 'Finnish';
|
1396 |
+
$QuicktimeLanguageLookup[14] = 'Greek';
|
1397 |
+
$QuicktimeLanguageLookup[15] = 'Icelandic';
|
1398 |
+
$QuicktimeLanguageLookup[16] = 'Maltese';
|
1399 |
+
$QuicktimeLanguageLookup[17] = 'Turkish';
|
1400 |
+
$QuicktimeLanguageLookup[18] = 'Croatian';
|
1401 |
+
$QuicktimeLanguageLookup[19] = 'Chinese (Traditional)';
|
1402 |
+
$QuicktimeLanguageLookup[20] = 'Urdu';
|
1403 |
+
$QuicktimeLanguageLookup[21] = 'Hindi';
|
1404 |
+
$QuicktimeLanguageLookup[22] = 'Thai';
|
1405 |
+
$QuicktimeLanguageLookup[23] = 'Korean';
|
1406 |
+
$QuicktimeLanguageLookup[24] = 'Lithuanian';
|
1407 |
+
$QuicktimeLanguageLookup[25] = 'Polish';
|
1408 |
+
$QuicktimeLanguageLookup[26] = 'Hungarian';
|
1409 |
+
$QuicktimeLanguageLookup[27] = 'Estonian';
|
1410 |
+
$QuicktimeLanguageLookup[28] = 'Lettish';
|
1411 |
+
$QuicktimeLanguageLookup[28] = 'Latvian';
|
1412 |
+
$QuicktimeLanguageLookup[29] = 'Saamisk';
|
1413 |
+
$QuicktimeLanguageLookup[29] = 'Lappish';
|
1414 |
+
$QuicktimeLanguageLookup[30] = 'Faeroese';
|
1415 |
+
$QuicktimeLanguageLookup[31] = 'Farsi';
|
1416 |
+
$QuicktimeLanguageLookup[31] = 'Persian';
|
1417 |
+
$QuicktimeLanguageLookup[32] = 'Russian';
|
1418 |
+
$QuicktimeLanguageLookup[33] = 'Chinese (Simplified)';
|
1419 |
+
$QuicktimeLanguageLookup[34] = 'Flemish';
|
1420 |
+
$QuicktimeLanguageLookup[35] = 'Irish';
|
1421 |
+
$QuicktimeLanguageLookup[36] = 'Albanian';
|
1422 |
+
$QuicktimeLanguageLookup[37] = 'Romanian';
|
1423 |
+
$QuicktimeLanguageLookup[38] = 'Czech';
|
1424 |
+
$QuicktimeLanguageLookup[39] = 'Slovak';
|
1425 |
+
$QuicktimeLanguageLookup[40] = 'Slovenian';
|
1426 |
+
$QuicktimeLanguageLookup[41] = 'Yiddish';
|
1427 |
+
$QuicktimeLanguageLookup[42] = 'Serbian';
|
1428 |
+
$QuicktimeLanguageLookup[43] = 'Macedonian';
|
1429 |
+
$QuicktimeLanguageLookup[44] = 'Bulgarian';
|
1430 |
+
$QuicktimeLanguageLookup[45] = 'Ukrainian';
|
1431 |
+
$QuicktimeLanguageLookup[46] = 'Byelorussian';
|
1432 |
+
$QuicktimeLanguageLookup[47] = 'Uzbek';
|
1433 |
+
$QuicktimeLanguageLookup[48] = 'Kazakh';
|
1434 |
+
$QuicktimeLanguageLookup[49] = 'Azerbaijani';
|
1435 |
+
$QuicktimeLanguageLookup[50] = 'AzerbaijanAr';
|
1436 |
+
$QuicktimeLanguageLookup[51] = 'Armenian';
|
1437 |
+
$QuicktimeLanguageLookup[52] = 'Georgian';
|
1438 |
+
$QuicktimeLanguageLookup[53] = 'Moldavian';
|
1439 |
+
$QuicktimeLanguageLookup[54] = 'Kirghiz';
|
1440 |
+
$QuicktimeLanguageLookup[55] = 'Tajiki';
|
1441 |
+
$QuicktimeLanguageLookup[56] = 'Turkmen';
|
1442 |
+
$QuicktimeLanguageLookup[57] = 'Mongolian';
|
1443 |
+
$QuicktimeLanguageLookup[58] = 'MongolianCyr';
|
1444 |
+
$QuicktimeLanguageLookup[59] = 'Pashto';
|
1445 |
+
$QuicktimeLanguageLookup[60] = 'Kurdish';
|
1446 |
+
$QuicktimeLanguageLookup[61] = 'Kashmiri';
|
1447 |
+
$QuicktimeLanguageLookup[62] = 'Sindhi';
|
1448 |
+
$QuicktimeLanguageLookup[63] = 'Tibetan';
|
1449 |
+
$QuicktimeLanguageLookup[64] = 'Nepali';
|
1450 |
+
$QuicktimeLanguageLookup[65] = 'Sanskrit';
|
1451 |
+
$QuicktimeLanguageLookup[66] = 'Marathi';
|
1452 |
+
$QuicktimeLanguageLookup[67] = 'Bengali';
|
1453 |
+
$QuicktimeLanguageLookup[68] = 'Assamese';
|
1454 |
+
$QuicktimeLanguageLookup[69] = 'Gujarati';
|
1455 |
+
$QuicktimeLanguageLookup[70] = 'Punjabi';
|
1456 |
+
$QuicktimeLanguageLookup[71] = 'Oriya';
|
1457 |
+
$QuicktimeLanguageLookup[72] = 'Malayalam';
|
1458 |
+
$QuicktimeLanguageLookup[73] = 'Kannada';
|
1459 |
+
$QuicktimeLanguageLookup[74] = 'Tamil';
|
1460 |
+
$QuicktimeLanguageLookup[75] = 'Telugu';
|
1461 |
+
$QuicktimeLanguageLookup[76] = 'Sinhalese';
|
1462 |
+
$QuicktimeLanguageLookup[77] = 'Burmese';
|
1463 |
+
$QuicktimeLanguageLookup[78] = 'Khmer';
|
1464 |
+
$QuicktimeLanguageLookup[79] = 'Lao';
|
1465 |
+
$QuicktimeLanguageLookup[80] = 'Vietnamese';
|
1466 |
+
$QuicktimeLanguageLookup[81] = 'Indonesian';
|
1467 |
+
$QuicktimeLanguageLookup[82] = 'Tagalog';
|
1468 |
+
$QuicktimeLanguageLookup[83] = 'MalayRoman';
|
1469 |
+
$QuicktimeLanguageLookup[84] = 'MalayArabic';
|
1470 |
+
$QuicktimeLanguageLookup[85] = 'Amharic';
|
1471 |
+
$QuicktimeLanguageLookup[86] = 'Tigrinya';
|
1472 |
+
$QuicktimeLanguageLookup[87] = 'Galla';
|
1473 |
+
$QuicktimeLanguageLookup[87] = 'Oromo';
|
1474 |
+
$QuicktimeLanguageLookup[88] = 'Somali';
|
1475 |
+
$QuicktimeLanguageLookup[89] = 'Swahili';
|
1476 |
+
$QuicktimeLanguageLookup[90] = 'Ruanda';
|
1477 |
+
$QuicktimeLanguageLookup[91] = 'Rundi';
|
1478 |
+
$QuicktimeLanguageLookup[92] = 'Chewa';
|
1479 |
+
$QuicktimeLanguageLookup[93] = 'Malagasy';
|
1480 |
+
$QuicktimeLanguageLookup[94] = 'Esperanto';
|
1481 |
+
$QuicktimeLanguageLookup[128] = 'Welsh';
|
1482 |
+
$QuicktimeLanguageLookup[129] = 'Basque';
|
1483 |
+
$QuicktimeLanguageLookup[130] = 'Catalan';
|
1484 |
+
$QuicktimeLanguageLookup[131] = 'Latin';
|
1485 |
+
$QuicktimeLanguageLookup[132] = 'Quechua';
|
1486 |
+
$QuicktimeLanguageLookup[133] = 'Guarani';
|
1487 |
+
$QuicktimeLanguageLookup[134] = 'Aymara';
|
1488 |
+
$QuicktimeLanguageLookup[135] = 'Tatar';
|
1489 |
+
$QuicktimeLanguageLookup[136] = 'Uighur';
|
1490 |
+
$QuicktimeLanguageLookup[137] = 'Dzongkha';
|
1491 |
+
$QuicktimeLanguageLookup[138] = 'JavaneseRom';
|
1492 |
+
}
|
1493 |
+
return (isset($QuicktimeLanguageLookup[$languageid]) ? $QuicktimeLanguageLookup[$languageid] : 'invalid');
|
1494 |
+
}
|
1495 |
+
|
1496 |
+
function QuicktimeVideoCodecLookup($codecid) {
|
1497 |
+
static $QuicktimeVideoCodecLookup = array();
|
1498 |
+
if (empty($QuicktimeVideoCodecLookup)) {
|
1499 |
+
$QuicktimeVideoCodecLookup['.SGI'] = 'SGI';
|
1500 |
+
$QuicktimeVideoCodecLookup['3IV1'] = '3ivx MPEG-4 v1';
|
1501 |
+
$QuicktimeVideoCodecLookup['3IV2'] = '3ivx MPEG-4 v2';
|
1502 |
+
$QuicktimeVideoCodecLookup['3IVX'] = '3ivx MPEG-4';
|
1503 |
+
$QuicktimeVideoCodecLookup['8BPS'] = 'Planar RGB';
|
1504 |
+
$QuicktimeVideoCodecLookup['avc1'] = 'H.264/MPEG-4 AVC';
|
1505 |
+
$QuicktimeVideoCodecLookup['avr '] = 'AVR-JPEG';
|
1506 |
+
$QuicktimeVideoCodecLookup['b16g'] = '16Gray';
|
1507 |
+
$QuicktimeVideoCodecLookup['b32a'] = '32AlphaGray';
|
1508 |
+
$QuicktimeVideoCodecLookup['b48r'] = '48RGB';
|
1509 |
+
$QuicktimeVideoCodecLookup['b64a'] = '64ARGB';
|
1510 |
+
$QuicktimeVideoCodecLookup['base'] = 'Base';
|
1511 |
+
$QuicktimeVideoCodecLookup['clou'] = 'Cloud';
|
1512 |
+
$QuicktimeVideoCodecLookup['cmyk'] = 'CMYK';
|
1513 |
+
$QuicktimeVideoCodecLookup['cvid'] = 'Cinepak';
|
1514 |
+
$QuicktimeVideoCodecLookup['dmb1'] = 'OpenDML JPEG';
|
1515 |
+
$QuicktimeVideoCodecLookup['dvc '] = 'DVC-NTSC';
|
1516 |
+
$QuicktimeVideoCodecLookup['dvcp'] = 'DVC-PAL';
|
1517 |
+
$QuicktimeVideoCodecLookup['dvpn'] = 'DVCPro-NTSC';
|
1518 |
+
$QuicktimeVideoCodecLookup['dvpp'] = 'DVCPro-PAL';
|
1519 |
+
$QuicktimeVideoCodecLookup['fire'] = 'Fire';
|
1520 |
+
$QuicktimeVideoCodecLookup['flic'] = 'FLC';
|
1521 |
+
$QuicktimeVideoCodecLookup['gif '] = 'GIF';
|
1522 |
+
$QuicktimeVideoCodecLookup['h261'] = 'H261';
|
1523 |
+
$QuicktimeVideoCodecLookup['h263'] = 'H263';
|
1524 |
+
$QuicktimeVideoCodecLookup['IV41'] = 'Indeo4';
|
1525 |
+
$QuicktimeVideoCodecLookup['jpeg'] = 'JPEG';
|
1526 |
+
$QuicktimeVideoCodecLookup['kpcd'] = 'PhotoCD';
|
1527 |
+
$QuicktimeVideoCodecLookup['mjpa'] = 'Motion JPEG-A';
|
1528 |
+
$QuicktimeVideoCodecLookup['mjpb'] = 'Motion JPEG-B';
|
1529 |
+
$QuicktimeVideoCodecLookup['msvc'] = 'Microsoft Video1';
|
1530 |
+
$QuicktimeVideoCodecLookup['myuv'] = 'MPEG YUV420';
|
1531 |
+
$QuicktimeVideoCodecLookup['path'] = 'Vector';
|
1532 |
+
$QuicktimeVideoCodecLookup['png '] = 'PNG';
|
1533 |
+
$QuicktimeVideoCodecLookup['PNTG'] = 'MacPaint';
|
1534 |
+
$QuicktimeVideoCodecLookup['qdgx'] = 'QuickDrawGX';
|
1535 |
+
$QuicktimeVideoCodecLookup['qdrw'] = 'QuickDraw';
|
1536 |
+
$QuicktimeVideoCodecLookup['raw '] = 'RAW';
|
1537 |
+
$QuicktimeVideoCodecLookup['ripl'] = 'WaterRipple';
|
1538 |
+
$QuicktimeVideoCodecLookup['rpza'] = 'Video';
|
1539 |
+
$QuicktimeVideoCodecLookup['smc '] = 'Graphics';
|
1540 |
+
$QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 1';
|
1541 |
+
$QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 3';
|
1542 |
+
$QuicktimeVideoCodecLookup['syv9'] = 'Sorenson YUV9';
|
1543 |
+
$QuicktimeVideoCodecLookup['tga '] = 'Targa';
|
1544 |
+
$QuicktimeVideoCodecLookup['tiff'] = 'TIFF';
|
1545 |
+
$QuicktimeVideoCodecLookup['WRAW'] = 'Windows RAW';
|
1546 |
+
$QuicktimeVideoCodecLookup['WRLE'] = 'BMP';
|
1547 |
+
$QuicktimeVideoCodecLookup['y420'] = 'YUV420';
|
1548 |
+
$QuicktimeVideoCodecLookup['yuv2'] = 'ComponentVideo';
|
1549 |
+
$QuicktimeVideoCodecLookup['yuvs'] = 'ComponentVideoUnsigned';
|
1550 |
+
$QuicktimeVideoCodecLookup['yuvu'] = 'ComponentVideoSigned';
|
1551 |
+
}
|
1552 |
+
return (isset($QuicktimeVideoCodecLookup[$codecid]) ? $QuicktimeVideoCodecLookup[$codecid] : '');
|
1553 |
+
}
|
1554 |
+
|
1555 |
+
function QuicktimeAudioCodecLookup($codecid) {
|
1556 |
+
static $QuicktimeAudioCodecLookup = array();
|
1557 |
+
if (empty($QuicktimeAudioCodecLookup)) {
|
1558 |
+
$QuicktimeAudioCodecLookup['.mp3'] = 'Fraunhofer MPEG Layer-III alias';
|
1559 |
+
$QuicktimeAudioCodecLookup['aac '] = 'ISO/IEC 14496-3 AAC';
|
1560 |
+
$QuicktimeAudioCodecLookup['agsm'] = 'Apple GSM 10:1';
|
1561 |
+
$QuicktimeAudioCodecLookup['alac'] = 'Apple Lossless Audio Codec';
|
1562 |
+
$QuicktimeAudioCodecLookup['alaw'] = 'A-law 2:1';
|
1563 |
+
$QuicktimeAudioCodecLookup['conv'] = 'Sample Format';
|
1564 |
+
$QuicktimeAudioCodecLookup['dvca'] = 'DV';
|
1565 |
+
$QuicktimeAudioCodecLookup['dvi '] = 'DV 4:1';
|
1566 |
+
$QuicktimeAudioCodecLookup['eqal'] = 'Frequency Equalizer';
|
1567 |
+
$QuicktimeAudioCodecLookup['fl32'] = '32-bit Floating Point';
|
1568 |
+
$QuicktimeAudioCodecLookup['fl64'] = '64-bit Floating Point';
|
1569 |
+
$QuicktimeAudioCodecLookup['ima4'] = 'Interactive Multimedia Association 4:1';
|
1570 |
+
$QuicktimeAudioCodecLookup['in24'] = '24-bit Integer';
|
1571 |
+
$QuicktimeAudioCodecLookup['in32'] = '32-bit Integer';
|
1572 |
+
$QuicktimeAudioCodecLookup['lpc '] = 'LPC 23:1';
|
1573 |
+
$QuicktimeAudioCodecLookup['MAC3'] = 'Macintosh Audio Compression/Expansion (MACE) 3:1';
|
1574 |
+
$QuicktimeAudioCodecLookup['MAC6'] = 'Macintosh Audio Compression/Expansion (MACE) 6:1';
|
1575 |
+
$QuicktimeAudioCodecLookup['mixb'] = '8-bit Mixer';
|
1576 |
+
$QuicktimeAudioCodecLookup['mixw'] = '16-bit Mixer';
|
1577 |
+
$QuicktimeAudioCodecLookup['mp4a'] = 'ISO/IEC 14496-3 AAC';
|
1578 |
+
$QuicktimeAudioCodecLookup['MS'."\x00\x02"] = 'Microsoft ADPCM';
|
1579 |
+
$QuicktimeAudioCodecLookup['MS'."\x00\x11"] = 'DV IMA';
|
1580 |
+
$QuicktimeAudioCodecLookup['MS'."\x00\x55"] = 'Fraunhofer MPEG Layer III';
|
1581 |
+
$QuicktimeAudioCodecLookup['NONE'] = 'No Encoding';
|
1582 |
+
$QuicktimeAudioCodecLookup['Qclp'] = 'Qualcomm PureVoice';
|
1583 |
+
$QuicktimeAudioCodecLookup['QDM2'] = 'QDesign Music 2';
|
1584 |
+
$QuicktimeAudioCodecLookup['QDMC'] = 'QDesign Music 1';
|
1585 |
+
$QuicktimeAudioCodecLookup['ratb'] = '8-bit Rate';
|
1586 |
+
$QuicktimeAudioCodecLookup['ratw'] = '16-bit Rate';
|
1587 |
+
$QuicktimeAudioCodecLookup['raw '] = 'raw PCM';
|
1588 |
+
$QuicktimeAudioCodecLookup['sour'] = 'Sound Source';
|
1589 |
+
$QuicktimeAudioCodecLookup['sowt'] = 'signed/two\'s complement (Little Endian)';
|
1590 |
+
$QuicktimeAudioCodecLookup['str1'] = 'Iomega MPEG layer II';
|
1591 |
+
$QuicktimeAudioCodecLookup['str2'] = 'Iomega MPEG *layer II';
|
1592 |
+
$QuicktimeAudioCodecLookup['str3'] = 'Iomega MPEG **layer II';
|
1593 |
+
$QuicktimeAudioCodecLookup['str4'] = 'Iomega MPEG ***layer II';
|
1594 |
+
$QuicktimeAudioCodecLookup['twos'] = 'signed/two\'s complement (Big Endian)';
|
1595 |
+
$QuicktimeAudioCodecLookup['ulaw'] = 'mu-law 2:1';
|
1596 |
+
}
|
1597 |
+
return (isset($QuicktimeAudioCodecLookup[$codecid]) ? $QuicktimeAudioCodecLookup[$codecid] : '');
|
1598 |
+
}
|
1599 |
+
|
1600 |
+
function QuicktimeDCOMLookup($compressionid) {
|
1601 |
+
static $QuicktimeDCOMLookup = array();
|
1602 |
+
if (empty($QuicktimeDCOMLookup)) {
|
1603 |
+
$QuicktimeDCOMLookup['zlib'] = 'ZLib Deflate';
|
1604 |
+
$QuicktimeDCOMLookup['adec'] = 'Apple Compression';
|
1605 |
+
}
|
1606 |
+
return (isset($QuicktimeDCOMLookup[$compressionid]) ? $QuicktimeDCOMLookup[$compressionid] : '');
|
1607 |
+
}
|
1608 |
+
|
1609 |
+
function QuicktimeColorNameLookup($colordepthid) {
|
1610 |
+
static $QuicktimeColorNameLookup = array();
|
1611 |
+
if (empty($QuicktimeColorNameLookup)) {
|
1612 |
+
$QuicktimeColorNameLookup[1] = '2-color (monochrome)';
|
1613 |
+
$QuicktimeColorNameLookup[2] = '4-color';
|
1614 |
+
$QuicktimeColorNameLookup[4] = '16-color';
|
1615 |
+
$QuicktimeColorNameLookup[8] = '256-color';
|
1616 |
+
$QuicktimeColorNameLookup[16] = 'thousands (16-bit color)';
|
1617 |
+
$QuicktimeColorNameLookup[24] = 'millions (24-bit color)';
|
1618 |
+
$QuicktimeColorNameLookup[32] = 'millions+ (32-bit color)';
|
1619 |
+
$QuicktimeColorNameLookup[33] = 'black & white';
|
1620 |
+
$QuicktimeColorNameLookup[34] = '4-gray';
|
1621 |
+
$QuicktimeColorNameLookup[36] = '16-gray';
|
1622 |
+
$QuicktimeColorNameLookup[40] = '256-gray';
|
1623 |
+
}
|
1624 |
+
return (isset($QuicktimeColorNameLookup[$colordepthid]) ? $QuicktimeColorNameLookup[$colordepthid] : 'invalid');
|
1625 |
+
}
|
1626 |
+
|
1627 |
+
function QuicktimeSTIKLookup($stik) {
|
1628 |
+
static $QuicktimeSTIKLookup = array();
|
1629 |
+
if (empty($QuicktimeSTIKLookup)) {
|
1630 |
+
$QuicktimeSTIKLookup[0] = 'Movie';
|
1631 |
+
$QuicktimeSTIKLookup[1] = 'Normal';
|
1632 |
+
$QuicktimeSTIKLookup[2] = 'Audiobook';
|
1633 |
+
$QuicktimeSTIKLookup[5] = 'Whacked Bookmark';
|
1634 |
+
$QuicktimeSTIKLookup[6] = 'Music Video';
|
1635 |
+
$QuicktimeSTIKLookup[9] = 'Short Film';
|
1636 |
+
$QuicktimeSTIKLookup[10] = 'TV Show';
|
1637 |
+
$QuicktimeSTIKLookup[11] = 'Booklet';
|
1638 |
+
$QuicktimeSTIKLookup[14] = 'Ringtone';
|
1639 |
+
$QuicktimeSTIKLookup[21] = 'Podcast';
|
1640 |
+
}
|
1641 |
+
return (isset($QuicktimeSTIKLookup[$stik]) ? $QuicktimeSTIKLookup[$stik] : 'invalid');
|
1642 |
+
}
|
1643 |
+
|
1644 |
+
function QuicktimeIODSaudioProfileName($audio_profile_id) {
|
1645 |
+
static $QuicktimeIODSaudioProfileNameLookup = array();
|
1646 |
+
if (empty($QuicktimeIODSaudioProfileNameLookup)) {
|
1647 |
+
$QuicktimeIODSaudioProfileNameLookup = array(
|
1648 |
+
0x00 => 'ISO Reserved (0x00)',
|
1649 |
+
0x01 => 'Main Audio Profile @ Level 1',
|
1650 |
+
0x02 => 'Main Audio Profile @ Level 2',
|
1651 |
+
0x03 => 'Main Audio Profile @ Level 3',
|
1652 |
+
0x04 => 'Main Audio Profile @ Level 4',
|
1653 |
+
0x05 => 'Scalable Audio Profile @ Level 1',
|
1654 |
+
0x06 => 'Scalable Audio Profile @ Level 2',
|
1655 |
+
0x07 => 'Scalable Audio Profile @ Level 3',
|
1656 |
+
0x08 => 'Scalable Audio Profile @ Level 4',
|
1657 |
+
0x09 => 'Speech Audio Profile @ Level 1',
|
1658 |
+
0x0A => 'Speech Audio Profile @ Level 2',
|
1659 |
+
0x0B => 'Synthetic Audio Profile @ Level 1',
|
1660 |
+
0x0C => 'Synthetic Audio Profile @ Level 2',
|
1661 |
+
0x0D => 'Synthetic Audio Profile @ Level 3',
|
1662 |
+
0x0E => 'High Quality Audio Profile @ Level 1',
|
1663 |
+
0x0F => 'High Quality Audio Profile @ Level 2',
|
1664 |
+
0x10 => 'High Quality Audio Profile @ Level 3',
|
1665 |
+
0x11 => 'High Quality Audio Profile @ Level 4',
|
1666 |
+
0x12 => 'High Quality Audio Profile @ Level 5',
|
1667 |
+
0x13 => 'High Quality Audio Profile @ Level 6',
|
1668 |
+
0x14 => 'High Quality Audio Profile @ Level 7',
|
1669 |
+
0x15 => 'High Quality Audio Profile @ Level 8',
|
1670 |
+
0x16 => 'Low Delay Audio Profile @ Level 1',
|
1671 |
+
0x17 => 'Low Delay Audio Profile @ Level 2',
|
1672 |
+
0x18 => 'Low Delay Audio Profile @ Level 3',
|
1673 |
+
0x19 => 'Low Delay Audio Profile @ Level 4',
|
1674 |
+
0x1A => 'Low Delay Audio Profile @ Level 5',
|
1675 |
+
0x1B => 'Low Delay Audio Profile @ Level 6',
|
1676 |
+
0x1C => 'Low Delay Audio Profile @ Level 7',
|
1677 |
+
0x1D => 'Low Delay Audio Profile @ Level 8',
|
1678 |
+
0x1E => 'Natural Audio Profile @ Level 1',
|
1679 |
+
0x1F => 'Natural Audio Profile @ Level 2',
|
1680 |
+
0x20 => 'Natural Audio Profile @ Level 3',
|
1681 |
+
0x21 => 'Natural Audio Profile @ Level 4',
|
1682 |
+
0x22 => 'Mobile Audio Internetworking Profile @ Level 1',
|
1683 |
+
0x23 => 'Mobile Audio Internetworking Profile @ Level 2',
|
1684 |
+
0x24 => 'Mobile Audio Internetworking Profile @ Level 3',
|
1685 |
+
0x25 => 'Mobile Audio Internetworking Profile @ Level 4',
|
1686 |
+
0x26 => 'Mobile Audio Internetworking Profile @ Level 5',
|
1687 |
+
0x27 => 'Mobile Audio Internetworking Profile @ Level 6',
|
1688 |
+
0x28 => 'AAC Profile @ Level 1',
|
1689 |
+
0x29 => 'AAC Profile @ Level 2',
|
1690 |
+
0x2A => 'AAC Profile @ Level 4',
|
1691 |
+
0x2B => 'AAC Profile @ Level 5',
|
1692 |
+
0x2C => 'High Efficiency AAC Profile @ Level 2',
|
1693 |
+
0x2D => 'High Efficiency AAC Profile @ Level 3',
|
1694 |
+
0x2E => 'High Efficiency AAC Profile @ Level 4',
|
1695 |
+
0x2F => 'High Efficiency AAC Profile @ Level 5',
|
1696 |
+
0xFE => 'Not part of MPEG-4 audio profiles',
|
1697 |
+
0xFF => 'No audio capability required',
|
1698 |
+
);
|
1699 |
+
}
|
1700 |
+
return (isset($QuicktimeIODSaudioProfileNameLookup[$audio_profile_id]) ? $QuicktimeIODSaudioProfileNameLookup[$audio_profile_id] : 'ISO Reserved / User Private');
|
1701 |
+
}
|
1702 |
+
|
1703 |
+
|
1704 |
+
function QuicktimeIODSvideoProfileName($video_profile_id) {
|
1705 |
+
static $QuicktimeIODSvideoProfileNameLookup = array();
|
1706 |
+
if (empty($QuicktimeIODSvideoProfileNameLookup)) {
|
1707 |
+
$QuicktimeIODSvideoProfileNameLookup = array(
|
1708 |
+
0x00 => 'Reserved (0x00) Profile',
|
1709 |
+
0x01 => 'Simple Profile @ Level 1',
|
1710 |
+
0x02 => 'Simple Profile @ Level 2',
|
1711 |
+
0x03 => 'Simple Profile @ Level 3',
|
1712 |
+
0x08 => 'Simple Profile @ Level 0',
|
1713 |
+
0x10 => 'Simple Scalable Profile @ Level 0',
|
1714 |
+
0x11 => 'Simple Scalable Profile @ Level 1',
|
1715 |
+
0x12 => 'Simple Scalable Profile @ Level 2',
|
1716 |
+
0x15 => 'AVC/H264 Profile',
|
1717 |
+
0x21 => 'Core Profile @ Level 1',
|
1718 |
+
0x22 => 'Core Profile @ Level 2',
|
1719 |
+
0x32 => 'Main Profile @ Level 2',
|
1720 |
+
0x33 => 'Main Profile @ Level 3',
|
1721 |
+
0x34 => 'Main Profile @ Level 4',
|
1722 |
+
0x42 => 'N-bit Profile @ Level 2',
|
1723 |
+
0x51 => 'Scalable Texture Profile @ Level 1',
|
1724 |
+
0x61 => 'Simple Face Animation Profile @ Level 1',
|
1725 |
+
0x62 => 'Simple Face Animation Profile @ Level 2',
|
1726 |
+
0x63 => 'Simple FBA Profile @ Level 1',
|
1727 |
+
0x64 => 'Simple FBA Profile @ Level 2',
|
1728 |
+
0x71 => 'Basic Animated Texture Profile @ Level 1',
|
1729 |
+
0x72 => 'Basic Animated Texture Profile @ Level 2',
|
1730 |
+
0x81 => 'Hybrid Profile @ Level 1',
|
1731 |
+
0x82 => 'Hybrid Profile @ Level 2',
|
1732 |
+
0x91 => 'Advanced Real Time Simple Profile @ Level 1',
|
1733 |
+
0x92 => 'Advanced Real Time Simple Profile @ Level 2',
|
1734 |
+
0x93 => 'Advanced Real Time Simple Profile @ Level 3',
|
1735 |
+
0x94 => 'Advanced Real Time Simple Profile @ Level 4',
|
1736 |
+
0xA1 => 'Core Scalable Profile @ Level1',
|
1737 |
+
0xA2 => 'Core Scalable Profile @ Level2',
|
1738 |
+
0xA3 => 'Core Scalable Profile @ Level3',
|
1739 |
+
0xB1 => 'Advanced Coding Efficiency Profile @ Level 1',
|
1740 |
+
0xB2 => 'Advanced Coding Efficiency Profile @ Level 2',
|
1741 |
+
0xB3 => 'Advanced Coding Efficiency Profile @ Level 3',
|
1742 |
+
0xB4 => 'Advanced Coding Efficiency Profile @ Level 4',
|
1743 |
+
0xC1 => 'Advanced Core Profile @ Level 1',
|
1744 |
+
0xC2 => 'Advanced Core Profile @ Level 2',
|
1745 |
+
0xD1 => 'Advanced Scalable Texture @ Level1',
|
1746 |
+
0xD2 => 'Advanced Scalable Texture @ Level2',
|
1747 |
+
0xE1 => 'Simple Studio Profile @ Level 1',
|
1748 |
+
0xE2 => 'Simple Studio Profile @ Level 2',
|
1749 |
+
0xE3 => 'Simple Studio Profile @ Level 3',
|
1750 |
+
0xE4 => 'Simple Studio Profile @ Level 4',
|
1751 |
+
0xE5 => 'Core Studio Profile @ Level 1',
|
1752 |
+
0xE6 => 'Core Studio Profile @ Level 2',
|
1753 |
+
0xE7 => 'Core Studio Profile @ Level 3',
|
1754 |
+
0xE8 => 'Core Studio Profile @ Level 4',
|
1755 |
+
0xF0 => 'Advanced Simple Profile @ Level 0',
|
1756 |
+
0xF1 => 'Advanced Simple Profile @ Level 1',
|
1757 |
+
0xF2 => 'Advanced Simple Profile @ Level 2',
|
1758 |
+
0xF3 => 'Advanced Simple Profile @ Level 3',
|
1759 |
+
0xF4 => 'Advanced Simple Profile @ Level 4',
|
1760 |
+
0xF5 => 'Advanced Simple Profile @ Level 5',
|
1761 |
+
0xF7 => 'Advanced Simple Profile @ Level 3b',
|
1762 |
+
0xF8 => 'Fine Granularity Scalable Profile @ Level 0',
|
1763 |
+
0xF9 => 'Fine Granularity Scalable Profile @ Level 1',
|
1764 |
+
0xFA => 'Fine Granularity Scalable Profile @ Level 2',
|
1765 |
+
0xFB => 'Fine Granularity Scalable Profile @ Level 3',
|
1766 |
+
0xFC => 'Fine Granularity Scalable Profile @ Level 4',
|
1767 |
+
0xFD => 'Fine Granularity Scalable Profile @ Level 5',
|
1768 |
+
0xFE => 'Not part of MPEG-4 Visual profiles',
|
1769 |
+
0xFF => 'No visual capability required',
|
1770 |
+
);
|
1771 |
+
}
|
1772 |
+
return (isset($QuicktimeIODSvideoProfileNameLookup[$video_profile_id]) ? $QuicktimeIODSvideoProfileNameLookup[$video_profile_id] : 'ISO Reserved Profile');
|
1773 |
+
}
|
1774 |
+
|
1775 |
+
|
1776 |
+
function QuicktimeContentRatingLookup($rtng) {
|
1777 |
+
static $QuicktimeContentRatingLookup = array();
|
1778 |
+
if (empty($QuicktimeContentRatingLookup)) {
|
1779 |
+
$QuicktimeContentRatingLookup[0] = 'None';
|
1780 |
+
$QuicktimeContentRatingLookup[2] = 'Clean';
|
1781 |
+
$QuicktimeContentRatingLookup[4] = 'Explicit';
|
1782 |
+
}
|
1783 |
+
return (isset($QuicktimeContentRatingLookup[$rtng]) ? $QuicktimeContentRatingLookup[$rtng] : 'invalid');
|
1784 |
+
}
|
1785 |
+
|
1786 |
+
function QuicktimeStoreAccountTypeLookup($akid) {
|
1787 |
+
static $QuicktimeStoreAccountTypeLookup = array();
|
1788 |
+
if (empty($QuicktimeStoreAccountTypeLookup)) {
|
1789 |
+
$QuicktimeStoreAccountTypeLookup[0] = 'iTunes';
|
1790 |
+
$QuicktimeStoreAccountTypeLookup[1] = 'AOL';
|
1791 |
+
}
|
1792 |
+
return (isset($QuicktimeStoreAccountTypeLookup[$akid]) ? $QuicktimeStoreAccountTypeLookup[$akid] : 'invalid');
|
1793 |
+
}
|
1794 |
+
|
1795 |
+
function QuicktimeStoreFrontCodeLookup($sfid) {
|
1796 |
+
static $QuicktimeStoreFrontCodeLookup = array();
|
1797 |
+
if (empty($QuicktimeStoreFrontCodeLookup)) {
|
1798 |
+
$QuicktimeStoreFrontCodeLookup[143460] = 'Australia';
|
1799 |
+
$QuicktimeStoreFrontCodeLookup[143445] = 'Austria';
|
1800 |
+
$QuicktimeStoreFrontCodeLookup[143446] = 'Belgium';
|
1801 |
+
$QuicktimeStoreFrontCodeLookup[143455] = 'Canada';
|
1802 |
+
$QuicktimeStoreFrontCodeLookup[143458] = 'Denmark';
|
1803 |
+
$QuicktimeStoreFrontCodeLookup[143447] = 'Finland';
|
1804 |
+
$QuicktimeStoreFrontCodeLookup[143442] = 'France';
|
1805 |
+
$QuicktimeStoreFrontCodeLookup[143443] = 'Germany';
|
1806 |
+
$QuicktimeStoreFrontCodeLookup[143448] = 'Greece';
|
1807 |
+
$QuicktimeStoreFrontCodeLookup[143449] = 'Ireland';
|
1808 |
+
$QuicktimeStoreFrontCodeLookup[143450] = 'Italy';
|
1809 |
+
$QuicktimeStoreFrontCodeLookup[143462] = 'Japan';
|
1810 |
+
$QuicktimeStoreFrontCodeLookup[143451] = 'Luxembourg';
|
1811 |
+
$QuicktimeStoreFrontCodeLookup[143452] = 'Netherlands';
|
1812 |
+
$QuicktimeStoreFrontCodeLookup[143461] = 'New Zealand';
|
1813 |
+
$QuicktimeStoreFrontCodeLookup[143457] = 'Norway';
|
1814 |
+
$QuicktimeStoreFrontCodeLookup[143453] = 'Portugal';
|
1815 |
+
$QuicktimeStoreFrontCodeLookup[143454] = 'Spain';
|
1816 |
+
$QuicktimeStoreFrontCodeLookup[143456] = 'Sweden';
|
1817 |
+
$QuicktimeStoreFrontCodeLookup[143459] = 'Switzerland';
|
1818 |
+
$QuicktimeStoreFrontCodeLookup[143444] = 'United Kingdom';
|
1819 |
+
$QuicktimeStoreFrontCodeLookup[143441] = 'United States';
|
1820 |
+
}
|
1821 |
+
return (isset($QuicktimeStoreFrontCodeLookup[$sfid]) ? $QuicktimeStoreFrontCodeLookup[$sfid] : 'invalid');
|
1822 |
+
}
|
1823 |
+
|
1824 |
+
function QuicktimeParseNikonNCTG($atom_data) {
|
1825 |
+
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
|
1826 |
+
// Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
|
1827 |
+
// Data is stored as records of:
|
1828 |
+
// * 4 bytes record type
|
1829 |
+
// * 2 bytes size of data field type:
|
1830 |
+
// 0x0001 = flag (size field *= 1-byte)
|
1831 |
+
// 0x0002 = char (size field *= 1-byte)
|
1832 |
+
// 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
|
1833 |
+
// 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
|
1834 |
+
// 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
|
1835 |
+
// 0x0007 = bytes (size field *= 1-byte), values are stored as ??????
|
1836 |
+
// 0x0008 = ????? (size field *= 2-byte), values are stored as ??????
|
1837 |
+
// * 2 bytes data size field
|
1838 |
+
// * ? bytes data (string data may be null-padded; datestamp fields are in the format "2011:05:25 20:24:15")
|
1839 |
+
// all integers are stored BigEndian
|
1840 |
+
|
1841 |
+
$NCTGtagName = array(
|
1842 |
+
0x00000001 => 'Make',
|
1843 |
+
0x00000002 => 'Model',
|
1844 |
+
0x00000003 => 'Software',
|
1845 |
+
0x00000011 => 'CreateDate',
|
1846 |
+
0x00000012 => 'DateTimeOriginal',
|
1847 |
+
0x00000013 => 'FrameCount',
|
1848 |
+
0x00000016 => 'FrameRate',
|
1849 |
+
0x00000022 => 'FrameWidth',
|
1850 |
+
0x00000023 => 'FrameHeight',
|
1851 |
+
0x00000032 => 'AudioChannels',
|
1852 |
+
0x00000033 => 'AudioBitsPerSample',
|
1853 |
+
0x00000034 => 'AudioSampleRate',
|
1854 |
+
0x02000001 => 'MakerNoteVersion',
|
1855 |
+
0x02000005 => 'WhiteBalance',
|
1856 |
+
0x0200000b => 'WhiteBalanceFineTune',
|
1857 |
+
0x0200001e => 'ColorSpace',
|
1858 |
+
0x02000023 => 'PictureControlData',
|
1859 |
+
0x02000024 => 'WorldTime',
|
1860 |
+
0x02000032 => 'UnknownInfo',
|
1861 |
+
0x02000083 => 'LensType',
|
1862 |
+
0x02000084 => 'Lens',
|
1863 |
+
);
|
1864 |
+
|
1865 |
+
$offset = 0;
|
1866 |
+
$datalength = strlen($atom_data);
|
1867 |
+
$parsed = array();
|
1868 |
+
while ($offset < $datalength) {
|
1869 |
+
//echo getid3_lib::PrintHexBytes(substr($atom_data, $offset, 4)).'<br>';
|
1870 |
+
$record_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4)); $offset += 4;
|
1871 |
+
$data_size_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2;
|
1872 |
+
$data_size = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2;
|
1873 |
+
switch ($data_size_type) {
|
1874 |
+
case 0x0001: // 0x0001 = flag (size field *= 1-byte)
|
1875 |
+
$data = getid3_lib::BigEndian2Int(substr($atom_data, $offset, $data_size * 1));
|
1876 |
+
$offset += ($data_size * 1);
|
1877 |
+
break;
|
1878 |
+
case 0x0002: // 0x0002 = char (size field *= 1-byte)
|
1879 |
+
$data = substr($atom_data, $offset, $data_size * 1);
|
1880 |
+
$offset += ($data_size * 1);
|
1881 |
+
$data = rtrim($data, "\x00");
|
1882 |
+
break;
|
1883 |
+
case 0x0003: // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
|
1884 |
+
$data = '';
|
1885 |
+
for ($i = $data_size - 1; $i >= 0; $i--) {
|
1886 |
+
$data .= substr($atom_data, $offset + ($i * 2), 2);
|
1887 |
+
}
|
1888 |
+
$data = getid3_lib::BigEndian2Int($data);
|
1889 |
+
$offset += ($data_size * 2);
|
1890 |
+
break;
|
1891 |
+
case 0x0004: // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
|
1892 |
+
$data = '';
|
1893 |
+
for ($i = $data_size - 1; $i >= 0; $i--) {
|
1894 |
+
$data .= substr($atom_data, $offset + ($i * 4), 4);
|
1895 |
+
}
|
1896 |
+
$data = getid3_lib::BigEndian2Int($data);
|
1897 |
+
$offset += ($data_size * 4);
|
1898 |
+
break;
|
1899 |
+
case 0x0005: // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
|
1900 |
+
$data = array();
|
1901 |
+
for ($i = 0; $i < $data_size; $i++) {
|
1902 |
+
$numerator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 0, 4));
|
1903 |
+
$denomninator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 4, 4));
|
1904 |
+
if ($denomninator == 0) {
|
1905 |
+
$data[$i] = false;
|
1906 |
+
} else {
|
1907 |
+
$data[$i] = (double) $numerator / $denomninator;
|
1908 |
+
}
|
1909 |
+
}
|
1910 |
+
$offset += (8 * $data_size);
|
1911 |
+
if (count($data) == 1) {
|
1912 |
+
$data = $data[0];
|
1913 |
+
}
|
1914 |
+
break;
|
1915 |
+
case 0x0007: // 0x0007 = bytes (size field *= 1-byte), values are stored as ??????
|
1916 |
+
$data = substr($atom_data, $offset, $data_size * 1);
|
1917 |
+
$offset += ($data_size * 1);
|
1918 |
+
break;
|
1919 |
+
case 0x0008: // 0x0008 = ????? (size field *= 2-byte), values are stored as ??????
|
1920 |
+
$data = substr($atom_data, $offset, $data_size * 2);
|
1921 |
+
$offset += ($data_size * 2);
|
1922 |
+
break;
|
1923 |
+
default:
|
1924 |
+
echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br>';
|
1925 |
+
break 2;
|
1926 |
+
}
|
1927 |
+
|
1928 |
+
switch ($record_type) {
|
1929 |
+
case 0x00000011: // CreateDate
|
1930 |
+
case 0x00000012: // DateTimeOriginal
|
1931 |
+
$data = strtotime($data);
|
1932 |
+
break;
|
1933 |
+
case 0x0200001e: // ColorSpace
|
1934 |
+
switch ($data) {
|
1935 |
+
case 1:
|
1936 |
+
$data = 'sRGB';
|
1937 |
+
break;
|
1938 |
+
case 2:
|
1939 |
+
$data = 'Adobe RGB';
|
1940 |
+
break;
|
1941 |
+
}
|
1942 |
+
break;
|
1943 |
+
case 0x02000023: // PictureControlData
|
1944 |
+
$PictureControlAdjust = array(0=>'default', 1=>'quick', 2=>'full');
|
1945 |
+
$FilterEffect = array(0x80=>'off', 0x81=>'yellow', 0x82=>'orange', 0x83=>'red', 0x84=>'green', 0xff=>'n/a');
|
1946 |
+
$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');
|
1947 |
+
$data = array(
|
1948 |
+
'PictureControlVersion' => substr($data, 0, 4),
|
1949 |
+
'PictureControlName' => rtrim(substr($data, 4, 20), "\x00"),
|
1950 |
+
'PictureControlBase' => rtrim(substr($data, 24, 20), "\x00"),
|
1951 |
+
//'?' => substr($data, 44, 4),
|
1952 |
+
'PictureControlAdjust' => $PictureControlAdjust[ord(substr($data, 48, 1))],
|
1953 |
+
'PictureControlQuickAdjust' => ord(substr($data, 49, 1)),
|
1954 |
+
'Sharpness' => ord(substr($data, 50, 1)),
|
1955 |
+
'Contrast' => ord(substr($data, 51, 1)),
|
1956 |
+
'Brightness' => ord(substr($data, 52, 1)),
|
1957 |
+
'Saturation' => ord(substr($data, 53, 1)),
|
1958 |
+
'HueAdjustment' => ord(substr($data, 54, 1)),
|
1959 |
+
'FilterEffect' => $FilterEffect[ord(substr($data, 55, 1))],
|
1960 |
+
'ToningEffect' => $ToningEffect[ord(substr($data, 56, 1))],
|
1961 |
+
'ToningSaturation' => ord(substr($data, 57, 1)),
|
1962 |
+
);
|
1963 |
+
break;
|
1964 |
+
case 0x02000024: // WorldTime
|
1965 |
+
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#WorldTime
|
1966 |
+
// timezone is stored as offset from GMT in minutes
|
1967 |
+
$timezone = getid3_lib::BigEndian2Int(substr($data, 0, 2));
|
1968 |
+
if ($timezone & 0x8000) {
|
1969 |
+
$timezone = 0 - (0x10000 - $timezone);
|
1970 |
+
}
|
1971 |
+
$timezone /= 60;
|
1972 |
+
|
1973 |
+
$dst = (bool) getid3_lib::BigEndian2Int(substr($data, 2, 1));
|
1974 |
+
switch (getid3_lib::BigEndian2Int(substr($data, 3, 1))) {
|
1975 |
+
case 2:
|
1976 |
+
$datedisplayformat = 'D/M/Y'; break;
|
1977 |
+
case 1:
|
1978 |
+
$datedisplayformat = 'M/D/Y'; break;
|
1979 |
+
case 0:
|
1980 |
+
default:
|
1981 |
+
$datedisplayformat = 'Y/M/D'; break;
|
1982 |
+
}
|
1983 |
+
|
1984 |
+
$data = array('timezone'=>floatval($timezone), 'dst'=>$dst, 'display'=>$datedisplayformat);
|
1985 |
+
break;
|
1986 |
+
case 0x02000083: // LensType
|
1987 |
+
$data = array(
|
1988 |
+
//'_' => $data,
|
1989 |
+
'mf' => (bool) ($data & 0x01),
|
1990 |
+
'd' => (bool) ($data & 0x02),
|
1991 |
+
'g' => (bool) ($data & 0x04),
|
1992 |
+
'vr' => (bool) ($data & 0x08),
|
1993 |
+
);
|
1994 |
+
break;
|
1995 |
+
}
|
1996 |
+
$tag_name = (isset($NCTGtagName[$record_type]) ? $NCTGtagName[$record_type] : '0x'.str_pad(dechex($record_type), 8, '0', STR_PAD_LEFT));
|
1997 |
+
$parsed[$tag_name] = $data;
|
1998 |
+
}
|
1999 |
+
return $parsed;
|
2000 |
+
}
|
2001 |
+
|
2002 |
+
|
2003 |
+
function CopyToAppropriateCommentsSection($keyname, $data, $boxname='') {
|
2004 |
+
static $handyatomtranslatorarray = array();
|
2005 |
+
if (empty($handyatomtranslatorarray)) {
|
2006 |
+
$handyatomtranslatorarray['�cpy'] = 'copyright';
|
2007 |
+
$handyatomtranslatorarray['�day'] = 'creation_date'; // iTunes 4.0
|
2008 |
+
$handyatomtranslatorarray['�dir'] = 'director';
|
2009 |
+
$handyatomtranslatorarray['�ed1'] = 'edit1';
|
2010 |
+
$handyatomtranslatorarray['�ed2'] = 'edit2';
|
2011 |
+
$handyatomtranslatorarray['�ed3'] = 'edit3';
|
2012 |
+
$handyatomtranslatorarray['�ed4'] = 'edit4';
|
2013 |
+
$handyatomtranslatorarray['�ed5'] = 'edit5';
|
2014 |
+
$handyatomtranslatorarray['�ed6'] = 'edit6';
|
2015 |
+
$handyatomtranslatorarray['�ed7'] = 'edit7';
|
2016 |
+
$handyatomtranslatorarray['�ed8'] = 'edit8';
|
2017 |
+
$handyatomtranslatorarray['�ed9'] = 'edit9';
|
2018 |
+
$handyatomtranslatorarray['�fmt'] = 'format';
|
2019 |
+
$handyatomtranslatorarray['�inf'] = 'information';
|
2020 |
+
$handyatomtranslatorarray['�prd'] = 'producer';
|
2021 |
+
$handyatomtranslatorarray['�prf'] = 'performers';
|
2022 |
+
$handyatomtranslatorarray['�req'] = 'system_requirements';
|
2023 |
+
$handyatomtranslatorarray['�src'] = 'source_credit';
|
2024 |
+
$handyatomtranslatorarray['�wrt'] = 'writer';
|
2025 |
+
|
2026 |
+
// http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
|
2027 |
+
$handyatomtranslatorarray['�nam'] = 'title'; // iTunes 4.0
|
2028 |
+
$handyatomtranslatorarray['�cmt'] = 'comment'; // iTunes 4.0
|
2029 |
+
$handyatomtranslatorarray['�wrn'] = 'warning';
|
2030 |
+
$handyatomtranslatorarray['�hst'] = 'host_computer';
|
2031 |
+
$handyatomtranslatorarray['�mak'] = 'make';
|
2032 |
+
$handyatomtranslatorarray['�mod'] = 'model';
|
2033 |
+
$handyatomtranslatorarray['�PRD'] = 'product';
|
2034 |
+
$handyatomtranslatorarray['�swr'] = 'software';
|
2035 |
+
$handyatomtranslatorarray['�aut'] = 'author';
|
2036 |
+
$handyatomtranslatorarray['�ART'] = 'artist';
|
2037 |
+
$handyatomtranslatorarray['�trk'] = 'track';
|
2038 |
+
$handyatomtranslatorarray['�alb'] = 'album'; // iTunes 4.0
|
2039 |
+
$handyatomtranslatorarray['�com'] = 'comment';
|
2040 |
+
$handyatomtranslatorarray['�gen'] = 'genre'; // iTunes 4.0
|
2041 |
+
$handyatomtranslatorarray['�ope'] = 'composer';
|
2042 |
+
$handyatomtranslatorarray['�url'] = 'url';
|
2043 |
+
$handyatomtranslatorarray['�enc'] = 'encoder';
|
2044 |
+
|
2045 |
+
// http://atomicparsley.sourceforge.net/mpeg-4files.html
|
2046 |
+
$handyatomtranslatorarray['�art'] = 'artist'; // iTunes 4.0
|
2047 |
+
$handyatomtranslatorarray['aART'] = 'album_artist';
|
2048 |
+
$handyatomtranslatorarray['trkn'] = 'track_number'; // iTunes 4.0
|
2049 |
+
$handyatomtranslatorarray['disk'] = 'disc_number'; // iTunes 4.0
|
2050 |
+
$handyatomtranslatorarray['gnre'] = 'genre'; // iTunes 4.0
|
2051 |
+
$handyatomtranslatorarray['�too'] = 'encoder'; // iTunes 4.0
|
2052 |
+
$handyatomtranslatorarray['tmpo'] = 'bpm'; // iTunes 4.0
|
2053 |
+
$handyatomtranslatorarray['cprt'] = 'copyright'; // iTunes 4.0?
|
2054 |
+
$handyatomtranslatorarray['cpil'] = 'compilation'; // iTunes 4.0
|
2055 |
+
$handyatomtranslatorarray['covr'] = 'picture'; // iTunes 4.0
|
2056 |
+
$handyatomtranslatorarray['rtng'] = 'rating'; // iTunes 4.0
|
2057 |
+
$handyatomtranslatorarray['�grp'] = 'grouping'; // iTunes 4.2
|
2058 |
+
$handyatomtranslatorarray['stik'] = 'stik'; // iTunes 4.9
|
2059 |
+
$handyatomtranslatorarray['pcst'] = 'podcast'; // iTunes 4.9
|
2060 |
+
$handyatomtranslatorarray['catg'] = 'category'; // iTunes 4.9
|
2061 |
+
$handyatomtranslatorarray['keyw'] = 'keyword'; // iTunes 4.9
|
2062 |
+
$handyatomtranslatorarray['purl'] = 'podcast_url'; // iTunes 4.9
|
2063 |
+
$handyatomtranslatorarray['egid'] = 'episode_guid'; // iTunes 4.9
|
2064 |
+
$handyatomtranslatorarray['desc'] = 'description'; // iTunes 5.0
|
2065 |
+
$handyatomtranslatorarray['�lyr'] = 'lyrics'; // iTunes 5.0
|
2066 |
+
$handyatomtranslatorarray['tvnn'] = 'tv_network_name'; // iTunes 6.0
|
2067 |
+
$handyatomtranslatorarray['tvsh'] = 'tv_show_name'; // iTunes 6.0
|
2068 |
+
$handyatomtranslatorarray['tvsn'] = 'tv_season'; // iTunes 6.0
|
2069 |
+
$handyatomtranslatorarray['tves'] = 'tv_episode'; // iTunes 6.0
|
2070 |
+
$handyatomtranslatorarray['purd'] = 'purchase_date'; // iTunes 6.0.2
|
2071 |
+
$handyatomtranslatorarray['pgap'] = 'gapless_playback'; // iTunes 7.0
|
2072 |
+
|
2073 |
+
// http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
|
2074 |
+
|
2075 |
+
|
2076 |
+
|
2077 |
+
// boxnames:
|
2078 |
+
$handyatomtranslatorarray['iTunSMPB'] = 'iTunSMPB';
|
2079 |
+
$handyatomtranslatorarray['iTunNORM'] = 'iTunNORM';
|
2080 |
+
$handyatomtranslatorarray['Encoding Params'] = 'Encoding Params';
|
2081 |
+
$handyatomtranslatorarray['replaygain_track_gain'] = 'replaygain_track_gain';
|
2082 |
+
$handyatomtranslatorarray['replaygain_track_peak'] = 'replaygain_track_peak';
|
2083 |
+
$handyatomtranslatorarray['replaygain_track_minmax'] = 'replaygain_track_minmax';
|
2084 |
+
$handyatomtranslatorarray['MusicIP PUID'] = 'MusicIP PUID';
|
2085 |
+
$handyatomtranslatorarray['MusicBrainz Artist Id'] = 'MusicBrainz Artist Id';
|
2086 |
+
$handyatomtranslatorarray['MusicBrainz Album Id'] = 'MusicBrainz Album Id';
|
2087 |
+
$handyatomtranslatorarray['MusicBrainz Album Artist Id'] = 'MusicBrainz Album Artist Id';
|
2088 |
+
$handyatomtranslatorarray['MusicBrainz Track Id'] = 'MusicBrainz Track Id';
|
2089 |
+
$handyatomtranslatorarray['MusicBrainz Disc Id'] = 'MusicBrainz Disc Id';
|
2090 |
+
}
|
2091 |
+
$info = &$this->getid3->info;
|
2092 |
+
$comment_key = '';
|
2093 |
+
if ($boxname && ($boxname != $keyname) && isset($handyatomtranslatorarray[$boxname])) {
|
2094 |
+
$comment_key = $handyatomtranslatorarray[$boxname];
|
2095 |
+
} elseif (isset($handyatomtranslatorarray[$keyname])) {
|
2096 |
+
$comment_key = $handyatomtranslatorarray[$keyname];
|
2097 |
+
}
|
2098 |
+
if ($comment_key) {
|
2099 |
+
if ($comment_key == 'picture') {
|
2100 |
+
if (!is_array($data)) {
|
2101 |
+
$image_mime = '';
|
2102 |
+
if (preg_match('#^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A#', $data)) {
|
2103 |
+
$image_mime = 'image/png';
|
2104 |
+
} elseif (preg_match('#^\xFF\xD8\xFF#', $data)) {
|
2105 |
+
$image_mime = 'image/jpeg';
|
2106 |
+
} elseif (preg_match('#^GIF#', $data)) {
|
2107 |
+
$image_mime = 'image/gif';
|
2108 |
+
} elseif (preg_match('#^BM#', $data)) {
|
2109 |
+
$image_mime = 'image/bmp';
|
2110 |
+
}
|
2111 |
+
$data = array('data'=>$data, 'image_mime'=>$image_mime);
|
2112 |
+
}
|
2113 |
+
}
|
2114 |
+
$info['quicktime']['comments'][$comment_key][] = $data;
|
2115 |
+
}
|
2116 |
+
return true;
|
2117 |
+
}
|
2118 |
+
|
2119 |
+
function NoNullString($nullterminatedstring) {
|
2120 |
+
// remove the single null terminator on null terminated strings
|
2121 |
+
if (substr($nullterminatedstring, strlen($nullterminatedstring) - 1, 1) === "\x00") {
|
2122 |
+
return substr($nullterminatedstring, 0, strlen($nullterminatedstring) - 1);
|
2123 |
+
}
|
2124 |
+
return $nullterminatedstring;
|
2125 |
+
}
|
2126 |
+
|
2127 |
+
function Pascal2String($pascalstring) {
|
2128 |
+
// Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string
|
2129 |
+
return substr($pascalstring, 1);
|
2130 |
+
}
|
2131 |
+
|
2132 |
+
}
|
2133 |
+
|
2134 |
+
?>
|
includes/lib/getid3/module.audio.aac.php
ADDED
@@ -0,0 +1,515 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
/////////////////////////////////////////////////////////////////
|
7 |
+
// See readme.txt for more details //
|
8 |
+
/////////////////////////////////////////////////////////////////
|
9 |
+
// //
|
10 |
+
// module.audio.aac.php //
|
11 |
+
// module for analyzing AAC Audio files //
|
12 |
+
// dependencies: NONE //
|
13 |
+
// ///
|
14 |
+
/////////////////////////////////////////////////////////////////
|
15 |
+
|
16 |
+
|
17 |
+
class getid3_aac extends getid3_handler
|
18 |
+
{
|
19 |
+
function Analyze() {
|
20 |
+
$info = &$this->getid3->info;
|
21 |
+
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
22 |
+
if (fread($this->getid3->fp, 4) == 'ADIF') {
|
23 |
+
$this->getAACADIFheaderFilepointer();
|
24 |
+
} else {
|
25 |
+
$this->getAACADTSheaderFilepointer();
|
26 |
+
}
|
27 |
+
return true;
|
28 |
+
}
|
29 |
+
|
30 |
+
|
31 |
+
|
32 |
+
function getAACADIFheaderFilepointer() {
|
33 |
+
$info = &$this->getid3->info;
|
34 |
+
$info['fileformat'] = 'aac';
|
35 |
+
$info['audio']['dataformat'] = 'aac';
|
36 |
+
$info['audio']['lossless'] = false;
|
37 |
+
|
38 |
+
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
39 |
+
$AACheader = fread($this->getid3->fp, 1024);
|
40 |
+
$offset = 0;
|
41 |
+
|
42 |
+
if (substr($AACheader, 0, 4) == 'ADIF') {
|
43 |
+
|
44 |
+
// http://faac.sourceforge.net/wiki/index.php?page=ADIF
|
45 |
+
|
46 |
+
// http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
|
47 |
+
// adif_header() {
|
48 |
+
// adif_id 32
|
49 |
+
// copyright_id_present 1
|
50 |
+
// if( copyright_id_present )
|
51 |
+
// copyright_id 72
|
52 |
+
// original_copy 1
|
53 |
+
// home 1
|
54 |
+
// bitstream_type 1
|
55 |
+
// bitrate 23
|
56 |
+
// num_program_config_elements 4
|
57 |
+
// for (i = 0; i < num_program_config_elements + 1; i++ ) {
|
58 |
+
// if( bitstream_type == '0' )
|
59 |
+
// adif_buffer_fullness 20
|
60 |
+
// program_config_element()
|
61 |
+
// }
|
62 |
+
// }
|
63 |
+
|
64 |
+
$AACheaderBitstream = getid3_lib::BigEndian2Bin($AACheader);
|
65 |
+
$bitoffset = 0;
|
66 |
+
|
67 |
+
$info['aac']['header_type'] = 'ADIF';
|
68 |
+
$bitoffset += 32;
|
69 |
+
$info['aac']['header']['mpeg_version'] = 4;
|
70 |
+
|
71 |
+
$info['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
72 |
+
$bitoffset += 1;
|
73 |
+
if ($info['aac']['header']['copyright']) {
|
74 |
+
$info['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72));
|
75 |
+
$bitoffset += 72;
|
76 |
+
}
|
77 |
+
$info['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
78 |
+
$bitoffset += 1;
|
79 |
+
$info['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
80 |
+
$bitoffset += 1;
|
81 |
+
$info['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
82 |
+
$bitoffset += 1;
|
83 |
+
if ($info['aac']['header']['is_vbr']) {
|
84 |
+
$info['audio']['bitrate_mode'] = 'vbr';
|
85 |
+
$info['aac']['header']['bitrate_max'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
|
86 |
+
$bitoffset += 23;
|
87 |
+
} else {
|
88 |
+
$info['audio']['bitrate_mode'] = 'cbr';
|
89 |
+
$info['aac']['header']['bitrate'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
|
90 |
+
$bitoffset += 23;
|
91 |
+
$info['audio']['bitrate'] = $info['aac']['header']['bitrate'];
|
92 |
+
}
|
93 |
+
if ($info['audio']['bitrate'] == 0) {
|
94 |
+
$info['error'][] = 'Corrupt AAC file: bitrate_audio == zero';
|
95 |
+
return false;
|
96 |
+
}
|
97 |
+
$info['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
98 |
+
$bitoffset += 4;
|
99 |
+
|
100 |
+
for ($i = 0; $i < $info['aac']['header']['num_program_configs']; $i++) {
|
101 |
+
// http://www.audiocoding.com/wiki/index.php?page=program_config_element
|
102 |
+
|
103 |
+
// buffer_fullness 20
|
104 |
+
|
105 |
+
// element_instance_tag 4
|
106 |
+
// object_type 2
|
107 |
+
// sampling_frequency_index 4
|
108 |
+
// num_front_channel_elements 4
|
109 |
+
// num_side_channel_elements 4
|
110 |
+
// num_back_channel_elements 4
|
111 |
+
// num_lfe_channel_elements 2
|
112 |
+
// num_assoc_data_elements 3
|
113 |
+
// num_valid_cc_elements 4
|
114 |
+
// mono_mixdown_present 1
|
115 |
+
// mono_mixdown_element_number 4 if mono_mixdown_present == 1
|
116 |
+
// stereo_mixdown_present 1
|
117 |
+
// stereo_mixdown_element_number 4 if stereo_mixdown_present == 1
|
118 |
+
// matrix_mixdown_idx_present 1
|
119 |
+
// matrix_mixdown_idx 2 if matrix_mixdown_idx_present == 1
|
120 |
+
// pseudo_surround_enable 1 if matrix_mixdown_idx_present == 1
|
121 |
+
// for (i = 0; i < num_front_channel_elements; i++) {
|
122 |
+
// front_element_is_cpe[i] 1
|
123 |
+
// front_element_tag_select[i] 4
|
124 |
+
// }
|
125 |
+
// for (i = 0; i < num_side_channel_elements; i++) {
|
126 |
+
// side_element_is_cpe[i] 1
|
127 |
+
// side_element_tag_select[i] 4
|
128 |
+
// }
|
129 |
+
// for (i = 0; i < num_back_channel_elements; i++) {
|
130 |
+
// back_element_is_cpe[i] 1
|
131 |
+
// back_element_tag_select[i] 4
|
132 |
+
// }
|
133 |
+
// for (i = 0; i < num_lfe_channel_elements; i++) {
|
134 |
+
// lfe_element_tag_select[i] 4
|
135 |
+
// }
|
136 |
+
// for (i = 0; i < num_assoc_data_elements; i++) {
|
137 |
+
// assoc_data_element_tag_select[i] 4
|
138 |
+
// }
|
139 |
+
// for (i = 0; i < num_valid_cc_elements; i++) {
|
140 |
+
// cc_element_is_ind_sw[i] 1
|
141 |
+
// valid_cc_element_tag_select[i] 4
|
142 |
+
// }
|
143 |
+
// byte_alignment() VAR
|
144 |
+
// comment_field_bytes 8
|
145 |
+
// for (i = 0; i < comment_field_bytes; i++) {
|
146 |
+
// comment_field_data[i] 8
|
147 |
+
// }
|
148 |
+
|
149 |
+
if (!$info['aac']['header']['is_vbr']) {
|
150 |
+
$info['aac']['program_configs'][$i]['buffer_fullness'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20));
|
151 |
+
$bitoffset += 20;
|
152 |
+
}
|
153 |
+
$info['aac']['program_configs'][$i]['element_instance_tag'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
154 |
+
$bitoffset += 4;
|
155 |
+
$info['aac']['program_configs'][$i]['object_type'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
156 |
+
$bitoffset += 2;
|
157 |
+
$info['aac']['program_configs'][$i]['sampling_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
158 |
+
$bitoffset += 4;
|
159 |
+
$info['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
160 |
+
$bitoffset += 4;
|
161 |
+
$info['aac']['program_configs'][$i]['num_side_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
162 |
+
$bitoffset += 4;
|
163 |
+
$info['aac']['program_configs'][$i]['num_back_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
164 |
+
$bitoffset += 4;
|
165 |
+
$info['aac']['program_configs'][$i]['num_lfe_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
166 |
+
$bitoffset += 2;
|
167 |
+
$info['aac']['program_configs'][$i]['num_assoc_data_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3));
|
168 |
+
$bitoffset += 3;
|
169 |
+
$info['aac']['program_configs'][$i]['num_valid_cc_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
170 |
+
$bitoffset += 4;
|
171 |
+
$info['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
172 |
+
$bitoffset += 1;
|
173 |
+
if ($info['aac']['program_configs'][$i]['mono_mixdown_present']) {
|
174 |
+
$info['aac']['program_configs'][$i]['mono_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
175 |
+
$bitoffset += 4;
|
176 |
+
}
|
177 |
+
$info['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
178 |
+
$bitoffset += 1;
|
179 |
+
if ($info['aac']['program_configs'][$i]['stereo_mixdown_present']) {
|
180 |
+
$info['aac']['program_configs'][$i]['stereo_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
181 |
+
$bitoffset += 4;
|
182 |
+
}
|
183 |
+
$info['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
184 |
+
$bitoffset += 1;
|
185 |
+
if ($info['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) {
|
186 |
+
$info['aac']['program_configs'][$i]['matrix_mixdown_idx'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
187 |
+
$bitoffset += 2;
|
188 |
+
$info['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
189 |
+
$bitoffset += 1;
|
190 |
+
}
|
191 |
+
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) {
|
192 |
+
$info['aac']['program_configs'][$i]['front_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
193 |
+
$bitoffset += 1;
|
194 |
+
$info['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
195 |
+
$bitoffset += 4;
|
196 |
+
}
|
197 |
+
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) {
|
198 |
+
$info['aac']['program_configs'][$i]['side_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
199 |
+
$bitoffset += 1;
|
200 |
+
$info['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
201 |
+
$bitoffset += 4;
|
202 |
+
}
|
203 |
+
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) {
|
204 |
+
$info['aac']['program_configs'][$i]['back_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
205 |
+
$bitoffset += 1;
|
206 |
+
$info['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
207 |
+
$bitoffset += 4;
|
208 |
+
}
|
209 |
+
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) {
|
210 |
+
$info['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
211 |
+
$bitoffset += 4;
|
212 |
+
}
|
213 |
+
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) {
|
214 |
+
$info['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
215 |
+
$bitoffset += 4;
|
216 |
+
}
|
217 |
+
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) {
|
218 |
+
$info['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
219 |
+
$bitoffset += 1;
|
220 |
+
$info['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
221 |
+
$bitoffset += 4;
|
222 |
+
}
|
223 |
+
|
224 |
+
$bitoffset = ceil($bitoffset / 8) * 8;
|
225 |
+
|
226 |
+
$info['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8));
|
227 |
+
$bitoffset += 8;
|
228 |
+
$info['aac']['program_configs'][$i]['comment_field'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $info['aac']['program_configs'][$i]['comment_field_bytes']));
|
229 |
+
$bitoffset += 8 * $info['aac']['program_configs'][$i]['comment_field_bytes'];
|
230 |
+
|
231 |
+
|
232 |
+
$info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['program_configs'][$i]['object_type'], $info['aac']['header']['mpeg_version']);
|
233 |
+
$info['aac']['program_configs'][$i]['sampling_frequency'] = self::AACsampleRateLookup($info['aac']['program_configs'][$i]['sampling_frequency_index']);
|
234 |
+
$info['audio']['sample_rate'] = $info['aac']['program_configs'][$i]['sampling_frequency'];
|
235 |
+
$info['audio']['channels'] = self::AACchannelCountCalculate($info['aac']['program_configs'][$i]);
|
236 |
+
if ($info['aac']['program_configs'][$i]['comment_field']) {
|
237 |
+
$info['aac']['comments'][] = $info['aac']['program_configs'][$i]['comment_field'];
|
238 |
+
}
|
239 |
+
}
|
240 |
+
$info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate'];
|
241 |
+
|
242 |
+
$info['audio']['encoder_options'] = $info['aac']['header_type'].' '.$info['aac']['header']['profile'];
|
243 |
+
|
244 |
+
|
245 |
+
|
246 |
+
return true;
|
247 |
+
|
248 |
+
} else {
|
249 |
+
|
250 |
+
unset($info['fileformat']);
|
251 |
+
unset($info['aac']);
|
252 |
+
$info['error'][] = 'AAC-ADIF synch not found at offset '.$info['avdataoffset'].' (expected "ADIF", found "'.substr($AACheader, 0, 4).'" instead)';
|
253 |
+
return false;
|
254 |
+
|
255 |
+
}
|
256 |
+
|
257 |
+
}
|
258 |
+
|
259 |
+
|
260 |
+
function getAACADTSheaderFilepointer($MaxFramesToScan=1000000, $ReturnExtendedInfo=false) {
|
261 |
+
$info = &$this->getid3->info;
|
262 |
+
|
263 |
+
// based loosely on code from AACfile by Jurgen Faul <jfaul�gmx.de>
|
264 |
+
// http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html
|
265 |
+
|
266 |
+
|
267 |
+
// http://faac.sourceforge.net/wiki/index.php?page=ADTS // dead link
|
268 |
+
// http://wiki.multimedia.cx/index.php?title=ADTS
|
269 |
+
|
270 |
+
// * ADTS Fixed Header: these don't change from frame to frame
|
271 |
+
// syncword 12 always: '111111111111'
|
272 |
+
// ID 1 0: MPEG-4, 1: MPEG-2
|
273 |
+
// MPEG layer 2 If you send AAC in MPEG-TS, set to 0
|
274 |
+
// protection_absent 1 0: CRC present; 1: no CRC
|
275 |
+
// profile 2 0: AAC Main; 1: AAC LC (Low Complexity); 2: AAC SSR (Scalable Sample Rate); 3: AAC LTP (Long Term Prediction)
|
276 |
+
// sampling_frequency_index 4 15 not allowed
|
277 |
+
// private_bit 1 usually 0
|
278 |
+
// channel_configuration 3
|
279 |
+
// original/copy 1 0: original; 1: copy
|
280 |
+
// home 1 usually 0
|
281 |
+
// emphasis 2 only if ID == 0 (ie MPEG-4) // not present in some documentation?
|
282 |
+
|
283 |
+
// * ADTS Variable Header: these can change from frame to frame
|
284 |
+
// copyright_identification_bit 1
|
285 |
+
// copyright_identification_start 1
|
286 |
+
// aac_frame_length 13 length of the frame including header (in bytes)
|
287 |
+
// adts_buffer_fullness 11 0x7FF indicates VBR
|
288 |
+
// no_raw_data_blocks_in_frame 2
|
289 |
+
|
290 |
+
// * ADTS Error check
|
291 |
+
// crc_check 16 only if protection_absent == 0
|
292 |
+
|
293 |
+
$byteoffset = $info['avdataoffset'];
|
294 |
+
$framenumber = 0;
|
295 |
+
|
296 |
+
// Init bit pattern array
|
297 |
+
static $decbin = array();
|
298 |
+
|
299 |
+
// Populate $bindec
|
300 |
+
for ($i = 0; $i < 256; $i++) {
|
301 |
+
$decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT);
|
302 |
+
}
|
303 |
+
|
304 |
+
// used to calculate bitrate below
|
305 |
+
$BitrateCache = array();
|
306 |
+
|
307 |
+
|
308 |
+
while (true) {
|
309 |
+
// breaks out when end-of-file encountered, or invalid data found,
|
310 |
+
// or MaxFramesToScan frames have been scanned
|
311 |
+
|
312 |
+
if (!getid3_lib::intValueSupported($byteoffset)) {
|
313 |
+
$info['warning'][] = 'Unable to parse AAC file beyond '.ftell($this->getid3->fp).' (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)';
|
314 |
+
return false;
|
315 |
+
}
|
316 |
+
fseek($this->getid3->fp, $byteoffset, SEEK_SET);
|
317 |
+
|
318 |
+
// First get substring
|
319 |
+
$substring = fread($this->getid3->fp, 9); // header is 7 bytes (or 9 if CRC is present)
|
320 |
+
$substringlength = strlen($substring);
|
321 |
+
if ($substringlength != 9) {
|
322 |
+
$info['error'][] = 'Failed to read 7 bytes at offset '.(ftell($this->getid3->fp) - $substringlength).' (only read '.$substringlength.' bytes)';
|
323 |
+
return false;
|
324 |
+
}
|
325 |
+
// this would be easier with 64-bit math, but split it up to allow for 32-bit:
|
326 |
+
$header1 = getid3_lib::BigEndian2Int(substr($substring, 0, 2));
|
327 |
+
$header2 = getid3_lib::BigEndian2Int(substr($substring, 2, 4));
|
328 |
+
$header3 = getid3_lib::BigEndian2Int(substr($substring, 6, 1));
|
329 |
+
|
330 |
+
$info['aac']['header']['raw']['syncword'] = ($header1 & 0xFFF0) >> 4;
|
331 |
+
if ($info['aac']['header']['raw']['syncword'] != 0x0FFF) {
|
332 |
+
$info['error'][] = 'Synch pattern (0x0FFF) not found at offset '.(ftell($this->getid3->fp) - $substringlength).' (found 0x0'.strtoupper(dechex($info['aac']['header']['raw']['syncword'])).' instead)';
|
333 |
+
//if ($info['fileformat'] == 'aac') {
|
334 |
+
// return true;
|
335 |
+
//}
|
336 |
+
unset($info['aac']);
|
337 |
+
return false;
|
338 |
+
}
|
339 |
+
|
340 |
+
// Gather info for first frame only - this takes time to do 1000 times!
|
341 |
+
if ($framenumber == 0) {
|
342 |
+
$info['aac']['header_type'] = 'ADTS';
|
343 |
+
$info['fileformat'] = 'aac';
|
344 |
+
$info['audio']['dataformat'] = 'aac';
|
345 |
+
|
346 |
+
$info['aac']['header']['raw']['mpeg_version'] = ($header1 & 0x0008) >> 3;
|
347 |
+
$info['aac']['header']['raw']['mpeg_layer'] = ($header1 & 0x0006) >> 1;
|
348 |
+
$info['aac']['header']['raw']['protection_absent'] = ($header1 & 0x0001) >> 0;
|
349 |
+
|
350 |
+
$info['aac']['header']['raw']['profile_code'] = ($header2 & 0xC0000000) >> 30;
|
351 |
+
$info['aac']['header']['raw']['sample_rate_code'] = ($header2 & 0x3C000000) >> 26;
|
352 |
+
$info['aac']['header']['raw']['private_stream'] = ($header2 & 0x02000000) >> 25;
|
353 |
+
$info['aac']['header']['raw']['channels_code'] = ($header2 & 0x01C00000) >> 22;
|
354 |
+
$info['aac']['header']['raw']['original'] = ($header2 & 0x00200000) >> 21;
|
355 |
+
$info['aac']['header']['raw']['home'] = ($header2 & 0x00100000) >> 20;
|
356 |
+
$info['aac']['header']['raw']['copyright_stream'] = ($header2 & 0x00080000) >> 19;
|
357 |
+
$info['aac']['header']['raw']['copyright_start'] = ($header2 & 0x00040000) >> 18;
|
358 |
+
$info['aac']['header']['raw']['frame_length'] = ($header2 & 0x0003FFE0) >> 5;
|
359 |
+
|
360 |
+
$info['aac']['header']['mpeg_version'] = ($info['aac']['header']['raw']['mpeg_version'] ? 2 : 4);
|
361 |
+
$info['aac']['header']['crc_present'] = ($info['aac']['header']['raw']['protection_absent'] ? false: true);
|
362 |
+
$info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['header']['raw']['profile_code'], $info['aac']['header']['mpeg_version']);
|
363 |
+
$info['aac']['header']['sample_frequency'] = self::AACsampleRateLookup($info['aac']['header']['raw']['sample_rate_code']);
|
364 |
+
$info['aac']['header']['private'] = (bool) $info['aac']['header']['raw']['private_stream'];
|
365 |
+
$info['aac']['header']['original'] = (bool) $info['aac']['header']['raw']['original'];
|
366 |
+
$info['aac']['header']['home'] = (bool) $info['aac']['header']['raw']['home'];
|
367 |
+
$info['aac']['header']['channels'] = (($info['aac']['header']['raw']['channels_code'] == 7) ? 8 : $info['aac']['header']['raw']['channels_code']);
|
368 |
+
if ($ReturnExtendedInfo) {
|
369 |
+
$info['aac'][$framenumber]['copyright_id_bit'] = (bool) $info['aac']['header']['raw']['copyright_stream'];
|
370 |
+
$info['aac'][$framenumber]['copyright_id_start'] = (bool) $info['aac']['header']['raw']['copyright_start'];
|
371 |
+
}
|
372 |
+
|
373 |
+
if ($info['aac']['header']['raw']['mpeg_layer'] != 0) {
|
374 |
+
$info['warning'][] = 'Layer error - expected "0", found "'.$info['aac']['header']['raw']['mpeg_layer'].'" instead';
|
375 |
+
}
|
376 |
+
if ($info['aac']['header']['sample_frequency'] == 0) {
|
377 |
+
$info['error'][] = 'Corrupt AAC file: sample_frequency == zero';
|
378 |
+
return false;
|
379 |
+
}
|
380 |
+
|
381 |
+
$info['audio']['sample_rate'] = $info['aac']['header']['sample_frequency'];
|
382 |
+
$info['audio']['channels'] = $info['aac']['header']['channels'];
|
383 |
+
}
|
384 |
+
|
385 |
+
$FrameLength = ($header2 & 0x0003FFE0) >> 5;
|
386 |
+
|
387 |
+
if (!isset($BitrateCache[$FrameLength])) {
|
388 |
+
$BitrateCache[$FrameLength] = ($info['aac']['header']['sample_frequency'] / 1024) * $FrameLength * 8;
|
389 |
+
}
|
390 |
+
getid3_lib::safe_inc($info['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1);
|
391 |
+
|
392 |
+
$info['aac'][$framenumber]['aac_frame_length'] = $FrameLength;
|
393 |
+
|
394 |
+
$info['aac'][$framenumber]['adts_buffer_fullness'] = (($header2 & 0x0000001F) << 6) & (($header3 & 0xFC) >> 2);
|
395 |
+
if ($info['aac'][$framenumber]['adts_buffer_fullness'] == 0x07FF) {
|
396 |
+
$info['audio']['bitrate_mode'] = 'vbr';
|
397 |
+
} else {
|
398 |
+
$info['audio']['bitrate_mode'] = 'cbr';
|
399 |
+
}
|
400 |
+
$info['aac'][$framenumber]['num_raw_data_blocks'] = (($header3 & 0x03) >> 0);
|
401 |
+
|
402 |
+
if ($info['aac']['header']['crc_present']) {
|
403 |
+
//$info['aac'][$framenumber]['crc'] = getid3_lib::BigEndian2Int(substr($substring, 7, 2);
|
404 |
+
}
|
405 |
+
|
406 |
+
if (!$ReturnExtendedInfo) {
|
407 |
+
unset($info['aac'][$framenumber]);
|
408 |
+
}
|
409 |
+
|
410 |
+
/*
|
411 |
+
$rounded_precision = 5000;
|
412 |
+
$info['aac']['bitrate_distribution_rounded'] = array();
|
413 |
+
foreach ($info['aac']['bitrate_distribution'] as $bitrate => $count) {
|
414 |
+
$rounded_bitrate = round($bitrate / $rounded_precision) * $rounded_precision;
|
415 |
+
getid3_lib::safe_inc($info['aac']['bitrate_distribution_rounded'][$rounded_bitrate], $count);
|
416 |
+
}
|
417 |
+
ksort($info['aac']['bitrate_distribution_rounded']);
|
418 |
+
*/
|
419 |
+
|
420 |
+
$byteoffset += $FrameLength;
|
421 |
+
if ((++$framenumber < $MaxFramesToScan) && (($byteoffset + 10) < $info['avdataend'])) {
|
422 |
+
|
423 |
+
// keep scanning
|
424 |
+
|
425 |
+
} else {
|
426 |
+
|
427 |
+
$info['aac']['frames'] = $framenumber;
|
428 |
+
$info['playtime_seconds'] = ($info['avdataend'] / $byteoffset) * (($framenumber * 1024) / $info['aac']['header']['sample_frequency']); // (1 / % of file scanned) * (samples / (samples/sec)) = seconds
|
429 |
+
if ($info['playtime_seconds'] == 0) {
|
430 |
+
$info['error'][] = 'Corrupt AAC file: playtime_seconds == zero';
|
431 |
+
return false;
|
432 |
+
}
|
433 |
+
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
434 |
+
ksort($info['aac']['bitrate_distribution']);
|
435 |
+
|
436 |
+
$info['audio']['encoder_options'] = $info['aac']['header_type'].' '.$info['aac']['header']['profile'];
|
437 |
+
|
438 |
+
return true;
|
439 |
+
|
440 |
+
}
|
441 |
+
}
|
442 |
+
// should never get here.
|
443 |
+
}
|
444 |
+
|
445 |
+
public static function AACsampleRateLookup($samplerateid) {
|
446 |
+
static $AACsampleRateLookup = array();
|
447 |
+
if (empty($AACsampleRateLookup)) {
|
448 |
+
$AACsampleRateLookup[0] = 96000;
|
449 |
+
$AACsampleRateLookup[1] = 88200;
|
450 |
+
$AACsampleRateLookup[2] = 64000;
|
451 |
+
$AACsampleRateLookup[3] = 48000;
|
452 |
+
$AACsampleRateLookup[4] = 44100;
|
453 |
+
$AACsampleRateLookup[5] = 32000;
|
454 |
+
$AACsampleRateLookup[6] = 24000;
|
455 |
+
$AACsampleRateLookup[7] = 22050;
|
456 |
+
$AACsampleRateLookup[8] = 16000;
|
457 |
+
$AACsampleRateLookup[9] = 12000;
|
458 |
+
$AACsampleRateLookup[10] = 11025;
|
459 |
+
$AACsampleRateLookup[11] = 8000;
|
460 |
+
$AACsampleRateLookup[12] = 0;
|
461 |
+
$AACsampleRateLookup[13] = 0;
|
462 |
+
$AACsampleRateLookup[14] = 0;
|
463 |
+
$AACsampleRateLookup[15] = 0;
|
464 |
+
}
|
465 |
+
return (isset($AACsampleRateLookup[$samplerateid]) ? $AACsampleRateLookup[$samplerateid] : 'invalid');
|
466 |
+
}
|
467 |
+
|
468 |
+
public static function AACprofileLookup($profileid, $mpegversion) {
|
469 |
+
static $AACprofileLookup = array();
|
470 |
+
if (empty($AACprofileLookup)) {
|
471 |
+
$AACprofileLookup[2][0] = 'Main profile';
|
472 |
+
$AACprofileLookup[2][1] = 'Low Complexity profile (LC)';
|
473 |
+
$AACprofileLookup[2][2] = 'Scalable Sample Rate profile (SSR)';
|
474 |
+
$AACprofileLookup[2][3] = '(reserved)';
|
475 |
+
$AACprofileLookup[4][0] = 'AAC_MAIN';
|
476 |
+
$AACprofileLookup[4][1] = 'AAC_LC';
|
477 |
+
$AACprofileLookup[4][2] = 'AAC_SSR';
|
478 |
+
$AACprofileLookup[4][3] = 'AAC_LTP';
|
479 |
+
}
|
480 |
+
return (isset($AACprofileLookup[$mpegversion][$profileid]) ? $AACprofileLookup[$mpegversion][$profileid] : 'invalid');
|
481 |
+
}
|
482 |
+
|
483 |
+
public static function AACchannelCountCalculate($program_configs) {
|
484 |
+
$channels = 0;
|
485 |
+
for ($i = 0; $i < $program_configs['num_front_channel_elements']; $i++) {
|
486 |
+
$channels++;
|
487 |
+
if ($program_configs['front_element_is_cpe'][$i]) {
|
488 |
+
// each front element is channel pair (CPE = Channel Pair Element)
|
489 |
+
$channels++;
|
490 |
+
}
|
491 |
+
}
|
492 |
+
for ($i = 0; $i < $program_configs['num_side_channel_elements']; $i++) {
|
493 |
+
$channels++;
|
494 |
+
if ($program_configs['side_element_is_cpe'][$i]) {
|
495 |
+
// each side element is channel pair (CPE = Channel Pair Element)
|
496 |
+
$channels++;
|
497 |
+
}
|
498 |
+
}
|
499 |
+
for ($i = 0; $i < $program_configs['num_back_channel_elements']; $i++) {
|
500 |
+
$channels++;
|
501 |
+
if ($program_configs['back_element_is_cpe'][$i]) {
|
502 |
+
// each back element is channel pair (CPE = Channel Pair Element)
|
503 |
+
$channels++;
|
504 |
+
}
|
505 |
+
}
|
506 |
+
for ($i = 0; $i < $program_configs['num_lfe_channel_elements']; $i++) {
|
507 |
+
$channels++;
|
508 |
+
}
|
509 |
+
return $channels;
|
510 |
+
}
|
511 |
+
|
512 |
+
}
|
513 |
+
|
514 |
+
|
515 |
+
?>
|
includes/lib/getid3/module.audio.ac3.php
ADDED
@@ -0,0 +1,473 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
/////////////////////////////////////////////////////////////////
|
7 |
+
// See readme.txt for more details //
|
8 |
+
/////////////////////////////////////////////////////////////////
|
9 |
+
// //
|
10 |
+
// module.audio.ac3.php //
|
11 |
+
// module for analyzing AC-3 (aka Dolby Digital) audio files //
|
12 |
+
// dependencies: NONE //
|
13 |
+
// ///
|
14 |
+
/////////////////////////////////////////////////////////////////
|
15 |
+
|
16 |
+
|
17 |
+
class getid3_ac3 extends getid3_handler
|
18 |
+
{
|
19 |
+
private $AC3header = '';
|
20 |
+
private $BSIoffset = 0;
|
21 |
+
|
22 |
+
|
23 |
+
public function Analyze() {
|
24 |
+
$info = &$this->getid3->info;
|
25 |
+
|
26 |
+
///AH
|
27 |
+
$info['ac3']['raw']['bsi'] = array();
|
28 |
+
$thisfile_ac3 = &$info['ac3'];
|
29 |
+
$thisfile_ac3_raw = &$thisfile_ac3['raw'];
|
30 |
+
$thisfile_ac3_raw_bsi = &$thisfile_ac3_raw['bsi'];
|
31 |
+
|
32 |
+
|
33 |
+
// http://www.atsc.org/standards/a_52a.pdf
|
34 |
+
|
35 |
+
$info['fileformat'] = 'ac3';
|
36 |
+
|
37 |
+
// An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames
|
38 |
+
// Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256
|
39 |
+
// new audio samples per channel. A synchronization information (SI) header at the beginning
|
40 |
+
// of each frame contains information needed to acquire and maintain synchronization. A
|
41 |
+
// bit stream information (BSI) header follows SI, and contains parameters describing the coded
|
42 |
+
// audio service. The coded audio blocks may be followed by an auxiliary data (Aux) field. At the
|
43 |
+
// end of each frame is an error check field that includes a CRC word for error detection. An
|
44 |
+
// additional CRC word is located in the SI header, the use of which, by a decoder, is optional.
|
45 |
+
//
|
46 |
+
// syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC
|
47 |
+
|
48 |
+
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
49 |
+
$this->AC3header['syncinfo'] = fread($this->getid3->fp, 5);
|
50 |
+
$thisfile_ac3_raw['synchinfo']['synchword'] = substr($this->AC3header['syncinfo'], 0, 2);
|
51 |
+
|
52 |
+
$magic = "\x0B\x77";
|
53 |
+
if ($thisfile_ac3_raw['synchinfo']['synchword'] != $magic) {
|
54 |
+
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($thisfile_ac3_raw['synchinfo']['synchword']).'"';
|
55 |
+
unset($info['fileformat'], $info['ac3']);
|
56 |
+
return false;
|
57 |
+
}
|
58 |
+
|
59 |
+
$info['audio']['dataformat'] = 'ac3';
|
60 |
+
$info['audio']['bitrate_mode'] = 'cbr';
|
61 |
+
$info['audio']['lossless'] = false;
|
62 |
+
|
63 |
+
// syncinfo() {
|
64 |
+
// syncword 16
|
65 |
+
// crc1 16
|
66 |
+
// fscod 2
|
67 |
+
// frmsizecod 6
|
68 |
+
// } /* end of syncinfo */
|
69 |
+
|
70 |
+
$thisfile_ac3_raw['synchinfo']['crc1'] = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], 2, 2));
|
71 |
+
$ac3_synchinfo_fscod_frmsizecod = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], 4, 1));
|
72 |
+
$thisfile_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xC0) >> 6;
|
73 |
+
$thisfile_ac3_raw['synchinfo']['frmsizecod'] = ($ac3_synchinfo_fscod_frmsizecod & 0x3F);
|
74 |
+
|
75 |
+
$thisfile_ac3['sample_rate'] = $this->AC3sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']);
|
76 |
+
if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) {
|
77 |
+
$info['audio']['sample_rate'] = $thisfile_ac3['sample_rate'];
|
78 |
+
}
|
79 |
+
|
80 |
+
$thisfile_ac3['frame_length'] = $this->AC3frameSizeLookup($thisfile_ac3_raw['synchinfo']['frmsizecod'], $thisfile_ac3_raw['synchinfo']['fscod']);
|
81 |
+
$thisfile_ac3['bitrate'] = $this->AC3bitrateLookup($thisfile_ac3_raw['synchinfo']['frmsizecod']);
|
82 |
+
$info['audio']['bitrate'] = $thisfile_ac3['bitrate'];
|
83 |
+
|
84 |
+
$this->AC3header['bsi'] = getid3_lib::BigEndian2Bin(fread($this->getid3->fp, 15));
|
85 |
+
$ac3_bsi_offset = 0;
|
86 |
+
|
87 |
+
$thisfile_ac3_raw_bsi['bsid'] = $this->readHeaderBSI(5);
|
88 |
+
if ($thisfile_ac3_raw_bsi['bsid'] > 8) {
|
89 |
+
// Decoders which can decode version 8 will thus be able to decode version numbers less than 8.
|
90 |
+
// If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used.
|
91 |
+
// Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8.
|
92 |
+
$info['error'][] = 'Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 8';
|
93 |
+
unset($thisfile_ac3);
|
94 |
+
return false;
|
95 |
+
}
|
96 |
+
|
97 |
+
$thisfile_ac3_raw_bsi['bsmod'] = $this->readHeaderBSI(3);
|
98 |
+
$thisfile_ac3_raw_bsi['acmod'] = $this->readHeaderBSI(3);
|
99 |
+
|
100 |
+
$thisfile_ac3['service_type'] = $this->AC3serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']);
|
101 |
+
$ac3_coding_mode = $this->AC3audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']);
|
102 |
+
foreach($ac3_coding_mode as $key => $value) {
|
103 |
+
$thisfile_ac3[$key] = $value;
|
104 |
+
}
|
105 |
+
switch ($thisfile_ac3_raw_bsi['acmod']) {
|
106 |
+
case 0:
|
107 |
+
case 1:
|
108 |
+
$info['audio']['channelmode'] = 'mono';
|
109 |
+
break;
|
110 |
+
case 3:
|
111 |
+
case 4:
|
112 |
+
$info['audio']['channelmode'] = 'stereo';
|
113 |
+
break;
|
114 |
+
default:
|
115 |
+
$info['audio']['channelmode'] = 'surround';
|
116 |
+
break;
|
117 |
+
}
|
118 |
+
$info['audio']['channels'] = $thisfile_ac3['num_channels'];
|
119 |
+
|
120 |
+
if ($thisfile_ac3_raw_bsi['acmod'] & 0x01) {
|
121 |
+
// If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream.
|
122 |
+
$thisfile_ac3_raw_bsi['cmixlev'] = $this->readHeaderBSI(2);
|
123 |
+
$thisfile_ac3['center_mix_level'] = $this->AC3centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']);
|
124 |
+
}
|
125 |
+
|
126 |
+
if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) {
|
127 |
+
// If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream.
|
128 |
+
$thisfile_ac3_raw_bsi['surmixlev'] = $this->readHeaderBSI(2);
|
129 |
+
$thisfile_ac3['surround_mix_level'] = $this->AC3surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']);
|
130 |
+
}
|
131 |
+
|
132 |
+
if ($thisfile_ac3_raw_bsi['acmod'] == 0x02) {
|
133 |
+
// When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround.
|
134 |
+
$thisfile_ac3_raw_bsi['dsurmod'] = $this->readHeaderBSI(2);
|
135 |
+
$thisfile_ac3['dolby_surround_mode'] = $this->AC3dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']);
|
136 |
+
}
|
137 |
+
|
138 |
+
$thisfile_ac3_raw_bsi['lfeon'] = (bool) $this->readHeaderBSI(1);
|
139 |
+
$thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['lfeon'];
|
140 |
+
if ($thisfile_ac3_raw_bsi['lfeon']) {
|
141 |
+
//$info['audio']['channels']++;
|
142 |
+
$info['audio']['channels'] .= '.1';
|
143 |
+
}
|
144 |
+
|
145 |
+
$thisfile_ac3['channels_enabled'] = $this->AC3channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']);
|
146 |
+
|
147 |
+
// This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1�31.
|
148 |
+
// 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.
|
149 |
+
$thisfile_ac3_raw_bsi['dialnorm'] = $this->readHeaderBSI(5);
|
150 |
+
$thisfile_ac3['dialogue_normalization'] = '-'.$thisfile_ac3_raw_bsi['dialnorm'].'dB';
|
151 |
+
|
152 |
+
$thisfile_ac3_raw_bsi['compre_flag'] = (bool) $this->readHeaderBSI(1);
|
153 |
+
if ($thisfile_ac3_raw_bsi['compre_flag']) {
|
154 |
+
$thisfile_ac3_raw_bsi['compr'] = $this->readHeaderBSI(8);
|
155 |
+
$thisfile_ac3['heavy_compression'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr']);
|
156 |
+
}
|
157 |
+
|
158 |
+
$thisfile_ac3_raw_bsi['langcode_flag'] = (bool) $this->readHeaderBSI(1);
|
159 |
+
if ($thisfile_ac3_raw_bsi['langcode_flag']) {
|
160 |
+
$thisfile_ac3_raw_bsi['langcod'] = $this->readHeaderBSI(8);
|
161 |
+
}
|
162 |
+
|
163 |
+
$thisfile_ac3_raw_bsi['audprodie'] = (bool) $this->readHeaderBSI(1);
|
164 |
+
if ($thisfile_ac3_raw_bsi['audprodie']) {
|
165 |
+
$thisfile_ac3_raw_bsi['mixlevel'] = $this->readHeaderBSI(5);
|
166 |
+
$thisfile_ac3_raw_bsi['roomtyp'] = $this->readHeaderBSI(2);
|
167 |
+
|
168 |
+
$thisfile_ac3['mixing_level'] = (80 + $thisfile_ac3_raw_bsi['mixlevel']).'dB';
|
169 |
+
$thisfile_ac3['room_type'] = $this->AC3roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']);
|
170 |
+
}
|
171 |
+
|
172 |
+
if ($thisfile_ac3_raw_bsi['acmod'] == 0x00) {
|
173 |
+
// If acmod is 0, then two completely independent program channels (dual mono)
|
174 |
+
// are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case,
|
175 |
+
// a number of additional items are present in BSI or audblk to fully describe Ch2.
|
176 |
+
|
177 |
+
// This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1�31.
|
178 |
+
// 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.
|
179 |
+
$thisfile_ac3_raw_bsi['dialnorm2'] = $this->readHeaderBSI(5);
|
180 |
+
$thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB';
|
181 |
+
|
182 |
+
$thisfile_ac3_raw_bsi['compre_flag2'] = (bool) $this->readHeaderBSI(1);
|
183 |
+
if ($thisfile_ac3_raw_bsi['compre_flag2']) {
|
184 |
+
$thisfile_ac3_raw_bsi['compr2'] = $this->readHeaderBSI(8);
|
185 |
+
$thisfile_ac3['heavy_compression2'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr2']);
|
186 |
+
}
|
187 |
+
|
188 |
+
$thisfile_ac3_raw_bsi['langcode_flag2'] = (bool) $this->readHeaderBSI(1);
|
189 |
+
if ($thisfile_ac3_raw_bsi['langcode_flag2']) {
|
190 |
+
$thisfile_ac3_raw_bsi['langcod2'] = $this->readHeaderBSI(8);
|
191 |
+
}
|
192 |
+
|
193 |
+
$thisfile_ac3_raw_bsi['audprodie2'] = (bool) $this->readHeaderBSI(1);
|
194 |
+
if ($thisfile_ac3_raw_bsi['audprodie2']) {
|
195 |
+
$thisfile_ac3_raw_bsi['mixlevel2'] = $this->readHeaderBSI(5);
|
196 |
+
$thisfile_ac3_raw_bsi['roomtyp2'] = $this->readHeaderBSI(2);
|
197 |
+
|
198 |
+
$thisfile_ac3['mixing_level2'] = (80 + $thisfile_ac3_raw_bsi['mixlevel2']).'dB';
|
199 |
+
$thisfile_ac3['room_type2'] = $this->AC3roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']);
|
200 |
+
}
|
201 |
+
|
202 |
+
}
|
203 |
+
|
204 |
+
$thisfile_ac3_raw_bsi['copyright'] = (bool) $this->readHeaderBSI(1);
|
205 |
+
|
206 |
+
$thisfile_ac3_raw_bsi['original'] = (bool) $this->readHeaderBSI(1);
|
207 |
+
|
208 |
+
$thisfile_ac3_raw_bsi['timecode1_flag'] = (bool) $this->readHeaderBSI(1);
|
209 |
+
if ($thisfile_ac3_raw_bsi['timecode1_flag']) {
|
210 |
+
$thisfile_ac3_raw_bsi['timecode1'] = $this->readHeaderBSI(14);
|
211 |
+
}
|
212 |
+
|
213 |
+
$thisfile_ac3_raw_bsi['timecode2_flag'] = (bool) $this->readHeaderBSI(1);
|
214 |
+
if ($thisfile_ac3_raw_bsi['timecode2_flag']) {
|
215 |
+
$thisfile_ac3_raw_bsi['timecode2'] = $this->readHeaderBSI(14);
|
216 |
+
}
|
217 |
+
|
218 |
+
$thisfile_ac3_raw_bsi['addbsi_flag'] = (bool) $this->readHeaderBSI(1);
|
219 |
+
if ($thisfile_ac3_raw_bsi['addbsi_flag']) {
|
220 |
+
$thisfile_ac3_raw_bsi['addbsi_length'] = $this->readHeaderBSI(6);
|
221 |
+
|
222 |
+
$this->AC3header['bsi'] .= getid3_lib::BigEndian2Bin(fread($this->getid3->fp, $thisfile_ac3_raw_bsi['addbsi_length']));
|
223 |
+
|
224 |
+
$thisfile_ac3_raw_bsi['addbsi_data'] = substr($this->AC3header['bsi'], $this->BSIoffset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8);
|
225 |
+
$this->BSIoffset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8;
|
226 |
+
}
|
227 |
+
|
228 |
+
return true;
|
229 |
+
}
|
230 |
+
|
231 |
+
private function readHeaderBSI($length) {
|
232 |
+
$data = substr($this->AC3header['bsi'], $this->BSIoffset, $length);
|
233 |
+
$this->BSIoffset += $length;
|
234 |
+
|
235 |
+
return bindec($data);
|
236 |
+
}
|
237 |
+
|
238 |
+
public static function AC3sampleRateCodeLookup($fscod) {
|
239 |
+
static $AC3sampleRateCodeLookup = array(
|
240 |
+
0 => 48000,
|
241 |
+
1 => 44100,
|
242 |
+
2 => 32000,
|
243 |
+
3 => 'reserved' // If the reserved code is indicated, the decoder should not attempt to decode audio and should mute.
|
244 |
+
);
|
245 |
+
return (isset($AC3sampleRateCodeLookup[$fscod]) ? $AC3sampleRateCodeLookup[$fscod] : false);
|
246 |
+
}
|
247 |
+
|
248 |
+
public static function AC3serviceTypeLookup($bsmod, $acmod) {
|
249 |
+
static $AC3serviceTypeLookup = array();
|
250 |
+
if (empty($AC3serviceTypeLookup)) {
|
251 |
+
for ($i = 0; $i <= 7; $i++) {
|
252 |
+
$AC3serviceTypeLookup[0][$i] = 'main audio service: complete main (CM)';
|
253 |
+
$AC3serviceTypeLookup[1][$i] = 'main audio service: music and effects (ME)';
|
254 |
+
$AC3serviceTypeLookup[2][$i] = 'associated service: visually impaired (VI)';
|
255 |
+
$AC3serviceTypeLookup[3][$i] = 'associated service: hearing impaired (HI)';
|
256 |
+
$AC3serviceTypeLookup[4][$i] = 'associated service: dialogue (D)';
|
257 |
+
$AC3serviceTypeLookup[5][$i] = 'associated service: commentary (C)';
|
258 |
+
$AC3serviceTypeLookup[6][$i] = 'associated service: emergency (E)';
|
259 |
+
}
|
260 |
+
|
261 |
+
$AC3serviceTypeLookup[7][1] = 'associated service: voice over (VO)';
|
262 |
+
for ($i = 2; $i <= 7; $i++) {
|
263 |
+
$AC3serviceTypeLookup[7][$i] = 'main audio service: karaoke';
|
264 |
+
}
|
265 |
+
}
|
266 |
+
return (isset($AC3serviceTypeLookup[$bsmod][$acmod]) ? $AC3serviceTypeLookup[$bsmod][$acmod] : false);
|
267 |
+
}
|
268 |
+
|
269 |
+
public static function AC3audioCodingModeLookup($acmod) {
|
270 |
+
static $AC3audioCodingModeLookup = array();
|
271 |
+
if (empty($AC3audioCodingModeLookup)) {
|
272 |
+
// array(channel configuration, # channels (not incl LFE), channel order)
|
273 |
+
$AC3audioCodingModeLookup = array (
|
274 |
+
0 => array('channel_config'=>'1+1', 'num_channels'=>2, 'channel_order'=>'Ch1,Ch2'),
|
275 |
+
1 => array('channel_config'=>'1/0', 'num_channels'=>1, 'channel_order'=>'C'),
|
276 |
+
2 => array('channel_config'=>'2/0', 'num_channels'=>2, 'channel_order'=>'L,R'),
|
277 |
+
3 => array('channel_config'=>'3/0', 'num_channels'=>3, 'channel_order'=>'L,C,R'),
|
278 |
+
4 => array('channel_config'=>'2/1', 'num_channels'=>3, 'channel_order'=>'L,R,S'),
|
279 |
+
5 => array('channel_config'=>'3/1', 'num_channels'=>4, 'channel_order'=>'L,C,R,S'),
|
280 |
+
6 => array('channel_config'=>'2/2', 'num_channels'=>4, 'channel_order'=>'L,R,SL,SR'),
|
281 |
+
7 => array('channel_config'=>'3/2', 'num_channels'=>5, 'channel_order'=>'L,C,R,SL,SR')
|
282 |
+
);
|
283 |
+
}
|
284 |
+
return (isset($AC3audioCodingModeLookup[$acmod]) ? $AC3audioCodingModeLookup[$acmod] : false);
|
285 |
+
}
|
286 |
+
|
287 |
+
public static function AC3centerMixLevelLookup($cmixlev) {
|
288 |
+
static $AC3centerMixLevelLookup;
|
289 |
+
if (empty($AC3centerMixLevelLookup)) {
|
290 |
+
$AC3centerMixLevelLookup = array(
|
291 |
+
0 => pow(2, -3.0 / 6), // 0.707 (�3.0 dB)
|
292 |
+
1 => pow(2, -4.5 / 6), // 0.595 (�4.5 dB)
|
293 |
+
2 => pow(2, -6.0 / 6), // 0.500 (�6.0 dB)
|
294 |
+
3 => 'reserved'
|
295 |
+
);
|
296 |
+
}
|
297 |
+
return (isset($AC3centerMixLevelLookup[$cmixlev]) ? $AC3centerMixLevelLookup[$cmixlev] : false);
|
298 |
+
}
|
299 |
+
|
300 |
+
public static function AC3surroundMixLevelLookup($surmixlev) {
|
301 |
+
static $AC3surroundMixLevelLookup;
|
302 |
+
if (empty($AC3surroundMixLevelLookup)) {
|
303 |
+
$AC3surroundMixLevelLookup = array(
|
304 |
+
0 => pow(2, -3.0 / 6),
|
305 |
+
1 => pow(2, -6.0 / 6),
|
306 |
+
2 => 0,
|
307 |
+
3 => 'reserved'
|
308 |
+
);
|
309 |
+
}
|
310 |
+
return (isset($AC3surroundMixLevelLookup[$surmixlev]) ? $AC3surroundMixLevelLookup[$surmixlev] : false);
|
311 |
+
}
|
312 |
+
|
313 |
+
public static function AC3dolbySurroundModeLookup($dsurmod) {
|
314 |
+
static $AC3dolbySurroundModeLookup = array(
|
315 |
+
0 => 'not indicated',
|
316 |
+
1 => 'Not Dolby Surround encoded',
|
317 |
+
2 => 'Dolby Surround encoded',
|
318 |
+
3 => 'reserved'
|
319 |
+
);
|
320 |
+
return (isset($AC3dolbySurroundModeLookup[$dsurmod]) ? $AC3dolbySurroundModeLookup[$dsurmod] : false);
|
321 |
+
}
|
322 |
+
|
323 |
+
public static function AC3channelsEnabledLookup($acmod, $lfeon) {
|
324 |
+
$AC3channelsEnabledLookup = array(
|
325 |
+
'ch1'=>(bool) ($acmod == 0),
|
326 |
+
'ch2'=>(bool) ($acmod == 0),
|
327 |
+
'left'=>(bool) ($acmod > 1),
|
328 |
+
'right'=>(bool) ($acmod > 1),
|
329 |
+
'center'=>(bool) ($acmod & 0x01),
|
330 |
+
'surround_mono'=>false,
|
331 |
+
'surround_left'=>false,
|
332 |
+
'surround_right'=>false,
|
333 |
+
'lfe'=>$lfeon);
|
334 |
+
switch ($acmod) {
|
335 |
+
case 4:
|
336 |
+
case 5:
|
337 |
+
$AC3channelsEnabledLookup['surround_mono'] = true;
|
338 |
+
break;
|
339 |
+
case 6:
|
340 |
+
case 7:
|
341 |
+
$AC3channelsEnabledLookup['surround_left'] = true;
|
342 |
+
$AC3channelsEnabledLookup['surround_right'] = true;
|
343 |
+
break;
|
344 |
+
}
|
345 |
+
return $AC3channelsEnabledLookup;
|
346 |
+
}
|
347 |
+
|
348 |
+
public static function AC3heavyCompression($compre) {
|
349 |
+
// The first four bits indicate gain changes in 6.02dB increments which can be
|
350 |
+
// implemented with an arithmetic shift operation. The following four bits
|
351 |
+
// indicate linear gain changes, and require a 5-bit multiply.
|
352 |
+
// We will represent the two 4-bit fields of compr as follows:
|
353 |
+
// X0 X1 X2 X3 . Y4 Y5 Y6 Y7
|
354 |
+
// The meaning of the X values is most simply described by considering X to represent a 4-bit
|
355 |
+
// signed integer with values from �8 to +7. The gain indicated by X is then (X + 1) * 6.02 dB. The
|
356 |
+
// following table shows this in detail.
|
357 |
+
|
358 |
+
// Meaning of 4 msb of compr
|
359 |
+
// 7 +48.16 dB
|
360 |
+
// 6 +42.14 dB
|
361 |
+
// 5 +36.12 dB
|
362 |
+
// 4 +30.10 dB
|
363 |
+
// 3 +24.08 dB
|
364 |
+
// 2 +18.06 dB
|
365 |
+
// 1 +12.04 dB
|
366 |
+
// 0 +6.02 dB
|
367 |
+
// -1 0 dB
|
368 |
+
// -2 �6.02 dB
|
369 |
+
// -3 �12.04 dB
|
370 |
+
// -4 �18.06 dB
|
371 |
+
// -5 �24.08 dB
|
372 |
+
// -6 �30.10 dB
|
373 |
+
// -7 �36.12 dB
|
374 |
+
// -8 �42.14 dB
|
375 |
+
|
376 |
+
$fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT);
|
377 |
+
if ($fourbit{0} == '1') {
|
378 |
+
$log_gain = -8 + bindec(substr($fourbit, 1));
|
379 |
+
} else {
|
380 |
+
$log_gain = bindec(substr($fourbit, 1));
|
381 |
+
}
|
382 |
+
$log_gain = ($log_gain + 1) * getid3_lib::RGADamplitude2dB(2);
|
383 |
+
|
384 |
+
// The value of Y is a linear representation of a gain change of up to �6 dB. Y is considered to
|
385 |
+
// be an unsigned fractional integer, with a leading value of 1, or: 0.1 Y4 Y5 Y6 Y7 (base 2). Y can
|
386 |
+
// represent values between 0.111112 (or 31/32) and 0.100002 (or 1/2). Thus, Y can represent gain
|
387 |
+
// changes from �0.28 dB to �6.02 dB.
|
388 |
+
|
389 |
+
$lin_gain = (16 + ($compre & 0x0F)) / 32;
|
390 |
+
|
391 |
+
// The combination of X and Y values allows compr to indicate gain changes from
|
392 |
+
// 48.16 � 0.28 = +47.89 dB, to
|
393 |
+
// �42.14 � 6.02 = �48.16 dB.
|
394 |
+
|
395 |
+
return $log_gain - $lin_gain;
|
396 |
+
}
|
397 |
+
|
398 |
+
public static function AC3roomTypeLookup($roomtyp) {
|
399 |
+
static $AC3roomTypeLookup = array(
|
400 |
+
0 => 'not indicated',
|
401 |
+
1 => 'large room, X curve monitor',
|
402 |
+
2 => 'small room, flat monitor',
|
403 |
+
3 => 'reserved'
|
404 |
+
);
|
405 |
+
return (isset($AC3roomTypeLookup[$roomtyp]) ? $AC3roomTypeLookup[$roomtyp] : false);
|
406 |
+
}
|
407 |
+
|
408 |
+
public static function AC3frameSizeLookup($frmsizecod, $fscod) {
|
409 |
+
$padding = (bool) ($frmsizecod % 2);
|
410 |
+
$framesizeid = floor($frmsizecod / 2);
|
411 |
+
|
412 |
+
static $AC3frameSizeLookup = array();
|
413 |
+
if (empty($AC3frameSizeLookup)) {
|
414 |
+
$AC3frameSizeLookup = array (
|
415 |
+
0 => array(128, 138, 192),
|
416 |
+
1 => array(40, 160, 174, 240),
|
417 |
+
2 => array(48, 192, 208, 288),
|
418 |
+
3 => array(56, 224, 242, 336),
|
419 |
+
4 => array(64, 256, 278, 384),
|
420 |
+
5 => array(80, 320, 348, 480),
|
421 |
+
6 => array(96, 384, 416, 576),
|
422 |
+
7 => array(112, 448, 486, 672),
|
423 |
+
8 => array(128, 512, 556, 768),
|
424 |
+
9 => array(160, 640, 696, 960),
|
425 |
+
10 => array(192, 768, 834, 1152),
|
426 |
+
11 => array(224, 896, 974, 1344),
|
427 |
+
12 => array(256, 1024, 1114, 1536),
|
428 |
+
13 => array(320, 1280, 1392, 1920),
|
429 |
+
14 => array(384, 1536, 1670, 2304),
|
430 |
+
15 => array(448, 1792, 1950, 2688),
|
431 |
+
16 => array(512, 2048, 2228, 3072),
|
432 |
+
17 => array(576, 2304, 2506, 3456),
|
433 |
+
18 => array(640, 2560, 2786, 3840)
|
434 |
+
);
|
435 |
+
}
|
436 |
+
if (($fscod == 1) && $padding) {
|
437 |
+
// frame lengths are padded by 1 word (16 bits) at 44100
|
438 |
+
$AC3frameSizeLookup[$frmsizecod] += 2;
|
439 |
+
}
|
440 |
+
return (isset($AC3frameSizeLookup[$framesizeid][$fscod]) ? $AC3frameSizeLookup[$framesizeid][$fscod] : false);
|
441 |
+
}
|
442 |
+
|
443 |
+
public static function AC3bitrateLookup($frmsizecod) {
|
444 |
+
$framesizeid = floor($frmsizecod / 2);
|
445 |
+
|
446 |
+
static $AC3bitrateLookup = array(
|
447 |
+
0 => 32000,
|
448 |
+
1 => 40000,
|
449 |
+
2 => 48000,
|
450 |
+
3 => 56000,
|
451 |
+
4 => 64000,
|
452 |
+
5 => 80000,
|
453 |
+
6 => 96000,
|
454 |
+
7 => 112000,
|
455 |
+
8 => 128000,
|
456 |
+
9 => 160000,
|
457 |
+
10 => 192000,
|
458 |
+
11 => 224000,
|
459 |
+
12 => 256000,
|
460 |
+
13 => 320000,
|
461 |
+
14 => 384000,
|
462 |
+
15 => 448000,
|
463 |
+
16 => 512000,
|
464 |
+
17 => 576000,
|
465 |
+
18 => 640000
|
466 |
+
);
|
467 |
+
return (isset($AC3bitrateLookup[$framesizeid]) ? $AC3bitrateLookup[$framesizeid] : false);
|
468 |
+
}
|
469 |
+
|
470 |
+
|
471 |
+
}
|
472 |
+
|
473 |
+
?>
|
includes/lib/getid3/module.audio.mp3.php
ADDED
@@ -0,0 +1,2011 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
/////////////////////////////////////////////////////////////////
|
7 |
+
// See readme.txt for more details //
|
8 |
+
/////////////////////////////////////////////////////////////////
|
9 |
+
// //
|
10 |
+
// module.audio.mp3.php //
|
11 |
+
// module for analyzing MP3 files //
|
12 |
+
// dependencies: NONE //
|
13 |
+
// ///
|
14 |
+
/////////////////////////////////////////////////////////////////
|
15 |
+
|
16 |
+
|
17 |
+
// number of frames to scan to determine if MPEG-audio sequence is valid
|
18 |
+
// Lower this number to 5-20 for faster scanning
|
19 |
+
// Increase this number to 50+ for most accurate detection of valid VBR/CBR
|
20 |
+
// mpeg-audio streams
|
21 |
+
define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
|
22 |
+
|
23 |
+
|
24 |
+
class getid3_mp3 extends getid3_handler
|
25 |
+
{
|
26 |
+
|
27 |
+
var $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
|
28 |
+
|
29 |
+
function Analyze() {
|
30 |
+
$info = &$this->getid3->info;
|
31 |
+
|
32 |
+
$initialOffset = $info['avdataoffset'];
|
33 |
+
|
34 |
+
if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) {
|
35 |
+
if ($this->allow_bruteforce) {
|
36 |
+
$info['error'][] = 'Rescanning file in BruteForce mode';
|
37 |
+
$this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info);
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
|
42 |
+
if (isset($info['mpeg']['audio']['bitrate_mode'])) {
|
43 |
+
$info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
|
44 |
+
}
|
45 |
+
|
46 |
+
if (((isset($info['id3v2']['headerlength']) && ($info['avdataoffset'] > $info['id3v2']['headerlength'])) || (!isset($info['id3v2']) && ($info['avdataoffset'] > 0) && ($info['avdataoffset'] != $initialOffset)))) {
|
47 |
+
|
48 |
+
$synchoffsetwarning = 'Unknown data before synch ';
|
49 |
+
if (isset($info['id3v2']['headerlength'])) {
|
50 |
+
$synchoffsetwarning .= '(ID3v2 header ends at '.$info['id3v2']['headerlength'].', then '.($info['avdataoffset'] - $info['id3v2']['headerlength']).' bytes garbage, ';
|
51 |
+
} elseif ($initialOffset > 0) {
|
52 |
+
$synchoffsetwarning .= '(should be at '.$initialOffset.', ';
|
53 |
+
} else {
|
54 |
+
$synchoffsetwarning .= '(should be at beginning of file, ';
|
55 |
+
}
|
56 |
+
$synchoffsetwarning .= 'synch detected at '.$info['avdataoffset'].')';
|
57 |
+
if (isset($info['audio']['bitrate_mode']) && ($info['audio']['bitrate_mode'] == 'cbr')) {
|
58 |
+
|
59 |
+
if (!empty($info['id3v2']['headerlength']) && (($info['avdataoffset'] - $info['id3v2']['headerlength']) == $info['mpeg']['audio']['framelength'])) {
|
60 |
+
|
61 |
+
$synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.';
|
62 |
+
$info['audio']['codec'] = 'LAME';
|
63 |
+
$CurrentDataLAMEversionString = 'LAME3.';
|
64 |
+
|
65 |
+
} elseif (empty($info['id3v2']['headerlength']) && ($info['avdataoffset'] == $info['mpeg']['audio']['framelength'])) {
|
66 |
+
|
67 |
+
$synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.';
|
68 |
+
$info['audio']['codec'] = 'LAME';
|
69 |
+
$CurrentDataLAMEversionString = 'LAME3.';
|
70 |
+
|
71 |
+
}
|
72 |
+
|
73 |
+
}
|
74 |
+
$info['warning'][] = $synchoffsetwarning;
|
75 |
+
|
76 |
+
}
|
77 |
+
|
78 |
+
if (isset($info['mpeg']['audio']['LAME'])) {
|
79 |
+
$info['audio']['codec'] = 'LAME';
|
80 |
+
if (!empty($info['mpeg']['audio']['LAME']['long_version'])) {
|
81 |
+
$info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['long_version'], "\x00");
|
82 |
+
} elseif (!empty($info['mpeg']['audio']['LAME']['short_version'])) {
|
83 |
+
$info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['short_version'], "\x00");
|
84 |
+
}
|
85 |
+
}
|
86 |
+
|
87 |
+
$CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : (isset($info['audio']['encoder']) ? $info['audio']['encoder'] : ''));
|
88 |
+
if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) {
|
89 |
+
// a version number of LAME that does not end with a number like "LAME3.92"
|
90 |
+
// or with a closing parenthesis like "LAME3.88 (alpha)"
|
91 |
+
// or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92)
|
92 |
+
|
93 |
+
// not sure what the actual last frame length will be, but will be less than or equal to 1441
|
94 |
+
$PossiblyLongerLAMEversion_FrameLength = 1441;
|
95 |
+
|
96 |
+
// Not sure what version of LAME this is - look in padding of last frame for longer version string
|
97 |
+
$PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
|
98 |
+
fseek($this->getid3->fp, $PossibleLAMEversionStringOffset);
|
99 |
+
$PossiblyLongerLAMEversion_Data = fread($this->getid3->fp, $PossiblyLongerLAMEversion_FrameLength);
|
100 |
+
switch (substr($CurrentDataLAMEversionString, -1)) {
|
101 |
+
case 'a':
|
102 |
+
case 'b':
|
103 |
+
// "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example
|
104 |
+
// need to trim off "a" to match longer string
|
105 |
+
$CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1);
|
106 |
+
break;
|
107 |
+
}
|
108 |
+
if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) {
|
109 |
+
if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) {
|
110 |
+
$PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3" "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)"
|
111 |
+
if (empty($info['audio']['encoder']) || (strlen($PossiblyLongerLAMEversion_NewString) > strlen($info['audio']['encoder']))) {
|
112 |
+
$info['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString;
|
113 |
+
}
|
114 |
+
}
|
115 |
+
}
|
116 |
+
}
|
117 |
+
if (!empty($info['audio']['encoder'])) {
|
118 |
+
$info['audio']['encoder'] = rtrim($info['audio']['encoder'], "\x00 ");
|
119 |
+
}
|
120 |
+
|
121 |
+
switch (isset($info['mpeg']['audio']['layer']) ? $info['mpeg']['audio']['layer'] : '') {
|
122 |
+
case 1:
|
123 |
+
case 2:
|
124 |
+
$info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
|
125 |
+
break;
|
126 |
+
}
|
127 |
+
if (isset($info['fileformat']) && ($info['fileformat'] == 'mp3')) {
|
128 |
+
switch ($info['audio']['dataformat']) {
|
129 |
+
case 'mp1':
|
130 |
+
case 'mp2':
|
131 |
+
case 'mp3':
|
132 |
+
$info['fileformat'] = $info['audio']['dataformat'];
|
133 |
+
break;
|
134 |
+
|
135 |
+
default:
|
136 |
+
$info['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"';
|
137 |
+
break;
|
138 |
+
}
|
139 |
+
}
|
140 |
+
|
141 |
+
if (empty($info['fileformat'])) {
|
142 |
+
unset($info['fileformat']);
|
143 |
+
unset($info['audio']['bitrate_mode']);
|
144 |
+
unset($info['avdataoffset']);
|
145 |
+
unset($info['avdataend']);
|
146 |
+
return false;
|
147 |
+
}
|
148 |
+
|
149 |
+
$info['mime_type'] = 'audio/mpeg';
|
150 |
+
$info['audio']['lossless'] = false;
|
151 |
+
|
152 |
+
// Calculate playtime
|
153 |
+
if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
|
154 |
+
$info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
|
155 |
+
}
|
156 |
+
|
157 |
+
$info['audio']['encoder_options'] = $this->GuessEncoderOptions();
|
158 |
+
|
159 |
+
return true;
|
160 |
+
}
|
161 |
+
|
162 |
+
|
163 |
+
function GuessEncoderOptions() {
|
164 |
+
// shortcuts
|
165 |
+
$info = &$this->getid3->info;
|
166 |
+
if (!empty($info['mpeg']['audio'])) {
|
167 |
+
$thisfile_mpeg_audio = &$info['mpeg']['audio'];
|
168 |
+
if (!empty($thisfile_mpeg_audio['LAME'])) {
|
169 |
+
$thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
|
170 |
+
}
|
171 |
+
}
|
172 |
+
|
173 |
+
$encoder_options = '';
|
174 |
+
static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256);
|
175 |
+
|
176 |
+
if (isset($thisfile_mpeg_audio['VBR_method']) && ($thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {
|
177 |
+
|
178 |
+
$encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
|
179 |
+
|
180 |
+
} elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
|
181 |
+
|
182 |
+
$encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
|
183 |
+
|
184 |
+
} elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {
|
185 |
+
|
186 |
+
static $KnownEncoderValues = array();
|
187 |
+
if (empty($KnownEncoderValues)) {
|
188 |
+
|
189 |
+
//$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name';
|
190 |
+
$KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane'; // 3.90, 3.90.1, 3.92
|
191 |
+
$KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane'; // 3.90.2, 3.90.3, 3.91
|
192 |
+
$KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane'; // 3.94, 3.95
|
193 |
+
$KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme'; // 3.90, 3.90.1, 3.92
|
194 |
+
$KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme'; // 3.90.2, 3.91
|
195 |
+
$KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme'; // 3.90.3
|
196 |
+
$KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme'; // 3.90, 3.90.1, 3.92
|
197 |
+
$KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme'; // 3.90.2, 3.90.3, 3.91
|
198 |
+
$KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
|
199 |
+
$KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard'; // 3.90.3
|
200 |
+
$KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
|
201 |
+
$KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3
|
202 |
+
$KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix'; // 3.90, 3.90.1, 3.92
|
203 |
+
$KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix'; // 3.90.2, 3.90.3, 3.91
|
204 |
+
$KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix'; // 3.94, 3.95
|
205 |
+
$KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium'; // 3.90.3
|
206 |
+
$KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium'; // 3.90.3
|
207 |
+
|
208 |
+
$KnownEncoderValues[0xFF][99][1][1][1][2][0] = '--preset studio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
|
209 |
+
$KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio'; // 3.90.3, 3.93.1
|
210 |
+
$KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio'; // 3.93
|
211 |
+
$KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio'; // 3.94, 3.95
|
212 |
+
$KnownEncoderValues[0xC0][88][1][1][1][2][0] = '--preset cd'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
|
213 |
+
$KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd'; // 3.90.3, 3.93.1
|
214 |
+
$KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd'; // 3.93
|
215 |
+
$KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd'; // 3.94, 3.95
|
216 |
+
$KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
|
217 |
+
$KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi'; // 3.90.3, 3.93, 3.93.1
|
218 |
+
$KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi'; // 3.94, 3.95
|
219 |
+
$KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
|
220 |
+
$KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
|
221 |
+
$KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
|
222 |
+
$KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm'; // 3.90.3, 3.93, 3.93.1
|
223 |
+
$KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm'; // 3.94, 3.95
|
224 |
+
$KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice'; // 3.90.3, 3.93, 3.93.1
|
225 |
+
$KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice'; // 3.94, 3.95
|
226 |
+
$KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice'; // 3.94a14
|
227 |
+
$KnownEncoderValues[0x28][65][1][1][0][2][7500] = '--preset mw-us'; // 3.90, 3.90.1, 3.92
|
228 |
+
$KnownEncoderValues[0x28][65][1][1][0][2][7600] = '--preset mw-us'; // 3.90.2, 3.91
|
229 |
+
$KnownEncoderValues[0x28][58][2][2][0][2][7000] = '--preset mw-us'; // 3.90.3, 3.93, 3.93.1
|
230 |
+
$KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us'; // 3.94, 3.95
|
231 |
+
$KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us'; // 3.94a14
|
232 |
+
$KnownEncoderValues[0x28][57][2][1][0][4][8800] = '--preset mw-us'; // 3.94a15
|
233 |
+
$KnownEncoderValues[0x18][58][2][2][0][2][4000] = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1
|
234 |
+
$KnownEncoderValues[0x18][58][2][2][0][2][3900] = '--preset phon+/lw/mw-eu/sw'; // 3.93
|
235 |
+
$KnownEncoderValues[0x18][57][2][1][0][4][5900] = '--preset phon+/lw/mw-eu/sw'; // 3.94, 3.95
|
236 |
+
$KnownEncoderValues[0x18][57][2][1][0][4][6200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a14
|
237 |
+
$KnownEncoderValues[0x18][57][2][1][0][4][3200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a15
|
238 |
+
$KnownEncoderValues[0x10][58][2][2][0][2][3800] = '--preset phone'; // 3.90.3, 3.93.1
|
239 |
+
$KnownEncoderValues[0x10][58][2][2][0][2][3700] = '--preset phone'; // 3.93
|
240 |
+
$KnownEncoderValues[0x10][57][2][1][0][4][5600] = '--preset phone'; // 3.94, 3.95
|
241 |
+
}
|
242 |
+
|
243 |
+
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']])) {
|
244 |
+
|
245 |
+
$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']];
|
246 |
+
|
247 |
+
} 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']])) {
|
248 |
+
|
249 |
+
$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']];
|
250 |
+
|
251 |
+
} elseif ($info['audio']['bitrate_mode'] == 'vbr') {
|
252 |
+
|
253 |
+
// http://gabriel.mp3-tech.org/mp3infotag.html
|
254 |
+
// int Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h
|
255 |
+
|
256 |
+
|
257 |
+
$LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10);
|
258 |
+
$LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10);
|
259 |
+
$encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value;
|
260 |
+
|
261 |
+
} elseif ($info['audio']['bitrate_mode'] == 'cbr') {
|
262 |
+
|
263 |
+
$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
|
264 |
+
|
265 |
+
} else {
|
266 |
+
|
267 |
+
$encoder_options = strtoupper($info['audio']['bitrate_mode']);
|
268 |
+
|
269 |
+
}
|
270 |
+
|
271 |
+
} elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) {
|
272 |
+
|
273 |
+
$encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr'];
|
274 |
+
|
275 |
+
} elseif (!empty($info['audio']['bitrate'])) {
|
276 |
+
|
277 |
+
if ($info['audio']['bitrate_mode'] == 'cbr') {
|
278 |
+
$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
|
279 |
+
} else {
|
280 |
+
$encoder_options = strtoupper($info['audio']['bitrate_mode']);
|
281 |
+
}
|
282 |
+
|
283 |
+
}
|
284 |
+
if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) {
|
285 |
+
$encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min'];
|
286 |
+
}
|
287 |
+
|
288 |
+
if (!empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']) || !empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'])) {
|
289 |
+
$encoder_options .= ' --nogap';
|
290 |
+
}
|
291 |
+
|
292 |
+
if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) {
|
293 |
+
$ExplodedOptions = explode(' ', $encoder_options, 4);
|
294 |
+
if ($ExplodedOptions[0] == '--r3mix') {
|
295 |
+
$ExplodedOptions[1] = 'r3mix';
|
296 |
+
}
|
297 |
+
switch ($ExplodedOptions[0]) {
|
298 |
+
case '--preset':
|
299 |
+
case '--alt-preset':
|
300 |
+
case '--r3mix':
|
301 |
+
if ($ExplodedOptions[1] == 'fast') {
|
302 |
+
$ExplodedOptions[1] .= ' '.$ExplodedOptions[2];
|
303 |
+
}
|
304 |
+
switch ($ExplodedOptions[1]) {
|
305 |
+
case 'portable':
|
306 |
+
case 'medium':
|
307 |
+
case 'standard':
|
308 |
+
case 'extreme':
|
309 |
+
case 'insane':
|
310 |
+
case 'fast portable':
|
311 |
+
case 'fast medium':
|
312 |
+
case 'fast standard':
|
313 |
+
case 'fast extreme':
|
314 |
+
case 'fast insane':
|
315 |
+
case 'r3mix':
|
316 |
+
static $ExpectedLowpass = array(
|
317 |
+
'insane|20500' => 20500,
|
318 |
+
'insane|20600' => 20600, // 3.90.2, 3.90.3, 3.91
|
319 |
+
'medium|18000' => 18000,
|
320 |
+
'fast medium|18000' => 18000,
|
321 |
+
'extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95
|
322 |
+
'extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1
|
323 |
+
'fast extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95
|
324 |
+
'fast extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1
|
325 |
+
'standard|19000' => 19000,
|
326 |
+
'fast standard|19000' => 19000,
|
327 |
+
'r3mix|19500' => 19500, // 3.90, 3.90.1, 3.92
|
328 |
+
'r3mix|19600' => 19600, // 3.90.2, 3.90.3, 3.91
|
329 |
+
'r3mix|18000' => 18000, // 3.94, 3.95
|
330 |
+
);
|
331 |
+
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))) {
|
332 |
+
$encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency'];
|
333 |
+
}
|
334 |
+
break;
|
335 |
+
|
336 |
+
default:
|
337 |
+
break;
|
338 |
+
}
|
339 |
+
break;
|
340 |
+
}
|
341 |
+
}
|
342 |
+
|
343 |
+
if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) {
|
344 |
+
if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) {
|
345 |
+
$encoder_options .= ' --resample 44100';
|
346 |
+
} elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) {
|
347 |
+
$encoder_options .= ' --resample 48000';
|
348 |
+
} elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) {
|
349 |
+
switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) {
|
350 |
+
case 0: // <= 32000
|
351 |
+
// may or may not be same as source frequency - ignore
|
352 |
+
break;
|
353 |
+
case 1: // 44100
|
354 |
+
case 2: // 48000
|
355 |
+
case 3: // 48000+
|
356 |
+
$ExplodedOptions = explode(' ', $encoder_options, 4);
|
357 |
+
switch ($ExplodedOptions[0]) {
|
358 |
+
case '--preset':
|
359 |
+
case '--alt-preset':
|
360 |
+
switch ($ExplodedOptions[1]) {
|
361 |
+
case 'fast':
|
362 |
+
case 'portable':
|
363 |
+
case 'medium':
|
364 |
+
case 'standard':
|
365 |
+
case 'extreme':
|
366 |
+
case 'insane':
|
367 |
+
$encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
|
368 |
+
break;
|
369 |
+
|
370 |
+
default:
|
371 |
+
static $ExpectedResampledRate = array(
|
372 |
+
'phon+/lw/mw-eu/sw|16000' => 16000,
|
373 |
+
'mw-us|24000' => 24000, // 3.95
|
374 |
+
'mw-us|32000' => 32000, // 3.93
|
375 |
+
'mw-us|16000' => 16000, // 3.92
|
376 |
+
'phone|16000' => 16000,
|
377 |
+
'phone|11025' => 11025, // 3.94a15
|
378 |
+
'radio|32000' => 32000, // 3.94a15
|
379 |
+
'fm/radio|32000' => 32000, // 3.92
|
380 |
+
'fm|32000' => 32000, // 3.90
|
381 |
+
'voice|32000' => 32000);
|
382 |
+
if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) {
|
383 |
+
$encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
|
384 |
+
}
|
385 |
+
break;
|
386 |
+
}
|
387 |
+
break;
|
388 |
+
|
389 |
+
case '--r3mix':
|
390 |
+
default:
|
391 |
+
$encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
|
392 |
+
break;
|
393 |
+
}
|
394 |
+
break;
|
395 |
+
}
|
396 |
+
}
|
397 |
+
}
|
398 |
+
if (empty($encoder_options) && !empty($info['audio']['bitrate']) && !empty($info['audio']['bitrate_mode'])) {
|
399 |
+
//$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
|
400 |
+
$encoder_options = strtoupper($info['audio']['bitrate_mode']);
|
401 |
+
}
|
402 |
+
|
403 |
+
return $encoder_options;
|
404 |
+
}
|
405 |
+
|
406 |
+
|
407 |
+
function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
|
408 |
+
static $MPEGaudioVersionLookup;
|
409 |
+
static $MPEGaudioLayerLookup;
|
410 |
+
static $MPEGaudioBitrateLookup;
|
411 |
+
static $MPEGaudioFrequencyLookup;
|
412 |
+
static $MPEGaudioChannelModeLookup;
|
413 |
+
static $MPEGaudioModeExtensionLookup;
|
414 |
+
static $MPEGaudioEmphasisLookup;
|
415 |
+
if (empty($MPEGaudioVersionLookup)) {
|
416 |
+
$MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray();
|
417 |
+
$MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray();
|
418 |
+
$MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray();
|
419 |
+
$MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray();
|
420 |
+
$MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray();
|
421 |
+
$MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray();
|
422 |
+
$MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray();
|
423 |
+
}
|
424 |
+
|
425 |
+
if (fseek($this->getid3->fp, $offset, SEEK_SET) != 0) {
|
426 |
+
$info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset;
|
427 |
+
return false;
|
428 |
+
}
|
429 |
+
//$headerstring = fread($this->getid3->fp, 1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
|
430 |
+
$headerstring = fread($this->getid3->fp, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
|
431 |
+
|
432 |
+
// MP3 audio frame structure:
|
433 |
+
// $aa $aa $aa $aa [$bb $bb] $cc...
|
434 |
+
// where $aa..$aa is the four-byte mpeg-audio header (below)
|
435 |
+
// $bb $bb is the optional 2-byte CRC
|
436 |
+
// and $cc... is the audio data
|
437 |
+
|
438 |
+
$head4 = substr($headerstring, 0, 4);
|
439 |
+
|
440 |
+
static $MPEGaudioHeaderDecodeCache = array();
|
441 |
+
if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
|
442 |
+
$MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
|
443 |
+
} else {
|
444 |
+
$MPEGheaderRawArray = getid3_mp3::MPEGaudioHeaderDecode($head4);
|
445 |
+
$MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
|
446 |
+
}
|
447 |
+
|
448 |
+
static $MPEGaudioHeaderValidCache = array();
|
449 |
+
if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache
|
450 |
+
//$MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
|
451 |
+
$MPEGaudioHeaderValidCache[$head4] = getid3_mp3::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'] = getid3_mp3::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 = getid3_mp3::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 |
+
|
652 |
+
$framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames'];
|
653 |
+
|
654 |
+
if ($thisfile_mpeg_audio['layer'] == '1') {
|
655 |
+
// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
|
656 |
+
//$info['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
|
657 |
+
$info['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12;
|
658 |
+
} else {
|
659 |
+
// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
|
660 |
+
//$info['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
|
661 |
+
$info['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144;
|
662 |
+
}
|
663 |
+
$thisfile_mpeg_audio['framelength'] = floor($framelengthfloat);
|
664 |
+
}
|
665 |
+
|
666 |
+
if ($thisfile_mpeg_audio['xing_flags']['toc']) {
|
667 |
+
$LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
|
668 |
+
for ($i = 0; $i < 100; $i++) {
|
669 |
+
$thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
|
670 |
+
}
|
671 |
+
}
|
672 |
+
if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
|
673 |
+
$thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
|
674 |
+
}
|
675 |
+
|
676 |
+
|
677 |
+
// http://gabriel.mp3-tech.org/mp3infotag.html
|
678 |
+
if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') {
|
679 |
+
|
680 |
+
// shortcut
|
681 |
+
$thisfile_mpeg_audio['LAME'] = array();
|
682 |
+
$thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
|
683 |
+
|
684 |
+
|
685 |
+
$thisfile_mpeg_audio_lame['long_version'] = substr($headerstring, $VBRidOffset + 120, 20);
|
686 |
+
$thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
|
687 |
+
|
688 |
+
if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
|
689 |
+
|
690 |
+
// extra 11 chars are not part of version string when LAMEtag present
|
691 |
+
unset($thisfile_mpeg_audio_lame['long_version']);
|
692 |
+
|
693 |
+
// It the LAME tag was only introduced in LAME v3.90
|
694 |
+
// http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
|
695 |
+
|
696 |
+
// Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
|
697 |
+
// are assuming a 'Xing' identifier offset of 0x24, which is the case for
|
698 |
+
// MPEG-1 non-mono, but not for other combinations
|
699 |
+
$LAMEtagOffsetContant = $VBRidOffset - 0x24;
|
700 |
+
|
701 |
+
// shortcuts
|
702 |
+
$thisfile_mpeg_audio_lame['RGAD'] = array('track'=>array(), 'album'=>array());
|
703 |
+
$thisfile_mpeg_audio_lame_RGAD = &$thisfile_mpeg_audio_lame['RGAD'];
|
704 |
+
$thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track'];
|
705 |
+
$thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album'];
|
706 |
+
$thisfile_mpeg_audio_lame['raw'] = array();
|
707 |
+
$thisfile_mpeg_audio_lame_raw = &$thisfile_mpeg_audio_lame['raw'];
|
708 |
+
|
709 |
+
// byte $9B VBR Quality
|
710 |
+
// This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
|
711 |
+
// Actually overwrites original Xing bytes
|
712 |
+
unset($thisfile_mpeg_audio['VBR_scale']);
|
713 |
+
$thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1));
|
714 |
+
|
715 |
+
// bytes $9C-$A4 Encoder short VersionString
|
716 |
+
$thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9);
|
717 |
+
|
718 |
+
// byte $A5 Info Tag revision + VBR method
|
719 |
+
$LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1));
|
720 |
+
|
721 |
+
$thisfile_mpeg_audio_lame['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
|
722 |
+
$thisfile_mpeg_audio_lame_raw['vbr_method'] = $LAMEtagRevisionVBRmethod & 0x0F;
|
723 |
+
$thisfile_mpeg_audio_lame['vbr_method'] = getid3_mp3::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
|
724 |
+
$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'
|
725 |
+
|
726 |
+
// byte $A6 Lowpass filter value
|
727 |
+
$thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100;
|
728 |
+
|
729 |
+
// bytes $A7-$AE Replay Gain
|
730 |
+
// http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
|
731 |
+
// bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
|
732 |
+
if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') {
|
733 |
+
// LAME 3.94a16 and later - 9.23 fixed point
|
734 |
+
// ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375
|
735 |
+
$thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608);
|
736 |
+
} else {
|
737 |
+
// LAME 3.94a15 and earlier - 32-bit floating point
|
738 |
+
// Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15
|
739 |
+
$thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4));
|
740 |
+
}
|
741 |
+
if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) {
|
742 |
+
unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
|
743 |
+
} else {
|
744 |
+
$thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
|
745 |
+
}
|
746 |
+
|
747 |
+
$thisfile_mpeg_audio_lame_raw['RGAD_track'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2));
|
748 |
+
$thisfile_mpeg_audio_lame_raw['RGAD_album'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2));
|
749 |
+
|
750 |
+
|
751 |
+
if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) {
|
752 |
+
|
753 |
+
$thisfile_mpeg_audio_lame_RGAD_track['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13;
|
754 |
+
$thisfile_mpeg_audio_lame_RGAD_track['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10;
|
755 |
+
$thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9;
|
756 |
+
$thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF;
|
757 |
+
$thisfile_mpeg_audio_lame_RGAD_track['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']);
|
758 |
+
$thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']);
|
759 |
+
$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']);
|
760 |
+
|
761 |
+
if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
|
762 |
+
$info['replay_gain']['track']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
|
763 |
+
}
|
764 |
+
$info['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator'];
|
765 |
+
$info['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db'];
|
766 |
+
} else {
|
767 |
+
unset($thisfile_mpeg_audio_lame_RGAD['track']);
|
768 |
+
}
|
769 |
+
if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) {
|
770 |
+
|
771 |
+
$thisfile_mpeg_audio_lame_RGAD_album['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13;
|
772 |
+
$thisfile_mpeg_audio_lame_RGAD_album['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10;
|
773 |
+
$thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9;
|
774 |
+
$thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF;
|
775 |
+
$thisfile_mpeg_audio_lame_RGAD_album['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']);
|
776 |
+
$thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']);
|
777 |
+
$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']);
|
778 |
+
|
779 |
+
if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
|
780 |
+
$info['replay_gain']['album']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
|
781 |
+
}
|
782 |
+
$info['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator'];
|
783 |
+
$info['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db'];
|
784 |
+
} else {
|
785 |
+
unset($thisfile_mpeg_audio_lame_RGAD['album']);
|
786 |
+
}
|
787 |
+
if (empty($thisfile_mpeg_audio_lame_RGAD)) {
|
788 |
+
unset($thisfile_mpeg_audio_lame['RGAD']);
|
789 |
+
}
|
790 |
+
|
791 |
+
|
792 |
+
// byte $AF Encoding flags + ATH Type
|
793 |
+
$EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1));
|
794 |
+
$thisfile_mpeg_audio_lame['encoding_flags']['nspsytune'] = (bool) ($EncodingFlagsATHtype & 0x10);
|
795 |
+
$thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20);
|
796 |
+
$thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'] = (bool) ($EncodingFlagsATHtype & 0x40);
|
797 |
+
$thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] = (bool) ($EncodingFlagsATHtype & 0x80);
|
798 |
+
$thisfile_mpeg_audio_lame['ath_type'] = $EncodingFlagsATHtype & 0x0F;
|
799 |
+
|
800 |
+
// byte $B0 if ABR {specified bitrate} else {minimal bitrate}
|
801 |
+
$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1));
|
802 |
+
if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR)
|
803 |
+
$thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
|
804 |
+
} elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR)
|
805 |
+
// ignore
|
806 |
+
} elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate
|
807 |
+
$thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
|
808 |
+
}
|
809 |
+
|
810 |
+
// bytes $B1-$B3 Encoder delays
|
811 |
+
$EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3));
|
812 |
+
$thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12;
|
813 |
+
$thisfile_mpeg_audio_lame['end_padding'] = $EncoderDelays & 0x000FFF;
|
814 |
+
|
815 |
+
// byte $B4 Misc
|
816 |
+
$MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1));
|
817 |
+
$thisfile_mpeg_audio_lame_raw['noise_shaping'] = ($MiscByte & 0x03);
|
818 |
+
$thisfile_mpeg_audio_lame_raw['stereo_mode'] = ($MiscByte & 0x1C) >> 2;
|
819 |
+
$thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
|
820 |
+
$thisfile_mpeg_audio_lame_raw['source_sample_freq'] = ($MiscByte & 0xC0) >> 6;
|
821 |
+
$thisfile_mpeg_audio_lame['noise_shaping'] = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
|
822 |
+
$thisfile_mpeg_audio_lame['stereo_mode'] = getid3_mp3::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
|
823 |
+
$thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
|
824 |
+
$thisfile_mpeg_audio_lame['source_sample_freq'] = getid3_mp3::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);
|
825 |
+
|
826 |
+
// byte $B5 MP3 Gain
|
827 |
+
$thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true);
|
828 |
+
$thisfile_mpeg_audio_lame['mp3_gain_db'] = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
|
829 |
+
$thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));
|
830 |
+
|
831 |
+
// bytes $B6-$B7 Preset and surround info
|
832 |
+
$PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2));
|
833 |
+
// Reserved = ($PresetSurroundBytes & 0xC000);
|
834 |
+
$thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
|
835 |
+
$thisfile_mpeg_audio_lame['surround_info'] = getid3_mp3::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
|
836 |
+
$thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF);
|
837 |
+
$thisfile_mpeg_audio_lame['preset_used'] = getid3_mp3::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
|
838 |
+
if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
|
839 |
+
$info['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org';
|
840 |
+
}
|
841 |
+
if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
|
842 |
+
// this may change if 3.90.4 ever comes out
|
843 |
+
$thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
|
844 |
+
}
|
845 |
+
|
846 |
+
// bytes $B8-$BB MusicLength
|
847 |
+
$thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4));
|
848 |
+
$ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);
|
849 |
+
|
850 |
+
// bytes $BC-$BD MusicCRC
|
851 |
+
$thisfile_mpeg_audio_lame['music_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2));
|
852 |
+
|
853 |
+
// bytes $BE-$BF CRC-16 of Info Tag
|
854 |
+
$thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2));
|
855 |
+
|
856 |
+
|
857 |
+
// LAME CBR
|
858 |
+
if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) {
|
859 |
+
|
860 |
+
$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
|
861 |
+
$thisfile_mpeg_audio['bitrate'] = getid3_mp3::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
|
862 |
+
$info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
|
863 |
+
//if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) {
|
864 |
+
// $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min'];
|
865 |
+
//}
|
866 |
+
|
867 |
+
}
|
868 |
+
|
869 |
+
}
|
870 |
+
}
|
871 |
+
|
872 |
+
} else {
|
873 |
+
|
874 |
+
// not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
|
875 |
+
$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
|
876 |
+
if ($recursivesearch) {
|
877 |
+
$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
|
878 |
+
if ($this->RecursiveFrameScanning($offset, $nextframetestoffset, true)) {
|
879 |
+
$recursivesearch = false;
|
880 |
+
$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
|
881 |
+
}
|
882 |
+
if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') {
|
883 |
+
$info['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.';
|
884 |
+
}
|
885 |
+
}
|
886 |
+
|
887 |
+
}
|
888 |
+
|
889 |
+
}
|
890 |
+
|
891 |
+
if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) {
|
892 |
+
if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) {
|
893 |
+
if (isset($info['fileformat']) && ($info['fileformat'] == 'riff')) {
|
894 |
+
// ignore, audio data is broken into chunks so will always be data "missing"
|
895 |
+
} elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) {
|
896 |
+
$info['warning'][] = 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)';
|
897 |
+
} else {
|
898 |
+
$info['warning'][] = 'Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)';
|
899 |
+
}
|
900 |
+
} else {
|
901 |
+
if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) {
|
902 |
+
// $prenullbytefileoffset = ftell($this->getid3->fp);
|
903 |
+
// fseek($this->getid3->fp, $info['avdataend'], SEEK_SET);
|
904 |
+
// $PossibleNullByte = fread($this->getid3->fp, 1);
|
905 |
+
// fseek($this->getid3->fp, $prenullbytefileoffset, SEEK_SET);
|
906 |
+
// if ($PossibleNullByte === "\x00") {
|
907 |
+
$info['avdataend']--;
|
908 |
+
// $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
|
909 |
+
// } else {
|
910 |
+
// $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)';
|
911 |
+
// }
|
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 |
+
}
|
916 |
+
}
|
917 |
+
|
918 |
+
if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) {
|
919 |
+
if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) {
|
920 |
+
$framebytelength = $this->FreeFormatFrameLength($offset, true);
|
921 |
+
if ($framebytelength > 0) {
|
922 |
+
$thisfile_mpeg_audio['framelength'] = $framebytelength;
|
923 |
+
if ($thisfile_mpeg_audio['layer'] == '1') {
|
924 |
+
// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
|
925 |
+
$info['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
|
926 |
+
} else {
|
927 |
+
// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
|
928 |
+
$info['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
|
929 |
+
}
|
930 |
+
} else {
|
931 |
+
$info['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header';
|
932 |
+
}
|
933 |
+
}
|
934 |
+
}
|
935 |
+
|
936 |
+
if (isset($thisfile_mpeg_audio['VBR_frames']) ? $thisfile_mpeg_audio['VBR_frames'] : '') {
|
937 |
+
switch ($thisfile_mpeg_audio['bitrate_mode']) {
|
938 |
+
case 'vbr':
|
939 |
+
case 'abr':
|
940 |
+
$bytes_per_frame = 1152;
|
941 |
+
if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) {
|
942 |
+
$bytes_per_frame = 384;
|
943 |
+
} elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) {
|
944 |
+
$bytes_per_frame = 576;
|
945 |
+
}
|
946 |
+
$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);
|
947 |
+
if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
|
948 |
+
$info['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate'];
|
949 |
+
$thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
|
950 |
+
}
|
951 |
+
break;
|
952 |
+
}
|
953 |
+
}
|
954 |
+
|
955 |
+
// End variable-bitrate headers
|
956 |
+
////////////////////////////////////////////////////////////////////////////////////
|
957 |
+
|
958 |
+
if ($recursivesearch) {
|
959 |
+
|
960 |
+
if (!$this->RecursiveFrameScanning($offset, $nextframetestoffset, $ScanAsCBR)) {
|
961 |
+
return false;
|
962 |
+
}
|
963 |
+
|
964 |
+
}
|
965 |
+
|
966 |
+
|
967 |
+
//if (false) {
|
968 |
+
// // experimental side info parsing section - not returning anything useful yet
|
969 |
+
//
|
970 |
+
// $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData);
|
971 |
+
// $SideInfoOffset = 0;
|
972 |
+
//
|
973 |
+
// if ($thisfile_mpeg_audio['version'] == '1') {
|
974 |
+
// if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
|
975 |
+
// // MPEG-1 (mono)
|
976 |
+
// $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
|
977 |
+
// $SideInfoOffset += 9;
|
978 |
+
// $SideInfoOffset += 5;
|
979 |
+
// } else {
|
980 |
+
// // MPEG-1 (stereo, joint-stereo, dual-channel)
|
981 |
+
// $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
|
982 |
+
// $SideInfoOffset += 9;
|
983 |
+
// $SideInfoOffset += 3;
|
984 |
+
// }
|
985 |
+
// } else { // 2 or 2.5
|
986 |
+
// if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
|
987 |
+
// // MPEG-2, MPEG-2.5 (mono)
|
988 |
+
// $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
|
989 |
+
// $SideInfoOffset += 8;
|
990 |
+
// $SideInfoOffset += 1;
|
991 |
+
// } else {
|
992 |
+
// // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
|
993 |
+
// $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
|
994 |
+
// $SideInfoOffset += 8;
|
995 |
+
// $SideInfoOffset += 2;
|
996 |
+
// }
|
997 |
+
// }
|
998 |
+
//
|
999 |
+
// if ($thisfile_mpeg_audio['version'] == '1') {
|
1000 |
+
// for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
|
1001 |
+
// for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
|
1002 |
+
// $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
|
1003 |
+
// $SideInfoOffset += 2;
|
1004 |
+
// }
|
1005 |
+
// }
|
1006 |
+
// }
|
1007 |
+
// for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) {
|
1008 |
+
// for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
|
1009 |
+
// $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
|
1010 |
+
// $SideInfoOffset += 12;
|
1011 |
+
// $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
|
1012 |
+
// $SideInfoOffset += 9;
|
1013 |
+
// $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
|
1014 |
+
// $SideInfoOffset += 8;
|
1015 |
+
// if ($thisfile_mpeg_audio['version'] == '1') {
|
1016 |
+
// $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
|
1017 |
+
// $SideInfoOffset += 4;
|
1018 |
+
// } else {
|
1019 |
+
// $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
|
1020 |
+
// $SideInfoOffset += 9;
|
1021 |
+
// }
|
1022 |
+
// $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
|
1023 |
+
// $SideInfoOffset += 1;
|
1024 |
+
//
|
1025 |
+
// if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') {
|
1026 |
+
//
|
1027 |
+
// $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
|
1028 |
+
// $SideInfoOffset += 2;
|
1029 |
+
// $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
|
1030 |
+
// $SideInfoOffset += 1;
|
1031 |
+
//
|
1032 |
+
// for ($region = 0; $region < 2; $region++) {
|
1033 |
+
// $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
|
1034 |
+
// $SideInfoOffset += 5;
|
1035 |
+
// }
|
1036 |
+
// $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0;
|
1037 |
+
//
|
1038 |
+
// for ($window = 0; $window < 3; $window++) {
|
1039 |
+
// $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
|
1040 |
+
// $SideInfoOffset += 3;
|
1041 |
+
// }
|
1042 |
+
//
|
1043 |
+
// } else {
|
1044 |
+
//
|
1045 |
+
// for ($region = 0; $region < 3; $region++) {
|
1046 |
+
// $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
|
1047 |
+
// $SideInfoOffset += 5;
|
1048 |
+
// }
|
1049 |
+
//
|
1050 |
+
// $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
|
1051 |
+
// $SideInfoOffset += 4;
|
1052 |
+
// $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
|
1053 |
+
// $SideInfoOffset += 3;
|
1054 |
+
// $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0;
|
1055 |
+
// }
|
1056 |
+
//
|
1057 |
+
// if ($thisfile_mpeg_audio['version'] == '1') {
|
1058 |
+
// $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
|
1059 |
+
// $SideInfoOffset += 1;
|
1060 |
+
// }
|
1061 |
+
// $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
|
1062 |
+
// $SideInfoOffset += 1;
|
1063 |
+
// $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
|
1064 |
+
// $SideInfoOffset += 1;
|
1065 |
+
// }
|
1066 |
+
// }
|
1067 |
+
//}
|
1068 |
+
|
1069 |
+
return true;
|
1070 |
+
}
|
1071 |
+
|
1072 |
+
function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
|
1073 |
+
$info = &$this->getid3->info;
|
1074 |
+
$firstframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
|
1075 |
+
$this->decodeMPEGaudioHeader($offset, $firstframetestarray, false);
|
1076 |
+
|
1077 |
+
for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) {
|
1078 |
+
// check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
|
1079 |
+
if (($nextframetestoffset + 4) >= $info['avdataend']) {
|
1080 |
+
// end of file
|
1081 |
+
return true;
|
1082 |
+
}
|
1083 |
+
|
1084 |
+
$nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
|
1085 |
+
if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) {
|
1086 |
+
if ($ScanAsCBR) {
|
1087 |
+
// force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header
|
1088 |
+
if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) {
|
1089 |
+
return false;
|
1090 |
+
}
|
1091 |
+
}
|
1092 |
+
|
1093 |
+
|
1094 |
+
// next frame is OK, get ready to check the one after that
|
1095 |
+
if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) {
|
1096 |
+
$nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
|
1097 |
+
} else {
|
1098 |
+
$info['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.';
|
1099 |
+
return false;
|
1100 |
+
}
|
1101 |
+
|
1102 |
+
} elseif (!empty($firstframetestarray['mpeg']['audio']['framelength']) && (($nextframetestoffset + $firstframetestarray['mpeg']['audio']['framelength']) > $info['avdataend'])) {
|
1103 |
+
|
1104 |
+
// 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
|
1105 |
+
return true;
|
1106 |
+
|
1107 |
+
} else {
|
1108 |
+
|
1109 |
+
// next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
|
1110 |
+
$info['warning'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.';
|
1111 |
+
|
1112 |
+
return false;
|
1113 |
+
}
|
1114 |
+
}
|
1115 |
+
return true;
|
1116 |
+
}
|
1117 |
+
|
1118 |
+
function FreeFormatFrameLength($offset, $deepscan=false) {
|
1119 |
+
$info = &$this->getid3->info;
|
1120 |
+
|
1121 |
+
fseek($this->getid3->fp, $offset, SEEK_SET);
|
1122 |
+
$MPEGaudioData = fread($this->getid3->fp, 32768);
|
1123 |
+
|
1124 |
+
$SyncPattern1 = substr($MPEGaudioData, 0, 4);
|
1125 |
+
// may be different pattern due to padding
|
1126 |
+
$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
|
1127 |
+
if ($SyncPattern2 === $SyncPattern1) {
|
1128 |
+
$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
|
1129 |
+
}
|
1130 |
+
|
1131 |
+
$framelength = false;
|
1132 |
+
$framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
|
1133 |
+
$framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
|
1134 |
+
if ($framelength1 > 4) {
|
1135 |
+
$framelength = $framelength1;
|
1136 |
+
}
|
1137 |
+
if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
|
1138 |
+
$framelength = $framelength2;
|
1139 |
+
}
|
1140 |
+
if (!$framelength) {
|
1141 |
+
|
1142 |
+
// LAME 3.88 has a different value for modeextension on the first frame vs the rest
|
1143 |
+
$framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4);
|
1144 |
+
$framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4);
|
1145 |
+
|
1146 |
+
if ($framelength1 > 4) {
|
1147 |
+
$framelength = $framelength1;
|
1148 |
+
}
|
1149 |
+
if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
|
1150 |
+
$framelength = $framelength2;
|
1151 |
+
}
|
1152 |
+
if (!$framelength) {
|
1153 |
+
$info['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset;
|
1154 |
+
return false;
|
1155 |
+
} else {
|
1156 |
+
$info['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)';
|
1157 |
+
$info['audio']['codec'] = 'LAME';
|
1158 |
+
$info['audio']['encoder'] = 'LAME3.88';
|
1159 |
+
$SyncPattern1 = substr($SyncPattern1, 0, 3);
|
1160 |
+
$SyncPattern2 = substr($SyncPattern2, 0, 3);
|
1161 |
+
}
|
1162 |
+
}
|
1163 |
+
|
1164 |
+
if ($deepscan) {
|
1165 |
+
|
1166 |
+
$ActualFrameLengthValues = array();
|
1167 |
+
$nextoffset = $offset + $framelength;
|
1168 |
+
while ($nextoffset < ($info['avdataend'] - 6)) {
|
1169 |
+
fseek($this->getid3->fp, $nextoffset - 1, SEEK_SET);
|
1170 |
+
$NextSyncPattern = fread($this->getid3->fp, 6);
|
1171 |
+
if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) {
|
1172 |
+
// good - found where expected
|
1173 |
+
$ActualFrameLengthValues[] = $framelength;
|
1174 |
+
} elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) {
|
1175 |
+
// ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
|
1176 |
+
$ActualFrameLengthValues[] = ($framelength - 1);
|
1177 |
+
$nextoffset--;
|
1178 |
+
} elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) {
|
1179 |
+
// ok - found one byte later than expected (last frame was padded, first frame wasn't)
|
1180 |
+
$ActualFrameLengthValues[] = ($framelength + 1);
|
1181 |
+
$nextoffset++;
|
1182 |
+
} else {
|
1183 |
+
$info['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset;
|
1184 |
+
return false;
|
1185 |
+
}
|
1186 |
+
$nextoffset += $framelength;
|
1187 |
+
}
|
1188 |
+
if (count($ActualFrameLengthValues) > 0) {
|
1189 |
+
$framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues)));
|
1190 |
+
}
|
1191 |
+
}
|
1192 |
+
return $framelength;
|
1193 |
+
}
|
1194 |
+
|
1195 |
+
function getOnlyMPEGaudioInfoBruteForce() {
|
1196 |
+
$MPEGaudioHeaderDecodeCache = array();
|
1197 |
+
$MPEGaudioHeaderValidCache = array();
|
1198 |
+
$MPEGaudioHeaderLengthCache = array();
|
1199 |
+
$MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray();
|
1200 |
+
$MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray();
|
1201 |
+
$MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray();
|
1202 |
+
$MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray();
|
1203 |
+
$MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray();
|
1204 |
+
$MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray();
|
1205 |
+
$MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray();
|
1206 |
+
$LongMPEGversionLookup = array();
|
1207 |
+
$LongMPEGlayerLookup = array();
|
1208 |
+
$LongMPEGbitrateLookup = array();
|
1209 |
+
$LongMPEGpaddingLookup = array();
|
1210 |
+
$LongMPEGfrequencyLookup = array();
|
1211 |
+
$Distribution['bitrate'] = array();
|
1212 |
+
$Distribution['frequency'] = array();
|
1213 |
+
$Distribution['layer'] = array();
|
1214 |
+
$Distribution['version'] = array();
|
1215 |
+
$Distribution['padding'] = array();
|
1216 |
+
|
1217 |
+
$info = &$this->getid3->info;
|
1218 |
+
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
1219 |
+
|
1220 |
+
$max_frames_scan = 5000;
|
1221 |
+
$frames_scanned = 0;
|
1222 |
+
|
1223 |
+
$previousvalidframe = $info['avdataoffset'];
|
1224 |
+
while (ftell($this->getid3->fp) < $info['avdataend']) {
|
1225 |
+
set_time_limit(30);
|
1226 |
+
$head4 = fread($this->getid3->fp, 4);
|
1227 |
+
if (strlen($head4) < 4) {
|
1228 |
+
break;
|
1229 |
+
}
|
1230 |
+
if ($head4{0} != "\xFF") {
|
1231 |
+
for ($i = 1; $i < 4; $i++) {
|
1232 |
+
if ($head4{$i} == "\xFF") {
|
1233 |
+
fseek($this->getid3->fp, $i - 4, SEEK_CUR);
|
1234 |
+
continue 2;
|
1235 |
+
}
|
1236 |
+
}
|
1237 |
+
continue;
|
1238 |
+
}
|
1239 |
+
if (!isset($MPEGaudioHeaderDecodeCache[$head4])) {
|
1240 |
+
$MPEGaudioHeaderDecodeCache[$head4] = getid3_mp3::MPEGaudioHeaderDecode($head4);
|
1241 |
+
}
|
1242 |
+
if (!isset($MPEGaudioHeaderValidCache[$head4])) {
|
1243 |
+
$MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false);
|
1244 |
+
}
|
1245 |
+
if ($MPEGaudioHeaderValidCache[$head4]) {
|
1246 |
+
|
1247 |
+
if (!isset($MPEGaudioHeaderLengthCache[$head4])) {
|
1248 |
+
$LongMPEGversionLookup[$head4] = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']];
|
1249 |
+
$LongMPEGlayerLookup[$head4] = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']];
|
1250 |
+
$LongMPEGbitrateLookup[$head4] = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']];
|
1251 |
+
$LongMPEGpaddingLookup[$head4] = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding'];
|
1252 |
+
$LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']];
|
1253 |
+
$MPEGaudioHeaderLengthCache[$head4] = getid3_mp3::MPEGaudioFrameLength(
|
1254 |
+
$LongMPEGbitrateLookup[$head4],
|
1255 |
+
$LongMPEGversionLookup[$head4],
|
1256 |
+
$LongMPEGlayerLookup[$head4],
|
1257 |
+
$LongMPEGpaddingLookup[$head4],
|
1258 |
+
$LongMPEGfrequencyLookup[$head4]);
|
1259 |
+
}
|
1260 |
+
if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
|
1261 |
+
$WhereWeWere = ftell($this->getid3->fp);
|
1262 |
+
fseek($this->getid3->fp, $MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
|
1263 |
+
$next4 = fread($this->getid3->fp, 4);
|
1264 |
+
if ($next4{0} == "\xFF") {
|
1265 |
+
if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
|
1266 |
+
$MPEGaudioHeaderDecodeCache[$next4] = getid3_mp3::MPEGaudioHeaderDecode($next4);
|
1267 |
+
}
|
1268 |
+
if (!isset($MPEGaudioHeaderValidCache[$next4])) {
|
1269 |
+
$MPEGaudioHeaderValidCache[$next4] = getid3_mp3::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
|
1270 |
+
}
|
1271 |
+
if ($MPEGaudioHeaderValidCache[$next4]) {
|
1272 |
+
fseek($this->getid3->fp, -4, SEEK_CUR);
|
1273 |
+
|
1274 |
+
getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]);
|
1275 |
+
getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]);
|
1276 |
+
getid3_lib::safe_inc($Distribution['version'][$LongMPEGversionLookup[$head4]]);
|
1277 |
+
getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]);
|
1278 |
+
getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]);
|
1279 |
+
if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
|
1280 |
+
$pct_data_scanned = (ftell($this->getid3->fp) - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
|
1281 |
+
$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.';
|
1282 |
+
foreach ($Distribution as $key1 => $value1) {
|
1283 |
+
foreach ($value1 as $key2 => $value2) {
|
1284 |
+
$Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
|
1285 |
+
}
|
1286 |
+
}
|
1287 |
+
break;
|
1288 |
+
}
|
1289 |
+
continue;
|
1290 |
+
}
|
1291 |
+
}
|
1292 |
+
unset($next4);
|
1293 |
+
fseek($this->getid3->fp, $WhereWeWere - 3, SEEK_SET);
|
1294 |
+
}
|
1295 |
+
|
1296 |
+
}
|
1297 |
+
}
|
1298 |
+
foreach ($Distribution as $key => $value) {
|
1299 |
+
ksort($Distribution[$key], SORT_NUMERIC);
|
1300 |
+
}
|
1301 |
+
ksort($Distribution['version'], SORT_STRING);
|
1302 |
+
$info['mpeg']['audio']['bitrate_distribution'] = $Distribution['bitrate'];
|
1303 |
+
$info['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency'];
|
1304 |
+
$info['mpeg']['audio']['layer_distribution'] = $Distribution['layer'];
|
1305 |
+
$info['mpeg']['audio']['version_distribution'] = $Distribution['version'];
|
1306 |
+
$info['mpeg']['audio']['padding_distribution'] = $Distribution['padding'];
|
1307 |
+
if (count($Distribution['version']) > 1) {
|
1308 |
+
$info['error'][] = 'Corrupt file - more than one MPEG version detected';
|
1309 |
+
}
|
1310 |
+
if (count($Distribution['layer']) > 1) {
|
1311 |
+
$info['error'][] = 'Corrupt file - more than one MPEG layer detected';
|
1312 |
+
}
|
1313 |
+
if (count($Distribution['frequency']) > 1) {
|
1314 |
+
$info['error'][] = 'Corrupt file - more than one MPEG sample rate detected';
|
1315 |
+
}
|
1316 |
+
|
1317 |
+
|
1318 |
+
$bittotal = 0;
|
1319 |
+
foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) {
|
1320 |
+
if ($bitratevalue != 'free') {
|
1321 |
+
$bittotal += ($bitratevalue * $bitratecount);
|
1322 |
+
}
|
1323 |
+
}
|
1324 |
+
$info['mpeg']['audio']['frame_count'] = array_sum($Distribution['bitrate']);
|
1325 |
+
if ($info['mpeg']['audio']['frame_count'] == 0) {
|
1326 |
+
$info['error'][] = 'no MPEG audio frames found';
|
1327 |
+
return false;
|
1328 |
+
}
|
1329 |
+
$info['mpeg']['audio']['bitrate'] = ($bittotal / $info['mpeg']['audio']['frame_count']);
|
1330 |
+
$info['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr');
|
1331 |
+
$info['mpeg']['audio']['sample_rate'] = getid3_lib::array_max($Distribution['frequency'], true);
|
1332 |
+
|
1333 |
+
$info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
|
1334 |
+
$info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
|
1335 |
+
$info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
|
1336 |
+
$info['audio']['dataformat'] = 'mp'.getid3_lib::array_max($Distribution['layer'], true);
|
1337 |
+
$info['fileformat'] = $info['audio']['dataformat'];
|
1338 |
+
|
1339 |
+
return true;
|
1340 |
+
}
|
1341 |
+
|
1342 |
+
|
1343 |
+
function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) {
|
1344 |
+
// looks for synch, decodes MPEG audio header
|
1345 |
+
|
1346 |
+
$info = &$this->getid3->info;
|
1347 |
+
|
1348 |
+
static $MPEGaudioVersionLookup;
|
1349 |
+
static $MPEGaudioLayerLookup;
|
1350 |
+
static $MPEGaudioBitrateLookup;
|
1351 |
+
if (empty($MPEGaudioVersionLookup)) {
|
1352 |
+
$MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray();
|
1353 |
+
$MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray();
|
1354 |
+
$MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray();
|
1355 |
+
|
1356 |
+
}
|
1357 |
+
|
1358 |
+
fseek($this->getid3->fp, $avdataoffset, SEEK_SET);
|
1359 |
+
$sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
|
1360 |
+
if ($sync_seek_buffer_size <= 0) {
|
1361 |
+
$info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset;
|
1362 |
+
return false;
|
1363 |
+
}
|
1364 |
+
$header = fread($this->getid3->fp, $sync_seek_buffer_size);
|
1365 |
+
$sync_seek_buffer_size = strlen($header);
|
1366 |
+
$SynchSeekOffset = 0;
|
1367 |
+
while ($SynchSeekOffset < $sync_seek_buffer_size) {
|
1368 |
+
if ((($avdataoffset + $SynchSeekOffset) < $info['avdataend']) && !feof($this->getid3->fp)) {
|
1369 |
+
|
1370 |
+
if ($SynchSeekOffset > $sync_seek_buffer_size) {
|
1371 |
+
// if a synch's not found within the first 128k bytes, then give up
|
1372 |
+
$info['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB';
|
1373 |
+
if (isset($info['audio']['bitrate'])) {
|
1374 |
+
unset($info['audio']['bitrate']);
|
1375 |
+
}
|
1376 |
+
if (isset($info['mpeg']['audio'])) {
|
1377 |
+
unset($info['mpeg']['audio']);
|
1378 |
+
}
|
1379 |
+
if (empty($info['mpeg'])) {
|
1380 |
+
unset($info['mpeg']);
|
1381 |
+
}
|
1382 |
+
return false;
|
1383 |
+
|
1384 |
+
} elseif (feof($this->getid3->fp)) {
|
1385 |
+
|
1386 |
+
$info['error'][] = 'Could not find valid MPEG audio synch before end of file';
|
1387 |
+
if (isset($info['audio']['bitrate'])) {
|
1388 |
+
unset($info['audio']['bitrate']);
|
1389 |
+
}
|
1390 |
+
if (isset($info['mpeg']['audio'])) {
|
1391 |
+
unset($info['mpeg']['audio']);
|
1392 |
+
}
|
1393 |
+
if (isset($info['mpeg']) && (!is_array($info['mpeg']) || (count($info['mpeg']) == 0))) {
|
1394 |
+
unset($info['mpeg']);
|
1395 |
+
}
|
1396 |
+
return false;
|
1397 |
+
}
|
1398 |
+
}
|
1399 |
+
|
1400 |
+
if (($SynchSeekOffset + 1) >= strlen($header)) {
|
1401 |
+
$info['error'][] = 'Could not find valid MPEG synch before end of file';
|
1402 |
+
return false;
|
1403 |
+
}
|
1404 |
+
|
1405 |
+
if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
|
1406 |
+
if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
|
1407 |
+
$FirstFrameThisfileInfo = $info;
|
1408 |
+
$FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
|
1409 |
+
if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $FirstFrameThisfileInfo, false)) {
|
1410 |
+
// if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
|
1411 |
+
// garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
|
1412 |
+
unset($FirstFrameThisfileInfo);
|
1413 |
+
}
|
1414 |
+
}
|
1415 |
+
|
1416 |
+
$dummy = $info; // only overwrite real data if valid header found
|
1417 |
+
if ($this->decodeMPEGaudioHeader($avdataoffset + $SynchSeekOffset, $dummy, true)) {
|
1418 |
+
$info = $dummy;
|
1419 |
+
$info['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
|
1420 |
+
switch (isset($info['fileformat']) ? $info['fileformat'] : '') {
|
1421 |
+
case '':
|
1422 |
+
case 'id3':
|
1423 |
+
case 'ape':
|
1424 |
+
case 'mp3':
|
1425 |
+
$info['fileformat'] = 'mp3';
|
1426 |
+
$info['audio']['dataformat'] = 'mp3';
|
1427 |
+
break;
|
1428 |
+
}
|
1429 |
+
if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
|
1430 |
+
if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
|
1431 |
+
// If there is garbage data between a valid VBR header frame and a sequence
|
1432 |
+
// of valid MPEG-audio frames the VBR data is no longer discarded.
|
1433 |
+
$info = $FirstFrameThisfileInfo;
|
1434 |
+
$info['avdataoffset'] = $FirstFrameAVDataOffset;
|
1435 |
+
$info['fileformat'] = 'mp3';
|
1436 |
+
$info['audio']['dataformat'] = 'mp3';
|
1437 |
+
$dummy = $info;
|
1438 |
+
unset($dummy['mpeg']['audio']);
|
1439 |
+
$GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
|
1440 |
+
$GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset;
|
1441 |
+
if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) {
|
1442 |
+
$info = $dummy;
|
1443 |
+
$info['avdataoffset'] = $GarbageOffsetEnd;
|
1444 |
+
$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;
|
1445 |
+
} else {
|
1446 |
+
$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.')';
|
1447 |
+
}
|
1448 |
+
}
|
1449 |
+
}
|
1450 |
+
if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) {
|
1451 |
+
// VBR file with no VBR header
|
1452 |
+
$BitrateHistogram = true;
|
1453 |
+
}
|
1454 |
+
|
1455 |
+
if ($BitrateHistogram) {
|
1456 |
+
|
1457 |
+
$info['mpeg']['audio']['stereo_distribution'] = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0);
|
1458 |
+
$info['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0);
|
1459 |
+
|
1460 |
+
if ($info['mpeg']['audio']['version'] == '1') {
|
1461 |
+
if ($info['mpeg']['audio']['layer'] == 3) {
|
1462 |
+
$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);
|
1463 |
+
} elseif ($info['mpeg']['audio']['layer'] == 2) {
|
1464 |
+
$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);
|
1465 |
+
} elseif ($info['mpeg']['audio']['layer'] == 1) {
|
1466 |
+
$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);
|
1467 |
+
}
|
1468 |
+
} elseif ($info['mpeg']['audio']['layer'] == 1) {
|
1469 |
+
$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);
|
1470 |
+
} else {
|
1471 |
+
$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);
|
1472 |
+
}
|
1473 |
+
|
1474 |
+
$dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
|
1475 |
+
$synchstartoffset = $info['avdataoffset'];
|
1476 |
+
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
1477 |
+
|
1478 |
+
// you can play with these numbers:
|
1479 |
+
$max_frames_scan = 50000;
|
1480 |
+
$max_scan_segments = 10;
|
1481 |
+
|
1482 |
+
// don't play with these numbers:
|
1483 |
+
$FastMode = false;
|
1484 |
+
$SynchErrorsFound = 0;
|
1485 |
+
$frames_scanned = 0;
|
1486 |
+
$this_scan_segment = 0;
|
1487 |
+
$frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments);
|
1488 |
+
$pct_data_scanned = 0;
|
1489 |
+
for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) {
|
1490 |
+
$frames_scanned_this_segment = 0;
|
1491 |
+
if (ftell($this->getid3->fp) >= $info['avdataend']) {
|
1492 |
+
break;
|
1493 |
+
}
|
1494 |
+
$scan_start_offset[$current_segment] = max(ftell($this->getid3->fp), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
|
1495 |
+
if ($current_segment > 0) {
|
1496 |
+
fseek($this->getid3->fp, $scan_start_offset[$current_segment], SEEK_SET);
|
1497 |
+
$buffer_4k = fread($this->getid3->fp, 4096);
|
1498 |
+
for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
|
1499 |
+
if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
|
1500 |
+
if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
|
1501 |
+
$calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
|
1502 |
+
if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
|
1503 |
+
$scan_start_offset[$current_segment] += $j;
|
1504 |
+
break;
|
1505 |
+
}
|
1506 |
+
}
|
1507 |
+
}
|
1508 |
+
}
|
1509 |
+
}
|
1510 |
+
$synchstartoffset = $scan_start_offset[$current_segment];
|
1511 |
+
while ($this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
|
1512 |
+
$FastMode = true;
|
1513 |
+
$thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
|
1514 |
+
|
1515 |
+
if (empty($dummy['mpeg']['audio']['framelength'])) {
|
1516 |
+
$SynchErrorsFound++;
|
1517 |
+
$synchstartoffset++;
|
1518 |
+
} else {
|
1519 |
+
getid3_lib::safe_inc($info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]);
|
1520 |
+
getid3_lib::safe_inc($info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]);
|
1521 |
+
getid3_lib::safe_inc($info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]);
|
1522 |
+
$synchstartoffset += $dummy['mpeg']['audio']['framelength'];
|
1523 |
+
}
|
1524 |
+
$frames_scanned++;
|
1525 |
+
if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) {
|
1526 |
+
$this_pct_scanned = (ftell($this->getid3->fp) - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']);
|
1527 |
+
if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) {
|
1528 |
+
// file likely contains < $max_frames_scan, just scan as one segment
|
1529 |
+
$max_scan_segments = 1;
|
1530 |
+
$frames_scan_per_segment = $max_frames_scan;
|
1531 |
+
} else {
|
1532 |
+
$pct_data_scanned += $this_pct_scanned;
|
1533 |
+
break;
|
1534 |
+
}
|
1535 |
+
}
|
1536 |
+
}
|
1537 |
+
}
|
1538 |
+
if ($pct_data_scanned > 0) {
|
1539 |
+
$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.';
|
1540 |
+
foreach ($info['mpeg']['audio'] as $key1 => $value1) {
|
1541 |
+
if (!preg_match('#_distribution$#i', $key1)) {
|
1542 |
+
continue;
|
1543 |
+
}
|
1544 |
+
foreach ($value1 as $key2 => $value2) {
|
1545 |
+
$info['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned);
|
1546 |
+
}
|
1547 |
+
}
|
1548 |
+
}
|
1549 |
+
|
1550 |
+
if ($SynchErrorsFound > 0) {
|
1551 |
+
$info['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis';
|
1552 |
+
//return false;
|
1553 |
+
}
|
1554 |
+
|
1555 |
+
$bittotal = 0;
|
1556 |
+
$framecounter = 0;
|
1557 |
+
foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) {
|
1558 |
+
$framecounter += $bitratecount;
|
1559 |
+
if ($bitratevalue != 'free') {
|
1560 |
+
$bittotal += ($bitratevalue * $bitratecount);
|
1561 |
+
}
|
1562 |
+
}
|
1563 |
+
if ($framecounter == 0) {
|
1564 |
+
$info['error'][] = 'Corrupt MP3 file: framecounter == zero';
|
1565 |
+
return false;
|
1566 |
+
}
|
1567 |
+
$info['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter);
|
1568 |
+
$info['mpeg']['audio']['bitrate'] = ($bittotal / $framecounter);
|
1569 |
+
|
1570 |
+
$info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
|
1571 |
+
|
1572 |
+
|
1573 |
+
// Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
|
1574 |
+
$distinct_bitrates = 0;
|
1575 |
+
foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) {
|
1576 |
+
if ($bitrate_count > 0) {
|
1577 |
+
$distinct_bitrates++;
|
1578 |
+
}
|
1579 |
+
}
|
1580 |
+
if ($distinct_bitrates > 1) {
|
1581 |
+
$info['mpeg']['audio']['bitrate_mode'] = 'vbr';
|
1582 |
+
} else {
|
1583 |
+
$info['mpeg']['audio']['bitrate_mode'] = 'cbr';
|
1584 |
+
}
|
1585 |
+
$info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
|
1586 |
+
|
1587 |
+
}
|
1588 |
+
|
1589 |
+
break; // exit while()
|
1590 |
+
}
|
1591 |
+
}
|
1592 |
+
|
1593 |
+
$SynchSeekOffset++;
|
1594 |
+
if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) {
|
1595 |
+
// end of file/data
|
1596 |
+
|
1597 |
+
if (empty($info['mpeg']['audio'])) {
|
1598 |
+
|
1599 |
+
$info['error'][] = 'could not find valid MPEG synch before end of file';
|
1600 |
+
if (isset($info['audio']['bitrate'])) {
|
1601 |
+
unset($info['audio']['bitrate']);
|
1602 |
+
}
|
1603 |
+
if (isset($info['mpeg']['audio'])) {
|
1604 |
+
unset($info['mpeg']['audio']);
|
1605 |
+
}
|
1606 |
+
if (isset($info['mpeg']) && (!is_array($info['mpeg']) || empty($info['mpeg']))) {
|
1607 |
+
unset($info['mpeg']);
|
1608 |
+
}
|
1609 |
+
return false;
|
1610 |
+
|
1611 |
+
}
|
1612 |
+
break;
|
1613 |
+
}
|
1614 |
+
|
1615 |
+
}
|
1616 |
+
$info['audio']['channels'] = $info['mpeg']['audio']['channels'];
|
1617 |
+
$info['audio']['channelmode'] = $info['mpeg']['audio']['channelmode'];
|
1618 |
+
$info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
|
1619 |
+
return true;
|
1620 |
+
}
|
1621 |
+
|
1622 |
+
|
1623 |
+
static function MPEGaudioVersionArray() {
|
1624 |
+
static $MPEGaudioVersion = array('2.5', false, '2', '1');
|
1625 |
+
return $MPEGaudioVersion;
|
1626 |
+
}
|
1627 |
+
|
1628 |
+
static function MPEGaudioLayerArray() {
|
1629 |
+
static $MPEGaudioLayer = array(false, 3, 2, 1);
|
1630 |
+
return $MPEGaudioLayer;
|
1631 |
+
}
|
1632 |
+
|
1633 |
+
static function MPEGaudioBitrateArray() {
|
1634 |
+
static $MPEGaudioBitrate;
|
1635 |
+
if (empty($MPEGaudioBitrate)) {
|
1636 |
+
$MPEGaudioBitrate = array (
|
1637 |
+
'1' => array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000),
|
1638 |
+
2 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000),
|
1639 |
+
3 => array('free', 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000)
|
1640 |
+
),
|
1641 |
+
|
1642 |
+
'2' => array (1 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000),
|
1643 |
+
2 => array('free', 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000),
|
1644 |
+
)
|
1645 |
+
);
|
1646 |
+
$MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2];
|
1647 |
+
$MPEGaudioBitrate['2.5'] = $MPEGaudioBitrate['2'];
|
1648 |
+
}
|
1649 |
+
return $MPEGaudioBitrate;
|
1650 |
+
}
|
1651 |
+
|
1652 |
+
static function MPEGaudioFrequencyArray() {
|
1653 |
+
static $MPEGaudioFrequency;
|
1654 |
+
if (empty($MPEGaudioFrequency)) {
|
1655 |
+
$MPEGaudioFrequency = array (
|
1656 |
+
'1' => array(44100, 48000, 32000),
|
1657 |
+
'2' => array(22050, 24000, 16000),
|
1658 |
+
'2.5' => array(11025, 12000, 8000)
|
1659 |
+
);
|
1660 |
+
}
|
1661 |
+
return $MPEGaudioFrequency;
|
1662 |
+
}
|
1663 |
+
|
1664 |
+
static function MPEGaudioChannelModeArray() {
|
1665 |
+
static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
|
1666 |
+
return $MPEGaudioChannelMode;
|
1667 |
+
}
|
1668 |
+
|
1669 |
+
static function MPEGaudioModeExtensionArray() {
|
1670 |
+
static $MPEGaudioModeExtension;
|
1671 |
+
if (empty($MPEGaudioModeExtension)) {
|
1672 |
+
$MPEGaudioModeExtension = array (
|
1673 |
+
1 => array('4-31', '8-31', '12-31', '16-31'),
|
1674 |
+
2 => array('4-31', '8-31', '12-31', '16-31'),
|
1675 |
+
3 => array('', 'IS', 'MS', 'IS+MS')
|
1676 |
+
);
|
1677 |
+
}
|
1678 |
+
return $MPEGaudioModeExtension;
|
1679 |
+
}
|
1680 |
+
|
1681 |
+
static function MPEGaudioEmphasisArray() {
|
1682 |
+
static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
|
1683 |
+
return $MPEGaudioEmphasis;
|
1684 |
+
}
|
1685 |
+
|
1686 |
+
static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
|
1687 |
+
return getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
|
1688 |
+
}
|
1689 |
+
|
1690 |
+
static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
|
1691 |
+
if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
|
1692 |
+
return false;
|
1693 |
+
}
|
1694 |
+
|
1695 |
+
static $MPEGaudioVersionLookup;
|
1696 |
+
static $MPEGaudioLayerLookup;
|
1697 |
+
static $MPEGaudioBitrateLookup;
|
1698 |
+
static $MPEGaudioFrequencyLookup;
|
1699 |
+
static $MPEGaudioChannelModeLookup;
|
1700 |
+
static $MPEGaudioModeExtensionLookup;
|
1701 |
+
static $MPEGaudioEmphasisLookup;
|
1702 |
+
if (empty($MPEGaudioVersionLookup)) {
|
1703 |
+
$MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray();
|
1704 |
+
$MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray();
|
1705 |
+
$MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray();
|
1706 |
+
$MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray();
|
1707 |
+
$MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray();
|
1708 |
+
$MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray();
|
1709 |
+
$MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray();
|
1710 |
+
}
|
1711 |
+
|
1712 |
+
if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
|
1713 |
+
$decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
|
1714 |
+
} else {
|
1715 |
+
echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : '');
|
1716 |
+
return false;
|
1717 |
+
}
|
1718 |
+
if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
|
1719 |
+
$decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
|
1720 |
+
} else {
|
1721 |
+
echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : '');
|
1722 |
+
return false;
|
1723 |
+
}
|
1724 |
+
if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
|
1725 |
+
echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : '');
|
1726 |
+
if ($rawarray['bitrate'] == 15) {
|
1727 |
+
// known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
|
1728 |
+
// let it go through here otherwise file will not be identified
|
1729 |
+
if (!$allowBitrate15) {
|
1730 |
+
return false;
|
1731 |
+
}
|
1732 |
+
} else {
|
1733 |
+
return false;
|
1734 |
+
}
|
1735 |
+
}
|
1736 |
+
if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
|
1737 |
+
echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : '');
|
1738 |
+
return false;
|
1739 |
+
}
|
1740 |
+
if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
|
1741 |
+
echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : '');
|
1742 |
+
return false;
|
1743 |
+
}
|
1744 |
+
if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
|
1745 |
+
echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : '');
|
1746 |
+
return false;
|
1747 |
+
}
|
1748 |
+
if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
|
1749 |
+
echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : '');
|
1750 |
+
return false;
|
1751 |
+
}
|
1752 |
+
// These are just either set or not set, you can't mess that up :)
|
1753 |
+
// $rawarray['protection'];
|
1754 |
+
// $rawarray['padding'];
|
1755 |
+
// $rawarray['private'];
|
1756 |
+
// $rawarray['copyright'];
|
1757 |
+
// $rawarray['original'];
|
1758 |
+
|
1759 |
+
return true;
|
1760 |
+
}
|
1761 |
+
|
1762 |
+
static function MPEGaudioHeaderDecode($Header4Bytes) {
|
1763 |
+
// AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM
|
1764 |
+
// A - Frame sync (all bits set)
|
1765 |
+
// B - MPEG Audio version ID
|
1766 |
+
// C - Layer description
|
1767 |
+
// D - Protection bit
|
1768 |
+
// E - Bitrate index
|
1769 |
+
// F - Sampling rate frequency index
|
1770 |
+
// G - Padding bit
|
1771 |
+
// H - Private bit
|
1772 |
+
// I - Channel Mode
|
1773 |
+
// J - Mode extension (Only if Joint stereo)
|
1774 |
+
// K - Copyright
|
1775 |
+
// L - Original
|
1776 |
+
// M - Emphasis
|
1777 |
+
|
1778 |
+
if (strlen($Header4Bytes) != 4) {
|
1779 |
+
return false;
|
1780 |
+
}
|
1781 |
+
|
1782 |
+
$MPEGrawHeader['synch'] = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
|
1783 |
+
$MPEGrawHeader['version'] = (ord($Header4Bytes{1}) & 0x18) >> 3; // BB
|
1784 |
+
$MPEGrawHeader['layer'] = (ord($Header4Bytes{1}) & 0x06) >> 1; // CC
|
1785 |
+
$MPEGrawHeader['protection'] = (ord($Header4Bytes{1}) & 0x01); // D
|
1786 |
+
$MPEGrawHeader['bitrate'] = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
|
1787 |
+
$MPEGrawHeader['sample_rate'] = (ord($Header4Bytes{2}) & 0x0C) >> 2; // FF
|
1788 |
+
$MPEGrawHeader['padding'] = (ord($Header4Bytes{2}) & 0x02) >> 1; // G
|
1789 |
+
$MPEGrawHeader['private'] = (ord($Header4Bytes{2}) & 0x01); // H
|
1790 |
+
$MPEGrawHeader['channelmode'] = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
|
1791 |
+
$MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; // JJ
|
1792 |
+
$MPEGrawHeader['copyright'] = (ord($Header4Bytes{3}) & 0x08) >> 3; // K
|
1793 |
+
$MPEGrawHeader['original'] = (ord($Header4Bytes{3}) & 0x04) >> 2; // L
|
1794 |
+
$MPEGrawHeader['emphasis'] = (ord($Header4Bytes{3}) & 0x03); // MM
|
1795 |
+
|
1796 |
+
return $MPEGrawHeader;
|
1797 |
+
}
|
1798 |
+
|
1799 |
+
static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
|
1800 |
+
static $AudioFrameLengthCache = array();
|
1801 |
+
|
1802 |
+
if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
|
1803 |
+
$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
|
1804 |
+
if ($bitrate != 'free') {
|
1805 |
+
|
1806 |
+
if ($version == '1') {
|
1807 |
+
|
1808 |
+
if ($layer == '1') {
|
1809 |
+
|
1810 |
+
// For Layer I slot is 32 bits long
|
1811 |
+
$FrameLengthCoefficient = 48;
|
1812 |
+
$SlotLength = 4;
|
1813 |
+
|
1814 |
+
} else { // Layer 2 / 3
|
1815 |
+
|
1816 |
+
// for Layer 2 and Layer 3 slot is 8 bits long.
|
1817 |
+
$FrameLengthCoefficient = 144;
|
1818 |
+
$SlotLength = 1;
|
1819 |
+
|
1820 |
+
}
|
1821 |
+
|
1822 |
+
} else { // MPEG-2 / MPEG-2.5
|
1823 |
+
|
1824 |
+
if ($layer == '1') {
|
1825 |
+
|
1826 |
+
// For Layer I slot is 32 bits long
|
1827 |
+
$FrameLengthCoefficient = 24;
|
1828 |
+
$SlotLength = 4;
|
1829 |
+
|
1830 |
+
} elseif ($layer == '2') {
|
1831 |
+
|
1832 |
+
// for Layer 2 and Layer 3 slot is 8 bits long.
|
1833 |
+
$FrameLengthCoefficient = 144;
|
1834 |
+
$SlotLength = 1;
|
1835 |
+
|
1836 |
+
} else { // layer 3
|
1837 |
+
|
1838 |
+
// for Layer 2 and Layer 3 slot is 8 bits long.
|
1839 |
+
$FrameLengthCoefficient = 72;
|
1840 |
+
$SlotLength = 1;
|
1841 |
+
|
1842 |
+
}
|
1843 |
+
|
1844 |
+
}
|
1845 |
+
|
1846 |
+
// FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
|
1847 |
+
if ($samplerate > 0) {
|
1848 |
+
$NewFramelength = ($FrameLengthCoefficient * $bitrate) / $samplerate;
|
1849 |
+
$NewFramelength = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I)
|
1850 |
+
if ($padding) {
|
1851 |
+
$NewFramelength += $SlotLength;
|
1852 |
+
}
|
1853 |
+
$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength;
|
1854 |
+
}
|
1855 |
+
}
|
1856 |
+
}
|
1857 |
+
return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
|
1858 |
+
}
|
1859 |
+
|
1860 |
+
static function ClosestStandardMP3Bitrate($bit_rate) {
|
1861 |
+
static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
|
1862 |
+
static $bit_rate_table = array (0=>'-');
|
1863 |
+
$round_bit_rate = intval(round($bit_rate, -3));
|
1864 |
+
if (!isset($bit_rate_table[$round_bit_rate])) {
|
1865 |
+
if ($round_bit_rate > max($standard_bit_rates)) {
|
1866 |
+
$bit_rate_table[$round_bit_rate] = round($bit_rate, 2 - strlen($bit_rate));
|
1867 |
+
} else {
|
1868 |
+
$bit_rate_table[$round_bit_rate] = max($standard_bit_rates);
|
1869 |
+
foreach ($standard_bit_rates as $standard_bit_rate) {
|
1870 |
+
if ($round_bit_rate >= $standard_bit_rate + (($bit_rate_table[$round_bit_rate] - $standard_bit_rate) / 2)) {
|
1871 |
+
break;
|
1872 |
+
}
|
1873 |
+
$bit_rate_table[$round_bit_rate] = $standard_bit_rate;
|
1874 |
+
}
|
1875 |
+
}
|
1876 |
+
}
|
1877 |
+
return $bit_rate_table[$round_bit_rate];
|
1878 |
+
}
|
1879 |
+
|
1880 |
+
static function XingVBRidOffset($version, $channelmode) {
|
1881 |
+
static $XingVBRidOffsetCache = array();
|
1882 |
+
if (empty($XingVBRidOffset)) {
|
1883 |
+
$XingVBRidOffset = array (
|
1884 |
+
'1' => array ('mono' => 0x15, // 4 + 17 = 21
|
1885 |
+
'stereo' => 0x24, // 4 + 32 = 36
|
1886 |
+
'joint stereo' => 0x24,
|
1887 |
+
'dual channel' => 0x24
|
1888 |
+
),
|
1889 |
+
|
1890 |
+
'2' => array ('mono' => 0x0D, // 4 + 9 = 13
|
1891 |
+
'stereo' => 0x15, // 4 + 17 = 21
|
1892 |
+
'joint stereo' => 0x15,
|
1893 |
+
'dual channel' => 0x15
|
1894 |
+
),
|
1895 |
+
|
1896 |
+
'2.5' => array ('mono' => 0x15,
|
1897 |
+
'stereo' => 0x15,
|
1898 |
+
'joint stereo' => 0x15,
|
1899 |
+
'dual channel' => 0x15
|
1900 |
+
)
|
1901 |
+
);
|
1902 |
+
}
|
1903 |
+
return $XingVBRidOffset[$version][$channelmode];
|
1904 |
+
}
|
1905 |
+
|
1906 |
+
static function LAMEvbrMethodLookup($VBRmethodID) {
|
1907 |
+
static $LAMEvbrMethodLookup = array(
|
1908 |
+
0x00 => 'unknown',
|
1909 |
+
0x01 => 'cbr',
|
1910 |
+
0x02 => 'abr',
|
1911 |
+
0x03 => 'vbr-old / vbr-rh',
|
1912 |
+
0x04 => 'vbr-new / vbr-mtrh',
|
1913 |
+
0x05 => 'vbr-mt',
|
1914 |
+
0x06 => 'vbr (full vbr method 4)',
|
1915 |
+
0x08 => 'cbr (constant bitrate 2 pass)',
|
1916 |
+
0x09 => 'abr (2 pass)',
|
1917 |
+
0x0F => 'reserved'
|
1918 |
+
);
|
1919 |
+
return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
|
1920 |
+
}
|
1921 |
+
|
1922 |
+
static function LAMEmiscStereoModeLookup($StereoModeID) {
|
1923 |
+
static $LAMEmiscStereoModeLookup = array(
|
1924 |
+
0 => 'mono',
|
1925 |
+
1 => 'stereo',
|
1926 |
+
2 => 'dual mono',
|
1927 |
+
3 => 'joint stereo',
|
1928 |
+
4 => 'forced stereo',
|
1929 |
+
5 => 'auto',
|
1930 |
+
6 => 'intensity stereo',
|
1931 |
+
7 => 'other'
|
1932 |
+
);
|
1933 |
+
return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
|
1934 |
+
}
|
1935 |
+
|
1936 |
+
static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
|
1937 |
+
static $LAMEmiscSourceSampleFrequencyLookup = array(
|
1938 |
+
0 => '<= 32 kHz',
|
1939 |
+
1 => '44.1 kHz',
|
1940 |
+
2 => '48 kHz',
|
1941 |
+
3 => '> 48kHz'
|
1942 |
+
);
|
1943 |
+
return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
|
1944 |
+
}
|
1945 |
+
|
1946 |
+
static function LAMEsurroundInfoLookup($SurroundInfoID) {
|
1947 |
+
static $LAMEsurroundInfoLookup = array(
|
1948 |
+
0 => 'no surround info',
|
1949 |
+
1 => 'DPL encoding',
|
1950 |
+
2 => 'DPL2 encoding',
|
1951 |
+
3 => 'Ambisonic encoding'
|
1952 |
+
);
|
1953 |
+
return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
|
1954 |
+
}
|
1955 |
+
|
1956 |
+
static function LAMEpresetUsedLookup($LAMEtag) {
|
1957 |
+
|
1958 |
+
if ($LAMEtag['preset_used_id'] == 0) {
|
1959 |
+
// no preset used (LAME >=3.93)
|
1960 |
+
// no preset recorded (LAME <3.93)
|
1961 |
+
return '';
|
1962 |
+
}
|
1963 |
+
$LAMEpresetUsedLookup = array();
|
1964 |
+
|
1965 |
+
///// THIS PART CANNOT BE STATIC .
|
1966 |
+
for ($i = 8; $i <= 320; $i++) {
|
1967 |
+
switch ($LAMEtag['vbr_method']) {
|
1968 |
+
case 'cbr':
|
1969 |
+
$LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i;
|
1970 |
+
break;
|
1971 |
+
case 'abr':
|
1972 |
+
default: // other VBR modes shouldn't be here(?)
|
1973 |
+
$LAMEpresetUsedLookup[$i] = '--alt-preset '.$i;
|
1974 |
+
break;
|
1975 |
+
}
|
1976 |
+
}
|
1977 |
+
|
1978 |
+
// named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
|
1979 |
+
|
1980 |
+
// named alt-presets
|
1981 |
+
$LAMEpresetUsedLookup[1000] = '--r3mix';
|
1982 |
+
$LAMEpresetUsedLookup[1001] = '--alt-preset standard';
|
1983 |
+
$LAMEpresetUsedLookup[1002] = '--alt-preset extreme';
|
1984 |
+
$LAMEpresetUsedLookup[1003] = '--alt-preset insane';
|
1985 |
+
$LAMEpresetUsedLookup[1004] = '--alt-preset fast standard';
|
1986 |
+
$LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme';
|
1987 |
+
$LAMEpresetUsedLookup[1006] = '--alt-preset medium';
|
1988 |
+
$LAMEpresetUsedLookup[1007] = '--alt-preset fast medium';
|
1989 |
+
|
1990 |
+
// LAME 3.94 additions/changes
|
1991 |
+
$LAMEpresetUsedLookup[1010] = '--preset portable'; // 3.94a15 Oct 21 2003
|
1992 |
+
$LAMEpresetUsedLookup[1015] = '--preset radio'; // 3.94a15 Oct 21 2003
|
1993 |
+
|
1994 |
+
$LAMEpresetUsedLookup[320] = '--preset insane'; // 3.94a15 Nov 12 2003
|
1995 |
+
$LAMEpresetUsedLookup[410] = '-V9';
|
1996 |
+
$LAMEpresetUsedLookup[420] = '-V8';
|
1997 |
+
$LAMEpresetUsedLookup[440] = '-V6';
|
1998 |
+
$LAMEpresetUsedLookup[430] = '--preset radio'; // 3.94a15 Nov 12 2003
|
1999 |
+
$LAMEpresetUsedLookup[450] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable'; // 3.94a15 Nov 12 2003
|
2000 |
+
$LAMEpresetUsedLookup[460] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium'; // 3.94a15 Nov 12 2003
|
2001 |
+
$LAMEpresetUsedLookup[470] = '--r3mix'; // 3.94b1 Dec 18 2003
|
2002 |
+
$LAMEpresetUsedLookup[480] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard'; // 3.94a15 Nov 12 2003
|
2003 |
+
$LAMEpresetUsedLookup[490] = '-V1';
|
2004 |
+
$LAMEpresetUsedLookup[500] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme'; // 3.94a15 Nov 12 2003
|
2005 |
+
|
2006 |
+
return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org');
|
2007 |
+
}
|
2008 |
+
|
2009 |
+
}
|
2010 |
+
|
2011 |
+
?>
|
includes/lib/getid3/module.tag.apetag.php
ADDED
@@ -0,0 +1,372 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
/////////////////////////////////////////////////////////////////
|
7 |
+
// See readme.txt for more details //
|
8 |
+
/////////////////////////////////////////////////////////////////
|
9 |
+
// //
|
10 |
+
// module.tag.apetag.php //
|
11 |
+
// module for analyzing APE tags //
|
12 |
+
// dependencies: NONE //
|
13 |
+
// ///
|
14 |
+
/////////////////////////////////////////////////////////////////
|
15 |
+
|
16 |
+
class getid3_apetag extends getid3_handler
|
17 |
+
{
|
18 |
+
var $inline_attachments = true; // true: return full data for all attachments; false: return no data for all attachments; integer: return data for attachments <= than this; string: save as file to this directory
|
19 |
+
var $overrideendoffset = 0;
|
20 |
+
|
21 |
+
function Analyze() {
|
22 |
+
$info = &$this->getid3->info;
|
23 |
+
|
24 |
+
if (!getid3_lib::intValueSupported($info['filesize'])) {
|
25 |
+
$info['warning'][] = 'Unable to check for APEtags because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
|
26 |
+
return false;
|
27 |
+
}
|
28 |
+
|
29 |
+
$id3v1tagsize = 128;
|
30 |
+
$apetagheadersize = 32;
|
31 |
+
$lyrics3tagsize = 10;
|
32 |
+
|
33 |
+
if ($this->overrideendoffset == 0) {
|
34 |
+
|
35 |
+
fseek($this->getid3->fp, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
|
36 |
+
$APEfooterID3v1 = fread($this->getid3->fp, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
|
37 |
+
|
38 |
+
//if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
|
39 |
+
if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
|
40 |
+
|
41 |
+
// APE tag found before ID3v1
|
42 |
+
$info['ape']['tag_offset_end'] = $info['filesize'] - $id3v1tagsize;
|
43 |
+
|
44 |
+
//} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
|
45 |
+
} elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') {
|
46 |
+
|
47 |
+
// APE tag found, no ID3v1
|
48 |
+
$info['ape']['tag_offset_end'] = $info['filesize'];
|
49 |
+
|
50 |
+
}
|
51 |
+
|
52 |
+
} else {
|
53 |
+
|
54 |
+
fseek($this->getid3->fp, $this->overrideendoffset - $apetagheadersize, SEEK_SET);
|
55 |
+
if (fread($this->getid3->fp, 8) == 'APETAGEX') {
|
56 |
+
$info['ape']['tag_offset_end'] = $this->overrideendoffset;
|
57 |
+
}
|
58 |
+
|
59 |
+
}
|
60 |
+
if (!isset($info['ape']['tag_offset_end'])) {
|
61 |
+
|
62 |
+
// APE tag not found
|
63 |
+
unset($info['ape']);
|
64 |
+
return false;
|
65 |
+
|
66 |
+
}
|
67 |
+
|
68 |
+
// shortcut
|
69 |
+
$thisfile_ape = &$info['ape'];
|
70 |
+
|
71 |
+
fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET);
|
72 |
+
$APEfooterData = fread($this->getid3->fp, 32);
|
73 |
+
if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
|
74 |
+
$info['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end'];
|
75 |
+
return false;
|
76 |
+
}
|
77 |
+
|
78 |
+
if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
|
79 |
+
fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET);
|
80 |
+
$thisfile_ape['tag_offset_start'] = ftell($this->getid3->fp);
|
81 |
+
$APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
|
82 |
+
} else {
|
83 |
+
$thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
|
84 |
+
fseek($this->getid3->fp, $thisfile_ape['tag_offset_start'], SEEK_SET);
|
85 |
+
$APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize']);
|
86 |
+
}
|
87 |
+
$info['avdataend'] = $thisfile_ape['tag_offset_start'];
|
88 |
+
|
89 |
+
if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) {
|
90 |
+
$info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data';
|
91 |
+
unset($info['id3v1']);
|
92 |
+
foreach ($info['warning'] as $key => $value) {
|
93 |
+
if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
|
94 |
+
unset($info['warning'][$key]);
|
95 |
+
sort($info['warning']);
|
96 |
+
break;
|
97 |
+
}
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
$offset = 0;
|
102 |
+
if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
|
103 |
+
if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) {
|
104 |
+
$offset += $apetagheadersize;
|
105 |
+
} else {
|
106 |
+
$info['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start'];
|
107 |
+
return false;
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
// shortcut
|
112 |
+
$info['replay_gain'] = array();
|
113 |
+
$thisfile_replaygain = &$info['replay_gain'];
|
114 |
+
|
115 |
+
for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) {
|
116 |
+
$value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
|
117 |
+
$offset += 4;
|
118 |
+
$item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
|
119 |
+
$offset += 4;
|
120 |
+
if (strstr(substr($APEtagData, $offset), "\x00") === false) {
|
121 |
+
$info['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset);
|
122 |
+
return false;
|
123 |
+
}
|
124 |
+
$ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset;
|
125 |
+
$item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength));
|
126 |
+
|
127 |
+
// shortcut
|
128 |
+
$thisfile_ape['items'][$item_key] = array();
|
129 |
+
$thisfile_ape_items_current = &$thisfile_ape['items'][$item_key];
|
130 |
+
|
131 |
+
$thisfile_ape_items_current['offset'] = $thisfile_ape['tag_offset_start'] + $offset;
|
132 |
+
|
133 |
+
$offset += ($ItemKeyLength + 1); // skip 0x00 terminator
|
134 |
+
$thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
|
135 |
+
$offset += $value_size;
|
136 |
+
|
137 |
+
$thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
|
138 |
+
switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
|
139 |
+
case 0: // UTF-8
|
140 |
+
case 3: // Locator (URL, filename, etc), UTF-8 encoded
|
141 |
+
$thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data']));
|
142 |
+
break;
|
143 |
+
|
144 |
+
default: // binary data
|
145 |
+
break;
|
146 |
+
}
|
147 |
+
|
148 |
+
switch (strtolower($item_key)) {
|
149 |
+
case 'replaygain_track_gain':
|
150 |
+
$thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
151 |
+
$thisfile_replaygain['track']['originator'] = 'unspecified';
|
152 |
+
break;
|
153 |
+
|
154 |
+
case 'replaygain_track_peak':
|
155 |
+
$thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
156 |
+
$thisfile_replaygain['track']['originator'] = 'unspecified';
|
157 |
+
if ($thisfile_replaygain['track']['peak'] <= 0) {
|
158 |
+
$info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
|
159 |
+
}
|
160 |
+
break;
|
161 |
+
|
162 |
+
case 'replaygain_album_gain':
|
163 |
+
$thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
164 |
+
$thisfile_replaygain['album']['originator'] = 'unspecified';
|
165 |
+
break;
|
166 |
+
|
167 |
+
case 'replaygain_album_peak':
|
168 |
+
$thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
169 |
+
$thisfile_replaygain['album']['originator'] = 'unspecified';
|
170 |
+
if ($thisfile_replaygain['album']['peak'] <= 0) {
|
171 |
+
$info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
|
172 |
+
}
|
173 |
+
break;
|
174 |
+
|
175 |
+
case 'mp3gain_undo':
|
176 |
+
list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
|
177 |
+
$thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
|
178 |
+
$thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
|
179 |
+
$thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false);
|
180 |
+
break;
|
181 |
+
|
182 |
+
case 'mp3gain_minmax':
|
183 |
+
list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
|
184 |
+
$thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
|
185 |
+
$thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
|
186 |
+
break;
|
187 |
+
|
188 |
+
case 'mp3gain_album_minmax':
|
189 |
+
list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
|
190 |
+
$thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
|
191 |
+
$thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
|
192 |
+
break;
|
193 |
+
|
194 |
+
case 'tracknumber':
|
195 |
+
if (is_array($thisfile_ape_items_current['data'])) {
|
196 |
+
foreach ($thisfile_ape_items_current['data'] as $comment) {
|
197 |
+
$thisfile_ape['comments']['track'][] = $comment;
|
198 |
+
}
|
199 |
+
}
|
200 |
+
break;
|
201 |
+
|
202 |
+
case 'cover art (artist)':
|
203 |
+
case 'cover art (back)':
|
204 |
+
case 'cover art (band logo)':
|
205 |
+
case 'cover art (band)':
|
206 |
+
case 'cover art (colored fish)':
|
207 |
+
case 'cover art (composer)':
|
208 |
+
case 'cover art (conductor)':
|
209 |
+
case 'cover art (front)':
|
210 |
+
case 'cover art (icon)':
|
211 |
+
case 'cover art (illustration)':
|
212 |
+
case 'cover art (lead)':
|
213 |
+
case 'cover art (leaflet)':
|
214 |
+
case 'cover art (lyricist)':
|
215 |
+
case 'cover art (media)':
|
216 |
+
case 'cover art (movie scene)':
|
217 |
+
case 'cover art (other icon)':
|
218 |
+
case 'cover art (other)':
|
219 |
+
case 'cover art (performance)':
|
220 |
+
case 'cover art (publisher logo)':
|
221 |
+
case 'cover art (recording)':
|
222 |
+
case 'cover art (studio)':
|
223 |
+
// list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html
|
224 |
+
list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("\x00", $thisfile_ape_items_current['data'], 2);
|
225 |
+
$thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00");
|
226 |
+
$thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']);
|
227 |
+
|
228 |
+
$thisfile_ape_items_current['image_mime'] = '';
|
229 |
+
$imageinfo = array();
|
230 |
+
$imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo);
|
231 |
+
$thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
|
232 |
+
|
233 |
+
do {
|
234 |
+
if ($this->inline_attachments === false) {
|
235 |
+
// skip entirely
|
236 |
+
unset($thisfile_ape_items_current['data']);
|
237 |
+
break;
|
238 |
+
}
|
239 |
+
if ($this->inline_attachments === true) {
|
240 |
+
// great
|
241 |
+
} elseif (is_int($this->inline_attachments)) {
|
242 |
+
if ($this->inline_attachments < $thisfile_ape_items_current['data_length']) {
|
243 |
+
// too big, skip
|
244 |
+
$info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' is too large to process inline ('.number_format($thisfile_ape_items_current['data_length']).' bytes)';
|
245 |
+
unset($thisfile_ape_items_current['data']);
|
246 |
+
break;
|
247 |
+
}
|
248 |
+
} elseif (is_string($this->inline_attachments)) {
|
249 |
+
$this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR);
|
250 |
+
if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) {
|
251 |
+
// cannot write, skip
|
252 |
+
$info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$this->inline_attachments.'" (not writable)';
|
253 |
+
unset($thisfile_ape_items_current['data']);
|
254 |
+
break;
|
255 |
+
}
|
256 |
+
}
|
257 |
+
// if we get this far, must be OK
|
258 |
+
if (is_string($this->inline_attachments)) {
|
259 |
+
$destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$thisfile_ape_items_current['data_offset'];
|
260 |
+
if (!file_exists($destination_filename) || is_writable($destination_filename)) {
|
261 |
+
file_put_contents($destination_filename, $thisfile_ape_items_current['data']);
|
262 |
+
} else {
|
263 |
+
$info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$destination_filename.'" (not writable)';
|
264 |
+
}
|
265 |
+
$thisfile_ape_items_current['data_filename'] = $destination_filename;
|
266 |
+
unset($thisfile_ape_items_current['data']);
|
267 |
+
} else {
|
268 |
+
if (!isset($info['ape']['comments']['picture'])) {
|
269 |
+
$info['ape']['comments']['picture'] = array();
|
270 |
+
}
|
271 |
+
$info['ape']['comments']['picture'][] = array('data'=>$thisfile_ape_items_current['data'], 'image_mime'=>$thisfile_ape_items_current['image_mime']);
|
272 |
+
}
|
273 |
+
} while (false);
|
274 |
+
break;
|
275 |
+
|
276 |
+
default:
|
277 |
+
if (is_array($thisfile_ape_items_current['data'])) {
|
278 |
+
foreach ($thisfile_ape_items_current['data'] as $comment) {
|
279 |
+
$thisfile_ape['comments'][strtolower($item_key)][] = $comment;
|
280 |
+
}
|
281 |
+
}
|
282 |
+
break;
|
283 |
+
}
|
284 |
+
|
285 |
+
}
|
286 |
+
if (empty($thisfile_replaygain)) {
|
287 |
+
unset($info['replay_gain']);
|
288 |
+
}
|
289 |
+
return true;
|
290 |
+
}
|
291 |
+
|
292 |
+
function parseAPEheaderFooter($APEheaderFooterData) {
|
293 |
+
// http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
|
294 |
+
|
295 |
+
// shortcut
|
296 |
+
$headerfooterinfo['raw'] = array();
|
297 |
+
$headerfooterinfo_raw = &$headerfooterinfo['raw'];
|
298 |
+
|
299 |
+
$headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8);
|
300 |
+
if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') {
|
301 |
+
return false;
|
302 |
+
}
|
303 |
+
$headerfooterinfo_raw['version'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 8, 4));
|
304 |
+
$headerfooterinfo_raw['tagsize'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4));
|
305 |
+
$headerfooterinfo_raw['tag_items'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4));
|
306 |
+
$headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4));
|
307 |
+
$headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8);
|
308 |
+
|
309 |
+
$headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000;
|
310 |
+
if ($headerfooterinfo['tag_version'] >= 2) {
|
311 |
+
$headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']);
|
312 |
+
}
|
313 |
+
return $headerfooterinfo;
|
314 |
+
}
|
315 |
+
|
316 |
+
function parseAPEtagFlags($rawflagint) {
|
317 |
+
// "Note: APE Tags 1.0 do not use any of the APE Tag flags.
|
318 |
+
// All are set to zero on creation and ignored on reading."
|
319 |
+
// http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html
|
320 |
+
$flags['header'] = (bool) ($rawflagint & 0x80000000);
|
321 |
+
$flags['footer'] = (bool) ($rawflagint & 0x40000000);
|
322 |
+
$flags['this_is_header'] = (bool) ($rawflagint & 0x20000000);
|
323 |
+
$flags['item_contents_raw'] = ($rawflagint & 0x00000006) >> 1;
|
324 |
+
$flags['read_only'] = (bool) ($rawflagint & 0x00000001);
|
325 |
+
|
326 |
+
$flags['item_contents'] = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']);
|
327 |
+
|
328 |
+
return $flags;
|
329 |
+
}
|
330 |
+
|
331 |
+
function APEcontentTypeFlagLookup($contenttypeid) {
|
332 |
+
static $APEcontentTypeFlagLookup = array(
|
333 |
+
0 => 'utf-8',
|
334 |
+
1 => 'binary',
|
335 |
+
2 => 'external',
|
336 |
+
3 => 'reserved'
|
337 |
+
);
|
338 |
+
return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid');
|
339 |
+
}
|
340 |
+
|
341 |
+
function APEtagItemIsUTF8Lookup($itemkey) {
|
342 |
+
static $APEtagItemIsUTF8Lookup = array(
|
343 |
+
'title',
|
344 |
+
'subtitle',
|
345 |
+
'artist',
|
346 |
+
'album',
|
347 |
+
'debut album',
|
348 |
+
'publisher',
|
349 |
+
'conductor',
|
350 |
+
'track',
|
351 |
+
'composer',
|
352 |
+
'comment',
|
353 |
+
'copyright',
|
354 |
+
'publicationright',
|
355 |
+
'file',
|
356 |
+
'year',
|
357 |
+
'record date',
|
358 |
+
'record location',
|
359 |
+
'genre',
|
360 |
+
'media',
|
361 |
+
'related',
|
362 |
+
'isrc',
|
363 |
+
'abstract',
|
364 |
+
'language',
|
365 |
+
'bibliography'
|
366 |
+
);
|
367 |
+
return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup);
|
368 |
+
}
|
369 |
+
|
370 |
+
}
|
371 |
+
|
372 |
+
?>
|
includes/lib/getid3/module.tag.id3v1.php
ADDED
@@ -0,0 +1,362 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
/////////////////////////////////////////////////////////////////
|
7 |
+
// See readme.txt for more details //
|
8 |
+
/////////////////////////////////////////////////////////////////
|
9 |
+
// //
|
10 |
+
// module.tag.id3v1.php //
|
11 |
+
// module for analyzing ID3v1 tags //
|
12 |
+
// dependencies: NONE //
|
13 |
+
// ///
|
14 |
+
/////////////////////////////////////////////////////////////////
|
15 |
+
|
16 |
+
|
17 |
+
class getid3_id3v1 extends getid3_handler
|
18 |
+
{
|
19 |
+
|
20 |
+
function Analyze() {
|
21 |
+
$info = &$this->getid3->info;
|
22 |
+
|
23 |
+
if (!getid3_lib::intValueSupported($info['filesize'])) {
|
24 |
+
$info['warning'][] = 'Unable to check for ID3v1 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
|
25 |
+
return false;
|
26 |
+
}
|
27 |
+
|
28 |
+
fseek($this->getid3->fp, -256, SEEK_END);
|
29 |
+
$preid3v1 = fread($this->getid3->fp, 128);
|
30 |
+
$id3v1tag = fread($this->getid3->fp, 128);
|
31 |
+
|
32 |
+
if (substr($id3v1tag, 0, 3) == 'TAG') {
|
33 |
+
|
34 |
+
$info['avdataend'] = $info['filesize'] - 128;
|
35 |
+
|
36 |
+
$ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30));
|
37 |
+
$ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30));
|
38 |
+
$ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30));
|
39 |
+
$ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4));
|
40 |
+
$ParsedID3v1['comment'] = substr($id3v1tag, 97, 30); // can't remove nulls yet, track detection depends on them
|
41 |
+
$ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1));
|
42 |
+
|
43 |
+
// If second-last byte of comment field is null and last byte of comment field is non-null
|
44 |
+
// then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
|
45 |
+
if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) {
|
46 |
+
$ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1));
|
47 |
+
$ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
|
48 |
+
}
|
49 |
+
$ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
|
50 |
+
|
51 |
+
$ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']);
|
52 |
+
if (!empty($ParsedID3v1['genre'])) {
|
53 |
+
unset($ParsedID3v1['genreid']);
|
54 |
+
}
|
55 |
+
if (isset($ParsedID3v1['genre']) && (empty($ParsedID3v1['genre']) || ($ParsedID3v1['genre'] == 'Unknown'))) {
|
56 |
+
unset($ParsedID3v1['genre']);
|
57 |
+
}
|
58 |
+
|
59 |
+
foreach ($ParsedID3v1 as $key => $value) {
|
60 |
+
$ParsedID3v1['comments'][$key][0] = $value;
|
61 |
+
}
|
62 |
+
|
63 |
+
// ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
|
64 |
+
$GoodFormatID3v1tag = $this->GenerateID3v1Tag(
|
65 |
+
$ParsedID3v1['title'],
|
66 |
+
$ParsedID3v1['artist'],
|
67 |
+
$ParsedID3v1['album'],
|
68 |
+
$ParsedID3v1['year'],
|
69 |
+
(isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false),
|
70 |
+
$ParsedID3v1['comment'],
|
71 |
+
(!empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : ''));
|
72 |
+
$ParsedID3v1['padding_valid'] = true;
|
73 |
+
if ($id3v1tag !== $GoodFormatID3v1tag) {
|
74 |
+
$ParsedID3v1['padding_valid'] = false;
|
75 |
+
$info['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding';
|
76 |
+
}
|
77 |
+
|
78 |
+
$ParsedID3v1['tag_offset_end'] = $info['filesize'];
|
79 |
+
$ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128;
|
80 |
+
|
81 |
+
$info['id3v1'] = $ParsedID3v1;
|
82 |
+
}
|
83 |
+
|
84 |
+
if (substr($preid3v1, 0, 3) == 'TAG') {
|
85 |
+
// The way iTunes handles tags is, well, brain-damaged.
|
86 |
+
// It completely ignores v1 if ID3v2 is present.
|
87 |
+
// This goes as far as adding a new v1 tag *even if there already is one*
|
88 |
+
|
89 |
+
// A suspected double-ID3v1 tag has been detected, but it could be that
|
90 |
+
// the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag
|
91 |
+
if (substr($preid3v1, 96, 8) == 'APETAGEX') {
|
92 |
+
// an APE tag footer was found before the last ID3v1, assume false "TAG" synch
|
93 |
+
} elseif (substr($preid3v1, 119, 6) == 'LYRICS') {
|
94 |
+
// a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch
|
95 |
+
} else {
|
96 |
+
// APE and Lyrics3 footers not found - assume double ID3v1
|
97 |
+
$info['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes';
|
98 |
+
$info['avdataend'] -= 128;
|
99 |
+
}
|
100 |
+
}
|
101 |
+
|
102 |
+
return true;
|
103 |
+
}
|
104 |
+
|
105 |
+
static function cutfield($str) {
|
106 |
+
return trim(substr($str, 0, strcspn($str, "\x00")));
|
107 |
+
}
|
108 |
+
|
109 |
+
static function ArrayOfGenres($allowSCMPXextended=false) {
|
110 |
+
static $GenreLookup = array(
|
111 |
+
0 => 'Blues',
|
112 |
+
1 => 'Classic Rock',
|
113 |
+
2 => 'Country',
|
114 |
+
3 => 'Dance',
|
115 |
+
4 => 'Disco',
|
116 |
+
5 => 'Funk',
|
117 |
+
6 => 'Grunge',
|
118 |
+
7 => 'Hip-Hop',
|
119 |
+
8 => 'Jazz',
|
120 |
+
9 => 'Metal',
|
121 |
+
10 => 'New Age',
|
122 |
+
11 => 'Oldies',
|
123 |
+
12 => 'Other',
|
124 |
+
13 => 'Pop',
|
125 |
+
14 => 'R&B',
|
126 |
+
15 => 'Rap',
|
127 |
+
16 => 'Reggae',
|
128 |
+
17 => 'Rock',
|
129 |
+
18 => 'Techno',
|
130 |
+
19 => 'Industrial',
|
131 |
+
20 => 'Alternative',
|
132 |
+
21 => 'Ska',
|
133 |
+
22 => 'Death Metal',
|
134 |
+
23 => 'Pranks',
|
135 |
+
24 => 'Soundtrack',
|
136 |
+
25 => 'Euro-Techno',
|
137 |
+
26 => 'Ambient',
|
138 |
+
27 => 'Trip-Hop',
|
139 |
+
28 => 'Vocal',
|
140 |
+
29 => 'Jazz+Funk',
|
141 |
+
30 => 'Fusion',
|
142 |
+
31 => 'Trance',
|
143 |
+
32 => 'Classical',
|
144 |
+
33 => 'Instrumental',
|
145 |
+
34 => 'Acid',
|
146 |
+
35 => 'House',
|
147 |
+
36 => 'Game',
|
148 |
+
37 => 'Sound Clip',
|
149 |
+
38 => 'Gospel',
|
150 |
+
39 => 'Noise',
|
151 |
+
40 => 'Alt. Rock',
|
152 |
+
41 => 'Bass',
|
153 |
+
42 => 'Soul',
|
154 |
+
43 => 'Punk',
|
155 |
+
44 => 'Space',
|
156 |
+
45 => 'Meditative',
|
157 |
+
46 => 'Instrumental Pop',
|
158 |
+
47 => 'Instrumental Rock',
|
159 |
+
48 => 'Ethnic',
|
160 |
+
49 => 'Gothic',
|
161 |
+
50 => 'Darkwave',
|
162 |
+
51 => 'Techno-Industrial',
|
163 |
+
52 => 'Electronic',
|
164 |
+
53 => 'Pop-Folk',
|
165 |
+
54 => 'Eurodance',
|
166 |
+
55 => 'Dream',
|
167 |
+
56 => 'Southern Rock',
|
168 |
+
57 => 'Comedy',
|
169 |
+
58 => 'Cult',
|
170 |
+
59 => 'Gangsta Rap',
|
171 |
+
60 => 'Top 40',
|
172 |
+
61 => 'Christian Rap',
|
173 |
+
62 => 'Pop/Funk',
|
174 |
+
63 => 'Jungle',
|
175 |
+
64 => 'Native American',
|
176 |
+
65 => 'Cabaret',
|
177 |
+
66 => 'New Wave',
|
178 |
+
67 => 'Psychedelic',
|
179 |
+
68 => 'Rave',
|
180 |
+
69 => 'Showtunes',
|
181 |
+
70 => 'Trailer',
|
182 |
+
71 => 'Lo-Fi',
|
183 |
+
72 => 'Tribal',
|
184 |
+
73 => 'Acid Punk',
|
185 |
+
74 => 'Acid Jazz',
|
186 |
+
75 => 'Polka',
|
187 |
+
76 => 'Retro',
|
188 |
+
77 => 'Musical',
|
189 |
+
78 => 'Rock & Roll',
|
190 |
+
79 => 'Hard Rock',
|
191 |
+
80 => 'Folk',
|
192 |
+
81 => 'Folk/Rock',
|
193 |
+
82 => 'National Folk',
|
194 |
+
83 => 'Swing',
|
195 |
+
84 => 'Fast-Fusion',
|
196 |
+
85 => 'Bebob',
|
197 |
+
86 => 'Latin',
|
198 |
+
87 => 'Revival',
|
199 |
+
88 => 'Celtic',
|
200 |
+
89 => 'Bluegrass',
|
201 |
+
90 => 'Avantgarde',
|
202 |
+
91 => 'Gothic Rock',
|
203 |
+
92 => 'Progressive Rock',
|
204 |
+
93 => 'Psychedelic Rock',
|
205 |
+
94 => 'Symphonic Rock',
|
206 |
+
95 => 'Slow Rock',
|
207 |
+
96 => 'Big Band',
|
208 |
+
97 => 'Chorus',
|
209 |
+
98 => 'Easy Listening',
|
210 |
+
99 => 'Acoustic',
|
211 |
+
100 => 'Humour',
|
212 |
+
101 => 'Speech',
|
213 |
+
102 => 'Chanson',
|
214 |
+
103 => 'Opera',
|
215 |
+
104 => 'Chamber Music',
|
216 |
+
105 => 'Sonata',
|
217 |
+
106 => 'Symphony',
|
218 |
+
107 => 'Booty Bass',
|
219 |
+
108 => 'Primus',
|
220 |
+
109 => 'Porn Groove',
|
221 |
+
110 => 'Satire',
|
222 |
+
111 => 'Slow Jam',
|
223 |
+
112 => 'Club',
|
224 |
+
113 => 'Tango',
|
225 |
+
114 => 'Samba',
|
226 |
+
115 => 'Folklore',
|
227 |
+
116 => 'Ballad',
|
228 |
+
117 => 'Power Ballad',
|
229 |
+
118 => 'Rhythmic Soul',
|
230 |
+
119 => 'Freestyle',
|
231 |
+
120 => 'Duet',
|
232 |
+
121 => 'Punk Rock',
|
233 |
+
122 => 'Drum Solo',
|
234 |
+
123 => 'A Cappella',
|
235 |
+
124 => 'Euro-House',
|
236 |
+
125 => 'Dance Hall',
|
237 |
+
126 => 'Goa',
|
238 |
+
127 => 'Drum & Bass',
|
239 |
+
128 => 'Club-House',
|
240 |
+
129 => 'Hardcore',
|
241 |
+
130 => 'Terror',
|
242 |
+
131 => 'Indie',
|
243 |
+
132 => 'BritPop',
|
244 |
+
133 => 'Negerpunk',
|
245 |
+
134 => 'Polsk Punk',
|
246 |
+
135 => 'Beat',
|
247 |
+
136 => 'Christian Gangsta Rap',
|
248 |
+
137 => 'Heavy Metal',
|
249 |
+
138 => 'Black Metal',
|
250 |
+
139 => 'Crossover',
|
251 |
+
140 => 'Contemporary Christian',
|
252 |
+
141 => 'Christian Rock',
|
253 |
+
142 => 'Merengue',
|
254 |
+
143 => 'Salsa',
|
255 |
+
144 => 'Thrash Metal',
|
256 |
+
145 => 'Anime',
|
257 |
+
146 => 'JPop',
|
258 |
+
147 => 'Synthpop',
|
259 |
+
|
260 |
+
255 => 'Unknown',
|
261 |
+
|
262 |
+
'CR' => 'Cover',
|
263 |
+
'RX' => 'Remix'
|
264 |
+
);
|
265 |
+
|
266 |
+
static $GenreLookupSCMPX = array();
|
267 |
+
if ($allowSCMPXextended && empty($GenreLookupSCMPX)) {
|
268 |
+
$GenreLookupSCMPX = $GenreLookup;
|
269 |
+
// http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended
|
270 |
+
// Extended ID3v1 genres invented by SCMPX
|
271 |
+
// Note that 255 "Japanese Anime" conflicts with standard "Unknown"
|
272 |
+
$GenreLookupSCMPX[240] = 'Sacred';
|
273 |
+
$GenreLookupSCMPX[241] = 'Northern Europe';
|
274 |
+
$GenreLookupSCMPX[242] = 'Irish & Scottish';
|
275 |
+
$GenreLookupSCMPX[243] = 'Scotland';
|
276 |
+
$GenreLookupSCMPX[244] = 'Ethnic Europe';
|
277 |
+
$GenreLookupSCMPX[245] = 'Enka';
|
278 |
+
$GenreLookupSCMPX[246] = 'Children\'s Song';
|
279 |
+
$GenreLookupSCMPX[247] = 'Japanese Sky';
|
280 |
+
$GenreLookupSCMPX[248] = 'Japanese Heavy Rock';
|
281 |
+
$GenreLookupSCMPX[249] = 'Japanese Doom Rock';
|
282 |
+
$GenreLookupSCMPX[250] = 'Japanese J-POP';
|
283 |
+
$GenreLookupSCMPX[251] = 'Japanese Seiyu';
|
284 |
+
$GenreLookupSCMPX[252] = 'Japanese Ambient Techno';
|
285 |
+
$GenreLookupSCMPX[253] = 'Japanese Moemoe';
|
286 |
+
$GenreLookupSCMPX[254] = 'Japanese Tokusatsu';
|
287 |
+
//$GenreLookupSCMPX[255] = 'Japanese Anime';
|
288 |
+
}
|
289 |
+
|
290 |
+
return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup);
|
291 |
+
}
|
292 |
+
|
293 |
+
static function LookupGenreName($genreid, $allowSCMPXextended=true) {
|
294 |
+
switch ($genreid) {
|
295 |
+
case 'RX':
|
296 |
+
case 'CR':
|
297 |
+
break;
|
298 |
+
default:
|
299 |
+
if (!is_numeric($genreid)) {
|
300 |
+
return false;
|
301 |
+
}
|
302 |
+
$genreid = intval($genreid); // to handle 3 or '3' or '03'
|
303 |
+
break;
|
304 |
+
}
|
305 |
+
$GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended);
|
306 |
+
return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
|
307 |
+
}
|
308 |
+
|
309 |
+
static function LookupGenreID($genre, $allowSCMPXextended=false) {
|
310 |
+
$GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended);
|
311 |
+
$LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre));
|
312 |
+
foreach ($GenreLookup as $key => $value) {
|
313 |
+
if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) {
|
314 |
+
return $key;
|
315 |
+
}
|
316 |
+
}
|
317 |
+
return false;
|
318 |
+
}
|
319 |
+
|
320 |
+
static function StandardiseID3v1GenreName($OriginalGenre) {
|
321 |
+
if (($GenreID = getid3_id3v1::LookupGenreID($OriginalGenre)) !== false) {
|
322 |
+
return getid3_id3v1::LookupGenreName($GenreID);
|
323 |
+
}
|
324 |
+
return $OriginalGenre;
|
325 |
+
}
|
326 |
+
|
327 |
+
static function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') {
|
328 |
+
$ID3v1Tag = 'TAG';
|
329 |
+
$ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
330 |
+
$ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
331 |
+
$ID3v1Tag .= str_pad(trim(substr($album, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
332 |
+
$ID3v1Tag .= str_pad(trim(substr($year, 0, 4)), 4, "\x00", STR_PAD_LEFT);
|
333 |
+
if (!empty($track) && ($track > 0) && ($track <= 255)) {
|
334 |
+
$ID3v1Tag .= str_pad(trim(substr($comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT);
|
335 |
+
$ID3v1Tag .= "\x00";
|
336 |
+
if (gettype($track) == 'string') {
|
337 |
+
$track = (int) $track;
|
338 |
+
}
|
339 |
+
$ID3v1Tag .= chr($track);
|
340 |
+
} else {
|
341 |
+
$ID3v1Tag .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
342 |
+
}
|
343 |
+
if (($genreid < 0) || ($genreid > 147)) {
|
344 |
+
$genreid = 255; // 'unknown' genre
|
345 |
+
}
|
346 |
+
switch (gettype($genreid)) {
|
347 |
+
case 'string':
|
348 |
+
case 'integer':
|
349 |
+
$ID3v1Tag .= chr(intval($genreid));
|
350 |
+
break;
|
351 |
+
default:
|
352 |
+
$ID3v1Tag .= chr(255); // 'unknown' genre
|
353 |
+
break;
|
354 |
+
}
|
355 |
+
|
356 |
+
return $ID3v1Tag;
|
357 |
+
}
|
358 |
+
|
359 |
+
}
|
360 |
+
|
361 |
+
|
362 |
+
?>
|
includes/lib/getid3/module.tag.id3v2.php
ADDED
@@ -0,0 +1,3327 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
/////////////////////////////////////////////////////////////////
|
7 |
+
// See readme.txt for more details //
|
8 |
+
/////////////////////////////////////////////////////////////////
|
9 |
+
/// //
|
10 |
+
// module.tag.id3v2.php //
|
11 |
+
// module for analyzing ID3v2 tags //
|
12 |
+
// dependencies: module.tag.id3v1.php //
|
13 |
+
// ///
|
14 |
+
/////////////////////////////////////////////////////////////////
|
15 |
+
|
16 |
+
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
|
17 |
+
|
18 |
+
class getid3_id3v2 extends getid3_handler
|
19 |
+
{
|
20 |
+
var $inline_attachments = true; // true: return full data for all attachments; false: return no data for all attachments; integer: return data for attachments <= than this; string: save as file to this directory
|
21 |
+
var $StartingOffset = 0;
|
22 |
+
|
23 |
+
function Analyze() {
|
24 |
+
$info = &$this->getid3->info;
|
25 |
+
|
26 |
+
// Overall tag structure:
|
27 |
+
// +-----------------------------+
|
28 |
+
// | Header (10 bytes) |
|
29 |
+
// +-----------------------------+
|
30 |
+
// | Extended Header |
|
31 |
+
// | (variable length, OPTIONAL) |
|
32 |
+
// +-----------------------------+
|
33 |
+
// | Frames (variable length) |
|
34 |
+
// +-----------------------------+
|
35 |
+
// | Padding |
|
36 |
+
// | (variable length, OPTIONAL) |
|
37 |
+
// +-----------------------------+
|
38 |
+
// | Footer (10 bytes, OPTIONAL) |
|
39 |
+
// +-----------------------------+
|
40 |
+
|
41 |
+
// Header
|
42 |
+
// ID3v2/file identifier "ID3"
|
43 |
+
// ID3v2 version $04 00
|
44 |
+
// ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x)
|
45 |
+
// ID3v2 size 4 * %0xxxxxxx
|
46 |
+
|
47 |
+
|
48 |
+
// shortcuts
|
49 |
+
$info['id3v2']['header'] = true;
|
50 |
+
$thisfile_id3v2 = &$info['id3v2'];
|
51 |
+
$thisfile_id3v2['flags'] = array();
|
52 |
+
$thisfile_id3v2_flags = &$thisfile_id3v2['flags'];
|
53 |
+
|
54 |
+
|
55 |
+
fseek($this->getid3->fp, $this->StartingOffset, SEEK_SET);
|
56 |
+
$header = fread($this->getid3->fp, 10);
|
57 |
+
if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) {
|
58 |
+
|
59 |
+
$thisfile_id3v2['majorversion'] = ord($header{3});
|
60 |
+
$thisfile_id3v2['minorversion'] = ord($header{4});
|
61 |
+
|
62 |
+
// shortcut
|
63 |
+
$id3v2_majorversion = &$thisfile_id3v2['majorversion'];
|
64 |
+
|
65 |
+
} else {
|
66 |
+
|
67 |
+
unset($info['id3v2']);
|
68 |
+
return false;
|
69 |
+
|
70 |
+
}
|
71 |
+
|
72 |
+
if ($id3v2_majorversion > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists)
|
73 |
+
|
74 |
+
$info['error'][] = 'this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion'];
|
75 |
+
return false;
|
76 |
+
|
77 |
+
}
|
78 |
+
|
79 |
+
$id3_flags = ord($header{5});
|
80 |
+
switch ($id3v2_majorversion) {
|
81 |
+
case 2:
|
82 |
+
// %ab000000 in v2.2
|
83 |
+
$thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
|
84 |
+
$thisfile_id3v2_flags['compression'] = (bool) ($id3_flags & 0x40); // b - Compression
|
85 |
+
break;
|
86 |
+
|
87 |
+
case 3:
|
88 |
+
// %abc00000 in v2.3
|
89 |
+
$thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
|
90 |
+
$thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header
|
91 |
+
$thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator
|
92 |
+
break;
|
93 |
+
|
94 |
+
case 4:
|
95 |
+
// %abcd0000 in v2.4
|
96 |
+
$thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
|
97 |
+
$thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header
|
98 |
+
$thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator
|
99 |
+
$thisfile_id3v2_flags['isfooter'] = (bool) ($id3_flags & 0x10); // d - Footer present
|
100 |
+
break;
|
101 |
+
}
|
102 |
+
|
103 |
+
$thisfile_id3v2['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
|
104 |
+
|
105 |
+
$thisfile_id3v2['tag_offset_start'] = $this->StartingOffset;
|
106 |
+
$thisfile_id3v2['tag_offset_end'] = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength'];
|
107 |
+
|
108 |
+
|
109 |
+
|
110 |
+
// create 'encoding' key - used by getid3::HandleAllTags()
|
111 |
+
// in ID3v2 every field can have it's own encoding type
|
112 |
+
// so force everything to UTF-8 so it can be handled consistantly
|
113 |
+
$thisfile_id3v2['encoding'] = 'UTF-8';
|
114 |
+
|
115 |
+
|
116 |
+
// Frames
|
117 |
+
|
118 |
+
// All ID3v2 frames consists of one frame header followed by one or more
|
119 |
+
// fields containing the actual information. The header is always 10
|
120 |
+
// bytes and laid out as follows:
|
121 |
+
//
|
122 |
+
// Frame ID $xx xx xx xx (four characters)
|
123 |
+
// Size 4 * %0xxxxxxx
|
124 |
+
// Flags $xx xx
|
125 |
+
|
126 |
+
$sizeofframes = $thisfile_id3v2['headerlength'] - 10; // not including 10-byte initial header
|
127 |
+
if (!empty($thisfile_id3v2['exthead']['length'])) {
|
128 |
+
$sizeofframes -= ($thisfile_id3v2['exthead']['length'] + 4);
|
129 |
+
}
|
130 |
+
if (!empty($thisfile_id3v2_flags['isfooter'])) {
|
131 |
+
$sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio
|
132 |
+
}
|
133 |
+
if ($sizeofframes > 0) {
|
134 |
+
|
135 |
+
$framedata = fread($this->getid3->fp, $sizeofframes); // read all frames from file into $framedata variable
|
136 |
+
|
137 |
+
// if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x)
|
138 |
+
if (!empty($thisfile_id3v2_flags['unsynch']) && ($id3v2_majorversion <= 3)) {
|
139 |
+
$framedata = $this->DeUnsynchronise($framedata);
|
140 |
+
}
|
141 |
+
// [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead
|
142 |
+
// of on tag level, making it easier to skip frames, increasing the streamability
|
143 |
+
// of the tag. The unsynchronisation flag in the header [S:3.1] indicates that
|
144 |
+
// there exists an unsynchronised frame, while the new unsynchronisation flag in
|
145 |
+
// the frame header [S:4.1.2] indicates unsynchronisation.
|
146 |
+
|
147 |
+
|
148 |
+
//$framedataoffset = 10 + ($thisfile_id3v2['exthead']['length'] ? $thisfile_id3v2['exthead']['length'] + 4 : 0); // how many bytes into the stream - start from after the 10-byte header (and extended header length+4, if present)
|
149 |
+
$framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header
|
150 |
+
|
151 |
+
|
152 |
+
// Extended Header
|
153 |
+
if (!empty($thisfile_id3v2_flags['exthead'])) {
|
154 |
+
$extended_header_offset = 0;
|
155 |
+
|
156 |
+
if ($id3v2_majorversion == 3) {
|
157 |
+
|
158 |
+
// v2.3 definition:
|
159 |
+
//Extended header size $xx xx xx xx // 32-bit integer
|
160 |
+
//Extended Flags $xx xx
|
161 |
+
// %x0000000 %00000000 // v2.3
|
162 |
+
// x - CRC data present
|
163 |
+
//Size of padding $xx xx xx xx
|
164 |
+
|
165 |
+
$thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), 0);
|
166 |
+
$extended_header_offset += 4;
|
167 |
+
|
168 |
+
$thisfile_id3v2['exthead']['flag_bytes'] = 2;
|
169 |
+
$thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes']));
|
170 |
+
$extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes'];
|
171 |
+
|
172 |
+
$thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x8000);
|
173 |
+
|
174 |
+
$thisfile_id3v2['exthead']['padding_size'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4));
|
175 |
+
$extended_header_offset += 4;
|
176 |
+
|
177 |
+
if ($thisfile_id3v2['exthead']['flags']['crc']) {
|
178 |
+
$thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4));
|
179 |
+
$extended_header_offset += 4;
|
180 |
+
}
|
181 |
+
$extended_header_offset += $thisfile_id3v2['exthead']['padding_size'];
|
182 |
+
|
183 |
+
} elseif ($id3v2_majorversion == 4) {
|
184 |
+
|
185 |
+
// v2.4 definition:
|
186 |
+
//Extended header size 4 * %0xxxxxxx // 28-bit synchsafe integer
|
187 |
+
//Number of flag bytes $01
|
188 |
+
//Extended Flags $xx
|
189 |
+
// %0bcd0000 // v2.4
|
190 |
+
// b - Tag is an update
|
191 |
+
// Flag data length $00
|
192 |
+
// c - CRC data present
|
193 |
+
// Flag data length $05
|
194 |
+
// Total frame CRC 5 * %0xxxxxxx
|
195 |
+
// d - Tag restrictions
|
196 |
+
// Flag data length $01
|
197 |
+
|
198 |
+
$thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), true);
|
199 |
+
$extended_header_offset += 4;
|
200 |
+
|
201 |
+
$thisfile_id3v2['exthead']['flag_bytes'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should always be 1
|
202 |
+
$extended_header_offset += 1;
|
203 |
+
|
204 |
+
$thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes']));
|
205 |
+
$extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes'];
|
206 |
+
|
207 |
+
$thisfile_id3v2['exthead']['flags']['update'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x40);
|
208 |
+
$thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x20);
|
209 |
+
$thisfile_id3v2['exthead']['flags']['restrictions'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x10);
|
210 |
+
|
211 |
+
if ($thisfile_id3v2['exthead']['flags']['update']) {
|
212 |
+
$ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 0
|
213 |
+
$extended_header_offset += 1;
|
214 |
+
}
|
215 |
+
|
216 |
+
if ($thisfile_id3v2['exthead']['flags']['crc']) {
|
217 |
+
$ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 5
|
218 |
+
$extended_header_offset += 1;
|
219 |
+
$thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $ext_header_chunk_length), true, false);
|
220 |
+
$extended_header_offset += $ext_header_chunk_length;
|
221 |
+
}
|
222 |
+
|
223 |
+
if ($thisfile_id3v2['exthead']['flags']['restrictions']) {
|
224 |
+
$ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 1
|
225 |
+
$extended_header_offset += 1;
|
226 |
+
|
227 |
+
// %ppqrrstt
|
228 |
+
$restrictions_raw = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1));
|
229 |
+
$extended_header_offset += 1;
|
230 |
+
$thisfile_id3v2['exthead']['flags']['restrictions']['tagsize'] = ($restrictions_raw & 0xC0) >> 6; // p - Tag size restrictions
|
231 |
+
$thisfile_id3v2['exthead']['flags']['restrictions']['textenc'] = ($restrictions_raw & 0x20) >> 5; // q - Text encoding restrictions
|
232 |
+
$thisfile_id3v2['exthead']['flags']['restrictions']['textsize'] = ($restrictions_raw & 0x18) >> 3; // r - Text fields size restrictions
|
233 |
+
$thisfile_id3v2['exthead']['flags']['restrictions']['imgenc'] = ($restrictions_raw & 0x04) >> 2; // s - Image encoding restrictions
|
234 |
+
$thisfile_id3v2['exthead']['flags']['restrictions']['imgsize'] = ($restrictions_raw & 0x03) >> 0; // t - Image size restrictions
|
235 |
+
|
236 |
+
$thisfile_id3v2['exthead']['flags']['restrictions_text']['tagsize'] = $this->LookupExtendedHeaderRestrictionsTagSizeLimits($thisfile_id3v2['exthead']['flags']['restrictions']['tagsize']);
|
237 |
+
$thisfile_id3v2['exthead']['flags']['restrictions_text']['textenc'] = $this->LookupExtendedHeaderRestrictionsTextEncodings($thisfile_id3v2['exthead']['flags']['restrictions']['textenc']);
|
238 |
+
$thisfile_id3v2['exthead']['flags']['restrictions_text']['textsize'] = $this->LookupExtendedHeaderRestrictionsTextFieldSize($thisfile_id3v2['exthead']['flags']['restrictions']['textsize']);
|
239 |
+
$thisfile_id3v2['exthead']['flags']['restrictions_text']['imgenc'] = $this->LookupExtendedHeaderRestrictionsImageEncoding($thisfile_id3v2['exthead']['flags']['restrictions']['imgenc']);
|
240 |
+
$thisfile_id3v2['exthead']['flags']['restrictions_text']['imgsize'] = $this->LookupExtendedHeaderRestrictionsImageSizeSize($thisfile_id3v2['exthead']['flags']['restrictions']['imgsize']);
|
241 |
+
}
|
242 |
+
|
243 |
+
if ($thisfile_id3v2['exthead']['length'] != $extended_header_offset) {
|
244 |
+
$info['warning'][] = 'ID3v2.4 extended header length mismatch (expecting '.intval($thisfile_id3v2['exthead']['length']).', found '.intval($extended_header_offset).')';
|
245 |
+
}
|
246 |
+
}
|
247 |
+
|
248 |
+
$framedataoffset += $extended_header_offset;
|
249 |
+
$framedata = substr($framedata, $extended_header_offset);
|
250 |
+
} // end extended header
|
251 |
+
|
252 |
+
|
253 |
+
while (isset($framedata) && (strlen($framedata) > 0)) { // cycle through until no more frame data is left to parse
|
254 |
+
if (strlen($framedata) <= $this->ID3v2HeaderLength($id3v2_majorversion)) {
|
255 |
+
// insufficient room left in ID3v2 header for actual data - must be padding
|
256 |
+
$thisfile_id3v2['padding']['start'] = $framedataoffset;
|
257 |
+
$thisfile_id3v2['padding']['length'] = strlen($framedata);
|
258 |
+
$thisfile_id3v2['padding']['valid'] = true;
|
259 |
+
for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) {
|
260 |
+
if ($framedata{$i} != "\x00") {
|
261 |
+
$thisfile_id3v2['padding']['valid'] = false;
|
262 |
+
$thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
|
263 |
+
$info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)';
|
264 |
+
break;
|
265 |
+
}
|
266 |
+
}
|
267 |
+
break; // skip rest of ID3v2 header
|
268 |
+
}
|
269 |
+
if ($id3v2_majorversion == 2) {
|
270 |
+
// Frame ID $xx xx xx (three characters)
|
271 |
+
// Size $xx xx xx (24-bit integer)
|
272 |
+
// Flags $xx xx
|
273 |
+
|
274 |
+
$frame_header = substr($framedata, 0, 6); // take next 6 bytes for header
|
275 |
+
$framedata = substr($framedata, 6); // and leave the rest in $framedata
|
276 |
+
$frame_name = substr($frame_header, 0, 3);
|
277 |
+
$frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 3, 3), 0);
|
278 |
+
$frame_flags = 0; // not used for anything in ID3v2.2, just set to avoid E_NOTICEs
|
279 |
+
|
280 |
+
} elseif ($id3v2_majorversion > 2) {
|
281 |
+
|
282 |
+
// Frame ID $xx xx xx xx (four characters)
|
283 |
+
// Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+)
|
284 |
+
// Flags $xx xx
|
285 |
+
|
286 |
+
$frame_header = substr($framedata, 0, 10); // take next 10 bytes for header
|
287 |
+
$framedata = substr($framedata, 10); // and leave the rest in $framedata
|
288 |
+
|
289 |
+
$frame_name = substr($frame_header, 0, 4);
|
290 |
+
if ($id3v2_majorversion == 3) {
|
291 |
+
$frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
|
292 |
+
} else { // ID3v2.4+
|
293 |
+
$frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value)
|
294 |
+
}
|
295 |
+
|
296 |
+
if ($frame_size < (strlen($framedata) + 4)) {
|
297 |
+
$nextFrameID = substr($framedata, $frame_size, 4);
|
298 |
+
if ($this->IsValidID3v2FrameName($nextFrameID, $id3v2_majorversion)) {
|
299 |
+
// next frame is OK
|
300 |
+
} elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) {
|
301 |
+
// MP3ext known broken frames - "ok" for the purposes of this test
|
302 |
+
} elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) {
|
303 |
+
$info['warning'][] = 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3';
|
304 |
+
$id3v2_majorversion = 3;
|
305 |
+
$frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
|
306 |
+
}
|
307 |
+
}
|
308 |
+
|
309 |
+
|
310 |
+
$frame_flags = getid3_lib::BigEndian2Int(substr($frame_header, 8, 2));
|
311 |
+
}
|
312 |
+
|
313 |
+
if ((($id3v2_majorversion == 2) && ($frame_name == "\x00\x00\x00")) || ($frame_name == "\x00\x00\x00\x00")) {
|
314 |
+
// padding encountered
|
315 |
+
|
316 |
+
$thisfile_id3v2['padding']['start'] = $framedataoffset;
|
317 |
+
$thisfile_id3v2['padding']['length'] = strlen($frame_header) + strlen($framedata);
|
318 |
+
$thisfile_id3v2['padding']['valid'] = true;
|
319 |
+
|
320 |
+
$len = strlen($framedata);
|
321 |
+
for ($i = 0; $i < $len; $i++) {
|
322 |
+
if ($framedata{$i} != "\x00") {
|
323 |
+
$thisfile_id3v2['padding']['valid'] = false;
|
324 |
+
$thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
|
325 |
+
$info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)';
|
326 |
+
break;
|
327 |
+
}
|
328 |
+
}
|
329 |
+
break; // skip rest of ID3v2 header
|
330 |
+
}
|
331 |
+
|
332 |
+
if ($frame_name == 'COM ') {
|
333 |
+
$info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably others too)]';
|
334 |
+
$frame_name = 'COMM';
|
335 |
+
}
|
336 |
+
if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) {
|
337 |
+
|
338 |
+
unset($parsedFrame);
|
339 |
+
$parsedFrame['frame_name'] = $frame_name;
|
340 |
+
$parsedFrame['frame_flags_raw'] = $frame_flags;
|
341 |
+
$parsedFrame['data'] = substr($framedata, 0, $frame_size);
|
342 |
+
$parsedFrame['datalength'] = getid3_lib::CastAsInt($frame_size);
|
343 |
+
$parsedFrame['dataoffset'] = $framedataoffset;
|
344 |
+
|
345 |
+
$this->ParseID3v2Frame($parsedFrame);
|
346 |
+
$thisfile_id3v2[$frame_name][] = $parsedFrame;
|
347 |
+
|
348 |
+
$framedata = substr($framedata, $frame_size);
|
349 |
+
|
350 |
+
} else { // invalid frame length or FrameID
|
351 |
+
|
352 |
+
if ($frame_size <= strlen($framedata)) {
|
353 |
+
|
354 |
+
if ($this->IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $id3v2_majorversion)) {
|
355 |
+
|
356 |
+
// next frame is valid, just skip the current frame
|
357 |
+
$framedata = substr($framedata, $frame_size);
|
358 |
+
$info['warning'][] = 'Next ID3v2 frame is valid, skipping current frame.';
|
359 |
+
|
360 |
+
} else {
|
361 |
+
|
362 |
+
// next frame is invalid too, abort processing
|
363 |
+
//unset($framedata);
|
364 |
+
$framedata = null;
|
365 |
+
$info['error'][] = 'Next ID3v2 frame is also invalid, aborting processing.';
|
366 |
+
|
367 |
+
}
|
368 |
+
|
369 |
+
} elseif ($frame_size == strlen($framedata)) {
|
370 |
+
|
371 |
+
// this is the last frame, just skip
|
372 |
+
$info['warning'][] = 'This was the last ID3v2 frame.';
|
373 |
+
|
374 |
+
} else {
|
375 |
+
|
376 |
+
// next frame is invalid too, abort processing
|
377 |
+
//unset($framedata);
|
378 |
+
$framedata = null;
|
379 |
+
$info['warning'][] = 'Invalid ID3v2 frame size, aborting.';
|
380 |
+
|
381 |
+
}
|
382 |
+
if (!$this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion)) {
|
383 |
+
|
384 |
+
switch ($frame_name) {
|
385 |
+
case "\x00\x00".'MP':
|
386 |
+
case "\x00".'MP3':
|
387 |
+
case ' MP3':
|
388 |
+
case 'MP3e':
|
389 |
+
case "\x00".'MP':
|
390 |
+
case ' MP':
|
391 |
+
case 'MP3':
|
392 |
+
$info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]';
|
393 |
+
break;
|
394 |
+
|
395 |
+
default:
|
396 |
+
$info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).';
|
397 |
+
break;
|
398 |
+
}
|
399 |
+
|
400 |
+
} elseif (!isset($framedata) || ($frame_size > strlen($framedata))) {
|
401 |
+
|
402 |
+
$info['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($framedata) ('.(isset($framedata) ? strlen($framedata) : 'null').')).';
|
403 |
+
|
404 |
+
} else {
|
405 |
+
|
406 |
+
$info['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).';
|
407 |
+
|
408 |
+
}
|
409 |
+
|
410 |
+
}
|
411 |
+
$framedataoffset += ($frame_size + $this->ID3v2HeaderLength($id3v2_majorversion));
|
412 |
+
|
413 |
+
}
|
414 |
+
|
415 |
+
}
|
416 |
+
|
417 |
+
|
418 |
+
// Footer
|
419 |
+
|
420 |
+
// The footer is a copy of the header, but with a different identifier.
|
421 |
+
// ID3v2 identifier "3DI"
|
422 |
+
// ID3v2 version $04 00
|
423 |
+
// ID3v2 flags %abcd0000
|
424 |
+
// ID3v2 size 4 * %0xxxxxxx
|
425 |
+
|
426 |
+
if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) {
|
427 |
+
$footer = fread($this->getid3->fp, 10);
|
428 |
+
if (substr($footer, 0, 3) == '3DI') {
|
429 |
+
$thisfile_id3v2['footer'] = true;
|
430 |
+
$thisfile_id3v2['majorversion_footer'] = ord($footer{3});
|
431 |
+
$thisfile_id3v2['minorversion_footer'] = ord($footer{4});
|
432 |
+
}
|
433 |
+
if ($thisfile_id3v2['majorversion_footer'] <= 4) {
|
434 |
+
$id3_flags = ord(substr($footer{5}));
|
435 |
+
$thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80);
|
436 |
+
$thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40);
|
437 |
+
$thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20);
|
438 |
+
$thisfile_id3v2_flags['isfooter_footer'] = (bool) ($id3_flags & 0x10);
|
439 |
+
|
440 |
+
$thisfile_id3v2['footerlength'] = getid3_lib::BigEndian2Int(substr($footer, 6, 4), 1);
|
441 |
+
}
|
442 |
+
} // end footer
|
443 |
+
|
444 |
+
if (isset($thisfile_id3v2['comments']['genre'])) {
|
445 |
+
foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) {
|
446 |
+
unset($thisfile_id3v2['comments']['genre'][$key]);
|
447 |
+
$thisfile_id3v2['comments'] = getid3_lib::array_merge_noclobber($thisfile_id3v2['comments'], array('genre'=>$this->ParseID3v2GenreString($value)));
|
448 |
+
}
|
449 |
+
}
|
450 |
+
|
451 |
+
if (isset($thisfile_id3v2['comments']['track'])) {
|
452 |
+
foreach ($thisfile_id3v2['comments']['track'] as $key => $value) {
|
453 |
+
if (strstr($value, '/')) {
|
454 |
+
list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]);
|
455 |
+
}
|
456 |
+
}
|
457 |
+
}
|
458 |
+
|
459 |
+
if (!isset($thisfile_id3v2['comments']['year']) && !empty($thisfile_id3v2['comments']['recording_time'][0]) && preg_match('#^([0-9]{4})#', trim($thisfile_id3v2['comments']['recording_time'][0]), $matches)) {
|
460 |
+
$thisfile_id3v2['comments']['year'] = array($matches[1]);
|
461 |
+
}
|
462 |
+
|
463 |
+
|
464 |
+
if (!empty($thisfile_id3v2['TXXX'])) {
|
465 |
+
// MediaMonkey does this, maybe others: write a blank RGAD frame, but put replay-gain adjustment values in TXXX frames
|
466 |
+
foreach ($thisfile_id3v2['TXXX'] as $txxx_array) {
|
467 |
+
switch ($txxx_array['description']) {
|
468 |
+
case 'replaygain_track_gain':
|
469 |
+
if (empty($info['replay_gain']['track']['adjustment']) && !empty($txxx_array['data'])) {
|
470 |
+
$info['replay_gain']['track']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data'])));
|
471 |
+
}
|
472 |
+
break;
|
473 |
+
case 'replaygain_track_peak':
|
474 |
+
if (empty($info['replay_gain']['track']['peak']) && !empty($txxx_array['data'])) {
|
475 |
+
$info['replay_gain']['track']['peak'] = floatval($txxx_array['data']);
|
476 |
+
}
|
477 |
+
break;
|
478 |
+
case 'replaygain_album_gain':
|
479 |
+
if (empty($info['replay_gain']['album']['adjustment']) && !empty($txxx_array['data'])) {
|
480 |
+
$info['replay_gain']['album']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data'])));
|
481 |
+
}
|
482 |
+
break;
|
483 |
+
}
|
484 |
+
}
|
485 |
+
}
|
486 |
+
|
487 |
+
|
488 |
+
// Set avdataoffset
|
489 |
+
$info['avdataoffset'] = $thisfile_id3v2['headerlength'];
|
490 |
+
if (isset($thisfile_id3v2['footer'])) {
|
491 |
+
$info['avdataoffset'] += 10;
|
492 |
+
}
|
493 |
+
|
494 |
+
return true;
|
495 |
+
}
|
496 |
+
|
497 |
+
|
498 |
+
function ParseID3v2GenreString($genrestring) {
|
499 |
+
// Parse genres into arrays of genreName and genreID
|
500 |
+
// ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)'
|
501 |
+
// ID3v2.4.x: '21' $00 'Eurodisco' $00
|
502 |
+
$clean_genres = array();
|
503 |
+
if (strpos($genrestring, "\x00") === false) {
|
504 |
+
$genrestring = preg_replace('#\(([0-9]{1,3})\)#', '$1'."\x00", $genrestring);
|
505 |
+
}
|
506 |
+
$genre_elements = explode("\x00", $genrestring);
|
507 |
+
foreach ($genre_elements as $element) {
|
508 |
+
$element = trim($element);
|
509 |
+
if ($element) {
|
510 |
+
if (preg_match('#^[0-9]{1,3}#', $element)) {
|
511 |
+
$clean_genres[] = getid3_id3v1::LookupGenreName($element);
|
512 |
+
} else {
|
513 |
+
$clean_genres[] = str_replace('((', '(', $element);
|
514 |
+
}
|
515 |
+
}
|
516 |
+
}
|
517 |
+
return $clean_genres;
|
518 |
+
}
|
519 |
+
|
520 |
+
|
521 |
+
function ParseID3v2Frame(&$parsedFrame) {
|
522 |
+
|
523 |
+
// shortcuts
|
524 |
+
$info = &$this->getid3->info;
|
525 |
+
$id3v2_majorversion = $info['id3v2']['majorversion'];
|
526 |
+
|
527 |
+
$parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']);
|
528 |
+
if (empty($parsedFrame['framenamelong'])) {
|
529 |
+
unset($parsedFrame['framenamelong']);
|
530 |
+
}
|
531 |
+
$parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']);
|
532 |
+
if (empty($parsedFrame['framenameshort'])) {
|
533 |
+
unset($parsedFrame['framenameshort']);
|
534 |
+
}
|
535 |
+
|
536 |
+
if ($id3v2_majorversion >= 3) { // frame flags are not part of the ID3v2.2 standard
|
537 |
+
if ($id3v2_majorversion == 3) {
|
538 |
+
// Frame Header Flags
|
539 |
+
// %abc00000 %ijk00000
|
540 |
+
$parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000); // a - Tag alter preservation
|
541 |
+
$parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // b - File alter preservation
|
542 |
+
$parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // c - Read only
|
543 |
+
$parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0080); // i - Compression
|
544 |
+
$parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // j - Encryption
|
545 |
+
$parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0020); // k - Grouping identity
|
546 |
+
|
547 |
+
} elseif ($id3v2_majorversion == 4) {
|
548 |
+
// Frame Header Flags
|
549 |
+
// %0abc0000 %0h00kmnp
|
550 |
+
$parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // a - Tag alter preservation
|
551 |
+
$parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // b - File alter preservation
|
552 |
+
$parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000); // c - Read only
|
553 |
+
$parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // h - Grouping identity
|
554 |
+
$parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0008); // k - Compression
|
555 |
+
$parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0004); // m - Encryption
|
556 |
+
$parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0002); // n - Unsynchronisation
|
557 |
+
$parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0001); // p - Data length indicator
|
558 |
+
|
559 |
+
// Frame-level de-unsynchronisation - ID3v2.4
|
560 |
+
if ($parsedFrame['flags']['Unsynchronisation']) {
|
561 |
+
$parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']);
|
562 |
+
}
|
563 |
+
|
564 |
+
if ($parsedFrame['flags']['DataLengthIndicator']) {
|
565 |
+
$parsedFrame['data_length_indicator'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4), 1);
|
566 |
+
$parsedFrame['data'] = substr($parsedFrame['data'], 4);
|
567 |
+
}
|
568 |
+
}
|
569 |
+
|
570 |
+
// Frame-level de-compression
|
571 |
+
if ($parsedFrame['flags']['compression']) {
|
572 |
+
$parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4));
|
573 |
+
if (!function_exists('gzuncompress')) {
|
574 |
+
$info['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"';
|
575 |
+
} else {
|
576 |
+
if ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) {
|
577 |
+
//if ($decompresseddata = @gzuncompress($parsedFrame['data'])) {
|
578 |
+
$parsedFrame['data'] = $decompresseddata;
|
579 |
+
unset($decompresseddata);
|
580 |
+
} else {
|
581 |
+
$info['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"';
|
582 |
+
}
|
583 |
+
}
|
584 |
+
}
|
585 |
+
}
|
586 |
+
|
587 |
+
if (!empty($parsedFrame['flags']['DataLengthIndicator'])) {
|
588 |
+
if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) {
|
589 |
+
$info['warning'][] = 'ID3v2 frame "'.$parsedFrame['frame_name'].'" should be '.$parsedFrame['data_length_indicator'].' bytes long according to DataLengthIndicator, but found '.strlen($parsedFrame['data']).' bytes of data';
|
590 |
+
}
|
591 |
+
}
|
592 |
+
|
593 |
+
if (isset($parsedFrame['datalength']) && ($parsedFrame['datalength'] == 0)) {
|
594 |
+
|
595 |
+
$warning = 'Frame "'.$parsedFrame['frame_name'].'" at offset '.$parsedFrame['dataoffset'].' has no data portion';
|
596 |
+
switch ($parsedFrame['frame_name']) {
|
597 |
+
case 'WCOM':
|
598 |
+
$warning .= ' (this is known to happen with files tagged by RioPort)';
|
599 |
+
break;
|
600 |
+
|
601 |
+
default:
|
602 |
+
break;
|
603 |
+
}
|
604 |
+
$info['warning'][] = $warning;
|
605 |
+
|
606 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'UFID')) || // 4.1 UFID Unique file identifier
|
607 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'UFI'))) { // 4.1 UFI Unique file identifier
|
608 |
+
// There may be more than one 'UFID' frame in a tag,
|
609 |
+
// but only one with the same 'Owner identifier'.
|
610 |
+
// <Header for 'Unique file identifier', ID: 'UFID'>
|
611 |
+
// Owner identifier <text string> $00
|
612 |
+
// Identifier <up to 64 bytes binary data>
|
613 |
+
$exploded = explode("\x00", $parsedFrame['data'], 2);
|
614 |
+
$parsedFrame['ownerid'] = (isset($exploded[0]) ? $exploded[0] : '');
|
615 |
+
$parsedFrame['data'] = (isset($exploded[1]) ? $exploded[1] : '');
|
616 |
+
|
617 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'TXXX')) || // 4.2.2 TXXX User defined text information frame
|
618 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'TXX'))) { // 4.2.2 TXX User defined text information frame
|
619 |
+
// There may be more than one 'TXXX' frame in each tag,
|
620 |
+
// but only one with the same description.
|
621 |
+
// <Header for 'User defined text information frame', ID: 'TXXX'>
|
622 |
+
// Text encoding $xx
|
623 |
+
// Description <text string according to encoding> $00 (00)
|
624 |
+
// Value <text string according to encoding>
|
625 |
+
|
626 |
+
$frame_offset = 0;
|
627 |
+
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
628 |
+
|
629 |
+
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
|
630 |
+
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
|
631 |
+
}
|
632 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
|
633 |
+
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
|
634 |
+
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
|
635 |
+
}
|
636 |
+
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
637 |
+
if (ord($frame_description) === 0) {
|
638 |
+
$frame_description = '';
|
639 |
+
}
|
640 |
+
$parsedFrame['encodingid'] = $frame_textencoding;
|
641 |
+
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
|
642 |
+
|
643 |
+
$parsedFrame['description'] = $frame_description;
|
644 |
+
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
|
645 |
+
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
|
646 |
+
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
|
647 |
+
}
|
648 |
+
//unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain
|
649 |
+
|
650 |
+
|
651 |
+
} elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame
|
652 |
+
// There may only be one text information frame of its kind in an tag.
|
653 |
+
// <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
|
654 |
+
// excluding 'TXXX' described in 4.2.6.>
|
655 |
+
// Text encoding $xx
|
656 |
+
// Information <text string(s) according to encoding>
|
657 |
+
|
658 |
+
$frame_offset = 0;
|
659 |
+
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
660 |
+
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
|
661 |
+
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
|
662 |
+
}
|
663 |
+
|
664 |
+
$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
|
665 |
+
|
666 |
+
$parsedFrame['encodingid'] = $frame_textencoding;
|
667 |
+
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
|
668 |
+
|
669 |
+
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
|
670 |
+
$string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
|
671 |
+
$string = rtrim($string, "\x00"); // remove possible terminating null (put by encoding id or software bug)
|
672 |
+
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string;
|
673 |
+
unset($string);
|
674 |
+
}
|
675 |
+
|
676 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'WXXX')) || // 4.3.2 WXXX User defined URL link frame
|
677 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'WXX'))) { // 4.3.2 WXX User defined URL link frame
|
678 |
+
// There may be more than one 'WXXX' frame in each tag,
|
679 |
+
// but only one with the same description
|
680 |
+
// <Header for 'User defined URL link frame', ID: 'WXXX'>
|
681 |
+
// Text encoding $xx
|
682 |
+
// Description <text string according to encoding> $00 (00)
|
683 |
+
// URL <text string>
|
684 |
+
|
685 |
+
$frame_offset = 0;
|
686 |
+
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
687 |
+
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
|
688 |
+
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
|
689 |
+
}
|
690 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
|
691 |
+
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
|
692 |
+
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
|
693 |
+
}
|
694 |
+
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
695 |
+
|
696 |
+
if (ord($frame_description) === 0) {
|
697 |
+
$frame_description = '';
|
698 |
+
}
|
699 |
+
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
|
700 |
+
|
701 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
|
702 |
+
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
|
703 |
+
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
|
704 |
+
}
|
705 |
+
if ($frame_terminatorpos) {
|
706 |
+
// there are null bytes after the data - this is not according to spec
|
707 |
+
// only use data up to first null byte
|
708 |
+
$frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
|
709 |
+
} else {
|
710 |
+
// no null bytes following data, just use all data
|
711 |
+
$frame_urldata = (string) $parsedFrame['data'];
|
712 |
+
}
|
713 |
+
|
714 |
+
$parsedFrame['encodingid'] = $frame_textencoding;
|
715 |
+
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
|
716 |
+
|
717 |
+
$parsedFrame['url'] = $frame_urldata;
|
718 |
+
$parsedFrame['description'] = $frame_description;
|
719 |
+
if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
|
720 |
+
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']);
|
721 |
+
}
|
722 |
+
unset($parsedFrame['data']);
|
723 |
+
|
724 |
+
|
725 |
+
} elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames
|
726 |
+
// There may only be one URL link frame of its kind in a tag,
|
727 |
+
// except when stated otherwise in the frame description
|
728 |
+
// <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX'
|
729 |
+
// described in 4.3.2.>
|
730 |
+
// URL <text string>
|
731 |
+
|
732 |
+
$parsedFrame['url'] = trim($parsedFrame['data']);
|
733 |
+
if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
|
734 |
+
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url'];
|
735 |
+
}
|
736 |
+
unset($parsedFrame['data']);
|
737 |
+
|
738 |
+
|
739 |
+
} elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only)
|
740 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'IPL'))) { // 4.4 IPL Involved people list (ID3v2.2 only)
|
741 |
+
// There may only be one 'IPL' frame in each tag
|
742 |
+
// <Header for 'User defined URL link frame', ID: 'IPL'>
|
743 |
+
// Text encoding $xx
|
744 |
+
// People list strings <textstrings>
|
745 |
+
|
746 |
+
$frame_offset = 0;
|
747 |
+
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
748 |
+
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
|
749 |
+
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
|
750 |
+
}
|
751 |
+
$parsedFrame['encodingid'] = $frame_textencoding;
|
752 |
+
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']);
|
753 |
+
|
754 |
+
$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
|
755 |
+
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
|
756 |
+
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
|
757 |
+
}
|
758 |
+
|
759 |
+
|
760 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MCDI')) || // 4.4 MCDI Music CD identifier
|
761 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MCI'))) { // 4.5 MCI Music CD identifier
|
762 |
+
// There may only be one 'MCDI' frame in each tag
|
763 |
+
// <Header for 'Music CD identifier', ID: 'MCDI'>
|
764 |
+
// CD TOC <binary data>
|
765 |
+
|
766 |
+
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
|
767 |
+
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
|
768 |
+
}
|
769 |
+
|
770 |
+
|
771 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ETCO')) || // 4.5 ETCO Event timing codes
|
772 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ETC'))) { // 4.6 ETC Event timing codes
|
773 |
+
// There may only be one 'ETCO' frame in each tag
|
774 |
+
// <Header for 'Event timing codes', ID: 'ETCO'>
|
775 |
+
// Time stamp format $xx
|
776 |
+
// Where time stamp format is:
|
777 |
+
// $01 (32-bit value) MPEG frames from beginning of file
|
778 |
+
// $02 (32-bit value) milliseconds from beginning of file
|
779 |
+
// Followed by a list of key events in the following format:
|
780 |
+
// Type of event $xx
|
781 |
+
// Time stamp $xx (xx ...)
|
782 |
+
// The 'Time stamp' is set to zero if directly at the beginning of the sound
|
783 |
+
// or after the previous event. All events MUST be sorted in chronological order.
|
784 |
+
|
785 |
+
$frame_offset = 0;
|
786 |
+
$parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
787 |
+
|
788 |
+
while ($frame_offset < strlen($parsedFrame['data'])) {
|
789 |
+
$parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1);
|
790 |
+
$parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']);
|
791 |
+
$parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
|
792 |
+
$frame_offset += 4;
|
793 |
+
}
|
794 |
+
unset($parsedFrame['data']);
|
795 |
+
|
796 |
+
|
797 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MLLT')) || // 4.6 MLLT MPEG location lookup table
|
798 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MLL'))) { // 4.7 MLL MPEG location lookup table
|
799 |
+
// There may only be one 'MLLT' frame in each tag
|
800 |
+
// <Header for 'Location lookup table', ID: 'MLLT'>
|
801 |
+
// MPEG frames between reference $xx xx
|
802 |
+
// Bytes between reference $xx xx xx
|
803 |
+
// Milliseconds between reference $xx xx xx
|
804 |
+
// Bits for bytes deviation $xx
|
805 |
+
// Bits for milliseconds dev. $xx
|
806 |
+
// Then for every reference the following data is included;
|
807 |
+
// Deviation in bytes %xxx....
|
808 |
+
// Deviation in milliseconds %xxx....
|
809 |
+
|
810 |
+
$frame_offset = 0;
|
811 |
+
$parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2));
|
812 |
+
$parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3));
|
813 |
+
$parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3));
|
814 |
+
$parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1));
|
815 |
+
$parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1));
|
816 |
+
$parsedFrame['data'] = substr($parsedFrame['data'], 10);
|
817 |
+
while ($frame_offset < strlen($parsedFrame['data'])) {
|
818 |
+
$deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
|
819 |
+
}
|
820 |
+
$reference_counter = 0;
|
821 |
+
while (strlen($deviationbitstream) > 0) {
|
822 |
+
$parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation']));
|
823 |
+
$parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation']));
|
824 |
+
$deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']);
|
825 |
+
$reference_counter++;
|
826 |
+
}
|
827 |
+
unset($parsedFrame['data']);
|
828 |
+
|
829 |
+
|
830 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYTC')) || // 4.7 SYTC Synchronised tempo codes
|
831 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'STC'))) { // 4.8 STC Synchronised tempo codes
|
832 |
+
// There may only be one 'SYTC' frame in each tag
|
833 |
+
// <Header for 'Synchronised tempo codes', ID: 'SYTC'>
|
834 |
+
// Time stamp format $xx
|
835 |
+
// Tempo data <binary data>
|
836 |
+
// Where time stamp format is:
|
837 |
+
// $01 (32-bit value) MPEG frames from beginning of file
|
838 |
+
// $02 (32-bit value) milliseconds from beginning of file
|
839 |
+
|
840 |
+
$frame_offset = 0;
|
841 |
+
$parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
842 |
+
$timestamp_counter = 0;
|
843 |
+
while ($frame_offset < strlen($parsedFrame['data'])) {
|
844 |
+
$parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
845 |
+
if ($parsedFrame[$timestamp_counter]['tempo'] == 255) {
|
846 |
+
$parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
847 |
+
}
|
848 |
+
$parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
|
849 |
+
$frame_offset += 4;
|
850 |
+
$timestamp_counter++;
|
851 |
+
}
|
852 |
+
unset($parsedFrame['data']);
|
853 |
+
|
854 |
+
|
855 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USLT')) || // 4.8 USLT Unsynchronised lyric/text transcription
|
856 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ULT'))) { // 4.9 ULT Unsynchronised lyric/text transcription
|
857 |
+
// There may be more than one 'Unsynchronised lyrics/text transcription' frame
|
858 |
+
// in each tag, but only one with the same language and content descriptor.
|
859 |
+
// <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'>
|
860 |
+
// Text encoding $xx
|
861 |
+
// Language $xx xx xx
|
862 |
+
// Content descriptor <text string according to encoding> $00 (00)
|
863 |
+
// Lyrics/text <full text string according to encoding>
|
864 |
+
|
865 |
+
$frame_offset = 0;
|
866 |
+
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
867 |
+
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
|
868 |
+
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
|
869 |
+
}
|
870 |
+
$frame_language = substr($parsedFrame['data'], $frame_offset, 3);
|
871 |
+
$frame_offset += 3;
|
872 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
|
873 |
+
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
|
874 |
+
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
|
875 |
+
}
|
876 |
+
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
877 |
+
if (ord($frame_description) === 0) {
|
878 |
+
$frame_description = '';
|
879 |
+
}
|
880 |
+
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
|
881 |
+
|
882 |
+
$parsedFrame['encodingid'] = $frame_textencoding;
|
883 |
+
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
|
884 |
+
|
885 |
+
$parsedFrame['data'] = $parsedFrame['data'];
|
886 |
+
$parsedFrame['language'] = $frame_language;
|
887 |
+
$parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
|
888 |
+
$parsedFrame['description'] = $frame_description;
|
889 |
+
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
|
890 |
+
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
|
891 |
+
}
|
892 |
+
unset($parsedFrame['data']);
|
893 |
+
|
894 |
+
|
895 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYLT')) || // 4.9 SYLT Synchronised lyric/text
|
896 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'SLT'))) { // 4.10 SLT Synchronised lyric/text
|
897 |
+
// There may be more than one 'SYLT' frame in each tag,
|
898 |
+
// but only one with the same language and content descriptor.
|
899 |
+
// <Header for 'Synchronised lyrics/text', ID: 'SYLT'>
|
900 |
+
// Text encoding $xx
|
901 |
+
// Language $xx xx xx
|
902 |
+
// Time stamp format $xx
|
903 |
+
// $01 (32-bit value) MPEG frames from beginning of file
|
904 |
+
// $02 (32-bit value) milliseconds from beginning of file
|
905 |
+
// Content type $xx
|
906 |
+
// Content descriptor <text string according to encoding> $00 (00)
|
907 |
+
// Terminated text to be synced (typically a syllable)
|
908 |
+
// Sync identifier (terminator to above string) $00 (00)
|
909 |
+
// Time stamp $xx (xx ...)
|
910 |
+
|
911 |
+
$frame_offset = 0;
|
912 |
+
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
913 |
+
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
|
914 |
+
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
|
915 |
+
}
|
916 |
+
$frame_language = substr($parsedFrame['data'], $frame_offset, 3);
|
917 |
+
$frame_offset += 3;
|
918 |
+
$parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
919 |
+
$parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
920 |
+
$parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']);
|
921 |
+
$parsedFrame['encodingid'] = $frame_textencoding;
|
922 |
+
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
|
923 |
+
|
924 |
+
$parsedFrame['language'] = $frame_language;
|
925 |
+
$parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
|
926 |
+
|
927 |
+
$timestampindex = 0;
|
928 |
+
$frame_remainingdata = substr($parsedFrame['data'], $frame_offset);
|
929 |
+
while (strlen($frame_remainingdata)) {
|
930 |
+
$frame_offset = 0;
|
931 |
+
$frame_terminatorpos = strpos($frame_remainingdata, $this->TextEncodingTerminatorLookup($frame_textencoding));
|
932 |
+
if ($frame_terminatorpos === false) {
|
933 |
+
$frame_remainingdata = '';
|
934 |
+
} else {
|
935 |
+
if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
|
936 |
+
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
|
937 |
+
}
|
938 |
+
$parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
|
939 |
+
|
940 |
+
$frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
|
941 |
+
if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) {
|
942 |
+
// timestamp probably omitted for first data item
|
943 |
+
} else {
|
944 |
+
$parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4));
|
945 |
+
$frame_remainingdata = substr($frame_remainingdata, 4);
|
946 |
+
}
|
947 |
+
$timestampindex++;
|
948 |
+
}
|
949 |
+
}
|
950 |
+
unset($parsedFrame['data']);
|
951 |
+
|
952 |
+
|
953 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMM')) || // 4.10 COMM Comments
|
954 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'COM'))) { // 4.11 COM Comments
|
955 |
+
// There may be more than one comment frame in each tag,
|
956 |
+
// but only one with the same language and content descriptor.
|
957 |
+
// <Header for 'Comment', ID: 'COMM'>
|
958 |
+
// Text encoding $xx
|
959 |
+
// Language $xx xx xx
|
960 |
+
// Short content descrip. <text string according to encoding> $00 (00)
|
961 |
+
// The actual text <full text string according to encoding>
|
962 |
+
|
963 |
+
if (strlen($parsedFrame['data']) < 5) {
|
964 |
+
|
965 |
+
$info['warning'][] = 'Invalid data (too short) for "'.$parsedFrame['frame_name'].'" frame at offset '.$parsedFrame['dataoffset'];
|
966 |
+
|
967 |
+
} else {
|
968 |
+
|
969 |
+
$frame_offset = 0;
|
970 |
+
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
971 |
+
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
|
972 |
+
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
|
973 |
+
}
|
974 |
+
$frame_language = substr($parsedFrame['data'], $frame_offset, 3);
|
975 |
+
$frame_offset += 3;
|
976 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
|
977 |
+
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
|
978 |
+
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
|
979 |
+
}
|
980 |
+
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
981 |
+
if (ord($frame_description) === 0) {
|
982 |
+
$frame_description = '';
|
983 |
+
}
|
984 |
+
$frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
|
985 |
+
|
986 |
+
$parsedFrame['encodingid'] = $frame_textencoding;
|
987 |
+
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
|
988 |
+
|
989 |
+
$parsedFrame['language'] = $frame_language;
|
990 |
+
$parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
|
991 |
+
$parsedFrame['description'] = $frame_description;
|
992 |
+
$parsedFrame['data'] = $frame_text;
|
993 |
+
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
|
994 |
+
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
|
995 |
+
}
|
996 |
+
|
997 |
+
}
|
998 |
+
|
999 |
+
} elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'RVA2')) { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
|
1000 |
+
// There may be more than one 'RVA2' frame in each tag,
|
1001 |
+
// but only one with the same identification string
|
1002 |
+
// <Header for 'Relative volume adjustment (2)', ID: 'RVA2'>
|
1003 |
+
// Identification <text string> $00
|
1004 |
+
// The 'identification' string is used to identify the situation and/or
|
1005 |
+
// device where this adjustment should apply. The following is then
|
1006 |
+
// repeated for every channel:
|
1007 |
+
// Type of channel $xx
|
1008 |
+
// Volume adjustment $xx xx
|
1009 |
+
// Bits representing peak $xx
|
1010 |
+
// Peak volume $xx (xx ...)
|
1011 |
+
|
1012 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00");
|
1013 |
+
$frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos);
|
1014 |
+
if (ord($frame_idstring) === 0) {
|
1015 |
+
$frame_idstring = '';
|
1016 |
+
}
|
1017 |
+
$frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00"));
|
1018 |
+
$parsedFrame['description'] = $frame_idstring;
|
1019 |
+
$RVA2channelcounter = 0;
|
1020 |
+
while (strlen($frame_remainingdata) >= 5) {
|
1021 |
+
$frame_offset = 0;
|
1022 |
+
$frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1));
|
1023 |
+
$parsedFrame[$RVA2channelcounter]['channeltypeid'] = $frame_channeltypeid;
|
1024 |
+
$parsedFrame[$RVA2channelcounter]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid);
|
1025 |
+
$parsedFrame[$RVA2channelcounter]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true); // 16-bit signed
|
1026 |
+
$frame_offset += 2;
|
1027 |
+
$parsedFrame[$RVA2channelcounter]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1));
|
1028 |
+
if (($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] < 1) || ($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] > 4)) {
|
1029 |
+
$info['warning'][] = 'ID3v2::RVA2 frame['.$RVA2channelcounter.'] contains invalid '.$parsedFrame[$RVA2channelcounter]['bitspeakvolume'].'-byte bits-representing-peak value';
|
1030 |
+
break;
|
1031 |
+
}
|
1032 |
+
$frame_bytespeakvolume = ceil($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] / 8);
|
1033 |
+
$parsedFrame[$RVA2channelcounter]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume));
|
1034 |
+
$frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume);
|
1035 |
+
$RVA2channelcounter++;
|
1036 |
+
}
|
1037 |
+
unset($parsedFrame['data']);
|
1038 |
+
|
1039 |
+
|
1040 |
+
} elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'RVAD')) || // 4.12 RVAD Relative volume adjustment (ID3v2.3 only)
|
1041 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'RVA'))) { // 4.12 RVA Relative volume adjustment (ID3v2.2 only)
|
1042 |
+
// There may only be one 'RVA' frame in each tag
|
1043 |
+
// <Header for 'Relative volume adjustment', ID: 'RVA'>
|
1044 |
+
// ID3v2.2 => Increment/decrement %000000ba
|
1045 |
+
// ID3v2.3 => Increment/decrement %00fedcba
|
1046 |
+
// Bits used for volume descr. $xx
|
1047 |
+
// Relative volume change, right $xx xx (xx ...) // a
|
1048 |
+
// Relative volume change, left $xx xx (xx ...) // b
|
1049 |
+
// Peak volume right $xx xx (xx ...)
|
1050 |
+
// Peak volume left $xx xx (xx ...)
|
1051 |
+
// ID3v2.3 only, optional (not present in ID3v2.2):
|
1052 |
+
// Relative volume change, right back $xx xx (xx ...) // c
|
1053 |
+
// Relative volume change, left back $xx xx (xx ...) // d
|
1054 |
+
// Peak volume right back $xx xx (xx ...)
|
1055 |
+
// Peak volume left back $xx xx (xx ...)
|
1056 |
+
// ID3v2.3 only, optional (not present in ID3v2.2):
|
1057 |
+
// Relative volume change, center $xx xx (xx ...) // e
|
1058 |
+
// Peak volume center $xx xx (xx ...)
|
1059 |
+
// ID3v2.3 only, optional (not present in ID3v2.2):
|
1060 |
+
// Relative volume change, bass $xx xx (xx ...) // f
|
1061 |
+
// Peak volume bass $xx xx (xx ...)
|
1062 |
+
|
1063 |
+
$frame_offset = 0;
|
1064 |
+
$frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
|
1065 |
+
$parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1);
|
1066 |
+
$parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1);
|
1067 |
+
$parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1068 |
+
$frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8);
|
1069 |
+
$parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
|
1070 |
+
if ($parsedFrame['incdec']['right'] === false) {
|
1071 |
+
$parsedFrame['volumechange']['right'] *= -1;
|
1072 |
+
}
|
1073 |
+
$frame_offset += $frame_bytesvolume;
|
1074 |
+
$parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
|
1075 |
+
if ($parsedFrame['incdec']['left'] === false) {
|
1076 |
+
$parsedFrame['volumechange']['left'] *= -1;
|
1077 |
+
}
|
1078 |
+
$frame_offset += $frame_bytesvolume;
|
1079 |
+
$parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
|
1080 |
+
$frame_offset += $frame_bytesvolume;
|
1081 |
+
$parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
|
1082 |
+
$frame_offset += $frame_bytesvolume;
|
1083 |
+
if ($id3v2_majorversion == 3) {
|
1084 |
+
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
|
1085 |
+
if (strlen($parsedFrame['data']) > 0) {
|
1086 |
+
$parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1);
|
1087 |
+
$parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1);
|
1088 |
+
$parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
|
1089 |
+
if ($parsedFrame['incdec']['rightrear'] === false) {
|
1090 |
+
$parsedFrame['volumechange']['rightrear'] *= -1;
|
1091 |
+
}
|
1092 |
+
$frame_offset += $frame_bytesvolume;
|
1093 |
+
$parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
|
1094 |
+
if ($parsedFrame['incdec']['leftrear'] === false) {
|
1095 |
+
$parsedFrame['volumechange']['leftrear'] *= -1;
|
1096 |
+
}
|
1097 |
+
$frame_offset += $frame_bytesvolume;
|
1098 |
+
$parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
|
1099 |
+
$frame_offset += $frame_bytesvolume;
|
1100 |
+
$parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
|
1101 |
+
$frame_offset += $frame_bytesvolume;
|
1102 |
+
}
|
1103 |
+
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
|
1104 |
+
if (strlen($parsedFrame['data']) > 0) {
|
1105 |
+
$parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1);
|
1106 |
+
$parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
|
1107 |
+
if ($parsedFrame['incdec']['center'] === false) {
|
1108 |
+
$parsedFrame['volumechange']['center'] *= -1;
|
1109 |
+
}
|
1110 |
+
$frame_offset += $frame_bytesvolume;
|
1111 |
+
$parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
|
1112 |
+
$frame_offset += $frame_bytesvolume;
|
1113 |
+
}
|
1114 |
+
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
|
1115 |
+
if (strlen($parsedFrame['data']) > 0) {
|
1116 |
+
$parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1);
|
1117 |
+
$parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
|
1118 |
+
if ($parsedFrame['incdec']['bass'] === false) {
|
1119 |
+
$parsedFrame['volumechange']['bass'] *= -1;
|
1120 |
+
}
|
1121 |
+
$frame_offset += $frame_bytesvolume;
|
1122 |
+
$parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
|
1123 |
+
$frame_offset += $frame_bytesvolume;
|
1124 |
+
}
|
1125 |
+
}
|
1126 |
+
unset($parsedFrame['data']);
|
1127 |
+
|
1128 |
+
|
1129 |
+
} elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'EQU2')) { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only)
|
1130 |
+
// There may be more than one 'EQU2' frame in each tag,
|
1131 |
+
// but only one with the same identification string
|
1132 |
+
// <Header of 'Equalisation (2)', ID: 'EQU2'>
|
1133 |
+
// Interpolation method $xx
|
1134 |
+
// $00 Band
|
1135 |
+
// $01 Linear
|
1136 |
+
// Identification <text string> $00
|
1137 |
+
// The following is then repeated for every adjustment point
|
1138 |
+
// Frequency $xx xx
|
1139 |
+
// Volume adjustment $xx xx
|
1140 |
+
|
1141 |
+
$frame_offset = 0;
|
1142 |
+
$frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1143 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1144 |
+
$frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1145 |
+
if (ord($frame_idstring) === 0) {
|
1146 |
+
$frame_idstring = '';
|
1147 |
+
}
|
1148 |
+
$parsedFrame['description'] = $frame_idstring;
|
1149 |
+
$frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00"));
|
1150 |
+
while (strlen($frame_remainingdata)) {
|
1151 |
+
$frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2;
|
1152 |
+
$parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true);
|
1153 |
+
$frame_remainingdata = substr($frame_remainingdata, 4);
|
1154 |
+
}
|
1155 |
+
$parsedFrame['interpolationmethod'] = $frame_interpolationmethod;
|
1156 |
+
unset($parsedFrame['data']);
|
1157 |
+
|
1158 |
+
|
1159 |
+
} elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'EQUA')) || // 4.12 EQUA Equalisation (ID3v2.3 only)
|
1160 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'EQU'))) { // 4.13 EQU Equalisation (ID3v2.2 only)
|
1161 |
+
// There may only be one 'EQUA' frame in each tag
|
1162 |
+
// <Header for 'Relative volume adjustment', ID: 'EQU'>
|
1163 |
+
// Adjustment bits $xx
|
1164 |
+
// This is followed by 2 bytes + ('adjustment bits' rounded up to the
|
1165 |
+
// nearest byte) for every equalisation band in the following format,
|
1166 |
+
// giving a frequency range of 0 - 32767Hz:
|
1167 |
+
// Increment/decrement %x (MSB of the Frequency)
|
1168 |
+
// Frequency (lower 15 bits)
|
1169 |
+
// Adjustment $xx (xx ...)
|
1170 |
+
|
1171 |
+
$frame_offset = 0;
|
1172 |
+
$parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1);
|
1173 |
+
$frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8);
|
1174 |
+
|
1175 |
+
$frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset);
|
1176 |
+
while (strlen($frame_remainingdata) > 0) {
|
1177 |
+
$frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2));
|
1178 |
+
$frame_incdec = (bool) substr($frame_frequencystr, 0, 1);
|
1179 |
+
$frame_frequency = bindec(substr($frame_frequencystr, 1, 15));
|
1180 |
+
$parsedFrame[$frame_frequency]['incdec'] = $frame_incdec;
|
1181 |
+
$parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes));
|
1182 |
+
if ($parsedFrame[$frame_frequency]['incdec'] === false) {
|
1183 |
+
$parsedFrame[$frame_frequency]['adjustment'] *= -1;
|
1184 |
+
}
|
1185 |
+
$frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes);
|
1186 |
+
}
|
1187 |
+
unset($parsedFrame['data']);
|
1188 |
+
|
1189 |
+
|
1190 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RVRB')) || // 4.13 RVRB Reverb
|
1191 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'REV'))) { // 4.14 REV Reverb
|
1192 |
+
// There may only be one 'RVRB' frame in each tag.
|
1193 |
+
// <Header for 'Reverb', ID: 'RVRB'>
|
1194 |
+
// Reverb left (ms) $xx xx
|
1195 |
+
// Reverb right (ms) $xx xx
|
1196 |
+
// Reverb bounces, left $xx
|
1197 |
+
// Reverb bounces, right $xx
|
1198 |
+
// Reverb feedback, left to left $xx
|
1199 |
+
// Reverb feedback, left to right $xx
|
1200 |
+
// Reverb feedback, right to right $xx
|
1201 |
+
// Reverb feedback, right to left $xx
|
1202 |
+
// Premix left to right $xx
|
1203 |
+
// Premix right to left $xx
|
1204 |
+
|
1205 |
+
$frame_offset = 0;
|
1206 |
+
$parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
|
1207 |
+
$frame_offset += 2;
|
1208 |
+
$parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
|
1209 |
+
$frame_offset += 2;
|
1210 |
+
$parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1211 |
+
$parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1212 |
+
$parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1213 |
+
$parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1214 |
+
$parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1215 |
+
$parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1216 |
+
$parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1217 |
+
$parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1218 |
+
unset($parsedFrame['data']);
|
1219 |
+
|
1220 |
+
|
1221 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'APIC')) || // 4.14 APIC Attached picture
|
1222 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'PIC'))) { // 4.15 PIC Attached picture
|
1223 |
+
// There may be several pictures attached to one file,
|
1224 |
+
// each in their individual 'APIC' frame, but only one
|
1225 |
+
// with the same content descriptor
|
1226 |
+
// <Header for 'Attached picture', ID: 'APIC'>
|
1227 |
+
// Text encoding $xx
|
1228 |
+
// ID3v2.3+ => MIME type <text string> $00
|
1229 |
+
// ID3v2.2 => Image format $xx xx xx
|
1230 |
+
// Picture type $xx
|
1231 |
+
// Description <text string according to encoding> $00 (00)
|
1232 |
+
// Picture data <binary data>
|
1233 |
+
|
1234 |
+
$frame_offset = 0;
|
1235 |
+
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1236 |
+
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
|
1237 |
+
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
|
1238 |
+
}
|
1239 |
+
|
1240 |
+
if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) {
|
1241 |
+
$frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3);
|
1242 |
+
if (strtolower($frame_imagetype) == 'ima') {
|
1243 |
+
// complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted
|
1244 |
+
// MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoff�pacbell*net)
|
1245 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1246 |
+
$frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1247 |
+
if (ord($frame_mimetype) === 0) {
|
1248 |
+
$frame_mimetype = '';
|
1249 |
+
}
|
1250 |
+
$frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype)));
|
1251 |
+
if ($frame_imagetype == 'JPEG') {
|
1252 |
+
$frame_imagetype = 'JPG';
|
1253 |
+
}
|
1254 |
+
$frame_offset = $frame_terminatorpos + strlen("\x00");
|
1255 |
+
} else {
|
1256 |
+
$frame_offset += 3;
|
1257 |
+
}
|
1258 |
+
}
|
1259 |
+
if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) {
|
1260 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1261 |
+
$frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1262 |
+
if (ord($frame_mimetype) === 0) {
|
1263 |
+
$frame_mimetype = '';
|
1264 |
+
}
|
1265 |
+
$frame_offset = $frame_terminatorpos + strlen("\x00");
|
1266 |
+
}
|
1267 |
+
|
1268 |
+
$frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1269 |
+
|
1270 |
+
if ($frame_offset >= $parsedFrame['datalength']) {
|
1271 |
+
$info['warning'][] = 'data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset);
|
1272 |
+
} else {
|
1273 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
|
1274 |
+
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
|
1275 |
+
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
|
1276 |
+
}
|
1277 |
+
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1278 |
+
if (ord($frame_description) === 0) {
|
1279 |
+
$frame_description = '';
|
1280 |
+
}
|
1281 |
+
$parsedFrame['encodingid'] = $frame_textencoding;
|
1282 |
+
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
|
1283 |
+
|
1284 |
+
if ($id3v2_majorversion == 2) {
|
1285 |
+
$parsedFrame['imagetype'] = $frame_imagetype;
|
1286 |
+
} else {
|
1287 |
+
$parsedFrame['mime'] = $frame_mimetype;
|
1288 |
+
}
|
1289 |
+
$parsedFrame['picturetypeid'] = $frame_picturetype;
|
1290 |
+
$parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
|
1291 |
+
$parsedFrame['description'] = $frame_description;
|
1292 |
+
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
|
1293 |
+
$parsedFrame['datalength'] = strlen($parsedFrame['data']);
|
1294 |
+
|
1295 |
+
$parsedFrame['image_mime'] = '';
|
1296 |
+
$imageinfo = array();
|
1297 |
+
$imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo);
|
1298 |
+
if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) {
|
1299 |
+
$parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]);
|
1300 |
+
if ($imagechunkcheck[0]) {
|
1301 |
+
$parsedFrame['image_width'] = $imagechunkcheck[0];
|
1302 |
+
}
|
1303 |
+
if ($imagechunkcheck[1]) {
|
1304 |
+
$parsedFrame['image_height'] = $imagechunkcheck[1];
|
1305 |
+
}
|
1306 |
+
}
|
1307 |
+
|
1308 |
+
do {
|
1309 |
+
if ($this->inline_attachments === false) {
|
1310 |
+
// skip entirely
|
1311 |
+
unset($parsedFrame['data']);
|
1312 |
+
break;
|
1313 |
+
}
|
1314 |
+
if ($this->inline_attachments === true) {
|
1315 |
+
// great
|
1316 |
+
} elseif (is_int($this->inline_attachments)) {
|
1317 |
+
if ($this->inline_attachments < $parsedFrame['data_length']) {
|
1318 |
+
// too big, skip
|
1319 |
+
$info['warning'][] = 'attachment at '.$frame_offset.' is too large to process inline ('.number_format($parsedFrame['data_length']).' bytes)';
|
1320 |
+
unset($parsedFrame['data']);
|
1321 |
+
break;
|
1322 |
+
}
|
1323 |
+
} elseif (is_string($this->inline_attachments)) {
|
1324 |
+
$this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR);
|
1325 |
+
if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) {
|
1326 |
+
// cannot write, skip
|
1327 |
+
$info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$this->inline_attachments.'" (not writable)';
|
1328 |
+
unset($parsedFrame['data']);
|
1329 |
+
break;
|
1330 |
+
}
|
1331 |
+
}
|
1332 |
+
// if we get this far, must be OK
|
1333 |
+
if (is_string($this->inline_attachments)) {
|
1334 |
+
$destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$frame_offset;
|
1335 |
+
if (!file_exists($destination_filename) || is_writable($destination_filename)) {
|
1336 |
+
file_put_contents($destination_filename, $parsedFrame['data']);
|
1337 |
+
} else {
|
1338 |
+
$info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$destination_filename.'" (not writable)';
|
1339 |
+
}
|
1340 |
+
$parsedFrame['data_filename'] = $destination_filename;
|
1341 |
+
unset($parsedFrame['data']);
|
1342 |
+
} else {
|
1343 |
+
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
|
1344 |
+
if (!isset($info['id3v2']['comments']['picture'])) {
|
1345 |
+
$info['id3v2']['comments']['picture'] = array();
|
1346 |
+
}
|
1347 |
+
$info['id3v2']['comments']['picture'][] = array('data'=>$parsedFrame['data'], 'image_mime'=>$parsedFrame['image_mime']);
|
1348 |
+
}
|
1349 |
+
}
|
1350 |
+
} while (false);
|
1351 |
+
}
|
1352 |
+
|
1353 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GEOB')) || // 4.15 GEOB General encapsulated object
|
1354 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'GEO'))) { // 4.16 GEO General encapsulated object
|
1355 |
+
// There may be more than one 'GEOB' frame in each tag,
|
1356 |
+
// but only one with the same content descriptor
|
1357 |
+
// <Header for 'General encapsulated object', ID: 'GEOB'>
|
1358 |
+
// Text encoding $xx
|
1359 |
+
// MIME type <text string> $00
|
1360 |
+
// Filename <text string according to encoding> $00 (00)
|
1361 |
+
// Content description <text string according to encoding> $00 (00)
|
1362 |
+
// Encapsulated object <binary data>
|
1363 |
+
|
1364 |
+
$frame_offset = 0;
|
1365 |
+
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1366 |
+
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
|
1367 |
+
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
|
1368 |
+
}
|
1369 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1370 |
+
$frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1371 |
+
if (ord($frame_mimetype) === 0) {
|
1372 |
+
$frame_mimetype = '';
|
1373 |
+
}
|
1374 |
+
$frame_offset = $frame_terminatorpos + strlen("\x00");
|
1375 |
+
|
1376 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
|
1377 |
+
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
|
1378 |
+
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
|
1379 |
+
}
|
1380 |
+
$frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1381 |
+
if (ord($frame_filename) === 0) {
|
1382 |
+
$frame_filename = '';
|
1383 |
+
}
|
1384 |
+
$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
|
1385 |
+
|
1386 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
|
1387 |
+
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
|
1388 |
+
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
|
1389 |
+
}
|
1390 |
+
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1391 |
+
if (ord($frame_description) === 0) {
|
1392 |
+
$frame_description = '';
|
1393 |
+
}
|
1394 |
+
$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
|
1395 |
+
|
1396 |
+
$parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset);
|
1397 |
+
$parsedFrame['encodingid'] = $frame_textencoding;
|
1398 |
+
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
|
1399 |
+
|
1400 |
+
$parsedFrame['mime'] = $frame_mimetype;
|
1401 |
+
$parsedFrame['filename'] = $frame_filename;
|
1402 |
+
$parsedFrame['description'] = $frame_description;
|
1403 |
+
unset($parsedFrame['data']);
|
1404 |
+
|
1405 |
+
|
1406 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PCNT')) || // 4.16 PCNT Play counter
|
1407 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CNT'))) { // 4.17 CNT Play counter
|
1408 |
+
// There may only be one 'PCNT' frame in each tag.
|
1409 |
+
// When the counter reaches all one's, one byte is inserted in
|
1410 |
+
// front of the counter thus making the counter eight bits bigger
|
1411 |
+
// <Header for 'Play counter', ID: 'PCNT'>
|
1412 |
+
// Counter $xx xx xx xx (xx ...)
|
1413 |
+
|
1414 |
+
$parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']);
|
1415 |
+
|
1416 |
+
|
1417 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POPM')) || // 4.17 POPM Popularimeter
|
1418 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'POP'))) { // 4.18 POP Popularimeter
|
1419 |
+
// There may be more than one 'POPM' frame in each tag,
|
1420 |
+
// but only one with the same email address
|
1421 |
+
// <Header for 'Popularimeter', ID: 'POPM'>
|
1422 |
+
// Email to user <text string> $00
|
1423 |
+
// Rating $xx
|
1424 |
+
// Counter $xx xx xx xx (xx ...)
|
1425 |
+
|
1426 |
+
$frame_offset = 0;
|
1427 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1428 |
+
$frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1429 |
+
if (ord($frame_emailaddress) === 0) {
|
1430 |
+
$frame_emailaddress = '';
|
1431 |
+
}
|
1432 |
+
$frame_offset = $frame_terminatorpos + strlen("\x00");
|
1433 |
+
$frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1434 |
+
$parsedFrame['counter'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
|
1435 |
+
$parsedFrame['email'] = $frame_emailaddress;
|
1436 |
+
$parsedFrame['rating'] = $frame_rating;
|
1437 |
+
unset($parsedFrame['data']);
|
1438 |
+
|
1439 |
+
|
1440 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RBUF')) || // 4.18 RBUF Recommended buffer size
|
1441 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'BUF'))) { // 4.19 BUF Recommended buffer size
|
1442 |
+
// There may only be one 'RBUF' frame in each tag
|
1443 |
+
// <Header for 'Recommended buffer size', ID: 'RBUF'>
|
1444 |
+
// Buffer size $xx xx xx
|
1445 |
+
// Embedded info flag %0000000x
|
1446 |
+
// Offset to next tag $xx xx xx xx
|
1447 |
+
|
1448 |
+
$frame_offset = 0;
|
1449 |
+
$parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3));
|
1450 |
+
$frame_offset += 3;
|
1451 |
+
|
1452 |
+
$frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
|
1453 |
+
$parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1);
|
1454 |
+
$parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
|
1455 |
+
unset($parsedFrame['data']);
|
1456 |
+
|
1457 |
+
|
1458 |
+
} elseif (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRM')) { // 4.20 Encrypted meta frame (ID3v2.2 only)
|
1459 |
+
// There may be more than one 'CRM' frame in a tag,
|
1460 |
+
// but only one with the same 'owner identifier'
|
1461 |
+
// <Header for 'Encrypted meta frame', ID: 'CRM'>
|
1462 |
+
// Owner identifier <textstring> $00 (00)
|
1463 |
+
// Content/explanation <textstring> $00 (00)
|
1464 |
+
// Encrypted datablock <binary data>
|
1465 |
+
|
1466 |
+
$frame_offset = 0;
|
1467 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1468 |
+
$frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1469 |
+
$frame_offset = $frame_terminatorpos + strlen("\x00");
|
1470 |
+
|
1471 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1472 |
+
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1473 |
+
if (ord($frame_description) === 0) {
|
1474 |
+
$frame_description = '';
|
1475 |
+
}
|
1476 |
+
$frame_offset = $frame_terminatorpos + strlen("\x00");
|
1477 |
+
|
1478 |
+
$parsedFrame['ownerid'] = $frame_ownerid;
|
1479 |
+
$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
|
1480 |
+
$parsedFrame['description'] = $frame_description;
|
1481 |
+
unset($parsedFrame['data']);
|
1482 |
+
|
1483 |
+
|
1484 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'AENC')) || // 4.19 AENC Audio encryption
|
1485 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRA'))) { // 4.21 CRA Audio encryption
|
1486 |
+
// There may be more than one 'AENC' frames in a tag,
|
1487 |
+
// but only one with the same 'Owner identifier'
|
1488 |
+
// <Header for 'Audio encryption', ID: 'AENC'>
|
1489 |
+
// Owner identifier <text string> $00
|
1490 |
+
// Preview start $xx xx
|
1491 |
+
// Preview length $xx xx
|
1492 |
+
// Encryption info <binary data>
|
1493 |
+
|
1494 |
+
$frame_offset = 0;
|
1495 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1496 |
+
$frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1497 |
+
if (ord($frame_ownerid) === 0) {
|
1498 |
+
$frame_ownerid == '';
|
1499 |
+
}
|
1500 |
+
$frame_offset = $frame_terminatorpos + strlen("\x00");
|
1501 |
+
$parsedFrame['ownerid'] = $frame_ownerid;
|
1502 |
+
$parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
|
1503 |
+
$frame_offset += 2;
|
1504 |
+
$parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
|
1505 |
+
$frame_offset += 2;
|
1506 |
+
$parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset);
|
1507 |
+
unset($parsedFrame['data']);
|
1508 |
+
|
1509 |
+
|
1510 |
+
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information
|
1511 |
+
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information
|
1512 |
+
// There may be more than one 'LINK' frame in a tag,
|
1513 |
+
// but only one with the same contents
|
1514 |
+
// <Header for 'Linked information', ID: 'LINK'>
|
1515 |
+
// ID3v2.3+ => Frame identifier $xx xx xx xx
|
1516 |
+
// ID3v2.2 => Frame identifier $xx xx xx
|
1517 |
+
// URL <text string> $00
|
1518 |
+
// ID and additional data <text string(s)>
|
1519 |
+
|
1520 |
+
$frame_offset = 0;
|
1521 |
+
if ($id3v2_majorversion == 2) {
|
1522 |
+
$parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3);
|
1523 |
+
$frame_offset += 3;
|
1524 |
+
} else {
|
1525 |
+
$parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4);
|
1526 |
+
$frame_offset += 4;
|
1527 |
+
}
|
1528 |
+
|
1529 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1530 |
+
$frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1531 |
+
if (ord($frame_url) === 0) {
|
1532 |
+
$frame_url = '';
|
1533 |
+
}
|
1534 |
+
$frame_offset = $frame_terminatorpos + strlen("\x00");
|
1535 |
+
$parsedFrame['url'] = $frame_url;
|
1536 |
+
|
1537 |
+
$parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset);
|
1538 |
+
if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
|
1539 |
+
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = utf8_encode($parsedFrame['url']);
|
1540 |
+
}
|
1541 |
+
unset($parsedFrame['data']);
|
1542 |
+
|
1543 |
+
|
1544 |
+
} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POSS')) { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only)
|
1545 |
+
// There may only be one 'POSS' frame in each tag
|
1546 |
+
// <Head for 'Position synchronisation', ID: 'POSS'>
|
1547 |
+
// Time stamp format $xx
|
1548 |
+
// Position $xx (xx ...)
|
1549 |
+
|
1550 |
+
$frame_offset = 0;
|
1551 |
+
$parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1552 |
+
$parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
|
1553 |
+
unset($parsedFrame['data']);
|
1554 |
+
|
1555 |
+
|
1556 |
+
} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USER')) { // 4.22 USER Terms of use (ID3v2.3+ only)
|
1557 |
+
// There may be more than one 'Terms of use' frame in a tag,
|
1558 |
+
// but only one with the same 'Language'
|
1559 |
+
// <Header for 'Terms of use frame', ID: 'USER'>
|
1560 |
+
// Text encoding $xx
|
1561 |
+
// Language $xx xx xx
|
1562 |
+
// The actual text <text string according to encoding>
|
1563 |
+
|
1564 |
+
$frame_offset = 0;
|
1565 |
+
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1566 |
+
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
|
1567 |
+
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
|
1568 |
+
}
|
1569 |
+
$frame_language = substr($parsedFrame['data'], $frame_offset, 3);
|
1570 |
+
$frame_offset += 3;
|
1571 |
+
$parsedFrame['language'] = $frame_language;
|
1572 |
+
$parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
|
1573 |
+
$parsedFrame['encodingid'] = $frame_textencoding;
|
1574 |
+
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
|
1575 |
+
|
1576 |
+
$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
|
1577 |
+
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
|
1578 |
+
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
|
1579 |
+
}
|
1580 |
+
unset($parsedFrame['data']);
|
1581 |
+
|
1582 |
+
|
1583 |
+
} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'OWNE')) { // 4.23 OWNE Ownership frame (ID3v2.3+ only)
|
1584 |
+
// There may only be one 'OWNE' frame in a tag
|
1585 |
+
// <Header for 'Ownership frame', ID: 'OWNE'>
|
1586 |
+
// Text encoding $xx
|
1587 |
+
// Price paid <text string> $00
|
1588 |
+
// Date of purch. <text string>
|
1589 |
+
// Seller <text string according to encoding>
|
1590 |
+
|
1591 |
+
$frame_offset = 0;
|
1592 |
+
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1593 |
+
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
|
1594 |
+
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
|
1595 |
+
}
|
1596 |
+
$parsedFrame['encodingid'] = $frame_textencoding;
|
1597 |
+
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
|
1598 |
+
|
1599 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1600 |
+
$frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1601 |
+
$frame_offset = $frame_terminatorpos + strlen("\x00");
|
1602 |
+
|
1603 |
+
$parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3);
|
1604 |
+
$parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']);
|
1605 |
+
$parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3);
|
1606 |
+
|
1607 |
+
$parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8);
|
1608 |
+
if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) {
|
1609 |
+
$parsedFrame['purchasedateunix'] = mktime (0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4));
|
1610 |
+
}
|
1611 |
+
$frame_offset += 8;
|
1612 |
+
|
1613 |
+
$parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
|
1614 |
+
unset($parsedFrame['data']);
|
1615 |
+
|
1616 |
+
|
1617 |
+
} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMR')) { // 4.24 COMR Commercial frame (ID3v2.3+ only)
|
1618 |
+
// There may be more than one 'commercial frame' in a tag,
|
1619 |
+
// but no two may be identical
|
1620 |
+
// <Header for 'Commercial frame', ID: 'COMR'>
|
1621 |
+
// Text encoding $xx
|
1622 |
+
// Price string <text string> $00
|
1623 |
+
// Valid until <text string>
|
1624 |
+
// Contact URL <text string> $00
|
1625 |
+
// Received as $xx
|
1626 |
+
// Name of seller <text string according to encoding> $00 (00)
|
1627 |
+
// Description <text string according to encoding> $00 (00)
|
1628 |
+
// Picture MIME type <string> $00
|
1629 |
+
// Seller logo <binary data>
|
1630 |
+
|
1631 |
+
$frame_offset = 0;
|
1632 |
+
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1633 |
+
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
|
1634 |
+
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
|
1635 |
+
}
|
1636 |
+
|
1637 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1638 |
+
$frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1639 |
+
$frame_offset = $frame_terminatorpos + strlen("\x00");
|
1640 |
+
$frame_rawpricearray = explode('/', $frame_pricestring);
|
1641 |
+
foreach ($frame_rawpricearray as $key => $val) {
|
1642 |
+
$frame_currencyid = substr($val, 0, 3);
|
1643 |
+
$parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid);
|
1644 |
+
$parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3);
|
1645 |
+
}
|
1646 |
+
|
1647 |
+
$frame_datestring = substr($parsedFrame['data'], $frame_offset, 8);
|
1648 |
+
$frame_offset += 8;
|
1649 |
+
|
1650 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1651 |
+
$frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1652 |
+
$frame_offset = $frame_terminatorpos + strlen("\x00");
|
1653 |
+
|
1654 |
+
$frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1655 |
+
|
1656 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
|
1657 |
+
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
|
1658 |
+
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
|
1659 |
+
}
|
1660 |
+
$frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1661 |
+
if (ord($frame_sellername) === 0) {
|
1662 |
+
$frame_sellername = '';
|
1663 |
+
}
|
1664 |
+
$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
|
1665 |
+
|
1666 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
|
1667 |
+
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
|
1668 |
+
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
|
1669 |
+
}
|
1670 |
+
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1671 |
+
if (ord($frame_description) === 0) {
|
1672 |
+
$frame_description = '';
|
1673 |
+
}
|
1674 |
+
$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
|
1675 |
+
|
1676 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1677 |
+
$frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1678 |
+
$frame_offset = $frame_terminatorpos + strlen("\x00");
|
1679 |
+
|
1680 |
+
$frame_sellerlogo = substr($parsedFrame['data'], $frame_offset);
|
1681 |
+
|
1682 |
+
$parsedFrame['encodingid'] = $frame_textencoding;
|
1683 |
+
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
|
1684 |
+
|
1685 |
+
$parsedFrame['pricevaliduntil'] = $frame_datestring;
|
1686 |
+
$parsedFrame['contacturl'] = $frame_contacturl;
|
1687 |
+
$parsedFrame['receivedasid'] = $frame_receivedasid;
|
1688 |
+
$parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid);
|
1689 |
+
$parsedFrame['sellername'] = $frame_sellername;
|
1690 |
+
$parsedFrame['description'] = $frame_description;
|
1691 |
+
$parsedFrame['mime'] = $frame_mimetype;
|
1692 |
+
$parsedFrame['logo'] = $frame_sellerlogo;
|
1693 |
+
unset($parsedFrame['data']);
|
1694 |
+
|
1695 |
+
|
1696 |
+
} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ENCR')) { // 4.25 ENCR Encryption method registration (ID3v2.3+ only)
|
1697 |
+
// There may be several 'ENCR' frames in a tag,
|
1698 |
+
// but only one containing the same symbol
|
1699 |
+
// and only one containing the same owner identifier
|
1700 |
+
// <Header for 'Encryption method registration', ID: 'ENCR'>
|
1701 |
+
// Owner identifier <text string> $00
|
1702 |
+
// Method symbol $xx
|
1703 |
+
// Encryption data <binary data>
|
1704 |
+
|
1705 |
+
$frame_offset = 0;
|
1706 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1707 |
+
$frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1708 |
+
if (ord($frame_ownerid) === 0) {
|
1709 |
+
$frame_ownerid = '';
|
1710 |
+
}
|
1711 |
+
$frame_offset = $frame_terminatorpos + strlen("\x00");
|
1712 |
+
|
1713 |
+
$parsedFrame['ownerid'] = $frame_ownerid;
|
1714 |
+
$parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1715 |
+
$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
|
1716 |
+
|
1717 |
+
|
1718 |
+
} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GRID')) { // 4.26 GRID Group identification registration (ID3v2.3+ only)
|
1719 |
+
|
1720 |
+
// There may be several 'GRID' frames in a tag,
|
1721 |
+
// but only one containing the same symbol
|
1722 |
+
// and only one containing the same owner identifier
|
1723 |
+
// <Header for 'Group ID registration', ID: 'GRID'>
|
1724 |
+
// Owner identifier <text string> $00
|
1725 |
+
// Group symbol $xx
|
1726 |
+
// Group dependent data <binary data>
|
1727 |
+
|
1728 |
+
$frame_offset = 0;
|
1729 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1730 |
+
$frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1731 |
+
if (ord($frame_ownerid) === 0) {
|
1732 |
+
$frame_ownerid = '';
|
1733 |
+
}
|
1734 |
+
$frame_offset = $frame_terminatorpos + strlen("\x00");
|
1735 |
+
|
1736 |
+
$parsedFrame['ownerid'] = $frame_ownerid;
|
1737 |
+
$parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1738 |
+
$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
|
1739 |
+
|
1740 |
+
|
1741 |
+
} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PRIV')) { // 4.27 PRIV Private frame (ID3v2.3+ only)
|
1742 |
+
// The tag may contain more than one 'PRIV' frame
|
1743 |
+
// but only with different contents
|
1744 |
+
// <Header for 'Private frame', ID: 'PRIV'>
|
1745 |
+
// Owner identifier <text string> $00
|
1746 |
+
// The private data <binary data>
|
1747 |
+
|
1748 |
+
$frame_offset = 0;
|
1749 |
+
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
|
1750 |
+
$frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
|
1751 |
+
if (ord($frame_ownerid) === 0) {
|
1752 |
+
$frame_ownerid = '';
|
1753 |
+
}
|
1754 |
+
$frame_offset = $frame_terminatorpos + strlen("\x00");
|
1755 |
+
|
1756 |
+
$parsedFrame['ownerid'] = $frame_ownerid;
|
1757 |
+
$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
|
1758 |
+
|
1759 |
+
|
1760 |
+
} elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SIGN')) { // 4.28 SIGN Signature frame (ID3v2.4+ only)
|
1761 |
+
// There may be more than one 'signature frame' in a tag,
|
1762 |
+
// but no two may be identical
|
1763 |
+
// <Header for 'Signature frame', ID: 'SIGN'>
|
1764 |
+
// Group symbol $xx
|
1765 |
+
// Signature <binary data>
|
1766 |
+
|
1767 |
+
$frame_offset = 0;
|
1768 |
+
$parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1769 |
+
$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
|
1770 |
+
|
1771 |
+
|
1772 |
+
} elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SEEK')) { // 4.29 SEEK Seek frame (ID3v2.4+ only)
|
1773 |
+
// There may only be one 'seek frame' in a tag
|
1774 |
+
// <Header for 'Seek frame', ID: 'SEEK'>
|
1775 |
+
// Minimum offset to next tag $xx xx xx xx
|
1776 |
+
|
1777 |
+
$frame_offset = 0;
|
1778 |
+
$parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
|
1779 |
+
|
1780 |
+
|
1781 |
+
} elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'ASPI')) { // 4.30 ASPI Audio seek point index (ID3v2.4+ only)
|
1782 |
+
// There may only be one 'audio seek point index' frame in a tag
|
1783 |
+
// <Header for 'Seek Point Index', ID: 'ASPI'>
|
1784 |
+
// Indexed data start (S) $xx xx xx xx
|
1785 |
+
// Indexed data length (L) $xx xx xx xx
|
1786 |
+
// Number of index points (N) $xx xx
|
1787 |
+
// Bits per index point (b) $xx
|
1788 |
+
// Then for every index point the following data is included:
|
1789 |
+
// Fraction at index (Fi) $xx (xx)
|
1790 |
+
|
1791 |
+
$frame_offset = 0;
|
1792 |
+
$parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
|
1793 |
+
$frame_offset += 4;
|
1794 |
+
$parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
|
1795 |
+
$frame_offset += 4;
|
1796 |
+
$parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
|
1797 |
+
$frame_offset += 2;
|
1798 |
+
$parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
|
1799 |
+
$frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8);
|
1800 |
+
for ($i = 0; $i < $frame_indexpoints; $i++) {
|
1801 |
+
$parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint));
|
1802 |
+
$frame_offset += $frame_bytesperpoint;
|
1803 |
+
}
|
1804 |
+
unset($parsedFrame['data']);
|
1805 |
+
|
1806 |
+
} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RGAD')) { // Replay Gain Adjustment
|
1807 |
+
// http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
|
1808 |
+
// There may only be one 'RGAD' frame in a tag
|
1809 |
+
// <Header for 'Replay Gain Adjustment', ID: 'RGAD'>
|
1810 |
+
// Peak Amplitude $xx $xx $xx $xx
|
1811 |
+
// Radio Replay Gain Adjustment %aaabbbcd %dddddddd
|
1812 |
+
// Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd
|
1813 |
+
// a - name code
|
1814 |
+
// b - originator code
|
1815 |
+
// c - sign bit
|
1816 |
+
// d - replay gain adjustment
|
1817 |
+
|
1818 |
+
$frame_offset = 0;
|
1819 |
+
$parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4));
|
1820 |
+
$frame_offset += 4;
|
1821 |
+
$rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
|
1822 |
+
$frame_offset += 2;
|
1823 |
+
$rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
|
1824 |
+
$frame_offset += 2;
|
1825 |
+
$parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3));
|
1826 |
+
$parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3));
|
1827 |
+
$parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1));
|
1828 |
+
$parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9));
|
1829 |
+
$parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3));
|
1830 |
+
$parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3));
|
1831 |
+
$parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1));
|
1832 |
+
$parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9));
|
1833 |
+
$parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']);
|
1834 |
+
$parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']);
|
1835 |
+
$parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']);
|
1836 |
+
$parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']);
|
1837 |
+
$parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']);
|
1838 |
+
$parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']);
|
1839 |
+
|
1840 |
+
$info['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude'];
|
1841 |
+
$info['replay_gain']['track']['originator'] = $parsedFrame['track']['originator'];
|
1842 |
+
$info['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment'];
|
1843 |
+
$info['replay_gain']['album']['originator'] = $parsedFrame['album']['originator'];
|
1844 |
+
$info['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment'];
|
1845 |
+
|
1846 |
+
unset($parsedFrame['data']);
|
1847 |
+
|
1848 |
+
}
|
1849 |
+
|
1850 |
+
return true;
|
1851 |
+
}
|
1852 |
+
|
1853 |
+
|
1854 |
+
function DeUnsynchronise($data) {
|
1855 |
+
return str_replace("\xFF\x00", "\xFF", $data);
|
1856 |
+
}
|
1857 |
+
|
1858 |
+
function LookupExtendedHeaderRestrictionsTagSizeLimits($index) {
|
1859 |
+
static $LookupExtendedHeaderRestrictionsTagSizeLimits = array(
|
1860 |
+
0x00 => 'No more than 128 frames and 1 MB total tag size',
|
1861 |
+
0x01 => 'No more than 64 frames and 128 KB total tag size',
|
1862 |
+
0x02 => 'No more than 32 frames and 40 KB total tag size',
|
1863 |
+
0x03 => 'No more than 32 frames and 4 KB total tag size',
|
1864 |
+
);
|
1865 |
+
return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : '');
|
1866 |
+
}
|
1867 |
+
|
1868 |
+
function LookupExtendedHeaderRestrictionsTextEncodings($index) {
|
1869 |
+
static $LookupExtendedHeaderRestrictionsTextEncodings = array(
|
1870 |
+
0x00 => 'No restrictions',
|
1871 |
+
0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8',
|
1872 |
+
);
|
1873 |
+
return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : '');
|
1874 |
+
}
|
1875 |
+
|
1876 |
+
function LookupExtendedHeaderRestrictionsTextFieldSize($index) {
|
1877 |
+
static $LookupExtendedHeaderRestrictionsTextFieldSize = array(
|
1878 |
+
0x00 => 'No restrictions',
|
1879 |
+
0x01 => 'No string is longer than 1024 characters',
|
1880 |
+
0x02 => 'No string is longer than 128 characters',
|
1881 |
+
0x03 => 'No string is longer than 30 characters',
|
1882 |
+
);
|
1883 |
+
return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : '');
|
1884 |
+
}
|
1885 |
+
|
1886 |
+
function LookupExtendedHeaderRestrictionsImageEncoding($index) {
|
1887 |
+
static $LookupExtendedHeaderRestrictionsImageEncoding = array(
|
1888 |
+
0x00 => 'No restrictions',
|
1889 |
+
0x01 => 'Images are encoded only with PNG or JPEG',
|
1890 |
+
);
|
1891 |
+
return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : '');
|
1892 |
+
}
|
1893 |
+
|
1894 |
+
function LookupExtendedHeaderRestrictionsImageSizeSize($index) {
|
1895 |
+
static $LookupExtendedHeaderRestrictionsImageSizeSize = array(
|
1896 |
+
0x00 => 'No restrictions',
|
1897 |
+
0x01 => 'All images are 256x256 pixels or smaller',
|
1898 |
+
0x02 => 'All images are 64x64 pixels or smaller',
|
1899 |
+
0x03 => 'All images are exactly 64x64 pixels, unless required otherwise',
|
1900 |
+
);
|
1901 |
+
return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : '');
|
1902 |
+
}
|
1903 |
+
|
1904 |
+
function LookupCurrencyUnits($currencyid) {
|
1905 |
+
|
1906 |
+
$begin = __LINE__;
|
1907 |
+
|
1908 |
+
/** This is not a comment!
|
1909 |
+
|
1910 |
+
|
1911 |
+
AED Dirhams
|
1912 |
+
AFA Afghanis
|
1913 |
+
ALL Leke
|
1914 |
+
AMD Drams
|
1915 |
+
ANG Guilders
|
1916 |
+
AOA Kwanza
|
1917 |
+
ARS Pesos
|
1918 |
+
ATS Schillings
|
1919 |
+
AUD Dollars
|
1920 |
+
AWG Guilders
|
1921 |
+
AZM Manats
|
1922 |
+
BAM Convertible Marka
|
1923 |
+
BBD Dollars
|
1924 |
+
BDT Taka
|
1925 |
+
BEF Francs
|
1926 |
+
BGL Leva
|
1927 |
+
BHD Dinars
|
1928 |
+
BIF Francs
|
1929 |
+
BMD Dollars
|
1930 |
+
BND Dollars
|
1931 |
+
BOB Bolivianos
|
1932 |
+
BRL Brazil Real
|
1933 |
+
BSD Dollars
|
1934 |
+
BTN Ngultrum
|
1935 |
+
BWP Pulas
|
1936 |
+
BYR Rubles
|
1937 |
+
BZD Dollars
|
1938 |
+
CAD Dollars
|
1939 |
+
CDF Congolese Francs
|
1940 |
+
CHF Francs
|
1941 |
+
CLP Pesos
|
1942 |
+
CNY Yuan Renminbi
|
1943 |
+
COP Pesos
|
1944 |
+
CRC Colones
|
1945 |
+
CUP Pesos
|
1946 |
+
CVE Escudos
|
1947 |
+
CYP Pounds
|
1948 |
+
CZK Koruny
|
1949 |
+
DEM Deutsche Marks
|
1950 |
+
DJF Francs
|
1951 |
+
DKK Kroner
|
1952 |
+
DOP Pesos
|
1953 |
+
DZD Algeria Dinars
|
1954 |
+
EEK Krooni
|
1955 |
+
EGP Pounds
|
1956 |
+
ERN Nakfa
|
1957 |
+
ESP Pesetas
|
1958 |
+
ETB Birr
|
1959 |
+
EUR Euro
|
1960 |
+
FIM Markkaa
|
1961 |
+
FJD Dollars
|
1962 |
+
FKP Pounds
|
1963 |
+
FRF Francs
|
1964 |
+
GBP Pounds
|
1965 |
+
GEL Lari
|
1966 |
+
GGP Pounds
|
1967 |
+
GHC Cedis
|
1968 |
+
GIP Pounds
|
1969 |
+
GMD Dalasi
|
1970 |
+
GNF Francs
|
1971 |
+
GRD Drachmae
|
1972 |
+
GTQ Quetzales
|
1973 |
+
GYD Dollars
|
1974 |
+
HKD Dollars
|
1975 |
+
HNL Lempiras
|
1976 |
+
HRK Kuna
|
1977 |
+
HTG Gourdes
|
1978 |
+
HUF Forints
|
1979 |
+
IDR Rupiahs
|
1980 |
+
IEP Pounds
|
1981 |
+
ILS New Shekels
|
1982 |
+
IMP Pounds
|
1983 |
+
INR Rupees
|
1984 |
+
IQD Dinars
|
1985 |
+
IRR Rials
|
1986 |
+
ISK Kronur
|
1987 |
+
ITL Lire
|
1988 |
+
JEP Pounds
|
1989 |
+
JMD Dollars
|
1990 |
+
JOD Dinars
|
1991 |
+
JPY Yen
|
1992 |
+
KES Shillings
|
1993 |
+
KGS Soms
|
1994 |
+
KHR Riels
|
1995 |
+
KMF Francs
|
1996 |
+
KPW Won
|
1997 |
+
KWD Dinars
|
1998 |
+
KYD Dollars
|
1999 |
+
KZT Tenge
|
2000 |
+
LAK Kips
|
2001 |
+
LBP Pounds
|
2002 |
+
LKR Rupees
|
2003 |
+
LRD Dollars
|
2004 |
+
LSL Maloti
|
2005 |
+
LTL Litai
|
2006 |
+
LUF Francs
|
2007 |
+
LVL Lati
|
2008 |
+
LYD Dinars
|
2009 |
+
MAD Dirhams
|
2010 |
+
MDL Lei
|
2011 |
+
MGF Malagasy Francs
|
2012 |
+
MKD Denars
|
2013 |
+
MMK Kyats
|
2014 |
+
MNT Tugriks
|
2015 |
+
MOP Patacas
|
2016 |
+
MRO Ouguiyas
|
2017 |
+
MTL Liri
|
2018 |
+
MUR Rupees
|
2019 |
+
MVR Rufiyaa
|
2020 |
+
MWK Kwachas
|
2021 |
+
MXN Pesos
|
2022 |
+
MYR Ringgits
|
2023 |
+
MZM Meticais
|
2024 |
+
NAD Dollars
|
2025 |
+
NGN Nairas
|
2026 |
+
NIO Gold Cordobas
|
2027 |
+
NLG Guilders
|
2028 |
+
NOK Krone
|
2029 |
+
NPR Nepal Rupees
|
2030 |
+
NZD Dollars
|
2031 |
+
OMR Rials
|
2032 |
+
PAB Balboa
|
2033 |
+
PEN Nuevos Soles
|
2034 |
+
PGK Kina
|
2035 |
+
PHP Pesos
|
2036 |
+
PKR Rupees
|
2037 |
+
PLN Zlotych
|
2038 |
+
PTE Escudos
|
2039 |
+
PYG Guarani
|
2040 |
+
QAR Rials
|
2041 |
+
ROL Lei
|
2042 |
+
RUR Rubles
|
2043 |
+
RWF Rwanda Francs
|
2044 |
+
SAR Riyals
|
2045 |
+
SBD Dollars
|
2046 |
+
SCR Rupees
|
2047 |
+
SDD Dinars
|
2048 |
+
SEK Kronor
|
2049 |
+
SGD Dollars
|
2050 |
+
SHP Pounds
|
2051 |
+
SIT Tolars
|
2052 |
+
SKK Koruny
|
2053 |
+
SLL Leones
|
2054 |
+
SOS Shillings
|
2055 |
+
SPL Luigini
|
2056 |
+
SRG Guilders
|
2057 |
+
STD Dobras
|
2058 |
+
SVC Colones
|
2059 |
+
SYP Pounds
|
2060 |
+
SZL Emalangeni
|
2061 |
+
THB Baht
|
2062 |
+
TJR Rubles
|
2063 |
+
TMM Manats
|
2064 |
+
TND Dinars
|
2065 |
+
TOP Pa'anga
|
2066 |
+
TRL Liras
|
2067 |
+
TTD Dollars
|
2068 |
+
TVD Tuvalu Dollars
|
2069 |
+
TWD New Dollars
|
2070 |
+
TZS Shillings
|
2071 |
+
UAH Hryvnia
|
2072 |
+
UGX Shillings
|
2073 |
+
USD Dollars
|
2074 |
+
UYU Pesos
|
2075 |
+
UZS Sums
|
2076 |
+
VAL Lire
|
2077 |
+
VEB Bolivares
|
2078 |
+
VND Dong
|
2079 |
+
VUV Vatu
|
2080 |
+
WST Tala
|
2081 |
+
XAF Francs
|
2082 |
+
XAG Ounces
|
2083 |
+
XAU Ounces
|
2084 |
+
XCD Dollars
|
2085 |
+
XDR Special Drawing Rights
|
2086 |
+
XPD Ounces
|
2087 |
+
XPF Francs
|
2088 |
+
XPT Ounces
|
2089 |
+
YER Rials
|
2090 |
+
YUM New Dinars
|
2091 |
+
ZAR Rand
|
2092 |
+
ZMK Kwacha
|
2093 |
+
ZWD Zimbabwe Dollars
|
2094 |
+
|
2095 |
+
*/
|
2096 |
+
|
2097 |
+
return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units');
|
2098 |
+
}
|
2099 |
+
|
2100 |
+
|
2101 |
+
function LookupCurrencyCountry($currencyid) {
|
2102 |
+
|
2103 |
+
$begin = __LINE__;
|
2104 |
+
|
2105 |
+
/** This is not a comment!
|
2106 |
+
|
2107 |
+
AED United Arab Emirates
|
2108 |
+
AFA Afghanistan
|
2109 |
+
ALL Albania
|
2110 |
+
AMD Armenia
|
2111 |
+
ANG Netherlands Antilles
|
2112 |
+
AOA Angola
|
2113 |
+
ARS Argentina
|
2114 |
+
ATS Austria
|
2115 |
+
AUD Australia
|
2116 |
+
AWG Aruba
|
2117 |
+
AZM Azerbaijan
|
2118 |
+
BAM Bosnia and Herzegovina
|
2119 |
+
BBD Barbados
|
2120 |
+
BDT Bangladesh
|
2121 |
+
BEF Belgium
|
2122 |
+
BGL Bulgaria
|
2123 |
+
BHD Bahrain
|
2124 |
+
BIF Burundi
|
2125 |
+
BMD Bermuda
|
2126 |
+
BND Brunei Darussalam
|
2127 |
+
BOB Bolivia
|
2128 |
+
BRL Brazil
|
2129 |
+
BSD Bahamas
|
2130 |
+
BTN Bhutan
|
2131 |
+
BWP Botswana
|
2132 |
+
BYR Belarus
|
2133 |
+
BZD Belize
|
2134 |
+
CAD Canada
|
2135 |
+
CDF Congo/Kinshasa
|
2136 |
+
CHF Switzerland
|
2137 |
+
CLP Chile
|
2138 |
+
CNY China
|
2139 |
+
COP Colombia
|
2140 |
+
CRC Costa Rica
|
2141 |
+
CUP Cuba
|
2142 |
+
CVE Cape Verde
|
2143 |
+
CYP Cyprus
|
2144 |
+
CZK Czech Republic
|
2145 |
+
DEM Germany
|
2146 |
+
DJF Djibouti
|
2147 |
+
DKK Denmark
|
2148 |
+
DOP Dominican Republic
|
2149 |
+
DZD Algeria
|
2150 |
+
EEK Estonia
|
2151 |
+
EGP Egypt
|
2152 |
+
ERN Eritrea
|
2153 |
+
ESP Spain
|
2154 |
+
ETB Ethiopia
|
2155 |
+
EUR Euro Member Countries
|
2156 |
+
FIM Finland
|
2157 |
+
FJD Fiji
|
2158 |
+
FKP Falkland Islands (Malvinas)
|
2159 |
+
FRF France
|
2160 |
+
GBP United Kingdom
|
2161 |
+
GEL Georgia
|
2162 |
+
GGP Guernsey
|
2163 |
+
GHC Ghana
|
2164 |
+
GIP Gibraltar
|
2165 |
+
GMD Gambia
|
2166 |
+
GNF Guinea
|
2167 |
+
GRD Greece
|
2168 |
+
GTQ Guatemala
|
2169 |
+
GYD Guyana
|
2170 |
+
HKD Hong Kong
|
2171 |
+
HNL Honduras
|
2172 |
+
HRK Croatia
|
2173 |
+
HTG Haiti
|
2174 |
+
HUF Hungary
|
2175 |
+
IDR Indonesia
|
2176 |
+
IEP Ireland (Eire)
|
2177 |
+
ILS Israel
|
2178 |
+
IMP Isle of Man
|
2179 |
+
INR India
|
2180 |
+
IQD Iraq
|
2181 |
+
IRR Iran
|
2182 |
+
ISK Iceland
|
2183 |
+
ITL Italy
|
2184 |
+
JEP Jersey
|
2185 |
+
JMD Jamaica
|
2186 |
+
JOD Jordan
|
2187 |
+
JPY Japan
|
2188 |
+
KES Kenya
|
2189 |
+
KGS Kyrgyzstan
|
2190 |
+
KHR Cambodia
|
2191 |
+
KMF Comoros
|
2192 |
+
KPW Korea
|
2193 |
+
KWD Kuwait
|
2194 |
+
KYD Cayman Islands
|
2195 |
+
KZT Kazakstan
|
2196 |
+
LAK Laos
|
2197 |
+
LBP Lebanon
|
2198 |
+
LKR Sri Lanka
|
2199 |
+
LRD Liberia
|
2200 |
+
LSL Lesotho
|
2201 |
+
LTL Lithuania
|
2202 |
+
LUF Luxembourg
|
2203 |
+
LVL Latvia
|
2204 |
+
LYD Libya
|
2205 |
+
MAD Morocco
|
2206 |
+
MDL Moldova
|
2207 |
+
MGF Madagascar
|
2208 |
+
MKD Macedonia
|
2209 |
+
MMK Myanmar (Burma)
|
2210 |
+
MNT Mongolia
|
2211 |
+
MOP Macau
|
2212 |
+
MRO Mauritania
|
2213 |
+
MTL Malta
|
2214 |
+
MUR Mauritius
|
2215 |
+
MVR Maldives (Maldive Islands)
|
2216 |
+
MWK Malawi
|
2217 |
+
MXN Mexico
|
2218 |
+
MYR Malaysia
|
2219 |
+
MZM Mozambique
|
2220 |
+
NAD Namibia
|
2221 |
+
NGN Nigeria
|
2222 |
+
NIO Nicaragua
|
2223 |
+
NLG Netherlands (Holland)
|
2224 |
+
NOK Norway
|
2225 |
+
NPR Nepal
|
2226 |
+
NZD New Zealand
|
2227 |
+
OMR Oman
|
2228 |
+
PAB Panama
|
2229 |
+
PEN Peru
|
2230 |
+
PGK Papua New Guinea
|
2231 |
+
PHP Philippines
|
2232 |
+
PKR Pakistan
|
2233 |
+
PLN Poland
|
2234 |
+
PTE Portugal
|
2235 |
+
PYG Paraguay
|
2236 |
+
QAR Qatar
|
2237 |
+
ROL Romania
|
2238 |
+
RUR Russia
|
2239 |
+
RWF Rwanda
|
2240 |
+
SAR Saudi Arabia
|
2241 |
+
SBD Solomon Islands
|
2242 |
+
SCR Seychelles
|
2243 |
+
SDD Sudan
|
2244 |
+
SEK Sweden
|
2245 |
+
SGD Singapore
|
2246 |
+
SHP Saint Helena
|
2247 |
+
SIT Slovenia
|
2248 |
+
SKK Slovakia
|
2249 |
+
SLL Sierra Leone
|
2250 |
+
SOS Somalia
|
2251 |
+
SPL Seborga
|
2252 |
+
SRG Suriname
|
2253 |
+
STD S�o Tome and Principe
|
2254 |
+
SVC El Salvador
|
2255 |
+
SYP Syria
|
2256 |
+
SZL Swaziland
|
2257 |
+
THB Thailand
|
2258 |
+
TJR Tajikistan
|
2259 |
+
TMM Turkmenistan
|
2260 |
+
TND Tunisia
|
2261 |
+
TOP Tonga
|
2262 |
+
TRL Turkey
|
2263 |
+
TTD Trinidad and Tobago
|
2264 |
+
TVD Tuvalu
|
2265 |
+
TWD Taiwan
|
2266 |
+
TZS Tanzania
|
2267 |
+
UAH Ukraine
|
2268 |
+
UGX Uganda
|
2269 |
+
USD United States of America
|
2270 |
+
UYU Uruguay
|
2271 |
+
UZS Uzbekistan
|
2272 |
+
VAL Vatican City
|
2273 |
+
VEB Venezuela
|
2274 |
+
VND Viet Nam
|
2275 |
+
VUV Vanuatu
|
2276 |
+
WST Samoa
|
2277 |
+
XAF Communaut� Financi�re Africaine
|
2278 |
+
XAG Silver
|
2279 |
+
XAU Gold
|
2280 |
+
XCD East Caribbean
|
2281 |
+
XDR International Monetary Fund
|
2282 |
+
XPD Palladium
|
2283 |
+
XPF Comptoirs Fran�ais du Pacifique
|
2284 |
+
XPT Platinum
|
2285 |
+
YER Yemen
|
2286 |
+
YUM Yugoslavia
|
2287 |
+
ZAR South Africa
|
2288 |
+
ZMK Zambia
|
2289 |
+
ZWD Zimbabwe
|
2290 |
+
|
2291 |
+
*/
|
2292 |
+
|
2293 |
+
return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country');
|
2294 |
+
}
|
2295 |
+
|
2296 |
+
|
2297 |
+
|
2298 |
+
static function LanguageLookup($languagecode, $casesensitive=false) {
|
2299 |
+
|
2300 |
+
if (!$casesensitive) {
|
2301 |
+
$languagecode = strtolower($languagecode);
|
2302 |
+
}
|
2303 |
+
|
2304 |
+
// http://www.id3.org/id3v2.4.0-structure.txt
|
2305 |
+
// [4. ID3v2 frame overview]
|
2306 |
+
// The three byte language field, present in several frames, is used to
|
2307 |
+
// describe the language of the frame's content, according to ISO-639-2
|
2308 |
+
// [ISO-639-2]. The language should be represented in lower case. If the
|
2309 |
+
// language is not known the string "XXX" should be used.
|
2310 |
+
|
2311 |
+
|
2312 |
+
// ISO 639-2 - http://www.id3.org/iso639-2.html
|
2313 |
+
|
2314 |
+
$begin = __LINE__;
|
2315 |
+
|
2316 |
+
/** This is not a comment!
|
2317 |
+
|
2318 |
+
XXX unknown
|
2319 |
+
xxx unknown
|
2320 |
+
aar Afar
|
2321 |
+
abk Abkhazian
|
2322 |
+
ace Achinese
|
2323 |
+
ach Acoli
|
2324 |
+
ada Adangme
|
2325 |
+
afa Afro-Asiatic (Other)
|
2326 |
+
afh Afrihili
|
2327 |
+
afr Afrikaans
|
2328 |
+
aka Akan
|
2329 |
+
akk Akkadian
|
2330 |
+
alb Albanian
|
2331 |
+
ale Aleut
|
2332 |
+
alg Algonquian Languages
|
2333 |
+
amh Amharic
|
2334 |
+
ang English, Old (ca. 450-1100)
|
2335 |
+
apa Apache Languages
|
2336 |
+
ara Arabic
|
2337 |
+
arc Aramaic
|
2338 |
+
arm Armenian
|
2339 |
+
arn Araucanian
|
2340 |
+
arp Arapaho
|
2341 |
+
art Artificial (Other)
|
2342 |
+
arw Arawak
|
2343 |
+
asm Assamese
|
2344 |
+
ath Athapascan Languages
|
2345 |
+
ava Avaric
|
2346 |
+
ave Avestan
|
2347 |
+
awa Awadhi
|
2348 |
+
aym Aymara
|
2349 |
+
aze Azerbaijani
|
2350 |
+
bad Banda
|
2351 |
+
bai Bamileke Languages
|
2352 |
+
bak Bashkir
|
2353 |
+
bal Baluchi
|
2354 |
+
bam Bambara
|
2355 |
+
ban Balinese
|
2356 |
+
baq Basque
|
2357 |
+
bas Basa
|
2358 |
+
bat Baltic (Other)
|
2359 |
+
bej Beja
|
2360 |
+
bel Byelorussian
|
2361 |
+
bem Bemba
|
2362 |
+
ben Bengali
|
2363 |
+
ber Berber (Other)
|
2364 |
+
bho Bhojpuri
|
2365 |
+
bih Bihari
|
2366 |
+
bik Bikol
|
2367 |
+
bin Bini
|
2368 |
+
bis Bislama
|
2369 |
+
bla Siksika
|
2370 |
+
bnt Bantu (Other)
|
2371 |
+
bod Tibetan
|
2372 |
+
bra Braj
|
2373 |
+
bre Breton
|
2374 |
+
bua Buriat
|
2375 |
+
bug Buginese
|
2376 |
+
bul Bulgarian
|
2377 |
+
bur Burmese
|
2378 |
+
cad Caddo
|
2379 |
+
cai Central American Indian (Other)
|
2380 |
+
car Carib
|
2381 |
+
cat Catalan
|
2382 |
+
cau Caucasian (Other)
|
2383 |
+
ceb Cebuano
|
2384 |
+
cel Celtic (Other)
|
2385 |
+
ces Czech
|
2386 |
+
cha Chamorro
|
2387 |
+
chb Chibcha
|
2388 |
+
che Chechen
|
2389 |
+
chg Chagatai
|
2390 |
+
chi Chinese
|
2391 |
+
chm Mari
|
2392 |
+
chn Chinook jargon
|
2393 |
+
cho Choctaw
|
2394 |
+
chr Cherokee
|
2395 |
+
chu Church Slavic
|
2396 |
+
chv Chuvash
|
2397 |
+
chy Cheyenne
|
2398 |
+
cop Coptic
|
2399 |
+
cor Cornish
|
2400 |
+
cos Corsican
|
2401 |
+
cpe Creoles and Pidgins, English-based (Other)
|
2402 |
+
cpf Creoles and Pidgins, French-based (Other)
|
2403 |
+
cpp Creoles and Pidgins, Portuguese-based (Other)
|
2404 |
+
cre Cree
|
2405 |
+
crp Creoles and Pidgins (Other)
|
2406 |
+
cus Cushitic (Other)
|
2407 |
+
cym Welsh
|
2408 |
+
cze Czech
|
2409 |
+
dak Dakota
|
2410 |
+
dan Danish
|
2411 |
+
del Delaware
|
2412 |
+
deu German
|
2413 |
+
din Dinka
|
2414 |
+
div Divehi
|
2415 |
+
doi Dogri
|
2416 |
+
dra Dravidian (Other)
|
2417 |
+
dua Duala
|
2418 |
+
dum Dutch, Middle (ca. 1050-1350)
|
2419 |
+
dut Dutch
|
2420 |
+
dyu Dyula
|
2421 |
+
dzo Dzongkha
|
2422 |
+
efi Efik
|
2423 |
+
egy Egyptian (Ancient)
|
2424 |
+
eka Ekajuk
|
2425 |
+
ell Greek, Modern (1453-)
|
2426 |
+
elx Elamite
|
2427 |
+
eng English
|
2428 |
+
enm English, Middle (ca. 1100-1500)
|
2429 |
+
epo Esperanto
|
2430 |
+
esk Eskimo (Other)
|
2431 |
+
esl Spanish
|
2432 |
+
est Estonian
|
2433 |
+
eus Basque
|
2434 |
+
ewe Ewe
|
2435 |
+
ewo Ewondo
|
2436 |
+
fan Fang
|
2437 |
+
fao Faroese
|
2438 |
+
fas Persian
|
2439 |
+
fat Fanti
|
2440 |
+
fij Fijian
|
2441 |
+
fin Finnish
|
2442 |
+
fiu Finno-Ugrian (Other)
|
2443 |
+
fon Fon
|
2444 |
+
fra French
|
2445 |
+
fre French
|
2446 |
+
frm French, Middle (ca. 1400-1600)
|
2447 |
+
fro French, Old (842- ca. 1400)
|
2448 |
+
fry Frisian
|
2449 |
+
ful Fulah
|
2450 |
+
gaa Ga
|
2451 |
+
gae Gaelic (Scots)
|
2452 |
+
gai Irish
|
2453 |
+
gay Gayo
|
2454 |
+
gdh Gaelic (Scots)
|
2455 |
+
gem Germanic (Other)
|
2456 |
+
geo Georgian
|
2457 |
+
ger German
|
2458 |
+
gez Geez
|
2459 |
+
gil Gilbertese
|
2460 |
+
glg Gallegan
|
2461 |
+
gmh German, Middle High (ca. 1050-1500)
|
2462 |
+
goh German, Old High (ca. 750-1050)
|
2463 |
+
gon Gondi
|
2464 |
+
got Gothic
|
2465 |
+
grb Grebo
|
2466 |
+
grc Greek, Ancient (to 1453)
|
2467 |
+
gre Greek, Modern (1453-)
|
2468 |
+
grn Guarani
|
2469 |
+
guj Gujarati
|
2470 |
+
hai Haida
|
2471 |
+
hau Hausa
|
2472 |
+
haw Hawaiian
|
2473 |
+
heb Hebrew
|
2474 |
+
her Herero
|
2475 |
+
hil Hiligaynon
|
2476 |
+
him Himachali
|
2477 |
+
hin Hindi
|
2478 |
+
hmo Hiri Motu
|
2479 |
+
hun Hungarian
|
2480 |
+
hup Hupa
|
2481 |
+
hye Armenian
|
2482 |
+
iba Iban
|
2483 |
+
ibo Igbo
|
2484 |
+
ice Icelandic
|
2485 |
+
ijo Ijo
|
2486 |
+
iku Inuktitut
|
2487 |
+
ilo Iloko
|
2488 |
+
ina Interlingua (International Auxiliary language Association)
|
2489 |
+
inc Indic (Other)
|
2490 |
+
ind Indonesian
|
2491 |
+
ine Indo-European (Other)
|
2492 |
+
ine Interlingue
|
2493 |
+
ipk Inupiak
|
2494 |
+
ira Iranian (Other)
|
2495 |
+
iri Irish
|
2496 |
+
iro Iroquoian uages
|
2497 |
+
isl Icelandic
|
2498 |
+
ita Italian
|
2499 |
+
jav Javanese
|
2500 |
+
jaw Javanese
|
2501 |
+
jpn Japanese
|
2502 |
+
jpr Judeo-Persian
|
2503 |
+
jrb Judeo-Arabic
|
2504 |
+
kaa Kara-Kalpak
|
2505 |
+
kab Kabyle
|
2506 |
+
kac Kachin
|
2507 |
+
kal Greenlandic
|
2508 |
+
kam Kamba
|
2509 |
+
kan Kannada
|
2510 |
+
kar Karen
|
2511 |
+
kas Kashmiri
|
2512 |
+
kat Georgian
|
2513 |
+
kau Kanuri
|
2514 |
+
kaw Kawi
|
2515 |
+
kaz Kazakh
|
2516 |
+
kha Khasi
|
2517 |
+
khi Khoisan (Other)
|
2518 |
+
khm Khmer
|
2519 |
+
kho Khotanese
|
2520 |
+
kik Kikuyu
|
2521 |
+
kin Kinyarwanda
|
2522 |
+
kir Kirghiz
|
2523 |
+
kok Konkani
|
2524 |
+
kom Komi
|
2525 |
+
kon Kongo
|
2526 |
+
kor Korean
|
2527 |
+
kpe Kpelle
|
2528 |
+
kro Kru
|
2529 |
+
kru Kurukh
|
2530 |
+
kua Kuanyama
|
2531 |
+
kum Kumyk
|
2532 |
+
kur Kurdish
|
2533 |
+
kus Kusaie
|
2534 |
+
kut Kutenai
|
2535 |
+
lad Ladino
|
2536 |
+
lah Lahnda
|
2537 |
+
lam Lamba
|
2538 |
+
lao Lao
|
2539 |
+
lat Latin
|
2540 |
+
lav Latvian
|
2541 |
+
lez Lezghian
|
2542 |
+
lin Lingala
|
2543 |
+
lit Lithuanian
|
2544 |
+
lol Mongo
|
2545 |
+
loz Lozi
|
2546 |
+
ltz Letzeburgesch
|
2547 |
+
lub Luba-Katanga
|
2548 |
+
lug Ganda
|
2549 |
+
lui Luiseno
|
2550 |
+
lun Lunda
|
2551 |
+
luo Luo (Kenya and Tanzania)
|
2552 |
+
mac Macedonian
|
2553 |
+
mad Madurese
|
2554 |
+
mag Magahi
|
2555 |
+
mah Marshall
|
2556 |
+
mai Maithili
|
2557 |
+
mak Macedonian
|
2558 |
+
mak Makasar
|
2559 |
+
mal Malayalam
|
2560 |
+
man Mandingo
|
2561 |
+
mao Maori
|
2562 |
+
map Austronesian (Other)
|
2563 |
+
mar Marathi
|
2564 |
+
mas Masai
|
2565 |
+
max Manx
|
2566 |
+
may Malay
|
2567 |
+
men Mende
|
2568 |
+
mga Irish, Middle (900 - 1200)
|
2569 |
+
mic Micmac
|
2570 |
+
min Minangkabau
|
2571 |
+
mis Miscellaneous (Other)
|
2572 |
+
mkh Mon-Kmer (Other)
|
2573 |
+
mlg Malagasy
|
2574 |
+
mlt Maltese
|
2575 |
+
mni Manipuri
|
2576 |
+
mno Manobo Languages
|
2577 |
+
moh Mohawk
|
2578 |
+
mol Moldavian
|
2579 |
+
mon Mongolian
|
2580 |
+
mos Mossi
|
2581 |
+
mri Maori
|
2582 |
+
msa Malay
|
2583 |
+
mul Multiple Languages
|
2584 |
+
mun Munda Languages
|
2585 |
+
mus Creek
|
2586 |
+
mwr Marwari
|
2587 |
+
mya Burmese
|
2588 |
+
myn Mayan Languages
|
2589 |
+
nah Aztec
|
2590 |
+
nai North American Indian (Other)
|
2591 |
+
nau Nauru
|
2592 |
+
nav Navajo
|
2593 |
+
nbl Ndebele, South
|
2594 |
+
nde Ndebele, North
|
2595 |
+
ndo Ndongo
|
2596 |
+
nep Nepali
|
2597 |
+
new Newari
|
2598 |
+
nic Niger-Kordofanian (Other)
|
2599 |
+
niu Niuean
|
2600 |
+
nla Dutch
|
2601 |
+
nno Norwegian (Nynorsk)
|
2602 |
+
non Norse, Old
|
2603 |
+
nor Norwegian
|
2604 |
+
nso Sotho, Northern
|
2605 |
+
nub Nubian Languages
|
2606 |
+
nya Nyanja
|
2607 |
+
nym Nyamwezi
|
2608 |
+
nyn Nyankole
|
2609 |
+
nyo Nyoro
|
2610 |
+
nzi Nzima
|
2611 |
+
oci Langue d'Oc (post 1500)
|
2612 |
+
oji Ojibwa
|
2613 |
+
ori Oriya
|
2614 |
+
orm Oromo
|
2615 |
+
osa Osage
|
2616 |
+
oss Ossetic
|
2617 |
+
ota Turkish, Ottoman (1500 - 1928)
|
2618 |
+
oto Otomian Languages
|
2619 |
+
paa Papuan-Australian (Other)
|
2620 |
+
pag Pangasinan
|
2621 |
+
pal Pahlavi
|
2622 |
+
pam Pampanga
|
2623 |
+
pan Panjabi
|
2624 |
+
pap Papiamento
|
2625 |
+
pau Palauan
|
2626 |
+
peo Persian, Old (ca 600 - 400 B.C.)
|
2627 |
+
per Persian
|
2628 |
+
phn Phoenician
|
2629 |
+
pli Pali
|
2630 |
+
pol Polish
|
2631 |
+
pon Ponape
|
2632 |
+
por Portuguese
|
2633 |
+
pra Prakrit uages
|
2634 |
+
pro Provencal, Old (to 1500)
|
2635 |
+
pus Pushto
|
2636 |
+
que Quechua
|
2637 |
+
raj Rajasthani
|
2638 |
+
rar Rarotongan
|
2639 |
+
roa Romance (Other)
|
2640 |
+
roh Rhaeto-Romance
|
2641 |
+
rom Romany
|
2642 |
+
ron Romanian
|
2643 |
+
rum Romanian
|
2644 |
+
run Rundi
|
2645 |
+
rus Russian
|
2646 |
+
sad Sandawe
|
2647 |
+
sag Sango
|
2648 |
+
sah Yakut
|
2649 |
+
sai South American Indian (Other)
|
2650 |
+
sal Salishan Languages
|
2651 |
+
sam Samaritan Aramaic
|
2652 |
+
san Sanskrit
|
2653 |
+
sco Scots
|
2654 |
+
scr Serbo-Croatian
|
2655 |
+
sel Selkup
|
2656 |
+
sem Semitic (Other)
|
2657 |
+
sga Irish, Old (to 900)
|
2658 |
+
shn Shan
|
2659 |
+
sid Sidamo
|
2660 |
+
sin Singhalese
|
2661 |
+
sio Siouan Languages
|
2662 |
+
sit Sino-Tibetan (Other)
|
2663 |
+
sla Slavic (Other)
|
2664 |
+
slk Slovak
|
2665 |
+
slo Slovak
|
2666 |
+
slv Slovenian
|
2667 |
+
smi Sami Languages
|
2668 |
+
smo Samoan
|
2669 |
+
sna Shona
|
2670 |
+
snd Sindhi
|
2671 |
+
sog Sogdian
|
2672 |
+
som Somali
|
2673 |
+
son Songhai
|
2674 |
+
sot Sotho, Southern
|
2675 |
+
spa Spanish
|
2676 |
+
sqi Albanian
|
2677 |
+
srd Sardinian
|
2678 |
+
srr Serer
|
2679 |
+
ssa Nilo-Saharan (Other)
|
2680 |
+
ssw Siswant
|
2681 |
+
ssw Swazi
|
2682 |
+
suk Sukuma
|
2683 |
+
sun Sudanese
|
2684 |
+
sus Susu
|
2685 |
+
sux Sumerian
|
2686 |
+
sve Swedish
|
2687 |
+
swa Swahili
|
2688 |
+
swe Swedish
|
2689 |
+
syr Syriac
|
2690 |
+
tah Tahitian
|
2691 |
+
tam Tamil
|
2692 |
+
tat Tatar
|
2693 |
+
tel Telugu
|
2694 |
+
tem Timne
|
2695 |
+
ter Tereno
|
2696 |
+
tgk Tajik
|
2697 |
+
tgl Tagalog
|
2698 |
+
tha Thai
|
2699 |
+
tib Tibetan
|
2700 |
+
tig Tigre
|
2701 |
+
tir Tigrinya
|
2702 |
+
tiv Tivi
|
2703 |
+
tli Tlingit
|
2704 |
+
tmh Tamashek
|
2705 |
+
tog Tonga (Nyasa)
|
2706 |
+
ton Tonga (Tonga Islands)
|
2707 |
+
tru Truk
|
2708 |
+
tsi Tsimshian
|
2709 |
+
tsn Tswana
|
2710 |
+
tso Tsonga
|
2711 |
+
tuk Turkmen
|
2712 |
+
tum Tumbuka
|
2713 |
+
tur Turkish
|
2714 |
+
tut Altaic (Other)
|
2715 |
+
twi Twi
|
2716 |
+
tyv Tuvinian
|
2717 |
+
uga Ugaritic
|
2718 |
+
uig Uighur
|
2719 |
+
ukr Ukrainian
|
2720 |
+
umb Umbundu
|
2721 |
+
und Undetermined
|
2722 |
+
urd Urdu
|
2723 |
+
uzb Uzbek
|
2724 |
+
vai Vai
|
2725 |
+
ven Venda
|
2726 |
+
vie Vietnamese
|
2727 |
+
vol Volap�k
|
2728 |
+
vot Votic
|
2729 |
+
wak Wakashan Languages
|
2730 |
+
wal Walamo
|
2731 |
+
war Waray
|
2732 |
+
was Washo
|
2733 |
+
wel Welsh
|
2734 |
+
wen Sorbian Languages
|
2735 |
+
wol Wolof
|
2736 |
+
xho Xhosa
|
2737 |
+
yao Yao
|
2738 |
+
yap Yap
|
2739 |
+
yid Yiddish
|
2740 |
+
yor Yoruba
|
2741 |
+
zap Zapotec
|
2742 |
+
zen Zenaga
|
2743 |
+
zha Zhuang
|
2744 |
+
zho Chinese
|
2745 |
+
zul Zulu
|
2746 |
+
zun Zuni
|
2747 |
+
|
2748 |
+
*/
|
2749 |
+
|
2750 |
+
return getid3_lib::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode');
|
2751 |
+
}
|
2752 |
+
|
2753 |
+
|
2754 |
+
static function ETCOEventLookup($index) {
|
2755 |
+
if (($index >= 0x17) && ($index <= 0xDF)) {
|
2756 |
+
return 'reserved for future use';
|
2757 |
+
}
|
2758 |
+
if (($index >= 0xE0) && ($index <= 0xEF)) {
|
2759 |
+
return 'not predefined synch 0-F';
|
2760 |
+
}
|
2761 |
+
if (($index >= 0xF0) && ($index <= 0xFC)) {
|
2762 |
+
return 'reserved for future use';
|
2763 |
+
}
|
2764 |
+
|
2765 |
+
static $EventLookup = array(
|
2766 |
+
0x00 => 'padding (has no meaning)',
|
2767 |
+
0x01 => 'end of initial silence',
|
2768 |
+
0x02 => 'intro start',
|
2769 |
+
0x03 => 'main part start',
|
2770 |
+
0x04 => 'outro start',
|
2771 |
+
0x05 => 'outro end',
|
2772 |
+
0x06 => 'verse start',
|
2773 |
+
0x07 => 'refrain start',
|
2774 |
+
0x08 => 'interlude start',
|
2775 |
+
0x09 => 'theme start',
|
2776 |
+
0x0A => 'variation start',
|
2777 |
+
0x0B => 'key change',
|
2778 |
+
0x0C => 'time change',
|
2779 |
+
0x0D => 'momentary unwanted noise (Snap, Crackle & Pop)',
|
2780 |
+
0x0E => 'sustained noise',
|
2781 |
+
0x0F => 'sustained noise end',
|
2782 |
+
0x10 => 'intro end',
|
2783 |
+
0x11 => 'main part end',
|
2784 |
+
0x12 => 'verse end',
|
2785 |
+
0x13 => 'refrain end',
|
2786 |
+
0x14 => 'theme end',
|
2787 |
+
0x15 => 'profanity',
|
2788 |
+
0x16 => 'profanity end',
|
2789 |
+
0xFD => 'audio end (start of silence)',
|
2790 |
+
0xFE => 'audio file ends',
|
2791 |
+
0xFF => 'one more byte of events follows'
|
2792 |
+
);
|
2793 |
+
|
2794 |
+
return (isset($EventLookup[$index]) ? $EventLookup[$index] : '');
|
2795 |
+
}
|
2796 |
+
|
2797 |
+
static function SYTLContentTypeLookup($index) {
|
2798 |
+
static $SYTLContentTypeLookup = array(
|
2799 |
+
0x00 => 'other',
|
2800 |
+
0x01 => 'lyrics',
|
2801 |
+
0x02 => 'text transcription',
|
2802 |
+
0x03 => 'movement/part name', // (e.g. 'Adagio')
|
2803 |
+
0x04 => 'events', // (e.g. 'Don Quijote enters the stage')
|
2804 |
+
0x05 => 'chord', // (e.g. 'Bb F Fsus')
|
2805 |
+
0x06 => 'trivia/\'pop up\' information',
|
2806 |
+
0x07 => 'URLs to webpages',
|
2807 |
+
0x08 => 'URLs to images'
|
2808 |
+
);
|
2809 |
+
|
2810 |
+
return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : '');
|
2811 |
+
}
|
2812 |
+
|
2813 |
+
static function APICPictureTypeLookup($index, $returnarray=false) {
|
2814 |
+
static $APICPictureTypeLookup = array(
|
2815 |
+
0x00 => 'Other',
|
2816 |
+
0x01 => '32x32 pixels \'file icon\' (PNG only)',
|
2817 |
+
0x02 => 'Other file icon',
|
2818 |
+
0x03 => 'Cover (front)',
|
2819 |
+
0x04 => 'Cover (back)',
|
2820 |
+
0x05 => 'Leaflet page',
|
2821 |
+
0x06 => 'Media (e.g. label side of CD)',
|
2822 |
+
0x07 => 'Lead artist/lead performer/soloist',
|
2823 |
+
0x08 => 'Artist/performer',
|
2824 |
+
0x09 => 'Conductor',
|
2825 |
+
0x0A => 'Band/Orchestra',
|
2826 |
+
0x0B => 'Composer',
|
2827 |
+
0x0C => 'Lyricist/text writer',
|
2828 |
+
0x0D => 'Recording Location',
|
2829 |
+
0x0E => 'During recording',
|
2830 |
+
0x0F => 'During performance',
|
2831 |
+
0x10 => 'Movie/video screen capture',
|
2832 |
+
0x11 => 'A bright coloured fish',
|
2833 |
+
0x12 => 'Illustration',
|
2834 |
+
0x13 => 'Band/artist logotype',
|
2835 |
+
0x14 => 'Publisher/Studio logotype'
|
2836 |
+
);
|
2837 |
+
if ($returnarray) {
|
2838 |
+
return $APICPictureTypeLookup;
|
2839 |
+
}
|
2840 |
+
return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : '');
|
2841 |
+
}
|
2842 |
+
|
2843 |
+
static function COMRReceivedAsLookup($index) {
|
2844 |
+
static $COMRReceivedAsLookup = array(
|
2845 |
+
0x00 => 'Other',
|
2846 |
+
0x01 => 'Standard CD album with other songs',
|
2847 |
+
0x02 => 'Compressed audio on CD',
|
2848 |
+
0x03 => 'File over the Internet',
|
2849 |
+
0x04 => 'Stream over the Internet',
|
2850 |
+
0x05 => 'As note sheets',
|
2851 |
+
0x06 => 'As note sheets in a book with other sheets',
|
2852 |
+
0x07 => 'Music on other media',
|
2853 |
+
0x08 => 'Non-musical merchandise'
|
2854 |
+
);
|
2855 |
+
|
2856 |
+
return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : '');
|
2857 |
+
}
|
2858 |
+
|
2859 |
+
static function RVA2ChannelTypeLookup($index) {
|
2860 |
+
static $RVA2ChannelTypeLookup = array(
|
2861 |
+
0x00 => 'Other',
|
2862 |
+
0x01 => 'Master volume',
|
2863 |
+
0x02 => 'Front right',
|
2864 |
+
0x03 => 'Front left',
|
2865 |
+
0x04 => 'Back right',
|
2866 |
+
0x05 => 'Back left',
|
2867 |
+
0x06 => 'Front centre',
|
2868 |
+
0x07 => 'Back centre',
|
2869 |
+
0x08 => 'Subwoofer'
|
2870 |
+
);
|
2871 |
+
|
2872 |
+
return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : '');
|
2873 |
+
}
|
2874 |
+
|
2875 |
+
static function FrameNameLongLookup($framename) {
|
2876 |
+
|
2877 |
+
$begin = __LINE__;
|
2878 |
+
|
2879 |
+
/** This is not a comment!
|
2880 |
+
|
2881 |
+
AENC Audio encryption
|
2882 |
+
APIC Attached picture
|
2883 |
+
ASPI Audio seek point index
|
2884 |
+
BUF Recommended buffer size
|
2885 |
+
CNT Play counter
|
2886 |
+
COM Comments
|
2887 |
+
COMM Comments
|
2888 |
+
COMR Commercial frame
|
2889 |
+
CRA Audio encryption
|
2890 |
+
CRM Encrypted meta frame
|
2891 |
+
ENCR Encryption method registration
|
2892 |
+
EQU Equalisation
|
2893 |
+
EQU2 Equalisation (2)
|
2894 |
+
EQUA Equalisation
|
2895 |
+
ETC Event timing codes
|
2896 |
+
ETCO Event timing codes
|
2897 |
+
GEO General encapsulated object
|
2898 |
+
GEOB General encapsulated object
|
2899 |
+
GRID Group identification registration
|
2900 |
+
IPL Involved people list
|
2901 |
+
IPLS Involved people list
|
2902 |
+
LINK Linked information
|
2903 |
+
LNK Linked information
|
2904 |
+
MCDI Music CD identifier
|
2905 |
+
MCI Music CD Identifier
|
2906 |
+
MLL MPEG location lookup table
|
2907 |
+
MLLT MPEG location lookup table
|
2908 |
+
OWNE Ownership frame
|
2909 |
+
PCNT Play counter
|
2910 |
+
PIC Attached picture
|
2911 |
+
POP Popularimeter
|
2912 |
+
POPM Popularimeter
|
2913 |
+
POSS Position synchronisation frame
|
2914 |
+
PRIV Private frame
|
2915 |
+
RBUF Recommended buffer size
|
2916 |
+
REV Reverb
|
2917 |
+
RVA Relative volume adjustment
|
2918 |
+
RVA2 Relative volume adjustment (2)
|
2919 |
+
RVAD Relative volume adjustment
|
2920 |
+
RVRB Reverb
|
2921 |
+
SEEK Seek frame
|
2922 |
+
SIGN Signature frame
|
2923 |
+
SLT Synchronised lyric/text
|
2924 |
+
STC Synced tempo codes
|
2925 |
+
SYLT Synchronised lyric/text
|
2926 |
+
SYTC Synchronised tempo codes
|
2927 |
+
TAL Album/Movie/Show title
|
2928 |
+
TALB Album/Movie/Show title
|
2929 |
+
TBP BPM (Beats Per Minute)
|
2930 |
+
TBPM BPM (beats per minute)
|
2931 |
+
TCM Composer
|
2932 |
+
TCMP Part of a compilation
|
2933 |
+
TCO Content type
|
2934 |
+
TCOM Composer
|
2935 |
+
TCON Content type
|
2936 |
+
TCOP Copyright message
|
2937 |
+
TCP Part of a compilation
|
2938 |
+
TCR Copyright message
|
2939 |
+
TDA Date
|
2940 |
+
TDAT Date
|
2941 |
+
TDEN Encoding time
|
2942 |
+
TDLY Playlist delay
|
2943 |
+
TDOR Original release time
|
2944 |
+
TDRC Recording time
|
2945 |
+
TDRL Release time
|
2946 |
+
TDTG Tagging time
|
2947 |
+
TDY Playlist delay
|
2948 |
+
TEN Encoded by
|
2949 |
+
TENC Encoded by
|
2950 |
+
TEXT Lyricist/Text writer
|
2951 |
+
TFLT File type
|
2952 |
+
TFT File type
|
2953 |
+
TIM Time
|
2954 |
+
TIME Time
|
2955 |
+
TIPL Involved people list
|
2956 |
+
TIT1 Content group description
|
2957 |
+
TIT2 Title/songname/content description
|
2958 |
+
TIT3 Subtitle/Description refinement
|
2959 |
+
TKE Initial key
|
2960 |
+
TKEY Initial key
|
2961 |
+
TLA Language(s)
|
2962 |
+
TLAN Language(s)
|
2963 |
+
TLE Length
|
2964 |
+
TLEN Length
|
2965 |
+
TMCL Musician credits list
|
2966 |
+
TMED Media type
|
2967 |
+
TMOO Mood
|
2968 |
+
TMT Media type
|
2969 |
+
TOA Original artist(s)/performer(s)
|
2970 |
+
TOAL Original album/movie/show title
|
2971 |
+
TOF Original filename
|
2972 |
+
TOFN Original filename
|
2973 |
+
TOL Original Lyricist(s)/text writer(s)
|
2974 |
+
TOLY Original lyricist(s)/text writer(s)
|
2975 |
+
TOPE Original artist(s)/performer(s)
|
2976 |
+
TOR Original release year
|
2977 |
+
TORY Original release year
|
2978 |
+
TOT Original album/Movie/Show title
|
2979 |
+
TOWN File owner/licensee
|
2980 |
+
TP1 Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group
|
2981 |
+
TP2 Band/Orchestra/Accompaniment
|
2982 |
+
TP3 Conductor/Performer refinement
|
2983 |
+
TP4 Interpreted, remixed, or otherwise modified by
|
2984 |
+
TPA Part of a set
|
2985 |
+
TPB Publisher
|
2986 |
+
TPE1 Lead performer(s)/Soloist(s)
|
2987 |
+
TPE2 Band/orchestra/accompaniment
|
2988 |
+
TPE3 Conductor/performer refinement
|
2989 |
+
TPE4 Interpreted, remixed, or otherwise modified by
|
2990 |
+
TPOS Part of a set
|
2991 |
+
TPRO Produced notice
|
2992 |
+
TPUB Publisher
|
2993 |
+
TRC ISRC (International Standard Recording Code)
|
2994 |
+
TRCK Track number/Position in set
|
2995 |
+
TRD Recording dates
|
2996 |
+
TRDA Recording dates
|
2997 |
+
TRK Track number/Position in set
|
2998 |
+
TRSN Internet radio station name
|
2999 |
+
TRSO Internet radio station owner
|
3000 |
+
TS2 Album-Artist sort order
|
3001 |
+
TSA Album sort order
|
3002 |
+
TSC Composer sort order
|
3003 |
+
TSI Size
|
3004 |
+
TSIZ Size
|
3005 |
+
TSO2 Album-Artist sort order
|
3006 |
+
TSOA Album sort order
|
3007 |
+
TSOC Composer sort order
|
3008 |
+
TSOP Performer sort order
|
3009 |
+
TSOT Title sort order
|
3010 |
+
TSP Performer sort order
|
3011 |
+
TSRC ISRC (international standard recording code)
|
3012 |
+
TSS Software/hardware and settings used for encoding
|
3013 |
+
TSSE Software/Hardware and settings used for encoding
|
3014 |
+
TSST Set subtitle
|
3015 |
+
TST Title sort order
|
3016 |
+
TT1 Content group description
|
3017 |
+
TT2 Title/Songname/Content description
|
3018 |
+
TT3 Subtitle/Description refinement
|
3019 |
+
TXT Lyricist/text writer
|
3020 |
+
TXX User defined text information frame
|
3021 |
+
TXXX User defined text information frame
|
3022 |
+
TYE Year
|
3023 |
+
TYER Year
|
3024 |
+
UFI Unique file identifier
|
3025 |
+
UFID Unique file identifier
|
3026 |
+
ULT Unsychronised lyric/text transcription
|
3027 |
+
USER Terms of use
|
3028 |
+
USLT Unsynchronised lyric/text transcription
|
3029 |
+
WAF Official audio file webpage
|
3030 |
+
WAR Official artist/performer webpage
|
3031 |
+
WAS Official audio source webpage
|
3032 |
+
WCM Commercial information
|
3033 |
+
WCOM Commercial information
|
3034 |
+
WCOP Copyright/Legal information
|
3035 |
+
WCP Copyright/Legal information
|
3036 |
+
WOAF Official audio file webpage
|
3037 |
+
WOAR Official artist/performer webpage
|
3038 |
+
WOAS Official audio source webpage
|
3039 |
+
WORS Official Internet radio station homepage
|
3040 |
+
WPAY Payment
|
3041 |
+
WPB Publishers official webpage
|
3042 |
+
WPUB Publishers official webpage
|
3043 |
+
WXX User defined URL link frame
|
3044 |
+
WXXX User defined URL link frame
|
3045 |
+
TFEA Featured Artist
|
3046 |
+
TSTU Recording Studio
|
3047 |
+
rgad Replay Gain Adjustment
|
3048 |
+
|
3049 |
+
*/
|
3050 |
+
|
3051 |
+
return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_long');
|
3052 |
+
|
3053 |
+
// Last three:
|
3054 |
+
// from Helium2 [www.helium2.com]
|
3055 |
+
// from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
|
3056 |
+
}
|
3057 |
+
|
3058 |
+
|
3059 |
+
static function FrameNameShortLookup($framename) {
|
3060 |
+
|
3061 |
+
$begin = __LINE__;
|
3062 |
+
|
3063 |
+
/** This is not a comment!
|
3064 |
+
|
3065 |
+
AENC audio_encryption
|
3066 |
+
APIC attached_picture
|
3067 |
+
ASPI audio_seek_point_index
|
3068 |
+
BUF recommended_buffer_size
|
3069 |
+
CNT play_counter
|
3070 |
+
COM comment
|
3071 |
+
COMM comment
|
3072 |
+
COMR commercial_frame
|
3073 |
+
CRA audio_encryption
|
3074 |
+
CRM encrypted_meta_frame
|
3075 |
+
ENCR encryption_method_registration
|
3076 |
+
EQU equalisation
|
3077 |
+
EQU2 equalisation
|
3078 |
+
EQUA equalisation
|
3079 |
+
ETC event_timing_codes
|
3080 |
+
ETCO event_timing_codes
|
3081 |
+
GEO general_encapsulated_object
|
3082 |
+
GEOB general_encapsulated_object
|
3083 |
+
GRID group_identification_registration
|
3084 |
+
IPL involved_people_list
|
3085 |
+
IPLS involved_people_list
|
3086 |
+
LINK linked_information
|
3087 |
+
LNK linked_information
|
3088 |
+
MCDI music_cd_identifier
|
3089 |
+
MCI music_cd_identifier
|
3090 |
+
MLL mpeg_location_lookup_table
|
3091 |
+
MLLT mpeg_location_lookup_table
|
3092 |
+
OWNE ownership_frame
|
3093 |
+
PCNT play_counter
|
3094 |
+
PIC attached_picture
|
3095 |
+
POP popularimeter
|
3096 |
+
POPM popularimeter
|
3097 |
+
POSS position_synchronisation_frame
|
3098 |
+
PRIV private_frame
|
3099 |
+
RBUF recommended_buffer_size
|
3100 |
+
REV reverb
|
3101 |
+
RVA relative_volume_adjustment
|
3102 |
+
RVA2 relative_volume_adjustment
|
3103 |
+
RVAD relative_volume_adjustment
|
3104 |
+
RVRB reverb
|
3105 |
+
SEEK seek_frame
|
3106 |
+
SIGN signature_frame
|
3107 |
+
SLT synchronised_lyric
|
3108 |
+
STC synced_tempo_codes
|
3109 |
+
SYLT synchronised_lyric
|
3110 |
+
SYTC synchronised_tempo_codes
|
3111 |
+
TAL album
|
3112 |
+
TALB album
|
3113 |
+
TBP bpm
|
3114 |
+
TBPM bpm
|
3115 |
+
TCM composer
|
3116 |
+
TCMP part_of_a_compilation
|
3117 |
+
TCO genre
|
3118 |
+
TCOM composer
|
3119 |
+
TCON genre
|
3120 |
+
TCOP copyright_message
|
3121 |
+
TCP part_of_a_compilation
|
3122 |
+
TCR copyright_message
|
3123 |
+
TDA date
|
3124 |
+
TDAT date
|
3125 |
+
TDEN encoding_time
|
3126 |
+
TDLY playlist_delay
|
3127 |
+
TDOR original_release_time
|
3128 |
+
TDRC recording_time
|
3129 |
+
TDRL release_time
|
3130 |
+
TDTG tagging_time
|
3131 |
+
TDY playlist_delay
|
3132 |
+
TEN encoded_by
|
3133 |
+
TENC encoded_by
|
3134 |
+
TEXT lyricist
|
3135 |
+
TFLT file_type
|
3136 |
+
TFT file_type
|
3137 |
+
TIM time
|
3138 |
+
TIME time
|
3139 |
+
TIPL involved_people_list
|
3140 |
+
TIT1 content_group_description
|
3141 |
+
TIT2 title
|
3142 |
+
TIT3 subtitle
|
3143 |
+
TKE initial_key
|
3144 |
+
TKEY initial_key
|
3145 |
+
TLA language
|
3146 |
+
TLAN language
|
3147 |
+
TLE length
|
3148 |
+
TLEN length
|
3149 |
+
TMCL musician_credits_list
|
3150 |
+
TMED media_type
|
3151 |
+
TMOO mood
|
3152 |
+
TMT media_type
|
3153 |
+
TOA original_artist
|
3154 |
+
TOAL original_album
|
3155 |
+
TOF original_filename
|
3156 |
+
TOFN original_filename
|
3157 |
+
TOL original_lyricist
|
3158 |
+
TOLY original_lyricist
|
3159 |
+
TOPE original_artist
|
3160 |
+
TOR original_year
|
3161 |
+
TORY original_year
|
3162 |
+
TOT original_album
|
3163 |
+
TOWN file_owner
|
3164 |
+
TP1 artist
|
3165 |
+
TP2 band
|
3166 |
+
TP3 conductor
|
3167 |
+
TP4 remixer
|
3168 |
+
TPA part_of_a_set
|
3169 |
+
TPB publisher
|
3170 |
+
TPE1 artist
|
3171 |
+
TPE2 band
|
3172 |
+
TPE3 conductor
|
3173 |
+
TPE4 remixer
|
3174 |
+
TPOS part_of_a_set
|
3175 |
+
TPRO produced_notice
|
3176 |
+
TPUB publisher
|
3177 |
+
TRC isrc
|
3178 |
+
TRCK track_number
|
3179 |
+
TRD recording_dates
|
3180 |
+
TRDA recording_dates
|
3181 |
+
TRK track_number
|
3182 |
+
TRSN internet_radio_station_name
|
3183 |
+
TRSO internet_radio_station_owner
|
3184 |
+
TS2 album_artist_sort_order
|
3185 |
+
TSA album_sort_order
|
3186 |
+
TSC composer_sort_order
|
3187 |
+
TSI size
|
3188 |
+
TSIZ size
|
3189 |
+
TSO2 album_artist_sort_order
|
3190 |
+
TSOA album_sort_order
|
3191 |
+
TSOC composer_sort_order
|
3192 |
+
TSOP performer_sort_order
|
3193 |
+
TSOT title_sort_order
|
3194 |
+
TSP performer_sort_order
|
3195 |
+
TSRC isrc
|
3196 |
+
TSS encoder_settings
|
3197 |
+
TSSE encoder_settings
|
3198 |
+
TSST set_subtitle
|
3199 |
+
TST title_sort_order
|
3200 |
+
TT1 description
|
3201 |
+
TT2 title
|
3202 |
+
TT3 subtitle
|
3203 |
+
TXT lyricist
|
3204 |
+
TXX text
|
3205 |
+
TXXX text
|
3206 |
+
TYE year
|
3207 |
+
TYER year
|
3208 |
+
UFI unique_file_identifier
|
3209 |
+
UFID unique_file_identifier
|
3210 |
+
ULT unsychronised_lyric
|
3211 |
+
USER terms_of_use
|
3212 |
+
USLT unsynchronised_lyric
|
3213 |
+
WAF url_file
|
3214 |
+
WAR url_artist
|
3215 |
+
WAS url_source
|
3216 |
+
WCM commercial_information
|
3217 |
+
WCOM commercial_information
|
3218 |
+
WCOP copyright
|
3219 |
+
WCP copyright
|
3220 |
+
WOAF url_file
|
3221 |
+
WOAR url_artist
|
3222 |
+
WOAS url_source
|
3223 |
+
WORS url_station
|
3224 |
+
WPAY url_payment
|
3225 |
+
WPB url_publisher
|
3226 |
+
WPUB url_publisher
|
3227 |
+
WXX url_user
|
3228 |
+
WXXX url_user
|
3229 |
+
TFEA featured_artist
|
3230 |
+
TSTU recording_studio
|
3231 |
+
rgad replay_gain_adjustment
|
3232 |
+
|
3233 |
+
*/
|
3234 |
+
|
3235 |
+
return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short');
|
3236 |
+
}
|
3237 |
+
|
3238 |
+
static function TextEncodingTerminatorLookup($encoding) {
|
3239 |
+
// http://www.id3.org/id3v2.4.0-structure.txt
|
3240 |
+
// Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
|
3241 |
+
static $TextEncodingTerminatorLookup = array(
|
3242 |
+
0 => "\x00", // $00 ISO-8859-1. Terminated with $00.
|
3243 |
+
1 => "\x00\x00", // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00.
|
3244 |
+
2 => "\x00\x00", // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
|
3245 |
+
3 => "\x00", // $03 UTF-8 encoded Unicode. Terminated with $00.
|
3246 |
+
255 => "\x00\x00"
|
3247 |
+
);
|
3248 |
+
return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : '');
|
3249 |
+
}
|
3250 |
+
|
3251 |
+
static function TextEncodingNameLookup($encoding) {
|
3252 |
+
// http://www.id3.org/id3v2.4.0-structure.txt
|
3253 |
+
// Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
|
3254 |
+
static $TextEncodingNameLookup = array(
|
3255 |
+
0 => 'ISO-8859-1', // $00 ISO-8859-1. Terminated with $00.
|
3256 |
+
1 => 'UTF-16', // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00.
|
3257 |
+
2 => 'UTF-16BE', // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
|
3258 |
+
3 => 'UTF-8', // $03 UTF-8 encoded Unicode. Terminated with $00.
|
3259 |
+
255 => 'UTF-16BE'
|
3260 |
+
);
|
3261 |
+
return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1');
|
3262 |
+
}
|
3263 |
+
|
3264 |
+
static function IsValidID3v2FrameName($framename, $id3v2majorversion) {
|
3265 |
+
switch ($id3v2majorversion) {
|
3266 |
+
case 2:
|
3267 |
+
return preg_match('#[A-Z][A-Z0-9]{2}#', $framename);
|
3268 |
+
break;
|
3269 |
+
|
3270 |
+
case 3:
|
3271 |
+
case 4:
|
3272 |
+
return preg_match('#[A-Z][A-Z0-9]{3}#', $framename);
|
3273 |
+
break;
|
3274 |
+
}
|
3275 |
+
return false;
|
3276 |
+
}
|
3277 |
+
|
3278 |
+
static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) {
|
3279 |
+
for ($i = 0; $i < strlen($numberstring); $i++) {
|
3280 |
+
if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) {
|
3281 |
+
if (($numberstring{$i} == '.') && $allowdecimal) {
|
3282 |
+
// allowed
|
3283 |
+
} elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) {
|
3284 |
+
// allowed
|
3285 |
+
} else {
|
3286 |
+
return false;
|
3287 |
+
}
|
3288 |
+
}
|
3289 |
+
}
|
3290 |
+
return true;
|
3291 |
+
}
|
3292 |
+
|
3293 |
+
static function IsValidDateStampString($datestamp) {
|
3294 |
+
if (strlen($datestamp) != 8) {
|
3295 |
+
return false;
|
3296 |
+
}
|
3297 |
+
if (!self::IsANumber($datestamp, false)) {
|
3298 |
+
return false;
|
3299 |
+
}
|
3300 |
+
$year = substr($datestamp, 0, 4);
|
3301 |
+
$month = substr($datestamp, 4, 2);
|
3302 |
+
$day = substr($datestamp, 6, 2);
|
3303 |
+
if (($year == 0) || ($month == 0) || ($day == 0)) {
|
3304 |
+
return false;
|
3305 |
+
}
|
3306 |
+
if ($month > 12) {
|
3307 |
+
return false;
|
3308 |
+
}
|
3309 |
+
if ($day > 31) {
|
3310 |
+
return false;
|
3311 |
+
}
|
3312 |
+
if (($day > 30) && (($month == 4) || ($month == 6) || ($month == 9) || ($month == 11))) {
|
3313 |
+
return false;
|
3314 |
+
}
|
3315 |
+
if (($day > 29) && ($month == 2)) {
|
3316 |
+
return false;
|
3317 |
+
}
|
3318 |
+
return true;
|
3319 |
+
}
|
3320 |
+
|
3321 |
+
static function ID3v2HeaderLength($majorversion) {
|
3322 |
+
return (($majorversion == 2) ? 6 : 10);
|
3323 |
+
}
|
3324 |
+
|
3325 |
+
}
|
3326 |
+
|
3327 |
+
?>
|
includes/lib/getid3/module.tag.lyrics3.php
ADDED
@@ -0,0 +1,297 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
/////////////////////////////////////////////////////////////////
|
7 |
+
// See readme.txt for more details //
|
8 |
+
/////////////////////////////////////////////////////////////////
|
9 |
+
/// //
|
10 |
+
// module.tag.lyrics3.php //
|
11 |
+
// module for analyzing Lyrics3 tags //
|
12 |
+
// dependencies: module.tag.apetag.php (optional) //
|
13 |
+
// ///
|
14 |
+
/////////////////////////////////////////////////////////////////
|
15 |
+
|
16 |
+
|
17 |
+
class getid3_lyrics3 extends getid3_handler
|
18 |
+
{
|
19 |
+
|
20 |
+
function Analyze() {
|
21 |
+
$info = &$this->getid3->info;
|
22 |
+
|
23 |
+
// http://www.volweb.cz/str/tags.htm
|
24 |
+
|
25 |
+
if (!getid3_lib::intValueSupported($info['filesize'])) {
|
26 |
+
$info['warning'][] = 'Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
|
27 |
+
return false;
|
28 |
+
}
|
29 |
+
|
30 |
+
fseek($this->getid3->fp, (0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - "LYRICSEND" - [Lyrics3size]
|
31 |
+
$lyrics3_id3v1 = fread($this->getid3->fp, 128 + 9 + 6);
|
32 |
+
$lyrics3lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size
|
33 |
+
$lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200
|
34 |
+
$id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1
|
35 |
+
|
36 |
+
if ($lyrics3end == 'LYRICSEND') {
|
37 |
+
// Lyrics3v1, ID3v1, no APE
|
38 |
+
|
39 |
+
$lyrics3size = 5100;
|
40 |
+
$lyrics3offset = $info['filesize'] - 128 - $lyrics3size;
|
41 |
+
$lyrics3version = 1;
|
42 |
+
|
43 |
+
} elseif ($lyrics3end == 'LYRICS200') {
|
44 |
+
// Lyrics3v2, ID3v1, no APE
|
45 |
+
|
46 |
+
// LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
|
47 |
+
$lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200');
|
48 |
+
$lyrics3offset = $info['filesize'] - 128 - $lyrics3size;
|
49 |
+
$lyrics3version = 2;
|
50 |
+
|
51 |
+
} elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICSEND')) {
|
52 |
+
// Lyrics3v1, no ID3v1, no APE
|
53 |
+
|
54 |
+
$lyrics3size = 5100;
|
55 |
+
$lyrics3offset = $info['filesize'] - $lyrics3size;
|
56 |
+
$lyrics3version = 1;
|
57 |
+
$lyrics3offset = $info['filesize'] - $lyrics3size;
|
58 |
+
|
59 |
+
} elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICS200')) {
|
60 |
+
|
61 |
+
// Lyrics3v2, no ID3v1, no APE
|
62 |
+
|
63 |
+
$lyrics3size = strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
|
64 |
+
$lyrics3offset = $info['filesize'] - $lyrics3size;
|
65 |
+
$lyrics3version = 2;
|
66 |
+
|
67 |
+
} else {
|
68 |
+
|
69 |
+
if (isset($info['ape']['tag_offset_start']) && ($info['ape']['tag_offset_start'] > 15)) {
|
70 |
+
|
71 |
+
fseek($this->getid3->fp, $info['ape']['tag_offset_start'] - 15, SEEK_SET);
|
72 |
+
$lyrics3lsz = fread($this->getid3->fp, 6);
|
73 |
+
$lyrics3end = fread($this->getid3->fp, 9);
|
74 |
+
|
75 |
+
if ($lyrics3end == 'LYRICSEND') {
|
76 |
+
// Lyrics3v1, APE, maybe ID3v1
|
77 |
+
|
78 |
+
$lyrics3size = 5100;
|
79 |
+
$lyrics3offset = $info['ape']['tag_offset_start'] - $lyrics3size;
|
80 |
+
$info['avdataend'] = $lyrics3offset;
|
81 |
+
$lyrics3version = 1;
|
82 |
+
$info['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability';
|
83 |
+
|
84 |
+
} elseif ($lyrics3end == 'LYRICS200') {
|
85 |
+
// Lyrics3v2, APE, maybe ID3v1
|
86 |
+
|
87 |
+
$lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
|
88 |
+
$lyrics3offset = $info['ape']['tag_offset_start'] - $lyrics3size;
|
89 |
+
$lyrics3version = 2;
|
90 |
+
$info['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability';
|
91 |
+
|
92 |
+
}
|
93 |
+
|
94 |
+
}
|
95 |
+
|
96 |
+
}
|
97 |
+
|
98 |
+
if (isset($lyrics3offset)) {
|
99 |
+
$info['avdataend'] = $lyrics3offset;
|
100 |
+
$this->getLyrics3Data($lyrics3offset, $lyrics3version, $lyrics3size);
|
101 |
+
|
102 |
+
if (!isset($info['ape'])) {
|
103 |
+
$GETID3_ERRORARRAY = &$info['warning'];
|
104 |
+
if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, false)) {
|
105 |
+
$getid3_temp = new getID3();
|
106 |
+
$getid3_temp->openfile($this->getid3->filename);
|
107 |
+
$getid3_apetag = new getid3_apetag($getid3_temp);
|
108 |
+
$getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start'];
|
109 |
+
$getid3_apetag->Analyze();
|
110 |
+
if (!empty($getid3_temp->info['ape'])) {
|
111 |
+
$info['ape'] = $getid3_temp->info['ape'];
|
112 |
+
}
|
113 |
+
if (!empty($getid3_temp->info['replay_gain'])) {
|
114 |
+
$info['replay_gain'] = $getid3_temp->info['replay_gain'];
|
115 |
+
}
|
116 |
+
unset($getid3_temp, $getid3_apetag);
|
117 |
+
}
|
118 |
+
}
|
119 |
+
|
120 |
+
}
|
121 |
+
|
122 |
+
return true;
|
123 |
+
}
|
124 |
+
|
125 |
+
function getLyrics3Data($endoffset, $version, $length) {
|
126 |
+
// http://www.volweb.cz/str/tags.htm
|
127 |
+
|
128 |
+
$info = &$this->getid3->info;
|
129 |
+
|
130 |
+
if (!getid3_lib::intValueSupported($endoffset)) {
|
131 |
+
$info['warning'][] = 'Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
|
132 |
+
return false;
|
133 |
+
}
|
134 |
+
|
135 |
+
fseek($this->getid3->fp, $endoffset, SEEK_SET);
|
136 |
+
if ($length <= 0) {
|
137 |
+
return false;
|
138 |
+
}
|
139 |
+
$rawdata = fread($this->getid3->fp, $length);
|
140 |
+
|
141 |
+
$ParsedLyrics3['raw']['lyrics3version'] = $version;
|
142 |
+
$ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
|
143 |
+
$ParsedLyrics3['tag_offset_start'] = $endoffset;
|
144 |
+
$ParsedLyrics3['tag_offset_end'] = $endoffset + $length - 1;
|
145 |
+
|
146 |
+
if (substr($rawdata, 0, 11) != 'LYRICSBEGIN') {
|
147 |
+
if (strpos($rawdata, 'LYRICSBEGIN') !== false) {
|
148 |
+
|
149 |
+
$info['warning'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but actually found at '.($endoffset + strpos($rawdata, 'LYRICSBEGIN')).' - this is invalid for Lyrics3 v'.$version;
|
150 |
+
$info['avdataend'] = $endoffset + strpos($rawdata, 'LYRICSBEGIN');
|
151 |
+
$rawdata = substr($rawdata, strpos($rawdata, 'LYRICSBEGIN'));
|
152 |
+
$length = strlen($rawdata);
|
153 |
+
$ParsedLyrics3['tag_offset_start'] = $info['avdataend'];
|
154 |
+
$ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
|
155 |
+
|
156 |
+
} else {
|
157 |
+
|
158 |
+
$info['error'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but found "'.substr($rawdata, 0, 11).'" instead';
|
159 |
+
return false;
|
160 |
+
|
161 |
+
}
|
162 |
+
|
163 |
+
}
|
164 |
+
|
165 |
+
switch ($version) {
|
166 |
+
|
167 |
+
case 1:
|
168 |
+
if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICSEND') {
|
169 |
+
$ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9));
|
170 |
+
$this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
|
171 |
+
} else {
|
172 |
+
$info['error'][] = '"LYRICSEND" expected at '.(ftell($this->getid3->fp) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead';
|
173 |
+
return false;
|
174 |
+
}
|
175 |
+
break;
|
176 |
+
|
177 |
+
case 2:
|
178 |
+
if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICS200') {
|
179 |
+
$ParsedLyrics3['raw']['unparsed'] = substr($rawdata, 11, strlen($rawdata) - 11 - 9 - 6); // LYRICSBEGIN + LYRICS200 + LSZ
|
180 |
+
$rawdata = $ParsedLyrics3['raw']['unparsed'];
|
181 |
+
while (strlen($rawdata) > 0) {
|
182 |
+
$fieldname = substr($rawdata, 0, 3);
|
183 |
+
$fieldsize = (int) substr($rawdata, 3, 5);
|
184 |
+
$ParsedLyrics3['raw'][$fieldname] = substr($rawdata, 8, $fieldsize);
|
185 |
+
$rawdata = substr($rawdata, 3 + 5 + $fieldsize);
|
186 |
+
}
|
187 |
+
|
188 |
+
if (isset($ParsedLyrics3['raw']['IND'])) {
|
189 |
+
$i = 0;
|
190 |
+
$flagnames = array('lyrics', 'timestamps', 'inhibitrandom');
|
191 |
+
foreach ($flagnames as $flagname) {
|
192 |
+
if (strlen($ParsedLyrics3['raw']['IND']) > $i++) {
|
193 |
+
$ParsedLyrics3['flags'][$flagname] = $this->IntString2Bool(substr($ParsedLyrics3['raw']['IND'], $i, 1 - 1));
|
194 |
+
}
|
195 |
+
}
|
196 |
+
}
|
197 |
+
|
198 |
+
$fieldnametranslation = array('ETT'=>'title', 'EAR'=>'artist', 'EAL'=>'album', 'INF'=>'comment', 'AUT'=>'author');
|
199 |
+
foreach ($fieldnametranslation as $key => $value) {
|
200 |
+
if (isset($ParsedLyrics3['raw'][$key])) {
|
201 |
+
$ParsedLyrics3['comments'][$value][] = trim($ParsedLyrics3['raw'][$key]);
|
202 |
+
}
|
203 |
+
}
|
204 |
+
|
205 |
+
if (isset($ParsedLyrics3['raw']['IMG'])) {
|
206 |
+
$imagestrings = explode("\r\n", $ParsedLyrics3['raw']['IMG']);
|
207 |
+
foreach ($imagestrings as $key => $imagestring) {
|
208 |
+
if (strpos($imagestring, '||') !== false) {
|
209 |
+
$imagearray = explode('||', $imagestring);
|
210 |
+
$ParsedLyrics3['images'][$key]['filename'] = (isset($imagearray[0]) ? $imagearray[0] : '');
|
211 |
+
$ParsedLyrics3['images'][$key]['description'] = (isset($imagearray[1]) ? $imagearray[1] : '');
|
212 |
+
$ParsedLyrics3['images'][$key]['timestamp'] = $this->Lyrics3Timestamp2Seconds(isset($imagearray[2]) ? $imagearray[2] : '');
|
213 |
+
}
|
214 |
+
}
|
215 |
+
}
|
216 |
+
if (isset($ParsedLyrics3['raw']['LYR'])) {
|
217 |
+
$this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
|
218 |
+
}
|
219 |
+
} else {
|
220 |
+
$info['error'][] = '"LYRICS200" expected at '.(ftell($this->getid3->fp) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead';
|
221 |
+
return false;
|
222 |
+
}
|
223 |
+
break;
|
224 |
+
|
225 |
+
default:
|
226 |
+
$info['error'][] = 'Cannot process Lyrics3 version '.$version.' (only v1 and v2)';
|
227 |
+
return false;
|
228 |
+
break;
|
229 |
+
}
|
230 |
+
|
231 |
+
|
232 |
+
if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] <= $ParsedLyrics3['tag_offset_end'])) {
|
233 |
+
$info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data';
|
234 |
+
unset($info['id3v1']);
|
235 |
+
foreach ($info['warning'] as $key => $value) {
|
236 |
+
if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
|
237 |
+
unset($info['warning'][$key]);
|
238 |
+
sort($info['warning']);
|
239 |
+
break;
|
240 |
+
}
|
241 |
+
}
|
242 |
+
}
|
243 |
+
|
244 |
+
$info['lyrics3'] = $ParsedLyrics3;
|
245 |
+
|
246 |
+
return true;
|
247 |
+
}
|
248 |
+
|
249 |
+
function Lyrics3Timestamp2Seconds($rawtimestamp) {
|
250 |
+
if (preg_match('#^\\[([0-9]{2}):([0-9]{2})\\]$#', $rawtimestamp, $regs)) {
|
251 |
+
return (int) (($regs[1] * 60) + $regs[2]);
|
252 |
+
}
|
253 |
+
return false;
|
254 |
+
}
|
255 |
+
|
256 |
+
function Lyrics3LyricsTimestampParse(&$Lyrics3data) {
|
257 |
+
$lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']);
|
258 |
+
foreach ($lyricsarray as $key => $lyricline) {
|
259 |
+
$regs = array();
|
260 |
+
unset($thislinetimestamps);
|
261 |
+
while (preg_match('#^(\\[[0-9]{2}:[0-9]{2}\\])#', $lyricline, $regs)) {
|
262 |
+
$thislinetimestamps[] = $this->Lyrics3Timestamp2Seconds($regs[0]);
|
263 |
+
$lyricline = str_replace($regs[0], '', $lyricline);
|
264 |
+
}
|
265 |
+
$notimestamplyricsarray[$key] = $lyricline;
|
266 |
+
if (isset($thislinetimestamps) && is_array($thislinetimestamps)) {
|
267 |
+
sort($thislinetimestamps);
|
268 |
+
foreach ($thislinetimestamps as $timestampkey => $timestamp) {
|
269 |
+
if (isset($Lyrics3data['synchedlyrics'][$timestamp])) {
|
270 |
+
// timestamps only have a 1-second resolution, it's possible that multiple lines
|
271 |
+
// could have the same timestamp, if so, append
|
272 |
+
$Lyrics3data['synchedlyrics'][$timestamp] .= "\r\n".$lyricline;
|
273 |
+
} else {
|
274 |
+
$Lyrics3data['synchedlyrics'][$timestamp] = $lyricline;
|
275 |
+
}
|
276 |
+
}
|
277 |
+
}
|
278 |
+
}
|
279 |
+
$Lyrics3data['unsynchedlyrics'] = implode("\r\n", $notimestamplyricsarray);
|
280 |
+
if (isset($Lyrics3data['synchedlyrics']) && is_array($Lyrics3data['synchedlyrics'])) {
|
281 |
+
ksort($Lyrics3data['synchedlyrics']);
|
282 |
+
}
|
283 |
+
return true;
|
284 |
+
}
|
285 |
+
|
286 |
+
function IntString2Bool($char) {
|
287 |
+
if ($char == '1') {
|
288 |
+
return true;
|
289 |
+
} elseif ($char == '0') {
|
290 |
+
return false;
|
291 |
+
}
|
292 |
+
return null;
|
293 |
+
}
|
294 |
+
}
|
295 |
+
|
296 |
+
|
297 |
+
?>
|
loader.php
CHANGED
@@ -1,18 +1,18 @@
|
|
1 |
<?php
|
2 |
/*
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
|
11 |
/* A constant that can be checked to see if the BP Media is installed or not. */
|
12 |
define('BP_MEDIA_IS_INSTALLED', 1);
|
13 |
|
14 |
/* Constant to store the current version of the BP Media Plugin. */
|
15 |
-
define('BP_MEDIA_VERSION', '2.0');
|
16 |
|
17 |
/* A constant to be used as base for other URLs throughout the plugin */
|
18 |
define('BP_MEDIA_PLUGIN_DIR', dirname(__FILE__));
|
@@ -53,4 +53,4 @@ function bp_media_deactivate() {
|
|
53 |
//todo
|
54 |
}
|
55 |
register_deactivation_hook(__FILE__, 'bp_media_deactivate');
|
56 |
-
?>
|
1 |
<?php
|
2 |
/*
|
3 |
+
Plugin Name: BuddyPress Media Component
|
4 |
+
Plugin URI: http://rtcamp.com/buddypress-media/
|
5 |
+
Description: This component adds missing media rich features like photos, videos and audios uploading to BuddyPress which are essential if you are building social network, seriously!
|
6 |
+
Version: 2.0.1
|
7 |
+
Author: rtCamp
|
8 |
+
Author URI: http://rtcamp.com
|
9 |
+
*/
|
10 |
|
11 |
/* A constant that can be checked to see if the BP Media is installed or not. */
|
12 |
define('BP_MEDIA_IS_INSTALLED', 1);
|
13 |
|
14 |
/* Constant to store the current version of the BP Media Plugin. */
|
15 |
+
define('BP_MEDIA_VERSION', '2.0.1');
|
16 |
|
17 |
/* A constant to be used as base for other URLs throughout the plugin */
|
18 |
define('BP_MEDIA_PLUGIN_DIR', dirname(__FILE__));
|
53 |
//todo
|
54 |
}
|
55 |
register_deactivation_hook(__FILE__, 'bp_media_deactivate');
|
56 |
+
?>
|
readme.txt
CHANGED
@@ -1,40 +1,48 @@
|
|
1 |
-
=== BuddyPress Media Component ===
|
2 |
-
Contributors: rtcamp
|
3 |
-
Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9488824
|
4 |
-
Tags: BuddyPress, media, multimedia, audio, video, photo, images, upload, share, MediaElement.js
|
5 |
-
License: GPLv2 or later
|
6 |
-
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
7 |
-
Requires at least: 3.3.2
|
8 |
-
Tested up to: 3.4.1
|
9 |
-
Stable tag: 2.0
|
10 |
-
|
11 |
-
BuddyPress Media Component adds multimedia features to your BuddyPress based social network.
|
12 |
-
|
13 |
-
== Description ==
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
*
|
19 |
-
*
|
20 |
-
*
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
Please visit [BuddyPress Media Component's
|
34 |
-
|
35 |
-
==
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
=== BuddyPress Media Component ===
|
2 |
+
Contributors: rtcamp
|
3 |
+
Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9488824
|
4 |
+
Tags: BuddyPress, media, multimedia, audio, video, photo, images, upload, share, MediaElement.js
|
5 |
+
License: GPLv2 or later
|
6 |
+
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
7 |
+
Requires at least: 3.3.2
|
8 |
+
Tested up to: 3.4.1
|
9 |
+
Stable tag: 2.0.1
|
10 |
+
|
11 |
+
BuddyPress Media Component adds multimedia features to your BuddyPress based social network.
|
12 |
+
|
13 |
+
== Description ==
|
14 |
+
|
15 |
+
[BuddyPress Media Component](http://rtcamp.com/buddypress-media/) adds multimedia features to your BuddyPress based social network, so that your members can upload and share photos, audio and videos with their friends.
|
16 |
+
|
17 |
+
= Features =
|
18 |
+
* Images, Audio and Video Support
|
19 |
+
* Superior Performance
|
20 |
+
* HTML5 player with fall back
|
21 |
+
* Highly Scalable
|
22 |
+
|
23 |
+
== Installation ==
|
24 |
+
|
25 |
+
Install the plugin from the 'Plugins' section in your dashboard (Plugins > Add New > Search for BuddyPress Media).
|
26 |
+
|
27 |
+
Alternatively you can [download](http://downloads.wordpress.org/plugin/buddypress-media.zip) the plugin from the repository. Unzip it and upload it to the plugins folder of your WordPress installation (wp-content/plugins/ directory of your WordPress installation).
|
28 |
+
|
29 |
+
Activate it through the 'Plugins' section.
|
30 |
+
|
31 |
+
== Frequently Asked Questions ==
|
32 |
+
|
33 |
+
Please visit [BuddyPress Media Component's FAQ page](http://rtcamp.com/buddypress-media/faq/)
|
34 |
+
|
35 |
+
== Screenshots ==
|
36 |
+
|
37 |
+
Please visit [BuddyPress Media Component's Features page](http://rtcamp.com/buddypress-media/features/)
|
38 |
+
|
39 |
+
== Changelog ==
|
40 |
+
|
41 |
+
= 2.0.1 =
|
42 |
+
* Replaced codec finding library
|
43 |
+
* Fixed warning on activities page
|
44 |
+
|
45 |
+
= 2.0 =
|
46 |
+
* Integration into BuddyPress Activities
|
47 |
+
* HTML5 Audio Tag Support (with fallback)
|
48 |
+
* HTML5 Video Tag Support (with fallback)
|