MediaRecorder Android 11 start failed -1004
Asked Answered
A

2

12

On Android 11 my MediaRecorder fails to initialize. I suspect the problem is related to scopedstorage, but I have been unable to figure out the cause. I am using MediaRecorder to record audio from the microphone. I extract the amplitude from the audio, so I have no intention to keep the file, that is why the path is /dev/null

 var mRecorder: MediaRecorder? = null


 if (mRecorder == null) {
        mRecorder = MediaRecorder()
        mRecorder!!.setAudioSource(MediaRecorder.AudioSource.MIC)
        mRecorder!!.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
        mRecorder!!.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
        mRecorder!!.setOutputFile("/dev/null")
        try {
            mRecorder!!.prepare()
        } catch (ioe: IOException) {
            Log.e("[Monkey]", "IOException: " + Log.getStackTraceString(ioe))
        } catch (e: SecurityException) {
            Log.e("[Monkey]", "SecurityException: " + Log.getStackTraceString(e))
        }
        try {
            mRecorder!!.start()
        } catch (e: SecurityException) {
            Log.e("[Monkey]", "SecurityException: " + Log.getStackTraceString(e))
        }

The crash is at MediaRecorded.start(). Is /dev/null not a valid path on Android 11?

Logcat:

start failed: -1004
2020-11-15 10:51:41.827 11836-11836/= E/AndroidRuntime: FATAL EXCEPTION: main
    Process: c=, PID: 11836
    java.lang.RuntimeException: start failed.
        at android.media.MediaRecorder.start(Native Method)
 
Allspice answered 15/11, 2020 at 16:1 Comment(0)
P
8

Replace "/dev/null" with the correct file path "${externalCacheDir.absolutePath}/test.3gp" and it should work.

Philbert answered 19/11, 2020 at 9:27 Comment(5)
Thank you for the response. I am surprised something so obvious did not occur to me. I really appreciate your help.Allspice
Thank you for this hint! This seems to fix the problem, however I wonder how this behaves when the app is running for a long time? Will the "test.3gp" file grow and grow and use all the available space on the device? Will it be automatically deleted when the app is closed? Thanks!Thenceforward
I don't see how this is an acceptable answer... The question explicitly says the output isn't needed, which is why /dev/null is being used, and this will save a potentially large persistent file...Letty
@Thenceforward Each time you run MediaRecorder, the "test.3gp" file will be overwritten. Therefore, its size will not increase.Philbert
@CruzJean MediaRecorder cannot work without saving the file. The file "test.3gp" will be overwritten every time and its size will not increase.Philbert
O
2

For some reason setting the "/dev/null" path to prevent MediaRecorder from storing is causing the crash at Android 11 version to this date, by setting an actual path it get's fixed just like is mentioned in another answer, but In my case all I needed in my app was to register the amplitude levels coming from the microphone, so in order to prevent storing in the output file indeterminately I flush the MediaRecorder object every minute.

This a full but simplified example:

class MainActivity : AppCompatActivity() {

    private lateinit var mediaRecorder: MediaRecorder
    private var handler: Handler = Handler()

    private var fakeOutput = ""
    private var needsFlush = false
    private var flushCounter = 300

    private val runnable = object : Runnable {
        override fun run() {
            Log.d("AudioAmplitude: ", mediaRecorder.maxAmplitude.toString())
            if (needsFlush) {
                if (flushCounter == 0) {
                    mediaRecorder.stop()
                    mediaRecorder.release()
                    initMediaRecorder()
                    flushCounter = 300
                }
                flushCounter--
            }
            handler.postDelayed(this, 200)
        }
    }

    private val requestedPermissions = arrayOf(
        Manifest.permission.RECORD_AUDIO,
        Manifest.permission.CAMERA
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        requestedPermissions.forEach {
            ContextCompat.checkSelfPermission(
                this,
                it
            )
        }

        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.RECORD_AUDIO
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(
                    (this as Activity?)!!,
                    Manifest.permission.RECORD_AUDIO
                )
            ) {
            } else {
                ActivityCompat.requestPermissions(
                    (this as Activity?)!!, arrayOf(Manifest.permission.RECORD_AUDIO),
                    0
                )
            }
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            fakeOutput = "${externalCacheDir?.absolutePath}/temp.3gp"
            needsFlush = true
        } else {
            fakeOutput = "/dev/null"
        }

        initMediaRecorder()
    }

    private fun initMediaRecorder() {
        mediaRecorder = MediaRecorder().apply {
            setAudioSource(MediaRecorder.AudioSource.MIC)
            setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
            setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
            setOutputFile(fakeOutput)
            prepare()
            start()
        }
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun onResume() {
        super.onResume()
        mediaRecorder.resume()
        handler.post(runnable)
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun onPause() {
        super.onPause()
        mediaRecorder.pause()
        handler.removeCallbacks(runnable)
    }
}

Notice that I use a Handler (maybe not the best practice) to print out the maxAmplitude which is the main reason why I was working on this, I am using it to flush every 60 seconds (counter is 300 minus one every 200 ms the handler calls itself).

Ocieock answered 16/11, 2021 at 18:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.