xref: /third_party/ffmpeg/libavcodec/noise_bsf.c (revision cabdff1a)
1/*
2 * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
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#include <stdlib.h>
22
23#include "bsf.h"
24#include "bsf_internal.h"
25
26#include "libavutil/log.h"
27#include "libavutil/opt.h"
28#include "libavutil/eval.h"
29
30static const char *const var_names[] = {
31    "n",                           /// packet index, starting from zero
32    "tb",                          /// timebase
33    "pts",                         /// packet presentation timestamp
34    "dts",                         /// packet decoding timestamp
35    "nopts",                       /// AV_NOPTS_VALUE
36    "startpts",                    /// first seen non-AV_NOPTS_VALUE packet timestamp
37    "startdts",                    /// first seen non-AV_NOPTS_VALUE packet timestamp
38    "duration", "d",               /// packet duration
39    "pos",                         /// original position of packet in its source
40    "size",                        /// packet size
41    "key" ,                        /// packet keyframe flag
42    "state",                       /// random-ish state
43    NULL
44};
45
46enum var_name {
47    VAR_N,
48    VAR_TB,
49    VAR_PTS,
50    VAR_DTS,
51    VAR_NOPTS,
52    VAR_STARTPTS,
53    VAR_STARTDTS,
54    VAR_DURATION, VAR_D,
55    VAR_POS,
56    VAR_SIZE,
57    VAR_KEY,
58    VAR_STATE,
59    VAR_VARS_NB
60};
61
62typedef struct NoiseContext {
63    const AVClass *class;
64
65    char *amount_str;
66    char *drop_str;
67    int dropamount;
68
69    AVExpr *amount_pexpr;
70    AVExpr *drop_pexpr;
71
72    double var_values[VAR_VARS_NB];
73
74    unsigned int state;
75    unsigned int pkt_idx;
76} NoiseContext;
77
78static int noise_init(AVBSFContext *ctx)
79{
80    NoiseContext *s = ctx->priv_data;
81    int ret;
82
83    if (!s->amount_str) {
84        s->amount_str = (!s->drop_str && !s->dropamount) ? av_strdup("-1") : av_strdup("0");
85        if (!s->amount_str)
86            return AVERROR(ENOMEM);
87    }
88
89    if (ctx->par_in->codec_id == AV_CODEC_ID_WRAPPED_AVFRAME &&
90        strcmp(s->amount_str, "0")) {
91        av_log(ctx, AV_LOG_ERROR, "Wrapped AVFrame noising is unsupported\n");
92        return AVERROR_PATCHWELCOME;
93    }
94
95    ret = av_expr_parse(&s->amount_pexpr, s->amount_str,
96                        var_names, NULL, NULL, NULL, NULL, 0, ctx);
97    if (ret < 0) {
98        av_log(ctx, AV_LOG_ERROR, "Error in parsing expr for amount: %s\n", s->amount_str);
99        return ret;
100    }
101
102    if (s->drop_str && s->dropamount) {
103        av_log(ctx, AV_LOG_WARNING, "Both drop '%s' and dropamount=%d set. Ignoring dropamount.\n",
104               s->drop_str, s->dropamount);
105        s->dropamount = 0;
106    }
107
108    if (s->drop_str) {
109        ret = av_expr_parse(&s->drop_pexpr, s->drop_str,
110                            var_names, NULL, NULL, NULL, NULL, 0, ctx);
111        if (ret < 0) {
112            av_log(ctx, AV_LOG_ERROR, "Error in parsing expr for drop: %s\n", s->drop_str);
113            return ret;
114        }
115    }
116
117    s->var_values[VAR_TB]       = ctx->time_base_out.den ? av_q2d(ctx->time_base_out) : 0;
118    s->var_values[VAR_NOPTS]    = AV_NOPTS_VALUE;
119    s->var_values[VAR_STARTPTS] = AV_NOPTS_VALUE;
120    s->var_values[VAR_STARTDTS] = AV_NOPTS_VALUE;
121    s->var_values[VAR_STATE] = 0;
122
123    return 0;
124}
125
126static int noise(AVBSFContext *ctx, AVPacket *pkt)
127{
128    NoiseContext *s = ctx->priv_data;
129    int i, ret, amount, drop = 0;
130    double res;
131
132    ret = ff_bsf_get_packet_ref(ctx, pkt);
133    if (ret < 0)
134        return ret;
135
136    s->var_values[VAR_N]           = s->pkt_idx++;
137    s->var_values[VAR_PTS]         = pkt->pts;
138    s->var_values[VAR_DTS]         = pkt->dts;
139    s->var_values[VAR_DURATION]    =
140    s->var_values[VAR_D]           = pkt->duration;
141    s->var_values[VAR_SIZE]        = pkt->size;
142    s->var_values[VAR_KEY]         = !!(pkt->flags & AV_PKT_FLAG_KEY);
143    s->var_values[VAR_POS]         = pkt->pos;
144
145    if (s->var_values[VAR_STARTPTS] == AV_NOPTS_VALUE)
146        s->var_values[VAR_STARTPTS] = pkt->pts;
147
148    if (s->var_values[VAR_STARTDTS] == AV_NOPTS_VALUE)
149        s->var_values[VAR_STARTDTS] = pkt->dts;
150
151    res = av_expr_eval(s->amount_pexpr, s->var_values, NULL);
152
153    if (isnan(res))
154        amount = 0;
155    else if (res < 0)
156        amount = (s->state % 10001 + 1);
157    else
158        amount = (int)res;
159
160    if (s->drop_str) {
161        res = av_expr_eval(s->drop_pexpr, s->var_values, NULL);
162
163        if (isnan(res))
164            drop = 0;
165        else if (res < 0)
166            drop = !(s->state % FFABS((int)res));
167        else
168            drop = !!res;
169    }
170
171    if(s->dropamount) {
172        drop = !(s->state % s->dropamount);
173    }
174
175    av_log(ctx, AV_LOG_VERBOSE, "Stream #%d packet %d pts %"PRId64" - amount %d drop %d\n",
176           pkt->stream_index, (unsigned int)s->var_values[VAR_N], pkt->pts, amount, drop);
177
178    if (drop) {
179        s->var_values[VAR_STATE] = ++s->state;
180        av_packet_unref(pkt);
181        return AVERROR(EAGAIN);
182    }
183
184    if (amount) {
185        ret = av_packet_make_writable(pkt);
186        if (ret < 0) {
187            av_packet_unref(pkt);
188            return ret;
189        }
190    }
191
192    for (i = 0; i < pkt->size; i++) {
193        s->state += pkt->data[i] + 1;
194        if (amount && s->state % amount == 0)
195            pkt->data[i] = s->state;
196    }
197
198    s->var_values[VAR_STATE] = s->state;
199
200    return 0;
201}
202
203static void noise_close(AVBSFContext *bsf)
204{
205    NoiseContext *s = bsf->priv_data;
206
207    av_expr_free(s->amount_pexpr);
208    av_expr_free(s->drop_pexpr);
209    s->amount_pexpr = s->drop_pexpr = NULL;
210}
211
212#define OFFSET(x) offsetof(NoiseContext, x)
213#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_BSF_PARAM)
214static const AVOption options[] = {
215    { "amount",     NULL, OFFSET(amount_str),     AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
216    { "drop",       NULL, OFFSET(drop_str),       AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
217    { "dropamount", NULL, OFFSET(dropamount),     AV_OPT_TYPE_INT,    { .i64 = 0    }, 0, INT_MAX, FLAGS },
218    { NULL },
219};
220
221static const AVClass noise_class = {
222    .class_name = "noise",
223    .item_name  = av_default_item_name,
224    .option     = options,
225    .version    = LIBAVUTIL_VERSION_INT,
226};
227
228const FFBitStreamFilter ff_noise_bsf = {
229    .p.name         = "noise",
230    .p.priv_class   = &noise_class,
231    .priv_data_size = sizeof(NoiseContext),
232    .init           = noise_init,
233    .close          = noise_close,
234    .filter         = noise,
235};
236