xref: /aosp_15_r20/external/libdav1d/tools/output/output.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 #include "cli_config.h"
30 
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 
36 #include "common/attributes.h"
37 #include "common/intops.h"
38 
39 #include "output/output.h"
40 #include "output/muxer.h"
41 
42 struct MuxerContext {
43     MuxerPriv *data;
44     const Muxer *impl;
45     int one_file_per_frame;
46     unsigned fps[2];
47     const char *filename;
48     int framenum;
49     uint64_t priv_data[];
50 };
51 
52 extern const Muxer null_muxer;
53 extern const Muxer md5_muxer;
54 extern const Muxer xxh3_muxer;
55 extern const Muxer yuv_muxer;
56 extern const Muxer y4m2_muxer;
57 static const Muxer *muxers[] = {
58     &null_muxer,
59     &md5_muxer,
60 #if HAVE_XXHASH_H
61     &xxh3_muxer,
62 #endif
63     &yuv_muxer,
64     &y4m2_muxer,
65     NULL
66 };
67 
find_extension(const char * const f)68 static const char *find_extension(const char *const f) {
69     const size_t l = strlen(f);
70 
71     if (l == 0) return NULL;
72 
73     const char *const end = &f[l - 1], *step = end;
74     while ((*step >= 'a' && *step <= 'z') ||
75            (*step >= 'A' && *step <= 'Z') ||
76            (*step >= '0' && *step <= '9'))
77     {
78         step--;
79     }
80 
81     return (step < end && step > f && *step == '.' && step[-1] != '/') ?
82            &step[1] : NULL;
83 }
84 
output_open(MuxerContext ** const c_out,const char * const name,const char * const filename,const Dav1dPictureParameters * const p,const unsigned fps[2])85 int output_open(MuxerContext **const c_out,
86                 const char *const name, const char *const filename,
87                 const Dav1dPictureParameters *const p, const unsigned fps[2])
88 {
89     const Muxer *impl;
90     MuxerContext *c;
91     unsigned i;
92     int res;
93     int name_offset = 0;
94 
95     if (name) {
96         name_offset = 5 * !strncmp(name, "frame", 5);
97         for (i = 0; muxers[i]; i++) {
98             if (!strcmp(muxers[i]->name, &name[name_offset])) {
99                 impl = muxers[i];
100                 break;
101             }
102         }
103         if (!muxers[i]) {
104             fprintf(stderr, "Failed to find muxer named \"%s\"\n", name);
105             return DAV1D_ERR(ENOPROTOOPT);
106         }
107     } else if (!strcmp(filename, "/dev/null")) {
108         impl = muxers[0];
109     } else {
110         const char *const ext = find_extension(filename);
111         if (!ext) {
112             fprintf(stderr, "No extension found for file %s\n", filename);
113             return -1;
114         }
115         for (i = 0; muxers[i]; i++) {
116             if (!strcmp(muxers[i]->extension, ext)) {
117                 impl = muxers[i];
118                 break;
119             }
120         }
121         if (!muxers[i]) {
122             fprintf(stderr, "Failed to find muxer for extension \"%s\"\n", ext);
123             return DAV1D_ERR(ENOPROTOOPT);
124         }
125     }
126 
127     if (!(c = malloc(offsetof(MuxerContext, priv_data) + impl->priv_data_size))) {
128         fprintf(stderr, "Failed to allocate memory\n");
129         return DAV1D_ERR(ENOMEM);
130     }
131     c->impl = impl;
132     c->data = (MuxerPriv *) c->priv_data;
133     int have_num_pattern = 0;
134     for (const char *ptr = filename ? strchr(filename, '%') : NULL;
135          !have_num_pattern && ptr; ptr = strchr(ptr, '%'))
136     {
137         ptr++; // skip '%'
138         while (*ptr >= '0' && *ptr <= '9')
139             ptr++; // skip length indicators
140         have_num_pattern = *ptr == 'n';
141     }
142     c->one_file_per_frame = name_offset || (!name && have_num_pattern);
143 
144     if (c->one_file_per_frame) {
145         c->fps[0] = fps[0];
146         c->fps[1] = fps[1];
147         c->filename = filename;
148         c->framenum = 0;
149     } else if (impl->write_header &&
150                (res = impl->write_header(c->data, filename, p, fps)) < 0)
151     {
152         free(c);
153         return res;
154     }
155     *c_out = c;
156 
157     return 0;
158 }
159 
safe_strncat(char * const dst,const int dst_len,const char * const src,const int src_len)160 static void safe_strncat(char *const dst, const int dst_len,
161                          const char *const src, const int src_len)
162 {
163     if (!src_len) return;
164     const int dst_fill = (int) strlen(dst);
165     assert(dst_fill < dst_len);
166     const int to_copy = imin(src_len, dst_len - dst_fill - 1);
167     if (!to_copy) return;
168     memcpy(dst + dst_fill, src, to_copy);
169     dst[dst_fill + to_copy] = 0;
170 }
171 
assemble_field(char * const dst,const int dst_len,const char * const fmt,const int fmt_len,const int field)172 static void assemble_field(char *const dst, const int dst_len,
173                            const char *const fmt, const int fmt_len,
174                            const int field)
175 {
176     char fmt_copy[32];
177 
178     assert(fmt[0] == '%');
179     fmt_copy[0] = '%';
180     if (fmt[1] >= '1' && fmt[1] <= '9') {
181         fmt_copy[1] = '0'; // pad with zeroes, not spaces
182         fmt_copy[2] = 0;
183     } else {
184         fmt_copy[1] = 0;
185     }
186     safe_strncat(fmt_copy, sizeof(fmt_copy), &fmt[1], fmt_len - 1);
187     safe_strncat(fmt_copy, sizeof(fmt_copy), "d", 1);
188 
189     char tmp[32];
190     snprintf(tmp, sizeof(tmp), fmt_copy, field);
191 
192     safe_strncat(dst, dst_len, tmp, (int) strlen(tmp));
193 }
194 
assemble_filename(MuxerContext * const ctx,char * const filename,const int filename_size,const Dav1dPictureParameters * const p)195 static void assemble_filename(MuxerContext *const ctx, char *const filename,
196                               const int filename_size,
197                               const Dav1dPictureParameters *const p)
198 {
199     filename[0] = 0;
200     const int framenum = ctx->framenum++;
201     assert(ctx->filename);
202     const char *ptr = ctx->filename, *iptr;
203     while ((iptr = strchr(ptr, '%'))) {
204         safe_strncat(filename, filename_size, ptr, (int) (iptr - ptr));
205         ptr = iptr;
206 
207         const char *iiptr = &iptr[1]; // skip '%'
208         while (*iiptr >= '0' && *iiptr <= '9')
209             iiptr++; // skip length indicators
210 
211         switch (*iiptr) {
212         case 'w':
213             assemble_field(filename, filename_size, ptr, (int) (iiptr - ptr), p->w);
214             break;
215         case 'h':
216             assemble_field(filename, filename_size, ptr, (int) (iiptr - ptr), p->h);
217             break;
218         case 'n':
219             assemble_field(filename, filename_size, ptr, (int) (iiptr - ptr), framenum);
220             break;
221         default:
222             safe_strncat(filename, filename_size, "%", 1);
223             ptr = &iptr[1];
224             continue;
225         }
226 
227         ptr = &iiptr[1];
228     }
229     safe_strncat(filename, filename_size, ptr, (int) strlen(ptr));
230 }
231 
output_write(MuxerContext * const ctx,Dav1dPicture * const p)232 int output_write(MuxerContext *const ctx, Dav1dPicture *const p) {
233     int res;
234 
235     if (ctx->one_file_per_frame && ctx->impl->write_header) {
236         char filename[1024];
237         assemble_filename(ctx, filename, sizeof(filename), &p->p);
238         res = ctx->impl->write_header(ctx->data, filename, &p->p, ctx->fps);
239         if (res < 0)
240             return res;
241     }
242     if ((res = ctx->impl->write_picture(ctx->data, p)) < 0)
243         return res;
244     if (ctx->one_file_per_frame && ctx->impl->write_trailer)
245         ctx->impl->write_trailer(ctx->data);
246 
247     return 0;
248 }
249 
output_close(MuxerContext * const ctx)250 void output_close(MuxerContext *const ctx) {
251     if (!ctx->one_file_per_frame && ctx->impl->write_trailer)
252         ctx->impl->write_trailer(ctx->data);
253     free(ctx);
254 }
255 
output_verify(MuxerContext * const ctx,const char * const md5_str)256 int output_verify(MuxerContext *const ctx, const char *const md5_str) {
257     const int res = ctx->impl->verify ?
258         ctx->impl->verify(ctx->data, md5_str) : 0;
259     free(ctx);
260     return res;
261 }
262