Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,23 @@ pub fn build(b: *std.Build) void {
});

{
const rtp_tests = b.addTest(.{ .root_module = rtp });
const test_filters = b.option([]const []const u8, "test-filter", "Skip tests that do not match any filter") orelse &[0][]const u8{};
const rtp_tests = b.addTest(.{
.root_module = rtp,
.filters = test_filters,
});
const run_rtp_tests = b.addRunArtifact(rtp_tests);

const sdp_tests = b.addTest(.{ .root_module = sdp });
const sdp_tests = b.addTest(.{
.root_module = sdp,
.filters = test_filters,
});
const run_sdp_tests = b.addRunArtifact(sdp_tests);

const rtsp_tests = b.addTest(.{ .root_module = rtsp });
const rtsp_tests = b.addTest(.{
.root_module = rtsp,
.filters = test_filters,
});
const run_rtsp_tests = b.addRunArtifact(rtsp_tests);

const test_step = b.step("test", "Run tests");
Expand Down
158 changes: 155 additions & 3 deletions src/rtp/packet.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ const std = @import("std");
const Reader = std.Io.Reader;
const Self = @This();

pub const Error = error{EndOfStream};
pub const ParseError = error{EndOfStream};
pub const WriteError = error{WriteFailed};

/// Describes an RTP header.
pub const Header = packed struct {
Expand Down Expand Up @@ -34,6 +35,12 @@ pub const Extension = struct {
.data = ext_data,
};
}

fn write(ext: *const Extension, writer: *std.Io.Writer) !void {
try writer.writeInt(u16, ext.profile, .big);
try writer.writeInt(u16, @intCast(@divExact(ext.data.len, 4)), .big);
try writer.writeAll(ext.data);
}
};

header: Header,
Expand All @@ -43,7 +50,7 @@ payload: []const u8,
padding_size: u8 = 0,

