~medibuntu-maintainers/mplayer/medibuntu.precise

« back to all changes in this revision

Viewing changes to libmpcodecs/vf_lavfi.c

  • Committer: Gauvain Pocentek
  • Date: 2012-03-06 11:59:12 UTC
  • mfrom: (66.1.15 precise)
  • Revision ID: gauvain@pocentek.net-20120306115912-h9d6kt9j0l532oo5
* Merge from Ubuntu:
  - put back faac support
  - recommends apport-hooks-medibuntu
  - change Maintainer, Uploaders & Vcs-* fields.
* New upstream snapshot
* upload to unstable
* Build against external libmpeg2
* drop 51_FTBFS_arm.patch again
* no longer build depend on libcdparanoia-dev on the Hurd
* Fix FTBFS on the hurd.
  Thanks to Samuel Thibault <sthibault@debian.org> (Closes: #654974)
* Fix FTBFS on arm
* New upstream snapshot, Closes: #650339, #643621, #481807
* Imported Upstream version 1.0~rc4+svn34492
* Bump standards version
* Bump dependency on libav >= 4:0.8~, Closes: #653887
* Fix build-indep
* Build mplayer-gui again, Closes: #568514
* Drop debian/all-lang-config-mak.sh, no longer needed
* include .dfsg1 in version number
* remove get-orig-source target
* no longer prune compiler flags from the environment
* No longer advertise nor build 3fdx, mga and dxr3 backends,
  Closes: #496106, #442181, #533546
* beautify mplayer version identification string
* Brown paperbag upload.
* Next try to fix build failure on sparce after recent binutils change.
* Brown paperbag upload.
* Really fix build failure on sparc after recent binutils change.
* Properly set Replaces/Conflicts on mplayer2{,-dbg} to avoid
  file overwrite errors.
* Adjust versioning of mplayer listed in the mplayer-dbg's Depends field.
* Fix build failure on sparc after recent binutils change.
* Urgency medium bumped because of RC-level bugfix
  and speeding up x264 transition.
* Update to my @debian.org email.
* Upload to unstable
* Enable joystick support on Linux only, Closes: #638408
* Rebuild fixes toolchain issue on arm, Closes: #637077
* New upstream snapshot
* following the discussion started by Diego Biurrun <diego@biurrun.de>
  in debian-devel, I have prepared a new packaging of 'mplayer'
  (with code that comes from CVS)
* the upstream tar.bz cannot be distributed by Debian, since it contains
   CSS code; so I am repackaging it 
* I have tried my best to address all known issues:
  - the package contains the detailed Copyright made by Diego Biurrun 
  - the package does not contain CSS code, or  AFAIK other code on which 
     there is active patent enforcement
  - there is a script  debian/cvs-changelog.sh  that shows all changes
     done to files included in this source.
    This should comply with GPLv2 sec 2.a  (in spirit if not in letter)
    For this reason, the source code contains CVS directories.
* needs   make (>= 3.80) for 'html-chunked-$(1)' in DOCS/xml/Makefile

* some corrections, as suggested Diego Biurrun
  - binary codecs should go into /usr/lib/codecs (upstream default)
  - better template 'mplayer/install_codecs'
  - an empty 'font=' in mplayer.conf breaks mplayer: postinst corrected
* correction in 'mplayer/cfgnote'
* better mplayer.postinst and mplayer.config

* New upstream release
* better debian/copyright file
* do not ship a skin
* New upstream release
* changed DEB_BUILD_OPTIONS to DEB_BUILD_CONFIGURE ,
  DEB_BUILD_OPTIONS is used as in debian policy
* use gcc-3.4
* changed xlibs-dev to a long list of dependencies, for Debian/etch
* try to adhere to  http://www.mplayerhq.hu/DOCS/tech/binary-packaging.txt
  (see README.Debian for details)
* removed dependency on xlibmesa-dev, disabled opengl
* New upstream release
* Simon McVittie <hacks@pseudorandom.co.uk> wonderful work:
- Work around Debian bug #267442 (glibc's sys/uio.h and gcc's altivec.h have
  conflicting uses for __vector) by re-ordering #includes
