Code style

This commit is contained in:
Martijn Braam 2020-12-06 15:44:31 +01:00
parent 101abe33cd
commit 72105c9491
20 changed files with 3200 additions and 2525 deletions

548
.clang-format Normal file
View File

@ -0,0 +1,548 @@
# SPDX-License-Identifier: GPL-2.0
#
# clang-format configuration file. Intended for clang-format >= 4.
#
# For more information, see:
#
# Documentation/process/clang-format.rst
# https://clang.llvm.org/docs/ClangFormat.html
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
#
---
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
#AlignEscapedNewlines: Left # Unknown to clang-format-4.0
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: All
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
#AfterExternBlock: false # Unknown to clang-format-5.0
BeforeCatch: false
BeforeElse: false
IndentBraces: false
#SplitEmptyFunction: true # Unknown to clang-format-4.0
#SplitEmptyRecord: true # Unknown to clang-format-4.0
#SplitEmptyNamespace: true # Unknown to clang-format-4.0
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 85
CommentPragmas: '^ IWYU pragma:'
#CompactNamespaces: false # Unknown to clang-format-4.0
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
#FixNamespaceComments: false # Unknown to clang-format-4.0
# Taken from:
# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \
# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \
# | sort | uniq
ForEachMacros:
- 'apei_estatus_for_each_section'
- 'ata_for_each_dev'
- 'ata_for_each_link'
- '__ata_qc_for_each'
- 'ata_qc_for_each'
- 'ata_qc_for_each_raw'
- 'ata_qc_for_each_with_internal'
- 'ax25_for_each'
- 'ax25_uid_for_each'
- '__bio_for_each_bvec'
- 'bio_for_each_bvec'
- 'bio_for_each_bvec_all'
- 'bio_for_each_integrity_vec'
- '__bio_for_each_segment'
- 'bio_for_each_segment'
- 'bio_for_each_segment_all'
- 'bio_list_for_each'
- 'bip_for_each_vec'
- 'bitmap_for_each_clear_region'
- 'bitmap_for_each_set_region'
- 'blkg_for_each_descendant_post'
- 'blkg_for_each_descendant_pre'
- 'blk_queue_for_each_rl'
- 'bond_for_each_slave'
- 'bond_for_each_slave_rcu'
- 'bpf_for_each_spilled_reg'
- 'btree_for_each_safe128'
- 'btree_for_each_safe32'
- 'btree_for_each_safe64'
- 'btree_for_each_safel'
- 'card_for_each_dev'
- 'cgroup_taskset_for_each'
- 'cgroup_taskset_for_each_leader'
- 'cpufreq_for_each_entry'
- 'cpufreq_for_each_entry_idx'
- 'cpufreq_for_each_valid_entry'
- 'cpufreq_for_each_valid_entry_idx'
- 'css_for_each_child'
- 'css_for_each_descendant_post'
- 'css_for_each_descendant_pre'
- 'device_for_each_child_node'
- 'dma_fence_chain_for_each'
- 'do_for_each_ftrace_op'
- 'drm_atomic_crtc_for_each_plane'
- 'drm_atomic_crtc_state_for_each_plane'
- 'drm_atomic_crtc_state_for_each_plane_state'
- 'drm_atomic_for_each_plane_damage'
- 'drm_client_for_each_connector_iter'
- 'drm_client_for_each_modeset'
- 'drm_connector_for_each_possible_encoder'
- 'drm_for_each_bridge_in_chain'
- 'drm_for_each_connector_iter'
- 'drm_for_each_crtc'
- 'drm_for_each_encoder'
- 'drm_for_each_encoder_mask'
- 'drm_for_each_fb'
- 'drm_for_each_legacy_plane'
- 'drm_for_each_plane'
- 'drm_for_each_plane_mask'
- 'drm_for_each_privobj'
- 'drm_mm_for_each_hole'
- 'drm_mm_for_each_node'
- 'drm_mm_for_each_node_in_range'
- 'drm_mm_for_each_node_safe'
- 'flow_action_for_each'
- 'for_each_active_dev_scope'
- 'for_each_active_drhd_unit'
- 'for_each_active_iommu'
- 'for_each_aggr_pgid'
- 'for_each_available_child_of_node'
- 'for_each_bio'
- 'for_each_board_func_rsrc'
- 'for_each_bvec'
- 'for_each_card_auxs'
- 'for_each_card_auxs_safe'
- 'for_each_card_components'
- 'for_each_card_dapms'
- 'for_each_card_pre_auxs'
- 'for_each_card_prelinks'
- 'for_each_card_rtds'
- 'for_each_card_rtds_safe'
- 'for_each_card_widgets'
- 'for_each_card_widgets_safe'
- 'for_each_cgroup_storage_type'
- 'for_each_child_of_node'
- 'for_each_clear_bit'
- 'for_each_clear_bit_from'
- 'for_each_cmsghdr'
- 'for_each_compatible_node'
- 'for_each_component_dais'
- 'for_each_component_dais_safe'
- 'for_each_comp_order'
- 'for_each_console'
- 'for_each_cpu'
- 'for_each_cpu_and'
- 'for_each_cpu_not'
- 'for_each_cpu_wrap'
- 'for_each_dapm_widgets'
- 'for_each_dev_addr'
- 'for_each_dev_scope'
- 'for_each_displayid_db'
- 'for_each_dma_cap_mask'
- 'for_each_dpcm_be'
- 'for_each_dpcm_be_rollback'
- 'for_each_dpcm_be_safe'
- 'for_each_dpcm_fe'
- 'for_each_drhd_unit'
- 'for_each_dss_dev'
- 'for_each_efi_memory_desc'
- 'for_each_efi_memory_desc_in_map'
- 'for_each_element'
- 'for_each_element_extid'
- 'for_each_element_id'
- 'for_each_endpoint_of_node'
- 'for_each_evictable_lru'
- 'for_each_fib6_node_rt_rcu'
- 'for_each_fib6_walker_rt'
- 'for_each_free_mem_pfn_range_in_zone'
- 'for_each_free_mem_pfn_range_in_zone_from'
- 'for_each_free_mem_range'
- 'for_each_free_mem_range_reverse'
- 'for_each_func_rsrc'
- 'for_each_hstate'
- 'for_each_if'
- 'for_each_iommu'
- 'for_each_ip_tunnel_rcu'
- 'for_each_irq_nr'
- 'for_each_link_codecs'
- 'for_each_link_cpus'
- 'for_each_link_platforms'
- 'for_each_lru'
- 'for_each_matching_node'
- 'for_each_matching_node_and_match'
- 'for_each_member'
- 'for_each_mem_region'
- 'for_each_memblock_type'
- 'for_each_memcg_cache_index'
- 'for_each_mem_pfn_range'
- '__for_each_mem_range'
- 'for_each_mem_range'
- '__for_each_mem_range_rev'
- 'for_each_mem_range_rev'
- 'for_each_migratetype_order'
- 'for_each_msi_entry'
- 'for_each_msi_entry_safe'
- 'for_each_net'
- 'for_each_net_continue_reverse'
- 'for_each_netdev'
- 'for_each_netdev_continue'
- 'for_each_netdev_continue_rcu'
- 'for_each_netdev_continue_reverse'
- 'for_each_netdev_feature'
- 'for_each_netdev_in_bond_rcu'
- 'for_each_netdev_rcu'
- 'for_each_netdev_reverse'
- 'for_each_netdev_safe'
- 'for_each_net_rcu'
- 'for_each_new_connector_in_state'
- 'for_each_new_crtc_in_state'
- 'for_each_new_mst_mgr_in_state'
- 'for_each_new_plane_in_state'
- 'for_each_new_private_obj_in_state'
- 'for_each_node'
- 'for_each_node_by_name'
- 'for_each_node_by_type'
- 'for_each_node_mask'
- 'for_each_node_state'
- 'for_each_node_with_cpus'
- 'for_each_node_with_property'
- 'for_each_nonreserved_multicast_dest_pgid'
- 'for_each_of_allnodes'
- 'for_each_of_allnodes_from'
- 'for_each_of_cpu_node'
- 'for_each_of_pci_range'
- 'for_each_old_connector_in_state'
- 'for_each_old_crtc_in_state'
- 'for_each_old_mst_mgr_in_state'
- 'for_each_oldnew_connector_in_state'
- 'for_each_oldnew_crtc_in_state'
- 'for_each_oldnew_mst_mgr_in_state'
- 'for_each_oldnew_plane_in_state'
- 'for_each_oldnew_plane_in_state_reverse'
- 'for_each_oldnew_private_obj_in_state'
- 'for_each_old_plane_in_state'
- 'for_each_old_private_obj_in_state'
- 'for_each_online_cpu'
- 'for_each_online_node'
- 'for_each_online_pgdat'
- 'for_each_pci_bridge'
- 'for_each_pci_dev'
- 'for_each_pci_msi_entry'
- 'for_each_pcm_streams'
- 'for_each_physmem_range'
- 'for_each_populated_zone'
- 'for_each_possible_cpu'
- 'for_each_present_cpu'
- 'for_each_prime_number'
- 'for_each_prime_number_from'
- 'for_each_process'
- 'for_each_process_thread'
- 'for_each_property_of_node'
- 'for_each_registered_fb'
- 'for_each_requested_gpio'
- 'for_each_requested_gpio_in_range'
- 'for_each_reserved_mem_range'
- 'for_each_reserved_mem_region'
- 'for_each_rtd_codec_dais'
- 'for_each_rtd_codec_dais_rollback'
- 'for_each_rtd_components'
- 'for_each_rtd_cpu_dais'
- 'for_each_rtd_cpu_dais_rollback'
- 'for_each_rtd_dais'
- 'for_each_set_bit'
- 'for_each_set_bit_from'
- 'for_each_set_clump8'
- 'for_each_sg'
- 'for_each_sg_dma_page'
- 'for_each_sg_page'
- 'for_each_sgtable_dma_page'
- 'for_each_sgtable_dma_sg'
- 'for_each_sgtable_page'
- 'for_each_sgtable_sg'
- 'for_each_sibling_event'
- 'for_each_subelement'
- 'for_each_subelement_extid'
- 'for_each_subelement_id'
- '__for_each_thread'
- 'for_each_thread'
- 'for_each_unicast_dest_pgid'
- 'for_each_wakeup_source'
- 'for_each_zone'
- 'for_each_zone_zonelist'
- 'for_each_zone_zonelist_nodemask'
- 'fwnode_for_each_available_child_node'
- 'fwnode_for_each_child_node'
- 'fwnode_graph_for_each_endpoint'
- 'gadget_for_each_ep'
- 'genradix_for_each'
- 'genradix_for_each_from'
- 'hash_for_each'
- 'hash_for_each_possible'
- 'hash_for_each_possible_rcu'
- 'hash_for_each_possible_rcu_notrace'
- 'hash_for_each_possible_safe'
- 'hash_for_each_rcu'
- 'hash_for_each_safe'
- 'hctx_for_each_ctx'
- 'hlist_bl_for_each_entry'
- 'hlist_bl_for_each_entry_rcu'
- 'hlist_bl_for_each_entry_safe'
- 'hlist_for_each'
- 'hlist_for_each_entry'
- 'hlist_for_each_entry_continue'
- 'hlist_for_each_entry_continue_rcu'
- 'hlist_for_each_entry_continue_rcu_bh'
- 'hlist_for_each_entry_from'
- 'hlist_for_each_entry_from_rcu'
- 'hlist_for_each_entry_rcu'
- 'hlist_for_each_entry_rcu_bh'
- 'hlist_for_each_entry_rcu_notrace'
- 'hlist_for_each_entry_safe'
- '__hlist_for_each_rcu'
- 'hlist_for_each_safe'
- 'hlist_nulls_for_each_entry'
- 'hlist_nulls_for_each_entry_from'
- 'hlist_nulls_for_each_entry_rcu'
- 'hlist_nulls_for_each_entry_safe'
- 'i3c_bus_for_each_i2cdev'
- 'i3c_bus_for_each_i3cdev'
- 'ide_host_for_each_port'
- 'ide_port_for_each_dev'
- 'ide_port_for_each_present_dev'
- 'idr_for_each_entry'
- 'idr_for_each_entry_continue'
- 'idr_for_each_entry_continue_ul'
- 'idr_for_each_entry_ul'
- 'in_dev_for_each_ifa_rcu'
- 'in_dev_for_each_ifa_rtnl'
- 'inet_bind_bucket_for_each'
- 'inet_lhash2_for_each_icsk_rcu'
- 'key_for_each'
- 'key_for_each_safe'
- 'klp_for_each_func'
- 'klp_for_each_func_safe'
- 'klp_for_each_func_static'
- 'klp_for_each_object'
- 'klp_for_each_object_safe'
- 'klp_for_each_object_static'
- 'kunit_suite_for_each_test_case'
- 'kvm_for_each_memslot'
- 'kvm_for_each_vcpu'
- 'list_for_each'
- 'list_for_each_codec'
- 'list_for_each_codec_safe'
- 'list_for_each_continue'
- 'list_for_each_entry'
- 'list_for_each_entry_continue'
- 'list_for_each_entry_continue_rcu'
- 'list_for_each_entry_continue_reverse'
- 'list_for_each_entry_from'
- 'list_for_each_entry_from_rcu'
- 'list_for_each_entry_from_reverse'
- 'list_for_each_entry_lockless'
- 'list_for_each_entry_rcu'
- 'list_for_each_entry_reverse'
- 'list_for_each_entry_safe'
- 'list_for_each_entry_safe_continue'
- 'list_for_each_entry_safe_from'
- 'list_for_each_entry_safe_reverse'
- 'list_for_each_prev'
- 'list_for_each_prev_safe'
- 'list_for_each_safe'
- 'llist_for_each'
- 'llist_for_each_entry'
- 'llist_for_each_entry_safe'
- 'llist_for_each_safe'
- 'mci_for_each_dimm'
- 'media_device_for_each_entity'
- 'media_device_for_each_intf'
- 'media_device_for_each_link'
- 'media_device_for_each_pad'
- 'nanddev_io_for_each_page'
- 'netdev_for_each_lower_dev'
- 'netdev_for_each_lower_private'
- 'netdev_for_each_lower_private_rcu'
- 'netdev_for_each_mc_addr'
- 'netdev_for_each_uc_addr'
- 'netdev_for_each_upper_dev_rcu'
- 'netdev_hw_addr_list_for_each'
- 'nft_rule_for_each_expr'
- 'nla_for_each_attr'
- 'nla_for_each_nested'
- 'nlmsg_for_each_attr'
- 'nlmsg_for_each_msg'
- 'nr_neigh_for_each'
- 'nr_neigh_for_each_safe'
- 'nr_node_for_each'
- 'nr_node_for_each_safe'
- 'of_for_each_phandle'
- 'of_property_for_each_string'
- 'of_property_for_each_u32'
- 'pci_bus_for_each_resource'
- 'pcm_for_each_format'
- 'ping_portaddr_for_each_entry'
- 'plist_for_each'
- 'plist_for_each_continue'
- 'plist_for_each_entry'
- 'plist_for_each_entry_continue'
- 'plist_for_each_entry_safe'
- 'plist_for_each_safe'
- 'pnp_for_each_card'
- 'pnp_for_each_dev'
- 'protocol_for_each_card'
- 'protocol_for_each_dev'
- 'queue_for_each_hw_ctx'
- 'radix_tree_for_each_slot'
- 'radix_tree_for_each_tagged'
- 'rbtree_postorder_for_each_entry_safe'
- 'rdma_for_each_block'
- 'rdma_for_each_port'
- 'rdma_umem_for_each_dma_block'
- 'resource_list_for_each_entry'
- 'resource_list_for_each_entry_safe'
- 'rhl_for_each_entry_rcu'
- 'rhl_for_each_rcu'
- 'rht_for_each'
- 'rht_for_each_entry'
- 'rht_for_each_entry_from'
- 'rht_for_each_entry_rcu'
- 'rht_for_each_entry_rcu_from'
- 'rht_for_each_entry_safe'
- 'rht_for_each_from'
- 'rht_for_each_rcu'
- 'rht_for_each_rcu_from'
- '__rq_for_each_bio'
- 'rq_for_each_bvec'
- 'rq_for_each_segment'
- 'scsi_for_each_prot_sg'
- 'scsi_for_each_sg'
- 'sctp_for_each_hentry'
- 'sctp_skb_for_each'
- 'shdma_for_each_chan'
- '__shost_for_each_device'
- 'shost_for_each_device'
- 'sk_for_each'
- 'sk_for_each_bound'
- 'sk_for_each_entry_offset_rcu'
- 'sk_for_each_from'
- 'sk_for_each_rcu'
- 'sk_for_each_safe'
- 'sk_nulls_for_each'
- 'sk_nulls_for_each_from'
- 'sk_nulls_for_each_rcu'
- 'snd_array_for_each'
- 'snd_pcm_group_for_each_entry'
- 'snd_soc_dapm_widget_for_each_path'
- 'snd_soc_dapm_widget_for_each_path_safe'
- 'snd_soc_dapm_widget_for_each_sink_path'
- 'snd_soc_dapm_widget_for_each_source_path'
- 'tb_property_for_each'
- 'tcf_exts_for_each_action'
- 'udp_portaddr_for_each_entry'
- 'udp_portaddr_for_each_entry_rcu'
- 'usb_hub_for_each_child'
- 'v4l2_device_for_each_subdev'
- 'v4l2_m2m_for_each_dst_buf'
- 'v4l2_m2m_for_each_dst_buf_safe'
- 'v4l2_m2m_for_each_src_buf'
- 'v4l2_m2m_for_each_src_buf_safe'
- 'virtio_device_for_each_vq'
- 'while_for_each_ftrace_op'
- 'xa_for_each'
- 'xa_for_each_marked'
- 'xa_for_each_range'
- 'xa_for_each_start'
- 'xas_for_each'
- 'xas_for_each_conflict'
- 'xas_for_each_marked'
- 'xbc_array_for_each_value'
- 'xbc_for_each_key_value'
- 'xbc_node_for_each_array_value'
- 'xbc_node_for_each_child'
- 'xbc_node_for_each_key_value'
- 'zorro_for_each_dev'
#IncludeBlocks: Preserve # Unknown to clang-format-5.0
IncludeCategories:
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
#IndentPPDirectives: None # Unknown to clang-format-5.0
IndentWidth: 8
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
# Taken from git's rules
#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakString: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SortIncludes: false
#SortUsingDeclarations: false # Unknown to clang-format-4.0
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0
#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0
SpaceBeforeParens: ControlStatements
#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp03
TabWidth: 8
UseTab: Always
...

1782
camera.c

File diff suppressed because it is too large Load Diff

View File

@ -5,19 +5,19 @@
#include <stdint.h> #include <stdint.h>
typedef enum { typedef enum {
MP_PIXEL_FMT_UNSUPPORTED, MP_PIXEL_FMT_UNSUPPORTED,
MP_PIXEL_FMT_BGGR8, MP_PIXEL_FMT_BGGR8,
MP_PIXEL_FMT_GBRG8, MP_PIXEL_FMT_GBRG8,
MP_PIXEL_FMT_GRBG8, MP_PIXEL_FMT_GRBG8,
MP_PIXEL_FMT_RGGB8, MP_PIXEL_FMT_RGGB8,
MP_PIXEL_FMT_BGGR10P, MP_PIXEL_FMT_BGGR10P,
MP_PIXEL_FMT_GBRG10P, MP_PIXEL_FMT_GBRG10P,
MP_PIXEL_FMT_GRBG10P, MP_PIXEL_FMT_GRBG10P,
MP_PIXEL_FMT_RGGB10P, MP_PIXEL_FMT_RGGB10P,
MP_PIXEL_FMT_UYVY, MP_PIXEL_FMT_UYVY,
MP_PIXEL_FMT_YUYV, MP_PIXEL_FMT_YUYV,
MP_PIXEL_FMT_MAX, MP_PIXEL_FMT_MAX,
} MPPixelFormat; } MPPixelFormat;
const char *mp_pixel_format_to_str(MPPixelFormat pixel_format); const char *mp_pixel_format_to_str(MPPixelFormat pixel_format);
@ -31,23 +31,24 @@ uint32_t mp_pixel_format_to_v4l_bus_code(MPPixelFormat pixel_format);
uint32_t mp_pixel_format_bits_per_pixel(MPPixelFormat pixel_format); uint32_t mp_pixel_format_bits_per_pixel(MPPixelFormat pixel_format);
uint32_t mp_pixel_format_width_to_bytes(MPPixelFormat pixel_format, uint32_t width); uint32_t mp_pixel_format_width_to_bytes(MPPixelFormat pixel_format, uint32_t width);
uint32_t mp_pixel_format_width_to_colors(MPPixelFormat pixel_format, uint32_t width); uint32_t mp_pixel_format_width_to_colors(MPPixelFormat pixel_format, uint32_t width);
uint32_t mp_pixel_format_height_to_colors(MPPixelFormat pixel_format, uint32_t height); uint32_t mp_pixel_format_height_to_colors(MPPixelFormat pixel_format,
uint32_t height);
typedef struct { typedef struct {
MPPixelFormat pixel_format; MPPixelFormat pixel_format;
struct v4l2_fract frame_interval; struct v4l2_fract frame_interval;
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
} MPCameraMode; } MPCameraMode;
bool mp_camera_mode_is_equivalent(const MPCameraMode *m1, const MPCameraMode *m2); bool mp_camera_mode_is_equivalent(const MPCameraMode *m1, const MPCameraMode *m2);
typedef struct { typedef struct {
uint32_t pixel_format; uint32_t pixel_format;
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
uint8_t *data; uint8_t *data;
} MPImage; } MPImage;
typedef struct _MPCamera MPCamera; typedef struct _MPCamera MPCamera;
@ -66,7 +67,8 @@ bool mp_camera_set_mode(MPCamera *camera, MPCameraMode *mode);
bool mp_camera_start_capture(MPCamera *camera); bool mp_camera_start_capture(MPCamera *camera);
bool mp_camera_stop_capture(MPCamera *camera); bool mp_camera_stop_capture(MPCamera *camera);
bool mp_camera_is_capturing(MPCamera *camera); bool mp_camera_is_capturing(MPCamera *camera);
bool mp_camera_capture_image(MPCamera *camera, void (*callback)(MPImage, void *), void *user_data); bool mp_camera_capture_image(MPCamera *camera, void (*callback)(MPImage, void *),
void *user_data);
typedef struct _MPCameraModeList MPCameraModeList; typedef struct _MPCameraModeList MPCameraModeList;
@ -77,21 +79,21 @@ MPCameraModeList *mp_camera_mode_list_next(MPCameraModeList *list);
void mp_camera_mode_list_free(MPCameraModeList *list); void mp_camera_mode_list_free(MPCameraModeList *list);
typedef struct { typedef struct {
uint32_t id; uint32_t id;
uint32_t type; uint32_t type;
char name[32]; char name[32];
int32_t min; int32_t min;
int32_t max; int32_t max;
int32_t step; int32_t step;
int32_t default_value; int32_t default_value;
uint32_t flags; uint32_t flags;
uint32_t element_size; uint32_t element_size;
uint32_t element_count; uint32_t element_count;
uint32_t dimensions_count; uint32_t dimensions_count;
uint32_t dimensions[V4L2_CTRL_MAX_DIMS]; uint32_t dimensions[V4L2_CTRL_MAX_DIMS];
} MPControl; } MPControl;
const char *mp_control_id_to_str(uint32_t id); const char *mp_control_id_to_str(uint32_t id);

