/* * Copyright (c) 2016, Alliance for Open Media. All rights reserved. * * This source code is subject to the terms of the BSD 2 Clause License and * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License * was not distributed with this source code in the LICENSE file, you can * obtain it at www.aomedia.org/license/software. If the Alliance for Open * Media Patent License 1.0 was not distributed with this source code in the * PATENTS file, you can obtain it at www.aomedia.org/license/patent. * * Based on code from the OggTheora software codec source code, * Copyright (C) 2002-2010 The Xiph.Org Foundation and contributors.
*/ #include <assert.h> #include <errno.h> #include <stdlib.h> #include <string.h>
#include"aom/aom_integer.h" #include"y4minput.h"
// Reads 'size' bytes from 'file' into 'buf' with some fault tolerance. // Returns true on success. staticint file_read(void *buf, size_t size, FILE *file) { constint kMaxTries = 5; int try_count = 0; int file_error = 0;
size_t len = 0; while (!feof(file) && len < size && try_count < kMaxTries) { const size_t n = fread((uint8_t *)buf + len, 1, size - len, file);
++try_count;
len += n;
file_error = ferror(file); if (file_error) { if (errno == EINTR || errno == EAGAIN) {
clearerr(file); continue;
} else {
fprintf(stderr, "Error reading file: %u of %u bytes read, %d: %s\n",
(uint32_t)len, (uint32_t)size, errno, strerror(errno)); return 0;
}
}
}
if (!feof(file) && len != size) {
fprintf(stderr, "Error reading file: %u of %u bytes read," " error: %d, tries: %d, %d: %s\n",
(uint32_t)len, (uint32_t)size, file_error, try_count, errno,
strerror(errno));
} return len == size;
}
// Stores the color range in 'y4m_ctx', returning 1 if successfully parsed, // 0 otherwise. staticint parse_color_range(y4m_input *y4m_ctx, constchar *buf) { // Note that default is studio range. if (strcmp(buf, "LIMITED") == 0) { return 1;
} if (strcmp(buf, "FULL") == 0) {
y4m_ctx->color_range = AOM_CR_FULL_RANGE; return 1;
}
fprintf(stderr, "Unknown color range value: %s\n", buf); return 0;
}
staticint parse_metadata(y4m_input *y4m_ctx, constchar *buf) { if (strncmp(buf, "COLORRANGE=", 11) == 0) { return parse_color_range(y4m_ctx, buf + 11);
} return 1; // No support for other metadata, just ignore them.
}
staticint y4m_parse_tags(y4m_input *_y4m, char *_tags) { char *p; char *q; for (p = _tags;; p = q) { /*Skip any leading spaces.*/ while (*p == ' ') p++; /*If that's all we have, stop.*/ if (p[0] == '\0') break; /*Find the end of this tag.*/ for (q = p + 1; *q != '\0' && *q != ' '; q++) {
} /*Process the tag.*/ switch (p[0]) { case'W': { if (sscanf(p + 1, "%d", &_y4m->pic_w) != 1) return -1;
} break; case'H': { if (sscanf(p + 1, "%d", &_y4m->pic_h) != 1) return -1;
} break; case'F': { if (sscanf(p + 1, "%d:%d", &_y4m->fps_n, &_y4m->fps_d) != 2) { return -1;
}
} break; case'I': {
_y4m->interlace = p[1];
} break; case'A': { if (sscanf(p + 1, "%d:%d", &_y4m->par_n, &_y4m->par_d) != 2) { return -1;
}
} break; case'C': { if (q - p > 16) return -1;
memcpy(_y4m->chroma_type, p + 1, q - p - 1);
_y4m->chroma_type[q - p - 1] = '\0';
} break; case'X': { if (!parse_metadata(_y4m, p + 1)) return -1;
} break; default: break; /*Ignore unknown tags.*/
}
} return 0;
}
// Copy a single tag into the buffer, along with a null character. // Returns 0 if any file IO errors occur. staticint copy_tag(char *buf, size_t buf_len, char *end_tag, FILE *file) {
size_t i;
assert(buf_len >= 1); // Skip leading space characters. do { if (!file_read(buf, 1, file)) { return 0;
}
} while (buf[0] == ' ');
// If we hit the newline, treat this as the "empty" tag. if (buf[0] == '\n') {
buf[0] = '\0';
*end_tag = '\n'; return 1;
}
// Copy over characters until a space is hit, or the buffer is exhausted. for (i = 1; i < buf_len; ++i) { if (!file_read(buf + i, 1, file)) { return 0;
} if (buf[i] == ' ' || buf[i] == '\n') { break;
}
} if (i == buf_len) {
fprintf(stderr, "Error: Y4M header tags must be less than %lu characters\n",
(unsignedlong)i); return 0;
}
*end_tag = buf[i];
buf[i] = '\0'; return 1;
}
// Returns 1 if tags were parsed successfully, 0 otherwise. staticint parse_tags(y4m_input *y4m_ctx, FILE *file) { char tag[256]; char end; // Character denoting the end of the tag, ' ' or '\n'. // Set Y4M tags to defaults, updating them as processing occurs. Mandatory // fields are marked with -1 and will be checked after the tags are parsed.
y4m_ctx->pic_w = -1;
y4m_ctx->pic_h = -1;
y4m_ctx->fps_n = -1; // Also serves as marker for fps_d
y4m_ctx->par_n = 0;
y4m_ctx->par_d = 0;
y4m_ctx->interlace = '?';
y4m_ctx->color_range = AOM_CR_STUDIO_RANGE;
snprintf(y4m_ctx->chroma_type, sizeof(y4m_ctx->chroma_type), "420");
// Find one tag at a time. do { if (!copy_tag(tag, sizeof(tag), &end, file)) { return 0;
} // y4m_parse_tags returns 0 on success. if (y4m_parse_tags(y4m_ctx, tag)) { return 0;
}
} while (end != '\n');
// Check the mandatory fields. if (y4m_ctx->pic_w == -1) {
fprintf(stderr, "Width field missing\n"); return 0;
} if (y4m_ctx->pic_h == -1) {
fprintf(stderr, "Height field missing\n"); return 0;
} if (y4m_ctx->fps_n == -1) {
fprintf(stderr, "FPS field missing\n"); return 0;
} return 1;
}
/*All anti-aliasing filters in the following conversion functions are based on one of two window functions: The 6-tap Lanczos window (for down-sampling and shifts): sinc(\pi*t)*sinc(\pi*t/3), |t|<3 (sinc(t)==sin(t)/t) 0, |t|>=3 The 4-tap Mitchell window (for up-sampling): 7|t|^3-12|t|^2+16/3, |t|<1 -(7/3)|x|^3+12|x|^2-20|x|+32/3, |t|<2 0, |t|>=2 The number of taps is intentionally kept small to reduce computational overhead and limit ringing.
The taps from these filters are scaled so that their sum is 1, and the result is scaled by 128 and rounded to integers to create a filter whose intermediate values fit inside 16 bits. Coefficients are rounded in such a way as to ensure their sum is still 128, which is usually equivalent to normal rounding.
Conversions which require both horizontal and vertical filtering could have these steps pipelined, for less memory consumption and better cache
performance, but we do them separately for simplicity.*/ #define OC_MINI(_a, _b) ((_a) > (_b) ? (_b) : (_a)) #define OC_MAXI(_a, _b) ((_a) < (_b) ? (_b) : (_a)) #define OC_CLAMPI(_a, _b, _c) (OC_MAXI(_a, OC_MINI(_b, _c)))
We use a resampling filter to shift the original site locations one quarter pixel (at the original chroma resolution) to the right. Then we use a second resampling filter to decimate the chroma planes by two
in the vertical direction.*/ staticvoid y4m_convert_422_420jpeg(y4m_input *_y4m, unsignedchar *_dst, unsignedchar *_aux) { unsignedchar *tmp; int c_w; int c_h; int c_sz; int dst_c_h; int dst_c_sz; int pli; /*Skip past the luma data.*/
_dst += _y4m->pic_w * _y4m->pic_h; /*Compute the size of each chroma plane.*/
c_w = (_y4m->pic_w + _y4m->src_c_dec_h - 1) / _y4m->src_c_dec_h;
c_h = _y4m->pic_h;
dst_c_h = (_y4m->pic_h + _y4m->dst_c_dec_v - 1) / _y4m->dst_c_dec_v;
c_sz = c_w * c_h;
dst_c_sz = c_w * dst_c_h;
tmp = _aux + 2 * c_sz; for (pli = 1; pli < 3; pli++) { /*In reality, the horizontal and vertical steps could be pipelined, for less memory consumption and better cache performance, but we do them
separately for simplicity.*/ /*First do horizontal filtering (convert to 422jpeg)*/
y4m_42xmpeg2_42xjpeg_helper(tmp, _aux, c_w, c_h); /*Now do the vertical filtering.*/
y4m_422jpeg_420jpeg_helper(_dst, tmp, c_w, c_h);
_aux += c_sz;
_dst += dst_c_sz;
}
}
int y4m_input_fetch_frame(y4m_input *_y4m, FILE *_fin, aom_image_t *_img) { char frame[6]; int pic_sz; int c_w; int c_h; int c_sz; int bytes_per_sample = _y4m->bit_depth > 8 ? 2 : 1; /*Read and skip the frame header.*/ if (!file_read(frame, 6, _fin)) return 0; if (memcmp(frame, "FRAME", 5)) {
fprintf(stderr, "Loss of framing in Y4M input data\n"); return -1;
} if (frame[5] != '\n') { char c; int j; for (j = 0; j < 79 && file_read(&c, 1, _fin) && c != '\n'; j++) {
} if (j == 79) {
fprintf(stderr, "Error parsing Y4M frame header\n"); return -1;
}
} /*Read the frame data that needs no conversion.*/ if (!file_read(_y4m->dst_buf, _y4m->dst_buf_read_sz, _fin)) {
fprintf(stderr, "Error reading Y4M frame data.\n"); return -1;
} /*Read the frame data that does need conversion.*/ if (!file_read(_y4m->aux_buf, _y4m->aux_buf_read_sz, _fin)) {
fprintf(stderr, "Error reading Y4M frame data.\n"); return -1;
} /*Now convert the just read frame.*/
(*_y4m->convert)(_y4m, _y4m->dst_buf, _y4m->aux_buf); /*Fill in the frame buffer pointers. We don't use aom_img_wrap() because it forces padding for odd picture
sizes, which would require a separate fread call for every row.*/
memset(_img, 0, sizeof(*_img)); /*Y4M has the planes in Y'CbCr order, which libaom calls Y, U, and V.*/
_img->fmt = _y4m->aom_fmt;
_img->w = _img->d_w = _y4m->pic_w;
_img->h = _img->d_h = _y4m->pic_h;
_img->bit_depth = _y4m->bit_depth;
_img->x_chroma_shift = _y4m->dst_c_dec_h >> 1;
_img->y_chroma_shift = _y4m->dst_c_dec_v >> 1;
_img->bps = _y4m->bps;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.