1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
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);
}
};
|