View File

@ -20,263 +20,269 @@ static char *exif_model;
static bool static bool
find_config(char *conffile) find_config(char *conffile)
{ {
char buf[512]; char buf[512];
char *xdg_config_home; char *xdg_config_home;
wordexp_t exp_result; wordexp_t exp_result;
FILE *fp; FILE *fp;
// Resolve XDG stuff // Resolve XDG stuff
if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL) { if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL) {
xdg_config_home = "~/.config"; xdg_config_home = "~/.config";
} }
wordexp(xdg_config_home, &exp_result, 0); wordexp(xdg_config_home, &exp_result, 0);
xdg_config_home = strdup(exp_result.we_wordv[0]); xdg_config_home = strdup(exp_result.we_wordv[0]);
wordfree(&exp_result); wordfree(&exp_result);
if (access("/proc/device-tree/compatible", F_OK) != -1) { if (access("/proc/device-tree/compatible", F_OK) != -1) {
// Reads to compatible string of the current device tree, looks like: // Reads to compatible string of the current device tree, looks like:
// pine64,pinephone-1.2\0allwinner,sun50i-a64\0 // pine64,pinephone-1.2\0allwinner,sun50i-a64\0
fp = fopen("/proc/device-tree/compatible", "r"); fp = fopen("/proc/device-tree/compatible", "r");
fgets(buf, 512, fp); fgets(buf, 512, fp);
fclose(fp); fclose(fp);
// Check config/%dt.ini in the current working directory // Check config/%dt.ini in the current working directory
sprintf(conffile, "config/%s.ini", buf); sprintf(conffile, "config/%s.ini", buf);
if(access(conffile, F_OK) != -1) { if (access(conffile, F_OK) != -1) {
printf("Found config file at %s\n", conffile); printf("Found config file at %s\n", conffile);
return true; return true;
} }
// Check for a config file in XDG_CONFIG_HOME // Check for a config file in XDG_CONFIG_HOME
sprintf(conffile, "%s/megapixels/config/%s.ini", xdg_config_home, buf); sprintf(conffile, "%s/megapixels/config/%s.ini", xdg_config_home,
if(access(conffile, F_OK) != -1) { buf);
printf("Found config file at %s\n", conffile); if (access(conffile, F_OK) != -1) {
return true; printf("Found config file at %s\n", conffile);
} return true;
}
// Check user overridden /etc/megapixels/config/$dt.ini // Check user overridden /etc/megapixels/config/$dt.ini
sprintf(conffile, "%s/megapixels/config/%s.ini", SYSCONFDIR, buf); sprintf(conffile, "%s/megapixels/config/%s.ini", SYSCONFDIR, buf);
if(access(conffile, F_OK) != -1) { if (access(conffile, F_OK) != -1) {
printf("Found config file at %s\n", conffile); printf("Found config file at %s\n", conffile);
return true; return true;
} }
// Check packaged /usr/share/megapixels/config/$dt.ini // Check packaged /usr/share/megapixels/config/$dt.ini
sprintf(conffile, "%s/megapixels/config/%s.ini", DATADIR, buf); sprintf(conffile, "%s/megapixels/config/%s.ini", DATADIR, buf);
if(access(conffile, F_OK) != -1) { if (access(conffile, F_OK) != -1) {
printf("Found config file at %s\n", conffile); printf("Found config file at %s\n", conffile);
return true; return true;
} }
printf("%s not found\n", conffile); printf("%s not found\n", conffile);
} else { } else {
printf("Could not read device name from device tree\n"); printf("Could not read device name from device tree\n");
} }
// If all else fails, fall back to /etc/megapixels.ini // If all else fails, fall back to /etc/megapixels.ini
sprintf(conffile, "/etc/megapixels.ini"); sprintf(conffile, "/etc/megapixels.ini");
if (access(conffile, F_OK) != -1) { if (access(conffile, F_OK) != -1) {
printf("Found config file at %s\n", conffile); printf("Found config file at %s\n", conffile);
return true; return true;
} }
return false; return false;
} }
static int static int
strtoint(const char *nptr, char **endptr, int base) strtoint(const char *nptr, char **endptr, int base)
{ {
long x = strtol(nptr, endptr, base); long x = strtol(nptr, endptr, base);
assert(x <= INT_MAX); assert(x <= INT_MAX);
return (int) x; return (int)x;
} }
static bool static bool
config_handle_camera_mode(const char *prefix, MPCameraMode * mode, const char *name, const char *value) config_handle_camera_mode(const char *prefix, MPCameraMode *mode, const char *name,
const char *value)
{ {
int prefix_length = strlen(prefix); int prefix_length = strlen(prefix);
if (strncmp(prefix, name, prefix_length) != 0) if (strncmp(prefix, name, prefix_length) != 0)
return false; return false;
name += prefix_length; name += prefix_length;
if (strcmp(name, "width") == 0) { if (strcmp(name, "width") == 0) {
mode->width = strtoint(value, NULL, 10); mode->width = strtoint(value, NULL, 10);
} else if (strcmp(name, "height") == 0) { } else if (strcmp(name, "height") == 0) {
mode->height = strtoint(value, NULL, 10); mode->height = strtoint(value, NULL, 10);
} else if (strcmp(name, "rate") == 0) { } else if (strcmp(name, "rate") == 0) {
mode->frame_interval.numerator = 1; mode->frame_interval.numerator = 1;
mode->frame_interval.denominator = strtoint(value, NULL, 10); mode->frame_interval.denominator = strtoint(value, NULL, 10);
} else if (strcmp(name, "fmt") == 0) { } else if (strcmp(name, "fmt") == 0) {
mode->pixel_format = mp_pixel_format_from_str(value); mode->pixel_format = mp_pixel_format_from_str(value);
if (mode->pixel_format == MP_PIXEL_FMT_UNSUPPORTED) { if (mode->pixel_format == MP_PIXEL_FMT_UNSUPPORTED) {
g_printerr("Unsupported pixelformat %s\n", value); g_printerr("Unsupported pixelformat %s\n", value);
exit(1); exit(1);
} }
} else { } else {
return false; return false;
} }
return true; return true;
} }
static int static int
config_ini_handler(void *user, const char *section, const char *name, config_ini_handler(void *user, const char *section, const char *name,
const char *value) const char *value)
{ {
if (strcmp(section, "device") == 0) { if (strcmp(section, "device") == 0) {
if (strcmp(name, "make") == 0) { if (strcmp(name, "make") == 0) {
exif_make = strdup(value); exif_make = strdup(value);
} else if (strcmp(name, "model") == 0) { } else if (strcmp(name, "model") == 0) {
exif_model = strdup(value); exif_model = strdup(value);
} else { } else {
g_printerr("Unknown key '%s' in [device]\n", name); g_printerr("Unknown key '%s' in [device]\n", name);
exit(1); exit(1);
} }
} else { } else {
if (num_cameras == MP_MAX_CAMERAS) { if (num_cameras == MP_MAX_CAMERAS) {
g_printerr("More cameras defined than NUM_CAMERAS\n"); g_printerr("More cameras defined than NUM_CAMERAS\n");
exit(1); exit(1);
} }
size_t index = 0; size_t index = 0;
for (; index < num_cameras; ++index) { for (; index < num_cameras; ++index) {
if (strcmp(cameras[index].cfg_name, section) == 0) { if (strcmp(cameras[index].cfg_name, section) == 0) {
break; break;
} }
} }
if (index == num_cameras) { if (index == num_cameras) {
printf("Adding camera %s from config\n", section); printf("Adding camera %s from config\n", section);
++num_cameras; ++num_cameras;
cameras[index].index = index; cameras[index].index = index;
strcpy(cameras[index].cfg_name, section); strcpy(cameras[index].cfg_name, section);
} }
struct mp_camera_config *cc = &cameras[index]; struct mp_camera_config *cc = &cameras[index];
if (config_handle_camera_mode("capture-", &cc->capture_mode, name, value)) { if (config_handle_camera_mode("capture-", &cc->capture_mode, name,
} else if (config_handle_camera_mode("preview-", &cc->preview_mode, name, value)) { value)) {
} else if (strcmp(name, "rotate") == 0) { } else if (config_handle_camera_mode("preview-", &cc->preview_mode,
cc->rotate = strtoint(value, NULL, 10); name, value)) {
} else if (strcmp(name, "mirrored") == 0) { } else if (strcmp(name, "rotate") == 0) {
cc->mirrored = strcmp(value, "true") == 0; cc->rotate = strtoint(value, NULL, 10);
} else if (strcmp(name, "driver") == 0) { } else if (strcmp(name, "mirrored") == 0) {
strcpy(cc->dev_name, value); cc->mirrored = strcmp(value, "true") == 0;
} else if (strcmp(name, "media-driver") == 0) { } else if (strcmp(name, "driver") == 0) {
strcpy(cc->media_dev_name, value); strcpy(cc->dev_name, value);
} else if (strcmp(name, "media-links") == 0) { } else if (strcmp(name, "media-driver") == 0) {
char **linkdefs = g_strsplit(value, ",", 0); strcpy(cc->media_dev_name, value);
} else if (strcmp(name, "media-links") == 0) {
char **linkdefs = g_strsplit(value, ",", 0);
for (int i = 0; i < MP_MAX_LINKS && linkdefs[i] != NULL; ++i) { for (int i = 0; i < MP_MAX_LINKS && linkdefs[i] != NULL;
char **linkdef = g_strsplit(linkdefs[i], "->", 2); ++i) {
char **porta = g_strsplit(linkdef[0], ":", 2); char **linkdef = g_strsplit(linkdefs[i], "->", 2);
char **portb = g_strsplit(linkdef[1], ":", 2); char **porta = g_strsplit(linkdef[0], ":", 2);
char **portb = g_strsplit(linkdef[1], ":", 2);
strcpy(cc->media_links[i].source_name, porta[0]); strcpy(cc->media_links[i].source_name, porta[0]);
strcpy(cc->media_links[i].target_name, portb[0]); strcpy(cc->media_links[i].target_name, portb[0]);
cc->media_links[i].source_port = strtoint(porta[1], NULL, 10); cc->media_links[i].source_port =
cc->media_links[i].target_port = strtoint(portb[1], NULL, 10); strtoint(porta[1], NULL, 10);
cc->media_links[i].target_port =
strtoint(portb[1], NULL, 10);
g_strfreev(portb); g_strfreev(portb);
g_strfreev(porta); g_strfreev(porta);
g_strfreev(linkdef); g_strfreev(linkdef);
++cc->num_media_links; ++cc->num_media_links;
} }
g_strfreev(linkdefs); g_strfreev(linkdefs);
} else if (strcmp(name, "colormatrix") == 0) { } else if (strcmp(name, "colormatrix") == 0) {
sscanf(value, "%f,%f,%f,%f,%f,%f,%f,%f,%f", sscanf(value, "%f,%f,%f,%f,%f,%f,%f,%f,%f",
cc->colormatrix+0, cc->colormatrix + 0, cc->colormatrix + 1,
cc->colormatrix+1, cc->colormatrix + 2, cc->colormatrix + 3,
cc->colormatrix+2, cc->colormatrix + 4, cc->colormatrix + 5,
cc->colormatrix+3, cc->colormatrix + 6, cc->colormatrix + 7,
cc->colormatrix+4, cc->colormatrix + 8);
cc->colormatrix+5, } else if (strcmp(name, "forwardmatrix") == 0) {
cc->colormatrix+6, sscanf(value, "%f,%f,%f,%f,%f,%f,%f,%f,%f",
cc->colormatrix+7, cc->forwardmatrix + 0, cc->forwardmatrix + 1,
cc->colormatrix+8 cc->forwardmatrix + 2, cc->forwardmatrix + 3,
); cc->forwardmatrix + 4, cc->forwardmatrix + 5,
} else if (strcmp(name, "forwardmatrix") == 0) { cc->forwardmatrix + 6, cc->forwardmatrix + 7,
sscanf(value, "%f,%f,%f,%f,%f,%f,%f,%f,%f", cc->forwardmatrix + 8);
cc->forwardmatrix+0, } else if (strcmp(name, "whitelevel") == 0) {
cc->forwardmatrix+1, cc->whitelevel = strtoint(value, NULL, 10);
cc->forwardmatrix+2, } else if (strcmp(name, "blacklevel") == 0) {
cc->forwardmatrix+3, cc->blacklevel = strtoint(value, NULL, 10);
cc->forwardmatrix+4, } else if (strcmp(name, "focallength") == 0) {
cc->forwardmatrix+5, cc->focallength = strtof(value, NULL);
cc->forwardmatrix+6, } else if (strcmp(name, "cropfactor") == 0) {
cc->forwardmatrix+7, cc->cropfactor = strtof(value, NULL);
cc->forwardmatrix+8 } else if (strcmp(name, "fnumber") == 0) {
); cc->fnumber = strtod(value, NULL);
} else if (strcmp(name, "whitelevel") == 0) { } else if (strcmp(name, "iso-min") == 0) {
cc->whitelevel = strtoint(value, NULL, 10); cc->iso_min = strtod(value, NULL);
} else if (strcmp(name, "blacklevel") == 0) { } else if (strcmp(name, "iso-max") == 0) {
cc->blacklevel = strtoint(value, NULL, 10); cc->iso_max = strtod(value, NULL);
} else if (strcmp(name, "focallength") == 0) { } else {
cc->focallength = strtof(value, NULL); g_printerr("Unknown key '%s' in [%s]\n", name, section);
} else if (strcmp(name, "cropfactor") == 0) { exit(1);
cc->cropfactor = strtof(value, NULL); }
} else if (strcmp(name, "fnumber") == 0) { }
cc->fnumber = strtod(value, NULL); return 1;
} else if (strcmp(name, "iso-min") == 0) {
cc->iso_min = strtod(value, NULL);
} else if (strcmp(name, "iso-max") == 0) {
cc->iso_max = strtod(value, NULL);
} else {
g_printerr("Unknown key '%s' in [%s]\n", name, section);
exit(1);
}
}
return 1;
} }
void void
calculate_matrices() { calculate_matrices()
{
for (size_t i = 0; i < MP_MAX_CAMERAS; ++i) { for (size_t i = 0; i < MP_MAX_CAMERAS; ++i) {
if (cameras[i].colormatrix != NULL && cameras[i].forwardmatrix != NULL) { if (cameras[i].colormatrix != NULL &&
multiply_matrices(cameras[i].colormatrix, cameras[i].forwardmatrix, cameras[i].previewmatrix); cameras[i].forwardmatrix != NULL) {
multiply_matrices(cameras[i].colormatrix,
cameras[i].forwardmatrix,
cameras[i].previewmatrix);
} }
} }
} }
bool mp_load_config() { bool
char file[512]; mp_load_config()
if (!find_config(file)) {
g_printerr("Could not find any config file\n");
return false;
}
int result = ini_parse(file, config_ini_handler, NULL);
if (result == -1) {
g_printerr("Config file not found\n");
return false;
}
if (result == -2) {
g_printerr("Could not allocate memory to parse config file\n");
return false;
}
if (result != 0) {
g_printerr("Could not parse config file\n");
return false;
}
calculate_matrices();
return true;
}
const char * mp_get_device_make()
{ {
return exif_make; char file[512];
if (!find_config(file)) {
g_printerr("Could not find any config file\n");
return false;
}
int result = ini_parse(file, config_ini_handler, NULL);
if (result == -1) {
g_printerr("Config file not found\n");
return false;
}
if (result == -2) {
g_printerr("Could not allocate memory to parse config file\n");
return false;
}
if (result != 0) {
g_printerr("Could not parse config file\n");
return false;
}
calculate_matrices();
return true;
} }
const char * mp_get_device_model() const char *
mp_get_device_make()
{ {
return exif_model; return exif_make;
} }
const struct mp_camera_config * mp_get_camera_config(size_t index) const char *
mp_get_device_model()
{ {
if (index >= num_cameras) return exif_model;
return NULL; }
return &cameras[index]; const struct mp_camera_config *
mp_get_camera_config(size_t index)
{
if (index >= num_cameras)
return NULL;
return &cameras[index];
} }

View File

@ -9,42 +9,42 @@
#define MP_MAX_LINKS 10 #define MP_MAX_LINKS 10
struct mp_media_link_config { struct mp_media_link_config {
char source_name[100]; char source_name[100];
char target_name[100]; char target_name[100];
int source_port; int source_port;
int target_port; int target_port;
}; };
struct mp_camera_config { struct mp_camera_config {
size_t index; size_t index;
char cfg_name[100]; char cfg_name[100];
char dev_name[260]; char dev_name[260];
char media_dev_name[260]; char media_dev_name[260];
MPCameraMode capture_mode; MPCameraMode capture_mode;
MPCameraMode preview_mode; MPCameraMode preview_mode;
int rotate; int rotate;
bool mirrored; bool mirrored;
struct mp_media_link_config media_links[MP_MAX_LINKS]; struct mp_media_link_config media_links[MP_MAX_LINKS];
int num_media_links; int num_media_links;
float colormatrix[9]; float colormatrix[9];
float forwardmatrix[9]; float forwardmatrix[9];
float previewmatrix[9]; float previewmatrix[9];
int blacklevel; int blacklevel;
int whitelevel; int whitelevel;
float focallength; float focallength;
float cropfactor; float cropfactor;
double fnumber; double fnumber;
int iso_min; int iso_min;
int iso_max; int iso_max;
}; };
bool mp_load_config(); bool mp_load_config();
const char *mp_get_device_make(); const char *mp_get_device_make();
const char *mp_get_device_model(); const char *mp_get_device_model();
const struct mp_camera_config * mp_get_camera_config(size_t index); const struct mp_camera_config *mp_get_camera_config(size_t index);

586
device.c
View File

