android 蓝牙sco开发

来源:互联网 发布:查找html源码 编辑:程序博客网 时间:2024/06/18 14:30

近段时间在做bluetooth双向通信,坑的不轻,各种问题不断,感觉这坑都填不完的一样。把这段时间的东西写下来,给需要的小伙伴参考下,能少坑是一点

public class Main2Activity extends AppCompatActivity {    AudioManager mAm;    InavrSR inavrSR;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main2);        mAm = (AudioManager) getSystemService(Context.AUDIO_SERVICE);        mAm.setMode(AudioManager.MODE_IN_COMMUNICATION);        initSCO();        initBlueToothHeadset();    }    private BluetoothHeadset bluetoothHeadset;    private BluetoothDevice bluetoothDevice;    BluetoothProfile.ServiceListener blueHeadsetListener = new BluetoothProfile.ServiceListener() {        @Override        public void onServiceDisconnected(int profile) {            Log.i("blueHeadsetListener>>", "onServiceDisconnected:" + profile);            if (profile == BluetoothProfile.HEADSET) {                if (bluetoothHeadset != null)                    bluetoothHeadset.stopVoiceRecognition(bluetoothDevice);                mAm.stopBluetoothSco();                mAm.setBluetoothScoOn(false);                bluetoothHeadset = null;            }//            initBlueToothHeadset();        }        @Override        public void onServiceConnected(int profile, BluetoothProfile proxy) {            Log.i("blueHeadsetListener", "onServiceConnected:" + profile);            if (profile == BluetoothProfile.HEADSET) {                bluetoothHeadset = (BluetoothHeadset) proxy;                scoEnable();                Log.i("sco", mAm.isBluetoothA2dpOn() + "," + mAm.isBluetoothScoOn());                if (bluetoothHeadset.getConnectedDevices().size() > 0) {                    bluetoothDevice = bluetoothHeadset.getConnectedDevices().get(0);                    Log.i("Main2Activity:", bluetoothHeadset.startVoiceRecognition(bluetoothDevice) + "");                }            }        }    };    private void initBlueToothHeadset() {        BluetoothAdapter adapter;        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {//android4.3之前直接用BluetoothAdapter.getDefaultAdapter()就能得到BluetoothAdapter            adapter = BluetoothAdapter.getDefaultAdapter();        } else {            BluetoothManager bm = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);            adapter = bm.getAdapter();        }        adapter.getProfileProxy(this, blueHeadsetListener, BluetoothProfile.HEADSET);    }    public void record() {        final Player player = new Player();        Record re = new Record(new IReward() {            @Override            public void onReward(byte[] bytes, int i, int i1) {                player.play(bytes);            }            @Override            public void onError(RewardException e) {                inavrSR.getWebsocketManager().newSocketConnection();            }        });        re.startRecord();    }    /* Broadcast receiver for the SCO State broadcast intent.*/    private final BroadcastReceiver mSCOHeadsetAudioState = new BroadcastReceiver() {        public void onReceive(Context context, Intent intent) {            if (intent.getAction().equals("android.bluetooth.device.action.ACL_CONNECTED")) {                initBlueToothHeadset();            } else {                //if(DEBUG)                int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);                Log.e("SCO", " mSCOHeadsetAudioState--->onReceive:" + state);                if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {                    Log.i("SCO", "SCO_AUDIO_STATE_CONNECTED");             record();                    unregisterReceiver(mSCOHeadsetAudioState);                } else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {                    Log.i("SCO  ", "SCO_AUDIO_STATE_DISCONNECTED");                }            }        }    };    private void initSCO() {        IntentFilter newintent = new IntentFilter();        newintent.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);        newintent.addAction("android.bluetooth.device.action.ACL_CONNECTED");        registerReceiver(mSCOHeadsetAudioState, newintent);    }    private void scoEnable() {        if (mAm.isBluetoothScoOn()) {            mAm.stopBluetoothSco();            mAm.setBluetoothScoOn(false);        }        mAm.setBluetoothScoOn(true);        mAm.startBluetoothSco();    }}

/** * Created by kakahsh on 2017/2/24. */public class Record extends ACEMCanceler implements IRecordAction {    private static final int SAMPLE_RATE_IN_HZ = 16000;    private static final int CHANNEL = AudioFormat.CHANNEL_IN_MONO;    //// 2017/8/30 关于640*2的解释,理论上只要加640就可以了,但会提示RecordThread: buffer overflow    private static final int BUFFER_SIZE = (AudioRecord.getMinBufferSize(SAMPLE_RATE_IN_HZ,            CHANNEL, AudioFormat.ENCODING_PCM_16BIT) / 640) * 640 + 640 * 2;    private AudioRecord mAudioRecord;    private boolean isVoiceRunning;    private Thread recordThread;    private IReward reward;    public Record(IReward iReward) {        this.reward = iReward;        mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION,                SAMPLE_RATE_IN_HZ, CHANNEL,                AudioFormat.ENCODING_PCM_16BIT, BUFFER_SIZE);        if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {            if (reward != null)                reward.onError(new RewardException("AudioRecord初始化失败,请确认已经取得录音权限"));        }        int audioSessionId = mAudioRecord.getAudioSessionId();//        initAEC(audioSessionId);    }    private byte[] buffer = new byte[BUFFER_SIZE];    private void getNoiseLevel() {        android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);        while (isVoiceRunning) {            //r是实际读取的数据长度,一般而言r会小于buffersize            int r = mAudioRecord.read(buffer, 0, BUFFER_SIZE);//            int calculateVolume = calculateVolume(buffer, 16);            reward.onReward(buffer, 0, r);        }    }    @Override    public void startRecord() {        mAudioRecord.startRecording();        recordThread = new Thread(new Runnable() {            @Override            public void run() {                getNoiseLevel();            }        });        recordThread.start();        isVoiceRunning = true;    }    @Override    public void stopRecord() {        isVoiceRunning = false;        if (recordThread != null) {            recordThread.interrupt();        }        if (mAudioRecord != null) {            mAudioRecord.stop();        }    }    @Override    public void realese() {        if (mAudioRecord != null) {            stopRecord();            mAudioRecord.release();        }        reward = null;    }    @Override    public boolean isInRecord() {        return isVoiceRunning;    }}

/** * Created by kakahsh on 2017/2/28. */public interface IReward {    void onReward(byte[] bytes, int start, int lenght);    void onError(RewardException e);}

/** * Created by kqw on 2016/8/26. * 播放音乐的线程 */public class Player extends Thread implements IAudioPlayer {    // 采样率    private final int mSampleRateInHz = 16000;    // 单声道    private final int mChannelConfig = AudioFormat.CHANNEL_OUT_MONO;    // 双声道(立体声)    // private int mChannelConfig = AudioFormat.CHANNEL_OUT_STEREO;    private static final String TAG = "PlayThread";    private AudioTrack mAudioTrack;    private byte[] data = new byte[640];    private int bufferSize;    private LinkedBlockingDeque<Object> dataQueue = new LinkedBlockingDeque<>();    private AudioDataInOut audioDataInOut;    private MessageWrapper messageWrapper;    // 互斥信号量    private Semaphore semaphore = new Semaphore(1);    public Player() {        bufferSize = AudioTrack.getMinBufferSize(mSampleRateInHz, mChannelConfig, AudioFormat.ENCODING_PCM_16BIT) / 640 * 640 + 640;        initTrack();        try {            // 默认需要抢占一个信号量。防止播放进程执行            semaphore.acquire();        } catch (InterruptedException e) {            e.printStackTrace();        }        start();    }    public Player(AudioDataInOut audioDataInOut) {        this();        this.audioDataInOut = audioDataInOut;    }    public Player(AudioDataInOut audioDataInOut, MessageWrapper messageWrapper) {        this();        this.audioDataInOut = audioDataInOut;        this.messageWrapper = messageWrapper;    }    private void initTrack() {        this.mAudioTrack = new AudioTrack(                AudioManager.STREAM_MUSIC,                mSampleRateInHz,                mChannelConfig,                AudioFormat.ENCODING_PCM_16BIT,                bufferSize,                AudioTrack.MODE_STREAM);//, ACEMCanceler.getInstance().getAudioSessionId()    }    @Override    public void run() {        super.run();        while (true) {            if (dataQueue.size() > 0) {                Object o = dataQueue.pollFirst();                if (o instanceof MessageEntry) {                    if (messageWrapper != null) {                        messageWrapper.execute((MessageEntry) o);                    }                } else {                    data = (byte[]) o;                    if (audioDataInOut != null)                        audioDataInOut.onInput(data, data.length);                    if (this.mAudioTrack != null)                        this.mAudioTrack.write(data, 0, data.length);                }            } else {                try {                    semaphore.acquire();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }    @Override    public void play() {        if (mAudioTrack == null)            initTrack();        if (mAudioTrack != null && mAudioTrack.getState() != AudioTrack.PLAYSTATE_PLAYING)            mAudioTrack.play();    }    @Override    public void play(byte[] bytes, int length) {        if (!isPlaying())            play();        pushToQueue(bytes);    }    public void play(Object o) {        if (!isPlaying())            play();        pushToQueue(o);    }    private synchronized void pushToQueue(Object o) {        try {            dataQueue.putLast(o);            semaphore.release();        } catch (InterruptedException e) {            e.printStackTrace();        }    }    @Override    public void releasePlayer() {        if (null != mAudioTrack) {            mAudioTrack.stop();            mAudioTrack.release();            mAudioTrack = null;        }        dataQueue.clear();        semaphore.release();    }    @Override    public void stopPlayer() {        if (mAudioTrack != null)            mAudioTrack.stop();    }    @Override    public boolean isPlaying() {        return mAudioTrack != null && mAudioTrack.getState() == AudioTrack.PLAYSTATE_PLAYING;    }    public void clearQueue() {        stopPlayer();        dataQueue.clear();    }    public interface AudioDataInOut {        void onInput(byte[] input, int length);    }}
  • 有些要注意的地方
    • mAm.setMode(AudioManager.MODE_IN_COMMUNICATION);,我在参考教程的时候,设置都是MODE_IN_CALL,但我在用的时候是不行的,不知道是不我的使用的问题。
    • 流程是要先获取bluetoothHeadset的对象,再通过bluetoothHeadset来发送连接成功的广播,接到广播打开sco,接到sco状态变更后再开始录音。
    • 一定要记得在接到sco连接成功后调用unregisterReceiver(mSCOHeadsetAudioState);关闭广播接收器,因为可能会有多条广播过来,可能出现在异常。
原创粉丝点击