Skip to content

Commit c019440

Browse files
authored
Merge pull request #223 from saeugetier/feature/191-implement-gphoto2-as-camera-source
Feature/191 implement gphoto2 as camera source
2 parents e5ae91b + 8bf7914 commit c019440

15 files changed

Lines changed: 893 additions & 147 deletions

.github/workflows/flatpak.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ jobs:
2121
runs-on: ${{ matrix.variant.runner }}
2222
steps:
2323
- uses: actions/checkout@v4
24+
with:
25+
submodules: recursive # fetch submodules (recursively)
26+
fetch-depth: 0 # ensure full history so submodule refs are resolvable
27+
token: ${{ secrets.GITHUB_TOKEN }}
2428
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6
2529
with:
2630
bundle: photobooth.flatpak

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "shared-modules"]
2+
path = shared-modules
3+
url = https://github.com/flathub/shared-modules

io.github.saeugetier.photobooth.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,37 @@
131131
}
132132
]
133133
},
134+
{
135+
"name": "libgphoto2",
136+
"builddir": true,
137+
"cleanup": [
138+
"/doc",
139+
"*.la"
140+
],
141+
"sources": [
142+
{
143+
"type": "git",
144+
"url": "https://github.com/gphoto/libgphoto2.git",
145+
"tag": "v2.5.32",
146+
"commit": "de1f0617b1ffa39c1980d1306343709c7dc0120e",
147+
"x-checker-data": {
148+
"type": "anitya",
149+
"project-id": 12558,
150+
"stable-only": true
151+
}
152+
},
153+
{
154+
"type": "script",
155+
"dest-filename": "autogen.sh",
156+
"commands": [
157+
"AUTOMAKE=\"automake --foreign\" autoreconf -vfis"
158+
]
159+
}
160+
],
161+
"modules": [
162+
"shared-modules/libusb/libusb.json"
163+
]
164+
},
134165
{
135166
"name": "qtbooth",
136167
"buildsystem": "qmake",

qml.qrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
<file>qml/SnapshotSettings.qml</file>
3535
<file>qml/SnapshotSettingsForm.ui.qml</file>
3636
<file>qml/content/CameraRenderer.qml</file>
37+
<file>qml/content/CameraSource.qml</file>
3738
<file>qml/content/CollageImageDelegate.qml</file>
3839
<file>qml/content/CollageRenderer.qml</file>
3940
<file>qml/content/Countdown.qml</file>

qml/Application.qml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ ApplicationWindow {
138138
console.log("Window mode changed to: " + applicationSettings.windowMode)
139139
}
140140

141-
settingsMenu.comboBoxCamera.onCurrentIndexChanged:
141+
settingsMenu.comboBoxCamera.onCurrentTextChanged:
142142
{
143143
applicationSettings.cameraName = settingsMenu.comboBoxCamera.currentText
144144
}
@@ -225,13 +225,5 @@ ApplicationWindow {
225225
{
226226
flow.imagePreview.effectButton.visible = !disableEffectPopup
227227
}
228-
229-
onCameraNameChanged:
230-
{
231-
print("Camera changed to " + cameraName)
232-
var id = flow.settingsMenu.findDeviceId(cameraName)
233-
print("Found ID: " + id)
234-
flow.snapshotMenu.cameraRenderer.deviceId = id
235-
}
236228
}
237229
}

qml/ApplicationFlow.qml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ ApplicationFlowForm {
6161
state = "collageSelection"
6262
}
6363

