Coverage Report

Created: 2026-08-14 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/contrib/contrib-build/bpg/libavutil/frame.c
Line
Count
Source
1
/*
2
 *
3
 * This file is part of FFmpeg.
4
 *
5
 * FFmpeg is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9
 *
10
 * FFmpeg is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16
 * License along with FFmpeg; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
 */
19
20
#include "channel_layout.h"
21
#include "avassert.h"
22
#include "buffer.h"
23
#include "common.h"
24
#include "dict.h"
25
#include "frame.h"
26
#include "imgutils.h"
27
#include "mem.h"
28
#include "samplefmt.h"
29
30
MAKE_ACCESSORS(AVFrame, frame, int64_t, best_effort_timestamp)
31
MAKE_ACCESSORS(AVFrame, frame, int64_t, pkt_duration)
32
MAKE_ACCESSORS(AVFrame, frame, int64_t, pkt_pos)
33
MAKE_ACCESSORS(AVFrame, frame, int64_t, channel_layout)
34
MAKE_ACCESSORS(AVFrame, frame, int,     channels)
35
MAKE_ACCESSORS(AVFrame, frame, int,     sample_rate)
36
MAKE_ACCESSORS(AVFrame, frame, int,     decode_error_flags)
37
MAKE_ACCESSORS(AVFrame, frame, int,     pkt_size)
38
MAKE_ACCESSORS(AVFrame, frame, enum AVColorSpace, colorspace)
39
MAKE_ACCESSORS(AVFrame, frame, enum AVColorRange, color_range)
40
41
#define CHECK_CHANNELS_CONSISTENCY(frame) \
42
    av_assert2(!(frame)->channel_layout || \
43
               (frame)->channels == \
44
               av_get_channel_layout_nb_channels((frame)->channel_layout))
