GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavcodec/mpeg2_metadata_bsf.c Lines: 66 119 55.5 %
Date: 2020-09-25 23:16:12 Branches: 34 90 37.8 %

Line Branch Exec 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 "libavutil/avstring.h"
20
#include "libavutil/common.h"
21
#include "libavutil/opt.h"
22
23
#include "bsf.h"
24
#include "bsf_internal.h"
25
#include "cbs.h"
26
#include "cbs_mpeg2.h"
27
#include "mpeg12.h"
28
29
typedef struct MPEG2MetadataContext {
30
    const AVClass *class;
31
32
    CodedBitstreamContext *cbc;
33
    CodedBitstreamFragment fragment;
34
35
    MPEG2RawExtensionData sequence_display_extension;
36
37
    AVRational display_aspect_ratio;
38
39
    AVRational frame_rate;
40
41
    int video_format;
42
    int colour_primaries;
43
    int transfer_characteristics;
44
    int matrix_coefficients;
45
46
    int mpeg1_warned;
47
} MPEG2MetadataContext;
48
49
50
18
static int mpeg2_metadata_update_fragment(AVBSFContext *bsf,
51
                                          CodedBitstreamFragment *frag)
52
{
53
18
    MPEG2MetadataContext             *ctx = bsf->priv_data;
54
18
    MPEG2RawSequenceHeader            *sh = NULL;
55
18
    MPEG2RawSequenceExtension         *se = NULL;
56
18
    MPEG2RawSequenceDisplayExtension *sde = NULL;
57
    int i, se_pos;
58
59
8861
    for (i = 0; i < frag->nb_units; i++) {
60
8843
        if (frag->units[i].type == MPEG2_START_SEQUENCE_HEADER) {
61
6
            sh = frag->units[i].content;
62
8837
        } else if (frag->units[i].type == MPEG2_START_EXTENSION) {
63
53
            MPEG2RawExtensionData *ext = frag->units[i].content;
64
53
            if (ext->extension_start_code_identifier ==
65
                MPEG2_EXTENSION_SEQUENCE) {
66
6
                se = &ext->data.sequence;
67
6
                se_pos = i;
68
47
            } else if (ext->extension_start_code_identifier ==
69
                MPEG2_EXTENSION_SEQUENCE_DISPLAY) {
70
3
                sde = &ext->data.sequence_display;
71
            }
72
        }
73
    }
74
75

18
    if (!sh || !se) {
76
        // No sequence header and sequence extension: not an MPEG-2 video
77
        // sequence.
78

12
        if (sh && !ctx->mpeg1_warned) {
79
            av_log(bsf, AV_LOG_WARNING, "Stream contains a sequence "
80
                   "header but not a sequence extension: maybe it's "
81
                   "actually MPEG-1?\n");
82
            ctx->mpeg1_warned = 1;
83
        }
84
12
        return 0;
85
    }
86
87

6
    if (ctx->display_aspect_ratio.num && ctx->display_aspect_ratio.den) {
88
        int num, den;
89
90
        av_reduce(&num, &den, ctx->display_aspect_ratio.num,
91
                  ctx->display_aspect_ratio.den, 65535);
92
93
        if (num == 4 && den == 3)
94
            sh->aspect_ratio_information = 2;
95
        else if (num == 16 && den == 9)
96
            sh->aspect_ratio_information = 3;
97
        else if (num == 221 && den == 100)
98
            sh->aspect_ratio_information = 4;
99
        else
100
            sh->aspect_ratio_information = 1;
101
    }
102
103

6
    if (ctx->frame_rate.num && ctx->frame_rate.den) {
104
        int code, ext_n, ext_d;
105
106
        ff_mpeg12_find_best_frame_rate(ctx->frame_rate,
107
                                       &code, &ext_n, &ext_d, 0);
108
109
        sh->frame_rate_code        = code;
110
        se->frame_rate_extension_n = ext_n;
111
        se->frame_rate_extension_d = ext_d;
112
    }
113
114
6
    if (ctx->video_format             >= 0 ||
115
6
        ctx->colour_primaries         >= 0 ||
116
6
        ctx->transfer_characteristics >= 0 ||
117
6
        ctx->matrix_coefficients      >= 0) {
118
        if (!sde) {
119
            int err;
120
            ctx->sequence_display_extension.extension_start_code =
121
                MPEG2_START_EXTENSION;
122
            ctx->sequence_display_extension.extension_start_code_identifier =
123
                MPEG2_EXTENSION_SEQUENCE_DISPLAY;
124
            sde = &ctx->sequence_display_extension.data.sequence_display;
125
126
            *sde = (MPEG2RawSequenceDisplayExtension) {
127
                .video_format = 5,
128
129
                .colour_description       = 0,
130
                .colour_primaries         = 2,
131
                .transfer_characteristics = 2,
132
                .matrix_coefficients      = 2,
133
134
                .display_horizontal_size =
135
                    se->horizontal_size_extension << 12 | sh->horizontal_size_value,
136
                .display_vertical_size =
137
                    se->vertical_size_extension << 12 | sh->vertical_size_value,
138
            };
139
140
            err = ff_cbs_insert_unit_content(frag, se_pos + 1,
141
                                             MPEG2_START_EXTENSION,
142
                                             &ctx->sequence_display_extension,
143
                                             NULL);
144
            if (err < 0) {
145
                av_log(bsf, AV_LOG_ERROR, "Failed to insert new sequence "
146
                       "display extension.\n");
147
                return err;
148
            }
149
        }
150
151
        if (ctx->video_format >= 0)
152
            sde->video_format = ctx->video_format;
153
154
        if (ctx->colour_primaries         >= 0 ||
155
            ctx->transfer_characteristics >= 0 ||
156
            ctx->matrix_coefficients      >= 0) {
157
            sde->colour_description = 1;
158
159
            if (ctx->colour_primaries >= 0)
160
                sde->colour_primaries = ctx->colour_primaries;
161
162
            if (ctx->transfer_characteristics >= 0)
163
                sde->transfer_characteristics = ctx->transfer_characteristics;
164
165
            if (ctx->matrix_coefficients >= 0)
166
                sde->matrix_coefficients = ctx->matrix_coefficients;
167
        }
168
    }
169
170
6
    return 0;
171
}
172
173
30
static int mpeg2_metadata_filter(AVBSFContext *bsf, AVPacket *pkt)
174
{
175
30
    MPEG2MetadataContext *ctx = bsf->priv_data;
176
30
    CodedBitstreamFragment *frag = &ctx->fragment;
177
    int err;
178
179
30
    err = ff_bsf_get_packet_ref(bsf, pkt);
180
30
    if (err < 0)
181
15
        return err;
182
183
15
    err = ff_cbs_read_packet(ctx->cbc, frag, pkt);
184
15
    if (err < 0) {
185
        av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n");
186
        goto fail;
187
    }
188
189
15
    err = mpeg2_metadata_update_fragment(bsf, frag);
190
15
    if (err < 0) {
191
        av_log(bsf, AV_LOG_ERROR, "Failed to update frame fragment.\n");
192
        goto fail;
193
    }
194
195
15
    err = ff_cbs_write_packet(ctx->cbc, pkt, frag);
196
15
    if (err < 0) {
197
        av_log(bsf, AV_LOG_ERROR, "Failed to write packet.\n");
198
        goto fail;
199
    }
200
201
15
    err = 0;
202
15
fail:
203
15
    ff_cbs_fragment_reset(frag);
204
205
15
    if (err < 0)
206
        av_packet_unref(pkt);
207
208
15
    return err;
209
}
210
211
3
static int mpeg2_metadata_init(AVBSFContext *bsf)
212
{
213
3
    MPEG2MetadataContext *ctx = bsf->priv_data;
214
3
    CodedBitstreamFragment *frag = &ctx->fragment;
215
    int err;
216
217
#define VALIDITY_CHECK(name) do { \
218
        if (!ctx->name) { \
219
            av_log(bsf, AV_LOG_ERROR, "The value 0 for %s is " \
220
                                      "forbidden.\n", #name); \
221
            return AVERROR(EINVAL); \
222
        } \
223
    } while (0)
224
3
    VALIDITY_CHECK(colour_primaries);
225
3
    VALIDITY_CHECK(transfer_characteristics);
226
3
    VALIDITY_CHECK(matrix_coefficients);
227
#undef VALIDITY_CHECK
228
229
3
    err = ff_cbs_init(&ctx->cbc, AV_CODEC_ID_MPEG2VIDEO, bsf);
230
3
    if (err < 0)
231
        return err;
232
233
3
    if (bsf->par_in->extradata) {
234
3
        err = ff_cbs_read_extradata(ctx->cbc, frag, bsf->par_in);
235
3
        if (err < 0) {
236
            av_log(bsf, AV_LOG_ERROR, "Failed to read extradata.\n");
237
            goto fail;
238
        }
239
240
3
        err = mpeg2_metadata_update_fragment(bsf, frag);
241
3
        if (err < 0) {
242
            av_log(bsf, AV_LOG_ERROR, "Failed to update metadata fragment.\n");
243
            goto fail;
244
        }
245
246
3
        err = ff_cbs_write_extradata(ctx->cbc, bsf->par_out, frag);
247
3
        if (err < 0) {
248
            av_log(bsf, AV_LOG_ERROR, "Failed to write extradata.\n");
249
            goto fail;
250
        }
251
    }
252
253
3
    err = 0;
254
3
fail:
255
3
    ff_cbs_fragment_reset(frag);
256
3
    return err;
257
}
258
259
3
static void mpeg2_metadata_close(AVBSFContext *bsf)
260
{
261
3
    MPEG2MetadataContext *ctx = bsf->priv_data;
262
263
3
    ff_cbs_fragment_free(&ctx->fragment);
264
3
    ff_cbs_close(&ctx->cbc);
265
3
}
266
267
#define OFFSET(x) offsetof(MPEG2MetadataContext, x)
268
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
269
static const AVOption mpeg2_metadata_options[] = {
270
    { "display_aspect_ratio", "Set display aspect ratio (table 6-3)",
271
        OFFSET(display_aspect_ratio), AV_OPT_TYPE_RATIONAL,
272
        { .dbl = 0.0 }, 0, 65535, FLAGS },
273
274
    { "frame_rate", "Set frame rate",
275
        OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL,
276
        { .dbl = 0.0 }, 0, UINT_MAX, FLAGS },
277
278
    { "video_format", "Set video format (table 6-6)",
279
        OFFSET(video_format), AV_OPT_TYPE_INT,
280
        { .i64 = -1 }, -1, 7, FLAGS },
281
    { "colour_primaries", "Set colour primaries (table 6-7)",
282
        OFFSET(colour_primaries), AV_OPT_TYPE_INT,
283
        { .i64 = -1 }, -1, 255, FLAGS },
284
    { "transfer_characteristics", "Set transfer characteristics (table 6-8)",
285
        OFFSET(transfer_characteristics), AV_OPT_TYPE_INT,
286
        { .i64 = -1 }, -1, 255, FLAGS },
287
    { "matrix_coefficients", "Set matrix coefficients (table 6-9)",
288
        OFFSET(matrix_coefficients), AV_OPT_TYPE_INT,
289
        { .i64 = -1 }, -1, 255, FLAGS },
290
291
    { NULL }
292
};
293
294
static const AVClass mpeg2_metadata_class = {
295
    .class_name = "mpeg2_metadata_bsf",
296
    .item_name  = av_default_item_name,
297
    .option     = mpeg2_metadata_options,
298
    .version    = LIBAVUTIL_VERSION_INT,
299
};
300
301
static const enum AVCodecID mpeg2_metadata_codec_ids[] = {
302
    AV_CODEC_ID_MPEG2VIDEO, AV_CODEC_ID_NONE,
303
};
304
305
const AVBitStreamFilter ff_mpeg2_metadata_bsf = {
306
    .name           = "mpeg2_metadata",
307
    .priv_data_size = sizeof(MPEG2MetadataContext),
308
    .priv_class     = &mpeg2_metadata_class,
309
    .init           = &mpeg2_metadata_init,
310
    .close          = &mpeg2_metadata_close,
311
    .filter         = &mpeg2_metadata_filter,
312
    .codec_ids      = mpeg2_metadata_codec_ids,
313
};