r24807 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r24806‎ | r24807 | r24808 >
Date:10:23, 15 August 2007
Author:tstarling
Status:old
Tags:
Comment:
* Major changes to the container parser. Fixed page splitting bug.
* Added overall length calculation for multiplexed streams, fixed a length calculation bug
* Fixed vendor='' case, seen in ffmpeg output (which was broken in other ways anyway)
Modified paths:
  • /trunk/extensions/OggHandler/PEAR/File_Ogg/File/Ogg.php (modified) (history)
  • /trunk/extensions/OggHandler/PEAR/File_Ogg/File/Ogg/Bitstream.php (modified) (history)
  • /trunk/extensions/OggHandler/PEAR/File_Ogg/File/Ogg/Media.php (modified) (history)

Diff [purge]

Index: trunk/extensions/OggHandler/PEAR/File_Ogg/File/Ogg.php
@@ -156,6 +156,16 @@
157157 var $_streams = array();
158158
159159 /**
 160+ * Length in seconds of each stream group
 161+ */
 162+ var $_groupLengths = array();
 163+
 164+ /**
 165+ * Total length in seconds of the entire file
 166+ */
 167+ var $_totalLength;
 168+
 169+ /**
160170 * Returns an interface to an Ogg physical stream.
161171 *
162172 * This method takes the path to a local file and examines it for a physical
@@ -335,17 +345,17 @@
336346 /**
337347 * @access private
338348 */
339 - function _decodePageHeader($pageData, $pageOffset, $pageFinish)
 349+ function _decodePageHeader($pageData, $pageOffset, $groupId)
