pyglet uses FFmpeg in favour of AVbin

944 views
Skip to first unread message

Daniel Gillet

unread,
Nov 21, 2017, 4:50:09 AM11/21/17
to pyglet-users
Hello,

Claudio Canepa and myself have worked on a new wrapping of FFmpeg with ctypes to replace AVbin. Benjamin Moran helped also a lot with his opinions on various topics and to coordinate our work with pyglet main branch. Many bugs were also fixed in the Audio Drivers and in the Media Player itself. There should not be any breaking API changes but there are a couple of additional features. All is described in the documentation.

Today our work is available from pyglet repository in the default branch. A new tag 1.4.0a1 will be added soon. So anyone who wants to give this a try can create a virtual environment as described in the documentation, and install pyglet from within the virtual environment with pip install -e hg+https://bitbucket.org/pyglet/pyglet/#egg=pyglet

Here is a quick extract from the new documentation regarding the installation of FFmeg.

FFmpeg installation

You can install FFmpeg for your platform by following the instructions found in the FFmpeg download page.

For Mac OS and Linux, the library is usually installed system-wide. For Windows users, it’s not recommended to install the library in one of the windows sub-folders.

Instead we recommend to use the pyglet.options search_local_libs:

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

This will allow pyglet to find the FFmpeg binaries in the lib folder located in your running script folder.




At the time of development, we've been using FFmpeg 3.3.
We are really thrilled by the new code and we're looking forward to hearing if you encountered any difficulties in order to improve the documentation or the code.

Dan

Benjamin Moran

unread,
Nov 21, 2017, 9:33:10 AM11/21/17
to pyglet-users
Thanks a lot to you and Claudio for your work on this.

To everyone else, please give this a test even if you don't use compressed audio or video in your projects. As Dan said there was a lot of work done to the media module in general, so feedback is greatly appreciated.

mat campos

unread,
Nov 21, 2017, 10:32:48 PM11/21/17
to pyglet-users
Tested it with a few videos (that had a few problems playing them with avbin) and now they work fine.

Nice work!

Rob

unread,
Nov 22, 2017, 12:15:19 PM11/22/17
to pyglet-users
Hi all,

I created the 1.4.0a1 packages to make it easier to test this. You can install it using pip:

  pip install pyglet==1.4.0a1

Manual downloads:

Charles

unread,
Nov 22, 2017, 4:39:08 PM11/22/17
to pyglet-users
Tested on Windows 10 x64, 32 bit ffmpeg 3.3 and 3.4. Realtek HD Audio. Using the noisy example to test wav and ogg.

The sound does not work without ffmpeg, and wav. Same with ffmpeg loaded. I tested an ogg file instead of wav and neither work.

With either, the first sound will be the distorted sound popping, and then nothing plays after the initial pop.

Not sure what other information you need from me or tests to perform.


Also unrelated, trying to start the example gives an error:
Traceback (most recent call last):
 
File "D:\pyglet-1.4.0a1\examples\noisy\noisy.py", line 46, in <module>
   
from pyglet.gl import *
 
File "C:\Python27\lib\site-packages\pyglet\gl\__init__.py", line 219, in <module>
   
from .win32 import Win32Config as Config
 
File "C:\Python27\lib\site-packages\pyglet\gl\win32.py", line 6, in <module>
   
from pyglet.canvas.win32 import Win32Canvas
 
File "C:\Python27\lib\site-packages\pyglet\canvas\__init__.py", line 99, in <module>
   
from pyglet.canvas.win32 import Win32Display as Display
 
File "C:\Python27\lib\site-packages\pyglet\canvas\win32.py", line 7, in <module>
   
from pyglet.libs.win32 import _kernel32, _user32, types, constants
 
File "C:\Python27\lib\site-packages\pyglet\libs\win32\__init__.py", line 237, in <module>
    _user32
.GetRawInputData.argtypes = [HRAWINPUT, UINT, LPVOID, PUINT, UINT]
NameError: name 'PUINT' is not defined


Removing the PUNIT works.

claudio canepa

