Merge branch 'master' into sharing
This commit is contained in:
commit
0387cc5f1f
|
@ -0,0 +1,3 @@
|
|||
#usertable, #grouptable{
|
||||
margin:0px;
|
||||
}
|
|
@ -36,7 +36,7 @@ function showLightbox(container,img){
|
|||
img.width = maxHeight * ratio;
|
||||
} else {
|
||||
img.width = maxWidth;
|
||||
img.height = maxWidth * ratio;
|
||||
img.height = maxWidth / ratio;
|
||||
}
|
||||
}
|
||||
container.empty();
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - media plugin
|
||||
*
|
||||
* @author Robin Appelman
|
||||
* @copyright 2010 Robin Appelman icewind1991@gmail.com
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
header('Content-type: text/html; charset=UTF-8') ;
|
||||
|
||||
//no apps
|
||||
$RUNTIME_NOAPPS=true;
|
||||
|
||||
require_once('../../../lib/base.php');
|
||||
require_once('../lib_collection.php');
|
||||
require_once('../lib_scanner.php');
|
||||
|
||||
error_reporting(E_ALL); //no script error reporting because of getID3
|
||||
|
||||
$arguments=$_POST;
|
||||
print_r($_POST);
|
||||
if(!isset($_POST['action']) and isset($_GET['action'])){
|
||||
$arguments=$_GET;
|
||||
}
|
||||
|
||||
foreach($arguments as &$argument){
|
||||
$argument=stripslashes($argument);
|
||||
}
|
||||
global $CONFIG_DATADIRECTORY;
|
||||
ob_clean();
|
||||
if(!isset($arguments['artist'])){
|
||||
$arguments['artist']=0;
|
||||
}
|
||||
if(!isset($arguments['album'])){
|
||||
$arguments['album']=0;
|
||||
}
|
||||
if(!isset($arguments['search'])){
|
||||
$arguments['search']='';
|
||||
}
|
||||
OC_MEDIA_COLLECTION::$uid=OC_USER::getUser();
|
||||
if($arguments['action']){
|
||||
switch($arguments['action']){
|
||||
case 'delete':
|
||||
$path=$arguments['path'];
|
||||
OC_MEDIA_COLLECTION::deleteSongByPath($path);
|
||||
$paths=explode(PATH_SEPARATOR,OC_PREFERENCES::getValue(OC_USER::getUser(),'media','paths',''));
|
||||
if(array_search($path,$paths)!==false){
|
||||
unset($paths[array_search($path,$paths)]);
|
||||
OC_PREFERENCES::setValue(OC_USER::getUser(),'media','paths',implode(PATH_SEPARATOR,$paths));
|
||||
}
|
||||
case 'get_collection':
|
||||
$artists=OC_MEDIA_COLLECTION::getArtists();
|
||||
foreach($artists as &$artist){
|
||||
$artist['albums']=OC_MEDIA_COLLECTION::getAlbums($artist['artist_id']);
|
||||
foreach($artist['albums'] as &$album){
|
||||
$album['songs']=OC_MEDIA_COLLECTION::getSongs($artist['artist_id'],$album['album_id']);
|
||||
}
|
||||
}
|
||||
echo json_encode($artists);
|
||||
break;
|
||||
case 'scan':
|
||||
set_time_limit(0); //recursive scan can take a while
|
||||
$path=$arguments['path'];
|
||||
if(OC_FILESYSTEM::is_dir($path)){
|
||||
$paths=explode(PATH_SEPARATOR,OC_PREFERENCES::getValue(OC_USER::getUser(),'media','paths',''));
|
||||
if(array_search($path,$paths)===false){
|
||||
$paths[]=$path;
|
||||
OC_PREFERENCES::setValue(OC_USER::getUser(),'media','paths',implode(PATH_SEPARATOR,$paths));
|
||||
}
|
||||
}
|
||||
echo OC_MEDIA_SCANNER::scanFolder($path);
|
||||
flush();
|
||||
break;
|
||||
case 'scanFile':
|
||||
echo (OC_MEDIA_SCANNER::scanFile($arguments['path']))?'true':'false';
|
||||
break;
|
||||
case 'get_artists':
|
||||
echo json_encode(OC_MEDIA_COLLECTION::getArtists($arguments['search']));
|
||||
break;
|
||||
case 'get_albums':
|
||||
echo json_encode(OC_MEDIA_COLLECTION::getAlbums($arguments['artist'],$arguments['search']));
|
||||
break;
|
||||
case 'get_songs':
|
||||
echo json_encode(OC_MEDIA_COLLECTION::getSongs($arguments['artist'],$arguments['album'],$arguments['search']));
|
||||
break;
|
||||
case 'play':
|
||||
ob_end_clean();
|
||||
|
||||
$ftype=OC_FILESYSTEM::getMimeType( $arguments['path'] );
|
||||
|
||||
header('Content-Type:'.$ftype);
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header('Pragma: public');
|
||||
header('Content-Length: '.OC_FILESYSTEM::filesize($arguments['path']));
|
||||
|
||||
OC_FILESYSTEM::readfile($arguments['path']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - media plugin
|
||||
*
|
||||
* @author Robin Appelman
|
||||
* @copyright 2010 Robin Appelman icewind1991@gmail.com
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
header('Content-type: text/html; charset=UTF-8') ;
|
||||
|
||||
//no apps or filesystem
|
||||
$RUNTIME_NOAPPS=true;
|
||||
$RUNTIME_NOSETUPFS=true;
|
||||
|
||||
require_once('../../../lib/base.php');
|
||||
|
||||
error_log($_GET['autoupdate']);
|
||||
$autoUpdate=(isset($_GET['autoupdate']) and $_GET['autoupdate']=='true');
|
||||
error_log((integer)$autoUpdate);
|
||||
|
||||
OC_PREFERENCES::setValue(OC_USER::getUser(),'media','autoupdate',(integer)$autoUpdate);
|
||||
|
||||
echo json_encode( array( "status" => "success", "data" => $autoUpdate));
|
||||
?>
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
/**
|
||||
* ownCloud - media plugin
|
||||
*
|
||||
* @author Robin Appelman
|
||||
* @copyright 2010 Robin Appelman icewind1991@gmail.com
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/
|
||||
*
|
||||
*/
|
||||
|
||||
require_once('apps/media/lib_media.php');
|
||||
|
||||
OC_UTIL::addScript('media','music');
|
||||
OC_UTIL::addScript('media','jquery.jplayer.min');
|
||||
OC_UTIL::addStyle('media','style');
|
||||
OC_UTIL::addStyle('media','jplayer');
|
||||
|
||||
OC_APP::register( array( 'order' => 3, 'id' => 'media', 'name' => 'Media' ));
|
||||
|
||||
OC_APP::addNavigationEntry( array( 'id' => 'media_index', 'order' => 2, 'href' => OC_HELPER::linkTo( 'media', 'index.php' ), 'icon' => OC_HELPER::imagePath( 'media', 'media.png' ), 'name' => 'Media' ));
|
||||
OC_APP::addSettingsPage( array( 'id' => 'media_settings', 'order' => 5, 'href' => OC_HELPER::linkTo( 'media', 'settings.php' ), 'name' => 'Media', 'icon' => OC_HELPER::imagePath( 'files', 'media.png' )));
|
||||
?>
|
|
@ -0,0 +1,241 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<database>
|
||||
|
||||
<name>*dbname*</name>
|
||||
<create>true</create>
|
||||
<overwrite>false</overwrite>
|
||||
|
||||
<charset>latin1</charset>
|
||||
|
||||
<table>
|
||||
|
||||
<name>*dbprefix*media_albums</name>
|
||||
|
||||
<declaration>
|
||||
|
||||
<field>
|
||||
<name>album_id</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<autoincrement>1</autoincrement>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>album_name</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>200</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>album_artist</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>album_art</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>200</length>
|
||||
</field>
|
||||
|
||||
</declaration>
|
||||
|
||||
</table>
|
||||
|
||||
<table>
|
||||
|
||||
<name>*dbprefix*media_artists</name>
|
||||
|
||||
<declaration>
|
||||
|
||||
<field>
|
||||
<name>artist_id</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<autoincrement>1</autoincrement>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>artist_name</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>200</length>
|
||||
</field>
|
||||
|
||||
<index>
|
||||
<name>artist_name</name>
|
||||
<unique>true</unique>
|
||||
<field>
|
||||
<name>artist_name</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
</declaration>
|
||||
|
||||
</table>
|
||||
|
||||
<table>
|
||||
|
||||
<name>*dbprefix*media_sessions</name>
|
||||
|
||||
<declaration>
|
||||
|
||||
<field>
|
||||
<name>session_id</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<autoincrement>1</autoincrement>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>token</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>64</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>user_id</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>64</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>start</name>
|
||||
<type>timestamp</type>
|
||||
<notnull>true</notnull>
|
||||
</field>
|
||||
|
||||
</declaration>
|
||||
|
||||
</table>
|
||||
|
||||
<table>
|
||||
|
||||
<name>*dbprefix*media_songs</name>
|
||||
|
||||
<declaration>
|
||||
|
||||
<field>
|
||||
<name>song_id</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<autoincrement>1</autoincrement>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>song_name</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>200</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>song_artist</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>song_album</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>song_path</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>200</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>song_user</name>
|
||||
<type>text</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<length>64</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>song_length</name>
|
||||
<type>integer</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>song_track</name>
|
||||
<type>integer</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>song_size</name>
|
||||
<type>integer</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
</declaration>
|
||||
|
||||
</table>
|
||||
|
||||
<table>
|
||||
|
||||
<name>*dbprefix*media_users</name>
|
||||
|
||||
<declaration>
|
||||
|
||||
<field>
|
||||
<name>user_id</name>
|
||||
<type>text</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<autoincrement>1</autoincrement>
|
||||
<length>64</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>user_password_sha256</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>64</length>
|
||||
</field>
|
||||
|
||||
</declaration>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
</database>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0"?>
|
||||
<info>
|
||||
<id>media</id>
|
||||
<name>Media</name>
|
||||
<description>Media player and server for ownCloud</description>
|
||||
<version>0.2</version>
|
||||
<licence>AGPL</licence>
|
||||
<author>Robin Appelman</author>
|
||||
<require>2</require>
|
||||
</info>
|
|
@ -0,0 +1,461 @@
|
|||
/*
|
||||
* Skin for jPlayer Plugin (jQuery JavaScript Library)
|
||||
* http://www.happyworm.com/jquery/jplayer
|
||||
*
|
||||
* Skin Name: Blue Monday
|
||||
*
|
||||
* Copyright (c) 2010 Happyworm Ltd
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
* - http://www.opensource.org/licenses/mit-license.php
|
||||
* - http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* Author: Silvia Benvenuti
|
||||
* Skin Version: 3.0 (jPlayer 2.0.0)
|
||||
* Date: 20th December 2010
|
||||
*/
|
||||
|
||||
div.jp-audio,
|
||||
div.jp-video {
|
||||
|
||||
/* Edit the font-size to counteract inherited font sizing.
|
||||
* Eg. 1.25em = 1 / 0.8em
|
||||
*/
|
||||
|
||||
font-size:1.25em;
|
||||
|
||||
font-family:Verdana, Arial, sans-serif;
|
||||
line-height:1.6;
|
||||
color: #000;
|
||||
}
|
||||
div.jp-audio {
|
||||
position:relative;
|
||||
margin-left:70ex;
|
||||
margin-right:0px;
|
||||
top:0px;
|
||||
margin-top:-30px;
|
||||
}
|
||||
div.jp-video-270p {
|
||||
width:480px;
|
||||
}
|
||||
div.jp-video-360p {
|
||||
width:640px;
|
||||
}
|
||||
div.jp-interface {
|
||||
position: fixed;
|
||||
z-index:100;
|
||||
width:25em;
|
||||
left:201px;
|
||||
top:-10px;
|
||||
}
|
||||
div.jp-type-playlist{
|
||||
width:100%;
|
||||
}
|
||||
div.jp-audio div.jp-type-single div.jp-interface {
|
||||
height:80px;
|
||||
border-bottom:none;
|
||||
}
|
||||
div.jp-audio div.jp-type-playlist div.jp-interface {
|
||||
height:80px;
|
||||
}
|
||||
div.jp-video div.jp-type-single div.jp-interface {
|
||||
height:60px;
|
||||
border-bottom:none;
|
||||
}
|
||||
div.jp-video div.jp-type-playlist div.jp-interface {
|
||||
height:60px;
|
||||
}
|
||||
div.jp-interface ul.jp-controls {
|
||||
list-style-type:none;
|
||||
padding:0;
|
||||
}
|
||||
div.jp-interface ul.jp-controls li {
|
||||
/* position: absolute; */
|
||||
display:inline;
|
||||
}
|
||||
div.jp-interface ul.jp-controls a {
|
||||
position: absolute;
|
||||
overflow:hidden;
|
||||
text-indent:-9999px;
|
||||
}
|
||||
a.jp-play,
|
||||
a.jp-pause {
|
||||
width:40px;
|
||||
height:40px;
|
||||
z-index:1;
|
||||
}
|
||||
div.jp-audio div.jp-type-single a.jp-play,
|
||||
div.jp-audio div.jp-type-single a.jp-pause {
|
||||
top:20px;
|
||||
left:40px;
|
||||
}
|
||||
div.jp-audio div.jp-type-playlist a.jp-play,
|
||||
div.jp-audio div.jp-type-playlist a.jp-pause {
|
||||
top:20px;
|
||||
left:48px;
|
||||
}
|
||||
div.jp-video a.jp-play,
|
||||
div.jp-video a.jp-pause {
|
||||
top:15px;
|
||||
}
|
||||
div.jp-video-270p div.jp-type-single a.jp-play,
|
||||
div.jp-video-270p div.jp-type-single a.jp-pause {
|
||||
left:195px;
|
||||
}
|
||||
div.jp-video-270p div.jp-type-playlist a.jp-play,
|
||||
div.jp-video-270p div.jp-type-playlist a.jp-pause {
|
||||
left:220px;
|
||||
}
|
||||
div.jp-video-360p div.jp-type-single a.jp-play,
|
||||
div.jp-video-360p div.jp-type-single a.jp-pause {
|
||||
left:275px;
|
||||
}
|
||||
div.jp-video-360p div.jp-type-playlist a.jp-play,
|
||||
div.jp-video-360p div.jp-type-playlist a.jp-pause {
|
||||
left:300px;
|
||||
}
|
||||
a.jp-play {
|
||||
background: url("../img/jplayer.blue.monday.png") 0 0 no-repeat;
|
||||
}
|
||||
a.jp-play:hover {
|
||||
background: url("../img/jplayer.blue.monday.png") -41px 0 no-repeat;
|
||||
}
|
||||
a.jp-pause {
|
||||
background: url("../img/jplayer.blue.monday.png") 0 -42px no-repeat;
|
||||
display: none;
|
||||
}
|
||||
a.jp-pause:hover {
|
||||
background: url("../img/jplayer.blue.monday.png") -41px -42px no-repeat;
|
||||
}
|
||||
div.jp-audio div.jp-type-single a.jp-stop {
|
||||
top:26px;
|
||||
left:90px;
|
||||
}
|
||||
div.jp-audio div.jp-type-playlist a.jp-stop {
|
||||
top:26px;
|
||||
left:126px;
|
||||
}
|
||||
div.jp-video a.jp-stop {
|
||||
top:21px;
|
||||
}
|
||||
div.jp-video-270p div.jp-type-single a.jp-stop {
|
||||
left:245px;
|
||||
}
|
||||
div.jp-video-270p div.jp-type-playlist a.jp-stop {
|
||||
left:298px;
|
||||
}
|
||||
div.jp-video-360p div.jp-type-single a.jp-stop {
|
||||
left:325px;
|
||||
}
|
||||
div.jp-video-360p div.jp-type-playlist a.jp-stop {
|
||||
left:378px;
|
||||
}
|
||||
a.jp-stop {
|
||||
background: url("../img/jplayer.blue.monday.png") 0 -83px no-repeat;
|
||||
width:28px;
|
||||
height:28px;
|
||||
z-index:1;
|
||||
}
|
||||
a.jp-stop:hover {
|
||||
background: url("../img/jplayer.blue.monday.png") -29px -83px no-repeat;
|
||||
}
|
||||
div.jp-audio div.jp-type-playlist a.jp-previous {
|
||||
left:20px;
|
||||
top:26px;
|
||||
}
|
||||
div.jp-video div.jp-type-playlist a.jp-previous {
|
||||
top:21px;
|
||||
}
|
||||
div.jp-video-270p div.jp-type-playlist a.jp-previous {
|
||||
left:192px;
|
||||
}
|
||||
div.jp-video-360p div.jp-type-playlist a.jp-previous {
|
||||
left:272px;
|
||||
}
|
||||
a.jp-previous {
|
||||
background: url("../img/jplayer.blue.monday.png") 0 -112px no-repeat;
|
||||
width:28px;
|
||||
height:28px;
|
||||
}
|
||||
a.jp-previous:hover {
|
||||
background: url("../img/jplayer.blue.monday.png") -29px -112px no-repeat;
|
||||
}
|
||||
div.jp-audio div.jp-type-playlist a.jp-next {
|
||||
left:88px;
|
||||
top:26px;
|
||||
}
|
||||
div.jp-video div.jp-type-playlist a.jp-next {
|
||||
top:21px;
|
||||
}
|
||||
div.jp-video-270p div.jp-type-playlist a.jp-next {
|
||||
left:260px;
|
||||
}
|
||||
div.jp-video-360p div.jp-type-playlist a.jp-next {
|
||||
left:340px;
|
||||
}
|
||||
a.jp-next {
|
||||
background: url("../img/jplayer.blue.monday.png") 0 -141px no-repeat;
|
||||
width:28px;
|
||||
height:28px;
|
||||
}
|
||||
a.jp-next:hover {
|
||||
background: url("../img/jplayer.blue.monday.png") -29px -141px no-repeat;
|
||||
}
|
||||
div.jp-progress {
|
||||
position: absolute;
|
||||
overflow:hidden;
|
||||
background-color: #293b51;
|
||||
}
|
||||
div.jp-audio div.jp-type-single div.jp-progress {
|
||||
top:32px;
|
||||
left:130px;
|
||||
width:122px;
|
||||
height:15px;
|
||||
}
|
||||
div.jp-audio div.jp-type-playlist div.jp-progress {
|
||||
top:32px;
|
||||
left:164px;
|
||||
width:122px;
|
||||
height:15px;
|
||||
}
|
||||
div.jp-video div.jp-progress {
|
||||
top:0px;
|
||||
left:0px;
|
||||
width:100%;
|
||||
height:10px;
|
||||
}
|
||||
div.jp-seek-bar {
|
||||
background: url("../img/jplayer.blue.monday.png") 0 -202px repeat-x;
|
||||
width:0px;
|
||||
/* height:15px; */
|
||||
height:100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
div.jp-play-bar {
|
||||
background: url("../img/jplayer.blue.monday.png") 0 -218px repeat-x ;
|
||||
width:0px;
|
||||
/* height:15px; */
|
||||
height:100%;
|
||||
}
|
||||
|
||||
/* The seeking class is added/removed inside jPlayer */
|
||||
div.jp-seeking-bg {
|
||||
background: url("../img/pbar-ani.gif");
|
||||
}
|
||||
|
||||
a.jp-mute,
|
||||
a.jp-unmute {
|
||||
width:18px;
|
||||
height:15px;
|
||||
}
|
||||
div.jp-audio div.jp-type-single a.jp-mute,
|
||||
div.jp-audio div.jp-type-single a.jp-unmute {
|
||||
top:32px;
|
||||
left:274px;
|
||||
}
|
||||
div.jp-audio div.jp-type-playlist a.jp-mute,
|
||||
div.jp-audio div.jp-type-playlist a.jp-unmute {
|
||||
top:32px;
|
||||
left:296px;
|
||||
}
|
||||
div.jp-video a.jp-mute,
|
||||
div.jp-video a.jp-unmute {
|
||||
top:27px;
|
||||
}
|
||||
div.jp-video-270p div.jp-type-single a.jp-mute,
|
||||
div.jp-video-270p div.jp-type-single a.jp-unmute {
|
||||
left:304px;
|
||||
}
|
||||
div.jp-video-270p div.jp-type-playlist a.jp-unmute,
|
||||
div.jp-video-270p div.jp-type-playlist a.jp-mute {
|
||||
left:363px;
|
||||
}
|
||||
div.jp-video-360p div.jp-type-single a.jp-mute,
|
||||
div.jp-video-360p div.jp-type-single a.jp-unmute {
|
||||
left:384px;
|
||||
}
|
||||
div.jp-video-360p div.jp-type-playlist a.jp-mute,
|
||||
div.jp-video-360p div.jp-type-playlist a.jp-unmute {
|
||||
left:443px;
|
||||
}
|
||||
a.jp-mute {
|
||||
background: url("../img/jplayer.blue.monday.png") 0 -186px no-repeat;
|
||||
}
|
||||
a.jp-mute:hover {
|
||||
background: url("../img/jplayer.blue.monday.png") -19px -170px no-repeat;
|
||||
}
|
||||
a.jp-unmute {
|
||||
background: url("../img/jplayer.blue.monday.png") 0 -170px no-repeat;
|
||||
display: none;
|
||||
}
|
||||
a.jp-unmute:hover {
|
||||
background: url("../img/jplayer.blue.monday.png") -19px -186px no-repeat;
|
||||
}
|
||||
div.jp-volume-bar {
|
||||
position: absolute;
|
||||
overflow:hidden;
|
||||
background: url("../img/jplayer.blue.monday.png") 0 -250px repeat-x;
|
||||
width:46px;
|
||||
height:5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
div.jp-audio div.jp-type-single div.jp-volume-bar {
|
||||
top:37px;
|
||||
left:302px;
|
||||
}
|
||||
div.jp-audio div.jp-type-playlist div.jp-volume-bar {
|
||||
top:37px;
|
||||
left:324px;
|
||||
}
|
||||
div.jp-video div.jp-volume-bar {
|
||||
top:32px;
|
||||
}
|
||||
div.jp-video-270p div.jp-type-single div.jp-volume-bar {
|
||||
left:332px;
|
||||
}
|
||||
div.jp-video-270p div.jp-type-playlist div.jp-volume-bar {
|
||||
left:391px;
|
||||
}
|
||||
div.jp-video-360p div.jp-type-single div.jp-volume-bar {
|
||||
left:412px;
|
||||
}
|
||||
div.jp-video-360p div.jp-type-playlist div.jp-volume-bar {
|
||||
left:471px;
|
||||
}
|
||||
div.jp-volume-bar-value {
|
||||
background: url("../img/jplayer.blue.monday.png") 0 -256px repeat-x;
|
||||
width:0px;
|
||||
height:5px;
|
||||
}
|
||||
div.jp-current-time,
|
||||
div.jp-duration {
|
||||
position: absolute;
|
||||
font-size:.64em;
|
||||
font-style:oblique;
|
||||
}
|
||||
div.jp-duration {
|
||||
text-align: right;
|
||||
}
|
||||
div.jp-audio div.jp-type-single div.jp-current-time,
|
||||
div.jp-audio div.jp-type-single div.jp-duration {
|
||||
top:49px;
|
||||
left:130px;
|
||||
width:122px;
|
||||
}
|
||||
div.jp-audio div.jp-type-playlist div.jp-current-time,
|
||||
div.jp-audio div.jp-type-playlist div.jp-duration {
|
||||
top:49px;
|
||||
left:164px;
|
||||
width:122px;
|
||||
}
|
||||
div.jp-video div.jp-current-time,
|
||||
div.jp-video div.jp-duration {
|
||||
top:10px;
|
||||
left:0px;
|
||||
width:98%;
|
||||
padding:0 1%;
|
||||
}
|
||||
div.jp-playlist {
|
||||
/* width:418px; */
|
||||
/* width:400px; */
|
||||
margin-top:6.3em;
|
||||
right:0px;
|
||||
}
|
||||
div.jp-playlist ul {
|
||||
list-style-type:none;
|
||||
margin:0;
|
||||
padding:0 20px;
|
||||
/* background-color:#ccc; */
|
||||
/* border:1px solid #009be3; */
|
||||
/* border-top:none; */
|
||||
/* width:378px; */
|
||||
font-size:.72em;
|
||||
}
|
||||
|
||||
|
||||
div.jp-type-single div.jp-playlist li {
|
||||
padding:5px 0 5px 20px;
|
||||
font-weight:bold;
|
||||
}
|
||||
div.jp-type-playlist div.jp-playlist li {
|
||||
padding:5px 0 4px 20px;
|
||||
border-bottom:1px solid #eee;
|
||||
}
|
||||
/*
|
||||
d *iv.jp-video div.jp-playlist li {
|
||||
padding:5px 0 5px 20px;
|
||||
font-weight:bold;
|
||||
}
|
||||
*/
|
||||
div.jp-type-playlist div.jp-playlist li.jp-playlist-last {
|
||||
padding:5px 0 5px 20px;
|
||||
border-bottom:none;
|
||||
}
|
||||
div.jp-type-playlist div.jp-playlist li.jp-playlist-current {
|
||||
list-style-type:square;
|
||||
list-style-position:inside;
|
||||
padding-left:8px;
|
||||
}
|
||||
div.jp-type-playlist div.jp-playlist a {
|
||||
color: #666;
|
||||
text-decoration: none;
|
||||
}
|
||||
div.jp-type-playlist div.jp-playlist a:hover {
|
||||
color:#0d88c1;
|
||||
}
|
||||
div.jp-type-playlist div.jp-playlist a.jp-playlist-current {
|
||||
color:#0d88c1;
|
||||
}
|
||||
div.jp-type-playlist div.jp-playlist div.jp-free-media {
|
||||
display:inline;
|
||||
margin-left:20px;
|
||||
}
|
||||
|
||||
div.jp-video div.jp-video-play {
|
||||
background: transparent url("../img/jplayer.blue.monday.video.play.png") no-repeat center;
|
||||
/* position: relative; */
|
||||
position: absolute;
|
||||
cursor:pointer;
|
||||
z-index:2;
|
||||
}
|
||||
div.jp-video div.jp-video-play:hover {
|
||||
background: transparent url("../img/jplayer.blue.monday.video.play.hover.png") no-repeat center;
|
||||
}
|
||||
div.jp-video-270p div.jp-video-play {
|
||||
top:-270px;
|
||||
width:480px;
|
||||
height:270px;
|
||||
}
|
||||
div.jp-video-360p div.jp-video-play {
|
||||
top:-360px;
|
||||
width:640px;
|
||||
height:360px;
|
||||
}
|
||||
|
||||
div.jp-jplayer {
|
||||
width:0px;
|
||||
height:0px;
|
||||
}
|
||||
div.jp-video div.jp-jplayer {
|
||||
border:1px solid #009be3;
|
||||
border-bottom:none;
|
||||
z-index:1;
|
||||
}
|
||||
div.jp-video-270p div.jp-jplayer {
|
||||
width:480px;
|
||||
height:270px;
|
||||
}
|
||||
div.jp-video-360p div.jp-jplayer {
|
||||
width:640px;
|
||||
height:360px;
|
||||
}
|
||||
div.jp-jplayer {
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
div.jp-playlist ul li button{
|
||||
display:none;
|
||||
}
|
||||
|
||||
div.jp-playlist ul li:hover button{
|
||||
display:inline;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
.right{
|
||||
float:right;
|
||||
}
|
||||
|
||||
#folderlist li{
|
||||
list-style-type:none;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
#folderlist button.prettybutton{
|
||||
font-size:1em;
|
||||
width:10ex;
|
||||
}
|
||||
|
||||
li button.right.prettybutton{
|
||||
font-size:1em;
|
||||
}
|
||||
|
||||
#collection{
|
||||
padding-top:1em;
|
||||
position:relative;
|
||||
width:70ex;
|
||||
float:left;
|
||||
}
|
||||
|
||||
#collection li{
|
||||
list-style-type:none;
|
||||
}
|
||||
|
||||
#collection li button{
|
||||
float:right;
|
||||
}
|
||||
|
||||
#collection li.album, #collection li.song{
|
||||
margin-left:3ex;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,319 @@
|
|||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------+
|
||||
// | PHP version 4.1.0 |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | Placed in public domain by Allan Hansen, 2002. Share and enjoy! |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | /demo/demo.audioinfo.class.php |
|
||||
// | |
|
||||
// | Example wrapper class to extract information from audio files |
|
||||
// | through getID3(). |
|
||||
// | |
|
||||
// | getID3() returns a lot of information. Much of this information is |
|
||||
// | not needed for the end-application. It is also possible that some |
|
||||
// | users want to extract specific info. Modifying getID3() files is a |
|
||||
// | bad idea, as modifications needs to be done to future versions of |
|
||||
// | getID3(). |
|
||||
// | |
|
||||
// | Modify this wrapper class instead. This example extracts certain |
|
||||
// | fields only and adds a new root value - encoder_options if possible. |
|
||||
// | It also checks for mp3 files with wave headers. |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | Example code: |
|
||||
// | $au = new AudioInfo(); |
|
||||
// | print_r($au->Info('file.flac'); |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | Authors: Allan Hansen <ahØartemis*dk> |
|
||||
// +----------------------------------------------------------------------+
|
||||
//
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* getID3() settings
|
||||
*/
|
||||
|
||||
require_once('../getid3/getid3.php');
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class for extracting information from audio files with getID3().
|
||||
*/
|
||||
|
||||
class AudioInfo {
|
||||
|
||||
/**
|
||||
* Private variables
|
||||
*/
|
||||
var $result = NULL;
|
||||
var $info = NULL;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
||||
function AudioInfo() {
|
||||
|
||||
// Initialize getID3 engine
|
||||
$this->getID3 = new getID3;
|
||||
$this->getID3->option_md5_data = true;
|
||||
$this->getID3->option_md5_data_source = true;
|
||||
$this->getID3->encoding = 'UTF-8';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Extract information - only public function
|
||||
*
|
||||
* @access public
|
||||
* @param string file Audio file to extract info from.
|
||||
*/
|
||||
|
||||
function Info($file) {
|
||||
|
||||
// Analyze file
|
||||
$this->info = $this->getID3->analyze($file);
|
||||
|
||||
// Exit here on error
|
||||
if (isset($this->info['error'])) {
|
||||
return array ('error' => $this->info['error']);
|
||||
}
|
||||
|
||||
// Init wrapper object
|
||||
$this->result = array ();
|
||||
$this->result['format_name'] = @$this->info['fileformat'].'/'.@$this->info['audio']['dataformat'].(isset($this->info['video']['dataformat']) ? '/'.@$this->info['video']['dataformat'] : '');
|
||||
$this->result['encoder_version'] = @$this->info['audio']['encoder'];
|
||||
$this->result['encoder_options'] = @$this->info['audio']['encoder_options'];
|
||||
$this->result['bitrate_mode'] = @$this->info['audio']['bitrate_mode'];
|
||||
$this->result['channels'] = @$this->info['audio']['channels'];
|
||||
$this->result['sample_rate'] = @$this->info['audio']['sample_rate'];
|
||||
$this->result['bits_per_sample'] = @$this->info['audio']['bits_per_sample'];
|
||||
$this->result['playing_time'] = @$this->info['playtime_seconds'];
|
||||
$this->result['avg_bit_rate'] = @$this->info['audio']['bitrate'];
|
||||
$this->result['tags'] = @$this->info['tags'];
|
||||
$this->result['comments'] = @$this->info['comments'];
|
||||
$this->result['warning'] = @$this->info['warning'];
|
||||
$this->result['md5'] = @$this->info['md5_data'];
|
||||
|
||||
// Post getID3() data handling based on file format
|
||||
$method = @$this->info['fileformat'].'Info';
|
||||
if (@$this->info['fileformat'] && method_exists($this, $method)) {
|
||||
$this->$method();
|
||||
}
|
||||
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* post-getID3() data handling for AAC files.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
|
||||
function aacInfo() {
|
||||
$this->result['format_name'] = 'AAC';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* post-getID3() data handling for Wave files.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
|
||||
function riffInfo() {
|
||||
if ($this->info['audio']['dataformat'] == 'wav') {
|
||||
|
||||
$this->result['format_name'] = 'Wave';
|
||||
|
||||
} else if (ereg('^mp[1-3]$', $this->info['audio']['dataformat'])) {
|
||||
|
||||
$this->result['format_name'] = strtoupper($this->info['audio']['dataformat']);
|
||||
|
||||
} else {
|
||||
|
||||
$this->result['format_name'] = 'riff/'.$this->info['audio']['dataformat'];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* * post-getID3() data handling for FLAC files.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
|
||||
function flacInfo() {
|
||||
$this->result['format_name'] = 'FLAC';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* post-getID3() data handling for Monkey's Audio files.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
|
||||
function macInfo() {
|
||||
$this->result['format_name'] = 'Monkey\'s Audio';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* post-getID3() data handling for Lossless Audio files.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
|
||||
function laInfo() {
|
||||
$this->result['format_name'] = 'La';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* post-getID3() data handling for Ogg Vorbis files.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
|
||||
function oggInfo() {
|
||||
if ($this->info['audio']['dataformat'] == 'vorbis') {
|
||||
|
||||
$this->result['format_name'] = 'Ogg Vorbis';
|
||||
|
||||
} else if ($this->info['audio']['dataformat'] == 'flac') {
|
||||
|
||||
$this->result['format_name'] = 'Ogg FLAC';
|
||||
|
||||
} else if ($this->info['audio']['dataformat'] == 'speex') {
|
||||
|
||||
$this->result['format_name'] = 'Ogg Speex';
|
||||
|
||||
} else {
|
||||
|
||||
$this->result['format_name'] = 'Ogg '.$this->info['audio']['dataformat'];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* post-getID3() data handling for Musepack files.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
|
||||
function mpcInfo() {
|
||||
$this->result['format_name'] = 'Musepack';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* post-getID3() data handling for MPEG files.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
|
||||
function mp3Info() {
|
||||
$this->result['format_name'] = 'MP3';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* post-getID3() data handling for MPEG files.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
|
||||
function mp2Info() {
|
||||
$this->result['format_name'] = 'MP2';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* post-getID3() data handling for MPEG files.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
|
||||
function mp1Info() {
|
||||
$this->result['format_name'] = 'MP1';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* post-getID3() data handling for WMA files.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
|
||||
function asfInfo() {
|
||||
$this->result['format_name'] = strtoupper($this->info['audio']['dataformat']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* post-getID3() data handling for Real files.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
|
||||
function realInfo() {
|
||||
$this->result['format_name'] = 'Real';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* post-getID3() data handling for VQF files.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
|
||||
function vqfInfo() {
|
||||
$this->result['format_name'] = 'VQF';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// /demo/demo.basic.php - part of getID3() //
|
||||
// Sample script showing most basic use of getID3() //
|
||||
// See readme.txt for more details //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
// include getID3() library (can be in a different directory if full path is specified)
|
||||
require_once('../getid3/getid3.php');
|
||||
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
|
||||
// Analyze file and store returned data in $ThisFileInfo
|
||||
$ThisFileInfo = $getID3->analyze($filename);
|
||||
|
||||
// Optional: copies data from all subarrays of [tags] into [comments] so
|
||||
// metadata is all available in one location for all tag formats
|
||||
// metainformation is always available under [tags] even if this is not called
|
||||
getid3_lib::CopyTagsToComments($ThisFileInfo);
|
||||
|
||||
// Output desired information in whatever format you want
|
||||
// Note: all entries in [comments] or [tags] are arrays of strings
|
||||
// See structure.txt for information on what information is available where
|
||||
// or check out the output of /demos/demo.browse.php for a particular file
|
||||
// to see the full detail of what information is returned where in the array
|
||||
echo @$ThisFileInfo['comments_html']['artist'][0]; // artist from any/all available tag formats
|
||||
echo @$ThisFileInfo['tags']['id3v2']['title'][0]; // title from ID3v2
|
||||
echo @$ThisFileInfo['audio']['bitrate']; // audio bitrate
|
||||
echo @$ThisFileInfo['playtime_string']; // playtime in minutes:seconds, formatted string
|
||||
|
||||
?>
|
|
@ -0,0 +1,679 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// /demo/demo.browse.php - part of getID3() //
|
||||
// Sample script for browsing/scanning files and displaying //
|
||||
// information returned by getID3() //
|
||||
// See readme.txt for more details //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//die('Due to a security issue, this demo has been disabled. It can be enabled by removing line '.__LINE__.' in demos/'.basename(__FILE__));
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// set predefined variables as if magic_quotes_gpc was off,
|
||||
// whether the server's got it or not:
|
||||
UnifyMagicQuotes(false);
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// showfile is used to display embedded images from table_var_dump()
|
||||
// md5 of requested file is required to prevent abuse where any
|
||||
// random file on the server could be viewed
|
||||
if (@$_REQUEST['showfile']) {
|
||||
if (is_readable($_REQUEST['showfile'])) {
|
||||
if (md5_file($_REQUEST['showfile']) == @$_REQUEST['md5']) {
|
||||
readfile($_REQUEST['showfile']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
die('Cannot display "'.$_REQUEST['showfile'].'"');
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
if (!function_exists('getmicrotime')) {
|
||||
function getmicrotime() {
|
||||
list($usec, $sec) = explode(' ', microtime());
|
||||
return ((float) $usec + (float) $sec);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
$writescriptfilename = 'demo.write.php';
|
||||
|
||||
require_once('../getid3/getid3.php');
|
||||
|
||||
// Needed for windows only
|
||||
define('GETID3_HELPERAPPSDIR', 'C:/helperapps/');
|
||||
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
$getID3->setOption(array('encoding' => 'UTF-8'));
|
||||
|
||||
$getID3checkColor_Head = 'CCCCDD';
|
||||
$getID3checkColor_DirectoryLight = 'FFCCCC';
|
||||
$getID3checkColor_DirectoryDark = 'EEBBBB';
|
||||
$getID3checkColor_FileLight = 'EEEEEE';
|
||||
$getID3checkColor_FileDark = 'DDDDDD';
|
||||
$getID3checkColor_UnknownLight = 'CCCCFF';
|
||||
$getID3checkColor_UnknownDark = 'BBBBDD';
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
ob_start();
|
||||
echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
|
||||
echo '<html><head>';
|
||||
echo '<title>getID3() - /demo/demo.browse.php (sample script)</title>';
|
||||
echo '<link rel="stylesheet" href="getid3.css" type="text/css">';
|
||||
echo '</head><body>';
|
||||
|
||||
if (isset($_REQUEST['deletefile'])) {
|
||||
if (file_exists($_REQUEST['deletefile'])) {
|
||||
if (unlink($_REQUEST['deletefile'])) {
|
||||
$deletefilemessage = 'Successfully deleted '.addslashes($_REQUEST['deletefile']);
|
||||
} else {
|
||||
$deletefilemessage = 'FAILED to delete '.addslashes($_REQUEST['deletefile']).' - error deleting file';
|
||||
}
|
||||
} else {
|
||||
$deletefilemessage = 'FAILED to delete '.addslashes($_REQUEST['deletefile']).' - file does not exist';
|
||||
}
|
||||
if (isset($_REQUEST['noalert'])) {
|
||||
echo '<b><font color="'.(($deletefilemessage{0} == 'F') ? '#FF0000' : '#008000').'">'.$deletefilemessage.'</font></b><hr>';
|
||||
} else {
|
||||
echo '<script type="text/javascript">alert("'.$deletefilemessage.'");</script>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (isset($_REQUEST['filename'])) {
|
||||
|
||||
if (!file_exists($_REQUEST['filename']) || !is_file($_REQUEST['filename'])) {
|
||||
die(getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-8', $_REQUEST['filename'].' does not exist'));
|
||||
}
|
||||
$starttime = getmicrotime();
|
||||
|
||||
//$getID3->setOption(array(
|
||||
// 'option_md5_data' => $AutoGetHashes,
|
||||
// 'option_sha1_data' => $AutoGetHashes,
|
||||
//));
|
||||
$ThisFileInfo = $getID3->analyze($_REQUEST['filename']);
|
||||
$AutoGetHashes = (bool) ((@$ThisFileInfo['filesize'] > 0) && ($ThisFileInfo['filesize'] < (50 * 1048576))); // auto-get md5_data, md5_file, sha1_data, sha1_file if filesize < 50MB, and NOT zero (which may indicate a file>2GB)
|
||||
if ($AutoGetHashes) {
|
||||
$ThisFileInfo['md5_file'] = getid3_lib::md5_file($_REQUEST['filename']);
|
||||
$ThisFileInfo['sha1_file'] = getid3_lib::sha1_file($_REQUEST['filename']);
|
||||
}
|
||||
|
||||
|
||||
getid3_lib::CopyTagsToComments($ThisFileInfo);
|
||||
|
||||
$listdirectory = dirname(getid3_lib::SafeStripSlashes($_REQUEST['filename']));
|
||||
$listdirectory = realpath($listdirectory); // get rid of /../../ references
|
||||
|
||||
if (GETID3_OS_ISWINDOWS) {
|
||||
// this mostly just gives a consistant look to Windows and *nix filesystems
|
||||
// (windows uses \ as directory seperator, *nix uses /)
|
||||
$listdirectory = str_replace('\\', '/', $listdirectory.'/');
|
||||
}
|
||||
|
||||
if (strstr($_REQUEST['filename'], 'http://') || strstr($_REQUEST['filename'], 'ftp://')) {
|
||||
echo '<i>Cannot browse remote filesystems</i><br>';
|
||||
} else {
|
||||
echo 'Browse: <a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.urlencode($listdirectory).'">'.getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-8', $listdirectory).'</a><br>';
|
||||
}
|
||||
|
||||
echo table_var_dump($ThisFileInfo);
|
||||
$endtime = getmicrotime();
|
||||
echo 'File parsed in '.number_format($endtime - $starttime, 3).' seconds.<br>';
|
||||
|
||||
} else {
|
||||
|
||||
$listdirectory = (isset($_REQUEST['listdirectory']) ? getid3_lib::SafeStripSlashes($_REQUEST['listdirectory']) : '.');
|
||||
$listdirectory = realpath($listdirectory); // get rid of /../../ references
|
||||
$currentfulldir = $listdirectory.'/';
|
||||
|
||||
if (GETID3_OS_ISWINDOWS) {
|
||||
// this mostly just gives a consistant look to Windows and *nix filesystems
|
||||
// (windows uses \ as directory seperator, *nix uses /)
|
||||
$currentfulldir = str_replace('\\', '/', $listdirectory.'/');
|
||||
}
|
||||
|
||||
if ($handle = @opendir($listdirectory)) {
|
||||
|
||||
echo str_repeat(' ', 300); // IE buffers the first 300 or so chars, making this progressive display useless - fill the buffer with spaces
|
||||
echo 'Processing';
|
||||
|
||||
$starttime = getmicrotime();
|
||||
|
||||
$TotalScannedUnknownFiles = 0;
|
||||
$TotalScannedKnownFiles = 0;
|
||||
$TotalScannedPlaytimeFiles = 0;
|
||||
$TotalScannedBitrateFiles = 0;
|
||||
$TotalScannedFilesize = 0;
|
||||
$TotalScannedPlaytime = 0;
|
||||
$TotalScannedBitrate = 0;
|
||||
$FilesWithWarnings = 0;
|
||||
$FilesWithErrors = 0;
|
||||
|
||||
while ($file = readdir($handle)) {
|
||||
$currentfilename = $listdirectory.'/'.$file;
|
||||
set_time_limit(30); // allocate another 30 seconds to process this file - should go much quicker than this unless intense processing (like bitrate histogram analysis) is enabled
|
||||
echo ' .'; // progress indicator dot
|
||||
flush(); // make sure the dot is shown, otherwise it's useless
|
||||
|
||||
switch ($file) {
|
||||
case '..':
|
||||
$ParentDir = realpath($file.'/..').'/';
|
||||
if (GETID3_OS_ISWINDOWS) {
|
||||
$ParentDir = str_replace('\\', '/', $ParentDir);
|
||||
}
|
||||
$DirectoryContents[$currentfulldir]['dir'][$file]['filename'] = $ParentDir;
|
||||
continue 2;
|
||||
break;
|
||||
|
||||
case '.':
|
||||
// ignore
|
||||
continue 2;
|
||||
break;
|
||||
}
|
||||
|
||||
// symbolic-link-resolution enhancements by davidbullock״ech-center*com
|
||||
$TargetObject = realpath($currentfilename); // Find actual file path, resolve if it's a symbolic link
|
||||
$TargetObjectType = filetype($TargetObject); // Check file type without examining extension
|
||||
|
||||
if ($TargetObjectType == 'dir') {
|
||||
|
||||
$DirectoryContents[$currentfulldir]['dir'][$file]['filename'] = $file;
|
||||
|
||||
} elseif ($TargetObjectType == 'file') {
|
||||
|
||||
$getID3->setOption(array('option_md5_data' => isset($_REQUEST['ShowMD5'])));
|
||||
$fileinformation = $getID3->analyze($currentfilename);
|
||||
|
||||
getid3_lib::CopyTagsToComments($fileinformation);
|
||||
|
||||
$TotalScannedFilesize += @$fileinformation['filesize'];
|
||||
|
||||
if (isset($_REQUEST['ShowMD5'])) {
|
||||
$fileinformation['md5_file'] = md5($currentfilename);
|
||||
$fileinformation['md5_file'] = getid3_lib::md5_file($currentfilename);
|
||||
}
|
||||
|
||||
if (!empty($fileinformation['fileformat'])) {
|
||||
$DirectoryContents[$currentfulldir]['known'][$file] = $fileinformation;
|
||||
$TotalScannedPlaytime += @$fileinformation['playtime_seconds'];
|
||||
$TotalScannedBitrate += @$fileinformation['bitrate'];
|
||||
$TotalScannedKnownFiles++;
|
||||
} else {
|
||||
$DirectoryContents[$currentfulldir]['other'][$file] = $fileinformation;
|
||||
$DirectoryContents[$currentfulldir]['other'][$file]['playtime_string'] = '-';
|
||||
$TotalScannedUnknownFiles++;
|
||||
}
|
||||
if (isset($fileinformation['playtime_seconds']) && ($fileinformation['playtime_seconds'] > 0)) {
|
||||
$TotalScannedPlaytimeFiles++;
|
||||
}
|
||||
if (isset($fileinformation['bitrate']) && ($fileinformation['bitrate'] > 0)) {
|
||||
$TotalScannedBitrateFiles++;
|
||||
}
|
||||
}
|
||||
}
|
||||
$endtime = getmicrotime();
|
||||
closedir($handle);
|
||||
echo 'done<br>';
|
||||
echo 'Directory scanned in '.number_format($endtime - $starttime, 2).' seconds.<br>';
|
||||
flush();
|
||||
|
||||
$columnsintable = 14;
|
||||
echo '<table class="table" cellspacing="0" cellpadding="3">';
|
||||
|
||||
echo '<tr bgcolor="#'.$getID3checkColor_Head.'"><th colspan="'.$columnsintable.'">Files in '.getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-8', $currentfulldir).'</th></tr>';
|
||||
$rowcounter = 0;
|
||||
foreach ($DirectoryContents as $dirname => $val) {
|
||||
if (isset($DirectoryContents[$dirname]['dir']) && is_array($DirectoryContents[$dirname]['dir'])) {
|
||||
uksort($DirectoryContents[$dirname]['dir'], 'MoreNaturalSort');
|
||||
foreach ($DirectoryContents[$dirname]['dir'] as $filename => $fileinfo) {
|
||||
echo '<tr bgcolor="#'.(($rowcounter++ % 2) ? $getID3checkColor_DirectoryLight : $getID3checkColor_DirectoryDark).'">';
|
||||
if ($filename == '..') {
|
||||
echo '<td colspan="'.$columnsintable.'">';
|
||||
echo '<form action="'.$_SERVER['PHP_SELF'].'" method="get">';
|
||||
echo 'Parent directory: ';
|
||||
echo '<input type="text" name="listdirectory" size="50" style="background-color: '.$getID3checkColor_DirectoryDark.';" value="';
|
||||
if (GETID3_OS_ISWINDOWS) {
|
||||
echo htmlentities(str_replace('\\', '/', realpath($dirname.$filename)), ENT_QUOTES);
|
||||
} else {
|
||||
echo htmlentities(realpath($dirname.$filename), ENT_QUOTES);
|
||||
}
|
||||
echo '"> <input type="submit" value="Go">';
|
||||
echo '</form></td>';
|
||||
} else {
|
||||
echo '<td colspan="'.$columnsintable.'"><a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.urlencode($dirname.$filename).'"><b>'.FixTextFields($filename).'</b></a></td>';
|
||||
}
|
||||
echo '</tr>';
|
||||
}
|
||||
}
|
||||
|
||||
echo '<tr bgcolor="#'.$getID3checkColor_Head.'">';
|
||||
echo '<th>Filename</th>';
|
||||
echo '<th>File Size</th>';
|
||||
echo '<th>Format</th>';
|
||||
echo '<th>Playtime</th>';
|
||||
echo '<th>Bitrate</th>';
|
||||
echo '<th>Artist</th>';
|
||||
echo '<th>Title</th>';
|
||||
if (isset($_REQUEST['ShowMD5'])) {
|
||||
echo '<th>MD5 File (File) (<a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.rawurlencode(isset($_REQUEST['listdirectory']) ? $_REQUEST['listdirectory'] : '.').'">disable</a>)</th>';
|
||||
echo '<th>MD5 Data (File) (<a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.rawurlencode(isset($_REQUEST['listdirectory']) ? $_REQUEST['listdirectory'] : '.').'">disable</a>)</th>';
|
||||
echo '<th>MD5 Data (Source) (<a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.rawurlencode(isset($_REQUEST['listdirectory']) ? $_REQUEST['listdirectory'] : '.').'">disable</a>)</th>';
|
||||
} else {
|
||||
echo '<th colspan="3">MD5 Data (<a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.rawurlencode(isset($_REQUEST['listdirectory']) ? $_REQUEST['listdirectory'] : '.').'&ShowMD5=1">enable</a>)</th>';
|
||||
}
|
||||
echo '<th>Tags</th>';
|
||||
echo '<th>Errors & Warnings</th>';
|
||||
echo '<th>Edit</th>';
|
||||
echo '<th>Delete</th>';
|
||||
echo '</tr>';
|
||||
|
||||
if (isset($DirectoryContents[$dirname]['known']) && is_array($DirectoryContents[$dirname]['known'])) {
|
||||
uksort($DirectoryContents[$dirname]['known'], 'MoreNaturalSort');
|
||||
foreach ($DirectoryContents[$dirname]['known'] as $filename => $fileinfo) {
|
||||
echo '<tr bgcolor="#'.(($rowcounter++ % 2) ? $getID3checkColor_FileDark : $getID3checkColor_FileLight).'">';
|
||||
echo '<td><a href="'.$_SERVER['PHP_SELF'].'?filename='.urlencode($dirname.$filename).'" title="View detailed analysis">'.FixTextFields(getid3_lib::SafeStripSlashes($filename)).'</a></td>';
|
||||
echo '<td align="right"> '.number_format($fileinfo['filesize']).'</td>';
|
||||
echo '<td align="right"> '.NiceDisplayFiletypeFormat($fileinfo).'</td>';
|
||||
echo '<td align="right"> '.(isset($fileinfo['playtime_string']) ? $fileinfo['playtime_string'] : '-').'</td>';
|
||||
echo '<td align="right"> '.(isset($fileinfo['bitrate']) ? BitrateText($fileinfo['bitrate'] / 1000, 0, ((@$fileinfo['audio']['bitrate_mode'] == 'vbr') ? true : false)) : '-').'</td>';
|
||||
echo '<td align="left"> '.(isset($fileinfo['comments_html']['artist']) ? implode('<br>', $fileinfo['comments_html']['artist']) : '').'</td>';
|
||||
echo '<td align="left"> '.(isset($fileinfo['comments_html']['title']) ? implode('<br>', $fileinfo['comments_html']['title']) : '').'</td>';
|
||||
if (isset($_REQUEST['ShowMD5'])) {
|
||||
echo '<td align="left"><tt>'.(isset($fileinfo['md5_file']) ? $fileinfo['md5_file'] : ' ').'</tt></td>';
|
||||
echo '<td align="left"><tt>'.(isset($fileinfo['md5_data']) ? $fileinfo['md5_data'] : ' ').'</tt></td>';
|
||||
echo '<td align="left"><tt>'.(isset($fileinfo['md5_data_source']) ? $fileinfo['md5_data_source'] : ' ').'</tt></td>';
|
||||
} else {
|
||||
echo '<td align="center" colspan="3">-</td>';
|
||||
}
|
||||
echo '<td align="left"> '.@implode(', ', array_keys($fileinfo['tags'])).'</td>';
|
||||
|
||||
echo '<td align="left"> ';
|
||||
if (!empty($fileinfo['warning'])) {
|
||||
$FilesWithWarnings++;
|
||||
echo '<a href="#" onClick="alert(\''.FixTextFields(implode('\\n', $fileinfo['warning'])).'\'); return false;" title="'.FixTextFields(implode("\n", $fileinfo['warning'])).'">warning</a><br>';
|
||||
}
|
||||
if (!empty($fileinfo['error'])) {
|
||||
$FilesWithErrors++;
|
||||
echo '<a href="#" onClick="alert(\''.FixTextFields(implode('\\n', $fileinfo['error'])).'\'); return false;" title="'.FixTextFields(implode("\n", $fileinfo['error'])).'">error</a><br>';
|
||||
}
|
||||
echo '</td>';
|
||||
|
||||
echo '<td align="left"> ';
|
||||
switch (@$fileinfo['fileformat']) {
|
||||
case 'mp3':
|
||||
case 'mp2':
|
||||
case 'mp1':
|
||||
case 'flac':
|
||||
case 'mpc':
|
||||
case 'real':
|
||||
echo '<a href="'.$writescriptfilename.'?Filename='.urlencode($dirname.$filename).'" title="Edit tags">edit tags</a>';
|
||||
break;
|
||||
case 'ogg':
|
||||
switch (@$fileinfo['audio']['dataformat']) {
|
||||
case 'vorbis':
|
||||
echo '<a href="'.$writescriptfilename.'?Filename='.urlencode($dirname.$filename).'" title="Edit tags">edit tags</a>';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
echo '</td>';
|
||||
echo '<td align="left"> <a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.urlencode($listdirectory).'&deletefile='.urlencode($dirname.$filename).'" onClick="return confirm(\'Are you sure you want to delete '.addslashes(htmlentities($dirname.$filename)).'? \n(this action cannot be un-done)\');" title="Permanently delete '."\n".FixTextFields($filename)."\n".' from'."\n".' '.FixTextFields($dirname).'">delete</a></td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($DirectoryContents[$dirname]['other']) && is_array($DirectoryContents[$dirname]['other'])) {
|
||||
uksort($DirectoryContents[$dirname]['other'], 'MoreNaturalSort');
|
||||
foreach ($DirectoryContents[$dirname]['other'] as $filename => $fileinfo) {
|
||||
echo '<tr bgcolor="#'.(($rowcounter++ % 2) ? $getID3checkColor_UnknownDark : $getID3checkColor_UnknownLight).'">';
|
||||
echo '<td><a href="'.$_SERVER['PHP_SELF'].'?filename='.urlencode($dirname.$filename).'"><i>'.$filename.'</i></a></td>';
|
||||
echo '<td align="right"> '.(isset($fileinfo['filesize']) ? number_format($fileinfo['filesize']) : '-').'</td>';
|
||||
echo '<td align="right"> '.NiceDisplayFiletypeFormat($fileinfo).'</td>';
|
||||
echo '<td align="right"> '.(isset($fileinfo['playtime_string']) ? $fileinfo['playtime_string'] : '-').'</td>';
|
||||
echo '<td align="right"> '.(isset($fileinfo['bitrate']) ? BitrateText($fileinfo['bitrate'] / 1000) : '-').'</td>';
|
||||
echo '<td align="left"> </td>'; // Artist
|
||||
echo '<td align="left"> </td>'; // Title
|
||||
echo '<td align="left" colspan="3"> </td>'; // MD5_data
|
||||
echo '<td align="left"> </td>'; // Tags
|
||||
|
||||
//echo '<td align="left"> </td>'; // Warning/Error
|
||||
echo '<td align="left"> ';
|
||||
if (!empty($fileinfo['warning'])) {
|
||||
$FilesWithWarnings++;
|
||||
echo '<a href="#" onClick="alert(\''.FixTextFields(implode('\\n', $fileinfo['warning'])).'\'); return false;" title="'.FixTextFields(implode("\n", $fileinfo['warning'])).'">warning</a><br>';
|
||||
}
|
||||
if (!empty($fileinfo['error'])) {
|
||||
if ($fileinfo['error'][0] != 'unable to determine file format') {
|
||||
$FilesWithErrors++;
|
||||
echo '<a href="#" onClick="alert(\''.FixTextFields(implode('\\n', $fileinfo['error'])).'\'); return false;" title="'.FixTextFields(implode("\n", $fileinfo['error'])).'">error</a><br>';
|
||||
}
|
||||
}
|
||||
echo '</td>';
|
||||
|
||||
echo '<td align="left"> </td>'; // Edit
|
||||
echo '<td align="left"> <a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.urlencode($listdirectory).'&deletefile='.urlencode($dirname.$filename).'" onClick="return confirm(\'Are you sure you want to delete '.addslashes($dirname.$filename).'? \n(this action cannot be un-done)\');" title="Permanently delete '.addslashes($dirname.$filename).'">delete</a></td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
}
|
||||
|
||||
echo '<tr bgcolor="#'.$getID3checkColor_Head.'">';
|
||||
echo '<td><b>Average:</b></td>';
|
||||
echo '<td align="right">'.number_format($TotalScannedFilesize / max($TotalScannedKnownFiles, 1)).'</td>';
|
||||
echo '<td> </td>';
|
||||
echo '<td align="right">'.getid3_lib::PlaytimeString($TotalScannedPlaytime / max($TotalScannedPlaytimeFiles, 1)).'</td>';
|
||||
echo '<td align="right">'.BitrateText(round(($TotalScannedBitrate / 1000) / max($TotalScannedBitrateFiles, 1))).'</td>';
|
||||
echo '<td rowspan="2" colspan="'.($columnsintable - 5).'"><table class="table" border="0" cellspacing="0" cellpadding="2"><tr><th align="right">Identified Files:</th><td align="right">'.number_format($TotalScannedKnownFiles).'</td><td> </td><th align="right">Errors:</th><td align="right">'.number_format($FilesWithErrors).'</td></tr><tr><th align="right">Unknown Files:</th><td align="right">'.number_format($TotalScannedUnknownFiles).'</td><td> </td><th align="right">Warnings:</th><td align="right">'.number_format($FilesWithWarnings).'</td></tr></table>';
|
||||
echo '</tr>';
|
||||
echo '<tr bgcolor="#'.$getID3checkColor_Head.'">';
|
||||
echo '<td><b>Total:</b></td>';
|
||||
echo '<td align="right">'.number_format($TotalScannedFilesize).'</td>';
|
||||
echo '<td> </td>';
|
||||
echo '<td align="right">'.getid3_lib::PlaytimeString($TotalScannedPlaytime).'</td>';
|
||||
echo '<td> </td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
echo '</table>';
|
||||
} else {
|
||||
echo '<b>ERROR: Could not open directory: <u>'.$currentfulldir.'</u></b><br>';
|
||||
}
|
||||
}
|
||||
echo PoweredBygetID3();
|
||||
echo 'Running on PHP v'.phpversion();
|
||||
echo '</body></html>';
|
||||
ob_end_flush();
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
function RemoveAccents($string) {
|
||||
// Revised version by markstewardרotmail*com
|
||||
// Again revised by James Heinrich (19-June-2006)
|
||||
return strtr(
|
||||
strtr(
|
||||
$string,
|
||||
"\x8A\x8E\x9A\x9E\x9F\xC0\xC1\xC2\xC3\xC4\xC5\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFF",
|
||||
'SZszYAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy'
|
||||
),
|
||||
array(
|
||||
"\xDE" => 'TH',
|
||||
"\xFE" => 'th',
|
||||
"\xD0" => 'DH',
|
||||
"\xF0" => 'dh',
|
||||
"\xDF" => 'ss',
|
||||
"\x8C" => 'OE',
|
||||
"\x9C" => 'oe',
|
||||
"\xC6" => 'AE',
|
||||
"\xE6" => 'ae',
|
||||
"\xB5" => 'u'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function BitrateColor($bitrate, $BitrateMaxScale=768) {
|
||||
// $BitrateMaxScale is bitrate of maximum-quality color (bright green)
|
||||
// below this is gradient, above is solid green
|
||||
|
||||
$bitrate *= (256 / $BitrateMaxScale); // scale from 1-[768]kbps to 1-256
|
||||
$bitrate = round(min(max($bitrate, 1), 256));
|
||||
$bitrate--; // scale from 1-256kbps to 0-255kbps
|
||||
|
||||
$Rcomponent = max(255 - ($bitrate * 2), 0);
|
||||
$Gcomponent = max(($bitrate * 2) - 255, 0);
|
||||
if ($bitrate > 127) {
|
||||
$Bcomponent = max((255 - $bitrate) * 2, 0);
|
||||
} else {
|
||||
$Bcomponent = max($bitrate * 2, 0);
|
||||
}
|
||||
return str_pad(dechex($Rcomponent), 2, '0', STR_PAD_LEFT).str_pad(dechex($Gcomponent), 2, '0', STR_PAD_LEFT).str_pad(dechex($Bcomponent), 2, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
function BitrateText($bitrate, $decimals=0, $vbr=false) {
|
||||
return '<SPAN STYLE="color: #'.BitrateColor($bitrate).($vbr ? '; font-weight: bold;' : '').'">'.number_format($bitrate, $decimals).' kbps</SPAN>';
|
||||
}
|
||||
|
||||
function FixTextFields($text) {
|
||||
$text = getid3_lib::SafeStripSlashes($text);
|
||||
$text = htmlentities($text, ENT_QUOTES);
|
||||
return $text;
|
||||
}
|
||||
|
||||
|
||||
function string_var_dump($variable) {
|
||||
ob_start();
|
||||
var_dump($variable);
|
||||
$dumpedvariable = ob_get_contents();
|
||||
ob_end_clean();
|
||||
return $dumpedvariable;
|
||||
}
|
||||
|
||||
|
||||
function table_var_dump($variable, $wrap_in_td=false) {
|
||||
$returnstring = '';
|
||||
switch (gettype($variable)) {
|
||||
case 'array':
|
||||
$returnstring .= ($wrap_in_td ? '<td>' : '');
|
||||
$returnstring .= '<table class="dump" cellspacing="0" cellpadding="2">';
|
||||
foreach ($variable as $key => $value) {
|
||||
$returnstring .= '<tr><td valign="top"><b>'.str_replace("\x00", ' ', $key).'</b></td>';
|
||||
$returnstring .= '<td valign="top">'.gettype($value);
|
||||
if (is_array($value)) {
|
||||
$returnstring .= ' ('.count($value).')';
|
||||
} elseif (is_string($value)) {
|
||||
$returnstring .= ' ('.strlen($value).')';
|
||||
}
|
||||
if (($key == 'data') && isset($variable['image_mime']) && isset($variable['dataoffset'])) {
|
||||
$imageinfo = array();
|
||||
$imagechunkcheck = getid3_lib::GetDataImageSize($value, $imageinfo);
|
||||
$DumpedImageSRC = (!empty($_REQUEST['filename']) ? $_REQUEST['filename'] : '.getid3').'.'.$variable['dataoffset'].'.'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]);
|
||||
if ($tempimagefile = @fopen($DumpedImageSRC, 'wb')) {
|
||||
fwrite($tempimagefile, $value);
|
||||
fclose($tempimagefile);
|
||||
}
|
||||
$returnstring .= '</td><td><img src="'.$_SERVER['PHP_SELF'].'?showfile='.urlencode($DumpedImageSRC).'&md5='.md5_file($DumpedImageSRC).'" width="'.$imagechunkcheck[0].'" height="'.$imagechunkcheck[1].'"></td></tr>';
|
||||
} else {
|
||||
$returnstring .= '</td>'.table_var_dump($value, true).'</tr>';
|
||||
}
|
||||
}
|
||||
$returnstring .= '</table>';
|
||||
$returnstring .= ($wrap_in_td ? '</td>' : '');
|
||||
break;
|
||||
|
||||
case 'boolean':
|
||||
$returnstring .= ($wrap_in_td ? '<td class="dump_boolean">' : '').($variable ? 'TRUE' : 'FALSE').($wrap_in_td ? '</td>' : '');
|
||||
break;
|
||||
|
||||
case 'integer':
|
||||
$returnstring .= ($wrap_in_td ? '<td class="dump_integer">' : '').$variable.($wrap_in_td ? '</td>' : '');
|
||||
break;
|
||||
|
||||
case 'double':
|
||||
case 'float':
|
||||
$returnstring .= ($wrap_in_td ? '<td class="dump_double">' : '').$variable.($wrap_in_td ? '</td>' : '');
|
||||
break;
|
||||
|
||||
case 'object':
|
||||
case 'null':
|
||||
$returnstring .= ($wrap_in_td ? '<td>' : '').string_var_dump($variable).($wrap_in_td ? '</td>' : '');
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
$variable = str_replace("\x00", ' ', $variable);
|
||||
$varlen = strlen($variable);
|
||||
for ($i = 0; $i < $varlen; $i++) {
|
||||
if (ereg('['."\x0A\x0D".' -;0-9A-Za-z]', $variable{$i})) {
|
||||
$returnstring .= $variable{$i};
|
||||
} else {
|
||||
$returnstring .= '&#'.str_pad(ord($variable{$i}), 3, '0', STR_PAD_LEFT).';';
|
||||
}
|
||||
}
|
||||
$returnstring = ($wrap_in_td ? '<td class="dump_string">' : '').nl2br($returnstring).($wrap_in_td ? '</td>' : '');
|
||||
break;
|
||||
|
||||
default:
|
||||
$imageinfo = array();
|
||||
$imagechunkcheck = getid3_lib::GetDataImageSize($variable, $imageinfo);
|
||||
if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) {
|
||||
$returnstring .= ($wrap_in_td ? '<td>' : '');
|
||||
$returnstring .= '<table class="dump" cellspacing="0" cellpadding="2">';
|
||||
$returnstring .= '<tr><td><b>type</b></td><td>'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]).'</td></tr>';
|
||||
$returnstring .= '<tr><td><b>width</b></td><td>'.number_format($imagechunkcheck[0]).' px</td></tr>';
|
||||
$returnstring .= '<tr><td><b>height</b></td><td>'.number_format($imagechunkcheck[1]).' px</td></tr>';
|
||||
$returnstring .= '<tr><td><b>size</b></td><td>'.number_format(strlen($variable)).' bytes</td></tr></table>';
|
||||
$returnstring .= ($wrap_in_td ? '</td>' : '');
|
||||
} else {
|
||||
$returnstring .= ($wrap_in_td ? '<td>' : '').nl2br(htmlspecialchars(str_replace("\x00", ' ', $variable))).($wrap_in_td ? '</td>' : '');
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $returnstring;
|
||||
}
|
||||
|
||||
|
||||
function NiceDisplayFiletypeFormat(&$fileinfo) {
|
||||
|
||||
if (empty($fileinfo['fileformat'])) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
$output = $fileinfo['fileformat'];
|
||||
if (empty($fileinfo['video']['dataformat']) && empty($fileinfo['audio']['dataformat'])) {
|
||||
return $output; // 'gif'
|
||||
}
|
||||
if (empty($fileinfo['video']['dataformat']) && !empty($fileinfo['audio']['dataformat'])) {
|
||||
if ($fileinfo['fileformat'] == $fileinfo['audio']['dataformat']) {
|
||||
return $output; // 'mp3'
|
||||
}
|
||||
$output .= '.'.$fileinfo['audio']['dataformat']; // 'ogg.flac'
|
||||
return $output;
|
||||
}
|
||||
if (!empty($fileinfo['video']['dataformat']) && empty($fileinfo['audio']['dataformat'])) {
|
||||
if ($fileinfo['fileformat'] == $fileinfo['video']['dataformat']) {
|
||||
return $output; // 'mpeg'
|
||||
}
|
||||
$output .= '.'.$fileinfo['video']['dataformat']; // 'riff.avi'
|
||||
return $output;
|
||||
}
|
||||
if ($fileinfo['video']['dataformat'] == $fileinfo['audio']['dataformat']) {
|
||||
if ($fileinfo['fileformat'] == $fileinfo['video']['dataformat']) {
|
||||
return $output; // 'real'
|
||||
}
|
||||
$output .= '.'.$fileinfo['video']['dataformat']; // any examples?
|
||||
return $output;
|
||||
}
|
||||
$output .= '.'.$fileinfo['video']['dataformat'];
|
||||
$output .= '.'.$fileinfo['audio']['dataformat']; // asf.wmv.wma
|
||||
return $output;
|
||||
|
||||
}
|
||||
|
||||
function MoreNaturalSort($ar1, $ar2) {
|
||||
if ($ar1 === $ar2) {
|
||||
return 0;
|
||||
}
|
||||
$len1 = strlen($ar1);
|
||||
$len2 = strlen($ar2);
|
||||
$shortest = min($len1, $len2);
|
||||
if (substr($ar1, 0, $shortest) === substr($ar2, 0, $shortest)) {
|
||||
// the shorter argument is the beginning of the longer one, like "str" and "string"
|
||||
if ($len1 < $len2) {
|
||||
return -1;
|
||||
} elseif ($len1 > $len2) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
$ar1 = RemoveAccents(strtolower(trim($ar1)));
|
||||
$ar2 = RemoveAccents(strtolower(trim($ar2)));
|
||||
$translatearray = array('\''=>'', '"'=>'', '_'=>' ', '('=>'', ')'=>'', '-'=>' ', ' '=>' ', '.'=>'', ','=>'');
|
||||
foreach ($translatearray as $key => $val) {
|
||||
$ar1 = str_replace($key, $val, $ar1);
|
||||
$ar2 = str_replace($key, $val, $ar2);
|
||||
}
|
||||
|
||||
if ($ar1 < $ar2) {
|
||||
return -1;
|
||||
} elseif ($ar1 > $ar2) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function PoweredBygetID3($string='<br><HR NOSHADE><DIV STYLE="font-size: 8pt; font-face: sans-serif;">Powered by <a href="http://getid3.sourceforge.net" TARGET="_blank"><b>getID3() v<!--GETID3VER--></b><br>http://getid3.sourceforge.net</a></DIV>') {
|
||||
return str_replace('<!--GETID3VER-->', GETID3_VERSION, $string);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Unify the contents of GPC,
|
||||
// whether magic_quotes_gpc is on or off
|
||||
|
||||
function AddStripSlashesArray($input, $addslashes=false) {
|
||||
if (is_array($input)) {
|
||||
|
||||
$output = $input;
|
||||
foreach ($input as $key => $value) {
|
||||
$output[$key] = AddStripSlashesArray($input[$key]);
|
||||
}
|
||||
return $output;
|
||||
|
||||
} elseif ($addslashes) {
|
||||
return addslashes($input);
|
||||
}
|
||||
return stripslashes($input);
|
||||
}
|
||||
|
||||
function UnifyMagicQuotes($turnon=false) {
|
||||
global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS;
|
||||
|
||||
if (get_magic_quotes_gpc() && !$turnon) {
|
||||
|
||||
// magic_quotes_gpc is on and we want it off!
|
||||
$_GET = AddStripSlashesArray($_GET, true);
|
||||
$_POST = AddStripSlashesArray($_POST, true);
|
||||
$_COOKIE = AddStripSlashesArray($_COOKIE, true);
|
||||
|
||||
unset($_REQUEST);
|
||||
$_REQUEST = array_merge_recursive($_GET, $_POST, $_COOKIE);
|
||||
|
||||
} elseif (!get_magic_quotes_gpc() && $turnon) {
|
||||
|
||||
// magic_quotes_gpc is off and we want it on (why??)
|
||||
$_GET = AddStripSlashesArray($_GET, true);
|
||||
$_POST = AddStripSlashesArray($_POST, true);
|
||||
$_COOKIE = AddStripSlashesArray($_COOKIE, true);
|
||||
|
||||
unset($_REQUEST);
|
||||
$_REQUEST = array_merge_recursive($_GET, $_POST, $_COOKIE);
|
||||
|
||||
}
|
||||
$HTTP_GET_VARS = $_GET;
|
||||
$HTTP_POST_VARS = $_POST;
|
||||
$HTTP_COOKIE_VARS = $_COOKIE;
|
||||
|
||||
return true;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
?>
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// /demo/demo.cache.dbm.php - part of getID3() //
|
||||
// Sample script demonstrating the use of the DBM caching //
|
||||
// extension for getID3() //
|
||||
// See readme.txt for more details //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
require_once('../getid3/getid3.php');
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'extension.cache.dbm.php', __FILE__, true);
|
||||
|
||||
$getID3 = new getID3_cached_dbm('db3', '/zimweb/test/test.dbm', '/zimweb/test/test.lock');
|
||||
|
||||
$r = $getID3->analyze('/path/to/files/filename.mp3');
|
||||
|
||||
echo '<pre>';
|
||||
var_dump($r);
|
||||
echo '</pre>';
|
||||
|
||||
// uncomment to clear cache
|
||||
// $getID3->clear_cache();
|
||||
|
||||
?>
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// /demo/demo.cache.mysql.php - part of getID3() //
|
||||
// Sample script demonstrating the use of the DBM caching //
|
||||
// extension for getID3() //
|
||||
// See readme.txt for more details //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
require_once('../getid3/getid3.php');
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'extension.cache.mysql.php', __FILE__, true);
|
||||
|
||||
$getID3 = new getID3_cached_mysql('localhost', 'database', 'username', 'password');
|
||||
|
||||
$r = $getID3->analyze('/path/to/files/filename.mp3');
|
||||
|
||||
echo '<pre>';
|
||||
var_dump($r);
|
||||
echo '</pre>';
|
||||
|
||||
// uncomment to clear cache
|
||||
//$getID3->clear_cache();
|
||||
|
||||
?>
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// /demo/demo.joinmp3.php - part of getID3() //
|
||||
// Sample script for splicing two or more MP3s together into //
|
||||
// one file. Does not attempt to fix VBR header frames. //
|
||||
// See readme.txt for more details //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// sample usage:
|
||||
// $FilenameOut = 'combined.mp3';
|
||||
// $FilenamesIn[] = 'file1.mp3';
|
||||
// $FilenamesIn[] = 'file2.mp3';
|
||||
// $FilenamesIn[] = 'file3.mp3';
|
||||
//
|
||||
// if (CombineMultipleMP3sTo($FilenameOut, $FilenamesIn)) {
|
||||
// echo 'Successfully copied '.implode(' + ', $FilenamesIn).' to '.$FilenameOut;
|
||||
// } else {
|
||||
// echo 'Failed to copy '.implode(' + ', $FilenamesIn).' to '.$FilenameOut;
|
||||
// }
|
||||
|
||||
function CombineMultipleMP3sTo($FilenameOut, $FilenamesIn) {
|
||||
|
||||
foreach ($FilenamesIn as $nextinputfilename) {
|
||||
if (!is_readable($nextinputfilename)) {
|
||||
echo 'Cannot read "'.$nextinputfilename.'"<BR>';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!is_writeable($FilenameOut)) {
|
||||
echo 'Cannot write "'.$FilenameOut.'"<BR>';
|
||||
return false;
|
||||
}
|
||||
|
||||
require_once('../getid3/getid3.php');
|
||||
if ($fp_output = @fopen($FilenameOut, 'wb')) {
|
||||
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
foreach ($FilenamesIn as $nextinputfilename) {
|
||||
|
||||
$CurrentFileInfo = $getID3->analyze($nextinputfilename);
|
||||
if ($CurrentFileInfo['fileformat'] == 'mp3') {
|
||||
|
||||
if ($fp_source = @fopen($nextinputfilename, 'rb')) {
|
||||
|
||||
$CurrentOutputPosition = ftell($fp_output);
|
||||
|
||||
// copy audio data from first file
|
||||
fseek($fp_source, $CurrentFileInfo['avdataoffset'], SEEK_SET);
|
||||
while (!feof($fp_source) && (ftell($fp_source) < $CurrentFileInfo['avdataend'])) {
|
||||
fwrite($fp_output, fread($fp_source, 32768));
|
||||
}
|
||||
fclose($fp_source);
|
||||
|
||||
// trim post-audio data (if any) copied from first file that we don't need or want
|
||||
$EndOfFileOffset = $CurrentOutputPosition + ($CurrentFileInfo['avdataend'] - $CurrentFileInfo['avdataoffset']);
|
||||
fseek($fp_output, $EndOfFileOffset, SEEK_SET);
|
||||
ftruncate($fp_output, $EndOfFileOffset);
|
||||
|
||||
} else {
|
||||
|
||||
echo 'failed to open '.$nextinputfilename.' for reading';
|
||||
fclose($fp_output);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
echo $nextinputfilename.' is not MP3 format';
|
||||
fclose($fp_output);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
echo 'failed to open '.$FilenameOut.' for writing';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
fclose($fp_output);
|
||||
return true;
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// /demo/demo.mimeonly.php - part of getID3() //
|
||||
// Sample script for scanning a single file and returning only //
|
||||
// the MIME information //
|
||||
// See readme.txt for more details //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
echo '<HTML><HEAD><STYLE>BODY, TD, TH { font-family: sans-serif; font-size: 10pt; }</STYLE></HEAD><BODY>';
|
||||
|
||||
if (!empty($_REQUEST['filename'])) {
|
||||
|
||||
echo 'The file "'.$_REQUEST['filename'].'" has a MIME type of "'.GetMIMEtype($_REQUEST['filename']).'"';
|
||||
|
||||
} else {
|
||||
|
||||
echo 'Usage: <TT>'.$_SERVER['PHP_SELF'].'?filename=<I>filename.ext</I></TT>';
|
||||
|
||||
}
|
||||
|
||||
|
||||
function GetMIMEtype($filename) {
|
||||
// include getID3() library (can be in a different directory if full path is specified)
|
||||
require_once('../getid3/getid3.php');
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
|
||||
$DeterminedMIMEtype = '';
|
||||
if ($fp = fopen($filename, 'rb')) {
|
||||
$ThisFileInfo = array('avdataoffset'=>0, 'avdataend'=>0);
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
|
||||
$tag = new getid3_id3v2($fp, $ThisFileInfo);
|
||||
|
||||
fseek($fp, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$formattest = fread($fp, 16); // 16 bytes is sufficient for any format except ISO CD-image
|
||||
fclose($fp);
|
||||
|
||||
$DeterminedFormatInfo = $getID3->GetFileFormat($formattest);
|
||||
$DeterminedMIMEtype = $DeterminedFormatInfo['mime_type'];
|
||||
}
|
||||
return $DeterminedMIMEtype;
|
||||
}
|
||||
|
||||
?>
|
||||
</BODY>
|
||||
</HTML>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// /demo/demo.simple.php - part of getID3() //
|
||||
// Sample script for scanning a single directory and //
|
||||
// displaying a few pieces of information for each file //
|
||||
// See readme.txt for more details //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
echo '<HTML><HEAD>';
|
||||
echo '<TITLE>getID3() - /demo/demo.simple.php (sample script)</TITLE>';
|
||||
echo '<STYLE>BODY,TD,TH { font-family: sans-serif; font-size: 9pt; }</STYLE>';
|
||||
echo '</HEAD><BODY>';
|
||||
|
||||
|
||||
// include getID3() library (can be in a different directory if full path is specified)
|
||||
require_once('../getid3/getid3.php');
|
||||
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
|
||||
$DirectoryToScan = '/change/to/directory/you/want/to/scan'; // change to whatever directory you want to scan
|
||||
$dir = opendir($DirectoryToScan);
|
||||
echo '<TABLE BORDER="1" CELLSPACING="0" CELLPADDING="3">';
|
||||
echo '<TR><TH>Filename</TH><TH>Artist</TH><TH>Title</TH><TH>Bitrate</TH><TH>Playtime</TH></TR>';
|
||||
while (($file = readdir($dir)) !== false) {
|
||||
$FullFileName = realpath($DirectoryToScan.'/'.$file);
|
||||
if (is_file($FullFileName)) {
|
||||
set_time_limit(30);
|
||||
|
||||
$ThisFileInfo = $getID3->analyze($FullFileName);
|
||||
|
||||
getid3_lib::CopyTagsToComments($ThisFileInfo);
|
||||
|
||||
// output desired information in whatever format you want
|
||||
echo '<TR>';
|
||||
echo '<TD>'.$ThisFileInfo['filenamepath'].'</TD>';
|
||||
echo '<TD>'.(!empty($ThisFileInfo['comments_html']['artist']) ? implode('<BR>', $ThisFileInfo['comments_html']['artist']) : ' ').'</TD>';
|
||||
echo '<TD>'.(!empty($ThisFileInfo['comments_html']['title']) ? implode('<BR>', $ThisFileInfo['comments_html']['title']) : ' ').'</TD>';
|
||||
echo '<TD ALIGN="RIGHT">'.(!empty($ThisFileInfo['audio']['bitrate']) ? round($ThisFileInfo['audio']['bitrate'] / 1000).' kbps' : ' ').'</TD>';
|
||||
echo '<TD ALIGN="RIGHT">'.(!empty($ThisFileInfo['playtime_string']) ? $ThisFileInfo['playtime_string'] : ' ').'</TD>';
|
||||
echo '</TR>';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
</BODY>
|
||||
</HTML>
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// /demo/demo.simple.write.php - part of getID3() //
|
||||
// Sample script showing basic syntax for writing tags //
|
||||
// See readme.txt for more details //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
$TaggingFormat = 'UTF-8';
|
||||
|
||||
require_once('../getid3/getid3.php');
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
$getID3->setOption(array('encoding'=>$TaggingFormat));
|
||||
|
||||
require_once('../getid3/write.php');
|
||||
// Initialize getID3 tag-writing module
|
||||
$tagwriter = new getid3_writetags;
|
||||
//$tagwriter->filename = '/path/to/file.mp3';
|
||||
$tagwriter->filename = 'd:/file.mp3';
|
||||
$tagwriter->tagformats = array('id3v1', 'id3v2.3');
|
||||
|
||||
// set various options (optional)
|
||||
$tagwriter->overwrite_tags = true;
|
||||
$tagwriter->tag_encoding = $TaggingFormat;
|
||||
$tagwriter->remove_other_tags = true;
|
||||
|
||||
// populate data array
|
||||
$TagData['title'][] = 'My Song';
|
||||
$TagData['artist'][] = 'The Artist';
|
||||
$TagData['album'][] = 'Greatest Hits';
|
||||
$TagData['year'][] = '2004';
|
||||
$TagData['genre'][] = 'Rock';
|
||||
$TagData['comment'][] = 'excellent!';
|
||||
$TagData['track'][] = '04/16';
|
||||
|
||||
$tagwriter->tag_data = $TagData;
|
||||
|
||||
// write tags
|
||||
if ($tagwriter->WriteTags()) {
|
||||
echo 'Successfully wrote tags<br>';
|
||||
if (!empty($tagwriter->warnings)) {
|
||||
echo 'There were some warnings:<br>'.implode('<br><br>', $tagwriter->warnings);
|
||||
}
|
||||
} else {
|
||||
echo 'Failed to write tags!<br>'.implode('<br><br>', $tagwriter->errors);
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,271 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// /demo/demo.write.php - part of getID3() //
|
||||
// sample script for demonstrating writing ID3v1 and ID3v2 //
|
||||
// tags for MP3, or Ogg comment tags for Ogg Vorbis //
|
||||
// See readme.txt for more details //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
die('Due to a security issue, this demo has been disabled. It can be enabled by removing line 16 in demos/demo.write.php');
|
||||
|
||||
|
||||
$TaggingFormat = 'UTF-8';
|
||||
|
||||
header('Content-Type: text/html; charset='.$TaggingFormat);
|
||||
echo '<HTML><HEAD><TITLE>getID3() - Sample tag writer</TITLE></HEAD><BODY>';
|
||||
|
||||
require_once('../getid3/getid3.php');
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
$getID3->setOption(array('encoding'=>$TaggingFormat));
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.php', __FILE__, true);
|
||||
|
||||
$browsescriptfilename = 'demo.browse.php';
|
||||
|
||||
function FixTextFields($text) {
|
||||
return htmlentities(getid3_lib::SafeStripSlashes($text), ENT_QUOTES);
|
||||
}
|
||||
|
||||
$Filename = (isset($_REQUEST['Filename']) ? getid3_lib::SafeStripSlashes($_REQUEST['Filename']) : '');
|
||||
|
||||
|
||||
|
||||
if (isset($_POST['WriteTags'])) {
|
||||
|
||||
$TagFormatsToWrite = (isset($_POST['TagFormatsToWrite']) ? $_POST['TagFormatsToWrite'] : array());
|
||||
if (!empty($TagFormatsToWrite)) {
|
||||
echo 'starting to write tag(s)<BR>';
|
||||
|
||||
$tagwriter = new getid3_writetags;
|
||||
$tagwriter->filename = $Filename;
|
||||
$tagwriter->tagformats = $TagFormatsToWrite;
|
||||
$tagwriter->overwrite_tags = true;
|
||||
$tagwriter->tag_encoding = $TaggingFormat;
|
||||
if (!empty($_POST['remove_other_tags'])) {
|
||||
$tagwriter->remove_other_tags = true;
|
||||
}
|
||||
|
||||
$commonkeysarray = array('Title', 'Artist', 'Album', 'Year', 'Comment');
|
||||
foreach ($commonkeysarray as $key) {
|
||||
if (!empty($_POST[$key])) {
|
||||
$TagData[strtolower($key)][] = getid3_lib::SafeStripSlashes($_POST[$key]);
|
||||
}
|
||||
}
|
||||
if (!empty($_POST['Genre'])) {
|
||||
$TagData['genre'][] = getid3_lib::SafeStripSlashes($_POST['Genre']);
|
||||
}
|
||||
if (!empty($_POST['GenreOther'])) {
|
||||
$TagData['genre'][] = getid3_lib::SafeStripSlashes($_POST['GenreOther']);
|
||||
}
|
||||
if (!empty($_POST['Track'])) {
|
||||
$TagData['track'][] = getid3_lib::SafeStripSlashes($_POST['Track'].(!empty($_POST['TracksTotal']) ? '/'.$_POST['TracksTotal'] : ''));
|
||||
}
|
||||
|
||||
if (!empty($_FILES['userfile']['tmp_name'])) {
|
||||
if (in_array('id3v2.4', $tagwriter->tagformats) || in_array('id3v2.3', $tagwriter->tagformats) || in_array('id3v2.2', $tagwriter->tagformats)) {
|
||||
if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {
|
||||
if ($fd = @fopen($_FILES['userfile']['tmp_name'], 'rb')) {
|
||||
$APICdata = fread($fd, filesize($_FILES['userfile']['tmp_name']));
|
||||
fclose ($fd);
|
||||
|
||||
list($APIC_width, $APIC_height, $APIC_imageTypeID) = GetImageSize($_FILES['userfile']['tmp_name']);
|
||||
$imagetypes = array(1=>'gif', 2=>'jpeg', 3=>'png');
|
||||
if (isset($imagetypes[$APIC_imageTypeID])) {
|
||||
|
||||
$TagData['attached_picture'][0]['data'] = $APICdata;
|
||||
$TagData['attached_picture'][0]['picturetypeid'] = $_POST['APICpictureType'];
|
||||
$TagData['attached_picture'][0]['description'] = $_FILES['userfile']['name'];
|
||||
$TagData['attached_picture'][0]['mime'] = 'image/'.$imagetypes[$APIC_imageTypeID];
|
||||
|
||||
} else {
|
||||
echo '<B>invalid image format (only GIF, JPEG, PNG)</B><BR>';
|
||||
}
|
||||
} else {
|
||||
echo '<B>cannot open '.$_FILES['userfile']['tmp_name'].'</B><BR>';
|
||||
}
|
||||
} else {
|
||||
echo '<B>!is_uploaded_file('.$_FILES['userfile']['tmp_name'].')</B><BR>';
|
||||
}
|
||||
} else {
|
||||
echo '<B>WARNING:</B> Can only embed images for ID3v2<BR>';
|
||||
}
|
||||
}
|
||||
|
||||
$tagwriter->tag_data = $TagData;
|
||||
if ($tagwriter->WriteTags()) {
|
||||
echo 'Successfully wrote tags<BR>';
|
||||
if (!empty($tagwriter->warnings)) {
|
||||
echo 'There were some warnings:<BLOCKQUOTE STYLE="background-color:#FFCC33; padding: 10px;">'.implode('<BR><BR>', $tagwriter->warnings).'</BLOCKQUOTE>';
|
||||
}
|
||||
} else {
|
||||
echo 'Failed to write tags!<BLOCKQUOTE STYLE="background-color:#FF9999; padding: 10px;">'.implode('<BR><BR>', $tagwriter->errors).'</BLOCKQUOTE>';
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
echo 'WARNING: no tag formats selected for writing - nothing written';
|
||||
|
||||
}
|
||||
echo '<HR>';
|
||||
|
||||
}
|
||||
|
||||
|
||||
echo '<H4>Sample tag editor/writer</H4>';
|
||||
echo '<A HREF="'.$browsescriptfilename.'?listdirectory='.rawurlencode(realpath(dirname($Filename))).'">Browse current directory</A><BR>';
|
||||
if (!empty($Filename)) {
|
||||
echo '<A HREF="'.$_SERVER['PHP_SELF'].'">Start Over</A><BR><BR>';
|
||||
echo '<TABLE BORDER="3" CELLSPACING="0" CELLPADDING="4"><FORM ACTION="'.$_SERVER['PHP_SELF'].'" METHOD="POST" ENCTYPE="multipart/form-data">';
|
||||
echo '<TR><TD ALIGN="RIGHT"><B>Filename: </B></TD><TD><INPUT TYPE="HIDDEN" NAME="Filename" VALUE="'.FixTextFields($Filename).'"><A HREF="'.$browsescriptfilename.'?filename='.rawurlencode($Filename).'" TARGET="_blank">'.$Filename.'</A></TD></TR>';
|
||||
if (file_exists($Filename)) {
|
||||
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
$OldThisFileInfo = $getID3->analyze($Filename);
|
||||
getid3_lib::CopyTagsToComments($OldThisFileInfo);
|
||||
|
||||
switch ($OldThisFileInfo['fileformat']) {
|
||||
case 'mp3':
|
||||
case 'mp2':
|
||||
case 'mp1':
|
||||
$ValidTagTypes = array('id3v1', 'id3v2.3', 'ape');
|
||||
break;
|
||||
|
||||
case 'mpc':
|
||||
$ValidTagTypes = array('ape');
|
||||
break;
|
||||
|
||||
case 'ogg':
|
||||
if (@$OldThisFileInfo['audio']['dataformat'] == 'flac') {
|
||||
//$ValidTagTypes = array('metaflac');
|
||||
// metaflac doesn't (yet) work with OggFLAC files
|
||||
$ValidTagTypes = array();
|
||||
} else {
|
||||
$ValidTagTypes = array('vorbiscomment');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'flac':
|
||||
$ValidTagTypes = array('metaflac');
|
||||
break;
|
||||
|
||||
case 'real':
|
||||
$ValidTagTypes = array('real');
|
||||
break;
|
||||
|
||||
default:
|
||||
$ValidTagTypes = array();
|
||||
break;
|
||||
}
|
||||
echo '<TR><TD ALIGN="RIGHT"><B>Title</B></TD> <TD><INPUT TYPE="TEXT" SIZE="40" NAME="Title" VALUE="'.FixTextFields(@implode(', ', @$OldThisFileInfo['comments']['title'])).'"></TD></TR>';
|
||||
echo '<TR><TD ALIGN="RIGHT"><B>Artist</B></TD><TD><INPUT TYPE="TEXT" SIZE="40" NAME="Artist" VALUE="'.FixTextFields(@implode(', ', @$OldThisFileInfo['comments']['artist'])).'"></TD></TR>';
|
||||
echo '<TR><TD ALIGN="RIGHT"><B>Album</B></TD> <TD><INPUT TYPE="TEXT" SIZE="40" NAME="Album" VALUE="'.FixTextFields(@implode(', ', @$OldThisFileInfo['comments']['album'])).'"></TD></TR>';
|
||||
echo '<TR><TD ALIGN="RIGHT"><B>Year</B></TD> <TD><INPUT TYPE="TEXT" SIZE="4" NAME="Year" VALUE="'.FixTextFields(@implode(', ', @$OldThisFileInfo['comments']['year'])).'"></TD></TR>';
|
||||
|
||||
$TracksTotal = '';
|
||||
$TrackNumber = '';
|
||||
if (!empty($OldThisFileInfo['comments']['tracknumber']) && is_array($OldThisFileInfo['comments']['tracknumber'])) {
|
||||
$RawTrackNumberArray = $OldThisFileInfo['comments']['tracknumber'];
|
||||
} elseif (!empty($OldThisFileInfo['comments']['track']) && is_array($OldThisFileInfo['comments']['track'])) {
|
||||
$RawTrackNumberArray = $OldThisFileInfo['comments']['track'];
|
||||
} else {
|
||||
$RawTrackNumberArray = array();
|
||||
}
|
||||
foreach ($RawTrackNumberArray as $key => $value) {
|
||||
if (strlen($value) > strlen($TrackNumber)) {
|
||||
// ID3v1 may store track as "3" but ID3v2/APE would store as "03/16"
|
||||
$TrackNumber = $value;
|
||||
}
|
||||
}
|
||||
if (strstr($TrackNumber, '/')) {
|
||||
list($TrackNumber, $TracksTotal) = explode('/', $TrackNumber);
|
||||
}
|
||||
echo '<TR><TD ALIGN="RIGHT"><B>Track</B></TD><TD><INPUT TYPE="TEXT" SIZE="2" NAME="Track" VALUE="'.FixTextFields($TrackNumber).'"> of <INPUT TYPE="TEXT" SIZE="2" NAME="TracksTotal" VALUE="'.FixTextFields($TracksTotal).'"></TD></TR>';
|
||||
|
||||
$ArrayOfGenresTemp = getid3_id3v1::ArrayOfGenres(); // get the array of genres
|
||||
foreach ($ArrayOfGenresTemp as $key => $value) { // change keys to match displayed value
|
||||
$ArrayOfGenres[$value] = $value;
|
||||
}
|
||||
unset($ArrayOfGenresTemp); // remove temporary array
|
||||
unset($ArrayOfGenres['Cover']); // take off these special cases
|
||||
unset($ArrayOfGenres['Remix']);
|
||||
unset($ArrayOfGenres['Unknown']);
|
||||
$ArrayOfGenres[''] = '- Unknown -'; // Add special cases back in with renamed key/value
|
||||
$ArrayOfGenres['Cover'] = '-Cover-';
|
||||
$ArrayOfGenres['Remix'] = '-Remix-';
|
||||
asort($ArrayOfGenres); // sort into alphabetical order
|
||||
echo '<TR><TD ALIGN="RIGHT"><B>Genre</B></TD><TD><SELECT NAME="Genre">';
|
||||
$AllGenresArray = (!empty($OldThisFileInfo['comments']['genre']) ? $OldThisFileInfo['comments']['genre'] : array());
|
||||
foreach ($ArrayOfGenres as $key => $value) {
|
||||
echo '<OPTION VALUE="'.$key.'"';
|
||||
if (in_array($key, $AllGenresArray)) {
|
||||
echo ' SELECTED';
|
||||
unset($AllGenresArray[array_search($key, $AllGenresArray)]);
|
||||
sort($AllGenresArray);
|
||||
}
|
||||
echo '>'.$value.'</OPTION>';
|
||||
//echo '<OPTION VALUE="'.FixTextFields($value).'"'.((@$OldThisFileInfo['comments']['genre'][0] == $value) ? ' SELECTED' : '').'>'.$value.'</OPTION>';
|
||||
}
|
||||
echo '</SELECT><INPUT TYPE="TEXT" NAME="GenreOther" SIZE="10" VALUE="'.FixTextFields(@$AllGenresArray[0]).'"></TD></TR>';
|
||||
|
||||
echo '<TR><TD ALIGN="RIGHT"><B>Write Tags</B></TD><TD>';
|
||||
foreach ($ValidTagTypes as $ValidTagType) {
|
||||
echo '<INPUT TYPE="CHECKBOX" NAME="TagFormatsToWrite[]" VALUE="'.$ValidTagType.'"';
|
||||
if (count($ValidTagTypes) == 1) {
|
||||
echo ' CHECKED';
|
||||
} else {
|
||||
switch ($ValidTagType) {
|
||||
case 'id3v2.2':
|
||||
case 'id3v2.3':
|
||||
case 'id3v2.4':
|
||||
if (isset($OldThisFileInfo['tags']['id3v2'])) {
|
||||
echo ' CHECKED';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isset($OldThisFileInfo['tags'][$ValidTagType])) {
|
||||
echo ' CHECKED';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
echo '>'.$ValidTagType.'<BR>';
|
||||
}
|
||||
if (count($ValidTagTypes) > 1) {
|
||||
echo '<hr><input type="checkbox" name="remove_other_tags" value="1"> Remove non-selected tag formats when writing new tag<br>';
|
||||
}
|
||||
echo '</TD></TR>';
|
||||
|
||||
echo '<TR><TD ALIGN="RIGHT"><B>Comment</B></TD><TD><TEXTAREA COLS="30" ROWS="3" NAME="Comment" WRAP="VIRTUAL">'.(isset($OldThisFileInfo['comments']['comment']) ? @implode("\n", $OldThisFileInfo['comments']['comment']) : '').'</TEXTAREA></TD></TR>';
|
||||
|
||||
echo '<TR><TD ALIGN="RIGHT"><B>Picture</B><BR>(ID3v2 only)</TD><TD><INPUT TYPE="FILE" NAME="userfile" ACCEPT="image/jpeg, image/gif, image/png"><BR>';
|
||||
echo '<SELECT NAME="APICpictureType">';
|
||||
$APICtypes = getid3_id3v2::APICPictureTypeLookup('', true);
|
||||
foreach ($APICtypes as $key => $value) {
|
||||
echo '<OPTION VALUE="'.FixTextFields($key).'">'.FixTextFields($value).'</OPTION>';
|
||||
}
|
||||
echo '</SELECT></TD></TR>';
|
||||
echo '<TR><TD ALIGN="CENTER" COLSPAN="2"><INPUT TYPE="SUBMIT" NAME="WriteTags" VALUE="Save Changes"> ';
|
||||
echo '<INPUT TYPE="RESET" VALUE="Reset"></TD></TR>';
|
||||
|
||||
} else {
|
||||
|
||||
echo '<TR><TD ALIGN="RIGHT"><B>Error</B></TD><TD>'.FixTextFields($Filename).' does not exist</TD></TR>';
|
||||
|
||||
}
|
||||
echo '</FORM></TABLE>';
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
</BODY>
|
||||
</HTML>
|
|
@ -0,0 +1,195 @@
|
|||
|
||||
/**
|
||||
* Common elements
|
||||
*/
|
||||
|
||||
body {
|
||||
font: 12px Verdana, sans-serif;
|
||||
background-color: white;
|
||||
color: black;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 30px;
|
||||
margin-left: 12px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
|
||||
h1 {
|
||||
font: bold 18px Verdana, sans-serif;
|
||||
line-height: 26px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 15px;
|
||||
margin-left: 0px;
|
||||
margin-right: 7px;
|
||||
background-color: #e6eaf6;
|
||||
padding-left: 10px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
|
||||
h3 {
|
||||
font: bold 13px Verdana, sans-serif;
|
||||
line-height: 26px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 0px;
|
||||
margin-left: 0px;
|
||||
margin-right: 7px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
|
||||
ul {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
|
||||
p, li {
|
||||
font: 9pt/135% sans-serif;
|
||||
margin-top: 1x;
|
||||
margin-bottom: 0x;
|
||||
}
|
||||
|
||||
|
||||
a, a:link, a:visited {
|
||||
color: #0000cc;
|
||||
}
|
||||
|
||||
|
||||
hr {
|
||||
height: 0;
|
||||
border: solid gray 0;
|
||||
border-top-width: thin;
|
||||
width: 700px;
|
||||
}
|
||||
|
||||
|
||||
table.table td {
|
||||
font: 9pt sans-serif;
|
||||
padding-top: 1px;
|
||||
padding-bottom: 1px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
|
||||
table.table td.header {
|
||||
background-color: #cccccc;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
table.table tr.even_files {
|
||||
background-color: #fefefe;
|
||||
}
|
||||
|
||||
|
||||
table.table tr.odd_files {
|
||||
background-color: #e9e9e9;
|
||||
}
|
||||
|
||||
|
||||
table.dump {
|
||||
border-top: solid 1px #cccccc;
|
||||
border-left: solid 1px #cccccc;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
|
||||
table.dump td {
|
||||
font: 9pt sans-serif;
|
||||
padding-top: 1px;
|
||||
padding-bottom: 1px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
border-right: solid 1px #cccccc;
|
||||
border-bottom: solid 1px #cccccc;
|
||||
}
|
||||
|
||||
|
||||
td.dump_string {
|
||||
font-weight: bold;
|
||||
color: #0000cc;
|
||||
}
|
||||
|
||||
|
||||
td.dump_integer {
|
||||
color: #cc0000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
td.dump_double {
|
||||
color: orange;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
td.dump_boolean {
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
.error {
|
||||
color: red
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Tool Tips
|
||||
*/
|
||||
|
||||
.tooltip {
|
||||
font: 9pt sans-serif;
|
||||
background: #ffffe1;
|
||||
color: black;
|
||||
border: black 1px solid;
|
||||
margin: 2px;
|
||||
padding: 7px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
z-index: 10000;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
||||
.tooltip p {
|
||||
margin-top: -2px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Forms
|
||||
*/
|
||||
|
||||
table.form td {
|
||||
font: 9pt/135% sans-serif;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
|
||||
select, input {
|
||||
font: 9pt/135% sans-serif;
|
||||
}
|
||||
|
||||
.select, .field {
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
#sel_field {
|
||||
width: 85px;
|
||||
}
|
||||
|
||||
|
||||
.button {
|
||||
margin-top: 10px;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
In this directory are a number of examples of how to use <A HREF="http://www.getid3.org">getID3()</A> - if you don't know what to run, take a look at <A HREF="demo.browse.php">demo.browse.php</A>
|
|
@ -0,0 +1,24 @@
|
|||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// dependencies.txt - part of getID3() //
|
||||
// See readme.txt for more details //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
lyrics3 depends on apetag (optional)
|
||||
ogg depends on flac
|
||||
id3v2 depends on id3v1
|
||||
apetag depends on id3v1 (optional, writing only)
|
||||
bonk depends on id3v2 (optional)
|
||||
riff depends on mp3
|
||||
mpeg depends on mp3
|
||||
quicktime depends on mp3
|
||||
flac depends on ogg
|
||||
optimfrog depends on riff
|
||||
la depends on riff
|
||||
lpac depends on riff
|
||||
asf depends on riff, id3v1 (optional)
|
|
@ -0,0 +1,222 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// extension.cache.dbm.php - part of getID3() //
|
||||
// Please see readme.txt for more information //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// This extension written by Allan Hansen <ahØartemis*dk> //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* This is a caching extension for getID3(). It works the exact same
|
||||
* way as the getID3 class, but return cached information very fast
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* Normal getID3 usage (example):
|
||||
*
|
||||
* require_once 'getid3/getid3.php';
|
||||
* $getID3 = new getID3;
|
||||
* $getID3->encoding = 'UTF-8';
|
||||
* $info1 = $getID3->analyze('file1.flac');
|
||||
* $info2 = $getID3->analyze('file2.wv');
|
||||
*
|
||||
* getID3_cached usage:
|
||||
*
|
||||
* require_once 'getid3/getid3.php';
|
||||
* require_once 'getid3/getid3/extension.cache.dbm.php';
|
||||
* $getID3 = new getID3_cached('db3', '/tmp/getid3_cache.dbm',
|
||||
* '/tmp/getid3_cache.lock');
|
||||
* $getID3->encoding = 'UTF-8';
|
||||
* $info1 = $getID3->analyze('file1.flac');
|
||||
* $info2 = $getID3->analyze('file2.wv');
|
||||
*
|
||||
*
|
||||
* Supported Cache Types
|
||||
*
|
||||
* SQL Databases: (use extension.cache.mysql)
|
||||
*
|
||||
* cache_type cache_options
|
||||
* -------------------------------------------------------------------
|
||||
* mysql host, database, username, password
|
||||
*
|
||||
*
|
||||
* DBM-Style Databases: (this extension)
|
||||
*
|
||||
* cache_type cache_options
|
||||
* -------------------------------------------------------------------
|
||||
* gdbm dbm_filename, lock_filename
|
||||
* ndbm dbm_filename, lock_filename
|
||||
* db2 dbm_filename, lock_filename
|
||||
* db3 dbm_filename, lock_filename
|
||||
* db4 dbm_filename, lock_filename (PHP5 required)
|
||||
*
|
||||
* PHP must have write access to both dbm_filename and lock_filename.
|
||||
*
|
||||
*
|
||||
* Recommended Cache Types
|
||||
*
|
||||
* Infrequent updates, many reads any DBM
|
||||
* Frequent updates mysql
|
||||
*/
|
||||
|
||||
|
||||
class getID3_cached_dbm extends getID3
|
||||
{
|
||||
|
||||
// public: constructor - see top of this file for cache type and cache_options
|
||||
function getID3_cached_dbm($cache_type, $dbm_filename, $lock_filename) {
|
||||
|
||||
// Check for dba extension
|
||||
if (!extension_loaded('dba')) {
|
||||
die('PHP is not compiled with dba support, required to use DBM style cache.');
|
||||
}
|
||||
|
||||
// Check for specific dba driver
|
||||
if (function_exists('dba_handlers')) { // PHP 4.3.0+
|
||||
if (!in_array('db3', dba_handlers())) {
|
||||
die('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.');
|
||||
}
|
||||
}
|
||||
else { // PHP <= 4.2.3
|
||||
ob_start(); // nasty, buy the only way to check...
|
||||
phpinfo();
|
||||
$contents = ob_get_contents();
|
||||
ob_end_clean();
|
||||
if (!strstr($contents, $cache_type)) {
|
||||
die('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.');
|
||||
}
|
||||
}
|
||||
|
||||
// Create lock file if needed
|
||||
if (!file_exists($lock_filename)) {
|
||||
if (!touch($lock_filename)) {
|
||||
die('failed to create lock file: ' . $lock_filename);
|
||||
}
|
||||
}
|
||||
|
||||
// Open lock file for writing
|
||||
if (!is_writeable($lock_filename)) {
|
||||
die('lock file: ' . $lock_filename . ' is not writable');
|
||||
}
|
||||
$this->lock = fopen($lock_filename, 'w');
|
||||
|
||||
// Acquire exclusive write lock to lock file
|
||||
flock($this->lock, LOCK_EX);
|
||||
|
||||
// Create dbm-file if needed
|
||||
if (!file_exists($dbm_filename)) {
|
||||
if (!touch($dbm_filename)) {
|
||||
die('failed to create dbm file: ' . $dbm_filename);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to open dbm file for writing
|
||||
$this->dba = @dba_open($dbm_filename, 'w', $cache_type);
|
||||
if (!$this->dba) {
|
||||
|
||||
// Failed - create new dbm file
|
||||
$this->dba = dba_open($dbm_filename, 'n', $cache_type);
|
||||
|
||||
if (!$this->dba) {
|
||||
die('failed to create dbm file: ' . $dbm_filename);
|
||||
}
|
||||
|
||||
// Insert getID3 version number
|
||||
dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba);
|
||||
}
|
||||
|
||||
// Init misc values
|
||||
$this->cache_type = $cache_type;
|
||||
$this->dbm_filename = $dbm_filename;
|
||||
|
||||
// Register destructor
|
||||
register_shutdown_function(array($this, '__destruct'));
|
||||
|
||||
// Check version number and clear cache if changed
|
||||
if (dba_fetch(GETID3_VERSION, $this->dba) != GETID3_VERSION) {
|
||||
$this->clear_cache();
|
||||
}
|
||||
|
||||
parent::getID3();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// public: destuctor
|
||||
function __destruct() {
|
||||
|
||||
// Close dbm file
|
||||
@dba_close($this->dba);
|
||||
|
||||
// Release exclusive lock
|
||||
@flock($this->lock, LOCK_UN);
|
||||
|
||||
// Close lock file
|
||||
@fclose($this->lock);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// public: clear cache
|
||||
function clear_cache() {
|
||||
|
||||
// Close dbm file
|
||||
dba_close($this->dba);
|
||||
|
||||
// Create new dbm file
|
||||
$this->dba = dba_open($this->dbm_filename, 'n', $this->cache_type);
|
||||
|
||||
if (!$this->dba) {
|
||||
die('failed to clear cache/recreate dbm file: ' . $this->dbm_filename);
|
||||
}
|
||||
|
||||
// Insert getID3 version number
|
||||
dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba);
|
||||
|
||||
// Reregister shutdown function
|
||||
register_shutdown_function(array($this, '__destruct'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// public: analyze file
|
||||
function analyze($filename) {
|
||||
|
||||
if (file_exists($filename)) {
|
||||
|
||||
// Calc key filename::mod_time::size - should be unique
|
||||
$key = $filename . '::' . filemtime($filename) . '::' . filesize($filename);
|
||||
|
||||
// Loopup key
|
||||
$result = dba_fetch($key, $this->dba);
|
||||
|
||||
// Hit
|
||||
if ($result !== false) {
|
||||
return unserialize($result);
|
||||
}
|
||||
}
|
||||
|
||||
// Miss
|
||||
$result = parent::analyze($filename);
|
||||
|
||||
// Save result
|
||||
if (file_exists($filename)) {
|
||||
dba_insert($key, serialize($result), $this->dba);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,171 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// extension.cache.mysql.php - part of getID3() //
|
||||
// Please see readme.txt for more information //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// This extension written by Allan Hansen <ahØartemis*dk> //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* This is a caching extension for getID3(). It works the exact same
|
||||
* way as the getID3 class, but return cached information very fast
|
||||
*
|
||||
* Example: (see also demo.cache.mysql.php in /demo/)
|
||||
*
|
||||
* Normal getID3 usage (example):
|
||||
*
|
||||
* require_once 'getid3/getid3.php';
|
||||
* $getID3 = new getID3;
|
||||
* $getID3->encoding = 'UTF-8';
|
||||
* $info1 = $getID3->analyze('file1.flac');
|
||||
* $info2 = $getID3->analyze('file2.wv');
|
||||
*
|
||||
* getID3_cached usage:
|
||||
*
|
||||
* require_once 'getid3/getid3.php';
|
||||
* require_once 'getid3/getid3/extension.cache.mysql.php';
|
||||
* $getID3 = new getID3_cached_mysql('localhost', 'database',
|
||||
* 'username', 'password');
|
||||
* $getID3->encoding = 'UTF-8';
|
||||
* $info1 = $getID3->analyze('file1.flac');
|
||||
* $info2 = $getID3->analyze('file2.wv');
|
||||
*
|
||||
*
|
||||
* Supported Cache Types (this extension)
|
||||
*
|
||||
* SQL Databases:
|
||||
*
|
||||
* cache_type cache_options
|
||||
* -------------------------------------------------------------------
|
||||
* mysql host, database, username, password
|
||||
*
|
||||
*
|
||||
* DBM-Style Databases: (use extension.cache.dbm)
|
||||
*
|
||||
* cache_type cache_options
|
||||
* -------------------------------------------------------------------
|
||||
* gdbm dbm_filename, lock_filename
|
||||
* ndbm dbm_filename, lock_filename
|
||||
* db2 dbm_filename, lock_filename
|
||||
* db3 dbm_filename, lock_filename
|
||||
* db4 dbm_filename, lock_filename (PHP5 required)
|
||||
*
|
||||
* PHP must have write access to both dbm_filename and lock_filename.
|
||||
*
|
||||
*
|
||||
* Recommended Cache Types
|
||||
*
|
||||
* Infrequent updates, many reads any DBM
|
||||
* Frequent updates mysql
|
||||
*/
|
||||
|
||||
|
||||
class getID3_cached_mysql extends getID3
|
||||
{
|
||||
|
||||
// private vars
|
||||
var $cursor;
|
||||
var $connection;
|
||||
|
||||
|
||||
// public: constructor - see top of this file for cache type and cache_options
|
||||
function getID3_cached_mysql($host, $database, $username, $password) {
|
||||
|
||||
// Check for mysql support
|
||||
if (!function_exists('mysql_pconnect')) {
|
||||
die('PHP not compiled with mysql support.');
|
||||
}
|
||||
|
||||
// Connect to database
|
||||
$this->connection = mysql_pconnect($host, $username, $password);
|
||||
if (!$this->connection) {
|
||||
die('mysql_pconnect() failed - check permissions and spelling.');
|
||||
}
|
||||
|
||||
// Select database
|
||||
if (!mysql_select_db($database, $this->connection)) {
|
||||
die('Cannot use database '.$database);
|
||||
}
|
||||
|
||||
// Create cache table if not exists
|
||||
$this->create_table();
|
||||
|
||||
// Check version number and clear cache if changed
|
||||
$this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename` = '".GETID3_VERSION."') AND (`filesize` = '-1') AND (`filetime` = '-1') AND (`analyzetime` = '-1')", $this->connection);
|
||||
list($version) = @mysql_fetch_array($this->cursor);
|
||||
if ($version != GETID3_VERSION) {
|
||||
$this->clear_cache();
|
||||
}
|
||||
|
||||
parent::getID3();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// public: clear cache
|
||||
function clear_cache() {
|
||||
|
||||
$this->cursor = mysql_query("DELETE FROM `getid3_cache`", $this->connection);
|
||||
$this->cursor = mysql_query("INSERT INTO `getid3_cache` VALUES ('".GETID3_VERSION."', -1, -1, -1, '".GETID3_VERSION."')", $this->connection);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// public: analyze file
|
||||
function analyze($filename) {
|
||||
|
||||
if (file_exists($filename)) {
|
||||
|
||||
// Short-hands
|
||||
$filetime = filemtime($filename);
|
||||
$filesize = filesize($filename);
|
||||
$filenam2 = mysql_escape_string($filename);
|
||||
|
||||
// Loopup file
|
||||
$this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename`='".$filenam2."') AND (`filesize`='".$filesize."') AND (`filetime`='".$filetime."')", $this->connection);
|
||||
list($result) = @mysql_fetch_array($this->cursor);
|
||||
|
||||
// Hit
|
||||
if ($result) {
|
||||
return unserialize($result);
|
||||
}
|
||||
}
|
||||
|
||||
// Miss
|
||||
$result = parent::analyze($filename);
|
||||
|
||||
// Save result
|
||||
if (file_exists($filename)) {
|
||||
$res2 = mysql_escape_string(serialize($result));
|
||||
$this->cursor = mysql_query("INSERT INTO `getid3_cache` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES ('".$filenam2."', '".$filesize."', '".$filetime."', '".time()."', '".$res2."')", $this->connection);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// private: (re)create sql table
|
||||
function create_table($drop = false) {
|
||||
|
||||
$this->cursor = mysql_query("CREATE TABLE IF NOT EXISTS `getid3_cache` (
|
||||
`filename` VARCHAR(255) NOT NULL DEFAULT '',
|
||||
`filesize` INT(11) NOT NULL DEFAULT '0',
|
||||
`filetime` INT(11) NOT NULL DEFAULT '0',
|
||||
`analyzetime` INT(11) NOT NULL DEFAULT '0',
|
||||
`value` TEXT NOT NULL,
|
||||
PRIMARY KEY (`filename`,`filesize`,`filetime`)) TYPE=MyISAM", $this->connection);
|
||||
echo mysql_error($this->connection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,271 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.archive.gzip.php //
|
||||
// module for analyzing GZIP files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Module originally written by //
|
||||
// Mike Mozolin <teddybearØmail*ru> //
|
||||
// //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_gzip {
|
||||
|
||||
// public: Optional file list - disable for speed.
|
||||
var $option_gzip_parse_contents = false; // decode gzipped files, if possible, and parse recursively (.tar.gz for example)
|
||||
|
||||
function getid3_gzip(&$fd, &$ThisFileInfo) {
|
||||
$ThisFileInfo['fileformat'] = 'gzip';
|
||||
|
||||
$start_length = 10;
|
||||
$unpack_header = 'a1id1/a1id2/a1cmethod/a1flags/a4mtime/a1xflags/a1os';
|
||||
//+---+---+---+---+---+---+---+---+---+---+
|
||||
//|ID1|ID2|CM |FLG| MTIME |XFL|OS |
|
||||
//+---+---+---+---+---+---+---+---+---+---+
|
||||
@fseek($fd, 0);
|
||||
$buffer = @fread($fd, $ThisFileInfo['filesize']);
|
||||
|
||||
$arr_members = explode("\x1F\x8B\x08", $buffer);
|
||||
while (true) {
|
||||
$is_wrong_members = false;
|
||||
$num_members = intval(count($arr_members));
|
||||
for ($i = 0; $i < $num_members; $i++) {
|
||||
if (strlen($arr_members[$i]) == 0) {
|
||||
continue;
|
||||
}
|
||||
$buf = "\x1F\x8B\x08".$arr_members[$i];
|
||||
|
||||
$attr = unpack($unpack_header, substr($buf, 0, $start_length));
|
||||
if (!$this->get_os_type(ord($attr['os']))) {
|
||||
// Merge member with previous if wrong OS type
|
||||
$arr_members[$i - 1] .= $buf;
|
||||
$arr_members[$i] = '';
|
||||
$is_wrong_members = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!$is_wrong_members) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$ThisFileInfo['gzip']['files'] = array();
|
||||
|
||||
$fpointer = 0;
|
||||
$idx = 0;
|
||||
for ($i = 0; $i < $num_members; $i++) {
|
||||
if (strlen($arr_members[$i]) == 0) {
|
||||
continue;
|
||||
}
|
||||
$thisThisFileInfo = &$ThisFileInfo['gzip']['member_header'][++$idx];
|
||||
|
||||
$buff = "\x1F\x8B\x08".$arr_members[$i];
|
||||
|
||||
$attr = unpack($unpack_header, substr($buff, 0, $start_length));
|
||||
$thisThisFileInfo['filemtime'] = getid3_lib::LittleEndian2Int($attr['mtime']);
|
||||
$thisThisFileInfo['raw']['id1'] = ord($attr['cmethod']);
|
||||
$thisThisFileInfo['raw']['id2'] = ord($attr['cmethod']);
|
||||
$thisThisFileInfo['raw']['cmethod'] = ord($attr['cmethod']);
|
||||
$thisThisFileInfo['raw']['os'] = ord($attr['os']);
|
||||
$thisThisFileInfo['raw']['xflags'] = ord($attr['xflags']);
|
||||
$thisThisFileInfo['raw']['flags'] = ord($attr['flags']);
|
||||
|
||||
$thisThisFileInfo['flags']['crc16'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x02);
|
||||
$thisThisFileInfo['flags']['extra'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x04);
|
||||
$thisThisFileInfo['flags']['filename'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x08);
|
||||
$thisThisFileInfo['flags']['comment'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x10);
|
||||
|
||||
$thisThisFileInfo['compression'] = $this->get_xflag_type($thisThisFileInfo['raw']['xflags']);
|
||||
|
||||
$thisThisFileInfo['os'] = $this->get_os_type($thisThisFileInfo['raw']['os']);
|
||||
if (!$thisThisFileInfo['os']) {
|
||||
$ThisFileInfo['error'][] = 'Read error on gzip file';
|
||||
return false;
|
||||
}
|
||||
|
||||
$fpointer = 10;
|
||||
$arr_xsubfield = array();
|
||||
// bit 2 - FLG.FEXTRA
|
||||
//+---+---+=================================+
|
||||
//| XLEN |...XLEN bytes of "extra field"...|
|
||||
//+---+---+=================================+
|
||||
if ($thisThisFileInfo['flags']['extra']) {
|
||||
$w_xlen = substr($buff, $fpointer, 2);
|
||||
$xlen = getid3_lib::LittleEndian2Int($w_xlen);
|
||||
$fpointer += 2;
|
||||
|
||||
$thisThisFileInfo['raw']['xfield'] = substr($buff, $fpointer, $xlen);
|
||||
// Extra SubFields
|
||||
//+---+---+---+---+==================================+
|
||||
//|SI1|SI2| LEN |... LEN bytes of subfield data ...|
|
||||
//+---+---+---+---+==================================+
|
||||
$idx = 0;
|
||||
while (true) {
|
||||
if ($idx >= $xlen) {
|
||||
break;
|
||||
}
|
||||
$si1 = ord(substr($buff, $fpointer + $idx++, 1));
|
||||
$si2 = ord(substr($buff, $fpointer + $idx++, 1));
|
||||
if (($si1 == 0x41) && ($si2 == 0x70)) {
|
||||
$w_xsublen = substr($buff, $fpointer + $idx, 2);
|
||||
$xsublen = getid3_lib::LittleEndian2Int($w_xsublen);
|
||||
$idx += 2;
|
||||
$arr_xsubfield[] = substr($buff, $fpointer + $idx, $xsublen);
|
||||
$idx += $xsublen;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$fpointer += $xlen;
|
||||
}
|
||||
// bit 3 - FLG.FNAME
|
||||
//+=========================================+
|
||||
//|...original file name, zero-terminated...|
|
||||
//+=========================================+
|
||||
// GZIP files may have only one file, with no filename, so assume original filename is current filename without .gz
|
||||
$thisThisFileInfo['filename'] = eregi_replace('.gz$', '', $ThisFileInfo['filename']);
|
||||
if ($thisThisFileInfo['flags']['filename']) {
|
||||
while (true) {
|
||||
if (ord($buff[$fpointer]) == 0) {
|
||||
$fpointer++;
|
||||
break;
|
||||
}
|
||||
$thisThisFileInfo['filename'] .= $buff[$fpointer];
|
||||
$fpointer++;
|
||||
}
|
||||
}
|
||||
// bit 4 - FLG.FCOMMENT
|
||||
//+===================================+
|
||||
//|...file comment, zero-terminated...|
|
||||
//+===================================+
|
||||
if ($thisThisFileInfo['flags']['comment']) {
|
||||
while (true) {
|
||||
if (ord($buff[$fpointer]) == 0) {
|
||||
$fpointer++;
|
||||
break;
|
||||
}
|
||||
$thisThisFileInfo['comment'] .= $buff[$fpointer];
|
||||
$fpointer++;
|
||||
}
|
||||
}
|
||||
// bit 1 - FLG.FHCRC
|
||||
//+---+---+
|
||||
//| CRC16 |
|
||||
//+---+---+
|
||||
if ($thisThisFileInfo['flags']['crc16']) {
|
||||
$w_crc = substr($buff, $fpointer, 2);
|
||||
$thisThisFileInfo['crc16'] = getid3_lib::LittleEndian2Int($w_crc);
|
||||
$fpointer += 2;
|
||||
}
|
||||
// bit 0 - FLG.FTEXT
|
||||
//if ($thisThisFileInfo['raw']['flags'] & 0x01) {
|
||||
// Ignored...
|
||||
//}
|
||||
// bits 5, 6, 7 - reserved
|
||||
|
||||
$thisThisFileInfo['crc32'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 8, 4));
|
||||
$thisThisFileInfo['filesize'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 4));
|
||||
|
||||
$ThisFileInfo['gzip']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['gzip']['files'], getid3_lib::CreateDeepArray($thisThisFileInfo['filename'], '/', $thisThisFileInfo['filesize']));
|
||||
|
||||
if ($this->option_gzip_parse_contents) {
|
||||
// Try to inflate GZip
|
||||
$csize = 0;
|
||||
$inflated = '';
|
||||
$chkcrc32 = '';
|
||||
if (function_exists('gzinflate')) {
|
||||
$cdata = substr($buff, $fpointer);
|
||||
$cdata = substr($cdata, 0, strlen($cdata) - 8);
|
||||
$csize = strlen($cdata);
|
||||
$inflated = gzinflate($cdata);
|
||||
|
||||
// Calculate CRC32 for inflated content
|
||||
$thisThisFileInfo['crc32_valid'] = (bool) (sprintf('%u', crc32($inflated)) == $thisThisFileInfo['crc32']);
|
||||
|
||||
// determine format
|
||||
$formattest = substr($inflated, 0, 32774);
|
||||
$newgetID3 = new getID3();
|
||||
$determined_format = $newgetID3->GetFileFormat($formattest);
|
||||
unset($newgetID3);
|
||||
|
||||
// file format is determined
|
||||
switch (@$determined_format['module']) {
|
||||
case 'tar':
|
||||
// view TAR-file info
|
||||
if (file_exists(GETID3_INCLUDEPATH.$determined_format['include']) && @include_once(GETID3_INCLUDEPATH.$determined_format['include'])) {
|
||||
if (($temp_tar_filename = tempnam('*', 'getID3')) === false) {
|
||||
// can't find anywhere to create a temp file, abort
|
||||
$ThisFileInfo['error'][] = 'Unable to create temp file to parse TAR inside GZIP file';
|
||||
break;
|
||||
}
|
||||
if ($fp_temp_tar = fopen($temp_tar_filename, 'w+b')) {
|
||||
fwrite($fp_temp_tar, $inflated);
|
||||
rewind($fp_temp_tar);
|
||||
$getid3_tar = new getid3_tar($fp_temp_tar, $dummy);
|
||||
$ThisFileInfo['gzip']['member_header'][$idx]['tar'] = $dummy['tar'];
|
||||
unset($dummy);
|
||||
unset($getid3_tar);
|
||||
fclose($fp_temp_tar);
|
||||
unlink($temp_tar_filename);
|
||||
} else {
|
||||
$ThisFileInfo['error'][] = 'Unable to fopen() temp file to parse TAR inside GZIP file';
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '':
|
||||
default:
|
||||
// unknown or unhandled format
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Converts the OS type
|
||||
function get_os_type($key) {
|
||||
static $os_type = array(
|
||||
'0' => 'FAT filesystem (MS-DOS, OS/2, NT/Win32)',
|
||||
'1' => 'Amiga',
|
||||
'2' => 'VMS (or OpenVMS)',
|
||||
'3' => 'Unix',
|
||||
'4' => 'VM/CMS',
|
||||
'5' => 'Atari TOS',
|
||||
'6' => 'HPFS filesystem (OS/2, NT)',
|
||||
'7' => 'Macintosh',
|
||||
'8' => 'Z-System',
|
||||
'9' => 'CP/M',
|
||||
'10' => 'TOPS-20',
|
||||
'11' => 'NTFS filesystem (NT)',
|
||||
'12' => 'QDOS',
|
||||
'13' => 'Acorn RISCOS',
|
||||
'255' => 'unknown'
|
||||
);
|
||||
return @$os_type[$key];
|
||||
}
|
||||
|
||||
// Converts the eXtra FLags
|
||||
function get_xflag_type($key) {
|
||||
static $xflag_type = array(
|
||||
'0' => 'unknown',
|
||||
'2' => 'maximum compression',
|
||||
'4' => 'fastest algorithm'
|
||||
);
|
||||
return @$xflag_type[$key];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.archive.rar.php //
|
||||
// module for analyzing RAR files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_rar
|
||||
{
|
||||
|
||||
var $option_use_rar_extension = false;
|
||||
|
||||
function getid3_rar(&$fd, &$ThisFileInfo) {
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'rar';
|
||||
|
||||
if ($this->option_use_rar_extension === true) {
|
||||
if (function_exists('rar_open')) {
|
||||
if ($rp = rar_open($ThisFileInfo['filename'])) {
|
||||
$ThisFileInfo['rar']['files'] = array();
|
||||
$entries = rar_list($rp);
|
||||
foreach ($entries as $entry) {
|
||||
$ThisFileInfo['rar']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['rar']['files'], getid3_lib::CreateDeepArray($entry->getName(), '/', $entry->getUnpackedSize()));
|
||||
}
|
||||
rar_close($rp);
|
||||
return true;
|
||||
} else {
|
||||
$ThisFileInfo['error'][] = 'failed to rar_open('.$ThisFileInfo['filename'].')';
|
||||
}
|
||||
} else {
|
||||
$ThisFileInfo['error'][] = 'RAR support does not appear to be available in this PHP installation';
|
||||
}
|
||||
} else {
|
||||
$ThisFileInfo['error'][] = 'PHP-RAR processing has been disabled (set $getid3_rar->option_use_rar_extension=true to enable)';
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.archive.szip.php //
|
||||
// module for analyzing SZIP compressed files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_szip
|
||||
{
|
||||
|
||||
function getid3_szip(&$fd, &$ThisFileInfo) {
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$SZIPHeader = fread($fd, 6);
|
||||
if (substr($SZIPHeader, 0, 4) != 'SZ'."\x0A\x04") {
|
||||
$ThisFileInfo['error'][] = 'Expecting "SZ[x0A][x04]" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($SZIPHeader, 0, 4).'"';
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'szip';
|
||||
|
||||
$ThisFileInfo['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 4, 1));
|
||||
$ThisFileInfo['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 5, 1));
|
||||
|
||||
while (!feof($fd)) {
|
||||
$NextBlockID = fread($fd, 2);
|
||||
switch ($NextBlockID) {
|
||||
case 'SZ':
|
||||
// Note that szip files can be concatenated, this has the same effect as
|
||||
// concatenating the files. this also means that global header blocks
|
||||
// might be present between directory/data blocks.
|
||||
fseek($fd, 4, SEEK_CUR);
|
||||
break;
|
||||
|
||||
case 'BH':
|
||||
$BHheaderbytes = getid3_lib::BigEndian2Int(fread($fd, 3));
|
||||
$BHheaderdata = fread($fd, $BHheaderbytes);
|
||||
$BHheaderoffset = 0;
|
||||
while (strpos($BHheaderdata, "\x00", $BHheaderoffset) > 0) {
|
||||
//filename as \0 terminated string (empty string indicates end)
|
||||
//owner as \0 terminated string (empty is same as last file)
|
||||
//group as \0 terminated string (empty is same as last file)
|
||||
//3 byte filelength in this block
|
||||
//2 byte access flags
|
||||
//4 byte creation time (like in unix)
|
||||
//4 byte modification time (like in unix)
|
||||
//4 byte access time (like in unix)
|
||||
|
||||
$BHdataArray['filename'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
|
||||
$BHheaderoffset += (strlen($BHdataArray['filename']) + 1);
|
||||
|
||||
$BHdataArray['owner'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
|
||||
$BHheaderoffset += (strlen($BHdataArray['owner']) + 1);
|
||||
|
||||
$BHdataArray['group'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
|
||||
$BHheaderoffset += (strlen($BHdataArray['group']) + 1);
|
||||
|
||||
$BHdataArray['filelength'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 3));
|
||||
$BHheaderoffset += 3;
|
||||
|
||||
$BHdataArray['access_flags'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 2));
|
||||
$BHheaderoffset += 2;
|
||||
|
||||
$BHdataArray['creation_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
|
||||
$BHheaderoffset += 4;
|
||||
|
||||
$BHdataArray['modification_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
|
||||
$BHheaderoffset += 4;
|
||||
|
||||
$BHdataArray['access_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
|
||||
$BHheaderoffset += 4;
|
||||
|
||||
$ThisFileInfo['szip']['BH'][] = $BHdataArray;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,175 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.archive.tar.php //
|
||||
// module for analyzing TAR files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Module originally written by //
|
||||
// Mike Mozolin <teddybearØmail*ru> //
|
||||
// //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_tar {
|
||||
|
||||
function getid3_tar(&$fd, &$ThisFileInfo) {
|
||||
$ThisFileInfo['fileformat'] = 'tar';
|
||||
$ThisFileInfo['tar']['files'] = array();
|
||||
|
||||
$unpack_header = 'a100fname/a8mode/a8uid/a8gid/a12size/a12mtime/a8chksum/a1typflag/a100lnkname/a6magic/a2ver/a32uname/a32gname/a8devmaj/a8devmin/a155prefix';
|
||||
$null_512k = str_repeat("\x00", 512); // end-of-file marker
|
||||
|
||||
@fseek($fd, 0);
|
||||
while (!feof($fd)) {
|
||||
$buffer = fread($fd, 512);
|
||||
if (strlen($buffer) < 512) {
|
||||
break;
|
||||
}
|
||||
|
||||
// check the block
|
||||
$checksum = 0;
|
||||
for ($i = 0; $i < 148; $i++) {
|
||||
$checksum += ord($buffer{$i});
|
||||
}
|
||||
for ($i = 148; $i < 156; $i++) {
|
||||
$checksum += ord(' ');
|
||||
}
|
||||
for ($i = 156; $i < 512; $i++) {
|
||||
$checksum += ord($buffer{$i});
|
||||
}
|
||||
$attr = unpack($unpack_header, $buffer);
|
||||
$name = trim(@$attr['fname']);
|
||||
$mode = octdec(trim(@$attr['mode']));
|
||||
$uid = octdec(trim(@$attr['uid']));
|
||||
$gid = octdec(trim(@$attr['gid']));
|
||||
$size = octdec(trim(@$attr['size']));
|
||||
$mtime = octdec(trim(@$attr['mtime']));
|
||||
$chksum = octdec(trim(@$attr['chksum']));
|
||||
$typflag = trim(@$attr['typflag']);
|
||||
$lnkname = trim(@$attr['lnkname']);
|
||||
$magic = trim(@$attr['magic']);
|
||||
$ver = trim(@$attr['ver']);
|
||||
$uname = trim(@$attr['uname']);
|
||||
$gname = trim(@$attr['gname']);
|
||||
$devmaj = octdec(trim(@$attr['devmaj']));
|
||||
$devmin = octdec(trim(@$attr['devmin']));
|
||||
$prefix = trim(@$attr['prefix']);
|
||||
if (($checksum == 256) && ($chksum == 0)) {
|
||||
// EOF Found
|
||||
break;
|
||||
}
|
||||
if ($prefix) {
|
||||
$name = $prefix.'/'.$name;
|
||||
}
|
||||
if ((preg_match('#/$#', $name)) && !$name) {
|
||||
$typeflag = 5;
|
||||
}
|
||||
if ($buffer == $null_512k) {
|
||||
// it's the end of the tar-file...
|
||||
break;
|
||||
}
|
||||
|
||||
// Read to the next chunk
|
||||
fseek($fd, $size, SEEK_CUR);
|
||||
|
||||
$diff = $size % 512;
|
||||
if ($diff != 0) {
|
||||
// Padding, throw away
|
||||
fseek($fd, (512 - $diff), SEEK_CUR);
|
||||
}
|
||||
// Protect against tar-files with garbage at the end
|
||||
if ($name == '') {
|
||||
break;
|
||||
}
|
||||
$ThisFileInfo['tar']['file_details'][$name] = array (
|
||||
'name' => $name,
|
||||
'mode_raw' => $mode,
|
||||
'mode' => getid3_tar::display_perms($mode),
|
||||
'uid' => $uid,
|
||||
'gid' => $gid,
|
||||
'size' => $size,
|
||||
'mtime' => $mtime,
|
||||
'chksum' => $chksum,
|
||||
'typeflag' => getid3_tar::get_flag_type($typflag),
|
||||
'linkname' => $lnkname,
|
||||
'magic' => $magic,
|
||||
'version' => $ver,
|
||||
'uname' => $uname,
|
||||
'gname' => $gname,
|
||||
'devmajor' => $devmaj,
|
||||
'devminor' => $devmin
|
||||
);
|
||||
$ThisFileInfo['tar']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['tar']['files'], getid3_lib::CreateDeepArray($ThisFileInfo['tar']['file_details'][$name]['name'], '/', $size));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parses the file mode to file permissions
|
||||
function display_perms($mode) {
|
||||
// Determine Type
|
||||
if ($mode & 0x1000) $type='p'; // FIFO pipe
|
||||
elseif ($mode & 0x2000) $type='c'; // Character special
|
||||
elseif ($mode & 0x4000) $type='d'; // Directory
|
||||
elseif ($mode & 0x6000) $type='b'; // Block special
|
||||
elseif ($mode & 0x8000) $type='-'; // Regular
|
||||
elseif ($mode & 0xA000) $type='l'; // Symbolic Link
|
||||
elseif ($mode & 0xC000) $type='s'; // Socket
|
||||
else $type='u'; // UNKNOWN
|
||||
|
||||
// Determine permissions
|
||||
$owner['read'] = (($mode & 00400) ? 'r' : '-');
|
||||
$owner['write'] = (($mode & 00200) ? 'w' : '-');
|
||||
$owner['execute'] = (($mode & 00100) ? 'x' : '-');
|
||||
$group['read'] = (($mode & 00040) ? 'r' : '-');
|
||||
$group['write'] = (($mode & 00020) ? 'w' : '-');
|
||||
$group['execute'] = (($mode & 00010) ? 'x' : '-');
|
||||
$world['read'] = (($mode & 00004) ? 'r' : '-');
|
||||
$world['write'] = (($mode & 00002) ? 'w' : '-');
|
||||
$world['execute'] = (($mode & 00001) ? 'x' : '-');
|
||||
|
||||
// Adjust for SUID, SGID and sticky bit
|
||||
if ($mode & 0x800) $owner['execute'] = ($owner['execute'] == 'x') ? 's' : 'S';
|
||||
if ($mode & 0x400) $group['execute'] = ($group['execute'] == 'x') ? 's' : 'S';
|
||||
if ($mode & 0x200) $world['execute'] = ($world['execute'] == 'x') ? 't' : 'T';
|
||||
|
||||
$s = sprintf('%1s', $type);
|
||||
$s .= sprintf('%1s%1s%1s', $owner['read'], $owner['write'], $owner['execute']);
|
||||
$s .= sprintf('%1s%1s%1s', $group['read'], $group['write'], $group['execute']);
|
||||
$s .= sprintf('%1s%1s%1s'."\n", $world['read'], $world['write'], $world['execute']);
|
||||
return $s;
|
||||
}
|
||||
|
||||
// Converts the file type
|
||||
function get_flag_type($typflag) {
|
||||
static $flag_types = array(
|
||||
'0' => 'LF_NORMAL',
|
||||
'1' => 'LF_LINK',
|
||||
'2' => 'LF_SYNLINK',
|
||||
'3' => 'LF_CHR',
|
||||
'4' => 'LF_BLK',
|
||||
'5' => 'LF_DIR',
|
||||
'6' => 'LF_FIFO',
|
||||
'7' => 'LF_CONFIG',
|
||||
'D' => 'LF_DUMPDIR',
|
||||
'K' => 'LF_LONGLINK',
|
||||
'L' => 'LF_LONGNAME',
|
||||
'M' => 'LF_MULTIVOL',
|
||||
'N' => 'LF_NAMES',
|
||||
'S' => 'LF_SPARSE',
|
||||
'V' => 'LF_VOLHDR'
|
||||
);
|
||||
return @$flag_types[$typflag];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,416 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.archive.zip.php //
|
||||
// module for analyzing pkZip files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_zip
|
||||
{
|
||||
|
||||
function getid3_zip(&$fd, &$ThisFileInfo) {
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'zip';
|
||||
$ThisFileInfo['zip']['encoding'] = 'ISO-8859-1';
|
||||
$ThisFileInfo['zip']['files'] = array();
|
||||
|
||||
$ThisFileInfo['zip']['compressed_size'] = 0;
|
||||
$ThisFileInfo['zip']['uncompressed_size'] = 0;
|
||||
$ThisFileInfo['zip']['entries_count'] = 0;
|
||||
|
||||
if ($ThisFileInfo['filesize'] < pow(2, 31)) {
|
||||
$EOCDsearchData = '';
|
||||
$EOCDsearchCounter = 0;
|
||||
while ($EOCDsearchCounter++ < 512) {
|
||||
|
||||
fseek($fd, -128 * $EOCDsearchCounter, SEEK_END);
|
||||
$EOCDsearchData = fread($fd, 128).$EOCDsearchData;
|
||||
|
||||
if (strstr($EOCDsearchData, 'PK'."\x05\x06")) {
|
||||
|
||||
$EOCDposition = strpos($EOCDsearchData, 'PK'."\x05\x06");
|
||||
fseek($fd, (-128 * $EOCDsearchCounter) + $EOCDposition, SEEK_END);
|
||||
$ThisFileInfo['zip']['end_central_directory'] = $this->ZIPparseEndOfCentralDirectory($fd);
|
||||
|
||||
fseek($fd, $ThisFileInfo['zip']['end_central_directory']['directory_offset'], SEEK_SET);
|
||||
$ThisFileInfo['zip']['entries_count'] = 0;
|
||||
while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($fd)) {
|
||||
$ThisFileInfo['zip']['central_directory'][] = $centraldirectoryentry;
|
||||
$ThisFileInfo['zip']['entries_count']++;
|
||||
$ThisFileInfo['zip']['compressed_size'] += $centraldirectoryentry['compressed_size'];
|
||||
$ThisFileInfo['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size'];
|
||||
|
||||
if ($centraldirectoryentry['uncompressed_size'] > 0) {
|
||||
$ThisFileInfo['zip']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['zip']['files'], getid3_lib::CreateDeepArray($centraldirectoryentry['filename'], '/', $centraldirectoryentry['uncompressed_size']));
|
||||
}
|
||||
}
|
||||
|
||||
if ($ThisFileInfo['zip']['entries_count'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'No Central Directory entries found (truncated file?)';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($ThisFileInfo['zip']['end_central_directory']['comment'])) {
|
||||
$ThisFileInfo['zip']['comments']['comment'][] = $ThisFileInfo['zip']['end_central_directory']['comment'];
|
||||
}
|
||||
|
||||
if (isset($ThisFileInfo['zip']['central_directory'][0]['compression_method'])) {
|
||||
$ThisFileInfo['zip']['compression_method'] = $ThisFileInfo['zip']['central_directory'][0]['compression_method'];
|
||||
}
|
||||
if (isset($ThisFileInfo['zip']['central_directory'][0]['flags']['compression_speed'])) {
|
||||
$ThisFileInfo['zip']['compression_speed'] = $ThisFileInfo['zip']['central_directory'][0]['flags']['compression_speed'];
|
||||
}
|
||||
if (isset($ThisFileInfo['zip']['compression_method']) && ($ThisFileInfo['zip']['compression_method'] == 'store') && !isset($ThisFileInfo['zip']['compression_speed'])) {
|
||||
$ThisFileInfo['zip']['compression_speed'] = 'store';
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->getZIPentriesFilepointer($fd, $ThisFileInfo)) {
|
||||
|
||||
// central directory couldn't be found and/or parsed
|
||||
// scan through actual file data entries, recover as much as possible from probable trucated file
|
||||
if ($ThisFileInfo['zip']['compressed_size'] > ($ThisFileInfo['filesize'] - 46 - 22)) {
|
||||
$ThisFileInfo['error'][] = 'Warning: Truncated file! - Total compressed file sizes ('.$ThisFileInfo['zip']['compressed_size'].' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures ('.($ThisFileInfo['filesize'] - 46 - 22).' bytes)';
|
||||
}
|
||||
$ThisFileInfo['error'][] = 'Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete';
|
||||
foreach ($ThisFileInfo['zip']['entries'] as $key => $valuearray) {
|
||||
$ThisFileInfo['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size'];
|
||||
}
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
unset($ThisFileInfo['zip']);
|
||||
$ThisFileInfo['fileformat'] = '';
|
||||
$ThisFileInfo['error'][] = 'Cannot find End Of Central Directory (truncated file?)';
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getZIPHeaderFilepointerTopDown(&$fd, &$ThisFileInfo) {
|
||||
$ThisFileInfo['fileformat'] = 'zip';
|
||||
|
||||
$ThisFileInfo['zip']['compressed_size'] = 0;
|
||||
$ThisFileInfo['zip']['uncompressed_size'] = 0;
|
||||
$ThisFileInfo['zip']['entries_count'] = 0;
|
||||
|
||||
rewind($fd);
|
||||
while ($fileentry = $this->ZIPparseLocalFileHeader($fd)) {
|
||||
$ThisFileInfo['zip']['entries'][] = $fileentry;
|
||||
$ThisFileInfo['zip']['entries_count']++;
|
||||
}
|
||||
if ($ThisFileInfo['zip']['entries_count'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'No Local File Header entries found';
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['zip']['entries_count'] = 0;
|
||||
while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($fd)) {
|
||||
$ThisFileInfo['zip']['central_directory'][] = $centraldirectoryentry;
|
||||
$ThisFileInfo['zip']['entries_count']++;
|
||||
$ThisFileInfo['zip']['compressed_size'] += $centraldirectoryentry['compressed_size'];
|
||||
$ThisFileInfo['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size'];
|
||||
}
|
||||
if ($ThisFileInfo['zip']['entries_count'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'No Central Directory entries found (truncated file?)';
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($EOCD = $this->ZIPparseEndOfCentralDirectory($fd)) {
|
||||
$ThisFileInfo['zip']['end_central_directory'] = $EOCD;
|
||||
} else {
|
||||
$ThisFileInfo['error'][] = 'No End Of Central Directory entry found (truncated file?)';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($ThisFileInfo['zip']['end_central_directory']['comment'])) {
|
||||
$ThisFileInfo['zip']['comments']['comment'][] = $ThisFileInfo['zip']['end_central_directory']['comment'];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function getZIPentriesFilepointer(&$fd, &$ThisFileInfo) {
|
||||
$ThisFileInfo['zip']['compressed_size'] = 0;
|
||||
$ThisFileInfo['zip']['uncompressed_size'] = 0;
|
||||
$ThisFileInfo['zip']['entries_count'] = 0;
|
||||
|
||||
rewind($fd);
|
||||
while ($fileentry = $this->ZIPparseLocalFileHeader($fd)) {
|
||||
$ThisFileInfo['zip']['entries'][] = $fileentry;
|
||||
$ThisFileInfo['zip']['entries_count']++;
|
||||
$ThisFileInfo['zip']['compressed_size'] += $fileentry['compressed_size'];
|
||||
$ThisFileInfo['zip']['uncompressed_size'] += $fileentry['uncompressed_size'];
|
||||
}
|
||||
if ($ThisFileInfo['zip']['entries_count'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'No Local File Header entries found';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function ZIPparseLocalFileHeader(&$fd) {
|
||||
$LocalFileHeader['offset'] = ftell($fd);
|
||||
|
||||
$ZIPlocalFileHeader = fread($fd, 30);
|
||||
|
||||
$LocalFileHeader['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 0, 4));
|
||||
if ($LocalFileHeader['raw']['signature'] != 0x04034B50) {
|
||||
// invalid Local File Header Signature
|
||||
fseek($fd, $LocalFileHeader['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly
|
||||
return false;
|
||||
}
|
||||
$LocalFileHeader['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 4, 2));
|
||||
$LocalFileHeader['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 6, 2));
|
||||
$LocalFileHeader['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 8, 2));
|
||||
$LocalFileHeader['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 10, 2));
|
||||
$LocalFileHeader['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 12, 2));
|
||||
$LocalFileHeader['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 14, 4));
|
||||
$LocalFileHeader['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 18, 4));
|
||||
$LocalFileHeader['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 22, 4));
|
||||
$LocalFileHeader['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 26, 2));
|
||||
$LocalFileHeader['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 28, 2));
|
||||
|
||||
$LocalFileHeader['extract_version'] = sprintf('%1.1f', $LocalFileHeader['raw']['extract_version'] / 10);
|
||||
$LocalFileHeader['host_os'] = $this->ZIPversionOSLookup(($LocalFileHeader['raw']['extract_version'] & 0xFF00) >> 8);
|
||||
$LocalFileHeader['compression_method'] = $this->ZIPcompressionMethodLookup($LocalFileHeader['raw']['compression_method']);
|
||||
$LocalFileHeader['compressed_size'] = $LocalFileHeader['raw']['compressed_size'];
|
||||
$LocalFileHeader['uncompressed_size'] = $LocalFileHeader['raw']['uncompressed_size'];
|
||||
$LocalFileHeader['flags'] = $this->ZIPparseGeneralPurposeFlags($LocalFileHeader['raw']['general_flags'], $LocalFileHeader['raw']['compression_method']);
|
||||
$LocalFileHeader['last_modified_timestamp'] = $this->DOStime2UNIXtime($LocalFileHeader['raw']['last_mod_file_date'], $LocalFileHeader['raw']['last_mod_file_time']);
|
||||
|
||||
$FilenameExtrafieldLength = $LocalFileHeader['raw']['filename_length'] + $LocalFileHeader['raw']['extra_field_length'];
|
||||
if ($FilenameExtrafieldLength > 0) {
|
||||
$ZIPlocalFileHeader .= fread($fd, $FilenameExtrafieldLength);
|
||||
|
||||
if ($LocalFileHeader['raw']['filename_length'] > 0) {
|
||||
$LocalFileHeader['filename'] = substr($ZIPlocalFileHeader, 30, $LocalFileHeader['raw']['filename_length']);
|
||||
}
|
||||
if ($LocalFileHeader['raw']['extra_field_length'] > 0) {
|
||||
$LocalFileHeader['raw']['extra_field_data'] = substr($ZIPlocalFileHeader, 30 + $LocalFileHeader['raw']['filename_length'], $LocalFileHeader['raw']['extra_field_length']);
|
||||
}
|
||||
}
|
||||
|
||||
$LocalFileHeader['data_offset'] = ftell($fd);
|
||||
//$LocalFileHeader['compressed_data'] = fread($fd, $LocalFileHeader['raw']['compressed_size']);
|
||||
fseek($fd, $LocalFileHeader['raw']['compressed_size'], SEEK_CUR);
|
||||
|
||||
if ($LocalFileHeader['flags']['data_descriptor_used']) {
|
||||
$DataDescriptor = fread($fd, 12);
|
||||
$LocalFileHeader['data_descriptor']['crc_32'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 0, 4));
|
||||
$LocalFileHeader['data_descriptor']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 4, 4));
|
||||
$LocalFileHeader['data_descriptor']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 8, 4));
|
||||
}
|
||||
|
||||
return $LocalFileHeader;
|
||||
}
|
||||
|
||||
|
||||
function ZIPparseCentralDirectory(&$fd) {
|
||||
$CentralDirectory['offset'] = ftell($fd);
|
||||
|
||||
$ZIPcentralDirectory = fread($fd, 46);
|
||||
|
||||
$CentralDirectory['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 0, 4));
|
||||
if ($CentralDirectory['raw']['signature'] != 0x02014B50) {
|
||||
// invalid Central Directory Signature
|
||||
fseek($fd, $CentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly
|
||||
return false;
|
||||
}
|
||||
$CentralDirectory['raw']['create_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 4, 2));
|
||||
$CentralDirectory['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 6, 2));
|
||||
$CentralDirectory['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 8, 2));
|
||||
$CentralDirectory['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 10, 2));
|
||||
$CentralDirectory['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 12, 2));
|
||||
$CentralDirectory['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 14, 2));
|
||||
$CentralDirectory['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 16, 4));
|
||||
$CentralDirectory['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 20, 4));
|
||||
$CentralDirectory['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 24, 4));
|
||||
$CentralDirectory['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 28, 2));
|
||||
$CentralDirectory['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 30, 2));
|
||||
$CentralDirectory['raw']['file_comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 32, 2));
|
||||
$CentralDirectory['raw']['disk_number_start'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 34, 2));
|
||||
$CentralDirectory['raw']['internal_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 36, 2));
|
||||
$CentralDirectory['raw']['external_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 38, 4));
|
||||
$CentralDirectory['raw']['local_header_offset'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 42, 4));
|
||||
|
||||
$CentralDirectory['entry_offset'] = $CentralDirectory['raw']['local_header_offset'];
|
||||
$CentralDirectory['create_version'] = sprintf('%1.1f', $CentralDirectory['raw']['create_version'] / 10);
|
||||
$CentralDirectory['extract_version'] = sprintf('%1.1f', $CentralDirectory['raw']['extract_version'] / 10);
|
||||
$CentralDirectory['host_os'] = $this->ZIPversionOSLookup(($CentralDirectory['raw']['extract_version'] & 0xFF00) >> 8);
|
||||
$CentralDirectory['compression_method'] = $this->ZIPcompressionMethodLookup($CentralDirectory['raw']['compression_method']);
|
||||
$CentralDirectory['compressed_size'] = $CentralDirectory['raw']['compressed_size'];
|
||||
$CentralDirectory['uncompressed_size'] = $CentralDirectory['raw']['uncompressed_size'];
|
||||
$CentralDirectory['flags'] = $this->ZIPparseGeneralPurposeFlags($CentralDirectory['raw']['general_flags'], $CentralDirectory['raw']['compression_method']);
|
||||
$CentralDirectory['last_modified_timestamp'] = $this->DOStime2UNIXtime($CentralDirectory['raw']['last_mod_file_date'], $CentralDirectory['raw']['last_mod_file_time']);
|
||||
|
||||
$FilenameExtrafieldCommentLength = $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'] + $CentralDirectory['raw']['file_comment_length'];
|
||||
if ($FilenameExtrafieldCommentLength > 0) {
|
||||
$FilenameExtrafieldComment = fread($fd, $FilenameExtrafieldCommentLength);
|
||||
|
||||
if ($CentralDirectory['raw']['filename_length'] > 0) {
|
||||
$CentralDirectory['filename'] = substr($FilenameExtrafieldComment, 0, $CentralDirectory['raw']['filename_length']);
|
||||
}
|
||||
if ($CentralDirectory['raw']['extra_field_length'] > 0) {
|
||||
$CentralDirectory['raw']['extra_field_data'] = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'], $CentralDirectory['raw']['extra_field_length']);
|
||||
}
|
||||
if ($CentralDirectory['raw']['file_comment_length'] > 0) {
|
||||
$CentralDirectory['file_comment'] = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'], $CentralDirectory['raw']['file_comment_length']);
|
||||
}
|
||||
}
|
||||
|
||||
return $CentralDirectory;
|
||||
}
|
||||
|
||||
function ZIPparseEndOfCentralDirectory(&$fd) {
|
||||
$EndOfCentralDirectory['offset'] = ftell($fd);
|
||||
|
||||
$ZIPendOfCentralDirectory = fread($fd, 22);
|
||||
|
||||
$EndOfCentralDirectory['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 0, 4));
|
||||
if ($EndOfCentralDirectory['signature'] != 0x06054B50) {
|
||||
// invalid End Of Central Directory Signature
|
||||
fseek($fd, $EndOfCentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly
|
||||
return false;
|
||||
}
|
||||
$EndOfCentralDirectory['disk_number_current'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 4, 2));
|
||||
$EndOfCentralDirectory['disk_number_start_directory'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 6, 2));
|
||||
$EndOfCentralDirectory['directory_entries_this_disk'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 8, 2));
|
||||
$EndOfCentralDirectory['directory_entries_total'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 10, 2));
|
||||
$EndOfCentralDirectory['directory_size'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 12, 4));
|
||||
$EndOfCentralDirectory['directory_offset'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 16, 4));
|
||||
$EndOfCentralDirectory['comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 20, 2));
|
||||
|
||||
if ($EndOfCentralDirectory['comment_length'] > 0) {
|
||||
$EndOfCentralDirectory['comment'] = fread($fd, $EndOfCentralDirectory['comment_length']);
|
||||
}
|
||||
|
||||
return $EndOfCentralDirectory;
|
||||
}
|
||||
|
||||
|
||||
function ZIPparseGeneralPurposeFlags($flagbytes, $compressionmethod) {
|
||||
$ParsedFlags['encrypted'] = (bool) ($flagbytes & 0x0001);
|
||||
|
||||
switch ($compressionmethod) {
|
||||
case 6:
|
||||
$ParsedFlags['dictionary_size'] = (($flagbytes & 0x0002) ? 8192 : 4096);
|
||||
$ParsedFlags['shannon_fano_trees'] = (($flagbytes & 0x0004) ? 3 : 2);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
case 9:
|
||||
switch (($flagbytes & 0x0006) >> 1) {
|
||||
case 0:
|
||||
$ParsedFlags['compression_speed'] = 'normal';
|
||||
break;
|
||||
case 1:
|
||||
$ParsedFlags['compression_speed'] = 'maximum';
|
||||
break;
|
||||
case 2:
|
||||
$ParsedFlags['compression_speed'] = 'fast';
|
||||
break;
|
||||
case 3:
|
||||
$ParsedFlags['compression_speed'] = 'superfast';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$ParsedFlags['data_descriptor_used'] = (bool) ($flagbytes & 0x0008);
|
||||
|
||||
return $ParsedFlags;
|
||||
}
|
||||
|
||||
|
||||
function ZIPversionOSLookup($index) {
|
||||
static $ZIPversionOSLookup = array(
|
||||
0 => 'MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)',
|
||||
1 => 'Amiga',
|
||||
2 => 'OpenVMS',
|
||||
3 => 'Unix',
|
||||
4 => 'VM/CMS',
|
||||
5 => 'Atari ST',
|
||||
6 => 'OS/2 H.P.F.S.',
|
||||
7 => 'Macintosh',
|
||||
8 => 'Z-System',
|
||||
9 => 'CP/M',
|
||||
10 => 'Windows NTFS',
|
||||
11 => 'MVS',
|
||||
12 => 'VSE',
|
||||
13 => 'Acorn Risc',
|
||||
14 => 'VFAT',
|
||||
15 => 'Alternate MVS',
|
||||
16 => 'BeOS',
|
||||
17 => 'Tandem'
|
||||
);
|
||||
|
||||
return (isset($ZIPversionOSLookup[$index]) ? $ZIPversionOSLookup[$index] : '[unknown]');
|
||||
}
|
||||
|
||||
function ZIPcompressionMethodLookup($index) {
|
||||
static $ZIPcompressionMethodLookup = array(
|
||||
0 => 'store',
|
||||
1 => 'shrink',
|
||||
2 => 'reduce-1',
|
||||
3 => 'reduce-2',
|
||||
4 => 'reduce-3',
|
||||
5 => 'reduce-4',
|
||||
6 => 'implode',
|
||||
7 => 'tokenize',
|
||||
8 => 'deflate',
|
||||
9 => 'deflate64',
|
||||
10 => 'PKWARE Date Compression Library Imploding'
|
||||
);
|
||||
|
||||
return (isset($ZIPcompressionMethodLookup[$index]) ? $ZIPcompressionMethodLookup[$index] : '[unknown]');
|
||||
}
|
||||
|
||||
function DOStime2UNIXtime($DOSdate, $DOStime) {
|
||||
// wFatDate
|
||||
// Specifies the MS-DOS date. The date is a packed 16-bit value with the following format:
|
||||
// Bits Contents
|
||||
// 0-4 Day of the month (1-31)
|
||||
// 5-8 Month (1 = January, 2 = February, and so on)
|
||||
// 9-15 Year offset from 1980 (add 1980 to get actual year)
|
||||
|
||||
$UNIXday = ($DOSdate & 0x001F);
|
||||
$UNIXmonth = (($DOSdate & 0x01E0) >> 5);
|
||||
$UNIXyear = (($DOSdate & 0xFE00) >> 9) + 1980;
|
||||
|
||||
// wFatTime
|
||||
// Specifies the MS-DOS time. The time is a packed 16-bit value with the following format:
|
||||
// Bits Contents
|
||||
// 0-4 Second divided by 2
|
||||
// 5-10 Minute (0-59)
|
||||
// 11-15 Hour (0-23 on a 24-hour clock)
|
||||
|
||||
$UNIXsecond = ($DOStime & 0x001F) * 2;
|
||||
$UNIXminute = (($DOStime & 0x07E0) >> 5);
|
||||
$UNIXhour = (($DOStime & 0xF800) >> 11);
|
||||
|
||||
return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.bink.php //
|
||||
// module for analyzing Bink or Smacker audio-video files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_bink
|
||||
{
|
||||
|
||||
function getid3_bink(&$fd, &$ThisFileInfo) {
|
||||
|
||||
$ThisFileInfo['error'][] = 'Bink / Smacker files not properly processed by this version of getID3()';
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$fileTypeID = fread($fd, 3);
|
||||
switch ($fileTypeID) {
|
||||
case 'BIK':
|
||||
return $this->ParseBink($fd, $ThisFileInfo);
|
||||
break;
|
||||
|
||||
case 'SMK':
|
||||
return $this->ParseSmacker($fd, $ThisFileInfo);
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['error'][] = 'Expecting "BIK" or "SMK" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$fileTypeID.'"';
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
function ParseBink(&$fd, &$ThisFileInfo) {
|
||||
$ThisFileInfo['fileformat'] = 'bink';
|
||||
$ThisFileInfo['video']['dataformat'] = 'bink';
|
||||
|
||||
$fileData = 'BIK'.fread($fd, 13);
|
||||
|
||||
$ThisFileInfo['bink']['data_size'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4));
|
||||
$ThisFileInfo['bink']['frame_count'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 2));
|
||||
|
||||
if (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) != ($ThisFileInfo['bink']['data_size'] + 8)) {
|
||||
$ThisFileInfo['error'][] = 'Probably truncated file: expecting '.$ThisFileInfo['bink']['data_size'].' bytes, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function ParseSmacker(&$fd, &$ThisFileInfo) {
|
||||
$ThisFileInfo['fileformat'] = 'smacker';
|
||||
$ThisFileInfo['video']['dataformat'] = 'smacker';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,505 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
// //
|
||||
// FLV module by Seth Kaufman <seth@whirl-i-gig.com> //
|
||||
// //
|
||||
// * version 0.1 (26 June 2005) //
|
||||
// //
|
||||
// minor modifications by James Heinrich <info@getid3.org> //
|
||||
// * version 0.1.1 (15 July 2005) //
|
||||
// //
|
||||
// Support for On2 VP6 codec and meta information //
|
||||
// by Steve Webster <steve.webster@featurecreep.com> //
|
||||
// * version 0.2 (22 February 2006) //
|
||||
// //
|
||||
// Modified to not read entire file into memory //
|
||||
// by James Heinrich <info@getid3.org> //
|
||||
// * version 0.3 (15 June 2006) //
|
||||
// //
|
||||
// Bugfixes for incorrectly parsed FLV dimensions //
|
||||
// and incorrect parsing of onMetaTag //
|
||||
// by Evgeny Moysevich <moysevich@gmail.com> //
|
||||
// * version 0.4 (07 December 2007) //
|
||||
// //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio-video.flv.php //
|
||||
// module for analyzing Shockwave Flash Video files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
define('GETID3_FLV_TAG_AUDIO', 8);
|
||||
define('GETID3_FLV_TAG_VIDEO', 9);
|
||||
define('GETID3_FLV_TAG_META', 18);
|
||||
|
||||
define('GETID3_FLV_VIDEO_H263', 2);
|
||||
define('GETID3_FLV_VIDEO_SCREEN', 3);
|
||||
define('GETID3_FLV_VIDEO_VP6', 4);
|
||||
|
||||
class getid3_flv
|
||||
{
|
||||
|
||||
function getid3_flv(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) {
|
||||
//$start_time = microtime(true);
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
|
||||
$FLVdataLength = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'];
|
||||
$FLVheader = fread($fd, 5);
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'flv';
|
||||
$ThisFileInfo['flv']['header']['signature'] = substr($FLVheader, 0, 3);
|
||||
$ThisFileInfo['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1));
|
||||
$TypeFlags = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1));
|
||||
|
||||
if ($ThisFileInfo['flv']['header']['signature'] != 'FLV') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "FLV" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['flv']['header']['signature'].'"';
|
||||
unset($ThisFileInfo['flv']);
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04);
|
||||
$ThisFileInfo['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01);
|
||||
|
||||
$FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($fd, 4));
|
||||
$FLVheaderFrameLength = 9;
|
||||
if ($FrameSizeDataLength > $FLVheaderFrameLength) {
|
||||
fseek($fd, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR);
|
||||
}
|
||||
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
|
||||
|
||||
$Duration = 0;
|
||||
$found_video = false;
|
||||
$found_audio = false;
|
||||
$found_meta = false;
|
||||
while ((ftell($fd) + 16) < $ThisFileInfo['avdataend']) {
|
||||
$ThisTagHeader = fread($fd, 16);
|
||||
|
||||
$PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 0, 4));
|
||||
$TagType = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 4, 1));
|
||||
$DataLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 5, 3));
|
||||
$Timestamp = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 8, 3));
|
||||
$LastHeaderByte = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1));
|
||||
$NextOffset = ftell($fd) - 1 + $DataLength;
|
||||
if ($Timestamp > $Duration) {
|
||||
$Duration = $Timestamp;
|
||||
}
|
||||
|
||||
//echo __LINE__.'['.ftell($fd).']=('.$TagType.')='.number_format(microtime(true) - $start_time, 3).'<br>';
|
||||
|
||||
switch ($TagType) {
|
||||
case GETID3_FLV_TAG_AUDIO:
|
||||
if (!$found_audio) {
|
||||
$found_audio = true;
|
||||
$ThisFileInfo['flv']['audio']['audioFormat'] = $LastHeaderByte & 0x07;
|
||||
$ThisFileInfo['flv']['audio']['audioRate'] = ($LastHeaderByte & 0x30) / 0x10;
|
||||
$ThisFileInfo['flv']['audio']['audioSampleSize'] = ($LastHeaderByte & 0x40) / 0x40;
|
||||
$ThisFileInfo['flv']['audio']['audioType'] = ($LastHeaderByte & 0x80) / 0x80;
|
||||
}
|
||||
break;
|
||||
|
||||
case GETID3_FLV_TAG_VIDEO:
|
||||
if (!$found_video) {
|
||||
$found_video = true;
|
||||
$ThisFileInfo['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07;
|
||||
|
||||
$FLVvideoHeader = fread($fd, 11);
|
||||
|
||||
if ($ThisFileInfo['flv']['video']['videoCodec'] != GETID3_FLV_VIDEO_VP6) {
|
||||
|
||||
$PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7;
|
||||
$PictureSizeType = $PictureSizeType & 0x0007;
|
||||
$ThisFileInfo['flv']['header']['videoSizeType'] = $PictureSizeType;
|
||||
switch ($PictureSizeType) {
|
||||
case 0:
|
||||
//$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
|
||||
//$PictureSizeEnc <<= 1;
|
||||
//$ThisFileInfo['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8;
|
||||
//$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
|
||||
//$PictureSizeEnc <<= 1;
|
||||
//$ThisFileInfo['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8;
|
||||
|
||||
$PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2));
|
||||
$PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
|
||||
$PictureSizeEnc['x'] >>= 7;
|
||||
$PictureSizeEnc['y'] >>= 7;
|
||||
$ThisFileInfo['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF;
|
||||
$ThisFileInfo['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
$PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3));
|
||||
$PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3));
|
||||
$PictureSizeEnc['x'] >>= 7;
|
||||
$PictureSizeEnc['y'] >>= 7;
|
||||
$ThisFileInfo['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF;
|
||||
$ThisFileInfo['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$ThisFileInfo['video']['resolution_x'] = 352;
|
||||
$ThisFileInfo['video']['resolution_y'] = 288;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
$ThisFileInfo['video']['resolution_x'] = 176;
|
||||
$ThisFileInfo['video']['resolution_y'] = 144;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
$ThisFileInfo['video']['resolution_x'] = 128;
|
||||
$ThisFileInfo['video']['resolution_y'] = 96;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
$ThisFileInfo['video']['resolution_x'] = 320;
|
||||
$ThisFileInfo['video']['resolution_y'] = 240;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
$ThisFileInfo['video']['resolution_x'] = 160;
|
||||
$ThisFileInfo['video']['resolution_y'] = 120;
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['video']['resolution_x'] = 0;
|
||||
$ThisFileInfo['video']['resolution_y'] = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Meta tag
|
||||
case GETID3_FLV_TAG_META:
|
||||
if (!$found_meta) {
|
||||
$found_meta = true;
|
||||
fseek($fd, -1, SEEK_CUR);
|
||||
$reader = new AMFReader(new AMFStream(fread($fd, $DataLength)));
|
||||
$eventName = $reader->readData();
|
||||
$ThisFileInfo['meta'][$eventName] = $reader->readData();
|
||||
unset($reader);
|
||||
|
||||
$ThisFileInfo['video']['frame_rate'] = @$ThisFileInfo['meta']['onMetaData']['framerate'];
|
||||
$ThisFileInfo['video']['resolution_x'] = @$ThisFileInfo['meta']['onMetaData']['width'];
|
||||
$ThisFileInfo['video']['resolution_y'] = @$ThisFileInfo['meta']['onMetaData']['height'];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// noop
|
||||
break;
|
||||
}
|
||||
|
||||
fseek($fd, $NextOffset, SEEK_SET);
|
||||
}
|
||||
|
||||
if ($ThisFileInfo['playtime_seconds'] = $Duration / 1000) {
|
||||
$ThisFileInfo['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds'];
|
||||
}
|
||||
|
||||
if ($ThisFileInfo['flv']['header']['hasAudio']) {
|
||||
$ThisFileInfo['audio']['codec'] = $this->FLVaudioFormat($ThisFileInfo['flv']['audio']['audioFormat']);
|
||||
$ThisFileInfo['audio']['sample_rate'] = $this->FLVaudioRate($ThisFileInfo['flv']['audio']['audioRate']);
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($ThisFileInfo['flv']['audio']['audioSampleSize']);
|
||||
|
||||
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo
|
||||
$ThisFileInfo['audio']['lossless'] = ($ThisFileInfo['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed
|
||||
$ThisFileInfo['audio']['dataformat'] = 'flv';
|
||||
}
|
||||
if (@$ThisFileInfo['flv']['header']['hasVideo']) {
|
||||
$ThisFileInfo['video']['codec'] = $this->FLVvideoCodec($ThisFileInfo['flv']['video']['videoCodec']);
|
||||
$ThisFileInfo['video']['dataformat'] = 'flv';
|
||||
$ThisFileInfo['video']['lossless'] = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function FLVaudioFormat($id) {
|
||||
$FLVaudioFormat = array(
|
||||
0 => 'uncompressed',
|
||||
1 => 'ADPCM',
|
||||
2 => 'mp3',
|
||||
5 => 'Nellymoser 8kHz mono',
|
||||
6 => 'Nellymoser',
|
||||
);
|
||||
return (@$FLVaudioFormat[$id] ? @$FLVaudioFormat[$id] : false);
|
||||
}
|
||||
|
||||
function FLVaudioRate($id) {
|
||||
$FLVaudioRate = array(
|
||||
0 => 5500,
|
||||
1 => 11025,
|
||||
2 => 22050,
|
||||
3 => 44100,
|
||||
);
|
||||
return (@$FLVaudioRate[$id] ? @$FLVaudioRate[$id] : false);
|
||||
}
|
||||
|
||||
function FLVaudioBitDepth($id) {
|
||||
$FLVaudioBitDepth = array(
|
||||
0 => 8,
|
||||
1 => 16,
|
||||
);
|
||||
return (@$FLVaudioBitDepth[$id] ? @$FLVaudioBitDepth[$id] : false);
|
||||
}
|
||||
|
||||
function FLVvideoCodec($id) {
|
||||
$FLVvideoCodec = array(
|
||||
GETID3_FLV_VIDEO_H263 => 'Sorenson H.263',
|
||||
GETID3_FLV_VIDEO_SCREEN => 'Screen video',
|
||||
GETID3_FLV_VIDEO_VP6 => 'On2 VP6',
|
||||
);
|
||||
return (@$FLVvideoCodec[$id] ? @$FLVvideoCodec[$id] : false);
|
||||
}
|
||||
}
|
||||
|
||||
class AMFStream {
|
||||
var $bytes;
|
||||
var $pos;
|
||||
|
||||
function AMFStream(&$bytes) {
|
||||
$this->bytes =& $bytes;
|
||||
$this->pos = 0;
|
||||
}
|
||||
|
||||
function readByte() {
|
||||
return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
|
||||
}
|
||||
|
||||
function readInt() {
|
||||
return ($this->readByte() << 8) + $this->readByte();
|
||||
}
|
||||
|
||||
function readLong() {
|
||||
return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte();
|
||||
}
|
||||
|
||||
function readDouble() {
|
||||
return getid3_lib::BigEndian2Float($this->read(8));
|
||||
}
|
||||
|
||||
function readUTF() {
|
||||
$length = $this->readInt();
|
||||
return $this->read($length);
|
||||
}
|
||||
|
||||
function readLongUTF() {
|
||||
$length = $this->readLong();
|
||||
return $this->read($length);
|
||||
}
|
||||
|
||||
function read($length) {
|
||||
$val = substr($this->bytes, $this->pos, $length);
|
||||
$this->pos += $length;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekByte() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readByte();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekInt() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readInt();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekLong() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readLong();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekDouble() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readDouble();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekUTF() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readUTF();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekLongUTF() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readLongUTF();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
}
|
||||
|
||||
class AMFReader {
|
||||
var $stream;
|
||||
|
||||
function AMFReader(&$stream) {
|
||||
$this->stream =& $stream;
|
||||
}
|
||||
|
||||
function readData() {
|
||||
$value = null;
|
||||
|
||||
$type = $this->stream->readByte();
|
||||
switch ($type) {
|
||||
|
||||
// Double
|
||||
case 0:
|
||||
$value = $this->readDouble();
|
||||
break;
|
||||
|
||||
// Boolean
|
||||
case 1:
|
||||
$value = $this->readBoolean();
|
||||
break;
|
||||
|
||||
// String
|
||||
case 2:
|
||||
$value = $this->readString();
|
||||
break;
|
||||
|
||||
// Object
|
||||
case 3:
|
||||
$value = $this->readObject();
|
||||
break;
|
||||
|
||||
// null
|
||||
case 6:
|
||||
return null;
|
||||
break;
|
||||
|
||||
// Mixed array
|
||||
case 8:
|
||||
$value = $this->readMixedArray();
|
||||
break;
|
||||
|
||||
// Array
|
||||
case 10:
|
||||
$value = $this->readArray();
|
||||
break;
|
||||
|
||||
// Date
|
||||
case 11:
|
||||
$value = $this->readDate();
|
||||
break;
|
||||
|
||||
// Long string
|
||||
case 13:
|
||||
$value = $this->readLongString();
|
||||
break;
|
||||
|
||||
// XML (handled as string)
|
||||
case 15:
|
||||
$value = $this->readXML();
|
||||
break;
|
||||
|
||||
// Typed object (handled as object)
|
||||
case 16:
|
||||
$value = $this->readTypedObject();
|
||||
break;
|
||||
|
||||
// Long string
|
||||
default:
|
||||
$value = '(unknown or unsupported data type)';
|
||||
break;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
function readDouble() {
|
||||
return $this->stream->readDouble();
|
||||
}
|
||||
|
||||
function readBoolean() {
|
||||
return $this->stream->readByte() == 1;
|
||||
}
|
||||
|
||||
function readString() {
|
||||
return $this->stream->readUTF();
|
||||
}
|
||||
|
||||
function readObject() {
|
||||
// Get highest numerical index - ignored
|
||||
// $highestIndex = $this->stream->readLong();
|
||||
|
||||
$data = array();
|
||||
|
||||
while ($key = $this->stream->readUTF()) {
|
||||
$data[$key] = $this->readData();
|
||||
}
|
||||
// Mixed array record ends with empty string (0x00 0x00) and 0x09
|
||||
if (($key == '') && ($this->stream->peekByte() == 0x09)) {
|
||||
// Consume byte
|
||||
$this->stream->readByte();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
function readMixedArray() {
|
||||
// Get highest numerical index - ignored
|
||||
$highestIndex = $this->stream->readLong();
|
||||
|
||||
$data = array();
|
||||
|
||||
while ($key = $this->stream->readUTF()) {
|
||||
if (is_numeric($key)) {
|
||||
$key = (float) $key;
|
||||
}
|
||||
$data[$key] = $this->readData();
|
||||
}
|
||||
// Mixed array record ends with empty string (0x00 0x00) and 0x09
|
||||
if (($key == '') && ($this->stream->peekByte() == 0x09)) {
|
||||
// Consume byte
|
||||
$this->stream->readByte();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
function readArray() {
|
||||
$length = $this->stream->readLong();
|
||||
$data = array();
|
||||
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$data[] = $this->readData();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
function readDate() {
|
||||
$timestamp = $this->stream->readDouble();
|
||||
$timezone = $this->stream->readInt();
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
function readLongString() {
|
||||
return $this->stream->readLongUTF();
|
||||
}
|
||||
|
||||
function readXML() {
|
||||
return $this->stream->readLongUTF();
|
||||
}
|
||||
|
||||
function readTypedObject() {
|
||||
$className = $this->stream->readUTF();
|
||||
return $this->readObject();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,292 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio-video.mpeg.php //
|
||||
// module for analyzing MPEG files //
|
||||
// dependencies: module.audio.mp3.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
|
||||
|
||||
define('GETID3_MPEG_VIDEO_PICTURE_START', "\x00\x00\x01\x00");
|
||||
define('GETID3_MPEG_VIDEO_USER_DATA_START', "\x00\x00\x01\xB2");
|
||||
define('GETID3_MPEG_VIDEO_SEQUENCE_HEADER', "\x00\x00\x01\xB3");
|
||||
define('GETID3_MPEG_VIDEO_SEQUENCE_ERROR', "\x00\x00\x01\xB4");
|
||||
define('GETID3_MPEG_VIDEO_EXTENSION_START', "\x00\x00\x01\xB5");
|
||||
define('GETID3_MPEG_VIDEO_SEQUENCE_END', "\x00\x00\x01\xB7");
|
||||
define('GETID3_MPEG_VIDEO_GROUP_START', "\x00\x00\x01\xB8");
|
||||
define('GETID3_MPEG_AUDIO_START', "\x00\x00\x01\xC0");
|
||||
|
||||
|
||||
class getid3_mpeg
|
||||
{
|
||||
|
||||
function getid3_mpeg(&$fd, &$ThisFileInfo) {
|
||||
if ($ThisFileInfo['avdataend'] <= $ThisFileInfo['avdataoffset']) {
|
||||
$ThisFileInfo['error'][] = '"avdataend" ('.$ThisFileInfo['avdataend'].') is unexpectedly less-than-or-equal-to "avdataoffset" ('.$ThisFileInfo['avdataoffset'].')';
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['fileformat'] = 'mpeg';
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$MPEGstreamData = fread($fd, min(100000, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']));
|
||||
$MPEGstreamDataLength = strlen($MPEGstreamData);
|
||||
|
||||
$foundVideo = true;
|
||||
$VideoChunkOffset = 0;
|
||||
while (substr($MPEGstreamData, $VideoChunkOffset++, 4) !== GETID3_MPEG_VIDEO_SEQUENCE_HEADER) {
|
||||
if ($VideoChunkOffset >= $MPEGstreamDataLength) {
|
||||
$foundVideo = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($foundVideo) {
|
||||
|
||||
// Start code 32 bits
|
||||
// horizontal frame size 12 bits
|
||||
// vertical frame size 12 bits
|
||||
// pixel aspect ratio 4 bits
|
||||
// frame rate 4 bits
|
||||
// bitrate 18 bits
|
||||
// marker bit 1 bit
|
||||
// VBV buffer size 10 bits
|
||||
// constrained parameter flag 1 bit
|
||||
// intra quant. matrix flag 1 bit
|
||||
// intra quant. matrix values 512 bits (present if matrix flag == 1)
|
||||
// non-intra quant. matrix flag 1 bit
|
||||
// non-intra quant. matrix values 512 bits (present if matrix flag == 1)
|
||||
|
||||
$ThisFileInfo['video']['dataformat'] = 'mpeg';
|
||||
|
||||
$VideoChunkOffset += (strlen(GETID3_MPEG_VIDEO_SEQUENCE_HEADER) - 1);
|
||||
|
||||
$FrameSizeDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 3));
|
||||
$VideoChunkOffset += 3;
|
||||
|
||||
$AspectRatioFrameRateDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 1));
|
||||
$VideoChunkOffset += 1;
|
||||
|
||||
$assortedinformation = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 4));
|
||||
$VideoChunkOffset += 4;
|
||||
|
||||
$ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal'] = ($FrameSizeDWORD & 0xFFF000) >> 12; // 12 bits for horizontal frame size
|
||||
$ThisFileInfo['mpeg']['video']['raw']['framesize_vertical'] = ($FrameSizeDWORD & 0x000FFF); // 12 bits for vertical frame size
|
||||
$ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio'] = ($AspectRatioFrameRateDWORD & 0xF0) >> 4;
|
||||
$ThisFileInfo['mpeg']['video']['raw']['frame_rate'] = ($AspectRatioFrameRateDWORD & 0x0F);
|
||||
|
||||
$ThisFileInfo['mpeg']['video']['framesize_horizontal'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal'];
|
||||
$ThisFileInfo['mpeg']['video']['framesize_vertical'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical'];
|
||||
|
||||
$ThisFileInfo['mpeg']['video']['pixel_aspect_ratio'] = $this->MPEGvideoAspectRatioLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']);
|
||||
$ThisFileInfo['mpeg']['video']['pixel_aspect_ratio_text'] = $this->MPEGvideoAspectRatioTextLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']);
|
||||
$ThisFileInfo['mpeg']['video']['frame_rate'] = $this->MPEGvideoFramerateLookup($ThisFileInfo['mpeg']['video']['raw']['frame_rate']);
|
||||
|
||||
$ThisFileInfo['mpeg']['video']['raw']['bitrate'] = getid3_lib::Bin2Dec(substr($assortedinformation, 0, 18));
|
||||
$ThisFileInfo['mpeg']['video']['raw']['marker_bit'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 18, 1));
|
||||
$ThisFileInfo['mpeg']['video']['raw']['vbv_buffer_size'] = getid3_lib::Bin2Dec(substr($assortedinformation, 19, 10));
|
||||
$ThisFileInfo['mpeg']['video']['raw']['constrained_param_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 29, 1));
|
||||
$ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 30, 1));
|
||||
if ($ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag']) {
|
||||
|
||||
// read 512 bits
|
||||
$ThisFileInfo['mpeg']['video']['raw']['intra_quant'] = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64));
|
||||
$VideoChunkOffset += 64;
|
||||
|
||||
$ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($ThisFileInfo['mpeg']['video']['raw']['intra_quant'], 511, 1));
|
||||
$ThisFileInfo['mpeg']['video']['raw']['intra_quant'] = getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1)).substr(getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64)), 0, 511);
|
||||
|
||||
if ($ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag']) {
|
||||
$ThisFileInfo['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64);
|
||||
$VideoChunkOffset += 64;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1));
|
||||
if ($ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag']) {
|
||||
$ThisFileInfo['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64);
|
||||
$VideoChunkOffset += 64;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($ThisFileInfo['mpeg']['video']['raw']['bitrate'] == 0x3FFFF) { // 18 set bits
|
||||
|
||||
$ThisFileInfo['warning'][] = 'This version of getID3() ['.GETID3_VERSION.'] cannot determine average bitrate of VBR MPEG video files';
|
||||
$ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'vbr';
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['mpeg']['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['raw']['bitrate'] * 400;
|
||||
$ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'cbr';
|
||||
$ThisFileInfo['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['bitrate'];
|
||||
|
||||
}
|
||||
|
||||
$ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['mpeg']['video']['framesize_horizontal'];
|
||||
$ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['mpeg']['video']['framesize_vertical'];
|
||||
$ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['mpeg']['video']['frame_rate'];
|
||||
$ThisFileInfo['video']['bitrate_mode'] = $ThisFileInfo['mpeg']['video']['bitrate_mode'];
|
||||
$ThisFileInfo['video']['pixel_aspect_ratio'] = $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio'];
|
||||
$ThisFileInfo['video']['lossless'] = false;
|
||||
$ThisFileInfo['video']['bits_per_sample'] = 24;
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['error'][] = 'Could not find start of video block in the first 100,000 bytes (or before end of file) - this might not be an MPEG-video file?';
|
||||
|
||||
}
|
||||
|
||||
//0x000001B3 begins the sequence_header of every MPEG video stream.
|
||||
//But in MPEG-2, this header must immediately be followed by an
|
||||
//extension_start_code (0x000001B5) with a sequence_extension ID (1).
|
||||
//(This extension contains all the additional MPEG-2 stuff.)
|
||||
//MPEG-1 doesn't have this extension, so that's a sure way to tell the
|
||||
//difference between MPEG-1 and MPEG-2 video streams.
|
||||
|
||||
if (substr($MPEGstreamData, $VideoChunkOffset, 4) == GETID3_MPEG_VIDEO_EXTENSION_START) {
|
||||
$ThisFileInfo['video']['codec'] = 'MPEG-2';
|
||||
} else {
|
||||
$ThisFileInfo['video']['codec'] = 'MPEG-1';
|
||||
}
|
||||
|
||||
|
||||
$AudioChunkOffset = 0;
|
||||
while (true) {
|
||||
while (substr($MPEGstreamData, $AudioChunkOffset++, 4) !== GETID3_MPEG_AUDIO_START) {
|
||||
if ($AudioChunkOffset >= $MPEGstreamDataLength) {
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = 0; $i <= 7; $i++) {
|
||||
// some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after
|
||||
// I have no idea why or what the difference is, so this is a stupid hack.
|
||||
// If anybody has any better idea of what's going on, please let me know - info@getid3.org
|
||||
|
||||
$dummy = $ThisFileInfo;
|
||||
if (getid3_mp3::decodeMPEGaudioHeader($fd, ($AudioChunkOffset + 3) + 8 + $i, $dummy, false)) {
|
||||
$ThisFileInfo = $dummy;
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
|
||||
$ThisFileInfo['audio']['lossless'] = false;
|
||||
break 2;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Temporary hack to account for interleaving overhead:
|
||||
if (!empty($ThisFileInfo['video']['bitrate']) && !empty($ThisFileInfo['audio']['bitrate'])) {
|
||||
$ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['video']['bitrate'] + $ThisFileInfo['audio']['bitrate']);
|
||||
|
||||
// Interleaved MPEG audio/video files have a certain amount of overhead that varies
|
||||
// by both video and audio bitrates, and not in any sensible, linear/logarithmic patter
|
||||
// Use interpolated lookup tables to approximately guess how much is overhead, because
|
||||
// playtime is calculated as filesize / total-bitrate
|
||||
$ThisFileInfo['playtime_seconds'] *= $this->MPEGsystemNonOverheadPercentage($ThisFileInfo['video']['bitrate'], $ThisFileInfo['audio']['bitrate']);
|
||||
|
||||
//switch ($ThisFileInfo['video']['bitrate']) {
|
||||
// case('5000000'):
|
||||
// $multiplier = 0.93292642112380355828048824319889;
|
||||
// break;
|
||||
// case('5500000'):
|
||||
// $multiplier = 0.93582895375200989965359777343219;
|
||||
// break;
|
||||
// case('6000000'):
|
||||
// $multiplier = 0.93796247714820932532911373859139;
|
||||
// break;
|
||||
// case('7000000'):
|
||||
// $multiplier = 0.9413264083635103463010117778776;
|
||||
// break;
|
||||
// default:
|
||||
// $multiplier = 1;
|
||||
// break;
|
||||
//}
|
||||
//$ThisFileInfo['playtime_seconds'] *= $multiplier;
|
||||
//$ThisFileInfo['warning'][] = 'Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.';
|
||||
if ($ThisFileInfo['video']['bitrate'] < 50000) {
|
||||
$ThisFileInfo['warning'][] = 'Interleaved MPEG audio/video playtime may be slightly inaccurate for video bitrates below 100kbps. Except in extreme low-bitrate situations, error should be less than 1%. Report to info@getid3.org if greater than this.';
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function MPEGsystemNonOverheadPercentage($VideoBitrate, $AudioBitrate) {
|
||||
$OverheadPercentage = 0;
|
||||
|
||||
$AudioBitrate = max(min($AudioBitrate / 1000, 384), 32); // limit to range of 32kbps - 384kbps (should be only legal bitrates, but maybe VBR?)
|
||||
$VideoBitrate = max(min($VideoBitrate / 1000, 10000), 10); // limit to range of 10kbps - 10Mbps (beyond that curves flatten anyways, no big loss)
|
||||
|
||||
|
||||
//OMBB[audiobitrate] = array(video-10kbps, video-100kbps, video-1000kbps, video-10000kbps)
|
||||
$OverheadMultiplierByBitrate[32] = array(0, 0.9676287944368530, 0.9802276264360310, 0.9844916183244460, 0.9852821845179940);
|
||||
$OverheadMultiplierByBitrate[48] = array(0, 0.9779100089209830, 0.9787770035359320, 0.9846738664076130, 0.9852683013799960);
|
||||
$OverheadMultiplierByBitrate[56] = array(0, 0.9731249855367600, 0.9776624308938040, 0.9832606361852130, 0.9843922606633340);
|
||||
$OverheadMultiplierByBitrate[64] = array(0, 0.9755642683275760, 0.9795256705493390, 0.9836573009193170, 0.9851122539404470);
|
||||
$OverheadMultiplierByBitrate[96] = array(0, 0.9788025247497290, 0.9798553314148700, 0.9822956869792560, 0.9834815119124690);
|
||||
$OverheadMultiplierByBitrate[128] = array(0, 0.9816940050925480, 0.9821675936072120, 0.9829756927470870, 0.9839763420152050);
|
||||
$OverheadMultiplierByBitrate[160] = array(0, 0.9825894094561180, 0.9820913399073960, 0.9823907143253970, 0.9832821783651570);
|
||||
$OverheadMultiplierByBitrate[192] = array(0, 0.9832038474336260, 0.9825731694317960, 0.9821028622712400, 0.9828262076447620);
|
||||
$OverheadMultiplierByBitrate[224] = array(0, 0.9836516298538770, 0.9824718601823890, 0.9818302180625380, 0.9823735101626480);
|
||||
$OverheadMultiplierByBitrate[256] = array(0, 0.9845863022094920, 0.9837229411967540, 0.9824521662210830, 0.9828645172100790);
|
||||
$OverheadMultiplierByBitrate[320] = array(0, 0.9849565280263180, 0.9837683142805110, 0.9822885275960400, 0.9824424382727190);
|
||||
$OverheadMultiplierByBitrate[384] = array(0, 0.9856094774357600, 0.9844573394432720, 0.9825970399837330, 0.9824673808303890);
|
||||
|
||||
$BitrateToUseMin = 32;
|
||||
$BitrateToUseMax = 32;
|
||||
$previousBitrate = 32;
|
||||
foreach ($OverheadMultiplierByBitrate as $key => $value) {
|
||||
if ($AudioBitrate >= $previousBitrate) {
|
||||
$BitrateToUseMin = $previousBitrate;
|
||||
}
|
||||
if ($AudioBitrate < $key) {
|
||||
$BitrateToUseMax = $key;
|
||||
break;
|
||||
}
|
||||
$previousBitrate = $key;
|
||||
}
|
||||
$FactorA = ($BitrateToUseMax - $AudioBitrate) / ($BitrateToUseMax - $BitrateToUseMin);
|
||||
|
||||
$VideoBitrateLog10 = log10($VideoBitrate);
|
||||
$VideoFactorMin1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][floor($VideoBitrateLog10)];
|
||||
$VideoFactorMin2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][floor($VideoBitrateLog10)];
|
||||
$VideoFactorMax1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][ceil($VideoBitrateLog10)];
|
||||
$VideoFactorMax2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][ceil($VideoBitrateLog10)];
|
||||
$FactorV = $VideoBitrateLog10 - floor($VideoBitrateLog10);
|
||||
|
||||
$OverheadPercentage = $VideoFactorMin1 * $FactorA * $FactorV;
|
||||
$OverheadPercentage += $VideoFactorMin2 * (1 - $FactorA) * $FactorV;
|
||||
$OverheadPercentage += $VideoFactorMax1 * $FactorA * (1 - $FactorV);
|
||||
$OverheadPercentage += $VideoFactorMax2 * (1 - $FactorA) * (1 - $FactorV);
|
||||
|
||||
return $OverheadPercentage;
|
||||
}
|
||||
|
||||
|
||||
function MPEGvideoFramerateLookup($rawframerate) {
|
||||
$MPEGvideoFramerateLookup = array(0, 23.976, 24, 25, 29.97, 30, 50, 59.94, 60);
|
||||
return (isset($MPEGvideoFramerateLookup[$rawframerate]) ? (float) $MPEGvideoFramerateLookup[$rawframerate] : (float) 0);
|
||||
}
|
||||
|
||||
function MPEGvideoAspectRatioLookup($rawaspectratio) {
|
||||
$MPEGvideoAspectRatioLookup = array(0, 1, 0.6735, 0.7031, 0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695, 1.0950, 1.1575, 1.2015, 0);
|
||||
return (isset($MPEGvideoAspectRatioLookup[$rawaspectratio]) ? (float) $MPEGvideoAspectRatioLookup[$rawaspectratio] : (float) 0);
|
||||
}
|
||||
|
||||
function MPEGvideoAspectRatioTextLookup($rawaspectratio) {
|
||||
$MPEGvideoAspectRatioTextLookup = array('forbidden', 'square pixels', '0.6735', '16:9, 625 line, PAL', '0.7615', '0.8055', '16:9, 525 line, NTSC', '0.8935', '4:3, 625 line, PAL, CCIR601', '0.9815', '1.0255', '1.0695', '4:3, 525 line, NTSC, CCIR601', '1.1575', '1.2015', 'reserved');
|
||||
return (isset($MPEGvideoAspectRatioTextLookup[$rawaspectratio]) ? $MPEGvideoAspectRatioTextLookup[$rawaspectratio] : '');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,224 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.nsv.php //
|
||||
// module for analyzing Nullsoft NSV files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_nsv
|
||||
{
|
||||
|
||||
function getid3_nsv(&$fd, &$ThisFileInfo) {
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$NSVheader = fread($fd, 4);
|
||||
|
||||
switch ($NSVheader) {
|
||||
case 'NSVs':
|
||||
if ($this->getNSVsHeaderFilepointer($fd, $ThisFileInfo, 0)) {
|
||||
$ThisFileInfo['fileformat'] = 'nsv';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'nsv';
|
||||
$ThisFileInfo['video']['dataformat'] = 'nsv';
|
||||
$ThisFileInfo['audio']['lossless'] = false;
|
||||
$ThisFileInfo['video']['lossless'] = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'NSVf':
|
||||
if ($this->getNSVfHeaderFilepointer($fd, $ThisFileInfo, 0)) {
|
||||
$ThisFileInfo['fileformat'] = 'nsv';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'nsv';
|
||||
$ThisFileInfo['video']['dataformat'] = 'nsv';
|
||||
$ThisFileInfo['audio']['lossless'] = false;
|
||||
$ThisFileInfo['video']['lossless'] = false;
|
||||
$this->getNSVsHeaderFilepointer($fd, $ThisFileInfo, $ThisFileInfo['nsv']['NSVf']['header_length']);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['error'][] = 'Expecting "NSVs" or "NSVf" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$NSVheader.'"';
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isset($ThisFileInfo['nsv']['NSVf'])) {
|
||||
$ThisFileInfo['warning'][] = 'NSVf header not present - cannot calculate playtime or bitrate';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getNSVsHeaderFilepointer(&$fd, &$ThisFileInfo, $fileoffset) {
|
||||
fseek($fd, $fileoffset, SEEK_SET);
|
||||
$NSVsheader = fread($fd, 28);
|
||||
$offset = 0;
|
||||
|
||||
$ThisFileInfo['nsv']['NSVs']['identifier'] = substr($NSVsheader, $offset, 4);
|
||||
$offset += 4;
|
||||
|
||||
if ($ThisFileInfo['nsv']['NSVs']['identifier'] != 'NSVs') {
|
||||
$ThisFileInfo['error'][] = 'expected "NSVs" at offset ('.$fileoffset.'), found "'.$ThisFileInfo['nsv']['NSVs']['identifier'].'" instead';
|
||||
unset($ThisFileInfo['nsv']['NSVs']);
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['nsv']['NSVs']['offset'] = $fileoffset;
|
||||
|
||||
$ThisFileInfo['nsv']['NSVs']['video_codec'] = substr($NSVsheader, $offset, 4);
|
||||
$offset += 4;
|
||||
$ThisFileInfo['nsv']['NSVs']['audio_codec'] = substr($NSVsheader, $offset, 4);
|
||||
$offset += 4;
|
||||
$ThisFileInfo['nsv']['NSVs']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$ThisFileInfo['nsv']['NSVs']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
|
||||
$offset += 2;
|
||||
|
||||
$ThisFileInfo['nsv']['NSVs']['framerate_index'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
//$ThisFileInfo['nsv']['NSVs']['unknown1b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
//$ThisFileInfo['nsv']['NSVs']['unknown1c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
//$ThisFileInfo['nsv']['NSVs']['unknown1d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
//$ThisFileInfo['nsv']['NSVs']['unknown2a'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
//$ThisFileInfo['nsv']['NSVs']['unknown2b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
//$ThisFileInfo['nsv']['NSVs']['unknown2c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
//$ThisFileInfo['nsv']['NSVs']['unknown2d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
|
||||
switch ($ThisFileInfo['nsv']['NSVs']['audio_codec']) {
|
||||
case 'PCM ':
|
||||
$ThisFileInfo['nsv']['NSVs']['bits_channel'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
$ThisFileInfo['nsv']['NSVs']['channels'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
$ThisFileInfo['nsv']['NSVs']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
|
||||
$offset += 2;
|
||||
|
||||
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['nsv']['NSVs']['sample_rate'];
|
||||
break;
|
||||
|
||||
case 'MP3 ':
|
||||
case 'NONE':
|
||||
default:
|
||||
//$ThisFileInfo['nsv']['NSVs']['unknown3'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 4));
|
||||
$offset += 4;
|
||||
break;
|
||||
}
|
||||
|
||||
$ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['nsv']['NSVs']['resolution_x'];
|
||||
$ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['nsv']['NSVs']['resolution_y'];
|
||||
$ThisFileInfo['nsv']['NSVs']['frame_rate'] = $this->NSVframerateLookup($ThisFileInfo['nsv']['NSVs']['framerate_index']);
|
||||
$ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['nsv']['NSVs']['frame_rate'];
|
||||
$ThisFileInfo['video']['bits_per_sample'] = 24;
|
||||
$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getNSVfHeaderFilepointer(&$fd, &$ThisFileInfo, $fileoffset, $getTOCoffsets=false) {
|
||||
fseek($fd, $fileoffset, SEEK_SET);
|
||||
$NSVfheader = fread($fd, 28);
|
||||
$offset = 0;
|
||||
|
||||
$ThisFileInfo['nsv']['NSVf']['identifier'] = substr($NSVfheader, $offset, 4);
|
||||
$offset += 4;
|
||||
|
||||
if ($ThisFileInfo['nsv']['NSVf']['identifier'] != 'NSVf') {
|
||||
$ThisFileInfo['error'][] = 'expected "NSVf" at offset ('.$fileoffset.'), found "'.$ThisFileInfo['nsv']['NSVf']['identifier'].'" instead';
|
||||
unset($ThisFileInfo['nsv']['NSVf']);
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['nsv']['NSVs']['offset'] = $fileoffset;
|
||||
|
||||
$ThisFileInfo['nsv']['NSVf']['header_length'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$ThisFileInfo['nsv']['NSVf']['file_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
if ($ThisFileInfo['nsv']['NSVf']['file_size'] > $ThisFileInfo['avdataend']) {
|
||||
$ThisFileInfo['warning'][] = 'truncated file - NSVf header indicates '.$ThisFileInfo['nsv']['NSVf']['file_size'].' bytes, file actually '.$ThisFileInfo['avdataend'].' bytes';
|
||||
}
|
||||
|
||||
$ThisFileInfo['nsv']['NSVf']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$ThisFileInfo['nsv']['NSVf']['meta_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$ThisFileInfo['nsv']['NSVf']['TOC_entries_1'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$ThisFileInfo['nsv']['NSVf']['TOC_entries_2'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
if ($ThisFileInfo['nsv']['NSVf']['playtime_ms'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt NSV file: NSVf.playtime_ms == zero';
|
||||
return false;
|
||||
}
|
||||
|
||||
$NSVfheader .= fread($fd, $ThisFileInfo['nsv']['NSVf']['meta_size'] + (4 * $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) + (4 * $ThisFileInfo['nsv']['NSVf']['TOC_entries_2']));
|
||||
$NSVfheaderlength = strlen($NSVfheader);
|
||||
$ThisFileInfo['nsv']['NSVf']['metadata'] = substr($NSVfheader, $offset, $ThisFileInfo['nsv']['NSVf']['meta_size']);
|
||||
$offset += $ThisFileInfo['nsv']['NSVf']['meta_size'];
|
||||
|
||||
if ($getTOCoffsets) {
|
||||
$TOCcounter = 0;
|
||||
while ($TOCcounter < $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) {
|
||||
if ($TOCcounter < $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) {
|
||||
$ThisFileInfo['nsv']['NSVf']['TOC_1'][$TOCcounter] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$TOCcounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (trim($ThisFileInfo['nsv']['NSVf']['metadata']) != '') {
|
||||
$ThisFileInfo['nsv']['NSVf']['metadata'] = str_replace('`', "\x01", $ThisFileInfo['nsv']['NSVf']['metadata']);
|
||||
$CommentPairArray = explode("\x01".' ', $ThisFileInfo['nsv']['NSVf']['metadata']);
|
||||
foreach ($CommentPairArray as $CommentPair) {
|
||||
if (strstr($CommentPair, '='."\x01")) {
|
||||
list($key, $value) = explode('='."\x01", $CommentPair, 2);
|
||||
$ThisFileInfo['nsv']['comments'][strtolower($key)][] = trim(str_replace("\x01", '', $value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['nsv']['NSVf']['playtime_ms'] / 1000;
|
||||
$ThisFileInfo['bitrate'] = ($ThisFileInfo['nsv']['NSVf']['file_size'] * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function NSVframerateLookup($framerateindex) {
|
||||
if ($framerateindex <= 127) {
|
||||
return (float) $framerateindex;
|
||||
}
|
||||
|
||||
static $NSVframerateLookup = array();
|
||||
if (empty($NSVframerateLookup)) {
|
||||
$NSVframerateLookup[129] = (float) 29.970;
|
||||
$NSVframerateLookup[131] = (float) 23.976;
|
||||
$NSVframerateLookup[133] = (float) 14.985;
|
||||
$NSVframerateLookup[197] = (float) 59.940;
|
||||
$NSVframerateLookup[199] = (float) 47.952;
|
||||
}
|
||||
return (isset($NSVframerateLookup[$framerateindex]) ? $NSVframerateLookup[$framerateindex] : false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,528 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio-video.real.php //
|
||||
// module for analyzing Real Audio/Video files //
|
||||
// dependencies: module.audio-video.riff.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
||||
|
||||
class getid3_real
|
||||
{
|
||||
|
||||
function getid3_real(&$fd, &$ThisFileInfo) {
|
||||
$ThisFileInfo['fileformat'] = 'real';
|
||||
$ThisFileInfo['bitrate'] = 0;
|
||||
$ThisFileInfo['playtime_seconds'] = 0;
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$ChunkCounter = 0;
|
||||
while (ftell($fd) < $ThisFileInfo['avdataend']) {
|
||||
$ChunkData = fread($fd, 8);
|
||||
$ChunkName = substr($ChunkData, 0, 4);
|
||||
$ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, 4, 4));
|
||||
|
||||
if ($ChunkName == '.ra'."\xFD") {
|
||||
$ChunkData .= fread($fd, $ChunkSize - 8);
|
||||
if ($this->ParseOldRAheader(substr($ChunkData, 0, 128), $ThisFileInfo['real']['old_ra_header'])) {
|
||||
$ThisFileInfo['audio']['dataformat'] = 'real';
|
||||
$ThisFileInfo['audio']['lossless'] = false;
|
||||
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['real']['old_ra_header']['sample_rate'];
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['real']['old_ra_header']['bits_per_sample'];
|
||||
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['real']['old_ra_header']['channels'];
|
||||
|
||||
$ThisFileInfo['playtime_seconds'] = 60 * ($ThisFileInfo['real']['old_ra_header']['audio_bytes'] / $ThisFileInfo['real']['old_ra_header']['bytes_per_minute']);
|
||||
$ThisFileInfo['audio']['bitrate'] = 8 * ($ThisFileInfo['real']['old_ra_header']['audio_bytes'] / $ThisFileInfo['playtime_seconds']);
|
||||
$ThisFileInfo['audio']['codec'] = $this->RealAudioCodecFourCClookup($ThisFileInfo['real']['old_ra_header']['fourcc'], $ThisFileInfo['audio']['bitrate']);
|
||||
|
||||
foreach ($ThisFileInfo['real']['old_ra_header']['comments'] as $key => $valuearray) {
|
||||
if (strlen(trim($valuearray[0])) > 0) {
|
||||
$ThisFileInfo['real']['comments'][$key][] = trim($valuearray[0]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
$ThisFileInfo['error'][] = 'There was a problem parsing this RealAudio file. Please submit it for analysis to http://www.getid3.org/upload/ or info@getid3.org';
|
||||
unset($ThisFileInfo['bitrate']);
|
||||
unset($ThisFileInfo['playtime_seconds']);
|
||||
return false;
|
||||
}
|
||||
|
||||
// shortcut
|
||||
$ThisFileInfo['real']['chunks'][$ChunkCounter] = array();
|
||||
$thisfile_real_chunks_currentchunk = &$ThisFileInfo['real']['chunks'][$ChunkCounter];
|
||||
|
||||
$thisfile_real_chunks_currentchunk['name'] = $ChunkName;
|
||||
$thisfile_real_chunks_currentchunk['offset'] = ftell($fd) - 8;
|
||||
$thisfile_real_chunks_currentchunk['length'] = $ChunkSize;
|
||||
if (($thisfile_real_chunks_currentchunk['offset'] + $thisfile_real_chunks_currentchunk['length']) > $ThisFileInfo['avdataend']) {
|
||||
$ThisFileInfo['warning'][] = 'Chunk "'.$thisfile_real_chunks_currentchunk['name'].'" at offset '.$thisfile_real_chunks_currentchunk['offset'].' claims to be '.$thisfile_real_chunks_currentchunk['length'].' bytes long, which is beyond end of file';
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($ChunkSize > (GETID3_FREAD_BUFFER_SIZE + 8)) {
|
||||
|
||||
$ChunkData .= fread($fd, GETID3_FREAD_BUFFER_SIZE - 8);
|
||||
fseek($fd, $thisfile_real_chunks_currentchunk['offset'] + $ChunkSize, SEEK_SET);
|
||||
|
||||
} elseif(($ChunkSize - 8) > 0) {
|
||||
|
||||
$ChunkData .= fread($fd, $ChunkSize - 8);
|
||||
|
||||
}
|
||||
$offset = 8;
|
||||
|
||||
switch ($ChunkName) {
|
||||
|
||||
case '.RMF': // RealMedia File Header
|
||||
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||
$offset += 2;
|
||||
switch ($thisfile_real_chunks_currentchunk['object_version']) {
|
||||
|
||||
case 0:
|
||||
$thisfile_real_chunks_currentchunk['file_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['headers_count'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
//$ThisFileInfo['warning'][] = 'Expected .RMF-object_version to be "0", actual value is "'.$thisfile_real_chunks_currentchunk['object_version'].'" (should not be a problem)';
|
||||
break;
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'PROP': // Properties Header
|
||||
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||
$offset += 2;
|
||||
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
|
||||
$thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['num_packets'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['index_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['data_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['num_streams'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_real_chunks_currentchunk['flags_raw'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||
$offset += 2;
|
||||
$ThisFileInfo['playtime_seconds'] = $thisfile_real_chunks_currentchunk['duration'] / 1000;
|
||||
if ($thisfile_real_chunks_currentchunk['duration'] > 0) {
|
||||
$ThisFileInfo['bitrate'] += $thisfile_real_chunks_currentchunk['avg_bit_rate'];
|
||||
}
|
||||
$thisfile_real_chunks_currentchunk['flags']['save_enabled'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0001);
|
||||
$thisfile_real_chunks_currentchunk['flags']['perfect_play'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0002);
|
||||
$thisfile_real_chunks_currentchunk['flags']['live_broadcast'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0004);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'MDPR': // Media Properties Header
|
||||
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||
$offset += 2;
|
||||
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
|
||||
$thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['start_time'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['stream_name_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1));
|
||||
$offset += 1;
|
||||
$thisfile_real_chunks_currentchunk['stream_name'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['stream_name_size']);
|
||||
$offset += $thisfile_real_chunks_currentchunk['stream_name_size'];
|
||||
$thisfile_real_chunks_currentchunk['mime_type_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1));
|
||||
$offset += 1;
|
||||
$thisfile_real_chunks_currentchunk['mime_type'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['mime_type_size']);
|
||||
$offset += $thisfile_real_chunks_currentchunk['mime_type_size'];
|
||||
$thisfile_real_chunks_currentchunk['type_specific_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['type_specific_data'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['type_specific_len']);
|
||||
$offset += $thisfile_real_chunks_currentchunk['type_specific_len'];
|
||||
|
||||
// shortcut
|
||||
$thisfile_real_chunks_currentchunk_typespecificdata = &$thisfile_real_chunks_currentchunk['type_specific_data'];
|
||||
|
||||
switch ($thisfile_real_chunks_currentchunk['mime_type']) {
|
||||
case 'video/x-pn-realvideo':
|
||||
case 'video/x-pn-multirate-realvideo':
|
||||
// http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
|
||||
|
||||
// shortcut
|
||||
$thisfile_real_chunks_currentchunk['video_info'] = array();
|
||||
$thisfile_real_chunks_currentchunk_videoinfo = &$thisfile_real_chunks_currentchunk['video_info'];
|
||||
|
||||
$thisfile_real_chunks_currentchunk_videoinfo['dwSize'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 0, 4));
|
||||
$thisfile_real_chunks_currentchunk_videoinfo['fourcc1'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 4, 4);
|
||||
$thisfile_real_chunks_currentchunk_videoinfo['fourcc2'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 8, 4);
|
||||
$thisfile_real_chunks_currentchunk_videoinfo['width'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 12, 2));
|
||||
$thisfile_real_chunks_currentchunk_videoinfo['height'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 14, 2));
|
||||
$thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 16, 2));
|
||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 18, 2));
|
||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 20, 2));
|
||||
$thisfile_real_chunks_currentchunk_videoinfo['frames_per_second'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 22, 2));
|
||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown3'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 24, 2));
|
||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown4'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 26, 2));
|
||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown5'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 28, 2));
|
||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown6'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 30, 2));
|
||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown7'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 32, 2));
|
||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown8'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 34, 2));
|
||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown9'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 36, 2));
|
||||
|
||||
$thisfile_real_chunks_currentchunk_videoinfo['codec'] = getid3_riff::RIFFfourccLookup($thisfile_real_chunks_currentchunk_videoinfo['fourcc2']);
|
||||
|
||||
$ThisFileInfo['video']['resolution_x'] = $thisfile_real_chunks_currentchunk_videoinfo['width'];
|
||||
$ThisFileInfo['video']['resolution_y'] = $thisfile_real_chunks_currentchunk_videoinfo['height'];
|
||||
$ThisFileInfo['video']['frame_rate'] = (float) $thisfile_real_chunks_currentchunk_videoinfo['frames_per_second'];
|
||||
$ThisFileInfo['video']['codec'] = $thisfile_real_chunks_currentchunk_videoinfo['codec'];
|
||||
$ThisFileInfo['video']['bits_per_sample'] = $thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample'];
|
||||
break;
|
||||
|
||||
case 'audio/x-pn-realaudio':
|
||||
case 'audio/x-pn-multirate-realaudio':
|
||||
$this->ParseOldRAheader($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk['parsed_audio_data']);
|
||||
|
||||
$ThisFileInfo['audio']['sample_rate'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['sample_rate'];
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['bits_per_sample'];
|
||||
$ThisFileInfo['audio']['channels'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['channels'];
|
||||
if (!empty($ThisFileInfo['audio']['dataformat'])) {
|
||||
foreach ($ThisFileInfo['audio'] as $key => $value) {
|
||||
if ($key != 'streams') {
|
||||
$ThisFileInfo['audio']['streams'][$thisfile_real_chunks_currentchunk['stream_number']][$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'logical-fileinfo':
|
||||
// shortcut
|
||||
$thisfile_real_chunks_currentchunk['logical_fileinfo'] = array();
|
||||
$thisfile_real_chunks_currentchunk_logicalfileinfo = &$thisfile_real_chunks_currentchunk['logical_fileinfo'];
|
||||
|
||||
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset = 0;
|
||||
$thisfile_real_chunks_currentchunk_logicalfileinfo['logical_fileinfo_length'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
|
||||
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
|
||||
|
||||
//$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
|
||||
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
|
||||
|
||||
$thisfile_real_chunks_currentchunk_logicalfileinfo['num_tags'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
|
||||
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
|
||||
|
||||
//$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
|
||||
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
|
||||
|
||||
//$thisfile_real_chunks_currentchunk_logicalfileinfo['d'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 1));
|
||||
|
||||
//$thisfile_real_chunks_currentchunk_logicalfileinfo['one_type'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
|
||||
//$thisfile_real_chunks_currentchunk_logicalfileinfo_thislength = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 4 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 2));
|
||||
//$thisfile_real_chunks_currentchunk_logicalfileinfo['one'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength);
|
||||
//$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += (6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (empty($ThisFileInfo['playtime_seconds'])) {
|
||||
$ThisFileInfo['playtime_seconds'] = max($ThisFileInfo['playtime_seconds'], ($thisfile_real_chunks_currentchunk['duration'] + $thisfile_real_chunks_currentchunk['start_time']) / 1000);
|
||||
}
|
||||
if ($thisfile_real_chunks_currentchunk['duration'] > 0) {
|
||||
switch ($thisfile_real_chunks_currentchunk['mime_type']) {
|
||||
case 'audio/x-pn-realaudio':
|
||||
case 'audio/x-pn-multirate-realaudio':
|
||||
$ThisFileInfo['audio']['bitrate'] = (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
|
||||
$ThisFileInfo['audio']['codec'] = $this->RealAudioCodecFourCClookup($thisfile_real_chunks_currentchunk['parsed_audio_data']['fourcc'], $ThisFileInfo['audio']['bitrate']);
|
||||
$ThisFileInfo['audio']['dataformat'] = 'real';
|
||||
$ThisFileInfo['audio']['lossless'] = false;
|
||||
break;
|
||||
|
||||
case 'video/x-pn-realvideo':
|
||||
case 'video/x-pn-multirate-realvideo':
|
||||
$ThisFileInfo['video']['bitrate'] = (isset($ThisFileInfo['video']['bitrate']) ? $ThisFileInfo['video']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
|
||||
$ThisFileInfo['video']['bitrate_mode'] = 'cbr';
|
||||
$ThisFileInfo['video']['dataformat'] = 'real';
|
||||
$ThisFileInfo['video']['lossless'] = false;
|
||||
$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
|
||||
break;
|
||||
|
||||
case 'audio/x-ralf-mpeg4-generic':
|
||||
$ThisFileInfo['audio']['bitrate'] = (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
|
||||
$ThisFileInfo['audio']['codec'] = 'RealAudio Lossless';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'real';
|
||||
$ThisFileInfo['audio']['lossless'] = true;
|
||||
break;
|
||||
}
|
||||
$ThisFileInfo['bitrate'] = (isset($ThisFileInfo['video']['bitrate']) ? $ThisFileInfo['video']['bitrate'] : 0) + (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'CONT': // Content Description Header (text comments)
|
||||
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||
$offset += 2;
|
||||
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
|
||||
$thisfile_real_chunks_currentchunk['title_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_real_chunks_currentchunk['title'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['title_len']);
|
||||
$offset += $thisfile_real_chunks_currentchunk['title_len'];
|
||||
|
||||
$thisfile_real_chunks_currentchunk['artist_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_real_chunks_currentchunk['artist'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['artist_len']);
|
||||
$offset += $thisfile_real_chunks_currentchunk['artist_len'];
|
||||
|
||||
$thisfile_real_chunks_currentchunk['copyright_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_real_chunks_currentchunk['copyright'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['copyright_len']);
|
||||
$offset += $thisfile_real_chunks_currentchunk['copyright_len'];
|
||||
|
||||
$thisfile_real_chunks_currentchunk['comment_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_real_chunks_currentchunk['comment'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['comment_len']);
|
||||
$offset += $thisfile_real_chunks_currentchunk['comment_len'];
|
||||
|
||||
|
||||
$commentkeystocopy = array('title'=>'title', 'artist'=>'artist', 'copyright'=>'copyright', 'comment'=>'comment');
|
||||
foreach ($commentkeystocopy as $key => $val) {
|
||||
if ($thisfile_real_chunks_currentchunk[$key]) {
|
||||
$ThisFileInfo['real']['comments'][$val][] = trim($thisfile_real_chunks_currentchunk[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'DATA': // Data Chunk Header
|
||||
// do nothing
|
||||
break;
|
||||
|
||||
case 'INDX': // Index Section Header
|
||||
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||
$offset += 2;
|
||||
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
|
||||
$thisfile_real_chunks_currentchunk['num_indices'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_real_chunks_currentchunk['next_index_header'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
if ($thisfile_real_chunks_currentchunk['next_index_header'] == 0) {
|
||||
// last index chunk found, ignore rest of file
|
||||
break 2;
|
||||
} else {
|
||||
// non-last index chunk, seek to next index chunk (skipping actual index data)
|
||||
fseek($fd, $thisfile_real_chunks_currentchunk['next_index_header'], SEEK_SET);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['warning'][] = 'Unhandled RealMedia chunk "'.$ChunkName.'" at offset '.$thisfile_real_chunks_currentchunk['offset'];
|
||||
break;
|
||||
}
|
||||
$ChunkCounter++;
|
||||
}
|
||||
|
||||
if (!empty($ThisFileInfo['audio']['streams'])) {
|
||||
$ThisFileInfo['audio']['bitrate'] = 0;
|
||||
foreach ($ThisFileInfo['audio']['streams'] as $key => $valuearray) {
|
||||
$ThisFileInfo['audio']['bitrate'] += $valuearray['bitrate'];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function ParseOldRAheader($OldRAheaderData, &$ParsedArray) {
|
||||
// http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
|
||||
|
||||
$ParsedArray = array();
|
||||
$ParsedArray['magic'] = substr($OldRAheaderData, 0, 4);
|
||||
if ($ParsedArray['magic'] != '.ra'."\xFD") {
|
||||
return false;
|
||||
}
|
||||
$ParsedArray['version1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 4, 2));
|
||||
|
||||
if ($ParsedArray['version1'] < 3) {
|
||||
|
||||
return false;
|
||||
|
||||
} elseif ($ParsedArray['version1'] == 3) {
|
||||
|
||||
$ParsedArray['fourcc1'] = '.ra3';
|
||||
$ParsedArray['bits_per_sample'] = 16; // hard-coded for old versions?
|
||||
$ParsedArray['sample_rate'] = 8000; // hard-coded for old versions?
|
||||
|
||||
$ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2));
|
||||
$ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 8, 2)); // always 1 (?)
|
||||
//$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 10, 2));
|
||||
//$ParsedArray['unknown2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 2));
|
||||
//$ParsedArray['unknown3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 14, 2));
|
||||
$ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2));
|
||||
$ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4));
|
||||
$ParsedArray['comments_raw'] = substr($OldRAheaderData, 22, $ParsedArray['header_size'] - 22 + 1); // not including null terminator
|
||||
|
||||
$commentoffset = 0;
|
||||
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
||||
$ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
||||
$commentoffset += $commentlength;
|
||||
|
||||
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
||||
$ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
||||
$commentoffset += $commentlength;
|
||||
|
||||
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
||||
$ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
||||
$commentoffset += $commentlength;
|
||||
|
||||
$commentoffset++; // final null terminator (?)
|
||||
$commentoffset++; // fourcc length (?) should be 4
|
||||
$ParsedArray['fourcc'] = substr($OldRAheaderData, 23 + $commentoffset, 4);
|
||||
|
||||
} elseif ($ParsedArray['version1'] <= 5) {
|
||||
|
||||
//$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2));
|
||||
$ParsedArray['fourcc1'] = substr($OldRAheaderData, 8, 4);
|
||||
$ParsedArray['file_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 4));
|
||||
$ParsedArray['version2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2));
|
||||
$ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4));
|
||||
$ParsedArray['codec_flavor_id'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 22, 2));
|
||||
$ParsedArray['coded_frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 24, 4));
|
||||
$ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 28, 4));
|
||||
$ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 32, 4));
|
||||
//$ParsedArray['unknown5'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 36, 4));
|
||||
$ParsedArray['sub_packet_h'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 40, 2));
|
||||
$ParsedArray['frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 42, 2));
|
||||
$ParsedArray['sub_packet_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 44, 2));
|
||||
//$ParsedArray['unknown6'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 46, 2));
|
||||
|
||||
switch ($ParsedArray['version1']) {
|
||||
|
||||
case 4:
|
||||
$ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 2));
|
||||
//$ParsedArray['unknown8'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 50, 2));
|
||||
$ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 2));
|
||||
$ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 54, 2));
|
||||
$ParsedArray['length_fourcc2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 1));
|
||||
$ParsedArray['fourcc2'] = substr($OldRAheaderData, 57, 4);
|
||||
$ParsedArray['length_fourcc3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 61, 1));
|
||||
$ParsedArray['fourcc3'] = substr($OldRAheaderData, 62, 4);
|
||||
//$ParsedArray['unknown9'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 66, 1));
|
||||
//$ParsedArray['unknown10'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 67, 2));
|
||||
$ParsedArray['comments_raw'] = substr($OldRAheaderData, 69, $ParsedArray['header_size'] - 69 + 16);
|
||||
|
||||
$commentoffset = 0;
|
||||
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
||||
$ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
||||
$commentoffset += $commentlength;
|
||||
|
||||
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
||||
$ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
||||
$commentoffset += $commentlength;
|
||||
|
||||
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
||||
$ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
||||
$commentoffset += $commentlength;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
$ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 4));
|
||||
$ParsedArray['sample_rate2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 4));
|
||||
$ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 4));
|
||||
$ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 60, 2));
|
||||
$ParsedArray['genr'] = substr($OldRAheaderData, 62, 4);
|
||||
$ParsedArray['fourcc3'] = substr($OldRAheaderData, 66, 4);
|
||||
$ParsedArray['comments'] = array();
|
||||
break;
|
||||
}
|
||||
$ParsedArray['fourcc'] = $ParsedArray['fourcc3'];
|
||||
|
||||
}
|
||||
foreach ($ParsedArray['comments'] as $key => $value) {
|
||||
if ($ParsedArray['comments'][$key][0] === false) {
|
||||
$ParsedArray['comments'][$key][0] = '';
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function RealAudioCodecFourCClookup($fourcc, $bitrate) {
|
||||
static $RealAudioCodecFourCClookup = array();
|
||||
if (empty($RealAudioCodecFourCClookup)) {
|
||||
// http://www.its.msstate.edu/net/real/reports/config/tags.stats
|
||||
// http://www.freelists.org/archives/matroska-devel/06-2003/fullthread18.html
|
||||
|
||||
$RealAudioCodecFourCClookup['14_4'][8000] = 'RealAudio v2 (14.4kbps)';
|
||||
$RealAudioCodecFourCClookup['14.4'][8000] = 'RealAudio v2 (14.4kbps)';
|
||||
$RealAudioCodecFourCClookup['lpcJ'][8000] = 'RealAudio v2 (14.4kbps)';
|
||||
$RealAudioCodecFourCClookup['28_8'][15200] = 'RealAudio v2 (28.8kbps)';
|
||||
$RealAudioCodecFourCClookup['28.8'][15200] = 'RealAudio v2 (28.8kbps)';
|
||||
$RealAudioCodecFourCClookup['sipr'][4933] = 'RealAudio v4 (5kbps Voice)';
|
||||
$RealAudioCodecFourCClookup['sipr'][6444] = 'RealAudio v4 (6.5kbps Voice)';
|
||||
$RealAudioCodecFourCClookup['sipr'][8444] = 'RealAudio v4 (8.5kbps Voice)';
|
||||
$RealAudioCodecFourCClookup['sipr'][16000] = 'RealAudio v4 (16kbps Wideband)';
|
||||
$RealAudioCodecFourCClookup['dnet'][8000] = 'RealAudio v3 (8kbps Music)';
|
||||
$RealAudioCodecFourCClookup['dnet'][16000] = 'RealAudio v3 (16kbps Music Low Response)';
|
||||
$RealAudioCodecFourCClookup['dnet'][15963] = 'RealAudio v3 (16kbps Music Mid/High Response)';
|
||||
$RealAudioCodecFourCClookup['dnet'][20000] = 'RealAudio v3 (20kbps Music Stereo)';
|
||||
$RealAudioCodecFourCClookup['dnet'][32000] = 'RealAudio v3 (32kbps Music Mono)';
|
||||
$RealAudioCodecFourCClookup['dnet'][31951] = 'RealAudio v3 (32kbps Music Stereo)';
|
||||
$RealAudioCodecFourCClookup['dnet'][39965] = 'RealAudio v3 (40kbps Music Mono)';
|
||||
$RealAudioCodecFourCClookup['dnet'][40000] = 'RealAudio v3 (40kbps Music Stereo)';
|
||||
$RealAudioCodecFourCClookup['dnet'][79947] = 'RealAudio v3 (80kbps Music Mono)';
|
||||
$RealAudioCodecFourCClookup['dnet'][80000] = 'RealAudio v3 (80kbps Music Stereo)';
|
||||
|
||||
$RealAudioCodecFourCClookup['dnet'][0] = 'RealAudio v3';
|
||||
$RealAudioCodecFourCClookup['sipr'][0] = 'RealAudio v4';
|
||||
$RealAudioCodecFourCClookup['cook'][0] = 'RealAudio G2';
|
||||
$RealAudioCodecFourCClookup['atrc'][0] = 'RealAudio 8';
|
||||
}
|
||||
$roundbitrate = intval(round($bitrate));
|
||||
if (isset($RealAudioCodecFourCClookup[$fourcc][$roundbitrate])) {
|
||||
return $RealAudioCodecFourCClookup[$fourcc][$roundbitrate];
|
||||
} elseif (isset($RealAudioCodecFourCClookup[$fourcc][0])) {
|
||||
return $RealAudioCodecFourCClookup[$fourcc][0];
|
||||
}
|
||||
return $fourcc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio-video.swf.php //
|
||||
// module for analyzing Shockwave Flash files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_swf
|
||||
{
|
||||
|
||||
function getid3_swf(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) {
|
||||
//$start_time = microtime(true);
|
||||
$ThisFileInfo['fileformat'] = 'swf';
|
||||
$ThisFileInfo['video']['dataformat'] = 'swf';
|
||||
|
||||
// http://www.openswf.org/spec/SWFfileformat.html
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
|
||||
$SWFfileData = fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data
|
||||
|
||||
$ThisFileInfo['swf']['header']['signature'] = substr($SWFfileData, 0, 3);
|
||||
switch ($ThisFileInfo['swf']['header']['signature']) {
|
||||
case 'FWS':
|
||||
$ThisFileInfo['swf']['header']['compressed'] = false;
|
||||
break;
|
||||
|
||||
case 'CWS':
|
||||
$ThisFileInfo['swf']['header']['compressed'] = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['error'][] = 'Expecting "FWS" or "CWS" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['swf']['header']['signature'].'"';
|
||||
unset($ThisFileInfo['swf']);
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
$ThisFileInfo['swf']['header']['version'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 3, 1));
|
||||
$ThisFileInfo['swf']['header']['length'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 4, 4));
|
||||
|
||||
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
|
||||
|
||||
if ($ThisFileInfo['swf']['header']['compressed']) {
|
||||
|
||||
$SWFHead = substr($SWFfileData, 0, 8);
|
||||
$SWFfileData = substr($SWFfileData, 8);
|
||||
if ($decompressed = @gzuncompress($SWFfileData)) {
|
||||
|
||||
$SWFfileData = $SWFHead.$decompressed;
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['error'][] = 'Error decompressing compressed SWF data ('.strlen($SWFfileData).' bytes compressed, should be '.($ThisFileInfo['swf']['header']['length'] - 8).' bytes uncompressed)';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
|
||||
|
||||
$FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xF8) >> 3;
|
||||
$FrameSizeDataLength = ceil((5 + (4 * $FrameSizeBitsPerValue)) / 8);
|
||||
$FrameSizeDataString = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 0x07), 3, '0', STR_PAD_LEFT);
|
||||
for ($i = 1; $i < $FrameSizeDataLength; $i++) {
|
||||
$FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT);
|
||||
}
|
||||
list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1));
|
||||
$ThisFileInfo['swf']['header']['frame_width'] = getid3_lib::Bin2Dec($X2);
|
||||
$ThisFileInfo['swf']['header']['frame_height'] = getid3_lib::Bin2Dec($Y2);
|
||||
|
||||
// http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
|
||||
// Next in the header is the frame rate, which is kind of weird.
|
||||
// It is supposed to be stored as a 16bit integer, but the first byte
|
||||
// (or last depending on how you look at it) is completely ignored.
|
||||
// Example: 0x000C -> 0x0C -> 12 So the frame rate is 12 fps.
|
||||
|
||||
// Byte at (8 + $FrameSizeDataLength) is always zero and ignored
|
||||
$ThisFileInfo['swf']['header']['frame_rate'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1));
|
||||
$ThisFileInfo['swf']['header']['frame_count'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2));
|
||||
|
||||
$ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['swf']['header']['frame_rate'];
|
||||
$ThisFileInfo['video']['resolution_x'] = intval(round($ThisFileInfo['swf']['header']['frame_width'] / 20));
|
||||
$ThisFileInfo['video']['resolution_y'] = intval(round($ThisFileInfo['swf']['header']['frame_height'] / 20));
|
||||
$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
|
||||
|
||||
if (($ThisFileInfo['swf']['header']['frame_count'] > 0) && ($ThisFileInfo['swf']['header']['frame_rate'] > 0)) {
|
||||
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['swf']['header']['frame_count'] / $ThisFileInfo['swf']['header']['frame_rate'];
|
||||
}
|
||||
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
|
||||
|
||||
|
||||
// SWF tags
|
||||
|
||||
$CurrentOffset = 12 + $FrameSizeDataLength;
|
||||
$SWFdataLength = strlen($SWFfileData);
|
||||
|
||||
while ($CurrentOffset < $SWFdataLength) {
|
||||
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
|
||||
|
||||
$TagIDTagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2));
|
||||
$TagID = ($TagIDTagLength & 0xFFFC) >> 6;
|
||||
$TagLength = ($TagIDTagLength & 0x003F);
|
||||
$CurrentOffset += 2;
|
||||
if ($TagLength == 0x3F) {
|
||||
$TagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4));
|
||||
$CurrentOffset += 4;
|
||||
}
|
||||
|
||||
unset($TagData);
|
||||
$TagData['offset'] = $CurrentOffset;
|
||||
$TagData['size'] = $TagLength;
|
||||
$TagData['id'] = $TagID;
|
||||
$TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength);
|
||||
switch ($TagID) {
|
||||
case 0: // end of movie
|
||||
break 2;
|
||||
|
||||
case 9: // Set background color
|
||||
//$ThisFileInfo['swf']['tags'][] = $TagData;
|
||||
$ThisFileInfo['swf']['bgcolor'] = strtoupper(str_pad(dechex(getid3_lib::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT));
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($ReturnAllTagData) {
|
||||
$ThisFileInfo['swf']['tags'][] = $TagData;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$CurrentOffset += $TagLength;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,542 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.aac.php //
|
||||
// module for analyzing AAC Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_aac
|
||||
{
|
||||
|
||||
// new combined constructor
|
||||
function getid3_aac(&$fd, &$ThisFileInfo, $option) {
|
||||
|
||||
if ($option === 'adif') {
|
||||
$this->getAACADIFheaderFilepointer($fd, $ThisFileInfo);
|
||||
}
|
||||
elseif ($option === 'adts') {
|
||||
$this->getAACADTSheaderFilepointer($fd, $ThisFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getAACADIFheaderFilepointer(&$fd, &$ThisFileInfo) {
|
||||
$ThisFileInfo['fileformat'] = 'aac';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'aac';
|
||||
$ThisFileInfo['audio']['lossless'] = false;
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$AACheader = fread($fd, 1024);
|
||||
$offset = 0;
|
||||
|
||||
if (substr($AACheader, 0, 4) == 'ADIF') {
|
||||
|
||||
// http://faac.sourceforge.net/wiki/index.php?page=ADIF
|
||||
|
||||
// http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
|
||||
// adif_header() {
|
||||
// adif_id 32
|
||||
// copyright_id_present 1
|
||||
// if( copyright_id_present )
|
||||
// copyright_id 72
|
||||
// original_copy 1
|
||||
// home 1
|
||||
// bitstream_type 1
|
||||
// bitrate 23
|
||||
// num_program_config_elements 4
|
||||
// for (i = 0; i < num_program_config_elements + 1; i++ ) {
|
||||
// if( bitstream_type == '0' )
|
||||
// adif_buffer_fullness 20
|
||||
// program_config_element()
|
||||
// }
|
||||
// }
|
||||
|
||||
$AACheaderBitstream = getid3_lib::BigEndian2Bin($AACheader);
|
||||
$bitoffset = 0;
|
||||
|
||||
$ThisFileInfo['aac']['header_type'] = 'ADIF';
|
||||
$bitoffset += 32;
|
||||
$ThisFileInfo['aac']['header']['mpeg_version'] = 4;
|
||||
|
||||
$ThisFileInfo['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
||||
$bitoffset += 1;
|
||||
if ($ThisFileInfo['aac']['header']['copyright']) {
|
||||
$ThisFileInfo['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72));
|
||||
$bitoffset += 72;
|
||||
}
|
||||
$ThisFileInfo['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
||||
$bitoffset += 1;
|
||||
$ThisFileInfo['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
||||
$bitoffset += 1;
|
||||
$ThisFileInfo['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
||||
$bitoffset += 1;
|
||||
if ($ThisFileInfo['aac']['header']['is_vbr']) {
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
|
||||
$ThisFileInfo['aac']['header']['bitrate_max'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
|
||||
$bitoffset += 23;
|
||||
} else {
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
|
||||
$ThisFileInfo['aac']['header']['bitrate'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
|
||||
$bitoffset += 23;
|
||||
$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['aac']['header']['bitrate'];
|
||||
}
|
||||
if ($ThisFileInfo['audio']['bitrate'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt AAC file: bitrate_audio == zero';
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
|
||||
for ($i = 0; $i < $ThisFileInfo['aac']['header']['num_program_configs']; $i++) {
|
||||
// http://www.audiocoding.com/wiki/index.php?page=program_config_element
|
||||
|
||||
// buffer_fullness 20
|
||||
|
||||
// element_instance_tag 4
|
||||
// object_type 2
|
||||
// sampling_frequency_index 4
|
||||
// num_front_channel_elements 4
|
||||
// num_side_channel_elements 4
|
||||
// num_back_channel_elements 4
|
||||
// num_lfe_channel_elements 2
|
||||
// num_assoc_data_elements 3
|
||||
// num_valid_cc_elements 4
|
||||
// mono_mixdown_present 1
|
||||
// mono_mixdown_element_number 4 if mono_mixdown_present == 1
|
||||
// stereo_mixdown_present 1
|
||||
// stereo_mixdown_element_number 4 if stereo_mixdown_present == 1
|
||||
// matrix_mixdown_idx_present 1
|
||||
// matrix_mixdown_idx 2 if matrix_mixdown_idx_present == 1
|
||||
// pseudo_surround_enable 1 if matrix_mixdown_idx_present == 1
|
||||
// for (i = 0; i < num_front_channel_elements; i++) {
|
||||
// front_element_is_cpe[i] 1
|
||||
// front_element_tag_select[i] 4
|
||||
// }
|
||||
// for (i = 0; i < num_side_channel_elements; i++) {
|
||||
// side_element_is_cpe[i] 1
|
||||
// side_element_tag_select[i] 4
|
||||
// }
|
||||
// for (i = 0; i < num_back_channel_elements; i++) {
|
||||
// back_element_is_cpe[i] 1
|
||||
// back_element_tag_select[i] 4
|
||||
// }
|
||||
// for (i = 0; i < num_lfe_channel_elements; i++) {
|
||||
// lfe_element_tag_select[i] 4
|
||||
// }
|
||||
// for (i = 0; i < num_assoc_data_elements; i++) {
|
||||
// assoc_data_element_tag_select[i] 4
|
||||
// }
|
||||
// for (i = 0; i < num_valid_cc_elements; i++) {
|
||||
// cc_element_is_ind_sw[i] 1
|
||||
// valid_cc_element_tag_select[i] 4
|
||||
// }
|
||||
// byte_alignment() VAR
|
||||
// comment_field_bytes 8
|
||||
// for (i = 0; i < comment_field_bytes; i++) {
|
||||
// comment_field_data[i] 8
|
||||
// }
|
||||
|
||||
if (!$ThisFileInfo['aac']['header']['is_vbr']) {
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['buffer_fullness'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20));
|
||||
$bitoffset += 20;
|
||||
}
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['element_instance_tag'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['object_type'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
||||
$bitoffset += 2;
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
||||
$bitoffset += 2;
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3));
|
||||
$bitoffset += 3;
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
if ($ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present']) {
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
}
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
if ($ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present']) {
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
}
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
if ($ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) {
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
||||
$bitoffset += 2;
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
}
|
||||
for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) {
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['front_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
}
|
||||
for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) {
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['side_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
}
|
||||
for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) {
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['back_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
}
|
||||
for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) {
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
}
|
||||
for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) {
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
}
|
||||
for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) {
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
}
|
||||
|
||||
$bitoffset = ceil($bitoffset / 8) * 8;
|
||||
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8));
|
||||
$bitoffset += 8;
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['comment_field'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes']));
|
||||
$bitoffset += 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'];
|
||||
|
||||
|
||||
$ThisFileInfo['aac']['header']['profile_text'] = $this->AACprofileLookup($ThisFileInfo['aac']['program_configs'][$i]['object_type'], $ThisFileInfo['aac']['header']['mpeg_version']);
|
||||
$ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency'] = $this->AACsampleRateLookup($ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index']);
|
||||
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency'];
|
||||
$ThisFileInfo['audio']['channels'] = $this->AACchannelCountCalculate($ThisFileInfo['aac']['program_configs'][$i]);
|
||||
if ($ThisFileInfo['aac']['program_configs'][$i]['comment_field']) {
|
||||
$ThisFileInfo['aac']['comments'][] = $ThisFileInfo['aac']['program_configs'][$i]['comment_field'];
|
||||
}
|
||||
}
|
||||
$ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate'];
|
||||
|
||||
$ThisFileInfo['audio']['encoder_options'] = $ThisFileInfo['aac']['header_type'].' '.$ThisFileInfo['aac']['header']['profile_text'];
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
unset($ThisFileInfo['aac']);
|
||||
$ThisFileInfo['error'][] = 'AAC-ADIF synch not found at offset '.$ThisFileInfo['avdataoffset'].' (expected "ADIF", found "'.substr($AACheader, 0, 4).'" instead)';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function getAACADTSheaderFilepointer(&$fd, &$ThisFileInfo, $MaxFramesToScan=1000000, $ReturnExtendedInfo=false) {
|
||||
// based loosely on code from AACfile by Jurgen Faul <jfaulØgmx.de>
|
||||
// http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html
|
||||
|
||||
|
||||
// http://faac.sourceforge.net/wiki/index.php?page=ADTS
|
||||
|
||||
// * ADTS Fixed Header: these don't change from frame to frame
|
||||
// syncword 12 always: '111111111111'
|
||||
// ID 1 0: MPEG-4, 1: MPEG-2
|
||||
// layer 2 always: '00'
|
||||
// protection_absent 1
|
||||
// profile 2
|
||||
// sampling_frequency_index 4
|
||||
// private_bit 1
|
||||
// channel_configuration 3
|
||||
// original/copy 1
|
||||
// home 1
|
||||
// emphasis 2 only if ID == 0 (ie MPEG-4)
|
||||
|
||||
// * ADTS Variable Header: these can change from frame to frame
|
||||
// copyright_identification_bit 1
|
||||
// copyright_identification_start 1
|
||||
// aac_frame_length 13 length of the frame including header (in bytes)
|
||||
// adts_buffer_fullness 11 0x7FF indicates VBR
|
||||
// no_raw_data_blocks_in_frame 2
|
||||
|
||||
// * ADTS Error check
|
||||
// crc_check 16 only if protection_absent == 0
|
||||
|
||||
$byteoffset = 0;
|
||||
$framenumber = 0;
|
||||
|
||||
// Init bit pattern array
|
||||
static $decbin = array();
|
||||
|
||||
// Populate $bindec
|
||||
for ($i = 0; $i < 256; $i++) {
|
||||
$decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
// used to calculate bitrate below
|
||||
$BitrateCache = array();
|
||||
|
||||
|
||||
while (true) {
|
||||
// breaks out when end-of-file encountered, or invalid data found,
|
||||
// or MaxFramesToScan frames have been scanned
|
||||
|
||||
if ($byteoffset >= pow(2, 31)) {
|
||||
$ThisFileInfo['warning'][] = 'Unable to parse AAC file beyond '.ftell($fd).' (PHP does not support file operations beyond 2GB)';
|
||||
return false;
|
||||
}
|
||||
fseek($fd, $byteoffset, SEEK_SET);
|
||||
|
||||
// First get substring
|
||||
$substring = fread($fd, 10);
|
||||
$substringlength = strlen($substring);
|
||||
if ($substringlength != 10) {
|
||||
$ThisFileInfo['error'][] = 'Failed to read 10 bytes at offset '.(ftell($fd) - $substringlength).' (only read '.$substringlength.' bytes)';
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialise $AACheaderBitstream
|
||||
$AACheaderBitstream = '';
|
||||
|
||||
// Loop thru substring chars
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$AACheaderBitstream .= $decbin[$substring{$i}];
|
||||
}
|
||||
|
||||
$bitoffset = 0;
|
||||
|
||||
$synctest = bindec(substr($AACheaderBitstream, $bitoffset, 12));
|
||||
|
||||
$bitoffset += 12;
|
||||
if ($synctest != 0x0FFF) {
|
||||
$ThisFileInfo['error'][] = 'Synch pattern (0x0FFF) not found at offset '.(ftell($fd) - 10).' (found 0x0'.strtoupper(dechex($synctest)).' instead)';
|
||||
if ($ThisFileInfo['fileformat'] == 'aac') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Gather info for first frame only - this takes time to do 1000 times!
|
||||
if ($framenumber > 0) {
|
||||
|
||||
if (!$AACheaderBitstream[$bitoffset]) {
|
||||
|
||||
// MPEG-4
|
||||
$bitoffset += 20;
|
||||
|
||||
} else {
|
||||
|
||||
// MPEG-2
|
||||
$bitoffset += 18;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['aac']['header_type'] = 'ADTS';
|
||||
$ThisFileInfo['aac']['header']['synch'] = $synctest;
|
||||
$ThisFileInfo['fileformat'] = 'aac';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'aac';
|
||||
|
||||
$ThisFileInfo['aac']['header']['mpeg_version'] = ((substr($AACheaderBitstream, $bitoffset, 1) == '0') ? 4 : 2);
|
||||
$bitoffset += 1;
|
||||
$ThisFileInfo['aac']['header']['layer'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
||||
$bitoffset += 2;
|
||||
if ($ThisFileInfo['aac']['header']['layer'] != 0) {
|
||||
$ThisFileInfo['error'][] = 'Layer error - expected 0x00, found 0x'.dechex($ThisFileInfo['aac']['header']['layer']).' instead';
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['aac']['header']['crc_present'] = ((substr($AACheaderBitstream, $bitoffset, 1) == '0') ? true : false);
|
||||
$bitoffset += 1;
|
||||
$ThisFileInfo['aac']['header']['profile_id'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
||||
$bitoffset += 2;
|
||||
$ThisFileInfo['aac']['header']['profile_text'] = $this->AACprofileLookup($ThisFileInfo['aac']['header']['profile_id'], $ThisFileInfo['aac']['header']['mpeg_version']);
|
||||
|
||||
$ThisFileInfo['aac']['header']['sample_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
$ThisFileInfo['aac']['header']['sample_frequency'] = $this->AACsampleRateLookup($ThisFileInfo['aac']['header']['sample_frequency_index']);
|
||||
if ($ThisFileInfo['aac']['header']['sample_frequency'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt AAC file: sample_frequency == zero';
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['aac']['header']['sample_frequency'];
|
||||
|
||||
$ThisFileInfo['aac']['header']['private'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
$ThisFileInfo['aac']['header']['channel_configuration'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3));
|
||||
$bitoffset += 3;
|
||||
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['aac']['header']['channel_configuration'];
|
||||
$ThisFileInfo['aac']['header']['original'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
$ThisFileInfo['aac']['header']['home'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
|
||||
if ($ThisFileInfo['aac']['header']['mpeg_version'] == 4) {
|
||||
$ThisFileInfo['aac']['header']['emphasis'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
||||
$bitoffset += 2;
|
||||
}
|
||||
|
||||
if ($ReturnExtendedInfo) {
|
||||
|
||||
$ThisFileInfo['aac'][$framenumber]['copyright_id_bit'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
$ThisFileInfo['aac'][$framenumber]['copyright_id_start'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
|
||||
} else {
|
||||
|
||||
$bitoffset += 2;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$FrameLength = bindec(substr($AACheaderBitstream, $bitoffset, 13));
|
||||
|
||||
if (!isset($BitrateCache[$FrameLength])) {
|
||||
$BitrateCache[$FrameLength] = ($ThisFileInfo['aac']['header']['sample_frequency'] / 1024) * $FrameLength * 8;
|
||||
}
|
||||
@$ThisFileInfo['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]]++;
|
||||
|
||||
$ThisFileInfo['aac'][$framenumber]['aac_frame_length'] = $FrameLength;
|
||||
$bitoffset += 13;
|
||||
$ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] = bindec(substr($AACheaderBitstream, $bitoffset, 11));
|
||||
$bitoffset += 11;
|
||||
if ($ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] == 0x07FF) {
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
|
||||
} else {
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
|
||||
}
|
||||
$ThisFileInfo['aac'][$framenumber]['num_raw_data_blocks'] = bindec(substr($AACheaderBitstream, $bitoffset, 2));
|
||||
$bitoffset += 2;
|
||||
|
||||
if ($ThisFileInfo['aac']['header']['crc_present']) {
|
||||
//$ThisFileInfo['aac'][$framenumber]['crc'] = bindec(substr($AACheaderBitstream, $bitoffset, 16));
|
||||
$bitoffset += 16;
|
||||
}
|
||||
|
||||
if (!$ReturnExtendedInfo) {
|
||||
unset($ThisFileInfo['aac'][$framenumber]);
|
||||
}
|
||||
|
||||
$byteoffset += $FrameLength;
|
||||
if ((++$framenumber < $MaxFramesToScan) && (($byteoffset + 10) < $ThisFileInfo['avdataend'])) {
|
||||
|
||||
// keep scanning
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['aac']['frames'] = $framenumber;
|
||||
$ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] / $byteoffset) * (($framenumber * 1024) / $ThisFileInfo['aac']['header']['sample_frequency']); // (1 / % of file scanned) * (samples / (samples/sec)) = seconds
|
||||
if ($ThisFileInfo['playtime_seconds'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt AAC file: playtime_seconds == zero';
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
ksort($ThisFileInfo['aac']['bitrate_distribution']);
|
||||
|
||||
$ThisFileInfo['audio']['encoder_options'] = $ThisFileInfo['aac']['header_type'].' '.$ThisFileInfo['aac']['header']['profile_text'];
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
// should never get here.
|
||||
}
|
||||
|
||||
function AACsampleRateLookup($samplerateid) {
|
||||
static $AACsampleRateLookup = array();
|
||||
if (empty($AACsampleRateLookup)) {
|
||||
$AACsampleRateLookup[0] = 96000;
|
||||
$AACsampleRateLookup[1] = 88200;
|
||||
$AACsampleRateLookup[2] = 64000;
|
||||
$AACsampleRateLookup[3] = 48000;
|
||||
$AACsampleRateLookup[4] = 44100;
|
||||
$AACsampleRateLookup[5] = 32000;
|
||||
$AACsampleRateLookup[6] = 24000;
|
||||
$AACsampleRateLookup[7] = 22050;
|
||||
$AACsampleRateLookup[8] = 16000;
|
||||
$AACsampleRateLookup[9] = 12000;
|
||||
$AACsampleRateLookup[10] = 11025;
|
||||
$AACsampleRateLookup[11] = 8000;
|
||||
$AACsampleRateLookup[12] = 0;
|
||||
$AACsampleRateLookup[13] = 0;
|
||||
$AACsampleRateLookup[14] = 0;
|
||||
$AACsampleRateLookup[15] = 0;
|
||||
}
|
||||
return (isset($AACsampleRateLookup[$samplerateid]) ? $AACsampleRateLookup[$samplerateid] : 'invalid');
|
||||
}
|
||||
|
||||
function AACprofileLookup($profileid, $mpegversion) {
|
||||
static $AACprofileLookup = array();
|
||||
if (empty($AACprofileLookup)) {
|
||||
$AACprofileLookup[2][0] = 'Main profile';
|
||||
$AACprofileLookup[2][1] = 'Low Complexity profile (LC)';
|
||||
$AACprofileLookup[2][2] = 'Scalable Sample Rate profile (SSR)';
|
||||
$AACprofileLookup[2][3] = '(reserved)';
|
||||
$AACprofileLookup[4][0] = 'AAC_MAIN';
|
||||
$AACprofileLookup[4][1] = 'AAC_LC';
|
||||
$AACprofileLookup[4][2] = 'AAC_SSR';
|
||||
$AACprofileLookup[4][3] = 'AAC_LTP';
|
||||
}
|
||||
return (isset($AACprofileLookup[$mpegversion][$profileid]) ? $AACprofileLookup[$mpegversion][$profileid] : 'invalid');
|
||||
}
|
||||
|
||||
function AACchannelCountCalculate($program_configs) {
|
||||
$channels = 0;
|
||||
for ($i = 0; $i < $program_configs['num_front_channel_elements']; $i++) {
|
||||
$channels++;
|
||||
if ($program_configs['front_element_is_cpe'][$i]) {
|
||||
// each front element is channel pair (CPE = Channel Pair Element)
|
||||
$channels++;
|
||||
}
|
||||
}
|
||||
for ($i = 0; $i < $program_configs['num_side_channel_elements']; $i++) {
|
||||
$channels++;
|
||||
if ($program_configs['side_element_is_cpe'][$i]) {
|
||||
// each side element is channel pair (CPE = Channel Pair Element)
|
||||
$channels++;
|
||||
}
|
||||
}
|
||||
for ($i = 0; $i < $program_configs['num_back_channel_elements']; $i++) {
|
||||
$channels++;
|
||||
if ($program_configs['back_element_is_cpe'][$i]) {
|
||||
// each back element is channel pair (CPE = Channel Pair Element)
|
||||
$channels++;
|
||||
}
|
||||
}
|
||||
for ($i = 0; $i < $program_configs['num_lfe_channel_elements']; $i++) {
|
||||
$channels++;
|
||||
}
|
||||
return $channels;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,497 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.ac3.php //
|
||||
// module for analyzing AC-3 (aka Dolby Digital) audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_ac3
|
||||
{
|
||||
|
||||
function getid3_ac3(&$fd, &$ThisFileInfo) {
|
||||
|
||||
///AH
|
||||
$ThisFileInfo['ac3']['raw']['bsi'] = array();
|
||||
$thisfile_ac3 = &$ThisFileInfo['ac3'];
|
||||
$thisfile_ac3_raw = &$thisfile_ac3['raw'];
|
||||
$thisfile_ac3_raw_bsi = &$thisfile_ac3_raw['bsi'];
|
||||
|
||||
|
||||
// http://www.atsc.org/standards/a_52a.pdf
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'ac3';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'ac3';
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
|
||||
$ThisFileInfo['audio']['lossless'] = false;
|
||||
|
||||
// An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames
|
||||
// Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256
|
||||
// new audio samples per channel. A synchronization information (SI) header at the beginning
|
||||
// of each frame contains information needed to acquire and maintain synchronization. A
|
||||
// bit stream information (BSI) header follows SI, and contains parameters describing the coded
|
||||
// audio service. The coded audio blocks may be followed by an auxiliary data (Aux) field. At the
|
||||
// end of each frame is an error check field that includes a CRC word for error detection. An
|
||||
// additional CRC word is located in the SI header, the use of which, by a decoder, is optional.
|
||||
//
|
||||
// syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$AC3header['syncinfo'] = fread($fd, 5);
|
||||
$thisfile_ac3_raw['synchinfo']['synchword'] = substr($AC3header['syncinfo'], 0, 2);
|
||||
|
||||
if ($thisfile_ac3_raw['synchinfo']['synchword'] != "\x0B\x77") {
|
||||
|
||||
$ThisFileInfo['error'][] = 'Expecting "\x0B\x77" at offset '.$ThisFileInfo['avdataoffset'].', found \x'.strtoupper(dechex($AC3header['syncinfo']{0})).'\x'.strtoupper(dechex($AC3header['syncinfo']{1})).' instead';
|
||||
unset($thisfile_ac3);
|
||||
return false;
|
||||
|
||||
} else {
|
||||
|
||||
// syncinfo() {
|
||||
// syncword 16
|
||||
// crc1 16
|
||||
// fscod 2
|
||||
// frmsizecod 6
|
||||
// } /* end of syncinfo */
|
||||
|
||||
$thisfile_ac3_raw['synchinfo']['crc1'] = getid3_lib::LittleEndian2Int(substr($AC3header['syncinfo'], 2, 2));
|
||||
$ac3_synchinfo_fscod_frmsizecod = getid3_lib::LittleEndian2Int(substr($AC3header['syncinfo'], 4, 1));
|
||||
$thisfile_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xC0) >> 6;
|
||||
$thisfile_ac3_raw['synchinfo']['frmsizecod'] = ($ac3_synchinfo_fscod_frmsizecod & 0x3F);
|
||||
|
||||
$thisfile_ac3['sample_rate'] = $this->AC3sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']);
|
||||
if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) {
|
||||
$ThisFileInfo['audio']['sample_rate'] = $thisfile_ac3['sample_rate'];
|
||||
}
|
||||
|
||||
$thisfile_ac3['frame_length'] = $this->AC3frameSizeLookup($thisfile_ac3_raw['synchinfo']['frmsizecod'], $thisfile_ac3_raw['synchinfo']['fscod']);
|
||||
$thisfile_ac3['bitrate'] = $this->AC3bitrateLookup($thisfile_ac3_raw['synchinfo']['frmsizecod']);
|
||||
$ThisFileInfo['audio']['bitrate'] = $thisfile_ac3['bitrate'];
|
||||
|
||||
$AC3header['bsi'] = getid3_lib::BigEndian2Bin(fread($fd, 15));
|
||||
$ac3_bsi_offset = 0;
|
||||
|
||||
$thisfile_ac3_raw_bsi['bsid'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
|
||||
$ac3_bsi_offset += 5;
|
||||
if ($thisfile_ac3_raw_bsi['bsid'] > 8) {
|
||||
// Decoders which can decode version 8 will thus be able to decode version numbers less than 8.
|
||||
// If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used.
|
||||
// Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8.
|
||||
$ThisFileInfo['error'][] = 'Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 8';
|
||||
unset($thisfile_ac3);
|
||||
return false;
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['bsmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 3));
|
||||
$ac3_bsi_offset += 3;
|
||||
$thisfile_ac3_raw_bsi['acmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 3));
|
||||
$ac3_bsi_offset += 3;
|
||||
|
||||
$thisfile_ac3['service_type'] = $this->AC3serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']);
|
||||
$ac3_coding_mode = $this->AC3audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']);
|
||||
foreach($ac3_coding_mode as $key => $value) {
|
||||
$thisfile_ac3[$key] = $value;
|
||||
}
|
||||
switch ($thisfile_ac3_raw_bsi['acmod']) {
|
||||
case 0:
|
||||
case 1:
|
||||
$ThisFileInfo['audio']['channelmode'] = 'mono';
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
$ThisFileInfo['audio']['channelmode'] = 'stereo';
|
||||
break;
|
||||
default:
|
||||
$ThisFileInfo['audio']['channelmode'] = 'surround';
|
||||
break;
|
||||
}
|
||||
$ThisFileInfo['audio']['channels'] = $thisfile_ac3['num_channels'];
|
||||
|
||||
if ($thisfile_ac3_raw_bsi['acmod'] & 0x01) {
|
||||
// If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream.
|
||||
$thisfile_ac3_raw_bsi['cmixlev'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
|
||||
$ac3_bsi_offset += 2;
|
||||
$thisfile_ac3['center_mix_level'] = $this->AC3centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']);
|
||||
}
|
||||
|
||||
if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) {
|
||||
// If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream.
|
||||
$thisfile_ac3_raw_bsi['surmixlev'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
|
||||
$ac3_bsi_offset += 2;
|
||||
$thisfile_ac3['surround_mix_level'] = $this->AC3surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']);
|
||||
}
|
||||
|
||||
if ($thisfile_ac3_raw_bsi['acmod'] == 0x02) {
|
||||
// When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround.
|
||||
$thisfile_ac3_raw_bsi['dsurmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
|
||||
$ac3_bsi_offset += 2;
|
||||
$thisfile_ac3['dolby_surround_mode'] = $this->AC3dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']);
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['lfeon'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
|
||||
$ac3_bsi_offset += 1;
|
||||
$thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['lfeon'];
|
||||
if ($thisfile_ac3_raw_bsi['lfeon']) {
|
||||
//$ThisFileInfo['audio']['channels']++;
|
||||
$ThisFileInfo['audio']['channels'] .= '.1';
|
||||
}
|
||||
|
||||
$thisfile_ac3['channels_enabled'] = $this->AC3channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']);
|
||||
|
||||
// This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31.
|
||||
// The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
|
||||
$thisfile_ac3_raw_bsi['dialnorm'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
|
||||
$ac3_bsi_offset += 5;
|
||||
$thisfile_ac3['dialogue_normalization'] = '-'.$thisfile_ac3_raw_bsi['dialnorm'].'dB';
|
||||
|
||||
$thisfile_ac3_raw_bsi['compre_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
|
||||
$ac3_bsi_offset += 1;
|
||||
if ($thisfile_ac3_raw_bsi['compre_flag']) {
|
||||
$thisfile_ac3_raw_bsi['compr'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8));
|
||||
$ac3_bsi_offset += 8;
|
||||
$thisfile_ac3['heavy_compression'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr']);
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['langcode_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
|
||||
$ac3_bsi_offset += 1;
|
||||
if ($thisfile_ac3_raw_bsi['langcode_flag']) {
|
||||
$thisfile_ac3_raw_bsi['langcod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8));
|
||||
$ac3_bsi_offset += 8;
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['audprodie'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
|
||||
$ac3_bsi_offset += 1;
|
||||
if ($thisfile_ac3_raw_bsi['audprodie']) {
|
||||
$thisfile_ac3_raw_bsi['mixlevel'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
|
||||
$ac3_bsi_offset += 5;
|
||||
$thisfile_ac3_raw_bsi['roomtyp'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
|
||||
$ac3_bsi_offset += 2;
|
||||
|
||||
$thisfile_ac3['mixing_level'] = (80 + $thisfile_ac3_raw_bsi['mixlevel']).'dB';
|
||||
$thisfile_ac3['room_type'] = $this->AC3roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']);
|
||||
}
|
||||
|
||||
if ($thisfile_ac3_raw_bsi['acmod'] == 0x00) {
|
||||
// If acmod is 0, then two completely independent program channels (dual mono)
|
||||
// are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case,
|
||||
// a number of additional items are present in BSI or audblk to fully describe Ch2.
|
||||
|
||||
|
||||
// This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31.
|
||||
// The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
|
||||
$thisfile_ac3_raw_bsi['dialnorm2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
|
||||
$ac3_bsi_offset += 5;
|
||||
$thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB';
|
||||
|
||||
$thisfile_ac3_raw_bsi['compre_flag2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
|
||||
$ac3_bsi_offset += 1;
|
||||
if ($thisfile_ac3_raw_bsi['compre_flag2']) {
|
||||
$thisfile_ac3_raw_bsi['compr2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8));
|
||||
$ac3_bsi_offset += 8;
|
||||
$thisfile_ac3['heavy_compression2'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr2']);
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['langcode_flag2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
|
||||
$ac3_bsi_offset += 1;
|
||||
if ($thisfile_ac3_raw_bsi['langcode_flag2']) {
|
||||
$thisfile_ac3_raw_bsi['langcod2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8));
|
||||
$ac3_bsi_offset += 8;
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['audprodie2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
|
||||
$ac3_bsi_offset += 1;
|
||||
if ($thisfile_ac3_raw_bsi['audprodie2']) {
|
||||
$thisfile_ac3_raw_bsi['mixlevel2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
|
||||
$ac3_bsi_offset += 5;
|
||||
$thisfile_ac3_raw_bsi['roomtyp2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
|
||||
$ac3_bsi_offset += 2;
|
||||
|
||||
$thisfile_ac3['mixing_level2'] = (80 + $thisfile_ac3_raw_bsi['mixlevel2']).'dB';
|
||||
$thisfile_ac3['room_type2'] = $this->AC3roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['copyright'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
|
||||
$ac3_bsi_offset += 1;
|
||||
|
||||
$thisfile_ac3_raw_bsi['original'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
|
||||
$ac3_bsi_offset += 1;
|
||||
|
||||
$thisfile_ac3_raw_bsi['timecode1_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
|
||||
$ac3_bsi_offset += 1;
|
||||
if ($thisfile_ac3_raw_bsi['timecode1_flag']) {
|
||||
$thisfile_ac3_raw_bsi['timecode1'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 14));
|
||||
$ac3_bsi_offset += 14;
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['timecode2_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
|
||||
$ac3_bsi_offset += 1;
|
||||
if ($thisfile_ac3_raw_bsi['timecode2_flag']) {
|
||||
$thisfile_ac3_raw_bsi['timecode2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 14));
|
||||
$ac3_bsi_offset += 14;
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['addbsi_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
|
||||
$ac3_bsi_offset += 1;
|
||||
if ($thisfile_ac3_raw_bsi['addbsi_flag']) {
|
||||
$thisfile_ac3_raw_bsi['addbsi_length'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 6));
|
||||
$ac3_bsi_offset += 6;
|
||||
|
||||
$AC3header['bsi'] .= getid3_lib::BigEndian2Bin(fread($fd, $thisfile_ac3_raw_bsi['addbsi_length']));
|
||||
|
||||
$thisfile_ac3_raw_bsi['addbsi_data'] = substr($AC3header['bsi'], $ac3_bsi_offset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8);
|
||||
$ac3_bsi_offset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function AC3sampleRateCodeLookup($fscod) {
|
||||
static $AC3sampleRateCodeLookup = array(
|
||||
0 => 48000,
|
||||
1 => 44100,
|
||||
2 => 32000,
|
||||
3 => 'reserved' // If the reserved code is indicated, the decoder should not attempt to decode audio and should mute.
|
||||
);
|
||||
return (isset($AC3sampleRateCodeLookup[$fscod]) ? $AC3sampleRateCodeLookup[$fscod] : false);
|
||||
}
|
||||
|
||||
function AC3serviceTypeLookup($bsmod, $acmod) {
|
||||
static $AC3serviceTypeLookup = array();
|
||||
if (empty($AC3serviceTypeLookup)) {
|
||||
for ($i = 0; $i <= 7; $i++) {
|
||||
$AC3serviceTypeLookup[0][$i] = 'main audio service: complete main (CM)';
|
||||
$AC3serviceTypeLookup[1][$i] = 'main audio service: music and effects (ME)';
|
||||
$AC3serviceTypeLookup[2][$i] = 'associated service: visually impaired (VI)';
|
||||
$AC3serviceTypeLookup[3][$i] = 'associated service: hearing impaired (HI)';
|
||||
$AC3serviceTypeLookup[4][$i] = 'associated service: dialogue (D)';
|
||||
$AC3serviceTypeLookup[5][$i] = 'associated service: commentary (C)';
|
||||
$AC3serviceTypeLookup[6][$i] = 'associated service: emergency (E)';
|
||||
}
|
||||
|
||||
$AC3serviceTypeLookup[7][1] = 'associated service: voice over (VO)';
|
||||
for ($i = 2; $i <= 7; $i++) {
|
||||
$AC3serviceTypeLookup[7][$i] = 'main audio service: karaoke';
|
||||
}
|
||||
}
|
||||
return (isset($AC3serviceTypeLookup[$bsmod][$acmod]) ? $AC3serviceTypeLookup[$bsmod][$acmod] : false);
|
||||
}
|
||||
|
||||
function AC3audioCodingModeLookup($acmod) {
|
||||
static $AC3audioCodingModeLookup = array();
|
||||
if (empty($AC3audioCodingModeLookup)) {
|
||||
// array(channel configuration, # channels (not incl LFE), channel order)
|
||||
$AC3audioCodingModeLookup = array (
|
||||
0 => array('channel_config'=>'1+1', 'num_channels'=>2, 'channel_order'=>'Ch1,Ch2'),
|
||||
1 => array('channel_config'=>'1/0', 'num_channels'=>1, 'channel_order'=>'C'),
|
||||
2 => array('channel_config'=>'2/0', 'num_channels'=>2, 'channel_order'=>'L,R'),
|
||||
3 => array('channel_config'=>'3/0', 'num_channels'=>3, 'channel_order'=>'L,C,R'),
|
||||
4 => array('channel_config'=>'2/1', 'num_channels'=>3, 'channel_order'=>'L,R,S'),
|
||||
5 => array('channel_config'=>'3/1', 'num_channels'=>4, 'channel_order'=>'L,C,R,S'),
|
||||
6 => array('channel_config'=>'2/2', 'num_channels'=>4, 'channel_order'=>'L,R,SL,SR'),
|
||||
7 => array('channel_config'=>'3/2', 'num_channels'=>5, 'channel_order'=>'L,C,R,SL,SR')
|
||||
);
|
||||
}
|
||||
return (isset($AC3audioCodingModeLookup[$acmod]) ? $AC3audioCodingModeLookup[$acmod] : false);
|
||||
}
|
||||
|
||||
function AC3centerMixLevelLookup($cmixlev) {
|
||||
static $AC3centerMixLevelLookup;
|
||||
if (empty($AC3centerMixLevelLookup)) {
|
||||
$AC3centerMixLevelLookup = array(
|
||||
0 => pow(2, -3.0 / 6), // 0.707 (–3.0 dB)
|
||||
1 => pow(2, -4.5 / 6), // 0.595 (–4.5 dB)
|
||||
2 => pow(2, -6.0 / 6), // 0.500 (–6.0 dB)
|
||||
3 => 'reserved'
|
||||
);
|
||||
}
|
||||
return (isset($AC3centerMixLevelLookup[$cmixlev]) ? $AC3centerMixLevelLookup[$cmixlev] : false);
|
||||
}
|
||||
|
||||
function AC3surroundMixLevelLookup($surmixlev) {
|
||||
static $AC3surroundMixLevelLookup;
|
||||
if (empty($AC3surroundMixLevelLookup)) {
|
||||
$AC3surroundMixLevelLookup = array(
|
||||
0 => pow(2, -3.0 / 6),
|
||||
1 => pow(2, -6.0 / 6),
|
||||
2 => 0,
|
||||
3 => 'reserved'
|
||||
);
|
||||
}
|
||||
return (isset($AC3surroundMixLevelLookup[$surmixlev]) ? $AC3surroundMixLevelLookup[$surmixlev] : false);
|
||||
}
|
||||
|
||||
function AC3dolbySurroundModeLookup($dsurmod) {
|
||||
static $AC3dolbySurroundModeLookup = array(
|
||||
0 => 'not indicated',
|
||||
1 => 'Not Dolby Surround encoded',
|
||||
2 => 'Dolby Surround encoded',
|
||||
3 => 'reserved'
|
||||
);
|
||||
return (isset($AC3dolbySurroundModeLookup[$dsurmod]) ? $AC3dolbySurroundModeLookup[$dsurmod] : false);
|
||||
}
|
||||
|
||||
function AC3channelsEnabledLookup($acmod, $lfeon) {
|
||||
$AC3channelsEnabledLookup = array(
|
||||
'ch1'=>(bool) ($acmod == 0),
|
||||
'ch2'=>(bool) ($acmod == 0),
|
||||
'left'=>(bool) ($acmod > 1),
|
||||
'right'=>(bool) ($acmod > 1),
|
||||
'center'=>(bool) ($acmod & 0x01),
|
||||
'surround_mono'=>false,
|
||||
'surround_left'=>false,
|
||||
'surround_right'=>false,
|
||||
'lfe'=>$lfeon);
|
||||
switch ($acmod) {
|
||||
case 4:
|
||||
case 5:
|
||||
$AC3channelsEnabledLookup['surround_mono'] = true;
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
$AC3channelsEnabledLookup['surround_left'] = true;
|
||||
$AC3channelsEnabledLookup['surround_right'] = true;
|
||||
break;
|
||||
}
|
||||
return $AC3channelsEnabledLookup;
|
||||
}
|
||||
|
||||
function AC3heavyCompression($compre) {
|
||||
// The first four bits indicate gain changes in 6.02dB increments which can be
|
||||
// implemented with an arithmetic shift operation. The following four bits
|
||||
// indicate linear gain changes, and require a 5-bit multiply.
|
||||
// We will represent the two 4-bit fields of compr as follows:
|
||||
// X0 X1 X2 X3 . Y4 Y5 Y6 Y7
|
||||
// The meaning of the X values is most simply described by considering X to represent a 4-bit
|
||||
// signed integer with values from –8 to +7. The gain indicated by X is then (X + 1) * 6.02 dB. The
|
||||
// following table shows this in detail.
|
||||
|
||||
// Meaning of 4 msb of compr
|
||||
// 7 +48.16 dB
|
||||
// 6 +42.14 dB
|
||||
// 5 +36.12 dB
|
||||
// 4 +30.10 dB
|
||||
// 3 +24.08 dB
|
||||
// 2 +18.06 dB
|
||||
// 1 +12.04 dB
|
||||
// 0 +6.02 dB
|
||||
// -1 0 dB
|
||||
// -2 –6.02 dB
|
||||
// -3 –12.04 dB
|
||||
// -4 –18.06 dB
|
||||
// -5 –24.08 dB
|
||||
// -6 –30.10 dB
|
||||
// -7 –36.12 dB
|
||||
// -8 –42.14 dB
|
||||
|
||||
$fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT);
|
||||
if ($fourbit{0} == '1') {
|
||||
$log_gain = -8 + bindec(substr($fourbit, 1));
|
||||
} else {
|
||||
$log_gain = bindec(substr($fourbit, 1));
|
||||
}
|
||||
$log_gain = ($log_gain + 1) * getid3_lib::RGADamplitude2dB(2);
|
||||
|
||||
// The value of Y is a linear representation of a gain change of up to –6 dB. Y is considered to
|
||||
// be an unsigned fractional integer, with a leading value of 1, or: 0.1 Y4 Y5 Y6 Y7 (base 2). Y can
|
||||
// represent values between 0.111112 (or 31/32) and 0.100002 (or 1/2). Thus, Y can represent gain
|
||||
// changes from –0.28 dB to –6.02 dB.
|
||||
|
||||
$lin_gain = (16 + ($compre & 0x0F)) / 32;
|
||||
|
||||
// The combination of X and Y values allows compr to indicate gain changes from
|
||||
// 48.16 – 0.28 = +47.89 dB, to
|
||||
// –42.14 – 6.02 = –48.16 dB.
|
||||
|
||||
return $log_gain - $lin_gain;
|
||||
}
|
||||
|
||||
function AC3roomTypeLookup($roomtyp) {
|
||||
static $AC3roomTypeLookup = array(
|
||||
0 => 'not indicated',
|
||||
1 => 'large room, X curve monitor',
|
||||
2 => 'small room, flat monitor',
|
||||
3 => 'reserved'
|
||||
);
|
||||
return (isset($AC3roomTypeLookup[$roomtyp]) ? $AC3roomTypeLookup[$roomtyp] : false);
|
||||
}
|
||||
|
||||
function AC3frameSizeLookup($frmsizecod, $fscod) {
|
||||
$padding = (bool) ($frmsizecod % 2);
|
||||
$framesizeid = floor($frmsizecod / 2);
|
||||
|
||||
static $AC3frameSizeLookup = array();
|
||||
if (empty($AC3frameSizeLookup)) {
|
||||
$AC3frameSizeLookup = array (
|
||||
0 => array(128, 138, 192),
|
||||
1 => array(40, 160, 174, 240),
|
||||
2 => array(48, 192, 208, 288),
|
||||
3 => array(56, 224, 242, 336),
|
||||
4 => array(64, 256, 278, 384),
|
||||
5 => array(80, 320, 348, 480),
|
||||
6 => array(96, 384, 416, 576),
|
||||
7 => array(112, 448, 486, 672),
|
||||
8 => array(128, 512, 556, 768),
|
||||
9 => array(160, 640, 696, 960),
|
||||
10 => array(192, 768, 834, 1152),
|
||||
11 => array(224, 896, 974, 1344),
|
||||
12 => array(256, 1024, 1114, 1536),
|
||||
13 => array(320, 1280, 1392, 1920),
|
||||
14 => array(384, 1536, 1670, 2304),
|
||||
15 => array(448, 1792, 1950, 2688),
|
||||
16 => array(512, 2048, 2228, 3072),
|
||||
17 => array(576, 2304, 2506, 3456),
|
||||
18 => array(640, 2560, 2786, 3840)
|
||||
);
|
||||
}
|
||||
if (($fscod == 1) && $padding) {
|
||||
// frame lengths are padded by 1 word (16 bits) at 44100
|
||||
$AC3frameSizeLookup[$frmsizecod] += 2;
|
||||
}
|
||||
return (isset($AC3frameSizeLookup[$framesizeid][$fscod]) ? $AC3frameSizeLookup[$framesizeid][$fscod] : false);
|
||||
}
|
||||
|
||||
function AC3bitrateLookup($frmsizecod) {
|
||||
$framesizeid = floor($frmsizecod / 2);
|
||||
|
||||
static $AC3bitrateLookup = array(
|
||||
0 => 32000,
|
||||
1 => 40000,
|
||||
2 => 48000,
|
||||
3 => 56000,
|
||||
4 => 64000,
|
||||
5 => 80000,
|
||||
6 => 96000,
|
||||
7 => 112000,
|
||||
8 => 128000,
|
||||
9 => 160000,
|
||||
10 => 192000,
|
||||
11 => 224000,
|
||||
12 => 256000,
|
||||
13 => 320000,
|
||||
14 => 384000,
|
||||
15 => 448000,
|
||||
16 => 512000,
|
||||
17 => 576000,
|
||||
18 => 640000
|
||||
);
|
||||
return (isset($AC3bitrateLookup[$framesizeid]) ? $AC3bitrateLookup[$framesizeid] : false);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,163 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.au.php //
|
||||
// module for analyzing AU files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_au
|
||||
{
|
||||
|
||||
function getid3_au(&$fd, &$ThisFileInfo) {
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$AUheader = fread($fd, 8);
|
||||
|
||||
if (substr($AUheader, 0, 4) != '.snd') {
|
||||
$ThisFileInfo['error'][] = 'Expecting ".snd" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($AUheader, 0, 4).'"';
|
||||
return false;
|
||||
}
|
||||
|
||||
// shortcut
|
||||
$ThisFileInfo['au'] = array();
|
||||
$thisfile_au = &$ThisFileInfo['au'];
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'au';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'au';
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
|
||||
$thisfile_au['encoding'] = 'ISO-8859-1';
|
||||
|
||||
$thisfile_au['header_length'] = getid3_lib::BigEndian2Int(substr($AUheader, 4, 4));
|
||||
$AUheader .= fread($fd, $thisfile_au['header_length'] - 8);
|
||||
$ThisFileInfo['avdataoffset'] += $thisfile_au['header_length'];
|
||||
|
||||
$thisfile_au['data_size'] = getid3_lib::BigEndian2Int(substr($AUheader, 8, 4));
|
||||
$thisfile_au['data_format_id'] = getid3_lib::BigEndian2Int(substr($AUheader, 12, 4));
|
||||
$thisfile_au['sample_rate'] = getid3_lib::BigEndian2Int(substr($AUheader, 16, 4));
|
||||
$thisfile_au['channels'] = getid3_lib::BigEndian2Int(substr($AUheader, 20, 4));
|
||||
$thisfile_au['comments']['comment'][] = trim(substr($AUheader, 24));
|
||||
|
||||
$thisfile_au['data_format'] = $this->AUdataFormatNameLookup($thisfile_au['data_format_id']);
|
||||
$thisfile_au['used_bits_per_sample'] = $this->AUdataFormatUsedBitsPerSampleLookup($thisfile_au['data_format_id']);
|
||||
if ($thisfile_au['bits_per_sample'] = $this->AUdataFormatBitsPerSampleLookup($thisfile_au['data_format_id'])) {
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample'];
|
||||
} else {
|
||||
unset($thisfile_au['bits_per_sample']);
|
||||
}
|
||||
|
||||
$ThisFileInfo['audio']['sample_rate'] = $thisfile_au['sample_rate'];
|
||||
$ThisFileInfo['audio']['channels'] = $thisfile_au['channels'];
|
||||
|
||||
if (($ThisFileInfo['avdataoffset'] + $thisfile_au['data_size']) > $ThisFileInfo['avdataend']) {
|
||||
$ThisFileInfo['warning'][] = 'Possible truncated file - expecting "'.$thisfile_au['data_size'].'" bytes of audio data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' bytes"';
|
||||
}
|
||||
|
||||
$ThisFileInfo['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8));
|
||||
$ThisFileInfo['audio']['bitrate'] = ($thisfile_au['data_size'] * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function AUdataFormatNameLookup($id) {
|
||||
static $AUdataFormatNameLookup = array(
|
||||
0 => 'unspecified format',
|
||||
1 => '8-bit mu-law',
|
||||
2 => '8-bit linear',
|
||||
3 => '16-bit linear',
|
||||
4 => '24-bit linear',
|
||||
5 => '32-bit linear',
|
||||
6 => 'floating-point',
|
||||
7 => 'double-precision float',
|
||||
8 => 'fragmented sampled data',
|
||||
9 => 'SUN_FORMAT_NESTED',
|
||||
10 => 'DSP program',
|
||||
11 => '8-bit fixed-point',
|
||||
12 => '16-bit fixed-point',
|
||||
13 => '24-bit fixed-point',
|
||||
14 => '32-bit fixed-point',
|
||||
|
||||
16 => 'non-audio display data',
|
||||
17 => 'SND_FORMAT_MULAW_SQUELCH',
|
||||
18 => '16-bit linear with emphasis',
|
||||
19 => '16-bit linear with compression',
|
||||
20 => '16-bit linear with emphasis + compression',
|
||||
21 => 'Music Kit DSP commands',
|
||||
22 => 'SND_FORMAT_DSP_COMMANDS_SAMPLES',
|
||||
23 => 'CCITT g.721 4-bit ADPCM',
|
||||
24 => 'CCITT g.722 ADPCM',
|
||||
25 => 'CCITT g.723 3-bit ADPCM',
|
||||
26 => 'CCITT g.723 5-bit ADPCM',
|
||||
27 => 'A-Law 8-bit'
|
||||
);
|
||||
return (isset($AUdataFormatNameLookup[$id]) ? $AUdataFormatNameLookup[$id] : false);
|
||||
}
|
||||
|
||||
function AUdataFormatBitsPerSampleLookup($id) {
|
||||
static $AUdataFormatBitsPerSampleLookup = array(
|
||||
1 => 8,
|
||||
2 => 8,
|
||||
3 => 16,
|
||||
4 => 24,
|
||||
5 => 32,
|
||||
6 => 32,
|
||||
7 => 64,
|
||||
|
||||
11 => 8,
|
||||
12 => 16,
|
||||
13 => 24,
|
||||
14 => 32,
|
||||
|
||||
18 => 16,
|
||||
19 => 16,
|
||||
20 => 16,
|
||||
|
||||
23 => 16,
|
||||
|
||||
25 => 16,
|
||||
26 => 16,
|
||||
27 => 8
|
||||
);
|
||||
return (isset($AUdataFormatBitsPerSampleLookup[$id]) ? $AUdataFormatBitsPerSampleLookup[$id] : false);
|
||||
}
|
||||
|
||||
function AUdataFormatUsedBitsPerSampleLookup($id) {
|
||||
static $AUdataFormatUsedBitsPerSampleLookup = array(
|
||||
1 => 8,
|
||||
2 => 8,
|
||||
3 => 16,
|
||||
4 => 24,
|
||||
5 => 32,
|
||||
6 => 32,
|
||||
7 => 64,
|
||||
|
||||
11 => 8,
|
||||
12 => 16,
|
||||
13 => 24,
|
||||
14 => 32,
|
||||
|
||||
18 => 16,
|
||||
19 => 16,
|
||||
20 => 16,
|
||||
|
||||
23 => 4,
|
||||
|
||||
25 => 3,
|
||||
26 => 5,
|
||||
27 => 8,
|
||||
);
|
||||
return (isset($AUdataFormatUsedBitsPerSampleLookup[$id]) ? $AUdataFormatUsedBitsPerSampleLookup[$id] : false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.avr.php //
|
||||
// module for analyzing AVR Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_avr
|
||||
{
|
||||
|
||||
function getid3_avr(&$fd, &$ThisFileInfo) {
|
||||
|
||||
// http://cui.unige.ch/OSG/info/AudioFormats/ap11.html
|
||||
// http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html
|
||||
// offset type length name comments
|
||||
// ---------------------------------------------------------------------
|
||||
// 0 char 4 ID format ID == "2BIT"
|
||||
// 4 char 8 name sample name (unused space filled with 0)
|
||||
// 12 short 1 mono/stereo 0=mono, -1 (0xFFFF)=stereo
|
||||
// With stereo, samples are alternated,
|
||||
// the first voice is the left :
|
||||
// (LRLRLRLRLRLRLRLRLR...)
|
||||
// 14 short 1 resolution 8, 12 or 16 (bits)
|
||||
// 16 short 1 signed or not 0=unsigned, -1 (0xFFFF)=signed
|
||||
// 18 short 1 loop or not 0=no loop, -1 (0xFFFF)=loop on
|
||||
// 20 short 1 MIDI note 0xFFnn, where 0 <= nn <= 127
|
||||
// 0xFFFF means "no MIDI note defined"
|
||||
// 22 byte 1 Replay speed Frequence in the Replay software
|
||||
// 0=5.485 Khz, 1=8.084 Khz, 2=10.971 Khz,
|
||||
// 3=16.168 Khz, 4=21.942 Khz, 5=32.336 Khz
|
||||
// 6=43.885 Khz, 7=47.261 Khz
|
||||
// -1 (0xFF)=no defined Frequence
|
||||
// 23 byte 3 sample rate in Hertz
|
||||
// 26 long 1 size in bytes (2 * bytes in stereo)
|
||||
// 30 long 1 loop begin 0 for no loop
|
||||
// 34 long 1 loop size equal to 'size' for no loop
|
||||
// 38 short 2 Reserved, MIDI keyboard split */
|
||||
// 40 short 2 Reserved, sample compression */
|
||||
// 42 short 2 Reserved */
|
||||
// 44 char 20; Additional filename space, used if (name[7] != 0)
|
||||
// 64 byte 64 user data
|
||||
// 128 bytes ? sample data (12 bits samples are coded on 16 bits:
|
||||
// 0000 xxxx xxxx xxxx)
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// Note that all values are in motorola (big-endian) format, and that long is
|
||||
// assumed to be 4 bytes, and short 2 bytes.
|
||||
// When reading the samples, you should handle both signed and unsigned data,
|
||||
// and be prepared to convert 16->8 bit, or mono->stereo if needed. To convert
|
||||
// 8-bit data between signed/unsigned just add 127 to the sample values.
|
||||
// Simularly for 16-bit data you should add 32769
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'avr';
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$AVRheader = fread($fd, 128);
|
||||
|
||||
$ThisFileInfo['avr']['raw']['magic'] = substr($AVRheader, 0, 4);
|
||||
if ($ThisFileInfo['avr']['raw']['magic'] != '2BIT') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "2BIT" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['avr']['raw']['magic'].'"';
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
unset($ThisFileInfo['avr']);
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['avdataoffset'] += 128;
|
||||
|
||||
$ThisFileInfo['avr']['sample_name'] = rtrim(substr($AVRheader, 4, 8));
|
||||
$ThisFileInfo['avr']['raw']['mono'] = getid3_lib::BigEndian2Int(substr($AVRheader, 12, 2));
|
||||
$ThisFileInfo['avr']['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($AVRheader, 14, 2));
|
||||
$ThisFileInfo['avr']['raw']['signed'] = getid3_lib::BigEndian2Int(substr($AVRheader, 16, 2));
|
||||
$ThisFileInfo['avr']['raw']['loop'] = getid3_lib::BigEndian2Int(substr($AVRheader, 18, 2));
|
||||
$ThisFileInfo['avr']['raw']['midi'] = getid3_lib::BigEndian2Int(substr($AVRheader, 20, 2));
|
||||
$ThisFileInfo['avr']['raw']['replay_freq'] = getid3_lib::BigEndian2Int(substr($AVRheader, 22, 1));
|
||||
$ThisFileInfo['avr']['sample_rate'] = getid3_lib::BigEndian2Int(substr($AVRheader, 23, 3));
|
||||
$ThisFileInfo['avr']['sample_length'] = getid3_lib::BigEndian2Int(substr($AVRheader, 26, 4));
|
||||
$ThisFileInfo['avr']['loop_start'] = getid3_lib::BigEndian2Int(substr($AVRheader, 30, 4));
|
||||
$ThisFileInfo['avr']['loop_end'] = getid3_lib::BigEndian2Int(substr($AVRheader, 34, 4));
|
||||
$ThisFileInfo['avr']['midi_split'] = getid3_lib::BigEndian2Int(substr($AVRheader, 38, 2));
|
||||
$ThisFileInfo['avr']['sample_compression'] = getid3_lib::BigEndian2Int(substr($AVRheader, 40, 2));
|
||||
$ThisFileInfo['avr']['reserved'] = getid3_lib::BigEndian2Int(substr($AVRheader, 42, 2));
|
||||
$ThisFileInfo['avr']['sample_name_extra'] = rtrim(substr($AVRheader, 44, 20));
|
||||
$ThisFileInfo['avr']['comment'] = rtrim(substr($AVRheader, 64, 64));
|
||||
|
||||
$ThisFileInfo['avr']['flags']['stereo'] = (($ThisFileInfo['avr']['raw']['mono'] == 0) ? false : true);
|
||||
$ThisFileInfo['avr']['flags']['signed'] = (($ThisFileInfo['avr']['raw']['signed'] == 0) ? false : true);
|
||||
$ThisFileInfo['avr']['flags']['loop'] = (($ThisFileInfo['avr']['raw']['loop'] == 0) ? false : true);
|
||||
|
||||
$ThisFileInfo['avr']['midi_notes'] = array();
|
||||
if (($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) != 0xFF00) {
|
||||
$ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) >> 8;
|
||||
}
|
||||
if (($ThisFileInfo['avr']['raw']['midi'] & 0x00FF) != 0x00FF) {
|
||||
$ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0x00FF);
|
||||
}
|
||||
|
||||
if (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) != ($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 1 : 2))) {
|
||||
$ThisFileInfo['warning'][] = 'Probable truncated file: expecting '.($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 1 : 2)).' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']);
|
||||
}
|
||||
|
||||
$ThisFileInfo['audio']['dataformat'] = 'avr';
|
||||
$ThisFileInfo['audio']['lossless'] = true;
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['avr']['bits_per_sample'];
|
||||
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['avr']['sample_rate'];
|
||||
$ThisFileInfo['audio']['channels'] = ($ThisFileInfo['avr']['flags']['stereo'] ? 2 : 1);
|
||||
$ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avr']['sample_length'] / $ThisFileInfo['audio']['channels']) / $ThisFileInfo['avr']['sample_rate'];
|
||||
$ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 8 : 16)) / $ThisFileInfo['playtime_seconds'];
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,221 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.la.php //
|
||||
// module for analyzing BONK audio files //
|
||||
// dependencies: module.tag.id3v2.php (optional) //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_bonk
|
||||
{
|
||||
function getid3_bonk(&$fd, &$ThisFileInfo) {
|
||||
|
||||
// shortcut
|
||||
$ThisFileInfo['bonk'] = array();
|
||||
$thisfile_bonk = &$ThisFileInfo['bonk'];
|
||||
|
||||
$thisfile_bonk['dataoffset'] = $ThisFileInfo['avdataoffset'];
|
||||
$thisfile_bonk['dataend'] = $ThisFileInfo['avdataend'];
|
||||
|
||||
if ($thisfile_bonk['dataend'] >= pow(2, 31)) {
|
||||
|
||||
$ThisFileInfo['warning'][] = 'Unable to parse BONK file from end (v0.6+ preferred method) because PHP filesystem functions only support up to 2GB';
|
||||
|
||||
} else {
|
||||
|
||||
// scan-from-end method, for v0.6 and higher
|
||||
fseek($fd, $thisfile_bonk['dataend'] - 8, SEEK_SET);
|
||||
$PossibleBonkTag = fread($fd, 8);
|
||||
while ($this->BonkIsValidTagName(substr($PossibleBonkTag, 4, 4), true)) {
|
||||
$BonkTagSize = getid3_lib::LittleEndian2Int(substr($PossibleBonkTag, 0, 4));
|
||||
fseek($fd, 0 - $BonkTagSize, SEEK_CUR);
|
||||
$BonkTagOffset = ftell($fd);
|
||||
$TagHeaderTest = fread($fd, 5);
|
||||
if (($TagHeaderTest{0} != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) {
|
||||
$ThisFileInfo['error'][] = 'Expecting "Ø'.strtoupper(substr($PossibleBonkTag, 4, 4)).'" at offset '.$BonkTagOffset.', found "'.$TagHeaderTest.'"';
|
||||
return false;
|
||||
}
|
||||
$BonkTagName = substr($TagHeaderTest, 1, 4);
|
||||
|
||||
$thisfile_bonk[$BonkTagName]['size'] = $BonkTagSize;
|
||||
$thisfile_bonk[$BonkTagName]['offset'] = $BonkTagOffset;
|
||||
$this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo);
|
||||
$NextTagEndOffset = $BonkTagOffset - 8;
|
||||
if ($NextTagEndOffset < $thisfile_bonk['dataoffset']) {
|
||||
if (empty($ThisFileInfo['audio']['encoder'])) {
|
||||
$ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
fseek($fd, $NextTagEndOffset, SEEK_SET);
|
||||
$PossibleBonkTag = fread($fd, 8);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// seek-from-beginning method for v0.4 and v0.5
|
||||
if (empty($thisfile_bonk['BONK'])) {
|
||||
fseek($fd, $thisfile_bonk['dataoffset'], SEEK_SET);
|
||||
do {
|
||||
$TagHeaderTest = fread($fd, 5);
|
||||
switch ($TagHeaderTest) {
|
||||
case "\x00".'BONK':
|
||||
if (empty($ThisFileInfo['audio']['encoder'])) {
|
||||
$ThisFileInfo['audio']['encoder'] = 'BONK v0.4';
|
||||
}
|
||||
break;
|
||||
|
||||
case "\x00".'INFO':
|
||||
$ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.5';
|
||||
break;
|
||||
|
||||
default:
|
||||
break 2;
|
||||
}
|
||||
$BonkTagName = substr($TagHeaderTest, 1, 4);
|
||||
$thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset'];
|
||||
$thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset'];
|
||||
$this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo);
|
||||
|
||||
} while (true);
|
||||
}
|
||||
|
||||
// parse META block for v0.6 - v0.8
|
||||
if (empty($thisfile_bonk['INFO']) && isset($thisfile_bonk['META']['tags']['info'])) {
|
||||
fseek($fd, $thisfile_bonk['META']['tags']['info'], SEEK_SET);
|
||||
$TagHeaderTest = fread($fd, 5);
|
||||
if ($TagHeaderTest == "\x00".'INFO') {
|
||||
$ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.6 - v0.8';
|
||||
|
||||
$BonkTagName = substr($TagHeaderTest, 1, 4);
|
||||
$thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset'];
|
||||
$thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset'];
|
||||
$this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($ThisFileInfo['audio']['encoder'])) {
|
||||
$ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+';
|
||||
}
|
||||
if (empty($thisfile_bonk['BONK'])) {
|
||||
unset($ThisFileInfo['bonk']);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
function HandleBonkTags(&$fd, &$BonkTagName, &$ThisFileInfo) {
|
||||
|
||||
switch ($BonkTagName) {
|
||||
case 'BONK':
|
||||
// shortcut
|
||||
$thisfile_bonk_BONK = &$ThisFileInfo['bonk']['BONK'];
|
||||
|
||||
$BonkData = "\x00".'BONK'.fread($fd, 17);
|
||||
$thisfile_bonk_BONK['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1));
|
||||
$thisfile_bonk_BONK['number_samples'] = getid3_lib::LittleEndian2Int(substr($BonkData, 6, 4));
|
||||
$thisfile_bonk_BONK['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BonkData, 10, 4));
|
||||
|
||||
$thisfile_bonk_BONK['channels'] = getid3_lib::LittleEndian2Int(substr($BonkData, 14, 1));
|
||||
$thisfile_bonk_BONK['lossless'] = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 15, 1));
|
||||
$thisfile_bonk_BONK['joint_stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 16, 1));
|
||||
$thisfile_bonk_BONK['number_taps'] = getid3_lib::LittleEndian2Int(substr($BonkData, 17, 2));
|
||||
$thisfile_bonk_BONK['downsampling_ratio'] = getid3_lib::LittleEndian2Int(substr($BonkData, 19, 1));
|
||||
$thisfile_bonk_BONK['samples_per_packet'] = getid3_lib::LittleEndian2Int(substr($BonkData, 20, 2));
|
||||
|
||||
$ThisFileInfo['avdataoffset'] = $thisfile_bonk_BONK['offset'] + 5 + 17;
|
||||
$ThisFileInfo['avdataend'] = $thisfile_bonk_BONK['offset'] + $thisfile_bonk_BONK['size'];
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'bonk';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'bonk';
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; // assumed
|
||||
$ThisFileInfo['audio']['channels'] = $thisfile_bonk_BONK['channels'];
|
||||
$ThisFileInfo['audio']['sample_rate'] = $thisfile_bonk_BONK['sample_rate'];
|
||||
$ThisFileInfo['audio']['channelmode'] = ($thisfile_bonk_BONK['joint_stereo'] ? 'joint stereo' : 'stereo');
|
||||
$ThisFileInfo['audio']['lossless'] = $thisfile_bonk_BONK['lossless'];
|
||||
$ThisFileInfo['audio']['codec'] = 'bonk';
|
||||
|
||||
$ThisFileInfo['playtime_seconds'] = $thisfile_bonk_BONK['number_samples'] / ($thisfile_bonk_BONK['sample_rate'] * $thisfile_bonk_BONK['channels']);
|
||||
if ($ThisFileInfo['playtime_seconds'] > 0) {
|
||||
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['bonk']['dataend'] - $ThisFileInfo['bonk']['dataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'INFO':
|
||||
// shortcut
|
||||
$thisfile_bonk_INFO = &$ThisFileInfo['bonk']['INFO'];
|
||||
|
||||
$thisfile_bonk_INFO['version'] = getid3_lib::LittleEndian2Int(fread($fd, 1));
|
||||
$thisfile_bonk_INFO['entries_count'] = 0;
|
||||
$NextInfoDataPair = fread($fd, 5);
|
||||
if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
|
||||
while (!feof($fd)) {
|
||||
//$CurrentSeekInfo['offset'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 0, 4));
|
||||
//$CurrentSeekInfo['nextbit'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 4, 1));
|
||||
//$thisfile_bonk_INFO[] = $CurrentSeekInfo;
|
||||
|
||||
$NextInfoDataPair = fread($fd, 5);
|
||||
if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
|
||||
fseek($fd, -5, SEEK_CUR);
|
||||
break;
|
||||
}
|
||||
$thisfile_bonk_INFO['entries_count']++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'META':
|
||||
$BonkData = "\x00".'META'.fread($fd, $ThisFileInfo['bonk']['META']['size'] - 5);
|
||||
$ThisFileInfo['bonk']['META']['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1));
|
||||
|
||||
$MetaTagEntries = floor(((strlen($BonkData) - 8) - 6) / 8); // BonkData - xxxxmeta - ØMETA
|
||||
$offset = 6;
|
||||
for ($i = 0; $i < $MetaTagEntries; $i++) {
|
||||
$MetaEntryTagName = substr($BonkData, $offset, 4);
|
||||
$offset += 4;
|
||||
$MetaEntryTagOffset = getid3_lib::LittleEndian2Int(substr($BonkData, $offset, 4));
|
||||
$offset += 4;
|
||||
$ThisFileInfo['bonk']['META']['tags'][$MetaEntryTagName] = $MetaEntryTagOffset;
|
||||
}
|
||||
break;
|
||||
|
||||
case ' ID3':
|
||||
$ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+';
|
||||
|
||||
// ID3v2 checking is optional
|
||||
if (class_exists('getid3_id3v2')) {
|
||||
$ThisFileInfo['bonk'][' ID3']['valid'] = new getid3_id3v2($fd, $ThisFileInfo, $ThisFileInfo['bonk'][' ID3']['offset'] + 2);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['warning'][] = 'Unexpected Bonk tag "'.$BonkTagName.'" at offset '.$ThisFileInfo['bonk'][$BonkTagName]['offset'];
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function BonkIsValidTagName($PossibleBonkTag, $ignorecase=false) {
|
||||
static $BonkIsValidTagName = array('BONK', 'INFO', ' ID3', 'META');
|
||||
foreach ($BonkIsValidTagName as $validtagname) {
|
||||
if ($validtagname == $PossibleBonkTag) {
|
||||
return true;
|
||||
} elseif ($ignorecase && (strtolower($validtagname) == strtolower($PossibleBonkTag))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.au.php //
|
||||
// module for analyzing Digital Speech Standard (DSS) files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_dss
|
||||
{
|
||||
|
||||
function getid3_dss(&$fd, &$ThisFileInfo) {
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$DSSheader = fread($fd, 1256);
|
||||
|
||||
if (substr($DSSheader, 0, 4) != "\x02".'dss') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "[x02]dss" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($DSSheader, 0, 4).'"';
|
||||
return false;
|
||||
}
|
||||
|
||||
// some structure information taken from http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
|
||||
|
||||
// shortcut
|
||||
$ThisFileInfo['dss'] = array();
|
||||
$thisfile_dss = &$ThisFileInfo['dss'];
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'dss';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'dss';
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
|
||||
//$thisfile_dss['encoding'] = 'ISO-8859-1';
|
||||
|
||||
$thisfile_dss['date_create'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 38, 12));
|
||||
$thisfile_dss['date_complete'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 50, 12));
|
||||
$thisfile_dss['length'] = intval(substr($DSSheader, 62, 6));
|
||||
$thisfile_dss['priority'] = ord(substr($DSSheader, 793, 1));
|
||||
$thisfile_dss['comments'] = trim(substr($DSSheader, 798, 100));
|
||||
|
||||
|
||||
//$ThisFileInfo['audio']['bits_per_sample'] = ?;
|
||||
//$ThisFileInfo['audio']['sample_rate'] = ?;
|
||||
$ThisFileInfo['audio']['channels'] = 1;
|
||||
|
||||
$ThisFileInfo['playtime_seconds'] = $thisfile_dss['length'];
|
||||
$ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['filesize'] * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function DSSdateStringToUnixDate($datestring) {
|
||||
$y = substr($datestring, 0, 2);
|
||||
$m = substr($datestring, 2, 2);
|
||||
$d = substr($datestring, 4, 2);
|
||||
$h = substr($datestring, 6, 2);
|
||||
$i = substr($datestring, 8, 2);
|
||||
$s = substr($datestring, 10, 2);
|
||||
$y += (($y < 95) ? 2000 : 1900);
|
||||
return mktime($h, $i, $s, $m, $d, $y);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,239 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.dts.php //
|
||||
// module for analyzing DTS Audio files //
|
||||
// dependencies: NONE //
|
||||
// //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_dts
|
||||
{
|
||||
|
||||
function getid3_dts(&$fd, &$ThisFileInfo) {
|
||||
// Specs taken from "DTS Coherent Acoustics;Core and Extensions, ETSI TS 102 114 V1.2.1 (2002-12)"
|
||||
// (http://pda.etsi.org/pda/queryform.asp)
|
||||
// With thanks to Gambit <macteam@users.sourceforge.net> http://mac.sourceforge.net/atl/
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'dts';
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$DTSheader = fread($fd, 16);
|
||||
|
||||
$ThisFileInfo['dts']['raw']['magic'] = getid3_lib::BigEndian2Int(substr($DTSheader, 0, 4));
|
||||
if ($ThisFileInfo['dts']['raw']['magic'] != 0x7FFE8001) {
|
||||
$ThisFileInfo['error'][] = 'Expecting "0x7FFE8001" at offset '.$ThisFileInfo['avdataoffset'].', found "0x'.str_pad(strtoupper(dechex($ThisFileInfo['dts']['raw']['magic'])), 8, '0', STR_PAD_LEFT).'"';
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
unset($ThisFileInfo['dts']);
|
||||
return false;
|
||||
}
|
||||
|
||||
$fhBS = getid3_lib::BigEndian2Bin(substr($DTSheader, 4, 12));
|
||||
$bsOffset = 0;
|
||||
$ThisFileInfo['dts']['raw']['frame_type'] = bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
|
||||
$ThisFileInfo['dts']['raw']['deficit_samples'] = bindec(substr($fhBS, $bsOffset, 5)); $bsOffset += 5;
|
||||
$ThisFileInfo['dts']['flags']['crc_present'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
|
||||
$ThisFileInfo['dts']['raw']['pcm_sample_blocks'] = bindec(substr($fhBS, $bsOffset, 7)); $bsOffset += 7;
|
||||
$ThisFileInfo['dts']['raw']['frame_byte_size'] = bindec(substr($fhBS, $bsOffset, 14)); $bsOffset += 14;
|
||||
$ThisFileInfo['dts']['raw']['channel_arrangement'] = bindec(substr($fhBS, $bsOffset, 6)); $bsOffset += 6;
|
||||
$ThisFileInfo['dts']['raw']['sample_frequency'] = bindec(substr($fhBS, $bsOffset, 4)); $bsOffset += 4;
|
||||
$ThisFileInfo['dts']['raw']['bitrate'] = bindec(substr($fhBS, $bsOffset, 5)); $bsOffset += 5;
|
||||
$ThisFileInfo['dts']['flags']['embedded_downmix'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
|
||||
$ThisFileInfo['dts']['flags']['dynamicrange'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
|
||||
$ThisFileInfo['dts']['flags']['timestamp'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
|
||||
$ThisFileInfo['dts']['flags']['auxdata'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
|
||||
$ThisFileInfo['dts']['flags']['hdcd'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
|
||||
$ThisFileInfo['dts']['raw']['extension_audio'] = bindec(substr($fhBS, $bsOffset, 3)); $bsOffset += 3;
|
||||
$ThisFileInfo['dts']['flags']['extended_coding'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
|
||||
$ThisFileInfo['dts']['flags']['audio_sync_insertion'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
|
||||
$ThisFileInfo['dts']['raw']['lfe_effects'] = bindec(substr($fhBS, $bsOffset, 2)); $bsOffset += 2;
|
||||
$ThisFileInfo['dts']['flags']['predictor_history'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
|
||||
if ($ThisFileInfo['dts']['flags']['crc_present']) {
|
||||
$ThisFileInfo['dts']['raw']['crc16'] = bindec(substr($fhBS, $bsOffset, 16)); $bsOffset += 16;
|
||||
}
|
||||
$ThisFileInfo['dts']['flags']['mri_perfect_reconst'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
|
||||
$ThisFileInfo['dts']['raw']['encoder_soft_version'] = bindec(substr($fhBS, $bsOffset, 4)); $bsOffset += 4;
|
||||
$ThisFileInfo['dts']['raw']['copy_history'] = bindec(substr($fhBS, $bsOffset, 2)); $bsOffset += 2;
|
||||
$ThisFileInfo['dts']['raw']['bits_per_sample'] = bindec(substr($fhBS, $bsOffset, 2)); $bsOffset += 2;
|
||||
$ThisFileInfo['dts']['flags']['surround_es'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
|
||||
$ThisFileInfo['dts']['flags']['front_sum_diff'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
|
||||
$ThisFileInfo['dts']['flags']['surround_sum_diff'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
|
||||
$ThisFileInfo['dts']['raw']['dialog_normalization'] = bindec(substr($fhBS, $bsOffset, 4)); $bsOffset += 4;
|
||||
|
||||
|
||||
$ThisFileInfo['dts']['bitrate'] = $this->DTSbitrateLookup($ThisFileInfo['dts']['raw']['bitrate']);
|
||||
$ThisFileInfo['dts']['bits_per_sample'] = $this->DTSbitPerSampleLookup($ThisFileInfo['dts']['raw']['bits_per_sample']);
|
||||
$ThisFileInfo['dts']['sample_rate'] = $this->DTSsampleRateLookup($ThisFileInfo['dts']['raw']['sample_frequency']);
|
||||
$ThisFileInfo['dts']['dialog_normalization'] = $this->DTSdialogNormalization($ThisFileInfo['dts']['raw']['dialog_normalization'], $ThisFileInfo['dts']['raw']['encoder_soft_version']);
|
||||
$ThisFileInfo['dts']['flags']['lossless'] = (($ThisFileInfo['dts']['raw']['bitrate'] == 31) ? true : false);
|
||||
$ThisFileInfo['dts']['bitrate_mode'] = (($ThisFileInfo['dts']['raw']['bitrate'] == 30) ? 'vbr' : 'cbr');
|
||||
$ThisFileInfo['dts']['channels'] = $this->DTSnumChannelsLookup($ThisFileInfo['dts']['raw']['channel_arrangement']);
|
||||
$ThisFileInfo['dts']['channel_arrangement'] = $this->DTSchannelArrangementLookup($ThisFileInfo['dts']['raw']['channel_arrangement']);
|
||||
|
||||
$ThisFileInfo['audio']['dataformat'] = 'dts';
|
||||
$ThisFileInfo['audio']['lossless'] = $ThisFileInfo['dts']['flags']['lossless'];
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['dts']['bitrate_mode'];
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['dts']['bits_per_sample'];
|
||||
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['dts']['sample_rate'];
|
||||
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['dts']['channels'];
|
||||
$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['dts']['bitrate'];
|
||||
if (isset($ThisFileInfo['avdataend'])) {
|
||||
$ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / ($ThisFileInfo['dts']['bitrate'] / 8);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function DTSbitrateLookup($index) {
|
||||
$DTSbitrateLookup = array(
|
||||
0 => 32000,
|
||||
1 => 56000,
|
||||
2 => 64000,
|
||||
3 => 96000,
|
||||
4 => 112000,
|
||||
5 => 128000,
|
||||
6 => 192000,
|
||||
7 => 224000,
|
||||
8 => 256000,
|
||||
9 => 320000,
|
||||
10 => 384000,
|
||||
11 => 448000,
|
||||
12 => 512000,
|
||||
13 => 576000,
|
||||
14 => 640000,
|
||||
15 => 768000,
|
||||
16 => 960000,
|
||||
17 => 1024000,
|
||||
18 => 1152000,
|
||||
19 => 1280000,
|
||||
20 => 1344000,
|
||||
21 => 1408000,
|
||||
22 => 1411200,
|
||||
23 => 1472000,
|
||||
24 => 1536000,
|
||||
25 => 1920000,
|
||||
26 => 2048000,
|
||||
27 => 3072000,
|
||||
28 => 3840000,
|
||||
29 => 'open',
|
||||
30 => 'variable',
|
||||
31 => 'lossless'
|
||||
);
|
||||
return @$DTSbitrateLookup[$index];
|
||||
}
|
||||
|
||||
function DTSsampleRateLookup($index) {
|
||||
$DTSsampleRateLookup = array(
|
||||
0 => 'invalid',
|
||||
1 => 8000,
|
||||
2 => 16000,
|
||||
3 => 32000,
|
||||
4 => 'invalid',
|
||||
5 => 'invalid',
|
||||
6 => 11025,
|
||||
7 => 22050,
|
||||
8 => 44100,
|
||||
9 => 'invalid',
|
||||
10 => 'invalid',
|
||||
11 => 12000,
|
||||
12 => 24000,
|
||||
13 => 48000,
|
||||
14 => 'invalid',
|
||||
15 => 'invalid'
|
||||
);
|
||||
return @$DTSsampleRateLookup[$index];
|
||||
}
|
||||
|
||||
function DTSbitPerSampleLookup($index) {
|
||||
$DTSbitPerSampleLookup = array(
|
||||
0 => 16,
|
||||
1 => 20,
|
||||
2 => 24,
|
||||
3 => 24,
|
||||
);
|
||||
return @$DTSbitPerSampleLookup[$index];
|
||||
}
|
||||
|
||||
function DTSnumChannelsLookup($index) {
|
||||
switch ($index) {
|
||||
case 0:
|
||||
return 1;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
return 2;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
return 3;
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
return 4;
|
||||
break;
|
||||
case 9:
|
||||
return 5;
|
||||
break;
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
return 6;
|
||||
break;
|
||||
case 13:
|
||||
return 7;
|
||||
break;
|
||||
case 14:
|
||||
case 15:
|
||||
return 8;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function DTSchannelArrangementLookup($index) {
|
||||
$DTSchannelArrangementLookup = array(
|
||||
0 => 'A',
|
||||
1 => 'A + B (dual mono)',
|
||||
2 => 'L + R (stereo)',
|
||||
3 => '(L+R) + (L-R) (sum-difference)',
|
||||
4 => 'LT + RT (left and right total)',
|
||||
5 => 'C + L + R',
|
||||
6 => 'L + R + S',
|
||||
7 => 'C + L + R + S',
|
||||
8 => 'L + R + SL + SR',
|
||||
9 => 'C + L + R + SL + SR',
|
||||
10 => 'CL + CR + L + R + SL + SR',
|
||||
11 => 'C + L + R+ LR + RR + OV',
|
||||
12 => 'CF + CR + LF + RF + LR + RR',
|
||||
13 => 'CL + C + CR + L + R + SL + SR',
|
||||
14 => 'CL + CR + L + R + SL1 + SL2 + SR1 + SR2',
|
||||
15 => 'CL + C+ CR + L + R + SL + S + SR',
|
||||
);
|
||||
return (@$DTSchannelArrangementLookup[$index] ? @$DTSchannelArrangementLookup[$index] : 'user-defined');
|
||||
}
|
||||
|
||||
function DTSdialogNormalization($index, $version) {
|
||||
switch ($version) {
|
||||
case 7:
|
||||
return 0 - $index;
|
||||
break;
|
||||
case 6:
|
||||
return 0 - 16 - $index;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,397 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.flac.php //
|
||||
// module for analyzing FLAC and OggFLAC audio files //
|
||||
// dependencies: module.audio.ogg.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
|
||||
|
||||
class getid3_flac
|
||||
{
|
||||
|
||||
function getid3_flac(&$fd, &$ThisFileInfo) {
|
||||
// http://flac.sourceforge.net/format.html
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$StreamMarker = fread($fd, 4);
|
||||
if ($StreamMarker != 'fLaC') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "fLaC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$StreamMarker.'"';
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['fileformat'] = 'flac';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'flac';
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
|
||||
$ThisFileInfo['audio']['lossless'] = true;
|
||||
|
||||
return getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo);
|
||||
}
|
||||
|
||||
|
||||
function FLACparseMETAdata(&$fd, &$ThisFileInfo) {
|
||||
|
||||
do {
|
||||
$METAdataBlockOffset = ftell($fd);
|
||||
$METAdataBlockHeader = fread($fd, 4);
|
||||
$METAdataLastBlockFlag = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x80);
|
||||
$METAdataBlockType = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x7F;
|
||||
$METAdataBlockLength = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 1, 3));
|
||||
$METAdataBlockTypeText = getid3_flac::FLACmetaBlockTypeLookup($METAdataBlockType);
|
||||
|
||||
if ($METAdataBlockLength < 0) {
|
||||
$ThisFileInfo['error'][] = 'corrupt or invalid METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset;
|
||||
break;
|
||||
}
|
||||
|
||||
$ThisFileInfo['flac'][$METAdataBlockTypeText]['raw'] = array();
|
||||
$ThisFileInfo_flac_METAdataBlockTypeText_raw = &$ThisFileInfo['flac'][$METAdataBlockTypeText]['raw'];
|
||||
|
||||
$ThisFileInfo_flac_METAdataBlockTypeText_raw['offset'] = $METAdataBlockOffset;
|
||||
$ThisFileInfo_flac_METAdataBlockTypeText_raw['last_meta_block'] = $METAdataLastBlockFlag;
|
||||
$ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type'] = $METAdataBlockType;
|
||||
$ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type_text'] = $METAdataBlockTypeText;
|
||||
$ThisFileInfo_flac_METAdataBlockTypeText_raw['block_length'] = $METAdataBlockLength;
|
||||
$ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'] = @fread($fd, $METAdataBlockLength);
|
||||
$ThisFileInfo['avdataoffset'] = ftell($fd);
|
||||
|
||||
switch ($METAdataBlockTypeText) {
|
||||
|
||||
case 'STREAMINFO':
|
||||
if (!getid3_flac::FLACparseSTREAMINFO($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'PADDING':
|
||||
// ignore
|
||||
break;
|
||||
|
||||
case 'APPLICATION':
|
||||
if (!getid3_flac::FLACparseAPPLICATION($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'SEEKTABLE':
|
||||
if (!getid3_flac::FLACparseSEEKTABLE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'VORBIS_COMMENT':
|
||||
$OldOffset = ftell($fd);
|
||||
fseek($fd, 0 - $METAdataBlockLength, SEEK_CUR);
|
||||
getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
|
||||
fseek($fd, $OldOffset, SEEK_SET);
|
||||
break;
|
||||
|
||||
case 'CUESHEET':
|
||||
if (!getid3_flac::FLACparseCUESHEET($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'PICTURE':
|
||||
if (!$this->FLACparsePICTURE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['warning'][] = 'Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset;
|
||||
break;
|
||||
}
|
||||
|
||||
} while ($METAdataLastBlockFlag === false);
|
||||
|
||||
|
||||
if (isset($ThisFileInfo['flac']['STREAMINFO'])) {
|
||||
$ThisFileInfo['flac']['compressed_audio_bytes'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'];
|
||||
$ThisFileInfo['flac']['uncompressed_audio_bytes'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] * $ThisFileInfo['flac']['STREAMINFO']['channels'] * ($ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] / 8);
|
||||
if ($ThisFileInfo['flac']['uncompressed_audio_bytes'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt FLAC file: uncompressed_audio_bytes == zero';
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['flac']['compression_ratio'] = $ThisFileInfo['flac']['compressed_audio_bytes'] / $ThisFileInfo['flac']['uncompressed_audio_bytes'];
|
||||
}
|
||||
|
||||
// set md5_data_source - built into flac 0.5+
|
||||
if (isset($ThisFileInfo['flac']['STREAMINFO']['audio_signature'])) {
|
||||
|
||||
if ($ThisFileInfo['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
|
||||
|
||||
$ThisFileInfo['warning'][] = 'FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)';
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['md5_data_source'] = '';
|
||||
$md5 = $ThisFileInfo['flac']['STREAMINFO']['audio_signature'];
|
||||
for ($i = 0; $i < strlen($md5); $i++) {
|
||||
$ThisFileInfo['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
|
||||
}
|
||||
if (!preg_match('/^[0-9a-f]{32}$/', $ThisFileInfo['md5_data_source'])) {
|
||||
unset($ThisFileInfo['md5_data_source']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'];
|
||||
if ($ThisFileInfo['audio']['bits_per_sample'] == 8) {
|
||||
// special case
|
||||
// must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value
|
||||
// MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed
|
||||
$ThisFileInfo['warning'][] = 'FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file';
|
||||
}
|
||||
if (!empty($ThisFileInfo['ogg']['vendor'])) {
|
||||
$ThisFileInfo['audio']['encoder'] = $ThisFileInfo['ogg']['vendor'];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function FLACmetaBlockTypeLookup($blocktype) {
|
||||
static $FLACmetaBlockTypeLookup = array();
|
||||
if (empty($FLACmetaBlockTypeLookup)) {
|
||||
$FLACmetaBlockTypeLookup[0] = 'STREAMINFO';
|
||||
$FLACmetaBlockTypeLookup[1] = 'PADDING';
|
||||
$FLACmetaBlockTypeLookup[2] = 'APPLICATION';
|
||||
$FLACmetaBlockTypeLookup[3] = 'SEEKTABLE';
|
||||
$FLACmetaBlockTypeLookup[4] = 'VORBIS_COMMENT';
|
||||
$FLACmetaBlockTypeLookup[5] = 'CUESHEET';
|
||||
$FLACmetaBlockTypeLookup[6] = 'PICTURE';
|
||||
}
|
||||
return (isset($FLACmetaBlockTypeLookup[$blocktype]) ? $FLACmetaBlockTypeLookup[$blocktype] : 'reserved');
|
||||
}
|
||||
|
||||
function FLACapplicationIDLookup($applicationid) {
|
||||
static $FLACapplicationIDLookup = array();
|
||||
if (empty($FLACapplicationIDLookup)) {
|
||||
// http://flac.sourceforge.net/id.html
|
||||
$FLACapplicationIDLookup[0x46746F6C] = 'flac-tools'; // 'Ftol'
|
||||
$FLACapplicationIDLookup[0x46746F6C] = 'Sound Font FLAC'; // 'SFFL'
|
||||
}
|
||||
return (isset($FLACapplicationIDLookup[$applicationid]) ? $FLACapplicationIDLookup[$applicationid] : 'reserved');
|
||||
}
|
||||
|
||||
function FLACpictureTypeLookup($type_id) {
|
||||
static $lookup = array (
|
||||
0 => 'Other',
|
||||
1 => '32x32 pixels \'file icon\' (PNG only)',
|
||||
2 => 'Other file icon',
|
||||
3 => 'Cover (front)',
|
||||
4 => 'Cover (back)',
|
||||
5 => 'Leaflet page',
|
||||
6 => 'Media (e.g. label side of CD)',
|
||||
7 => 'Lead artist/lead performer/soloist',
|
||||
8 => 'Artist/performer',
|
||||
9 => 'Conductor',
|
||||
10 => 'Band/Orchestra',
|
||||
11 => 'Composer',
|
||||
12 => 'Lyricist/text writer',
|
||||
13 => 'Recording Location',
|
||||
14 => 'During recording',
|
||||
15 => 'During performance',
|
||||
16 => 'Movie/video screen capture',
|
||||
17 => 'A bright coloured fish',
|
||||
18 => 'Illustration',
|
||||
19 => 'Band/artist logotype',
|
||||
20 => 'Publisher/Studio logotype',
|
||||
);
|
||||
return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved');
|
||||
}
|
||||
|
||||
function FLACparseSTREAMINFO($METAdataBlockData, &$ThisFileInfo) {
|
||||
$offset = 0;
|
||||
$ThisFileInfo['flac']['STREAMINFO']['min_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
|
||||
$offset += 2;
|
||||
$ThisFileInfo['flac']['STREAMINFO']['max_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
|
||||
$offset += 2;
|
||||
$ThisFileInfo['flac']['STREAMINFO']['min_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3));
|
||||
$offset += 3;
|
||||
$ThisFileInfo['flac']['STREAMINFO']['max_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3));
|
||||
$offset += 3;
|
||||
|
||||
$SampleRateChannelsSampleBitsStreamSamples = getid3_lib::BigEndian2Bin(substr($METAdataBlockData, $offset, 8));
|
||||
$ThisFileInfo['flac']['STREAMINFO']['sample_rate'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 0, 20));
|
||||
$ThisFileInfo['flac']['STREAMINFO']['channels'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 20, 3)) + 1;
|
||||
$ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 23, 5)) + 1;
|
||||
$ThisFileInfo['flac']['STREAMINFO']['samples_stream'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 28, 36));
|
||||
$offset += 8;
|
||||
|
||||
$ThisFileInfo['flac']['STREAMINFO']['audio_signature'] = substr($METAdataBlockData, $offset, 16);
|
||||
$offset += 16;
|
||||
|
||||
if (!empty($ThisFileInfo['flac']['STREAMINFO']['sample_rate'])) {
|
||||
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
|
||||
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['flac']['STREAMINFO']['sample_rate'];
|
||||
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['flac']['STREAMINFO']['channels'];
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'];
|
||||
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] / $ThisFileInfo['flac']['STREAMINFO']['sample_rate'];
|
||||
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['error'][] = 'Corrupt METAdata block: STREAMINFO';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
unset($ThisFileInfo['flac']['STREAMINFO']['raw']);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function FLACparseAPPLICATION($METAdataBlockData, &$ThisFileInfo) {
|
||||
$offset = 0;
|
||||
$ApplicationID = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 4));
|
||||
$offset += 4;
|
||||
$ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['name'] = getid3_flac::FLACapplicationIDLookup($ApplicationID);
|
||||
$ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['data'] = substr($METAdataBlockData, $offset);
|
||||
$offset = $METAdataBlockLength;
|
||||
|
||||
unset($ThisFileInfo['flac']['APPLICATION']['raw']);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function FLACparseSEEKTABLE($METAdataBlockData, &$ThisFileInfo) {
|
||||
$offset = 0;
|
||||
$METAdataBlockLength = strlen($METAdataBlockData);
|
||||
$placeholderpattern = str_repeat("\xFF", 8);
|
||||
while ($offset < $METAdataBlockLength) {
|
||||
$SampleNumberString = substr($METAdataBlockData, $offset, 8);
|
||||
$offset += 8;
|
||||
if ($SampleNumberString == $placeholderpattern) {
|
||||
|
||||
// placeholder point
|
||||
@$ThisFileInfo['flac']['SEEKTABLE']['placeholders']++;
|
||||
$offset += 10;
|
||||
|
||||
} else {
|
||||
|
||||
$SampleNumber = getid3_lib::BigEndian2Int($SampleNumberString);
|
||||
$ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
|
||||
$offset += 8;
|
||||
$ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
|
||||
$offset += 2;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
unset($ThisFileInfo['flac']['SEEKTABLE']['raw']);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function FLACparseCUESHEET($METAdataBlockData, &$ThisFileInfo) {
|
||||
$offset = 0;
|
||||
$ThisFileInfo['flac']['CUESHEET']['media_catalog_number'] = trim(substr($METAdataBlockData, $offset, 128), "\0");
|
||||
$offset += 128;
|
||||
$ThisFileInfo['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
|
||||
$offset += 8;
|
||||
$ThisFileInfo['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)) & 0x80);
|
||||
$offset += 1;
|
||||
|
||||
$offset += 258; // reserved
|
||||
|
||||
$ThisFileInfo['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
|
||||
$offset += 1;
|
||||
|
||||
for ($track = 0; $track < $ThisFileInfo['flac']['CUESHEET']['number_tracks']; $track++) {
|
||||
$TrackSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
|
||||
$offset += 8;
|
||||
$TrackNumber = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
|
||||
$offset += 1;
|
||||
|
||||
$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset;
|
||||
|
||||
$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($METAdataBlockData, $offset, 12);
|
||||
$offset += 12;
|
||||
|
||||
$TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
|
||||
$offset += 1;
|
||||
$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80);
|
||||
$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40);
|
||||
|
||||
$offset += 13; // reserved
|
||||
|
||||
$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
|
||||
$offset += 1;
|
||||
|
||||
for ($index = 0; $index < $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) {
|
||||
$IndexSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
|
||||
$offset += 8;
|
||||
$IndexNumber = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
|
||||
$offset += 1;
|
||||
|
||||
$offset += 3; // reserved
|
||||
|
||||
$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset;
|
||||
}
|
||||
}
|
||||
|
||||
unset($ThisFileInfo['flac']['CUESHEET']['raw']);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function FLACparsePICTURE($meta_data_block_data, &$ThisFileInfo) {
|
||||
$picture = &$ThisFileInfo['flac']['PICTURE'][sizeof($ThisFileInfo['flac']['PICTURE']) - 1];
|
||||
|
||||
$offset = 0;
|
||||
|
||||
$picture['type'] = $this->FLACpictureTypeLookup(getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4)));
|
||||
$offset += 4;
|
||||
|
||||
$length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$picture['mime_type'] = substr($meta_data_block_data, $offset, $length);
|
||||
$offset += $length;
|
||||
|
||||
$length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$picture['description'] = substr($meta_data_block_data, $offset, $length);
|
||||
$offset += $length;
|
||||
|
||||
$picture['width'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$picture['height'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$picture['color_depth'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$picture['colors_indexed'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$picture['image_data'] = substr($meta_data_block_data, $offset, $length);
|
||||
$offset += $length;
|
||||
|
||||
unset($ThisFileInfo['flac']['PICTURE']['raw']);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,228 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.la.php //
|
||||
// module for analyzing LA audio files //
|
||||
// dependencies: module.audio.riff.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
||||
|
||||
class getid3_la
|
||||
{
|
||||
|
||||
function getid3_la(&$fd, &$ThisFileInfo) {
|
||||
$offset = 0;
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$rawdata = fread($fd, GETID3_FREAD_BUFFER_SIZE);
|
||||
|
||||
switch (substr($rawdata, $offset, 4)) {
|
||||
case 'LA02':
|
||||
case 'LA03':
|
||||
case 'LA04':
|
||||
$ThisFileInfo['fileformat'] = 'la';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'la';
|
||||
$ThisFileInfo['audio']['lossless'] = true;
|
||||
|
||||
$ThisFileInfo['la']['version_major'] = (int) substr($rawdata, $offset + 2, 1);
|
||||
$ThisFileInfo['la']['version_minor'] = (int) substr($rawdata, $offset + 3, 1);
|
||||
$ThisFileInfo['la']['version'] = (float) $ThisFileInfo['la']['version_major'] + ($ThisFileInfo['la']['version_minor'] / 10);
|
||||
$offset += 4;
|
||||
|
||||
$ThisFileInfo['la']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$offset += 4;
|
||||
if ($ThisFileInfo['la']['uncompressed_size'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt LA file: uncompressed_size == zero';
|
||||
return false;
|
||||
}
|
||||
|
||||
$WAVEchunk = substr($rawdata, $offset, 4);
|
||||
if ($WAVEchunk !== 'WAVE') {
|
||||
$ThisFileInfo['error'][] = 'Expected "WAVE" ('.getid3_lib::PrintHexBytes('WAVE').') at offset '.$offset.', found "'.$WAVEchunk.'" ('.getid3_lib::PrintHexBytes($WAVEchunk).') instead.';
|
||||
return false;
|
||||
}
|
||||
$offset += 4;
|
||||
|
||||
$ThisFileInfo['la']['fmt_size'] = 24;
|
||||
if ($ThisFileInfo['la']['version'] >= 0.3) {
|
||||
|
||||
$ThisFileInfo['la']['fmt_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$ThisFileInfo['la']['header_size'] = 49 + $ThisFileInfo['la']['fmt_size'] - 24;
|
||||
$offset += 4;
|
||||
|
||||
} else {
|
||||
|
||||
// version 0.2 didn't support additional data blocks
|
||||
$ThisFileInfo['la']['header_size'] = 41;
|
||||
|
||||
}
|
||||
|
||||
$fmt_chunk = substr($rawdata, $offset, 4);
|
||||
if ($fmt_chunk !== 'fmt ') {
|
||||
$ThisFileInfo['error'][] = 'Expected "fmt " ('.getid3_lib::PrintHexBytes('fmt ').') at offset '.$offset.', found "'.$fmt_chunk.'" ('.getid3_lib::PrintHexBytes($fmt_chunk).') instead.';
|
||||
return false;
|
||||
}
|
||||
$offset += 4;
|
||||
$fmt_size = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$ThisFileInfo['la']['raw']['format'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
||||
$offset += 2;
|
||||
|
||||
$ThisFileInfo['la']['channels'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
||||
$offset += 2;
|
||||
if ($ThisFileInfo['la']['channels'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt LA file: channels == zero';
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['la']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$offset += 4;
|
||||
if ($ThisFileInfo['la']['sample_rate'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt LA file: sample_rate == zero';
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['la']['bytes_per_second'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$offset += 4;
|
||||
$ThisFileInfo['la']['bytes_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
||||
$offset += 2;
|
||||
$ThisFileInfo['la']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
||||
$offset += 2;
|
||||
|
||||
$ThisFileInfo['la']['samples'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$ThisFileInfo['la']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 1));
|
||||
$offset += 1;
|
||||
$ThisFileInfo['la']['flags']['seekable'] = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x01);
|
||||
if ($ThisFileInfo['la']['version'] >= 0.4) {
|
||||
$ThisFileInfo['la']['flags']['high_compression'] = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x02);
|
||||
}
|
||||
|
||||
$ThisFileInfo['la']['original_crc'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
// mikeØbevin*de
|
||||
// Basically, the blocksize/seekevery are 61440/19 in La0.4 and 73728/16
|
||||
// in earlier versions. A seekpoint is added every blocksize * seekevery
|
||||
// samples, so 4 * int(totalSamples / (blockSize * seekEvery)) should
|
||||
// give the number of bytes used for the seekpoints. Of course, if seeking
|
||||
// is disabled, there are no seekpoints stored.
|
||||
if ($ThisFileInfo['la']['version'] >= 0.4) {
|
||||
$ThisFileInfo['la']['blocksize'] = 61440;
|
||||
$ThisFileInfo['la']['seekevery'] = 19;
|
||||
} else {
|
||||
$ThisFileInfo['la']['blocksize'] = 73728;
|
||||
$ThisFileInfo['la']['seekevery'] = 16;
|
||||
}
|
||||
|
||||
$ThisFileInfo['la']['seekpoint_count'] = 0;
|
||||
if ($ThisFileInfo['la']['flags']['seekable']) {
|
||||
$ThisFileInfo['la']['seekpoint_count'] = floor($ThisFileInfo['la']['samples'] / ($ThisFileInfo['la']['blocksize'] * $ThisFileInfo['la']['seekevery']));
|
||||
|
||||
for ($i = 0; $i < $ThisFileInfo['la']['seekpoint_count']; $i++) {
|
||||
$ThisFileInfo['la']['seekpoints'][] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$offset += 4;
|
||||
}
|
||||
}
|
||||
|
||||
if ($ThisFileInfo['la']['version'] >= 0.3) {
|
||||
|
||||
// Following the main header information, the program outputs all of the
|
||||
// seekpoints. Following these is what I called the 'footer start',
|
||||
// i.e. the position immediately after the La audio data is finished.
|
||||
$ThisFileInfo['la']['footerstart'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
if ($ThisFileInfo['la']['footerstart'] > $ThisFileInfo['filesize']) {
|
||||
$ThisFileInfo['warning'][] = 'FooterStart value points to offset '.$ThisFileInfo['la']['footerstart'].' which is beyond end-of-file ('.$ThisFileInfo['filesize'].')';
|
||||
$ThisFileInfo['la']['footerstart'] = $ThisFileInfo['filesize'];
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// La v0.2 didn't have FooterStart value
|
||||
$ThisFileInfo['la']['footerstart'] = $ThisFileInfo['avdataend'];
|
||||
|
||||
}
|
||||
|
||||
if ($ThisFileInfo['la']['footerstart'] < $ThisFileInfo['avdataend']) {
|
||||
if ($RIFFtempfilename = tempnam('*', 'id3')) {
|
||||
if ($RIFF_fp = fopen($RIFFtempfilename, 'w+b')) {
|
||||
$RIFFdata = 'WAVE';
|
||||
if ($ThisFileInfo['la']['version'] == 0.2) {
|
||||
$RIFFdata .= substr($rawdata, 12, 24);
|
||||
} else {
|
||||
$RIFFdata .= substr($rawdata, 16, 24);
|
||||
}
|
||||
if ($ThisFileInfo['la']['footerstart'] < $ThisFileInfo['avdataend']) {
|
||||
fseek($fd, $ThisFileInfo['la']['footerstart'], SEEK_SET);
|
||||
$RIFFdata .= fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['la']['footerstart']);
|
||||
}
|
||||
$RIFFdata = 'RIFF'.getid3_lib::LittleEndian2String(strlen($RIFFdata), 4, false).$RIFFdata;
|
||||
fwrite($RIFF_fp, $RIFFdata, strlen($RIFFdata));
|
||||
$dummy = $ThisFileInfo;
|
||||
$dummy['filesize'] = strlen($RIFFdata);
|
||||
$dummy['avdataoffset'] = 0;
|
||||
$dummy['avdataend'] = $dummy['filesize'];
|
||||
|
||||
$riff = new getid3_riff($RIFF_fp, $dummy);
|
||||
if (empty($dummy['error'])) {
|
||||
$ThisFileInfo['riff'] = $dummy['riff'];
|
||||
} else {
|
||||
$ThisFileInfo['warning'][] = 'Error parsing RIFF portion of La file: '.implode($dummy['error']);
|
||||
}
|
||||
unset($riff);
|
||||
unset($dummy);
|
||||
fclose($RIFF_fp);
|
||||
}
|
||||
unlink($RIFFtempfilename);
|
||||
}
|
||||
}
|
||||
|
||||
// $ThisFileInfo['avdataoffset'] should be zero to begin with, but just in case it's not, include the addition anyway
|
||||
$ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $ThisFileInfo['la']['footerstart'];
|
||||
$ThisFileInfo['avdataoffset'] = $ThisFileInfo['avdataoffset'] + $offset;
|
||||
|
||||
//$ThisFileInfo['la']['codec'] = RIFFwFormatTagLookup($ThisFileInfo['la']['raw']['format']);
|
||||
$ThisFileInfo['la']['compression_ratio'] = (float) (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['la']['uncompressed_size']);
|
||||
$ThisFileInfo['playtime_seconds'] = (float) ($ThisFileInfo['la']['samples'] / $ThisFileInfo['la']['sample_rate']) / $ThisFileInfo['la']['channels'];
|
||||
if ($ThisFileInfo['playtime_seconds'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt LA file: playtime_seconds == zero';
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds'];
|
||||
//$ThisFileInfo['audio']['codec'] = $ThisFileInfo['la']['codec'];
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['la']['bits_per_sample'];
|
||||
break;
|
||||
|
||||
default:
|
||||
if (substr($rawdata, $offset, 2) == 'LA') {
|
||||
$ThisFileInfo['error'][] = 'This version of getID3() (v'.GETID3_VERSION.') doesn\'t support LA version '.substr($rawdata, $offset + 2, 1).'.'.substr($rawdata, $offset + 3, 1).' which this appears to be - check http://getid3.sourceforge.net for updates.';
|
||||
} else {
|
||||
$ThisFileInfo['error'][] = 'Not a LA (Lossless-Audio) file';
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['la']['channels'];
|
||||
$ThisFileInfo['audio']['sample_rate'] = (int) $ThisFileInfo['la']['sample_rate'];
|
||||
$ThisFileInfo['audio']['encoder'] = 'LA v'.$ThisFileInfo['la']['version'];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.lpac.php //
|
||||
// module for analyzing LPAC Audio files //
|
||||
// dependencies: module.audio-video.riff.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
||||
|
||||
class getid3_lpac
|
||||
{
|
||||
|
||||
function getid3_lpac(&$fd, &$ThisFileInfo) {
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$LPACheader = fread($fd, 14);
|
||||
if (substr($LPACheader, 0, 4) != 'LPAC') {
|
||||
$ThisFileInfo['error'][] = 'Expected "LPAC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$StreamMarker.'"';
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['avdataoffset'] += 14;
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'lpac';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'lpac';
|
||||
$ThisFileInfo['audio']['lossless'] = true;
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
|
||||
|
||||
$ThisFileInfo['lpac']['file_version'] = getid3_lib::BigEndian2Int(substr($LPACheader, 4, 1));
|
||||
$flags['audio_type'] = getid3_lib::BigEndian2Int(substr($LPACheader, 5, 1));
|
||||
$ThisFileInfo['lpac']['total_samples']= getid3_lib::BigEndian2Int(substr($LPACheader, 6, 4));
|
||||
$flags['parameters'] = getid3_lib::BigEndian2Int(substr($LPACheader, 10, 4));
|
||||
|
||||
$ThisFileInfo['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40);
|
||||
$ThisFileInfo['lpac']['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x04);
|
||||
$ThisFileInfo['lpac']['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x02);
|
||||
$ThisFileInfo['lpac']['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x01);
|
||||
|
||||
if ($ThisFileInfo['lpac']['flags']['24_bit'] && $ThisFileInfo['lpac']['flags']['16_bit']) {
|
||||
$ThisFileInfo['warning'][] = '24-bit and 16-bit flags cannot both be set';
|
||||
}
|
||||
|
||||
$ThisFileInfo['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000);
|
||||
$ThisFileInfo['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x08000000);
|
||||
$ThisFileInfo['lpac']['block_length'] = pow(2, (($flags['parameters'] & 0x07000000) >> 24)) * 256;
|
||||
$ThisFileInfo['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x00800000);
|
||||
$ThisFileInfo['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x00400000);
|
||||
$ThisFileInfo['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x00040000);
|
||||
$ThisFileInfo['lpac']['quantization'] = ($flags['parameters'] & 0x00001F00) >> 8;
|
||||
$ThisFileInfo['lpac']['max_prediction_order'] = ($flags['parameters'] & 0x0000003F);
|
||||
|
||||
if ($ThisFileInfo['lpac']['flags']['fast_compress'] && ($ThisFileInfo['lpac']['max_prediction_order'] != 3)) {
|
||||
$ThisFileInfo['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "'.$ThisFileInfo['lpac']['max_prediction_order'].'"';
|
||||
}
|
||||
switch ($ThisFileInfo['lpac']['file_version']) {
|
||||
case 6:
|
||||
if ($ThisFileInfo['lpac']['flags']['adaptive_quantization']) {
|
||||
$ThisFileInfo['warning'][] = 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true';
|
||||
}
|
||||
if ($ThisFileInfo['lpac']['quantization'] != 20) {
|
||||
$ThisFileInfo['warning'][] = 'Quantization expected to be 20 in LPAC file stucture v6, actually '.$ThisFileInfo['lpac']['flags']['Q'];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
//$ThisFileInfo['warning'][] = 'This version of getID3() only supports LPAC file format version 6, this file is version '.$ThisFileInfo['lpac']['file_version'].' - please report to info@getid3.org';
|
||||
break;
|
||||
}
|
||||
|
||||
$dummy = $ThisFileInfo;
|
||||
$riff = new getid3_riff($fd, $dummy);
|
||||
unset($riff);
|
||||
$ThisFileInfo['avdataoffset'] = $dummy['avdataoffset'];
|
||||
$ThisFileInfo['riff'] = $dummy['riff'];
|
||||
$ThisFileInfo['error'] = $dummy['error'];
|
||||
$ThisFileInfo['warning'] = $dummy['warning'];
|
||||
$ThisFileInfo['lpac']['comments']['comment'] = $dummy['comments'];
|
||||
$ThisFileInfo['audio']['sample_rate'] = $dummy['audio']['sample_rate'];
|
||||
|
||||
$ThisFileInfo['audio']['channels'] = ($ThisFileInfo['lpac']['flags']['stereo'] ? 2 : 1);
|
||||
|
||||
if ($ThisFileInfo['lpac']['flags']['24_bit']) {
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['riff']['audio'][0]['bits_per_sample'];
|
||||
} elseif ($ThisFileInfo['lpac']['flags']['16_bit']) {
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = 16;
|
||||
} else {
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = 8;
|
||||
}
|
||||
|
||||
if ($ThisFileInfo['lpac']['flags']['fast_compress']) {
|
||||
// fast
|
||||
$ThisFileInfo['audio']['encoder_options'] = '-1';
|
||||
} else {
|
||||
switch ($ThisFileInfo['lpac']['max_prediction_order']) {
|
||||
case 20: // simple
|
||||
$ThisFileInfo['audio']['encoder_options'] = '-2';
|
||||
break;
|
||||
case 30: // medium
|
||||
$ThisFileInfo['audio']['encoder_options'] = '-3';
|
||||
break;
|
||||
case 40: // high
|
||||
$ThisFileInfo['audio']['encoder_options'] = '-4';
|
||||
break;
|
||||
case 60: // extrahigh
|
||||
$ThisFileInfo['audio']['encoder_options'] = '-5';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['lpac']['total_samples'] / $ThisFileInfo['audio']['sample_rate'];
|
||||
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,522 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.midi.php //
|
||||
// module for Midi Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_midi
|
||||
{
|
||||
|
||||
function getid3_midi(&$fd, &$ThisFileInfo, $scanwholefile=true) {
|
||||
|
||||
// shortcut
|
||||
$ThisFileInfo['midi']['raw'] = array();
|
||||
$thisfile_midi = &$ThisFileInfo['midi'];
|
||||
$thisfile_midi_raw = &$thisfile_midi['raw'];
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'midi';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'midi';
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$MIDIdata = fread($fd, GETID3_FREAD_BUFFER_SIZE);
|
||||
$offset = 0;
|
||||
$MIDIheaderID = substr($MIDIdata, $offset, 4); // 'MThd'
|
||||
if ($MIDIheaderID != 'MThd') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "MThd" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$MIDIheaderID.'"';
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
return false;
|
||||
}
|
||||
$offset += 4;
|
||||
$thisfile_midi_raw['headersize'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_midi_raw['fileformat'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_midi_raw['tracks'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_midi_raw['ticksperqnote'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
|
||||
$offset += 2;
|
||||
|
||||
for ($i = 0; $i < $thisfile_midi_raw['tracks']; $i++) {
|
||||
if ((strlen($MIDIdata) - $offset) < 8) {
|
||||
$MIDIdata .= fread($fd, GETID3_FREAD_BUFFER_SIZE);
|
||||
}
|
||||
$trackID = substr($MIDIdata, $offset, 4);
|
||||
$offset += 4;
|
||||
if ($trackID == 'MTrk') {
|
||||
$tracksize = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4));
|
||||
$offset += 4;
|
||||
// $thisfile_midi['tracks'][$i]['size'] = $tracksize;
|
||||
$trackdataarray[$i] = substr($MIDIdata, $offset, $tracksize);
|
||||
$offset += $tracksize;
|
||||
} else {
|
||||
$ThisFileInfo['error'][] = 'Expecting "MTrk" at '.$offset.', found '.$trackID.' instead';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($trackdataarray) || !is_array($trackdataarray)) {
|
||||
$ThisFileInfo['error'][] = 'Cannot find MIDI track information';
|
||||
unset($thisfile_midi);
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($scanwholefile) { // this can take quite a long time, so have the option to bypass it if speed is very important
|
||||
$thisfile_midi['totalticks'] = 0;
|
||||
$ThisFileInfo['playtime_seconds'] = 0;
|
||||
$CurrentMicroSecondsPerBeat = 500000; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat
|
||||
$CurrentBeatsPerMinute = 120; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat
|
||||
$MicroSecondsPerQuarterNoteAfter = array ();
|
||||
|
||||
foreach ($trackdataarray as $tracknumber => $trackdata) {
|
||||
|
||||
$eventsoffset = 0;
|
||||
$LastIssuedMIDIcommand = 0;
|
||||
$LastIssuedMIDIchannel = 0;
|
||||
$CumulativeDeltaTime = 0;
|
||||
$TicksAtCurrentBPM = 0;
|
||||
while ($eventsoffset < strlen($trackdata)) {
|
||||
$eventid = 0;
|
||||
if (isset($MIDIevents[$tracknumber]) && is_array($MIDIevents[$tracknumber])) {
|
||||
$eventid = count($MIDIevents[$tracknumber]);
|
||||
}
|
||||
$deltatime = 0;
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
$deltatimebyte = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
$deltatime = ($deltatime << 7) + ($deltatimebyte & 0x7F);
|
||||
if ($deltatimebyte & 0x80) {
|
||||
// another byte follows
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$CumulativeDeltaTime += $deltatime;
|
||||
$TicksAtCurrentBPM += $deltatime;
|
||||
$MIDIevents[$tracknumber][$eventid]['deltatime'] = $deltatime;
|
||||
$MIDI_event_channel = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
if ($MIDI_event_channel & 0x80) {
|
||||
// OK, normal event - MIDI command has MSB set
|
||||
$LastIssuedMIDIcommand = $MIDI_event_channel >> 4;
|
||||
$LastIssuedMIDIchannel = $MIDI_event_channel & 0x0F;
|
||||
} else {
|
||||
// running event - assume last command
|
||||
$eventsoffset--;
|
||||
}
|
||||
$MIDIevents[$tracknumber][$eventid]['eventid'] = $LastIssuedMIDIcommand;
|
||||
$MIDIevents[$tracknumber][$eventid]['channel'] = $LastIssuedMIDIchannel;
|
||||
if ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x08) { // Note off (key is released)
|
||||
|
||||
$notenumber = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
$velocity = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
|
||||
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x09) { // Note on (key is pressed)
|
||||
|
||||
$notenumber = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
$velocity = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
|
||||
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0A) { // Key after-touch
|
||||
|
||||
$notenumber = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
$velocity = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
|
||||
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0B) { // Control Change
|
||||
|
||||
$controllernum = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
$newvalue = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
|
||||
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0C) { // Program (patch) change
|
||||
|
||||
$newprogramnum = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
|
||||
$thisfile_midi_raw['track'][$tracknumber]['instrumentid'] = $newprogramnum;
|
||||
if ($tracknumber == 10) {
|
||||
$thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIpercussionLookup($newprogramnum);
|
||||
} else {
|
||||
$thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIinstrumentLookup($newprogramnum);
|
||||
}
|
||||
|
||||
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0D) { // Channel after-touch
|
||||
|
||||
$channelnumber = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
|
||||
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0E) { // Pitch wheel change (2000H is normal or no change)
|
||||
|
||||
$changeLSB = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
$changeMSB = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
$pitchwheelchange = (($changeMSB & 0x7F) << 7) & ($changeLSB & 0x7F);
|
||||
|
||||
} elseif (($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0F) && ($MIDIevents[$tracknumber][$eventid]['channel'] == 0x0F)) {
|
||||
|
||||
$METAeventCommand = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
$METAeventLength = ord(substr($trackdata, $eventsoffset++, 1));
|
||||
$METAeventData = substr($trackdata, $eventsoffset, $METAeventLength);
|
||||
$eventsoffset += $METAeventLength;
|
||||
switch ($METAeventCommand) {
|
||||
case 0x00: // Set track sequence number
|
||||
$track_sequence_number = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['seqno'] = $track_sequence_number;
|
||||
break;
|
||||
|
||||
case 0x01: // Text: generic
|
||||
$text_generic = substr($METAeventData, 0, $METAeventLength);
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['text'] = $text_generic;
|
||||
$thisfile_midi['comments']['comment'][] = $text_generic;
|
||||
break;
|
||||
|
||||
case 0x02: // Text: copyright
|
||||
$text_copyright = substr($METAeventData, 0, $METAeventLength);
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['copyright'] = $text_copyright;
|
||||
$thisfile_midi['comments']['copyright'][] = $text_copyright;
|
||||
break;
|
||||
|
||||
case 0x03: // Text: track name
|
||||
$text_trackname = substr($METAeventData, 0, $METAeventLength);
|
||||
$thisfile_midi_raw['track'][$tracknumber]['name'] = $text_trackname;
|
||||
break;
|
||||
|
||||
case 0x04: // Text: track instrument name
|
||||
$text_instrument = substr($METAeventData, 0, $METAeventLength);
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['instrument'] = $text_instrument;
|
||||
break;
|
||||
|
||||
case 0x05: // Text: lyrics
|
||||
$text_lyrics = substr($METAeventData, 0, $METAeventLength);
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['lyrics'] = $text_lyrics;
|
||||
if (!isset($thisfile_midi['lyrics'])) {
|
||||
$thisfile_midi['lyrics'] = '';
|
||||
}
|
||||
$thisfile_midi['lyrics'] .= $text_lyrics."\n";
|
||||
break;
|
||||
|
||||
case 0x06: // Text: marker
|
||||
$text_marker = substr($METAeventData, 0, $METAeventLength);
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['marker'] = $text_marker;
|
||||
break;
|
||||
|
||||
case 0x07: // Text: cue point
|
||||
$text_cuepoint = substr($METAeventData, 0, $METAeventLength);
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['cuepoint'] = $text_cuepoint;
|
||||
break;
|
||||
|
||||
case 0x2F: // End Of Track
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['EOT'] = $CumulativeDeltaTime;
|
||||
break;
|
||||
|
||||
case 0x51: // Tempo: microseconds / quarter note
|
||||
$CurrentMicroSecondsPerBeat = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
|
||||
if ($CurrentMicroSecondsPerBeat == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt MIDI file: CurrentMicroSecondsPerBeat == zero';
|
||||
return false;
|
||||
}
|
||||
$thisfile_midi_raw['events'][$tracknumber][$CumulativeDeltaTime]['us_qnote'] = $CurrentMicroSecondsPerBeat;
|
||||
$CurrentBeatsPerMinute = (1000000 / $CurrentMicroSecondsPerBeat) * 60;
|
||||
$MicroSecondsPerQuarterNoteAfter[$CumulativeDeltaTime] = $CurrentMicroSecondsPerBeat;
|
||||
$TicksAtCurrentBPM = 0;
|
||||
break;
|
||||
|
||||
case 0x58: // Time signature
|
||||
$timesig_numerator = getid3_lib::BigEndian2Int($METAeventData{0});
|
||||
$timesig_denominator = pow(2, getid3_lib::BigEndian2Int($METAeventData{1})); // $02 -> x/4, $03 -> x/8, etc
|
||||
$timesig_32inqnote = getid3_lib::BigEndian2Int($METAeventData{2}); // number of 32nd notes to the quarter note
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_32inqnote'] = $timesig_32inqnote;
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_numerator'] = $timesig_numerator;
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_denominator'] = $timesig_denominator;
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_text'] = $timesig_numerator.'/'.$timesig_denominator;
|
||||
$thisfile_midi['timesignature'][] = $timesig_numerator.'/'.$timesig_denominator;
|
||||
break;
|
||||
|
||||
case 0x59: // Keysignature
|
||||
$keysig_sharpsflats = getid3_lib::BigEndian2Int($METAeventData{0});
|
||||
if ($keysig_sharpsflats & 0x80) {
|
||||
// (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps)
|
||||
$keysig_sharpsflats -= 256;
|
||||
}
|
||||
|
||||
$keysig_majorminor = getid3_lib::BigEndian2Int($METAeventData{1}); // 0 -> major, 1 -> minor
|
||||
$keysigs = array(-7=>'Cb', -6=>'Gb', -5=>'Db', -4=>'Ab', -3=>'Eb', -2=>'Bb', -1=>'F', 0=>'C', 1=>'G', 2=>'D', 3=>'A', 4=>'E', 5=>'B', 6=>'F#', 7=>'C#');
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0);
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_flats'] = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0);
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] = (bool) $keysig_majorminor;
|
||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_text'] = $keysigs[$keysig_sharpsflats].' '.($thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] ? 'minor' : 'major');
|
||||
|
||||
// $keysigs[$keysig_sharpsflats] gets an int key (correct) - $keysigs["$keysig_sharpsflats"] gets a string key (incorrect)
|
||||
$thisfile_midi['keysignature'][] = $keysigs[$keysig_sharpsflats].' '.((bool) $keysig_majorminor ? 'minor' : 'major');
|
||||
break;
|
||||
|
||||
case 0x7F: // Sequencer specific information
|
||||
$custom_data = substr($METAeventData, 0, $METAeventLength);
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['warning'][] = 'Unhandled META Event Command: '.$METAeventCommand;
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['warning'][] = 'Unhandled MIDI Event ID: '.$MIDIevents[$tracknumber][$eventid]['eventid'].' + Channel ID: '.$MIDIevents[$tracknumber][$eventid]['channel'];
|
||||
|
||||
}
|
||||
}
|
||||
if (($tracknumber > 0) || (count($trackdataarray) == 1)) {
|
||||
$thisfile_midi['totalticks'] = max($thisfile_midi['totalticks'], $CumulativeDeltaTime);
|
||||
}
|
||||
}
|
||||
$previoustickoffset = null;
|
||||
|
||||
ksort($MicroSecondsPerQuarterNoteAfter);
|
||||
foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) {
|
||||
if (is_null($previoustickoffset)) {
|
||||
$prevmicrosecondsperbeat = $microsecondsperbeat;
|
||||
$previoustickoffset = $tickoffset;
|
||||
continue;
|
||||
}
|
||||
if ($thisfile_midi['totalticks'] > $tickoffset) {
|
||||
|
||||
if ($thisfile_midi_raw['ticksperqnote'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['playtime_seconds'] += (($tickoffset - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($prevmicrosecondsperbeat / 1000000);
|
||||
|
||||
$prevmicrosecondsperbeat = $microsecondsperbeat;
|
||||
$previoustickoffset = $tickoffset;
|
||||
}
|
||||
}
|
||||
if ($thisfile_midi['totalticks'] > $previoustickoffset) {
|
||||
|
||||
if ($thisfile_midi_raw['ticksperqnote'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['playtime_seconds'] += (($thisfile_midi['totalticks'] - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($microsecondsperbeat / 1000000);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (@$ThisFileInfo['playtime_seconds'] > 0) {
|
||||
$ThisFileInfo['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
}
|
||||
|
||||
if (!empty($thisfile_midi['lyrics'])) {
|
||||
$thisfile_midi['comments']['lyrics'][] = $thisfile_midi['lyrics'];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function GeneralMIDIinstrumentLookup($instrumentid) {
|
||||
|
||||
$begin = __LINE__;
|
||||
|
||||
/** This is not a comment!
|
||||
|
||||
0 Acoustic Grand
|
||||
1 Bright Acoustic
|
||||
2 Electric Grand
|
||||
3 Honky-Tonk
|
||||
4 Electric Piano 1
|
||||
5 Electric Piano 2
|
||||
6 Harpsichord
|
||||
7 Clavier
|
||||
8 Celesta
|
||||
9 Glockenspiel
|
||||
10 Music Box
|
||||
11 Vibraphone
|
||||
12 Marimba
|
||||
13 Xylophone
|
||||
14 Tubular Bells
|
||||
15 Dulcimer
|
||||
16 Drawbar Organ
|
||||
17 Percussive Organ
|
||||
18 Rock Organ
|
||||
19 Church Organ
|
||||
20 Reed Organ
|
||||
21 Accordian
|
||||
22 Harmonica
|
||||
23 Tango Accordian
|
||||
24 Acoustic Guitar (nylon)
|
||||
25 Acoustic Guitar (steel)
|
||||
26 Electric Guitar (jazz)
|
||||
27 Electric Guitar (clean)
|
||||
28 Electric Guitar (muted)
|
||||
29 Overdriven Guitar
|
||||
30 Distortion Guitar
|
||||
31 Guitar Harmonics
|
||||
32 Acoustic Bass
|
||||
33 Electric Bass (finger)
|
||||
34 Electric Bass (pick)
|
||||
35 Fretless Bass
|
||||
36 Slap Bass 1
|
||||
37 Slap Bass 2
|
||||
38 Synth Bass 1
|
||||
39 Synth Bass 2
|
||||
40 Violin
|
||||
41 Viola
|
||||
42 Cello
|
||||
43 Contrabass
|
||||
44 Tremolo Strings
|
||||
45 Pizzicato Strings
|
||||
46 Orchestral Strings
|
||||
47 Timpani
|
||||
48 String Ensemble 1
|
||||
49 String Ensemble 2
|
||||
50 SynthStrings 1
|
||||
51 SynthStrings 2
|
||||
52 Choir Aahs
|
||||
53 Voice Oohs
|
||||
54 Synth Voice
|
||||
55 Orchestra Hit
|
||||
56 Trumpet
|
||||
57 Trombone
|
||||
58 Tuba
|
||||
59 Muted Trumpet
|
||||
60 French Horn
|
||||
61 Brass Section
|
||||
62 SynthBrass 1
|
||||
63 SynthBrass 2
|
||||
64 Soprano Sax
|
||||
65 Alto Sax
|
||||
66 Tenor Sax
|
||||
67 Baritone Sax
|
||||
68 Oboe
|
||||
69 English Horn
|
||||
70 Bassoon
|
||||
71 Clarinet
|
||||
72 Piccolo
|
||||
73 Flute
|
||||
74 Recorder
|
||||
75 Pan Flute
|
||||
76 Blown Bottle
|
||||
77 Shakuhachi
|
||||
78 Whistle
|
||||
79 Ocarina
|
||||
80 Lead 1 (square)
|
||||
81 Lead 2 (sawtooth)
|
||||
82 Lead 3 (calliope)
|
||||
83 Lead 4 (chiff)
|
||||
84 Lead 5 (charang)
|
||||
85 Lead 6 (voice)
|
||||
86 Lead 7 (fifths)
|
||||
87 Lead 8 (bass + lead)
|
||||
88 Pad 1 (new age)
|
||||
89 Pad 2 (warm)
|
||||
90 Pad 3 (polysynth)
|
||||
91 Pad 4 (choir)
|
||||
92 Pad 5 (bowed)
|
||||
93 Pad 6 (metallic)
|
||||
94 Pad 7 (halo)
|
||||
95 Pad 8 (sweep)
|
||||
96 FX 1 (rain)
|
||||
97 FX 2 (soundtrack)
|
||||
98 FX 3 (crystal)
|
||||
99 FX 4 (atmosphere)
|
||||
100 FX 5 (brightness)
|
||||
101 FX 6 (goblins)
|
||||
102 FX 7 (echoes)
|
||||
103 FX 8 (sci-fi)
|
||||
104 Sitar
|
||||
105 Banjo
|
||||
106 Shamisen
|
||||
107 Koto
|
||||
108 Kalimba
|
||||
109 Bagpipe
|
||||
110 Fiddle
|
||||
111 Shanai
|
||||
112 Tinkle Bell
|
||||
113 Agogo
|
||||
114 Steel Drums
|
||||
115 Woodblock
|
||||
116 Taiko Drum
|
||||
117 Melodic Tom
|
||||
118 Synth Drum
|
||||
119 Reverse Cymbal
|
||||
120 Guitar Fret Noise
|
||||
121 Breath Noise
|
||||
122 Seashore
|
||||
123 Bird Tweet
|
||||
124 Telephone Ring
|
||||
125 Helicopter
|
||||
126 Applause
|
||||
127 Gunshot
|
||||
|
||||
*/
|
||||
|
||||
return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIinstrument');
|
||||
}
|
||||
|
||||
function GeneralMIDIpercussionLookup($instrumentid) {
|
||||
|
||||
$begin = __LINE__;
|
||||
|
||||
/** This is not a comment!
|
||||
|
||||
35 Acoustic Bass Drum
|
||||
36 Bass Drum 1
|
||||
37 Side Stick
|
||||
38 Acoustic Snare
|
||||
39 Hand Clap
|
||||
40 Electric Snare
|
||||
41 Low Floor Tom
|
||||
42 Closed Hi-Hat
|
||||
43 High Floor Tom
|
||||
44 Pedal Hi-Hat
|
||||
45 Low Tom
|
||||
46 Open Hi-Hat
|
||||
47 Low-Mid Tom
|
||||
48 Hi-Mid Tom
|
||||
49 Crash Cymbal 1
|
||||
50 High Tom
|
||||
51 Ride Cymbal 1
|
||||
52 Chinese Cymbal
|
||||
53 Ride Bell
|
||||
54 Tambourine
|
||||
55 Splash Cymbal
|
||||
56 Cowbell
|
||||
57 Crash Cymbal 2
|
||||
59 Ride Cymbal 2
|
||||
60 Hi Bongo
|
||||
61 Low Bongo
|
||||
62 Mute Hi Conga
|
||||
63 Open Hi Conga
|
||||
64 Low Conga
|
||||
65 High Timbale
|
||||
66 Low Timbale
|
||||
67 High Agogo
|
||||
68 Low Agogo
|
||||
69 Cabasa
|
||||
70 Maracas
|
||||
71 Short Whistle
|
||||
72 Long Whistle
|
||||
73 Short Guiro
|
||||
74 Long Guiro
|
||||
75 Claves
|
||||
76 Hi Wood Block
|
||||
77 Low Wood Block
|
||||
78 Mute Cuica
|
||||
79 Open Cuica
|
||||
80 Mute Triangle
|
||||
81 Open Triangle
|
||||
|
||||
*/
|
||||
|
||||
return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIpercussion');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.mod.php //
|
||||
// module for analyzing MOD Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_mod
|
||||
{
|
||||
|
||||
// new combined constructor
|
||||
function getid3_mod(&$fd, &$ThisFileInfo, $option) {
|
||||
|
||||
if ($option === 'mod') {
|
||||
$this->getMODheaderFilepointer($fd, $ThisFileInfo);
|
||||
}
|
||||
elseif ($option === 'xm') {
|
||||
$this->getXMheaderFilepointer($fd, $ThisFileInfo);
|
||||
}
|
||||
elseif ($option === 'it') {
|
||||
$this->getITheaderFilepointer($fd, $ThisFileInfo);
|
||||
}
|
||||
elseif ($option === 's3m') {
|
||||
$this->getS3MheaderFilepointer($fd, $ThisFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getMODheaderFilepointer(&$fd, &$ThisFileInfo) {
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'] + 1080);
|
||||
$FormatID = fread($fd, 4);
|
||||
if (!ereg('^(M.K.|[5-9]CHN|[1-3][0-9]CH)$', $FormatID)) {
|
||||
$ThisFileInfo['error'][] = 'This is not a known type of MOD file';
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'mod';
|
||||
|
||||
$ThisFileInfo['error'][] = 'MOD parsing not enabled in this version of getID3()';
|
||||
return false;
|
||||
}
|
||||
|
||||
function getXMheaderFilepointer(&$fd, &$ThisFileInfo) {
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset']);
|
||||
$FormatID = fread($fd, 15);
|
||||
if (!ereg('^Extended Module$', $FormatID)) {
|
||||
$ThisFileInfo['error'][] = 'This is not a known type of XM-MOD file';
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'xm';
|
||||
|
||||
$ThisFileInfo['error'][] = 'XM-MOD parsing not enabled in this version of getID3()';
|
||||
return false;
|
||||
}
|
||||
|
||||
function getS3MheaderFilepointer(&$fd, &$ThisFileInfo) {
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'] + 44);
|
||||
$FormatID = fread($fd, 4);
|
||||
if (!ereg('^SCRM$', $FormatID)) {
|
||||
$ThisFileInfo['error'][] = 'This is not a ScreamTracker MOD file';
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['fileformat'] = 's3m';
|
||||
|
||||
$ThisFileInfo['error'][] = 'ScreamTracker parsing not enabled in this version of getID3()';
|
||||
return false;
|
||||
}
|
||||
|
||||
function getITheaderFilepointer(&$fd, &$ThisFileInfo) {
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset']);
|
||||
$FormatID = fread($fd, 4);
|
||||
if (!ereg('^IMPM$', $FormatID)) {
|
||||
$ThisFileInfo['error'][] = 'This is not an ImpulseTracker MOD file';
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'it';
|
||||
|
||||
$ThisFileInfo['error'][] = 'ImpulseTracker parsing not enabled in this version of getID3()';
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,202 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.monkey.php //
|
||||
// module for analyzing Monkey's Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_monkey
|
||||
{
|
||||
|
||||
function getid3_monkey(&$fd, &$ThisFileInfo) {
|
||||
// based loosely on code from TMonkey by Jurgen Faul <jfaulØgmx*de>
|
||||
// http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'mac';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'mac';
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
|
||||
$ThisFileInfo['audio']['lossless'] = true;
|
||||
|
||||
$ThisFileInfo['monkeys_audio']['raw'] = array();
|
||||
$thisfile_monkeysaudio = &$ThisFileInfo['monkeys_audio'];
|
||||
$thisfile_monkeysaudio_raw = &$thisfile_monkeysaudio['raw'];
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$MACheaderData = fread($fd, 74);
|
||||
|
||||
$thisfile_monkeysaudio_raw['magic'] = substr($MACheaderData, 0, 4);
|
||||
if ($thisfile_monkeysaudio_raw['magic'] != 'MAC ') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "MAC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_monkeysaudio_raw['magic'].'"';
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
return false;
|
||||
}
|
||||
$thisfile_monkeysaudio_raw['nVersion'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 4, 2)); // appears to be uint32 in 3.98+
|
||||
|
||||
if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
|
||||
$thisfile_monkeysaudio_raw['nCompressionLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 6, 2));
|
||||
$thisfile_monkeysaudio_raw['nFormatFlags'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 8, 2));
|
||||
$thisfile_monkeysaudio_raw['nChannels'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 10, 2));
|
||||
$thisfile_monkeysaudio_raw['nSampleRate'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 12, 4));
|
||||
$thisfile_monkeysaudio_raw['nHeaderDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 16, 4));
|
||||
$thisfile_monkeysaudio_raw['nWAVTerminatingBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 20, 4));
|
||||
$thisfile_monkeysaudio_raw['nTotalFrames'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 24, 4));
|
||||
$thisfile_monkeysaudio_raw['nFinalFrameSamples'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 28, 4));
|
||||
$thisfile_monkeysaudio_raw['nPeakLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 32, 4));
|
||||
$thisfile_monkeysaudio_raw['nSeekElements'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 38, 2));
|
||||
$offset = 8;
|
||||
} else {
|
||||
$offset = 8;
|
||||
// APE_DESCRIPTOR
|
||||
$thisfile_monkeysaudio_raw['nDescriptorBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_monkeysaudio_raw['nHeaderBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_monkeysaudio_raw['nSeekTableBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_monkeysaudio_raw['nHeaderDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_monkeysaudio_raw['nAPEFrameDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_monkeysaudio_raw['nAPEFrameDataBytesHigh'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_monkeysaudio_raw['nTerminatingDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_monkeysaudio_raw['cFileMD5'] = substr($MACheaderData, $offset, 16);
|
||||
$offset += 16;
|
||||
|
||||
// APE_HEADER
|
||||
$thisfile_monkeysaudio_raw['nCompressionLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_monkeysaudio_raw['nFormatFlags'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_monkeysaudio_raw['nBlocksPerFrame'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_monkeysaudio_raw['nFinalFrameBlocks'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_monkeysaudio_raw['nTotalFrames'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_monkeysaudio_raw['nBitsPerSample'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_monkeysaudio_raw['nChannels'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_monkeysaudio_raw['nSampleRate'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||
$offset += 4;
|
||||
}
|
||||
|
||||
$thisfile_monkeysaudio['flags']['8-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0001);
|
||||
$thisfile_monkeysaudio['flags']['crc-32'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0002);
|
||||
$thisfile_monkeysaudio['flags']['peak_level'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0004);
|
||||
$thisfile_monkeysaudio['flags']['24-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0008);
|
||||
$thisfile_monkeysaudio['flags']['seek_elements'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0010);
|
||||
$thisfile_monkeysaudio['flags']['no_wav_header'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0020);
|
||||
$thisfile_monkeysaudio['version'] = $thisfile_monkeysaudio_raw['nVersion'] / 1000;
|
||||
$thisfile_monkeysaudio['compression'] = $this->MonkeyCompressionLevelNameLookup($thisfile_monkeysaudio_raw['nCompressionLevel']);
|
||||
if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
|
||||
$thisfile_monkeysaudio['samples_per_frame'] = $this->MonkeySamplesPerFrame($thisfile_monkeysaudio_raw['nVersion'], $thisfile_monkeysaudio_raw['nCompressionLevel']);
|
||||
}
|
||||
$thisfile_monkeysaudio['bits_per_sample'] = ($thisfile_monkeysaudio['flags']['24-bit'] ? 24 : ($thisfile_monkeysaudio['flags']['8-bit'] ? 8 : 16));
|
||||
$thisfile_monkeysaudio['channels'] = $thisfile_monkeysaudio_raw['nChannels'];
|
||||
$ThisFileInfo['audio']['channels'] = $thisfile_monkeysaudio['channels'];
|
||||
$thisfile_monkeysaudio['sample_rate'] = $thisfile_monkeysaudio_raw['nSampleRate'];
|
||||
if ($thisfile_monkeysaudio['sample_rate'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt MAC file: frequency == zero';
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['audio']['sample_rate'] = $thisfile_monkeysaudio['sample_rate'];
|
||||
if ($thisfile_monkeysaudio['flags']['peak_level']) {
|
||||
$thisfile_monkeysaudio['peak_level'] = $thisfile_monkeysaudio_raw['nPeakLevel'];
|
||||
$thisfile_monkeysaudio['peak_ratio'] = $thisfile_monkeysaudio['peak_level'] / pow(2, $thisfile_monkeysaudio['bits_per_sample'] - 1);
|
||||
}
|
||||
if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
|
||||
$thisfile_monkeysaudio['samples'] = (($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio_raw['nBlocksPerFrame']) + $thisfile_monkeysaudio_raw['nFinalFrameBlocks'];
|
||||
} else {
|
||||
$thisfile_monkeysaudio['samples'] = (($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio['samples_per_frame']) + $thisfile_monkeysaudio_raw['nFinalFrameSamples'];
|
||||
}
|
||||
$thisfile_monkeysaudio['playtime'] = $thisfile_monkeysaudio['samples'] / $thisfile_monkeysaudio['sample_rate'];
|
||||
if ($thisfile_monkeysaudio['playtime'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt MAC file: playtime == zero';
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['playtime_seconds'] = $thisfile_monkeysaudio['playtime'];
|
||||
$thisfile_monkeysaudio['compressed_size'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'];
|
||||
$thisfile_monkeysaudio['uncompressed_size'] = $thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * ($thisfile_monkeysaudio['bits_per_sample'] / 8);
|
||||
if ($thisfile_monkeysaudio['uncompressed_size'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt MAC file: uncompressed_size == zero';
|
||||
return false;
|
||||
}
|
||||
$thisfile_monkeysaudio['compression_ratio'] = $thisfile_monkeysaudio['compressed_size'] / ($thisfile_monkeysaudio['uncompressed_size'] + $thisfile_monkeysaudio_raw['nHeaderDataBytes']);
|
||||
$thisfile_monkeysaudio['bitrate'] = (($thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * $thisfile_monkeysaudio['bits_per_sample']) / $thisfile_monkeysaudio['playtime']) * $thisfile_monkeysaudio['compression_ratio'];
|
||||
$ThisFileInfo['audio']['bitrate'] = $thisfile_monkeysaudio['bitrate'];
|
||||
|
||||
// add size of MAC header to avdataoffset
|
||||
if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
|
||||
$ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nDescriptorBytes'];
|
||||
$ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderBytes'];
|
||||
$ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nSeekTableBytes'];
|
||||
$ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderDataBytes'];
|
||||
|
||||
$ThisFileInfo['avdataend'] -= $thisfile_monkeysaudio_raw['nTerminatingDataBytes'];
|
||||
} else {
|
||||
$ThisFileInfo['avdataoffset'] += $offset;
|
||||
}
|
||||
|
||||
if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
|
||||
if ($thisfile_monkeysaudio_raw['cFileMD5'] === str_repeat("\x00", 16)) {
|
||||
//$ThisFileInfo['warning'][] = 'cFileMD5 is null';
|
||||
} else {
|
||||
$ThisFileInfo['md5_data_source'] = '';
|
||||
$md5 = $thisfile_monkeysaudio_raw['cFileMD5'];
|
||||
for ($i = 0; $i < strlen($md5); $i++) {
|
||||
$ThisFileInfo['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
|
||||
}
|
||||
if (!preg_match('/^[0-9a-f]{32}$/', $ThisFileInfo['md5_data_source'])) {
|
||||
unset($ThisFileInfo['md5_data_source']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $thisfile_monkeysaudio['bits_per_sample'];
|
||||
$ThisFileInfo['audio']['encoder'] = 'MAC v'.number_format($thisfile_monkeysaudio['version'], 2);
|
||||
$ThisFileInfo['audio']['encoder_options'] = ucfirst($thisfile_monkeysaudio['compression']).' compression';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function MonkeyCompressionLevelNameLookup($compressionlevel) {
|
||||
static $MonkeyCompressionLevelNameLookup = array(
|
||||
0 => 'unknown',
|
||||
1000 => 'fast',
|
||||
2000 => 'normal',
|
||||
3000 => 'high',
|
||||
4000 => 'extra-high',
|
||||
5000 => 'insane'
|
||||
);
|
||||
return (isset($MonkeyCompressionLevelNameLookup[$compressionlevel]) ? $MonkeyCompressionLevelNameLookup[$compressionlevel] : 'invalid');
|
||||
}
|
||||
|
||||
function MonkeySamplesPerFrame($versionid, $compressionlevel) {
|
||||
if ($versionid >= 3950) {
|
||||
return 73728 * 4;
|
||||
} elseif ($versionid >= 3900) {
|
||||
return 73728;
|
||||
} elseif (($versionid >= 3800) && ($compressionlevel == 4000)) {
|
||||
return 73728;
|
||||
} else {
|
||||
return 9216;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,502 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.mpc.php //
|
||||
// module for analyzing Musepack/MPEG+ Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_mpc
|
||||
{
|
||||
|
||||
function getid3_mpc(&$fd, &$ThisFileInfo) {
|
||||
$ThisFileInfo['mpc']['header'] = array();
|
||||
$thisfile_mpc_header = &$ThisFileInfo['mpc']['header'];
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'mpc';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'mpc';
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
|
||||
$ThisFileInfo['audio']['channels'] = 2; // up to SV7 the format appears to have been hardcoded for stereo only
|
||||
$ThisFileInfo['audio']['lossless'] = false;
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$MPCheaderData = fread($fd, 4);
|
||||
$ThisFileInfo['mpc']['header']['preamble'] = substr($MPCheaderData, 0, 4); // should be 'MPCK' (SV8) or 'MP+' (SV7), otherwise possible stream data (SV4-SV6)
|
||||
if (ereg('^MPCK', $ThisFileInfo['mpc']['header']['preamble'])) {
|
||||
|
||||
// this is SV8
|
||||
return $this->ParseMPCsv8($fd, $ThisFileInfo);
|
||||
|
||||
} elseif (ereg('^MP\+', $ThisFileInfo['mpc']['header']['preamble'])) {
|
||||
|
||||
// this is SV7
|
||||
return $this->ParseMPCsv7($fd, $ThisFileInfo);
|
||||
|
||||
} elseif (preg_match('/^[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0]/s', $MPCheaderData)) {
|
||||
|
||||
// this is SV4 - SV6, handle seperately
|
||||
return $this->ParseMPCsv6($fd, $ThisFileInfo);
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['error'][] = 'Expecting "MP+" or "MPCK" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($MPCheaderData, 0, 4).'"';
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
unset($ThisFileInfo['mpc']);
|
||||
return false;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function ParseMPCsv8(&$fd, &$ThisFileInfo) {
|
||||
// this is SV8
|
||||
// http://trac.musepack.net/trac/wiki/SV8Specification
|
||||
|
||||
$thisfile_mpc_header = &$ThisFileInfo['mpc']['header'];
|
||||
|
||||
$keyNameSize = 2;
|
||||
$maxHandledPacketLength = 9; // specs say: "n*8; 0 < n < 10"
|
||||
|
||||
$offset = ftell($fd);
|
||||
while ($offset < $ThisFileInfo['avdataend']) {
|
||||
$thisPacket = array();
|
||||
$thisPacket['offset'] = $offset;
|
||||
$packet_offset = 0;
|
||||
|
||||
// Size is a variable-size field, could be 1-4 bytes (possibly more?)
|
||||
// read enough data in and figure out the exact size later
|
||||
$MPCheaderData = fread($fd, $keyNameSize + $maxHandledPacketLength);
|
||||
$packet_offset += $keyNameSize;
|
||||
$thisPacket['key'] = substr($MPCheaderData, 0, $keyNameSize);
|
||||
$thisPacket['key_name'] = $this->MPCsv8PacketName($thisPacket['key']);
|
||||
if ($thisPacket['key'] == $thisPacket['key_name']) {
|
||||
$ThisFileInfo['error'][] = 'Found unexpected key value "'.$thisPacket['key'].'" at offset '.$thisPacket['offset'];
|
||||
return false;
|
||||
}
|
||||
$packetLength = 0;
|
||||
$thisPacket['packet_size'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $keyNameSize), $packetLength); // includes keyname and packet_size field
|
||||
if ($thisPacket['packet_size'] === false) {
|
||||
$ThisFileInfo['error'][] = 'Did not find expected packet length within '.$maxHandledPacketLength.' bytes at offset '.($thisPacket['offset'] + $keyNameSize);
|
||||
return false;
|
||||
}
|
||||
$packet_offset += $packetLength;
|
||||
$offset += $thisPacket['packet_size'];
|
||||
|
||||
switch ($thisPacket['key']) {
|
||||
case 'SH': // Stream Header
|
||||
$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
|
||||
if ($moreBytesToRead > 0) {
|
||||
$MPCheaderData .= fread($fd, $moreBytesToRead);
|
||||
}
|
||||
$thisPacket['crc'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 4));
|
||||
$packet_offset += 4;
|
||||
$thisPacket['stream_version'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
||||
$packet_offset += 1;
|
||||
|
||||
$packetLength = 0;
|
||||
$thisPacket['sample_count'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
|
||||
$packet_offset += $packetLength;
|
||||
|
||||
$packetLength = 0;
|
||||
$thisPacket['beginning_silence'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
|
||||
$packet_offset += $packetLength;
|
||||
|
||||
$otherUsefulData = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
|
||||
$packet_offset += 2;
|
||||
$thisPacket['sample_frequency_raw'] = (($otherUsefulData & 0xE000) >> 13);
|
||||
$thisPacket['max_bands_used'] = (($otherUsefulData & 0x1F00) >> 8);
|
||||
$thisPacket['channels'] = (($otherUsefulData & 0x00F0) >> 4) + 1;
|
||||
$thisPacket['ms_used'] = (bool) (($otherUsefulData & 0x0008) >> 3);
|
||||
$thisPacket['audio_block_frames'] = (($otherUsefulData & 0x0007) >> 0);
|
||||
$thisPacket['sample_frequency'] = $this->MPCfrequencyLookup($thisPacket['sample_frequency_raw']);
|
||||
|
||||
$thisfile_mpc_header['mid_side_stereo'] = $thisPacket['ms_used'];
|
||||
$thisfile_mpc_header['sample_rate'] = $thisPacket['sample_frequency'];
|
||||
$thisfile_mpc_header['samples'] = $thisPacket['sample_count'];
|
||||
$thisfile_mpc_header['stream_version_major'] = $thisPacket['stream_version'];
|
||||
|
||||
$ThisFileInfo['audio']['channels'] = $thisPacket['channels'];
|
||||
$ThisFileInfo['audio']['sample_rate'] = $thisPacket['sample_frequency'];
|
||||
$ThisFileInfo['playtime_seconds'] = $thisPacket['sample_count'] / $thisPacket['sample_frequency'];
|
||||
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
break;
|
||||
|
||||
case 'RG': // Replay Gain
|
||||
$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
|
||||
if ($moreBytesToRead > 0) {
|
||||
$MPCheaderData .= fread($fd, $moreBytesToRead);
|
||||
}
|
||||
$thisPacket['replaygain_version'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
||||
$packet_offset += 1;
|
||||
$thisPacket['replaygain_title_gain'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
|
||||
$packet_offset += 2;
|
||||
$thisPacket['replaygain_title_peak'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
|
||||
$packet_offset += 2;
|
||||
$thisPacket['replaygain_album_gain'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
|
||||
$packet_offset += 2;
|
||||
$thisPacket['replaygain_album_peak'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
|
||||
$packet_offset += 2;
|
||||
|
||||
if ($thisPacket['replaygain_title_gain']) { $ThisFileInfo['replay_gain']['title']['gain'] = $thisPacket['replaygain_title_gain']; }
|
||||
if ($thisPacket['replaygain_title_peak']) { $ThisFileInfo['replay_gain']['title']['peak'] = $thisPacket['replaygain_title_peak']; }
|
||||
if ($thisPacket['replaygain_album_gain']) { $ThisFileInfo['replay_gain']['album']['gain'] = $thisPacket['replaygain_album_gain']; }
|
||||
if ($thisPacket['replaygain_album_peak']) { $ThisFileInfo['replay_gain']['album']['peak'] = $thisPacket['replaygain_album_peak']; }
|
||||
break;
|
||||
|
||||
case 'EI': // Encoder Info
|
||||
$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
|
||||
if ($moreBytesToRead > 0) {
|
||||
$MPCheaderData .= fread($fd, $moreBytesToRead);
|
||||
}
|
||||
$profile_pns = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
||||
$packet_offset += 1;
|
||||
$quality_int = (($profile_pns & 0xF0) >> 4);
|
||||
$quality_dec = (($profile_pns & 0x0E) >> 3);
|
||||
$thisPacket['quality'] = (float) $quality_int + ($quality_dec / 8);
|
||||
$thisPacket['pns_tool'] = (bool) (($profile_pns & 0x01) >> 0);
|
||||
$thisPacket['version_major'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
||||
$packet_offset += 1;
|
||||
$thisPacket['version_minor'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
||||
$packet_offset += 1;
|
||||
$thisPacket['version_build'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
||||
$packet_offset += 1;
|
||||
$thisPacket['version'] = $thisPacket['version_major'].'.'.$thisPacket['version_minor'].'.'.$thisPacket['version_build'];
|
||||
|
||||
$ThisFileInfo['audio']['encoder'] = 'MPC v'.$thisPacket['version'].' ('.(($thisPacket['version_minor'] % 2) ? 'unstable' : 'stable').')';
|
||||
$thisfile_mpc_header['encoder_version'] = $ThisFileInfo['audio']['encoder'];
|
||||
//$thisfile_mpc_header['quality'] = (float) ($thisPacket['quality'] / 1.5875); // values can range from 0.000 to 15.875, mapped to qualities of 0.0 to 10.0
|
||||
$thisfile_mpc_header['quality'] = (float) ($thisPacket['quality'] - 5); // values can range from 0.000 to 15.875, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0
|
||||
break;
|
||||
|
||||
case 'SO': // Seek Table Offset
|
||||
$packetLength = 0;
|
||||
$thisPacket['seek_table_offset'] = $thisPacket['offset'] + $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
|
||||
$packet_offset += $packetLength;
|
||||
break;
|
||||
|
||||
case 'ST': // Seek Table
|
||||
case 'SE': // Stream End
|
||||
case 'AP': // Audio Data
|
||||
// nothing useful here, just skip this packet
|
||||
$thisPacket = array();
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['error'][] = 'Found unhandled key type "'.$thisPacket['key'].'" at offset '.$thisPacket['offset'];
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
if (!empty($thisPacket)) {
|
||||
$ThisFileInfo['mpc']['packets'][] = $thisPacket;
|
||||
}
|
||||
fseek($fd, $offset);
|
||||
}
|
||||
$thisfile_mpc_header['size'] = $offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
function ParseMPCsv7(&$fd, &$ThisFileInfo) {
|
||||
// this is SV7
|
||||
// http://www.uni-jena.de/~pfk/mpp/sv8/header.html
|
||||
$thisfile_mpc_header = &$ThisFileInfo['mpc']['header'];
|
||||
$offset = 0;
|
||||
|
||||
$thisfile_mpc_header['size'] = 28;
|
||||
$MPCheaderData = $ThisFileInfo['mpc']['header']['preamble'];
|
||||
$MPCheaderData .= fread($fd, $thisfile_mpc_header['size'] - strlen($ThisFileInfo['mpc']['header']['preamble']));
|
||||
$offset = strlen('MP+');
|
||||
|
||||
$StreamVersionByte = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
|
||||
$offset += 1;
|
||||
$thisfile_mpc_header['stream_version_major'] = ($StreamVersionByte & 0x0F) >> 0;
|
||||
$thisfile_mpc_header['stream_version_minor'] = ($StreamVersionByte & 0xF0) >> 4; // should always be 0, subversions no longer exist in SV8
|
||||
$thisfile_mpc_header['frame_count'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
if ($thisfile_mpc_header['stream_version_major'] != 7) {
|
||||
$ThisFileInfo['error'][] = 'Only Musepack SV7 supported (this file claims to be v'.$thisfile_mpc_header['stream_version_major'].')';
|
||||
return false;
|
||||
}
|
||||
|
||||
$FlagsDWORD1 = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_mpc_header['intensity_stereo'] = (bool) (($FlagsDWORD1 & 0x80000000) >> 31);
|
||||
$thisfile_mpc_header['mid_side_stereo'] = (bool) (($FlagsDWORD1 & 0x40000000) >> 30);
|
||||
$thisfile_mpc_header['max_subband'] = ($FlagsDWORD1 & 0x3F000000) >> 24;
|
||||
$thisfile_mpc_header['raw']['profile'] = ($FlagsDWORD1 & 0x00F00000) >> 20;
|
||||
$thisfile_mpc_header['begin_loud'] = (bool) (($FlagsDWORD1 & 0x00080000) >> 19);
|
||||
$thisfile_mpc_header['end_loud'] = (bool) (($FlagsDWORD1 & 0x00040000) >> 18);
|
||||
$thisfile_mpc_header['raw']['sample_rate'] = ($FlagsDWORD1 & 0x00030000) >> 16;
|
||||
$thisfile_mpc_header['max_level'] = ($FlagsDWORD1 & 0x0000FFFF);
|
||||
|
||||
$thisfile_mpc_header['raw']['title_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_mpc_header['raw']['title_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
|
||||
$offset += 2;
|
||||
|
||||
$thisfile_mpc_header['raw']['album_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_mpc_header['raw']['album_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
|
||||
$offset += 2;
|
||||
|
||||
$FlagsDWORD2 = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_mpc_header['true_gapless'] = (bool) (($FlagsDWORD2 & 0x80000000) >> 31);
|
||||
$thisfile_mpc_header['last_frame_length'] = ($FlagsDWORD2 & 0x7FF00000) >> 20;
|
||||
|
||||
|
||||
$thisfile_mpc_header['raw']['not_sure_what'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 3));
|
||||
$offset += 3;
|
||||
$thisfile_mpc_header['raw']['encoder_version'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
|
||||
$offset += 1;
|
||||
|
||||
$thisfile_mpc_header['profile'] = $this->MPCprofileNameLookup($thisfile_mpc_header['raw']['profile']);
|
||||
$thisfile_mpc_header['sample_rate'] = $this->MPCfrequencyLookup($thisfile_mpc_header['raw']['sample_rate']);
|
||||
if ($thisfile_mpc_header['sample_rate'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt MPC file: frequency == zero';
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate'];
|
||||
$thisfile_mpc_header['samples'] = ((($thisfile_mpc_header['frame_count'] - 1) * 1152) + $thisfile_mpc_header['last_frame_length']) * $ThisFileInfo['audio']['channels'];
|
||||
|
||||
$ThisFileInfo['playtime_seconds'] = ($thisfile_mpc_header['samples'] / $ThisFileInfo['audio']['channels']) / $ThisFileInfo['audio']['sample_rate'];
|
||||
if ($ThisFileInfo['playtime_seconds'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt MPC file: playtime_seconds == zero';
|
||||
return false;
|
||||
}
|
||||
|
||||
// add size of file header to avdataoffset - calc bitrate correctly + MD5 data
|
||||
$ThisFileInfo['avdataoffset'] += $thisfile_mpc_header['size'];
|
||||
|
||||
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
|
||||
$thisfile_mpc_header['title_peak'] = $thisfile_mpc_header['raw']['title_peak'];
|
||||
$thisfile_mpc_header['title_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['title_peak']);
|
||||
if ($thisfile_mpc_header['raw']['title_gain'] < 0) {
|
||||
$thisfile_mpc_header['title_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['title_gain']) / -100;
|
||||
} else {
|
||||
$thisfile_mpc_header['title_gain_db'] = (float) $thisfile_mpc_header['raw']['title_gain'] / 100;
|
||||
}
|
||||
|
||||
$thisfile_mpc_header['album_peak'] = $thisfile_mpc_header['raw']['album_peak'];
|
||||
$thisfile_mpc_header['album_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['album_peak']);
|
||||
if ($thisfile_mpc_header['raw']['album_gain'] < 0) {
|
||||
$thisfile_mpc_header['album_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['album_gain']) / -100;
|
||||
} else {
|
||||
$thisfile_mpc_header['album_gain_db'] = (float) $thisfile_mpc_header['raw']['album_gain'] / 100;;
|
||||
}
|
||||
$thisfile_mpc_header['encoder_version'] = $this->MPCencoderVersionLookup($thisfile_mpc_header['raw']['encoder_version']);
|
||||
|
||||
$ThisFileInfo['replay_gain']['track']['adjustment'] = $thisfile_mpc_header['title_gain_db'];
|
||||
$ThisFileInfo['replay_gain']['album']['adjustment'] = $thisfile_mpc_header['album_gain_db'];
|
||||
|
||||
if ($thisfile_mpc_header['title_peak'] > 0) {
|
||||
$ThisFileInfo['replay_gain']['track']['peak'] = $thisfile_mpc_header['title_peak'];
|
||||
} elseif (round($thisfile_mpc_header['max_level'] * 1.18) > 0) {
|
||||
$ThisFileInfo['replay_gain']['track']['peak'] = getid3_lib::CastAsInt(round($thisfile_mpc_header['max_level'] * 1.18)); // why? I don't know - see mppdec.c
|
||||
}
|
||||
if ($thisfile_mpc_header['album_peak'] > 0) {
|
||||
$ThisFileInfo['replay_gain']['album']['peak'] = $thisfile_mpc_header['album_peak'];
|
||||
}
|
||||
|
||||
//$ThisFileInfo['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major'].'.'.$thisfile_mpc_header['stream_version_minor'].', '.$thisfile_mpc_header['encoder_version'];
|
||||
$ThisFileInfo['audio']['encoder'] = $thisfile_mpc_header['encoder_version'];
|
||||
$ThisFileInfo['audio']['encoder_options'] = $thisfile_mpc_header['profile'];
|
||||
$thisfile_mpc_header['quality'] = (float) ($thisfile_mpc_header['raw']['profile'] - 5); // values can range from 0 to 15, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function ParseMPCsv6(&$fd, &$ThisFileInfo) {
|
||||
// this is SV4 - SV6
|
||||
$thisfile_mpc_header = &$ThisFileInfo['mpc']['header'];
|
||||
$offset = 0;
|
||||
|
||||
$thisfile_mpc_header['size'] = 8;
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$MPCheaderData = fread($fd, $thisfile_mpc_header['size']);
|
||||
|
||||
// add size of file header to avdataoffset - calc bitrate correctly + MD5 data
|
||||
$ThisFileInfo['avdataoffset'] += $thisfile_mpc_header['size'];
|
||||
|
||||
// Most of this code adapted from Jurgen Faul's MPEGplus source code - thanks Jurgen! :)
|
||||
$HeaderDWORD[0] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 0, 4));
|
||||
$HeaderDWORD[1] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 4, 4));
|
||||
|
||||
|
||||
// DDDD DDDD CCCC CCCC BBBB BBBB AAAA AAAA
|
||||
// aaaa aaaa abcd dddd dddd deee eeff ffff
|
||||
//
|
||||
// a = bitrate = anything
|
||||
// b = IS = anything
|
||||
// c = MS = anything
|
||||
// d = streamversion = 0000000004 or 0000000005 or 0000000006
|
||||
// e = maxband = anything
|
||||
// f = blocksize = 000001 for SV5+, anything(?) for SV4
|
||||
|
||||
$thisfile_mpc_header['target_bitrate'] = (($HeaderDWORD[0] & 0xFF800000) >> 23);
|
||||
$thisfile_mpc_header['intensity_stereo'] = (bool) (($HeaderDWORD[0] & 0x00400000) >> 22);
|
||||
$thisfile_mpc_header['mid_side_stereo'] = (bool) (($HeaderDWORD[0] & 0x00200000) >> 21);
|
||||
$thisfile_mpc_header['stream_version_major'] = ($HeaderDWORD[0] & 0x001FF800) >> 11;
|
||||
$thisfile_mpc_header['stream_version_minor'] = 0; // no sub-version numbers before SV7
|
||||
$thisfile_mpc_header['max_band'] = ($HeaderDWORD[0] & 0x000007C0) >> 6; // related to lowpass frequency, not sure how it translates exactly
|
||||
$thisfile_mpc_header['block_size'] = ($HeaderDWORD[0] & 0x0000003F);
|
||||
|
||||
switch ($thisfile_mpc_header['stream_version_major']) {
|
||||
case 4:
|
||||
$thisfile_mpc_header['frame_count'] = ($HeaderDWORD[1] >> 16);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
case 6:
|
||||
$thisfile_mpc_header['frame_count'] = $HeaderDWORD[1];
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['error'] = 'Expecting 4, 5 or 6 in version field, found '.$thisfile_mpc_header['stream_version_major'].' instead';
|
||||
unset($ThisFileInfo['mpc']);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (($thisfile_mpc_header['stream_version_major'] > 4) && ($thisfile_mpc_header['block_size'] != 1)) {
|
||||
$ThisFileInfo['warning'][] = 'Block size expected to be 1, actual value found: '.$thisfile_mpc_header['block_size'];
|
||||
}
|
||||
|
||||
$thisfile_mpc_header['sample_rate'] = 44100; // AB: used by all files up to SV7
|
||||
$ThisFileInfo['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate'];
|
||||
$thisfile_mpc_header['samples'] = $thisfile_mpc_header['frame_count'] * 1152 * $ThisFileInfo['audio']['channels'];
|
||||
|
||||
if ($thisfile_mpc_header['target_bitrate'] == 0) {
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
|
||||
} else {
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
|
||||
}
|
||||
|
||||
$ThisFileInfo['mpc']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 * 44100 / $thisfile_mpc_header['frame_count'] / 1152;
|
||||
$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpc']['bitrate'];
|
||||
$ThisFileInfo['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major'];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function MPCprofileNameLookup($profileid) {
|
||||
static $MPCprofileNameLookup = array(
|
||||
0 => 'no profile',
|
||||
1 => 'Experimental',
|
||||
2 => 'unused',
|
||||
3 => 'unused',
|
||||
4 => 'unused',
|
||||
5 => 'below Telephone (q = 0.0)',
|
||||
6 => 'below Telephone (q = 1.0)',
|
||||
7 => 'Telephone (q = 2.0)',
|
||||
8 => 'Thumb (q = 3.0)',
|
||||
9 => 'Radio (q = 4.0)',
|
||||
10 => 'Standard (q = 5.0)',
|
||||
11 => 'Extreme (q = 6.0)',
|
||||
12 => 'Insane (q = 7.0)',
|
||||
13 => 'BrainDead (q = 8.0)',
|
||||
14 => 'above BrainDead (q = 9.0)',
|
||||
15 => 'above BrainDead (q = 10.0)'
|
||||
);
|
||||
return (isset($MPCprofileNameLookup[$profileid]) ? $MPCprofileNameLookup[$profileid] : 'invalid');
|
||||
}
|
||||
|
||||
function MPCfrequencyLookup($frequencyid) {
|
||||
static $MPCfrequencyLookup = array(
|
||||
0 => 44100,
|
||||
1 => 48000,
|
||||
2 => 37800,
|
||||
3 => 32000
|
||||
);
|
||||
return (isset($MPCfrequencyLookup[$frequencyid]) ? $MPCfrequencyLookup[$frequencyid] : 'invalid');
|
||||
}
|
||||
|
||||
function MPCpeakDBLookup($intvalue) {
|
||||
if ($intvalue > 0) {
|
||||
return ((log10($intvalue) / log10(2)) - 15) * 6;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function MPCencoderVersionLookup($encoderversion) {
|
||||
//Encoder version * 100 (106 = 1.06)
|
||||
//EncoderVersion % 10 == 0 Release (1.0)
|
||||
//EncoderVersion % 2 == 0 Beta (1.06)
|
||||
//EncoderVersion % 2 == 1 Alpha (1.05a...z)
|
||||
|
||||
if ($encoderversion == 0) {
|
||||
// very old version, not known exactly which
|
||||
return 'Buschmann v1.7.0-v1.7.9 or Klemm v0.90-v1.05';
|
||||
}
|
||||
|
||||
if (($encoderversion % 10) == 0) {
|
||||
|
||||
// release version
|
||||
return number_format($encoderversion / 100, 2);
|
||||
|
||||
} elseif (($encoderversion % 2) == 0) {
|
||||
|
||||
// beta version
|
||||
return number_format($encoderversion / 100, 2).' beta';
|
||||
|
||||
}
|
||||
|
||||
// alpha version
|
||||
return number_format($encoderversion / 100, 2).' alpha';
|
||||
}
|
||||
|
||||
function SV8variableLengthInteger($data, &$packetLength, $maxHandledPacketLength=9) {
|
||||
$packet_size = 0;
|
||||
for ($packetLength = 1; $packetLength <= $maxHandledPacketLength; $packetLength++) {
|
||||
// variable-length size field:
|
||||
// bits, big-endian
|
||||
// 0xxx xxxx - value 0 to 2^7-1
|
||||
// 1xxx xxxx 0xxx xxxx - value 0 to 2^14-1
|
||||
// 1xxx xxxx 1xxx xxxx 0xxx xxxx - value 0 to 2^21-1
|
||||
// 1xxx xxxx 1xxx xxxx 1xxx xxxx 0xxx xxxx - value 0 to 2^28-1
|
||||
// ...
|
||||
$thisbyte = ord(substr($data, ($packetLength - 1), 1));
|
||||
// look through bytes until find a byte with MSB==0
|
||||
$packet_size = ($packet_size << 7);
|
||||
$packet_size = ($packet_size | ($thisbyte & 0x7F));
|
||||
if (($thisbyte & 0x80) === 0) {
|
||||
break;
|
||||
}
|
||||
if ($packetLength >= $maxHandledPacketLength) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return $packet_size;
|
||||
}
|
||||
|
||||
function MPCsv8PacketName($packetKey) {
|
||||
static $MPCsv8PacketName = array();
|
||||
if (empty($MPCsv8PacketName)) {
|
||||
$MPCsv8PacketName = array(
|
||||
'AP' => 'Audio Packet',
|
||||
'CT' => 'Chapter Tag',
|
||||
'EI' => 'Encoder Info',
|
||||
'RG' => 'Replay Gain',
|
||||
'SE' => 'Stream End',
|
||||
'SH' => 'Stream Header',
|
||||
'SO' => 'Seek Table Offset',
|
||||
'ST' => 'Seek Table',
|
||||
);
|
||||
}
|
||||
return (isset($MPCsv8PacketName[$packetKey]) ? $MPCsv8PacketName[$packetKey] : $packetKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,556 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.ogg.php //
|
||||
// module for analyzing Ogg Vorbis, OggFLAC and Speex files //
|
||||
// dependencies: module.audio.flac.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true);
|
||||
|
||||
class getid3_ogg
|
||||
{
|
||||
|
||||
function getid3_ogg(&$fd, &$ThisFileInfo) {
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'ogg';
|
||||
|
||||
// Warn about illegal tags - only vorbiscomments are allowed
|
||||
if (isset($ThisFileInfo['id3v2'])) {
|
||||
$ThisFileInfo['warning'][] = 'Illegal ID3v2 tag present.';
|
||||
}
|
||||
if (isset($ThisFileInfo['id3v1'])) {
|
||||
$ThisFileInfo['warning'][] = 'Illegal ID3v1 tag present.';
|
||||
}
|
||||
if (isset($ThisFileInfo['ape'])) {
|
||||
$ThisFileInfo['warning'][] = 'Illegal APE tag present.';
|
||||
}
|
||||
|
||||
|
||||
// Page 1 - Stream Header
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
|
||||
$oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
|
||||
|
||||
if (ftell($fd) >= GETID3_FREAD_BUFFER_SIZE) {
|
||||
$ThisFileInfo['error'][] = 'Could not find start of Ogg page in the first '.GETID3_FREAD_BUFFER_SIZE.' bytes (this might not be an Ogg-Vorbis file?)';
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
unset($ThisFileInfo['ogg']);
|
||||
return false;
|
||||
}
|
||||
|
||||
$filedata = fread($fd, $oggpageinfo['page_length']);
|
||||
$filedataoffset = 0;
|
||||
|
||||
if (substr($filedata, 0, 4) == 'fLaC') {
|
||||
|
||||
$ThisFileInfo['audio']['dataformat'] = 'flac';
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
|
||||
$ThisFileInfo['audio']['lossless'] = true;
|
||||
|
||||
} elseif (substr($filedata, 1, 6) == 'vorbis') {
|
||||
|
||||
$this->ParseVorbisPageHeader($filedata, $filedataoffset, $ThisFileInfo, $oggpageinfo);
|
||||
|
||||
} elseif (substr($filedata, 0, 8) == 'Speex ') {
|
||||
|
||||
// http://www.speex.org/manual/node10.html
|
||||
|
||||
$ThisFileInfo['audio']['dataformat'] = 'speex';
|
||||
$ThisFileInfo['mime_type'] = 'audio/speex';
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'abr';
|
||||
$ThisFileInfo['audio']['lossless'] = false;
|
||||
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex '
|
||||
$filedataoffset += 8;
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20);
|
||||
$filedataoffset += 20;
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
|
||||
$ThisFileInfo['speex']['speex_version'] = trim($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']);
|
||||
$ThisFileInfo['speex']['sample_rate'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'];
|
||||
$ThisFileInfo['speex']['channels'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'];
|
||||
$ThisFileInfo['speex']['vbr'] = (bool) $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'];
|
||||
$ThisFileInfo['speex']['band_type'] = getid3_ogg::SpeexBandModeLookup($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']);
|
||||
|
||||
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['speex']['sample_rate'];
|
||||
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['speex']['channels'];
|
||||
if ($ThisFileInfo['speex']['vbr']) {
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['error'][] = 'Expecting either "Speex " or "vorbis" identifier strings, found neither';
|
||||
unset($ThisFileInfo['ogg']);
|
||||
unset($ThisFileInfo['mime_type']);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Page 2 - Comment Header
|
||||
|
||||
$oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
|
||||
|
||||
switch ($ThisFileInfo['audio']['dataformat']) {
|
||||
|
||||
case 'vorbis':
|
||||
$filedata = fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1));
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6); // hard-coded to 'vorbis'
|
||||
|
||||
getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
|
||||
break;
|
||||
|
||||
case 'flac':
|
||||
if (!getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo)) {
|
||||
$ThisFileInfo['error'][] = 'Failed to parse FLAC headers';
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'speex':
|
||||
fseek($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
|
||||
getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Last Page - Number of Samples
|
||||
|
||||
if ($ThisFileInfo['avdataend'] >= pow(2, 31)) {
|
||||
|
||||
$ThisFileInfo['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond 2GB)';
|
||||
|
||||
} else {
|
||||
|
||||
fseek($fd, max($ThisFileInfo['avdataend'] - GETID3_FREAD_BUFFER_SIZE, 0), SEEK_SET);
|
||||
$LastChunkOfOgg = strrev(fread($fd, GETID3_FREAD_BUFFER_SIZE));
|
||||
if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
|
||||
fseek($fd, $ThisFileInfo['avdataend'] - ($LastOggSpostion + strlen('SggO')), SEEK_SET);
|
||||
$ThisFileInfo['avdataend'] = ftell($fd);
|
||||
$ThisFileInfo['ogg']['pageheader']['eos'] = getid3_ogg::ParseOggPageHeader($fd);
|
||||
$ThisFileInfo['ogg']['samples'] = $ThisFileInfo['ogg']['pageheader']['eos']['pcm_abs_position'];
|
||||
if ($ThisFileInfo['ogg']['samples'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt Ogg file: eos.number of samples == zero';
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['ogg']['bitrate_average'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['ogg']['samples'] / $ThisFileInfo['audio']['sample_rate']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!empty($ThisFileInfo['ogg']['bitrate_average'])) {
|
||||
$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_average'];
|
||||
} elseif (!empty($ThisFileInfo['ogg']['bitrate_nominal'])) {
|
||||
$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_nominal'];
|
||||
} elseif (!empty($ThisFileInfo['ogg']['bitrate_min']) && !empty($ThisFileInfo['ogg']['bitrate_max'])) {
|
||||
$ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['ogg']['bitrate_min'] + $ThisFileInfo['ogg']['bitrate_max']) / 2;
|
||||
}
|
||||
if (isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['playtime_seconds'])) {
|
||||
if ($ThisFileInfo['audio']['bitrate'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt Ogg file: bitrate_audio == zero';
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['playtime_seconds'] = (float) ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']);
|
||||
}
|
||||
|
||||
if (isset($ThisFileInfo['ogg']['vendor'])) {
|
||||
$ThisFileInfo['audio']['encoder'] = preg_replace('/^Encoded with /', '', $ThisFileInfo['ogg']['vendor']);
|
||||
|
||||
// Vorbis only
|
||||
if ($ThisFileInfo['audio']['dataformat'] == 'vorbis') {
|
||||
|
||||
// Vorbis 1.0 starts with Xiph.Org
|
||||
if (preg_match('/^Xiph.Org/', $ThisFileInfo['audio']['encoder'])) {
|
||||
|
||||
if ($ThisFileInfo['audio']['bitrate_mode'] == 'abr') {
|
||||
|
||||
// Set -b 128 on abr files
|
||||
$ThisFileInfo['audio']['encoder_options'] = '-b '.round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000);
|
||||
|
||||
} elseif (($ThisFileInfo['audio']['bitrate_mode'] == 'vbr') && ($ThisFileInfo['audio']['channels'] == 2) && ($ThisFileInfo['audio']['sample_rate'] >= 44100) && ($ThisFileInfo['audio']['sample_rate'] <= 48000)) {
|
||||
// Set -q N on vbr files
|
||||
$ThisFileInfo['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($ThisFileInfo['ogg']['bitrate_nominal']);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($ThisFileInfo['audio']['encoder_options']) && !empty($ThisFileInfo['ogg']['bitrate_nominal'])) {
|
||||
$ThisFileInfo['audio']['encoder_options'] = 'Nominal bitrate: '.intval(round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000)).'kbps';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$ThisFileInfo, &$oggpageinfo) {
|
||||
$ThisFileInfo['audio']['dataformat'] = 'vorbis';
|
||||
$ThisFileInfo['audio']['lossless'] = false;
|
||||
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
|
||||
$filedataoffset += 1;
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis'
|
||||
$filedataoffset += 6;
|
||||
$ThisFileInfo['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
|
||||
$filedataoffset += 1;
|
||||
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['ogg']['numberofchannels'];
|
||||
$ThisFileInfo['ogg']['samplerate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
if ($ThisFileInfo['ogg']['samplerate'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt Ogg file: sample rate == zero';
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['ogg']['samplerate'];
|
||||
$ThisFileInfo['ogg']['samples'] = 0; // filled in later
|
||||
$ThisFileInfo['ogg']['bitrate_average'] = 0; // filled in later
|
||||
$ThisFileInfo['ogg']['bitrate_max'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['bitrate_nominal'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['bitrate_min'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$ThisFileInfo['ogg']['blocksize_small'] = pow(2, getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F);
|
||||
$ThisFileInfo['ogg']['blocksize_large'] = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4);
|
||||
$ThisFileInfo['ogg']['stop_bit'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet
|
||||
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr
|
||||
if ($ThisFileInfo['ogg']['bitrate_max'] == 0xFFFFFFFF) {
|
||||
unset($ThisFileInfo['ogg']['bitrate_max']);
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'abr';
|
||||
}
|
||||
if ($ThisFileInfo['ogg']['bitrate_nominal'] == 0xFFFFFFFF) {
|
||||
unset($ThisFileInfo['ogg']['bitrate_nominal']);
|
||||
}
|
||||
if ($ThisFileInfo['ogg']['bitrate_min'] == 0xFFFFFFFF) {
|
||||
unset($ThisFileInfo['ogg']['bitrate_min']);
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'abr';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function ParseOggPageHeader(&$fd) {
|
||||
// http://xiph.org/ogg/vorbis/doc/framing.html
|
||||
$oggheader['page_start_offset'] = ftell($fd); // where we started from in the file
|
||||
|
||||
$filedata = fread($fd, GETID3_FREAD_BUFFER_SIZE);
|
||||
$filedataoffset = 0;
|
||||
while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) {
|
||||
if ((ftell($fd) - $oggheader['page_start_offset']) >= GETID3_FREAD_BUFFER_SIZE) {
|
||||
// should be found before here
|
||||
return false;
|
||||
}
|
||||
if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
|
||||
if (feof($fd) || (($filedata .= fread($fd, GETID3_FREAD_BUFFER_SIZE)) === false)) {
|
||||
// get some more data, unless eof, in which case fail
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
$filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS'
|
||||
|
||||
$oggheader['stream_structver'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
|
||||
$filedataoffset += 1;
|
||||
$oggheader['flags_raw'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
|
||||
$filedataoffset += 1;
|
||||
$oggheader['flags']['fresh'] = (bool) ($oggheader['flags_raw'] & 0x01); // fresh packet
|
||||
$oggheader['flags']['bos'] = (bool) ($oggheader['flags_raw'] & 0x02); // first page of logical bitstream (bos)
|
||||
$oggheader['flags']['eos'] = (bool) ($oggheader['flags_raw'] & 0x04); // last page of logical bitstream (eos)
|
||||
|
||||
$oggheader['pcm_abs_position'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
|
||||
$filedataoffset += 8;
|
||||
$oggheader['stream_serialno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$oggheader['page_seqno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$oggheader['page_checksum'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$oggheader['page_segments'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
|
||||
$filedataoffset += 1;
|
||||
$oggheader['page_length'] = 0;
|
||||
for ($i = 0; $i < $oggheader['page_segments']; $i++) {
|
||||
$oggheader['segment_table'][$i] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
|
||||
$filedataoffset += 1;
|
||||
$oggheader['page_length'] += $oggheader['segment_table'][$i];
|
||||
}
|
||||
$oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset;
|
||||
$oggheader['page_end_offset'] = $oggheader['header_end_offset'] + $oggheader['page_length'];
|
||||
fseek($fd, $oggheader['header_end_offset'], SEEK_SET);
|
||||
|
||||
return $oggheader;
|
||||
}
|
||||
|
||||
|
||||
function ParseVorbisCommentsFilepointer(&$fd, &$ThisFileInfo) {
|
||||
|
||||
$OriginalOffset = ftell($fd);
|
||||
$CommentStartOffset = $OriginalOffset;
|
||||
$commentdataoffset = 0;
|
||||
$VorbisCommentPage = 1;
|
||||
|
||||
switch ($ThisFileInfo['audio']['dataformat']) {
|
||||
case 'vorbis':
|
||||
$CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block
|
||||
fseek($fd, $CommentStartOffset, SEEK_SET);
|
||||
$commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
|
||||
$commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
|
||||
|
||||
$commentdataoffset += (strlen('vorbis') + 1);
|
||||
break;
|
||||
|
||||
case 'flac':
|
||||
fseek($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['offset'] + 4, SEEK_SET);
|
||||
$commentdata = fread($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['block_length']);
|
||||
break;
|
||||
|
||||
case 'speex':
|
||||
$CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block
|
||||
fseek($fd, $CommentStartOffset, SEEK_SET);
|
||||
$commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
|
||||
$commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
$VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
|
||||
$commentdataoffset += 4;
|
||||
|
||||
$ThisFileInfo['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize);
|
||||
$commentdataoffset += $VendorSize;
|
||||
|
||||
$CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
|
||||
$commentdataoffset += 4;
|
||||
$ThisFileInfo['avdataoffset'] = $CommentStartOffset + $commentdataoffset;
|
||||
|
||||
$basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT');
|
||||
for ($i = 0; $i < $CommentsCount; $i++) {
|
||||
|
||||
$ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
|
||||
|
||||
if (ftell($fd) < ($ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + 4)) {
|
||||
$VorbisCommentPage++;
|
||||
|
||||
$oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
|
||||
|
||||
// First, save what we haven't read yet
|
||||
$AsYetUnusedData = substr($commentdata, $commentdataoffset);
|
||||
|
||||
// Then take that data off the end
|
||||
$commentdata = substr($commentdata, 0, $commentdataoffset);
|
||||
|
||||
// Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
|
||||
$commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
|
||||
$commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
|
||||
|
||||
// Finally, stick the unused data back on the end
|
||||
$commentdata .= $AsYetUnusedData;
|
||||
|
||||
//$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
|
||||
$commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1));
|
||||
|
||||
}
|
||||
$ThisFileInfo['ogg']['comments_raw'][$i]['size'] = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
|
||||
|
||||
// replace avdataoffset with position just after the last vorbiscomment
|
||||
$ThisFileInfo['avdataoffset'] = $ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + $ThisFileInfo['ogg']['comments_raw'][$i]['size'] + 4;
|
||||
|
||||
$commentdataoffset += 4;
|
||||
while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo['ogg']['comments_raw'][$i]['size']) {
|
||||
if (($ThisFileInfo['ogg']['comments_raw'][$i]['size'] > $ThisFileInfo['avdataend']) || ($ThisFileInfo['ogg']['comments_raw'][$i]['size'] < 0)) {
|
||||
$ThisFileInfo['error'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo['ogg']['comments_raw'][$i]['size']).' bytes) - aborting reading comments';
|
||||
break 2;
|
||||
}
|
||||
|
||||
$VorbisCommentPage++;
|
||||
|
||||
$oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
|
||||
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
|
||||
|
||||
// First, save what we haven't read yet
|
||||
$AsYetUnusedData = substr($commentdata, $commentdataoffset);
|
||||
|
||||
// Then take that data off the end
|
||||
$commentdata = substr($commentdata, 0, $commentdataoffset);
|
||||
|
||||
// Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
|
||||
$commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
|
||||
$commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
|
||||
|
||||
// Finally, stick the unused data back on the end
|
||||
$commentdata .= $AsYetUnusedData;
|
||||
|
||||
//$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
|
||||
$commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1));
|
||||
|
||||
//$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset'];
|
||||
}
|
||||
$commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo['ogg']['comments_raw'][$i]['size']);
|
||||
$commentdataoffset += $ThisFileInfo['ogg']['comments_raw'][$i]['size'];
|
||||
|
||||
if (!$commentstring) {
|
||||
|
||||
// no comment?
|
||||
$ThisFileInfo['warning'][] = 'Blank Ogg comment ['.$i.']';
|
||||
|
||||
} elseif (strstr($commentstring, '=')) {
|
||||
|
||||
$commentexploded = explode('=', $commentstring, 2);
|
||||
$ThisFileInfo['ogg']['comments_raw'][$i]['key'] = strtoupper($commentexploded[0]);
|
||||
$ThisFileInfo['ogg']['comments_raw'][$i]['value'] = @$commentexploded[1];
|
||||
$ThisFileInfo['ogg']['comments_raw'][$i]['data'] = base64_decode($ThisFileInfo['ogg']['comments_raw'][$i]['value']);
|
||||
|
||||
$ThisFileInfo['ogg']['comments'][strtolower($ThisFileInfo['ogg']['comments_raw'][$i]['key'])][] = $ThisFileInfo['ogg']['comments_raw'][$i]['value'];
|
||||
|
||||
$imageinfo = array();
|
||||
$imagechunkcheck = getid3_lib::GetDataImageSize($ThisFileInfo['ogg']['comments_raw'][$i]['data'], $imageinfo);
|
||||
$ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] = getid3_lib::image_type_to_mime_type($imagechunkcheck[2]);
|
||||
if (!$ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] || ($ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] == 'application/octet-stream')) {
|
||||
unset($ThisFileInfo['ogg']['comments_raw'][$i]['image_mime']);
|
||||
unset($ThisFileInfo['ogg']['comments_raw'][$i]['data']);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Replay Gain Adjustment
|
||||
// http://privatewww.essex.ac.uk/~djmrob/replaygain/
|
||||
if (isset($ThisFileInfo['ogg']['comments']) && is_array($ThisFileInfo['ogg']['comments'])) {
|
||||
foreach ($ThisFileInfo['ogg']['comments'] as $index => $commentvalue) {
|
||||
switch ($index) {
|
||||
case 'rg_audiophile':
|
||||
case 'replaygain_album_gain':
|
||||
$ThisFileInfo['replay_gain']['album']['adjustment'] = (double) $commentvalue[0];
|
||||
unset($ThisFileInfo['ogg']['comments'][$index]);
|
||||
break;
|
||||
|
||||
case 'rg_radio':
|
||||
case 'replaygain_track_gain':
|
||||
$ThisFileInfo['replay_gain']['track']['adjustment'] = (double) $commentvalue[0];
|
||||
unset($ThisFileInfo['ogg']['comments'][$index]);
|
||||
break;
|
||||
|
||||
case 'replaygain_album_peak':
|
||||
$ThisFileInfo['replay_gain']['album']['peak'] = (double) $commentvalue[0];
|
||||
unset($ThisFileInfo['ogg']['comments'][$index]);
|
||||
break;
|
||||
|
||||
case 'rg_peak':
|
||||
case 'replaygain_track_peak':
|
||||
$ThisFileInfo['replay_gain']['track']['peak'] = (double) $commentvalue[0];
|
||||
unset($ThisFileInfo['ogg']['comments'][$index]);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fseek($fd, $OriginalOffset, SEEK_SET);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function SpeexBandModeLookup($mode) {
|
||||
static $SpeexBandModeLookup = array();
|
||||
if (empty($SpeexBandModeLookup)) {
|
||||
$SpeexBandModeLookup[0] = 'narrow';
|
||||
$SpeexBandModeLookup[1] = 'wide';
|
||||
$SpeexBandModeLookup[2] = 'ultra-wide';
|
||||
}
|
||||
return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
|
||||
}
|
||||
|
||||
|
||||
function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
|
||||
for ($i = 0; $i < $SegmentNumber; $i++) {
|
||||
$segmentlength = 0;
|
||||
foreach ($OggInfoArray['segment_table'] as $key => $value) {
|
||||
$segmentlength += $value;
|
||||
if ($value < 255) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $segmentlength;
|
||||
}
|
||||
|
||||
|
||||
function get_quality_from_nominal_bitrate($nominal_bitrate) {
|
||||
|
||||
// decrease precision
|
||||
$nominal_bitrate = $nominal_bitrate / 1000;
|
||||
|
||||
if ($nominal_bitrate < 128) {
|
||||
// q-1 to q4
|
||||
$qval = ($nominal_bitrate - 64) / 16;
|
||||
} elseif ($nominal_bitrate < 256) {
|
||||
// q4 to q8
|
||||
$qval = $nominal_bitrate / 32;
|
||||
} elseif ($nominal_bitrate < 320) {
|
||||
// q8 to q9
|
||||
$qval = ($nominal_bitrate + 256) / 64;
|
||||
} else {
|
||||
// q9 to q10
|
||||
$qval = ($nominal_bitrate + 1300) / 180;
|
||||
}
|
||||
//return $qval; // 5.031324
|
||||
//return intval($qval); // 5
|
||||
return round($qval, 1); // 5 or 4.9
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,408 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.optimfrog.php //
|
||||
// module for analyzing OptimFROG audio files //
|
||||
// dependencies: module.audio.riff.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
||||
|
||||
class getid3_optimfrog
|
||||
{
|
||||
|
||||
function getid3_optimfrog(&$fd, &$ThisFileInfo) {
|
||||
$ThisFileInfo['fileformat'] = 'ofr';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'ofr';
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
|
||||
$ThisFileInfo['audio']['lossless'] = true;
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$OFRheader = fread($fd, 8);
|
||||
if (substr($OFRheader, 0, 5) == '*RIFF') {
|
||||
|
||||
return $this->ParseOptimFROGheader42($fd, $ThisFileInfo);
|
||||
|
||||
} elseif (substr($OFRheader, 0, 3) == 'OFR') {
|
||||
|
||||
return $this->ParseOptimFROGheader45($fd, $ThisFileInfo);
|
||||
|
||||
}
|
||||
|
||||
$ThisFileInfo['error'][] = 'Expecting "*RIFF" or "OFR " at offset '.$ThisFileInfo['avdataoffset'].', found "'.$OFRheader.'"';
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function ParseOptimFROGheader42(&$fd, &$ThisFileInfo) {
|
||||
// for fileformat of v4.21 and older
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$OptimFROGheaderData = fread($fd, 45);
|
||||
$ThisFileInfo['avdataoffset'] = 45;
|
||||
|
||||
$OptimFROGencoderVersion_raw = getid3_lib::LittleEndian2Int(substr($OptimFROGheaderData, 0, 1));
|
||||
$OptimFROGencoderVersion_major = floor($OptimFROGencoderVersion_raw / 10);
|
||||
$OptimFROGencoderVersion_minor = $OptimFROGencoderVersion_raw - ($OptimFROGencoderVersion_major * 10);
|
||||
$RIFFdata = substr($OptimFROGheaderData, 1, 44);
|
||||
$OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8;
|
||||
$OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
|
||||
|
||||
if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
|
||||
$ThisFileInfo['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
|
||||
fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET);
|
||||
$RIFFdata .= fread($fd, $OrignalRIFFheaderSize - $OrignalRIFFdataSize);
|
||||
}
|
||||
|
||||
// move the data chunk after all other chunks (if any)
|
||||
// so that the RIFF parser doesn't see EOF when trying
|
||||
// to skip over the data chunk
|
||||
$RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
|
||||
getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo);
|
||||
|
||||
$ThisFileInfo['audio']['encoder'] = 'OptimFROG '.$OptimFROGencoderVersion_major.'.'.$OptimFROGencoderVersion_minor;
|
||||
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['riff']['audio'][0]['channels'];
|
||||
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['riff']['audio'][0]['sample_rate'];
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['riff']['audio'][0]['bits_per_sample'];
|
||||
$ThisFileInfo['playtime_seconds'] = $OrignalRIFFdataSize / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate'] * ($ThisFileInfo['audio']['bits_per_sample'] / 8));
|
||||
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function ParseOptimFROGheader45(&$fd, &$ThisFileInfo) {
|
||||
// for fileformat of v4.50a and higher
|
||||
|
||||
$RIFFdata = '';
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
while (!feof($fd) && (ftell($fd) < $ThisFileInfo['avdataend'])) {
|
||||
$BlockOffset = ftell($fd);
|
||||
$BlockData = fread($fd, 8);
|
||||
$offset = 8;
|
||||
$BlockName = substr($BlockData, 0, 4);
|
||||
$BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
|
||||
|
||||
if ($BlockName == 'OFRX') {
|
||||
$BlockName = 'OFR ';
|
||||
}
|
||||
if (!isset($ThisFileInfo['ofr'][$BlockName])) {
|
||||
$ThisFileInfo['ofr'][$BlockName] = array();
|
||||
}
|
||||
$thisfile_ofr_thisblock = &$ThisFileInfo['ofr'][$BlockName];
|
||||
|
||||
switch ($BlockName) {
|
||||
case 'OFR ':
|
||||
|
||||
// shortcut
|
||||
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
||||
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
||||
|
||||
$ThisFileInfo['audio']['encoder'] = 'OptimFROG 4.50 alpha';
|
||||
switch ($BlockSize) {
|
||||
case 12:
|
||||
case 15:
|
||||
// good
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['warning'][] = '"'.$BlockName.'" contains more data than expected (expected 12 or 15 bytes, found '.$BlockSize.' bytes)';
|
||||
break;
|
||||
}
|
||||
$BlockData .= fread($fd, $BlockSize);
|
||||
|
||||
$thisfile_ofr_thisblock['total_samples'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 6));
|
||||
$offset += 6;
|
||||
$thisfile_ofr_thisblock['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
|
||||
$thisfile_ofr_thisblock['sample_type'] = $this->OptimFROGsampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
|
||||
$offset += 1;
|
||||
$thisfile_ofr_thisblock['channel_config'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
|
||||
$thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config'];
|
||||
$offset += 1;
|
||||
$thisfile_ofr_thisblock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
if ($BlockSize > 12) {
|
||||
|
||||
// OFR 4.504b or higher
|
||||
$thisfile_ofr_thisblock['channels'] = $this->OptimFROGchannelConfigNumChannelsLookup($thisfile_ofr_thisblock['channel_config']);
|
||||
$thisfile_ofr_thisblock['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
|
||||
$thisfile_ofr_thisblock['encoder'] = $this->OptimFROGencoderNameLookup($thisfile_ofr_thisblock['raw']['encoder_id']);
|
||||
$offset += 2;
|
||||
$thisfile_ofr_thisblock['raw']['compression'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
|
||||
$thisfile_ofr_thisblock['compression'] = $this->OptimFROGcompressionLookup($thisfile_ofr_thisblock['raw']['compression']);
|
||||
$thisfile_ofr_thisblock['speedup'] = $this->OptimFROGspeedupLookup($thisfile_ofr_thisblock['raw']['compression']);
|
||||
$offset += 1;
|
||||
|
||||
$ThisFileInfo['audio']['encoder'] = 'OptimFROG '.$thisfile_ofr_thisblock['encoder'];
|
||||
$ThisFileInfo['audio']['encoder_options'] = '--mode '.$thisfile_ofr_thisblock['compression'];
|
||||
|
||||
if ((($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xF0) >> 4) == 7) { // v4.507
|
||||
if (strtolower(getid3_lib::fileextension($ThisFileInfo['filename'])) == 'ofs') {
|
||||
// OptimFROG DualStream format is lossy, but as of v4.507 there is no way to tell the difference
|
||||
// between lossless and lossy other than the file extension.
|
||||
$ThisFileInfo['audio']['dataformat'] = 'ofs';
|
||||
$ThisFileInfo['audio']['lossless'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$ThisFileInfo['audio']['channels'] = $thisfile_ofr_thisblock['channels'];
|
||||
$ThisFileInfo['audio']['sample_rate'] = $thisfile_ofr_thisblock['sample_rate'];
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
|
||||
break;
|
||||
|
||||
|
||||
case 'COMP':
|
||||
// unlike other block types, there CAN be multiple COMP blocks
|
||||
|
||||
$COMPdata['offset'] = $BlockOffset;
|
||||
$COMPdata['size'] = $BlockSize;
|
||||
|
||||
if ($ThisFileInfo['avdataoffset'] == 0) {
|
||||
$ThisFileInfo['avdataoffset'] = $BlockOffset;
|
||||
}
|
||||
|
||||
// Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data
|
||||
$BlockData .= fread($fd, 14);
|
||||
fseek($fd, $BlockSize - 14, SEEK_CUR);
|
||||
|
||||
$COMPdata['crc_32'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
|
||||
$offset += 4;
|
||||
$COMPdata['sample_count'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
|
||||
$offset += 4;
|
||||
$COMPdata['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
|
||||
$COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']);
|
||||
$offset += 1;
|
||||
$COMPdata['raw']['channel_configuration'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
|
||||
$COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']);
|
||||
$offset += 1;
|
||||
$COMPdata['raw']['algorithm_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
|
||||
//$COMPdata['algorithm'] = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']);
|
||||
$offset += 2;
|
||||
|
||||
if ($ThisFileInfo['ofr']['OFR ']['size'] > 12) {
|
||||
|
||||
// OFR 4.504b or higher
|
||||
$COMPdata['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
|
||||
$COMPdata['encoder'] = $this->OptimFROGencoderNameLookup($COMPdata['raw']['encoder_id']);
|
||||
$offset += 2;
|
||||
|
||||
}
|
||||
|
||||
if ($COMPdata['crc_32'] == 0x454E4F4E) {
|
||||
// ASCII value of 'NONE' - placeholder value in v4.50a
|
||||
$COMPdata['crc_32'] = false;
|
||||
}
|
||||
|
||||
$thisfile_ofr_thisblock[] = $COMPdata;
|
||||
break;
|
||||
|
||||
case 'HEAD':
|
||||
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
||||
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
||||
|
||||
$RIFFdata .= fread($fd, $BlockSize);
|
||||
break;
|
||||
|
||||
case 'TAIL':
|
||||
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
||||
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
||||
|
||||
if ($BlockSize > 0) {
|
||||
$RIFFdata .= fread($fd, $BlockSize);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'RECV':
|
||||
// block contains no useful meta data - simply note and skip
|
||||
|
||||
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
||||
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
||||
|
||||
fseek($fd, $BlockSize, SEEK_CUR);
|
||||
break;
|
||||
|
||||
|
||||
case 'APET':
|
||||
// APEtag v2
|
||||
|
||||
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
||||
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
||||
$ThisFileInfo['warning'][] = 'APEtag processing inside OptimFROG not supported in this version ('.GETID3_VERSION.') of getID3()';
|
||||
|
||||
fseek($fd, $BlockSize, SEEK_CUR);
|
||||
break;
|
||||
|
||||
|
||||
case 'MD5 ':
|
||||
// APEtag v2
|
||||
|
||||
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
||||
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
||||
|
||||
if ($BlockSize == 16) {
|
||||
|
||||
$thisfile_ofr_thisblock['md5_binary'] = fread($fd, $BlockSize);
|
||||
$thisfile_ofr_thisblock['md5_string'] = getid3_lib::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false);
|
||||
$ThisFileInfo['md5_data_source'] = $thisfile_ofr_thisblock['md5_string'];
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found '.$BlockSize.' instead';
|
||||
fseek($fd, $BlockSize, SEEK_CUR);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
||||
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
||||
|
||||
$ThisFileInfo['warning'][] = 'Unhandled OptimFROG block type "'.$BlockName.'" at offset '.$thisfile_ofr_thisblock['offset'];
|
||||
fseek($fd, $BlockSize, SEEK_CUR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isset($ThisFileInfo['ofr']['TAIL']['offset'])) {
|
||||
$ThisFileInfo['avdataend'] = $ThisFileInfo['ofr']['TAIL']['offset'];
|
||||
}
|
||||
|
||||
$ThisFileInfo['playtime_seconds'] = (float) $ThisFileInfo['ofr']['OFR ']['total_samples'] / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate']);
|
||||
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
|
||||
// move the data chunk after all other chunks (if any)
|
||||
// so that the RIFF parser doesn't see EOF when trying
|
||||
// to skip over the data chunk
|
||||
$RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
|
||||
getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function OptimFROGsampleTypeLookup($SampleType) {
|
||||
static $OptimFROGsampleTypeLookup = array(
|
||||
0 => 'unsigned int (8-bit)',
|
||||
1 => 'signed int (8-bit)',
|
||||
2 => 'unsigned int (16-bit)',
|
||||
3 => 'signed int (16-bit)',
|
||||
4 => 'unsigned int (24-bit)',
|
||||
5 => 'signed int (24-bit)',
|
||||
6 => 'unsigned int (32-bit)',
|
||||
7 => 'signed int (32-bit)',
|
||||
8 => 'float 0.24 (32-bit)',
|
||||
9 => 'float 16.8 (32-bit)',
|
||||
10 => 'float 24.0 (32-bit)'
|
||||
);
|
||||
return (isset($OptimFROGsampleTypeLookup[$SampleType]) ? $OptimFROGsampleTypeLookup[$SampleType] : false);
|
||||
}
|
||||
|
||||
function OptimFROGbitsPerSampleTypeLookup($SampleType) {
|
||||
static $OptimFROGbitsPerSampleTypeLookup = array(
|
||||
0 => 8,
|
||||
1 => 8,
|
||||
2 => 16,
|
||||
3 => 16,
|
||||
4 => 24,
|
||||
5 => 24,
|
||||
6 => 32,
|
||||
7 => 32,
|
||||
8 => 32,
|
||||
9 => 32,
|
||||
10 => 32
|
||||
);
|
||||
return (isset($OptimFROGbitsPerSampleTypeLookup[$SampleType]) ? $OptimFROGbitsPerSampleTypeLookup[$SampleType] : false);
|
||||
}
|
||||
|
||||
function OptimFROGchannelConfigurationLookup($ChannelConfiguration) {
|
||||
static $OptimFROGchannelConfigurationLookup = array(
|
||||
0 => 'mono',
|
||||
1 => 'stereo'
|
||||
);
|
||||
return (isset($OptimFROGchannelConfigurationLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigurationLookup[$ChannelConfiguration] : false);
|
||||
}
|
||||
|
||||
function OptimFROGchannelConfigNumChannelsLookup($ChannelConfiguration) {
|
||||
static $OptimFROGchannelConfigNumChannelsLookup = array(
|
||||
0 => 1,
|
||||
1 => 2
|
||||
);
|
||||
return (isset($OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration] : false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// function OptimFROGalgorithmNameLookup($AlgorithID) {
|
||||
// static $OptimFROGalgorithmNameLookup = array();
|
||||
// return (isset($OptimFROGalgorithmNameLookup[$AlgorithID]) ? $OptimFROGalgorithmNameLookup[$AlgorithID] : false);
|
||||
// }
|
||||
|
||||
|
||||
function OptimFROGencoderNameLookup($EncoderID) {
|
||||
// version = (encoderID >> 4) + 4500
|
||||
// system = encoderID & 0xF
|
||||
|
||||
$EncoderVersion = number_format(((($EncoderID & 0xF0) >> 4) + 4500) / 1000, 3);
|
||||
$EncoderSystemID = ($EncoderID & 0x0F);
|
||||
|
||||
static $OptimFROGencoderSystemLookup = array(
|
||||
0x00 => 'Windows console',
|
||||
0x01 => 'Linux console',
|
||||
0x0F => 'unknown'
|
||||
);
|
||||
return $EncoderVersion.' ('.(isset($OptimFROGencoderSystemLookup[$EncoderSystemID]) ? $OptimFROGencoderSystemLookup[$EncoderSystemID] : 'undefined encoder type (0x'.dechex($EncoderSystemID).')').')';
|
||||
}
|
||||
|
||||
function OptimFROGcompressionLookup($CompressionID) {
|
||||
// mode = compression >> 3
|
||||
// speedup = compression & 0x07
|
||||
|
||||
$CompressionModeID = ($CompressionID & 0xF8) >> 3;
|
||||
//$CompressionSpeedupID = ($CompressionID & 0x07);
|
||||
|
||||
static $OptimFROGencoderModeLookup = array(
|
||||
0x00 => 'fast',
|
||||
0x01 => 'normal',
|
||||
0x02 => 'high',
|
||||
0x03 => 'extra', // extranew (some versions)
|
||||
0x04 => 'best', // bestnew (some versions)
|
||||
0x05 => 'ultra',
|
||||
0x06 => 'insane',
|
||||
0x07 => 'highnew',
|
||||
0x08 => 'extranew',
|
||||
0x09 => 'bestnew'
|
||||
);
|
||||
return (isset($OptimFROGencoderModeLookup[$CompressionModeID]) ? $OptimFROGencoderModeLookup[$CompressionModeID] : 'undefined mode (0x'.str_pad(dechex($CompressionModeID), 2, '0', STR_PAD_LEFT).')');
|
||||
}
|
||||
|
||||
function OptimFROGspeedupLookup($CompressionID) {
|
||||
// mode = compression >> 3
|
||||
// speedup = compression & 0x07
|
||||
|
||||
//$CompressionModeID = ($CompressionID & 0xF8) >> 3;
|
||||
$CompressionSpeedupID = ($CompressionID & 0x07);
|
||||
|
||||
static $OptimFROGencoderSpeedupLookup = array(
|
||||
0x00 => '1x',
|
||||
0x01 => '2x',
|
||||
0x02 => '4x'
|
||||
);
|
||||
|
||||
return (isset($OptimFROGencoderSpeedupLookup[$CompressionSpeedupID]) ? $OptimFROGencoderSpeedupLookup[$CompressionSpeedupID] : 'undefined mode (0x'.dechex($CompressionSpeedupID));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.shorten.php //
|
||||
// module for analyzing Shorten Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_rkau
|
||||
{
|
||||
|
||||
function getid3_rkau(&$fd, &$ThisFileInfo) {
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$RKAUHeader = fread($fd, 20);
|
||||
if (substr($RKAUHeader, 0, 3) != 'RKA') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "RKA" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($RKAUHeader, 0, 3).'"';
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'rkau';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'rkau';
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
|
||||
|
||||
$ThisFileInfo['rkau']['raw']['version'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 3, 1));
|
||||
$ThisFileInfo['rkau']['version'] = '1.'.str_pad($ThisFileInfo['rkau']['raw']['version'] & 0x0F, 2, '0', STR_PAD_LEFT);
|
||||
if (($ThisFileInfo['rkau']['version'] > 1.07) || ($ThisFileInfo['rkau']['version'] < 1.06)) {
|
||||
$ThisFileInfo['error'][] = 'This version of getID3() can only parse RKAU files v1.06 and 1.07 (this file is v'.$ThisFileInfo['rkau']['version'].')';
|
||||
unset($ThisFileInfo['rkau']);
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['rkau']['source_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 4, 4));
|
||||
$ThisFileInfo['rkau']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 8, 4));
|
||||
$ThisFileInfo['rkau']['channels'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 12, 1));
|
||||
$ThisFileInfo['rkau']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 13, 1));
|
||||
|
||||
$ThisFileInfo['rkau']['raw']['quality'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 14, 1));
|
||||
$this->RKAUqualityLookup($ThisFileInfo['rkau']);
|
||||
|
||||
$ThisFileInfo['rkau']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 15, 1));
|
||||
$ThisFileInfo['rkau']['flags']['joint_stereo'] = (bool) (!($ThisFileInfo['rkau']['raw']['flags'] & 0x01));
|
||||
$ThisFileInfo['rkau']['flags']['streaming'] = (bool) ($ThisFileInfo['rkau']['raw']['flags'] & 0x02);
|
||||
$ThisFileInfo['rkau']['flags']['vrq_lossy_mode'] = (bool) ($ThisFileInfo['rkau']['raw']['flags'] & 0x04);
|
||||
|
||||
if ($ThisFileInfo['rkau']['flags']['streaming']) {
|
||||
$ThisFileInfo['avdataoffset'] += 20;
|
||||
$ThisFileInfo['rkau']['compressed_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 16, 4));
|
||||
} else {
|
||||
$ThisFileInfo['avdataoffset'] += 16;
|
||||
$ThisFileInfo['rkau']['compressed_bytes'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - 1;
|
||||
}
|
||||
// Note: compressed_bytes does not always equal what appears to be the actual number of compressed bytes,
|
||||
// sometimes it's more, sometimes less. No idea why(?)
|
||||
|
||||
$ThisFileInfo['audio']['lossless'] = $ThisFileInfo['rkau']['lossless'];
|
||||
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['rkau']['channels'];
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['rkau']['bits_per_sample'];
|
||||
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['rkau']['sample_rate'];
|
||||
|
||||
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['rkau']['source_bytes'] / ($ThisFileInfo['rkau']['sample_rate'] * $ThisFileInfo['rkau']['channels'] * ($ThisFileInfo['rkau']['bits_per_sample'] / 8));
|
||||
$ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['rkau']['compressed_bytes'] * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function RKAUqualityLookup(&$RKAUdata) {
|
||||
$level = ($RKAUdata['raw']['quality'] & 0xF0) >> 4;
|
||||
$quality = $RKAUdata['raw']['quality'] & 0x0F;
|
||||
|
||||
$RKAUdata['lossless'] = (($quality == 0) ? true : false);
|
||||
$RKAUdata['compression_level'] = $level + 1;
|
||||
if (!$RKAUdata['lossless']) {
|
||||
$RKAUdata['quality_setting'] = $quality;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,180 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.shorten.php //
|
||||
// module for analyzing Shorten Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_shorten
|
||||
{
|
||||
|
||||
function getid3_shorten(&$fd, &$ThisFileInfo) {
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
|
||||
$ShortenHeader = fread($fd, 8);
|
||||
if (substr($ShortenHeader, 0, 4) != 'ajkg') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "ajkg" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($ShortenHeader, 0, 4).'"';
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo['fileformat'] = 'shn';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'shn';
|
||||
$ThisFileInfo['audio']['lossless'] = true;
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
|
||||
|
||||
$ThisFileInfo['shn']['version'] = getid3_lib::LittleEndian2Int(substr($ShortenHeader, 4, 1));
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataend'] - 12, SEEK_SET);
|
||||
$SeekTableSignatureTest = fread($fd, 12);
|
||||
$ThisFileInfo['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK');
|
||||
if ($ThisFileInfo['shn']['seektable']['present']) {
|
||||
$ThisFileInfo['shn']['seektable']['length'] = getid3_lib::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4));
|
||||
$ThisFileInfo['shn']['seektable']['offset'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['shn']['seektable']['length'];
|
||||
fseek($fd, $ThisFileInfo['shn']['seektable']['offset'], SEEK_SET);
|
||||
$SeekTableMagic = fread($fd, 4);
|
||||
if ($SeekTableMagic != 'SEEK') {
|
||||
|
||||
$ThisFileInfo['error'][] = 'Expecting "SEEK" at offset '.$ThisFileInfo['shn']['seektable']['offset'].', found "'.$SeekTableMagic.'"';
|
||||
return false;
|
||||
|
||||
} else {
|
||||
|
||||
// typedef struct tag_TSeekEntry
|
||||
// {
|
||||
// unsigned long SampleNumber;
|
||||
// unsigned long SHNFileByteOffset;
|
||||
// unsigned long SHNLastBufferReadPosition;
|
||||
// unsigned short SHNByteGet;
|
||||
// unsigned short SHNBufferOffset;
|
||||
// unsigned short SHNFileBitOffset;
|
||||
// unsigned long SHNGBuffer;
|
||||
// unsigned short SHNBitShift;
|
||||
// long CBuf0[3];
|
||||
// long CBuf1[3];
|
||||
// long Offset0[4];
|
||||
// long Offset1[4];
|
||||
// }TSeekEntry;
|
||||
|
||||
$SeekTableData = fread($fd, $ThisFileInfo['shn']['seektable']['length'] - 16);
|
||||
$ThisFileInfo['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80);
|
||||
//$ThisFileInfo['shn']['seektable']['entries'] = array();
|
||||
//$SeekTableOffset = 0;
|
||||
//for ($i = 0; $i < $ThisFileInfo['shn']['seektable']['entry_count']; $i++) {
|
||||
// $SeekTableEntry['sample_number'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
|
||||
// $SeekTableOffset += 4;
|
||||
// $SeekTableEntry['shn_file_byte_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
|
||||
// $SeekTableOffset += 4;
|
||||
// $SeekTableEntry['shn_last_buffer_read_position'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
|
||||
// $SeekTableOffset += 4;
|
||||
// $SeekTableEntry['shn_byte_get'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
|
||||
// $SeekTableOffset += 2;
|
||||
// $SeekTableEntry['shn_buffer_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
|
||||
// $SeekTableOffset += 2;
|
||||
// $SeekTableEntry['shn_file_bit_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
|
||||
// $SeekTableOffset += 2;
|
||||
// $SeekTableEntry['shn_gbuffer'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
|
||||
// $SeekTableOffset += 4;
|
||||
// $SeekTableEntry['shn_bit_shift'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
|
||||
// $SeekTableOffset += 2;
|
||||
// for ($j = 0; $j < 3; $j++) {
|
||||
// $SeekTableEntry['cbuf0'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
|
||||
// $SeekTableOffset += 4;
|
||||
// }
|
||||
// for ($j = 0; $j < 3; $j++) {
|
||||
// $SeekTableEntry['cbuf1'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
|
||||
// $SeekTableOffset += 4;
|
||||
// }
|
||||
// for ($j = 0; $j < 4; $j++) {
|
||||
// $SeekTableEntry['offset0'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
|
||||
// $SeekTableOffset += 4;
|
||||
// }
|
||||
// for ($j = 0; $j < 4; $j++) {
|
||||
// $SeekTableEntry['offset1'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
|
||||
// $SeekTableOffset += 4;
|
||||
// }
|
||||
//
|
||||
// $ThisFileInfo['shn']['seektable']['entries'][] = $SeekTableEntry;
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ((bool) ini_get('safe_mode')) {
|
||||
$ThisFileInfo['error'][] = 'PHP running in Safe Mode - backtick operator not available, cannot run shntool to analyze Shorten files';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GETID3_OS_ISWINDOWS) {
|
||||
|
||||
$RequiredFiles = array('shorten.exe', 'cygwin1.dll', 'head.exe');
|
||||
foreach ($RequiredFiles as $required_file) {
|
||||
if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
|
||||
$ThisFileInfo['error'][] = GETID3_HELPERAPPSDIR.$required_file.' does not exist';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$commandline = GETID3_HELPERAPPSDIR.'shorten.exe -x "'.$ThisFileInfo['filenamepath'].'" - | '.GETID3_HELPERAPPSDIR.'head.exe -c 64';
|
||||
$commandline = str_replace('/', '\\', $commandline);
|
||||
|
||||
} else {
|
||||
|
||||
static $shorten_present;
|
||||
if (!isset($shorten_present)) {
|
||||
$shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`;
|
||||
}
|
||||
if (!$shorten_present) {
|
||||
$ThisFileInfo['error'][] = 'shorten binary was not found in path or /usr/local/bin';
|
||||
return false;
|
||||
}
|
||||
$commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '' ) . 'shorten -x '.escapeshellarg($ThisFileInfo['filenamepath']).' - | head -c 64';
|
||||
|
||||
}
|
||||
|
||||
$output = `$commandline`;
|
||||
|
||||
if (!empty($output) && (substr($output, 12, 4) == 'fmt ')) {
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
||||
|
||||
$fmt_size = getid3_lib::LittleEndian2Int(substr($output, 16, 4));
|
||||
$DecodedWAVFORMATEX = getid3_riff::RIFFparseWAVEFORMATex(substr($output, 20, $fmt_size));
|
||||
$ThisFileInfo['audio']['channels'] = $DecodedWAVFORMATEX['channels'];
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample'];
|
||||
$ThisFileInfo['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate'];
|
||||
|
||||
if (substr($output, 20 + $fmt_size, 4) == 'data') {
|
||||
|
||||
$ThisFileInfo['playtime_seconds'] = getid3_lib::LittleEndian2Int(substr($output, 20 + 4 + $fmt_size, 4)) / $DecodedWAVFORMATEX['raw']['nAvgBytesPerSec'];
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['error'][] = 'shorten failed to decode DATA chunk to expected location, cannot determine playtime';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds']) * 8;
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['error'][] = 'shorten failed to decode file to WAV for parsing';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.tta.php //
|
||||
// module for analyzing TTA Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_tta
|
||||
{
|
||||
|
||||
function getid3_tta(&$fd, &$ThisFileInfo) {
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'tta';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'tta';
|
||||
$ThisFileInfo['audio']['lossless'] = true;
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$ttaheader = fread($fd, 26);
|
||||
|
||||
$ThisFileInfo['tta']['magic'] = substr($ttaheader, 0, 3);
|
||||
if ($ThisFileInfo['tta']['magic'] != 'TTA') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "TTA" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['tta']['magic'].'"';
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
unset($ThisFileInfo['audio']);
|
||||
unset($ThisFileInfo['tta']);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ($ttaheader{3}) {
|
||||
case "\x01": // TTA v1.x
|
||||
case "\x02": // TTA v1.x
|
||||
case "\x03": // TTA v1.x
|
||||
// "It was the demo-version of the TTA encoder. There is no released format with such header. TTA encoder v1 is not supported about a year."
|
||||
$ThisFileInfo['tta']['major_version'] = 1;
|
||||
$ThisFileInfo['avdataoffset'] += 16;
|
||||
|
||||
$ThisFileInfo['tta']['compression_level'] = ord($ttaheader{3});
|
||||
$ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2));
|
||||
$ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
|
||||
$ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 4));
|
||||
$ThisFileInfo['tta']['samples_per_channel'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4));
|
||||
|
||||
$ThisFileInfo['audio']['encoder_options'] = '-e'.$ThisFileInfo['tta']['compression_level'];
|
||||
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['samples_per_channel'] / $ThisFileInfo['tta']['sample_rate'];
|
||||
break;
|
||||
|
||||
case '2': // TTA v2.x
|
||||
// "I have hurried to release the TTA 2.0 encoder. Format documentation is removed from our site. This format still in development. Please wait the TTA2 format, encoder v4."
|
||||
$ThisFileInfo['tta']['major_version'] = 2;
|
||||
$ThisFileInfo['avdataoffset'] += 20;
|
||||
|
||||
$ThisFileInfo['tta']['compression_level'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2));
|
||||
$ThisFileInfo['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
|
||||
$ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2));
|
||||
$ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 2));
|
||||
$ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4));
|
||||
$ThisFileInfo['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 16, 4));
|
||||
|
||||
$ThisFileInfo['audio']['encoder_options'] = '-e'.$ThisFileInfo['tta']['compression_level'];
|
||||
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate'];
|
||||
break;
|
||||
|
||||
case '1': // TTA v3.x
|
||||
// "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher."
|
||||
$ThisFileInfo['tta']['major_version'] = 3;
|
||||
$ThisFileInfo['avdataoffset'] += 26;
|
||||
|
||||
$ThisFileInfo['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); // getid3_riff::RIFFwFormatTagLookup()
|
||||
$ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
|
||||
$ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2));
|
||||
$ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 4));
|
||||
$ThisFileInfo['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 14, 4));
|
||||
$ThisFileInfo['tta']['crc32_footer'] = substr($ttaheader, 18, 4);
|
||||
$ThisFileInfo['tta']['seek_point'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 22, 4));
|
||||
|
||||
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate'];
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['error'][] = 'This version of getID3() only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v'.$ttaheader{3};
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
$ThisFileInfo['audio']['encoder'] = 'TTA v'.$ThisFileInfo['tta']['major_version'];
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['tta']['bits_per_sample'];
|
||||
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['tta']['sample_rate'];
|
||||
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['tta']['channels'];
|
||||
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,205 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.voc.php //
|
||||
// module for analyzing Creative VOC Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_voc
|
||||
{
|
||||
|
||||
function getid3_voc(&$fd, &$ThisFileInfo) {
|
||||
|
||||
$OriginalAVdataOffset = $ThisFileInfo['avdataoffset'];
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$VOCheader = fread($fd, 26);
|
||||
|
||||
if (substr($VOCheader, 0, 19) != 'Creative Voice File') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "Creative Voice File" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($VOCheader, 0, 19).'"';
|
||||
return false;
|
||||
}
|
||||
|
||||
// shortcuts
|
||||
$thisfile_audio = &$ThisFileInfo['audio'];
|
||||
$ThisFileInfo['voc'] = array();
|
||||
$thisfile_voc = &$ThisFileInfo['voc'];
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'voc';
|
||||
$thisfile_audio['dataformat'] = 'voc';
|
||||
$thisfile_audio['bitrate_mode'] = 'cbr';
|
||||
$thisfile_audio['lossless'] = true;
|
||||
$thisfile_audio['channels'] = 1; // might be overriden below
|
||||
$thisfile_audio['bits_per_sample'] = 8; // might be overriden below
|
||||
|
||||
// byte # Description
|
||||
// ------ ------------------------------------------
|
||||
// 00-12 'Creative Voice File'
|
||||
// 13 1A (eof to abort printing of file)
|
||||
// 14-15 Offset of first datablock in .voc file (std 1A 00 in Intel Notation)
|
||||
// 16-17 Version number (minor,major) (VOC-HDR puts 0A 01)
|
||||
// 18-19 2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11)
|
||||
|
||||
$thisfile_voc['header']['datablock_offset'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 20, 2));
|
||||
$thisfile_voc['header']['minor_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 22, 1));
|
||||
$thisfile_voc['header']['major_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 23, 1));
|
||||
|
||||
do {
|
||||
|
||||
$BlockOffset = ftell($fd);
|
||||
$BlockData = fread($fd, 4);
|
||||
$BlockType = ord($BlockData{0});
|
||||
$BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3));
|
||||
$ThisBlock = array();
|
||||
|
||||
@$thisfile_voc['blocktypes'][$BlockType]++;
|
||||
switch ($BlockType) {
|
||||
case 0: // Terminator
|
||||
// do nothing, we'll break out of the loop down below
|
||||
break;
|
||||
|
||||
case 1: // Sound data
|
||||
$BlockData .= fread($fd, 2);
|
||||
if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) {
|
||||
$ThisFileInfo['avdataoffset'] = ftell($fd);
|
||||
}
|
||||
fseek($fd, $BlockSize - 2, SEEK_CUR);
|
||||
|
||||
$ThisBlock['sample_rate_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 1));
|
||||
$ThisBlock['compression_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, 5, 1));
|
||||
|
||||
$ThisBlock['compression_name'] = $this->VOCcompressionTypeLookup($ThisBlock['compression_type']);
|
||||
if ($ThisBlock['compression_type'] <= 3) {
|
||||
$thisfile_voc['compressed_bits_per_sample'] = getid3_lib::CastAsInt(str_replace('-bit', '', $ThisBlock['compression_name']));
|
||||
}
|
||||
|
||||
// Less accurate sample_rate calculation than the Extended block (#8) data (but better than nothing if Extended Block is not available)
|
||||
if (empty($thisfile_audio['sample_rate'])) {
|
||||
// SR byte = 256 - (1000000 / sample_rate)
|
||||
$thisfile_audio['sample_rate'] = getid3_lib::trunc((1000000 / (256 - $ThisBlock['sample_rate_id'])) / $thisfile_audio['channels']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // Sound continue
|
||||
case 3: // Silence
|
||||
case 4: // Marker
|
||||
case 6: // Repeat
|
||||
case 7: // End repeat
|
||||
// nothing useful, just skip
|
||||
fseek($fd, $BlockSize, SEEK_CUR);
|
||||
break;
|
||||
|
||||
case 8: // Extended
|
||||
$BlockData .= fread($fd, 4);
|
||||
|
||||
//00-01 Time Constant:
|
||||
// Mono: 65536 - (256000000 / sample_rate)
|
||||
// Stereo: 65536 - (256000000 / (sample_rate * 2))
|
||||
$ThisBlock['time_constant'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 2));
|
||||
$ThisBlock['pack_method'] = getid3_lib::LittleEndian2Int(substr($BlockData, 6, 1));
|
||||
$ThisBlock['stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BlockData, 7, 1));
|
||||
|
||||
$thisfile_audio['channels'] = ($ThisBlock['stereo'] ? 2 : 1);
|
||||
$thisfile_audio['sample_rate'] = getid3_lib::trunc((256000000 / (65536 - $ThisBlock['time_constant'])) / $thisfile_audio['channels']);
|
||||
break;
|
||||
|
||||
case 9: // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit
|
||||
$BlockData .= fread($fd, 12);
|
||||
if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) {
|
||||
$ThisFileInfo['avdataoffset'] = ftell($fd);
|
||||
}
|
||||
fseek($fd, $BlockSize - 12, SEEK_CUR);
|
||||
|
||||
$ThisBlock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
|
||||
$ThisBlock['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($BlockData, 8, 1));
|
||||
$ThisBlock['channels'] = getid3_lib::LittleEndian2Int(substr($BlockData, 9, 1));
|
||||
$ThisBlock['wFormat'] = getid3_lib::LittleEndian2Int(substr($BlockData, 10, 2));
|
||||
|
||||
$ThisBlock['compression_name'] = $this->VOCwFormatLookup($ThisBlock['wFormat']);
|
||||
if ($this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat'])) {
|
||||
$thisfile_voc['compressed_bits_per_sample'] = $this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat']);
|
||||
}
|
||||
|
||||
$thisfile_audio['sample_rate'] = $ThisBlock['sample_rate'];
|
||||
$thisfile_audio['bits_per_sample'] = $ThisBlock['bits_per_sample'];
|
||||
$thisfile_audio['channels'] = $ThisBlock['channels'];
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['warning'][] = 'Unhandled block type "'.$BlockType.'" at offset '.$BlockOffset;
|
||||
fseek($fd, $BlockSize, SEEK_CUR);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!empty($ThisBlock)) {
|
||||
$ThisBlock['block_offset'] = $BlockOffset;
|
||||
$ThisBlock['block_size'] = $BlockSize;
|
||||
$ThisBlock['block_type_id'] = $BlockType;
|
||||
$thisfile_voc['blocks'][] = $ThisBlock;
|
||||
}
|
||||
|
||||
} while (!feof($fd) && ($BlockType != 0));
|
||||
|
||||
// Terminator block doesn't have size field, so seek back 3 spaces
|
||||
fseek($fd, -3, SEEK_CUR);
|
||||
|
||||
ksort($thisfile_voc['blocktypes']);
|
||||
|
||||
if (!empty($thisfile_voc['compressed_bits_per_sample'])) {
|
||||
$ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($thisfile_voc['compressed_bits_per_sample'] * $thisfile_audio['channels'] * $thisfile_audio['sample_rate']);
|
||||
$thisfile_audio['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function VOCcompressionTypeLookup($index) {
|
||||
static $VOCcompressionTypeLookup = array(
|
||||
0 => '8-bit',
|
||||
1 => '4-bit',
|
||||
2 => '2.6-bit',
|
||||
3 => '2-bit'
|
||||
);
|
||||
return (isset($VOCcompressionTypeLookup[$index]) ? $VOCcompressionTypeLookup[$index] : 'Multi DAC ('.($index - 3).') channels');
|
||||
}
|
||||
|
||||
function VOCwFormatLookup($index) {
|
||||
static $VOCwFormatLookup = array(
|
||||
0x0000 => '8-bit unsigned PCM',
|
||||
0x0001 => 'Creative 8-bit to 4-bit ADPCM',
|
||||
0x0002 => 'Creative 8-bit to 3-bit ADPCM',
|
||||
0x0003 => 'Creative 8-bit to 2-bit ADPCM',
|
||||
0x0004 => '16-bit signed PCM',
|
||||
0x0006 => 'CCITT a-Law',
|
||||
0x0007 => 'CCITT u-Law',
|
||||
0x2000 => 'Creative 16-bit to 4-bit ADPCM'
|
||||
);
|
||||
return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false);
|
||||
}
|
||||
|
||||
function VOCwFormatActualBitsPerSampleLookup($index) {
|
||||
static $VOCwFormatLookup = array(
|
||||
0x0000 => 8,
|
||||
0x0001 => 4,
|
||||
0x0002 => 3,
|
||||
0x0003 => 2,
|
||||
0x0004 => 16,
|
||||
0x0006 => 8,
|
||||
0x0007 => 8,
|
||||
0x2000 => 4
|
||||
);
|
||||
return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.vqf.php //
|
||||
// module for analyzing VQF audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_vqf
|
||||
{
|
||||
function getid3_vqf(&$fd, &$ThisFileInfo) {
|
||||
// based loosely on code from TTwinVQ by Jurgen Faul <jfaulØgmx*de>
|
||||
// http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'vqf';
|
||||
$ThisFileInfo['audio']['dataformat'] = 'vqf';
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
|
||||
$ThisFileInfo['audio']['lossless'] = false;
|
||||
|
||||
// shortcut
|
||||
$ThisFileInfo['vqf']['raw'] = array();
|
||||
$thisfile_vqf = &$ThisFileInfo['vqf'];
|
||||
$thisfile_vqf_raw = &$thisfile_vqf['raw'];
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$VQFheaderData = fread($fd, 16);
|
||||
|
||||
$offset = 0;
|
||||
$thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4);
|
||||
if ($thisfile_vqf_raw['header_tag'] != 'TWIN') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "TWIN" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_vqf_raw['header_tag'].'"';
|
||||
unset($ThisFileInfo['vqf']);
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
return false;
|
||||
}
|
||||
$offset += 4;
|
||||
$thisfile_vqf_raw['version'] = substr($VQFheaderData, $offset, 8);
|
||||
$offset += 8;
|
||||
$thisfile_vqf_raw['size'] = getid3_lib::BigEndian2Int(substr($VQFheaderData, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
while (ftell($fd) < $ThisFileInfo['avdataend']) {
|
||||
|
||||
$ChunkBaseOffset = ftell($fd);
|
||||
$chunkoffset = 0;
|
||||
$ChunkData = fread($fd, 8);
|
||||
$ChunkName = substr($ChunkData, $chunkoffset, 4);
|
||||
if ($ChunkName == 'DATA') {
|
||||
$ThisFileInfo['avdataoffset'] = $ChunkBaseOffset;
|
||||
break;
|
||||
}
|
||||
$chunkoffset += 4;
|
||||
$ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
|
||||
$chunkoffset += 4;
|
||||
if ($ChunkSize > ($ThisFileInfo['avdataend'] - ftell($fd))) {
|
||||
$ThisFileInfo['error'][] = 'Invalid chunk size ('.$ChunkSize.') for chunk "'.$ChunkName.'" at offset '.$ChunkBaseOffset;
|
||||
break;
|
||||
}
|
||||
if ($ChunkSize > 0) {
|
||||
$ChunkData .= fread($fd, $ChunkSize);
|
||||
}
|
||||
|
||||
switch ($ChunkName) {
|
||||
case 'COMM':
|
||||
// shortcut
|
||||
$thisfile_vqf['COMM'] = array();
|
||||
$thisfile_vqf_COMM = &$thisfile_vqf['COMM'];
|
||||
|
||||
$thisfile_vqf_COMM['channel_mode'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
|
||||
$chunkoffset += 4;
|
||||
$thisfile_vqf_COMM['bitrate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
|
||||
$chunkoffset += 4;
|
||||
$thisfile_vqf_COMM['sample_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
|
||||
$chunkoffset += 4;
|
||||
$thisfile_vqf_COMM['security_level'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
|
||||
$chunkoffset += 4;
|
||||
|
||||
$ThisFileInfo['audio']['channels'] = $thisfile_vqf_COMM['channel_mode'] + 1;
|
||||
$ThisFileInfo['audio']['sample_rate'] = $this->VQFchannelFrequencyLookup($thisfile_vqf_COMM['sample_rate']);
|
||||
$ThisFileInfo['audio']['bitrate'] = $thisfile_vqf_COMM['bitrate'] * 1000;
|
||||
$ThisFileInfo['audio']['encoder_options'] = 'CBR' . ceil($ThisFileInfo['audio']['bitrate']/1000);
|
||||
|
||||
if ($ThisFileInfo['audio']['bitrate'] == 0) {
|
||||
$ThisFileInfo['error'][] = 'Corrupt VQF file: bitrate_audio == zero';
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'NAME':
|
||||
case 'AUTH':
|
||||
case '(c) ':
|
||||
case 'FILE':
|
||||
case 'COMT':
|
||||
case 'ALBM':
|
||||
$thisfile_vqf['comments'][$this->VQFcommentNiceNameLookup($ChunkName)][] = trim(substr($ChunkData, 8));
|
||||
break;
|
||||
|
||||
case 'DSIZ':
|
||||
$thisfile_vqf['DSIZ'] = getid3_lib::BigEndian2Int(substr($ChunkData, 8, 4));
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['warning'][] = 'Unhandled chunk type "'.$ChunkName.'" at offset '.$ChunkBaseOffset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate'];
|
||||
|
||||
if (isset($thisfile_vqf['DSIZ']) && (($thisfile_vqf['DSIZ'] != ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA'))))) {
|
||||
switch ($thisfile_vqf['DSIZ']) {
|
||||
case 0:
|
||||
case 1:
|
||||
$ThisFileInfo['warning'][] = 'Invalid DSIZ value "'.$thisfile_vqf['DSIZ'].'". This is known to happen with VQF files encoded by Ahead Nero, and seems to be its way of saying this is TwinVQF v'.($thisfile_vqf['DSIZ'] + 1).'.0';
|
||||
$ThisFileInfo['audio']['encoder'] = 'Ahead Nero';
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['warning'][] = 'Probable corrupted file - should be '.$thisfile_vqf['DSIZ'].' bytes, actually '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function VQFchannelFrequencyLookup($frequencyid) {
|
||||
static $VQFchannelFrequencyLookup = array(
|
||||
11 => 11025,
|
||||
22 => 22050,
|
||||
44 => 44100
|
||||
);
|
||||
return (isset($VQFchannelFrequencyLookup[$frequencyid]) ? $VQFchannelFrequencyLookup[$frequencyid] : $frequencyid * 1000);
|
||||
}
|
||||
|
||||
function VQFcommentNiceNameLookup($shortname) {
|
||||
static $VQFcommentNiceNameLookup = array(
|
||||
'NAME' => 'title',
|
||||
'AUTH' => 'artist',
|
||||
'(c) ' => 'copyright',
|
||||
'FILE' => 'filename',
|
||||
'COMT' => 'comment',
|
||||
'ALBM' => 'album'
|
||||
);
|
||||
return (isset($VQFcommentNiceNameLookup[$shortname]) ? $VQFcommentNiceNameLookup[$shortname] : $shortname);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,372 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.wavpack.php //
|
||||
// module for analyzing WavPack v4.0+ Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_wavpack
|
||||
{
|
||||
|
||||
function getid3_wavpack(&$fd, &$ThisFileInfo) {
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
|
||||
while (true) {
|
||||
|
||||
$wavpackheader = fread($fd, 32);
|
||||
|
||||
if (ftell($fd) >= $ThisFileInfo['avdataend']) {
|
||||
break;
|
||||
} elseif (feof($fd)) {
|
||||
break;
|
||||
} elseif (
|
||||
(@$ThisFileInfo['wavpack']['blockheader']['total_samples'] > 0) &&
|
||||
(@$ThisFileInfo['wavpack']['blockheader']['block_samples'] > 0) &&
|
||||
(!isset($ThisFileInfo['wavpack']['riff_trailer_size']) || ($ThisFileInfo['wavpack']['riff_trailer_size'] <= 0)) &&
|
||||
((@$ThisFileInfo['wavpack']['config_flags']['md5_checksum'] === false) || !empty($ThisFileInfo['md5_data_source']))) {
|
||||
break;
|
||||
}
|
||||
|
||||
$blockheader_offset = ftell($fd) - 32;
|
||||
$blockheader_magic = substr($wavpackheader, 0, 4);
|
||||
$blockheader_size = getid3_lib::LittleEndian2Int(substr($wavpackheader, 4, 4));
|
||||
|
||||
if ($blockheader_magic != 'wvpk') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "wvpk" at offset '.$blockheader_offset.', found "'.$blockheader_magic.'"';
|
||||
if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) {
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
unset($ThisFileInfo['audio']);
|
||||
unset($ThisFileInfo['wavpack']);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if ((@$ThisFileInfo['wavpack']['blockheader']['block_samples'] <= 0) ||
|
||||
(@$ThisFileInfo['wavpack']['blockheader']['total_samples'] <= 0)) {
|
||||
// Also, it is possible that the first block might not have
|
||||
// any samples (block_samples == 0) and in this case you should skip blocks
|
||||
// until you find one with samples because the other information (like
|
||||
// total_samples) are not guaranteed to be correct until (block_samples > 0)
|
||||
|
||||
// Finally, I have defined a format for files in which the length is not known
|
||||
// (for example when raw files are created using pipes). In these cases
|
||||
// total_samples will be -1 and you must seek to the final block to determine
|
||||
// the total number of samples.
|
||||
|
||||
|
||||
$ThisFileInfo['audio']['dataformat'] = 'wavpack';
|
||||
$ThisFileInfo['fileformat'] = 'wavpack';
|
||||
$ThisFileInfo['audio']['lossless'] = true;
|
||||
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
|
||||
|
||||
$ThisFileInfo['wavpack']['blockheader']['offset'] = $blockheader_offset;
|
||||
$ThisFileInfo['wavpack']['blockheader']['magic'] = $blockheader_magic;
|
||||
$ThisFileInfo['wavpack']['blockheader']['size'] = $blockheader_size;
|
||||
|
||||
if ($ThisFileInfo['wavpack']['blockheader']['size'] >= 0x100000) {
|
||||
$ThisFileInfo['error'][] = 'Expecting WavPack block size less than "0x100000", found "'.$ThisFileInfo['wavpack']['blockheader']['size'].'" at offset '.$ThisFileInfo['wavpack']['blockheader']['offset'];
|
||||
if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) {
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
unset($ThisFileInfo['audio']);
|
||||
unset($ThisFileInfo['wavpack']);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['wavpack']['blockheader']['minor_version'] = ord($wavpackheader{8});
|
||||
$ThisFileInfo['wavpack']['blockheader']['major_version'] = ord($wavpackheader{9});
|
||||
|
||||
if (($ThisFileInfo['wavpack']['blockheader']['major_version'] != 4) ||
|
||||
(($ThisFileInfo['wavpack']['blockheader']['minor_version'] < 4) &&
|
||||
($ThisFileInfo['wavpack']['blockheader']['minor_version'] > 16))) {
|
||||
$ThisFileInfo['error'][] = 'Expecting WavPack version between "4.2" and "4.16", found version "'.$ThisFileInfo['wavpack']['blockheader']['major_version'].'.'.$ThisFileInfo['wavpack']['blockheader']['minor_version'].'" at offset '.$ThisFileInfo['wavpack']['blockheader']['offset'];
|
||||
if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) {
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
unset($ThisFileInfo['audio']);
|
||||
unset($ThisFileInfo['wavpack']);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['wavpack']['blockheader']['track_number'] = ord($wavpackheader{10}); // unused
|
||||
$ThisFileInfo['wavpack']['blockheader']['index_number'] = ord($wavpackheader{11}); // unused
|
||||
$ThisFileInfo['wavpack']['blockheader']['total_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 12, 4));
|
||||
$ThisFileInfo['wavpack']['blockheader']['block_index'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 16, 4));
|
||||
$ThisFileInfo['wavpack']['blockheader']['block_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 20, 4));
|
||||
$ThisFileInfo['wavpack']['blockheader']['flags_raw'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 24, 4));
|
||||
$ThisFileInfo['wavpack']['blockheader']['crc'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 28, 4));
|
||||
|
||||
$ThisFileInfo['wavpack']['blockheader']['flags']['bytes_per_sample'] = 1 + ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000003);
|
||||
$ThisFileInfo['wavpack']['blockheader']['flags']['mono'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000004);
|
||||
$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000008);
|
||||
$ThisFileInfo['wavpack']['blockheader']['flags']['joint_stereo'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000010);
|
||||
$ThisFileInfo['wavpack']['blockheader']['flags']['cross_decorrelation'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000020);
|
||||
$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_noiseshape'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000040);
|
||||
$ThisFileInfo['wavpack']['blockheader']['flags']['ieee_32bit_float'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000080);
|
||||
$ThisFileInfo['wavpack']['blockheader']['flags']['int_32bit'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000100);
|
||||
$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_bitrate_noise'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000200);
|
||||
$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_balance_noise'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000400);
|
||||
$ThisFileInfo['wavpack']['blockheader']['flags']['multichannel_initial'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000800);
|
||||
$ThisFileInfo['wavpack']['blockheader']['flags']['multichannel_final'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00001000);
|
||||
|
||||
$ThisFileInfo['audio']['lossless'] = !$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid'];
|
||||
}
|
||||
|
||||
while (!feof($fd) && (ftell($fd) < ($blockheader_offset + $blockheader_size + 8))) {
|
||||
|
||||
$metablock = array('offset'=>ftell($fd));
|
||||
$metablockheader = fread($fd, 2);
|
||||
if (feof($fd)) {
|
||||
break;
|
||||
}
|
||||
$metablock['id'] = ord($metablockheader{0});
|
||||
$metablock['function_id'] = ($metablock['id'] & 0x3F);
|
||||
$metablock['function_name'] = $this->WavPackMetablockNameLookup($metablock['function_id']);
|
||||
|
||||
// The 0x20 bit in the id of the meta subblocks (which is defined as
|
||||
// ID_OPTIONAL_DATA) is a permanent part of the id. The idea is that
|
||||
// if a decoder encounters an id that it does not know about, it uses
|
||||
// that "ID_OPTIONAL_DATA" flag to determine what to do. If it is set
|
||||
// then the decoder simply ignores the metadata, but if it is zero
|
||||
// then the decoder should quit because it means that an understanding
|
||||
// of the metadata is required to correctly decode the audio.
|
||||
$metablock['non_decoder'] = (bool) ($metablock['id'] & 0x20);
|
||||
|
||||
$metablock['padded_data'] = (bool) ($metablock['id'] & 0x40);
|
||||
$metablock['large_block'] = (bool) ($metablock['id'] & 0x80);
|
||||
if ($metablock['large_block']) {
|
||||
$metablockheader .= fread($fd, 2);
|
||||
}
|
||||
$metablock['size'] = getid3_lib::LittleEndian2Int(substr($metablockheader, 1)) * 2; // size is stored in words
|
||||
$metablock['data'] = null;
|
||||
|
||||
if ($metablock['size'] > 0) {
|
||||
|
||||
switch ($metablock['function_id']) {
|
||||
case 0x21: // ID_RIFF_HEADER
|
||||
case 0x22: // ID_RIFF_TRAILER
|
||||
case 0x23: // ID_REPLAY_GAIN
|
||||
case 0x24: // ID_CUESHEET
|
||||
case 0x25: // ID_CONFIG_BLOCK
|
||||
case 0x26: // ID_MD5_CHECKSUM
|
||||
$metablock['data'] = fread($fd, $metablock['size']);
|
||||
|
||||
if ($metablock['padded_data']) {
|
||||
// padded to the nearest even byte
|
||||
$metablock['size']--;
|
||||
$metablock['data'] = substr($metablock['data'], 0, -1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x00: // ID_DUMMY
|
||||
case 0x01: // ID_ENCODER_INFO
|
||||
case 0x02: // ID_DECORR_TERMS
|
||||
case 0x03: // ID_DECORR_WEIGHTS
|
||||
case 0x04: // ID_DECORR_SAMPLES
|
||||
case 0x05: // ID_ENTROPY_VARS
|
||||
case 0x06: // ID_HYBRID_PROFILE
|
||||
case 0x07: // ID_SHAPING_WEIGHTS
|
||||
case 0x08: // ID_FLOAT_INFO
|
||||
case 0x09: // ID_INT32_INFO
|
||||
case 0x0A: // ID_WV_BITSTREAM
|
||||
case 0x0B: // ID_WVC_BITSTREAM
|
||||
case 0x0C: // ID_WVX_BITSTREAM
|
||||
case 0x0D: // ID_CHANNEL_INFO
|
||||
fseek($fd, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET);
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['warning'][] = 'Unexpected metablock type "0x'.str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT).'" at offset '.$metablock['offset'];
|
||||
fseek($fd, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET);
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($metablock['function_id']) {
|
||||
case 0x21: // ID_RIFF_HEADER
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
||||
$original_wav_filesize = getid3_lib::LittleEndian2Int(substr($metablock['data'], 4, 4));
|
||||
getid3_riff::ParseRIFFdata($metablock['data'], $ParsedRIFFheader);
|
||||
$metablock['riff'] = $ParsedRIFFheader['riff'];
|
||||
$metablock['riff']['original_filesize'] = $original_wav_filesize;
|
||||
$ThisFileInfo['wavpack']['riff_trailer_size'] = $original_wav_filesize - $metablock['riff']['WAVE']['data'][0]['size'] - $metablock['riff']['header_size'];
|
||||
|
||||
$ThisFileInfo['audio']['sample_rate'] = $ParsedRIFFheader['riff']['raw']['fmt ']['nSamplesPerSec'];
|
||||
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['wavpack']['blockheader']['total_samples'] / $ThisFileInfo['audio']['sample_rate'];
|
||||
|
||||
// Safe RIFF header in case there's a RIFF footer later
|
||||
$metablockRIFFheader = $metablock['data'];
|
||||
break;
|
||||
|
||||
|
||||
case 0x22: // ID_RIFF_TRAILER
|
||||
$metablockRIFFfooter = $metablockRIFFheader.$metablock['data'];
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
||||
|
||||
$ftell_old = ftell($fd);
|
||||
$startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2);
|
||||
$ParsedRIFFfooter = array('avdataend'=>$ThisFileInfo['avdataend'], 'fileformat'=>'riff', 'error'=>array(), 'warning'=>array());
|
||||
$metablock['riff'] = getid3_riff::ParseRIFF($fd, $startoffset, $startoffset + $metablock['size'], $ParsedRIFFfooter);
|
||||
fseek($fd, $ftell_old, SEEK_SET);
|
||||
|
||||
if (!empty($metablock['riff']['INFO'])) {
|
||||
getid3_riff::RIFFcommentsParse($metablock['riff']['INFO'], $metablock['comments']);
|
||||
$ThisFileInfo['tags']['riff'] = $metablock['comments'];
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 0x23: // ID_REPLAY_GAIN
|
||||
$ThisFileInfo['warning'][] = 'WavPack "Replay Gain" contents not yet handled by getID3() in metablock at offset '.$metablock['offset'];
|
||||
break;
|
||||
|
||||
|
||||
case 0x24: // ID_CUESHEET
|
||||
$ThisFileInfo['warning'][] = 'WavPack "Cuesheet" contents not yet handled by getID3() in metablock at offset '.$metablock['offset'];
|
||||
break;
|
||||
|
||||
|
||||
case 0x25: // ID_CONFIG_BLOCK
|
||||
$metablock['flags_raw'] = getid3_lib::LittleEndian2Int(substr($metablock['data'], 0, 3));
|
||||
|
||||
$metablock['flags']['adobe_mode'] = (bool) ($metablock['flags_raw'] & 0x000001); // "adobe" mode for 32-bit floats
|
||||
$metablock['flags']['fast_flag'] = (bool) ($metablock['flags_raw'] & 0x000002); // fast mode
|
||||
$metablock['flags']['very_fast_flag'] = (bool) ($metablock['flags_raw'] & 0x000004); // double fast
|
||||
$metablock['flags']['high_flag'] = (bool) ($metablock['flags_raw'] & 0x000008); // high quality mode
|
||||
$metablock['flags']['very_high_flag'] = (bool) ($metablock['flags_raw'] & 0x000010); // double high (not used yet)
|
||||
$metablock['flags']['bitrate_kbps'] = (bool) ($metablock['flags_raw'] & 0x000020); // bitrate is kbps, not bits / sample
|
||||
$metablock['flags']['auto_shaping'] = (bool) ($metablock['flags_raw'] & 0x000040); // automatic noise shaping
|
||||
$metablock['flags']['shape_override'] = (bool) ($metablock['flags_raw'] & 0x000080); // shaping mode specified
|
||||
$metablock['flags']['joint_override'] = (bool) ($metablock['flags_raw'] & 0x000100); // joint-stereo mode specified
|
||||
$metablock['flags']['copy_time'] = (bool) ($metablock['flags_raw'] & 0x000200); // copy file-time from source
|
||||
$metablock['flags']['create_exe'] = (bool) ($metablock['flags_raw'] & 0x000400); // create executable
|
||||
$metablock['flags']['create_wvc'] = (bool) ($metablock['flags_raw'] & 0x000800); // create correction file
|
||||
$metablock['flags']['optimize_wvc'] = (bool) ($metablock['flags_raw'] & 0x001000); // maximize bybrid compression
|
||||
$metablock['flags']['quality_mode'] = (bool) ($metablock['flags_raw'] & 0x002000); // psychoacoustic quality mode
|
||||
$metablock['flags']['raw_flag'] = (bool) ($metablock['flags_raw'] & 0x004000); // raw mode (not implemented yet)
|
||||
$metablock['flags']['calc_noise'] = (bool) ($metablock['flags_raw'] & 0x008000); // calc noise in hybrid mode
|
||||
$metablock['flags']['lossy_mode'] = (bool) ($metablock['flags_raw'] & 0x010000); // obsolete (for information)
|
||||
$metablock['flags']['extra_mode'] = (bool) ($metablock['flags_raw'] & 0x020000); // extra processing mode
|
||||
$metablock['flags']['skip_wvx'] = (bool) ($metablock['flags_raw'] & 0x040000); // no wvx stream w/ floats & big ints
|
||||
$metablock['flags']['md5_checksum'] = (bool) ($metablock['flags_raw'] & 0x080000); // compute & store MD5 signature
|
||||
$metablock['flags']['quiet_mode'] = (bool) ($metablock['flags_raw'] & 0x100000); // don't report progress %
|
||||
|
||||
$ThisFileInfo['wavpack']['config_flags'] = $metablock['flags'];
|
||||
|
||||
|
||||
if ($ThisFileInfo['wavpack']['blockheader']['flags']['hybrid']) {
|
||||
@$ThisFileInfo['audio']['encoder_options'] .= ' -b???';
|
||||
}
|
||||
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['adobe_mode'] ? ' -a' : '');
|
||||
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['optimize_wvc'] ? ' -cc' : '');
|
||||
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['create_exe'] ? ' -e' : '');
|
||||
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['fast_flag'] ? ' -f' : '');
|
||||
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['joint_override'] ? ' -j?' : '');
|
||||
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['high_flag'] ? ' -h' : '');
|
||||
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['md5_checksum'] ? ' -m' : '');
|
||||
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['calc_noise'] ? ' -n' : '');
|
||||
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['shape_override'] ? ' -s?' : '');
|
||||
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['extra_mode'] ? ' -x?' : '');
|
||||
if (@$ThisFileInfo['audio']['encoder_options']) {
|
||||
$ThisFileInfo['audio']['encoder_options'] = trim(@$ThisFileInfo['audio']['encoder_options']);
|
||||
}
|
||||
elseif (isset($ThisFileInfo['audio']['encoder_options'])) {
|
||||
unset($ThisFileInfo['audio']['encoder_options']);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 0x26: // ID_MD5_CHECKSUM
|
||||
if (strlen($metablock['data']) == 16) {
|
||||
$ThisFileInfo['md5_data_source'] = strtolower(getid3_lib::PrintHexBytes($metablock['data'], true, false, false));
|
||||
} else {
|
||||
$ThisFileInfo['warning'][] = 'Expecting 16 bytes of WavPack "MD5 Checksum" in metablock at offset '.$metablock['offset'].', but found '.strlen($metablock['data']).' bytes';
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 0x00: // ID_DUMMY
|
||||
case 0x01: // ID_ENCODER_INFO
|
||||
case 0x02: // ID_DECORR_TERMS
|
||||
case 0x03: // ID_DECORR_WEIGHTS
|
||||
case 0x04: // ID_DECORR_SAMPLES
|
||||
case 0x05: // ID_ENTROPY_VARS
|
||||
case 0x06: // ID_HYBRID_PROFILE
|
||||
case 0x07: // ID_SHAPING_WEIGHTS
|
||||
case 0x08: // ID_FLOAT_INFO
|
||||
case 0x09: // ID_INT32_INFO
|
||||
case 0x0A: // ID_WV_BITSTREAM
|
||||
case 0x0B: // ID_WVC_BITSTREAM
|
||||
case 0x0C: // ID_WVX_BITSTREAM
|
||||
case 0x0D: // ID_CHANNEL_INFO
|
||||
unset($metablock);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (!empty($metablock)) {
|
||||
$ThisFileInfo['wavpack']['metablocks'][] = $metablock;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$ThisFileInfo['audio']['encoder'] = 'WavPack v'.$ThisFileInfo['wavpack']['blockheader']['major_version'].'.'.str_pad($ThisFileInfo['wavpack']['blockheader']['minor_version'], 2, '0', STR_PAD_LEFT);
|
||||
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['wavpack']['blockheader']['flags']['bytes_per_sample'] * 8;
|
||||
$ThisFileInfo['audio']['channels'] = ($ThisFileInfo['wavpack']['blockheader']['flags']['mono'] ? 1 : 2);
|
||||
|
||||
if (@$ThisFileInfo['playtime_seconds']) {
|
||||
|
||||
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['audio']['dataformat'] = 'wvc';
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function WavPackMetablockNameLookup(&$id) {
|
||||
static $WavPackMetablockNameLookup = array(
|
||||
0x00 => 'Dummy',
|
||||
0x01 => 'Encoder Info',
|
||||
0x02 => 'Decorrelation Terms',
|
||||
0x03 => 'Decorrelation Weights',
|
||||
0x04 => 'Decorrelation Samples',
|
||||
0x05 => 'Entropy Variables',
|
||||
0x06 => 'Hybrid Profile',
|
||||
0x07 => 'Shaping Weights',
|
||||
0x08 => 'Float Info',
|
||||
0x09 => 'Int32 Info',
|
||||
0x0A => 'WV Bitstream',
|
||||
0x0B => 'WVC Bitstream',
|
||||
0x0C => 'WVX Bitstream',
|
||||
0x0D => 'Channel Info',
|
||||
0x21 => 'RIFF header',
|
||||
0x22 => 'RIFF trailer',
|
||||
0x23 => 'Replay Gain',
|
||||
0x24 => 'Cuesheet',
|
||||
0x25 => 'Config Block',
|
||||
0x26 => 'MD5 Checksum',
|
||||
);
|
||||
return (@$WavPackMetablockNameLookup[$id]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,685 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.graphic.bmp.php //
|
||||
// module for analyzing BMP Image files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_bmp
|
||||
{
|
||||
|
||||
function getid3_bmp(&$fd, &$ThisFileInfo, $ExtractPalette=false, $ExtractData=false) {
|
||||
|
||||
// shortcuts
|
||||
$ThisFileInfo['bmp']['header']['raw'] = array();
|
||||
$thisfile_bmp = &$ThisFileInfo['bmp'];
|
||||
$thisfile_bmp_header = &$thisfile_bmp['header'];
|
||||
$thisfile_bmp_header_raw = &$thisfile_bmp_header['raw'];
|
||||
|
||||
// BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
|
||||
// all versions
|
||||
// WORD bfType;
|
||||
// DWORD bfSize;
|
||||
// WORD bfReserved1;
|
||||
// WORD bfReserved2;
|
||||
// DWORD bfOffBits;
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$offset = 0;
|
||||
$BMPheader = fread($fd, 14 + 40);
|
||||
|
||||
$thisfile_bmp_header_raw['identifier'] = substr($BMPheader, $offset, 2);
|
||||
$offset += 2;
|
||||
|
||||
if ($thisfile_bmp_header_raw['identifier'] != 'BM') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "BM" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_bmp_header_raw['identifier'].'"';
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
unset($ThisFileInfo['bmp']);
|
||||
return false;
|
||||
}
|
||||
|
||||
$thisfile_bmp_header_raw['filesize'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['reserved1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_bmp_header_raw['reserved2'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_bmp_header_raw['data_offset'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['header_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
|
||||
// check if the hardcoded-to-1 "planes" is at offset 22 or 26
|
||||
$planes22 = getid3_lib::LittleEndian2Int(substr($BMPheader, 22, 2));
|
||||
$planes26 = getid3_lib::LittleEndian2Int(substr($BMPheader, 26, 2));
|
||||
if (($planes22 == 1) && ($planes26 != 1)) {
|
||||
$thisfile_bmp['type_os'] = 'OS/2';
|
||||
$thisfile_bmp['type_version'] = 1;
|
||||
} elseif (($planes26 == 1) && ($planes22 != 1)) {
|
||||
$thisfile_bmp['type_os'] = 'Windows';
|
||||
$thisfile_bmp['type_version'] = 1;
|
||||
} elseif ($thisfile_bmp_header_raw['header_size'] == 12) {
|
||||
$thisfile_bmp['type_os'] = 'OS/2';
|
||||
$thisfile_bmp['type_version'] = 1;
|
||||
} elseif ($thisfile_bmp_header_raw['header_size'] == 40) {
|
||||
$thisfile_bmp['type_os'] = 'Windows';
|
||||
$thisfile_bmp['type_version'] = 1;
|
||||
} elseif ($thisfile_bmp_header_raw['header_size'] == 84) {
|
||||
$thisfile_bmp['type_os'] = 'Windows';
|
||||
$thisfile_bmp['type_version'] = 4;
|
||||
} elseif ($thisfile_bmp_header_raw['header_size'] == 100) {
|
||||
$thisfile_bmp['type_os'] = 'Windows';
|
||||
$thisfile_bmp['type_version'] = 5;
|
||||
} else {
|
||||
$ThisFileInfo['error'][] = 'Unknown BMP subtype (or not a BMP file)';
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
unset($ThisFileInfo['bmp']);
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'bmp';
|
||||
$ThisFileInfo['video']['dataformat'] = 'bmp';
|
||||
$ThisFileInfo['video']['lossless'] = true;
|
||||
$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
|
||||
|
||||
if ($thisfile_bmp['type_os'] == 'OS/2') {
|
||||
|
||||
// OS/2-format BMP
|
||||
// http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
|
||||
|
||||
// DWORD Size; /* Size of this structure in bytes */
|
||||
// DWORD Width; /* Bitmap width in pixels */
|
||||
// DWORD Height; /* Bitmap height in pixel */
|
||||
// WORD NumPlanes; /* Number of bit planes (color depth) */
|
||||
// WORD BitsPerPixel; /* Number of bits per pixel per plane */
|
||||
|
||||
$thisfile_bmp_header_raw['width'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_bmp_header_raw['height'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_bmp_header_raw['planes'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_bmp_header_raw['bits_per_pixel'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
|
||||
$offset += 2;
|
||||
|
||||
$ThisFileInfo['video']['resolution_x'] = $thisfile_bmp_header_raw['width'];
|
||||
$ThisFileInfo['video']['resolution_y'] = $thisfile_bmp_header_raw['height'];
|
||||
$ThisFileInfo['video']['codec'] = 'BI_RGB '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
|
||||
$ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
|
||||
|
||||
if ($thisfile_bmp['type_version'] >= 2) {
|
||||
// DWORD Compression; /* Bitmap compression scheme */
|
||||
// DWORD ImageDataSize; /* Size of bitmap data in bytes */
|
||||
// DWORD XResolution; /* X resolution of display device */
|
||||
// DWORD YResolution; /* Y resolution of display device */
|
||||
// DWORD ColorsUsed; /* Number of color table indices used */
|
||||
// DWORD ColorsImportant; /* Number of important color indices */
|
||||
// WORD Units; /* Type of units used to measure resolution */
|
||||
// WORD Reserved; /* Pad structure to 4-byte boundary */
|
||||
// WORD Recording; /* Recording algorithm */
|
||||
// WORD Rendering; /* Halftoning algorithm used */
|
||||
// DWORD Size1; /* Reserved for halftoning algorithm use */
|
||||
// DWORD Size2; /* Reserved for halftoning algorithm use */
|
||||
// DWORD ColorEncoding; /* Color model used in bitmap */
|
||||
// DWORD Identifier; /* Reserved for application use */
|
||||
|
||||
$thisfile_bmp_header_raw['compression'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['bmp_data_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['resolution_h'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['resolution_v'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['colors_used'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['colors_important'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['resolution_units'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_bmp_header_raw['reserved1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_bmp_header_raw['recording'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_bmp_header_raw['rendering'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_bmp_header_raw['size1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['size2'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['color_encoding'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['identifier'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$thisfile_bmp_header['compression'] = $this->BMPcompressionOS2Lookup($thisfile_bmp_header_raw['compression']);
|
||||
|
||||
$ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
|
||||
}
|
||||
|
||||
} elseif ($thisfile_bmp['type_os'] == 'Windows') {
|
||||
|
||||
// Windows-format BMP
|
||||
|
||||
// BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
|
||||
// all versions
|
||||
// DWORD biSize;
|
||||
// LONG biWidth;
|
||||
// LONG biHeight;
|
||||
// WORD biPlanes;
|
||||
// WORD biBitCount;
|
||||
// DWORD biCompression;
|
||||
// DWORD biSizeImage;
|
||||
// LONG biXPelsPerMeter;
|
||||
// LONG biYPelsPerMeter;
|
||||
// DWORD biClrUsed;
|
||||
// DWORD biClrImportant;
|
||||
|
||||
// possibly integrate this section and module.audio-video.riff.php::ParseBITMAPINFOHEADER() ?
|
||||
|
||||
$thisfile_bmp_header_raw['width'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['height'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['planes'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_bmp_header_raw['bits_per_pixel'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_bmp_header_raw['compression'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['bmp_data_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['resolution_h'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['resolution_v'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['colors_used'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['colors_important'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$thisfile_bmp_header['compression'] = $this->BMPcompressionWindowsLookup($thisfile_bmp_header_raw['compression']);
|
||||
$ThisFileInfo['video']['resolution_x'] = $thisfile_bmp_header_raw['width'];
|
||||
$ThisFileInfo['video']['resolution_y'] = $thisfile_bmp_header_raw['height'];
|
||||
$ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
|
||||
$ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
|
||||
|
||||
if (($thisfile_bmp['type_version'] >= 4) || ($thisfile_bmp_header_raw['compression'] == 3)) {
|
||||
// should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen
|
||||
$BMPheader .= fread($fd, 44);
|
||||
|
||||
// BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp
|
||||
// Win95+, WinNT4.0+
|
||||
// DWORD bV4RedMask;
|
||||
// DWORD bV4GreenMask;
|
||||
// DWORD bV4BlueMask;
|
||||
// DWORD bV4AlphaMask;
|
||||
// DWORD bV4CSType;
|
||||
// CIEXYZTRIPLE bV4Endpoints;
|
||||
// DWORD bV4GammaRed;
|
||||
// DWORD bV4GammaGreen;
|
||||
// DWORD bV4GammaBlue;
|
||||
$thisfile_bmp_header_raw['red_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['green_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['blue_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['alpha_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['cs_type'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['ciexyz_red'] = substr($BMPheader, $offset, 4);
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['ciexyz_green'] = substr($BMPheader, $offset, 4);
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['ciexyz_blue'] = substr($BMPheader, $offset, 4);
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['gamma_red'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['gamma_green'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['gamma_blue'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$thisfile_bmp_header['ciexyz_red'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red']));
|
||||
$thisfile_bmp_header['ciexyz_green'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green']));
|
||||
$thisfile_bmp_header['ciexyz_blue'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_blue']));
|
||||
}
|
||||
|
||||
if ($thisfile_bmp['type_version'] >= 5) {
|
||||
$BMPheader .= fread($fd, 16);
|
||||
|
||||
// BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp
|
||||
// Win98+, Win2000+
|
||||
// DWORD bV5Intent;
|
||||
// DWORD bV5ProfileData;
|
||||
// DWORD bV5ProfileSize;
|
||||
// DWORD bV5Reserved;
|
||||
$thisfile_bmp_header_raw['intent'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['profile_data_offset'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['profile_data_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_bmp_header_raw['reserved3'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['error'][] = 'Unknown BMP format in header.';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
if ($ExtractPalette || $ExtractData) {
|
||||
$PaletteEntries = 0;
|
||||
if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) {
|
||||
$PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']);
|
||||
} elseif (isset($thisfile_bmp_header_raw['colors_used']) && ($thisfile_bmp_header_raw['colors_used'] > 0) && ($thisfile_bmp_header_raw['colors_used'] <= 256)) {
|
||||
$PaletteEntries = $thisfile_bmp_header_raw['colors_used'];
|
||||
}
|
||||
if ($PaletteEntries > 0) {
|
||||
$BMPpalette = fread($fd, 4 * $PaletteEntries);
|
||||
$paletteoffset = 0;
|
||||
for ($i = 0; $i < $PaletteEntries; $i++) {
|
||||
// RGBQUAD - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp
|
||||
// BYTE rgbBlue;
|
||||
// BYTE rgbGreen;
|
||||
// BYTE rgbRed;
|
||||
// BYTE rgbReserved;
|
||||
$blue = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
|
||||
$green = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
|
||||
$red = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
|
||||
if (($thisfile_bmp['type_os'] == 'OS/2') && ($thisfile_bmp['type_version'] == 1)) {
|
||||
// no padding byte
|
||||
} else {
|
||||
$paletteoffset++; // padding byte
|
||||
}
|
||||
$thisfile_bmp['palette'][$i] = (($red << 16) | ($green << 8) | $blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($ExtractData) {
|
||||
fseek($fd, $thisfile_bmp_header_raw['data_offset'], SEEK_SET);
|
||||
$RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4; // round up to nearest DWORD boundry
|
||||
$BMPpixelData = fread($fd, $thisfile_bmp_header_raw['height'] * $RowByteLength);
|
||||
$pixeldataoffset = 0;
|
||||
switch (@$thisfile_bmp_header_raw['compression']) {
|
||||
|
||||
case 0: // BI_RGB
|
||||
switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
|
||||
case 1:
|
||||
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
|
||||
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
|
||||
$paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
|
||||
for ($i = 7; $i >= 0; $i--) {
|
||||
$paletteindex = ($paletteindexbyte & (0x01 << $i)) >> $i;
|
||||
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
|
||||
$col++;
|
||||
}
|
||||
}
|
||||
while (($pixeldataoffset % 4) != 0) {
|
||||
// lines are padded to nearest DWORD
|
||||
$pixeldataoffset++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
|
||||
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
|
||||
$paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
|
||||
for ($i = 1; $i >= 0; $i--) {
|
||||
$paletteindex = ($paletteindexbyte & (0x0F << (4 * $i))) >> (4 * $i);
|
||||
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
|
||||
$col++;
|
||||
}
|
||||
}
|
||||
while (($pixeldataoffset % 4) != 0) {
|
||||
// lines are padded to nearest DWORD
|
||||
$pixeldataoffset++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 8:
|
||||
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
|
||||
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
|
||||
$paletteindex = ord($BMPpixelData{$pixeldataoffset++});
|
||||
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
|
||||
}
|
||||
while (($pixeldataoffset % 4) != 0) {
|
||||
// lines are padded to nearest DWORD
|
||||
$pixeldataoffset++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 24:
|
||||
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
|
||||
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
|
||||
$thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
|
||||
$pixeldataoffset += 3;
|
||||
}
|
||||
while (($pixeldataoffset % 4) != 0) {
|
||||
// lines are padded to nearest DWORD
|
||||
$pixeldataoffset++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 32:
|
||||
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
|
||||
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
|
||||
$thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+3}) << 24) | (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
|
||||
$pixeldataoffset += 4;
|
||||
}
|
||||
while (($pixeldataoffset % 4) != 0) {
|
||||
// lines are padded to nearest DWORD
|
||||
$pixeldataoffset++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 16:
|
||||
// ?
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 1: // BI_RLE8 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
|
||||
switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
|
||||
case 8:
|
||||
$pixelcounter = 0;
|
||||
while ($pixeldataoffset < strlen($BMPpixelData)) {
|
||||
$firstbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
|
||||
$secondbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
|
||||
if ($firstbyte == 0) {
|
||||
|
||||
// escaped/absolute mode - the first byte of the pair can be set to zero to
|
||||
// indicate an escape character that denotes the end of a line, the end of
|
||||
// a bitmap, or a delta, depending on the value of the second byte.
|
||||
switch ($secondbyte) {
|
||||
case 0:
|
||||
// end of line
|
||||
// no need for special processing, just ignore
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// end of bitmap
|
||||
$pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// delta - The 2 bytes following the escape contain unsigned values
|
||||
// indicating the horizontal and vertical offsets of the next pixel
|
||||
// from the current position.
|
||||
$colincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
|
||||
$rowincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
|
||||
$col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
|
||||
$row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement;
|
||||
$pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col;
|
||||
break;
|
||||
|
||||
default:
|
||||
// In absolute mode, the first byte is zero and the second byte is a
|
||||
// value in the range 03H through FFH. The second byte represents the
|
||||
// number of bytes that follow, each of which contains the color index
|
||||
// of a single pixel. Each run must be aligned on a word boundary.
|
||||
for ($i = 0; $i < $secondbyte; $i++) {
|
||||
$paletteindex = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
|
||||
$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
|
||||
$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
|
||||
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
|
||||
$pixelcounter++;
|
||||
}
|
||||
while (($pixeldataoffset % 2) != 0) {
|
||||
// Each run must be aligned on a word boundary.
|
||||
$pixeldataoffset++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// encoded mode - the first byte specifies the number of consecutive pixels
|
||||
// to be drawn using the color index contained in the second byte.
|
||||
for ($i = 0; $i < $firstbyte; $i++) {
|
||||
$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
|
||||
$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
|
||||
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$secondbyte];
|
||||
$pixelcounter++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 2: // BI_RLE4 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
|
||||
switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
|
||||
case 4:
|
||||
$pixelcounter = 0;
|
||||
while ($pixeldataoffset < strlen($BMPpixelData)) {
|
||||
$firstbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
|
||||
$secondbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
|
||||
if ($firstbyte == 0) {
|
||||
|
||||
// escaped/absolute mode - the first byte of the pair can be set to zero to
|
||||
// indicate an escape character that denotes the end of a line, the end of
|
||||
// a bitmap, or a delta, depending on the value of the second byte.
|
||||
switch ($secondbyte) {
|
||||
case 0:
|
||||
// end of line
|
||||
// no need for special processing, just ignore
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// end of bitmap
|
||||
$pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// delta - The 2 bytes following the escape contain unsigned values
|
||||
// indicating the horizontal and vertical offsets of the next pixel
|
||||
// from the current position.
|
||||
$colincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
|
||||
$rowincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
|
||||
$col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
|
||||
$row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement;
|
||||
$pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col;
|
||||
break;
|
||||
|
||||
default:
|
||||
// In absolute mode, the first byte is zero. The second byte contains the number
|
||||
// of color indexes that follow. Subsequent bytes contain color indexes in their
|
||||
// high- and low-order 4 bits, one color index for each pixel. In absolute mode,
|
||||
// each run must be aligned on a word boundary.
|
||||
unset($paletteindexes);
|
||||
for ($i = 0; $i < ceil($secondbyte / 2); $i++) {
|
||||
$paletteindexbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
|
||||
$paletteindexes[] = ($paletteindexbyte & 0xF0) >> 4;
|
||||
$paletteindexes[] = ($paletteindexbyte & 0x0F);
|
||||
}
|
||||
while (($pixeldataoffset % 2) != 0) {
|
||||
// Each run must be aligned on a word boundary.
|
||||
$pixeldataoffset++;
|
||||
}
|
||||
|
||||
foreach ($paletteindexes as $paletteindex) {
|
||||
$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
|
||||
$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
|
||||
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
|
||||
$pixelcounter++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// encoded mode - the first byte of the pair contains the number of pixels to be
|
||||
// drawn using the color indexes in the second byte. The second byte contains two
|
||||
// color indexes, one in its high-order 4 bits and one in its low-order 4 bits.
|
||||
// The first of the pixels is drawn using the color specified by the high-order
|
||||
// 4 bits, the second is drawn using the color in the low-order 4 bits, the third
|
||||
// is drawn using the color in the high-order 4 bits, and so on, until all the
|
||||
// pixels specified by the first byte have been drawn.
|
||||
$paletteindexes[0] = ($secondbyte & 0xF0) >> 4;
|
||||
$paletteindexes[1] = ($secondbyte & 0x0F);
|
||||
for ($i = 0; $i < $firstbyte; $i++) {
|
||||
$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
|
||||
$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
|
||||
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindexes[($i % 2)]];
|
||||
$pixelcounter++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 3: // BI_BITFIELDS
|
||||
switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
|
||||
case 16:
|
||||
case 32:
|
||||
$redshift = 0;
|
||||
$greenshift = 0;
|
||||
$blueshift = 0;
|
||||
while ((($thisfile_bmp_header_raw['red_mask'] >> $redshift) & 0x01) == 0) {
|
||||
$redshift++;
|
||||
}
|
||||
while ((($thisfile_bmp_header_raw['green_mask'] >> $greenshift) & 0x01) == 0) {
|
||||
$greenshift++;
|
||||
}
|
||||
while ((($thisfile_bmp_header_raw['blue_mask'] >> $blueshift) & 0x01) == 0) {
|
||||
$blueshift++;
|
||||
}
|
||||
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
|
||||
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
|
||||
$pixelvalue = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset, $thisfile_bmp_header_raw['bits_per_pixel'] / 8));
|
||||
$pixeldataoffset += $thisfile_bmp_header_raw['bits_per_pixel'] / 8;
|
||||
|
||||
$red = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['red_mask']) >> $redshift) / ($thisfile_bmp_header_raw['red_mask'] >> $redshift)) * 255));
|
||||
$green = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['green_mask']) >> $greenshift) / ($thisfile_bmp_header_raw['green_mask'] >> $greenshift)) * 255));
|
||||
$blue = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['blue_mask']) >> $blueshift) / ($thisfile_bmp_header_raw['blue_mask'] >> $blueshift)) * 255));
|
||||
$thisfile_bmp['data'][$row][$col] = (($red << 16) | ($green << 8) | ($blue));
|
||||
}
|
||||
while (($pixeldataoffset % 4) != 0) {
|
||||
// lines are padded to nearest DWORD
|
||||
$pixeldataoffset++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default: // unhandled compression type
|
||||
$ThisFileInfo['error'][] = 'Unknown/unhandled compression type value ('.$thisfile_bmp_header_raw['compression'].') - cannot decompress pixel data';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function PlotBMP(&$BMPinfo) {
|
||||
$starttime = time();
|
||||
if (!isset($BMPinfo['bmp']['data']) || !is_array($BMPinfo['bmp']['data'])) {
|
||||
echo 'ERROR: no pixel data<BR>';
|
||||
return false;
|
||||
}
|
||||
set_time_limit(intval(round($BMPinfo['resolution_x'] * $BMPinfo['resolution_y'] / 10000)));
|
||||
if ($im = ImageCreateTrueColor($BMPinfo['resolution_x'], $BMPinfo['resolution_y'])) {
|
||||
for ($row = 0; $row < $BMPinfo['resolution_y']; $row++) {
|
||||
for ($col = 0; $col < $BMPinfo['resolution_x']; $col++) {
|
||||
if (isset($BMPinfo['bmp']['data'][$row][$col])) {
|
||||
$red = ($BMPinfo['bmp']['data'][$row][$col] & 0x00FF0000) >> 16;
|
||||
$green = ($BMPinfo['bmp']['data'][$row][$col] & 0x0000FF00) >> 8;
|
||||
$blue = ($BMPinfo['bmp']['data'][$row][$col] & 0x000000FF);
|
||||
$pixelcolor = ImageColorAllocate($im, $red, $green, $blue);
|
||||
ImageSetPixel($im, $col, $row, $pixelcolor);
|
||||
} else {
|
||||
//echo 'ERROR: no data for pixel '.$row.' x '.$col.'<BR>';
|
||||
//return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (headers_sent()) {
|
||||
echo 'plotted '.($BMPinfo['resolution_x'] * $BMPinfo['resolution_y']).' pixels in '.(time() - $starttime).' seconds<BR>';
|
||||
ImageDestroy($im);
|
||||
exit;
|
||||
} else {
|
||||
header('Content-type: image/png');
|
||||
ImagePNG($im);
|
||||
ImageDestroy($im);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function BMPcompressionWindowsLookup($compressionid) {
|
||||
static $BMPcompressionWindowsLookup = array(
|
||||
0 => 'BI_RGB',
|
||||
1 => 'BI_RLE8',
|
||||
2 => 'BI_RLE4',
|
||||
3 => 'BI_BITFIELDS',
|
||||
4 => 'BI_JPEG',
|
||||
5 => 'BI_PNG'
|
||||
);
|
||||
return (isset($BMPcompressionWindowsLookup[$compressionid]) ? $BMPcompressionWindowsLookup[$compressionid] : 'invalid');
|
||||
}
|
||||
|
||||
function BMPcompressionOS2Lookup($compressionid) {
|
||||
static $BMPcompressionOS2Lookup = array(
|
||||
0 => 'BI_RGB',
|
||||
1 => 'BI_RLE8',
|
||||
2 => 'BI_RLE4',
|
||||
3 => 'Huffman 1D',
|
||||
4 => 'BI_RLE24',
|
||||
);
|
||||
return (isset($BMPcompressionOS2Lookup[$compressionid]) ? $BMPcompressionOS2Lookup[$compressionid] : 'invalid');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.graphic.gif.php //
|
||||
// module for analyzing GIF Image files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_gif
|
||||
{
|
||||
|
||||
function getid3_gif(&$fd, &$ThisFileInfo) {
|
||||
$ThisFileInfo['fileformat'] = 'gif';
|
||||
$ThisFileInfo['video']['dataformat'] = 'gif';
|
||||
$ThisFileInfo['video']['lossless'] = true;
|
||||
$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$GIFheader = fread($fd, 13);
|
||||
$offset = 0;
|
||||
|
||||
$ThisFileInfo['gif']['header']['raw']['identifier'] = substr($GIFheader, $offset, 3);
|
||||
$offset += 3;
|
||||
|
||||
if ($ThisFileInfo['gif']['header']['raw']['identifier'] != 'GIF') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "GIF" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['gif']['header']['raw']['identifier'].'"';
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
unset($ThisFileInfo['gif']);
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['gif']['header']['raw']['version'] = substr($GIFheader, $offset, 3);
|
||||
$offset += 3;
|
||||
$ThisFileInfo['gif']['header']['raw']['width'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$ThisFileInfo['gif']['header']['raw']['height'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$ThisFileInfo['gif']['header']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
|
||||
$offset += 1;
|
||||
$ThisFileInfo['gif']['header']['raw']['bg_color_index'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
|
||||
$offset += 1;
|
||||
$ThisFileInfo['gif']['header']['raw']['aspect_ratio'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
|
||||
$offset += 1;
|
||||
|
||||
$ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['gif']['header']['raw']['width'];
|
||||
$ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['gif']['header']['raw']['height'];
|
||||
$ThisFileInfo['gif']['version'] = $ThisFileInfo['gif']['header']['raw']['version'];
|
||||
$ThisFileInfo['gif']['header']['flags']['global_color_table'] = (bool) ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x80);
|
||||
if ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x80) {
|
||||
// Number of bits per primary color available to the original image, minus 1
|
||||
$ThisFileInfo['gif']['header']['bits_per_pixel'] = 3 * ((($ThisFileInfo['gif']['header']['raw']['flags'] & 0x70) >> 4) + 1);
|
||||
} else {
|
||||
$ThisFileInfo['gif']['header']['bits_per_pixel'] = 0;
|
||||
}
|
||||
$ThisFileInfo['gif']['header']['flags']['global_color_sorted'] = (bool) ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x40);
|
||||
if ($ThisFileInfo['gif']['header']['flags']['global_color_table']) {
|
||||
// the number of bytes contained in the Global Color Table. To determine that
|
||||
// actual size of the color table, raise 2 to [the value of the field + 1]
|
||||
$ThisFileInfo['gif']['header']['global_color_size'] = pow(2, ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x07) + 1);
|
||||
$ThisFileInfo['video']['bits_per_sample'] = ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x07) + 1;
|
||||
} else {
|
||||
$ThisFileInfo['gif']['header']['global_color_size'] = 0;
|
||||
}
|
||||
if ($ThisFileInfo['gif']['header']['raw']['aspect_ratio'] != 0) {
|
||||
// Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
|
||||
$ThisFileInfo['gif']['header']['aspect_ratio'] = ($ThisFileInfo['gif']['header']['raw']['aspect_ratio'] + 15) / 64;
|
||||
}
|
||||
|
||||
// if ($ThisFileInfo['gif']['header']['flags']['global_color_table']) {
|
||||
// $GIFcolorTable = fread($fd, 3 * $ThisFileInfo['gif']['header']['global_color_size']);
|
||||
// $offset = 0;
|
||||
// for ($i = 0; $i < $ThisFileInfo['gif']['header']['global_color_size']; $i++) {
|
||||
// $red = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
|
||||
// $green = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
|
||||
// $blue = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
|
||||
// $ThisFileInfo['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Image Descriptor
|
||||
// while (!feof($fd)) {
|
||||
// $NextBlockTest = fread($fd, 1);
|
||||
// switch ($NextBlockTest) {
|
||||
//
|
||||
// case ',': // ',' - Image separator character
|
||||
//
|
||||
// $ImageDescriptorData = $NextBlockTest.fread($fd, 9);
|
||||
// $ImageDescriptor = array();
|
||||
// $ImageDescriptor['image_left'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 1, 2));
|
||||
// $ImageDescriptor['image_top'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 3, 2));
|
||||
// $ImageDescriptor['image_width'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 5, 2));
|
||||
// $ImageDescriptor['image_height'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 7, 2));
|
||||
// $ImageDescriptor['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 9, 1));
|
||||
// $ImageDescriptor['flags']['use_local_color_map'] = (bool) ($ImageDescriptor['flags_raw'] & 0x80);
|
||||
// $ImageDescriptor['flags']['image_interlaced'] = (bool) ($ImageDescriptor['flags_raw'] & 0x40);
|
||||
// $ThisFileInfo['gif']['image_descriptor'][] = $ImageDescriptor;
|
||||
//
|
||||
// if ($ImageDescriptor['flags']['use_local_color_map']) {
|
||||
//
|
||||
// $ThisFileInfo['warning'][] = 'This version of getID3() cannot parse local color maps for GIFs';
|
||||
// return true;
|
||||
//
|
||||
// }
|
||||
//echo 'Start of raster data: '.ftell($fd).'<BR>';
|
||||
// $RasterData = array();
|
||||
// $RasterData['code_size'] = getid3_lib::LittleEndian2Int(fread($fd, 1));
|
||||
// $RasterData['block_byte_count'] = getid3_lib::LittleEndian2Int(fread($fd, 1));
|
||||
// $ThisFileInfo['gif']['raster_data'][count($ThisFileInfo['gif']['image_descriptor']) - 1] = $RasterData;
|
||||
//
|
||||
// $CurrentCodeSize = $RasterData['code_size'] + 1;
|
||||
// for ($i = 0; $i < pow(2, $RasterData['code_size']); $i++) {
|
||||
// $DefaultDataLookupTable[$i] = chr($i);
|
||||
// }
|
||||
// $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 0] = ''; // Clear Code
|
||||
// $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 1] = ''; // End Of Image Code
|
||||
//
|
||||
//
|
||||
// $NextValue = $this->GetLSBits($fd, $CurrentCodeSize);
|
||||
// echo 'Clear Code: '.$NextValue.'<BR>';
|
||||
//
|
||||
// $NextValue = $this->GetLSBits($fd, $CurrentCodeSize);
|
||||
// echo 'First Color: '.$NextValue.'<BR>';
|
||||
//
|
||||
// $Prefix = $NextValue;
|
||||
//$i = 0;
|
||||
// while ($i++ < 20) {
|
||||
// $NextValue = $this->GetLSBits($fd, $CurrentCodeSize);
|
||||
// echo $NextValue.'<BR>';
|
||||
// }
|
||||
//return true;
|
||||
// break;
|
||||
//
|
||||
// case '!':
|
||||
// // GIF Extension Block
|
||||
// $ExtensionBlockData = $NextBlockTest.fread($fd, 2);
|
||||
// $ExtensionBlock = array();
|
||||
// $ExtensionBlock['function_code'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 1, 1));
|
||||
// $ExtensionBlock['byte_length'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 2, 1));
|
||||
// $ExtensionBlock['data'] = fread($fd, $ExtensionBlock['byte_length']);
|
||||
// $ThisFileInfo['gif']['extension_blocks'][] = $ExtensionBlock;
|
||||
// break;
|
||||
//
|
||||
// case ';':
|
||||
// $ThisFileInfo['gif']['terminator_offset'] = ftell($fd) - 1;
|
||||
// // GIF Terminator
|
||||
// break;
|
||||
//
|
||||
// default:
|
||||
// break;
|
||||
//
|
||||
//
|
||||
// }
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function GetLSBits($fd, $bits) {
|
||||
static $bitbuffer = '';
|
||||
while (strlen($bitbuffer) < $bits) {
|
||||
//echo 'Read another byte: '.ftell($fd).'<BR>';
|
||||
$bitbuffer = str_pad(decbin(ord(fread($fd, 1))), 8, '0', STR_PAD_LEFT).$bitbuffer;
|
||||
}
|
||||
|
||||
$value = bindec(substr($bitbuffer, 0 - $bits));
|
||||
$bitbuffer = substr($bitbuffer, 0, 0 - $bits);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,249 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.graphic.jpg.php //
|
||||
// module for analyzing JPEG Image files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_jpg
|
||||
{
|
||||
|
||||
|
||||
function getid3_jpg(&$fd, &$ThisFileInfo) {
|
||||
$ThisFileInfo['fileformat'] = 'jpg';
|
||||
$ThisFileInfo['video']['dataformat'] = 'jpg';
|
||||
$ThisFileInfo['video']['lossless'] = false;
|
||||
$ThisFileInfo['video']['bits_per_sample'] = 24;
|
||||
$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
|
||||
$imageinfo = array();
|
||||
list($width, $height, $type) = getid3_lib::GetDataImageSize(fread($fd, $ThisFileInfo['filesize']), $imageinfo);
|
||||
|
||||
if (isset($imageinfo['APP13'])) {
|
||||
// http://php.net/iptcparse
|
||||
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
|
||||
$iptc_parsed = iptcparse($imageinfo['APP13']);
|
||||
if (is_array($iptc_parsed)) {
|
||||
foreach ($iptc_parsed as $iptc_key_raw => $iptc_values) {
|
||||
list($iptc_record, $iptc_tagkey) = explode('#', $iptc_key_raw);
|
||||
$iptc_tagkey = intval(ltrim($iptc_tagkey, '0'));
|
||||
foreach ($iptc_values as $key => $value) {
|
||||
@$ThisFileInfo['iptc'][$this->IPTCrecordName($iptc_record)][$this->IPTCrecordTagName($iptc_record, $iptc_tagkey)][] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
//echo '<pre>'.htmlentities(print_r($iptc_parsed, true)).'</pre>';
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 2: // JPEG
|
||||
$ThisFileInfo['video']['resolution_x'] = $width;
|
||||
$ThisFileInfo['video']['resolution_y'] = $height;
|
||||
|
||||
if (version_compare(phpversion(), '4.2.0', '>=')) {
|
||||
|
||||
if (function_exists('exif_read_data')) {
|
||||
|
||||
ob_start();
|
||||
$ThisFileInfo['jpg']['exif'] = exif_read_data($ThisFileInfo['filenamepath'], '', true, false);
|
||||
$errors = ob_get_contents();
|
||||
if ($errors) {
|
||||
$ThisFileInfo['warning'][] = strip_tags($errors);
|
||||
unset($ThisFileInfo['jpg']['exif']);
|
||||
}
|
||||
ob_end_clean();
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['warning'][] = 'EXIF parsing only available when '.(GETID3_OS_ISWINDOWS ? 'php_exif.dll enabled' : 'compiled with --enable-exif');
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['warning'][] = 'EXIF parsing only available in PHP v4.2.0 and higher compiled with --enable-exif (or php_exif.dll enabled for Windows). You are using PHP v'.phpversion();
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function IPTCrecordName($iptc_record) {
|
||||
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
|
||||
static $IPTCrecordName = array();
|
||||
if (empty($IPTCrecordName)) {
|
||||
$IPTCrecordName = array(
|
||||
1 => 'IPTCEnvelope',
|
||||
2 => 'IPTCApplication',
|
||||
3 => 'IPTCNewsPhoto',
|
||||
7 => 'IPTCPreObjectData',
|
||||
8 => 'IPTCObjectData',
|
||||
9 => 'IPTCPostObjectData',
|
||||
);
|
||||
}
|
||||
return (isset($IPTCrecordName[$iptc_record]) ? $IPTCrecordName[$iptc_record] : '');
|
||||
}
|
||||
|
||||
|
||||
function IPTCrecordTagName($iptc_record, $iptc_tagkey) {
|
||||
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
|
||||
static $IPTCrecordTagName = array();
|
||||
if (empty($IPTCrecordTagName)) {
|
||||
$IPTCrecordTagName = array(
|
||||
1 => array( // IPTC EnvelopeRecord Tags
|
||||
0 => 'EnvelopeRecordVersion',
|
||||
5 => 'Destination',
|
||||
20 => 'FileFormat',
|
||||
22 => 'FileVersion',
|
||||
30 => 'ServiceIdentifier',
|
||||
40 => 'EnvelopeNumber',
|
||||
50 => 'ProductID',
|
||||
60 => 'EnvelopePriority',
|
||||
70 => 'DateSent',
|
||||
80 => 'TimeSent',
|
||||
90 => 'CodedCharacterSet',
|
||||
100 => 'UniqueObjectName',
|
||||
120 => 'ARMIdentifier',
|
||||
122 => 'ARMVersion',
|
||||
),
|
||||
2 => array( // IPTC ApplicationRecord Tags
|
||||
0 => 'ApplicationRecordVersion',
|
||||
3 => 'ObjectTypeReference',
|
||||
4 => 'ObjectAttributeReference',
|
||||
5 => 'ObjectName',
|
||||
7 => 'EditStatus',
|
||||
8 => 'EditorialUpdate',
|
||||
10 => 'Urgency',
|
||||
12 => 'SubjectReference',
|
||||
15 => 'Category',
|
||||
20 => 'SupplementalCategories',
|
||||
22 => 'FixtureIdentifier',
|
||||
25 => 'Keywords',
|
||||
26 => 'ContentLocationCode',
|
||||
27 => 'ContentLocationName',
|
||||
30 => 'ReleaseDate',
|
||||
35 => 'ReleaseTime',
|
||||
37 => 'ExpirationDate',
|
||||
38 => 'ExpirationTime',
|
||||
40 => 'SpecialInstructions',
|
||||
42 => 'ActionAdvised',
|
||||
45 => 'ReferenceService',
|
||||
47 => 'ReferenceDate',
|
||||
50 => 'ReferenceNumber',
|
||||
55 => 'DateCreated',
|
||||
60 => 'TimeCreated',
|
||||
62 => 'DigitalCreationDate',
|
||||
63 => 'DigitalCreationTime',
|
||||
65 => 'OriginatingProgram',
|
||||
70 => 'ProgramVersion',
|
||||
75 => 'ObjectCycle',
|
||||
80 => 'By-line',
|
||||
85 => 'By-lineTitle',
|
||||
90 => 'City',
|
||||
92 => 'Sub-location',
|
||||
95 => 'Province-State',
|
||||
100 => 'Country-PrimaryLocationCode',
|
||||
101 => 'Country-PrimaryLocationName',
|
||||
103 => 'OriginalTransmissionReference',
|
||||
105 => 'Headline',
|
||||
110 => 'Credit',
|
||||
115 => 'Source',
|
||||
116 => 'CopyrightNotice',
|
||||
118 => 'Contact',
|
||||
120 => 'Caption-Abstract',
|
||||
121 => 'LocalCaption',
|
||||
122 => 'Writer-Editor',
|
||||
125 => 'RasterizedCaption',
|
||||
130 => 'ImageType',
|
||||
131 => 'ImageOrientation',
|
||||
135 => 'LanguageIdentifier',
|
||||
150 => 'AudioType',
|
||||
151 => 'AudioSamplingRate',
|
||||
152 => 'AudioSamplingResolution',
|
||||
153 => 'AudioDuration',
|
||||
154 => 'AudioOutcue',
|
||||
184 => 'JobID',
|
||||
185 => 'MasterDocumentID',
|
||||
186 => 'ShortDocumentID',
|
||||
187 => 'UniqueDocumentID',
|
||||
188 => 'OwnerID',
|
||||
200 => 'ObjectPreviewFileFormat',
|
||||
201 => 'ObjectPreviewFileVersion',
|
||||
202 => 'ObjectPreviewData',
|
||||
221 => 'Prefs',
|
||||
225 => 'ClassifyState',
|
||||
228 => 'SimilarityIndex',
|
||||
230 => 'DocumentNotes',
|
||||
231 => 'DocumentHistory',
|
||||
232 => 'ExifCameraInfo',
|
||||
),
|
||||
3 => array( // IPTC NewsPhoto Tags
|
||||
0 => 'NewsPhotoVersion',
|
||||
10 => 'IPTCPictureNumber',
|
||||
20 => 'IPTCImageWidth',
|
||||
30 => 'IPTCImageHeight',
|
||||
40 => 'IPTCPixelWidth',
|
||||
50 => 'IPTCPixelHeight',
|
||||
55 => 'SupplementalType',
|
||||
60 => 'ColorRepresentation',
|
||||
64 => 'InterchangeColorSpace',
|
||||
65 => 'ColorSequence',
|
||||
66 => 'ICC_Profile',
|
||||
70 => 'ColorCalibrationMatrix',
|
||||
80 => 'LookupTable',
|
||||
84 => 'NumIndexEntries',
|
||||
85 => 'ColorPalette',
|
||||
86 => 'IPTCBitsPerSample',
|
||||
90 => 'SampleStructure',
|
||||
100 => 'ScanningDirection',
|
||||
102 => 'IPTCImageRotation',
|
||||
110 => 'DataCompressionMethod',
|
||||
120 => 'QuantizationMethod',
|
||||
125 => 'EndPoints',
|
||||
130 => 'ExcursionTolerance',
|
||||
135 => 'BitsPerComponent',
|
||||
140 => 'MaximumDensityRange',
|
||||
145 => 'GammaCompensatedValue',
|
||||
),
|
||||
7 => array( // IPTC PreObjectData Tags
|
||||
10 => 'SizeMode',
|
||||
20 => 'MaxSubfileSize',
|
||||
90 => 'ObjectSizeAnnounced',
|
||||
95 => 'MaximumObjectSize',
|
||||
),
|
||||
8 => array( // IPTC ObjectData Tags
|
||||
10 => 'SubFile',
|
||||
),
|
||||
9 => array( // IPTC PostObjectData Tags
|
||||
10 => 'ConfirmedObjectSize',
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
return (isset($IPTCrecordTagName[$iptc_record][$iptc_tagkey]) ? $IPTCrecordTagName[$iptc_record][$iptc_tagkey] : $iptc_tagkey);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.graphic.pcd.php //
|
||||
// module for analyzing PhotoCD (PCD) Image files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_pcd
|
||||
{
|
||||
function getid3_pcd(&$fd, &$ThisFileInfo, $ExtractData=0) {
|
||||
$ThisFileInfo['fileformat'] = 'pcd';
|
||||
$ThisFileInfo['video']['dataformat'] = 'pcd';
|
||||
$ThisFileInfo['video']['lossless'] = false;
|
||||
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'] + 72, SEEK_SET);
|
||||
|
||||
$PCDflags = fread($fd, 1);
|
||||
$PCDisVertical = ((ord($PCDflags) & 0x01) ? true : false);
|
||||
|
||||
|
||||
if ($PCDisVertical) {
|
||||
$ThisFileInfo['video']['resolution_x'] = 3072;
|
||||
$ThisFileInfo['video']['resolution_y'] = 2048;
|
||||
} else {
|
||||
$ThisFileInfo['video']['resolution_x'] = 2048;
|
||||
$ThisFileInfo['video']['resolution_y'] = 3072;
|
||||
}
|
||||
|
||||
|
||||
if ($ExtractData > 3) {
|
||||
|
||||
$ThisFileInfo['error'][] = 'Cannot extract PSD image data for detail levels above BASE (3)';
|
||||
|
||||
} elseif ($ExtractData > 0) {
|
||||
|
||||
$PCD_levels[1] = array( 192, 128, 0x02000); // BASE/16
|
||||
$PCD_levels[2] = array( 384, 256, 0x0B800); // BASE/4
|
||||
$PCD_levels[3] = array( 768, 512, 0x30000); // BASE
|
||||
//$PCD_levels[4] = array(1536, 1024, ??); // BASE*4 - encrypted with Kodak-proprietary compression/encryption
|
||||
//$PCD_levels[5] = array(3072, 2048, ??); // BASE*16 - encrypted with Kodak-proprietary compression/encryption
|
||||
//$PCD_levels[6] = array(6144, 4096, ??); // BASE*64 - encrypted with Kodak-proprietary compression/encryption; PhotoCD-Pro only
|
||||
|
||||
list($PCD_width, $PCD_height, $PCD_dataOffset) = $PCD_levels[3];
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'] + $PCD_dataOffset, SEEK_SET);
|
||||
|
||||
for ($y = 0; $y < $PCD_height; $y += 2) {
|
||||
// The image-data of these subtypes start at the respective offsets of 02000h, 0b800h and 30000h.
|
||||
// To decode the YcbYr to the more usual RGB-code, three lines of data have to be read, each
|
||||
// consisting of ‘w’ bytes, where ‘w’ is the width of the image-subtype. The first ‘w’ bytes and
|
||||
// the first half of the third ‘w’ bytes contain data for the first RGB-line, the second ‘w’ bytes
|
||||
// and the second half of the third ‘w’ bytes contain data for a second RGB-line.
|
||||
|
||||
$PCD_data_Y1 = fread($fd, $PCD_width);
|
||||
$PCD_data_Y2 = fread($fd, $PCD_width);
|
||||
$PCD_data_Cb = fread($fd, intval(round($PCD_width / 2)));
|
||||
$PCD_data_Cr = fread($fd, intval(round($PCD_width / 2)));
|
||||
|
||||
for ($x = 0; $x < $PCD_width; $x++) {
|
||||
if ($PCDisVertical) {
|
||||
$ThisFileInfo['pcd']['data'][$PCD_width - $x][$y] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
|
||||
$ThisFileInfo['pcd']['data'][$PCD_width - $x][$y + 1] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
|
||||
} else {
|
||||
$ThisFileInfo['pcd']['data'][$y][$x] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
|
||||
$ThisFileInfo['pcd']['data'][$y + 1][$x] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Example for plotting extracted data
|
||||
//getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
|
||||
//if ($PCDisVertical) {
|
||||
// $BMPinfo['resolution_x'] = $PCD_height;
|
||||
// $BMPinfo['resolution_y'] = $PCD_width;
|
||||
//} else {
|
||||
// $BMPinfo['resolution_x'] = $PCD_width;
|
||||
// $BMPinfo['resolution_y'] = $PCD_height;
|
||||
//}
|
||||
//$BMPinfo['bmp']['data'] = $ThisFileInfo['pcd']['data'];
|
||||
//getid3_bmp::PlotBMP($BMPinfo);
|
||||
//exit;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function YCbCr2RGB($Y, $Cb, $Cr) {
|
||||
static $YCbCr_constants = array();
|
||||
if (empty($YCbCr_constants)) {
|
||||
$YCbCr_constants['red']['Y'] = 0.0054980 * 256;
|
||||
$YCbCr_constants['red']['Cb'] = 0.0000000 * 256;
|
||||
$YCbCr_constants['red']['Cr'] = 0.0051681 * 256;
|
||||
$YCbCr_constants['green']['Y'] = 0.0054980 * 256;
|
||||
$YCbCr_constants['green']['Cb'] = -0.0015446 * 256;
|
||||
$YCbCr_constants['green']['Cr'] = -0.0026325 * 256;
|
||||
$YCbCr_constants['blue']['Y'] = 0.0054980 * 256;
|
||||
$YCbCr_constants['blue']['Cb'] = 0.0079533 * 256;
|
||||
$YCbCr_constants['blue']['Cr'] = 0.0000000 * 256;
|
||||
}
|
||||
|
||||
$RGBcolor = array('red'=>0, 'green'=>0, 'blue'=>0);
|
||||
foreach ($RGBcolor as $rgbname => $dummy) {
|
||||
$RGBcolor[$rgbname] = max(0,
|
||||
min(255,
|
||||
intval(
|
||||
round(
|
||||
($YCbCr_constants[$rgbname]['Y'] * $Y) +
|
||||
($YCbCr_constants[$rgbname]['Cb'] * ($Cb - 156)) +
|
||||
($YCbCr_constants[$rgbname]['Cr'] * ($Cr - 137))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
return (($RGBcolor['red'] * 65536) + ($RGBcolor['green'] * 256) + $RGBcolor['blue']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,519 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.graphic.png.php //
|
||||
// module for analyzing PNG Image files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_png
|
||||
{
|
||||
|
||||
function getid3_png(&$fd, &$ThisFileInfo) {
|
||||
|
||||
// shortcut
|
||||
$ThisFileInfo['png'] = array();
|
||||
$thisfile_png = &$ThisFileInfo['png'];
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'png';
|
||||
$ThisFileInfo['video']['dataformat'] = 'png';
|
||||
$ThisFileInfo['video']['lossless'] = false;
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$PNGfiledata = fread($fd, GETID3_FREAD_BUFFER_SIZE);
|
||||
$offset = 0;
|
||||
|
||||
$PNGidentifier = substr($PNGfiledata, $offset, 8); // $89 $50 $4E $47 $0D $0A $1A $0A
|
||||
$offset += 8;
|
||||
|
||||
if ($PNGidentifier != "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") {
|
||||
$ThisFileInfo['error'][] = 'First 8 bytes of file ('.getid3_lib::PrintHexBytes($PNGidentifier).') did not match expected PNG identifier';
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
return false;
|
||||
}
|
||||
|
||||
while (((ftell($fd) - (strlen($PNGfiledata) - $offset)) < $ThisFileInfo['filesize'])) {
|
||||
$chunk['data_length'] = getid3_lib::BigEndian2Int(substr($PNGfiledata, $offset, 4));
|
||||
$offset += 4;
|
||||
while (((strlen($PNGfiledata) - $offset) < ($chunk['data_length'] + 4)) && (ftell($fd) < $ThisFileInfo['filesize'])) {
|
||||
$PNGfiledata .= fread($fd, GETID3_FREAD_BUFFER_SIZE);
|
||||
}
|
||||
$chunk['type_text'] = substr($PNGfiledata, $offset, 4);
|
||||
$offset += 4;
|
||||
$chunk['type_raw'] = getid3_lib::BigEndian2Int($chunk['type_text']);
|
||||
$chunk['data'] = substr($PNGfiledata, $offset, $chunk['data_length']);
|
||||
$offset += $chunk['data_length'];
|
||||
$chunk['crc'] = getid3_lib::BigEndian2Int(substr($PNGfiledata, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$chunk['flags']['ancilliary'] = (bool) ($chunk['type_raw'] & 0x20000000);
|
||||
$chunk['flags']['private'] = (bool) ($chunk['type_raw'] & 0x00200000);
|
||||
$chunk['flags']['reserved'] = (bool) ($chunk['type_raw'] & 0x00002000);
|
||||
$chunk['flags']['safe_to_copy'] = (bool) ($chunk['type_raw'] & 0x00000020);
|
||||
|
||||
// shortcut
|
||||
$thisfile_png[$chunk['type_text']] = array();
|
||||
$thisfile_png_chunk_type_text = &$thisfile_png[$chunk['type_text']];
|
||||
|
||||
switch ($chunk['type_text']) {
|
||||
|
||||
case 'IHDR': // Image Header
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
$thisfile_png_chunk_type_text['width'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 4));
|
||||
$thisfile_png_chunk_type_text['height'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 4));
|
||||
$thisfile_png_chunk_type_text['raw']['bit_depth'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 8, 1));
|
||||
$thisfile_png_chunk_type_text['raw']['color_type'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 9, 1));
|
||||
$thisfile_png_chunk_type_text['raw']['compression_method'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 10, 1));
|
||||
$thisfile_png_chunk_type_text['raw']['filter_method'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 11, 1));
|
||||
$thisfile_png_chunk_type_text['raw']['interlace_method'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 12, 1));
|
||||
|
||||
$thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['raw']['compression_method']);
|
||||
$thisfile_png_chunk_type_text['color_type']['palette'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x01);
|
||||
$thisfile_png_chunk_type_text['color_type']['true_color'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x02);
|
||||
$thisfile_png_chunk_type_text['color_type']['alpha'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x04);
|
||||
|
||||
$ThisFileInfo['video']['resolution_x'] = $thisfile_png_chunk_type_text['width'];
|
||||
$ThisFileInfo['video']['resolution_y'] = $thisfile_png_chunk_type_text['height'];
|
||||
|
||||
$ThisFileInfo['video']['bits_per_sample'] = $this->IHDRcalculateBitsPerSample($thisfile_png_chunk_type_text['raw']['color_type'], $thisfile_png_chunk_type_text['raw']['bit_depth']);
|
||||
break;
|
||||
|
||||
|
||||
case 'PLTE': // Palette
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
$paletteoffset = 0;
|
||||
for ($i = 0; $i <= 255; $i++) {
|
||||
//$thisfile_png_chunk_type_text['red'][$i] = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
|
||||
//$thisfile_png_chunk_type_text['green'][$i] = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
|
||||
//$thisfile_png_chunk_type_text['blue'][$i] = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
|
||||
$red = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
|
||||
$green = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
|
||||
$blue = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
|
||||
$thisfile_png_chunk_type_text[$i] = (($red << 16) | ($green << 8) | ($blue));
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'tRNS': // Transparency
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
switch ($thisfile_png['IHDR']['raw']['color_type']) {
|
||||
case 0:
|
||||
$thisfile_png_chunk_type_text['transparent_color_gray'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 2));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$thisfile_png_chunk_type_text['transparent_color_red'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 2));
|
||||
$thisfile_png_chunk_type_text['transparent_color_green'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 2, 2));
|
||||
$thisfile_png_chunk_type_text['transparent_color_blue'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 2));
|
||||
break;
|
||||
|
||||
case 3:
|
||||
for ($i = 0; $i < strlen($chunk['data']); $i++) {
|
||||
$thisfile_png_chunk_type_text['palette_opacity'][$i] = getid3_lib::BigEndian2Int(substr($chunk['data'], $i, 1));
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
case 6:
|
||||
$ThisFileInfo['error'][] = 'Invalid color_type in tRNS chunk: '.$thisfile_png['IHDR']['raw']['color_type'];
|
||||
|
||||
default:
|
||||
$ThisFileInfo['warning'][] = 'Unhandled color_type in tRNS chunk: '.$thisfile_png['IHDR']['raw']['color_type'];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'gAMA': // Image Gamma
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
$thisfile_png_chunk_type_text['gamma'] = getid3_lib::BigEndian2Int($chunk['data']) / 100000;
|
||||
break;
|
||||
|
||||
|
||||
case 'cHRM': // Primary Chromaticities
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
$thisfile_png_chunk_type_text['white_x'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 4)) / 100000;
|
||||
$thisfile_png_chunk_type_text['white_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 4)) / 100000;
|
||||
$thisfile_png_chunk_type_text['red_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 8, 4)) / 100000;
|
||||
$thisfile_png_chunk_type_text['red_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 12, 4)) / 100000;
|
||||
$thisfile_png_chunk_type_text['green_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 16, 4)) / 100000;
|
||||
$thisfile_png_chunk_type_text['green_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 20, 4)) / 100000;
|
||||
$thisfile_png_chunk_type_text['blue_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 24, 4)) / 100000;
|
||||
$thisfile_png_chunk_type_text['blue_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 28, 4)) / 100000;
|
||||
break;
|
||||
|
||||
|
||||
case 'sRGB': // Standard RGB Color Space
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
$thisfile_png_chunk_type_text['reindering_intent'] = getid3_lib::BigEndian2Int($chunk['data']);
|
||||
$thisfile_png_chunk_type_text['reindering_intent_text'] = $this->PNGsRGBintentLookup($thisfile_png_chunk_type_text['reindering_intent']);
|
||||
break;
|
||||
|
||||
|
||||
case 'iCCP': // Embedded ICC Profile
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
list($profilename, $compressiondata) = explode("\x00", $chunk['data'], 2);
|
||||
$thisfile_png_chunk_type_text['profile_name'] = $profilename;
|
||||
$thisfile_png_chunk_type_text['compression_method'] = getid3_lib::BigEndian2Int(substr($compressiondata, 0, 1));
|
||||
$thisfile_png_chunk_type_text['compression_profile'] = substr($compressiondata, 1);
|
||||
|
||||
$thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']);
|
||||
break;
|
||||
|
||||
|
||||
case 'tEXt': // Textual Data
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
list($keyword, $text) = explode("\x00", $chunk['data'], 2);
|
||||
$thisfile_png_chunk_type_text['keyword'] = $keyword;
|
||||
$thisfile_png_chunk_type_text['text'] = $text;
|
||||
|
||||
$thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text'];
|
||||
break;
|
||||
|
||||
|
||||
case 'zTXt': // Compressed Textual Data
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
list($keyword, $otherdata) = explode("\x00", $chunk['data'], 2);
|
||||
$thisfile_png_chunk_type_text['keyword'] = $keyword;
|
||||
$thisfile_png_chunk_type_text['compression_method'] = getid3_lib::BigEndian2Int(substr($otherdata, 0, 1));
|
||||
$thisfile_png_chunk_type_text['compressed_text'] = substr($otherdata, 1);
|
||||
$thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']);
|
||||
switch ($thisfile_png_chunk_type_text['compression_method']) {
|
||||
case 0:
|
||||
$thisfile_png_chunk_type_text['text'] = gzuncompress($thisfile_png_chunk_type_text['compressed_text']);
|
||||
break;
|
||||
|
||||
default:
|
||||
// unknown compression method
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($thisfile_png_chunk_type_text['text'])) {
|
||||
$thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text'];
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'iTXt': // International Textual Data
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
list($keyword, $otherdata) = explode("\x00", $chunk['data'], 2);
|
||||
$thisfile_png_chunk_type_text['keyword'] = $keyword;
|
||||
$thisfile_png_chunk_type_text['compression'] = (bool) getid3_lib::BigEndian2Int(substr($otherdata, 0, 1));
|
||||
$thisfile_png_chunk_type_text['compression_method'] = getid3_lib::BigEndian2Int(substr($otherdata, 1, 1));
|
||||
$thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']);
|
||||
list($languagetag, $translatedkeyword, $text) = explode("\x00", substr($otherdata, 2), 3);
|
||||
$thisfile_png_chunk_type_text['language_tag'] = $languagetag;
|
||||
$thisfile_png_chunk_type_text['translated_keyword'] = $translatedkeyword;
|
||||
|
||||
if ($thisfile_png_chunk_type_text['compression']) {
|
||||
|
||||
switch ($thisfile_png_chunk_type_text['compression_method']) {
|
||||
case 0:
|
||||
$thisfile_png_chunk_type_text['text'] = gzuncompress($text);
|
||||
break;
|
||||
|
||||
default:
|
||||
// unknown compression method
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$thisfile_png_chunk_type_text['text'] = $text;
|
||||
|
||||
}
|
||||
|
||||
if (isset($thisfile_png_chunk_type_text['text'])) {
|
||||
$thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text'];
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'bKGD': // Background Color
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
switch ($thisfile_png['IHDR']['raw']['color_type']) {
|
||||
case 0:
|
||||
case 4:
|
||||
$thisfile_png_chunk_type_text['background_gray'] = getid3_lib::BigEndian2Int($chunk['data']);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 6:
|
||||
$thisfile_png_chunk_type_text['background_red'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth']));
|
||||
$thisfile_png_chunk_type_text['background_green'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 1 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth']));
|
||||
$thisfile_png_chunk_type_text['background_blue'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 2 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth']));
|
||||
break;
|
||||
|
||||
case 3:
|
||||
$thisfile_png_chunk_type_text['background_index'] = getid3_lib::BigEndian2Int($chunk['data']);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'pHYs': // Physical Pixel Dimensions
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
$thisfile_png_chunk_type_text['pixels_per_unit_x'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 4));
|
||||
$thisfile_png_chunk_type_text['pixels_per_unit_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 4));
|
||||
$thisfile_png_chunk_type_text['unit_specifier'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 8, 1));
|
||||
$thisfile_png_chunk_type_text['unit'] = $this->PNGpHYsUnitLookup($thisfile_png_chunk_type_text['unit_specifier']);
|
||||
break;
|
||||
|
||||
|
||||
case 'sBIT': // Significant Bits
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
switch ($thisfile_png['IHDR']['raw']['color_type']) {
|
||||
case 0:
|
||||
$thisfile_png_chunk_type_text['significant_bits_gray'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
$thisfile_png_chunk_type_text['significant_bits_red'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
|
||||
$thisfile_png_chunk_type_text['significant_bits_green'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 1, 1));
|
||||
$thisfile_png_chunk_type_text['significant_bits_blue'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 2, 1));
|
||||
break;
|
||||
|
||||
case 4:
|
||||
$thisfile_png_chunk_type_text['significant_bits_gray'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
|
||||
$thisfile_png_chunk_type_text['significant_bits_alpha'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 1, 1));
|
||||
break;
|
||||
|
||||
case 6:
|
||||
$thisfile_png_chunk_type_text['significant_bits_red'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
|
||||
$thisfile_png_chunk_type_text['significant_bits_green'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 1, 1));
|
||||
$thisfile_png_chunk_type_text['significant_bits_blue'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 2, 1));
|
||||
$thisfile_png_chunk_type_text['significant_bits_alpha'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 3, 1));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'sPLT': // Suggested Palette
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
list($palettename, $otherdata) = explode("\x00", $chunk['data'], 2);
|
||||
$thisfile_png_chunk_type_text['palette_name'] = $palettename;
|
||||
$sPLToffset = 0;
|
||||
$thisfile_png_chunk_type_text['sample_depth_bits'] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, 1));
|
||||
$sPLToffset += 1;
|
||||
$thisfile_png_chunk_type_text['sample_depth_bytes'] = $thisfile_png_chunk_type_text['sample_depth_bits'] / 8;
|
||||
$paletteCounter = 0;
|
||||
while ($sPLToffset < strlen($otherdata)) {
|
||||
$thisfile_png_chunk_type_text['red'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
|
||||
$sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
|
||||
$thisfile_png_chunk_type_text['green'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
|
||||
$sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
|
||||
$thisfile_png_chunk_type_text['blue'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
|
||||
$sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
|
||||
$thisfile_png_chunk_type_text['alpha'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
|
||||
$sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
|
||||
$thisfile_png_chunk_type_text['frequency'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, 2));
|
||||
$sPLToffset += 2;
|
||||
$paletteCounter++;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'hIST': // Palette Histogram
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
$hISTcounter = 0;
|
||||
while ($hISTcounter < strlen($chunk['data'])) {
|
||||
$thisfile_png_chunk_type_text[$hISTcounter] = getid3_lib::BigEndian2Int(substr($chunk['data'], $hISTcounter / 2, 2));
|
||||
$hISTcounter += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'tIME': // Image Last-Modification Time
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
$thisfile_png_chunk_type_text['year'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 2));
|
||||
$thisfile_png_chunk_type_text['month'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 2, 1));
|
||||
$thisfile_png_chunk_type_text['day'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 3, 1));
|
||||
$thisfile_png_chunk_type_text['hour'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 1));
|
||||
$thisfile_png_chunk_type_text['minute'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 5, 1));
|
||||
$thisfile_png_chunk_type_text['second'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 6, 1));
|
||||
$thisfile_png_chunk_type_text['unix'] = gmmktime($thisfile_png_chunk_type_text['hour'], $thisfile_png_chunk_type_text['minute'], $thisfile_png_chunk_type_text['second'], $thisfile_png_chunk_type_text['month'], $thisfile_png_chunk_type_text['day'], $thisfile_png_chunk_type_text['year']);
|
||||
break;
|
||||
|
||||
|
||||
case 'oFFs': // Image Offset
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
$thisfile_png_chunk_type_text['position_x'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 4), false, true);
|
||||
$thisfile_png_chunk_type_text['position_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 4), false, true);
|
||||
$thisfile_png_chunk_type_text['unit_specifier'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 8, 1));
|
||||
$thisfile_png_chunk_type_text['unit'] = $this->PNGoFFsUnitLookup($thisfile_png_chunk_type_text['unit_specifier']);
|
||||
break;
|
||||
|
||||
|
||||
case 'pCAL': // Calibration Of Pixel Values
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
list($calibrationname, $otherdata) = explode("\x00", $chunk['data'], 2);
|
||||
$thisfile_png_chunk_type_text['calibration_name'] = $calibrationname;
|
||||
$pCALoffset = 0;
|
||||
$thisfile_png_chunk_type_text['original_zero'] = getid3_lib::BigEndian2Int(substr($chunk['data'], $pCALoffset, 4), false, true);
|
||||
$pCALoffset += 4;
|
||||
$thisfile_png_chunk_type_text['original_max'] = getid3_lib::BigEndian2Int(substr($chunk['data'], $pCALoffset, 4), false, true);
|
||||
$pCALoffset += 4;
|
||||
$thisfile_png_chunk_type_text['equation_type'] = getid3_lib::BigEndian2Int(substr($chunk['data'], $pCALoffset, 1));
|
||||
$pCALoffset += 1;
|
||||
$thisfile_png_chunk_type_text['equation_type_text'] = $this->PNGpCALequationTypeLookup($thisfile_png_chunk_type_text['equation_type']);
|
||||
$thisfile_png_chunk_type_text['parameter_count'] = getid3_lib::BigEndian2Int(substr($chunk['data'], $pCALoffset, 1));
|
||||
$pCALoffset += 1;
|
||||
$thisfile_png_chunk_type_text['parameters'] = explode("\x00", substr($chunk['data'], $pCALoffset));
|
||||
break;
|
||||
|
||||
|
||||
case 'sCAL': // Physical Scale Of Image Subject
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
$thisfile_png_chunk_type_text['unit_specifier'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
|
||||
$thisfile_png_chunk_type_text['unit'] = $this->PNGsCALUnitLookup($thisfile_png_chunk_type_text['unit_specifier']);
|
||||
list($pixelwidth, $pixelheight) = explode("\x00", substr($chunk['data'], 1));
|
||||
$thisfile_png_chunk_type_text['pixel_width'] = $pixelwidth;
|
||||
$thisfile_png_chunk_type_text['pixel_height'] = $pixelheight;
|
||||
break;
|
||||
|
||||
|
||||
case 'gIFg': // GIF Graphic Control Extension
|
||||
$gIFgCounter = 0;
|
||||
if (isset($thisfile_png_chunk_type_text) && is_array($thisfile_png_chunk_type_text)) {
|
||||
$gIFgCounter = count($thisfile_png_chunk_type_text);
|
||||
}
|
||||
$thisfile_png_chunk_type_text[$gIFgCounter]['header'] = $chunk;
|
||||
$thisfile_png_chunk_type_text[$gIFgCounter]['disposal_method'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
|
||||
$thisfile_png_chunk_type_text[$gIFgCounter]['user_input_flag'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 1, 1));
|
||||
$thisfile_png_chunk_type_text[$gIFgCounter]['delay_time'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 2, 2));
|
||||
break;
|
||||
|
||||
|
||||
case 'gIFx': // GIF Application Extension
|
||||
$gIFxCounter = 0;
|
||||
if (isset($thisfile_png_chunk_type_text) && is_array($thisfile_png_chunk_type_text)) {
|
||||
$gIFxCounter = count($thisfile_png_chunk_type_text);
|
||||
}
|
||||
$thisfile_png_chunk_type_text[$gIFxCounter]['header'] = $chunk;
|
||||
$thisfile_png_chunk_type_text[$gIFxCounter]['application_identifier'] = substr($chunk['data'], 0, 8);
|
||||
$thisfile_png_chunk_type_text[$gIFxCounter]['authentication_code'] = substr($chunk['data'], 8, 3);
|
||||
$thisfile_png_chunk_type_text[$gIFxCounter]['application_data'] = substr($chunk['data'], 11);
|
||||
break;
|
||||
|
||||
|
||||
case 'IDAT': // Image Data
|
||||
$idatinformationfieldindex = 0;
|
||||
if (isset($thisfile_png['IDAT']) && is_array($thisfile_png['IDAT'])) {
|
||||
$idatinformationfieldindex = count($thisfile_png['IDAT']);
|
||||
}
|
||||
unset($chunk['data']);
|
||||
$thisfile_png_chunk_type_text[$idatinformationfieldindex]['header'] = $chunk;
|
||||
break;
|
||||
|
||||
|
||||
case 'IEND': // Image Trailer
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
//unset($chunk['data']);
|
||||
$thisfile_png_chunk_type_text['header'] = $chunk;
|
||||
$ThisFileInfo['warning'][] = 'Unhandled chunk type: '.$chunk['type_text'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function PNGsRGBintentLookup($sRGB) {
|
||||
static $PNGsRGBintentLookup = array(
|
||||
0 => 'Perceptual',
|
||||
1 => 'Relative colorimetric',
|
||||
2 => 'Saturation',
|
||||
3 => 'Absolute colorimetric'
|
||||
);
|
||||
return (isset($PNGsRGBintentLookup[$sRGB]) ? $PNGsRGBintentLookup[$sRGB] : 'invalid');
|
||||
}
|
||||
|
||||
function PNGcompressionMethodLookup($compressionmethod) {
|
||||
static $PNGcompressionMethodLookup = array(
|
||||
0 => 'deflate/inflate'
|
||||
);
|
||||
return (isset($PNGcompressionMethodLookup[$compressionmethod]) ? $PNGcompressionMethodLookup[$compressionmethod] : 'invalid');
|
||||
}
|
||||
|
||||
function PNGpHYsUnitLookup($unitid) {
|
||||
static $PNGpHYsUnitLookup = array(
|
||||
0 => 'unknown',
|
||||
1 => 'meter'
|
||||
);
|
||||
return (isset($PNGpHYsUnitLookup[$unitid]) ? $PNGpHYsUnitLookup[$unitid] : 'invalid');
|
||||
}
|
||||
|
||||
function PNGoFFsUnitLookup($unitid) {
|
||||
static $PNGoFFsUnitLookup = array(
|
||||
0 => 'pixel',
|
||||
1 => 'micrometer'
|
||||
);
|
||||
return (isset($PNGoFFsUnitLookup[$unitid]) ? $PNGoFFsUnitLookup[$unitid] : 'invalid');
|
||||
}
|
||||
|
||||
function PNGpCALequationTypeLookup($equationtype) {
|
||||
static $PNGpCALequationTypeLookup = array(
|
||||
0 => 'Linear mapping',
|
||||
1 => 'Base-e exponential mapping',
|
||||
2 => 'Arbitrary-base exponential mapping',
|
||||
3 => 'Hyperbolic mapping'
|
||||
);
|
||||
return (isset($PNGpCALequationTypeLookup[$equationtype]) ? $PNGpCALequationTypeLookup[$equationtype] : 'invalid');
|
||||
}
|
||||
|
||||
function PNGsCALUnitLookup($unitid) {
|
||||
static $PNGsCALUnitLookup = array(
|
||||
0 => 'meter',
|
||||
1 => 'radian'
|
||||
);
|
||||
return (isset($PNGsCALUnitLookup[$unitid]) ? $PNGsCALUnitLookup[$unitid] : 'invalid');
|
||||
}
|
||||
|
||||
function IHDRcalculateBitsPerSample($color_type, $bit_depth) {
|
||||
switch ($color_type) {
|
||||
case 0: // Each pixel is a grayscale sample.
|
||||
return $bit_depth;
|
||||
break;
|
||||
|
||||
case 2: // Each pixel is an R,G,B triple
|
||||
return 3 * $bit_depth;
|
||||
break;
|
||||
|
||||
case 3: // Each pixel is a palette index; a PLTE chunk must appear.
|
||||
return $bit_depth;
|
||||
break;
|
||||
|
||||
case 4: // Each pixel is a grayscale sample, followed by an alpha sample.
|
||||
return 2 * $bit_depth;
|
||||
break;
|
||||
|
||||
case 6: // Each pixel is an R,G,B triple, followed by an alpha sample.
|
||||
return 4 * $bit_depth;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.graphic.svg.php //
|
||||
// module for analyzing SVG Image files //
|
||||
// dependencies: NONE //
|
||||
// author: Bryce Harrington <bryceØbryceharrington*org> //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_svg
|
||||
{
|
||||
|
||||
|
||||
function getid3_svg(&$fd, &$ThisFileInfo) {
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
|
||||
// I'm making this up, please modify as appropriate
|
||||
$SVGheader = fread($fd, 32);
|
||||
$ThisFileInfo['svg']['magic'] = substr($SVGheader, 0, 4);
|
||||
if ($ThisFileInfo['svg']['magic'] == 'aBcD') {
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'svg';
|
||||
$ThisFileInfo['video']['dataformat'] = 'svg';
|
||||
$ThisFileInfo['video']['lossless'] = true;
|
||||
$ThisFileInfo['video']['bits_per_sample'] = 24;
|
||||
$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
|
||||
|
||||
$ThisFileInfo['svg']['width'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4));
|
||||
$ThisFileInfo['svg']['height'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 4));
|
||||
|
||||
$ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['svg']['width'];
|
||||
$ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['svg']['height'];
|
||||
|
||||
return true;
|
||||
}
|
||||
$ThisFileInfo['error'][] = 'Did not find SVG magic bytes "aBcD" at '.$ThisFileInfo['avdataoffset'];
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,221 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.archive.tiff.php //
|
||||
// module for analyzing TIFF files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_tiff
|
||||
{
|
||||
|
||||
function getid3_tiff(&$fd, &$ThisFileInfo) {
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$TIFFheader = fread($fd, 4);
|
||||
|
||||
switch (substr($TIFFheader, 0, 2)) {
|
||||
case 'II':
|
||||
$ThisFileInfo['tiff']['byte_order'] = 'Intel';
|
||||
break;
|
||||
case 'MM':
|
||||
$ThisFileInfo['tiff']['byte_order'] = 'Motorola';
|
||||
break;
|
||||
default:
|
||||
$ThisFileInfo['error'][] = 'Invalid TIFF byte order identifier ('.substr($TIFFheader, 0, 2).') at offset '.$ThisFileInfo['avdataoffset'];
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'tiff';
|
||||
$ThisFileInfo['video']['dataformat'] = 'tiff';
|
||||
$ThisFileInfo['video']['lossless'] = true;
|
||||
$ThisFileInfo['tiff']['ifd'] = array();
|
||||
$CurrentIFD = array();
|
||||
|
||||
$FieldTypeByteLength = array(1=>1, 2=>1, 3=>2, 4=>4, 5=>8);
|
||||
|
||||
$nextIFDoffset = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']);
|
||||
|
||||
while ($nextIFDoffset > 0) {
|
||||
|
||||
$CurrentIFD['offset'] = $nextIFDoffset;
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'] + $nextIFDoffset, SEEK_SET);
|
||||
$CurrentIFD['fieldcount'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']);
|
||||
|
||||
for ($i = 0; $i < $CurrentIFD['fieldcount']; $i++) {
|
||||
$CurrentIFD['fields'][$i]['raw']['tag'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']);
|
||||
$CurrentIFD['fields'][$i]['raw']['type'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']);
|
||||
$CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']);
|
||||
$CurrentIFD['fields'][$i]['raw']['offset'] = fread($fd, 4);
|
||||
|
||||
switch ($CurrentIFD['fields'][$i]['raw']['type']) {
|
||||
case 1: // BYTE An 8-bit unsigned integer.
|
||||
if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
|
||||
$CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 1), $ThisFileInfo['tiff']['byte_order']);
|
||||
} else {
|
||||
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // ASCII 8-bit bytes that store ASCII codes; the last byte must be null.
|
||||
if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
|
||||
$CurrentIFD['fields'][$i]['value'] = substr($CurrentIFD['fields'][$i]['raw']['offset'], 3);
|
||||
} else {
|
||||
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // SHORT A 16-bit (2-byte) unsigned integer.
|
||||
if ($CurrentIFD['fields'][$i]['raw']['length'] <= 2) {
|
||||
$CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 2), $ThisFileInfo['tiff']['byte_order']);
|
||||
} else {
|
||||
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4: // LONG A 32-bit (4-byte) unsigned integer.
|
||||
if ($CurrentIFD['fields'][$i]['raw']['length'] <= 1) {
|
||||
$CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
|
||||
} else {
|
||||
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 5: // RATIONAL Two LONG_s: the first represents the numerator of a fraction, the second the denominator.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$ThisFileInfo['tiff']['ifd'][] = $CurrentIFD;
|
||||
$CurrentIFD = array();
|
||||
$nextIFDoffset = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']);
|
||||
|
||||
}
|
||||
|
||||
foreach ($ThisFileInfo['tiff']['ifd'] as $IFDid => $IFDarray) {
|
||||
foreach ($IFDarray['fields'] as $key => $fieldarray) {
|
||||
switch ($fieldarray['raw']['tag']) {
|
||||
case 256: // ImageWidth
|
||||
case 257: // ImageLength
|
||||
case 258: // BitsPerSample
|
||||
case 259: // Compression
|
||||
if (!isset($fieldarray['value'])) {
|
||||
fseek($fd, $fieldarray['offset'], SEEK_SET);
|
||||
$ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($fd, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case 270: // ImageDescription
|
||||
case 271: // Make
|
||||
case 272: // Model
|
||||
case 305: // Software
|
||||
case 306: // DateTime
|
||||
case 315: // Artist
|
||||
case 316: // HostComputer
|
||||
if (isset($fieldarray['value'])) {
|
||||
$ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $fieldarray['value'];
|
||||
} else {
|
||||
fseek($fd, $fieldarray['offset'], SEEK_SET);
|
||||
$ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($fd, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]);
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch ($fieldarray['raw']['tag']) {
|
||||
case 256: // ImageWidth
|
||||
$ThisFileInfo['video']['resolution_x'] = $fieldarray['value'];
|
||||
break;
|
||||
|
||||
case 257: // ImageLength
|
||||
$ThisFileInfo['video']['resolution_y'] = $fieldarray['value'];
|
||||
break;
|
||||
|
||||
case 258: // BitsPerSample
|
||||
if (isset($fieldarray['value'])) {
|
||||
$ThisFileInfo['video']['bits_per_sample'] = $fieldarray['value'];
|
||||
} else {
|
||||
$ThisFileInfo['video']['bits_per_sample'] = 0;
|
||||
for ($i = 0; $i < $fieldarray['raw']['length']; $i++) {
|
||||
$ThisFileInfo['video']['bits_per_sample'] += $this->TIFFendian2Int(substr($ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'], $i * $FieldTypeByteLength[$fieldarray['raw']['type']], $FieldTypeByteLength[$fieldarray['raw']['type']]), $ThisFileInfo['tiff']['byte_order']);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 259: // Compression
|
||||
$ThisFileInfo['video']['codec'] = $this->TIFFcompressionMethod($fieldarray['value']);
|
||||
break;
|
||||
|
||||
case 270: // ImageDescription
|
||||
case 271: // Make
|
||||
case 272: // Model
|
||||
case 305: // Software
|
||||
case 306: // DateTime
|
||||
case 315: // Artist
|
||||
case 316: // HostComputer
|
||||
@$ThisFileInfo['tiff']['comments'][$this->TIFFcommentName($fieldarray['raw']['tag'])][] = $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function TIFFendian2Int($bytestring, $byteorder) {
|
||||
if ($byteorder == 'Intel') {
|
||||
return getid3_lib::LittleEndian2Int($bytestring);
|
||||
} elseif ($byteorder == 'Motorola') {
|
||||
return getid3_lib::BigEndian2Int($bytestring);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function TIFFcompressionMethod($id) {
|
||||
static $TIFFcompressionMethod = array();
|
||||
if (empty($TIFFcompressionMethod)) {
|
||||
$TIFFcompressionMethod = array(
|
||||
1 => 'Uncompressed',
|
||||
2 => 'Huffman',
|
||||
3 => 'Fax - CCITT 3',
|
||||
5 => 'LZW',
|
||||
32773 => 'PackBits',
|
||||
);
|
||||
}
|
||||
return (isset($TIFFcompressionMethod[$id]) ? $TIFFcompressionMethod[$id] : 'unknown/invalid ('.$id.')');
|
||||
}
|
||||
|
||||
function TIFFcommentName($id) {
|
||||
static $TIFFcommentName = array();
|
||||
if (empty($TIFFcommentName)) {
|
||||
$TIFFcommentName = array(
|
||||
270 => 'imagedescription',
|
||||
271 => 'make',
|
||||
272 => 'model',
|
||||
305 => 'software',
|
||||
306 => 'datetime',
|
||||
315 => 'artist',
|
||||
316 => 'hostcomputer',
|
||||
);
|
||||
}
|
||||
return (isset($TIFFcommentName[$id]) ? $TIFFcommentName[$id] : 'unknown/invalid ('.$id.')');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.archive.doc.php //
|
||||
// module for analyzing MS Office (.doc, .xls, etc) files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_doc
|
||||
{
|
||||
|
||||
function getid3_doc(&$fd, &$ThisFileInfo) {
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'doc';
|
||||
|
||||
$ThisFileInfo['error'][] = 'MS Office (.doc, .xls, etc) parsing not enabled in this version of getID3()';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.misc.exe.php //
|
||||
// module for analyzing EXE files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_exe
|
||||
{
|
||||
|
||||
function getid3_exe(&$fd, &$ThisFileInfo) {
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$EXEheader = fread($fd, 28);
|
||||
|
||||
if (substr($EXEheader, 0, 2) != 'MZ') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "MZ" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($EXEheader, 0, 2).'" instead.';
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'exe';
|
||||
$ThisFileInfo['exe']['mz']['magic'] = 'MZ';
|
||||
|
||||
$ThisFileInfo['exe']['mz']['raw']['last_page_size'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 2, 2));
|
||||
$ThisFileInfo['exe']['mz']['raw']['page_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 4, 2));
|
||||
$ThisFileInfo['exe']['mz']['raw']['relocation_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 6, 2));
|
||||
$ThisFileInfo['exe']['mz']['raw']['header_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 8, 2));
|
||||
$ThisFileInfo['exe']['mz']['raw']['min_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 10, 2));
|
||||
$ThisFileInfo['exe']['mz']['raw']['max_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 12, 2));
|
||||
$ThisFileInfo['exe']['mz']['raw']['initial_ss'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 14, 2));
|
||||
$ThisFileInfo['exe']['mz']['raw']['initial_sp'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 16, 2));
|
||||
$ThisFileInfo['exe']['mz']['raw']['checksum'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 18, 2));
|
||||
$ThisFileInfo['exe']['mz']['raw']['cs_ip'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 20, 4));
|
||||
$ThisFileInfo['exe']['mz']['raw']['relocation_table_offset'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 24, 2));
|
||||
$ThisFileInfo['exe']['mz']['raw']['overlay_number'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 26, 2));
|
||||
|
||||
$ThisFileInfo['exe']['mz']['byte_size'] = (($ThisFileInfo['exe']['mz']['raw']['page_count'] - 1)) * 512 + $ThisFileInfo['exe']['mz']['raw']['last_page_size'];
|
||||
$ThisFileInfo['exe']['mz']['header_size'] = $ThisFileInfo['exe']['mz']['raw']['header_paragraphs'] * 16;
|
||||
$ThisFileInfo['exe']['mz']['memory_minimum'] = $ThisFileInfo['exe']['mz']['raw']['min_memory_paragraphs'] * 16;
|
||||
$ThisFileInfo['exe']['mz']['memory_recommended'] = $ThisFileInfo['exe']['mz']['raw']['max_memory_paragraphs'] * 16;
|
||||
|
||||
$ThisFileInfo['error'][] = 'EXE parsing not enabled in this version of getID3()';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,386 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.misc.iso.php //
|
||||
// module for analyzing ISO files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_iso
|
||||
{
|
||||
|
||||
function getid3_iso($fd, &$ThisFileInfo) {
|
||||
$ThisFileInfo['fileformat'] = 'iso';
|
||||
|
||||
for ($i = 16; $i <= 19; $i++) {
|
||||
fseek($fd, 2048 * $i, SEEK_SET);
|
||||
$ISOheader = fread($fd, 2048);
|
||||
if (substr($ISOheader, 1, 5) == 'CD001') {
|
||||
switch (ord($ISOheader{0})) {
|
||||
case 1:
|
||||
$ThisFileInfo['iso']['primary_volume_descriptor']['offset'] = 2048 * $i;
|
||||
$this->ParsePrimaryVolumeDescriptor($ISOheader, $ThisFileInfo);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$ThisFileInfo['iso']['supplementary_volume_descriptor']['offset'] = 2048 * $i;
|
||||
$this->ParseSupplementaryVolumeDescriptor($ISOheader, $ThisFileInfo);
|
||||
break;
|
||||
|
||||
default:
|
||||
// skip
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->ParsePathTable($fd, $ThisFileInfo);
|
||||
|
||||
$ThisFileInfo['iso']['files'] = array();
|
||||
foreach ($ThisFileInfo['iso']['path_table']['directories'] as $directorynum => $directorydata) {
|
||||
|
||||
$ThisFileInfo['iso']['directories'][$directorynum] = $this->ParseDirectoryRecord($fd, $directorydata, $ThisFileInfo);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function ParsePrimaryVolumeDescriptor(&$ISOheader, &$ThisFileInfo) {
|
||||
// ISO integer values are stored *BOTH* Little-Endian AND Big-Endian format!!
|
||||
// ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field
|
||||
|
||||
// shortcuts
|
||||
$ThisFileInfo['iso']['primary_volume_descriptor']['raw'] = array();
|
||||
$thisfile_iso_primaryVD = &$ThisFileInfo['iso']['primary_volume_descriptor'];
|
||||
$thisfile_iso_primaryVD_raw = &$thisfile_iso_primaryVD['raw'];
|
||||
|
||||
$thisfile_iso_primaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 0, 1));
|
||||
$thisfile_iso_primaryVD_raw['standard_identifier'] = substr($ISOheader, 1, 5);
|
||||
if ($thisfile_iso_primaryVD_raw['standard_identifier'] != 'CD001') {
|
||||
$ThisFileInfo['error'][] = 'Expected "CD001" at offset ('.($thisfile_iso_primaryVD['offset'] + 1).'), found "'.$thisfile_iso_primaryVD_raw['standard_identifier'].'" instead';
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
unset($ThisFileInfo['iso']);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$thisfile_iso_primaryVD_raw['volume_descriptor_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 6, 1));
|
||||
//$thisfile_iso_primaryVD_raw['unused_1'] = substr($ISOheader, 7, 1);
|
||||
$thisfile_iso_primaryVD_raw['system_identifier'] = substr($ISOheader, 8, 32);
|
||||
$thisfile_iso_primaryVD_raw['volume_identifier'] = substr($ISOheader, 40, 32);
|
||||
//$thisfile_iso_primaryVD_raw['unused_2'] = substr($ISOheader, 72, 8);
|
||||
$thisfile_iso_primaryVD_raw['volume_space_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 80, 4));
|
||||
//$thisfile_iso_primaryVD_raw['unused_3'] = substr($ISOheader, 88, 32);
|
||||
$thisfile_iso_primaryVD_raw['volume_set_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 120, 2));
|
||||
$thisfile_iso_primaryVD_raw['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 124, 2));
|
||||
$thisfile_iso_primaryVD_raw['logical_block_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 128, 2));
|
||||
$thisfile_iso_primaryVD_raw['path_table_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 132, 4));
|
||||
$thisfile_iso_primaryVD_raw['path_table_l_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 140, 2));
|
||||
$thisfile_iso_primaryVD_raw['path_table_l_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 144, 2));
|
||||
$thisfile_iso_primaryVD_raw['path_table_m_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 148, 2));
|
||||
$thisfile_iso_primaryVD_raw['path_table_m_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 152, 2));
|
||||
$thisfile_iso_primaryVD_raw['root_directory_record'] = substr($ISOheader, 156, 34);
|
||||
$thisfile_iso_primaryVD_raw['volume_set_identifier'] = substr($ISOheader, 190, 128);
|
||||
$thisfile_iso_primaryVD_raw['publisher_identifier'] = substr($ISOheader, 318, 128);
|
||||
$thisfile_iso_primaryVD_raw['data_preparer_identifier'] = substr($ISOheader, 446, 128);
|
||||
$thisfile_iso_primaryVD_raw['application_identifier'] = substr($ISOheader, 574, 128);
|
||||
$thisfile_iso_primaryVD_raw['copyright_file_identifier'] = substr($ISOheader, 702, 37);
|
||||
$thisfile_iso_primaryVD_raw['abstract_file_identifier'] = substr($ISOheader, 739, 37);
|
||||
$thisfile_iso_primaryVD_raw['bibliographic_file_identifier'] = substr($ISOheader, 776, 37);
|
||||
$thisfile_iso_primaryVD_raw['volume_creation_date_time'] = substr($ISOheader, 813, 17);
|
||||
$thisfile_iso_primaryVD_raw['volume_modification_date_time'] = substr($ISOheader, 830, 17);
|
||||
$thisfile_iso_primaryVD_raw['volume_expiration_date_time'] = substr($ISOheader, 847, 17);
|
||||
$thisfile_iso_primaryVD_raw['volume_effective_date_time'] = substr($ISOheader, 864, 17);
|
||||
$thisfile_iso_primaryVD_raw['file_structure_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 881, 1));
|
||||
//$thisfile_iso_primaryVD_raw['unused_4'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 882, 1));
|
||||
$thisfile_iso_primaryVD_raw['application_data'] = substr($ISOheader, 883, 512);
|
||||
//$thisfile_iso_primaryVD_raw['unused_5'] = substr($ISOheader, 1395, 653);
|
||||
|
||||
$thisfile_iso_primaryVD['system_identifier'] = trim($thisfile_iso_primaryVD_raw['system_identifier']);
|
||||
$thisfile_iso_primaryVD['volume_identifier'] = trim($thisfile_iso_primaryVD_raw['volume_identifier']);
|
||||
$thisfile_iso_primaryVD['volume_set_identifier'] = trim($thisfile_iso_primaryVD_raw['volume_set_identifier']);
|
||||
$thisfile_iso_primaryVD['publisher_identifier'] = trim($thisfile_iso_primaryVD_raw['publisher_identifier']);
|
||||
$thisfile_iso_primaryVD['data_preparer_identifier'] = trim($thisfile_iso_primaryVD_raw['data_preparer_identifier']);
|
||||
$thisfile_iso_primaryVD['application_identifier'] = trim($thisfile_iso_primaryVD_raw['application_identifier']);
|
||||
$thisfile_iso_primaryVD['copyright_file_identifier'] = trim($thisfile_iso_primaryVD_raw['copyright_file_identifier']);
|
||||
$thisfile_iso_primaryVD['abstract_file_identifier'] = trim($thisfile_iso_primaryVD_raw['abstract_file_identifier']);
|
||||
$thisfile_iso_primaryVD['bibliographic_file_identifier'] = trim($thisfile_iso_primaryVD_raw['bibliographic_file_identifier']);
|
||||
$thisfile_iso_primaryVD['volume_creation_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_creation_date_time']);
|
||||
$thisfile_iso_primaryVD['volume_modification_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_modification_date_time']);
|
||||
$thisfile_iso_primaryVD['volume_expiration_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_expiration_date_time']);
|
||||
$thisfile_iso_primaryVD['volume_effective_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_effective_date_time']);
|
||||
|
||||
if (($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048) > $ThisFileInfo['filesize']) {
|
||||
$ThisFileInfo['error'][] = 'Volume Space Size ('.($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048).' bytes) is larger than the file size ('.$ThisFileInfo['filesize'].' bytes) (truncated file?)';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function ParseSupplementaryVolumeDescriptor(&$ISOheader, &$ThisFileInfo) {
|
||||
// ISO integer values are stored Both-Endian format!!
|
||||
// ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field
|
||||
|
||||
// shortcuts
|
||||
$ThisFileInfo['iso']['supplementary_volume_descriptor']['raw'] = array();
|
||||
$thisfile_iso_supplementaryVD = &$ThisFileInfo['iso']['supplementary_volume_descriptor'];
|
||||
$thisfile_iso_supplementaryVD_raw = &$thisfile_iso_supplementaryVD['raw'];
|
||||
|
||||
$thisfile_iso_supplementaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 0, 1));
|
||||
$thisfile_iso_supplementaryVD_raw['standard_identifier'] = substr($ISOheader, 1, 5);
|
||||
if ($thisfile_iso_supplementaryVD_raw['standard_identifier'] != 'CD001') {
|
||||
$ThisFileInfo['error'][] = 'Expected "CD001" at offset ('.($thisfile_iso_supplementaryVD['offset'] + 1).'), found "'.$thisfile_iso_supplementaryVD_raw['standard_identifier'].'" instead';
|
||||
unset($ThisFileInfo['fileformat']);
|
||||
unset($ThisFileInfo['iso']);
|
||||
return false;
|
||||
}
|
||||
|
||||
$thisfile_iso_supplementaryVD_raw['volume_descriptor_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 6, 1));
|
||||
//$thisfile_iso_supplementaryVD_raw['unused_1'] = substr($ISOheader, 7, 1);
|
||||
$thisfile_iso_supplementaryVD_raw['system_identifier'] = substr($ISOheader, 8, 32);
|
||||
$thisfile_iso_supplementaryVD_raw['volume_identifier'] = substr($ISOheader, 40, 32);
|
||||
//$thisfile_iso_supplementaryVD_raw['unused_2'] = substr($ISOheader, 72, 8);
|
||||
$thisfile_iso_supplementaryVD_raw['volume_space_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 80, 4));
|
||||
if ($thisfile_iso_supplementaryVD_raw['volume_space_size'] == 0) {
|
||||
// Supplementary Volume Descriptor not used
|
||||
//unset($thisfile_iso_supplementaryVD);
|
||||
//return false;
|
||||
}
|
||||
|
||||
//$thisfile_iso_supplementaryVD_raw['unused_3'] = substr($ISOheader, 88, 32);
|
||||
$thisfile_iso_supplementaryVD_raw['volume_set_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 120, 2));
|
||||
$thisfile_iso_supplementaryVD_raw['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 124, 2));
|
||||
$thisfile_iso_supplementaryVD_raw['logical_block_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 128, 2));
|
||||
$thisfile_iso_supplementaryVD_raw['path_table_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 132, 4));
|
||||
$thisfile_iso_supplementaryVD_raw['path_table_l_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 140, 2));
|
||||
$thisfile_iso_supplementaryVD_raw['path_table_l_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 144, 2));
|
||||
$thisfile_iso_supplementaryVD_raw['path_table_m_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 148, 2));
|
||||
$thisfile_iso_supplementaryVD_raw['path_table_m_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 152, 2));
|
||||
$thisfile_iso_supplementaryVD_raw['root_directory_record'] = substr($ISOheader, 156, 34);
|
||||
$thisfile_iso_supplementaryVD_raw['volume_set_identifier'] = substr($ISOheader, 190, 128);
|
||||
$thisfile_iso_supplementaryVD_raw['publisher_identifier'] = substr($ISOheader, 318, 128);
|
||||
$thisfile_iso_supplementaryVD_raw['data_preparer_identifier'] = substr($ISOheader, 446, 128);
|
||||
$thisfile_iso_supplementaryVD_raw['application_identifier'] = substr($ISOheader, 574, 128);
|
||||
$thisfile_iso_supplementaryVD_raw['copyright_file_identifier'] = substr($ISOheader, 702, 37);
|
||||
$thisfile_iso_supplementaryVD_raw['abstract_file_identifier'] = substr($ISOheader, 739, 37);
|
||||
$thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier'] = substr($ISOheader, 776, 37);
|
||||
$thisfile_iso_supplementaryVD_raw['volume_creation_date_time'] = substr($ISOheader, 813, 17);
|
||||
$thisfile_iso_supplementaryVD_raw['volume_modification_date_time'] = substr($ISOheader, 830, 17);
|
||||
$thisfile_iso_supplementaryVD_raw['volume_expiration_date_time'] = substr($ISOheader, 847, 17);
|
||||
$thisfile_iso_supplementaryVD_raw['volume_effective_date_time'] = substr($ISOheader, 864, 17);
|
||||
$thisfile_iso_supplementaryVD_raw['file_structure_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 881, 1));
|
||||
//$thisfile_iso_supplementaryVD_raw['unused_4'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 882, 1));
|
||||
$thisfile_iso_supplementaryVD_raw['application_data'] = substr($ISOheader, 883, 512);
|
||||
//$thisfile_iso_supplementaryVD_raw['unused_5'] = substr($ISOheader, 1395, 653);
|
||||
|
||||
$thisfile_iso_supplementaryVD['system_identifier'] = trim($thisfile_iso_supplementaryVD_raw['system_identifier']);
|
||||
$thisfile_iso_supplementaryVD['volume_identifier'] = trim($thisfile_iso_supplementaryVD_raw['volume_identifier']);
|
||||
$thisfile_iso_supplementaryVD['volume_set_identifier'] = trim($thisfile_iso_supplementaryVD_raw['volume_set_identifier']);
|
||||
$thisfile_iso_supplementaryVD['publisher_identifier'] = trim($thisfile_iso_supplementaryVD_raw['publisher_identifier']);
|
||||
$thisfile_iso_supplementaryVD['data_preparer_identifier'] = trim($thisfile_iso_supplementaryVD_raw['data_preparer_identifier']);
|
||||
$thisfile_iso_supplementaryVD['application_identifier'] = trim($thisfile_iso_supplementaryVD_raw['application_identifier']);
|
||||
$thisfile_iso_supplementaryVD['copyright_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['copyright_file_identifier']);
|
||||
$thisfile_iso_supplementaryVD['abstract_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['abstract_file_identifier']);
|
||||
$thisfile_iso_supplementaryVD['bibliographic_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier']);
|
||||
$thisfile_iso_supplementaryVD['volume_creation_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_creation_date_time']);
|
||||
$thisfile_iso_supplementaryVD['volume_modification_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_modification_date_time']);
|
||||
$thisfile_iso_supplementaryVD['volume_expiration_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_expiration_date_time']);
|
||||
$thisfile_iso_supplementaryVD['volume_effective_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_effective_date_time']);
|
||||
|
||||
if (($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']) > $ThisFileInfo['filesize']) {
|
||||
$ThisFileInfo['error'][] = 'Volume Space Size ('.($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']).' bytes) is larger than the file size ('.$ThisFileInfo['filesize'].' bytes) (truncated file?)';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function ParsePathTable($fd, &$ThisFileInfo) {
|
||||
if (!isset($ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']) && !isset($ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_l_location'])) {
|
||||
return false;
|
||||
}
|
||||
if (isset($ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location'])) {
|
||||
$PathTableLocation = $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location'];
|
||||
$PathTableSize = $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_size'];
|
||||
$TextEncoding = 'UTF-16BE'; // Big-Endian Unicode
|
||||
} else {
|
||||
$PathTableLocation = $ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_l_location'];
|
||||
$PathTableSize = $ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_size'];
|
||||
$TextEncoding = 'ISO-8859-1'; // Latin-1
|
||||
}
|
||||
|
||||
if (($PathTableLocation * 2048) > $ThisFileInfo['filesize']) {
|
||||
$ThisFileInfo['error'][] = 'Path Table Location specifies an offset ('.($PathTableLocation * 2048).') beyond the end-of-file ('.$ThisFileInfo['filesize'].')';
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['iso']['path_table']['offset'] = $PathTableLocation * 2048;
|
||||
fseek($fd, $ThisFileInfo['iso']['path_table']['offset'], SEEK_SET);
|
||||
$ThisFileInfo['iso']['path_table']['raw'] = fread($fd, $PathTableSize);
|
||||
|
||||
$offset = 0;
|
||||
$pathcounter = 1;
|
||||
while ($offset < $PathTableSize) {
|
||||
// shortcut
|
||||
$ThisFileInfo['iso']['path_table']['directories'][$pathcounter] = array();
|
||||
$thisfile_iso_pathtable_directories_current = &$ThisFileInfo['iso']['path_table']['directories'][$pathcounter];
|
||||
|
||||
$thisfile_iso_pathtable_directories_current['length'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 1));
|
||||
$offset += 1;
|
||||
$thisfile_iso_pathtable_directories_current['extended_length'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 1));
|
||||
$offset += 1;
|
||||
$thisfile_iso_pathtable_directories_current['location_logical'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 4));
|
||||
$offset += 4;
|
||||
$thisfile_iso_pathtable_directories_current['parent_directory'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 2));
|
||||
$offset += 2;
|
||||
$thisfile_iso_pathtable_directories_current['name'] = substr($ThisFileInfo['iso']['path_table']['raw'], $offset, $thisfile_iso_pathtable_directories_current['length']);
|
||||
$offset += $thisfile_iso_pathtable_directories_current['length'] + ($thisfile_iso_pathtable_directories_current['length'] % 2);
|
||||
|
||||
$thisfile_iso_pathtable_directories_current['name_ascii'] = getid3_lib::iconv_fallback($TextEncoding, $ThisFileInfo['encoding'], $thisfile_iso_pathtable_directories_current['name']);
|
||||
|
||||
$thisfile_iso_pathtable_directories_current['location_bytes'] = $thisfile_iso_pathtable_directories_current['location_logical'] * 2048;
|
||||
if ($pathcounter == 1) {
|
||||
$thisfile_iso_pathtable_directories_current['full_path'] = '/';
|
||||
} else {
|
||||
$thisfile_iso_pathtable_directories_current['full_path'] = $ThisFileInfo['iso']['path_table']['directories'][$thisfile_iso_pathtable_directories_current['parent_directory']]['full_path'].$thisfile_iso_pathtable_directories_current['name_ascii'].'/';
|
||||
}
|
||||
$FullPathArray[] = $thisfile_iso_pathtable_directories_current['full_path'];
|
||||
|
||||
$pathcounter++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function ParseDirectoryRecord(&$fd, $directorydata, &$ThisFileInfo) {
|
||||
if (isset($ThisFileInfo['iso']['supplementary_volume_descriptor'])) {
|
||||
$TextEncoding = 'UTF-16BE'; // Big-Endian Unicode
|
||||
} else {
|
||||
$TextEncoding = 'ISO-8859-1'; // Latin-1
|
||||
}
|
||||
|
||||
fseek($fd, $directorydata['location_bytes'], SEEK_SET);
|
||||
$DirectoryRecordData = fread($fd, 1);
|
||||
|
||||
while (ord($DirectoryRecordData{0}) > 33) {
|
||||
|
||||
$DirectoryRecordData .= fread($fd, ord($DirectoryRecordData{0}) - 1);
|
||||
|
||||
$ThisDirectoryRecord['raw']['length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 0, 1));
|
||||
$ThisDirectoryRecord['raw']['extended_attribute_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 1, 1));
|
||||
$ThisDirectoryRecord['raw']['offset_logical'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 2, 4));
|
||||
$ThisDirectoryRecord['raw']['filesize'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 10, 4));
|
||||
$ThisDirectoryRecord['raw']['recording_date_time'] = substr($DirectoryRecordData, 18, 7);
|
||||
$ThisDirectoryRecord['raw']['file_flags'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 25, 1));
|
||||
$ThisDirectoryRecord['raw']['file_unit_size'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 26, 1));
|
||||
$ThisDirectoryRecord['raw']['interleave_gap_size'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 27, 1));
|
||||
$ThisDirectoryRecord['raw']['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 28, 2));
|
||||
$ThisDirectoryRecord['raw']['file_identifier_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 32, 1));
|
||||
$ThisDirectoryRecord['raw']['file_identifier'] = substr($DirectoryRecordData, 33, $ThisDirectoryRecord['raw']['file_identifier_length']);
|
||||
|
||||
$ThisDirectoryRecord['file_identifier_ascii'] = getid3_lib::iconv_fallback($TextEncoding, $ThisFileInfo['encoding'], $ThisDirectoryRecord['raw']['file_identifier']);
|
||||
|
||||
$ThisDirectoryRecord['filesize'] = $ThisDirectoryRecord['raw']['filesize'];
|
||||
$ThisDirectoryRecord['offset_bytes'] = $ThisDirectoryRecord['raw']['offset_logical'] * 2048;
|
||||
$ThisDirectoryRecord['file_flags']['hidden'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x01);
|
||||
$ThisDirectoryRecord['file_flags']['directory'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x02);
|
||||
$ThisDirectoryRecord['file_flags']['associated'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x04);
|
||||
$ThisDirectoryRecord['file_flags']['extended'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x08);
|
||||
$ThisDirectoryRecord['file_flags']['permissions'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x10);
|
||||
$ThisDirectoryRecord['file_flags']['multiple'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x80);
|
||||
$ThisDirectoryRecord['recording_timestamp'] = $this->ISOtime2UNIXtime($ThisDirectoryRecord['raw']['recording_date_time']);
|
||||
|
||||
if ($ThisDirectoryRecord['file_flags']['directory']) {
|
||||
$ThisDirectoryRecord['filename'] = $directorydata['full_path'];
|
||||
} else {
|
||||
$ThisDirectoryRecord['filename'] = $directorydata['full_path'].$this->ISOstripFilenameVersion($ThisDirectoryRecord['file_identifier_ascii']);
|
||||
$ThisFileInfo['iso']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['iso']['files'], getid3_lib::CreateDeepArray($ThisDirectoryRecord['filename'], '/', $ThisDirectoryRecord['filesize']));
|
||||
}
|
||||
|
||||
$DirectoryRecord[] = $ThisDirectoryRecord;
|
||||
$DirectoryRecordData = fread($fd, 1);
|
||||
}
|
||||
|
||||
return $DirectoryRecord;
|
||||
}
|
||||
|
||||
function ISOstripFilenameVersion($ISOfilename) {
|
||||
// convert 'filename.ext;1' to 'filename.ext'
|
||||
if (!strstr($ISOfilename, ';')) {
|
||||
return $ISOfilename;
|
||||
} else {
|
||||
return substr($ISOfilename, 0, strpos($ISOfilename, ';'));
|
||||
}
|
||||
}
|
||||
|
||||
function ISOtimeText2UNIXtime($ISOtime) {
|
||||
|
||||
$UNIXyear = (int) substr($ISOtime, 0, 4);
|
||||
$UNIXmonth = (int) substr($ISOtime, 4, 2);
|
||||
$UNIXday = (int) substr($ISOtime, 6, 2);
|
||||
$UNIXhour = (int) substr($ISOtime, 8, 2);
|
||||
$UNIXminute = (int) substr($ISOtime, 10, 2);
|
||||
$UNIXsecond = (int) substr($ISOtime, 12, 2);
|
||||
|
||||
if (!$UNIXyear) {
|
||||
return false;
|
||||
}
|
||||
return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
|
||||
}
|
||||
|
||||
function ISOtime2UNIXtime($ISOtime) {
|
||||
// Represented by seven bytes:
|
||||
// 1: Number of years since 1900
|
||||
// 2: Month of the year from 1 to 12
|
||||
// 3: Day of the Month from 1 to 31
|
||||
// 4: Hour of the day from 0 to 23
|
||||
// 5: Minute of the hour from 0 to 59
|
||||
// 6: second of the minute from 0 to 59
|
||||
// 7: Offset from Greenwich Mean Time in number of 15 minute intervals from -48 (West) to +52 (East)
|
||||
|
||||
$UNIXyear = ord($ISOtime{0}) + 1900;
|
||||
$UNIXmonth = ord($ISOtime{1});
|
||||
$UNIXday = ord($ISOtime{2});
|
||||
$UNIXhour = ord($ISOtime{3});
|
||||
$UNIXminute = ord($ISOtime{4});
|
||||
$UNIXsecond = ord($ISOtime{5});
|
||||
$GMToffset = $this->TwosCompliment2Decimal(ord($ISOtime{5}));
|
||||
|
||||
return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
|
||||
}
|
||||
|
||||
function TwosCompliment2Decimal($BinaryValue) {
|
||||
// http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html
|
||||
// First check if the number is negative or positive by looking at the sign bit.
|
||||
// If it is positive, simply convert it to decimal.
|
||||
// If it is negative, make it positive by inverting the bits and adding one.
|
||||
// Then, convert the result to decimal.
|
||||
// The negative of this number is the value of the original binary.
|
||||
|
||||
if ($BinaryValue & 0x80) {
|
||||
|
||||
// negative number
|
||||
return (0 - ((~$BinaryValue & 0xFF) + 1));
|
||||
} else {
|
||||
// positive number
|
||||
return $BinaryValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.archive.doc.php //
|
||||
// module for analyzing MS Office (.doc, .xls, etc) files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_doc
|
||||
{
|
||||
|
||||
function getid3_doc(&$fd, &$ThisFileInfo) {
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'doc';
|
||||
|
||||
$ThisFileInfo['error'][] = 'MS Office (.doc, .xls, etc) parsing not enabled in this version of getID3()';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.misc.par2.php //
|
||||
// module for analyzing PAR2 files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_par2
|
||||
{
|
||||
|
||||
function getid3_par2(&$fd, &$ThisFileInfo) {
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'par2';
|
||||
|
||||
$ThisFileInfo['error'][] = 'PAR2 parsing not enabled in this version of getID3()';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.misc.pdf.php //
|
||||
// module for analyzing PDF files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_pdf
|
||||
{
|
||||
|
||||
function getid3_pdf(&$fd, &$ThisFileInfo) {
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'pdf';
|
||||
|
||||
$ThisFileInfo['error'][] = 'PDF parsing not enabled in this version of getID3()';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,290 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.tag.apetag.php //
|
||||
// module for analyzing APE tags //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
class getid3_apetag
|
||||
{
|
||||
|
||||
function getid3_apetag(&$fd, &$ThisFileInfo, $overrideendoffset=0) {
|
||||
|
||||
if ($ThisFileInfo['filesize'] >= pow(2, 31)) {
|
||||
$ThisFileInfo['warning'][] = 'Unable to check for APEtags because file is larger than 2GB';
|
||||
return false;
|
||||
}
|
||||
|
||||
$id3v1tagsize = 128;
|
||||
$apetagheadersize = 32;
|
||||
$lyrics3tagsize = 10;
|
||||
|
||||
if ($overrideendoffset == 0) {
|
||||
|
||||
fseek($fd, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
|
||||
$APEfooterID3v1 = fread($fd, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
|
||||
|
||||
//if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
|
||||
if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
|
||||
|
||||
// APE tag found before ID3v1
|
||||
$ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'] - $id3v1tagsize;
|
||||
|
||||
//} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
|
||||
} elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') {
|
||||
|
||||
// APE tag found, no ID3v1
|
||||
$ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'];
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
fseek($fd, $overrideendoffset - $apetagheadersize, SEEK_SET);
|
||||
if (fread($fd, 8) == 'APETAGEX') {
|
||||
$ThisFileInfo['ape']['tag_offset_end'] = $overrideendoffset;
|
||||
}
|
||||
|
||||
}
|
||||
if (!isset($ThisFileInfo['ape']['tag_offset_end'])) {
|
||||
|
||||
// APE tag not found
|
||||
unset($ThisFileInfo['ape']);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// shortcut
|
||||
$thisfile_ape = &$ThisFileInfo['ape'];
|
||||
|
||||
fseek($fd, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET);
|
||||
$APEfooterData = fread($fd, 32);
|
||||
if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
|
||||
$ThisFileInfo['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end'];
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
|
||||
fseek($fd, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET);
|
||||
$thisfile_ape['tag_offset_start'] = ftell($fd);
|
||||
$APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
|
||||
} else {
|
||||
$thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
|
||||
fseek($fd, $thisfile_ape['tag_offset_start'], SEEK_SET);
|
||||
$APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize']);
|
||||
}
|
||||
$ThisFileInfo['avdataend'] = $thisfile_ape['tag_offset_start'];
|
||||
|
||||
if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) {
|
||||
$ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data';
|
||||
unset($ThisFileInfo['id3v1']);
|
||||
foreach ($ThisFileInfo['warning'] as $key => $value) {
|
||||
if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
|
||||
unset($ThisFileInfo['warning'][$key]);
|
||||
sort($ThisFileInfo['warning']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$offset = 0;
|
||||
if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
|
||||
if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) {
|
||||
$offset += $apetagheadersize;
|
||||
} else {
|
||||
$ThisFileInfo['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start'];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// shortcut
|
||||
$ThisFileInfo['replay_gain'] = array();
|
||||
$thisfile_replaygain = &$ThisFileInfo['replay_gain'];
|
||||
|
||||
for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) {
|
||||
$value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
|
||||
$offset += 4;
|
||||
$item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
|
||||
$offset += 4;
|
||||
if (strstr(substr($APEtagData, $offset), "\x00") === false) {
|
||||
$ThisFileInfo['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset);
|
||||
return false;
|
||||
}
|
||||
$ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset;
|
||||
$item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength));
|
||||
|
||||
// shortcut
|
||||
$thisfile_ape['items'][$item_key] = array();
|
||||
$thisfile_ape_items_current = &$thisfile_ape['items'][$item_key];
|
||||
|
||||
$offset += ($ItemKeyLength + 1); // skip 0x00 terminator
|
||||
$thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
|
||||
$offset += $value_size;
|
||||
|
||||
$thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
|
||||
switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
|
||||
case 0: // UTF-8
|
||||
case 3: // Locator (URL, filename, etc), UTF-8 encoded
|
||||
$thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data']));
|
||||
break;
|
||||
|
||||
default: // binary data
|
||||
break;
|
||||
}
|
||||
|
||||
switch (strtolower($item_key)) {
|
||||
case 'replaygain_track_gain':
|
||||
$thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
||||
$thisfile_replaygain['track']['originator'] = 'unspecified';
|
||||
break;
|
||||
|
||||
case 'replaygain_track_peak':
|
||||
$thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
||||
$thisfile_replaygain['track']['originator'] = 'unspecified';
|
||||
if ($thisfile_replaygain['track']['peak'] <= 0) {
|
||||
$ThisFileInfo['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'replaygain_album_gain':
|
||||
$thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
||||
$thisfile_replaygain['album']['originator'] = 'unspecified';
|
||||
break;
|
||||
|
||||
case 'replaygain_album_peak':
|
||||
$thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
||||
$thisfile_replaygain['album']['originator'] = 'unspecified';
|
||||
if ($thisfile_replaygain['album']['peak'] <= 0) {
|
||||
$ThisFileInfo['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'mp3gain_undo':
|
||||
list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
|
||||
$thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
|
||||
$thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
|
||||
$thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false);
|
||||
break;
|
||||
|
||||
case 'mp3gain_minmax':
|
||||
list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
|
||||
$thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
|
||||
$thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
|
||||
break;
|
||||
|
||||
case 'mp3gain_album_minmax':
|
||||
list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
|
||||
$thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
|
||||
$thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
|
||||
break;
|
||||
|
||||
case 'tracknumber':
|
||||
foreach ($thisfile_ape_items_current['data'] as $comment) {
|
||||
$thisfile_ape['comments']['track'][] = $comment;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
foreach ($thisfile_ape_items_current['data'] as $comment) {
|
||||
$thisfile_ape['comments'][strtolower($item_key)][] = $comment;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (empty($thisfile_replaygain)) {
|
||||
unset($ThisFileInfo['replay_gain']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function parseAPEheaderFooter($APEheaderFooterData) {
|
||||
// http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
|
||||
|
||||
// shortcut
|
||||
$headerfooterinfo['raw'] = array();
|
||||
$headerfooterinfo_raw = &$headerfooterinfo['raw'];
|
||||
|
||||
$headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8);
|
||||
if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') {
|
||||
return false;
|
||||
}
|
||||
$headerfooterinfo_raw['version'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 8, 4));
|
||||
$headerfooterinfo_raw['tagsize'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4));
|
||||
$headerfooterinfo_raw['tag_items'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4));
|
||||
$headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4));
|
||||
$headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8);
|
||||
|
||||
$headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000;
|
||||
if ($headerfooterinfo['tag_version'] >= 2) {
|
||||
$headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']);
|
||||
}
|
||||
return $headerfooterinfo;
|
||||
}
|
||||
|
||||
function parseAPEtagFlags($rawflagint) {
|
||||
// "Note: APE Tags 1.0 do not use any of the APE Tag flags.
|
||||
// All are set to zero on creation and ignored on reading."
|
||||
// http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html
|
||||
$flags['header'] = (bool) ($rawflagint & 0x80000000);
|
||||
$flags['footer'] = (bool) ($rawflagint & 0x40000000);
|
||||
$flags['this_is_header'] = (bool) ($rawflagint & 0x20000000);
|
||||
$flags['item_contents_raw'] = ($rawflagint & 0x00000006) >> 1;
|
||||
$flags['read_only'] = (bool) ($rawflagint & 0x00000001);
|
||||
|
||||
$flags['item_contents'] = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']);
|
||||
|
||||
return $flags;
|
||||
}
|
||||
|
||||
function APEcontentTypeFlagLookup($contenttypeid) {
|
||||
static $APEcontentTypeFlagLookup = array(
|
||||
0 => 'utf-8',
|
||||
1 => 'binary',
|
||||
2 => 'external',
|
||||
3 => 'reserved'
|
||||
);
|
||||
return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid');
|
||||
}
|
||||
|
||||
function APEtagItemIsUTF8Lookup($itemkey) {
|
||||
static $APEtagItemIsUTF8Lookup = array(
|
||||
'title',
|
||||
'subtitle',
|
||||
'artist',
|
||||
'album',
|
||||
'debut album',
|
||||
'publisher',
|
||||
'conductor',
|
||||
'track',
|
||||
'composer',
|
||||
'comment',
|
||||
'copyright',
|
||||
'publicationright',
|
||||
'file',
|
||||
'year',
|
||||
'record date',
|
||||
'record location',
|
||||
'genre',
|
||||
'media',
|
||||
'related',
|
||||
'isrc',
|
||||
'abstract',
|
||||
'language',
|
||||
'bibliography'
|
||||
);
|
||||
return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,361 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.tag.id3v1.php //
|
||||
// module for analyzing ID3v1 tags //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_id3v1
|
||||
{
|
||||
|
||||
function getid3_id3v1(&$fd, &$ThisFileInfo) {
|
||||
|
||||
if ($ThisFileInfo['filesize'] >= pow(2, 31)) {
|
||||
$ThisFileInfo['warning'][] = 'Unable to check for ID3v1 because file is larger than 2GB';
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek($fd, -256, SEEK_END);
|
||||
$preid3v1 = fread($fd, 128);
|
||||
$id3v1tag = fread($fd, 128);
|
||||
|
||||
if (substr($id3v1tag, 0, 3) == 'TAG') {
|
||||
|
||||
$ThisFileInfo['avdataend'] = $ThisFileInfo['filesize'] - 128;
|
||||
|
||||
$ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30));
|
||||
$ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30));
|
||||
$ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30));
|
||||
$ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4));
|
||||
$ParsedID3v1['comment'] = substr($id3v1tag, 97, 30); // can't remove nulls yet, track detection depends on them
|
||||
$ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1));
|
||||
|
||||
// If second-last byte of comment field is null and last byte of comment field is non-null
|
||||
// then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
|
||||
if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) {
|
||||
$ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1));
|
||||
$ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
|
||||
}
|
||||
$ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
|
||||
|
||||
$ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']);
|
||||
if (!empty($ParsedID3v1['genre'])) {
|
||||
unset($ParsedID3v1['genreid']);
|
||||
}
|
||||
if (empty($ParsedID3v1['genre']) || (@$ParsedID3v1['genre'] == 'Unknown')) {
|
||||
unset($ParsedID3v1['genre']);
|
||||
}
|
||||
|
||||
foreach ($ParsedID3v1 as $key => $value) {
|
||||
$ParsedID3v1['comments'][$key][0] = $value;
|
||||
}
|
||||
|
||||
// ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
|
||||
$GoodFormatID3v1tag = $this->GenerateID3v1Tag(
|
||||
$ParsedID3v1['title'],
|
||||
$ParsedID3v1['artist'],
|
||||
$ParsedID3v1['album'],
|
||||
$ParsedID3v1['year'],
|
||||
(isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false),
|
||||
$ParsedID3v1['comment'],
|
||||
@$ParsedID3v1['track']);
|
||||
$ParsedID3v1['padding_valid'] = true;
|
||||
if ($id3v1tag !== $GoodFormatID3v1tag) {
|
||||
$ParsedID3v1['padding_valid'] = false;
|
||||
$ThisFileInfo['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding';
|
||||
}
|
||||
|
||||
$ParsedID3v1['tag_offset_end'] = $ThisFileInfo['filesize'];
|
||||
$ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128;
|
||||
|
||||
$ThisFileInfo['id3v1'] = $ParsedID3v1;
|
||||
}
|
||||
|
||||
if (substr($preid3v1, 0, 3) == 'TAG') {
|
||||
// The way iTunes handles tags is, well, brain-damaged.
|
||||
// It completely ignores v1 if ID3v2 is present.
|
||||
// This goes as far as adding a new v1 tag *even if there already is one*
|
||||
|
||||
// A suspected double-ID3v1 tag has been detected, but it could be that
|
||||
// the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag
|
||||
if (substr($preid3v1, 96, 8) == 'APETAGEX') {
|
||||
// an APE tag footer was found before the last ID3v1, assume false "TAG" synch
|
||||
} elseif (substr($preid3v1, 119, 6) == 'LYRICS') {
|
||||
// a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch
|
||||
} else {
|
||||
// APE and Lyrics3 footers not found - assume double ID3v1
|
||||
$ThisFileInfo['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes';
|
||||
$ThisFileInfo['avdataend'] -= 128;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function cutfield($str) {
|
||||
return trim(substr($str, 0, strcspn($str, "\x00")));
|
||||
}
|
||||
|
||||
function ArrayOfGenres($allowSCMPXextended=false) {
|
||||
static $GenreLookup = array(
|
||||
0 => 'Blues',
|
||||
1 => 'Classic Rock',
|
||||
2 => 'Country',
|
||||
3 => 'Dance',
|
||||
4 => 'Disco',
|
||||
5 => 'Funk',
|
||||
6 => 'Grunge',
|
||||
7 => 'Hip-Hop',
|
||||
8 => 'Jazz',
|
||||
9 => 'Metal',
|
||||
10 => 'New Age',
|
||||
11 => 'Oldies',
|
||||
12 => 'Other',
|
||||
13 => 'Pop',
|
||||
14 => 'R&B',
|
||||
15 => 'Rap',
|
||||
16 => 'Reggae',
|
||||
17 => 'Rock',
|
||||
18 => 'Techno',
|
||||
19 => 'Industrial',
|
||||
20 => 'Alternative',
|
||||
21 => 'Ska',
|
||||
22 => 'Death Metal',
|
||||
23 => 'Pranks',
|
||||
24 => 'Soundtrack',
|
||||
25 => 'Euro-Techno',
|
||||
26 => 'Ambient',
|
||||
27 => 'Trip-Hop',
|
||||
28 => 'Vocal',
|
||||
29 => 'Jazz+Funk',
|
||||
30 => 'Fusion',
|
||||
31 => 'Trance',
|
||||
32 => 'Classical',
|
||||
33 => 'Instrumental',
|
||||
34 => 'Acid',
|
||||
35 => 'House',
|
||||
36 => 'Game',
|
||||
37 => 'Sound Clip',
|
||||
38 => 'Gospel',
|
||||
39 => 'Noise',
|
||||
40 => 'Alt. Rock',
|
||||
41 => 'Bass',
|
||||
42 => 'Soul',
|
||||
43 => 'Punk',
|
||||
44 => 'Space',
|
||||
45 => 'Meditative',
|
||||
46 => 'Instrumental Pop',
|
||||
47 => 'Instrumental Rock',
|
||||
48 => 'Ethnic',
|
||||
49 => 'Gothic',
|
||||
50 => 'Darkwave',
|
||||
51 => 'Techno-Industrial',
|
||||
52 => 'Electronic',
|
||||
53 => 'Pop-Folk',
|
||||
54 => 'Eurodance',
|
||||
55 => 'Dream',
|
||||
56 => 'Southern Rock',
|
||||
57 => 'Comedy',
|
||||
58 => 'Cult',
|
||||
59 => 'Gangsta Rap',
|
||||
60 => 'Top 40',
|
||||
61 => 'Christian Rap',
|
||||
62 => 'Pop/Funk',
|
||||
63 => 'Jungle',
|
||||
64 => 'Native American',
|
||||
65 => 'Cabaret',
|
||||
66 => 'New Wave',
|
||||
67 => 'Psychedelic',
|
||||
68 => 'Rave',
|
||||
69 => 'Showtunes',
|
||||
70 => 'Trailer',
|
||||
71 => 'Lo-Fi',
|
||||
72 => 'Tribal',
|
||||
73 => 'Acid Punk',
|
||||
74 => 'Acid Jazz',
|
||||
75 => 'Polka',
|
||||
76 => 'Retro',
|
||||
77 => 'Musical',
|
||||
78 => 'Rock & Roll',
|
||||
79 => 'Hard Rock',
|
||||
80 => 'Folk',
|
||||
81 => 'Folk/Rock',
|
||||
82 => 'National Folk',
|
||||
83 => 'Swing',
|
||||
84 => 'Fast-Fusion',
|
||||
85 => 'Bebob',
|
||||
86 => 'Latin',
|
||||
87 => 'Revival',
|
||||
88 => 'Celtic',
|
||||
89 => 'Bluegrass',
|
||||
90 => 'Avantgarde',
|
||||
91 => 'Gothic Rock',
|
||||
92 => 'Progressive Rock',
|
||||
93 => 'Psychedelic Rock',
|
||||
94 => 'Symphonic Rock',
|
||||
95 => 'Slow Rock',
|
||||
96 => 'Big Band',
|
||||
97 => 'Chorus',
|
||||
98 => 'Easy Listening',
|
||||
99 => 'Acoustic',
|
||||
100 => 'Humour',
|
||||
101 => 'Speech',
|
||||
102 => 'Chanson',
|
||||
103 => 'Opera',
|
||||
104 => 'Chamber Music',
|
||||
105 => 'Sonata',
|
||||
106 => 'Symphony',
|
||||
107 => 'Booty Bass',
|
||||
108 => 'Primus',
|
||||
109 => 'Porn Groove',
|
||||
110 => 'Satire',
|
||||
111 => 'Slow Jam',
|
||||
112 => 'Club',
|
||||
113 => 'Tango',
|
||||
114 => 'Samba',
|
||||
115 => 'Folklore',
|
||||
116 => 'Ballad',
|
||||
117 => 'Power Ballad',
|
||||
118 => 'Rhythmic Soul',
|
||||
119 => 'Freestyle',
|
||||
120 => 'Duet',
|
||||
121 => 'Punk Rock',
|
||||
122 => 'Drum Solo',
|
||||
123 => 'A Cappella',
|
||||
124 => 'Euro-House',
|
||||
125 => 'Dance Hall',
|
||||
126 => 'Goa',
|
||||
127 => 'Drum & Bass',
|
||||
128 => 'Club-House',
|
||||
129 => 'Hardcore',
|
||||
130 => 'Terror',
|
||||
131 => 'Indie',
|
||||
132 => 'BritPop',
|
||||
133 => 'Negerpunk',
|
||||
134 => 'Polsk Punk',
|
||||
135 => 'Beat',
|
||||
136 => 'Christian Gangsta Rap',
|
||||
137 => 'Heavy Metal',
|
||||
138 => 'Black Metal',
|
||||
139 => 'Crossover',
|
||||
140 => 'Contemporary Christian',
|
||||
141 => 'Christian Rock',
|
||||
142 => 'Merengue',
|
||||
143 => 'Salsa',
|
||||
144 => 'Trash Metal',
|
||||
145 => 'Anime',
|
||||
146 => 'JPop',
|
||||
147 => 'Synthpop',
|
||||
|
||||
255 => 'Unknown',
|
||||
|
||||
'CR' => 'Cover',
|
||||
'RX' => 'Remix'
|
||||
);
|
||||
|
||||
static $GenreLookupSCMPX = array();
|
||||
if ($allowSCMPXextended && empty($GenreLookupSCMPX)) {
|
||||
$GenreLookupSCMPX = $GenreLookup;
|
||||
// http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended
|
||||
// Extended ID3v1 genres invented by SCMPX
|
||||
// Note that 255 "Japanese Anime" conflicts with standard "Unknown"
|
||||
$GenreLookupSCMPX[240] = 'Sacred';
|
||||
$GenreLookupSCMPX[241] = 'Northern Europe';
|
||||
$GenreLookupSCMPX[242] = 'Irish & Scottish';
|
||||
$GenreLookupSCMPX[243] = 'Scotland';
|
||||
$GenreLookupSCMPX[244] = 'Ethnic Europe';
|
||||
$GenreLookupSCMPX[245] = 'Enka';
|
||||
$GenreLookupSCMPX[246] = 'Children\'s Song';
|
||||
$GenreLookupSCMPX[247] = 'Japanese Sky';
|
||||
$GenreLookupSCMPX[248] = 'Japanese Heavy Rock';
|
||||
$GenreLookupSCMPX[249] = 'Japanese Doom Rock';
|
||||
$GenreLookupSCMPX[250] = 'Japanese J-POP';
|
||||
$GenreLookupSCMPX[251] = 'Japanese Seiyu';
|
||||
$GenreLookupSCMPX[252] = 'Japanese Ambient Techno';
|
||||
$GenreLookupSCMPX[253] = 'Japanese Moemoe';
|
||||
$GenreLookupSCMPX[254] = 'Japanese Tokusatsu';
|
||||
//$GenreLookupSCMPX[255] = 'Japanese Anime';
|
||||
}
|
||||
|
||||
return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup);
|
||||
}
|
||||
|
||||
function LookupGenreName($genreid, $allowSCMPXextended=true) {
|
||||
switch ($genreid) {
|
||||
case 'RX':
|
||||
case 'CR':
|
||||
break;
|
||||
default:
|
||||
$genreid = intval($genreid); // to handle 3 or '3' or '03'
|
||||
break;
|
||||
}
|
||||
$GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended);
|
||||
return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
|
||||
}
|
||||
|
||||
function LookupGenreID($genre, $allowSCMPXextended=false) {
|
||||
$GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended);
|
||||
$LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre));
|
||||
foreach ($GenreLookup as $key => $value) {
|
||||
foreach ($GenreLookup as $key => $value) {
|
||||
if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) {
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
|
||||
}
|
||||
|
||||
function StandardiseID3v1GenreName($OriginalGenre) {
|
||||
if (($GenreID = getid3_id3v1::LookupGenreID($OriginalGenre)) !== false) {
|
||||
return getid3_id3v1::LookupGenreName($GenreID);
|
||||
}
|
||||
return $OriginalGenre;
|
||||
}
|
||||
|
||||
function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') {
|
||||
$ID3v1Tag = 'TAG';
|
||||
$ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
||||
$ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
||||
$ID3v1Tag .= str_pad(trim(substr($album, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
||||
$ID3v1Tag .= str_pad(trim(substr($year, 0, 4)), 4, "\x00", STR_PAD_LEFT);
|
||||
if (!empty($track) && ($track > 0) && ($track <= 255)) {
|
||||
$ID3v1Tag .= str_pad(trim(substr($comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT);
|
||||
$ID3v1Tag .= "\x00";
|
||||
if (gettype($track) == 'string') {
|
||||
$track = (int) $track;
|
||||
}
|
||||
$ID3v1Tag .= chr($track);
|
||||
} else {
|
||||
$ID3v1Tag .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
||||
}
|
||||
if (($genreid < 0) || ($genreid > 147)) {
|
||||
$genreid = 255; // 'unknown' genre
|
||||
}
|
||||
switch (gettype($genreid)) {
|
||||
case 'string':
|
||||
case 'integer':
|
||||
$ID3v1Tag .= chr(intval($genreid));
|
||||
break;
|
||||
default:
|
||||
$ID3v1Tag .= chr(255); // 'unknown' genre
|
||||
break;
|
||||
}
|
||||
|
||||
return $ID3v1Tag;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,282 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// //
|
||||
// module.tag.lyrics3.php //
|
||||
// module for analyzing Lyrics3 tags //
|
||||
// dependencies: module.tag.apetag.php (optional) //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_lyrics3
|
||||
{
|
||||
|
||||
function getid3_lyrics3(&$fd, &$ThisFileInfo) {
|
||||
// http://www.volweb.cz/str/tags.htm
|
||||
|
||||
if ($ThisFileInfo['filesize'] >= pow(2, 31)) {
|
||||
$ThisFileInfo['warning'][] = 'Unable to check for Lyrics3 because file is larger than 2GB';
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek($fd, (0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - LYRICSEND - [Lyrics3size]
|
||||
$lyrics3_id3v1 = fread($fd, 128 + 9 + 6);
|
||||
$lyrics3lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size
|
||||
$lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200
|
||||
$id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1
|
||||
|
||||
if ($lyrics3end == 'LYRICSEND') {
|
||||
// Lyrics3v1, ID3v1, no APE
|
||||
|
||||
$lyrics3size = 5100;
|
||||
$lyrics3offset = $ThisFileInfo['filesize'] - 128 - $lyrics3size;
|
||||
$lyrics3version = 1;
|
||||
|
||||
} elseif ($lyrics3end == 'LYRICS200') {
|
||||
// Lyrics3v2, ID3v1, no APE
|
||||
|
||||
// LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
|
||||
$lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200');
|
||||
$lyrics3offset = $ThisFileInfo['filesize'] - 128 - $lyrics3size;
|
||||
$lyrics3version = 2;
|
||||
|
||||
} elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICSEND')) {
|
||||
// Lyrics3v1, no ID3v1, no APE
|
||||
|
||||
$lyrics3size = 5100;
|
||||
$lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size;
|
||||
$lyrics3version = 1;
|
||||
$lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size;
|
||||
|
||||
} elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICS200')) {
|
||||
|
||||
// Lyrics3v2, no ID3v1, no APE
|
||||
|
||||
$lyrics3size = strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
|
||||
$lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size;
|
||||
$lyrics3version = 2;
|
||||
|
||||
} else {
|
||||
|
||||
if (isset($ThisFileInfo['ape']['tag_offset_start']) && ($ThisFileInfo['ape']['tag_offset_start'] > 15)) {
|
||||
|
||||
fseek($fd, $ThisFileInfo['ape']['tag_offset_start'] - 15, SEEK_SET);
|
||||
$lyrics3lsz = fread($fd, 6);
|
||||
$lyrics3end = fread($fd, 9);
|
||||
|
||||
if ($lyrics3end == 'LYRICSEND') {
|
||||
// Lyrics3v1, APE, maybe ID3v1
|
||||
|
||||
$lyrics3size = 5100;
|
||||
$lyrics3offset = $ThisFileInfo['ape']['tag_offset_start'] - $lyrics3size;
|
||||
$ThisFileInfo['avdataend'] = $lyrics3offset;
|
||||
$lyrics3version = 1;
|
||||
$ThisFileInfo['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability';
|
||||
|
||||
} elseif ($lyrics3end == 'LYRICS200') {
|
||||
// Lyrics3v2, APE, maybe ID3v1
|
||||
|
||||
$lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
|
||||
$lyrics3offset = $ThisFileInfo['ape']['tag_offset_start'] - $lyrics3size;
|
||||
$lyrics3version = 2;
|
||||
$ThisFileInfo['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (isset($lyrics3offset)) {
|
||||
$ThisFileInfo['avdataend'] = $lyrics3offset;
|
||||
$this->getLyrics3Data($ThisFileInfo, $fd, $lyrics3offset, $lyrics3version, $lyrics3size);
|
||||
|
||||
if (!isset($ThisFileInfo['ape'])) {
|
||||
$GETID3_ERRORARRAY = &$ThisFileInfo['warning'];
|
||||
if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, false)) {
|
||||
$tag = new getid3_apetag($fd, $ThisFileInfo, $ThisFileInfo['lyrics3']['tag_offset_start']);
|
||||
unset($tag);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getLyrics3Data(&$ThisFileInfo, &$fd, $endoffset, $version, $length) {
|
||||
// http://www.volweb.cz/str/tags.htm
|
||||
|
||||
if ($endoffset >= pow(2, 31)) {
|
||||
$ThisFileInfo['warning'][] = 'Unable to check for Lyrics3 because file is larger than 2GB';
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek($fd, $endoffset, SEEK_SET);
|
||||
if ($length <= 0) {
|
||||
return false;
|
||||
}
|
||||
$rawdata = fread($fd, $length);
|
||||
|
||||
if (substr($rawdata, 0, 11) != 'LYRICSBEGIN') {
|
||||
if (strpos($rawdata, 'LYRICSBEGIN') !== false) {
|
||||
|
||||
$ThisFileInfo['warning'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but actually found at '.($endoffset + strpos($rawdata, 'LYRICSBEGIN')).' - this is invalid for Lyrics3 v'.$version;
|
||||
$ThisFileInfo['avdataend'] = $endoffset + strpos($rawdata, 'LYRICSBEGIN');
|
||||
$ParsedLyrics3['tag_offset_start'] = $ThisFileInfo['avdataend'];
|
||||
$rawdata = substr($rawdata, strpos($rawdata, 'LYRICSBEGIN'));
|
||||
$length = strlen($rawdata);
|
||||
|
||||
} else {
|
||||
|
||||
$ThisFileInfo['error'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but found "'.substr($rawdata, 0, 11).'" instead';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$ParsedLyrics3['raw']['lyrics3version'] = $version;
|
||||
$ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
|
||||
$ParsedLyrics3['tag_offset_start'] = $endoffset;
|
||||
$ParsedLyrics3['tag_offset_end'] = $endoffset + $length;
|
||||
|
||||
switch ($version) {
|
||||
|
||||
case 1:
|
||||
if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICSEND') {
|
||||
$ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9));
|
||||
$this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
|
||||
} else {
|
||||
$ThisFileInfo['error'][] = '"LYRICSEND" expected at '.(ftell($fd) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead';
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICS200') {
|
||||
$ParsedLyrics3['raw']['unparsed'] = substr($rawdata, 11, strlen($rawdata) - 11 - 9 - 6); // LYRICSBEGIN + LYRICS200 + LSZ
|
||||
$rawdata = $ParsedLyrics3['raw']['unparsed'];
|
||||
while (strlen($rawdata) > 0) {
|
||||
$fieldname = substr($rawdata, 0, 3);
|
||||
$fieldsize = (int) substr($rawdata, 3, 5);
|
||||
$ParsedLyrics3['raw'][$fieldname] = substr($rawdata, 8, $fieldsize);
|
||||
$rawdata = substr($rawdata, 3 + 5 + $fieldsize);
|
||||
}
|
||||
|
||||
if (isset($ParsedLyrics3['raw']['IND'])) {
|
||||
$i = 0;
|
||||
$flagnames = array('lyrics', 'timestamps', 'inhibitrandom');
|
||||
foreach ($flagnames as $flagname) {
|
||||
if (strlen($ParsedLyrics3['raw']['IND']) > $i++) {
|
||||
$ParsedLyrics3['flags'][$flagname] = $this->IntString2Bool(substr($ParsedLyrics3['raw']['IND'], $i, 1 - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$fieldnametranslation = array('ETT'=>'title', 'EAR'=>'artist', 'EAL'=>'album', 'INF'=>'comment', 'AUT'=>'author');
|
||||
foreach ($fieldnametranslation as $key => $value) {
|
||||
if (isset($ParsedLyrics3['raw'][$key])) {
|
||||
$ParsedLyrics3['comments'][$value][] = trim($ParsedLyrics3['raw'][$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($ParsedLyrics3['raw']['IMG'])) {
|
||||
$imagestrings = explode("\r\n", $ParsedLyrics3['raw']['IMG']);
|
||||
foreach ($imagestrings as $key => $imagestring) {
|
||||
if (strpos($imagestring, '||') !== false) {
|
||||
$imagearray = explode('||', $imagestring);
|
||||
$ParsedLyrics3['images'][$key]['filename'] = @$imagearray[0];
|
||||
$ParsedLyrics3['images'][$key]['description'] = @$imagearray[1];
|
||||
$ParsedLyrics3['images'][$key]['timestamp'] = $this->Lyrics3Timestamp2Seconds(@$imagearray[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($ParsedLyrics3['raw']['LYR'])) {
|
||||
$this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
|
||||
}
|
||||
} else {
|
||||
$ThisFileInfo['error'][] = '"LYRICS200" expected at '.(ftell($fd) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead';
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['error'][] = 'Cannot process Lyrics3 version '.$version.' (only v1 and v2)';
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $ParsedLyrics3['tag_offset_end'])) {
|
||||
$ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data';
|
||||
unset($ThisFileInfo['id3v1']);
|
||||
foreach ($ThisFileInfo['warning'] as $key => $value) {
|
||||
if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
|
||||
unset($ThisFileInfo['warning'][$key]);
|
||||
sort($ThisFileInfo['warning']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ThisFileInfo['lyrics3'] = $ParsedLyrics3;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function Lyrics3Timestamp2Seconds($rawtimestamp) {
|
||||
if (ereg('^\\[([0-9]{2}):([0-9]{2})\\]$', $rawtimestamp, $regs)) {
|
||||
return (int) (($regs[1] * 60) + $regs[2]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function Lyrics3LyricsTimestampParse(&$Lyrics3data) {
|
||||
$lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']);
|
||||
foreach ($lyricsarray as $key => $lyricline) {
|
||||
$regs = array();
|
||||
unset($thislinetimestamps);
|
||||
while (ereg('^(\\[[0-9]{2}:[0-9]{2}\\])', $lyricline, $regs)) {
|
||||
$thislinetimestamps[] = $this->Lyrics3Timestamp2Seconds($regs[0]);
|
||||
$lyricline = str_replace($regs[0], '', $lyricline);
|
||||
}
|
||||
$notimestamplyricsarray[$key] = $lyricline;
|
||||
if (isset($thislinetimestamps) && is_array($thislinetimestamps)) {
|
||||
sort($thislinetimestamps);
|
||||
foreach ($thislinetimestamps as $timestampkey => $timestamp) {
|
||||
if (isset($Lyrics3data['synchedlyrics'][$timestamp])) {
|
||||
// timestamps only have a 1-second resolution, it's possible that multiple lines
|
||||
// could have the same timestamp, if so, append
|
||||
$Lyrics3data['synchedlyrics'][$timestamp] .= "\r\n".$lyricline;
|
||||
} else {
|
||||
$Lyrics3data['synchedlyrics'][$timestamp] = $lyricline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$Lyrics3data['unsynchedlyrics'] = implode("\r\n", $notimestamplyricsarray);
|
||||
if (isset($Lyrics3data['synchedlyrics']) && is_array($Lyrics3data['synchedlyrics'])) {
|
||||
ksort($Lyrics3data['synchedlyrics']);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function IntString2Bool($char) {
|
||||
if ($char == '1') {
|
||||
return true;
|
||||
} elseif ($char == '0') {
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,228 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// write.apetag.php //
|
||||
// module for writing APE tags //
|
||||
// dependencies: module.tag.apetag.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
|
||||
|
||||
class getid3_write_apetag
|
||||
{
|
||||
|
||||
var $filename;
|
||||
var $tag_data;
|
||||
var $always_preserve_replaygain = true; // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data
|
||||
var $warnings = array(); // any non-critical errors will be stored here
|
||||
var $errors = array(); // any critical errors will be stored here
|
||||
|
||||
function getid3_write_apetag() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function WriteAPEtag() {
|
||||
// NOTE: All data passed to this function must be UTF-8 format
|
||||
|
||||
$getID3 = new getID3;
|
||||
$ThisFileInfo = $getID3->analyze($this->filename);
|
||||
|
||||
if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
|
||||
if ($ThisFileInfo['ape']['tag_offset_start'] >= $ThisFileInfo['lyrics3']['tag_offset_end']) {
|
||||
// Current APE tag between Lyrics3 and ID3v1/EOF
|
||||
// This break Lyrics3 functionality
|
||||
if (!$this->DeleteAPEtag()) {
|
||||
return false;
|
||||
}
|
||||
$ThisFileInfo = $getID3->analyze($this->filename);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->always_preserve_replaygain) {
|
||||
$ReplayGainTagsToPreserve = array('mp3gain_minmax', 'mp3gain_album_minmax', 'mp3gain_undo', 'replaygain_track_peak', 'replaygain_track_gain', 'replaygain_album_peak', 'replaygain_album_gain');
|
||||
foreach ($ReplayGainTagsToPreserve as $rg_key) {
|
||||
if (isset($ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]) && !isset($this->tag_data[strtoupper($rg_key)][0])) {
|
||||
$this->tag_data[strtoupper($rg_key)][0] = $ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($APEtag = $this->GenerateAPEtag()) {
|
||||
if ($fp = @fopen($this->filename, 'a+b')) {
|
||||
$oldignoreuserabort = ignore_user_abort(true);
|
||||
flock($fp, LOCK_EX);
|
||||
|
||||
$PostAPEdataOffset = $ThisFileInfo['avdataend'];
|
||||
if (isset($ThisFileInfo['ape']['tag_offset_end'])) {
|
||||
$PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['ape']['tag_offset_end']);
|
||||
}
|
||||
if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) {
|
||||
$PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']);
|
||||
}
|
||||
fseek($fp, $PostAPEdataOffset, SEEK_SET);
|
||||
$PostAPEdata = '';
|
||||
if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) {
|
||||
$PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset);
|
||||
}
|
||||
|
||||
fseek($fp, $PostAPEdataOffset, SEEK_SET);
|
||||
if (isset($ThisFileInfo['ape']['tag_offset_start'])) {
|
||||
fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
|
||||
}
|
||||
ftruncate($fp, ftell($fp));
|
||||
fwrite($fp, $APEtag, strlen($APEtag));
|
||||
if (!empty($PostAPEdata)) {
|
||||
fwrite($fp, $PostAPEdata, strlen($PostAPEdata));
|
||||
}
|
||||
flock($fp, LOCK_UN);
|
||||
fclose($fp);
|
||||
ignore_user_abort($oldignoreuserabort);
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function DeleteAPEtag() {
|
||||
$getID3 = new getID3;
|
||||
$ThisFileInfo = $getID3->analyze($this->filename);
|
||||
if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) {
|
||||
if ($fp = @fopen($this->filename, 'a+b')) {
|
||||
|
||||
flock($fp, LOCK_EX);
|
||||
$oldignoreuserabort = ignore_user_abort(true);
|
||||
|
||||
fseek($fp, $ThisFileInfo['ape']['tag_offset_end'], SEEK_SET);
|
||||
$DataAfterAPE = '';
|
||||
if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) {
|
||||
$DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']);
|
||||
}
|
||||
|
||||
ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']);
|
||||
fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
|
||||
|
||||
if (!empty($DataAfterAPE)) {
|
||||
fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE));
|
||||
}
|
||||
|
||||
flock($fp, LOCK_UN);
|
||||
fclose($fp);
|
||||
ignore_user_abort($oldignoreuserabort);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function GenerateAPEtag() {
|
||||
// NOTE: All data passed to this function must be UTF-8 format
|
||||
|
||||
$items = array();
|
||||
if (!is_array($this->tag_data)) {
|
||||
return false;
|
||||
}
|
||||
foreach ($this->tag_data as $key => $arrayofvalues) {
|
||||
if (!is_array($arrayofvalues)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$valuestring = '';
|
||||
foreach ($arrayofvalues as $value) {
|
||||
$valuestring .= str_replace("\x00", '', $value)."\x00";
|
||||
}
|
||||
$valuestring = rtrim($valuestring, "\x00");
|
||||
|
||||
// Length of the assigned value in bytes
|
||||
$tagitem = getid3_lib::LittleEndian2String(strlen($valuestring), 4);
|
||||
|
||||
//$tagitem .= $this->GenerateAPEtagFlags(true, true, false, 0, false);
|
||||
$tagitem .= "\x00\x00\x00\x00";
|
||||
|
||||
$tagitem .= $this->CleanAPEtagItemKey($key)."\x00";
|
||||
$tagitem .= $valuestring;
|
||||
|
||||
$items[] = $tagitem;
|
||||
|
||||
}
|
||||
|
||||
return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false);
|
||||
}
|
||||
|
||||
function GenerateAPEtagHeaderFooter(&$items, $isheader=false) {
|
||||
$tagdatalength = 0;
|
||||
foreach ($items as $itemdata) {
|
||||
$tagdatalength += strlen($itemdata);
|
||||
}
|
||||
|
||||
$APEheader = 'APETAGEX';
|
||||
$APEheader .= getid3_lib::LittleEndian2String(2000, 4);
|
||||
$APEheader .= getid3_lib::LittleEndian2String(32 + $tagdatalength, 4);
|
||||
$APEheader .= getid3_lib::LittleEndian2String(count($items), 4);
|
||||
$APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false);
|
||||
$APEheader .= str_repeat("\x00", 8);
|
||||
|
||||
return $APEheader;
|
||||
}
|
||||
|
||||
function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) {
|
||||
$APEtagFlags = array_fill(0, 4, 0);
|
||||
if ($header) {
|
||||
$APEtagFlags[0] |= 0x80; // Tag contains a header
|
||||
}
|
||||
if (!$footer) {
|
||||
$APEtagFlags[0] |= 0x40; // Tag contains no footer
|
||||
}
|
||||
if ($isheader) {
|
||||
$APEtagFlags[0] |= 0x20; // This is the header, not the footer
|
||||
}
|
||||
|
||||
// 0: Item contains text information coded in UTF-8
|
||||
// 1: Item contains binary information °)
|
||||
// 2: Item is a locator of external stored information °°)
|
||||
// 3: reserved
|
||||
$APEtagFlags[3] |= ($encodingid << 1);
|
||||
|
||||
if ($readonly) {
|
||||
$APEtagFlags[3] |= 0x01; // Tag or Item is Read Only
|
||||
}
|
||||
|
||||
return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]);
|
||||
}
|
||||
|
||||
function CleanAPEtagItemKey($itemkey) {
|
||||
$itemkey = eregi_replace("[^\x20-\x7E]", '', $itemkey);
|
||||
|
||||
// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
|
||||
switch (strtoupper($itemkey)) {
|
||||
case 'EAN/UPC':
|
||||
case 'ISBN':
|
||||
case 'LC':
|
||||
case 'ISRC':
|
||||
$itemkey = strtoupper($itemkey);
|
||||
break;
|
||||
|
||||
default:
|
||||
$itemkey = ucwords($itemkey);
|
||||
break;
|
||||
}
|
||||
return $itemkey;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// write.id3v1.php //
|
||||
// module for writing ID3v1 tags //
|
||||
// dependencies: module.tag.id3v1.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
|
||||
|
||||
class getid3_write_id3v1
|
||||
{
|
||||
var $filename;
|
||||
var $tag_data;
|
||||
var $warnings = array(); // any non-critical errors will be stored here
|
||||
var $errors = array(); // any critical errors will be stored here
|
||||
|
||||
function getid3_write_id3v1() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function WriteID3v1() {
|
||||
if ((filesize($this->filename) >= (pow(2, 31) - 128)) || (filesize($this->filename) < 0)) {
|
||||
$this->errors[] = 'Unable to write ID3v1 because file is larger than 2GB';
|
||||
return false;
|
||||
}
|
||||
|
||||
// File MUST be writeable - CHMOD(646) at least
|
||||
if (is_writeable($this->filename)) {
|
||||
if ($fp_source = @fopen($this->filename, 'r+b')) {
|
||||
|
||||
fseek($fp_source, -128, SEEK_END);
|
||||
if (fread($fp_source, 3) == 'TAG') {
|
||||
fseek($fp_source, -128, SEEK_END); // overwrite existing ID3v1 tag
|
||||
} else {
|
||||
fseek($fp_source, 0, SEEK_END); // append new ID3v1 tag
|
||||
}
|
||||
$this->tag_data['track'] = (isset($this->tag_data['track']) ? $this->tag_data['track'] : (isset($this->tag_data['track_number']) ? $this->tag_data['track_number'] : (isset($this->tag_data['tracknumber']) ? $this->tag_data['tracknumber'] : '')));
|
||||
|
||||
$new_id3v1_tag_data = getid3_id3v1::GenerateID3v1Tag(
|
||||
@$this->tag_data['title'],
|
||||
@$this->tag_data['artist'],
|
||||
@$this->tag_data['album'],
|
||||
@$this->tag_data['year'],
|
||||
@$this->tag_data['genreid'],
|
||||
@$this->tag_data['comment'],
|
||||
@$this->tag_data['track']);
|
||||
fwrite($fp_source, $new_id3v1_tag_data, 128);
|
||||
fclose($fp_source);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$this->errors[] = 'File is not writeable: '.$this->filename;
|
||||
return false;
|
||||
}
|
||||
|
||||
function FixID3v1Padding() {
|
||||
// ID3v1 data is supposed to be padded with NULL characters, but some taggers incorrectly use spaces
|
||||
// This function rewrites the ID3v1 tag with correct padding
|
||||
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
$ThisFileInfo = $getID3->analyze($this->filename);
|
||||
if ($ThisFileInfo['filesize'] >= (pow(2, 31) - 128)) {
|
||||
// cannot write tags on files > 2GB
|
||||
return false;
|
||||
}
|
||||
if (isset($ThisFileInfo['tags']['id3v1'])) {
|
||||
foreach ($ThisFileInfo['tags']['id3v1'] as $key => $value) {
|
||||
$id3v1data[$key] = implode(',', $value);
|
||||
}
|
||||
$this->tag_data = $id3v1data;
|
||||
return $this->WriteID3v1();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function RemoveID3v1() {
|
||||
if ($ThisFileInfo['filesize'] >= pow(2, 31)) {
|
||||
$this->errors[] = 'Unable to write ID3v1 because file is larger than 2GB';
|
||||
return false;
|
||||
}
|
||||
|
||||
// File MUST be writeable - CHMOD(646) at least
|
||||
if (is_writeable($this->filename)) {
|
||||
if ($fp_source = @fopen($this->filename, 'r+b')) {
|
||||
|
||||
fseek($fp_source, -128, SEEK_END);
|
||||
if (fread($fp_source, 3) == 'TAG') {
|
||||
ftruncate($fp_source, filesize($this->filename) - 128);
|
||||
} else {
|
||||
// no ID3v1 tag to begin with - do nothing
|
||||
}
|
||||
fclose($fp_source);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = $this->filename.' is not writeable';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// write.lyrics3.php //
|
||||
// module for writing Lyrics3 tags //
|
||||
// dependencies: module.tag.lyrics3.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_write_lyrics3
|
||||
{
|
||||
var $filename;
|
||||
var $tag_data;
|
||||
//var $lyrics3_version = 2; // 1 or 2
|
||||
var $warnings = array(); // any non-critical errors will be stored here
|
||||
var $errors = array(); // any critical errors will be stored here
|
||||
|
||||
function getid3_write_lyrics3() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function WriteLyrics3() {
|
||||
$this->errors[] = 'WriteLyrics3() not yet functional - cannot write Lyrics3';
|
||||
return false;
|
||||
}
|
||||
|
||||
function DeleteLyrics3() {
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
$ThisFileInfo = $getID3->analyze($this->filename);
|
||||
if (isset($ThisFileInfo['lyrics3']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
|
||||
if ($fp = @fopen($this->filename, 'a+b')) {
|
||||
|
||||
flock($fp, LOCK_EX);
|
||||
$oldignoreuserabort = ignore_user_abort(true);
|
||||
|
||||
fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_end'], SEEK_SET);
|
||||
$DataAfterLyrics3 = '';
|
||||
if ($ThisFileInfo['filesize'] > $ThisFileInfo['lyrics3']['tag_offset_end']) {
|
||||
$DataAfterLyrics3 = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['lyrics3']['tag_offset_end']);
|
||||
}
|
||||
|
||||
ftruncate($fp, $ThisFileInfo['lyrics3']['tag_offset_start']);
|
||||
|
||||
if (!empty($DataAfterLyrics3)) {
|
||||
fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_start'], SEEK_SET);
|
||||
fwrite($fp, $DataAfterLyrics3, strlen($DataAfterLyrics3));
|
||||
}
|
||||
|
||||
flock($fp, LOCK_UN);
|
||||
fclose($fp);
|
||||
ignore_user_abort($oldignoreuserabort);
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
$this->errors[] = 'Cannot open "'.$this->filename.'" in "a+b" mode';
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
// no Lyrics3 present
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// write.metaflac.php //
|
||||
// module for writing metaflac tags //
|
||||
// dependencies: /helperapps/metaflac.exe //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_write_metaflac
|
||||
{
|
||||
|
||||
var $filename;
|
||||
var $tag_data;
|
||||
var $warnings = array(); // any non-critical errors will be stored here
|
||||
var $errors = array(); // any critical errors will be stored here
|
||||
|
||||
function getid3_write_metaflac() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function WriteMetaFLAC() {
|
||||
|
||||
if (!ini_get('safe_mode')) {
|
||||
|
||||
// Create file with new comments
|
||||
$tempcommentsfilename = tempnam('*', 'getID3');
|
||||
if ($fpcomments = @fopen($tempcommentsfilename, 'wb')) {
|
||||
|
||||
foreach ($this->tag_data as $key => $value) {
|
||||
foreach ($value as $commentdata) {
|
||||
fwrite($fpcomments, $this->CleanmetaflacName($key).'='.$commentdata."\n");
|
||||
}
|
||||
}
|
||||
fclose($fpcomments);
|
||||
|
||||
} else {
|
||||
|
||||
$this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
$oldignoreuserabort = ignore_user_abort(true);
|
||||
if (GETID3_OS_ISWINDOWS) {
|
||||
|
||||
if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) {
|
||||
//$commandline = '"'.GETID3_HELPERAPPSDIR.'metaflac.exe" --no-utf8-convert --remove-all-tags --import-tags-from="'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"';
|
||||
// metaflac works fine if you copy-paste the above commandline into a command prompt,
|
||||
// but refuses to work with `backtick` if there are "doublequotes" present around BOTH
|
||||
// the metaflac pathname and the target filename. For whatever reason...??
|
||||
// The solution is simply ensure that the metaflac pathname has no spaces,
|
||||
// and therefore does not need to be quoted
|
||||
|
||||
// On top of that, if error messages are not always captured properly under Windows
|
||||
// To at least see if there was a problem, compare file modification timestamps before and after writing
|
||||
clearstatcache();
|
||||
$timestampbeforewriting = filemtime($this->filename);
|
||||
|
||||
$commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --no-utf8-convert --remove-all-tags --import-tags-from="'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1';
|
||||
$metaflacError = `$commandline`;
|
||||
|
||||
if (empty($metaflacError)) {
|
||||
clearstatcache();
|
||||
if ($timestampbeforewriting == filemtime($this->filename)) {
|
||||
$metaflacError = 'File modification timestamp has not changed - it looks like the tags were not written';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$metaflacError = 'metaflac.exe not found in '.GETID3_HELPERAPPSDIR;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// It's simpler on *nix
|
||||
$commandline = 'metaflac --no-utf8-convert --remove-all-tags --import-tags-from='.$tempcommentsfilename.' "'.$this->filename.'" 2>&1';
|
||||
$metaflacError = `$commandline`;
|
||||
|
||||
}
|
||||
|
||||
// Remove temporary comments file
|
||||
unlink($tempcommentsfilename);
|
||||
ignore_user_abort($oldignoreuserabort);
|
||||
|
||||
if (!empty($metaflacError)) {
|
||||
|
||||
$this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not written';
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function DeleteMetaFLAC() {
|
||||
|
||||
if (!ini_get('safe_mode')) {
|
||||
|
||||
$oldignoreuserabort = ignore_user_abort(true);
|
||||
if (GETID3_OS_ISWINDOWS) {
|
||||
|
||||
if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) {
|
||||
// To at least see if there was a problem, compare file modification timestamps before and after writing
|
||||
clearstatcache();
|
||||
$timestampbeforewriting = filemtime($this->filename);
|
||||
|
||||
$commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --remove-all-tags "'.$this->filename.'" 2>&1';
|
||||
$metaflacError = `$commandline`;
|
||||
|
||||
if (empty($metaflacError)) {
|
||||
clearstatcache();
|
||||
if ($timestampbeforewriting == filemtime($this->filename)) {
|
||||
$metaflacError = 'File modification timestamp has not changed - it looks like the tags were not deleted';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$metaflacError = 'metaflac.exe not found in '.GETID3_HELPERAPPSDIR;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// It's simpler on *nix
|
||||
$commandline = 'metaflac --remove-all-tags "'.$this->filename.'" 2>&1';
|
||||
$metaflacError = `$commandline`;
|
||||
|
||||
}
|
||||
|
||||
ignore_user_abort($oldignoreuserabort);
|
||||
|
||||
if (!empty($metaflacError)) {
|
||||
$this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not deleted';
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function CleanmetaflacName($originalcommentname) {
|
||||
// A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded.
|
||||
// ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through
|
||||
// 0x7A inclusive (a-z).
|
||||
|
||||
// replace invalid chars with a space, return uppercase text
|
||||
// Thanks Chris Bolt <chris-getid3Øbolt*cx> for improving this function
|
||||
// note: ereg_replace() replaces nulls with empty string (not space)
|
||||
return strtoupper(ereg_replace('[^ -<>-}]', ' ', str_replace("\x00", ' ', $originalcommentname)));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,592 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// //
|
||||
// write.php //
|
||||
// module for writing tags (APEv2, ID3v1, ID3v2) //
|
||||
// dependencies: getid3.lib.php //
|
||||
// write.apetag.php (optional) //
|
||||
// write.id3v1.php (optional) //
|
||||
// write.id3v2.php (optional) //
|
||||
// write.vorbiscomment.php (optional) //
|
||||
// write.metaflac.php (optional) //
|
||||
// write.lyrics3.php (optional) //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
if (!defined('GETID3_INCLUDEPATH')) {
|
||||
die('getid3.php MUST be included before calling getid3_writetags');
|
||||
}
|
||||
if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
|
||||
die('write.php depends on getid3.lib.php, which is missing.');
|
||||
}
|
||||
|
||||
|
||||
// NOTES:
|
||||
//
|
||||
// You should pass data here with standard field names as follows:
|
||||
// * TITLE
|
||||
// * ARTIST
|
||||
// * ALBUM
|
||||
// * TRACKNUMBER
|
||||
// * COMMENT
|
||||
// * GENRE
|
||||
// * YEAR
|
||||
// * ATTACHED_PICTURE (ID3v2 only)
|
||||
//
|
||||
// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
|
||||
// The APEv2 Tag Items Keys definition says "TRACK" is correct but foobar2000 uses "TRACKNUMBER" instead
|
||||
// Pass data here as "TRACKNUMBER" for compatability with all formats
|
||||
|
||||
|
||||
class getid3_writetags
|
||||
{
|
||||
// public
|
||||
var $filename; // absolute filename of file to write tags to
|
||||
var $tagformats = array(); // array of tag formats to write ('id3v1', 'id3v2.2', 'id2v2.3', 'id3v2.4', 'ape', 'vorbiscomment', 'metaflac', 'real')
|
||||
var $tag_data = array(array()); // 2-dimensional array of tag data (ex: $data['ARTIST'][0] = 'Elvis')
|
||||
var $tag_encoding = 'ISO-8859-1'; // text encoding used for tag data ('ISO-8859-1', 'UTF-8', 'UTF-16', 'UTF-16LE', 'UTF-16BE', )
|
||||
var $overwrite_tags = true; // if true will erase existing tag data and write only passed data; if false will merge passed data with existing tag data
|
||||
var $remove_other_tags = false; // if true will erase remove all existing tags and only write those passed in $tagformats; if false will ignore any tags not mentioned in $tagformats
|
||||
|
||||
var $id3v2_tag_language = 'eng'; // ISO-639-2 3-character language code needed for some ID3v2 frames (http://www.id3.org/iso639-2.html)
|
||||
var $id3v2_paddedlength = 4096; // minimum length of ID3v2 tags (will be padded to this length if tag data is shorter)
|
||||
|
||||
var $warnings = array(); // any non-critical errors will be stored here
|
||||
var $errors = array(); // any critical errors will be stored here
|
||||
|
||||
// private
|
||||
var $ThisFileInfo; // analysis of file before writing
|
||||
|
||||
function getid3_writetags() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function WriteTags() {
|
||||
|
||||
if (empty($this->filename)) {
|
||||
$this->errors[] = 'filename is undefined in getid3_writetags';
|
||||
return false;
|
||||
} elseif (!file_exists($this->filename)) {
|
||||
$this->errors[] = 'filename set to non-existant file "'.$this->filename.'" in getid3_writetags';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_array($this->tagformats)) {
|
||||
$this->errors[] = 'tagformats must be an array in getid3_writetags';
|
||||
return false;
|
||||
}
|
||||
|
||||
$TagFormatsToRemove = array();
|
||||
if (filesize($this->filename) == 0) {
|
||||
|
||||
// empty file special case - allow any tag format, don't check existing format
|
||||
// could be useful if you want to generate tag data for a non-existant file
|
||||
$this->ThisFileInfo = array('fileformat'=>'');
|
||||
$AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3');
|
||||
|
||||
} else {
|
||||
|
||||
$getID3 = new getID3;
|
||||
$getID3->encoding = $this->tag_encoding;
|
||||
$this->ThisFileInfo = $getID3->analyze($this->filename);
|
||||
|
||||
// check for what file types are allowed on this fileformat
|
||||
switch (@$this->ThisFileInfo['fileformat']) {
|
||||
case 'mp3':
|
||||
case 'mp2':
|
||||
case 'mp1':
|
||||
case 'riff': // maybe not officially, but people do it anyway
|
||||
$AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3');
|
||||
break;
|
||||
|
||||
case 'mpc':
|
||||
$AllowedTagFormats = array('ape');
|
||||
break;
|
||||
|
||||
case 'flac':
|
||||
$AllowedTagFormats = array('metaflac');
|
||||
break;
|
||||
|
||||
case 'real':
|
||||
$AllowedTagFormats = array('real');
|
||||
break;
|
||||
|
||||
case 'ogg':
|
||||
switch (@$this->ThisFileInfo['audio']['dataformat']) {
|
||||
case 'flac':
|
||||
//$AllowedTagFormats = array('metaflac');
|
||||
$this->errors[] = 'metaflac is not (yet) compatible with OggFLAC files';
|
||||
return false;
|
||||
break;
|
||||
case 'vorbis':
|
||||
$AllowedTagFormats = array('vorbiscomment');
|
||||
break;
|
||||
default:
|
||||
$this->errors[] = 'metaflac is not (yet) compatible with Ogg files other than OggVorbis';
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$AllowedTagFormats = array();
|
||||
break;
|
||||
}
|
||||
foreach ($this->tagformats as $requested_tag_format) {
|
||||
if (!in_array($requested_tag_format, $AllowedTagFormats)) {
|
||||
$errormessage = 'Tag format "'.$requested_tag_format.'" is not allowed on "'.@$this->ThisFileInfo['fileformat'];
|
||||
if (@$this->ThisFileInfo['fileformat'] != @$this->ThisFileInfo['audio']['dataformat']) {
|
||||
$errormessage .= '.'.@$this->ThisFileInfo['audio']['dataformat'];
|
||||
}
|
||||
$errormessage .= '" files';
|
||||
$this->errors[] = $errormessage;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// List of other tag formats, removed if requested
|
||||
if ($this->remove_other_tags) {
|
||||
foreach ($AllowedTagFormats as $AllowedTagFormat) {
|
||||
switch ($AllowedTagFormat) {
|
||||
case 'id3v2.2':
|
||||
case 'id3v2.3':
|
||||
case 'id3v2.4':
|
||||
if (!in_array('id3v2', $TagFormatsToRemove) && !in_array('id3v2.2', $this->tagformats) && !in_array('id3v2.3', $this->tagformats) && !in_array('id3v2.4', $this->tagformats)) {
|
||||
$TagFormatsToRemove[] = 'id3v2';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!in_array($AllowedTagFormat, $this->tagformats)) {
|
||||
$TagFormatsToRemove[] = $AllowedTagFormat;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$WritingFilesToInclude = array_merge($this->tagformats, $TagFormatsToRemove);
|
||||
|
||||
// Check for required include files and include them
|
||||
foreach ($WritingFilesToInclude as $tagformat) {
|
||||
switch ($tagformat) {
|
||||
case 'ape':
|
||||
$GETID3_ERRORARRAY = &$this->errors;
|
||||
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.apetag.php', __FILE__, false)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'id3v1':
|
||||
case 'lyrics3':
|
||||
case 'vorbiscomment':
|
||||
case 'metaflac':
|
||||
case 'real':
|
||||
$GETID3_ERRORARRAY = &$this->errors;
|
||||
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.'.$tagformat.'.php', __FILE__, false)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'id3v2.2':
|
||||
case 'id3v2.3':
|
||||
case 'id3v2.4':
|
||||
case 'id3v2':
|
||||
$GETID3_ERRORARRAY = &$this->errors;
|
||||
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.id3v2.php', __FILE__, false)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->errors[] = 'unknown tag format "'.$tagformat.'" in $tagformats in WriteTags()';
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Validation of supplied data
|
||||
if (!is_array($this->tag_data)) {
|
||||
$this->errors[] = '$tag_data is not an array in WriteTags()';
|
||||
return false;
|
||||
}
|
||||
// convert supplied data array keys to upper case, if they're not already
|
||||
foreach ($this->tag_data as $tag_key => $tag_array) {
|
||||
if (strtoupper($tag_key) !== $tag_key) {
|
||||
$this->tag_data[strtoupper($tag_key)] = $this->tag_data[$tag_key];
|
||||
unset($this->tag_data[$tag_key]);
|
||||
}
|
||||
}
|
||||
// convert source data array keys to upper case, if they're not already
|
||||
if (!empty($this->ThisFileInfo['tags'])) {
|
||||
foreach ($this->ThisFileInfo['tags'] as $tag_format => $tag_data_array) {
|
||||
foreach ($tag_data_array as $tag_key => $tag_array) {
|
||||
if (strtoupper($tag_key) !== $tag_key) {
|
||||
$this->ThisFileInfo['tags'][$tag_format][strtoupper($tag_key)] = $this->ThisFileInfo['tags'][$tag_format][$tag_key];
|
||||
unset($this->ThisFileInfo['tags'][$tag_format][$tag_key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert "TRACK" to "TRACKNUMBER" (if needed) for compatability with all formats
|
||||
if (isset($this->tag_data['TRACK']) && !isset($this->tag_data['TRACKNUMBER'])) {
|
||||
$this->tag_data['TRACKNUMBER'] = $this->tag_data['TRACK'];
|
||||
unset($this->tag_data['TRACK']);
|
||||
}
|
||||
|
||||
// Remove all other tag formats, if requested
|
||||
if ($this->remove_other_tags) {
|
||||
$this->DeleteTags($TagFormatsToRemove);
|
||||
}
|
||||
|
||||
// Write data for each tag format
|
||||
foreach ($this->tagformats as $tagformat) {
|
||||
$success = false; // overridden if tag writing is successful
|
||||
switch ($tagformat) {
|
||||
case 'ape':
|
||||
$ape_writer = new getid3_write_apetag;
|
||||
if (($ape_writer->tag_data = $this->FormatDataForAPE()) !== false) {
|
||||
$ape_writer->filename = $this->filename;
|
||||
if (($success = $ape_writer->WriteAPEtag()) === false) {
|
||||
$this->errors[] = 'WriteAPEtag() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $ape_writer->errors)).'</LI></UL></PRE>';
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = 'FormatDataForAPE() failed';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'id3v1':
|
||||
$id3v1_writer = new getid3_write_id3v1;
|
||||
if (($id3v1_writer->tag_data = $this->FormatDataForID3v1()) !== false) {
|
||||
$id3v1_writer->filename = $this->filename;
|
||||
if (($success = $id3v1_writer->WriteID3v1()) === false) {
|
||||
$this->errors[] = 'WriteID3v1() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v1_writer->errors)).'</LI></UL></PRE>';
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = 'FormatDataForID3v1() failed';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'id3v2.2':
|
||||
case 'id3v2.3':
|
||||
case 'id3v2.4':
|
||||
$id3v2_writer = new getid3_write_id3v2;
|
||||
$id3v2_writer->majorversion = intval(substr($tagformat, -1));
|
||||
$id3v2_writer->paddedlength = $this->id3v2_paddedlength;
|
||||
if (($id3v2_writer->tag_data = $this->FormatDataForID3v2($id3v2_writer->majorversion)) !== false) {
|
||||
$id3v2_writer->filename = $this->filename;
|
||||
if (($success = $id3v2_writer->WriteID3v2()) === false) {
|
||||
$this->errors[] = 'WriteID3v2() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v2_writer->errors)).'</LI></UL></PRE>';
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = 'FormatDataForID3v2() failed';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'vorbiscomment':
|
||||
$vorbiscomment_writer = new getid3_write_vorbiscomment;
|
||||
if (($vorbiscomment_writer->tag_data = $this->FormatDataForVorbisComment()) !== false) {
|
||||
$vorbiscomment_writer->filename = $this->filename;
|
||||
if (($success = $vorbiscomment_writer->WriteVorbisComment()) === false) {
|
||||
$this->errors[] = 'WriteVorbisComment() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $vorbiscomment_writer->errors)).'</LI></UL></PRE>';
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = 'FormatDataForVorbisComment() failed';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'metaflac':
|
||||
$metaflac_writer = new getid3_write_metaflac;
|
||||
if (($metaflac_writer->tag_data = $this->FormatDataForMetaFLAC()) !== false) {
|
||||
$metaflac_writer->filename = $this->filename;
|
||||
if (($success = $metaflac_writer->WriteMetaFLAC()) === false) {
|
||||
$this->errors[] = 'WriteMetaFLAC() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $metaflac_writer->errors)).'</LI></UL></PRE>';
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = 'FormatDataForMetaFLAC() failed';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'real':
|
||||
$real_writer = new getid3_write_real;
|
||||
if (($real_writer->tag_data = $this->FormatDataForReal()) !== false) {
|
||||
$real_writer->filename = $this->filename;
|
||||
if (($success = $real_writer->WriteReal()) === false) {
|
||||
$this->errors[] = 'WriteReal() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $real_writer->errors)).'</LI></UL></PRE>';
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = 'FormatDataForReal() failed';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->errors[] = 'Invalid tag format to write: "'.$tagformat.'"';
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
if (!$success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function DeleteTags($TagFormatsToDelete) {
|
||||
foreach ($TagFormatsToDelete as $DeleteTagFormat) {
|
||||
$success = false; // overridden if tag deletion is successful
|
||||
switch ($DeleteTagFormat) {
|
||||
case 'id3v1':
|
||||
$id3v1_writer = new getid3_write_id3v1;
|
||||
$id3v1_writer->filename = $this->filename;
|
||||
if (($success = $id3v1_writer->RemoveID3v1()) === false) {
|
||||
$this->errors[] = 'RemoveID3v1() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v1_writer->errors)).'</LI></UL></PRE>';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'id3v2':
|
||||
$id3v2_writer = new getid3_write_id3v2;
|
||||
$id3v2_writer->filename = $this->filename;
|
||||
if (($success = $id3v2_writer->RemoveID3v2()) === false) {
|
||||
$this->errors[] = 'RemoveID3v2() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v2_writer->errors)).'</LI></UL></PRE>';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ape':
|
||||
$ape_writer = new getid3_write_apetag;
|
||||
$ape_writer->filename = $this->filename;
|
||||
if (($success = $ape_writer->DeleteAPEtag()) === false) {
|
||||
$this->errors[] = 'DeleteAPEtag() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $ape_writer->errors)).'</LI></UL></PRE>';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'vorbiscomment':
|
||||
$vorbiscomment_writer = new getid3_write_vorbiscomment;
|
||||
$vorbiscomment_writer->filename = $this->filename;
|
||||
if (($success = $vorbiscomment_writer->DeleteVorbisComment()) === false) {
|
||||
$this->errors[] = 'DeleteVorbisComment() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $vorbiscomment_writer->errors)).'</LI></UL></PRE>';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'metaflac':
|
||||
$metaflac_writer = new getid3_write_metaflac;
|
||||
$metaflac_writer->filename = $this->filename;
|
||||
if (($success = $metaflac_writer->DeleteMetaFLAC()) === false) {
|
||||
$this->errors[] = 'DeleteMetaFLAC() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $metaflac_writer->errors)).'</LI></UL></PRE>';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'lyrics3':
|
||||
$lyrics3_writer = new getid3_write_lyrics3;
|
||||
$lyrics3_writer->filename = $this->filename;
|
||||
if (($success = $lyrics3_writer->DeleteLyrics3()) === false) {
|
||||
$this->errors[] = 'DeleteLyrics3() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $lyrics3_writer->errors)).'</LI></UL></PRE>';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'real':
|
||||
$real_writer = new getid3_write_real;
|
||||
$real_writer->filename = $this->filename;
|
||||
if (($success = $real_writer->RemoveReal()) === false) {
|
||||
$this->errors[] = 'RemoveReal() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $real_writer->errors)).'</LI></UL></PRE>';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->errors[] = 'Invalid tag format to delete: "'.$tagformat.'"';
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
if (!$success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function MergeExistingTagData($TagFormat, &$tag_data) {
|
||||
// Merge supplied data with existing data, if requested
|
||||
if ($this->overwrite_tags) {
|
||||
// do nothing - ignore previous data
|
||||
} else {
|
||||
if (!isset($this->ThisFileInfo['tags'][$TagFormat])) {
|
||||
return false;
|
||||
}
|
||||
$tag_data = array_merge_recursive($tag_data, $this->ThisFileInfo['tags'][$TagFormat]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function FormatDataForAPE() {
|
||||
$ape_tag_data = array();
|
||||
foreach ($this->tag_data as $tag_key => $valuearray) {
|
||||
switch ($tag_key) {
|
||||
case 'ATTACHED_PICTURE':
|
||||
// ATTACHED_PICTURE is ID3v2 only - ignore
|
||||
$this->warnings[] = '$data['.$tag_key.'] is assumed to be ID3v2 APIC data - NOT written to APE tag';
|
||||
break;
|
||||
|
||||
default:
|
||||
foreach ($valuearray as $key => $value) {
|
||||
if (is_string($value) || is_numeric($value)) {
|
||||
$ape_tag_data[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value);
|
||||
} else {
|
||||
$this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to APE tag';
|
||||
unset($ape_tag_data[$tag_key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->MergeExistingTagData('ape', $ape_tag_data);
|
||||
return $ape_tag_data;
|
||||
}
|
||||
|
||||
|
||||
function FormatDataForID3v1() {
|
||||
$tag_data_id3v1['genreid'] = 255;
|
||||
if (!empty($this->tag_data['GENRE'])) {
|
||||
foreach ($this->tag_data['GENRE'] as $key => $value) {
|
||||
if (getid3_id3v1::LookupGenreID($value) !== false) {
|
||||
$tag_data_id3v1['genreid'] = getid3_id3v1::LookupGenreID($value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tag_data_id3v1['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TITLE']));
|
||||
$tag_data_id3v1['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ARTIST']));
|
||||
$tag_data_id3v1['album'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ALBUM']));
|
||||
$tag_data_id3v1['year'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['YEAR']));
|
||||
$tag_data_id3v1['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COMMENT']));
|
||||
|
||||
$tag_data_id3v1['track'] = intval(getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TRACKNUMBER'])));
|
||||
if ($tag_data_id3v1['track'] <= 0) {
|
||||
$tag_data_id3v1['track'] = '';
|
||||
}
|
||||
|
||||
$this->MergeExistingTagData('id3v1', $tag_data_id3v1);
|
||||
return $tag_data_id3v1;
|
||||
}
|
||||
|
||||
function FormatDataForID3v2($id3v2_majorversion) {
|
||||
$tag_data_id3v2 = array();
|
||||
|
||||
$ID3v2_text_encoding_lookup[2] = array('ISO-8859-1'=>0, 'UTF-16'=>1);
|
||||
$ID3v2_text_encoding_lookup[3] = array('ISO-8859-1'=>0, 'UTF-16'=>1);
|
||||
$ID3v2_text_encoding_lookup[4] = array('ISO-8859-1'=>0, 'UTF-16'=>1, 'UTF-16BE'=>2, 'UTF-8'=>3);
|
||||
foreach ($this->tag_data as $tag_key => $valuearray) {
|
||||
$ID3v2_framename = getid3_write_id3v2::ID3v2ShortFrameNameLookup($id3v2_majorversion, $tag_key);
|
||||
switch ($ID3v2_framename) {
|
||||
case 'APIC':
|
||||
foreach ($valuearray as $key => $apic_data_array) {
|
||||
if (isset($apic_data_array['data']) &&
|
||||
isset($apic_data_array['picturetypeid']) &&
|
||||
isset($apic_data_array['description']) &&
|
||||
isset($apic_data_array['mime'])) {
|
||||
$tag_data_id3v2['APIC'][] = $apic_data_array;
|
||||
} else {
|
||||
$this->errors[] = 'ID3v2 APIC data is not properly structured';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '':
|
||||
$this->errors[] = 'ID3v2: Skipping "'.$tag_key.'" because cannot match it to a known ID3v2 frame type';
|
||||
// some other data type, don't know how to handle it, ignore it
|
||||
break;
|
||||
|
||||
default:
|
||||
// most other (text) frames can be copied over as-is
|
||||
foreach ($valuearray as $key => $value) {
|
||||
if (isset($ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding])) {
|
||||
// source encoding is valid in ID3v2 - use it with no conversion
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = $ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding];
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['data'] = $value;
|
||||
} else {
|
||||
// source encoding is NOT valid in ID3v2 - convert it to an ID3v2-valid encoding first
|
||||
if ($id3v2_majorversion < 4) {
|
||||
// convert data from other encoding to UTF-16
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 1;
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['data'] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-16', $value);
|
||||
|
||||
} else {
|
||||
// convert data from other encoding to UTF-8
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 3;
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['data'] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value);
|
||||
}
|
||||
}
|
||||
|
||||
// These values are not needed for all frame types, but if they're not used no matter
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['description'] = '';
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['language'] = $this->id3v2_tag_language;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->MergeExistingTagData('id3v2', $tag_data_id3v2);
|
||||
return $tag_data_id3v2;
|
||||
}
|
||||
|
||||
function FormatDataForVorbisComment() {
|
||||
$tag_data_vorbiscomment = $this->tag_data;
|
||||
|
||||
// check for multi-line comment values - split out to multiple comments if neccesary
|
||||
// and convert data to UTF-8 strings
|
||||
foreach ($tag_data_vorbiscomment as $tag_key => $valuearray) {
|
||||
foreach ($valuearray as $key => $value) {
|
||||
str_replace("\r", "\n", $value);
|
||||
if (strstr($value, "\n")) {
|
||||
unset($tag_data_vorbiscomment[$tag_key][$key]);
|
||||
$multilineexploded = explode("\n", $value);
|
||||
foreach ($multilineexploded as $newcomment) {
|
||||
if (strlen(trim($newcomment)) > 0) {
|
||||
$tag_data_vorbiscomment[$tag_key][] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $newcomment);
|
||||
}
|
||||
}
|
||||
} elseif (is_string($value) || is_numeric($value)) {
|
||||
$tag_data_vorbiscomment[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value);
|
||||
} else {
|
||||
$this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to VorbisComment tag';
|
||||
unset($tag_data_vorbiscomment[$tag_key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->MergeExistingTagData('vorbiscomment', $tag_data_vorbiscomment);
|
||||
return $tag_data_vorbiscomment;
|
||||
}
|
||||
|
||||
function FormatDataForMetaFLAC() {
|
||||
// FLAC & OggFLAC use VorbisComments same as OggVorbis
|
||||
// but require metaflac to do the writing rather than vorbiscomment
|
||||
return $this->FormatDataForVorbisComment();
|
||||
}
|
||||
|
||||
function FormatDataForReal() {
|
||||
$tag_data_real['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TITLE']));
|
||||
$tag_data_real['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ARTIST']));
|
||||
$tag_data_real['copyright'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COPYRIGHT']));
|
||||
$tag_data_real['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COMMENT']));
|
||||
|
||||
$this->MergeExistingTagData('real', $tag_data_real);
|
||||
return $tag_data_real;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,295 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// write.real.php //
|
||||
// module for writing RealAudio/RealVideo tags //
|
||||
// dependencies: module.tag.real.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
class getid3_write_real
|
||||
{
|
||||
var $filename;
|
||||
var $tag_data = array();
|
||||
var $warnings = array(); // any non-critical errors will be stored here
|
||||
var $errors = array(); // any critical errors will be stored here
|
||||
var $paddedlength = 512; // minimum length of CONT tag in bytes
|
||||
|
||||
function getid3_write_real() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function WriteReal() {
|
||||
// File MUST be writeable - CHMOD(646) at least
|
||||
if (is_writeable($this->filename)) {
|
||||
if ($fp_source = @fopen($this->filename, 'r+b')) {
|
||||
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
$OldThisFileInfo = $getID3->analyze($this->filename);
|
||||
if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
|
||||
$this->errors[] = 'Cannot write Real tags on old-style file format';
|
||||
fclose($fp_source);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($OldThisFileInfo['real']['chunks'])) {
|
||||
$this->errors[] = 'Cannot write Real tags because cannot find DATA chunk in file';
|
||||
fclose($fp_source);
|
||||
return false;
|
||||
}
|
||||
foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
|
||||
$oldChunkInfo[$chunkarray['name']] = $chunkarray;
|
||||
}
|
||||
if (!empty($oldChunkInfo['CONT']['length'])) {
|
||||
$this->paddedlength = max($oldChunkInfo['CONT']['length'], $this->paddedlength);
|
||||
}
|
||||
|
||||
$new_CONT_tag_data = $this->GenerateCONTchunk();
|
||||
$new_PROP_tag_data = $this->GeneratePROPchunk($OldThisFileInfo['real']['chunks'], $new_CONT_tag_data);
|
||||
$new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']);
|
||||
|
||||
if (@$oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data)) {
|
||||
fseek($fp_source, $oldChunkInfo['.RMF']['offset'], SEEK_SET);
|
||||
fwrite($fp_source, $new__RMF_tag_data);
|
||||
} else {
|
||||
$this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)';
|
||||
fclose($fp_source);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (@$oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data)) {
|
||||
fseek($fp_source, $oldChunkInfo['PROP']['offset'], SEEK_SET);
|
||||
fwrite($fp_source, $new_PROP_tag_data);
|
||||
} else {
|
||||
$this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)';
|
||||
fclose($fp_source);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (@$oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data)) {
|
||||
|
||||
// new data length is same as old data length - just overwrite
|
||||
fseek($fp_source, $oldChunkInfo['CONT']['offset'], SEEK_SET);
|
||||
fwrite($fp_source, $new_CONT_tag_data);
|
||||
fclose($fp_source);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
if (empty($oldChunkInfo['CONT'])) {
|
||||
// no existing CONT chunk
|
||||
$BeforeOffset = $oldChunkInfo['DATA']['offset'];
|
||||
$AfterOffset = $oldChunkInfo['DATA']['offset'];
|
||||
} else {
|
||||
// new data is longer than old data
|
||||
$BeforeOffset = $oldChunkInfo['CONT']['offset'];
|
||||
$AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
|
||||
}
|
||||
if ($tempfilename = tempnam('*', 'getID3')) {
|
||||
ob_start();
|
||||
if ($fp_temp = fopen($tempfilename, 'wb')) {
|
||||
|
||||
rewind($fp_source);
|
||||
fwrite($fp_temp, fread($fp_source, $BeforeOffset));
|
||||
fwrite($fp_temp, $new_CONT_tag_data);
|
||||
fseek($fp_source, $AfterOffset, SEEK_SET);
|
||||
while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
|
||||
fwrite($fp_temp, $buffer, strlen($buffer));
|
||||
}
|
||||
fclose($fp_temp);
|
||||
|
||||
if (copy($tempfilename, $this->filename)) {
|
||||
unlink($tempfilename);
|
||||
fclose($fp_source);
|
||||
return true;
|
||||
}
|
||||
unlink($tempfilename);
|
||||
$this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents());
|
||||
|
||||
} else {
|
||||
|
||||
$this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
|
||||
|
||||
}
|
||||
ob_end_clean();
|
||||
}
|
||||
fclose($fp_source);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$this->errors[] = 'File is not writeable: '.$this->filename;
|
||||
return false;
|
||||
}
|
||||
|
||||
function GenerateRMFchunk(&$chunks) {
|
||||
$oldCONTexists = false;
|
||||
foreach ($chunks as $key => $chunk) {
|
||||
$chunkNameKeys[$chunk['name']] = $key;
|
||||
if ($chunk['name'] == 'CONT') {
|
||||
$oldCONTexists = true;
|
||||
}
|
||||
}
|
||||
$newHeadersCount = $chunks[$chunkNameKeys['.RMF']]['headers_count'] + ($oldCONTexists ? 0 : 1);
|
||||
|
||||
$RMFchunk = "\x00\x00"; // object version
|
||||
$RMFchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['.RMF']]['file_version'], 4);
|
||||
$RMFchunk .= getid3_lib::BigEndian2String($newHeadersCount, 4);
|
||||
|
||||
$RMFchunk = '.RMF'.getid3_lib::BigEndian2String(strlen($RMFchunk) + 8, 4).$RMFchunk; // .RMF chunk identifier + chunk length
|
||||
return $RMFchunk;
|
||||
}
|
||||
|
||||
function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data) {
|
||||
$old_CONT_length = 0;
|
||||
$old_DATA_offset = 0;
|
||||
$old_INDX_offset = 0;
|
||||
foreach ($chunks as $key => $chunk) {
|
||||
$chunkNameKeys[$chunk['name']] = $key;
|
||||
if ($chunk['name'] == 'CONT') {
|
||||
$old_CONT_length = $chunk['length'];
|
||||
} elseif ($chunk['name'] == 'DATA') {
|
||||
if (!$old_DATA_offset) {
|
||||
$old_DATA_offset = $chunk['offset'];
|
||||
}
|
||||
} elseif ($chunk['name'] == 'INDX') {
|
||||
if (!$old_INDX_offset) {
|
||||
$old_INDX_offset = $chunk['offset'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$CONTdelta = strlen($new_CONT_tag_data) - $old_CONT_length;
|
||||
|
||||
$PROPchunk = "\x00\x00"; // object version
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_bit_rate'], 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_bit_rate'], 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_packet_size'], 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_packet_size'], 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_packets'], 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['duration'], 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['preroll'], 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_INDX_offset + $CONTdelta), 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_DATA_offset + $CONTdelta), 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_streams'], 2);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['flags_raw'], 2);
|
||||
|
||||
$PROPchunk = 'PROP'.getid3_lib::BigEndian2String(strlen($PROPchunk) + 8, 4).$PROPchunk; // PROP chunk identifier + chunk length
|
||||
return $PROPchunk;
|
||||
}
|
||||
|
||||
function GenerateCONTchunk() {
|
||||
foreach ($this->tag_data as $key => $value) {
|
||||
// limit each value to 0xFFFF bytes
|
||||
$this->tag_data[$key] = substr($value, 0, 65535);
|
||||
}
|
||||
|
||||
$CONTchunk = "\x00\x00"; // object version
|
||||
|
||||
$CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['title']), 2);
|
||||
$CONTchunk .= @$this->tag_data['title'];
|
||||
|
||||
$CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['artist']), 2);
|
||||
$CONTchunk .= @$this->tag_data['artist'];
|
||||
|
||||
$CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['copyright']), 2);
|
||||
$CONTchunk .= @$this->tag_data['copyright'];
|
||||
|
||||
$CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['comment']), 2);
|
||||
$CONTchunk .= @$this->tag_data['comment'];
|
||||
|
||||
if ($this->paddedlength > (strlen($CONTchunk) + 8)) {
|
||||
$CONTchunk .= str_repeat("\x00", $this->paddedlength - strlen($CONTchunk) - 8);
|
||||
}
|
||||
|
||||
$CONTchunk = 'CONT'.getid3_lib::BigEndian2String(strlen($CONTchunk) + 8, 4).$CONTchunk; // CONT chunk identifier + chunk length
|
||||
|
||||
return $CONTchunk;
|
||||
}
|
||||
|
||||
function RemoveReal() {
|
||||
// File MUST be writeable - CHMOD(646) at least
|
||||
if (is_writeable($this->filename)) {
|
||||
if ($fp_source = @fopen($this->filename, 'r+b')) {
|
||||
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
$OldThisFileInfo = $getID3->analyze($this->filename);
|
||||
if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
|
||||
$this->errors[] = 'Cannot remove Real tags from old-style file format';
|
||||
fclose($fp_source);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($OldThisFileInfo['real']['chunks'])) {
|
||||
$this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file';
|
||||
fclose($fp_source);
|
||||
return false;
|
||||
}
|
||||
foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
|
||||
$oldChunkInfo[$chunkarray['name']] = $chunkarray;
|
||||
}
|
||||
|
||||
if (empty($oldChunkInfo['CONT'])) {
|
||||
// no existing CONT chunk
|
||||
fclose($fp_source);
|
||||
return true;
|
||||
}
|
||||
|
||||
$BeforeOffset = $oldChunkInfo['CONT']['offset'];
|
||||
$AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
|
||||
if ($tempfilename = tempnam('*', 'getID3')) {
|
||||
ob_start();
|
||||
if ($fp_temp = fopen($tempfilename, 'wb')) {
|
||||
|
||||
rewind($fp_source);
|
||||
fwrite($fp_temp, fread($fp_source, $BeforeOffset));
|
||||
fseek($fp_source, $AfterOffset, SEEK_SET);
|
||||
while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
|
||||
fwrite($fp_temp, $buffer, strlen($buffer));
|
||||
}
|
||||
fclose($fp_temp);
|
||||
|
||||
if (copy($tempfilename, $this->filename)) {
|
||||
unlink($tempfilename);
|
||||
fclose($fp_source);
|
||||
return true;
|
||||
}
|
||||
unlink($tempfilename);
|
||||
$this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents());
|
||||
|
||||
} else {
|
||||
|
||||
$this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
|
||||
|
||||
}
|
||||
ob_end_clean();
|
||||
}
|
||||
fclose($fp_source);
|
||||
return false;
|
||||
|
||||
|
||||
} else {
|
||||
$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$this->errors[] = 'File is not writeable: '.$this->filename;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// write.vorbiscomment.php //
|
||||
// module for writing VorbisComment tags //
|
||||
// dependencies: /helperapps/vorbiscomment.exe //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_write_vorbiscomment
|
||||
{
|
||||
|
||||
var $filename;
|
||||
var $tag_data;
|
||||
var $warnings = array(); // any non-critical errors will be stored here
|
||||
var $errors = array(); // any critical errors will be stored here
|
||||
|
||||
function getid3_write_vorbiscomment() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function WriteVorbisComment() {
|
||||
|
||||
if (!ini_get('safe_mode')) {
|
||||
|
||||
// Create file with new comments
|
||||
$tempcommentsfilename = tempnam('*', 'getID3');
|
||||
if ($fpcomments = @fopen($tempcommentsfilename, 'wb')) {
|
||||
|
||||
foreach ($this->tag_data as $key => $value) {
|
||||
foreach ($value as $commentdata) {
|
||||
fwrite($fpcomments, $this->CleanVorbisCommentName($key).'='.$commentdata."\n");
|
||||
}
|
||||
}
|
||||
fclose($fpcomments);
|
||||
|
||||
} else {
|
||||
|
||||
$this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
$oldignoreuserabort = ignore_user_abort(true);
|
||||
if (GETID3_OS_ISWINDOWS) {
|
||||
|
||||
if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
|
||||
//$commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w --raw -c "'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"';
|
||||
// vorbiscomment works fine if you copy-paste the above commandline into a command prompt,
|
||||
// but refuses to work with `backtick` if there are "doublequotes" present around BOTH
|
||||
// the metaflac pathname and the target filename. For whatever reason...??
|
||||
// The solution is simply ensure that the metaflac pathname has no spaces,
|
||||
// and therefore does not need to be quoted
|
||||
|
||||
// On top of that, if error messages are not always captured properly under Windows
|
||||
// To at least see if there was a problem, compare file modification timestamps before and after writing
|
||||
clearstatcache();
|
||||
$timestampbeforewriting = filemtime($this->filename);
|
||||
|
||||
$commandline = GETID3_HELPERAPPSDIR.'vorbiscomment.exe -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1';
|
||||
$VorbiscommentError = `$commandline`;
|
||||
|
||||
if (empty($VorbiscommentError)) {
|
||||
clearstatcache();
|
||||
if ($timestampbeforewriting == filemtime($this->filename)) {
|
||||
$VorbiscommentError = 'File modification timestamp has not changed - it looks like the tags were not written';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$VorbiscommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$commandline = 'vorbiscomment -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1';
|
||||
$VorbiscommentError = `$commandline`;
|
||||
|
||||
}
|
||||
|
||||
// Remove temporary comments file
|
||||
unlink($tempcommentsfilename);
|
||||
ignore_user_abort($oldignoreuserabort);
|
||||
|
||||
if (!empty($VorbiscommentError)) {
|
||||
|
||||
$this->errors[] = 'system call to vorbiscomment failed with message: '."\n\n".$VorbiscommentError;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call vorbiscomment, tags not written';
|
||||
return false;
|
||||
}
|
||||
|
||||
function DeleteVorbisComment() {
|
||||
$this->tag_data = array(array());
|
||||
return $this->WriteVorbisComment();
|
||||
}
|
||||
|
||||
function CleanVorbisCommentName($originalcommentname) {
|
||||
// A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded.
|
||||
// ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through
|
||||
// 0x7A inclusive (a-z).
|
||||
|
||||
// replace invalid chars with a space, return uppercase text
|
||||
// Thanks Chris Bolt <chris-getid3Øbolt*cx> for improving this function
|
||||
// note: ereg_replace() replaces nulls with empty string (not space)
|
||||
return strtoupper(ereg_replace('[^ -<>-}]', ' ', str_replace("\x00", ' ', $originalcommentname)));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,55 @@
|
|||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// /helperapps/readme.txt - part of getID3() //
|
||||
// List of binary files required under Windows for some //
|
||||
// features and/or file formats //
|
||||
// See /readme.txt for more details //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
This directory should contain binaries of various helper applications
|
||||
that getID3() depends on to handle some file formats under Windows.
|
||||
|
||||
The location of this directory is configurable in /getid3/getid3.php
|
||||
as GETID3_HELPERAPPSDIR
|
||||
|
||||
If this directory is empty, or you are missing any files, please
|
||||
download the latest version of the "getID3()-WindowsSupport" package
|
||||
from the usual download location (http://getid3.sourceforge.net)
|
||||
|
||||
|
||||
|
||||
Included files:
|
||||
=====================================================
|
||||
|
||||
Taken from http://www.cygwin.com/
|
||||
* cygwin1.dll
|
||||
|
||||
Taken from http://unxutils.sourceforge.net/
|
||||
* head.exe
|
||||
* md5sum.exe
|
||||
* tail.exe
|
||||
|
||||
Taken from http://ebible.org/mpj/software.htm
|
||||
* sha1sum.exe
|
||||
|
||||
Taken from http://www.vorbis.com/download.psp
|
||||
* vorbiscomment.exe
|
||||
|
||||
Taken from http://flac.sourceforge.net/download.html
|
||||
* metaflac.exe
|
||||
|
||||
Taken from http://www.etree.org/shncom.html
|
||||
* shorten.exe
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
Changelog:
|
||||
|
||||
2003.12.29:
|
||||
* Initial release
|
|
@ -0,0 +1,27 @@
|
|||
getID3() Commercial License
|
||||
===========================
|
||||
|
||||
getID3() is licensed under the "GNU Public License" (GPL) and/or the
|
||||
"getID3() Commercial License" (gCL). This document describes the gCL.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
|
||||
The license is non-exclusively granted to a single person or company,
|
||||
per payment of the license fee, for the lifetime of that person or
|
||||
company. The license is non-transferrable.
|
||||
|
||||
The gCL grants the licensee the right to use getID3() in commercial
|
||||
closed-source projects. Modifications may be made to getID3() with no
|
||||
obligation to release the modified source code. getID3() (or pieces
|
||||
thereof) may be included in any number of projects authored (in whole
|
||||
or in part) by the licensee.
|
||||
|
||||
The licensee may use any version of getID3(), past, present or future,
|
||||
as is most convenient. This license does not entitle the licensee to
|
||||
receive any technical support, updates or bugfixes, except as such are
|
||||
made publicly available to all getID3() users.
|
||||
|
||||
The licensee may not sub-license getID3() itself, meaning that any
|
||||
commercially released product containing all or parts of getID3() must
|
||||
have added functionality beyond what is available in getID3();
|
||||
getID3() itself may not be re-licensed by the licensee.
|
|
@ -0,0 +1,340 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
|
@ -0,0 +1,549 @@
|
|||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// changelog.txt - part of getID3() //
|
||||
// See readme.txt for more details //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
This code is released under the GNU GPL:
|
||||
http://www.gnu.org/copyleft/gpl.html
|
||||
|
||||
+---------------------------------------------+
|
||||
| If you do use this code somewhere, send me |
|
||||
| an email and tell me how/where you used it. |
|
||||
| |
|
||||
| If you want to donate, there is a link on |
|
||||
| http://www.getid3.org for PayPal donations. |
|
||||
+---------------------------------------------+
|
||||
|
||||
|
||||
|
||||
Quick Start
|
||||
===========================================================================
|
||||
|
||||
Q: How can I check that getID3() works on my server/files?
|
||||
A: Unzip getID3() to a directory, then access /demos/demo.browse.php
|
||||
|
||||
|
||||
|
||||
Sourceforge Notification
|
||||
===========================================================================
|
||||
|
||||
It's highly recommended that you sign up for notification from
|
||||
Sourceforge for when new versions are released. Please visit:
|
||||
http://sourceforge.net/project/showfiles.php?group_id=55859
|
||||
and click the little "monitor package" icon/link. If you're
|
||||
previously signed up for the mailing list, be aware that it has
|
||||
been discontinued, only the automated Sourceforge notification
|
||||
will be used from now on.
|
||||
|
||||
|
||||
|
||||
What does getID3() do?
|
||||
===========================================================================
|
||||
|
||||
Reads & parses (to varying degrees):
|
||||
¤ tags:
|
||||
* APE (v1 and v2)
|
||||
* ID3v1 (& ID3v1.1)
|
||||
* ID3v2 (v2.4, v2.3, v2.2)
|
||||
* Lyrics3 (v1 & v2)
|
||||
|
||||
¤ audio-lossy:
|
||||
* MP3/MP2/MP1
|
||||
* MPC / Musepack
|
||||
* Ogg (Vorbis, OggFLAC, Speex)
|
||||
* RealAudio
|
||||
* Speex
|
||||
* VQF
|
||||
|
||||
¤ audio-lossless:
|
||||
* AIFF
|
||||
* AU
|
||||
* Bonk
|
||||
* CD-audio (*.cda)
|
||||
* FLAC
|
||||
* LA (Lossless Audio)
|
||||
* LPAC
|
||||
* MIDI
|
||||
* Monkey's Audio
|
||||
* OptimFROG
|
||||
* RKAU
|
||||
* VOC
|
||||
* WAV (RIFF)
|
||||
* WavPack
|
||||
|
||||
¤ audio-video:
|
||||
* ASF: ASF, Windows Media Audio (WMA), Windows Media Video (WMV)
|
||||
* AVI (RIFF)
|
||||
* Flash
|
||||
* MPEG-1 / MPEG-2
|
||||
* NSV (Nullsoft Streaming Video)
|
||||
* Quicktime
|
||||
* RealVideo
|
||||
|
||||
¤ still image:
|
||||
* BMP
|
||||
* GIF
|
||||
* JPEG
|
||||
* PNG
|
||||
|
||||
¤ data:
|
||||
* ISO-9660 CD-ROM image (directory structure)
|
||||
* SZIP (limited support)
|
||||
* ZIP (directory structure)
|
||||
|
||||
|
||||
Writes:
|
||||
* ID3v1 (& ID3v1.1)
|
||||
* ID3v2 (v2.3 & v2.4)
|
||||
* VorbisComment on OggVorbis
|
||||
* VorbisComment on FLAC (not OggFLAC)
|
||||
* APE v2
|
||||
* Lyrics3 (delete only)
|
||||
|
||||
|
||||
|
||||
Requirements
|
||||
===========================================================================
|
||||
|
||||
* PHP 4.2.0 (or higher) for getID3() 1.7.8 (and up).
|
||||
* PHP 5.0.0 (or higher) for getID3() 2.0.0 (and up).
|
||||
* at least 4MB memory for PHP. 8MB is highly recommended.
|
||||
12MB is required with all modules loaded.
|
||||
|
||||
|
||||
|
||||
Usage
|
||||
===========================================================================
|
||||
|
||||
See /demos/demo.basic.php for a very basic use of getID3() with no
|
||||
fancy output, just scanning one file.
|
||||
|
||||
See structure.txt for the returned data structure.
|
||||
|
||||
*> For an example of a complete directory-browsing, <*
|
||||
*> file-scanning implementation of getID3(), please run <*
|
||||
*> /demos/demo.browse.php <*
|
||||
|
||||
See /demos/demo.mysql.php for a sample recursive scanning code that
|
||||
scans every file in a given directory, and all sub-directories, stores
|
||||
the results in a database and allows various analysis / maintenance
|
||||
operations
|
||||
|
||||
To analyze remote files over HTTP or FTP you need to copy the file
|
||||
locally first before running getID3(). Your code would look something
|
||||
like this:
|
||||
|
||||
// Copy remote file locally to scan with getID3()
|
||||
$remotefilename = 'http://www.example.com/filename.mp3';
|
||||
if ($fp_remote = fopen($remotefilename, 'rb')) {
|
||||
$localtempfilename = tempnam('/tmp', 'getID3');
|
||||
if ($fp_local = fopen($localtempfilename, 'wb')) {
|
||||
while ($buffer = fread($fp_remote, 8192)) {
|
||||
fwrite($fp_local, $buffer);
|
||||
}
|
||||
fclose($fp_local);
|
||||
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
|
||||
$ThisFileInfo = $getID3->analyze($filename);
|
||||
|
||||
// Delete temporary file
|
||||
unlink($localtempfilename);
|
||||
}
|
||||
fclose($fp_remote);
|
||||
}
|
||||
|
||||
|
||||
See /demos/demo.write.php for how to write tags.
|
||||
|
||||
|
||||
|
||||
What does the returned data structure look like?
|
||||
===========================================================================
|
||||
|
||||
See structure.txt
|
||||
|
||||
It is recommended that you look at the output of
|
||||
/demos/demo.browse.php scanning the file(s) you're interested in to
|
||||
confirm what data is actually returned for any particular filetype in
|
||||
general, and your files in particular, as the actual data returned
|
||||
may vary considerably depending on what information is available in
|
||||
the file itself.
|
||||
|
||||
|
||||
|
||||
Notes
|
||||
===========================================================================
|
||||
|
||||
getID3() 1.7:
|
||||
If the format parser encounters a critical problem, it will return
|
||||
something in $fileinfo['error'], describing the encountered error. If
|
||||
a less critical error or notice is generated it will appear in
|
||||
$fileinfo['warning']. Both keys may contain more than one warning or
|
||||
error. If something is returned in ['error'] then the file was not
|
||||
correctly parsed and returned data may or may not be correct and/or
|
||||
complete. If something is returned in ['warning'] (and not ['error'])
|
||||
then the data that is returned is OK - usually getID3() is reporting
|
||||
errors in the file that have been worked around due to known bugs in
|
||||
other programs. Some warnings may indicate that the data that is
|
||||
returned is OK but that some data could not be extracted due to
|
||||
errors in the file.
|
||||
|
||||
getID3() 2.0:
|
||||
See above except errors are thrown (so you will only get one error).
|
||||
|
||||
|
||||
|
||||
Disclaimer
|
||||
===========================================================================
|
||||
|
||||
getID3() has been tested on many systems, on many types of files,
|
||||
under many operating systems, and is generally believe to be stable
|
||||
and safe. That being said, there is still the chance there is an
|
||||
undiscovered and/or unfixed bug that may potentially corrupt your
|
||||
file, especially within the writing functions. By using getID3() you
|
||||
agree that it's not my fault if any of your files are corrupted.
|
||||
In fact, I'm not liable for anything :)
|
||||
|
||||
|
||||
|
||||
License
|
||||
===========================================================================
|
||||
|
||||
GNU General Public License - see license.txt
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
FAQ:
|
||||
Q: Can I use getID3() in my program? Do I need a commercial license?
|
||||
A: You're generally free to use getID3 however you see fit. The only
|
||||
case in which you would require a commercial license is if you're
|
||||
selling your closed-source program that integrates getID3. If you
|
||||
sell your program including a copy of getID3, that's fine as long
|
||||
as you include a copy of the sourcecode when you sell it. Or you
|
||||
can distribute your code without getID3 and say "download it from
|
||||
getid3.sourceforge.net"
|
||||
|
||||
|
||||
|
||||
Future Plans
|
||||
===========================================================================
|
||||
|
||||
* Writing support for Real
|
||||
* Better support for MP4 container format
|
||||
* Support for Matroska (www.matroska.org)
|
||||
http://corecodec.com/modules.php?op=modload&name=PNphpBB2&file=viewtopic&t=227
|
||||
* Scan for appended ID3v2 tag at end of file per ID3v2.4 specs (Section 5.0)
|
||||
* Support for JPEG-2000 (http://www.morgan-multimedia.com/jpeg2000_overview.htm)
|
||||
* Support for MOD (mod/stm/s3m/it/xm/mtm/ult/669)
|
||||
* Support for ACE (thanks Vince)
|
||||
* Support for Ogg other than Vorbis, Speex and OggFlac (ie. Ogg+Xvid)
|
||||
* Ability to create Xing/LAME VBR header for VBR MP3s that are missing VBR header
|
||||
* Ability to "clean" ID3v2 padding (replace invalid padding with valid padding)
|
||||
* Warn if MP3s change version mid-stream (in full-scan mode)
|
||||
* check for corrupt/broken mid-file MP3 streams in histogram scan
|
||||
* Support for lossless-compression formats
|
||||
(http://www.firstpr.com.au/audiocomp/lossless/#Links)
|
||||
(http://compression.ca/act-sound.html)
|
||||
(http://web.inter.nl.net/users/hvdh/lossless/lossless.htm)
|
||||
* Support for RIFF-INFO chunks
|
||||
* http://lotto.st-andrews.ac.uk/~njh/tag_interchange.html
|
||||
(thanks Nick Humfrey <njhØsurgeradio*co*uk>)
|
||||
* http://abcavi.narod.ru/sof/abcavi/infotags.htm
|
||||
(thanks Kibi)
|
||||
* Better support for Bink video
|
||||
* http://www.hr/josip/DSP/AudioFile2.html
|
||||
* http://www.pcisys.net/~melanson/codecs/
|
||||
* Detect mp3PRO
|
||||
* Support for PSD
|
||||
* Support for JPC
|
||||
* Support for JP2
|
||||
* Support for JPX
|
||||
* Support for JB2
|
||||
* Support for IFF
|
||||
* Support for ICO
|
||||
* Support for ANI
|
||||
* Support for EXE (comments, author, etc) (thanks p*quaedackersØplanet*nl)
|
||||
* Support for DVD-IFO (region, subtitles, aspect ratio, etc)
|
||||
(thanks p*quaedackersØplanet*nl)
|
||||
* More complete support for SWF - parsing encapsulated MP3 and/or JPEG content
|
||||
(thanks n8n8Øyahoo*com)
|
||||
* Support for a2b
|
||||
* Optional scan-through-frames for AVI verification
|
||||
(thanks rockcohenØmassive-interactive*nl)
|
||||
* Support for TTF (thanks infoØbutterflyx*com)
|
||||
* Support for DSS (http://www.getid3.org/phpBB2/viewtopic.php?t=171)
|
||||
* Support for SMAF (http://smaf-yamaha.com/what/demo.html)
|
||||
http://www.getid3.org/phpBB2/viewtopic.php?t=182
|
||||
* Support for AMR (http://www.getid3.org/phpBB2/viewtopic.php?t=195)
|
||||
* Support for 3gpp (http://www.getid3.org/phpBB2/viewtopic.php?t=195)
|
||||
* Support for ID4 (http://www.wackysoft.cjb.net grizlyY2KØhotmail*com)
|
||||
* Parse XML data returned in Ogg comments
|
||||
* Parse XML data from Quicktime SMIL metafiles (klausrathØmac*com)
|
||||
* ID3v2 genre string creator function
|
||||
* More complete parsing of JPG
|
||||
* Support for all old-style ASF packets
|
||||
* ASF/WMA/WMV tag writing
|
||||
* Parse declared T??? ID3v2 text information frames, where appropriate
|
||||
(thanks Christian Fritz for the idea)
|
||||
* Recognize encoder:
|
||||
http://www.guerillasoft.com/EncSpot2/index.html
|
||||
http://ff123.net/identify.html
|
||||
http://www.hydrogenaudio.org/?act=ST&f=16&t=9414
|
||||
http://www.hydrogenaudio.org/?showtopic=11785
|
||||
* Support for other OS/2 bitmap structures: Bitmap Array('BA'),
|
||||
Color Icon('CI'), Color Pointer('CP'), Icon('IC'), Pointer ('PT')
|
||||
http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
|
||||
* Support for WavPack RAW mode
|
||||
* ASF/WMA/WMV data packet parsing
|
||||
* ID3v2FrameFlagsLookupTagAlter()
|
||||
* ID3v2FrameFlagsLookupFileAlter()
|
||||
* obey ID3v2 tag alter/preserve/discard rules
|
||||
* http://www.geocities.com/SiliconValley/Sector/9654/Softdoc/Illyrium/Aolyr.htm
|
||||
* proper checking for LINK/LNK frame validity in ID3v2 writing
|
||||
* proper checking for ASPI-TLEN frame validity in ID3v2 writing
|
||||
* proper checking for COMR frame validity in ID3v2 writing
|
||||
* http://www.geocities.co.jp/SiliconValley-Oakland/3664/index.html
|
||||
* decode GEOB ID3v2 structure as encoded by RealJukebox,
|
||||
decode NCON ID3v2 structure as encoded by MusicMatch
|
||||
(probably won't happen - the formats are proprietary)
|
||||
|
||||
|
||||
|
||||
Known Bugs/Issues in getID3() that may be fixed eventually
|
||||
===========================================================================
|
||||
|
||||
* Cannot determine bitrate for MPEG video with VBR video data
|
||||
(need documentation)
|
||||
* Interlace/progressive cannot be determined for MPEG video
|
||||
(need documentation)
|
||||
* MIDI playtime is sometimes inaccurate
|
||||
* AAC-RAW mode files cannot be identified
|
||||
* WavPack-RAW mode files cannot be identified
|
||||
* mp4 files report lots of "Unknown QuickTime atom type"
|
||||
(need documentation)
|
||||
* Encrypted ASF/WMA/WMV files warn about "unhandled GUID
|
||||
ASF_Content_Encryption_Object"
|
||||
* Bitrate split between audio and video cannot be calculated for
|
||||
NSV, only the total bitrate. (need documentation)
|
||||
* All Ogg formats (Vorbis, OggFLAC, Speex) are affected by the
|
||||
problem of large VorbisComments spanning multiple Ogg pages, but
|
||||
but only OggVorbis files can be processed with vorbiscomment.
|
||||
* The version of "head" supplied with Mac OS 10.2.8 (maybe other
|
||||
versions too) does only understands a single option (-n) and
|
||||
therefore fails. getID3 ignores this and returns wrong md5_data.
|
||||
|
||||
|
||||
|
||||
Known Bugs/Issues in getID3() that cannot be fixed
|
||||
--------------------------------------------------
|
||||
|
||||
* Files larger than 2GB cannot always be parsed fully by getID3()
|
||||
due to limitations in the PHP filesystem functions.
|
||||
NOTE: Since v1.7.8b3 there is partial support for larger-than-
|
||||
2GB files, most of which will parse OK, as long as no critical
|
||||
data is located beyond the 2GB offset.
|
||||
Known will-work:
|
||||
* ZIP (format doesn't support files >2GB)
|
||||
* FLAC (current encoders don't support files >2GB)
|
||||
Known will-not-work:
|
||||
* ID3v1 tags (always located at end-of-file)
|
||||
* Lyrics3 tags (always located at end-of-file)
|
||||
* APE tags (always located at end-of-file)
|
||||
Maybe-will-work:
|
||||
* Quicktime (will work if needed metadata is before 2GB offset,
|
||||
that is if the file has been hinted/optimized for streaming)
|
||||
* RIFF.WAV (should work fine, but gives warnings about not being
|
||||
able to parse all chunks)
|
||||
* RIFF.AVI (playtime will probably be wrong, is only based on
|
||||
"movi" chunk that fits in the first 2GB, should issue error
|
||||
to show that playtime is incorrect. Other data should be mostly
|
||||
correct, assuming that data is constant throughout the file)
|
||||
|
||||
|
||||
|
||||
Known Bugs/Issues in other programs
|
||||
-----------------------------------
|
||||
|
||||
* Winamp (up to v2.80 at least) does not support ID3v2.4 tags,
|
||||
only ID3v2.3
|
||||
see: http://forums.winamp.com/showthread.php?postid=387524
|
||||
* Some versions of Helium2 (www.helium2.com) do not write
|
||||
ID3v2.4-compliant Frame Sizes, even though the tag is marked
|
||||
as ID3v2.4) (detected by getID3())
|
||||
* MP3ext V3.3.17 places a non-compliant padding string at the end
|
||||
of the ID3v2 header. This is supposedly fixed in v3.4b21 but
|
||||
only if you manually add a registry key. This fix is not yet
|
||||
confirmed. (detected by getID3())
|
||||
* CDex v1.40 (fixed by v1.50b7) writes non-compliant Ogg comment
|
||||
strings, supposed to be in the format "NAME=value" but actually
|
||||
written just "value" (detected by getID3())
|
||||
* Oggenc 0.9-rc3 flags the encoded file as ABR whether it's
|
||||
actually ABR or VBR.
|
||||
* iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably
|
||||
other versions are too) writes ID3v2.3 comment tags using a
|
||||
frame name 'COM ' which is not valid for ID3v2.3+ (it's an
|
||||
ID3v2.2-style frame name) (detected by getID3())
|
||||
* MP2enc does not encode mono CBR MP2 files properly (half speed
|
||||
sound and double playtime)
|
||||
* MP2enc does not encode mono VBR MP2 files properly (actually
|
||||
encoded as stereo)
|
||||
* tooLAME does not encode mono VBR MP2 files properly (actually
|
||||
encoded as stereo)
|
||||
* AACenc encodes files in VBR mode (actually ABR) even if CBR is
|
||||
specified
|
||||
* AAC/ADIF - bitrate_mode = cbr for vbr files
|
||||
* LAME 3.90-3.92 prepends one frame of null data (space for the
|
||||
LAME/VBR header, but it never gets written) when encoding in CBR
|
||||
mode with the DLL
|
||||
* Ahead Nero encodes TwinVQF with a DSIZ value (which is supposed
|
||||
to be the filesize in bytes) of "0" for TwinVQF v1.0 and "1" for
|
||||
TwinVQF v2.0 (detected by getID3())
|
||||
* Ahead Nero encodes TwinVQF files 1 second shorter than they
|
||||
should be
|
||||
* AAC-ADTS files are always actually encoded VBR, even if CBR mode
|
||||
is specified (the CBR-mode switches on the encoder enable ABR
|
||||
mode, not CBR as such, but it's not possible to tell the
|
||||
difference between such ABR files and true VBR)
|
||||
* STREAMINFO.audio_signature in OggFLAC is always null. "The reason
|
||||
it's like that is because there is no seeking support in
|
||||
libOggFLAC yet, so it has no way to go back and write the
|
||||
computed sum after encoding. Seeking support in Ogg FLAC is the
|
||||
#1 item for the next release." - Josh Coalson (FLAC developer)
|
||||
NOTE: getID3() will calculate md5_data in a method similar to
|
||||
other file formats, but that value cannot be compared to the
|
||||
md5_data value from FLAC data in a FLAC file format.
|
||||
* STREAMINFO.audio_signature is not calculated in FLAC v0.3.0 &
|
||||
v0.4.0 - getID3() will calculate md5_data in a method similar to
|
||||
other file formats, but that value cannot be compared to the
|
||||
md5_data value from FLAC v0.5.0+
|
||||
* RioPort (various versions including 2.0 and 3.11) tags ID3v2 with
|
||||
a WCOM frame that has no data portion
|
||||
* Earlier versions of Coolplayer adds illegal ID3 tags to Ogg Vorbis
|
||||
files, thus making them corrupt.
|
||||
* Meracl ID3 Tag Writer v1.3.4 (and older) incorrectly truncates the
|
||||
last byte of data from an MP3 file when appending a new ID3v1 tag.
|
||||
(detected by getID3())
|
||||
* Lossless-Audio files encoded with and without the -noseek switch
|
||||
do actually differ internally and therefore cannot match md5_data
|
||||
* iTunes has been known to append a new ID3v1 tag on the end of an
|
||||
existing ID3v1 tag when ID3v2 tag is also present
|
||||
(detected by getID3())
|
||||
|
||||
|
||||
|
||||
|
||||
Reference material:
|
||||
===========================================================================
|
||||
|
||||
[www.id3.org material now mirrored at http://id3lib.sourceforge.net/id3/]
|
||||
* http://www.id3.org/id3v2.4.0-structure.txt
|
||||
* http://www.id3.org/id3v2.4.0-frames.txt
|
||||
* http://www.id3.org/id3v2.4.0-changes.txt
|
||||
* http://www.id3.org/id3v2.3.0.txt
|
||||
* http://www.id3.org/id3v2-00.txt
|
||||
* http://www.id3.org/mp3frame.html
|
||||
* http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html <mathewhendry@hotmail.com>
|
||||
* http://www.dv.co.yu/mpgscript/mpeghdr.htm
|
||||
* http://www.mp3-tech.org/programmer/frame_header.html
|
||||
* http://users.belgacom.net/gc247244/extra/tag.html
|
||||
* http://gabriel.mp3-tech.org/mp3infotag.html
|
||||
* http://www.id3.org/iso4217.html
|
||||
* http://www.unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT
|
||||
* http://www.xiph.org/ogg/vorbis/doc/framing.html
|
||||
* http://www.xiph.org/ogg/vorbis/doc/v-comment.html
|
||||
* http://leknor.com/code/php/class.ogg.php.txt
|
||||
* http://www.id3.org/iso639-2.html
|
||||
* http://www.id3.org/lyrics3.html
|
||||
* http://www.id3.org/lyrics3200.html
|
||||
* http://www.psc.edu/general/software/packages/ieee/ieee.html
|
||||
* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
|
||||
* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
|
||||
* http://www.jmcgowan.com/avi.html
|
||||
* http://www.wotsit.org/
|
||||
* http://www.herdsoft.com/ti/davincie/davp3xo2.htm
|
||||
* http://www.mathdogs.com/vorbis-illuminated/bitstream-appendix.html
|
||||
* "Standard MIDI File Format" by Dustin Caldwell (from www.wotsit.org)
|
||||
* http://midistudio.com/Help/GMSpecs_Patches.htm
|
||||
* http://www.xiph.org/archives/vorbis/200109/0459.html
|
||||
* http://www.replaygain.org/
|
||||
* http://www.lossless-audio.com/
|
||||
* http://download.microsoft.com/download/winmediatech40/Doc/1.0/WIN98MeXP/EN-US/ASF_Specification_v.1.0.exe
|
||||
* http://mediaxw.sourceforge.net/files/doc/Active%20Streaming%20Format%20(ASF)%201.0%20Specification.pdf
|
||||
* http://www.uni-jena.de/~pfk/mpp/sv8/
|
||||
* http://jfaul.de/atl/
|
||||
* http://www.uni-jena.de/~pfk/mpp/
|
||||
* http://www.libpng.org/pub/png/spec/png-1.2-pdg.html
|
||||
* http://www.real.com/devzone/library/creating/rmsdk/doc/rmff.htm
|
||||
* http://www.fastgraph.com/help/bmp_os2_header_format.html
|
||||
* http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
|
||||
* http://flac.sourceforge.net/format.html
|
||||
* http://www.research.att.com/projects/mpegaudio/mpeg2.html
|
||||
* http://www.audiocoding.com/wiki/index.php?page=AAC
|
||||
* http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
|
||||
* http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
|
||||
* http://developer.apple.com/techpubs/quicktime/qtdevdocs/RM/frameset.htm
|
||||
* http://www.nullsoft.com/nsv/
|
||||
* http://www.wotsit.org/download.asp?f=iso9660
|
||||
* http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html
|
||||
* http://www.cdroller.com/htm/readdata.html
|
||||
* http://www.speex.org/manual/node10.html
|
||||
* http://www.harmony-central.com/Computer/Programming/aiff-file-format.doc
|
||||
* http://www.faqs.org/rfcs/rfc2361.html
|
||||
* http://ghido.shelter.ro/
|
||||
* http://www.ebu.ch/tech_t3285.pdf
|
||||
* http://www.sr.se/utveckling/tu/bwf
|
||||
* http://ftp.aessc.org/pub/aes46-2002.pdf
|
||||
* http://cartchunk.org:8080/
|
||||
* http://www.broadcastpapers.com/radio/cartchunk01.htm
|
||||
* http://www.hr/josip/DSP/AudioFile2.html
|
||||
* http://home.attbi.com/~chris.bagwell/AudioFormats-11.html
|
||||
* http://www.pure-mac.com/extkey.html
|
||||
* http://cesnet.dl.sourceforge.net/sourceforge/bonkenc/bonk-binary-format-0.9.txt
|
||||
* http://www.headbands.com/gspot/
|
||||
* http://www.openswf.org/spec/SWFfileformat.html
|
||||
* http://j-faul.virtualave.net/
|
||||
* http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html
|
||||
* http://cui.unige.ch/OSG/info/AudioFormats/ap11.html
|
||||
* http://sswf.sourceforge.net/SWFalexref.html
|
||||
* http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
|
||||
* http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
|
||||
* http://developer.apple.com/quicktime/icefloe/dispatch012.html
|
||||
* http://www.csdn.net/Dev/Format/graphics/PCD.htm
|
||||
* http://tta.iszf.irk.ru/
|
||||
* http://www.atsc.org/standards/a_52a.pdf
|
||||
* http://www.alanwood.net/unicode/
|
||||
* http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
|
||||
* http://www.its.msstate.edu/net/real/reports/config/tags.stats
|
||||
* http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
|
||||
* http://brennan.young.net/Comp/LiveStage/things.html
|
||||
* http://www.multiweb.cz/twoinches/MP3inside.htm
|
||||
* http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended
|
||||
* http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
|
||||
* http://www.unicode.org/unicode/faq/utf_bom.html
|
||||
* http://tta.corecodec.org/?menu=format
|
||||
* http://www.scvi.net/nsvformat.htm
|
||||
* http://pda.etsi.org/pda/queryform.asp
|
||||
* http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
|
||||
* http://trac.musepack.net/trac/wiki/SV8Specification
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
After Width: | Height: | Size: 297 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue