/* * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> #include "libavutil/mem.h" #include "ffmpeg.h" static int nb_hw_devices; static HWDevice **hw_devices; HWDevice *hw_device_get_by_type(enum AVHWDeviceType type) { HWDevice *found = NULL; int i; for (i = 0; i < nb_hw_devices; i++) { if (hw_devices[i]->type == type) { if (found) return NULL; found = hw_devices[i]; } } return found; } HWDevice *hw_device_get_by_name(const char *name) { int i; for (i = 0; i < nb_hw_devices; i++) { if (!strcmp(hw_devices[i]->name, name)) return hw_devices[i]; } return NULL; } static HWDevice *hw_device_add(void) { int err; err = av_reallocp_array(&hw_devices, nb_hw_devices + 1, sizeof(*hw_devices)); if (err) { nb_hw_devices = 0; return NULL; } hw_devices[nb_hw_devices] = av_mallocz(sizeof(HWDevice)); if (!hw_devices[nb_hw_devices]) return NULL; return hw_devices[nb_hw_devices++]; } static char *hw_device_default_name(enum AVHWDeviceType type) { // Make an automatic name of the form "type%d". We arbitrarily // limit at 1000 anonymous devices of the same type - there is // probably something else very wrong if you get to this limit. const char *type_name = av_hwdevice_get_type_name(type); char *name; size_t index_pos; int index, index_limit = 1000; index_pos = strlen(type_name); name = av_malloc(index_pos + 4); if (!name) return NULL; for (index = 0; index < index_limit; index++) { snprintf(name, index_pos + 4, "%s%d", type_name, index); if (!hw_device_get_by_name(name)) break; } if (index >= index_limit) { av_freep(&name); return NULL; } return name; } int hw_device_init_from_string(const char *arg, HWDevice **dev_out) { // "type=name" // "type=name,key=value,key2=value2" // "type=name:device,key=value,key2=value2" // "type:device,key=value,key2=value2" // -> av_hwdevice_ctx_create() // "type=name@name" // "type@name" // -> av_hwdevice_ctx_create_derived() AVDictionary *options = NULL; const char *type_name = NULL, *name = NULL, *device = NULL; enum AVHWDeviceType type; HWDevice *dev, *src; AVBufferRef *device_ref = NULL; int err; const char *errmsg, *p, *q; size_t k; k = strcspn(arg, ":=@"); p = arg + k; type_name = av_strndup(arg, k); if (!type_name) { err = AVERROR(ENOMEM); goto fail; } type = av_hwdevice_find_type_by_name(type_name); if (type == AV_HWDEVICE_TYPE_NONE) { errmsg = "unknown device type"; goto invalid; } if (*p == '=') { k = strcspn(p + 1, ":@,"); name = av_strndup(p + 1, k); if (!name) { err = AVERROR(ENOMEM); goto fail; } if (hw_device_get_by_name(name)) { errmsg = "named device already exists"; goto invalid; } p += 1 + k; } else { name = hw_device_default_name(type); if (!name) { err = AVERROR(ENOMEM); goto fail; } } if (!*p) { // New device with no parameters. err = av_hwdevice_ctx_create(&device_ref, type, NULL, NULL, 0); if (err < 0) goto fail; } else if (*p == ':') { // New device with some parameters. ++p; q = strchr(p, ','); if (q) { if (q - p > 0) { device = av_strndup(p, q - p); if (!device) { err = AVERROR(ENOMEM); goto fail; } } err = av_dict_parse_string(&options, q + 1, "=", ",", 0); if (err < 0) { errmsg = "failed to parse options"; goto invalid; } } err = av_hwdevice_ctx_create(&device_ref, type, q ? device : p[0] ? p : NULL, options, 0); if (err < 0) goto fail; } else if (*p == '@') { // Derive from existing device. src = hw_device_get_by_name(p + 1); if (!src) { errmsg = "invalid source device name"; goto invalid; } err = av_hwdevice_ctx_create_derived(&device_ref, type, src->device_ref, 0); if (err < 0) goto fail; } else if (*p == ',') { err = av_dict_parse_string(&options, p + 1, "=", ",", 0); if (err < 0) { errmsg = "failed to parse options"; goto invalid; } err = av_hwdevice_ctx_create(&device_ref, type, NULL, options, 0); if (err < 0) goto fail; } else { errmsg = "parse error"; goto invalid; } dev = hw_device_add(); if (!dev) { err = AVERROR(ENOMEM); goto fail; } dev->name = name; dev->type = type; dev->device_ref = device_ref; if (dev_out) *dev_out = dev; name = NULL; err = 0; done: av_freep(&type_name); av_freep(&name); av_freep(&device); av_dict_free(&options); return err; invalid: av_log(NULL, AV_LOG_ERROR, "Invalid device specification \"%s\": %s\n", arg, errmsg); err = AVERROR(EINVAL); goto done; fail: av_log(NULL, AV_LOG_ERROR, "Device creation failed: %d.\n", err); av_buffer_unref(&device_ref); goto done; } int hw_device_init_from_type(enum AVHWDeviceType type, const char *device, HWDevice **dev_out) { AVBufferRef *device_ref = NULL; HWDevice *dev; char *name; int err; name = hw_device_default_name(type); if (!name) { err = AVERROR(ENOMEM); goto fail; } err = av_hwdevice_ctx_create(&device_ref, type, device, NULL, 0); if (err < 0) { av_log(NULL, AV_LOG_ERROR, "Device creation failed: %d.\n", err); goto fail; } dev = hw_device_add(); if (!dev) { err = AVERROR(ENOMEM); goto fail; } dev->name = name; dev->type = type; dev->device_ref = device_ref; if (dev_out) *dev_out = dev; return 0; fail: av_freep(&name); av_buffer_unref(&device_ref); return err; } void hw_device_free_all(void) { int i; for (i = 0; i < nb_hw_devices; i++) { av_freep(&hw_devices[i]->name); av_buffer_unref(&hw_devices[i]->device_ref); av_freep(&hw_devices[i]); } av_freep(&hw_devices); nb_hw_devices = 0; } AVBufferRef *hw_device_for_filter(void) { // Pick the last hardware device if the user doesn't pick the device for // filters explicitly with the filter_hw_device option. if (filter_hw_device) return filter_hw_device->device_ref; else if (nb_hw_devices > 0) { HWDevice *dev = hw_devices[nb_hw_devices - 1]; if (nb_hw_devices > 1) av_log(NULL, AV_LOG_WARNING, "There are %d hardware devices. device " "%s of type %s is picked for filters by default. Set hardware " "device explicitly with the filter_hw_device option if device " "%s is not usable for filters.\n", nb_hw_devices, dev->name, av_hwdevice_get_type_name(dev->type), dev->name); return dev->device_ref; } return NULL; }