-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathevx1enc.cpp
More file actions
307 lines (245 loc) · 9.06 KB
/
evx1enc.cpp
File metadata and controls
307 lines (245 loc) · 9.06 KB
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
#include "evx1enc.h"
#include "config.h"
#include "convert.h"
#include "macroblock.h"
namespace evx {
evx_status engine_encode_frame(const image &input, const evx_frame &frame_desc, evx_context *context, bit_stream *output);
evx1_encoder_impl::evx1_encoder_impl()
{
initialized = false;
clear_frame(&frame);
clear_header(&header);
}
evx1_encoder_impl::~evx1_encoder_impl()
{
if (EVX_SUCCESS != clear())
{
evx_post_error(EVX_ERROR_EXECUTION_FAILURE);
}
}
evx_status evx1_encoder_impl::clear()
{
if (!initialized)
{
return EVX_SUCCESS;
}
clear_frame(&frame);
clear_context(&context);
initialized = false;
return EVX_SUCCESS;
}
evx_status evx1_encoder_impl::insert_intra()
{
// This allows the host to determine when to issue a new i-frame. Usually this
// is due to a dropped packet event which causes the encoder/decoder to lose
// sync and introduce a flush.
frame.type = EVX_FRAME_INTRA;
return EVX_SUCCESS;
}
evx_status evx1_encoder_impl::set_quality(uint8 quality)
{
quality = clip_range(quality, 1, 31);
// Quality can range from 1-31. Level 0 is the highest and should be
// set for efficiency research. For latency measurements, a level of
// between 16 and 24 is usually preferred.
frame.quality = quality;
return EVX_SUCCESS;
}
evx_status evx1_encoder_impl::initialize(uint32 width, uint32 height)
{
if (initialized)
{
// The encoder cannot be re-initialized unless it is first cleared.
return evx_post_error(EVX_ERROR_INVALID_RESOURCE);
}
// Initialize will place the encoder in a default state that is
// ready for encoding operations.
initialize_header(width, height, &header);
// Initialize image resources.
uint32 aligned_width = align(width, EVX_MACROBLOCK_SIZE);
uint32 aligned_height = align(height, EVX_MACROBLOCK_SIZE);
if (EVX_SUCCESS != initialize_context(aligned_width, aligned_height, &context))
{
return evx_post_error(EVX_ERROR_EXECUTION_FAILURE);
}
initialized = true;
return EVX_SUCCESS;
}
evx_status evx1_encoder_impl::encode(void *input, uint32 width, uint32 height, bit_stream *output)
{
if (EVX_PARAM_CHECK)
{
if (!output || 0 == width || 0 == height || !input)
{
return evx_post_error(EVX_ERROR_INVALIDARG);
}
}
// Encode's job is merely to setup the pipeline and ensure that the incoming
// frame matches the expected dimensions.
if (!initialized)
{
if (evx_failed(initialize(width, height)))
{
return evx_post_error(EVX_ERROR_EXECUTION_FAILURE);
}
// serialize out our header to begin the stream.
if (evx_failed(output->write_bytes(&header, sizeof(evx_header))))
{
return evx_post_error(EVX_ERROR_EXECUTION_FAILURE);
}
}
if (width != header.frame_width || height != header.frame_height)
{
// The incoming frame size does not match our current encoder settings.
// We could adapt or simply rip.
return evx_post_error(EVX_ERROR_INVALID_RESOURCE);
}
// Serialize our frame state.
if (evx_failed(output->write_bytes(&frame, sizeof(evx_frame))))
{
return evx_post_error(EVX_ERROR_EXECUTION_FAILURE);
}
if (evx_failed(encode_frame(input, width, height, output)))
{
return evx_post_error(EVX_ERROR_EXECUTION_FAILURE);
}
#if EVX_ALLOW_INTER_FRAMES
frame.type = EVX_FRAME_INTER;
#endif
// Periodically insert intra frames into the stream. This enables seekability
// and periodic error correction.
if (EVX_PERIODIC_INTRA_RATE)
{
if (0 == ((frame.index + 1) % EVX_PERIODIC_INTRA_RATE))
{
insert_intra();
}
}
frame.index++;
return EVX_SUCCESS;
}
evx_status evx1_encoder_impl::encode_frame(void *input, uint32 width, uint32 height, bit_stream *output)
{
image input_image;
if (evx_failed(create_image(EVX_IMAGE_FORMAT_R8G8B8, input, width, height, &input_image)))
{
return evx_post_error(EVX_ERROR_EXECUTION_FAILURE);
}
return engine_encode_frame(input_image, frame, &context, output);
}
evx_status evx1_encoder_impl::peek(EVX_PEEK_STATE peek_state, void *output)
{
if (EVX_PARAM_CHECK)
{
if (!output)
{
return evx_post_error(EVX_ERROR_INVALIDARG);
}
}
if (!initialized)
{
return EVX_SUCCESS;
}
image output_image;
if (evx_failed(create_image(EVX_IMAGE_FORMAT_R8G8B8, output, header.frame_width, header.frame_height, &output_image)))
{
return evx_post_error(EVX_ERROR_EXECUTION_FAILURE);
}
switch (peek_state)
{
case EVX_PEEK_SOURCE: return convert_image(context.cache_bank.input_cache, &output_image);
case EVX_PEEK_DESTINATION:
{
uint32 dest_index = query_prediction_index_by_offset(frame, 1);
return convert_image(context.cache_bank.prediction_cache[dest_index], &output_image);
}
case EVX_PEEK_BLOCK_TABLE:
{
for (uint32 j = 0; j < header.frame_height; j++)
for (uint32 i = 0; i < header.frame_width; i++)
{
uint8 *out_data = output_image.query_data() + output_image.query_block_offset(i, j);
uint32 block_index = (i / EVX_MACROBLOCK_SIZE) + (j / EVX_MACROBLOCK_SIZE) * context.width_in_blocks;
evx_block_desc *entry = &context.block_table[block_index];
// Pure red (255, 0, 0) is the most expensive type of block overall.
// Pure blue (0, 0, 255) is the least expensive type of block overall.
// Pure white (255, 255, 255) is the least expensive intra block.
// Pure black (0, 0, 0) is the least expensive inter block.
out_data[2] = 255 * EVX_IS_COPY_BLOCK_TYPE(entry->block_type);
out_data[1] = 255 * EVX_IS_MOTION_BLOCK_TYPE(entry->block_type);
out_data[0] = 255 * EVX_IS_INTRA_BLOCK_TYPE(entry->block_type);
}
} break;
case EVX_PEEK_QUANT_TABLE:
{
for (uint32 j = 0; j < header.frame_height; j++)
for (uint32 i = 0; i < header.frame_width; i++)
{
uint8 *out_data = output_image.query_data() + output_image.query_block_offset(i, j);
uint32 block_index = (i / EVX_MACROBLOCK_SIZE) + (j / EVX_MACROBLOCK_SIZE) * context.width_in_blocks;
evx_block_desc *entry = &context.block_table[block_index];
if (!EVX_IS_COPY_BLOCK_TYPE(entry->block_type))
{
out_data[0] = 255 - 15 * entry->q_index;
out_data[1] = 255 - 15 * entry->q_index;
out_data[2] = 255 - 15 * entry->q_index;
}
else
{
out_data[0] = 255;
out_data[1] = 0;
out_data[2] = 0;
}
}
} break;
case EVX_PEEK_BLOCK_VARIANCE:
{
for (uint32 j = 0; j < header.frame_height; j++)
for (uint32 i = 0; i < header.frame_width; i++)
{
uint8 *out_data = output_image.query_data() + output_image.query_block_offset(i, j);
uint32 block_index = (i / EVX_MACROBLOCK_SIZE) + (j / EVX_MACROBLOCK_SIZE) * context.width_in_blocks;
evx_block_desc *entry = &context.block_table[block_index];
if (!EVX_IS_COPY_BLOCK_TYPE(entry->block_type))
{
int16 value = clip_range(entry->variance / 30, 0, 255);
out_data[0] = value;
out_data[1] = value;
out_data[2] = value;
}
else
{
out_data[0] = 255;
out_data[1] = 0;
out_data[2] = 0;
}
}
} break;
case EVX_PEEK_SPMP_TABLE:
{
for (uint32 j = 0; j < header.frame_height; j++)
for (uint32 i = 0; i < header.frame_width; i++)
{
uint8 *out_data = output_image.query_data() + output_image.query_block_offset(i, j);
uint32 block_index = (i / EVX_MACROBLOCK_SIZE) + (j / EVX_MACROBLOCK_SIZE) * context.width_in_blocks;
evx_block_desc *entry = &context.block_table[block_index];
if (!entry->sp_pred)
{
// No sub-pixel motion prediction.
out_data[0] = out_data[1] = out_data[2] = 0;
}
else
{
// Blue = half pixel enabled
// Green = quarter pixel enabled
out_data[0] = 0;
out_data[1] = 255 * entry->sp_amount;
out_data[2] = 255 * !entry->sp_amount;
}
}
} break;
default: return EVX_ERROR_NOTIMPL;
}
return EVX_SUCCESS;
}
} // namespace evx