android 音频子系统-Surfaceflinger(二)

音频系统的核心:AudioFlinger

AudioFlinger为上层提供访问和管理音频的接口,同时通过hal来管理音频设备。

AudioFlinger服务的启动:

Framework/av/media/audioserver/main_audioserver.cpp
int main(int argc __unused, char **argv){
	AudioFlinger::instantiate();
	AudioPolicyService::instantiate();
	RadioService::instantiate();
	SoundTriggerHwService::instantiate();
}

把音频相关的服务启动起来。这个服务是系统开机时由init进程启动的。

audioserver.rc
service audioserver /system/bin/audioserver
    class main
user audioserver

服务的类型是main,就是在启动main这类service时,audioserver也就启动了。

那么audioFlinger什么时候开始执行工作?根据其继承关系:

class AudioFlinger : publicBinderService<AudioFlinger>,public BnAudioFlinger

一方面继承BinderService,完成了把AudioFlinger添加到serviceManager的工作,另一方面继承BnAudioFlinger,使其是一个强指针引用,在被第一次引用时,执行onFirstRef()开启工作。在这之后,其他进程就可以通过serviceManager来访问AudioFlinger,调用其接口来让audiofligner执行音频处理的操作。

从功能上讲,audiopolicyservice是策略的制定者,如什么时候打开音频接口设备、某种stream类型额音频对应什么设备由其决定,audioflinger是策略的执行者,如具体如何与音频设备通信、如何维护系统中的音频设备、多个音频流的混音处理由它完成。

Audio系统中支持的音频设备接口有:

AudioFlinger.cpp
static const char * const audio_interfaces[] = {
    AUDIO_HARDWARE_MODULE_ID_PRIMARY,//主音频设备
    AUDIO_HARDWARE_MODULE_ID_A2DP,//蓝牙a2dp音频
    AUDIO_HARDWARE_MODULE_ID_USB,//usb音频
};

AudiopolicyService会读取音频设备的描述文件:audio_policy.conf,然后打开这三类音频接口中存在的那个。这个过程最终调用的是AudioFlinger::loadHwModule(constchar *name),其中的name就是要打开音频设备的名字。

看下具体的调用过程:

void AudioPolicyService::onFirstRef(){
	mAudioPolicyClient = new AudioPolicyClient(this);
mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);
}
AudioPolicyFactory.cpp
extern "C" AudioPolicyInterface* createAudioPolicyManager(
        AudioPolicyClientInterface *clientInterface)
{
    return new AudioPolicyManager(clientInterface);
}

AudioPolicyManager.cpp的构造函数中,会加载配置文件,调用audioflinger的loadHwModule方法。

AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface){
ConfigParsingUtils::loadConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE, config);
ConfigParsingUtils::loadConfig(AUDIO_POLICY_CONFIG_FILE, config);
mpClientInterface->loadHwModule(mHwModules[i]->getName());
}

这里的mpClientInterface,就是audiopolicyservice中的mAudioPolicyClient对象。

AudioPolicyClientImpl.cpp
audio_module_handle_t AudioPolicyService::AudioPolicyClient::loadHwModule(const char *name){
	sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
	return af->loadHwModule(name);
}

这里先获取audioflinger服务句柄,然后调用其:loadHwModule方法。

AudioFlinger.cpp
audio_module_handle_t AudioFlinger::loadHwModule(const char *name){
    Mutex::Autolock _l(mLock);
    return loadHwModule_l(name)
}

加锁后调用

audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name){
//是否已经添加过这个audio_interface,是就直接返回。
    for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
        if (strncmp(mAudioHwDevs.valueAt(i)->moduleName(), name, strlen(name)) == 0) {
            ALOGW("loadHwModule() module %s already loaded", name);
            return mAudioHwDevs.keyAt(i);
        }
}

//加载指定的audio_interface,会加载设备所需的库文件,
    audio_hw_device_t *dev;
int rc = load_audio_interface(name, &dev);

//执行初始化,每次操作device前,要先改变mHardwareStatus的值,操作结束后再将其恢复为AUDIO_HW_IDLE。
    mHardwareStatus = AUDIO_HW_INIT;
    rc = dev->init_check(dev);
mHardwareStatus = AUDIO_HW_IDLE;

//把加载后的设备,添加到mAudioHwDevs这个键值对中,其中key是全局唯一的。
    audio_module_handle_t handle = (audio_module_handle_t) nextUniqueId(AUDIO_UNIQUE_ID_USE_MODULE);
    mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));
}

