[FFmpeg-devel] [PATCH 1/2] avdevice/dshow: implement capabilities API
Diederick Niehorster
dcnieho at gmail.com
Fri Jun 4 01:32:58 EEST 2021
This implements avdevice_capabilities_create and avdevice_capabilities_free for the dshow device.
Signed-off-by: Diederick Niehorster <dcnieho at gmail.com>
---
libavdevice/dshow.c | 498 ++++++++++++++++++++++++++++++++++++++++----
1 file changed, 462 insertions(+), 36 deletions(-)
diff --git a/libavdevice/dshow.c b/libavdevice/dshow.c
index 8d0a6fcc09..c65492119e 100644
--- a/libavdevice/dshow.c
+++ b/libavdevice/dshow.c
@@ -30,6 +30,48 @@
#include "objidl.h"
#include "shlwapi.h"
+enum DshowCapQueryType {
+ CAP_QUERY_NONE = 0,
+ CAP_QUERY_SAMPLE_FORMAT,
+ CAP_QUERY_SAMPLE_RATE,
+ CAP_QUERY_CHANNELS,
+ CAP_QUERY_CODEC,
+ CAP_QUERY_PIXEL_FORMAT,
+ CAP_QUERY_FRAME_SIZE,
+ CAP_QUERY_FPS
+};
+typedef struct DshowCapQueryTypeEntry {
+ const char* name;
+ enum DshowCapQueryType query_type;
+} DshowCapQueryTypeEntry;
+
+static const DshowCapQueryTypeEntry query_table[] = {
+ { "sample_format", CAP_QUERY_SAMPLE_FORMAT },
+ { "sample_rate", CAP_QUERY_SAMPLE_RATE },
+ { "channels", CAP_QUERY_CHANNELS },
+ { "codec", CAP_QUERY_CODEC },
+ { "pixel_format", CAP_QUERY_PIXEL_FORMAT },
+ { "frame_size", CAP_QUERY_FRAME_SIZE },
+ { "fps", CAP_QUERY_FPS },
+};
+
+static enum DshowCapQueryType dshow_get_query_type(const char* option_name)
+{
+ for (int i = 0; i < FF_ARRAY_ELEMS(query_table); ++i) {
+ if (!strcmp(query_table[i].name, option_name))
+ return query_table[i].query_type;
+ }
+ return CAP_QUERY_NONE;
+}
+
+static const char* dshow_get_query_type_string(enum DshowCapQueryType query_type)
+{
+ for (int i = 0; i < FF_ARRAY_ELEMS(query_table); ++i) {
+ if (query_table[i].query_type == query_type)
+ return query_table[i].name;
+ }
+ return NULL;
+}
static enum AVPixelFormat dshow_pixfmt(DWORD biCompression, WORD biBitCount)
{
@@ -54,6 +96,26 @@ static enum AVPixelFormat dshow_pixfmt(DWORD biCompression, WORD biBitCount)
return avpriv_find_pix_fmt(avpriv_get_raw_pix_fmt_tags(), biCompression); // all others
}
+static enum AVCodecID waveform_codec_id(enum AVSampleFormat sample_fmt)
+{
+ switch (sample_fmt) {
+ case AV_SAMPLE_FMT_U8: return AV_CODEC_ID_PCM_U8;
+ case AV_SAMPLE_FMT_S16: return AV_CODEC_ID_PCM_S16LE;
+ case AV_SAMPLE_FMT_S32: return AV_CODEC_ID_PCM_S32LE;
+ default: return AV_CODEC_ID_NONE; /* Should never happen. */
+ }
+}
+
+static enum AVSampleFormat sample_fmt_bits_per_sample(int bits)
+{
+ switch (bits) {
+ case 8: return AV_SAMPLE_FMT_U8;
+ case 16: return AV_SAMPLE_FMT_S16;
+ case 32: return AV_SAMPLE_FMT_S32;
+ default: return AV_SAMPLE_FMT_NONE; /* Should never happen. */
+ }
+}
+
static int
dshow_read_close(AVFormatContext *s)
{
@@ -317,10 +379,13 @@ fail1:
* try to set parameters specified through AVOptions and if successful
* return 1 in *pformat_set.
* If pformat_set is NULL, list all pin capabilities.
+ * When listing pin capabilities, if ranges is NULL, output to log,
+ * else store capabilities in ranges.
*/
static void
dshow_cycle_formats(AVFormatContext *avctx, enum dshowDeviceType devtype,
- IPin *pin, int *pformat_set)
+ IPin *pin, int *pformat_set,
+ AVOptionRanges *ranges, enum DshowCapQueryType query_type)
{
struct dshow_ctx *ctx = avctx->priv_data;
IAMStreamConfig *config = NULL;
@@ -338,7 +403,11 @@ dshow_cycle_formats(AVFormatContext *avctx, enum dshowDeviceType devtype,
if (!caps)
goto end;
- for (i = 0; i < n && !format_set; i++) {
+ for (i = 0; i < n && (!format_set || ranges); i++) {
+ AVOptionRange *range = NULL;
+ AVOptionRange **range_list = NULL;
+ int nb_range = 0;
+
r = IAMStreamConfig_GetStreamCaps(config, i, &type, (void *) caps);
if (r != S_OK)
goto next;
@@ -365,7 +434,7 @@ dshow_cycle_formats(AVFormatContext *avctx, enum dshowDeviceType devtype,
} else {
goto next;
}
- if (!pformat_set) {
+ if (!pformat_set && !ranges) {
enum AVPixelFormat pix_fmt = dshow_pixfmt(bih->biCompression, bih->biBitCount);
if (pix_fmt == AV_PIX_FMT_NONE) {
enum AVCodecID codec_id = av_codec_get_id(tags, bih->biCompression);
@@ -410,6 +479,81 @@ dshow_cycle_formats(AVFormatContext *avctx, enum dshowDeviceType devtype,
bih->biWidth = ctx->requested_width;
bih->biHeight = ctx->requested_height;
}
+
+ if (ranges) {
+ if (query_type == CAP_QUERY_FRAME_SIZE) {
+ for (int j = 0; j < 3; j++) {
+ range = av_mallocz(sizeof(AVOptionRange));
+ if (!range)
+ goto next;
+ range->str = av_strdup(j == 0 ? "pixel_count" : (j == 1 ? "width" : (j == 2 ? "height" : "")));
+ if (!range->str)
+ goto next;
+ switch (j)
+ {
+ case 0:
+ range->value_min = vcaps->MinOutputSize.cx * vcaps->MinOutputSize.cy;
+ range->value_max = vcaps->MaxOutputSize.cx * vcaps->MaxOutputSize.cy;
+ break;
+ case 1:
+ range->value_min = vcaps->MinOutputSize.cx;
+ range->value_max = vcaps->MaxOutputSize.cx;
+ break;
+ case 2:
+ range->value_min = vcaps->MinOutputSize.cy;
+ range->value_max = vcaps->MaxOutputSize.cy;
+ break;
+ }
+ range->is_range = range->value_min != range->value_max;
+
+ if (av_dynarray_add_nofree(&range_list,
+ &nb_range, range) < 0)
+ goto next;
+ range = NULL; // copied into array, make sure not freed below
+ }
+ }
+ else {
+ range = av_mallocz(sizeof(AVOptionRange));
+ if (!range)
+ goto next;
+ range->str = av_strdup(dshow_get_query_type_string(query_type));
+ if (!range->str)
+ goto next;
+
+ switch (query_type)
+ {
+ case CAP_QUERY_CODEC:
+ if (dshow_pixfmt(bih->biCompression, bih->biBitCount) == AV_PIX_FMT_NONE) {
+ const AVCodecTag* const tags[] = { avformat_get_riff_video_tags(), NULL };
+ range->value_min = av_codec_get_id(tags, bih->biCompression);
+ }
+ else
+ range->value_min = AV_CODEC_ID_RAWVIDEO;
+ range->value_max = range->value_min;
+ break;
+ case CAP_QUERY_PIXEL_FORMAT:
+ range->value_min = range->value_max = dshow_pixfmt(bih->biCompression, bih->biBitCount);
+ range->value_min;
+ break;
+ case CAP_QUERY_FPS:
+ range->value_min = 1e7 / vcaps->MaxFrameInterval;
+ range->value_max = 1e7 / vcaps->MinFrameInterval;
+ break;
+
+ default:
+ // an audio property is being queried, output 0
+ range->value_min = range->value_max = 0;
+ break;
+ }
+
+ range->is_range = range->value_min != range->value_max;
+
+ if (av_dynarray_add_nofree(&range_list,
+ &nb_range, range) < 0)
+ goto next;
+ range = NULL; // copied into array, make sure not freed below
+ }
+ }
} else {
AUDIO_STREAM_CONFIG_CAPS *acaps = caps;
WAVEFORMATEX *fx;
@@ -421,7 +565,7 @@ dshow_cycle_formats(AVFormatContext *avctx, enum dshowDeviceType devtype,
} else {
goto next;
}
- if (!pformat_set) {
+ if (!pformat_set && !ranges) {
av_log(avctx, AV_LOG_INFO, " min ch=%lu bits=%lu rate=%6lu max ch=%lu bits=%lu rate=%6lu\n",
acaps->MinimumChannels, acaps->MinimumBitsPerSample, acaps->MinimumSampleFrequency,
acaps->MaximumChannels, acaps->MaximumBitsPerSample, acaps->MaximumSampleFrequency);
@@ -445,11 +589,90 @@ dshow_cycle_formats(AVFormatContext *avctx, enum dshowDeviceType devtype,
goto next;
fx->nChannels = ctx->channels;
}
+
+ if (ranges) {
+ if (query_type == CAP_QUERY_FRAME_SIZE) {
+ for (int j = 0; j < 3; j++) {
+ range = av_mallocz(sizeof(AVOptionRange));
+ if (!range)
+ goto next;
+ range->str = av_strdup(j == 0 ? "pixel_count" : (j == 1 ? "width" : (j == 2 ? "height" : "")));
+ if (!range->str)
+ goto next;
+ // an video property is being queried, output 0
+ range->value_min = range->value_max = 0;
+ range->is_range = 0;
+
+ if (av_dynarray_add_nofree(&range_list,
+ &nb_range, range) < 0)
+ goto next;
+ range = NULL; // copied into array, make sure not freed below
+ }
+ }
+ else {
+ range = av_mallocz(sizeof(AVOptionRange));
+ if (!range)
+ goto next;
+ range->str = av_strdup(dshow_get_query_type_string(query_type));
+ if (!range->str)
+ goto next;
+
+ switch (query_type)
+ {
+ case CAP_QUERY_SAMPLE_FORMAT:
+ range->value_min = sample_fmt_bits_per_sample(acaps->MinimumBitsPerSample);
+ range->value_max = sample_fmt_bits_per_sample(acaps->MaximumBitsPerSample);
+ break;
+ case CAP_QUERY_SAMPLE_RATE:
+ range->value_min = acaps->MinimumSampleFrequency;
+ range->value_max = acaps->MaximumSampleFrequency;
+ break;
+ case CAP_QUERY_CHANNELS:
+ range->value_min = acaps->MinimumChannels;
+ range->value_max = acaps->MaximumChannels;
+ break;
+
+ default:
+ // an video property is being queried, output 0
+ range->value_min = range->value_max = 0;
+ break;
+ }
+
+ range->is_range = range->value_min != range->value_max;
+
+ if (av_dynarray_add_nofree(&range_list,
+ &nb_range, range) < 0)
+ goto next;
+ range = NULL; // copied into array, make sure not freed below
+ }
+ }
}
if (IAMStreamConfig_SetFormat(config, type) != S_OK)
goto next;
+ else if (ranges) {
+ // format matched and could be set successfully. Add capabilities to ranges output
+ for (int j=0; j< nb_range; ++j) {
+ if (av_dynarray_add_nofree(&ranges->range,
+ &ranges->nb_ranges, range_list[j]) < 0)
+ goto next;
+ range_list[j] = NULL; // copied into array, make sure not freed below
+ }
+ }
format_set = 1;
next:
+ if (range) {
+ av_freep(&range->str);
+ av_freep(&range);
+ }
+ if (range_list) {
+ for (int j = 0; j < nb_range; ++j) {
+ if (range_list[j]) {
+ av_freep(&range_list[j]->str);
+ av_freep(&range_list[j]);
+ }
+ }
+ av_freep(&range_list);
+ }
if (type->pbFormat)
CoTaskMemFree(type->pbFormat);
CoTaskMemFree(type);
@@ -558,10 +781,13 @@ end:
* devtype, retrieve the first output pin and return the pointer to the
* object found in *ppin.
* If ppin is NULL, cycle through all pins listing audio/video capabilities.
+ * If ppin is not NULL and ranges is also not null, enumerate all formats
+ * supported by the selected pin.
*/
static int
dshow_cycle_pins(AVFormatContext *avctx, enum dshowDeviceType devtype,
- enum dshowSourceFilterType sourcetype, IBaseFilter *device_filter, IPin **ppin)
+ enum dshowSourceFilterType sourcetype, IBaseFilter *device_filter,
+ IPin **ppin, AVOptionRanges *ranges, enum DshowCapQueryType query_type)
{
struct dshow_ctx *ctx = avctx->priv_data;
IEnumPins *pins = 0;
@@ -630,7 +856,7 @@ dshow_cycle_pins(AVFormatContext *avctx, enum dshowDeviceType devtype,
if (!ppin) {
av_log(avctx, AV_LOG_INFO, " Pin \"%s\" (alternative pin name \"%s\")\n", name_buf, pin_buf);
- dshow_cycle_formats(avctx, devtype, pin, NULL);
+ dshow_cycle_formats(avctx, devtype, pin, NULL, NULL, CAP_QUERY_NONE);
goto next;
}
@@ -642,13 +868,13 @@ dshow_cycle_pins(AVFormatContext *avctx, enum dshowDeviceType devtype,
}
}
- if (set_format) {
- dshow_cycle_formats(avctx, devtype, pin, &format_set);
+ if (set_format || ranges) {
+ dshow_cycle_formats(avctx, devtype, pin, &format_set, ranges, query_type);
if (!format_set) {
goto next;
}
}
- if (devtype == AudioDevice && ctx->audio_buffer_size) {
+ if (devtype == AudioDevice && ctx->audio_buffer_size && !ranges) {
if (dshow_set_audio_buffer_size(avctx, pin) < 0) {
av_log(avctx, AV_LOG_ERROR, "unable to set audio buffer size %d to pin, using pin anyway...", ctx->audio_buffer_size);
}
@@ -673,8 +899,22 @@ next:
IEnumMediaTypes_Release(types);
if (p)
IKsPropertySet_Release(p);
- if (device_pin != pin)
+ if (device_pin != pin) {
IPin_Release(pin);
+ // clear all ranges info again, wrong pin
+ if (ranges) {
+ for (int i = 0; i < ranges->nb_ranges; i++) {
+ AVOptionRange* range = ranges->range[i];
+ if (range) {
+ av_freep(&range->str);
+ av_freep(&ranges->range[i]);
+ }
+ }
+ av_freep(&ranges->range);
+ ranges->nb_ranges = 0;
+ }
+ device_pin = NULL;
+ }
av_free(name_buf);
av_free(pin_buf);
if (pin_id)
@@ -706,17 +946,19 @@ next:
*/
static int
dshow_list_device_options(AVFormatContext *avctx, ICreateDevEnum *devenum,
- enum dshowDeviceType devtype, enum dshowSourceFilterType sourcetype)
+ enum dshowDeviceType devtype, enum dshowSourceFilterType sourcetype,
+ AVOptionRanges *ranges, enum DshowCapQueryType query_type)
{
struct dshow_ctx *ctx = avctx->priv_data;
IBaseFilter *device_filter = NULL;
+ IPin *device_pin = NULL;
char *device_unique_name = NULL;
int r;
if ((r = dshow_cycle_devices(avctx, devenum, devtype, sourcetype, &device_filter, &device_unique_name)) < 0)
return r;
ctx->device_filter[devtype] = device_filter;
- if ((r = dshow_cycle_pins(avctx, devtype, sourcetype, device_filter, NULL)) < 0)
+ if ((r = dshow_cycle_pins(avctx, devtype, sourcetype, device_filter, ranges ? &device_pin : NULL, ranges, query_type)) < 0)
return r;
av_freep(&device_unique_name);
return 0;
@@ -800,7 +1042,7 @@ dshow_open_device(AVFormatContext *avctx, ICreateDevEnum *devenum,
goto error;
}
- if ((r = dshow_cycle_pins(avctx, devtype, sourcetype, device_filter, &device_pin)) < 0) {
+ if ((r = dshow_cycle_pins(avctx, devtype, sourcetype, device_filter, &device_pin, NULL, CAP_QUERY_NONE)) < 0) {
ret = r;
goto error;
}
@@ -912,26 +1154,6 @@ error:
return ret;
}
-static enum AVCodecID waveform_codec_id(enum AVSampleFormat sample_fmt)
-{
- switch (sample_fmt) {
- case AV_SAMPLE_FMT_U8: return AV_CODEC_ID_PCM_U8;
- case AV_SAMPLE_FMT_S16: return AV_CODEC_ID_PCM_S16LE;
- case AV_SAMPLE_FMT_S32: return AV_CODEC_ID_PCM_S32LE;
- default: return AV_CODEC_ID_NONE; /* Should never happen. */
- }
-}
-
-static enum AVSampleFormat sample_fmt_bits_per_sample(int bits)
-{
- switch (bits) {
- case 8: return AV_SAMPLE_FMT_U8;
- case 16: return AV_SAMPLE_FMT_S16;
- case 32: return AV_SAMPLE_FMT_S32;
- default: return AV_SAMPLE_FMT_NONE; /* Should never happen. */
- }
-}
-
static int
dshow_add_device(AVFormatContext *avctx,
enum dshowDeviceType devtype)
@@ -1138,14 +1360,14 @@ static int dshow_read_header(AVFormatContext *avctx)
}
if (ctx->list_options) {
if (ctx->device_name[VideoDevice])
- if ((r = dshow_list_device_options(avctx, devenum, VideoDevice, VideoSourceDevice))) {
+ if ((r = dshow_list_device_options(avctx, devenum, VideoDevice, VideoSourceDevice, NULL, CAP_QUERY_NONE))) {
ret = r;
goto error;
}
if (ctx->device_name[AudioDevice]) {
- if (dshow_list_device_options(avctx, devenum, AudioDevice, AudioSourceDevice)) {
+ if (dshow_list_device_options(avctx, devenum, AudioDevice, AudioSourceDevice, NULL, CAP_QUERY_NONE)) {
/* show audio options from combined video+audio sources as fallback */
- if ((r = dshow_list_device_options(avctx, devenum, AudioDevice, VideoSourceDevice))) {
+ if ((r = dshow_list_device_options(avctx, devenum, AudioDevice, VideoSourceDevice, NULL, CAP_QUERY_NONE))) {
ret = r;
goto error;
}
@@ -1289,6 +1511,208 @@ static int dshow_read_packet(AVFormatContext *s, AVPacket *pkt)
return ctx->eof ? AVERROR(EIO) : pkt->size;
}
+// TODO: how to expose extra info? If no field found, check if it in a set of extra keys (like color_range)?
+static int dshow_query_ranges_func(AVOptionRanges** ranges_arg, void* obj, const char* key, int flags)
+{
+ AVDeviceCapabilitiesQuery *caps = obj;
+ const AVFormatContext *avctx = caps->device_context;
+ struct dshow_ctx *ctx = avctx->priv_data;
+
+ int backup_sample_size;
+ int backup_sample_rate;
+ int backup_channels;
+ enum AVCodecID backup_video_codec_id;
+ enum AVPixelFormat backup_pixel_format;
+ int backup_requested_width;
+ int backup_requested_height;
+ char* backup_framerate = NULL;
+
+ enum DshowCapQueryType query_type = CAP_QUERY_NONE;
+
+ AVOptionRanges *ranges = av_mallocz(sizeof(AVOptionRanges));
+ const AVOption *field = av_opt_find(obj, key, NULL, 0, flags);
+ int ret;
+
+ ICreateDevEnum *devenum = NULL;
+
+ *ranges_arg = NULL;
+
+ if (!ranges) {
+ ret = AVERROR(ENOMEM);
+ goto fail1;
+ }
+
+ if (!field) {
+ ret = AVERROR_OPTION_NOT_FOUND;
+ goto fail1;
+ }
+
+ // turn option name into cap query
+ query_type = dshow_get_query_type(field->name);
+
+ if (query_type == CAP_QUERY_NONE) {
+ av_log(avctx, AV_LOG_ERROR, "Querying the option %s is not supported for a dshow device\n",field->name);
+ ret = AVERROR(EINVAL);
+ goto fail1;
+ }
+
+ if (ctx->device_name[0]) {
+ av_log(avctx, AV_LOG_ERROR, "You cannot query device capabilities on an opened device\n");
+ ret = AVERROR(EIO);
+ goto fail1;
+ }
+
+ if (!parse_device_name(avctx)) {
+ av_log(avctx, AV_LOG_ERROR, "You must set a device name (AVFormatContext url) to specify which device to query capabilities from\n");
+ ret = AVERROR(EINVAL);
+ goto fail1;
+ }
+
+ // take backup of dshow parameters/options
+ // audio
+ backup_sample_size = ctx->sample_size;
+ backup_sample_rate = ctx->sample_rate;
+ backup_channels = ctx->channels;
+ // video
+ backup_video_codec_id = ctx->video_codec_id;
+ backup_pixel_format = ctx->pixel_format;
+ backup_requested_width = ctx->requested_width;
+ backup_requested_height= ctx->requested_height;
+ backup_framerate = ctx->framerate;
+
+
+ // set format constraints set in AVDeviceCapabilitiesQuery
+ // audio (NB: channel_layout not used)
+ ctx->sample_size = av_get_bytes_per_sample(caps->sample_format) << 3;
+ ctx->sample_rate = (caps->sample_rate == -1) ? 0 : caps->sample_rate;
+ ctx->channels = (caps->channels == -1) ? 0 : caps->channels;
+ // video (NB: window_width and window_height not used)
+ ctx->video_codec_id = caps->codec;
+ ctx->pixel_format = caps->pixel_format;
+ ctx->requested_width = caps->frame_width;
+ ctx->requested_height = caps->frame_height;
+ // checking whether requested framerate is set is done by !ctx->framerate
+ if (caps->fps.num > 0 && caps->fps.den > 0) {
+ ctx->requested_framerate = caps->fps;
+ ctx->framerate = av_strdup("dummy"); // just make sure its non-zero
+ if (!ctx->framerate) {
+ ret = AVERROR(ENOMEM);
+ goto fail2;
+ }
+ }
+ else
+ ctx->framerate = NULL; // make sure its NULL (if it wasn't, we already have a backup of the pointer to restore later)
+
+ // now iterate matching format of pin that would be selected when device
+ // is opened with options currently in effect.
+ // for each matching format, output its parameter range, also if that same
+ // range already returned for another format. That way, user can reconstruct
+ // possible valid combinations by av_opt_query_ranges() for each of the
+ // format options and matching returned values by sequence number.
+ CoInitialize(0);
+
+ if (CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
+ &IID_ICreateDevEnum, (void **) &devenum)) {
+ av_log(avctx, AV_LOG_ERROR, "Could not enumerate system devices.\n");
+ goto fail2;
+ }
+ ctx->video_codec_id = avctx->video_codec_id ? avctx->video_codec_id
+ : AV_CODEC_ID_RAWVIDEO;
+
+ ranges->nb_components = field->type == AV_OPT_TYPE_IMAGE_SIZE ? 3 : 1;
+ if (ctx->device_name[VideoDevice])
+ if ((ret = dshow_list_device_options(avctx, devenum, VideoDevice, VideoSourceDevice, ranges, query_type)) < 0)
+ goto fail2;
+ if (ctx->device_name[AudioDevice]) {
+ if (dshow_list_device_options(avctx, devenum, AudioDevice, AudioSourceDevice, ranges, query_type) < 0) {
+ /* show audio options from combined video+audio sources as fallback */
+ if ((ret = dshow_list_device_options(avctx, devenum, AudioDevice, VideoSourceDevice, ranges, query_type)) < 0)
+ goto fail2;
+ }
+ }
+ ret = ranges->nb_ranges ? ranges->nb_components : 0;
+
+ // when dealing with a multi-component item (regardless of whether
+ // AV_OPT_MULTI_COMPONENT_RANGE is set or not), we need to reorganize the
+ // output range array from [r1_c1 r1_c2 r1_c3 r2_c1 r2_c2 r2_c3 ...] to
+ // [r1_c1 r2_c1 ... r1_c2 r2_c2 ... r1_c3 r2_c3 ...] to be consistent with
+ // documentation of AVOptionRanges in libavutil/opt.h
+ // this is only the case for a AV_OPT_TYPE_IMAGE_SIZE option.
+ if (ranges->nb_ranges && ranges->nb_components>1) {
+ AVOptionRanges* old_ranges;
+ ranges->nb_ranges /= ranges->nb_components;
+ old_ranges = ranges;
+ ranges = av_mallocz(sizeof(AVOptionRanges));
+ if (!ranges) {
+ ranges = old_ranges; // for cleanup
+ ret = AVERROR(ENOMEM);
+ goto fail2;
+ }
+ ranges->nb_components = old_ranges->nb_components;
+ ranges->nb_ranges = old_ranges->nb_ranges;
+ ranges->range = av_malloc(ranges->nb_components * ranges->nb_ranges * sizeof(AVOptionRange*));
+
+ for (int n = 0; n < ranges->nb_components * ranges->nb_ranges; n++) {
+ int i = n / ranges->nb_components;
+ int j = n % ranges->nb_components;
+ ranges->range[ranges->nb_ranges * j + i] = old_ranges->range[n];
+ old_ranges->range[n] = NULL; // don't keep double references
+ }
+
+ av_opt_freep_ranges(&old_ranges);
+ }
+
+ // success, set output
+ *ranges_arg = ranges;
+
+fail2:
+ // set dshow parameters/options back to original values
+ // audio
+ ctx->sample_size = backup_sample_size;
+ ctx->sample_rate = backup_sample_rate;
+ ctx->channels = backup_channels;
+ // video
+ ctx->video_codec_id = backup_video_codec_id;
+ ctx->pixel_format = backup_pixel_format;
+ ctx->requested_width = backup_requested_width;
+ ctx->requested_height = backup_requested_height;
+ if (ctx->framerate)
+ av_free(ctx->framerate);
+ ctx->framerate = backup_framerate;
+
+ if (devenum)
+ ICreateDevEnum_Release(devenum);
+
+ dshow_read_close(avctx); // clears filenames and removes device_filters or other state variables that may have been set
+
+fail1:
+ if (ret < 0)
+ av_opt_freep_ranges(&ranges);
+
+ return ret;
+}
+
+// fake class to point av_opt_query_ranges to our query_ranges function
+static const AVClass dshow_dev_caps_class = {
+ .class_name = "",
+ .item_name = av_default_item_name,
+ .option = av_device_capabilities,
+ .version = LIBAVDEVICE_VERSION_INT,
+ .query_ranges = dshow_query_ranges_func,
+};
+
+static int dshow_create_device_capabilities(struct AVFormatContext* s, struct AVDeviceCapabilitiesQuery* caps)
+{
+ caps->av_class = &dshow_dev_caps_class;
+ return 0;
+}
+
+static int dshow_free_device_capabilities(struct AVFormatContext* s, struct AVDeviceCapabilitiesQuery* caps)
+{
+ // nothing to clean up
+ return 0;
+}
+
#define OFFSET(x) offsetof(struct dshow_ctx, x)
#define DEC AV_OPT_FLAG_DECODING_PARAM
static const AVOption options[] = {
@@ -1335,6 +1759,8 @@ const AVInputFormat ff_dshow_demuxer = {
.read_header = dshow_read_header,
.read_packet = dshow_read_packet,
.read_close = dshow_read_close,
+ .create_device_capabilities = dshow_create_device_capabilities,
+ .free_device_capabilities = dshow_free_device_capabilities,
.flags = AVFMT_NOFILE,
.priv_class = &dshow_class,
};
--
2.28.0.windows.1
More information about the ffmpeg-devel
mailing list