播放声音和视频

Piglet可以加载和播放许多音频和视频格式,通常支持环绕立体声和视频效果。

WAV和MP3文件是跨平台最受支持的文件。特定计算机可以播放的格式取决于以下哪种格式可用:

  1. 内置的侏儒WAV文件解码器(始终可用)

  2. 特定于平台的API和库

  3. PyOgg

  4. FFmpeg 版本4、5或6

视频被播放到OpenGL纹理中,允许应用程序实时操作。示例包括在3D环境中使用或基于着色器的效果。要播放视频, FFmpeg 必须安装。

音频通过以下方式之一进行播放:OpenAL、XAudio2、DirectSound或PulseAudio。所有这些设备都提供硬件加速混音功能。除PulseAudio外,所有后端都提供3D位置音频和环绕立体声功能。

音频驱动程序

Piglet可以使用OpenAL、XAudio2、DirectSound或PulseAudio播放声音。一次只能使用一个驱动程序,但可以通过更改配置和重新启动程序来更改选择。

默认驾驶员首选项顺序适用于大多数用户。但是,您可以通过将不同的首选项序列设置在 pyglet.media 模块已加载。看见 选择音频驱动程序 了解更多信息。

可用的驱动程序取决于您的操作系统:

窗口

Mac OS X

Linux

OpenAL [2]

OpenAL

OpenAL [2]

DirectSound

扩展音频2

PulseAudio [1]

选择音频驱动程序

这个 'audio' 的关键字 pyglet.options 字典指定音频驱动程序的首选项顺序。

在导入时, pyglet.media 将从头到尾尝试每个条目,直到找到工作的驱动程序或条目用完。例如,默认设置相当于设置下列值:

pyglet.options['audio'] = ('xaudio2', 'directsound', 'openal', 'pulse', 'silent')

您还可以设置自定义首选项顺序。例如,我们可以在导入媒体模块之前添加此行::

pyglet.options['audio'] = ('openal', 'pulse', 'xaudio2', 'directsound', 'silent')

它告诉pyglet首先尝试使用OpenAL驱动程序。如果不可用,请按该顺序尝试PulseAudio、XAudio2和DirectSound。如果所有这些都失败了,则不会实例化任何驱动程序,游戏将静默运行。

属性的值 'audio' Key可以是包含以下一个或多个字符串的列表或元组:

细绳

音频驱动程序

'openal'

OpenAL

'directsound'

DirectSound

'xaudio2'

扩展音频2

'pulse'

PulseAudio

'silent'

无音频输出

您必须设置任何自定义 'audio' 导入前的首选项顺序 pyglet.media 。这也可以通过环境变量进行设置;请参见 环境设置

以下各节介绍每个音频驱动程序的要求和限制。

扩展音频2

XAudio2仅在Windows Vista及更高版本上可用,是DirectSound的替代品。这为较新的操作系统提供了硬件加速音频支持。

请注意,在Windows 10的一些精简版本中,XAudio2可能在安装所需的DLL之前不可用。

DirectSound

DirectSound仅在Windows上可用,并且是默认安装的。Piglet仅使用DirectX 7功能。在Windows Vista上,DirectSound不支持硬件混音或环绕立体声。

OpenAL

Mac OS X上最受欢迎的驱动程序,但也可以在其他系统上使用。

该驱动程序具有以下优点:

  • 在支持的平台上预装或轻松安装。

  • 实现其他驱动程序或操作系统特定版本的支持API可能缺少的功能。

它的主要缺点是:

  • 不保证安装在Mac OS X以外的平台上

  • 在最新的Windows版本上, 扩展音频2DirectSound 后端可能支持更多功能。

Windows用户可以从以下地址下载OpenAL实现 openal.org 或者他们音响设备的制造商。

在Linux上,以下条件适用:

  • 它通常可以通过发行版的包管理器进行安装。

  • 它可能已经作为其他包的依赖项安装。

  • 它不具有 PulseAudio 司机。

以下命令应在最常见的Linux发行版上安装OpenAL:

常见的Linux发行版

