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
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::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
- 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 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
- 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)
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 ?

View File

@ -360,22 +360,25 @@ namespace dlib
struct device_details
{
struct instance
{
std::string name;
std::string description;
};
std::string device_type;
std::vector<instance> devices;
bool is_audio_type{false};
bool is_video_type{false};
};
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();
struct device_instance
{
std::string name;
std::string description;
};
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
std::vector<std::string> protocols;
void* opaque = nullptr;
const char* name = 0;
while (init && (name = avio_enum_protocols(&opaque, 0)))
protocols.emplace_back(name);
const static auto protocols = []
{
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
std::vector<std::string> protocols;
void* opaque = nullptr;
const char* name = 0;
while (init && (name = avio_enum_protocols(&opaque, 0)))
protocols.emplace_back(name);
opaque = nullptr;
name = 0;
opaque = nullptr;
name = 0;
while (init && (name = avio_enum_protocols(&opaque, 1)))
protocols.emplace_back(name);
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 bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
std::vector<std::string> demuxers;
const AVInputFormat* demuxer = nullptr;
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)
// See https://github.com/FFmpeg/FFmpeg/blob/70d25268c21cbee5f08304da95be1f647c630c15/doc/APIchanges#L86
while (init && (demuxer = av_iformat_next(demuxer)))
#else
void* opaque = nullptr;
while (init && (demuxer = av_demuxer_iterate(&opaque)))
#endif
demuxers.push_back(demuxer->name);
#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
void* opaque = nullptr;
while (init && (demuxer = av_demuxer_iterate(&opaque)))
#endif
demuxers.push_back(demuxer->name);
return demuxers;
}();
return demuxers;
}
@ -1226,13 +1239,12 @@ namespace dlib
// ---------------------------------------------------------------------------------------------------
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,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 auto codecs = list_codecs();
std::vector<muxer_details> all_details;
const AVOutputFormat* muxer = nullptr;
#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
void* opaque = nullptr;
while (init && (muxer = av_muxer_iterate(&opaque)))
#endif
const static auto ret = []
{
muxer_details details;
details.name = muxer->name;
details.supported_codecs = list_codecs_for_muxer(muxer, codecs);
all_details.push_back(details);
}
return all_details;
}
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
// ---------------------------------------------------------------------------------------------------
std::vector<muxer_details> all_details;
const AVOutputFormat* muxer = nullptr;
inline std::vector<codec_details> list_codecs()
{
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
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;
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)
{
for (size_t j = i + 1 ; j < details.size() ; ++j)
#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
void* opaque = nullptr;
while (init && (muxer = av_muxer_iterate(&opaque)))
#endif
{
if (details[i].codec_name == details[j].codec_name)
{
details[i].supports_encoding |= details[j].supports_encoding;
details[i].supports_decoding |= details[j].supports_decoding;
details[j] = {};
}
}
}
muxer_details details;
details.name = muxer->name;
details.supported_codecs = list_codecs_for_muxer(muxer);
all_details.push_back(details);
}
details.erase(std::remove_if(details.begin(), details.end(), [](const auto& d) {return d.codec_name.empty();}), details.end());
return all_details;
}();
return details;
return ret;
}
// ---------------------------------------------------------------------------------------------------
inline std::vector<device_details> list_input_devices()
inline const std::vector<codec_details>& list_codecs()
{
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)
using AVInputFormatPtr = AVInputFormat*;
#else
using AVInputFormatPtr = const AVInputFormat*;
#endif
AVInputFormatPtr device{nullptr};
details::av_ptr<AVDeviceInfoList> managed;
const auto iter = [&](AVInputFormatPtr device)
const static auto ret = []
{
device_details details;
details.device_type = std::string(device->name);
const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away
std::vector<codec_details> details;
AVDeviceInfoList* device_list = nullptr;
avdevice_list_input_sources(device, nullptr, nullptr, &device_list);
managed.reset(device_list);
if (device_list)
#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
{
for (int i = 0 ; i < device_list->nb_devices ; ++i)
{
device_details::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));
}
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));
}
devices.push_back(std::move(details));
};
//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)
{
for (size_t j = i + 1 ; j < details.size() ; ++j)
{
if (details[i].codec_name == details[j].codec_name)
{
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());
while (init && (device = av_input_audio_device_next(device)))
iter(device);
return details;
}();
device = nullptr;
while (init && (device = av_input_video_device_next(device)))
iter(device);
return devices;
return ret;
}
// ---------------------------------------------------------------------------------------------------
inline std::vector<device_details> list_output_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
std::vector<device_details> devices;
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)
using AVOutputFormatPtr = AVOutputFormat*;
using AVInputFormatPtr = AVInputFormat*;
#else
using AVOutputFormatPtr = const AVOutputFormat*;
using AVInputFormatPtr = const AVInputFormat*;
#endif
AVOutputFormatPtr device{nullptr};
AVInputFormatPtr device{nullptr};
details::av_ptr<AVDeviceInfoList> managed;
const auto iter = [&](AVOutputFormatPtr device)
{
device_details details;
details.device_type = std::string(device->name);
AVDeviceInfoList* device_list = nullptr;
avdevice_list_output_sinks(device, nullptr, nullptr, &device_list);
managed.reset(device_list);
if (device_list)
while (init && (device = av_input_audio_device_next(device)))
{
for (int i = 0 ; i < device_list->nb_devices ; ++i)
{
device_details::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));
}
device_details details;
details.device_type = device->name;
details.is_audio_type = true;
devices.push_back(std::move(details));
}
devices.push_back(std::move(details));
};
device = nullptr;
while (init && (device = av_output_audio_device_next(device)))
iter(device);
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));
}
device = nullptr;
return devices;
}();
while (init && (device = av_output_video_device_next(device)))
iter(device);
return ret;
}
return devices;
// ---------------------------------------------------------------------------------------------------
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(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_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 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)
using AVOutputFormatPtr = AVOutputFormat*;
#else
using AVOutputFormatPtr = const AVOutputFormat*;
#endif
AVOutputFormatPtr device{nullptr};
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));
}
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(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_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;
}
// ---------------------------------------------------------------------------------------------------

View File

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