340350 {
341351 // Extract the various bits and pieces found in each packet header.
342352 if (substr($pageData, 0, 4) != OGG_CAPTURE_PATTERN)
343353 return (false);
344354
345 - $stream_version = unpack("c1data", substr($pageData, 4, 1));
 355+ $stream_version = unpack("C1data", substr($pageData, 4, 1));
346356 if ($stream_version['data'] != 0x00)
347 - return (falses);
 357+ return (false);
348358
349 - $header_flag = unpack("cdata", substr($pageData, 5, 1));
 359+ $header_flag = unpack("Cdata", substr($pageData, 5, 1));
350360
351361 // Exact granule position
352362 $abs_granule_pos = self::_littleEndianBin2Hex( substr($pageData, 6, 8));
@@ -357,24 +367,29 @@
358368 $stream_serial = unpack("Vdata", substr($pageData, 14, 4));
359369 $page_sequence = unpack("Vdata", substr($pageData, 18, 4));
360370 $checksum = unpack("Vdata", substr($pageData, 22, 4));
361 - $page_segments = unpack("cdata", substr($pageData, 26, 1));
 371+ $page_segments = unpack("Cdata", substr($pageData, 26, 1));
362372 $segments_total = 0;
363373 for ($i = 0; $i < $page_segments['data']; ++$i) {
364 - $segments = unpack("Cdata", substr($pageData, 26 + ($i + 1), 1));
365 - $segments_total += $segments['data'];
 374+ $segment_length = unpack("Cdata", substr($pageData, 26 + ($i + 1), 1));
 375+ $segments_total += $segment_length['data'];
366376 }
367 - $this->_streamList[$stream_serial['data']]['stream_page'][$page_sequence['data']]['stream_version'] = $stream_version['data'];
368 - $this->_streamList[$stream_serial['data']]['stream_page'][$page_sequence['data']]['header_flag'] = $header_flag['data'];
369 - $this->_streamList[$stream_serial['data']]['stream_page'][$page_sequence['data']]['abs_granule_pos'] = $abs_granule_pos;
370 - $this->_streamList[$stream_serial['data']]['stream_page'][$page_sequence['data']]['approx_granule_pos'] = $approx_granule_pos;
371 - $this->_streamList[$stream_serial['data']]['stream_page'][$page_sequence['data']]['checksum'] = sprintf("%u", $checksum['data']);
372 - $this->_streamList[$stream_serial['data']]['stream_page'][$page_sequence['data']]['segments'] = $segments_total;
373 - $this->_streamList[$stream_serial['data']]['stream_page'][$page_sequence['data']]['head_offset'] = $pageOffset;
374 - $this->_streamList[$stream_serial['data']]['stream_page'][$page_sequence['data']]['body_offset'] = $pageOffset + 26 + $page_segments['data'] + 1;
375 - $this->_streamList[$stream_serial['data']]['stream_page'][$page_sequence['data']]['body_finish'] = $pageFinish;
376 - $this->_streamList[$stream_serial['data']]['stream_page'][$page_sequence['data']]['data_length'] = $pageFinish - $pageOffset;
377 -
378 - return (true);
 377+ $pageFinish = $pageOffset + 27 + $page_segments['data'] + $segments_total;
 378+ $page = array(
 379+ 'stream_version' => $stream_version['data'],
 380+ 'header_flag' => $header_flag['data'],
 381+ 'abs_granule_pos' => $abs_granule_pos,
 382+ 'approx_granule_pos' => $approx_granule_pos,
 383+ 'checksum' => sprintf("%u", $checksum['data']),
 384+ 'segments' => $page_segments['data'],
 385+ 'head_offset' => $pageOffset,
 386+ 'body_offset' => $pageOffset + 27 + $page_segments['data'],
 387+ 'body_finish' => $pageFinish,
 388+ 'data_length' => $pageFinish - $pageOffset,
 389+ 'group' => $groupId,
 390+ );
 391+
 392+ $this->_streamList[$stream_serial['data']]['stream_page'][$page_sequence['data']] = $page;
 393+ return $page;
379394 }
380395
381396 /**
@@ -383,64 +398,70 @@
384399 function _splitStreams()
385400 {
386401 // Loop through the physical stream until there are no more pages to read.
387 - while (true) {
388 - $this_page_offset = ftell($this->_filePointer);
389 - $next_page_offset = $this_page_offset;
390 -
391 - // Read in 65311 bytes from the physical stream. Ogg documentation
392 - // states that a page has a maximum size of 65307 bytes. An extra
393 - // 4 bytes are added to ensure that the capture pattern of the next
394 - // pages comes through.
395 - if (! ($stream_data = fread($this->_filePointer, OGG_MAXIMUM_PAGE_SIZE)))
 402+ $groupId = 0;
 403+ $openStreams = 0;
 404+ $this_page_offset = 0;
 405+ while (!feof($this->_filePointer)) {
 406+ $pageData = fread($this->_filePointer, 282);
 407+ if (strval($pageData) === '') {
396408 break;
 409+ }
 410+ $page = $this->_decodePageHeader($pageData, $this_page_offset, $groupId);
 411+ if ($page === false) {
 412+ throw new PEAR_Exception("Cannot decode Ogg file: Invalid page at offset $this_page_offset", OGG_ERROR_UNDECODABLE);
 413+ }
397414
398 - // Split the data into various pages.
399 - $stream_pages = explode(OGG_CAPTURE_PATTERN, $stream_data);
400 - // If the maximum data has been read, it is likely that this is an
401 - // intermediate page. Since the split adds an empty element at the
402 - // start of the array, we must account for that by substracting one
403 - // iteration from the loop. This argument also follows if the data
404 - // includes an incomplete page at the end, in which case we substract
405 - // two iterations from the loop.
406 - $number_pages = (strlen($stream_data) == OGG_MAXIMUM_PAGE_SIZE) ? count($stream_pages) - 2 : count($stream_pages) - 1;
407 - if (! count($stream_pages))
408 - break;
409 - if ($number_pages <= 0) {
410 - // Don't go into an infinite loop
411 - throw new PEAR_Exception('No pages found', OGG_ERROR_UNDECODABLE);
 415+ // Keep track of multiplexed groups
 416+ if ($page['header_flag'] & 2/*bos*/) {
 417+ $openStreams++;
 418+ } elseif ($page['header_flag'] & 4/*eos*/) {
 419+ $openStreams--;
 420+ if (!$openStreams) {
 421+ // End of group
 422+ $groupId++;
 423+ }
412424 }
 425+ if ($openStreams < 0) {
 426+ throw new PEAR_Exception("Unexpected end of stream", OGG_ERROR_UNDECODABLE);
 427+ }
