diff options
| author | Imayhaveborkedit <[email protected]> | 2019-06-22 02:33:53 -0400 |
|---|---|---|
| committer | Rapptz <[email protected]> | 2019-07-22 20:46:40 -0400 |
| commit | fedf26bf3e91e12a403144e8282300a785c204d4 (patch) | |
| tree | a321db5dcad79ccff112fc1fa59a52fe47eeb318 /discord/oggparse.py | |
| parent | [commands] update sys.modules in load_extension again (diff) | |
| download | discord.py-fedf26bf3e91e12a403144e8282300a785c204d4.tar.xz discord.py-fedf26bf3e91e12a403144e8282300a785c204d4.zip | |
Add FFmpegOpusAudio and other voice improvements
Rework FFmpeg player and add FFmpegOpusAudio
I have extracted some of the base FFmpeg source code into its own
base class and reimplemented the PCM and the new Opus variants.
Support avconv probing
Also fix a few things
Update `__all__`
Fix the bugs
Rework probe functions and add factory function
Probing involves subprocess so it has been reworked into an async
factory function.
Add docs + a few tweaks
* Removed unnecessary read() and is_opus() functions from FFmpegAudio
* Clear self._stdout in cleanup()
* Add 20 second process communication timeout to probe functions
* Capped probe function bitrate values at 512
Change AudioPlayer to use more accurate, monotonic time.perf_counter()
Add lazy opus loading
The library now no longer loads libopus on import, only on
opus.Encoder creation or manually.
Fix review nits
Diffstat (limited to 'discord/oggparse.py')
| -rw-r--r-- | discord/oggparse.py | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/discord/oggparse.py b/discord/oggparse.py new file mode 100644 index 00000000..6f50502f --- /dev/null +++ b/discord/oggparse.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- + +""" +The MIT License (MIT) + +Copyright (c) 2015-2019 Rapptz + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +""" + +import struct + +from .errors import DiscordException + +class OggError(DiscordException): + """An exception that is thrown for Ogg stream parsing errors.""" + pass + +# https://tools.ietf.org/html/rfc3533 +# https://tools.ietf.org/html/rfc7845 + +class OggPage: + _header = struct.Struct('<xBQIIIB') + + def __init__(self, stream): + try: + header = stream.read(struct.calcsize(self._header.format)) + + self.flag, self.gran_pos, self.serial, \ + self.pagenum, self.crc, self.segnum = self._header.unpack(header) + + self.segtable = stream.read(self.segnum) + bodylen = sum(struct.unpack('B'*self.segnum, self.segtable)) + self.data = stream.read(bodylen) + except Exception: + raise OggError('bad data stream') from None + + def iter_packets(self): + packetlen = offset = 0 + partial = True + + for seg in self.segtable: + if seg == 255: + packetlen += 255 + partial = True + else: + packetlen += seg + yield self.data[offset:offset+packetlen], True + offset += packetlen + packetlen = 0 + partial = False + + if partial: + yield self.data[offset:], False + +class OggStream: + def __init__(self, stream): + self.stream = stream + + def _next_page(self): + head = self.stream.read(4) + if head == b'OggS': + return OggPage(self.stream) + else: + raise OggError('invalid header magic') + + def _iter_pages(self): + page = self._next_page() + while page: + yield page + page = self._next_page() + + def iter_packets(self): + partial = b'' + for page in self._iter_pages(): + for data, complete in page.iter_packets(): + partial += data + if complete: + yield partial + partial = b'' |