aboutsummaryrefslogtreecommitdiffstats
path: root/src/video/ffmpeg.zig
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2025-03-10 14:52:42 +0000
committers-ol <s+removethis@s-ol.nu>2025-03-10 14:52:42 +0000
commit3df8ced3f0895289506565fbd42c530ba3871e82 (patch)
tree97cf711e3182a574fc5518a660e663055fbb94da /src/video/ffmpeg.zig
parentupdate for Zig 0.14.0 (diff)
downloadglsl-view-3df8ced3f0895289506565fbd42c530ba3871e82.tar.gz
glsl-view-3df8ced3f0895289506565fbd42c530ba3871e82.zip
fix conditional compilation
Diffstat (limited to 'src/video/ffmpeg.zig')
-rw-r--r--src/video/ffmpeg.zig126
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;
+ }
+ }
+};