45
46
static void get_frame_defaults(AVFrame *frame)
47
0
{
48
0
    memset(frame, 0, sizeof(*frame));
49
50
0
    frame->pts                   =
51
0
    frame->pkt_dts               =
52
0
    frame->pkt_pts               = AV_NOPTS_VALUE;
53
0
    av_frame_set_best_effort_timestamp(frame, AV_NOPTS_VALUE);
54
0
    av_frame_set_pkt_duration         (frame, 0);
55
0
    av_frame_set_pkt_pos              (frame, -1);
56
0
    av_frame_set_pkt_size             (frame, -1);
57
0
    frame->key_frame           = 1;
58
0
    frame->sample_aspect_ratio = (AVRational){ 0, 1 };
59
0
    frame->format              = -1; /* unknown */
60
0
    frame->color_primaries     = AVCOL_PRI_UNSPECIFIED;
61
0
    frame->color_trc           = AVCOL_TRC_UNSPECIFIED;
62
0
    frame->colorspace          = AVCOL_SPC_UNSPECIFIED;
63
0
    frame->color_range         = AVCOL_RANGE_UNSPECIFIED;
64
0
    frame->chroma_location     = AVCHROMA_LOC_UNSPECIFIED;
65
0
}
66
67
static void free_side_data(AVFrameSideData **ptr_sd)
68
0
{
69
0
    AVFrameSideData *sd = *ptr_sd;
70
0
71
0
    av_freep(&sd->data);
72
0
    av_freep(ptr_sd);
73
0
}
74
75
AVFrame *av_frame_alloc(void)
76
0
{
77
0
    AVFrame *frame = av_mallocz(sizeof(*frame));
78
79
0
    if (!frame)
80
0
        return NULL;
81
82
0
    get_frame_defaults(frame);
83
84
0
    return frame;
85
0
}
86
87
void av_frame_free(AVFrame **frame)
88
8
{
89
8
    if (!frame || !*frame)
90
8
        return;
91
92
0
    av_frame_unref(*frame);
93
0
    av_freep(frame);
94
0
}
95
96
void av_frame_unref(AVFrame *frame)
97
0
{
98
0
    int i;
99
100
0
    for (i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
101
0
        av_buffer_unref(&frame->buf[i]);
102
103
0
    get_frame_defaults(frame);
104
0
}
105
106
void av_frame_move_ref(AVFrame *dst, AVFrame *src)
107
0
{
108
0
    *dst = *src;
109
0
    memset(src, 0, sizeof(*src));
110
0
    get_frame_defaults(src);
111
0
}
112
113
int av_frame_ref(AVFrame *dst, const AVFrame *src)
114
0
{
115
0
    int i, ret = 0;
116
117
0
    dst->format         = src->format;
118
0
    dst->width          = src->width;
119
0
    dst->height         = src->height;
120
0
    dst->channels       = src->channels;
121
0
    dst->channel_layout = src->channel_layout;
122
0
    dst->nb_samples     = src->nb_samples;
123
124
    /* duplicate the frame data if it's not refcounted */
125
0
    if (!src->buf[0]) {
126
0
        abort();
127
#if 0
128
        ret = av_frame_get_buffer(dst, 32);
129
        if (ret < 0)
130
            return ret;
131
132
        ret = av_frame_copy(dst, src);
133
        if (ret < 0)
134
            av_frame_unref(dst);
135
136
        return ret;
137
#endif
138
0
    }
139
140
    /* ref the buffers */
141
0
    for (i = 0; i < FF_ARRAY_ELEMS(src->buf); i++) {
142
0
        if (!src->buf[i])
143
0
            continue;
144
0
        dst->buf[i] = av_buffer_ref(src->buf[i]);
145
0
        if (!dst->buf[i]) {
146
0
            ret = AVERROR(ENOMEM);
147
0
            goto fail;
148
0
        }
149
0
    }
150
151
0
    memcpy(dst->data,     src->data,     sizeof(src->data));
152
0
    memcpy(dst->linesize, src->linesize, sizeof(src->linesize));
153
154
0
    return 0;
155
156
0
fail:
157
0
    av_frame_unref(dst);
158
0
    return ret;
159
0
}
160
161
#if 0
162
static int get_video_buffer(AVFrame *frame, int align)
163
{
164
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
165
    int ret, i;
166
167
    if (!desc)
168
        return AVERROR(EINVAL);
169
170
    if ((ret = av_image_check_size(frame->width, frame->height, 0, NULL)) < 0)
171
        return ret;
172
173
    if (!frame->linesize[0]) {
174
        for(i=1; i<=align; i+=i) {
175
            ret = av_image_fill_linesizes(frame->linesize, frame->format,
176
                                          FFALIGN(frame->width, i));
177
            if (ret < 0)
178
                return ret;
179
            if (!(frame->linesize[0] & (align-1)))
180
                break;
181
        }
182
183
        for (i = 0; i < 4 && frame->linesize[i]; i++)
184
            frame->linesize[i] = FFALIGN(frame->linesize[i], align);
185
    }
186
187
    for (i = 0; i < 4 && frame->linesize[i]; i++) {
188
        int h = FFALIGN(frame->height, 32);
189
        if (i == 1 || i == 2)
190
            h = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
191
192
        frame->buf[i] = av_buffer_alloc(frame->linesize[i] * h + 16 + 16/*STRIDE_ALIGN*/ - 1);
193
        if (!frame->buf[i])
194
            goto fail;
195
196
        frame->data[i] = frame->buf[i]->data;
197
    }
198
    if (desc->flags & AV_PIX_FMT_FLAG_PAL || desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL) {
199
        av_buffer_unref(&frame->buf[1]);
200
        frame->buf[1] = av_buffer_alloc(1024);
201
        if (!frame->buf[1])
202
            goto fail;
203
        frame->data[1] = frame->buf[1]->data;
204
    }
205
206
    frame->extended_data = frame->data;
207
208
    return 0;
209
fail:
210
    av_frame_unref(frame);
211
    return AVERROR(ENOMEM);
212
}
213
214
static int get_audio_buffer(AVFrame *frame, int align)
215
{
216
    int channels;
217
    int planar   = av_sample_fmt_is_planar(frame->format);
218
    int planes;
219
    int ret, i;
220
221
    if (!frame->channels)
222
        frame->channels = av_get_channel_layout_nb_channels(frame->channel_layout);
223
224
    channels = frame->channels;
225
    planes = planar ? channels : 1;
226
227
    CHECK_CHANNELS_CONSISTENCY(frame);
228
    if (!frame->linesize[0]) {
229
        ret = av_samples_get_buffer_size(&frame->linesize[0], channels,
230
                                         frame->nb_samples, frame->format,
231
                                         align);
232
        if (ret < 0)
233
            return ret;
234
    }
235
236
    if (planes > AV_NUM_DATA_POINTERS) {
237
        frame->extended_data = av_mallocz_array(planes,
238
                                          sizeof(*frame->extended_data));
239
        frame->extended_buf  = av_mallocz_array((planes - AV_NUM_DATA_POINTERS),
240
                                          sizeof(*frame->extended_buf));
241
        if (!frame->extended_data || !frame->extended_buf) {
242
            av_freep(&frame->extended_data);
243
            av_freep(&frame->extended_buf);
244
            return AVERROR(ENOMEM);
245
        }
246
        frame->nb_extended_buf = planes - AV_NUM_DATA_POINTERS;
247
    } else
248
        frame->extended_data = frame->data;
249
250
    for (i = 0; i < FFMIN(planes, AV_NUM_DATA_POINTERS); i++) {
251
        frame->buf[i] = av_buffer_alloc(frame->linesize[0]);
252
        if (!frame->buf[i]) {
253
            av_frame_unref(frame);
254
            return AVERROR(ENOMEM);
255
        }
256
        frame->extended_data[i] = frame->data[i] = frame->buf[i]->data;
257
    }
258
    for (i = 0; i < planes - AV_NUM_DATA_POINTERS; i++) {
259
        frame->extended_buf[i] = av_buffer_alloc(frame->linesize[0]);
260
        if (!frame->extended_buf[i]) {
261
            av_frame_unref(frame);
262
            return AVERROR(ENOMEM);
263
        }
264
        frame->extended_data[i + AV_NUM_DATA_POINTERS] = frame->extended_buf[i]->data;
265
    }
266
    return 0;
267
268
}
269
270
int av_frame_get_buffer(AVFrame *frame, int align)
271
{
272
    if (frame->format < 0)
273
        return AVERROR(EINVAL);
274
275
    if (frame->width > 0 && frame->height > 0)
276
        return get_video_buffer(frame, align);
277
    else if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0))
