`
mmdev
  • 浏览: 12892943 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

Android音频数据传输

 
阅读更多
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<audio_track_cblk_t*>(cblk->pointer());


cblk的由来:
sp<IMemory> cblk = track->getCblk();


track的由来:
sp<IAudioTrack> 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<audio_track_cblk_t *>(mCblkMemory->pointer()); // 这个成员变量也很重要


client是构造函数参数:
const sp<Client>& client


函数AudioFlinger::Client::heap:
return mMemoryDealer;


mMemoryDealer的赋值在函数AudioFlinger::Client::Client中:
mMemoryDealer(new MemoryDealer(1024*1024, "AudioFlinger::Client"))


看看函数MemoryDealer::allocate:
sp<IMemory> MemoryDealer::allocate(size_t size)
{
sp<IMemory> 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<audio_track_cblk_t *>(mCblkMemory->pointer())。


函数IMemory::pointer的实现:
void* IMemory::pointer() const {
ssize_t offset;
sp<IMemoryHeap> heap = getMemory(&offset);
void* const base = heap!=0 ? heap->base() : MAP_FAILED;
if (base == MAP_FAILED)
return 0;
return static_cast<char*>(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;
}
然后调用函数AudioMixer::setParameter将Track的main buffer也设置到mAudioMixer(AudioMixer)中。
函数AudioMixer::setParameter中与main buffer相关的部分代码:
if (name == MAIN_BUFFER) {
if (mState.tracks[ mActiveTrack ].mainBuffer != valueBuf) {
mState.tracks[ mActiveTrack ].mainBuffer = valueBuf;
LOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf);
invalidateState(1<<mActiveTrack);
}
return NO_ERROR;
}


类函数AudioMixer中是如何来使用bufferProvider的呢?
AudioMixer中的以process__为前缀的几个函数都是有到了bufferProvider。
但从数据传输上来说,这几个函数是类似的。
我们只看其中的AudioMixer::process__OneTrack16BitsStereoNoResampling函数。
函数中有代码:
t.bufferProvider->getNextBuffer(&b);
我们知道,bufferProvider其实就是Track,而我们探讨的是音频播放,所以就跳到了函数:AudioFlinger::PlaybackThread::Track::getNextBuffer,
也就是前面我们搜到的,调用audio_track_cblk_t::framesReady的三个函数之一。
函数AudioFlinger::PlaybackThread::Track::getNextBuffer是从audio_track_cblk_t中取得已准备好的音频数据,
写到什么地方去,下面我们开始研究。


回到函数process__OneTrack16BitsStereoNoResampling中,我们知道,调用AudioFlinger::PlaybackThread::Track::getNextBuffer获取的地址赋值给了in:
int16_t const *in = b.i16;


后来取in的数据给了rl:
uint32_t rl = *reinterpret_cast<uint32_t const *>(in);


然后将rl赋值给out:
*out++ = (r<<16) | (l & 0xFFFF);


由此可见,我们刚才疑惑的“写到什么地方去”,其实就是写到out里了。


out的来历:
int32_t* out = t.mainBuffer;


这不就是函数AudioMixer::setParameter中赋值的main buffer么???
搞了一圈,原来AudioMixer的功能就是将Track里audio_track_cblk_t中的数据,赋值给Track里的mainBuffer。


下面好好看看这个main buffer的来历吧。


由前面的分析可知,调用函数AudioMixer::setParameter,来设置main buffer的地方是函数AudioFlinger::MixerThread::prepareTracks_l。
回过去看看对函数AudioMixer::setParameter的调用:
mAudioMixer->setParameter(
AudioMixer::TRACK,
AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer());
函数mainBuffer()实现:
return mMainBuffer;

mMainBuffer的赋值在函数setMainBuffer中:
setMainBuffer(int16_t *buffer) { mMainBuffer = buffer; }

看看谁调用了函数setMainBuffer吧。

有三个地方调用了函数setMainBuffer:
a、AudioFlinger::PlaybackThread::createTrack_l函数
b、AudioFlinger::PlaybackThread::addEffectChain_l函数
c、AudioFlinger::PlaybackThread::removeEffectChain_l函数

b和c类似,都是将PlaybackThread的mMixBuffer赋值给了main buffer。

先看看a.
track->setMainBuffer(chain->inBuffer());
作为main buffer的是chain->inBuffer()这个东东。

inBuffer()函数实现:return mInBuffer;
mInBuffer中函数setInBuffer中被赋值:
mInBuffer = buffer;

看看调用setInBuffer函数的地方:
AudioFlinger::PlaybackThread::addEffectChain_l函数
AudioFlinger::EffectChain::addEffect_l函数

先看看AudioFlinger::PlaybackThread::addEffectChain_l函数中的处理,
函数中根据是否为DIRECT output thread,有两种处理方式:
一种是处理direct output thread:
调用函数setMainBuffer将PlaybackThread的mMixBuffer告诉给Track,即告诉Track,在AudioMixer中往PlaybackThread的mMixBuffer中copy数据。
然后将effect chain的input buffer和output buffer都设置为PlaybackThread的mMixBuffer。
(目的是让该effect chain不起作用?存在注释“Only one effect chain can be present in direct output thread and it usesthe mix buffer as input”)
另一种是处理非direct output thread:
new一段buffer出来。
调用函数setMainBuffer将新buffer告诉给Track,即告诉Track,在AudioMixer中往新buffer中copy数据。
调用函数setInBuffer来实装chain的input buffer。(发现函数AudioFlinger::PlaybackThread::createTrack_l中其实有将chain的input buffer赋值给Track的main buffer)。
然后将PlaybackThread的mMixBuffer赋值给chain的output buffer。
也就是说,Track将数据copy到自己的main buffer(即effect chain的input buffer),
effect chain对数据进行处理,然后将处理过的数据赋值给自己的output buffer(即PlaybackThread的mMixBuffer)

mMixBuffer是如何被使用的呢?


函数AudioFlinger::MixerThread::threadLoop中调用函数AudioStreamOutALSA::write(HAL侧)。
函数AudioStreamOutALSA::write中调用了函数snd_pcm_mmap_writei或者snd_pcm_writei。
函数snd_pcm_mmap_writei或者snd_pcm_writei将mMixBuffer中的数据写入到底层。


分享到:
评论

相关推荐

    音频采集传输播放

    Android实时音频采集,通过网络传输到客户端,客户端直接获取数据流后直接进行播放。

    android socket实时传输到服务端

    android 实时传输摄像头内容到服务器,服务器支持查看

    android G711 实时传输

    通过组播,socket,将android实时采集的音频数据进行g711编码, 发送,然后接收再进行g711解码,实时播放采集的声音

    Android Audio 音频开发

    Audio系统在Android中负责音频方面的数据流传输和控制功能,也负责音频设备的管理。

    Android+Speex+RTP实时语音对讲

    参考别人的代码,然后自己优化后的Android加Speex编码,然后利用RTP传输数据的实时语音DEMO,亲测在两台手机上可用,一个说的话效果还不错,如果2个人同时说回音还是有点大,有需要的可以参考下。

    Android平台点对点音视频对讲源码.zip

    4. jrtplib库进行视频音频数据发送,本项目修改jrtplib库,添加了大数据切片功能,方便进行网络传输 5. ffmpeg对接收到的数据进行解码,利用ffmpeg软解码的原因是减少延时,Android硬解码由于机制问题,会存在 缓冲...

    Android通用蓝牙连接协议

    Android扫描蓝牙设备,连接蓝牙设备,读取设备数据,向设备写入数据的方法类。

    新版Android开发教程.rar

    ----------------------------------- Android 编程基础 1 封面----------------------------------- Android 编程基础 2 开放手机联盟 --Open --Open --Open --Open Handset Handset Handset Handset Alliance ...

    蓝牙数据传输技术知识

    Android 4.0 全面而又细致的讲解Blutooth 4.0 OPP 相关的部分。值得一看。

    小程序蓝牙传输

    小程序蓝牙传输,递归调用方便大数据量传输的情况,ios,android使用

    Android 即时通讯开发包

    优化音视频播放模块,音视频同步更精准,缩短...优化服务器缓冲区管理机制,提高服务器流媒体数据传输效率; 修正部分网络环境下操作UPNP设备时程序长时间被阻塞的Bug; 修正GetSDKOption接口获取部分参数失败的Bug。

    Android底层开发技术实战详解--内核、移植和驱动.(电子工业.王振丽).part1

    3.4.3 使用relay实现内核到用户空间的数据传输 66 3.5 三类驱动程序 70 3.5.1 字符设备驱动程序 70 3.5.2 块设备驱动程序 79 3.5.3 网络设备驱动程序 82 第4章 hal层深入分析 84 4.1 ...

    Android底层开发技术实战详解--内核、移植和驱动.(电子工业.王振丽).part3

    3.4.3 使用relay实现内核到用户空间的数据传输 66 3.5 三类驱动程序 70 3.5.1 字符设备驱动程序 70 3.5.2 块设备驱动程序 79 3.5.3 网络设备驱动程序 82 第4章 hal层深入分析 84 4.1 ...

    RtpSDK.zip

    基于rtp协议封装的数据收发sdk可用于android工程或修改后用于java项目,对于大量数据实时传输的应用。目前采用pcm格式语音数据做的测试,数据收发稳定

    android audio system

    Audio 系统在 Android 中负责音频方面的数据流传输和控制功能,也负责音频设备的管理。这个部分作为 Android 的 Audio 系统的输入/输出层次,一般负责播放 PCM 声音输出和从外部获取 PCM 声音,以及管理声音设备和设置...

    Android平台做的文件管理器可以通过WIFI和电脑传数据,加上3D的背景切换

    Android平台做的文件管理器可以通过WIFI和电脑传数据,加上3D的背景切换

    audio-streamer-kotlin-mvp:用Kotlin编写的实时音频流示例Android应用

    音频流媒体实时音频流Android应用程序,使用户可以将音频记录和流式传输到本地服务器,并进行简单的分析。概括这是一个示例应用程序,可让用户录制音频并将其实时流式传输到本地服务器。 作为回报,服务器分析音频并...

    Android平台下通过JNI快速实现音视频通信

    想要在Android平台下实现音视频通信,最快捷的方法是寻找开源项目或调用其他公司封装好的API

    安卓手机数据恢复

    这款Android数据恢复工具可以恢复手机或从其它设备传输的任何照片与视频,除了音乐,文件,档案外,还包括存储在您的Android手机上的SD卡中的资料。进行扫描,检查和恢复,3步即可完成。 下面是一些主要特点: - 从...

Global site tag (gtag.js) - Google Analytics