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 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 name;
std::string description;
};
std::string device_type; std::string device_type;
std::vector<instance> devices; bool is_audio_type{false};
bool is_video_type{false};
}; };
std::vector<std::string> list_protocols(); 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;
};
const std::vector<std::string>& list_protocols();
/*! /*!
ensures 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 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 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 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 Note that not all codecs support encoding, unless your installation of ffmpeg is built with third party library
dependencies like libx264, libx265, etc. 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 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 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) 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`"); 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.connecting_time = system_clock::now();
st.connected_time = system_clock::time_point::max(); 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 // 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) { if (std::find_if(begin(supported_codecs), end(supported_codecs), [&](const auto& supported) {
return args.args_codec.codec != AV_CODEC_ID_NONE ? return args.args_codec.codec != AV_CODEC_ID_NONE ?

@ -360,22 +360,25 @@ namespace dlib
struct device_details struct device_details
{ {
struct instance
{
std::string name;
std::string description;
};
std::string device_type; std::string device_type;
std::vector<instance> devices; bool is_audio_type{false};
bool is_video_type{false};
}; };
std::vector<std::string> list_protocols(); struct device_instance
std::vector<std::string> list_demuxers(); {
std::vector<muxer_details> list_muxers(); std::string name;
std::vector<codec_details> list_codecs(); std::string description;
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,40 +1188,50 @@ namespace dlib
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------
inline std::vector<std::string> list_protocols() inline const std::vector<std::string>& list_protocols()
{ {
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away const static auto protocols = []
std::vector<std::string> protocols; {
void* opaque = nullptr; const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
const char* name = 0; std::vector<std::string> protocols;
while (init && (name = avio_enum_protocols(&opaque, 0))) void* opaque = nullptr;
protocols.emplace_back(name); const char* name = 0;
while (init && (name = avio_enum_protocols(&opaque, 0)))
protocols.emplace_back(name);
opaque = nullptr;
name = 0;
opaque = nullptr; while (init && (name = avio_enum_protocols(&opaque, 1)))
name = 0; protocols.emplace_back(name);
while (init && (name = avio_enum_protocols(&opaque, 1))) return protocols;
protocols.emplace_back(name); }();
return protocols; return protocols;
} }
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------
inline std::vector<std::string> list_demuxers() inline const std::vector<std::string>& list_demuxers()
{ {
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away const static auto demuxers = []
std::vector<std::string> demuxers; {
const AVInputFormat* demuxer = nullptr; 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 // See https://github.com/FFmpeg/FFmpeg/blob/70d25268c21cbee5f08304da95be1f647c630c15/doc/APIchanges#L86
while (init && (demuxer = av_iformat_next(demuxer))) while (init && (demuxer = av_iformat_next(demuxer)))
#else #else
void* opaque = nullptr; void* opaque = nullptr;
while (init && (demuxer = av_demuxer_iterate(&opaque))) while (init && (demuxer = av_demuxer_iterate(&opaque)))
#endif #endif
demuxers.push_back(demuxer->name); demuxers.push_back(demuxer->name);
return demuxers;
}();
return demuxers; return demuxers;
} }
@ -1226,13 +1239,12 @@ namespace dlib
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------
inline std::vector<codec_details> list_codecs_for_muxer ( inline std::vector<codec_details> list_codecs_for_muxer (
const AVOutputFormat* oformat, const AVOutputFormat* oformat
const std::vector<codec_details>& all_codecs = list_codecs()
) )
{ {
std::vector<codec_details> supported_codecs; 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) if (avformat_query_codec(oformat, codec.codec_id, FF_COMPLIANCE_STRICT) == 1)
supported_codecs.push_back(codec); supported_codecs.push_back(codec);
@ -1241,177 +1253,226 @@ namespace dlib
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------
inline std::vector<muxer_details> list_muxers() inline const std::vector<muxer_details>& list_muxers()
{ {
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away 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;
std::vector<muxer_details> all_details; #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
const AVOutputFormat* muxer = nullptr; // See https://github.com/FFmpeg/FFmpeg/blob/70d25268c21cbee5f08304da95be1f647c630c15/doc/APIchanges#L86
while (init && (muxer = av_oformat_next(muxer)))
#else
void* opaque = nullptr;
while (init && (muxer = av_muxer_iterate(&opaque)))
#endif
{
muxer_details details;
details.name = muxer->name;
details.supported_codecs = list_codecs_for_muxer(muxer);
all_details.push_back(details);
}
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100) return all_details;
// See https://github.com/FFmpeg/FFmpeg/blob/70d25268c21cbee5f08304da95be1f647c630c15/doc/APIchanges#L86 }();
while (init && (muxer = av_oformat_next(muxer)))
#else
void* opaque = nullptr;
while (init && (muxer = av_muxer_iterate(&opaque)))
#endif
{
muxer_details details;
details.name = muxer->name;
details.supported_codecs = list_codecs_for_muxer(muxer, codecs);
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 bool init = details::register_ffmpeg::get(); // Don't let this get optimized away const static auto ret = []
std::vector<codec_details> details;
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
// See https://github.com/FFmpeg/FFmpeg/blob/70d25268c21cbee5f08304da95be1f647c630c15/doc/APIchanges#L91
AVCodec* codec = nullptr;
while (init && (codec = av_codec_next(codec)))
#else
const AVCodec* codec = nullptr;
void* opaque = nullptr;
while (init && (codec = av_codec_iterate(&opaque)))
#endif
{ {
codec_details detail; const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
detail.codec_id = codec->id; std::vector<codec_details> details;
detail.codec_name = codec->name;
detail.supports_encoding = av_codec_is_encoder(codec);
detail.supports_decoding = av_codec_is_decoder(codec);
details.push_back(std::move(detail));
}
//sort #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
std::sort(details.begin(), details.end(), [](const codec_details& a, const codec_details& b) {return a.codec_name < b.codec_name;}); // See https://github.com/FFmpeg/FFmpeg/blob/70d25268c21cbee5f08304da95be1f647c630c15/doc/APIchanges#L91
//merge AVCodec* codec = nullptr;
for (size_t i = 0 ; i < details.size() ; ++i) while (init && (codec = av_codec_next(codec)))
{ #else
for (size_t j = i + 1 ; j < details.size() ; ++j) const AVCodec* codec = nullptr;
void* opaque = nullptr;
while (init && (codec = av_codec_iterate(&opaque)))
#endif
{
codec_details detail;
detail.codec_id = codec->id;
detail.codec_name = codec->name;
detail.supports_encoding = av_codec_is_encoder(codec);
detail.supports_decoding = av_codec_is_decoder(codec);
details.push_back(std::move(detail));
}
//sort
std::sort(details.begin(), details.end(), [](const codec_details& a, const codec_details& b) {return a.codec_name < b.codec_name;});
//merge
for (size_t i = 0 ; i < details.size() ; ++i)
{ {
if (details[i].codec_name == details[j].codec_name) for (size_t j = i + 1 ; j < details.size() ; ++j)
{ {
details[i].supports_encoding |= details[j].supports_encoding; if (details[i].codec_name == details[j].codec_name)
details[i].supports_decoding |= details[j].supports_decoding; {
details[j] = {}; details[i].supports_encoding |= details[j].supports_encoding;
details[i].supports_decoding |= details[j].supports_decoding;
details[j] = {};
}
} }
} }
}
details.erase(std::remove_if(details.begin(), details.end(), [](const auto& d) {return d.codec_name.empty();}), details.end()); details.erase(std::remove_if(details.begin(), details.end(), [](const auto& d) {return d.codec_name.empty();}), details.end());
return details;
}();
return details; return ret;
} }
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------
inline std::vector<device_details> list_input_devices() inline const std::vector<device_details>& list_input_device_types()
{ {
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away const static auto ret = []
std::vector<device_details> devices; {
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 AVInputFormatPtr = AVInputFormat*; using AVInputFormatPtr = AVInputFormat*;
#else #else
using AVInputFormatPtr = const AVInputFormat*; using AVInputFormatPtr = const AVInputFormat*;
#endif #endif
AVInputFormatPtr device{nullptr}; 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; 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));
}
AVDeviceInfoList* device_list = nullptr; device = nullptr;
avdevice_list_input_sources(device, nullptr, nullptr, &device_list);
managed.reset(device_list);
if (device_list) while (init && (device = av_input_video_device_next(device)))
{ {
for (int i = 0 ; i < device_list->nb_devices ; ++i) device_details details;
{ details.device_type = device->name;
device_details::instance instance; details.is_video_type = true;
instance.name = std::string(device_list->devices[i]->device_name); devices.push_back(std::move(details));
instance.description = std::string(device_list->devices[i]->device_description);
details.devices.push_back(std::move(instance));
}
} }
devices.push_back(std::move(details)); return devices;
}; }();
while (init && (device = av_input_audio_device_next(device))) return ret;
iter(device); }
device = nullptr; // ---------------------------------------------------------------------------------------------------
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 {};
while (init && (device = av_input_video_device_next(device))) std::vector<device_instance> instances;
iter(device);
details::av_ptr<AVDeviceInfoList> managed;
AVDeviceInfoList* device_list = nullptr;
avdevice_list_input_sources(nullptr, ret->device_type.c_str(), nullptr, &device_list);
managed.reset(device_list);
return devices; if (device_list)
{
for (int i = 0 ; i < device_list->nb_devices ; ++i)
{
device_instance instance;
instance.name = std::string(device_list->devices[i]->device_name);
instance.description = std::string(device_list->devices[i]->device_description);
instances.push_back(std::move(instance));
}
}
return instances;
} }
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------
inline std::vector<device_details> list_output_devices() inline const std::vector<device_details>& list_output_device_types()
{ {
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away const static auto ret = []
std::vector<device_details> devices; {
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
#if LIBAVDEVICE_VERSION_INT < AV_VERSION_INT(59, 0, 100) std::vector<device_details> devices;
using AVOutputFormatPtr = AVOutputFormat*;
#else
using AVOutputFormatPtr = const AVOutputFormat*;
#endif
AVOutputFormatPtr device{nullptr}; #if LIBAVDEVICE_VERSION_INT < AV_VERSION_INT(59, 0, 100)
using AVOutputFormatPtr = AVOutputFormat*;
#else
using AVOutputFormatPtr = const AVOutputFormat*;
#endif
details::av_ptr<AVDeviceInfoList> managed; AVOutputFormatPtr device{nullptr};
const auto iter = [&](AVOutputFormatPtr device) while (init && (device = av_output_audio_device_next(device)))
{ {
device_details details; device_details details;
details.device_type = std::string(device->name); details.device_type = std::string(device->name);
details.is_audio_type = true;
devices.push_back(std::move(details));
}
AVDeviceInfoList* device_list = nullptr; device = nullptr;
avdevice_list_output_sinks(device, nullptr, nullptr, &device_list);
managed.reset(device_list);
if (device_list) while (init && (device = av_output_video_device_next(device)))
{ {
for (int i = 0 ; i < device_list->nb_devices ; ++i) device_details details;
{ details.device_type = std::string(device->name);
device_details::instance instance; details.is_video_type = true;
instance.name = std::string(device_list->devices[i]->device_name); devices.push_back(std::move(details));
instance.description = std::string(device_list->devices[i]->device_description);
details.devices.push_back(std::move(instance));
}
} }
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 {};
while (init && (device = av_output_audio_device_next(device))) std::vector<device_instance> instances;
iter(device);
device = nullptr; details::av_ptr<AVDeviceInfoList> managed;
AVDeviceInfoList* device_list = nullptr;
avdevice_list_output_sinks(nullptr, ret->device_type.c_str(), nullptr, &device_list);
managed.reset(device_list);
while (init && (device = av_output_video_device_next(device))) if (device_list)
iter(device); {
for (int i = 0 ; i < device_list->nb_devices ; ++i)
{
device_instance instance;
instance.name = std::string(device_list->devices[i]->device_name);
instance.description = std::string(device_list->devices[i]->device_description);
instances.push_back(std::move(instance));
}
}
return devices; return instances;
} }
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------

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

Loading…
Cancel
Save