1*c0909341SAndroid Build Coastguard Worker /*
2*c0909341SAndroid Build Coastguard Worker * Copyright © 2018, VideoLAN and dav1d authors
3*c0909341SAndroid Build Coastguard Worker * Copyright © 2018, Two Orioles, LLC
4*c0909341SAndroid Build Coastguard Worker * All rights reserved.
5*c0909341SAndroid Build Coastguard Worker *
6*c0909341SAndroid Build Coastguard Worker * Redistribution and use in source and binary forms, with or without
7*c0909341SAndroid Build Coastguard Worker * modification, are permitted provided that the following conditions are met:
8*c0909341SAndroid Build Coastguard Worker *
9*c0909341SAndroid Build Coastguard Worker * 1. Redistributions of source code must retain the above copyright notice, this
10*c0909341SAndroid Build Coastguard Worker * list of conditions and the following disclaimer.
11*c0909341SAndroid Build Coastguard Worker *
12*c0909341SAndroid Build Coastguard Worker * 2. Redistributions in binary form must reproduce the above copyright notice,
13*c0909341SAndroid Build Coastguard Worker * this list of conditions and the following disclaimer in the documentation
14*c0909341SAndroid Build Coastguard Worker * and/or other materials provided with the distribution.
15*c0909341SAndroid Build Coastguard Worker *
16*c0909341SAndroid Build Coastguard Worker * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17*c0909341SAndroid Build Coastguard Worker * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18*c0909341SAndroid Build Coastguard Worker * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19*c0909341SAndroid Build Coastguard Worker * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20*c0909341SAndroid Build Coastguard Worker * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21*c0909341SAndroid Build Coastguard Worker * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22*c0909341SAndroid Build Coastguard Worker * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23*c0909341SAndroid Build Coastguard Worker * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24*c0909341SAndroid Build Coastguard Worker * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25*c0909341SAndroid Build Coastguard Worker * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*c0909341SAndroid Build Coastguard Worker */
27*c0909341SAndroid Build Coastguard Worker
28*c0909341SAndroid Build Coastguard Worker #include "config.h"
29*c0909341SAndroid Build Coastguard Worker #include "cli_config.h"
30*c0909341SAndroid Build Coastguard Worker
31*c0909341SAndroid Build Coastguard Worker #include <errno.h>
32*c0909341SAndroid Build Coastguard Worker #include <stdio.h>
33*c0909341SAndroid Build Coastguard Worker #include <stdlib.h>
34*c0909341SAndroid Build Coastguard Worker #include <string.h>
35*c0909341SAndroid Build Coastguard Worker
36*c0909341SAndroid Build Coastguard Worker #include "common/attributes.h"
37*c0909341SAndroid Build Coastguard Worker #include "common/intops.h"
38*c0909341SAndroid Build Coastguard Worker
39*c0909341SAndroid Build Coastguard Worker #include "output/output.h"
40*c0909341SAndroid Build Coastguard Worker #include "output/muxer.h"
41*c0909341SAndroid Build Coastguard Worker
42*c0909341SAndroid Build Coastguard Worker struct MuxerContext {
43*c0909341SAndroid Build Coastguard Worker MuxerPriv *data;
44*c0909341SAndroid Build Coastguard Worker const Muxer *impl;
45*c0909341SAndroid Build Coastguard Worker int one_file_per_frame;
46*c0909341SAndroid Build Coastguard Worker unsigned fps[2];
47*c0909341SAndroid Build Coastguard Worker const char *filename;
48*c0909341SAndroid Build Coastguard Worker int framenum;
49*c0909341SAndroid Build Coastguard Worker uint64_t priv_data[];
50*c0909341SAndroid Build Coastguard Worker };
51*c0909341SAndroid Build Coastguard Worker
52*c0909341SAndroid Build Coastguard Worker extern const Muxer null_muxer;
53*c0909341SAndroid Build Coastguard Worker extern const Muxer md5_muxer;
54*c0909341SAndroid Build Coastguard Worker extern const Muxer xxh3_muxer;
55*c0909341SAndroid Build Coastguard Worker extern const Muxer yuv_muxer;
56*c0909341SAndroid Build Coastguard Worker extern const Muxer y4m2_muxer;
57*c0909341SAndroid Build Coastguard Worker static const Muxer *muxers[] = {
58*c0909341SAndroid Build Coastguard Worker &null_muxer,
59*c0909341SAndroid Build Coastguard Worker &md5_muxer,
60*c0909341SAndroid Build Coastguard Worker #if HAVE_XXHASH_H
61*c0909341SAndroid Build Coastguard Worker &xxh3_muxer,
62*c0909341SAndroid Build Coastguard Worker #endif
63*c0909341SAndroid Build Coastguard Worker &yuv_muxer,
64*c0909341SAndroid Build Coastguard Worker &y4m2_muxer,
65*c0909341SAndroid Build Coastguard Worker NULL
66*c0909341SAndroid Build Coastguard Worker };
67*c0909341SAndroid Build Coastguard Worker
find_extension(const char * const f)68*c0909341SAndroid Build Coastguard Worker static const char *find_extension(const char *const f) {
69*c0909341SAndroid Build Coastguard Worker const size_t l = strlen(f);
70*c0909341SAndroid Build Coastguard Worker
71*c0909341SAndroid Build Coastguard Worker if (l == 0) return NULL;
72*c0909341SAndroid Build Coastguard Worker
73*c0909341SAndroid Build Coastguard Worker const char *const end = &f[l - 1], *step = end;
74*c0909341SAndroid Build Coastguard Worker while ((*step >= 'a' && *step <= 'z') ||
75*c0909341SAndroid Build Coastguard Worker (*step >= 'A' && *step <= 'Z') ||
76*c0909341SAndroid Build Coastguard Worker (*step >= '0' && *step <= '9'))
77*c0909341SAndroid Build Coastguard Worker {
78*c0909341SAndroid Build Coastguard Worker step--;
79*c0909341SAndroid Build Coastguard Worker }
80*c0909341SAndroid Build Coastguard Worker
81*c0909341SAndroid Build Coastguard Worker return (step < end && step > f && *step == '.' && step[-1] != '/') ?
82*c0909341SAndroid Build Coastguard Worker &step[1] : NULL;
83*c0909341SAndroid Build Coastguard Worker }
84*c0909341SAndroid Build Coastguard Worker
output_open(MuxerContext ** const c_out,const char * const name,const char * const filename,const Dav1dPictureParameters * const p,const unsigned fps[2])85*c0909341SAndroid Build Coastguard Worker int output_open(MuxerContext **const c_out,
86*c0909341SAndroid Build Coastguard Worker const char *const name, const char *const filename,
87*c0909341SAndroid Build Coastguard Worker const Dav1dPictureParameters *const p, const unsigned fps[2])
88*c0909341SAndroid Build Coastguard Worker {
89*c0909341SAndroid Build Coastguard Worker const Muxer *impl;
90*c0909341SAndroid Build Coastguard Worker MuxerContext *c;
91*c0909341SAndroid Build Coastguard Worker unsigned i;
92*c0909341SAndroid Build Coastguard Worker int res;
93*c0909341SAndroid Build Coastguard Worker int name_offset = 0;
94*c0909341SAndroid Build Coastguard Worker
95*c0909341SAndroid Build Coastguard Worker if (name) {
96*c0909341SAndroid Build Coastguard Worker name_offset = 5 * !strncmp(name, "frame", 5);
97*c0909341SAndroid Build Coastguard Worker for (i = 0; muxers[i]; i++) {
98*c0909341SAndroid Build Coastguard Worker if (!strcmp(muxers[i]->name, &name[name_offset])) {
99*c0909341SAndroid Build Coastguard Worker impl = muxers[i];
100*c0909341SAndroid Build Coastguard Worker break;
101*c0909341SAndroid Build Coastguard Worker }
102*c0909341SAndroid Build Coastguard Worker }
103*c0909341SAndroid Build Coastguard Worker if (!muxers[i]) {
104*c0909341SAndroid Build Coastguard Worker fprintf(stderr, "Failed to find muxer named \"%s\"\n", name);
105*c0909341SAndroid Build Coastguard Worker return DAV1D_ERR(ENOPROTOOPT);
106*c0909341SAndroid Build Coastguard Worker }
107*c0909341SAndroid Build Coastguard Worker } else if (!strcmp(filename, "/dev/null")) {
108*c0909341SAndroid Build Coastguard Worker impl = muxers[0];
109*c0909341SAndroid Build Coastguard Worker } else {
110*c0909341SAndroid Build Coastguard Worker const char *const ext = find_extension(filename);
111*c0909341SAndroid Build Coastguard Worker if (!ext) {
112*c0909341SAndroid Build Coastguard Worker fprintf(stderr, "No extension found for file %s\n", filename);
113*c0909341SAndroid Build Coastguard Worker return -1;
114*c0909341SAndroid Build Coastguard Worker }
115*c0909341SAndroid Build Coastguard Worker for (i = 0; muxers[i]; i++) {
116*c0909341SAndroid Build Coastguard Worker if (!strcmp(muxers[i]->extension, ext)) {
117*c0909341SAndroid Build Coastguard Worker impl = muxers[i];
118*c0909341SAndroid Build Coastguard Worker break;
119*c0909341SAndroid Build Coastguard Worker }
120*c0909341SAndroid Build Coastguard Worker }
121*c0909341SAndroid Build Coastguard Worker if (!muxers[i]) {
122*c0909341SAndroid Build Coastguard Worker fprintf(stderr, "Failed to find muxer for extension \"%s\"\n", ext);
123*c0909341SAndroid Build Coastguard Worker return DAV1D_ERR(ENOPROTOOPT);
124*c0909341SAndroid Build Coastguard Worker }
125*c0909341SAndroid Build Coastguard Worker }
126*c0909341SAndroid Build Coastguard Worker
127*c0909341SAndroid Build Coastguard Worker if (!(c = malloc(offsetof(MuxerContext, priv_data) + impl->priv_data_size))) {
128*c0909341SAndroid Build Coastguard Worker fprintf(stderr, "Failed to allocate memory\n");
129*c0909341SAndroid Build Coastguard Worker return DAV1D_ERR(ENOMEM);
130*c0909341SAndroid Build Coastguard Worker }
131*c0909341SAndroid Build Coastguard Worker c->impl = impl;
132*c0909341SAndroid Build Coastguard Worker c->data = (MuxerPriv *) c->priv_data;
133*c0909341SAndroid Build Coastguard Worker int have_num_pattern = 0;
134*c0909341SAndroid Build Coastguard Worker for (const char *ptr = filename ? strchr(filename, '%') : NULL;
135*c0909341SAndroid Build Coastguard Worker !have_num_pattern && ptr; ptr = strchr(ptr, '%'))
136*c0909341SAndroid Build Coastguard Worker {
137*c0909341SAndroid Build Coastguard Worker ptr++; // skip '%'
138*c0909341SAndroid Build Coastguard Worker while (*ptr >= '0' && *ptr <= '9')
139*c0909341SAndroid Build Coastguard Worker ptr++; // skip length indicators
140*c0909341SAndroid Build Coastguard Worker have_num_pattern = *ptr == 'n';
141*c0909341SAndroid Build Coastguard Worker }
142*c0909341SAndroid Build Coastguard Worker c->one_file_per_frame = name_offset || (!name && have_num_pattern);
143*c0909341SAndroid Build Coastguard Worker
144*c0909341SAndroid Build Coastguard Worker if (c->one_file_per_frame) {
145*c0909341SAndroid Build Coastguard Worker c->fps[0] = fps[0];
146*c0909341SAndroid Build Coastguard Worker c->fps[1] = fps[1];
147*c0909341SAndroid Build Coastguard Worker c->filename = filename;
148*c0909341SAndroid Build Coastguard Worker c->framenum = 0;
149*c0909341SAndroid Build Coastguard Worker } else if (impl->write_header &&
150*c0909341SAndroid Build Coastguard Worker (res = impl->write_header(c->data, filename, p, fps)) < 0)
151*c0909341SAndroid Build Coastguard Worker {
152*c0909341SAndroid Build Coastguard Worker free(c);
153*c0909341SAndroid Build Coastguard Worker return res;
154*c0909341SAndroid Build Coastguard Worker }
155*c0909341SAndroid Build Coastguard Worker *c_out = c;
156*c0909341SAndroid Build Coastguard Worker
157*c0909341SAndroid Build Coastguard Worker return 0;
158*c0909341SAndroid Build Coastguard Worker }
159*c0909341SAndroid Build Coastguard Worker
safe_strncat(char * const dst,const int dst_len,const char * const src,const int src_len)160*c0909341SAndroid Build Coastguard Worker static void safe_strncat(char *const dst, const int dst_len,
161*c0909341SAndroid Build Coastguard Worker const char *const src, const int src_len)
162*c0909341SAndroid Build Coastguard Worker {
163*c0909341SAndroid Build Coastguard Worker if (!src_len) return;
164*c0909341SAndroid Build Coastguard Worker const int dst_fill = (int) strlen(dst);
165*c0909341SAndroid Build Coastguard Worker assert(dst_fill < dst_len);
166*c0909341SAndroid Build Coastguard Worker const int to_copy = imin(src_len, dst_len - dst_fill - 1);
167*c0909341SAndroid Build Coastguard Worker if (!to_copy) return;
168*c0909341SAndroid Build Coastguard Worker memcpy(dst + dst_fill, src, to_copy);
169*c0909341SAndroid Build Coastguard Worker dst[dst_fill + to_copy] = 0;
170*c0909341SAndroid Build Coastguard Worker }
171*c0909341SAndroid Build Coastguard Worker
assemble_field(char * const dst,const int dst_len,const char * const fmt,const int fmt_len,const int field)172*c0909341SAndroid Build Coastguard Worker static void assemble_field(char *const dst, const int dst_len,
173*c0909341SAndroid Build Coastguard Worker const char *const fmt, const int fmt_len,
174*c0909341SAndroid Build Coastguard Worker const int field)
175*c0909341SAndroid Build Coastguard Worker {
176*c0909341SAndroid Build Coastguard Worker char fmt_copy[32];
177*c0909341SAndroid Build Coastguard Worker
178*c0909341SAndroid Build Coastguard Worker assert(fmt[0] == '%');
179*c0909341SAndroid Build Coastguard Worker fmt_copy[0] = '%';
180*c0909341SAndroid Build Coastguard Worker if (fmt[1] >= '1' && fmt[1] <= '9') {
181*c0909341SAndroid Build Coastguard Worker fmt_copy[1] = '0'; // pad with zeroes, not spaces
182*c0909341SAndroid Build Coastguard Worker fmt_copy[2] = 0;
183*c0909341SAndroid Build Coastguard Worker } else {
184*c0909341SAndroid Build Coastguard Worker fmt_copy[1] = 0;
185*c0909341SAndroid Build Coastguard Worker }
186*c0909341SAndroid Build Coastguard Worker safe_strncat(fmt_copy, sizeof(fmt_copy), &fmt[1], fmt_len - 1);
187*c0909341SAndroid Build Coastguard Worker safe_strncat(fmt_copy, sizeof(fmt_copy), "d", 1);
188*c0909341SAndroid Build Coastguard Worker
189*c0909341SAndroid Build Coastguard Worker char tmp[32];
190*c0909341SAndroid Build Coastguard Worker snprintf(tmp, sizeof(tmp), fmt_copy, field);
191*c0909341SAndroid Build Coastguard Worker
192*c0909341SAndroid Build Coastguard Worker safe_strncat(dst, dst_len, tmp, (int) strlen(tmp));
193*c0909341SAndroid Build Coastguard Worker }
194*c0909341SAndroid Build Coastguard Worker
assemble_filename(MuxerContext * const ctx,char * const filename,const int filename_size,const Dav1dPictureParameters * const p)195*c0909341SAndroid Build Coastguard Worker static void assemble_filename(MuxerContext *const ctx, char *const filename,
196*c0909341SAndroid Build Coastguard Worker const int filename_size,
197*c0909341SAndroid Build Coastguard Worker const Dav1dPictureParameters *const p)
198*c0909341SAndroid Build Coastguard Worker {
199*c0909341SAndroid Build Coastguard Worker filename[0] = 0;
200*c0909341SAndroid Build Coastguard Worker const int framenum = ctx->framenum++;
201*c0909341SAndroid Build Coastguard Worker assert(ctx->filename);
202*c0909341SAndroid Build Coastguard Worker const char *ptr = ctx->filename, *iptr;
203*c0909341SAndroid Build Coastguard Worker while ((iptr = strchr(ptr, '%'))) {
204*c0909341SAndroid Build Coastguard Worker safe_strncat(filename, filename_size, ptr, (int) (iptr - ptr));
205*c0909341SAndroid Build Coastguard Worker ptr = iptr;
206*c0909341SAndroid Build Coastguard Worker
207*c0909341SAndroid Build Coastguard Worker const char *iiptr = &iptr[1]; // skip '%'
208*c0909341SAndroid Build Coastguard Worker while (*iiptr >= '0' && *iiptr <= '9')
209*c0909341SAndroid Build Coastguard Worker iiptr++; // skip length indicators
210*c0909341SAndroid Build Coastguard Worker
211*c0909341SAndroid Build Coastguard Worker switch (*iiptr) {
212*c0909341SAndroid Build Coastguard Worker case 'w':
213*c0909341SAndroid Build Coastguard Worker assemble_field(filename, filename_size, ptr, (int) (iiptr - ptr), p->w);
214*c0909341SAndroid Build Coastguard Worker break;
215*c0909341SAndroid Build Coastguard Worker case 'h':
216*c0909341SAndroid Build Coastguard Worker assemble_field(filename, filename_size, ptr, (int) (iiptr - ptr), p->h);
217*c0909341SAndroid Build Coastguard Worker break;
218*c0909341SAndroid Build Coastguard Worker case 'n':
219*c0909341SAndroid Build Coastguard Worker assemble_field(filename, filename_size, ptr, (int) (iiptr - ptr), framenum);
220*c0909341SAndroid Build Coastguard Worker break;
221*c0909341SAndroid Build Coastguard Worker default:
222*c0909341SAndroid Build Coastguard Worker safe_strncat(filename, filename_size, "%", 1);
223*c0909341SAndroid Build Coastguard Worker ptr = &iptr[1];
224*c0909341SAndroid Build Coastguard Worker continue;
225*c0909341SAndroid Build Coastguard Worker }
226*c0909341SAndroid Build Coastguard Worker
227*c0909341SAndroid Build Coastguard Worker ptr = &iiptr[1];
228*c0909341SAndroid Build Coastguard Worker }
229*c0909341SAndroid Build Coastguard Worker safe_strncat(filename, filename_size, ptr, (int) strlen(ptr));
230*c0909341SAndroid Build Coastguard Worker }
231*c0909341SAndroid Build Coastguard Worker
output_write(MuxerContext * const ctx,Dav1dPicture * const p)232*c0909341SAndroid Build Coastguard Worker int output_write(MuxerContext *const ctx, Dav1dPicture *const p) {
233*c0909341SAndroid Build Coastguard Worker int res;
234*c0909341SAndroid Build Coastguard Worker
235*c0909341SAndroid Build Coastguard Worker if (ctx->one_file_per_frame && ctx->impl->write_header) {
236*c0909341SAndroid Build Coastguard Worker char filename[1024];
237*c0909341SAndroid Build Coastguard Worker assemble_filename(ctx, filename, sizeof(filename), &p->p);
238*c0909341SAndroid Build Coastguard Worker res = ctx->impl->write_header(ctx->data, filename, &p->p, ctx->fps);
239*c0909341SAndroid Build Coastguard Worker if (res < 0)
240*c0909341SAndroid Build Coastguard Worker return res;
241*c0909341SAndroid Build Coastguard Worker }
242*c0909341SAndroid Build Coastguard Worker if ((res = ctx->impl->write_picture(ctx->data, p)) < 0)
243*c0909341SAndroid Build Coastguard Worker return res;
244*c0909341SAndroid Build Coastguard Worker if (ctx->one_file_per_frame && ctx->impl->write_trailer)
245*c0909341SAndroid Build Coastguard Worker ctx->impl->write_trailer(ctx->data);
246*c0909341SAndroid Build Coastguard Worker
247*c0909341SAndroid Build Coastguard Worker return 0;
248*c0909341SAndroid Build Coastguard Worker }
249*c0909341SAndroid Build Coastguard Worker
output_close(MuxerContext * const ctx)250*c0909341SAndroid Build Coastguard Worker void output_close(MuxerContext *const ctx) {
251*c0909341SAndroid Build Coastguard Worker if (!ctx->one_file_per_frame && ctx->impl->write_trailer)
252*c0909341SAndroid Build Coastguard Worker ctx->impl->write_trailer(ctx->data);
253*c0909341SAndroid Build Coastguard Worker free(ctx);
254*c0909341SAndroid Build Coastguard Worker }
255*c0909341SAndroid Build Coastguard Worker
output_verify(MuxerContext * const ctx,const char * const md5_str)256*c0909341SAndroid Build Coastguard Worker int output_verify(MuxerContext *const ctx, const char *const md5_str) {
257*c0909341SAndroid Build Coastguard Worker const int res = ctx->impl->verify ?
258*c0909341SAndroid Build Coastguard Worker ctx->impl->verify(ctx->data, md5_str) : 0;
259*c0909341SAndroid Build Coastguard Worker free(ctx);
260*c0909341SAndroid Build Coastguard Worker return res;
261*c0909341SAndroid Build Coastguard Worker }
262