oboe: Normally-working code crashes with: signal 11 (SIGSEGV), code 2 (SEGV_ACCERR) (AudioRecord)

Hi, I am new to Oboe but am working on a cross-platform audio app which uses it for the Android version. I just bought a (relatively) new Android phone for testing, and was surprised to see that Oboe seems to have some issue with it. (This app is working fine on a variety of other phones.)

For reference, the Android-specific code for the app is maintained here. Please let me know if I need to supply more details, thanks!

Android version(s): 11 – UPDATE: Actually version 12 Android device(s): Xiaomi Redmi Note 10 Pro Oboe version: 1.6.2 App name used for testing: koord-rt

Short description When an audio stream is opened for recording, the app exits suddenly with: F libc : Fatal signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7c45df2000 in tid 20486 (AudioRecord), pid 20231 (e.koord.koordrt)

See below for more debug logs.

Steps to reproduce

  • connect to a live session, which opens device for recording

Expected behavior

  • audio stream is opened without problem

Actual behavior

  • app crashes - exits without message

Device Xiaomi Redmi Note 10 Pro

Crash details from Google Play console:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
pid: 0, tid: 0 >>> live.koord.koordrt <<<

backtrace:
  #00  pc 000000000008797c  /apex/com.android.runtime/lib64/bionic/libc.so (memset+156)
  #00  pc 00000000001401a4  /data/app/~~3UZEfri9poPsW3XvET5Pxw==/live.koord.koordrt-iFacayaQEnlDg63jR80yDg==/lib/arm64/libKoord-RT_arm64-v8a.so (CSound::onAudioInput(oboe::AudioStream*, void*, int)+108)
  #00  pc 000000000014080c  /data/app/~~3UZEfri9poPsW3XvET5Pxw==/live.koord.koordrt-iFacayaQEnlDg63jR80yDg==/lib/arm64/libKoord-RT_arm64-v8a.so (non-virtual thunk to CSound::onAudioReady(oboe::AudioStream*, void*, int)+152)
  #00  pc 0000000000144fa0  /data/app/~~3UZEfri9poPsW3XvET5Pxw==/live.koord.koordrt-iFacayaQEnlDg63jR80yDg==/lib/arm64/libKoord-RT_arm64-v8a.so (oboe::AudioStream::fireDataCallback(void*, int)+96)
  #00  pc 00000000001434e0  /data/app/~~3UZEfri9poPsW3XvET5Pxw==/live.koord.koordrt-iFacayaQEnlDg63jR80yDg==/lib/arm64/libKoord-RT_arm64-v8a.so (oboe::AudioStreamAAudio::callOnAudioReady(AAudioStreamStruct*, void*, int)+40)
  #00  pc 0000000000022428  /system/lib64/libaaudio_internal.so (aaudio::AudioStream::maybeCallDataCallback(void*, int)+196)
  #00  pc 0000000000024d04  /system/lib64/libaaudio_internal.so (aaudio::AudioStreamLegacy::callDataCallbackFrames(unsigned char*, int)+308)
  #00  pc 000000000002df60  /system/lib64/libaaudio_internal.so (FixedBlockWriter::processVariableBlock(unsigned char*, int)+336)
  #00  pc 0000000000025518  /system/lib64/libaaudio_internal.so (aaudio::AudioStreamLegacy::processCallbackCommon(int, void*)+896)
  #00  pc 000000000005ac3c  /system/lib64/libaudioclient.so (android::AudioRecord::processAudioBuffer()+1308)
  #00  pc 000000000005a428  /system/lib64/libaudioclient.so (android::AudioRecord::AudioRecordThread::threadLoop()+288)
  #00  pc 0000000000013654  /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+464)
  #00  pc 00000000000bf4b8  /system/lib64/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+148)
  #00  pc 0000000000012de8  /system/lib64/libutils.so (thread_data_t::trampoline(thread_data_t const*)+408)
  #00  pc 00000000000f0d44  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+264)
  #00  pc 000000000008d57c  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+68)

Further debug from Qt Creator / Android emulator session:

