• Save
Android audio system(audioflinger)
Upcoming SlideShare
Loading in...5

Android audio system(audioflinger)






Total Views
Views on SlideShare
Embed Views



6 Embeds 386

http://fefe7270.blogspot.kr 327
http://fefe7270.blogspot.com 28
http://collab.lge.com 22
http://dsync.iptime.org 6
http://fefe7270.blogspot.in 2
http://fefe7270.blogspot.dk 1



Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • Hi, I am a beginer in android.
    At the moment I am doing a project, an native C daemon for Android, that needs to
    collect the state of the phone like: is the audio ON (music plays), what is the current
    level of the output sound, is the gps ON, the CPU freq, is the screen ON etc.
    For some of them, I easily found the corresponding files in the sysfs and procfs, and I can get the values. But, for example like for the audio I cannot find this values. From your presentation, I see that I need to use the alsa api.
    Please can you give me some guideline how to do that? How to check if music is running?

    Thank you,
    Are you sure you want to
    Your message goes here
  • This code based on gingerbread code.

    I will upload the data that is include JellyBean code.

    Are you sure you want to
    Your message goes here
  • /* 70 * FIXME: This code needs to instantiate the correct audio device 71 * interface. For now - we use compile-time switches. 72 */ 73 AudioHardwareInterface* hw = 0; 74 char value[PROPERTY_VALUE_MAX]; 75 76#ifdef GENERIC_AUDIO 77 hw = new AudioHardwareGeneric(); 78#else 79 // if running in emulation - use the emulator driver 80 if (property_get('ro.kernel.qemu', value, 0)) { 81 LOGD('Running in emulation - using generic audio driver'); 82 hw = new AudioHardwareGeneric(); 83 } 84 else { 85 LOGV('Creating Vendor Specific AudioHardware'); 86 hw = createAudioHardware(); 87 } 88#endif 89 if (hw->initCheck() != NO_ERROR) { 90 LOGW('Using stubbed audio hardware. No sound will be produced.'); 91 delete hw; 92 hw = new AudioHardwareStub(); 93 } 94 95#ifdef WITH_A2DP 96 hw = new A2dpAudioInterface(hw); 97#endif

    I am not seen this part of in JelleyBean code and I want to know in current code where is this implementation?
    Are you sure you want to
    Your message goes here
Post Comment
Edit your comment

