Created
September 24, 2017 17:48
-
-
Save Xkeeper0/573425de8cf722541978c06855608def to your computer and use it in GitHub Desktop.
a hastily-written puggsy prototype video data decoder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
JonTT's Puggsy prototype video decoder | |
Hastily written at one in the morning | |
Get the video | |
$ youtube-dl https://www.youtube.com/watch?v=2ZdXmhwcbhQ | |
Give it a more reasonable name | |
$ mv WE\ REACHED\ 3\,000\ SUBS\,\ SO\ HERE\'S\ A\ CHALLENGE\!\ Do\ you\ accept-2ZdXmhwcbhQ.mp4 burp.mp4 | |
Extract frames into individual images | |
$ ffmpeg -i burp.mp4 -f image2 images%06d.png | |
Delete images before images000807 and after images000902 (not part of data) | |
Then run this thing. It will dump out.bin into your directory | |
*/ | |
// Tracking pages of data | |
$page = 0; // Current page | |
$pagel = false; // Last image was separator | |
$pages = []; // Page data | |
for ($ii = 807; $ii <= 902; $ii++) { | |
$file = sprintf("/tmp/burp/images%06d.png", $ii); | |
print $file ."\n"; | |
// Gets a string of 0 and 1 (or null if separator) | |
$d = decodeimage($file); | |
if ($d === null && $pagel !== true) { | |
// If first not-a-page image, move to the next page | |
$page++; | |
$pagel = true; | |
print "Separator image\n"; | |
continue; | |
} elseif ($d === null) { | |
// If still not a page, keep going | |
print "Separator image\n"; | |
continue; | |
} | |
$pagel = false; | |
// Page, probably. Go ahead and copy it | |
if (!isset($pages[$page])) { | |
// New page, copy it in | |
$pages[$page] = $d; | |
print "Decoded page $page\n"; | |
} else { | |
if ($pages[$page] === $d) { | |
// Different image, same data, looks good | |
print "Verified page $page\n"; | |
} else { | |
// This page was already decoded, but for some reason | |
// this particular attempt didn't match | |
// No attempt at resolution is made | |
print "Page $page MISMATCH!\n"; | |
} | |
} | |
} | |
// Concat every page | |
$total = ""; | |
foreach ($pages as $page) { | |
$total .= $page; | |
} | |
$len = strlen($total); | |
print "Len: ". $len ." (". ($len / 8) .")\n"; | |
// Convert every 8 characters into one byte | |
$out = ""; | |
for ($cp = 0; $cp < $len; $cp += 8) { | |
$bin = substr($total, $cp, 8); | |
$out .= chr(bindec($bin)); | |
} | |
// Dump to file | |
file_put_contents("out.bin", $out); | |
// The magic happens here | |
function decodeimage($file) { | |
$img = imagecreatefrompng($file); | |
// The top left pixel of all data pages is red | |
if (pixel(ica($img, 0, 0)) !== null) { | |
return null; | |
} | |
// Padding area | |
$px = 6; | |
$py = 4; | |
// Approximate size of pixels | |
// I assume these were an even 4 before the padding was added | |
$sx = 3.9632; | |
$sy = 3.962; | |
// Count of pixels | |
$cx = 320; | |
$cy = 180; | |
// Old cruft | |
$cf = 0; | |
$c1 = 0; | |
$c0 = 0; | |
$str = ""; | |
for ($y = 0; $y < $cy; $y++) { | |
for ($x = 0; $x < $cx; $x++) { | |
// Based on internal x/y, get image x/y | |
$ax = ceil($px + ($x * $sx)); | |
$ay = ceil($py + ($y * $sy)); | |
// Get actual color values for our pixel | |
$ics = ica($img, $ax, $ay); | |
// Get bit | |
$z = pixel($ics); | |
if ($z === -1) { | |
// Not red, but not solidly black/white either | |
print "$file: Unknown pixel @ $ax, $ay: $ics[r] $ics[g] $ics[b]\n"; | |
} | |
if ($z === true) { | |
$str .= "1"; | |
} | |
if ($z === false) { | |
$str .= "0"; | |
} | |
} | |
} | |
return $str; | |
} | |
function ica($i, $x, $y) { | |
$rgb = imagecolorat($i, $x, $y); | |
$r = ($rgb >> 16) & 0xFF; | |
$g = ($rgb >> 8) & 0xFF; | |
$b = $rgb & 0xFF; | |
return ['r' => $r, 'g' => $g, 'b' => $b]; | |
} | |
function pixel($a) { | |
// Technically these can be whatever, but these values worked for me | |
$maxBlack = 0x60; | |
$minWhite = 0xA0; | |
// Red pixel | |
if ( | |
$a['r'] > 0xB0 && | |
$a['g'] < 0x80 && | |
$a['b'] < 0x80 | |
) { | |
return null; | |
} | |
// White pixel ("on") | |
if ( | |
$a['r'] > $minWhite && | |
$a['g'] > $minWhite && | |
$a['b'] > $minWhite | |
) { | |
return true; | |
} | |
// Black pixel | |
if ( | |
$a['r'] < $maxBlack && | |
$a['g'] < $maxBlack && | |
$a['b'] < $maxBlack | |
) { | |
return false; | |
} | |
return -1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment