MobileFFmpeg iOS / tvOS API  4.4
fftools_ffmpeg_hw.c
Go to the documentation of this file.
1 /*
2  * This file is part of FFmpeg.
3  *
4  * FFmpeg is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * FFmpeg is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with FFmpeg; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 /*
20  * CHANGES 12.2019
21  * - Concurrent execution support
22  *
23  * CHANGES 08.2018
24  * --------------------------------------------------------
25  * - fftools_ prefix added to file name and parent header
26  */
27 
28 #include <string.h>
29 
30 #include "libavutil/avstring.h"
31 
32 #include "fftools_ffmpeg.h"
33 
34 __thread int nb_hw_devices;
35 __thread HWDevice **hw_devices;
36 
37 static HWDevice *hw_device_get_by_type(enum AVHWDeviceType type)
38 {
39  HWDevice *found = NULL;
40  int i;
41  for (i = 0; i < nb_hw_devices; i++) {
42  if (hw_devices[i]->type == type) {
43  if (found)
44  return NULL;
45  found = hw_devices[i];
46  }
47  }
48  return found;
49 }
50 
51 HWDevice *hw_device_get_by_name(const char *name)
52 {
53  int i;
54  for (i = 0; i < nb_hw_devices; i++) {
55  if (!strcmp(hw_devices[i]->name, name))
56  return hw_devices[i];
57  }
58  return NULL;
59 }
60 
61 static HWDevice *hw_device_add(void)
62 {
63  int err;
64  err = av_reallocp_array(&hw_devices, nb_hw_devices + 1,
65  sizeof(*hw_devices));
66  if (err) {
67  nb_hw_devices = 0;
68  return NULL;
69  }
70  hw_devices[nb_hw_devices] = av_mallocz(sizeof(HWDevice));
72  return NULL;
73  return hw_devices[nb_hw_devices++];
74 }
75 
76 static char *hw_device_default_name(enum AVHWDeviceType type)
77 {
78  // Make an automatic name of the form "type%d". We arbitrarily
79  // limit at 1000 anonymous devices of the same type - there is
80  // probably something else very wrong if you get to this limit.
81  const char *type_name = av_hwdevice_get_type_name(type);
82  char *name;
83  size_t index_pos;
84  int index, index_limit = 1000;
85  index_pos = strlen(type_name);
86  name = av_malloc(index_pos + 4);
87  if (!name)
88  return NULL;
89  for (index = 0; index < index_limit; index++) {
90  snprintf(name, index_pos + 4, "%s%d", type_name, index);
91  if (!hw_device_get_by_name(name))
92  break;
93  }
94  if (index >= index_limit) {
95  av_freep(&name);
96  return NULL;
97  }
98  return name;
99 }
100 
101 int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
102 {
103  // "type=name:device,key=value,key2=value2"
104  // "type:device,key=value,key2=value2"
105  // -> av_hwdevice_ctx_create()
106  // "type=name@name"
107  // "type@name"
108  // -> av_hwdevice_ctx_create_derived()
109 
110  AVDictionary *options = NULL;
111  const char *type_name = NULL, *name = NULL, *device = NULL;
112  enum AVHWDeviceType type;
113  HWDevice *dev, *src;
114  AVBufferRef *device_ref = NULL;
115  int err;
116  const char *errmsg, *p, *q;
117  size_t k;
118 
119  k = strcspn(arg, ":=@");
120  p = arg + k;
121 
122  type_name = av_strndup(arg, k);
123  if (!type_name) {
124  err = AVERROR(ENOMEM);
125  goto fail;
126  }
127  type = av_hwdevice_find_type_by_name(type_name);
128  if (type == AV_HWDEVICE_TYPE_NONE) {
129  errmsg = "unknown device type";
130  goto invalid;
131  }
132 
133  if (*p == '=') {
134  k = strcspn(p + 1, ":@");
135 
136  name = av_strndup(p + 1, k);
137  if (!name) {
138  err = AVERROR(ENOMEM);
139  goto fail;
140  }
141  if (hw_device_get_by_name(name)) {
142  errmsg = "named device already exists";
143  goto invalid;
144  }
145 
146  p += 1 + k;
147  } else {
148  name = hw_device_default_name(type);
149  if (!name) {
150  err = AVERROR(ENOMEM);
151  goto fail;
152  }
153  }
154 
155  if (!*p) {
156  // New device with no parameters.
157  err = av_hwdevice_ctx_create(&device_ref, type,
158  NULL, NULL, 0);
159  if (err < 0)
160  goto fail;
161 
162  } else if (*p == ':') {
163  // New device with some parameters.
164  ++p;
165  q = strchr(p, ',');
166  if (q) {
167  if (q - p > 0) {
168  device = av_strndup(p, q - p);
169  if (!device) {
170  err = AVERROR(ENOMEM);
171  goto fail;
172  }
173  }
174  err = av_dict_parse_string(&options, q + 1, "=", ",", 0);
175  if (err < 0) {
176  errmsg = "failed to parse options";
177  goto invalid;
178  }
179  }
180 
181  err = av_hwdevice_ctx_create(&device_ref, type,
182  q ? device : p[0] ? p : NULL,
183  options, 0);
184  if (err < 0)
185  goto fail;
186 
187  } else if (*p == '@') {
188  // Derive from existing device.
189 
190  src = hw_device_get_by_name(p + 1);
191  if (!src) {
192  errmsg = "invalid source device name";
193  goto invalid;
194  }
195 
196  err = av_hwdevice_ctx_create_derived(&device_ref, type,
197  src->device_ref, 0);
198  if (err < 0)
199  goto fail;
200  } else {
201  errmsg = "parse error";
202  goto invalid;
203  }
204 
205  dev = hw_device_add();
206  if (!dev) {
207  err = AVERROR(ENOMEM);
208  goto fail;
209  }
210 
211  dev->name = name;
212  dev->type = type;
213  dev->device_ref = device_ref;
214 
215  if (dev_out)
216  *dev_out = dev;
217 
218  name = NULL;
219  err = 0;
220 done:
221  av_freep(&type_name);
222  av_freep(&name);
223  av_freep(&device);
224  av_dict_free(&options);
225  return err;
226 invalid:
227  av_log(NULL, AV_LOG_ERROR,
228  "Invalid device specification \"%s\": %s\n", arg, errmsg);
229  err = AVERROR(EINVAL);
230  goto done;
231 fail:
232  av_log(NULL, AV_LOG_ERROR,
233  "Device creation failed: %d.\n", err);
234  av_buffer_unref(&device_ref);
235  goto done;
236 }
237 
238 static int hw_device_init_from_type(enum AVHWDeviceType type,
239  const char *device,
240  HWDevice **dev_out)
241 {
242  AVBufferRef *device_ref = NULL;
243  HWDevice *dev;
244  char *name;
245  int err;
246 
247  name = hw_device_default_name(type);
248  if (!name) {
249  err = AVERROR(ENOMEM);
250  goto fail;
251  }
252 
253  err = av_hwdevice_ctx_create(&device_ref, type, device, NULL, 0);
254  if (err < 0) {
255  av_log(NULL, AV_LOG_ERROR,
256  "Device creation failed: %d.\n", err);
257  goto fail;
258  }
259 
260  dev = hw_device_add();
261  if (!dev) {
262  err = AVERROR(ENOMEM);
263  goto fail;
264  }
265 
266  dev->name = name;
267  dev->type = type;
268  dev->device_ref = device_ref;
269 
270  if (dev_out)
271  *dev_out = dev;
272 
273  return 0;
274 
275 fail:
276  av_freep(&name);
277  av_buffer_unref(&device_ref);
278  return err;
279 }
280 
282 {
283  int i;
284  for (i = 0; i < nb_hw_devices; i++) {
285  av_freep(&hw_devices[i]->name);
286  av_buffer_unref(&hw_devices[i]->device_ref);
287  av_freep(&hw_devices[i]);
288  }
289  av_freep(&hw_devices);
290  nb_hw_devices = 0;
291 }
292 
293 static HWDevice *hw_device_match_by_codec(const AVCodec *codec)
294 {
295  const AVCodecHWConfig *config;
296  HWDevice *dev;
297  int i;
298  for (i = 0;; i++) {
299  config = avcodec_get_hw_config(codec, i);
300  if (!config)
301  return NULL;
302  if (!(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX))
303  continue;
304  dev = hw_device_get_by_type(config->device_type);
305  if (dev)
306  return dev;
307  }
308 }
309 
311 {
312  const AVCodecHWConfig *config;
313  enum AVHWDeviceType type;
314  HWDevice *dev = NULL;
315  int err, auto_device = 0;
316 
317  if (ist->hwaccel_device) {
319  if (!dev) {
320  if (ist->hwaccel_id == HWACCEL_AUTO) {
321  auto_device = 1;
322  } else if (ist->hwaccel_id == HWACCEL_GENERIC) {
323  type = ist->hwaccel_device_type;
324  err = hw_device_init_from_type(type, ist->hwaccel_device,
325  &dev);
326  } else {
327  // This will be dealt with by API-specific initialisation
328  // (using hwaccel_device), so nothing further needed here.
329  return 0;
330  }
331  } else {
332  if (ist->hwaccel_id == HWACCEL_AUTO) {
333  ist->hwaccel_device_type = dev->type;
334  } else if (ist->hwaccel_device_type != dev->type) {
335  av_log(ist->dec_ctx, AV_LOG_ERROR, "Invalid hwaccel device "
336  "specified for decoder: device %s of type %s is not "
337  "usable with hwaccel %s.\n", dev->name,
338  av_hwdevice_get_type_name(dev->type),
339  av_hwdevice_get_type_name(ist->hwaccel_device_type));
340  return AVERROR(EINVAL);
341  }
342  }
343  } else {
344  if (ist->hwaccel_id == HWACCEL_AUTO) {
345  auto_device = 1;
346  } else if (ist->hwaccel_id == HWACCEL_GENERIC) {
347  type = ist->hwaccel_device_type;
348  dev = hw_device_get_by_type(type);
349  if (!dev)
350  err = hw_device_init_from_type(type, NULL, &dev);
351  } else {
352  dev = hw_device_match_by_codec(ist->dec);
353  if (!dev) {
354  // No device for this codec, but not using generic hwaccel
355  // and therefore may well not need one - ignore.
356  return 0;
357  }
358  }
359  }
360 
361  if (auto_device) {
362  int i;
363  if (!avcodec_get_hw_config(ist->dec, 0)) {
364  // Decoder does not support any hardware devices.
365  return 0;
366  }
367  for (i = 0; !dev; i++) {
368  config = avcodec_get_hw_config(ist->dec, i);
369  if (!config)
370  break;
371  type = config->device_type;
372  dev = hw_device_get_by_type(type);
373  if (dev) {
374  av_log(ist->dec_ctx, AV_LOG_INFO, "Using auto "
375  "hwaccel type %s with existing device %s.\n",
376  av_hwdevice_get_type_name(type), dev->name);
377  }
378  }
379  for (i = 0; !dev; i++) {
380  config = avcodec_get_hw_config(ist->dec, i);
381  if (!config)
382  break;
383  type = config->device_type;
384  // Try to make a new device of this type.
385  err = hw_device_init_from_type(type, ist->hwaccel_device,
386  &dev);
387  if (err < 0) {
388  // Can't make a device of this type.
389  continue;
390  }
391  if (ist->hwaccel_device) {
392  av_log(ist->dec_ctx, AV_LOG_INFO, "Using auto "
393  "hwaccel type %s with new device created "
394  "from %s.\n", av_hwdevice_get_type_name(type),
395  ist->hwaccel_device);
396  } else {
397  av_log(ist->dec_ctx, AV_LOG_INFO, "Using auto "
398  "hwaccel type %s with new default device.\n",
399  av_hwdevice_get_type_name(type));
400  }
401  }
402  if (dev) {
403  ist->hwaccel_device_type = type;
404  } else {
405  av_log(ist->dec_ctx, AV_LOG_INFO, "Auto hwaccel "
406  "disabled: no device found.\n");
407  ist->hwaccel_id = HWACCEL_NONE;
408  return 0;
409  }
410  }
411 
412  if (!dev) {
413  av_log(ist->dec_ctx, AV_LOG_ERROR, "No device available "
414  "for decoder: device type %s needed for codec %s.\n",
415  av_hwdevice_get_type_name(type), ist->dec->name);
416  return err;
417  }
418 
419  ist->dec_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref);
420  if (!ist->dec_ctx->hw_device_ctx)
421  return AVERROR(ENOMEM);
422 
423  return 0;
424 }
425 
427 {
428  HWDevice *dev;
429 
430  dev = hw_device_match_by_codec(ost->enc);
431  if (dev) {
432  ost->enc_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref);
433  if (!ost->enc_ctx->hw_device_ctx)
434  return AVERROR(ENOMEM);
435  return 0;
436  } else {
437  // No device required, or no device available.
438  return 0;
439  }
440 }
441 
442 static int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input)
443 {
444  InputStream *ist = avctx->opaque;
445  AVFrame *output = NULL;
446  enum AVPixelFormat output_format = ist->hwaccel_output_format;
447  int err;
448 
449  if (input->format == output_format) {
450  // Nothing to do.
451  return 0;
452  }
453 
454  output = av_frame_alloc();
455  if (!output)
456  return AVERROR(ENOMEM);
457 
458  output->format = output_format;
459 
460  err = av_hwframe_transfer_data(output, input, 0);
461  if (err < 0) {
462  av_log(avctx, AV_LOG_ERROR, "Failed to transfer data to "
463  "output frame: %d.\n", err);
464  goto fail;
465  }
466 
467  err = av_frame_copy_props(output, input);
468  if (err < 0) {
469  av_frame_unref(output);
470  goto fail;
471  }
472 
473  av_frame_unref(input);
474  av_frame_move_ref(input, output);
475  av_frame_free(&output);
476 
477  return 0;
478 
479 fail:
480  av_frame_free(&output);
481  return err;
482 }
483 
484 int hwaccel_decode_init(AVCodecContext *avctx)
485 {
486  InputStream *ist = avctx->opaque;
487 
489 
490  return 0;
491 }
AV_LOG_INFO
#define AV_LOG_INFO
Definition: MobileFFmpegConfig.h:66
hw_device_get_by_name
HWDevice * hw_device_get_by_name(const char *name)
Definition: fftools_ffmpeg_hw.c:51
fftools_ffmpeg.h
InputStream
Definition: fftools_ffmpeg.h:318
hw_device_get_by_type
static HWDevice * hw_device_get_by_type(enum AVHWDeviceType type)
Definition: fftools_ffmpeg_hw.c:37
HWACCEL_NONE
@ HWACCEL_NONE
Definition: fftools_ffmpeg.h:82
InputStream::hwaccel_device_type
enum AVHWDeviceType hwaccel_device_type
Definition: fftools_ffmpeg.h:388
AV_LOG_ERROR
#define AV_LOG_ERROR
Definition: MobileFFmpegConfig.h:55
hw_device_default_name
static char * hw_device_default_name(enum AVHWDeviceType type)
Definition: fftools_ffmpeg_hw.c:76
HWACCEL_AUTO
@ HWACCEL_AUTO
Definition: fftools_ffmpeg.h:83
HWDevice::type
enum AVHWDeviceType type
Definition: fftools_ffmpeg.h:99
HWDevice::name
const char * name
Definition: fftools_ffmpeg.h:98
hwaccel_retrieve_data
static int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input)
Definition: fftools_ffmpeg_hw.c:442
hw_device_init_from_type
static int hw_device_init_from_type(enum AVHWDeviceType type, const char *device, HWDevice **dev_out)
Definition: fftools_ffmpeg_hw.c:238
hw_device_match_by_codec
static HWDevice * hw_device_match_by_codec(const AVCodec *codec)
Definition: fftools_ffmpeg_hw.c:293
hw_devices
__thread HWDevice ** hw_devices
Definition: fftools_ffmpeg_hw.c:35
hw_device_add
static HWDevice * hw_device_add(void)
Definition: fftools_ffmpeg_hw.c:61
hw_device_init_from_string
int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
Definition: fftools_ffmpeg_hw.c:101
hw_device_setup_for_encode
int hw_device_setup_for_encode(OutputStream *ost)
Definition: fftools_ffmpeg_hw.c:426
InputStream::dec_ctx
AVCodecContext * dec_ctx
Definition: fftools_ffmpeg.h:327
InputStream::hwaccel_retrieve_data
int(* hwaccel_retrieve_data)(AVCodecContext *s, AVFrame *frame)
Definition: fftools_ffmpeg.h:396
hwaccel_decode_init
int hwaccel_decode_init(AVCodecContext *avctx)
Definition: fftools_ffmpeg_hw.c:484
nb_hw_devices
__thread int nb_hw_devices
Definition: fftools_ffmpeg_hw.c:34
OutputStream::enc
AVCodec * enc
Definition: fftools_ffmpeg.h:490
HWDevice
Definition: fftools_ffmpeg.h:97
InputStream::hwaccel_device
char * hwaccel_device
Definition: fftools_ffmpeg.h:389
InputStream::hwaccel_id
enum HWAccelID hwaccel_id
Definition: fftools_ffmpeg.h:387
InputStream::dec
AVCodec * dec
Definition: fftools_ffmpeg.h:328
OutputStream::enc_ctx
AVCodecContext * enc_ctx
Definition: fftools_ffmpeg.h:488
HWDevice::device_ref
AVBufferRef * device_ref
Definition: fftools_ffmpeg.h:100
OutputStream
Definition: fftools_ffmpeg.h:465
InputStream::hwaccel_output_format
enum AVPixelFormat hwaccel_output_format
Definition: fftools_ffmpeg.h:390
hw_device_setup_for_decode
int hw_device_setup_for_decode(InputStream *ist)
Definition: fftools_ffmpeg_hw.c:310
HWACCEL_GENERIC
@ HWACCEL_GENERIC
Definition: fftools_ffmpeg.h:84
hw_device_free_all
void hw_device_free_all(void)
Definition: fftools_ffmpeg_hw.c:281