75 define(
'PATH_MOOVRELOCATOR', CH_DIRECTORY_PATH_PLUGINS .
'moovrelocator/lib/');
111 private static $_instance =
null;
119 private $_outputFile =
false;
127 private $_filesize = 0;
135 private $_fp =
false;
162 private $_middleBytes;
170 private $_fileAtoms = array();
178 private $_lastAtom =
null;
186 private $_successfullyParsed =
false;
194 private $_fileValid =
null;
216 if (!file_exists($filename) || !($this->_fp = fopen($filename,
'rb'))) {
217 return 'cannot open file: '.$filename;
221 $this->_filesize = filesize($filename);
224 if ($this->_filesize == 0) {
225 return 'file '.$filename.
' seems to be empty!';
229 while (!feof($this->_fp) && ftell($this->_fp) < $this->_filesize) {
230 $this->_fileAtoms[] = $this->_parseAtomsFromInput();
233 $moovEOFCheck = $this->_moovAtomAtEOF();
234 if ($moovEOFCheck !==
true) {
235 return $moovEOFCheck;
238 $this->_successfullyParsed =
true;
257 private function _parseAtomsFromInput()
260 if ((isset($this->_fileAtoms[count($this->_fileAtoms) - 1]))) {
261 $lastAtom = $this->_fileAtoms[count($this->_fileAtoms) - 1];
262 $position = $lastAtom->getSize() + $lastAtom->getOffset();
263 if (ftell($this->_fp) <= $position) {
264 fseek($this->_fp, $position);
269 $atomData = unpack(
'NtotalSize/NboxType', fread($this->_fp, 8));
271 $offset = ftell($this->_fp) - 8;
272 $size = $atomData[
'totalSize'];
273 $type = pack(
'N', $atomData[
'boxType']);
277 $highInt = $atomData[
'totalSize'];
278 $size = ($highInt << 32) | $atomData[
'totalSize'];
282 fseek($this->_fp, ($size + $offset));
300 private function _isValidQTFile()
303 if (!$this->_successfullyParsed) {
304 return 'please open a file first!';
308 $firstAtom = $this->_fileAtoms[0];
312 $this->_fileValid =
false;
313 return 'encountered non-QT top-level atom (is this a Quicktime file?)';
317 $lastAtom = $this->_fileAtoms[count($this->_fileAtoms) - 1];
320 $this->_fileValid =
false;
321 return 'encountered non-QT top-level atom (is this a Quicktime file?';
325 return $this->_fileValid =
true;
340 private function _moovAtomAtEOF()
342 if ($this->_fileAtoms[count($this->_fileAtoms) - 1]->getType() !=
Atom::MOOV_ATOM) {
343 return 'The moov-atom isn\'t located at the end of the file, the file is allready ready for progressive download or it is a invalid file';
367 $this->_outputFile = $filename;
392 $result = $this->
setInput($filename);
393 if ($result !==
true) {
397 if (is_null($outputFilename) && $overwrite ===
true) {
398 $fileResult = $filename;
400 $fileResult = $outputFilename;
405 if ($result !==
true) {
410 $result = $this->
fix();
411 if ($result !==
true) {
434 if (!$this->_successfullyParsed) {
435 return 'please open a file first! (syntax: '.__CLASS__.
'->setInput($filename);)';
438 if (!$this->_outputFile) {
439 return 'please set an outputfile first! (syntax: '.__CLASS__.
'->setOutput($file);)';
443 if (!$this->_moovAtomAtEOF()) {
444 return 'nothing to do! moov allready at begin of file!';
453 $result = $this->_readBytes();
454 if ($result !==
true) {
459 $result = $this->_swapIndex();
460 if ($result !==
true) {
465 $result = $this->_writeFile();
466 if ($result !==
true) {
486 private function _readBytes()
489 for ($atom = 0; $atom < count($this->_fileAtoms); $atom++) {
492 $currentAtom = $this->_fileAtoms[$atom];
493 $currentAtomType = $currentAtom->getType();
498 fseek($this->_fp, 0);
499 $bytes = fread($this->_fp, $currentAtom->getSize());
500 $this->_ftypBytes->writeBytes($bytes);
503 $bytes = fread($this->_fp, $currentAtom->getSize());
504 $this->_moovBytes->writeBytes($bytes);
507 $bytes = fread($this->_fp, $currentAtom->getSize());
508 $this->_middleBytes->writeBytes($bytes);
527 private function _swapIndex()
529 $moovSize = $this->_moovBytes->bytesAvailable();
535 $compressionCheck = $this->_moovBytes->readBytes(12, 4);
538 throw new Exception(
'compressed MP4/QT-file can\'t do this file: '.$file);
542 $metaDataOffsets = array();
543 $metaDataStrings = array();
544 $metaDataCurrentLevel = 0;
546 $moovStartOffset = 12;
548 for ($i = $moovStartOffset; $i < $moovSize - $moovStartOffset; $i++) {
549 $moovAType = $this->_moovBytes->readUTFBytes($i, 4);
553 $moovASize = $this->_moovBytes->readUnsignedInt($i - 4);
555 if (($moovASize > 8) && ($moovASize + $i < ($moovSize - $moovStartOffset))) {
558 $containerLength = 0;
559 $containerString = $moovAType;
561 for ($mi = count($metaDataOffsets) - 1; $mi > - 1; $mi--) {
563 $containerLength = $metaDataOffsets[$mi];
565 if ($i - $moovStartOffset < $containerLength && $i - $moovStartOffset + $moovASize > $containerLength) {
566 throw new Exception(
'bad atom nested size');
569 if ($i - $moovStartOffset == $containerLength) {
570 array_pop($metaDataOffsets);
571 array_pop($metaDataStrings);
573 $containerString = $metaDataStrings[$mi].
".".$containerString;
577 if (($i - $moovStartOffset) <= $containerLength) {
578 array_push($metaDataOffsets, ($i - $moovStartOffset + $moovASize));
579 array_push($metaDataStrings, $moovAType);
585 $i += $moovASize - 4;
588 catch(Exception $e) {
589 echo
'EXCEPTION: '.$e->getMessage();
596 $moovASize = $this->_moovBytes->readUnsignedInt($i - 4);
598 if ($i + $moovASize - $moovStartOffset > $moovSize) {
599 throw new Exception(
'bad atom size');
603 $offsetCount = $this->_moovBytes->readUnsignedInt($i + 8);
605 for ($j = 0; $j < $offsetCount; $j++) {
606 $position = ($i + 12 + $j * 4);
608 $currentOffset = $this->_moovBytes->readUnsignedInt($position);
613 $currentOffset += $moovSize;
617 $i += $moovASize - 4;
620 $moovASize = $this->_moovBytes->readUnsignedInt($i - 4);
622 if ($i + $moovASize - $moovStartOffset > $moovSize) {
623 throw new Exception(
'bad atom size');
627 $offsetCount = $this->_moovBytes->readDouble($i + 8);
629 for ($j2 = 0; $j2 < $offsetCount; $j2++) {
630 $position = ($i + 12 + $j * 8);
632 $currentOffset = $this->_moovBytes->readUnsignedInt($position);
637 $currentOffset += $moovSize;
642 $i += $moovASize - 4;
661 private function _writeFile()
664 if (file_exists($this->_outputFile)) {
672 if (!@unlink($this->_outputFile)) {
673 return 'error deleting file: '.$this->_outputFile.
' outputfile "'.$this->_outputFile.
'" exists (overwite = true)!';
678 if (!$fh = fopen($this->_outputFile,
'wb+')) {
679 return 'error opening outputfile: '.$this->_outputFile.
' for wb+ access!';
683 if (!fwrite($fh, $this->_ftypBytes->readAllBytes())) {
684 return 'error writing ftyp-atom to outputfile: '.$this->_outputFile;
688 if (!fwrite($fh, $this->_moovBytes->readAllBytes())) {
689 return 'error writing moov-atom to outputfile: '.$this->_outputFile;
693 if (!fwrite($fh, $this->_middleBytes->readAllBytes())) {
694 return 'error writing other atom(s) to outputfile: '.$this->_outputFile;
720 public static function factory($offset, $size, $type)
726 $atom->setOffset($offset);
727 $atom->setSize($size);
728 $atom->setType($type);
745 private function _reset()
747 $this->_fileAtoms = array();
748 $this->_lastAtom =
null;
749 $this->_successfullyParsed =
false;
750 $this->_fileValid =
null;
771 if (is_null(self::$_instance)) {
772 self::$_instance =
new self();
774 return self::$_instance;
790 private function __clone()