I'm developing on a Nitro HD with Gingerbread. I want to record audio and I experience an infinite hang while calling MediaRecorder.stop().
I know that my phone can record sound because I have an application that does it exactly.
I read the book "Android for programmers" from Deitel et al. and there is the example VoiceRecorder in chapter 16. Everything seems fine but the app hangs forever when it calls MediaRecorder.stop(). Also, the resource is not released and I have to reboot the phone to release it.
Here is the part of the code where the calls are done (see Deitel et al., "Android for Programmers", Prentice Hall, 2012, chap 16):
// starts/stops a recording
OnCheckedChangeListener recordButtonListener =
new OnCheckedChangeListener()
{
#Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked)
{
if (isChecked)
{
visualizer.clear(); // clear visualizer for next recording
saveButton.setEnabled(false); // disable saveButton
deleteButton.setEnabled(false); // disable deleteButton
viewSavedRecordingsButton.setEnabled(false); // disable
// create MediaRecorder and configure recording options
if (recorder == null)
recorder = new MediaRecorder(); // create MediaRecorder
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(
MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
recorder.setAudioEncodingBitRate(16);
recorder.setAudioSamplingRate(44100);
try
{
// create temporary file to store recording
File tempFile = File.createTempFile(
"VoiceRecorder", ".3gp", getExternalFilesDir(null));
// store File as tag for saveButton and deleteButton
saveButton.setTag(tempFile);
deleteButton.setTag(tempFile);
// set the MediaRecorder's output file
recorder.setOutputFile(tempFile.getAbsolutePath());
recorder.prepare(); // prepare to record
recorder.start(); // start recording
recording = true; // we are currently recording
handler.post(updateVisualizer); // start updating view
} // end try
catch (IllegalStateException e)
{
Log.e(TAG, e.toString());
} // end catch
catch (IOException e)
{
Log.e(TAG, e.toString());
} // end catch
} // end if
else
{
recorder.stop(); // stop recording
recorder.reset(); // reset the MediaRecorder
recording = false; // we are no longer recording
saveButton.setEnabled(true); // enable saveButton
deleteButton.setEnabled(true); // enable deleteButton
recordButton.setEnabled(false); // disable recordButton
} // end else
} // end method onCheckedChanged
}; // end OnCheckedChangedListener
In a debug session, the "else" scope is entered but it hangs on its first (stop()) line.
I repeat, I know the phone and its OS are correct because another app works correctly. So, do you have any idea on how to solve this problem, a work around maybe?
Thanks!
EDIT When the recorder is started(), there is a handler that is executed at each 50ms to display a graph of the amplitude of the sound. The method recorder.getMaxAmplitude() always returns 0. Maybe this is the symptom of a badly initialized MediaRecorder?
The argument of setAudioEncodingBitRate() might be too low.
what is good setAudioEncodingBitRate on record voice
Hope that's help.
You could have a null recorder at that spot. You're not creating a new MediaRecorder() if you enter the else case of isChecked.
So, 2 things:
The API docs state that if you call stop() before start you'll throw a RuntimeException And if you fail to record anything you'll throw an IllegalStateException.
Check recorder before calling stop:
if (recorder != null) {
recorder.stop();
// some recorder stuff here
}
Related
Hi I am using android with java. I have set up a very simple button which when held down records audio and when released stops recording. I have two questions:
When I run the following implementation of my idea, I get runtime a warning mediarecorder went away with unhandled events every time the button is released. I can't find what is causing this! I see that this has been answered previously on this forum many years ago with the suggestion to add mediaRecorder.update(), but this does not address why the warning is occurring. What does it mean by unhandled events and what could be causing it? I have done nothing different I can see than in the documentation, other than using an onTouchListener...
Second, should I be wary of user's being able to switch on and off the button very rapidly - could this cause runtime problems and should I take steps to guard against this?
The relevant code I use is more-or-less this:
public void set() {
View.OnTouchListener recordOnTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
if (requestMultiplePermissions(Permissions).granted) {
audioSetup();
recordAudio();
}
return true;
case MotionEvent.ACTION_UP:
stopAudio();
return true;
default:
return false;
}
}
binding.addnewvocabRecordVocab.setOnTouchListener(recordOnTouchListener)
}
where
private void audioSetup() {
File filedir = new File(filepath);
if (!filedir.exists()) {filedir.mkdirs();
file = new File(filepath,filename);
if (file.exists()) { file.delete();}
}
public void recordAudio () {
isRecording = true;
if (mediaRecorder != null) {
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;
}
try {
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mediaRecorder.setOutputFile(file);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mediaRecorder.prepare();
} catch (Exception e) {
e.printStackTrace();
}
mediaRecorder.start();
}
public void stopAudio () {
if (isRecording) {
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;
isRecording = false;
}
}
This isn't a full answer to my own question, and the question still stands. But after playing with this a while, I have learnt a few lessons.
If the user taps the record button for a second, then mediarecorder.stop() will produce an error if there is not sufficient data recorded. See this. So if one wants to prevent the app from crashing, one needs to wrap mediarecorder.stop() in some catch - as the discussion in the link advises us. In fact, on some shortish taps, the stop method seems to take rather a long time (well over a second), so it might be worth considering disabling the button just before and after the stop method is called.
Another problem with fast repeated tapping is that it seems to keep adding to the main thread queue, which is probably inadvisable. I have found that using an executor thread with submit is a nice way of dealing with this. Schematically we can
public class audioLibrary() {
//or could go directly in main code
ExecutorService executor;
public void onStartRecord() {
if (executor == null) {
this.executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
//check isRecording and record stuff
});
}
}
public void onStopRecord() {
if (executor != null) {
executor.submit(() -> {
//check isRecording and shut down mediarecorder
//together with catch for onStop.
});
executor.shutdown();
}
}
}
Clearly, one must be a little careful to make sure the thread really does always get shut down.
Curiously, I do not get the `unhandled events' when I do this now...
Maybe someone could expand on this and comment on my code above to see whether they agree with the gist of it, whether it is necessary and on possible improvements.
I'm currently going through the 'Building A Camera App' tutorial -http://developer.android.com/guide/topics/media/camera.html#custom-camera
As someone relatively new to android, I find it a bit confusing / unclear at times.
I'm trying to understand where this code is supposed to go:
private boolean isRecording = false;
// Add a listener to the Capture button
Button captureButton = (Button) findViewById(id.button_capture);
captureButton.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
if (isRecording) {
// stop recording and release camera
mMediaRecorder.stop(); // stop the recording
releaseMediaRecorder(); // release the MediaRecorder object
mCamera.lock(); // take camera access back from MediaRecorder
// inform the user that recording has stopped
setCaptureButtonText("Capture");
isRecording = false;
} else {
// initialize video camera
if (prepareVideoRecorder()) {
// Camera is available and unlocked, MediaRecorder is prepared,
// now you can start recording
mMediaRecorder.start();
// inform the user that recording has started
setCaptureButtonText("Stop");
isRecording = true;
} else {
// prepare didn't work, release the camera
releaseMediaRecorder();
// inform user
}
}
}
}
);
Can this be anywhere within the top level class, or is this supposed to be inside one of the provided methods or inner classes?
No matter where I put this code it's causing errors telling me to add or remove '}', but I'm sure I must just have it in the wrong place, since I'm sure google's code is fine.
Any help much appreciated!
This code belongs in the activity that loads the layout containing the "button_capture" Button. One you find that activity, out this inside of the onCreate() method.
No matter where I put this code it's causing errors telling me to add or remove '}'
This is simply a matter of getting your braces paired up properly.
I am creating a MediaRecorder and using it to generate a video clip. It works perfectly the first time. I end the video shooting process by setting the maximum file size to 5MB, after which it enters the onInfo method and the completely that particular video snap.
Now I want to generate 5 such clips, one after the other. For which I add the following to the onInfo method:
#Override
public void onInfo(MediaRecorder mr, int what, int extra) {
// TODO Auto-generated method stub
//System.out.println("Reached onInfoListener");
if(what==android.media.MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED)
{
Toast.makeText(getApplicationContext(), "Video clip "+video_count+" recorded", Toast.LENGTH_SHORT).show();
recorder.stop(); //recorder is an object of type MediaRecorder
recorder.reset();
initRecorder(); //Reinitializing for subsequent video generation
prepareRecorder(); //Re preparing for subsequent video generation
}
}
private void initRecorder() {
recorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
recorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath(),"/FOLDERNAME");
if(!dir.exists())
{
dir.mkdir();
}
CamcorderProfile cpHigh = CamcorderProfile
.get(CamcorderProfile.QUALITY_HIGH);
recorder.setProfile(cpHigh);
recorder.setOutputFile("/sdcard/FOLDERNAME/video"+video_count+".mp4");
recorder.setMaxDuration(50000); // 50 seconds
recorder.setMaxFileSize(5*1048576); // Approximately 5 megabytes
}
private void prepareRecorder() {
recorder.setPreviewDisplay(cameraView.getHolder().getSurface());
try {
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
finish();
} catch (IOException e) {
e.printStackTrace();
finish();
}
}
NOTE: videocount is a variable used to give a distinct name to each generated video clip.
However after successfully capturing the first video clip, and just before the second clip an start recording, a IllegalStateException is encountered when I try to start the recorder object again. Since I am using recorder.reset() API, I thought that I would be able to reuse the recorder object for subsequent iterations. But it is giving this problem.
How to solve this issue? Is it neccessary to provide some delay after reinitializing the recorder object?
EDIT: If I keep the recorder.start() inside a button click, this works, that is, on every button click, a separate video is taken. But if I ask it to take say, 5 videos, on a single button click, the app crashes before it starts taking the second video, that is, it works only once. How to overcome this non-uniformity?
Going by the official documentation you need to call the setOutputFormat() method before you can call prepare(), like this:
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
Calling stop() on my MediaRecorder hangs indefinitely on the Samsung Galaxy Camera. Placing this call in a separate thread does not help the problem either.
Logcat does not show any error messages. However, running this same app does not incur any problems on the Samsung Galaxy Nexus.
This is the code surrounding my call to stop:
View.OnClickListener captureListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if (isRecording) {
// stop recording and release camera
mMediaRecorder.stop();
releaseMediaRecorder(); // release the MediaRecorder object
mCamera.lock(); // take camera access back from MediaRecorder
// inform the user that recording has stopped
captureButton.setText("Capture");
isRecording = false;
} else {
// initialize video camera
if (prepareVideoRecorder()) {
// Camera is available and unlocked, MediaRecorder is prepared,
// now you can start recording
mMediaRecorder.start();
// inform the user that recording has started
captureButton.setText("Stop");
isRecording = true;
} else {
// prepare didn't work, release the camera
releaseMediaRecorder();
// inform user
}
}
}
};
One thing I saw is that for some devices MediaRecorder.stop() hangs if there is no preview attached (i.e. you called Camera.stopPreview() before or maybe you never called startPreview()).
I'm using AudioRecorder to record short audio clips but I'm getting IllegalStateException when calling AudioRecord.start() I've been looking for hours but can't find the cause of this...
I've set Audio Rec + Write External Storage permissions.
Here's a piece of my code:
// main activity...
// Audio inits
final MediaRecorder recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(getTempPath());
...
// called the sound rec async
new SoundComponent(tvmic, pb, tb).execute(recorder);
// SoundComponent.java
// Getting IllegalStateException when calling recorder[0].start();
[..]
protected Long doInBackground(MediaRecorder... recorder) {
try {
recorder[0].prepare();
} catch (IOException e) {
Log.e("100", "prepare() failed");
}
while (tb.isChecked())
{
//publishProgress();
//recorder[0].prepare();
recorder[0].start(); // here it were it throws
try {
Thread.sleep(250);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// int amplitude = recorder[0].getMaxAmplitude();
recorder[0].stop();
}
// TODO Auto-generated method stub
return null;
}
[..]
public String getTempPath() // audio temp path
{
String path = Environment.getExternalStorageDirectory().getAbsolutePath();
path+="/temp/audiorectemp.3gp";
return path;
}
Starting and stopping the MediaRecorder multiple times in a loop probably isn't a good idea. Look closely at what you're doing, I've trimmed your code to make it easier to see...
while (tb.isChecked())
{
recorder[0].start(); // here it were it throws
// Sleep here
recorder[0].stop();
}
It probably isn't throwing an exception the first time you call start() but it will on the second loop. See the state machine diagram...MediaRecorder
Also, to detect when the doInBackground(...) thread should be exited, ther is a method on AsyncTask which can be called from the UI thread to cancel it.
The loop should ideally be while (!isCancelled()) and you should call the AsyncTask.cancel(...) method from the onCheckedChanged listener of tb in the main Activity code (assuming tb is a CheckBox or some other CompoundButton).