- Fix potential symlink attack in ./configure
- Disable support for binary codecs on platforms for which those codecs
  aren't available; also disable the corresponding Debconf note when it's
  inappropriate
- Changed Build-Depends: so it works in pbuilder
- Explicitly build-depend on libjpeg62-dev, libfontconfig1-dev,
  libungif4-dev 
- Tweak debian/rules to avoid certain errors being ignored
- Use --language=all
* provide a target  'debian/rules get-orig-source' 
  that recreates the orig.tar.gz ; then use the above orig.tar.gz
* rewrote some parts of debian/rules
* don't clean and recompile docs if upstream ships them
* mplayer-doc was shipping too much stuff
* translated man pages where not installed properly
* compile with libdv4-dev
* correct README.Debian
* Forgot build-dep on libtheora
* Must not depend on libxvidcore
* New upstream release
* new release.
* rc1 to become 0.90
* new pre-release
* new pre-release
* gtk bug fixed.
* new release.
* version bumped
* 0.60 pre2 release
* 0.60 pre-release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2011 Nicolas George <nicolas.george@normalesup.org>
 
3
 *
 
4
 * This file is part of MPlayer.
 
5
 *
 
6
 * MPlayer is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * MPlayer 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
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License along
 
17
 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
 
18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
19
 */
 
20
 
 
21
#include "vf.h"
 
22
#include "m_struct.h"
 
23
#include "fmt-conversion.h"
 
24
#include "mp_msg.h"
 
25
#include "libavfilter/avfilter.h"
 
26
#include "libavfilter/avfiltergraph.h"
 
27
#include "libavutil/pixdesc.h"
 
28
 
 
29
struct vf_priv_s {
 
30
    AVFilterGraph *graph;
 
31
    AVFilterContext *in;
 
32
    AVFilterContext *out;
 
33
    int in_w;
 
34
    int in_h;
 
35
    enum PixelFormat in_pixfmt;
 
36
    int in_imgfmt;
 
37
    AVRational in_sar;
 
38
    int out_w;
 
39
    int out_h;
 
40
    enum PixelFormat out_pixfmt;
 
41
    int out_imgfmt;
 
42
    AVRational out_sar;
 
43
    struct AVFilterBufferRef *in_buf;
 
44
    mp_image_t *in_mpi;
 
45
};
 
46
 
 
47
static void buf_mpi_free(AVFilterBuffer *buf)
 
48
{
 
49
    ((mp_image_t *)buf->priv)->usage_count--;
 
50
    av_free(buf);
 
51
}
 
52
 
 
53
static AVFilterBufferRef *mpi_to_bufref(mp_image_t *mpi, enum PixelFormat fmt,
 
54
                                        AVRational sar)
 
55
{
 
56
    AVFilterBufferRef *buf;
 
57
    int perms = AV_PERM_READ;
 
58
 
 
59
    if ((mpi->flags & MP_IMGFLAG_ALLOCATED))
 
60
        perms |= AV_PERM_REUSE2;
 
61
    if (!(mpi->flags & MP_IMGFLAG_PRESERVE))
 
62
        perms |= AV_PERM_WRITE;
 
63
    buf = avfilter_get_video_buffer_ref_from_arrays(mpi->planes, mpi->stride,
 
64
                                                    perms,
 
65
                                                    mpi->w, mpi->h,
 
66
                                                    fmt);
 
67
    buf->video->sample_aspect_ratio = sar;
 
68
    buf->buf->priv = mpi;
 
69
    buf->buf->free = buf_mpi_free;
 
70
    return buf;
 
71
}
 
72
 
 
73
static void bufref_to_mpi(AVFilterBufferRef *ref, mp_image_t *mpi)
 
74
{
 
75
    memcpy(mpi->planes, ref->data, sizeof(ref->data));
 
76
    memcpy(mpi->stride, ref->linesize, sizeof(ref->linesize));
 
77
}
 
78
 
 
79
struct mpsink_priv {
 
80
    struct vf_instance *vf;
 
81
};
 
82
 
 
83
static int mpsink_init(AVFilterContext *ctx,
 
84
                       av_unused const char *args, void *opaque)
 
