Утечка памяти в AudioTrack - PullRequest
       26

Утечка памяти в AudioTrack

5 голосов
/ 15 марта 2019

Я столкнулся с серьезной проблемой утечки памяти, когда AudioTrack и MediaSync используются вместе.Мне кажется, проблема в том, что AudioTrack не выпускает некоторые собственные ресурсы.В результате приложение может быть запущено только несколько раз, после чего AudioTrack не может быть создан, поскольку больше нет доступных треков.

Ниже приведен краткий пример, который вызывает утечку памяти.Полный проект можно скачать здесь на GitHub.APK-файл можно загрузить здесь .

final MediaSync mediaSync = new MediaSync();
mediaSync.setSurface(mSurface);
final Surface inputSurface = mediaSync.createInputSurface(); // There is no the memory leak if I don't create this input surface.

final AudioTrack audioTrack = new AudioTrack.Builder()
        .setAudioAttributes(new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE)
                .build())
        .setAudioFormat(new AudioFormat.Builder()
                .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                .setSampleRate(48000)
                .setChannelMask(12)
                .build())
        .build();
mediaSync.setAudioTrack(audioTrack); // There is no the memory leak if I don't set AudioTrack.

mediaSync.release();
inputSurface.release();
audioTrack.release();

. Воспроизведите проблему следующим образом:

  1. Запустите приложение.
  2. Нажмите кнопку «Домой», запустите снова.Повторите это примерно 14 раз, после этого получите ошибку.

Logcat:

2019-03-15 09:19:57.313 239-15387/? E/AudioFlinger: no more track names available
2019-03-15 09:19:57.313 239-15387/? E/AudioFlinger: createTrack_l() initCheck failed -12; no control block?
2019-03-15 09:19:57.313 3413-3413/com.audiotrackmemoryleak E/AudioTrack: AudioFlinger could not create track, status: -12
2019-03-15 09:19:57.313 3413-3413/com.audiotrackmemoryleak E/AudioTrack-JNI: Error -12 initializing AudioTrack
2019-03-15 09:19:57.313 3413-3413/com.audiotrackmemoryleak E/android.media.AudioTrack: Error code -20 when initializing AudioTrack.
2019-03-15 09:19:57.315 3413-3413/com.audiotrackmemoryleak D/AndroidRuntime: Shutting down VM
2019-03-15 09:19:57.316 3413-3413/com.audiotrackmemoryleak E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.audiotrackmemoryleak, PID: 3413
    java.lang.UnsupportedOperationException: Cannot create AudioTrack
        at android.media.AudioTrack$Builder.build(AudioTrack.java:776)
        at com.audiotrackmemoryleak.MainActivity.createMediaSync(MainActivity.java:68)
        at com.audiotrackmemoryleak.MainActivity.access$100(MainActivity.java:18)
        at com.audiotrackmemoryleak.MainActivity$1.surfaceCreated(MainActivity.java:32)
        at android.view.SurfaceView.updateWindow(SurfaceView.java:618)
        at ...

Команда adb shell dumpsys media.audio_flinger демонстрирует проблему:

...
Clients:             
  pid: 3413          
Notification Clients:
  pid: 239           
  pid: 841           
  pid: 3413          
  pid: 28651         
Global session refs: 
  session   pid count
     3193  3413     1
     3201  3413     1
     3209  3413     1
     3217  3413     1
     3225  3413     1
     3233  3413     1
     3241  3413     1
     3249  3413     1
     3257  3413     1
     3265  3413     1
     3273  3413     1
     3281  3413     1
     3289  3413     1
     3297  3413     1