413428
414 - for ($i = 1; $i <= $number_pages; ++$i) {
415 - $stream_pages[$i] = OGG_CAPTURE_PATTERN . $stream_pages[$i];
416 - // Set the current page offset to the next page offset of the
417 - // previous loop iteration.
418 - $this_page_offset = $next_page_offset;
419 - // Set the next page offset to the current page offset plus the
420 - // length of the current page.
421 - $next_page_offset += strlen($stream_pages[$i]);
422 - $this->_decodePageHeader($stream_pages[$i], $this_page_offset, $next_page_offset - 1);
423 - }
424 - fseek($this->_filePointer, $next_page_offset, SEEK_SET);
 429+ $this_page_offset = $page['body_finish'];
 430+ fseek($this->_filePointer, $this_page_offset, SEEK_SET);
425431 }
426432 // Loop through the streams, and find out what type of stream is available.
 433+ $groupLengths = array();
427434 foreach ($this->_streamList as $stream_serial => $pages) {
428435 fseek($this->_filePointer, $pages['stream_page'][0]['body_offset'], SEEK_SET);
429436 $pattern = fread($this->_filePointer, 8);
430437 if (preg_match("/" . OGG_STREAM_CAPTURE_VORBIS . "/", $pattern)) {
431438 $this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_VORBIS;
432 - $this->_streams[$stream_serial] =& new File_Ogg_Vorbis($stream_serial, $this->_streamList[$stream_serial]['stream_page'], $this->_filePointer);
 439+ $stream = new File_Ogg_Vorbis($stream_serial, $pages['stream_page'], $this->_filePointer);
433440 } elseif (preg_match("/" . OGG_STREAM_CAPTURE_SPEEX . "/", $pattern)) {
434441 $this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_SPEEX;
435 - $this->_streams[$stream_serial] =& new File_Ogg_Speex($stream_serial, $this->_streamList[$stream_serial]['stream_page'], $this->_filePointer);
 442+ $stream = new File_Ogg_Speex($stream_serial, $pages['stream_page'], $this->_filePointer);
436443 } elseif (preg_match("/" . OGG_STREAM_CAPTURE_FLAC . "/", $pattern)) {
437444 $this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_FLAC;
438 - $this->_streams[$stream_serial] =& new File_Ogg_Flac($stream_serial, $this->_streamList[$stream_serial]['stream_page'], $this->_filePointer);
 445+ $stream = new File_Ogg_Flac($stream_serial, $pages['stream_page'], $this->_filePointer);
439446 } elseif (preg_match("/" . OGG_STREAM_CAPTURE_THEORA . "/", $pattern)) {
440447 $this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_THEORA;
441 - $this->_streams[$stream_serial] =& new File_Ogg_Theora($stream_serial, $this->_streamList[$stream_serial]['stream_page'], $this->_filePointer);
442 - } else
443 - $this->_streamList[$stream_serial]['stream_type'] = "unknown";
 448+ $stream = new File_Ogg_Theora($stream_serial, $pages['stream_page'], $this->_filePointer);
 449+ } else {
 450+ $pages['stream_type'] = "unknown";
 451+ $stream = false;
 452+ }
 453+
 454+ if ($stream) {
 455+ $this->_streams[$stream_serial] = $stream;
 456+ $group = $pages['stream_page'][0]['group'];
 457+ if (isset($groupLengths[$group])) {
 458+ $groupLengths[$group] = max($groupLengths[$group], $stream->getLength());
 459+ } else {
 460+ $groupLengths[$group] = $stream->getLength();
 461+ }
 462+ }
