FFmpeg
dnn_backend_native.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018 Sergey Lavrushkin
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /**
22  * @file
23  * DNN native backend implementation.
24  */
25 
26 #include "dnn_backend_native.h"
27 #include "libavutil/avassert.h"
30 
31 static DNNReturnType get_input_native(void *model, DNNData *input, const char *input_name)
32 {
33  ConvolutionalNetwork *network = (ConvolutionalNetwork *)model;
34 
35  for (int i = 0; i < network->operands_num; ++i) {
36  DnnOperand *oprd = &network->operands[i];
37  if (strcmp(oprd->name, input_name) == 0) {
38  if (oprd->type != DOT_INPUT)
39  return DNN_ERROR;
40  input->dt = oprd->data_type;
41  av_assert0(oprd->dims[0] == 1);
42  input->height = oprd->dims[1];
43  input->width = oprd->dims[2];
44  input->channels = oprd->dims[3];
45  return DNN_SUCCESS;
46  }
47  }
48 
49  // do not find the input operand
50  return DNN_ERROR;
51 }
52 
53 static DNNReturnType set_input_output_native(void *model, DNNData *input, const char *input_name, const char **output_names, uint32_t nb_output)
54 {
55  ConvolutionalNetwork *network = (ConvolutionalNetwork *)model;
56  DnnOperand *oprd = NULL;
57 
58  if (network->layers_num <= 0 || network->operands_num <= 0)
59  return DNN_ERROR;
60 
61  /* inputs */
62  for (int i = 0; i < network->operands_num; ++i) {
63  oprd = &network->operands[i];
64  if (strcmp(oprd->name, input_name) == 0) {
65  if (oprd->type != DOT_INPUT)
66  return DNN_ERROR;
67  break;
68  }
69  oprd = NULL;
70  }
71 
72  if (!oprd)
73  return DNN_ERROR;
74 
75  oprd->dims[0] = 1;
76  oprd->dims[1] = input->height;
77  oprd->dims[2] = input->width;
78  oprd->dims[3] = input->channels;
79 
80  av_freep(&oprd->data);
82  if (oprd->length <= 0)
83  return DNN_ERROR;
84  oprd->data = av_malloc(oprd->length);
85  if (!oprd->data)
86  return DNN_ERROR;
87 
88  input->data = oprd->data;
89 
90  /* outputs */
91  network->nb_output = 0;
92  av_freep(&network->output_indexes);
93  network->output_indexes = av_mallocz_array(nb_output, sizeof(*network->output_indexes));
94  if (!network->output_indexes)
95  return DNN_ERROR;
96 
97  for (uint32_t i = 0; i < nb_output; ++i) {
98  const char *output_name = output_names[i];
99  for (int j = 0; j < network->operands_num; ++j) {
100  oprd = &network->operands[j];
101  if (strcmp(oprd->name, output_name) == 0) {
102  network->output_indexes[network->nb_output++] = j;
103  break;
104  }
105  }
106  }
107 
108  if (network->nb_output != nb_output)
109  return DNN_ERROR;
110 
111  return DNN_SUCCESS;
112 }
113 
114 // Loads model and its parameters that are stored in a binary file with following structure:
115 // layers_num,layer_type,layer_parameterss,layer_type,layer_parameters...
116 // For CONV layer: activation_function, input_num, output_num, kernel_size, kernel, biases
117 // For DEPTH_TO_SPACE layer: block_size
118 DNNModel *ff_dnn_load_model_native(const char *model_filename)
119 {
120  DNNModel *model = NULL;
121  char header_expected[] = "FFMPEGDNNNATIVE";
122  char *buf;
123  size_t size;
124  int version, header_size, major_version_expected = 1;
125  ConvolutionalNetwork *network = NULL;
126  AVIOContext *model_file_context;
127  int file_size, dnn_size, parsed_size;
128  int32_t layer;
129  DNNLayerType layer_type;
130 
131  if (avio_open(&model_file_context, model_filename, AVIO_FLAG_READ) < 0){
132  return NULL;
133  }
134  file_size = avio_size(model_file_context);
135 
136  model = av_mallocz(sizeof(DNNModel));
137  if (!model){
138  goto fail;
139  }
140 
141  /**
142  * check file header with string and version
143  */
144  size = sizeof(header_expected);
145  buf = av_malloc(size);
146  if (!buf) {
147  goto fail;
148  }
149 
150  // size - 1 to skip the ending '\0' which is not saved in file
151  avio_get_str(model_file_context, size - 1, buf, size);
152  dnn_size = size - 1;
153  if (strncmp(buf, header_expected, size) != 0) {
154  av_freep(&buf);
155  goto fail;
156  }
157  av_freep(&buf);
158 
159  version = (int32_t)avio_rl32(model_file_context);
160  dnn_size += 4;
161  if (version != major_version_expected) {
162  goto fail;
163  }
164 
165  // currently no need to check minor version
166  version = (int32_t)avio_rl32(model_file_context);
167  dnn_size += 4;
168  header_size = dnn_size;
169 
170  network = av_mallocz(sizeof(ConvolutionalNetwork));
171  if (!network){
172  goto fail;
173  }
174  model->model = (void *)network;
175 
176  avio_seek(model_file_context, file_size - 8, SEEK_SET);
177  network->layers_num = (int32_t)avio_rl32(model_file_context);
178  network->operands_num = (int32_t)avio_rl32(model_file_context);
179  dnn_size += 8;
180  avio_seek(model_file_context, header_size, SEEK_SET);
181 
182  network->layers = av_mallocz(network->layers_num * sizeof(Layer));
183  if (!network->layers){
184  goto fail;
185  }
186 
187  network->operands = av_mallocz(network->operands_num * sizeof(DnnOperand));
188  if (!network->operands){
189  goto fail;
190  }
191 
192  for (layer = 0; layer < network->layers_num; ++layer){
193  layer_type = (int32_t)avio_rl32(model_file_context);
194  dnn_size += 4;
195 
196  if (layer_type >= DLT_COUNT) {
197  goto fail;
198  }
199 
200  network->layers[layer].type = layer_type;
201  parsed_size = layer_funcs[layer_type].pf_load(&network->layers[layer], model_file_context, file_size, network->operands_num);
202  if (!parsed_size) {
203  goto fail;
204  }
205  dnn_size += parsed_size;
206  }
207 
208  for (int32_t i = 0; i < network->operands_num; ++i){
209  DnnOperand *oprd;
210  int32_t name_len;
211  int32_t operand_index = (int32_t)avio_rl32(model_file_context);
212  dnn_size += 4;
213 
214  if (operand_index >= network->operands_num) {
215  goto fail;
216  }
217 
218  oprd = &network->operands[operand_index];
219  name_len = (int32_t)avio_rl32(model_file_context);
220  dnn_size += 4;
221 
222  avio_get_str(model_file_context, name_len, oprd->name, sizeof(oprd->name));
223  dnn_size += name_len;
224 
225  oprd->type = (int32_t)avio_rl32(model_file_context);
226  dnn_size += 4;
227 
228  oprd->data_type = (int32_t)avio_rl32(model_file_context);
229  dnn_size += 4;
230 
231  for (int32_t dim = 0; dim < 4; ++dim) {
232  oprd->dims[dim] = (int32_t)avio_rl32(model_file_context);
233  dnn_size += 4;
234  }
235 
236  oprd->isNHWC = 1;
237  }
238 
239  avio_closep(&model_file_context);
240 
241  if (dnn_size != file_size){
242  ff_dnn_free_model_native(&model);
243  return NULL;
244  }
245 
247  model->get_input = &get_input_native;
248 
249  return model;
250 
251 fail:
252  ff_dnn_free_model_native(&model);
253  avio_closep(&model_file_context);
254  return NULL;
255 }
256 
258 {
259  ConvolutionalNetwork *network = (ConvolutionalNetwork *)model->model;
260  int32_t layer;
261  uint32_t nb = FFMIN(nb_output, network->nb_output);
262 
263  if (network->layers_num <= 0 || network->operands_num <= 0)
264  return DNN_ERROR;
265  if (!network->operands[0].data)
266  return DNN_ERROR;
267 
268  for (layer = 0; layer < network->layers_num; ++layer){
269  DNNLayerType layer_type = network->layers[layer].type;
270  layer_funcs[layer_type].pf_exec(network->operands,
271  network->layers[layer].input_operand_indexes,
272  network->layers[layer].output_operand_index,
273  network->layers[layer].params);
274  }
275 
276  for (uint32_t i = 0; i < nb; ++i) {
277  DnnOperand *oprd = &network->operands[network->output_indexes[i]];
278  outputs[i].data = oprd->data;
279  outputs[i].height = oprd->dims[1];
280  outputs[i].width = oprd->dims[2];
281  outputs[i].channels = oprd->dims[3];
282  outputs[i].dt = oprd->data_type;
283  }
284 
285  return DNN_SUCCESS;
286 }
287 
289 {
290  int32_t result = 1;
291  for (int i = 0; i < 4; ++i)
292  result *= oprd->dims[i];
293 
294  return result;
295 }
296 
298 {
299  // currently, we just support DNN_FLOAT
300  uint64_t len = sizeof(float);
301  for (int i = 0; i < 4; i++) {
302  len *= oprd->dims[i];
303  if (len > INT32_MAX)
304  return 0;
305  }
306  return len;
307 }
308 
310 {
311  ConvolutionalNetwork *network;
312  ConvolutionalParams *conv_params;
313  int32_t layer;
314 
315  if (*model)
316  {
317  if ((*model)->model) {
318  network = (ConvolutionalNetwork *)(*model)->model;
319  if (network->layers) {
320  for (layer = 0; layer < network->layers_num; ++layer){
321  if (network->layers[layer].type == DLT_CONV2D){
322  conv_params = (ConvolutionalParams *)network->layers[layer].params;
323  av_freep(&conv_params->kernel);
324  av_freep(&conv_params->biases);
325  }
326  av_freep(&network->layers[layer].params);
327  }
328  av_freep(&network->layers);
329  }
330 
331  if (network->operands) {
332  for (uint32_t operand = 0; operand < network->operands_num; ++operand)
333  av_freep(&network->operands[operand].data);
334  av_freep(&network->operands);
335  }
336 
337  av_freep(&network->output_indexes);
338  av_freep(&network);
339  }
340  av_freep(model);
341  }
342 }
DLT_COUNT
@ DLT_COUNT
Definition: dnn_backend_native.h:46
calculate_operand_data_length
int32_t calculate_operand_data_length(const DnnOperand *oprd)
Definition: dnn_backend_native.c:297
DnnOperand::isNHWC
int8_t isNHWC
NHWC if 1, otherwise NCHW.
Definition: dnn_backend_native.h:85
ConvolutionalNetwork::output_indexes
int32_t * output_indexes
Definition: dnn_backend_native.h:113
ConvolutionalParams::kernel
float * kernel
Definition: dnn_backend_native_layer_conv2d.h:35
layer_funcs
LayerFunc layer_funcs[DLT_COUNT]
Definition: dnn_backend_native_layers.c:30
av_mallocz_array
void * av_mallocz_array(size_t nmemb, size_t size)
Definition: mem.c:190
dnn_backend_native_layers.h
get_input_native
static DNNReturnType get_input_native(void *model, DNNData *input, const char *input_name)
Definition: dnn_backend_native.c:31
avio_size
int64_t avio_size(AVIOContext *s)
Get the filesize.
Definition: aviobuf.c:334
av_malloc
#define av_malloc(s)
Definition: tableprint_vlc.h:31
fail
#define fail()
Definition: checkasm.h:123
ConvolutionalNetwork::layers_num
int32_t layers_num
Definition: dnn_backend_native.h:110
DNN_SUCCESS
@ DNN_SUCCESS
Definition: dnn_interface.h:31
ConvolutionalNetwork::operands_num
int32_t operands_num
Definition: dnn_backend_native.h:112
ConvolutionalNetwork
Definition: dnn_backend_native.h:108
DNNLayerType
DNNLayerType
the enum value of DNNLayerType should not be changed, the same values are used in convert_from_tensor...
Definition: dnn_backend_native.h:38
DnnOperand::type
DNNOperandType type
input/output/intermediate operand of the network
Definition: dnn_backend_native.h:73
avassert.h
ff_dnn_load_model_native
DNNModel * ff_dnn_load_model_native(const char *model_filename)
Definition: dnn_backend_native.c:118
Layer::type
DNNLayerType type
Definition: dnn_backend_native.h:52
DLT_CONV2D
@ DLT_CONV2D
Definition: dnn_backend_native.h:40
DnnOperand::name
char name[128]
to avoid possible memory leak, do not use char *name
Definition: dnn_backend_native.h:90
DnnOperand::data
void * data
data pointer with data length in bytes.
Definition: dnn_backend_native.h:98
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:37
DNNReturnType
DNNReturnType
Definition: dnn_interface.h:31
DnnOperand::data_type
DNNDataType data_type
support different kinds of data type such as float, half float, int8 etc, first support float now.
Definition: dnn_backend_native.h:79
DNNData
Definition: dnn_interface.h:37
outputs
static const AVFilterPad outputs[]
Definition: af_acontrast.c:203
DNNModel::get_input
DNNReturnType(* get_input)(void *model, DNNData *input, const char *input_name)
Definition: dnn_interface.h:48
int32_t
int32_t
Definition: audio_convert.c:194
if
if(ret)
Definition: filter_design.txt:179
ff_dnn_free_model_native
void ff_dnn_free_model_native(DNNModel **model)
Definition: dnn_backend_native.c:309
Layer::params
void * params
Definition: dnn_backend_native.h:60
result
and forward the result(frame or status change) to the corresponding input. If nothing is possible
NULL
#define NULL
Definition: coverity.c:32
DnnOperand::dims
int32_t dims[4]
there are two memory layouts, NHWC or NCHW, so we use dims, dims[0] is Number.
Definition: dnn_backend_native.h:68
ConvolutionalNetwork::operands
DnnOperand * operands
Definition: dnn_backend_native.h:111
ConvolutionalNetwork::layers
Layer * layers
Definition: dnn_backend_native.h:109
DnnOperand::length
int32_t length
Definition: dnn_backend_native.h:99
DOT_INPUT
@ DOT_INPUT
Definition: dnn_backend_native.h:49
avio_rl32
unsigned int avio_rl32(AVIOContext *s)
Definition: aviobuf.c:747
AVIOContext
Bytestream IO Context.
Definition: avio.h:161
Layer::output_operand_index
int32_t output_operand_index
Definition: dnn_backend_native.h:59
Layer
Definition: dnn_backend_native.h:51
Layer::input_operand_indexes
int32_t input_operand_indexes[4]
a layer can have multiple inputs and one output.
Definition: dnn_backend_native.h:58
ff_dnn_execute_model_native
DNNReturnType ff_dnn_execute_model_native(const DNNModel *model, DNNData *outputs, uint32_t nb_output)
Definition: dnn_backend_native.c:257
avio_get_str
int avio_get_str(AVIOContext *pb, int maxlen, char *buf, int buflen)
Read a string from pb into buf.
Definition: aviobuf.c:857
calculate_operand_dims_count
int32_t calculate_operand_dims_count(const DnnOperand *oprd)
Definition: dnn_backend_native.c:288
size
int size
Definition: twinvq_data.h:11134
dnn_backend_native_layer_conv2d.h
FFMIN
#define FFMIN(a, b)
Definition: common.h:96
LayerFunc::pf_exec
LAYER_EXEC_FUNC pf_exec
Definition: dnn_backend_native_layers.h:32
version
version
Definition: libkvazaar.c:292
dnn_backend_native.h
input
and forward the test the status of outputs and forward it to the corresponding return FFERROR_NOT_READY If the filters stores internally one or a few frame for some input
Definition: filter_design.txt:172
LayerFunc::pf_load
LAYER_LOAD_FUNC pf_load
Definition: dnn_backend_native_layers.h:33
ConvolutionalNetwork::nb_output
uint32_t nb_output
Definition: dnn_backend_native.h:114
avio_closep
int avio_closep(AVIOContext **s)
Close the resource accessed by the AVIOContext *s, free it and set the pointer pointing to it to NULL...
Definition: aviobuf.c:1170
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:269
DNN_ERROR
@ DNN_ERROR
Definition: dnn_interface.h:31
av_mallocz
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:237
set_input_output_native
static DNNReturnType set_input_output_native(void *model, DNNData *input, const char *input_name, const char **output_names, uint32_t nb_output)
Definition: dnn_backend_native.c:53
len
int len
Definition: vorbis_enc_data.h:452
DNNModel::set_input_output
DNNReturnType(* set_input_output)(void *model, DNNData *input, const char *input_name, const char **output_names, uint32_t nb_output)
Definition: dnn_interface.h:51
dim
int dim
Definition: vorbis_enc_data.h:451
avio_seek
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:241
DnnOperand
Definition: dnn_backend_native.h:63
avio_open
int avio_open(AVIOContext **s, const char *url, int flags)
Create and initialize a AVIOContext for accessing the resource indicated by url.
Definition: aviobuf.c:1115
DNNModel
Definition: dnn_interface.h:43
AVIO_FLAG_READ
#define AVIO_FLAG_READ
read-only
Definition: avio.h:674
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
ConvolutionalParams
Definition: dnn_backend_native_layer_conv2d.h:29
DNNModel::model
void * model
Definition: dnn_interface.h:45
ConvolutionalParams::biases
float * biases
Definition: dnn_backend_native_layer_conv2d.h:36