const std = @import("std"); const c = @import("c.zig"); 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, codec_par: c.AVCodecParameters, ) ffmpeg.Errors!*ffmpeg.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; } 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 processPacket( decoder: *ffmpeg.Decoder, packet: *c.AVPacket, texture: ?*const gl.Texture, ) ffmpeg.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 true; } 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; } };