reorganize omt to separate folder (wip)
This commit is contained in:
167
omt/build.zig
Normal file
167
omt/build.zig
Normal file
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// OMT module for zig
|
||||
//
|
||||
// Build required OMT parts and link against our code
|
||||
//
|
||||
// Run `zig build omt` to build dependencies before anything else.
|
||||
//
|
||||
// I don't know yet how to best create a conditional step to build and install
|
||||
// the dependencies only if they aren't installed yet. Just putting the installs
|
||||
// as dependencies runs the omt build scripts on every `zig build`.
|
||||
//
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const triple = target.result;
|
||||
if (triple.os.tag != .linux or triple.cpu.arch != .x86_64) {
|
||||
std.log.warn("The build file only supports Linux x86-64 right now.\n", .{});
|
||||
std.log.warn(
|
||||
"You can extend the build.zig to call the appropriate platform scripts " ++
|
||||
"in 3rd/libomtnet/build/ and 3rd/libomt/build and 3rd/libvmx/build/ " ++
|
||||
"and modifying the install scripts",
|
||||
.{},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// This comes from the libomt build scripts
|
||||
const omt_out_path = "3rd/libomt/bin/Release/net8.0/linux-x64/publish/";
|
||||
|
||||
// Build libomtnet
|
||||
const libomtnet_build_script = b.addSystemCommand(&.{"bash"});
|
||||
libomtnet_build_script.cwd = b.path("3rd/libomtnet/build");
|
||||
libomtnet_build_script.addFileArg(b.path("3rd/libomtnet/build/buildall.sh"));
|
||||
|
||||
// Build libomt
|
||||
const libomt_build_script = b.addSystemCommand(&.{"bash"});
|
||||
libomt_build_script.cwd = b.path("3rd/libomt/build");
|
||||
libomt_build_script.addFileArg(b.path("3rd/libomt/build/buildlinuxx64.sh"));
|
||||
libomt_build_script.step.dependOn(&libomtnet_build_script.step);
|
||||
|
||||
// Install libomt
|
||||
const install_omt_h = b.addInstallHeaderFile(
|
||||
b.path(b.pathJoin(&.{ omt_out_path, "libomt.h" })),
|
||||
"libomt.h",
|
||||
);
|
||||
install_omt_h.step.dependOn(&libomt_build_script.step);
|
||||
|
||||
const install_omt_so = b.addInstallLibFile(
|
||||
b.path(b.pathJoin(&.{ omt_out_path, "libomt.so" })),
|
||||
"libomt.so",
|
||||
);
|
||||
install_omt_so.step.dependOn(&libomt_build_script.step);
|
||||
|
||||
// Build libvmx
|
||||
const libvmx_build = b.addSystemCommand(&.{"bash"});
|
||||
libvmx_build.cwd = b.path("3rd/libvmx/build");
|
||||
libvmx_build.addFileArg(b.path("3rd/libvmx/build/buildlinuxx64.sh"));
|
||||
|
||||
// Install libvmx
|
||||
const install_vmx_so = b.addInstallLibFile(
|
||||
b.path("3rd/libvmx/build/libvmx.so"),
|
||||
"libvmx.so",
|
||||
);
|
||||
install_vmx_so.step.dependOn(&libvmx_build.step);
|
||||
|
||||
{
|
||||
// Manual build step for libomt dependencies
|
||||
const build_omt_step = b.step("omt", "Build libomtnet, libomt and libvmx");
|
||||
build_omt_step.dependOn(&install_omt_h.step);
|
||||
build_omt_step.dependOn(&install_omt_so.step);
|
||||
build_omt_step.dependOn(&install_vmx_so.step);
|
||||
}
|
||||
}
|
||||
|
||||
// The output lib and header directories
|
||||
const lib_output_path: std.Build.LazyPath = .{
|
||||
.cwd_relative = b.lib_dir,
|
||||
};
|
||||
const include_output_path: std.Build.LazyPath = .{
|
||||
.cwd_relative = b.h_dir,
|
||||
};
|
||||
|
||||
// Zig omt module
|
||||
const omt_module = b.addModule("omt", .{
|
||||
.root_source_file = b.path("src/omt.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
omt_module.linkSystemLibrary("omt", .{});
|
||||
omt_module.addLibraryPath(lib_output_path);
|
||||
omt_module.addIncludePath(include_output_path);
|
||||
|
||||
{
|
||||
// Define executables
|
||||
const sender_exe = b.addExecutable(.{
|
||||
.name = "omtoy-sender",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("utils/sender.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}),
|
||||
});
|
||||
sender_exe.root_module.addImport("omt", omt_module);
|
||||
|
||||
sender_exe.root_module.addLibraryPath(lib_output_path);
|
||||
sender_exe.root_module.addIncludePath(include_output_path);
|
||||
sender_exe.linkSystemLibrary("omt");
|
||||
sender_exe.linkLibC();
|
||||
|
||||
b.installArtifact(sender_exe);
|
||||
|
||||
// Add run step for sender
|
||||
const run_sender_step = b.step("sender-run", "Run the sender client");
|
||||
const run_sender_cmd = b.addRunArtifact(sender_exe);
|
||||
run_sender_step.dependOn(&run_sender_cmd.step);
|
||||
run_sender_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
// Don't actually have arguments, so this is moot
|
||||
if (b.args) |args| {
|
||||
run_sender_cmd.addArgs(args);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const receiver_exe = b.addExecutable(.{
|
||||
.name = "omtoy-receiver",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("utils/receiver.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}),
|
||||
});
|
||||
receiver_exe.root_module.addImport("omt", omt_module);
|
||||
|
||||
receiver_exe.root_module.addLibraryPath(lib_output_path);
|
||||
receiver_exe.root_module.addIncludePath(include_output_path);
|
||||
receiver_exe.linkSystemLibrary("omt");
|
||||
receiver_exe.linkLibC();
|
||||
|
||||
b.installArtifact(receiver_exe);
|
||||
|
||||
const run_receiver_step = b.step("receiver-run", "Run the receiver client");
|
||||
const run_receiver_cmd = b.addRunArtifact(receiver_exe);
|
||||
run_receiver_step.dependOn(&run_receiver_cmd.step);
|
||||
run_receiver_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
// Don't actually have arguments so this is moot
|
||||
if (b.args) |args| {
|
||||
run_receiver_cmd.addArgs(args);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: write tests and stuff
|
||||
// const exe_tests = b.addTest(.{
|
||||
// .root_module = sender_exe.root_module,
|
||||
// });
|
||||
|
||||
// const run_exe_tests = b.addRunArtifact(exe_tests);
|
||||
|
||||
// const test_step = b.step("test", "Run tests");
|
||||
// test_step.dependOn(&run_exe_tests.step);
|
||||
}
|
||||
27
omt/src/omt.zig
Normal file
27
omt/src/omt.zig
Normal file
@@ -0,0 +1,27 @@
|
||||
const std = @import("std");
|
||||
const c = @cImport(@cInclude("libomt.h"));
|
||||
|
||||
pub fn codecToString(codec: c.OMTCodec) ?[]const u8 {
|
||||
return switch (codec) {
|
||||
c.OMTCodec_VMX1 => "VMX1",
|
||||
c.OMTCodec_FPA1 => "FPA1", //Planar audio
|
||||
c.OMTCodec_UYVY => "UYVY",
|
||||
c.OMTCodec_YUY2 => "YUY2",
|
||||
c.OMTCodec_BGRA => "BGRA",
|
||||
c.OMTCodec_NV12 => "NV12",
|
||||
c.OMTCodec_YV12 => "YV12",
|
||||
c.OMTCodec_UYVA => "UYVA",
|
||||
c.OMTCodec_P216 => "P216",
|
||||
c.OMTCodec_PA16 => "PA16,",
|
||||
else => "Invalid",
|
||||
};
|
||||
}
|
||||
|
||||
pub fn colorSpaceToString(cs: c.OMTColorSpace) []const u8 {
|
||||
return switch (cs) {
|
||||
c.OMTColorSpace_Undefined => "Undefined",
|
||||
c.OMTColorSpace_BT601 => "BT601",
|
||||
c.OMTColorSpace_BT709 => "BT709",
|
||||
else => "Invalid",
|
||||
};
|
||||
}
|
||||
111
omt/utils/receiver.zig
Normal file
111
omt/utils/receiver.zig
Normal file
@@ -0,0 +1,111 @@
|
||||
const std = @import("std");
|
||||
const omt = @import("omt");
|
||||
const comt = @cImport(@cInclude("libomt.h"));
|
||||
|
||||
fn discoverStream() ?[*c]u8 {
|
||||
var discovery_count: i32 = 0;
|
||||
|
||||
while (discovery_count == 0) {
|
||||
const discovered = comt.omt_discovery_getaddresses(&discovery_count);
|
||||
std.log.info("Found {} streams:", .{discovery_count});
|
||||
|
||||
const count: usize = @intCast(discovery_count);
|
||||
for (0..count) |i| {
|
||||
const name = discovered[i];
|
||||
std.log.info(" {s}", .{name});
|
||||
}
|
||||
|
||||
if (discovery_count > 0) {
|
||||
return discovered[0];
|
||||
}
|
||||
std.Thread.sleep(1000 * 1000 * 1000);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
std.log.info("Receiving...", .{});
|
||||
comt.omt_setloggingfilename("omtrecvtest.log");
|
||||
|
||||
const stream = discoverStream() orelse undefined;
|
||||
|
||||
const frametype_flags = comt.OMTFrameType_Video | comt.OMTFrameType_Audio | comt.OMTFrameType_Metadata;
|
||||
const receiver = comt.omt_receive_create(
|
||||
stream,
|
||||
frametype_flags,
|
||||
comt.OMTPreferredVideoFormat_UYVYorBGRA,
|
||||
0,
|
||||
);
|
||||
defer comt.omt_receive_destroy(receiver);
|
||||
|
||||
while (true) {
|
||||
const frame = comt.omt_receive(receiver, frametype_flags, 40);
|
||||
if (frame != null) {
|
||||
dumpOMTMediaFrameInfo(allocator, frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dumpOMTMediaFrameInfo(allocator: std.mem.Allocator, frame: *comt.OMTMediaFrame) void {
|
||||
std.log.info("--------- Received --------", .{});
|
||||
switch (frame.*.Type) {
|
||||
comt.OMTFrameType_Video => dumpVideoFrameInfo(frame),
|
||||
comt.OMTFrameType_Audio => dumpAudioFrameInfo(frame),
|
||||
comt.OMTFrameType_Metadata => dumpMetadataFrameInfo(allocator, frame),
|
||||
else => std.log.err("Undefined frame type", .{}),
|
||||
}
|
||||
}
|
||||
|
||||
fn dumpVideoFrameInfo(frame: *comt.OMTMediaFrame) void {
|
||||
std.log.info("Frame: Video", .{});
|
||||
std.log.info(
|
||||
\\Timestamp: {d}
|
||||
\\Codec: {s}
|
||||
\\Width: {d}
|
||||
\\Height: {d}
|
||||
\\Stride: {d}
|
||||
\\FrameRateN: {d}
|
||||
\\FrameRateD: {d}
|
||||
\\AspectRatio: {d}
|
||||
\\ColorSpace: {s}
|
||||
, .{
|
||||
frame.Timestamp,
|
||||
omt.codecToString(frame.Codec) orelse "Invalid",
|
||||
frame.Width,
|
||||
frame.Height,
|
||||
frame.Stride,
|
||||
frame.FrameRateN,
|
||||
frame.FrameRateD,
|
||||
frame.AspectRatio,
|
||||
omt.colorSpaceToString(frame.ColorSpace),
|
||||
});
|
||||
}
|
||||
|
||||
fn dumpAudioFrameInfo(frame: *comt.OMTMediaFrame) void {
|
||||
std.log.info("Frame: Audio", .{});
|
||||
std.log.info(
|
||||
\\Timestamp: {d}
|
||||
\\SampleRate: {d}
|
||||
\\Channels: {d}
|
||||
\\SamplesPerChannel: {d}
|
||||
, .{ frame.Timestamp, frame.SampleRate, frame.Channels, frame.SamplesPerChannel });
|
||||
}
|
||||
|
||||
fn dumpMetadataFrameInfo(allocator: std.mem.Allocator, frame: *comt.OMTMediaFrame) void {
|
||||
std.log.info("Frame: Metadata", .{});
|
||||
if (frame.FrameMetadataLength == 0 or frame.FrameMetadata == null) {
|
||||
std.log.info("None", .{});
|
||||
return;
|
||||
}
|
||||
|
||||
const metadata_length: usize = @intCast(frame.FrameMetadataLength);
|
||||
const buffer = allocator.alloc(u8, metadata_length) catch @panic("OOM");
|
||||
defer allocator.free(buffer);
|
||||
|
||||
std.debug.print("metadata: {?p}\n", .{frame.FrameMetadata});
|
||||
std.mem.copyForwards(u8, buffer, @ptrCast(@alignCast(frame.FrameMetadata)));
|
||||
std.log.info("{s}", .{buffer});
|
||||
}
|
||||
140
omt/utils/sender.zig
Normal file
140
omt/utils/sender.zig
Normal file
@@ -0,0 +1,140 @@
|
||||
const std = @import("std");
|
||||
const omt = @cImport(@cInclude("libomt.h"));
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
std.log.info("Sending...", .{});
|
||||
omt.omt_setloggingfilename("omtsendtest.log");
|
||||
|
||||
const sender = omt.omt_send_create("Omtoy Sender", omt.OMTQuality_Default) orelse undefined;
|
||||
defer omt.omt_send_destroy(sender);
|
||||
|
||||
std.log.info("Sender created: Omtoy Sender", .{});
|
||||
|
||||
{
|
||||
var sender_info = std.mem.zeroes(omt.OMTSenderInfo);
|
||||
|
||||
const product_name = "Omtoy";
|
||||
const manufacturer = "Kasper Toy Productions";
|
||||
const version = "0.1";
|
||||
|
||||
@memcpy(sender_info.ProductName[0..product_name.len], product_name);
|
||||
@memcpy(sender_info.Manufacturer[0..manufacturer.len], manufacturer);
|
||||
@memcpy(sender_info.Version[0..version.len], version);
|
||||
|
||||
omt.omt_send_setsenderinformation(sender, &sender_info);
|
||||
}
|
||||
|
||||
const width = 1920;
|
||||
const height = 1080;
|
||||
const stride = width * 2;
|
||||
const data_length = stride * height;
|
||||
|
||||
const frame_buffer = allocator.alloc(u8, data_length) catch undefined;
|
||||
defer allocator.free(frame_buffer);
|
||||
|
||||
var video_frame = omt.OMTMediaFrame{
|
||||
.Type = omt.OMTFrameType_Video,
|
||||
.Width = width,
|
||||
.Height = height,
|
||||
.Codec = omt.OMTCodec_UYVY,
|
||||
.Timestamp = -1,
|
||||
.ColorSpace = omt.OMTColorSpace_BT709,
|
||||
.Flags = omt.OMTVideoFlags_None,
|
||||
.Stride = width * 2,
|
||||
.DataLength = width * 2 * height,
|
||||
.Data = @ptrCast(frame_buffer),
|
||||
.FrameRateN = 60000,
|
||||
.FrameRateD = 1000,
|
||||
.AspectRatio = @as(f32, width) / @as(f32, height),
|
||||
.CompressedData = null,
|
||||
.CompressedLength = 0,
|
||||
.FrameMetadata = null,
|
||||
.FrameMetadataLength = 0,
|
||||
};
|
||||
|
||||
const content = try std.fs.cwd().readFileAlloc("california-1080-uyvy.yuv", allocator, .unlimited);
|
||||
defer allocator.free(content);
|
||||
|
||||
const audio_samples = 800;
|
||||
const audio_buffer = try allocator.alloc(f32, audio_samples);
|
||||
defer allocator.free(audio_buffer);
|
||||
|
||||
var prng = std.Random.DefaultPrng.init(80085);
|
||||
const rng = prng.random();
|
||||
fillAudioBuffer(rng, audio_buffer);
|
||||
|
||||
var audio_frame = omt.OMTMediaFrame{
|
||||
.Type = omt.OMTFrameType_Audio,
|
||||
.Timestamp = -1,
|
||||
.Codec = omt.OMTCodec_FPA1,
|
||||
.SampleRate = 48000,
|
||||
.Channels = 2,
|
||||
.SamplesPerChannel = audio_samples,
|
||||
.Data = @ptrCast(audio_buffer),
|
||||
.DataLength = @intCast(audio_buffer.len * 2),
|
||||
.FrameMetadata = null,
|
||||
.FrameMetadataLength = 0,
|
||||
};
|
||||
|
||||
var tally = omt.OMTTally{ .preview = 0, .program = 0 };
|
||||
var stats = std.mem.zeroes(omt.OMTStatistics);
|
||||
|
||||
var frame_count: i32 = 0;
|
||||
var bytes: i32 = 0;
|
||||
|
||||
const two_lines_len = stride * 2;
|
||||
const two_lines = try allocator.alloc(u8, two_lines_len);
|
||||
defer allocator.free(two_lines);
|
||||
|
||||
@memset(two_lines, 255);
|
||||
var line_position: usize = 0;
|
||||
|
||||
// send 10000 frames
|
||||
while (frame_count < 10000) : (frame_count += 1) {
|
||||
@memcpy(frame_buffer, content);
|
||||
|
||||
const two_lines_slice = frame_buffer[line_position..(line_position + two_lines_len)];
|
||||
@memcpy(two_lines_slice, two_lines[0..two_lines_len]);
|
||||
|
||||
line_position += stride * 2;
|
||||
if (line_position >= video_frame.DataLength) {
|
||||
line_position = 0;
|
||||
}
|
||||
|
||||
bytes += omt.omt_send(sender, &video_frame);
|
||||
|
||||
if (@mod(frame_count, 60) == 0) {
|
||||
std.log.info("sent: {}", .{bytes});
|
||||
|
||||
_ = omt.omt_send_gettally(sender, 0, &tally);
|
||||
std.log.info("tally preview: {}, program: {}", .{ tally.preview, tally.program });
|
||||
|
||||
const frame = omt.omt_send_receive(sender, 0);
|
||||
if (frame != null) {
|
||||
const metadata = frame.*.Data;
|
||||
if (metadata != null) {
|
||||
const metastr = @as([]u8, @ptrCast(metadata));
|
||||
std.log.info("meta: {s}", .{metastr});
|
||||
}
|
||||
}
|
||||
|
||||
const connections = omt.omt_send_connections(sender);
|
||||
std.log.info("connections: {}", .{connections});
|
||||
|
||||
omt.omt_send_getvideostatistics(sender, &stats);
|
||||
std.log.info("statistics sent bytes: {}, frames: {}", .{ stats.BytesSent, stats.Frames });
|
||||
}
|
||||
|
||||
_ = omt.omt_send(sender, &audio_frame);
|
||||
fillAudioBuffer(rng, audio_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
fn fillAudioBuffer(rng: std.Random, buffer: []f32) void {
|
||||
for (buffer) |*val| {
|
||||
val.* = rng.float(f32) * 2 - 1.0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user