mirror of
https://github.com/vichan-devel/vichan.git
synced 2024-11-28 01:10:51 +01:00
improved frame extraction (including alpha)
This commit is contained in:
parent
14ac1b562d
commit
dd3cae6f70
@ -35,7 +35,6 @@ And add this to stylesheets/style.css:
|
|||||||
float: left;
|
float: left;
|
||||||
margin: 10px 20px;
|
margin: 10px 20px;
|
||||||
border: none;
|
border: none;
|
||||||
background: #aaa;
|
|
||||||
}
|
}
|
||||||
div.post video.post-image {
|
div.post video.post-image {
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
10
matroska.php
10
matroska.php
@ -497,7 +497,7 @@ function readMatroska($fileHandle) {
|
|||||||
return $root;
|
return $root;
|
||||||
}
|
}
|
||||||
|
|
||||||
function encodeVarInt($n) {
|
function ebmlEncodeVarInt($n) {
|
||||||
$data = '';
|
$data = '';
|
||||||
$flag = 0x80;
|
$flag = 0x80;
|
||||||
while ($n >= $flag) {
|
while ($n >= $flag) {
|
||||||
@ -512,11 +512,11 @@ function encodeVarInt($n) {
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
function encodeElementName($name) {
|
function ebmlEncodeElementName($name) {
|
||||||
global $EBML_ELEMENTS;
|
global $EBML_ELEMENTS;
|
||||||
return encodeVarInt($EBML_ELEMENTS->id($name));
|
return ebmlEncodeVarInt($EBML_ELEMENTS->id($name));
|
||||||
}
|
}
|
||||||
|
|
||||||
function encodeElement($name, $content) {
|
function ebmlEncodeElement($name, $content) {
|
||||||
return encodeElementName($name) . encodeVarInt(strlen($content)) . $content;
|
return ebmlEncodeElementName($name) . ebmlEncodeVarInt(strlen($content)) . $content;
|
||||||
}
|
}
|
||||||
|
122
videodata.php
122
videodata.php
@ -1,76 +1,53 @@
|
|||||||
<?php
|
<?php
|
||||||
require dirname(__FILE__) . '/matroska.php';
|
require dirname(__FILE__) . '/matroska.php';
|
||||||
|
|
||||||
|
function matroskaSeekElement($name, $pos) {
|
||||||
|
return ebmlEncodeElement('Seek',
|
||||||
|
ebmlEncodeElement('SeekID', ebmlEncodeElementName($name))
|
||||||
|
. ebmlEncodeElement('SeekPosition', pack('N', $pos))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Make video from single VPx keyframe
|
// Make video from single VPx keyframe
|
||||||
function muxVPxFrame($width, $height, $codecID, $data) {
|
function muxVPxFrame($trackNumber, $videoTrack, $frame) {
|
||||||
$size = strlen($data);
|
$lenSeekHead = 73;
|
||||||
$lenSeekHead = 61;
|
$lenCues = 24;
|
||||||
$lenCues = 18;
|
$ebml = ebmlEncodeElement('EBML',
|
||||||
$ebml = encodeElement('EBML',
|
ebmlEncodeElement('DocType', "webm")
|
||||||
encodeElement('EBMLVersion', "\x01")
|
. ebmlEncodeElement('DocTypeVersion', "\x02")
|
||||||
. encodeElement('EBMLReadVersion', "\x01")
|
. ebmlEncodeElement('DocTypeReadVersion', "\x02")
|
||||||
. encodeElement('EBMLMaxIDLength', "\x04")
|
|
||||||
. encodeElement('EBMLMaxSizeLength', "\x08")
|
|
||||||
. encodeElement('DocType', "webm")
|
|
||||||
. encodeElement('DocTypeVersion', "\x02")
|
|
||||||
. encodeElement('DocTypeReadVersion', "\x02")
|
|
||||||
);
|
);
|
||||||
$info = encodeElement('Info',
|
$info = ebmlEncodeElement('Info',
|
||||||
encodeElement('TimecodeScale', "\x0F\x42\x40")
|
ebmlEncodeElement('Duration', "\x41\x20\x00\x00")
|
||||||
. encodeElement('Duration', "\x41\x20\x00\x00")
|
. ebmlEncodeElement('MuxingApp', 'ccframe')
|
||||||
. encodeElement('MuxingApp', 'f')
|
. ebmlEncodeElement('WritingApp', 'ccframe')
|
||||||
. encodeElement('WritingApp', 'f')
|
|
||||||
);
|
);
|
||||||
$tracks = encodeElement('Tracks',
|
$tracks = ebmlEncodeElement('Tracks',
|
||||||
encodeElement('TrackEntry',
|
ebmlEncodeElement('TrackEntry', $videoTrack->content()->readAll())
|
||||||
encodeElement('TrackNumber', "\x01")
|
);
|
||||||
. encodeElement('TrackUID', "\x01")
|
$cues = ebmlEncodeElement('Cues',
|
||||||
. encodeElement('TrackType', "\x01")
|
ebmlEncodeElement('CuePoint',
|
||||||
. encodeElement('DefaultDuration', "\x98\x96\x80")
|
ebmlEncodeElement('CueTime', "\x00")
|
||||||
. encodeElement('CodecID', $codecID)
|
. ebmlEncodeElement('CueTrackPositions',
|
||||||
. encodeElement('Video',
|
ebmlEncodeElement('CueTrack', pack('N', $trackNumber))
|
||||||
encodeElement('PixelWidth', pack('N', $width))
|
. ebmlEncodeElement('CueClusterPosition', pack('N', $lenSeekHead + strlen($info) + strlen($tracks) + $lenCues))
|
||||||
. encodeElement('PixelHeight', pack('N', $height))
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$cues = encodeElement('Cues',
|
if (strlen($cues) != $lenCues) throw new Exception('length of Cues element wrong');
|
||||||
encodeElement('CuePoint',
|
$cluster = ebmlEncodeElement('Cluster',
|
||||||
encodeElement('CueTime', "\x00")
|
ebmlEncodeElement('Timecode', "\x00")
|
||||||
. encodeElement('CueTrackPositions',
|
. ebmlEncodeElement($frame->name(), $frame->content()->readAll())
|
||||||
encodeElement('CueTrack', "\x01")
|
. ebmlEncodeElement('Void', '')
|
||||||
. encodeElement('CueClusterPosition', chr($lenSeekHead + strlen($info) + strlen($tracks) + $lenCues))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
$seekHead = encodeElement('SeekHead',
|
$seekHead = ebmlEncodeElement('SeekHead',
|
||||||
encodeElement('Seek',
|
matroskaSeekElement('Info', $lenSeekHead)
|
||||||
encodeElement('SeekID', encodeElementName('Info'))
|
. matroskaSeekElement('Tracks', $lenSeekHead + strlen($info))
|
||||||
. encodeElement('SeekPosition', chr($lenSeekHead))
|
. matroskaSeekElement('Cues', $lenSeekHead + strlen($info) + strlen($tracks))
|
||||||
)
|
. matroskaSeekElement('Cluster', $lenSeekHead + strlen($info) + strlen($tracks) + $lenCues)
|
||||||
. encodeElement('Seek',
|
|
||||||
encodeElement('SeekID', encodeElementName('Tracks'))
|
|
||||||
. encodeElement('SeekPosition', chr($lenSeekHead + strlen($info)))
|
|
||||||
)
|
|
||||||
. encodeElement('Seek',
|
|
||||||
encodeElement('SeekID', encodeElementName('Cues'))
|
|
||||||
. encodeElement('SeekPosition', chr($lenSeekHead + strlen($info) + strlen($tracks)))
|
|
||||||
)
|
|
||||||
. encodeElement('Seek',
|
|
||||||
encodeElement('SeekID', encodeElementName('Cluster'))
|
|
||||||
. encodeElement('SeekPosition', chr($lenSeekHead + strlen($info) + strlen($tracks) + $lenCues))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$cluster = "\x1F\x43\xB6\x75\x08" . pack('N', $size + 13) . (
|
|
||||||
//. encodeElement('Cluster',
|
|
||||||
encodeElement('Timecode', "\x00")
|
|
||||||
. "\xA3\x08" . pack('N', $size + 4) . ("\x81\x00\x00\x80" . $data)
|
|
||||||
//. encodeElement('SimpleBlock', "\x81\x00\x00\x80" . $data)
|
|
||||||
);
|
|
||||||
$segment = "\x18\x53\x80\x67\x08" . pack('N', $size + 173) . (
|
|
||||||
// . encodeElement('Segment',
|
|
||||||
$seekHead . $info . $tracks . $cues . $cluster
|
|
||||||
);
|
);
|
||||||
|
if (strlen($seekHead) != $lenSeekHead) throw new Exception('length of SeekHead element wrong');
|
||||||
|
$segment = ebmlEncodeElement('Segment', $seekHead . $info . $tracks . $cues . $cluster);
|
||||||
return $ebml . $segment;
|
return $ebml . $segment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,23 +56,20 @@ function firstVPxFrame($segment, $trackNumber, $skip=0) {
|
|||||||
foreach($segment as $x1) {
|
foreach($segment as $x1) {
|
||||||
if ($x1->name() == 'Cluster') {
|
if ($x1->name() == 'Cluster') {
|
||||||
$cluserTimecode = $x1->Get('Timecode');
|
$cluserTimecode = $x1->Get('Timecode');
|
||||||
foreach($x1 as $x2) {
|
foreach($x1 as $blockGroup) {
|
||||||
$blockRaw = NULL;
|
$blockRaw = NULL;
|
||||||
if ($x2->name() == 'SimpleBlock') {
|
if ($blockGroup->name() == 'SimpleBlock') {
|
||||||
$blockRaw = $x2->value();
|
$blockRaw = $blockGroup->value();
|
||||||
} elseif ($x2->name() == 'BlockGroup') {
|
} elseif ($blockGroup->name() == 'BlockGroup') {
|
||||||
$blockRaw = $x2->get('Block');
|
$blockRaw = $blockGroup->get('Block');
|
||||||
}
|
}
|
||||||
if (isset($blockRaw)) {
|
if (isset($blockRaw)) {
|
||||||
$block = new MatroskaBlock($blockRaw);
|
$block = new MatroskaBlock($blockRaw);
|
||||||
if ($block->trackNumber == $trackNumber) {
|
if ($block->trackNumber == $trackNumber && $block->keyframe) {
|
||||||
$frame = $block->frames[0];
|
|
||||||
if ($block->keyframe) {
|
|
||||||
if (!isset($cluserTimecode) || $cluserTimecode + $block->timecode >= $skip) {
|
if (!isset($cluserTimecode) || $cluserTimecode + $block->timecode >= $skip) {
|
||||||
return $frame;
|
return $blockGroup;
|
||||||
} elseif (!isset($frame1)) {
|
} elseif (!isset($frame1)) {
|
||||||
$frame1 = $frame;
|
$frame1 = $blockGroup;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,7 +149,7 @@ function videoData($filename) {
|
|||||||
}
|
}
|
||||||
$frame = firstVPxFrame($segment, $trackNumber, $skip);
|
$frame = firstVPxFrame($segment, $trackNumber, $skip);
|
||||||
if (!isset($frame)) throw new Exception('no keyframes');
|
if (!isset($frame)) throw new Exception('no keyframes');
|
||||||
$data['frame'] = muxVPxFrame($pixelWidth, $pixelHeight, $codecID, $frame->readAll());
|
$data['frame'] = muxVPxFrame($trackNumber, $videoTrack, $frame);
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
error_log($e->getMessage());
|
error_log($e->getMessage());
|
||||||
|
Loading…
Reference in New Issue
Block a user