Successfully reported this slideshow.

Android audio system(오디오 출력-트랙생성)

5,905 views

Published on

Published in: Technology
  • Be the first to comment

Android audio system(오디오 출력-트랙생성)

  1. 1. 안드로이드 아나토미 정리 Android Audio System(오디오 출력-트랙생성) 박철희 1
  2. 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. 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. 4. 2.트랙 생성 단계 안드로이드 아나토미 정리 AudioTrack.java Android_media_ AudiioTrack.cpp Audiotrack.cpp 서비스 클라이언트 서비스 서버
  5. 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. 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. 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. 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. 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. 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. 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. 12. 2.트랙 생성 단계(서비스 서버) 안드로이드 아나토미 정리 TrackframeCount*ch PCM 데이터 mBufferEndannelCount* 공유 버퍼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. 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. 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. 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 오디오 컨트롤 블록

×