每个audio_interface包含的设备通常不止一个,目前支持的音频设备如图:

一个audio_interface包含的音频输出通道output可能也不止一个,下面先看audioflinger是如何打开一个output通道的。

打开音频输出通道output在audioflinger中对应接口是openOutput。

AudioFlinger.cpp

status_t AudioFlinger::openOutput(audio_module_handle_t module,
                                  audio_io_handle_t *output,
                                  audio_config_t *config,
                                  audio_devices_t *devices,
                                  const String8& address,
                                  uint32_t *latencyMs,
                                  audio_output_flags_t flags){
//调用openOutput_l,这里的module就是loadHwModule获得的,它是一个audio interface的id号,通过这个id在mAudioHwDevs中找到对应的AudioHwDevices对象。
sp<PlaybackThread> thread = openOutput_l(module, output, config, *devices, address, flags);

//如果当前设备时主设备primary output,需要进行相关模式设置,
        if ((mPrimaryHardwareDev == NULL) && (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) {
            ALOGI("Using module %d has the primary audio interface", module);
            mPrimaryHardwareDev = thread->getOutput()->audioHwDev;

            AutoMutex lock(mHardwareLock);
            mHardwareStatus = AUDIO_HW_SET_MODE;
            mPrimaryHardwareDev->hwDevice()->set_mode(mPrimaryHardwareDev->hwDevice(), mMode);
            mHardwareStatus = AUDIO_HW_IDLE;
        }
}

这里主要围绕AudioHwDevice*outHwDev做一些操作。

sp<AudioFlinger::PlaybackThread> AudioFlinger::openOutput_l(
audio_module_handle_t module,
 	audio_io_handle_t *output,
audio_config_t *config,
audio_devices_t devices,
const String8& address,
audio_output_flags_t flags){
//查找合适的音频接口设备audio interface,
AudioHwDevice *outHwDev = findSuitableHwDev_l(module, devices);

//为设备打开一个输出流,会获得一个audio_stream_out_t *stream; 一个audio_devices_t devices,其中会生成一个AudioStreamOut(AudioStreamOut *outputStream = new AudioStreamOut(this, flags);)就是来封装audio_stream_out_t和audio_devices_t的。
AudioStreamOut *outputStream = NULL;
status_t status = outHwDev->openOutputStream(
	&outputStream, *output, devices, flags, config, address.string());

//创建播放线程,并添加到mPlaybackThreads全局变量中,
PlaybackThread *thread;
if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
	thread = new OffloadThread(this, outputStream, *output, devices, mSystemReady);
} else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT){
	thread = new DirectOutputThread(this, outputStream, *output, devices, mSystemReady);
} else {
	thread = new MixerThread(this, outputStream, *output, devices, mSystemReady);
}
mPlaybackThreads.add(*output, thread);
}

AudioHwDevice 类型的指针变量*outHwDev,代表了一个打开的音频接口设备,它由一个成员变量audio_hw_device_t* const   mHwDevice;数据类型audio_hw_device_t包含了一个音频接口设备所具有的属性集合。

typedef struct audio_hw_device audio_hw_device_t; 这是一个类型定义
struct audio_hw_device {
// hw_device_t 类型的common,是音频设备的通用方法,代表了硬件设备在HAL层要实现的共有属性。它必须是audio_hw_device的第一个成员,这个写法有点类似于c++的继承,表示audio_hw_device继承于hw_device_t,使用这个数据结构时将把hw_device_t转成audio_hw_device指针,在上下文中这是一个已知的hw_device_t的引用,代表的是audio_hw_device。
	struct hw_device_t common;
//设置audio interface的主音量,
	int (*set_master_volume)(struct audio_hw_device *dev, float volume);
//设置音频模式类型,当音频模式改变时被调用,标准音频播放是AUDIO_MODE_NORMAL,播放铃声是AUDIO_MODE_RINGTONE,正在通话是AUDIO_MODE_IN_CALL。
int (*set_mode)(struct audio_hw_device *dev, audio_mode_t mode);
//打开音频硬件的输出流,
    int (*open_output_stream)(struct audio_hw_device *dev,
                              audio_io_handle_t handle,
                              audio_devices_t devices,
                              audio_output_flags_t flags,
                              struct audio_config *config,
                              struct audio_stream_out **stream_out,
                              const char *address);
}

