Coverage Report

Created: 2026-07-30 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/bsfgraph.c
Line
Count
Source
1
/*
2
 * This file is part of FFmpeg.
3
 *
4
 * FFmpeg is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * FFmpeg is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with FFmpeg; if not, write to the Free Software
16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
 */
18
19
#include "config.h"
20
21
#include <stddef.h>
22
#include <string.h>
23
24
#include "libavutil/avassert.h"
25
#include "libavutil/error.h"
26
#include "libavutil/mem.h"
27
#include "libavutil/opt.h"
28
29
#include "bsf.h"
30
#include "bsf_internal.h"
31
32
#define OFFSET(x) offsetof(AVBitStreamFilterGraph, x)
33
#define FLAGS (AV_OPT_FLAG_BSF_PARAM|AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_VIDEO_PARAM)
34
static const AVOption filtergraph_options[] = {
35
    {"max_buffered_packets"  , "maximum number of buffered packets allowed", OFFSET(max_buffered_packets),
36
        AV_OPT_TYPE_UINT,   {.i64 = 0}, 0, UINT_MAX, FLAGS },
37
    { NULL },
38
};
39
40
static const AVClass filtergraph_class = {
41
    .class_name = "AVBitStreamFilterGraph",
42
    .item_name  = av_default_item_name,
43
    .version    = LIBAVUTIL_VERSION_INT,
44
    .option     = filtergraph_options,
45
    .category   = AV_CLASS_CATEGORY_BITSTREAM_FILTER,
46
};
47
48
AVBitStreamFilterGraph *av_bsf_graph_alloc(void)
49
0
{
50
0
    FFBitStreamFilterGraph *graph = av_mallocz(sizeof(*graph));
51
0
    AVBitStreamFilterGraph *ret;
52
53
0
    if (!graph)
54
0
        return NULL;
55
56
0
    ret = &graph->p;
57
0
    ret->av_class = &filtergraph_class;
58
0
    av_opt_set_defaults(ret);
59
0
    graph->max_packet_queue = SIZE_MAX;
60
61
0
    return ret;
62
0
}
63
64
void ff_bsf_graph_remove_filter(AVBitStreamFilterGraph *graph, AVBitStreamFilterContext *filter)
65
0
{
66
0
    int i, j;
67
0
    for (i = 0; i < graph->nb_filters; i++) {
68
0
        if (graph->filters[i] == filter) {
69
0
            FFSWAP(AVBitStreamFilterContext*, graph->filters[i],
70
0
                   graph->filters[graph->nb_filters - 1]);
71
0
            graph->nb_filters--;
72
0
            filter->graph = NULL;
73
0
            for (j = 0; j<filter->nb_outputs; j++)
74
0
                if (filter->outputs[j])
75
0
                    filter->outputs[j]->graph = NULL;
76
77
0
            return;
78
0
        }
79
0
    }
80
0
}
81
82
AVBitStreamFilterContext *av_bsf_graph_get_filter(AVBitStreamFilterGraph *graph, const char *name)
83
0
{
84
0
    int i;
85
86
0
    for (i = 0; i < graph->nb_filters; i++)
87
0
        if (graph->filters[i]->name && !strcmp(name, graph->filters[i]->name))
88
0
            return graph->filters[i];
89
90
0
    return NULL;
91
0
}
92
93
void av_bsf_graph_free(AVBitStreamFilterGraph **graphp)
94
0
{
95
0
    AVBitStreamFilterGraph *graph = *graphp;
96
0
    FFBitStreamFilterGraph *graphi = ffbsffiltergraph(graph);
97
98
0
    if (!graph)
99
0
        return;
100
101
0
    while (graph->nb_filters)
102
0
        ff_bsf_free(graph->filters[0]);
103
104
0
    av_freep(&graphi->sink_links);
105
0
    av_freep(&graphi->source_links);
106
107
0
    av_opt_free(graph);
108
109
0
    av_freep(&graph->filters);
110
0
    av_freep(graphp);
111
0
}
112
113
int av_bsf_graph_create_filter(AVBitStreamFilterContext **filt_ctx, const AVBitStreamFilter *filt,
114
                               const char *name, AVDictionary **options, AVBitStreamFilterGraph *graph_ctx)