85
{
 
86
    struct mpsink_priv *c = ctx->priv;
 
87
    c->vf = opaque;
 
88
    return 0;
 
89
}
 
90
 
 
91
static int mpsink_query_formats(AVFilterContext *ctx)
 
92
{
 
93
    struct mpsink_priv *c = ctx->priv;
 
94
    struct vf_instance *vf = c->vf;
 
95
    AVFilterFormats *all;
 
96
    enum PixelFormat *sup;
 
97
    unsigned i, nsup = 0;
 
98
    int ifmt;
 
99
 
 
100
    all = avfilter_all_formats(AVMEDIA_TYPE_VIDEO);
 
101
    sup = av_mallocz(sizeof(*sup) * (all->format_count + 1));
 
102
    if (!sup)
 
103
        return AVERROR(errno);
 
104
    for(i = 0; i < all->format_count; i++) {
 
105
        ifmt = pixfmt2imgfmt(all->formats[i]);
 
106
        if (vf->next->query_format(vf->next, ifmt) > 0)
 
107
            sup[nsup++] = all->formats[i];
 
108
    }
 
109
    sup[nsup++] = PIX_FMT_NONE;
 
110
    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(sup));
 
111
    av_free(sup);
 
112
    return 0;
 
113
}
 
114
 
 
115
static AVFilterBufferRef *mpsink_get_video_buffer(AVFilterLink *link,
 
116
                                                  int perms, int w, int h)
 
117
{
 
118
    struct mpsink_priv *c = link->dst->priv;
 
119
    struct vf_instance *vf = c->vf;
 
120
    mp_image_t *dmpi;
 
121
    int type;
 
122
    int flags = 0;
 
123
 
 
124
    type = MP_IMGTYPE_NUMBERED | (-1 << 16);
 
125
    if ((perms & AV_PERM_PRESERVE))
 
126
        flags |= MP_IMGFLAG_PRESERVE;
 
127
    if ((perms & AV_PERM_READ))
 
128
        flags |= MP_IMGFLAG_READABLE;
 
129
    if ((perms & AV_PERM_NEG_LINESIZES))
 
130
        flags |= MP_IMGFLAG_ACCEPT_STRIDE;
 
131
    if (vf->priv->in_mpi) {
 
132
        type = vf->priv->in_mpi->type;
 
133
        vf->priv->in_mpi = NULL;
 
134
    }
 
135
    dmpi = vf_get_image(vf->next, vf->priv->out_imgfmt, type, flags, w, h);
 
136
    return mpi_to_bufref(dmpi, vf->priv->out_pixfmt, vf->priv->out_sar);
 
137
}
 
138
 
 
139
static void mpsink_end_frame(AVFilterLink *link)
 
140
{
 
141
    struct mpsink_priv *c = link->dst->priv;
 
142
    struct vf_instance *vf = c->vf;
 
143
    AVFilterBufferRef *buf = link->cur_buf;
 
144
    mp_image_t *mpi = buf->buf->priv;
 
145
    double pts;
 
146
 
 
147
    pts = buf->pts == (int64_t)AV_NOPTS_VALUE ? MP_NOPTS_VALUE :
 
148
          buf->pts * av_q2d(link->time_base);
 
149
    mpi->pict_type = buf->video->pict_type;
 
150
    mpi->fields = (buf->video->interlaced      ? MP_IMGFIELD_INTERLACED : 0) |
 
151
                  (buf->video->top_field_first ? MP_IMGFIELD_TOP_FIRST : 0);
 
152
    vf_next_put_image(vf, mpi, pts);
 
153
    avfilter_unref_buffer(link->cur_buf);
 
154
}
 
155
 
 
156
static AVFilter mpsink = {
 
157
    .name        = "mpsink",
 
158
    .description = "Video sink for mplayer interaction",
 
159
    .priv_size   = sizeof(struct mpsink_priv),
 
160
 
 
161
    .init          = mpsink_init,
 
162
    .query_formats = mpsink_query_formats,
 
163
 
 
164
    .inputs  = (AVFilterPad[]) {{ .name             = "default",
 
165
                                  .type             = AVMEDIA_TYPE_VIDEO,
 
166
                                  .end_frame        = mpsink_end_frame,
 
167
                                  .get_video_buffer = mpsink_get_video_buffer,
 
168
                                  .min_perms        = AV_PERM_READ, },
 
169
                                { .name = NULL }},
 
170
    .outputs = (AVFilterPad[]) {{ .name = NULL }},
 
171
};
 
172
 
 
173
struct mpsrc_priv {
 
174
    struct vf_instance *vf;
 
175
};
 
176
 
 
177
static int mpsrc_init(AVFilterContext *ctx,
 
178
                      av_unused const char *args, void *opaque)
 
179
{
 
180
    struct mpsrc_priv *c = ctx->priv;
 
181
    c->vf = opaque;
 
182
    return 0;
 
183
}
 
184
 
 
185
static int mpsrc_query_formats(AVFilterContext *ctx)
 
186
{
 
187
    struct mpsrc_priv *c = ctx->priv;
 
188
    enum PixelFormat pix_fmts[] = { c->vf->priv->in_pixfmt, PIX_FMT_NONE };
 
189
    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
 
190
    return 0;
 
191
}
 
192
 
 
193
static int mpsrc_config_props(AVFilterLink *link)
 
194
{
 
195
    struct mpsrc_priv *c = link->src->priv;
 
196
    struct vf_instance *vf = c->vf;
 
197
    link->w = vf->priv->in_w;
 
198
    link->h = vf->priv->in_h;
 
199
    link->sample_aspect_ratio = vf->priv->in_sar;
 
200
    link->time_base = AV_TIME_BASE_Q;
 
201
    return 0;
 
202
}
 
203
 
 
204
static int mpsrc_request_frame(AVFilterLink *link)
 
205
{
 
206
    struct mpsrc_priv *c = link->src->priv;
 
207
    struct vf_instance *vf = c->vf;
 
208
 
 
209
    if (!vf->priv->in_buf)
 
210
        return AVERROR(EINVAL);
 
211
    avfilter_start_frame(link, avfilter_ref_buffer(vf->priv->in_buf, ~0));
 
212
    avfilter_draw_slice(link, 0, link->h, 1);
 
213
    avfilter_end_frame(link);
 
214
    vf->priv->in_buf = NULL;
 
215
    return 0;
 
216
}
 
217
 
 
218
static int mpsrc_poll_frame(AVFilterLink *link)
 
219
{
 
220
    struct mpsrc_priv *c = link->src->priv;
 
221
    struct vf_instance *vf = c->vf;
 
222
    return vf->priv->in_buf != NULL;
 
223
}
 
224
 
 
225
AVFilter mpsrc = {
 
226
    .name          = "mpsrc",
 
227
    .description   = "Video source for mplayer interaction",
 
228
    .priv_size     = sizeof(struct mpsrc_priv),
 
229
    .query_formats = mpsrc_query_formats,
 
230
 
 
231
    .init      = mpsrc_init,
 
232
 
 
233
    .inputs  = (AVFilterPad[]) {{ .name = NULL }},
 
234
    .outputs = (AVFilterPad[]) {{ .name           = "default",
 
235
                                  .type           = AVMEDIA_TYPE_VIDEO,
 
236
                                  .request_frame  = mpsrc_request_frame,
 
237
                                  .poll_frame     = mpsrc_poll_frame,
 
238
                                  .config_props   = mpsrc_config_props, },
 
239
                                { .name = NULL }},
 
240
};
 
241
 
 
242
static int config(struct vf_instance *vf, int w, int h, int dw, int dh,
 
243
                        unsigned flags, unsigned fmt)
 
