tools_common.c (20198B)
1 /* 2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved. 3 * 4 * This source code is subject to the terms of the BSD 2 Clause License and 5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License 6 * was not distributed with this source code in the LICENSE file, you can 7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open 8 * Media Patent License 1.0 was not distributed with this source code in the 9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent. 10 */ 11 12 #include <assert.h> 13 #include <math.h> 14 #include <stdarg.h> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 19 #include "common/tools_common.h" 20 21 #if CONFIG_AV1_ENCODER 22 #include "aom/aomcx.h" 23 #endif 24 25 #if CONFIG_AV1_DECODER 26 #include "aom/aomdx.h" 27 #endif 28 29 #if defined(_WIN32) 30 #include <io.h> 31 #include <fcntl.h> 32 #endif 33 34 #define LOG_ERROR(label) \ 35 do { \ 36 const char *l = label; \ 37 va_list ap; \ 38 va_start(ap, fmt); \ 39 if (l) fprintf(stderr, "%s: ", l); \ 40 vfprintf(stderr, fmt, ap); \ 41 fprintf(stderr, "\n"); \ 42 va_end(ap); \ 43 } while (0) 44 45 FILE *set_binary_mode(FILE *stream) { 46 (void)stream; 47 #if defined(_WIN32) 48 _setmode(_fileno(stream), _O_BINARY); 49 #endif 50 return stream; 51 } 52 53 void die(const char *fmt, ...) { 54 LOG_ERROR(NULL); 55 usage_exit(); 56 } 57 58 void fatal(const char *fmt, ...) { 59 LOG_ERROR("Fatal"); 60 exit(EXIT_FAILURE); 61 } 62 63 void aom_tools_warn(const char *fmt, ...) { LOG_ERROR("Warning"); } 64 65 void die_codec(aom_codec_ctx_t *ctx, const char *s) { 66 const char *detail = aom_codec_error_detail(ctx); 67 68 fprintf(stderr, "%s: %s\n", s, aom_codec_error(ctx)); 69 if (detail) fprintf(stderr, " %s\n", detail); 70 exit(EXIT_FAILURE); 71 } 72 73 const char *image_format_to_string(aom_img_fmt_t fmt) { 74 switch (fmt) { 75 case AOM_IMG_FMT_I420: return "I420"; 76 case AOM_IMG_FMT_I422: return "I422"; 77 case AOM_IMG_FMT_I444: return "I444"; 78 case AOM_IMG_FMT_YV12: return "YV12"; 79 case AOM_IMG_FMT_NV12: return "NV12"; 80 case AOM_IMG_FMT_YV1216: return "YV1216"; 81 case AOM_IMG_FMT_I42016: return "I42016"; 82 case AOM_IMG_FMT_I42216: return "I42216"; 83 case AOM_IMG_FMT_I44416: return "I44416"; 84 default: return "Other"; 85 } 86 } 87 88 int read_yuv_frame(struct AvxInputContext *input_ctx, aom_image_t *yuv_frame) { 89 FILE *f = input_ctx->file; 90 struct FileTypeDetectionBuffer *detect = &input_ctx->detect; 91 int plane = 0; 92 int shortread = 0; 93 const int bytespp = (yuv_frame->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1; 94 95 for (plane = 0; plane < 3; ++plane) { 96 uint8_t *ptr; 97 int w = aom_img_plane_width(yuv_frame, plane); 98 const int h = aom_img_plane_height(yuv_frame, plane); 99 int r; 100 // Assuming that for nv12 we read all chroma data at once 101 if (yuv_frame->fmt == AOM_IMG_FMT_NV12 && plane > 1) break; 102 if (yuv_frame->fmt == AOM_IMG_FMT_NV12 && plane == 1) w *= 2; 103 /* Determine the correct plane based on the image format. The for-loop 104 * always counts in Y,U,V order, but this may not match the order of 105 * the data on disk. 106 */ 107 switch (plane) { 108 case 1: 109 ptr = 110 yuv_frame->planes[yuv_frame->fmt == AOM_IMG_FMT_YV12 ? AOM_PLANE_V 111 : AOM_PLANE_U]; 112 break; 113 case 2: 114 ptr = 115 yuv_frame->planes[yuv_frame->fmt == AOM_IMG_FMT_YV12 ? AOM_PLANE_U 116 : AOM_PLANE_V]; 117 break; 118 default: ptr = yuv_frame->planes[plane]; 119 } 120 121 for (r = 0; r < h; ++r) { 122 size_t needed = w * bytespp; 123 size_t buf_position = 0; 124 const size_t left = detect->buf_read - detect->position; 125 if (left > 0) { 126 const size_t more = (left < needed) ? left : needed; 127 memcpy(ptr, detect->buf + detect->position, more); 128 buf_position = more; 129 needed -= more; 130 detect->position += more; 131 } 132 if (needed > 0) { 133 shortread |= (fread(ptr + buf_position, 1, needed, f) < needed); 134 } 135 136 ptr += yuv_frame->stride[plane]; 137 } 138 } 139 140 return shortread; 141 } 142 143 struct CodecInfo { 144 // Pointer to a function of zero arguments that returns an aom_codec_iface_t. 145 aom_codec_iface_t *(*interface)(void); 146 const char *short_name; 147 uint32_t fourcc; 148 }; 149 150 #if CONFIG_AV1_ENCODER 151 static const struct CodecInfo aom_encoders[] = { 152 { &aom_codec_av1_cx, "av1", AV1_FOURCC }, 153 }; 154 155 int get_aom_encoder_count(void) { 156 return sizeof(aom_encoders) / sizeof(aom_encoders[0]); 157 } 158 159 aom_codec_iface_t *get_aom_encoder_by_index(int i) { 160 assert(i >= 0 && i < get_aom_encoder_count()); 161 return aom_encoders[i].interface(); 162 } 163 164 aom_codec_iface_t *get_aom_encoder_by_short_name(const char *name) { 165 for (int i = 0; i < get_aom_encoder_count(); ++i) { 166 const struct CodecInfo *info = &aom_encoders[i]; 167 if (strcmp(info->short_name, name) == 0) return info->interface(); 168 } 169 return NULL; 170 } 171 172 uint32_t get_fourcc_by_aom_encoder(aom_codec_iface_t *iface) { 173 for (int i = 0; i < get_aom_encoder_count(); ++i) { 174 const struct CodecInfo *info = &aom_encoders[i]; 175 if (info->interface() == iface) { 176 return info->fourcc; 177 } 178 } 179 return 0; 180 } 181 182 const char *get_short_name_by_aom_encoder(aom_codec_iface_t *iface) { 183 for (int i = 0; i < get_aom_encoder_count(); ++i) { 184 const struct CodecInfo *info = &aom_encoders[i]; 185 if (info->interface() == iface) { 186 return info->short_name; 187 } 188 } 189 return NULL; 190 } 191 192 #endif // CONFIG_AV1_ENCODER 193 194 #if CONFIG_AV1_DECODER 195 static const struct CodecInfo aom_decoders[] = { 196 { &aom_codec_av1_dx, "av1", AV1_FOURCC }, 197 }; 198 199 int get_aom_decoder_count(void) { 200 return sizeof(aom_decoders) / sizeof(aom_decoders[0]); 201 } 202 203 aom_codec_iface_t *get_aom_decoder_by_index(int i) { 204 assert(i >= 0 && i < get_aom_decoder_count()); 205 return aom_decoders[i].interface(); 206 } 207 208 aom_codec_iface_t *get_aom_decoder_by_short_name(const char *name) { 209 for (int i = 0; i < get_aom_decoder_count(); ++i) { 210 const struct CodecInfo *info = &aom_decoders[i]; 211 if (strcmp(info->short_name, name) == 0) return info->interface(); 212 } 213 return NULL; 214 } 215 216 aom_codec_iface_t *get_aom_decoder_by_fourcc(uint32_t fourcc) { 217 for (int i = 0; i < get_aom_decoder_count(); ++i) { 218 const struct CodecInfo *info = &aom_decoders[i]; 219 if (info->fourcc == fourcc) return info->interface(); 220 } 221 return NULL; 222 } 223 224 const char *get_short_name_by_aom_decoder(aom_codec_iface_t *iface) { 225 for (int i = 0; i < get_aom_decoder_count(); ++i) { 226 const struct CodecInfo *info = &aom_decoders[i]; 227 if (info->interface() == iface) { 228 return info->short_name; 229 } 230 } 231 return NULL; 232 } 233 234 uint32_t get_fourcc_by_aom_decoder(aom_codec_iface_t *iface) { 235 for (int i = 0; i < get_aom_decoder_count(); ++i) { 236 const struct CodecInfo *info = &aom_decoders[i]; 237 if (info->interface() == iface) { 238 return info->fourcc; 239 } 240 } 241 return 0; 242 } 243 244 #endif // CONFIG_AV1_DECODER 245 246 void aom_img_write(const aom_image_t *img, FILE *file) { 247 int plane; 248 const int bytespp = (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1; 249 250 for (plane = 0; plane < 3; ++plane) { 251 const unsigned char *buf = img->planes[plane]; 252 const int stride = img->stride[plane]; 253 int w = aom_img_plane_width(img, plane); 254 const int h = aom_img_plane_height(img, plane); 255 int y; 256 257 // Assuming that for nv12 we write all chroma data at once 258 if (img->fmt == AOM_IMG_FMT_NV12 && plane > 1) break; 259 if (img->fmt == AOM_IMG_FMT_NV12 && plane == 1) w *= 2; 260 261 for (y = 0; y < h; ++y) { 262 fwrite(buf, bytespp, w, file); 263 buf += stride; 264 } 265 } 266 } 267 268 bool aom_img_read(aom_image_t *img, FILE *file) { 269 int plane; 270 const int bytespp = (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1; 271 272 for (plane = 0; plane < 3; ++plane) { 273 unsigned char *buf = img->planes[plane]; 274 const int stride = img->stride[plane]; 275 int w = aom_img_plane_width(img, plane); 276 const int h = aom_img_plane_height(img, plane); 277 int y; 278 279 // Assuming that for nv12 we read all chroma data at once 280 if (img->fmt == AOM_IMG_FMT_NV12 && plane > 1) break; 281 if (img->fmt == AOM_IMG_FMT_NV12 && plane == 1) w *= 2; 282 283 for (y = 0; y < h; ++y) { 284 if (fread(buf, bytespp, w, file) != (size_t)w) return false; 285 buf += stride; 286 } 287 } 288 289 return true; 290 } 291 292 // TODO(dkovalev) change sse_to_psnr signature: double -> int64_t 293 double sse_to_psnr(double samples, double peak, double sse) { 294 static const double kMaxPSNR = 100.0; 295 296 if (sse > 0.0) { 297 const double psnr = 10.0 * log10(samples * peak * peak / sse); 298 return psnr > kMaxPSNR ? kMaxPSNR : psnr; 299 } else { 300 return kMaxPSNR; 301 } 302 } 303 304 // TODO(debargha): Consolidate the functions below into a separate file. 305 static void highbd_img_upshift(aom_image_t *dst, const aom_image_t *src, 306 int input_shift) { 307 // Note the offset is 1 less than half. 308 const int offset = input_shift > 0 ? (1 << (input_shift - 1)) - 1 : 0; 309 int plane; 310 if (dst->d_w != src->d_w || dst->d_h != src->d_h || 311 dst->x_chroma_shift != src->x_chroma_shift || 312 dst->y_chroma_shift != src->y_chroma_shift || dst->fmt != src->fmt || 313 input_shift < 0) { 314 fatal("Unsupported image conversion"); 315 } 316 switch (src->fmt) { 317 case AOM_IMG_FMT_I42016: 318 case AOM_IMG_FMT_I42216: 319 case AOM_IMG_FMT_I44416: break; 320 default: fatal("Unsupported image conversion"); 321 } 322 for (plane = 0; plane < 3; plane++) { 323 int w = src->d_w; 324 int h = src->d_h; 325 int x, y; 326 if (plane) { 327 w = (w + src->x_chroma_shift) >> src->x_chroma_shift; 328 h = (h + src->y_chroma_shift) >> src->y_chroma_shift; 329 } 330 for (y = 0; y < h; y++) { 331 const uint16_t *p_src = 332 (const uint16_t *)(src->planes[plane] + y * src->stride[plane]); 333 uint16_t *p_dst = 334 (uint16_t *)(dst->planes[plane] + y * dst->stride[plane]); 335 for (x = 0; x < w; x++) *p_dst++ = (*p_src++ << input_shift) + offset; 336 } 337 } 338 } 339 340 static void lowbd_img_upshift(aom_image_t *dst, const aom_image_t *src, 341 int input_shift) { 342 // Note the offset is 1 less than half. 343 const int offset = input_shift > 0 ? (1 << (input_shift - 1)) - 1 : 0; 344 int plane; 345 if (dst->d_w != src->d_w || dst->d_h != src->d_h || 346 dst->x_chroma_shift != src->x_chroma_shift || 347 dst->y_chroma_shift != src->y_chroma_shift || 348 dst->fmt != src->fmt + AOM_IMG_FMT_HIGHBITDEPTH || input_shift < 0) { 349 fatal("Unsupported image conversion"); 350 } 351 switch (src->fmt) { 352 case AOM_IMG_FMT_YV12: 353 case AOM_IMG_FMT_I420: 354 case AOM_IMG_FMT_I422: 355 case AOM_IMG_FMT_I444: break; 356 default: fatal("Unsupported image conversion"); 357 } 358 for (plane = 0; plane < 3; plane++) { 359 int w = src->d_w; 360 int h = src->d_h; 361 int x, y; 362 if (plane) { 363 w = (w + src->x_chroma_shift) >> src->x_chroma_shift; 364 h = (h + src->y_chroma_shift) >> src->y_chroma_shift; 365 } 366 for (y = 0; y < h; y++) { 367 const uint8_t *p_src = src->planes[plane] + y * src->stride[plane]; 368 uint16_t *p_dst = 369 (uint16_t *)(dst->planes[plane] + y * dst->stride[plane]); 370 for (x = 0; x < w; x++) { 371 *p_dst++ = (*p_src++ << input_shift) + offset; 372 } 373 } 374 } 375 } 376 377 void aom_img_upshift(aom_image_t *dst, const aom_image_t *src, 378 int input_shift) { 379 if (src->fmt & AOM_IMG_FMT_HIGHBITDEPTH) { 380 highbd_img_upshift(dst, src, input_shift); 381 } else { 382 lowbd_img_upshift(dst, src, input_shift); 383 } 384 } 385 386 void aom_img_truncate_16_to_8(aom_image_t *dst, const aom_image_t *src) { 387 int plane; 388 if (dst->fmt + AOM_IMG_FMT_HIGHBITDEPTH != src->fmt || dst->d_w != src->d_w || 389 dst->d_h != src->d_h || dst->x_chroma_shift != src->x_chroma_shift || 390 dst->y_chroma_shift != src->y_chroma_shift) { 391 fatal("Unsupported image conversion"); 392 } 393 switch (dst->fmt) { 394 case AOM_IMG_FMT_I420: 395 case AOM_IMG_FMT_I422: 396 case AOM_IMG_FMT_I444: break; 397 default: fatal("Unsupported image conversion"); 398 } 399 for (plane = 0; plane < 3; plane++) { 400 int w = src->d_w; 401 int h = src->d_h; 402 int x, y; 403 if (plane) { 404 w = (w + src->x_chroma_shift) >> src->x_chroma_shift; 405 h = (h + src->y_chroma_shift) >> src->y_chroma_shift; 406 } 407 for (y = 0; y < h; y++) { 408 const uint16_t *p_src = 409 (const uint16_t *)(src->planes[plane] + y * src->stride[plane]); 410 uint8_t *p_dst = dst->planes[plane] + y * dst->stride[plane]; 411 for (x = 0; x < w; x++) { 412 *p_dst++ = (uint8_t)(*p_src++); 413 } 414 } 415 } 416 } 417 418 static void highbd_img_downshift(aom_image_t *dst, const aom_image_t *src, 419 int down_shift) { 420 int plane; 421 if (dst->d_w != src->d_w || dst->d_h != src->d_h || 422 dst->x_chroma_shift != src->x_chroma_shift || 423 dst->y_chroma_shift != src->y_chroma_shift || dst->fmt != src->fmt || 424 down_shift < 0) { 425 fatal("Unsupported image conversion"); 426 } 427 switch (src->fmt) { 428 case AOM_IMG_FMT_I42016: 429 case AOM_IMG_FMT_I42216: 430 case AOM_IMG_FMT_I44416: break; 431 default: fatal("Unsupported image conversion"); 432 } 433 for (plane = 0; plane < 3; plane++) { 434 int w = src->d_w; 435 int h = src->d_h; 436 int x, y; 437 if (plane) { 438 w = (w + src->x_chroma_shift) >> src->x_chroma_shift; 439 h = (h + src->y_chroma_shift) >> src->y_chroma_shift; 440 } 441 for (y = 0; y < h; y++) { 442 const uint16_t *p_src = 443 (const uint16_t *)(src->planes[plane] + y * src->stride[plane]); 444 uint16_t *p_dst = 445 (uint16_t *)(dst->planes[plane] + y * dst->stride[plane]); 446 for (x = 0; x < w; x++) *p_dst++ = *p_src++ >> down_shift; 447 } 448 } 449 } 450 451 static void lowbd_img_downshift(aom_image_t *dst, const aom_image_t *src, 452 int down_shift) { 453 int plane; 454 if (dst->d_w != src->d_w || dst->d_h != src->d_h || 455 dst->x_chroma_shift != src->x_chroma_shift || 456 dst->y_chroma_shift != src->y_chroma_shift || 457 src->fmt != dst->fmt + AOM_IMG_FMT_HIGHBITDEPTH || down_shift < 0) { 458 fatal("Unsupported image conversion"); 459 } 460 switch (dst->fmt) { 461 case AOM_IMG_FMT_I420: 462 case AOM_IMG_FMT_I422: 463 case AOM_IMG_FMT_I444: break; 464 default: fatal("Unsupported image conversion"); 465 } 466 for (plane = 0; plane < 3; plane++) { 467 int w = src->d_w; 468 int h = src->d_h; 469 int x, y; 470 if (plane) { 471 w = (w + src->x_chroma_shift) >> src->x_chroma_shift; 472 h = (h + src->y_chroma_shift) >> src->y_chroma_shift; 473 } 474 for (y = 0; y < h; y++) { 475 const uint16_t *p_src = 476 (const uint16_t *)(src->planes[plane] + y * src->stride[plane]); 477 uint8_t *p_dst = dst->planes[plane] + y * dst->stride[plane]; 478 for (x = 0; x < w; x++) { 479 *p_dst++ = *p_src++ >> down_shift; 480 } 481 } 482 } 483 } 484 485 void aom_img_downshift(aom_image_t *dst, const aom_image_t *src, 486 int down_shift) { 487 if (dst->fmt & AOM_IMG_FMT_HIGHBITDEPTH) { 488 highbd_img_downshift(dst, src, down_shift); 489 } else { 490 lowbd_img_downshift(dst, src, down_shift); 491 } 492 } 493 494 static int img_shifted_realloc_required(const aom_image_t *img, 495 const aom_image_t *shifted, 496 aom_img_fmt_t required_fmt) { 497 return img->d_w != shifted->d_w || img->d_h != shifted->d_h || 498 required_fmt != shifted->fmt; 499 } 500 501 bool aom_shift_img(unsigned int output_bit_depth, aom_image_t **img_ptr, 502 aom_image_t **img_shifted_ptr) { 503 aom_image_t *img = *img_ptr; 504 aom_image_t *img_shifted = *img_shifted_ptr; 505 506 const aom_img_fmt_t shifted_fmt = output_bit_depth == 8 507 ? img->fmt & ~AOM_IMG_FMT_HIGHBITDEPTH 508 : img->fmt | AOM_IMG_FMT_HIGHBITDEPTH; 509 510 if (shifted_fmt != img->fmt || output_bit_depth != img->bit_depth) { 511 if (img_shifted && 512 img_shifted_realloc_required(img, img_shifted, shifted_fmt)) { 513 aom_img_free(img_shifted); 514 img_shifted = NULL; 515 } 516 if (img_shifted) { 517 img_shifted->monochrome = img->monochrome; 518 } 519 if (!img_shifted) { 520 img_shifted = aom_img_alloc(NULL, shifted_fmt, img->d_w, img->d_h, 16); 521 if (!img_shifted) { 522 *img_shifted_ptr = NULL; 523 return false; 524 } 525 img_shifted->bit_depth = output_bit_depth; 526 img_shifted->monochrome = img->monochrome; 527 img_shifted->csp = img->csp; 528 } 529 if (output_bit_depth > img->bit_depth) { 530 aom_img_upshift(img_shifted, img, output_bit_depth - img->bit_depth); 531 } else { 532 aom_img_downshift(img_shifted, img, img->bit_depth - output_bit_depth); 533 } 534 *img_shifted_ptr = img_shifted; 535 *img_ptr = img_shifted; 536 } 537 538 return true; 539 } 540 541 // Related to I420, NV12 format has one luma "luminance" plane Y and one plane 542 // with U and V values interleaved. 543 void aom_img_write_nv12(const aom_image_t *img, FILE *file) { 544 // Y plane 545 const unsigned char *buf = img->planes[0]; 546 int stride = img->stride[0]; 547 int w = aom_img_plane_width(img, 0) * 548 ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1); 549 int h = aom_img_plane_height(img, 0); 550 int x, y; 551 552 for (y = 0; y < h; ++y) { 553 fwrite(buf, 1, w, file); 554 buf += stride; 555 } 556 557 // Interleaved U and V plane 558 const unsigned char *ubuf = img->planes[1]; 559 const unsigned char *vbuf = img->planes[2]; 560 const size_t size = (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1; 561 stride = img->stride[1]; 562 w = aom_img_plane_width(img, 1); 563 h = aom_img_plane_height(img, 1); 564 565 for (y = 0; y < h; ++y) { 566 for (x = 0; x < w; ++x) { 567 fwrite(ubuf, size, 1, file); 568 fwrite(vbuf, size, 1, file); 569 ubuf += size; 570 vbuf += size; 571 } 572 ubuf += (stride - w * size); 573 vbuf += (stride - w * size); 574 } 575 } 576 577 size_t read_from_input(struct AvxInputContext *input_ctx, size_t n, 578 unsigned char *buf) { 579 const size_t buffered_bytes = 580 input_ctx->detect.buf_read - input_ctx->detect.position; 581 size_t read_n; 582 if (buffered_bytes == 0) { 583 read_n = fread(buf, 1, n, input_ctx->file); 584 } else if (n <= buffered_bytes) { 585 memcpy(buf, input_ctx->detect.buf + input_ctx->detect.position, n); 586 input_ctx->detect.position += n; 587 read_n = n; 588 } else { 589 memcpy(buf, input_ctx->detect.buf + input_ctx->detect.position, 590 buffered_bytes); 591 input_ctx->detect.position += buffered_bytes; 592 read_n = buffered_bytes; 593 read_n += 594 fread(buf + buffered_bytes, 1, n - buffered_bytes, input_ctx->file); 595 } 596 return read_n; 597 } 598 599 size_t input_to_detect_buf(struct AvxInputContext *input_ctx, size_t n) { 600 if (n + input_ctx->detect.position > DETECT_BUF_SZ) { 601 die("Failed to store in the detect buffer, maximum size exceeded."); 602 } 603 const size_t buffered_bytes = 604 input_ctx->detect.buf_read - input_ctx->detect.position; 605 size_t read_n; 606 if (buffered_bytes == 0) { 607 read_n = fread(input_ctx->detect.buf + input_ctx->detect.buf_read, 1, n, 608 input_ctx->file); 609 input_ctx->detect.buf_read += read_n; 610 } else if (n <= buffered_bytes) { 611 // In this case, don't need to do anything as the data is already in 612 // the detect buffer 613 read_n = n; 614 } else { 615 read_n = fread(input_ctx->detect.buf + input_ctx->detect.buf_read, 1, 616 n - buffered_bytes, input_ctx->file); 617 input_ctx->detect.buf_read += read_n; 618 read_n += buffered_bytes; 619 } 620 return read_n; 621 } 622 623 // Read from detect buffer to a buffer. If not enough, read from input and also 624 // buffer them first. 625 size_t buffer_input(struct AvxInputContext *input_ctx, size_t n, 626 unsigned char *buf, bool buffered) { 627 if (!buffered) { 628 return read_from_input(input_ctx, n, buf); 629 } 630 const size_t buf_n = input_to_detect_buf(input_ctx, n); 631 if (buf_n < n) { 632 return buf_n; 633 } 634 return read_from_input(input_ctx, n, buf); 635 } 636 637 void rewind_detect(struct AvxInputContext *input_ctx) { 638 input_ctx->detect.position = 0; 639 } 640 641 bool input_eof(struct AvxInputContext *input_ctx) { 642 return feof(input_ctx->file) && 643 input_ctx->detect.position == input_ctx->detect.buf_read; 644 }