具体看下上述步骤中的详细过程,

首先,查找相应的audiointerface,当前系统支持的音频设备都记录在mAudioHwDevs中。

AudioHwDevice*AudioFlinger::findSuitableHwDev_l(

audio_module_handle_tmodule, audio_devices_t devices){

// if module is 0, the request comes from an old policy manager and weshould load

// well knownmodules

//module等于0,首先加载所有已知的音频接口设备,这个加载最终还是调用audioflinger的loadHwModule方法实现;然后根据devices确定符合要求的设备。

if (module == 0) {

for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) {

loadHwModule_l(audio_interfaces[i]);

}

// then try to find a module supporting the requested device.

for (size_t i = 0; i < mAudioHwDevs.size(); i++) {

AudioHwDevice *audioHwDevice = mAudioHwDevs.valueAt(i);

audio_hw_device_t *dev = audioHwDevice->hwDevice();

if ((dev->get_supported_devices != NULL) &&

(dev->get_supported_devices(dev) & devices) == devices)

return audioHwDevice;

}

}else {

// check a match for the requested module handle

//module非0时,查找全局的mAudioHwDevs变量确认符合要求的设备。

AudioHwDevice *audioHwDevice = mAudioHwDevs.valueFor(module);

if (audioHwDevice != NULL) {

return audioHwDevice;

}

}

return NULL;

}

其次,打开音频输出流,

AudioHwDevice.cpp

status_t AudioHwDevice::openOutputStream(
        AudioStreamOut **ppStreamOut,
        audio_io_handle_t handle,
        audio_devices_t devices,
        audio_output_flags_t flags,
        struct audio_config *config,
        const char *address){
	AudioStreamOut *outputStream = new AudioStreamOut(this, flags);
	status_t status = outputStream->open(handle, devices, config, address);
}

openOutputStream 也就是打开了一个audio_stream_out_t,这个指向指针的指针参数:AudioStreamOut**ppStreamOut,实际是一个出参,它包含了AudioStreamOut*outputStream,这个outputStream也就是要返回的结果,AudioStreamOut中有一个变量是audio_stream_out_t *stream;这个打开流的过程,主要是对audio_stream_out_t变量的操作,为他的函数指针做初始化,这个过程涉及到驱动,不太懂,就不往下跟了。

AudioStreamOut.cpp
status_t AudioStreamOut::open(
        audio_io_handle_t handle,
        audio_devices_t devices,
        struct audio_config *config,
        const char *address){
	audio_stream_out_t *outStream;
    int status = hwDev()->open_output_stream(
            hwDev(),
            handle,
            devices,
            customFlags,
            config,
            &outStream,
            address);
}

最后,通道打开后,就要往通道中传数据了,也就是playbackthread播放线程做的工作。包括混音和直接输出。以混音为例,创建混音线程MixerThread,添加到mPlaybackThreads中。

DefaultKeyedVector< audio_io_handle_t,sp<PlaybackThread> > mPlaybackThreads;

DefaultKeyedVector< audio_io_handle_t,sp<RecordThread> >   mRecordThreads;

系统中的播放线程都会添加到全局变量mPlaybackThreads中,录音线程也会添加到全局变量mRecordThreads中。

播放线程的继承关系图

播放线程的基类都是Thread,

class MixerThread : public PlaybackThread ;

class PlaybackThread : public ThreadBase(frameworks/av/services/audioflinger/Threads.h)

class ThreadBase : public Thread(frameworks/av/services/audioflinger/Threads.h)

classThread : virtual public RefBase (system/core/include/utils/Thread.h)

Threads.cpp

AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, audio_io_handle_t id, audio_devices_t device, bool systemReady, type_t type) :   PlaybackThread(audioFlinger, output, id, device, type, systemReady), mFastMixerFutex(0), mMasterMono(false){
	AudioMixer* mAudioMixer;    // normal mixer
	sp<NBAIO_Sink>          mOutputSink;
	mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
	mOutputSink = new AudioStreamOutSink(output->stream);
	mOutputSink->negotiate(offers, 1, NULL, numCounterOffers);
	。。。
}

首先生成AudioMixer对象mAudioMixer,接着创建一个NBAIO(Non-blocking audio I/O interface 非阻塞音频IO接口)Sink对象mOutputSink。

一个播放线程的任务就是循环处理上层的音频数据回放请求,然后将其传到下一层,最终写入硬件设备,所以应该会有一个线程循环的地方。

