Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
142 views
in Technique[技术] by (71.8m points)

android - Green flickering on Samsung S6 when recording video with camera2

I'm having a problem with Samsung Galaxy s6 when I record a video, preview is fine but when I finish recording the video the result is a video with glitching and green flickering. I'm having this problem with this model with all phones it redord at it should be...tbh...I've tried a lot of changes and I don't know what to do

Here a video of the problem

Here the SurfaceView:

class AutoFitSurfaceView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyle: Int = 0
) : SurfaceView(context, attrs, defStyle) {

    private var aspectRatio = 0f

    /**
    * Sets the aspect ratio for this view. The size of the view will be
    * measured based on the ratio calculated from the parameters.
    *
    * @param width  Camera resolution horizontal size
    * @param height Camera resolution vertical size
    */
    fun setAspectRatio(width: Int, height: Int) {
        require(width > 0 && height > 0) { "Size cannot be negative" }
        aspectRatio = width.toFloat() / height.toFloat()
        holder.setFixedSize(width, height)
        requestLayout()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val width = MeasureSpec.getSize(widthMeasureSpec)
        val height = MeasureSpec.getSize(heightMeasureSpec)
        if (aspectRatio == 0f) {
            setMeasuredDimension(width, height)
        } else {
            // Performs height-crop transformation of the camera frames if necessary
            val newHeight: Int
            val actualRatio = 1f / aspectRatio
            newHeight = (width / actualRatio).roundToInt()
            setMeasuredDimension(width, newHeight)
        }
    }
}

This is the implementation:

