您的位置:首页 > 其它

项目中遇到的录音、6.0权限、隐式服务转显式的问题

2017-01-10 10:10 267 查看
文章中用不到的代码都用斜杠注销了,可以忽略

录音步骤:先写个AudioRecorder

public class AudioRecorder {
String TAG = "AudioRecorder";
private static final int SAMPLE_RATE = 44100; //采样率(CD音质)
private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO; //音频通道(单声道)
private static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT; //音频格式
private static final int AUDIO_SOURCE = MediaRecorder.AudioSource.MIC;  //音频源(麦克风)
private static boolean is_recording = false;
public static File recordFile;
private AudioEncoder audioEncoder;
private static AudioRecorder instance;
private RecorderTask recorderTask = new RecorderTask();

private AudioRecorder(File file) {
recordFile = file;
}

public static AudioRecorder getInstance(File file) {
return new AudioRecorder(file);

}

public void setAudioEncoder(AudioEncoder audioEncoder) {
this.audioEncoder = audioEncoder;
}

/*
开始录音
*/
public void startAudioRecording() {

new Thread(recorderTask).start();
}

/*
停止录音
*/
public void stopAudioRecording() {
is_recording = false;
}

class RecorderTask implements Runnable {
int bufferReadResult = 0;
public int samples_per_frame = 2048;

@Override
public void run() {
long audioPresentationTimeNs; //音频时间戳 pts
//获取最小缓冲区大小
int bufferSizeInBytes = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT);
AudioRecord audioRecord = new AudioRecord(
AUDIO_SOURCE,   //音频源
SAMPLE_RATE,    //采样率
CHANNEL_CONFIG,  //音频通道
AUDIO_FORMAT,    //音频格式
bufferSizeInBytes //缓冲区
);
audioRecord.startRecording();
is_recording = true;

Log.v(TAG, "recordFile.getAbsolutepath---" + recordFile.getAbsolutePath());

while (is_recording) {
byte[] buffer = new byte[samples_per_frame];
audioPresentationTimeNs = System.nanoTime();
//从缓冲区中读取数据,存入到buffer字节数组数组中
bufferReadResult = audioRecord.read(buffer, 0, samples_per_frame);
//判断是否读取成功
if (bufferReadResult == AudioRecord.ERROR_BAD_VALUE || bufferReadResult == AudioRecord.ERROR_INVALID_OPERATION)
Log.e(TAG, "Read error");
if (audioRecord != null) {
audioEncoder.offerAudioEncoder(buffer, audioPresentationTimeNs);
}

}
if (audioRecord != null) {
audioRecord.setRecordPositionUpdateListener(null);
audioRecord.stop();
audioRecord.release();
audioRecord = null;
}

}
}
}
然后是编码,写个AudioEncoder:

public class AudioEncoder {
String TAG = "AudioEncoder";
//编码
private MediaCodec mAudioCodec;     //音频编解码器
private MediaFormat mAudioFormat;
private static final String AUDIO_MIME_TYPE = "audio/mp4a-latm"; //音频类型
private static final int SAMPLE_RATE = 44100; //采样率(CD音质)
private TrackIndex mAudioTrackIndex = new TrackIndex();
private MediaMuxer mMediaMuxer;     //混合器
private boolean mMuxerStart = false; //混合器启动的标志
private MediaCodec.BufferInfo mAudioBufferInfo;
private static long audioBytesReceived = 0;        //接收到的音频数据 用来设置录音起始时间的
private long audioStartTime;
private String recordFile;
private boolean eosReceived = false;  //终止录音的标志
private ExecutorService encodingService = Executors.newSingleThreadExecutor(); //序列化线程任务

//枚举值 一个用来标志编码 一个标志编码完成
enum EncoderTaskType {
ENCODE_FRAME, FINALIZE_ENCODER
}

;

public AudioEncoder() {
recordFile = AudioRecorder.recordFile.getAbsolutePath();
prepareEncoder();
}

class TrackIndex {
int index = 0;
}

public void prepareEncoder() {
eosReceived = false;
audioBytesReceived = 0;
mAudioBufferInfo = new MediaCodec.BufferInfo();
mAudioFormat = new MediaFormat();
mAudioFormat.setString(MediaFormat.KEY_MIME, AUDIO_MIME_TYPE);
mAudioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
mAudioFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, SAMPLE_RATE);
mAudioFormat.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
mAudioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
mAudioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 16384);
try {
mAudioCodec = MediaCodec.createEncoderByType(AUDIO_MIME_TYPE);
mAudioCodec.configure(mAudioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mAudioCodec.start();
mMediaMuxer = new MediaMuxer(recordFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
} catch (IOException e) {
e.printStackTrace();
}

}

//此方法 由AudioRecorder任务调用 开启编码任务
public void offerAudioEncoder(byte[] input, long presentationTimeStampNs) {
if (!encodingService.isShutdown()) {
//           Log.d(TAG,"encodingService--submit");
encodingService.submit(new AudioEncodeTask(this, input, presentationTimeStampNs));
}

}

//发送音频数据和时间进行编码
public void _offerAudioEncoder(byte[] input, long pts) {
if (audioBytesReceived == 0) {
audioStartTime = pts;
}
audioBytesReceived += input.length;
drainEncoder(mAudioCodec, mAudioBufferInfo, mAudioTrackIndex, false);
try {
ByteBuffer[] inputBuffers = mAudioCodec.getInputBuffers();
int inputBufferIndex = mAudioCodec.dequeueInputBuffer(-1);
//        Log.d(TAG,"inputBufferIndex--"+inputBufferIndex);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(input);

//录音时长
long presentationTimeUs = (pts - audioStartTime) / 1000;
Log.d("hsk", "presentationTimeUs--" + presentationTimeUs);
if (eosReceived) {
mAudioCodec.queueInputBuffer(inputBufferIndex, 0, input.length, presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
closeEncoder(mAudioCodec, mAudioBufferInfo, mAudioTrackIndex);
closeMuxer();
encodingService.shutdown();

} else {
mAudioCodec.queueInputBuffer(inputBufferIndex, 0, input.length, presentationTimeUs, 0);
}
}

} catch (Throwable t) {
Log.e(TAG, "_offerAudioEncoder exception");
}

}

public void drainEncoder(MediaCodec encoder, MediaCodec.BufferInfo bufferInfo, TrackIndex trackIndex, boolean endOfStream) {
final int TIMEOUT_USEC = 100;
ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers();
while (true) {
int encoderIndex = encoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
Log.d("hsk", "encoderIndex---" + encoderIndex);
if (encoderIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
//没有可进行混合的输出流数据 但还没有结束录音 此时退出循环

4000
Log.d(TAG, "info_try_again_later");
if (!endOfStream)
break;
else
Log.d(TAG, "no output available, spinning to await EOS");
} else if (encoderIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
//只会在第一次接收数据前 调用一次
if (mMuxerStart)
throw new RuntimeException("format 在muxer启动后发生了改变");
MediaFormat newFormat = encoder.getOutputFormat();
trackIndex.index = mMediaMuxer.addTrack(newFormat);
mMediaMuxer.start();
mMuxerStart = true;
} else if (encoderIndex < 0) {
Log.w(TAG, "encoderIndex 非法" + encoderIndex);
} else {
ByteBuffer encodeData = encoderOutputBuffers[encoderIndex];
if (encodeData == null) {
throw new RuntimeException("编码数据为空");
}
if (bufferInfo.size != 0) {
if (!mMuxerStart) {
throw new RuntimeException("混合器未开启");
}
encodeData.position(bufferInfo.offset);
encodeData.limit(bufferInfo.offset + bufferInfo.size);
mMediaMuxer.writeSampleData(trackIndex.index, encodeData, bufferInfo);
}

encoder.releaseOutputBuffer(encoderIndex, false);
//退出循环
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
break;
}

}
}

}

/**
* 关闭编码
*
* @param encoder
* @param bufferInfo
*/
public void closeEncoder(MediaCodec encoder, MediaCodec.BufferInfo bufferInfo, TrackIndex trackIndex) {
drainEncoder(encoder, bufferInfo, trackIndex, true);
encoder.stop();
encoder.release();
encoder = null;

}

/**
* 关闭混合器
*/
public void closeMuxer() {
mMediaMuxer.stop();
mMediaMuxer.release();
mMediaMuxer = null;
mMuxerStart = false;
}

//发送终止编码信息
public void stop() {
if (!encodingService.isShutdown()) {
encodingService.submit(new AudioEncodeTask(this, EncoderTaskType.FINALIZE_ENCODER));
}
}

//终止编码
public void _stop() {
eosReceived = true;
Log.d(TAG, "停止编码");
}