安装命令

Ubuntu,流行!_OS,Debian

apt install libopenal1

曼加罗邦阿奇

pacman -S openal

诺巴拉软呢帽

dnf install openal-soft

您可能需要在这些命令前添加前缀 sudo 或者是另一个命令。有关更多信息,请参考您的发行版文档。

PulseAudio

这个后端几乎总是受支持,但它的功能有限。

如果无法初始化,请参考发行版的文档以了解可以安装哪些受支持的音频后端。

缺少的功能

虽然PulseAudio理论上可以支持高级多声道音频,但侏儒驱动程序不支持。以下功能将无法正常工作:

  1. 位置音频:根据声源的位置自动更改各个音频声道的音量

  2. 与环绕立体声集成

正在切换到 OpenAL 应该自动启用它们。

支持的媒体类型

Pyglet支持加载Wave(.wav)文件,因此保证可以在所有平台上运行。Pyglet还将使用各种平台库和框架来支持有限数量的压缩音频类型,而不需要FFmpeg。虽然FFmpeg支持大量的格式和编解码器,但当只需要简单的音频回放时,它可能是一个不必要的大依赖。

以下系统和编解码器本身支持这些格式:

Windows Media Foundation

在Windows操作系统上受支持。

以下内容受支持 Windows Vista and above

  • MP3

  • WMA

  • ASF

  • SAMI/SMI

以下内容也受支持 Windows 7 and above

  • AAC/ADTS

以下内容未记录在案,但已知可以使用 Windows 10

  • FLAC

GStreamer

在安装了GStreamer的Linux操作系统上支持。请注意,还需要为gobject和gst提供相关的Python包。这随发行版的不同而不同,但通常会随GStreamer一起安装。

  • MP3

  • FLAC

  • OGG

  • M4A

CoreAudio

在Mac操作系统上受支持。

  • AAC

  • AC3

  • AIF

  • AU

  • CAF

  • MP3

  • M4A

  • SND

  • SD2

PyOgg

在Windows、Linux和Mac操作系统上受支持。

PyOgg是一个轻量级的Python库,它为Opus、Vorbis和FLAC编解码器提供了Python绑定。

如果您的站点包中安装了PyOgg模块,则pyglet将选择性地检测和使用它。由于并非所有操作系统都可以在本地解码相同的音频格式,因此选择一种真正跨平台且占用空间较小的音频格式往往是一件麻烦的事情。创建这个包装器就是为了帮助解决这个问题。

支持以下格式:

  • OGG

  • FLAC

  • OPUS

要安装PyOgg,请参阅他们的 installation guide on readthedocs.io

FFmpeg

备注

最新的侏儒版本可以使用FFmpeg 4.x、5.x或6.x

看见 FFmpeg安装 了解更多信息。

当您需要支持最大数量的格式和编码时,FFmpeg是最好的。同样值得考虑的还有以下几点:

请参阅以下各节以了解更多信息。

看见 FFmpeg和许可证 了解更多信息。

支持的格式

由于FFmpeg支持大量音频和视频编解码器、选项和容器格式,因此很难提供完整的FFmpeg功能列表。参考 the FFmpeg documentation 以获取更多信息。

已知的支持的音频格式包括:

  • AU

  • MP2

  • MP3

  • Ogg/Vorbis

  • WAV

  • WMA

已知的支持的视频格式包括:

  • AVI

  • DivX

  • H.263

  • H.264

  • MPEG

  • MPEG-2

  • Ogg/Theora

  • XVID

  • WMV

  • WebM

检查文件是否将通过FFmpeg加载的最简单方法是尝试通过 media_player.py 举个例子。新版本的FFmpeg可能会修复错误并添加对新格式的支持。

FFmpeg和许可证

FFmpeg的代码对不同部分使用不同的许可证。

该项目的核心使用修改后的LGPL许可证。但是,GPL用于某些可选部件。使用这些组件以及捆绑包含这些组件的FFmpeg二进制文件可能需要完全符合GPL。因此,一些组织可能会限制FFmpeg的部分或全部使用。

