• 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
  • 相关阅读:
    C++覆盖、重载、多态区别
    C++的模板与类属类
    虚函数表和虚基类表
    常用的设计模式
    计算机内存中的对齐和C++ 类的存储空间大小
    C++的多态
    前端框架你究竟选什么
    [zt]程序员的本质
    javascript
    Web开发人员应有的15本免费电子书
  • 原文地址:https://www.cnblogs.com/pengxinglove/p/5469448.html
  • Copyright © 2020-2023  润新知