unread,
Nov 22, 2017, 6:31:50 PM11/22/17
to pyglet...@googlegroups.com
Regardind the PUINT traceback: it is a py 2.7 compatibility problem.
Seems that PUINT was added in py 3.2 [0]

Can be seen in py3.6 at https://github.com/python/cpython/blob/3.6/Lib/ctypes/wintypes.py , at line 195

It is not present in 2.7 , https://github.com/python/cpython/blob/2.7/Lib/ctypes/wintypes.py
 
[0] https://bugs.python.org/issue3612

--
You received this message because you are subscribed to the Google Groups "pyglet-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pyglet-users+unsubscribe@googlegroups.com.
To post to this group, send email to pyglet...@googlegroups.com.
Visit this group at https://groups.google.com/group/pyglet-users.
For more options, visit https://groups.google.com/d/optout.

Daniel Gillet

unread,
Nov 23, 2017, 9:45:18 AM11/23/17
to pyglet-users
Thank you Charles for trying out the code. Your report was very useful and allowed me to identify a bug.

Claudio correctly identified the first issue. The issue with the sound was only due to the fact that I didn't properly test the creation of Player through Source.play(). There was an issue with the player being immediately destroyed as no references were kept anywhere.

The bugs have now been corrected and I've submitted a pull request. As soon as it's accepted, You might want to try again with the new code.

Dan

Alice Haugen

unread,
Nov 24, 2017, 6:29:43 AM11/24/17
to pyglet-users
Thanks for the work on this!

One issue I encountered: one of my libraries wants to play an audio file, then loop from a certain point rather than the beginning of the audio. Previously, it was tolerably effective to divide the audio into "intro" and "looping" files, create a SourceGroup for the "looping" file with loop set, and then queue the intro file and the SourceGroup. With the change of SourceGroup into PlayList and migration of loop to the Player, this no longer appears possible. I can handle this by e.g. setting loop on the Player from on_player_next_source, but this is less elegant and more prone to error than the earlier solution.

Daniel Gillet

unread,
Nov 24, 2017, 7:40:16 AM11/24/17
to pyglet...@googlegroups.com
​Hello,

Thank you for trying out the new version. This is an interesting use case you're describing. My first thought on this was : why bother splitting the audio file into 2 files? You could just override the Player on_eos method to seek back to where you want.

class LoopPlayer(Player):
    offset_start = 20 # Intro is 20 seconds
    
    def on_eos(self):
        if self.loop:
            self.seek(LoopPlayer.offset_start)
        else:
            self.next_source()​

But then I can see that it might be useful to have a loop attribute on each Source in the PlayList. Without external intervention this Source would loop, unless Player.next_source() is called.

If we go down this route, I presume that the Player.queue signature would become something like Player.queue(source, loop=False). Would this meet your expectations? Do you have a better idea?

Petr Viktorin

unread,
Nov 24, 2017, 8:22:36 AM11/24/17
to pyglet...@googlegroups.com
On 11/24/2017 01:40 PM, Daniel Gillet wrote:
> ​Hello,
>
> Thank you for trying out the new version. This is an interesting use
> case you're describing. My first thought on this was : why bother
> splitting the audio file into 2 files? You could just override the
> Player on_eos method to seek back to where you want.
>
> class LoopPlayer(Player):
>     offset_start = 20 # Intro is 20 seconds
>     def on_eos(self):
>         if self.loop:
>             self.seek(LoopPlayer.offset_start)
>         else:
>             self.next_source()​

I can only assume the playback needs to be gap-less, so the looping
needs to be scheduled in advance.

Benjamin Moran

unread,
Nov 24, 2017, 9:24:50 AM11/24/17
to pyglet-users
This is great feedback, and a perfect time to discuss.

Dan, I like that idea, because the API is simple and clean. One issue, could be if the user wants to do the following:
1. User creates a list of 10 songs, and then queues them.
2. The User wants these 10 songs to get repeated as a group, not individually. (Like a music playlist, on repeat).

This is one use case that I didn't think of before, and it seems that the SourceGroup was used for.
I suppose an alternative, would be to allow passing an iterable to `Player.queue`. That list of sources could be treated as a group. If loop=True is also passed, they could be looped as a group as well.