244
{
 
245
    int ret;
 
246
    AVFilterLink *out;
 
247
    AVRational iar, dar;
 
248
 
 
249
    av_reduce(&iar.num, &iar.den, w, h, INT_MAX);
 
250
    av_reduce(&dar.num, &dar.den, dw, dh, INT_MAX);
 
251
    vf->priv->in_pixfmt = imgfmt2pixfmt(fmt);
 
252
    vf->priv->in_imgfmt = fmt;
 
253
    vf->priv->in_w = w;
 
254
    vf->priv->in_h = h;
 
255
    vf->priv->in_sar = av_div_q(dar, iar);
 
256
    ret = avfilter_graph_config(vf->priv->graph, NULL);
 
257
    if (ret < 0)
 
258
        return 0;
 
259
    out = vf->priv->out->inputs[0];
 
260
    vf->priv->out_w = out->w;
 
261
    vf->priv->out_h = out->h;
 
262
    vf->priv->out_pixfmt = out->format;
 
263
    vf->priv->out_imgfmt = pixfmt2imgfmt(out->format);
 
264
    vf->priv->out_sar = out->sample_aspect_ratio;
 
265
    if (vf->priv->out_sar.num != vf->priv->in_sar.num ||
 
266
        vf->priv->out_sar.den != vf->priv->in_sar.den ||
 
267
        out->w != w || out->h != h) {
 
268
        av_reduce(&iar.num, &iar.den, out->w, out->h, INT_MAX);
 
269
        dar = av_mul_q(iar, out->sample_aspect_ratio);
 
270
        if (av_cmp_q(dar, iar) >= 0) {
 
271
            dh = out->h;
 
272
            dw = av_rescale(dh, dar.num, dar.den);
 
273
        } else {
 
274
            dw = out->w;
 
275
            dh = av_rescale(dw, dar.den, dar.num);
 
276
        }
 
277
    }
 
278
    return vf_next_config(vf, out->w, out->h, dw, dh, flags, fmt);
 
279
}
 
280
 
 
281
static void get_image(struct vf_instance *vf, mp_image_t *mpi)
 
282
{
 
283
    AVFilterBufferRef *buf;
 
284
    unsigned perms = AV_PERM_WRITE | AV_PERM_REUSE2;
 
285
 
 
286
    avfilter_unref_buffer(mpi->priv);
 
287
    mpi->priv = NULL; /* for safety */
 
288
    if (mpi->flags & MP_IMGFLAG_READABLE)
 
289
        perms |= AV_PERM_READ;
 
290
    if (mpi->flags & MP_IMGFLAG_PRESERVE)
 
291
        perms |= AV_PERM_PRESERVE;
 
292
    vf->priv->in_mpi = mpi;
 
293
    buf = avfilter_get_video_buffer(vf->priv->in->outputs[0], perms,
 
294
                                    mpi->w, mpi->h);
 
295
    vf->priv->in_mpi = NULL;
 
296
    bufref_to_mpi(buf, mpi);
 
297
    mpi->flags |= MP_IMGFLAG_DIRECT;
 
298
    mpi->flags &= ~MP_IMGFLAG_ALLOCATED;
 
299
    mpi->priv = buf;
 
300
}
 
301
 
 
302
static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
 
303
{
 
304
    AVFilterBufferRef *buf;
 
305
    mp_image_t *cmpi = NULL;
 
306
 
 
307
    if (!(mpi->flags & MP_IMGFLAG_DIRECT)) {
 
308
        cmpi = vf_get_image(vf, mpi->imgfmt, MP_IMGTYPE_TEMP,
 
309
                            MP_IMGFLAG_PREFER_ALIGNED_STRIDE,
 
310
                            mpi->w, mpi->h);
 
311
        copy_mpi(cmpi, mpi);
 
312
        buf = cmpi->priv;
 
313
    } else {
 
314
        buf = mpi->priv;
 
315
    }
 
316
    buf->video->key_frame = mpi->pict_type == 1;
 
317
    buf->video->pict_type = mpi->pict_type; /* seems to be the same code */
 
318
    buf->video->interlaced = !!(mpi->fields & MP_IMGFIELD_INTERLACED);
 
319
    buf->video->top_field_first = !!(mpi->fields & MP_IMGFIELD_TOP_FIRST);
 
320
    vf->priv->in_buf = buf;
 
321
    if (pts != MP_NOPTS_VALUE)
 
322
        buf->pts = pts * AV_TIME_BASE;
 
323
    while (avfilter_poll_frame(vf->priv->out->inputs[0])) {
 
324
        if (avfilter_request_frame(vf->priv->out->inputs[0]))
 
325
            break;
 
326
    }
 
327
    return 1;
 
328
}
 
