aboutsummaryrefslogtreecommitdiffstats
path: root/src/video/hap.zig
blob: 70b194a8216133d7308883f660cb4595bc07c52b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
const std = @import("std");
const c = @import("../c.zig");
const gl = @import("../gl.zig");
const dec = @import("decoder.zig");

fn check(err: c_uint) dec.Errors!void {
    return switch (err) {
        c.HapResult_No_Error => return,
        c.HapResult_Bad_Arguments => return error.HAPBadArguments,
        c.HapResult_Buffer_Too_Small => return error.HAPBufferTooSmall,
        c.HapResult_Bad_Frame => return error.HAPBadFrame,
        c.HapResult_Internal_Error => return error.HAPInternalError,
        else => unreachable,
    };
}

pub const HAPDecoder = struct {
    decoder: dec.Decoder,

    codec_par: c.AVCodecParameters,
    format: ?c.HapTextureFormat,
    gl_format: c.GLenum,

    pub fn init(allocator: std.mem.Allocator, codec_par: c.AVCodecParameters) dec.Errors!*dec.Decoder {
        const self = try allocator.create(HAPDecoder);

        self.* = .{
            .decoder = .{
                .process_packet_fn = processPacket,
                .init_texture_fn = initTexture,
                .reset_fn = null,
                .deinit_fn = deinit,
                .num_frames = 0,
            },
            .codec_par = codec_par,
            .format = null,
            .gl_format = undefined,
        };
        return &self.decoder;
    }

    pub fn deinit(decoder: *dec.Decoder, allocator: std.mem.Allocator) void {
        const self: *HAPDecoder = @fieldParentPtr("decoder", decoder);
        allocator.destroy(self);
    }

    pub fn initTexture(decoder: *dec.Decoder, texture: *const gl.Texture) void {
        const self: *HAPDecoder = @fieldParentPtr("decoder", decoder);

        texture.allocate(
            self.codec_par.width,
            self.codec_par.height,
            self.decoder.num_frames,
            self.gl_format,
        );
    }

    fn launchThread(workfn: c.HapDecodeWorkFunction, p: ?*anyopaque, count: c_uint, info: ?*anyopaque) callconv(.C) void {
        _ = info;

        var i: c_uint = 0;
        while (i < count) : (i += 1) {
            workfn.?(p, i);
        }
    }

    pub fn processPacket(decoder: *dec.Decoder, packet: *c.AVPacket, texture: ?*const gl.Texture) dec.Errors!bool {
        const self: *HAPDecoder = @fieldParentPtr("decoder", decoder);

        var textureCount: u32 = undefined;
        try check(c.HapGetFrameTextureCount(packet.data, @intCast(packet.size), &textureCount));
        if (textureCount != 1) {
            return error.UnsupportedCodec;
        }

        var format: c.HapTextureFormat = undefined;

        if (texture) |tex| {
            var buffer: [16 * 1024 * 1024]u8 = undefined;
            var length: c_ulong = undefined;
            try check(c.HapDecode(
                packet.data,
                @intCast(packet.size),
                0,
                launchThread,
                self,
                &buffer,
                buffer.len,
                &length,
                &format,
            ));

            tex.setLayerCompressed(
                self.codec_par.width,
                self.codec_par.height,
                self.decoder.num_frames,
                self.gl_format,
                buffer[0..length],
            );

            if (tex.type == .TEXTURE_2D) return false;
        } else {
            try check(c.HapGetFrameTextureFormat(packet.data, @intCast(packet.size), 0, &format));
            if (self.format) |fmt| {
                if (fmt != format) return error.UnsupportedCodec;
            } else {
                self.format = format;
                self.gl_format = switch (format) {
                    c.HapTextureFormat_RGB_DXT1 => c.GL_COMPRESSED_RGB_S3TC_DXT1_EXT,
                    c.HapTextureFormat_RGBA_DXT5 => c.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT,
                    c.HapTextureFormat_YCoCg_DXT5,
                    c.HapTextureFormat_A_RGTC1,
                    c.HapTextureFormat_RGBA_BPTC_UNORM,
                    c.HapTextureFormat_RGB_BPTC_UNSIGNED_FLOAT,
                    c.HapTextureFormat_RGB_BPTC_SIGNED_FLOAT,
                    => return error.UnsupportedCodec,
                    else => unreachable,
                };
            }
        }

        self.decoder.num_frames += 1;
        return true;
    }
};