/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 | } |