= 60) { $secs -= 60; $mins++; } return sprintf("%d:%02d", $mins, $secs); } } function ind($n) { for ($i = INDSIZE; $i < $n; $i++) { dprintf("-"); } } function ParseTag($metaItemKey, $metaItemData, &$tags) { global $indent; ind($indent+INDSIZE); switch($metaItemKey) { case "İnam": dprintf("title: %s\n",$metaItemData); $tags['title'] = $metaItemData; break; case "İART": dprintf("artist: %s\n",$metaItemData); $tags['artist'] = $metaItemData; break; case "İalb": dprintf("album: %s\n",$metaItemData); $tags['album'] = $metaItemData; break; case "gnre": $tags['genrename'] = DecodeGenre(ord($metaItemData{1})); $tags['genrenum'] = ord($metaItemData{1}); dprintf("genre: %s (%d)\n", $tags['genrename'], $tags['genrenum']); break; case "trkn": dprintf("track: %d\n",ord($metaItemData{3})); $tags['track'] = ord($metaItemData{3}); break; case "disk": dprintf("disk: %d of %d\n",ord($metaItemData{3}), ord($metaItemData{5})); break; case "İday": dprintf("year: %s\n",$metaItemData); $tags['year'] = $metaItemData; break; default: $commanumber = number_format(strlen($metaItemData)); dprintf("other: %s: %s (%s bytes)\n", $metaItemKey, substr($metaItemData, 0, 10), $commanumber); break; } // switch } // parsetag function DecodeGenre($num) { global $GenreList; return (isset($GenreList[$num-1]) ? $GenreList[$num-1] : "unknown"); } function ParseAtom($f, $firstOff, $stopAt, &$tags) { global $AtomType; global $indent; $indent += INDSIZE; fseek($f, 0, SEEK_END); $end = ftell($f); $off = $firstOff; while($off < $stopAt) { fseek($f, $off, SEEK_SET); $AtomHeader = fread($f, 8); $atomsize = BigEndian2Int(substr($AtomHeader, 0, 4)); $atomname = substr($AtomHeader, 4, 4); ind($indent); dprintf("%s (%s bytes)\n", $atomname, number_format($atomsize)); if(($off + $atomsize) > $end) { printf("atom at %d claims %d bytes, end = %d)\n", $off, $atomsize, $end); return FALSE; } $tags['atoms'][] = array('name' => $atomname, 'size' => $atomsize, 'pos' => ftell($f)-8, 'level' => $indent/2 > 0 ? $indent/2 : 0); if(strcmp($atomname, "moov") == 0) $tags['moovpos'] = ftell($f) - 8; if(strcmp($atomname, "free") == 0) { $tags['freepos'][] = ftell($f) - 8; $tags['freesize'][] = $atomsize; } if(strcmp($atomname, "udta") == 0) { $tags['udtapos'][] = ftell($f) - 8; $tags['udtasize'][] = $atomsize; } if(strcmp($atomname, "mdat") == 0) { $tags['mdatsize'] = $atomsize; } // if it's a container atom, parse the contents of the atom if(in_array($atomname, $AtomType)) { ParseAtom($f, ftell($f), $off+$atomsize, $tags); } // meta atom contains tags and some other data if(strcmp($atomname, "meta") == 0) { $atomdata = fread($f, $atomsize-4); $NextTagPosition = strpos($atomdata, 'İ'); while ($NextTagPosition < strlen($atomdata)) { $metaItemSize = BigEndian2Int(substr($atomdata, $NextTagPosition - 4, 4)) - 4; $metaItemRaw = substr($atomdata, $NextTagPosition, $metaItemSize); $metaItemKey = substr($metaItemRaw, 0, 4); $metaItemData = substr($metaItemRaw, 20); $NextTagPosition += $metaItemSize + 4; ParseTag($metaItemKey, $metaItemData, $tags); } // while } // if meta // mdhd has data for calculating playtime if(strcmp($atomname, "mdhd") == 0) { $atomdata = fread($f, 20); $time_scale = BigEndian2Int(substr($atomdata, 12, 4)); $duration = BigEndian2Int(substr($atomdata, 16, 4)); $tags['playtime'] = $duration / $time_scale; } // if mdhd // stsd has data for sample, channels and codec if(strcmp($atomname, "stsd") == 0) { $atomdata = fread($f, $atomsize-8); stsd($tags, $atomdata); } // bump offest and continue if($atomsize == 0) { // a 0 byte atom is the end $off = $stopAt; } else { $off += $atomsize; } } // while $indent -= INDSIZE; return TRUE; } // parseatom function stsd(&$tags, $atomdata) { $num = BigEndian2Int(substr($atomdata, 4, 4)); $stsdOff = 8; for ($i = 0; $i < $num; $i++) { $size = BigEndian2Int(substr($atomdata, $stsdOff, 4)); $stsdOff += 4; $data_format = substr($atomdata, $stsdOff, 4); $stsdOff += 12; $data = substr($atomdata, $stsdOff, ($size - 4 - 12)); $stsdOff += ($size - 4 - 12); $encoder_vendor = substr($data, 4, 4); if ($encoder_vendor == chr(0).chr(0).chr(0).chr(0)) { $tags['sample_rate'] = FixedPoint16_16(substr($data, 16, 4)); $tags['channels'] = BigEndian2Int(substr($data, 8, 2)); if(strcmp($data_format, "mp4a") == 0) $tags['codec'] = "AAC"; else $tags['codec'] = $data_format; } // if } // for } //stsd function printtags($tags) { printf("Format: %s\n", $tags['codec']); printf("Details: "); if(isset($tags['sample_rate'])) { printf("%s Hz ", $tags['sample_rate']); } if(isset($tags['channels'])) { if($tags['channels'] == 2) { printf("Stereo, "); } else if($tags['channels'] == 1) { printf("Mono, "); } else if($tags['channels'] !== 0) { printf("%s, ", $tags['channels']); } } $bitrate = (($tags['mdatsize'] * 8) / $tags['playtime']) / 1000; printf("%d kbps, playtime %s\n", $bitrate, TimeString($tags['playtime']) ); printf("Artist: %s\n", $tags['artist']); printf("Album: %s\n", $tags['album']); printf("Title: %s\n", $tags['title']); printf("Track: %d\n", $tags['track']); printf("Genre: %s\n", $tags['genrename']); printf("Year: %s\n", $tags['year']); } function OptFree($infile, &$tags) { $f = fopen($infile, "r+b"); $top = ""; $changed = 0; $num = count($tags['atoms']); for($i = 0; $i < $num; $i++) { if(($tags['atoms'][$i]['level']-1) == 0) { $top = $tags['atoms'][$i]['name']; continue; } if( (strcmp($top, "moov") == 0) && (($tags['atoms'][$i]['level']-1) == 1) && (strcmp($tags['atoms'][$i]['name'], "free") == 0) ) { printf("free: %s bytes at %s\n", number_format($tags['atoms'][$i]['size']), number_format($tags['atoms'][$i]['pos']) ); if( ($i+1) < $num && (strcmp($tags['atoms'][$i+1]['name'], 'free') == 0)) { $tags['atoms'][$i+1]['pos'] = $tags['atoms'][$i]['pos']; $tags['atoms'][$i+1]['size'] += $tags['atoms'][$i]['size']; $changed = 1; $last = $i+1; } } } if($changed) { fseek($f, $tags['atoms'][$last]['pos']); fwrite($f, pack("N", $tags['atoms'][$last]['size'])); // printf("wrote %d at %d\n", $tags['atoms'][$last]['size'], $tags['atoms'][$last]['pos']); } fclose($f); } // OptFree // main if(0) { global $debug; if($argc > 2) { if(!strcmp($argv[2], "--debug")) { $debug = 1; } } else { $debug = 0; } $infile = $argv[1]; $f = fopen($infile, "rb"); fseek($f, 0, SEEK_END); $end = ftell($f); ParseAtom($f, 0, $end, $tags); $num = count($tags['atoms']); for($i = 0; $i < $num; $i++) printf("%s%s: %s bytes at %s\n", str_repeat('-',$tags['atoms'][$i]['level']-1), $tags['atoms'][$i]['name'], number_format($tags['atoms'][$i]['size']), number_format($tags['atoms'][$i]['pos']) ); printf("\ntags:\n"); printf("Artist: %s\n", $tags['artist']); printf("Album: %s\n", $tags['album']); printf("Title: %s\n", $tags['title']); printf("Track: %d\n", $tags['track']); printf("Genre: %s\n", $tags['genrename']); printf("Year: %s\n", $tags['year']); $bitrate = (($tags['mdatsize'] * 8) / $tags['playtime']) / 1000; printf("%d kpbs, playtime %s\n", $bitrate, TimeString($tags['playtime']) ); printf("%s %d %d\n", $tags['codec'], $tags['sample_rate'], $tags['channels']); fclose($f); // OptFree($infile, $tags); } // main ?>