2014-01-24 10:37:15 +00:00
|
|
|
/*****************************************************************************
|
|
|
|
* This file is part of Kvazaar HEVC encoder.
|
2014-02-21 13:00:20 +00:00
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* Copyright (C) 2013-2015 Tampere University of Technology and others (see
|
2014-01-24 10:37:15 +00:00
|
|
|
* COPYING file).
|
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* 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.
|
2014-01-24 10:37:15 +00:00
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* 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.
|
2014-01-24 10:37:15 +00:00
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with Kvazaar. If not, see <http://www.gnu.org/licenses/>.
|
2014-01-24 10:37:15 +00:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* \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>
|
2014-04-11 09:42:37 +00:00
|
|
|
#include <time.h>
|
2013-09-23 13:48:34 +00:00
|
|
|
|
2014-06-11 05:48:36 +00:00
|
|
|
#include "checkpoint.h"
|
2013-09-19 09:48:25 +00:00
|
|
|
#include "global.h"
|
|
|
|
#include "config.h"
|
2014-06-16 08:18:11 +00:00
|
|
|
#include "threads.h"
|
2013-09-19 09:48:25 +00:00
|
|
|
#include "encoder.h"
|
|
|
|
#include "cabac.h"
|
2014-06-05 12:54:58 +00:00
|
|
|
#include "image.h"
|
2013-09-19 09:48:25 +00:00
|
|
|
#include "transform.h"
|
2014-04-16 07:40:42 +00:00
|
|
|
#include "scalinglist.h"
|
2014-04-29 08:14:42 +00:00
|
|
|
#include "strategyselector.h"
|
2015-05-13 09:45:11 +00:00
|
|
|
#include "cli.h"
|
2015-05-18 08:43:10 +00:00
|
|
|
#include "kvazaar.h"
|
2014-02-21 13:00:20 +00:00
|
|
|
|
2015-05-14 15:33:57 +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[])
|
2014-02-21 13:00:20 +00:00
|
|
|
{
|
2015-03-04 11:30:26 +00:00
|
|
|
config_t *cfg = NULL; //!< Global configuration
|
2013-09-19 09:48:25 +00:00
|
|
|
FILE *input = NULL; //!< input file (YUV)
|
|
|
|
FILE *output = NULL; //!< output file (HEVC NAL stream)
|
2014-06-16 07:22:59 +00:00
|
|
|
uint64_t curpos = 0;
|
2014-03-10 16:07:48 +00:00
|
|
|
FILE *recout = NULL; //!< reconstructed YUV output, --debug
|
2014-04-11 09:42:37 +00:00
|
|
|
clock_t start_time = clock();
|
2014-06-16 08:18:11 +00:00
|
|
|
clock_t encoding_start_cpu_time;
|
|
|
|
CLOCK_T encoding_start_real_time;
|
|
|
|
|
|
|
|
clock_t encoding_end_cpu_time;
|
|
|
|
CLOCK_T encoding_end_real_time;
|
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
|
2014-06-11 05:48:36 +00:00
|
|
|
|
|
|
|
CHECKPOINTS_INIT();
|
2014-01-31 12:32:41 +00:00
|
|
|
|
2013-09-19 09:48:25 +00:00
|
|
|
// Handle configuration
|
|
|
|
cfg = config_alloc();
|
2014-02-21 13:00:20 +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)) {
|
2015-05-13 09:45:11 +00:00
|
|
|
print_version();
|
|
|
|
print_help();
|
2013-09-19 09:48:25 +00:00
|
|
|
|
2014-04-17 08:58:03 +00:00
|
|
|
goto exit_failure;
|
2013-09-19 09:48:25 +00:00
|
|
|
}
|
|
|
|
|
2014-10-14 09:01:56 +00:00
|
|
|
//Initialize strategies
|
|
|
|
if (!strategyselector_init(cfg->cpuid)) {
|
|
|
|
fprintf(stderr, "Failed to initialize strategies.\n");
|
|
|
|
goto exit_failure;
|
|
|
|
}
|
|
|
|
|
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");
|
2014-04-17 08:58:03 +00:00
|
|
|
goto exit_failure;
|
2013-09-19 09:48:25 +00:00
|
|
|
}
|
2014-02-21 13:00:20 +00:00
|
|
|
|
2014-11-13 09:45:53 +00:00
|
|
|
// Check if the output file name is a dash, this means stdout
|
|
|
|
if (!strcmp(cfg->output, "-")) {
|
|
|
|
output = stdout;
|
|
|
|
} else {
|
|
|
|
// Otherwise we try to open the output file
|
|
|
|
output = fopen(cfg->output, "wb");
|
2015-05-20 14:08:35 +00:00
|
|
|
// Check that output was opened correctly
|
|
|
|
if (output == NULL) {
|
|
|
|
fprintf(stderr, "Could not open output file, shutting down!\n");
|
|
|
|
goto exit_failure;
|
|
|
|
}
|
2014-11-13 09:45:53 +00:00
|
|
|
}
|
|
|
|
|
2015-05-20 14:08:35 +00:00
|
|
|
bitstream_t output_stream;
|
|
|
|
if (!bitstream_init(&output_stream, BITSTREAM_TYPE_FILE)) {
|
|
|
|
fprintf(stderr, "Could not initialize stream!\n");
|
|
|
|
return 0;
|
2013-09-19 09:48:25 +00:00
|
|
|
}
|
2015-05-20 14:08:35 +00:00
|
|
|
output_stream.file.output = output;
|
2013-09-19 09:48:25 +00:00
|
|
|
|
2014-03-10 16:07:48 +00:00
|
|
|
if (cfg->debug != NULL) {
|
|
|
|
recout = fopen(cfg->debug, "wb");
|
|
|
|
if (recout == NULL) {
|
|
|
|
fprintf(stderr, "Could not open reconstruction file (%s), shutting down!\n", cfg->debug);
|
2014-04-17 08:58:03 +00:00
|
|
|
goto exit_failure;
|
2014-03-10 16:07:48 +00:00
|
|
|
}
|
|
|
|
}
|
2014-04-17 12:42:20 +00:00
|
|
|
|
|
|
|
//Allocate and init exp golomb table
|
|
|
|
if (!init_exp_golomb(4096*8)) {
|
|
|
|
fprintf(stderr, "Failed to allocate the exp golomb code table, shutting down!\n");
|
2014-04-17 08:58:03 +00:00
|
|
|
goto exit_failure;
|
2014-04-17 12:42:20 +00:00
|
|
|
}
|
2013-09-19 09:48:25 +00:00
|
|
|
|
2015-05-18 08:43:10 +00:00
|
|
|
const kvz_api *api = kvz_api_get(8);
|
|
|
|
|
|
|
|
kvz_encoder* enc = api->encoder_open(cfg);
|
|
|
|
if (!enc) {
|
|
|
|
fprintf(stderr, "Failed to open encoder.");
|
2014-04-17 12:42:20 +00:00
|
|
|
goto exit_failure;
|
|
|
|
}
|
2015-05-18 08:43:10 +00:00
|
|
|
|
|
|
|
encoder_control_t *encoder = enc->control;
|
2014-04-17 12:42:20 +00:00
|
|
|
|
2014-04-11 13:50:59 +00:00
|
|
|
fprintf(stderr, "Input: %s, output: %s\n", cfg->input, cfg->output);
|
2014-04-11 13:55:08 +00:00
|
|
|
fprintf(stderr, " Video size: %dx%d (input=%dx%d)\n",
|
2015-05-18 08:43:10 +00:00
|
|
|
encoder->in.width, encoder->in.height,
|
|
|
|
encoder->in.real_width, encoder->in.real_height);
|
2014-04-17 12:42:20 +00:00
|
|
|
|
2014-06-05 07:09:25 +00:00
|
|
|
//Now, do the real stuff
|
|
|
|
{
|
2014-06-10 08:59:31 +00:00
|
|
|
|
2014-06-05 07:09:25 +00:00
|
|
|
int i;
|
|
|
|
int current_encoder_state = 0;
|
2014-04-17 12:42:20 +00:00
|
|
|
|
2015-05-14 15:37:55 +00:00
|
|
|
|
|
|
|
if (cfg->seek > 0) {
|
|
|
|
int frame_bytes = cfg->width * cfg->height * 3 / 2;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
if (!strcmp(cfg->input, "-")) {
|
|
|
|
// Input is stdin.
|
|
|
|
int i;
|
2015-05-18 15:21:23 +00:00
|
|
|
image_t *img_in = image_alloc(encoder->in.width, encoder->in.height, 0);
|
|
|
|
if (!img_in) {
|
|
|
|
fprintf(stderr, "Failed to allocate image.\n");
|
|
|
|
goto exit_failure;
|
|
|
|
}
|
2015-05-14 15:37:55 +00:00
|
|
|
for (i = 0; !error && i < cfg->seek; ++i) {
|
2015-05-18 15:21:23 +00:00
|
|
|
error = !read_one_frame(input, &enc->states[current_encoder_state], img_in);
|
2015-05-14 15:37:55 +00:00
|
|
|
}
|
2015-05-18 15:21:23 +00:00
|
|
|
image_free(img_in);
|
2015-05-14 15:37:55 +00:00
|
|
|
} else {
|
|
|
|
// input is a file. We hope. Proper detection is OS dependent.
|
|
|
|
error = fseek(input, cfg->seek * frame_bytes, SEEK_CUR);
|
|
|
|
}
|
|
|
|
if (error && !feof(input)) {
|
|
|
|
fprintf(stderr, "Failed to seek %d frames.\n", cfg->seek);
|
|
|
|
goto exit_failure;
|
|
|
|
}
|
|
|
|
}
|
2014-06-05 07:09:25 +00:00
|
|
|
|
2014-06-16 08:18:11 +00:00
|
|
|
GET_TIME(&encoding_start_real_time);
|
|
|
|
encoding_start_cpu_time = clock();
|
2014-06-05 07:09:25 +00:00
|
|
|
|
2015-01-22 10:16:46 +00:00
|
|
|
uint64_t bitstream_length = 0;
|
2015-05-14 15:33:57 +00:00
|
|
|
uint32_t frames_started = 0;
|
|
|
|
uint32_t frames_done = 0;
|
|
|
|
double psnr_sum[3] = { 0.0, 0.0, 0.0 };
|
2015-01-22 10:16:46 +00:00
|
|
|
|
2014-06-05 07:09:25 +00:00
|
|
|
// Start coding cycle while data on input and not on the last frame
|
2015-05-18 13:56:54 +00:00
|
|
|
encoder_state_t *state = &enc->states[current_encoder_state];
|
2015-05-14 15:33:57 +00:00
|
|
|
while (cfg->frames == 0 || frames_started < cfg->frames) {
|
|
|
|
frames_started += 1;
|
2015-05-18 15:21:23 +00:00
|
|
|
|
2014-06-16 07:22:59 +00:00
|
|
|
|
2015-05-18 15:21:23 +00:00
|
|
|
image_t *img_in = image_alloc(state->tile->frame->width, state->tile->frame->height, 0);
|
|
|
|
if (!img_in) {
|
|
|
|
fprintf(stderr, "Failed to allocate image.\n");
|
|
|
|
goto exit_failure;
|
|
|
|
}
|
2014-06-05 07:09:25 +00:00
|
|
|
|
|
|
|
// Read one frame from the input
|
2015-05-18 15:21:23 +00:00
|
|
|
if (!read_one_frame(input, &enc->states[current_encoder_state], img_in)) {
|
2014-06-05 07:09:25 +00:00
|
|
|
if (!feof(input))
|
2015-05-18 08:43:10 +00:00
|
|
|
fprintf(stderr, "Failed to read a frame %d\n", enc->states[current_encoder_state].global->frame);
|
2015-05-18 15:21:23 +00:00
|
|
|
|
2014-06-16 11:21:52 +00:00
|
|
|
|
2015-05-18 08:43:10 +00:00
|
|
|
enc->states[current_encoder_state].stats_done = 1;
|
2014-06-05 07:09:25 +00:00
|
|
|
break;
|
2014-03-19 11:23:41 +00:00
|
|
|
}
|
2014-06-05 07:09:25 +00:00
|
|
|
|
2015-05-19 14:48:37 +00:00
|
|
|
image_t *img_out = NULL;
|
2015-06-03 17:09:36 +00:00
|
|
|
if (1 != api->encoder_encode(enc, img_in, &img_out, &output_stream)) {
|
|
|
|
fprintf(stderr, "Failed to encode image.\n");
|
|
|
|
goto exit_failure;
|
|
|
|
}
|
2015-05-18 15:21:23 +00:00
|
|
|
|
|
|
|
if (img_out != NULL) {
|
2015-05-19 14:48:37 +00:00
|
|
|
state = &enc->states[enc->cur_state_num];
|
2015-05-18 13:56:54 +00:00
|
|
|
double frame_psnr[3] = { 0.0, 0.0, 0.0 };
|
|
|
|
encoder_compute_stats(state, recout, frame_psnr, &bitstream_length);
|
2015-05-18 15:21:23 +00:00
|
|
|
|
2015-05-18 13:56:54 +00:00
|
|
|
frames_done += 1;
|
|
|
|
psnr_sum[0] += frame_psnr[0];
|
|
|
|
psnr_sum[1] += frame_psnr[1];
|
|
|
|
psnr_sum[2] += frame_psnr[2];
|
|
|
|
|
|
|
|
print_frame_info(state, frame_psnr);
|
|
|
|
}
|
2015-05-18 15:21:23 +00:00
|
|
|
|
|
|
|
image_free(img_in);
|
|
|
|
image_free(img_out);
|
2014-06-05 07:09:25 +00:00
|
|
|
}
|
2014-06-16 07:22:59 +00:00
|
|
|
|
|
|
|
//Compute stats for the remaining encoders
|
|
|
|
{
|
2015-06-03 17:09:36 +00:00
|
|
|
image_t *img_out = NULL;
|
|
|
|
while (1 == api->encoder_encode(enc, NULL, &img_out, &output_stream)) {
|
2015-05-19 14:48:37 +00:00
|
|
|
if (img_out != NULL) {
|
2015-06-03 17:09:36 +00:00
|
|
|
double frame_psnr[3] = { 0.0, 0.0, 0.0 };
|
|
|
|
encoder_state_t *state = &enc->states[enc->cur_state_num];
|
2015-05-14 15:33:57 +00:00
|
|
|
|
|
|
|
encoder_compute_stats(state, recout, frame_psnr, &bitstream_length);
|
|
|
|
print_frame_info(state, frame_psnr);
|
|
|
|
frames_done += 1;
|
|
|
|
psnr_sum[0] += frame_psnr[0];
|
|
|
|
psnr_sum[1] += frame_psnr[1];
|
|
|
|
psnr_sum[2] += frame_psnr[2];
|
2015-05-19 14:48:37 +00:00
|
|
|
|
|
|
|
image_free(img_out);
|
2015-06-03 17:09:36 +00:00
|
|
|
img_out = NULL;
|
2015-05-14 15:33:57 +00:00
|
|
|
}
|
2015-06-03 17:09:36 +00:00
|
|
|
}
|
2014-06-16 07:22:59 +00:00
|
|
|
}
|
|
|
|
|
2014-06-16 08:18:11 +00:00
|
|
|
GET_TIME(&encoding_end_real_time);
|
|
|
|
encoding_end_cpu_time = clock();
|
|
|
|
|
2015-05-18 08:43:10 +00:00
|
|
|
threadqueue_flush(encoder->threadqueue);
|
2014-06-13 09:58:12 +00:00
|
|
|
|
2014-06-16 07:22:59 +00:00
|
|
|
|
2014-06-05 07:09:25 +00:00
|
|
|
// Coding finished
|
|
|
|
fgetpos(output,(fpos_t*)&curpos);
|
2013-09-19 09:48:25 +00:00
|
|
|
|
2014-06-05 07:09:25 +00:00
|
|
|
// Print statistics of the coding
|
2015-01-22 10:16:46 +00:00
|
|
|
fprintf(stderr, " Processed %d frames, %10llu bits AVG PSNR: %2.4f %2.4f %2.4f\n",
|
2015-05-14 15:33:57 +00:00
|
|
|
frames_done, (long long unsigned int)bitstream_length * 8,
|
|
|
|
psnr_sum[0] / frames_done, psnr_sum[1] / frames_done, psnr_sum[2] / frames_done);
|
2014-06-16 08:18:11 +00:00
|
|
|
fprintf(stderr, " Total CPU time: %.3f s.\n", ((float)(clock() - start_time)) / CLOCKS_PER_SEC);
|
|
|
|
|
|
|
|
{
|
2014-11-14 14:38:07 +00:00
|
|
|
double encoding_time = ( (double)(encoding_end_cpu_time - encoding_start_cpu_time) ) / (double) CLOCKS_PER_SEC;
|
2014-06-16 08:18:11 +00:00
|
|
|
double wall_time = CLOCK_T_AS_DOUBLE(encoding_end_real_time) - CLOCK_T_AS_DOUBLE(encoding_start_real_time);
|
2014-11-14 14:38:07 +00:00
|
|
|
fprintf(stderr, " Encoding time: %.3f s.\n", encoding_time);
|
|
|
|
fprintf(stderr, " Encoding wall time: %.3f s.\n", wall_time);
|
|
|
|
fprintf(stderr, " Encoding CPU usage: %.2f%%\n", encoding_time/wall_time*100.f);
|
2015-05-14 15:33:57 +00:00
|
|
|
fprintf(stderr, " FPS: %.2f\n", ((double)frames_done)/wall_time);
|
2014-06-16 08:18:11 +00:00
|
|
|
}
|
2013-09-19 09:48:25 +00:00
|
|
|
|
2015-05-20 14:08:35 +00:00
|
|
|
bitstream_finalize(&output_stream);
|
|
|
|
|
2014-06-05 07:09:25 +00:00
|
|
|
fclose(input);
|
|
|
|
fclose(output);
|
|
|
|
if(recout != NULL) fclose(recout);
|
|
|
|
|
2015-05-18 08:43:10 +00:00
|
|
|
for (i = 0; i <= encoder->owf; ++i) {
|
|
|
|
encoder_state_finalize(&enc->states[i]);
|
2014-06-05 07:09:25 +00:00
|
|
|
}
|
2014-06-10 08:59:31 +00:00
|
|
|
|
2015-05-18 08:43:10 +00:00
|
|
|
api->encoder_close(enc);
|
2014-06-05 07:09:25 +00:00
|
|
|
}
|
2013-09-19 09:48:25 +00:00
|
|
|
|
|
|
|
// Deallocating
|
|
|
|
config_destroy(cfg);
|
2014-04-17 12:42:20 +00:00
|
|
|
|
2014-04-04 08:04:24 +00:00
|
|
|
free_exp_golomb();
|
2014-04-29 08:14:42 +00:00
|
|
|
|
|
|
|
strategyselector_free();
|
2014-06-11 05:48:36 +00:00
|
|
|
|
|
|
|
CHECKPOINTS_FINALIZE();
|
2013-09-19 09:48:25 +00:00
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
2014-04-17 08:58:03 +00:00
|
|
|
|
|
|
|
exit_failure:
|
|
|
|
if (cfg) config_destroy(cfg);
|
|
|
|
if (input) fclose(input);
|
|
|
|
if (output) fclose(output);
|
|
|
|
if (recout) fclose(recout);
|
2014-04-29 08:14:42 +00:00
|
|
|
strategyselector_free();
|
2014-06-11 05:48:36 +00:00
|
|
|
CHECKPOINTS_FINALIZE();
|
2014-04-17 08:58:03 +00:00
|
|
|
return EXIT_FAILURE;
|
2013-09-19 09:48:25 +00:00
|
|
|
}
|