I guess the question is whether it makes sense to put all of this code inside the Player class or not?

Daniel Gillet

unread,
Nov 24, 2017, 9:59:23 AM11/24/17
to pyglet-users
Hi,

The original SourceGroup was not very clear on its intent, but it was pretty specific about the fact that once a Source is played, it was discarded. So there was no possibility to do as you describe, ie. loop over a list of Sources.

This is something that could be done obviously. There is obviously a cost in terms of memory if we want to have this behaviour. Let's see what everybody thinks of it.

I would imagine that the Player.loop attribute would be used to tell the Player to loop over the whole Playlist, without discarding any previously played Source. If Player.loop is False, then the Sources are discarded from the Playlist after being played.
The Playlist would keep track of individual loop flags for each Source, that are set by client code when queuing a source on the Player: player.queue(music_theme, loop=True). Then when this source is being player, it will repeat indefinitely until the client calls player.next_source().

What do you think?

Greg Ewing

unread,
Nov 24, 2017, 4:41:38 PM11/24/17
to pyglet...@googlegroups.com
Benjamin Moran wrote:
> I suppose an alternative, would be to allow passing an iterable to
> `Player.queue`. That list of sources could be treated as a group. If
> loop=True is also passed, they could be looped as a group as well.

Would it be possible to arrange things so that an infinite
iterator can be passed to get the effect of a looping group?

That seems like the most general and Pythonic solution to me.
It means you'd be able to set up any kind of looping structure
you wanted by making the iterator behave appropriately.

--
Greg

Daniel Gillet

unread,
Nov 25, 2017, 3:27:49 AM11/25/17
to pyglet-users
Hello,


Would it be possible to arrange things so that an infinite
iterator can be passed to get the effect of a looping group?

That seems like the most general and Pythonic solution to me.
It means you'd be able to set up any kind of looping structure
you wanted by making the iterator behave appropriately.

Although this seems like an excellent idea, I think we still want to allow the player to skip a Source which is supposed to loop forever. If we would use the idea of an iterator, one could create it this way:

from itertools import chain, repeat

intro = pyglet.resource.media("intro.mp3")
main_theme = pyglet.resource.media("main_theme.mp3")
ending = pyglet.resource.media("ending.mp3")

my_playlist = chain(intro, repeat(main_theme), ending)
player.queue(my_playlist)

The idea is that the intro plays once, then the main_theme loops over until player.next_source() is called. But if this just takes the next element in the my_playlist iterator, we will never reach the ending song as the repeat(main_theme) will provide an infinite amount of sources.

If I think about a Media Player, the usual functionalities are : play once, repeat one song, repeat the whole playlist. So I think this is what we should try to implement. I guess that the current behaviour of discarding a Source once it is played is actually surprising to the user. I would then make this an option, but not the default.

I will make some tests and see if memory-wise it's not too hungry.

Daniel

Greg Ewing

unread,
Nov 25, 2017, 7:06:32 PM11/25/17
to pyglet...@googlegroups.com
Daniel Gillet wrote:

> my_playlist = chain(intro, repeat(main_theme), ending)
> player.queue(my_playlist)
>
> if this just takes the next
> element in the my_playlist iterator, we will never reach the ending song

That's not how you would do that. You'd do it like this:

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

player.queue(my_playlist())

When the game ends it will need to call player.next_source()
to jog it into action, but the iterator will take care of
breaking out of the loop.

> If I think about a Media Player, the usual functionalities are : play
> once, repeat one song, repeat the whole playlist.

The "usual" things provided by an API are not always the most
appropriate ways to do things in Python. Given that playlists
are iterators, all three of the above are trivial to express:

player.queue(song)
player.queue(repeat(song))
player.queue(cycle(song_list))

> I guess that the current behaviour of
> discarding a Source once it is played is actually surprising to the
> user. I would then make this an option, but not the default.

It's certainly surprising in a Python context -- one would
expect Sources to behave like any other Python object and
stay around until they're no longer referenced, unless
something is explicitly done to dispose of them.

--
Greg

Daniel Gillet

unread,
Nov 26, 2017, 3:51:30 AM11/26/17
to pyglet...@googlegroups.com
Hi Greg,

Thanks for the great feedback and discussion. I must say I'm glad we're talking about things like PlayList and not about video not playing correctly! :)

I see your point and it's a great one. I can better imagine how one could make any sort of playlists. I like the idea and I'm trying to figure out a couple of things. If playlists are iterators, it means that player.queue takes as an argument an iterator. But player.queue(song) does not qualify anymore, as song is a Source and not an iterator. I can see solutions to this problem, but I was wondering how you were envisioning that. Also I think we should let users write code like the following:

player.queue(song1)
player.queue(song2)

The user would probably expect that the songs are all played once and then the player stops once all the songs have been played.

Does this mean that the Player has some sort of default PlayList which would be similar to a list where queuing songs mean you're appending them to the list? And obviously the Player would then get an iterator from that list. If on the other hand the client code provides an iterator to the player.queue method, then we just use that one to get the Sources.

Dan



--
You received this message because you are subscribed to a topic in the Google Groups "pyglet-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/pyglet-users/ah2I54BSrZg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to pyglet-users+unsubscribe@googlegroups.com.

Greg Ewing

unread,
Nov 26, 2017, 4:55:16 PM11/26/17
to pyglet...@googlegroups.com
Daniel Gillet wrote:
> If playlists are iterators, it means that
> player.queue takes as an argument an iterator. But player.queue(song)
> does not qualify anymore, as song is a Source and not an iterator.

There are a couple of ways to handle that. One is to have
player.queue() inspect its argument and do different things
depending on whether it's a Source instance or not.

Another is to have two methods, such as player.queue_source()
and player.queue_playlist().

There are pros and cons either way. The single method is
less to remember and maybe slightly easier to document,
but two separate methods is friendlier towards duck-typing
and makes the intent of the code clearer to the reader.

I'll leave it up to you to decide.

> player.queue(song1)
> player.queue(song2)
>
> The user would probably expect that the songs are all played once and
> then the player stops once all the songs have been played.
>
> Does this mean that the Player has some sort of default PlayList which
> would be similar to a list where queuing songs mean you're appending
> them to the list? And obviously the Player would then get an iterator
> from that list. If on the other hand the client code provides an
> iterator to the player.queue method, then we just use that one to get
> the Sources.

I don't think that's quite right. If the user does this:

player.queue(song1)
player.queue(some_iterator)
player.queue(song2)

it should play song1, then all the songs returned by the
iterator, then song2.

I think what you need is an internal list of "things to be
played", each of which can be either a Source or an iterator,
and have it work its way through that.

If it would make anything simpler, you could wrap a single
Source in a one-element sequence before putting it in the
list, so that all the things in the list are iterables.

--
Greg

Benjamin Moran

unread,
Nov 29, 2017, 8:48:07 PM11/29/17
to pyglet-users
To go back to the bugs for a moment, I'm seeing an issue with the PulseAudio abstraction on Linux. The player context does not seem to be created with the throw-away Player (from source.play()). I took a quick peek at the code, and it seems that self.context is still set to None at the point of the crash. In the code below, "note_wave" is just a static source. I haven't yet dug into it further. Could any other Linux users confirm? I'm using the "examples/synthesizer.py" example to test.

  File "synthesizer.py", line 63, in play_note
    note_wave
.play()
 
File "/home/ben/Documents/PycharmProjects/pyglet/pyglet/media/sources/base.py", line 275, in play
    player
.play()
 
File "/home/ben/Documents/PycharmProjects/pyglet/pyglet/media/player.py", line 238, in play
   
self._set_playing(True)
 
File "/home/ben/Documents/PycharmProjects/pyglet/pyglet/media/player.py", line 190, in _set_playing
   
self._create_audio_player()
 
File "/home/ben/Documents/PycharmProjects/pyglet/pyglet/media/player.py", line 342, in _create_audio_player
    setattr
(self, attr, value)
 
File "/home/ben/Documents/PycharmProjects/pyglet/pyglet/media/player.py", line 124, in __set__
    getattr
(obj._audio_player, 'set_' + self.attribute)(value)
 
