android.media.ImageWriter produces green screen screen on every second posted Image frame

285 Views Asked by At

I have a sample app to generate YUV images out of predefined jpeg test data and post it to the ImageWriter in order to mock the Camera API for end2end tests: https://github.com/adjorno/Bitmap2YUV

package com.example.bitmap2yuv

import android.graphics.*
import android.media.ImageWriter
import android.os.Bundle
import android.view.Surface
import android.view.TextureView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.scale
import androidx.lifecycle.lifecycleScope
import io.github.crow_misia.libyuv.AbgrBuffer
import io.github.crow_misia.libyuv.I420Buffer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity(), TextureView.SurfaceTextureListener {
    private lateinit var textureView: TextureView
    private var bitmap: Bitmap? = null
    private var imageWriter: ImageWriter? = null
    private var yuvBuffer: I420Buffer? = null
    private var running = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        textureView = TextureView(this)
        textureView.surfaceTextureListener = this
        setContentView(textureView)
    }

    private fun createImageWriter(surface: Surface, width: Int, height: Int) {
        yuvBuffer = I420Buffer.allocate(width, height)
        bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).apply {
            eraseColor(Color.BLUE)
        }

        val argbBuffer = AbgrBuffer.allocate(width, height)
        bitmap?.copyPixelsToBuffer(argbBuffer.asBuffer())
        argbBuffer.convertTo(yuvBuffer!!)
        imageWriter = ImageWriter.newInstance(surface, 1, ImageFormat.YUV_420_888)
        startRendering()
    }

    private fun startRendering() {
        imageWriter?.let { imageWriter ->
            yuvBuffer?.let { yuvBuffer ->
                running = true
                lifecycleScope.launch(Dispatchers.IO) {
                    while (running) {
                        delay(1000)
                        val image = imageWriter.dequeueInputImage()
                        image.planes[0].buffer.put(yuvBuffer.planeY.buffer)
                        image.planes[1].buffer.put(yuvBuffer.planeU.buffer)
                        image.planes[2].buffer.put(yuvBuffer.planeV.buffer)
                        imageWriter.queueInputImage(image)
                    }
                }
            }
        }
    }

    override fun onResume() {
        super.onResume()
        startRendering()
    }

    override fun onStop() {
        super.onStop()
        running = false
    }

    override fun onSurfaceTextureAvailable(surfaceTexture: SurfaceTexture, width: Int, height: Int) {
        createImageWriter(Surface(surfaceTexture), width, height)
    }

    override fun onSurfaceTextureSizeChanged(p0: SurfaceTexture, p1: Int, p2: Int) {
    }

    override fun onSurfaceTextureDestroyed(p0: SurfaceTexture): Boolean {
        return true
    }

    override fun onSurfaceTextureUpdated(p0: SurfaceTexture) {
    }
}

I am expecting to see the blue screen all the time (with 1 fps), but instead I see that every second frame is replaced with a green screen. How to get rid of it?

Blue/Green screen

1

There are 1 best solutions below

1
On

Based on library creator's comment: "Image rowStribe and width are not equal, so size of data per row is different between Image and I420Buffer, resulting in image misalignment.

If Buffer is created from Image, rowStribe of Image is used."

val image = imageWriter.dequeueInputImage()
yuvBuffer.convertTo(image.toI420Buffer())
imageWriter.queueInputImage(image)