278
        return get_audio_buffer(frame, align);
279
280
    return AVERROR(EINVAL);
281
}
282
283
284
int av_frame_ref(AVFrame *dst, const AVFrame *src)
285
{
286
    int i, ret = 0;
287
288
    dst->format         = src->format;
289
    dst->width          = src->width;
290
    dst->height         = src->height;
291
    dst->channels       = src->channels;
292
    dst->channel_layout = src->channel_layout;
293
    dst->nb_samples     = src->nb_samples;
294
295
    ret = av_frame_copy_props(dst, src);
296
    if (ret < 0)
297
        return ret;
298
299
    /* duplicate the frame data if it's not refcounted */
300
    if (!src->buf[0]) {
301
        ret = av_frame_get_buffer(dst, 32);
302
        if (ret < 0)
303
            return ret;
304
305
        ret = av_frame_copy(dst, src);
306
        if (ret < 0)
307
            av_frame_unref(dst);
308
309
        return ret;
310
    }
311
312
    /* ref the buffers */
313
    for (i = 0; i < FF_ARRAY_ELEMS(src->buf); i++) {
314
        if (!src->buf[i])
315
            continue;
316
        dst->buf[i] = av_buffer_ref(src->buf[i]);
317
        if (!dst->buf[i]) {
318
            ret = AVERROR(ENOMEM);
319
            goto fail;
320
        }
321
    }
322
323
    if (src->extended_buf) {
324
        dst->extended_buf = av_mallocz_array(sizeof(*dst->extended_buf),
325
                                       src->nb_extended_buf);
326
        if (!dst->extended_buf) {
327
            ret = AVERROR(ENOMEM);
328
            goto fail;
329
        }
330
        dst->nb_extended_buf = src->nb_extended_buf;
331
332
        for (i = 0; i < src->nb_extended_buf; i++) {
333
            dst->extended_buf[i] = av_buffer_ref(src->extended_buf[i]);
334
            if (!dst->extended_buf[i]) {
335
                ret = AVERROR(ENOMEM);
336
                goto fail;
337
            }
338
        }
339
    }
340
341
    /* duplicate extended data */
342
    if (src->extended_data != src->data) {
343
        int ch = src->channels;
344
345
        if (!ch) {
346
            ret = AVERROR(EINVAL);
347
            goto fail;
348
        }
349
        CHECK_CHANNELS_CONSISTENCY(src);
350
351
        dst->extended_data = av_malloc_array(sizeof(*dst->extended_data), ch);
352
        if (!dst->extended_data) {
353
            ret = AVERROR(ENOMEM);
354
            goto fail;
355
        }
356
        memcpy(dst->extended_data, src->extended_data, sizeof(*src->extended_data) * ch);
357
    } else
358
        dst->extended_data = dst->data;
359
360
    memcpy(dst->data,     src->data,     sizeof(src->data));
361
    memcpy(dst->linesize, src->linesize, sizeof(src->linesize));
362
363
    return 0;
364
365
fail:
366
    av_frame_unref(dst);
367
    return ret;
368
}
369
370
AVFrame *av_frame_clone(const AVFrame *src)
371
{
372
    AVFrame *ret = av_frame_alloc();
373
374
    if (!ret)
375
        return NULL;
376
377
    if (av_frame_ref(ret, src) < 0)
378
        av_frame_free(&ret);
379
380
    return ret;
381
}
382
383
int av_frame_is_writable(AVFrame *frame)
384
{
385
    int i, ret = 1;
386
387
    /* assume non-refcounted frames are not writable */
388
    if (!frame->buf[0])
389
        return 0;
390
391
    for (i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)
392
        if (frame->buf[i])
393
            ret &= !!av_buffer_is_writable(frame->buf[i]);
394
    for (i = 0; i < frame->nb_extended_buf; i++)
395
        ret &= !!av_buffer_is_writable(frame->extended_buf[i]);
396
397
    return ret;
398
}
399
400
int av_frame_make_writable(AVFrame *frame)
401
{
402
    AVFrame tmp;
403
    int ret;
404
405
    if (!frame->buf[0])
406
        return AVERROR(EINVAL);
407
408
    if (av_frame_is_writable(frame))
409
        return 0;
410
411
    memset(&tmp, 0, sizeof(tmp));
412
    tmp.format         = frame->format;
413
    tmp.width          = frame->width;
414
    tmp.height         = frame->height;
415
    tmp.channels       = frame->channels;
416
    tmp.channel_layout = frame->channel_layout;
417
    tmp.nb_samples     = frame->nb_samples;
418
    ret = av_frame_get_buffer(&tmp, 32);
419
    if (ret < 0)
420
        return ret;
421
422
    ret = av_frame_copy(&tmp, frame);
423
    if (ret < 0) {
424
        av_frame_unref(&tmp);
425
        return ret;
426
    }
427
428
    ret = av_frame_copy_props(&tmp, frame);
429
    if (ret < 0) {
430
        av_frame_unref(&tmp);
431
        return ret;
432
    }
433
434
    av_frame_unref(frame);
435
436
    *frame = tmp;
437
    if (tmp.data == tmp.extended_data)
438
        frame->extended_data = frame->data;
439
440
    return 0;
441
}
442
443
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
444
{
445
    int i;
446
447
    dst->key_frame              = src->key_frame;
448
    dst->pict_type              = src->pict_type;
449
    dst->sample_aspect_ratio    = src->sample_aspect_ratio;
450
    dst->pts                    = src->pts;
451
    dst->repeat_pict            = src->repeat_pict;
452
    dst->interlaced_frame       = src->interlaced_frame;
453
    dst->top_field_first        = src->top_field_first;
454
    dst->palette_has_changed    = src->palette_has_changed;
455
    dst->sample_rate            = src->sample_rate;
456
    dst->opaque                 = src->opaque;
457
    dst->pkt_pts                = src->pkt_pts;
458
    dst->pkt_dts                = src->pkt_dts;
459
    dst->pkt_pos                = src->pkt_pos;
460
    dst->pkt_size               = src->pkt_size;
461
    dst->pkt_duration           = src->pkt_duration;
462
    dst->reordered_opaque       = src->reordered_opaque;
463
    dst->quality                = src->quality;
464
    dst->best_effort_timestamp  = src->best_effort_timestamp;
465
    dst->coded_picture_number   = src->coded_picture_number;
466
    dst->display_picture_number = src->display_picture_number;
467
    dst->flags                  = src->flags;
468
    dst->decode_error_flags     = src->decode_error_flags;
469
    dst->color_primaries        = src->color_primaries;
470
    dst->color_trc              = src->color_trc;
471
    dst->colorspace             = src->colorspace;
472
    dst->color_range            = src->color_range;
473
    dst->chroma_location        = src->chroma_location;
474
475
    memcpy(dst->error, src->error, sizeof(dst->error));
476
477
    for (i = 0; i < src->nb_side_data; i++) {
478
        const AVFrameSideData *sd_src = src->side_data[i];
479
        AVFrameSideData *sd_dst;
480
        if (   sd_src->type == AV_FRAME_DATA_PANSCAN
481
            && (src->width != dst->width || src->height != dst->height))
482
            continue;
483
        sd_dst = av_frame_new_side_data(dst, sd_src->type,
484
                                                         sd_src->size);
485
        if (!sd_dst) {
486
            for (i = 0; i < dst->nb_side_data; i++) {
487
                free_side_data(&dst->side_data[i]);
488
            }
489
            av_freep(&dst->side_data);
490
            return AVERROR(ENOMEM);
491
        }
492
        memcpy(sd_dst->data, sd_src->data, sd_src->size);
493
    }
494
495
    return 0;
496
}
497
498
AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int plane)
499
{
500
    uint8_t *data;
501
    int planes, i;
502
503
    if (frame->nb_samples) {
504
        int channels = frame->channels;
505
        if (!channels)
506
            return NULL;
507
        CHECK_CHANNELS_CONSISTENCY(frame);
508
        planes = av_sample_fmt_is_planar(frame->format) ? channels : 1;
509
    } else
510
        planes = 4;
511
512
    if (plane < 0 || plane >= planes || !frame->extended_data[plane])
513
        return NULL;
514
    data = frame->extended_data[plane];
515
516
    for (i = 0; i < FF_ARRAY_ELEMS(frame->buf) && frame->buf[i]; i++) {
517
        AVBufferRef *buf = frame->buf[i];
518
        if (data >= buf->data && data < buf->data + buf->size)
519
            return buf;
520
    }
521
    for (i = 0; i < frame->nb_extended_buf; i++) {
522
        AVBufferRef *buf = frame->extended_buf[i];
523
        if (data >= buf->data && data < buf->data + buf->size)
524
            return buf;
525
    }
526
    return NULL;
527
}
528
529
AVFrameSideData *av_frame_new_side_data(AVFrame *frame,
530
                                        enum AVFrameSideDataType type,
531
                                        int size)
