aboutsummaryrefslogtreecommitdiffstats
path: root/src/hap.zig
blob: 7b4579973c44c2851baae2e8fa70c7ba9132dfe2 (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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
const std = @import("std");
const c = @import("c.zig").c;
const gl = @import("gl.zig");
const ffmpeg = @import("ffmpeg.zig");

fn check(err: c_uint) ffmpeg.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: ffmpeg.Decoder,

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

    pub fn init(
        allocator: std.mem.Allocator,
        format: *c.AVFormatContext,
        stream: *c.AVStream,
    ) ffmpeg.Errors!*ffmpeg.Decoder {
        const self = try allocator.create(HAPDecoder);

        self.* = .{
            .decoder = ffmpeg.Decoder.init(
                format,
                stream,
                .{
                    .process_next_frame_fn = processNextFrame,
                    .init_texture_fn = initTexture,
                    .reset_fn = null,
                    .deinit_fn = deinit,
                },
            ),
            .codec_par = stream.codecpar.*,
            .format = null,
            .gl_format = undefined,
        };
        return &self.decoder;
    }

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

    fn initTexture(decoder: *ffmpeg.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);
        }
    }

    fn processNextFrame(decoder: *ffmpeg.Decoder, texture: ?*const gl.Texture, z: i32) ffmpeg.Errors!?f64 {
        const self: *HAPDecoder = @fieldParentPtr("decoder", decoder);

        try self.decoder.getNextPacket();
        defer c.av_packet_unref(self.decoder.packet);

        if (self.decoder.packet) |packet| {
            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,
                    z,
                    self.gl_format,
                    buffer[0..length],
                );
            } 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,
                    };
                }
            }

            const pts: f64 = @floatFromInt(packet.*.pts);
            return pts * c.av_q2d(self.decoder.stream.*.time_base);
        }

        return null;
    }
};