pax_global_header00006660000000000000000000000064143502354070014515gustar00rootroot0000000000000052 comment=afeb7faf8a24282434b6381cd78c0f9032972f2c libaperture-0-0.1.0+git20221220/000077500000000000000000000000001435023540700155605ustar00rootroot00000000000000libaperture-0-0.1.0+git20221220/.editorconfig000066400000000000000000000003611435023540700202350ustar00rootroot00000000000000root = true [*] end_of_line = lf indent_style = space insert_final_newline = true charset = utf-8 trim_trailing_whitespace = true [*.json] indent_size=4 [*.{c,h,yml,xml,xml.in,ent.in,ui,sh}] indent_size = 2 [meson.build] indent_size = 2 libaperture-0-0.1.0+git20221220/.gitignore000066400000000000000000000000501435023540700175430ustar00rootroot00000000000000_build *~ .flatpak-builder .buildconfig libaperture-0-0.1.0+git20221220/.gitlab-ci.yml000066400000000000000000000023131435023540700202130ustar00rootroot00000000000000include: 'https://gitlab.gnome.org/GNOME/citemplates/raw/master/flatpak/flatpak_ci_initiative.yml' stages: - build - publish flatpak: extends: .flatpak image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:master stage: build variables: MANIFEST_PATH: "demo/io.gnome.Aperture.Demo.json" RUNTIME_REPO: "https://flathub.org/repo/flathub.flatpakrepo" FLATPAK_MODULE: "libaperture" MESON_ARGS: "" APP_ID: "io.gnome.Aperture.Demo" BUNDLE: "aperture-demo.flatpak" fedora: image: registry.gitlab.gnome.org/gnome/gtk/fedora-base:v19 stage: build script: - dnf install -y vala gstreamer1-plugins-good-gtk gstreamer1-plugins-bad-free-extras - meson --buildtype=release _build -Db_coverage=true - ninja -C _build aperture-doc # make sure everything gets built, including VAPI, GIR, etc. - ninja -C _build install - xvfb-run ninja -C _build test - ninja -C _build test-coverage - mv _build/doc/html/ _reference/ coverage: '/^\s+lines\.+:\s+([\d.]+\%)\s+/' artifacts: paths: - _reference - _build pages: stage: publish dependencies: - fedora script: - mv _reference public artifacts: paths: - public only: - main libaperture-0-0.1.0+git20221220/COPYING000066400000000000000000000167441435023540700166270ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. libaperture-0-0.1.0+git20221220/demo/000077500000000000000000000000001435023540700165045ustar00rootroot00000000000000libaperture-0-0.1.0+git20221220/demo/aperture-demo-window.c000066400000000000000000000161341435023540700227330ustar00rootroot00000000000000/* aperture-demo-window.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include "aperture-demo-window.h" struct _ApertureDemoWindow { GtkApplicationWindow parent_instance; GtkStack *stack; GtkWidget *controls; ApertureViewfinder *viewfinder; GtkStack *controls_stack; GtkStack *no_cameras_stack; GtkButton *switch_camera; gboolean recording; gboolean taking_picture; }; G_DEFINE_TYPE (ApertureDemoWindow, aperture_demo_window, GTK_TYPE_APPLICATION_WINDOW) static char * get_file (GUserDirectory user_dir, gchar *extension) { /* Get a file path for an image or video. The filename is based on the * date and time. If that file already exists, a number will be added to * the end so that a new file is created. */ g_autoptr(GDateTime) date = g_date_time_new_now_local (); const char *dir = g_get_user_special_dir (user_dir); g_autofree char *filename = g_date_time_format (date, "%F_%T"); g_autofree char *basename = g_strdup_printf ("%s.%s", filename, extension); char *path = g_build_filename (dir, basename, NULL); for (int i = 1; g_file_test (path, G_FILE_TEST_EXISTS); i ++) { g_free (path); g_free (basename); basename = g_strdup_printf ("%s_%d.%s", filename, i, extension); path = g_build_filename (dir, basename, NULL); } return path; } static void update_ui (ApertureDemoWindow *self) { /* Update the UI depending on the state of the viewfinder and the current * operation. * * If it's recording, show the stop button, otherwise show the regular * controls. If it's not in the READY state, make the controls insensitive * so they can't be used. If there are no cameras, show that screen * instead. */ ApertureViewfinderState state = aperture_viewfinder_get_state (self->viewfinder); gtk_widget_set_sensitive (self->controls, state == APERTURE_VIEWFINDER_STATE_READY && !(self->recording || self->taking_picture)); gtk_stack_set_visible_child_name (self->controls_stack, self->recording ? "video" : "main"); gtk_stack_set_visible_child_name (self->no_cameras_stack, state == APERTURE_VIEWFINDER_STATE_NO_CAMERAS ? "no_cameras" : "main"); } static void on_viewfinder_state_notify (ApertureDemoWindow *self) { update_ui (self); } static void on_photo_taken (ApertureViewfinder *source, GAsyncResult *res, ApertureDemoWindow *self) { g_autofree char *file = get_file (G_USER_DIRECTORY_PICTURES, "jpg"); g_autoptr(GError) err = NULL; g_autoptr(GdkPixbuf) pixbuf = aperture_viewfinder_take_picture_finish (source, res, &err); if (err) { g_critical ("%s", err->message); } else { gdk_pixbuf_save (pixbuf, file, "jpeg", &err, NULL); if (err) { g_critical ("%s", err->message); } else { g_debug ("Saved picture to %s", (char *) file); } } self->taking_picture = FALSE; update_ui (self); } static void on_take_photo_clicked (GtkButton *button, ApertureDemoWindow *self) { self->taking_picture = TRUE; update_ui (self); aperture_viewfinder_take_picture_async (self->viewfinder, NULL, (GAsyncReadyCallback) on_photo_taken, self); } static void on_take_video_clicked (GtkButton *button, ApertureDemoWindow *self) { g_autofree char *file = get_file (G_USER_DIRECTORY_VIDEOS, "mp4"); g_autoptr(GError) err = NULL; self->recording = TRUE; update_ui (self); aperture_viewfinder_start_recording_to_file (self->viewfinder, file, &err); if (err) { g_critical ("%s", err->message); } } static void on_video_done (ApertureViewfinder *source, GAsyncResult *res, ApertureDemoWindow *self) { self->recording = FALSE; update_ui (self); } static void on_stop_video_clicked (GtkButton *button, ApertureDemoWindow *self) { aperture_viewfinder_stop_recording_async (self->viewfinder, NULL, (GAsyncReadyCallback) on_video_done, self); } static void on_barcode_detected (ApertureViewfinder *viewfinder, ApertureBarcode barcode_type, const char *data) { const char *type = g_enum_to_string (APERTURE_TYPE_BARCODE, barcode_type); g_print ("Barcode detected (%s): %s\n", type, data); } static void on_switch_camera_clicked (GtkButton *button, ApertureDemoWindow *self) { /* When the "switch camera" button is clicked, use * aperture_device_manager_next_camera() to find the next camera, and * start using that. */ g_autoptr(ApertureCamera) camera = NULL; g_autoptr(ApertureDeviceManager) manager = aperture_device_manager_get_instance (); g_autoptr(GError) err = NULL; camera = aperture_device_manager_next_camera (manager, aperture_viewfinder_get_camera (self->viewfinder)); aperture_viewfinder_set_camera (self->viewfinder, camera, &err); if (err) { g_critical ("%s", err->message); } } /* INIT */ static void aperture_demo_window_class_init (ApertureDemoWindowClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); gtk_widget_class_set_template_from_resource (widget_class, "/io/gnome/Aperture/Demo/ui/demo-window.ui"); gtk_widget_class_bind_template_child (widget_class, ApertureDemoWindow, viewfinder); gtk_widget_class_bind_template_child (widget_class, ApertureDemoWindow, controls); gtk_widget_class_bind_template_child (widget_class, ApertureDemoWindow, stack); gtk_widget_class_bind_template_child (widget_class, ApertureDemoWindow, controls_stack); gtk_widget_class_bind_template_child (widget_class, ApertureDemoWindow, switch_camera); gtk_widget_class_bind_template_child (widget_class, ApertureDemoWindow, no_cameras_stack); gtk_widget_class_bind_template_callback (widget_class, on_barcode_detected); gtk_widget_class_bind_template_callback (widget_class, on_stop_video_clicked); gtk_widget_class_bind_template_callback (widget_class, on_switch_camera_clicked); gtk_widget_class_bind_template_callback (widget_class, on_take_photo_clicked); gtk_widget_class_bind_template_callback (widget_class, on_take_video_clicked); gtk_widget_class_bind_template_callback (widget_class, on_viewfinder_state_notify); } static void aperture_demo_window_init (ApertureDemoWindow *self) { gtk_widget_init_template (GTK_WIDGET (self)); /* Make sure the UI is in the correct state */ on_viewfinder_state_notify (self); } /* PUBLIC */ ApertureDemoWindow * aperture_demo_window_new (GtkApplication *app) { g_return_val_if_fail (GTK_IS_APPLICATION (app), NULL); return g_object_new (APERTURE_TYPE_DEMO_WINDOW, "application", app, NULL); } libaperture-0-0.1.0+git20221220/demo/aperture-demo-window.h000066400000000000000000000021651435023540700227370ustar00rootroot00000000000000/* aperture-demo-window.h * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include #include G_BEGIN_DECLS #define APERTURE_TYPE_DEMO_WINDOW (aperture_demo_window_get_type()) G_DECLARE_FINAL_TYPE (ApertureDemoWindow, aperture_demo_window, APERTURE, DEMO_WINDOW, GtkApplicationWindow) ApertureDemoWindow *aperture_demo_window_new (GtkApplication *app); G_END_DECLS libaperture-0-0.1.0+git20221220/demo/aperture-demo.c000066400000000000000000000027101435023540700214210ustar00rootroot00000000000000/* aperture-demo.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include #include #include "aperture-demo-window.h" static void on_activate (GtkApplication *app) { ApertureDemoWindow *window = aperture_demo_window_new (app); gtk_widget_show (GTK_WIDGET (window)); } int main (int argc, char **argv) { g_autoptr(GtkApplication) app; char *diagnostic; aperture_init (&argc, &argv); diagnostic = aperture_get_diagnostic_info (); g_print ("%s\n", diagnostic); g_free (diagnostic); app = gtk_application_new ("io.gnome.Aperture.Demo", G_APPLICATION_FLAGS_NONE); g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); return g_application_run (G_APPLICATION (app), argc, argv); } libaperture-0-0.1.0+git20221220/demo/demo-window.ui000066400000000000000000000150771435023540700213060ustar00rootroot00000000000000 libaperture-0-0.1.0+git20221220/demo/demo.gresources.xml000066400000000000000000000003001435023540700223230ustar00rootroot00000000000000 demo-window.ui libaperture-0-0.1.0+git20221220/demo/io.gnome.Aperture.Demo.json000066400000000000000000000047411435023540700235710ustar00rootroot00000000000000{ "app-id" : "io.gnome.Aperture.Demo", "runtime" : "org.gnome.Platform", "runtime-version" : "master", "sdk" : "org.gnome.Sdk", "command" : "aperture-demo", "finish-args" : [ "--share=network", "--share=ipc", "--socket=x11", "--socket=wayland", "--device=all", "--socket=pulseaudio" ], "cleanup" : [ "/include", "/lib/pkgconfig", "/man", "/share/doc", "/share/gtk-doc", "/share/man", "/share/pkgconfig", "/share/vala", "*.la", "*.a" ], "modules" : [ { "name": "gst-plugins-bad", "buildsystem": "meson", "config-opts": [ "--auto-features=disabled", "-Dzbar=enabled" ], "sources": [ { "type": "git", "url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad.git", "branch": "1.16.3", "commit": "ee8144e98b084d75ffabaef0ef3dca2af8d72061" } ], "modules": [{ "name": "zbar", "config-opts": [ "--without-qt", "--without-gtk", "--without-xv", "--without-imagemagick", "--disable-video", "--without-java", "--with-python2=no", "--with-doc=no", "--with-introspection=no" ], "sources": [{ "type": "git", "url": "git://git.linuxtv.org/zbar.git", "commit": "14900c67eccfb97e152063296f20cd32aa787578" }, { "type": "script", "dest-filename": "autogen.sh", "commands": [ "autoreconf -vfi -W none" ] } ] }] }, { "name" : "libaperture", "builddir" : true, "buildsystem" : "meson", "config-opts": [ "-Dbarcode_tests_skippable=false" ], "sources" : [ { "type": "git", "url": "https://gitlab.gnome.org/jwestman/libaperture.git" } ] } ] } libaperture-0-0.1.0+git20221220/demo/meson.build000066400000000000000000000004421435023540700206460ustar00rootroot00000000000000demo_sources = files( 'aperture-demo.c', 'aperture-demo-window.c', ) demo_resources = gnome.compile_resources( 'aperture-demo-resources', 'demo.gresources.xml', ) demo = executable('aperture-demo', demo_sources, demo_resources, dependencies: libaperture_dep, install: true, ) libaperture-0-0.1.0+git20221220/doc/000077500000000000000000000000001435023540700163255ustar00rootroot00000000000000libaperture-0-0.1.0+git20221220/doc/aperture-docs.xml000066400000000000000000000037761435023540700216410ustar00rootroot00000000000000 %gtkdocentities; ]> &package_name; Reference Manual This document is the API reference for &package_name; &package_version;. Aperture is a camera library for GTK. If you find any issues in this API reference, please report them using the bugtracker. 2020 James Westman and other contributors API Reference Index Object Hierarchy Index of Symbols Index of New Symbols in 0.1 libaperture-0-0.1.0+git20221220/doc/gtkdocentities.ent.in000066400000000000000000000006131435023540700224620ustar00rootroot00000000000000 libaperture-0-0.1.0+git20221220/doc/meson.build000066400000000000000000000016771435023540700205020ustar00rootroot00000000000000if get_option('gtk_doc') ent_conf = configuration_data({ 'PACKAGE': 'Aperture', 'PACKAGE_BUGREPORT': 'https://gitlab.gnome.org/jwestman/libaperture/issues', 'PACKAGE_NAME': 'Aperture', 'PACKAGE_STRING': 'libaperture', 'PACKAGE_TARNAME': 'libaperture-' + meson.project_version(), 'PACKAGE_URL': 'https://gitlab.gnome.org/jwestman/libaperture', 'PACKAGE_VERSION': meson.project_version(), 'PACKAGE_API_VERSION': aperture_api_version, 'PACKAGE_API_NAME': aperture_library_name, }) configure_file( input: 'gtkdocentities.ent.in', output: 'gtkdocentities.ent', configuration: ent_conf ) gnome.gtkdoc('aperture', main_xml: 'aperture-docs.xml', src_dir: [meson.source_root() / 'src', meson.build_root() / 'src'], dependencies: libaperture_dep, scan_args: [ '--rebuild-types', '--rebuild-sections', '--ignore-headers=devices pipeline private', ], install_dir: aperture_library_name, install: true, ) endif libaperture-0-0.1.0+git20221220/meson.build000066400000000000000000000017711435023540700177300ustar00rootroot00000000000000project('aperture', ['c'], version: '0.1.0', meson_version: '>= 0.50.0', license: 'LGPL3+', ) aperture_api_version = '0' aperture_library_name = 'aperture-@0@'.format(aperture_api_version) version_arr = meson.project_version().split('.') aperture_major = version_arr[0].to_int() aperture_minor = version_arr[1].to_int() aperture_micro = version_arr[2].to_int() add_project_arguments([ '-DAPERTURE_COMPILATION', ], language: 'c') gnome = import('gnome') i18n = import('i18n') pkgconfig = import('pkgconfig') cc = meson.get_compiler('c') g_ir_compiler = find_program('g-ir-compiler') gio_dep = dependency('gio-2.0') gtk_dep = dependency('gtk+-3.0') gst_dep = dependency('gstreamer-1.0') gst_video_dep = dependency('gstreamer-video-1.0') gst_app_dep = dependency('gstreamer-app-1.0') libaperture_deps = [ gio_dep, gtk_dep, gst_dep, gst_video_dep, gst_app_dep, ] subdir('src') subdir('doc') if get_option('demo') subdir('demo') endif subdir('tests') libaperture-0-0.1.0+git20221220/meson_options.txt000066400000000000000000000010051435023540700212110ustar00rootroot00000000000000option('gtk_doc', type: 'boolean', value: true, description: 'Build the documentation') option('demo', type: 'boolean', value: true, description: 'Build the demo application') option('gir', type: 'boolean', value: true, description: 'Build the GObject Introspection bindings') option('vapi', type: 'boolean', value: true, description: 'Build the Vala API bindings') option('barcode_tests_skippable', type: 'boolean', value: true, description: 'Allow tests which require ZBar to be skipped if it is not available.') libaperture-0-0.1.0+git20221220/src/000077500000000000000000000000001435023540700163475ustar00rootroot00000000000000libaperture-0-0.1.0+git20221220/src/aperture-camera.c000066400000000000000000000162521435023540700215760ustar00rootroot00000000000000/* aperture-device.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ /** * SECTION:aperture-camera * @title: ApertureCamera * @short_description: A camera device * * #ApertureCamera represents a camera plugged into the device. It is used to * query information about the camera or change its parameters. * * Since: 0.1 */ #include #include "aperture-camera.h" #include "private/aperture-camera-private.h" typedef struct { GstDevice *gst_device; } ApertureCameraPrivate; G_DEFINE_TYPE_WITH_PRIVATE (ApertureCamera, aperture_camera, G_TYPE_OBJECT) /* VFUNCS */ static void aperture_camera_finalize (GObject *object) { ApertureCamera *self = APERTURE_CAMERA (object); ApertureCameraPrivate *priv = aperture_camera_get_instance_private (self); g_clear_object (&priv->gst_device); G_OBJECT_CLASS (aperture_camera_parent_class)->finalize (object); } static void aperture_camera_do_flash_async_impl (ApertureCamera *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_return_if_fail (APERTURE_IS_CAMERA (self)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); task = g_task_new (self, cancellable, callback, user_data); g_task_set_source_tag (task, aperture_camera_do_flash_async_impl); g_task_return_boolean (task, FALSE); g_object_unref (task); } static gboolean aperture_camera_do_flash_finish_impl (ApertureCamera *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (APERTURE_IS_CAMERA (self), FALSE); g_return_val_if_fail (G_IS_TASK (result), FALSE); return g_task_propagate_boolean (G_TASK (result), error); } static GstElement * aperture_camera_get_source_element_impl (ApertureCamera *self, GstElement *previous) { ApertureCameraPrivate *priv; g_return_val_if_fail (APERTURE_IS_CAMERA (self), NULL); g_return_val_if_fail (previous == NULL || GST_IS_ELEMENT (previous), NULL); priv = aperture_camera_get_instance_private (self); if (gst_device_reconfigure_element (priv->gst_device, previous)) { return NULL; } return gst_device_create_element (priv->gst_device, NULL); } static void aperture_camera_set_torch_impl (ApertureCamera *self, gboolean state) { /* noop */ } /* INIT */ static void aperture_camera_class_init (ApertureCameraClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = aperture_camera_finalize; klass->do_flash_async = aperture_camera_do_flash_async_impl; klass->do_flash_finish = aperture_camera_do_flash_finish_impl; klass->get_source_element = aperture_camera_get_source_element_impl; klass->set_torch = aperture_camera_set_torch_impl; } static void aperture_camera_init (ApertureCamera *self) { } /* PUBLIC */ /** * aperture_camera_do_flash_async: * @self: an #ApertureCamera * @cancellable: (nullable): a #GCancellable * @callback: a #GAsyncReadyCallback to execute upon completion * @user_data: closure data for @callback * * Activates the flash associated with this camera. When this is done, * @callback will be called. * * The flash will be turned off automatically, usually after a few hundred * milliseconds (depending on the model of the flash device). The callback is * called while the flash is still on. * * Since: 0.1 */ void aperture_camera_do_flash_async (ApertureCamera *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { APERTURE_CAMERA_GET_CLASS (self)->do_flash_async (self, cancellable, callback, user_data); } /** * aperture_camera_do_flash_finish: * @self: an #ApertureCamera * @result: a #GAsyncResult provided to callback * @error: a location for a #GError, or %NULL * * Gets the result of an operation started by aperture_camera_do_flash_async(). * You should call this in your callback if you want to know whether the * operation succeeded; if you don't care, there is no need to call this * function. * * Returns: %TRUE if the flash was successfully enabled, otherwise %FALSE * * Since: 0.1 */ gboolean aperture_camera_do_flash_finish (ApertureCamera *self, GAsyncResult *result, GError **error) { return APERTURE_CAMERA_GET_CLASS (self)->do_flash_finish (self, result, error); } /** * aperture_camera_set_torch: * @self: an #ApertureCamera * @state: %TRUE to turn the torch on, or %FALSE to turn it off * * Turns the torch associated with this camera on or off. * * Typically, flash bulbs have two modes: flash and torch. Flash is brighter, * but only lasts for a few hundred milliseconds. Torch is not as bright but * can be left on indefinitely. * * Since: 0.1 */ void aperture_camera_set_torch (ApertureCamera *self, gboolean state) { APERTURE_CAMERA_GET_CLASS (self)->set_torch (self, state); } /* INTERNAL */ /** * PRIVATE:aperture_camera_new: * @gst_device: (transfer full): The GStreamer device for the camera * * Create a new #ApertureCamera. * * Returns: (transfer full): a newly created #ApertureCamera */ ApertureCamera * aperture_camera_new (GstDevice *gst_device) { ApertureCamera *camera; ApertureCameraPrivate *priv; g_return_val_if_fail (GST_IS_DEVICE (gst_device), NULL);; camera = g_object_new (APERTURE_TYPE_CAMERA, NULL); priv = aperture_camera_get_instance_private (camera); priv->gst_device = g_object_ref (gst_device); return camera; } /** * PRIVATE:aperture_camera_get_source_element: * @self: an #ApertureCamera * @previous: (nullable): a #GstElement to reconfigure, or %NULL * * Gets a GStreamer source element that provides this camera's video feed. * * Returns: (transfer full): a newly created source #GstElement, or %NULL * if @previous was reconfigured instead */ GstElement * aperture_camera_get_source_element (ApertureCamera *self, GstElement *previous) { g_return_val_if_fail (APERTURE_IS_CAMERA (self), NULL); g_return_val_if_fail (previous == NULL || GST_IS_ELEMENT (previous), NULL); return APERTURE_CAMERA_GET_CLASS (self)->get_source_element (self, previous); } /** * PRIVATE:aperture_camera_get_gst_device: * @self: an #ApertureCamera * * Gets the #GstDevice corresponding to an #ApertureCamera. * * Returns: (transfer none): the #GstDevice representing the camera */ GstDevice * aperture_camera_get_gst_device (ApertureCamera *self) { ApertureCameraPrivate *priv; g_return_val_if_fail (APERTURE_IS_CAMERA (self), NULL); priv = aperture_camera_get_instance_private (self); return priv->gst_device; } libaperture-0-0.1.0+git20221220/src/aperture-camera.h000066400000000000000000000032461435023540700216020ustar00rootroot00000000000000/* aperture-device.h * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include #include G_BEGIN_DECLS #define APERTURE_TYPE_CAMERA (aperture_camera_get_type ()) G_DECLARE_DERIVABLE_TYPE (ApertureCamera, aperture_camera, APERTURE, CAMERA, GObject) void aperture_camera_do_flash_async (ApertureCamera *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean aperture_camera_do_flash_finish (ApertureCamera *self, GAsyncResult *result, GError **error); void aperture_camera_set_torch (ApertureCamera *self, gboolean state); G_END_DECLS libaperture-0-0.1.0+git20221220/src/aperture-device-manager.c000066400000000000000000000247731435023540700232240ustar00rootroot00000000000000/* aperture-device-manager.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ /** * SECTION:aperture-device-manager * @title: ApertureDeviceManager * @short_description: Finds and monitors camera devices * * #ApertureDeviceManager finds and monitors camera devices that can be used * in Aperture. It provides functions for listing cameras, and signals for * detecting when cameras are plugged in or unplugged. */ #include #include #include "aperture-device-manager.h" #include "devices/aperture-device.h" #include "private/aperture-camera-private.h" struct _ApertureDeviceManager { GObject parent_instance; GstDeviceMonitor *monitor; GListStore *device_list; }; G_DEFINE_TYPE (ApertureDeviceManager, aperture_device_manager, G_TYPE_OBJECT) enum { PROP_0, PROP_NUM_CAMERAS, N_PROPS }; static GParamSpec *props[N_PROPS]; enum { SIGNAL_CAMERA_ADDED, SIGNAL_CAMERA_REMOVED, N_SIGNALS, }; static guint signals[N_SIGNALS]; /* Finds a matching ApertureCamera in our list, given a GstDevice */ static gboolean find_device_in_list_model (GListModel *model, GstDevice *gst_device, uint *position) { g_autoptr(ApertureCamera) camera = NULL; int i; int n = g_list_model_get_n_items (model); for (i = 0; i < n; i ++) { g_clear_object (&camera); camera = g_list_model_get_item (model, i); if (aperture_camera_get_gst_device (camera) == gst_device) { *position = i; return TRUE; } } return FALSE; } static uint find_camera_in_list_model (GListModel *model, ApertureCamera *camera) { /* FIXME: When GLib 2.64 becomes common enough, use g_list_store_find() */ g_autoptr(ApertureCamera) current = NULL; int i; int n = g_list_model_get_n_items (model); for (i = 0; i < n; i ++) { g_clear_object (¤t); current = g_list_model_get_item (model, i); if (camera == current) { return i; } } return -1; } /* Gets an #ApertureCamera instance from the #ApertureDevice implementation, * adds it to the device manager's list, and returns it. */ static ApertureCamera * add_camera (ApertureDeviceManager *self, GstDevice *gst_device) { ApertureDevice *device = aperture_device_get_instance (); g_autoptr(ApertureCamera) camera = aperture_device_get_camera (device, gst_device); /* aperture_device_get_camera might return NULL, which means we should * ignore this device */ if (camera != NULL) { g_list_store_append (self->device_list, camera); return camera; } return NULL; } static gboolean on_bus_message (GstBus *bus, GstMessage *message, gpointer user_data) { ApertureDeviceManager *self = APERTURE_DEVICE_MANAGER (user_data); g_autoptr(GstDevice) device = NULL; ApertureCamera *camera; guint device_index = -1; switch (message->type) { case GST_MESSAGE_DEVICE_ADDED: gst_message_parse_device_added (message, &device); g_debug ("New camera detected: %s", gst_device_get_display_name (device)); camera = add_camera (self, device); if (camera) { g_object_notify_by_pspec (G_OBJECT (self), props[PROP_NUM_CAMERAS]); g_signal_emit (self, signals[SIGNAL_CAMERA_ADDED], 0, camera); } break; case GST_MESSAGE_DEVICE_REMOVED: gst_message_parse_device_removed (message, &device); g_debug ("Camera removed: %s", gst_device_get_display_name (device)); if (find_device_in_list_model (G_LIST_MODEL (self->device_list), device, &device_index)) { camera = g_list_model_get_item (G_LIST_MODEL (self->device_list), device_index); g_list_store_remove (self->device_list, device_index); g_object_notify_by_pspec (G_OBJECT (self), props[PROP_NUM_CAMERAS]); g_signal_emit (self, signals[SIGNAL_CAMERA_REMOVED], 0, camera); g_object_unref (camera); } break; default: break; } return G_SOURCE_CONTINUE; } /* VFUNCS */ static void aperture_device_manager_finalize (GObject *object) { ApertureDeviceManager *self = APERTURE_DEVICE_MANAGER (object); g_autoptr(GstBus) bus = gst_device_monitor_get_bus (self->monitor); gst_bus_remove_watch (bus); gst_device_monitor_stop (self->monitor); g_clear_object (&self->monitor); g_clear_object (&self->device_list); G_OBJECT_CLASS (aperture_device_manager_parent_class)->finalize (object); } static void aperture_device_manager_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ApertureDeviceManager *self = APERTURE_DEVICE_MANAGER (object); switch (prop_id) { case PROP_NUM_CAMERAS: g_value_set_int (value, aperture_device_manager_get_num_cameras (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } /* INIT */ static void aperture_device_manager_class_init (ApertureDeviceManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = aperture_device_manager_finalize; object_class->get_property = aperture_device_manager_get_property; props[PROP_NUM_CAMERAS] = g_param_spec_int ("num-cameras", "Number of cameras", "The number of cameras available.", 0, G_MAXINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); g_object_class_install_properties (object_class, N_PROPS, props); /** * ApertureDeviceManager::camera-added: * @self: the #ApertureDeviceManager * @camera_index: the new camera * * Emitted when a camera is discovered. * * Since: 0.1 */ signals[SIGNAL_CAMERA_ADDED] = g_signal_new ("camera-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, APERTURE_TYPE_CAMERA); /** * ApertureDeviceManager::camera-removed: * @self: the #ApertureDeviceManager * @camera_index: the (now removed) camera * * Emitted when a camera is removed (typically because it has been unplugged). * * Since: 0.1 */ signals[SIGNAL_CAMERA_REMOVED] = g_signal_new ("camera-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, APERTURE_TYPE_CAMERA); } static void aperture_device_manager_init (ApertureDeviceManager *self) { ApertureDevice *device = aperture_device_get_instance (); g_autolist(ApertureCamera) cameras = aperture_device_list_cameras (device); g_autoptr(GstBus) bus = NULL; g_autolist(GstDevice) devices = NULL; GList *i; self->monitor = gst_device_monitor_new (); gst_device_monitor_add_filter (self->monitor, "Source/Video", NULL); gst_device_monitor_start (self->monitor); self->device_list = g_list_store_new (APERTURE_TYPE_CAMERA); /* Add built-in cameras from the device */ for (i = cameras; i != NULL; i = i->next) { g_list_store_append (self->device_list, i->data); } /* Add devices from GstDeviceMonitor */ devices = gst_device_monitor_get_devices (self->monitor); for (i = devices; i != NULL; i = i->next) { add_camera (self, i->data); } bus = gst_device_monitor_get_bus (self->monitor); gst_bus_add_watch (bus, on_bus_message, self); } /* PUBLIC */ /** * aperture_device_manager_get_instance: * * Gets an #ApertureDeviceManager. * * Returns: (transfer full): an #ApertureDeviceManager * Since: 0.1 */ ApertureDeviceManager * aperture_device_manager_get_instance (void) { static ApertureDeviceManager *instance; if (instance == NULL) { instance = g_object_new (APERTURE_TYPE_DEVICE_MANAGER, NULL); g_object_add_weak_pointer (G_OBJECT (instance), (gpointer *)&instance); } else { g_object_ref (instance); } return instance; } /** * aperture_device_manager_get_num_cameras: * @self: an #ApertureDeviceManager * * Gets the number of available cameras. * * Returns: the number of available cameras * Since: 0.1 */ int aperture_device_manager_get_num_cameras (ApertureDeviceManager *self) { g_return_val_if_fail (APERTURE_IS_DEVICE_MANAGER (self), 0); return g_list_model_get_n_items (G_LIST_MODEL (self->device_list)); } /** * aperture_device_manager_next_camera: * @self: an #ApertureDeviceManager * @camera: (nullable): an #ApertureCamera * * Gets the next camera index after @camera. If there are no cameras available, * returns %NULL. * * If @camera is %NULL, the first camera will be returned. * * Returns: (transfer full): the next camera, or %NULL if there are no cameras * Since: 0.1 */ ApertureCamera * aperture_device_manager_next_camera (ApertureDeviceManager *self, ApertureCamera *camera) { int num_cameras; int idx; g_return_val_if_fail (APERTURE_IS_DEVICE_MANAGER (self), 0); g_return_val_if_fail (camera == NULL || APERTURE_IS_CAMERA (camera), NULL); num_cameras = aperture_device_manager_get_num_cameras (self); if (num_cameras == 0) { return NULL; } if (camera == NULL) { idx = 0; } else { idx = find_camera_in_list_model (G_LIST_MODEL (self->device_list), camera) + 1; } if (idx >= num_cameras) { idx = 0; } return aperture_device_manager_get_camera (self, idx); } /** * aperture_device_manager_get_camera: * @self: an #ApertureDeviceManager * @idx: a camera index * * Gets an #ApertureCamera object for the given camera index. * * Returns: (transfer full): the #ApertureCamera at @idx */ ApertureCamera * aperture_device_manager_get_camera (ApertureDeviceManager *self, int idx) { g_return_val_if_fail (APERTURE_IS_DEVICE_MANAGER (self), NULL); g_return_val_if_fail (idx >= 0, NULL); g_return_val_if_fail (idx < g_list_model_get_n_items (G_LIST_MODEL (self->device_list)), NULL); return g_list_model_get_item (G_LIST_MODEL (self->device_list), idx); } libaperture-0-0.1.0+git20221220/src/aperture-device-manager.h000066400000000000000000000033471435023540700232230ustar00rootroot00000000000000/* aperture-device-manager.h * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #if !defined(_LIBAPERTURE_INSIDE) && !defined(_LIBAPERTURE_COMPILATION) #error "Only can be included directly." #endif #include #include "aperture-camera.h" G_BEGIN_DECLS #define APERTURE_TYPE_DEVICE_MANAGER (aperture_device_manager_get_type()) G_DECLARE_FINAL_TYPE (ApertureDeviceManager, aperture_device_manager, APERTURE, DEVICE_MANAGER, GObject) ApertureDeviceManager *aperture_device_manager_get_instance (void); int aperture_device_manager_get_num_cameras (ApertureDeviceManager *self); ApertureCamera *aperture_device_manager_next_camera (ApertureDeviceManager *self, ApertureCamera *camera); ApertureCamera *aperture_device_manager_get_camera (ApertureDeviceManager *self, int idx); G_END_DECLS libaperture-0-0.1.0+git20221220/src/aperture-utils.c000066400000000000000000000235351435023540700215100ustar00rootroot00000000000000/* aperture-utils.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include #include #include "aperture-build-info.h" #include "aperture-utils.h" #define BOOL_STR(x) (x ? "TRUE" : "FALSE") static gboolean initialized = FALSE; static gboolean init_warning = FALSE; /** * SECTION:aperture-utils * @title: Miscellaneous Utilities * @short_description: Miscellaneous utility functions and enums */ /** * aperture_init: * @argc: (inout) (optional): pointer to the number of command line arguments, * or %NULL * @argv: (array length=argc) (inout) (nullable) (optional) (transfer none): * pointer to the program's command line arguments, or %NULL * * Initializes the Aperture library, if it hasn't been initialized already. * * This will initialize GStreamer for you. If you don't want this, initialize * GStreamer yourself before calling aperture_init(). * * Since: 0.1 */ void aperture_init (int *argc, char ***argv) { if (initialized) { return; } if (!gst_is_initialized ()) { gst_init (argc, argv); } initialized = TRUE; } /** * aperture_is_initialized: * * Gets whether Aperture is initialized. * * Returns: %TRUE if the library is initialized, otherwise %FALSE * Since: 0.1 */ gboolean aperture_is_initialized (void) { return initialized; } /** * PRIVATE:aperture_private_ensure_initialized: * * Emits an error on the console if Aperture is not initialized. * * Since: 0.1 */ void aperture_private_ensure_initialized (void) { if (!initialized && !init_warning) { g_critical ("Aperture is not initialized! Please call aperture_init() before using the rest of the library to avoid errors and crashes."); init_warning = TRUE; } } /** * aperture_is_barcode_detection_enabled: * * Determines whether the barcode detection features of Aperture are * enabled. * * This is based on whether the `zbar` element is available to GStreamer. * It is part of the gst-plugins-bad package. Note that many distributions * don't enable the zbar component of gst-plugins-bad by default, because * it needs an extra dependency (the zbar library). You may need to find * a gst-plugins-bad-extras package or similar, or compile that particular * plugin yourself. For a Flatpak example, see the demo application in * Aperture's source code. * * Note that Aperture itself does *not* need to be recompiled to enable * barcode detection. It is based solely on whether the GStreamer plugin * is available. * * Returns: %TRUE if barcode detection is available, otherwise %FALSE * Since: 0.1 */ gboolean aperture_is_barcode_detection_enabled (void) { g_autoptr(GstElementFactory) factory = gst_element_factory_find ("zbar"); return factory != NULL; } /* Convenience function to read the contents of a file, and fail gracefully by * returning an empty string. */ static char * read_file (const char *filename) { g_autoptr(GFile) file = g_file_new_for_path (filename); g_autoptr(GError) err = NULL; char *contents = NULL; g_file_load_contents (file, NULL, &contents, NULL, NULL, &err); if (err) { return g_strdup (""); } else { return contents; } } /** * aperture_get_diagnostic_info: * * Gets a string containing useful debugging information, suitable for * including in bug reports, for example. This could include versions of * relevant libraries, basic software and hardware information, etc. * * No guarantees are made about the format of the string. * * Returns: (transfer full): the diagnostic string. Free with g_free(). * Since: 0.1 */ char * aperture_get_diagnostic_info (void) { g_autoptr(GstDeviceMonitor) monitor = NULL; g_autolist(GstDevice) devices = NULL; g_autoptr(GString) device_info = g_string_new (NULL); g_autofree char *etc_os_release = read_file ("/etc/os-release"); if (gst_is_initialized ()) { int n = 0; monitor = gst_device_monitor_new (); gst_device_monitor_add_filter (monitor, "Source/Video", NULL); gst_device_monitor_start (monitor); devices = gst_device_monitor_get_devices (monitor); gst_device_monitor_stop (monitor); for (GList *l = devices; l != NULL; l = l->next) { GstDevice *device = l->data; g_autoptr(GstStructure) props = gst_device_get_properties (device); g_autoptr(GstCaps) caps = gst_device_get_caps (device); g_string_append_printf ( device_info, " [devices.%d]\n" " name = %s\n" " properties = %s\n" " caps = %s\n" , n ++, gst_object_get_name (GST_OBJECT (device)), gst_structure_to_string (props), gst_caps_to_string (caps) ); } } return g_strdup_printf ( "[/etc/os-release]\n" "%s\n" "[GLib]\n" " version = %d.%d.%d\n" "[GTK]\n" " version = %d.%d.%d\n" "[GStreamer]\n" " version = %d.%d.%d\n" " initialized = %s\n" "[Aperture]\n" " version = %d.%d.%d\n" " initialized = %s\n" " zbar_enabled = %s\n" "%s" , etc_os_release, GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION, GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION, GST_VERSION_MAJOR, GST_VERSION_MINOR, GST_VERSION_MICRO, BOOL_STR (gst_is_initialized ()), APERTURE_MAJOR_VERSION, APERTURE_MINOR_VERSION, APERTURE_MICRO_VERSION, BOOL_STR (aperture_is_initialized ()), BOOL_STR (aperture_is_barcode_detection_enabled ()), device_info->str ); } /** * ApertureBarcode: * @APERTURE_BARCODE_UNKNOWN: A barcode was detected, but Aperture does not recognize its type. * @APERTURE_BARCODE_COMPOSITE: The code is a composite of multiple barcode types. * @APERTURE_BARCODE_EAN2: * @APERTURE_BARCODE_EAN5: * @APERTURE_BARCODE_EAN8: * @APERTURE_BARCODE_EAN13: * @APERTURE_BARCODE_UPCA: * @APERTURE_BARCODE_UPCE: * @APERTURE_BARCODE_ISBN10: * @APERTURE_BARCODE_ISBN13: * @APERTURE_BARCODE_I25: * @APERTURE_BARCODE_DATABAR: * @APERTURE_BARCODE_DATABAR_EXP: * @APERTURE_BARCODE_CODABAR: * @APERTURE_BARCODE_CODE39: * @APERTURE_BARCODE_CODE93: * @APERTURE_BARCODE_CODE128: * @APERTURE_BARCODE_PDF417: * @APERTURE_BARCODE_QR: * * Represents the type of a barcode detected in a video stream. * * Different barcode types are used for different purposes and different types * of data, so it is important to check a barcode's type before attempting to * use its data. * * Since: 0.1 */ /** * PRIVATE:aperture_barcode_type_from_string: * @string: a barcode type string from ZBar * * Takes a string representing a barcode type from ZBar and returns the * matching #ApertureBarcode value. * * Returns: the barcode enum, or %APERTURE_BARCODE_UNKNOWN if the type is * not recognized */ ApertureBarcode aperture_barcode_type_from_string (const char *string) { // This list is from https://github.com/ZBar/ZBar/blob/854a5d97059e395807091ac4d80c53f7968abb8f/zbar/symbol.c if (g_strcmp0 (string, "COMPOSITE") == 0) { return APERTURE_BARCODE_COMPOSITE; } else if (g_strcmp0 (string, "EAN-2") == 0) { return APERTURE_BARCODE_EAN2; } else if (g_strcmp0 (string, "EAN-5") == 0) { return APERTURE_BARCODE_EAN5; } else if (g_strcmp0 (string, "EAN-8") == 0) { return APERTURE_BARCODE_EAN8; } else if (g_strcmp0 (string, "EAN-13") == 0) { return APERTURE_BARCODE_EAN13; } else if (g_strcmp0 (string, "UPC-A") == 0) { return APERTURE_BARCODE_UPCA; } else if (g_strcmp0 (string, "UPC-E") == 0) { return APERTURE_BARCODE_UPCE; } else if (g_strcmp0 (string, "ISBN-10") == 0) { return APERTURE_BARCODE_ISBN13; } else if (g_strcmp0 (string, "ISBN-13") == 0) { return APERTURE_BARCODE_ISBN10; } else if (g_strcmp0 (string, "I2/5") == 0) { return APERTURE_BARCODE_I25; } else if (g_strcmp0 (string, "DataBar") == 0) { return APERTURE_BARCODE_DATABAR; } else if (g_strcmp0 (string, "DataBar-Exp") == 0) { return APERTURE_BARCODE_DATABAR_EXP; } else if (g_strcmp0 (string, "Codabar") == 0) { return APERTURE_BARCODE_CODABAR; } else if (g_strcmp0 (string, "CODE-39") == 0) { return APERTURE_BARCODE_CODE39; } else if (g_strcmp0 (string, "CODE-93") == 0) { return APERTURE_BARCODE_CODE93; } else if (g_strcmp0 (string, "CODE-128") == 0) { return APERTURE_BARCODE_CODE128; } else if (g_strcmp0 (string, "PDF417") == 0) { return APERTURE_BARCODE_PDF417; } else if (g_strcmp0 (string, "QR-Code") == 0) { return APERTURE_BARCODE_QR; } else { return APERTURE_BARCODE_UNKNOWN; } } libaperture-0-0.1.0+git20221220/src/aperture-utils.h000066400000000000000000000036341435023540700215130ustar00rootroot00000000000000/* aperture-utils.h * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include #if !defined(_LIBAPERTURE_INSIDE) && !defined(_LIBAPERTURE_COMPILATION) #error "Only can be included directly." #endif G_BEGIN_DECLS typedef enum { APERTURE_BARCODE_UNKNOWN, APERTURE_BARCODE_COMPOSITE, APERTURE_BARCODE_EAN2, APERTURE_BARCODE_EAN5, APERTURE_BARCODE_EAN8, APERTURE_BARCODE_EAN13, APERTURE_BARCODE_UPCA, APERTURE_BARCODE_UPCE, APERTURE_BARCODE_ISBN10, APERTURE_BARCODE_ISBN13, APERTURE_BARCODE_I25, APERTURE_BARCODE_DATABAR, APERTURE_BARCODE_DATABAR_EXP, APERTURE_BARCODE_CODABAR, APERTURE_BARCODE_CODE39, APERTURE_BARCODE_CODE93, APERTURE_BARCODE_CODE128, APERTURE_BARCODE_PDF417, APERTURE_BARCODE_QR, } ApertureBarcode; void aperture_init (int *argc, char ***argv); gboolean aperture_is_initialized (void); gboolean aperture_is_barcode_detection_enabled (void); ApertureBarcode aperture_barcode_type_from_string (const char *string); char *aperture_get_diagnostic_info (void); G_END_DECLS libaperture-0-0.1.0+git20221220/src/aperture-viewfinder.c000066400000000000000000000757041435023540700225170ustar00rootroot00000000000000/* aperture-viewfinder.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ /** * SECTION:aperture-viewfinder * @title: ApertureViewfinder * @short_description: A GTK widget for displaying a camera feed and taking * pictures and videos from it * * The #ApertureViewfinder is the main widget in Aperture. It is responsible * for displaying a camera feed in your UI, and for using that camera feed to * do useful things, like taking pictures, recording video, and detecting * barcodes. * * #ApertureViewfinder does not contain camera controls, however--just the * camera feed. You'll need to build a controls UI yourself. */ /** * ApertureViewfinderState: * @APERTURE_VIEWFINDER_STATE_LOADING: The #ApertureViewfinder is still loading. * @APERTURE_VIEWFINDER_STATE_READY: The #ApertureViewfinder is ready to be used. * @APERTURE_VIEWFINDER_STATE_NO_CAMERAS: The #ApertureViewfinder could not find any cameras to use. * @APERTURE_VIEWFINDER_STATE_ERROR: An error has occurred and the viewfinder is not usable. * * Indicates what the viewfinder is currently doing. Many tasks, like taking * a picture, recording video, or switching cameras, requires the viewfinder * to be in a particular state. * * Since: 0.1 */ /** * APERTURE_MEDIA_CAPTURE_ERROR: * * Error domain for errors that occur while using an #ApertureViewfinder. */ /** * ApertureMediaCaptureError: * @APERTURE_MEDIA_CAPTURE_ERROR_OPERATION_IN_PROGRESS: Another operation is in progress. Wait for it to finish before starting another operation. * @APERTURE_MEDIA_CAPTURE_ERROR_NO_RECORDING_TO_STOP: There is no recording to stop (applies to aperture_viewfinder_stop_recording_async()). * @APERTURE_MEDIA_CAPTURE_ERROR_CAMERA_DISCONNECTED: The active camera was disconnected during the operation. * @APERTURE_MEDIA_CAPTURE_ERROR_INTERRUPTED: The operation was interrupted by an unknown error. * @APERTURE_MEDIA_CAPTURE_ERROR_NOT_READY: The viewfinder is not in the %APERTURE_VIEWFINDER_STATE_READY #ApertureViewfinder:state. * * Indicates the error that caused an operation to fail. * * Note that functions might set errors from other domains as well. For * example, if an error occurs in the GStreamer pipeline during the operation, * that error will be passed directly to your async handler. * * Since: 0.1 */ #include "pipeline/aperture-pipeline-tee.h" #include "private/aperture-camera-private.h" #include "private/aperture-private.h" #include "aperture-camera.h" #include "aperture-device-manager.h" #include "aperture-utils.h" #include "aperture-viewfinder.h" struct _ApertureViewfinder { GtkBin parent_instance; ApertureDeviceManager *devices; ApertureCamera *camera; GstElement *camera_src; ApertureViewfinderState state; GstElement *branch_zbar; GstElement *gtksink; GtkWidget *sink_widget; GstElement *camerabin; AperturePipelineTee *tee; GstElement *pipeline; GTask *task_take_picture; char *take_picture_tmp_file; gboolean recording_video; GTask *task_take_video; }; G_DEFINE_TYPE (ApertureViewfinder, aperture_viewfinder, GTK_TYPE_BIN) enum { PROP_0, PROP_CAMERA, PROP_STATE, PROP_DETECT_BARCODES, N_PROPS, }; static GParamSpec *props[N_PROPS]; enum { SIGNAL_BARCODE_DETECTED, N_SIGNALS, }; static guint signals[N_SIGNALS]; static void end_take_photo_operation (ApertureViewfinder *self) { g_free (self->take_picture_tmp_file); self->take_picture_tmp_file = NULL; g_clear_object (&self->task_take_picture); } static void end_take_video_operation (ApertureViewfinder *self) { self->recording_video = FALSE; g_clear_object (&self->task_take_video); } /* Cancels any ongoing operations. Called when an error occurs, or when the * current camera is unplugged. @err is copied, so you still need to unref it * afterward. */ static void cancel_current_operation (ApertureViewfinder *self, GError *err) { if (self->task_take_picture) { g_task_return_error (self->task_take_picture, g_error_copy (err)); end_take_photo_operation (self); } else if (self->task_take_video) { g_task_return_error (self->task_take_video, g_error_copy (err)); end_take_video_operation (self); } } static void set_state (ApertureViewfinder *self, ApertureViewfinderState state) { g_autoptr(GError) err = NULL; if (self->state == state) { return; } if (state != APERTURE_VIEWFINDER_STATE_READY) { if (state == APERTURE_VIEWFINDER_STATE_NO_CAMERAS) { err = g_error_new (APERTURE_MEDIA_CAPTURE_ERROR, APERTURE_MEDIA_CAPTURE_ERROR_CAMERA_DISCONNECTED, "The active camera was disconnected during the operation"); } else { err = g_error_new (APERTURE_MEDIA_CAPTURE_ERROR, APERTURE_MEDIA_CAPTURE_ERROR_INTERRUPTED, "An error occurred during the operation"); } cancel_current_operation (self, err); } self->state = state; g_object_notify_by_pspec (G_OBJECT (self), props[PROP_STATE]); } /* Creates an element. Puts the viewfinder in the error state if that fails. * Thus, should only be used where the viewfinder doesn't work without the * element (otherwise, use gst_element_factory_make()). */ static GstElement * create_element (ApertureViewfinder *self, const char *type) { GstElement *element = gst_element_factory_make (type, NULL); if (element == NULL) { g_critical ("Element %s is not installed", type); set_state (self, APERTURE_VIEWFINDER_STATE_ERROR); } return element; } static GstElement * create_zbar_bin () { GstElement *bin = gst_bin_new (NULL); g_autoptr(GstPad) pad = NULL; GstPad *ghost_pad; GstElement *videoconvert; GstElement *zbar; GstElement *fakesink; videoconvert = gst_element_factory_make ("videoconvert", NULL); zbar = gst_element_factory_make ("zbar", NULL); fakesink = gst_element_factory_make ("fakesink", NULL); g_object_set (zbar, "cache", TRUE, NULL); gst_bin_add_many (GST_BIN (bin), videoconvert, zbar, fakesink, NULL); gst_element_link_many (videoconvert, zbar, fakesink, NULL); pad = gst_element_get_static_pad (videoconvert, "sink"); ghost_pad = gst_ghost_pad_new ("sink", pad); gst_pad_set_active (ghost_pad, TRUE); gst_element_add_pad (bin, ghost_pad); return bin; } /* If an operation (take photo, take video, switch camera) is in progress, * set @err. */ static void get_current_operation (ApertureViewfinder *self, GError **err) { /* for convenience, do nothing if there's already an error */ if (err && *err) { return; } if (self->task_take_picture) { g_set_error (err, APERTURE_MEDIA_CAPTURE_ERROR, APERTURE_MEDIA_CAPTURE_ERROR_OPERATION_IN_PROGRESS, "Operation in progress: Take picture"); } else if (self->task_take_video || self->recording_video) { g_set_error (err, APERTURE_MEDIA_CAPTURE_ERROR, APERTURE_MEDIA_CAPTURE_ERROR_OPERATION_IN_PROGRESS, "Operation in progress: Video recording"); } } static void set_error_if_not_ready (ApertureViewfinder *self, GError **err) { /* for convenience, do nothing if there's already an error */ if (err && *err) { return; } if (aperture_viewfinder_get_state (self) != APERTURE_VIEWFINDER_STATE_READY) { g_set_error (err, APERTURE_MEDIA_CAPTURE_ERROR, APERTURE_MEDIA_CAPTURE_ERROR_NOT_READY, "The viewfinder is not in the READY state."); } } static void on_pipeline_error (ApertureViewfinder *self, GstMessage *message) { g_autoptr(GError) err = NULL; g_autofree char *debug_info = NULL; gst_message_parse_error (message, &err, &debug_info); g_prefix_error (&err, "Error received from element %s: ", message->src->name); cancel_current_operation (self, err); g_debug ("Debugging information: %s", debug_info ? debug_info : "none"); if (GST_ELEMENT (self->camerabin)->current_state != GST_STATE_PLAYING) { set_state (self, APERTURE_VIEWFINDER_STATE_ERROR); g_critical ("%s", err->message); } } static void on_pixbuf_opened (GObject *object, GAsyncResult *res, gpointer user_data) { ApertureViewfinder *self = APERTURE_VIEWFINDER (user_data); GError *error = NULL; GdkPixbuf *pixbuf; g_autoptr(GFile) tmp_file = g_file_new_for_path (self->take_picture_tmp_file); pixbuf = gdk_pixbuf_new_from_stream_finish (res, &error); g_file_delete_async (tmp_file, G_PRIORITY_LOW, NULL, NULL, NULL); if (error != NULL) { g_task_return_error (self->task_take_picture, error); g_object_unref (pixbuf); end_take_photo_operation (self); return; } g_task_return_pointer (self->task_take_picture, pixbuf, g_object_unref); end_take_photo_operation (self); } static void on_image_file_opened (GObject *object, GAsyncResult *res, gpointer user_data) { ApertureViewfinder *self = APERTURE_VIEWFINDER (user_data); GError *error = NULL; g_autoptr(GInputStream) stream = NULL; stream = G_INPUT_STREAM (g_file_read_finish (G_FILE (object), res, &error)); if (error != NULL) { g_task_return_error (self->task_take_picture, error); end_take_photo_operation (self); return; } gdk_pixbuf_new_from_stream_async (stream, g_task_get_cancellable (self->task_take_picture), on_pixbuf_opened, self); } static void on_image_done (ApertureViewfinder *self, GstMessage *message) { const GstStructure *structure = gst_message_get_structure (message); const char *location; g_autoptr(GFile) file = NULL; if (self->task_take_picture) { gst_structure_get (structure, "filename", G_TYPE_STRING, &location, NULL); file = g_file_new_for_path (location); g_file_read_async (file, G_PRIORITY_LOW, g_task_get_cancellable (self->task_take_picture), on_image_file_opened, self); } } static void on_video_done (ApertureViewfinder *self) { g_task_return_boolean (self->task_take_video, TRUE); end_take_video_operation (self); } static void on_barcode_detected (ApertureViewfinder *self, GstMessage *message) { const char *code_type_str = NULL; ApertureBarcode code_type; const char *data = NULL; const GstStructure *structure; structure = gst_message_get_structure (message); code_type_str = gst_structure_get_string (structure, "type"); code_type = aperture_barcode_type_from_string (code_type_str); data = gst_structure_get_string (structure, "symbol"); g_signal_emit (self, signals[SIGNAL_BARCODE_DETECTED], 0, code_type, data); } /* Bus message handler for the pipeline */ static gboolean on_bus_message_async (GstBus *bus, GstMessage *message, gpointer user_data) { ApertureViewfinder *self = APERTURE_VIEWFINDER (user_data); switch (message->type) { case GST_MESSAGE_ERROR: on_pipeline_error (self, message); break; case GST_MESSAGE_ELEMENT: if (gst_message_has_name (message, "image-done")) { on_image_done (self, message); } else if (gst_message_has_name (message, "video-done")) { on_video_done (self); } else if (gst_message_has_name (message, "barcode")) { on_barcode_detected (self, message); } break; default: break; } return G_SOURCE_CONTINUE; } static void on_camera_added (ApertureViewfinder *self, ApertureCamera *camera, ApertureDeviceManager *devices) { if (self->state == APERTURE_VIEWFINDER_STATE_NO_CAMERAS) { set_state (self, APERTURE_VIEWFINDER_STATE_READY); aperture_viewfinder_set_camera (self, camera, NULL); } } /* Handler for when a camera is removed (unplugged, etc). If that was our * current camera, switch to a different camera. */ static void on_camera_removed (ApertureViewfinder *self, ApertureCamera *camera, ApertureDeviceManager *devices) { g_autoptr(GError) err = NULL; g_autoptr(ApertureCamera) next_camera = NULL; if (camera == self->camera) { int num_cameras = aperture_device_manager_get_num_cameras (self->devices); /* if the active camera was disconnected, any active operations should be * cancelled */ err = g_error_new (APERTURE_MEDIA_CAPTURE_ERROR, APERTURE_MEDIA_CAPTURE_ERROR_CAMERA_DISCONNECTED, "The active camera was disconnected during the operation"); cancel_current_operation (self, err); if (num_cameras == 0) { aperture_viewfinder_set_camera (self, NULL, NULL); set_state (self, APERTURE_VIEWFINDER_STATE_NO_CAMERAS); } else { next_camera = aperture_device_manager_get_camera (self->devices, 0); aperture_viewfinder_set_camera (self, next_camera, NULL); } } } /* VFUNCS */ static void aperture_viewfinder_finalize (GObject *object) { ApertureViewfinder *self = APERTURE_VIEWFINDER (object); g_clear_object (&self->devices); g_clear_object (&self->camerabin); g_clear_object (&self->tee); G_OBJECT_CLASS (aperture_viewfinder_parent_class)->finalize (object); } static void aperture_viewfinder_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ApertureViewfinder *self = APERTURE_VIEWFINDER (object); switch (prop_id) { case PROP_CAMERA: g_value_set_object (value, aperture_viewfinder_get_camera (self)); break; case PROP_STATE: g_value_set_enum (value, aperture_viewfinder_get_state (self)); break; case PROP_DETECT_BARCODES: g_value_set_boolean (value, aperture_viewfinder_get_detect_barcodes (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void aperture_viewfinder_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ApertureViewfinder *self = APERTURE_VIEWFINDER (object); switch (prop_id) { case PROP_CAMERA: aperture_viewfinder_set_camera (self, g_value_get_object (value), NULL); break; case PROP_DETECT_BARCODES: aperture_viewfinder_set_detect_barcodes (self, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } /* Starts the pipeline when the widget is realized */ static void aperture_viewfinder_realize (GtkWidget *widget) { ApertureViewfinder *self = APERTURE_VIEWFINDER (widget); GTK_WIDGET_CLASS (aperture_viewfinder_parent_class)->realize (widget); gst_element_set_state (self->camerabin, GST_STATE_PLAYING); } /* Stops the pipeline when the widget is unrealized */ static void aperture_viewfinder_unrealize (GtkWidget *widget) { ApertureViewfinder *self = APERTURE_VIEWFINDER (widget); GTK_WIDGET_CLASS (aperture_viewfinder_parent_class)->unrealize (widget); gst_element_set_state (self->camerabin, GST_STATE_NULL); } /* INIT */ static void aperture_viewfinder_class_init (ApertureViewfinderClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->finalize = aperture_viewfinder_finalize; object_class->get_property = aperture_viewfinder_get_property; object_class->set_property = aperture_viewfinder_set_property; widget_class->realize = aperture_viewfinder_realize; widget_class->unrealize = aperture_viewfinder_unrealize; /** * ApertureViewfinder:camera: * * The camera that is currently being used. * * Use #ApertureDeviceManager to obtain #ApertureCamera objects. * * To successfully switch cameras, the #ApertureViewfinder must be in the * %APERTURE_VIEWFINDER_STATE_READY state. This is because switching camera * sources would interrupt any picture or video that is being taken. * * Since: 0.1 */ props [PROP_CAMERA] = g_param_spec_object ("camera", "Camera", "The camera to use", APERTURE_TYPE_CAMERA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); /** * ApertureViewfinder:state: * * What the viewfinder is currently doing. * * The state indicates what the viewfinder is currently doing, or sometimes * that an error occurred. Many operations, like taking a picture or starting * a recording, require that the #ApertureViewfinder be in the * %APERTURE_VIEWFINDER_STATE_READY state. * * Since: 0.1 */ props [PROP_STATE] = g_param_spec_enum ("state", "State", "What the viewfinder is currently doing", APERTURE_TYPE_VIEWFINDER_STATE, APERTURE_VIEWFINDER_STATE_LOADING, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * ApertureViewfinder:detect-barcodes: * * Whether the #ApertureViewfinder should detect barcodes. * * When a barcode is detected, the ::barcode-detected signal will be * emitted. * * This only works if barcode detection is enabled. See * aperture_is_barcode_detection_enabled(). If barcode detection is not * available, the value of this property will always be %FALSE, even if you * try to set it to %TRUE. * * Since: 0.1 */ props [PROP_DETECT_BARCODES] = g_param_spec_boolean ("detect-barcodes", "Detect barcodes", "Whether to detect barcodes in the camera feed", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); g_object_class_install_properties (object_class, N_PROPS, props); /** * ApertureViewfinder::barcode-detected: * @self: the #ApertureViewfinder * @barcode_type: the type of barcode * @data: the data encoded in the barcode * * Emitted when a barcode is detected in the camera feed. * * This will only be emitted if #ApertureViewfinder:detect-barcodes is %TRUE. * * Barcodes are only detected when they appear on the feed, not on every * frame when they are visible. * * Since: 0.1 */ signals[SIGNAL_BARCODE_DETECTED] = g_signal_new ("barcode-detected", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, APERTURE_TYPE_BARCODE, G_TYPE_STRING); } static void aperture_viewfinder_init (ApertureViewfinder *self) { g_autoptr(ApertureCamera) camera = NULL; aperture_private_ensure_initialized (); self->gtksink = create_element (self, "gtksink"); g_object_get (self->gtksink, "widget", &self->sink_widget, NULL); gtk_widget_set_hexpand (self->sink_widget, TRUE); gtk_widget_set_vexpand (self->sink_widget, TRUE); gtk_widget_show (self->sink_widget); gtk_container_add (GTK_CONTAINER (self), self->sink_widget); self->camerabin = create_element (self, "camerabin"); gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (self->camerabin)), on_bus_message_async, self); self->tee = aperture_pipeline_tee_new (); aperture_pipeline_tee_add_branch (self->tee, self->gtksink); g_object_set (self->camerabin, "viewfinder-sink", self->tee, NULL); self->camera = NULL; self->devices = aperture_device_manager_get_instance (); g_signal_connect_object (self->devices, "camera-added", G_CALLBACK (on_camera_added), self, G_CONNECT_SWAPPED); g_signal_connect_object (self->devices, "camera-removed", G_CALLBACK (on_camera_removed), self, G_CONNECT_SWAPPED); if (aperture_device_manager_get_num_cameras (self->devices) > 0) { set_state (self, APERTURE_VIEWFINDER_STATE_READY); camera = aperture_device_manager_get_camera (self->devices, 0); aperture_viewfinder_set_camera (self, camera, NULL); } else { set_state (self, APERTURE_VIEWFINDER_STATE_NO_CAMERAS); } } /* PUBLIC */ /** * aperture_viewfinder_new: * * Creates a new #ApertureViewfinder. * * Returns: (transfer full): a new #ApertureViewfinder * Since: 0.1 */ ApertureViewfinder * aperture_viewfinder_new (void) { return g_object_new (APERTURE_TYPE_VIEWFINDER, NULL); } /** * aperture_viewfinder_set_camera: * @self: an #ApertureViewfinder * @camera: a camera index * @error: a location for a #GError, or %NULL * * Sets the camera that the #ApertureViewfinder will use. See * #ApertureViewfinder:camera. * * Since: 0.1 */ void aperture_viewfinder_set_camera (ApertureViewfinder *self, ApertureCamera *camera, GError **error) { g_autoptr(GstElement) wrapper = NULL; g_autoptr(GstElement) camera_src = NULL; GError *err = NULL; g_return_if_fail (APERTURE_IS_VIEWFINDER (self)); g_return_if_fail (camera == NULL || APERTURE_IS_CAMERA (camera)); get_current_operation (self, &err); set_error_if_not_ready (self, &err); if (err) { g_propagate_error (error, err); return; } if (self->camera == camera) { return; } g_set_object (&self->camera, camera); /* Must change camerabin to NULL and back to PLAYING for the change to take * effect */ gst_element_set_state (self->camerabin, GST_STATE_NULL); if (camera != NULL) { wrapper = create_element (self, "wrappercamerabinsrc"); camera_src = aperture_camera_get_source_element (camera, self->camera_src); /* camera_src might be NULL, which means the element was reconfigured and * we should keep using it */ if (camera_src) { g_object_set (wrapper, "video-source", camera_src, NULL); g_object_set (self->camerabin, "camera-source", wrapper, NULL); g_clear_object (&self->camera_src); self->camera_src = camera_src; } } if (gtk_widget_get_realized (GTK_WIDGET (self->sink_widget))) { gst_element_set_state (self->camerabin, GST_STATE_PLAYING); } g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CAMERA]); } /** * aperture_viewfinder_get_camera: * @self: an #ApertureViewfinder * * Gets the camera that the #ApertureViewfinder is currently using. See * #ApertureViewfinder:camera. * * Returns: (transfer none): the current camera * Since: 0.1 */ ApertureCamera * aperture_viewfinder_get_camera (ApertureViewfinder *self) { g_return_val_if_fail (APERTURE_IS_VIEWFINDER (self), NULL); return self->camera; } /** * aperture_viewfinder_get_state: * @self: an #ApertureViewfinder * * Gets the state of the #ApertureViewfinder. See #ApertureViewfinder:state. * * Returns: the viewfinder's state * Since: 0.1 */ ApertureViewfinderState aperture_viewfinder_get_state (ApertureViewfinder *self) { g_return_val_if_fail (APERTURE_IS_VIEWFINDER (self), 0); return self->state; } /** * aperture_viewfinder_set_detect_barcodes: * @self: an #ApertureViewfinder * @detect_barcodes: %TRUE to detect barcodes, otherwise %FALSE * * Sets whether the #ApertureViewfinder should look for barcodes in its camera * feed. See #ApertureViewfinder:detect-barcodes. * * Before calling this function, use aperture_is_barcode_detection_enabled() * to make sure the barcode detection feature is enabled. * * Since: 0.1 */ void aperture_viewfinder_set_detect_barcodes (ApertureViewfinder *self, gboolean detect_barcodes) { g_return_if_fail (APERTURE_IS_VIEWFINDER (self)); detect_barcodes = !!detect_barcodes; g_return_if_fail (!detect_barcodes || aperture_is_barcode_detection_enabled ()); if (aperture_viewfinder_get_detect_barcodes (self) == detect_barcodes) { return; } if (detect_barcodes) { self->branch_zbar = create_zbar_bin (); aperture_pipeline_tee_add_branch (self->tee, GST_ELEMENT (self->branch_zbar)); } else { aperture_pipeline_tee_remove_branch (self->tee, GST_ELEMENT (self->branch_zbar)); self->branch_zbar = NULL; } g_object_notify_by_pspec (G_OBJECT (self), props[PROP_DETECT_BARCODES]); } /** * aperture_viewfinder_get_detect_barcodes: * @self: an #ApertureViewfinder * * Gets whether the #ApertureViewfinder is looking for barcodes in its camera * feed. * * Returns: %TRUE if the viewfinder is looking for barcodes, otherwise %FALSE * Since: 0.1 */ gboolean aperture_viewfinder_get_detect_barcodes (ApertureViewfinder *self) { g_return_val_if_fail (APERTURE_IS_VIEWFINDER (self), FALSE); return self->branch_zbar != NULL; } /** * aperture_viewfinder_take_picture_async: * @self: an #ApertureViewfinder * @cancellable: (nullable): a #GCancellable * @callback: a #GAsyncReadyCallback to execute upon completion * @user_data: closure data for @callback * * Takes a picture. * * This may take a while. The resolution might be changed temporarily, * autofocusing might take place, etc. Basically everything you'd expect * to happen when you click the photo button in a camera app. * * When the picture has been taken, @callback will be called. Use * aperture_viewfinder_take_picture_finish() to get the picture as a * #GdkPixbuf. * * Since: 0.1 */ void aperture_viewfinder_take_picture_async (ApertureViewfinder *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task = NULL; GError *err = NULL; int tmp_file_handle; g_return_if_fail (APERTURE_IS_VIEWFINDER (self)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); /* Set up task */ task = g_task_new (self, cancellable, callback, user_data); g_task_set_source_tag (task, aperture_viewfinder_take_picture_async); set_error_if_not_ready (self, &err); get_current_operation (self, &err); if (err) { g_task_return_error (task, err); g_object_unref (task); return; } /* Create a temporary file to save to (camerabin doesn't seem to have a way * to save an image in memory) */ /* we don't actually need it open, so close it immediately */ tmp_file_handle = g_file_open_tmp ("XXXXXX.jpg", &self->take_picture_tmp_file, &err); if (err) { g_task_return_error (task, err); g_object_unref (task); return; } close (tmp_file_handle); self->task_take_picture = task; /* Start the picture taking process */ g_object_set (self->camerabin, "mode", 1, NULL); g_object_set (self->camerabin, "location", self->take_picture_tmp_file, NULL); g_signal_emit_by_name (self->camerabin, "start-capture", NULL); } /** * aperture_viewfinder_take_picture_finish: * @self: an #ApertureViewfinder * @result: a #GAsyncResult provided to callback * @error: a location for a #GError, or %NULL * * Finishes an operation started by * aperture_viewfinder_take_picture_async(). * * Returns: (transfer full): the image that was taken, or %NULL if there was an * error * Since: 0.1 */ GdkPixbuf * aperture_viewfinder_take_picture_finish (ApertureViewfinder *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (APERTURE_IS_VIEWFINDER (self), NULL); g_return_val_if_fail (G_IS_TASK (result), FALSE); return g_task_propagate_pointer (G_TASK (result), error); } /** * aperture_viewfinder_start_recording_to_file: * @self: an #ApertureViewfinder * @file: file path to save the video to * @error: a location for a #GError, or %NULL * * Starts recording a video. The video will be saved to @file. * * Call aperture_viewfinder_stop_recording_async() to stop recording. * * Since: 0.1 */ void aperture_viewfinder_start_recording_to_file (ApertureViewfinder *self, const char *file, GError **error) { GError *err = NULL; g_return_if_fail (APERTURE_IS_VIEWFINDER (self)); g_return_if_fail (file != NULL); set_error_if_not_ready (self, &err); get_current_operation (self, &err); if (err) { g_propagate_error (error, err); return; } self->recording_video = TRUE; g_object_set (self->camerabin, "mode", 2, "location", file, NULL); g_signal_emit_by_name (self->camerabin, "start-capture"); } /** * aperture_viewfinder_stop_recording_async: * @self: an #ApertureViewfinder * @cancellable: (nullable): a #GCancellable * @callback: a #GAsyncReadyCallback to execute upon completion * @user_data: closure data for @callback * * Stop recording video. @callback will be called when this is complete. * * Since: 0.1 */ void aperture_viewfinder_stop_recording_async (ApertureViewfinder *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task = NULL; g_return_if_fail (APERTURE_IS_VIEWFINDER (self)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); /* Set up the task */ task = g_task_new (self, cancellable, callback, user_data); g_task_set_source_tag (task, aperture_viewfinder_stop_recording_async); /* Make sure there's an ongoing recording and that we're not already * stopping it*/ if (!self->recording_video) { g_task_return_new_error (task, APERTURE_MEDIA_CAPTURE_ERROR, APERTURE_MEDIA_CAPTURE_ERROR_NO_RECORDING_TO_STOP, "There is no recording to stop"); } if (self->task_take_video) { g_task_return_new_error (task, APERTURE_MEDIA_CAPTURE_ERROR, APERTURE_MEDIA_CAPTURE_ERROR_OPERATION_IN_PROGRESS, "Operation in progress: Stop recording"); } self->task_take_video = task; g_signal_emit_by_name (self->camerabin, "stop-capture"); } /** * aperture_viewfinder_stop_recording_finish: * @self: an #ApertureViewfinder * @result: a #GAsyncResult provided to callback * @error: a location for a #GError, or %NULL * * Finishes an operation started by aperture_viewfinder_stop_recording_async(). * * Returns: %TRUE if the process succeeded, otherwise %FALSE * Since: 0.1 */ gboolean aperture_viewfinder_stop_recording_finish (ApertureViewfinder *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (APERTURE_IS_VIEWFINDER (self), FALSE); g_return_val_if_fail (G_IS_TASK (result), FALSE); return g_task_propagate_boolean (G_TASK (result), error); } G_DEFINE_QUARK (APERTURE_MEDIA_CAPTURE_ERROR, aperture_media_capture_error); libaperture-0-0.1.0+git20221220/src/aperture-viewfinder.h000066400000000000000000000104051435023540700225070ustar00rootroot00000000000000/* aperture-viewfinder.h * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #if !defined(_LIBAPERTURE_INSIDE) && !defined(_LIBAPERTURE_COMPILATION) #error "Only can be included directly." #endif #include #include "aperture-camera.h" #include "aperture-enums.h" G_BEGIN_DECLS typedef enum { APERTURE_VIEWFINDER_STATE_LOADING, APERTURE_VIEWFINDER_STATE_READY, APERTURE_VIEWFINDER_STATE_NO_CAMERAS, APERTURE_VIEWFINDER_STATE_ERROR, } ApertureViewfinderState; typedef enum { APERTURE_MEDIA_CAPTURE_ERROR_OPERATION_IN_PROGRESS, APERTURE_MEDIA_CAPTURE_ERROR_NO_RECORDING_TO_STOP, APERTURE_MEDIA_CAPTURE_ERROR_CAMERA_DISCONNECTED, APERTURE_MEDIA_CAPTURE_ERROR_INTERRUPTED, APERTURE_MEDIA_CAPTURE_ERROR_NOT_READY, } ApertureMediaCaptureError; #define APERTURE_TYPE_VIEWFINDER (aperture_viewfinder_get_type()) G_DECLARE_FINAL_TYPE (ApertureViewfinder, aperture_viewfinder, APERTURE, VIEWFINDER, GtkBin) ApertureViewfinder *aperture_viewfinder_new (void); void aperture_viewfinder_set_camera (ApertureViewfinder *self, ApertureCamera *camera, GError **error); ApertureCamera *aperture_viewfinder_get_camera (ApertureViewfinder *self); ApertureViewfinderState aperture_viewfinder_get_state (ApertureViewfinder *self); void aperture_viewfinder_set_detect_barcodes (ApertureViewfinder *self, gboolean detect_barcodes); gboolean aperture_viewfinder_get_detect_barcodes (ApertureViewfinder *self); void aperture_viewfinder_take_picture_async (ApertureViewfinder *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GdkPixbuf *aperture_viewfinder_take_picture_finish (ApertureViewfinder *self, GAsyncResult *result, GError **error); void aperture_viewfinder_start_recording_to_file (ApertureViewfinder *self, const char *file, GError **error); void aperture_viewfinder_stop_recording_async (ApertureViewfinder *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean aperture_viewfinder_stop_recording_finish (ApertureViewfinder *self, GAsyncResult *result, GError **error); #define APERTURE_MEDIA_CAPTURE_ERROR (aperture_media_capture_error_quark()) GQuark aperture_media_capture_error_quark (void); G_END_DECLS libaperture-0-0.1.0+git20221220/src/aperture.h000066400000000000000000000020601435023540700203450ustar00rootroot00000000000000/* aperture.h * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include #include #define _LIBAPERTURE_INSIDE #include "aperture-build-info.h" #include "aperture-device-manager.h" #include "aperture-enums.h" #include "aperture-utils.h" #include "aperture-viewfinder.h" #undef _LIBAPERTURE_INSIDE libaperture-0-0.1.0+git20221220/src/devices/000077500000000000000000000000001435023540700177715ustar00rootroot00000000000000libaperture-0-0.1.0+git20221220/src/devices/aperture-device.c000066400000000000000000000074151435023540700232300ustar00rootroot00000000000000/* aperture-device.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include #include "aperture-device.h" #include "private/aperture-camera-private.h" G_DEFINE_TYPE (ApertureDevice, aperture_device, G_TYPE_OBJECT) /* Autodetects what device this is, and return an instance of the correct * #ApertureDevice implementation (or the default, if no supported device is * detected) . */ static ApertureDevice * get_device (void) { return g_object_new (APERTURE_TYPE_DEVICE, NULL); } /* VFUNCS */ static GList * aperture_device_list_cameras_impl (ApertureDevice *self) { return NULL; } static ApertureCamera * aperture_device_get_camera_impl (ApertureDevice *self, GstDevice *device) { return aperture_camera_new (device); } /* INIT */ static void aperture_device_class_init (ApertureDeviceClass *klass) { klass->device_class = "unrecognized"; klass->list_cameras = aperture_device_list_cameras_impl; klass->get_camera = aperture_device_get_camera_impl; } static void aperture_device_init (ApertureDevice *self) { } /* INTERNAL */ /** * PRIVATE:aperture_device_get_instance: * * Gets the singleton instance of #ApertureDevice. * * Returns: (transfer none): the #ApertureDevice */ ApertureDevice * aperture_device_get_instance (void) { static ApertureDevice *device; if (G_UNLIKELY (device == NULL)) { device = get_device (); g_debug ("DEVICE CLASS: %s", APERTURE_DEVICE_GET_CLASS (device)->device_class); } return device; } /** * PRIVATE:aperture_device_get_camera: * @self: an #ApertureDevice * @gst_device: a #GstDevice * * Creates a new #ApertureCamera for the given #GstDevice detected by a * #GstDeviceMonitor. * * Sometimes, a device that is detected by GStreamer should actually be * skipped. In this case, the device implementation will return %NULL, and * the #GstDevice should not be used. * * Implementations should return a custom #ApertureCamera subclass for devices * that they know about, and have special functionality for. For unknown * devices, implementations must chain the call up so that the default * #ApertureCamera is used. * * Returns: (transfer full)(nullable): an #ApertureCamera, or %NULL if the * device should be skipped. */ ApertureCamera * aperture_device_get_camera (ApertureDevice *self, GstDevice *gst_device) { g_return_val_if_fail (APERTURE_IS_DEVICE (self), NULL); g_return_val_if_fail (GST_IS_DEVICE (gst_device), NULL); return APERTURE_DEVICE_GET_CLASS (self)->get_camera (self, gst_device); } /** * PRIVATE:aperture_device_list_cameras: * @self: an #ApertureDevice * * Gets a list of built-in cameras. Only cameras that are not detected by a * #GstDeviceMonitor should be listed here. For devices that are detected, * implementations should use get_camera() to provide an #ApertureCamera. * * Returns: (transfer full)(element-type ApertureCamera): a list of * #ApertureCamera objects */ GList * aperture_device_list_cameras (ApertureDevice *self) { g_return_val_if_fail (APERTURE_IS_DEVICE (self), NULL); return APERTURE_DEVICE_GET_CLASS (self)->list_cameras (self); } libaperture-0-0.1.0+git20221220/src/devices/aperture-device.h000066400000000000000000000030071435023540700232260ustar00rootroot00000000000000/* aperture-device.h * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include #include "aperture-camera.h" G_BEGIN_DECLS #define APERTURE_TYPE_DEVICE (aperture_device_get_type()) G_DECLARE_DERIVABLE_TYPE (ApertureDevice, aperture_device, APERTURE, DEVICE, GObject) struct _ApertureDeviceClass { GObjectClass parent_class; const char *device_class; GList * (* list_cameras) (ApertureDevice *device); ApertureCamera * (* get_camera) (ApertureDevice *device, GstDevice *gst_device); }; ApertureDevice *aperture_device_get_instance (void); GList *aperture_device_list_cameras (ApertureDevice *device); ApertureCamera *aperture_device_get_camera (ApertureDevice *device, GstDevice *gst_device); G_END_DECLS libaperture-0-0.1.0+git20221220/src/meson.build000066400000000000000000000061521435023540700205150ustar00rootroot00000000000000aperture_build_dir = meson.current_build_dir() # List headers and sources libaperture_headers = [ 'aperture.h', 'aperture-camera.h', 'aperture-device-manager.h', 'aperture-utils.h', 'aperture-viewfinder.h' ] libaperture_generated_headers = [] libaperture_sources = files( 'devices/aperture-device.c', 'pipeline/aperture-pipeline-tee.c', 'aperture-camera.c', 'aperture-device-manager.c', 'aperture-utils.c', 'aperture-viewfinder.c', ) libaperture_c_flags = [ '-DG_LOG_DOMAIN="Aperture"', '-D_LIBAPERTURE_COMPILATION', '-Wimplicit-fallthrough', '-Wdeclaration-after-statement', ] libaperture_header_install_dir = get_option('includedir') / aperture_library_name install_headers(libaperture_headers, install_dir: libaperture_header_install_dir) configure_file( output: 'aperture-build-info.h', configuration: { 'APERTURE_MAJOR_VERSION': aperture_major, 'APERTURE_MINOR_VERSION': aperture_minor, 'APERTURE_MICRO_VERSION': aperture_micro, }, install: true, install_dir: libaperture_header_install_dir ) libaperture_generated_headers += ['aperture-build-info.h'] libaperture_enum_headers = files( 'aperture-utils.h', 'aperture-viewfinder.h', ) libaperture_enums = gnome.mkenums_simple('aperture-enums', sources: libaperture_enum_headers, header_prefix: ''' #if !defined(_LIBAPERTURE_INSIDE) && !defined(_LIBAPERTURE_COMPILATION) #error "Only can be included directly." #endif''', install_header: true, install_dir: libaperture_header_install_dir, ) libaperture_sources += libaperture_enums[0] libaperture_headers += libaperture_enums[1] libaperture_generated_headers += libaperture_enums[1] # Create the library libaperture_lib = shared_library(aperture_library_name, libaperture_sources, libaperture_headers, dependencies: [ libaperture_deps ], c_args: libaperture_c_flags, version: aperture_api_version, install: true, install_dir: true, ) # Create a dependency to use later libaperture_dep = declare_dependency( sources: libaperture_headers, link_with: libaperture_lib, include_directories: include_directories('.', '..'), dependencies: libaperture_deps, ) if get_option('gir') # GIR/introspection stuff libaperture_gir = gnome.generate_gir(libaperture_lib, sources: libaperture_headers + libaperture_sources, nsversion: aperture_api_version, export_packages: aperture_library_name, namespace: 'Aperture', header: 'aperture.h', symbol_prefix: 'aperture', identifier_prefix: 'Aperture', link_with: libaperture_lib, includes: ['Gst-1.0', 'Gtk-3.0'], install: true, extra_args: [ '-D_LIBAPERTURE_COMPILATION', ] ) if get_option('vapi') gnome.generate_vapi(aperture_library_name, sources: libaperture_gir[0], packages: [ 'gtk+-3.0', 'gio-2.0', 'gstreamer-1.0' ], install: true, metadata_dirs: [meson.current_source_dir()], ) endif endif # Pkg-config file pkgconfig.generate(libaperture_lib, name: 'Aperture', description: 'The Aperture camera widget library for GTK', subdirs: aperture_library_name, filebase: aperture_library_name, requires: libaperture_deps, ) libaperture-0-0.1.0+git20221220/src/pipeline/000077500000000000000000000000001435023540700201545ustar00rootroot00000000000000libaperture-0-0.1.0+git20221220/src/pipeline/aperture-pipeline-tee.c000066400000000000000000000120141435023540700245230ustar00rootroot00000000000000/* aperture-pipeline-tee.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include "aperture-pipeline-tee.h" struct _AperturePipelineTee { GstBin parent_instance; GstElement *tee; GHashTable *queues; }; G_DEFINE_TYPE (AperturePipelineTee, aperture_pipeline_tee, GST_TYPE_BIN) /* Used for transferring data into a pad probe */ typedef struct { AperturePipelineTee *self; GstElement *branch; GstElement *queue; GstPad *tee_pad; } RemoveBranchProbeData; static void pad_probe_async_func (GstElement *element, gpointer user_data) { RemoveBranchProbeData *probe = user_data; gst_element_release_request_pad (probe->self->tee, probe->tee_pad); gst_element_set_state (probe->branch, GST_STATE_NULL); gst_element_set_state (probe->queue, GST_STATE_NULL); gst_bin_remove (GST_BIN (probe->self), probe->queue); gst_bin_remove (GST_BIN (probe->self), probe->branch); } static GstPadProbeReturn pad_probe (GstPad *pad, GstPadProbeInfo *info, gpointer user_data) { RemoveBranchProbeData *probe = user_data; gst_element_call_async (probe->self->tee, pad_probe_async_func, user_data, g_free); return GST_PAD_PROBE_REMOVE; } /* VFUNCS */ static void aperture_pipeline_tee_finalize (GObject *object) { AperturePipelineTee *self = APERTURE_PIPELINE_TEE (object); g_hash_table_unref (self->queues); G_OBJECT_CLASS (aperture_pipeline_tee_parent_class)->finalize (object); } /* INIT */ static void aperture_pipeline_tee_class_init (AperturePipelineTeeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = aperture_pipeline_tee_finalize; } static void aperture_pipeline_tee_init (AperturePipelineTee *self) { g_autoptr(GstPad) pad = NULL; GstPad *ghost_pad; self->queues = g_hash_table_new (NULL, NULL); self->tee = gst_element_factory_make ("tee", NULL); gst_bin_add (GST_BIN (self), self->tee); pad = gst_element_get_static_pad (self->tee, "sink"); ghost_pad = gst_ghost_pad_new ("sink", pad); gst_pad_set_active (ghost_pad, TRUE); gst_element_add_pad (GST_ELEMENT (self), ghost_pad); } /* PUBLIC */ /** * PRIVATE:aperture_pipeline_tee_new: * * Creates a new #AperturePipelineTee. * * Returns: (transfer full): a new #AperturePipelineTee */ AperturePipelineTee * aperture_pipeline_tee_new (void) { return g_object_new (APERTURE_TYPE_PIPELINE_TEE, NULL); } /** * PRIVATE:aperture_pipeline_tee_add_branch: * @self: an #AperturePipelineTee * @branch: (transfer full): an element to add to the tee * * Adds an element to the tee. * * A queue will be inserted between the tee and the element, and element states * are synced automatically. */ void aperture_pipeline_tee_add_branch (AperturePipelineTee *self, GstElement *branch) { GstElement *queue; g_autoptr(GstPad) tee_pad = NULL; g_autoptr(GstPad) queue_pad = NULL; g_return_if_fail (APERTURE_IS_PIPELINE_TEE (self)); g_return_if_fail (GST_IS_ELEMENT (branch)); queue = gst_element_factory_make ("queue", NULL); g_hash_table_insert (self->queues, branch, queue); gst_bin_add_many (GST_BIN (self), queue, branch, NULL); gst_element_link (queue, branch); tee_pad = gst_element_get_request_pad (self->tee, "src_%u"); queue_pad = gst_element_get_static_pad (queue, "sink"); gst_pad_link (tee_pad, queue_pad); gst_element_sync_state_with_parent (queue); gst_element_sync_state_with_parent (branch); } /** * PRIVATE:aperture_pipeline_tee_remove_branch: * @self: an #AperturePipelineTee * @branch: the element to remove * * Removes an element from the tee. */ void aperture_pipeline_tee_remove_branch (AperturePipelineTee *self, GstElement *branch) { GstElement *queue; RemoveBranchProbeData *data; g_autoptr(GstPad) tee_pad = NULL; g_autoptr(GstPad) queue_pad = NULL; g_return_if_fail (APERTURE_IS_PIPELINE_TEE (self)); g_return_if_fail (GST_IS_ELEMENT (branch)); g_return_if_fail (g_hash_table_contains (self->queues, branch)); queue = g_hash_table_lookup (self->queues, branch); g_hash_table_remove (self->queues, branch); queue_pad = gst_element_get_static_pad (queue, "sink"); tee_pad = gst_pad_get_peer (queue_pad); data = g_new (RemoveBranchProbeData, 1); data->self = self; data->queue = queue; data->branch = branch; data->tee_pad = tee_pad; gst_pad_add_probe (tee_pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, pad_probe, data, NULL); } libaperture-0-0.1.0+git20221220/src/pipeline/aperture-pipeline-tee.h000066400000000000000000000023741435023540700245400ustar00rootroot00000000000000/* aperture-pipeline-tee.h * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include G_BEGIN_DECLS #define APERTURE_TYPE_PIPELINE_TEE (aperture_pipeline_tee_get_type()) G_DECLARE_FINAL_TYPE (AperturePipelineTee, aperture_pipeline_tee, APERTURE, PIPELINE_TEE, GstBin) AperturePipelineTee *aperture_pipeline_tee_new (); void aperture_pipeline_tee_add_branch (AperturePipelineTee *self, GstElement *branch); void aperture_pipeline_tee_remove_branch (AperturePipelineTee *self, GstElement *branch); G_END_DECLS libaperture-0-0.1.0+git20221220/src/private/000077500000000000000000000000001435023540700200215ustar00rootroot00000000000000libaperture-0-0.1.0+git20221220/src/private/aperture-camera-private.h000066400000000000000000000035071435023540700247240ustar00rootroot00000000000000/* aperture-camera-private.h * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include #include "aperture-camera.h" G_BEGIN_DECLS struct _ApertureCameraClass { GObjectClass parent_class; void (* do_flash_async) (ApertureCamera *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean (* do_flash_finish) (ApertureCamera *device, GAsyncResult *result, GError **error); void (* set_torch) (ApertureCamera *device, gboolean flash); GstElement * (* get_source_element) (ApertureCamera *self, GstElement *previous); }; ApertureCamera *aperture_camera_new (GstDevice *gst_device); GstDevice *aperture_camera_get_gst_device (ApertureCamera *camera); GstElement *aperture_camera_get_source_element (ApertureCamera *camera, GstElement *previous_element); G_END_DECLS libaperture-0-0.1.0+git20221220/src/private/aperture-private.h000066400000000000000000000020171435023540700234710ustar00rootroot00000000000000/* aperture-private.h * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include #include "aperture-utils.h" G_BEGIN_DECLS void aperture_private_ensure_initialized (void); ApertureBarcode aperture_barcode_type_from_string (const char *string); G_END_DECLS libaperture-0-0.1.0+git20221220/tests/000077500000000000000000000000001435023540700167225ustar00rootroot00000000000000libaperture-0-0.1.0+git20221220/tests/coverage.sh000077500000000000000000000015001435023540700210500ustar00rootroot00000000000000#!/bin/sh echo "Source root: $MESON_SOURCE_ROOT" echo "Build root: $MESON_BUILD_ROOT" DIR=$MESON_BUILD_ROOT/meson-logs/test-coverage mkdir -p $DIR if [ -x "$(command -v gcovr)" ]; then gcovr -r $MESON_SOURCE_ROOT $MESON_BUILD_ROOT -f "../src/*" --html --html-details --print-summary -o $DIR/index.html gcovr -r $MESON_SOURCE_ROOT $MESON_BUILD_ROOT -f "../src/*" --xml -o $DIR/coverage.xml else TRACEFILE=$DIR/coverage.info lcov --directory $MESON_BUILD_ROOT --capture --initial -o $TRACEFILE.initial lcov --directory $MESON_BUILD_ROOT --capture -o $TRACEFILE.run lcov -a $TRACEFILE.initial -a $TRACEFILE.run -o $TRACEFILE lcov --extract $TRACEFILE $MESON_SOURCE_ROOT/src/\* -o $TRACEFILE genhtml --prefix $MESON_SOURCE_ROOT --title "Code Coverage" --legend --show-details --branch-coverage -o $DIR $TRACEFILE fi libaperture-0-0.1.0+git20221220/tests/data/000077500000000000000000000000001435023540700176335ustar00rootroot00000000000000libaperture-0-0.1.0+git20221220/tests/data/helloworld.png000066400000000000000000000012261435023540700225150ustar00rootroot00000000000000PNG  IHDRddpT pHYsysHIDATxj1 C=w5BJm`NVloa/TvS)@2L)@4yQR@s}?@H~)moHY`HYO_`);@;:@)^U|~U'醪 wBR@H~2ri eH6SLQ?Q^ H2گ@Oj?:ą~߭$w=~K5[_ 2Wu jy,@jybK/iUO? t xTj] FR& bjv?w @"M@Қ?8u\ >P݆ q$mpi 郻_ZH ܁HՁRv: vyjJ yj~_: ڽT m@2L)@2LUѱelIENDB`libaperture-0-0.1.0+git20221220/tests/data/meson.build000066400000000000000000000001601435023540700217720ustar00rootroot00000000000000test_gresources = gnome.compile_resources('aperture-resources', 'tests.gresource.xml', c_name: 'aperture' ) libaperture-0-0.1.0+git20221220/tests/data/quadrants.png000066400000000000000000000007161435023540700223470ustar00rootroot00000000000000PNG  IHDR>a pHYsodtEXtSoftwarewww.inkscape.org<[IDATx A0+ )|rp+\ Wp+\ Wp+\ Wp+\ Wp+\ Wp+\ Wp+\ Wp+\ W7{=ֻp+\ Wp+\ Wp+\ Wp+\ Wp+\ Wp+\ Wp+\ Wp+\ Ws{(E\IENDB`libaperture-0-0.1.0+git20221220/tests/data/tests.gresource.xml000066400000000000000000000002631435023540700235150ustar00rootroot00000000000000 helloworld.png quadrants.png libaperture-0-0.1.0+git20221220/tests/dummy-device-provider.c000066400000000000000000000131311435023540700233050ustar00rootroot00000000000000/* dummy-device-provider.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include "dummy-device-provider.h" #include "dummy-device.h" struct _DummyDeviceProvider { GstDeviceProvider parent_instance; GList *devices; }; G_DEFINE_TYPE (DummyDeviceProvider, dummy_device_provider, GST_TYPE_DEVICE_PROVIDER) enum { PROP_0, N_PROPS }; static GParamSpec *properties [N_PROPS]; DummyDevice *dummy_device_provider_add (DummyDeviceProvider *self); void dummy_device_provider_remove (DummyDeviceProvider *self); /* VFUNCS */ static void dummy_device_provider_finalize (GObject *object) { DummyDeviceProvider *self = (DummyDeviceProvider *)object; g_list_free_full (self->devices, g_object_unref); G_OBJECT_CLASS (dummy_device_provider_parent_class)->finalize (object); } static GList * dummy_device_provider_probe (GstDeviceProvider *self) { return g_list_copy_deep (self->devices, (GCopyFunc) g_object_ref, NULL); } static gboolean dummy_device_provider_start (GstDeviceProvider *provider) { return TRUE; } static void dummy_device_provider_stop (GstDeviceProvider *provider) { DummyDeviceProvider *self = DUMMY_DEVICE_PROVIDER (provider); while (self->devices) { dummy_device_provider_remove (self); } } /* INIT */ static void dummy_device_provider_class_init (DummyDeviceProviderClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GstDeviceProviderClass *device_provider_class = GST_DEVICE_PROVIDER_CLASS (klass); object_class->finalize = dummy_device_provider_finalize; device_provider_class->probe = dummy_device_provider_probe; device_provider_class->start = dummy_device_provider_start; device_provider_class->stop = dummy_device_provider_stop; gst_device_provider_class_set_static_metadata (device_provider_class, "Dummy device provider", "Source/Video", "Dummy device provider for unit tests", "James Westman "); } static void dummy_device_provider_init (DummyDeviceProvider *self) { } /* PUBLIC */ /** * PRIVATE:dummy_device_provider_new: * * Creates a new #DummyDeviceProvider. * * Returns: (transfer full): a new #DummyDeviceProvider */ DummyDeviceProvider * dummy_device_provider_new (void) { return g_object_new (DUMMY_TYPE_DEVICE_PROVIDER, NULL); } /** * PRIVATE:dummy_device_provider_add: * @self: a #DummyDeviceProvider * * Adds a new device to the dummy provider, causing a device added message to * be posted on the device monitor's bus. * * Note that the device manager starts with one device by default, so this is * unnecessary unless you're testing adding and removing devices, or testing * multiple devices. * * Returns: (transfer none): the new device */ DummyDevice * dummy_device_provider_add (DummyDeviceProvider *self) { DummyDevice *device; g_return_val_if_fail (DUMMY_IS_DEVICE_PROVIDER (self), NULL); device = dummy_device_new (); gst_device_provider_device_add (GST_DEVICE_PROVIDER (self), GST_DEVICE (device)); self->devices = g_list_prepend (self->devices, device); return device; } /** * PRIVATE:dummy_device_provider_remove: * @self: a #DummyDeviceProvider * * Removes a device from the device provider, causing a device removed message * to be posted on the device monitor's bus. Devices are removed opposite the * order they were added (in a stack structure). */ void dummy_device_provider_remove (DummyDeviceProvider *self) { GList *first; g_return_if_fail (DUMMY_IS_DEVICE_PROVIDER (self)); if (!self->devices) { return; } first = g_list_first (self->devices); gst_device_provider_device_remove (GST_DEVICE_PROVIDER (self), GST_DEVICE (first->data)); self->devices = g_list_delete_link (first, first); } /** * PRIVATE:dummy_device_provider_register: * * Registers the dummy device provider. * * This should only be called once. */ void dummy_device_provider_register (void) { g_autoptr(GstDeviceProvider) dummy = NULL; g_autolist(GstDeviceProviderFactory) providers = NULL; GList *i; /* Register the dummy provider */ gst_device_provider_register (NULL, "dummy-device-provider", G_MAXINT, DUMMY_TYPE_DEVICE_PROVIDER); /* Hide all the other providers so we're only working with our dummy devices. * This allows us to test things like no-cameras state, even on machines * that have real cameras attached. */ dummy = gst_device_provider_factory_get_by_name ("dummy-device-provider"); providers = gst_device_provider_factory_list_get_device_providers (GST_RANK_NONE); for (i = providers; i != NULL; i = i->next) { g_autofree char *name = gst_object_get_name (GST_OBJECT (i->data)); if (g_strcmp0 (name, "dummy-device-provider") != 0) { g_print ("%s\n", name); gst_device_provider_hide_provider (dummy, name); } } } libaperture-0-0.1.0+git20221220/tests/dummy-device-provider.h000066400000000000000000000025271435023540700233210ustar00rootroot00000000000000/* dummy-device-provider.h * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include #include "dummy-device.h" G_BEGIN_DECLS #define DUMMY_TYPE_DEVICE_PROVIDER (dummy_device_provider_get_type()) G_DECLARE_FINAL_TYPE (DummyDeviceProvider, dummy_device_provider, DUMMY, DEVICE_PROVIDER, GstDeviceProvider) DummyDeviceProvider *dummy_device_provider_new (void); DummyDevice *dummy_device_provider_add (DummyDeviceProvider *self); void dummy_device_provider_remove (DummyDeviceProvider *self); void dummy_device_provider_register (void); G_END_DECLS libaperture-0-0.1.0+git20221220/tests/dummy-device.c000066400000000000000000000122171435023540700214610ustar00rootroot00000000000000/* dummy-device.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include #include #include "dummy-device.h" struct _DummyDevice { GstDevice parent_instance; gchar *image; }; G_DEFINE_TYPE (DummyDevice, dummy_device, GST_TYPE_DEVICE) enum { PROP_0, PROP_IMAGE, N_PROPS }; static GParamSpec *props[N_PROPS]; static GstElement * create_image_source (const gchar *image) { GstElement *bin = gst_bin_new (NULL); g_autoptr(GstPad) pad = NULL; GstPad *ghost_pad; GBytes *buffer_bytes; gsize buf_size; GstBuffer *buffer; gpointer buf_raw; GError *error = NULL; GstElement *appsrc; GstElement *decodebin; GstElement *imagefreeze; appsrc = gst_element_factory_make ("appsrc", NULL); decodebin = gst_element_factory_make ("pngdec", NULL); imagefreeze = gst_element_factory_make ("imagefreeze", NULL); buffer_bytes = g_resources_lookup_data (image, G_RESOURCE_LOOKUP_FLAGS_NONE, &error); g_assert_no_error (error); buf_raw = g_bytes_unref_to_data (buffer_bytes, &buf_size); buffer = gst_buffer_new_wrapped (buf_raw, buf_size); gst_app_src_push_buffer (GST_APP_SRC (appsrc), buffer); gst_bin_add_many (GST_BIN (bin), appsrc, decodebin, imagefreeze, NULL); gst_element_link_many (appsrc, decodebin, imagefreeze, NULL); pad = gst_element_get_static_pad (imagefreeze, "src"); ghost_pad = gst_ghost_pad_new ("src", pad); gst_pad_set_active (ghost_pad, TRUE); gst_element_add_pad (bin, ghost_pad); return bin; } /* VFUNCS */ static void dummy_device_finalize (GObject *object) { DummyDevice *self = (DummyDevice *)object; g_free (self->image); G_OBJECT_CLASS (dummy_device_parent_class)->finalize (object); } static void dummy_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { DummyDevice *self = DUMMY_DEVICE (object); switch (prop_id) { case PROP_IMAGE: g_value_set_string (value, dummy_device_get_image (self)); default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void dummy_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { DummyDevice *self = DUMMY_DEVICE (object); switch (prop_id) { case PROP_IMAGE: dummy_device_set_image (self, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static GstElement * dummy_device_create_element (GstDevice *device, const char *name) { DummyDevice *self; GstElement *element = NULL; g_return_val_if_fail (DUMMY_IS_DEVICE (device), NULL); self = DUMMY_DEVICE (device); if (self->image) { return create_image_source (self->image); } else { element = gst_element_factory_make ("videotestsrc", NULL); g_object_set (element, "pattern", 18, NULL); return element; } } /* INIT */ static void dummy_device_class_init (DummyDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GstDeviceClass *device_class = GST_DEVICE_CLASS (klass); object_class->finalize = dummy_device_finalize; object_class->get_property = dummy_device_get_property; object_class->set_property = dummy_device_set_property; device_class->create_element = dummy_device_create_element; props [PROP_IMAGE] = g_param_spec_string ("image", "Pixbuf", "Image to use instead of test stream", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); } static void dummy_device_init (DummyDevice *self) { } /* PUBLIC */ /** * PRIVATE:dummy_device_new: * * Creates a new #DummyDevice. * * Returns: (transfer full): a new #DummyDevice */ DummyDevice * dummy_device_new (void) { GstCaps *caps = gst_caps_new_any (); return g_object_new (DUMMY_TYPE_DEVICE, "caps", caps, "device-class", "Source/Video", "display-name", "Dummy Camera", NULL); } const char * dummy_device_get_image (DummyDevice *self) { return self->image; } void dummy_device_set_image (DummyDevice *self, const char *image) { g_return_if_fail (DUMMY_IS_DEVICE (self)); g_clear_pointer (&self->image, g_free); self->image = g_strdup (image); } libaperture-0-0.1.0+git20221220/tests/dummy-device.h000066400000000000000000000022741435023540700214700ustar00rootroot00000000000000/* dummy-device.h * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include G_BEGIN_DECLS #define DUMMY_TYPE_DEVICE (dummy_device_get_type()) G_DECLARE_FINAL_TYPE (DummyDevice, dummy_device, DUMMY, DEVICE, GstDevice) DummyDevice *dummy_device_new (void); const char *dummy_device_get_image (DummyDevice *self); void dummy_device_set_image (DummyDevice *self, const char *image); G_END_DECLS libaperture-0-0.1.0+git20221220/tests/main.c000066400000000000000000000025761435023540700200240ustar00rootroot00000000000000/* test-utils.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include #include #include "private/aperture-private.h" #include "dummy-device-provider.h" void add_barcodes_tests (void); void add_camera_tests (void); void add_device_manager_tests (void); void add_viewfinder_tests (void); int main (int argc, char **argv) { aperture_init (&argc, &argv); gtk_init (&argc, &argv); g_test_init (&argc, &argv, NULL); /* Set up the dummy device provider in GStreamer */ dummy_device_provider_register (); add_barcodes_tests (); add_camera_tests (); add_device_manager_tests (); add_viewfinder_tests (); return g_test_run (); } libaperture-0-0.1.0+git20221220/tests/meson.build000066400000000000000000000022531435023540700210660ustar00rootroot00000000000000test_env = [ 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()), 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), ] test_sources = files( 'dummy-device.c', 'dummy-device-provider.c', 'main.c', 'test-barcodes.c', 'test-camera.c', 'test-device-manager.c', 'test-viewfinder.c', 'utils.c', ) subdir('data') test_sources += test_gresources test_c_args = [] if get_option('barcode_tests_skippable') test_c_args += '-DBARCODE_TESTS_SKIPPABLE' endif test_executable = executable('aperture-tests', test_sources, dependencies: libaperture_dep, c_args: test_c_args, install: true, ) test('aperture-tests', test_executable, env: test_env, ) # We could use the built-in, default coverage reports. However, doing it # manually here allows us to filter the files, so that only files under src/ # are included. # Otherwise, the tests themselves, the demo project, and some generated source # files would skew the results. # This could also be done using a gcovr.cfg file, but meson will use lcov if # it's installed, which does not obey that file. if get_option('b_coverage') test_coverage = run_target('test-coverage', command: ['coverage.sh']) endif libaperture-0-0.1.0+git20221220/tests/test-barcodes.c000066400000000000000000000071711435023540700216330ustar00rootroot00000000000000/* test-barcodes.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include #include #include "utils.h" #include "dummy-device-provider.h" static void test_barcodes_enum () { g_assert_cmpint (aperture_barcode_type_from_string ("COMPOSITE"), ==, APERTURE_BARCODE_COMPOSITE); g_assert_cmpint (aperture_barcode_type_from_string ("DataBar"), ==, APERTURE_BARCODE_DATABAR); g_assert_cmpint (aperture_barcode_type_from_string ("QR-Code"), ==, APERTURE_BARCODE_QR); g_assert_cmpint (aperture_barcode_type_from_string ("I2/5"), ==, APERTURE_BARCODE_I25); g_assert_cmpint (aperture_barcode_type_from_string ("three zebras walking into a bar"), ==, APERTURE_BARCODE_UNKNOWN); } static void test_barcodes_enabled () { g_test_summary ("Test that barcode detection is enabled in the test environment"); #ifdef BARCODE_TESTS_SKIPPABLE if (!aperture_is_barcode_detection_enabled ()) { g_test_skip ("Skipping test that requires barcode detection, because it is not available"); return; } #endif g_assert_true (aperture_is_barcode_detection_enabled ()); } static void barcode_detected_cb (TestUtilsCallback *cb, ApertureBarcode barcode, char *data) { g_assert_cmpstr (data, ==, "hello world"); g_assert_cmpint (barcode, ==, APERTURE_BARCODE_QR); testutils_callback_call (cb); } static void test_barcodes_detection () { g_autoptr(DummyDeviceProvider) provider = DUMMY_DEVICE_PROVIDER (gst_device_provider_factory_get_by_name ("dummy-device-provider")); ApertureViewfinder *viewfinder; GtkWidget *window; TestUtilsCallback detected_callback; DummyDevice *device; #ifdef BARCODE_TESTS_SKIPPABLE if (!aperture_is_barcode_detection_enabled ()) { g_test_skip ("Skipping test that requires barcode detection, because it is not available"); return; } #endif testutils_callback_init (&detected_callback); device = dummy_device_provider_add (provider); dummy_device_set_image (device, "/aperture/helloworld.png"); viewfinder = aperture_viewfinder_new (); g_signal_connect_swapped (viewfinder, "barcode-detected", G_CALLBACK (barcode_detected_cb), &detected_callback); aperture_viewfinder_set_detect_barcodes (viewfinder, TRUE); /* make sure that worked */ g_assert_true (aperture_viewfinder_get_detect_barcodes (viewfinder)); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (viewfinder)); gtk_widget_show_all (window); testutils_callback_assert_called (&detected_callback, 1000); aperture_viewfinder_set_detect_barcodes (viewfinder, FALSE); aperture_viewfinder_set_detect_barcodes (viewfinder, TRUE); testutils_callback_assert_called (&detected_callback, 1000); gtk_widget_destroy (window); } void add_barcodes_tests () { g_test_add_func ("/barcodes/enum", test_barcodes_enum); g_test_add_func ("/barcodes/enabled", test_barcodes_enabled); g_test_add_func ("/barcodes/detection", test_barcodes_detection); } libaperture-0-0.1.0+git20221220/tests/test-camera.c000066400000000000000000000042121435023540700212720ustar00rootroot00000000000000/* test-camera.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include #include #include "utils.h" #include "dummy-device-provider.h" static void on_camera_flash (ApertureCamera *source, GAsyncResult *res, TestUtilsCallback *callback) { g_autoptr(GError) err = NULL; gboolean result = aperture_camera_do_flash_finish (source, res, &err); g_assert_no_error (err); // default implementation returns %FALSE since there's no flash to enable g_assert_false (result); testutils_callback_call (callback); } static void test_camera_flash () { g_autoptr(ApertureDeviceManager) manager = aperture_device_manager_get_instance (); g_autoptr(DummyDeviceProvider) provider = DUMMY_DEVICE_PROVIDER (gst_device_provider_factory_get_by_name ("dummy-device-provider")); g_autoptr(ApertureCamera) camera = NULL; TestUtilsCallback flash_callback; testutils_callback_init (&flash_callback); dummy_device_provider_add (provider); testutils_wait_for_device_change (manager); camera = aperture_device_manager_next_camera (manager, NULL); g_assert_nonnull (camera); aperture_camera_do_flash_async (camera, NULL, (GAsyncReadyCallback) on_camera_flash, &flash_callback); testutils_callback_assert_called (&flash_callback, 1000); aperture_camera_set_torch (camera, TRUE); aperture_camera_set_torch (camera, FALSE); } void add_camera_tests () { g_test_add_func ("/camera/flash", test_camera_flash); } libaperture-0-0.1.0+git20221220/tests/test-device-manager.c000066400000000000000000000161761435023540700227250ustar00rootroot00000000000000/* test-device-manager.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include #include #include "private/aperture-camera-private.h" #include "dummy-device-provider.h" #include "utils.h" static void test_device_manager_refcounting () { /* Test that managers are destroyed when they are not in use anymore, and * that if a device manager does exist, it is returned rather than a new * instance */ ApertureDeviceManager *manager1 = aperture_device_manager_get_instance (); ApertureDeviceManager *manager2 = aperture_device_manager_get_instance (); ApertureDeviceManager *manager3 = NULL; g_assert_nonnull (manager1); g_assert_nonnull (manager2); /* make sure the managers are the same, since the first was still active * when the second was requested */ g_assert_true (manager1 == manager2); /* Set some data on the manager, so that later we can make sure we have a * different manager object */ g_object_set_data (G_OBJECT (manager1), "test", "Hello, world!"); g_assert_cmpstr (g_object_get_data (G_OBJECT (manager1), "test"), ==, "Hello, world!"); g_object_unref (manager1); g_assert_finalize_object (G_OBJECT (manager2)); manager3 = aperture_device_manager_get_instance (); g_assert_nonnull (manager3); /* the old device manager was destroyed. make sure we got a new one */ g_assert_null (g_object_get_data (G_OBJECT (manager3), "test")); g_assert_finalize_object (G_OBJECT (manager3)); } /* * Determines whether the device manager contains the test device. */ static gboolean manager_contains_test_device (ApertureDeviceManager *manager) { g_autoptr(ApertureCamera) camera = NULL; int num_dummy_cameras = 0; int num_cameras = aperture_device_manager_get_num_cameras (manager); int i; for (i = 0; i < num_cameras; i ++) { g_set_object (&camera, aperture_device_manager_get_camera (manager, i)); if (APERTURE_IS_CAMERA (camera)) { g_autoptr(GstElement) element = aperture_camera_get_source_element (camera, NULL); if (g_str_has_prefix (gst_object_get_name (GST_OBJECT (element)), "videotestsrc")) { num_dummy_cameras ++; } } } return num_dummy_cameras == 1; } static void test_device_manager_works () { /* Test that device managers properly list devices obtained from GStreamer */ g_autoptr(ApertureDeviceManager) manager = NULL; g_autoptr(DummyDeviceProvider) provider = DUMMY_DEVICE_PROVIDER (gst_device_provider_factory_get_by_name ("dummy-device-provider")); int num_cameras; dummy_device_provider_add (provider); manager = aperture_device_manager_get_instance (); g_object_get (manager, "num-cameras", &num_cameras, NULL); g_assert_cmpint (num_cameras, ==, 1); /* make sure one of the devices is the dummy one */ g_assert_true (manager_contains_test_device (manager)); } static void test_device_manager_monitoring () { g_test_summary ("Test that the ::camera-added and ::camera-removed signals work"); g_autoptr(ApertureDeviceManager) manager = aperture_device_manager_get_instance (); g_autoptr(DummyDeviceProvider) provider = DUMMY_DEVICE_PROVIDER (gst_device_provider_factory_get_by_name ("dummy-device-provider")); TestUtilsCallback added_callback, removed_callback; testutils_callback_init (&added_callback); testutils_callback_init (&removed_callback); g_signal_connect_swapped (manager, "camera-added", G_CALLBACK (testutils_callback_call), &added_callback); g_signal_connect_swapped (manager, "camera-removed", G_CALLBACK (testutils_callback_call), &removed_callback); dummy_device_provider_add (provider); testutils_callback_assert_called (&added_callback, 1000); g_assert_true (manager_contains_test_device (manager)); g_assert_cmpint (aperture_device_manager_get_num_cameras (manager), ==, 1); dummy_device_provider_remove (provider); testutils_callback_assert_called (&removed_callback, 1000); g_assert_false (manager_contains_test_device (manager)); g_assert_cmpint (aperture_device_manager_get_num_cameras (manager), ==, 0); } static void test_device_manager_next_camera () { g_autoptr(ApertureDeviceManager) manager = aperture_device_manager_get_instance (); g_autoptr(DummyDeviceProvider) provider = DUMMY_DEVICE_PROVIDER (gst_device_provider_factory_get_by_name ("dummy-device-provider")); g_autoptr(ApertureCamera) camera0A = NULL; g_autoptr(ApertureCamera) camera0B = NULL; g_autoptr(ApertureCamera) camera1A = NULL; g_autoptr(ApertureCamera) camera1B = NULL; g_autoptr(ApertureCamera) camera1C = NULL; g_autoptr(ApertureCamera) camera1D = NULL; g_autoptr(ApertureCamera) camera2A = NULL; int num_cameras; /* Zero cameras test cases */ num_cameras = aperture_device_manager_get_num_cameras (manager); g_assert_cmpint (num_cameras, ==, 0); camera0A = aperture_device_manager_next_camera (manager, NULL); g_assert_null (camera0A); /* One camera test cases */ dummy_device_provider_add (provider); testutils_wait_for_device_change (manager); num_cameras = aperture_device_manager_get_num_cameras (manager); g_assert_cmpint (num_cameras, ==, 1); camera1A = aperture_device_manager_next_camera (manager, NULL); g_assert_nonnull (camera1A); camera1B = aperture_device_manager_next_camera (manager, NULL); g_assert_true (camera1A == camera1B); /* Two cameras test cases */ dummy_device_provider_add (provider); testutils_wait_for_device_change (manager); num_cameras = aperture_device_manager_get_num_cameras (manager); g_assert_cmpint (num_cameras, ==, 2); camera2A = aperture_device_manager_next_camera (manager, camera1A); g_assert_true (camera1A != camera2A); camera1C = aperture_device_manager_next_camera (manager, camera2A); g_assert_true (camera1C == camera1A); /* Removed camera test cases */ dummy_device_provider_remove (provider); testutils_wait_for_device_change (manager); camera1D = aperture_device_manager_next_camera (manager, camera2A); g_assert_true (camera1D == camera1A); dummy_device_provider_remove (provider); testutils_wait_for_device_change (manager); camera0B = aperture_device_manager_next_camera (manager, camera1A); g_assert_null (camera0B); } void add_device_manager_tests () { g_test_add_func ("/device-manager/refcounting", test_device_manager_refcounting); g_test_add_func ("/device-manager/works", test_device_manager_works); g_test_add_func ("/device-manager/monitoring", test_device_manager_monitoring); g_test_add_func ("/device-manager/next-camera", test_device_manager_next_camera); } libaperture-0-0.1.0+git20221220/tests/test-viewfinder.c000066400000000000000000000177511435023540700222200ustar00rootroot00000000000000/* test-viewfinder.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include #include #include "dummy-device-provider.h" #include "utils.h" static void test_viewfinder_no_camera_state () { g_autoptr(ApertureDeviceManager) manager = aperture_device_manager_get_instance (); g_autoptr(DummyDeviceProvider) provider = DUMMY_DEVICE_PROVIDER (gst_device_provider_factory_get_by_name ("dummy-device-provider")); g_autoptr(ApertureViewfinder) viewfinder = NULL; /* make sure there are no cameras to start */ g_assert_cmpint (aperture_device_manager_get_num_cameras (manager), ==, 0); /* if there are no cameras, new viewfinders should be in NO_CAMERAS state */ viewfinder = aperture_viewfinder_new (); g_object_ref_sink (viewfinder); g_assert_cmpint (aperture_viewfinder_get_state (viewfinder), ==, APERTURE_VIEWFINDER_STATE_NO_CAMERAS); g_assert_null (aperture_viewfinder_get_camera (viewfinder)); dummy_device_provider_add (provider); testutils_wait_for_device_change (manager); g_assert_cmpint (aperture_viewfinder_get_state (viewfinder), ==, APERTURE_VIEWFINDER_STATE_READY); g_assert_nonnull (aperture_viewfinder_get_camera (viewfinder)); dummy_device_provider_remove (provider); testutils_wait_for_device_change (manager); g_assert_cmpint (aperture_viewfinder_get_state (viewfinder), ==, APERTURE_VIEWFINDER_STATE_NO_CAMERAS); g_assert_null (aperture_viewfinder_get_camera (viewfinder)); } static void on_picture_taken (ApertureViewfinder *source, GAsyncResult *res, TestUtilsCallback *callback) { g_autoptr(GError) err = NULL; g_autoptr(GdkPixbuf) pixbuf = NULL; pixbuf = aperture_viewfinder_take_picture_finish (source, res, &err); g_assert_no_error (err); testutils_assert_quadrants_pixbuf (pixbuf); testutils_callback_call (callback); } static void test_viewfinder_take_picture () { g_autoptr(ApertureDeviceManager) manager = aperture_device_manager_get_instance (); g_autoptr(DummyDeviceProvider) provider = DUMMY_DEVICE_PROVIDER (gst_device_provider_factory_get_by_name ("dummy-device-provider")); ApertureViewfinder *viewfinder; GtkWidget *window; TestUtilsCallback picture_callback; DummyDevice *device; testutils_callback_init (&picture_callback); device = dummy_device_provider_add (provider); dummy_device_set_image (device, "/aperture/quadrants.png"); testutils_wait_for_device_change (manager); viewfinder = aperture_viewfinder_new (); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (viewfinder)); gtk_widget_show_all (window); aperture_viewfinder_take_picture_async (viewfinder, NULL, (GAsyncReadyCallback) on_picture_taken, &picture_callback); testutils_callback_assert_called (&picture_callback, 1000); gtk_widget_destroy (window); } static void simultaneous_operations_on_picture_taken_1 (ApertureViewfinder *source, GAsyncResult *res, TestUtilsCallback *callback) { testutils_callback_call (callback); } static void simultaneous_operations_on_picture_taken_2 (ApertureViewfinder *source, GAsyncResult *res, TestUtilsCallback *callback) { g_autoptr(GError) err = NULL; g_autoptr(GdkPixbuf) pixbuf = NULL; pixbuf = aperture_viewfinder_take_picture_finish (source, res, &err); g_assert_null (pixbuf); g_assert_error (err, APERTURE_MEDIA_CAPTURE_ERROR, APERTURE_MEDIA_CAPTURE_ERROR_OPERATION_IN_PROGRESS); testutils_callback_call (callback); } static void test_viewfinder_simultaneous_operations () { g_autoptr(ApertureDeviceManager) manager = aperture_device_manager_get_instance (); g_autoptr(DummyDeviceProvider) provider = DUMMY_DEVICE_PROVIDER (gst_device_provider_factory_get_by_name ("dummy-device-provider")); ApertureViewfinder *viewfinder; GtkWidget *window; g_autoptr(GError) err1 = NULL; g_autoptr(GError) err2 = NULL; TestUtilsCallback picture_callback_1; TestUtilsCallback picture_callback_2; testutils_callback_init (&picture_callback_1); testutils_callback_init (&picture_callback_2); dummy_device_provider_add (provider); dummy_device_provider_add (provider); testutils_wait_for_device_change (manager); testutils_wait_for_device_change (manager); viewfinder = aperture_viewfinder_new (); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (viewfinder)); gtk_widget_show_all (window); /* this is the operation that will block the others */ aperture_viewfinder_take_picture_async (viewfinder, NULL, (GAsyncReadyCallback) simultaneous_operations_on_picture_taken_1, &picture_callback_1); /* setting the camera should not work */ aperture_viewfinder_set_camera (viewfinder, NULL, &err1); g_assert_error (err1, APERTURE_MEDIA_CAPTURE_ERROR, APERTURE_MEDIA_CAPTURE_ERROR_OPERATION_IN_PROGRESS); /* starting a video should not work */ aperture_viewfinder_start_recording_to_file (viewfinder, "not_a_real_filename", &err2); g_assert_error (err2, APERTURE_MEDIA_CAPTURE_ERROR, APERTURE_MEDIA_CAPTURE_ERROR_OPERATION_IN_PROGRESS); /* taking another picture should not work */ aperture_viewfinder_take_picture_async (viewfinder, NULL, (GAsyncReadyCallback) simultaneous_operations_on_picture_taken_2, &picture_callback_2); testutils_callback_assert_called (&picture_callback_1, 1000); testutils_callback_assert_called (&picture_callback_2, 1000); gtk_widget_destroy (window); } static void disconnect_on_picture_taken (ApertureViewfinder *source, GAsyncResult *res, TestUtilsCallback *callback) { g_autoptr(GError) err = NULL; g_autoptr(GdkPixbuf) pixbuf = NULL; pixbuf = aperture_viewfinder_take_picture_finish (source, res, &err); g_assert_null (pixbuf); g_assert_error (err, APERTURE_MEDIA_CAPTURE_ERROR, APERTURE_MEDIA_CAPTURE_ERROR_CAMERA_DISCONNECTED); testutils_callback_call (callback); } static void test_viewfinder_disconnect_camera () { g_autoptr(ApertureDeviceManager) manager = aperture_device_manager_get_instance (); g_autoptr(DummyDeviceProvider) provider = DUMMY_DEVICE_PROVIDER (gst_device_provider_factory_get_by_name ("dummy-device-provider")); ApertureViewfinder *viewfinder; GtkWidget *window; TestUtilsCallback picture_callback; testutils_callback_init (&picture_callback); g_test_summary ("Test error handling when the active camera is disconnected during an operation"); dummy_device_provider_add (provider); testutils_wait_for_device_change (manager); g_assert_cmpint (aperture_device_manager_get_num_cameras (manager), ==, 1); viewfinder = aperture_viewfinder_new (); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (viewfinder)); gtk_widget_show_all (window); aperture_viewfinder_take_picture_async (viewfinder, NULL, (GAsyncReadyCallback) disconnect_on_picture_taken, &picture_callback); dummy_device_provider_remove (provider); testutils_wait_for_device_change (manager); testutils_callback_assert_called (&picture_callback, 1000); gtk_widget_destroy (window); } void add_viewfinder_tests () { g_test_add_func ("/viewfinder/no_camera", test_viewfinder_no_camera_state); g_test_add_func ("/viewfinder/take_picture", test_viewfinder_take_picture); g_test_add_func ("/viewfinder/simultaneous_operations", test_viewfinder_simultaneous_operations); g_test_add_func ("/viewfinder/disconnect_camera", test_viewfinder_disconnect_camera); } libaperture-0-0.1.0+git20221220/tests/utils.c000066400000000000000000000103071435023540700202270ustar00rootroot00000000000000/* utils.c * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ /* Various utility functions for running tests. */ /* * TestUtilsCallback is a simple way to test asynchronous callbacks. * * To use it: * - Initialize a TestUtilsCallback using testutils_callback_init() * - Call testutils_callback_call() with a pointer to the TestUtilsCallback as * the first argument (for signals, this means you will need to use * g_signal_connect_swapped()) * - Call testutils_callback_assert_called(). If the callback has been called * already, it will return fine. If it has not, a main loop will be run to * give it a chance to be called; if the timeout expires and the callback * still has not been called, an assertion will be made and the test will fail. */ #include #include #include "utils.h" static gboolean timeout_reached (TestUtilsCallback *self) { gtk_main_quit (); g_assert_not_reached (); return G_SOURCE_REMOVE; } void testutils_callback_init (TestUtilsCallback *self) { self->calls = 0; self->loop_running = FALSE; } void testutils_callback_assert_already_called (TestUtilsCallback *self) { g_assert_cmpint (self->calls, >, 0); self->calls --; } void testutils_callback_assert_called (TestUtilsCallback *self, int timeout) { if (timeout == 0) { testutils_callback_assert_already_called (self); return; } if (self->calls > 0) { self->calls --; return; } self->loop_running = TRUE; self->timeout_id = g_timeout_add (timeout, G_SOURCE_FUNC (timeout_reached), self); gtk_main (); g_source_remove (self->timeout_id); testutils_callback_assert_already_called (self); self->loop_running = FALSE; } void testutils_callback_call (TestUtilsCallback *self) { self->calls ++; if (self->loop_running) { gtk_main_quit (); } } /** * PRIVATE:testutils_wait_for_device_added: * * Runs a main loop until an #ApertureDeviceManager emits the * #ApertureDeviceManager::device-added or #ApertureDeviceManager::device-removed * signals. */ void testutils_wait_for_device_change (ApertureDeviceManager *manager) { TestUtilsCallback callback; ulong added, removed; testutils_callback_init (&callback); added = g_signal_connect_swapped (manager, "camera-added", G_CALLBACK (testutils_callback_call), &callback); removed = g_signal_connect_swapped (manager, "camera-removed", G_CALLBACK (testutils_callback_call), &callback); testutils_callback_assert_called (&callback, 1000); g_signal_handler_disconnect (manager, added); g_signal_handler_disconnect (manager, removed); } /* Get the RGB component of a pixel in a pixbuf */ static guint32 pixbuf_pixel (GdkPixbuf *pixbuf, int x, int y) { int channels = gdk_pixbuf_get_n_channels (pixbuf); int rowstride = gdk_pixbuf_get_rowstride (pixbuf); int offset = y*rowstride + x*channels; const guint8 *pixel = &gdk_pixbuf_read_pixels (pixbuf)[offset]; return pixel[0] << 16 | pixel[1] << 8 | pixel[2]; } /* Assert that the given pixbuf matches the quadrants.png image in the * tests/data directory. */ void testutils_assert_quadrants_pixbuf (GdkPixbuf *pixbuf) { g_assert_true (GDK_IS_PIXBUF (pixbuf)); g_assert_cmpint (gdk_pixbuf_get_width (pixbuf), ==, 128); g_assert_cmpint (gdk_pixbuf_get_height (pixbuf), ==, 128); g_assert_cmphex (pixbuf_pixel (pixbuf, 32, 32), ==, 0xFF0000); g_assert_cmphex (pixbuf_pixel (pixbuf, 96, 32), ==, 0x00FF00); g_assert_cmphex (pixbuf_pixel (pixbuf, 32, 96), ==, 0x0000FF); g_assert_cmphex (pixbuf_pixel (pixbuf, 96, 96), ==, 0x000000); } libaperture-0-0.1.0+git20221220/tests/utils.h000066400000000000000000000027351435023540700202420ustar00rootroot00000000000000/* utils.h * * Copyright 2020 James Westman * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include #include G_BEGIN_DECLS typedef struct { gboolean loop_running; int calls; uint timeout_id; } TestUtilsCallback; void testutils_callback_init (TestUtilsCallback *self); void testutils_callback_assert_called (TestUtilsCallback *self, int timeout); void testutils_callback_assert_already_called (TestUtilsCallback *self); void testutils_callback_call (TestUtilsCallback *self); void testutils_wait_for_device_change (ApertureDeviceManager *manager); void testutils_assert_quadrants_pixbuf (GdkPixbuf *pixbuf); G_END_DECLS