2013-05-07 31 views
24

iOS 6.1.3 iPad2と新しいiPadでSIPオーディオストリーミングアプリを実行しています。ヘッドフォンの抜き差し時にiOSアプリがクラッシュする

私のiPad(何も差し込まれていない)に私のアプリを起動します。
オーディオが動作します。
ヘッドフォンを接続します。
アプリのクラッシュ:malloc関数:オブジェクト0xのためのエラーが....:ポインタが解放さを割り当てられていないか、またはEXC_BAD_ACCESS

:ヘッドフォンが接続して

私は(私のiPad上で自分のアプリケーションを起動します)。
ヘッドフォンから音声が出力されます。
ヘッドフォンのプラグを抜きます。
アプリクラッシュ:malloc関数:オブジェクト0Xのエラー....:解放されるポインタがを割り当てられていないか、EXC_BAD_ACCESS

アプリケーションコードがhttp://code.google.com/p/ios-coreaudio-example/サンプルコード(下記参照)に基づいて、AudioUnitのAPIを採用しています。

私は変更の意識を取得するためにkAudioSessionProperty_AudioRouteChangeコールバックを使用します。だから、OSのサウンドマネージャーあちこちに3つのコールバックがあります。
1)プロセスを記録マイクサンプル
2)はテストをたくさんした後、スピーカー
3)通知オーディオHWの存在

のためのサンプルが提供する私の気持ちは難しいということですコードはマイクキャプチャを実行するコードです。プラグ/アンプルアクションの後、RouteChangeが呼び出される前にレコーディングコールバックが何度か呼び出されて、後で 'セグメンテーションフォールト'が発生し、RouteChangeコールバックが呼び出されないことがほとんどです。より具体的には、AudioUnitRender関数は例外がスローされない間に 'メモリの不正なアクセス'を引き起こすと思います。

私の気持ちは、デバイスを音に関連する構造のOS更新と非アトミック記録コールバックコードのレースということです。したがって、非アトミックなものは、録音コールバックがOS HW更新と録音コールバックの並行性よりも高い可能性があります。

私は可能な限り薄く記録コールバックを残すために私のコードを変更しますが、私の気持ちは、私のアプリの他のスレッドによってもたらされる高い処理負荷が前述の同時実行性のレースを供給していることです。したがって、AudioUnitRenderの不正アクセスのために、コードの他の部分でmalloc/freeエラーが発生します。

は私がして記録をコールバック待ち時間を削減しようとした:

UInt32 numFrames = 256; 
UInt32 dataSize = sizeof(numFrames); 

AudioUnitSetProperty(audioUnit, 
    kAudioUnitProperty_MaximumFramesPerSlice, 
    kAudioUnitScope_Global, 
    0, 
    &numFrames, 
    dataSize); 

と私は問題のあるコードを後押ししようとした:

dispatch_async(dispatch_get_main_queue(), ^{ 

誰もがそのためのヒントや解決策を持っていますか?ここでエラーを再現するために は私のオーディオセッションコードです:

// 
// IosAudioController.m 
// Aruts 
// 
// Created by Simon Epskamp on 10/11/10. 
// Copyright 2010 __MyCompanyName__. All rights reserved. 
// 

#import "IosAudioController.h" 
#import <AudioToolbox/AudioToolbox.h> 

#define kOutputBus 0 
#define kInputBus 1 

IosAudioController* iosAudio; 

void checkStatus(int status) { 
    if (status) { 
     printf("Status not 0! %d\n", status); 
     // exit(1); 
    } 
} 

/** 
* This callback is called when new audio data from the microphone is available. 
*/ 
static OSStatus recordingCallback(void *inRefCon, 
    AudioUnitRenderActionFlags *ioActionFlags, 
    const AudioTimeStamp *inTimeStamp, 
    UInt32 inBusNumber, 
    UInt32 inNumberFrames, 
    AudioBufferList *ioData) { 

    // Because of the way our audio format (setup below) is chosen: 
    // we only need 1 buffer, since it is mono 
    // Samples are 16 bits = 2 bytes. 
    // 1 frame includes only 1 sample 

    AudioBuffer buffer; 

    buffer.mNumberChannels = 1; 
    buffer.mDataByteSize = inNumberFrames * 2; 
    buffer.mData = malloc(inNumberFrames * 2); 

    // Put buffer in a AudioBufferList 
    AudioBufferList bufferList; 
    bufferList.mNumberBuffers = 1; 
    bufferList.mBuffers[0] = buffer; 

    NSLog(@"Recording Callback 1 0x%x ? 0x%x",buffer.mData, 
     bufferList.mBuffers[0].mData); 

    // Then: 
    // Obtain recorded samples 

    OSStatus status; 
    status = AudioUnitRender([iosAudio audioUnit], 
     ioActionFlags, 
     inTimeStamp, 
     inBusNumber, 
     inNumberFrames, 
     &bufferList); 
     checkStatus(status); 

    // Now, we have the samples we just read sitting in buffers in bufferList 
    // Process the new data 
    [iosAudio processAudio:&bufferList]; 

    NSLog(@"Recording Callback 2 0x%x ? 0x%x",buffer.mData, 
     bufferList.mBuffers[0].mData); 

    // release the malloc'ed data in the buffer we created earlier 
    free(bufferList.mBuffers[0].mData); 

    return noErr; 
} 

/** 
* This callback is called when the audioUnit needs new data to play through the 
* speakers. If you don't have any, just don't write anything in the buffers 
*/ 
static OSStatus playbackCallback(void *inRefCon, 
    AudioUnitRenderActionFlags *ioActionFlags, 
    const AudioTimeStamp *inTimeStamp, 
    UInt32 inBusNumber, 
    UInt32 inNumberFrames, 
    AudioBufferList *ioData) { 
     // Notes: ioData contains buffers (may be more than one!) 
     // Fill them up as much as you can. 
     // Remember to set the size value in each 
     // buffer to match how much data is in the buffer. 

    for (int i=0; i < ioData->mNumberBuffers; i++) { 
     // in practice we will only ever have 1 buffer, since audio format is mono 
     AudioBuffer buffer = ioData->mBuffers[i]; 

     // NSLog(@" Buffer %d has %d channels and wants %d bytes of data.", i, 
      buffer.mNumberChannels, buffer.mDataByteSize); 

     // copy temporary buffer data to output buffer 
     UInt32 size = min(buffer.mDataByteSize, 
      [iosAudio tempBuffer].mDataByteSize); 

     // dont copy more data then we have, or then fits 
     memcpy(buffer.mData, [iosAudio tempBuffer].mData, size); 
     // indicate how much data we wrote in the buffer 
     buffer.mDataByteSize = size; 

     // uncomment to hear random noise 
     /* 
     * UInt16 *frameBuffer = buffer.mData; 
     * for (int j = 0; j < inNumberFrames; j++) { 
     *  frameBuffer[j] = rand(); 
     * } 
     */ 
    } 

    return noErr; 
} 

@implementation IosAudioController 
@synthesize audioUnit, tempBuffer; 

void propListener(void *inClientData, 
    AudioSessionPropertyID inID, 
    UInt32 inDataSize, 
    const void *inData) { 

    if (inID == kAudioSessionProperty_AudioRouteChange) { 

     UInt32 isAudioInputAvailable; 
     UInt32 size = sizeof(isAudioInputAvailable); 
     CFStringRef newRoute; 
     size = sizeof(CFStringRef); 

     AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &size, &newRoute); 

     if (newRoute) { 
      CFIndex length = CFStringGetLength(newRoute); 
      CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, 
       kCFStringEncodingUTF8); 

      char *buffer = (char *)malloc(maxSize); 
      CFStringGetCString(newRoute, buffer, maxSize, 
       kCFStringEncodingUTF8); 

      //CFShow(newRoute); 
      printf("New route is %s\n",buffer); 

      if (CFStringCompare(newRoute, CFSTR("HeadsetInOut"), NULL) == 
       kCFCompareEqualTo) // headset plugged in 
      { 
       printf("Headset\n"); 
      } else { 
       printf("Another device\n"); 

       UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker; 
       AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, 
        sizeof (audioRouteOverride),&audioRouteOverride); 
      } 
      printf("New route is %s\n",buffer); 
      free(buffer); 
     } 
     newRoute = nil; 
    } 
} 

