From 95396a4b6aaf20d7d143532b684ba937797f4250 Mon Sep 17 00:00:00 2001 From: zheng_wenlong Date: Fri, 5 Apr 2019 14:53:38 +0900 Subject: Add demo3 camera source code Add demo3 camera source code. [Patch Set 2] Update LICENSE file. Change-Id: If8b734e31c9ffcaadee288b23ca07cfc1fe1a5a2 BUG-AGL: SPEC-2261 Signed-off-by: zheng_wenlong --- app/CameraControl.qml | 162 ++++ app/app.pri | 20 + app/app.pro | 21 + app/camera.cpp | 130 +++ app/camera.h | 67 ++ app/images/camera/camera_bg.svg | 484 +++++++++++ app/images/camera/camerainfo_bg.svg | 93 ++ app/images/homescreen/homebg_bottom.svg | 1397 +++++++++++++++++++++++++++++++ app/images/images.qrc | 7 + app/main.cpp | 129 +++ app/main.qml | 61 ++ app/main.qrc | 6 + 12 files changed, 2577 insertions(+) create mode 100644 app/CameraControl.qml create mode 100644 app/app.pri create mode 100644 app/app.pro create mode 100644 app/camera.cpp create mode 100644 app/camera.h create mode 100644 app/images/camera/camera_bg.svg create mode 100644 app/images/camera/camerainfo_bg.svg create mode 100644 app/images/homescreen/homebg_bottom.svg create mode 100644 app/images/images.qrc create mode 100644 app/main.cpp create mode 100644 app/main.qml create mode 100644 app/main.qrc (limited to 'app') diff --git a/app/CameraControl.qml b/app/CameraControl.qml new file mode 100644 index 0000000..0259e05 --- /dev/null +++ b/app/CameraControl.qml @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2019 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.0 + +RowLayout { + property var device + property var ratio: 4/3 + property var number: [] + property var fps: ["10", "20", "30", "40", "50", "60"] + property var resolution: ["320*240", "640*480", "1280*720", "1920*1080"] + property var models: [number, fps, resolution] + property real back: 1 + property bool switchstatus: true + property bool switchChecked: false + + Repeater { + id: info + model: ["No:", "FPS:", "W*H:"] + delegate: RowLayout{ + Label { + id: label + text: model.modelData + color: "#59FF7F" + } + ComboBox { + id: camerainfo + implicitWidth: index != 2 ? 100 : 245 + font: label.font + model: models[index] + contentItem: Text { + text: camerainfo.displayText + font: camerainfo.font + color: camerainfo.pressed ? "#17a81a" : "white" + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + indicator: Canvas { + id: canvas + x: camerainfo.width - width - camerainfo.rightPadding + y: camerainfo.topPadding + (camerainfo.availableHeight - height) / 2 + width: 20 + height: 12 + contextType: "2d" + + Connections { + target: camerainfo + onPressedChanged: canvas.requestPaint() + } + + onPaint: { + context.reset(); + context.moveTo(0, 0); + context.lineTo(width, 0); + context.lineTo(width / 2, height); + context.closePath(); + context.fillStyle = camerainfo.pressed ? "#17a81a" : "white"; + context.fill(); + } + } + popup: Popup { + id: popup + y: camerainfo.height - 1 + implicitWidth: camerainfo.width + implicitHeight: listview.count > 6 ? (listview.contentHeight/3.3) : listview.contentHeight + padding: 0 + + contentItem: ListView { + id: listview + clip: true + model: camerainfo.visible ? camerainfo.delegateModel : null + currentIndex: camerainfo.highlightedIndex + ScrollIndicator.vertical: ScrollIndicator { } + } + background: Image { source: "images/camera/camerainfo_bg.svg" } + } + delegate: ItemDelegate { + id: popupdelegate + width: camerainfo.width + contentItem: Item { + implicitHeight: 30 + Text { + text: modelData + color: popupdelegate.pressed || highlighted ? "#21be2b" : "white" + font: camerainfo.font + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + } + highlighted: camerainfo.highlightedIndex == index + } + + background: Image { source: "images/camera/camerainfo_bg.svg" } + onCurrentIndexChanged: + if(cameraswitch.checked && device){ + var dNo = info.itemAt(0).children[1].currentText + var dFps = info.itemAt(1).children[1].currentText + var dRes = info.itemAt(2).children[1].currentText + if (index === 0){ + dNo = number[listview.currentIndex] + } + if (index === 1){ + dFps = fps[listview.currentIndex] + } + if (index === 2){ + dRes = resolution[listview.currentIndex] + if(listview.currentIndex === 0 || listview.currentIndex === 1){ + ratio = 4/3 + } + else{ + ratio = 16/9 + } + } + device.stop() + device.start(dNo, dFps, dRes) + } + } + } + } + Switch { + id: cameraswitch + enabled: switchstatus + checked: switchChecked + onCheckedChanged: { + if (info.itemAt(2).children[1].currentText === "320*240" || info.itemAt(2).children[1].currentText === "640*480"){ + ratio = 4/3 + } + else{ + ratio = 16/9 + } + if (checked && device){ + device.start(info.itemAt(0).children[1].currentText, info.itemAt(1).children[1].currentText, info.itemAt(2).children[1].currentText) + }else if (!checked && device){ + device.stop() + } + } + } + + Component.onCompleted: { + device.enumerateCameras(); + number = device.camranum(); + + if (device.cameraCnt() === 0) + switchstatus = false; + } +} diff --git a/app/app.pri b/app/app.pri new file mode 100644 index 0000000..590c154 --- /dev/null +++ b/app/app.pri @@ -0,0 +1,20 @@ +TEMPLATE = app + +load(configure) + +qtCompileTest(libhomescreen) +qtCompileTest(qlibwindowmanager) + +config_libhomescreen { + CONFIG += link_pkgconfig + PKGCONFIG += libhomescreen + DEFINES += HAVE_LIBHOMESCREEN +} + +config_qlibwindowmanager { + CONFIG += link_pkgconfig + PKGCONFIG += qlibwindowmanager + DEFINES += HAVE_QLIBWINDOWMANAGER +} + +DESTDIR = $${OUT_PWD}/../package/root/bin diff --git a/app/app.pro b/app/app.pro new file mode 100644 index 0000000..8dfa093 --- /dev/null +++ b/app/app.pro @@ -0,0 +1,21 @@ +TARGET = camera +QT = quickcontrols2 + +equals(QT_ARCH, "arm") { + QMAKE_CXXFLAGS += -mfp16-format=ieee +} + +HEADERS += \ + camera.h + +SOURCES += \ + main.cpp \ + camera.cpp + +LIBS += -lopencv_core -lopencv_videoio + +RESOURCES += \ + main.qrc \ + images/images.qrc + +include(app.pri) diff --git a/app/camera.cpp b/app/camera.cpp new file mode 100644 index 0000000..3ae1e4a --- /dev/null +++ b/app/camera.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2019 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "camera.h" +#include +#include + +#include +#include +#include +#include + +using namespace cv; + +Camera::Camera() { + running = false; + timer = new QTimer(this); + capture = new VideoCapture(); + + connect(timer, SIGNAL(timeout()), this, SLOT(grab())); +} + +Camera::~Camera() { + if (timer->isActive()) + timer->stop(); + if (capture && capture->isOpened()) + capture->release(); + delete timer; + delete capture; +} + +void Camera::paint(QPainter *painter) { + painter->drawImage(0, 0, image); +} + +void Camera::enumerateCameras() { + int maxID = 20; + for (int idx = maxID; idx > 2; idx--){ + std::stringstream no; + no << "/dev/video" << idx; + qDebug() << idx; + int fd = open(no.str().c_str(), O_RDWR); + if (fd != -1){ + struct v4l2_capability cap; + + if (ioctl(fd,VIDIOC_QUERYCAP,&cap) != -1){ + if ((cap.capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING)) == (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING)){ + qDebug()<< (char*)(cap.driver) << ">" << (char*)(cap.card) << ">" << (char*)(cap.bus_info);// << (cap.version>>16)&0xFF << (cap.version>>8)&0XFF << cap.version&0xFF; + cam_arr.push_back(idx); // vector of all available cameras + } + } + close(fd); + } + } + + camcnt = cam_arr.length(); +} + +void Camera::start(int no, int fps, QString res) { + int retryCount = 0; + while(!capture->open(no)){ + if(retryCount++==5){ + qDebug()<< "Try to open camera for 5 times failed, give up."; + return; + }else{ + qDebug()<< "open camera failed, retry " << retryCount; + usleep(200000); + } + } + + capture->set(CAP_PROP_FRAME_WIDTH, res.section("*", 0, 0).toInt()); + capture->set(CAP_PROP_FRAME_HEIGHT, res.section("*", 1, 1).toInt()); + + if (fps > 0){ + timer->start(1000/fps); + running = true; + emit isrunningChanged(running); + } +} + +void Camera::stop() { + if (timer->isActive()) + timer->stop(); + if (capture->isOpened()){ + qDebug()<< "release camera."; + capture->release(); + } + image = QImage(); + update(); + running = false; + emit isrunningChanged(running); +} + +QVariantList Camera::camranum() const{ + return cam_arr; +} + +int Camera::cameraCnt() { + return camcnt; +} + +bool Camera::isrunning() const{ + return running; +} + +void Camera::grab() { + if (capture && capture->isOpened()){ + Mat frame; + capture->read(frame); + + if (!frame.empty()){ + image = QImage((const uchar*)(frame.data), frame.cols, frame.rows, frame.step, QImage::Format_RGB888).rgbSwapped(); + image = image.scaled(this->width(), this->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + update(); + } + } +} \ No newline at end of file diff --git a/app/camera.h b/app/camera.h new file mode 100644 index 0000000..004f113 --- /dev/null +++ b/app/camera.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CAMERA_H +#define CAMERA_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +static QVariantList cam_arr_bak; + +class Camera : public QQuickPaintedItem +{ + Q_OBJECT + Q_PROPERTY(bool isrunning READ isrunning NOTIFY isrunningChanged) +public: + Camera(); + ~Camera(); + void paint(QPainter *painter); + bool isrunning() const; + Q_INVOKABLE QVariantList camranum() const; + + Q_INVOKABLE void start(int no, int fps, QString res); + Q_INVOKABLE void stop(); + + Q_INVOKABLE void enumerateCameras(); + Q_INVOKABLE int cameraCnt(); + +signals: + void isrunningChanged(const bool& isrunning); + +public slots: + void grab(); + +private: + bool running; + QImage image; + QTimer* timer; + cv::VideoCapture* capture; + + QVariantList cam_arr; + int camcnt; +}; + +#endif // CAMERA_H diff --git a/app/images/camera/camera_bg.svg b/app/images/camera/camera_bg.svg new file mode 100644 index 0000000..458f81c --- /dev/null +++ b/app/images/camera/camera_bg.svg @@ -0,0 +1,484 @@ + + + + + + + + image/svg+xml + + + + + + + diff --git a/app/images/camera/camerainfo_bg.svg b/app/images/camera/camerainfo_bg.svg new file mode 100644 index 0000000..4251412 --- /dev/null +++ b/app/images/camera/camerainfo_bg.svg @@ -0,0 +1,93 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/homescreen/homebg_bottom.svg b/app/images/homescreen/homebg_bottom.svg new file mode 100644 index 0000000..78bf678 --- /dev/null +++ b/app/images/homescreen/homebg_bottom.svg @@ -0,0 +1,1397 @@ + + + + + + + + image/svg+xml + + + + + + + diff --git a/app/images/images.qrc b/app/images/images.qrc new file mode 100644 index 0000000..24782e3 --- /dev/null +++ b/app/images/images.qrc @@ -0,0 +1,7 @@ + + + homescreen/homebg_bottom.svg + camera/camera_bg.svg + camera/camerainfo_bg.svg + + diff --git a/app/main.cpp b/app/main.cpp new file mode 100644 index 0000000..cb76641 --- /dev/null +++ b/app/main.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2019 TOYOTA MOTOR CORPORATION + * Copyright (C) 2016 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "camera.h" + +#include + +#ifdef HAVE_LIBHOMESCREEN +#include +#endif +#ifdef HAVE_QLIBWINDOWMANAGER +#include +#endif + +int main(int argc, char *argv[]) +{ + QString myname = QString("camera"); + + QGuiApplication app(argc, argv); + app.setApplicationName(myname); + app.setApplicationVersion(QStringLiteral("0.1.0")); + app.setOrganizationDomain(QStringLiteral("automotivelinux.org")); + app.setOrganizationName(QStringLiteral("AutomotiveGradeLinux")); + + QQuickStyle::setStyle("AGL"); + + QCommandLineParser parser; + parser.addPositionalArgument("port", app.translate("main", "port for binding")); + parser.addPositionalArgument("secret", app.translate("main", "secret for binding")); + parser.addHelpOption(); + parser.addVersionOption(); + parser.process(app); + QStringList positionalArguments = parser.positionalArguments(); + + qmlRegisterType("Camera", 1, 0, "Camera"); + + QQmlApplicationEngine engine; + QQmlContext *context = engine.rootContext(); + QUrl bindingAddress; + int port = 0; + QString secret; + if (positionalArguments.length() == 2) { + port = positionalArguments.takeFirst().toInt(); + secret = positionalArguments.takeFirst(); + bindingAddress.setScheme(QStringLiteral("ws")); + bindingAddress.setHost(QStringLiteral("localhost")); + bindingAddress.setPort(port); + bindingAddress.setPath(QStringLiteral("/api")); + QUrlQuery query; + query.addQueryItem(QStringLiteral("token"), secret); + bindingAddress.setQuery(query); + context->setContextProperty(QStringLiteral("bindingAddress"), bindingAddress); + } else { + context->setContextProperty(QStringLiteral("bindingAddress"), bindingAddress); + } + +#ifdef HAVE_QLIBWINDOWMANAGER + // WindowManager + QLibWindowmanager* qwm = new QLibWindowmanager(); + if(qwm->init(port,secret) != 0){ + exit(EXIT_FAILURE); + } + // Request a surface as described in layers.json windowmanager’s file + if (qwm->requestSurface(myname) != 0) { + exit(EXIT_FAILURE); + } + // Create an event callback against an event type. Here a lambda is called when SyncDraw event occurs + qwm->set_event_handler(QLibWindowmanager::Event_SyncDraw, [qwm, myname](json_object *object) { + fprintf(stderr, "Surface got syncDraw!\n"); + qwm->endDraw(myname); + }); +#endif + +#ifdef HAVE_LIBHOMESCREEN + // HomeScreen + LibHomeScreen* hs = new LibHomeScreen(); + std::string token = secret.toStdString(); + hs->init(port, token.c_str()); + // Set the event handler for Event_TapShortcut which will activate the surface for windowmanager + hs->set_event_handler(LibHomeScreen::Event_TapShortcut, [qwm, myname](json_object *object){ + qwm->activateSurface(myname); + }); +#endif + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + QObject *root = engine.rootObjects().first(); + QQuickWindow *window = qobject_cast(root); +#ifdef HAVE_QLIBWINDOWMANAGER + QObject::connect(window, SIGNAL(frameSwapped()), qwm, SLOT(slotActivateSurface())); + + qwm->set_event_handler(QLibWindowmanager::Event_Visible, [qwm, root](json_object *object) { + fprintf(stderr, "Surface got Visible!\n"); + QMetaObject::invokeMethod(root, "changeVisible", Q_ARG(QVariant, true)); + }); + + qwm->set_event_handler(QLibWindowmanager::Event_Invisible, [qwm, root](json_object *object) { + fprintf(stderr, "Surface got Invisible!\n"); + QMetaObject::invokeMethod(root, "changeVisible", Q_ARG(QVariant, false)); + }); +#else + window->resize(1280, 720); + window->setVisible(true); +#endif + + return app.exec(); +} diff --git a/app/main.qml b/app/main.qml new file mode 100644 index 0000000..0a9cb92 --- /dev/null +++ b/app/main.qml @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.0 +import QtQuick.Window 2.2 +import Camera 1.0 +import QtWebSockets 1.0 + +ApplicationWindow { + id: root + width: 1920 + height: 720 + + Camera { + id: camdev_device + anchors.horizontalCenter: parent.horizontalCenter + width: height * cameracontrol.ratio + height: root.height + anchors.top: parent.top + onIsrunningChanged: { + camerabg.visible = !isrunning + } + + Image { + id: camerabg + anchors.centerIn: parent + width: 200 + height: 200 + source: "images/camera/camera_bg.svg" + } + } + + CameraControl { + id:cameracontrol + anchors.horizontalCenter: parent.horizontalCenter + ratio: 4/3 + device: camdev_device + width: camdev_device.width + height:80 + } + + function changeVisible(visible) { + console.log("camera visible is " + visible) + cameracontrol.switchChecked = visible + } +} diff --git a/app/main.qrc b/app/main.qrc new file mode 100644 index 0000000..4a5aac9 --- /dev/null +++ b/app/main.qrc @@ -0,0 +1,6 @@ + + + main.qml + CameraControl.qml + + -- cgit 1.2.3-korg