From ad11d1bca50e409cb541eac36e75fa15ad8fc53b Mon Sep 17 00:00:00 2001 From: Ari Koivula Date: Mon, 18 May 2015 11:43:10 +0300 Subject: [PATCH] Add kvazaar.h to hold high-level encoder API. - Move encoder initialization from main to kvazaar.c. - Have main use the API for initialization. Conflicts: src/encmain.c --- build/kvazaar_lib/kvazaar_lib.vcxproj | 3 + build/kvazaar_lib/kvazaar_lib.vcxproj.filters | 9 ++ src/Makefile | 1 + src/config.h | 2 +- src/encmain.c | 76 ++++------ src/kvazaar.c | 130 ++++++++++++++++++ src/kvazaar.h | 106 ++++++++++++++ src/kvazaar_version.h | 26 ++++ 8 files changed, 306 insertions(+), 47 deletions(-) create mode 100644 src/kvazaar.c create mode 100644 src/kvazaar.h create mode 100644 src/kvazaar_version.h diff --git a/build/kvazaar_lib/kvazaar_lib.vcxproj b/build/kvazaar_lib/kvazaar_lib.vcxproj index 0088b920..498d8b3b 100644 --- a/build/kvazaar_lib/kvazaar_lib.vcxproj +++ b/build/kvazaar_lib/kvazaar_lib.vcxproj @@ -125,6 +125,7 @@ + @@ -205,6 +206,8 @@ + + diff --git a/build/kvazaar_lib/kvazaar_lib.vcxproj.filters b/build/kvazaar_lib/kvazaar_lib.vcxproj.filters index ba867cd0..20e17bff 100644 --- a/build/kvazaar_lib/kvazaar_lib.vcxproj.filters +++ b/build/kvazaar_lib/kvazaar_lib.vcxproj.filters @@ -189,6 +189,9 @@ Source Files + + Source Files + Source Files @@ -335,6 +338,12 @@ Header Files + + Header Files + + + Header Files + Header Files diff --git a/src/Makefile b/src/Makefile index 9a914f3a..b2f39d12 100644 --- a/src/Makefile +++ b/src/Makefile @@ -91,6 +91,7 @@ OBJS = \ filter.o \ inter.o \ intra.o \ + kvazaar.o \ nal.o \ imagelist.o \ rdo.o \ diff --git a/src/config.h b/src/config.h index 65da7243..79f2a543 100644 --- a/src/config.h +++ b/src/config.h @@ -43,7 +43,7 @@ typedef struct { /*! \brief Struct which contains all configuration data */ -typedef struct +typedef struct config_t { char *input; /*!< \brief Pointer to input filename */ char *output; /*!< \brief Pointer to output filename */ diff --git a/src/encmain.c b/src/encmain.c index 455c5aa5..4ab4cbc3 100644 --- a/src/encmain.c +++ b/src/encmain.c @@ -47,6 +47,7 @@ #include "scalinglist.h" #include "strategyselector.h" #include "cli.h" +#include "kvazaar.h" /** @@ -60,7 +61,6 @@ int main(int argc, char *argv[]) config_t *cfg = NULL; //!< Global configuration FILE *input = NULL; //!< input file (YUV) FILE *output = NULL; //!< output file (HEVC NAL stream) - encoder_control_t encoder; uint64_t curpos = 0; FILE *recout = NULL; //!< reconstructed YUV output, --debug clock_t start_time = clock(); @@ -139,48 +139,33 @@ int main(int argc, char *argv[]) goto exit_failure; } - if (!encoder_control_init(&encoder, cfg)) { + const kvz_api *api = kvz_api_get(8); + + kvz_encoder* enc = api->encoder_open(cfg); + if (!enc) { + fprintf(stderr, "Failed to open encoder."); goto exit_failure; } + + + for (unsigned i = 0; i <= cfg->owf; ++i) { + enc->states[i].stream.file.output = output; + } + + encoder_control_t *encoder = enc->control; fprintf(stderr, "Input: %s, output: %s\n", cfg->input, cfg->output); fprintf(stderr, " Video size: %dx%d (input=%dx%d)\n", - encoder.in.width, encoder.in.height, - encoder.in.real_width, encoder.in.real_height); + encoder->in.width, encoder->in.height, + encoder->in.real_width, encoder->in.real_height); //Now, do the real stuff { - encoder_state_t *encoder_states = malloc((encoder.owf + 1) * sizeof(encoder_state_t)); - if (encoder_states == NULL) { - fprintf(stderr, "Failed to allocate memory."); - goto exit_failure; - } int i; int current_encoder_state = 0; - for (i = 0; i <= encoder.owf; ++i) { - encoder_states[i].encoder_control = &encoder; - if (i > 0) { - encoder_states[i].previous_encoder_state = &encoder_states[i-1]; - } else { - //i == 0, use last encoder as the previous one - encoder_states[i].previous_encoder_state = &encoder_states[encoder.owf]; - } - if (!encoder_state_init(&encoder_states[i], NULL)) { - goto exit_failure; - } - encoder_states[i].stream.file.output = output; - encoder_states[i].global->QP = (int8_t)encoder.cfg->qp; - } - for (i = 0; i <= encoder.owf; ++i) { - encoder_state_match_children_of_previous_frame(&encoder_states[i]); - } - //Initial frame - encoder_states[current_encoder_state].global->frame = -1; - - // Skip '--seek' frames before input. if (cfg->seek > 0) { int frame_bytes = cfg->width * cfg->height * 3 / 2; int error = 0; @@ -189,7 +174,7 @@ int main(int argc, char *argv[]) // Input is stdin. int i; for (i = 0; !error && i < cfg->seek; ++i) { - error = !read_one_frame(input, &encoder_states[current_encoder_state]); + error = !read_one_frame(input, &enc->states[current_encoder_state]); } } else { // input is a file. We hope. Proper detection is OS dependent. @@ -211,13 +196,13 @@ int main(int argc, char *argv[]) // Start coding cycle while data on input and not on the last frame while (cfg->frames == 0 || frames_started < cfg->frames) { - encoder_state_t *state = &encoder_states[current_encoder_state]; + encoder_state_t *state = &enc->states[current_encoder_state]; // If we have started as many frames as we are going to encode in parallel, wait for the first one we started encoding to finish before // encoding more. if (frames_started > cfg->owf) { double frame_psnr[3] = { 0.0, 0.0, 0.0 }; - encoder_compute_stats(&encoder_states[current_encoder_state], recout, frame_psnr, &bitstream_length); + encoder_compute_stats(&enc->states[current_encoder_state], recout, frame_psnr, &bitstream_length); frames_done += 1; psnr_sum[0] += frame_psnr[0]; psnr_sum[1] += frame_psnr[1]; @@ -228,25 +213,25 @@ int main(int argc, char *argv[]) frames_started += 1; //Clear encoder - encoder_next_frame(&encoder_states[current_encoder_state]); + encoder_next_frame(&enc->states[current_encoder_state]); CHECKPOINT_MARK("read source frame: %d", encoder_states[current_encoder_state].global->frame + cfg->seek); // Read one frame from the input - if (!read_one_frame(input, &encoder_states[current_encoder_state])) { + if (!read_one_frame(input, &enc->states[current_encoder_state])) { if (!feof(input)) - fprintf(stderr, "Failed to read a frame %d\n", encoder_states[current_encoder_state].global->frame); + fprintf(stderr, "Failed to read a frame %d\n", enc->states[current_encoder_state].global->frame); //Ignore this frame, which is not valid... - encoder_states[current_encoder_state].stats_done = 1; + enc->states[current_encoder_state].stats_done = 1; break; } // The actual coding happens here, after this function we have a coded frame - encode_one_frame(&encoder_states[current_encoder_state]); + encode_one_frame(&enc->states[current_encoder_state]); //Switch to the next encoder - current_encoder_state = (current_encoder_state + 1) % (encoder.owf + 1); + current_encoder_state = (current_encoder_state + 1) % (encoder->owf + 1); } //Compute stats for the remaining encoders @@ -254,7 +239,7 @@ int main(int argc, char *argv[]) int first_enc = current_encoder_state; do { double frame_psnr[3] = { 0.0, 0.0, 0.0 }; - encoder_state_t *state = &encoder_states[current_encoder_state]; + encoder_state_t *state = &enc->states[current_encoder_state]; if (!state->stats_done) { encoder_compute_stats(state, recout, frame_psnr, &bitstream_length); @@ -265,14 +250,14 @@ int main(int argc, char *argv[]) psnr_sum[2] += frame_psnr[2]; } - current_encoder_state = (current_encoder_state + 1) % (encoder.owf + 1); + current_encoder_state = (current_encoder_state + 1) % (encoder->owf + 1); } while (current_encoder_state != first_enc); } GET_TIME(&encoding_end_real_time); encoding_end_cpu_time = clock(); - threadqueue_flush(encoder.threadqueue); + threadqueue_flush(encoder->threadqueue); // Coding finished @@ -297,16 +282,15 @@ int main(int argc, char *argv[]) fclose(output); if(recout != NULL) fclose(recout); - for (i = 0; i <= encoder.owf; ++i) { - encoder_state_finalize(&encoder_states[i]); + for (i = 0; i <= encoder->owf; ++i) { + encoder_state_finalize(&enc->states[i]); } - free(encoder_states); + api->encoder_close(enc); } // Deallocating config_destroy(cfg); - encoder_control_finalize(&encoder); free_exp_golomb(); diff --git a/src/kvazaar.c b/src/kvazaar.c new file mode 100644 index 00000000..c18f920b --- /dev/null +++ b/src/kvazaar.c @@ -0,0 +1,130 @@ +/***************************************************************************** +* 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 . +****************************************************************************/ + +#include "kvazaar.h" + +#include + +#include "config.h" +#include "encoder.h" +#include "strategyselector.h" +#include "encoderstate.h" + + +static void kvazaar_close(kvz_encoder *encoder) +{ + if (encoder) { + if (encoder->control) { + encoder_control_finalize(encoder->control); + } + FREE_POINTER(encoder->control); + FREE_POINTER(encoder->states); + } + FREE_POINTER(encoder); +} + + +static kvz_encoder * kvazaar_open(config_t *cfg) +{ + kvz_encoder *encoder = NULL; + + //Initialize strategies + // TODO: Make strategies non-global + if (!strategyselector_init(cfg->cpuid)) { + fprintf(stderr, "Failed to initialize strategies.\n"); + goto kvazaar_open_failure; + } + + //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"); + goto kvazaar_open_failure; + } + + encoder = MALLOC(kvz_encoder, 1); + if (!encoder) { + goto kvazaar_open_failure; + } + + encoder->control = MALLOC(encoder_control_t, 1); + if (!encoder->control || !encoder_control_init(encoder->control, cfg)) { + goto kvazaar_open_failure; + } + + encoder->num_encoder_states = cfg->owf + 1; + encoder->cur_state_num = 0; + encoder->states = MALLOC(encoder_state_t, encoder->num_encoder_states); + if (!encoder->states) { + goto kvazaar_open_failure; + } + + for (unsigned i = 0; i <= cfg->owf; ++i) { + encoder->states[i].encoder_control = encoder->control; + + if (!encoder_state_init(&encoder->states[i], NULL)) { + goto kvazaar_open_failure; + } + + encoder->states[i].global->QP = (int8_t)cfg->qp; + } + + for (int i = 0; i <= cfg->owf; ++i) { + if (i == 0) { + encoder->states[i].previous_encoder_state = &encoder->states[encoder->num_encoder_states - 1]; + } else { + encoder->states[i].previous_encoder_state = &encoder->states[(i - 1) % encoder->num_encoder_states]; + } + encoder_state_match_children_of_previous_frame(&encoder->states[i]); + } + + encoder->states[encoder->cur_state_num].global->frame = -1; + + return encoder; + +kvazaar_open_failure: + kvazaar_close(encoder); + return NULL; +} + + +static int kvazaar_encode(kvz_encoder *encoder, kvz_picture *pic_in, kvz_picture *pic_out, kvz_payload **payload) +{ + return 0; +} + +kvz_api kvz_8bit_api = { + .config_alloc = config_alloc, + .config_init = config_init, + .config_read = config_read, + .config_destroy = config_destroy, + + .picture_create = NULL, + .picture_destroy = NULL, + + .encoder_open = kvazaar_open, + .encoder_close = kvazaar_close, + .encoder_encode = kvazaar_encode, +}; + + +const kvz_api * kvz_api_get(int bit_depth) +{ + return &kvz_8bit_api; +} diff --git a/src/kvazaar.h b/src/kvazaar.h new file mode 100644 index 00000000..5ad238db --- /dev/null +++ b/src/kvazaar.h @@ -0,0 +1,106 @@ +#ifndef KVAZAAR_H_ +#define KVAZAAR_H_ +/***************************************************************************** +* 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 . +****************************************************************************/ + +/** + * \file + * \brief This file defines the public API of Kvazaar when used as a library. + */ + +#include +#include + +#include "kvazaar_version.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct config_t kvz_cfg; +typedef struct encoder_state_t encoder_state_t; +typedef struct kvz_payload kvz_payload; +typedef struct encoder_control_t encoder_control_t; + +/** +* A payload unit containing at most a single frame. +* If next is not NULL, the bytestream continues in that payload unit. +*/ +typedef struct kvz_payload { + uint32_t type; + uint32_t size_bytes; + uint8_t *payload; + kvz_payload *next; +} kvz_payload; + +typedef struct kvz_picture { + void* planes[3]; + uint32_t stride[3]; + uint8_t bit_depth; + uint8_t poc; +} kvz_picture; + +/** + * Main datastructure representing one instance of the encoder. + * - encoder_open + * - encoder_close + */ +typedef struct kvz_encoder { + encoder_control_t* control; + encoder_state_t* states; + int num_encoder_states; + int cur_state_num; + + size_t bitstream_length; +} kvz_encoder; + + +typedef struct kvz_api { + kvz_cfg * (*config_alloc)(void); + int (*config_destroy)(kvz_cfg *); + int (*config_init)(kvz_cfg *); + int (*config_read)(kvz_cfg *, int argc, char *argv[]); + + kvz_picture * (*picture_create)(void); + void (*picture_destroy)(kvz_picture *); + + kvz_encoder * (*encoder_open)(kvz_cfg *); + void (*encoder_close)(kvz_encoder *); + + // \brief Encode one picture. + // \param encoder + // \param pic_in Picture containing the encoded data. + // \param pic_out Picture containing the reconstructed data. + // \param nals_out The first NAL containing bitstream generated, or NULL. + // \return 1 on success, negative on error. + int (*encoder_encode)(kvz_encoder *encoder, kvz_picture *pic_in, kvz_picture *pic_out, kvz_payload **payload); +} kvz_api; + +// Append API version to the getters name to prevent linking against incompatible versions. +#define KVZ_API_CONCAT(func, version) func ## _apiv ## version +#define KVZ_API_EXPAND_VERSION(func, version) KVZ_API_CONCAT(func, version) +#define kvz_api_get KVZ_API_EXPAND_VERSION(kvz_api_get, KVZ_API_VERSION) +const kvz_api* kvz_api_get(int bit_depth); + +#ifdef __cplusplus +} +#endif + +#endif // KVAZAAR_H_ diff --git a/src/kvazaar_version.h b/src/kvazaar_version.h new file mode 100644 index 00000000..d1e87cd8 --- /dev/null +++ b/src/kvazaar_version.h @@ -0,0 +1,26 @@ +#ifndef KVAZAAR_VERSION_H_ +#define KVAZAAR_VERSION_H_ +/***************************************************************************** +* 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 . +****************************************************************************/ + +// KVZ_API_VERSION is incremented every time the public api changes. +#define KVZ_API_VERSION 1 + +#endif // KVAZAAR_VERSION_H_