2. 1.오디오 출력 안드로이드 아나토미 정리
오디오 출력은 크게 3단계로 구성됨.
1)Track생성
2)Track 활성화단계
3)PCM 데이터 출력 요청 단계
Ex)Mediaplayerservice에서의 오디오 출력 예
1)선행단계 -> audiooutput class를 audiosink 함수로 설정 함.
Audioplayer의 audiosink가 불리면 mediaplayerservice의
audiooutput class가 호출 됨.
MediaplayerBase
AudioSink
Mediaplayer MediaplayerService
Interface
AudioOutput setAudioSink(AudioOutput)
stagefrightPlayer
Awesomeplayer
AudioPlayer
2
3. 1.오디오 출력 안드로이드 아나토미 정리
AudioPlayer.cpp
2)Track생성 및 활성화 status_t AudioPlayer::start(bool sourceAlreadyStarted) {
mAudioSink->open(
Mediaplayerservice.cpp
mSampleRate, numChannels,
status_t MediaPlayerService::Client::start()
AudioSystem::PCM_16_BIT,
{
DEFAULT_AUDIOSINK_BUFFERCOUNT,
LOGV("[%d] start", mConnId);
&AudioPlayer::AudioSinkCallback, this);
sp<MediaPlayerBase> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
mAudioSink->start();
p->setLooping(mLoop);
return p->start();
}
}
Stagefrightplayer.cpp status_t MediaPlayerService::AudioOutput::open(){
status_t StagefrightPlayer::start() {
LOGV("start"); t = new AudioTrack(…); Track생성
return mPlayer->play(); }
}
void MediaPlayerService::AudioOutput::start()
status_t AwesomePlayer::play() { {
return play_l(); if (mTrack) {
} mTrack->start(); Track활성화
}
}
status_t AwesomePlayer::play_l() { ssize_t MediaPlayerService::AudioOutput::write
mAudioPlayer = new AudioPlayer(mAudioSink, this); {
mAudioPlayer->start(); if (mTrack) {
ssize_t ret = mTrack->write(buffer, size); PCM데이터
}
return ret; 출력
}
4. 2.트랙 생성 단계 안드로이드 아나토미 정리
AudioTrack.java
Android_media_
AudiioTrack.cpp
Audiotrack.cpp
서비스 클라이언트 서비스 서버
5. 2.트랙 생성 단계(서비스 클라이언트) 안드로이드 아나토미 정리
Audiotrack.java
public AudioTrack(…){
int initResult = native_setup(new WeakReference<AudioTrack>(this),
mStreamType, mSampleRate, mChannels, mAudioFormat,
mNativeBufferSizeInBytes, mDataLoadMode, session);
}
Android_media_audiotrack.cpp
android_media_AudioTrack_native_setup{
lpTrack->set(
atStreamType,// stream type
sampleRateInHertz,
format,// word length, PCM
channels,
frameCount,
0,// flags
audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
0,// shared mem
true,// thread can call Java
sessionId);// audio session ID
}
AudioTrack.cpp
status_t AudioTrack::set(…){
1)출력 스트림 핸들 획득
audio_io_handle_t output = AudioSystem::getOutput((AudioSystem::stream_type)streamType,
sampleRate, format, channels, (AudioSystem::output_flags)flags);
6. 2.트랙 생성 단계(서비스 클라이언트) 안드로이드 아나토미 정리
2)트랙 생성 요청
status_t status = createTrack(streamType, sampleRate, format, channelCount,frameCount, flags, sharedBuffer, output, true);
status_t AudioTrack::createTrack(){
sp<IAudioTrack> track = audioFlinger->createTrack(getpid(),
streamType,
sampleRate,
format,
channelCount,
frameCount,
((uint16_t)flags) << 16,
sharedBuffer,
output,
&mSessionId,
&status);
}
3)트랙 핸들 서비스 프록시 획득
mAudioTrack = track;
mAudioTrack 은 trackhandle 서비스의 proxy를 가리킨다.
즉,이 mAudioTrack 변수를 통해서 trackhandle 서비스로 접근해서 생성된 track을 control한다.
4)오디오 트랙 쓰레드 생성
if (cbf != 0) {
mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
}
7. 2.트랙 생성 단계(서비스 클라이언트) 안드로이드 아나토미 정리
AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava)
: Thread(bCanCallJava), mReceiver(receiver)
{
}
bool AudioTrack::AudioTrackThread::threadLoop()
{
return mReceiver.processAudioBuffer(this);
}
status_t AudioTrack::AudioTrackThread::readyToRun()
{
return NO_ERROR;
}
void AudioTrack::AudioTrackThread::onFirstRef()
{
}
onFirstRef 에 아무 code 도 없기 때문에 readyToRun, threadLoop 이 불리지 않는다.
AudioTrack::start() 에서 t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT); 이 불리면
readyToRun-> threadLoop이 호출 된다.
threadLoop 에서는 AudioTrack::processAudioBuffer를 반복 호출 한다.
tonegenerator AudioPlayer Android_media_audiotrack
Audiocallback() Audiocallback() Audiocallback()
AudioTrackThread
EVENT_MORE_DAT
A
event EVENT_UNDERRU
N
processAudioBuffer
EVENT_NEW_POS
8. 2.트랙 생성 단계(서비스 클라이언트) 안드로이드 아나토미 정리
Ex) EVENT_MORE_DATA 생성 및 처리
1) processAudioBuffer 에서 EVENT_MORE_DATA 생성
-EVENT_MORE_DATA:오디오 버퍼에 PCM 데이터를 쓰도록 요청
status_t err = obtainBuffer(&audioBuffer, 1);
오디오 버퍼를 확보.
mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
확보된 오디오 버퍼를 인자로 EVENT_MORE_DATA event 전달
Audioplayer.cpp에 AudioCallback이 등록된 경우
void AudioPlayer::AudioCallback(int event, void *info) {
if (event != AudioTrack::EVENT_MORE_DATA) {
return;
}
AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
size_t numBytesWritten = fillBuffer(buffer->raw, buffer->size);
buffer->size = numBytesWritten;
}
오디오 버퍼에 전달된 data를 copy한다.
size_t AudioPlayer::fillBuffer(void *data, size_t size) {
memcpy((char *)data + size_done,
(const char *)mInputBuffer->data() + mInputBuffer->range_offset(),
copy);
}
9. 2.트랙 생성 단계(서비스 서버) 안드로이드 아나토미 정리
서비스 클라이언트로 부터 요청 받은 track 생성 요구를 서비스 서버에서 처리하는 과정이다.
sp<IAudioTrack> AudioFlinger::createTrack(…,output..){
PlaybackThread *thread = checkPlaybackThread_l(output); 1)playbackthread 획득
client = new Client(this, pid); 2) 트랙 공유 메모리 생성
track = thread->createTrack_l(client, streamType, sampleRate, format,
channelCount, frameCount, sharedBuffer, lSessionId, &lStatus);
3) 트랙 생성
trackHandle = new TrackHandle(track); 4) 트랙 핸들 리모트 서비스 생성 후 반환
return trackHandle;
1)playbackthread 획득
인자로 들어온 출력스트림(output)을 이용하여 playbackthread를 획득한다.
AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(int output) const
{
PlaybackThread *thread = NULL;
if (mPlaybackThreads.indexOfKey(output) >= 0) {
thread = (PlaybackThread *)mPlaybackThreads.valueFor(output).get();
}
return thread;
}
10. 2.트랙 생성 단계(서비스 서버) 안드로이드 아나토미 정리
2) 트랙 공유 메모리 생성
서비스 클라이언트와 서비스 서버간의 PCM 데이터를 공유하기 위한 공유 메모리 생성
Client 객체가 생성될 때 공유 메모리 생성 됨.
sp<IAudioTrack> AudioFlinger::createTrack{ AudioFlinger::Client::Client()
: mMemoryDealer(
wclient = mClients.valueFor(pid); new MemoryDealer(1024*1024, "AudioFlinger::Client")
Client 생성자는 MemoryDealer를
if (wclient != NULL) { 통해 1Mbytes의 공유 메모리확보
client = wclient.promote();
} else {
client = new Client(this, pid);
mClients.add(pid, client);
}
}
Singletone pattern으로 하나의 process당 하나의 Client만 존재하게 된다.
(즉, application은 audioflinger에 있는 createtrack()함수를 여러 번 호출 할 수 있지만, 하나의 Client만 생성하게 된다.)
AudioTrack++ Audioflinger
write read write android
kernel
Track[3 32kbyte
1]
AudioFlinger::Client
1M Audio
(shared memory)
Driver
Track[0] buffer
11. 2.트랙 생성 단계(서비스 서버) 안드로이드 아나토미 정리
3) 트랙 생성
sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(
track = new Track(this, client, streamType, sampleRate, format,
channelCount, frameCount, sharedBuffer, sessionId);
AudioFlinger::PlaybackThread::Track::Track()
: TrackBase(thread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer, sessionId),
{
…
}
Trackbase의 생성자가 먼저 호출됨.(오디오 트랙 컨트롤 블록 생성)
AudioFlinger::ThreadBase::TrackBase::TrackBase(){
size_t size = sizeof(audio_track_cblk_t);
size_t bufferSize = 0;
if ( (format == AudioSystem::PCM_16_BIT) ||
(format == AudioSystem::PCM_8_BIT))
{
bufferSize = frameCount*channelCount*sizeof(int16_t);
}
size += bufferSize;
mCblkMemory = client->heap()->allocate(size); 1)size 만큼 메모리 할당
mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer());
new(mCblk) audio_track_cblk_t(); 2)할당 된 메모리에 64kbyte의 audio_track_cblk_t 구조체 생성
mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); 3)pcm 데이터 공유 버퍼 설정
memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
mBufferEnd = (uint8_t *)mBuffer + bufferSize;
12. 2.트랙 생성 단계(서비스 서버) 안드로이드 아나토미 정리
Track
frameCount*ch PCM 데이터 mBufferEnd
annelCount* 공유 버퍼
sizeof(int16_t); mBuffer
mCblk
64kbytes 오디오 컨트롤 블록
user
출력 대기중인
struct audio_track_cblk_t PCM 데이터
{ server
User
offset
volatile uint32_t user; Server
volatile uint32_t server; offset
uint32_t userBase;
uint32_t serverBase;
void* buffers; userBase serverBase
uint32_t frameCount;
…
} 오디오 컨트롤 블록
13. 2.트랙 생성 단계(서비스 서버) 안드로이드 아나토미 정리
Useroffset 계산 과정(user-userbase)
-512 프레임씩 write한다고 가정.
-pcm 데이터 공유 버퍼 framecount 3,072
-pcm 데이터 공유 버퍼가 full 인 상태(user가 3,072)에서 write할려고 할 경우
buffer overflow 발생
userbase를 0에서 framecount 인 3,072로 변경
유저오프셋=user(3,072)-userbase(3,072)=0
즉,buffer의 제일 처음 부터 다시 write함.
14. 2.트랙 생성 단계(서비스 서버) 안드로이드 아나토미 정리
-Track class 생성자 실행
AudioFlinger::PlaybackThread::Track::Track()
{
mName = playbackThread->getTrackName_l();트랙 이름(0x1000)부터 시작,1씩 증가함.
mMainBuffer = playbackThread->mixBuffer(); mixerbuffer를 가리킴.
mCblk->frameSize = AudioSystem::isLinearPCM(format) ? channelCount * sizeof(int16_t) : sizeof(int8_t);
}
Audioflinger
Track
mBufferEnd
mBuffer
mCblk
mName Mixer
mMainBuffer buffer
android
kernel
Audio
PCM 데이터 Driver
공유 버퍼 buffer
오디오 컨트롤 블록
15. 2.트랙 생성 단계(트랙 공유 메모리 참조)-서비스 클라이언트 안드로이드 아나토미 정리
서비스 클라이언트가(audiotrack) 트랙 생성을 요청한 이후 서비스 서버측(audioflinger)에 의해
생성된 트랙 공유 메모리를 참조하는 과정
status_t AudioTrack::createTrack()
{
sp<IAudioTrack> track = audioFlinger->createTrack();
sp<IMemory> cblk = track->getCblk(); 1)trackhandle의 getCblk() 호출 함으로써 트랙 공유 메모리의
공유 메모리 서비스 프락시를 얻어온다.
mCblkMemory = cblk;
mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());
트랙 공유 메모리의 오디오 컨트롤 블록을 가리킨다.
mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
pcm 데이터 공유 버퍼를 가리킨다.
}
Track
AudioTrack PCM 데이터
공유 버퍼 mBufferEnd
mBuffer mBuffer
mCblk
mCblk
오디오 컨트롤 블록