diff options
| author | s-ol <s+removethis@s-ol.nu> | 2025-03-10 14:52:42 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-03-10 14:52:42 +0000 |
| commit | 3df8ced3f0895289506565fbd42c530ba3871e82 (patch) | |
| tree | 97cf711e3182a574fc5518a660e663055fbb94da /src/video/ffmpeg.zig | |
| parent | update for Zig 0.14.0 (diff) | |
| download | glsl-view-3df8ced3f0895289506565fbd42c530ba3871e82.tar.gz glsl-view-3df8ced3f0895289506565fbd42c530ba3871e82.zip | |
fix conditional compilation
Diffstat (limited to 'src/video/ffmpeg.zig')
| -rw-r--r-- | src/video/ffmpeg.zig | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/src/video/ffmpeg.zig b/src/video/ffmpeg.zig new file mode 100644 index 0000000..03456d5 --- /dev/null +++ b/src/video/ffmpeg.zig @@ -0,0 +1,126 @@ +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; + } + } +}; |