File "/home/ben/Documents/PycharmProjects/pyglet/pyglet/media/drivers/pulse/adaptation.py", line 418, in set_volume
   
with self.context:
AttributeError: __enter__

Daniel Gillet

unread,
Nov 30, 2017, 12:04:32 PM11/30/17
to pyglet-users
Thanks Greg for those info. I've started to work on this but I'm a bit swamped at the moment so I'm not making much progress.

@Benjamin: thanks for the bug report! I didn't look into it too deeply yet. But it does not sound like the context is not yet initialised, but that there was no strong reference to the context and it's been garbage collected. That's at least my initial thought. I need obviously to dig into it.

I have a Ubuntu on a portable drive, so I'll use that to investigate.

Don't expect much progress in the following days, but I'm not giving up. :)

Dan

Daniel Gillet

unread,
Dec 2, 2017, 10:21:24 AM12/2/17
to pyglet-users
Benjamin, the bug was confirmed.

I've corrected it (I hope) and it was not what I initially thought. The details are not super important and they can be found in my commit.
I've created a new pull request for you to review. Let me know if it solves the problem.

I don't forget the playlist feature. But I don't have the time right now. It will take a bit more time because I need to create some test cases for this new functionality. This means I need to first understand all the different test cases that exist for the media Player.

I'll let you know once I make some progresses. :)

Dan

Rob van der Most

unread,
Dec 3, 2017, 4:24:39 AM12/3/17
to pyglet...@googlegroups.com
To me the fire and forget use of players had been a resource management headache from the first time I touched media. I am strongly in favor of deprecating it. So if you create a player but don't hold on to it, the player should be destroyed. 
Issue 105 is an example of the trouble it causes. 

Rob

--
You received this message because you are subscribed to the Google Groups "pyglet-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pyglet-users+unsubscribe@googlegroups.com.

Benjamin Moran

unread,
Dec 3, 2017, 9:36:19 AM12/3/17
to pyglet-users
Dan, your fix solves the crash issue for me. I am seeing some other slightly strange behavior still, which might just be my specific machine. If you try out examples/synthesizer.py,  the audio sounds a little corrupted if you play a few notes at once (just mash on the keyboard :) ). OpenAL does not have this issue.

In addition, this only occurs with the fire-and-forget player. As a test, I made a deque of Players, and just queued on the next one each time. This allows multiple channels to play at once, without corruption. I can share some example code if you need, but basically I made a deque of players like this:
self.players = deque([pyglet.media.Player() for _ in range(5)])
Then, instead of doing "note_wave.play()", I did something like this:
self.players[0].queue(note_wave)
self.players[0].play()
self.players.rotate()
Strangely, this works fine. It doesn't seem like the fire and forget players are getting collected to early, but maybe it's related.

Things are a lot better than they were before, though, so we're definitely going in the right direction!

Benjamin Moran

unread,
Dec 3, 2017, 9:47:43 AM12/3/17
to pyglet-users
Rob,

I like the simplicity of the fire and forget players, but I have to agree with you. It really is a mess under the hood to make it all work.
I do use them myself though, for one reason:  It's nice to be able to play multiple sounds simultaniously, without thinking about rotating a list of Players to queue them on.  I try to avoid this though, because I know it's messy. Instead, I often make a simple AudioPlayer class, with the following characteristics:
  • One Player instance for music tracks.
  • A deque of many Player instances for sound effects.
  • A `play` method for sounds that should be played instantly, and mixed with other already playing sounds.
  • A `play_music` method to replace the currently playing music track.

If we do deprecate the fire-and-forget functionality, perhaps we can implement a slightly higher level abstraction for simplicity. Something along the lines of the KeyStateHandler. Simple to write yourself, but so common that it's nice to have a ready made class.




On Sunday, December 3, 2017 at 6:24:39 PM UTC+9, Rob wrote:
To me the fire and forget use of players had been a resource management headache from the first time I touched media. I am strongly in favor of deprecating it. So if you create a player but don't hold on to it, the player should be destroyed. 
Issue 105 is an example of the trouble it causes. 

