《android多媒體api》之AudioRecord原始音訊pcm錄製api
阿新 • • 發佈:2019-01-30
《android多媒體api》系列是整合梳理android開發中經常用到的媒體相關api;多媒體開發主要內容有音訊、視訊錄製播放、攝像頭操作、錄製操作、流媒體、直播、推流、拉流等方面;最近幾年移動直播和視訊應用發展猶如雨後春筍一般直插雲霄,呃。。好吧這段比喻可以不用看了!!,反正行業興起肯定催生了很多多媒體相關應用開發程式設計師。那麼怎樣才能成為多媒體開發程式設計師,首先必須要熟練使用和了解android自帶的多媒體api,並且還要掌握pcm、yuv、rgb、h264、aac、flv、mpegts、mp4、udp、rtp、rtmp等等眾多檔案格式和流媒體協議等等。所以這裡整理android相關多媒體api,提供給想從事流媒體同學作為參照,同樣還是要鳴謝網路上那些具有分享精神大神們!!
基本概念:
- 視訊播放:demuxer(解複用)->分離出音訊流和視訊流->decoder(解碼)->播放原始資料(例如:pcm yuv)
- 視訊錄製:採集原始資料(例如:pcm yuv)->encoder(編碼)->muxer(封裝格式 例如:mp4 3gp)
- 流媒體協議:udp、rtp、rtmp、rtcp、rtsp等
- 音視訊封裝格式:mp4 、3gp、flv等
- 音視訊編碼格式:aac、amr、h264、h265等
- 原始音視訊資料格式:pcm 、yuv、rgb等
流程圖:
文章目錄:
AudioRecord是什麼?
AudioRecord是可以錄製原始音訊資料pcm的api,如果是一些音樂錄製,或者直播語音等都需要使用音訊資料前置處理,比如:降噪、多音訊合成、特效音效處理等等。那麼就需要獲取原始音訊資料後處理完畢後在編碼,因為編碼後的資料是不能夠處理降噪、特效等操作的。那麼就下來看看怎麼用AudioRecord來錄製原始音訊資料;下面做了一個demo,主要是錄製音訊後儲存到檔案中去。pcm錄音時候需要制定幾個重要引數,這幾個引數在以後播放的時候也要對應設定,要不然無法播放。錄製時候還需要設定錄製緩衝區大小,快取區越大,記憶體溢位風險越小。
pcm引數:
1、取樣率
2、聲道數
3、位寬
首先視訊音訊錄製是屬於使用者敏感資訊,所以使用之前一定要申請許可權:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
基於AudioRecord錄音功能:
xml佈局檔案:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<SurfaceView
android:id="@+id/surfaceView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:onClick="onClick"
android:id="@+id/start_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="開始"/>
<Button
android:onClick="onClick"
android:layout_marginLeft="80dp"
android:id="@+id/btnStop"
android:layout_width="80dip"
android:layout_height="wrap_content"
android:text="停止"/>
</LinearLayout>
</FrameLayout>
java程式碼:
package com.jared.helloffmpeg;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.media.*;
import android.media.AudioRecord;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Toast;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class RecordMP4Push extends Activity implements View.OnClickListener, SurfaceHolder.Callback {
private SurfaceView surfaceView;
private byte[] outBuf;
private boolean isStart=false;
private AudioRecord audioRecord;
private int bufferSize;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setContentView(R.layout.record_aac_and_pcm);
surfaceView=findViewById(R.id.surfaceView1);
surfaceView.getHolder().addCallback(this);
}
private void initAudioRecord() {
int sampleRateInHz = 48000;//取樣率
int channel= AudioFormat.CHANNEL_IN_STEREO;//聲道數
int audioFormat=AudioFormat.ENCODING_PCM_16BIT;//位寬
bufferSize = AudioRecord.getMinBufferSize(sampleRateInHz, channel, audioFormat);
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channel, audioFormat, bufferSize*4);
outBuf=new byte[bufferSize];
Log.i(getClass().getSimpleName(), "init record="+bufferSize);
}
@Override
public void onClick(View view) {
if (view.getId()==R.id.start_btn)
{
isStart=true;
initAudioRecord();
new Thread(new Runnable() {
@Override
public void run() {
startRecord();
}
}).start();
Toast.makeText(this, "開始錄製",Toast.LENGTH_SHORT).show();
}
if (view.getId()==R.id.btnStop)
{
isStart=false;
Toast.makeText(this, "停止錄製",Toast.LENGTH_SHORT).show();
}
}
private void startRecord() {
FileOutputStream fileOutputStream=null;
try {
audioRecord.startRecording();
fileOutputStream=new FileOutputStream(new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/outputs.pcm"));
while (isStart)
{
int len = audioRecord.read(outBuf, 0, bufferSize);
if (len == AudioRecord.ERROR_INVALID_OPERATION || len == AudioRecord.ERROR_BAD_VALUE) {
continue;
}
if (len != 0 && len != -1) {
fileOutputStream.write(outBuf, 0, len);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fileOutputStream.flush();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
audioRecord.stop();
audioRecord.release();
audioRecord=null;
}
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
}