64+
snapshotMenu.cameraRenderer.cameraName: applicationSettings.cameraName
65+
6466
imagePreview.onAccept: (filename, effect) =>
6567
{
6668
if(applicationSettings.printEnable)

qml/SettingsMenu.qml

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import QtQuick.Controls
44
import QtQuick.Dialogs
55
import Qt.labs.platform
66
import QtQml
7+
import GPhotoCamera
78
import "content"
89

910
SettingsMenuForm {
@@ -25,26 +26,22 @@ SettingsMenuForm {
2526
{
2627
listModel.push(availableCameras[i].description)
2728
}
29+
var gphotoCameras = gphotoCamera.availableCameras();
30+
console.log("GPhoto Camera Count: " + Number(gphotoCameras.length).toString())
31+
for(i = 0; i < gphotoCameras.length; i++)
32+
{
33+
listModel.push(gphotoCameras[i])
34+
}
2835
return listModel;
2936
}
3037

31-
MediaDevices
32-
{
33-
id: mediaDevices
38+
GPhotoCamera {
39+
id: gphotoCamera
3440
}
3541

36-
function findDeviceId(cameraName)
42+
MediaDevices
3743
{
38-
var i;
39-
var availableCameras = mediaDevices.videoInputs;
40-
for(i = 0; i < availableCameras.length; i++)
41-
{
42-
if(availableCameras[i].description === cameraName)
43-
{
44-
return availableCameras[i].id;
45-
}
46-
}
47-
return mediaDevices.defaultVideoInput.id
44+
id: mediaDevices
4845
}
4946

5047
Component.onCompleted:

qml/SettingsMenuForm.ui.qml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ Item {
154154
}
155155
ComboBox {
156156
id: comboBoxCamera
157+
Layout.preferredWidth: 300
157158
}
158159
}
159160

@@ -206,6 +207,7 @@ Item {
206207
}
207208
ComboBox {
208209
id: comboBoxCameraOrientation
210+
Layout.preferredWidth: 300
209211
textRole: "text"
210212
valueRole: "value"
211213
model: [{

qml/content/CameraRenderer.qml

Lines changed: 36 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Qt5Compat.GraphicalEffects
55
import QtQuick.Layouts
66
import BackgroundFilter
77
import CaptureProcessor
8+
import GPhotoCamera
89

910
Item {
1011
id: renderer
@@ -13,35 +14,16 @@ Item {
1314

1415
property bool photoProcessing: (state === "snapshot")
1516
property bool mirrored: true
16-
property string deviceId: camera.deviceId
17+
property string cameraName: ""
1718
property alias backgroundFilter: backgroundFilter
1819
property bool backgroundFilterEnabled: false
1920
property url backgroundImage: ""
2021

21-
function printDevicesToConsole(devices) {
22-
console.log("Found " + devices.length + " camera devices!")
23-
for (var i = 0; i < devices.length; i++) {
24-
console.log(
25-
"Found device: " + devices[i].deviceId + " with number " + i)
26-
}
27-
}
28-
29-
MediaDevices {
30-
id: mediaDevices
22+
onCameraNameChanged:
23+
{
24+
print("Camera changed to " + cameraName)
3125
}
3226

33-
onDeviceIdChanged: id => {
34-
// get the camera device with id from mediaDevices
35-
console.log("Selected camera: " + id)
36-
var availableCameras = mediaDevices.videoInputs
37-
for (var i = 0; i < availableCameras.length; i++) {
38-
if (availableCameras[i].deviceId === id) {
39-
camera.cameraDevice = availableCameras[i]
40-
break
41-
}
42-
}
43-
}
44-
4527
CaptureProcessor
4628
{
4729
id: captureProcessor
@@ -65,83 +47,42 @@ Item {
6547
}
6648
}
6749

68-
CaptureSession {
50+
CameraSource
51+
{
52+
id: cameraSource
6953

70-
camera: Camera {
71-
id: camera
72-
cameraDevice: mediaDevices.defaultVideoInput
73-
exposureMode: Camera.ExposurePortrait
74-
exposureCompensation: -1.0
75-
whiteBalanceMode: Camera.WhiteBalanceAuto
76-
flashMode: Camera.FlashAuto
77-
torchMode: Camera.TorchAuto
78-
}
54+
cameraName: renderer.cameraName
7955

80-
id: cameraSession
81-
82-
videoOutput: output
83-
84-
imageCapture: ImageCapture {
85-
id: imageCapture
86-
87-
onImageCaptured:
88-
{
89-
whiteOverlay.state = "released"
90-
renderer.state = "store"
91-
console.log("Captured")
92-
93-
console.log(applicationSettings.foldername.toString())
94-
var path = applicationSettings.foldername.toString()
95-
if(backgroundFilterEnabled)
96-
{
97-
path = path + "/raw"
98-
}
99-
path = path.replace(/^(file:\/{2})/, "")
100-
var cleanPath = decodeURIComponent(path)
101-
console.log(cleanPath)
102-
103-
captureProcessor.saveCapture(preview, cleanPath + "/Pict_" + new Date().toLocaleString(
104-
locale, "dd_MM_yyyy_hh_mm_ss") + ".jpg")
105-
}
106-
onErrorOccurred: {
107-
renderer.state = "preview"
108-
failed()
109-
}
110-
onErrorStringChanged: {
111-
console.log("Camera error: " + errorString)
56+
onImageCaptured: function(image) {
57+
whiteOverlay.state = "released"
58+
renderer.state = "store"
59+
console.log("Captured: " + image)
60+
61+
console.log(applicationSettings.foldername.toString())
62+
var path = applicationSettings.foldername.toString()
63+
if(backgroundFilterEnabled)
64+
{
65+
path = path + "/raw"
11266
}
113-
}
67+
path = path.replace(/^(file:\/{2})/, "")
68+
var cleanPath = decodeURIComponent(path)
69+
console.log(cleanPath)
11470

71+
captureProcessor.saveCapture(image, cleanPath + "/Pict_" + new Date().toLocaleString(
72+
locale, "dd_MM_yyyy_hh_mm_ss") + ".jpg")
73+
}
11574

116-
/*onCameraStateChanged:
117-
{
118-
if(camera.cameraState == Camera.UnloadedState)
119-
{
120-
console.log("Camera State Changed: Unloaded")
121-
printDevicesToConsole(QtMultimedia.availableCameras)
122-
camera.stop()
123-
cameraDiscoveryTimer.start()
124-
}
125-
else if(camera.cameraState == Camera.LoadedState)
126-
{
127-
console.log("Camera State Changed: Loaded")
128-
printDevicesToConsole(QtMultimedia.availableCameras)
129-
}
130-
else if(camera.cameraState == Camera.ActiveState)
131-
{
132-
console.log("Camera State Changed: Active");
133-
cameraDiscoveryTimer.stop()
134-
}
135-
else
136-
{
137-
console.log("Camera State Changed: Unknown");
138-
}
139-
}*/
75+
onErrorOccurred: function(errorString) {
76+
renderer.state = "preview"
77+
console.log("Camera error: " + errorString)
78+
failed()
79+
}
14080
}
14181

82+
14283
ReplaceBackgroundVideoFilter {
14384
id: backgroundFilter
144-
videoSink: output.videoSink
85+
videoSink: cameraSource.output.videoSink
14586
background: backgroundImage
14687

14788
onCaptureProcessingFinished: fileName =>
@@ -155,14 +96,6 @@ Item {
15596
}
15697
}
15798

158-
Connections {
159-
id: cameraErrorListener
160-
target: camera
161-
function errorOccured(_, errorString) {
162-
console.log("Camera Error: " + errorString)
163-
}
164-
}
165-
16699
VideoOutput {
167100
id: output
168101

@@ -222,31 +155,10 @@ Item {
222155
height: output.height
223156
}
224157

225-
/* Timer
226-
{
227-
id: cameraDiscoveryTimer
228-
229-
interval: 1000
230-
repeat: true
231-
232-
onTriggered:
233-
{
234-
//camera discovery is delayed
235-
var availableCameras = QtMultimedia.availableCameras
236-
printDevicesToConsole(availableCameras)
237-
238-
if(availableCameras.length > 0)
239-
{
240-
camera.deviceId = availableCameras[0].deviceId
241-
camera.start()
242-
}
243-
244-
}
245-
}*/
246158
function takePhoto() {
247-
if (cameraSession.imageCapture.readyForCapture) {
159+
if (cameraSource.readyForCapture) {
248160
state = "snapshot"
249-
cameraSession.imageCapture.capture()
161+
cameraSource.captureImage()
250162
} else {
251163
renderer.state = "preview"
252164
failed()
@@ -279,7 +191,7 @@ Item {
279191
}
280192
StateChangeScript {
281193
script: {
282-
camera.start()
194+
cameraSource.start()
283195
}
284196
}
285197
},
@@ -310,7 +222,7 @@ Item {
310222
}
311223
ScriptAction {
312224
script: {
313-
camera.stop()
225+
cameraSource.stop()
314226
}
315227
}
316228
}

0 commit comments

Comments
 (0)