hawkeye camera support(not tested)

This commit is contained in:
meelstorm
2025-08-19 12:45:50 +02:00
parent f80b275ad8
commit 6ef6aced8d
170 changed files with 20190 additions and 72 deletions

View File

@@ -0,0 +1,209 @@
using System;
using System.Diagnostics;
using System.Globalization;
// (c) Roger Hardiman 2016
// This class uses a System Timer to generate a YUV image at regular intervals
// The ReceivedYUVFrame event is fired for each new YUV image
namespace Inspectron.HawkEye.RTSP.Server
{
public class TestCard
{
// Events that applications can receive
public event ReceivedYUVFrameHandler ReceivedYUVFrame;
// Delegated functions (essentially the function prototype)
public delegate void ReceivedYUVFrameHandler(uint timestamp, int width, int height, byte[] data);
// Local variables
private System.Timers.Timer frame_timer;
private int fps = 0;
private Stopwatch stopwatch;
private byte[] yuv_frame = null;
private int x_position = 0;
private int y_position = 0;
private int width = 0;
private int height = 0;
private Object generate_lock = new Object();
private long count = 0;
// ASCII Font
// Created by Roger Hardiman using an online generation tool
// http://www.riyas.org/2013/12/online-led-matrix-font-generator-with.html
byte[] ascii_0 = { 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c };
byte[] ascii_1 = { 0x00, 0x08, 0x18, 0x28, 0x08, 0x08, 0x08, 0x3e };
byte[] ascii_2 = { 0x00, 0x3e, 0x42, 0x02, 0x0c, 0x30, 0x40, 0x7e };
byte[] ascii_3 = { 0x00, 0x7c, 0x02, 0x02, 0x3c, 0x02, 0x02, 0x7c };
byte[] ascii_4 = { 0x00, 0x0c, 0x14, 0x24, 0x44, 0x7e, 0x04, 0x04 };
byte[] ascii_5 = { 0x00, 0x7e, 0x40, 0x40, 0x7c, 0x02, 0x02, 0x7c };
byte[] ascii_6 = { 0x00, 0x3e, 0x40, 0x40, 0x7c, 0x42, 0x42, 0x3c };
byte[] ascii_7 = { 0x00, 0x7e, 0x02, 0x02, 0x04, 0x08, 0x10, 0x20 };
byte[] ascii_8 = { 0x00, 0x3c, 0x42, 0x42, 0x3c, 0x42, 0x42, 0x3c };
byte[] ascii_9 = { 0x00, 0x3c, 0x42, 0x42, 0x3c, 0x02, 0x02, 0x3e };
byte[] ascii_colon = { 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00 };
byte[] ascii_space = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] ascii_dot = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00 };
// Constructor
public TestCard(int width, int height, int fps)
{
this.width = width;
this.height = height;
this.fps = fps;
// YUV size
int y_size = width * height;
int u_size = (width >> 1) * (height >> 1);
int v_size = (width >> 1) * (height >> 1);
yuv_frame = new byte[y_size + u_size + v_size];
// Set all values to 127
for (int x = 0; x < yuv_frame.Length; x++)
{
yuv_frame[x] = 127;
}
stopwatch = new Stopwatch();
stopwatch.Start();
// Start timer. The Timer will generate each YUV frame
frame_timer = new System.Timers.Timer();
frame_timer.Interval = 1; // on first pass timer will fire straight away (cannot have zero interval)
frame_timer.AutoReset = false; // do not restart timer after the time has elapsed
frame_timer.Elapsed += (object sender, System.Timers.ElapsedEventArgs e) =>
{
// send a frame
Send_YUV_Frame();
count++;
// Some CPU cycles will have been used in Sending the YUV Frame.
// Compute the delay required (the Timer Interval) before sending the next YUV frame
long time_for_next_tick_ms = (count * 1000) / fps;
long time_to_wait = time_for_next_tick_ms - stopwatch.ElapsedMilliseconds;
if (time_to_wait <= 0) time_to_wait = 1; // cannot have negative or zero intervals
frame_timer.Interval = time_to_wait;
frame_timer.Start();
};
frame_timer.Start();
}
// Dispose
public void Disconnect()
{
// Stop the frame timer
frame_timer.Stop();
frame_timer.Dispose();
}
private void Send_YUV_Frame()
{
lock (generate_lock)
{
// Get the current time
DateTime now_utc = DateTime.UtcNow;
DateTime now_local = now_utc.ToLocalTime();
long timestamp_ms = ((long)(now_utc.Ticks / TimeSpan.TicksPerMillisecond));
// Generate the String to write
char[] overlay = null;
if (width >= 96)
{
// Need 12 characters of 8x8 pixels. 12*8 = 96
// HH:MM:SS.mmm
String overlay_str = now_local.ToString("HH:mm:ss.fff", CultureInfo.InvariantCulture); // do not replace : or . by local formats
overlay = overlay_str.ToCharArray();
}
else
{
// Min for most video formats is 16x16, enough for 2 characters
String overlay_str = now_local.ToString("ss", CultureInfo.InvariantCulture); // do not replace : or . by local formats
overlay = overlay_str.ToCharArray();
}
// process each character
int start_row = ((height / 2) - 4); // start 4 pixels above the centre row (4 is half the font height)
for (int c = 0; c < overlay.Length; c++)
{
byte[] font = ascii_space;
if (overlay[c] == '0') font = ascii_0;
if (overlay[c] == '1') font = ascii_1;
if (overlay[c] == '2') font = ascii_2;
if (overlay[c] == '3') font = ascii_3;
if (overlay[c] == '4') font = ascii_4;
if (overlay[c] == '5') font = ascii_5;
if (overlay[c] == '6') font = ascii_6;
if (overlay[c] == '7') font = ascii_7;
if (overlay[c] == '8') font = ascii_8;
if (overlay[c] == '9') font = ascii_9;
if (overlay[c] == ' ') font = ascii_space;
if (overlay[c] == ':') font = ascii_colon;
if (overlay[c] == '.') font = ascii_dot;
// process the font character
for (int rows = 0; rows < 8; rows++)
{
int y_plane_pos = (start_row * width) + (rows * width) + (c * 8);
byte row_byte = font[rows];
// bit shift the row byte into individual pixels where the font On/Off maps to Y intensity 50 or 200
for (int bits = 0; bits < 8; bits++)
{
if ((row_byte & 0x80) == 0x80)
{
// Pixel On
yuv_frame[y_plane_pos] = 200;
}
else
{
yuv_frame[y_plane_pos] = 50;
}
y_plane_pos++;
row_byte = (byte)(row_byte << 1); // shift up so the next 'bit' to process is the most significant bit
}
}
}
// Toggle the pixel value
byte pixel_value = yuv_frame[(y_position * width) + x_position];
// change brightness of pixel
if (pixel_value > 128) pixel_value = 30;
else pixel_value = 230;
yuv_frame[(y_position * width) + x_position] = pixel_value;
// move the x and y position
x_position = x_position + 5;
if (x_position >= width)
{
x_position = 0;
y_position = y_position + 1;
}
if (y_position >= height)
{
y_position = 0;
}
// fire the Event
if (ReceivedYUVFrame != null)
{
ReceivedYUVFrame((uint)stopwatch.ElapsedMilliseconds, width, height, yuv_frame);
}
}
}
}
}