From 781fde7789e27bf35033bfbea740d56426b4d251 Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 6 Apr 2014 21:21:17 +0200 Subject: move php files to a more sane directory --- inc/lib/webm/videodata.php | 173 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 inc/lib/webm/videodata.php (limited to 'inc/lib/webm/videodata.php') diff --git a/inc/lib/webm/videodata.php b/inc/lib/webm/videodata.php new file mode 100644 index 00000000..d54d484b --- /dev/null +++ b/inc/lib/webm/videodata.php @@ -0,0 +1,173 @@ +get('Video'); + if (isset($videoAttr)) { + if ($videoAttr->get('StereoMode') !== NULL) $version = 3; + if ($videoAttr->get('AlphaMode') !== NULL) $version = 3; + } + if ($videoTrack->get('CodecDelay') !== NULL) $version = 4; + if ($videoTrack->get('SeekPreRoll') !== NULL) $version = 4; + if ($frame->name() == 'BlockGroup' && $frame->get('DiscardPadding') !== NULL) $version = 4; + + // EBML header + $ebml = ebmlEncodeElement('EBML', + ebmlEncodeElement('DocType', "webm") + . ebmlEncodeElement('DocTypeVersion', chr($version)) + . ebmlEncodeElement('DocTypeReadVersion', "\x02") + ); + + // Segment + $info = ebmlEncodeElement('Info', + ebmlEncodeElement('Duration', "\x41\x20\x00\x00") + . ebmlEncodeElement('MuxingApp', 'ccframe') + . ebmlEncodeElement('WritingApp', 'ccframe') + ); + $tracks = ebmlEncodeElement('Tracks', + ebmlEncodeElement('TrackEntry', $videoTrack->content()->readAll()) + ); + $cues = ebmlEncodeElement('Cues', + ebmlEncodeElement('CuePoint', + ebmlEncodeElement('CueTime', "\x00") + . ebmlEncodeElement('CueTrackPositions', + ebmlEncodeElement('CueTrack', pack('N', $videoTrack->get('TrackNumber'))) + . ebmlEncodeElement('CueClusterPosition', pack('N', $lenSeekHead + strlen($info) + strlen($tracks) + $lenCues)) + ) + ) + ); + if (strlen($cues) != $lenCues) throw new Exception('length of Cues element wrong'); + $cluster = ebmlEncodeElement('Cluster', + ebmlEncodeElement('Timecode', "\x00") + . ebmlEncodeElement($frame->name(), $frame->content()->readAll()) + . ebmlEncodeElement('Void', '') + ); + $seekHead = ebmlEncodeElement('SeekHead', + matroskaSeekElement('Info', $lenSeekHead) + . matroskaSeekElement('Tracks', $lenSeekHead + strlen($info)) + . matroskaSeekElement('Cues', $lenSeekHead + strlen($info) + strlen($tracks)) + . matroskaSeekElement('Cluster', $lenSeekHead + strlen($info) + strlen($tracks) + $lenCues) + ); + if (strlen($seekHead) != $lenSeekHead) throw new Exception('length of SeekHead element wrong'); + $segment = ebmlEncodeElement('Segment', $seekHead . $info . $tracks . $cues . $cluster); + + return $ebml . $segment; +} + +// Locate first WebM keyframe of track $trackNumber after timecode $skip +function firstWebMFrame($segment, $trackNumber, $skip=0) { + foreach($segment as $x1) { + if ($x1->name() == 'Cluster') { + $cluserTimecode = $x1->Get('Timecode'); + foreach($x1 as $blockGroup) { + $blockRaw = NULL; + if ($blockGroup->name() == 'SimpleBlock') { + $blockRaw = $blockGroup->value(); + } elseif ($blockGroup->name() == 'BlockGroup') { + $blockRaw = $blockGroup->get('Block'); + } + if (isset($blockRaw)) { + $block = new MatroskaBlock($blockRaw); + if ($block->trackNumber == $trackNumber && $block->keyframe) { + if (!isset($cluserTimecode) || $cluserTimecode + $block->timecode >= $skip) { + return $blockGroup; + } elseif (!isset($frame1)) { + $frame1 = $blockGroup; + } + } + } + } + } + } + return isset($frame1) ? $frame1 : NULL; +} + +function videoData($filename) { + $data = array(); + + // Open file + $fileHandle = fopen($filename, 'rb'); + if (!$fileHandle) { + error_log('could not open file'); + return $data; + } + + try { + $root = readMatroska($fileHandle); + $data['container'] = $root->get('EBML')->get('DocType'); + + // Locate segment information and tracks + $segment = $root->get('Segment'); + if (!isset($segment)) throw new Exception('missing Segment element'); + + // Get segment information + $info = $segment->get('Info'); + if (isset($info)) { + $timecodeScale = $info->get('TimecodeScale'); + $duration = $info->get('Duration'); + if (isset($timecodeScale) && isset($duration)) { + $data['duration'] = 1e-9 * $timecodeScale * $duration; + } + } + + // Locate video track + $tracks = $segment->get('Tracks'); + if (!isset($tracks)) throw new Exception('missing Tracks element'); + foreach($tracks as $trackEntry) { + if ($trackEntry->name() == 'TrackEntry' && $trackEntry->get('TrackType') == 1) { + $videoTrack = $trackEntry; + break; + } + } + if (!isset($videoTrack)) throw new Exception('no video track'); + + // Get track information + $videoAttr = $videoTrack->get('Video'); + if (!isset($videoAttr)) throw new Exception('missing video parameters'); + $pixelWidth = $videoAttr->get('PixelWidth'); + $pixelHeight = $videoAttr->get('PixelHeight'); + if (!isset($pixelWidth) || !isset($pixelHeight)) throw new Exception('no width or height'); + if ($pixelWidth == 0 || $pixelHeight == 0) throw new Exception('bad PixelWidth/PixelHeight'); + $displayWidth = $videoAttr->get('DisplayWidth', $pixelWidth); + $displayHeight = $videoAttr->get('DisplayHeight', $pixelHeight); + if ($displayWidth == 0 || $displayHeight == 0) throw new Exception('bad DisplayWidth/DisplayHeight'); + $data['width'] = $displayWidth; + $data['height'] = $displayHeight; + + // Extract frame to use as thumbnail + if ($videoAttr->get('AlphaMode') != NULL) { + if (!($pixelWidth % 2 == 0 && $pixelHeight % 2 == 0 && $displayWidth % 2 == 0 && $displayHeight % 2 == 0)) { + throw new Exception('preview frame blocked due to Chromium bug'); + } + } + $trackNumber = $videoTrack->get('TrackNumber'); + if (!isset($trackNumber)) throw new Exception('missing track number'); + if (isset($data['duration']) && $data['duration'] >= 5) { + $skip = 1e9 / $timecodeScale; + } else { + $skip = 0; + } + $frame = firstWebMFrame($segment, $trackNumber, $skip); + if (!isset($frame)) throw new Exception('no keyframes'); + $data['frame'] = muxWebMFrame($videoTrack, $frame); + } catch (Exception $e) { + error_log($e->getMessage()); + } + + fclose($fileHandle); + return $data; +} -- cgit v1.3