summaryrefslogtreecommitdiffstats
path: root/debian/transcode/transcode-1.1.7/src/counter.c
blob: 3d9a60cd359574670528f4b4a183d8ccf2b622b2 (plain)
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
/*
 * counter.c - transcode progress counter routines
 * Written by Andrew Church <achurch@achurch.org>
 *
 * This file is part of transcode, a video stream processing tool.
 * transcode is free software, distributable under the terms of the GNU
 * General Public License (version 2 or later).  See the file COPYING
 * for details.
 */

#include "transcode.h"
#include "counter.h"
#include "frame_threads.h"
#include <math.h>

/*************************************************************************/

static int counter_active = 0;    /* Is the counter active? */
static int frames_to_encode = 0;  /* Total number of frames to encode */
static int encoded_frames = 0;    /* Number of frames encoded so far */
static double encoded_time = 0;   /* Time spent encoding so far */
static int frames_to_skip = 0;    /* Total number of frames to skip */
static int skipped_frames = 0;    /* Number of frames skipped so far */
static double skipped_time = 0;   /* Time spent skipping so far */
static int highest_frame = 0;     /* Highest frame number to be seen */

static int printed = 0;           /* Have we printed a line? */

static void print_counter_line(int encoding, int frame, int first, int last,
                               double fps, double done, double timestamp,
                               int secleft, int decodebuf[2], int filterbuf[2],
                               int encodebuf[2]);

/*************************************************************************/
/*************************************************************************/

/**
 * counter_on:  Activate the counter display.
 *
 * Parameters:
 *     None.
 * Return value:
 *     None.
 */

void counter_on(void)
{
    counter_active = 1;
}

/*************************************************************************/

/**
 * counter_off:  Deactivate the counter display.
 *
 * Parameters:
 *     None.
 * Return value:
 *     None.
 * Side effects:
 *     When in human-readable mode (tc_progress_meter == 1), if the counter
 *     has been displayed at least once, a newline is written to standard
 *     output.
 */

void counter_off(void)
{
    if (printed) {
        if (tc_progress_meter == 1)
            fprintf(stderr, "\n");
        printed = 0;
    }
    counter_active = 0;
}

/*************************************************************************/

/**
 * counter_add_range:  Add the given range of frames to the total number of
 * frames to be encoded or skipped.
 *
 * Parameters:
 *      first: First frame of range.
 *       last: Last frame of range.
 *     encode: True (nonzero) if frames are to be encoded.
 *             False (zero) if frames are being skipped.
 * Return value:
 *     None.
 */

void counter_add_range(int first, int last, int encode)
{
    if (encode) {
        frames_to_encode += last+1 - first;
    } else {
        frames_to_skip += last+1 - first;
    }
    if (last > highest_frame)
        highest_frame = last;
}

/*************************************************************************/

/**
 * counter_reset_ranges:  Reset the counter's stored range data.
 *
 * Parameters:
 *     None.
 * Return value:
 *     None.
 */

void counter_reset_ranges(void)
{
    frames_to_encode = 0;
    encoded_frames = 0;
    encoded_time = 0;
    frames_to_skip = 0;
    skipped_frames = 0;
    skipped_time = 0;
    highest_frame = 0;
}

/*************************************************************************/

/**
 * counter_print:  Display the progress counter, if active.
 *
 * Parameters:
 *     encoding: True (nonzero) if frames are being encoded.
 *               False (zero) if frames are being skipped.
 *        frame: Current frame being encoded or skipped.
 *        first: First frame of current range.
 *         last: Last frame of current range, -1 if unknown.
 * Return value:
 *     None.
 */