Rob
On 2 Dec 2017 4:21 pm, "Daniel Gillet" <dan.gi...@gmail.com> wrote:
Benjamin, the bug was confirmed.

I've corrected it (I hope) and it was not what I initially thought. The details are not super important and they can be found in my commit.
I've created a new pull request for you to review. Let me know if it solves the problem.

I don't forget the playlist feature. But I don't have the time right now. It will take a bit more time because I need to create some test cases for this new functionality. This means I need to first understand all the different test cases that exist for the media Player.

I'll let you know once I make some progresses. :)

Dan

--
You received this message because you are subscribed to the Google Groups "pyglet-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pyglet-users...@googlegroups.com.

Daniel Gillet

unread,
Dec 3, 2017, 10:20:29 AM12/3/17
to pyglet-users
Benjamin, I noticed the sound problem (garbles) you describe when playing multiple sounds together. I'll investigate it.

At the moment I'm working on the player queue mechanism. The basics are implemented. I've adjusted the tests where needed. I would need now to add more tests to check if everything is working correctly when using an iterable (or iterator) of sources.

I'll push soon my work in progress on my repo: https://bitbucket.org/dangillet/pyglet/ Anyone who wants to give it a try can pull directly from there. I will only make a pull request once I'm happy with the code.

Dan

Benjamin Moran

unread,
Dec 3, 2017, 11:31:49 PM12/3/17
to pyglet-users
Dan, I had a quick look at your changes. Besides the new logic in the Player, it looks like the other code was simplified a little bit. That's nice to see.

Daniel Gillet

unread,
Dec 10, 2017, 3:39:12 AM12/10/17
to pyglet-users
I'm still investigating the issue with playing multiple sounds on PulseAudio.

Benjamin, if I try to use a list of Players, instead of using fire-and-forget players, I still have the sound problem. Could you please try again on your end and let me know if this is indeed the case. Maybe also create more players - say 20 instead of 5 - to allow a lot of players to play together. I would also recommend increasing the duration of the notes in synthesizer.py to have the time to play enough Player simultaneously.

I also noticed a problem in examples/noisy/noisy.py. With PulseAudio it seems that the sound is cut too ealry and I don't hear correctly the sound. Could you please also let me know if you've got the same problem?

I must say I've spent hours on this but I don't seem to find the culprit. The thing that worried me in the PulseAudio Stream documentation was :

Note: there is a user-controllable slider in mixer applications such as pavucontrol corresponding to each of the created streams. Multiple (especially identically named) volume sliders for the same application might confuse the user. Also, the server supports only a limited number of simultaneous streams. Because of this, it is not always appropriate to create multiple streams in one application that needs to output multiple sounds. The rough guideline is: if there is no use case that would require separate user-initiated volume changes for each stream, perform the mixing inside the application.

Still digging...
Dan

Benjamin Moran

unread,
Dec 10, 2017, 10:43:19 PM12/10/17
to pyglet-users
Hi Dan,

Sorry for the delay - busy weekend here!  I'll do more testing as requested an update you with the results.
An observation I had is that the behavior is a bit strange when viewed in the system mixer (for example, the "applications" tab of the "Sounds" section under the Gnome environment). When using OpenAL, you see a single application appear, even when using the fire and forget Players. With PulseAudio, you see a new "application" appear for each and every Player created. For fire and forget Players, they appear and dissapear rapidly as the Players are garbage collected. With normal reusable Players, you just see a few redundant PulseAudio clients connected and sitting there (instead of just one, like with OpenAL).

In the PulseAudio documentation, there is a section at the bottom of this page entitled "Synchronizing Multiple Playback Streams":
https://freedesktop.org/software/pulseaudio/doxygen/streams.html
It looks like we might need to reuse a main PulseAudio "master stream".
I looked in the pulseaudio/interface.py file, there is a #TODO next to this exact thing in the "connect_playback" method from line 477. My understanding is that we need to keep a reference to a single "master stream" somewhere, and all Players created after that will send data to this same stream.

Sorry if that is hard to follow, but let me know and I'll try to clarify. In the mean time, I'll see if I can do those tests.
-Ben

Paul Craven

