_           _                 
 | |_ ___  __| |____  ___ _   _ 
 | __/ _ \/ _` |_  / / _ \ | | |
 | ||  __/ (_| |/ / |  __/ |_| |
  \__\___|\__,_/___(_)___|\__,_|

Binary signal detective video generation process

The following C program prints a random 4-character alphanumeric code:

//
// random_string.c - Written by Ted Burke - 18-Apr-2023
// Prints a random 4-character alphanumeric string
//

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(int argc, char **argv)
{
    int m, n;

    srand(clock());

    for (m=0 ; m<4 ; ++m)
    {
        // Generate random alphanumeric character
        //   characters 0-9 are ascii 48-57
        //   characters A-Z are ascii 65-90
        //   characters a-z are ascii 97-122
        n = (rand() % 62) + 48;
        if (n > 57) n += 7;
        if (n > 90) n += 6;
        printf("%c", n);
    }

    return 0;
}

With the aid of a Vim macro, I ran the above program once for each line of a text file containing a list of student numbers. I then edited the file further to create Bash script that generated a set of video files using the program below (the program creates one video file each time it runs).

//
// create_video.c - Written by Ted Burke - 18-Apr-2023
//
// Example command to run this program for a student C12345678 with code word "x7fh":
//
//   ./create_video C12345678 x7fh
//

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>

#define W 640
#define H 480
#define L 256

uint8_t p[2][H*W*3] = {0};

int main(int argc, char** argv)
{
    const char *student = argv[1]; // student number
    const char *codeword = argv[2]; // 4-character code word

    int x, y, a, b, i;
    int bits[40];

    // Parse bits from 4 character code supplied as argv[2]
    for (a=0 ; a<4 ; ++a)
    {
        char c = codeword[a];
        bits[a*10] = 1; // start bit
        for (b=0 ; b<8 ; ++b) bits[a*10+1+b] = (c >> b) & 1u; // 8 data bits
        bits[a*10+9] = 0; // stop bit
    }

    for (b=0 ; b<40 ; ++b)
    {
        if (b%10 == 0) printf(" ");
        printf("%d", bits[b]);
    }
    printf("\n");

    // Render "zero" frame
    fprintf(stderr, "Creating zero frame\n");
    char command[1024];
    sprintf(command, "convert -background black -fill white -size 640x480 -pointsize 64 -gravity South -depth 24 label:%s gray:temp.bin", argv[1]);
    system(command);
    FILE *f = fopen("temp.bin", "rb");
    fread(&p[0][0], H*W*3, 1, f);
    fclose(f);

    // Render "one" frame
    fprintf(stderr, "Creating one frame\n");
    memcpy(&p[1][0], &p[0][0], H*W*3);
    for (y=H/2-L/2 ; y<H/2+L/2 ; ++y) for (x=W/2-L/2 ; x<W/2+L/2 ; ++x)
    {
        i = 3*(y*W+x);
        p[1][i] = 255;
        p[1][++i] = 255;
        p[1][++i] = 255;
    }


    // Write frames to video by piping them to ffmpeg
    fprintf(stderr, "Writing frames to video\n");
    errno = 0;
    sprintf(command, "ffmpeg -y -f rawvideo -vcodec rawvideo -pix_fmt rgb24 -s %dx%d -r 30 -i - -f mp4 -q:v 5 -an -vcodec mpeg4 %s.mp4", W, H, argv[1]);
    fprintf(stderr, "Command: %s\n", command);
    FILE *pout = popen(command, "w");

    //FILE *pout = popen("ffmpeg -y -f rawvideo -vcodec rawvideo -pix_fmt rgb24 -s 640x480 -r 30 -i - -f mp4 -q:v 5 -an -vcodec mpeg4 C12345678.mp4", "w");

    if (!pout)
    {
        fprintf(stderr, "Error opening pipe to ffmpeg. errno=%d\n", errno);
    }

    // Write the equivalent of 20 zero bits as preamble before transmission
    fprintf(stderr, "Writing preamble zero frames\n");
    for (b=0 ; b<20 ; ++b) for (i=0 ; i<6 ; ++i) fwrite(&p[0][0], W*H*3, 1, pout);

    // Write frames for the 40 bits in the transmission (n frames per bit)
    fprintf(stderr, "Writing data frames\n");
    for (b=0 ; b<40 ; ++b)
    {
        // Write whichever frame n times to ffmpeg via pipe     
        for (i=0 ; i<6 ; ++i) fwrite(&p[bits[b]][0], W*H*3, 1, pout);
    }

    // Write the equivalent of 20 zero bits as padding after transmission
    fprintf(stderr, "Writing padding zero frames\n");
    for (b=0 ; b<20 ; ++b) for (i=0 ; i<6 ; ++i) fwrite(&p[0][0], W*H*3, 1, pout);

    // Flush and close pipe to ffmpeg
    fprintf(stderr, "Flushing and closing pipe\n");
    fflush(pout);
    pclose(pout);

    return 0;
}

To create all videos, I created a Bash script (video_script.sh) with a line for each student in the following format:

#!/bin/bash

./create_video C12345678 Demo
./create_video C87654321 abcd
etc etc

To make the Bash script executable and then run it:

chmod +x video_script.sh
./video_script

To convert a video from MP4 to a browser-friendly .webm format:

ffmpeg -i C12345678.mp4 -f webm -vcodec libvpx-vp9 -vb 1024k test.webm

To do that automatically to every mp4 file in the current diirectory, use the following bash script:

#!/bin/bash

for file in *.mp4; do
    ffmpeg -i "$file" -f webm -vcodec libvpx-vp9 -vb 1024k "${file%.mp4}.webm"
done

This is the PHP code for the online validator (validator.php):

<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

// Array containing code words
$codes = [
    "C12345678" => "Demo",
    "C87654321" => "abcd",
    "C12341234" => "bl4h",
];

// Default strings
$correct_code = 0;
$student_id = "";
$code_word = "";

// Max length for each submitted string
$max_length = 100;

// If form was submitted, update strings
if ( $_SERVER['REQUEST_METHOD'] === 'POST' && 
      isset($_POST['student_id']) && isset($_POST['code_word']))
{
  // Retrieve student_id and code_word from POST request parameters
  // Limit the length in characters of each time to $max_length (set above)
  $student_id = strtoupper(substr(trim($_POST['student_id']), 0, $max_length));
  $code_word = substr(trim($_POST['code_word']), 0, $max_length);

  if (array_key_exists($student_id, $codes))
  {
    if ($codes[$student_id] == $code_word && $code_word != "")
    {
        $correct_code = 1;
    }
  }
}
?>

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="../jotz.css">
  <title>EEPP2 Binary Signal Detective validator</title>
</head>
<body>

<pre class="asciiart">
 ____  _                          ____  _                   _ 
| __ )(_)_ __   __ _ _ __ _   _  / ___|(_) __ _ _ __   __ _| |
|  _ \| | '_ \ / _` | '__| | | | \___ \| |/ _` | '_ \ / _` | |
| |_) | | | | | (_| | |  | |_| |  ___) | | (_| | | | | (_| | |
|____/|_|_| |_|\__,_|_|   \__, | |____/|_|\__, |_| |_|\__,_|_|
                          |___/           |___/               
 ____       _            _   _           
|  _ \  ___| |_ ___  ___| |_(_)_   _____ 
| | | |/ _ \ __/ _ \/ __| __| \ \ / / _ \
| |_| |  __/ ||  __/ (__| |_| |\ V /  __/
|____/ \___|\__\___|\___|\__|_| \_/ \___|

</pre>


<h1>Binary Signal Detective Validator</h1>

<?php
$video_filename = "$student_id.webm";
if (file_exists($video_filename)) 
{
  echo "<a href=\"$video_filename\">$video_filename</a><br><br>";
}
?>

<form action="validator.php" id="validator_form" method="post">
  <label for="student_id">Student number:</label><br>
  <input type="text" id="student_id" name="student_id" value="<?php echo($student_id) ?>"><br>
  <label for="lname">4-character code word:</label><br>
  <input type="text" id="code_word" name="code_word" value="<?php echo($code_word) ?>"><br><br>
  <input type="submit" value="Validate code word / Get video link">
</form> 

<div style="font-size:200%;">

<?php
if ($correct_code)
{
  echo "<br>Congratulations $student_id,<br>\"$code_word\" is the correct code! 🎉🎉🎉<br>";
}
else if ($code_word != "" && $student_id != "")
{
  echo "Sorry $student_id,<br>\"$code_word\" is not the correct code. 😢<br>";
}
echo "<br>";
?>
</div>

<?php
if (file_exists($video_filename))
{
  echo "<video controls autoplay loop>";
  echo "<source src=\"$video_filename\" type=\"video/webm\">";
  echo "Your browser does not support the video tag.";
  echo "</video>";
}
?>

</body>
</html>