MediaRecorder on Android interfeering with other Media Aplications - android

I am using the MediaRecorder in my app to measure decibel. For this a new MediaRecorder-Object is created once every 10 seconds, it runs for one second during which getMaxAmplitude() is called 10 times and the average is calculated. After this the MediaRecorder object is stopped and deleted.
The Method to start the Recorder looks like this
public boolean start()
{
mRecorder = new 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 (IllegalStateException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
catch(Exception e)
{
e.printStackTrace();
return false;
}
mRecorder.start();
mRecorder.getMaxAmplitude();
return true;
}
and the Method to stop looks like this
public void stop()
{
//Log.d("SPLService", "stop()");
mRecorder.stop();
mRecorder.reset();
mRecorder.release();
mRecorder=null;
}
This is pretty much the way the tutorial on the Android Website instructed.
The problem arises when I use this app in parallel with other Apps using Media, like the Mediaplayer on Android. Whenever a sound measurement is taken using my App other media Apps stop or even crash, even though their objects should be independent of mine.
I have tried using AudioRecord instead of MediaRecorder but the interference with other apps was the same.
I guess my question would be: how can I avoid such interference?

Related

Android Camera 2 Api

I have been trying camera2 API. I have downloaded code from
https://developer.android.com/samples/Camera2Video/index.html to learn about how it works. It works fine till I stop recording. When I stop recording it runs following code.
private void stopRecordingVideo() {
// UI
mIsRecordingVideo = false;
mBtn_Video.setText(R.string.record);
// Stop recording
try {
mMediaRecorder.stop();
mMediaRecorder.reset();
}
catch (Exception e) {
e.printStackTrace();
}
Activity activity = getActivity();
if (null != activity) {
System.out.println("file " + getVideoFile(activity));
Toast.makeText(activity, "Video saved: " + getVideoFile(activity),
Toast.LENGTH_SHORT).show();
}
startPreview();
at mMediaRecorder.stop(); it throw following error
01-12 16:24:23.115 2161-2200/com.cameratwoapi E/Surface﹕ queueBuffer: error queuing buffer to SurfaceTexture, -19
01-12 16:24:23.135 2161-2200/com.cameratwoapi E/EGL_emulation﹕ tid 2200: swapBuffers(285): error 0x3003 (EGL_BAD_ALLOC)
01-12 16:24:23.197 2161-2200/com.cameratwoapi E/CameraDeviceGLThread-0﹕ Received exception on GL render thread:
java.lang.IllegalStateException: swapBuffers: EGL error: 0x3003
at android.hardware.camera2.legacy.SurfaceTextureRenderer.checkEglError(SurfaceTextureRenderer.java:487)
at android.hardware.camera2.legacy.SurfaceTextureRenderer.swapBuffers(SurfaceTextureRenderer.java:480)
at android.hardware.camera2.legacy.SurfaceTextureRenderer.drawIntoSurfaces(SurfaceTextureRenderer.java:681)
at android.hardware.camera2.legacy.GLThreadManager$1.handleMessage(GLThreadManager.java:103)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:135)
at android.os.HandlerThread.run(HandlerThread.java:61)
Any Idea what I am doing wrong. I spent few hours but couldn't find any solution.
Edit - I am using geneymotion emulator. The path I am using
file /storage/emulated/0/Android/data/com.gold.cameratwoapi/files/video.mp4
Thanks
My solution is to change void stopRecordingVideo() as following:
private void stopRecordingVideo() {
// UI
mIsRecordingVideo = false;
mButtonVideo.setText(R.string.record);
// Added by Ben Ning, to resolve exception issue when stop recording.
try {
mPreviewSession.stopRepeating();
mPreviewSession.abortCaptures();
} catch (CameraAccessException e) {
e.printStackTrace();
}
// Stop recording
mMediaRecorder.stop();
mMediaRecorder.reset();
}
Key is:
try {
mPreviewSession.stopRepeating();
mPreviewSession.abortCaptures();
} catch (CameraAccessException e) {
e.printStackTrace();
}
private void stopRecordingVideo() {
// UI
mIsRecordingVideo = false;
mButtonVideo.setText(R.string.record);
// Added by Ben Ning, to resolve exception issue when stop recording.
try {
mPreviewSession.stopRepeating();
mPreviewSession.abortCaptures();
} catch (CameraAccessException e) {
e.printStackTrace();
}
// Stop recording
mMediaRecorder.stop();
mMediaRecorder.reset();
Activity activity = getActivity();
if (null != activity) {
Toast.makeText(activity, "Video saved: " + getVideoFile(activity),
Toast.LENGTH_SHORT).show();
}
startPreview();
}
this is working for me.
After calling mMediaRecorder.stop() an IllegalStateException is always thrown. I've noticed that on devices with INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY the CameraDevice changes status to error, immediately calling onError() in the CameraDevice.StateCallback.
In the sample you referenced, onError() closes the camera and finishes the activity, so just change onError() to re-open the camera, like this:
#Override
public void onError(CameraDevice cameraDevice, int error) {
// mCameraOpenCloseLock.release();
// cameraDevice.close();
// mCameraDevice = null;
// Activity activity = getActivity();
// if (null != activity) {
// activity.finish();
// }
closeCamera();
openCamera(mTextureView.getWidth(), mTextureView.getHeight());
}
It'd also be a good idea to put some check in there to make sure that if an error really did happen, that the commented out code gets called instead of entering a loop of trying to open the camera over and over.
Tested on a Moto G 2nd gen, with Android 5.0.2
It depends on what you are doing with the CameraCaptureSession and the MediaRecorder but when you call mMediaRecorder.stop() I think it is destroying the surface used for the camera preview session which causes this error because the documentation says
Once recording is stopped, you will have to configure it again as if it has just been constructed
Therefore if you call PreviewSession.abortCaptures() (mPreviewSession.stopRepeating(); isn't necessary from what I gather) it stops the camera sending output to the recorder surface which will allow you to stop the MediaRecorder without issue.
PreviewSession.abortCaptures(); doesn't instantly stop the camera preview output so you might find you need to call MediaRecorder.stop() in the onClosed() or onReady() method of the CameraCaptureSession.StateCallback
In my case, I use TimerTask and a Handler. There is error direct to the mMediaRecorder.stop(). So I use this method
final Handler mTimerHandler = new Handler(Looper.getMainLooper());
mIsRecordingVideo = false;
// Stop recording
try {
mPreviewSession.stopRepeating();
mPreviewSession.abortCaptures();
} catch (CameraAccessException e) {
e.printStackTrace();
}
try{
Timer timer = new Timer();
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
mTimerHandler.post(new Runnable() {
#Override
public void run() {
mMediaRecorder.stop();
mMediaRecorder.reset();
}
});
}
};
timer.schedule(timerTask,30);
}catch(RuntimeException e){
Log.e("----------------","---->>>>>>>>>"+e);
e.printStackTrace();
}

