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
8 changes: 6 additions & 2 deletions src/rtp/depacketizer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ pub const InitOptions = struct {
initial_capacity: usize = initial_capacity,
};

const Error = error{Err};

pub const VTable = struct {
/// Depacketize an rtp packet payload into a buffer.
///
/// This function should return frame info which contains the number of written bytes into
/// the slice and if the packet contains a keyframe.
///
/// If the buffer is not enough for the whole frame, the implementation should return `error.ShortBuffer`.
depacketize: *const fn (*anyopaque, []const u8, []u8) anyerror!?FrameInfo,
};

Expand Down
102 changes: 46 additions & 56 deletions src/rtp/packet.zig
Original file line number Diff line number Diff line change
@@ -1,82 +1,72 @@
//! Describes an RTP packet.
const std = @import("std");

const Reader = std.Io.Reader;
const Self = @This();

pub const Error = error{EndOfStream};

/// RTP Packet Structure
pub const Header = struct {
version: u2,
padding: bool,
extension: bool,
csrc_count: u4,
marker: bool,
payload_type: u7,
sequence_number: u16,
timestamp: u32,
/// Describes an RTP header.
pub const Header = packed struct {
ssrc: u32,
csrc_list: []align(1) const u32,
extension_profile: ?u16 = null,
extensions: ?[]const u8 = null,
padding_size: u8 = 0,
size: usize,

/// Parse RTP Header from byte slice
pub fn parse(data: []const u8) Reader.Error!Header {
var reader = Reader.fixed(data);
const bytes = try reader.take(2);

var header: Header = .{
.version = @intCast(bytes[0] >> 6),
.padding = bytes[0] & 0x20 != 0,
.marker = bytes[1] & 0x80 != 0,
.payload_type = @intCast(bytes[1] & 0x7F),
.extension = bytes[0] & 0x10 != 0,
.sequence_number = try reader.takeInt(u16, .big),
.timestamp = try reader.takeInt(u32, .big),
.ssrc = try reader.takeInt(u32, .big),
.csrc_count = @intCast(bytes[0] & 0x0F),
.csrc_list = undefined,
.size = 0,
};
timestamp: u32,
sequence_number: u16,
payload_type: u7,
marker: bool,
csrc_count: u4 = 0,
extension: bool,
padding: bool,
version: u2 = 2,
};

header.csrc_list = std.mem.bytesAsSlice(u32, try reader.take(@as(usize, header.csrc_count) * 4));
/// Describes an RTP Extension
pub const Extension = struct {
profile: u16,
data: []const u8,

if (header.extension) {
header.extension_profile = try reader.takeInt(u16, .big);
const extension_size = try reader.takeInt(u16, .big) * 4;
header.extensions = try reader.take(extension_size);
}
fn parse(reader: *Reader) !Extension {
const profile = reader.takeInt(u16, .big) catch return error.EndOfStream;
const extension_size = (reader.takeInt(u16, .big) catch return error.EndOfStream) * 4;
const ext_data = reader.take(extension_size) catch return error.EndOfStream;

header.size = reader.seek;
return header;
return .{
.profile = profile,
.data = ext_data,
};
}
};

header: Header,
csrc_list: []align(1) const u32 = &.{},
extension: ?Extension = null,
payload: []const u8,
padding_size: u8 = 0,

/// Parses RTP Packet from slice
pub fn parse(data: []const u8) Error!Self {
var header = Header.parse(data) catch |err| switch (err) {
error.EndOfStream => return error.EndOfStream,
else => unreachable,
var reader = std.Io.Reader.fixed(data);
var packet: Self = .{
.header = undefined,
.payload = &.{},
};

if (header.padding) {
if (header.size >= data.len or data[data.len - 1] + header.size > data.len) {
packet.header = reader.takeStruct(Header, .big) catch return error.EndOfStream;
const csrc_count = reader.take(@as(usize, packet.header.csrc_count) * 4) catch return error.EndOfStream;
packet.csrc_list = std.mem.bytesAsSlice(u32, csrc_count);

if (packet.header.extension) packet.extension = try .parse(&reader);

if (packet.header.padding) {
if (reader.seek >= data.len or data[data.len - 1] + reader.seek > data.len) {
@branchHint(.unlikely);
return error.EndOfStream;
}

header.padding_size = data[data.len - 1];
packet.padding_size = data[data.len - 1];
}
packet.payload = data[reader.seek .. reader.end - packet.padding_size];

return .{
.header = header,
.payload = data[header.size .. data.len - header.padding_size],
};
return packet;
}

pub fn format(self: Self, writer: *std.Io.Writer) !void {
Expand Down Expand Up @@ -140,7 +130,7 @@ test "packet with csrc" {
const parsed_packet = try Self.parse(packet[0..]);
try std.testing.expect(parsed_packet.header.csrc_count == 3);

for (csrc_list, parsed_packet.header.csrc_list) |csrc, parsed_csrc| {
for (csrc_list, parsed_packet.csrc_list) |csrc, parsed_csrc| {
try std.testing.expect(csrc == parsed_csrc);
}
}
Expand All @@ -157,8 +147,8 @@ test "packet with extension" {

const parsed_packet = try Self.parse(packet[0..]);
try std.testing.expect(parsed_packet.header.extension);
try std.testing.expect(parsed_packet.header.extension_profile == 0xBDDE);
try std.testing.expectEqualSlices(u8, packet[16..28], parsed_packet.header.extensions.?);
try std.testing.expect(parsed_packet.extension.?.profile == 0xBDDE);
try std.testing.expectEqualSlices(u8, packet[16..28], parsed_packet.extension.?.data);
}

test "packet with padding" {
Expand All @@ -174,5 +164,5 @@ test "packet with padding" {

const parsed_packet = try Self.parse(packet[0..]);
try std.testing.expect(parsed_packet.header.padding);
try std.testing.expect(parsed_packet.header.padding_size == 4);
try std.testing.expect(parsed_packet.padding_size == 4);
}
Loading