diff options
Diffstat (limited to 'CCCB Display')
-rw-r--r-- | CCCB Display/AppDelegate.swift | 1 | ||||
-rw-r--r-- | CCCB Display/Assets.xcassets/AppIcon.appiconset/Contents.json | 1 | ||||
-rw-r--r-- | CCCB Display/Assets.xcassets/AppIcon.appiconset/IMG_4898.jpeg | bin | 0 -> 534617 bytes | |||
-rw-r--r-- | CCCB Display/Assets.xcassets/IMG_4898.imageset/Contents.json | 21 | ||||
-rw-r--r-- | CCCB Display/Assets.xcassets/IMG_4898.imageset/IMG_4898.jpeg | bin | 0 -> 534617 bytes | |||
-rw-r--r-- | CCCB Display/Base.lproj/LaunchScreen.storyboard | 29 | ||||
-rw-r--r-- | CCCB Display/Base.lproj/Main.storyboard | 129 | ||||
-rw-r--r-- | CCCB Display/ConfigController.swift | 35 | ||||
-rw-r--r-- | CCCB Display/ViewController.swift | 336 |
9 files changed, 539 insertions, 13 deletions
diff --git a/CCCB Display/AppDelegate.swift b/CCCB Display/AppDelegate.swift index 8639c64..a3e61dc 100644 --- a/CCCB Display/AppDelegate.swift +++ b/CCCB Display/AppDelegate.swift | |||
@@ -14,6 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { | |||
14 | 14 | ||
15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { |
16 | // Override point for customization after application launch. | 16 | // Override point for customization after application launch. |
17 | UIApplication.shared.isIdleTimerDisabled = true | ||
17 | return true | 18 | return true |
18 | } | 19 | } |
19 | 20 | ||
diff --git a/CCCB Display/Assets.xcassets/AppIcon.appiconset/Contents.json b/CCCB Display/Assets.xcassets/AppIcon.appiconset/Contents.json index 13613e3..bfe77c1 100644 --- a/CCCB Display/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/CCCB Display/Assets.xcassets/AppIcon.appiconset/Contents.json | |||
@@ -1,6 +1,7 @@ | |||
1 | { | 1 | { |
2 | "images" : [ | 2 | "images" : [ |
3 | { | 3 | { |
4 | "filename" : "IMG_4898.jpeg", | ||
4 | "idiom" : "universal", | 5 | "idiom" : "universal", |
5 | "platform" : "ios", | 6 | "platform" : "ios", |
6 | "size" : "1024x1024" | 7 | "size" : "1024x1024" |
diff --git a/CCCB Display/Assets.xcassets/AppIcon.appiconset/IMG_4898.jpeg b/CCCB Display/Assets.xcassets/AppIcon.appiconset/IMG_4898.jpeg new file mode 100644 index 0000000..b9f2603 --- /dev/null +++ b/CCCB Display/Assets.xcassets/AppIcon.appiconset/IMG_4898.jpeg | |||
Binary files differ | |||
diff --git a/CCCB Display/Assets.xcassets/IMG_4898.imageset/Contents.json b/CCCB Display/Assets.xcassets/IMG_4898.imageset/Contents.json new file mode 100644 index 0000000..ce6efb2 --- /dev/null +++ b/CCCB Display/Assets.xcassets/IMG_4898.imageset/Contents.json | |||
@@ -0,0 +1,21 @@ | |||
1 | { | ||
2 | "images" : [ | ||
3 | { | ||
4 | "filename" : "IMG_4898.jpeg", | ||
5 | "idiom" : "universal", | ||
6 | "scale" : "1x" | ||
7 | }, | ||
8 | { | ||
9 | "idiom" : "universal", | ||
10 | "scale" : "2x" | ||
11 | }, | ||
12 | { | ||
13 | "idiom" : "universal", | ||
14 | "scale" : "3x" | ||
15 | } | ||
16 | ], | ||
17 | "info" : { | ||
18 | "author" : "xcode", | ||
19 | "version" : 1 | ||
20 | } | ||
21 | } | ||
diff --git a/CCCB Display/Assets.xcassets/IMG_4898.imageset/IMG_4898.jpeg b/CCCB Display/Assets.xcassets/IMG_4898.imageset/IMG_4898.jpeg new file mode 100644 index 0000000..b9f2603 --- /dev/null +++ b/CCCB Display/Assets.xcassets/IMG_4898.imageset/IMG_4898.jpeg | |||
Binary files differ | |||
diff --git a/CCCB Display/Base.lproj/LaunchScreen.storyboard b/CCCB Display/Base.lproj/LaunchScreen.storyboard index 865e932..37c47c1 100644 --- a/CCCB Display/Base.lproj/LaunchScreen.storyboard +++ b/CCCB Display/Base.lproj/LaunchScreen.storyboard | |||
@@ -1,8 +1,10 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
2 | <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> | 2 | <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> |
3 | <device id="retina6_12" orientation="landscape" appearance="light"/> | ||
3 | <dependencies> | 4 | <dependencies> |
4 | <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/> | 5 | <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21678"/> |
5 | <capability name="Safe area layout guides" minToolsVersion="9.0"/> | 6 | <capability name="Safe area layout guides" minToolsVersion="9.0"/> |
7 | <capability name="System colors in document resources" minToolsVersion="11.0"/> | ||
6 | <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | 8 | <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> |
7 | </dependencies> | 9 | </dependencies> |
8 | <scenes> | 10 | <scenes> |
@@ -11,10 +13,21 @@ | |||
11 | <objects> | 13 | <objects> |
12 | <viewController id="01J-lp-oVM" sceneMemberID="viewController"> | 14 | <viewController id="01J-lp-oVM" sceneMemberID="viewController"> |
13 | <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> | 15 | <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> |
14 | <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> | 16 | <rect key="frame" x="0.0" y="0.0" width="852" height="393"/> |
15 | <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | 17 | <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> |
16 | <color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> | 18 | <subviews> |
19 | <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="IMG_4898" translatesAutoresizingMaskIntoConstraints="NO" id="pnd-eL-XdQ"> | ||
20 | <rect key="frame" x="59" y="0.0" width="734" height="372"/> | ||
21 | </imageView> | ||
22 | </subviews> | ||
17 | <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> | 23 | <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> |
24 | <color key="backgroundColor" systemColor="systemBackgroundColor"/> | ||
25 | <constraints> | ||
26 | <constraint firstItem="pnd-eL-XdQ" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="Nv3-O9-veR"/> | ||
27 | <constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="pnd-eL-XdQ" secondAttribute="bottom" id="fB9-uM-dp6"/> | ||
28 | <constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="pnd-eL-XdQ" secondAttribute="trailing" id="i5C-Km-vv3"/> | ||
29 | <constraint firstItem="pnd-eL-XdQ" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="wKN-64-4YA"/> | ||
30 | </constraints> | ||
18 | </view> | 31 | </view> |
19 | </viewController> | 32 | </viewController> |
20 | <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> | 33 | <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> |
@@ -22,4 +35,10 @@ | |||
22 | <point key="canvasLocation" x="53" y="375"/> | 35 | <point key="canvasLocation" x="53" y="375"/> |
23 | </scene> | 36 | </scene> |
24 | </scenes> | 37 | </scenes> |
38 | <resources> | ||
39 | <image name="IMG_4898" width="1024" height="1024"/> | ||
40 | <systemColor name="systemBackgroundColor"> | ||
41 | <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | ||
42 | </systemColor> | ||
43 | </resources> | ||
25 | </document> | 44 | </document> |
diff --git a/CCCB Display/Base.lproj/Main.storyboard b/CCCB Display/Base.lproj/Main.storyboard index 25a7638..247b634 100644 --- a/CCCB Display/Base.lproj/Main.storyboard +++ b/CCCB Display/Base.lproj/Main.storyboard | |||
@@ -1,24 +1,143 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
2 | <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r"> | 2 | <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r"> |
3 | <device id="retina6_12" orientation="landscape" appearance="light"/> | ||
3 | <dependencies> | 4 | <dependencies> |
4 | <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/> | 5 | <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21678"/> |
5 | <capability name="Safe area layout guides" minToolsVersion="9.0"/> | 6 | <capability name="Safe area layout guides" minToolsVersion="9.0"/> |
7 | <capability name="System colors in document resources" minToolsVersion="11.0"/> | ||
6 | <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | 8 | <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> |
7 | </dependencies> | 9 | </dependencies> |
8 | <scenes> | 10 | <scenes> |
9 | <!--View Controller--> | 11 | <!--View Controller--> |
10 | <scene sceneID="tne-QT-ifu"> | 12 | <scene sceneID="tne-QT-ifu"> |
11 | <objects> | 13 | <objects> |
12 | <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController"> | 14 | <viewController id="BYZ-38-t0r" customClass="ViewController" customModule="CCCB_Display" customModuleProvider="target" sceneMemberID="viewController"> |
13 | <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> | 15 | <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> |
14 | <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> | 16 | <rect key="frame" x="0.0" y="0.0" width="852" height="393"/> |
15 | <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | 17 | <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> |
16 | <color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> | 18 | <subviews> |
19 | <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Zg1-dS-4VN"> | ||
20 | <rect key="frame" x="0.0" y="0.0" width="852" height="393"/> | ||
21 | <color key="backgroundColor" systemColor="systemBackgroundColor"/> | ||
22 | </view> | ||
23 | <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LB5-99-rhM"> | ||
24 | <rect key="frame" x="689" y="8" width="72" height="35"/> | ||
25 | <state key="normal" title="Button"/> | ||
26 | <buttonConfiguration key="configuration" style="filled" title="config"/> | ||
27 | <connections> | ||
28 | <segue destination="Srg-Ai-hZD" kind="popoverPresentation" popoverAnchorView="LB5-99-rhM" id="qNt-fx-jP2"> | ||
29 | <popoverArrowDirection key="popoverArrowDirection" up="YES" down="YES" left="YES" right="YES"/> | ||
30 | </segue> | ||
31 | </connections> | ||
32 | </button> | ||
33 | <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="right" contentVerticalAlignment="bottom" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4IW-d6-qWW"> | ||
34 | <rect key="frame" x="630" y="8" width="51" height="35"/> | ||
35 | <state key="normal" title="Button"/> | ||
36 | <buttonConfiguration key="configuration" style="filled" title="Flip"/> | ||
37 | <connections> | ||
38 | <action selector="switchCameraSideWithSender:" destination="BYZ-38-t0r" eventType="touchUpInside" id="15B-T0-yhg"/> | ||
39 | </connections> | ||
40 | </button> | ||
41 | <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FPx-ZU-vSE"> | ||
42 | <rect key="frame" x="59" y="8" width="80" height="22"/> | ||
43 | <color key="backgroundColor" systemColor="systemGreenColor"/> | ||
44 | <fontDescription key="fontDescription" type="system" pointSize="17"/> | ||
45 | <nil key="textColor"/> | ||
46 | <nil key="highlightedColor"/> | ||
47 | </label> | ||
48 | </subviews> | ||
17 | <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> | 49 | <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> |
50 | <color key="backgroundColor" systemColor="systemBackgroundColor"/> | ||
51 | <constraints> | ||
52 | <constraint firstItem="LB5-99-rhM" firstAttribute="leading" secondItem="4IW-d6-qWW" secondAttribute="trailing" constant="8" id="0JW-YO-OfQ"/> | ||
53 | <constraint firstItem="FPx-ZU-vSE" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="8" id="2HN-cV-t4S"/> | ||
54 | <constraint firstItem="FPx-ZU-vSE" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="3qY-jD-MCQ"/> | ||
55 | <constraint firstItem="Zg1-dS-4VN" firstAttribute="bottom" secondItem="8bC-Xf-vdC" secondAttribute="bottom" id="8Uw-BH-OnX"/> | ||
56 | <constraint firstItem="Zg1-dS-4VN" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="AJu-22-uph"/> | ||
57 | <constraint firstItem="Zg1-dS-4VN" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="top" id="EDS-vg-saF"/> | ||
58 | <constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="LB5-99-rhM" secondAttribute="trailing" constant="32" id="FMt-88-9e3"/> | ||
59 | <constraint firstItem="4IW-d6-qWW" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="8" id="G8m-hM-E2u"/> | ||
60 | <constraint firstItem="LB5-99-rhM" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="8" id="OP9-7G-8by"/> | ||
61 | <constraint firstAttribute="trailing" secondItem="Zg1-dS-4VN" secondAttribute="trailing" id="esB-po-Jgq"/> | ||
62 | <constraint firstItem="Zg1-dS-4VN" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="wPB-mw-ptE"/> | ||
63 | </constraints> | ||
18 | </view> | 64 | </view> |
65 | <connections> | ||
66 | <outlet property="cameraView" destination="Zg1-dS-4VN" id="27A-lq-3JJ"/> | ||
67 | <outlet property="frameRateLabel" destination="FPx-ZU-vSE" id="twM-qM-WQM"/> | ||
68 | </connections> | ||
19 | </viewController> | 69 | </viewController> |
20 | <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> | 70 | <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> |
21 | </objects> | 71 | </objects> |
72 | <point key="canvasLocation" x="111.9718309859155" y="-2.2900763358778624"/> | ||
73 | </scene> | ||
74 | <!--Config Controller--> | ||
75 | <scene sceneID="Ozy-FI-kCz"> | ||
76 | <objects> | ||
77 | <viewController modalPresentationStyle="pageSheet" id="Srg-Ai-hZD" customClass="ConfigController" customModule="CCCB_Display" customModuleProvider="target" sceneMemberID="viewController"> | ||
78 | <view key="view" contentMode="scaleToFill" id="RKC-6a-725"> | ||
79 | <rect key="frame" x="0.0" y="0.0" width="852" height="393"/> | ||
80 | <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||
81 | <subviews> | ||
82 | <textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="248" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="IP Address" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="XB9-tI-41p"> | ||
83 | <rect key="frame" x="210.99999999999997" y="68" width="401.33333333333326" height="35"/> | ||
84 | <fontDescription key="fontDescription" type="system" pointSize="14"/> | ||
85 | <textInputTraits key="textInputTraits"/> | ||
86 | </textField> | ||
87 | <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Display Address" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eRN-cy-tvC"> | ||
88 | <rect key="frame" x="80" y="75" width="123" height="21"/> | ||
89 | <fontDescription key="fontDescription" type="system" pointSize="17"/> | ||
90 | <nil key="textColor"/> | ||
91 | <nil key="highlightedColor"/> | ||
92 | </label> | ||
93 | <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eoj-db-q0p"> | ||
94 | <rect key="frame" x="705" y="68" width="65" height="35"/> | ||
95 | <state key="normal" title="Button"/> | ||
96 | <buttonConfiguration key="configuration" style="filled" title="Done"/> | ||
97 | <connections> | ||
98 | <action selector="donePressed:" destination="Srg-Ai-hZD" eventType="touchUpInside" id="RFf-QJ-Omk"/> | ||
99 | </connections> | ||
100 | </button> | ||
101 | <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dKt-Le-k5D"> | ||
102 | <rect key="frame" x="620.33333333333337" y="68" width="76.666666666666629" height="35"/> | ||
103 | <state key="normal" title="Button"/> | ||
104 | <buttonConfiguration key="configuration" style="filled" title="Cancel"/> | ||
105 | <connections> | ||
106 | <action selector="cancelPressed:" destination="Srg-Ai-hZD" eventType="touchUpInside" id="8qq-Jm-VhP"/> | ||
107 | </connections> | ||
108 | </button> | ||
109 | </subviews> | ||
110 | <viewLayoutGuide key="safeArea" id="D96-4o-bMR"/> | ||
111 | <color key="backgroundColor" systemColor="systemBackgroundColor"/> | ||
112 | <constraints> | ||
113 | <constraint firstItem="XB9-tI-41p" firstAttribute="top" secondItem="eoj-db-q0p" secondAttribute="top" id="1Aw-0G-Omh"/> | ||
114 | <constraint firstItem="XB9-tI-41p" firstAttribute="leading" secondItem="eRN-cy-tvC" secondAttribute="trailing" constant="8" id="3FR-zi-YS0"/> | ||
115 | <constraint firstItem="dKt-Le-k5D" firstAttribute="top" secondItem="eoj-db-q0p" secondAttribute="top" id="6db-VB-pbU"/> | ||
116 | <constraint firstItem="XB9-tI-41p" firstAttribute="centerY" secondItem="eRN-cy-tvC" secondAttribute="centerY" id="Ob5-Ea-9SL"/> | ||
117 | <constraint firstItem="XB9-tI-41p" firstAttribute="centerY" secondItem="eoj-db-q0p" secondAttribute="centerY" id="R2J-lD-arp"/> | ||
118 | <constraint firstItem="dKt-Le-k5D" firstAttribute="leading" secondItem="XB9-tI-41p" secondAttribute="trailing" constant="8" id="TNp-0v-8LR"/> | ||
119 | <constraint firstItem="eRN-cy-tvC" firstAttribute="leading" secondItem="D96-4o-bMR" secondAttribute="leading" constant="80" id="aD8-8R-LiO"/> | ||
120 | <constraint firstItem="eoj-db-q0p" firstAttribute="leading" secondItem="dKt-Le-k5D" secondAttribute="trailing" constant="8" id="dSO-oI-Wci"/> | ||
121 | <constraint firstItem="D96-4o-bMR" firstAttribute="trailing" secondItem="eoj-db-q0p" secondAttribute="trailing" constant="82" id="riC-ZY-AgB"/> | ||
122 | <constraint firstItem="XB9-tI-41p" firstAttribute="top" secondItem="D96-4o-bMR" secondAttribute="top" constant="68" id="sUl-XQ-UGi"/> | ||
123 | </constraints> | ||
124 | </view> | ||
125 | <extendedEdge key="edgesForExtendedLayout" bottom="YES"/> | ||
126 | <connections> | ||
127 | <outlet property="display_address" destination="XB9-tI-41p" id="EZU-kl-5r7"/> | ||
128 | </connections> | ||
129 | </viewController> | ||
130 | <placeholder placeholderIdentifier="IBFirstResponder" id="XVb-PU-k6x" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/> | ||
131 | </objects> | ||
132 | <point key="canvasLocation" x="904" y="-2"/> | ||
22 | </scene> | 133 | </scene> |
23 | </scenes> | 134 | </scenes> |
135 | <resources> | ||
136 | <systemColor name="systemBackgroundColor"> | ||
137 | <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | ||
138 | </systemColor> | ||
139 | <systemColor name="systemGreenColor"> | ||
140 | <color red="0.20392156862745098" green="0.7803921568627451" blue="0.34901960784313724" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | ||
141 | </systemColor> | ||
142 | </resources> | ||
24 | </document> | 143 | </document> |
diff --git a/CCCB Display/ConfigController.swift b/CCCB Display/ConfigController.swift new file mode 100644 index 0000000..92b6e37 --- /dev/null +++ b/CCCB Display/ConfigController.swift | |||
@@ -0,0 +1,35 @@ | |||
1 | // | ||
2 | // ConfigController.swift | ||
3 | // CCCB Display | ||
4 | // | ||
5 | // Created by Dirk Engling on 26.05.23. | ||
6 | // | ||
7 | |||
8 | import Foundation | ||
9 | import UIKit | ||
10 | |||
11 | |||
12 | class ConfigController: UIViewController { | ||
13 | |||
14 | @IBOutlet weak var display_address: UITextField! | ||
15 | |||
16 | override func viewWillAppear(_ animated: Bool) { | ||
17 | super.viewWillAppear(animated) | ||
18 | let defaults = UserDefaults.standard | ||
19 | if let ip = defaults.string(forKey: "Display_Address") { | ||
20 | display_address.text = ip | ||
21 | } else { | ||
22 | display_address.text = "172.23.42.29" | ||
23 | } | ||
24 | } | ||
25 | |||
26 | @IBAction func cancelPressed(_ sender: Any) { | ||
27 | self.dismiss(animated: true) | ||
28 | } | ||
29 | |||
30 | @IBAction func donePressed(_ sender: Any) { | ||
31 | self.dismiss(animated: true) | ||
32 | let defaults = UserDefaults.standard | ||
33 | defaults.set(display_address.text, forKey: "Display_Address") | ||
34 | } | ||
35 | } | ||
diff --git a/CCCB Display/ViewController.swift b/CCCB Display/ViewController.swift index 53f3cee..d0f8dbe 100644 --- a/CCCB Display/ViewController.swift +++ b/CCCB Display/ViewController.swift | |||
@@ -6,14 +6,344 @@ | |||
6 | // | 6 | // |
7 | 7 | ||
8 | import UIKit | 8 | import UIKit |
9 | import AVFoundation | ||
10 | import CoreImage | ||
11 | import Network | ||
9 | 12 | ||
10 | class ViewController: UIViewController { | 13 | class ViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate, ObservableObject, AVCaptureVideoDataOutputSampleBufferDelegate { |
14 | |||
15 | @IBOutlet weak var frameRateLabel: UILabel! | ||
16 | @IBOutlet weak var cameraView: UIView! | ||
17 | |||
18 | var device: AVCaptureDevice? | ||
19 | var input: AVCaptureDeviceInput? | ||
20 | var prevLayer: AVCaptureVideoPreviewLayer? | ||
21 | |||
22 | private let captureSession = AVCaptureSession() | ||
23 | private let videoDataOutput = AVCaptureVideoDataOutput() | ||
24 | private let sessionQueue = DispatchQueue(label: "sessionQueue") | ||
25 | private let context = CIContext() | ||
26 | |||
27 | // var hostUDP: NWEndpoint.Host = "172.23.42.29" | ||
28 | var hostUDP: NWEndpoint.Host? | ||
29 | var portUDP: NWEndpoint.Port = 2342 | ||
30 | var connectionUDP: NWConnection? | ||
31 | |||
32 | var lastTimeStamp: CFTimeInterval = CACurrentMediaTime() | ||
33 | |||
34 | /* Physical Display control packet parameters: */ | ||
35 | private let HEADERLEN = 10 | ||
36 | private let WIDTH = 448 | ||
37 | private let HEIGHT = 160 | ||
38 | private let VHEIGHT = 236 | ||
11 | 39 | ||
12 | override func viewDidLoad() { | 40 | override func viewDidLoad() { |
13 | super.viewDidLoad() | 41 | super.viewDidLoad() |
14 | // Do any additional setup after loading the view. | 42 | |
43 | UserDefaults.standard.addObserver(self, forKeyPath: "Display_Address", options: .new, context: nil) | ||
44 | constructSocket() | ||
45 | |||
46 | switch AVCaptureDevice.authorizationStatus(for: .video) { | ||
47 | case .authorized: // the user has already authorized to access the camera. | ||
48 | DispatchQueue.main.async { | ||
49 | self.createSession() | ||
50 | } | ||
51 | case .notDetermined: | ||
52 | AVCaptureDevice.requestAccess (for: .video) { (granted) in | ||
53 | if granted { | ||
54 | print("the user has granted to access the camera") | ||
55 | DispatchQueue.main.async { | ||
56 | self.createSession () | ||
57 | } | ||
58 | } else { | ||
59 | print("the user has not granted to access the camera") | ||
60 | let dialogMessage = UIAlertController(title: "Attention", message: "Can not work without camera access", preferredStyle: .alert) | ||
61 | self.present(dialogMessage, animated: true, completion: nil) | ||
62 | } | ||
63 | } | ||
64 | case .denied: | ||
65 | print("the user has denied previously to access the camera.") | ||
66 | let dialogMessage = UIAlertController(title: "Attention", message: "Can not work without camera access", preferredStyle: .alert) | ||
67 | self.present(dialogMessage, animated: true, completion: nil) | ||
68 | |||
69 | case .restricted: | ||
70 | print("the user can't give camera access due to some restriction.") | ||
71 | let dialogMessage = UIAlertController(title: "Attention", message: "Can not work without camera access", preferredStyle: .alert) | ||
72 | self.present(dialogMessage, animated: true, completion: nil) | ||
73 | |||
74 | default: | ||
75 | print("something has wrong due to we can't access the camera.") | ||
76 | let dialogMessage = UIAlertController(title: "Attention", message: "Can not work without camera access", preferredStyle: .alert) | ||
77 | self.present(dialogMessage, animated: true, completion: nil) | ||
78 | } | ||
79 | } | ||
80 | |||
81 | override func viewDidAppear(_ animated: Bool) { | ||
82 | super.viewDidAppear(animated) | ||
83 | prevLayer?.frame.size = cameraView.frame.size | ||
15 | } | 84 | } |
16 | 85 | ||
86 | func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { | ||
17 | 87 | ||
18 | } | 88 | let now = CACurrentMediaTime() |
89 | let freq = (Int)(1 / (now - lastTimeStamp)) | ||
90 | // print ("Elapsed: \(now - lastTimeStamp) - Frequency: \(1 / (now - lastTimeStamp))") | ||
91 | lastTimeStamp = now | ||
92 | |||
93 | guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } | ||
94 | |||
95 | CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly) | ||
96 | let bufferWidth = CVPixelBufferGetWidth(pixelBuffer) | ||
97 | let bufferHeight = CVPixelBufferGetHeight(pixelBuffer) | ||
98 | let bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer) | ||
99 | let kBytesPerPixel = 4 | ||
100 | |||
101 | print("\(bufferWidth) \(bufferHeight) \(bytesPerRow)") | ||
102 | guard let baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer) else { return } | ||
103 | |||
104 | var packet: [UInt8] = [0, 0x12, 0, 0, 0x23, 0, 0, 0, 0, 0] | ||
105 | var scratch: [Int] = Array(repeating: 0, count: WIDTH*(2+VHEIGHT)) | ||
106 | |||
107 | let t1 = CACurrentMediaTime() | ||
108 | |||
109 | // 160 real rows are interleaved with 19 gaps of 4 pixels height on the display | ||
110 | // so we create 20 virtual blocks of 8 real and 4 virtual pixels | ||
111 | // we overlay VHEIGHT==236 virtual rows on the image and later skip the 4 invisble rows | ||
112 | var off = 0 | ||
113 | for row in 0..<VHEIGHT { | ||
114 | let zeile = (Int)(((Double)(row) / (Double)(VHEIGHT)) * (Double)(bufferHeight)) * bytesPerRow | ||
115 | for column in 0..<WIDTH { | ||
116 | let pixel = kBytesPerPixel * (Int)(((Double)(column) / (Double)(WIDTH)) * (Double)(bufferWidth)) | ||
117 | |||
118 | let red = (Int)(baseAddress.load(fromByteOffset: zeile + pixel + 0, as: UInt8.self)) * 19535 | ||
119 | let green = (Int)(baseAddress.load(fromByteOffset: zeile + pixel + 1, as: UInt8.self)) * 38470 | ||
120 | let blue = (Int)(baseAddress.load(fromByteOffset: zeile + pixel + 2, as: UInt8.self)) * 7448 | ||
121 | //scratch[row * WIDTH + column] = red + blue + green | ||
122 | scratch[off] = red + blue + green | ||
123 | off += 1 | ||
124 | } | ||
125 | } | ||
126 | |||
127 | var acc = 0 | ||
128 | var accv = 0 | ||
129 | |||
130 | for row in 0..<VHEIGHT { | ||
131 | for column in 0..<WIDTH { | ||
132 | let pixel = scratch[row * WIDTH + column] | ||
133 | let bwpixel = (pixel < 0x810000) ? 0 : 0xffffff | ||
134 | |||
135 | // account for gaps on display (virtual lines 9-12) | ||
136 | if (row % 12) < 8 { | ||
137 | acc = (acc << 1) + (bwpixel >> 23) | ||
138 | accv += 1 | ||
139 | if accv == 8 { | ||
140 | packet.append((UInt8)(acc)) | ||
141 | acc = 0 | ||
142 | accv = 0 | ||
143 | } | ||
144 | } | ||
145 | |||
146 | let err = (pixel - bwpixel) / 42 | ||
147 | |||
148 | func AddSatShift(_ scr: inout Array<Int>, _ X: Int, _ Y: Int, _ SHIFT: Int) { | ||
149 | let inner_p = (row + Y) * WIDTH + column + X | ||
150 | var r = scr[inner_p] + (err << (16 - SHIFT)) | ||
151 | if r < 0 { | ||
152 | r = 0 | ||
153 | } | ||
154 | if r > 0xffffff { | ||
155 | r = 0xffffff | ||
156 | } | ||
157 | scr[inner_p] = r | ||
158 | } | ||
159 | |||
160 | AddSatShift(&scratch, 0, 1, 13) | ||
161 | AddSatShift(&scratch, 0, 2, 14) | ||
162 | if (column > 0) { | ||
163 | AddSatShift(&scratch, -1, 1, 14) | ||
164 | AddSatShift(&scratch, -1, 2, 15) | ||
165 | } | ||
166 | |||
167 | if (column > 1) { | ||
168 | AddSatShift(&scratch, -2, 1, 15) | ||
169 | AddSatShift(&scratch, -2, 2, 16) | ||
170 | } | ||
171 | |||
172 | if (column < WIDTH - 1) { | ||
173 | AddSatShift(&scratch, 1, 0, 13) | ||
174 | AddSatShift(&scratch, 1, 1, 14) | ||
175 | AddSatShift(&scratch, 1, 2, 15) | ||
176 | } | ||
177 | |||
178 | if (column < WIDTH - 2) { | ||
179 | AddSatShift(&scratch, 2, 0, 14) | ||
180 | AddSatShift(&scratch, 2, 1, 15) | ||
181 | AddSatShift(&scratch, 2, 2, 16) | ||
182 | } | ||
183 | |||
184 | } | ||
185 | } | ||
186 | |||
187 | let t2 = CACurrentMediaTime() | ||
188 | |||
189 | // print("dur \(t2 - t1)") | ||
190 | DispatchQueue.main.async { | ||
191 | self.frameRateLabel.text = String(format: "%.04f (%d Hz)", t2 - t1, freq) | ||
192 | } | ||
193 | |||
194 | self.connectionUDP?.send(content: packet, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in | ||
195 | if (NWError == nil) { | ||
196 | print("Data was sent to UDP") | ||
197 | } else { | ||
198 | print("ERROR! Error when data (Type: Data) sending. NWError: \n \(NWError!)") | ||
199 | self.constructSocket() | ||
200 | } | ||
201 | }))) | ||
202 | } | ||
19 | 203 | ||
204 | func createSession() { | ||
205 | guard let device = AVCaptureDevice.default(for: AVMediaType.video) else { return } | ||
206 | do { | ||
207 | input = try AVCaptureDeviceInput(device: device) | ||
208 | } | ||
209 | catch { | ||
210 | print(error) | ||
211 | } | ||
212 | |||
213 | captureSession.sessionPreset = AVCaptureSession.Preset.vga640x480 | ||
214 | if let input = input { | ||
215 | captureSession.addInput(input) | ||
216 | } | ||
217 | |||
218 | prevLayer = AVCaptureVideoPreviewLayer(session: captureSession) | ||
219 | prevLayer?.frame.size = cameraView.frame.size | ||
220 | prevLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill | ||
221 | |||
222 | cameraView.layer.addSublayer(prevLayer!) | ||
223 | captureSession.addOutput(videoDataOutput) | ||
224 | captureSession.commitConfiguration() | ||
225 | |||
226 | videoDataOutput.videoSettings.updateValue(kCVPixelFormatType_32BGRA, forKey: "PixelFormatType") | ||
227 | videoDataOutput.setSampleBufferDelegate(self, queue: self.sessionQueue) | ||
228 | |||
229 | do { | ||
230 | try device.lockForConfiguration() | ||
231 | device.activeVideoMaxFrameDuration = CMTimeMake(value: 1, timescale: 60) | ||
232 | device.activeVideoMinFrameDuration = CMTimeMake(value: 1, timescale: 60) | ||
233 | device.unlockForConfiguration() | ||
234 | } catch { | ||
235 | print(error) | ||
236 | } | ||
237 | |||
238 | let captureConnection = videoDataOutput.connection(with: .video) | ||
239 | captureConnection?.isEnabled = true | ||
240 | deviceOrientationDidChange(Notification(name: UIDevice.orientationDidChangeNotification)) | ||
241 | // captureConnection?.videoOrientation = .landscapeRight | ||
242 | |||
243 | sessionQueue.async { self.captureSession.startRunning() } | ||
244 | } | ||
245 | |||
246 | func cameraWithPosition(position: AVCaptureDevice.Position) -> AVCaptureDevice? { | ||
247 | if #available(iOS 11.1, *) { | ||
248 | let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera, .builtInTelephotoCamera, .builtInTrueDepthCamera, .builtInWideAngleCamera, ], mediaType: .video, position: position) | ||
249 | |||
250 | if let device = deviceDiscoverySession.devices.first { | ||
251 | return device | ||
252 | } | ||
253 | else { | ||
254 | //add code here | ||
255 | } | ||
256 | return nil | ||
257 | } | ||
258 | |||
259 | return device | ||
260 | } | ||
261 | |||
262 | func transformOrientation(orientation: UIInterfaceOrientation) -> AVCaptureVideoOrientation { | ||
263 | switch orientation { | ||
264 | case .landscapeLeft: | ||
265 | return .landscapeLeft | ||
266 | case .landscapeRight: | ||
267 | return .landscapeRight | ||
268 | case .portraitUpsideDown: | ||
269 | return .portraitUpsideDown | ||
270 | default: | ||
271 | return .portrait | ||
272 | } | ||
273 | } | ||
274 | |||
275 | override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { | ||
276 | if keyPath == "Display_Address" { | ||
277 | constructSocket() | ||
278 | } | ||
279 | } | ||
280 | |||
281 | func constructSocket() { | ||
282 | let defaults = UserDefaults.standard | ||
283 | if let ip = defaults.string(forKey: "Display_Address") { | ||
284 | hostUDP = NWEndpoint.Host(ip) | ||
285 | } else { | ||
286 | hostUDP = NWEndpoint.Host("172.23.42.29") | ||
287 | // hostUDP = NWEndpoint.Host("84.200.61.9") | ||
288 | // hostUDP = NWEndpoint.Host("192.168.178.69") | ||
289 | } | ||
290 | // hostUDP = NWEndpoint.Host("192.168.178.69") | ||
291 | |||
292 | self.connectionUDP = NWConnection(host: hostUDP!, port: portUDP, using: .udp) | ||
293 | self.connectionUDP?.start(queue: .global()) | ||
294 | } | ||
295 | |||
296 | @IBAction func switchCameraSide(sender: AnyObject) { | ||
297 | let currentCameraInput: AVCaptureInput = captureSession.inputs[0] | ||
298 | captureSession.removeInput(currentCameraInput) | ||
299 | var newCamera: AVCaptureDevice | ||
300 | if (currentCameraInput as! AVCaptureDeviceInput).device.position == .back { | ||
301 | newCamera = self.cameraWithPosition(position: .front)! | ||
302 | } else { | ||
303 | newCamera = self.cameraWithPosition(position: .back)! | ||
304 | } | ||
305 | |||
306 | var newVideoInput: AVCaptureDeviceInput? | ||
307 | do{ | ||
308 | newVideoInput = try AVCaptureDeviceInput(device: newCamera) | ||
309 | } | ||
310 | catch{ | ||
311 | print(error) | ||
312 | } | ||
313 | |||
314 | if let newVideoInput = newVideoInput{ | ||
315 | captureSession.addInput(newVideoInput) | ||
316 | deviceOrientationDidChange(Notification(name: UIDevice.orientationDidChangeNotification)) | ||
317 | } | ||
318 | } | ||
319 | |||
320 | override func viewWillAppear(_ animated: Bool) { | ||
321 | super.viewWillAppear(animated) | ||
322 | |||
323 | NotificationCenter.default.addObserver(self, selector: #selector(deviceOrientationDidChange), | ||
324 | name: UIDevice.orientationDidChangeNotification, object: nil) | ||
325 | deviceOrientationDidChange(Notification(name: UIDevice.orientationDidChangeNotification)) | ||
326 | |||
327 | sessionQueue.async { self.captureSession.startRunning() } | ||
328 | } | ||
329 | |||
330 | override func viewWillDisappear(_ animated: Bool) { | ||
331 | super.viewWillDisappear(animated) | ||
332 | |||
333 | sessionQueue.async { self.captureSession.stopRunning() } | ||
334 | NotificationCenter.default.removeObserver(self) | ||
335 | } | ||
336 | |||
337 | @objc func deviceOrientationDidChange(_ notification: Notification) { | ||
338 | let orientation = UIDevice.current.orientation | ||
339 | let captureConnection = videoDataOutput.connection(with: .video) | ||
340 | |||
341 | if orientation == .landscapeLeft { | ||
342 | self.prevLayer?.connection?.videoOrientation = .landscapeRight | ||
343 | captureConnection?.videoOrientation = .landscapeRight | ||
344 | } else { | ||
345 | self.prevLayer?.connection?.videoOrientation = .landscapeLeft | ||
346 | captureConnection?.videoOrientation = .landscapeLeft | ||
347 | } | ||
348 | } | ||
349 | } | ||