diff options
Diffstat (limited to 'src/ffmpeg.zig')
| -rw-r--r-- | src/ffmpeg.zig | 136 |
1 files changed, 135 insertions, 1 deletions
diff --git a/src/ffmpeg.zig b/src/ffmpeg.zig index fdf9068..6846eb1 100644 --- a/src/ffmpeg.zig +++ b/src/ffmpeg.zig @@ -40,7 +40,6 @@ fn get_format_context( var options: ?*c.AVDictionary = null; var i: usize = 0; std.debug.assert(format_options.len % 2 == 0); - std.debug.print("len {}\n", .{format_options.len}); while (i < format_options.len) : (i += 2) { try check(c.av_dict_set( &options, @@ -421,3 +420,138 @@ pub const StreamSource = struct { allocator.destroy(self); } }; + +pub const BufferedStreamSource = struct { + source: src.Source, + flags: src.StreamFlags, + + decoder: *Decoder, + format: *c.AVFormatContext, + stream: i32, + depth: i32, + packet: *c.AVPacket, + + thread: *gl.Thread, + + pub fn init( + allocator: std.mem.Allocator, + constants: *const gl.Constants, + texture_type: gl.Texture.Type, + depth: i32, + filename: [*:0]const u8, + format_name: ?[*:0]const u8, + format_options: []const [*c]c.lo_arg, + ) !*src.Source { + const self = try allocator.create(BufferedStreamSource); + errdefer allocator.destroy(self); + + var format = try get_format_context(filename, format_name, format_options); + errdefer c.avformat_close_input(@ptrCast(&format)); + format.flags |= c.AVFMT_FLAG_NONBLOCK; + + try check(c.avformat_find_stream_info(format, null)); + + const video_stream = for (format.streams, 0..format.nb_streams) |c_stream, _| { + const stream = @as(*c.AVStream, c_stream orelse unreachable); + if (stream.codecpar.*.codec_type == c.AVMEDIA_TYPE_VIDEO) break stream; + } else unreachable; + + const codec_par = video_stream.codecpar.*; + + var packet = c.av_packet_alloc(); + errdefer c.av_packet_free(&packet); + + const texture = switch (texture_type) { + .TEXTURE_3D, .TEXTURE_2D_ARRAY => |t| gl.Texture.create(t), + else => return error.textureTypeInvalid, + }; + texture.allocate( + codec_par.width, + codec_par.height, + depth, + c.GL_RGBA8, + ); + + const decoder = try AVDecoder.init(allocator, codec_par); + errdefer decoder.deinit(allocator); + + self.* = .{ + .source = .{ + .texture = texture, + .deinit_fn = deinit, + .register_fn = register_methods, + .unregister_fn = unregister_methods, + .update_uniform_fn = update_uniform, + }, + .flags = .{}, + + .decoder = decoder, + .format = format, + .stream = video_stream.index, + .depth = depth, + .packet = packet, + + .thread = undefined, + }; + + try self.update(); + self.thread = try gl.Thread.init(allocator, constants, update_loop, .{self}); + + return &self.source; + } + + fn register_methods(source: *src.Source, name: []const u8, control: *ctrl.ControlServer) void { + const self: *BufferedStreamSource = @fieldParentPtr("source", source); + self.flags.register(name, control); + } + fn unregister_methods(source: *src.Source, name: []const u8, control: *ctrl.ControlServer) void { + const self: *BufferedStreamSource = @fieldParentPtr("source", source); + self.flags.unregister(name, control); + } + + fn update_uniform(source: *src.Source, param: []const u8, value: gl.UniformPointer) !void { + const self: *BufferedStreamSource = @fieldParentPtr("source", source); + + if (!std.mem.eql(u8, param, "offset")) return error.unknownSourceParam; + const frame = @rem(self.decoder.num_frames + self.depth - 1, self.depth); + try switch (value) { + .FLOAT => |val| val.* = @as(f32, @floatFromInt(frame)) / @as(f32, @floatFromInt(self.depth)), + .DOUBLE => |val| val.* = @as(f64, @floatFromInt(frame)) / @as(f64, @floatFromInt(self.depth)), + .INT => |val| val.* = frame, + .UNSIGNED_INT => |val| val.* = @intCast(frame), + else => error.uniformNotSupported, + }; + } + + fn update_loop(self: *BufferedStreamSource) !void { + while (!self.thread.quit) { + try self.update(); + c.glFlush(); + try std.Thread.yield(); + } + } + + fn update(self: *BufferedStreamSource) !void { + try check(c.av_read_frame(self.format, self.packet)); + defer c.av_packet_unref(self.packet); + + if (self.packet.*.stream_index != self.stream) return; + + _ = try self.decoder.process_packet_fn( + self.decoder, + self.packet, + if (self.flags.shouldStep()) &self.source.texture else null, + ); + self.decoder.num_frames = @rem(self.decoder.num_frames, self.depth); + } + + fn deinit(source: *src.Source, allocator: std.mem.Allocator) void { + const self: *BufferedStreamSource = @fieldParentPtr("source", source); + + self.thread.deinit(allocator); + self.decoder.deinit(allocator); + c.av_packet_free(@ptrCast(&self.packet)); + c.avformat_close_input(@ptrCast(&self.format)); + allocator.destroy(self); + } +}; |