/**
* 音频编码任务
*/
class AudioEncodeTask implements Runnable {
private static final String TAG = "AudioEncoderTask";
private boolean is_initialized = false;
private AudioEncoder encoder;
private byte[] audio_data;
long pts;
private EncoderTaskType type;

//进行编码任务时 调用此构造方法
public AudioEncodeTask(AudioEncoder encoder, byte[] audio_data, long pts) {
this.encoder = encoder;
this.audio_data = audio_data;
this.pts = pts;
is_initialized = true;
this.type = EncoderTaskType.ENCODE_FRAME;
//这里是有数据的
//            Log.d(TAG,"AudioData--"+audio_data);
//            Log.d(TAG,"pts--"+pts);
}

//当要停止编码任务时 调用此构造方法
public AudioEncodeTask(AudioEncoder encoder, EncoderTaskType type) {
this.type = type;

if (type == EncoderTaskType.FINALIZE_ENCODER) {
this.encoder = encoder;
is_initialized = true;
}
Log.d(TAG, "完成...");

}

////编码
private void encodeFrame() {
Log.d(TAG, "audio_data---encoder--" + audio_data + " " + encoder);
if (audio_data != null && encoder != null) {
encoder._offerAudioEncoder(audio_data, pts);
audio_data = null;
}

}

//终止编码
private void finalizeEncoder() {
encoder._stop();
}

@Override
public void run() {
Log.d(TAG, "is_initialized--" + is_initialized);
if (is_initialized) {
switch (type) {
case ENCODE_FRAME:
//进行编码
encodeFrame();
break;
case FINALIZE_ENCODER:
//完成编码
finalizeEncoder();
break;
}
is_initialized = false;
} else {
//打印错误日志
Log.e(TAG, "AudioEncoderTask is not initiallized");
}
}
}
}

播放录音用的是绑定服务,而且是隐式调用,但是在6.0手机上一直闪退,低版本手机没有问题,查了下才知道android5.0以上服务必须显式调用。然后写个ServieChangetoExplicit类可以把隐式变为显式:
public class ServieChangetoExplicit {

/**
* 把隐式Service变为显式
* @param context
* @param implicitIntent
* @return
*/
public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
}
然后是BindService代码如下:
public class BindService extends Service {
private MyBinder binder = new MyBinder();
private MediaPlayer mediaPlayer = null;

public BindService() {

}
public MediaPlayer getMediaPlayer(){
return mediaPlayer;
}
/**
* 实现 Service的抽象方法onBind,并返回一个实现 IBinder接口的对象
*/

@Override
public IBinder onBind(Intent intent) {
return binder;
}
/**
*1、定义内部类MyBinder继承Binder实现IBinder接口,并提供方法返回Service实例。
*/
public class MyBinder extends Binder {
/**
* 获取 Service实例
* @return
*/
public BindService getService(){
return BindService.this;
}
}

public void play(String strfile){
if(mediaPlayer==null){
mediaPlayer=new MediaPlayer();
try {
mediaPlayer.setDataSource(strfile);
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
//            mediaPlayer=MediaPlayer.create(this,R.raw.pyghlkn);
}
if(mediaPlayer!=null&&!mediaPlayer.isPlaying()){
mediaPlayer.start();
}
}
public void stop(){
if(mediaPlayer!=null&&mediaPlayer.isPlaying()){
mediaPlayer.stop();
try {
//在调用stop后如果需要再次通过start进行播放,需要之前调用prepare函数
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void pause(){
if(mediaPlayer!=null&&mediaPlayer.isPlaying()){
mediaPlayer.pause();
}
}

@Override
public void onDestroy() {
super.onDestroy();
if(mediaPlayer!=null){
mediaPlayer.stop();
mediaPlayer.release();
}
}
}

然后在要使用到录音和播放功能的Activity中的代码:

private AudioRecorder audioRecorder;
private AudioEncoder audioEncoder;
private File file=null;

private BindService b_service;
private long recordstarttime=0;
private long recordendtime=0;
private long recordtime=0;

private static final int MY_PERMISSIONS_REQUEST_RECORD_AUDIO = 1;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hidden_danger_upload);
File path = new File(Environment.getExternalStorageDirectory(), "AudioRecord");
path.mkdirs();
System.out.print("path" + path);

initView();
initListener();
connBind();

}
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_upload:
break;
case R.id.iv_recordplayback:
if(file!=null&&b_service!=null){//文章中用不到的代码注释掉了
//iv_playAnimation.setBackgroundResource(R.drawable.playrecord_animation);
//ad_play=(AnimationDrawable)iv_playAnimation.getBackground();
//iv_playAnimation.setVisibility(View.VISIBLE);
//iv_recordplayback.setVisibility(View.INVISIBLE);
//ad_play.start();//播放录音动画开始
//System.out.println("--->>>"+file.toString());
b_service.play(file.toString());//播放录音
b_service.getMediaPlayer().setOnCompletionListener(new MediaPlayer.OnCompletionListener() {//录音播放结束
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
// if (ad_play.isRunning()) {
//  ad_play.stop();
unbindService(conn);
connBind();
//iv_recordplayback.setVisibility(View.VISIBLE);
//iv_playAnimation.setVisibility(View.INVISIBLE);

//}
}
});
}

break;
}
}
之前一直是录音后再录音播放的一直是第一次录音文件,但是file的路径都变了 啊,就各种试,然后发现播放结束后先解绑再绑定就可以了

