Rapptz / discord.py

An API wrapper for Discord written in Python.
http://discordpy.rtfd.org/en/latest
MIT License
14.8k stars 3.75k forks source link

OpusError on macOS arm64 platform (only) #8046

Closed m13253 closed 2 years ago

m13253 commented 2 years ago

Summary

When creating an opus.Encoder() instance, an exception of discord.opus.OpusError: invalid argument is thrown

Reproduction Steps

First, install either Python 3.9 or Python 3.10.

$ brew install python
$ python3 -m pip install -U "discord.py[voice]"

Then, execute the code in the section below.

Minimal Reproducible Code

import discord.opus
opus_encoder = discord.opus.Encoder()

Expected Results

Should load libopus.dylib and create an Opus encoder instance.

Actual Results

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/homebrew/lib/python3.10/site-packages/discord/opus.py", line 290, in __init__
    self.set_bitrate(128)
  File "/opt/homebrew/lib/python3.10/site-packages/discord/opus.py", line 308, in set_bitrate
    _lib.opus_encoder_ctl(self._state, CTL_SET_BITRATE, kbps * 1024)
  File "/opt/homebrew/lib/python3.10/site-packages/discord/opus.py", line 92, in _err_lt
    raise OpusError(result)
discord.opus.OpusError: invalid argument

Intents

Not applicable

System Information

- Python v3.10.4-final
- discord.py v1.7.3-final
- aiohttp v3.7.4.post0
- system info: Darwin 21.4.0 Darwin Kernel Version 21.4.0: Fri Mar 18 00:46:32 PDT 2022; root:xnu-8020.101.4~15/RELEASE_ARM64_T6000

Checklist

Additional Context

[1/4] This bug only happens on arm64 macOS?

Yes, only on arm64 macOS.

Confirmed no issue on x86_64 macOS or Windows or Linux.

[2/4] Why can't I use Windows or Linux instead?

It's because my music production software is on this platform.

I'm willing to run test code snippets on my machine if you wish.

[3/4] Some more check on libopus

I try to check if libopus is correctly loaded. Result: yes.

>>> import discord.opus
>>> discord.opus._load_default()
True
>>> discord.opus._lib
<CDLL '/opt/homebrew/lib/libopus.dylib', handle 209560680 at 0x104f380a0>
>>> discord.opus._OpusStruct.get_opus_version()
'libopus 1.3.1'

I try to check whether there is an architecture mismatch between Python and libopus. Result: Both arm64. None are “fat binary”.

$ file /opt/homebrew/bin/python3
/opt/homebrew/bin/python3: Mach-O 64-bit executable arm64
$ file /opt/homebrew/lib/libopus.dylib
/opt/homebrew/lib/libopus.dylib: Mach-O 64-bit dynamically linked shared library arm64

I try to check if the request numbers are different on arm64. Result: The numbers are same with x86_64.

$ cd /opt/homebrew/include/opus
$ grep -r '#define OPUS_SET_[^ ]*_REQUEST'
./opus_defines.h:#define OPUS_SET_APPLICATION_REQUEST         4000
./opus_defines.h:#define OPUS_SET_BITRATE_REQUEST             4002
./opus_defines.h:#define OPUS_SET_MAX_BANDWIDTH_REQUEST       4004
./opus_defines.h:#define OPUS_SET_VBR_REQUEST                 4006
./opus_defines.h:#define OPUS_SET_BANDWIDTH_REQUEST           4008
./opus_defines.h:#define OPUS_SET_COMPLEXITY_REQUEST          4010
./opus_defines.h:#define OPUS_SET_INBAND_FEC_REQUEST          4012
./opus_defines.h:#define OPUS_SET_PACKET_LOSS_PERC_REQUEST    4014
./opus_defines.h:#define OPUS_SET_DTX_REQUEST                 4016
./opus_defines.h:#define OPUS_SET_VBR_CONSTRAINT_REQUEST      4020
./opus_defines.h:#define OPUS_SET_FORCE_CHANNELS_REQUEST      4022
./opus_defines.h:#define OPUS_SET_SIGNAL_REQUEST              4024
./opus_defines.h:#define OPUS_SET_GAIN_REQUEST                4034
./opus_defines.h:#define OPUS_SET_LSB_DEPTH_REQUEST           4036
./opus_defines.h:#define OPUS_SET_EXPERT_FRAME_DURATION_REQUEST 4040
./opus_defines.h:#define OPUS_SET_PREDICTION_DISABLED_REQUEST 4042
./opus_defines.h:#define OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST 4046

[4/4] Why am I directly calling discord.opus.Encoder? It's supposed to be a private API.

First, the public API will eventually call into this private API and raise exception. There is no difference.

Second, my bot program has to call into this private API in order to perform some WebRTC-level magic.

Rapptz commented 2 years ago

Thanks for the detailed bug report. After a lot of debugging, it seems this is an issue with ctypes itself. I've reported an issue upstream if you want to track its progress: https://github.com/python/cpython/issues/92892

m13253 commented 2 years ago

Cool! Thanks for forwarding to the upstream.