444463 }
 464+ $this->_groupLengths = $groupLengths;
 465+ $this->_totalLength = array_sum( $groupLengths );
445466 unset($this->_streamList);
446467 }
447468
@@ -549,5 +570,12 @@
550571 else
551572 return array();
552573 }
 574+
 575+ /**
 576+ * Get the total length of the group of streams
 577+ */
 578+ function getLength() {
 579+ return $this->_totalLength;
 580+ }
553581 }
554582 ?>
Index: trunk/extensions/OggHandler/PEAR/File_Ogg/File/Ogg/Bitstream.php
@@ -81,8 +81,12 @@
8282 // This gives an accuracy of approximately 99.7% to the streamsize of ogginfo.
8383 foreach ( $streamData as $packet ) {
8484 $this->_streamSize += $packet['data_length'];
85 - $this->_lastGranulePos = max($this->_lastGranulePos, $packet['abs_granule_pos']);
 85+ # Reject -1 as a granule pos, that means no segment finished in the packet
 86+ if ( $packet['abs_granule_pos'] != 'ffffffffffffffff' ) {
 87+ $this->_lastGranulePos = max($this->_lastGranulePos, $packet['abs_granule_pos']);
 88+ }
8689 }
 90+ $this->_group = $streamData[0]['group'];
8791 }
8892
8993 /**
@@ -114,6 +118,14 @@
115119 return ($this->_streamSize);
116120 }
117121
 122+ /**
 123+ * Get the multiplexed group ID
 124+ */
 125+ function getGroup()
 126+ {
 127+ return $this->_group;
 128+ }
 129+
118130 }
119131
120132 ?>
Index: trunk/extensions/OggHandler/PEAR/File_Ogg/File/Ogg/Media.php
@@ -104,8 +104,12 @@
105105 {
106106 // Decode the vendor string length as a 32-bit unsigned integer.
107107 $vendor_len = unpack("Vdata", fread($this->_filePointer, 4));
108 - // Retrieve the vendor string from the stream.
109 - $this->_vendor = fread($this->_filePointer, $vendor_len['data']);
 108+ if ( $vendor_len['data'] > 0 ) {
 109+ // Retrieve the vendor string from the stream.
 110+ $this->_vendor = fread($this->_filePointer, $vendor_len['data']);
 111+ } else {
 112+ $this->_vendor = '';
 113+ }
110114 // Decode the size of the comments list as a 32-bit unsigned integer.
111115 $comment_list_length = unpack("Vdata", fread($this->_filePointer, 4));
112116 // Iterate through the comments list.
@@ -177,31 +181,6 @@
178182 }
179183
180184 /**
181 - * Set a field for this stream's meta description.
182 - *
183 - * @access public
184 - * @param string $field
185 - * @param mixed $value
186 - * @param boolean $replace
187 - */
188 - function setField($field, $value, $replace = true)
189 - {
190 - if (strpos($field, "=") !== false)
191 - return PEAR::raiseError("Comments must not contain equals signs.", OGG_VORBIS_ERROR_ILLEGAL_COMMENT);
192 - if (strpos($value, "=") !== false)
193 - return PEAR::raiseError("Comments must not contain equals signs.", OGG_VORBIS_ERROR_ILLEGAL_COMMENT);
194 -
195 - if ($replace || ! isset($this->_comments[$field])) {
196 - $this->_comments[$field] = $value;
197 - } else {
198 - if (is_array($this->_comments[$field]))
199 - $this->_comments[$field][] = $value;
200 - else
201 - $this->_comments[$field] = array($this->_comments[$field], $value);
202 - }
203 - }
204 -
205 - /**
206185 * Get the entire comments array.
207186 * May return an empty array if the bitstream does not support comments.
208187 *

Status & tagging log