diff options
| author | s-ol <s+removethis@s-ol.nu> | 2025-10-23 12:28:43 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-10-24 11:01:17 +0000 |
| commit | d2b98c9ac2ffe045daac4b73da9e08edd7068b77 (patch) | |
| tree | c028c3b0f6f08e633ef5f4e37349a9836f2c1201 | |
| parent | Stream*Source: rewind on EOF (diff) | |
| download | glsl-view-d2b98c9ac2ffe045daac4b73da9e08edd7068b77.tar.gz glsl-view-d2b98c9ac2ffe045daac4b73da9e08edd7068b77.zip | |
ffmpeg: refactor/turn inside avcodec loops inside-out
| -rw-r--r-- | src/control.zig | 4 | ||||
| -rw-r--r-- | src/ffmpeg.zig | 398 | ||||
| -rw-r--r-- | src/hap.zig | 128 |
3 files changed, 231 insertions, 299 deletions
diff --git a/src/control.zig b/src/control.zig index 4a29309..053f985 100644 --- a/src/control.zig +++ b/src/control.zig @@ -407,7 +407,7 @@ pub const ControlServer = struct { if (!std.mem.eql(u8, std.mem.span(texture_typeZ), "TEXTURE_2D")) return error.textureTypeInvalid; const ffmpeg = @import("ffmpeg.zig"); - const source = try ffmpeg.StreamSource.init(self.cache.allocator, self.constants, filenameZ, formatZ, args); + const source = try ffmpeg.StreamSource.init(self.cache.allocator, self.constants, .TEXTURE_2D, 1, filenameZ, formatZ, args); try self.add_source(name, source); return true; @@ -430,7 +430,7 @@ pub const ControlServer = struct { const args = if (argv.len > 4) argv[4..] else &.{}; const ffmpeg = @import("ffmpeg.zig"); - const source = try ffmpeg.BufferedStreamSource.init(self.cache.allocator, self.constants, texture_type, depth, filenameZ, formatZ, args); + const source = try ffmpeg.StreamSource.init(self.cache.allocator, self.constants, texture_type, depth, filenameZ, formatZ, args); try self.add_source(name, source); return true; diff --git a/src/ffmpeg.zig b/src/ffmpeg.zig index 71c214b..f5f0ee4 100644 --- a/src/ffmpeg.zig +++ b/src/ffmpeg.zig @@ -15,6 +15,9 @@ pub const Errors = error{ UnsupportedCodec, OutOfMemory, NoFrames, + + EOF, + TryLater, }; pub fn check(err: c_int) Errors!void { @@ -59,59 +62,107 @@ fn get_format_context( 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, + format: *c.AVFormatContext, + stream: *c.AVStream, + packet: [*c]c.AVPacket, + + vtable: VTable, + + pub const VTable = struct { + process_next_frame_fn: *const fn (self: *Decoder, texture: ?*const gl.Texture, z: i32) Errors!void, + 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, + }; + + pub const StreamState = enum { + done, + more_now, + more_later, + }; + + pub fn init(format: *c.AVFormatContext, stream: *c.AVStream, vtable: VTable) Decoder { + return .{ + .num_frames = 0, + + .format = format, + .stream = stream, + .packet = c.av_packet_alloc(), + + .vtable = vtable, + }; + } + + pub fn getNextPacket(self: *Decoder) Errors!void { + while (true) { + return switch (c.av_read_frame(self.format, self.packet)) { + c.AVERROR_EOF => error.EOF, + c.AVERROR(c.EAGAIN) => error.TryLater, + else => |res| { + try check(res); + + if (self.packet.*.stream_index == self.stream.index) { + return; + } else { + c.av_packet_unref(self.packet); + continue; + } + }, + }; + } + } 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); + var just_one = false; + if (texture) |tex| { + if (tex.type == .TEXTURE_2D) { + just_one = true; + } + } + try self.rewind(); 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; - if (texture) |tex| { - if (tex.type == .TEXTURE_2D) break; - } - } - c.av_packet_unref(packet); - } else { - if (res != c.AVERROR_EOF) try check(res); + while (true) { + const res = self.vtable.process_next_frame_fn(self, texture, self.num_frames); + if (res != error.EOF) try res; + + self.num_frames += 1; + progress.setCompletedItems(@intCast(self.num_frames)); + + if (just_one or res == error.EOF) break; + } + } + + pub fn processFrame( + self: *Decoder, + texture: ?*const gl.Texture, + z: i32, + ) Errors!void { + if (!self.opened) { + try self.getNextPacket(); + self.opened = true; } + try self.vtable.process_next_frame_fn(self, texture, z); } - pub fn rewind(self: *Decoder, format: *c.AVFormatContext, stream_index: i32) !void { - try check(c.avformat_seek_file(format, stream_index, 0, 0, 0, 0)); - if (self.reset_fn) |reset_fn| reset_fn(self); + pub fn rewind(self: *Decoder) !void { + try check(c.avformat_seek_file(self.format, self.stream.index, 0, 0, 0, 0)); + if (self.vtable.reset_fn) |reset_fn| reset_fn(self); } 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, + outer_progress.start("scanning video frames", @intCast(self.stream.nb_frames)), null, ); if (self.num_frames <= 0) return error.NoFrames; @@ -119,18 +170,17 @@ pub const Decoder = struct { const texture = gl.Texture.create(texture_type); errdefer texture.destroy(); - self.init_texture_fn(self, &texture); + self.vtable.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); + c.av_packet_free(&self.packet); + self.vtable.deinit_fn(self, allocator); } }; @@ -144,10 +194,11 @@ pub const AVDecoder = struct { raw_frame: [*c]c.AVFrame, rgba_frame: [*c]c.AVFrame, - pub fn init(allocator: std.mem.Allocator, codec_par: c.AVCodecParameters) Errors!*Decoder { + pub fn init(allocator: std.mem.Allocator, format: *c.AVFormatContext, stream: *c.AVStream) Errors!*Decoder { const self = try allocator.create(AVDecoder); errdefer allocator.destroy(self); + const codec_par = stream.codecpar.*; 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; @@ -175,13 +226,16 @@ pub const AVDecoder = struct { 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, - }, + .decoder = Decoder.init( + format, + stream, + .{ + .process_next_frame_fn = processNextFrame, + .init_texture_fn = initTexture, + .reset_fn = reset, + .deinit_fn = deinit, + }, + ), .sws_ctx = sws_ctx, .codec_par = codec_par, .codec_ctx = codec_ctx, @@ -221,39 +275,46 @@ pub const AVDecoder = struct { ); } - fn processPacket(decoder: *Decoder, packet: *c.AVPacket, texture: ?*const gl.Texture) Errors!bool { + fn getNextFrame(self: *AVDecoder) Errors!void { + while (true) { + switch (c.avcodec_receive_frame(self.codec_ctx, self.raw_frame)) { + c.AVERROR_EOF => return error.EOF, + c.AVERROR(c.EAGAIN) => { + try self.decoder.getNextPacket(); + defer c.av_packet_unref(self.decoder.packet); + + try check(c.avcodec_send_packet(self.codec_ctx, self.decoder.packet)); + continue; + }, + else => |res| { + try check(res); + return; + }, + } + } + } + + fn processNextFrame(decoder: *Decoder, texture: ?*const gl.Texture, z: i32) Errors!void { 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 self.getNextFrame(); + if (texture) |tex| { 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 true; - } - - self.decoder.num_frames += 1; + tex.setLayer( + self.rgba_frame.*.width, + self.rgba_frame.*.height, + z, + c.GL_RGBA, + self.rgba_frame.*.data[0], + ); } } }; pub const VideoSource = struct { source: src.Source, - decoder: *Decoder, pub fn init( allocator: std.mem.Allocator, @@ -280,33 +341,27 @@ pub const VideoSource = struct { } else unreachable; const codec_par = video_stream.codecpar.*; - - var packet = c.av_packet_alloc(); - defer c.av_packet_free(&packet); - const is_hap = codec_par.codec_tag == c.MKTAG('H', 'a', 'p', '1') or codec_par.codec_tag == c.MKTAG('H', 'a', 'p', '5'); + const decoder = if (is_hap and build_config.have_hap) + try @import("hap.zig").HAPDecoder.init(allocator, format, video_stream) + else + try AVDecoder.init(allocator, format, video_stream); + errdefer decoder.deinit(allocator); + self.* = .{ .source = .{ - .texture = undefined, + .texture = try decoder.createTexture(progress, texture_type), .deinit_fn = deinit, }, - .decoder = if (is_hap and build_config.have_hap) - try @import("hap.zig").HAPDecoder.init(allocator, codec_par) - else - try AVDecoder.init(allocator, codec_par), }; - errdefer self.decoder.deinit(allocator); - self.source.texture = try self.decoder.createTexture(progress, format, video_stream, texture_type); return &self.source; } fn deinit(source: *const src.Source, allocator: std.mem.Allocator) void { const self: *const VideoSource = @fieldParentPtr("source", source); - - self.decoder.deinit(allocator); allocator.destroy(self); } }; @@ -317,129 +372,8 @@ pub const StreamSource = struct { decoder: *Decoder, format: *c.AVFormatContext, - stream: i32, - packet: *c.AVPacket, - - thread: *gl.Thread, - - pub fn init( - allocator: std.mem.Allocator, - constants: *const gl.Constants, - filename: [*:0]const u8, - format_name: ?[*:0]const u8, - format_options: []const [*c]c.lo_arg, - ) !*src.Source { - const self = try allocator.create(StreamSource); - 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 = gl.Texture.create(.TEXTURE_2D); - texture.allocate( - codec_par.width, - codec_par.height, - 1, - 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, - }, - .flags = .{}, - - .decoder = decoder, - .format = format, - .stream = video_stream.index, - .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: *StreamSource = @fieldParentPtr("source", source); - self.flags.register(name, control); - } - fn unregister_methods(source: *src.Source, name: []const u8, control: *ctrl.ControlServer) void { - const self: *StreamSource = @fieldParentPtr("source", source); - self.flags.unregister(name, control); - } - - fn update_loop(self: *StreamSource) !void { - while (!self.thread.quit) { - const more = try self.update(); - if (!more) { - try self.decoder.rewind(self.format, self.stream); - _ = try self.update(); - } - - c.glFlush(); - try std.Thread.yield(); - } - } - - fn update(self: *StreamSource) !bool { - const res = c.av_read_frame(self.format, self.packet); - if (res == c.AVERROR_EOF) return false; - try check(res); - defer c.av_packet_unref(self.packet); - - if (self.packet.*.stream_index != self.stream) return true; - - self.decoder.num_frames = 0; - return try self.decoder.process_packet_fn( - self.decoder, - self.packet, - if (self.flags.shouldStep()) &self.source.texture else null, - ); - } - - fn deinit(source: *src.Source, allocator: std.mem.Allocator) void { - const self: *StreamSource = @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); - } -}; - -pub const BufferedStreamSource = struct { - source: src.Source, - flags: src.StreamFlags, - - decoder: *Decoder, - format: *c.AVFormatContext, - stream: i32, depth: i32, - packet: *c.AVPacket, + frame: i32, thread: *gl.Thread, @@ -452,7 +386,7 @@ pub const BufferedStreamSource = struct { format_name: ?[*:0]const u8, format_options: []const [*c]c.lo_arg, ) !*src.Source { - const self = try allocator.create(BufferedStreamSource); + const self = try allocator.create(StreamSource); errdefer allocator.destroy(self); var format = try get_format_context(filename, format_name, format_options); @@ -468,11 +402,14 @@ pub const BufferedStreamSource = struct { const codec_par = video_stream.codecpar.*; - var packet = c.av_packet_alloc(); - errdefer c.av_packet_free(&packet); - + switch (texture_type) { + .TEXTURE_2D, .TEXTURE_RECTANGLE => { + std.debug.assert(depth == 1); + }, + else => {}, + } const texture = switch (texture_type) { - .TEXTURE_3D, .TEXTURE_2D_ARRAY => |t| gl.Texture.create(t), + .TEXTURE_3D, .TEXTURE_2D_ARRAY, .TEXTURE_2D, .TEXTURE_RECTANGLE => |t| gl.Texture.create(t), else => return error.textureTypeInvalid, }; texture.allocate( @@ -482,7 +419,7 @@ pub const BufferedStreamSource = struct { c.GL_RGBA8, ); - const decoder = try AVDecoder.init(allocator, codec_par); + const decoder = try AVDecoder.init(allocator, format, video_stream); errdefer decoder.deinit(allocator); self.* = .{ @@ -497,9 +434,8 @@ pub const BufferedStreamSource = struct { .decoder = decoder, .format = format, - .stream = video_stream.index, .depth = depth, - .packet = packet, + .frame = 0, .thread = undefined, }; @@ -511,19 +447,19 @@ pub const BufferedStreamSource = struct { } fn register_methods(source: *src.Source, name: []const u8, control: *ctrl.ControlServer) void { - const self: *BufferedStreamSource = @fieldParentPtr("source", source); + const self: *StreamSource = @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); + const self: *StreamSource = @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); + const self: *StreamSource = @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); + const frame = @rem(self.frame + 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)), @@ -533,12 +469,18 @@ pub const BufferedStreamSource = struct { }; } - fn update_loop(self: *BufferedStreamSource) !void { + fn update_loop(self: *StreamSource) !void { while (!self.thread.quit) { - const more = try self.update(); - if (!more) { - try self.decoder.rewind(self.format, self.stream); - _ = try self.update(); + while (true) { + if (self.update()) { + break; + } else |err| { + switch (err) { + error.TryLater => break, + error.EOF => try self.decoder.rewind(), + else => return err, + } + } } c.glFlush(); @@ -546,29 +488,19 @@ pub const BufferedStreamSource = struct { } } - fn update(self: *BufferedStreamSource) !bool { - const res = c.av_read_frame(self.format, self.packet); - if (res == c.AVERROR_EOF) return false; - try check(res); - defer c.av_packet_unref(self.packet); - - if (self.packet.*.stream_index != self.stream) return true; - - const more = try self.decoder.process_packet_fn( - self.decoder, - self.packet, + fn update(self: *StreamSource) Errors!void { + try self.decoder.processFrame( if (self.flags.shouldStep()) &self.source.texture else null, + self.frame, ); - self.decoder.num_frames = @rem(self.decoder.num_frames, self.depth); - return more; + self.frame = @rem(self.frame + 1, self.depth); } fn deinit(source: *src.Source, allocator: std.mem.Allocator) void { - const self: *BufferedStreamSource = @fieldParentPtr("source", source); + const self: *StreamSource = @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); } diff --git a/src/hap.zig b/src/hap.zig index 7a2dd50..073cb8b 100644 --- a/src/hap.zig +++ b/src/hap.zig @@ -23,19 +23,23 @@ pub const HAPDecoder = struct { pub fn init( allocator: std.mem.Allocator, - codec_par: c.AVCodecParameters, + format: *c.AVFormatContext, + stream: *c.AVStream, ) ffmpeg.Errors!*ffmpeg.Decoder { const self = try allocator.create(HAPDecoder); self.* = .{ - .decoder = .{ - .process_packet_fn = processPacket, - .init_texture_fn = initTexture, - .reset_fn = null, - .deinit_fn = deinit, - .num_frames = 0, - }, - .codec_par = codec_par, + .decoder = ffmpeg.Decoder.init( + format, + stream, + .{ + .process_next_frame_fn = processNextFrame, + .init_texture_fn = initTexture, + .reset_fn = null, + .deinit_fn = deinit, + }, + ), + .codec_par = stream.codecpar.*, .format = null, .gl_format = undefined, }; @@ -72,66 +76,62 @@ pub const HAPDecoder = struct { } } - fn processPacket( - decoder: *ffmpeg.Decoder, - packet: *c.AVPacket, - texture: ?*const gl.Texture, - ) ffmpeg.Errors!bool { + fn processNextFrame(decoder: *ffmpeg.Decoder, texture: ?*const gl.Texture, z: i32) ffmpeg.Errors!void { const self: *HAPDecoder = @fieldParentPtr("decoder", decoder); - var textureCount: u32 = undefined; - try check(c.HapGetFrameTextureCount(packet.data, @intCast(packet.size), &textureCount)); - if (textureCount != 1) { - return error.UnsupportedCodec; - } + try self.decoder.getNextPacket(); + defer c.av_packet_unref(self.decoder.packet); + + if (self.decoder.packet) |packet| { + var textureCount: u32 = undefined; + try check(c.HapGetFrameTextureCount(packet.*.data, @intCast(packet.*.size), &textureCount)); + if (textureCount != 1) { + return error.UnsupportedCodec; + } - var format: c.HapTextureFormat = undefined; - - if (texture) |tex| { - var buffer: [16 * 1024 * 1024]u8 = undefined; - var length: c_ulong = undefined; - try check(c.HapDecode( - packet.data, - @intCast(packet.size), - 0, - launchThread, - self, - &buffer, - buffer.len, - &length, - &format, - )); - - tex.setLayerCompressed( - self.codec_par.width, - self.codec_par.height, - self.decoder.num_frames, - self.gl_format, - buffer[0..length], - ); - - if (tex.type == .TEXTURE_2D) return true; - } else { - try check(c.HapGetFrameTextureFormat(packet.data, @intCast(packet.size), 0, &format)); - if (self.format) |fmt| { - if (fmt != format) return error.UnsupportedCodec; + var format: c.HapTextureFormat = undefined; + + if (texture) |tex| { + var buffer: [16 * 1024 * 1024]u8 = undefined; + var length: c_ulong = undefined; + try check(c.HapDecode( + packet.*.data, + @intCast(packet.*.size), + 0, + launchThread, + self, + &buffer, + buffer.len, + &length, + &format, + )); + + tex.setLayerCompressed( + self.codec_par.width, + self.codec_par.height, + z, + self.gl_format, + buffer[0..length], + ); } else { - self.format = format; - self.gl_format = switch (format) { - c.HapTextureFormat_RGB_DXT1 => c.GL_COMPRESSED_RGB_S3TC_DXT1_EXT, - c.HapTextureFormat_RGBA_DXT5 => c.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, - c.HapTextureFormat_YCoCg_DXT5, - c.HapTextureFormat_A_RGTC1, - c.HapTextureFormat_RGBA_BPTC_UNORM, - c.HapTextureFormat_RGB_BPTC_UNSIGNED_FLOAT, - c.HapTextureFormat_RGB_BPTC_SIGNED_FLOAT, - => return error.UnsupportedCodec, - else => unreachable, - }; + try check(c.HapGetFrameTextureFormat(packet.*.data, @intCast(packet.*.size), 0, &format)); + if (self.format) |fmt| { + if (fmt != format) return error.UnsupportedCodec; + } else { + self.format = format; + self.gl_format = switch (format) { + c.HapTextureFormat_RGB_DXT1 => c.GL_COMPRESSED_RGB_S3TC_DXT1_EXT, + c.HapTextureFormat_RGBA_DXT5 => c.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, + c.HapTextureFormat_YCoCg_DXT5, + c.HapTextureFormat_A_RGTC1, + c.HapTextureFormat_RGBA_BPTC_UNORM, + c.HapTextureFormat_RGB_BPTC_UNSIGNED_FLOAT, + c.HapTextureFormat_RGB_BPTC_SIGNED_FLOAT, + => return error.UnsupportedCodec, + else => unreachable, + }; + } } } - - self.decoder.num_frames += 1; - return true; } }; |
