2014-01-24 10:37:15 +00:00
|
|
|
/*****************************************************************************
|
|
|
|
* This file is part of Kvazaar HEVC encoder.
|
2012-05-30 12:10:23 +00:00
|
|
|
*
|
2014-01-24 10:37:15 +00:00
|
|
|
* Copyright (C) 2013-2014 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 General Public License version 2 as published
|
|
|
|
* by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* 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 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
|
2013-09-18 14:29:30 +00:00
|
|
|
*
|
2012-05-30 12:10:23 +00:00
|
|
|
*/
|
|
|
|
|
2014-01-31 12:32:41 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
/* The following two defines must be located before the inclusion of any system header files. */
|
|
|
|
#define WINVER 0x0500
|
|
|
|
#define _WIN32_WINNT 0x0500
|
|
|
|
#include <io.h> /* _setmode() */
|
|
|
|
#include <fcntl.h> /* _O_BINARY */
|
|
|
|
#endif
|
|
|
|
|
2013-09-19 09:48:25 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2013-09-23 13:48:34 +00:00
|
|
|
|
2013-09-19 09:48:25 +00:00
|
|
|
#include "global.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "encoder.h"
|
|
|
|
#include "cabac.h"
|
|
|
|
#include "picture.h"
|
|
|
|
#include "transform.h"
|
2012-05-30 12:10:23 +00:00
|
|
|
|
2013-09-19 09:48:25 +00:00
|
|
|
// Assembly optimization headers
|
2014-01-30 15:56:52 +00:00
|
|
|
#include "x86/cpu.h"
|
2012-05-30 12:10:23 +00:00
|
|
|
|
2013-09-19 09:48:25 +00:00
|
|
|
/**
|
|
|
|
* \brief Program main function.
|
|
|
|
* \param argc Argument count from commandline
|
|
|
|
* \param argv Argument list
|
|
|
|
* \return Program exit state
|
2012-05-30 12:10:23 +00:00
|
|
|
*/
|
2013-09-19 09:48:25 +00:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int ecx = 0,edx =0;
|
|
|
|
/* CPU feature bits */
|
|
|
|
enum { BIT_SSE3 = 0,BIT_SSSE3 = 9, BIT_SSE41 = 19, BIT_SSE42 = 20, BIT_MMX = 24, BIT_SSE = 25, BIT_SSE2 = 26, BIT_AVX = 28};
|
|
|
|
uint32_t cur_frame = 0;
|
|
|
|
config *cfg = NULL; //!< Global configuration
|
|
|
|
FILE *input = NULL; //!< input file (YUV)
|
|
|
|
FILE *output = NULL; //!< output file (HEVC NAL stream)
|
2014-01-31 18:34:50 +00:00
|
|
|
encoder_control *encoder = NULL; //!< Encoder control struct
|
2013-09-19 09:48:25 +00:00
|
|
|
double psnr[3] = { 0.0, 0.0, 0.0 };
|
2013-11-12 10:04:54 +00:00
|
|
|
uint64_t curpos = 0;
|
|
|
|
uint64_t lastpos = 0;
|
2013-09-19 09:48:25 +00:00
|
|
|
#ifdef _DEBUG
|
2013-12-20 15:15:00 +00:00
|
|
|
FILE *recout = fopen("encrec_832x480_60.yuv","wb"); //!< reconstructed YUV output (only on debug mode)
|
2013-09-19 09:48:25 +00:00
|
|
|
#endif
|
2014-01-31 12:32:41 +00:00
|
|
|
|
2014-02-05 17:16:44 +00:00
|
|
|
// Stdin and stdout need to be binary for input and output to work.
|
|
|
|
// Stderr needs to be text mode to convert \n to \r\n in Windows.
|
2014-01-31 12:32:41 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
_setmode( _fileno( stdin ), _O_BINARY );
|
|
|
|
_setmode( _fileno( stdout ), _O_BINARY );
|
2014-02-05 17:16:44 +00:00
|
|
|
_setmode( _fileno( stderr ), _O_TEXT );
|
2014-01-31 12:32:41 +00:00
|
|
|
#endif
|
|
|
|
|
2013-09-19 09:48:25 +00:00
|
|
|
// Handle configuration
|
|
|
|
cfg = config_alloc();
|
2013-09-23 13:48:34 +00:00
|
|
|
|
2013-09-19 09:48:25 +00:00
|
|
|
// If problem with configuration, print banner and shutdown
|
2014-01-31 18:34:50 +00:00
|
|
|
if (!cfg || !config_init(cfg) || !config_read(cfg,argc,argv)) {
|
2014-02-05 17:16:44 +00:00
|
|
|
fprintf(stderr,
|
|
|
|
"/***********************************************/\n"
|
|
|
|
" * Kvazaar HEVC Encoder v. " VERSION_STRING "*\n"
|
|
|
|
" * Tampere University of Technology 2014 *\n"
|
|
|
|
"/***********************************************/\n\n");
|
2013-09-19 09:48:25 +00:00
|
|
|
|
2014-02-05 17:16:44 +00:00
|
|
|
fprintf(stderr,
|
|
|
|
"Usage:\n"
|
|
|
|
"hevc_encoder -i <input> -w <width> -h <height> -o <output>\n"
|
|
|
|
"Optional parameters:\n"
|
|
|
|
" -n, --frames <integer> : number of frames to decode\n"
|
|
|
|
" -q, --qp <integer> : Quantization Parameter, default 32\n"
|
|
|
|
" -p, --period <integer> : Period of intra pictures, default 0\n"
|
|
|
|
" 0: only first picture is intra\n"
|
|
|
|
" 1: all pictures are intra\n"
|
|
|
|
" 2-N: every Nth picture is intra\n"
|
|
|
|
" --no-deblock : Disable deblocking filter\n"
|
|
|
|
" --deblock <beta:tc> : Deblocking filter parameters\n"
|
|
|
|
" --no-sao : Disable sample adaptive offset\n");
|
2013-09-19 09:48:25 +00:00
|
|
|
|
2014-01-31 15:26:09 +00:00
|
|
|
if (cfg)
|
|
|
|
config_destroy(cfg);
|
|
|
|
|
2013-09-19 09:48:25 +00:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dig CPU features with cpuid
|
2014-01-30 15:56:52 +00:00
|
|
|
kvz_cpu_cpuid(&ecx,&edx);
|
2013-09-19 09:48:25 +00:00
|
|
|
printf("CPU features enabled: ");
|
|
|
|
// EDX
|
|
|
|
if (edx & (1<<BIT_MMX)) printf("MMX ");
|
|
|
|
if (edx & (1<<BIT_SSE)) printf("SSE ");
|
|
|
|
if (edx & (1<<BIT_SSE2)) printf("SSE2 ");
|
|
|
|
// ECX
|
|
|
|
if (ecx & (1<<BIT_SSE3)) printf("SSE3 ");
|
|
|
|
if (ecx & (1<<BIT_SSSE3)) printf("SSSE3 ");
|
|
|
|
if (ecx & (1<<BIT_SSE41)) printf("SSE4.1 ");
|
|
|
|
if (ecx & (1<<BIT_SSE42)) printf("SSE4.2 ");
|
|
|
|
if (ecx & (1<<BIT_AVX)) printf("AVX ");
|
|
|
|
printf("\r\n");
|
2013-05-20 14:26:57 +00:00
|
|
|
|
2012-06-04 10:47:12 +00:00
|
|
|
|
2014-01-27 13:02:07 +00:00
|
|
|
printf("Input: %s, output: %s\n", cfg->input, cfg->output);
|
2013-09-19 09:48:25 +00:00
|
|
|
printf(" Video size: %dx%d\n", cfg->width, cfg->height);
|
|
|
|
|
2014-01-31 12:32:41 +00:00
|
|
|
// Check if the input file name is a dash, this means stdin
|
|
|
|
if (!strcmp(cfg->input, "-")) {
|
|
|
|
input = stdin;
|
|
|
|
} else {
|
|
|
|
// Otherwise we try to open the input file
|
|
|
|
input = fopen(cfg->input, "rb");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that input was opened correctly
|
2013-09-19 09:48:25 +00:00
|
|
|
if (input == NULL) {
|
|
|
|
fprintf(stderr, "Could not open input file, shutting down!\n");
|
|
|
|
config_destroy(cfg);
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
2012-05-30 12:10:23 +00:00
|
|
|
|
2013-09-19 09:48:25 +00:00
|
|
|
// Open output file and check that it was opened correctly
|
|
|
|
output = fopen(cfg->output, "wb");
|
|
|
|
if (output == NULL) {
|
|
|
|
fprintf(stderr, "Could not open output file, shutting down!\n");
|
|
|
|
config_destroy(cfg);
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2014-01-31 18:34:50 +00:00
|
|
|
encoder = init_encoder_control(cfg);
|
|
|
|
if (!encoder)
|
|
|
|
return EXIT_FAILURE;
|
2013-09-19 09:48:25 +00:00
|
|
|
|
|
|
|
// Set output file
|
|
|
|
encoder->output = output;
|
|
|
|
|
|
|
|
// input init (TODO: read from commandline / config)
|
|
|
|
encoder->bitdepth = 8;
|
|
|
|
encoder->frame = 0;
|
2014-01-27 13:02:07 +00:00
|
|
|
encoder->QP = encoder->cfg->qp;
|
2013-09-19 09:48:25 +00:00
|
|
|
encoder->in.video_format = FORMAT_420;
|
|
|
|
// deblocking filter
|
2014-02-03 22:16:42 +00:00
|
|
|
encoder->deblock_enable = encoder->cfg->deblock_enable;
|
2014-02-03 22:31:24 +00:00
|
|
|
encoder->beta_offset_div2 = encoder->cfg->deblock_beta;
|
|
|
|
encoder->tc_offset_div2 = encoder->cfg->deblock_tc;
|
2013-09-19 09:48:25 +00:00
|
|
|
// SAO
|
2014-02-03 22:55:51 +00:00
|
|
|
encoder->sao_enable = encoder->cfg->sao_enable;
|
2013-09-19 09:48:25 +00:00
|
|
|
|
|
|
|
init_encoder_input(&encoder->in, input, cfg->width, cfg->height);
|
|
|
|
|
2013-10-15 14:56:50 +00:00
|
|
|
// Init coeff data table
|
2013-10-18 08:39:13 +00:00
|
|
|
encoder->in.cur_pic->coeff_y = MALLOC(coefficient, cfg->width * cfg->height);
|
|
|
|
encoder->in.cur_pic->coeff_u = MALLOC(coefficient, (cfg->width * cfg->height) >> 2);
|
|
|
|
encoder->in.cur_pic->coeff_v = MALLOC(coefficient, (cfg->width * cfg->height) >> 2);
|
|
|
|
|
|
|
|
// Init predicted data table
|
|
|
|
encoder->in.cur_pic->pred_y = MALLOC(pixel, cfg->width * cfg->height);
|
|
|
|
encoder->in.cur_pic->pred_u = MALLOC(pixel, (cfg->width * cfg->height) >> 2);
|
|
|
|
encoder->in.cur_pic->pred_v = MALLOC(pixel, (cfg->width * cfg->height) >> 2);
|
2013-10-15 14:56:50 +00:00
|
|
|
|
2013-09-19 09:48:25 +00:00
|
|
|
// Start coding cycle while data on input and not on the last frame
|
2014-02-04 10:50:39 +00:00
|
|
|
while(!cfg->frames || encoder->frame < cfg->frames) {
|
2013-09-19 09:48:25 +00:00
|
|
|
int32_t diff;
|
|
|
|
double temp_psnr[3];
|
|
|
|
|
|
|
|
// Read one frame from the input
|
2014-02-04 10:50:39 +00:00
|
|
|
if (!read_one_frame(input, encoder)) {
|
|
|
|
if (!feof(input))
|
|
|
|
printf("Failed to read a frame %d\n", encoder->frame);
|
|
|
|
break;
|
|
|
|
}
|
2013-09-19 09:48:25 +00:00
|
|
|
|
|
|
|
// The actual coding happens here, after this function we have a coded frame
|
|
|
|
encode_one_frame(encoder);
|
2012-06-05 11:01:47 +00:00
|
|
|
|
2013-03-11 14:26:09 +00:00
|
|
|
#ifdef _DEBUG
|
2013-09-19 09:48:25 +00:00
|
|
|
// Write reconstructed frame out (for debugging purposes)
|
|
|
|
fwrite(encoder->in.cur_pic->y_recdata, cfg->width * cfg->height, 1, recout);
|
|
|
|
fwrite(encoder->in.cur_pic->u_recdata, (cfg->width * cfg->height)>>2, 1, recout);
|
|
|
|
fwrite(encoder->in.cur_pic->v_recdata, (cfg->width * cfg->height)>>2, 1, recout);
|
2013-03-11 14:26:09 +00:00
|
|
|
#endif
|
2012-05-30 12:10:23 +00:00
|
|
|
|
2013-09-19 09:48:25 +00:00
|
|
|
// Calculate the bytes pushed to output for this frame
|
2013-11-12 10:04:54 +00:00
|
|
|
fgetpos(output,(fpos_t*)&curpos);
|
2013-09-19 09:48:25 +00:00
|
|
|
diff = (int32_t)(curpos-lastpos);
|
|
|
|
lastpos = curpos;
|
|
|
|
|
|
|
|
// PSNR calculations
|
|
|
|
temp_psnr[0] = image_psnr(encoder->in.cur_pic->y_data, encoder->in.cur_pic->y_recdata, cfg->width, cfg->height);
|
|
|
|
temp_psnr[1] = image_psnr(encoder->in.cur_pic->u_data, encoder->in.cur_pic->u_recdata, cfg->width>>1, cfg->height>>1);
|
|
|
|
temp_psnr[2] = image_psnr(encoder->in.cur_pic->v_data, encoder->in.cur_pic->v_recdata, cfg->width>>1, cfg->height>>1);
|
|
|
|
|
|
|
|
printf("POC %4d (%c-frame) %10d bits PSNR: %2.4f %2.4f %2.4f\n", encoder->frame,
|
|
|
|
"BPI"[encoder->in.cur_pic->slicetype%3], diff<<3,
|
|
|
|
temp_psnr[0], temp_psnr[1], temp_psnr[2]);
|
|
|
|
|
|
|
|
// Increment total PSNR
|
|
|
|
psnr[0] += temp_psnr[0];
|
|
|
|
psnr[1] += temp_psnr[1];
|
|
|
|
psnr[2] += temp_psnr[2];
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: add more than one reference
|
|
|
|
|
|
|
|
// Remove the ref pic (if present)
|
|
|
|
picture_list_rem(encoder->ref, 0, 1);
|
|
|
|
// Add current picture as reference
|
|
|
|
picture_list_add(encoder->ref, encoder->in.cur_pic);
|
|
|
|
// Allocate new memory to current picture
|
|
|
|
// TODO: reuse memory from old reference
|
|
|
|
encoder->in.cur_pic = picture_init(encoder->in.width, encoder->in.height, encoder->in.width_in_lcu, encoder->in.height_in_lcu);
|
|
|
|
|
2013-10-15 14:56:50 +00:00
|
|
|
// Copy pointer from the last cur_pic because we don't want to reallocate it
|
2013-10-18 08:39:13 +00:00
|
|
|
MOVE_POINTER(encoder->in.cur_pic->coeff_y,encoder->ref->pics[0]->coeff_y);
|
|
|
|
MOVE_POINTER(encoder->in.cur_pic->coeff_u,encoder->ref->pics[0]->coeff_u);
|
|
|
|
MOVE_POINTER(encoder->in.cur_pic->coeff_v,encoder->ref->pics[0]->coeff_v);
|
|
|
|
|
|
|
|
MOVE_POINTER(encoder->in.cur_pic->pred_y,encoder->ref->pics[0]->pred_y);
|
|
|
|
MOVE_POINTER(encoder->in.cur_pic->pred_u,encoder->ref->pics[0]->pred_u);
|
|
|
|
MOVE_POINTER(encoder->in.cur_pic->pred_v,encoder->ref->pics[0]->pred_v);
|
2013-10-15 14:56:50 +00:00
|
|
|
|
2013-09-19 09:48:25 +00:00
|
|
|
encoder->frame++;
|
2014-01-31 08:23:56 +00:00
|
|
|
encoder->poc++;
|
2013-09-19 09:48:25 +00:00
|
|
|
}
|
|
|
|
// Coding finished
|
2013-11-12 10:04:54 +00:00
|
|
|
fgetpos(output,(fpos_t*)&curpos);
|
2013-09-19 09:48:25 +00:00
|
|
|
|
|
|
|
// Print statistics of the coding
|
|
|
|
printf(" Processed %d frames, %10d bits AVG PSNR: %2.4f %2.4f %2.4f\n", encoder->frame, ((int32_t)curpos)<<3,
|
|
|
|
psnr[0] / encoder->frame, psnr[1] / encoder->frame, psnr[2] / encoder->frame);
|
|
|
|
|
|
|
|
fclose(input);
|
|
|
|
fclose(output);
|
|
|
|
#ifdef _DEBUG
|
|
|
|
fclose(recout);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Deallocating
|
|
|
|
config_destroy(cfg);
|
|
|
|
scalinglist_destroy();
|
|
|
|
picture_list_destroy(encoder->ref);
|
2014-02-03 09:52:43 +00:00
|
|
|
picture_destroy(encoder->in.cur_pic);
|
|
|
|
FREE_POINTER(encoder->in.cur_pic);
|
|
|
|
bitstream_free(encoder->stream);
|
|
|
|
FREE_POINTER(encoder->stream);
|
2014-01-31 14:54:20 +00:00
|
|
|
free(encoder);
|
2014-02-03 09:52:43 +00:00
|
|
|
free_tables();
|
|
|
|
FREE_POINTER(g_exp_table);
|
2013-09-19 09:48:25 +00:00
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|