class VideoCameraImplCamera2(
    private val context: Context,
    private val lifecycleOwner: LifecycleOwner,
    private val viewFinder: AutoFitSurfaceView,
    private val videoDirectory: String,
    private val resolution: Resolution,
    override val listener: VideoCamera.VideoCameraListener
    ) : VideoCamera, LifecycleObserver {

    enum class CameraLens(val value: Int) {
        FRONT(1),
        BACK(0)
    }

    companion object {
        private const val RECORDER_VIDEO_BITRATE: Int = 10_000_000
        private const val FRAME_PER_SECOND: Int = 30
        private const val RECORDER_AUDIO_BITRATE: Int = 192_000
        private const val RECORDER_AUDIO_SAMPLING_RATE: Int = 44_100

        private const val DEVICE_ROTATION = Surface.ROTATION_0

        private const val SENSOR_ORIENTATION_DEFAULT_DEGREES = 90
        private const val SENSOR_ORIENTATION_INVERSE_DEGREES = 270
        private val DEFAULT_ORIENTATIONS = SparseIntArray().apply {
            append(Surface.ROTATION_0, 90)
            append(Surface.ROTATION_90, 0)
            append(Surface.ROTATION_180, 270)
            append(Surface.ROTATION_270, 180)
        }
        private val INVERSE_ORIENTATIONS = SparseIntArray().apply {
            append(Surface.ROTATION_0, 270)
            append(Surface.ROTATION_90, 180)
            append(Surface.ROTATION_180, 90)
            append(Surface.ROTATION_270, 0)
        }
    }

    /**
    * Orientation of the camera sensor
    */
    private var sensorOrientation = 0

    /**
    * Camera lens active
    */
    private var cameraLens = CameraLens.FRONT

    /**
    * Whether the flash is active
    */
    private var cameraFlash = VideoCamera.FlashMode.OFF

    /**
    * Min recording time for each video
    */
    private var minRecordingTimeCompleted = true

    /**
    * Whether the video recording stopped before min time
    */
    private var stoppedBeforeMinTimeCompleted = false

    /**
    * Whether the app is showing that is recording
    */
    private var isRecording = false

    /**
    * Whether the app is recording video now
    */
    private var isRecordingVideo = false

    /**
    * Whether the camera is available
    */
    private var isAvailable = false

    /**
    * Minimum recording duration countdown
    */
    private val minDurationCountDownTimer =
        object : CountDownTimer(VideoCamera.MIN_RECORD_DURATION, 100) {
            init {
                minRecordingTimeCompleted = false
            }

            override fun onFinish() {
                if (stoppedBeforeMinTimeCompleted && isRecording) {
                    Timber.d("Minimum recording time completed")
                    stopInternalRecording()
                }
                minRecordingTimeCompleted = true
            }

            override fun onTick(millisUntilFinished: Long) {
            }
        }

    /**
    * Camera id based in [cameraLens] value
    */
    private val cameraId: String
        get() = cameraManager.cameraIdList[cameraLens.value]

    /** Detects, characterizes, and connects to a CameraDevice (used for all camera operations) */
    private val cameraManager: CameraManager by lazy {
        val context = context.applicationContext
        context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
    }

    /** [CameraCharacteristics] corresponding to the provided Camera ID */
    private val characteristics: CameraCharacteristics
        get() = cameraManager.getCameraCharacteristics(cameraId)

    /**
    * Setup a persistent [Surface] for the recorder so we can use it as an output target for the
    * camera session without preparing the recorder
    */
    private lateinit var recorderSurface: Surface

    /** Saves the video recording */
    private lateinit var recorder: MediaRecorder

    /** [HandlerThread] where all camera operations run */
    private val cameraThread = HandlerThread("CameraThread").apply { start() }

    /** [Handler] corresponding to [cameraThread] */
    private val cameraHandler = Handler(cameraThread.looper)

    /** Captures frames from a [CameraDevice] for our video recording */
    private lateinit var session: CameraCaptureSession

    /** The [CameraDevice] that will be opened in this fragment */
    private lateinit var camera: CameraDevice

    /** Requests used for preview only in the [CameraCaptureSession] */
    private lateinit var previewRequest: CaptureRequest.Builder

    /** Requests used for preview and recording in the [CameraCaptureSession] */
    private lateinit var recordRequest: CaptureRequest.Builder

    private var recordingStartMillis: Long = 0L

    /** Number or requested recording this session*/
    private var recordingRequested = 0

    /** Last clip duration */
    private var lastRecordingDuration = 0F

    /** Indicate whether the camera is initialized */
    private var initialized = false

    /** Whether the camera is in pause */
    private var onPause = true

    /** The [android.util.Size] of video recording. */
    private lateinit var videoSize: Size

    /** Absolute path of recording video file. It will be create a new one in each recording */
    private lateinit var videoFilePath: String

    init {
        viewFinder.holder.addCallback(object : SurfaceHolder.Callback {
            override fun surfaceDestroyed(holder: SurfaceHolder) = Unit
            override fun surfaceChanged(
                holder: SurfaceHolder,
                format: Int,
                width: Int,
                height: Int
            ) = Unit

            override fun surfaceCreated(holder: SurfaceHolder) {

                // Selects appropriate preview size and configures view finder
                val previewSize = chooseVideoSize()

                Timber.d("View finder size: ${viewFinder.width} x ${viewFinder.height}")
                Timber.d("Selected preview size: $previewSize")
                viewFinder.setAspectRatio(previewSize.width, previewSize.height)
            }
        })
    }

    /**
    * Initialize video camera
    */
    override fun initialize() {
        initialized = true
        onPause = false
        viewFinder.post { initializeCamera() }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun onResume() {
        if (onPause) {
            initialize()
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onStop() {
        onPause = true
        try {
            camera.close()
        } catch (exc: Throwable) {
            Timber.e(exc, "Error closing camera")
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy() {
        recorderSurface.release()
        cameraThread.quitSafely()
        recorderSurface.release()
    }

    private fun createRecorderSurface(width: Int, height: Int): Surface {

        // Get a persistent Surface from MediaCodec, don't forget to release when done
        val surface = MediaCodec.createPersistentInputSurface()

        // Prepare and release a dummy MediaRecorder with our new surface
        // Required to allocate an appropriately sized buffer before passing the Surface as the
        //  output target to the capture session
        createRecorder(
            surface,
            File(context.cacheDir, "dummy.mp4").absolutePath,
            width,
            height
        ).apply {
            prepare()
            release()
        }

        return surface
    }

    /** Creates a [MediaRecorder] instance using the provided [Surface] as input */
    private fun createRecorder(
        surface: Surface,
        absoluteFilePath: String,
        width: Int,
        height: Int
    ) = MediaRecorder().apply {
        setAudioSource(MediaRecorder.AudioSource.MIC)
        setVideoSource(MediaRecorder.VideoSource.SURFACE)
        setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
        setOutputFile(absoluteFilePath)
        setVideoEncodingBitRate(RECORDER_VIDEO_BITRATE)
        if (FRAME_PER_SECOND > 0) setVideoFrameRate(FRAME_PER_SECOND)
        setVideoSize(width, height)
        setVideoEncoder(MediaRecorder.VideoEncoder.H264)
        setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
        setAudioEncodingBitRate(RECORDER_AUDIO_BITRATE)
        setAudioSamplingRate(RECORDER_AUDIO_SAMPLING_RATE)
        setInputSurface(surface)
    }

    /** Creates a [CaptureRequest.Builder] instance for preview using [session] variable*/
    private fun createPreviewRequest(): CaptureRequest.Builder {
        return session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
            // Add the preview surface target
            addTarget(viewFinder.holder.surface)
        }
    }

    /** Creates a [CaptureRequest.Builder] instance for recording using [session] variable*/
    private fun createRecordRequest(): CaptureRequest.Builder {
        // Capture request holds references to target surfaces
        return session.device.createCaptureRequest(CameraDevice.TEMPLATE_RECORD).apply {
            // Add the preview and recording surface targets
            addTarget(viewFinder.holder.surface)
            addTarget(recorderSurface)
            // Sets user requested FPS for all targets
            set(
                CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
                Range(FRAME_PER_SECOND, FRAME_PER_SECOND)
            )
        }
    }

    /**
    * Begin all camera operations in a coroutine in the main thread. This function:
    * - Opens the camera
    * - Configures the camera session
    * - Starts the preview by dispatching a repeating request
 

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...