Pyglet的FFmpeg绑定不依赖于可选的GPL许可部分。因此,大多数项目应该可以自由地使用他们为自己的代码选择的任何许可证,只要他们使用以下方法之一:

  • 要求用户使用以下任一方法自行安装FFmpeg:

    • 这个 FFmpeg安装 此页上的部分

    • 特定FFmpeg版本的自定义说明

  • 将FFmpeg设置为可选,如 FFmpeg安装 使用说明

  • 捆绑FFmpeg的仅LGPL版本

请参阅以下内容以了解更多信息:

FFmpeg安装

您可以按照中的说明为您的平台安装FFmpeg FFmpeg download 佩奇。您必须选择目标操作系统的共享构建,其体系结构类似于Python解释器。

所有最新的侏儒版本都支持FFmpeg 4.x和5.x。要使用FFmpeg 6.x,您必须使用Piglet 2.0.8或更高版本。

根据目标选择正确的体系结构 Python interpreter 。如果您的项目附带32位解释器,则必须下载32位共享二进制文件。

在Windows上,下载错误的体系结构时通常会出现以下错误消息:

WindowsError: [Error 193] %1 is not a valid Win32 application

最后,确保您下载 shared 构建,而不是静态或开发构建。

对于Mac OS和Linux,库通常已经在系统范围内安装。最简单的做法可能是将FFmpeg列为项目的一个要求,然后让用户来确保安装它。对于Windows用户,不建议将库安装在其中一个Windows子文件夹中。

相反,我们建议使用 pyglet.options search_local_libs **

import pyglet
pyglet.options['search_local_libs'] = True

这将允许Piglet在 lib 位于您的运行脚本文件夹中的子文件夹。

另一种解决方案是操纵环境变量。在Windows上,您可以将DLL位置添加到路径::

os.environ["PATH"] += "path/to/ffmpeg"

对于Linux和Mac OS::

os.environ["LD_LIBRARY_PATH"] += ":" + "path/to/ffmpeg"

小技巧

通过在加载媒体之前检查FFmpeg来防止崩溃!

打电话 pyglet.media.have_ffmpeg() 以检查是否正确检测到FFmpeg。如果它回来了 False ,你可以采取适当的行动,而不是崩溃。示例包括:

  • 在图形用户界面或控制台输出中显示有用的错误

  • 在用户单击对话框上的确定后优雅地退出

  • 限制项目将尝试加载的格式

正在加载媒体

音频和视频文件以相同的方式加载,使用 pyglet.media.load() 函数,提供一个文件名::

source = pyglet.media.load('explosion.wav')

如果媒体文件与应用程序捆绑在一起,请考虑使用 resource 模块(请参见 应用程序资源 )。

加载媒体文件的结果是 Source 对象。此对象提供有关文件中编码的媒体类型的有用信息,并用作用于回放文件的不透明对象(在下一节中介绍)。

这个 load() 函数将引发 MediaException 如果格式未知。 IOError 如果无法从磁盘读取文件,也可能引发。未来版本的pyglet还将支持读取任意类文件对象,但当前必须提供有效的文件名。

媒体文件的长度由 duration 属性,该属性返回以秒为单位的媒体长度。

音频元数据在源的 audio_format 属性,该属性是 None 用于无声视频。此元数据通常对应用程序并不有用。请参阅 AudioFormat 有关详细信息,请参阅类文档。

视频元数据在源的 video_format 属性,该属性是 None 用于音频文件。建议在尝试回放视频文件之前检查此属性--如果电影文件具有可读的音轨但未知的视频格式,则它将显示为音频文件。

您可以使用视频元数据,如 VideoFormat 对象,以在开始回放之前设置视频的显示。这些属性如下:

属性

描述

width, height

视频图像的宽度和高度,以像素为单位。

sample_aspect

每个视频像素的纵横比。

出于显示目的,必须注意将样本纵横比应用于视频图像大小。以下代码确定给定视频格式的显示大小:

def get_video_size(width, height, sample_aspect):
    if sample_aspect > 1.:
        return width * sample_aspect, height
    elif sample_aspect < 1.:
        return width, height / sample_aspect
    else:
        return width, height

媒体文件通常不会完全从磁盘读取;相反,它们会流入解码器,然后仅在需要时才进入音频缓冲区和视频内存。这减少了加载文件的启动时间,并减少了应用程序的内存要求。

然而,有时需要首先对存储器中的音频文件进行完全解码。例如,将播放多次的声音(如子弹或爆炸)应该只解码一次。您可以指示pyglet在加载时将音频文件完全解码到内存中::

explosion = pyglet.media.load('explosion.wav', streaming=False)

生成的源是 StaticSource ,它提供与 StreamingSource 。您还可以构造一个 StaticSource 直接从已加载的 Source **

explosion = pyglet.media.StaticSource(pyglet.media.load('explosion.wav'))

音频合成

除了加载音频文件外, pyglet.media.synthesis 模块可用于简单的音频合成。有几种基本波形可用,包括:

这些波形可以通过指定持续时间、频率和采样率来构建。至少需要一个持续时间。例如::

sine = pyglet.media.synthesis.Sine(3.0, frequency=440, sample_rate=44800)

对于整形波形,有几个简单的包络可用。这些封套会影响幅度(音量),并可以产生更自然的音调。首先创建一个信封实例,然后将其传递到上述任意波形的构造函数中。可以将相同的包络实例传递给任意数量的波形,从而减少创建多个声音时的重复代码。如果不使用包络,所有波形将默认为最大幅度的平坦包络,这对声音没有任何影响。查看每个信封的模块文档以了解哪些参数可用。

创建包络和波形的示例如下:

adsr = pyglet.media.synthesis.ADSREnvelope(attack=0.05, decay=0.2, release=0.1)
saw = pyglet.media.synthesis.Sawtooth(duration=1.0, frequency=220, envelope=adsr)

您使用合成模块创建的波形可以像播放任何其他加载的声音一样播放。有关回放的更多详细信息,请参阅下一节。

简单的音频播放

许多应用程序,尤其是游戏,需要播放完整的声音,而不需要跟踪它们。例如,当玩家的宇宙飞船爆炸时,需要播放一种声音,但这种声音永远不需要调整音量、倒带或中断。

Piglet为这种用例提供了一个简单的接口。调用 play() Any方法 Source 立即完整地播放::

explosion = pyglet.media.load('explosion.wav', streaming=False)
explosion.play()

你可以打电话给 play() 在任何 Source ,不仅仅是 StaticSource

的返回值 play() 是一种 Player ,它可以被丢弃,也可以保留以保持对声音播放的控制。

控制播放

属性可以实现媒体播放器的许多常见功能。 Player 班级。对于视频播放,使用此类也是必要的。它的构造没有参数::

player = pyglet.media.Player()

玩家将播放任何符合以下条件的来源 queued 这就去。任意数量的源可以在单个玩家上排队,但一旦排队,源就永远不能出列(直到完成后自动移除)。该排队机制的主要用途是促进媒体文件的回放之间的“无缝”转换。

这个 queue() 方法用于对播放器上的媒体进行排队 StreamingSource 或者是 StaticSource 。您可以传递一个实例,也可以传递一个可迭代的源代码。这提供了极大的灵活性。例如,您可以创建一个生成器,它负责有关播放什么音乐的逻辑::

def my_playlist():
   yield intro
   while game_is_running():
      yield main_theme
   yield ending

player.queue(my_playlist())

当游戏结束时,您仍需要呼唤玩家::

player.next_source()

生成器将通过 ending 媒体对球员的影响。

A StreamingSource 只能在一个玩家上排队,并且只能在该玩家上排队一次。 StaticSource 对象可以在任意数量的玩家上排队任意次数。回想一下,一个 StaticSource 可以通过传递 streaming=False 发送到 pyglet.media.load() 方法。

在下面的示例中,两种声音被排队到一个播放器上:

player.queue(source1)
player.queue(source2)

回放开始于播放器的 play() 方法被调用::

player.play()