/// Parses RTP Packet from slice
pub fn parse(data: []const u8) Error!Self {
pub fn parse(data: []const u8) ParseError!Self {
var reader = std.Io.Reader.fixed(data);
var packet: Self = .{
.header = undefined,
Expand All @@ -69,6 +76,23 @@ pub fn parse(data: []const u8) Error!Self {
return packet;
}

/// Serializes the rtp packet.
pub fn write(packet: *const Self, writer: *std.Io.Writer) WriteError!void {
try writer.writeStruct(packet.header, .big);

const csrc_list: []const u8 = std.mem.sliceAsBytes(packet.csrc_list);
try writer.writeAll(csrc_list);

if (packet.extension) |ext| try ext.write(writer);

try writer.writeAll(packet.payload);
if (packet.header.padding) {
const pad: u8 = @intCast(4 - @rem(packet.payload.len, 4));
for (0..pad - 1) |_| try writer.writeByte(0);
try writer.writeByte(pad);
}
}

pub fn format(self: Self, writer: *std.Io.Writer) !void {
try writer.writeAll("RTP Packet:\n");
try writer.writeAll("\tVersion: ");
Expand Down Expand Up @@ -112,7 +136,7 @@ test "packet too short" {
const short_packet: [10]u8 = [_]u8{ 0x80, 0xE0, 0x51, 0xA4, 0x00, 0x0D, 0xDF, 0x22, 0x54, 0xA7 };

const result = Self.parse(short_packet[0..]);
try std.testing.expectError(Error.EndOfStream, result);
try std.testing.expectError(ParseError.EndOfStream, result);
}

test "packet with csrc" {
Expand Down Expand Up @@ -166,3 +190,131 @@ test "packet with padding" {
try std.testing.expect(parsed_packet.header.padding);
try std.testing.expect(parsed_packet.padding_size == 4);
}

test "write packet" {
const expected = [_]u8{
0x80, 0xE0, 0x51, 0xA4, 0x00, 0x0D, 0xDF,
0x22, 0x54, 0xA7, 0xD4, 0xF3, 0x01, 0x02,
0x03, 0x04,
};

const packet: Self = .{
.header = .{
.padding = false,
.extension = false,
.payload_type = 96,
.csrc_count = 0,
.sequence_number = 0x51A4,
.marker = true,
.timestamp = 0x000DDF22,
.ssrc = 0x54A7D4F3,
},
.payload = &[_]u8{ 0x01, 0x02, 0x03, 0x04 },
};

var buffer: [1024]u8 = undefined;
var writer = std.Io.Writer.fixed(&buffer);
try packet.write(&writer);
try std.testing.expectEqualSlices(u8, &expected, writer.buffered());
}

test "write packet with csrc" {
const expected = [_]u8{
0x83, 0x6F, 0x41, 0xFF, 0xD2,
0x14, 0x8B, 0xBA, 0x37, 0xB8,
0x30, 0x7F, 0x37, 0xB8, 0x30,
0x7F, 0x37, 0xB8, 0x30, 0x7E,
0x37, 0xB8, 0x30, 0x73, 0x00,
0x00, 0x05, 0x00, 0x09,
};

const packet: Self = .{
.header = .{
.padding = false,
.extension = false,
.payload_type = 111,
.csrc_count = 3,
.sequence_number = 0x41FF,
.marker = false,
.timestamp = 0xD2148BBA,
.ssrc = 0x37B8307F,
},
.csrc_list = std.mem.bytesAsSlice(u32, expected[12..24]),
.payload = &[_]u8{ 0x00, 0x00, 0x05, 0x00, 0x09 },
};

var buffer: [1024]u8 = undefined;
var writer = std.Io.Writer.fixed(&buffer);
try packet.write(&writer);
try std.testing.expectEqualSlices(u8, &expected, writer.buffered());
}

test "write packet with extension" {
const expected = [_]u8{
0x90, 0x6F, 0x41, 0xFF, 0xD2, 0x14,
0x8B, 0xBA, 0x37, 0xB8, 0x30, 0x7F,
0xBD, 0xDE, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x09,
};

const packet: Self = .{
.header = .{
.padding = false,
.extension = true,
.payload_type = 111,
.csrc_count = 0,
.sequence_number = 0x41FF,
.marker = false,
.timestamp = 0xD2148BBA,
.ssrc = 0x37B8307F,
},
.extension = .{
.profile = 0xBDDE,
.data = expected[16..28],
},
.payload = expected[28..33],
};

var buffer: [1024]u8 = undefined;
var writer = std.Io.Writer.fixed(&buffer);
try packet.write(&writer);
try std.testing.expectEqualSlices(u8, &expected, writer.buffered());
}

test "write packet with padding" {
const expected = [_]u8{
0xB3, 0x6F, 0x41, 0xFF, 0xD2, 0x14, 0x8B,
0xBA, 0x37, 0xB8, 0x30, 0x7F, 0x37, 0xB8,
0x30, 0x7F, 0x37, 0xB8, 0x30, 0x7E, 0x37,
0xB8, 0x30, 0x73, 0xBD, 0xDE, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,
0x09, 0x00, 0x00, 0x00, 0x00, 0x04,
};

const packet: Self = .{
.header = .{
.padding = true,
.extension = true,
.payload_type = 111,
.csrc_count = 3,
.sequence_number = 0x41FF,
.marker = false,
.timestamp = 0xD2148BBA,
.ssrc = 0x37B8307F,
},
.csrc_list = std.mem.bytesAsSlice(u32, expected[12..24]),
.extension = .{
.profile = 0xBDDE,
.data = expected[28..40],
},
.payload = expected[40..44],
};

var buffer: [1024]u8 = undefined;
var writer = std.Io.Writer.fixed(&buffer);
try packet.write(&writer);
try std.testing.expectEqualSlices(u8, &expected, writer.buffered());
}
Loading