summaryrefslogtreecommitdiff
path: root/gpr/source/lib/vc5_encoder/codebooks.c
diff options
context:
space:
mode:
Diffstat (limited to 'gpr/source/lib/vc5_encoder/codebooks.c')
-rwxr-xr-xgpr/source/lib/vc5_encoder/codebooks.c425
1 files changed, 425 insertions, 0 deletions
diff --git a/gpr/source/lib/vc5_encoder/codebooks.c b/gpr/source/lib/vc5_encoder/codebooks.c
new file mode 100755
index 0000000..c013f4d
--- /dev/null
+++ b/gpr/source/lib/vc5_encoder/codebooks.c
@@ -0,0 +1,425 @@
+/*! @file codebooks.c
+ *
+ * @brief Implementation of the routines for computing the encoding tables from a codebook.
+ *
+ * (C) Copyright 2018 GoPro Inc (http://gopro.com/).
+ *
+ * Licensed under either:
+ * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0
+ * - MIT license, http://opensource.org/licenses/MIT
+ * at your option.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "headers.h"
+#include "table17.inc"
+
+//! Length of the codebook for runs of zeros
+#define RUNS_TABLE_LENGTH 3072
+
+
+/*!
+ @brief Define the codeset used by the reference codec
+
+ The baseline codec only supports codebook #17.
+
+ Codebook #17 is intended to be used with cubic companding
+ (see @ref FillMagnitudeEncodingTable and @ref ComputeCubicTable).
+ */
+ENCODER_CODESET encoder_codeset_17 = {
+ "Codebook set 17 from data by David Newman with tables automatically generated for the FSM decoder",
+ (const CODEBOOK *)&table17,
+ CODESET_FLAGS_COMPANDING_CUBIC,
+ NULL,
+ NULL,
+};
+
+/*!
+ @brief Initialize the codeset by creating more efficient tables for encoding
+
+ This routine takes the original codebook in the codeset and creates a table
+ of codewords for runs of zeros, indexed by the run length, and a table for
+ coefficient magnitudes, indexed by the coefficient magnitude. This allows
+ runs of zeros and non-zero coefficients to be entropy coded using a simple
+ table lookup.
+*/
+CODEC_ERROR PrepareCodebooks(const gpr_allocator *allocator, ENCODER_CODESET *cs)
+{
+ CODEC_ERROR error = CODEC_ERROR_OKAY;
+
+ // Create a new table for runs of zeros with the default length
+ const int runs_table_length = RUNS_TABLE_LENGTH;
+
+ // Initialize the indexable table of run length codes
+ RLV *old_codes = (RLV *)(((uint8_t *)cs->codebook) + sizeof(CODEBOOK));
+ int old_length = cs->codebook->length;
+
+ size_t runs_table_size = runs_table_length * sizeof(RLC) + sizeof(RUNS_TABLE);
+
+ RUNS_TABLE *runs_table = allocator->Alloc(runs_table_size);
+ RLC *new_codes = (RLC *)(((uint8_t *)runs_table) + sizeof(RUNS_TABLE));
+ int new_length = runs_table_length;
+
+ // Set the length of the table for encoding coefficient magnitudes
+ int mags_table_shift = 8;
+ int mags_table_length;
+ size_t mags_table_size;
+ MAGS_TABLE *mags_table;
+ VLE *mags_table_entries;
+
+ // Use a larger table if companding
+ if (CompandingParameter() > 0) {
+ //mags_table_shift = 11;
+ mags_table_shift = 10;
+ }
+
+ // Allocate the table for encoding coefficient magnitudes
+ mags_table_length = (1 << mags_table_shift);
+ mags_table_size = mags_table_length * sizeof(VLE) + sizeof(MAGS_TABLE);
+ mags_table = allocator->Alloc(mags_table_size);
+ if (mags_table == NULL) {
+ allocator->Free( (void *)runs_table);
+ return CODEC_ERROR_OUTOFMEMORY;
+ }
+
+ mags_table_entries = (VLE *)(((uint8_t *)mags_table) + sizeof(MAGS_TABLE));
+
+ // Create a more efficient codebook for encoding runs of zeros
+ error = ComputeRunLengthCodeTable(allocator,
+ old_codes, old_length, new_codes, new_length);
+ if (error != CODEC_ERROR_OKAY) {
+ allocator->Free( (void *)runs_table);
+ return error;
+ }
+
+ // Store the new table for runs of zeros in the codeset
+ runs_table->length = runs_table_length;
+ cs->runs_table = runs_table;
+
+ error = FillMagnitudeEncodingTable(cs->codebook, mags_table_entries, mags_table_length, cs->flags);
+ if (error != CODEC_ERROR_OKAY) {
+ allocator->Free( (void *)runs_table);
+ return error;
+ }
+
+ mags_table->length = mags_table_length;
+ cs->mags_table = mags_table;
+
+ // The codebooks have been initialized successfully
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Free all data structures allocated for the codebooks
+*/
+CODEC_ERROR ReleaseCodebooks(gpr_allocator *allocator, ENCODER_CODESET *cs)
+{
+ allocator->Free( (void *)cs->runs_table );
+ allocator->Free( (void *)cs->mags_table);
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Compute a table of codewords for runs of zeros
+
+ The table is indexed by the length of the run of zeros.
+*/
+CODEC_ERROR ComputeRunLengthCodeTable(const gpr_allocator *allocator,
+ RLV *input_codes, int input_length,
+ RLC *output_codes, int output_length)
+{
+ // Need enough space for the codebook and the code for a single value
+ int runs_codebook_length = input_length + 1;
+ size_t runs_codebook_size = runs_codebook_length * sizeof(RLC);
+ RLC *runs_codebook = (RLC *)allocator->Alloc(runs_codebook_size);
+ bool single_zero_run_flag = false;
+ int input_index;
+ int runs_codebook_count = 0;
+ CODEC_ERROR return_code = CODEC_ERROR_OKAY;
+
+ // Copy the codes for runs of zeros into the temporary codebook for sorting
+ for (input_index = 0; input_index < input_length; input_index++)
+ {
+ uint32_t count = input_codes[input_index].count;
+ int32_t value = input_codes[input_index].value;
+
+ // Is this code for a run of zeros?
+ if (value != 0 || count == 0) {
+ // Skip codebook entries for coefficient magnitudes and special codes
+ continue;
+ }
+
+ // Is this code for a single zero
+ if (count == 1 && value == 0) {
+ single_zero_run_flag = true;
+ }
+
+ // Check that the temporary runs codebook is not full
+ assert(runs_codebook_count < runs_codebook_length);
+
+ // Copy the code into the temporary runs codebook
+ runs_codebook[runs_codebook_count].size = input_codes[input_index].size;
+ runs_codebook[runs_codebook_count].bits = input_codes[input_index].bits;
+ runs_codebook[runs_codebook_count].count = count;
+
+ // Check the codebook entry
+ assert(runs_codebook[runs_codebook_count].size > 0);
+ assert(runs_codebook[runs_codebook_count].count > 0);
+
+ // Increment the count of codes in the temporary runs codebook
+ runs_codebook_count++;
+ }
+
+ // Check that the runs codebook includes a run of a single zero
+ if( single_zero_run_flag == false )
+ {
+ assert(false);
+ return_code = CODEC_ERROR_UNEXPECTED;
+ }
+
+ // Sort the codewords into decreasing run length
+ SortDecreasingRunLength(runs_codebook, runs_codebook_count);
+
+ // The last code must be for a single run
+ assert(runs_codebook[runs_codebook_count - 1].count == 1);
+
+ // Fill the lookup table with codes for runs indexed by the run length
+ FillRunLengthEncodingTable(runs_codebook, runs_codebook_count, output_codes, output_length);
+
+ // Free the space used for the sorted codewords
+ allocator->Free(runs_codebook);
+
+ return return_code;
+}
+
+/*!
+ @brief Sort the codebook into decreasing length of the run
+*/
+CODEC_ERROR SortDecreasingRunLength(RLC *codebook, int length)
+{
+ int i;
+ int j;
+
+ // Perform a simple bubble sort since the codebook may already be sorted
+ for (i = 0; i < length; i++)
+ {
+ for (j = i+1; j < length; j++)
+ {
+ // Should not have more than one codebook entry with the same run length
+ assert(codebook[i].count != codebook[j].count);
+
+ // Exchange codebook entries if the current entry is smaller
+ if (codebook[i].count < codebook[j].count)
+ {
+ int size = codebook[i].size;
+ uint32_t bits = codebook[i].bits;
+ int count = codebook[i].count;
+
+ codebook[i].size = codebook[j].size;
+ codebook[i].bits = codebook[j].bits;
+ codebook[i].count = codebook[j].count;
+
+ codebook[j].size = size;
+ codebook[j].bits = bits;
+ codebook[j].count = count;
+ }
+ }
+
+ // After two iterations that last two items should be in the proper order
+ assert(i == 0 || codebook[i-1].count > codebook[i].count);
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Use a sparse run length code table to create an indexable table for faster encoding
+*/
+CODEC_ERROR FillRunLengthEncodingTable(RLC *codebook, int codebook_length, RLC *table, int table_length)
+{
+ int i; // Index into the lookup table
+ int j; // Index into the codebook
+
+ // Use all of the bits except the sign bit for the codewords
+ int max_code_size = bit_word_count - 1;
+
+ // Check that the input codes are sorted into decreasing run length
+ for (j = 1; j < codebook_length; j++)
+ {
+ RLC *previous = &codebook[j-1];
+ RLC *current = &codebook[j];
+
+ assert(previous->count > current->count);
+ if (! (previous->count > current->count)) {
+ return CODEC_ERROR_UNEXPECTED;
+ }
+ }
+
+ // The last input code should be the code for a single zero
+ assert(codebook[codebook_length - 1].count == 1);
+
+ // Create the shortest codeword for each table entry
+ for (i = 0; i < table_length; i++)
+ {
+ int length = i; // Length of the run for this table entry
+ uint32_t codeword = 0; // Composite codeword for this run length
+ int codesize = 0; // Number of bits in the composite codeword
+ int remaining; // Remaining run length not covered by the codeword
+
+ remaining = length;
+
+ for (j = 0; j < codebook_length; j++)
+ {
+ int repetition; // Number of times the codeword is used
+ int k;
+
+ // Nothing to do if the remaining run length is zero
+ if (remaining == 0) break;
+
+ // The number of times that the codeword is used is the number
+ // of times that it divides evenly into the remaining run length
+ repetition = remaining / codebook[j].count;
+
+ // Append the codes to the end of the composite codeword
+ for (k = 0; k < repetition; k++)
+ {
+ // Terminate the loop if the codeword will not fit
+ if (codebook[j].size > (max_code_size - codesize))
+ {
+ if (codesize)
+ {
+ remaining -= (k * codebook[j].count);
+ goto next;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // Shift the codeword to make room for the appended codes
+ codeword <<= codebook[j].size;
+
+ // Insert the codeword from the codebook at the end of the composite codeword
+ codeword |= codebook[j].bits;
+
+ // Increment the number of bits in the composite codeword
+ codesize += codebook[j].size;
+ }
+
+ // Reduce the run length by the amount that was consumed by the repeated codeword
+ remaining -= (k * codebook[j].count);
+ }
+
+next:
+ // Should have covered the entire run if the last codeword would fit
+ //assert(remaining == 0 || (max_code_size - codesize) < codebook[codebook_length - 1].size);
+
+ // Store the composite run length in the lookup table
+ table[i].bits = codeword;
+ table[i].size = codesize;
+ table[i].count = length - remaining;
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Fill lookup table for encoding coefficient magnitudes
+
+ The table for encoding coefficient magnitudes is indexed by the magnitude.
+ Each entry is a codeword and the size in bits.
+
+ @todo Implement cubic companding
+*/
+CODEC_ERROR FillMagnitudeEncodingTable(const CODEBOOK *codebook, VLE *mags_table_entry, int mags_table_length, uint32_t flags)
+{
+ // Get the length of the codebook and a pointer to the entries
+ //int codebook_length = codebook->length;
+ RLV *codebook_entry = (RLV *)((uint8_t *)codebook + sizeof(CODEBOOK));
+
+ int32_t maximum_magnitude_value = 0;
+
+ uint32_t codebook_index;
+
+ int16_t cubic_table[1025];
+ int cubic_table_length = sizeof(cubic_table) / sizeof(cubic_table[0]);
+
+ int mags_table_index;
+
+ // Find the maximum coefficient magnitude in the codebook
+ for (codebook_index = 0; codebook_index < codebook->length; codebook_index++)
+ {
+ // Does this codebook entry correspond to a coefficient magnitude?
+ if (codebook_entry[codebook_index].count == 1)
+ {
+ int32_t codebook_value = codebook_entry[codebook_index].value;
+ if (maximum_magnitude_value < codebook_value) {
+ maximum_magnitude_value = codebook_value;
+ }
+ }
+ }
+ assert(maximum_magnitude_value > 0);
+
+ if (flags & CODESET_FLAGS_COMPANDING_CUBIC)
+ {
+ ComputeCubicTable(cubic_table, cubic_table_length, maximum_magnitude_value);
+ }
+
+ // Fill each table entry with the codeword for that (signed) value
+ for (mags_table_index = 0; mags_table_index < mags_table_length; mags_table_index++)
+ {
+ // Compute the magnitude that corresponds to this index
+ int32_t magnitude = mags_table_index;
+ uint32_t codeword;
+ int codesize;
+
+ // Apply the companding curve
+ if (flags & CODESET_FLAGS_COMPANDING_CUBIC)
+ {
+ // Apply a cubic companding curve
+ assert(magnitude < cubic_table_length);
+ magnitude = cubic_table[magnitude];
+ }
+ else if (flags & CODESET_FLAGS_COMPANDING_NONE)
+ {
+ // Do not apply a companding curve
+ }
+ else
+ {
+ // Apply an old-style companding curve
+ magnitude = CompandedValue(magnitude);
+ }
+
+ // Is the magnitude larger than the number of entries in the codebook?
+ if (magnitude > maximum_magnitude_value) {
+ magnitude = maximum_magnitude_value;
+ }
+
+ // Find the codebook entry corresponding to this coefficient magnitude
+ codeword = 0;
+ codesize = 0;
+ for (codebook_index = 0; codebook_index < codebook->length; codebook_index++)
+ {
+ if (codebook_entry[codebook_index].value == magnitude)
+ {
+ assert(codebook_entry[codebook_index].count == 1);
+ codeword = codebook_entry[codebook_index].bits;
+ codesize = codebook_entry[codebook_index].size;
+ break;
+ }
+ }
+ assert(0 < codesize && codesize <= 32);
+
+ mags_table_entry[mags_table_index].bits = codeword;
+ mags_table_entry[mags_table_index].size = codesize;
+
+ }
+
+ return CODEC_ERROR_OKAY;
+}