用于控制回放的标准控件由以下方法提供:

方法

描述

play()

开始或继续播放当前信号源。

pause()

暂停当前信号源的播放。

next_source()

将当前来源排出队列,并立即移至下一个来源。

seek()

寻找当前来源内的特定时间。

请注意,没有 stop 方法。如果您不需要恢复播放,只需暂停播放并丢弃播放器和源对象。使用 next_source() 方法不能保证无缝回放。

有几个属性描述了玩家的当前状态:

属性

描述

time

当前信号源中的当前播放位置,以秒为单位。这是只读的(但请参阅 seek() 方法)。

playing

如果播放机当前正在播放,则为True;如果没有信号源排队或播放机暂停,则为False。这是只读的(但请参阅 pause()play() 方法)。

source

对正在播放的当前源的引用。这是只读的(但请参阅 queue() 方法)。

volume

音频级别,表示为从0(静音)到1(正常音量)的浮点数。这可以在任何时候设置。

loop

True 是否应在到达末尾时重复电流源。如果设置为 False ,则播放将继续到下一个排队的信号源。

处理回放事件

当玩家到达当前源的末尾时,会引发 on_eos() (在源代码结束时)事件被调度。玩家对此事件有一个默认的处理程序,该处理程序将重复当前源(如果 loop 属性已设置为 True ),或者立即移动到下一个排队的源。当没有更多排队的源时, on_player_eos() 事件被调度,并且播放停止,直到另一个源排队。

对于循环控制,您可以更改 loop 属性,但请注意,除非为将来的数据提供足够的时间进行解码和缓冲,否则在播放过程中可能会出现卡顿或间隙。如果设置在震源结束之前很久(比如几秒钟),就不会出现中断。

通过设置您自己的事件处理程序,可以进一步自定义源代码结束行为;请参见 事件调度和处理 。您可以直接替换默认的事件处理程序,也可以按照参考中的说明添加其他事件。例如::

my_player.on_eos = my_player.pause

无缝回放

为了在没有任何声音间隙的情况下重放多个相似的源, SourceGroup 是提供的。一个 SourceGroup 只能包含具有相同音频或视频格式的媒体源。首先创建一个 SourceGroup ,然后添加所有所需的附加源 add() 方法。之后,您可以将 SourceGroup 就像它是单一来源一样。

整合视频

当一个 Player 正在播放带有视频的信号源,请使用 texture 属性以获取视频帧图像。这可以用来显示与音频轨道同步的当前视频图像,例如::

@window.event
def on_draw():
    player.texture.blit(0, 0)

该纹理是 pyglet.image.Texture ,内部格式为 GL_TEXTURE_2DGL_TEXTURE_RECTANGLE_ARB 。虽然纹理通常只创建一次并随后更新每一帧,但您不应在应用程序中做出这样的假设-未来版本的Piglet可能会使用多个纹理对象。

位置音频

pyglet包括在3D空间中定位声音的功能。这对于环绕立体声设置特别有效,但也适用于立体声系统。

A Player 它在3D空间中有一个关联的位置--也就是说,它等同于一个OpenAL“源”。API文档中更详细地介绍了用于设置这些参数的属性;例如,请参阅 positionpitch

“Listener”对象由音频驱动程序提供。要获取当前音频驱动程序的监听程序::

pyglet.media.get_audio_driver().get_listener()

这提供了类似的属性,例如 positionforward_orientationup_orientation 它描述了用户在3D空间中的位置。

请注意,只能定位单声道声音。立体声将正常播放,只有其音量和音调属性会影响声音。

滴答作响的时钟

如果你在一个小程序之外使用小程序的媒体库,你将需要使用某种循环来周期性地计时(可能每隔200ms左右),否则只会播放第一个小样本的媒体::

pyglet.clock.tick()

如果您希望媒体源连续循环 (player.loop = True )还需要确保在循环中调度Pyglet的事件::

pyglet.app.platform_event_loop.dispatch_posted_events()

如果您在一个pyglet应用程序中,则调用 pyglet.app.run() 会帮你解决这一切的。