329
 
 
330
static void uninit(struct vf_instance *vf)
 
331
{
 
332
    unsigned i;
 
333
 
 
334
#define FREE_MPI_ARRAY(field) \
 
335
    for (i = 0; i < FF_ARRAY_ELEMS(vf->imgctx.field); i++) \
 
336
        if (vf->imgctx.field[i]) \
 
337
            avfilter_unref_buffer(vf->imgctx.field[i]->priv);
 
338
    FREE_MPI_ARRAY(static_images);
 
339
    FREE_MPI_ARRAY(temp_images);
 
340
    FREE_MPI_ARRAY(export_images);
 
341
    FREE_MPI_ARRAY(numbered_images);
 
342
    avfilter_graph_free(&vf->priv->graph);
 
343
    av_free(vf->priv);
 
344
}
 
345
 
 
346
static int lavfi_open(struct vf_instance *vf, char *args)
 
347
{
 
348
    AVFilterInOut *outputs;
 
349
    AVFilterInOut *inputs;
 
350
    int ret;
 
351
 
 
352
    avfilter_register_all();
 
353
    if (!args) {
 
354
        mp_msg(MSGT_VFILTER, MSGL_ERR, "lavfi: filtergraph needed\n");
 
355
        goto fail;
 
356
    }
 
357
    if (args[0] == '$') {
 
358
        char *e = getenv(args + 1);
 
359
        if (!e) {
 
360
            mp_msg(MSGT_VFILTER, MSGL_ERR, "lavfi: %s not defined\n", args);
 
361
            goto fail;
 
362
        }
 
363
        args = e;
 
364
    }
 
365
    vf->priv = av_mallocz(sizeof(struct vf_priv_s));
 
366
    if (!vf->priv)
 
367
        return 0;
 
368
 
 
369
    vf->priv->graph = avfilter_graph_alloc();
 
370
    if (!vf->priv->graph)
 
371
        goto fail;
 
372
    ret = avfilter_graph_create_filter(&vf->priv->in, &mpsrc, "in",
 
373
                                       NULL, vf, vf->priv->graph);
 
374
    if (ret < 0)
 
375
        goto fail;
 
376
    ret = avfilter_graph_create_filter(&vf->priv->out, &mpsink, "out",
 
377
                                       NULL, vf, vf->priv->graph);
 
378
    if (ret < 0)
 
379
        return 0;
 
380
    outputs = avfilter_inout_alloc();
 
381
    inputs  = avfilter_inout_alloc();
 
382
    if (!outputs || !inputs)
 
383
        goto fail;
 
384
    outputs->name = av_strdup("in");
 
385
    outputs->filter_ctx = vf->priv->in;
 
386
    outputs->pad_idx = 0;
 
387
    outputs->next = NULL;
 
388
    inputs->name = av_strdup("out");
 
389
    inputs->filter_ctx = vf->priv->out;
 
390
    inputs->pad_idx = 0;
 
391
    inputs->next = NULL;
 
392
    ret = avfilter_graph_parse(vf->priv->graph, args, &inputs, &outputs, NULL);
 
393
    if (ret < 0)
 
394
        goto fail;
 
395
 
 
396
    vf->config    = config;
 
397
    vf->uninit    = uninit;
 
398
    vf->put_image = put_image;
 
399
    vf->get_image = get_image;
 
400
    return 1;
 
401
 
 
402
fail:
 
403
    avfilter_inout_free(&inputs);
 
404
    avfilter_inout_free(&outputs);
 
405
    avfilter_graph_free(&vf->priv->graph);
 
406
    av_free(vf->priv);
 
407
    return 0;
 
408
}
 
409
 
 
410
static const m_option_t vf_opts_fields[] = { { .name = NULL } };
 
411
 
 
412
const vf_info_t vf_info_lavfi = {
 
413
    "libavfilter wrapper",
 
414
    "lavfi",
 
415
    "Nicolas George",
 
416
    "",
 
417
    lavfi_open,
 
418
    NULL,
 
419
};