Python ctypes и pulseaudio segfault после обновления до 12.04

Сегодня я наконец сделал решающий шаг и обновил свой настольный компьютер с 11.10 до 12.04, используя процесс обновления, а не новую установку. Первым, что я протестировал после завершения обновления, была небольшая библиотека Python, которая воспроизводит звук через pulseaudio, в основном порт python 2.x для ответа на https://askubuntu.com/a/33602/ 55992

Этот код теперь работает с ошибками в последней строке, тогда как до обновления он этого не делал:

import ctypes
import struct

PA_SAMPLE_FLOAT32LE = 5
PA_STREAM_PLAYBACK = 1

pa = ctypes.cdll.LoadLibrary("libpulse-simple.so.0")
pa.pa_strerror.restype = ctypes.c_char_p

pat_sample_spec = ctypes.c_buffer(struct.pack("=LLB", PA_SAMPLE_FLOAT32LE, 44100, 2))    
error = ctypes.c_int(0)

s = pa.pa_simple_new(0, "Python", PA_STREAM_PLAYBACK, 0, "Test", ctypes.byref(pat_sample_spec), 0, 0, ctypes.byref(error))

Что странно, что этот же код работает на ноутбуке I есть, что я сделал свежую установку 12.04 пару месяцев назад. Я использую Python 2.7.3 на обеих системах.

Есть идеи, что сломано? Это работает для других?

РЕДАКТИРОВАТЬ: я должен отметить, что я могу заставить это хромать, возиться с ним немного, например. Однажды, просто добавив несколько строк печати для отладки перед вызовом pa_simple_new, я смог избежать segfault, и все работало правильно, но это решение кажется хакерским и нестабильным, здесь что-то не так.

РЕДАКТИРОВАТЬ 2: Вот обратная трассировка (надеюсь, я делаю это правильно, у меня нет опыта работы с GDB):

#0  0x00007ffff5d7ba69 in pa_channel_map_valid () from /usr/lib/x86_64-linux-gnu/libpulse.so.0
#1  0x00007ffff5fb99a7 in pa_simple_new () from /usr/lib/x86_64-linux-gnu/libpulse-simple.so.0
#2  0x00007ffff61d6ea4 in ffi_call_unix64 () from /usr/lib/python2.7/lib-dynload/_ctypes.so
#3  0x00007ffff61d68c5 in ffi_call () from /usr/lib/python2.7/lib-dynload/_ctypes.so
#4  0x00007ffff61c72c2 in _ctypes_callproc () from /usr/lib/python2.7/lib-dynload/_ctypes.so
#5  0x00007ffff61c7aa2 in ?? () from /usr/lib/python2.7/lib-dynload/_ctypes.so
#6  0x00000000004c7c76 in PyObject_Call ()
#7  0x000000000042aa4a in PyEval_EvalFrameEx ()
#8  0x00000000004317f2 in PyEval_EvalCodeEx ()
#9  0x000000000054b171 in PyRun_FileExFlags ()
#10 0x000000000054b7d8 in PyRun_SimpleFileExFlags ()
#11 0x000000000054c5d6 in Py_Main ()
#12 0x00007ffff68e576d in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6
#13 0x000000000041b931 in _start ()

РЕДАКТИРОВАНИЕ 3: Запуск того же сценария на моем ноутбуке (где нет segfault) единственное отличие состоит в том, что вместо фрейма 2 используется ffi_call_SYSV (), а затем мы переходим к 32-битным версиям pulseaudio. Я был бы удивлен, если бы проблема была 32 против 64 бит, хотя этот код прекрасно работал на той же машине до обновления до 12.04.

1
задан 13 April 2017 в 15:23

1 ответ

Наконец исправили это. Мораль этой истории: будьте осторожны при копировании кода из Интернета, который «выглядит правильно», но вы на самом деле не работали сами.

Пример строки буфера спецификации структуры, приведенной выше, на самом деле представляет собой небольшую модификацию кода для . Как записать необработанные байты на звуковое устройство? - он вызывает собственный порядок байтов, но без выравнивания через "= msgstr ", в отличие от исходного кода, который имеет собственный порядок и выравнивание байтов (как в случае реализации на C). Я не могу вспомнить, почему я бы так сделал. Тем не менее, ни один из них не работает, и я не могу понять, как все это работало раньше. Во-первых, «L» в 64-битной системе составляет 8 байтов, в то время как код импульса и аудио (pulse / sample.h) использует 32-битные (4 байта) целочисленные типы для первых 2 полей структуры. Во-вторых, использование c_buffer без аргумента длины добавляет нулевой терминатор, фактически увеличивая длину буфера, представляющего структуру, на один байт.

Вы могли бы подумать, что исправление этого сделает все счастливым, но нет. Я создал буфер, используя сгенерированные байты и дамп памяти из эквивалентной программы на C, и до сих пор без кубиков. Что-то должно происходить в ctypes. После того, как я действительно прочитал страницу ctypes от начала до конца, а не просто просмотрел ее для того, что мне нужно, я остановился на следующей более строгой реализации ctypes, используя реальный подкласс Structure (и передавая нулевые указатели как «None»). ", который ничего не меняет, но все равно хорош):

import ctypes
import struct

PA_SAMPLE_FLOAT32LE = 5
PA_STREAM_PLAYBACK = 1

pa = ctypes.cdll.LoadLibrary("libpulse-simple.so.0")

class SampleSpec(ctypes.Structure):
    _fields_ = [("format", ctypes.c_int), ("rate", ctypes.c_int), ("channels", ctypes.c_byte)]

pa_sample_spec = SampleSpec(PA_SAMPLE_FLOAT32LE, 44100, 2)

error = ctypes.c_int(0)

s = pa.pa_simple_new(None, "Python", PA_STREAM_PLAYBACK, None, "Test", ctypes.byref(pa_sample_spec), None, None, ctypes.byref(error))

Нет segfault, и весь мой старый код для прокачки аудио через это работает. Уф!

0
ответ дан 13 April 2017 в 15:23

Другие вопросы по тегам:

Похожие вопросы: