Coverage Report

Created: 2025-07-09 07:14

/src/libavif/src/codec_dav1d.c
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2019 Joe Drago. All rights reserved.
2
// SPDX-License-Identifier: BSD-2-Clause
3
4
#include "avif/internal.h"
5
6
#if defined(_MSC_VER)
7
#pragma warning(disable : 4201) // nonstandard extension used: nameless struct/union
8
#endif
9
#if defined(__clang__)
10
#pragma clang diagnostic push
11
#pragma clang diagnostic ignored "-Wc11-extensions" // C11 extension used: nameless struct/union
12
#endif
13
#include "dav1d/dav1d.h"
14
#if defined(__clang__)
15
#pragma clang diagnostic pop
16
#endif
17
18
#include <string.h>
19
20
// For those building with an older version of dav1d (not recommended).
21
#ifndef DAV1D_ERR
22
#define DAV1D_ERR(e) (-(e))
23
#endif
24
25
struct avifCodecInternal
26
{
27
    Dav1dContext * dav1dContext;
28
    Dav1dPicture dav1dPicture;
29
    avifBool hasPicture;
30
    avifRange colorRange;
31
};
32
33
static void avifDav1dFreeCallback(const uint8_t * buf, void * cookie)
34
99.8k
{
35
    // This data is owned by the decoder; nothing to free here
36
99.8k
    (void)buf;
37
99.8k
    (void)cookie;
38
99.8k
}
39
40
static void dav1dCodecDestroyInternal(avifCodec * codec)
41
63.8k
{
42
63.8k
    if (codec->internal->hasPicture) {
43
49.7k
        dav1d_picture_unref(&codec->internal->dav1dPicture);
44
49.7k
    }
45
63.8k
    if (codec->internal->dav1dContext) {
46
62.7k
        dav1d_close(&codec->internal->dav1dContext);
47
62.7k
    }
48
63.8k
    avifFree(codec->internal);
49
63.8k
}
50
51
static avifBool dav1dCodecGetNextImage(struct avifCodec * codec,
52
                                       const avifDecodeSample * sample,
53
                                       avifBool alpha,
54
                                       avifBool * isLimitedRangeAlpha,
55
                                       avifImage * image)