How to use the same recorder object repeatedly

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);

Android 4.0.4 MediaPlayer prepare issue using RTSP urls

I am experiencing an odd issue with a video streaming application I am working on. The actual streaming of video/audio is working fine on all of my test devices. However, on seemingly any device 4.0+, when using an RTSP URL, prepare() returns instantly (this causes an issue providing proper feedback to the users while a video is loading and interferes with a few other systems I have in place).
Below is the block of code where I initialize and setup my MediaPlayer, but keep a few things in mind:
My initPlayer method is called from an AsyncTask.
The video does eventually play correctly, but prepare returning instantly creates a lack of feedback to the user during a video load.
No errors of any kind occur during the entire process
start() is called on the MediaPlayer via the onPrepared method in my OnPreparedListener, which obviously becomes an issue when prepare() returns before it is actually ready to be played.
HTTP streams seem to work fine, and on every test device below 4.0 the issue does not occur.
I have been trying to fix this for a ridiculous amount of time, and haven't been able to find anyone else who has ran into this problem. Any ideas would be greatly appreciated.
public void initPlayer() {
//We first need to make sure the MediaPlayer isn't null
if(mMediaPlayer==null){
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnPreparedListener(mediaPlayerPreparedListener);
mMediaPlayer.setOnCompletionListener(mediaPlayerCompletionListener);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
//If a video/stream has been chosen while another is already playing
else if(mMediaPlayer.isPlaying()){
mMediaPlayer.reset();
}
//Video is not in full screen mode
second = false;
try {
mMediaPlayer.setDataSource(videoString);
holder = mPreview.getHolder();
mMediaPlayer.setDisplay(holder);
mMediaPlayer.prepare();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//onPreparedListener
private OnPreparedListener mediaPlayerPreparedListener = new OnPreparedListener(){
public void onPrepared(MediaPlayer mp) {
mp.start();
vidPb.setVisibility(View.INVISIBLE);
}
};
Use mp.prepareAsync() as it is better for streaming media. Using prepare() blocks until MediaPlayer is ready for playback or an IllegalStateException occurs. Also, in android 4 (ICS), blocking on any UI thread is even more strict and may cause an ANR (Activity not responding) dialog to appear.
One final thought, try to avoid using e.printStackTrace(); in android apps.
Instead, use the Log.e("TAG_STRING", e.getMessage(), e); to print errors to the android logging system that you can access from logcat.
All in all, it should looks something like this:
try {
mMediaPlayer.setDataSource(videoString);
holder = mPreview.getHolder();
mMediaPlayer.setDisplay(holder);
mMediaPlayer.prepareAsync();
} catch (IllegalArgumentException e) {
Log.e("TAG_STRING", e.getMessage(), e);
} catch (SecurityException e) {
Log.e("TAG_STRING", e.getMessage(), e);
} catch (IllegalStateException e) {
Log.e("TAG_STRING", e.getMessage(), e);
} catch (IOException e) {
Log.e("TAG_STRING", e.getMessage(), e);
}

how to clear surface holder when media player is finished?

I made a video player with surfaceview and mediaplayer.
i have 10 videos and 10 buttons.
if click on each buttons, each videos are playing.
here is my code..
//onCreate
holder = surfaceview.getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
//Button1
if(mp == null)mp = new MediaPlayer();
mp.setDataSource(mediaplay_path);
mp.setDisplay(holder);
mp.setScreenOnWhilePlaying(true);
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.prepare();
mp.start();
//Button2
if(mp != null){
mp.stop();
mp.reset();
}
mp.setDataSource(mediaplay_path2);
mp.setDisplay(holder);
mp.setScreenOnWhilePlaying(true);
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.prepare();
mp.start();
//Button3~Button10 is same as Button2..
everything is fine.
my custom videoview is working alright.
but when the video turns to the next, the last scene of the previous video is remain for a while and turns to the next video scene.
i think it's because the previous surfaceview should be clear before next video is playing.
but i have no idea how to clear the surfaceview or surface holder.
i've searched for this but only could find how to play the video, not how to clear the surfaceview which is set the disaply from mediaplayer.
please help me~~!
Took me two weeks to figure this out. By setting the surfaceholder to TRANSPARENT, Android will destroy the surface. Then setting it back to OPAQUE creates a new surface "clearing" the surface. Note surfacecreate and surfacedestroy events will fire, so if you have code there, beware. I put a imageview set to black to give it a black background. There maybe better ways for that.
private void playVideoA() {
imageViewBlack.bringToFront();
surfaceHolder.setFormat(PixelFormat.TRANSPARENT);
surfaceHolder.setFormat(PixelFormat.OPAQUE);
surfaceView.bringToFront();
mediaPlayerA.setDisplay(surfaceHolder);
//surfaceView.setAlpha((float) 0.01);
mediaPlayerA.start();
};
private void prepareVideoA(String url) {
try {
mediaPlayerA = new MediaPlayer();
mediaPlayerA.setDataSource(url);
mediaPlayerA.prepareAsync();
mediaPlayerA.setOnPreparedListener(this);
mediaPlayerA.setOnCompletionListener(this);
mediaPlayerA.setOnBufferingUpdateListener(this);
mediaPlayerA.setOnInfoListener(this);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
#Override
public void onPrepared(MediaPlayer mp) {
playVideoA()
}
video.getHolder().setFormat(PixelFormat.TRANSPARENT);
video.getHolder().setFormat(PixelFormat.OPAQUE);
video.setVideoURI(Uri.parse(temp));
video.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
it work for me
The SurfaceHolder has lockCanvas methods that will allow you to draw directly to the Surface. Use the drawColor method of the Canvas to fill it with black.
That said, it may be preferable to remove the SurfaceView (as suggested by smile2you), because that should destroy the Surface altogether and free up unused resources. And definitely make sure you are calling release on the MediaPlayers after they are done with playback. Holding on to too many video resources will crash your app.
may be you can use removeView to remove the old custome videoview ,then add the new view
surfaceview.setVisibility(View.GONE);

IllegalStateException when calling AudioRecord.start()

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).

Categories

Resources