Directory: | ../../../ffmpeg/ |
---|---|
File: | src/libavcodec/motionpixels.c |
Date: | 2022-07-06 18:02:43 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 167 | 188 | 88.8% |
Branches: | 76 | 98 | 77.6% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Motion Pixels Video Decoder | ||
3 | * Copyright (c) 2008 Gregory Montoir (cyx@users.sourceforge.net) | ||
4 | * | ||
5 | * This file is part of FFmpeg. | ||
6 | * | ||
7 | * FFmpeg is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * FFmpeg is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with FFmpeg; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #include "libavutil/thread.h" | ||
23 | |||
24 | #include "config.h" | ||
25 | |||
26 | #include "avcodec.h" | ||
27 | #include "get_bits.h" | ||
28 | #include "bswapdsp.h" | ||
29 | #include "codec_internal.h" | ||
30 | #include "internal.h" | ||
31 | |||
32 | #define MAX_HUFF_CODES 16 | ||
33 | |||
34 | #include "motionpixels_tablegen.h" | ||
35 | |||
36 | typedef struct HuffCode { | ||
37 | uint8_t size; | ||
38 | uint8_t delta; | ||
39 | } HuffCode; | ||
40 | |||
41 | typedef struct MotionPixelsContext { | ||
42 | AVCodecContext *avctx; | ||
43 | AVFrame *frame; | ||
44 | BswapDSPContext bdsp; | ||
45 | uint8_t *changes_map; | ||
46 | int offset_bits_len; | ||
47 | int codes_count, current_codes_count; | ||
48 | int max_codes_bits; | ||
49 | HuffCode codes[MAX_HUFF_CODES]; | ||
50 | VLC vlc; | ||
51 | YuvPixel *vpt, *hpt; | ||
52 | uint8_t gradient_scale[3]; | ||
53 | uint8_t *bswapbuf; | ||
54 | int bswapbuf_size; | ||
55 | } MotionPixelsContext; | ||
56 | |||
57 | 2 | static av_cold int mp_decode_end(AVCodecContext *avctx) | |
58 | { | ||
59 | 2 | MotionPixelsContext *mp = avctx->priv_data; | |
60 | |||
61 | 2 | av_freep(&mp->changes_map); | |
62 | 2 | av_freep(&mp->vpt); | |
63 | 2 | av_freep(&mp->hpt); | |
64 | 2 | av_freep(&mp->bswapbuf); | |
65 | 2 | av_frame_free(&mp->frame); | |
66 | |||
67 | 2 | return 0; | |
68 | } | ||
69 | |||
70 | 2 | static av_cold int mp_decode_init(AVCodecContext *avctx) | |
71 | { | ||
72 | av_unused static AVOnce init_static_once = AV_ONCE_INIT; | ||
73 | 2 | MotionPixelsContext *mp = avctx->priv_data; | |
74 | 2 | int w4 = (avctx->width + 3) & ~3; | |
75 | 2 | int h4 = (avctx->height + 3) & ~3; | |
76 | |||
77 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if(avctx->extradata_size < 2){ |
78 | ✗ | av_log(avctx, AV_LOG_ERROR, "extradata too small\n"); | |
79 | ✗ | return AVERROR_INVALIDDATA; | |
80 | } | ||
81 | |||
82 | 2 | mp->avctx = avctx; | |
83 | 2 | ff_bswapdsp_init(&mp->bdsp); | |
84 | 2 | mp->changes_map = av_calloc(avctx->width, h4); | |
85 | 2 | mp->offset_bits_len = av_log2(avctx->width * avctx->height) + 1; | |
86 | 2 | mp->vpt = av_calloc(avctx->height, sizeof(*mp->vpt)); | |
87 | 2 | mp->hpt = av_calloc(h4 / 4, w4 / 4 * sizeof(*mp->hpt)); | |
88 |
3/6✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
|
2 | if (!mp->changes_map || !mp->vpt || !mp->hpt) |
89 | ✗ | return AVERROR(ENOMEM); | |
90 | 2 | avctx->pix_fmt = AV_PIX_FMT_RGB555; | |
91 | |||
92 | 2 | mp->frame = av_frame_alloc(); | |
93 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!mp->frame) |
94 | ✗ | return AVERROR(ENOMEM); | |
95 | |||
96 | #if !CONFIG_HARDCODED_TABLES | ||
97 | 2 | ff_thread_once(&init_static_once, motionpixels_tableinit); | |
98 | #endif | ||
99 | |||
100 | 2 | return 0; | |
101 | } | ||
102 | |||
103 | 444 | static void mp_read_changes_map(MotionPixelsContext *mp, GetBitContext *gb, int count, int bits_len, int read_color) | |
104 | { | ||
105 | uint16_t *pixels; | ||
106 | 444 | int offset, w, h, color = 0, x, y, i; | |
107 | |||
108 |
2/2✓ Branch 0 taken 26975 times.
✓ Branch 1 taken 444 times.
|
27419 | while (count--) { |
109 | 26975 | offset = get_bits_long(gb, mp->offset_bits_len); | |
110 | 26975 | w = get_bits(gb, bits_len) + 1; | |
111 | 26975 | h = get_bits(gb, bits_len) + 1; | |
112 |
2/2✓ Branch 0 taken 16095 times.
✓ Branch 1 taken 10880 times.
|
26975 | if (read_color) |
113 | 16095 | color = get_bits(gb, 15); | |
114 | 26975 | x = offset % mp->avctx->width; | |
115 | 26975 | y = offset / mp->avctx->width; | |
116 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26975 times.
|
26975 | if (y >= mp->avctx->height) |
117 | ✗ | continue; | |
118 | 26975 | w = FFMIN(w, mp->avctx->width - x); | |
119 | 26975 | h = FFMIN(h, mp->avctx->height - y); | |
120 | 26975 | pixels = (uint16_t *)&mp->frame->data[0][y * mp->frame->linesize[0] + x * 2]; | |
121 |
2/2✓ Branch 0 taken 298286 times.
✓ Branch 1 taken 26975 times.
|
325261 | while (h--) { |
122 | 298286 | mp->changes_map[offset] = w; | |
123 |
2/2✓ Branch 0 taken 186030 times.
✓ Branch 1 taken 112256 times.
|
298286 | if (read_color) |
124 |
2/2✓ Branch 0 taken 1646821 times.
✓ Branch 1 taken 186030 times.
|
1832851 | for (i = 0; i < w; ++i) |
125 | 1646821 | pixels[i] = color; | |
126 | 298286 | offset += mp->avctx->width; | |
127 | 298286 | pixels += mp->frame->linesize[0] / 2; | |
128 | } | ||
129 | } | ||
130 | 444 | } | |
131 | |||
132 | 1651 | static int mp_get_code(MotionPixelsContext *mp, GetBitContext *gb, int size) | |
133 | { | ||
134 |
2/2✓ Branch 1 taken 1540 times.
✓ Branch 2 taken 1651 times.
|
3191 | while (get_bits1(gb)) { |
135 | 1540 | ++size; | |
136 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1540 times.
|
1540 | if (size > mp->max_codes_bits) { |
137 | ✗ | av_log(mp->avctx, AV_LOG_ERROR, "invalid code size %d/%d\n", size, mp->max_codes_bits); | |
138 | ✗ | return AVERROR_INVALIDDATA; | |
139 | } | ||
140 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1540 times.
|
1540 | if (mp_get_code(mp, gb, size) < 0) |
141 | ✗ | return AVERROR_INVALIDDATA; | |
142 | } | ||
143 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1651 times.
|
1651 | if (mp->current_codes_count >= mp->codes_count) { |
144 | ✗ | av_log(mp->avctx, AV_LOG_ERROR, "too many codes\n"); | |
145 | ✗ | return AVERROR_INVALIDDATA; | |
146 | } | ||
147 | |||
148 | 1651 | mp->codes[mp->current_codes_count++].size = size; | |
149 | 1651 | return 0; | |
150 | } | ||
151 | |||
152 | 111 | static int mp_read_codes_table(MotionPixelsContext *mp, GetBitContext *gb) | |
153 | { | ||
154 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
|
111 | if (mp->codes_count == 1) { |
155 | ✗ | mp->codes[0].delta = get_bits(gb, 4); | |
156 | } else { | ||
157 | int i; | ||
158 | int ret; | ||
159 | |||
160 | 111 | mp->max_codes_bits = get_bits(gb, 4); | |
161 |
2/2✓ Branch 0 taken 1651 times.
✓ Branch 1 taken 111 times.
|
1762 | for (i = 0; i < mp->codes_count; ++i) |
162 | 1651 | mp->codes[i].delta = get_bits(gb, 4); | |
163 | 111 | mp->current_codes_count = 0; | |
164 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 111 times.
|
111 | if ((ret = mp_get_code(mp, gb, 0)) < 0) |
165 | ✗ | return ret; | |
166 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
|
111 | if (mp->current_codes_count < mp->codes_count) { |
167 | ✗ | av_log(mp->avctx, AV_LOG_ERROR, "too few codes\n"); | |
168 | ✗ | return AVERROR_INVALIDDATA; | |
169 | } | ||
170 | } | ||
171 | 111 | return 0; | |
172 | } | ||
173 | |||
174 | 6395631 | static av_always_inline int mp_gradient(MotionPixelsContext *mp, int component, int v) | |
175 | { | ||
176 | int delta; | ||
177 | |||
178 | 6395631 | delta = (v - 7) * mp->gradient_scale[component]; | |
179 |
4/4✓ Branch 0 taken 6312633 times.
✓ Branch 1 taken 82998 times.
✓ Branch 2 taken 79238 times.
✓ Branch 3 taken 6233395 times.
|
6395631 | mp->gradient_scale[component] = (v == 0 || v == 14) ? 2 : 1; |
180 | 6395631 | return delta; | |
181 | } | ||
182 | |||
183 | 340991 | static YuvPixel mp_get_yuv_from_rgb(MotionPixelsContext *mp, int x, int y) | |
184 | { | ||
185 | int color; | ||
186 | |||
187 | 340991 | color = *(uint16_t *)&mp->frame->data[0][y * mp->frame->linesize[0] + x * 2]; | |
188 | 340991 | return mp_rgb_yuv_table[color]; | |
189 | } | ||
190 | |||
191 | 5666143 | static void mp_set_rgb_from_yuv(MotionPixelsContext *mp, int x, int y, const YuvPixel *p) | |
192 | { | ||
193 | int color; | ||
194 | |||
195 | 5666143 | color = mp_yuv_to_rgb(p->y, p->v, p->u, 1); | |
196 | 5666143 | *(uint16_t *)&mp->frame->data[0][y * mp->frame->linesize[0] + x * 2] = color; | |
197 | 5666143 | } | |
198 | |||
199 | 6395631 | static av_always_inline int mp_get_vlc(MotionPixelsContext *mp, GetBitContext *gb) | |
200 | { | ||
201 | 6395631 | return mp->vlc.table ? get_vlc2(gb, mp->vlc.table, mp->max_codes_bits, 1) | |
202 |
1/2✓ Branch 0 taken 6395631 times.
✗ Branch 1 not taken.
|
12791262 | : mp->codes[0].delta; |
203 | } | ||
204 | |||
205 | 26640 | static void mp_decode_line(MotionPixelsContext *mp, GetBitContext *gb, int y) | |
206 | { | ||
207 | YuvPixel p; | ||
208 | 26640 | const int y0 = y * mp->avctx->width; | |
209 | 26640 | int w, i, x = 0; | |
210 | |||
211 | 26640 | p = mp->vpt[y]; | |
212 |
2/2✓ Branch 0 taken 18532 times.
✓ Branch 1 taken 8108 times.
|
26640 | if (mp->changes_map[y0 + x] == 0) { |
213 | 18532 | memset(mp->gradient_scale, 1, sizeof(mp->gradient_scale)); | |
214 | 18532 | ++x; | |
215 | } | ||
216 |
2/2✓ Branch 0 taken 5945997 times.
✓ Branch 1 taken 26640 times.
|
5972637 | while (x < mp->avctx->width) { |
217 | 5945997 | w = mp->changes_map[y0 + x]; | |
218 |
2/2✓ Branch 0 taken 298386 times.
✓ Branch 1 taken 5647611 times.
|
5945997 | if (w != 0) { |
219 |
2/2✓ Branch 0 taken 73166 times.
✓ Branch 1 taken 225220 times.
|
298386 | if ((y & 3) == 0) { |
220 |
2/2✓ Branch 0 taken 69448 times.
✓ Branch 1 taken 3718 times.
|
73166 | if (mp->changes_map[y0 + x + mp->avctx->width] < w || |
221 |
2/2✓ Branch 0 taken 65081 times.
✓ Branch 1 taken 4367 times.
|
69448 | mp->changes_map[y0 + x + mp->avctx->width * 2] < w || |
222 |
2/2✓ Branch 0 taken 5232 times.
✓ Branch 1 taken 59849 times.
|
65081 | mp->changes_map[y0 + x + mp->avctx->width * 3] < w) { |
223 |
2/2✓ Branch 0 taken 34497 times.
✓ Branch 1 taken 13317 times.
|
47814 | for (i = (x + 3) & ~3; i < x + w; i += 4) { |
224 | 34497 | mp->hpt[((y / 4) * mp->avctx->width + i) / 4] = mp_get_yuv_from_rgb(mp, i, y); | |
225 | } | ||
226 | } | ||
227 | } | ||
228 | 298386 | x += w; | |
229 | 298386 | memset(mp->gradient_scale, 1, sizeof(mp->gradient_scale)); | |
230 | 298386 | p = mp_get_yuv_from_rgb(mp, x - 1, y); | |
231 | } else { | ||
232 | 5647611 | p.y += mp_gradient(mp, 0, mp_get_vlc(mp, gb)); | |
233 | 5647611 | p.y = av_clip_uintp2(p.y, 5); | |
234 |
2/2✓ Branch 0 taken 1417461 times.
✓ Branch 1 taken 4230150 times.
|
5647611 | if ((x & 3) == 0) { |
235 |
2/2✓ Branch 0 taken 360154 times.
✓ Branch 1 taken 1057307 times.
|
1417461 | if ((y & 3) == 0) { |
236 | 360154 | p.v += mp_gradient(mp, 1, mp_get_vlc(mp, gb)); | |
237 | 360154 | p.v = av_clip_intp2(p.v, 5); | |
238 | 360154 | p.u += mp_gradient(mp, 2, mp_get_vlc(mp, gb)); | |
239 | 360154 | p.u = av_clip_intp2(p.u, 5); | |
240 | 360154 | mp->hpt[((y / 4) * mp->avctx->width + x) / 4] = p; | |
241 | } else { | ||
242 | 1057307 | p.v = mp->hpt[((y / 4) * mp->avctx->width + x) / 4].v; | |
243 | 1057307 | p.u = mp->hpt[((y / 4) * mp->avctx->width + x) / 4].u; | |
244 | } | ||
245 | } | ||
246 | 5647611 | mp_set_rgb_from_yuv(mp, x, y, &p); | |
247 | 5647611 | ++x; | |
248 | } | ||
249 | } | ||
250 | 26640 | } | |
251 | |||
252 | 111 | static void mp_decode_frame_helper(MotionPixelsContext *mp, GetBitContext *gb) | |
253 | { | ||
254 | YuvPixel p; | ||
255 | int y, y0; | ||
256 | |||
257 | av_assert1(mp->changes_map[0]); | ||
258 | |||
259 |
2/2✓ Branch 0 taken 26640 times.
✓ Branch 1 taken 111 times.
|
26751 | for (y = 0; y < mp->avctx->height; ++y) { |
260 |
2/2✓ Branch 0 taken 8108 times.
✓ Branch 1 taken 18532 times.
|
26640 | if (mp->changes_map[y * mp->avctx->width] != 0) { |
261 | 8108 | memset(mp->gradient_scale, 1, sizeof(mp->gradient_scale)); | |
262 | 8108 | p = mp_get_yuv_from_rgb(mp, 0, y); | |
263 | } else { | ||
264 | 18532 | p.y += mp_gradient(mp, 0, mp_get_vlc(mp, gb)); | |
265 | 18532 | p.y = av_clip_uintp2(p.y, 5); | |
266 |
2/2✓ Branch 0 taken 4590 times.
✓ Branch 1 taken 13942 times.
|
18532 | if ((y & 3) == 0) { |
267 | 4590 | p.v += mp_gradient(mp, 1, mp_get_vlc(mp, gb)); | |
268 | 4590 | p.v = av_clip_intp2(p.v, 5); | |
269 | 4590 | p.u += mp_gradient(mp, 2, mp_get_vlc(mp, gb)); | |
270 | 4590 | p.u = av_clip_intp2(p.u, 5); | |
271 | } | ||
272 | 18532 | mp->vpt[y] = p; | |
273 | 18532 | mp_set_rgb_from_yuv(mp, 0, y, &p); | |
274 | } | ||
275 | } | ||
276 |
2/2✓ Branch 0 taken 222 times.
✓ Branch 1 taken 111 times.
|
333 | for (y0 = 0; y0 < 2; ++y0) |
277 |
2/2✓ Branch 0 taken 26640 times.
✓ Branch 1 taken 222 times.
|
26862 | for (y = y0; y < mp->avctx->height; y += 2) |
278 | 26640 | mp_decode_line(mp, gb, y); | |
279 | 111 | } | |
280 | |||
281 | 111 | static int mp_decode_frame(AVCodecContext *avctx, AVFrame *rframe, | |
282 | int *got_frame, AVPacket *avpkt) | ||
283 | { | ||
284 | 111 | const uint8_t *buf = avpkt->data; | |
285 | 111 | int buf_size = avpkt->size; | |
286 | 111 | MotionPixelsContext *mp = avctx->priv_data; | |
287 | GetBitContext gb; | ||
288 | int i, count1, count2, sz, ret; | ||
289 | |||
290 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 111 times.
|
111 | if ((ret = ff_reget_buffer(avctx, mp->frame, 0)) < 0) |
291 | ✗ | return ret; | |
292 | |||
293 | /* le32 bitstream msb first */ | ||
294 | 111 | av_fast_padded_malloc(&mp->bswapbuf, &mp->bswapbuf_size, buf_size); | |
295 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
|
111 | if (!mp->bswapbuf) |
296 | ✗ | return AVERROR(ENOMEM); | |
297 | 111 | mp->bdsp.bswap_buf((uint32_t *) mp->bswapbuf, (const uint32_t *) buf, | |
298 | buf_size / 4); | ||
299 |
2/2✓ Branch 0 taken 87 times.
✓ Branch 1 taken 24 times.
|
111 | if (buf_size & 3) |
300 | 87 | memcpy(mp->bswapbuf + (buf_size & ~3), buf + (buf_size & ~3), buf_size & 3); | |
301 | 111 | init_get_bits(&gb, mp->bswapbuf, buf_size * 8); | |
302 | |||
303 | 111 | memset(mp->changes_map, 0, avctx->width * avctx->height); | |
304 |
2/2✓ Branch 0 taken 222 times.
✓ Branch 1 taken 111 times.
|
333 | for (i = !(avctx->extradata[1] & 2); i < 2; ++i) { |
305 | 222 | count1 = get_bits(&gb, 12); | |
306 | 222 | count2 = get_bits(&gb, 12); | |
307 | 222 | mp_read_changes_map(mp, &gb, count1, 8, i); | |
308 | 222 | mp_read_changes_map(mp, &gb, count2, 4, i); | |
309 | } | ||
310 | |||
311 | 111 | mp->codes_count = get_bits(&gb, 4); | |
312 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
|
111 | if (mp->codes_count == 0) |
313 | ✗ | goto end; | |
314 | |||
315 |
2/2✓ Branch 0 taken 100 times.
✓ Branch 1 taken 11 times.
|
111 | if (mp->changes_map[0] == 0) { |
316 | 100 | *(uint16_t *)mp->frame->data[0] = get_bits(&gb, 15); | |
317 | 100 | mp->changes_map[0] = 1; | |
318 | } | ||
319 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 111 times.
|
111 | if (mp_read_codes_table(mp, &gb) < 0) |
320 | ✗ | goto end; | |
321 | |||
322 | 111 | sz = get_bits(&gb, 18); | |
323 |
1/2✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
|
111 | if (avctx->extradata[0] != 5) |
324 | 111 | sz += get_bits(&gb, 18); | |
325 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
|
111 | if (sz == 0) |
326 | ✗ | goto end; | |
327 | |||
328 |
1/2✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
|
111 | if (mp->codes_count > 1) { |
329 | /* The entries of the mp->codes array are sorted from right to left | ||
330 | * in the Huffman tree, hence -(int)sizeof(HuffCode). */ | ||
331 | 111 | ret = ff_init_vlc_from_lengths(&mp->vlc, mp->max_codes_bits, mp->codes_count, | |
332 | 111 | &mp->codes[mp->codes_count - 1].size, -(int)sizeof(HuffCode), | |
333 | 111 | &mp->codes[mp->codes_count - 1].delta, -(int)sizeof(HuffCode), 1, | |
334 | 0, 0, avctx); | ||
335 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
|
111 | if (ret < 0) |
336 | ✗ | goto end; | |
337 | } | ||
338 | 111 | mp_decode_frame_helper(mp, &gb); | |
339 | 111 | ff_free_vlc(&mp->vlc); | |
340 | |||
341 | 111 | end: | |
342 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 111 times.
|
111 | if ((ret = av_frame_ref(rframe, mp->frame)) < 0) |
343 | ✗ | return ret; | |
344 | 111 | *got_frame = 1; | |
345 | 111 | return buf_size; | |
346 | } | ||
347 | |||
348 | const FFCodec ff_motionpixels_decoder = { | ||
349 | .p.name = "motionpixels", | ||
350 | .p.long_name = NULL_IF_CONFIG_SMALL("Motion Pixels video"), | ||
351 | .p.type = AVMEDIA_TYPE_VIDEO, | ||
352 | .p.id = AV_CODEC_ID_MOTIONPIXELS, | ||
353 | .priv_data_size = sizeof(MotionPixelsContext), | ||
354 | .init = mp_decode_init, | ||
355 | .close = mp_decode_end, | ||
356 | FF_CODEC_DECODE_CB(mp_decode_frame), | ||
357 | .p.capabilities = AV_CODEC_CAP_DR1, | ||
358 | .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_INIT_THREADSAFE, | ||
359 | }; | ||
360 |