从前面MixerThread的继承关系,它的父类有RefBase这个强指针,所以在其第一次被引用时将调用onFirstRef方法。

Threads.cpp

void AudioFlinger::PlaybackThread::onFirstRef()
{
    run(mThreadName, ANDROID_PRIORITY_URGENT_AUDIO);
}

这个run方法,会create一个新线程,并调用新线程的threadLoop,然后循环的处理混音业务,这个新线程的threadLoop的返回值如果为true,threadLoop就会被再起调用,继续循环,如果返回false,就结束循环。看下具体代码:

这个run方法是system/core/include/utils/Thread.h中的,

后面两个参数是默认参数。

virtual status_t    run(   const char* name,

int32_t priority = PRIORITY_DEFAULT,  size_t stack = 0);

它的实现:

System/core/libutils/Threads.cpp

status_t Thread::run(const char* name, int32_t priority, size_t stack){
//这个函数一方面doCreateThread创建一个线程,另一方面_threadLoop方法,这个_threadLoop方法中将调用子类的threadLoop,并判断是否结束循环,
res = androidCreateRawThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
}

这里调用了子类的threadLoop,也即是playbackthread的threadloop。

int Thread::_threadLoop(void* user){
	do {
		result = self->threadLoop();
		if (result == false || self->mExitPending) {
			self->mExitPending = true;
			break;
}
} while(strong != 0);
}

这里的self就是thread子类对象,如果threadLoop返回false,或者子类自己退出了,都会跳出while循环。

这样音频通道的建立就完成了,下面就是AudioTrack往这个通道输入数据。

整个过程:audiopolicyManager构造时,会根据audio_policy.conf来分析系统中有哪些audio interface,然后通过audioflinger::loadHwModule加载audiointerface对应的库文件,依次打开其中的输出通道output。打开openoutput时,打开了audio_stream_out_t通道,生成了audioStreamOut对象,创建了playbackthread线程。后面就等着audiotrack不断地跟audioflinger传递数据,整个音频回放就开始了。

接下来看playbackthread的循环体threadloop的处理过程:

Framework/av/services/audioflinger/Threads.cpp
bool AudioFlinger::PlaybackThread::threadLoop(){
	while (!exitPending()){
//处理配置变更。
		processConfigEvents_l();
//判断是否要进入standby,当前活跃的track数为0,或需要挂起都会进入standby
		if ((!mActiveTracks.size() && systemTime() > mStandbyTimeNs) || isSuspended()){
			if (shouldStandby_l()) {
				threadLoop_standby();
				mStandby = true;
			}
}
//准备音频数据。
mMixerStatus = prepareTracks_l(&tracksToRemove);

//如果准备好了,就执行混音mix,否则休眠一段时间。
if (mMixerStatus == MIXER_TRACKS_READY) {
	threadLoop_mix();
}else{
	threadLoop_sleepTime();
}

//如果mSleepTimeUs等于0,说明一定要写入数据到音频硬件设备,否则进入wait,等待时间是mSleepTimeUs
if (mSleepTimeUs == 0) {
	ret = threadLoop_write();
}else{
	mWaitWorkCV.waitRelative(mLock, microseconds((nsecs_t)mSleepTimeUs));
}

//最后,移除相关track。
threadLoop_removeTracks(tracksToRemove);
tracksToRemove.clear();
}
}

看下关键步骤的详细过程:

prepareTracks_l准备数据:

AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
	Vector< sp<Track> > *tracksToRemove){
//当前需要处理的track的数量。mActiveTracks记录当前活跃的track,有新的audiotrack加入,也会在audiotrack工作结束或出错时remove相应track。
	size_t count = mActiveTracks.size();

//循环处理每一个track。
	for (size_t i=0 ; i<count ; i++) {
		const sp<Track> t = mActiveTracks[i].promote();

//处理fast track
		if (track->isFastTrack()) {

}

//准备数据块。
audio_track_cblk_t* cblk = track->cblk();

//回放音频,需要准备多少帧数据。
desiredFrames = sourceFramesNeededWithTimestretch(
	sampleRate, mNormalFrameCount, mSampleRate, playbackRate.mSpeed);
desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
//track->sharedBuffer()为0,说明这个audiotrack不是static模式,也即是数据不是一次性传送的。
if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
	(mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
minFrames = desiredFrames;
}

//数据准备完毕,设置音量、设置一些参数
size_t framesReady = track->framesReady();
mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf);
mAudioMixer->setParameter(……);
}//for循环结束

}

