Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Copyright (c) 2021 Paul B Mahol | ||
3 | * | ||
4 | * This file is part of FFmpeg. | ||
5 | * | ||
6 | * FFmpeg is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU Lesser General Public | ||
8 | * License as published by the Free Software Foundation; either | ||
9 | * version 2.1 of the License, or (at your option) any later version. | ||
10 | * | ||
11 | * FFmpeg is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | * Lesser General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU Lesser General Public | ||
17 | * License along with FFmpeg; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file | ||
23 | * OpenEXR encoder | ||
24 | */ | ||
25 | |||
26 | #include <float.h> | ||
27 | #include <zlib.h> | ||
28 | |||
29 | #include "libavutil/avassert.h" | ||
30 | #include "libavutil/intfloat.h" | ||
31 | #include "libavutil/mem.h" | ||
32 | #include "libavutil/opt.h" | ||
33 | #include "libavutil/imgutils.h" | ||
34 | #include "libavutil/float2half.h" | ||
35 | #include "avcodec.h" | ||
36 | #include "bytestream.h" | ||
37 | #include "codec_internal.h" | ||
38 | #include "encode.h" | ||
39 | |||
40 | enum ExrCompr { | ||
41 | EXR_RAW, | ||
42 | EXR_RLE, | ||
43 | EXR_ZIP1, | ||
44 | EXR_ZIP16, | ||
45 | EXR_NBCOMPR, | ||
46 | }; | ||
47 | |||
48 | enum ExrPixelType { | ||
49 | EXR_UINT, | ||
50 | EXR_HALF, | ||
51 | EXR_FLOAT, | ||
52 | EXR_UNKNOWN, | ||
53 | }; | ||
54 | |||
55 | static const char abgr_chlist[4] = { 'A', 'B', 'G', 'R' }; | ||
56 | static const char bgr_chlist[4] = { 'B', 'G', 'R', 'A' }; | ||
57 | static const char y_chlist[4] = { 'Y' }; | ||
58 | static const uint8_t gbra_order[4] = { 3, 1, 0, 2 }; | ||
59 | static const uint8_t gbr_order[4] = { 1, 0, 2, 0 }; | ||
60 | static const uint8_t y_order[4] = { 0 }; | ||
61 | |||
62 | typedef struct EXRScanlineData { | ||
63 | uint8_t *compressed_data; | ||
64 | unsigned int compressed_size; | ||
65 | |||
66 | uint8_t *uncompressed_data; | ||
67 | unsigned int uncompressed_size; | ||
68 | |||
69 | uint8_t *tmp; | ||
70 | unsigned int tmp_size; | ||
71 | |||
72 | int64_t actual_size; | ||
73 | } EXRScanlineData; | ||
74 | |||
75 | typedef struct EXRContext { | ||
76 | const AVClass *class; | ||
77 | |||
78 | int compression; | ||
79 | int pixel_type; | ||
80 | int planes; | ||
81 | int nb_scanlines; | ||
82 | int scanline_height; | ||
83 | float gamma; | ||
84 | const char *ch_names; | ||
85 | const uint8_t *ch_order; | ||
86 | PutByteContext pb; | ||
87 | |||
88 | EXRScanlineData *scanline; | ||
89 | |||
90 | Float2HalfTables f2h_tables; | ||
91 | } EXRContext; | ||
92 | |||
93 | 12 | static av_cold int encode_init(AVCodecContext *avctx) | |
94 | { | ||
95 | 12 | EXRContext *s = avctx->priv_data; | |
96 | |||
97 | 12 | ff_init_float2half_tables(&s->f2h_tables); | |
98 | |||
99 |
3/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
12 | switch (avctx->pix_fmt) { |
100 | 4 | case AV_PIX_FMT_GBRPF32: | |
101 | 4 | s->planes = 3; | |
102 | 4 | s->ch_names = bgr_chlist; | |
103 | 4 | s->ch_order = gbr_order; | |
104 | 4 | break; | |
105 | 4 | case AV_PIX_FMT_GBRAPF32: | |
106 | 4 | s->planes = 4; | |
107 | 4 | s->ch_names = abgr_chlist; | |
108 | 4 | s->ch_order = gbra_order; | |
109 | 4 | break; | |
110 | 4 | case AV_PIX_FMT_GRAYF32: | |
111 | 4 | s->planes = 1; | |
112 | 4 | s->ch_names = y_chlist; | |
113 | 4 | s->ch_order = y_order; | |
114 | 4 | break; | |
115 | ✗ | default: | |
116 | ✗ | av_assert0(0); | |
117 | } | ||
118 | |||
119 |
2/3✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
12 | switch (s->compression) { |
120 | 9 | case EXR_RAW: | |
121 | case EXR_RLE: | ||
122 | case EXR_ZIP1: | ||
123 | 9 | s->scanline_height = 1; | |
124 | 9 | s->nb_scanlines = avctx->height; | |
125 | 9 | break; | |
126 | 3 | case EXR_ZIP16: | |
127 | 3 | s->scanline_height = 16; | |
128 | 3 | s->nb_scanlines = (avctx->height + s->scanline_height - 1) / s->scanline_height; | |
129 | 3 | break; | |
130 | ✗ | default: | |
131 | ✗ | av_assert0(0); | |
132 | } | ||
133 | |||
134 | 12 | s->scanline = av_calloc(s->nb_scanlines, sizeof(*s->scanline)); | |
135 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (!s->scanline) |
136 | ✗ | return AVERROR(ENOMEM); | |
137 | |||
138 | 12 | return 0; | |
139 | } | ||
140 | |||
141 | 12 | static av_cold int encode_close(AVCodecContext *avctx) | |
142 | { | ||
143 | 12 | EXRContext *s = avctx->priv_data; | |
144 | |||
145 |
3/4✓ Branch 0 taken 2646 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 2646 times.
✗ Branch 3 not taken.
|
2658 | for (int y = 0; y < s->nb_scanlines && s->scanline; y++) { |
146 | 2646 | EXRScanlineData *scanline = &s->scanline[y]; | |
147 | |||
148 | 2646 | av_freep(&scanline->tmp); | |
149 | 2646 | av_freep(&scanline->compressed_data); | |
150 | 2646 | av_freep(&scanline->uncompressed_data); | |
151 | } | ||
152 | |||
153 | 12 | av_freep(&s->scanline); | |
154 | |||
155 | 12 | return 0; | |
156 | } | ||
157 | |||
158 | 23166 | static void reorder_pixels(uint8_t *dst, const uint8_t *src, ptrdiff_t size) | |
159 | { | ||
160 | 23166 | const ptrdiff_t half_size = (size + 1) / 2; | |
161 | 23166 | uint8_t *t1 = dst; | |
162 | 23166 | uint8_t *t2 = dst + half_size; | |
163 | |||
164 |
2/2✓ Branch 0 taken 63258624 times.
✓ Branch 1 taken 23166 times.
|
63281790 | for (ptrdiff_t i = 0; i < half_size; i++) { |
165 | 63258624 | t1[i] = *(src++); | |
166 | 63258624 | t2[i] = *(src++); | |
167 | } | ||
168 | 23166 | } | |
169 | |||
170 | 23166 | static void predictor(uint8_t *src, ptrdiff_t size) | |
171 | { | ||
172 | 23166 | int p = src[0]; | |
173 | |||
174 |
2/2✓ Branch 0 taken 126494082 times.
✓ Branch 1 taken 23166 times.
|
126517248 | for (ptrdiff_t i = 1; i < size; i++) { |
175 | 126494082 | int d = src[i] - p + 384; | |
176 | |||
177 | 126494082 | p = src[i]; | |
178 | 126494082 | src[i] = d; | |
179 | } | ||
180 | 23166 | } | |
181 | |||
182 | 11232 | static int64_t rle_compress(uint8_t *out, int64_t out_size, | |
183 | const uint8_t *in, int64_t in_size) | ||
184 | { | ||
185 | 11232 | int64_t i = 0, o = 0, run = 1, copy = 0; | |
186 | |||
187 |
2/2✓ Branch 0 taken 1104099 times.
✓ Branch 1 taken 11160 times.
|
1115259 | while (i < in_size) { |
188 |
6/6✓ Branch 0 taken 4481360 times.
✓ Branch 1 taken 218090 times.
✓ Branch 2 taken 3614071 times.
✓ Branch 3 taken 867289 times.
✓ Branch 4 taken 3595351 times.
✓ Branch 5 taken 18720 times.
|
4699450 | while (i + run < in_size && in[i] == in[i + run] && run < 128) |
189 | 3595351 | run++; | |
190 | |||
191 |
2/2✓ Branch 0 taken 106157 times.
✓ Branch 1 taken 997942 times.
|
1104099 | if (run >= 3) { |
192 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 106157 times.
|
106157 | if (o + 2 >= out_size) |
193 | ✗ | return -1; | |
194 | 106157 | out[o++] = run - 1; | |
195 | 106157 | out[o++] = in[i]; | |
196 | 106157 | i += run; | |
197 | } else { | ||
198 |
2/2✓ Branch 0 taken 780024 times.
✓ Branch 1 taken 217918 times.
|
997942 | if (i + run < in_size) |
199 | 780024 | copy += run; | |
200 |
6/6✓ Branch 0 taken 38791597 times.
✓ Branch 1 taken 10988 times.
✓ Branch 2 taken 38647669 times.
✓ Branch 3 taken 143928 times.
✓ Branch 4 taken 37804643 times.
✓ Branch 5 taken 843026 times.
|
38802585 | while (i + copy < in_size && copy < 127 && in[i + copy] != in[i + copy - 1]) |
201 | 37804643 | copy++; | |
202 | |||
203 |
2/2✓ Branch 0 taken 72 times.
✓ Branch 1 taken 997870 times.
|
997942 | if (o + 1 + copy >= out_size) |
204 | 72 | return -1; | |
205 | 997870 | out[o++] = -copy; | |
206 | |||
207 |
2/2✓ Branch 0 taken 38789590 times.
✓ Branch 1 taken 997870 times.
|
39787460 | for (int x = 0; x < copy; x++) |
208 | 38789590 | out[o + x] = in[i + x]; | |
209 | |||
210 | 997870 | o += copy; | |
211 | 997870 | i += copy; | |
212 | 997870 | copy = 0; | |
213 | } | ||
214 | |||
215 | 1104027 | run = 1; | |
216 | } | ||
217 | |||
218 | 11160 | return o; | |
219 | } | ||
220 | |||
221 | 39 | static int encode_scanline_rle(EXRContext *s, const AVFrame *frame) | |
222 | { | ||
223 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
|
39 | const int64_t element_size = s->pixel_type == EXR_HALF ? 2LL : 4LL; |
224 | |||
225 |
2/2✓ Branch 0 taken 11232 times.
✓ Branch 1 taken 39 times.
|
11271 | for (int y = 0; y < frame->height; y++) { |
226 | 11232 | EXRScanlineData *scanline = &s->scanline[y]; | |
227 | 11232 | int64_t tmp_size = element_size * s->planes * frame->width; | |
228 | 11232 | int64_t max_compressed_size = tmp_size * 3 / 2; | |
229 | |||
230 | 11232 | av_fast_padded_malloc(&scanline->uncompressed_data, &scanline->uncompressed_size, tmp_size); | |
231 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11232 times.
|
11232 | if (!scanline->uncompressed_data) |
232 | ✗ | return AVERROR(ENOMEM); | |
233 | |||
234 | 11232 | av_fast_padded_malloc(&scanline->tmp, &scanline->tmp_size, tmp_size); | |
235 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11232 times.
|
11232 | if (!scanline->tmp) |
236 | ✗ | return AVERROR(ENOMEM); | |
237 | |||
238 | 11232 | av_fast_padded_malloc(&scanline->compressed_data, &scanline->compressed_size, max_compressed_size); | |
239 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11232 times.
|
11232 | if (!scanline->compressed_data) |
240 | ✗ | return AVERROR(ENOMEM); | |
241 | |||
242 |
1/3✓ Branch 0 taken 11232 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
11232 | switch (s->pixel_type) { |
243 | 11232 | case EXR_FLOAT: | |
244 |
2/2✓ Branch 0 taken 29952 times.
✓ Branch 1 taken 11232 times.
|
41184 | for (int p = 0; p < s->planes; p++) { |
245 | 29952 | int ch = s->ch_order[p]; | |
246 | |||
247 | 29952 | memcpy(scanline->uncompressed_data + frame->width * 4 * p, | |
248 | 29952 | frame->data[ch] + y * frame->linesize[ch], frame->width * 4); | |
249 | } | ||
250 | 11232 | break; | |
251 | ✗ | case EXR_HALF: | |
252 | ✗ | for (int p = 0; p < s->planes; p++) { | |
253 | ✗ | int ch = s->ch_order[p]; | |
254 | ✗ | uint16_t *dst = (uint16_t *)(scanline->uncompressed_data + frame->width * 2 * p); | |
255 | ✗ | const uint32_t *src = (const uint32_t *)(frame->data[ch] + y * frame->linesize[ch]); | |
256 | |||
257 | ✗ | for (int x = 0; x < frame->width; x++) | |
258 | ✗ | dst[x] = float2half(src[x], &s->f2h_tables); | |
259 | } | ||
260 | ✗ | break; | |
261 | } | ||
262 | |||
263 | 11232 | reorder_pixels(scanline->tmp, scanline->uncompressed_data, tmp_size); | |
264 | 11232 | predictor(scanline->tmp, tmp_size); | |
265 | 22464 | scanline->actual_size = rle_compress(scanline->compressed_data, | |
266 | max_compressed_size, | ||
267 | 11232 | scanline->tmp, tmp_size); | |
268 | |||
269 |
4/4✓ Branch 0 taken 11160 times.
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 7005 times.
✓ Branch 3 taken 4155 times.
|
11232 | if (scanline->actual_size <= 0 || scanline->actual_size >= tmp_size) { |
270 | 7077 | FFSWAP(uint8_t *, scanline->uncompressed_data, scanline->compressed_data); | |
271 | 7077 | FFSWAP(int, scanline->uncompressed_size, scanline->compressed_size); | |
272 | 7077 | scanline->actual_size = tmp_size; | |
273 | } | ||
274 | } | ||
275 | |||
276 | 39 | return 0; | |
277 | } | ||
278 | |||
279 | 78 | static int encode_scanline_zip(EXRContext *s, const AVFrame *frame) | |
280 | { | ||
281 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
|
78 | const int64_t element_size = s->pixel_type == EXR_HALF ? 2LL : 4LL; |
282 | |||
283 |
2/2✓ Branch 0 taken 11934 times.
✓ Branch 1 taken 78 times.
|
12012 | for (int y = 0; y < s->nb_scanlines; y++) { |
284 | 11934 | EXRScanlineData *scanline = &s->scanline[y]; | |
285 | 11934 | const int scanline_height = FFMIN(s->scanline_height, frame->height - y * s->scanline_height); | |
286 | 11934 | int64_t tmp_size = element_size * s->planes * frame->width * scanline_height; | |
287 | 11934 | int64_t max_compressed_size = tmp_size * 3 / 2; | |
288 | unsigned long actual_size, source_size; | ||
289 | |||
290 | 11934 | av_fast_padded_malloc(&scanline->uncompressed_data, &scanline->uncompressed_size, tmp_size); | |
291 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11934 times.
|
11934 | if (!scanline->uncompressed_data) |
292 | ✗ | return AVERROR(ENOMEM); | |
293 | |||
294 | 11934 | av_fast_padded_malloc(&scanline->tmp, &scanline->tmp_size, tmp_size); | |
295 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11934 times.
|
11934 | if (!scanline->tmp) |
296 | ✗ | return AVERROR(ENOMEM); | |
297 | |||
298 | 11934 | av_fast_padded_malloc(&scanline->compressed_data, &scanline->compressed_size, max_compressed_size); | |
299 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11934 times.
|
11934 | if (!scanline->compressed_data) |
300 | ✗ | return AVERROR(ENOMEM); | |
301 | |||
302 |
1/3✓ Branch 0 taken 11934 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
11934 | switch (s->pixel_type) { |
303 | 11934 | case EXR_FLOAT: | |
304 |
2/2✓ Branch 0 taken 22464 times.
✓ Branch 1 taken 11934 times.
|
34398 | for (int l = 0; l < scanline_height; l++) { |
305 | 22464 | const int scanline_size = frame->width * 4 * s->planes; | |
306 | |||
307 |
2/2✓ Branch 0 taken 59904 times.
✓ Branch 1 taken 22464 times.
|
82368 | for (int p = 0; p < s->planes; p++) { |
308 | 59904 | int ch = s->ch_order[p]; | |
309 | |||
310 | 59904 | memcpy(scanline->uncompressed_data + scanline_size * l + p * frame->width * 4, | |
311 | 59904 | frame->data[ch] + (y * s->scanline_height + l) * frame->linesize[ch], | |
312 | 59904 | frame->width * 4); | |
313 | } | ||
314 | } | ||
315 | 11934 | break; | |
316 | ✗ | case EXR_HALF: | |
317 | ✗ | for (int l = 0; l < scanline_height; l++) { | |
318 | ✗ | const int scanline_size = frame->width * 2 * s->planes; | |
319 | |||
320 | ✗ | for (int p = 0; p < s->planes; p++) { | |
321 | ✗ | int ch = s->ch_order[p]; | |
322 | ✗ | uint16_t *dst = (uint16_t *)(scanline->uncompressed_data + scanline_size * l + p * frame->width * 2); | |
323 | ✗ | const uint32_t *src = (const uint32_t *)(frame->data[ch] + (y * s->scanline_height + l) * frame->linesize[ch]); | |
324 | |||
325 | ✗ | for (int x = 0; x < frame->width; x++) | |
326 | ✗ | dst[x] = float2half(src[x], &s->f2h_tables); | |
327 | } | ||
328 | } | ||
329 | ✗ | break; | |
330 | } | ||
331 | |||
332 | 11934 | reorder_pixels(scanline->tmp, scanline->uncompressed_data, tmp_size); | |
333 | 11934 | predictor(scanline->tmp, tmp_size); | |
334 | 11934 | source_size = tmp_size; | |
335 | 11934 | actual_size = max_compressed_size; | |
336 | 11934 | compress(scanline->compressed_data, &actual_size, | |
337 | 11934 | scanline->tmp, source_size); | |
338 | |||
339 | 11934 | scanline->actual_size = actual_size; | |
340 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11934 times.
|
11934 | if (scanline->actual_size >= tmp_size) { |
341 | ✗ | FFSWAP(uint8_t *, scanline->uncompressed_data, scanline->compressed_data); | |
342 | ✗ | FFSWAP(int, scanline->uncompressed_size, scanline->compressed_size); | |
343 | ✗ | scanline->actual_size = tmp_size; | |
344 | } | ||
345 | } | ||
346 | |||
347 | 78 | return 0; | |
348 | } | ||
349 | |||
350 | 156 | static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, | |
351 | const AVFrame *frame, int *got_packet) | ||
352 | { | ||
353 | 156 | EXRContext *s = avctx->priv_data; | |
354 | 156 | PutByteContext *pb = &s->pb; | |
355 | int64_t offset; | ||
356 | int ret; | ||
357 | 312 | int64_t out_size = 2048LL + avctx->height * 16LL + | |
358 | 156 | av_image_get_buffer_size(avctx->pix_fmt, | |
359 | avctx->width, | ||
360 | 156 | avctx->height, 64) * 3LL / 2; | |
361 | |||
362 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 156 times.
|
156 | if ((ret = ff_get_encode_buffer(avctx, pkt, out_size, 0)) < 0) |
363 | ✗ | return ret; | |
364 | |||
365 | 156 | bytestream2_init_writer(pb, pkt->data, pkt->size); | |
366 | |||
367 | 156 | bytestream2_put_le32(pb, 20000630); | |
368 | 156 | bytestream2_put_byte(pb, 2); | |
369 | 156 | bytestream2_put_le24(pb, 0); | |
370 | 156 | bytestream2_put_buffer(pb, "channels\0chlist\0", 16); | |
371 | 156 | bytestream2_put_le32(pb, s->planes * 18 + 1); | |
372 | |||
373 |
2/2✓ Branch 0 taken 416 times.
✓ Branch 1 taken 156 times.
|
572 | for (int p = 0; p < s->planes; p++) { |
374 | 416 | bytestream2_put_byte(pb, s->ch_names[p]); | |
375 | 416 | bytestream2_put_byte(pb, 0); | |
376 | 416 | bytestream2_put_le32(pb, s->pixel_type); | |
377 | 416 | bytestream2_put_le32(pb, 0); | |
378 | 416 | bytestream2_put_le32(pb, 1); | |
379 | 416 | bytestream2_put_le32(pb, 1); | |
380 | } | ||
381 | 156 | bytestream2_put_byte(pb, 0); | |
382 | |||
383 | 156 | bytestream2_put_buffer(pb, "compression\0compression\0", 24); | |
384 | 156 | bytestream2_put_le32(pb, 1); | |
385 | 156 | bytestream2_put_byte(pb, s->compression); | |
386 | |||
387 | 156 | bytestream2_put_buffer(pb, "dataWindow\0box2i\0", 17); | |
388 | 156 | bytestream2_put_le32(pb, 16); | |
389 | 156 | bytestream2_put_le32(pb, 0); | |
390 | 156 | bytestream2_put_le32(pb, 0); | |
391 | 156 | bytestream2_put_le32(pb, avctx->width - 1); | |
392 | 156 | bytestream2_put_le32(pb, avctx->height - 1); | |
393 | |||
394 | 156 | bytestream2_put_buffer(pb, "displayWindow\0box2i\0", 20); | |
395 | 156 | bytestream2_put_le32(pb, 16); | |
396 | 156 | bytestream2_put_le32(pb, 0); | |
397 | 156 | bytestream2_put_le32(pb, 0); | |
398 | 156 | bytestream2_put_le32(pb, avctx->width - 1); | |
399 | 156 | bytestream2_put_le32(pb, avctx->height - 1); | |
400 | |||
401 | 156 | bytestream2_put_buffer(pb, "lineOrder\0lineOrder\0", 20); | |
402 | 156 | bytestream2_put_le32(pb, 1); | |
403 | 156 | bytestream2_put_byte(pb, 0); | |
404 | |||
405 | 156 | bytestream2_put_buffer(pb, "screenWindowCenter\0v2f\0", 23); | |
406 | 156 | bytestream2_put_le32(pb, 8); | |
407 | 156 | bytestream2_put_le64(pb, 0); | |
408 | |||
409 | 156 | bytestream2_put_buffer(pb, "screenWindowWidth\0float\0", 24); | |
410 | 156 | bytestream2_put_le32(pb, 4); | |
411 | 156 | bytestream2_put_le32(pb, av_float2int(1.f)); | |
412 | |||
413 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
156 | if (avctx->sample_aspect_ratio.num && avctx->sample_aspect_ratio.den) { |
414 | ✗ | bytestream2_put_buffer(pb, "pixelAspectRatio\0float\0", 23); | |
415 | ✗ | bytestream2_put_le32(pb, 4); | |
416 | ✗ | bytestream2_put_le32(pb, av_float2int(av_q2d(avctx->sample_aspect_ratio))); | |
417 | } | ||
418 | |||
419 |
2/4✓ Branch 0 taken 156 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 156 times.
✗ Branch 3 not taken.
|
156 | if (avctx->framerate.num && avctx->framerate.den) { |
420 | 156 | bytestream2_put_buffer(pb, "framesPerSecond\0rational\0", 25); | |
421 | 156 | bytestream2_put_le32(pb, 8); | |
422 | 156 | bytestream2_put_le32(pb, avctx->framerate.num); | |
423 | 156 | bytestream2_put_le32(pb, avctx->framerate.den); | |
424 | } | ||
425 | |||
426 | 156 | bytestream2_put_buffer(pb, "gamma\0float\0", 12); | |
427 | 156 | bytestream2_put_le32(pb, 4); | |
428 | 156 | bytestream2_put_le32(pb, av_float2int(s->gamma)); | |
429 | |||
430 | 156 | bytestream2_put_buffer(pb, "writer\0string\0", 14); | |
431 | 156 | bytestream2_put_le32(pb, 4); | |
432 | 156 | bytestream2_put_buffer(pb, "lavc", 4); | |
433 | 156 | bytestream2_put_byte(pb, 0); | |
434 | |||
435 |
3/4✓ Branch 0 taken 39 times.
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
|
156 | switch (s->compression) { |
436 | 39 | case EXR_RAW: | |
437 | /* nothing to do */ | ||
438 | 39 | break; | |
439 | 39 | case EXR_RLE: | |
440 | 39 | encode_scanline_rle(s, frame); | |
441 | 39 | break; | |
442 | 78 | case EXR_ZIP16: | |
443 | case EXR_ZIP1: | ||
444 | 78 | encode_scanline_zip(s, frame); | |
445 | 78 | break; | |
446 | ✗ | default: | |
447 | ✗ | av_assert0(0); | |
448 | } | ||
449 | |||
450 |
2/3✓ Branch 0 taken 39 times.
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
|
156 | switch (s->compression) { |
451 | 39 | case EXR_RAW: | |
452 | 39 | offset = bytestream2_tell_p(pb) + avctx->height * 8LL; | |
453 | |||
454 |
1/2✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
|
39 | if (s->pixel_type == EXR_FLOAT) { |
455 | |||
456 |
2/2✓ Branch 0 taken 11232 times.
✓ Branch 1 taken 39 times.
|
11271 | for (int y = 0; y < avctx->height; y++) { |
457 | 11232 | bytestream2_put_le64(pb, offset); | |
458 | 11232 | offset += avctx->width * s->planes * 4 + 8; | |
459 | } | ||
460 | |||
461 |
2/2✓ Branch 0 taken 11232 times.
✓ Branch 1 taken 39 times.
|
11271 | for (int y = 0; y < avctx->height; y++) { |
462 | 11232 | bytestream2_put_le32(pb, y); | |
463 | 11232 | bytestream2_put_le32(pb, s->planes * avctx->width * 4); | |
464 |
2/2✓ Branch 0 taken 29952 times.
✓ Branch 1 taken 11232 times.
|
41184 | for (int p = 0; p < s->planes; p++) { |
465 | 29952 | int ch = s->ch_order[p]; | |
466 | 29952 | bytestream2_put_buffer(pb, frame->data[ch] + y * frame->linesize[ch], | |
467 | 29952 | avctx->width * 4); | |
468 | } | ||
469 | } | ||
470 | } else { | ||
471 | ✗ | for (int y = 0; y < avctx->height; y++) { | |
472 | ✗ | bytestream2_put_le64(pb, offset); | |
473 | ✗ | offset += avctx->width * s->planes * 2 + 8; | |
474 | } | ||
475 | |||
476 | ✗ | for (int y = 0; y < avctx->height; y++) { | |
477 | ✗ | bytestream2_put_le32(pb, y); | |
478 | ✗ | bytestream2_put_le32(pb, s->planes * avctx->width * 2); | |
479 | ✗ | for (int p = 0; p < s->planes; p++) { | |
480 | ✗ | int ch = s->ch_order[p]; | |
481 | ✗ | const uint32_t *src = (const uint32_t *)(frame->data[ch] + y * frame->linesize[ch]); | |
482 | |||
483 | ✗ | for (int x = 0; x < frame->width; x++) | |
484 | ✗ | bytestream2_put_le16(pb, float2half(src[x], &s->f2h_tables)); | |
485 | } | ||
486 | } | ||
487 | } | ||
488 | 39 | break; | |
489 | 117 | case EXR_ZIP16: | |
490 | case EXR_ZIP1: | ||
491 | case EXR_RLE: | ||
492 | 117 | offset = bytestream2_tell_p(pb) + s->nb_scanlines * 8LL; | |
493 | |||
494 |
2/2✓ Branch 0 taken 23166 times.
✓ Branch 1 taken 117 times.
|
23283 | for (int y = 0; y < s->nb_scanlines; y++) { |
495 | 23166 | EXRScanlineData *scanline = &s->scanline[y]; | |
496 | |||
497 | 23166 | bytestream2_put_le64(pb, offset); | |
498 | 23166 | offset += scanline->actual_size + 8; | |
499 | } | ||
500 | |||
501 |
2/2✓ Branch 0 taken 23166 times.
✓ Branch 1 taken 117 times.
|
23283 | for (int y = 0; y < s->nb_scanlines; y++) { |
502 | 23166 | EXRScanlineData *scanline = &s->scanline[y]; | |
503 | |||
504 | 23166 | bytestream2_put_le32(pb, y * s->scanline_height); | |
505 | 23166 | bytestream2_put_le32(pb, scanline->actual_size); | |
506 | 23166 | bytestream2_put_buffer(pb, scanline->compressed_data, | |
507 | 23166 | scanline->actual_size); | |
508 | } | ||
509 | 117 | break; | |
510 | ✗ | default: | |
511 | ✗ | av_assert0(0); | |
512 | } | ||
513 | |||
514 | 156 | av_shrink_packet(pkt, bytestream2_tell_p(pb)); | |
515 | |||
516 | 156 | *got_packet = 1; | |
517 | |||
518 | 156 | return 0; | |
519 | } | ||
520 | |||
521 | #define OFFSET(x) offsetof(EXRContext, x) | ||
522 | #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM | ||
523 | static const AVOption options[] = { | ||
524 | { "compression", "set compression type", OFFSET(compression), AV_OPT_TYPE_INT, {.i64=0}, 0, EXR_NBCOMPR-1, VE, .unit = "compr" }, | ||
525 | { "none", "none", 0, AV_OPT_TYPE_CONST, {.i64=EXR_RAW}, 0, 0, VE, .unit = "compr" }, | ||
526 | { "rle" , "RLE", 0, AV_OPT_TYPE_CONST, {.i64=EXR_RLE}, 0, 0, VE, .unit = "compr" }, | ||
527 | { "zip1", "ZIP1", 0, AV_OPT_TYPE_CONST, {.i64=EXR_ZIP1}, 0, 0, VE, .unit = "compr" }, | ||
528 | { "zip16", "ZIP16", 0, AV_OPT_TYPE_CONST, {.i64=EXR_ZIP16}, 0, 0, VE, .unit = "compr" }, | ||
529 | { "format", "set pixel type", OFFSET(pixel_type), AV_OPT_TYPE_INT, {.i64=EXR_FLOAT}, EXR_HALF, EXR_UNKNOWN-1, VE, .unit = "pixel" }, | ||
530 | { "half" , NULL, 0, AV_OPT_TYPE_CONST, {.i64=EXR_HALF}, 0, 0, VE, .unit = "pixel" }, | ||
531 | { "float", NULL, 0, AV_OPT_TYPE_CONST, {.i64=EXR_FLOAT}, 0, 0, VE, .unit = "pixel" }, | ||
532 | { "gamma", "set gamma", OFFSET(gamma), AV_OPT_TYPE_FLOAT, {.dbl=1.f}, 0.001, FLT_MAX, VE }, | ||
533 | { NULL}, | ||
534 | }; | ||
535 | |||
536 | static const AVClass exr_class = { | ||
537 | .class_name = "exr", | ||
538 | .item_name = av_default_item_name, | ||
539 | .option = options, | ||
540 | .version = LIBAVUTIL_VERSION_INT, | ||
541 | }; | ||
542 | |||
543 | const FFCodec ff_exr_encoder = { | ||
544 | .p.name = "exr", | ||
545 | CODEC_LONG_NAME("OpenEXR image"), | ||
546 | .priv_data_size = sizeof(EXRContext), | ||
547 | .p.priv_class = &exr_class, | ||
548 | .p.type = AVMEDIA_TYPE_VIDEO, | ||
549 | .p.id = AV_CODEC_ID_EXR, | ||
550 | .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | | ||
551 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, | ||
552 | .init = encode_init, | ||
553 | FF_CODEC_ENCODE_CB(encode_frame), | ||
554 | .close = encode_close, | ||
555 | CODEC_PIXFMTS(AV_PIX_FMT_GRAYF32, AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32), | ||
556 | }; | ||
557 |