FFmpeg : improvements to ffmpeg::list() functions (#2773)

* added a couple more audio examples

* statically initialize all list_() functions where possible.

---------

Co-authored-by: pf <pf@me>
pull/2779/head
pfeatherstone 1 year ago committed by GitHub
parent b1fe026e06
commit 5f7026ab52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -296,55 +296,73 @@ namespace dlib
{
/*!
WHAT THIS OBJECT REPRESENTS
This object informs on available devices provided by the installation of ffmpeg dlib is linked against.
This object informs on available device types provided by the installation of ffmpeg dlib is linked against.
!*/
struct instance
std::string device_type;
bool is_audio_type{false};
bool is_video_type{false};
};
struct device_instance
{
/*!
WHAT THIS OBJECT REPRESENTS
This object informs on the currently available device instances readable by ffmpeg.
!*/
std::string name;
std::string description;
};
std::string device_type;
std::vector<instance> devices;
};
std::vector<std::string> list_protocols();
const std::vector<std::string>& list_protocols();
/*!
ensures
- returns a list of all registered ffmpeg protocols
- returns a list of all available ffmpeg protocols
!*/
std::vector<std::string> list_demuxers();
const std::vector<std::string>& list_demuxers();
/*!
ensures
- returns a list of all registered ffmpeg demuxers
- returns a list of all available ffmpeg demuxers
!*/
std::vector<muxer_details> list_muxers();
const std::vector<muxer_details>& list_muxers();
/*!
ensures
- returns a list of all registered ffmpeg muxers
- returns a list of all available ffmpeg muxers
!*/
std::vector<codec_details> list_codecs();
const std::vector<codec_details>& list_codecs();
/*!
ensures
- returns a list of all registered ffmpeg codecs with information on whether decoding and/or encoding is supported.
- returns a list of all available ffmpeg codecs with information on whether decoding and/or encoding is supported.
Note that not all codecs support encoding, unless your installation of ffmpeg is built with third party library
dependencies like libx264, libx265, etc.
!*/
std::vector<device_details> list_input_devices();
const std::vector<device_details>& list_input_device_types();
/*!
ensures
- returns a list of all available ffmpeg input device types (e.g. alsa, v4l2, etc)
!*/
const std::vector<device_details>& list_output_device_types();
/*!
ensures
- returns a list of all available ffmpeg output device types (e.g. alsa, v4l2, etc)
!*/
std::vector<device_instance> list_input_device_instances(const std::string& device_type);
/*!
ensures
- returns a list of all registered ffmpeg input devices and available instances of those devices
- returns a list of all available ffmpeg input device instances for device type *device_type (e.g. hw:0,0, /dev/video0, etc)
!*/
std::vector<device_details> list_output_devices();
std::vector<device_instance> list_output_device_instances(const std::string& device_type);
/*!
ensures
- returns a list of all registered ffmpeg output devices and available instances of those devices
- returns a list of all available ffmpeg output device instances for device type *device_type (e.g. hw:0,0, /dev/video0, etc)
!*/
// ---------------------------------------------------------------------------------------------------

