00001 <?php
00010 class GIFMetadataExtractor {
00011 static $gif_frame_sep;
00012 static $gif_extension_sep;
00013 static $gif_term;
00014
00015 static function getMetadata( $filename ) {
00016 self::$gif_frame_sep = pack( "C", ord("," ) );
00017 self::$gif_extension_sep = pack( "C", ord("!" ) );
00018 self::$gif_term = pack( "C", ord(";" ) );
00019
00020 $frameCount = 0;
00021 $duration = 0.0;
00022 $isLooped = false;
00023
00024 if (!$filename)
00025 throw new Exception( "No file name specified" );
00026 elseif ( !file_exists($filename) || is_dir($filename) )
00027 throw new Exception( "File $filename does not exist" );
00028
00029 $fh = fopen( $filename, 'r' );
00030
00031 if (!$fh)
00032 throw new Exception( "Unable to open file $filename" );
00033
00034
00035 $buf = fread( $fh, 6 );
00036 if ( !($buf == 'GIF87a' || $buf == 'GIF89a') ) {
00037 throw new Exception( "Not a valid GIF file; header: $buf" );
00038 }
00039
00040
00041 fread( $fh, 4 );
00042
00043
00044 $buf = fread( $fh, 1 );
00045 $bpp = self::decodeBPP( $buf );
00046
00047
00048 fread( $fh, 2 );
00049
00050
00051 self::readGCT( $fh, $bpp );
00052
00053 while( !feof( $fh ) ) {
00054 $buf = fread( $fh, 1 );
00055
00056 if ($buf == self::$gif_frame_sep) {
00057
00058 $frameCount++;
00059
00060 ## Skip bounding box
00061 fread( $fh, 8 );
00062
00063 ## Read BPP
00064 $buf = fread( $fh, 1 );
00065 $bpp = self::decodeBPP( $buf );
00066
00067 ## Read GCT
00068 self::readGCT( $fh, $bpp );
00069 fread( $fh, 1 );
00070 self::skipBlock( $fh );
00071 } elseif ( $buf == self::$gif_extension_sep ) {
00072 $buf = fread( $fh, 1 );
00073 $extension_code = unpack( 'C', $buf );
00074 $extension_code = $extension_code[1];
00075
00076 if ($extension_code == 0xF9) {
00077
00078 fread( $fh, 1 );
00079
00080 fread( $fh, 1 );
00081
00082 $buf = fread( $fh, 2 );
00083 $delay = unpack( 'v', $buf );
00084 $delay = $delay[1];
00085 $duration += $delay * 0.01;
00086
00087 fread( $fh, 1 );
00088
00089 $term = fread( $fh, 1 );
00090 $term = unpack( 'C', $term );
00091 $term = $term[1];
00092 if ($term != 0 )
00093 throw new Exception( "Malformed Graphics Control Extension block" );
00094 } elseif ($extension_code == 0xFF) {
00095
00096 $blockLength = fread( $fh, 1 );
00097 $blockLength = unpack( 'C', $blockLength );
00098 $blockLength = $blockLength[1];
00099 $data = fread( $fh, $blockLength );
00100
00101
00102 if ($blockLength != 11 || $data != 'NETSCAPE2.0') {
00103 fseek( $fh, -($blockLength + 1), SEEK_CUR );
00104 self::skipBlock( $fh );
00105 continue;
00106 }
00107
00108 $data = fread( $fh, 2 );
00109
00110 if ($data != "\x03\x01") {
00111 throw new Exception( "Expected \x03\x01, got $data" );
00112 }
00113
00114
00115 $loopData = fread( $fh, 2 );
00116 $loopData = unpack( 'v', $loopData );
00117 $loopCount = $loopData[1];
00118
00119 if ($loopCount != 1) {
00120 $isLooped = true;
00121 }
00122
00123
00124 fread( $fh, 1 );
00125 } else {
00126 self::skipBlock( $fh );
00127 }
00128 } elseif ( $buf == self::$gif_term ) {
00129 break;
00130 } else {
00131 $byte = unpack( 'C', $buf );
00132 $byte = $byte[1];
00133 throw new Exception( "At position: ".ftell($fh). ", Unknown byte ".$byte );
00134 }
00135 }
00136
00137 return array(
00138 'frameCount' => $frameCount,
00139 'looped' => $isLooped,
00140 'duration' => $duration
00141 );
00142
00143 }
00144
00145 static function readGCT( $fh, $bpp ) {
00146 if ($bpp > 0) {
00147 for( $i=1; $i<=pow(2,$bpp); ++$i ) {
00148 fread( $fh, 3 );
00149 }
00150 }
00151 }
00152
00153 static function decodeBPP( $data ) {
00154 $buf = unpack( 'C', $data );
00155 $buf = $buf[1];
00156 $bpp = ( $buf & 7 ) + 1;
00157 $buf >>= 7;
00158
00159 $have_map = $buf & 1;
00160
00161 return $have_map ? $bpp : 0;
00162 }
00163
00164 static function skipBlock( $fh ) {
00165 while ( !feof( $fh ) ) {
00166 $buf = fread( $fh, 1 );
00167 $block_len = unpack( 'C', $buf );
00168 $block_len = $block_len[1];
00169 if ($block_len == 0)
00170 return;
00171 fread( $fh, $block_len );
00172 }
00173 }
00174
00175 }