...
          14 Tracks of which 0 are active
    Name Active Client Type      Fmt Chn mask Session fCount S F SRate  L dB  R dB    Server Main buf  Aux Buf Flags UndFrmCnt
       7     no   3413    3 00000001 00000003    3249   1924 I 0 48000     0     0  00000000 0xa77fe000 0x0 0x000         0
       5     no   3413    3 00000001 00000003    3233   1924 I 0 48000     0     0  00000000 0xa77fe000 0x0 0x000         0
      12     no   3413    3 00000001 00000003    3289   1924 I 0 48000     0     0  00000000 0xa77fe000 0x0 0x000         0
       3     no   3413    3 00000001 00000003    3217   1924 I 0 48000     0     0  00000000 0xa77fe000 0x0 0x000         0
       8     no   3413    3 00000001 00000003    3257   1924 I 0 48000     0     0  00000000 0xa77fe000 0x0 0x000         0
       9     no   3413    3 00000001 00000003    3265   1924 I 0 48000     0     0  00000000 0xa77fe000 0x0 0x000         0
       4     no   3413    3 00000001 00000003    3225   1924 I 0 48000     0     0  00000000 0xa77fe000 0x0 0x000         0
       1     no   3413    3 00000001 00000003    3201   1924 I 0 48000     0     0  00000000 0xa77fe000 0x0 0x000         0
      11     no   3413    3 00000001 00000003    3281   1924 I 0 48000     0     0  00000000 0xa77fe000 0x0 0x000         0
       0     no   3413    3 00000001 00000003    3193   1924 I 0 48000     0     0  00000000 0xa77fe000 0x0 0x000         0
       6     no   3413    3 00000001 00000003    3241   1924 I 0 48000     0     0  00000000 0xa77fe000 0x0 0x000         0
      13     no   3413    3 00000001 00000003    3297   1924 I 0 48000     0     0  00000000 0xa77fe000 0x0 0x000         0
       2     no   3413    3 00000001 00000003    3209   1924 I 0 48000     0     0  00000000 0xa77fe000 0x0 0x000         0
      10     no   3413    3 00000001 00000003    3273   1924 I 0 48000     0     0  00000000 0xa77fe000 0x0 0x000         0
  0 Effect Chains
...

Интересно, есть здесь кто-нибудь, кто может объяснить, что происходит?Как правильно выпустить AudioTrack?

1 Ответ

4 голосов
/ 19 марта 2019

Я не уверен, что это ошибка SDK или нет, но она связана с AudioAttributes.FLAG_DEEP_BUFFER.Когда вы строите AudioTrack через Builder, этот флаг устанавливается по умолчанию.См. Ваш случай здесь : проверка shouldEnablePowerSaving() возвращает значение true и регистр переключателя приводит к PERFORMANCE_MODE_POWER_SAVING с включенным FLAG_DEEP_BUFFER.

Чтобы исправить это, вы должны отключить этот флаг дляПример добавления .setFlags(AudioAttributes.FLAG_LOW_LATENCY) вызова к вашему AudioAttributes, но для этого требуется минимум SDK 24. В противном случае вы можете вообще отказаться от использования AudioTrack.Builder и построить трек следующим образом:

    int audioSampleRate = 48000;
    int channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
    int bufSize = AudioTrack.getMinBufferSize(audioSampleRate, channelConfig, AudioFormat.ENCODING_PCM_16BIT);

    AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, audioSampleRate,
            channelConfig, AudioFormat.ENCODING_PCM_16BIT, bufSize, AudioTrack.MODE_STREAM);

Или вы можете проверить код shouldEnablePowerSaving() проверьте и сделайте так, чтобы он не проходил другим способом.

ОБНОВЛЕНИЕ : Таким образом, вышеупомянутое решение просто перенесло утечку в другой аудиопоток.Я исследовал дальше и заметил, что surfaceCreated() вручает тот же объект Surface на моем устройстве Android 8И действительно, это правильное поведение, поскольку Android 7 .Я предполагаю, что это как-то нарушает логику поверхности mediaSync: если вы удалите вызов на mediaSync.createInputSurface() и добавите вызов mediaSync.setSurface(null) перед его освобождением, утечка исчезнет.

Я не знаю, как обойти этопроблема, поскольку Surface повторно используется системой, и нет способа узнать, когда он будет фактически уничтожен.Я предлагаю перейти на TextureView, он имеет похожий, но более понятный API и не разрушает поверхность при паузе активности.Вам нужно будет удалить вызов createMediaSync() из onResume() и использовать его следующим образом:

setContentView(R.layout.activity_main);
final TextureView textureView = findViewById(R.id.texture_view);

textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        mSurface = new Surface(surface);
        createMediaSync();
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        //release resources here
        return true;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    }
});

Удачи!

...