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; +}