summaryrefslogtreecommitdiff
path: root/videodata.php
diff options
context:
space:
mode:
authorccd0 <admin@containerchan.org>2013-11-09 01:11:22 -0800
committerccd0 <admin@containerchan.org>2013-11-09 01:11:22 -0800
commit674b2e8f1a8b3659d91e2e6e5a337e3134d757a3 (patch)
tree873dfec7620f6881ade44f64ea79cde359dea0c5 /videodata.php
parent705af14064f71c4709a54276e3f34d56cfe4b32c (diff)
add files
Diffstat (limited to 'videodata.php')
-rw-r--r--videodata.php131
1 files changed, 131 insertions, 0 deletions
diff --git a/videodata.php b/videodata.php
new file mode 100644
index 00000000..3fe9eeee
--- /dev/null
+++ b/videodata.php
@@ -0,0 +1,131 @@
+<?php
+require dirname(__FILE__) . '/matroska.php';
+
+// Header for single VPx keyframe
+function vpxFrameHeader($size, $width, $height, $codecID) {
+ return "\x1A\x45\xDF\xA3\x9F\x42\x86\x81\x01\x42\xF7\x81\x01\x42\xF2\x81"
+ . "\x04\x42\xF3\x81\x08\x42\x82\x84\x77\x65\x62\x6D\x42\x87\x81\x02"
+ . "\x42\x85\x81\x02\x18\x53\x80\x67\x08" . pack('N', $size + 173) . "\x11\x4D\x9B"
+ . "\x74\xB8\x4D\xBB\x8B\x53\xAB\x84\x15\x49\xA9\x66\x53\xAC\x81\x3D"
+ . "\x4D\xBB\x8B\x53\xAB\x84\x16\x54\xAE\x6B\x53\xAC\x81\x58\x4D\xBB"
+ . "\x8B\x53\xAB\x84\x1C\x53\xBB\x6B\x53\xAC\x81\x85\x4D\xBB\x8B\x53"
+ . "\xAB\x84\x1F\x43\xB6\x75\x53\xAC\x81\x97\x15\x49\xA9\x66\x96\x2A"
+ . "\xD7\xB1\x83\x0F\x42\x40\x44\x89\x84\x41\x20\x00\x00\x4D\x80\x81"
+ . "\x66\x57\x41\x81\x66\x16\x54\xAE\x6B\xA8\xAE\xA6\xD7\x81\x01\x73"
+ . "\xC5\x81\x01\x83\x81\x01\x23\xE3\x83\x83\x98\x96\x80\x86\x85" . $codecID
+ . "\xE0\x8C\xB0\x84" . pack('N', $width) . "\xBA\x84" . pack('N', $height)
+ . "\x1C\x53\xBB\x6B\x8D\xBB\x8B\xB3\x81\x00\xB7\x86\xF7\x81"
+ . "\x01\xF1\x81\x97\x1F\x43\xB6\x75\x08" . pack('N', $size + 13) . "\xE7\x81\x00"
+ . "\xA3\x08" . pack('N', $size + 4) . "\x81\x00\x00\x80";
+}
+
+// Locate first VPx keyframe of track $trackNumber after timecode $skip
+function firstVPxFrame($segment, $trackNumber, $skip=0) {
+ foreach($segment as $x1) {
+ if ($x1->name() == 'Cluster') {
+ $cluserTimecode = $x1->Get('Timecode');
+ foreach($x1 as $x2) {
+ $blockRaw = NULL;
+ if ($x2->name() == 'SimpleBlock') {
+ $blockRaw = $x2->value();
+ } elseif ($x2->name() == 'BlockGroup') {
+ $blockRaw = $x2->get('Block');
+ }
+ if (isset($blockRaw)) {
+ $block = new MatroskaBlock($blockRaw);
+ if ($block->trackNumber == $trackNumber) {
+ $frame = $block->frames[0];
+ if ($block->keyframe) {
+ if (!isset($cluserTimecode) || $cluserTimecode + $block->timecode >= $skip) {
+ return $frame;
+ } elseif (!isset($frame1)) {
+ $frame1 = $frame;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ 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);
+
+ // 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)) {
+ $pixelWidth = $videoAttr->get('PixelWidth');
+ $pixelHeight = $videoAttr->get('PixelHeight');
+ if ($pixelWidth == 0 || $pixelHeight == 0) {
+ error_log('bad PixelWidth/PixelHeight');
+ $pixelWidth = NULL;
+ $pixelHeight = NULL;
+ }
+ $data['width'] = $videoAttr->get('DisplayWidth', $pixelWidth);
+ $data['height'] = $videoAttr->get('DisplayHeight', $pixelHeight);
+ if ($data['width'] == 0 || $data['height'] == 0) {
+ error_log('bad DisplayWidth/DisplayHeight');
+ $data['width'] = $pixelWidth;
+ $data['height'] = $pixelHeight;
+ }
+ }
+
+ // Extract frame to use as thumbnail
+ $trackNumber = $videoTrack->get('TrackNumber');
+ if (!isset($trackNumber)) throw new Exception('missing track number');
+ $codecID = $videoTrack->get('CodecID');
+ if ($codecID != 'V_VP8' && $codecID != 'V_VP9') throw new Exception('codec is not VP8 or VP9');
+ if (!isset($pixelWidth) || !isset($pixelHeight)) throw new Exception('no width or height');
+ if (isset($data['duration']) && $data['duration'] >= 5) {
+ $skip = 1e9 / $timecodeScale;
+ } else {
+ $skip = 0;
+ }
+ $frame = firstVPxFrame($segment, $trackNumber, $skip);
+ if (!isset($frame)) throw new Exception('no keyframes');
+ $data['frame'] = vpxFrameHeader($frame->size(), $pixelWidth, $pixelHeight, $codecID) . $frame->readAll();
+
+ } catch (Exception $e) {
+ error_log($e->getMessage());
+ }
+
+ fclose($fileHandle);
+ return $data;
+}