const std = @import("std"); const c = @import("../c.zig"); const gl = @import("../gl.zig"); const build_config = @import("build_config"); const dec = @import("decoder.zig"); const check = dec.check; pub const AVDecoder = struct { decoder: dec.Decoder, sws_ctx: *c.SwsContext, codec_par: c.AVCodecParameters, codec_ctx: [*c]c.AVCodecContext, raw_frame: [*c]c.AVFrame, rgba_frame: [*c]c.AVFrame, pub fn init(allocator: std.mem.Allocator, codec_par: c.AVCodecParameters) dec.Errors!*dec.Decoder { const self = try allocator.create(AVDecoder); errdefer allocator.destroy(self); const codec = c.avcodec_find_decoder(codec_par.codec_id) orelse return error.UnsupportedCodec; var codec_ctx = c.avcodec_alloc_context3(codec) orelse return error.AVAllocationError; errdefer c.avcodec_free_context(&codec_ctx); var raw_frame = c.av_frame_alloc() orelse return error.AVAllocationError; errdefer c.av_frame_free(&raw_frame); var rgba_frame = c.av_frame_alloc() orelse return error.AVAllocationError; errdefer c.av_frame_free(&rgba_frame); try check(c.avcodec_parameters_to_context(codec_ctx, &codec_par)); try check(c.avcodec_open2(codec_ctx, codec, null)); errdefer check(c.avcodec_close(codec_ctx)) catch unreachable; const sws_ctx = c.sws_getContext( codec_par.width, codec_par.height, codec_par.format, codec_par.width, codec_par.height, c.AV_PIX_FMT_RGBA, c.SWS_POINT, null, null, 0, ) orelse return error.AVGenericError; errdefer c.sws_freeContext(sws_ctx); self.* = .{ .decoder = .{ .process_packet_fn = processPacket, .init_texture_fn = initTexture, .reset_fn = reset, .deinit_fn = deinit, .num_frames = 0, }, .sws_ctx = sws_ctx, .codec_par = codec_par, .codec_ctx = codec_ctx, .raw_frame = raw_frame, .rgba_frame = rgba_frame, }; return &self.decoder; } pub fn deinit(decoder: *dec.Decoder, allocator: std.mem.Allocator) void { const self: *AVDecoder = @fieldParentPtr("decoder", decoder); const raw_ptr: [*c][*c]c.AVFrame = &self.raw_frame; const rgba_ptr: [*c][*c]c.AVFrame = &self.rgba_frame; c.av_frame_free(raw_ptr); c.av_frame_free(rgba_ptr); c.sws_freeContext(self.sws_ctx); check(c.avcodec_close(self.codec_ctx)) catch unreachable; c.avcodec_free_context(&self.codec_ctx); allocator.destroy(self); } pub fn reset(decoder: *dec.Decoder) void { const self: *AVDecoder = @fieldParentPtr("decoder", decoder); c.avcodec_flush_buffers(self.codec_ctx); } pub fn initTexture(decoder: *dec.Decoder, texture: *const gl.Texture) void { const self: *AVDecoder = @fieldParentPtr("decoder", decoder); texture.allocate( self.codec_par.width, self.codec_par.height, self.decoder.num_frames, c.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, ); } pub fn processPacket(decoder: *dec.Decoder, packet: *c.AVPacket, texture: ?*const gl.Texture) dec.Errors!bool { const self: *AVDecoder = @fieldParentPtr("decoder", decoder); try check(c.avcodec_send_packet(self.codec_ctx, packet)); while (true) { const ires = c.avcodec_receive_frame(self.codec_ctx, self.raw_frame); if (ires == c.AVERROR_EOF) return false; if (ires == c.AVERROR(c.EAGAIN)) return true; try check(ires); try check(c.sws_scale_frame(self.sws_ctx, self.rgba_frame, self.raw_frame)); c.glPixelStorei(c.GL_UNPACK_ROW_LENGTH, @divFloor(self.rgba_frame.*.linesize[0], @sizeOf(u8) * 4)); if (texture) |tex| { tex.setLayer( self.rgba_frame.*.width, self.rgba_frame.*.height, self.decoder.num_frames, c.GL_RGBA, self.rgba_frame.*.data[0], ); if (tex.type == .TEXTURE_2D) return false; } self.decoder.num_frames += 1; } } };