summaryrefslogtreecommitdiffstats
path: root/debian/transcode/transcode-1.1.7/src/encoder-buffer.c
blob: 53af7cba806c70e1f6ade832cfbeeee6cb90b25f (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
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
/*
 *  encoder-buffer.c -- encoder interface to transcode frame ringbuffers.
 *
 *  Copyright (C) Thomas Oestreich - June 2001
 *  Updated and partially rewritten by
 *  Francesco Romani - January 2006
 *
 *  This file is part of transcode, a video stream processing tool
 *
 *  transcode is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  transcode is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "transcode.h"
#include "decoder.h"
#include "encoder.h"
#include "filter.h"
#include "framebuffer.h"
#include "counter.h"
#include "video_trans.h"
#include "audio_trans.h"
#include "frame_threads.h"

#include <stdint.h>
#include <sys/types.h>

/*
 * Quick Summary:
 *
 * This code provide glue between frames ringbuffer and real encoder
 * code, in order to make encoder modular and independent from
 * a single frame source, to promote reusability (tcexport).
 * This code also take acare of some oddities like handling filtering
 * if no frame threads are avalaible.
 *
 * This code isn't clean as one (i.e.: me) would like since it
 * must cope with a lot of legacy constraints and some other nasty
 * stuff. Of course situation will be improved in future releases
 * when we keep going away from legacy oddities and continue
 * sanitize/modernize the codebase.
 *
 * NOTE about counter/condition/mutex handling inside various
 * encoder helpers.
 *
 * Code are still a little bit confusing since things aren't
 * updated or used at the same function level.
 * Code works, but isn't still well readable.
 * We need stil more cleanup and refactoring for future releases.
 */


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

static int have_aud_threads = 0;
static int have_vid_threads = 0;

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

/*
 * apply_{video,audio}_filters:
 *       Apply the filter chain to current respectively video
 *       or audio frame.
 *       This function is used if no frame threads are avalaible,
 *       but of course filtering still must be applied.
 *       This function should be never exported.
 *
 * Parameters:
 *       vptr, aptr: respectively video or aufio framebuffer
 *                   pointer to frame to be filtered.
 *              vob: pointer to vob_t structure holding stream
 *                   parameters.
 */
static void apply_video_filters(vframe_list_t *vptr, vob_t *vob);
static void apply_audio_filters(aframe_list_t *aptr, vob_t *vob);

/*
 * encoder_acquire_{v,a}frame:
 *      Get respectively a new video or audio framebuffer for encoding.
 *      This roughly means:
 *      1. to wait for a new frame avalaible for encoder
 *      2. apply the filters if no frame threads are avalaible
 *      3. apply the encoder filters (POST_S_PROCESS)
 *      4. verify the status of audio framebuffer after all filtering.
 *         if acquired framebuffer is skipped, we must acquire a new one
 *         before continue with encoding, so we must restart from step 1.
 *
 * Parameters:
 *      buf: encoder buffer to use (and fullfull with acquired frame).
 *      vob: pointer to vob_t structure describing stream parameters.
 *           Used Internally.
 * Return Value:
 *       0 when a new framebuffer is avalaible for encoding;
 *      <0 if no more framebuffers are avalaible.
 *         As for encoder_wait_{v,a}frame, this usually happens
 *         when video/audio stream ends.
 */
static int encoder_acquire_vframe(TCEncoderBuffer *buf, vob_t *vob);
static int encoder_acquire_aframe(TCEncoderBuffer *buf, vob_t *vob);

/*
 * encoder_dispose_{v,a}frame:
 *       Mark a framebuffer (respectively video or audio) as completed
 *       from encoder viewpoint, so release it to source ringbuffer,
 *       update counters and do all cleanup actions needed internally.
 *
 * Parameters:
 *       buf: encoder buffer to use (release currente framebuffers
 *            and update related counters and internal variables).
 * Return Value:
 *       None.
 */
static void encoder_dispose_vframe(TCEncoderBuffer *buf);
static void encoder_dispose_aframe(TCEncoderBuffer *buf);


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

static void apply_video_filters(vframe_list_t *vptr, vob_t *vob)
{
    if (!have_vid_threads) {
        if (TC_FRAME_NEED_PROCESSING(vptr)) {
            /* external plugin pre-processing */
            vptr->tag = TC_VIDEO|TC_PRE_M_PROCESS;
            tc_filter_process((frame_list_t *)vptr);

            /* internal processing of video */
            vptr->tag = TC_VIDEO;
            process_vid_frame(vob, vptr);

            /* external plugin post-processing */
            vptr->tag = TC_VIDEO|TC_POST_M_PROCESS;
            tc_filter_process((frame_list_t *)vptr);
        }
    }

    if (TC_FRAME_NEED_PROCESSING(vptr)) {
        /* second stage post-processing - (synchronous) */
        vptr->tag = TC_VIDEO|TC_POST_S_PROCESS;
        tc_filter_process((frame_list_t *)vptr);
        postprocess_vid_frame(vob, vptr);
        /* preview _after_ all post-processing */
        vptr->tag = TC_VIDEO|TC_PREVIEW;
        tc_filter_process((frame_list_t *)vptr);
    }
}

static void apply_audio_filters(aframe_list_t *aptr, vob_t *vob)
{
    /* now we try to process the audio frame */
    if (!have_aud_threads) {
        if (TC_FRAME_NEED_PROCESSING(aptr)) {
            /* external plugin pre-processing */
            aptr->tag = TC_AUDIO|TC_PRE_M_PROCESS;
            tc_filter_process((frame_list_t *)aptr);

            /* internal processing of audio */
            aptr->tag = TC_AUDIO;
            process_aud_frame(vob, aptr);

            /* external plugin post-processing */
            aptr->tag = TC_AUDIO|TC_POST_M_PROCESS;
            tc_filter_process((frame_list_t *)aptr);
        }
    }

    if (TC_FRAME_NEED_PROCESSING(aptr)) {
        /* second stage post-processing - (synchronous) */
        aptr->tag = TC_AUDIO|TC_POST_S_PROCESS;
        tc_filter_process((frame_list_t *)aptr);
        /* preview _after_ all post-processing */
        aptr->tag = TC_AUDIO|TC_PREVIEW;
        tc_filter_process((frame_list_t *)aptr);
    }
}

static int encoder_acquire_vframe(TCEncoderBuffer *buf, vob_t *vob)
{
    int got_frame = TC_TRUE;

    do {
        buf->vptr = vframe_retrieve();
        if (!buf->vptr) {
            if (verbose >= TC_THREADS)
                tc_log_msg(__FILE__, "frame retrieve interrupted!");
            return -1; /* can't acquire video frame */
        }
        got_frame = TC_TRUE;
        buf->frame_id = buf->vptr->id + tc_get_frames_skipped_cloned();

        if (verbose & TC_STATS) {
            tc_log_info(__FILE__, "got frame %p (id=%i)",
                                  buf->vptr, buf->frame_id);
        }

        /*
         * now we do the post processing ... this way, if just a video frame is
         * skipped, we'll know.
         *
         * we have to check to make sure that before we do any processing
         * that this frame isn't out of range (if it is, and one is using
         * the "-t" split option, we'll see this frame again.
         */
        apply_video_filters(buf->vptr, vob);

        if (buf->vptr->attributes & TC_FRAME_IS_SKIPPED) {
            if (buf->vptr != NULL
              && (buf->vptr->attributes & TC_FRAME_WAS_CLONED)
            ) {
                /* XXX do we want to track skipped cloned flags? */
                tc_update_frames_cloned(1);
            }

            if (buf->vptr != NULL
              && (buf->vptr->attributes & TC_FRAME_IS_CLONED)
            ) {
                /* XXX what to do when a frame is cloned and skipped? */
                /*
                 * I'd like to say they cancel, but perhaps they will end
                 * up also skipping the clone?  or perhaps they'll keep,
                 * but modify the clone?  Best to do the whole drill :/
                 */
                if (verbose & TC_DEBUG) {
                    tc_log_info (__FILE__, "(%i) V pointer done. "
                                           "Skipped and Cloned: (%i)",
                                           buf->vptr->id,
                                           (buf->vptr->attributes));
                }

                /* update flags */
                buf->vptr->attributes &= ~TC_FRAME_IS_CLONED;
                buf->vptr->attributes |= TC_FRAME_WAS_CLONED;
                /*
                 * this has to be done here,
                 * frame_threads.c won't see the frame again
                 */
            }
            if (buf->vptr != NULL
              && !(buf->vptr->attributes & TC_FRAME_IS_CLONED)
            ) {
                vframe_remove(buf->vptr);
                /* reset pointer for next retrieve */
                buf->vptr = NULL;
            }
            // tc_update_frames_skipped(1);
            got_frame = TC_FALSE;
        }
    } while (!got_frame);

    return 0;
}

static int encoder_acquire_aframe(TCEncoderBuffer *buf, vob_t *vob)
{
    int got_frame = TC_TRUE;

    do {
        buf->aptr = aframe_retrieve();
        if (!buf->aptr) {
            if (verbose >= TC_THREADS)
                tc_log_msg(__FILE__, "frame retrieve interrupted!");
            return -1;
        }
        got_frame = TC_TRUE;

        if (verbose & TC_STATS) {
            tc_log_info(__FILE__, "got audio frame (id=%i)", buf->aptr->id);
        }

        apply_audio_filters(buf->aptr, vob);

        if (buf->aptr->attributes & TC_FRAME_IS_SKIPPED) {
            if (buf->aptr != NULL
              && !(buf->aptr->attributes & TC_FRAME_IS_CLONED)
            ) {
                aframe_remove(buf->aptr);
                /* reset pointer for next retrieve */
                buf->aptr = NULL;
            }

            if (buf->aptr != NULL
              && (buf->aptr->attributes & TC_FRAME_IS_CLONED)
            ) {
                if (verbose & TC_DEBUG) {
                    tc_log_info(__FILE__, "(%i) A pointer done. Skipped and Cloned: (%i)",
                                        buf->aptr->id, (buf->aptr->attributes));
                }

                /* adjust clone flags */
                buf->aptr->attributes &= ~TC_FRAME_IS_CLONED;
                buf->aptr->attributes |= TC_FRAME_WAS_CLONED;

                /*
                 * this has to be done here,
                 * frame_threads.c won't see the frame again
                 */
            }
            got_frame = TC_FALSE;
        }
    } while (!got_frame);

    return 0;
}


static void encoder_dispose_vframe(TCEncoderBuffer *buf)
{
    if (buf->vptr != NULL
      && (buf->vptr->attributes & TC_FRAME_WAS_CLONED)
    ) {
        tc_update_frames_cloned(1);
    }

    if (buf->vptr != NULL
      && !(buf->vptr->attributes & TC_FRAME_IS_CLONED)
    ) {
        vframe_remove(buf->vptr);
        /* reset pointer for next retrieve */
        buf->vptr = NULL;
    }

    if (buf->vptr != NULL
      && (buf->vptr->attributes & TC_FRAME_IS_CLONED)
    ) {
        if(verbose & TC_DEBUG) {
            tc_log_info(__FILE__, "(%i) V pointer done. Cloned: (%i)",
                                buf->vptr->id, (buf->vptr->attributes));
        }
        buf->vptr->attributes &= ~TC_FRAME_IS_CLONED;
        buf->vptr->attributes |= TC_FRAME_WAS_CLONED;
        // update counter
        //tc_update_frames_cloned(1);
    }
}


static void encoder_dispose_aframe(TCEncoderBuffer *buf)
{
    if (buf->aptr != NULL
      && !(buf->aptr->attributes & TC_FRAME_IS_CLONED)
    ) {
        aframe_remove(buf->aptr);
        /* reset pointer for next retrieve */
        buf->aptr = NULL;
    }

    if (buf->aptr != NULL
      && (buf->aptr->attributes & TC_FRAME_IS_CLONED)
    ) {
        if (verbose & TC_DEBUG) {
            tc_log_info(__FILE__, "(%i) A pointer done. Cloned: (%i)",
                                 buf->aptr->id, (buf->aptr->attributes));
        }

        buf->aptr->attributes &= ~TC_FRAME_IS_CLONED;
        buf->aptr->attributes |= TC_FRAME_WAS_CLONED;
    }
}


static TCEncoderBuffer tc_builtin_buffer = {
    .frame_id = 0,

    .vptr = NULL,
    .aptr = NULL,

    .acquire_video_frame = encoder_acquire_vframe,
    .acquire_audio_frame = encoder_acquire_aframe,
    .dispose_video_frame = encoder_dispose_vframe,
    .dispose_audio_frame = encoder_dispose_aframe,
};

/* default main transcode buffer */
TCEncoderBuffer *tc_get_ringbuffer(int aworkers, int vworkers)
{
    have_aud_threads = aworkers;
    have_vid_threads = vworkers;

    return &tc_builtin_buffer;
}

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

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