2012-05-30 12:10:23 +00:00
|
|
|
/**
|
|
|
|
* HEVC Encoder
|
2013-04-16 08:23:03 +00:00
|
|
|
* - Marko Viitanen ( fador at iki.fi ), Tampere University of Technology, Department of Pervasive Computing.
|
2012-05-30 12:10:23 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \file decmain.c
|
|
|
|
\brief main file for the Decoder
|
|
|
|
\author Marko Viitanen
|
|
|
|
\date 2012-05
|
|
|
|
|
|
|
|
This file contains main() function
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \mainpage HEVC Encoder
|
|
|
|
*
|
|
|
|
* \section Coding style
|
|
|
|
*
|
|
|
|
* Coding style is explained in it's own document.
|
|
|
|
*
|
|
|
|
* \section usage_sec Usage
|
|
|
|
*
|
|
|
|
* \subsection encode_subsec Basic Decoding:
|
|
|
|
* Use encmain.exe -i input.yuv -o output.hevc
|
|
|
|
*
|
|
|
|
* \subsection options_subsec All program options:
|
|
|
|
* - -i <filename>: input
|
|
|
|
* - -o <filename>: output
|
|
|
|
* - -w <width>: frame width
|
|
|
|
* - -h <height>: frame height
|
|
|
|
* - -n <n>: encode only n frames
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Suppress some windows warnings */
|
|
|
|
#ifdef WIN32
|
|
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "global.h"
|
2012-05-30 12:26:39 +00:00
|
|
|
#include "config.h"
|
2012-06-01 12:31:06 +00:00
|
|
|
#include "encoder.h"
|
2012-06-04 10:47:12 +00:00
|
|
|
#include "cabac.h"
|
2012-06-05 11:01:47 +00:00
|
|
|
#include "picture.h"
|
2013-02-05 13:48:06 +00:00
|
|
|
#include "transform.h"
|
2012-05-30 12:10:23 +00:00
|
|
|
|
2013-04-10 13:55:31 +00:00
|
|
|
/* Assembly optimizations */
|
|
|
|
#ifndef X64
|
|
|
|
#include "x86/test.h"
|
|
|
|
#else
|
|
|
|
#include "x64/test64.h"
|
|
|
|
#endif
|
2012-05-30 12:10:23 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
\brief Program main function.
|
|
|
|
\param argc Argument count from commandline
|
|
|
|
\param argv Argument list
|
|
|
|
\return Program exit state
|
|
|
|
*/
|
|
|
|
int main(int argc, char* argv[])
|
2013-04-10 13:55:31 +00:00
|
|
|
{
|
|
|
|
int ecx = 0,edx =0;
|
2013-04-16 08:23:03 +00:00
|
|
|
/* 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};
|
2012-06-05 11:01:47 +00:00
|
|
|
uint32_t curFrame = 0;
|
2012-05-30 12:10:23 +00:00
|
|
|
config *cfg = NULL; /* Global configuration */
|
|
|
|
FILE *input = NULL;
|
|
|
|
FILE *output = NULL;
|
2013-03-12 15:06:21 +00:00
|
|
|
double PSNR[3] = { 0.0, 0.0, 0.0 };
|
2013-06-12 12:41:57 +00:00
|
|
|
fpos_t curpos = 0;
|
|
|
|
fpos_t lastpos = 0;
|
2013-03-11 14:26:09 +00:00
|
|
|
#ifdef _DEBUG
|
2013-03-07 15:42:00 +00:00
|
|
|
FILE *recout = fopen("encrec.yuv","wb");
|
2013-03-11 14:26:09 +00:00
|
|
|
#endif
|
2013-05-22 14:27:15 +00:00
|
|
|
encoder_control* encoder = (encoder_control*)malloc(sizeof(encoder_control));
|
2013-04-10 13:55:31 +00:00
|
|
|
|
2013-05-20 14:26:57 +00:00
|
|
|
|
2012-05-30 12:10:23 +00:00
|
|
|
|
|
|
|
/* Handle configuration */
|
|
|
|
cfg = config_alloc();
|
|
|
|
|
|
|
|
/* If problem with configuration, shutdown */
|
|
|
|
if(!config_init(cfg) || !config_read(cfg,argc,argv))
|
|
|
|
{
|
2012-06-05 11:01:47 +00:00
|
|
|
fprintf(stderr, "/***********************************************/\r\n");
|
|
|
|
fprintf(stderr, " * HEVC Encoder v. " VERSION_STRING "*\r\n");
|
2013-03-11 10:06:08 +00:00
|
|
|
fprintf(stderr, " * Tampere University of Technology 2013 *\r\n");
|
2012-06-05 11:01:47 +00:00
|
|
|
fprintf(stderr, "/***********************************************/\r\n\r\n");
|
2012-05-30 12:10:23 +00:00
|
|
|
|
|
|
|
fprintf(stderr, "Usage:\r\n");
|
|
|
|
fprintf(stderr, "encmain -i <input> -w <width> -h <height> -o <output>\r\n");
|
|
|
|
fprintf(stderr, "Optional parameters:\r\n");
|
|
|
|
fprintf(stderr, " -n <frames> : number of frames to decode\r\n");
|
|
|
|
fprintf(stderr, " -s <frames> : number of frames to skip from the beginning\r\n");
|
|
|
|
|
|
|
|
config_destroy(cfg);
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
2013-05-20 14:26:57 +00:00
|
|
|
/* CPU id */
|
|
|
|
|
|
|
|
#ifndef X64
|
2013-06-03 11:22:50 +00:00
|
|
|
//cpuId32(&ecx,&edx);
|
2013-05-20 14:26:57 +00:00
|
|
|
#else
|
|
|
|
cpuId64(&ecx,&edx);
|
2013-05-22 14:27:15 +00:00
|
|
|
#endif
|
2013-05-20 14:26:57 +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");
|
|
|
|
|
2012-06-04 10:47:12 +00:00
|
|
|
|
2012-06-05 14:45:17 +00:00
|
|
|
printf("Input: %s, output: %s\n", cfg->input, cfg->output);
|
|
|
|
printf(" Video size: %dx%d\n", cfg->width, cfg->height);
|
2012-05-30 12:10:23 +00:00
|
|
|
|
|
|
|
/* Open input file and check that it was opened correctly */
|
|
|
|
input = fopen(cfg->input, "rb");
|
|
|
|
if(input == NULL)
|
|
|
|
{
|
2012-06-05 14:45:17 +00:00
|
|
|
fprintf(stderr, "Could not open input file, shutting down!\n");
|
2012-05-30 12:10:23 +00:00
|
|
|
config_destroy(cfg);
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open output file and check that it was opened correctly */
|
|
|
|
output = fopen(cfg->output, "wb");
|
|
|
|
if(output == NULL)
|
|
|
|
{
|
2012-06-05 14:45:17 +00:00
|
|
|
fprintf(stderr, "Could not open output file, shutting down!\n");
|
2012-05-30 12:10:23 +00:00
|
|
|
config_destroy(cfg);
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2012-06-04 10:47:12 +00:00
|
|
|
/* Initialization */
|
2012-08-15 14:18:58 +00:00
|
|
|
init_tables();
|
2012-06-05 11:01:47 +00:00
|
|
|
init_exp_golomb(4096*8);
|
2012-06-04 10:47:12 +00:00
|
|
|
cabac_init(&cabac);
|
2013-02-05 13:48:06 +00:00
|
|
|
scalinglist_init();
|
2013-09-12 13:28:40 +00:00
|
|
|
init_encoder_control(encoder, (bitstream*)malloc(sizeof(bitstream)));
|
|
|
|
encoder->ref = picture_list_init(MAX_REF_PIC_COUNT);
|
2013-02-05 13:48:06 +00:00
|
|
|
|
2012-06-05 11:01:47 +00:00
|
|
|
/* Init bitstream */
|
|
|
|
bitstream_init(encoder->stream);
|
2012-06-05 12:38:54 +00:00
|
|
|
encoder->stream->buffer_pos = 0;
|
2012-06-05 14:45:17 +00:00
|
|
|
encoder->stream->output = 0;
|
2013-03-07 15:42:00 +00:00
|
|
|
/* Alloc 1MB */
|
2013-06-12 12:41:57 +00:00
|
|
|
bitstream_alloc(encoder->stream, 1024*2*cfg->width);
|
2012-06-05 11:01:47 +00:00
|
|
|
|
|
|
|
/* Config pointer to encoder struct */
|
|
|
|
encoder->cfg = cfg;
|
|
|
|
/* Set output file */
|
|
|
|
encoder->output = output;
|
|
|
|
/* Set CABAC output bitstream */
|
|
|
|
cabac.stream = encoder->stream;
|
|
|
|
|
2013-09-09 11:22:53 +00:00
|
|
|
/* input init (TODO: read from commandline / config) */
|
2013-02-06 14:31:01 +00:00
|
|
|
encoder->bitdepth = 8;
|
|
|
|
encoder->frame = 0;
|
2013-06-12 12:41:57 +00:00
|
|
|
encoder->QP = 32;
|
2012-06-12 14:35:45 +00:00
|
|
|
encoder->in.video_format = FORMAT_420;
|
2013-06-03 11:22:50 +00:00
|
|
|
/* deblocking */
|
2013-06-12 12:41:57 +00:00
|
|
|
encoder->deblock_enable = 1;
|
2013-09-18 11:49:01 +00:00
|
|
|
encoder->beta_offset_div2 = 0;
|
|
|
|
encoder->tc_offset_div2 = 0;
|
2013-06-03 11:22:50 +00:00
|
|
|
/* SAO */
|
|
|
|
encoder->sao_enable = 0;
|
|
|
|
|
2012-06-05 11:01:47 +00:00
|
|
|
init_encoder_input(&encoder->in, input, cfg->width, cfg->height);
|
|
|
|
|
|
|
|
/* Start coding cycle */
|
2013-03-11 10:06:08 +00:00
|
|
|
while(!feof(input) && (!cfg->frames || encoder->frame < cfg->frames))
|
2013-02-21 14:45:22 +00:00
|
|
|
{
|
2012-06-05 11:01:47 +00:00
|
|
|
/* Read one frame from the input */
|
2013-09-09 15:45:41 +00:00
|
|
|
read_one_frame(input, encoder);
|
2013-03-19 13:45:50 +00:00
|
|
|
|
2013-04-05 13:27:18 +00:00
|
|
|
/* Clear reconstruction buffers (not needed, for debugging) */
|
|
|
|
/*
|
2013-09-12 13:28:40 +00:00
|
|
|
memset(encoder->in.cur_pic->yRecData, 0, cfg->width*cfg->height);
|
|
|
|
memset(encoder->in.cur_pic->uRecData, 128, cfg->width*cfg->height>>2);
|
|
|
|
memset(encoder->in.cur_pic->vRecData, 128, cfg->width*cfg->height>>2);
|
2013-04-05 13:27:18 +00:00
|
|
|
*/
|
2013-03-19 13:45:50 +00:00
|
|
|
/* /////////////THE ACTUAL CODING HAPPENDS HERE\\\\\\\\\\\\\\\\\\\ */
|
2012-06-05 11:01:47 +00:00
|
|
|
encode_one_frame(encoder);
|
2013-04-18 11:04:15 +00:00
|
|
|
/* ////////////CODING NOW DONE\\\\\\\\\\\\\\\\\ */
|
2013-03-07 15:42:00 +00:00
|
|
|
|
2013-03-11 14:26:09 +00:00
|
|
|
#ifdef _DEBUG
|
|
|
|
/* Write reconstructed frame out */
|
2013-09-12 13:28:40 +00:00
|
|
|
fwrite(encoder->in.cur_pic->yRecData,cfg->width*cfg->height,1,recout);
|
|
|
|
fwrite(encoder->in.cur_pic->uRecData,cfg->width*cfg->height>>2,1,recout);
|
|
|
|
fwrite(encoder->in.cur_pic->vRecData,cfg->width*cfg->height>>2,1,recout);
|
2013-03-11 14:26:09 +00:00
|
|
|
#endif
|
2013-03-12 15:06:21 +00:00
|
|
|
{
|
2013-06-12 12:41:57 +00:00
|
|
|
int32_t diff;
|
2013-03-12 15:06:21 +00:00
|
|
|
double temp_PSNR[3];
|
2013-06-12 12:41:57 +00:00
|
|
|
fgetpos(output,&curpos);
|
|
|
|
diff = (int32_t)(curpos-lastpos);
|
|
|
|
lastpos = curpos;
|
|
|
|
|
2013-09-12 13:28:40 +00:00
|
|
|
temp_PSNR[0] = imagePSNR(encoder->in.cur_pic->yData,encoder->in.cur_pic->yRecData,cfg->width,cfg->height);
|
|
|
|
temp_PSNR[1] = imagePSNR(encoder->in.cur_pic->uData,encoder->in.cur_pic->uRecData,cfg->width>>1,cfg->height>>1);
|
|
|
|
temp_PSNR[2] = imagePSNR(encoder->in.cur_pic->vData,encoder->in.cur_pic->vRecData,cfg->width>>1,cfg->height>>1);
|
2013-04-18 11:04:15 +00:00
|
|
|
|
2013-09-12 13:28:40 +00:00
|
|
|
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,
|
2013-03-12 15:06:21 +00:00
|
|
|
temp_PSNR[0],temp_PSNR[1],temp_PSNR[2]);
|
|
|
|
PSNR[0]+=temp_PSNR[0];
|
|
|
|
PSNR[1]+=temp_PSNR[1];
|
|
|
|
PSNR[2]+=temp_PSNR[2];
|
|
|
|
}
|
2013-09-12 15:50:11 +00:00
|
|
|
|
|
|
|
/* 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 */
|
2013-09-18 11:49:01 +00:00
|
|
|
encoder->in.cur_pic = picture_init(encoder->in.width, encoder->in.height, encoder->in.width_in_lcu, encoder->in.height_in_lcu);
|
2013-09-12 15:50:11 +00:00
|
|
|
|
2012-06-05 11:01:47 +00:00
|
|
|
encoder->frame++;
|
|
|
|
}
|
|
|
|
/* Coding finished */
|
2013-06-12 12:41:57 +00:00
|
|
|
fgetpos(output,&curpos);
|
2012-06-05 11:01:47 +00:00
|
|
|
|
2013-06-12 12:41:57 +00:00
|
|
|
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);
|
2012-06-04 10:47:12 +00:00
|
|
|
|
2012-05-30 12:10:23 +00:00
|
|
|
fclose(input);
|
|
|
|
fclose(output);
|
2013-03-11 14:26:09 +00:00
|
|
|
#ifdef _DEBUG
|
2013-03-07 15:42:00 +00:00
|
|
|
fclose(recout);
|
2013-03-11 14:26:09 +00:00
|
|
|
#endif
|
2012-05-30 12:10:23 +00:00
|
|
|
|
2013-02-05 13:48:06 +00:00
|
|
|
/* Deallocating */
|
2012-05-30 12:10:23 +00:00
|
|
|
config_destroy(cfg);
|
2013-02-05 13:48:06 +00:00
|
|
|
scalinglist_destroy();
|
2013-09-12 14:38:08 +00:00
|
|
|
picture_list_destroy(encoder->ref);
|
2012-05-30 12:10:23 +00:00
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|