I have an issue using Android's MediaRecorder to record sound from microphone to .m4a files (AAC-LC, MPEG-4 container). Starting from API level 18, the default sampling rate drops from 44.1 or 48 kHz (depending on device) to only 8 Hz. If I specify the sampling rate using MediaRecorder.setAudioSamplingRate, it uses the specified rate but there are a lot of strange noise in the recording.
In LogCat, the following warning are happening from time to time:
(1)
Tag: AudioSource
Text: AudioRecord reported overrun
(2)
Tag: AudioFlinger
Text: RecordThread: buffer overflow
Here's the code:
MediaRecorder recorder = new MediaRecorder();
recorder.setAudioChannels(2);
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
recorder.setAudioSamplingRate(48000); // if not specified, defaults to 8kHz, if specified 44.1 or 48 kHz, lots of noise
recorder.setOutputFile("test.m4a");
try {
recorder.prepare();
recorder.start();
} catch (IOException ioe) {
Log.e(TAG, "IOException", ioe);
} catch (IllegalStateException ise) {
Log.e(TAG, "IllegalStateException", ise);
} catch (Exception e) {
Log.e(TAG, "Exception", e);
}
Any help is greatly appreciated.
After a long research and tries, this is the best working solution I made:
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setAudioEncodingBitRate(384000);
mRecorder.setAudioSamplingRate(44100);
You can set both the SamplingRate (as you have done) and the EncodingBitRate, which you have omitted.
I have been able to achieve very high quality recordings using the following:
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC);
mRecorder.setAudioSamplingRate(48000);
mRecorder.setAudioEncodingBitRate(384000);
This will encode with 8 bits per sample, which is probably beyond the available quality of the microphone on most devices.
Related
For an application, I'm setting up the MediaRecorder using the following:
mediaRecorder.setAudioSource(AudioSource.MIC);
mediaRecorder.setVideoSource(VideoSource.SURFACE);
CamcorderProfile cpHigh = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
cpHigh.audioChannels = 2;
mediaRecorder.setProfile(cpHigh);
mediaRecorder.setVideoSize(specifiedWidth, specifiedHeight);
mediaRecorder.setOutputFile(videoFilename);
try {
mediaRecorder.prepare();
} catch (Exception e) {
...
}
Despite setting cpHigh.audioChannels = 2; the saved files have mono audio. If I change the code to have mediaRecorder.setAudioChannels(2); instead, it still doesn't work; I'm still seeing Mono, instead of Stereo. Any idea why?
I'm getting an error when I start a live streaming using mediarecoder using libstreaming android on sony z and wowza streaming server.
07-20 10:49:37.832: E/MediaRecorder(6752): start failed: -19
ConfNotSupportedException
This error is thrown when I change frame rate of video in (15 < fps < 30 or fps > 30). If I set fps = 15 or fps = 30, this error is not thrown. This error is only on sony z device, on some different devices are Samsung, Htc, Nexus are not.
I downloaded Wowza Gocoder app to test whether this error is only on sony z. And, I can change fps from 15 to 60 without any errors. So, I guess the libstreaming library has problems.
My config code :
// Configures the SessionBuilder
mSession = SessionBuilder.getInstance()
.setContext(getApplicationContext())
.setAudioEncoder(SessionBuilder.AUDIO_AAC)
.setAudioQuality(new AudioQuality(8000, 16000))
.setVideoEncoder(SessionBuilder.VIDEO_H264)
.setSurfaceView(mSurfaceView).setPreviewOrientation(0)
.setCallback(this).build();
// Configures the RTSP client
mClient = new RtspClient();
mClient.setSession(mSession);
mClient.setCallback(this);
// Use this to force streaming with the MediaRecorder API
mSession.getVideoTrack().setStreamingMethod(
MediaStream.MODE_MEDIARECORDER_API);
This code below start stream :
protected void encodeWithMediaRecorder() throws IOException, ConfNotSupportedException {
Log.d(TAG,"Video encoded using the MediaRecorder API");
// We need a local socket to forward data output by the camera to the packetizer
createSockets();
// Reopens the camera if needed
destroyCamera();
createCamera();
// The camera must be unlocked before the MediaRecorder can use it
unlockCamera();
try {
mMediaRecorder = new MediaRecorder();
mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setVideoEncoder(mVideoEncoder);
mMediaRecorder.setPreviewDisplay(mSurfaceView.getHolder().getSurface());
mMediaRecorder.setVideoSize(mRequestedQuality.resX,mRequestedQuality.resY);
mMediaRecorder.setVideoFrameRate(mRequestedQuality.framerate);
// The bandwidth actually consumed is often above what was requested
mMediaRecorder.setVideoEncodingBitRate((int)(mRequestedQuality.bitrate*0.8));
// We write the output of the camera in a local socket instead of a file !
// This one little trick makes streaming feasible quiet simply: data from the camera
// can then be manipulated at the other end of the socket
FileDescriptor fd = null;
if (sPipeApi == PIPE_API_PFD) {
fd = mParcelWrite.getFileDescriptor();
} else {
fd = mSender.getFileDescriptor();
}
mMediaRecorder.setOutputFile(fd);
mMediaRecorder.prepare();
mMediaRecorder.start();
} catch (Exception e) {
throw new ConfNotSupportedException(e.getMessage());
}
Any one have ideas? Thanks!
Commenting out
mMediaRecorder.setVideoFrameRate(mRequestedQuality.framerate);
solved my problem.
It seems that even after I am setting the video recorder profile to low, the video is in the highest quality.
Here is my code :
camera.setDisplayOrientation(90);
camera.unlock();
recorder.reset();
recorder.setCamera(camera);
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
recorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_LOW));
//when removing these comments I get an exception on my 4.2.2 device when calling start() on the recorder.
/* recorder.setVideoFrameRate(24);
recorder.setVideoSize(480, 360);
*/
recorder.setOrientationHint(90);
file = FileUtils.getFileName(FileTypes.VIDEO);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
recorder.setOutputFile(FileUtils.getFileName(FileTypes.VIDEO).toString());
recorder.setMaxDuration(45000);
Try this. Although this seems like the same as your code, It works for me.create a seperate instance for CamcorderProfile and set the recorder profile to this instance.
CamcorderProfile cprofileLow = CamcorderProfile
.get(CamcorderProfile.QUALITY_LOW);
recorder.setProfile(cprofileLow);
recorder.setOutputFile("/sdcard/videocapture_example.mp4");
recorder.setMaxDuration(50000); // 50 seconds
recorder.setMaxFileSize(3000000); // Approximately 3 megabytes
Please do notice that you have a high end device(since yours is of 4.2.2), you may get a comparitively good resolution provided,it is the lowest possible resolution in your device.
hello,i want to use mediaRecorder to record voice. i want to save the format is amr.
this.mediaRecorder = new MediaRecorder();
this.mediaRecorder.setAudioChannels(1);
this.mediaRecorder.setAudioSamplingRate(8000);
this.mediaRecorder.setAudioEncodingBitRate(16);
this.mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
this.mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
this.mediaRecorder.setOutputFile(this.file.getAbsolutePath());
this.mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
i used this.mediaRecorder.setAudioEncodingBitRate(16), some device is ok
mediaRecorder.setAudioEncodingBitRate(12500),somedevice is ok
but i delete the mediaRecorder.setAudioEncodingBitRate some device is ok
so my question how to get the default the AudioEncodingBitRate.
which parameter i need to use?
You set the AudioEncodingBitRate too low. I made the same mistake :-)
This seems to work:
MediaRecorder recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
if (Build.VERSION.SDK_INT >= 10) {
recorder.setAudioSamplingRate(44100);
recorder.setAudioEncodingBitRate(96000);
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
} else {
// older version of Android, use crappy sounding voice codec
recorder.setAudioSamplingRate(8000);
recorder.setAudioEncodingBitRate(12200);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
}
recorder.setOutputFile(file.getAbsolutePath());
try {
recorder.prepare();
} catch (IOException e) {
throw new RuntimeException(e);
}
The idea comes from here
plus: read the docs. The docs of setAudioSamplingRate say the following:
The sampling rate really depends on the format for the audio recording, as well as the capabilities of the platform. For instance, the sampling rate supported by AAC audio coding standard ranges from 8 to 96 kHz, the sampling rate supported by AMRNB is 8kHz, and the sampling rate supported by AMRWB is 16kHz.
I am using bellow configurations and gives amazing clear recording output.
localFileName = getFileName()+".wav";
localFile = new File(localdir, localFileName);
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mRecorder.setOutputFormat(AudioFormat.ENCODING_PCM_16BIT);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setAudioChannels(1);
mRecorder.setAudioEncodingBitRate(128000);
mRecorder.setAudioSamplingRate(44100);
mRecorder.setOutputFile(localFile.getPath());
however if you are recording along with playing audio simultaneously it has some issues in samsung devices.
[but again only when you are playing audio and recording both together at the same time]
I find that the encoding bitrate should be calculated from the sample rate.
There is a good write-up of how these values relate on https://micropyramid.com/blog/understanding-audio-quality-bit-rate-sample-rate/
I use 8:1 compression for high-quality recordings. I prefer 48 KHz sampling, but the same logic works at an 8000 Hz sample rate requested for this post.
final int BITS_PER_SAMPLE = 16; // 16-bit data
final int NUMBER_CHANNELS = 1; // Mono
final int COMPRESSION_AMOUNT = 8; // Compress the audio at 8:1
public MediaRecorder setupRecorder(String filename, int selectedAudioSource, int sampleRate) {
final int uncompressedBitRate = sampleRate * BITS_PER_SAMPLE * NUMBER_CHANNELS;
final int encodedBitRate = uncompressedBitRate / COMPRESSION_AMOUNT;
mediaRecorder = new MediaRecorder();
try {
mediaRecorder.setAudioSource(selectedAudioSource);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mediaRecorder.setAudioEncoder(MediaRecorder.OutputFormat.AMR_NB);
mediaRecorder.setAudioSamplingRate(sampleRate);
mediaRecorder.setAudioEncodingBitRate(encodedBitRate);
mediaRecorder.setOutputFile(filename);
}catch (Exception e) {
// TODO
}
return mediaRecorder;
}
MediaRecorder mediaRecorder = setupRecorder(this.file.getAbsolutePath(),
MediaRecorder.AudioSource.MIC,
8000);
hi
i have audio recording in my i use mediarecorder class for recding audio but i have out of memory exception when i reach 2 m , that my limit. i given my code below.
time
private static void audiorecding()
{
if (audio_recding_stop_flag == 0) {
audio_recding_stop_flag = 1;
recorder.stop();
recorder.reset();
recorder.release();
recorder=new MediaRecorder();
int maxtime = audio_seekbar_play_indication.getMax();
audio_seekbar_progress = 0;
audio_seekbar_incr = maxtime/ AngiesData.gettotalaudiorecdingtimeinseconds(audio_recding_time.getText().toString());
audio_recding_time.stop();
recd_stop_audio.setBackgroundResource(R.drawable.rec_btn);
audio_play_pause.setEnabled(true);
delete_recding.setEnabled(true);
AngiesData.angiesListContext.setAudioRecd(true);
AngiesData.angiesListContext.setAudioRecdingTime((String) audio_recding_time.getText());
}
else {
audio_recding_stop_flag = 0;
try {
audio_play_pause.setEnabled(false);
delete_recding.setEnabled(false);
recd_stop_audio.setBackgroundResource(R.drawable.recording_stop);
audio_recding_time .setBase(SystemClock.elapsedRealtime());
audio_recding_time.start();
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC); // AudioSource
recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); // OutputFormat
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
path = InitialValueLoader.sanitizePath("Audio/VoiceReport");
InitialValueLoader.filecheck(path);
recorder.setOutputFile(path);
recorder.setMaxDuration(120000);
recorder.prepare();
recorder.start();
} catch (Exception e) {
e.printStackTrace();
}
}
I would try something with the AudioRecord-Class ( http://developer.android.com/reference/android/media/AudioRecord.html), use the read()-function with a while-loop and keep going until you got your 2 min. of samples (check the sampleRate, e.g. 44.1 Khz => 44100 smpls/sec => 60*44100 smpl/min). Making sure you have 2 min. of audio with the help of a timer is not a good idea. With timers you can never be sure on the precision, if another thread is interferring your timer will be evoked later, and in audio samples speaking it might be MUCH later.
I suggest useing a Chronometer instead of a timer
In programming if you have to deal with timings generally all time values are in milliseconds. You are trying to stop the recording after two seconds there not two minutes which would be 120000 milliseconds.