115
0
{
116
0
    AVBitStreamFilterContext *s;
117
0
    int ret;
118
119
0
    ret = av_bsf_graph_alloc_filter(&s, filt, name, graph_ctx);
120
0
    if (ret < 0)
121
0
        return ret;
122
123
0
    ret = av_bsf_init_dict(s, options);
124
0
    if (ret < 0)
125
0
        goto fail;
126
127
0
    if (filt_ctx)
128
0
        *filt_ctx = s;
129
130
0
    return 0;
131
132
0
fail:
133
0
    ff_bsf_free(s);
134
0
    if (filt_ctx)
135
0
        *filt_ctx = NULL;
136
0
    return ret;
137
0
}
138
139
int av_bsf_graph_alloc_filter(AVBitStreamFilterContext **filt_ctx,
140
                              const AVBitStreamFilter *filter,
141
                              const char *name,
142
                              AVBitStreamFilterGraph *graph)
143
0
{
144
0
    AVBitStreamFilterContext **filters, *s;
145
0
    int ret;
146
147
0
    if (!ff_bsf(filter)->activate && !ff_bsf(filter)->nb_inputs && !ff_bsf(filter)->nb_outputs)
148
0
        return AVERROR(ENOTSUP);
149
150
0
    filters = av_realloc_array(graph->filters, graph->nb_filters + 1, sizeof(*filters));
151
0
    if (!filters)
152
0
        return AVERROR(ENOMEM);
153
0
    graph->filters = filters;
154
155
0
    ret = ff_bsf_alloc(filter, name, &s);
156
0
    if (ret < 0)
157
0
        return ret;
158
159
0
    graph->filters[graph->nb_filters++] = s;
160
161
0
    s->graph = graph;
162
163
0
    if (filt_ctx)
164
0
        *filt_ctx = s;
165
166
0
    return ret;
167
0
}
168
169
/**
170
 * Check for the validity of graph.
171
 *
172
 * A graph is considered valid if all its input and output pads are
173
 * connected.
174
 *
175
 * @return >= 0 in case of success, a negative value otherwise
176
 */
177
static int graph_check_validity(AVBitStreamFilterGraph *graph, void *log_ctx)
178
0
{
179
0
    AVBitStreamFilterContext *filt;
180
0
    int i, j;
181
182
0
    for (i = 0; i < graph->nb_filters; i++) {
183
0
        const AVBitStreamFilterPad *pad;
184
0
        filt = graph->filters[i];
185
186
0
        for (j = 0; j < filt->nb_inputs; j++) {
187
0
            if (!filt->inputs[j] || !filt->inputs[j]->src) {
188
0
                pad = &filt->input_pads[j];
189
0
                av_log(log_ctx, AV_LOG_ERROR,
190
0
                       "Input pad \"%s\" of the filter instance \"%s\" of %s not connected to any source\n",
191
0
                       pad->name, filt->name, filt->filter->name);
192
0
                return AVERROR(EINVAL);
193
0
            }
194
0
        }
195
196
0
        for (j = 0; j < filt->nb_outputs; j++) {
197
0
            if (!filt->outputs[j] || !filt->outputs[j]->dst) {
198
0
                pad = &filt->output_pads[j];
199
0
                av_log(log_ctx, AV_LOG_ERROR,
200
0
                       "Output pad \"%s\" of the filter instance \"%s\" of %s not connected to any destination\n",
201
0
                       pad->name, filt->name, filt->filter->name);
202
0
                return AVERROR(EINVAL);
203
0
            }
204
0
        }
205
0
    }
206
207
0
    return 0;
208
0
}
209
210
/**
211
 * Configure all the links of graphctx.
212
 *
213
 * @return >= 0 in case of success, a negative value otherwise
214
 */