/** 
* Initialize the audioUnit and allocate our own temporary buffer. 
* The temporary buffer will hold the latest data coming in from the microphone, 
* and will be copied to the output when this is requested. 
*/ 
- (id) init { 
    self = [super init]; 
    OSStatus status; 

    // Initialize and configure the audio session 
    AudioSessionInitialize(NULL, NULL, NULL, self); 

    UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord; 
    AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, 
     sizeof(audioCategory), &audioCategory); 
    AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, 
     propListener, self); 

    Float32 preferredBufferSize = .020; 
    AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, 
     sizeof(preferredBufferSize), &preferredBufferSize); 

    AudioSessionSetActive(true); 

    // Describe audio component 
    AudioComponentDescription desc; 
    desc.componentType = kAudioUnitType_Output; 
    desc.componentSubType = 
     kAudioUnitSubType_VoiceProcessingIO/*kAudioUnitSubType_RemoteIO*/; 
    desc.componentFlags = 0; 
    desc.componentFlagsMask = 0; 
    desc.componentManufacturer = kAudioUnitManufacturer_Apple; 

    // Get component 
    AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc); 

    // Get audio units 
    status = AudioComponentInstanceNew(inputComponent, &audioUnit); 
    checkStatus(status); 

    // Enable IO for recording 
    UInt32 flag = 1; 
    status = AudioUnitSetProperty(audioUnit, 
     kAudioOutputUnitProperty_EnableIO, 
     kAudioUnitScope_Input, 
     kInputBus, 
     &flag, 
     sizeof(flag)); 
     checkStatus(status); 

    // Enable IO for playback 
    flag = 1; 
    status = AudioUnitSetProperty(audioUnit, 
     kAudioOutputUnitProperty_EnableIO, 
     kAudioUnitScope_Output, 
     kOutputBus, 
     &flag, 
     sizeof(flag)); 

    checkStatus(status); 

    // Describe format 
    AudioStreamBasicDescription audioFormat; 
    audioFormat.mSampleRate = 8000.00; 
    //audioFormat.mSampleRate = 44100.00; 
    audioFormat.mFormatID = kAudioFormatLinearPCM; 
    audioFormat.mFormatFlags = 
     kAudioFormatFlagsCanonical/* kAudioFormatFlagIsSignedInteger | 
     kAudioFormatFlagIsPacked*/; 
    audioFormat.mFramesPerPacket = 1; 
    audioFormat.mChannelsPerFrame = 1; 
    audioFormat.mBitsPerChannel = 16; 
    audioFormat.mBytesPerPacket = 2; 
    audioFormat.mBytesPerFrame = 2; 

    // Apply format 
    status = AudioUnitSetProperty(audioUnit, 
     kAudioUnitProperty_StreamFormat, 
     kAudioUnitScope_Output, 
     kInputBus, 
     &audioFormat, 
     sizeof(audioFormat)); 

    checkStatus(status); 
    status = AudioUnitSetProperty(audioUnit, 
     kAudioUnitProperty_StreamFormat, 
     kAudioUnitScope_Input, 
     kOutputBus, 
     &audioFormat, 
     sizeof(audioFormat)); 

    checkStatus(status); 


    // Set input callback 
    AURenderCallbackStruct callbackStruct; 
    callbackStruct.inputProc = recordingCallback; 
    callbackStruct.inputProcRefCon = self; 
    status = AudioUnitSetProperty(audioUnit, 
     AudioOutputUnitProperty_SetInputCallback, 
     kAudioUnitScope_Global, 
     kInputBus, 
     &callbackStruct, 
     sizeof(callbackStruct)); 

    checkStatus(status); 
    // Set output callback 
    callbackStruct.inputProc = playbackCallback; 
    callbackStruct.inputProcRefCon = self; 
    status = AudioUnitSetProperty(audioUnit, 
     kAudioUnitProperty_SetRenderCallback, 
     kAudioUnitScope_Global, 
     kOutputBus, 
     &callbackStruct, 
     sizeof(callbackStruct)); 

    checkStatus(status); 

    // Disable buffer allocation for the recorder (optional - do this if we want to 
    // pass in our own) 

    flag = 0; 
    status = AudioUnitSetProperty(audioUnit, 
     kAudioUnitProperty_ShouldAllocateBuffer, 
     kAudioUnitScope_Output, 
     kInputBus, 
     &flag, 
     sizeof(flag)); 


    flag = 0; 
    status = AudioUnitSetProperty(audioUnit, 
    kAudioUnitProperty_ShouldAllocateBuffer, 
     kAudioUnitScope_Output, 
     kOutputBus, 
     &flag, 
     sizeof(flag)); 

    // Allocate our own buffers (1 channel, 16 bits per sample, thus 16 bits per 
    // frame, thus 2 bytes per frame). 
    // Practice learns the buffers used contain 512 frames, 
    // if this changes it will be fixed in processAudio. 
    tempBuffer.mNumberChannels = 1; 
    tempBuffer.mDataByteSize = 512 * 2; 
    tempBuffer.mData = malloc(512 * 2); 

    // Initialise 
    status = AudioUnitInitialize(audioUnit); 
    checkStatus(status); 

    return self; 
} 