Android audio system(audioflinger) Android audio system(audioflinger) Presentation Transcript

  • 안드로이드의 모든것 분석과 포팅 정리Android Audio System (AudioFlinger) 박철희 1
  • 1.Audio system 전체 구조 안드로이드의 모든것 분석과 포팅 정리Application Media player App Media Recorder App Volume /mode changeApplicationFrameworkNativeFrameworkHALKernel 2
  • 1.Audio HAL의 초기화 안드로이드의 모든것 분석과 포팅 정리 Init.rc service media /system/bin/mediaserver user media group system audio camera graphics inet net_bt net_bt_admin net_raw sdcard_rw Main_mediaserver.cpp int main(int argc, char** argv) { AudioFlinger::instantiate(); AudioPolicyService::instantiate(); } AudioFlinger.h class AudioFlinger : public BinderService<AudioFlinger>, public BnAudioFlinger { friend class BinderService<AudioFlinger>; public: … 3
  • 1.Audio HAL의 초기화 안드로이드의 모든것 분석과 포팅 정리 BinderService.h class BinderService { public: static status_t publish() { sp<IServiceManager> sm(defaultServiceManager()); return sm->addService(String16(SERVICE::getServiceName()), new SERVICE()); } static void instantiate() { publish(); } }AudioFlinger.cppAudioFlinger::AudioFlinger() : BnAudioFlinger(), mAudioHardware(0), mFmEnabled(false), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1){ AudioHardwareInterface* mAudioHardware = AudioHardwareInterface::create(); …} 4
  • 1.Audio HAL의 초기화 안드로이드의 모든것 분석과 포팅 정리AudioHardwareinterface.cpp Audioflinger/android.mkAudioHardwareInterface* AudioHardwareInterface::create() ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true){ LOCAL_CFLAGS += -DGENERIC_AUDIO #ifdef GENERIC_AUDIO hw = new AudioHardwareGeneric();#else // if running in emulation - use the emulator driver BoardConfig.mk if (property_get("ro.kernel.qemu", value, 0)) { # MultiMedia defines hw = new AudioHardwareGeneric(); BOARD_USES_GENERIC_AUDIO := false } BOARD_USES_ALSA_AUDIO := true else { BUILD_WITH_ALSA_UTILS := true hw = createAudioHardware(); BOARD_USES_TI_CAMERA_HAL := true } CAMERA_YUV_FAST_CONVERT := true Android.mk(AudioFlinger)AudioHardwareALSA.cppandroid::AudioHardwareInterface *createAudioHardware(void) { return android::AudioHardwareALSA::create(); libAudio libaudioPolicy } AudioHardwareALSA.cpp AudioPolicyManagerALSA.cpp AudioStreamOutALSA.cpp + libaudiopolicybase AudioStreamInALSA.cpp (AudioPolicyManagerBase.cpp)AudioHardwareInterface *AudioHardwareALSA::create() { ALSAStreamOps.cpp return new AudioHardwareALSA(); ALSAMixer.cpp} ALSAControl.cpp + libaudiointerface (AudioHardwareInterface.cpp) 5
  • 1.Audio HAL의 초기화 안드로이드의 모든것 분석과 포팅 정리AudioHardwareALSA::AudioHardwareALSA() : mALSADevice(0), mTtyMode(TTY_OFF), mAcousticDevice(0) “alsa”{ int err = hw_get_module(ALSA_HARDWARE_MODULE_ID, (hw_module_t const**)&module); ..}int hw_get_module(const char *id, const struct hw_module_t **module){ for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) { if (i < HAL_VARIANT_KEYS_COUNT) { if (property_get(variant_keys[i], prop, NULL) == 0) { --------------(1) continue; “/system/lib/hw” } snprintf(path, sizeof(path), "%s/%s.%s.so“,HAL_LIBRARY_PATH1, id, prop); ---------------(2) if (access(path, R_OK) == 0) break; “/vendor/lib/hw” snprintf(path, sizeof(path), "%s/%s.%s.so“,HAL_LIBRARY_PATH2, id, prop); ---------------(3) if (access(path, R_OK) == 0) break; } else { snprintf(path, sizeof(path), "%s/%s.default.so“,HAL_LIBRARY_PATH1, id); ---------------(4) if (access(path, R_OK) == 0) break; } }if (i < HAL_VARIANT_KEYS_COUNT+1) { status = load(id, path, module); ---------------(5) } 6
  • 1.Audio HAL의 초기화 안드로이드의 모든것 분석과 포팅 정리if (property_get(variant_keys[i], prop, NULL) == 0) { --------------(1) continue;variant_keys property value들을 읽어온다. static const char *variant_keys[] = { "ro.hardware",  제조회사 ex)HTC "ro.product.board",  board명 ex) H "ro.board.platform",  omap3 "ro.arch“ ”” }; snprintf(path, sizeof(path), "%s/%s.%s.so“,HAL_LIBRARY_PATH1, id, prop); ---------------(2) if (access(path, R_OK) == 0) break; /system/lib/hw/alsa.omap3.so가 있는지 찾는다. Target 모델의 경우 /system/lib/hw의 so file들은 아래와 같다. acoustics.default.so alsa.omap3.so gps.omap3.so gralloc.default.so gralloc.omap3.so lights.omap3.so overlay.omap3.so sensors.omap3.so  따라서, path가 alsa.omap3.so 일 경우 break된다. 7
  • 1.Audio HAL의 초기화 안드로이드의 모든것 분석과 포팅 정리snprintf(path, sizeof(path), "%s/%s.%s.so“,HAL_LIBRARY_PATH2, id, prop); ---------------(3)if (access(path, R_OK) == 0) break;/vendor/lib/hw 에 있는 so file들을 찾는다. snprintf(path, sizeof(path), "%s/%s.default.so“,HAL_LIBRARY_PATH1, id); ---------------(4) if (access(path, R_OK) == 0) break; so file을 찾는데 실패 할 경우에는 alsa.default.so 를 load한다. if (i < HAL_VARIANT_KEYS_COUNT+1) { status = load(id, path, module); ---------------(5) } alsa.omap3.so를 load한다. static int load(const char *id,const char *path, const struct hw_module_t **pHmi) { handle = dlopen(path, RTLD_NOW); -------------------------(1-1) const char *sym = HAL_MODULE_INFO_SYM_AS_STR; hmi = (struct hw_module_t *)dlsym(handle, sym);------------(2-1) } handle = dlopen(path, RTLD_NOW); -------------------------(1-1) /system/lib/hw/alsa.omap3.so 를 open한다. 8
  • 1.Audio HAL의 초기화 안드로이드의 모든것 분석과 포팅 정리hmi = (struct hw_module_t *)dlsym(handle, sym);------------(2-1)alsa_omap3.cpp의 HAL_MODULE_INFO_SYM 구조체를 hmi로 가져온다. Alsa_omap3.cpp extern "C" const hw_module_t HAL_MODULE_INFO_SYM = { tag : HARDWARE_MODULE_TAG, version_major : 1, static hw_module_methods_t s_module_methods = { version_minor : 0, open : s_device_open id : ALSA_HARDWARE_MODULE_ID, }; name : "Omap3 ALSA module", author : "Texas Instruments", methods : &s_module_methods, dso : 0, reserved : {0,}, };AudioHardwareALSA::AudioHardwareALSA() : mALSADevice(0), mTtyMode(TTY_OFF), mAcousticDevice(0){ int err = hw_get_module(ALSA_HARDWARE_MODULE_ID, (hw_module_t const**)&module);  module 은 HAL_MODULE_INFO_SYM 를 가리킨다. err = module->methods->open(module, ALSA_HARDWARE_NAME, &device);-------------------(3-1)  HAL_MODULE_INFO_SYM -> methods ->open 즉, s_device_open을 가리킴. if (err == 0) { mALSADevice = (alsa_device_t *)device; mALSADevice->init(mALSADevice, mDeviceList); mALSADevice->init 은 alsa_htc.cpp에 있는 s_init을 호출 함.} 9
  • 1.Audio HAL의 초기화 안드로이드의 모든것 분석과 포팅 정리 static int s_device_open(const hw_module_t* module, const char* name,hw_device_t** device)----(3-2){ … dev->common.close = s_device_close; dev->init = s_init; dev->open = s_open; dev->close = s_close; dev->standby = s_standby; dev->route = s_route;*device = &dev->common;}AudioHardwareinterface.cppAudioHardwareInterface* AudioHardwareInterface::create(){ AudioHardwareALSA.cpp hw = createAudioHardware(); status_t AudioHardwareALSA::initCheck() hw는 AudioHardwareALSA 를 나타냄. { if (mALSADevice && mMixer && mMixer->isValid()) if (hw->initCheck() != NO_ERROR) { return NO_ERROR; hw = new AudioHardwareStub(); else return NO_INIT; } }} 10
  • 2.AudioFlinger class 구조 안드로이드의 모든것 분석과 포팅 정리 BnAudioFlinger AudioFlinger Thread ThreadBase AudioBufferProvider TrackBase BnAudioTrack AudioBufferProvider playbackThread BnAudioRecord Track TrackHandle RecordThread outputTrack RecordTrack RecordHandle MixerThread DirectoutputThread DuplicatingThread 11
  • 3.IAudioFlinger 인터페이스 안드로이드의 모든것 분석과 포팅 정리 IInterface IAudioFlinger BnAudioFlinger BpAudioFlinger virtual sp<IAudioTrack> createTrack, virtual sp<IAudioRecord> openRecord, AudioFlinger virtual int openOutput, setMasterVolume, setMicMute … 12
  • 4.PlaybackThread(MixerThread)의 생성 안드로이드의 모든것 분석과 포팅 정리 Main_mediaserver.cpp int main(int argc, char** argv) { AudioFlinger::instantiate(); AudioPolicyService::instantiate(); } AudioPolicyService.cpp void AudioPolicyService::instantiate() { defaultServiceManager()->addService( String16("media.audio_policy"), new AudioPolicyService()); } AudioPolicyService.cpp AudioPolicyService::AudioPolicyService() : BnAudioPolicyService() , mpPolicyManager(NULL) { …. mpPolicyManager = createAudioPolicyManager(this); } AudioPolicyManagerALSA.cpp extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface) { return new AudioPolicyManagerALSA(clientInterface); } 13
  • 4.PlaybackThread(MixerThread)의 생성 안드로이드의 모든것 분석과 포팅 정리 AudioPolicyManagerALSA 생성자 호출 AudioPolicyManagerALSA::AudioPolicyManagerALSA(AudioPolicyClientInterface *clientInterface) : AudioPolicyManagerBase(clientInterface) { } AudioPolicyManagerALSA 는 AudioPolicyManagerBase로 부터 상속 받기 때문에 AudioPolicyManagerBase의 생성자를 호출한다.AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface){ mpClientInterface = clientInterface; mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE | AudioSystem::DEVICE_OUT_SPEAKER; mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC; mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice, &outputDesc->mSamplingRate, &outputDesc->mFormat, &outputDesc->mChannels, &outputDesc->mLatency, outputDesc->mFlags);} mpClientInterface는 AudioPolicyService를 나타냄으로 AuidoPolicyService.cpp의 openOutput method를 호출한다. 14
  • 4.PlaybackThread(MixerThread)의 생성 안드로이드의 모든것 분석과 포팅 정리AuidoPolicyService.cppaudio_io_handle_t AudioPolicyService::openOutput(…){ { sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); af는 BpAudioFlinger를 가리킨다. return af->openOutput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels, pLatencyMs, flags);} AudioFlinger.cpp int AudioFlinger::openOutput(…) { … AudioStreamOut *output = mAudioHardware->openOutputStream(*pDevices, (int *)&format, &channels, &samplingRate, &status); mAudioHardware 는 AudioHardwareALSA 를 가리킨다. AudioHardwareALSA::openOutputStream는 alsa_omap3.cpp 에 있는 s_open을 호출해서 PCM device를 open하고 HAL의 데이터 출력 Class인 AudioStreamOutALSA를 생성한다. int id = nextUniqueId();  unique한 id를 생성하고 thread = new MixerThread(this, output, id, *pDevices); 그 id로 MixerThread를 생성한다. .. } 15
  • 4.PlaybackThread(MixerThread)의 생성 안드로이드의 모든것 분석과 포팅 정리AudioFlinger::MixerThread::MixerThread(…) : PlaybackThread(audioFlinger, output, id, device), mAudioMixer(0){ mType = PlaybackThread::MIXER; mAudioMixer = new AudioMixer(mFrameCount, mSampleRate); AudioMixer class 생성} PlaybackThread 생성자 호출 AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device) : ThreadBase(audioFlinger, id), … ThreadBase생성자 호출AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id) : Thread(false), mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0), mFrameSize(1), mFormat(0), mStandby(false), mId(id), mExiting(false){} MixerThread id를 mid에 저장한다. 16
  • 4.PlaybackThread(MixerThread)의 생성 안드로이드의 모든것 분석과 포팅 정리AudioFlinger.cppint AudioFlinger::openOutput(…) DefaultKeyedVector< int, sp<PlaybackThread> > mPlaybackThreads;{ … mPlaybackThreads.add(id, thread); 생성된 MixerThread를 mPlaybackThreads 에 추가한다. return id; MixerThread id를 return한다.} AudioFlinger Threadbase playbackThread MixerThread AudioHardwareInterface AudioStreamOutALSA 17
  • 5.RecordThread의 생성 안드로이드의 모든것 분석과 포팅 정리Soundrecorder.java MediaPlayerService.cppmRecorder = new Recorder(); sp<IMediaRecorder>mRecorder.startRecording MediaPlayerService::createMediaRecorder(pid_t pid) { sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid);Recorder.java }public void startRecording{.. mRecorder = new MediaRecorder();mRecorder.start(); MediaRecorderClient::MediaRecorderClient(…) { MediaRecorder.java mRecorder = new StagefrightRecorder; public class MediaRecorder{ } native_setup(new WeakReference<MediaRecorder>(this)); } JNI Android_media_MediaRecorder.cpp static void StagefrightRecorder::StagefrightRecorder() android_media_MediaRecorder_native_setup{ : mWriter(NULL), sp<MediaRecorder> mr = new MediaRecorder(); mOutputFd(-1) { } LOGV("Constructor"); reset(); MediaRecorder.cpp } MediaRecorder::MediaRecorder(){ const sp<IMediaPlayerService>& service(getMediaPlayerService()); if (service != NULL) { mMediaRecorder = service->createMediaRecorder(getpid()); } 18
  • 5.RecordThread의 생성 안드로이드의 모든것 분석과 포팅 정리 Recorder.java StagefrightRecorder.cpp public void startRecording{ sp<MediaSource> StagefrightRecorder::createAudioSource() .. { mRecorder.start(); … sp<AudioSource> audioSource = new AudioSource( mAudioSource, mSampleRate, status_t StagefrightRecorder::start() { mAudioChannels); … } case OUTPUT_FORMAT_DEFAULT: case OUTPUT_FORMAT_THREE_GPP: case OUTPUT_FORMAT_MPEG_4: return startMPEG4Recording(); AudioSource.cpp AudioSource::AudioSource(…) {status_t StagefrightRecorder::startMPEG4Recording() { …sp<MediaWriter> writer = new MPEG4Writer(dup(mOutputFd)); mRecord = new AudioRecord(err = setupAudioEncoder(writer); inputSource, sampleRate, AudioSystem::PCM_16_BIT,status_t StagefrightRecorder::setupAudioEncoder(const channels > 1?sp<MediaWriter>& writer) AudioSystem::CHANNEL_IN_STEREO:{ AudioSystem::CHANNEL_IN_MONO, … 4 * kMaxBufferSize / sizeof(int16_t), switch(mAudioEncoder) { flags); case AUDIO_ENCODER_AMR_NB: } case AUDIO_ENCODER_AMR_WB: case AUDIO_ENCODER_AAC: audioEncoder = createAudioSource(); 19
  • 5.RecordThread의 생성 안드로이드의 모든것 분석과 포팅 정리AudioRecord.cppAudioRecord::AudioRecord(…){{ mStatus = set(inputSource, sampleRate, format, channels, frameCount, flags, cbf, user, notificationFrames, sessionId);} status_t AudioRecord::set(…) { audio_io_handle_t input = AudioSystem::getInput(inputSource, sampleRate, format, channels, (AudioSystem::audio_in_acoustics)flags); } AudioSystem.cpp audio_io_handle_t AudioSystem::getInput(int inputSource, uint32_t samplingRate, uint32_t format, uint32_t channels, audio_in_acoustics acoustics) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return 0; return aps->getInput(inputSource, samplingRate, format, channels, acoustics); } 20
  • 5.RecordThread의 생성 안드로이드의 모든것 분석과 포팅 정리 audio_io_handle_t AudioPolicyService::getInput(…) { Mutex::Autolock _l(mLock); return mpPolicyManager->getInput(inputSource, samplingRate, format, channels, acoustics); } mpPolicyManager는 AudioPolicyManagerALSA class를 가리키는데 이 class는 AudioPolicyManagerBase를 상속받는다. 그래서 getInput method는 AudioPolicyManagerBase에 정의된 것을 사용한다. AudioPolicyManagerBase.cpp audio_io_handle_t AudioPolicyManagerBase::getInput(…){ inputDesc->mInputSource = inputSource; inputDesc->mDevice = device; inputDesc->mSamplingRate = samplingRate; inputDesc->mFormat = format; inputDesc->mChannels = channels; inputDesc->mAcoustics = acoustics; inputDesc->mRefCount = 0; input = mpClientInterface->openInput(&inputDesc->mDevice, &inputDesc->mSamplingRate, &inputDesc->mFormat, &inputDesc->mChannels, inputDesc->mAcoustics); } AudioPolicyService.cpp audio_io_handle_t AudioPolicyService::openInput(…) { sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); return af->openInput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels, acoustics); } 21
  • 5.RecordThread의 생성 안드로이드의 모든것 분석과 포팅 정리int AudioFlinger::openInput(…){ … AudioStreamIn *input = mAudioHardware->openInputStream(*pDevices, (int *)&format, &channels, &samplingRate, &status,mAudioHardware 는 AudioHardwareALSA 를 가리킨다. AudioHardwareALSA::openInputStream는 alsa_omap3.cpp 에 있는 s_open을 호출해서 PCM device를 open하고 HAL 의AudioStreamInALSA를 생성한다.int id = nextUniqueId(); unique한 id를 생성하고thread = new RecordThread(this, input, reqSamplingRate, reqChannels, id);  그 id로 RecordThread생성mRecordThreads.add(id, thread);생성된 RecordThread를 mRecordThreads에 추가한다. AudioFlingerreturn id; RecordThread id를 return함. AudioBufferProvider RecordThread AudioHardwareInterface AudioStreamInALSA 22
  • 6. DuplicatingThread의 생성 안드로이드의 모든것 분석과 포팅 정리안드로이드 시스템이 두 개의 디바이스로 동시에 출력 할 경우 DuplicatingThread 를 사용한다.AudioService.javaAudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE, address);AudioSystem.cppstatus_t AudioSystem::setDeviceConnectionState(audio_devices device, device_connection_state state, const char *device_address){ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; return aps->setDeviceConnectionState(device, state, device_address);}AudioPolicyService.cppstatus_t AudioPolicyService::setDeviceConnectionState(…){ … return mpPolicyManager->setDeviceConnectionState(device, state, device_address);} AudioPolicyManagerALSA.cpp status_t AudioPolicyManagerALSA::setDeviceConnectionState(…) { … if (AudioSystem::isA2dpDevice(device)) { status_t status = handleA2dpConnection(device, device_address); 23
  • 6. DuplicatingThread의 생성 안드로이드의 모든것 분석과 포팅 정리AudioPolicyMangerALSA.cppstatus_t AudioPolicyManagerALSA::setDeviceConnectionState(…){ … if (AudioSystem::isA2dpDevice(device)) { status_t status = handleA2dpConnection(device, device_address); }}status_t AudioPolicyManagerBase::handleA2dpConnection(…){ … mA2dpOutput = mpClientInterface->openOutput(&outputDesc->mDevice, &outputDesc->mSamplingRate, &outputDesc->mFormat, &outputDesc->mChannels, &outputDesc->mLatency, outputDesc->mFlags);Bluetooth 디바이스 출력을 위한 playbackthread(mixerthread)생성(mPlaybackThreads에 add 됨.)mDuplicatedOutput = mpClientInterface->openDuplicateOutput(mA2dpOutput, mHardwareOutput);}audio_io_handle_t AudioPolicyService::openDuplicateOutput(…){sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();return af->openDuplicateOutput(output1, output2);} 24
  • 6. DuplicatingThread의 생성 안드로이드의 모든것 분석과 포팅 정리int AudioFlinger::openDuplicateOutput(int output1, int output2){ .. MixerThread *thread1 = checkMixerThread_l(output1); output1은 mA2dpOutput 을 가리 킴. 즉, handleA2dpConnection에서 생성한 bluetooth mixerthread를 mPlaybackThreads 에서 가져 옴.MixerThread *thread2 = checkMixerThread_l(output2);output2는 mHardwareOutput를 가리킴. 기존에 생성되어진 mixerthread를 가리킴.DuplicatingThread *thread = new DuplicatingThread(this, thread1, id);DuplicatingThread를 생성하면서 thread1(bluetooth thread)를 moutputTracks에 add한다.thread->addOutputTrack(thread2);DuplicatingThread의 moutputTracks에 기존의 mixerthread를 add한다.mPlaybackThreads.add(id, thread);mPlaybackThreads.add에 DuplicatingThread를 add한다...} AudioFlinger mPlaybackThreads DuplicatingThread BT mixerthread 기존 mixerthread 25