看完了prepareTracks_l的实现,在回到前面的thread_loop。

如果prepare_track_l的数据准备工作已经完成,就开始进行混音操作,即threadLoop_mix。

void AudioFlinger::MixerThread::threadLoop_mix(){
	mAudioMixer->process();//进入到AudioMix处理。
}

每一个MixerThread都有一个唯一对应的AudioMixer,它的作用是完成音频的混音操作。

AudioMixer对外接口主要有Parameter相关(setParameter),Resampler(setResampler),Volume(adjustVolumeRamp),Buffer(setBufferProvider),Track(getTrackName)几部分。

AudioMixer的核心是一个state_t类型的变量mState,所有的混音工作都会在这个变量中体现出来:

struct state_t {
        uint32_t        enabledTracks;
        uint32_t        needsChanged;
        size_t          frameCount;
        process_hook_t  hook;   // one of process__*, never NULL
        int32_t         *outputTemp;
        int32_t         *resampleTemp;
        NBLog::Writer*  mLog;
        int32_t         reserved[1];
        // FIXME allocate dynamically to save some memory when maxNumTracks < MAX_NUM_TRACKS
        track_t         tracks[MAX_NUM_TRACKS] __attribute__((aligned(32)));
};

数组tracks的大小MAX_NUM_TRACKS =32,表示最多支持32路同时混音,其类型track_t是对每一个track的描述,setParameter接口最终影响的就是track的属性。

struct track_t {
        uint32_t    needs;
        union {
        int16_t     volume[MAX_NUM_VOLUMES]; // U4.12 fixed point (top bit should be zero)
        int32_t     volumeRL;
        };
		const void* in;             // current location in buffer
		AudioResampler*     resampler;
		……
}

AudioFlinger中threadloop,不断调用prepareTracks_l来准备数据,每次prepare实际都是对所有Tracks的一次调整 ,如果属性有变化,会通过setParamter通知AudioMixer。

前面AudioMixer::process()调用了mState.hook(&mState);hook是一个函数指针,根据不同场景会分别指向不同函数实现:

在AudioMixer初始化时,hook指向process__nop;

mState.hook         = process__nop;

在状态改变、参数变化时,hook指向process__validate

void AudioMixer::setParameter(int name, int target, int param, void *value){
	invalidateState(1 << name);
}
void AudioMixer::invalidateState(uint32_t mask){
	mState.hook = process__validate;
}

process__validate又会根据不同的场景,将hook指向不同的函数:

void AudioMixer::process__validate(state_t* state){
//初始值
	state->hook = process__nop;
//针对处于enable状态的track
	if (countActiveTracks > 0) {
		if (resampling)
			state->hook = process__genericResampling; //重采样
		else
			state->hook = process__genericNoResampling;//不重采样
}
}

混音之后,将数据写入hal中,进一步写入硬件设备中:

ssize_t AudioFlinger::PlaybackThread::threadLoop_write(){
//如果NBAIO(Non blocking audio I/O)sink是存在的,就用mNormalSink写入hal设备,否则就用AudioStreamOut将数据输出。
	if (mNormalSink != 0) {
		ssize_t framesWritten = mNormalSink->write((char *)mSinkBuffer + offset, count);
}else{
	bytesWritten = mOutput->write((char *)mSinkBuffer + offset, mBytesRemaining);
}
}

最后,调用:threadLoop_removeTracks移除tracksToRemove中指示的tracks,在这个列表中的track,与其相关的output将收到stop请求(即AudioSystem::stopOutput(…))。

void AudioFlinger::PlaybackThread::threadLoop_removeTracks(
        const Vector< sp<Track> >& tracksToRemove){
	AudioSystem::stopOutput(mId, track->streamType(),track->sessionId());
}

时间: 2024-10-01 04:21:57

android 音频子系统-Surfaceflinger(二)的相关文章

Android Camera子系统之用户View

一.拍照模式 打开原生Camera应用,将出现如下所示拍照界面 左边为预览区域,右边为控制面板. 控制面板分为三部分,从上到下依次为缩略图.快门按钮和模式选择器. 模式选择器中显示当前模式为拍照模式. 按下快门按钮将会执行拍照操作,缩略图区域显示所拍照片的缩略图. 二.录像模式 点击模式选择器区域,选择录像模式,将会进入录像界面,如下图所示 点击快门按钮将会开始录像,再次点击快门,结束录像. Android Camera子系统之用户View,码迷,mamicode.com

