const std = @import("std"); const c = @import("../c.zig"); const gl = @import("../gl.zig"); pub const Errors = error{ AVGenericError, AVAllocationError, HAPBadArguments, HAPBufferTooSmall, HAPBadFrame, HAPInternalError, UnsupportedCodec, OutOfMemory, NoFrames, }; pub fn check(err: c_int) Errors!void { if (err >= 0) return; var buf: [c.AV_ERROR_MAX_STRING_SIZE]u8 = undefined; _ = c.av_make_error_string(&buf, buf.len, err); std.debug.print("libav error: {s}\n", .{buf}); return error.AVGenericError; } pub const Decoder = struct { num_frames: i32, process_packet_fn: *const fn (self: *Decoder, packet: *c.AVPacket, texture: ?*const gl.Texture) Errors!bool, init_texture_fn: *const fn (self: *Decoder, texture: *const gl.Texture) void, reset_fn: ?*const fn (self: *Decoder) void, deinit_fn: *const fn (self: *Decoder, allocator: std.mem.Allocator) void, fn processStream( self: *Decoder, progress: std.Progress.Node, format: *c.AVFormatContext, stream: *c.AVStream, texture: ?*const gl.Texture, ) Errors!void { defer progress.end(); try check(c.avformat_seek_file(format, stream.index, 0, 0, 0, 0)); if (self.reset_fn) |reset_fn| reset_fn(self); var packet = c.av_packet_alloc(); defer c.av_packet_free(&packet); self.num_frames = 0; var res = c.av_read_frame(format, packet); while (res >= 0) : (res = c.av_read_frame(format, packet)) { if (packet.*.stream_index == stream.index) { const more = try self.process_packet_fn(self, packet, texture); progress.setCompletedItems(@intCast(self.num_frames)); if (!more) break; } c.av_packet_unref(packet); } else { if (res != c.AVERROR_EOF) try check(res); } } pub fn createTexture( self: *Decoder, outer_progress: std.Progress.Node, format: *c.AVFormatContext, stream: *c.AVStream, texture_type: gl.Texture.Type, ) Errors!gl.Texture { try self.processStream( outer_progress.start("scanning video frames", @intCast(stream.nb_frames)), format, stream, null, ); if (self.num_frames <= 0) return error.NoFrames; const texture = gl.Texture.create(texture_type); errdefer texture.destroy(); self.init_texture_fn(self, &texture); try self.processStream( outer_progress.start("loading video frames", @intCast(self.num_frames)), format, stream, &texture, ); return texture; } pub fn deinit(self: *Decoder, allocator: std.mem.Allocator) void { self.deinit_fn(self, allocator); } };