215
static int graph_config_links(AVBitStreamFilterGraph *graph, void *log_ctx)
216
0
{
217
0
    AVBitStreamFilterContext *filt;
218
0
    int i, ret;
219
220
0
    for (i = 0; i < graph->nb_filters; i++) {
221
0
        filt = graph->filters[i];
222
223
0
        if (!filt->nb_outputs) {
224
0
            if ((ret = ff_bsf_config_links(filt)))
225
0
                return ret;
226
0
        }
227
0
    }
228
229
0
    return 0;
230
0
}
231
232
static int graph_config_pointers(AVBitStreamFilterGraph *graph, void *log_ctx)
233
0
{
234
0
    unsigned i, j;
235
0
    int sink_links_count = 0, source_links_count = 0, n = 0;
236
0
    AVBitStreamFilterContext *f;
237
0
    BitStreamFilterLinkInternal **sinks, **sources;
238
239
0
    for (i = 0; i < graph->nb_filters; i++) {
240
0
        f = graph->filters[i];
241
0
        for (j = 0; j < f->nb_inputs; j++) {
242
0
            ff_link_internal(f->inputs[j])->age_index  = -1;
243
0
        }
244
0
        for (j = 0; j < f->nb_outputs; j++) {
245
0
            ff_link_internal(f->outputs[j])->age_index = -1;
246
0
        }
247
0
        if (!f->nb_outputs) {
248
0
            if (f->nb_inputs > INT_MAX - sink_links_count)
249
0
                return AVERROR(EINVAL);
250
0
            sink_links_count += f->nb_inputs;
251
0
        }
252
0
        if (!f->nb_inputs && !strcmp(f->filter->name, "source")) {
253
0
            if (f->nb_outputs > INT_MAX - source_links_count)
254
0
                return AVERROR(EINVAL);
255
0
            source_links_count += f->nb_outputs;
256
0
        }
257
0
    }
258
0
    sinks = av_calloc(sink_links_count, sizeof(*sinks));
259
0
    if (!sinks)
260
0
        return AVERROR(ENOMEM);
261
0
    for (i = 0; i < graph->nb_filters; i++) {
262
0
        f = graph->filters[i];
263
0
        if (!f->nb_outputs) {
264
0
            for (j = 0; j < f->nb_inputs; j++) {
265
0
                sinks[n] = ff_link_internal(f->inputs[j]);
266
0
                sinks[n]->age_index = n;
267
0
                n++;
268
0
            }
269
0
        }
270
0
    }
271
0
    av_assert0(n == sink_links_count);
272
0
    ffbsffiltergraph(graph)->sink_links       = sinks;
273
0
    ffbsffiltergraph(graph)->sink_links_count = sink_links_count;
274
275
0
    sources = av_calloc(source_links_count, sizeof(*sources));
276
0
    if (!sources)
277
0
        return AVERROR(ENOMEM);
278
0
    for (i = 0, n = 0; i < graph->nb_filters; i++) {
279
0
        f = graph->filters[i];
280
0
        if (!f->nb_inputs && !strcmp(f->filter->name, "source")) {
281
0
            for (j = 0; j < f->nb_outputs; j++) {
282
0
                sources[n] = ff_link_internal(f->outputs[j]);
283
0
                n++;
284
0
            }
285
0
        }
286
0
    }
287
0
    av_assert0(n == source_links_count);
288
0
    ffbsffiltergraph(graph)->source_links       = sources;
289
0
    ffbsffiltergraph(graph)->source_links_count = source_links_count;
290
291
0
    return 0;
292
0
}
293
294
int av_bsf_graph_config(AVBitStreamFilterGraph *graphctx, void *log_ctx)
295
0
{
296
0
    int ret;
297
298
0
    if (graphctx->max_buffered_packets)
299
0
        ffbsffiltergraph(graphctx)->max_packet_queue = graphctx->max_buffered_packets;
300
0
    if ((ret = graph_check_validity(graphctx, log_ctx)))
301
0
        return ret;
302
0
    if ((ret = graph_config_links(graphctx, log_ctx)))
303
0
        return ret;
304
0
    if ((ret = graph_config_pointers(graphctx, log_ctx)))
305
0
        return ret;
306
307
0
    return 0;
308
0
}
309
310
static void heap_bubble_up(FFBitStreamFilterGraph *graph,
311
                           BitStreamFilterLinkInternal *li, int index)
