-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathav.zig
3765 lines (3542 loc) · 125 KB
/
av.zig
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
const std = @import("std");
const assert = std.debug.assert;
/// Prefer `FormatContext.alloc`.
pub extern fn avformat_alloc_context() ?*FormatContext;
/// Prefer `FormatContext.free`.
pub extern fn avformat_free_context(?*FormatContext) void;
/// Prefer `FormatContext.open_input`.
pub extern fn avformat_open_input(ps: *?*FormatContext, url: [*:0]const u8, fmt: ?*const InputFormat, options: ?*Dictionary.Mutable) c_int;
/// Prefer `FormatContext.close_input`.
pub extern fn avformat_close_input(s: *?*FormatContext) void;
/// Prefer `FormatContext.find_stream_info`.
pub extern fn avformat_find_stream_info(ic: *FormatContext, options: ?[*]Dictionary.Mutable) c_int;
/// Prefer `FormatContext.find_best_stream`.
pub extern fn av_find_best_stream(
ic: *FormatContext,
media_type: MediaType,
wanted_stream_nb: c_int,
related_stream: c_int,
decoder_ret: ?*?*const Codec,
flags: c_int,
) c_int;
/// Prefer `FormatContext.read_frame`.
pub extern fn av_read_frame(s: *FormatContext, pkt: *Packet) c_int;
/// Prefer `FormatContext.seek_frame`.
pub extern fn av_seek_frame(s: *FormatContext, stream_index: c_int, timestamp: i64, flags: c_int) c_int;
/// Prefer `FormatContext.flush`.
pub extern fn avformat_flush(s: *FormatContext) c_int;
/// Prefer `FormatContext.dump`
pub extern fn av_dump_format(ic: *FormatContext, index: c_uint, url: ?[*:0]const u8, is_output: enum(c_int) { input, output }) void;
/// Prefer `IOContext.alloc`.
pub extern fn avio_alloc_context(
buffer: [*c]u8,
buffer_size: c_int,
write_flag: IOContext.WriteFlag,
@"opaque": ?*anyopaque,
read_packet: ?*const fn (?*anyopaque, [*:0]u8, c_int) callconv(.C) c_int,
write_packet: ?*const fn (?*anyopaque, [*:0]u8, c_int) callconv(.C) c_int,
seek: ?*const fn (?*anyopaque, i64, SEEK) callconv(.C) i64,
) [*c]IOContext;
/// Prefer `IOContext.free`.
pub extern fn avio_context_free(s: *?*IOContext) void;
/// Prefer `IOContext.close`.
pub extern fn avio_close(s: ?*IOContext) c_int;
/// Prefer `LOG.set`.
pub extern fn av_log_set_level(level: LOG) void;
/// Prefer `malloc`.
pub extern fn av_malloc(size: usize) ?[*]u8;
/// Prefer `free`.
pub extern fn av_free(ptr: ?*anyopaque) void;
/// Prefer `Dictionary.Const.get` or `Dictionary.Mutable.get`
pub extern fn av_dict_get(m: Dictionary.Const, key: [*:0]const u8, prev: ?*const Dictionary.Entry, flags: Dictionary.Flags) ?*const Dictionary.Entry;
/// Prefer `Dictionary.Const.iterate` or `Dictionary.Mutable.iterate`.
pub extern fn av_dict_iterate(m: Dictionary.Const, prev: ?*const Dictionary.Entry) ?*const Dictionary.Entry;
/// Prefer `Dictionary.Const.count` or `Dictionary.Mutable.count`.
pub extern fn av_dict_count(m: Dictionary.Const) c_int;
/// Prefer `Dictionary.Mutable.set`.
pub extern fn av_dict_set(pm: *Dictionary.Mutable, key: [*:0]const u8, value: ?[*:0]const u8, flags: Dictionary.Flags) c_int;
/// Prefer `Dictionary.Mutable.set_int`.
pub extern fn av_dict_set_int(pm: *Dictionary.Mutable, key: [*:0]const u8, value: i64, flags: Dictionary.Flags) c_int;
/// Prefer `Dictionary.Mutable.copy`.
pub extern fn av_dict_copy(dst: *Dictionary.Mutable, src: Dictionary.Const, flags: Dictionary.Flags) void;
/// Prefer `Dictionary.Const.free` or `Dictionary.Mutable.free`.
pub extern fn av_dict_free(pm: *Dictionary.Const) void;
/// Prefer `FilterContext.opt_set`.
pub extern fn av_opt_set(obj: *anyopaque, name: [*:0]const u8, val: [*:0]const u8, search_flags: OPT_SEARCH) c_int;
/// Prefer `FilterContext.opt_set_int`.
pub extern fn av_opt_set_int(obj: *anyopaque, name: [*:0]const u8, val: i64, search_flags: OPT_SEARCH) c_int;
/// Prefer `FilterContext.opt_set_double`.
pub extern fn av_opt_set_double(obj: *anyopaque, name: [*:0]const u8, val: f64, search_flags: OPT_SEARCH) c_int;
/// Prefer `FilterContext.opt_set_q`.
pub extern fn av_opt_set_q(obj: *anyopaque, name: [*:0]const u8, val: Rational, search_flags: OPT_SEARCH) c_int;
/// Prefer `FilterContext.opt_get_double`.
pub extern fn av_opt_get_double(obj: *anyopaque, name: [*:0]const u8, search_flags: OPT_SEARCH, out_val: *f64) c_int;
/// Prefer `FilterContext.init_str`.
pub extern fn avfilter_init_str(ctx: *FilterContext, args: ?[*:0]const u8) c_int;
/// Prefer `FilterContext.link`.
pub extern fn avfilter_link(src: *FilterContext, srcpad: c_uint, dst: *FilterContext, dstpad: c_uint) c_int;
/// Prefer `FilterContext.buffersrc_write_frame`.
pub extern fn av_buffersrc_write_frame(ctx: *FilterContext, frame: *const Frame) c_int;
/// Prefer `FilterContext.buffersrc_add_frame`.
pub extern fn av_buffersrc_add_frame(ctx: *FilterContext, frame: ?*Frame) c_int;
/// Prefer `FilterContext.buffersink_get_frame_flags`.
pub extern fn av_buffersink_get_frame_flags(ctx: *FilterContext, frame: *Frame, flags: BUFFERSINK_FLAG) c_int;
/// Prefer `FilterContext.buffersink_get_samples`.
pub extern fn av_buffersink_get_samples(ctx: *FilterContext, frame: *Frame, nb_samples: c_int) c_int;
/// Prefer `FilterContext.buffersink_set_frame_size`.
pub extern fn av_buffersink_set_frame_size(ctx: *FilterContext, frame_size: c_uint) void;
/// Prefer `CodecContext.alloc`.
pub extern fn avcodec_alloc_context3(codec: *const Codec) ?*Codec.Context;
/// Prefer `CodecContext.free`.
pub extern fn avcodec_free_context(avctx: *?*Codec.Context) void;
/// Prefer `CodecContext.parameters_to_context`.
pub extern fn avcodec_parameters_to_context(codec: *Codec.Context, par: *const Codec.Parameters) c_int;
/// Prefer `CodecContext.avcodec_open`.
pub extern fn avcodec_open2(avctx: *Codec.Context, codec: *const Codec, options: ?*Dictionary.Mutable) c_int;
/// Prefer `CodecContext.send_packet`.
pub extern fn avcodec_send_packet(avctx: *Codec.Context, avpkt: ?*const Packet) c_int;
/// Prefer `CodecContext.receive_frame`.
pub extern fn avcodec_receive_frame(avctx: *Codec.Context, frame: *Frame) c_int;
/// Prefer `CodecContext.flush_buffers`.
pub extern fn avcodec_flush_buffers(avctx: *Codec.Context) void;
/// Prefer `Codec.iterate`
pub extern fn av_codec_iterate(@"opaque": *?*Codec.Iterator) ?*const Codec;
/// Prefer `Codec.find_decoder`
pub extern fn avcodec_find_decoder(id: Codec.ID) ?*const Codec;
/// Prefer `Codec.find_decoder_by_name`
pub extern fn avcodec_find_decoder_by_name(name: [*:0]const u8) ?*const Codec;
/// Prefer `Codec.find_encoder`
pub extern fn avcodec_find_encoder(id: Codec.ID) ?*const Codec;
/// Prefer `Codec.find_encoder_by_name`
pub extern fn avcodec_find_encoder_by_name(name: [*:0]const u8) ?*const Codec;
/// Prefer `Codec.is_encoder`
pub extern fn av_codec_is_encoder(codec: *const Codec) c_int;
/// Prefer `Codec.is_decoder`
pub extern fn av_codec_is_decoder(codec: *const Codec) c_int;
/// Prefer `Codec.get_profile_name`
pub extern fn av_get_profile_name(codec: *const Codec, profile: c_int) ?[*:0]const u8;
/// Prefer `Packet.alloc`.
pub extern fn av_packet_alloc() ?*Packet;
/// Prefer `Packet.free`.
pub extern fn av_packet_free(pkt: *?*Packet) void;
/// Prefer `Packet.ref`.
pub extern fn av_packet_ref(dst: *Packet, src: *const Packet) c_int;
/// Prefer `Packet.unref`.
pub extern fn av_packet_unref(pkt: *Packet) void;
/// Prefer `Frame.alloc`.
pub extern fn av_frame_alloc() ?*Frame;
/// Prefer `Frame.free`.
pub extern fn av_frame_free(frame: *?*Frame) void;
/// Prefer `Frame.ref`.
pub extern fn av_frame_ref(dst: *Frame, src: *const Frame) c_int;
/// Prefer `Frame.unref`.
pub extern fn av_frame_unref(frame: *Frame) void;
/// Prefer `FilterGraph.alloc`.
pub extern fn avfilter_graph_alloc() ?*FilterGraph;
/// Prefer `FilterGraph.free`.
pub extern fn avfilter_graph_free(graph: *?*FilterGraph) void;
/// Prefer `FilterGraph.alloc_filter`.
pub extern fn avfilter_graph_alloc_filter(graph: *FilterGraph, filter: *const Filter, name: ?[*:0]const u8) ?*FilterContext;
/// Prefer `FilterGraph.config`.
pub extern fn avfilter_graph_config(graphctx: *FilterGraph, log_ctx: ?*anyopaque) c_int;
/// Prefer `Filter.get_by_name`.
pub extern fn avfilter_get_by_name(name: [*:0]const u8) ?*const Filter;
/// Prefer `ChannelLayout.compare`.
pub extern fn av_channel_layout_compare(a: *const ChannelLayout, b: *const ChannelLayout) c_int;
/// Prefer `ChannelLayout.uninit`.
pub extern fn av_channel_layout_uninit(channel_layout: *ChannelLayout) void;
/// Prefer `ChannelLayout.describe`.
pub extern fn av_channel_layout_describe(channel_layout: *const ChannelLayout, buf: [*]u8, buf_size: usize) c_int;
/// Prefer `ChannelLayout.from_mask`.
pub extern fn av_channel_layout_from_mask(channel_layout: *ChannelLayout, mask: u64) c_int;
/// Prefer `SampleFormat.get_name`.
pub extern fn av_get_sample_fmt_name(sample_fmt: SampleFormat) ?[*:0]const u8;
/// Prefer `SampleFormat.get_bytes_per_sample`.
pub extern fn av_get_bytes_per_sample(sample_fmt: SampleFormat) c_int;
/// Prefer `SampleFormat.is_planar`.
pub extern fn av_sample_fmt_is_planar(sample_fmt: SampleFormat) c_int;
/// Prefer `RDFTContext.init`.
pub extern fn av_rdft_init(nbits: c_int, trans: RDFTransformType) ?*RDFTContext;
/// Prefer `RDFTContext.calc`.
pub extern fn av_rdft_calc(s: *RDFTContext, data: [*]FFTSample) void;
/// Prefer `RDFTContext.end`.
pub extern fn av_rdft_end(s: *RDFTContext) void;
/// Prefer `TXContext.init`.
pub extern fn av_tx_init(
ctx: *?*TXContext,
tx: *?*const tx_fn,
@"type": TXType,
inv: c_int,
len: c_int,
scale: ?*const anyopaque,
flags: TXFlags,
) c_int;
/// Prefer `TXContext.uninit`.
pub extern fn av_tx_uninit(ctx: *?*TXContext) void;
/// Prefer `sws.Context.alloc`.
pub extern fn sws_alloc_context() ?*sws.Context;
/// Prefer `sws.Context.init`.
pub extern fn sws_init_context(sws_context: *sws.Context, srcFilter: ?*sws.Filter, dstFilter: ?*sws.Filter) c_int;
/// Prefer `sws.Context.free`.
pub extern fn sws_freeContext(swsContext: ?*sws.Context) void;
/// Prefer `sws.Context.get`.
pub extern fn sws_getContext(srcW: c_int, srcH: c_int, srcFormat: PixelFormat, dstW: c_int, dstH: c_int, dstFormat: PixelFormat, flags: sws.Flags, srcFilter: ?*sws.Filter, dstFilter: ?*sws.Filter, ?[*]const f64) ?*sws.Context;
/// Prefer `sws.Context.scale`.
pub extern fn sws_scale(c: *sws.Context, srcSlice: [*]const [*]const u8, srcStride: [*]const c_int, srcSliceY: c_int, srcSliceH: c_int, dst: [*]const [*]u8, dstStride: [*]const c_int) c_int;
/// Prefer `sws.Context.scale_frame`.
pub extern fn sws_scale_frame(c: *sws.Context, dst: *Frame, src: *const Frame) c_int;
/// Function pointer to a function to perform the transform.
///
/// Using a different context than the one allocated during `av_tx_init` is not
/// allowed.
///
/// The out and in arrays must be aligned to the maximum required by the CPU
/// architecture unless the `TXFlags.UNALIGNED` flag was set in `av_tx_init`.
///
/// The stride must follow the constraints the transform type has specified.
pub const tx_fn = fn (
s: *TXContext,
output_array: ?*anyopaque,
input_array: ?*anyopaque,
stride_in_bytes: isize,
) callconv(.C) void;
pub fn malloc(size: usize) error{OutOfMemory}![]u8 {
const ptr = av_malloc(size) orelse return error.OutOfMemory;
return ptr[0..size];
}
pub const free = av_free;
/// Undefined timestamp value.
///
/// Usually reported by demuxer that work on containers that do not provide
/// either pts or dts.
pub const NOPTS_VALUE: i64 = @bitCast(@as(u64, 0x8000000000000000));
pub const OPT_SEARCH = packed struct(c_int) {
CHILDREN: bool = false,
FAKE_OBJ: bool = false,
_: u30 = 0,
};
pub const LOG = enum(c_int) {
QUIET = -8,
PANIC = 0,
FATAL = 8,
ERROR = 16,
WARNING = 24,
INFO = 32,
VERBOSE = 40,
DEBUG = 48,
TRACE = 56,
pub fn set_level(level: LOG) void {
av_log_set_level(level);
}
};
fn wrap(averror: c_int) Error!c_uint {
if (averror >= 0) return @intCast(averror);
const E = std.posix.E;
return switch (averror) {
0 => unreachable, // handled above
-@as(c_int, @intFromEnum(E.INVAL)) => return error.FFmpegInvalid,
-@as(c_int, @intFromEnum(E.NOENT)) => return error.FileNotFound,
-@as(c_int, @intFromEnum(E.NOMEM)) => return error.OutOfMemory,
-@as(c_int, @intFromEnum(E.PERM)) => return error.PermissionDenied,
-@as(c_int, @intFromEnum(E.AGAIN)) => return error.WouldBlock,
-@as(c_int, @intFromEnum(E.RANGE)) => return error.OutOfRange,
@intFromEnum(ERROR.BSF_NOT_FOUND) => return error.BsfNotFound,
@intFromEnum(ERROR.BUG) => return error.FFmpegBug,
@intFromEnum(ERROR.BUG2) => return error.FFmpegBug,
@intFromEnum(ERROR.BUFFER_TOO_SMALL) => return error.BufferTooSmall,
@intFromEnum(ERROR.DECODER_NOT_FOUND) => return error.DecoderNotFound,
@intFromEnum(ERROR.DEMUXER_NOT_FOUND) => return error.DemuxerNotFound,
@intFromEnum(ERROR.ENCODER_NOT_FOUND) => return error.EncoderNotFound,
@intFromEnum(ERROR.EOF) => return error.EndOfFile,
@intFromEnum(ERROR.EXIT) => return error.FFmpegExit,
@intFromEnum(ERROR.EXTERNAL) => return error.FFmpegDependencyFailure,
@intFromEnum(ERROR.UNKNOWN) => return error.FFmpegDependencyFailure,
@intFromEnum(ERROR.FILTER_NOT_FOUND) => return error.FilterNotFound,
@intFromEnum(ERROR.INVALIDDATA) => return error.InvalidData,
@intFromEnum(ERROR.MUXER_NOT_FOUND) => return error.MuxerNotFound,
@intFromEnum(ERROR.OPTION_NOT_FOUND) => return error.OptionNotFound,
@intFromEnum(ERROR.PATCHWELCOME) => return error.FFmpegUnimplemented,
@intFromEnum(ERROR.PROTOCOL_NOT_FOUND) => return error.ProtocolNotFound,
@intFromEnum(ERROR.STREAM_NOT_FOUND) => return error.StreamNotFound,
@intFromEnum(ERROR.EXPERIMENTAL) => return error.FFmpegExperimentalFeature,
@intFromEnum(ERROR.INPUT_CHANGED) => unreachable, // not legal to use with wrap()
@intFromEnum(ERROR.OUTPUT_CHANGED) => unreachable, // not legal to use with wrap()
@intFromEnum(ERROR.HTTP_BAD_REQUEST) => return error.HttpBadRequest,
@intFromEnum(ERROR.HTTP_UNAUTHORIZED) => return error.HttpUnauthorized,
@intFromEnum(ERROR.HTTP_FORBIDDEN) => return error.HttpForbidden,
@intFromEnum(ERROR.HTTP_NOT_FOUND) => return error.HttpNotFound,
@intFromEnum(ERROR.HTTP_OTHER_4XX) => return error.HttpOther4xx,
@intFromEnum(ERROR.HTTP_SERVER_ERROR) => return error.Http5xx,
else => {
std.log.debug("unexpected ffmpeg error code: {d}", .{averror});
return error.Unexpected;
},
};
}
pub const Error = error{
FileNotFound,
OutOfMemory,
PermissionDenied,
OutOfRange,
/// Bitstream filter not found
BsfNotFound,
/// Internal FFmpeg bug
FFmpegBug,
/// Usually indicates invalid API usage, which would have been an assertion
/// rather than an error, but is also returned for input files that failed
/// to demux or decode.
FFmpegInvalid,
BufferTooSmall,
DecoderNotFound,
DemuxerNotFound,
EncoderNotFound,
/// * The decoder has been flushed, and no new packets can be sent to it
/// (also returned if more than 1 flush packet is sent)
/// * The codec has been fully flushed, and there will be no more output
/// frames.
EndOfFile,
/// Immediate exit was requested; the called function should not be restarted.
FFmpegExit,
/// Generic error in an external library.
FFmpegDependencyFailure,
FilterNotFound,
/// Invalid data found when processing input
InvalidData,
MuxerNotFound,
OptionNotFound,
/// Not yet implemented in FFmpeg, patches welcome.
FFmpegUnimplemented,
ProtocolNotFound,
StreamNotFound,
/// Requested feature is flagged experimental. Set strict_std_compliance if you really want to use it.
FFmpegExperimentalFeature,
/// * input is not accepted in the current state - user must read output with
/// `Codec.Context.receive_frame` (once all output is read, the packet
/// should be resent, and the call will not fail with WouldBlock).
/// * output is not available in this state - user must try to send new input.
WouldBlock,
HttpBadRequest,
HttpUnauthorized,
HttpForbidden,
HttpNotFound,
HttpOther4xx,
Http5xx,
/// FFmpeg returned an undocumented error code.
Unexpected,
};
pub const ERROR = enum(c_int) {
BSF_NOT_FOUND = TAG(0xF8, 'B', 'S', 'F'),
BUG = TAG('B', 'U', 'G', '!'),
BUFFER_TOO_SMALL = TAG('B', 'U', 'F', 'S'),
DECODER_NOT_FOUND = TAG(0xF8, 'D', 'E', 'C'),
DEMUXER_NOT_FOUND = TAG(0xF8, 'D', 'E', 'M'),
ENCODER_NOT_FOUND = TAG(0xF8, 'E', 'N', 'C'),
EOF = TAG('E', 'O', 'F', ' '),
EXIT = TAG('E', 'X', 'I', 'T'),
EXTERNAL = TAG('E', 'X', 'T', ' '),
FILTER_NOT_FOUND = TAG(0xF8, 'F', 'I', 'L'),
INVALIDDATA = TAG('I', 'N', 'D', 'A'),
MUXER_NOT_FOUND = TAG(0xF8, 'M', 'U', 'X'),
OPTION_NOT_FOUND = TAG(0xF8, 'O', 'P', 'T'),
PATCHWELCOME = TAG('P', 'A', 'W', 'E'),
PROTOCOL_NOT_FOUND = TAG(0xF8, 'P', 'R', 'O'),
STREAM_NOT_FOUND = TAG(0xF8, 'S', 'T', 'R'),
BUG2 = TAG('B', 'U', 'G', ' '),
UNKNOWN = TAG('U', 'N', 'K', 'N'),
EXPERIMENTAL = -@as(i32, @bitCast(@as(u32, 0x2bb2afa8))),
INPUT_CHANGED = -@as(i32, @bitCast(@as(u32, 0x636e6701))),
OUTPUT_CHANGED = -@as(i32, @bitCast(@as(u32, 0x636e6702))),
HTTP_BAD_REQUEST = TAG(0xF8, '4', '0', '0'),
HTTP_UNAUTHORIZED = TAG(0xF8, '4', '0', '1'),
HTTP_FORBIDDEN = TAG(0xF8, '4', '0', '3'),
HTTP_NOT_FOUND = TAG(0xF8, '4', '0', '4'),
HTTP_OTHER_4XX = TAG(0xF8, '4', 'X', 'X'),
HTTP_SERVER_ERROR = TAG(0xF8, '5', 'X', 'X'),
pub fn TAG(a: u8, b: u8, c: u8, d: u8) i32 {
const aw: u32 = a;
const bw: u32 = b;
const cw: u32 = c;
const dw: u32 = d;
const signed: i32 = (aw << 0) | (bw << 8) | (cw << 16) | (dw << 24);
return -signed;
}
};
/// Format I/O context.
///
/// New fields can be added to the end with minor version bumps.
/// Removal, reordering and changes to existing fields require a major
/// version bump.
/// `@sizeOf(FormatContext)` must not be used outside libav*, use
/// `FormatContext.alloc` to create a `FormatContext`.
///
/// Fields can be accessed through AVOptions (av_opt*),
/// the name string used matches the associated command line parameter name and
/// can be found in libavformat/options_table.h.
/// The AVOption/command line parameter names differ in some cases from the C
/// structure field names for historic reasons or brevity.
pub const FormatContext = extern struct {
/// A class for logging and @ref avoptions. Set by
/// avformat_alloc_context(). Exports (de)muxer private options if they
/// exist.
av_class: *const Class,
/// The input container format.
///
/// Demuxing only, set by `open_input`.
iformat: *const InputFormat,
/// The output container format.
///
/// Muxing only, must be set by the caller before avformat_write_header().
oformat: *const OutputFormat,
/// Format private data. This is an AVOptions-enabled struct
/// if and only if iformat/oformat.priv_class is not NULL.
///
/// - muxing: set by avformat_write_header()
/// - demuxing: set by `open_input`
priv_data: ?*anyopaque,
/// I/O context.
///
/// - demuxing: either set by the user before `open_input` (then
/// the user must close it manually) or set by `open_input`.
/// - muxing: set by the user before avformat_write_header(). The caller must
/// take care of closing / freeing the IO context.
///
/// Do NOT set this field if AVFMT_NOFILE flag is set in
/// iformat/oformat.flags. In such a case, the (de)muxer will handle
/// I/O in some other way and this field will be NULL.
pb: ?*IOContext,
/// Flags signalling stream properties. A combination of AVFMTCTX_*.
/// Set by libavformat.
ctx_flags: c_int,
/// Number of elements in AVFormatContext.streams.
///
/// Set by avformat_new_stream(), must not be modified by any other code.
nb_streams: c_uint,
/// A list of all streams in the file. New streams are created with
/// avformat_new_stream().
///
/// - demuxing: streams are created by libavformat in `open_input`.
/// If AVFMTCTX_NOHEADER is set in ctx_flags, then new streams may also
/// appear in av_read_frame().
/// - muxing: streams are created by the user before avformat_write_header().
///
/// Freed by libavformat in avformat_free_context().
streams: [*]*Stream,
/// Number of elements in `stream_groups`.
///
/// Set by avformat_stream_group_create(); must not be modified by any other code.
nb_stream_groups: c_uint,
/// A list of all stream groups in the file.
///
/// New groups are created with avformat_stream_group_create(), and filled
/// with avformat_stream_group_add_stream().
///
/// - demuxing: groups may be created by libavformat in avformat_open_input().
/// If AVFMTCTX_NOHEADER is set in ctx_flags, then new groups may also
/// appear in av_read_frame().
/// - muxing: groups may be created by the user before avformat_write_header().
///
/// Freed by libavformat in `free`.
stream_groups: [*]*StreamGroup,
/// Number of chapters in `Chapter` array.
/// When muxing, chapters are normally written in the file header,
/// so nb_chapters should normally be initialized before write_header
/// is called. Some muxers (e.g. mov and mkv) can also write chapters
/// in the trailer. To write chapters in the trailer, nb_chapters
/// must be zero when write_header is called and non-zero when
/// write_trailer is called.
/// - muxing: set by user
/// - demuxing: set by libavformat
nb_chapters: c_uint,
chapters: [*]*Chapter,
/// input or output URL. Unlike the old filename field, this field has no
/// length restriction.
///
/// - demuxing: set by `open_input`, initialized to an empty
/// string if url parameter was NULL in `open_input`.
/// - muxing: may be set by the caller before calling avformat_write_header()
/// (or avformat_init_output() if that is called first) to a string
/// which is freeable by av_free(). Set to an empty string if it
/// was NULL in avformat_init_output().
///
/// Freed by libavformat in avformat_free_context().
url: [*:0]u8,
/// Position of the first frame of the component, in
/// AV_TIME_BASE fractional seconds. NEVER set this value directly:
/// It is deduced from the AVStream values.
///
/// Demuxing only, set by libavformat.
start_time: i64,
/// Duration of the stream, in AV_TIME_BASE fractional
/// seconds. Only set this value if you know none of the individual stream
/// durations and also do not set any of them. This is deduced from the
/// AVStream values if not set.
///
/// Demuxing only, set by libavformat.
duration: i64,
/// Total stream bitrate in bit/s, 0 if not
/// available. Never set it directly if the file_size and the
/// duration are known as FFmpeg can compute it automatically.
bit_rate: i64,
packet_size: c_uint,
max_delay: c_int,
/// Flags modifying the (de)muxer behaviour. A combination of AVFMT_FLAG_*.
/// Set by the user before `open_input` / avformat_write_header().
flags: c_int,
/// Maximum number of bytes read from input in order to determine stream
/// properties. Used when reading the global header and in
/// avformat_find_stream_info().
///
/// Demuxing only, set by the caller before `open_input`.
///
/// @note this is \e not used for determining the \ref AVInputFormat
/// "input format"
/// @sa format_probesize
probesize: i64,
/// Maximum duration (in AV_TIME_BASE units) of the data read
/// from input in avformat_find_stream_info().
/// Demuxing only, set by the caller before avformat_find_stream_info().
/// Can be set to 0 to let avformat choose using a heuristic.
max_analyze_duration: i64,
key: [*]const u8,
keylen: c_int,
nb_programs: c_uint,
programs: [*]*Program,
/// Forced video codec_id.
/// Demuxing: Set by user.
video_codec_id: Codec.ID,
/// Forced audio codec_id.
/// Demuxing: Set by user.
audio_codec_id: Codec.ID,
/// Forced subtitle codec_id.
/// Demuxing: Set by user.
subtitle_codec_id: Codec.ID,
/// Forced Data codec_id.
/// Demuxing: Set by user.
data_codec_id: Codec.ID,
/// Metadata that applies to the whole file.
///
/// - demuxing: set by libavformat in `open_input`
/// - muxing: may be set by the caller before avformat_write_header()
///
/// Freed by libavformat in avformat_free_context().
metadata: Dictionary.Mutable,
/// Start time of the stream in real world time, in microseconds
/// since the Unix epoch (00:00 1st January 1970). That is, pts=0 in the
/// stream was captured at this real world time.
/// - muxing: Set by the caller before avformat_write_header(). If set to
/// either 0 or AV_NOPTS_VALUE, then the current wall-time will
/// be used.
/// - demuxing: Set by libavformat. AV_NOPTS_VALUE if unknown. Note that
/// the value may become known after some number of frames
/// have been received.
start_time_realtime: i64,
/// The number of frames used for determining the framerate in
/// avformat_find_stream_info().
/// Demuxing only, set by the caller before avformat_find_stream_info().
fps_probe_size: c_int,
/// Error recognition; higher values will detect more errors but may
/// misdetect some more or less valid parts as errors.
/// Demuxing only, set by the caller before `open_input`.
error_recognition: c_int,
/// Custom interrupt callbacks for the I/O layer.
///
/// demuxing: set by the user before `open_input`.
/// muxing: set by the user before avformat_write_header()
/// (mainly useful for AVFMT_NOFILE formats). The callback
/// should also be passed to avio_open2() if it's used to
/// open the file.
interrupt_callback: IOInterruptCB,
/// Flags to enable debugging.
debug: c_int,
/// The maximum number of streams.
/// - encoding: unused
/// - decoding: set by user
max_streams: c_int,
/// Maximum amount of memory in bytes to use for the index of each stream.
/// If the index exceeds this size, entries will be discarded as
/// needed to maintain a smaller size. This can lead to slower or less
/// accurate seeking (depends on demuxer).
/// Demuxers for which a full in-memory index is mandatory will ignore
/// this.
/// - muxing: unused
/// - demuxing: set by user
max_index_size: c_uint,
/// Maximum amount of memory in bytes to use for buffering frames
/// obtained from realtime capture devices.
max_picture_buffer: c_uint,
/// Maximum buffering duration for interleaving.
///
/// To ensure all the streams are interleaved correctly,
/// av_interleaved_write_frame() will wait until it has at least one packet
/// for each stream before actually writing any packets to the output file.
/// When some streams are "sparse" (i.e. there are large gaps between
/// successive packets), this can result in excessive buffering.
///
/// This field specifies the maximum difference between the timestamps of the
/// first and the last packet in the muxing queue, above which libavformat
/// will output a packet regardless of whether it has queued a packet for all
/// the streams.
///
/// Muxing only, set by the caller before avformat_write_header().
max_interleave_delta: i64,
/// Maximum number of packets to read while waiting for the first timestamp.
/// Decoding only.
max_ts_probe: c_int,
/// Max chunk time in microseconds.
/// Note, not all formats support this and unpredictable things may happen if it is used when not supported.
/// - encoding: Set by user
/// - decoding: unused
max_chunk_duration: c_int,
/// Max chunk size in bytes
/// Note, not all formats support this and unpredictable things may happen if it is used when not supported.
/// - encoding: Set by user
/// - decoding: unused
max_chunk_size: c_int,
/// Maximum number of packets that can be probed
/// - encoding: unused
/// - decoding: set by user
max_probe_packets: c_int,
/// Allow non-standard and experimental extension
/// See `Codec.Context.strict_std_compliance`
strict_std_compliance: c_int,
/// Flags indicating events happening on the file, a combination of
/// AVFMT_EVENT_FLAG_*.
///
/// - demuxing: may be set by the demuxer in `open_input`,
/// avformat_find_stream_info() and av_read_frame(). Flags must be cleared
/// by the user once the event has been handled.
/// - muxing: may be set by the user after avformat_write_header() to
/// indicate a user-triggered event. The muxer will clear the flags for
/// events it has handled in av_[interleaved]_write_frame().
event_flags: c_int,
/// Avoid negative timestamps during muxing.
/// Any value of the AVFMT_AVOID_NEG_TS_* constants.
/// Note, this works better when using av_interleaved_write_frame().
/// - muxing: Set by user
/// - demuxing: unused
avoid_negative_ts: c_int,
/// Audio preload in microseconds.
/// Note, not all formats support this and unpredictable things may happen if it is used when not supported.
/// - encoding: Set by user
/// - decoding: unused
audio_preload: c_int,
/// forces the use of wallclock timestamps as pts/dts of packets
/// This has undefined results in the presence of B frames.
/// - encoding: unused
/// - decoding: Set by user
use_wallclock_as_timestamps: c_int,
/// Skip duration calcuation in estimate_timings_from_pts.
/// - encoding: unused
/// - decoding: set by user
skip_estimate_duration_from_pts: c_int,
/// used to force AVIO_FLAG_DIRECT.
/// - encoding: unused
/// - decoding: Set by user
avio_flags: c_int,
/// The duration field can be estimated through various ways, and this field can be used
/// to know how the duration was estimated.
/// - encoding: unused
/// - decoding: Read by user
duration_estimation_method: DurationEstimationMethod,
/// Skip initial bytes when opening stream
/// - encoding: unused
/// - decoding: Set by user
skip_initial_bytes: i64,
/// Correct single timestamp overflows
/// - encoding: unused
/// - decoding: Set by user
correct_ts_overflow: c_uint,
/// Force seeking to any (also non key) frames.
/// - encoding: unused
/// - decoding: Set by user
seek2any: c_int,
/// Flush the I/O context after each packet.
/// - encoding: Set by user
/// - decoding: unused
flush_packets: c_int,
/// format probing score.
///
/// The maximal score is AVPROBE_SCORE_MAX, its set when the demuxer probes
/// the format.
/// - encoding: unused
/// - decoding: set by avformat, read by user
probe_score: c_int,
/// Maximum number of bytes read from input in order to identify the
/// \ref AVInputFormat "input format". Only used when the format is not set
/// explicitly by the caller.
///
/// Demuxing only, set by the caller before `open_input`.
///
/// @sa probesize
format_probesize: c_int,
/// ',' separated list of allowed decoders.
/// If NULL then all are allowed
/// - encoding: unused
/// - decoding: set by user
codec_whitelist: [*:0]u8,
/// ',' separated list of allowed demuxers.
/// If NULL then all are allowed
/// - encoding: unused
/// - decoding: set by user
format_whitelist: [*:0]u8,
/// ',' separated list of allowed protocols.
/// - encoding: unused
/// - decoding: set by user
protocol_whitelist: [*:0]u8,
/// ',' separated list of disallowed protocols.
/// - encoding: unused
/// - decoding: set by user
protocol_blacklist: [*:0]u8,
/// IO repositioned flag.
/// This is set by avformat when the underlaying IO context read pointer
/// is repositioned, for example when doing byte based seeking.
/// Demuxers can use the flag to detect such changes.
io_repositioned: c_int,
/// Forced video codec.
///
/// This allows forcing a specific decoder, even when there are multiple with
/// the same codec_id.
/// Demuxing: Set by user
video_codec: *const Codec,
/// Forced audio codec.
///
/// This allows forcing a specific decoder, even when there are multiple with
/// the same codec_id.
/// Demuxing: Set by user
audio_codec: *const Codec,
/// Forced subtitle codec.
///
/// This allows forcing a specific decoder, even when there are multiple with
/// the same codec_id.
/// Demuxing: Set by user
subtitle_codec: *const Codec,
/// Forced data codec.
///
/// This allows forcing a specific decoder, even when there are multiple with
/// the same codec_id.
/// Demuxing: Set by user
data_codec: *const Codec,
/// Number of bytes to be written as padding in a metadata header.
///
/// Demuxing: Unused.
/// Muxing: Set by user via av_format_set_metadata_header_padding.
metadata_header_padding: c_int,
/// User data.
/// This is a place for some private data of the user.
@"opaque": ?*anyopaque,
/// Callback used by devices to communicate with application.
control_message_cb: av_format_control_message,
/// Output timestamp offset, in microseconds.
/// Muxing: set by user
output_ts_offset: i64,
/// dump format separator.
/// can be ", " or "\n " or anything else
/// - muxing: Set by user.
/// - demuxing: Set by user.
dump_separator: *u8,
/// A callback for opening new IO streams.
///
/// Whenever a muxer or a demuxer needs to open an IO stream (typically from
/// `open_input` for demuxers, but for certain formats can happen at
/// other times as well), it will call this callback to obtain an IO context.
///
/// @param s the format context
/// @param pb on success, the newly opened IO context should be returned here
/// @param url the url to open
/// @param flags a combination of AVIO_FLAG_*
/// @param options a dictionary of additional options, with the same
/// semantics as in avio_open2()
/// @return 0 on success, a negative AVERROR code on failure
///
/// @note Certain muxers and demuxers do nesting, i.e. they open one or more
/// additional internal format contexts. Thus the AVFormatContext pointer
/// passed to this callback may be different from the one facing the caller.
/// It will, however, have the same 'opaque' field.
io_open: ?*const fn (*FormatContext, **IOContext, [*]const u8, c_int, *Dictionary.Mutable) callconv(.C) c_int,
/// A callback for closing the streams opened with AVFormatContext.io_open().
///
/// Using this is preferred over io_close, because this can return an error.
/// Therefore this callback is used instead of io_close by the generic
/// libavformat code if io_close is NULL or the default.
///
/// @param s the format context
/// @param pb IO context to be closed and freed
/// @return 0 on success, a negative AVERROR code on failure
io_close2: ?*const fn (*FormatContext, *IOContext) callconv(.C) c_int,
/// `free` can be used to free the context and everything
/// allocated by the framework within it.
pub fn alloc() error{OutOfMemory}!*FormatContext {
return avformat_alloc_context() orelse return error.OutOfMemory;
}
// Free a `FormatContext` and all its streams.
pub const free = avformat_free_context;
/// Open an input stream and read the header.
///
/// The codecs are not opened. The stream must be closed with
/// `close_input`.
pub fn open_input(
/// URL of the stream to open.
url: [*:0]const u8,
/// If non-NULL, this parameter forces a specific input format.
/// Otherwise the format is autodetected.
fmt: ?*const InputFormat,
/// A dictionary filled with `FormatContext` and demuxer-private
/// options.
///
/// On return this parameter will be destroyed and replaced with
/// a dict containing options that were not found. May be NULL.
options: ?*Dictionary.Mutable,
pb: ?*IOContext,
) Error!*FormatContext {
var ps: ?*FormatContext = try alloc();
ps.?.pb = pb;
// avformat_open_input takes ownership of the allocation.
_ = try wrap(avformat_open_input(&ps, url, fmt, options));
return ps.?;
}
/// Close an opened input `FormatContext`. Free it and all its contents.
pub fn close_input(s: *FormatContext) void {
var keep_your_dirty_hands_off_my_pointers_ffmpeg: ?*FormatContext = s;
avformat_close_input(&keep_your_dirty_hands_off_my_pointers_ffmpeg);
}
/// Read packets of a media file to get stream information.
///
/// This is useful for file formats with no headers such as MPEG. This
/// function also computes the real framerate in case of MPEG-2 repeat
/// frame mode.
///
/// The logical file position is not changed by this function; examined
/// packets may be buffered for later processing.
///
/// This function isn't guaranteed to open all the codecs, so
/// options being non-empty at return is a perfectly normal behavior.
///
/// Does not let the user decide somehow what information is needed;
/// sometimes wastes time getting stuff the user does not need.
pub fn find_stream_info(
ic: *FormatContext,
/// If non-NULL, an ic.nb_streams long array of pointers to
/// dictionaries, where i-th member contains options for codec
/// corresponding to i-th stream.
///
/// On return each dictionary will be filled with options that were not found.
options: ?[*]Dictionary.Mutable,
) Error!void {
_ = try wrap(avformat_find_stream_info(ic, options));
}
/// Find the "best" stream in the file.
///
/// The best stream is determined according to various heuristics as the most
/// likely to be what the user expects.
///
/// If the decoder parameter is non-NULL, `find_best_stream` will find the
/// default decoder for the stream's codec; streams for which no decoder can
/// be found are ignored.
///
/// Returns the non-negative stream number in case of success,
/// AVERROR_STREAM_NOT_FOUND if no stream with the requested type could be
/// found, AVERROR_DECODER_NOT_FOUND if streams were found but no decoder
///
pub fn find_best_stream(
ic: *FormatContext,
/// stream type: video, audio, subtitles, etc.
media_type: MediaType,
/// user-requested stream number, or -1 for automatic selection
wanted_stream_nb: c_int,
/// try to find a stream related (eg. in the same program) to this one,
/// or -1 if none
related_stream: c_int,
) Error!struct { c_uint, *const Codec } {
var decoder: ?*const Codec = undefined;
const n = try wrap(av_find_best_stream(ic, media_type, wanted_stream_nb, related_stream, &decoder, 0));
return .{ n, decoder.? };
}
/// Return the next frame of a stream.
///
/// This function returns what is stored in the file, and does not validate
/// that what is there are valid frames for the decoder. It will split what
/// is stored in the file into frames and return one for each call. It will
/// not omit invalid data between valid frames so as to give the decoder
/// the maximum information possible for decoding.
///
/// On success, the returned packet is reference-counted (pkt->buf is set)
/// and valid indefinitely. The packet must be freed with av_packet_unref()
/// when it is no longer needed. For video, the packet contains exactly one
/// frame. For audio, it contains an integer number of frames if each frame
/// has a known fixed size (e.g. PCM or ADPCM data). If the audio frames
/// have a variable size (e.g. MPEG audio), then it contains one frame.
///
/// pkt->pts, pkt->dts and pkt->duration are always set to correct values
/// in `Stream.time_base` units (and guessed if the format cannot provide
/// them). pkt->pts can be `NOPTS_VALUE` if the video format has B-frames,
/// so it is better to rely on pkt->dts if you do not decompress the
/// payload.
///
/// Returns 0 if OK, < 0 on error or end of file. On error, pkt will be
/// blank (as if it came from `Packet.alloc`).
///
/// `pkt` will be initialized, so it may be uninitialized, but it must not
/// contain data that needs to be freed.
pub fn read_frame(s: *FormatContext, pkt: *Packet) Error!void {
_ = try wrap(av_read_frame(s, pkt));
}
/// Seek to the keyframe at timestamp in the specified stream.
pub fn seek_frame(
/// media file handle
s: *FormatContext,
/// If stream_index is (-1), a default stream is selected, and
/// timestamp is automatically converted from AV_TIME_BASE units to the
/// stream specific time_base.
stream_index: c_int,
/// In AVStream.time_base units or, if no stream is specified, in
/// AV_TIME_BASE units.
timestamp: i64,
/// select direction and seeking mode
flags: c_int,
) Error!void {
_ = try wrap(av_seek_frame(s, stream_index, timestamp, flags));
}
/// Discard all internally buffered data. This can be useful when dealing with
/// discontinuities in the byte stream. Generally works only with formats that
/// can resync. This includes headerless formats like MPEG-TS/TS but should also
/// work with NUT, Ogg and in a limited way AVI for example.
///
/// The set of streams, the detected duration, stream parameters and codecs do
/// not change when calling this function. If you want a complete reset, it's
/// better to open a new `FormatContext`.
///
/// This does not flush the `IOContext` (s->pb). If necessary, call
/// avio_flush(s->pb) before calling this function.
///
/// @return >=0 on success, error code otherwise
pub fn flush(s: *FormatContext) Error!void {
_ = try wrap(avformat_flush(s));
}
/// Print detailed information about the input or output format, such as
/// duration, bitrate, streams, container, programs, metadata, side data,
/// codec and time base.
pub const dump = av_dump_format;
};
pub const Class = extern struct {
class_name: [*c]const u8,
item_name: ?*const fn (?*anyopaque) callconv(.C) [*c]const u8,
option: ?*const Option_6,
version: c_int,
log_level_offset_offset: c_int,
parent_log_context_offset: c_int,
category: ClassCategory,
get_category: ?*const fn (?*anyopaque) callconv(.C) ClassCategory,
query_ranges: ?*const fn ([*c]?*OptionRanges, ?*anyopaque, [*c]const u8, c_int) callconv(.C) c_int,
child_next: ?*const fn (?*anyopaque, ?*anyopaque) callconv(.C) ?*anyopaque,
child_class_iterate: ?*const fn ([*c]?*anyopaque) callconv(.C) [*c]const Class,
};
pub const InputFormat = extern struct {
name: [*c]const u8,
long_name: [*c]const u8,
flags: c_int,
extensions: [*c]const u8,
codec_tag: [*c]const ?*const CodecTag,
priv_class: [*c]const Class,
mime_type: [*c]const u8,