上面是播放录音的时候的代码,但是6.0获取权限和之前也不一样,然后就在录音按钮的按下状态下判断:

/**
* 录音按钮点击事件
*/
btn_record.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN://按下
if (ContextCompat.checkSelfPermission(HiddenDangerUploadActivity.this,
Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(HiddenDangerUploadActivity.this,
new String[]{Manifest.permission.RECORD_AUDIO},
MY_PERMISSIONS_REQUEST_RECORD_AUDIO);
}else{
StartRecord();//开始录音方法
}
break;
case MotionEvent.ACTION_UP://抬起
StopRecord();//停止录音方法
break;
}
return false;
}
});

/**
* 开始录音方法
*/
private void StartRecord(){
//iv_animation.setBackgroundResource(R.drawable.record_animation);
//ad_record = (AnimationDrawable) iv_animation.getBackground();
//iv_animation.setVisibility(View.VISIBLE);
//ad_record.start();//开始录音动画
btn_record.setText("松开保存");
file = new File(Environment.getExternalStorageDirectory() + "/AudioRecord/", System.currentTimeMillis() + ".m4a");
audioRecorder = AudioRecorder.getInstance(file);
audioEncoder = new AudioEncoder();
audioRecorder.setAudioEncoder(audioEncoder);
recordstarttime=System.currentTimeMillis();
audioRecorder.startAudioRecording();//开始录音
}

/**
* 结束录音方法
*/
private void StopRecord(){
//if(ad_record!=null&&ad_record.isRunning()){
//ad_record.stop();//结束录音动画
audioRecorder.stopAudioRecording();//停止录音
recordendtime=System.currentTimeMillis();
audioEncoder.stop();//停止编码
recordtime=recordendtime-recordstarttime;
if(recordtime>0){
tv_recordtime.setText(recordtime/1000+"'");
tv_recordtime.setVisibility(View.VISIBLE);
iv_recordplayback.setVisibility(View.VISIBLE);
iv_recordplayback.setOnClickListener(HiddenDangerUploadActivity.this);
}
// }
//iv_animation.setVisibility(View.INVISIBLE);
//btn_record.setText("按住录音");

}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
{

if (requestCode == MY_PERMISSIONS_REQUEST_RECORD_AUDIO) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// StartRecord();//注意!!!网上的6.0权限判断方法中写的简单例子基本都是在onCreate中写的,但是现在是在按钮的触摸事件中,所以这里不要再调用要执行的方法
} else {
// Permission Denied
Toast.makeText(HiddenDangerUploadActivity.this, "没有录音权限,请在手机设置中添加权限", Toast.LENGTH_SHORT).show();
}
return;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}


下面的代码是bindService方面的:

public void connBind(){
//隐式调用
Intent intent=new Intent();
intent.setAction("com.xxxxxxxxxxxxxxxxxxxxxx.BindService");
Intent eintent = new Inte
9593
nt(ServieChangetoExplicit.getExplicitIntent(HiddenDangerUploadActivity.this,intent));

/**
* 注:标志位Context.BIND_AUTO_CREATE,
* 说明:表示收到绑定请求的时候,如果服务尚未创建,则即刻创建,
* 在系统内存不足需要先摧毁优先级组件来释放内存,
* 且只有驻留该服务的进程成为被摧毁对象时,服务才被摧毁
*/
bindService(eintent,conn, Context.BIND_AUTO_CREATE);

}
/**
* Activity里定义ServiceConnection接口对象并实现ServiceConnection接口,
* 重写onServiceConnected方法进行Service连接
* 重写onServiceDisconnected方法进行Service销毁
*/
private ServiceConnection conn=new ServiceConnection() {
//连接建立成功
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
b_service=((BindService.MyBinder)((Binder)service)).getService();
}
//连接断开
@Override
public void onServiceDisconnected(ComponentName name) {
b_service=null;
}
};
@Override
protected void onDestroy() {
super.onDestroy();
if(conn!=null){
unbindService(conn);
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  录音
相关文章推荐