summaryrefslogtreecommitdiff
path: root/gpr/source/lib/vc5_encoder/codebooks.c
blob: c013f4d356aadc0c3ab36b44329bf5a88856421f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
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;
}