Android 音频播放——AudioTrack直接播PCM、MediaPlayer播媒体文件可以是audio

http://blog.csdn.net/java_android_c/article/details/52678265 Android平台播放音频的方式一般有3种.1.利用系统内置的应用程序播放音频    2.利用AudioTrack播放原始音频   3.使用MediaPlayer播放.此3种音频播放方式,以第三种MediaPlayer播放这种方式使用的最多,必须掌握! 一.使用系统内置的程序. Google想的"周到",一般都给我们提供了一些内置程序,然而这些内置程序的UI效果,那

Android Camera子系统之Linux C应用开发者View

Android Camera HAL通过V4L2接口与内核Camera Driver交互.本文从Linux应用开发者的角度审视Android Camera子系统. V4L2应用开发一般流程: 1. 打开设备文件. int fd=open("/dev/videoX″,O_RDWR); 2.取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等.VIDIOC_QUERYCAP,structv4l2_capability 3.选择视频输入,一个视频设备可以有多个

Android ROM开发(二)——ROM架构以及Updater-Script脚本分析,常见的Status错误解决的方法

Android ROM开发(二)--ROM架构以及Updater-Script脚本分析,常见的Status错误解决的方法 怪自己二了.写好的不小心弄没了,如今仅仅好又一次写一些了.上篇简单的配置了一下环境.这里呢,就来讲一下相关的仅仅是点 我们先下载一个ROM.随便下,原理都是差点儿相同的,这里我就下载一个红米Note的MIUI稳定版 1.ROM结构 ROM依据厂商的定制可能有所不同,可是大体是不变的 data 内置一些软件 META-INF 脚本文件 update-binary 二进制文件 u

Android 音频播放

Android平台播放音频的方式一般有3种.1.利用系统内置的应用程序播放音频    2.利用AudioTrack播放原始音频   3.使用MediaPlayer播放.此3种音频播放方式,以第三种MediaPlayer播放这种方式使用的最多,必须掌握! 一.使用系统内置的程序. Google想的"周到",一般都给我们提供了一些内置程序,然而这些内置程序的UI效果,那真是感人啊!一般内置程序,我们就是看看而已. Intent intent=new Intent(Intent.ACTION

Android音频处理——通过AudioRecord去保存PCM文件进行录制,播放,停止,删除功能

Android音频处理--通过AudioRecord去保存PCM文件进行录制,播放,停止,删除功能 音频这方面很博大精深,我这里肯定讲不了什么高级的东西,最多也只是一些基础类知识,首先,我们要介绍一下Android他提供的录音类,实际上他有两个,一个是MediaRecorder,还有一个就是我们今天要用到的AudioRecord,那他们有什么区别呢? 一.区别 MediaRecorder和AudioRecord都可以录制音频,区别是MediaRecorder录制的音频文件是经过压缩后的,需要设置

Android 音频采集——MediaRecord(编码后录影文件) 、AudioRecord(PCM原始数据)

http://blog.csdn.net/java_android_c/article/details/52619737 Android 音频简介 常见的音频编解码的类型:AAC  OPUS MP3  AMR  Ogg  PCM AAC: 高级音频编码  对应  .m4a(audio/m4a)或者.3pg(audio/3gpp)文件   HEAAC:高级AAC,使用的比较多. OPUS:有损声音编码的格式,由互联网工程任务组(IETF)进来开发,适用于网络上的实时声音传输,如:语音通话 MP3:

android 电容屏(二):驱动调试之基本概念篇

关键词:android  电容屏 tp 工作队列 中断 多点触摸协议平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:S5PV310(samsung exynos 4210)  作者:xubin341719(欢迎转载,请注明作者) 参考网站:http://edsionte.com/techblog/archives/1582这部分参考别人的多一点 android 电容屏(一):电容屏基本原理篇 android 电容屏(二):驱动调试之基本概念篇

Android -- 经验分享(二)

目录                                                                                   自定义两个View进行画图,让其各自刷新重绘 Activity设为singleTop,长按Home键启动的问题 Activity设为singleTop,长按Home键启动问题 Android软键盘挡住界面 Android使用AsyncTask下载图片,最好使用WeakReference Android webview 加载网络视