unread,
Dec 19, 2017, 2:33:42 PM12/19/17
to pyglet-users
I recently tried to get pyglet and ffmpeg working on the Mac. I ran into a lot of problems loading the libraries.

I could adjust the load library path by adjusting os.environ['LD_LIBRARY_PATH'] either by the environment variable or by changing it before importing pyglet.

But the builds you can download for the mac assume file names like "libavutil.56.dylib" and pyglet assumes naming like "libavutil.dylib". 

I can't just rename the files because the libraries are linked together assuming they are named with the extra numbers. It would be REALLY nice for pyglet to include some kind of mapping options so I can tell it exactly where avutil is, and what it is named.

I know this is an alpha release, but I had this issue with avbin as well. 

Paul Craven

unread,
Dec 19, 2017, 4:11:38 PM12/19/17
to pyglet-users
Ok, I used symbolic links and got the libraries to load. It quits with a "ValueError: NULL pointer access" in ffmpeg.py line 917. Has anyone gotten this to work on a Mac?

Daniel Gillet

unread,
May 22, 2018, 12:38:06 PM5/22/18
to pyglet-users
Hi Paul,

About 6 months later ... here I come looking at your problem. Sorry to hear you could not make this work on Mac. It actually already worked on Mac, but I don't understand why the naming is here different.

Anyway, a similar problem exists on Windows, where the lib files are also named with a version number. I was for a long time wondering what the best approach would be to let the user place the version needed. But I realized that the ctypes binding was done for a specific version of FFmpeg! Chances are small that the API would change but the binding could nevertheless break if a newer version is used, or it could introduce subtle bugs.

When I did the binding, I was working with FFmpeg 3.3. More specifically with the following lib versions:
libavutil      55.
libavcodec     57.
libavformat    57.
libavdevice    57.
libavfilter     6.
libswscale      4.
libswresample   2.
libpostproc    54.

In order to make this also work on Mac, following the naming convention you described, there is a way to fix the problem. If you're willing to give it a try, you would need to go in the folder pyglet\media\codecs\ffmpeg_lib and open all the files like libavcodec.py, etc. I'll take libavcodec.py as an example. At the beginning of the file you will find the following code

avcodec = pyglet.lib.load_library('avcodec', win32='avcodec-57')

You would just need to update it to the following:

avcodec = pyglet.lib.load_library('avcodec', win32='avcodec-57', darwin='avcodec.57')

This obviously assumes you're using libavcodec 57. This should then look for the lib named libavcodec.57.dylib.

I don't have access to a Mac. But I can try to help make it work.

Dan

Wolf Weidner

unread,
May 30, 2018, 5:34:29 AM5/30/18
to pyglet-users


Am Dienstag, 21. November 2017 10:50:09 UTC+1 schrieb Daniel Gillet:
Hello,

Claudio Canepa and myself have worked on a new wrapping of FFmpeg with ctypes to replace AVbin. Benjamin Moran helped also a lot with his opinions on various topics and to coordinate our work with pyglet main branch. Many bugs were also fixed in the Audio Drivers and in the Media Player itself. There should not be any breaking API changes but there are a couple of additional features. All is described in the documentation.


Sorry to dig up the thread again, does this mean the current pyglet is not using avbin anymore? 

Daniel Gillet

unread,
May 30, 2018, 9:01:32 AM5/30/18
to pyglet-users
Hello Wolf,

The situation as I understand it is that Libav is not showing the same standard as FFmpeg. You can see the following threads for some clarifications:
AVbin is a thin wrapper which provided an ABI for Libav. Many issues with the media library could be traced back to the fact that pyglet used Libav.

So Claudio and I tried to re-write part of the media library to use FFmpeg instead. We used ctypes to wrap the minimum required C functions and structures to have a viable solution.

This is still a work in progress. The use of FFmpeg is only from pyglet version 1.4.0a1 (alpha version). The current stable version is 1.3.2. I must confess that I lost some momentum those last 6 months due to a nagging bug on Linux with PulseAudio and this is not my normal development OS. Also I got busy with other stuff professionally.

