android remote submix 录制系统内置的声音

Android 4.4中AudioRecord用例 - 录制系统内置声音

http://blog.csdn.net/jinzhuojun/article/details/33748031?utm_source=tuicool&utm_medium=referral

通过API 19新加的MediaRecorder.AudioSource.REMOTE_SUBMIX参数可以让系统App录制系统内置的声音,也就是扬声器的声音。下面是一个巨简单的例子来示例如何通过AudioRecord配合REMOTE_SUBMIX参数进行录制。

public class MainActivity extends Activity {   private static String TAG = "JZJ";       AudioRecord mRecord = null;   boolean mReqStop = false;   @Override protected void onCreate(Bundle savedInstanceState) {   super.onCreate(savedInstanceState);           setContentView(R.layout.activity_main);           init();           (new Thread() {   @Override public void run() {                   recordAndPlay();               }           }).start();       }   private final int kSampleRate = 44100;   private final int kChannelMode = AudioFormat.CHANNEL_IN_STEREO;   private final int kEncodeFormat = AudioFormat.ENCODING_PCM_16BIT;   private void init() {   int minBufferSize = AudioRecord.getMinBufferSize(kSampleRate, kChannelMode,                   kEncodeFormat);           mRecord = new AudioRecord( MediaRecorder.AudioSource.REMOTE_SUBMIX,                   kSampleRate, kChannelMode, kEncodeFormat, minBufferSize * 2);       }   private final int kFrameSize = 2048;   private String filePath = "/sdcard/voice.pcm";   private void recordAndPlay() {           FileOutputStream os = null;           mRecord.startRecording();   try {               os = new FileOutputStream(filePath);   byte[] buffer = new byte[kFrameSize];   int num = 0;   while (!mReqStop) {                   num = mRecord.read(buffer, 0, kFrameSize);                   Log.d(TAG, "buffer = " + buffer.toString() + ", num = " + num);                   os.write(buffer, 0, num);               }               Log.d(TAG, "exit loop");               os.close();           } catch (IOException e) {               e.printStackTrace();               Log.e(TAG, "Dump PCM to file failed");           }           mRecord.stop();           mRecord.release();           mRecord = null;           Log.d(TAG, "clean up");       }   public void stop(View view) {           mReqStop = true;           Button stopBtn = (Button) findViewById(R.id.stopBtn);           stopBtn.setText("Stopped");           stopBtn.setEnabled(false);       }   } 

Android 4.2 Wifi Display 之 Settings 源码分析(2)

http://blog.csdn.net/mznewfacer/article/details/8268930

frameworks/base/services/java/com/android/server/display/WifiDisplayController.java

接下来,我们将重点看一看updateConnection()函数,此函数是建立Wifidisplay连接,监听RTSP连接的核心实现函数。

