diff --git a/dlib/media/ffmpeg_abstract.h b/dlib/media/ffmpeg_abstract.h index c8fa948da..de7873f64 100644 --- a/dlib/media/ffmpeg_abstract.h +++ b/dlib/media/ffmpeg_abstract.h @@ -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 devices; + bool is_audio_type{false}; + bool is_video_type{false}; }; - std::vector 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& list_protocols(); /*! ensures - - returns a list of all registered ffmpeg protocols + - returns a list of all available ffmpeg protocols !*/ - std::vector list_demuxers(); + const std::vector& list_demuxers(); /*! ensures - - returns a list of all registered ffmpeg demuxers + - returns a list of all available ffmpeg demuxers !*/ - std::vector list_muxers(); + const std::vector& list_muxers(); /*! ensures - - returns a list of all registered ffmpeg muxers + - returns a list of all available ffmpeg muxers !*/ - std::vector list_codecs(); + const std::vector& 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 list_input_devices(); + const std::vector& list_input_device_types(); + /*! + ensures + - returns a list of all available ffmpeg input device types (e.g. alsa, v4l2, etc) + !*/ + + const std::vector& list_output_device_types(); + /*! + ensures + - returns a list of all available ffmpeg output device types (e.g. alsa, v4l2, etc) + !*/ + + std::vector 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 list_output_devices(); + std::vector 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) !*/ // --------------------------------------------------------------------------------------------------- diff --git a/dlib/media/ffmpeg_muxer.h b/dlib/media/ffmpeg_muxer.h index b51f90b84..493f054fa 100644 --- a/dlib/media/ffmpeg_muxer.h +++ b/dlib/media/ffmpeg_muxer.h @@ -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 ? diff --git a/dlib/media/ffmpeg_utils.h b/dlib/media/ffmpeg_utils.h index 6efae05b1..2d8f543a1 100644 --- a/dlib/media/ffmpeg_utils.h +++ b/dlib/media/ffmpeg_utils.h @@ -360,22 +360,25 @@ namespace dlib struct device_details { - struct instance - { - std::string name; - std::string description; - }; - std::string device_type; - std::vector devices; + bool is_audio_type{false}; + bool is_video_type{false}; }; - std::vector list_protocols(); - std::vector list_demuxers(); - std::vector list_muxers(); - std::vector list_codecs(); - std::vector list_input_devices(); - std::vector list_output_devices(); + struct device_instance + { + std::string name; + std::string description; + }; + + const std::vector& list_protocols(); + const std::vector& list_demuxers(); + const std::vector& list_muxers(); + const std::vector& list_codecs(); + const std::vector& list_input_device_types(); + const std::vector& list_output_device_types(); + std::vector list_input_device_instances(const std::string& device_type); + std::vector list_output_device_instances(const std::string& device_type); // --------------------------------------------------------------------------------------------------- @@ -1185,40 +1188,50 @@ namespace dlib // --------------------------------------------------------------------------------------------------- - inline std::vector list_protocols() + inline const std::vector& list_protocols() { - const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away - std::vector 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 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 list_demuxers() + inline const std::vector& list_demuxers() { - const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away - std::vector 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 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 list_codecs_for_muxer ( - const AVOutputFormat* oformat, - const std::vector& all_codecs = list_codecs() + const AVOutputFormat* oformat ) { std::vector 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 list_muxers() + inline const std::vector& 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 all_details; + const AVOutputFormat* muxer = nullptr; - std::vector 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 + { + muxer_details details; + details.name = muxer->name; + details.supported_codecs = list_codecs_for_muxer(muxer); + all_details.push_back(details); + } + + return all_details; + }(); - #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 - { - 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 list_codecs() + inline const std::vector& list_codecs() { - const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away - std::vector 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 + const static auto ret = [] { - 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)); - } + const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away + std::vector 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 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 { - if (details[i].codec_name == details[j].codec_name) + 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) { - details[i].supports_encoding |= details[j].supports_encoding; - details[i].supports_decoding |= details[j].supports_decoding; - details[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()); + + 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 list_input_devices() + inline const std::vector& list_input_device_types() { - const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away - std::vector devices; + const static auto ret = [] + { + const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away + std::vector devices; #if LIBAVDEVICE_VERSION_INT < AV_VERSION_INT(59, 0, 100) - using AVInputFormatPtr = AVInputFormat*; + using AVInputFormatPtr = AVInputFormat*; #else - using AVInputFormatPtr = const AVInputFormat*; + using AVInputFormatPtr = const AVInputFormat*; #endif - AVInputFormatPtr device{nullptr}; - details::av_ptr managed; + AVInputFormatPtr device{nullptr}; - const auto iter = [&](AVInputFormatPtr device) - { - device_details details; - details.device_type = std::string(device->name); + while (init && (device = av_input_audio_device_next(device))) + { + device_details details; + details.device_type = device->name; + details.is_audio_type = true; + devices.push_back(std::move(details)); + } - AVDeviceInfoList* device_list = nullptr; - avdevice_list_input_sources(device, nullptr, nullptr, &device_list); - managed.reset(device_list); + device = nullptr; - if (device_list) + while (init && (device = av_input_video_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_video_type = true; + devices.push_back(std::move(details)); } - devices.push_back(std::move(details)); - }; + return devices; + }(); - while (init && (device = av_input_audio_device_next(device))) - iter(device); + return ret; + } - device = nullptr; +// --------------------------------------------------------------------------------------------------- + + inline std::vector 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))) - iter(device); + std::vector instances; + + details::av_ptr 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 list_output_devices() + inline const std::vector& list_output_device_types() { - const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away - std::vector devices; - -#if LIBAVDEVICE_VERSION_INT < AV_VERSION_INT(59, 0, 100) - using AVOutputFormatPtr = AVOutputFormat*; -#else - using AVOutputFormatPtr = const AVOutputFormat*; -#endif + const static auto ret = [] + { + const bool init = details::register_ffmpeg::get(); // Don't let this get optimized away + std::vector devices; - 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 managed; + AVOutputFormatPtr device{nullptr}; - const auto iter = [&](AVOutputFormatPtr device) - { - device_details details; - details.device_type = std::string(device->name); + 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)); + } - AVDeviceInfoList* device_list = nullptr; - avdevice_list_output_sinks(device, nullptr, nullptr, &device_list); - managed.reset(device_list); + device = nullptr; - if (device_list) + while (init && (device = av_output_video_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 = std::string(device->name); + details.is_video_type = true; + devices.push_back(std::move(details)); } - devices.push_back(std::move(details)); - }; + return devices; + }(); + + return ret; + } + +// --------------------------------------------------------------------------------------------------- + + std::vector 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))) - iter(device); + std::vector instances; - device = nullptr; + details::av_ptr 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))) - iter(device); + 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 devices; + return instances; } // --------------------------------------------------------------------------------------------------- diff --git a/examples/ffmpeg_info_ex.cpp b/examples/ffmpeg_info_ex.cpp index 3ffde8292..ca8963381 100644 --- a/examples/ffmpeg_info_ex.cpp +++ b/examples/ffmpeg_info_ex.cpp @@ -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'; } }