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);
            }
        }

    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);

    时间: 2024-10-12 23:21:01

    android remote submix 录制系统内置的声音的相关文章

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

    通过API 19新加的MediaRecorder.AudioSource.REMOTE_SUBMIX参数可以让系统App录制系统内置的声音,也就是扬声器的声音.下面是一个巨简单的例子来示例如何通过AudioRecord配合REMOTE_SUBMIX参数进行录制. 1. 编译apk MainActivity.java: package com.example.audiotest; import java.io.FileOutputStream; import java.io.IOException

    D90 录制系统内置声音的log 文件

    [相关文件] W/MediaMetadataRetriever(  522): MediaMetadataRetriever server died! I/ServiceManager(  106): service 'media.audio_flinger' died I/ServiceManager(  106): service 'media.player' died I/ServiceManager(  106): service 'media.camera' died I/Servic

    android 4.2上面实现录制系统内置声音remote submix

    1. android4.2当中wifi display已经使用了此功能.只是android4.2没有像4.4以上那样封装的那么好. android4.2有相应的接口,只是将其 @hide不像应用开发者使用 2.基本流程 mAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); mAudioManager.setRemoteSubmixOn(true, 0); 目标是将音频驱动切换到加载了remotesubmix模

    Android 使用意图录制视频

    本文章主要将通过意图触发内置的Camera应用程序来录制视频. 源代码: 布局文件: activity_main: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" androi

    深度分析:Android4.3下MMS发送到附件为音频文件(音频为系统内置音频)的彩信给自己,添加音频-发送彩信-接收彩信-下载音频附件-预览-播放(二,发送彩信&lt;1&gt;)

    当准备工作(添加附件,输入文本内容)完成之后,我们这里开始进行该流程分析的第二阶段,也就是发送彩信.这里我们从ComposeMessageActivity类的点击发送按钮(mSendButtonMms)的点击事件开始:<TAG 1-1> @Override public void onClick(View v) { if (mShowTwoButtons && (v == mSendButtonSmsViewSec || v == mSendButtonMmsViewSec)

    Android通过意图使用内置的音频播放器

    如果实现一个音频文件的播放,那么在应用程序中提供播放音频文件功能的最简单的方式是利用内置的"Music(音乐)"应用程序的功能--即使用系统自带的或已安装好的音乐播放器来播放指定的音频文件. 本例比较简单,下面直接给出源代码: 布局文件activity_main: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http:/

    WebEx如何录制电脑内的声音

    WebEx如何录制电脑内的声音 事情是这样的,我需要参加一个网络课程,视频讲课的,但是呢,又因为自己白天需要干别的事,就想着怎么把视频录下来晚上回去看,找了WebEx录屏软件,尝试了下,录屏听好,就是不能录电脑内的声音,也就是说,录的是外面人说话的声音,怎么办呢?百度了一下,另外自己尝试了下,终于配置好了.. 第一步:右键 小喇叭 打开电脑的“录音设备”选项卡 在“录制”选项卡中 把 “立体声混音”设为默认——确定. 好了电脑这边就设置好了. 第二步:设置WebEx端,在  设置——音频设置向导

    android 控制手机音量的大小 切换声音的模式

    (1)程序说明 在android API的AudioManager中,提供了调节手机音量的办法. audioMa.adjustVolume(AudioManager.ADJUST_LOWER, 0); audioMa.adjustVolume(AudioManager.ADJUST_RAISE, 0); 也可以调节手机声音的模式为震动或者静音 audioMa.setRingerMode(AudioManager.RINGER_MODE_NORMAL); audioMa.setRingerMode

    C#中常用的系统内置委托

    在公共语言运行时(CLR)环境中系统为我们内置了一些常用的委托,包括Action类的委托.Func类的委托.Predicate<T>委托.Comparison<T>委托等等.以上这些委托的命名空间都是System,所属程序集都是 mscorlib.dll,今天我就来讲一讲这些委托的使用方法. 就像我们自己已定义好的一样,要实现某些功能,我们可以直接利用系统内置委托,实例化它们,而不必显式定义一个新委托并将命名方法分配给该委托.如: public static void Test()