I don't know what the maintainers will decide for the future of Pyglet. But I still believe that polishing the support for FFmpeg would be a good decision.

I hope this answers your question.

Daniel

Daniel Gillet

unread,
Jun 10, 2018, 9:59:01 AM6/10/18
to pyglet-users
Hello,


Le dimanche 3 décembre 2017 16:20:29 UTC+1, Daniel Gillet a écrit :
[...] At the moment I'm working on the player queue mechanism. The basics are implemented. I've adjusted the tests where needed. I would need now to add more tests to check if everything is working correctly when using an iterable (or iterator) of sources.

I'll push soon my work in progress on my repo: https://bitbucket.org/dangillet/pyglet/ Anyone who wants to give it a try can pull directly from there. I will only make a pull request once I'm happy with the code.

Dan

I still have that work on a branch in my Pyglet fork. In the meantime Benjamin changed the layout of the files and merging it back will take some efforts, but it's totally doable. Before rushing into this, do you think I should do that? Or do you prefer to stick with the current PlayList which is less Pythonic as Greg Ewing correctly explained.

Dan 

Neon22

unread,
Jun 10, 2018, 6:41:59 PM6/10/18
to pyglet-users
Hi Dan, is that the version with two approaches like Greg suggested ?

Another is to have two methods, such as player.queue_source() and player.queue_playlist().

So someone can setup a queue to play a number of source files, as well as play a specific source simultaneously. I.e. now.
I'm not sure I fully understood the ins and outs of the entire discussion.

Neon22

unread,
Jun 10, 2018, 6:50:47 PM6/10/18
to pyglet-users
OK. I think I got it.
If you want to play two simultaneously then you instantiate a second ```Player```.
and a ```Player``` can have a queue and it plays items in the queue in order, as you might expect.
On finishing playing a source it releases the source so it no longer consumes memory.
Not sure what happens if the source appears again later in the queue.

Daniel Gillet

unread,
Jun 11, 2018, 10:24:12 AM6/11/18
to pyglet-users
I know it is very confusing! The discussion was about the queuing of media on a given Player, especially in regards with looping on some video / musics, etc.

Greg proposal was to allow the Player to queue either a single Source or an iterable of Sources. He said either we could have 2 functions or a single one. I implemented the version with a single function which analyse its attribute and figure out whether it's a Source or an iterable or Sources.

The benefit with this approach is that the iterable could be a generator which provides the different media depending on some conditions. Greg's example was:

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

    player.queue(my_playlist())


So everytime the Player finishes playing a media, it asks the iterable for the next media until it is exhausted. The source (media) can appear as often as we want in that generator. The only restriction is that if the Source is streamed, it can only be played on one Player. This is checked within the code I wrote.

All of this has nothing to do with playing multiple players at the same time.

For something unrelated, I've been also thinking about the fire and forget usage of the Player, and I think I have an idea. Anyone can of course create a Player. When the references to it disappear, it is gone from memory. Done. But I would also keep the fire and forget player, which is used by calling .play() on a Source. To deal with it, I would add a global set of Players in the module player.py. The Source would both instantiate the player and add it to the set. Also the Player would be given a callback to remove itself from that list once it reaches its end-of-stream. Finally the module player.py would also have an atexit.register() callback to empty the list of Players (if any) when we exit. This should allow the program to gracefully exit.

Dan

Neon22

unread,
Jun 11, 2018, 5:11:07 PM6/11/18
to pyglet-users
Well FWIW my 2 cents is it sounds great. well considered, clear and useful.

Benjamin Moran

unread,
Jun 13, 2018, 4:39:38 AM6/13/18
to pyglet-users
HI Dan,

Sorry for the difficulty regarding the media module refactor. It was done to unify the codec structure that is shared between the image, media, and upcoming model modules. Let me know if you need assistance with merging anything in. I'm fairly familiar with the module, so I'm happy to do a little manual work.

With regards to interable sources, I prefer the idea of `Player.queue` detecting whether the source is iterable or not. It doesn't necessarily have to be difficult to explain. The documentation can simply explain that you can queue a single source, or list of sources (or any iterable of sources).
Reply all
Reply to author
Forward
0 new messages