56
99.8k
{
57
99.8k
    if (codec->internal->dav1dContext == NULL) {
58
62.7k
        Dav1dSettings dav1dSettings;
59
62.7k
        dav1d_default_settings(&dav1dSettings);
60
        // Give all available threads to decode a single frame as fast as possible
61
62.7k
#if DAV1D_API_VERSION_MAJOR >= 6
62
62.7k
        dav1dSettings.max_frame_delay = 1;
63
62.7k
        dav1dSettings.n_threads = AVIF_CLAMP(codec->maxThreads, 1, DAV1D_MAX_THREADS);
64
#else
65
        dav1dSettings.n_frame_threads = 1;
66
        dav1dSettings.n_tile_threads = AVIF_CLAMP(codec->maxThreads, 1, DAV1D_MAX_TILE_THREADS);
67
#endif // DAV1D_API_VERSION_MAJOR >= 6
68
        // Set a maximum frame size limit to avoid OOM'ing fuzzers. In 32-bit builds, if
69
        // frame_size_limit > 8192 * 8192, dav1d reduces frame_size_limit to 8192 * 8192 and logs
70
        // a message, so we set frame_size_limit to at most 8192 * 8192 to avoid the dav1d_log
71
        // message.
72
62.7k
        dav1dSettings.frame_size_limit = (sizeof(size_t) < 8) ? AVIF_MIN(codec->imageSizeLimit, 8192 * 8192) : codec->imageSizeLimit;
73
62.7k
        dav1dSettings.operating_point = codec->operatingPoint;
74
62.7k
        dav1dSettings.all_layers = codec->allLayers;
75
76
62.7k
        if (dav1d_open(&codec->internal->dav1dContext, &dav1dSettings) != 0) {
77
0
            return AVIF_FALSE;
78
0
        }
79
62.7k
    }
80
81
99.8k
    avifBool gotPicture = AVIF_FALSE;
82
99.8k
    Dav1dPicture nextFrame;
83
99.8k
    memset(&nextFrame, 0, sizeof(Dav1dPicture));
84
85
99.8k
    Dav1dData dav1dData;
86
99.8k
    if (dav1d_data_wrap(&dav1dData, sample->data.data, sample->data.size, avifDav1dFreeCallback, NULL) != 0) {
87
0
        return AVIF_FALSE;
88
0
    }
89
90
99.8k
    int res;
91
99.8k
    for (;;) {
92
99.8k
        if (dav1dData.data) {
93
99.8k
            res = dav1d_send_data(codec->internal->dav1dContext, &dav1dData);
94
99.8k
            if ((res < 0) && (res != DAV1D_ERR(EAGAIN))) {
95
12.0k
                dav1d_data_unref(&dav1dData);
96
12.0k
                return AVIF_FALSE;
97
12.0k
            }
98
99.8k
        }
99
100
87.7k
        res = dav1d_get_picture(codec->internal->dav1dContext, &nextFrame);
101
87.7k
        if (res == DAV1D_ERR(EAGAIN)) {
102
859
            if (dav1dData.data) {
103
                // send more data
104
0
                continue;
105
0
            }
106
859
            return AVIF_FALSE;
107
86.8k
        } else if (res < 0) {
108
            // No more frames
109
0
            if (dav1dData.data) {
110
0
                dav1d_data_unref(&dav1dData);
111
0
            }
112
0
            return AVIF_FALSE;
113
86.8k
        } else {
114
            // Got a picture!
115
86.8k
            if ((sample->spatialID != AVIF_SPATIAL_ID_UNSET) && (sample->spatialID != nextFrame.frame_hdr->spatial_id)) {
116
                // Layer selection: skip this unwanted layer
117
0
                dav1d_picture_unref(&nextFrame);
118
86.8k
            } else {
119
86.8k
                gotPicture = AVIF_TRUE;
120
86.8k
                break;
121
86.8k
            }
122
86.8k
        }
123
87.7k
    }
124
86.8k
    if (dav1dData.data) {
125
0
        dav1d_data_unref(&dav1dData);
126
0
    }
127
128
    // Drain all buffered frames in the decoder.
129
    //
130
    // The sample should have only one frame of the desired layer. If there are more frames after
131
    // that frame, we need to discard them so that they won't be mistakenly output when the decoder
132
    // is used to decode another sample.
133
86.8k
    Dav1dPicture bufferedFrame;
134
86.8k
    memset(&bufferedFrame, 0, sizeof(Dav1dPicture));
135
89.1k
    do {
136
89.1k
        res = dav1d_get_picture(codec->internal->dav1dContext, &bufferedFrame);
137
89.1k
        if (res < 0) {
138
86.8k
            if (res != DAV1D_ERR(EAGAIN)) {
139
1.53k
                if (gotPicture) {
140
1.53k
                    dav1d_picture_unref(&nextFrame);
141
1.53k
                }
142
1.53k
                return AVIF_FALSE;
143
1.53k
            }
144
86.8k
        } else {
145
2.30k
            dav1d_picture_unref(&bufferedFrame);
146
2.30k
        }
147
89.1k
    } while (res == 0);
148
149
85.3k
    if (gotPicture) {
150
85.3k
        dav1d_picture_unref(&codec->internal->dav1dPicture);
151
85.3k
        codec->internal->dav1dPicture = nextFrame;
152
85.3k
        codec->internal->colorRange = codec->internal->dav1dPicture.seq_hdr->color_range ? AVIF_RANGE_FULL : AVIF_RANGE_LIMITED;
153
85.3k
        codec->internal->hasPicture = AVIF_TRUE;
154
85.3k
    } else {
155
0
        if (alpha && codec->internal->hasPicture) {
156
            // Special case: reuse last alpha frame
157
0
        } else {
158
0
            return AVIF_FALSE;
159
0
        }
160
0
    }
161
162
85.3k
    Dav1dPicture * dav1dImage = &codec->internal->dav1dPicture;
163
85.3k
    avifBool isColor = !alpha;
164
85.3k
    if (isColor) {
165
        // Color (YUV) planes - set image to correct size / format, fill color
166
167
65.0k
        avifPixelFormat yuvFormat = AVIF_PIXEL_FORMAT_NONE;
168
65.0k
        switch (dav1dImage->p.layout) {
169
28.7k
            case DAV1D_PIXEL_LAYOUT_I400:
170
28.7k
                yuvFormat = AVIF_PIXEL_FORMAT_YUV400;
171
28.7k
                break;
172
13.8k
            case DAV1D_PIXEL_LAYOUT_I420:
173
13.8k
                yuvFormat = AVIF_PIXEL_FORMAT_YUV420;
174
13.8k
                break;
175
9.05k
            case DAV1D_PIXEL_LAYOUT_I422:
176
9.05k
                yuvFormat = AVIF_PIXEL_FORMAT_YUV422;
177
9.05k
                break;
178
13.4k
            case DAV1D_PIXEL_LAYOUT_I444:
179
13.4k
                yuvFormat = AVIF_PIXEL_FORMAT_YUV444;
180
13.4k
                break;
181
65.0k
        }
182
183
65.0k
        if (image->width && image->height) {
184
14.8k
            if ((image->width != (uint32_t)dav1dImage->p.w) || (image->height != (uint32_t)dav1dImage->p.h) ||
185
14.8k
                (image->depth != (uint32_t)dav1dImage->p.bpc) || (image->yuvFormat != yuvFormat)) {
186
                // Throw it all out
187
420
                avifImageFreePlanes(image, AVIF_PLANES_ALL);
188
420
            }
189
14.8k
        }
190
65.0k
        image->width = dav1dImage->p.w;
191
65.0k
        image->height = dav1dImage->p.h;
192
65.0k
        image->depth = dav1dImage->p.bpc;
193
194
65.0k
        image->yuvFormat = yuvFormat;
195
65.0k
        image->yuvRange = codec->internal->colorRange;
196
65.0k
        image->yuvChromaSamplePosition = (avifChromaSamplePosition)dav1dImage->seq_hdr->chr;
197
198
65.0k
        image->colorPrimaries = (avifColorPrimaries)dav1dImage->seq_hdr->pri;
199
65.0k
        image->transferCharacteristics = (avifTransferCharacteristics)dav1dImage->seq_hdr->trc;
200
65.0k
        image->matrixCoefficients = (avifMatrixCoefficients)dav1dImage->seq_hdr->mtrx;
201
202
        // Steal the pointers from the decoder's image directly
203
65.0k
        avifImageFreePlanes(image, AVIF_PLANES_YUV);
204
65.0k
        int yuvPlaneCount = (yuvFormat == AVIF_PIXEL_FORMAT_YUV400) ? 1 : 3;
205
202k
        for (int yuvPlane = 0; yuvPlane < yuvPlaneCount; ++yuvPlane) {
206
137k
            image->yuvPlanes[yuvPlane] = dav1dImage->data[yuvPlane];
207
137k
            image->yuvRowBytes[yuvPlane] = (uint32_t)dav1dImage->stride[(yuvPlane == AVIF_CHAN_Y) ? 0 : 1];
208
137k
        }
209
65.0k
        image->imageOwnsYUVPlanes = AVIF_FALSE;
210
65.0k
    } else {
211
        // Alpha plane - ensure image is correct size, fill color
212
213
20.2k
        if (image->width && image->height) {
214
5.76k
            if ((image->width != (uint32_t)dav1dImage->p.w) || (image->height != (uint32_t)dav1dImage->p.h) ||
215
5.76k
                (image->depth != (uint32_t)dav1dImage->p.bpc)) {
216
                // Alpha plane doesn't match previous alpha plane decode, bail out
217
16
                return AVIF_FALSE;
218
16
            }
219
5.76k
        }
220
20.2k
        image->width = dav1dImage->p.w;
221
20.2k
        image->height = dav1dImage->p.h;
222
20.2k
        image->depth = dav1dImage->p.bpc;
223
224
20.2k
        avifImageFreePlanes(image, AVIF_PLANES_A);
225
20.2k
        image->alphaPlane = dav1dImage->data[0];
226
20.2k
        image->alphaRowBytes = (uint32_t)dav1dImage->stride[0];
227
20.2k
        *isLimitedRangeAlpha = (codec->internal->colorRange == AVIF_RANGE_LIMITED);
228
20.2k
        image->imageOwnsAlphaPlane = AVIF_FALSE;
229
20.2k
    }
230
85.3k
    return AVIF_TRUE;
231
85.3k
}
232
233
const char * avifCodecVersionDav1d(void)
234
0
{
235
0
    return dav1d_version();
236
0
}
237
238
avifCodec * avifCodecCreateDav1d(void)
239
63.8k
{
240
63.8k
    avifCodec * codec = (avifCodec *)avifAlloc(sizeof(avifCodec));
241
63.8k
    if (codec == NULL) {
242
0
        return NULL;
243
0
    }
244
63.8k
    memset(codec, 0, sizeof(struct avifCodec));
245
63.8k
    codec->getNextImage = dav1dCodecGetNextImage;
246
63.8k
    codec->destroyInternal = dav1dCodecDestroyInternal;
247
248
63.8k
    codec->internal = (struct avifCodecInternal *)avifAlloc(sizeof(struct avifCodecInternal));
249
63.8k
    if (codec->internal == NULL) {
250
0
        avifFree(codec);
251
0
        return NULL;
252
0
    }
253
63.8k
    memset(codec->internal, 0, sizeof(struct avifCodecInternal));
254
63.8k
    return codec;
255
63.8k
}