2 * Copyright © 2018, VideoLAN and dav1d authors
3 * Copyright © 2018, Two Orioles, LLC
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright notice, this
10 * list of conditions and the following disclaimer.
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.
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.
29 #include "vcs_version.h"
30 #include "cli_config.h"
49 #include <mach/mach_time.h>
52 #include "dav1d/dav1d.h"
54 #include "input/input.h"
56 #include "output/output.h"
58 #include "dav1d_cli_parse.h"
60 static uint64_t get_time_nanos(void) {
62 LARGE_INTEGER frequency
;
63 QueryPerformanceFrequency(&frequency
);
65 QueryPerformanceCounter(&t
);
66 uint64_t seconds
= t
.QuadPart
/ frequency
.QuadPart
;
67 uint64_t fractions
= t
.QuadPart
% frequency
.QuadPart
;
68 return 1000000000 * seconds
+ 1000000000 * fractions
/ frequency
.QuadPart
;
69 #elif defined(HAVE_CLOCK_GETTIME)
71 clock_gettime(CLOCK_MONOTONIC
, &ts
);
72 return 1000000000ULL * ts
.tv_sec
+ ts
.tv_nsec
;
73 #elif defined(__APPLE__)
74 mach_timebase_info_data_t info
;
75 mach_timebase_info(&info
);
76 return mach_absolute_time() * info
.numer
/ info
.denom
;
80 static void sleep_nanos(uint64_t d
) {
82 Sleep((unsigned)(d
/ 1000000));
84 const struct timespec ts
= {
85 .tv_sec
= (time_t)(d
/ 1000000000),
86 .tv_nsec
= d
% 1000000000,
92 static void synchronize(const int realtime
, const unsigned cache
,
93 const unsigned n_out
, const uint64_t nspf
,
94 const uint64_t tfirst
, uint64_t *const elapsed
,
95 FILE *const frametimes
)
97 const uint64_t tcurr
= get_time_nanos();
98 const uint64_t last
= *elapsed
;
99 *elapsed
= tcurr
- tfirst
;
101 const uint64_t deadline
= nspf
* n_out
;
102 if (*elapsed
< deadline
) {
103 const uint64_t remaining
= deadline
- *elapsed
;
104 if (remaining
> nspf
* cache
) sleep_nanos(remaining
- nspf
* cache
);
109 const uint64_t frametime
= *elapsed
- last
;
110 fprintf(frametimes
, "%" PRIu64
"\n", frametime
);
115 static void print_stats(const int istty
, const unsigned n
, const unsigned num
,
116 const uint64_t elapsed
, const double i_fps
)
118 char buf
[80], *b
= buf
, *const end
= buf
+ 80;
122 if (num
== 0xFFFFFFFF)
123 b
+= snprintf(b
, end
- b
, "Decoded %u frames", n
);
125 b
+= snprintf(b
, end
- b
, "Decoded %u/%u frames (%.1lf%%)",
126 n
, num
, 100.0 * n
/ num
);
128 const double d_fps
= 1e9
* n
/ elapsed
;
130 const double speed
= d_fps
/ i_fps
;
131 b
+= snprintf(b
, end
- b
, " - %.2lf/%.2lf fps (%.2lfx)",
132 d_fps
, i_fps
, speed
);
134 b
+= snprintf(b
, end
- b
, " - %.2lf fps", d_fps
);
138 strcpy(b
> end
- 2 ? end
- 2 : b
, "\n");
142 int main(const int argc
, char *const *const argv
) {
143 const int istty
= isatty(fileno(stderr
));
145 CLISettings cli_settings
;
146 Dav1dSettings lib_settings
;
148 MuxerContext
*out
= NULL
;
152 unsigned n_out
= 0, total
, fps
[2], timebase
[2];
153 uint64_t nspf
, tfirst
, elapsed
;
155 FILE *frametimes
= NULL
;
156 const char *version
= dav1d_version();
158 if (strcmp(version
, DAV1D_VERSION
)) {
159 fprintf(stderr
, "Version mismatch (library: %s, executable: %s)\n",
160 version
, DAV1D_VERSION
);
164 parse(argc
, argv
, &cli_settings
, &lib_settings
);
166 if ((res
= input_open(&in
, cli_settings
.demuxer
,
167 cli_settings
.inputfile
,
168 fps
, &total
, timebase
)) < 0)
172 for (unsigned i
= 0; i
<= cli_settings
.skip
; i
++) {
173 if ((res
= input_read(in
, &data
)) < 0) {
177 if (i
< cli_settings
.skip
) dav1d_data_unref(&data
);
180 if (!cli_settings
.quiet
)
181 fprintf(stderr
, "dav1d %s - by VideoLAN\n", dav1d_version());
183 // skip frames until a sequence header is found
184 if (cli_settings
.skip
) {
185 Dav1dSequenceHeader seq
;
186 unsigned seq_skip
= 0;
187 while (dav1d_parse_sequence_header(&seq
, data
.data
, data
.sz
)) {
188 if ((res
= input_read(in
, &data
)) < 0) {
194 if (seq_skip
&& !cli_settings
.quiet
)
196 "skipped %u packets due to missing sequence header\n",
201 if (cli_settings
.limit
!= 0 && cli_settings
.limit
< total
)
202 total
= cli_settings
.limit
;
204 if ((res
= dav1d_open(&c
, &lib_settings
)))
207 if (cli_settings
.frametimes
)
208 frametimes
= fopen(cli_settings
.frametimes
, "w");
210 if (cli_settings
.realtime
!= REALTIME_CUSTOM
) {
215 i_fps
= (double)fps
[0] / fps
[1];
216 nspf
= 1000000000ULL * fps
[1] / fps
[0];
219 i_fps
= cli_settings
.realtime_fps
;
220 nspf
= (uint64_t)(1000000000.0 / cli_settings
.realtime_fps
);
222 tfirst
= get_time_nanos();
225 memset(&p
, 0, sizeof(p
));
226 if ((res
= dav1d_send_data(c
, &data
)) < 0) {
227 if (res
!= DAV1D_ERR(EAGAIN
)) {
228 fprintf(stderr
, "Error decoding frame: %s\n",
229 strerror(DAV1D_ERR(res
)));
234 if ((res
= dav1d_get_picture(c
, &p
)) < 0) {
235 if (res
!= DAV1D_ERR(EAGAIN
)) {
236 fprintf(stderr
, "Error decoding frame: %s\n",
237 strerror(DAV1D_ERR(res
)));
243 if ((res
= output_open(&out
, cli_settings
.muxer
,
244 cli_settings
.outputfile
,
247 if (frametimes
) fclose(frametimes
);
251 if ((res
= output_write(out
, &p
)) < 0)
254 if (nspf
|| !cli_settings
.quiet
) {
255 synchronize(cli_settings
.realtime
, cli_settings
.realtime_cache
,
256 n_out
, nspf
, tfirst
, &elapsed
, frametimes
);
258 if (!cli_settings
.quiet
)
259 print_stats(istty
, n_out
, total
, elapsed
, i_fps
);
262 if (cli_settings
.limit
&& n_out
== cli_settings
.limit
)
264 } while (data
.sz
> 0 || !input_read(in
, &data
));
266 if (data
.sz
> 0) dav1d_data_unref(&data
);
269 if (res
== 0) while (!cli_settings
.limit
|| n_out
< cli_settings
.limit
) {
270 if ((res
= dav1d_get_picture(c
, &p
)) < 0) {
271 if (res
!= DAV1D_ERR(EAGAIN
)) {
272 fprintf(stderr
, "Error decoding frame: %s\n",
273 strerror(DAV1D_ERR(res
)));
280 if ((res
= output_open(&out
, cli_settings
.muxer
,
281 cli_settings
.outputfile
,
284 if (frametimes
) fclose(frametimes
);
288 if ((res
= output_write(out
, &p
)) < 0)
291 if (nspf
|| !cli_settings
.quiet
) {
292 synchronize(cli_settings
.realtime
, cli_settings
.realtime_cache
,
293 n_out
, nspf
, tfirst
, &elapsed
, frametimes
);
295 if (!cli_settings
.quiet
)
296 print_stats(istty
, n_out
, total
, elapsed
, i_fps
);
300 if (frametimes
) fclose(frametimes
);
304 if (!cli_settings
.quiet
&& istty
)
305 fprintf(stderr
, "\n");
306 if (cli_settings
.verify
)
307 res
|= output_verify(out
, cli_settings
.verify
);
311 fprintf(stderr
, "No data decoded\n");
316 return (res
== 0) ? EXIT_SUCCESS
: EXIT_FAILURE
;