Reputation: 1047
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)
Upvotes: 12
Views: 4454
Reputation: 83
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).
Upvotes: 2
Reputation: 314
Replace "/dev/null" with the correct file path "${externalCacheDir.absolutePath}/test.3gp" and it should work.
Upvotes: 7