2015-06-05 08:15:18 +00:00
|
|
|
/*****************************************************************************
|
|
|
|
* 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>
|
|
|
|
|
2015-06-17 12:24:07 +00:00
|
|
|
#include "yuv_io.h"
|
2015-06-05 08:15:18 +00:00
|
|
|
|
|
|
|
static void fill_after_frame(unsigned height, unsigned array_width,
|
2015-06-30 08:43:48 +00:00
|
|
|
unsigned array_height, kvz_pixel *data)
|
2015-06-05 08:15:18 +00:00
|
|
|
{
|
2015-06-30 08:43:48 +00:00
|
|
|
kvz_pixel* p = data + height * array_width;
|
|
|
|
kvz_pixel* end = data + array_width * array_height;
|
2015-06-05 08:15:18 +00:00
|
|
|
|
|
|
|
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,
|
2015-06-30 08:43:48 +00:00
|
|
|
unsigned array_width, kvz_pixel *data)
|
2015-06-05 08:15:18 +00:00
|
|
|
{
|
2015-06-30 08:43:48 +00:00
|
|
|
kvz_pixel* p = data;
|
|
|
|
kvz_pixel* end = data + array_width * height;
|
|
|
|
kvz_pixel fill_char;
|
2015-06-05 08:15:18 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-11 05:42:09 +00:00
|
|
|
/**
|
|
|
|
* \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;
|
|
|
|
}
|
|
|
|
|
2015-06-05 08:15:18 +00:00
|
|
|
/**
|
|
|
|
* \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
|
|
|
|
*/
|
2015-06-17 12:24:07 +00:00
|
|
|
int yuv_io_read(FILE* file,
|
|
|
|
unsigned input_width, unsigned input_height,
|
2015-06-30 07:56:29 +00:00
|
|
|
kvz_picture *img_out)
|
2015-06-05 08:15:18 +00:00
|
|
|
{
|
2015-07-15 08:37:37 +00:00
|
|
|
assert(input_width % 2 == 0);
|
|
|
|
assert(input_height % 2 == 0);
|
|
|
|
|
2015-06-05 08:15:18 +00:00
|
|
|
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;
|
|
|
|
|
2015-06-17 12:24:07 +00:00
|
|
|
const unsigned uv_array_width = img_out->width / 2;
|
|
|
|
const unsigned uv_array_height = img_out->height / 2;
|
2015-06-05 08:15:18 +00:00
|
|
|
|
2015-06-17 12:24:07 +00:00
|
|
|
if (input_width == img_out->width) {
|
2015-06-05 08:15:18 +00:00
|
|
|
// 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.
|
2015-06-17 12:24:07 +00:00
|
|
|
if (!read_and_fill_frame_data(file, input_width, input_height, img_out->width, img_out->y)) return 0;
|
2015-06-05 08:15:18 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-06-17 12:24:07 +00:00
|
|
|
if (input_height != img_out->height) {
|
2015-06-05 08:15:18 +00:00
|
|
|
// Need to copy pixels to fill the image in vertical direction.
|
2015-06-17 12:24:07 +00:00
|
|
|
fill_after_frame(input_height, img_out->width, img_out->height, img_out->y);
|
2015-06-05 08:15:18 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-08-11 05:42:09 +00:00
|
|
|
#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
|
|
|
|
|
2015-06-05 08:15:18 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2015-06-09 07:20:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-06-17 12:24:07 +00:00
|
|
|
* \brief Seek forward in a YUV file.
|
2015-06-09 07:20:21 +00:00
|
|
|
*
|
|
|
|
* \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
|
|
|
|
*/
|
2015-06-17 12:24:07 +00:00
|
|
|
int yuv_io_seek(FILE* file, unsigned frames,
|
|
|
|
unsigned input_width, unsigned input_height)
|
2015-06-09 07:20:21 +00:00
|
|
|
{
|
|
|
|
const size_t frame_bytes = input_width * input_height * 3 / 2;
|
2015-08-31 06:25:17 +00:00
|
|
|
const int64_t skip_bytes = (int64_t)(frames * frame_bytes);
|
2015-06-09 07:20:21 +00:00
|
|
|
|
|
|
|
// Attempt to seek normally.
|
2015-08-31 06:25:17 +00:00
|
|
|
size_t error = fseek(file, skip_bytes, SEEK_CUR);
|
2015-06-09 07:20:21 +00:00
|
|
|
if (!error) return 1;
|
|
|
|
|
|
|
|
// Seek failed. Skip data by reading.
|
2015-07-06 09:07:05 +00:00
|
|
|
error = 0;
|
2015-06-09 07:20:21 +00:00
|
|
|
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);
|
|
|
|
}
|
2015-06-17 12:25:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \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,
|
2015-06-30 07:56:29 +00:00
|
|
|
const kvz_picture *img,
|
2015-06-17 12:25:32 +00:00
|
|
|
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;
|
|
|
|
}
|