/** 
* Start the audioUnit. This means data will be provided from 
* the microphone, and requested for feeding to the speakers, by 
* use of the provided callbacks. 
*/ 
- (void) start { 
    OSStatus status = AudioOutputUnitStart(audioUnit); 
    checkStatus(status); 
} 

/** 
* Stop the audioUnit 
*/ 
- (void) stop { 
    OSStatus status = AudioOutputUnitStop(audioUnit); 
    checkStatus(status); 
} 

/** 
* Change this function to decide what is done with incoming 
* audio data from the microphone. 
* Right now we copy it to our own temporary buffer. 
*/ 
- (void) processAudio: (AudioBufferList*) bufferList { 
    AudioBuffer sourceBuffer = bufferList->mBuffers[0]; 

    // fix tempBuffer size if it's the wrong size 
    if (tempBuffer.mDataByteSize != sourceBuffer.mDataByteSize) { 
     free(tempBuffer.mData); 
     tempBuffer.mDataByteSize = sourceBuffer.mDataByteSize; 
     tempBuffer.mData = malloc(sourceBuffer.mDataByteSize); 
    } 

    // copy incoming audio data to temporary buffer 
    memcpy(tempBuffer.mData, bufferList->mBuffers[0].mData, 
     bufferList->mBuffers[0].mDataByteSize); 
    usleep(1000000); // <- TO REPRODUCE THE ERROR, CONCURRENCY MORE LIKELY 

} 

/** 
* Clean up. 
*/ 
- (void) dealloc { 
    [super dealloc]; 
    AudioUnitUninitialize(audioUnit); 
    free(tempBuffer.mData); 
} 

@end 
+1

あなたはmalloc_error_break' 'にブレークポイントを追加してみました - それはあなたが二度解放されていますポインタを与える必要があります。 – Mar0ux

+2

'char * buffer =(char *)malloc(maxSize);'がリークしていますか?あなたはそのコードを必要としません.btw - 'CFStringRef'は' NSString'へのフリーダイヤルブリッジなので、単に 'newRoute'を' NSString * 'に型キャストして' NSString'メソッドを使うだけです。 – Mar0ux

