aboutsummaryrefslogtreecommitdiffstats
path: root/libavcodec/bsf/dv_error_marker.c
blob: 131c6fcbe646d777c24d682710f8c351337d8b03 (plain) (blame)
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
/*
 * Copyright (c) 2022 Michael Niedermayer
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "bsf.h"
#include "bsf_internal.h"
#include "libavutil/colorspace.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/opt.h"

typedef struct DVErrorMarkerContext {
    const AVClass *class;
    uint8_t color_rgba[4];
    int sta;
    uint8_t marked_block[76];
} DVErrorMarkerContext;

static void setdc(uint8_t *b, const uint8_t color_rgba[4], int cblocks, int y_step, int v_step, int u_step) {
    for (int i=0; i<4; i++) {
        b[0] = RGB_TO_Y_JPEG(color_rgba[0], color_rgba[1],color_rgba[2]) + 128;
        b[1] = 0x06;
        b += y_step;
    }
    for (int i=0; i<cblocks; i++) {
        b[0] = RGB_TO_V_JPEG(color_rgba[0], color_rgba[1],color_rgba[2]) - 128;
        b[1] = 0x16;
        b += v_step;
    }
    for (int i=0; i<cblocks; i++) {
        b[0] = RGB_TO_U_JPEG(color_rgba[0], color_rgba[1],color_rgba[2]) - 128;
        b[1] = 0x16;
        b += u_step;
    }
}

static int dv_error_marker_init(AVBSFContext *ctx)
{
    DVErrorMarkerContext *s = ctx->priv_data;

    memset(s->marked_block, -1, 76);
    setdc(s->marked_block, s->color_rgba, 1, 14, 10, 10);
    setdc(s->marked_block, s->color_rgba, 2, 10, 10,  8);

    return 0;
}

static int dv_error_marker_filter(AVBSFContext *ctx, AVPacket *pkt)
{
    DVErrorMarkerContext *s = ctx->priv_data;
    int ret = ff_bsf_get_packet_ref(ctx, pkt);
    uint8_t *p;
    int writable = 0;
    int stamask = s->sta;
    int match_count = 0;

    if (ret < 0)
        return ret;

    p = pkt->data;
    for(int i = 0; i < pkt->size - 79; i+=80) {
        // see page 44-46 or section 5.5 of http://web.archive.org/web/20060927044735/http://www.smpte.org/smpte_store/standards/pdf/s314m.pdf.
        if ((p[i] >> 4) == 9 && ((stamask >> (p[i+3] >> 4))&1)) {
            if (!writable) {
                ret = av_packet_make_writable(pkt);
                if (ret < 0) {
                    av_packet_unref(pkt);
                    return ret;
                }
                writable = 1;
                p = pkt->data;
            }
            memcpy(p+i+4, s->marked_block, 76);
            match_count ++;
        }
    }
    av_log(ctx, AV_LOG_DEBUG, "%8"PRId64": Replaced %5d blocks by color %X\n", pkt->pts, match_count, AV_RB32(s->color_rgba));

    return 0;
}

#define OFFSET(x) offsetof(DVErrorMarkerContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption options[] = {
    { "color"  , "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "yellow"}, 0,      0, FLAGS },
    { "sta"    , "specify which error status value to match"
                            , OFFSET(sta       ), AV_OPT_TYPE_FLAGS, {.i64 =   0xFFFE}, 0, 0xFFFF, FLAGS, "sta" },
    { "ok"     , "No error, no concealment",                        0, AV_OPT_TYPE_CONST, {.i64 =   0x0001}, 0, 0xFFFF, FLAGS, "sta"},
    { "Aa"     , "No error, concealment from previous frame type a",0, AV_OPT_TYPE_CONST, {.i64 =   0x0004}, 0, 0xFFFF, FLAGS, "sta"},
    { "Ba"     , "No error, concealment from next frame type a",    0, AV_OPT_TYPE_CONST, {.i64 =   0x0010}, 0, 0xFFFF, FLAGS, "sta"},
    { "Ca"     , "No error, unspecified concealment type a",        0, AV_OPT_TYPE_CONST, {.i64 =   0x0040}, 0, 0xFFFF, FLAGS, "sta"},
    { "erri"   , "Error with inserted code, No concealment",        0, AV_OPT_TYPE_CONST, {.i64 =   0x0080}, 0, 0xFFFF, FLAGS, "sta"},
    { "erru"   , "Error with unidentified pos, No concealment",     0, AV_OPT_TYPE_CONST, {.i64 =   0x8000}, 0, 0xFFFF, FLAGS, "sta"},
    { "err"    , "Error, No concealment",                           0, AV_OPT_TYPE_CONST, {.i64 =   0x8080}, 0, 0xFFFF, FLAGS, "sta"},
    { "Ab"     , "No error, concealment from previous frame type b",0, AV_OPT_TYPE_CONST, {.i64 =   0x0400}, 0, 0xFFFF, FLAGS, "sta"},
    { "Bb"     , "No error, concealment from next frame type b",    0, AV_OPT_TYPE_CONST, {.i64 =   0x1000}, 0, 0xFFFF, FLAGS, "sta"},
    { "Cb"     , "No error, unspecified concealment type b",        0, AV_OPT_TYPE_CONST, {.i64 =   0x4000}, 0, 0xFFFF, FLAGS, "sta"},
    { "A"      , "No error, concealment from previous frame",       0, AV_OPT_TYPE_CONST, {.i64 =   0x0404}, 0, 0xFFFF, FLAGS, "sta"},
    { "B"      , "No error, concealment from next frame",           0, AV_OPT_TYPE_CONST, {.i64 =   0x1010}, 0, 0xFFFF, FLAGS, "sta"},
    { "C"      , "No error, unspecified concealment",               0, AV_OPT_TYPE_CONST, {.i64 =   0x4040}, 0, 0xFFFF, FLAGS, "sta"},
    { "a"      , "No error, concealment type a",                    0, AV_OPT_TYPE_CONST, {.i64 =   0x0054}, 0, 0xFFFF, FLAGS, "sta"},
    { "b"      , "No error, concealment type b",                    0, AV_OPT_TYPE_CONST, {.i64 =   0x5400}, 0, 0xFFFF, FLAGS, "sta"},
    { "res"    , "Reserved",                                        0, AV_OPT_TYPE_CONST, {.i64 =   0x2B2A}, 0, 0xFFFF, FLAGS, "sta"},
    { "notok"  , "Error or concealment",                            0, AV_OPT_TYPE_CONST, {.i64 =   0xD4D4}, 0, 0xFFFF, FLAGS, "sta"},
    { "notres" , "Not reserved",                                    0, AV_OPT_TYPE_CONST, {.i64 =   0xD4D5}, 0, 0xFFFF, FLAGS, "sta"},
    { NULL },
};

static const AVClass dv_error_marker_class = {
    .class_name = "dv_error_marker",
    .item_name  = av_default_item_name,
    .option     = options,
    .version    = LIBAVUTIL_VERSION_INT,
};

const FFBitStreamFilter ff_dv_error_marker_bsf = {
    .p.name         = "dv_error_marker",
    .p.codec_ids    = (const enum AVCodecID []){ AV_CODEC_ID_DVVIDEO, AV_CODEC_ID_NONE },
    .p.priv_class   = &dv_error_marker_class,
    .priv_data_size = sizeof(DVErrorMarkerContext),
    .init           = dv_error_marker_init,
    .filter         = dv_error_marker_filter,
};