532
{
533
    AVFrameSideData *ret, **tmp;
534
535
    if (frame->nb_side_data > INT_MAX / sizeof(*frame->side_data) - 1)
536
        return NULL;
537
538
    tmp = av_realloc(frame->side_data,
539
                     (frame->nb_side_data + 1) * sizeof(*frame->side_data));
540
    if (!tmp)
541
        return NULL;
542
    frame->side_data = tmp;
543
544
    ret = av_mallocz(sizeof(*ret));
545
    if (!ret)
546
        return NULL;
547
548
    ret->data = av_malloc(size);
549
    if (!ret->data) {
550
        av_freep(&ret);
551
        return NULL;
552
    }
553
554
    ret->size = size;
555
    ret->type = type;
556
557
    frame->side_data[frame->nb_side_data++] = ret;
558
559
    return ret;
560
}
561
562
AVFrameSideData *av_frame_get_side_data(const AVFrame *frame,
563
                                        enum AVFrameSideDataType type)
564
{
565
    int i;
566
567
    for (i = 0; i < frame->nb_side_data; i++) {
568
        if (frame->side_data[i]->type == type)
569
            return frame->side_data[i];
570
    }
571
    return NULL;
572
}
573
574
static int frame_copy_video(AVFrame *dst, const AVFrame *src)
575
{
576
    const uint8_t *src_data[4];
577
    int i, planes;
578
579
    if (dst->width  < src->width ||
580
        dst->height < src->height)
581
        return AVERROR(EINVAL);
582
583
    planes = av_pix_fmt_count_planes(dst->format);
584
    for (i = 0; i < planes; i++)
585
        if (!dst->data[i] || !src->data[i])
586
            return AVERROR(EINVAL);
587
588
    memcpy(src_data, src->data, sizeof(src_data));
589
    av_image_copy(dst->data, dst->linesize,
590
                  src_data, src->linesize,
591
                  dst->format, src->width, src->height);
592
593
    return 0;
594
}
595
596
static int frame_copy_audio(AVFrame *dst, const AVFrame *src)
597
{
598
    int planar   = av_sample_fmt_is_planar(dst->format);
599
    int channels = dst->channels;
600
    int planes   = planar ? channels : 1;
601
    int i;
602
603
    if (dst->nb_samples     != src->nb_samples ||
604
        dst->channels       != src->channels ||
605
        dst->channel_layout != src->channel_layout)
606
        return AVERROR(EINVAL);
607
608
    CHECK_CHANNELS_CONSISTENCY(src);
609
610
    for (i = 0; i < planes; i++)
611
        if (!dst->extended_data[i] || !src->extended_data[i])
612
            return AVERROR(EINVAL);
613
614
    av_samples_copy(dst->extended_data, src->extended_data, 0, 0,
615
                    dst->nb_samples, channels, dst->format);
616
617
    return 0;
618
}
619
620
int av_frame_copy(AVFrame *dst, const AVFrame *src)
621
{
622
    if (dst->format != src->format || dst->format < 0)
623
        return AVERROR(EINVAL);
624
625
    if (dst->width > 0 && dst->height > 0)
626
        return frame_copy_video(dst, src);
627
    else if (dst->nb_samples > 0 && dst->channel_layout)
628
        return frame_copy_audio(dst, src);
629
630
    return AVERROR(EINVAL);
631
}
632
633
void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type)
634
{
635
    int i;
636
637
    for (i = 0; i < frame->nb_side_data; i++) {
638
        AVFrameSideData *sd = frame->side_data[i];
639
        if (sd->type == type) {
640
            free_side_data(&frame->side_data[i]);
641
            frame->side_data[i] = frame->side_data[frame->nb_side_data - 1];
642
            frame->nb_side_data--;
643
        }
644
    }
645
}
646
647
const char *av_frame_side_data_name(enum AVFrameSideDataType type)
648
{
649
    switch(type) {
650
    case AV_FRAME_DATA_PANSCAN:         return "AVPanScan";
651
    case AV_FRAME_DATA_A53_CC:          return "ATSC A53 Part 4 Closed Captions";
652
    case AV_FRAME_DATA_STEREO3D:        return "Stereoscopic 3d metadata";
653
    case AV_FRAME_DATA_MATRIXENCODING:  return "AVMatrixEncoding";
654
    case AV_FRAME_DATA_DOWNMIX_INFO:    return "Metadata relevant to a downmix procedure";
655
    case AV_FRAME_DATA_REPLAYGAIN:      return "AVReplayGain";
656
    case AV_FRAME_DATA_DISPLAYMATRIX:   return "3x3 displaymatrix";
657
    case AV_FRAME_DATA_MOTION_VECTORS:  return "Motion vectors";
658
    }
659
    return NULL;
660
}
661
#endif