private void updateConnection() {
       //在尝试连接到新设备时,需要通知系统这里已经与旧的设备断开连接
        if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice) {
            ...
            mRemoteDisplay.dispose();  //释放NativeRemoteDisplay资源停止监听
            mRemoteDisplay = null;   //监听返回对象置为空
            mRemoteDisplayInterface = null;   //监听端口置为空
            mRemoteDisplayConnected = false;  //连接标识为未连接
            mHandler.removeCallbacks(mRtspTimeout);//将挂起的mRtspTimeout线程从消息队列中移除

            setRemoteSubmixOn(false);   //关闭远程混音重建模式
            unadvertiseDisplay();  
        }
        if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) {
             ...
            unadvertiseDisplay();

            final WifiP2pDevice oldDevice = mConnectedDevice;
            mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() {
                @Override
                public void onSuccess() {
                    ...
                    next();
                }

                @Override
                public void onFailure(int reason) {
                   ...
                    next();
                }

                private void next() {
                    if (mConnectedDevice == oldDevice) {  //确保连接设备已经不是旧的设备否则递归调用该函数
                        mConnectedDevice = null;
                        updateConnection();
                    }
                }
            });
            return;
        }

        if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) {
            ...
            unadvertiseDisplay();
            mHandler.removeCallbacks(mConnectionTimeout);

            final WifiP2pDevice oldDevice = mConnectingDevice;
            mWifiP2pManager.cancelConnect(mWifiP2pChannel, new ActionListener() {  //在尝试连接到新设备之前,取消正在进行的p2p连接
                @Override
                public void onSuccess() {
                    ...
                    next();
                }

                @Override
                public void onFailure(int reason) {
                    ...
                    next();
                }

                private void next() {
                    if (mConnectingDevice == oldDevice) {
                        mConnectingDevice = null;
                        updateConnection();
                    }
                }
            });
            return;
        }
    //  如果想断开连接,则任务结束
        if (mDesiredDevice == null) {
            unadvertiseDisplay();
            return;
        }

        if (mConnectedDevice == null && mConnectingDevice == null) {
            Slog.i(TAG, "Connecting to Wifi display: " + mDesiredDevice.deviceName);
            mConnectingDevice = mDesiredDevice;
            WifiP2pConfig config = new WifiP2pConfig();
            config.deviceAddress = mConnectingDevice.deviceAddress;
            config.groupOwnerIntent = WifiP2pConfig.MIN_GROUP_OWNER_INTENT;

            WifiDisplay display = createWifiDisplay(mConnectingDevice);
            advertiseDisplay(display, null, 0, 0, 0);

            final WifiP2pDevice newDevice = mDesiredDevice;
            mWifiP2pManager.connect(mWifiP2pChannel, config, new ActionListener() {
      //以特定的配置信息开启P2P连接,如果当前设备不是P2P组的一部分,会建立P2P小组并发起连接请求;如果当前设备是现存P2P组的一部分,则加入该组的邀请会发送至该配对设备。

                @Override
                public void onSuccess() {
        //为了防止连接还没有建立成功,这里设定了等待处理函数,如果在定长时间内还没有接受到WIFI_P2P_CONNECTION_CHANGED_ACTION广播,则按照handleConnectionFailure(true)处理。
                    Slog.i(TAG, "Initiated connection to Wifi display: " + newDevice.deviceName);
                    mHandler.postDelayed(mConnectionTimeout, CONNECTION_TIMEOUT_SECONDS * 1000);
                }

                @Override
                public void onFailure(int reason) {
                    if (mConnectingDevice == newDevice) {
                        Slog.i(TAG, "Failed to initiate connection to Wifi display: "
                                + newDevice.deviceName + ", reason=" + reason);
                        mConnectingDevice = null;
                        handleConnectionFailure(false);
                    }
                }
            });
            return;
        }
        // 根据连接的网络地址和端口号监听Rtsp流连接
        if (mConnectedDevice != null && mRemoteDisplay == null) {
            Inet4Address addr = getInterfaceAddress(mConnectedDeviceGroupInfo);
            if (addr == null) {
                Slog.i(TAG, "Failed to get local interface address for communicating "
                        + "with Wifi display: " + mConnectedDevice.deviceName);
                handleConnectionFailure(false);
                return; // done
            }

           setRemoteSubmixOn(true);

            final WifiP2pDevice oldDevice = mConnectedDevice;
            final int port = getPortNumber(mConnectedDevice);
            final String iface = addr.getHostAddress() + ":" + port;
            mRemoteDisplayInterface = iface;

            Slog.i(TAG, "Listening for RTSP connection on " + iface
                    + " from Wifi display: " + mConnectedDevice.deviceName);

            mRemoteDisplay = RemoteDisplay.listen(iface, new RemoteDisplay.Listener() {
//开始监听连接上的接口
                @Override
                public void onDisplayConnected(Surface surface,
                        int width, int height, int flags) {
                    if (mConnectedDevice == oldDevice && !mRemoteDisplayConnected) {
                        Slog.i(TAG, "Opened RTSP connection with Wifi display: "
                                + mConnectedDevice.deviceName);
                        mRemoteDisplayConnected = true;
                        mHandler.removeCallbacks(mRtspTimeout);

                        final WifiDisplay display = createWifiDisplay(mConnectedDevice);
                        advertiseDisplay(display, surface, width, height, flags);
                    }
                }

                @Override
                public void onDisplayDisconnected() {
                    if (mConnectedDevice == oldDevice) {
                        Slog.i(TAG, "Closed RTSP connection with Wifi display: "
                                + mConnectedDevice.deviceName);
                        mHandler.removeCallbacks(mRtspTimeout);
                        disconnect();
                    }
                }

                @Override
                public void onDisplayError(int error) {
                    if (mConnectedDevice == oldDevice) {
                        Slog.i(TAG, "Lost RTSP connection with Wifi display due to error "
                                + error + ": " + mConnectedDevice.deviceName);
                        mHandler.removeCallbacks(mRtspTimeout);
                        handleConnectionFailure(false);
                    }
                }
            }, mHandler);

            mHandler.postDelayed(mRtspTimeout, RTSP_TIMEOUT_SECONDS * 1000);
        }
    }

 

How to record submix on android device in realtime? (if it's possible)

http://stackoverflow.com/questions/26363669/how-to-record-submix-on-android-device-in-realtime-if-its-possible

  • I'm trying to record a submix from device for monitoring and I have some issues with that. I'm using AudioRecord instance and audio source is MediaRecorder.AudioSource.REMOTE_SUBMIX. I found in internet information that I must use android.permission.CAPTURE_AUDIO_OUTPUT permission but it's said "not for use by third-party apps". I tried to launch my application but it throws an exception:

  •  

 

 

http://blog.csdn.net/michaelcao1980/article/details/42290747

audio_policy.conf文件分析

不同的Android产品在音频的设计上通常是存在差异的,而这些差异可以同过Audio的配置文件audio_policy.conf来获得。在Android系统中音频配置文件存放路径有两处,存放地址可以从AudioPolicyManagerBase.cpp文件中知道:

http://blog.csdn.net/michaelcao1980/article/details/42426169

 

android4.3之系统SetSpeakerphoneOn实现的Audio Output Path切换

http://blog.csdn.net/michaelcao1980/article/details/42290747

在通话过程中要切换Audio Output Path从蓝牙耳机到Speaker,但是却第一次却切换到了earpiece,再切换一次

才切到Speaker,我就根据这个bug的分析,来熟悉下音频通道的切换过程;

首先还是需要看下google对于Android audio系统架构分析;

http://source.android.com/devices/audio.html

android的HAL(Hardware Abstraction Layer)是连接上层的audio-specific framework APIs和底层的audio驱动和硬体的关键;

下面这张图展示了这样的架构以及每层的代码分布:图片说明文字

一、Application framework

在Application framework层级是app层的code,是通过android.media提供的API来与audio硬件进行交互动作,这部分的代码是通过

audio JNI来调用native代码从而达到影响硬件的效果;

android_platform_frameworks_base/media/java/android/media/IAudioService.aidl

oneway void setRemoteSubmixOn(boolean on, int address);

 

Android Capture Android System Audio

http://www.cnblogs.com/fansen/p/5127931.html

转载于:https://www.cnblogs.com/pengxinglove/p/5469448.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值