void counter_print(int encoding, int frame, int first, int last)
{
    vob_t *vob = tc_get_vob();
    struct timeval tv;
    struct timezone dummy_tz = {0,0};
    double now, timediff, fps, time;
    int buf_im[2], buf_fl[2], buf_ex[2];
    /* Values of 'first' and `last' during last call (-1 = not called yet) */
    static int old_first = -1, old_last = -1;
    /* Time of first call for this range */
    static double start_time = 0;
    /* Time of last call */
    static double old_time = 0;

    if (!tc_progress_meter
     || !tc_progress_rate
     || !counter_active
     || frame % tc_progress_rate != 0
    ) {
        return;
    }
    if (frame < 0 || first < 0) {
        static int warned = 0;
        if (!warned) {
            tc_log_warn(__FILE__, "invalid arguments to counter_print"
                        " (%d,%d,%d,%d)", encoding, frame, first, last);
            warned = 1;
        }
        return;
    }

#ifdef HAVE_GETTIMEOFDAY
    if (gettimeofday(&tv, &dummy_tz) != 0) {
        static int warned = 0;
        if (!warned) {
            tc_log_warn(__FILE__, "gettimeofday() failed!");
            warned = 1;
        }
        return;
    }
    now = tv.tv_sec + (double)tv.tv_usec/1000000.0;
#else
    now = time(NULL);
#endif

    timediff = now - old_time;
    old_time = now;
    if (old_first != first || old_last != last) {
        /* In human-readable mode, start a new counter line for each range
         * if we don't know the total number of frames to be encoded. */
        if (tc_progress_meter == 1 && old_first != -1 && frames_to_encode == 0)
            fprintf(stderr, "\n");
        start_time = now;
        old_first = first;
        old_last = last;
        /* We decrement the frame counts here to compensate for this frame
         * which took an unknown amount of time to complete. */
        if (encoding && frames_to_encode > 0)
            frames_to_encode--;
        else if (!encoding && frames_to_skip > 0)
            frames_to_skip--;
        return;
    }

    /* Note that we don't add 1 to the numerator here, since start_time is
     * the time we were called for the first frame, so frame first+1 is one
     * one frame later than start_time, not two. */
    if (now > start_time) {
        fps = (frame - first) / (now - start_time);
    } else {
        /* No time has passed (maybe we don't have gettimeofday()) */
        fps = 0;
    }

    vframe_get_counters(&buf_im[0], &buf_fl[0], &buf_ex[0]);
    aframe_get_counters(&buf_im[1], &buf_fl[1], &buf_ex[1]);

    time = (double)frame / ((vob->ex_fps<1.0) ? 1.0 : vob->ex_fps);

    if (last == -1) {
        /* Can't calculate ETA, just display current timestamp */
        print_counter_line(encoding, frame, first, -1, fps, -1, time, -1,
                           buf_im, buf_fl, buf_ex);

    } else if (frames_to_encode == 0) {
        /* Total number of frames unknown, just display for current range */
        double done = (double)(frame - first + 1) / (double)(last+1 - first);
        int secleft = fps>0 ? ((last+1)-frame) / fps : -1;
        print_counter_line(encoding, frame, first, last, fps, done, time,
                           secleft, buf_im, buf_fl, buf_ex);

    } else {
        /* Estimate time remaining for entire run */
        double done;
        int secleft;
        if (encoding) {
            encoded_frames++;
            encoded_time += timediff;
        } else {
            skipped_frames++;
            skipped_time += timediff;
        }
        if (encoded_frames > frames_to_encode)
            frames_to_encode = encoded_frames;
        if (skipped_frames > frames_to_skip)
            frames_to_skip = skipped_frames;
        if (encoded_frames == 0) {
            /* We don't know how long it will take to encode frames; avoid
             * understating the ETA, and just say we don't know */
            secleft = -1;
        } else {
            double encode_fps, skip_fps, total_time;
            /* Find the processing speed for encoding and skipping */
            encode_fps = encoded_time ? encoded_frames / encoded_time : 0;
            if (skipped_frames > 0 && skipped_time > 0) {
                skip_fps = skipped_frames / skipped_time;
            } else {
                /* Just assume the same FPS for skipping as for encoding.
                 * Overstating the ETA isn't as bad as understating it, and
                 * certainly better than giving the user "unknown" for an
                 * entire encoding range. */
                skip_fps = encode_fps;
            }
            if (encode_fps > 0) {
                /* Estimate the total processing time required */
                total_time = (frames_to_encode / encode_fps)
                           + (frames_to_skip / skip_fps);
                /* Determine time left (round up) */
                secleft = ceil(total_time - (encoded_time + skipped_time));
            } else {
                total_time = -1;
                secleft = -1;
            }
            /* Use the proper overall FPS in the status line */
            fps = encoding ? encode_fps : skip_fps;
        }
        /* Just use the frame ratio for completion percentage */
        done = (double)(encoded_frames + skipped_frames)
             / (double)(frames_to_encode + frames_to_skip);
        print_counter_line(encoding, frame, 0, highest_frame, fps, done,
                           time, secleft, buf_im, buf_fl, buf_ex);
    }

    fflush(stdout);
}

