1.按住录制松开录制完成
2.录制倒计时
3.Seekbar展示
4.录制视频声音大小展示
废话不多说 直接上代码 注释很详细
** * [camera.setDisplayOrientation(90);// 设置预览视频时时竖屏] * * @author ly * @version 1.0 * @date 2016年8月1日 下午4:10:37 **/ @SuppressLint("NewApi") public class VideoActivity extends Activity implements Callback, MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener { private SurfaceView mSurfaceview; private Button mBtnStartPress;// 开始停止录制按键 private MediaRecorder mRecorder;// 录制视频的类 private SurfaceHolder mSurfaceHolder;// 显示视频 private Camera camera; private int flag = 1; private View rcChat_popup;//显示出音量大小 private Handler mHandler = new Handler(); private long startVoiceT, endVoiceT; private static final int POLL_INTERVAL = 300; private Chronometer timedown;//显示倒计时 private LinearLayout voice_rcd_hint_rcding; private long timeTotalInS = 0; private long timeLeftInS = 0; private ImageView volume; private ProgressBar mProgressBar; private int mRecordMaxTime = 10;// 一次拍摄的最长时间 private Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case 1:// 开始录制 // timeView.setText(time + "s"); break; case 2:// 录制结束 Toast.makeText(VideoActivity.this, "录制完成", Toast.LENGTH_SHORT).show(); break; } } ; }; private CamcorderProfile mProfile; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE);// 去掉标题栏 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);// 设置全屏 getWindow().setFormat(PixelFormat.TRANSLUCENT); // 选择支持半透明模式,在有surfaceview的activity中使用。 setContentView(R.layout.video_layout);// 加载布局 mSurfaceview = (SurfaceView) findViewById(R.id.surfaceview); mBtnStartPress = (Button) findViewById(R.id.btn_press); rcChat_popup = this.findViewById(R.id.rcChat_popup); timedown = (Chronometer) findViewById(R.id.timedown); voice_rcd_hint_rcding = (LinearLayout) this.findViewById(R.id.voice_rcd_hint_rcding); volume = (ImageView) this.findViewById(R.id.volume); mProgressBar = (ProgressBar) findViewById(R.id.progressBar); mProgressBar.setMax(mRecordMaxTime);//设置最大值 mBtnStartPress.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent event) { // TODO Auto-generated method stub if (event.getAction() == MotionEvent.ACTION_DOWN) { mBtnStartPress.setBackgroundColor(getResources().getColor(R.color.actionbar_blue)); mBtnStartPress.setText("松开 结束"); mBtnStartPress.setTextColor(getResources().getColor(R.color.voice)); int[] location = new int[2]; mBtnStartPress.getLocationInWindow(location); // 获取在当前窗口内的绝对坐标 int btn_rc_Y = location[1]; int btn_rc_X = location[0]; if (flag == 1) { if (!Environment.getExternalStorageDirectory().exists()) { Toast.makeText(VideoActivity.this, "No SDCard", Toast.LENGTH_LONG).show(); return false; } System.out.println(event.getY() + "..." + btn_rc_Y + "...." + event.getX() + "...." + btn_rc_X); if (event.getY() < btn_rc_Y && event.getX() > btn_rc_X) {//判断手势按下的位置是否是语音录制按钮的范围内 rcChat_popup.setVisibility(View.VISIBLE); mHandler.postDelayed(new Runnable() { public void run() { } }, 300); startVoiceT = SystemClock.currentThreadTimeMillis(); recordMedia(); down(); timedown.setVisibility(View.VISIBLE); //设置录音时间 initTimer(mRecordMaxTime); timedown.start(); flag = 2; } } } else if (event.getAction() == MotionEvent.ACTION_UP) { mBtnStartPress.setBackgroundColor(getResources().getColor(R.color.voice)); mBtnStartPress.setText("按住 拍摄"); mBtnStartPress.setTextColor(Color.BLACK); timedown.stop(); if (flag == 2) { rcChat_popup.setVisibility(View.GONE); timedown.setVisibility(View.GONE); up(); flag = 1; } else { voice_rcd_hint_rcding.setVisibility(View.GONE); up(); endVoiceT = SystemClock.currentThreadTimeMillis(); flag = 1; int time = (int) ((endVoiceT - startVoiceT) / 1000); System.out.println(time); } } return false; } }); SurfaceHolder holder = mSurfaceview.getHolder();// 取得holder holder.addCallback(this); // holder加入回调接口 holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);// setType必须设置,要不出错. mProfile = CamcorderProfile.get(0, 6); } public void recordMedia() { mHandler.postDelayed(mPollTask, POLL_INTERVAL); // 获取类的实例 recorder = ExtAudioRecorder.getInstanse(true); // false为未压缩的录音(WAV) true为压缩的 recorder.recordChat(Environment.getExternalStorageDirectory() .getAbsolutePath()+ File.separator, "upload_media.wav"); //开始录音 } private Runnable mSleepTask = new Runnable() { public void run() { up(); } }; private ExtAudioRecorder recorder = null; private Runnable mPollTask = new Runnable() { public void run() { double amp = recorder.getAmplitude(); // double amp = mSensor.getAmplitude(); updateDisplay(amp); mHandler.postDelayed(mPollTask, POLL_INTERVAL); } }; private void updateDisplay(double signalEMA) { switch ((int) signalEMA) { case 0: case 1: volume.setImageResource(R.drawable.amp1); break; case 2: case 3: volume.setImageResource(R.drawable.amp2); break; case 4: case 5: volume.setImageResource(R.drawable.amp3); break; case 6: case 7: volume.setImageResource(R.drawable.amp4); break; case 8: case 9: volume.setImageResource(R.drawable.amp5); break; case 10: case 11: volume.setImageResource(R.drawable.amp6); break; default: volume.setImageResource(R.drawable.amp7); break; } } /** * 初始化计时器,计时器是通过widget.Chronometer来实现的 * * @param total 一共多少秒 */ private void initTimer(int total) { this.timeTotalInS = total; this.timeLeftInS = total; timedown.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() { @Override public void onChronometerTick(Chronometer chronometer) { if (timeLeftInS <= 0) { Toast.makeText(VideoActivity.this, "录像时间到", Toast.LENGTH_SHORT).show(); timedown.stop(); //录像停止 rcChat_popup.setVisibility(View.GONE); timedown.setVisibility(View.GONE); return; } timeLeftInS--; refreshTimeLeft(); // 设置进度条 mProgressBar.setProgress((int) (mRecordMaxTime - timeLeftInS)); } }); } private void refreshTimeLeft() { this.timedown.setText("录音时间剩余:" + timeLeftInS); //TODO 格式化字符串 } public void down(){ // 开始 if (mRecorder == null) { mRecorder = new MediaRecorder(); // 创建mediarecorder的对象 } try { camera.unlock(); mRecorder.setCamera(camera); mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);// 这两项需要放在setOutputFormat之前 mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);// 设置录制视频源为Camera(相机) //mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);// // 设置录制完成后视频的封装格式THREE_GPP为3gp.MPEG_4为mp4 //1.8 以上 mRecorder.setProfile(mProfile);//这句是让视频文件在PC端也能视频竖屏播放的保证,跟下面注释了的不能联用 // 这两项需要放在setOutputFormat之后 // mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);// 设置录制的视频编码h263 // // mRecorder.setVideoSize(800, 480);// 设置视频录制的分辨率。必须放在设置编码和格式的后面,否则报错 // mRecorder.setVideoFrameRate(30);// 设置录制的视频帧率。必须放在设置编码和格式的后面,否则报错 mRecorder.setMaxDuration(10000);// 设置最大的录制时间 // mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); // 设置路径 String path = getSDPath(); if (path != null) { File dir = new File(path + "/TestCameraFile"); if (!dir.exists()) { dir.mkdir(); } path = dir + "/" + getDate() + ".mp4"; mRecorder.setOutputFile(path); mRecorder.setOrientationHint(90); mRecorder.prepare();// 准备录制 mRecorder.start(); // 开始录制 mRecorder.setOnErrorListener(this); mRecorder.setOnInfoListener(this); } } catch (Exception e) { e.printStackTrace(); } } public void up(){ // 停止 mHandler.removeCallbacks(mSleepTask); mHandler.removeCallbacks(mPollTask); try { mRecorder.stop(); //停止 mRecorder.reset(); // 重置 mProgressBar.setProgress(0); handler.sendEmptyMessageDelayed(2, 1000); } catch (Exception e) { e.printStackTrace(); } } /** * 使用时间对录像起名 * * @return */ public static String getDate() { Calendar ca = Calendar.getInstance(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmss"); String date = sdf.format(ca.getTimeInMillis()); return date; } /** * 获取SD path * * @return */ public String getSDPath() { File sdDir = null; boolean sdCardExist = Environment.getExternalStorageState().equals( android.os.Environment.MEDIA_MOUNTED); // 判断sd卡是否存在 if (sdCardExist) { sdDir = Environment.getExternalStorageDirectory();// 获取跟目录 // Toast.makeText(this,sdDir.toString(),Toast.LENGTH_LONG).show(); return sdDir.toString(); } else { Toast.makeText(this, "没有SD卡", Toast.LENGTH_LONG).show(); } return null; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { camera = Camera.open(); // 获取Camera实例 try { camera.setPreviewDisplay(holder); mSurfaceview.setLayoutParams(new LinearLayout.LayoutParams(width, height)); } catch (Exception e) { // 如果出现异常,则释放Camera对象 camera.release(); } camera.setDisplayOrientation(90);// 设置预览视频时时竖屏 // 启动预览功能 camera.startPreview(); // 将holder,这个holder为开始在onCreate里面取得的holder,将它赋给mSurfaceHolder mSurfaceHolder = holder; } @Override public void surfaceCreated(SurfaceHolder holder) { // 将holder,这个holder为开始在onCreate里面取得的holder,将它赋给mSurfaceHolder mSurfaceHolder = holder; } @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub // surfaceDestroyed的时候同时对象设置为null mSurfaceview = null; mSurfaceHolder = null; if (mRecorder != null) { mRecorder.release(); // Now the object cannot be reused mRecorder = null; } if (camera != null) { camera.release(); camera = null; } } @Override public void onInfo(MediaRecorder arg0, int what, int arg2) { // TODO Auto-generated method stub if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) { } else if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) { } } @Override public void onError(MediaRecorder arg0, int what, int extra) { // TODO Auto-generated method stub if (what == MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN) { } } }
布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <SurfaceView android:id="@+id/surfaceview" android:layout_width="fill_parent" android:layout_height="0dp" android:layout_weight="15" /> <ProgressBar android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="0.3" style="?android:attr/progressBarStyleHorizontal" android:max="100" android:progress="0" android:id="@+id/progressBar" android:layout_gravity="center_horizontal" /> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:background="@color/voice" android:orientation="horizontal"> <TextView android:id="@+id/textView1" android:layout_width="0dp" android:layout_height="match_parent" android:layout_centerHorizontal="true" android:layout_weight="2" android:gravity="center" android:text="0S" android:visibility="gone" /> <Chronometer android:id="@+id/timedown" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="30:00" android:textColor="@color/actionbar_blue" android:textSize="13sp" android:visibility="gone" /> <Button android:id="@+id/stops" android:layout_width="0dp" android:layout_height="match_parent" android:layout_centerHorizontal="true" android:layout_weight="9" android:background="@color/voice" android:text="点击 录制" android:textSize="16dp" android:visibility="gone" /> <Button android:id="@+id/btn_press" android:layout_width="0dp" android:layout_height="match_parent" android:layout_centerHorizontal="true" android:layout_weight="9" android:background="@color/voice" android:text="按住 录制" android:textSize="16dp" /> <LinearLayout android:id="@+id/rcChat_popup" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="3" android:gravity="center" android:orientation="horizontal" android:visibility="gone"> <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:orientation="vertical"> <include layout="@layout/rcd_hint_window" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> </LinearLayout> </LinearLayout> </LinearLayout> </LinearLayout>
音频的一个类,为了展示出录制视频声音大小
public class ExtAudioRecorder { private final static int[] sampleRates = {44100, 22050, 11025, 8000, 5512}; private static ExtAudioRecorder result = null; public static ExtAudioRecorder getInstanse(Boolean recordingCompressed) { if (recordingCompressed) { result = new ExtAudioRecorder(false, AudioSource.MIC, sampleRates[3], AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_8BIT); } else { int i = 2; // do { result = new ExtAudioRecorder(true, AudioSource.MIC, sampleRates[i], AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT); // } while ((++i < sampleRates.length) // & !(result.getState() == ExtAudioRecorder.State.INITIALIZING)); } return result; } /** * INITIALIZING : recorder is initializing; READY : recorder has been * initialized, recorder not yet started RECORDING : recording ERROR : * reconstruction needed STOPPED: reset needed */ public enum State { INITIALIZING, READY, RECORDING, ERROR, STOPPED } ; public static final boolean RECORDING_UNCOMPRESSED = true; public static final boolean RECORDING_COMPRESSED = false; // The interval in which the recorded samples are output to the file // Used only in uncompressed mode private static final int TIMER_INTERVAL = 120; // Toggles uncompressed recording on/off; RECORDING_UNCOMPRESSED / // RECORDING_COMPRESSED private boolean rUncompressed; // Recorder used for uncompressed recording private AudioRecord audioRecorder = null; // Recorder used for compressed recording private MediaRecorder mediaRecorder = null; // Stores current amplitude (only in uncompressed mode) private int cAmplitude = 0; // Output file path private String filePath = null; // Recorder state; see State private State state; // File writer (only in uncompressed mode) private RandomAccessFile randomAccessWriter; // Number of channels, sample rate, sample size(size in bits), buffer size, // audio source, sample size(see AudioFormat) private short nChannels; private int sRate; private short bSamples; private int bufferSize; private int aSource; private int aFormat; // Number of frames written to file on each output(only in uncompressed // mode) private int framePeriod; // Buffer for output(only in uncompressed mode) private byte[] buffer; // Number of bytes written to file after header(only in uncompressed mode) // after stop() is called, this size is written to the header/data chunk in // the wave file private int payloadSize; /** * Returns the state of the recorder in a RehearsalAudioRecord.State typed * object. Useful, as no exceptions are thrown. * * @return recorder state */ public State getState() { return state; } /* * * Method used for recording. */ private AudioRecord.OnRecordPositionUpdateListener updateListener = new AudioRecord.OnRecordPositionUpdateListener() { public void onPeriodicNotification(AudioRecord recorder) { audioRecorder.read(buffer, 0, buffer.length); // Fill buffer try { randomAccessWriter.write(buffer); // Write buffer to file payloadSize += buffer.length; if (bSamples == 16) { for (int i = 0; i < buffer.length / 2; i++) { // 16bit // sample // size short curSample = getShort(buffer[i * 2], buffer[i * 2 + 1]); if (curSample > cAmplitude) { // Check amplitude cAmplitude = curSample; } } } else { // 8bit sample size for (int i = 0; i < buffer.length; i++) { if (buffer[i] > cAmplitude) { // Check amplitude cAmplitude = buffer[i]; } } } } catch (IOException e) { Log.e(ExtAudioRecorder.class.getName(), "Error occured in updateListener, recording is aborted"); // stop(); } } public void onMarkerReached(AudioRecord recorder) { // NOT USED } }; /** * Default constructor * <p/> * Instantiates a new recorder, in case of compressed recording the * parameters can be left as 0. In case of errors, no exception is thrown, * but the state is set to ERROR */ public ExtAudioRecorder(boolean uncompressed, int audioSource, int sampleRate, int channelConfig, int audioFormat) { try { rUncompressed = uncompressed; if (rUncompressed) { // RECORDING_UNCOMPRESSED if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) { bSamples = 16; } else { bSamples = 8; } if (channelConfig == AudioFormat.CHANNEL_CONFIGURATION_MONO) { nChannels = 1; } else { nChannels = 2; } aSource = audioSource; sRate = sampleRate; aFormat = audioFormat; framePeriod = sampleRate * TIMER_INTERVAL / 1000; bufferSize = framePeriod * 2 * bSamples * nChannels / 8; if (bufferSize < AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)) { // Check to make sure // buffer size is not // smaller than the // smallest allowed one bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat); // Set frame period and timer interval accordingly framePeriod = bufferSize / (2 * bSamples * nChannels / 8); Log.w(ExtAudioRecorder.class.getName(), "Increasing buffer size to " + Integer.toString(bufferSize)); } audioRecorder = new AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, bufferSize); if (audioRecorder.getState() != AudioRecord.STATE_INITIALIZED) throw new Exception("AudioRecord initialization failed"); audioRecorder.setRecordPositionUpdateListener(updateListener); audioRecorder.setPositionNotificationPeriod(framePeriod); } else { // RECORDING_COMPRESSED mediaRecorder = new MediaRecorder(); mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mediaRecorder .setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mediaRecorder .setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); } cAmplitude = 0; filePath = null; state = State.INITIALIZING; } catch (Exception e) { if (e.getMessage() != null) { Log.e(ExtAudioRecorder.class.getName(), e.getMessage()); } else { Log.e(ExtAudioRecorder.class.getName(), "Unknown error occured while initializing recording"); } state = State.ERROR; } } /** * Sets output file path, call directly after construction/reset. * */ public void setOutputFile(String argPath) { try { if (state == State.INITIALIZING) { filePath = argPath; if (!rUncompressed) { mediaRecorder.setOutputFile(filePath); } } } catch (Exception e) { if (e.getMessage() != null) { Log.e(ExtAudioRecorder.class.getName(), e.getMessage()); } else { Log.e(ExtAudioRecorder.class.getName(), "Unknown error occured while setting output path"); } state = State.ERROR; } } /** * Returns the largest amplitude sampled since the last call to this method. * * @return returns the largest amplitude since the last call, or 0 when not * in recording state. */ public int getMaxAmplitude() { if (state == State.RECORDING) { if (rUncompressed) { int result = cAmplitude; cAmplitude = 0; return result; } else { try { return mediaRecorder.getMaxAmplitude(); } catch (IllegalStateException e) { return 0; } } } else { return 0; } } /** * Prepares the recorder for recording, in case the recorder is not in the * INITIALIZING state and the file path was not set the recorder is set to * the ERROR state, which makes a reconstruction necessary. In case * uncompressed recording is toggled, the header of the wave file is * written. In case of an exception, the state is changed to ERROR */ public void prepare() { try { if (state == State.INITIALIZING) { if (rUncompressed) { if ((audioRecorder.getState() == AudioRecord.STATE_INITIALIZED) & (filePath != null)) { // write file header randomAccessWriter = new RandomAccessFile(filePath, "rw"); randomAccessWriter.setLength(0); // Set file length to // 0, to prevent // unexpected // behavior in case // the file already // existed randomAccessWriter.writeBytes("RIFF"); randomAccessWriter.writeInt(0); // Final file size not // known yet, write 0 randomAccessWriter.writeBytes("WAVE"); randomAccessWriter.writeBytes("fmt "); randomAccessWriter.writeInt(Integer.reverseBytes(16)); // Sub-chunk // size, // 16 // for // PCM randomAccessWriter.writeShort(Short .reverseBytes((short) 1)); // AudioFormat, 1 for // PCM randomAccessWriter.writeShort(Short .reverseBytes(nChannels));// Number of channels, // 1 for mono, 2 for // stereo randomAccessWriter .writeInt(Integer.reverseBytes(sRate)); // Sample // rate randomAccessWriter.writeInt(Integer.reverseBytes(sRate * bSamples * nChannels / 8)); // Byte rate, // SampleRate*NumberOfChannels*BitsPerSample/8 randomAccessWriter .writeShort(Short .reverseBytes((short) (nChannels * bSamples / 8))); // Block // align, // NumberOfChannels*BitsPerSample/8 randomAccessWriter.writeShort(Short .reverseBytes(bSamples)); // Bits per sample randomAccessWriter.writeBytes("data"); randomAccessWriter.writeInt(0); // Data chunk size not // known yet, write 0 buffer = new byte[framePeriod * bSamples / 8 * nChannels]; state = State.READY; } else { Log.e(ExtAudioRecorder.class.getName(), "prepare() method called on uninitialized recorder"); state = State.ERROR; } } else { mediaRecorder.prepare(); state = State.READY; } } else { Log.e(ExtAudioRecorder.class.getName(), "prepare() method called on illegal state"); release(); state = State.ERROR; } } catch (Exception e) { if (e.getMessage() != null) { Log.e(ExtAudioRecorder.class.getName(), e.getMessage()); } else { Log.e(ExtAudioRecorder.class.getName(), "Unknown error occured in prepare()"); } state = State.ERROR; } } /** * Releases the resources associated with this class, and removes the * unnecessary files, when necessary */ public void release() { if (state == State.RECORDING) { stop(); } else { if ((state == State.READY) & (rUncompressed)) { try { randomAccessWriter.close(); // Remove prepared file } catch (IOException e) { Log.e(ExtAudioRecorder.class.getName(), "I/O exception occured while closing output file"); } (new File(filePath)).delete(); } } if (rUncompressed) { if (audioRecorder != null) { audioRecorder.release(); } } else { if (mediaRecorder != null) { mediaRecorder.release(); } } } /** * Resets the recorder to the INITIALIZING state, as if it was just created. * In case the class was in RECORDING state, the recording is stopped. In * case of exceptions the class is set to the ERROR state. */ public void reset() { try { if (state != State.ERROR) { release(); filePath = null; // Reset file path cAmplitude = 0; // Reset amplitude if (rUncompressed) { audioRecorder = new AudioRecord(aSource, sRate, nChannels + 1, aFormat, bufferSize); } else { mediaRecorder = new MediaRecorder(); mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mediaRecorder .setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mediaRecorder .setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); } state = State.INITIALIZING; } } catch (Exception e) { Log.e(ExtAudioRecorder.class.getName(), e.getMessage()); state = State.ERROR; } } /** * Starts the recording, and sets the state to RECORDING. Call after * prepare(). */ public void start() { if (state == State.READY) { if (rUncompressed) { payloadSize = 0; audioRecorder.startRecording(); audioRecorder.read(buffer, 0, buffer.length); } else { mediaRecorder.start(); } state = State.RECORDING; } else { Log.e(ExtAudioRecorder.class.getName(), "start() called on illegal state"); state = State.ERROR; } } /** * Stops the recording, and sets the state to STOPPED. In case of further * usage, a reset is needed. Also finalizes the wave file in case of * uncompressed recording. */ public void stop() { if (state == State.RECORDING) { if (rUncompressed) { audioRecorder.stop(); try { randomAccessWriter.seek(4); // Write size to RIFF header randomAccessWriter.writeInt(Integer .reverseBytes(32 + payloadSize)); randomAccessWriter.seek(40); // Write size to Subchunk2Size // field randomAccessWriter.writeInt(Integer .reverseBytes(payloadSize)); randomAccessWriter.close(); } catch (IOException e) { Log.e(ExtAudioRecorder.class.getName(), "I/O exception occured while closing output file"); state = State.ERROR; } } else { try{ mediaRecorder.stop(); }catch(Exception e){ }finally { state = State.STOPPED; } } state = State.STOPPED; } else { Log.e(ExtAudioRecorder.class.getName(), "stop() called on illegal state"); state = State.ERROR; } } /* * * Converts a byte[2] to a short, in LITTLE_ENDIAN format */ private short getShort(byte argB1, byte argB2) { return (short) (argB1 | (argB2 << 8)); } /** * 录制wav格式文件 * */ public File recordChat(String savePath, String fileName) { File dir = new File(savePath); // 如果该目录没有存在,则新建目录 if (dir.list() == null) { dir.mkdirs(); } // 获取录音文件 File file = new File(savePath + fileName); // 设置输出文件 result.setOutputFile(savePath + fileName); result.prepare(); // 开始录音 result.start(); return file; } /** * 停止录音 * * mediaRecorder 待停止的录音机 * @return 返回 */ public void stopRecord() { if (result != null) { result.stop(); result.release(); result = null; } } public double getAmplitude() { if (result != null) return (result.getMaxAmplitude() / 2700.0); else return 0; } }
权限 习惯弄很多 省的麻烦
<!-- 授予录音权限 --> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-feature android:name="android.hardware.camera" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" /> <uses-permission android:name="android.permission.NFC" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.SET_WALLPAPER" /> <uses-permission android:name="android.permission.SUBSCRIBED_FEEDS_READ" /> <uses-permission android:name="android.permission.SUBSCRIBED_FEEDS_WRITE" /> <uses-permission android:name="android.permission.USE_CREDENTIALS" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />