Coverage Report

Created: 2026-01-16 07:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/bsf/av1_frame_split.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2019 James Almer <jamrial@gmail.com>
3
 *
4
 * This file is part of FFmpeg.
5
 *
6
 * FFmpeg is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * FFmpeg is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with FFmpeg; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
 */
20
21
/**
22
 * @file
23
 * This bitstream filter splits AV1 Temporal Units into packets containing
24
 * just one frame, plus any leading and trailing OBUs that may be present at
25
 * the beginning or end, respectively.
26
 *
27
 * Temporal Units already containing only one frame will be passed through
28
 * unchanged. When splitting can't be performed, the Temporal Unit will be
29
 * passed through containing only the remaining OBUs starting from the first
30
 * one after the last successfully split frame.
31
 */
32
33
#include "libavutil/avassert.h"
34
35
#include "bsf.h"
36
#include "bsf_internal.h"
37
#include "cbs.h"
38
#include "cbs_av1.h"
39
40
typedef struct AV1FSplitContext {
41
    AVPacket *buffer_pkt;
42
    CodedBitstreamContext *cbc;
43
    CodedBitstreamFragment temporal_unit;
44
45
    int nb_frames;
46
    int cur_frame;
47
    int cur_frame_idx;
48
    int last_frame_idx;
49
} AV1FSplitContext;
50
51
static int av1_frame_split_filter(AVBSFContext *ctx, AVPacket *out)
52
535k
{
53
535k
    AV1FSplitContext *s = ctx->priv_data;
54
535k
    CodedBitstreamFragment *td = &s->temporal_unit;
55
535k
    int i, ret;
56
535k
    int split = !!s->buffer_pkt->data;
57
58
535k
    if (!s->buffer_pkt->data) {
59
276k
        int nb_frames = 0;
60
61
276k
        ret = ff_bsf_get_packet_ref(ctx, s->buffer_pkt);
62
276k
        if (ret < 0)
63
138k
            return ret;
64
65
137k
        ret = ff_cbs_read_packet(s->cbc, td, s->buffer_pkt);
66
137k
        if (ret < 0) {
67
126k
            av_log(ctx, AV_LOG_WARNING, "Failed to parse temporal unit.\n");
68
126k
            goto passthrough;
69
126k
        }
70
71
1.64M
        for (i = 0; i < td->nb_units; i++) {
72
1.63M
            CodedBitstreamUnit *unit = &td->units[i];
73
74
1.63M
            if (unit->type == AV1_OBU_FRAME ||
75
1.62M
                unit->type == AV1_OBU_FRAME_HEADER)
76
265k
                nb_frames++;
77
1.36M
            else if (unit->type == AV1_OBU_TILE_LIST) {
78
349
                av_log(ctx, AV_LOG_VERBOSE, "Large scale tiles are unsupported.\n");
79
349
                goto passthrough;
80
349
            }
81
1.63M
        }
82
9.76k
        if (nb_frames > 1) {
83
1.69k
            s->cur_frame = 0;
84
1.69k
            s->cur_frame_idx = s->last_frame_idx = 0;
85
1.69k
            s->nb_frames = nb_frames;
86
1.69k
            split = 1;
87
1.69k
        }
88
9.76k
    }
89
90
269k
    if (split) {
91
261k
        AV1RawFrameHeader *frame = NULL;
92
261k
        int cur_frame_type = -1, size = 0;
93
94
349k
        for (i = s->cur_frame_idx; i < td->nb_units; i++) {
95
349k
            CodedBitstreamUnit *unit = &td->units[i];
96
97
349k
            size += unit->data_size;
98
349k
            if (unit->type == AV1_OBU_FRAME) {
99
3.59k
                AV1RawOBU *obu = unit->content;
100
101
3.59k
                if (frame) {
102
194
                    av_log(ctx, AV_LOG_WARNING, "Frame OBU found when Tile data for a "
103
194
                                                "previous frame was expected.\n");
104
194
                    goto passthrough;
105
194
                }
106
107
3.40k
                frame = &obu->obu.frame.header;
108
3.40k
                cur_frame_type = obu->header.obu_type;
109
3.40k
                s->last_frame_idx = s->cur_frame_idx;
110
3.40k
                s->cur_frame_idx  = i + 1;
111
3.40k
                s->cur_frame++;
112
113
                // split here unless it's the last frame, in which case
114
                // include every trailing OBU
115
3.40k
                if (s->cur_frame < s->nb_frames)
116
2.83k
                    break;
117
345k
            } else if (unit->type == AV1_OBU_FRAME_HEADER) {
118
257k
                AV1RawOBU *obu = unit->content;
119
120
257k
                if (frame) {
121
194
                    av_log(ctx, AV_LOG_WARNING, "Frame Header OBU found when Tile data for a "
122
194
                                                "previous frame was expected.\n");
123
194
                    goto passthrough;
124
194
                }
125
126
257k
                frame = &obu->obu.frame_header;
127
257k
                cur_frame_type = obu->header.obu_type;
128
257k
                s->last_frame_idx = s->cur_frame_idx;
129
257k
                s->cur_frame++;
130
131
                // split here if show_existing_frame unless it's the last
132
                // frame, in which case include every trailing OBU
133
257k
                if (frame->show_existing_frame &&
134
256k
                    s->cur_frame < s->nb_frames) {
135
256k
                    s->cur_frame_idx = i + 1;
136
256k
                    break;
137
256k
                }
138
257k
            } else if (unit->type == AV1_OBU_TILE_GROUP) {
139
1.09k
                AV1RawOBU *obu = unit->content;
140
1.09k
                AV1RawTileGroup *group = &obu->obu.tile_group;
141
142
1.09k
                if (!frame || cur_frame_type != AV1_OBU_FRAME_HEADER) {
143
395
                    av_log(ctx, AV_LOG_WARNING, "Unexpected Tile Group OBU found before a "
144
395
                                                "Frame Header.\n");
145
395
                    goto passthrough;
146
395
                }
147
148
695
                if ((group->tg_end == (frame->tile_cols * frame->tile_rows) - 1) &&
149
                    // include every trailing OBU with the last frame
150
476
                    s->cur_frame < s->nb_frames) {
151
195
                    s->cur_frame_idx = i + 1;
152
195
                    break;
153
195
                }
154
695
            }
155
349k
        }
156
260k
        av_assert0(frame && s->cur_frame <= s->nb_frames);
157
158
260k
        ret = av_packet_ref(out, s->buffer_pkt);
159
260k
        if (ret < 0)
160
0
            goto fail;
161
162
260k
        out->data = (uint8_t *)td->units[s->last_frame_idx].data;
163
260k
        out->size = size;
164
165
        // skip the frame in the buffer packet if it's split successfully, so it's not present
166
        // if the packet is passed through in case of failure when splitting another frame.
167
260k
        s->buffer_pkt->data += size;
168
260k
        s->buffer_pkt->size -= size;
169
170
260k
        if (!frame->show_existing_frame && !frame->show_frame)
171
491
            out->pts = AV_NOPTS_VALUE;
172
173
260k
        if (s->cur_frame == s->nb_frames) {
174
907
            av_packet_unref(s->buffer_pkt);
175
907
            ff_cbs_fragment_reset(td);
176
907
        }
177
178
260k
        return 0;
179
260k
    }
180
181
136k
passthrough:
182
136k
    av_packet_move_ref(out, s->buffer_pkt);
183
184
136k
    ret = 0;
185
136k
fail:
186
136k
    if (ret < 0) {
187
0
        av_packet_unref(out);
188
0
        av_packet_unref(s->buffer_pkt);
189
0
    }
190
136k
    ff_cbs_fragment_reset(td);
191
192
136k
    return ret;
193
136k
}
194
195
static const CodedBitstreamUnitType decompose_unit_types[] = {
196
    AV1_OBU_TEMPORAL_DELIMITER,
197
    AV1_OBU_SEQUENCE_HEADER,
198
    AV1_OBU_FRAME_HEADER,
199
    AV1_OBU_TILE_GROUP,
200
    AV1_OBU_FRAME,
201
};
202
203
static int av1_frame_split_init(AVBSFContext *ctx)
204
1.96k
{
205
1.96k
    AV1FSplitContext *s = ctx->priv_data;
206
1.96k
    CodedBitstreamFragment *td = &s->temporal_unit;
207
1.96k
    int ret;
208
209
1.96k
    s->buffer_pkt = av_packet_alloc();
210
1.96k
    if (!s->buffer_pkt)
211
0
        return AVERROR(ENOMEM);
212
213
1.96k
    ret = ff_cbs_init(&s->cbc, AV_CODEC_ID_AV1, ctx);
214
1.96k
    if (ret < 0)
215
0
        return ret;
216
217
1.96k
    s->cbc->decompose_unit_types    = decompose_unit_types;
218
1.96k
    s->cbc->nb_decompose_unit_types = FF_ARRAY_ELEMS(decompose_unit_types);
219
220
1.96k
    if (!ctx->par_in->extradata_size)
221
1.89k
        return 0;
222
223
69
    ret = ff_cbs_read_extradata(s->cbc, td, ctx->par_in);
224
69
    if (ret < 0)
225
49
        av_log(ctx, AV_LOG_WARNING, "Failed to parse extradata.\n");
226
227
69
    ff_cbs_fragment_reset(td);
228
229
69
    return 0;
230
1.96k
}
231
232
static void av1_frame_split_flush(AVBSFContext *ctx)
233
36.1k
{
234
36.1k
    AV1FSplitContext *s = ctx->priv_data;
235
236
36.1k
    av_packet_unref(s->buffer_pkt);
237
36.1k
    ff_cbs_fragment_reset(&s->temporal_unit);
238
36.1k
}
239
240
static void av1_frame_split_close(AVBSFContext *ctx)
241
1.98k
{
242
1.98k
    AV1FSplitContext *s = ctx->priv_data;
243
244
1.98k
    av_packet_free(&s->buffer_pkt);
245
1.98k
    ff_cbs_fragment_free(&s->temporal_unit);
246
1.98k
    ff_cbs_close(&s->cbc);
247
1.98k
}
248
249
static const enum AVCodecID av1_frame_split_codec_ids[] = {
250
    AV_CODEC_ID_AV1, AV_CODEC_ID_NONE,
251
};
252
253
const FFBitStreamFilter ff_av1_frame_split_bsf = {
254
    .p.name         = "av1_frame_split",
255
    .p.codec_ids    = av1_frame_split_codec_ids,
256
    .priv_data_size = sizeof(AV1FSplitContext),
257
    .init           = av1_frame_split_init,
258
    .flush          = av1_frame_split_flush,
259
    .close          = av1_frame_split_close,
260
    .filter         = av1_frame_split_filter,
261
};