xref: /aosp_15_r20/external/libdav1d/tools/input/ivf.c (revision c09093415860a1c2373dacd84c4fde00c507cdfd)
1 /*
2  * Copyright © 2018, VideoLAN and dav1d authors
3  * Copyright © 2018, Two Orioles, LLC
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice, this
10  *    list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  *    this list of conditions and the following disclaimer in the documentation
14  *    and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "config.h"
29 
30 #include <errno.h>
31 #include <limits.h>
32 #include <math.h>
33 #include <stdio.h>
34 #include <stdint.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 #include "input/demuxer.h"
39 
40 typedef struct DemuxerPriv {
41     FILE *f;
42     int broken;
43     double timebase;
44     uint64_t last_ts;
45     uint64_t step;
46 } IvfInputContext;
47 
48 static const uint8_t probe_data[] = {
49     'D', 'K', 'I', 'F',
50     0, 0, 0x20, 0,
51     'A', 'V', '0', '1',
52 };
53 
ivf_probe(const uint8_t * const data)54 static int ivf_probe(const uint8_t *const data) {
55     return !memcmp(data, probe_data, sizeof(probe_data));
56 }
57 
rl32(const uint8_t * const p)58 static unsigned rl32(const uint8_t *const p) {
59     return ((uint32_t)p[3] << 24U) | (p[2] << 16U) | (p[1] << 8U) | p[0];
60 }
61 
rl64(const uint8_t * const p)62 static int64_t rl64(const uint8_t *const p) {
63     return (((uint64_t) rl32(&p[4])) << 32) | rl32(p);
64 }
65 
ivf_open(IvfInputContext * const c,const char * const file,unsigned fps[2],unsigned * const num_frames,unsigned timebase[2])66 static int ivf_open(IvfInputContext *const c, const char *const file,
67                     unsigned fps[2], unsigned *const num_frames, unsigned timebase[2])
68 {
69     uint8_t hdr[32];
70 
71     if (!(c->f = fopen(file, "rb"))) {
72         fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
73         return -1;
74     } else if (fread(hdr, 32, 1, c->f) != 1) {
75         fprintf(stderr, "Failed to read stream header: %s\n", strerror(errno));
76         fclose(c->f);
77         return -1;
78     } else if (memcmp(hdr, "DKIF", 4)) {
79         fprintf(stderr, "%s is not an IVF file [tag=%.4s|0x%02x%02x%02x%02x]\n",
80                 file, hdr, hdr[0], hdr[1], hdr[2], hdr[3]);
81         fclose(c->f);
82         return -1;
83     } else if (memcmp(&hdr[8], "AV01", 4)) {
84         fprintf(stderr, "%s is not an AV1 file [tag=%.4s|0x%02x%02x%02x%02x]\n",
85                 file, &hdr[8], hdr[8], hdr[9], hdr[10], hdr[11]);
86         fclose(c->f);
87         return -1;
88     }
89 
90     timebase[0] = rl32(&hdr[16]);
91     timebase[1] = rl32(&hdr[20]);
92     const unsigned duration = rl32(&hdr[24]);
93 
94     uint8_t data[8];
95     c->broken = 0;
96     for (*num_frames = 0;; (*num_frames)++) {
97         if (fread(data, 4, 1, c->f) != 1) break; // EOF
98         size_t sz = rl32(data);
99         if (fread(data, 8, 1, c->f) != 1) break; // EOF
100         const uint64_t ts = rl64(data);
101         if (*num_frames && ts <= c->last_ts)
102             c->broken = 1;
103         c->last_ts = ts;
104         fseeko(c->f, sz, SEEK_CUR);
105     }
106 
107     uint64_t fps_num = (uint64_t) timebase[0] * *num_frames;
108     uint64_t fps_den = (uint64_t) timebase[1] * duration;
109     if (fps_num && fps_den) { /* Reduce fraction */
110         uint64_t gcd = fps_num;
111         for (uint64_t a = fps_den, b; (b = a % gcd); a = gcd, gcd = b);
112         fps_num /= gcd;
113         fps_den /= gcd;
114 
115         while ((fps_num | fps_den) > UINT_MAX) {
116             fps_num >>= 1;
117             fps_den >>= 1;
118         }
119     }
120     if (fps_num && fps_den) {
121         fps[0] = (unsigned) fps_num;
122         fps[1] = (unsigned) fps_den;
123     } else {
124         fps[0] = fps[1] = 0;
125     }
126     c->timebase = (double)timebase[0] / timebase[1];
127     c->step = duration / *num_frames;
128 
129     fseeko(c->f, 32, SEEK_SET);
130     c->last_ts = 0;
131 
132     return 0;
133 }
134 
ivf_read_header(IvfInputContext * const c,ptrdiff_t * const sz,int64_t * const off_,uint64_t * const ts)135 static inline int ivf_read_header(IvfInputContext *const c, ptrdiff_t *const sz,
136                                   int64_t *const off_, uint64_t *const ts)
137 {
138     uint8_t data[8];
139     int64_t const off = ftello(c->f);
140     if (off_) *off_ = off;
141     if (fread(data, 4, 1, c->f) != 1) return -1; // EOF
142     *sz = rl32(data);
143     if (!c->broken) {
144         if (fread(data, 8, 1, c->f) != 1) return -1;
145         *ts = rl64(data);
146     } else {
147         if (fseeko(c->f, 8, SEEK_CUR)) return -1;
148         *ts = off > 32 ? c->last_ts + c->step : 0;
149     }
150     return 0;
151 }
152 
ivf_read(IvfInputContext * const c,Dav1dData * const buf)153 static int ivf_read(IvfInputContext *const c, Dav1dData *const buf) {
154     uint8_t *ptr;
155     ptrdiff_t sz;
156     int64_t off;
157     uint64_t ts;
158     if (ivf_read_header(c, &sz, &off, &ts)) return -1;
159     if (!(ptr = dav1d_data_create(buf, sz))) return -1;
160     if (fread(ptr, sz, 1, c->f) != 1) {
161         fprintf(stderr, "Failed to read frame data: %s\n", strerror(errno));
162         dav1d_data_unref(buf);
163         return -1;
164     }
165     buf->m.offset = off;
166     buf->m.timestamp = ts;
167     c->last_ts = ts;
168     return 0;
169 }
170 
ivf_seek(IvfInputContext * const c,const uint64_t pts)171 static int ivf_seek(IvfInputContext *const c, const uint64_t pts) {
172     uint64_t cur;
173     const uint64_t ts = llround((pts * c->timebase) / 1000000000.0);
174     if (ts <= c->last_ts)
175         if (fseeko(c->f, 32, SEEK_SET)) goto error;
176     while (1) {
177         ptrdiff_t sz;
178         if (ivf_read_header(c, &sz, NULL, &cur)) goto error;
179         if (cur >= ts) break;
180         if (fseeko(c->f, sz, SEEK_CUR)) goto error;
181         c->last_ts = cur;
182     }
183     if (fseeko(c->f, -12, SEEK_CUR)) goto error;
184     return 0;
185 error:
186     fprintf(stderr, "Failed to seek: %s\n", strerror(errno));
187     return -1;
188 }
189 
ivf_close(IvfInputContext * const c)190 static void ivf_close(IvfInputContext *const c) {
191     fclose(c->f);
192 }
193 
194 const Demuxer ivf_demuxer = {
195     .priv_data_size = sizeof(IvfInputContext),
196     .name = "ivf",
197     .probe = ivf_probe,
198     .probe_sz = sizeof(probe_data),
199     .open = ivf_open,
200     .read = ivf_read,
201     .seek = ivf_seek,
202     .close = ivf_close,
203 };
204