File indexing completed on 2024-05-12 05:58:12

0001 <?php
0002 /////////////////////////////////////////////////////////////////
0003 /// getID3() by James Heinrich <info@getid3.org>               //
0004 //  available at http://getid3.sourceforge.net                 //
0005 //            or http://www.getid3.org                         //
0006 //          also https://github.com/JamesHeinrich/getID3       //
0007 /////////////////////////////////////////////////////////////////
0008 // See readme.txt for more details                             //
0009 /////////////////////////////////////////////////////////////////
0010 //                                                             //
0011 // module.audio.nsv.php                                        //
0012 // module for analyzing Nullsoft NSV files                     //
0013 // dependencies: NONE                                          //
0014 //                                                            ///
0015 /////////////////////////////////////////////////////////////////
0016 
0017 
0018 class getid3_nsv extends getid3_handler
0019 {
0020 
0021   public function Analyze() {
0022     $info = &$this->getid3->info;
0023 
0024     $this->fseek($info['avdataoffset']);
0025     $NSVheader = $this->fread(4);
0026 
0027     switch ($NSVheader) {
0028       case 'NSVs':
0029         if ($this->getNSVsHeaderFilepointer(0)) {
0030           $info['fileformat']          = 'nsv';
0031           $info['audio']['dataformat'] = 'nsv';
0032           $info['video']['dataformat'] = 'nsv';
0033           $info['audio']['lossless']   = false;
0034           $info['video']['lossless']   = false;
0035         }
0036         break;
0037 
0038       case 'NSVf':
0039         if ($this->getNSVfHeaderFilepointer(0)) {
0040           $info['fileformat']          = 'nsv';
0041           $info['audio']['dataformat'] = 'nsv';
0042           $info['video']['dataformat'] = 'nsv';
0043           $info['audio']['lossless']   = false;
0044           $info['video']['lossless']   = false;
0045           $this->getNSVsHeaderFilepointer($info['nsv']['NSVf']['header_length']);
0046         }
0047         break;
0048 
0049       default:
0050         $info['error'][] = 'Expecting "NSVs" or "NSVf" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($NSVheader).'"';
0051         return false;
0052         break;
0053     }
0054 
0055     if (!isset($info['nsv']['NSVf'])) {
0056       $info['warning'][] = 'NSVf header not present - cannot calculate playtime or bitrate';
0057     }
0058 
0059     return true;
0060   }
0061 
0062   public function getNSVsHeaderFilepointer($fileoffset) {
0063     $info = &$this->getid3->info;
0064     $this->fseek($fileoffset);
0065     $NSVsheader = $this->fread(28);
0066     $offset = 0;
0067 
0068     $info['nsv']['NSVs']['identifier']      =                  substr($NSVsheader, $offset, 4);
0069     $offset += 4;
0070 
0071     if ($info['nsv']['NSVs']['identifier'] != 'NSVs') {
0072       $info['error'][] = 'expected "NSVs" at offset ('.$fileoffset.'), found "'.$info['nsv']['NSVs']['identifier'].'" instead';
0073       unset($info['nsv']['NSVs']);
0074       return false;
0075     }
0076 
0077     $info['nsv']['NSVs']['offset']          = $fileoffset;
0078 
0079     $info['nsv']['NSVs']['video_codec']     =                              substr($NSVsheader, $offset, 4);
0080     $offset += 4;
0081     $info['nsv']['NSVs']['audio_codec']     =                              substr($NSVsheader, $offset, 4);
0082     $offset += 4;
0083     $info['nsv']['NSVs']['resolution_x']    = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
0084     $offset += 2;
0085     $info['nsv']['NSVs']['resolution_y']    = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
0086     $offset += 2;
0087 
0088     $info['nsv']['NSVs']['framerate_index'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
0089     $offset += 1;
0090     //$info['nsv']['NSVs']['unknown1b']       = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
0091     $offset += 1;
0092     //$info['nsv']['NSVs']['unknown1c']       = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
0093     $offset += 1;
0094     //$info['nsv']['NSVs']['unknown1d']       = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
0095     $offset += 1;
0096     //$info['nsv']['NSVs']['unknown2a']       = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
0097     $offset += 1;
0098     //$info['nsv']['NSVs']['unknown2b']       = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
0099     $offset += 1;
0100     //$info['nsv']['NSVs']['unknown2c']       = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
0101     $offset += 1;
0102     //$info['nsv']['NSVs']['unknown2d']       = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
0103     $offset += 1;
0104 
0105     switch ($info['nsv']['NSVs']['audio_codec']) {
0106       case 'PCM ':
0107         $info['nsv']['NSVs']['bits_channel'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
0108         $offset += 1;
0109         $info['nsv']['NSVs']['channels']     = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
0110         $offset += 1;
0111         $info['nsv']['NSVs']['sample_rate']  = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
0112         $offset += 2;
0113 
0114         $info['audio']['sample_rate']        = $info['nsv']['NSVs']['sample_rate'];
0115         break;
0116 
0117       case 'MP3 ':
0118       case 'NONE':
0119       default:
0120         //$info['nsv']['NSVs']['unknown3']     = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 4));
0121         $offset += 4;
0122         break;
0123     }
0124 
0125     $info['video']['resolution_x']       = $info['nsv']['NSVs']['resolution_x'];
0126     $info['video']['resolution_y']       = $info['nsv']['NSVs']['resolution_y'];
0127     $info['nsv']['NSVs']['frame_rate']   = $this->NSVframerateLookup($info['nsv']['NSVs']['framerate_index']);
0128     $info['video']['frame_rate']         = $info['nsv']['NSVs']['frame_rate'];
0129     $info['video']['bits_per_sample']    = 24;
0130     $info['video']['pixel_aspect_ratio'] = (float) 1;
0131 
0132     return true;
0133   }
0134 
0135   public function getNSVfHeaderFilepointer($fileoffset, $getTOCoffsets=false) {
0136     $info = &$this->getid3->info;
0137     $this->fseek($fileoffset);
0138     $NSVfheader = $this->fread(28);
0139     $offset = 0;
0140 
0141     $info['nsv']['NSVf']['identifier']    =                  substr($NSVfheader, $offset, 4);
0142     $offset += 4;
0143 
0144     if ($info['nsv']['NSVf']['identifier'] != 'NSVf') {
0145       $info['error'][] = 'expected "NSVf" at offset ('.$fileoffset.'), found "'.$info['nsv']['NSVf']['identifier'].'" instead';
0146       unset($info['nsv']['NSVf']);
0147       return false;
0148     }
0149 
0150     $info['nsv']['NSVs']['offset']        = $fileoffset;
0151 
0152     $info['nsv']['NSVf']['header_length'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
0153     $offset += 4;
0154     $info['nsv']['NSVf']['file_size']     = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
0155     $offset += 4;
0156 
0157     if ($info['nsv']['NSVf']['file_size'] > $info['avdataend']) {
0158       $info['warning'][] = 'truncated file - NSVf header indicates '.$info['nsv']['NSVf']['file_size'].' bytes, file actually '.$info['avdataend'].' bytes';
0159     }
0160 
0161     $info['nsv']['NSVf']['playtime_ms']   = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
0162     $offset += 4;
0163     $info['nsv']['NSVf']['meta_size']     = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
0164     $offset += 4;
0165     $info['nsv']['NSVf']['TOC_entries_1'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
0166     $offset += 4;
0167     $info['nsv']['NSVf']['TOC_entries_2'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
0168     $offset += 4;
0169 
0170     if ($info['nsv']['NSVf']['playtime_ms'] == 0) {
0171       $info['error'][] = 'Corrupt NSV file: NSVf.playtime_ms == zero';
0172       return false;
0173     }
0174 
0175     $NSVfheader .= $this->fread($info['nsv']['NSVf']['meta_size'] + (4 * $info['nsv']['NSVf']['TOC_entries_1']) + (4 * $info['nsv']['NSVf']['TOC_entries_2']));
0176     $NSVfheaderlength = strlen($NSVfheader);
0177     $info['nsv']['NSVf']['metadata']      =                  substr($NSVfheader, $offset, $info['nsv']['NSVf']['meta_size']);
0178     $offset += $info['nsv']['NSVf']['meta_size'];
0179 
0180     if ($getTOCoffsets) {
0181       $TOCcounter = 0;
0182       while ($TOCcounter < $info['nsv']['NSVf']['TOC_entries_1']) {
0183         if ($TOCcounter < $info['nsv']['NSVf']['TOC_entries_1']) {
0184           $info['nsv']['NSVf']['TOC_1'][$TOCcounter] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
0185           $offset += 4;
0186           $TOCcounter++;
0187         }
0188       }
0189     }
0190 
0191     if (trim($info['nsv']['NSVf']['metadata']) != '') {
0192       $info['nsv']['NSVf']['metadata'] = str_replace('`', "\x01", $info['nsv']['NSVf']['metadata']);
0193       $CommentPairArray = explode("\x01".' ', $info['nsv']['NSVf']['metadata']);
0194       foreach ($CommentPairArray as $CommentPair) {
0195         if (strstr($CommentPair, '='."\x01")) {
0196           list($key, $value) = explode('='."\x01", $CommentPair, 2);
0197           $info['nsv']['comments'][strtolower($key)][] = trim(str_replace("\x01", '', $value));
0198         }
0199       }
0200     }
0201 
0202     $info['playtime_seconds'] = $info['nsv']['NSVf']['playtime_ms'] / 1000;
0203     $info['bitrate']          = ($info['nsv']['NSVf']['file_size'] * 8) / $info['playtime_seconds'];
0204 
0205     return true;
0206   }
0207 
0208 
0209   public static function NSVframerateLookup($framerateindex) {
0210     if ($framerateindex <= 127) {
0211       return (float) $framerateindex;
0212     }
0213     static $NSVframerateLookup = array();
0214     if (empty($NSVframerateLookup)) {
0215       $NSVframerateLookup[129] = (float) 29.970;
0216       $NSVframerateLookup[131] = (float) 23.976;
0217       $NSVframerateLookup[133] = (float) 14.985;
0218       $NSVframerateLookup[197] = (float) 59.940;
0219       $NSVframerateLookup[199] = (float) 47.952;
0220     }
0221     return (isset($NSVframerateLookup[$framerateindex]) ? $NSVframerateLookup[$framerateindex] : false);
0222   }
0223 
0224 }