#include "stdafx.h" #include "qt_camera_handler.h" #include "Emu/System.h" #include #include LOG_CHANNEL(camera_log, "Camera"); qt_camera_handler::qt_camera_handler() : camera_handler_base() { } qt_camera_handler::~qt_camera_handler() { close_camera(); } void qt_camera_handler::set_camera(const QCameraInfo& cameraInfo) { if (cameraInfo.isNull()) { camera_log.error("No camera present"); return; } // Determine if the camera is front facing, in which case we will need to flip the image horizontally. const bool front_facing = cameraInfo.position() == QCamera::Position::FrontFace; camera_log.success("Using camera: name=\"%s\", description=\"%s\", front_facing=%d", cameraInfo.deviceName().toStdString(), cameraInfo.description().toStdString(), front_facing); // Create camera and video surface m_surface.reset(new qt_camera_video_surface(front_facing, nullptr)); m_camera.reset(new QCamera(cameraInfo)); m_error_handler.reset(new qt_camera_error_handler(m_camera, [this](QCamera::Status status) { switch (status) { case QCamera::UnavailableStatus: m_state = camera_handler_state::not_available; break; case QCamera::UnloadedStatus: case QCamera::UnloadingStatus: m_state = camera_handler_state::closed; break; case QCamera::StandbyStatus: case QCamera::StoppingStatus: case QCamera::LoadedStatus: case QCamera::LoadingStatus: m_state = camera_handler_state::open; break; case QCamera::StartingStatus: case QCamera::ActiveStatus: m_state = camera_handler_state::running; break; default: camera_log.error("Ignoring unknown status %d", static_cast(status)); break; } })); // Set view finder and update the settings m_camera->setViewfinder(m_surface.get()); update_camera_settings(); } void qt_camera_handler::open_camera() { // List available cameras for (const QCameraInfo& cameraInfo : QCameraInfo::availableCameras()) { camera_log.success("Found camera: name=%s, description=%s", cameraInfo.deviceName().toStdString(), cameraInfo.description().toStdString()); } // Let's use the default camera for now set_camera(QCameraInfo::defaultCamera()); camera_log.notice("Loading camera"); if (!m_camera) { camera_log.error("No camera found"); m_state = camera_handler_state::not_available; return; } if (m_camera->state() != QCamera::State::UnloadedState) { camera_log.notice("Camera already loaded"); return; } // Load/open camera m_camera->load(); // List all supported formats for debugging for (const QCamera::FrameRateRange& frame_rate : m_camera->supportedViewfinderFrameRateRanges()) { camera_log.notice("Supported frame rate range: %f-%f", frame_rate.minimumFrameRate, frame_rate.maximumFrameRate); } for (const QVideoFrame::PixelFormat& pixel_format : m_camera->supportedViewfinderPixelFormats()) { camera_log.notice("Supported pixel format: %d", static_cast(pixel_format)); } for (const QSize& resolution : m_camera->supportedViewfinderResolutions()) { camera_log.notice("Supported resolution: %dx%d", resolution.width(), resolution.height()); } // Update camera and view finder settings update_camera_settings(); } void qt_camera_handler::close_camera() { camera_log.notice("Unloading camera"); if (!m_camera) { camera_log.error("No camera found"); m_state = camera_handler_state::not_available; return; } if (m_camera->state() == QCamera::State::UnloadedState) { camera_log.notice("Camera already unloaded"); return; } // Unload/close camera m_camera->unload(); } void qt_camera_handler::start_camera() { camera_log.notice("Starting camera"); if (!m_camera) { camera_log.error("No camera found"); m_state = camera_handler_state::not_available; return; } if (m_camera->state() == QCamera::State::ActiveState) { camera_log.notice("Camera already started"); return; } if (m_camera->state() == QCamera::State::UnloadedState) { camera_log.notice("Camera not open"); open_camera(); } // Start camera. We will start receiving frames now. m_camera->start(); } void qt_camera_handler::stop_camera() { camera_log.notice("Stopping camera"); if (!m_camera) { camera_log.error("No camera found"); m_state = camera_handler_state::not_available; return; } if (m_camera->state() == QCamera::State::LoadedState) { camera_log.notice("Camera already stopped"); return; } // Stop camera. The camera will still be drawing power. m_camera->stop(); } void qt_camera_handler::set_format(s32 format, u32 bytesize) { m_format = format; m_bytesize = bytesize; update_camera_settings(); } void qt_camera_handler::set_frame_rate(u32 frame_rate) { m_frame_rate = frame_rate; update_camera_settings(); } void qt_camera_handler::set_resolution(u32 width, u32 height) { m_width = width; m_height = height; update_camera_settings(); } void qt_camera_handler::set_mirrored(bool mirrored) { m_mirrored = mirrored; update_camera_settings(); } u64 qt_camera_handler::frame_number() const { return m_surface ? m_surface->frame_number() : 0; } camera_handler_base::camera_handler_state qt_camera_handler::get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read) { width = 0; height = 0; frame_number = 0; bytes_read = 0; if (!m_camera || !m_surface) { camera_log.fatal("Error: camera invalid"); m_state = camera_handler_state::not_available; return camera_handler_state::not_available; } // Backup current state. State may change through events. const camera_handler_state current_state = m_state; if (current_state == camera_handler_state::running) { // Copy latest image into out buffer. m_surface->get_image(buf, size, width, height, frame_number, bytes_read); } else { camera_log.error("Camera not running (m_state=%d)", static_cast(current_state)); } return current_state; } void qt_camera_handler::update_camera_settings() { // Update camera if possible. We can only do this if it is already loaded. if (m_camera && m_camera->state() != QCamera::State::UnloadedState) { // List all available settings in a cascading fashion and choose the proper value if possible. // After each step, the next one will only list the settings that are compatible with the prior ones. QCameraViewfinderSettings settings; // Set resolution if possible. for (const QSize& resolution : m_camera->supportedViewfinderResolutions(settings)) { if (m_width == resolution.width() && m_height == resolution.height()) { settings.setResolution(resolution.width(), resolution.height()); break; } } // Set frame rate if possible. for (const QCamera::FrameRateRange& frame_rate : m_camera->supportedViewfinderFrameRateRanges(settings)) { // Some cameras do not have an exact match, so let's approximate. if (static_cast(m_frame_rate) >= (frame_rate.maximumFrameRate - 0.5) && static_cast(m_frame_rate) <= (frame_rate.maximumFrameRate + 0.5)) { // Lock the frame rate by setting the min and max to the same value. settings.setMinimumFrameRate(m_frame_rate); settings.setMaximumFrameRate(m_frame_rate); break; } } // Set pixel format if possible. (Unused for now, because formats differ between Qt and cell) //for (const QVideoFrame::PixelFormat& pixel_format : m_camera->supportedViewfinderPixelFormats(settings)) //{ // if (pixel_format matches m_format) // { // settings.setPixelFormat(pixel_format); // break; // } //} camera_log.notice("Setting view finder settings: frame_rate=%f, width=%d, height=%d, pixel_format=%d", settings.maximumFrameRate(), settings.resolution().width(), settings.resolution().height(), static_cast(settings.pixelFormat())); // Apply settings. m_camera->setViewfinderSettings(settings); } // Update video surface if possible if (m_surface) { m_surface->set_resolution(m_width, m_height); m_surface->set_format(m_format, m_bytesize); m_surface->set_mirrored(m_mirrored); } }