// Copyright 2018 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include #include #include #include #include "citra_qt/camera/qt_multimedia_camera.h" #include "citra_qt/main.h" namespace Camera { QList QtCameraSurface::supportedPixelFormats( QAbstractVideoBuffer::HandleType handleType) const { Q_UNUSED(handleType); return QList() << QVideoFrame::Format_ARGB32 << QVideoFrame::Format_ARGB32_Premultiplied << QVideoFrame::Format_RGB32 << QVideoFrame::Format_RGB24 << QVideoFrame::Format_RGB565 << QVideoFrame::Format_RGB555 << QVideoFrame::Format_ARGB8565_Premultiplied << QVideoFrame::Format_BGRA32 << QVideoFrame::Format_BGRA32_Premultiplied << QVideoFrame::Format_BGR32 << QVideoFrame::Format_BGR24 << QVideoFrame::Format_BGR565 << QVideoFrame::Format_BGR555 << QVideoFrame::Format_BGRA5658_Premultiplied << QVideoFrame::Format_AYUV444 << QVideoFrame::Format_AYUV444_Premultiplied << QVideoFrame::Format_YUV444 << QVideoFrame::Format_YUV420P << QVideoFrame::Format_YV12 << QVideoFrame::Format_UYVY << QVideoFrame::Format_YUYV << QVideoFrame::Format_NV12 << QVideoFrame::Format_NV21 << QVideoFrame::Format_IMC1 << QVideoFrame::Format_IMC2 << QVideoFrame::Format_IMC3 << QVideoFrame::Format_IMC4 << QVideoFrame::Format_Y8 << QVideoFrame::Format_Y16 << QVideoFrame::Format_Jpeg << QVideoFrame::Format_CameraRaw << QVideoFrame::Format_AdobeDng; // Supporting all the formats } bool QtCameraSurface::present(const QVideoFrame& frame) { if (!frame.isValid()) { return false; } QVideoFrame cloneFrame(frame); cloneFrame.map(QAbstractVideoBuffer::ReadOnly); const QImage image(cloneFrame.bits(), cloneFrame.width(), cloneFrame.height(), QVideoFrame::imageFormatFromPixelFormat(cloneFrame.pixelFormat())); QMutexLocker locker(&mutex); current_frame = image.mirrored(true, true); locker.unlock(); cloneFrame.unmap(); return true; } QtMultimediaCamera::QtMultimediaCamera(const std::string& camera_name, const Service::CAM::Flip& flip) : handler(QtMultimediaCameraHandler::GetHandler()) { if (handler->thread() == QThread::currentThread()) { handler->CreateCamera(camera_name); } else { QMetaObject::invokeMethod(handler.get(), "CreateCamera", Qt::BlockingQueuedConnection, Q_ARG(const std::string&, camera_name)); } using namespace Service::CAM; flip_horizontal = basic_flip_horizontal = (flip == Flip::Horizontal) || (flip == Flip::Reverse); flip_vertical = basic_flip_vertical = (flip == Flip::Vertical) || (flip == Flip::Reverse); } QtMultimediaCamera::~QtMultimediaCamera() { handler->StopCamera(); QtMultimediaCameraHandler::ReleaseHandler(handler); } void QtMultimediaCamera::StartCapture() { if (handler->thread() == QThread::currentThread()) { handler->StartCamera(); } else { QMetaObject::invokeMethod(handler.get(), "StartCamera", Qt::BlockingQueuedConnection); } } void QtMultimediaCamera::StopCapture() { handler->StopCamera(); } void QtMultimediaCamera::SetFormat(Service::CAM::OutputFormat output_format) { output_rgb = output_format == Service::CAM::OutputFormat::RGB565; } void QtMultimediaCamera::SetFrameRate(Service::CAM::FrameRate frame_rate) { const std::array FrameRateList = { /* Rate_15 */ QCamera::FrameRateRange(15, 15), /* Rate_15_To_5 */ QCamera::FrameRateRange(5, 15), /* Rate_15_To_2 */ QCamera::FrameRateRange(2, 15), /* Rate_10 */ QCamera::FrameRateRange(10, 10), /* Rate_8_5 */ QCamera::FrameRateRange(8.5, 8.5), /* Rate_5 */ QCamera::FrameRateRange(5, 5), /* Rate_20 */ QCamera::FrameRateRange(20, 20), /* Rate_20_To_5 */ QCamera::FrameRateRange(5, 20), /* Rate_30 */ QCamera::FrameRateRange(30, 30), /* Rate_30_To_5 */ QCamera::FrameRateRange(5, 30), /* Rate_15_To_10 */ QCamera::FrameRateRange(10, 15), /* Rate_20_To_10 */ QCamera::FrameRateRange(10, 20), /* Rate_30_To_10 */ QCamera::FrameRateRange(10, 30), }; auto framerate = FrameRateList[static_cast(frame_rate)]; handler->settings.setMinimumFrameRate(framerate.minimumFrameRate); handler->settings.setMinimumFrameRate(framerate.maximumFrameRate); } void QtMultimediaCamera::SetResolution(const Service::CAM::Resolution& resolution) { width = resolution.width; height = resolution.height; } void QtMultimediaCamera::SetFlip(Service::CAM::Flip flip) { using namespace Service::CAM; flip_horizontal = basic_flip_horizontal ^ (flip == Flip::Horizontal || flip == Flip::Reverse); flip_vertical = basic_flip_vertical ^ (flip == Flip::Vertical || flip == Flip::Reverse); } void QtMultimediaCamera::SetEffect(Service::CAM::Effect effect) { if (effect != Service::CAM::Effect::None) { NGLOG_ERROR(Service_CAM, "Unimplemented effect {}", static_cast(effect)); } } std::vector QtMultimediaCamera::ReceiveFrame() { QMutexLocker locker(&handler->camera_surface.mutex); return CameraUtil::ProcessImage(handler->camera_surface.current_frame, width, height, output_rgb, flip_horizontal, flip_vertical); } bool QtMultimediaCamera::IsPreviewAvailable() { return handler->CameraAvailable(); } std::unique_ptr QtMultimediaCameraFactory::Create( const std::string& config, const Service::CAM::Flip& flip) const { return std::make_unique(config, flip); } std::array, 3> QtMultimediaCameraHandler::handlers; std::array QtMultimediaCameraHandler::status; void QtMultimediaCameraHandler::Init() { for (auto& handler : handlers) { handler = std::make_shared(); } } std::shared_ptr QtMultimediaCameraHandler::GetHandler() { for (int i = 0; i < handlers.size(); i++) { if (!status[i]) { NGLOG_INFO(Service_CAM, "Successfully got handler {}", i); status[i] = true; return handlers[i]; } } NGLOG_CRITICAL(Service_CAM, "All handlers taken up"); return nullptr; } void QtMultimediaCameraHandler::ReleaseHandler( const std::shared_ptr& handler) { for (int i = 0; i < handlers.size(); i++) { if (handlers[i] == handler) { NGLOG_INFO(Service_CAM, "Successfully released handler {}", i); status[i] = false; handlers[i]->started = false; break; } } } void QtMultimediaCameraHandler::CreateCamera(const std::string& camera_name) { QList cameras = QCameraInfo::availableCameras(); for (const QCameraInfo& cameraInfo : cameras) { if (cameraInfo.deviceName().toStdString() == camera_name) camera = std::make_unique(cameraInfo); } if (!camera) { // no cameras found, using default camera camera = std::make_unique(); } settings.setMinimumFrameRate(30); settings.setMaximumFrameRate(30); camera->setViewfinder(&camera_surface); } void QtMultimediaCameraHandler::StopCamera() { camera->stop(); started = false; } void QtMultimediaCameraHandler::StartCamera() { camera->setViewfinderSettings(settings); camera->start(); started = true; } bool QtMultimediaCameraHandler::CameraAvailable() const { return camera && camera->isAvailable(); } void QtMultimediaCameraHandler::StopCameras() { NGLOG_INFO(Service_CAM, "Stopping all cameras"); for (auto& handler : handlers) { if (handler && handler->started) { handler->StopCamera(); } } } void QtMultimediaCameraHandler::ResumeCameras() { for (auto& handler : handlers) { if (handler && handler->started) { handler->StartCamera(); } } } void QtMultimediaCameraHandler::ReleaseHandlers() { StopCameras(); NGLOG_INFO(Service_CAM, "Releasing all handlers"); for (int i = 0; i < handlers.size(); i++) { status[i] = false; handlers[i]->started = false; } } } // namespace Camera