I OboeAudio: openStream() OUTPUT -------- OboeVersion1.6.2 --------
I OboeAudio: openStream() OUTPUT -------- OboeVersion1.6.2 --------
I AAudio  : AAudioStreamBuilder_openStream() called ----------------------------------------
I AudioStreamBuilder: rate   =      0, channels  = 2, format   = 1, sharing = EX, dir = OUTPUT
I AudioStreamBuilder: device =      0, sessionId = -1, perfMode = 12, callback: ON with frames = 128
I AudioStreamBuilder: usage  =      1, contentType = 2, inputPreset = 6, allowedCapturePolicy = 0
I AudioStreamBuilder: privacy sensitive = false
I AudioStreamBuilder: opPackageName = (null)
I AudioStreamBuilder: attributionTag = (null)
D AudioStreamBuilder: build() EXCLUSIVE sharing mode not supported. Use SHARED.
D e.koord.koordr: PlayerBase::PlayerBase()
D AudioStreamTrack: open(), request notificationFrames = -8, frameCount = 0
I e.koord.koordr: getDeviceIsSupportElevoc() The device is not support elevoc
I AudioTrack: createTrack_l(0): AUDIO_OUTPUT_FLAG_FAST successful; frameCount 0 -> 1536
D AudioTrack: setVolume: left = 1.000000 right = 1.000000
D AAudioStream: setState(s#1) from 0 to 2
D AudioStreamTrack: open() flags changed from 0x00000104 to 0x00000004
I AAudio  : AAudioStreamBuilder_openStream() returns 0 = AAUDIO_OK for s#1 ----------------
I libKoord-RT_arm64-v8a.so: Stream details: [sDirection:  "Output" , FramesPerBurst:  "192" , BufferSizeInFrames:  "256" , BytesPerFrame:  "4" , BytesPerSample:  "2" , BufferCapacityInFrames:  "1536" , PerformanceMode:  "LowLatency" , SharingMode:  "Shared" , DeviceID:  "3" , SampleRate:  "48000" , AudioFormat:  "I16" , FramesPerCallback:  "128" ]
I OboeAudio: openStream() INPUT -------- OboeVersion1.6.2 --------
I OboeAudio: openStream() INPUT -------- OboeVersion1.6.2 --------
I AAudio  : AAudioStreamBuilder_openStream() called ----------------------------------------
I AudioStreamBuilder: rate   =      0, channels  = 2, format   = 1, sharing = EX, dir = INPUT
I AudioStreamBuilder: device =      0, sessionId = -1, perfMode = 12, callback: ON with frames = 128
I AudioStreamBuilder: usage  =      1, contentType = 2, inputPreset = 6, allowedCapturePolicy = 0
I AudioStreamBuilder: privacy sensitive = false
I AudioStreamBuilder: opPackageName = (null)
I AudioStreamBuilder: attributionTag = (null)
D AudioStreamBuilder: build() EXCLUSIVE sharing mode not supported. Use SHARED.
D e.koord.koordr: PlayerBase::PlayerBase()
D AudioRecord: set(): inputSource 0, sampleRate 0, format 0x1, channelMask 0xc, frameCount 0, notificationFrames 0, sessionId 0, transferType 1, flags 0x5, attributionSource AttributionSourceState{pid: 20231, uid: 10276, packageName: (null), attributionTag: (null), token: , renouncedPermissions: (null), next: []}uid -1, pid -1,isIsolated:0
I AudioRecord: createRecord_l(6619216): AUDIO_INPUT_FLAG_FAST successful; frameCount 0 -> 4096
W AudioStreamRecord: open() flags changed from 0x00000005 to 0x00000001
D AAudioStream: setState(s#2) from 0 to 2
I AAudio  : AAudioStreamBuilder_openStream() returns 0 = AAUDIO_OK for s#2 ----------------
I libKoord-RT_arm64-v8a.so: Stream details: [sDirection:  "Input" , FramesPerBurst:  "144" , BufferSizeInFrames:  "4096" , BytesPerFrame:  "4" , BytesPerSample:  "2" , BufferCapacityInFrames:  "4096" , PerformanceMode:  "LowLatency" , SharingMode:  "Shared" , DeviceID:  "22" , SampleRate:  "48000" , AudioFormat:  "I16" , FramesPerCallback:  "128" ]
I libKoord-RT_arm64-v8a.so: Stream details: [sDirection:  "Output" , FramesPerBurst:  "192" , BufferSizeInFrames:  "256" , BytesPerFrame:  "4" , BytesPerSample:  "2" , BufferCapacityInFrames:  "1536" , PerformanceMode:  "LowLatency" , SharingMode:  "Shared" , DeviceID:  "3" , SampleRate:  "48000" , AudioFormat:  "I16" , FramesPerCallback:  "128" ]
D AAudio  : AAudioStream_requestStart(s#2) called --------------
D AAudioStream: setState(s#2) from 2 to 3
D AAudio  : AAudioStream_requestStart(s#2) returned 0 ---------
D AAudio  : AAudioStream_requestStart(s#1) called --------------
D AAudioStream: setState(s#1) from 2 to 3
D AudioStreamLegacy: onAudioDeviceUpdate(deviceId = 22)
D AudioStreamLegacy: onAudioDeviceUpdate(deviceId = 22)
D libKoord-RT_arm64-v8a.so: Stats:  frames_in:  0 ,frames_out:  0 ,frames_filled_out:  0 ,in_callback_calls:  0 ,out_callback_calls:  0 ,ring_overrun:  0
D AAudio  : AAudioStream_requestStart(s#1) returned 0 ---------
D AudioStreamLegacy: onAudioDeviceUpdate(deviceId = 3)
D AAudioStream: setState(s#1) from 3 to 4
F libc    : Fatal signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7c45df2000 in tid 20486 (AudioRecord), pid 20231 (e.koord.koordrt)

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 25

Most upvoted comments

I don’t think AAudio MMAP is supported on the Xiaomi Redmi Note 10 Pro. It goes straight to Legacy.

I agree with Jiabin that the problem is the memset() in the input callback. Do not write to the input audioData buffer. It can cause crashes as you have seen. Please pass this information to the Jamulus folks.

Could you please specify particular changes to make and test?

We cannot advise you in detail on how to write your app. Basically if you want to skip some input buffers then process your own buffer full of zeros. Do not write zeros to the audioData buffer.

@danryu

Is MMAP really particular to a small sub-set of phones? I’m surprised this Xiaomi Redmi 10 Pro would not be equipped with this, as it’s a relatively-premium phone running Android 12 after all. Is it possible Oboe is falling back unnecessarily to Legacy mode, when MMAP is actually available?

There are lots of devices support MMAP on the market. MMAP is available in 2016, if my memory is correct. In that case, any android os after 2016 may potentially support MMAP. However, it is not a “must” feature from Android CDD. In Android 12 CDD, it is “STRONG RECOMMENDED”. In that case, it is up to phone manufacturers to decide if they want to support MMAP on their devices or not. If the device does not support MMAP path, then it can only be in legacy mode. Even if the device supports MMAP, it is not guaranteed that the app can always get MMAP path. It is more related to the requested parameters and the current resource availability.

Oboe will just pass everything the apps requested, it will not directly fall back to legacy mode unless the app ask to do so.

I think the issue happened due to you were modifying the callback data on the recording path. Code here.

When your app was using legacy path and getting fast mode by requesting low latency, given your callback size(128) is smaller than the burst size(144), aaudio would send your app the data buffer from native audio server directly to save one memory copy. The data buffer in this case is read only for the client. In that case, when you call memset ( audioData, 0 /* value */, numBytes );, the crash happened. When you used oboe callback adapter, the data from native audio server would be copied to an intermediate buffer in oboe. In that case, your app would not crash.

In that case, I’d suggest remove memset in onAudioInput. Instead, you can just call vecsTmpInputAudioSndCrdStereo.Reset(0) to fill silence data if you want to discard the first 500ms recording data.

Robert is trying to get a Xiaomi device for testing.

See code below in QuirkManager.cpp. @danryu can you help us log which of these statements are true/false in the if statement? This will help us figure out what to change in Oboe.

    // There are multiple bugs involving using callback with a specified callback size.
    // Issue #778: O to Q had a problem with Legacy INPUT streams for FLOAT streams
    // and a specified callback size. It would assert because of a bad buffer size.
    //
    // Issue #973: O to R had a problem with Legacy output streams using callback and a specified callback size.
    // An AudioTrack stream could still be running when the AAudio FixedBlockReader was closed.
    // Internally b/161914201#comment25
    //
    // Issue #983: O to R would glitch if the framesPerCallback was too small.
    //
    // Most of these problems were related to Legacy stream. MMAP was OK. But we don't
    // know if we will get an MMAP stream. So, to be safe, just do the conversion in Oboe.
    if (OboeGlobals::areWorkaroundsEnabled()
            && builder.willUseAAudio()
            && builder.isDataCallbackSpecified()
            && builder.getFramesPerDataCallback() != 0
            && getSdkVersion() <= __ANDROID_API_R__) {
        LOGI("QuirksManager::%s() avoid setFramesPerCallback(n>0)", __func__);
        childBuilder.setFramesPerCallback(oboe::Unspecified);
        conversionNeeded = true;
    }