ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
Real.php
Go to the documentation of this file.
1<?php
2
3namespace GetId3\Write;
4
7
10// available at http://getid3.sourceforge.net //
11// or http://www.getid3.org //
13// See readme.txt for more details //
15// //
16// write.real.php //
17// module for writing RealAudio/RealVideo tags //
18// dependencies: module.tag.real.php //
19// ///
21
29class Real
30{
35 public $filename;
40 public $tag_data = array();
45 public $fread_buffer_size = 32768; // read buffer size in bytes
50 public $warnings = array(); // any non-critical errors will be stored here
55 public $errors = array(); // any critical errors will be stored here
60 public $paddedlength = 512; // minimum length of CONT tag in bytes
61
66 public function __construct()
67 {
68 return true;
69 }
70
75 public function WriteReal()
76 {
77 // File MUST be writeable - CHMOD(646) at least
78 if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) {
79
80 // Initialize GetId3 engine
81 $getID3 = new GetId3Core();
82 $OldThisFileInfo = $getID3->analyze($this->filename);
83 if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
84 $this->errors[] = 'Cannot write Real tags on old-style file format';
85 fclose($fp_source);
86
87 return false;
88 }
89
90 if (empty($OldThisFileInfo['real']['chunks'])) {
91 $this->errors[] = 'Cannot write Real tags because cannot find DATA chunk in file';
92 fclose($fp_source);
93
94 return false;
95 }
96 foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
97 $oldChunkInfo[$chunkarray['name']] = $chunkarray;
98 }
99 if (!empty($oldChunkInfo['CONT']['length'])) {
100 $this->paddedlength = max($oldChunkInfo['CONT']['length'], $this->paddedlength);
101 }
102
103 $new_CONT_tag_data = $this->GenerateCONTchunk();
104 $new_PROP_tag_data = $this->GeneratePROPchunk($OldThisFileInfo['real']['chunks'], $new_CONT_tag_data);
105 $new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']);
106
107 if (isset($oldChunkInfo['.RMF']['length']) && ($oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data))) {
108 fseek($fp_source, $oldChunkInfo['.RMF']['offset'], SEEK_SET);
109 fwrite($fp_source, $new__RMF_tag_data);
110 } else {
111 $this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)';
112 fclose($fp_source);
113
114 return false;
115 }
116
117 if (isset($oldChunkInfo['PROP']['length']) && ($oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data))) {
118 fseek($fp_source, $oldChunkInfo['PROP']['offset'], SEEK_SET);
119 fwrite($fp_source, $new_PROP_tag_data);
120 } else {
121 $this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)';
122 fclose($fp_source);
123
124 return false;
125 }
126
127 if (isset($oldChunkInfo['CONT']['length']) && ($oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data))) {
128
129 // new data length is same as old data length - just overwrite
130 fseek($fp_source, $oldChunkInfo['CONT']['offset'], SEEK_SET);
131 fwrite($fp_source, $new_CONT_tag_data);
132 fclose($fp_source);
133
134 return true;
135
136 } else {
137
138 if (empty($oldChunkInfo['CONT'])) {
139 // no existing CONT chunk
140 $BeforeOffset = $oldChunkInfo['DATA']['offset'];
141 $AfterOffset = $oldChunkInfo['DATA']['offset'];
142 } else {
143 // new data is longer than old data
144 $BeforeOffset = $oldChunkInfo['CONT']['offset'];
145 $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
146 }
147 if ($tempfilename = tempnam(GetId3Core::getTempDir(), 'getID3')) {
148 if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) {
149
150 rewind($fp_source);
151 fwrite($fp_temp, fread($fp_source, $BeforeOffset));
152 fwrite($fp_temp, $new_CONT_tag_data);
153 fseek($fp_source, $AfterOffset, SEEK_SET);
154 while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
155 fwrite($fp_temp, $buffer, strlen($buffer));
156 }
157 fclose($fp_temp);
158
159 if (copy($tempfilename, $this->filename)) {
160 unlink($tempfilename);
161 fclose($fp_source);
162
163 return true;
164 }
165 unlink($tempfilename);
166 $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.')';
167
168 } else {
169 $this->errors[] = 'Could not fopen("'.$tempfilename.'", "wb")';
170 }
171 }
172 fclose($fp_source);
173
174 return false;
175
176 }
177
178 }
179 $this->errors[] = 'Could not fopen("'.$this->filename.'", "r+b")';
180
181 return false;
182 }
183
189 public function GenerateRMFchunk(&$chunks)
190 {
191 $oldCONTexists = false;
192 foreach ($chunks as $key => $chunk) {
193 $chunkNameKeys[$chunk['name']] = $key;
194 if ($chunk['name'] == 'CONT') {
195 $oldCONTexists = true;
196 }
197 }
198 $newHeadersCount = $chunks[$chunkNameKeys['.RMF']]['headers_count'] + ($oldCONTexists ? 0 : 1);
199
200 $RMFchunk = "\x00\x00"; // object version
201 $RMFchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['.RMF']]['file_version'], 4);
202 $RMFchunk .= Helper::BigEndian2String($newHeadersCount, 4);
203
204 $RMFchunk = '.RMF'.Helper::BigEndian2String(strlen($RMFchunk) + 8, 4).$RMFchunk; // .RMF chunk identifier + chunk length
205
206 return $RMFchunk;
207 }
208
215 public function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data)
216 {
217 $old_CONT_length = 0;
218 $old_DATA_offset = 0;
219 $old_INDX_offset = 0;
220 foreach ($chunks as $key => $chunk) {
221 $chunkNameKeys[$chunk['name']] = $key;
222 if ($chunk['name'] == 'CONT') {
223 $old_CONT_length = $chunk['length'];
224 } elseif ($chunk['name'] == 'DATA') {
225 if (!$old_DATA_offset) {
226 $old_DATA_offset = $chunk['offset'];
227 }
228 } elseif ($chunk['name'] == 'INDX') {
229 if (!$old_INDX_offset) {
230 $old_INDX_offset = $chunk['offset'];
231 }
232 }
233 }
234 $CONTdelta = strlen($new_CONT_tag_data) - $old_CONT_length;
235
236 $PROPchunk = "\x00\x00"; // object version
237 $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_bit_rate'], 4);
238 $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_bit_rate'], 4);
239 $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_packet_size'], 4);
240 $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_packet_size'], 4);
241 $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_packets'], 4);
242 $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['duration'], 4);
243 $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['preroll'], 4);
244 $PROPchunk .= Helper::BigEndian2String(max(0, $old_INDX_offset + $CONTdelta), 4);
245 $PROPchunk .= Helper::BigEndian2String(max(0, $old_DATA_offset + $CONTdelta), 4);
246 $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_streams'], 2);
247 $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['flags_raw'], 2);
248
249 $PROPchunk = 'PROP'.Helper::BigEndian2String(strlen($PROPchunk) + 8, 4).$PROPchunk; // PROP chunk identifier + chunk length
250
251 return $PROPchunk;
252 }
253
258 public function GenerateCONTchunk()
259 {
260 foreach ($this->tag_data as $key => $value) {
261 // limit each value to 0xFFFF bytes
262 $this->tag_data[$key] = substr($value, 0, 65535);
263 }
264
265 $CONTchunk = "\x00\x00"; // object version
266
267 $CONTchunk .= Helper::BigEndian2String((!empty($this->tag_data['title']) ? strlen($this->tag_data['title']) : 0), 2);
268 $CONTchunk .= (!empty($this->tag_data['title']) ? strlen($this->tag_data['title']) : '');
269
270 $CONTchunk .= Helper::BigEndian2String((!empty($this->tag_data['artist']) ? strlen($this->tag_data['artist']) : 0), 2);
271 $CONTchunk .= (!empty($this->tag_data['artist']) ? strlen($this->tag_data['artist']) : '');
272
273 $CONTchunk .= Helper::BigEndian2String((!empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : 0), 2);
274 $CONTchunk .= (!empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : '');
275
276 $CONTchunk .= Helper::BigEndian2String((!empty($this->tag_data['comment']) ? strlen($this->tag_data['comment']) : 0), 2);
277 $CONTchunk .= (!empty($this->tag_data['comment']) ? strlen($this->tag_data['comment']) : '');
278
279 if ($this->paddedlength > (strlen($CONTchunk) + 8)) {
280 $CONTchunk .= str_repeat("\x00", $this->paddedlength - strlen($CONTchunk) - 8);
281 }
282
283 $CONTchunk = 'CONT'.Helper::BigEndian2String(strlen($CONTchunk) + 8, 4).$CONTchunk; // CONT chunk identifier + chunk length
284
285 return $CONTchunk;
286 }
287
292 public function RemoveReal()
293 {
294 // File MUST be writeable - CHMOD(646) at least
295 if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) {
296
297 // Initialize GetId3 engine
298 $getID3 = new GetId3Core();
299 $OldThisFileInfo = $getID3->analyze($this->filename);
300 if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
301 $this->errors[] = 'Cannot remove Real tags from old-style file format';
302 fclose($fp_source);
303
304 return false;
305 }
306
307 if (empty($OldThisFileInfo['real']['chunks'])) {
308 $this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file';
309 fclose($fp_source);
310
311 return false;
312 }
313 foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
314 $oldChunkInfo[$chunkarray['name']] = $chunkarray;
315 }
316
317 if (empty($oldChunkInfo['CONT'])) {
318 // no existing CONT chunk
319 fclose($fp_source);
320
321 return true;
322 }
323
324 $BeforeOffset = $oldChunkInfo['CONT']['offset'];
325 $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
326 if ($tempfilename = tempnam(GetId3Core::getTempDir(), 'getID3')) {
327 if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) {
328
329 rewind($fp_source);
330 fwrite($fp_temp, fread($fp_source, $BeforeOffset));
331 fseek($fp_source, $AfterOffset, SEEK_SET);
332 while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
333 fwrite($fp_temp, $buffer, strlen($buffer));
334 }
335 fclose($fp_temp);
336
337 if (copy($tempfilename, $this->filename)) {
338 unlink($tempfilename);
339 fclose($fp_source);
340
341 return true;
342 }
343 unlink($tempfilename);
344 $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.')';
345
346 } else {
347 $this->errors[] = 'Could not fopen("'.$tempfilename.'", "wb")';
348 }
349 }
350 fclose($fp_source);
351
352 return false;
353 }
354 $this->errors[] = 'Could not fopen("'.$this->filename.'", "r+b")';
355
356 return false;
357 }
358
359}
An exception for terminatinating execution or to throw for unit testing.
GetId3() by James Heinrich info@getid3.org //.
Definition: GetId3Core.php:26
GetId3() by James Heinrich info@getid3.org //.
Definition: Helper.php:27
static BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false)
Definition: Helper.php:444
GetId3() by James Heinrich info@getid3.org //.
Definition: Real.php:30
GenerateRMFchunk(&$chunks)
Definition: Real.php:189
GeneratePROPchunk(&$chunks, &$new_CONT_tag_data)
Definition: Real.php:215