@ -8,416 +8,456 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
bool mp_find_device_path(struct media_v2_intf_devnode devnode, char *path, int length) bool
mp_find_device_path(struct media_v2_intf_devnode devnode, char *path, int length)
{ {
char uevent_path[256]; char uevent_path[256];
snprintf(uevent_path, 256, "/sys/dev/char/%d:%d/uevent", devnode.major, devnode.minor); snprintf(uevent_path, 256, "/sys/dev/char/%d:%d/uevent", devnode.major,
devnode.minor);
FILE *f = fopen(uevent_path, "r"); FILE *f = fopen(uevent_path, "r");
if (!f) { if (!f) {
return false; return false;
} }
char line[512]; char line[512];
while (fgets(line, 512, f)) { while (fgets(line, 512, f)) {
if (strncmp(line, "DEVNAME=", 8) == 0) { if (strncmp(line, "DEVNAME=", 8) == 0) {
// Drop newline // Drop newline
int length = strlen(line); int length = strlen(line);
if (line[length - 1] == '\n') if (line[length - 1] == '\n')
line[length - 1] = '\0'; line[length - 1] = '\0';
snprintf(path, length, "/dev/%s", line + 8); snprintf(path, length, "/dev/%s", line + 8);
return true; return true;
} }
} }
fclose(f); fclose(f);
return false; return false;
} }
struct _MPDevice { struct _MPDevice {
int fd; int fd;
struct media_device_info info; struct media_device_info info;
struct media_v2_entity *entities; struct media_v2_entity *entities;
size_t num_entities; size_t num_entities;
struct media_v2_interface *interfaces; struct media_v2_interface *interfaces;
size_t num_interfaces; size_t num_interfaces;
struct media_v2_pad *pads; struct media_v2_pad *pads;
size_t num_pads; size_t num_pads;
struct media_v2_link *links; struct media_v2_link *links;
size_t num_links; size_t num_links;
}; };
static void errno_printerr(const char *s) static void
errno_printerr(const char *s)
{ {
g_printerr("MPDevice: %s error %d, %s\n", s, errno, strerror(errno)); g_printerr("MPDevice: %s error %d, %s\n", s, errno, strerror(errno));
} }
static int xioctl(int fd, int request, void *arg) static int
xioctl(int fd, int request, void *arg)
{ {
int r; int r;
do { do {
r = ioctl(fd, request, arg); r = ioctl(fd, request, arg);
} while (r == -1 && errno == EINTR); } while (r == -1 && errno == EINTR);
return r; return r;
} }
MPDevice *mp_device_find(const char *driver_name) MPDevice *
mp_device_find(const char *driver_name)
{ {
MPDeviceList *list = mp_device_list_new(); MPDeviceList *list = mp_device_list_new();
MPDevice *found_device = mp_device_list_find_remove(&list, driver_name); MPDevice *found_device = mp_device_list_find_remove(&list, driver_name);
mp_device_list_free(list);
mp_device_list_free(list); return found_device;
return found_device;
} }
MPDevice *mp_device_open(const char *path) MPDevice *
mp_device_open(const char *path)
{ {
int fd = open(path, O_RDWR); int fd = open(path, O_RDWR);
if (fd == -1) { if (fd == -1) {
errno_printerr("open"); errno_printerr("open");
return NULL; return NULL;
} }
return mp_device_new(fd); return mp_device_new(fd);
} }
MPDevice *mp_device_new(int fd) MPDevice *
mp_device_new(int fd)
{ {
// Get the topology of the media device // Get the topology of the media device
struct media_v2_topology topology = {}; struct media_v2_topology topology = {};
if (xioctl(fd, MEDIA_IOC_G_TOPOLOGY, &topology) == -1 if (xioctl(fd, MEDIA_IOC_G_TOPOLOGY, &topology) == -1 ||
|| topology.num_entities == 0) { topology.num_entities == 0) {
close(fd); close(fd);
return NULL; return NULL;
} }
// Create the device // Create the device
MPDevice *device = calloc(1, sizeof(MPDevice)); MPDevice *device = calloc(1, sizeof(MPDevice));
device->fd = fd; device->fd = fd;
device->entities = calloc(topology.num_entities, sizeof(struct media_v2_entity)); device->entities =
device->num_entities = topology.num_entities; calloc(topology.num_entities, sizeof(struct media_v2_entity));
device->interfaces = calloc(topology.num_interfaces, sizeof(struct media_v2_interface)); device->num_entities = topology.num_entities;
device->num_interfaces = topology.num_interfaces; device->interfaces =
device->pads = calloc(topology.num_pads, sizeof(struct media_v2_pad)); calloc(topology.num_interfaces, sizeof(struct media_v2_interface));
device->num_pads = topology.num_pads; device->num_interfaces = topology.num_interfaces;
device->links = calloc(topology.num_links, sizeof(struct media_v2_link)); device->pads = calloc(topology.num_pads, sizeof(struct media_v2_pad));
device->num_links = topology.num_links; device->num_pads = topology.num_pads;
device->links = calloc(topology.num_links, sizeof(struct media_v2_link));
device->num_links = topology.num_links;
// Get the actual devices and interfaces // Get the actual devices and interfaces
topology.ptr_entities = (uint64_t)device->entities; topology.ptr_entities = (uint64_t)device->entities;
topology.ptr_interfaces = (uint64_t)device->interfaces; topology.ptr_interfaces = (uint64_t)device->interfaces;
topology.ptr_pads = (uint64_t)device->pads; topology.ptr_pads = (uint64_t)device->pads;
topology.ptr_links = (uint64_t)device->links; topology.ptr_links = (uint64_t)device->links;
if (xioctl(fd, MEDIA_IOC_G_TOPOLOGY, &topology) == -1) { if (xioctl(fd, MEDIA_IOC_G_TOPOLOGY, &topology) == -1) {
errno_printerr("MEDIA_IOC_G_TOPOLOGY"); errno_printerr("MEDIA_IOC_G_TOPOLOGY");
mp_device_close(device); mp_device_close(device);
return NULL; return NULL;
} }
// Get device info // Get device info
if (xioctl(fd, MEDIA_IOC_DEVICE_INFO, &device->info) == -1) { if (xioctl(fd, MEDIA_IOC_DEVICE_INFO, &device->info) == -1) {
errno_printerr("MEDIA_IOC_DEVICE_INFO"); errno_printerr("MEDIA_IOC_DEVICE_INFO");
mp_device_close(device); mp_device_close(device);
return NULL; return NULL;
} }
return device; return device;
} }
void mp_device_close(MPDevice *device) void
mp_device_close(MPDevice *device)
{ {
close(device->fd); close(device->fd);
free(device->entities); free(device->entities);
free(device->interfaces); free(device->interfaces);
free(device->pads); free(device->pads);
free(device->links); free(device->links);
free(device); free(device);
} }
bool mp_device_setup_link(MPDevice *device, uint32_t source_pad_id, uint32_t sink_pad_id, bool enabled) bool
mp_device_setup_link(MPDevice *device, uint32_t source_pad_id, uint32_t sink_pad_id,
bool enabled)
{ {
const struct media_v2_pad *source_pad = mp_device_get_pad(device, source_pad_id); const struct media_v2_pad *source_pad =
g_return_val_if_fail(source_pad, false); mp_device_get_pad(device, source_pad_id);
g_return_val_if_fail(source_pad, false);
const struct media_v2_pad *sink_pad = mp_device_get_pad(device, sink_pad_id); const struct media_v2_pad *sink_pad = mp_device_get_pad(device, sink_pad_id);
g_return_val_if_fail(sink_pad, false); g_return_val_if_fail(sink_pad, false);
struct media_link_desc link = {}; struct media_link_desc link = {};
link.flags = enabled ? MEDIA_LNK_FL_ENABLED : 0; link.flags = enabled ? MEDIA_LNK_FL_ENABLED : 0;
link.source.entity = source_pad->entity_id; link.source.entity = source_pad->entity_id;
link.source.index = 0; link.source.index = 0;
link.sink.entity = sink_pad->entity_id; link.sink.entity = sink_pad->entity_id;
link.sink.index = 0; link.sink.index = 0;
if (xioctl(device->fd, MEDIA_IOC_SETUP_LINK, &link) == -1) { if (xioctl(device->fd, MEDIA_IOC_SETUP_LINK, &link) == -1) {
errno_printerr("MEDIA_IOC_SETUP_LINK"); errno_printerr("MEDIA_IOC_SETUP_LINK");
return false; return false;
} }
return true; return true;
} }
const struct media_v2_entity *mp_device_find_entity(const MPDevice *device, const char *driver_name) const struct media_v2_entity *
mp_device_find_entity(const MPDevice *device, const char *driver_name)
{ {
int length = strlen(driver_name); int length = strlen(driver_name);
// Find the entity from the name // Find the entity from the name
for (uint32_t i = 0; i < device->num_entities; ++i) { for (uint32_t i = 0; i < device->num_entities; ++i) {
if (strncmp(device->entities[i].name, driver_name, length) == 0) { if (strncmp(device->entities[i].name, driver_name, length) == 0) {
return &device->entities[i]; return &device->entities[i];
} }
} }
return NULL; return NULL;
} }
const struct media_v2_entity *mp_device_find_entity_type(const MPDevice *device, const uint32_t type) const struct media_v2_entity *
mp_device_find_entity_type(const MPDevice *device, const uint32_t type)
{ {
// Find the entity from the entity type // Find the entity from the entity type
for (uint32_t i = 0; i < device->num_entities; ++i) { for (uint32_t i = 0; i < device->num_entities; ++i) {
if (device->entities[i].function == type) { if (device->entities[i].function == type) {
return &device->entities[i]; return &device->entities[i];
} }
} }
return NULL; return NULL;
} }
const struct media_device_info *mp_device_get_info(const MPDevice *device) const struct media_device_info *
mp_device_get_info(const MPDevice *device)
{ {
return &device->info; return &device->info;
} }
const struct media_v2_entity *mp_device_get_entity(const MPDevice *device, uint32_t id) const struct media_v2_entity *
mp_device_get_entity(const MPDevice *device, uint32_t id)
{ {
for (int i = 0; i < device->num_entities; ++i) { for (int i = 0; i < device->num_entities; ++i) {
if (device->entities[i].id == id) { if (device->entities[i].id == id) {
return &device->entities[i]; return &device->entities[i];
} }
} }
return NULL; return NULL;
} }
const struct media_v2_entity *mp_device_get_entities(const MPDevice *device) const struct media_v2_entity *
mp_device_get_entities(const MPDevice *device)
{ {
return device->entities; return device->entities;
} }
size_t mp_device_get_num_entities(const MPDevice *device) size_t
mp_device_get_num_entities(const MPDevice *device)
{ {
return device->num_entities; return device->num_entities;
} }
const struct media_v2_interface *mp_device_find_entity_interface(const MPDevice *device, uint32_t entity_id) const struct media_v2_interface *
mp_device_find_entity_interface(const MPDevice *device, uint32_t entity_id)
{ {
// Find the interface through the link // Find the interface through the link
const struct media_v2_link *link = mp_device_find_link_to(device, entity_id); const struct media_v2_link *link = mp_device_find_link_to(device, entity_id);
if (!link) { if (!link) {
return NULL; return NULL;
} }
return mp_device_get_interface(device, link->source_id); return mp_device_get_interface(device, link->source_id);
} }
const struct media_v2_interface *mp_device_get_interface(const MPDevice *device, uint32_t id) const struct media_v2_interface *
mp_device_get_interface(const MPDevice *device, uint32_t id)
{ {
for (int i = 0; i < device->num_interfaces; ++i) { for (int i = 0; i < device->num_interfaces; ++i) {
if (device->interfaces[i].id == id) { if (device->interfaces[i].id == id) {
return &device->interfaces[i]; return &device->interfaces[i];
} }
} }
return NULL; return NULL;
} }
const struct media_v2_interface *mp_device_get_interfaces(const MPDevice *device) const struct media_v2_interface *
mp_device_get_interfaces(const MPDevice *device)
{ {
return device->interfaces; return device->interfaces;
} }
size_t mp_device_get_num_interfaces(const MPDevice *device) size_t
mp_device_get_num_interfaces(const MPDevice *device)
{ {
return device->num_interfaces; return device->num_interfaces;
} }
const struct media_v2_pad *mp_device_get_pad_from_entity(const MPDevice *device, uint32_t entity_id) const struct media_v2_pad *
mp_device_get_pad_from_entity(const MPDevice *device, uint32_t entity_id)
{ {
for (int i = 0; i < device->num_pads; ++i) { for (int i = 0; i < device->num_pads; ++i) {
if (device->pads[i].entity_id == entity_id) { if (device->pads[i].entity_id == entity_id) {
return &device->pads[i]; return &device->pads[i];
} }
} }
return NULL; return NULL;
} }
const struct media_v2_pad *mp_device_get_pad(const MPDevice *device, uint32_t id) const struct media_v2_pad *
mp_device_get_pad(const MPDevice *device, uint32_t id)
{ {
for (int i = 0; i < device->num_pads; ++i) { for (int i = 0; i < device->num_pads; ++i) {
if (device->pads[i].id == id) { if (device->pads[i].id == id) {
return &device->pads[i]; return &device->pads[i];
} }
} }
return NULL; return NULL;
} }
const struct media_v2_pad *mp_device_get_pads(const MPDevice *device) const struct media_v2_pad *
mp_device_get_pads(const MPDevice *device)
{ {
return device->pads; return device->pads;
} }
size_t mp_device_get_num_pads(const MPDevice *device) size_t
mp_device_get_num_pads(const MPDevice *device)
{ {
return device->num_pads; return device->num_pads;
} }
const struct media_v2_link *mp_device_find_entity_link(const MPDevice *device, uint32_t entity_id) const struct media_v2_link *
mp_device_find_entity_link(const MPDevice *device, uint32_t entity_id)
{ {
const struct media_v2_pad *pad = mp_device_get_pad_from_entity(device, entity_id); const struct media_v2_pad *pad =
const struct media_v2_link *link = mp_device_find_link_to(device, pad->id); mp_device_get_pad_from_entity(device, entity_id);
if (link) { const struct media_v2_link *link = mp_device_find_link_to(device, pad->id);
return link; if (link) {
} return link;
return mp_device_find_link_from(device, pad->id); }
return mp_device_find_link_from(device, pad->id);
} }
const struct media_v2_link *mp_device_find_link_from(const MPDevice *device, uint32_t source) const struct media_v2_link *
mp_device_find_link_from(const MPDevice *device, uint32_t source)
{ {
for (int i = 0; i < device->num_links; ++i) { for (int i = 0; i < device->num_links; ++i) {
if (device->links[i].source_id == source) { if (device->links[i].source_id == source) {
return &device->links[i]; return &device->links[i];
} }
} }
return NULL; return NULL;
} }
const struct media_v2_link *mp_device_find_link_to(const MPDevice *device, uint32_t sink) const struct media_v2_link *
mp_device_find_link_to(const MPDevice *device, uint32_t sink)
{ {
for (int i = 0; i < device->num_links; ++i) { for (int i = 0; i < device->num_links; ++i) {
if (device->links[i].sink_id == sink) { if (device->links[i].sink_id == sink) {
return &device->links[i]; return &device->links[i];
} }
} }
return NULL; return NULL;
} }
const struct media_v2_link *mp_device_find_link_between(const MPDevice *device, uint32_t source, uint32_t sink) const struct media_v2_link *
mp_device_find_link_between(const MPDevice *device, uint32_t source, uint32_t sink)
{ {
for (int i = 0; i < device->num_links; ++i) { for (int i = 0; i < device->num_links; ++i) {
if (device->links[i].source_id == source if (device->links[i].source_id == source &&
&& device->links[i].sink_id == sink) { device->links[i].sink_id == sink) {
return &device->links[i]; return &device->links[i];
} }
} }
return NULL; return NULL;
} }
const struct media_v2_link *mp_device_get_link(const MPDevice *device, uint32_t id) const struct media_v2_link *
mp_device_get_link(const MPDevice *device, uint32_t id)
{ {
for (int i = 0; i < device->num_links; ++i) { for (int i = 0; i < device->num_links; ++i) {
if (device->links[i].id == id) { if (device->links[i].id == id) {
return &device->links[i]; return &device->links[i];
} }
} }
return NULL; return NULL;
} }
const struct media_v2_link *mp_device_get_links(const MPDevice *device) const struct media_v2_link *
mp_device_get_links(const MPDevice *device)
{ {
return device->links; return device->links;
} }
size_t mp_device_get_num_links(const MPDevice *device) size_t
mp_device_get_num_links(const MPDevice *device)
{ {
return device->num_links; return device->num_links;
} }
struct _MPDeviceList { struct _MPDeviceList {
MPDevice *device; MPDevice *device;
MPDeviceList *next; MPDeviceList *next;
}; };
MPDeviceList *mp_device_list_new() MPDeviceList *
mp_device_list_new()
{ {
MPDeviceList *current = NULL; MPDeviceList *current = NULL;
// Enumerate media device files // Enumerate media device files
struct dirent *dir; struct dirent *dir;
DIR *d = opendir("/dev"); DIR *d = opendir("/dev");
while ((dir = readdir(d)) != NULL) { while ((dir = readdir(d)) != NULL) {
if (strncmp(dir->d_name, "media", 5) == 0) { if (strncmp(dir->d_name, "media", 5) == 0) {
char path[261]; char path[261];
snprintf(path, 261, "/dev/%s", dir->d_name); snprintf(path, 261, "/dev/%s", dir->d_name);
MPDevice *device = mp_device_open(path); MPDevice *device = mp_device_open(path);
if (device) { if (device) {
MPDeviceList *next = malloc(sizeof(MPDeviceList)); MPDeviceList *next = malloc(sizeof(MPDeviceList));
next->device = device; next->device = device;
next->next = current; next->next = current;
current = next; current = next;
} }
} }
} }
closedir(d); closedir(d);
return current; return current;
} }
void mp_device_list_free(MPDeviceList *device_list) void
mp_device_list_free(MPDeviceList *device_list)
{ {
while (device_list) { while (device_list) {
MPDeviceList *tmp = device_list; MPDeviceList *tmp = device_list;
device_list = tmp->next; device_list = tmp->next;
mp_device_close(tmp->device); mp_device_close(tmp->device);
free(tmp); free(tmp);
} }
} }
MPDevice *mp_device_list_find_remove(MPDeviceList **list, const char *driver_name) MPDevice *
mp_device_list_find_remove(MPDeviceList **list, const char *driver_name)
{ {
MPDevice *found_device = NULL; MPDevice *found_device = NULL;
int length = strlen(driver_name); int length = strlen(driver_name);
while (*list) { while (*list) {
MPDevice *device = mp_device_list_get(*list); MPDevice *device = mp_device_list_get(*list);
const struct media_device_info *info = mp_device_get_info(device); const struct media_device_info *info = mp_device_get_info(device);
if (strncmp(info->driver, driver_name, length) == 0) { if (strncmp(info->driver, driver_name, length) == 0) {
found_device = mp_device_list_remove(list); found_device = mp_device_list_remove(list);
break; break;
} }
list = &(*list)->next; list = &(*list)->next;
} }
return found_device; return found_device;
} }
MPDevice *mp_device_list_remove(MPDeviceList **device_list) MPDevice *
mp_device_list_remove(MPDeviceList **device_list)
{ {
MPDevice *device = (*device_list)->device; MPDevice *device = (*device_list)->device;
if ((*device_list)->next) { if ((*device_list)->next) {
MPDeviceList *tmp = (*device_list)->next; MPDeviceList *tmp = (*device_list)->next;
**device_list = *tmp; **device_list = *tmp;
free(tmp); free(tmp);
} else { } else {
free(*device_list); free(*device_list);
*device_list = NULL; *device_list = NULL;
} }
return device; return device;
} }
MPDevice *mp_device_list_get(const MPDeviceList *device_list) MPDevice *
mp_device_list_get(const MPDeviceList *device_list)
{ {
return device_list->device; return device_list->device;
} }
MPDeviceList *mp_device_list_next(const MPDeviceList *device_list) MPDeviceList *
mp_device_list_next(const MPDeviceList *device_list)
{ {
return device_list->next; return device_list->next;
} }

View File

@ -5,7 +5,8 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
bool mp_find_device_path(struct media_v2_intf_devnode devnode, char *path, int length); bool mp_find_device_path(struct media_v2_intf_devnode devnode, char *path,
int length);
typedef struct _MPDevice MPDevice; typedef struct _MPDevice MPDevice;
@ -14,26 +15,37 @@ MPDevice *mp_device_open(const char *path);
MPDevice *mp_device_new(int fd); MPDevice *mp_device_new(int fd);
void mp_device_close(MPDevice *device); void mp_device_close(MPDevice *device);
bool mp_device_setup_link(MPDevice *device, uint32_t source_pad_id, uint32_t sink_pad_id, bool enabled); bool mp_device_setup_link(MPDevice *device, uint32_t source_pad_id,
uint32_t sink_pad_id, bool enabled);
const struct media_device_info *mp_device_get_info(const MPDevice *device); const struct media_device_info *mp_device_get_info(const MPDevice *device);
const struct media_v2_entity *mp_device_find_entity(const MPDevice *device, const char *driver_name); const struct media_v2_entity *mp_device_find_entity(const MPDevice *device,
const struct media_v2_entity *mp_device_find_entity_type(const MPDevice *device, const uint32_t type); const char *driver_name);
const struct media_v2_entity *mp_device_get_entity(const MPDevice *device, uint32_t id); const struct media_v2_entity *mp_device_find_entity_type(const MPDevice *device,
const uint32_t type);
const struct media_v2_entity *mp_device_get_entity(const MPDevice *device,
uint32_t id);
const struct media_v2_entity *mp_device_get_entities(const MPDevice *device); const struct media_v2_entity *mp_device_get_entities(const MPDevice *device);
size_t mp_device_get_num_entities(const MPDevice *device); size_t mp_device_get_num_entities(const MPDevice *device);
const struct media_v2_interface *mp_device_find_entity_interface(const MPDevice *device, uint32_t entity_id); const struct media_v2_interface *
const struct media_v2_interface *mp_device_get_interface(const MPDevice *device, uint32_t id); mp_device_find_entity_interface(const MPDevice *device, uint32_t entity_id);
const struct media_v2_interface *mp_device_get_interface(const MPDevice *device,
uint32_t id);
const struct media_v2_interface *mp_device_get_interfaces(const MPDevice *device); const struct media_v2_interface *mp_device_get_interfaces(const MPDevice *device);
size_t mp_device_get_num_interfaces(const MPDevice *device); size_t mp_device_get_num_interfaces(const MPDevice *device);
const struct media_v2_pad *mp_device_get_pad_from_entity(const MPDevice *device, uint32_t entity_id); const struct media_v2_pad *mp_device_get_pad_from_entity(const MPDevice *device,
uint32_t entity_id);
const struct media_v2_pad *mp_device_get_pad(const MPDevice *device, uint32_t id); const struct media_v2_pad *mp_device_get_pad(const MPDevice *device, uint32_t id);
const struct media_v2_pad *mp_device_get_pads(const MPDevice *device); const struct media_v2_pad *mp_device_get_pads(const MPDevice *device);
size_t mp_device_get_num_pads(const MPDevice *device); size_t mp_device_get_num_pads(const MPDevice *device);
const struct media_v2_link *mp_device_find_entity_link(const MPDevice *device, uint32_t entity_id); const struct media_v2_link *mp_device_find_entity_link(const MPDevice *device,
const struct media_v2_link *mp_device_find_link_from(const MPDevice *device, uint32_t source); uint32_t entity_id);
const struct media_v2_link *mp_device_find_link_to(const MPDevice *device, uint32_t sink); const struct media_v2_link *mp_device_find_link_from(const MPDevice *device,
const struct media_v2_link *mp_device_find_link_between(const MPDevice *device, uint32_t source, uint32_t sink); uint32_t source);
const struct media_v2_link *mp_device_find_link_to(const MPDevice *device,
uint32_t sink);
const struct media_v2_link *
mp_device_find_link_between(const MPDevice *device, uint32_t source, uint32_t sink);
const struct media_v2_link *mp_device_get_link(const MPDevice *device, uint32_t id); const struct media_v2_link *mp_device_get_link(const MPDevice *device, uint32_t id);
const struct media_v2_link *mp_device_get_links(const MPDevice *device); const struct media_v2_link *mp_device_get_links(const MPDevice *device);
size_t mp_device_get_num_links(const MPDevice *device); size_t mp_device_get_num_links(const MPDevice *device);
@ -43,7 +55,8 @@ typedef struct _MPDeviceList MPDeviceList;
MPDeviceList *mp_device_list_new(); MPDeviceList *mp_device_list_new();
void mp_device_list_free(MPDeviceList *device_list); void mp_device_list_free(MPDeviceList *device_list);
MPDevice *mp_device_list_find_remove(MPDeviceList **device_list, const char *driver_name); MPDevice *mp_device_list_find_remove(MPDeviceList **device_list,
const char *driver_name);
MPDevice *mp_device_list_remove(MPDeviceList **device_list); MPDevice *mp_device_list_remove(MPDeviceList **device_list);
MPDevice *mp_device_list_get(const MPDeviceList *device_list); MPDevice *mp_device_list_get(const MPDeviceList *device_list);

221
ini.c
View File

@ -25,171 +25,174 @@ https://github.com/benhoyt/inih
#define MAX_NAME 50 #define MAX_NAME 50
/* Strip whitespace chars off end of given string, in place. Return s. */ /* Strip whitespace chars off end of given string, in place. Return s. */
static char* rstrip(char* s) static char *
rstrip(char *s)
{ {
char* p = s + strlen(s); char *p = s + strlen(s);
while (p > s && isspace((unsigned char)(*--p))) while (p > s && isspace((unsigned char)(*--p)))
*p = '\0'; *p = '\0';
return s; return s;
} }
/* Return pointer to first non-whitespace char in given string. */ /* Return pointer to first non-whitespace char in given string. */
static char* lskip(const char* s) static char *
lskip(const char *s)
{ {
while (*s && isspace((unsigned char)(*s))) while (*s && isspace((unsigned char)(*s)))
s++; s++;
return (char*)s; return (char *)s;
} }
/* Return pointer to first char (of chars) or inline comment in given string, /* Return pointer to first char (of chars) or inline comment in given string,
or pointer to null at end of string if neither found. Inline comment must or pointer to null at end of string if neither found. Inline comment must
be prefixed by a whitespace character to register as a comment. */ be prefixed by a whitespace character to register as a comment. */
static char* find_chars_or_comment(const char* s, const char* chars) static char *
find_chars_or_comment(const char *s, const char *chars)
{ {
#if INI_ALLOW_INLINE_COMMENTS #if INI_ALLOW_INLINE_COMMENTS
int was_space = 0; int was_space = 0;
while (*s && (!chars || !strchr(chars, *s)) && while (*s && (!chars || !strchr(chars, *s)) &&
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
was_space = isspace((unsigned char)(*s)); was_space = isspace((unsigned char)(*s));
s++; s++;
} }
#else #else
while (*s && (!chars || !strchr(chars, *s))) { while (*s && (!chars || !strchr(chars, *s))) {
s++; s++;
} }
#endif #endif
return (char*)s; return (char *)s;
} }
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ /* Version of strncpy that ensures dest (size bytes) is null-terminated. */
static char* strncpy0(char* dest, const char* src, size_t size) static char *
strncpy0(char *dest, const char *src, size_t size)
{ {
strncpy(dest, src, size-1); strncpy(dest, src, size - 1);
dest[size - 1] = '\0'; dest[size - 1] = '\0';
return dest; return dest;
} }
/* See documentation in header file. */ /* See documentation in header file. */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, int
void* user) ini_parse_stream(ini_reader reader, void *stream, ini_handler handler, void *user)
{ {
/* Uses a fair bit of stack (use heap instead if you need to) */ /* Uses a fair bit of stack (use heap instead if you need to) */
#if INI_USE_STACK #if INI_USE_STACK
char line[INI_MAX_LINE]; char line[INI_MAX_LINE];
#else #else
char* line; char *line;
#endif #endif
char section[MAX_SECTION] = ""; char section[MAX_SECTION] = "";
char prev_name[MAX_NAME] = ""; char prev_name[MAX_NAME] = "";
char* start; char *start;
char* end; char *end;
char* name; char *name;
char* value; char *value;
int lineno = 0; int lineno = 0;
int error = 0; int error = 0;
#if !INI_USE_STACK #if !INI_USE_STACK
line = (char*)malloc(INI_MAX_LINE); line = (char *)malloc(INI_MAX_LINE);
if (!line) { if (!line) {
return -2; return -2;
} }
#endif #endif
/* Scan through stream line by line */ /* Scan through stream line by line */
while (reader(line, INI_MAX_LINE, stream) != NULL) { while (reader(line, INI_MAX_LINE, stream) != NULL) {
lineno++; lineno++;
start = line; start = line;
#if INI_ALLOW_BOM #if INI_ALLOW_BOM
if (lineno == 1 && (unsigned char)start[0] == 0xEF && if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
(unsigned char)start[1] == 0xBB && (unsigned char)start[1] == 0xBB &&
(unsigned char)start[2] == 0xBF) { (unsigned char)start[2] == 0xBF) {
start += 3; start += 3;
} }
#endif #endif
start = lskip(rstrip(start)); start = lskip(rstrip(start));
if (*start == ';' || *start == '#') { if (*start == ';' || *start == '#') {
/* Per Python configparser, allow both ; and # comments at the /* Per Python configparser, allow both ; and # comments at the
start of a line */ start of a line */
} }
#if INI_ALLOW_MULTILINE #if INI_ALLOW_MULTILINE
else if (*prev_name && *start && start > line) { else if (*prev_name && *start && start > line) {
/* Non-blank line with leading whitespace, treat as continuation /* Non-blank line with leading whitespace, treat as continuation
of previous name's value (as per Python configparser). */ of previous name's value (as per Python configparser). */
if (!handler(user, section, prev_name, start) && !error) if (!handler(user, section, prev_name, start) && !error)
error = lineno; error = lineno;
} }
#endif #endif
else if (*start == '[') { else if (*start == '[') {
/* A "[section]" line */ /* A "[section]" line */
end = find_chars_or_comment(start + 1, "]"); end = find_chars_or_comment(start + 1, "]");
if (*end == ']') { if (*end == ']') {
*end = '\0'; *end = '\0';
strncpy0(section, start + 1, sizeof(section)); strncpy0(section, start + 1, sizeof(section));
*prev_name = '\0'; *prev_name = '\0';
} } else if (!error) {
else if (!error) { /* No ']' found on section line */
/* No ']' found on section line */ error = lineno;
error = lineno; }
} } else if (*start) {
} /* Not a comment, must be a name[=:]value pair */
else if (*start) { end = find_chars_or_comment(start, "=:");
/* Not a comment, must be a name[=:]value pair */ if (*end == '=' || *end == ':') {
end = find_chars_or_comment(start, "=:"); *end = '\0';
if (*end == '=' || *end == ':') { name = rstrip(start);
*end = '\0'; value = lskip(end + 1);
name = rstrip(start);
value = lskip(end + 1);
#if INI_ALLOW_INLINE_COMMENTS #if INI_ALLOW_INLINE_COMMENTS
end = find_chars_or_comment(value, NULL); end = find_chars_or_comment(value, NULL);
if (*end) if (*end)
*end = '\0'; *end = '\0';
#endif #endif
rstrip(value); rstrip(value);
/* Valid name[=:]value pair found, call handler */ /* Valid name[=:]value pair found, call handler */
strncpy0(prev_name, name, sizeof(prev_name)); strncpy0(prev_name, name, sizeof(prev_name));
if (!handler(user, section, name, value) && !error) if (!handler(user, section, name, value) && !error)
error = lineno; error = lineno;
memset(value, 0, strlen(value)); memset(value, 0, strlen(value));
} } else if (!error) {
else if (!error) { /* No '=' or ':' found on name[=:]value line */
/* No '=' or ':' found on name[=:]value line */ error = lineno;
error = lineno; }
} }
}
#if INI_STOP_ON_FIRST_ERROR #if INI_STOP_ON_FIRST_ERROR
if (error) if (error)
break; break;
#endif #endif
} }
#if !INI_USE_STACK #if !INI_USE_STACK
free(line); free(line);
#endif #endif
return error; return error;
} }
/* See documentation in header file. */ /* See documentation in header file. */
int ini_parse_file(FILE* file, ini_handler handler, void* user) int
ini_parse_file(FILE *file, ini_handler handler, void *user)
{ {
return ini_parse_stream((ini_reader)fgets, file, handler, user); return ini_parse_stream((ini_reader)fgets, file, handler, user);
} }
/* See documentation in header file. */ /* See documentation in header file. */
int ini_parse(const char* filename, ini_handler handler, void* user) int
ini_parse(const char *filename, ini_handler handler, void *user)
{ {
FILE* file; FILE *file;
int error; int error;
file = fopen(filename, "r"); file = fopen(filename, "r");
if (!file) if (!file)
return -1; return -1;
error = ini_parse_file(file, handler, user); error = ini_parse_file(file, handler, user);
fclose(file); fclose(file);
return error; return error;
} }

14
ini.h
View File

@ -18,11 +18,11 @@ extern "C" {
#include <stdio.h> #include <stdio.h>
/* Typedef for prototype of handler function. */ /* Typedef for prototype of handler function. */
typedef int (*ini_handler)(void* user, const char* section, typedef int (*ini_handler)(void *user, const char *section, const char *name,
const char* name, const char* value); const char *value);
/* Typedef for prototype of fgets-style reader function. */ /* Typedef for prototype of fgets-style reader function. */
typedef char* (*ini_reader)(char* str, int num, void* stream); typedef char *(*ini_reader)(char *str, int num, void *stream);
/* Parse given INI-style file. May have [section]s, name=value pairs /* Parse given INI-style file. May have [section]s, name=value pairs
(whitespace stripped), and comments starting with ';' (semicolon). Section (whitespace stripped), and comments starting with ';' (semicolon). Section
@ -37,16 +37,16 @@ typedef char* (*ini_reader)(char* str, int num, void* stream);
stop on first error), -1 on file open error, or -2 on memory allocation stop on first error), -1 on file open error, or -2 on memory allocation
error (only when INI_USE_STACK is zero). error (only when INI_USE_STACK is zero).
*/ */
int ini_parse(const char* filename, ini_handler handler, void* user); int ini_parse(const char *filename, ini_handler handler, void *user);
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't /* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
close the file when it's finished -- the caller must do that. */ close the file when it's finished -- the caller must do that. */
int ini_parse_file(FILE* file, ini_handler handler, void* user); int ini_parse_file(FILE *file, ini_handler handler, void *user);
/* Same as ini_parse(), but takes an ini_reader function pointer instead of /* Same as ini_parse(), but takes an ini_reader function pointer instead of
filename. Used for implementing custom or string-based I/O. */ filename. Used for implementing custom or string-based I/O. */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, int ini_parse_stream(ini_reader reader, void *stream, ini_handler handler,
void* user); void *user);
/* Nonzero to allow multi-line value parsing, in the style of Python's /* Nonzero to allow multi-line value parsing, in the style of Python's
configparser. If allowed, ini_parse() will call the handler with the same configparser. If allowed, ini_parse() will call the handler with the same

View File

@ -13,51 +13,49 @@
#include <stdio.h> #include <stdio.h>
struct media_link_info { struct media_link_info {
unsigned int source_entity_id; unsigned int source_entity_id;
unsigned int target_entity_id; unsigned int target_entity_id;
char source_fname[260]; char source_fname[260];
char target_fname[260]; char target_fname[260];
}; };
struct camera_info { struct camera_info {
size_t device_index; size_t device_index;
unsigned int pad_id; unsigned int pad_id;
char dev_fname[260]; char dev_fname[260];
int fd; int fd;
MPCamera *camera; MPCamera *camera;
int gain_ctrl; int gain_ctrl;
int gain_max; int gain_max;
bool has_auto_focus_continuous; bool has_auto_focus_continuous;
bool has_auto_focus_start; bool has_auto_focus_start;
// unsigned int entity_id;
// enum v4l2_buf_type type;
// unsigned int entity_id; // char media_dev_fname[260];
// enum v4l2_buf_type type; // char video_dev_fname[260];
// int media_fd;
// char media_dev_fname[260]; // struct mp_media_link media_links[MP_MAX_LINKS];
// char video_dev_fname[260]; // int num_media_links;
// int media_fd;
// int gain_ctrl;
// struct mp_media_link media_links[MP_MAX_LINKS];
// int num_media_links;
// int gain_ctrl;
}; };
struct device_info { struct device_info {
const char *media_dev_name; // owned by camera config const char *media_dev_name; // owned by camera config
MPDevice *device; MPDevice *device;
unsigned int interface_pad_id; unsigned int interface_pad_id;
int video_fd; int video_fd;
}; };
static struct camera_info cameras[MP_MAX_CAMERAS]; static struct camera_info cameras[MP_MAX_CAMERAS];
@ -78,11 +76,11 @@ static int preview_width;
static int preview_height; static int preview_height;
struct control_state { struct control_state {
bool gain_is_manual; bool gain_is_manual;
int gain; int gain;
bool exposure_is_manual; bool exposure_is_manual;
int exposure; int exposure;
}; };
static struct control_state desired_controls = {}; static struct control_state desired_controls = {};
@ -93,413 +91,450 @@ static bool want_focus = false;
static MPPipeline *pipeline; static MPPipeline *pipeline;
static GSource *capture_source; static GSource *capture_source;
static void setup_camera(MPDeviceList **device_list, const struct mp_camera_config *config) static void
setup_camera(MPDeviceList **device_list, const struct mp_camera_config *config)
{ {
// Find device info // Find device info
size_t device_index = 0; size_t device_index = 0;
for (; device_index < num_devices; ++device_index) { for (; device_index < num_devices; ++device_index) {
if (strcmp(config->media_dev_name, devices[device_index].media_dev_name) == 0) { if (strcmp(config->media_dev_name,
break; devices[device_index].media_dev_name) == 0) {
} break;
} }
}
if (device_index == num_devices) if (device_index == num_devices) {
{ device_index = num_devices;
device_index = num_devices;
// Initialize new device // Initialize new device
struct device_info *info = &devices[device_index]; struct device_info *info = &devices[device_index];
info->media_dev_name = config->media_dev_name; info->media_dev_name = config->media_dev_name;
info->device = mp_device_list_find_remove(device_list, info->media_dev_name); info->device = mp_device_list_find_remove(device_list,
if (!info->device) { info->media_dev_name);
g_printerr("Could not find /dev/media* node matching '%s'\n", info->media_dev_name); if (!info->device) {
exit(EXIT_FAILURE); g_printerr("Could not find /dev/media* node matching '%s'\n",
} info->media_dev_name);
exit(EXIT_FAILURE);
}
const struct media_v2_entity *entity = mp_device_find_entity_type(info->device, MEDIA_ENT_F_IO_V4L); const struct media_v2_entity *entity =
if (!entity) { mp_device_find_entity_type(info->device, MEDIA_ENT_F_IO_V4L);
g_printerr("Could not find device video entity\n"); if (!entity) {
exit(EXIT_FAILURE); g_printerr("Could not find device video entity\n");
} exit(EXIT_FAILURE);
}
const struct media_v2_pad *pad = mp_device_get_pad_from_entity(info->device, entity->id); const struct media_v2_pad *pad =
info->interface_pad_id = pad->id; mp_device_get_pad_from_entity(info->device, entity->id);
info->interface_pad_id = pad->id;
const struct media_v2_interface *interface = mp_device_find_entity_interface(info->device, entity->id); const struct media_v2_interface *interface =
char dev_name[260]; mp_device_find_entity_interface(info->device, entity->id);
if (!mp_find_device_path(interface->devnode, dev_name, 260)) { char dev_name[260];
g_printerr("Could not find video path\n"); if (!mp_find_device_path(interface->devnode, dev_name, 260)) {
exit(EXIT_FAILURE); g_printerr("Could not find video path\n");
} exit(EXIT_FAILURE);
}
info->video_fd = open(dev_name, O_RDWR); info->video_fd = open(dev_name, O_RDWR);
if (info->video_fd == -1) { if (info->video_fd == -1) {
g_printerr("Could not open %s\n", dev_name); g_printerr("Could not open %s\n", dev_name);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
++num_devices; ++num_devices;
} }
{ {
struct camera_info *info = &cameras[config->index]; struct camera_info *info = &cameras[config->index];
struct device_info *dev_info = &devices[device_index]; struct device_info *dev_info = &devices[device_index];
info->device_index = device_index; info->device_index = device_index;
const struct media_v2_entity *entity = mp_device_find_entity(dev_info->device, config->dev_name); const struct media_v2_entity *entity =
if (!entity) { mp_device_find_entity(dev_info->device, config->dev_name);
g_printerr("Could not find camera entity matching '%s'\n", config->dev_name); if (!entity) {
exit(EXIT_FAILURE); g_printerr("Could not find camera entity matching '%s'\n",
} config->dev_name);
exit(EXIT_FAILURE);
}
const struct media_v2_pad *pad = mp_device_get_pad_from_entity(dev_info->device, entity->id); const struct media_v2_pad *pad =
mp_device_get_pad_from_entity(dev_info->device, entity->id);
info->pad_id = pad->id; info->pad_id = pad->id;
// Make sure the camera starts out as disabled // Make sure the camera starts out as disabled
mp_device_setup_link( mp_device_setup_link(dev_info->device, info->pad_id,
dev_info->device, dev_info->interface_pad_id, false);
info->pad_id,
dev_info->interface_pad_id,
false);
const struct media_v2_interface *interface = mp_device_find_entity_interface(dev_info->device, entity->id); const struct media_v2_interface *interface =
mp_device_find_entity_interface(dev_info->device,
entity->id);
if (!mp_find_device_path(interface->devnode, info->dev_fname, 260)) { if (!mp_find_device_path(interface->devnode, info->dev_fname, 260)) {
g_printerr("Could not find camera device path\n"); g_printerr("Could not find camera device path\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
info->fd = open(info->dev_fname, O_RDWR); info->fd = open(info->dev_fname, O_RDWR);
if (info->fd == -1) { if (info->fd == -1) {
g_printerr("Could not open %s\n", info->dev_fname); g_printerr("Could not open %s\n", info->dev_fname);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
info->camera = mp_camera_new(dev_info->video_fd, info->fd); info->camera = mp_camera_new(dev_info->video_fd, info->fd);
// Trigger continuous auto focus if the sensor supports it // Trigger continuous auto focus if the sensor supports it
if (mp_camera_query_control(info->camera, V4L2_CID_FOCUS_AUTO, NULL)) { if (mp_camera_query_control(info->camera, V4L2_CID_FOCUS_AUTO,
info->has_auto_focus_continuous = true; NULL)) {
mp_camera_control_set_bool(info->camera, V4L2_CID_FOCUS_AUTO, true); info->has_auto_focus_continuous = true;
} mp_camera_control_set_bool(info->camera, V4L2_CID_FOCUS_AUTO,
if (mp_camera_query_control(info->camera, V4L2_CID_AUTO_FOCUS_START, NULL)) { true);
info->has_auto_focus_start = true; }
} if (mp_camera_query_control(info->camera, V4L2_CID_AUTO_FOCUS_START,
NULL)) {
info->has_auto_focus_start = true;
}
MPControl control; MPControl control;
if (mp_camera_query_control(info->camera, V4L2_CID_GAIN, &control)) { if (mp_camera_query_control(info->camera, V4L2_CID_GAIN, &control)) {
info->gain_ctrl = V4L2_CID_GAIN; info->gain_ctrl = V4L2_CID_GAIN;
info->gain_max = control.max; info->gain_max = control.max;
} } else if (mp_camera_query_control(
else if (mp_camera_query_control(info->camera, V4L2_CID_ANALOGUE_GAIN, &control)) { info->camera, V4L2_CID_ANALOGUE_GAIN, &control)) {
info->gain_ctrl = V4L2_CID_ANALOGUE_GAIN; info->gain_ctrl = V4L2_CID_ANALOGUE_GAIN;
info->gain_max = control.max; info->gain_max = control.max;
} }
} }
} }
static void setup(MPPipeline *pipeline, const void *data) static void
setup(MPPipeline *pipeline, const void *data)
{ {
MPDeviceList *device_list = mp_device_list_new(); MPDeviceList *device_list = mp_device_list_new();
for (size_t i = 0; i < MP_MAX_CAMERAS; ++i) { for (size_t i = 0; i < MP_MAX_CAMERAS; ++i) {
const struct mp_camera_config *config = mp_get_camera_config(i); const struct mp_camera_config *config = mp_get_camera_config(i);
if (!config) { if (!config) {
break; break;
} }
setup_camera(&device_list, config); setup_camera(&device_list, config);
} }
mp_device_list_free(device_list); mp_device_list_free(device_list);
} }
void mp_io_pipeline_start() void
mp_io_pipeline_start()
{ {
mp_process_pipeline_start(); mp_process_pipeline_start();
pipeline = mp_pipeline_new(); pipeline = mp_pipeline_new();
mp_pipeline_invoke(pipeline, setup, NULL, 0); mp_pipeline_invoke(pipeline, setup, NULL, 0);
} }
void mp_io_pipeline_stop() void
mp_io_pipeline_stop()
{ {
if (capture_source) { if (capture_source) {
g_source_destroy(capture_source); g_source_destroy(capture_source);
} }
mp_pipeline_free(pipeline); mp_pipeline_free(pipeline);
mp_process_pipeline_stop(); mp_process_pipeline_stop();
} }
static void static void
update_process_pipeline() update_process_pipeline()
{ {
struct camera_info *info = &cameras[camera->index]; struct camera_info *info = &cameras[camera->index];
// Grab the latest control values // Grab the latest control values
if (!current_controls.gain_is_manual) { if (!current_controls.gain_is_manual) {
current_controls.gain = mp_camera_control_get_int32(info->camera, info->gain_ctrl); current_controls.gain =
} mp_camera_control_get_int32(info->camera, info->gain_ctrl);
if (!current_controls.exposure_is_manual) { }
current_controls.exposure = mp_camera_control_get_int32(info->camera, V4L2_CID_EXPOSURE); if (!current_controls.exposure_is_manual) {
} current_controls.exposure =
mp_camera_control_get_int32(info->camera, V4L2_CID_EXPOSURE);
}
struct mp_process_pipeline_state pipeline_state = { struct mp_process_pipeline_state pipeline_state = {
.camera = camera, .camera = camera,
.mode = mode, .mode = mode,
.burst_length = burst_length, .burst_length = burst_length,
.preview_width = preview_width, .preview_width = preview_width,
.preview_height = preview_height, .preview_height = preview_height,
.gain_is_manual = current_controls.gain_is_manual, .gain_is_manual = current_controls.gain_is_manual,
.gain = current_controls.gain, .gain = current_controls.gain,
.gain_max = info->gain_max, .gain_max = info->gain_max,
.exposure_is_manual = current_controls.exposure_is_manual, .exposure_is_manual = current_controls.exposure_is_manual,
.exposure = current_controls.exposure, .exposure = current_controls.exposure,
.has_auto_focus_continuous = info->has_auto_focus_continuous, .has_auto_focus_continuous = info->has_auto_focus_continuous,
.has_auto_focus_start = info->has_auto_focus_start, .has_auto_focus_start = info->has_auto_focus_start,
}; };
mp_process_pipeline_update_state(&pipeline_state); mp_process_pipeline_update_state(&pipeline_state);
} }
static void static void
focus(MPPipeline *pipeline, const void *data) focus(MPPipeline *pipeline, const void *data)
{ {
want_focus = true; want_focus = true;
} }
void mp_io_pipeline_focus() void
mp_io_pipeline_focus()
{ {
mp_pipeline_invoke(pipeline, focus, NULL, 0); mp_pipeline_invoke(pipeline, focus, NULL, 0);
} }
static void static void
capture(MPPipeline *pipeline, const void *data) capture(MPPipeline *pipeline, const void *data)
{ {
struct camera_info *info = &cameras[camera->index]; struct camera_info *info = &cameras[camera->index];
captures_remaining = burst_length; captures_remaining = burst_length;
// Disable the autogain/exposure while taking the burst // Disable the autogain/exposure while taking the burst
mp_camera_control_set_int32(info->camera, V4L2_CID_AUTOGAIN, 0); mp_camera_control_set_int32(info->camera, V4L2_CID_AUTOGAIN, 0);
mp_camera_control_set_int32(info->camera, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL); mp_camera_control_set_int32(info->camera, V4L2_CID_EXPOSURE_AUTO,
V4L2_EXPOSURE_MANUAL);
// Change camera mode for capturing // Change camera mode for capturing
mp_camera_stop_capture(info->camera); mp_camera_stop_capture(info->camera);
mode = camera->capture_mode; mode = camera->capture_mode;
mp_camera_set_mode(info->camera, &mode); mp_camera_set_mode(info->camera, &mode);
just_switched_mode = true; just_switched_mode = true;
mp_camera_start_capture(info->camera); mp_camera_start_capture(info->camera);
update_process_pipeline(); update_process_pipeline();
mp_process_pipeline_capture(); mp_process_pipeline_capture();
} }
void mp_io_pipeline_capture() void
mp_io_pipeline_capture()
{ {
mp_pipeline_invoke(pipeline, capture, NULL, 0); mp_pipeline_invoke(pipeline, capture, NULL, 0);
} }
static void static void
update_controls() update_controls()
{ {
// Don't update controls while capturing // Don't update controls while capturing
if (captures_remaining > 0) { if (captures_remaining > 0) {
return; return;
} }
struct camera_info *info = &cameras[camera->index]; struct camera_info *info = &cameras[camera->index];
if (want_focus) { if (want_focus) {
if (info->has_auto_focus_continuous) { if (info->has_auto_focus_continuous) {
mp_camera_control_set_bool(info->camera, V4L2_CID_FOCUS_AUTO, 1); mp_camera_control_set_bool(info->camera, V4L2_CID_FOCUS_AUTO,
} else if (info->has_auto_focus_start) { 1);
mp_camera_control_set_bool(info->camera, V4L2_CID_AUTO_FOCUS_START, 1); } else if (info->has_auto_focus_start) {
} mp_camera_control_set_bool(info->camera,
V4L2_CID_AUTO_FOCUS_START, 1);
}
want_focus = false; want_focus = false;
} }
if (current_controls.gain_is_manual != desired_controls.gain_is_manual) { if (current_controls.gain_is_manual != desired_controls.gain_is_manual) {
mp_camera_control_set_bool(info->camera, V4L2_CID_AUTOGAIN, !desired_controls.gain_is_manual); mp_camera_control_set_bool(info->camera, V4L2_CID_AUTOGAIN,
} !desired_controls.gain_is_manual);
}
if (desired_controls.gain_is_manual && current_controls.gain != desired_controls.gain) { if (desired_controls.gain_is_manual &&
mp_camera_control_set_int32(info->camera, info->gain_ctrl, desired_controls.gain); current_controls.gain != desired_controls.gain) {
} mp_camera_control_set_int32(info->camera, info->gain_ctrl,
desired_controls.gain);
}
if (current_controls.exposure_is_manual != desired_controls.exposure_is_manual) { if (current_controls.exposure_is_manual !=
mp_camera_control_set_int32( desired_controls.exposure_is_manual) {
info->camera, mp_camera_control_set_int32(info->camera, V4L2_CID_EXPOSURE_AUTO,
V4L2_CID_EXPOSURE_AUTO, desired_controls.exposure_is_manual ?
desired_controls.exposure_is_manual ? V4L2_EXPOSURE_MANUAL : V4L2_EXPOSURE_AUTO); V4L2_EXPOSURE_MANUAL :
} V4L2_EXPOSURE_AUTO);
}
if (desired_controls.exposure_is_manual && current_controls.exposure != desired_controls.exposure) { if (desired_controls.exposure_is_manual &&
mp_camera_control_set_int32(info->camera, V4L2_CID_EXPOSURE, desired_controls.exposure); current_controls.exposure != desired_controls.exposure) {
} mp_camera_control_set_int32(info->camera, V4L2_CID_EXPOSURE,
desired_controls.exposure);
}
current_controls = desired_controls; current_controls = desired_controls;
} }
static void static void
on_frame(MPImage image, void *data) on_frame(MPImage image, void *data)
{ {
// Only update controls right after a frame was captured // Only update controls right after a frame was captured
update_controls(); update_controls();
// When the mode is switched while capturing we get a couple blank frames, // When the mode is switched while capturing we get a couple blank frames,
// presumably from buffers made ready during the switch. Ignore these. // presumably from buffers made ready during the switch. Ignore these.
if (just_switched_mode) if (just_switched_mode) {
{ if (blank_frame_count < 20) {
if (blank_frame_count < 20) { // Only check a 50x50 area
// Only check a 50x50 area size_t test_size =
size_t test_size = MIN(50, image.width) * MIN(50, image.height); MIN(50, image.width) * MIN(50, image.height);
bool image_is_blank = true; bool image_is_blank = true;
for (size_t i = 0; i < test_size; ++i) { for (size_t i = 0; i < test_size; ++i) {
if (image.data[i] != 0) { if (image.data[i] != 0) {
image_is_blank = false; image_is_blank = false;
} }
} }
if (image_is_blank) { if (image_is_blank) {
++blank_frame_count; ++blank_frame_count;
return; return;
} }
} else { } else {
printf("Blank image limit reached, resulting capture may be blank\n"); printf("Blank image limit reached, resulting capture may be blank\n");
} }
just_switched_mode = false; just_switched_mode = false;
blank_frame_count = 0; blank_frame_count = 0;
} }
// Copy from the camera buffer // Copy from the camera buffer
size_t size = mp_pixel_format_width_to_bytes(image.pixel_format, image.width) * image.height; size_t size =
uint8_t *buffer = malloc(size); mp_pixel_format_width_to_bytes(image.pixel_format, image.width) *
memcpy(buffer, image.data, size); image.height;
uint8_t *buffer = malloc(size);
memcpy(buffer, image.data, size);
image.data = buffer; image.data = buffer;
// Send the image off for processing // Send the image off for processing
mp_process_pipeline_process_image(image); mp_process_pipeline_process_image(image);
if (captures_remaining > 0) { if (captures_remaining > 0) {
--captures_remaining; --captures_remaining;
if (captures_remaining == 0) { if (captures_remaining == 0) {
struct camera_info *info = &cameras[camera->index]; struct camera_info *info = &cameras[camera->index];
// Restore the auto exposure and gain if needed // Restore the auto exposure and gain if needed
if (!current_controls.exposure_is_manual) { if (!current_controls.exposure_is_manual) {
mp_camera_control_set_int32(info->camera, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_AUTO); mp_camera_control_set_int32(info->camera,
} V4L2_CID_EXPOSURE_AUTO,
V4L2_EXPOSURE_AUTO);
}
if (!current_controls.gain_is_manual) { if (!current_controls.gain_is_manual) {
mp_camera_control_set_bool(info->camera, V4L2_CID_AUTOGAIN, true); mp_camera_control_set_bool(info->camera,
} V4L2_CID_AUTOGAIN, true);
}
// Go back to preview mode // Go back to preview mode
mp_camera_stop_capture(info->camera); mp_camera_stop_capture(info->camera);
mode = camera->preview_mode; mode = camera->preview_mode;
mp_camera_set_mode(info->camera, &mode); mp_camera_set_mode(info->camera, &mode);
just_switched_mode = true; just_switched_mode = true;
mp_camera_start_capture(info->camera); mp_camera_start_capture(info->camera);
update_process_pipeline(); update_process_pipeline();
} }
} }
} }
static void static void
update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state) update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state)
{ {
// Make sure the state isn't updated more than it needs to be by checking // Make sure the state isn't updated more than it needs to be by checking
// whether this state change actually changes anything. // whether this state change actually changes anything.
bool has_changed = false; bool has_changed = false;
if (camera != state->camera) { if (camera != state->camera) {
has_changed = true; has_changed = true;
if (camera) { if (camera) {
struct camera_info *info = &cameras[camera->index]; struct camera_info *info = &cameras[camera->index];
struct device_info *dev_info = &devices[info->device_index]; struct device_info *dev_info = &devices[info->device_index];
mp_camera_stop_capture(info->camera); mp_camera_stop_capture(info->camera);
mp_device_setup_link( mp_device_setup_link(dev_info->device, info->pad_id,
dev_info->device, dev_info->interface_pad_id, false);
info->pad_id, }
dev_info->interface_pad_id,
false);
}
if (capture_source) { if (capture_source) {
g_source_destroy(capture_source); g_source_destroy(capture_source);
capture_source = NULL; capture_source = NULL;
} }
camera = state->camera; camera = state->camera;
if (camera) { if (camera) {
struct camera_info *info = &cameras[camera->index]; struct camera_info *info = &cameras[camera->index];
struct device_info *dev_info = &devices[info->device_index]; struct device_info *dev_info = &devices[info->device_index];
mp_device_setup_link( mp_device_setup_link(dev_info->device, info->pad_id,
dev_info->device, dev_info->interface_pad_id, true);
info->pad_id,
dev_info->interface_pad_id,
true);
mode = camera->preview_mode; mode = camera->preview_mode;
mp_camera_set_mode(info->camera, &mode); mp_camera_set_mode(info->camera, &mode);
mp_camera_start_capture(info->camera); mp_camera_start_capture(info->camera);
capture_source = mp_pipeline_add_capture_source(pipeline, info->camera, on_frame, NULL); capture_source = mp_pipeline_add_capture_source(
pipeline, info->camera, on_frame, NULL);
current_controls.gain_is_manual = mp_camera_control_get_int32(info->camera, V4L2_CID_EXPOSURE_AUTO) == V4L2_EXPOSURE_MANUAL; current_controls.gain_is_manual =
current_controls.gain = mp_camera_control_get_int32(info->camera, info->gain_ctrl); mp_camera_control_get_int32(
info->camera, V4L2_CID_EXPOSURE_AUTO) ==
V4L2_EXPOSURE_MANUAL;
current_controls.gain = mp_camera_control_get_int32(
info->camera, info->gain_ctrl);
current_controls.exposure_is_manual = mp_camera_control_get_bool(info->camera, V4L2_CID_AUTOGAIN) == 0; current_controls.exposure_is_manual =
current_controls.exposure = mp_camera_control_get_int32(info->camera, V4L2_CID_EXPOSURE); mp_camera_control_get_bool(info->camera,
} V4L2_CID_AUTOGAIN) == 0;
} current_controls.exposure = mp_camera_control_get_int32(
info->camera, V4L2_CID_EXPOSURE);
}
}
has_changed = has_changed has_changed = has_changed || burst_length != state->burst_length ||
|| burst_length != state->burst_length preview_width != state->preview_width ||
|| preview_width != state->preview_width preview_height != state->preview_height;
|| preview_height != state->preview_height;
burst_length = state->burst_length; burst_length = state->burst_length;
preview_width = state->preview_width; preview_width = state->preview_width;
preview_height = state->preview_height; preview_height = state->preview_height;
if (camera) { if (camera) {
struct control_state previous_desired = desired_controls; struct control_state previous_desired = desired_controls;
desired_controls.gain_is_manual = state->gain_is_manual; desired_controls.gain_is_manual = state->gain_is_manual;
desired_controls.gain = state->gain; desired_controls.gain = state->gain;
desired_controls.exposure_is_manual = state->exposure_is_manual; desired_controls.exposure_is_manual = state->exposure_is_manual;
desired_controls.exposure = state->exposure; desired_controls.exposure = state->exposure;
has_changed = has_changed || memcmp(&previous_desired, &desired_controls, sizeof(struct control_state)) != 0; has_changed =
} has_changed || memcmp(&previous_desired, &desired_controls,
sizeof(struct control_state)) != 0;
}
assert(has_changed); assert(has_changed);
update_process_pipeline(); update_process_pipeline();
} }
void mp_io_pipeline_update_state(const struct mp_io_pipeline_state *state) void
mp_io_pipeline_update_state(const struct mp_io_pipeline_state *state)
{ {
mp_pipeline_invoke(pipeline, (MPPipelineCallback)update_state, state, sizeof(struct mp_io_pipeline_state)); mp_pipeline_invoke(pipeline, (MPPipelineCallback)update_state, state,
sizeof(struct mp_io_pipeline_state));
} }

View File

@ -3,18 +3,18 @@
#include "camera_config.h" #include "camera_config.h"
struct mp_io_pipeline_state { struct mp_io_pipeline_state {
const struct mp_camera_config *camera; const struct mp_camera_config *camera;
int burst_length; int burst_length;
int preview_width; int preview_width;
int preview_height; int preview_height;
bool gain_is_manual; bool gain_is_manual;
int gain; int gain;
bool exposure_is_manual; bool exposure_is_manual;
int exposure; int exposure;
}; };
void mp_io_pipeline_start(); void mp_io_pipeline_start();

224
main.c
View File

@ -21,10 +21,7 @@
#include "quickpreview.h" #include "quickpreview.h"
#include "io_pipeline.h" #include "io_pipeline.h"
enum user_control { enum user_control { USER_CONTROL_ISO, USER_CONTROL_SHUTTER };
USER_CONTROL_ISO,
USER_CONTROL_SHUTTER
};
static bool camera_is_initialized = false; static bool camera_is_initialized = false;
static const struct mp_camera_config *camera = NULL; static const struct mp_camera_config *camera = NULL;
@ -121,17 +118,14 @@ update_state(const struct mp_main_state *state)
return false; return false;
} }
void mp_main_update_state(const struct mp_main_state *state) void
mp_main_update_state(const struct mp_main_state *state)
{ {
struct mp_main_state *state_copy = malloc(sizeof(struct mp_main_state)); struct mp_main_state *state_copy = malloc(sizeof(struct mp_main_state));
*state_copy = *state; *state_copy = *state;
g_main_context_invoke_full( g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT_IDLE,
g_main_context_default(), (GSourceFunc)update_state, state_copy, free);
G_PRIORITY_DEFAULT_IDLE,
(GSourceFunc)update_state,
state_copy,
free);
} }
static bool static bool
@ -145,19 +139,16 @@ set_preview(cairo_surface_t *image)
return false; return false;
} }
void mp_main_set_preview(cairo_surface_t *image) void
mp_main_set_preview(cairo_surface_t *image)
{ {
g_main_context_invoke_full( g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT_IDLE,
g_main_context_default(), (GSourceFunc)set_preview, image, NULL);
G_PRIORITY_DEFAULT_IDLE,
(GSourceFunc)set_preview,
image,
NULL);
} }
static void static void
draw_surface_scaled_centered( draw_surface_scaled_centered(cairo_t *cr, uint32_t dst_width, uint32_t dst_height,
cairo_t *cr, uint32_t dst_width, uint32_t dst_height, cairo_surface_t *surface) cairo_surface_t *surface)
{ {
cairo_save(cr); cairo_save(cr);
@ -165,7 +156,7 @@ draw_surface_scaled_centered(
int width = cairo_image_surface_get_width(surface); int width = cairo_image_surface_get_width(surface);
int height = cairo_image_surface_get_height(surface); int height = cairo_image_surface_get_height(surface);
double scale = MIN(dst_width / (double) width, dst_height / (double) height); double scale = MIN(dst_width / (double)width, dst_height / (double)height);
cairo_scale(cr, scale, scale); cairo_scale(cr, scale, scale);
cairo_translate(cr, -width / 2, -height / 2); cairo_translate(cr, -width / 2, -height / 2);
@ -181,10 +172,8 @@ capture_completed(const char *fname)
strncpy(last_path, fname, 260); strncpy(last_path, fname, 260);
// Create a thumbnail from the current surface // Create a thumbnail from the current surface
cairo_surface_t *thumb = cairo_image_surface_create( cairo_surface_t *thumb =
CAIRO_FORMAT_ARGB32, cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 24, 24);
24,
24);
cairo_t *cr = cairo_create(thumb); cairo_t *cr = cairo_create(thumb);
draw_surface_scaled_centered(cr, 24, 24, surface); draw_surface_scaled_centered(cr, 24, 24, surface);
@ -196,16 +185,13 @@ capture_completed(const char *fname)
return false; return false;
} }
void mp_main_capture_completed(const char *fname) void
mp_main_capture_completed(const char *fname)
{ {
gchar *name = g_strdup(fname); gchar *name = g_strdup(fname);
g_main_context_invoke_full( g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT_IDLE,
g_main_context_default(), (GSourceFunc)capture_completed, name, g_free);
G_PRIORITY_DEFAULT_IDLE,
(GSourceFunc)capture_completed,
name,
g_free);
} }
static void static void
@ -217,14 +203,16 @@ draw_controls()
char shutterangle[6]; char shutterangle[6];
if (exposure_is_manual) { if (exposure_is_manual) {
temp = (int)((float)exposure / (float)camera->capture_mode.height * 360); temp = (int)((float)exposure / (float)camera->capture_mode.height *
360);
sprintf(shutterangle, "%d\u00b0", temp); sprintf(shutterangle, "%d\u00b0", temp);
} else { } else {
sprintf(shutterangle, "auto"); sprintf(shutterangle, "auto");
} }
if (gain_is_manual) { if (gain_is_manual) {
temp = remap(gain - 1, 0, gain_max, camera->iso_min, camera->iso_max); temp = remap(gain - 1, 0, gain_max, camera->iso_min,
camera->iso_max);
sprintf(iso, "%d", temp); sprintf(iso, "%d", temp);
} else { } else {
sprintf(iso, "auto"); sprintf(iso, "auto");
@ -237,16 +225,18 @@ draw_controls()
if (gtk_widget_get_window(preview) == NULL) { if (gtk_widget_get_window(preview) == NULL) {
return; return;
} }
status_surface = gdk_window_create_similar_surface(gtk_widget_get_window(preview), status_surface =
CAIRO_CONTENT_COLOR_ALPHA, gdk_window_create_similar_surface(gtk_widget_get_window(preview),
preview_width, 32); CAIRO_CONTENT_COLOR_ALPHA,
preview_width, 32);
cr = cairo_create(status_surface); cr = cairo_create(status_surface);
cairo_set_source_rgba(cr, 0, 0, 0, 0.0); cairo_set_source_rgba(cr, 0, 0, 0, 0.0);
cairo_paint(cr); cairo_paint(cr);
// Draw the outlines for the headings // Draw the outlines for the headings
cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size(cr, 9); cairo_set_font_size(cr, 9);
cairo_set_source_rgba(cr, 0, 0, 0, 1); cairo_set_source_rgba(cr, 0, 0, 0, 1);
@ -266,7 +256,8 @@ draw_controls()
cairo_show_text(cr, "Shutter"); cairo_show_text(cr, "Shutter");
// Draw the outlines for the values // Draw the outlines for the values
cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, 11); cairo_set_font_size(cr, 11);
cairo_set_source_rgba(cr, 0, 0, 0, 1); cairo_set_source_rgba(cr, 0, 0, 0, 1);
@ -286,7 +277,7 @@ draw_controls()
cairo_show_text(cr, shutterangle); cairo_show_text(cr, shutterangle);
cairo_destroy(cr); cairo_destroy(cr);
gtk_widget_queue_draw_area(preview, 0, 0, preview_width, 32); gtk_widget_queue_draw_area(preview, 0, 0, preview_width, 32);
} }
@ -302,7 +293,8 @@ preview_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
// Draw camera preview // Draw camera preview
if (surface) { if (surface) {
draw_surface_scaled_centered(cr, preview_width, preview_height, surface); draw_surface_scaled_centered(cr, preview_width, preview_height,
surface);
} }
// Draw control overlay // Draw control overlay
@ -311,15 +303,14 @@ preview_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
return FALSE; return FALSE;
} }
static gboolean static gboolean
preview_configure(GtkWidget *widget, GdkEventConfigure *event) preview_configure(GtkWidget *widget, GdkEventConfigure *event)
{ {
int new_preview_width = gtk_widget_get_allocated_width(widget); int new_preview_width = gtk_widget_get_allocated_width(widget);
int new_preview_height = gtk_widget_get_allocated_height(widget); int new_preview_height = gtk_widget_get_allocated_height(widget);
if (preview_width != new_preview_width || preview_height != new_preview_height) if (preview_width != new_preview_width ||
{ preview_height != new_preview_height) {
preview_width = new_preview_width; preview_width = new_preview_width;
preview_height = new_preview_height; preview_height = new_preview_height;
update_io_pipeline(); update_io_pipeline();
@ -336,11 +327,11 @@ on_open_last_clicked(GtkWidget *widget, gpointer user_data)
char uri[275]; char uri[275];
GError *error = NULL; GError *error = NULL;
if(strlen(last_path) == 0) { if (strlen(last_path) == 0) {
return; return;
} }
sprintf(uri, "file://%s.tiff", last_path); sprintf(uri, "file://%s.tiff", last_path);
if(!g_app_info_launch_default_for_uri(uri, NULL, &error)){ if (!g_app_info_launch_default_for_uri(uri, NULL, &error)) {
g_printerr("Could not launch image viewer: %s\n", error->message); g_printerr("Could not launch image viewer: %s\n", error->message);
} }
} }
@ -351,7 +342,7 @@ on_open_directory_clicked(GtkWidget *widget, gpointer user_data)
char uri[270]; char uri[270];
GError *error = NULL; GError *error = NULL;
sprintf(uri, "file://%s/Pictures", getenv("HOME")); sprintf(uri, "file://%s/Pictures", getenv("HOME"));
if(!g_app_info_launch_default_for_uri(uri, NULL, &error)){ if (!g_app_info_launch_default_for_uri(uri, NULL, &error)) {
g_printerr("Could not launch image viewer: %s\n", error->message); g_printerr("Could not launch image viewer: %s\n", error->message);
} }
} }
@ -377,11 +368,12 @@ on_preview_tap(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
gtk_widget_show(control_box); gtk_widget_show(control_box);
} }
if (event->x < 60 ) { if (event->x < 60) {
// ISO // ISO
current_control = USER_CONTROL_ISO; current_control = USER_CONTROL_ISO;
gtk_label_set_text(GTK_LABEL(control_name), "ISO"); gtk_label_set_text(GTK_LABEL(control_name), "ISO");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto), !gain_is_manual); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto),
!gain_is_manual);
gtk_adjustment_set_lower(control_slider, 0.0); gtk_adjustment_set_lower(control_slider, 0.0);
gtk_adjustment_set_upper(control_slider, (float)gain_max); gtk_adjustment_set_upper(control_slider, (float)gain_max);
gtk_adjustment_set_value(control_slider, (double)gain); gtk_adjustment_set_value(control_slider, (double)gain);
@ -390,7 +382,8 @@ on_preview_tap(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
// Shutter angle // Shutter angle
current_control = USER_CONTROL_SHUTTER; current_control = USER_CONTROL_SHUTTER;
gtk_label_set_text(GTK_LABEL(control_name), "Shutter"); gtk_label_set_text(GTK_LABEL(control_name), "Shutter");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto), !exposure_is_manual); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto),
!exposure_is_manual);
gtk_adjustment_set_lower(control_slider, 1.0); gtk_adjustment_set_lower(control_slider, 1.0);
gtk_adjustment_set_upper(control_slider, 360.0); gtk_adjustment_set_upper(control_slider, 360.0);
gtk_adjustment_set_value(control_slider, (double)exposure); gtk_adjustment_set_value(control_slider, (double)exposure);
@ -415,7 +408,8 @@ void
on_camera_switch_clicked(GtkWidget *widget, gpointer user_data) on_camera_switch_clicked(GtkWidget *widget, gpointer user_data)
{ {
size_t next_index = camera->index + 1; size_t next_index = camera->index + 1;
const struct mp_camera_config *next_camera = mp_get_camera_config(next_index); const struct mp_camera_config *next_camera =
mp_get_camera_config(next_index);
if (!next_camera) { if (!next_camera) {
next_index = 0; next_index = 0;
@ -445,18 +439,18 @@ on_control_auto_toggled(GtkToggleButton *widget, gpointer user_data)
bool has_changed; bool has_changed;
switch (current_control) { switch (current_control) {
case USER_CONTROL_ISO: case USER_CONTROL_ISO:
if (gain_is_manual != is_manual) { if (gain_is_manual != is_manual) {
gain_is_manual = is_manual; gain_is_manual = is_manual;
has_changed = true; has_changed = true;
} }
break; break;
case USER_CONTROL_SHUTTER: case USER_CONTROL_SHUTTER:
if (exposure_is_manual != is_manual) { if (exposure_is_manual != is_manual) {
exposure_is_manual = is_manual; exposure_is_manual = is_manual;
has_changed = true; has_changed = true;
} }
break; break;
} }
if (has_changed) { if (has_changed) {
@ -472,22 +466,22 @@ on_control_slider_changed(GtkAdjustment *widget, gpointer user_data)
bool has_changed = false; bool has_changed = false;
switch (current_control) { switch (current_control) {
case USER_CONTROL_ISO: case USER_CONTROL_ISO:
if (value != gain) { if (value != gain) {
gain = (int)value; gain = (int)value;
has_changed = true; has_changed = true;
}
break;
case USER_CONTROL_SHUTTER:
{
// So far all sensors use exposure time in number of sensor rows
int new_exposure = (int)(value / 360.0 * camera->capture_mode.height);
if (new_exposure != exposure) {
exposure = new_exposure;
has_changed = true;
}
break;
} }
break;
case USER_CONTROL_SHUTTER: {
// So far all sensors use exposure time in number of sensor rows
int new_exposure =
(int)(value / 360.0 * camera->capture_mode.height);
if (new_exposure != exposure) {
exposure = new_exposure;
has_changed = true;
}
break;
}
} }
if (has_changed) { if (has_changed) {
@ -504,19 +498,26 @@ main(int argc, char *argv[])
setenv("LC_NUMERIC", "C", 1); setenv("LC_NUMERIC", "C", 1);
gtk_init(&argc, &argv); gtk_init(&argc, &argv);
g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme", TRUE, NULL); g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme",
GtkBuilder *builder = gtk_builder_new_from_resource("/org/postmarketos/Megapixels/camera.glade"); TRUE, NULL);
GtkBuilder *builder = gtk_builder_new_from_resource(
"/org/postmarketos/Megapixels/camera.glade");
GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window")); GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
GtkWidget *shutter = GTK_WIDGET(gtk_builder_get_object(builder, "shutter")); GtkWidget *shutter = GTK_WIDGET(gtk_builder_get_object(builder, "shutter"));
GtkWidget *switch_btn = GTK_WIDGET(gtk_builder_get_object(builder, "switch_camera")); GtkWidget *switch_btn =
GtkWidget *settings_btn = GTK_WIDGET(gtk_builder_get_object(builder, "settings")); GTK_WIDGET(gtk_builder_get_object(builder, "switch_camera"));
GtkWidget *settings_back = GTK_WIDGET(gtk_builder_get_object(builder, "settings_back")); GtkWidget *settings_btn =
GtkWidget *error_close = GTK_WIDGET(gtk_builder_get_object(builder, "error_close")); GTK_WIDGET(gtk_builder_get_object(builder, "settings"));
GtkWidget *open_last = GTK_WIDGET(gtk_builder_get_object(builder, "open_last")); GtkWidget *settings_back =
GtkWidget *open_directory = GTK_WIDGET(gtk_builder_get_object(builder, "open_directory")); GTK_WIDGET(gtk_builder_get_object(builder, "settings_back"));
GtkWidget *error_close =
GTK_WIDGET(gtk_builder_get_object(builder, "error_close"));
GtkWidget *open_last =
GTK_WIDGET(gtk_builder_get_object(builder, "open_last"));
GtkWidget *open_directory =
GTK_WIDGET(gtk_builder_get_object(builder, "open_directory"));
preview = GTK_WIDGET(gtk_builder_get_object(builder, "preview")); preview = GTK_WIDGET(gtk_builder_get_object(builder, "preview"));
error_box = GTK_WIDGET(gtk_builder_get_object(builder, "error_box")); error_box = GTK_WIDGET(gtk_builder_get_object(builder, "error_box"));
error_message = GTK_WIDGET(gtk_builder_get_object(builder, "error_message")); error_message = GTK_WIDGET(gtk_builder_get_object(builder, "error_message"));
@ -524,38 +525,49 @@ main(int argc, char *argv[])
thumb_last = GTK_WIDGET(gtk_builder_get_object(builder, "thumb_last")); thumb_last = GTK_WIDGET(gtk_builder_get_object(builder, "thumb_last"));
control_box = GTK_WIDGET(gtk_builder_get_object(builder, "control_box")); control_box = GTK_WIDGET(gtk_builder_get_object(builder, "control_box"));
control_name = GTK_WIDGET(gtk_builder_get_object(builder, "control_name")); control_name = GTK_WIDGET(gtk_builder_get_object(builder, "control_name"));
control_slider = GTK_ADJUSTMENT(gtk_builder_get_object(builder, "control_adj")); control_slider =
GTK_ADJUSTMENT(gtk_builder_get_object(builder, "control_adj"));
control_auto = GTK_WIDGET(gtk_builder_get_object(builder, "control_auto")); control_auto = GTK_WIDGET(gtk_builder_get_object(builder, "control_auto"));
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(shutter, "clicked", G_CALLBACK(on_shutter_clicked), NULL); g_signal_connect(shutter, "clicked", G_CALLBACK(on_shutter_clicked), NULL);
g_signal_connect(error_close, "clicked", G_CALLBACK(on_error_close_clicked), NULL); g_signal_connect(error_close, "clicked", G_CALLBACK(on_error_close_clicked),
g_signal_connect(switch_btn, "clicked", G_CALLBACK(on_camera_switch_clicked), NULL); NULL);
g_signal_connect(settings_btn, "clicked", G_CALLBACK(on_settings_btn_clicked), NULL); g_signal_connect(switch_btn, "clicked", G_CALLBACK(on_camera_switch_clicked),
g_signal_connect(settings_back, "clicked", G_CALLBACK(on_back_clicked), NULL); NULL);
g_signal_connect(open_last, "clicked", G_CALLBACK(on_open_last_clicked), NULL); g_signal_connect(settings_btn, "clicked",
g_signal_connect(open_directory, "clicked", G_CALLBACK(on_open_directory_clicked), NULL); G_CALLBACK(on_settings_btn_clicked), NULL);
g_signal_connect(settings_back, "clicked", G_CALLBACK(on_back_clicked),
NULL);
g_signal_connect(open_last, "clicked", G_CALLBACK(on_open_last_clicked),
NULL);
g_signal_connect(open_directory, "clicked",
G_CALLBACK(on_open_directory_clicked), NULL);
g_signal_connect(preview, "draw", G_CALLBACK(preview_draw), NULL); g_signal_connect(preview, "draw", G_CALLBACK(preview_draw), NULL);
g_signal_connect(preview, "configure-event", G_CALLBACK(preview_configure), NULL); g_signal_connect(preview, "configure-event", G_CALLBACK(preview_configure),
NULL);
gtk_widget_set_events(preview, gtk_widget_get_events(preview) | gtk_widget_set_events(preview, gtk_widget_get_events(preview) |
GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK); GDK_BUTTON_PRESS_MASK |
g_signal_connect(preview, "button-press-event", G_CALLBACK(on_preview_tap), NULL); GDK_POINTER_MOTION_MASK);
g_signal_connect(control_auto, "toggled", G_CALLBACK(on_control_auto_toggled), NULL); g_signal_connect(preview, "button-press-event", G_CALLBACK(on_preview_tap),
g_signal_connect(control_slider, "value-changed", G_CALLBACK(on_control_slider_changed), NULL); NULL);
g_signal_connect(control_auto, "toggled",
G_CALLBACK(on_control_auto_toggled), NULL);
g_signal_connect(control_slider, "value-changed",
G_CALLBACK(on_control_slider_changed), NULL);
GtkCssProvider *provider = gtk_css_provider_new(); GtkCssProvider *provider = gtk_css_provider_new();
if (access("camera.css", F_OK) != -1) { if (access("camera.css", F_OK) != -1) {
gtk_css_provider_load_from_path(provider, "camera.css", NULL); gtk_css_provider_load_from_path(provider, "camera.css", NULL);
} else { } else {
gtk_css_provider_load_from_resource(provider, "/org/postmarketos/Megapixels/camera.css"); gtk_css_provider_load_from_resource(
provider, "/org/postmarketos/Megapixels/camera.css");
} }
GtkStyleContext *context = gtk_widget_get_style_context(error_box); GtkStyleContext *context = gtk_widget_get_style_context(error_box);
gtk_style_context_add_provider(context, gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
GTK_STYLE_PROVIDER_PRIORITY_USER);
context = gtk_widget_get_style_context(control_box); context = gtk_widget_get_style_context(control_box);
gtk_style_context_add_provider(context, gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
GTK_STYLE_PROVIDER_PRIORITY_USER);
mp_io_pipeline_start(); mp_io_pipeline_start();

21
main.h
View File

@ -4,18 +4,18 @@
#include "gtk/gtk.h" #include "gtk/gtk.h"
struct mp_main_state { struct mp_main_state {
const struct mp_camera_config *camera; const struct mp_camera_config *camera;
MPCameraMode mode; MPCameraMode mode;
bool gain_is_manual; bool gain_is_manual;
int gain; int gain;
int gain_max; int gain_max;
bool exposure_is_manual; bool exposure_is_manual;
int exposure; int exposure;
bool has_auto_focus_continuous; bool has_auto_focus_continuous;
bool has_auto_focus_start; bool has_auto_focus_start;
}; };
void mp_main_update_state(const struct mp_main_state *state); void mp_main_update_state(const struct mp_main_state *state);
@ -23,5 +23,4 @@ void mp_main_update_state(const struct mp_main_state *state);
void mp_main_set_preview(cairo_surface_t *image); void mp_main_set_preview(cairo_surface_t *image);
void mp_main_capture_completed(const char *fname); void mp_main_capture_completed(const char *fname);
int int remap(int value, int input_min, int input_max, int output_min, int output_max);
remap(int value, int input_min, int input_max, int output_min, int output_max);

View File

@ -1,14 +1,15 @@
void void
multiply_matrices(float a[9], float b[9], float out[9]) { multiply_matrices(float a[9], float b[9], float out[9])
{
// zero out target matrix // zero out target matrix
for(int i=0; i<9; i++) { for (int i = 0; i < 9; i++) {
out[i] = 0; out[i] = 0;
} }
for(int i=0; i<3; i++) { for (int i = 0; i < 3; i++) {
for(int j=0; j<3; j++) { for (int j = 0; j < 3; j++) {
for(int k=0; k<3; k++) { for (int k = 0; k < 3; k++) {
out[i*3+j] += a[i*3+k] * b[k*3+j]; out[i * 3 + j] += a[i * 3 + k] * b[k * 3 + j];
} }
} }
} }

View File

@ -5,104 +5,106 @@
#include <assert.h> #include <assert.h>
struct _MPPipeline { struct _MPPipeline {
GMainContext *main_context; GMainContext *main_context;
GMainLoop *main_loop; GMainLoop *main_loop;
pthread_t thread; pthread_t thread;
}; };
static void *thread_main_loop(void *arg) static void *
thread_main_loop(void *arg)
{ {
MPPipeline *pipeline = arg; MPPipeline *pipeline = arg;
g_main_loop_run(pipeline->main_loop); g_main_loop_run(pipeline->main_loop);
return NULL; return NULL;
} }
MPPipeline *mp_pipeline_new() MPPipeline *
mp_pipeline_new()
{ {
MPPipeline *pipeline = malloc(sizeof(MPPipeline)); MPPipeline *pipeline = malloc(sizeof(MPPipeline));
pipeline->main_context = g_main_context_new(); pipeline->main_context = g_main_context_new();
pipeline->main_loop = g_main_loop_new(pipeline->main_context, false); pipeline->main_loop = g_main_loop_new(pipeline->main_context, false);
int res = pthread_create( int res =
&pipeline->thread, NULL, thread_main_loop, pipeline); pthread_create(&pipeline->thread, NULL, thread_main_loop, pipeline);
assert(res == 0); assert(res == 0);
return pipeline; return pipeline;
} }
struct invoke_args { struct invoke_args {
MPPipeline *pipeline; MPPipeline *pipeline;
MPPipelineCallback callback; MPPipelineCallback callback;
}; };
static bool invoke_impl(struct invoke_args *args) static bool
invoke_impl(struct invoke_args *args)
{ {
args->callback(args->pipeline, args + 1); args->callback(args->pipeline, args + 1);
return false; return false;
} }
void mp_pipeline_invoke(MPPipeline *pipeline, MPPipelineCallback callback, const void *data, size_t size) void
mp_pipeline_invoke(MPPipeline *pipeline, MPPipelineCallback callback,
const void *data, size_t size)
{ {
if (pthread_self() != pipeline->thread) { if (pthread_self() != pipeline->thread) {
struct invoke_args *args = malloc(sizeof(struct invoke_args) + size); struct invoke_args *args = malloc(sizeof(struct invoke_args) + size);
args->pipeline = pipeline; args->pipeline = pipeline;
args->callback = callback; args->callback = callback;
if (size > 0) { if (size > 0) {
memcpy(args + 1, data, size); memcpy(args + 1, data, size);
} }
g_main_context_invoke_full( g_main_context_invoke_full(pipeline->main_context,
pipeline->main_context, G_PRIORITY_DEFAULT,
G_PRIORITY_DEFAULT, (GSourceFunc)invoke_impl, args, free);
(GSourceFunc)invoke_impl, } else {
args, callback(pipeline, data);
free); }
} else {
callback(pipeline, data);
}
} }
void mp_pipeline_free(MPPipeline *pipeline) void
mp_pipeline_free(MPPipeline *pipeline)
{ {
g_main_loop_quit(pipeline->main_loop); g_main_loop_quit(pipeline->main_loop);
// Force the main thread loop to wake up, otherwise we might not exit // Force the main thread loop to wake up, otherwise we might not exit
g_main_context_wakeup(pipeline->main_context); g_main_context_wakeup(pipeline->main_context);
void *r; void *r;
pthread_join(pipeline->thread, &r); pthread_join(pipeline->thread, &r);
free(pipeline); free(pipeline);
} }
struct capture_source_args struct capture_source_args {
{ MPCamera *camera;
MPCamera *camera; void (*callback)(MPImage, void *);
void (*callback)(MPImage, void *); void *user_data;
void *user_data;
}; };
static bool on_capture(int fd, GIOCondition condition, struct capture_source_args *args) static bool
on_capture(int fd, GIOCondition condition, struct capture_source_args *args)
{ {
mp_camera_capture_image(args->camera, args->callback, args->user_data); mp_camera_capture_image(args->camera, args->callback, args->user_data);
return true; return true;
} }
// Not thread safe // Not thread safe
GSource *mp_pipeline_add_capture_source(MPPipeline *pipeline, MPCamera *camera, void (*callback)(MPImage, void *), void *user_data) GSource *
mp_pipeline_add_capture_source(MPPipeline *pipeline, MPCamera *camera,
void (*callback)(MPImage, void *), void *user_data)
{ {
int video_fd = mp_camera_get_video_fd(camera); int video_fd = mp_camera_get_video_fd(camera);
GSource *video_source = g_unix_fd_source_new(video_fd, G_IO_IN); GSource *video_source = g_unix_fd_source_new(video_fd, G_IO_IN);
struct capture_source_args *args = malloc(sizeof(struct capture_source_args)); struct capture_source_args *args =
args->camera = camera; malloc(sizeof(struct capture_source_args));
args->callback = callback; args->camera = camera;
args->user_data = user_data; args->callback = callback;
g_source_set_callback( args->user_data = user_data;
video_source, g_source_set_callback(video_source, (GSourceFunc)on_capture, args, free);
(GSourceFunc)on_capture, g_source_attach(video_source, pipeline->main_context);
args, return video_source;
free);
g_source_attach(video_source, pipeline->main_context);
return video_source;
} }

View File

@ -9,7 +9,10 @@ typedef struct _MPPipeline MPPipeline;
typedef void (*MPPipelineCallback)(MPPipeline *, const void *); typedef void (*MPPipelineCallback)(MPPipeline *, const void *);
MPPipeline *mp_pipeline_new(); MPPipeline *mp_pipeline_new();
void mp_pipeline_invoke(MPPipeline *pipeline, MPPipelineCallback callback, const void *data, size_t size); void mp_pipeline_invoke(MPPipeline *pipeline, MPPipelineCallback callback,
const void *data, size_t size);
void mp_pipeline_free(MPPipeline *pipeline); void mp_pipeline_free(MPPipeline *pipeline);
GSource *mp_pipeline_add_capture_source(MPPipeline *pipeline, MPCamera *camera, void (*callback)(MPImage, void *), void *user_data); GSource *mp_pipeline_add_capture_source(MPPipeline *pipeline, MPCamera *camera,
void (*callback)(MPImage, void *),
void *user_data);

View File

@ -12,11 +12,8 @@
#define TIFFTAG_FORWARDMATRIX1 50964 #define TIFFTAG_FORWARDMATRIX1 50964
static const float colormatrix_srgb[] = { static const float colormatrix_srgb[] = { 3.2409, -1.5373, -0.4986, -0.9692, 1.8759,
3.2409, -1.5373, -0.4986, 0.0415, 0.0556, -0.2039, 1.0569 };
-0.9692, 1.8759, 0.0415,
0.0556, -0.2039, 1.0569
};
static MPPipeline *pipeline; static MPPipeline *pipeline;
@ -49,373 +46,378 @@ static char capture_fname[255];
static void static void
register_custom_tiff_tags(TIFF *tif) register_custom_tiff_tags(TIFF *tif)
{ {
static const TIFFFieldInfo custom_fields[] = { static const TIFFFieldInfo custom_fields[] = {
{TIFFTAG_FORWARDMATRIX1, -1, -1, TIFF_SRATIONAL, FIELD_CUSTOM, 1, 1, "ForwardMatrix1"}, { TIFFTAG_FORWARDMATRIX1, -1, -1, TIFF_SRATIONAL, FIELD_CUSTOM, 1, 1,
}; "ForwardMatrix1" },
};
// Add missing dng fields // Add missing dng fields
TIFFMergeFieldInfo(tif, custom_fields, sizeof(custom_fields) / sizeof(custom_fields[0])); TIFFMergeFieldInfo(tif, custom_fields,
sizeof(custom_fields) / sizeof(custom_fields[0]));
} }
static bool static bool
find_processor(char *script) find_processor(char *script)
{ {
char *xdg_config_home; char *xdg_config_home;
char filename[] = "postprocess.sh"; char filename[] = "postprocess.sh";
wordexp_t exp_result; wordexp_t exp_result;
// Resolve XDG stuff // Resolve XDG stuff
if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL) { if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL) {
xdg_config_home = "~/.config"; xdg_config_home = "~/.config";
} }
wordexp(xdg_config_home, &exp_result, 0); wordexp(xdg_config_home, &exp_result, 0);
xdg_config_home = strdup(exp_result.we_wordv[0]); xdg_config_home = strdup(exp_result.we_wordv[0]);
wordfree(&exp_result); wordfree(&exp_result);
// Check postprocess.h in the current working directory // Check postprocess.h in the current working directory
sprintf(script, "%s", filename); sprintf(script, "%s", filename);
if(access(script, F_OK) != -1) { if (access(script, F_OK) != -1) {
sprintf(script, "./%s", filename); sprintf(script, "./%s", filename);
printf("Found postprocessor script at %s\n", script); printf("Found postprocessor script at %s\n", script);
return true; return true;
} }
// Check for a script in XDG_CONFIG_HOME // Check for a script in XDG_CONFIG_HOME
sprintf(script, "%s/megapixels/%s", xdg_config_home, filename); sprintf(script, "%s/megapixels/%s", xdg_config_home, filename);
if(access(script, F_OK) != -1) { if (access(script, F_OK) != -1) {
printf("Found postprocessor script at %s\n", script); printf("Found postprocessor script at %s\n", script);
return true; return true;
} }
// Check user overridden /etc/megapixels/postprocessor.sh // Check user overridden /etc/megapixels/postprocessor.sh
sprintf(script, "%s/megapixels/%s", SYSCONFDIR, filename); sprintf(script, "%s/megapixels/%s", SYSCONFDIR, filename);
if(access(script, F_OK) != -1) { if (access(script, F_OK) != -1) {
printf("Found postprocessor script at %s\n", script); printf("Found postprocessor script at %s\n", script);
return true; return true;
} }
// Check packaged /usr/share/megapixels/postprocessor.sh // Check packaged /usr/share/megapixels/postprocessor.sh
sprintf(script, "%s/megapixels/%s", DATADIR, filename); sprintf(script, "%s/megapixels/%s", DATADIR, filename);
if(access(script, F_OK) != -1) { if (access(script, F_OK) != -1) {
printf("Found postprocessor script at %s\n", script); printf("Found postprocessor script at %s\n", script);
return true; return true;
} }
return false; return false;
} }
static void setup(MPPipeline *pipeline, const void *data) static void
setup(MPPipeline *pipeline, const void *data)
{ {
TIFFSetTagExtender(register_custom_tiff_tags); TIFFSetTagExtender(register_custom_tiff_tags);
if (!find_processor(processing_script)) { if (!find_processor(processing_script)) {
g_printerr("Could not find any post-process script\n"); g_printerr("Could not find any post-process script\n");
exit(1); exit(1);
} }
} }
void mp_process_pipeline_start() void
mp_process_pipeline_start()
{ {
pipeline = mp_pipeline_new(); pipeline = mp_pipeline_new();
mp_pipeline_invoke(pipeline, setup, NULL, 0); mp_pipeline_invoke(pipeline, setup, NULL, 0);
} }
void mp_process_pipeline_stop() void
mp_process_pipeline_stop()
{ {
mp_pipeline_free(pipeline); mp_pipeline_free(pipeline);
} }
static void static void
process_image_for_preview(const MPImage *image) process_image_for_preview(const MPImage *image)
{ {
uint32_t surface_width, surface_height, skip; uint32_t surface_width, surface_height, skip;
quick_preview_size( quick_preview_size(&surface_width, &surface_height, &skip, preview_width,
&surface_width, preview_height, image->width, image->height,
&surface_height, image->pixel_format, camera->rotate);
&skip,
preview_width,
preview_height,
image->width,
image->height,
image->pixel_format,
camera->rotate);
cairo_surface_t *surface = cairo_image_surface_create( cairo_surface_t *surface = cairo_image_surface_create(
CAIRO_FORMAT_RGB24, CAIRO_FORMAT_RGB24, surface_width, surface_height);
surface_width,
surface_height);
uint8_t *pixels = cairo_image_surface_get_data(surface); uint8_t *pixels = cairo_image_surface_get_data(surface);
quick_preview( quick_preview((uint32_t *)pixels, surface_width, surface_height, image->data,
(uint32_t *)pixels, image->width, image->height, image->pixel_format,
surface_width, camera->rotate, camera->mirrored,
surface_height, camera->previewmatrix[0] == 0 ? NULL : camera->previewmatrix,
image->data, camera->blacklevel, skip);
image->width,
image->height,
image->pixel_format,
camera->rotate,
camera->mirrored,
camera->previewmatrix[0] == 0 ? NULL : camera->previewmatrix,
camera->blacklevel,
skip);
mp_main_set_preview(surface); mp_main_set_preview(surface);
} }
static void static void
process_image_for_capture(const MPImage *image, int count) process_image_for_capture(const MPImage *image, int count)
{ {
time_t rawtime; time_t rawtime;
time(&rawtime); time(&rawtime);
struct tm tim = *(localtime(&rawtime)); struct tm tim = *(localtime(&rawtime));
char datetime[20] = {0}; char datetime[20] = { 0 };
strftime(datetime, 20, "%Y:%m:%d %H:%M:%S", &tim); strftime(datetime, 20, "%Y:%m:%d %H:%M:%S", &tim);
char fname[255]; char fname[255];
sprintf(fname, "%s/%d.dng", burst_dir, count); sprintf(fname, "%s/%d.dng", burst_dir, count);
TIFF *tif = TIFFOpen(fname, "w"); TIFF *tif = TIFFOpen(fname, "w");
if(!tif) { if (!tif) {
printf("Could not open tiff\n"); printf("Could not open tiff\n");
} }
// Define TIFF thumbnail // Define TIFF thumbnail
TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 1); TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 1);
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, image->width >> 4); TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, image->width >> 4);
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, image->height >> 4); TIFFSetField(tif, TIFFTAG_IMAGELENGTH, image->height >> 4);
TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
TIFFSetField(tif, TIFFTAG_MAKE, mp_get_device_make()); TIFFSetField(tif, TIFFTAG_MAKE, mp_get_device_make());
TIFFSetField(tif, TIFFTAG_MODEL, mp_get_device_model()); TIFFSetField(tif, TIFFTAG_MODEL, mp_get_device_model());
uint16_t orientation; uint16_t orientation;
if (camera->rotate == 0) { if (camera->rotate == 0) {
orientation = camera->mirrored ? ORIENTATION_TOPRIGHT : ORIENTATION_TOPLEFT; orientation = camera->mirrored ? ORIENTATION_TOPRIGHT :
} else if (camera->rotate == 90) { ORIENTATION_TOPLEFT;
orientation = camera->mirrored ? ORIENTATION_RIGHTBOT : ORIENTATION_LEFTBOT; } else if (camera->rotate == 90) {
} else if (camera->rotate == 180) { orientation = camera->mirrored ? ORIENTATION_RIGHTBOT :
orientation = camera->mirrored ? ORIENTATION_BOTLEFT : ORIENTATION_BOTRIGHT; ORIENTATION_LEFTBOT;
} else { } else if (camera->rotate == 180) {
orientation = camera->mirrored ? ORIENTATION_LEFTTOP : ORIENTATION_RIGHTTOP; orientation = camera->mirrored ? ORIENTATION_BOTLEFT :
} ORIENTATION_BOTRIGHT;
TIFFSetField(tif, TIFFTAG_ORIENTATION, orientation); } else {
TIFFSetField(tif, TIFFTAG_DATETIME, datetime); orientation = camera->mirrored ? ORIENTATION_LEFTTOP :
TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); ORIENTATION_RIGHTTOP;
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); }
TIFFSetField(tif, TIFFTAG_SOFTWARE, "Megapixels"); TIFFSetField(tif, TIFFTAG_ORIENTATION, orientation);
long sub_offset = 0; TIFFSetField(tif, TIFFTAG_DATETIME, datetime);
TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &sub_offset); TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
TIFFSetField(tif, TIFFTAG_DNGVERSION, "\001\001\0\0"); TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(tif, TIFFTAG_DNGBACKWARDVERSION, "\001\0\0\0"); TIFFSetField(tif, TIFFTAG_SOFTWARE, "Megapixels");
char uniquecameramodel[255]; long sub_offset = 0;
sprintf(uniquecameramodel, "%s %s", mp_get_device_make(), mp_get_device_model()); TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &sub_offset);
TIFFSetField(tif, TIFFTAG_UNIQUECAMERAMODEL, uniquecameramodel); TIFFSetField(tif, TIFFTAG_DNGVERSION, "\001\001\0\0");
if(camera->colormatrix[0]) { TIFFSetField(tif, TIFFTAG_DNGBACKWARDVERSION, "\001\0\0\0");
TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, camera->colormatrix); char uniquecameramodel[255];
} else { sprintf(uniquecameramodel, "%s %s", mp_get_device_make(),
TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, colormatrix_srgb); mp_get_device_model());
} TIFFSetField(tif, TIFFTAG_UNIQUECAMERAMODEL, uniquecameramodel);
if(camera->forwardmatrix[0]) { if (camera->colormatrix[0]) {
TIFFSetField(tif, TIFFTAG_FORWARDMATRIX1, 9, camera->forwardmatrix); TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, camera->colormatrix);
} } else {
static const float neutral[] = {1.0, 1.0, 1.0}; TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, colormatrix_srgb);
TIFFSetField(tif, TIFFTAG_ASSHOTNEUTRAL, 3, neutral); }
TIFFSetField(tif, TIFFTAG_CALIBRATIONILLUMINANT1, 21); if (camera->forwardmatrix[0]) {
// Write black thumbnail, only windows uses this TIFFSetField(tif, TIFFTAG_FORWARDMATRIX1, 9, camera->forwardmatrix);
{ }
unsigned char *buf = (unsigned char *)calloc(1, (int)image->width >> 4); static const float neutral[] = { 1.0, 1.0, 1.0 };
for (int row = 0; row < image->height>>4; row++) { TIFFSetField(tif, TIFFTAG_ASSHOTNEUTRAL, 3, neutral);
TIFFWriteScanline(tif, buf, row, 0); TIFFSetField(tif, TIFFTAG_CALIBRATIONILLUMINANT1, 21);
} // Write black thumbnail, only windows uses this
free(buf); {
} unsigned char *buf =
TIFFWriteDirectory(tif); (unsigned char *)calloc(1, (int)image->width >> 4);
for (int row = 0; row < (image->height >> 4); row++) {
TIFFWriteScanline(tif, buf, row, 0);
}
free(buf);
}
TIFFWriteDirectory(tif);
// Define main photo // Define main photo
TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0); TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0);
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, image->width); TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, image->width);
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, image->height); TIFFSetField(tif, TIFFTAG_IMAGELENGTH, image->height);
TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA); TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);
TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
static const short cfapatterndim[] = {2, 2}; static const short cfapatterndim[] = { 2, 2 };
TIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, cfapatterndim); TIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, cfapatterndim);
TIFFSetField(tif, TIFFTAG_CFAPATTERN, "\002\001\001\000"); // BGGR TIFFSetField(tif, TIFFTAG_CFAPATTERN, "\002\001\001\000"); // BGGR
if(camera->whitelevel) { if (camera->whitelevel) {
TIFFSetField(tif, TIFFTAG_WHITELEVEL, 1, &camera->whitelevel); TIFFSetField(tif, TIFFTAG_WHITELEVEL, 1, &camera->whitelevel);
} }
if(camera->blacklevel) { if (camera->blacklevel) {
TIFFSetField(tif, TIFFTAG_BLACKLEVEL, 1, &camera->blacklevel); TIFFSetField(tif, TIFFTAG_BLACKLEVEL, 1, &camera->blacklevel);
} }
TIFFCheckpointDirectory(tif); TIFFCheckpointDirectory(tif);
printf("Writing frame to %s\n", fname); printf("Writing frame to %s\n", fname);
unsigned char *pLine = (unsigned char*)malloc(image->width); unsigned char *pLine = (unsigned char *)malloc(image->width);
for(int row = 0; row < image->height; row++){ for (int row = 0; row < image->height; row++) {
TIFFWriteScanline(tif, image->data + (row * image->width), row, 0); TIFFWriteScanline(tif, image->data + (row * image->width), row, 0);
} }
free(pLine); free(pLine);
TIFFWriteDirectory(tif); TIFFWriteDirectory(tif);
// Add an EXIF block to the tiff // Add an EXIF block to the tiff
TIFFCreateEXIFDirectory(tif); TIFFCreateEXIFDirectory(tif);
// 1 = manual, 2 = full auto, 3 = aperture priority, 4 = shutter priority // 1 = manual, 2 = full auto, 3 = aperture priority, 4 = shutter priority
if (!exposure_is_manual) { if (!exposure_is_manual) {
TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 2); TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 2);
} else { } else {
TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 1); TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 1);
} }
TIFFSetField(tif, EXIFTAG_EXPOSURETIME, (mode.frame_interval.numerator / (float)mode.frame_interval.denominator) / ((float)image->height / (float)exposure)); TIFFSetField(tif, EXIFTAG_EXPOSURETIME,
uint16_t isospeed[1]; (mode.frame_interval.numerator /
isospeed[0] = (uint16_t)remap(gain - 1, 0, gain_max, camera->iso_min, camera->iso_max); (float)mode.frame_interval.denominator) /
TIFFSetField(tif, EXIFTAG_ISOSPEEDRATINGS, 1, isospeed); ((float)image->height / (float)exposure));
TIFFSetField(tif, EXIFTAG_FLASH, 0); uint16_t isospeed[1];
isospeed[0] = (uint16_t)remap(gain - 1, 0, gain_max, camera->iso_min,
camera->iso_max);
TIFFSetField(tif, EXIFTAG_ISOSPEEDRATINGS, 1, isospeed);
TIFFSetField(tif, EXIFTAG_FLASH, 0);
TIFFSetField(tif, EXIFTAG_DATETIMEORIGINAL, datetime); TIFFSetField(tif, EXIFTAG_DATETIMEORIGINAL, datetime);
TIFFSetField(tif, EXIFTAG_DATETIMEDIGITIZED, datetime); TIFFSetField(tif, EXIFTAG_DATETIMEDIGITIZED, datetime);
if(camera->fnumber) { if (camera->fnumber) {
TIFFSetField(tif, EXIFTAG_FNUMBER, camera->fnumber); TIFFSetField(tif, EXIFTAG_FNUMBER, camera->fnumber);
} }
if(camera->focallength) { if (camera->focallength) {
TIFFSetField(tif, EXIFTAG_FOCALLENGTH, camera->focallength); TIFFSetField(tif, EXIFTAG_FOCALLENGTH, camera->focallength);
} }
if(camera->focallength && camera->cropfactor) { if (camera->focallength && camera->cropfactor) {
TIFFSetField(tif, EXIFTAG_FOCALLENGTHIN35MMFILM, (short)(camera->focallength * camera->cropfactor)); TIFFSetField(tif, EXIFTAG_FOCALLENGTHIN35MMFILM,
} (short)(camera->focallength * camera->cropfactor));
uint64_t exif_offset = 0; }
TIFFWriteCustomDirectory(tif, &exif_offset); uint64_t exif_offset = 0;
TIFFFreeDirectory(tif); TIFFWriteCustomDirectory(tif, &exif_offset);
TIFFFreeDirectory(tif);
// Update exif pointer // Update exif pointer
TIFFSetDirectory(tif, 0); TIFFSetDirectory(tif, 0);
TIFFSetField(tif, TIFFTAG_EXIFIFD, exif_offset); TIFFSetField(tif, TIFFTAG_EXIFIFD, exif_offset);
TIFFRewriteDirectory(tif); TIFFRewriteDirectory(tif);
TIFFClose(tif); TIFFClose(tif);
} }
static void static void
process_capture_burst() process_capture_burst()
{ {
time_t rawtime; time_t rawtime;
time(&rawtime); time(&rawtime);
struct tm tim = *(localtime(&rawtime)); struct tm tim = *(localtime(&rawtime));
char timestamp[30]; char timestamp[30];
strftime(timestamp, 30, "%Y%m%d%H%M%S", &tim); strftime(timestamp, 30, "%Y%m%d%H%M%S", &tim);
sprintf(capture_fname, "%s/Pictures/IMG%s", getenv("HOME"), timestamp); sprintf(capture_fname, "%s/Pictures/IMG%s", getenv("HOME"), timestamp);
// Start post-processing the captured burst // Start post-processing the captured burst
g_print("Post process %s to %s.ext\n", burst_dir, capture_fname); g_print("Post process %s to %s.ext\n", burst_dir, capture_fname);
char command[1024]; char command[1024];
sprintf(command, "%s %s %s &", processing_script, burst_dir, capture_fname); sprintf(command, "%s %s %s &", processing_script, burst_dir, capture_fname);
system(command); system(command);
} }
static void static void
process_image(MPPipeline *pipeline, const MPImage *image) process_image(MPPipeline *pipeline, const MPImage *image)
{ {
assert(image->width == mode.width && image->height == mode.height); assert(image->width == mode.width && image->height == mode.height);
process_image_for_preview(image); process_image_for_preview(image);
if (captures_remaining > 0) { if (captures_remaining > 0) {
int count = burst_length - captures_remaining; int count = burst_length - captures_remaining;
--captures_remaining; --captures_remaining;
process_image_for_capture(image, count); process_image_for_capture(image, count);
if (captures_remaining == 0) { if (captures_remaining == 0) {
process_capture_burst(); process_capture_burst();
mp_main_capture_completed(capture_fname); mp_main_capture_completed(capture_fname);
} }
} }
free(image->data); free(image->data);
++frames_processed; ++frames_processed;
if (captures_remaining == 0) { if (captures_remaining == 0) {
is_capturing = false; is_capturing = false;
} }
} }
void mp_process_pipeline_process_image(MPImage image) void
mp_process_pipeline_process_image(MPImage image)
{ {
// If we haven't processed the previous frame yet, drop this one // If we haven't processed the previous frame yet, drop this one
if (frames_received != frames_processed && !is_capturing) { if (frames_received != frames_processed && !is_capturing) {
printf("Dropped frame at capture\n"); printf("Dropped frame at capture\n");
return; return;
} }
++frames_received; ++frames_received;
mp_pipeline_invoke(pipeline, (MPPipelineCallback)process_image, &image, sizeof(MPImage)); mp_pipeline_invoke(pipeline, (MPPipelineCallback)process_image, &image,
sizeof(MPImage));
} }
static void capture() static void
capture()
{ {
char template[] = "/tmp/megapixels.XXXXXX"; char template[] = "/tmp/megapixels.XXXXXX";
char *tempdir; char *tempdir;
tempdir = mkdtemp(template); tempdir = mkdtemp(template);
if (tempdir == NULL) { if (tempdir == NULL) {
g_printerr("Could not make capture directory %s\n", template); g_printerr("Could not make capture directory %s\n", template);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
strcpy(burst_dir, tempdir); strcpy(burst_dir, tempdir);
captures_remaining = burst_length; captures_remaining = burst_length;
} }
void mp_process_pipeline_capture() void
mp_process_pipeline_capture()
{ {
is_capturing = true; is_capturing = true;
mp_pipeline_invoke(pipeline, capture, NULL, 0); mp_pipeline_invoke(pipeline, capture, NULL, 0);
} }
static void static void
update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state) update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state)
{ {
camera = state->camera; camera = state->camera;
mode = state->mode; mode = state->mode;
burst_length = state->burst_length; burst_length = state->burst_length;
preview_width = state->preview_width; preview_width = state->preview_width;
preview_height = state->preview_height; preview_height = state->preview_height;
// gain_is_manual = state->gain_is_manual; // gain_is_manual = state->gain_is_manual;
gain = state->gain; gain = state->gain;
gain_max = state->gain_max; gain_max = state->gain_max;
exposure_is_manual = state->exposure_is_manual; exposure_is_manual = state->exposure_is_manual;
exposure = state->exposure; exposure = state->exposure;
struct mp_main_state main_state = { struct mp_main_state main_state = {
.camera = camera, .camera = camera,
.mode = mode, .mode = mode,
.gain_is_manual = state->gain_is_manual, .gain_is_manual = state->gain_is_manual,
.gain = gain, .gain = gain,
.gain_max = gain_max, .gain_max = gain_max,
.exposure_is_manual = exposure_is_manual, .exposure_is_manual = exposure_is_manual,
.exposure = exposure, .exposure = exposure,
.has_auto_focus_continuous = state->has_auto_focus_continuous, .has_auto_focus_continuous = state->has_auto_focus_continuous,
.has_auto_focus_start = state->has_auto_focus_start, .has_auto_focus_start = state->has_auto_focus_start,
}; };
mp_main_update_state(&main_state); mp_main_update_state(&main_state);
} }
void mp_process_pipeline_update_state(const struct mp_process_pipeline_state *new_state) void
mp_process_pipeline_update_state(const struct mp_process_pipeline_state *new_state)
{ {
mp_pipeline_invoke(pipeline, (MPPipelineCallback)update_state, new_state, sizeof(struct mp_process_pipeline_state)); mp_pipeline_invoke(pipeline, (MPPipelineCallback)update_state, new_state,
sizeof(struct mp_process_pipeline_state));
} }

View File

@ -3,23 +3,23 @@
#include "camera_config.h" #include "camera_config.h"
struct mp_process_pipeline_state { struct mp_process_pipeline_state {
const struct mp_camera_config *camera; const struct mp_camera_config *camera;
MPCameraMode mode; MPCameraMode mode;
int burst_length; int burst_length;
int preview_width; int preview_width;
int preview_height; int preview_height;
bool gain_is_manual; bool gain_is_manual;
int gain; int gain;
int gain_max; int gain_max;
bool exposure_is_manual; bool exposure_is_manual;
int exposure; int exposure;
bool has_auto_focus_continuous; bool has_auto_focus_continuous;
bool has_auto_focus_start; bool has_auto_focus_start;
}; };
void mp_process_pipeline_start(); void mp_process_pipeline_start();

View File

@ -7,12 +7,14 @@
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
static inline uint32_t pack_rgb(uint8_t r, uint8_t g, uint8_t b) static inline uint32_t
pack_rgb(uint8_t r, uint8_t g, uint8_t b)
{ {
return (r << 16) | (g << 8) | b; return (r << 16) | (g << 8) | b;
} }
static inline uint32_t convert_yuv_to_srgb(uint8_t y, uint8_t u, uint8_t v) static inline uint32_t
convert_yuv_to_srgb(uint8_t y, uint8_t u, uint8_t v)
{ {
uint32_t r = 1.164f * y + 1.596f * (v - 128); uint32_t r = 1.164f * y + 1.596f * (v - 128);
uint32_t g = 1.164f * y - 0.813f * (v - 128) - 0.391f * (u - 128); uint32_t g = 1.164f * y - 0.813f * (v - 128) - 0.391f * (u - 128);
@ -20,15 +22,22 @@ static inline uint32_t convert_yuv_to_srgb(uint8_t y, uint8_t u, uint8_t v)
return pack_rgb(r, g, b); return pack_rgb(r, g, b);
} }
static inline uint32_t apply_colormatrix(uint32_t color, const float *colormatrix) static inline uint32_t
apply_colormatrix(uint32_t color, const float *colormatrix)
{ {
if (!colormatrix) { if (!colormatrix) {
return color; return color;
} }
uint32_t r = (color >> 16) * colormatrix[0] + ((color >> 8) & 0xFF) * colormatrix[1] + (color & 0xFF) * colormatrix[2]; uint32_t r = (color >> 16) * colormatrix[0] +
uint32_t g = (color >> 16) * colormatrix[3] + ((color >> 8) & 0xFF) * colormatrix[4] + (color & 0xFF) * colormatrix[5]; ((color >> 8) & 0xFF) * colormatrix[1] +
uint32_t b = (color >> 16) * colormatrix[6] + ((color >> 8) & 0xFF) * colormatrix[7] + (color & 0xFF) * colormatrix[8]; (color & 0xFF) * colormatrix[2];
uint32_t g = (color >> 16) * colormatrix[3] +
((color >> 8) & 0xFF) * colormatrix[4] +
(color & 0xFF) * colormatrix[5];
uint32_t b = (color >> 16) * colormatrix[6] +
((color >> 8) & 0xFF) * colormatrix[7] +
(color & 0xFF) * colormatrix[8];
// Clip colors // Clip colors
if (r > 0xFF) if (r > 0xFF)
@ -40,7 +49,9 @@ static inline uint32_t apply_colormatrix(uint32_t color, const float *colormatri
return pack_rgb(r, g, b); return pack_rgb(r, g, b);
} }
static inline uint32_t coord_map(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int rotation, bool mirrored) static inline uint32_t
coord_map(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int rotation,
bool mirrored)
{ {
uint32_t x_r, y_r; uint32_t x_r, y_r;
if (rotation == 0) { if (rotation == 0) {
@ -68,19 +79,13 @@ static inline uint32_t coord_map(uint32_t x, uint32_t y, uint32_t width, uint32_
return index; return index;
} }
static void quick_preview_rggb8( static void
uint32_t *dst, quick_preview_rggb8(uint32_t *dst, const uint32_t dst_width,
const uint32_t dst_width, const uint32_t dst_height, const uint8_t *src,
const uint32_t dst_height, const uint32_t src_width, const uint32_t src_height,
const uint8_t *src, const MPPixelFormat format, const uint32_t rotation,
const uint32_t src_width, const bool mirrored, const float *colormatrix,
const uint32_t src_height, const uint8_t blacklevel, const uint32_t skip)
const MPPixelFormat format,
const uint32_t rotation,
const bool mirrored,
const float *colormatrix,
const uint8_t blacklevel,
const uint32_t skip)
{ {
uint32_t src_y = 0, dst_y = 0; uint32_t src_y = 0, dst_y = 0;
while (src_y < src_height) { while (src_y < src_height) {
@ -94,25 +99,26 @@ static void quick_preview_rggb8(
uint32_t color; uint32_t color;
switch (format) { switch (format) {
case MP_PIXEL_FMT_BGGR8: case MP_PIXEL_FMT_BGGR8:
color = pack_rgb(b2, b1, b0); color = pack_rgb(b2, b1, b0);
break; break;
case MP_PIXEL_FMT_GBRG8: case MP_PIXEL_FMT_GBRG8:
color = pack_rgb(b2, b0, b1); color = pack_rgb(b2, b0, b1);
break; break;
case MP_PIXEL_FMT_GRBG8: case MP_PIXEL_FMT_GRBG8:
color = pack_rgb(b1, b0, b2); color = pack_rgb(b1, b0, b2);
break; break;
case MP_PIXEL_FMT_RGGB8: case MP_PIXEL_FMT_RGGB8:
color = pack_rgb(b0, b1, b2); color = pack_rgb(b0, b1, b2);
break; break;
default: default:
assert(false); assert(false);
} }
color = apply_colormatrix(color, colormatrix); color = apply_colormatrix(color, colormatrix);
dst[coord_map(dst_x, dst_y, dst_width, dst_height, rotation, mirrored)] = color; dst[coord_map(dst_x, dst_y, dst_width, dst_height, rotation,
mirrored)] = color;
src_x += 2 + 2 * skip; src_x += 2 + 2 * skip;
++dst_x; ++dst_x;
@ -123,19 +129,13 @@ static void quick_preview_rggb8(
} }
} }
static void quick_preview_rggb10( static void
uint32_t *dst, quick_preview_rggb10(uint32_t *dst, const uint32_t dst_width,
const uint32_t dst_width, const uint32_t dst_height, const uint8_t *src,
const uint32_t dst_height, const uint32_t src_width, const uint32_t src_height,
const uint8_t *src, const MPPixelFormat format, const uint32_t rotation,
const uint32_t src_width, const bool mirrored, const float *colormatrix,
const uint32_t src_height, const uint8_t blacklevel, const uint32_t skip)
const MPPixelFormat format,
const uint32_t rotation,
const bool mirrored,
const float *colormatrix,
const uint8_t blacklevel,
const uint32_t skip)
{ {
assert(src_width % 2 == 0); assert(src_width % 2 == 0);
@ -153,25 +153,26 @@ static void quick_preview_rggb10(
uint32_t color; uint32_t color;
switch (format) { switch (format) {
case MP_PIXEL_FMT_BGGR10P: case MP_PIXEL_FMT_BGGR10P:
color = pack_rgb(b2, b1, b0); color = pack_rgb(b2, b1, b0);
break; break;
case MP_PIXEL_FMT_GBRG10P: case MP_PIXEL_FMT_GBRG10P:
color = pack_rgb(b2, b0, b1); color = pack_rgb(b2, b0, b1);
break; break;
case MP_PIXEL_FMT_GRBG10P: case MP_PIXEL_FMT_GRBG10P:
color = pack_rgb(b1, b0, b2); color = pack_rgb(b1, b0, b2);
break; break;
case MP_PIXEL_FMT_RGGB10P: case MP_PIXEL_FMT_RGGB10P:
color = pack_rgb(b0, b1, b2); color = pack_rgb(b0, b1, b2);
break; break;
default: default:
assert(false); assert(false);
} }
color = apply_colormatrix(color, colormatrix); color = apply_colormatrix(color, colormatrix);
dst[coord_map(dst_x, dst_y, dst_width, dst_height, rotation, mirrored)] = color; dst[coord_map(dst_x, dst_y, dst_width, dst_height, rotation,
mirrored)] = color;
uint32_t advance = 1 + skip; uint32_t advance = 1 + skip;
if (src_x % 5 == 0) { if (src_x % 5 == 0) {
@ -187,18 +188,12 @@ static void quick_preview_rggb10(
} }
} }
static void quick_preview_yuv( static void
uint32_t *dst, quick_preview_yuv(uint32_t *dst, const uint32_t dst_width, const uint32_t dst_height,
const uint32_t dst_width, const uint8_t *src, const uint32_t src_width,
const uint32_t dst_height, const uint32_t src_height, const MPPixelFormat format,
const uint8_t *src, const uint32_t rotation, const bool mirrored,
const uint32_t src_width, const float *colormatrix, const uint32_t skip)
const uint32_t src_height,
const MPPixelFormat format,
const uint32_t rotation,
const bool mirrored,
const float *colormatrix,
const uint32_t skip)
{ {
assert(src_width % 2 == 0); assert(src_width % 2 == 0);
@ -222,28 +217,31 @@ static void quick_preview_yuv(
uint32_t color1, color2; uint32_t color1, color2;
switch (format) { switch (format) {
case MP_PIXEL_FMT_UYVY: case MP_PIXEL_FMT_UYVY:
color1 = convert_yuv_to_srgb(b1, b0, b2); color1 = convert_yuv_to_srgb(b1, b0, b2);
color2 = convert_yuv_to_srgb(b3, b0, b2); color2 = convert_yuv_to_srgb(b3, b0, b2);
break; break;
case MP_PIXEL_FMT_YUYV: case MP_PIXEL_FMT_YUYV:
color1 = convert_yuv_to_srgb(b0, b1, b3); color1 = convert_yuv_to_srgb(b0, b1, b3);
color2 = convert_yuv_to_srgb(b2, b1, b3); color2 = convert_yuv_to_srgb(b2, b1, b3);
break; break;
default: default:
assert(false); assert(false);
} }
color1 = apply_colormatrix(color1, colormatrix); color1 = apply_colormatrix(color1, colormatrix);
color2 = apply_colormatrix(color2, colormatrix); color2 = apply_colormatrix(color2, colormatrix);
uint32_t dst_i1 = coord_map(dst_x, dst_y, dst_width, dst_height, rotation, mirrored); uint32_t dst_i1 = coord_map(dst_x, dst_y, dst_width,
dst_height, rotation, mirrored);
dst[dst_i1] = color1; dst[dst_i1] = color1;
++dst_x; ++dst_x;
// The last pixel needs to be skipped if we have an odd un-rotated width // The last pixel needs to be skipped if we have an odd un-rotated width
if (dst_x < unrot_dst_width) { if (dst_x < unrot_dst_width) {
uint32_t dst_i2 = coord_map(dst_x, dst_y, dst_width, dst_height, rotation, mirrored); uint32_t dst_i2 =
coord_map(dst_x, dst_y, dst_width,
dst_height, rotation, mirrored);
dst[dst_i2] = color2; dst[dst_i2] = color2;
++dst_x; ++dst_x;
} }
@ -256,92 +254,52 @@ static void quick_preview_yuv(
} }
} }
void quick_preview( void
uint32_t *dst, quick_preview(uint32_t *dst, const uint32_t dst_width, const uint32_t dst_height,
const uint32_t dst_width, const uint8_t *src, const uint32_t src_width,
const uint32_t dst_height, const uint32_t src_height, const MPPixelFormat format,
const uint8_t *src, const uint32_t rotation, const bool mirrored, const float *colormatrix,
const uint32_t src_width, const uint8_t blacklevel, const uint32_t skip)
const uint32_t src_height,
const MPPixelFormat format,
const uint32_t rotation,
const bool mirrored,
const float *colormatrix,
const uint8_t blacklevel,
const uint32_t skip)
{ {
switch (format) { switch (format) {
case MP_PIXEL_FMT_BGGR8: case MP_PIXEL_FMT_BGGR8:
case MP_PIXEL_FMT_GBRG8: case MP_PIXEL_FMT_GBRG8:
case MP_PIXEL_FMT_GRBG8: case MP_PIXEL_FMT_GRBG8:
case MP_PIXEL_FMT_RGGB8: case MP_PIXEL_FMT_RGGB8:
quick_preview_rggb8( quick_preview_rggb8(dst, dst_width, dst_height, src, src_width,
dst, src_height, format, rotation, mirrored,
dst_width, colormatrix, blacklevel, skip);
dst_height, break;
src, case MP_PIXEL_FMT_BGGR10P:
src_width, case MP_PIXEL_FMT_GBRG10P:
src_height, case MP_PIXEL_FMT_GRBG10P:
format, case MP_PIXEL_FMT_RGGB10P:
rotation, quick_preview_rggb10(dst, dst_width, dst_height, src, src_width,
mirrored, src_height, format, rotation, mirrored,
colormatrix, colormatrix, blacklevel, skip);
blacklevel, break;
skip); case MP_PIXEL_FMT_UYVY:
break; case MP_PIXEL_FMT_YUYV:
case MP_PIXEL_FMT_BGGR10P: quick_preview_yuv(dst, dst_width, dst_height, src, src_width,
case MP_PIXEL_FMT_GBRG10P: src_height, format, rotation, mirrored,
case MP_PIXEL_FMT_GRBG10P: colormatrix, skip);
case MP_PIXEL_FMT_RGGB10P: break;
quick_preview_rggb10( default:
dst, assert(false);
dst_width,
dst_height,
src,
src_width,
src_height,
format,
rotation,
mirrored,
colormatrix,
blacklevel,
skip);
break;
case MP_PIXEL_FMT_UYVY:
case MP_PIXEL_FMT_YUYV:
quick_preview_yuv(
dst,
dst_width,
dst_height,
src,
src_width,
src_height,
format,
rotation,
mirrored,
colormatrix,
skip);
break;
default:
assert(false);
} }
} }
static uint32_t div_ceil(uint32_t x, uint32_t y) static uint32_t
div_ceil(uint32_t x, uint32_t y)
{ {
return x/y + !!(x % y); return x / y + !!(x % y);
} }
void quick_preview_size( void
uint32_t *dst_width, quick_preview_size(uint32_t *dst_width, uint32_t *dst_height, uint32_t *skip,
uint32_t *dst_height, const uint32_t preview_width, const uint32_t preview_height,
uint32_t *skip, const uint32_t src_width, const uint32_t src_height,
const uint32_t preview_width, const MPPixelFormat format, const int rotation)
const uint32_t preview_height,
const uint32_t src_width,
const uint32_t src_height,
const MPPixelFormat format,
const int rotation)
{ {
uint32_t colors_x = mp_pixel_format_width_to_colors(format, src_width); uint32_t colors_x = mp_pixel_format_width_to_colors(format, src_width);
uint32_t colors_y = mp_pixel_format_height_to_colors(format, src_height); uint32_t colors_y = mp_pixel_format_height_to_colors(format, src_height);

View File

@ -1,27 +1,14 @@
#include "camera.h" #include "camera.h"
#include <stdint.h> #include <stdint.h>
void quick_preview( void quick_preview(uint32_t *dst, const uint32_t dst_width,
uint32_t *dst, const uint32_t dst_height, const uint8_t *src,
const uint32_t dst_width, const uint32_t src_width, const uint32_t src_height,
const uint32_t dst_height, const MPPixelFormat format, const uint32_t rotation,
const uint8_t *src, const bool mirrored, const float *colormatrix,
const uint32_t src_width, const uint8_t blacklevel, const uint32_t skip);
const uint32_t src_height,
const MPPixelFormat format,
const uint32_t rotation,
const bool mirrored,
const float *colormatrix,
const uint8_t blacklevel,
const uint32_t skip);
void quick_preview_size( void quick_preview_size(uint32_t *dst_width, uint32_t *dst_height, uint32_t *skip,
uint32_t *dst_width, const uint32_t preview_width, const uint32_t preview_height,
uint32_t *dst_height, const uint32_t src_width, const uint32_t src_height,
uint32_t *skip, const MPPixelFormat format, const int rotation);
const uint32_t preview_width,
const uint32_t preview_height,
const uint32_t src_width,
const uint32_t src_height,
const MPPixelFormat format,
const int rotation);