@ -662,8 +662,6 @@ namespace dlib
if (!st.args_.enable_audio && !st.args_.enable_image)
return fail(*st.log, "You need to set at least one of `enable_audio` or `enable_image`");
static const auto all_codecs = list_codecs();
{
st.connecting_time = system_clock::now();
st.connected_time = system_clock::time_point::max();
@ -723,7 +721,7 @@ namespace dlib
};
// Before we create the encoder, check the codec is supported by this muxer
const auto supported_codecs = list_codecs_for_muxer(st.pFormatCtx->oformat, all_codecs);
const auto supported_codecs = list_codecs_for_muxer(st.pFormatCtx->oformat);
if (std::find_if(begin(supported_codecs), end(supported_codecs), [&](const auto& supported) {
return args.args_codec.codec != AV_CODEC_ID_NONE ?

@ -360,22 +360,25 @@ namespace dlib
struct device_details
{
struct instance
std::string device_type;
bool is_audio_type{false};
bool is_video_type{false};
};
struct device_instance
{
std::string name;
std::string description;
};
std::string device_type;
std::vector<instance> devices;
};
std::vector<std::string> list_protocols();
std::vector<std::string> list_demuxers();
std::vector<muxer_details> list_muxers();
std::vector<codec_details> list_codecs();
std::vector<device_details> list_input_devices();
std::vector<device_details> list_output_devices();
const std::vector<std::string>& list_protocols();
const std::vector<std::string>& list_demuxers();
const std::vector<muxer_details>& list_muxers();
const std::vector<codec_details>& list_codecs();
const std::vector<device_details>& list_input_device_types();
const std::vector<device_details>& list_output_device_types();
std::vector<device_instance> list_input_device_instances(const std::string& device_type);
std::vector<device_instance> list_output_device_instances(const std::string& device_type);
// ---------------------------------------------------------------------------------------------------
@ -1185,7 +1188,9 @@ namespace dlib
// ---------------------------------------------------------------------------------------------------
inline std::vector<std::string> list_protocols()
inline const std::vector<std::string>& list_protocols()
{
const static auto protocols = []
{
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
std::vector<std::string> protocols;
@ -1200,39 +1205,46 @@ namespace dlib
while (init && (name = avio_enum_protocols(&opaque, 1)))
protocols.emplace_back(name);
return protocols;
}();
return protocols;
}
// ---------------------------------------------------------------------------------------------------
inline std::vector<std::string> list_demuxers()
inline const std::vector<std::string>& list_demuxers()
{
const static auto demuxers = []
{
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
std::vector<std::string> demuxers;
const AVInputFormat* demuxer = nullptr;
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
// See https://github.com/FFmpeg/FFmpeg/blob/70d25268c21cbee5f08304da95be1f647c630c15/doc/APIchanges#L86
while (init && (demuxer = av_iformat_next(demuxer)))
#else
#else
void* opaque = nullptr;
while (init && (demuxer = av_demuxer_iterate(&opaque)))
#endif
#endif
demuxers.push_back(demuxer->name);
return demuxers;
}();
return demuxers;
}
// ---------------------------------------------------------------------------------------------------
inline std::vector<codec_details> list_codecs_for_muxer (
const AVOutputFormat* oformat,
const std::vector<codec_details>& all_codecs = list_codecs()
const AVOutputFormat* oformat
)
{
std::vector<codec_details> supported_codecs;
for (const auto& codec : all_codecs)
for (const auto& codec : list_codecs())
if (avformat_query_codec(oformat, codec.codec_id, FF_COMPLIANCE_STRICT) == 1)
supported_codecs.push_back(codec);
@ -1241,35 +1253,40 @@ namespace dlib
// ---------------------------------------------------------------------------------------------------
inline std::vector<muxer_details> list_muxers()
inline const std::vector<muxer_details>& list_muxers()
{
const static auto ret = []
{
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
const auto codecs = list_codecs();
std::vector<muxer_details> all_details;
const AVOutputFormat* muxer = nullptr;
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
// See https://github.com/FFmpeg/FFmpeg/blob/70d25268c21cbee5f08304da95be1f647c630c15/doc/APIchanges#L86
while (init && (muxer = av_oformat_next(muxer)))
#else
#else
void* opaque = nullptr;
while (init && (muxer = av_muxer_iterate(&opaque)))
#endif
#endif
{
muxer_details details;
details.name = muxer->name;
details.supported_codecs = list_codecs_for_muxer(muxer, codecs);
details.supported_codecs = list_codecs_for_muxer(muxer);
all_details.push_back(details);
}
return all_details;
}();
return ret;
}
// ---------------------------------------------------------------------------------------------------
inline std::vector<codec_details> list_codecs()
inline const std::vector<codec_details>& list_codecs()
{
const static auto ret = []
{
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
std::vector<codec_details> details;
@ -1311,11 +1328,16 @@ namespace dlib
details.erase(std::remove_if(details.begin(), details.end(), [](const auto& d) {return d.codec_name.empty();}), details.end());
return details;
}();
return ret;
}
// ---------------------------------------------------------------------------------------------------
inline std::vector<device_details> list_input_devices()
inline const std::vector<device_details>& list_input_device_types()
{
const static auto ret = []
{
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
std::vector<device_details> devices;
@ -1327,91 +1349,130 @@ namespace dlib
#endif
AVInputFormatPtr device{nullptr};
details::av_ptr<AVDeviceInfoList> managed;
const auto iter = [&](AVInputFormatPtr device)
while (init && (device = av_input_audio_device_next(device)))
{
device_details details;
details.device_type = std::string(device->name);
details.device_type = device->name;
details.is_audio_type = true;
devices.push_back(std::move(details));
}
device = nullptr;
while (init && (device = av_input_video_device_next(device)))
{
device_details details;
details.device_type = device->name;
details.is_video_type = true;
devices.push_back(std::move(details));
}
return devices;
}();
return ret;
}
// ---------------------------------------------------------------------------------------------------
inline std::vector<device_instance> list_input_device_instances(const std::string& device_type)
{
const auto& types = list_input_device_types();
auto ret = std::find_if(types.begin(), types.end(), [&](const auto& type) {return type.device_type == device_type;});
if (ret == types.end())
return {};
std::vector<device_instance> instances;
details::av_ptr<AVDeviceInfoList> managed;
AVDeviceInfoList* device_list = nullptr;
avdevice_list_input_sources(device, nullptr, nullptr, &device_list);
avdevice_list_input_sources(nullptr, ret->device_type.c_str(), nullptr, &device_list);
managed.reset(device_list);
if (device_list)
{
for (int i = 0 ; i < device_list->nb_devices ; ++i)
{
device_details::instance instance;
device_instance instance;
instance.name = std::string(device_list->devices[i]->device_name);
instance.description = std::string(device_list->devices[i]->device_description);
details.devices.push_back(std::move(instance));
instances.push_back(std::move(instance));
}
}
devices.push_back(std::move(details));
};
while (init && (device = av_input_audio_device_next(device)))
iter(device);
device = nullptr;
while (init && (device = av_input_video_device_next(device)))
iter(device);
return devices;
return instances;
}
// ---------------------------------------------------------------------------------------------------
inline std::vector<device_details> list_output_devices()
inline const std::vector<device_details>& list_output_device_types()
{
const static auto ret = []
{
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
std::vector<device_details> devices;
#if LIBAVDEVICE_VERSION_INT < AV_VERSION_INT(59, 0, 100)
#if LIBAVDEVICE_VERSION_INT < AV_VERSION_INT(59, 0, 100)
using AVOutputFormatPtr = AVOutputFormat*;
#else
#else
using AVOutputFormatPtr = const AVOutputFormat*;
#endif
#endif
AVOutputFormatPtr device{nullptr};
details::av_ptr<AVDeviceInfoList> managed;
while (init && (device = av_output_audio_device_next(device)))
{
device_details details;
details.device_type = std::string(device->name);
details.is_audio_type = true;
devices.push_back(std::move(details));
}
const auto iter = [&](AVOutputFormatPtr device)
device = nullptr;
while (init && (device = av_output_video_device_next(device)))
{
device_details details;
details.device_type = std::string(device->name);
details.is_video_type = true;
devices.push_back(std::move(details));
}
return devices;
}();
return ret;
}
// ---------------------------------------------------------------------------------------------------
std::vector<device_instance> list_output_device_instances(const std::string& device_type)
{
const auto& types = list_output_device_types();
auto ret = std::find_if(types.begin(), types.end(), [&](const auto& type) {return type.device_type == device_type;});
if (ret == types.end())
return {};
std::vector<device_instance> instances;
details::av_ptr<AVDeviceInfoList> managed;
AVDeviceInfoList* device_list = nullptr;
avdevice_list_output_sinks(device, nullptr, nullptr, &device_list);
avdevice_list_output_sinks(nullptr, ret->device_type.c_str(), nullptr, &device_list);
managed.reset(device_list);
if (device_list)
{
for (int i = 0 ; i < device_list->nb_devices ; ++i)
{
device_details::instance instance;
device_instance instance;
instance.name = std::string(device_list->devices[i]->device_name);
instance.description = std::string(device_list->devices[i]->device_description);
details.devices.push_back(std::move(instance));
instances.push_back(std::move(instance));
}
}
devices.push_back(std::move(details));
};
while (init && (device = av_output_audio_device_next(device)))
iter(device);
device = nullptr;
while (init && (device = av_output_video_device_next(device)))
iter(device);
return devices;
return instances;
}
// ---------------------------------------------------------------------------------------------------

@ -49,13 +49,15 @@ int main()
// List all input devices supported by this installation of ffmpeg libraries
cout << "Supported input devices:\n";
for (const auto& device : ffmpeg::list_input_devices())
for (const auto& device : ffmpeg::list_input_device_types())
{
cout << " device type : " << device.device_type << '\n';
if (!device.devices.empty())
cout << " device type : `" << device.device_type << "` is audio " << device.is_audio_type << " is video " << device.is_video_type << '\n';
const auto instances = ffmpeg::list_input_device_instances(device.device_type);
if (!instances.empty())
{
cout << " instances :\n";
for (const auto& instance : device.devices)
for (const auto& instance : instances)
cout << " name : " << left << setw(32) << instance.name << ", description : " << instance.description << '\n';
}
}
@ -64,13 +66,15 @@ int main()
// List all input devices supported by this installation of ffmpeg libraries
cout << "Supported output devices:\n";
for (const auto& device : ffmpeg::list_output_devices())
for (const auto& device : ffmpeg::list_output_device_types())
{
cout << " device type : " << device.device_type << '\n';
if (!device.devices.empty())
cout << " device type : `" << device.device_type << "` is audio " << device.is_audio_type << " is video " << device.is_video_type << '\n';
const auto instances = ffmpeg::list_output_device_instances(device.device_type);
if (!instances.empty())
{
cout << " instances :\n";
for (const auto& instance : device.devices)
for (const auto& instance : instances)
cout << " name : " << left << setw(32) << instance.name << ", description : " << instance.description << '\n';
}
}

Loading…
Cancel
Save