uvg266/src/yuv_io.c
Ari Koivula 8ad7d2a714 Move interlacing stuff to libkvazaaar API
This moves the interlacing from CLI code to api->encoder_encode, in
order to make it possible to use field coding through the lib API.

The field order is now determined per frame, as FFmpeg gives it per
frame and it's signaled per frame.

As a side effect, the CLI also now prints info from frames instead of
fields. While we might want to extend the API in the future to allow
printing of more detailed information about fields, for now it's
more important that the CLI uses the real lib API.

PSNR calculation for interlaced frames disabled until we have a way to
avoid deinterlacing the frame when it's not necessary.
2016-01-27 15:29:45 +02:00

206 lines
6.6 KiB
C

/*****************************************************************************
* This file is part of Kvazaar HEVC encoder.
*
* Copyright (C) 2013-2015 Tampere University of Technology and others (see
* COPYING file).
*
* Kvazaar is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* Kvazaar is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with Kvazaar. If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/
/*
* \file
*/
#include <string.h>
#include <stdio.h>
#include "yuv_io.h"
static void fill_after_frame(unsigned height, unsigned array_width,
unsigned array_height, kvz_pixel *data)
{
kvz_pixel* p = data + height * array_width;
kvz_pixel* end = data + array_width * array_height;
while (p < end) {
// Fill the line by copying the line above.
memcpy(p, p - array_width, array_width);
p += array_width;
}
}
static int read_and_fill_frame_data(FILE *file,
unsigned width, unsigned height,
unsigned array_width, kvz_pixel *data)
{
kvz_pixel* p = data;
kvz_pixel* end = data + array_width * height;
kvz_pixel fill_char;
unsigned i;
while (p < end) {
// Read the beginning of the line from input.
if (width != fread(p, sizeof(unsigned char), width, file))
return 0;
// Fill the rest with the last pixel value.
fill_char = p[width - 1];
for (i = width; i < array_width; ++i) {
p[i] = fill_char;
}
p += array_width;
}
return 1;
}
/**
* \brief Convert 8 bit (single byte per pixel) to 10bit (two bytes per pixel) array
*
* \param input input/output buffer
* \return 1
*/
int frame_8bit_to_10bit(kvz_pixel* input, int width, int height) {
uint8_t* temp_buffer = (uint8_t*)input;
const uint32_t pixels = width*height;
for (int i = pixels - 1; i >= 0; i--) {
input[i] = temp_buffer[i] << 2;
}
return 1;
}
/**
* \brief Read a single frame from a file.
*
* Read luma and chroma values from file. Extend pixels if the image buffer
* is larger than the input image.
*
* \param file input file
* \param input_width width of the input video in pixels
* \param input_height height of the input video in pixels
* \param img_out image buffer
*
* \return 1 on success, 0 on failure
*/
int yuv_io_read(FILE* file,
unsigned input_width, unsigned input_height,
kvz_picture *img_out)
{
assert(input_width % 2 == 0);
assert(input_height % 2 == 0);
const unsigned y_size = input_width * input_height;
const unsigned uv_input_width = input_width / 2;
const unsigned uv_input_height = input_height / 2;
const unsigned uv_size = uv_input_width * uv_input_height;
const unsigned uv_array_width = img_out->width / 2;
const unsigned uv_array_height = img_out->height / 2;
if (input_width == img_out->width) {
// No need to extend pixels.
const size_t pixel_size = sizeof(unsigned char);
if (fread(img_out->y, pixel_size, y_size, file) != y_size) return 0;
if (fread(img_out->u, pixel_size, uv_size, file) != uv_size) return 0;
if (fread(img_out->v, pixel_size, uv_size, file) != uv_size) return 0;
} else {
// Need to copy pixels to fill the image in horizontal direction.
if (!read_and_fill_frame_data(file, input_width, input_height, img_out->width, img_out->y)) return 0;
if (!read_and_fill_frame_data(file, uv_input_width, uv_input_height, uv_array_width, img_out->u)) return 0;
if (!read_and_fill_frame_data(file, uv_input_width, uv_input_height, uv_array_width, img_out->v)) return 0;
}
if (input_height != img_out->height) {
// Need to copy pixels to fill the image in vertical direction.
fill_after_frame(input_height, img_out->width, img_out->height, img_out->y);
fill_after_frame(uv_input_height, uv_array_width, uv_array_height, img_out->u);
fill_after_frame(uv_input_height, uv_array_width, uv_array_height, img_out->v);
}
#if KVZ_BIT_DEPTH == 10
frame_8bit_to_10bit(img_out->y, img_out->width, img_out->height);
frame_8bit_to_10bit(img_out->u, img_out->width >> 1, img_out->height >> 1);
frame_8bit_to_10bit(img_out->v, img_out->width >> 1, img_out->height >> 1);
#endif
return 1;
}
/**
* \brief Seek forward in a YUV file.
*
* \param file the input file
* \param frames number of frames to seek
* \param input_width width of the input video in pixels
* \param input_height height of the input video in pixels
*
* \return 1 on success, 0 on failure
*/
int yuv_io_seek(FILE* file, unsigned frames,
unsigned input_width, unsigned input_height)
{
const size_t frame_bytes = input_width * input_height * 3 / 2;
const int64_t skip_bytes = (int64_t)(frames * frame_bytes);
// Attempt to seek normally.
size_t error = fseek(file, skip_bytes, SEEK_CUR);
if (!error) return 1;
// Seek failed. Skip data by reading.
error = 0;
unsigned char* tmp[4096];
size_t bytes_left = skip_bytes;
while (bytes_left > 0 && !error) {
const size_t skip = MIN(4096, bytes_left);
error = fread(tmp, sizeof(unsigned char), skip, file) != skip;
bytes_left -= skip;
}
return !error || feof(file);
}
/**
* \brief Write a single frame to a file.
*
* \param file output file
* \param img image to output
* \param output_width width of the output in pixels
* \param output_height height of the output in pixels
*
* \return 1 on success, 0 on failure
*/
int yuv_io_write(FILE* file,
const kvz_picture *img,
unsigned output_width, unsigned output_height)
{
const int width = img->width;
for (int y = 0; y < output_height; ++y) {
fwrite(&img->y[y * width], sizeof(*img->y), output_width, file);
// TODO: Check that fwrite succeeded.
}
for (int y = 0; y < output_height / 2; ++y) {
fwrite(&img->u[y * width / 2], sizeof(*img->u), output_width / 2, file);
}
for (int y = 0; y < output_height / 2; ++y) {
fwrite(&img->v[y * width / 2], sizeof(*img->v), output_width / 2, file);
}
return 1;
}