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>
This commit is contained in:
pfeatherstone 2023-04-15 14:28:38 +01:00 committed by GitHub
parent b1fe026e06
commit 5f7026ab52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 285 additions and 204 deletions

View File

@ -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 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 name;
std::string description; std::string description;
}; };
std::string device_type; const std::vector<std::string>& list_protocols();
std::vector<instance> devices;
};
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 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 types (e.g. alsa, v4l2, etc)
!*/ !*/
std::vector<device_details> list_output_devices(); const std::vector<device_details>& list_output_device_types();
/*! /*!
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 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 available ffmpeg input device instances for device type *device_type (e.g. hw:0,0, /dev/video0, etc)
!*/
std::vector<device_instance> list_output_device_instances(const std::string& device_type);
/*!
ensures
- returns a list of all available ffmpeg output device instances for device type *device_type (e.g. hw:0,0, /dev/video0, etc)
!*/ !*/
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------

View File

@ -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 ?

View File

@ -360,22 +360,25 @@ namespace dlib
struct device_details 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 name;
std::string description; std::string description;
}; };
std::string device_type; const std::vector<std::string>& list_protocols();
std::vector<instance> devices; const std::vector<std::string>& list_demuxers();
}; const std::vector<muxer_details>& list_muxers();
const std::vector<codec_details>& list_codecs();
std::vector<std::string> list_protocols(); const std::vector<device_details>& list_input_device_types();
std::vector<std::string> list_demuxers(); const std::vector<device_details>& list_output_device_types();
std::vector<muxer_details> list_muxers(); std::vector<device_instance> list_input_device_instances(const std::string& device_type);
std::vector<codec_details> list_codecs(); std::vector<device_instance> list_output_device_instances(const std::string& device_type);
std::vector<device_details> list_input_devices();
std::vector<device_details> list_output_devices();
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------
@ -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 const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
std::vector<std::string> protocols; std::vector<std::string> protocols;
@ -1200,12 +1205,17 @@ namespace dlib
while (init && (name = avio_enum_protocols(&opaque, 1))) while (init && (name = avio_enum_protocols(&opaque, 1)))
protocols.emplace_back(name); protocols.emplace_back(name);
return protocols;
}();
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 const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
std::vector<std::string> demuxers; std::vector<std::string> demuxers;
@ -1220,19 +1230,21 @@ namespace dlib
#endif #endif
demuxers.push_back(demuxer->name); demuxers.push_back(demuxer->name);
return demuxers;
}();
return demuxers; return demuxers;
} }
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------
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,12 +1253,12 @@ 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 bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
const auto codecs = list_codecs();
std::vector<muxer_details> all_details; std::vector<muxer_details> all_details;
const AVOutputFormat* muxer = nullptr; const AVOutputFormat* muxer = nullptr;
@ -1260,16 +1272,21 @@ namespace dlib
{ {
muxer_details details; muxer_details details;
details.name = muxer->name; 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); all_details.push_back(details);
} }
return all_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 const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
std::vector<codec_details> details; 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()); 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 static auto ret = []
{ {
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
std::vector<device_details> devices; std::vector<device_details> devices;
@ -1327,45 +1349,66 @@ namespace dlib
#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));
}
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; 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); managed.reset(device_list);
if (device_list) if (device_list)
{ {
for (int i = 0 ; i < device_list->nb_devices ; ++i) 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.name = std::string(device_list->devices[i]->device_name);
instance.description = std::string(device_list->devices[i]->device_description); 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)); return instances;
};
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;
} }
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------
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 const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
std::vector<device_details> devices; std::vector<device_details> devices;
@ -1378,40 +1421,58 @@ namespace dlib
AVOutputFormatPtr device{nullptr}; AVOutputFormatPtr device{nullptr};
details::av_ptr<AVDeviceInfoList> managed; while (init && (device = av_output_audio_device_next(device)))
const auto iter = [&](AVOutputFormatPtr 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));
}
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; 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); managed.reset(device_list);
if (device_list) if (device_list)
{ {
for (int i = 0 ; i < device_list->nb_devices ; ++i) 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.name = std::string(device_list->devices[i]->device_name);
instance.description = std::string(device_list->devices[i]->device_description); 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)); return instances;
};
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;
} }
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------

View File

@ -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';
} }
} }