+1

私はテストコードに含めるのを忘れました@ Mar0uxハイライトの問題の修正を提供しましたが、私のアプリではそれらはすでに修正済みです( 'free(buffer);'と 'newRoute = nil;')。エラーは_malloc:オブジェクト0xのエラーです....:ポインタは解放されませんでした。他の回はmemcpyのエラーEXC_BAD_ACCESSです。 –

答えて

8

私のテストによると、SEGVエラーをトリガラインは、最終的に

AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, 
            sizeof (audioRouteOverride),&audioRouteOverride); 

のAudioUnitチェーン半ばのプロパティを変更されました-flightは常に厄介ですが、を再ルーティングする前にAudioUnitを停止し、再度起動すると、保存されているすべてのバッファを使い切ってから、新しいパラメータを使用し続けます。

これは受け入れ可能なのでしょうか、ルートの変更と録画の再開の間にギャップが必要ですか?私が何をしたか

されました:

void propListener(void *inClientData, 
       AudioSessionPropertyID inID, 
       UInt32 inDataSize, 
       const void *inData) { 

[iosAudio stop]; 
// ... 

[iosAudio start]; 
} 

私のiPhone 5でのこれ以上のクラッシュを

私はその動作のために持っている最も論理的な説明、多少これらによってサポートされます(あなたの走行距離は異なるハードウェアと異なる場合があります)テストは、レンダリングパイプが非同期であることです。あなたのバッファを永遠に操作すると、キューに入れられたままになります。しかし、AudioUnitの設定を変更すると、未知の副作用を伴うレンダリングキューで一括リセットが発生します。問題は、これらの変更が同期的であることです。これは、すべての非同期呼び出しが順番に忍耐強く待っている遡及的な方法に影響します。

あなたが逃したサンプルを気にしないならば、あなたのような何かを行うことができます。

static BOOL isStopped = NO; 
static OSStatus recordingCallback(void *inRefCon, //... 
{ 
    if(isStopped) { 
    NSLog(@"Stopped, ignoring"); 
    return noErr; 
    } 
    // ... 
} 

static OSStatus playbackCallback(void *inRefCon, //... 
{ 
    if(isStopped) { 
    NSLog(@"Stopped, ignoring"); 
    return noErr; 
    } 
    // ... 
} 

// ... 

/** 
* Start the audioUnit. This means data will be provided from 
* the microphone, and requested for feeding to the speakers, by 
* use of the provided callbacks. 
*/ 
- (void) start { 
    OSStatus status = AudioOutputUnitStart(_audioUnit); 
    checkStatus(status); 

    isStopped = NO; 
} 

/** 
* Stop the audioUnit 
*/ 
- (void) stop { 

    isStopped = YES; 

    OSStatus status = AudioOutputUnitStop(_audioUnit); 
    checkStatus(status); 
} 

// ... 
+0

ありがとう@krug、あなたの提案はこのコードの問題を修正しました。しかし、私のアプリでは、録音と再生のコールバックは、プラグイン/アンプラグのアクティビティの後、 'propListener'コールバックが呼び出される前に3-4回呼ばれます。これらの記録再生コールバックの内部では、いくつかの外部構造体ポインタは、 'propListener'コールバックが呼び出されるまで、それらのポインタの変更が停止しても、ダメージは既に行われています。だから、コミュニケーションの終わりに、後で_errorポインタが解放され、コードが外部構造を破壊するときには割り付けられませんでした。 –

+1

解決策は、すべてのサンプルが処理されたらロックを解除するために、停止直後にプロセスをロックアウトすることです。 – krug

+0

こんにちは@krug、私はすでに@synchronized(iosAudio){...}(ミューテックスのような)で録音と再生のコールバックを保護しようとしました。回答の変更は同じ結果に含まれます。 –

関連する問題