Android Jpeg Capturing (CameraX) создает на некоторых устройствах разорванные черные изображения размером 192 КБ - PullRequest
1 голос
/ 30 января 2020

Цель

Я пытался преобразовать образец кода CameraXBasi c в отдельный класс CapturingProcess

  • , который связывает сценарий использования capture в new CapturingProcess(context, lifecycleOwner)
  • , который предлагает простой метод requestPicture() для захвата нескольких изображений (например, по одной в секунду)

Описание ошибки

  • Выполнение requestPicture() несколько раз дает
    • 4 обычных изображения JPEG (> 1 МБ)
    • все остальные файлы JPEG полностью черный и всегда 192 КБ большой

Generated JPEG pictures

Действия по воспроизведению

  • Оформление заказа и сборка Пример приложения
  • Запустите приложение и нажмите кнопку 5-10 раз
  • Проверьте изображения в Android/media/com.example.cameraxblackjpegbug/CameraXBlackJpegBug/
  • , вы видите, что только первые 4 изображения Jpeg в порядке
    • (только на некоторых устройствах см. Раздел «Проверено») *

Ожидаемый результат * 1 070 *

  • Все изображения не повреждены
  • каждый раз, когда capturingProcess.requestPicture() называется неразбитым Jpeg изображение снимается

Проверено на

  • Pixel 3a Andorid 10 (воспроизводит ошибку)
    • Примечание. Ранее у меня была похожая ошибка с камерой2, но тогда я не смог ее исправить
  • Huawei P smart 2019 Android 9 (ошибка не воспроизводится)
    • не имеет функции «ручной датчик»

Известные обходные пути

  • Когда я создаю new CapturingProcess(), например, после трехкратного нажатия кнопки, ошибка не возникает
    • , однако это неприемлемо. В моем случае мне нужно делать снимки каждую секунду в течение длительного времени в фоновом режиме, поэтому для экономии ресурсов я не могу каждый раз создавать новый экземпляр.

Соответствующий код

class MainActivity : AppCompatActivity() {

    private lateinit var capturingProcess: CapturingProcess

    override fun onCreate(savedInstanceState: Bundle?) {
        // Create Capturing Process
        capturingProcess = CapturingProcess(applicationContext, this)

        // Register capture action to button
        fab.setOnClickListener { capturingProcess.requestPicture() }
    }
}
class CapturingProcess(context: Context, lifecycleOwner: LifecycleOwner) {

    private var context: Context = context
    private var outputDirectory: File
    private var mainExecutor: Executor
    private lateinit var cameraControl: CameraControl
    private lateinit var cameraInfo: CameraInfo
    private var capture: ImageCapture? = null
    private var lensFacing: Int = CameraSelector.LENS_FACING_BACK
    private var lifecycleOwner: LifecycleOwner = lifecycleOwner

    fun requestPicture() {
        capture?.let { imageCapture ->
            val photoFile = createFile(outputDirectory, FILENAME, PHOTO_EXTENSION)
            val metadata = ImageCapture.Metadata().apply {
                isReversedHorizontal = lensFacing == CameraSelector.LENS_FACING_FRONT
            }
            imageCapture.takePicture(photoFile, metadata, mainExecutor, imageSavedListener)
        }
    }

    private fun bindCameraUseCases() {

        val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
        val cameraProviderFuture = ProcessCameraProvider.getInstance(this.context)
        cameraProviderFuture.addListener(Runnable {

            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

            capture = ImageCapture.Builder()
                .setTargetName("Capture")
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .build()

            cameraProvider.unbindAll()

            try {
                val camera =
                    cameraProvider.bindToLifecycle(this.lifecycleOwner, cameraSelector, capture)
                cameraControl = camera.cameraControl
                cameraInfo = camera.cameraInfo
            } catch (e: Exception) {
                throw IllegalStateException(e)
            }

        }, this.mainExecutor)
    }

    private val imageSavedListener = object : ImageCapture.OnImageSavedCallback {

        override fun onError(imageCaptureError: Int, message: String, cause: Throwable?) {
            Log.e(TAG, "Photo capture failed: $message")
        }

        override fun onImageSaved(photoFile: File) {

            Log.d(TAG, "Photo capture succeeded: ${photoFile.absolutePath}")

            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
                context.sendBroadcast(
                    Intent("android.hardware.action.NEW_PICTURE", Uri.fromFile(photoFile))
                )
            }

            val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(photoFile.extension)
            MediaScannerConnection.scanFile(
                context, arrayOf(photoFile.absolutePath), arrayOf(mimeType), null
            )
        }
    }

    companion object {
        private const val FILENAME = "yyyy-MM-dd-HH-mm-ss-SSS"
        private const val PHOTO_EXTENSION = ".jpg"
        private const val TAG = "CapturingProcess"


        private fun createFile(
            baseFolder: File,
            @Suppress("SameParameterValue") format: String,
            @Suppress("SameParameterValue") extension: String
        ) = File(
            baseFolder,
            SimpleDateFormat(
                format,
                Locale.getDefault()
            ).format(System.currentTimeMillis()) + extension
        )

        fun getOutputDirectory(context: Context): File {
            val appContext = context.applicationContext
            val mediaDir = context.externalMediaDirs.firstOrNull()?.let {
                File(it, context.getString(R.string.app_name)).apply { mkdirs() }
            }
            return if (mediaDir != null && mediaDir.exists()) {
                mediaDir
            } else {
                appContext.filesDir
            }
        }
    }

    init {
        this.mainExecutor = ContextCompat.getMainExecutor(context)
        outputDirectory = getOutputDirectory(context)
        bindCameraUseCases()
    }
}

Вопрос

  • Чтобы уточнить: я хочу найти ошибку в примере кода /project.
  • Таким образом, мой вопрос: почему описанная ошибка возникает, когда я выполняю requestPicture()> 4 раза на одном и том же экземпляре CapturingProcess, но не когда я создаю новые экземпляры после его выполнения, например, 3 раза - как это исправить?
...