wp/wp-includes/ID3/module.audio.ogg.php
changeset 16 a86126ab1dd4
parent 7 cf61fcea0001
child 19 3d72ae0968f4
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
     1 <?php
     1 <?php
       
     2 
     2 /////////////////////////////////////////////////////////////////
     3 /////////////////////////////////////////////////////////////////
     3 /// getID3() by James Heinrich <info@getid3.org>               //
     4 /// getID3() by James Heinrich <info@getid3.org>               //
     4 //  available at http://getid3.sourceforge.net                 //
     5 //  available at https://github.com/JamesHeinrich/getID3       //
     5 //            or http://www.getid3.org                         //
     6 //            or https://www.getid3.org                        //
     6 //          also https://github.com/JamesHeinrich/getID3       //
     7 //            or http://getid3.sourceforge.net                 //
     7 /////////////////////////////////////////////////////////////////
     8 //  see readme.txt for more details                            //
     8 // See readme.txt for more details                             //
       
     9 /////////////////////////////////////////////////////////////////
     9 /////////////////////////////////////////////////////////////////
    10 //                                                             //
    10 //                                                             //
    11 // module.audio.ogg.php                                        //
    11 // module.audio.ogg.php                                        //
    12 // module for analyzing Ogg Vorbis, OggFLAC and Speex files    //
    12 // module for analyzing Ogg Vorbis, OggFLAC and Speex files    //
    13 // dependencies: module.audio.flac.php                         //
    13 // dependencies: module.audio.flac.php                         //
    14 //                                                            ///
    14 //                                                            ///
    15 /////////////////////////////////////////////////////////////////
    15 /////////////////////////////////////////////////////////////////
    16 
    16 
       
    17 if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
       
    18 	exit;
       
    19 }
    17 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true);
    20 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true);
    18 
    21 
    19 class getid3_ogg extends getid3_handler
    22 class getid3_ogg extends getid3_handler
    20 {
    23 {
    21 	// http://xiph.org/vorbis/doc/Vorbis_I_spec.html
    24 	/**
       
    25 	 * @link http://xiph.org/vorbis/doc/Vorbis_I_spec.html
       
    26 	 *
       
    27 	 * @return bool
       
    28 	 */
    22 	public function Analyze() {
    29 	public function Analyze() {
    23 		$info = &$this->getid3->info;
    30 		$info = &$this->getid3->info;
    24 
    31 
    25 		$info['fileformat'] = 'ogg';
    32 		$info['fileformat'] = 'ogg';
    26 
    33 
    63 
    70 
    64 			$this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
    71 			$this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
    65 
    72 
    66 		} elseif (substr($filedata, 0, 8) == 'OpusHead') {
    73 		} elseif (substr($filedata, 0, 8) == 'OpusHead') {
    67 
    74 
    68 			if( $this->ParseOpusPageHeader($filedata, $filedataoffset, $oggpageinfo) == false ) {
    75 			if ($this->ParseOpusPageHeader($filedata, $filedataoffset, $oggpageinfo) === false) {
    69 				return false;
    76 				return false;
    70 			}
    77 			}
    71 
    78 
    72 		} elseif (substr($filedata, 0, 8) == 'Speex   ') {
    79 		} elseif (substr($filedata, 0, 8) == 'Speex   ') {
    73 
    80 
   177 				$info['video']['frame_rate'] = (float) $info['ogg']['pageheader']['theora']['frame_rate_numerator'] / $info['ogg']['pageheader']['theora']['frame_rate_denominator'];
   184 				$info['video']['frame_rate'] = (float) $info['ogg']['pageheader']['theora']['frame_rate_numerator'] / $info['ogg']['pageheader']['theora']['frame_rate_denominator'];
   178 			}
   185 			}
   179 			if ($info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] > 0) {
   186 			if ($info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] > 0) {
   180 				$info['video']['pixel_aspect_ratio'] = (float) $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] / $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'];
   187 				$info['video']['pixel_aspect_ratio'] = (float) $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] / $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'];
   181 			}
   188 			}
   182 $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().'] -- bitrate, playtime and all audio data are currently unavailable');
   189 			$this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().'] -- bitrate, playtime and all audio data are currently unavailable');
   183 
   190 
   184 
   191 
   185 		} elseif (substr($filedata, 0, 8) == "fishead\x00") {
   192 		} elseif (substr($filedata, 0, 8) == "fishead\x00") {
   186 
   193 
   187 			// Ogg Skeleton version 3.0 Format Specification
   194 			// Ogg Skeleton version 3.0 Format Specification
   257 			$this->fseek($oggpageinfo['page_start_offset']);
   264 			$this->fseek($oggpageinfo['page_start_offset']);
   258 
   265 
   259 			$this->error('Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().']');
   266 			$this->error('Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().']');
   260 			//return false;
   267 			//return false;
   261 
   268 
       
   269 		} elseif (substr($filedata, 0, 5) == "\x7F".'FLAC') {
       
   270 			// https://xiph.org/flac/ogg_mapping.html
       
   271 
       
   272 			$info['audio']['dataformat']   = 'flac';
       
   273 			$info['audio']['bitrate_mode'] = 'vbr';
       
   274 			$info['audio']['lossless']     = true;
       
   275 
       
   276 			$info['ogg']['flac']['header']['version_major']  =                         ord(substr($filedata,  5, 1));
       
   277 			$info['ogg']['flac']['header']['version_minor']  =                         ord(substr($filedata,  6, 1));
       
   278 			$info['ogg']['flac']['header']['header_packets'] =   getid3_lib::BigEndian2Int(substr($filedata,  7, 2)) + 1; // "A two-byte, big-endian binary number signifying the number of header (non-audio) packets, not including this one. This number may be zero (0x0000) to signify 'unknown' but be aware that some decoders may not be able to handle such streams."
       
   279 			$info['ogg']['flac']['header']['magic']          =                             substr($filedata,  9, 4);
       
   280 			if ($info['ogg']['flac']['header']['magic'] != 'fLaC') {
       
   281 				$this->error('Ogg-FLAC expecting "fLaC", found "'.$info['ogg']['flac']['header']['magic'].'" ('.trim(getid3_lib::PrintHexBytes($info['ogg']['flac']['header']['magic'])).')');
       
   282 				return false;
       
   283 			}
       
   284 			$info['ogg']['flac']['header']['STREAMINFO_bytes'] = getid3_lib::BigEndian2Int(substr($filedata, 13, 4));
       
   285 			$info['flac']['STREAMINFO'] = getid3_flac::parseSTREAMINFOdata(substr($filedata, 17, 34));
       
   286 			if (!empty($info['flac']['STREAMINFO']['sample_rate'])) {
       
   287 				$info['audio']['bitrate_mode']    = 'vbr';
       
   288 				$info['audio']['sample_rate']     = $info['flac']['STREAMINFO']['sample_rate'];
       
   289 				$info['audio']['channels']        = $info['flac']['STREAMINFO']['channels'];
       
   290 				$info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample'];
       
   291 				$info['playtime_seconds']         = $info['flac']['STREAMINFO']['samples_stream'] / $info['flac']['STREAMINFO']['sample_rate'];
       
   292 			}
       
   293 
   262 		} else {
   294 		} else {
   263 
   295 
   264 			$this->error('Expecting either "Speex   ", "OpusHead" or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"');
   296 			$this->error('Expecting one of "vorbis", "Speex", "OpusHead", "vorbis", "fishhead", "theora", "fLaC" identifier strings, found "'.substr($filedata, 0, 8).'"');
   265 			unset($info['ogg']);
   297 			unset($info['ogg']);
   266 			unset($info['mime_type']);
   298 			unset($info['mime_type']);
   267 			return false;
   299 			return false;
   268 
   300 
   269 		}
   301 		}
   376 		}
   408 		}
   377 
   409 
   378 		return true;
   410 		return true;
   379 	}
   411 	}
   380 
   412 
       
   413 	/**
       
   414 	 * @param string $filedata
       
   415 	 * @param int    $filedataoffset
       
   416 	 * @param array  $oggpageinfo
       
   417 	 *
       
   418 	 * @return bool
       
   419 	 */
   381 	public function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
   420 	public function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
   382 		$info = &$this->getid3->info;
   421 		$info = &$this->getid3->info;
   383 		$info['audio']['dataformat'] = 'vorbis';
   422 		$info['audio']['dataformat'] = 'vorbis';
   384 		$info['audio']['lossless']   = false;
   423 		$info['audio']['lossless']   = false;
   385 
   424 
   424 			$info['audio']['bitrate_mode'] = 'abr';
   463 			$info['audio']['bitrate_mode'] = 'abr';
   425 		}
   464 		}
   426 		return true;
   465 		return true;
   427 	}
   466 	}
   428 
   467 
   429 	// http://tools.ietf.org/html/draft-ietf-codec-oggopus-03
   468 	/**
       
   469 	 * @link http://tools.ietf.org/html/draft-ietf-codec-oggopus-03
       
   470 	 *
       
   471 	 * @param string $filedata
       
   472 	 * @param int    $filedataoffset
       
   473 	 * @param array  $oggpageinfo
       
   474 	 *
       
   475 	 * @return bool
       
   476 	 */
   430 	public function ParseOpusPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
   477 	public function ParseOpusPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
   431 		$info = &$this->getid3->info;
   478 		$info = &$this->getid3->info;
   432 		$info['audio']['dataformat']   = 'opus';
   479 		$info['audio']['dataformat']   = 'opus';
   433 		$info['mime_type']             = 'audio/ogg; codecs=opus';
   480 		$info['mime_type']             = 'audio/ogg; codecs=opus';
   434 
   481 
   456 		}
   503 		}
   457 
   504 
   458 		$info['ogg']['pageheader']['opus']['pre_skip'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  2));
   505 		$info['ogg']['pageheader']['opus']['pre_skip'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  2));
   459 		$filedataoffset += 2;
   506 		$filedataoffset += 2;
   460 
   507 
   461 		$info['ogg']['pageheader']['opus']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  4));
   508 		$info['ogg']['pageheader']['opus']['input_sample_rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  4));
   462 		$filedataoffset += 4;
   509 		$filedataoffset += 4;
   463 
   510 
   464 		//$info['ogg']['pageheader']['opus']['output_gain'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  2));
   511 		//$info['ogg']['pageheader']['opus']['output_gain'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  2));
   465 		//$filedataoffset += 2;
   512 		//$filedataoffset += 2;
   466 
   513 
   467 		//$info['ogg']['pageheader']['opus']['channel_mapping_family'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  1));
   514 		//$info['ogg']['pageheader']['opus']['channel_mapping_family'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  1));
   468 		//$filedataoffset += 1;
   515 		//$filedataoffset += 1;
   469 
   516 
   470 		$info['opus']['opus_version']      = $info['ogg']['pageheader']['opus']['version'];
   517 		$info['opus']['opus_version']       = $info['ogg']['pageheader']['opus']['version'];
   471 		$info['opus']['sample_rate']       = $info['ogg']['pageheader']['opus']['sample_rate'];
   518 		$info['opus']['sample_rate_input']  = $info['ogg']['pageheader']['opus']['input_sample_rate'];
   472 		$info['opus']['out_channel_count'] = $info['ogg']['pageheader']['opus']['out_channel_count'];
   519 		$info['opus']['out_channel_count']  = $info['ogg']['pageheader']['opus']['out_channel_count'];
   473 
   520 
   474 		$info['audio']['channels']      = $info['opus']['out_channel_count'];
   521 		$info['audio']['channels']          = $info['opus']['out_channel_count'];
   475 		$info['audio']['sample_rate']   = $info['opus']['sample_rate'];
   522 		$info['audio']['sample_rate_input'] = $info['opus']['sample_rate_input'];
       
   523 		$info['audio']['sample_rate']       = 48000; // "All Opus audio is coded at 48 kHz, and should also be decoded at 48 kHz for playback (unless the target hardware does not support this sampling rate). However, this field may be used to resample the audio back to the original sampling rate, for example, when saving the output to a file." -- https://mf4.xiph.org/jenkins/view/opus/job/opusfile-unix/ws/doc/html/structOpusHead.html
   476 		return true;
   524 		return true;
   477 	}
   525 	}
   478 
   526 
   479 
   527 	/**
       
   528 	 * @return array|false
       
   529 	 */
   480 	public function ParseOggPageHeader() {
   530 	public function ParseOggPageHeader() {
   481 		// http://xiph.org/ogg/vorbis/doc/framing.html
   531 		// http://xiph.org/ogg/vorbis/doc/framing.html
   482 		$oggheader['page_start_offset'] = $this->ftell(); // where we started from in the file
   532 		$oggheader['page_start_offset'] = $this->ftell(); // where we started from in the file
   483 
   533 
   484 		$filedata = $this->fread($this->getid3->fread_buffer_size());
   534 		$filedata = $this->fread($this->getid3->fread_buffer_size());
   487 			if (($this->ftell() - $oggheader['page_start_offset']) >= $this->getid3->fread_buffer_size()) {
   537 			if (($this->ftell() - $oggheader['page_start_offset']) >= $this->getid3->fread_buffer_size()) {
   488 				// should be found before here
   538 				// should be found before here
   489 				return false;
   539 				return false;
   490 			}
   540 			}
   491 			if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
   541 			if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
   492 				if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === false)) {
   542 				if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === '')) {
   493 					// get some more data, unless eof, in which case fail
   543 					// get some more data, unless eof, in which case fail
   494 					return false;
   544 					return false;
   495 				}
   545 				}
   496 			}
   546 			}
   497 		}
   547 		}
   526 		$this->fseek($oggheader['header_end_offset']);
   576 		$this->fseek($oggheader['header_end_offset']);
   527 
   577 
   528 		return $oggheader;
   578 		return $oggheader;
   529 	}
   579 	}
   530 
   580 
   531     // http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005
   581 	/**
       
   582 	 * @link http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005
       
   583 	 *
       
   584 	 * @return bool
       
   585 	 */
   532 	public function ParseVorbisComments() {
   586 	public function ParseVorbisComments() {
   533 		$info = &$this->getid3->info;
   587 		$info = &$this->getid3->info;
   534 
   588 
   535 		$OriginalOffset = $this->ftell();
   589 		$OriginalOffset = $this->ftell();
       
   590 		$commentdata = null;
   536 		$commentdataoffset = 0;
   591 		$commentdataoffset = 0;
   537 		$VorbisCommentPage = 1;
   592 		$VorbisCommentPage = 1;
       
   593 		$CommentStartOffset = 0;
   538 
   594 
   539 		switch ($info['audio']['dataformat']) {
   595 		switch ($info['audio']['dataformat']) {
   540 			case 'vorbis':
   596 			case 'vorbis':
   541 			case 'speex':
   597 			case 'speex':
   542 			case 'opus':
   598 			case 'opus':
   560 				$commentdata = $this->fread($info['flac']['VORBIS_COMMENT']['raw']['block_length']);
   616 				$commentdata = $this->fread($info['flac']['VORBIS_COMMENT']['raw']['block_length']);
   561 				break;
   617 				break;
   562 
   618 
   563 			default:
   619 			default:
   564 				return false;
   620 				return false;
   565 				break;
       
   566 		}
   621 		}
   567 
   622 
   568 		$VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
   623 		$VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
   569 		$commentdataoffset += 4;
   624 		$commentdataoffset += 4;
   570 
   625 
   763 		$this->fseek($OriginalOffset);
   818 		$this->fseek($OriginalOffset);
   764 
   819 
   765 		return true;
   820 		return true;
   766 	}
   821 	}
   767 
   822 
       
   823 	/**
       
   824 	 * @param int $mode
       
   825 	 *
       
   826 	 * @return string|null
       
   827 	 */
   768 	public static function SpeexBandModeLookup($mode) {
   828 	public static function SpeexBandModeLookup($mode) {
   769 		static $SpeexBandModeLookup = array();
   829 		static $SpeexBandModeLookup = array();
   770 		if (empty($SpeexBandModeLookup)) {
   830 		if (empty($SpeexBandModeLookup)) {
   771 			$SpeexBandModeLookup[0] = 'narrow';
   831 			$SpeexBandModeLookup[0] = 'narrow';
   772 			$SpeexBandModeLookup[1] = 'wide';
   832 			$SpeexBandModeLookup[1] = 'wide';
   773 			$SpeexBandModeLookup[2] = 'ultra-wide';
   833 			$SpeexBandModeLookup[2] = 'ultra-wide';
   774 		}
   834 		}
   775 		return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
   835 		return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
   776 	}
   836 	}
   777 
   837 
   778 
   838 	/**
       
   839 	 * @param array $OggInfoArray
       
   840 	 * @param int   $SegmentNumber
       
   841 	 *
       
   842 	 * @return int
       
   843 	 */
   779 	public static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
   844 	public static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
       
   845 		$segmentlength = 0;
   780 		for ($i = 0; $i < $SegmentNumber; $i++) {
   846 		for ($i = 0; $i < $SegmentNumber; $i++) {
   781 			$segmentlength = 0;
   847 			$segmentlength = 0;
   782 			foreach ($OggInfoArray['segment_table'] as $key => $value) {
   848 			foreach ($OggInfoArray['segment_table'] as $key => $value) {
   783 				$segmentlength += $value;
   849 				$segmentlength += $value;
   784 				if ($value < 255) {
   850 				if ($value < 255) {
   787 			}
   853 			}
   788 		}
   854 		}
   789 		return $segmentlength;
   855 		return $segmentlength;
   790 	}
   856 	}
   791 
   857 
   792 
   858 	/**
       
   859 	 * @param int $nominal_bitrate
       
   860 	 *
       
   861 	 * @return float
       
   862 	 */
   793 	public static function get_quality_from_nominal_bitrate($nominal_bitrate) {
   863 	public static function get_quality_from_nominal_bitrate($nominal_bitrate) {
   794 
   864 
   795 		// decrease precision
   865 		// decrease precision
   796 		$nominal_bitrate = $nominal_bitrate / 1000;
   866 		$nominal_bitrate = $nominal_bitrate / 1000;
   797 
   867 
   811 		//return $qval; // 5.031324
   881 		//return $qval; // 5.031324
   812 		//return intval($qval); // 5
   882 		//return intval($qval); // 5
   813 		return round($qval, 1); // 5 or 4.9
   883 		return round($qval, 1); // 5 or 4.9
   814 	}
   884 	}
   815 
   885 
       
   886 	/**
       
   887 	 * @param int $colorspace_id
       
   888 	 *
       
   889 	 * @return string|null
       
   890 	 */
   816 	public static function TheoraColorSpace($colorspace_id) {
   891 	public static function TheoraColorSpace($colorspace_id) {
   817 		// http://www.theora.org/doc/Theora.pdf (table 6.3)
   892 		// http://www.theora.org/doc/Theora.pdf (table 6.3)
   818 		static $TheoraColorSpaceLookup = array();
   893 		static $TheoraColorSpaceLookup = array();
   819 		if (empty($TheoraColorSpaceLookup)) {
   894 		if (empty($TheoraColorSpaceLookup)) {
   820 			$TheoraColorSpaceLookup[0] = 'Undefined';
   895 			$TheoraColorSpaceLookup[0] = 'Undefined';
   823 			$TheoraColorSpaceLookup[3] = 'Reserved';
   898 			$TheoraColorSpaceLookup[3] = 'Reserved';
   824 		}
   899 		}
   825 		return (isset($TheoraColorSpaceLookup[$colorspace_id]) ? $TheoraColorSpaceLookup[$colorspace_id] : null);
   900 		return (isset($TheoraColorSpaceLookup[$colorspace_id]) ? $TheoraColorSpaceLookup[$colorspace_id] : null);
   826 	}
   901 	}
   827 
   902 
       
   903 	/**
       
   904 	 * @param int $pixelformat_id
       
   905 	 *
       
   906 	 * @return string|null
       
   907 	 */
   828 	public static function TheoraPixelFormat($pixelformat_id) {
   908 	public static function TheoraPixelFormat($pixelformat_id) {
   829 		// http://www.theora.org/doc/Theora.pdf (table 6.4)
   909 		// http://www.theora.org/doc/Theora.pdf (table 6.4)
   830 		static $TheoraPixelFormatLookup = array();
   910 		static $TheoraPixelFormatLookup = array();
   831 		if (empty($TheoraPixelFormatLookup)) {
   911 		if (empty($TheoraPixelFormatLookup)) {
   832 			$TheoraPixelFormatLookup[0] = '4:2:0';
   912 			$TheoraPixelFormatLookup[0] = '4:2:0';