/*************************************************************************/

/**
 * print_counter_line:  Helper function to format display arguments into a
 * progress counter line depending on settings.
 *
 * Parameters:
 *      encoding: True (nonzero) if frames are being encoded.
 *                False (zero) if frames are being skipped.
 *         frame: Current frame being encoded or skipped.
 *         first: First frame of current range.
 *          last: Last frame of current range, -1 if unknown.
 *           fps: Estimated frames processed per second.
 *          done: Completion ratio (0..1), -1 if unknown.
 *     timestamp: Timestamp of current frame, in seconds.
 *       secleft: Estimated time remaining to completion, in seconds (-1 if
 *                unknown).
 *     decodebuf: Number of buffered frames awaiting decoding [V, A].
 *     filterbuf: Number of buffered frames awaiting filtering [V, A].
 *     encodebuf: Number of buffered frames awaiting encoding [V, A].
 * Return value:
 *     None.
 */

static void print_counter_line(int encoding, int frame, int first, int last,
                               double fps, double done, double timestamp,
                               int secleft,
                               int decodebuf[2], int filterbuf[2], int encodebuf[2])
{
    if (tc_progress_meter == 2) {
        /* Raw data format */
        printf("encoding=%d frame=%d first=%d last=%d fps=%.3f done=%.6f"
               " timestamp=%.3f timeleft=%d decodebuf=%d filterbuf=%d"
               " encodebuf=%d\n",
               encoding, frame, first, last, fps, done,
               timestamp, secleft, decodebuf[0] + decodebuf[1],
               filterbuf[0] + filterbuf[1], encodebuf[0] + encodebuf[1]);
    } else if (last < 0 || done < 0 || secleft < 0) {
        int timeint = floor(timestamp);
        fprintf(stderr, "%s frames [%d-%d], %6.2f fps, CFT: %d:%02d:%02d,"
                        "  (%2d,%2d|%2d,%2d|%2d,%2d) \r",
                encoding ? "encoding" : "skipping",
                first, frame,
                fps,
                timeint/3600, (timeint/60) % 60, timeint % 60,
                decodebuf[0], decodebuf[1], filterbuf[0], filterbuf[1],
                encodebuf[0], encodebuf[1]
        );
    } else {
        char eta_buf[100];
        if (secleft < 0) {
            snprintf(eta_buf, sizeof(eta_buf), "--:--:--");
        } else {
            snprintf(eta_buf, sizeof(eta_buf), "%d:%02d:%02d",
                     secleft/3600, (secleft/60) % 60, secleft % 60);
        }
        fprintf(stderr, "%s frame [%d/%d], %6.2f fps, %5.1f%%, ETA: %s,"
                        "  (%2d,%2d|%2d,%2d|%2d,%2d) \r",
                encoding ? "encoding" : "skipping",
                frame, last+1,
                fps,
                100*done,
                eta_buf,
                decodebuf[0], decodebuf[1], filterbuf[0], filterbuf[1],
                encodebuf[0], encodebuf[1]
        );
    }
    printed = 1;
}

/*************************************************************************/

/*
 * Local variables:
 *   c-file-style: "stroustrup"
 *   c-file-offsets: ((case-label . *) (statement-case-intro . *))
 *   indent-tabs-mode: nil
 * End:
 *
 * vim: expandtab shiftwidth=4:
 */