/src/libvpx/vpx_scale/generic/yv12config.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2010 The WebM project authors. All Rights Reserved. |
3 | | * |
4 | | * Use of this source code is governed by a BSD-style license |
5 | | * that can be found in the LICENSE file in the root of the source |
6 | | * tree. An additional intellectual property rights grant can be found |
7 | | * in the file PATENTS. All contributing project authors may |
8 | | * be found in the AUTHORS file in the root of the source tree. |
9 | | */ |
10 | | |
11 | | #include <assert.h> |
12 | | #include <limits.h> |
13 | | #include <stdint.h> |
14 | | |
15 | | #include "vpx_scale/yv12config.h" |
16 | | #include "vpx_mem/vpx_mem.h" |
17 | | #include "vpx_ports/mem.h" |
18 | | |
19 | | #if defined(VPX_MAX_ALLOCABLE_MEMORY) |
20 | | #include "vp9/common/vp9_onyxc_int.h" |
21 | | #endif // VPX_MAX_ALLOCABLE_MEMORY |
22 | | /**************************************************************************** |
23 | | * Exports |
24 | | ****************************************************************************/ |
25 | | |
26 | | /**************************************************************************** |
27 | | * |
28 | | ****************************************************************************/ |
29 | | #define yv12_align_addr(addr, align) \ |
30 | 1.95M | (void *)(((size_t)(addr) + ((align) - 1)) & (size_t)-(align)) |
31 | | |
32 | 395k | int vp8_yv12_de_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf) { |
33 | 395k | if (ybf) { |
34 | | // If libvpx is using frame buffer callbacks then buffer_alloc_sz must |
35 | | // not be set. |
36 | 395k | if (ybf->buffer_alloc_sz > 0) { |
37 | 114k | vpx_free(ybf->buffer_alloc); |
38 | 114k | } |
39 | | |
40 | | /* buffer_alloc isn't accessed by most functions. Rather y_buffer, |
41 | | u_buffer and v_buffer point to buffer_alloc and are used. Clear out |
42 | | all of this so that a freed pointer isn't inadvertently used */ |
43 | 395k | memset(ybf, 0, sizeof(YV12_BUFFER_CONFIG)); |
44 | 395k | } else { |
45 | 0 | return -1; |
46 | 0 | } |
47 | | |
48 | 395k | return 0; |
49 | 395k | } |
50 | | |
51 | | int vp8_yv12_realloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, int width, |
52 | 114k | int height, int border) { |
53 | 114k | if (ybf) { |
54 | 114k | int aligned_width = (width + 15) & ~15; |
55 | 114k | int aligned_height = (height + 15) & ~15; |
56 | 114k | int y_stride = ((aligned_width + 2 * border) + 31) & ~31; |
57 | 114k | int yplane_size = (aligned_height + 2 * border) * y_stride; |
58 | 114k | int uv_width = aligned_width >> 1; |
59 | 114k | int uv_height = aligned_height >> 1; |
60 | | /** There is currently a bunch of code which assumes |
61 | | * uv_stride == y_stride/2, so enforce this here. */ |
62 | 114k | int uv_stride = y_stride >> 1; |
63 | 114k | int uvplane_size = (uv_height + border) * uv_stride; |
64 | 114k | const size_t frame_size = yplane_size + 2 * uvplane_size; |
65 | | |
66 | 114k | if (!ybf->buffer_alloc) { |
67 | 114k | ybf->buffer_alloc = (uint8_t *)vpx_memalign(32, frame_size); |
68 | 114k | if (!ybf->buffer_alloc) { |
69 | 0 | ybf->buffer_alloc_sz = 0; |
70 | 0 | return -1; |
71 | 0 | } |
72 | 114k | #if defined(__has_feature) |
73 | | #if __has_feature(memory_sanitizer) |
74 | | // This memset is needed for fixing the issue of using uninitialized |
75 | | // value in msan test. It will cause a perf loss, so only do this for |
76 | | // msan test. |
77 | | memset(ybf->buffer_alloc, 0, frame_size); |
78 | | #endif |
79 | 114k | #endif |
80 | 114k | ybf->buffer_alloc_sz = frame_size; |
81 | 114k | } |
82 | | |
83 | 114k | if (ybf->buffer_alloc_sz < frame_size) return -1; |
84 | | |
85 | | /* Only support allocating buffers that have a border that's a multiple |
86 | | * of 32. The border restriction is required to get 16-byte alignment of |
87 | | * the start of the chroma rows without introducing an arbitrary gap |
88 | | * between planes, which would break the semantics of things like |
89 | | * vpx_img_set_rect(). */ |
90 | 114k | if (border & 0x1f) return -3; |
91 | | |
92 | 114k | ybf->y_crop_width = width; |
93 | 114k | ybf->y_crop_height = height; |
94 | 114k | ybf->y_width = aligned_width; |
95 | 114k | ybf->y_height = aligned_height; |
96 | 114k | ybf->y_stride = y_stride; |
97 | | |
98 | 114k | ybf->uv_crop_width = (width + 1) / 2; |
99 | 114k | ybf->uv_crop_height = (height + 1) / 2; |
100 | 114k | ybf->uv_width = uv_width; |
101 | 114k | ybf->uv_height = uv_height; |
102 | 114k | ybf->uv_stride = uv_stride; |
103 | | |
104 | 114k | ybf->alpha_width = 0; |
105 | 114k | ybf->alpha_height = 0; |
106 | 114k | ybf->alpha_stride = 0; |
107 | | |
108 | 114k | ybf->border = border; |
109 | 114k | ybf->frame_size = frame_size; |
110 | | |
111 | 114k | ybf->y_buffer = ybf->buffer_alloc + (border * y_stride) + border; |
112 | 114k | ybf->u_buffer = |
113 | 114k | ybf->buffer_alloc + yplane_size + (border / 2 * uv_stride) + border / 2; |
114 | 114k | ybf->v_buffer = ybf->buffer_alloc + yplane_size + uvplane_size + |
115 | 114k | (border / 2 * uv_stride) + border / 2; |
116 | 114k | ybf->alpha_buffer = NULL; |
117 | | |
118 | 114k | ybf->corrupted = 0; /* assume not currupted by errors */ |
119 | 114k | return 0; |
120 | 114k | } |
121 | 0 | return -2; |
122 | 114k | } |
123 | | |
124 | | int vp8_yv12_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, int width, int height, |
125 | 114k | int border) { |
126 | 114k | if (ybf) { |
127 | 114k | vp8_yv12_de_alloc_frame_buffer(ybf); |
128 | 114k | return vp8_yv12_realloc_frame_buffer(ybf, width, height, border); |
129 | 114k | } |
130 | 0 | return -2; |
131 | 114k | } |
132 | | |
133 | | #if CONFIG_VP9 |
134 | | // TODO(jkoleszar): Maybe replace this with struct vpx_image |
135 | | |
136 | 581k | int vpx_free_frame_buffer(YV12_BUFFER_CONFIG *ybf) { |
137 | 581k | if (ybf) { |
138 | 581k | if (ybf->buffer_alloc_sz > 0) { |
139 | 125k | vpx_free(ybf->buffer_alloc); |
140 | 125k | } |
141 | | |
142 | | /* buffer_alloc isn't accessed by most functions. Rather y_buffer, |
143 | | u_buffer and v_buffer point to buffer_alloc and are used. Clear out |
144 | | all of this so that a freed pointer isn't inadvertently used */ |
145 | 581k | memset(ybf, 0, sizeof(YV12_BUFFER_CONFIG)); |
146 | 581k | } else { |
147 | 0 | return -1; |
148 | 0 | } |
149 | | |
150 | 581k | return 0; |
151 | 581k | } |
152 | | |
153 | | int vpx_realloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, int width, int height, |
154 | | int ss_x, int ss_y, |
155 | | #if CONFIG_VP9_HIGHBITDEPTH |
156 | | int use_highbitdepth, |
157 | | #endif |
158 | | int border, int byte_alignment, |
159 | | vpx_codec_frame_buffer_t *fb, |
160 | 583k | vpx_get_frame_buffer_cb_fn_t cb, void *cb_priv) { |
161 | 583k | #if CONFIG_SIZE_LIMIT |
162 | 583k | if (width > DECODE_WIDTH_LIMIT || height > DECODE_HEIGHT_LIMIT) return -1; |
163 | 582k | #endif |
164 | | |
165 | | /* Only support allocating buffers that have a border that's a multiple |
166 | | * of 32. The border restriction is required to get 16-byte alignment of |
167 | | * the start of the chroma rows without introducing an arbitrary gap |
168 | | * between planes, which would break the semantics of things like |
169 | | * vpx_img_set_rect(). */ |
170 | 582k | if (border & 0x1f) return -3; |
171 | | |
172 | 582k | if (ybf) { |
173 | 582k | const int vp9_byte_align = (byte_alignment == 0) ? 1 : byte_alignment; |
174 | 582k | const int aligned_width = (width + 7) & ~7; |
175 | 582k | const int aligned_height = (height + 7) & ~7; |
176 | 582k | const int y_stride = ((aligned_width + 2 * border) + 31) & ~31; |
177 | 582k | const uint64_t yplane_size = |
178 | 582k | (aligned_height + 2 * border) * (uint64_t)y_stride + byte_alignment; |
179 | 582k | const int uv_width = aligned_width >> ss_x; |
180 | 582k | const int uv_height = aligned_height >> ss_y; |
181 | 582k | const int uv_stride = y_stride >> ss_x; |
182 | 582k | const int uv_border_w = border >> ss_x; |
183 | 582k | const int uv_border_h = border >> ss_y; |
184 | 582k | const uint64_t uvplane_size = |
185 | 582k | (uv_height + 2 * uv_border_h) * (uint64_t)uv_stride + byte_alignment; |
186 | | |
187 | 582k | #if CONFIG_VP9_HIGHBITDEPTH |
188 | 582k | const uint64_t frame_size = |
189 | 582k | (1 + use_highbitdepth) * (yplane_size + 2 * uvplane_size); |
190 | | #else |
191 | | const uint64_t frame_size = yplane_size + 2 * uvplane_size; |
192 | | #endif // CONFIG_VP9_HIGHBITDEPTH |
193 | | |
194 | 582k | uint8_t *buf = NULL; |
195 | | |
196 | 582k | #if defined(VPX_MAX_ALLOCABLE_MEMORY) |
197 | | // The decoder may allocate REF_FRAMES frame buffers in the frame buffer |
198 | | // pool. Bound the total amount of allocated memory as if these REF_FRAMES |
199 | | // frame buffers were allocated in a single allocation. |
200 | 582k | if (frame_size > VPX_MAX_ALLOCABLE_MEMORY / REF_FRAMES) return -1; |
201 | 582k | #endif // VPX_MAX_ALLOCABLE_MEMORY |
202 | | |
203 | | #if UINT64_MAX > SIZE_MAX |
204 | | // frame_size is stored in buffer_alloc_sz, which is a size_t. If it won't |
205 | | // fit, fail early. |
206 | | if (frame_size > SIZE_MAX) { |
207 | | return -1; |
208 | | } |
209 | | #endif |
210 | | |
211 | 582k | if (cb != NULL) { |
212 | 204k | const int align_addr_extra_size = 31; |
213 | 204k | const uint64_t external_frame_size = frame_size + align_addr_extra_size; |
214 | | |
215 | 204k | assert(fb != NULL); |
216 | | |
217 | 204k | if (external_frame_size != (size_t)external_frame_size) return -1; |
218 | | |
219 | | // Allocation to hold larger frame, or first allocation. |
220 | 204k | if (cb(cb_priv, (size_t)external_frame_size, fb) < 0) return -1; |
221 | | |
222 | 204k | if (fb->data == NULL || fb->size < external_frame_size) return -1; |
223 | | |
224 | 204k | ybf->buffer_alloc = (uint8_t *)yv12_align_addr(fb->data, 32); |
225 | | |
226 | 204k | #if defined(__has_feature) |
227 | | #if __has_feature(memory_sanitizer) |
228 | | // This memset is needed for fixing the issue of using uninitialized |
229 | | // value in msan test. It will cause a perf loss, so only do this for |
230 | | // msan test. |
231 | | memset(ybf->buffer_alloc, 0, (size_t)frame_size); |
232 | | #endif |
233 | 204k | #endif |
234 | 378k | } else if (frame_size > ybf->buffer_alloc_sz) { |
235 | | // Allocation to hold larger frame, or first allocation. |
236 | 125k | vpx_free(ybf->buffer_alloc); |
237 | 125k | ybf->buffer_alloc = NULL; |
238 | 125k | ybf->buffer_alloc_sz = 0; |
239 | | |
240 | 125k | ybf->buffer_alloc = (uint8_t *)vpx_memalign(32, (size_t)frame_size); |
241 | 125k | if (!ybf->buffer_alloc) return -1; |
242 | | |
243 | 125k | ybf->buffer_alloc_sz = (size_t)frame_size; |
244 | | |
245 | | // This memset is needed for fixing valgrind error from C loop filter |
246 | | // due to access uninitialized memory in frame border. It could be |
247 | | // removed if border is totally removed. |
248 | 125k | memset(ybf->buffer_alloc, 0, ybf->buffer_alloc_sz); |
249 | 125k | } |
250 | | |
251 | 582k | ybf->y_crop_width = width; |
252 | 582k | ybf->y_crop_height = height; |
253 | 582k | ybf->y_width = aligned_width; |
254 | 582k | ybf->y_height = aligned_height; |
255 | 582k | ybf->y_stride = y_stride; |
256 | | |
257 | 582k | ybf->uv_crop_width = (width + ss_x) >> ss_x; |
258 | 582k | ybf->uv_crop_height = (height + ss_y) >> ss_y; |
259 | 582k | ybf->uv_width = uv_width; |
260 | 582k | ybf->uv_height = uv_height; |
261 | 582k | ybf->uv_stride = uv_stride; |
262 | | |
263 | 582k | ybf->border = border; |
264 | 582k | ybf->frame_size = (size_t)frame_size; |
265 | 582k | ybf->subsampling_x = ss_x; |
266 | 582k | ybf->subsampling_y = ss_y; |
267 | | |
268 | 582k | buf = ybf->buffer_alloc; |
269 | 582k | #if CONFIG_VP9_HIGHBITDEPTH |
270 | 582k | if (use_highbitdepth) { |
271 | | // Store uint16 addresses when using 16bit framebuffers |
272 | 116k | buf = CONVERT_TO_BYTEPTR(ybf->buffer_alloc); |
273 | 116k | ybf->flags = YV12_FLAG_HIGHBITDEPTH; |
274 | 466k | } else { |
275 | 466k | ybf->flags = 0; |
276 | 466k | } |
277 | 582k | #endif // CONFIG_VP9_HIGHBITDEPTH |
278 | | |
279 | 582k | ybf->y_buffer = (uint8_t *)yv12_align_addr( |
280 | 582k | buf + (border * y_stride) + border, vp9_byte_align); |
281 | 582k | ybf->u_buffer = (uint8_t *)yv12_align_addr( |
282 | 582k | buf + yplane_size + (uv_border_h * uv_stride) + uv_border_w, |
283 | 582k | vp9_byte_align); |
284 | 582k | ybf->v_buffer = |
285 | 582k | (uint8_t *)yv12_align_addr(buf + yplane_size + uvplane_size + |
286 | 582k | (uv_border_h * uv_stride) + uv_border_w, |
287 | 582k | vp9_byte_align); |
288 | | |
289 | 582k | ybf->corrupted = 0; /* assume not corrupted by errors */ |
290 | 582k | return 0; |
291 | 582k | } |
292 | 0 | return -2; |
293 | 582k | } |
294 | | |
295 | | int vpx_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, int width, int height, |
296 | | int ss_x, int ss_y, |
297 | | #if CONFIG_VP9_HIGHBITDEPTH |
298 | | int use_highbitdepth, |
299 | | #endif |
300 | 100k | int border, int byte_alignment) { |
301 | 100k | if (ybf) { |
302 | 100k | vpx_free_frame_buffer(ybf); |
303 | 100k | return vpx_realloc_frame_buffer(ybf, width, height, ss_x, ss_y, |
304 | 100k | #if CONFIG_VP9_HIGHBITDEPTH |
305 | 100k | use_highbitdepth, |
306 | 100k | #endif |
307 | 100k | border, byte_alignment, NULL, NULL, NULL); |
308 | 100k | } |
309 | 0 | return -2; |
310 | 100k | } |
311 | | #endif |