| 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 | .alpha_modes = AVALPHA_MODE_PREMULTIPLIED, | ||
| 557 | }; | ||
| 558 |