Add support for enumerating the sources/sinks via the ffmpeg command line options, as opposed to having to create a real pipeline and use the "-list_devices" option which does exit() after dumping out the options. Note that this patch preserves the existing "-list_devices" option, but now shares common code for the actual enumeration. Updated to reflect feedback from Marton Balint <cus@passwd.hu>. Signed-off-by: Devin Heitmueller <dheitmueller@ltnglobal.com> Signed-off-by: Marton Balint <cus@passwd.hu>tags/n3.4
@@ -37,6 +37,7 @@ extern "C" { | |||||
#include "libavutil/imgutils.h" | #include "libavutil/imgutils.h" | ||||
#include "libavutil/intreadwrite.h" | #include "libavutil/intreadwrite.h" | ||||
#include "libavutil/bswap.h" | #include "libavutil/bswap.h" | ||||
#include "avdevice.h" | |||||
} | } | ||||
#include "decklink_common.h" | #include "decklink_common.h" | ||||
@@ -261,24 +262,100 @@ int ff_decklink_set_format(AVFormatContext *avctx, decklink_direction_t directio | |||||
return ff_decklink_set_format(avctx, 0, 0, 0, 0, AV_FIELD_UNKNOWN, direction, num); | return ff_decklink_set_format(avctx, 0, 0, 0, 0, AV_FIELD_UNKNOWN, direction, num); | ||||
} | } | ||||
int ff_decklink_list_devices(AVFormatContext *avctx) | |||||
int ff_decklink_list_devices(AVFormatContext *avctx, | |||||
struct AVDeviceInfoList *device_list, | |||||
int show_inputs, int show_outputs) | |||||
{ | { | ||||
IDeckLink *dl = NULL; | IDeckLink *dl = NULL; | ||||
IDeckLinkIterator *iter = CreateDeckLinkIteratorInstance(); | IDeckLinkIterator *iter = CreateDeckLinkIteratorInstance(); | ||||
int ret = 0; | |||||
if (!iter) { | if (!iter) { | ||||
av_log(avctx, AV_LOG_ERROR, "Could not create DeckLink iterator\n"); | av_log(avctx, AV_LOG_ERROR, "Could not create DeckLink iterator\n"); | ||||
return AVERROR(EIO); | return AVERROR(EIO); | ||||
} | } | ||||
av_log(avctx, AV_LOG_INFO, "Blackmagic DeckLink devices:\n"); | |||||
while (iter->Next(&dl) == S_OK) { | |||||
while (ret == 0 && iter->Next(&dl) == S_OK) { | |||||
IDeckLinkOutput *output_config; | |||||
IDeckLinkInput *input_config; | |||||
const char *displayName; | const char *displayName; | ||||
AVDeviceInfo *new_device = NULL; | |||||
int add = 0; | |||||
ff_decklink_get_display_name(dl, &displayName); | ff_decklink_get_display_name(dl, &displayName); | ||||
av_log(avctx, AV_LOG_INFO, "\t'%s'\n", displayName); | |||||
av_free((void *) displayName); | |||||
if (show_outputs) { | |||||
if (dl->QueryInterface(IID_IDeckLinkOutput, (void **)&output_config) == S_OK) { | |||||
output_config->Release(); | |||||
add = 1; | |||||
} | |||||
} | |||||
if (show_inputs) { | |||||
if (dl->QueryInterface(IID_IDeckLinkInput, (void **)&input_config) == S_OK) { | |||||
input_config->Release(); | |||||
add = 1; | |||||
} | |||||
} | |||||
if (add == 1) { | |||||
new_device = (AVDeviceInfo *) av_mallocz(sizeof(AVDeviceInfo)); | |||||
if (!new_device) { | |||||
ret = AVERROR(ENOMEM); | |||||
goto next; | |||||
} | |||||
new_device->device_name = av_strdup(displayName); | |||||
if (!new_device->device_name) { | |||||
ret = AVERROR(ENOMEM); | |||||
goto next; | |||||
} | |||||
new_device->device_description = av_strdup(displayName); | |||||
if (!new_device->device_description) { | |||||
av_freep(&new_device->device_name); | |||||
ret = AVERROR(ENOMEM); | |||||
goto next; | |||||
} | |||||
if ((ret = av_dynarray_add_nofree(&device_list->devices, | |||||
&device_list->nb_devices, new_device)) < 0) { | |||||
av_freep(&new_device->device_name); | |||||
av_freep(&new_device->device_description); | |||||
av_freep(&new_device); | |||||
goto next; | |||||
} | |||||
} | |||||
next: | |||||
av_freep(&displayName); | |||||
dl->Release(); | dl->Release(); | ||||
} | } | ||||
iter->Release(); | iter->Release(); | ||||
return 0; | |||||
return ret; | |||||
} | |||||
/* This is a wrapper around the ff_decklink_list_devices() which dumps the | |||||
output to av_log() and exits (for backward compatibility with the | |||||
"-list_devices" argument). */ | |||||
void ff_decklink_list_devices_legacy(AVFormatContext *avctx, | |||||
int show_inputs, int show_outputs) | |||||
{ | |||||
struct AVDeviceInfoList *device_list = NULL; | |||||
int ret; | |||||
device_list = (struct AVDeviceInfoList *) av_mallocz(sizeof(AVDeviceInfoList)); | |||||
if (!device_list) | |||||
return; | |||||
ret = ff_decklink_list_devices(avctx, device_list, show_inputs, show_outputs); | |||||
if (ret == 0) { | |||||
av_log(avctx, AV_LOG_INFO, "Blackmagic DeckLink %s devices:\n", | |||||
show_inputs ? "input" : "output"); | |||||
for (int i = 0; i < device_list->nb_devices; i++) { | |||||
av_log(avctx, AV_LOG_INFO, "\t'%s'\n", device_list->devices[i]->device_name); | |||||
} | |||||
} | |||||
avdevice_free_list_devices(&device_list); | |||||
} | } | ||||
int ff_decklink_list_formats(AVFormatContext *avctx, decklink_direction_t direction) | int ff_decklink_list_formats(AVFormatContext *avctx, decklink_direction_t direction) | ||||
@@ -135,7 +135,8 @@ static const BMDVideoConnection decklink_video_connection_map[] = { | |||||
HRESULT ff_decklink_get_display_name(IDeckLink *This, const char **displayName); | HRESULT ff_decklink_get_display_name(IDeckLink *This, const char **displayName); | ||||
int ff_decklink_set_format(AVFormatContext *avctx, int width, int height, int tb_num, int tb_den, enum AVFieldOrder field_order, decklink_direction_t direction = DIRECTION_OUT, int num = 0); | int ff_decklink_set_format(AVFormatContext *avctx, int width, int height, int tb_num, int tb_den, enum AVFieldOrder field_order, decklink_direction_t direction = DIRECTION_OUT, int num = 0); | ||||
int ff_decklink_set_format(AVFormatContext *avctx, decklink_direction_t direction, int num); | int ff_decklink_set_format(AVFormatContext *avctx, decklink_direction_t direction, int num); | ||||
int ff_decklink_list_devices(AVFormatContext *avctx); | |||||
int ff_decklink_list_devices(AVFormatContext *avctx, struct AVDeviceInfoList *device_list, int show_inputs, int show_outputs); | |||||
void ff_decklink_list_devices_legacy(AVFormatContext *avctx, int show_inputs, int show_outputs); | |||||
int ff_decklink_list_formats(AVFormatContext *avctx, decklink_direction_t direction = DIRECTION_OUT); | int ff_decklink_list_formats(AVFormatContext *avctx, decklink_direction_t direction = DIRECTION_OUT); | ||||
void ff_decklink_cleanup(AVFormatContext *avctx); | void ff_decklink_cleanup(AVFormatContext *avctx); | ||||
int ff_decklink_init_device(AVFormatContext *avctx, const char* name); | int ff_decklink_init_device(AVFormatContext *avctx, const char* name); | ||||
@@ -39,6 +39,7 @@ extern "C" { | |||||
#include "libavutil/time.h" | #include "libavutil/time.h" | ||||
#include "libavutil/mathematics.h" | #include "libavutil/mathematics.h" | ||||
#include "libavutil/reverse.h" | #include "libavutil/reverse.h" | ||||
#include "avdevice.h" | |||||
#if CONFIG_LIBZVBI | #if CONFIG_LIBZVBI | ||||
#include <libzvbi.h> | #include <libzvbi.h> | ||||
#endif | #endif | ||||
@@ -868,7 +869,7 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) | |||||
/* List available devices. */ | /* List available devices. */ | ||||
if (ctx->list_devices) { | if (ctx->list_devices) { | ||||
ff_decklink_list_devices(avctx); | |||||
ff_decklink_list_devices_legacy(avctx, 1, 0); | |||||
return AVERROR_EXIT; | return AVERROR_EXIT; | ||||
} | } | ||||
@@ -1063,4 +1064,9 @@ int ff_decklink_read_packet(AVFormatContext *avctx, AVPacket *pkt) | |||||
return 0; | return 0; | ||||
} | } | ||||
int ff_decklink_list_input_devices(AVFormatContext *avctx, struct AVDeviceInfoList *device_list) | |||||
{ | |||||
return ff_decklink_list_devices(avctx, device_list, 1, 0); | |||||
} | |||||
} /* extern "C" */ | } /* extern "C" */ |
@@ -29,6 +29,7 @@ extern "C" { | |||||
int ff_decklink_read_header(AVFormatContext *avctx); | int ff_decklink_read_header(AVFormatContext *avctx); | ||||
int ff_decklink_read_packet(AVFormatContext *avctx, AVPacket *pkt); | int ff_decklink_read_packet(AVFormatContext *avctx, AVPacket *pkt); | ||||
int ff_decklink_read_close(AVFormatContext *avctx); | int ff_decklink_read_close(AVFormatContext *avctx); | ||||
int ff_decklink_list_input_devices(AVFormatContext *avctx, struct AVDeviceInfoList *device_list); | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
} /* extern "C" */ | } /* extern "C" */ | ||||
@@ -89,6 +89,7 @@ AVInputFormat ff_decklink_demuxer = { | |||||
.flags = AVFMT_NOFILE, | .flags = AVFMT_NOFILE, | ||||
.priv_class = &decklink_demuxer_class, | .priv_class = &decklink_demuxer_class, | ||||
.priv_data_size = sizeof(struct decklink_cctx), | .priv_data_size = sizeof(struct decklink_cctx), | ||||
.get_device_list = ff_decklink_list_input_devices, | |||||
.read_header = ff_decklink_read_header, | .read_header = ff_decklink_read_header, | ||||
.read_packet = ff_decklink_read_packet, | .read_packet = ff_decklink_read_packet, | ||||
.read_close = ff_decklink_read_close, | .read_close = ff_decklink_read_close, | ||||
@@ -33,6 +33,7 @@ extern "C" { | |||||
extern "C" { | extern "C" { | ||||
#include "libavformat/avformat.h" | #include "libavformat/avformat.h" | ||||
#include "libavutil/imgutils.h" | #include "libavutil/imgutils.h" | ||||
#include "avdevice.h" | |||||
} | } | ||||
#include "decklink_common.h" | #include "decklink_common.h" | ||||
@@ -335,9 +336,9 @@ av_cold int ff_decklink_write_header(AVFormatContext *avctx) | |||||
ctx->preroll = cctx->preroll; | ctx->preroll = cctx->preroll; | ||||
cctx->ctx = ctx; | cctx->ctx = ctx; | ||||
/* List available devices. */ | |||||
/* List available devices and exit. */ | |||||
if (ctx->list_devices) { | if (ctx->list_devices) { | ||||
ff_decklink_list_devices(avctx); | |||||
ff_decklink_list_devices_legacy(avctx, 0, 1); | |||||
return AVERROR_EXIT; | return AVERROR_EXIT; | ||||
} | } | ||||
@@ -400,4 +401,9 @@ int ff_decklink_write_packet(AVFormatContext *avctx, AVPacket *pkt) | |||||
return AVERROR(EIO); | return AVERROR(EIO); | ||||
} | } | ||||
int ff_decklink_list_output_devices(AVFormatContext *avctx, struct AVDeviceInfoList *device_list) | |||||
{ | |||||
return ff_decklink_list_devices(avctx, device_list, 0, 1); | |||||
} | |||||
} /* extern "C" */ | } /* extern "C" */ |
@@ -29,6 +29,7 @@ extern "C" { | |||||
int ff_decklink_write_header(AVFormatContext *avctx); | int ff_decklink_write_header(AVFormatContext *avctx); | ||||
int ff_decklink_write_packet(AVFormatContext *avctx, AVPacket *pkt); | int ff_decklink_write_packet(AVFormatContext *avctx, AVPacket *pkt); | ||||
int ff_decklink_write_trailer(AVFormatContext *avctx); | int ff_decklink_write_trailer(AVFormatContext *avctx); | ||||
int ff_decklink_list_output_devices(AVFormatContext *avctx, struct AVDeviceInfoList *device_list); | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
} /* extern "C" */ | } /* extern "C" */ | ||||
@@ -49,6 +49,7 @@ AVOutputFormat ff_decklink_muxer = { | |||||
.video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, | .video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, | ||||
.subtitle_codec = AV_CODEC_ID_NONE, | .subtitle_codec = AV_CODEC_ID_NONE, | ||||
.flags = AVFMT_NOFILE, | .flags = AVFMT_NOFILE, | ||||
.get_device_list = ff_decklink_list_output_devices, | |||||
.priv_class = &decklink_muxer_class, | .priv_class = &decklink_muxer_class, | ||||
.priv_data_size = sizeof(struct decklink_cctx), | .priv_data_size = sizeof(struct decklink_cctx), | ||||
.write_header = ff_decklink_write_header, | .write_header = ff_decklink_write_header, | ||||