Android音频数据传输
- 格式:docx
- 大小:16.29 KB
- 文档页数:7
用户指南单声道蓝牙™耳机简介 (3)功能概览 (3)硬件概览 (3)基本信息 (5)充电 (5)打开和关闭 (5)调节音量 (5)佩戴说明 (6)使用耳机 (7)将耳机与Android或iOS设备配对 (7)使用耳机进行通话 (7)使用耳机收听音乐 (7)使用语音助手功能 (8)使用多点模式 (8)重设耳机 (8)法律信息 (9)回收传输设备中的电池 (9)Declaration of Conformity for MBH22 (10)功能概览与兼容Android™或iOS设备配对时,您的单声道蓝牙™耳机 支持免提通话、听音乐以及使用语音助手。
您可以将耳机同时连接到最多两台兼容设备。
这样,您可以接听来自这两台设备的来电,而无需断开并重新连接。
硬件概览1.听筒处理来电或收听音乐。
2.充电器/USB Type-C™插入USB Type-C连接线给电池充电。
连接线端口3.LED指示灯LED颜色指示电池电量:•绿色:85%或更多。
•橙色:15% - 85%。
•红色:15%或更少。
LED闪烁频率指示状态:•按电源键检查电池电量时,缓慢闪烁3次。
•在配对模式和来电或去电期间快速闪烁。
4.电源键短按可以:•查看电池电量。
长按1秒可以:•开机并尝试连接。
•关机。
长按5秒可以:•开机并进入配对模式。
长按10秒可以:•重设耳机。
5.麦克风拨打电话或使用语音助手。
6.音量键按下可以降低音量。
7.音量键按下可以提高音量。
8.多功能键短按可以:•接听来电或结束通话。
•播放或暂停当前音乐曲目。
•尝试重新连接。
长按1秒可以:•拒绝来电。
•通话过程中使麦克风静音或取消静音。
•启动语音助手。
快速双击可以:•在两个通话之间切换。
•跳至下一首音乐曲目。
快速点按三次可以:•在耳机和配对的Android或iOS设备之间传输音频内容。
•跳至上一首音乐曲目。
基本信息充电在第一次使用耳机之前,建议使用索尼充电器对耳机进行充电。
当电池电量高于85%时,LED指示灯变绿。
adb push 用法-回复adb push 是一种在Android 开发中常用的命令行工具,它允许开发者将文件从计算机推送到连接的Android 设备上。
这种方式非常方便,可以帮助开发者快速传输文件,以便在Android 设备上进行测试和调试。
本文将详细介绍adb push 的用法,并逐步回答与这一主题相关的问题。
1. 什么是adb push?adb push 是Android 调试桥(Android Debug Bridge)的一部分,它是一个位于计算机和Android 设备之间的桥梁。
通过adb push 命令,开发者可以将文件从计算机传输到连接的Android 设备上。
2. 如何使用adb push?使用adb push 命令非常简单,只需要在命令行中输入以下命令:adb push <local_file> <device_directory>其中,`<local_file>` 是要传输的本地文件的路径,`<device_directory>` 是要在Android 设备上保存文件的目标路径。
例如,要将名为`example.jpg` 的图片文件从计算机的`D:\` 目录传输到Android 设备上的`/sdcard/Pictures/` 目录,可以使用以下命令:adb push D:\example.jpg /sdcard/Pictures/请确保Android 设备已通过USB 连接到计算机,并已启用开发者选项和USB 调试模式。
3. adb push 的常见用途有哪些?adb push 在Android 开发中有许多常见用途。
以下是一些常见的用例:- 传输应用安装包(APK):开发者可以使用adb push 将APK 安装包从计算机传输到Android 设备,以便在设备上进行测试和安装。
- 传输媒体文件:开发者可以将音频、视频、图片等媒体文件传输到Android 设备上,以便在应用程序中使用。
mediacodec ndk编程
MediaCodec NDK编程是指使用Android NDK(Native Development Kit)来开发使用MediaCodec API的应用程序。
MediaCodec API是Android中用于音视频编解码的API,它提
供了硬件加速的编解码功能,可以在开发中实现音视频数据的压缩、解压、编码和解码等操作。
在NDK编程中使用MediaCodec API可以有以下步骤:
1. 配置Android.mk文件:在NDK项目的Android.mk文件中
添加依赖库的链接,例如:LOCAL_LDLIBS += -lmediandk
2. 调用MediaCodec API:在NDK的C/C++代码中调用MediaCodec API来实现音视频编解码的操作,例如:创建编
解码器、配置编解码参数、调用start()方法来启动编解码器等。
3. 使用Buffer进行数据传输:通过设置输入输出缓冲区来传
输音视频数据,音频数据通过AudioBufferInfo来描述,视频
数据通过VideoBufferInfo来描述。
4. 处理编解码结果:通过获取编解码器的输出缓冲区来处理编解码结果,可以获得解码后的音视频数据,通过回调或写入文件等方式进行处理。
需要注意的是,MediaCodec API的使用需要对编解码相关的
知识有一定的了解,并且需要通过Android NDK来进行开发。
掌握C/C++编程语言和Android NDK开发技术的基础,对于进行MediaCodec NDK编程是非常有帮助的。
audiotrack 用法-回复Audiotrack 是一个非常有用的Android 类,它用于播放音频文件。
在本文中,我将一步一步地解释Audiotrack 的使用方法,并给出一些示例代码以帮助读者更好地理解。
首先,要使用Audiotrack,您需要从Android 的MediaPlayer 类中获取音频数据。
这可以通过以下几个步骤来完成:第一步是创建一个AudioTrack 实例. 若要创建一个Audiotrack 实例,需要提供以下参数:音频流类型、采样率、声道配置、音频格式以及缓冲区大小。
以下是一个简单的示例代码:javaint bufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);AudioTrack audioTrack = new AudioTrack(streamType, sampleRate, channelConfig, audioFormat, bufferSize, mode);其中,sampleRate 是采样率,channelConfig 是声道配置,audioFormat 是音频格式,streamType 是音频流类型,bufferSize 是缓冲区大小,mode 是模式。
第二步是设置音频数据。
在这一步中,我们将使用audioTrack.write() 方法来将音频数据写入缓冲区。
以下是一个示例代码:javabyte[] audioData = new byte[bufferSize];audioTrack.write(audioData, 0, bufferSize);第三步是开始播放音频数据。
以下是一个使用audioTrack.play() 方法来开始播放音频的示例代码:javaaudioTrack.play();此外,还可以使用stop() 方法停止音频播放:javaaudioTrack.stop();这些是基本的使用Audiotrack 的步骤。
Android视频播放数据读取的流程在Android平台上,视频播放数据的读取流程主要包括以下几个步骤:1. 创建MediaExtractor对象:MediaExtractor是Android中用于从多种媒体文件中读取数据的类。
通过调用其构造函数并传入媒体文件的路径,可以创建一个用于读取视频数据的MediaExtractor对象。
2. 选择数据轨道:对于包含视频、音频或字幕等多个轨道的媒体文件,需要选择需要读取的数据轨道。
可以通过调用MediaExtractor的getTrackCount(方法获取媒体文件的轨道数,然后通过调用selectTrack(方法选择需要读取的轨道。
3. 创建MediaCodec对象:MediaCodec是Android中用于解码媒体数据的类。
可以通过调用createDecoderByType(方法并传入媒体文件中对应轨道的MIME类型,来创建一个用于解码视频数据的MediaCodec对象。
4. 配置MediaCodec对象:配置MediaCodec对象包括设置解码器的输入格式和输出格式。
可以通过调用MediaExtractor的getTrackFormat(方法获取当前数据轨道的格式,并将其设置为MediaCodec的输入格式;同时,还需要设置解码后的数据输出的Surface。
5. 启动MediaCodec对象:配置完成后,需要调用MediaCodec的start(方法启动解码器。
6. 获取输入缓冲区:对于解码器的输入数据,需要先获取一个可用的输入缓冲区。
可以通过调用MediaCodec的getInputBuffers(方法来获取解码器的输入缓冲区数组,然后根据返回的索引获取一个可用的输入缓冲区。
7. 读取数据至输入缓冲区:将从MediaExtractor获取的待解码的数据读取至输入缓冲区中。
可以通过调用MediaExtractor的readSampleData(方法,传入一个ByteBuffer对象,来将媒体数据读取至ByteBuffer中。
基于UPnP协议和Android平台的 无线传屏系统设计与实现*余燕青 周雄图 张永爱 林志贤 郭太良 福州大学物理与信息工程学院摘要:针对目前Android设备在实现多媒体资源分享时需要外接电缆辅助、操作不灵活、无线传输距离受限、设备兼容性不高等问题,本文将UPnP设备连接协议与屏幕投影结合,提出了一种Android设备的无线传屏系统方案。
在移动智能手机端和电视端分别搭载该多屏互动系统,通过UPnP设备互联协议设计、屏幕和音视频数据获取、数据编码压缩与解码、数据传输与显示等过程的设计,实现了手机端与电视端同屏的效果。
测试结果表明系统达到了整体的设计要求,具有较好的稳定性。
关键词:多屏互动 屏幕投影 Android UPnP1 引言信息技术的发展促进了家庭生活的变革,数字家庭的概念逐渐深入人心。
多屏互动技术可以实现电视屏与智能手机、Pad等智能终端屏幕的有机结合和互动,开启了“瘦终端+云后台”的“泛电视”时代,极大促进了智能电视行业和数字家庭的发展与推广,已被广泛应用于现代家庭、商务、教育、娱乐等环境[1]。
在数字家庭环境下,方便灵活地实现各种智能显示终端的互联互通,进行多媒体资源共享,是实现多屏互动的一个重要前提。
目前实现多屏互动的方法主要有四种:一是英特尔公司提出的Wireless Display(Wi-Di)技术,该技术借助无线接收装置接收信号,通过有线电缆将接收到的信号传输到显示设备上进行显示,需要外接电缆辅助且操作复杂;二是苹果公司开发提出的AirPlay技术,支持镜像功能,用户通过Wi-Fi可将Mac、iPhone、iPad上的多媒体内容同时呈现在Apple TV上,实现多媒体内容的共享,AirPlay一般只支持苹果系产品和通过苹果认证的产品间使用,设备普及率低;三是由索尼、英特尔、微软等公司共同提出的数字生活网络联盟(DLNA)技术,DLNA协议旨在解决家庭设备间的互联互通,高度依赖当前应用很广泛的技术和协议,张儒根据DLNA协议设计了基于Cling库和Android网络框架的双屏互动与远程控制技术架构[2],何衍根据DLNA标准在基于Android操作系统上实现了DLNA多屏互动系统[3],朱平洋在Android系统的智能手机上实现了DLNA协议[4],由于DLNA是以内容推送为主,而多媒体的格式众多,因此DLNA能支持的多媒体格式过少,使得其存在兼容性问题;四是Wi-FiAlliance提出的Miracast技术,其以Wi-Fi Direct为基础,支持镜像功能。
Android深⼊浅出之AudioTrack分析Android深⼊浅出之Audio第⼀部分 AudioTrack分析⼀⽬的本⽂的⽬的是通过从Audio系统来分析Android的代码,包括Android⾃定义的那套机制和⼀些常见类的使⽤,⽐如Thread,MemoryBase等。
分析的流程是:l 先从API层对应的某个类开始,⽤户层先要有⼀个简单的使⽤流程。
l 根据这个流程,⼀步步进⼊到JNI,服务层。
在此过程中,碰到不熟悉或者第⼀次见到的类或者⽅法,都会解释。
也就是深度优先的⽅法。
1.1 分析⼯具分析⼯具很简单,就是sourceinsight和android的API doc⽂档。
当然还得有android的源代码。
我这⾥是基于froyo的源码。
注意,froyo源码太多了,不要⼀股脑的加⼊到sourceinsight中,只要把framwork⽬录下的源码加进去就可以了,后续如要⽤的话,再加别的⽬录。
⼆ Audio系统先看看Audio⾥边有哪些东西?通过Android的SDK⽂档,发现主要有三个:l AudioManager:这个主要是⽤来管理Audio系统的l AudioTrack:这个主要是⽤来播放声⾳的l AudioRecord:这个主要是⽤来录⾳的其中AudioManager的理解需要考虑整个系统上声⾳的策略问题,例如来电话铃声,短信铃声等,主要是策略上的问题。
⼀般看来,最简单的就是播放声⾳了。
所以我们打算从AudioTrack开始分析。
三 AudioTrack(JAVA层)JAVA的AudioTrack类的代码在:framework\base\media\java\android\media\AudioTrack.java中。
3.1 AudioTrack API的使⽤例⼦先看看使⽤例⼦,然后跟进去分析。
⾄于AudioTrack的其他使⽤⽅法和说明,需要⼤家⾃⼰去看API⽂档了。
//根据采样率,采样精度,单双声道来得到frame的⼤⼩。
详解Android开发录⾳和播放⾳频的步骤(动态获取权限)步骤:配置权限:<manifest xmlns:android="/apk/res/android" package="com.work.mediaplay"><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission><uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission>代码步骤:public class MainActivity extends AppCompatActivity implements View.OnClickListener{private Button btn_start, btn_stop;private ListView lv_content;private File sdcardfile = null;private String[] files;private MediaRecorder recorder=null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_main);initView();getSDCardFile();getFileList();}/*** ①实例化控件*/private void initView() {btn_start = (Button) findViewById(R.id.btn_stat);btn_stop = (Button) findViewById(R.id.btn_stop);lv_content = (ListView) findViewById(R.id.lv_content);//⑤给按钮添加监听事件btn_start.setOnClickListener(this);btn_stop.setOnClickListener(this);//设置起始状态开始按钮可⽤,停⽌按钮不可⽤btn_start.setEnabled(true);btn_stop.setEnabled(false);}/*** ②获取内存卡中⽂件的⽅法*/private void getSDCardFile() {if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {//内存卡存在sdcardfile=Environment.getExternalStorageDirectory();//获取⽬录⽂件}else {Toast.makeText(this,"未找到内存卡",Toast.LENGTH_SHORT).show();}}/*** ③获取⽂件列表(listView中的数据源)* 返回指定⽂件类型的⽂件名的集合作为数据源*/private void getFileList(){if(sdcardfile!=null){files=sdcardfile.list(new MyFilter());lv_content.setAdapter(new ArrayAdapter<String>(this,yout.simple_list_item_1,files));//⑥给ListView中的元素添加点击播放事件lv_content.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {//⑩定义播放⾳频的⽅法play(files[position]);}}}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.btn_stat://⑧申请录制⾳频的动态权限if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED){ActivityCompat.requestPermissions(this,new String[]{android.Manifest.permission.RECORD_AUDIO},1);}else {startRecord();}break;case R.id.btn_stop:stopRcecord();break;}}/*** ④定义⼀个⽂件过滤器MyFilter的内部类,实现FilenameFilter接⼝* 重写⾥边accept⽅法*/class MyFilter implements FilenameFilter{@Overridepublic boolean accept(File pathname,String fileName) {return fileName.endsWith(".amr");}}/*** ⑦给两个按钮定义开始和暂停的⽅法**/private void startRecord(){if(recorder==null){recorder=new MediaRecorder();}recorder.setAudioSource(MediaRecorder.AudioSource.MIC);//设置⾳频源为⼿机麦克风recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);//设置输出格式3gprecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);//设置⾳频编码为amr格式 //获取内存卡的根⽬录,创建临时⽂件try {File file=File.createTempFile("录⾳_",".amr",sdcardfile);recorder.setOutputFile(file.getAbsolutePath());//设置⽂件输出路径//准备和启动录制⾳频recorder.prepare();recorder.start();} catch (IOException e) {e.printStackTrace();}//启动后交换两个按钮的可⽤状态btn_start.setEnabled(false);btn_stop.setEnabled(true);}private void stopRcecord(){if(recorder!=null){recorder.stop();recorder.release();recorder=null;}btn_start.setEnabled(true);btn_stop.setEnabled(false);//刷新列表数据getFileList();}/*** ⑨重写onRequestPermissionsResult⽅法* 获取动态权限请求的结果,再开启录制⾳频@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if(requestCode==1&&grantResults[0]==PackageManager.PERMISSION_GRANTED){startRecord();}else {Toast.makeText(this,"⽤户拒绝了权限",Toast.LENGTH_SHORT).show();}super.onRequestPermissionsResult(requestCode, permissions, grantResults);}/*** ⑩定义播放⾳频的⽅法*/private void play(String fileName){Intent intent=new Intent(Intent.ACTION_VIEW);//播放⾳频需要uri,从⽂件中获取,⽂件中需要路径Uri uri=Uri.fromFile(new File(sdcardfile.getAbsoluteFile()+File.separator+fileName));//设置播放数据和类型intent.setDataAndType(uri,"audio/*");startActivity(intent);}以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
mtp传送协议书MTP(Media Transfer Protocol)是一种用于传送媒体文件的协议。
它被广泛使用在移动设备和计算机之间传输音频、视频和图像等媒体文件的过程中。
MTP协议旨在提供一种简单、高效的方式,让用户能够方便地管理和传输他们的媒体文件。
MTP协议的设计目标之一是兼容性。
它被设计成可以在不同的操作系统和设备之间无缝工作。
无论是Windows还是Linux、iOS还是Android,只要设备支持MTP协议,用户就可以使用相同的方式和工具来管理他们的媒体文件。
这使得MTP成为了一个非常强大的协议,能够满足不同操作系统和设备之间的媒体传输需求。
MTP协议的另一个关键特性是它的可靠性和稳定性。
在传输媒体文件的过程中,MTP协议采用了一种基于TCP/IP的传输方式,确保了数据的可靠传输。
无论是传输大型的视频文件还是小型的音乐文件,MTP都能够保证文件的完整性和准确性。
此外,MTP还支持断点续传的功能,即使在传输过程中出现错误或中断,用户也可以从断点处重新开始传输,节省了用户的时间和精力。
MTP协议在数据管理方面也有很多创新之处。
用户可以使用MTP协议来浏览、复制、移动和删除他们的媒体文件。
不仅如此,MTP还支持一系列高级功能,如文件夹管理、元数据编辑、搜索和排序等。
这使得用户在管理和组织他们的媒体文件时更加方便和灵活。
无论是对于个人用户还是专业用户,MTP都提供了一种快速、直观的方式来管理和传输媒体文件。
最后,MTP协议还支持安全传输和访问控制。
在传输媒体文件时,MTP协议使用了一种加密算法来保护数据的安全性。
只有经过授权的用户才能够访问和修改文件,确保了用户的隐私和数据的安全。
此外,MTP还支持数字版权管理(DRM)的功能,允许版权所有者对他们的媒体文件进行保护和授权。
总之,MTP是一种用于传送媒体文件的协议,具有兼容性、可靠性、数据管理和安全访问控制等多个方面的优势。
它是用户在移动设备和计算机之间管理和传输媒体文件的理想选择。
MediaPlayer那边就不看了,从AudioTrack开始研究。 1、AudioTrack::write函数 调用函数obtainBuffer获取到一块buffer,然后把传入的数据copy到获取的buffer中。
2、AudioTrack::obtainBuffer函数 该函数的主要功能就是对传入的audioBuffer进行赋值。 看看audioBuffer的类型: class Buffer { public: enum { MUTE = 0x00000001 }; uint32_t flags; int channelCount; int format; size_t frameCount; size_t size; union { void* raw; short* i16; int8_t* i8; }; };
其中存放数据的是下面这个东东: union { void* raw; short* i16; int8_t* i8; };
对这块东东赋值的代码如下: audioBuffer->raw = (int8_t *)cblk->buffer(u);
先看其中cblk的来历: audio_track_cblk_t* cblk = mCblk;
mCblk的赋值在函数AudioTrack::createTrack中: mCblk = static_cast(cblk->pointer()); cblk的由来: sp cblk = track->getCblk();
track的由来: sp track = audioFlinger->createTrack(getpid(), streamType, sampleRate, format, channelCount, frameCount, ((uint16_t)flags) << 16, sharedBuffer, output, &mSessionId, &status);
函数AudioFlinger::createTrack返回的是一个TrackHandle对象: trackHandle = new TrackHandle(track); return trackHandle;
track的由来: track = thread->createTrack_l(client, streamType, sampleRate, format, channelCount, frameCount, sharedBuffer, lSessionId, &lStatus);
函数AudioFlinger::PlaybackThread::createTrack_l返回的是一个Track对象: track = new Track(this, client, streamType, sampleRate, format, channelCount, frameCount, sharedBuffer, sessionId); return track;
看看函数TrackHandle::getCblk() : return mTrack->getCblk();
mTrack就是作为构造函数传入的track对象。 函数AudioFlinger::ThreadBase::TrackBase::getCblk() 的实现: return mCblkMemory; mCblkMemory的赋值在构造函数AudioFlinger::ThreadBase::TrackBase::TrackBase中: mCblkMemory = client->heap()->allocate(size); mCblk = static_cast(mCblkMemory->pointer()); // 这个成员变量也很重要
client是构造函数参数: const sp& client
函数AudioFlinger::Client::heap: return mMemoryDealer;
mMemoryDealer的赋值在函数AudioFlinger::Client::Client中: mMemoryDealer(new MemoryDealer(1024*1024, "AudioFlinger::Client"))
看看函数MemoryDealer::allocate: sp MemoryDealer::allocate(size_t size) { sp memory; // allocator()直接返回mAllocator // mAllocator的赋值在构造函数中:mAllocator(new SimpleBestFitAllocator(size)) /× 函数SimpleBestFitAllocator::allocate的实现: size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags) { Mutex::Autolock _l(mLock); // 暂止 ssize_t offset = alloc(size, flags); return offset; } ×/ const ssize_t offset = allocator()->allocate(size); if (offset >= 0) { // heap()直接返回mHeap // mHeap的赋值在构造函数中:mHeap(new MemoryHeapBase(size, 0, name)) memory = new Allocation(this, heap(), offset, size); } return memory; }
可见前面的mCblkMemory其实就是一个Allocation对象。 可见AudioTrack的成员变量mCblk和AudioFlinger::ThreadBase::TrackBase的成员变量mCblk的值相同, 都是:static_cast(mCblkMemory->pointer())。
函数IMemory::pointer的实现: void* IMemory::pointer() const { ssize_t offset; sp heap = getMemory(&offset); void* const base = heap!=0 ? heap->base() : MAP_FAILED; if (base == MAP_FAILED) return 0; return static_cast(base) + offset; }
回头过去,看看函数audio_track_cblk_t::buffer: return (int8_t *)this->buffers + (offset - userBase) * this->frameSize;
可见audio_track_cblk_t的主要作用是申请了一块内存空间。 调用函数AudioTrack::write的时候,会先将数据写到这个内存空间中。
3、数据写入到了audio_track_cblk_t中,谁又会来使用这些数据呢? 看代码可知,函数AudioTrack::obtainBuffer中会先调用audio_track_cblk_t::framesAvailable。 同时,我们发现还有一个函数audio_track_cblk_t::framesReady。 单从字面上也可以看出来,这是告诉用户准备好了多少数据。 搜搜哪儿调用了函数audio_track_cblk_t::framesReady吧。 搜了下,发现有三个函数中调用了它,分别是: AudioFlinger::MixerThread::prepareTracks_l函数 AudioFlinger::DirectOutputThread::threadLoop函数 AudioFlinger::PlaybackThread::Track::getNextBuffer函数
4、先看看函数AudioFlinger::MixerThread::prepareTracks_l函数。 字面上看,应该是准备提供数据的Tracks。 果然不错,函数中调用AudioMixer::setBufferProvider将Track设置到mAudioMixer(AudioMixer)中。 函数AudioMixer::setBufferProvider实现: status_t AudioMixer::setBufferProvider(AudioBufferProvider* buffer) { mState.tracks[ mActiveTrack ].bufferProvider = buffer; return NO_ERROR;