312
0
{
313
0
    BitStreamFilterLinkInternal **links = graph->sink_links;
314
315
0
    av_assert0(index >= 0);
316
317
0
    while (index) {
318
0
        int parent = (index - 1) >> 1;
319
0
        if (links[parent]->l.current_pts_us >= li->l.current_pts_us)
320
0
            break;
321
0
        links[index] = links[parent];
322
0
        links[index]->age_index = index;
323
0
        index = parent;
324
0
    }
325
0
    links[index] = li;
326
0
    li->age_index = index;
327
0
}
328
329
static void heap_bubble_down(FFBitStreamFilterGraph *graph,
330
                             BitStreamFilterLinkInternal *li, int index)
331
0
{
332
0
    BitStreamFilterLinkInternal **links = graph->sink_links;
333
334
0
    av_assert0(index >= 0);
335
336
0
    while (1) {
337
0
        int child = 2 * index + 1;
338
0
        if (child >= graph->sink_links_count)
339
0
            break;
340
0
        if (child + 1 < graph->sink_links_count &&
341
0
            links[child + 1]->l.current_pts_us < links[child]->l.current_pts_us)
342
0
            child++;
343
0
        if (li->l.current_pts_us < links[child]->l.current_pts_us)
344
0
            break;
345
0
        links[index] = links[child];
346
0
        links[index]->age_index = index;
347
0
        index = child;
348
0
    }
349
0
    links[index] = li;
350
0
    li->age_index = index;
351
0
}
352
353
void ff_bsf_graph_update_heap(AVBitStreamFilterGraph *graph, BitStreamFilterLinkInternal *li)
354
0
{
355
0
    FFBitStreamFilterGraph  *graphi = ffbsffiltergraph(graph);
356
357
0
    heap_bubble_up  (graphi, li, li->age_index);
358
0
    heap_bubble_down(graphi, li, li->age_index);
359
0
}
360
361
int ff_bsf_graph_run_once(AVBitStreamFilterGraph *graph)
362
0
{
363
0
    FFBitStreamFilterContext *ctxi;
364
0
    unsigned i;
365
366
0
    av_assert0(graph->nb_filters);
367
0
    ctxi = ffbsfctx(graph->filters[0]);
368
0
    for (i = 1; i < graph->nb_filters; i++) {
369
0
        FFBitStreamFilterContext *ctxi_other = ffbsfctx(graph->filters[i]);
370
371
0
        if (ctxi_other->ready > ctxi->ready)
372
0
            ctxi = ctxi_other;
373
0
    }
374
375
0
    if (!ctxi->ready)
376
0
        return AVERROR(EAGAIN);
377
378
0
    ctxi->ready = 0;
379
380
0
    return ff_bsf_activate(&ctxi->p);
381
0
}
382
383
int av_bsf_graph_source_needs_input(const AVBitStreamFilterGraph *graph)
384
0
{
385
0
    const FFBitStreamFilterGraph *graphi = cffbsffiltergraph(graph);
386
0
    int nb_requests, nb_requests_max = -1;
387
0
    int best_input = AVERROR(EOF);
388
389
0
    for (int i = 0; i < graphi->source_links_count; i++) {
390
0
        const BitStreamFilterLinkInternal *sourcei = graphi->source_links[i];
391
0
        const AVBitStreamFilterLink *source = &sourcei->l;
392
393
0
        if (av_bsf_source_get_status(source->src) == AVERROR(EOF))
394
0
            continue;
395
396
0
        nb_requests = ff_bsf_source_get_nb_failed_requests(source->src);
397
0
        if (nb_requests > nb_requests_max) {
398
0
            nb_requests_max = nb_requests;
399
0
            best_input = i;
400
0
        }
401
0
    }
402
403
0
    return best_input;
404
0
}