diff --git a/CHANGES.md b/CHANGES.md index 18ddd2fd..9103edec 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,8 @@ ## develop +- [CHANGE] `--list-devices` の戻り値を JSON に変更する + - @voluntas - [CHANGE] `--video-device` オプションを `--video-input-device` に変更する - @voluntas - [CHANGE] `--no-video-device` オプションを `--no-video-input-device` に変更する diff --git a/src/device_info.h b/src/device_info.h index 7635be5b..227c49f2 100644 --- a/src/device_info.h +++ b/src/device_info.h @@ -15,9 +15,16 @@ struct AudioDeviceInfo { bool is_input; }; +struct VideoDeviceFormat { + int width; + int height; + std::vector framerates; // FPS のリスト +}; + struct VideoDeviceInfo { int index; std::string name; + std::vector formats; }; std::vector GetVideoDeviceInfos(); diff --git a/src/mac_helper/mac_capturer.mm b/src/mac_helper/mac_capturer.mm index aa6c74dc..73a9938e 100644 --- a/src/mac_helper/mac_capturer.mm +++ b/src/mac_helper/mac_capturer.mm @@ -10,6 +10,9 @@ #include "mac_capturer.h" +#include +#include + // WebRTC #include @@ -126,6 +129,36 @@ - (void)capturer:(RTCVideoCapturer*)capturer VideoDeviceInfo info; info.index = static_cast(i); info.name = [device.localizedName UTF8String]; + + // サポートされているフォーマットを取得 + NSArray* formats = + [RTCCameraVideoCapturer supportedFormatsForDevice:device]; + + // 解像度ごとにグループ化して、フレームレートをまとめる + std::map, std::set> resolution_fps_map; + + for (AVCaptureDeviceFormat* format in formats) { + CMVideoDimensions dimension = + CMVideoFormatDescriptionGetDimensions(format.formatDescription); + int width = dimension.width; + int height = dimension.height; + + // フレームレート範囲を取得 + for (AVFrameRateRange* range in format.videoSupportedFrameRateRanges) { + int max_fps = static_cast(range.maxFrameRate); + resolution_fps_map[{width, height}].insert(max_fps); + } + } + + // VideoDeviceFormat に変換 + for (const auto& [resolution, fps_set] : resolution_fps_map) { + VideoDeviceFormat format; + format.width = resolution.first; + format.height = resolution.second; + format.framerates.assign(fps_set.begin(), fps_set.end()); + info.formats.push_back(format); + } + infos_ptr->push_back(info); }]; return infos; diff --git a/src/main.cpp b/src/main.cpp index 8c93a351..7bb28120 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,9 @@ #include #include +// Boost +#include + // SDL3 #include @@ -73,8 +76,10 @@ const size_t kDefaultMaxLogFileSize = 10 * 1024 * 1024; #if defined(__APPLE__) || defined(__linux__) static void ListDevices() { - // オーディオデバイス一覧 - auto print_audio_devices = [&](bool is_input) { + boost::json::object root; + + // オーディオデバイス一覧を JSON 化 + auto audio_device_to_json = [&](bool is_input) -> boost::json::array { auto infos = GetAudioDeviceInfos( #if defined(__linux__) webrtc::AudioDeviceModule::kLinuxPulseAudio, @@ -83,49 +88,101 @@ static void ListDevices() { #endif is_input); - const char* title = is_input ? "=== Available audio input devices ===" - : "=== Available audio output devices ==="; - std::cout << title << std::endl; - std::cout << std::endl; - - if (infos.empty()) { - std::cout << " (none)" << std::endl << std::endl; - return; - } - + boost::json::array devices; for (const auto& info : infos) { - std::cout << " [" << info.index << "] " << info.name; + boost::json::object device; + device["index"] = info.index; + device["name"] = info.name; if (!info.guid.empty()) { - std::cout << " (" << info.guid << ")"; + device["guid"] = info.guid; } - std::cout << std::endl; + devices.push_back(device); } - std::cout << std::endl; + return devices; }; - print_audio_devices(true); - print_audio_devices(false); + root["audio_input_devices"] = audio_device_to_json(true); + root["audio_output_devices"] = audio_device_to_json(false); - // ビデオデバイス一覧 + // ビデオデバイス一覧を JSON 化 #if defined(__linux__) auto devices = sora::EnumV4L2CaptureDevices(); if (!devices) { std::cerr << "Failed to enumerate video devices" << std::endl; return; } -#endif - std::cout << "=== Available video devices ===" << std::endl; - std::cout << std::endl; -#if defined(__linux__) - std::cout << sora::FormatV4L2Devices(*devices); + boost::json::array video_devices; + for (const auto& dev : *devices) { + boost::json::object device; + device["index"] = dev.index; + device["path"] = dev.path; + device["card"] = dev.card; + device["bus_info"] = dev.bus_info; + + boost::json::array formats; + for (const auto& fmt : dev.format_descriptions) { + boost::json::object format; + format["index"] = fmt.index; + format["pixel_format"] = fmt.pixel_format; + format["description"] = fmt.description; + + boost::json::array frame_sizes; + for (const auto& fs : fmt.frame_sizes) { + boost::json::object frame_size; + frame_size["index"] = fs.index; + frame_size["width"] = fs.width; + frame_size["height"] = fs.height; + + boost::json::array intervals; + for (const auto& interval : fs.intervals) { + boost::json::object interval_obj; + interval_obj["index"] = interval.index; + interval_obj["numerator"] = interval.numerator; + interval_obj["denominator"] = interval.denominator; + intervals.push_back(interval_obj); + } + frame_size["intervals"] = intervals; + frame_sizes.push_back(frame_size); + } + format["frame_sizes"] = frame_sizes; + formats.push_back(format); + } + device["formats"] = formats; + video_devices.push_back(device); + } + root["video_input_devices"] = video_devices; #else auto video_device_infos = MacCapturer::GetVideoDeviceInfos(); + boost::json::array video_devices; for (const auto& info : video_device_infos) { - std::cout << " [" << info.index << "] " << info.name << std::endl; + boost::json::object device; + device["index"] = info.index; + device["name"] = info.name; + + // フォーマット情報を追加 + boost::json::array formats; + for (const auto& fmt : info.formats) { + boost::json::object format; + format["width"] = fmt.width; + format["height"] = fmt.height; + + boost::json::array framerates; + for (int fps : fmt.framerates) { + framerates.push_back(fps); + } + format["framerates"] = framerates; + formats.push_back(format); + } + device["formats"] = formats; + + video_devices.push_back(device); } - std::cout << std::endl; + root["video_input_devices"] = video_devices; #endif + + // JSON を出力 + std::cout << boost::json::serialize(root) << std::endl; } #endif