Created
September 20, 2012 16:11
-
-
Save Thinkscape/3756836 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* qtfaststart.php v0.1 by Valentin Schmidt | |
* based on qt-faststart.c v0.2 by Mike Melanson ([email protected]) | |
* | |
* This file is placed in the public domain. Use the program however you | |
* see fit. | |
* | |
* This utility rearranges a Quicktime file such that the moov atom | |
* is in front of the data, thus facilitating network streaming. | |
* | |
* Notes: Quicktime files can come in many configurations of top-level | |
* atoms. This utility stipulates that the very last atom in the file needs | |
* to be a moov atom. When given such a file, this utility will rearrange | |
* the top-level atoms by shifting the moov atom from the back of the file | |
* to the front, and patch the chunk offsets along the way. This utility | |
* presently only operates on uncompressed moov atoms. | |
*/ | |
define('MOOV_ATOM', 'moov'); | |
define('FTYP_ATOM', 'ftyp'); | |
define('CMOV_ATOM', 'cmov'); | |
define('STCO_ATOM', 'stco'); | |
define('CO64_ATOM', 'co64'); | |
define('ATOM_PREAMBLE_SIZE', 8); | |
define('COPY_BUFFER_SIZE', 1024); | |
function | |
BE_32($x, $offset=0){ | |
return | |
(ord($x[$offset+0]) << 24) | | |
(ord($x[$offset+1]) << 16) | | |
(ord($x[$offset+2]) << 8) | | |
(ord($x[$offset+3])); | |
} | |
function | |
BE_64($x, $offset=0){ | |
return | |
(ord($x[$offset+0]) << 56) | | |
(ord($x[$offset+1]) << 48) | | |
(ord($x[$offset+2]) << 40) | | |
(ord($x[$offset+3]) << 32) | | |
(ord($x[$offset+4]) << 24) | | |
(ord($x[$offset+5]) << 16) | | |
(ord($x[$offset+6]) << 8) | | |
(ord($x[$offset+7])); | |
} | |
function | |
status($s){ | |
echo $s.'<br />'; | |
} | |
function | |
qtfaststart($inputVideoFile, $outputVideoFile){ | |
$atom_offset = 0; | |
$atom_type = ''; | |
$infile = fopen($inputVideoFile, "rb"); | |
if (!$infile) { | |
return 0; | |
} | |
/* traverse through the atoms in the file to make sure that 'moov' is at the end */ | |
while (!feof($infile)) { | |
if (($atom_bytes = fread($infile, ATOM_PREAMBLE_SIZE)) == FALSE) { | |
break; | |
} | |
$atom_size = BE_32($atom_bytes, 0); | |
$atom_type = substr($atom_bytes, 4, 4); | |
/* keep ftyp atom */ | |
if ($atom_type == FTYP_ATOM) { | |
$ftyp_atom_size = $atom_size; | |
fseek($infile, -ATOM_PREAMBLE_SIZE, SEEK_CUR); | |
$ftyp_atom = fread($infile, $ftyp_atom_size); | |
$start_offset = ftell($infile); | |
} else { | |
/* 64-bit special case */ | |
if ($atom_size == 1) { | |
if (($atom_bytes = fread($infile, ATOM_PREAMBLE_SIZE)) == FALSE) { | |
break; | |
} | |
$atom_size = BE_64($atom_bytes, 0); | |
fseek($infile, $atom_size - ATOM_PREAMBLE_SIZE * 2, SEEK_CUR); | |
} else { | |
fseek($infile, $atom_size - ATOM_PREAMBLE_SIZE, SEEK_CUR); | |
} | |
} | |
$atom_offset += $atom_size; | |
/* The atom header is 8 (or 16 bytes), if the atom size (which | |
* includes these 8 or 16 bytes) is less than that, we won't be | |
* able to continue scanning sensibly after this atom, so break. */ | |
if ($atom_size < 8) break; | |
} | |
if ( | |
$atom_type != MOOV_ATOM) { | |
status("last atom in file was not a moov atom"); | |
fclose($infile); | |
return 0; | |
} | |
/* moov atom was, in fact, the last atom in the chunk; load the whole moov atom */ | |
fseek($infile, -$atom_size, SEEK_END); | |
$last_offset = ftell($infile); | |
$moov_atom_size = $atom_size; | |
if (($moov_atom = fread($infile, $moov_atom_size)) == FALSE) { | |
fclose($infile); | |
return 0; | |
} | |
/* this utility does not support compressed atoms yet, so disqualify | |
* files with compressed QT atoms */ | |
if (substr($moov_atom, 12, 4) == CMOV_ATOM) { | |
status("this utility does not support compressed moov atoms yet"); | |
fclose($infile); | |
return 0; | |
} | |
/* close; will be re-opened later */ | |
fclose($infile); | |
/* crawl through the moov chunk in search of stco or co64 atoms */ | |
for ($i = 4; $i < $moov_atom_size - 4; $i++) { | |
$atom_type = substr($moov_atom, $i, 4); | |
if ($atom_type == STCO_ATOM) { | |
status(" patching stco atom"); | |
$atom_size = BE_32($moov_atom, $i - 4); | |
if ($i + $atom_size - 4 > $moov_atom_size) { | |
status(" bad atom size"); | |
return 0; | |
} | |
$offset_count = BE_32($moov_atom, $i + 8); | |
for ($j = 0; $j < $offset_count; $j++) { | |
$current_offset = BE_32($moov_atom, $i + 12 + $j * 4); | |
$current_offset += $moov_atom_size; | |
$moov_atom[$i + 12 + $j * 4 + 0] = chr( ($current_offset >> 24) & 0xFF); | |
$moov_atom[$i + 12 + $j * 4 + 1] = chr( ($current_offset >> 16) & 0xFF); | |
$moov_atom[$i + 12 + $j * 4 + 2] = chr( ($current_offset >> 8) & 0xFF); | |
$moov_atom[$i + 12 + $j * 4 + 3] = chr( ($current_offset >> 0) & 0xFF); | |
} | |
$i += $atom_size - 4; | |
} else if ($atom_type == CO64_ATOM) { | |
status(" patching co64 atom"); | |
$atom_size = BE_32($moov_atom, $i - 4); | |
if ($i + $atom_size - 4 > $moov_atom_size) { | |
status(" bad atom size"); | |
return 0; | |
} | |
$offset_count = BE_32($moov_atom, $i + 8); | |
for ($j = 0; $j < $offset_count; $j++) { | |
$current_offset = BE_64($moov_atom, $i + 12 + $j * 8); | |
$current_offset += $moov_atom_size; | |
$moov_atom[$i + 12 + $j * 8 + 0] = chr( ($current_offset >> 56) & 0xFF); | |
$moov_atom[$i + 12 + $j * 8 + 1] = chr( ($current_offset >> 48) & 0xFF); | |
$moov_atom[$i + 12 + $j * 8 + 2] = chr( ($current_offset >> 40) & 0xFF); | |
$moov_atom[$i + 12 + $j * 8 + 3] = chr( ($current_offset >> 32) & 0xFF); | |
$moov_atom[$i + 12 + $j * 8 + 4] = chr( ($current_offset >> 24) & 0xFF); | |
$moov_atom[$i + 12 + $j * 8 + 5] = chr( ($current_offset >> 16) & 0xFF); | |
$moov_atom[$i + 12 + $j * 8 + 6] = chr( ($current_offset >> 8) & 0xFF); | |
$moov_atom[$i + 12 + $j * 8 + 7] = chr( ($current_offset >> 0) & 0xFF); | |
} | |
$i += $atom_size - 4; | |
} | |
} | |
/* re-open the input file and open the output file */ | |
$infile = fopen($inputVideoFile, "rb"); | |
if (!$infile) { | |
return 0; | |
} | |
if ( | |
$start_offset > 0) { /* seek after ftyp atom */ | |
fseek($infile, $start_offset, SEEK_SET); | |
$last_offset -= $start_offset; | |
} | |
$outfile = fopen($outputVideoFile, "wb"); | |
if (!$outfile) { | |
fclose($infile); | |
return 0; | |
} | |
/* dump the same ftyp atom */ | |
if ($ftyp_atom_size > 0) { | |
status(" writing ftyp atom"); | |
if ((fwrite($outfile, $ftyp_atom, $ftyp_atom_size)) == FALSE) { | |
fclose($infile); | |
fclose($outfile); | |
return 0; | |
} | |
} | |
/* dump the new moov atom */ | |
status(" writing moov atom"); | |
if ((fwrite($outfile, $moov_atom, $moov_atom_size)) == FALSE) { | |
fclose($infile); | |
fclose($outfile); | |
return 0; | |
} | |
/* copy the remainder of the infile, from offset 0 -> last_offset - 1 */ | |
status(" copying rest of file"); | |
while ($last_offset) { | |
if ($last_offset > COPY_BUFFER_SIZE) | |
$bytes_to_copy = COPY_BUFFER_SIZE; | |
else | |
$bytes_to_copy = $last_offset; | |
if (($copy_buffer = fread($infile, $bytes_to_copy)) == FALSE) { | |
fclose($infile); | |
fclose($outfile); | |
return 0; | |
} | |
if ((fwrite($outfile, $copy_buffer, $bytes_to_copy)) == FALSE) { | |
fclose($infile); | |
fclose($outfile); | |
return 0; | |
} | |
$last_offset -= $bytes_to_copy; | |
} | |
fclose($infile); | |
fclose($outfile); | |
return 1; | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment