diff --git a/.gitignore b/.gitignore
index 46dbe0b5f..76a508a46 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@
.externalNativeBuild
.tags*
.cxx/
+linux/build/
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 000000000..580def2f9
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,33 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "GDB Debug JpegBenchmark",
+ "type": "cppdbg",
+ "request": "launch",
+ "program": "${workspaceFolder}/build/jpegbenchmark",
+ "args": [],
+ "stopAtEntry": false,
+ "cwd": "${workspaceFolder}",
+ "environment": [],
+ "externalConsole": false,
+ "MIMode": "gdb",
+ "setupCommands": [
+ {
+ "description": "Enable pretty-printing for gdb",
+ "text": "-enable-pretty-printing",
+ "ignoreFailures": true
+ }
+ ],
+ "preLaunchTask": "CMakeBuild",
+ "logging": {
+ "engineLogging": true
+ },
+ "targetArchitecture": "x86_64",
+ "launchCompleteCommand": "exec-run"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..7f1085985
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,67 @@
+{
+ "files.associations": {
+ "cstdint": "cpp",
+ "ostream": "cpp",
+ "*.tcc": "cpp",
+ "cstdlib": "cpp",
+ "mutex": "cpp",
+ "format": "cpp",
+ "array": "cpp",
+ "string_view": "cpp",
+ "initializer_list": "cpp",
+ "span": "cpp",
+ "utility": "cpp",
+ "atomic": "cpp",
+ "bit": "cpp",
+ "cctype": "cpp",
+ "charconv": "cpp",
+ "chrono": "cpp",
+ "clocale": "cpp",
+ "cmath": "cpp",
+ "compare": "cpp",
+ "concepts": "cpp",
+ "condition_variable": "cpp",
+ "cstdarg": "cpp",
+ "cstddef": "cpp",
+ "cstdio": "cpp",
+ "ctime": "cpp",
+ "cwchar": "cpp",
+ "cwctype": "cpp",
+ "deque": "cpp",
+ "list": "cpp",
+ "map": "cpp",
+ "string": "cpp",
+ "unordered_map": "cpp",
+ "vector": "cpp",
+ "exception": "cpp",
+ "algorithm": "cpp",
+ "functional": "cpp",
+ "iterator": "cpp",
+ "memory": "cpp",
+ "memory_resource": "cpp",
+ "numeric": "cpp",
+ "optional": "cpp",
+ "random": "cpp",
+ "ratio": "cpp",
+ "system_error": "cpp",
+ "tuple": "cpp",
+ "type_traits": "cpp",
+ "iomanip": "cpp",
+ "iosfwd": "cpp",
+ "iostream": "cpp",
+ "istream": "cpp",
+ "limits": "cpp",
+ "new": "cpp",
+ "numbers": "cpp",
+ "semaphore": "cpp",
+ "sstream": "cpp",
+ "stdexcept": "cpp",
+ "stop_token": "cpp",
+ "streambuf": "cpp",
+ "thread": "cpp",
+ "cinttypes": "cpp",
+ "typeinfo": "cpp",
+ "variant": "cpp"
+ },
+ "cmake.sourceDirectory": "${workspaceFolder}/linux/"
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 000000000..8c157a144
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,38 @@
+{
+ "tasks": [
+ {
+ "label": "CMakePrepare",
+ "type": "shell",
+ "command": "cmake",
+ "args": [
+ "-B",
+ "${workspaceFolder}/build",
+ "-S",
+ "${workspaceFolder}/linux/",
+ "-DCMAKE_BUILD_TYPE=Debug"
+ ],
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ },
+ "problemMatcher": ["$gcc"],
+ "detail": "Generated task by Debugger."
+ },
+ {
+ "label": "CMakeBuild",
+ "type": "shell",
+ "command": "cmake",
+ "args": [
+ "--build",
+ "${workspaceFolder}/build"
+ ],
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ },
+ "problemMatcher": ["$gcc"],
+ "detail": "Generated task by Debugger."
+ }
+ ],
+ "version": "2.0.0"
+}
\ No newline at end of file
diff --git a/AndroidUSBCamera.code-workspace b/AndroidUSBCamera.code-workspace
new file mode 100644
index 000000000..deee503df
--- /dev/null
+++ b/AndroidUSBCamera.code-workspace
@@ -0,0 +1,73 @@
+{
+ "folders": [
+ {
+ "path": "."
+ }
+ ],
+ "settings": {
+ "files.associations": {
+ "cstdint": "cpp",
+ "ostream": "cpp",
+ "*.tcc": "cpp",
+ "cstdlib": "cpp",
+ "mutex": "cpp",
+ "format": "cpp",
+ "array": "cpp",
+ "string_view": "cpp",
+ "initializer_list": "cpp",
+ "span": "cpp",
+ "utility": "cpp",
+ "atomic": "cpp",
+ "bit": "cpp",
+ "cctype": "cpp",
+ "charconv": "cpp",
+ "chrono": "cpp",
+ "clocale": "cpp",
+ "cmath": "cpp",
+ "compare": "cpp",
+ "concepts": "cpp",
+ "condition_variable": "cpp",
+ "cstdarg": "cpp",
+ "cstddef": "cpp",
+ "cstdio": "cpp",
+ "ctime": "cpp",
+ "cwchar": "cpp",
+ "cwctype": "cpp",
+ "deque": "cpp",
+ "list": "cpp",
+ "map": "cpp",
+ "string": "cpp",
+ "unordered_map": "cpp",
+ "vector": "cpp",
+ "exception": "cpp",
+ "algorithm": "cpp",
+ "functional": "cpp",
+ "iterator": "cpp",
+ "memory": "cpp",
+ "memory_resource": "cpp",
+ "numeric": "cpp",
+ "optional": "cpp",
+ "random": "cpp",
+ "ratio": "cpp",
+ "system_error": "cpp",
+ "tuple": "cpp",
+ "type_traits": "cpp",
+ "iomanip": "cpp",
+ "iosfwd": "cpp",
+ "iostream": "cpp",
+ "istream": "cpp",
+ "limits": "cpp",
+ "new": "cpp",
+ "numbers": "cpp",
+ "semaphore": "cpp",
+ "sstream": "cpp",
+ "stdexcept": "cpp",
+ "stop_token": "cpp",
+ "streambuf": "cpp",
+ "thread": "cpp",
+ "cinttypes": "cpp",
+ "typeinfo": "cpp",
+ "variant": "cpp"
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2426dd75d..d3d297a04 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,10 +4,9 @@
-
-
-
+
+
+
+
-
+
+
+
+
+
+
diff --git a/app/src/main/assets/jpeg_samples/sample1_0360.jpg b/app/src/main/assets/jpeg_samples/sample1_0360.jpg
new file mode 100644
index 000000000..a66d14812
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample1_0360.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample1_0480.jpg b/app/src/main/assets/jpeg_samples/sample1_0480.jpg
new file mode 100644
index 000000000..1621919c5
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample1_0480.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample1_0720.jpg b/app/src/main/assets/jpeg_samples/sample1_0720.jpg
new file mode 100644
index 000000000..44c2ad0eb
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample1_0720.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample1_1080.jpg b/app/src/main/assets/jpeg_samples/sample1_1080.jpg
new file mode 100644
index 000000000..daa58fe12
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample1_1080.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample1_1440.jpg b/app/src/main/assets/jpeg_samples/sample1_1440.jpg
new file mode 100644
index 000000000..1c6cc06ac
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample1_1440.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample1_2160.jpg b/app/src/main/assets/jpeg_samples/sample1_2160.jpg
new file mode 100644
index 000000000..64e61585d
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample1_2160.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample2_0360.jpg b/app/src/main/assets/jpeg_samples/sample2_0360.jpg
new file mode 100644
index 000000000..57b6fdb5e
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample2_0360.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample2_0480.jpg b/app/src/main/assets/jpeg_samples/sample2_0480.jpg
new file mode 100644
index 000000000..02d76fa23
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample2_0480.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample2_0720.jpg b/app/src/main/assets/jpeg_samples/sample2_0720.jpg
new file mode 100644
index 000000000..b96af2d12
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample2_0720.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample2_1080.jpg b/app/src/main/assets/jpeg_samples/sample2_1080.jpg
new file mode 100644
index 000000000..3322bdaa2
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample2_1080.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample2_1440.jpg b/app/src/main/assets/jpeg_samples/sample2_1440.jpg
new file mode 100644
index 000000000..10e830aae
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample2_1440.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample2_2160.jpg b/app/src/main/assets/jpeg_samples/sample2_2160.jpg
new file mode 100644
index 000000000..20dcdd261
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample2_2160.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample3_0360.jpg b/app/src/main/assets/jpeg_samples/sample3_0360.jpg
new file mode 100644
index 000000000..4caf97a70
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample3_0360.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample3_0480.jpg b/app/src/main/assets/jpeg_samples/sample3_0480.jpg
new file mode 100644
index 000000000..952f6209b
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample3_0480.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample3_0720.jpg b/app/src/main/assets/jpeg_samples/sample3_0720.jpg
new file mode 100644
index 000000000..918f98f16
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample3_0720.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample3_1080.jpg b/app/src/main/assets/jpeg_samples/sample3_1080.jpg
new file mode 100644
index 000000000..8d702d894
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample3_1080.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample3_1440.jpg b/app/src/main/assets/jpeg_samples/sample3_1440.jpg
new file mode 100644
index 000000000..227f56e03
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample3_1440.jpg differ
diff --git a/app/src/main/assets/jpeg_samples/sample3_2160.jpg b/app/src/main/assets/jpeg_samples/sample3_2160.jpg
new file mode 100644
index 000000000..73814dda5
Binary files /dev/null and b/app/src/main/assets/jpeg_samples/sample3_2160.jpg differ
diff --git a/app/src/main/java/com/vsh/activity/DevicesActivity.kt b/app/src/main/java/com/vsh/activity/DevicesActivity.kt
index 16a038342..3a49d328e 100644
--- a/app/src/main/java/com/vsh/activity/DevicesActivity.kt
+++ b/app/src/main/java/com/vsh/activity/DevicesActivity.kt
@@ -19,12 +19,14 @@ import android.hardware.usb.UsbManager
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
+import com.example.cupcake.ui.theme.AusbcTheme
import com.jiangdg.demo.MainActivity
-import com.vsh.screens.DeviceListScreen
+import com.vsh.screens.AusbcApp
import com.vsh.screens.DeviceListViewModel
import com.vsh.screens.DeviceListViewModelFactory
import kotlinx.coroutines.launch
@@ -35,6 +37,7 @@ class DevicesActivity : ComponentActivity() {
lateinit var viewModel: DeviceListViewModel
override fun onCreate(savedInstanceState: Bundle?) {
+ enableEdgeToEdge()
super.onCreate(savedInstanceState)
getWindow().getDecorView().setBackgroundColor(Color.White.toArgb())
viewModel = ViewModelProvider(
@@ -43,7 +46,9 @@ class DevicesActivity : ComponentActivity() {
)
).get(DeviceListViewModel::class.java)
setContent {
- DeviceListScreen.ScreenContent(viewModel = viewModel)
+ AusbcTheme {
+ AusbcApp(viewModel = viewModel)
+ }
}
}
diff --git a/app/src/main/java/com/vsh/activity/TestUsbBulkActivity.kt b/app/src/main/java/com/vsh/activity/TestUsbBulkActivity.kt
new file mode 100644
index 000000000..ac69e294c
--- /dev/null
+++ b/app/src/main/java/com/vsh/activity/TestUsbBulkActivity.kt
@@ -0,0 +1,92 @@
+package com.vsh.activity
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.hardware.usb.UsbDevice
+import android.hardware.usb.UsbDeviceConnection
+import android.hardware.usb.UsbEndpoint
+import android.hardware.usb.UsbManager
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import java.nio.charset.Charset
+
+class UsbHelper(private val context: Context) {
+ private val usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager
+ private val ACTION_USB_PERMISSION = "com.example.USB_PERMISSION"
+
+ fun findAndConnectDevice(): Pair? {
+ val deviceList = usbManager.deviceList
+ val device = deviceList.values.find { it.vendorId == 0x0000 && it.productId == 0x0001 }
+
+ if (device == null) {
+ println("Device not found")
+ return null
+ }
+
+ val permissionIntent = PendingIntent.getBroadcast(
+ context, 0, Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE
+ )
+
+ usbManager.requestPermission(device, permissionIntent)
+
+ if (!usbManager.hasPermission(device)) {
+ println("No permission to access USB device")
+ return null
+ }
+
+ val usbDeviceConnection = usbManager.openDevice(device) ?: return null
+ val interfaceUsb = device.getInterface(0)
+
+ val endpointOut = interfaceUsb.getEndpoint(0) // OUT
+ val endpointIn = interfaceUsb.getEndpoint(1) // IN
+
+ usbDeviceConnection.claimInterface(interfaceUsb, true)
+
+ println("Device connected: ${device.deviceName}")
+ return Pair(usbDeviceConnection, device)
+ }
+
+ fun sendData(connection: UsbDeviceConnection, endpointOut: UsbEndpoint, data: String) {
+ val bytes = data.toByteArray(Charset.defaultCharset())
+ val result = connection.bulkTransfer(endpointOut, bytes, bytes.size, 1000)
+ if (result > 0) {
+ println("Sent: $data")
+ } else {
+ println("Failed to send data")
+ }
+ }
+
+ fun receiveData(connection: UsbDeviceConnection, endpointIn: UsbEndpoint, bufferSize: Int = 64): String {
+ val buffer = ByteArray(bufferSize)
+ val received = connection.bulkTransfer(endpointIn, buffer, buffer.size, 1000)
+ return if (received > 0) {
+ String(buffer, 0, received, Charset.defaultCharset())
+ } else {
+ "No data received"
+ }
+ }
+}
+
+class TestUsbBulkActivity: ComponentActivity() {
+ private lateinit var usbHelper: UsbHelper
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ usbHelper = UsbHelper(this)
+
+ val pair = usbHelper.findAndConnectDevice()
+ if (pair != null) {
+ val device = pair.second
+ val connection = pair.first
+ val interfaceUsb = device.getInterface(0)
+ val endpointOut = interfaceUsb.getEndpoint(0)
+ val endpointIn = interfaceUsb.getEndpoint(1)
+
+ usbHelper.sendData(connection, endpointOut, "Hello USB!")
+ val response = usbHelper.receiveData(connection, endpointIn)
+ println("Received: $response")
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/vsh/components/CommonUi.kt b/app/src/main/java/com/vsh/components/CommonUi.kt
new file mode 100644
index 000000000..01206daa6
--- /dev/null
+++ b/app/src/main/java/com/vsh/components/CommonUi.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.cupcake.ui.components
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+
diff --git a/app/src/main/java/com/vsh/screens/DeviceListScreen.kt b/app/src/main/java/com/vsh/screens/DeviceListScreen.kt
index 275887f4f..25727fcdd 100644
--- a/app/src/main/java/com/vsh/screens/DeviceListScreen.kt
+++ b/app/src/main/java/com/vsh/screens/DeviceListScreen.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2024 vschryabets@gmail.com
+ * Copyright 2024-2025 vschryabets@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,24 +14,47 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalMaterial3Api::class)
+
package com.vsh.screens
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
-import androidx.compose.material.Button
-import androidx.compose.material.Text
+import androidx.compose.material.icons.Icons
+import androidx.compose.material3.Button
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.Icon
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.navigation.NavHostController
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import com.jiangdg.demo.R
+
+enum class AusbcScreen() {
+ Start,
+ Benchmarks,
+}
object DeviceListScreen {
@Composable
@@ -58,32 +81,108 @@ object DeviceListScreen {
}
@Composable
- fun ScreenContent(viewModel: DeviceListViewModel) {
- val state by viewModel.state.collectAsState(DeviceListViewState())
+ fun ScreenContent(
+ uiState: DeviceListViewState,
+ onBenchmarks: () -> Unit,
+ onReload: () -> Unit,
+ onSelectUsbDevice: (UsbDevice) -> Unit
+ ) {
Column(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth()
) {
- Button(
- modifier = Modifier.align(Alignment.End),
- onClick = {
- viewModel.onEnumarate()
- },
-
+ Row(
+ modifier = Modifier.align(Alignment.End)
+ ) {
+ Button(
+ modifier = Modifier.padding(start = 16.dp),
+ onClick = onBenchmarks
+ ) {
+ Text("Benchmarks")
+ }
+ Button(
+ modifier = Modifier.padding(start = 16.dp),
+ onClick = onReload
) {
- Text("Reload USB devices")
+ Text("Reload USB devices")
+ }
}
LazyColumn(
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 8.dp),
) {
- this.items(state.devices) {
- ProductItem(product = it) {
- viewModel.onClick(it)
- }
+ this.items(uiState.devices) {
+ ProductItem(product = it, onItemClick = onSelectUsbDevice)
+ }
+ }
+ }
+ }
+
+ @Composable
+ fun Benchmarks(viewModel: DeviceListViewModel) {
+
+ }
+}
+
+@Composable
+fun AusbcAppBar(
+ canNavigateBack: Boolean,
+ navigateUp: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ TopAppBar(
+ title = { Text(stringResource(id = R.string.app_name)) },
+ colors = TopAppBarDefaults.mediumTopAppBarColors(
+ containerColor = MaterialTheme.colorScheme.primaryContainer
+ ),
+ modifier = modifier,
+ navigationIcon = {
+ if (canNavigateBack) {
+ IconButton(onClick = navigateUp) {
+ Icon(
+ imageVector = Icons.Filled.ArrowBack,
+ contentDescription = stringResource(R.string.back_button)
+ )
}
}
}
+ )
+}
+
+@Composable
+fun AusbcApp(
+ viewModel: DeviceListViewModel,
+ navController: NavHostController = rememberNavController()
+) {
+ Scaffold(
+ topBar = {
+ AusbcAppBar(
+ canNavigateBack = false,
+ navigateUp = { /* TODO: implement back navigation */ }
+ )
+ }
+ ) { innerPadding ->
+ val uiState by viewModel.state.collectAsState()
+
+ NavHost(
+ navController = navController,
+ startDestination = AusbcScreen.Start.name,
+ modifier = Modifier.padding(innerPadding)
+ ) {
+ composable(route = AusbcScreen.Start.name) {
+ DeviceListScreen.ScreenContent(
+ uiState = uiState,
+ onBenchmarks = {
+ viewModel.onBenchmarks()
+ navController.navigate(AusbcScreen.Benchmarks.name)
+ },
+ onReload = { viewModel.onEnumarate() },
+ onSelectUsbDevice = { viewModel.onClick(it) })
+ }
+ composable(route = AusbcScreen.Benchmarks.name) {
+ DeviceListScreen.Benchmarks(viewModel)
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/vsh/screens/DeviceListViewModel.kt b/app/src/main/java/com/vsh/screens/DeviceListViewModel.kt
index 73e5f078d..85d2671d9 100644
--- a/app/src/main/java/com/vsh/screens/DeviceListViewModel.kt
+++ b/app/src/main/java/com/vsh/screens/DeviceListViewModel.kt
@@ -106,4 +106,8 @@ class DeviceListViewModel(
it.copy(openPreviewDeviceId = null)
}
}
+
+ fun onBenchmarks() {
+
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/vsh/theme/Color.kt b/app/src/main/java/com/vsh/theme/Color.kt
new file mode 100644
index 000000000..17fe94f70
--- /dev/null
+++ b/app/src/main/java/com/vsh/theme/Color.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.cupcake.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val md_theme_light_primary = Color(0xFF407C98)
+val md_theme_light_onPrimary = Color(0xFFFFFFFF)
+val md_theme_light_primaryContainer = Color(0xFFD9F7FF)
+val md_theme_light_onPrimaryContainer = Color(0xFF3E001E)
+val md_theme_light_secondary = Color(0xFF74565F)
+val md_theme_light_onSecondary = Color(0xFFFFFFFF)
+val md_theme_light_secondaryContainer = Color(0xFFD9FEFF)
+val md_theme_light_onSecondaryContainer = Color(0xFF2B151C)
+val md_theme_light_tertiary = Color(0xFF7C5635)
+val md_theme_light_onTertiary = Color(0xFFFFFFFF)
+val md_theme_light_tertiaryContainer = Color(0xFFC2FFE5)
+val md_theme_light_onTertiaryContainer = Color(0xFF2E1500)
+val md_theme_light_error = Color(0xFFBA1A1A)
+val md_theme_light_errorContainer = Color(0xFFFFDAD6)
+val md_theme_light_onError = Color(0xFFFFFFFF)
+val md_theme_light_onErrorContainer = Color(0xFF410002)
+val md_theme_light_background = Color(0xFFFFFBFF)
+val md_theme_light_onBackground = Color(0xFF201A1B)
+val md_theme_light_surface = Color(0xFFFFFBFF)
+val md_theme_light_onSurface = Color(0xFF201A1B)
+val md_theme_light_surfaceVariant = Color(0xFFF2DDE2)
+val md_theme_light_onSurfaceVariant = Color(0xFF514347)
+val md_theme_light_outline = Color(0xFF837377)
+val md_theme_light_inverseOnSurface = Color(0xFFFAEEEF)
+val md_theme_light_inverseSurface = Color(0xFF352F30)
+val md_theme_light_inversePrimary = Color(0xFFB0F8FF)
+val md_theme_light_surfaceTint = Color(0xFF409198)
+val md_theme_light_outlineVariant = Color(0xFFD5C2C6)
+val md_theme_light_scrim = Color(0xFF000000)
+
+val md_theme_dark_primary = Color(0xFFB0FFFF)
+val md_theme_dark_onPrimary = Color(0xFF115E5B)
+val md_theme_dark_primaryContainer = Color(0xFF297B7B)
+val md_theme_dark_onPrimaryContainer = Color(0xFFD9FFFE)
+val md_theme_dark_secondary = Color(0xFFBDE2DF)
+val md_theme_dark_onSecondary = Color(0xFF422931)
+val md_theme_dark_secondaryContainer = Color(0xFF5A3F47)
+val md_theme_dark_onSecondaryContainer = Color(0xFFD9FFFE)
+val md_theme_dark_tertiary = Color(0xFF94EFCB)
+val md_theme_dark_onTertiary = Color(0xFF48290C)
+val md_theme_dark_tertiaryContainer = Color(0xFF623F20)
+val md_theme_dark_onTertiaryContainer = Color(0xFFC2FFDA)
+val md_theme_dark_error = Color(0xFFFFB4AB)
+val md_theme_dark_errorContainer = Color(0xFF93000A)
+val md_theme_dark_onError = Color(0xFF690005)
+val md_theme_dark_onErrorContainer = Color(0xFFD6FFED)
+val md_theme_dark_background = Color(0xFF201A1B)
+val md_theme_dark_onBackground = Color(0xFFEBE0E1)
+val md_theme_dark_surface = Color(0xFF201A1B)
+val md_theme_dark_onSurface = Color(0xFFEBE0E1)
+val md_theme_dark_surfaceVariant = Color(0xFF514347)
+val md_theme_dark_onSurfaceVariant = Color(0xFFD5C2C6)
+val md_theme_dark_outline = Color(0xFF9E8C90)
+val md_theme_dark_inverseOnSurface = Color(0xFF201A1B)
+val md_theme_dark_inverseSurface = Color(0xFFEBE0E1)
+val md_theme_dark_inversePrimary = Color(0xFF409891)
+val md_theme_dark_surfaceTint = Color(0xFFB0FFFC)
+val md_theme_dark_outlineVariant = Color(0xFF514347)
+val md_theme_dark_scrim = Color(0xFF000000)
diff --git a/app/src/main/java/com/vsh/theme/Theme.kt b/app/src/main/java/com/vsh/theme/Theme.kt
new file mode 100644
index 000000000..029b38039
--- /dev/null
+++ b/app/src/main/java/com/vsh/theme/Theme.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.cupcake.ui.theme
+
+import android.app.Activity
+import android.os.Build
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
+import androidx.core.view.WindowCompat
+
+private val LightColors = lightColorScheme(
+ primary = md_theme_light_primary,
+ onPrimary = md_theme_light_onPrimary,
+ primaryContainer = md_theme_light_primaryContainer,
+ onPrimaryContainer = md_theme_light_onPrimaryContainer,
+ secondary = md_theme_light_secondary,
+ onSecondary = md_theme_light_onSecondary,
+ secondaryContainer = md_theme_light_secondaryContainer,
+ onSecondaryContainer = md_theme_light_onSecondaryContainer,
+ tertiary = md_theme_light_tertiary,
+ onTertiary = md_theme_light_onTertiary,
+ tertiaryContainer = md_theme_light_tertiaryContainer,
+ onTertiaryContainer = md_theme_light_onTertiaryContainer,
+ error = md_theme_light_error,
+ errorContainer = md_theme_light_errorContainer,
+ onError = md_theme_light_onError,
+ onErrorContainer = md_theme_light_onErrorContainer,
+ background = md_theme_light_background,
+ onBackground = md_theme_light_onBackground,
+ surface = md_theme_light_surface,
+ onSurface = md_theme_light_onSurface,
+ surfaceVariant = md_theme_light_surfaceVariant,
+ onSurfaceVariant = md_theme_light_onSurfaceVariant,
+ outline = md_theme_light_outline,
+ inverseOnSurface = md_theme_light_inverseOnSurface,
+ inverseSurface = md_theme_light_inverseSurface,
+ inversePrimary = md_theme_light_inversePrimary,
+ surfaceTint = md_theme_light_surfaceTint,
+ outlineVariant = md_theme_light_outlineVariant,
+ scrim = md_theme_light_scrim,
+)
+
+private val DarkColors = darkColorScheme(
+ primary = md_theme_dark_primary,
+ onPrimary = md_theme_dark_onPrimary,
+ primaryContainer = md_theme_dark_primaryContainer,
+ onPrimaryContainer = md_theme_dark_onPrimaryContainer,
+ secondary = md_theme_dark_secondary,
+ onSecondary = md_theme_dark_onSecondary,
+ secondaryContainer = md_theme_dark_secondaryContainer,
+ onSecondaryContainer = md_theme_dark_onSecondaryContainer,
+ tertiary = md_theme_dark_tertiary,
+ onTertiary = md_theme_dark_onTertiary,
+ tertiaryContainer = md_theme_dark_tertiaryContainer,
+ onTertiaryContainer = md_theme_dark_onTertiaryContainer,
+ error = md_theme_dark_error,
+ errorContainer = md_theme_dark_errorContainer,
+ onError = md_theme_dark_onError,
+ onErrorContainer = md_theme_dark_onErrorContainer,
+ background = md_theme_dark_background,
+ onBackground = md_theme_dark_onBackground,
+ surface = md_theme_dark_surface,
+ onSurface = md_theme_dark_onSurface,
+ surfaceVariant = md_theme_dark_surfaceVariant,
+ onSurfaceVariant = md_theme_dark_onSurfaceVariant,
+ outline = md_theme_dark_outline,
+ inverseOnSurface = md_theme_dark_inverseOnSurface,
+ inverseSurface = md_theme_dark_inverseSurface,
+ inversePrimary = md_theme_dark_inversePrimary,
+ surfaceTint = md_theme_dark_surfaceTint,
+ outlineVariant = md_theme_dark_outlineVariant,
+ scrim = md_theme_dark_scrim,
+)
+
+@Composable
+fun AusbcTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ // Dynamic color is available on Android 12+ but turned off for training purposes
+ dynamicColor: Boolean = false,
+ content: @Composable () -> Unit
+) {
+ val colorScheme = when {
+ dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ val context = LocalContext.current
+ if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+ }
+
+ darkTheme -> DarkColors
+ else -> LightColors
+ }
+ val view = LocalView.current
+ if (!view.isInEditMode) {
+ SideEffect {
+ val window = (view.context as Activity).window
+ window.statusBarColor = colorScheme.primary.toArgb()
+ WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
+ }
+ }
+
+ MaterialTheme(
+ colorScheme = colorScheme,
+ typography = Typography,
+ content = content
+ )
+}
diff --git a/app/src/main/java/com/vsh/theme/Type.kt b/app/src/main/java/com/vsh/theme/Type.kt
new file mode 100644
index 000000000..1085d8546
--- /dev/null
+++ b/app/src/main/java/com/vsh/theme/Type.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.cupcake.ui.theme
+
+import androidx.compose.material3.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ bodyLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp
+ )
+)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3c578ac8a..4eddeb876 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -3,4 +3,5 @@
You have already denied permission access. Go to the Settings page to turn on permissions
Contact me:
765067602@qq.com\n\n❤ version: %s
+ back
\ No newline at end of file
diff --git a/app/src/main/res/xml/usb_device_filter.xml b/app/src/main/res/xml/usb_device_filter.xml
new file mode 100644
index 000000000..3b9ecb2e0
--- /dev/null
+++ b/app/src/main/res/xml/usb_device_filter.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/utils/BuildCheck.java b/libausbc/src/main/java/com/jiangdg/utils/BuildCheck.java
deleted file mode 100644
index 381459cfb..000000000
--- a/libausbc/src/main/java/com/jiangdg/utils/BuildCheck.java
+++ /dev/null
@@ -1,476 +0,0 @@
-package com.jiangdg.utils;
-/*
- * libcommon
- * utility/helper classes for myself
- *
- * Copyright (c) 2014-2018 saki t_saki@serenegiant.com
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
-
-import android.os.Build;
-
-public final class BuildCheck {
-
- private static final boolean check(final int value) {
- return (Build.VERSION.SDK_INT >= value);
- }
-
- /**
- * Magic version number for a current development build,
- * which has not yet turned into an official release. API=10000
- * @return
- */
- public static boolean isCurrentDevelopment() {
- return (Build.VERSION.SDK_INT == Build.VERSION_CODES.CUR_DEVELOPMENT);
- }
-
- /**
- * October 2008: The original, first, version of Android. Yay!, API>=1
- * @return
- */
- public static boolean isBase() {
- return check(Build.VERSION_CODES.BASE);
- }
-
- /**
- * February 2009: First Android update, officially called 1.1., API>=2
- * @return
- */
- public static boolean isBase11() {
- return check(Build.VERSION_CODES.BASE_1_1);
- }
-
- /**
- * May 2009: Android 1.5., API>=3
- * @return
- */
- public static boolean isCupcake() {
- return check(Build.VERSION_CODES.CUPCAKE);
- }
-
- /**
- * May 2009: Android 1.5., API>=3
- * @return
- */
- public static boolean isAndroid1_5() {
- return check(Build.VERSION_CODES.CUPCAKE);
- }
-
- /**
- * September 2009: Android 1.6., API>=4
- * @return
- */
- public static boolean isDonut() {
- return check(Build.VERSION_CODES.DONUT);
- }
-
- /**
- * September 2009: Android 1.6., API>=4
- * @return
- */
- public static boolean isAndroid1_6() {
- return check(Build.VERSION_CODES.DONUT);
- }
-
- /**
- * November 2009: Android 2.0, API>=5
- * @return
- */
- public static boolean isEclair() {
- return check(Build.VERSION_CODES.ECLAIR);
- }
-
- /**
- * November 2009: Android 2.0, API>=5
- * @return
- */
- public static boolean isAndroid2_0() {
- return check(Build.VERSION_CODES.ECLAIR);
- }
-
- /**
- * December 2009: Android 2.0.1, API>=6
- * @return
- */
- public static boolean isEclair01() {
- return check(Build.VERSION_CODES.ECLAIR_0_1);
- }
-
- /**
- * January 2010: Android 2.1, API>=7
- * @return
- */
- public static boolean isEclairMR1() {
- return check(Build.VERSION_CODES.ECLAIR_MR1);
- }
-
- /**
- * June 2010: Android 2.2, API>=8
- * @return
- */
- public static boolean isFroyo() {
- return check(Build.VERSION_CODES.FROYO);
- }
-
- /**
- * June 2010: Android 2.2, API>=8
- * @return
- */
- public static boolean isAndroid2_2() {
- return check(Build.VERSION_CODES.FROYO);
- }
-
- /**
- * November 2010: Android 2.3, API>=9
- * @return
- */
- public static boolean isGingerBread() {
- return check(Build.VERSION_CODES.GINGERBREAD);
- }
-
- /**
- * November 2010: Android 2.3, API>=9
- * @return
- */
- public static boolean isAndroid2_3() {
- return check(Build.VERSION_CODES.GINGERBREAD);
- }
-
- /**
- * February 2011: Android 2.3.3., API>=10
- * @return
- */
- public static boolean isGingerBreadMR1() {
- return check(Build.VERSION_CODES.GINGERBREAD_MR1);
- }
-
- /**
- * February 2011: Android 2.3.3., API>=10
- * @return
- */
- public static boolean isAndroid2_3_3() {
- return check(Build.VERSION_CODES.GINGERBREAD_MR1);
- }
-
- /**
- * February 2011: Android 3.0., API>=11
- * @return
- */
- public static boolean isHoneyComb() {
- return check(Build.VERSION_CODES.HONEYCOMB);
- }
-
- /**
- * February 2011: Android 3.0., API>=11
- * @return
- */
- public static boolean isAndroid3() {
- return check(Build.VERSION_CODES.HONEYCOMB);
- }
-
- /**
- * May 2011: Android 3.1., API>=12
- * @return
- */
- public static boolean isHoneyCombMR1() {
- return check(Build.VERSION_CODES.HONEYCOMB_MR1);
- }
-
- /**
- * May 2011: Android 3.1., API>=12
- * @return
- */
- public static boolean isAndroid3_1() {
- return check(Build.VERSION_CODES.HONEYCOMB_MR1);
- }
-
- /**
- * June 2011: Android 3.2., API>=13
- * @return
- */
- public static boolean isHoneyCombMR2() {
- return check(Build.VERSION_CODES.HONEYCOMB_MR2);
- }
-
- /**
- * June 2011: Android 3.2., API>=13
- * @return
- */
- public static boolean isAndroid3_2() {
- return check(Build.VERSION_CODES.HONEYCOMB_MR2);
- }
-
- /**
- * October 2011: Android 4.0., API>=14
- * @return
- */
- public static boolean isIcecreamSandwich() {
- return check(Build.VERSION_CODES.ICE_CREAM_SANDWICH);
- }
-
- /**
- * October 2011: Android 4.0., API>=14
- * @return
- */
- public static boolean isAndroid4() {
- return check(Build.VERSION_CODES.ICE_CREAM_SANDWICH);
- }
-
- /**
- * December 2011: Android 4.0.3., API>=15
- * @return
- */
- public static boolean isIcecreamSandwichMR1() {
- return check(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1);
- }
-
- /**
- * December 2011: Android 4.0.3., API>=15
- * @return
- */
- public static boolean isAndroid4_0_3() {
- return check(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1);
- }
-
- /**
- * June 2012: Android 4.1., API>=16
- * @return
- */
- public static boolean isJellyBean() {
- return check(Build.VERSION_CODES.JELLY_BEAN);
- }
-
- /**
- * June 2012: Android 4.1., API>=16
- * @return
- */
- public static boolean isAndroid4_1() {
- return check(Build.VERSION_CODES.JELLY_BEAN);
- }
-
- /**
- * November 2012: Android 4.2, Moar jelly beans!, API>=17
- * @return
- */
- public static boolean isJellyBeanMr1() {
- return check(Build.VERSION_CODES.JELLY_BEAN_MR1);
- }
-
- /**
- * November 2012: Android 4.2, Moar jelly beans!, API>=17
- * @return
- */
- public static boolean isAndroid4_2() {
- return check(Build.VERSION_CODES.JELLY_BEAN_MR1);
- }
-
- /**
- * July 2013: Android 4.3, the revenge of the beans., API>=18
- * @return
- */
- public static boolean isJellyBeanMR2() {
- return check(Build.VERSION_CODES.JELLY_BEAN_MR2);
- }
-
- /**
- * July 2013: Android 4.3, the revenge of the beans., API>=18
- * @return
- */
- public static boolean isAndroid4_3() {
- return check(Build.VERSION_CODES.JELLY_BEAN_MR2);
- }
-
- /**
- * October 2013: Android 4.4, KitKat, another tasty treat., API>=19
- * @return
- */
- public static boolean isKitKat() {
- return check(Build.VERSION_CODES.KITKAT);
- }
-
- /**
- * October 2013: Android 4.4, KitKat, another tasty treat., API>=19
- * @return
- */
- public static boolean isAndroid4_4() {
- return check(Build.VERSION_CODES.KITKAT);
- }
-
- /**
- * Android 4.4W: KitKat for watches, snacks on the run., API>=20
- * @return
- */
- public static boolean isKitKatWatch() {
- return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH);
- }
-
- /**
- * Lollipop. A flat one with beautiful shadows. But still tasty., API>=21
- * @return
- */
- public static boolean isL() {
- return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP);
- }
-
- /**
- * Lollipop. A flat one with beautiful shadows. But still tasty., API>=21
- * @return
- */
- public static boolean isLollipop() {
- return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP);
- }
-
- /**
- * Lollipop. A flat one with beautiful shadows. But still tasty., API>=21
- * @return
- */
- public static boolean isAndroid5() {
- return check(Build.VERSION_CODES.LOLLIPOP);
- }
-
- /**
- * Lollipop with an extra sugar coating on the outside!, API>=22
- * @return
- */
- public static boolean isLollipopMR1() {
- return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1);
- }
-
- /**
- * Marshmallow. A flat one with beautiful shadows. But still tasty., API>=23
- * @return
- */
- public static boolean isM() {
- return check(Build.VERSION_CODES.M);
- }
-
- /**
- * Marshmallow. A flat one with beautiful shadows. But still tasty., API>=23
- * @return
- */
- public static boolean isMarshmallow() {
- return check(Build.VERSION_CODES.M);
- }
-
- /**
- * Marshmallow. A flat one with beautiful shadows. But still tasty., API>=23
- * @return
- */
- public static boolean isAndroid6() {
- return check(Build.VERSION_CODES.M);
- }
-
- /**
- * 虫歯の元, API >= 24
- * @return
- */
- public static boolean isN() {
- return check(Build.VERSION_CODES.N);
- }
-
- /**
- * 歯にくっつくやつ, API >= 24
- * @return
- */
- public static boolean isNougat() {
- return check(Build.VERSION_CODES.N);
- }
- /**
- * API >= 24
- * @return
- */
- public static boolean isAndroid7() {
- return check(Build.VERSION_CODES.N);
- }
-
- /**
- * API>=25
- * @return
- */
- public static boolean isNMR1() {
- return check(Build.VERSION_CODES.N_MR1);
- }
-
- /**
- * API>=25
- * @return
- */
- public static boolean isNougatMR1() {
- return check(Build.VERSION_CODES.N_MR1);
- }
-
- /**
- * おれおれぇー API>=26
- * @return
- */
- public static boolean isO() {
- return check(Build.VERSION_CODES.O);
- }
-
- /**
- * おれおれぇー API>=26
- * @return
- */
- public static boolean isOreo() {
- return check(Build.VERSION_CODES.O);
- }
-
- /**
- * おれおれぇー API>=26
- * @return
- */
- public static boolean isAndroid8() {
- return check(Build.VERSION_CODES.O);
- }
-
- /**
- * おれおれぇー API>=27
- * @return
- */
- public static boolean isOMR1() {
- return check(Build.VERSION_CODES.O_MR1);
- }
-
- /**
- * おれおれぇー MR1 API>=27
- * @return
- */
- public static boolean isOreoMR1() {
- return check((Build.VERSION_CODES.O_MR1));
- }
-
- /**
- * おっ!ぱい API>=28
- * @return
- */
- public static boolean isP() {
- return check((Build.VERSION_CODES.P));
- }
-
- /**
- * おっ!ぱい API>=28
- * @return
- */
- public static boolean isPie() {
- return check((Build.VERSION_CODES.P));
- }
-
- /**
- * おっ!ぱい API>=28
- * @return
- */
- public static boolean isAndroid9() {
- return check((Build.VERSION_CODES.P));
- }
-}
diff --git a/libausbc/src/main/java/com/vsh/uvc/JpegBenchmark.kt b/libausbc/src/main/java/com/vsh/uvc/JpegBenchmark.kt
new file mode 100644
index 000000000..4657f3bf3
--- /dev/null
+++ b/libausbc/src/main/java/com/vsh/uvc/JpegBenchmark.kt
@@ -0,0 +1,41 @@
+package com.vsh.uvc
+
+class JpegBenchmark {
+ data class Results (
+ val id: Int,
+ val decodeCount360: Int,
+ val decodeTime360Ms: Int,
+ val decodeCount480: Int,
+ val decodeTime480Ms: Int,
+ val decodeCount720: Int,
+ val decodeTime720Ms: Int,
+ val decodeCount1080: Int,
+ val decodeTime1080Ms: Int,
+ val decodeCount1440: Int,
+ val decodeTime1440Ms: Int,
+ val decodeCount2160: Int,
+ val decodeTime2160Ms: Int,
+ )
+
+ data class Arguments (
+ val images360: Array,
+ val images480: Array,
+ val images720: Array,
+ val images1080: Array,
+ val images1440: Array,
+ val images2160: Array,
+ val decodeNumber: Int = 300,
+ )
+
+ fun startBenchmark(args: Arguments): Int = nativeStartBenchmark(args)
+
+ fun cancelBenchmark(id: Int) {
+
+ }
+
+ fun getBenchmarkResults(id: Int): Results = getBenchmarkResults(id)
+
+ private external fun nativeStartBenchmark(args: Arguments): Int
+ private external fun nativeCancelBenchmark(id: Int)
+ private external fun nativeGetBenchamrkResults(id: Int): Results
+}
\ No newline at end of file
diff --git a/libausbc/src/main/jni/UVCCamera/DI.h b/libausbc/src/main/jni/UVCCamera/DI.h
new file mode 100644
index 000000000..0cf6be40e
--- /dev/null
+++ b/libausbc/src/main/jni/UVCCamera/DI.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2025 vschryabets@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include
+#include
+
+class LoadJpegImageUseCase {
+ public:
+ struct Result {
+ uint8_t* buffer;
+ size_t size;
+ };
+ public:
+ virtual Result load(std::string imageId) = 0;
+};
+
+class DecodeJpegImageUseCase {
+ public:
+ virtual void decodeImage(uint8_t* encodedBuffer,
+ size_t encodedBufferSize,
+ uint8_t* decodedBuffer,
+ size_t decodedBufferSize) = 0;
+ virtual std::string getDecoderName() = 0;
+};
+
+class SaveBitmapImageUseCase {
+ public:
+ virtual void save(std::string imageId, uint8_t* buffer, size_t size) = 0;
+};
+
+struct UseCases {
+ LoadJpegImageUseCase* imageLoader;
+ DecodeJpegImageUseCase* imageDecoder;
+ SaveBitmapImageUseCase* imageSaver;
+};
+
+class DI {
+ private:
+ static DI* instance;
+ public:
+ static DI* getInstance() { return instance; }
+ static void setInstance(DI* instance) { DI::instance = instance; }
+ virtual UseCases* getUseCases() = 0;
+};
\ No newline at end of file
diff --git a/libausbc/src/main/jni/UVCCamera/JniJpegBenchmark.cpp b/libausbc/src/main/jni/UVCCamera/JniJpegBenchmark.cpp
new file mode 100644
index 000000000..a151851ce
--- /dev/null
+++ b/libausbc/src/main/jni/UVCCamera/JniJpegBenchmark.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2025 vschryabets@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "JniJpegBenchmark.h"
+#include
+
+extern "C" {
+
+JNIEXPORT jint JNICALL
+Java_com_vsh_uvc_JpegBenchmark_nativeStartBenchmark(JNIEnv *env,
+ jobject thiz,
+ jobject args) {
+
+}
+
+JNIEXPORT void JNICALL
+Java_com_vsh_uvc_JpegBenchmark_nativeCancelBenchmark(JNIEnv *env,
+ jobject thiz,
+ jint id) {
+ // TODO: implement nativeCancelBenchmark()
+}
+
+JNIEXPORT jobject JNICALL
+Java_com_vsh_uvc_JpegBenchmark_nativeGetBenchamrkResults(JNIEnv *env, jobject thiz, jint id) {
+ // TODO: implement nativeGetBenchamrkResults()
+}
+}
+
diff --git a/libausbc/src/main/jni/UVCCamera/JniJpegBenchmark.h b/libausbc/src/main/jni/UVCCamera/JniJpegBenchmark.h
new file mode 100644
index 000000000..c60cbb325
--- /dev/null
+++ b/libausbc/src/main/jni/UVCCamera/JniJpegBenchmark.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2025 vschryabets@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROIDUSBCAMERA_JNIJPEGBENCHMARK_H
+#define ANDROIDUSBCAMERA_JNIJPEGBENCHMARK_H
+
+
+
+#endif //ANDROIDUSBCAMERA_JNIJPEGBENCHMARK_H
diff --git a/libausbc/src/main/jni/UVCCamera/JpegBenchmark.cpp b/libausbc/src/main/jni/UVCCamera/JpegBenchmark.cpp
new file mode 100644
index 000000000..968a8fad8
--- /dev/null
+++ b/libausbc/src/main/jni/UVCCamera/JpegBenchmark.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2025 vschryabets@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "JpegBenchmark.h"
+#include
+#include
+
+#ifdef USE_LIBJPEG
+ #include "jpeglib.h"
+#endif
+
+LoadJpegImageUseCase::Result LoadJpegImageFromFileUseCase::load(std::string imageId) {
+ FILE* file = fopen(imageId.c_str(), "rb");
+ if (file == nullptr) {
+ return LoadJpegImageUseCase::Result{nullptr, 0};
+ }
+ fseek(file, 0, SEEK_END);
+ size_t size = ftell(file);
+ uint8_t* buffer = new uint8_t[size];
+ fseek(file, 0, SEEK_SET);
+ fread(buffer, 1, size, file);
+ fclose(file);
+ return LoadJpegImageUseCase::Result{buffer, size};
+}
+
+#ifdef USE_TURBOJPEG
+void DecodeJpegImageTurboJpegUseCase::decodeImage(uint8_t* encodedBuffer,
+ size_t encodedBufferSize,
+ uint8_t* decodedBuffer,
+ size_t decodedBufferSize) {
+ // Decode image
+}
+
+std::string DecodeJpegImageTurboJpegUseCase::getDecoderName() {
+ return "JPEG";
+}
+#endif
+
+#ifdef USE_LIBJPEG
+void DecodeJpegImageLibJpeg9UseCase::decodeImage(uint8_t* encodedBuffer,
+ size_t encodedBufferSize,
+ uint8_t* decodedBuffer,
+ size_t decodedBufferSize) {
+ // Decode image
+ jpeg_decompress_struct cinfo;
+ jpeg_error_mgr jerr;
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_decompress(&cinfo);
+ jpeg_mem_src(&cinfo, encodedBuffer, encodedBufferSize);
+ jpeg_read_header(&cinfo, TRUE);
+ jpeg_start_decompress(&cinfo);
+
+ int row_stride = cinfo.output_width * cinfo.output_components;
+ while (cinfo.output_scanline < cinfo.output_height) {
+ uint8_t* buffer_array[1];
+ buffer_array[0] = decodedBuffer + (cinfo.output_scanline) * row_stride;
+ jpeg_read_scanlines(&cinfo, buffer_array, 1);
+ }
+
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
+}
+
+void DecodeJpegImageLibJpeg9UseCase::JPEGVersionError(j_common_ptr cinfo){}
+
+std::string DecodeJpegImageLibJpeg9UseCase::getDecoderName() {
+ jpeg_decompress_struct cinfo;
+ jpeg_error_mgr error_mgr;
+ error_mgr.error_exit = &JPEGVersionError;
+ cinfo.err = &error_mgr;
+ char *result = nullptr;
+ jpeg_CreateDecompress(&cinfo, -1 /*version*/, sizeof(cinfo));
+ int jpeg_version = cinfo.err->msg_parm.i[0];
+ return "LibJPEG " + std::to_string(jpeg_version);
+}
+#endif
+
+JpegBenchmark::JpegBenchmark() {
+
+}
+
+void JpegBenchmark::start(const Arguments& args) {
+
+ std::vector> buffers(args.imageSamples.size());
+
+ std::transform(args.imageSamples.begin(), args.imageSamples.end(), buffers.begin(),
+ [load=DI::getInstance()->getUseCases()->imageLoader](auto &it) {
+ return std::pair(it.first, load->load(it.second));
+ }
+ );
+
+ size_t decodedBufferSize = 3840 * 2160 * 3;
+ uint8_t *decodedBuffer4k = new uint8_t[decodedBufferSize];
+ DecodeJpegImageUseCase* decode = DI::getInstance()->getUseCases()->imageDecoder;
+
+ for (auto& it : buffers) {
+ for (int i = 0; i < args.iterations; i++) {
+ decode->decodeImage(it.second.buffer, it.second.size, decodedBuffer4k, decodedBufferSize);
+ }
+ }
+ delete[] decodedBuffer4k;
+ for (auto& it : buffers) {
+ delete[] it.second.buffer;
+ }
+}
+
+void SaveBitmapImageToFileUseCase::save(std::string imageId, uint8_t* buffer, size_t size) {
+ FILE* file = fopen(imageId.c_str(), "wb");
+ if (file == nullptr) {
+ return;
+ }
+ fwrite(buffer, 1, size, file);
+ fclose(file);
+}
\ No newline at end of file
diff --git a/libausbc/src/main/jni/UVCCamera/JpegBenchmark.h b/libausbc/src/main/jni/UVCCamera/JpegBenchmark.h
new file mode 100644
index 000000000..3f04dc377
--- /dev/null
+++ b/libausbc/src/main/jni/UVCCamera/JpegBenchmark.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2025 vschryabets@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include
+#include
+#include
+#include
+#include "DI.h"
+
+#ifdef USE_LIBJPEG
+ #include "jpeglib.h"
+#endif
+
+class LoadJpegImageFromFileUseCase : public LoadJpegImageUseCase {
+ public:
+ Result load(std::string imageId) override;
+};
+
+class SaveBitmapImageToFileUseCase : public SaveBitmapImageUseCase {
+ public:
+ void save(std::string imageId, uint8_t* buffer, size_t size) override;
+};
+
+#ifdef USE_TURBOJPEG
+class DecodeJpegImageTurboJpegUseCase : public DecodeJpegImageUseCase {
+ public:
+ void decodeImage(uint8_t* encodedBuffer,
+ size_t encodedBufferSize,
+ uint8_t* decodedBuffer,
+ size_t decodedBufferSize) override;
+ std::string getDecoderName() override;
+};
+#endif
+
+#ifdef USE_LIBJPEG
+class DecodeJpegImageLibJpeg9UseCase : public DecodeJpegImageUseCase {
+ private:
+ static void JPEGVersionError(j_common_ptr cinfo);
+ public:
+ void decodeImage(uint8_t* encodedBuffer,
+ size_t encodedBufferSize,
+ uint8_t* decodedBuffer,
+ size_t decodedBufferSize) override;
+ std::string getDecoderName() override;
+};
+#endif
+
+class JpegBenchmark {
+ public:
+ struct Arguments {
+ std::vector> imageSamples;
+ int iterations;
+ };
+ public:
+ JpegBenchmark();
+ virtual ~JpegBenchmark() {};
+ void start(const Arguments& args);
+};
\ No newline at end of file
diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt
new file mode 100644
index 000000000..a77882ddf
--- /dev/null
+++ b/linux/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 3.22)
+
+project(LinuxUvcTests)
+
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+set(CMAKE_C_STANDARD 99)
+set(CMAKE_C_STANDARD_REQUIRED True)
+
+find_package( Threads REQUIRED )
+find_package (JPEG REQUIRED)
+
+include_directories(
+ ../libausbc/src/main/jni/UVCCamera/
+)
+
+add_executable(uvccapture
+ uvccapture.cpp
+)
+
+target_link_libraries(
+ uvccapture
+ ${CMAKE_THREAD_LIBS_INIT})
+
+
+add_executable(jpegbenchmark
+ jpegdecodebenchmark.cpp
+ ../libausbc/src/main/jni/UVCCamera/JpegBenchmark.cpp
+ )
+target_compile_definitions(jpegbenchmark PRIVATE -DUSE_LIBJPEG)
+target_link_libraries(
+ jpegbenchmark
+ ${JPEG_LIBRARIES}
+ ${CMAKE_THREAD_LIBS_INIT})
+
+
diff --git a/linux/jpegdecodebenchmark.cpp b/linux/jpegdecodebenchmark.cpp
new file mode 100644
index 000000000..2b4086230
--- /dev/null
+++ b/linux/jpegdecodebenchmark.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2025 vschryabets@gmail.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include
+#include
+#include
+
+#include "DI.h"
+#include "JpegBenchmark.h"
+
+class LinuxUseCases: public UseCases {
+ private:
+ LoadJpegImageFromFileUseCase imageLoader;
+ DecodeJpegImageLibJpeg9UseCase decoder;
+ public:
+ LoadJpegImageUseCase* getImageLoader() { return &imageLoader; }
+ DecodeJpegImageUseCase* getImageDecoder() { return &decoder; }
+};
+
+DI* DI::instance = nullptr;
+class LinuxDi: public DI {
+ private:
+ LoadJpegImageFromFileUseCase imageLoader;
+ DecodeJpegImageLibJpeg9UseCase decoder;
+ SaveBitmapImageToFileUseCase imageSaver;
+
+ UseCases useCases;
+ public:
+ LinuxDi(): useCases{
+ .imageLoader = &imageLoader,
+ .imageDecoder = &decoder,
+ .imageSaver = &imageSaver
+ } {
+ DI::setInstance(this);
+ }
+
+ UseCases* getUseCases() {
+ return &useCases;
+ }
+};
+
+int main(void) {
+ LinuxDi di;
+ printf("Decoder %s\n", di.getUseCases()->imageDecoder->getDecoderName().c_str());
+ JpegBenchmark benchmark;
+ benchmark.start({
+ .imageSamples = {
+ { 360, "./app/src/main/assets/jpeg_samples/sample1_0360.jpg" },
+ { 480, "./app/src/main/assets/jpeg_samples/sample1_0480.jpg" },
+ { 720, "./app/src/main/assets/jpeg_samples/sample1_0720.jpg" },
+ { 1080, "./app/src/main/assets/jpeg_samples/sample1_1080.jpg" },
+ { 1440, "./app/src/main/assets/jpeg_samples/sample1_1440.jpg" },
+ { 2160, "./app/src/main/assets/jpeg_samples/sample1_2160.jpg" },
+ },
+ .iterations = 1
+ });
+
+ return 0;
+}
diff --git a/linux/uvccapture.cpp b/linux/uvccapture.cpp
new file mode 100644
index 000000000..5935bf10c
--- /dev/null
+++ b/linux/uvccapture.cpp
@@ -0,0 +1,544 @@
+#include
+#include
+#include
+
+// V4L UVC
+#include
+#include
+#include
+#include
+#include /* low-level i/o */
+#include
+#include
+
+using namespace std::chrono;
+
+enum io_method {
+ IO_METHOD_READ,
+ IO_METHOD_MMAP,
+ IO_METHOD_USERPTR,
+};
+
+struct buffer {
+ void *start;
+ size_t length;
+};
+
+int fd = -1;
+struct buffer *buffers;
+static unsigned int n_buffers;
+static enum io_method io = IO_METHOD_MMAP;
+static int force_format = 1;
+
+void draw_frame() {
+}
+
+void preDraw() {
+}
+
+#define CLEAR(x) memset(&(x), 0, sizeof(x))
+
+static void errno_exit(const char *s) {
+ fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));
+ exit(EXIT_FAILURE);
+}
+
+static void open_device(const char *dev_name) {
+ struct stat st;
+
+ if (-1 == stat(dev_name, &st)) {
+ fprintf(stderr, "Cannot identify '%s': %d, %s\n",
+ dev_name, errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (!S_ISCHR(st.st_mode)) {
+ fprintf(stderr, "%s is no device\n", dev_name);
+ exit(EXIT_FAILURE);
+ }
+
+ fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
+
+ if (-1 == fd) {
+ fprintf(stderr, "Cannot open '%s': %d, %s\n",
+ dev_name, errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void close_device(void) {
+ if (-1 == close(fd))
+ errno_exit("close");
+ fd = -1;
+}
+
+
+static int xioctl(int fh, int request, void *arg) {
+ int r;
+
+ do {
+ r = ioctl(fh, request, arg);
+ } while (-1 == r && EINTR == errno);
+
+ return r;
+}
+
+
+static void init_read(unsigned int buffer_size)
+{
+ buffers = (buffer*)calloc(1, sizeof(*buffers));
+
+ if (!buffers) {
+ fprintf(stderr, "Out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ buffers[0].length = buffer_size;
+ buffers[0].start = malloc(buffer_size);
+
+ if (!buffers[0].start) {
+ fprintf(stderr, "Out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void init_mmap(const char* dev_name)
+{
+ struct v4l2_requestbuffers req;
+
+ CLEAR(req);
+
+ req.count = 4;
+ req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req.memory = V4L2_MEMORY_MMAP;
+
+ if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
+ if (EINVAL == errno) {
+ fprintf(stderr, "%s does not support "
+ "memory mapping\n", dev_name);
+ exit(EXIT_FAILURE);
+ } else {
+ errno_exit("VIDIOC_REQBUFS");
+ }
+ }
+
+ if (req.count < 2) {
+ fprintf(stderr, "Insufficient buffer memory on %s\n",
+ dev_name);
+ exit(EXIT_FAILURE);
+ }
+
+ buffers = (buffer*)calloc(req.count, sizeof(*buffers));
+
+ if (!buffers) {
+ fprintf(stderr, "Out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
+ struct v4l2_buffer buf;
+
+ CLEAR(buf);
+
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = n_buffers;
+
+ if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))
+ errno_exit("VIDIOC_QUERYBUF");
+
+ buffers[n_buffers].length = buf.length;
+ buffers[n_buffers].start =
+ mmap(NULL /* start anywhere */,
+ buf.length,
+ PROT_READ | PROT_WRITE /* required */,
+ MAP_SHARED /* recommended */,
+ fd, buf.m.offset);
+
+ if (MAP_FAILED == buffers[n_buffers].start)
+ errno_exit("mmap");
+ }
+}
+
+static void init_userp(unsigned int buffer_size, const char* dev_name)
+{
+ struct v4l2_requestbuffers req;
+
+ CLEAR(req);
+
+ req.count = 4;
+ req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req.memory = V4L2_MEMORY_USERPTR;
+
+ if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
+ if (EINVAL == errno) {
+ fprintf(stderr, "%s does not support "
+ "user pointer i/o\n", dev_name);
+ exit(EXIT_FAILURE);
+ } else {
+ errno_exit("VIDIOC_REQBUFS");
+ }
+ }
+
+ buffers = (buffer*)calloc(4, sizeof(*buffers));
+
+ if (!buffers) {
+ fprintf(stderr, "Out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ for (n_buffers = 0; n_buffers < 4; ++n_buffers) {
+ buffers[n_buffers].length = buffer_size;
+ buffers[n_buffers].start = malloc(buffer_size);
+
+ if (!buffers[n_buffers].start) {
+ fprintf(stderr, "Out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+static void init_device(const char *dev_name) {
+ struct v4l2_capability cap;
+ struct v4l2_cropcap cropcap;
+ struct v4l2_crop crop;
+ struct v4l2_format fmt;
+ unsigned int min;
+
+ if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {
+ if (EINVAL == errno) {
+ fprintf(stderr, "%s is no V4L2 device\n", dev_name);
+ exit(EXIT_FAILURE);
+ } else {
+ errno_exit("VIDIOC_QUERYCAP");
+ }
+ }
+
+ if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
+ fprintf(stderr, "%s is no video capture device\n",
+ dev_name);
+ exit(EXIT_FAILURE);
+ }
+
+ switch (io) {
+ case IO_METHOD_READ:
+ if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
+ fprintf(stderr, "%s does not support read i/o\n",
+ dev_name);
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case IO_METHOD_MMAP:
+ case IO_METHOD_USERPTR:
+ if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
+ fprintf(stderr, "%s does not support streaming i/o\n",
+ dev_name);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ }
+
+
+ /* Select video input, video standard and tune here. */
+
+
+ CLEAR(cropcap);
+
+ cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {
+ crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ crop.c = cropcap.defrect; /* reset to default */
+
+ if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {
+ switch (errno) {
+ case EINVAL:
+ /* Cropping not supported. */
+ break;
+ default:
+ /* Errors ignored. */
+ break;
+ }
+ }
+ } else {
+ /* Errors ignored. */
+ }
+
+
+ CLEAR(fmt);
+
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (force_format) {
+ fprintf(stderr, "Set H264\r\n");
+ fmt.fmt.pix.width = 640; //replace
+ fmt.fmt.pix.height = 480; //replace
+ fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; //V4L2_PIX_FMT_H264; //replace
+ fmt.fmt.pix.field = V4L2_FIELD_ANY;
+
+ if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))
+ errno_exit("VIDIOC_S_FMT");
+
+ /* Note VIDIOC_S_FMT may change width and height. */
+ } else {
+ /* Preserve original settings as set by v4l2-ctl for example */
+ if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt))
+ errno_exit("VIDIOC_G_FMT");
+ }
+
+ /* Buggy driver paranoia. */
+ min = fmt.fmt.pix.width * 2;
+ if (fmt.fmt.pix.bytesperline < min)
+ fmt.fmt.pix.bytesperline = min;
+ min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
+ if (fmt.fmt.pix.sizeimage < min)
+ fmt.fmt.pix.sizeimage = min;
+
+ switch (io) {
+ case IO_METHOD_READ:
+ init_read(fmt.fmt.pix.sizeimage);
+ break;
+
+ case IO_METHOD_MMAP:
+ init_mmap(dev_name);
+ break;
+
+ case IO_METHOD_USERPTR:
+ init_userp(fmt.fmt.pix.sizeimage, dev_name);
+ break;
+ }
+}
+
+static void uninit_device(void) {
+ unsigned int i;
+ switch (io) {
+ case IO_METHOD_READ:
+ free(buffers[0].start);
+ break;
+ case IO_METHOD_MMAP:
+ for (i = 0; i < n_buffers; ++i)
+ if (-1 == munmap(buffers[i].start, buffers[i].length))
+ errno_exit("munmap");
+ break;
+
+ case IO_METHOD_USERPTR:
+ for (i = 0; i < n_buffers; ++i)
+ free(buffers[i].start);
+ break;
+ }
+
+ free(buffers);
+}
+
+static void start_capturing(void)
+{
+ unsigned int i;
+ enum v4l2_buf_type type;
+
+ switch (io) {
+ case IO_METHOD_READ:
+ /* Nothing to do. */
+ break;
+
+ case IO_METHOD_MMAP:
+ for (i = 0; i < n_buffers; ++i) {
+ struct v4l2_buffer buf;
+
+ CLEAR(buf);
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = i;
+
+ if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
+ errno_exit("VIDIOC_QBUF");
+ }
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
+ errno_exit("VIDIOC_STREAMON");
+ break;
+
+ case IO_METHOD_USERPTR:
+ for (i = 0; i < n_buffers; ++i) {
+ struct v4l2_buffer buf;
+
+ CLEAR(buf);
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_USERPTR;
+ buf.index = i;
+ buf.m.userptr = (unsigned long)buffers[i].start;
+ buf.length = buffers[i].length;
+
+ if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
+ errno_exit("VIDIOC_QBUF");
+ }
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
+ errno_exit("VIDIOC_STREAMON");
+ break;
+ }
+}
+
+static void stop_capturing(void)
+{
+ enum v4l2_buf_type type;
+
+ switch (io) {
+ case IO_METHOD_READ:
+ /* Nothing to do. */
+ break;
+
+ case IO_METHOD_MMAP:
+ case IO_METHOD_USERPTR:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))
+ errno_exit("VIDIOC_STREAMOFF");
+ break;
+ }
+}
+
+static int frame_number = 0;
+static void process_image(const void *p, int size)
+{
+ frame_number++;
+ char filename[15];
+ sprintf(filename, "frame-%d.bin", frame_number);
+ FILE *fp=fopen(filename,"wb");
+ fwrite(p, size, 1, fp);
+ fflush(fp);
+ fclose(fp);
+}
+
+
+static int read_frame(void)
+{
+ struct v4l2_buffer buf;
+ unsigned int i;
+
+ switch (io) {
+ case IO_METHOD_READ:
+ if (-1 == read(fd, buffers[0].start, buffers[0].length)) {
+ switch (errno) {
+ case EAGAIN:
+ return 0;
+
+ case EIO:
+ /* Could ignore EIO, see spec. */
+
+ /* fall through */
+
+ default:
+ errno_exit("read");
+ }
+ }
+ printf("Process image 1 %p %d\n", buffers[0].start, buffers[0].length);
+ process_image(buffers[0].start, buffers[0].length);
+ break;
+
+ case IO_METHOD_MMAP:
+ CLEAR(buf);
+
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+
+ if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
+ switch (errno) {
+ case EAGAIN:
+ return 0;
+
+ case EIO:
+ /* Could ignore EIO, see spec. */
+
+ /* fall through */
+
+ default:
+ errno_exit("VIDIOC_DQBUF");
+ }
+ }
+
+ printf("Process image 2 %p %d\n", buffers[buf.index].start, buf.bytesused);
+ process_image(buffers[buf.index].start, buf.bytesused);
+ if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
+ errno_exit("VIDIOC_QBUF");
+ break;
+
+ case IO_METHOD_USERPTR:
+ CLEAR(buf);
+
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_USERPTR;
+
+ if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
+ switch (errno) {
+ case EAGAIN:
+ return 0;
+
+ case EIO:
+ /* Could ignore EIO, see spec. */
+
+ /* fall through */
+
+ default:
+ errno_exit("VIDIOC_DQBUF");
+ }
+ }
+
+ for (i = 0; i < n_buffers; ++i)
+ if (buf.m.userptr == (unsigned long)buffers[i].start
+ && buf.length == buffers[i].length)
+ break;
+
+ printf("Process image 3 %p %d\n", (void *)buf.m.userptr, buf.bytesused);
+ process_image((void *)buf.m.userptr, buf.bytesused);
+ if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
+ errno_exit("VIDIOC_QBUF");
+ break;
+ }
+
+ return 1;
+}
+
+int main(void) {
+ const char *videodevice = "/dev/video0";
+
+ open_device(videodevice);
+ init_device(videodevice);
+
+ preDraw();
+ start_capturing();
+
+ //vkwnd->loop([](SdlWindow* wnd, uint64_t frameCounter){
+ fd_set fds;
+ struct timeval tv;
+ int r;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ /* Timeout. */
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+
+ r = select(fd + 1, &fds, NULL, NULL, &tv);
+
+ if (0 == r) {
+ fprintf(stderr, "select timeout\n");
+ exit(EXIT_FAILURE);
+ }
+ if (r > 0 ) {
+ read_frame();
+ } else if (-1 == r) {
+ if (EINTR != errno)
+ errno_exit("select");
+ }
+ draw_frame();
+// });
+ //vkwnd->deinit();
+
+
+ stop_capturing();
+
+ uninit_device();
+ close_device();
+
+ return 0;
+}