Location: PHPKode > projects > Brunhilde Media Server > brunhilde/mpeginfo.php
<?
/*
 *Minor Modifications and Bug Fixes by Corey O. (http://www.coreyo.net)
 *
 *
 * MPEG Information class - fetches headers, id3v1(.1) and id3v2 tags from an mp3 file
 *
 * $Id: mpeginfo.php,v 1.2 2001/05/16 04:22:14 chris Exp $
 *
 * Simple Usage: $mpeginfo = new mpeginfo();
 *               $arr = $mpeginfo->getinfo("somefile.mp3");
 *
 * More detailed documentation can be found at
 * http://www.bolt.cx/mpeginfo/mpeginfo.html
 *
 * Copyright (c) 2001, Christopher Bolt (chris at bolt dot cx). All rights reserved.
 *
 * LICENSE
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided
 * that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *    following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 *    the following disclaimer in the documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

class mpeginfo {
  var $fp, $size;

  // More info at http://www.id3.org/frames.html and http://www.id3.org/id3v2.4.0-frames.txt
  var $v2frames = array(
    "AENC" => "Audio encryption",
    "APIC" => "Attached picture",
    "ASPI" => "Audio seek point index",
    "COMM" => "Comments",
    "COMR" => "Commercial frame",
    "ENCR" => "Encryption method registration",
    "EQU2" => "Equalisation (2)",
    "ETCO" => "Event timing codes",
    "GEOB" => "General encapsulated object",
    "GRID" => "Group identification registration",
    "LINK" => "Linked information",
    "MCDI" => "Music CD identifier",
    "MLLT" => "MPEG location lookup table",
    "OWNE" => "Ownership frame",
    "PRIV" => "Private frame",
    "PCNT" => "Play counter",
    "POPM" => "Popularimeter",
    "POSS" => "Position synchronisation frame",
    "RBUF" => "Recommended buffer size",
    "RVA2" => "Relative volume adjustment (2)",
    "RVRB" => "Reverb",
    "SEEK" => "Seek frame",
    "SIGN" => "Signature frame",
    "SYLT" => "Synchronised lyric/text",
    "SYTC" => "Synchronised tempo codes",
    "TALB" => "Album/Movie/Show title",
    "TBPM" => "BPM (beats per minute)",
    "TCOM" => "Composer",
    "TCON" => "Content type",
    "TCOP" => "Copyright message",
    "TDEN" => "Encoding time",
    "TDLY" => "Playlist delay",
    "TDOR" => "Original release time",
    "TDRC" => "Recording time",
    "TDRL" => "Release time",
    "TDTG" => "Tagging time",
    "TENC" => "Encoded by",
    "TEXT" => "Lyricist/Text writer",
    "TFLT" => "File type",
    "TIPL" => "Involved people list",
    "TIT1" => "Content group description",
    "TIT2" => "Title/songname/content description",
    "TIT3" => "Subtitle/Description refinement",
    "TKEY" => "Initial key",
    "TLAN" => "Language(s)",
    "TLEN" => "Length",
    "TMCL" => "Musician credits list",
    "TMED" => "Media type",
    "TMOO" => "Mood",
    "TOAL" => "Original album/movie/show title",
    "TOFN" => "Original filename",
    "TOLY" => "Original lyricist(s)/text writer(s)",
    "TOPE" => "Original artist(s)/performer(s)",
    "TOWN" => "File owner/licensee",
    "TPE1" => "Lead performer(s)/Soloist(s)",
    "TPE2" => "Band/orchestra/accompaniment",
    "TPE3" => "Conductor/performer refinement",
    "TPE4" => "Interpreted, remixed, or otherwise modified by",
    "TPOS" => "Part of a set",
    "TPRO" => "Produced notice",
    "TPUB" => "Publisher",
    "TRCK" => "Track number/Position in set",
    "TRSN" => "Internet radio station name",
    "TRSO" => "Internet radio station owner",
    "TSOA" => "Album sort order",
    "TSOP" => "Performer sort order",
    "TSOT" => "Title sort order",
    "TSRC" => "ISRC (international standard recording code)",
    "TSSE" => "Software/Hardware and settings used for encoding",
    "TSST" => "Set subtitle",
    "TXXX" => "User defined text information frame",
    "UFID" => "Unique file identifier",
    "USER" => "Terms of use",
    "USLT" => "Unsynchronised lyric/text transcription",
    "WCOM" => "Commercial information",
    "WCOP" => "Copyright/Legal information",
    "WOAF" => "Official audio file webpage",
    "WOAR" => "Official artist/performer webpage",
    "WOAS" => "Official audio source webpage",
    "WORS" => "Official Internet radio station homepage",
    "WPAY" => "Payment",
    "WPUB" => "Publishers official webpage",
    "WXXX" => "User defined URL link frame"
  );
  var $v2tov1 = array(
    "TIT2" => "title",
    "TPE1" => "artist",
    "TALB" => "album",
    "TYER" => "year",
    "COMM" => "comments",
    "TRCK" => "track",
    "TCON" => "genre"
  );
  var $genre = array(
    "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age",
    "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska",
    "Death Metal", "Pranks","Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion",
    "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alt. Rock",
    "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic",
    "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock","Comedy", "Cult",
    "Gangsta Rap", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave",
    "Psychedelic", "Rave", "Showtunes","Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro",
    "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing","Fast-Fusion", "Bebob",
    "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock",
    "Symphonic Rock", "SlowRock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
    "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam",
    "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock",
    "DrumSolo", "A Cappella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror",
    "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", "Heavy Metal", "Black Metal",
    "Crossover", "Contemporary Christian", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop",
    "Synthpop"
  );
  // http://www.id3.org/mp3frame.html
  var $versions = array("00" => 2.5, "01" => 0, "10" => 2, "11" => 1);
  var $layers   = array("00" => 0,   "01" => 3, "10" => 2, "11" => 1);
  var $cmodes   = array(
    "00" => "Stereo",
    "01" => "Joint Stereo",
    "10" => "Dual Channel",
    "11" => "Mono"
  );
  var $emphases = array(
    "00" => "none",
    "01" => "50/15 ms",
    "10" => "reserved",
    "11" => "CCIT J.17"
  );
  var $bitrates = array(
    "0001" => array(1 => 32,  2 => 32,  3 => 32,  4 => 32,  5 => 32,  6 => 8),
    "0010" => array(1 => 64,  2 => 48,  3 => 40,  4 => 64,  5 => 48,  6 => 16),
    "0011" => array(1 => 96,  2 => 56,  3 => 48,  4 => 96,  5 => 56,  6 => 24),
    "0100" => array(1 => 128, 2 => 64,  3 => 56,  4 => 128, 5 => 64,  6 => 32),
    "0101" => array(1 => 160, 2 => 80,  3 => 64,  4 => 160, 5 => 80,  6 => 40),
    "0110" => array(1 => 192, 2 => 96,  3 => 80,  4 => 192, 5 => 96,  6 => 48),
    "0111" => array(1 => 224, 2 => 112, 3 => 96,  4 => 224, 5 => 112, 6 => 56),
    "1000" => array(1 => 256, 2 => 128, 3 => 112, 4 => 256, 5 => 128, 6 => 64),
    "1001" => array(1 => 288, 2 => 160, 3 => 128, 4 => 288, 5 => 144, 6 => 80),
    "1010" => array(1 => 320, 2 => 192, 3 => 160, 4 => 320, 5 => 160, 6 => 96),
    "1011" => array(1 => 352, 2 => 224, 3 => 192, 4 => 352, 5 => 176, 6 => 112),
    "1100" => array(1 => 384, 2 => 256, 3 => 224, 4 => 384, 5 => 192, 6 => 128),
    "1101" => array(1 => 416, 2 => 320, 3 => 256, 4 => 416, 5 => 224, 6 => 144),
    "1110" => array(1 => 448, 2 => 384, 3 => 320, 4 => 448, 5 => 256, 6 => 160)
  );
  var $samplerates = array(
    1   => array("00" => 44100, "01" => 48000, "10" => 32000, "11" => "reserved"),
    2   => array("00" => 22050, "01" => 24000, "10" => 16000, "11" => "reserved"),
    2.5 => array("00" => 11025, "01" => 12000, "10" => 8000,  "11" => "reserved")
  );

  function mpeginfo($file = "") {
    if (!empty($file))
      list($this->fp, $this->size) = $this->getfp($file);
  }

  function getfp($file) {
    if (empty($file)) {
      $fp = $this->fp;
      $size = $this->size;
    }
    elseif (gettype($file) != "resource") {
      $fp = fopen($file, "rb");
      if (!$fp || !flock($fp, 1)) return;
      $size = filesize($file);
    }
    else {
      $fp = $file;
      $stats = fstat($file);
      $size = $stats["size"];
    }
    return array($fp, $size);
  }

  function stripnulls($string) {
    for ($i = 0; $i < strlen($string); $i++)
      if (ord(substr($string, $i, 1)) == 0) return(trim(substr($string, 0, $i)));
    return(trim($string));
  }

  function headers($file = "") {
    global $genre, $versions, $layers, $cmodes, $emphases, $bitrates, $samplerates;
    list($fp, $size) = $this->getfp($file);

    while (!feof($fp)) {
      $tmp = fgetc($fp);
      if (ord($tmp) == 255) {
        $tmp = fgetc($fp);
        if (substr((decbin(ord($tmp))), 0, 3) == "111")
          break;
      }
    }

    if (feof($fp))
      return false;

    $headers["filesize"] = $size;
    $inf = decbin(ord($tmp));
    $inf = sprintf("%08d", $inf);
    $bitstream = $inf;
    $tmp = fgetc($fp);
    $inf = decbin(ord($tmp));
    $inf = sprintf("%08d", $inf);
    $bitstream = $bitstream . $inf;
    $tmp = fgetc($fp);
    $inf = decbin(ord($tmp));
    $inf = sprintf("%08d", $inf);
    $bitstream = $bitstream . $inf;

    $headers["version"]    = $this->versions[substr($bitstream, 3, 2)];
    $headers["layer"]      = $this->layers[substr($bitstream, 5, 2)];
    $headers["crc"]        = substr($bitstream, 7, 1);
    $lv                    = $headers["layer"] + pow(4, $headers["version"] - 1) - 1;
    $headers["bitrate"]    = $this->bitrates[substr($bitstream, 8, 4)][$lv];
    $headers["samplerate"] = $this->samplerates[$headers["version"]][substr($bitstream, 12, 2)];
    $padding               = substr($bitstream, 14, 1);
    $headers["cmode"]      = $this->cmodes[substr($bitstream, 16, 2)];
    $headers["copyright"]  = substr($bitstream, 20, 1);
    $headers["original"]   = substr($bitstream, 21, 1);
    $headers["emphasis"]   = $this->emphases[substr($bitstream, 22, 2)];

    if ($headers["samplerate"] > 0 && $headers["bitrate"] > 0) {
      if ($headers["layer"] == "1") {
        $headers["frames"] = floor($headers["filesize"] / (floor(((12000 * $headers["bitrate"]) /
                             ($headers["samplerate"] + $padding)) * 4)));
        $headers["length"] = floor((384 / $headers["samplerate"]) * $headers["frames"]);
      }
      else {
        $headers["frames"] = floor($headers["filesize"] / (floor((144000 * $headers["bitrate"]) /
                             $headers["samplerate"])));
        $headers["length"] = floor((1152 / $headers["samplerate"]) * $headers["frames"]);
      }
    }

    fseek($fp, ftell($fp) + 32);
    if (fread($fp, 4) == "Xing") {
      fseek($fp, ftell($fp) + 3);
      $tmp = sprintf("%08d", decbin(ord(fgetc($fp))));
      if (substr($tmp, 7, 1) == 1) {
        $tmp = fread($fp, 4);
        for ($i = 0; $i < 4; $i++)
          $temp[$i] = ord(substr($tmp, $i, 1));
        $flags = (
          (($temp[0] & 255) << 24) |
          (($temp[1] & 255) << 16) |
          (($temp[2] & 255) <<  8) |
          (($temp[3] & 255)      )
        );
        $headers["frames"] = $flags;
        $headers["bitrate"] = intval((($headers["filesize"] / $headers["frames"]) * ($headers["samplerate"] / 1000)) / (($headers["version"] == 1) ? 144 : 72));
     // $headers["length"] = floor((384 / $headers["samplerate"]) * $headers["frames"]);
        $headers["length"] = $headers["frames"] * ((115200/2)*(1+(($headers["version"] == 1) ? 1 : 0))) / $headers["samplerate"];
        $headers["length"] = $headers["length"] / 100;
      }
    }

    if (gettype($file) != "resource" && !empty($file)) fclose($fp);
    return $headers;
  }

  function v1info($file = "") {
    list($fp, $size) = $this->getfp($file);
    fseek($fp, $size - 128);
    $tag = fread($fp, 128);
    if (substr($tag, 0, 3) == "TAG") {
      $headers["title"]      = $this->stripnulls(substr($tag, 3,  30));
      $headers["artist"]     = $this->stripnulls(substr($tag, 33, 30));
      $headers["album"]      = $this->stripnulls(substr($tag, 63, 30));
      $headers["year"]       = $this->stripnulls(substr($tag, 93, 4));
      if (strlen($this->stripnulls($headers["comment"])) <= 28) {
        $headers["track"]    = ord(substr($tag, 126, 1));
        $headers["comments"] = $this->stripnulls(substr($tag, 97, 28));
      }
      else
        $headers["comments"] = $this->stripnulls(substr($tag, 97, 30));
      $headers["genreid"]    = $this->stripnulls(ord(substr($tag, 127, 1)));
      $headers["genre"]      = ($headers["genreid"] >= 0 && $headers["genreid"] <= 147)
                               ? $this->genre[$headers["genreid"]] : "";
    }
    if (gettype($file) != "resource" && !empty($file)) fclose($fp);
    return $headers;
  }

  function v2seek($fp, $hlen, $num, $off) {
    fseek($fp, $off);
    $bytes = fread($fp, $hlen);
    if (!preg_match("/^([A-Z0-9]\{$num})/", $bytes, $matches)) return;
    $id = $matches[1];
    $size = $hlen;
    $bytes = array_reverse(unpack("C$num", substr($bytes, $num, $num)));
    for ($i = 0; $i < $num; $i++)
      $size += $bytes[$i] * pow(256, $i);
    return(array($id, $size));
  }

  function v2info($file = "") {
    list($fp, $size) = $this->getfp($file);
    rewind($fp);
    if (fread($fp, 3) != "ID3") return;
    $version = unpack("c2", fread($fp, 2));
    $flags = fread($fp, 1);
    if ($version[1] == 2) {
      list($unsync, $compression) = unpack("b8", $flags);
      $extheader = 0;
      $experimental = 0;
    }
    else
      list($unsync, $ext_header, $experimental) = unpack("b8", $flags);
    $tagsize = 10;
    $taglength = array_reverse(unpack("C4", fread($fp, 4)));
    for ($i = 0; $i < 4; $i++)
      $tagsize += $taglength[$i] * pow(128, $i);
    $extheadersize = 0;
    if ($extheader > 0) {
      $extheadersize = 10;
      $taglength = array_reverse(unpack("C4", fread($fp, 4)));
      for ($i = 0; $i < 4; $i++)
        $extheadersize += $taglength[$i] * pow(256, $i);
    }
    if ($version[1] < 2)
      return false;
    if ($version[1] == 2) {
      $hlen = 6;
      $num = 3;
    }
    else {
      $hlen = 10;
      $num = 4;
    }
    $off = $extheadersize + 10;
    while ($off < $tagsize) {
      $arr = $this->v2seek($fp, $hlen, $num, $off);
      if (!is_array($arr)) break;
      list($id, $size) = $arr;
      fseek($fp, $off + $hlen);
      $bytes = fread($fp, $size - $hlen);
      if ($id == "COMM")
        $bytes = preg_replace("/\\000*([^\\000]*)\\000/", "", $bytes);
      $bytes = str_replace(chr(0), "", $bytes);
      if ($h[$id]) {
        if (is_array($h[$id]))
          $h[$id][] = $bytes;
        else
          $h[$id] .= $bytes;
      }
      else
        $h[$id] = $bytes;
      $off += $size;
    }
    if (gettype($file) != "resource" && !empty($file)) fclose($fp);
    return $h;
  }

  function getinfo($file = "") {
    list($this->fp, $this->size) = $this->getfp($file);
    $headers = $this->headers();
    $v1info  = $this->v1info();
    $v2info  = $this->v2info();
    foreach ($this->v2tov1 as $v2tag => $v1tag)
      if (strlen($v2info[$v2tag]) > strlen($v1info[$v1tag]))
        $v1info[$v1tag] = $v2info[$v2tag];
    $info = array_merge($headers, $v1info, $v2info);
    if (ereg("^\(([0-9]+)\)(.*)", $info["genre"], $matches)) {
      $info["genre"] = $matches[2];
      $info["genreid"] = $matches[1];
    }
    return $info;
  }
}
?>
Return current item: Brunhilde Media Server