Merging gst-python

This commit is contained in:
Thibault Saunier 2021-09-24 16:16:03 -03:00
commit d0c25d3184
95 changed files with 22474 additions and 0 deletions

1
.gitlab-ci.yml Normal file
View file

@ -0,0 +1 @@
include: "https://gitlab.freedesktop.org/gstreamer/gst-ci/raw/master/gitlab/ci_template.yml"

1
AUTHORS Normal file
View file

@ -0,0 +1 @@
This file will be autogenerated. Please read README-docs.

510
COPYING Normal file
View file

@ -0,0 +1,510 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations
below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it
becomes a de-facto standard. To achieve this, non-free programs must
be allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control
compilation and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at least
three years, to give the same user the materials specified in
Subsection 6a, above, for a charge no more than the cost of
performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply, and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License
may add an explicit geographical distribution limitation excluding those
countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms
of the ordinary General Public License).
To apply these terms, attach the following notices to the library.
It is safest to attach them to the start of each source file to most
effectively convey the exclusion of warranty; and each file should
have at least the "copyright" line and a pointer to where the full
notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or
your school, if any, to sign a "copyright disclaimer" for the library,
if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James
Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

7668
ChangeLog Normal file

File diff suppressed because it is too large Load diff

299
NEWS Normal file
View file

@ -0,0 +1,299 @@
GStreamer 1.20 Release Notes
GStreamer 1.20 has not been released yet. It is scheduled for release
around October/November 2021.
1.19.x is the unstable development version that is being developed in
the git main branch and which will eventually result in 1.20, and 1.19.2
is the current development release in that series
It is expected that feature freeze will be in early October 2021,
followed by one or two 1.19.9x pre-releases and the new 1.20 stable
release around October/November 2021.
1.20 will be backwards-compatible to the stable 1.18, 1.16, 1.14, 1.12,
1.10, 1.8, 1.6,, 1.4, 1.2 and 1.0 release series.
See https://gstreamer.freedesktop.org/releases/1.20/ for the latest
version of this document.
Last updated: Wednesday 22 September 2021, 18:00 UTC (log)
Introduction
The GStreamer team is proud to announce a new major feature release in
the stable 1.x API series of your favourite cross-platform multimedia
framework!
As always, this release is again packed with many new features, bug
fixes and other improvements.
Highlights
- this section will be completed in due course
Major new features and changes
Noteworthy new features and API
- this section will be filled in in due course
New elements
- this section will be filled in in due course
New element features and additions
- this section will be filled in in due course
Plugin and library moves
- this section will be filled in in due course
- There were no plugin moves or library moves in this cycle.
Plugin removals
The following elements or plugins have been removed:
- this section will be filled in in due course
Miscellaneous API additions
- this section will be filled in in due course
Miscellaneous performance, latency and memory optimisations
- this section will be filled in in due course
Miscellaneous other changes and enhancements
- this section will be filled in in due course
Tracing framework and debugging improvements
- this section will be filled in in due course
Tools
- this section will be filled in in due course
GStreamer RTSP server
- this section will be filled in in due course
GStreamer VAAPI
- this section will be filled in in due course
GStreamer OMX
- this section will be filled in in due course
GStreamer Editing Services and NLE
- this section will be filled in in due course
GStreamer validate
- this section will be filled in in due course
GStreamer Python Bindings
- this section will be filled in in due course
GStreamer C# Bindings
- this section will be filled in in due course
GStreamer Rust Bindings and Rust Plugins
The GStreamer Rust bindings are released separately with a different
release cadence thats tied to gtk-rs, but the latest release has
already been updated for the upcoming new GStreamer 1.20 API.
gst-plugins-rs, the module containing GStreamer plugins written in Rust,
has also seen lots of activity with many new elements and plugins.
What follows is a list of elements and plugins available in
gst-plugins-rs, so people dont miss out on all those potentially useful
elements that have no C equivalent.
- FIXME: add new elements
Rust audio plugins
- audiornnoise: New element for audio denoising which implements the
noise removal algorithm of the Xiph RNNoise library, in Rust
- rsaudioecho: Port of the audioecho element from gst-plugins-good
rsaudioloudnorm: Live audio loudness normalization element based on
the FFmpeg af_loudnorm filter
- claxondec: FLAC lossless audio codec decoder element based on the
pure-Rust claxon implementation
- csoundfilter: Audio filter that can use any filter defined via the
Csound audio programming language
- lewtondec: Vorbis audio decoder element based on the pure-Rust
lewton implementation
Rust video plugins
- cdgdec/cdgparse: Decoder and parser for the CD+G video codec based
on a pure-Rust CD+G implementation, used for example by karaoke CDs
- cea608overlay: CEA-608 Closed Captions overlay element
- cea608tott: CEA-608 Closed Captions to timed-text (e.g. VTT or SRT
subtitles) converter
- tttocea608: CEA-608 Closed Captions from timed-text converter
- mccenc/mccparse: MacCaption Closed Caption format encoder and parser
- sccenc/sccparse: Scenarist Closed Caption format encoder and parser
- dav1dec: AV1 video decoder based on the dav1d decoder implementation
by the VLC project
- rav1enc: AV1 video encoder based on the fast and pure-Rust rav1e
encoder implementation
- rsflvdemux: Alternative to the flvdemux FLV demuxer element from
gst-plugins-good, not feature-equivalent yet
- rsgifenc/rspngenc: GIF/PNG encoder elements based on the pure-Rust
implementations by the image-rs project
Rust text plugins
- textwrap: Element for line-wrapping timed text (e.g. subtitles) for
better screen-fitting, including hyphenation support for some
languages
Rust network plugins
- reqwesthttpsrc: HTTP(S) source element based on the Rust
reqwest/hyper HTTP implementations and almost feature-equivalent
with the main GStreamer HTTP source souphttpsrc
- s3src/s3sink: Source/sink element for the Amazon S3 cloud storage
- awstranscriber: Live audio to timed text transcription element using
the Amazon AWS Transcribe API
Generic Rust plugins
- sodiumencrypter/sodiumdecrypter: Encryption/decryption element based
on libsodium/NaCl
- togglerecord: Recording element that allows to pause/resume
recordings easily and considers keyframe boundaries
- fallbackswitch/fallbacksrc: Elements for handling potentially
failing (network) sources, restarting them on errors/timeout and
showing a fallback stream instead
- threadshare: Set of elements that provide alternatives for various
existing GStreamer elements but allow to share the streaming threads
between each other to reduce the number of threads
- rsfilesrc/rsfilesink: File source/sink elements as replacements for
the existing filesrc/filesink elements
Build and Dependencies
- this section will be filled in in due course
gst-build
- this section will be filled in in due course
Cerbero
Cerbero is a meta build system used to build GStreamer plus dependencies
on platforms where dependencies are not readily available, such as
Windows, Android, iOS and macOS.
General improvements
- this section will be filled in in due course
macOS / iOS
- this section will be filled in in due course
Windows
- this section will be filled in in due course
Windows MSI installer
- this section will be filled in in due course
Linux
- this section will be filled in in due course
Android
- this section will be filled in in due course
Platform-specific changes and improvements
Android
- this section will be filled in in due course
macOS and iOS
- this section will be filled in in due course
Windows
- this section will be filled in in due course
Linux
- this section will be filled in in due course
Documentation improvements
- this section will be filled in in due course
Possibly Breaking Changes
- this section will be filled in in due course
- MPEG-TS SCTE-35 API changes (FIXME: flesh out)
- gst_parse_launch() and friends now error out on non-existing
properties on top-level bins where they would silently fail and
ignore those before.
Known Issues
- this section will be filled in in due course
- There are a couple of known WebRTC-related regressions/blockers:
- webrtc: DTLS setup with Chrome is broken
- webrtcbin: First keyframe is usually lost
Contributors
- this section will be filled in in due course
… and many others who have contributed bug reports, translations, sent
suggestions or helped testing.
Stable 1.20 branch
After the 1.20.0 release there will be several 1.20.x bug-fix releases
which will contain bug fixes which have been deemed suitable for a
stable branch, but no new features or intrusive changes will be added to
a bug-fix release usually. The 1.20.x bug-fix releases will be made from
the git 1.20 branch, which will be a stable branch.
1.20.0
1.20.0 is scheduled to be released around October/November 2021.
Schedule for 1.22
Our next major feature release will be 1.22, and 1.21 will be the
unstable development version leading up to the stable 1.22 release. The
development of 1.21/1.22 will happen in the git main branch.
The plan for the 1.22 development cycle is yet to be confirmed.
1.22 will be backwards-compatible to the stable 1.20, 1.18, 1.16, 1.14,
1.12, 1.10, 1.8, 1.6, 1.4, 1.2 and 1.0 release series.
------------------------------------------------------------------------
These release notes have been prepared by Tim-Philipp Müller with
contributions from …
License: CC BY-SA 4.0

1
README Normal file
View file

@ -0,0 +1 @@
This file will be autogenerated. Please read README-docs.

96
RELEASE Normal file
View file

@ -0,0 +1,96 @@
This is GStreamer gst-python 1.19.2.
GStreamer 1.19 is the development branch leading up to the next major
stable version which will be 1.20.
The 1.19 development series adds new features on top of the 1.18 series and is
part of the API and ABI-stable 1.x release series of the GStreamer multimedia
framework.
Full release notes will one day be found at:
https://gstreamer.freedesktop.org/releases/1.20/
Binaries for Android, iOS, Mac OS X and Windows will usually be provided
shortly after the release.
This module will not be very useful by itself and should be used in conjunction
with other GStreamer modules for a complete multimedia experience.
- gstreamer: provides the core GStreamer libraries and some generic plugins
- gst-plugins-base: a basic set of well-supported plugins and additional
media-specific GStreamer helper libraries for audio,
video, rtsp, rtp, tags, OpenGL, etc.
- gst-plugins-good: a set of well-supported plugins under our preferred
license
- gst-plugins-ugly: a set of well-supported plugins which might pose
problems for distributors
- gst-plugins-bad: a set of plugins of varying quality that have not made
their way into one of core/base/good/ugly yet, for one
reason or another. Many of these are are production quality
elements, but may still be missing documentation or unit
tests; others haven't passed the rigorous quality testing
we expect yet.
- gst-libav: a set of codecs plugins based on the ffmpeg library. This is
where you can find audio and video decoders and encoders
for a wide variety of formats including H.264, AAC, etc.
- gstreamer-vaapi: hardware-accelerated video decoding and encoding using
VA-API on Linux. Primarily for Intel graphics hardware.
- gst-omx: hardware-accelerated video decoding and encoding, primarily for
embedded Linux systems that provide an OpenMax
implementation layer such as the Raspberry Pi.
- gst-rtsp-server: library to serve files or streaming pipelines via RTSP
- gst-editing-services: library an plugins for non-linear editing
==== Download ====
You can find source releases of gstreamer in the download
directory: https://gstreamer.freedesktop.org/src/gstreamer/
The git repository and details how to clone it can be found at
https://gitlab.freedesktop.org/gstreamer/
==== Homepage ====
The project's website is https://gstreamer.freedesktop.org/
==== Support and Bugs ====
We have recently moved from GNOME Bugzilla to GitLab on freedesktop.org
for bug reports and feature requests:
https://gitlab.freedesktop.org/gstreamer
Please submit patches via GitLab as well, in form of Merge Requests. See
https://gstreamer.freedesktop.org/documentation/contribute/
for more details.
For help and support, please subscribe to and send questions to the
gstreamer-devel mailing list (see below for details).
There is also a #gstreamer IRC channel on the Freenode IRC network.
==== Developers ====
GStreamer source code repositories can be found on GitLab on freedesktop.org:
https://gitlab.freedesktop.org/gstreamer
and can also be cloned from there and this is also where you can submit
Merge Requests or file issues for bugs or feature requests.
Interested developers of the core library, plugins, and applications should
subscribe to the gstreamer-devel list:
https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel

1
TODO Normal file
View file

@ -0,0 +1 @@
Port old examples to GStreamer 1.0

11
examples/README.md Normal file
View file

@ -0,0 +1,11 @@
# Dependencies
Some of the examples require external python dependencies, for this purpose
an illustrative requirements.txt is provided, with annotations documenting
which example requires a dependency.
You can install all the dependencies with:
```
python3 -m pip install -r requirements.txt --user
```

90
examples/dynamic_src.py Normal file
View file

@ -0,0 +1,90 @@
#!/usr/bin/env python3
'''
Simple example to demonstrate dynamically adding and removing source elements
to a playing pipeline.
'''
import sys
import random
import gi
gi.require_version('Gst', '1.0')
gi.require_version('GLib', '2.0')
gi.require_version('GObject', '2.0')
from gi.repository import GLib, GObject, Gst
class ProbeData:
def __init__(self, pipe, src):
self.pipe = pipe
self.src = src
def bus_call(bus, message, loop):
t = message.type
if t == Gst.MessageType.EOS:
sys.stdout.write("End-of-stream\n")
loop.quit()
elif t == Gst.MessageType.ERROR:
err, debug = message.parse_error()
sys.stderr.write("Error: %s: %s\n" % (err, debug))
loop.quit()
return True
def dispose_src_cb(src):
src.set_state(Gst.State.NULL)
def probe_cb(pad, info, pdata):
peer = pad.get_peer()
pad.unlink(peer)
pdata.pipe.remove(pdata.src)
# Can't set the state of the src to NULL from its streaming thread
GLib.idle_add(dispose_src_cb, pdata.src)
pdata.src = Gst.ElementFactory.make('videotestsrc')
pdata.src.props.pattern = random.randint(0, 24)
pdata.pipe.add(pdata.src)
srcpad = pdata.src.get_static_pad ("src")
srcpad.link(peer)
pdata.src.sync_state_with_parent()
GLib.timeout_add_seconds(1, timeout_cb, pdata)
return Gst.PadProbeReturn.REMOVE
def timeout_cb(pdata):
srcpad = pdata.src.get_static_pad('src')
srcpad.add_probe(Gst.PadProbeType.IDLE, probe_cb, pdata)
return GLib.SOURCE_REMOVE
def main(args):
GObject.threads_init()
Gst.init(None)
pipe = Gst.Pipeline.new('dynamic')
src = Gst.ElementFactory.make('videotestsrc')
sink = Gst.ElementFactory.make('autovideosink')
pipe.add(src, sink)
src.link(sink)
pdata = ProbeData(pipe, src)
loop = GObject.MainLoop()
GLib.timeout_add_seconds(1, timeout_cb, pdata)
bus = pipe.get_bus()
bus.add_signal_watch()
bus.connect ("message", bus_call, loop)
# start play back and listen to events
pipe.set_state(Gst.State.PLAYING)
try:
loop.run()
except:
pass
# cleanup
pipe.set_state(Gst.State.NULL)
if __name__ == '__main__':
sys.exit(main(sys.argv))

58
examples/helloworld.py Normal file
View file

@ -0,0 +1,58 @@
#!/usr/bin/env python
import sys
import gi
gi.require_version('Gst', '1.0')
from gi.repository import GObject, Gst
def bus_call(bus, message, loop):
t = message.type
if t == Gst.MessageType.EOS:
sys.stdout.write("End-of-stream\n")
loop.quit()
elif t == Gst.MessageType.ERROR:
err, debug = message.parse_error()
sys.stderr.write("Error: %s: %s\n" % (err, debug))
loop.quit()
return True
def main(args):
if len(args) != 2:
sys.stderr.write("usage: %s <media file or uri>\n" % args[0])
sys.exit(1)
GObject.threads_init()
Gst.init(None)
playbin = Gst.ElementFactory.make("playbin", None)
if not playbin:
sys.stderr.write("'playbin' gstreamer plugin missing\n")
sys.exit(1)
# take the commandline argument and ensure that it is a uri
if Gst.uri_is_valid(args[1]):
uri = args[1]
else:
uri = Gst.filename_to_uri(args[1])
playbin.set_property('uri', uri)
# create and event loop and feed gstreamer bus mesages to it
loop = GObject.MainLoop()
bus = playbin.get_bus()
bus.add_signal_watch()
bus.connect ("message", bus_call, loop)
# start play back and listed to events
playbin.set_state(Gst.State.PLAYING)
try:
loop.run()
except:
pass
# cleanup
playbin.set_state(Gst.State.NULL)
if __name__ == '__main__':
sys.exit(main(sys.argv))

View file

@ -0,0 +1,242 @@
'''
Element that transforms audio samples to video frames representing
the waveform.
Requires matplotlib, numpy and numpy_ringbuffer
Example pipeline:
gst-launch-1.0 audiotestsrc ! audioplot window-duration=0.01 ! videoconvert ! autovideosink
'''
import gi
gi.require_version('Gst', '1.0')
gi.require_version('GstBase', '1.0')
gi.require_version('GstAudio', '1.0')
gi.require_version('GstVideo', '1.0')
from gi.repository import Gst, GLib, GObject, GstBase, GstAudio, GstVideo
try:
import numpy as np
import matplotlib.patheffects as pe
from numpy_ringbuffer import RingBuffer
from matplotlib import pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg
except ImportError:
Gst.error('audioplot requires numpy, numpy_ringbuffer and matplotlib')
raise
Gst.init(None)
AUDIO_FORMATS = [f.strip() for f in
GstAudio.AUDIO_FORMATS_ALL.strip('{ }').split(',')]
ICAPS = Gst.Caps(Gst.Structure('audio/x-raw',
format=Gst.ValueList(AUDIO_FORMATS),
layout='interleaved',
rate = Gst.IntRange(range(1, GLib.MAXINT)),
channels = Gst.IntRange(range(1, GLib.MAXINT))))
OCAPS = Gst.Caps(Gst.Structure('video/x-raw',
format='ARGB',
width=Gst.IntRange(range(1, GLib.MAXINT)),
height=Gst.IntRange(range(1, GLib.MAXINT)),
framerate=Gst.FractionRange(Gst.Fraction(1, 1),
Gst.Fraction(GLib.MAXINT, 1))))
DEFAULT_WINDOW_DURATION = 1.0
DEFAULT_WIDTH = 640
DEFAULT_HEIGHT = 480
DEFAULT_FRAMERATE_NUM = 25
DEFAULT_FRAMERATE_DENOM = 1
class AudioPlotFilter(GstBase.BaseTransform):
__gstmetadata__ = ('AudioPlotFilter','Filter', \
'Plot audio waveforms', 'Mathieu Duponchelle')
__gsttemplates__ = (Gst.PadTemplate.new("src",
Gst.PadDirection.SRC,
Gst.PadPresence.ALWAYS,
OCAPS),
Gst.PadTemplate.new("sink",
Gst.PadDirection.SINK,
Gst.PadPresence.ALWAYS,
ICAPS))
__gproperties__ = {
"window-duration": (float,
"Window Duration",
"Duration of the sliding window, in seconds",
0.01,
100.0,
DEFAULT_WINDOW_DURATION,
GObject.ParamFlags.READWRITE
)
}
def __init__(self):
GstBase.BaseTransform.__init__(self)
self.window_duration = DEFAULT_WINDOW_DURATION
def do_get_property(self, prop):
if prop.name == 'window-duration':
return self.window_duration
else:
raise AttributeError('unknown property %s' % prop.name)
def do_set_property(self, prop, value):
if prop.name == 'window-duration':
self.window_duration = value
else:
raise AttributeError('unknown property %s' % prop.name)
def do_transform(self, inbuf, outbuf):
if not self.h:
self.h, = self.ax.plot(np.array(self.ringbuffer),
lw=0.5,
color='k',
path_effects=[pe.Stroke(linewidth=1.0,
foreground='g'),
pe.Normal()])
else:
self.h.set_ydata(np.array(self.ringbuffer))
self.fig.canvas.restore_region(self.background)
self.ax.draw_artist(self.h)
self.fig.canvas.blit(self.ax.bbox)
s = self.agg.tostring_argb()
outbuf.fill(0, s)
outbuf.pts = self.next_time
outbuf.duration = self.frame_duration
self.next_time += self.frame_duration
return Gst.FlowReturn.OK
def __append(self, data):
arr = np.array(data)
end = self.thinning_factor * int(len(arr) / self.thinning_factor)
arr = np.mean(arr[:end].reshape(-1, self.thinning_factor), 1)
self.ringbuffer.extend(arr)
def do_generate_output(self):
inbuf = self.queued_buf
_, info = inbuf.map(Gst.MapFlags.READ)
res, data = self.converter.convert(GstAudio.AudioConverterFlags.NONE,
info.data)
data = memoryview(data).cast('i')
nsamples = len(data) - self.buf_offset
if nsamples == 0:
self.buf_offset = 0
inbuf.unmap(info)
return Gst.FlowReturn.OK, None
if self.cur_offset + nsamples < self.next_offset:
self.__append(data[self.buf_offset:])
self.buf_offset = 0
self.cur_offset += nsamples
inbuf.unmap(info)
return Gst.FlowReturn.OK, None
consumed = self.next_offset - self.cur_offset
self.__append(data[self.buf_offset:self.buf_offset + consumed])
inbuf.unmap(info)
_, outbuf = GstBase.BaseTransform.do_prepare_output_buffer(self, inbuf)
ret = self.do_transform(inbuf, outbuf)
self.next_offset += self.samplesperbuffer
self.cur_offset += consumed
self.buf_offset += consumed
return ret, outbuf
def do_transform_caps(self, direction, caps, filter_):
if direction == Gst.PadDirection.SRC:
res = ICAPS
else:
res = OCAPS
if filter_:
res = res.intersect(filter_)
return res
def do_fixate_caps(self, direction, caps, othercaps):
if direction == Gst.PadDirection.SRC:
return othercaps.fixate()
else:
so = othercaps.get_structure(0).copy()
so.fixate_field_nearest_fraction("framerate",
DEFAULT_FRAMERATE_NUM,
DEFAULT_FRAMERATE_DENOM)
so.fixate_field_nearest_int("width", DEFAULT_WIDTH)
so.fixate_field_nearest_int("height", DEFAULT_HEIGHT)
ret = Gst.Caps.new_empty()
ret.append_structure(so)
return ret.fixate()
def do_set_caps(self, icaps, ocaps):
in_info = GstAudio.AudioInfo()
in_info.from_caps(icaps)
out_info = GstVideo.VideoInfo()
out_info.from_caps(ocaps)
self.convert_info = GstAudio.AudioInfo()
self.convert_info.set_format(GstAudio.AudioFormat.S32,
in_info.rate,
in_info.channels,
in_info.position)
self.converter = GstAudio.AudioConverter.new(GstAudio.AudioConverterFlags.NONE,
in_info,
self.convert_info,
None)
self.fig = plt.figure()
dpi = self.fig.get_dpi()
self.fig.patch.set_alpha(0.3)
self.fig.set_size_inches(out_info.width / float(dpi),
out_info.height / float(dpi))
self.ax = plt.Axes(self.fig, [0., 0., 1., 1.])
self.fig.add_axes(self.ax)
self.ax.set_axis_off()
self.ax.set_ylim((GLib.MININT, GLib.MAXINT))
self.agg = self.fig.canvas.switch_backends(FigureCanvasAgg)
self.h = None
samplesperwindow = int(in_info.rate * in_info.channels * self.window_duration)
self.thinning_factor = max(int(samplesperwindow / out_info.width - 1), 1)
cap = int(samplesperwindow / self.thinning_factor)
self.ax.set_xlim([0, cap])
self.ringbuffer = RingBuffer(capacity=cap)
self.ringbuffer.extend([0.0] * cap)
self.frame_duration = Gst.util_uint64_scale_int(Gst.SECOND,
out_info.fps_d,
out_info.fps_n)
self.next_time = self.frame_duration
self.agg.draw()
self.background = self.fig.canvas.copy_from_bbox(self.ax.bbox)
self.samplesperbuffer = Gst.util_uint64_scale_int(in_info.rate * in_info.channels,
out_info.fps_d,
out_info.fps_n)
self.next_offset = self.samplesperbuffer
self.cur_offset = 0
self.buf_offset = 0
return True
GObject.type_register(AudioPlotFilter)
__gstelementfactory__ = ("audioplot", Gst.Rank.NONE, AudioPlotFilter)

View file

@ -0,0 +1,53 @@
#!/usr/bin/python3
# exampleTransform.py
# 2019 Daniel Klamt <graphics@pengutronix.de>
# Inverts a grayscale image in place, requires numpy.
#
# gst-launch-1.0 videotestsrc ! ExampleTransform ! videoconvert ! xvimagesink
import gi
gi.require_version('Gst', '1.0')
gi.require_version('GstBase', '1.0')
gi.require_version('GstVideo', '1.0')
from gi.repository import Gst, GObject, GstBase, GstVideo
import numpy as np
Gst.init(None)
FIXED_CAPS = Gst.Caps.from_string('video/x-raw,format=GRAY8,width=[1,2147483647],height=[1,2147483647]')
class ExampleTransform(GstBase.BaseTransform):
__gstmetadata__ = ('ExampleTransform Python','Transform',
'example gst-python element that can modify the buffer gst-launch-1.0 videotestsrc ! ExampleTransform ! videoconvert ! xvimagesink', 'dkl')
__gsttemplates__ = (Gst.PadTemplate.new("src",
Gst.PadDirection.SRC,
Gst.PadPresence.ALWAYS,
FIXED_CAPS),
Gst.PadTemplate.new("sink",
Gst.PadDirection.SINK,
Gst.PadPresence.ALWAYS,
FIXED_CAPS))
def do_set_caps(self, incaps, outcaps):
struct = incaps.get_structure(0)
self.width = struct.get_int("width").value
self.height = struct.get_int("height").value
return True
def do_transform_ip(self, buf):
try:
with buf.map(Gst.MapFlags.READ | Gst.MapFlags.WRITE) as info:
# Create a NumPy ndarray from the memoryview and modify it in place:
A = np.ndarray(shape = (self.height, self.width), dtype = np.uint8, buffer = info.data)
A[:] = np.invert(A)
return Gst.FlowReturn.OK
except Gst.MapError as e:
Gst.error("Mapping error: %s" % e)
return Gst.FlowReturn.ERROR
GObject.type_register(ExampleTransform)
__gstelementfactory__ = ("ExampleTransform", Gst.Rank.NONE, ExampleTransform)

View file

@ -0,0 +1,42 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# identity.py
# 2016 Marianna S. Buschle <msb@qtec.com>
#
# Simple identity element in python
#
# You can run the example from the source doing from gst-python/:
#
# $ export GST_PLUGIN_PATH=$GST_PLUGIN_PATH:$PWD/plugin:$PWD/examples/plugins
# $ GST_DEBUG=python:4 gst-launch-1.0 fakesrc num-buffers=10 ! identity_py ! fakesink
import gi
gi.require_version('GstBase', '1.0')
from gi.repository import Gst, GObject, GstBase
Gst.init(None)
#
# Simple Identity element created entirely in python
#
class Identity(GstBase.BaseTransform):
__gstmetadata__ = ('Identity Python','Transform', \
'Simple identity element written in python', 'Marianna S. Buschle')
__gsttemplates__ = (Gst.PadTemplate.new("src",
Gst.PadDirection.SRC,
Gst.PadPresence.ALWAYS,
Gst.Caps.new_any()),
Gst.PadTemplate.new("sink",
Gst.PadDirection.SINK,
Gst.PadPresence.ALWAYS,
Gst.Caps.new_any()))
def do_transform_ip(self, buffer):
Gst.info("timestamp(buffer):%s" % (Gst.TIME_ARGS(buffer.pts)))
return Gst.FlowReturn.OK
GObject.type_register(Identity)
__gstelementfactory__ = ("identity_py", Gst.Rank.NONE, Identity)

View file

@ -0,0 +1,104 @@
'''
Simple mixer element, accepts 320 x 240 RGBA at 30 fps
on any number of sinkpads.
Requires PIL (Python Imaging Library)
Example pipeline:
gst-launch-1.0 py_videomixer name=mixer ! videoconvert ! autovideosink \
videotestsrc ! mixer. \
videotestsrc pattern=ball ! mixer. \
videotestsrc pattern=snow ! mixer.
'''
import gi
gi.require_version('Gst', '1.0')
gi.require_version('GstBase', '1.0')
gi.require_version('GObject', '2.0')
from gi.repository import Gst, GObject, GstBase
Gst.init(None)
try:
from PIL import Image
except ImportError:
Gst.error('py_videomixer requires PIL')
raise
# Completely fixed input / output
ICAPS = Gst.Caps(Gst.Structure('video/x-raw',
format='RGBA',
width=320,
height=240,
framerate=Gst.Fraction(30, 1)))
OCAPS = Gst.Caps(Gst.Structure('video/x-raw',
format='RGBA',
width=320,
height=240,
framerate=Gst.Fraction(30, 1)))
class BlendData:
def __init__(self, outimg):
self.outimg = outimg
self.pts = 0
self.eos = True
class Videomixer(GstBase.Aggregator):
__gstmetadata__ = ('Videomixer','Video/Mixer', \
'Python video mixer', 'Mathieu Duponchelle')
__gsttemplates__ = (
Gst.PadTemplate.new_with_gtype("sink_%u",
Gst.PadDirection.SINK,
Gst.PadPresence.REQUEST,
ICAPS,
GstBase.AggregatorPad.__gtype__),
Gst.PadTemplate.new_with_gtype("src",
Gst.PadDirection.SRC,
Gst.PadPresence.ALWAYS,
OCAPS,
GstBase.AggregatorPad.__gtype__)
)
def mix_buffers(self, agg, pad, bdata):
buf = pad.pop_buffer()
_, info = buf.map(Gst.MapFlags.READ)
img = Image.frombuffer('RGBA', (320, 240), info.data, "raw", 'RGBA', 0, 1)
bdata.outimg = Image.blend(bdata.outimg, img, alpha=0.5)
bdata.pts = buf.pts
buf.unmap(info)
bdata.eos = False
return True
def do_aggregate(self, timeout):
outimg = Image.new('RGBA', (320, 240), 0x00000000)
bdata = BlendData(outimg)
self.foreach_sink_pad(self.mix_buffers, bdata)
data = bdata.outimg.tobytes()
outbuf = Gst.Buffer.new_allocate(None, len(data), None)
outbuf.fill(0, data)
outbuf.pts = bdata.pts
self.finish_buffer (outbuf)
# We are EOS when no pad was ready to be aggregated,
# this would obviously not work for live
if bdata.eos:
return Gst.FlowReturn.EOS
return Gst.FlowReturn.OK
GObject.type_register(Videomixer)
__gstelementfactory__ = ("py_videomixer", Gst.Rank.NONE, Videomixer)

View file

@ -0,0 +1,193 @@
'''
Element that generates a sine audio wave with the specified frequency
Requires numpy
Example pipeline:
gst-launch-1.0 py_audiotestsrc ! autoaudiosink
'''
import gi
gi.require_version('Gst', '1.0')
gi.require_version('GstBase', '1.0')
gi.require_version('GstAudio', '1.0')
from gi.repository import Gst, GLib, GObject, GstBase, GstAudio
try:
import numpy as np
except ImportError:
Gst.error('py_audiotestsrc requires numpy')
raise
OCAPS = Gst.Caps.from_string (
'audio/x-raw, format=F32LE, layout=interleaved, rate=44100, channels=2')
SAMPLESPERBUFFER = 1024
DEFAULT_FREQ = 440
DEFAULT_VOLUME = 0.8
DEFAULT_MUTE = False
DEFAULT_IS_LIVE = False
class AudioTestSrc(GstBase.BaseSrc):
__gstmetadata__ = ('CustomSrc','Src', \
'Custom test src element', 'Mathieu Duponchelle')
__gproperties__ = {
"freq": (int,
"Frequency",
"Frequency of test signal",
1,
GLib.MAXINT,
DEFAULT_FREQ,
GObject.ParamFlags.READWRITE
),
"volume": (float,
"Volume",
"Volume of test signal",
0.0,
1.0,
DEFAULT_VOLUME,
GObject.ParamFlags.READWRITE
),
"mute": (bool,
"Mute",
"Mute the test signal",
DEFAULT_MUTE,
GObject.ParamFlags.READWRITE
),
"is-live": (bool,
"Is live",
"Whether to act as a live source",
DEFAULT_IS_LIVE,
GObject.ParamFlags.READWRITE
),
}
__gsttemplates__ = Gst.PadTemplate.new("src",
Gst.PadDirection.SRC,
Gst.PadPresence.ALWAYS,
OCAPS)
def __init__(self):
GstBase.BaseSrc.__init__(self)
self.info = GstAudio.AudioInfo()
self.freq = DEFAULT_FREQ
self.volume = DEFAULT_VOLUME
self.mute = DEFAULT_MUTE
self.set_live(DEFAULT_IS_LIVE)
self.set_format(Gst.Format.TIME)
def do_set_caps(self, caps):
self.info.from_caps(caps)
self.set_blocksize(self.info.bpf * SAMPLESPERBUFFER)
return True
def do_get_property(self, prop):
if prop.name == 'freq':
return self.freq
elif prop.name == 'volume':
return self.volume
elif prop.name == 'mute':
return self.mute
elif prop.name == 'is-live':
return self.is_live
else:
raise AttributeError('unknown property %s' % prop.name)
def do_set_property(self, prop, value):
if prop.name == 'freq':
self.freq = value
elif prop.name == 'volume':
self.volume = value
elif prop.name == 'mute':
self.mute = value
elif prop.name == 'is-live':
self.set_live(value)
else:
raise AttributeError('unknown property %s' % prop.name)
def do_start (self):
self.next_sample = 0
self.next_byte = 0
self.next_time = 0
self.accumulator = 0
self.generate_samples_per_buffer = SAMPLESPERBUFFER
return True
def do_gst_base_src_query(self, query):
if query.type == Gst.QueryType.LATENCY:
latency = Gst.util_uint64_scale_int(self.generate_samples_per_buffer,
Gst.SECOND, self.info.rate)
is_live = self.is_live
query.set_latency(is_live, latency, Gst.CLOCK_TIME_NONE)
res = True
else:
res = GstBase.BaseSrc.do_query(self, query)
return res
def do_get_times(self, buf):
end = 0
start = 0
if self.is_live:
ts = buf.pts
if ts != Gst.CLOCK_TIME_NONE:
duration = buf.duration
if duration != Gst.CLOCK_TIME_NONE:
end = ts + duration
start = ts
else:
start = Gst.CLOCK_TIME_NONE
end = Gst.CLOCK_TIME_NONE
return start, end
def do_fill(self, offset, length, buf):
if length == -1:
samples = SAMPLESPERBUFFER
else:
samples = int(length / self.info.bpf)
self.generate_samples_per_buffer = samples
bytes_ = samples * self.info.bpf
next_sample = self.next_sample + samples
next_byte = self.next_byte + bytes_
next_time = Gst.util_uint64_scale_int(next_sample, Gst.SECOND, self.info.rate)
try:
with buf.map(Gst.MapFlags.WRITE) as info:
array = np.ndarray(shape = self.info.channels * samples, dtype = np.float32, buffer = info.data)
if not self.mute:
r = np.repeat(np.arange(self.accumulator, self.accumulator + samples),
self.info.channels)
np.sin(2 * np.pi * r * self.freq / self.info.rate, out=array)
array *= self.volume
else:
array[:] = 0
except Exception as e:
Gst.error("Mapping error: %s" % e)
return Gst.FlowReturn.ERROR
buf.offset = self.next_sample
buf.offset_end = next_sample
buf.pts = self.next_time
buf.duration = next_time - self.next_time
self.next_time = next_time
self.next_sample = next_sample
self.next_byte = next_byte
self.accumulator += samples
self.accumulator %= self.info.rate / self.freq
return (Gst.FlowReturn.OK, buf)
__gstelementfactory__ = ("py_audiotestsrc", Gst.Rank.NONE, AudioTestSrc)

View file

@ -0,0 +1,39 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# sinkelement.py
# (c) 2005 Edward Hervey <edward@fluendo.com>
# (c) 2007 Jan Schmidt <jan@fluendo.com>
# Licensed under LGPL
#
# Small test application to show how to write a sink element
# in 20 lines in python and place into the gstreamer registry
# so it can be autoplugged or used from parse_launch.
#
# You can run the example from the source doing from gst-python/:
#
# $ export GST_PLUGIN_PATH=$GST_PLUGIN_PATH:$PWD/plugin:$PWD/examples/plugins
# $ GST_DEBUG=python:4 gst-launch-1.0 fakesrc num-buffers=10 ! mysink
from gi.repository import Gst, GObject, GstBase
Gst.init(None)
#
# Simple Sink element created entirely in python
#
class MySink(GstBase.BaseSink):
__gstmetadata__ = ('CustomSink','Sink', \
'Custom test sink element', 'Edward Hervey')
__gsttemplates__ = Gst.PadTemplate.new("sink",
Gst.PadDirection.SINK,
Gst.PadPresence.ALWAYS,
Gst.Caps.new_any())
def do_render(self, buffer):
Gst.info("timestamp(buffer):%s" % (Gst.TIME_ARGS(buffer.pts)))
return Gst.FlowReturn.OK
GObject.type_register(MySink)
__gstelementfactory__ = ("mysink", Gst.Rank.NONE, MySink)

89
examples/record_sound.py Executable file
View file

@ -0,0 +1,89 @@
#!/usr/bin/env python3
'''
Simple example to demonstrate using Gst.DeviceMonitor and `transcodebin` to
record audio from a microphone into an .oga file
'''
import gi
import sys
gi.require_version('Gst', '1.0')
gi.require_version('GstPbutils', '1.0')
gi.require_version('GLib', '2.0')
gi.require_version('GObject', '2.0')
from gi.repository import GLib, GObject, Gst, GstPbutils
def bus_call(bus, message, loop):
t = message.type
Gst.debug_bin_to_dot_file_with_ts(pipeline, Gst.DebugGraphDetails.ALL, "test")
if t == Gst.MessageType.EOS:
sys.stdout.write("End-of-stream\n")
loop.quit()
elif t == Gst.MessageType.ERROR:
err, debug = message.parse_error()
sys.stderr.write("Error: %s: %s\n" % (err, debug))
loop.quit()
return True
def stop(loop, pipeline):
_, position = pipeline.query_position(Gst.Format.TIME)
print("Position: %s\r" % Gst.TIME_ARGS(position))
if position > 10 * Gst.SECOND:
loop.quit()
print("Stopping after 10 seconds")
return False
return True
if __name__ == "__main__":
Gst.init(sys.argv)
if len(sys.argv) != 2:
print("Missing <output-location> parametter")
sys.exit(1)
monitor = Gst.DeviceMonitor.new()
monitor.add_filter("Audio/Source", None)
monitor.start()
# This is happening synchonously, use the GstBus based API and
# monitor.start() to avoid blocking the main thread.
devices = monitor.get_devices()
if not devices:
print("No microphone found...")
sys.exit(1)
default = [d for d in devices if d.get_properties().get_value("is-default") is True]
if len(default) == 1:
device = default[0]
else:
print("Avalaible microphones:")
for i, d in enumerate(devices):
print("%d - %s" % (i, d.get_display_name()))
res = int(input("Select device: "))
device = devices[res]
pipeline = Gst.ElementFactory.make("pipeline", None)
source = device.create_element()
transcodebin = Gst.ElementFactory.make("transcodebin", None)
Gst.util_set_object_arg(transcodebin, "profile", "video/ogg:audio/x-opus")
filesink = Gst.ElementFactory.make("filesink", None)
filesink.props.location = sys.argv[1]
pipeline.add(source, transcodebin, filesink)
source.link(transcodebin)
transcodebin.link(filesink)
pipeline.set_state(Gst.State.PLAYING)
bus = pipeline.get_bus()
bus.add_signal_watch()
loop = GLib.MainLoop()
GLib.timeout_add_seconds(1, stop, loop, pipeline)
bus.connect ("message", bus_call, loop)
loop.run()
pipeline.set_state(Gst.State.NULL)
pipeline.get_state(Gst.CLOCK_TIME_NONE)

View file

@ -0,0 +1,9 @@
# py_videomixer plugin
Pillow >= 5.1.0
# audioplot plugin
matplotlib >= 2.1.1
numpy_ringbuffer >= 0.2.1
# audioplot and py_audiotestsrc plugins
numpy >= 1.14.5

1
gi/meson.build Normal file
View file

@ -0,0 +1 @@
subdir('overrides')

746
gi/overrides/Gst.py Normal file
View file

@ -0,0 +1,746 @@
# -*- Mode: Python; py-indent-offset: 4 -*-
# vim: tabstop=4 shiftwidth=4 expandtab
#
# Gst.py
#
# Copyright (C) 2012 Thibault Saunier <thibault.saunier@collabora.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
import sys
import inspect
import itertools
import weakref
from ..overrides import override
from ..module import get_introspection_module
from gi.repository import GLib
Gst = get_introspection_module('Gst')
__all__ = []
if Gst._version == '0.10':
import warnings
warn_msg = "You have imported the Gst 0.10 module. Because Gst 0.10 \
was not designed for use with introspection some of the \
interfaces and API will fail. As such this is not supported \
by the GStreamer development team and we encourage you to \
port your app to Gst 1 or greater. gst-python is the recommended \
python module to use with Gst 0.10"
warnings.warn(warn_msg, RuntimeWarning)
class Element(Gst.Element):
@staticmethod
def link_many(*args):
'''
@raises: Gst.LinkError
'''
for pair in pairwise(args):
if not pair[0].link(pair[1]):
raise LinkError(
'Failed to link {} and {}'.format(pair[0], pair[1]))
Element = override(Element)
__all__.append('Element')
class Bin(Gst.Bin):
def __init__(self, name=None):
Gst.Bin.__init__(self, name=name)
def add(self, *args):
for arg in args:
if not Gst.Bin.add(self, arg):
raise AddError(arg)
def make_and_add(self, factory_name, instance_name=None):
'''
@raises: Gst.AddError
'''
elem = Gst.ElementFactory.make(factory_name, instance_name)
if not elem:
raise AddError(
'No such element: {}'.format(factory_name))
self.add(elem)
return elem
Bin = override(Bin)
__all__.append('Bin')
class Caps(Gst.Caps):
def __nonzero__(self):
return not self.is_empty()
def __new__(cls, *args):
if not args:
return Caps.new_empty()
elif len(args) > 1:
raise TypeError("wrong arguments when creating GstCaps object")
elif isinstance(args[0], str):
return Caps.from_string(args[0])
elif isinstance(args[0], Caps):
return args[0].copy()
elif isinstance(args[0], Structure):
res = Caps.new_empty()
res.append_structure(args[0])
return res
elif isinstance(args[0], (list, tuple)):
res = Caps.new_empty()
for e in args[0]:
res.append_structure(e)
return res
raise TypeError("wrong arguments when creating GstCaps object")
def __init__(self, *args, **kwargs):
return super(Caps, self).__init__()
def __str__(self):
return self.to_string()
def __getitem__(self, index):
if index >= self.get_size():
raise IndexError('structure index out of range')
return self.get_structure(index)
def __len__(self):
return self.get_size()
Caps = override(Caps)
__all__.append('Caps')
class PadFunc:
def __init__(self, func):
self.func = func
def __call__(self, pad, parent, obj):
if isinstance(self.func, weakref.WeakMethod):
func = self.func()
else:
func = self.func
try:
res = func(pad, obj)
except TypeError:
try:
res = func(pad, parent, obj)
except TypeError:
raise TypeError("Invalid method %s, 2 or 3 arguments required"
% func)
return res
class Pad(Gst.Pad):
def __init__(self, *args, **kwargs):
super(Gst.Pad, self).__init__(*args, **kwargs)
def set_chain_function(self, func):
self.set_chain_function_full(PadFunc(func), None)
def set_event_function(self, func):
self.set_event_function_full(PadFunc(func), None)
def set_query_function(self, func):
self.set_query_function_full(PadFunc(func), None)
def query_caps(self, filter=None):
return Gst.Pad.query_caps(self, filter)
def set_caps(self, caps):
if not isinstance(caps, Gst.Caps):
raise TypeError("%s is not a Gst.Caps." % (type(caps)))
if not caps.is_fixed():
return False
event = Gst.Event.new_caps(caps)
if self.direction == Gst.PadDirection.SRC:
res = self.push_event(event)
else:
res = self.send_event(event)
return res
def link(self, pad):
ret = Gst.Pad.link(self, pad)
if ret != Gst.PadLinkReturn.OK:
raise LinkError(ret)
return ret
Pad = override(Pad)
__all__.append('Pad')
class GhostPad(Gst.GhostPad):
def __init__(self, name, target=None, direction=None):
if direction is None:
if target is None:
raise TypeError('you must pass at least one of target '
'and direction')
direction = target.props.direction
Gst.GhostPad.__init__(self, name=name, direction=direction)
self.construct()
if target is not None:
self.set_target(target)
def query_caps(self, filter=None):
return Gst.GhostPad.query_caps(self, filter)
GhostPad = override(GhostPad)
__all__.append('GhostPad')
class IteratorError(Exception):
pass
__all__.append('IteratorError')
class AddError(Exception):
pass
__all__.append('AddError')
class LinkError(Exception):
pass
__all__.append('LinkError')
class MapError(Exception):
pass
__all__.append('MapError')
class Iterator(Gst.Iterator):
def __iter__(self):
while True:
result, value = self.next()
if result == Gst.IteratorResult.DONE:
break
if result != Gst.IteratorResult.OK:
raise IteratorError(result)
yield value
Iterator = override(Iterator)
__all__.append('Iterator')
class ElementFactory(Gst.ElementFactory):
# ElementFactory
def get_longname(self):
return self.get_metadata("long-name")
def get_description(self):
return self.get_metadata("description")
def get_klass(self):
return self.get_metadata("klass")
@classmethod
def make(cls, factory_name, instance_name=None):
return Gst.ElementFactory.make(factory_name, instance_name)
class Pipeline(Gst.Pipeline):
def __init__(self, name=None):
Gst.Pipeline.__init__(self, name=name)
Pipeline = override(Pipeline)
__all__.append('Pipeline')
class Structure(Gst.Structure):
def __new__(cls, *args, **kwargs):
if not args:
if kwargs:
raise TypeError("wrong arguments when creating GstStructure, first argument"
" must be the structure name.")
return Structure.new_empty()
elif len(args) > 1:
raise TypeError("wrong arguments when creating GstStructure object")
elif isinstance(args[0], str):
if not kwargs:
return Structure.from_string(args[0])[0]
struct = Structure.new_empty(args[0])
for k, v in kwargs.items():
struct[k] = v
return struct
elif isinstance(args[0], Structure):
return args[0].copy()
raise TypeError("wrong arguments when creating GstStructure object")
def __init__(self, *args, **kwargs):
pass
def __getitem__(self, key):
return self.get_value(key)
def keys(self):
keys = set()
def foreach(fid, value, unused1, udata):
keys.add(GLib.quark_to_string(fid))
return True
self.foreach(foreach, None, None)
return keys
def __setitem__(self, key, value):
return self.set_value(key, value)
def __str__(self):
return self.to_string()
Structure = override(Structure)
__all__.append('Structure')
ElementFactory = override(ElementFactory)
__all__.append('ElementFactory')
class Fraction(Gst.Fraction):
def __init__(self, num, denom=1):
def __gcd(a, b):
while b != 0:
tmp = a
a = b
b = tmp % b
return abs(a)
def __simplify():
num = self.num
denom = self.denom
if num < 0:
num = -num
denom = -denom
# Compute greatest common divisor
gcd = __gcd(num, denom)
if gcd != 0:
num /= gcd
denom /= gcd
self.num = num
self.denom = denom
self.num = num
self.denom = denom
__simplify()
self.type = "fraction"
def __repr__(self):
return '<Gst.Fraction %s>' % (str(self))
def __value__(self):
return self.num / self.denom
def __eq__(self, other):
if isinstance(other, Fraction):
return self.num * other.denom == other.num * self.denom
return False
def __ne__(self, other):
return not self.__eq__(other)
def __mul__(self, other):
if isinstance(other, Fraction):
return Fraction(self.num * other.num,
self.denom * other.denom)
elif isinstance(other, int):
return Fraction(self.num * other, self.denom)
raise TypeError("%s is not supported, use Gst.Fraction or int." %
(type(other)))
__rmul__ = __mul__
def __truediv__(self, other):
if isinstance(other, Fraction):
return Fraction(self.num * other.denom,
self.denom * other.num)
elif isinstance(other, int):
return Fraction(self.num, self.denom * other)
return TypeError("%s is not supported, use Gst.Fraction or int." %
(type(other)))
__div__ = __truediv__
def __rtruediv__(self, other):
if isinstance(other, int):
return Fraction(self.denom * other, self.num)
return TypeError("%s is not an int." % (type(other)))
__rdiv__ = __rtruediv__
def __float__(self):
return float(self.num) / float(self.denom)
def __str__(self):
return '%d/%d' % (self.num, self.denom)
Fraction = override(Fraction)
__all__.append('Fraction')
class IntRange(Gst.IntRange):
def __init__(self, r):
if not isinstance(r, range):
raise TypeError("%s is not a range." % (type(r)))
if (r.start >= r.stop):
raise TypeError("Range start must be smaller then stop")
if r.start % r.step != 0:
raise TypeError("Range start must be a multiple of the step")
if r.stop % r.step != 0:
raise TypeError("Range stop must be a multiple of the step")
self.range = r
def __repr__(self):
return '<Gst.IntRange [%d,%d,%d]>' % (self.range.start,
self.range.stop, self.range.step)
def __str__(self):
if self.range.step == 1:
return '[%d,%d]' % (self.range.start, self.range.stop)
else:
return '[%d,%d,%d]' % (self.range.start, self.range.stop,
self.range.step)
def __eq__(self, other):
if isinstance(other, range):
return self.range == other
elif isinstance(other, IntRange):
return self.range == other.range
return False
if sys.version_info >= (3, 0):
IntRange = override(IntRange)
__all__.append('IntRange')
class Int64Range(Gst.Int64Range):
def __init__(self, r):
if not isinstance(r, range):
raise TypeError("%s is not a range." % (type(r)))
if (r.start >= r.stop):
raise TypeError("Range start must be smaller then stop")
if r.start % r.step != 0:
raise TypeError("Range start must be a multiple of the step")
if r.stop % r.step != 0:
raise TypeError("Range stop must be a multiple of the step")
self.range = r
def __repr__(self):
return '<Gst.Int64Range [%d,%d,%d]>' % (self.range.start,
self.range.stop, self.range.step)
def __str__(self):
if self.range.step == 1:
return '(int64)[%d,%d]' % (self.range.start, self.range.stop)
else:
return '(int64)[%d,%d,%d]' % (self.range.start, self.range.stop,
self.range.step)
def __eq__(self, other):
if isinstance(other, range):
return self.range == other
elif isinstance(other, IntRange):
return self.range == other.range
return False
class Bitmask(Gst.Bitmask):
def __init__(self, v):
if not isinstance(v, int):
raise TypeError("%s is not an int." % (type(v)))
self.v = int(v)
def __str__(self):
return hex(self.v)
def __eq__(self, other):
return self.v == other
Bitmask = override(Bitmask)
__all__.append('Bitmask')
if sys.version_info >= (3, 0):
Int64Range = override(Int64Range)
__all__.append('Int64Range')
class DoubleRange(Gst.DoubleRange):
def __init__(self, start, stop):
self.start = float(start)
self.stop = float(stop)
if (start >= stop):
raise TypeError("Range start must be smaller then stop")
def __repr__(self):
return '<Gst.DoubleRange [%s,%s]>' % (str(self.start), str(self.stop))
def __str__(self):
return '(double)[%s,%s]' % (str(self.range.start), str(self.range.stop))
DoubleRange = override(DoubleRange)
__all__.append('DoubleRange')
class FractionRange(Gst.FractionRange):
def __init__(self, start, stop):
if not isinstance(start, Gst.Fraction):
raise TypeError("%s is not a Gst.Fraction." % (type(start)))
if not isinstance(stop, Gst.Fraction):
raise TypeError("%s is not a Gst.Fraction." % (type(stop)))
if (float(start) >= float(stop)):
raise TypeError("Range start must be smaller then stop")
self.start = start
self.stop = stop
def __repr__(self):
return '<Gst.FractionRange [%s,%s]>' % (str(self.start),
str(self.stop))
def __str__(self):
return '(fraction)[%s,%s]' % (str(self.start), str(self.stop))
FractionRange = override(FractionRange)
__all__.append('FractionRange')
class ValueArray(Gst.ValueArray):
def __init__(self, array):
self.array = list(array)
def __getitem__(self, index):
return self.array[index]
def __setitem__(self, index, value):
self.array[index] = value
def __len__(self):
return len(self.array)
def __str__(self):
return '<' + ','.join(map(str,self.array)) + '>'
def __repr__(self):
return '<Gst.ValueArray %s>' % (str(self))
ValueArray = override(ValueArray)
__all__.append('ValueArray')
class ValueList(Gst.ValueList):
def __init__(self, array):
self.array = list(array)
def __getitem__(self, index):
return self.array[index]
def __setitem__(self, index, value):
self.array[index] = value
def __len__(self):
return len(self.array)
def __str__(self):
return '{' + ','.join(map(str,self.array)) + '}'
def __repr__(self):
return '<Gst.ValueList %s>' % (str(self))
ValueList = override(ValueList)
__all__.append('ValueList')
# From https://docs.python.org/3/library/itertools.html
def pairwise(iterable):
a, b = itertools.tee(iterable)
next(b, None)
return zip(a, b)
class MapInfo:
def __init__(self):
self.memory = None
self.flags = Gst.MapFlags(0)
self.size = 0
self.maxsize = 0
self.data = None
self.user_data = None
self.__parent__ = None
def __iter__(self):
# Make it behave like a tuple similar to the PyGObject generated API for
# the `Gst.Buffer.map()` and friends.
for i in (self.__parent__ is not None, self):
yield i
def __enter__(self):
if not self.__parent__:
raise MapError('MappingError', 'Mapping was not successful')
return self
def __exit__(self, type, value, tb):
if not self.__parent__.unmap(self):
raise MapError('MappingError', 'Unmapping was not successful')
__all__.append("MapInfo")
class Buffer(Gst.Buffer):
def map_range(self, idx, length, flags):
mapinfo = MapInfo()
if (_gi_gst.buffer_override_map_range(self, mapinfo, idx, length, int(flags))):
mapinfo.__parent__ = self
return mapinfo
def map(self, flags):
mapinfo = MapInfo()
if _gi_gst.buffer_override_map(self, mapinfo, int(flags)):
mapinfo.__parent__ = self
return mapinfo
def unmap(self, mapinfo):
mapinfo.__parent__ = None
return _gi_gst.buffer_override_unmap(self, mapinfo)
Buffer = override(Buffer)
__all__.append('Buffer')
class Memory(Gst.Memory):
def map(self, flags):
mapinfo = MapInfo()
if (_gi_gst.memory_override_map(self, mapinfo, int(flags))):
mapinfo.__parent__ = self
return mapinfo
def unmap(self, mapinfo):
mapinfo.__parent__ = None
return _gi_gst.memory_override_unmap(self, mapinfo)
Memory = override(Memory)
__all__.append('Memory')
def TIME_ARGS(time):
if time == Gst.CLOCK_TIME_NONE:
return "CLOCK_TIME_NONE"
return "%u:%02u:%02u.%09u" % (time / (Gst.SECOND * 60 * 60),
(time / (Gst.SECOND * 60)) % 60,
(time / Gst.SECOND) % 60,
time % Gst.SECOND)
__all__.append('TIME_ARGS')
from gi.overrides import _gi_gst
_gi_gst
# maybe more python and less C some day if core turns a bit more introspection
# and binding friendly in the debug area
Gst.trace = _gi_gst.trace
Gst.log = _gi_gst.log
Gst.debug = _gi_gst.debug
Gst.info = _gi_gst.info
Gst.warning = _gi_gst.warning
Gst.error = _gi_gst.error
Gst.fixme = _gi_gst.fixme
Gst.memdump = _gi_gst.memdump
# Make sure PyGst is not usable if GStreamer has not been initialized
class NotInitialized(Exception):
pass
__all__.append('NotInitialized')
def fake_method(*args):
raise NotInitialized("Please call Gst.init(argv) before using GStreamer")
real_functions = [o for o in inspect.getmembers(Gst) if isinstance(o[1], type(Gst.init))]
class_methods = []
for cname_klass in [o for o in inspect.getmembers(Gst) if isinstance(o[1], type(Gst.Element)) or isinstance(o[1], type(Gst.Caps))]:
class_methods.append((cname_klass,
[(o, cname_klass[1].__dict__[o])
for o in cname_klass[1].__dict__
if isinstance(cname_klass[1].__dict__[o], type(Gst.init))]))
def init_pygst():
for fname, function in real_functions:
if fname not in ["init", "init_check", "deinit"]:
setattr(Gst, fname, function)
for cname_class, methods in class_methods:
for mname, method in methods:
setattr(cname_class[1], mname, method)
def deinit_pygst():
for fname, func in real_functions:
if fname not in ["init", "init_check", "deinit", "is_initialized"]:
setattr(Gst, fname, fake_method)
for cname_class, methods in class_methods:
for mname, method in methods:
setattr(cname_class[1], mname, fake_method)
real_init = Gst.init
def init(argv):
init_pygst()
return real_init(argv)
Gst.init = init
real_init_check = Gst.init_check
def init_check(argv):
init_pygst()
return real_init_check(argv)
Gst.init_check = init_check
real_deinit = Gst.deinit
def deinit():
deinit_pygst()
return real_deinit()
Gst.deinit = deinit
if not Gst.is_initialized():
deinit_pygst()

View file

@ -0,0 +1,92 @@
# -*- Mode: Python; py-indent-offset: 4 -*-
# vim: tabstop=4 shiftwidth=4 expandtab
#
# Gst.py
#
# Copyright (C) 2012 Alessandro Decina <alessandro.d@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
from ..overrides import override as override_
from ..module import get_introspection_module
import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst # noqa
GstPbutils = get_introspection_module('GstPbutils')
__all__ = []
def override(cls):
name = cls.__name__
globals()[name] = override_(cls)
__all__.append(name)
return cls
real_init = GstPbutils.pb_utils_init
def init():
if not Gst.is_initialized():
raise RuntimeError("Gst.init() needs to be called before importing GstPbutils")
real_init()
@override
class EncodingVideoProfile(GstPbutils.EncodingVideoProfile):
def __init__(self, format, preset=None, restriction=None, presence=0):
GstPbutils.EncodingVideoProfile.__init__(self)
self.set_format(format)
if preset is not None:
self.set_preset(preset)
if restriction is None:
restriction = Gst.Caps('ANY')
self.set_restriction(restriction)
self.set_presence(presence)
@override
class EncodingAudioProfile(GstPbutils.EncodingAudioProfile):
def __init__(self, format, preset=None, restriction=None, presence=0):
GstPbutils.EncodingAudioProfile.__init__(self)
self.set_format(format)
if preset is not None:
self.set_preset(preset)
if restriction is None:
restriction = Gst.Caps('ANY')
self.set_restriction(restriction)
self.set_presence(presence)
@override
class EncodingContainerProfile(GstPbutils.EncodingContainerProfile):
def __init__(self, name, description, format, preset=None):
GstPbutils.EncodingContainerProfile.__init__(self)
self.set_format(format)
if name is not None:
self.set_name(name)
if description is not None:
self.set_description(description)
if preset is not None:
self.set_preset(preset)
GstPbutils.pb_utils_init = init
GstPbutils.init = init
if Gst.is_initialized():
init()

1070
gi/overrides/gstmodule.c Normal file

File diff suppressed because it is too large Load diff

10
gi/overrides/meson.build Normal file
View file

@ -0,0 +1,10 @@
pysources = ['Gst.py', 'GstPbutils.py']
install_data(pysources,
install_dir: pygi_override_dir)
gstpython = python.extension_module('_gi_gst',
sources: ['gstmodule.c'],
install: true,
install_dir : pygi_override_dir,
include_directories : [configinc],
dependencies : [gst_dep, python_dep, pygobject_dep])

662
gst-python.doap Normal file
View file

@ -0,0 +1,662 @@
<Project
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns="http://usefulinc.com/ns/doap#"
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:admin="http://webns.net/mvcb/">
<name>GStreamer Python Bindings</name>
<shortname>gst-python</shortname>
<homepage rdf:resource="http://gstreamer.freedesktop.org/modules/gst-python.html" />
<created>1999-10-31</created>
<shortdesc xml:lang="en">
Python bindings for GStreamer
</shortdesc>
<description xml:lang="en">
GStreamer Python Bindings is a set of overrides and Gst fundamental types handling for the dynamically generated PyGObject bindings.
</description>
<category></category>
<bug-database rdf:resource="https://gitlab.freedesktop.org/gstreamer/gst-python/issues/" />
<screenshots></screenshots>
<mailing-list rdf:resource="http://lists.sourceforge.net/lists/listinfo/gstreamer-devel/" />
<programming-language>Python</programming-language>
<license rdf:resource="http://usefulinc.com/doap/licenses/lgpl" />
<download-page rdf:resource="http://gstreamer.freedesktop.org/download/" />
<repository>
<GitRepository>
<location rdf:resource="https://gitlab.freedesktop.org/gstreamer/gst-python"/>
<browse rdf:resource="http://gitlab.freedesktop.org/gstreamer/gst-python"/>
</GitRepository>
</repository>
<release>
<Version>
<revision>1.19.2</revision>
<branch>master</branch>
<name></name>
<created>2021-09-23</created>
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-python/gst-python-1.19.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.19.1</revision>
<branch>master</branch>
<name></name>
<created>2021-06-01</created>
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-python/gst-python-1.19.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.18.0</revision>
<branch>master</branch>
<name></name>
<created>2020-09-08</created>
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-python/gst-python-1.18.0.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.17.90</revision>
<branch>master</branch>
<name></name>
<created>2020-08-20</created>
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-python/gst-python-1.17.90.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.17.2</revision>
<branch>master</branch>
<name></name>
<created>2020-07-03</created>
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-python/gst-python-1.17.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.17.1</revision>
<branch>master</branch>
<name></name>
<created>2020-06-19</created>
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-python/gst-python-1.17.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.16.0</revision>
<branch>master</branch>
<name></name>
<created>2019-04-19</created>
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-python/gst-python-1.16.0.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.15.90</revision>
<branch>master</branch>
<name></name>
<created>2019-04-11</created>
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-python/gst-python-1.15.90.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.15.2</revision>
<branch>master</branch>
<name></name>
<created>2019-02-26</created>
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-python/gst-python-1.15.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.15.1</revision>
<branch>master</branch>
<name></name>
<created>2019-01-17</created>
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-python/gst-python-1.15.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.14.0</revision>
<branch>master</branch>
<name></name>
<created>2018-03-19</created>
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-python/gst-python-1.14.0.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.13.91</revision>
<branch>master</branch>
<name></name>
<created>2018-03-13</created>
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-python/gst-python-1.13.91.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.13.90</revision>
<branch>master</branch>
<name></name>
<created>2018-03-03</created>
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-python/gst-python-1.13.90.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.13.1</revision>
<branch>master</branch>
<name></name>
<created>2018-02-15</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.13.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.12.4</revision>
<branch>1.12</branch>
<name></name>
<created>2017-12-07</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.12.4.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.12.3</revision>
<branch>1.12</branch>
<name></name>
<created>2017-09-18</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.12.3.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.12.2</revision>
<branch>1.12</branch>
<name></name>
<created>2017-07-14</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.12.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.12.1</revision>
<branch>1.12</branch>
<name></name>
<created>2017-06-20</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.12.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.12.0</revision>
<branch>master</branch>
<name></name>
<created>2017-05-04</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.12.0.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.11.91</revision>
<branch>master</branch>
<name></name>
<created>2017-04-27</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.11.91.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.11.90</revision>
<branch>master</branch>
<name></name>
<created>2017-04-07</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.11.90.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.11.2</revision>
<branch>master</branch>
<name></name>
<created>2017-02-24</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.11.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.11.1</revision>
<branch>master</branch>
<name></name>
<created>2017-01-12</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.11.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.10.0</revision>
<branch>master</branch>
<created>2016-11-01</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.10.0.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.9.90</revision>
<branch>master</branch>
<created>2016-09-30</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.9.90.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.9.2</revision>
<branch>master</branch>
<created>2016-09-01</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.9.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.9.1</revision>
<branch>master</branch>
<created>2016-06-06</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.9.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.8.0</revision>
<branch>master</branch>
<created>2016-03-24</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.8.0.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.7.91</revision>
<branch>master</branch>
<created>2016-03-15</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.7.91.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.7.90</revision>
<branch>master</branch>
<created>2016-03-01</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.7.90.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.7.2</revision>
<branch>master</branch>
<created>2016-02-19</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.7.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.7.1</revision>
<branch>master</branch>
<created>2015-12-24</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.7.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.6.2</revision>
<branch>1.6</branch>
<created>2015-12-14</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.6.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.6.1</revision>
<branch>1.6</branch>
<created>2015-10-30</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.6.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.6.0</revision>
<branch>1.6</branch>
<created>2015-09-25</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.6.0.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.5.2</revision>
<branch>1.5</branch>
<created>2015-06-24</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.5.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.4.0</revision>
<branch>1.4.0</branch>
<created>2014-10-20</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.4.0.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.3.90</revision>
<branch>1.3.90</branch>
<created>2014-09-24</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.3.90.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.2.0</revision>
<branch>1.2.0</branch>
<created>2014-03-15</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.2.0.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.1.90</revision>
<branch>1.1</branch>
<created>2013-09-25</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.1.90.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.1.90.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>1.1.90</revision>
<branch>1.1</branch>
<created>2013-09-25</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.1.90.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-1.1.90.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.22</revision>
<branch>0.10</branch>
<name>Ninety Tons of Thunder</name>
<created>2011-10-29</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.22.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.22.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.21</revision>
<branch>0.10</branch>
<name>he used to be an ironhorse, twenty years ago</name>
<created>2011-01-20</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.21.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.21.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.19</revision>
<branch>0.10</branch>
<name>Insert Casablanca quote here</name>
<created>2010-07-15</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.19.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.19.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.18</revision>
<branch>0.10</branch>
<name>A pigeon carrying a 500ton block</name>
<created>2010-02-11</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.18.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.18.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.17</revision>
<branch>0.10</branch>
<name>Shiny new button</name>
<created>2009-10-05</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.17.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.17.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.16</revision>
<branch>0.10</branch>
<name>Distorted memory</name>
<created>2009-08-04</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.16.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.16.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.15</revision>
<branch>0.10</branch>
<name>We built a wall</name>
<created>2009-05-10</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.15.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.15.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.14</revision>
<branch>0.10</branch>
<name>You Better Think</name>
<created>2009-01-19</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.14.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.14.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.13</revision>
<branch>0.10</branch>
<name>Feel The Sun Rise</name>
<created>2008-10-02</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.13.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.13.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.12</revision>
<branch>0.10</branch>
<name>A Wild Finish</name>
<created>2008-06-18</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.12.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.12.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.11</revision>
<branch>0.10</branch>
<name>What I got</name>
<created>2008-03-21</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.11.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.11.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.10</revision>
<branch>0.10</branch>
<name>Destination Overtime</name>
<created>2008-01-28</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.10.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.10.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.9</revision>
<branch>0.10</branch>
<name>I've heard a lot of stories in my time</name>
<created>2007-11-28</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.9.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.9.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.6</revision>
<branch>0.10</branch>
<name>You're not very subtle, but you are effective</name>
<created>2006-12-04</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.6.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.6.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.5</revision>
<branch>0.10</branch>
<name>My Little Poney wants some Funk</name>
<created>2006-07-20</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.5.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.5.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.4</revision>
<branch>0.10</branch>
<name>Alegre</name>
<created>2006-04-28</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.4.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.10.4.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.3</revision>
<branch>0.10</branch>
<name>Maybe not today. Maybe not tomorrow, but soon...</name>
<created>2006-03-21</created>
</Version>
</release>
<release>
<Version>
<revision>0.10.2</revision>
<branch>0.10</branch>
<name>And if the devil is six</name>
<created>2006-01-16</created>
</Version>
</release>
<release>
<Version>
<revision>0.10.1</revision>
<branch>0.10</branch>
<name>Krisimas Yakanaka</name>
<created>2005-12-23</created>
</Version>
</release>
<release>
<Version>
<revision>0.10.0</revision>
<branch>0.10</branch>
<name>Reblochon</name>
<created>2005-12-05</created>
</Version>
</release>
<release>
<Version>
<revision>0.8.4</revision>
<branch>0.8</branch>
<name>64 bit is enough for everyone</name>
<created>2006-03-08</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.8.4.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-python/gst-python-0.8.4.tar.gz" />
</Version>
</release>
<maintainer>
<foaf:Person>
<foaf:name>Wim Taymans</foaf:name>
<foaf:mbox_sha1sum>0d93fde052812d51a05fd86de9bdbf674423daa2</foaf:mbox_sha1sum>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Edward Hervey</foaf:name>
</foaf:Person>
</maintainer>
</Project>

83
hooks/pre-commit.hook Executable file
View file

@ -0,0 +1,83 @@
#!/bin/sh
#
# Check that the code follows a consistant code style
#
# Check for existence of indent, and error out if not present.
# On some *bsd systems the binary seems to be called gnunindent,
# so check for that first.
version=`gnuindent --version 2>/dev/null`
if test "x$version" = "x"; then
version=`gindent --version 2>/dev/null`
if test "x$version" = "x"; then
version=`indent --version 2>/dev/null`
if test "x$version" = "x"; then
echo "GStreamer git pre-commit hook:"
echo "Did not find GNU indent, please install it before continuing."
exit 1
else
INDENT=indent
fi
else
INDENT=gindent
fi
else
INDENT=gnuindent
fi
case `$INDENT --version` in
GNU*)
;;
default)
echo "GStreamer git pre-commit hook:"
echo "Did not find GNU indent, please install it before continuing."
echo "(Found $INDENT, but it doesn't seem to be GNU indent)"
exit 1
;;
esac
INDENT_PARAMETERS="--braces-on-if-line \
--case-brace-indentation0 \
--case-indentation2 \
--braces-after-struct-decl-line \
--line-length80 \
--no-tabs \
--cuddle-else \
--dont-line-up-parentheses \
--continuation-indentation4 \
--honour-newlines \
--tab-size8 \
--indent-level2 \
--leave-preprocessor-space"
echo "--Checking style--"
for file in `git diff-index --cached --name-only HEAD --diff-filter=ACMR| grep "\.c$"` ; do
# nf is the temporary checkout. This makes sure we check against the
# revision in the index (and not the checked out version).
nf=`git checkout-index --temp ${file} | cut -f 1`
newfile=`mktemp /tmp/${nf}.XXXXXX` || exit 1
$INDENT ${INDENT_PARAMETERS} \
$nf -o $newfile 2>> /dev/null
# FIXME: Call indent twice as it tends to do line-breaks
# different for every second call.
$INDENT ${INDENT_PARAMETERS} \
$newfile 2>> /dev/null
diff -u -p "${nf}" "${newfile}"
r=$?
rm "${newfile}"
rm "${nf}"
if [ $r != 0 ] ; then
echo "================================================================================================="
echo " Code style error in: $file "
echo " "
echo " Please fix before committing. Don't forget to run git add before trying to commit again. "
echo " If the whole file is to be committed, this should work (run from the top-level directory): "
echo " "
echo " gst-indent $file; git add $file; git commit"
echo " "
echo "================================================================================================="
exit 1
fi
done
echo "--Checking style pass--"

103
meson.build Normal file
View file

@ -0,0 +1,103 @@
project('gst-python', 'c', 'cpp',
version : '1.19.2',
meson_version : '>= 0.54',
default_options : [ 'warning_level=1',
'c_std=gnu99',
'buildtype=debugoptimized' ])
gst_version = meson.project_version()
version_arr = gst_version.split('.')
gst_version_major = version_arr[0]
gst_version_minor = version_arr[1]
api_version = '@0@.0'.format(gst_version_major)
add_project_arguments('-DHAVE_CONFIG_H', language: 'c')
gst_req = '>= @0@.@1@.0'.format(gst_version_major, gst_version_minor)
gst_dep = dependency('gstreamer-1.0', version : gst_req,
fallback : ['gstreamer', 'gst_dep'])
gstbase_dep = dependency('gstreamer-base-1.0', version : gst_req,
fallback : ['gstreamer', 'gst_base_dep'])
gmodule_dep = dependency('gmodule-2.0')
pygobject_dep = dependency('pygobject-3.0', fallback: ['pygobject', 'pygobject_dep'], version : '>= 3.8')
pymod = import('python')
python = pymod.find_installation(get_option('python'))
pythonver = python.language_version()
if pythonver.version_compare('<3.0')
error('Python2 is not supported anymore, please port your code to python3 (@0@ specified)'.format(python.language_version()))
endif
python_dep = python.dependency(embed:true, required : true)
python_abi_flags = python.get_variable('ABIFLAGS', '')
pylib_loc = get_option('libpython-dir')
if pylib_loc == ''
check_path_exists = 'import os, sys; assert(os.path.exists(sys.argv[1]))'
pylib_loc = python.get_variable('LIBPL', '')
if host_machine.system() != 'windows' and host_machine.system() != 'darwin'
pylib_ldlibrary = python.get_variable('LDLIBRARY', '')
if run_command(python, '-c', check_path_exists, join_paths(pylib_loc, pylib_ldlibrary)).returncode() != 0
# Workaround for Fedora
pylib_loc = python.get_variable('LIBDIR', '')
message('pylib_loc = @0@'.format(pylib_loc))
endif
assert(
run_command(python, '-c', check_path_exists, join_paths(pylib_loc, pylib_ldlibrary)).returncode() == 0,
'Python dynamic library path could not be determined'
)
endif
endif
message('python_abi_flags = @0@'.format(python_abi_flags))
message('pylib_loc = @0@'.format(pylib_loc))
pygi_override_dir = get_option('pygi-overrides-dir')
if pygi_override_dir == ''
pygi_override_dir = python.get_install_dir(
subdir : join_paths('gi', 'overrides')
)
endif
message('pygobject overrides directory = @0@'.format(pygi_override_dir))
# libdir has to be built from pieces.
libdir = get_option('prefix')+'/'+get_option('libdir')
pylib_suffix = 'so'
if host_machine.system() == 'windows'
pylib_suffix = 'dll'
elif host_machine.system() == 'darwin'
pylib_suffix = 'dylib'
endif
cdata = configuration_data()
cdata.set('PACKAGE', '"gst-python"')
cdata.set('VERSION', '"@0@"'.format(gst_version))
cdata.set('GST_PACKAGE_NAME', '"GStreamer Python"')
cdata.set('PACKAGE_NAME', '"GStreamer Python"')
cdata.set('GST_API_VERSION', '"@0@"'.format(api_version))
cdata.set('PLUGINDIR', '"@0@/gstreamer-1.0"'.format(libdir))
cdata.set('PY_LIB_LOC', '"@0@"'.format(pylib_loc))
cdata.set('PY_ABI_FLAGS', '"@0@"'.format(python_abi_flags))
cdata.set('PY_LIB_SUFFIX', '"@0@"'.format(pylib_suffix))
cdata.set('PYTHON_VERSION', '"@0@"'.format(python_dep.version()))
configure_file(output : 'config.h', configuration : cdata)
configinc = include_directories('.')
pkgconfig = import('pkgconfig')
plugins_install_dir = join_paths(libdir, 'gstreamer-1.0')
plugins_pkgconfig_install_dir = join_paths(plugins_install_dir, 'pkgconfig')
if get_option('default_library') == 'shared'
# If we don't build static plugins there is no need to generate pc files
plugins_pkgconfig_install_dir = disabler()
endif
subdir('gi')
subdir('plugin')
subdir('testsuite')
run_command(python, '-c', 'import shutil; shutil.copy("hooks/pre-commit.hook", ".git/hooks/pre-commit")')

5
meson_options.txt Normal file
View file

@ -0,0 +1,5 @@
option('pygi-overrides-dir', type : 'string', value : '',
description: 'Path to pygobject overrides directory')
option('libpython-dir', type : 'string', value : '',
description: 'Path to find libpythonXX.so')
option('python', type : 'string', value : 'python3')

View file

@ -0,0 +1,40 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# audio-controller.py
# (c) 2005 Edward Hervey <edward at fluendo dot com>
# Test case for the GstController on sinesrc -> alsasink
# Inspired from ensonic's examples/controller/audio-controller.c
import pygst
pygst.require('0.10')
import gst
import time
def main():
pipeline = gst.Pipeline("audiocontroller")
src = gst.element_factory_make("audiotestsrc", "src")
sink = gst.element_factory_make("alsasink", "sink")
pipeline.add(src, sink)
src.link(sink)
control = gst.Controller(src, "freq", "volume")
control.set_interpolation_mode("volume", gst.INTERPOLATE_LINEAR)
control.set_interpolation_mode("freq", gst.INTERPOLATE_LINEAR)
control.set("volume", 0, 0.0)
control.set("volume", 2 * gst.SECOND, 1.0)
control.set("volume", 4 * gst.SECOND, 0.0)
control.set("volume", 6 * gst.SECOND, 1.0)
control.set("freq", 0, 440.0)
control.set("freq", 3 * gst.SECOND, 3000.0)
control.set("freq", 6 * gst.SECOND, 880.0)
pipeline.set_state(gst.STATE_PLAYING)
time.sleep(7)
if __name__ == "__main__":
main()

192
old_examples/audioconcat.py Normal file
View file

@ -0,0 +1,192 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# audioconcat.py - Concatenates multiple audio files to single ogg/vorbis file
# Uses the gnonlin elements (http://gnonlin.sf.net/)
# Copyright (C) 2005 Edward Hervey <edward@fluendo.com>
# 2006 Jason Gerard DeRose <jderose@jasonderose.org>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
from gst.extend.discoverer import Discoverer
class AudioDec(gst.Bin):
'''Decodes audio file, outputs at specified caps'''
def __init__(self, location, caps):
gst.Bin.__init__(self)
# Create elements
src = gst.element_factory_make('filesrc')
dec = gst.element_factory_make('decodebin')
conv = gst.element_factory_make('audioconvert')
rsmpl = gst.element_factory_make('audioresample')
ident = gst.element_factory_make('identity')
# Set 'location' property on filesrc
src.set_property('location', location)
# Connect handler for 'new-decoded-pad' signal
dec.connect('new-decoded-pad', self.__on_new_decoded_pad)
# Add elements to bin
self.add(src, dec, conv, rsmpl, ident)
# Link *some* elements
# This is completed in self.__on_new_decoded_pad()
src.link(dec)
conv.link(rsmpl)
rsmpl.link(ident, caps)
# Reference used in self.__on_new_decoded_pad()
self.__apad = conv.get_pad('sink')
# Add ghost pad
self.add_pad(gst.GhostPad('src', ident.get_pad('src')))
def __on_new_decoded_pad(self, element, pad, last):
caps = pad.get_caps()
name = caps[0].get_name()
print '\n__on_new_decoded_pad:', name
if 'audio' in name:
if not self.__apad.is_linked(): # Only link once
pad.link(self.__apad)
class AudioConcat:
'''Concatenates multiple audio files to single ogg/vorbis file'''
caps = gst.caps_from_string('audio/x-raw-float, rate=44100, channels=2, endianness=1234, width=32')
def __init__(self, infiles, outfile):
# These are used in iteration through infiles
self.infiles = infiles
self.i = 0
self.start = 0L
# The pipeline
self.pipeline = gst.Pipeline()
# Create bus and connect 'eos' and 'error' handlers
self.bus = self.pipeline.get_bus()
self.bus.add_signal_watch()
self.bus.connect('message::eos', self.on_eos)
self.bus.connect('message::error', self.on_error)
# Create elements
self.comp = gst.element_factory_make('gnlcomposition')
self.enc = gst.element_factory_make('vorbisenc')
self.mux = gst.element_factory_make('oggmux')
self.sink = gst.element_factory_make('filesink')
# Connect handler for 'pad-added' signal
self.comp.connect('pad-added', self.on_pad_added)
# Set 'location' property on filesink
self.sink.set_property('location', outfile)
# Add elements to pipeline
self.pipeline.add(self.comp, self.enc, self.mux, self.sink)
# Link *some* elements
# This in completed in self.on_pad_added()
gst.element_link_many(self.enc, self.mux, self.sink)
# Reference used in self.on_pad_added()
self.apad = self.enc.get_pad('sink')
# The MainLoop
self.mainloop = gobject.MainLoop()
# Iterate through infiles
gobject.idle_add(self.discover)
self.mainloop.run()
def discover(self):
infile = self.infiles[self.i]
discoverer = Discoverer(infile)
discoverer.connect('discovered', self.on_discovered, infile)
discoverer.discover()
return False # Don't repeat idle call
def on_discovered(self, discoverer, ismedia, infile):
print '\non_discovered:', infile
discoverer.print_info()
if discoverer.is_audio:
dec = AudioDec(infile, self.caps)
src = gst.element_factory_make('gnlsource')
src.add(dec)
src.set_property('media-start', 0L)
src.set_property('media-duration', discoverer.audiolength)
src.set_property('start', self.start)
src.set_property('duration', discoverer.audiolength)
self.comp.add(src)
self.start += discoverer.audiolength
self.i += 1
if self.i < len(self.infiles):
gobject.idle_add(self.discover)
else:
if self.start > 0: # At least 1 infile is_audio and audiolength > 0
self.pipeline.set_state(gst.STATE_PLAYING)
else:
self.mainloop.quit()
def on_pad_added(self, element, pad):
caps = pad.get_caps()
name = caps[0].get_name()
print '\non_pad_added:', name
if name == 'audio/x-raw-float':
if not self.apad.is_linked(): # Only link once
pad.link(self.apad)
def on_eos(self, bus, msg):
print '\non_eos'
self.mainloop.quit()
def on_error(self, bus, msg):
error = msg.parse_error()
print '\non_error:', error[1]
self.mainloop.quit()
if __name__ == '__main__':
if len(sys.argv) >= 3:
AudioConcat(sys.argv[1:-1], sys.argv[-1])
else:
print 'Usage: %s <input_file(s)> <output_file>' % sys.argv[0]
print 'Example: %s song1.mp3 song2.ogg output.ogg' % sys.argv[0]

120
old_examples/bps.py Executable file
View file

@ -0,0 +1,120 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2003 David I. Lehn
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
# Author: David I. Lehn <dlehn@users.sourceforge.net>
#
import pygtk
pygtk.require('2.0')
import sys
import time
import gobject
import gtk
import pygst
pygst.require('0.10')
import gst
class BPS(object):
def __init__(self):
self.buffers = 0
self.start = 0
def done(self):
end = time.time()
dt = end - self.start
bps = self.buffers/dt
spb = dt/self.buffers
print '\t%d buffers / %fs\t= %f bps\t= %f spb' % (self.buffers, dt, bps, spb)
def fakesrc(self, buffers):
src = gst.element_factory_make('fakesrc','src')
src.set_property('silent', 1)
src.set_property('num_buffers', buffers)
return src
def fakesink(self):
sink = gst.element_factory_make('fakesink','sink')
sink.set_property('silent', 1)
return sink
def build_pipeline(self, buffers):
pipeline = gst.Pipeline('pipeline')
src = self.fakesrc(buffers)
pipeline.add(src)
sink = self.fakesink()
pipeline.add(sink)
src.link(sink)
return pipeline
def idle(self, pipeline):
return pipeline.iterate()
def test(self):
self.bus = self.pipeline.get_bus()
self.start = time.time()
self.pipeline.set_state(gst.STATE_PLAYING)
while 1:
msg = self.bus.poll(gst.MESSAGE_EOS | gst.MESSAGE_ERROR, gst.SECOND)
if msg:
break
self.pipeline.set_state(gst.STATE_NULL)
self.done()
def run(self, buffers):
self.buffers = buffers
print '# Testing buffer processing rate for "fakesrc ! fakesink"'
print '# bps = buffers per second'
print '# spb = seconds per buffer'
self.pipeline = self.build_pipeline(buffers)
assert self.pipeline
self.test()
def main(args):
"GStreamer Buffers-Per-Second tester"
if len(args) < 2:
print 'usage: %s buffers' % args[0]
return 1
bps = BPS()
buffers = int(args[1])
if buffers < 1:
print 'buffers must be higher than 0'
return
bps.run(buffers)
if __name__ == '__main__':
sys.exit(main(sys.argv))

138
old_examples/buffer-draw.py Normal file
View file

@ -0,0 +1,138 @@
#!/usr/bin/env python
import sys
import traceback
from math import pi
import pygtk
pygtk.require ("2.0")
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
import cairo
WIDTH, HEIGHT = 640, 480
FRAMES = 300
FRAMERATE = 15
class PyGstBufferDraw(gst.Element):
_sinkpadtemplate = gst.PadTemplate ("sink",
gst.PAD_SINK,
gst.PAD_ALWAYS,
gst.caps_from_string ("video/x-raw-rgb,bpp=32,depth=32,blue_mask=-16777216,green_mask=16711680, red_mask=65280, alpha_mask=255,width=[ 1, 2147483647 ],height=[ 1, 2147483647 ],framerate=[ 0/1, 2147483647/1 ]"))
_srcpadtemplate = gst.PadTemplate ("src",
gst.PAD_SRC,
gst.PAD_ALWAYS,
gst.caps_from_string ("video/x-raw-rgb,bpp=32,depth=32,blue_mask=-16777216,green_mask=16711680, red_mask=65280, alpha_mask=255,width=[ 1, 2147483647 ],height=[ 1, 2147483647 ],framerate=[ 0/1, 2147483647/1 ]"))
def __init__(self):
gst.Element.__init__(self)
self.sinkpad = gst.Pad(self._sinkpadtemplate, "sink")
self.sinkpad.set_chain_function(self.chainfunc)
self.sinkpad.set_event_function(self.eventfunc)
self.sinkpad.set_getcaps_function(gst.Pad.proxy_getcaps)
self.sinkpad.set_setcaps_function(gst.Pad.proxy_setcaps)
self.add_pad (self.sinkpad)
self.srcpad = gst.Pad(self._srcpadtemplate, "src")
self.srcpad.set_event_function(self.srceventfunc)
self.srcpad.set_query_function(self.srcqueryfunc)
self.srcpad.set_getcaps_function(gst.Pad.proxy_getcaps)
self.srcpad.set_setcaps_function(gst.Pad.proxy_setcaps)
self.add_pad (self.srcpad)
def chainfunc(self, pad, buffer):
try:
outbuf = buffer.copy_on_write ()
self.draw_on (outbuf)
return self.srcpad.push (outbuf)
except:
return GST_FLOW_ERROR
def eventfunc(self, pad, event):
return self.srcpad.push_event (event)
def srcqueryfunc (self, pad, query):
return self.sinkpad.query (query)
def srceventfunc (self, pad, event):
return self.sinkpad.push_event (event)
def draw_on (self, buf):
try:
caps = buf.get_caps()
width = caps[0]['width']
height = caps[0]['height']
framerate = caps[0]['framerate']
surface = cairo.ImageSurface.create_for_data (buf, cairo.FORMAT_ARGB32, width, height, 4 * width)
ctx = cairo.Context(surface)
except:
print "Failed to create cairo surface for buffer"
traceback.print_exc()
return
try:
center_x = width/4
center_y = 3*height/4
# draw a circle
radius = float (min (width, height)) * 0.25
ctx.set_source_rgba (0.0, 0.0, 0.0, 0.9)
ctx.move_to (center_x, center_y)
ctx.arc (center_x, center_y, radius, 0, 2.0*pi)
ctx.close_path()
ctx.fill()
ctx.set_source_rgba (1.0, 1.0, 1.0, 1.0)
ctx.set_font_size(0.3 * radius)
txt = "Hello World"
extents = ctx.text_extents (txt)
ctx.move_to(center_x - extents[2]/2, center_y + extents[3]/2)
ctx.text_path(txt)
ctx.fill()
except:
print "Failed cairo render"
traceback.print_exc()
gobject.type_register(PyGstBufferDraw)
pipe = gst.Pipeline()
vt = gst.element_factory_make ("videotestsrc")
cf = gst.element_factory_make ("capsfilter")
c1 = PyGstBufferDraw()
color = gst.element_factory_make ("ffmpegcolorspace")
scale = gst.element_factory_make ("videoscale")
q1 = gst.element_factory_make ("queue")
sink = gst.element_factory_make ("autovideosink")
caps = gst.caps_from_string ("video/x-raw-rgb,width=%d,height=%d,framerate=%d/1" % (WIDTH, HEIGHT, FRAMERATE))
cf.set_property ("caps", caps)
vt.set_property ("num-buffers", FRAMES)
pipe.add (vt, cf, c1, q1, color, scale, sink)
gst.element_link_many (vt, cf, c1, q1, color, scale, sink)
def on_eos (bus, msg):
mainloop.quit()
bus = pipe.get_bus()
bus.add_signal_watch()
bus.connect('message::eos', on_eos)
pipe.set_state (gst.STATE_PLAYING)
mainloop = gobject.MainLoop()
try:
mainloop.run()
except:
pass
pipe.set_state (gst.STATE_NULL)
pipe.get_state (gst.CLOCK_TIME_NONE)

83
old_examples/cp.py Executable file
View file

@ -0,0 +1,83 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2002 David I. Lehn <dlehn@users.sourceforge.net>
# 2004 Johan Dahlin <johan@gnome.org>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
# Author: David I. Lehn <dlehn@users.sourceforge.net>
#
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
mainloop = gobject.MainLoop()
def on_eos(bus, msg):
mainloop.quit()
def filter(input, output):
"A GStreamer copy pipeline which can add arbitrary filters"
# create a new bin to hold the elements
bin = gst.parse_launch('filesrc name=source ! ' +
'progressreport ! ' +
# This 'statistics' element is depreciated in 0.10
#'statistics silent=false buffer-update-freq=1 ' +
#'update_on_eos=true ! ' +
'filesink name=sink')
filesrc = bin.get_by_name('source')
filesrc.set_property('location', input)
filesink = bin.get_by_name('sink')
filesink.set_property('location', output)
bus = bin.get_bus()
bus.add_signal_watch()
bus.connect('message::eos', on_eos)
# start playing
bin.set_state(gst.STATE_PLAYING)
try:
mainloop.run()
except KeyboardInterrupt:
pass
# stop the bin
bin.set_state(gst.STATE_NULL)
def main(args):
"A GStreamer based cp(1) with stats"
if len(args) != 3:
print 'usage: %s source dest' % (sys.argv[0])
return -1
return filter(args[1], args[2])
if __name__ == '__main__':
sys.exit(main(sys.argv))

80
old_examples/cutter.py Normal file
View file

@ -0,0 +1,80 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2005 Thomas Vander Stichele
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
import gst
import time
import gobject
#gobject.threads_init() # so we can safely receive signals from threads
count = 0
def on_message_application(cutter, message, loop):
global count
s = message.structure
which = 'below'
if s['above']: which = 'above'
print "%s: %s threshold" % (gst.TIME_ARGS(s['timestamp']), which)
if s['above']: count += 1
if count > 2: loop.quit()
def main():
type = 'async'
loop = gobject.MainLoop()
pipeline = gst.Pipeline("cutter")
src = gst.element_factory_make("sinesrc", "src")
cutter = gst.element_factory_make("cutter")
cutter.set_property('threshold', 0.5)
sink = gst.element_factory_make("fakesink", "sink")
pipeline.add(src, cutter, sink)
src.link(cutter)
cutter.link(sink)
control = gst.Controller(src, "volume")
control.set_interpolation_mode("volume", gst.INTERPOLATE_LINEAR)
control.set("volume", 0, 0.0)
control.set("volume", 2 * gst.SECOND, 1.0)
control.set("volume", 4 * gst.SECOND, 0.0)
control.set("volume", 6 * gst.SECOND, 1.0)
control.set("volume", 8 * gst.SECOND, 0.0)
control.set("volume", 10 * gst.SECOND, 1.0)
bus = pipeline.get_bus()
if type == 'async':
bus.add_signal_watch()
bus.connect('message::element', on_message_application, loop)
else:
# FIXME: needs wrapping in gst-python
bus.set_sync_handler(bus.sync_signal_handler)
bus.connect('sync-message::element', on_message_application, loop)
pipeline.set_state(gst.STATE_PLAYING)
loop.run()
pipeline.set_state(gst.STATE_NULL)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,59 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2005 Fluendo S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
# Author: Andy Wingo <wingo@pobox.com>
import gtk
from gtk import gdk
import gobject
import pygst
pygst.require('0.10')
import gst
class DebugSlider(gtk.HScale):
def __init__(self):
adj = gtk.Adjustment(int(gst.debug_get_default_threshold()),
0, 5, 1, 0, 0)
gtk.HScale.__init__(self, adj)
self.set_digits(0)
self.set_draw_value(True)
self.set_value_pos(gtk.POS_TOP)
def value_changed(self):
newlevel = int(self.get_adjustment().get_value())
gst.debug_set_default_threshold(newlevel)
self.connect('value-changed', value_changed)
if __name__ == '__main__':
p = gst.parse_launch('fakesrc ! fakesink')
p.set_state(gst.STATE_PLAYING)
w = gtk.Window()
s = DebugSlider()
w.add(s)
s.show()
w.set_default_size(200, 40)
w.show()
w.connect('delete-event', lambda *args: gtk.main_quit())
gtk.main()

109
old_examples/decodebin.py Normal file
View file

@ -0,0 +1,109 @@
#!/usr/bin/env python
# decodebin.py - Audio autopluging example using 'decodebin' element
# Copyright (C) 2006 Jason Gerard DeRose <jderose@jasonderose.org>
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
class Decodebin:
def __init__(self, location):
# The pipeline
self.pipeline = gst.Pipeline()
# Create bus and connect several handlers
self.bus = self.pipeline.get_bus()
self.bus.add_signal_watch()
self.bus.connect('message::eos', self.on_eos)
self.bus.connect('message::tag', self.on_tag)
self.bus.connect('message::error', self.on_error)
# Create elements
self.src = gst.element_factory_make('filesrc')
self.dec = gst.element_factory_make('decodebin')
self.conv = gst.element_factory_make('audioconvert')
self.rsmpl = gst.element_factory_make('audioresample')
self.sink = gst.element_factory_make('alsasink')
# Set 'location' property on filesrc
self.src.set_property('location', location)
# Connect handler for 'new-decoded-pad' signal
self.dec.connect('new-decoded-pad', self.on_new_decoded_pad)
# Add elements to pipeline
self.pipeline.add(self.src, self.dec, self.conv, self.rsmpl, self.sink)
# Link *some* elements
# This is completed in self.on_new_decoded_pad()
self.src.link(self.dec)
gst.element_link_many(self.conv, self.rsmpl, self.sink)
# Reference used in self.on_new_decoded_pad()
self.apad = self.conv.get_pad('sink')
# The MainLoop
self.mainloop = gobject.MainLoop()
# And off we go!
self.pipeline.set_state(gst.STATE_PLAYING)
self.mainloop.run()
def on_new_decoded_pad(self, element, pad, last):
caps = pad.get_caps()
name = caps[0].get_name()
print 'on_new_decoded_pad:', name
if name == 'audio/x-raw-float' or name == 'audio/x-raw-int':
if not self.apad.is_linked(): # Only link once
pad.link(self.apad)
def on_eos(self, bus, msg):
print 'on_eos'
self.mainloop.quit()
def on_tag(self, bus, msg):
taglist = msg.parse_tag()
print 'on_tag:'
for key in taglist.keys():
print '\t%s = %s' % (key, taglist[key])
def on_error(self, bus, msg):
error = msg.parse_error()
print 'on_error:', error[1]
self.mainloop.quit()
if __name__ == '__main__':
if len(sys.argv) == 2:
Decodebin(sys.argv[1])
else:
print 'Usage: %s /path/to/media/file' % sys.argv[0]

64
old_examples/f2f.py Executable file
View file

@ -0,0 +1,64 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2002 David I. Lehn
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
# Author: David I. Lehn <dlehn@users.sourceforge.net>
#
import sys
import pygst
pygst.require('0.10')
import gst
def handoff_cb(sender, *args):
print sender.get_name(), args
def main(args):
# create a new bin to hold the elements
#gst_debug_set_categories(-1)
bin = gst.parse_launch('fakesrc name=source silent=1 num-buffers=10 signal-handoffs=true ! ' +
'fakesink name=sink silent=1 signal-handoffs=true')
source = bin.get_by_name('source')
source.connect('handoff', handoff_cb)
source.get_pad("src").connect("have-data", handoff_cb)
sink = bin.get_by_name('sink')
sink.connect('handoff', handoff_cb)
sink.get_pad("sink").connect('have-data', handoff_cb)
print source, sink
bus = bin.get_bus()
res = bin.set_state(gst.STATE_PLAYING);
assert res
while 1:
msg = bus.poll(gst.MESSAGE_EOS | gst.MESSAGE_ERROR, gst.SECOND)
if msg:
break
res = bin.set_state(gst.STATE_NULL)
assert res
if __name__ == '__main__':
sys.exit(main(sys.argv))

99
old_examples/filesrc.py Executable file
View file

@ -0,0 +1,99 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# GStreamer python bindings
# Copyright (C) 2002 David I. Lehn <dlehn@users.sourceforge.net>
# 2004 Johan Dahlin <johan@gnome.org>
#
# filesrc.py: implements a file source element completely in python
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
import sys
import gobject; gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
class FileSource(gst.BaseSrc):
__gsttemplates__ = (
gst.PadTemplate("src",
gst.PAD_SRC,
gst.PAD_ALWAYS,
gst.caps_new_any()),
)
blocksize = 4096
fd = None
def __init__(self, name):
self.__gobject_init__()
self.curoffset = 0
self.set_name(name)
def set_property(self, name, value):
if name == 'location':
self.fd = open(value, 'r')
def do_create(self, offset, size):
if offset != self.curoffset:
self.fd.seek(offset, 0)
data = self.fd.read(self.blocksize)
if data:
self.curoffset += len(data)
return gst.FLOW_OK, gst.Buffer(data)
else:
return gst.FLOW_UNEXPECTED, None
gobject.type_register(FileSource)
def main(args):
if len(args) != 3:
print 'Usage: %s input output' % (args[0])
return -1
bin = gst.Pipeline('pipeline')
filesrc = FileSource('filesource')
assert filesrc
filesrc.set_property('location', args[1])
filesink = gst.element_factory_make('filesink', 'sink')
filesink.set_property('location', args[2])
bin.add(filesrc, filesink)
gst.element_link_many(filesrc, filesink)
bin.set_state(gst.STATE_PLAYING);
mainloop = gobject.MainLoop()
def bus_event(bus, message):
t = message.type
if t == gst.MESSAGE_EOS:
mainloop.quit()
elif t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
mainloop.quit()
return True
bin.get_bus().add_watch(bus_event)
mainloop.run()
bin.set_state(gst.STATE_NULL)
if __name__ == '__main__':
sys.exit(main(sys.argv))

239
old_examples/fvumeter.py Normal file
View file

@ -0,0 +1,239 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2005 Fluendo S.L.
# Originally from the Flumotion streaming server.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
# Author: Zaheer Merali <zaheermerali at gmail dot com>
import gtk
from gtk import gdk
import gobject
# this VUMeter respects IEC standard
# BS 6840-18:1996/IEC-268-18
# and is inspired by JACK's meterbridge dpm_meters.c
class FVUMeter(gtk.DrawingArea):
__gsignals__ = { 'expose-event' : 'override',
'size-allocate': 'override',
'size-request': 'override',
'realize' : 'override'
}
__gproperties__ = {
'peak' : (gobject.TYPE_FLOAT,
'peak volume level',
'peak volume level in dB',
-90.0,
0,
-90.0,
gobject.PARAM_READWRITE),
'decay' : (gobject.TYPE_FLOAT,
'decay volume level',
'decay volume level in dB',
-90.0,
0,
-90.0,
gobject.PARAM_READWRITE),
'orange-threshold': (gobject.TYPE_FLOAT,
'threshold for orange',
'threshold for orange use in dB',
-90.0,
0,
-10.0,
gobject.PARAM_READWRITE),
'red-threshold': (gobject.TYPE_FLOAT,
'threshold for red',
'threshold for red use in dB',
-90.0,
0,
-1.0,
gobject.PARAM_READWRITE)
}
green_gc = None
orange_gc = None
red_gc = None
yellow_gc = None
topborder = 7
peaklevel = -90.0
decaylevel = -90.0
orange_threshold = -10.0
red_threshold = -1.0
bottomborder = 25
leftborder = 15
rightborder = 65
# Returns the meter deflection percentage given a db value
def iec_scale(self, db):
pct = 0.0
if db < -70.0:
pct = 0.0
elif db < -60.0:
pct = (db + 70.0) * 0.25
elif db < -50.0:
pct = (db + 60.0) * 0.5 + 2.5
elif db < -40.0:
pct = (db + 50.0) * 0.75 + 7.5
elif db < -30.0:
pct = (db + 40.0) * 1.5 + 15.0
elif db < -20.0:
pct = (db + 30.0) * 2.0 + 30.0
elif db < 0.0:
pct = (db + 20.0) * 2.5 + 50.0
else:
pct = 100.0
return pct
def do_get_property(self, property):
if property.name == 'peak':
return self.peaklevel
elif property.name == 'decay':
return self.decaylevel
elif property.name == 'orange-threshold':
return self.orange_threshold
elif property.name == 'red-threshold':
return self.red_threshold
else:
raise AttributeError, 'unknown property %s' % property.name
def do_set_property(self, property, value):
if property.name == 'peak':
self.peaklevel = value
elif property.name == 'decay':
self.decaylevel = value
elif property.name == 'orange-threshold':
self.orange_threshold = value
elif property.name == 'red-threshold':
self.red_threshold = value
else:
raise AttributeError, 'unknown property %s' % property.name
self.queue_draw()
def do_size_request(self, requisition):
requisition.width = 250
requisition.height = 50
def do_size_allocate(self, allocation):
self.allocation = allocation
if self.flags() & gtk.REALIZED:
self.window.move_resize(*allocation)
def do_realize(self):
self.set_flags(self.flags() | gtk.REALIZED)
self.window = gdk.Window(self.get_parent_window(),
width=self.allocation.width,
height=self.allocation.height,
window_type=gdk.WINDOW_CHILD,
wclass=gdk.INPUT_OUTPUT,
event_mask=self.get_events() | gdk.EXPOSURE_MASK)
colormap = gtk.gdk.colormap_get_system()
green = colormap.alloc_color(0, 65535, 0)
orange = colormap.alloc_color(65535, 32768, 0)
red = colormap.alloc_color(65535, 0, 0)
yellow = colormap.alloc_color(65535, 65535, 0)
self.green_gc = gdk.GC(self.window, foreground=green)
self.orange_gc = gdk.GC(self.window, foreground=orange)
self.red_gc = gdk.GC(self.window, foreground=red)
self.yellow_gc = gdk.GC(self.window, foreground=yellow)
self.window.set_user_data(self)
self.style.attach(self.window)
self.style.set_background(self.window, gtk.STATE_NORMAL)
def do_expose_event(self, event):
self.chain(event)
x, y, w, h = self.allocation
vumeter_width = w - (self.leftborder + self.rightborder)
vumeter_height = h - (self.topborder + self.bottomborder)
self.window.draw_rectangle(self.style.black_gc, True,
self.leftborder, self.topborder,
vumeter_width,
vumeter_height)
# draw peak level
# 0 maps to width of 0, full scale maps to total width
peaklevelpct = self.iec_scale(self.peaklevel)
peakwidth = int(vumeter_width * (peaklevelpct / 100))
draw_gc = self.green_gc
if self.peaklevel >= self.orange_threshold:
draw_gc = self.orange_gc
if self.peaklevel >= self.red_threshold:
draw_gc = self.red_gc
if peakwidth > 0:
self.window.draw_rectangle(draw_gc, True,
self.leftborder, self.topborder,
peakwidth, vumeter_height)
# draw yellow decay level
if self.decaylevel > -90.0:
decaylevelpct = self.iec_scale(self.decaylevel)
decaywidth = int(vumeter_width * (decaylevelpct / 100))
# cheat the geometry by drawing 0% level at pixel 0,
# which is same position as just above 0%
if decaywidth == 0:
decaywidth = 1
self.window.draw_line(self.yellow_gc,
self.leftborder + decaywidth - 1,
self.topborder,
self.leftborder + decaywidth - 1,
self.topborder + vumeter_height - 1)
# draw tick marks
scalers = [
('-90', 0.0),
('-40', 0.15),
('-30', 0.30),
('-20', 0.50),
('-10', 0.75),
( '-5', 0.875),
( '0', 1.0),
]
for level, scale in scalers:
# tick mark, 6 pixels high
# we cheat again here by putting the 0 at the first pixel
self.window.draw_line(self.style.black_gc,
self.leftborder + int(scale * (vumeter_width - 1)),
h - self.bottomborder,
self.leftborder + int(scale * (vumeter_width - 1)),
h - self.bottomborder + 5)
# tick label
layout = self.create_pango_layout(level)
layout_width, layout_height = layout.get_pixel_size()
self.window.draw_layout(self.style.black_gc,
self.leftborder + int(scale * vumeter_width)
- int(layout_width / 2),
h - self.bottomborder + 7, layout)
# draw the peak level to the right
layout = self.create_pango_layout("%.2fdB" % self.peaklevel)
layout_width, layout_height = layout.get_pixel_size()
self.window.draw_layout(self.style.black_gc,
self.leftborder + vumeter_width + 5,
self.topborder + int(vumeter_height / 2 - layout_height / 2),
layout)
gobject.type_register(FVUMeter)

89
old_examples/gst-discover Executable file
View file

@ -0,0 +1,89 @@
#!/usr/bin/env python
# gst-python
# Copyright (C) 2006 Andy Wingo <wingo at pobox.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
import os
import sys
import pygtk
pygtk.require('2.0')
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
from gst.extend import discoverer
def fail(path):
print "error: %r does not appear to be a media file" % path
sys.exit(1)
def succeed(d):
def pp(prop, val):
print '%s: %s' % (prop, val)
pp('media type', d.mimetype)
pp('has video', d.is_video)
if d.is_video:
pp('video caps', d.videocaps)
pp('video width (pixels)', d.videowidth)
pp('video height (pixels)', d.videoheight)
pp('video length (hh:mm:ss)', gst.TIME_ARGS(d.videolength))
pp('framerate (fps)', '%s/%s' % (d.videorate.num, d.videorate.denom))
pp('has audio', d.is_audio)
if d.is_audio:
pp('audio caps', d.audiocaps)
pp('audio format', d.audiofloat and 'floating-point' or 'integer')
pp('sample rate (Hz)', d.audiorate)
pp('sample width (bits)', d.audiowidth)
pp('sample depth (bits)', d.audiodepth)
pp('audio length (hh:mm:ss)', gst.TIME_ARGS(d.audiolength))
pp('audio channels', d.audiochannels)
sys.exit(0)
def discover(path):
def discovered(d, is_media):
if is_media:
succeed(d)
else:
fail(path)
d = discoverer.Discoverer(path)
d.connect('discovered', discovered)
d.discover()
gobject.MainLoop().run()
def usage():
print >>sys.stderr, "usage: gst-discover PATH-TO-MEDIA-FILE"
sys.exit(1)
def main(argv):
if len(argv) != 2:
usage()
path = argv.pop()
if not os.path.isfile(path):
print >>sys.stderr, "error: file %r does not exist" % path
usage()
return discover(path)
if __name__ == '__main__':
sys.exit(main(sys.argv))

73
old_examples/gstfile.py Normal file
View file

@ -0,0 +1,73 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gstfile.py
# (c) 2005 Edward Hervey <edward at fluendo dot com>
# Discovers and prints out multimedia information of files
# This example shows how to use gst-python:
# _ in an object-oriented way (Discoverer class)
# _ subclassing a gst.Pipeline
# _ and overidding existing methods (do_iterate())
import os
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
from gst.extend.discoverer import Discoverer
class GstFile:
"""
Analyses one or more files and prints out the multimedia information of
each file.
"""
def __init__(self, files):
self.files = files
self.mainloop = gobject.MainLoop()
self.current = None
def run(self):
gobject.idle_add(self._discover_one)
self.mainloop.run()
def _discovered(self, discoverer, ismedia):
discoverer.print_info()
self.current = None
if len(self.files):
print "\n"
gobject.idle_add(self._discover_one)
def _discover_one(self):
if not len(self.files):
gobject.idle_add(self.mainloop.quit)
return False
filename = self.files.pop(0)
if not os.path.isfile(filename):
gobject.idle_add(self._discover_one)
return False
print "Running on", filename
# create a discoverer for that file
self.current = Discoverer(filename)
# connect a callback on the 'discovered' signal
self.current.connect('discovered', self._discovered)
# start the discovery
self.current.discover()
return False
def main(args):
if len(args) < 2:
print 'usage: %s files...' % args[0]
return 2
gstfile = GstFile(args[1:])
gstfile.run()
if __name__ == '__main__':
sys.exit(main(sys.argv))

View file

@ -0,0 +1,54 @@
#!/usr/bin/env python
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
def bus_call(bus, message, loop):
t = message.type
if t == gst.MESSAGE_EOS:
sys.stout.write("End-of-stream\n")
loop.quit()
elif t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
sys.stderr.write("Error: %s: %s\n" % err, debug)
loop.quit()
return True
def main(args):
if len(args) != 2:
sys.stderr.write("usage: %s <media file or uri>\n" % args[0])
sys.exit(1)
playbin = gst.element_factory_make("playbin2", None)
if not playbin:
sys.stderr.write("'playbin2' gstreamer plugin missing\n")
sys.exit(1)
# take the commandline argument and ensure that it is a uri
if gst.uri_is_valid(args[1]):
uri = args[1]
else:
uri = gst.filename_to_uri(args[1])
playbin.set_property('uri', uri)
# create and event loop and feed gstreamer bus mesages to it
loop = gobject.MainLoop()
bus = playbin.get_bus()
bus.add_watch(bus_call, loop)
# start play back and listed to events
playbin.set_state(gst.STATE_PLAYING)
loop.run()
# cleanup
playbin.set_state(gst.STATE_NULL)
if __name__ == '__main__':
sys.exit(main(sys.argv))

101
old_examples/maemogst.py Normal file
View file

@ -0,0 +1,101 @@
import gobject
gobject.threads_init()
import gtk
gtk.gdk.threads_init()
import hildon
import gst
import sys
# VideoWidget taken from play.py in gst-python examples
class VideoWidget(gtk.DrawingArea):
def __init__(self):
gtk.DrawingArea.__init__(self)
self.imagesink = None
self.unset_flags(gtk.DOUBLE_BUFFERED)
def do_expose_event(self, event):
if self.imagesink:
self.imagesink.expose()
return False
else:
return True
def set_sink(self, sink):
assert self.window.xid
self.imagesink = sink
self.imagesink.set_xwindow_id(self.window.xid)
class MaemoGstView:
def __init__(self):
# hildon has one program instance per app, so get instance
self.p = hildon.Program.get_instance()
# set name of application: this shows in titlebar
gtk.set_application_name("Maemo GStreamer VideoTest")
# stackable window in case we want more windows in future in app
self.w = hildon.StackableWindow()
box = gtk.VBox()
self.video_widget = VideoWidget()
# video widget we want to expand to size
box.pack_start(self.video_widget, True, True, 0)
# a button finger height to play/pause
self.button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT,
hildon.BUTTON_ARRANGEMENT_VERTICAL, title="Pause")
self.button.connect_after("clicked", self.on_button_clicked)
# don't want button to expand or fill, just stay finger height
box.pack_start(self.button, False, False, 0)
self.w.add(box)
self.w.connect("delete-event", gtk.main_quit)
self.p.add_window(self.w)
self.w.show_all()
self.start_streaming()
def start_streaming(self):
# we use ximagesink solely for screenshotting ability
# less cpu usage would happen with videotestsrc ! xvimagesink
self.pipeline = \
gst.parse_launch("videotestsrc ! videoscale ! ximagesink")
bus = self.pipeline.get_bus()
# need to connect to sync message handler so we get the sink to be
# embedded at the right time and not have a temporary new window
bus.enable_sync_message_emission()
bus.add_signal_watch()
bus.connect("sync-message::element", self.on_sync_message)
bus.connect("message", self.on_message)
self.pipeline.set_state(gst.STATE_PLAYING)
def on_sync_message(self, bus, message):
if message.structure is None:
return
if message.structure.get_name() == 'prepare-xwindow-id':
# all this is needed to sync with the X server before giving the
# x id to the sink
gtk.gdk.threads_enter()
gtk.gdk.display_get_default().sync()
self.video_widget.set_sink(message.src)
message.src.set_property("force-aspect-ratio", True)
gtk.gdk.threads_leave()
def on_message(self, bus, message):
if message.type == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
hildon.hildon_banner_show_information(self.w, '',
"Error: %s" % err)
def on_button_clicked(self, widget):
success, state, pending = self.pipeline.get_state(1)
# do not listen if in middle of state change
if not pending:
if state == gst.STATE_PLAYING:
self.pipeline.set_state(gst.STATE_PAUSED)
self.button.set_label("Play")
else:
self.pipeline.set_state(gst.STATE_PLAYING)
self.button.set_label("Pause")
def main():
view = MaemoGstView()
gtk.main()
if __name__ == '__main__':
sys.exit(main())

31
old_examples/mixer.py Normal file
View file

@ -0,0 +1,31 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import sys
import gst
import gst.interfaces
pipeline = "alsasrc"
if sys.argv[1:]:
pipeline = " ".join(sys.argv[1:])
a = gst.element_factory_make(pipeline)
print dir(a)
res = a.set_state(gst.STATE_PAUSED)
if res != gst.STATE_CHANGE_SUCCESS:
print "Could not set pipeline %s to PAUSED" % pipeline
print "Inputs:"
for t in a.list_tracks():
if t.flags & gst.interfaces.MIXER_TRACK_INPUT:
sys.stdout.write(t.label)
sys.stdout.write(': %d - %d' % (t.min_volume, t.max_volume))
volumes = a.get_volume(t)
sys.stdout.write(': %r' % (volumes, ))
if t.props.num_channels > 0:
a.set_volume(t, volumes=volumes)
if t.flags & gst.interfaces.MIXER_TRACK_RECORD:
sys.stdout.write(' (selected)')
sys.stdout.write('\n')

View file

@ -0,0 +1,43 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import sys
import pygtk
pygtk.require('2.0')
from gobject.option import OptionParser, OptionGroup
import pygst
pygst.require('0.10')
import gstoption
def main(args):
parser = OptionParser()
group = OptionGroup('flumotion', 'Flumotion options',
option_list=[])
group.add_option('-v', '--verbose',
action="store_true", dest="verbose",
help="be verbose")
group.add_option('', '--version',
action="store_true", dest="version",
default=False,
help="show version information")
parser.add_option_group(group)
parser.add_option_group(gstoption.get_group())
options, args = parser.parse_args(args)
if options.verbose:
print 'Verbose mode'
import gst
if options.version:
print sys.version, gst.version
if __name__ == '__main__':
sys.exit(main(sys.argv))

263
old_examples/pipeline-tester Executable file
View file

@ -0,0 +1,263 @@
#!/usr/bin/env python
#
# gst-python
# Copyright (C) 2005 Andy Wingo <wingo@pobox.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# A test more of gst-plugins than of gst-python.
import sys
import pygtk
pygtk.require('2.0')
import gtk
import gtk.gdk
import pango
import gobject
import pygst
pygst.require('0.10')
import gst
import debugslider
data = (('Video capture via V4L',
'v4lsrc name=source \n'
' ! videorate \n'
' ! ffmpegcolorspace ! autovideosink'),
('Video capture via V4L, fixed frame rate',
'v4lsrc name=source autoprobe=false autoprobe-fps=false \n'
' ! video/x-raw-yuv,format=(fourcc)I420,framerate=(double)7.5 \n'
' ! videorate \n'
' ! ffmpegcolorspace \n'
' ! autovideosink'),
('Sound capture',
'gconfaudiosrc\n'
' ! audio/x-raw-int,rate=22050,depth=16,channels=1,width=16,signed=(boolean)TRUE,endianness=(int)BYTE_ORDER\n'
' ! level message=true\n'
' ! fakesink'),
('Streaming Ogg/Theora+Vorbis playback, tee to disk',
'gnomevfssrc location=http://gstreamer.freedesktop.org/media/small/cooldance.ogg \n'
' ! tee name=tee \n'
' tee. ! oggdemux name=demux \n'
' demux. ! queue ! theoradec ! ffmpegcolorspace ! autovideosink \n'
' demux. ! queue ! vorbisdec ! audioconvert ! autoaudiosink \n'
' tee. ! queue ! filesink location=/tmp/cooldance.ogg'),
('Video test, YUV format',
'videotestsrc \n'
' ! video/x-raw-yuv,format=(fourcc)I420 \n'
' ! ffmpegcolorspace ! autovideosink'),
('Video test, RGB format',
'videotestsrc \n'
' ! video/x-raw-rgb,red_mask=0xff00 \n'
' ! ffmpegcolorspace \n'
' ! autovideosink'),
('Software scaling',
'videotestsrc \n'
' ! video/x-raw-rgb,height=200,width=320 \n'
' ! videoscale method=2 \n'
' ! ffmpegcolorspace ! autovideosink'),
('Reencode Vorbis to mulaw, play',
'filesrc location=/tmp/cooldance.ogg \n'
' ! oggdemux \n'
' ! vorbisdec ! audioconvert \n'
' ! mulawenc ! mulawdec ! autoaudiosink'),
('Capture DV via firewire, transcode into Ogg',
'dv1394src \n'
' ! dvdemux name=demux \n'
' ! queue \n'
' ! video/x-dv,systemstream=(boolean)false \n'
' ! dvdec drop-factor=2 \n'
' ! videorate \n'
' ! videoscale \n'
' ! video/x-raw-yuv,width=360,height=288 \n'
' ! videoscale \n'
' ! video/x-raw-yuv,width=240,height=192,framerate=10.0,format=(fourcc)YUY2 \n'
' ! ffmpegcolorspace \n'
' ! theoraenc \n'
' ! oggmux name=mux \n'
' ! filesink location=/tmp/dv.ogg \n'
' \n'
' demux. \n'
' ! audio/x-raw-int \n'
' ! queue \n'
' ! audioconvert \n'
' ! vorbisenc \n'
' ! mux.'))
def escape(s, chars, escaper='\\'):
for c in chars:
s = s.replace(c, '%s%s' % (escaper, c))
return s
def make_model():
m = gtk.ListStore(str, str)
for pair in data:
i = m.append()
m.set_value(i, 0, pair[0])
m.set_value(i, 1, pair[1])
return m
class Window(gtk.Window):
def __init__(self):
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
self.playing = False
self.selected_pipe = None
self.pipeline = None
self.prepare_ui()
def prepare_ui(self):
self.set_default_size(300,400)
self.set_title('GStreamer Pipeline Tester')
self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
self.connect('delete-event', lambda *x: gtk.main_quit())
self.set_border_width(18)
b = gtk.VBox(False, 12)
b.show()
self.add(b)
l = gtk.Label()
l.set_markup('<big><b>GStreamer Pipeline Tester</b></big>')
l.show()
b.pack_start(l, False, False, 6)
l = gtk.Label('Choose a pipeline below to run.')
l.show()
b.pack_start(l, False, False, 0)
sw = gtk.ScrolledWindow()
sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER)
sw.set_shadow_type(gtk.SHADOW_IN)
sw.show()
b.pack_start(sw, True, True, 6)
tv = gtk.TreeView(make_model())
tv.set_property('can-default', False)
r = gtk.CellRendererText()
r.set_property('xalign', 0.5)
c = gtk.TreeViewColumn('System', r, text=0)
tv.append_column(c)
tv.set_headers_visible(False)
tv.show()
sw.add(tv)
ds = debugslider.DebugSlider()
ds.show()
b.pack_start(ds, False, False, 0)
l = gtk.Label()
l.set_selectable(True)
l.show()
b.pack_start(l, False, False, 0)
bb = gtk.HButtonBox()
bb.set_layout(gtk.BUTTONBOX_SPREAD)
bb.show()
b.pack_start(bb, False, False, 0)
bu = gtk.Button(stock=gtk.STOCK_MEDIA_PLAY)
bu.set_property('can-default', True)
bu.set_focus_on_click(False)
bu.show()
bb.pack_start(bu, True, False, 0)
bu.set_property('has-default', True)
self.button = bu
def on_changed(s):
m, i = s.get_selected()
if m:
self.selected_pipe = m.get_value(i, 1)
pasteable = escape(self.selected_pipe, '\n)(')
l.set_markup('<small><tt>%s</tt></small>' % pasteable)
else:
self.selected_pipe = None
l.set_markup('')
tv.get_selection().connect('changed', on_changed)
tv.connect('row-activated', lambda *x: self.play_toggled())
bu.connect('clicked', lambda *x: self.play_toggled())
def error(self, message, secondary=None):
m = gtk.MessageDialog(self,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR,
gtk.BUTTONS_OK,
message)
if secondary:
m.format_secondary_text(secondary)
m.run()
m.destroy()
self.stop()
def on_message(self, bus, message):
t = message.type
print message
if t == gst.MESSAGE_STATE_CHANGED:
pass
elif t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
self.error("%s" % err, debug)
elif t == gst.MESSAGE_EOS:
self.play_toggled()
else:
print '%s: %s:' % (message.src.get_path_string(),
message.type.value_nicks[1])
if message.structure:
print ' %s' % message.structure.to_string()
else:
print ' (no structure)'
return True
def play(self):
pipestr = self.selected_pipe
try:
self.set_sensitive(False)
pipeline = gst.parse_launch(pipestr)
self.set_sensitive(True)
except gobject.GError, e:
self.set_sensitive(True)
self.error('Could not create pipeline', str(e))
return False
bus = pipeline.get_bus()
bus.add_signal_watch()
watch_id = bus.connect('message', self.on_message)
self.pipeline = pipeline
self.watch_id = watch_id
pipeline.set_state(gst.STATE_PLAYING)
def stop(self):
bus = self.pipeline.get_bus()
bus.disconnect(self.watch_id)
bus.remove_signal_watch()
self.pipeline.set_state(gst.STATE_NULL)
self.pipeline = None
del self.watch_id
def play_toggled(self):
if self.playing:
self.stop()
self.button.set_label(gtk.STOCK_MEDIA_PLAY)
self.playing = False
else:
self.play()
self.playing = True
self.button.set_label(gtk.STOCK_MEDIA_STOP)
if __name__ == '__main__':
w = Window()
w.show()
gtk.main()

299
old_examples/play.py Normal file
View file

@ -0,0 +1,299 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import pygtk
pygtk.require('2.0')
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
import gst.interfaces
import gtk
gtk.gdk.threads_init()
class GstPlayer:
def __init__(self, videowidget):
self.playing = False
self.player = gst.element_factory_make("playbin", "player")
self.videowidget = videowidget
self.on_eos = False
bus = self.player.get_bus()
bus.enable_sync_message_emission()
bus.add_signal_watch()
bus.connect('sync-message::element', self.on_sync_message)
bus.connect('message', self.on_message)
def on_sync_message(self, bus, message):
if message.structure is None:
return
if message.structure.get_name() == 'prepare-xwindow-id':
# Sync with the X server before giving the X-id to the sink
gtk.gdk.threads_enter()
gtk.gdk.display_get_default().sync()
self.videowidget.set_sink(message.src)
message.src.set_property('force-aspect-ratio', True)
gtk.gdk.threads_leave()
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
if self.on_eos:
self.on_eos()
self.playing = False
elif t == gst.MESSAGE_EOS:
if self.on_eos:
self.on_eos()
self.playing = False
def set_location(self, location):
self.player.set_property('uri', location)
def query_position(self):
"Returns a (position, duration) tuple"
try:
position, format = self.player.query_position(gst.FORMAT_TIME)
except:
position = gst.CLOCK_TIME_NONE
try:
duration, format = self.player.query_duration(gst.FORMAT_TIME)
except:
duration = gst.CLOCK_TIME_NONE
return (position, duration)
def seek(self, location):
"""
@param location: time to seek to, in nanoseconds
"""
gst.debug("seeking to %r" % location)
event = gst.event_new_seek(1.0, gst.FORMAT_TIME,
gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE,
gst.SEEK_TYPE_SET, location,
gst.SEEK_TYPE_NONE, 0)
res = self.player.send_event(event)
if res:
gst.info("setting new stream time to 0")
self.player.set_new_stream_time(0L)
else:
gst.error("seek to %r failed" % location)
def pause(self):
gst.info("pausing player")
self.player.set_state(gst.STATE_PAUSED)
self.playing = False
def play(self):
gst.info("playing player")
self.player.set_state(gst.STATE_PLAYING)
self.playing = True
def stop(self):
self.player.set_state(gst.STATE_NULL)
gst.info("stopped player")
def get_state(self, timeout=1):
return self.player.get_state(timeout=timeout)
def is_playing(self):
return self.playing
class VideoWidget(gtk.DrawingArea):
def __init__(self):
gtk.DrawingArea.__init__(self)
self.imagesink = None
self.unset_flags(gtk.DOUBLE_BUFFERED)
def do_expose_event(self, event):
if self.imagesink:
self.imagesink.expose()
return False
else:
return True
def set_sink(self, sink):
assert self.window.xid
self.imagesink = sink
self.imagesink.set_xwindow_id(self.window.xid)
class PlayerWindow(gtk.Window):
UPDATE_INTERVAL = 500
def __init__(self):
gtk.Window.__init__(self)
self.set_default_size(410, 325)
self.create_ui()
self.player = GstPlayer(self.videowidget)
def on_eos():
self.player.seek(0L)
self.play_toggled()
self.player.on_eos = lambda *x: on_eos()
self.update_id = -1
self.changed_id = -1
self.seek_timeout_id = -1
self.p_position = gst.CLOCK_TIME_NONE
self.p_duration = gst.CLOCK_TIME_NONE
def on_delete_event():
self.player.stop()
gtk.main_quit()
self.connect('delete-event', lambda *x: on_delete_event())
def load_file(self, location):
self.player.set_location(location)
def create_ui(self):
vbox = gtk.VBox()
self.add(vbox)
self.videowidget = VideoWidget()
vbox.pack_start(self.videowidget)
hbox = gtk.HBox()
vbox.pack_start(hbox, fill=False, expand=False)
self.pause_image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PAUSE,
gtk.ICON_SIZE_BUTTON)
self.pause_image.show()
self.play_image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PLAY,
gtk.ICON_SIZE_BUTTON)
self.play_image.show()
self.button = button = gtk.Button()
button.add(self.play_image)
button.set_property('can-default', True)
button.set_focus_on_click(False)
button.show()
hbox.pack_start(button, False)
button.set_property('has-default', True)
button.connect('clicked', lambda *args: self.play_toggled())
self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0)
hscale = gtk.HScale(self.adjustment)
hscale.set_digits(2)
hscale.set_update_policy(gtk.UPDATE_CONTINUOUS)
hscale.connect('button-press-event', self.scale_button_press_cb)
hscale.connect('button-release-event', self.scale_button_release_cb)
hscale.connect('format-value', self.scale_format_value_cb)
hbox.pack_start(hscale)
self.hscale = hscale
self.videowidget.connect_after('realize',
lambda *x: self.play_toggled())
def play_toggled(self):
self.button.remove(self.button.child)
if self.player.is_playing():
self.player.pause()
self.button.add(self.play_image)
else:
self.player.play()
if self.update_id == -1:
self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
self.update_scale_cb)
self.button.add(self.pause_image)
def scale_format_value_cb(self, scale, value):
if self.p_duration == -1:
real = 0
else:
real = value * self.p_duration / 100
seconds = real / gst.SECOND
return "%02d:%02d" % (seconds / 60, seconds % 60)
def scale_button_press_cb(self, widget, event):
# see seek.c:start_seek
gst.debug('starting seek')
self.button.set_sensitive(False)
self.was_playing = self.player.is_playing()
if self.was_playing:
self.player.pause()
# don't timeout-update position during seek
if self.update_id != -1:
gobject.source_remove(self.update_id)
self.update_id = -1
# make sure we get changed notifies
if self.changed_id == -1:
self.changed_id = self.hscale.connect('value-changed',
self.scale_value_changed_cb)
def scale_value_changed_cb(self, scale):
# see seek.c:seek_cb
real = long(scale.get_value() * self.p_duration / 100) # in ns
gst.debug('value changed, perform seek to %r' % real)
self.player.seek(real)
# allow for a preroll
self.player.get_state(timeout=50*gst.MSECOND) # 50 ms
def scale_button_release_cb(self, widget, event):
# see seek.cstop_seek
widget.disconnect(self.changed_id)
self.changed_id = -1
self.button.set_sensitive(True)
if self.seek_timeout_id != -1:
gobject.source_remove(self.seek_timeout_id)
self.seek_timeout_id = -1
else:
gst.debug('released slider, setting back to playing')
if self.was_playing:
self.player.play()
if self.update_id != -1:
self.error('Had a previous update timeout id')
else:
self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
self.update_scale_cb)
def update_scale_cb(self):
self.p_position, self.p_duration = self.player.query_position()
if self.p_position != gst.CLOCK_TIME_NONE:
value = self.p_position * 100.0 / self.p_duration
self.adjustment.set_value(value)
return True
def main(args):
def usage():
sys.stderr.write("usage: %s URI-OF-MEDIA-FILE\n" % args[0])
sys.exit(1)
# Need to register our derived widget types for implicit event
# handlers to get called.
gobject.type_register(PlayerWindow)
gobject.type_register(VideoWidget)
w = PlayerWindow()
if len(args) != 2:
usage()
if not gst.uri_is_valid(args[1]):
sys.stderr.write("Error: Invalid URI: %s\n" % args[1])
sys.exit(1)
w.load_file(args[1])
w.show_all()
gtk.main()
if __name__ == '__main__':
sys.exit(main(sys.argv))

View file

@ -0,0 +1,68 @@
#!/usr/bin/env python
import pygtk
pygtk.require ("2.0")
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
class PyIdentity(gst.Element):
_sinkpadtemplate = gst.PadTemplate ("sink",
gst.PAD_SINK,
gst.PAD_ALWAYS,
gst.caps_new_any())
_srcpadtemplate = gst.PadTemplate ("src",
gst.PAD_SRC,
gst.PAD_ALWAYS,
gst.caps_new_any())
def __init__(self):
gst.Element.__init__(self)
self.sinkpad = gst.Pad(self._sinkpadtemplate, "sink")
self.sinkpad.set_chain_function(self.chainfunc)
self.sinkpad.set_event_function(self.eventfunc)
self.sinkpad.set_getcaps_function(gst.Pad.proxy_getcaps)
self.sinkpad.set_setcaps_function(gst.Pad.proxy_setcaps)
self.add_pad (self.sinkpad)
self.srcpad = gst.Pad(self._srcpadtemplate, "src")
self.srcpad.set_event_function(self.srceventfunc)
self.srcpad.set_query_function(self.srcqueryfunc)
self.srcpad.set_getcaps_function(gst.Pad.proxy_getcaps)
self.srcpad.set_setcaps_function(gst.Pad.proxy_setcaps)
self.add_pad (self.srcpad)
def chainfunc(self, pad, buffer):
gst.log ("Passing buffer with ts %d" % (buffer.timestamp))
return self.srcpad.push (buffer)
def eventfunc(self, pad, event):
return self.srcpad.push_event (event)
def srcqueryfunc (self, pad, query):
return self.sinkpad.query (query)
def srceventfunc (self, pad, event):
return self.sinkpad.push_event (event)
gobject.type_register(PyIdentity)
pipe = gst.Pipeline()
vt = gst.element_factory_make ("videotestsrc")
i1 = PyIdentity()
color = gst.element_factory_make ("ffmpegcolorspace")
scale = gst.element_factory_make ("videoscale")
q1 = gst.element_factory_make ("queue")
i2 = PyIdentity()
sink = gst.element_factory_make ("autovideosink")
pipe.add (vt, i1, q1, i2, color, scale, sink)
gst.element_link_many (vt, i1, q1, i2, color, scale, sink)
pipe.set_state (gst.STATE_PLAYING)
gobject.MainLoop().run()

840
old_examples/remuxer.py Normal file
View file

@ -0,0 +1,840 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import pygtk
pygtk.require('2.0')
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
import gst.interfaces
import gtk
gtk.gdk.threads_init()
class GstPlayer:
def __init__(self, videowidget):
self.playing = False
self.player = gst.element_factory_make("playbin", "player")
self.videowidget = videowidget
bus = self.player.get_bus()
bus.enable_sync_message_emission()
bus.add_signal_watch()
bus.connect('sync-message::element', self.on_sync_message)
bus.connect('message', self.on_message)
def on_sync_message(self, bus, message):
if message.structure is None:
return
if message.structure.get_name() == 'prepare-xwindow-id':
# Sync with the X server before giving the X-id to the sink
gtk.gdk.threads_enter()
gtk.gdk.display_get_default().sync()
self.videowidget.set_sink(message.src)
message.src.set_property('force-aspect-ratio', True)
gtk.gdk.threads_leave()
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
if self.on_eos:
self.on_eos()
self.playing = False
elif t == gst.MESSAGE_EOS:
if self.on_eos:
self.on_eos()
self.playing = False
def set_location(self, location):
self.player.set_state(gst.STATE_NULL)
self.player.set_property('uri', location)
def get_location(self):
return self.player.get_property('uri')
def query_position(self):
"Returns a (position, duration) tuple"
try:
position, format = self.player.query_position(gst.FORMAT_TIME)
except:
position = gst.CLOCK_TIME_NONE
try:
duration, format = self.player.query_duration(gst.FORMAT_TIME)
except:
duration = gst.CLOCK_TIME_NONE
return (position, duration)
def seek(self, location):
"""
@param location: time to seek to, in nanoseconds
"""
gst.debug("seeking to %r" % location)
event = gst.event_new_seek(1.0, gst.FORMAT_TIME,
gst.SEEK_FLAG_FLUSH,
gst.SEEK_TYPE_SET, location,
gst.SEEK_TYPE_NONE, 0)
res = self.player.send_event(event)
if res:
gst.info("setting new stream time to 0")
self.player.set_new_stream_time(0L)
else:
gst.error("seek to %r failed" % location)
def pause(self):
gst.info("pausing player")
self.player.set_state(gst.STATE_PAUSED)
self.playing = False
def play(self):
gst.info("playing player")
self.player.set_state(gst.STATE_PLAYING)
self.playing = True
def stop(self):
self.player.set_state(gst.STATE_NULL)
gst.info("stopped player")
def get_state(self, timeout=1):
return self.player.get_state(timeout=timeout)
def is_playing(self):
return self.playing
class VideoWidget(gtk.DrawingArea):
def __init__(self):
gtk.DrawingArea.__init__(self)
self.imagesink = None
self.unset_flags(gtk.DOUBLE_BUFFERED)
def do_expose_event(self, event):
if self.imagesink:
self.imagesink.expose()
return False
else:
return True
def set_sink(self, sink):
assert self.window.xid
self.imagesink = sink
self.imagesink.set_xwindow_id(self.window.xid)
class TimeControl(gtk.HBox):
# all labels same size
sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
__gproperties__ = {'time': (gobject.TYPE_UINT64, 'Time', 'Time',
# not actually usable: see #335854
# kept for .notify() usage
0L, (1<<63)-1, 0L,
gobject.PARAM_READABLE)}
def __init__(self, window, label):
gtk.HBox.__init__(self)
self.pwindow = window
self.label = label
self.create_ui()
def get_property(self, param, pspec):
if param == 'time':
return self.get_time()
else:
assert param in self.__gproperties__, \
'Unknown property: %s' % param
def create_ui(self):
label = gtk.Label(self.label + ": ")
label.show()
a = gtk.Alignment(1.0, 0.5)
a.add(label)
a.set_padding(0, 0, 12, 0)
a.show()
self.sizegroup.add_widget(a)
self.pack_start(a, True, False, 0)
self.minutes = minutes = gtk.Entry(5)
minutes.set_width_chars(5)
minutes.set_alignment(1.0)
minutes.connect('changed', lambda *x: self.notify('time'))
minutes.connect_after('activate', lambda *x: self.activated())
label2 = gtk.Label(":")
self.seconds = seconds = gtk.Entry(2)
seconds.set_width_chars(2)
seconds.set_alignment(1.0)
seconds.connect('changed', lambda *x: self.notify('time'))
seconds.connect_after('activate', lambda *x: self.activated())
label3 = gtk.Label(".")
self.milliseconds = milliseconds = gtk.Entry(3)
milliseconds.set_width_chars(3)
milliseconds.set_alignment(0.0)
milliseconds.connect('changed', lambda *x: self.notify('time'))
milliseconds.connect_after('activate', lambda *x: self.activated())
set = gtk.Button('Set')
goto = gtk.Button('Go')
goto.set_property('image',
gtk.image_new_from_stock(gtk.STOCK_JUMP_TO,
gtk.ICON_SIZE_BUTTON))
for w in minutes, label2, seconds, label3, milliseconds:
w.show()
self.pack_start(w, False)
set.show()
self.pack_start(set, False, False, 6)
goto.show()
self.pack_start(goto, False, False, 0)
set.connect('clicked', lambda *x: self.set_now())
goto.connect('clicked', lambda *x: self.activated())
pad = gtk.Label("")
pad.show()
self.pack_start(pad, True, False, 0)
def get_time(self):
time = 0
for w, multiplier in ((self.minutes, gst.SECOND*60),
(self.seconds, gst.SECOND),
(self.milliseconds, gst.MSECOND)):
text = w.get_text()
try:
val = int(text)
except ValueError:
val = 0
w.set_text(val and str(val) or '0')
time += val * multiplier
return time
def set_time(self, time):
if time == gst.CLOCK_TIME_NONE:
print "Can't set '%s' (invalid time)" % self.label
return
self.freeze_notify()
for w, multiplier in ((self.minutes, gst.SECOND*60),
(self.seconds, gst.SECOND),
(self.milliseconds, gst.MSECOND)):
val = time // multiplier
w.set_text(str(val))
time -= val * multiplier
self.thaw_notify()
def set_now(self):
time, dur = self.pwindow.player.query_position()
self.set_time(time)
def activated(self):
time = self.get_time()
if self.pwindow.player.is_playing():
self.pwindow.play_toggled()
self.pwindow.player.seek(time)
self.pwindow.player.get_state(timeout=gst.MSECOND * 200)
class ProgressDialog(gtk.Dialog):
def __init__(self, title, description, task, parent, flags, buttons):
gtk.Dialog.__init__(self, title, parent, flags, buttons)
self._create_ui(title, description, task)
def _create_ui(self, title, description, task):
self.set_border_width(6)
self.set_resizable(False)
self.set_has_separator(False)
vbox = gtk.VBox()
vbox.set_border_width(6)
vbox.show()
self.vbox.pack_start(vbox, False)
label = gtk.Label('<big><b>%s</b></big>' % title)
label.set_use_markup(True)
label.set_alignment(0.0, 0.0)
label.show()
vbox.pack_start(label, False)
label = gtk.Label(description)
label.set_use_markup(True)
label.set_alignment(0.0, 0.0)
label.set_line_wrap(True)
label.set_padding(0, 12)
label.show()
vbox.pack_start(label, False)
self.progress = progress = gtk.ProgressBar()
progress.show()
vbox.pack_start(progress, False)
self.progresstext = label = gtk.Label('')
label.set_line_wrap(True)
label.set_use_markup(True)
label.set_alignment(0.0, 0.0)
label.show()
vbox.pack_start(label)
self.set_task(task)
def set_task(self, task):
self.progresstext.set_markup('<i>%s</i>' % task)
UNKNOWN = 0
SUCCESS = 1
FAILURE = 2
CANCELLED = 3
class RemuxProgressDialog(ProgressDialog):
def __init__(self, parent, start, stop, fromname, toname):
ProgressDialog.__init__(self,
"Writing to disk",
('Writing the selected segment of <b>%s</b> '
'to <b>%s</b>. This may take some time.'
% (fromname, toname)),
'Starting media pipeline',
parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, CANCELLED,
gtk.STOCK_CLOSE, SUCCESS))
self.start = start
self.stop = stop
self.update_position(start)
self.set_completed(False)
def update_position(self, pos):
pos = min(max(pos, self.start), self.stop)
remaining = self.stop - pos
minutes = remaining // (gst.SECOND * 60)
seconds = (remaining - minutes * gst.SECOND * 60) // gst.SECOND
self.progress.set_text('%d:%02d of video remaining' % (minutes, seconds))
self.progress.set_fraction(1.0 - float(remaining) / (self.stop - self.start))
def set_completed(self, completed):
self.set_response_sensitive(CANCELLED, not completed)
self.set_response_sensitive(SUCCESS, completed)
def set_connection_blocked_async_marshalled(pads, proc, *args, **kwargs):
def clear_list(l):
while l:
l.pop()
to_block = list(pads)
to_relink = [(x, x.get_peer()) for x in pads]
def on_pad_blocked_sync(pad, is_blocked):
if pad not in to_block:
# can happen after the seek and before unblocking -- racy,
# but no prob, bob.
return
to_block.remove(pad)
if not to_block:
# marshal to main thread
gobject.idle_add(on_pads_blocked)
def on_pads_blocked():
for src, sink in to_relink:
src.link(sink)
proc(*args, **kwargs)
for src, sink in to_relink:
src.set_blocked_async(False, lambda *x: None)
clear_list(to_relink)
for src, sink in to_relink:
src.unlink(sink)
src.set_blocked_async(True, on_pad_blocked_sync)
class Remuxer(gst.Pipeline):
__gsignals__ = {'done': (gobject.SIGNAL_RUN_LAST, None, (int,))}
def __init__(self, fromuri, touri, start, stop):
# HACK: should do Pipeline.__init__, but that doesn't do what we
# want; there's a bug open aboooot that
self.__gobject_init__()
assert start >= 0
assert stop > start
self.fromuri = fromuri
self.touri = None
self.start_time = start
self.stop_time = stop
self.src = self.remuxbin = self.sink = None
self.resolution = UNKNOWN
self.window = None
self.pdialog = None
self._query_id = -1
def do_setup_pipeline(self):
self.src = gst.element_make_from_uri(gst.URI_SRC, self.fromuri)
self.remuxbin = RemuxBin(self.start_time, self.stop_time)
self.sink = gst.element_make_from_uri(gst.URI_SINK, self.touri)
self.resolution = UNKNOWN
if gobject.signal_lookup('allow-overwrite', self.sink.__class__):
self.sink.connect('allow-overwrite', lambda *x: True)
self.add(self.src, self.remuxbin, self.sink)
self.src.link(self.remuxbin)
self.remuxbin.link(self.sink)
def do_get_touri(self):
chooser = gtk.FileChooserDialog('Save as...',
self.window,
action=gtk.FILE_CHOOSER_ACTION_SAVE,
buttons=(gtk.STOCK_CANCEL,
CANCELLED,
gtk.STOCK_SAVE,
SUCCESS))
chooser.set_uri(self.fromuri) # to select the folder
chooser.unselect_all()
chooser.set_do_overwrite_confirmation(True)
name = self.fromuri.split('/')[-1][:-4] + '-remuxed.ogg'
chooser.set_current_name(name)
resp = chooser.run()
uri = chooser.get_uri()
chooser.destroy()
if resp == SUCCESS:
return uri
else:
return None
def _start_queries(self):
def do_query():
try:
# HACK: self.remuxbin.query() should do the same
# (requires implementing a vmethod, dunno how to do that
# although i think it's possible)
# HACK: why does self.query_position(..) not give useful
# answers?
pad = self.remuxbin.get_pad('src')
pos, duration = pad.query_position(gst.FORMAT_TIME)
if pos != gst.CLOCK_TIME_NONE:
self.pdialog.update_position(pos)
except:
# print 'query failed'
pass
return True
if self._query_id == -1:
self._query_id = gobject.timeout_add(100, # 10 Hz
do_query)
def _stop_queries(self):
if self._query_id != -1:
gobject.source_remove(self._query_id)
self._query_id = -1
def _bus_watch(self, bus, message):
if message.type == gst.MESSAGE_ERROR:
print 'error', message
self._stop_queries()
m = gtk.MessageDialog(self.window,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR,
gtk.BUTTONS_CLOSE,
"Error processing file")
gerror, debug = message.parse_error()
txt = ('There was an error processing your file: %s\n\n'
'Debug information:\n%s' % (gerror, debug))
m.format_secondary_text(txt)
m.run()
m.destroy()
self.response(FAILURE)
elif message.type == gst.MESSAGE_WARNING:
print 'warning', message
elif message.type == gst.MESSAGE_EOS:
# print 'eos, woot', message.src
name = self.touri
if name.startswith('file://'):
name = name[7:]
self.pdialog.set_task('Finished writing %s' % name)
self.pdialog.update_position(self.stop_time)
self._stop_queries()
self.pdialog.set_completed(True)
elif message.type == gst.MESSAGE_STATE_CHANGED:
if message.src == self:
old, new, pending = message.parse_state_changed()
if ((old, new, pending) ==
(gst.STATE_READY, gst.STATE_PAUSED,
gst.STATE_VOID_PENDING)):
self.pdialog.set_task('Processing file')
self.pdialog.update_position(self.start_time)
self._start_queries()
self.set_state(gst.STATE_PLAYING)
def response(self, response):
assert self.resolution == UNKNOWN
self.resolution = response
self.set_state(gst.STATE_NULL)
self.pdialog.destroy()
self.pdialog = None
self.window.set_sensitive(True)
self.emit('done', response)
def start(self, main_window):
self.window = main_window
self.touri = self.do_get_touri()
if not self.touri:
return False
self.do_setup_pipeline()
bus = self.get_bus()
bus.add_signal_watch()
bus.connect('message', self._bus_watch)
if self.window:
# can be None if we are debugging...
self.window.set_sensitive(False)
fromname = self.fromuri.split('/')[-1]
toname = self.touri.split('/')[-1]
self.pdialog = RemuxProgressDialog(main_window, self.start_time,
self.stop_time, fromname, toname)
self.pdialog.show()
self.pdialog.connect('response', lambda w, r: self.response(r))
self.set_state(gst.STATE_PAUSED)
return True
def run(self, main_window):
if self.start(main_window):
loop = gobject.MainLoop()
self.connect('done', lambda *x: gobject.idle_add(loop.quit))
loop.run()
else:
self.resolution = CANCELLED
return self.resolution
class RemuxBin(gst.Bin):
def __init__(self, start_time, stop_time):
self.__gobject_init__()
self.parsefactories = self._find_parsers()
self.parsers = []
self.demux = gst.element_factory_make('oggdemux')
self.mux = gst.element_factory_make('oggmux')
self.add(self.demux, self.mux)
self.add_pad(gst.GhostPad('sink', self.demux.get_pad('sink')))
self.add_pad(gst.GhostPad('src', self.mux.get_pad('src')))
self.demux.connect('pad-added', self._new_demuxed_pad)
self.demux.connect('no-more-pads', self._no_more_pads)
self.start_time = start_time
self.stop_time = stop_time
def _find_parsers(self):
registry = gst.registry_get_default()
ret = {}
for f in registry.get_feature_list(gst.ElementFactory):
if f.get_klass().find('Parser') >= 0:
for t in f.get_static_pad_templates():
if t.direction == gst.PAD_SINK:
for s in t.get_caps():
ret[s.get_name()] = f.get_name()
break
return ret
def _new_demuxed_pad(self, element, pad):
format = pad.get_caps()[0].get_name()
if format not in self.parsefactories:
self.async_error("Unsupported media type: %s", format)
return
queue = gst.element_factory_make('queue', None);
queue.set_property('max-size-buffers', 1000)
parser = gst.element_factory_make(self.parsefactories[format])
self.add(queue)
self.add(parser)
queue.set_state(gst.STATE_PAUSED)
parser.set_state(gst.STATE_PAUSED)
pad.link(queue.get_compatible_pad(pad))
queue.link(parser)
parser.link(self.mux)
self.parsers.append(parser)
def _do_seek(self):
flags = gst.SEEK_FLAG_FLUSH
# HACK: self.seek should work, should try that at some point
return self.demux.seek(1.0, gst.FORMAT_TIME, flags,
gst.SEEK_TYPE_SET, self.start_time,
gst.SEEK_TYPE_SET, self.stop_time)
def _no_more_pads(self, element):
pads = [x.get_pad('src') for x in self.parsers]
set_connection_blocked_async_marshalled(pads,
self._do_seek)
class PlayerWindow(gtk.Window):
UPDATE_INTERVAL = 500
def __init__(self):
gtk.Window.__init__(self)
self.set_default_size(600, 425)
self.create_ui()
self.player = GstPlayer(self.videowidget)
def on_eos():
self.player.seek(0L)
self.play_toggled()
self.player.on_eos = lambda *x: on_eos()
self.update_id = -1
self.changed_id = -1
self.seek_timeout_id = -1
self.p_position = gst.CLOCK_TIME_NONE
self.p_duration = gst.CLOCK_TIME_NONE
def on_delete_event():
self.player.stop()
gtk.main_quit()
self.connect('delete-event', lambda *x: on_delete_event())
def load_file(self, location):
filename = location.split('/')[-1]
self.set_title('%s munger' % filename)
self.player.set_location(location)
if self.videowidget.flags() & gtk.REALIZED:
self.play_toggled()
else:
self.videowidget.connect_after('realize',
lambda *x: self.play_toggled())
def create_ui(self):
vbox = gtk.VBox()
vbox.show()
self.add(vbox)
self.videowidget = VideoWidget()
self.videowidget.show()
vbox.pack_start(self.videowidget)
hbox = gtk.HBox()
hbox.show()
vbox.pack_start(hbox, fill=False, expand=False)
self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0)
hscale = gtk.HScale(self.adjustment)
hscale.set_digits(2)
hscale.set_update_policy(gtk.UPDATE_CONTINUOUS)
hscale.connect('button-press-event', self.scale_button_press_cb)
hscale.connect('button-release-event', self.scale_button_release_cb)
hscale.connect('format-value', self.scale_format_value_cb)
hbox.pack_start(hscale)
hscale.show()
self.hscale = hscale
table = gtk.Table(2,3)
table.show()
vbox.pack_start(table, fill=False, expand=False, padding=6)
self.button = button = gtk.Button(stock=gtk.STOCK_MEDIA_PLAY)
button.set_property('can-default', True)
button.set_focus_on_click(False)
button.show()
# problem: play and paused are of different widths and cause the
# window to re-layout
# "solution": add more buttons to a vbox so that the horizontal
# width is enough
bvbox = gtk.VBox()
bvbox.add(button)
bvbox.add(gtk.Button(stock=gtk.STOCK_MEDIA_PLAY))
bvbox.add(gtk.Button(stock=gtk.STOCK_MEDIA_PAUSE))
sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
for kid in bvbox.get_children():
sizegroup.add_widget(kid)
bvbox.show()
table.attach(bvbox, 0, 1, 0, 2, gtk.FILL, gtk.FILL)
# can't set this property before the button has a window
button.set_property('has-default', True)
button.connect('clicked', lambda *args: self.play_toggled())
self.cutin = cut = TimeControl(self, "Cut in time")
cut.show()
table.attach(cut, 1, 2, 0, 1, gtk.EXPAND, 0, 12)
self.cutout = cut = TimeControl(self, "Cut out time")
cut.show()
table.attach(cut, 1, 2, 1, 2, gtk.EXPAND, 0, 12)
button = gtk.Button("_Open other movie...")
button.show()
button.connect('clicked', lambda *x: self.do_choose_file())
table.attach(button, 2, 3, 0, 1, gtk.FILL, gtk.FILL)
button = gtk.Button("_Write to disk")
button.set_property('image',
gtk.image_new_from_stock(gtk.STOCK_SAVE_AS,
gtk.ICON_SIZE_BUTTON))
button.connect('clicked', lambda *x: self.do_remux())
button.show()
table.attach(button, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
#self.cutin.connect('notify::time', lambda *x: self.check_cutout())
#self.cutout.connect('notify::time', lambda *x: self.check_cutin())
def do_remux(self):
if self.player.is_playing():
self.play_toggled()
in_uri = self.player.get_location()
out_uri = in_uri[:-4] + '-remuxed.ogg'
r = Remuxer(in_uri, out_uri,
self.cutin.get_time(), self.cutout.get_time())
r.run(self)
def do_choose_file(self):
if self.player.is_playing():
self.play_toggled()
chooser = gtk.FileChooserDialog('Choose a movie to cut cut cut',
self,
buttons=(gtk.STOCK_CANCEL,
CANCELLED,
gtk.STOCK_OPEN,
SUCCESS))
chooser.set_local_only(False)
chooser.set_select_multiple(False)
f = gtk.FileFilter()
f.set_name("All files")
f.add_pattern("*")
chooser.add_filter(f)
f = gtk.FileFilter()
f.set_name("Ogg files")
f.add_pattern("*.og[gvax]") # as long as this is the only thing we
# support...
chooser.add_filter(f)
chooser.set_filter(f)
prev = self.player.get_location()
if prev:
chooser.set_uri(prev)
resp = chooser.run()
uri = chooser.get_uri()
chooser.destroy()
if resp == SUCCESS and uri != None:
self.load_file(uri)
return True
else:
return False
def check_cutout(self):
if self.cutout.get_time() <= self.cutin.get_time():
pos, dur = self.player.query_position()
self.cutout.set_time(dur)
def check_cutin(self):
if self.cutin.get_time() >= self.cutout.get_time():
self.cutin.set_time(0)
def play_toggled(self):
if self.player.is_playing():
self.player.pause()
self.button.set_label(gtk.STOCK_MEDIA_PLAY)
else:
self.player.play()
if self.update_id == -1:
self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
self.update_scale_cb)
self.button.set_label(gtk.STOCK_MEDIA_PAUSE)
def scale_format_value_cb(self, scale, value):
if self.p_duration == -1:
real = 0
else:
real = value * self.p_duration / 100
seconds = real / gst.SECOND
return "%02d:%02d" % (seconds / 60, seconds % 60)
def scale_button_press_cb(self, widget, event):
# see seek.c:start_seek
gst.debug('starting seek')
self.button.set_sensitive(False)
self.was_playing = self.player.is_playing()
if self.was_playing:
self.player.pause()
# don't timeout-update position during seek
if self.update_id != -1:
gobject.source_remove(self.update_id)
self.update_id = -1
# make sure we get changed notifies
if self.changed_id == -1:
self.changed_id = self.hscale.connect('value-changed',
self.scale_value_changed_cb)
def scale_value_changed_cb(self, scale):
# see seek.c:seek_cb
real = long(scale.get_value() * self.p_duration / 100) # in ns
gst.debug('value changed, perform seek to %r' % real)
self.player.seek(real)
# allow for a preroll
self.player.get_state(timeout=50*gst.MSECOND) # 50 ms
def scale_button_release_cb(self, widget, event):
# see seek.cstop_seek
widget.disconnect(self.changed_id)
self.changed_id = -1
self.button.set_sensitive(True)
if self.seek_timeout_id != -1:
gobject.source_remove(self.seek_timeout_id)
self.seek_timeout_id = -1
else:
gst.debug('released slider, setting back to playing')
if self.was_playing:
self.player.play()
if self.update_id != -1:
self.error('Had a previous update timeout id')
else:
self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
self.update_scale_cb)
def update_scale_cb(self):
had_duration = self.p_duration != gst.CLOCK_TIME_NONE
self.p_position, self.p_duration = self.player.query_position()
if self.p_position != gst.CLOCK_TIME_NONE:
value = self.p_position * 100.0 / self.p_duration
self.adjustment.set_value(value)
if not had_duration:
self.cutin.set_time(0)
return True
def main(args):
def usage():
sys.stderr.write("usage: %s [URI-OF-MEDIA-FILE]\n" % args[0])
return 1
w = PlayerWindow()
w.show()
if len(args) == 1:
if not w.do_choose_file():
return 1
elif len(args) == 2:
if not gst.uri_is_valid(args[1]):
sys.stderr.write("Error: Invalid URI: %s\n" % args[1])
return 1
w.load_file(args[1])
else:
return usage()
gtk.main()
if __name__ == '__main__':
sys.exit(main(sys.argv))

198
old_examples/segments.py Executable file
View file

@ -0,0 +1,198 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Segments.py
# Copyright (C) 2006 Artem Popov <artfwo@gmail.com>
#
# This example demonstrates segment seeking
# and seamless looping within playbin.
import pygst
pygst.require ("0.10")
import gst
import pygtk
pygtk.require ("2.0")
import gobject
class Looper (gobject.GObject):
__gproperties__ = {
"loop": (gobject.TYPE_BOOLEAN,
"loop",
"Whether to loop the segment",
False,
gobject.PARAM_READWRITE),
"start-pos": (gobject.TYPE_UINT64,
"start position",
"The segment start marker",
0,
0xfffffffffffffff, # max long possible
0,
gobject.PARAM_READWRITE),
"stop-pos": (gobject.TYPE_UINT64,
"stop position",
"The segment stop marker",
0,
0xfffffffffffffff, # max long possible
0,
gobject.PARAM_READWRITE),
} # __gproperties__
__gsignals__ = {
"stopped": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
"position-updated": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT,)),
"error": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
} # __gsignals__
def __init__ (self, location = None):
gobject.GObject.__init__ (self)
self.__playbin = gst.element_factory_make ("playbin")
self.__playbin.props.video_sink = gst.element_factory_make ("fakesink")
bus = self.__playbin.get_bus ()
bus.add_watch (self.__on_bus_message)
self.__loop = False
self.__start_pos = 0
self.__stop_pos = 0
self.__timeout_id = 0
if location:
self.load (location)
def load (self, location):
self.__playbin.props.uri = location
self.__start_position = 0
self.__stop_position = 0
def set_segment (self, start, stop):
self.props.start_pos = start
self.props.stop_pos = stop
def play (self):
if not (self.__start_pos or self.__stop_pos):
raise RuntimeError, "Cannot start playback, segment was not set!"
self.__playbin.set_state (gst.STATE_PLAYING)
def stop (self, silent = False):
self.__playbin.set_state (gst.STATE_NULL)
if not silent:
self.emit ("stopped")
def do_get_property (self, property):
if property.name == "loop":
return self.__loop
elif property.name == "start-pos":
return self.__start_pos
elif property.name == "stop-pos":
return self.__stop_pos
else:
raise AttributeError, "Unknown property %s" % property.name
def do_set_property (self, property, value):
if property.name == "loop":
self.__loop = value
elif property.name == "start-pos":
self.__start_pos = value
elif property.name == "stop-pos":
self.__stop_pos = value
else:
raise AttributeError, "Unknown property %s" % property.name
def do_stopped (self):
if self.__timeout_id:
gobject.source_remove (self.__timeout_id)
self.__timeout_id = 0
def __seek (self, start, stop, flush):
flags = gst.SEEK_FLAG_SEGMENT | gst.SEEK_FLAG_ACCURATE
if flush:
flags = flags | gst.SEEK_FLAG_FLUSH
self.__playbin.seek (1.0, gst.FORMAT_TIME, flags,
gst.SEEK_TYPE_SET, start,
gst.SEEK_TYPE_SET, stop)
def __on_timeout (self):
position = self.__playbin.query_position (gst.FORMAT_TIME) [0]
self.emit ("position-updated", float (position))
return True
def __on_bus_message (self, bus, message):
if message.type == gst.MESSAGE_ERROR:
error, debug = message.parse_error ()
self.stop () # this looks neccessary here
self.emit ("error", (error, debug))
elif message.type == gst.MESSAGE_NEW_CLOCK:
# we connect the timeout handler here to be sure that further queries succeed
interval = int ((self.__stop_position - self.__start_position) / (2 * gst.SECOND) + 50)
self.__timeout_id = gobject.timeout_add (interval, self.__on_timeout)
elif message.type == gst.MESSAGE_STATE_CHANGED:
old_state, new_state, pending = message.parse_state_changed ()
if old_state == gst.STATE_READY and new_state == gst.STATE_PAUSED and message.src == self.__playbin:
self.__seek (self.__start_pos, self.__stop_pos, True)
elif message.type == gst.MESSAGE_SEGMENT_DONE:
if self.__loop:
self.__seek (self.__start_pos, self.__stop_pos, False)
else:
src = self.__playbin.get_property ("source")
pad = src.get_pad ('src')
pad.push_event (gst.event_new_eos ())
# this is the good old way:
#
# pads = src.src_pads ()
# while True:
# try:
# pad = pads.next ()
# pad.push_event (gst.event_new_eos ())
# except:
# break
elif message.type == gst.MESSAGE_EOS:
self.stop ()
return True
mainloop = gobject.MainLoop ()
def on_looper_stopped (looper):
mainloop.quit ()
def on_looper_pos_updated (looper, position):
print round (position / gst.SECOND, 2)
def on_looper_error (looper, error_tuple):
error, debug = error_tuple
print "\n\n%s\n\n%s\n\n" % (error, debug)
mainloop.quit ()
if __name__ == "__main__":
import sys
if len (sys.argv) != 5:
print "Usage: %s <filename|uri> <start_seconds> <stop_seconds> <loop = 0|1>" % sys.argv [0]
sys.exit (1)
if "://" in sys.argv [1]:
uri = sys.argv [1]
else:
import os.path
uri = "file://" + os.path.abspath (sys.argv [1])
looper = Looper (uri)
looper.props.start_pos = long (sys.argv [2]) * gst.SECOND
looper.props.stop_pos = long (sys.argv [3]) * gst.SECOND
looper.props.loop = int (sys.argv [4])
looper.connect ("stopped", on_looper_stopped)
looper.connect ("position-updated", on_looper_pos_updated)
looper.connect ("error", on_looper_error)
looper.play ()
mainloop.run ()

View file

@ -0,0 +1,69 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# sinkelement.py
# (c) 2005 Edward Hervey <edward@fluendo.com>
# (c) 2007 Jan Schmidt <jan@fluendo.com>
# Licensed under LGPL
#
# Small test application to show how to write a sink element
# in 20 lines in python and place into the gstreamer registry
# so it can be autoplugged or used from parse_launch.
#
# Run this script with GST_DEBUG=python:5 to see the debug
# messages
import pygst
pygst.require('0.10')
import gst
import gobject
gobject.threads_init ()
#
# Simple Sink element created entirely in python
#
class MySink(gst.Element):
__gstdetails__ = ('CustomSink','Sink', \
'Custom test sink element', 'Edward Hervey')
_sinkpadtemplate = gst.PadTemplate ("sinkpadtemplate",
gst.PAD_SINK,
gst.PAD_ALWAYS,
gst.caps_new_any())
def __init__(self):
gst.Element.__init__(self)
gst.info('creating sinkpad')
self.sinkpad = gst.Pad(self._sinkpadtemplate, "sink")
gst.info('adding sinkpad to self')
self.add_pad(self.sinkpad)
gst.info('setting chain/event functions')
self.sinkpad.set_chain_function(self.chainfunc)
self.sinkpad.set_event_function(self.eventfunc)
def chainfunc(self, pad, buffer):
self.info("%s timestamp(buffer):%d" % (pad, buffer.timestamp))
return gst.FLOW_OK
def eventfunc(self, pad, event):
self.info("%s event:%r" % (pad, event.type))
return True
gobject.type_register(MySink)
# Register the element into this process' registry.
gst.element_register (MySink, 'mysink', gst.RANK_MARGINAL)
print "Use --gst-debug=python:3 to see output from this example"
#
# Code to test the MySink class
#
gst.info('About to create MySink')
pipeline = gst.parse_launch ("fakesrc ! mysink")
pipeline.set_state(gst.STATE_PLAYING)
gobject.MainLoop().run()

View file

@ -0,0 +1,68 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# sinkelement.py
# (c) 2005 Edward Hervey <edward@fluendo.com>
# Licensed under LGPL
#
# Small test application to show how to write a sink element
# in 20 lines in python
#
# Run this script with GST_DEBUG=python:5 to see the debug
# messages
import pygst
pygst.require('0.10')
import gst
import gobject
gobject.threads_init ()
#
# Simple Sink element created entirely in python
#
class MySink(gst.Element):
_sinkpadtemplate = gst.PadTemplate ("sinkpadtemplate",
gst.PAD_SINK,
gst.PAD_ALWAYS,
gst.caps_new_any())
def __init__(self):
gst.Element.__init__(self)
gst.info('creating sinkpad')
self.sinkpad = gst.Pad(self._sinkpadtemplate, "sink")
gst.info('adding sinkpad to self')
self.add_pad(self.sinkpad)
gst.info('setting chain/event functions')
self.sinkpad.set_chain_function(self.chainfunc)
self.sinkpad.set_event_function(self.eventfunc)
def chainfunc(self, pad, buffer):
self.info("%s timestamp(buffer):%d" % (pad, buffer.timestamp))
return gst.FLOW_OK
def eventfunc(self, pad, event):
self.info("%s event:%r" % (pad, event.type))
return True
gobject.type_register(MySink)
#
# Code to test the MySink class
#
src = gst.element_factory_make('fakesrc')
gst.info('About to create MySink')
sink = MySink()
pipeline = gst.Pipeline()
pipeline.add(src, sink)
src.link(sink)
pipeline.set_state(gst.STATE_PLAYING)
gobject.MainLoop().run()

192
old_examples/switch.py Executable file
View file

@ -0,0 +1,192 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import pygtk
pygtk.require('2.0')
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
import gst.interfaces
import gtk
gtk.gdk.threads_init()
class SwitchTest:
def __init__(self, videowidget):
self.playing = False
pipestr = ('videotestsrc pattern=0 ! queue ! s.sink0'
' videotestsrc pattern=1 ! queue ! s.sink1'
' input-selector name=s ! autovideosink')
self.pipeline = gst.parse_launch(pipestr)
self.videowidget = videowidget
bus = self.pipeline.get_bus()
bus.enable_sync_message_emission()
bus.add_signal_watch()
bus.connect('sync-message::element', self.on_sync_message)
bus.connect('message', self.on_message)
def on_sync_message(self, bus, message):
if message.structure is None:
return
if message.structure.get_name() == 'prepare-xwindow-id':
# Sync with the X server before giving the X-id to the sink
gtk.gdk.threads_enter()
gtk.gdk.display_get_default().sync()
self.videowidget.set_sink(message.src)
message.src.set_property('force-aspect-ratio', True)
gtk.gdk.threads_leave()
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
if self.on_eos:
self.on_eos()
self.playing = False
elif t == gst.MESSAGE_EOS:
if self.on_eos:
self.on_eos()
self.playing = False
def play(self):
self.playing = True
gst.info("playing player")
self.pipeline.set_state(gst.STATE_PLAYING)
def stop(self):
self.pipeline.set_state(gst.STATE_NULL)
gst.info("stopped player")
self.playing = False
def get_state(self, timeout=1):
return self.pipeline.get_state(timeout=timeout)
def is_playing(self):
return self.playing
def switch(self, padname):
switch = self.pipeline.get_by_name('s')
stop_time = switch.emit('block')
newpad = switch.get_static_pad(padname)
start_time = newpad.get_property('running-time')
gst.warning('stop time = %d' % (stop_time,))
gst.warning('stop time = %s' % (gst.TIME_ARGS(stop_time),))
gst.warning('start time = %d' % (start_time,))
gst.warning('start time = %s' % (gst.TIME_ARGS(start_time),))
gst.warning('switching from %r to %r'
% (switch.get_property('active-pad'), padname))
switch.emit('switch', newpad, stop_time, start_time)
class VideoWidget(gtk.DrawingArea):
def __init__(self):
gtk.DrawingArea.__init__(self)
self.imagesink = None
self.unset_flags(gtk.DOUBLE_BUFFERED)
def do_expose_event(self, event):
if self.imagesink:
self.imagesink.expose()
return False
else:
return True
def set_sink(self, sink):
assert self.window.xid
self.imagesink = sink
self.imagesink.set_xwindow_id(self.window.xid)
class SwitchWindow(gtk.Window):
UPDATE_INTERVAL = 500
def __init__(self):
gtk.Window.__init__(self)
self.set_default_size(410, 325)
self.create_ui()
self.player = SwitchTest(self.videowidget)
self.populate_combobox()
self.update_id = -1
self.changed_id = -1
self.seek_timeout_id = -1
self.p_position = gst.CLOCK_TIME_NONE
self.p_duration = gst.CLOCK_TIME_NONE
def on_delete_event():
self.player.stop()
gtk.main_quit()
self.connect('delete-event', lambda *x: on_delete_event())
def load_file(self, location):
self.player.set_location(location)
def play(self):
self.player.play()
def populate_combobox(self):
switch = self.player.pipeline.get_by_name('s')
for i, pad in enumerate([p for p in switch.pads()
if p.get_direction() == gst.PAD_SINK]):
self.combobox.append_text(pad.get_name())
if switch.get_property('active-pad') == pad.get_name():
self.combobox.set_active(i)
if self.combobox.get_active() == -1:
self.combobox.set_active(0)
def combobox_changed(self):
model = self.combobox.get_model()
row = model[self.combobox.get_active()]
padname, = row
self.player.switch(padname)
def create_ui(self):
vbox = gtk.VBox()
self.add(vbox)
self.videowidget = VideoWidget()
vbox.pack_start(self.videowidget)
hbox = gtk.HBox()
vbox.pack_start(hbox, fill=False, expand=False)
self.combobox = combobox = gtk.combo_box_new_text()
combobox.show()
hbox.pack_start(combobox)
self.combobox.connect('changed',
lambda *x: self.combobox_changed())
self.videowidget.connect_after('realize',
lambda *x: self.play())
def main(args):
def usage():
sys.stderr.write("usage: %s\n" % args[0])
return 1
# Need to register our derived widget types for implicit event
# handlers to get called.
gobject.type_register(SwitchWindow)
gobject.type_register(VideoWidget)
if len(args) != 1:
return usage()
w = SwitchWindow()
w.show_all()
gtk.main()
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))

820
old_examples/synchronizer.py Executable file
View file

@ -0,0 +1,820 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import pygtk
pygtk.require('2.0')
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
import gst.interfaces
import gtk
gtk.gdk.threads_init()
class GstPlayer:
def __init__(self, videowidget):
self.playing = False
self.player = gst.element_factory_make("playbin", "player")
self.videowidget = videowidget
bus = self.player.get_bus()
bus.enable_sync_message_emission()
bus.add_signal_watch()
bus.connect('sync-message::element', self.on_sync_message)
bus.connect('message', self.on_message)
def on_sync_message(self, bus, message):
if message.structure is None:
return
if message.structure.get_name() == 'prepare-xwindow-id':
# Sync with the X server before giving the X-id to the sink
gtk.gdk.threads_enter()
gtk.gdk.display_get_default().sync()
self.videowidget.set_sink(message.src)
message.src.set_property('force-aspect-ratio', True)
gtk.gdk.threads_leave()
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
if self.on_eos:
self.on_eos()
self.playing = False
elif t == gst.MESSAGE_EOS:
if self.on_eos:
self.on_eos()
self.playing = False
def set_location(self, location):
self.player.set_state(gst.STATE_NULL)
self.player.set_property('uri', location)
def get_location(self):
return self.player.get_property('uri')
def query_position(self):
"Returns a (position, duration) tuple"
try:
position, format = self.player.query_position(gst.FORMAT_TIME)
except:
position = gst.CLOCK_TIME_NONE
try:
duration, format = self.player.query_duration(gst.FORMAT_TIME)
except:
duration = gst.CLOCK_TIME_NONE
return (position, duration)
def seek(self, location):
"""
@param location: time to seek to, in nanoseconds
"""
gst.debug("seeking to %r" % location)
event = gst.event_new_seek(1.0, gst.FORMAT_TIME,
gst.SEEK_FLAG_FLUSH,
gst.SEEK_TYPE_SET, location,
gst.SEEK_TYPE_NONE, 0)
res = self.player.send_event(event)
if res:
gst.info("setting new stream time to 0")
self.player.set_new_stream_time(0L)
else:
gst.error("seek to %r failed" % location)
def pause(self):
gst.info("pausing player")
self.player.set_state(gst.STATE_PAUSED)
self.playing = False
def play(self):
gst.info("playing player")
self.player.set_state(gst.STATE_PLAYING)
self.playing = True
def stop(self):
self.player.set_state(gst.STATE_NULL)
gst.info("stopped player")
def get_state(self, timeout=1):
return self.player.get_state(timeout=timeout)
def is_playing(self):
return self.playing
class VideoWidget(gtk.DrawingArea):
def __init__(self):
gtk.DrawingArea.__init__(self)
self.imagesink = None
self.unset_flags(gtk.DOUBLE_BUFFERED)
def do_expose_event(self, event):
if self.imagesink:
self.imagesink.expose()
return False
else:
return True
def set_sink(self, sink):
assert self.window.xid
self.imagesink = sink
self.imagesink.set_xwindow_id(self.window.xid)
class SyncPoints(gtk.VBox):
def __init__(self, window):
gtk.VBox.__init__(self)
self.pwindow = window
self.create_ui()
def get_time_as_str(self, iter, i):
value = self.model.get_value(iter, i)
ret = ''
for div, sep, mod, pad in ((gst.SECOND*60, '', 0, 0),
(gst.SECOND, ':', 60, 2),
(gst.MSECOND, '.', 1000, 3)):
n = value // div
if mod:
n %= mod
ret += sep + ('%%0%dd' % pad) % n
return ret
def create_ui(self):
self.model = model = gtk.ListStore(gobject.TYPE_UINT64,
gobject.TYPE_UINT64)
self.view = view = gtk.TreeView(self.model)
renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn("Audio time", renderer)
def time_to_text(column, cell, method, iter, i):
cell.set_property('text', self.get_time_as_str(iter, i))
column.set_cell_data_func(renderer, time_to_text, 0)
column.set_expand(True)
column.set_clickable(True)
view.append_column(column)
renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn("Video time", renderer)
column.set_cell_data_func(renderer, time_to_text, 1)
column.set_expand(True)
view.append_column(column)
view.show()
self.pack_start(view, True, True, 6)
hbox = gtk.HBox(False, 0)
hbox.show()
self.pack_start(hbox, False, False, 0)
add = gtk.Button(stock=gtk.STOCK_ADD)
add.show()
def add_and_select(*x):
iter = model.append()
self.view.get_selection().select_iter(iter)
self.changed()
add.connect("clicked", add_and_select)
hbox.pack_end(add, False, False, 0)
remove = gtk.Button(stock=gtk.STOCK_REMOVE)
remove.show()
def remove_selected(*x):
model, iter = self.view.get_selection().get_selected()
model.remove(iter)
self.changed()
remove.connect("clicked", remove_selected)
hbox.pack_end(remove, False, False, 0)
pad = gtk.Label(' ')
pad.show()
hbox.pack_end(pad)
label = gtk.Label("Set: ")
label.show()
hbox.pack_start(label)
a = gtk.Button("A_udio")
a.show()
a.connect("clicked", lambda *x: self.set_selected_audio_now())
hbox.pack_start(a)
l = gtk.Label(" / ")
l.show()
hbox.pack_start(l)
v = gtk.Button("_Video")
v.show()
v.connect("clicked", lambda *x: self.set_selected_video_now())
hbox.pack_start(v)
def get_sync_points(self):
def get_value(row, i):
return self.model.get_value(row.iter, i)
pairs = [(get_value(row, 1), get_value(row, 0)) for row in self.model]
pairs.sort()
ret = []
maxdiff = 0
for pair in pairs:
maxdiff = max(maxdiff, abs(pair[1] - pair[0]))
ret.extend(pair)
return ret, maxdiff
def changed(self):
print 'Sync times now:'
for index, row in enumerate(self.model):
print 'A/V %d: %s -- %s' % (index,
self.get_time_as_str(row.iter, 0),
self.get_time_as_str(row.iter, 1))
def set_selected_audio(self, time):
sel = self.view.get_selection()
model, iter = sel.get_selected()
if iter:
model.set_value(iter, 0, time)
self.changed()
def set_selected_video(self, time):
sel = self.view.get_selection()
model, iter = sel.get_selected()
if iter:
model.set_value(iter, 1, time)
self.changed()
def set_selected_audio_now(self):
time, dur = self.pwindow.player.query_position()
self.set_selected_audio(time)
def set_selected_video_now(self):
# pause and preroll first
if self.pwindow.player.is_playing():
self.pwindow.play_toggled()
self.pwindow.player.get_state(timeout=gst.MSECOND * 200)
time, dur = self.pwindow.player.query_position()
self.set_selected_video(time)
def seek_and_pause(self, time):
if self.pwindow.player.is_playing():
self.pwindow.play_toggled()
self.pwindow.player.seek(time)
if self.pwindow.player.is_playing():
self.pwindow.play_toggled()
self.pwindow.player.get_state(timeout=gst.MSECOND * 200)
class ProgressDialog(gtk.Dialog):
def __init__(self, title, description, task, parent, flags, buttons):
gtk.Dialog.__init__(self, title, parent, flags, buttons)
self._create_ui(title, description, task)
def _create_ui(self, title, description, task):
self.set_border_width(6)
self.set_resizable(False)
self.set_has_separator(False)
vbox = gtk.VBox()
vbox.set_border_width(6)
vbox.show()
self.vbox.pack_start(vbox, False)
label = gtk.Label('<big><b>%s</b></big>' % title)
label.set_use_markup(True)
label.set_alignment(0.0, 0.0)
label.show()
vbox.pack_start(label, False)
label = gtk.Label(description)
label.set_use_markup(True)
label.set_alignment(0.0, 0.0)
label.set_line_wrap(True)
label.set_padding(0, 12)
label.show()
vbox.pack_start(label, False)
self.progress = progress = gtk.ProgressBar()
progress.show()
vbox.pack_start(progress, False)
self.progresstext = label = gtk.Label('')
label.set_line_wrap(True)
label.set_use_markup(True)
label.set_alignment(0.0, 0.0)
label.show()
vbox.pack_start(label)
self.set_task(task)
def set_task(self, task):
self.progresstext.set_markup('<i>%s</i>' % task)
UNKNOWN = 0
SUCCESS = 1
FAILURE = 2
CANCELLED = 3
class RemuxProgressDialog(ProgressDialog):
def __init__(self, parent, fromname, toname):
ProgressDialog.__init__(self,
"Writing to disk",
('Writing the newly synchronized <b>%s</b> '
'to <b>%s</b>. This may take some time.'
% (fromname, toname)),
'Starting media pipeline',
parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, CANCELLED,
gtk.STOCK_CLOSE, SUCCESS))
self.set_completed(False)
def update_position(self, pos, dur):
remaining = dur - pos
minutes = remaining // (gst.SECOND * 60)
seconds = (remaining - minutes * gst.SECOND * 60) // gst.SECOND
self.progress.set_text('%d:%02d of video remaining' % (minutes, seconds))
self.progress.set_fraction(1.0 - float(remaining) / dur)
def set_completed(self, completed):
self.set_response_sensitive(CANCELLED, not completed)
self.set_response_sensitive(SUCCESS, completed)
class Resynchronizer(gst.Pipeline):
__gsignals__ = {'done': (gobject.SIGNAL_RUN_LAST, None, (int,))}
def __init__(self, fromuri, touri, (syncpoints, maxdiff)):
# HACK: should do Pipeline.__init__, but that doesn't do what we
# want; there's a bug open aboooot that
self.__gobject_init__()
self.fromuri = fromuri
self.touri = None
self.syncpoints = syncpoints
self.maxdiff = maxdiff
self.src = self.resyncbin = self.sink = None
self.resolution = UNKNOWN
self.window = None
self.pdialog = None
self._query_id = -1
def do_setup_pipeline(self):
self.src = gst.element_make_from_uri(gst.URI_SRC, self.fromuri)
self.resyncbin = ResyncBin(self.syncpoints, self.maxdiff)
self.sink = gst.element_make_from_uri(gst.URI_SINK, self.touri)
self.resolution = UNKNOWN
if gobject.signal_lookup('allow-overwrite', self.sink.__class__):
self.sink.connect('allow-overwrite', lambda *x: True)
self.add(self.src, self.resyncbin, self.sink)
self.src.link(self.resyncbin)
self.resyncbin.link(self.sink)
def do_get_touri(self):
chooser = gtk.FileChooserDialog('Save as...',
self.window,
action=gtk.FILE_CHOOSER_ACTION_SAVE,
buttons=(gtk.STOCK_CANCEL,
CANCELLED,
gtk.STOCK_SAVE,
SUCCESS))
chooser.set_uri(self.fromuri) # to select the folder
chooser.unselect_all()
chooser.set_do_overwrite_confirmation(True)
name = self.fromuri.split('/')[-1][:-4] + '-remuxed.ogg'
chooser.set_current_name(name)
resp = chooser.run()
uri = chooser.get_uri()
chooser.destroy()
if resp == SUCCESS:
return uri
else:
return None
def _start_queries(self):
def do_query():
try:
# HACK: self.remuxbin.query() should do the same
# (requires implementing a vmethod, dunno how to do that
# although i think it's possible)
# HACK: why does self.query_position(..) not give useful
# answers?
pad = self.resyncbin.get_pad('src')
pos, format = pad.query_position(gst.FORMAT_TIME)
dur, format = pad.query_duration(gst.FORMAT_TIME)
if pos != gst.CLOCK_TIME_NONE:
self.pdialog.update_position(pos, duration)
except:
# print 'query failed'
pass
return True
if self._query_id == -1:
self._query_id = gobject.timeout_add(100, # 10 Hz
do_query)
def _stop_queries(self):
if self._query_id != -1:
gobject.source_remove(self._query_id)
self._query_id = -1
def _bus_watch(self, bus, message):
if message.type == gst.MESSAGE_ERROR:
print 'error', message
self._stop_queries()
m = gtk.MessageDialog(self.window,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR,
gtk.BUTTONS_CLOSE,
"Error processing file")
gerror, debug = message.parse_error()
txt = ('There was an error processing your file: %s\n\n'
'Debug information:\n%s' % (gerror, debug))
m.format_secondary_text(txt)
m.run()
m.destroy()
self.response(FAILURE)
elif message.type == gst.MESSAGE_WARNING:
print 'warning', message
elif message.type == gst.MESSAGE_EOS:
# print 'eos, woot', message.src
name = self.touri
if name.startswith('file://'):
name = name[7:]
self.pdialog.set_task('Finished writing %s' % name)
self.pdialog.update_position(1,1)
self._stop_queries()
self.pdialog.set_completed(True)
elif message.type == gst.MESSAGE_STATE_CHANGED:
if message.src == self:
old, new, pending = message.parse_state_changed()
if ((old, new, pending) ==
(gst.STATE_READY, gst.STATE_PAUSED,
gst.STATE_VOID_PENDING)):
self.pdialog.set_task('Processing file')
self._start_queries()
self.set_state(gst.STATE_PLAYING)
def response(self, response):
assert self.resolution == UNKNOWN
self.resolution = response
self.set_state(gst.STATE_NULL)
self.pdialog.destroy()
self.pdialog = None
self.window.set_sensitive(True)
self.emit('done', response)
def start(self, main_window):
self.window = main_window
self.touri = self.do_get_touri()
if not self.touri:
return False
self.do_setup_pipeline()
bus = self.get_bus()
bus.add_signal_watch()
bus.connect('message', self._bus_watch)
if self.window:
# can be None if we are debugging...
self.window.set_sensitive(False)
fromname = self.fromuri.split('/')[-1]
toname = self.touri.split('/')[-1]
self.pdialog = RemuxProgressDialog(main_window, fromname, toname)
self.pdialog.show()
self.pdialog.connect('response', lambda w, r: self.response(r))
self.set_state(gst.STATE_PAUSED)
return True
def run(self, main_window):
if self.start(main_window):
loop = gobject.MainLoop()
self.connect('done', lambda *x: gobject.idle_add(loop.quit))
loop.run()
else:
self.resolution = CANCELLED
return self.resolution
class ResyncBin(gst.Bin):
def __init__(self, sync_points, maxdiff):
self.__gobject_init__()
self.parsefactories = self._find_parsers()
self.parsers = []
self.demux = gst.element_factory_make('oggdemux')
self.mux = gst.element_factory_make('oggmux')
self.add(self.demux, self.mux)
self.add_pad(gst.GhostPad('sink', self.demux.get_pad('sink')))
self.add_pad(gst.GhostPad('src', self.mux.get_pad('src')))
self.demux.connect('pad-added', self._new_demuxed_pad)
self.sync_points = sync_points
self.maxdiff = maxdiff
def _find_parsers(self):
registry = gst.registry_get_default()
ret = {}
for f in registry.get_feature_list(gst.ElementFactory):
if f.get_klass().find('Parser') >= 0:
for t in f.get_static_pad_templates():
if t.direction == gst.PAD_SINK:
for s in t.get_caps():
ret[s.get_name()] = f.get_name()
break
return ret
def _new_demuxed_pad(self, element, pad):
format = pad.get_caps()[0].get_name()
if format not in self.parsefactories:
self.async_error("Unsupported media type: %s", format)
return
queue = gst.element_factory_make('queue', 'queue_' + format)
queue.set_property('max-size-buffers', 0)
queue.set_property('max-size-bytes', 0)
print self.maxdiff
queue.set_property('max-size-time', int(self.maxdiff * 1.5))
parser = gst.element_factory_make(self.parsefactories[format])
self.add(queue)
self.add(parser)
queue.set_state(gst.STATE_PAUSED)
parser.set_state(gst.STATE_PAUSED)
pad.link(queue.get_compatible_pad(pad))
queue.link(parser)
parser.link(self.mux)
self.parsers.append(parser)
print repr(self.sync_points)
if 'video' in format:
parser.set_property('synchronization-points',
self.sync_points)
class PlayerWindow(gtk.Window):
UPDATE_INTERVAL = 500
def __init__(self):
gtk.Window.__init__(self)
self.set_default_size(600, 500)
self.create_ui()
self.player = GstPlayer(self.videowidget)
def on_eos():
self.player.seek(0L)
self.play_toggled()
self.player.on_eos = lambda *x: on_eos()
self.update_id = -1
self.changed_id = -1
self.seek_timeout_id = -1
self.p_position = gst.CLOCK_TIME_NONE
self.p_duration = gst.CLOCK_TIME_NONE
def on_delete_event():
self.player.stop()
gtk.main_quit()
self.connect('delete-event', lambda *x: on_delete_event())
def load_file(self, location):
filename = location.split('/')[-1]
self.set_title('%s munger' % filename)
self.player.set_location(location)
if self.videowidget.flags() & gtk.REALIZED:
self.play_toggled()
else:
self.videowidget.connect_after('realize',
lambda *x: self.play_toggled())
def create_ui(self):
vbox = gtk.VBox()
vbox.show()
self.add(vbox)
self.videowidget = VideoWidget()
self.videowidget.show()
vbox.pack_start(self.videowidget)
hbox = gtk.HBox()
hbox.show()
vbox.pack_start(hbox, fill=False, expand=False)
self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0)
hscale = gtk.HScale(self.adjustment)
hscale.set_digits(2)
hscale.set_update_policy(gtk.UPDATE_CONTINUOUS)
hscale.connect('button-press-event', self.scale_button_press_cb)
hscale.connect('button-release-event', self.scale_button_release_cb)
hscale.connect('format-value', self.scale_format_value_cb)
hbox.pack_start(hscale)
hscale.show()
self.hscale = hscale
table = gtk.Table(3,3)
table.show()
vbox.pack_start(table, fill=False, expand=False, padding=6)
self.button = button = gtk.Button(stock=gtk.STOCK_MEDIA_PLAY)
button.set_property('can-default', True)
button.set_focus_on_click(False)
button.show()
# problem: play and paused are of different widths and cause the
# window to re-layout
# "solution": add more buttons to a vbox so that the horizontal
# width is enough
bvbox = gtk.VBox()
bvbox.add(button)
bvbox.add(gtk.Button(stock=gtk.STOCK_MEDIA_PLAY))
bvbox.add(gtk.Button(stock=gtk.STOCK_MEDIA_PAUSE))
sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
for kid in bvbox.get_children():
sizegroup.add_widget(kid)
bvbox.show()
table.attach(bvbox, 0, 1, 1, 3, gtk.FILL, gtk.FILL)
# can't set this property before the button has a window
button.set_property('has-default', True)
button.connect('clicked', lambda *args: self.play_toggled())
self.sync = sync = SyncPoints(self)
sync.show()
table.attach(sync, 1, 2, 0, 3, gtk.EXPAND, gtk.EXPAND|gtk.FILL, 12)
# nasty things to get sizes
l = gtk.Label('\n\n\n')
l.show()
table.attach(l, 0, 1, 0, 1, 0, 0, 0)
l = gtk.Label('\n\n\n')
l.show()
table.attach(l, 2, 3, 0, 1, 0, 0, 0)
button = gtk.Button("_Open other movie...")
button.show()
button.connect('clicked', lambda *x: self.do_choose_file())
table.attach(button, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
button = gtk.Button("_Write to disk")
button.set_property('image',
gtk.image_new_from_stock(gtk.STOCK_SAVE_AS,
gtk.ICON_SIZE_BUTTON))
button.connect('clicked', lambda *x: self.do_remux())
button.show()
table.attach(button, 2, 3, 2, 3, gtk.FILL, gtk.FILL)
def do_remux(self):
if self.player.is_playing():
self.play_toggled()
in_uri = self.player.get_location()
out_uri = in_uri[:-4] + '-remuxed.ogg'
r = Resynchronizer(in_uri, out_uri, self.sync.get_sync_points())
r.run(self)
def do_choose_file(self):
if self.player.is_playing():
self.play_toggled()
chooser = gtk.FileChooserDialog('Choose a movie to bork bork bork',
self,
buttons=(gtk.STOCK_CANCEL,
CANCELLED,
gtk.STOCK_OPEN,
SUCCESS))
chooser.set_local_only(False)
chooser.set_select_multiple(False)
f = gtk.FileFilter()
f.set_name("All files")
f.add_pattern("*")
chooser.add_filter(f)
f = gtk.FileFilter()
f.set_name("Ogg files")
f.add_pattern("*.ogg") # as long as this is the only thing we
# support...
chooser.add_filter(f)
chooser.set_filter(f)
prev = self.player.get_location()
if prev:
chooser.set_uri(prev)
resp = chooser.run()
uri = chooser.get_uri()
chooser.destroy()
if resp == SUCCESS:
self.load_file(uri)
return True
else:
return False
def play_toggled(self):
if self.player.is_playing():
self.player.pause()
self.button.set_label(gtk.STOCK_MEDIA_PLAY)
else:
self.player.play()
if self.update_id == -1:
self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
self.update_scale_cb)
self.button.set_label(gtk.STOCK_MEDIA_PAUSE)
def scale_format_value_cb(self, scale, value):
if self.p_duration == -1:
real = 0
else:
real = value * self.p_duration / 100
seconds = real / gst.SECOND
return "%02d:%02d" % (seconds / 60, seconds % 60)
def scale_button_press_cb(self, widget, event):
# see seek.c:start_seek
gst.debug('starting seek')
self.button.set_sensitive(False)
self.was_playing = self.player.is_playing()
if self.was_playing:
self.player.pause()
# don't timeout-update position during seek
if self.update_id != -1:
gobject.source_remove(self.update_id)
self.update_id = -1
# make sure we get changed notifies
if self.changed_id == -1:
self.changed_id = self.hscale.connect('value-changed',
self.scale_value_changed_cb)
def scale_value_changed_cb(self, scale):
# see seek.c:seek_cb
real = long(scale.get_value() * self.p_duration / 100) # in ns
gst.debug('value changed, perform seek to %r' % real)
self.player.seek(real)
# allow for a preroll
self.player.get_state(timeout=50*gst.MSECOND) # 50 ms
def scale_button_release_cb(self, widget, event):
# see seek.cstop_seek
widget.disconnect(self.changed_id)
self.changed_id = -1
self.button.set_sensitive(True)
if self.seek_timeout_id != -1:
gobject.source_remove(self.seek_timeout_id)
self.seek_timeout_id = -1
else:
gst.debug('released slider, setting back to playing')
if self.was_playing:
self.player.play()
if self.update_id != -1:
self.error('Had a previous update timeout id')
else:
self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
self.update_scale_cb)
def update_scale_cb(self):
had_duration = self.p_duration != gst.CLOCK_TIME_NONE
self.p_position, self.p_duration = self.player.query_position()
if self.p_position != gst.CLOCK_TIME_NONE:
value = self.p_position * 100.0 / self.p_duration
self.adjustment.set_value(value)
return True
def main(args):
def usage():
sys.stderr.write("usage: %s [URI-OF-MEDIA-FILE]\n" % args[0])
return 1
w = PlayerWindow()
w.show()
if len(args) == 1:
if not w.do_choose_file():
return 1
elif len(args) == 2:
if not gst.uri_is_valid(args[1]):
sys.stderr.write("Error: Invalid URI: %s\n" % args[1])
return 1
w.load_file(args[1])
else:
return usage()
gtk.main()
if __name__ == '__main__':
sys.exit(main(sys.argv))

77
old_examples/tagsetter.py Executable file
View file

@ -0,0 +1,77 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2009 Stefan Kost <ensonic@user.sf.net>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
mainloop = gobject.MainLoop()
def on_eos(bus, msg):
mainloop.quit()
def main(args):
"Tagsetter test, test result with:"
"gst-launch -t playbin uri=file://$PWD/test.avi"
# create a new bin to hold the elements
bin = gst.parse_launch('audiotestsrc num-buffers=100 ! ' +
'lame ! ' +
'avimux name=mux ! ' +
'filesink location=test.avi')
mux = bin.get_by_name('mux')
bus = bin.get_bus()
bus.add_signal_watch()
bus.connect('message::eos', on_eos)
# prepare
bin.set_state(gst.STATE_READY)
# send tags
l = gst.TagList()
l[gst.TAG_ARTIST] = "Unknown Genius"
l[gst.TAG_TITLE] = "Unnamed Artwork"
mux.merge_tags(l, gst.TAG_MERGE_APPEND)
# start playing
bin.set_state(gst.STATE_PLAYING)
try:
mainloop.run()
except KeyboardInterrupt:
pass
# stop the bin
bin.set_state(gst.STATE_NULL)
if __name__ == '__main__':
sys.exit(main(sys.argv))

View file

@ -0,0 +1,44 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# videomixer-controller.py
# (c) 2008 Stefan Kost <ensonic@users.sf.net>
# Test case for the GstController using videomixer and videotestsrc
import pygst
pygst.require('0.10')
import gst
import time
def main():
pipeline = gst.Pipeline("videocontroller")
src = gst.element_factory_make("videotestsrc", "src")
mix = gst.element_factory_make("videomixer", "mix")
conv = gst.element_factory_make("ffmpegcolorspace", "conv")
sink = gst.element_factory_make("autovideosink", "sink")
pipeline.add(src, mix, conv, sink)
spad = src.get_static_pad('src')
dpad = mix.get_request_pad('sink_%d')
spad.link(dpad)
mix.link(conv)
conv.link(sink)
control = gst.Controller(dpad, "xpos", "ypos")
control.set_interpolation_mode("xpos", gst.INTERPOLATE_LINEAR)
control.set_interpolation_mode("ypos", gst.INTERPOLATE_LINEAR)
control.set("xpos", 0, 0)
control.set("xpos", 5 * gst.SECOND, 200)
control.set("ypos", 0, 0)
control.set("ypos", 5 * gst.SECOND, 200)
pipeline.set_state(gst.STATE_PLAYING)
time.sleep(7)
if __name__ == "__main__":
main()

108
old_examples/vumeter.py Executable file
View file

@ -0,0 +1,108 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2005 Andy Wingo <wingo@pobox.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# A test more of gst-plugins than of gst-python.
import pygtk
pygtk.require('2.0')
import gtk
import gobject
import pygst
pygst.require('0.10')
import gst
import fvumeter
def clamp(x, min, max):
if x < min:
return min
elif x > max:
return max
return x
class Window(gtk.Dialog):
def __init__(self):
gtk.Dialog.__init__(self, 'Volume Level')
self.prepare_ui()
def prepare_ui(self):
self.set_default_size(200,60)
self.set_title('Volume Level')
self.connect('delete-event', lambda *x: gtk.main_quit())
self.vus = []
self.vus.append(fvumeter.FVUMeter())
self.vus.append(fvumeter.FVUMeter())
self.vbox.add(self.vus[0])
self.vbox.add(self.vus[1])
self.vus[0].show()
self.vus[1].show()
def error(self, message, secondary=None):
m = gtk.MessageDialog(self,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR,
gtk.BUTTONS_OK,
message)
if secondary:
m.format_secondary_text(secondary)
m.run()
def on_message(self, bus, message):
if message.structure.get_name() == 'level':
s = message.structure
for i in range(0, len(s['peak'])):
self.vus[i].freeze_notify()
decay = clamp(s['decay'][i], -90.0, 0.0)
peak = clamp(s['peak'][i], -90.0, 0.0)
if peak > decay:
print "ERROR: peak bigger than decay!"
self.vus[i].set_property('decay', decay)
self.vus[i].set_property('peak', peak)
return True
def run(self):
try:
self.set_sensitive(False)
s = 'alsasrc ! level message=true ! fakesink'
pipeline = gst.parse_launch(s)
self.set_sensitive(True)
pipeline.get_bus().add_signal_watch()
i = pipeline.get_bus().connect('message::element', self.on_message)
pipeline.set_state(gst.STATE_PLAYING)
gtk.Dialog.run(self)
pipeline.get_bus().disconnect(i)
pipeline.get_bus().remove_signal_watch()
pipeline.set_state(gst.STATE_NULL)
except gobject.GError, e:
self.set_sensitive(True)
self.error('Could not create pipeline', e.__str__)
if __name__ == '__main__':
w = Window()
w.show_all()
w.run()

345
plugin/gstpythonplugin.c Normal file
View file

@ -0,0 +1,345 @@
/* gst-python
* Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
* 2005 Benjamin Otte <otte@gnome.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* include this first, before NO_IMPORT_PYGOBJECT is defined */
#include <pygobject.h>
#include <gst/gst.h>
#include <gmodule.h>
#include <Python.h>
void *_PyGstElement_Type;
GST_DEBUG_CATEGORY_STATIC (pyplugindebug);
#define GST_CAT_DEFAULT pyplugindebug
#define GST_ORIGIN "http://gstreamer.freedesktop.org"
static gboolean
gst_python_plugin_load_file (GstPlugin * plugin, const char *name)
{
PyObject *main_module, *main_locals;
PyObject *elementfactory;
PyObject *module;
const gchar *facname;
guint rank;
PyObject *class;
GST_DEBUG ("loading plugin %s", name);
main_module = PyImport_AddModule ("__main__");
if (main_module == NULL) {
GST_WARNING ("Could not get __main__, ignoring plugin %s", name);
PyErr_Print ();
PyErr_Clear ();
return FALSE;
}
main_locals = PyModule_GetDict (main_module);
module =
PyImport_ImportModuleEx ((char *) name, main_locals, main_locals, NULL);
if (!module) {
GST_DEBUG ("Could not load module, ignoring plugin %s", name);
PyErr_Print ();
PyErr_Clear ();
return FALSE;
}
/* Get __gstelementfactory__ from file */
elementfactory = PyObject_GetAttrString (module, "__gstelementfactory__");
if (!elementfactory) {
GST_DEBUG ("python file doesn't contain __gstelementfactory__");
PyErr_Clear ();
return FALSE;
}
/* parse tuple : name, rank, gst.ElementClass */
if (!PyArg_ParseTuple (elementfactory, "sIO", &facname, &rank, &class)) {
GST_WARNING ("__gstelementfactory__ isn't correctly formatted");
PyErr_Print ();
PyErr_Clear ();
Py_DECREF (elementfactory);
return FALSE;
}
if (!PyObject_IsSubclass (class, (PyObject *) & PyGObject_Type)) {
GST_WARNING ("the class provided isn't a subclass of GObject.Object");
PyErr_Print ();
PyErr_Clear ();
Py_DECREF (elementfactory);
Py_DECREF (class);
return FALSE;
}
if (!g_type_is_a (pyg_type_from_object (class), GST_TYPE_ELEMENT)) {
GST_WARNING ("the class provided isn't a subclass of Gst.Element");
PyErr_Print ();
PyErr_Clear ();
Py_DECREF (elementfactory);
Py_DECREF (class);
return FALSE;
}
GST_INFO ("Valid plugin");
Py_DECREF (elementfactory);
return gst_element_register (plugin, facname, rank,
pyg_type_from_object (class));
}
static gboolean
gst_python_load_directory (GstPlugin * plugin, const gchar * path)
{
GDir *dir;
const gchar *file;
GError *error = NULL;
gboolean ret = TRUE;
dir = g_dir_open (path, 0, &error);
if (!dir) {
/*retval should probably be depending on error, but since we ignore it... */
GST_DEBUG ("Couldn't open Python plugin dir: %s", error->message);
g_error_free (error);
return FALSE;
}
while ((file = g_dir_read_name (dir))) {
/* FIXME : go down in subdirectories */
if (g_str_has_suffix (file, ".py")) {
gsize len = strlen (file) - 3;
gchar *name = g_strndup (file, len);
ret &= gst_python_plugin_load_file (plugin, name);
g_free (name);
}
}
return TRUE;
}
static gboolean
gst_python_plugin_load (GstPlugin * plugin)
{
PyObject *sys_path;
const gchar *plugin_path;
gboolean ret = TRUE;
sys_path = PySys_GetObject ("path");
/* Mimic the order in which the registry is checked in core */
/* 1. check env_variable GST_PLUGIN_PATH */
plugin_path = g_getenv ("GST_PLUGIN_PATH_1_0");
if (plugin_path == NULL)
plugin_path = g_getenv ("GST_PLUGIN_PATH");
if (plugin_path) {
char **list;
int i;
GST_DEBUG ("GST_PLUGIN_PATH set to %s", plugin_path);
list = g_strsplit (plugin_path, G_SEARCHPATH_SEPARATOR_S, 0);
for (i = 0; list[i]; i++) {
gchar *sysdir = g_build_filename (list[i], "python", NULL);
PyList_Insert (sys_path, 0, PyUnicode_FromString (sysdir));
gst_python_load_directory (plugin, sysdir);
g_free (sysdir);
}
g_strfreev (list);
}
/* 2. Check for GST_PLUGIN_SYSTEM_PATH */
plugin_path = g_getenv ("GST_PLUGIN_SYSTEM_PATH_1_0");
if (plugin_path == NULL)
plugin_path = g_getenv ("GST_PLUGIN_SYSTEM_PATH");
if (plugin_path == NULL) {
char *home_plugins;
/* 2.a. Scan user and system-wide plugin directory */
GST_DEBUG ("GST_PLUGIN_SYSTEM_PATH not set");
/* plugins in the user's home directory take precedence over
* system-installed ones */
home_plugins = g_build_filename (g_get_home_dir (),
".gstreamer-" GST_API_VERSION, "plugins", "python", NULL);
PyList_Insert (sys_path, 0, PyUnicode_FromString (home_plugins));
gst_python_load_directory (plugin, home_plugins);
g_free (home_plugins);
/* add the main (installed) library path */
PyList_Insert (sys_path, 0, PyUnicode_FromString (PLUGINDIR "/python"));
gst_python_load_directory (plugin, PLUGINDIR "/python");
} else {
gchar **list;
gint i;
/* 2.b. Scan GST_PLUGIN_SYSTEM_PATH */
GST_DEBUG ("GST_PLUGIN_SYSTEM_PATH set to %s", plugin_path);
list = g_strsplit (plugin_path, G_SEARCHPATH_SEPARATOR_S, 0);
for (i = 0; list[i]; i++) {
gchar *sysdir;
sysdir = g_build_filename (list[i], "python", NULL);
PyList_Insert (sys_path, 0, PyUnicode_FromString (sysdir));
gst_python_load_directory (plugin, sysdir);
g_free (sysdir);
}
g_strfreev (list);
}
return ret;
}
static gboolean
plugin_init (GstPlugin * plugin)
{
PyGILState_STATE state = 0;
PyObject *gi, *require_version, *args, *gst, *dict, *pyplugin;
gboolean we_initialized = FALSE;
GModule *libpython;
gpointer has_python = NULL;
const gchar *override_path;
GST_DEBUG_CATEGORY_INIT (pyplugindebug, "pyplugin", 0,
"Python plugin loader");
gst_plugin_add_dependency_simple (plugin,
"HOME/.gstreamer-" GST_API_VERSION
"/plugins/python:GST_PLUGIN_SYSTEM_PATH/python:GST_PLUGIN_PATH/python",
PLUGINDIR "/python:HOME/.gstreamer-" GST_API_VERSION "/plugins/python:"
"GST_PLUGIN_SYSTEM_PATH/python:GST_PLUGIN_PATH/python", NULL,
GST_PLUGIN_DEPENDENCY_FLAG_NONE);
GST_LOG ("Checking to see if libpython is already loaded");
if (g_module_symbol (g_module_open (NULL, G_MODULE_BIND_LOCAL),
"_Py_NoneStruct", &has_python) && has_python) {
GST_LOG ("libpython is already loaded");
} else {
const gchar *libpython_path =
PY_LIB_LOC "/libpython" PYTHON_VERSION PY_ABI_FLAGS "." PY_LIB_SUFFIX;
GST_LOG ("loading libpython from '%s'", libpython_path);
libpython = g_module_open (libpython_path, 0);
if (!libpython) {
g_critical ("Couldn't g_module_open libpython. Reason: %s",
g_module_error ());
return FALSE;
}
}
if (!Py_IsInitialized ()) {
GST_LOG ("python wasn't initialized");
/* set the correct plugin for registering stuff */
Py_Initialize ();
we_initialized = TRUE;
} else {
GST_LOG ("python was already initialized");
state = PyGILState_Ensure ();
}
if ((override_path = g_getenv ("GST_OVERRIDE_SRC_PATH"))) {
gchar *overrides_setup =
g_build_filename (override_path, "..", "..", "testsuite",
"overrides_hack.py", NULL);
FILE *fd = fopen (overrides_setup, "rb");
if (!fd || PyRun_SimpleFileExFlags (fd, overrides_setup, 1, 0)) {
g_free (overrides_setup);
return FALSE;
} else {
g_free (overrides_setup);
GST_INFO ("Imported overrides setup");
}
}
GST_LOG ("Running with python version '%s'", Py_GetVersion ());
GST_LOG ("initializing pygobject");
if (!pygobject_init (3, 0, 0)) {
g_critical ("pygobject initialization failed");
return FALSE;
}
gi = PyImport_ImportModule ("gi");
if (!gi) {
g_critical ("can't find gi");
return FALSE;
}
require_version = PyObject_GetAttrString (gi, (char *) "require_version");
args = PyTuple_Pack (2, PyUnicode_FromString ("Gst"),
PyUnicode_FromString ("1.0"));
PyObject_CallObject (require_version, args);
Py_DECREF (require_version);
Py_DECREF (args);
Py_DECREF (gi);
gst = PyImport_ImportModule ("gi.repository.Gst");
if (!gst) {
g_critical ("can't find gi.repository.Gst");
return FALSE;
}
if (we_initialized) {
PyObject *tmp;
dict = PyModule_GetDict (gst);
if (!dict) {
g_critical ("gi.repository.Gst is no dict");
return FALSE;
}
tmp =
PyObject_GetAttr (PyMapping_GetItemString (dict,
"_introspection_module"), PyUnicode_FromString ("__dict__"));
_PyGstElement_Type = PyMapping_GetItemString (tmp, "Element");
if (!_PyGstElement_Type) {
g_critical ("Could not get Gst.Element");
return FALSE;
}
pyplugin = pygobject_new (G_OBJECT (plugin));
if (!pyplugin || PyModule_AddObject (gst, "__plugin__", pyplugin) != 0) {
g_critical ("Couldn't set __plugin__ attribute");
if (pyplugin)
Py_DECREF (pyplugin);
return FALSE;
}
}
gst_python_plugin_load (plugin);
if (we_initialized) {
/* We need to release the GIL since we're going back to C land */
PyEval_SaveThread ();
} else
PyGILState_Release (state);
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR, python,
"loader for plugins written in python",
plugin_init, VERSION, "LGPL", PACKAGE_NAME, GST_ORIGIN)

9
plugin/meson.build Normal file
View file

@ -0,0 +1,9 @@
gstpython = library('gstpython',
['gstpythonplugin.c'],
include_directories : [configinc],
dependencies : [gst_dep, pygobject_dep, gstbase_dep, python_dep, gmodule_dep],
install : true,
install_dir : '@0@/gstreamer-1.0'.format(get_option('libdir')),
)
pkgconfig.generate(gstpython, install_dir : plugins_pkgconfig_install_dir)
plugins = [gstpython]

0
testsuite/__init__.py Normal file
View file

139
testsuite/common.py Normal file
View file

@ -0,0 +1,139 @@
# -*- Mode: Python; py-indent-offset: 4 -*-
# vim: tabstop=4 shiftwidth=4 expandtab
#
# Copyright (C) 2015 Thibault Saunier <thibault.saunier@collabora.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
"""
A collection of objects to use for testing
Copyied from pitivi
"""
import os
import gc
import unittest
import gi.overrides
import gi
gi.require_version("Gst", "1.0")
from gi.repository import Gst
detect_leaks = os.environ.get("TEST_DETECT_LEAKS", "1") not in ("0", "")
class TestCase(unittest.TestCase):
_tracked_types = (Gst.MiniObject, Gst.Element, Gst.Pad, Gst.Caps)
def gctrack(self):
self.gccollect()
self._tracked = []
for obj in gc.get_objects():
if not isinstance(obj, self._tracked_types):
continue
self._tracked.append(obj)
def gccollect(self):
ret = 0
while True:
c = gc.collect()
ret += c
if c == 0:
break
return ret
def gcverify(self):
leaked = []
for obj in gc.get_objects():
if not isinstance(obj, self._tracked_types) or \
obj in self._tracked:
continue
leaked.append(obj)
# we collect again here to get rid of temporary objects created in the
# above loop
self.gccollect()
for elt in leaked:
print(elt)
for i in gc.get_referrers(elt):
print(" ", i)
self.assertFalse(leaked, leaked)
del self._tracked
def setUp(self):
self._num_failures = len(getattr(self._result, 'failures', []))
self._num_errors = len(getattr(self._result, 'errors', []))
if detect_leaks:
self.gctrack()
def tearDown(self):
# don't barf gc info all over the console if we have already failed a
# test case
if (self._num_failures < len(getattr(self._result, 'failures', []))
or self._num_errors < len(getattr(self._result, 'failures', []))):
return
if detect_leaks:
self.gccollect()
self.gcverify()
# override run() to save a reference to the test result object
def run(self, result=None):
if not result:
result = self.defaultTestResult()
self._result = result
unittest.TestCase.run(self, result)
class SignalMonitor(object):
def __init__(self, obj, *signals):
self.signals = signals
self.connectToObj(obj)
def connectToObj(self, obj):
self.obj = obj
for signal in self.signals:
obj.connect(signal, self._signalCb, signal)
setattr(self, self._getSignalCounterName(signal), 0)
setattr(self, self._getSignalCollectName(signal), [])
def disconnectFromObj(self, obj):
obj.disconnect_by_func(self._signalCb)
del self.obj
def _getSignalCounterName(self, signal):
field = '%s_count' % signal.replace('-', '_')
return field
def _getSignalCollectName(self, signal):
field = '%s_collect' % signal.replace('-', '_')
return field
def _signalCb(self, obj, *args):
name = args[-1]
field = self._getSignalCounterName(name)
setattr(self, field, getattr(self, field, 0) + 1)
field = self._getSignalCollectName(name)
setattr(self, field, getattr(self, field, []) + [args[:-1]])

241
testsuite/gstpython.supp Normal file
View file

@ -0,0 +1,241 @@
{
pthread leak
Memcheck:Leak
fun:calloc
fun:allocate_dtv
fun:_dl_allocate_tls*
}
{
pthread leak 2
Memcheck:Leak
fun:memalign
fun:_dl_allocate_tls*
}
{
popt leak
Memcheck:Leak
fun:malloc
fun:nss_parse_service_list
fun:__nss_database_lookup
obj:*
obj:*
fun:getpwuid_r@@GLIBC_2.2.5
fun:g_get_any_init_do
fun:g_get_home_dir
fun:init_post
fun:init_popt_callback
}
{
pygobject init leak
Memcheck:Leak
fun:calloc
fun:g_malloc0
fun:type_node_*
fun:type_node_*
fun:*
fun:*
fun:g_type_init*
fun:initgobject
}
{
borked pthread creation
Memcheck:Param
write(buf)
fun:__pthread_initialize_manager
fun:pthread_create@@GLIBC_2.2.5
fun:g_thread_create*
fun:g_thread_create*
}
{
borked pthread creation 2
Memcheck:Param
write(buf)
fun:pthread_create@@GLIBC_2.2.5
fun:*
fun:*
fun:*
fun:*
fun:gst_task_start
}
{
Syscall param clone(child_tidptr) contains uninitialised byte(s)
Memcheck:Param
clone(child_tidptr)
fun:clone
}
{
memory loss when creating thread
Memcheck:Leak
fun:malloc
fun:__pthread_initialize_manager
fun:pthread_create*
}
# pyg_enable_threads memleak
{
memleak in pyg_enable_threads
Memcheck:Leak
fun:malloc
fun:*
fun:*
fun:*
fun:*
fun:*
fun:pyg_enable_threads
}
{
memleak in pyg_enable_threads 2
Memcheck:Leak
fun:malloc
fun:*
fun:*
fun:*
fun:*
fun:pyg_enable_threads
}
{
memleak in pyg_enable_threads 3
Memcheck:Leak
fun:malloc
fun:*
fun:*
fun:*
fun:pyg_enable_threads
}
#pygobject leaks
{
PyType_Ready leak
Memcheck:Leak
fun:malloc
fun:PyObject_Malloc
fun:_PyObject_GC_Malloc
fun:PyType_GenericAlloc
fun:*
fun:*
fun:PyType_Ready
}
#gst debug category new leak
{
gst debug category new leak
Memcheck:Leak
fun:malloc
fun:g_malloc
fun:g_strdup
fun:_gst_debug_category_new
}
# memleak in gst_element_state_get_name that we can't get rid of
{
gst_element_state_get_name
Memcheck:Leak
fun:malloc
fun:*
fun:g_vasprintf
fun:g_strdup*
fun:g_strdup*
fun:_wrap_gst_element_state_get_name
}
#memleak in pygobject_new_with_interfaces
# weird, cos it seems to free the return value of g_type_interfaces
{
_gst_element_factory_make
Memcheck:Leak
fun:malloc
fun:g_malloc
fun:g_type_interfaces
}
#memleak in static_pad_template
{
gst_static_pad_template_get
Memcheck:Leak
fun:calloc
fun:g_malloc0
fun:g_type_create_instance
fun:g_object_constructor
fun:gst_object_constructor
fun:*
fun:*
fun:*
fun:gst_static_pad_template_get
}
#leak in libxml
{
xml_parse_memory leak
Memcheck:Leak
fun:malloc
fun:*
fun:xml*
}
# FIXME : This is an awful leak that has do to with the gst_pad_set_*_function wrappers
{
leak in gst_pad_set_*_function wrappers
Memcheck:Leak
fun:calloc
fun:g_malloc0
fun:pad_private
}
# python leak in runtime compiler
{
python leak in runtime compiler
Memcheck:Leak
fun:malloc
fun:_PyObject_GC_Malloc
fun:_PyObject_GC_New*
fun:PyDict_New
fun:PySymtableEntry_New
fun:symtable_*
fun:symtable_*
fun:jcompile
}
#FIXME : These leaks are in core. See bug #344761
{
leak in init_gst, when creating the argv to give to gst_init_check()
Memcheck:Leak
fun:*
fun:g_malloc
fun:init_gst
}
{
The GOption context is leaking in gst_init_check
Memcheck:Leak
fun:*
fun:g_malloc0
fun:g_option_context_new
fun:gst_init_check
fun:init_gst
}
{
The GDir is leaked.
Memcheck:Leak
fun:*
fun:g_malloc
fun:g_dir_open
fun:gst_registry_scan_path_level
fun:gst_registry_scan_path
fun:init_post
fun:g_option_context_parse
fun:gst_init_check
fun:init_gst
}

48
testsuite/meson.build Normal file
View file

@ -0,0 +1,48 @@
runtests = files('runtests.py')
tests = [
['Test gst', 'test_gst.py'],
['Test fundamentals', 'test_types.py'],
['Test plugins', 'test_plugin.py'],
]
pluginsdirs = []
if not meson.is_subproject()
pkgconfig = find_program('pkg-config')
runcmd = run_command(pkgconfig, '--variable=pluginsdir',
'gstreamer-' + api_version)
if runcmd.returncode() == 0
pluginsdirs = runcmd.stdout().split()
else
error('Could not determine GStreamer core plugins directory for unit tests.')
endif
endif
runcmd = run_command(python, '-c', '''with open("@0@/mesonconfig.py", "w") as f:
f.write("path='@1@'")'''.format(
join_paths(meson.current_build_dir()), join_paths(meson.current_build_dir(), '..')))
if runcmd.returncode() != 0
error('Could not configure testsuite config file.' + runcmd.stderr())
endif
pluginsdirs = []
if gst_dep.type_name() == 'pkgconfig'
pbase = dependency('gstreamer-plugins-base-' + api_version, required : false)
pluginsdirs = [gst_dep.get_pkgconfig_variable('pluginsdir'),
pbase.get_pkgconfig_variable('pluginsdir')]
endif
pypluginsdir = [join_paths (meson.build_root(), 'plugin'), meson.current_source_dir()]
foreach i: tests
test_name = i.get(0)
env = environment()
env.set('GST_OVERRIDE_SRC_PATH', join_paths (meson.current_source_dir(), '..', 'gi', 'overrides'))
env.set('GST_OVERRIDE_BUILD_PATH', join_paths (meson.current_build_dir(), '..', 'gi', 'overrides'))
env.set('GST_PLUGIN_LOADING_WHITELIST', 'gstreamer',
'gst-plugins-base@' + meson.build_root(), 'gst-python@' + meson.build_root())
env.set('GST_PLUGIN_PATH_1_0', meson.build_root(), pluginsdirs + pypluginsdir)
env.set('GST_REGISTRY', join_paths(meson.current_build_dir(), '@0@.registry'.format(test_name)))
test(test_name, python, args: [runtests, i.get(1)], env: env)
endforeach

View file

@ -0,0 +1,28 @@
#include "test-object.h"
enum
{
/* FILL ME */
SIGNAL_EVENT,
LAST_SIGNAL
};
static guint test_object_signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT);
static void
test_object_init (TestObject * self)
{
}
static void
test_object_class_init (TestObjectClass * klass)
{
test_object_signals[SIGNAL_EVENT] =
g_signal_new ("event", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (TestObjectClass, event), NULL, NULL,
g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, GST_TYPE_EVENT);
}

View file

@ -0,0 +1,23 @@
#include <glib-object.h>
#include <gst/gstevent.h>
/* TestObject */
typedef struct {
GObject parent;
} TestObject;
typedef struct {
GObjectClass parent_class;
/* signals */
void (*event) (TestObject *object, GstEvent *event);
} TestObjectClass;
#define TEST_TYPE_OBJECT (test_object_get_type())
#define TEST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_OBJECT, TestObject))
#define TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjectClass))
#define TEST_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_OBJECT))
#define TEST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT))
#define TEST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TEST_TYPE_OBJECT, TestObjectClass))
GType test_object_get_type (void);

View file

@ -0,0 +1,83 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2009 Edward Hervey
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from common import gobject, gst, unittest, TestCase
class AdapterTest(TestCase):
def setUp(self):
TestCase.setUp(self)
self.adapter = gst.Adapter()
def tearDown(self):
self.adapter = None
TestCase.tearDown(self)
def testAvailable(self):
# starts empty
self.assertEquals(self.adapter.available(), 0)
self.assertEquals(self.adapter.available_fast(), 0)
# let's give it 4 bytes
self.adapter.push(gst.Buffer("1234"))
self.assertEquals(self.adapter.available_fast(), 4)
# let's give it another 5 bytes
self.adapter.push(gst.Buffer("56789"))
# we now have 9 bytes
self.assertEquals(self.adapter.available(), 9)
# but can only do a fast take of 4 bytes (the first buffer)
self.assertEquals(self.adapter.available_fast(), 4)
def testPeek(self):
self.adapter.push(gst.Buffer("0123456789"))
# let's peek at 5 bytes
b = self.adapter.peek(5)
# it can return more than 5 bytes
self.assert_(len(b) >= 5)
self.assertEquals(b, "01234")
# it's still 10 bytes big
self.assertEquals(self.adapter.available(), 10)
# if we try to peek more than what's available, we'll have None
self.assertEquals(self.adapter.peek(11), None)
def testFlush(self):
self.adapter.push(gst.Buffer("0123456789"))
self.assertEquals(self.adapter.available(), 10)
self.adapter.flush(5)
self.assertEquals(self.adapter.available(), 5)
# it flushed the first 5 bytes
self.assertEquals(self.adapter.peek(5), "56789")
self.adapter.flush(5)
self.assertEquals(self.adapter.available(), 0)
def testTake(self):
self.adapter.push(gst.Buffer("0123456789"))
self.assertEquals(self.adapter.available(), 10)
s = self.adapter.take(5)
self.assertEquals(s, "01234")
self.assertEquals(self.adapter.available(), 5)

View file

@ -0,0 +1,38 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from common import gobject, gst, unittest, TestCase
class Audio(TestCase):
def testBufferclip(self):
assert hasattr(gst.audio, "buffer_clip")
# create a segment
segment = gst.Segment()
gst.debug("Created the new segment")
# we'll put a new segment of 500ms to 1000ms
segment.set_newsegment(False, 1.0, gst.FORMAT_TIME, 0, -1, 0)
gst.debug("Initialized the new segment")
# create a new dummy buffer
b = gst.Buffer("this is a really useless line")
gst.debug("Created the buffer")
# clip... which shouldn't do anything
b2 = gst.audio.buffer_clip(b, segment, 44100, 8)
gst.debug("DONE !")

196
testsuite/old/test_bin.py Normal file
View file

@ -0,0 +1,196 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2002 David I. Lehn
# Copyright (C) 2004 Johan Dahlin
# Copyright (C) 2005 Edward Hervey
# Copyright (C) 2005 Thomas Vander Stichele
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from common import gobject, gst, unittest, TestCase, pygobject_2_13
import sys
import time
# see
# http://www.sicem.biz/personal/lgs/docs/gobject-python/gobject-tutorial.html
class MyBin(gst.Bin):
_state_changed = False
def __init__(self, name):
# we need to call GObject's init to be able to do self.do_*
gobject.GObject.__init__(self)
# since we can't chain up to our parent's __init__, we set the
# name manually
self.set_property('name', name)
def do_change_state(self, state_change):
if state_change == gst.STATE_CHANGE_PAUSED_TO_PLAYING:
self._state_changed = True
# FIXME: it seems a vmethod increases the refcount without unreffing
# print self.__gstrefcount__
# print self.__grefcount__
# chain up to parent
return gst.Bin.do_change_state(self, state_change)
# we need to register the type for PyGTK < 2.8
gobject.type_register(MyBin)
# FIXME: fix leak in vmethods before removing overriding fixture
class BinSubclassTest(TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def testStateChange(self):
bin = MyBin("mybin")
self.assertEquals(bin.__gstrefcount__, 1)
self.assertEquals(sys.getrefcount(bin), pygobject_2_13 and 2 or 3)
self.assertEquals(bin.get_name(), "mybin")
self.assertEquals(bin.__gstrefcount__, 1)
# test get_state with no timeout
(ret, state, pending) = bin.get_state()
self.failIfEqual(ret, gst.STATE_CHANGE_FAILURE)
self.assertEquals(bin.__gstrefcount__, 1)
# set to playing
bin.set_state(gst.STATE_PLAYING)
self.failUnless(bin._state_changed)
# test get_state with no timeout
(ret, state, pending) = bin.get_state()
self.failIfEqual(ret, gst.STATE_CHANGE_FAILURE)
if ret == gst.STATE_CHANGE_SUCCESS:
self.assertEquals(state, gst.STATE_PLAYING)
self.assertEquals(pending, gst.STATE_VOID_PENDING)
# test get_state with a timeout
(ret, state, pending) = bin.get_state(1)
self.failIfEqual(ret, gst.STATE_CHANGE_FAILURE)
if ret == gst.STATE_CHANGE_SUCCESS:
self.assertEquals(state, gst.STATE_PLAYING)
self.assertEquals(pending, gst.STATE_VOID_PENDING)
(ret, state, pending) = bin.get_state(timeout=gst.SECOND)
# back to NULL
bin.set_state(gst.STATE_NULL)
class BinAddRemove(TestCase):
def setUp(self):
TestCase.setUp(self)
self.bin = gst.Bin('bin')
def tearDown(self):
del self.bin
TestCase.tearDown(self)
def testError(self):
gst.info("creating fakesrc")
src = gst.element_factory_make('fakesrc', 'name')
gst.info("creating fakesink")
sink = gst.element_factory_make('fakesink', 'name')
gst.info("adding src:%d to bin" % src.__gstrefcount__)
self.assertEqual(src.__gstrefcount__, 1)
self.bin.add(src)
self.assertEqual(src.__gstrefcount__, 2)
gst.info("added src:%d" % src.__gstrefcount__)
self.assertRaises(gst.AddError, self.bin.add, sink)
self.assertRaises(gst.AddError, self.bin.add, src)
self.assertRaises(gst.RemoveError, self.bin.remove, sink)
gst.info("removing src")
self.bin.remove(src)
gst.info("removed")
self.assertRaises(gst.RemoveError, self.bin.remove, src)
def testMany(self):
src = gst.element_factory_make('fakesrc')
sink = gst.element_factory_make('fakesink')
self.bin.add(src, sink)
self.assertRaises(gst.AddError, self.bin.add, src, sink)
self.bin.remove(src, sink)
self.assertRaises(gst.RemoveError, self.bin.remove, src, sink)
class Preroll(TestCase):
def setUp(self):
TestCase.setUp(self)
self.bin = gst.Bin('bin')
def tearDown(self):
# FIXME: wait for state change thread to settle down
while self.bin.__gstrefcount__ > 1:
time.sleep(0.1)
self.assertEquals(self.bin.__gstrefcount__, 1)
del self.bin
TestCase.tearDown(self)
def testFake(self):
src = gst.element_factory_make('fakesrc')
sink = gst.element_factory_make('fakesink')
self.bin.add(src)
# bin will go to paused, src pad task will start and error out
self.bin.set_state(gst.STATE_PAUSED)
ret = self.bin.get_state()
self.assertEquals(ret[0], gst.STATE_CHANGE_SUCCESS)
self.assertEquals(ret[1], gst.STATE_PAUSED)
self.assertEquals(ret[2], gst.STATE_VOID_PENDING)
# adding the sink will cause the bin to go in preroll mode
gst.debug('adding sink and setting to PAUSED, should cause preroll')
self.bin.add(sink)
sink.set_state(gst.STATE_PAUSED)
ret = self.bin.get_state(timeout=0)
self.assertEquals(ret[0], gst.STATE_CHANGE_ASYNC)
self.assertEquals(ret[1], gst.STATE_PAUSED)
self.assertEquals(ret[2], gst.STATE_PAUSED)
# to actually complete preroll, we need to link and re-enable fakesrc
src.set_state(gst.STATE_READY)
src.link(sink)
src.set_state(gst.STATE_PAUSED)
ret = self.bin.get_state()
self.assertEquals(ret[0], gst.STATE_CHANGE_SUCCESS)
self.assertEquals(ret[1], gst.STATE_PAUSED)
self.assertEquals(ret[2], gst.STATE_VOID_PENDING)
self.bin.set_state(gst.STATE_NULL)
self.bin.get_state()
class ConstructorTest(TestCase):
def testGood(self):
bin = gst.Bin()
bin = gst.Bin(None)
bin = gst.Bin('')
bin = gst.Bin('myname')
def testBad(self):
# these are now valid. pygobject will take care of converting
# the arguments to a string.
#self.assertRaises(TypeError, gst.Bin, 0)
#self.assertRaises(TypeError, gst.Bin, gst.Bin())
pass
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,178 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2002 David I. Lehn
# Copyright (C) 2004 Johan Dahlin
# Copyright (C) 2005 Edward Hervey
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import sys
import gc
from common import gobject, gst, unittest, TestCase
class BufferTest(TestCase):
def testBufferBuffer(self):
buf = gst.Buffer('test')
assert str(buffer(buf)) == 'test'
def testBufferStr(self):
buffer = gst.Buffer('test')
assert str(buffer) == 'test'
def testBufferAlloc(self):
bla = 'mooooooo'
buffer = gst.Buffer(bla + '12345')
gc.collect ()
assert str(buffer) == 'mooooooo12345'
def testBufferBadConstructor(self):
self.assertRaises(TypeError, gst.Buffer, 'test', 0)
def testBufferStrNull(self):
test_string = 't\0e\0s\0t\0'
buffer = gst.Buffer(test_string)
assert str(buffer) == test_string
def testBufferSize(self):
test_string = 'a little string'
buffer = gst.Buffer(test_string)
assert len(buffer) == len(test_string)
assert hasattr(buffer, 'size')
assert buffer.size == len(buffer)
def testBufferCreateSub(self):
s = ''
for i in range(64):
s += '%02d' % i
buffer = gst.Buffer(s)
self.assertEquals(len(buffer), 128)
sub = buffer.create_sub(16, 16)
self.assertEquals(sub.size, 16)
self.assertEquals(sub.data, buffer.data[16:32])
self.assertEquals(sub.offset, gst.CLOCK_TIME_NONE)
def testBufferMerge(self):
buffer1 = gst.Buffer('foo')
buffer2 = gst.Buffer('bar')
merged_buffer = buffer1.merge(buffer2)
assert str(merged_buffer) == 'foobar'
def testBufferJoin(self):
buffer1 = gst.Buffer('foo')
buffer2 = gst.Buffer('bar')
joined_buffer = buffer1.merge(buffer2)
assert str(joined_buffer) == 'foobar'
def testBufferSpan(self):
buffer1 = gst.Buffer('foo')
buffer2 = gst.Buffer('bar')
spaned_buffer = buffer1.span(0L, buffer2, 6L)
assert str(spaned_buffer) == 'foobar'
def testBufferCopyOnWrite(self):
s='test_vector'
buffer = gst.Buffer(s)
sub = buffer.create_sub(0, buffer.size)
self.assertEquals(sub.size, buffer.size)
out = sub.copy_on_write ()
self.assertEquals(out.size, sub.size)
assert str(out) == str(buffer)
out[5] = 'w'
assert str(out) == 'test_wector'
def testBufferFlagIsSet(self):
buffer = gst.Buffer()
# Off by default
assert not buffer.flag_is_set(gst.BUFFER_FLAG_READONLY)
# Try switching on and off
buffer.flag_set(gst.BUFFER_FLAG_READONLY)
assert buffer.flag_is_set(gst.BUFFER_FLAG_READONLY)
buffer.flag_unset(gst.BUFFER_FLAG_READONLY)
assert not buffer.flag_is_set(gst.BUFFER_FLAG_READONLY)
# Try switching on and off
buffer.flag_set(gst.BUFFER_FLAG_IN_CAPS)
assert buffer.flag_is_set(gst.BUFFER_FLAG_IN_CAPS)
buffer.flag_unset(gst.BUFFER_FLAG_IN_CAPS)
assert not buffer.flag_is_set(gst.BUFFER_FLAG_IN_CAPS)
def testAttrFlags(self):
buffer = gst.Buffer()
assert hasattr(buffer, "flags")
assert isinstance(buffer.flags, int)
def testAttrTimestamp(self):
buffer = gst.Buffer()
assert hasattr(buffer, "timestamp")
assert isinstance(buffer.timestamp, long)
assert buffer.timestamp == gst.CLOCK_TIME_NONE
buffer.timestamp = 0
assert buffer.timestamp == 0
buffer.timestamp = 2**64 - 1
assert buffer.timestamp == 2**64 - 1
def testAttrDuration(self):
buffer = gst.Buffer()
assert hasattr(buffer, "duration")
assert isinstance(buffer.duration, long)
assert buffer.duration == gst.CLOCK_TIME_NONE
buffer.duration = 0
assert buffer.duration == 0
buffer.duration = 2**64 - 1
assert buffer.duration == 2**64 - 1
def testAttrOffset(self):
buffer = gst.Buffer()
assert hasattr(buffer, "offset")
assert isinstance(buffer.offset, long)
assert buffer.offset == gst.CLOCK_TIME_NONE
buffer.offset = 0
assert buffer.offset == 0
buffer.offset = 2**64 - 1
assert buffer.offset == 2**64 - 1
def testAttrOffset_end(self):
buffer = gst.Buffer()
assert hasattr(buffer, "offset_end")
assert isinstance(buffer.offset_end, long)
assert buffer.offset_end == gst.CLOCK_TIME_NONE
buffer.offset_end = 0
assert buffer.offset_end == 0
buffer.offset_end = 2**64 - 1
assert buffer.offset_end == 2**64 - 1
def testBufferCaps(self):
buffer = gst.Buffer()
caps = gst.caps_from_string('foo/blah')
gst.info("before settings caps")
buffer.set_caps(caps)
gst.info("after settings caps")
c = buffer.get_caps()
gst.info("after getting caps")
self.assertEquals(caps, c)
if __name__ == "__main__":
unittest.main()

242
testsuite/old/test_bus.py Normal file
View file

@ -0,0 +1,242 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2005 Thomas Vander Stichele
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from common import gst, unittest, TestCase
import gobject
import time
import sys
class BusSignalTest(TestCase):
def testGoodConstructor(self):
loop = gobject.MainLoop()
gst.info ("creating pipeline")
pipeline = gst.parse_launch("fakesrc ! fakesink")
gst.info ("getting bus")
bus = pipeline.get_bus()
gst.info ("got bus")
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
self.assertEquals(bus.__gstrefcount__, 2)
self.assertEquals(pipeline.__gstrefcount__, 1)
gst.info ("about to add a watch on the bus")
watch_id = bus.connect("message", self._message_received, pipeline, loop, "one")
bus.add_signal_watch()
gst.info ("added a watch on the bus")
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
self.assertEquals(bus.__gstrefcount__, 3)
self.assertEquals(pipeline.__gstrefcount__, 1)
gst.info("setting to playing")
ret = pipeline.set_state(gst.STATE_PLAYING)
gst.info("set to playing %s, loop.run" % ret)
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
loop.run()
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
gst.info("setting to paused")
ret = pipeline.set_state(gst.STATE_PAUSED)
gst.info("set to paused %s, loop.run" % ret)
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
loop.run()
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
gst.info("setting to ready")
ret = pipeline.set_state(gst.STATE_READY)
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
gst.info("set to READY %s, loop.run" % ret)
loop.run()
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
gst.info("setting to NULL")
ret = pipeline.set_state(gst.STATE_NULL)
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
gst.info("set to NULL %s" % ret)
self.gccollect()
self.assertEquals(bus.__gstrefcount__, 3)
# FIXME: state change thread needs to die
while pipeline.__gstrefcount__ > 1:
gst.debug('waiting for pipeline refcount to drop')
time.sleep(0.1)
self.assertEquals(pipeline.__gstrefcount__, 1)
gst.info("about to remove the watch id")
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
bus.remove_signal_watch()
gst.info("bus watch id removed")
bus.disconnect(watch_id)
gst.info("disconnected callback")
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
self.gccollect()
gst.info("pipeliner:%d/%d busr:%d" % (pipeline.__gstrefcount__, pipeline.__grefcount__, bus.__gstrefcount__))
self.assertEquals(bus.__gstrefcount__, 2)
self.assertEquals(pipeline.__gstrefcount__, 1)
gst.info("removing pipeline")
del pipeline
gst.info("pipeline removed")
gst.info("busr:%d" % bus.__gstrefcount__)
self.gccollect()
# flush the bus
bus.set_flushing(True)
bus.set_flushing(False)
self.gccollect()
# FIXME: refcount is still 2
self.assertEquals(bus.__gstrefcount__, 1)
def _message_received(self, bus, message, pipeline, loop, id):
self.failUnless(isinstance(bus, gst.Bus))
self.failUnless(isinstance(message, gst.Message))
self.assertEquals(id, "one")
loop.quit()
return True
def testSyncHandlerCallbackRefcount(self):
def callback1():
pass
def callback2():
pass
bus = gst.Bus()
# set
self.failUnless(sys.getrefcount(callback1), 2)
bus.set_sync_handler(callback1)
self.failUnless(sys.getrefcount(callback1), 3)
# set again
self.failUnless(sys.getrefcount(callback1), 3)
bus.set_sync_handler(callback1)
self.failUnless(sys.getrefcount(callback1), 3)
# replace
# this erros out in gst_bus_set_sync_handler, but we need to check that
# we don't leak anyway
self.failUnless(sys.getrefcount(callback2), 2)
bus.set_sync_handler(callback2)
self.failUnless(sys.getrefcount(callback1), 2)
self.failUnless(sys.getrefcount(callback2), 3)
# unset
bus.set_sync_handler(None)
self.failUnless(sys.getrefcount(callback2), 2)
class BusAddWatchTest(TestCase):
def testADumbExample(self):
gst.info("creating pipeline")
pipeline = gst.parse_launch("fakesrc ! fakesink")
gst.info("pipeliner:%s" % pipeline.__gstrefcount__)
bus = pipeline.get_bus()
gst.info("got bus, pipeliner:%d, busr:%d" % (pipeline.__gstrefcount__,
bus.__gstrefcount__))
## watch_id = bus.add_watch(self._message_received, pipeline)
## gst.info("added watch, pipeliner:%d, busr:%d" % (pipeline.__gstrefcount__,
## bus.__gstrefcount__))
## gobject.source_remove(watch_id)
## gst.info("removed watch, pipeliner:%d, busr:%d" % (pipeline.__gstrefcount__,
## bus.__gstrefcount__))
def testGoodConstructor(self):
loop = gobject.MainLoop()
gst.info ("creating pipeline")
pipeline = gst.parse_launch("fakesrc ! fakesink")
gst.info ("getting bus")
bus = pipeline.get_bus()
gst.info ("got bus")
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
self.assertEquals(bus.__gstrefcount__, 2)
self.assertEquals(pipeline.__gstrefcount__, 1)
gst.info ("about to add a watch on the bus")
watch_id = bus.add_watch(self._message_received, pipeline, loop, "one")
gst.info ("added a watch on the bus")
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
self.assertEquals(bus.__gstrefcount__, 3)
self.assertEquals(pipeline.__gstrefcount__, 1)
gst.info("setting to playing")
ret = pipeline.set_state(gst.STATE_PLAYING)
gst.info("set to playing %s, loop.run" % ret)
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
loop.run()
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
gst.info("setting to paused")
ret = pipeline.set_state(gst.STATE_PAUSED)
gst.info("set to paused %s, loop.run" % ret)
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
loop.run()
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
gst.info("setting to ready")
ret = pipeline.set_state(gst.STATE_READY)
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
gst.info("set to READY %s, loop.run" % ret)
loop.run()
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
gst.info("setting to NULL")
ret = pipeline.set_state(gst.STATE_NULL)
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
gst.info("set to NULL %s" % ret)
self.gccollect()
self.assertEquals(bus.__gstrefcount__, 3)
self.assertEquals(pipeline.__gstrefcount__, 1)
gst.info("about to remove the watch id")
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
self.failUnless(gobject.source_remove(watch_id))
gst.info("bus watch id removed")
gst.info("pipeliner:%d busr:%d" % (pipeline.__gstrefcount__, bus.__gstrefcount__))
self.gccollect()
gst.info("pipeliner:%d/%d busr:%d" % (pipeline.__gstrefcount__, pipeline.__grefcount__, bus.__gstrefcount__))
self.assertEquals(bus.__gstrefcount__, 2)
self.assertEquals(pipeline.__gstrefcount__, 1)
gst.info("removing pipeline")
del pipeline
gst.info("pipeline removed")
gst.info("busr:%d" % bus.__gstrefcount__)
self.gccollect()
# flush the bus
bus.set_flushing(True)
bus.set_flushing(False)
self.gccollect()
# FIXME: refcount is still 2
self.assertEquals(bus.__gstrefcount__, 1)
def _message_received(self, bus, message, pipeline, loop, id):
self.failUnless(isinstance(bus, gst.Bus))
self.failUnless(isinstance(message, gst.Message))
self.assertEquals(id, "one")
# doesn't the following line stop the mainloop before the end of the state change ?
loop.quit()
return True
if __name__ == "__main__":
unittest.main()

196
testsuite/old/test_caps.py Normal file
View file

@ -0,0 +1,196 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2002 David I. Lehn
# Copyright (C) 2004 Johan Dahlin
# Copyright (C) 2005 Edward Hervey
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import sys
from common import gst, unittest, TestCase
class CapsTest(TestCase):
def setUp(self):
TestCase.setUp(self)
self.caps = gst.caps_from_string('video/x-raw-yuv,width=10,framerate=5/1;video/x-raw-rgb,width=15,framerate=10/1')
self.assertEquals(self.caps.__refcount__, 1)
self.structure = self.caps[0]
self.any = gst.Caps("ANY")
self.assertEquals(self.any.__refcount__, 1)
self.empty = gst.Caps()
self.assertEquals(self.empty.__refcount__, 1)
def testCapsMime(self):
mime = self.structure.get_name()
assert mime == 'video/x-raw-yuv'
def testCapsList(self):
'check if we can access Caps as a list'
structure = self.caps[0]
mime = structure.get_name()
assert mime == 'video/x-raw-yuv'
structure = self.caps[1]
mime = structure.get_name()
assert mime == 'video/x-raw-rgb'
def testCapsContainingMiniObjects(self):
# buffer contains hex encoding of ascii 'abcd'
caps = gst.Caps("video/x-raw-yuv, buf=(buffer)61626364")
buf = caps[0]['buf']
assert isinstance(buf, gst.Buffer)
assert buf.data == "abcd"
buf = gst.Buffer("1234")
caps[0]['buf2'] = buf
buf2 = caps[0]['buf2']
assert buf2 == buf
def testCapsConstructEmpty(self):
caps = gst.Caps()
assert isinstance(caps, gst.Caps)
def testCapsConstructFromString(self):
caps = gst.Caps('video/x-raw-yuv,width=10')
assert isinstance(caps, gst.Caps)
assert len(caps) == 1
assert isinstance(caps[0], gst.Structure)
assert caps[0].get_name() == 'video/x-raw-yuv'
assert isinstance(caps[0]['width'], int)
assert caps[0]['width'] == 10
def testCapsConstructFromStructure(self):
struct = gst.structure_from_string('video/x-raw-yuv,width=10,framerate=[0/1, 25/3]')
caps = gst.Caps(struct)
assert isinstance(caps, gst.Caps)
assert len(caps) == 1
assert isinstance(caps[0], gst.Structure)
assert caps[0].get_name() == 'video/x-raw-yuv'
assert isinstance(caps[0]['width'], int)
assert caps[0]['width'] == 10
assert isinstance(caps[0]['framerate'], gst.FractionRange)
def testCapsConstructFromStructures(self):
struct1 = gst.structure_from_string('video/x-raw-yuv,width=10')
struct2 = gst.structure_from_string('video/x-raw-rgb,height=20.0')
caps = gst.Caps(struct1, struct2)
assert isinstance(caps, gst.Caps)
assert len(caps) == 2
struct = caps[0]
assert isinstance(struct, gst.Structure), struct
assert struct.get_name() == 'video/x-raw-yuv', struct.get_name()
assert struct.has_key('width')
assert isinstance(struct['width'], int)
assert struct['width'] == 10
struct = caps[1]
assert isinstance(struct, gst.Structure), struct
assert struct.get_name() == 'video/x-raw-rgb', struct.get_name()
assert struct.has_key('height')
assert isinstance(struct['height'], float)
assert struct['height'] == 20.0
def testCapsReferenceStructs(self):
'test that shows why it\'s not a good idea to use structures by reference'
caps = gst.Caps('hi/mom,width=0')
structure = caps[0]
del caps
assert structure['width'] == 0
def testCapsStructureChange(self):
'test if changing the structure of the caps works by reference'
assert self.structure['width'] == 10
self.structure['width'] = 5
assert self.structure['width'] == 5.0
# check if we changed the caps as well
structure = self.caps[0]
assert structure['width'] == 5.0
def testCapsBadConstructor(self):
struct = gst.structure_from_string('video/x-raw-yuv,width=10')
self.assertRaises(TypeError, gst.Caps, None)
self.assertRaises(TypeError, gst.Caps, 1)
self.assertRaises(TypeError, gst.Caps, 2.0)
self.assertRaises(TypeError, gst.Caps, object)
self.assertRaises(TypeError, gst.Caps, 1, 2, 3)
# This causes segfault!
#self.assertRaises(TypeError, gst.Caps, struct, 10, None)
def testTrueFalse(self):
'test that comparisons using caps work the intended way'
assert self.any # not empty even though it has no structures
assert not self.empty
assert not gst.Caps('EMPTY') # also empty
assert gst.Caps('your/mom')
def testComparisons(self):
assert self.empty < self.any
assert self.empty < self.structure
assert self.empty < self.caps
assert self.caps < self.any
assert self.empty <= self.empty
assert self.caps <= self.caps
assert self.caps <= self.any
assert self.empty == "EMPTY"
assert self.caps != self.any
assert self.empty != self.any
assert self.any > self.empty
assert self.any >= self.empty
def testFilters(self):
name = 'video/x-raw-yuv'
filtercaps = gst.Caps(*[struct for struct in self.caps if struct.get_name() == name])
intersection = self.caps & 'video/x-raw-yuv'
assert filtercaps == intersection
def doSubtract(self, set, subset):
'''mimic the test in GStreamer core's testsuite/caps/subtract.c'''
assert not set - set
assert not subset - subset
assert not subset - set
test = set - subset
assert test
test2 = test | subset
test = test2 - set
assert not test
#our own extensions foolow here
assert subset == set & subset
assert set == set | subset
assert set - subset == set ^ subset
def testSubtract(self):
self.doSubtract(
gst.Caps ("some/mime, _int = [ 1, 2 ], list = { \"A\", \"B\", \"C\" }"),
gst.Caps ("some/mime, _int = 1, list = \"A\""))
self.doSubtract(
gst.Caps ("some/mime, _double = (double) 1.0; other/mime, _int = { 1, 2 }"),
gst.Caps ("some/mime, _double = (double) 1.0"))
def testNoneValue(self):
caps = gst.Caps("foo")
def invalid_assignment():
caps[0]["bar"] = None
self.assertRaises(TypeError, invalid_assignment)
def invalid_set_value():
caps[0].set_value("bar", None)
self.assertRaises(TypeError, invalid_set_value)
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,268 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2002 David I. Lehn
# Copyright (C) 2004 Johan Dahlin
# Copyright (C) 2005 Edward Hervey
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from common import gst, unittest, TestCase, pygobject_2_13
import sys
# since I can't subclass gst.Element for some reason, I use a bin here
# it don't matter to Jesus
class TestElement(gst.Bin):
def break_it_down(self):
self.debug('Hammer Time')
class ElementTest(TestCase):
name = 'fakesink'
alias = 'sink'
def testGoodConstructor(self):
element = gst.element_factory_make(self.name, self.alias)
assert element is not None, 'element is None'
assert isinstance(element, gst.Element)
assert element.get_name() == self.alias
## FIXME : Make a new test for state changes, using bus signals
## class FakeSinkTest(ElementTest):
## FAKESINK_STATE_ERROR_NONE = "0"
## FAKESINK_STATE_ERROR_NULL_READY, = "1"
## FAKESINK_STATE_ERROR_READY_PAUSED, = "2"
## FAKESINK_STATE_ERROR_PAUSED_PLAYING = "3"
## FAKESINK_STATE_ERROR_PLAYING_PAUSED = "4"
## FAKESINK_STATE_ERROR_PAUSED_READY = "5"
## FAKESINK_STATE_ERROR_READY_NULL = "6"
## name = 'fakesink'
## alias = 'sink'
## def setUp(self):
## ElementTest.setUp(self)
## self.element = gst.element_factory_make('fakesink', 'sink')
## def tearDown(self):
## self.element.set_state(gst.STATE_NULL)
## del self.element
## ElementTest.tearDown(self)
## def checkError(self, old_state, state, name):
## assert self.element.get_state() == gst.STATE_NULL
## assert self.element.set_state(old_state)
## assert self.element.get_state() == old_state
## self.element.set_property('state-error', name)
## self.error = False
## def error_cb(element, source, gerror, debug):
## assert isinstance(element, gst.Element)
## assert element == self.element
## assert isinstance(source, gst.Element)
## assert source == self.element
## assert isinstance(gerror, gst.GError)
## self.error = True
## self.element.connect('error', error_cb)
## self.element.set_state (state)
## assert self.error, 'error not set'
## #assert error_message.find('ERROR') != -1
## self.element.get_state() == old_state, 'state changed'
## def testStateErrorNullReady(self):
## self.checkError(gst.STATE_NULL, gst.STATE_READY,
## self.FAKESINK_STATE_ERROR_NULL_READY)
## def testStateErrorReadyPaused(self):
## self.checkError(gst.STATE_READY, gst.STATE_PAUSED,
## self.FAKESINK_STATE_ERROR_READY_PAUSED)
## def testStateErrorPausedPlaying(self):
## self.checkError(gst.STATE_PAUSED, gst.STATE_PLAYING,
## self.FAKESINK_STATE_ERROR_PAUSED_PLAYING)
## def testStateErrorPlayingPaused(self):
## self.checkError(gst.STATE_PLAYING, gst.STATE_PAUSED,
## self.FAKESINK_STATE_ERROR_PLAYING_PAUSED)
## def testStateErrorPausedReady(self):
## self.checkError(gst.STATE_PAUSED, gst.STATE_READY,
## self.FAKESINK_STATE_ERROR_PAUSED_READY)
## def testStateErrorReadyNull(self):
## self.checkError(gst.STATE_READY, gst.STATE_NULL,
## self.FAKESINK_STATE_ERROR_READY_NULL)
## def checkStateChange(self, old, new):
## def state_change_cb(element, old_s, new_s):
## assert isinstance(element, gst.Element)
## assert element == self.element
## assert old_s == old
## assert new_s == new
## assert self.element.set_state(old)
## assert self.element.get_state(0.0)[1] == old
## # FIXME: replace with messages
## # self.element.connect('state-change', state_change_cb)
## assert self.element.set_state(new)
## assert self.element.get_state(0.0)[1] == new
## def testStateChangeNullReady(self):
## self.checkStateChange(gst.STATE_NULL, gst.STATE_READY)
## def testStateChangeReadyPaused(self):
## self.checkStateChange(gst.STATE_READY, gst.STATE_PAUSED)
## def testStateChangePausedPlaying(self):
## self.checkStateChange(gst.STATE_PAUSED, gst.STATE_PLAYING)
## def testStateChangePlayingPaused(self):
## self.checkStateChange(gst.STATE_PLAYING, gst.STATE_PAUSED)
## def testStateChangePausedReady(self):
## self.checkStateChange(gst.STATE_PAUSED, gst.STATE_READY)
## def testStateChangeReadyNull(self):
## self.checkStateChange(gst.STATE_READY, gst.STATE_NULL)
class NonExistentTest(ElementTest):
name = 'this-element-does-not-exist'
alias = 'no-alias'
testGoodConstructor = lambda s: None
testGoodConstructor2 = lambda s: None
class FileSrcTest(ElementTest):
name = 'filesrc'
alias = 'source'
class FileSinkTest(ElementTest):
name = 'filesink'
alias = 'sink'
class ElementName(TestCase):
def testElementStateGetName(self):
get_name = gst.element_state_get_name
for state in ('NULL',
'READY',
'PLAYING',
'PAUSED'):
name = 'STATE_' + state
assert hasattr(gst, name)
attr = getattr(gst, name)
assert get_name(attr) == state
assert get_name(gst.STATE_VOID_PENDING) == 'VOID_PENDING'
assert get_name(-1) == 'UNKNOWN!(-1)'
self.assertRaises(TypeError, get_name, '')
class QueryTest(TestCase):
def setUp(self):
TestCase.setUp(self)
self.pipeline = gst.parse_launch('fakesrc name=source ! fakesink')
self.assertEquals(self.pipeline.__gstrefcount__, 1)
self.element = self.pipeline.get_by_name('source')
self.assertEquals(self.pipeline.__gstrefcount__, 1)
self.assertEquals(self.element.__gstrefcount__, 2)
self.assertEquals(sys.getrefcount(self.element), pygobject_2_13 and 2 or 3)
def tearDown(self):
del self.pipeline
del self.element
TestCase.tearDown(self)
def testQuery(self):
gst.debug('querying fakesrc in FORMAT_BYTES')
res = self.element.query_position(gst.FORMAT_BYTES)
self.assertEquals(self.pipeline.__gstrefcount__, 1)
self.assertEquals(sys.getrefcount(self.pipeline), pygobject_2_13 and 2 or 3)
self.assertEquals(self.element.__gstrefcount__, 2)
self.assertEquals(sys.getrefcount(self.element), pygobject_2_13 and 2 or 3)
assert res
assert res[0] == 0
self.assertRaises(gst.QueryError, self.element.query_position,
gst.FORMAT_TIME)
self.gccollect()
class QueueTest(TestCase):
def testConstruct(self):
queue = gst.element_factory_make('queue')
assert queue.get_name() == 'queue0'
self.assertEquals(queue.__gstrefcount__, 1)
class DebugTest(TestCase):
def testDebug(self):
e = gst.element_factory_make('fakesrc')
e.error('I am an error string')
e.warning('I am a warning string')
e.info('I am an info string')
e.debug('I am a debug string')
e.log('I am a log string')
e.debug('I am a formatted %s %s' % ('log', 'string'))
def testElementDebug(self):
e = TestElement("testelement")
e.set_property("name", "testelement")
e.break_it_down()
class LinkTest(TestCase):
def testLinkNoPads(self):
src = gst.Bin()
sink = gst.Bin()
self.assertRaises(gst.LinkError, src.link, sink)
def testLink(self):
src = gst.element_factory_make('fakesrc')
sink = gst.element_factory_make('fakesink')
self.failUnless(src.link(sink))
# FIXME: this unlink leaks, no idea why
# src.unlink(sink)
# print src.__gstrefcount__
def testLinkPads(self):
src = gst.element_factory_make('fakesrc')
sink = gst.element_factory_make('fakesink')
# print src.__gstrefcount__
self.failUnless(src.link_pads("src", sink, "sink"))
src.unlink_pads("src", sink, "sink")
def testLinkFiltered(self):
# a filtered link uses capsfilter and thus needs a bin
bin = gst.Bin()
src = gst.element_factory_make('fakesrc')
sink = gst.element_factory_make('fakesink')
bin.add(src, sink)
caps = gst.caps_from_string("audio/x-raw-int")
self.failUnless(src.link(sink, caps))
# DANGER WILL. src is not actually connected to sink, since
# there's a capsfilter in the way. What a leaky abstraction.
# FIXME
# src.unlink(sink)
# instead, mess with pads directly
pad = src.get_pad('src')
pad.unlink(pad.get_peer())
pad = sink.get_pad('sink')
pad.get_peer().unlink(pad)
if __name__ == "__main__":
unittest.main()

244
testsuite/old/test_event.py Normal file
View file

@ -0,0 +1,244 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2002 David I. Lehn
# Copyright (C) 2004 Johan Dahlin
# Copyright (C) 2005 Edward Hervey
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import os
import sys
import time
import tempfile
from common import gst, unittest, testhelper, TestCase
class EventTest(TestCase):
def setUp(self):
TestCase.setUp(self)
self.pipeline = gst.parse_launch('fakesrc ! fakesink name=sink')
self.sink = self.pipeline.get_by_name('sink')
self.pipeline.set_state(gst.STATE_PLAYING)
def tearDown(self):
gst.debug('setting pipeline to NULL')
self.pipeline.set_state(gst.STATE_NULL)
gst.debug('set pipeline to NULL')
# FIXME: wait for state change thread to die
while self.pipeline.__gstrefcount__ > 1:
gst.debug('waiting for self.pipeline G rc to drop to 1')
time.sleep(0.1)
self.assertEquals(self.pipeline.__gstrefcount__, 1)
del self.sink
del self.pipeline
TestCase.tearDown(self)
def testEventSeek(self):
# this event only serves to change the rate of data transfer
event = gst.event_new_seek(1.0, gst.FORMAT_BYTES, gst.SEEK_FLAG_FLUSH,
gst.SEEK_TYPE_NONE, 0, gst.SEEK_TYPE_NONE, 0)
# FIXME: but basesrc goes into an mmap/munmap spree, needs to be fixed
event = gst.event_new_seek(1.0, gst.FORMAT_BYTES, gst.SEEK_FLAG_FLUSH,
gst.SEEK_TYPE_SET, 0, gst.SEEK_TYPE_NONE, 0)
assert event
gst.debug('sending event')
self.sink.send_event(event)
gst.debug('sent event')
self.assertEqual(event.parse_seek(), (1.0, gst.FORMAT_BYTES, gst.SEEK_FLAG_FLUSH,
gst.SEEK_TYPE_SET, 0, gst.SEEK_TYPE_NONE, 0))
def testWrongEvent(self):
buffer = gst.Buffer()
self.assertRaises(TypeError, self.sink.send_event, buffer)
number = 1
self.assertRaises(TypeError, self.sink.send_event, number)
class EventFileSrcTest(TestCase):
def setUp(self):
TestCase.setUp(self)
gst.info("start")
self.filename = tempfile.mktemp()
open(self.filename, 'w').write(''.join(map(str, range(10))))
self.pipeline = gst.parse_launch('filesrc name=source location=%s blocksize=1 ! fakesink signal-handoffs=1 name=sink' % self.filename)
self.source = self.pipeline.get_by_name('source')
self.sink = self.pipeline.get_by_name('sink')
self.sigid = self.sink.connect('handoff', self.handoff_cb)
self.bus = self.pipeline.get_bus()
def tearDown(self):
self.pipeline.set_state(gst.STATE_NULL)
self.sink.disconnect(self.sigid)
if os.path.exists(self.filename):
os.remove(self.filename)
del self.bus
del self.pipeline
del self.source
del self.sink
del self.handoffs
TestCase.tearDown(self)
def handoff_cb(self, element, buffer, pad):
self.handoffs.append(str(buffer))
def playAndIter(self):
self.handoffs = []
self.pipeline.set_state(gst.STATE_PLAYING)
assert self.pipeline.set_state(gst.STATE_PLAYING)
while 42:
msg = self.bus.pop()
if msg and msg.type == gst.MESSAGE_EOS:
break
assert self.pipeline.set_state(gst.STATE_PAUSED)
handoffs = self.handoffs
self.handoffs = []
return handoffs
def sink_seek(self, offset, method=gst.SEEK_TYPE_SET):
self.sink.seek(1.0, gst.FORMAT_BYTES, gst.SEEK_FLAG_FLUSH,
method, offset,
gst.SEEK_TYPE_NONE, 0)
def testSimple(self):
handoffs = self.playAndIter()
assert handoffs == map(str, range(10))
def testSeekCur(self):
self.sink_seek(8)
self.playAndIter()
class TestEmit(TestCase):
def testEmit(self):
object = testhelper.get_object()
object.connect('event', self._event_cb)
# First emit from C
testhelper.emit_event(object)
# Then emit from Python
object.emit('event', gst.event_new_eos())
def _event_cb(self, obj, event):
assert isinstance(event, gst.Event)
class TestDelayedEventProbe(TestCase):
# this test:
# starts a pipeline with only a source
# adds an event probe to catch the (first) new-segment
# adds a buffer probe to "autoplug" and send out this event
def setUp(self):
TestCase.setUp(self)
self.pipeline = gst.Pipeline()
self.src = gst.element_factory_make('fakesrc')
self.src.set_property('num-buffers', 10)
self.pipeline.add(self.src)
self.srcpad = self.src.get_pad('src')
def tearDown(self):
gst.debug('setting pipeline to NULL')
self.pipeline.set_state(gst.STATE_NULL)
gst.debug('set pipeline to NULL')
# FIXME: wait for state change thread to die
while self.pipeline.__gstrefcount__ > 1:
gst.debug('waiting for self.pipeline G rc to drop to 1')
time.sleep(0.1)
self.assertEquals(self.pipeline.__gstrefcount__, 1)
def testProbe(self):
self.srcpad.add_event_probe(self._event_probe_cb)
self._buffer_probe_id = self.srcpad.add_buffer_probe(
self._buffer_probe_cb)
self._newsegment = None
self._eos = None
self._had_buffer = False
self.pipeline.set_state(gst.STATE_PLAYING)
while not self._eos:
time.sleep(0.1)
# verify if our newsegment event is still around and valid
self.failUnless(self._newsegment)
self.assertEquals(self._newsegment.type, gst.EVENT_NEWSEGMENT)
self.assertEquals(self._newsegment.__grefcount__, 1)
# verify if our eos event is still around and valid
self.failUnless(self._eos)
self.assertEquals(self._eos.type, gst.EVENT_EOS)
self.assertEquals(self._eos.__grefcount__, 1)
def _event_probe_cb(self, pad, event):
if event.type == gst.EVENT_NEWSEGMENT:
self._newsegment = event
self.assertEquals(event.__grefcount__, 3)
# drop the event, we're storing it for later sending
return False
if event.type == gst.EVENT_EOS:
self._eos = event
# we also want fakesink to get it
return True
# sinks now send Latency events upstream
if event.type == gst.EVENT_LATENCY:
return True
self.fail("Got an unknown event %r" % event)
def _buffer_probe_cb(self, pad, buffer):
self.failUnless(self._newsegment)
# fake autoplugging by now putting in a fakesink
sink = gst.element_factory_make('fakesink')
self.pipeline.add(sink)
self.src.link(sink)
sink.set_state(gst.STATE_PLAYING)
pad = sink.get_pad('sink')
pad.send_event(self._newsegment)
# we don't want to be called again
self.srcpad.remove_buffer_probe(self._buffer_probe_id)
self._had_buffer = True
# now let the buffer through
return True
class TestEventCreationParsing(TestCase):
def testEventStep(self):
if hasattr(gst.Event, "parse_step"):
e = gst.event_new_step(gst.FORMAT_TIME, 42, 1.0, True, True)
self.assertEquals(e.type, gst.EVENT_STEP)
fmt, amount, rate, flush, intermediate = e.parse_step()
self.assertEquals(fmt, gst.FORMAT_TIME)
self.assertEquals(amount, 42)
self.assertEquals(rate, 1.0)
self.assertEquals(flush, True)
self.assertEquals(intermediate, True)
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,194 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2002 David I. Lehn
# Copyright (C) 2004 Johan Dahlin
# Copyright (C) 2005 Edward Hervey
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from common import gst, unittest, TestCase, pygobject_2_13
import sys
import gc
import gobject
class SrcBin(gst.Bin):
def prepare(self):
src = gst.element_factory_make('fakesrc')
self.add(src)
pad = src.get_pad("src")
ghostpad = gst.GhostPad("src", pad)
self.add_pad(ghostpad)
gobject.type_register(SrcBin)
class SinkBin(gst.Bin):
def prepare(self):
sink = gst.element_factory_make('fakesink')
self.add(sink)
pad = sink.get_pad("sink")
ghostpad = gst.GhostPad("sink", pad)
self.add_pad(ghostpad)
self.sink = sink
def connect_handoff(self, cb, *args, **kwargs):
self.sink.set_property('signal-handoffs', True)
self.sink.connect('handoff', cb, *args, **kwargs)
gobject.type_register(SinkBin)
class PipeTest(TestCase):
def setUp(self):
gst.info("setUp")
TestCase.setUp(self)
self.pipeline = gst.Pipeline()
self.assertEquals(self.pipeline.__gstrefcount__, 1)
self.assertEquals(sys.getrefcount(self.pipeline), pygobject_2_13 and 2 or 3)
self.src = SrcBin()
self.src.prepare()
self.sink = SinkBin()
self.sink.prepare()
self.assertEquals(self.src.__gstrefcount__, 1)
self.assertEquals(sys.getrefcount(self.src), pygobject_2_13 and 2 or 3)
self.assertEquals(self.sink.__gstrefcount__, 1)
self.assertEquals(sys.getrefcount(self.sink), pygobject_2_13 and 2 or 3)
gst.info("end of SetUp")
def tearDown(self):
gst.info("tearDown")
self.assertTrue (self.pipeline.__gstrefcount__ >= 1 and self.pipeline.__gstrefcount__ <= 2)
self.assertEquals(sys.getrefcount(self.pipeline), pygobject_2_13 and 2 or 3)
self.assertEquals(self.src.__gstrefcount__, 2)
self.assertEquals(sys.getrefcount(self.src), pygobject_2_13 and 2 or 3)
self.assertEquals(self.sink.__gstrefcount__, 2)
self.assertEquals(sys.getrefcount(self.sink), 3)
gst.debug('deleting pipeline')
del self.pipeline
self.gccollect()
self.assertEquals(self.src.__gstrefcount__, 1) # parent gone
self.assertEquals(self.sink.__gstrefcount__, 1) # parent gone
self.assertEquals(sys.getrefcount(self.src), pygobject_2_13 and 2 or 3)
self.assertEquals(sys.getrefcount(self.sink), pygobject_2_13 and 2 or 3)
gst.debug('deleting src')
del self.src
self.gccollect()
gst.debug('deleting sink')
del self.sink
self.gccollect()
TestCase.tearDown(self)
def testBinState(self):
self.pipeline.add(self.src, self.sink)
self.src.link(self.sink)
self.sink.connect_handoff(self._sink_handoff_cb)
self._handoffs = 0
self.assertTrue(self.pipeline.set_state(gst.STATE_PLAYING) != gst.STATE_CHANGE_FAILURE)
while True:
(ret, cur, pen) = self.pipeline.get_state()
if ret == gst.STATE_CHANGE_SUCCESS and cur == gst.STATE_PLAYING:
break
while self._handoffs < 10:
pass
self.assertEquals(self.pipeline.set_state(gst.STATE_NULL), gst.STATE_CHANGE_SUCCESS)
while True:
(ret, cur, pen) = self.pipeline.get_state()
if ret == gst.STATE_CHANGE_SUCCESS and cur == gst.STATE_NULL:
break
## def testProbedLink(self):
## self.pipeline.add(self.src)
## pad = self.src.get_pad("src")
## self.sink.connect_handoff(self._sink_handoff_cb)
## self._handoffs = 0
## # FIXME: adding a probe to the ghost pad does not work atm
## # id = pad.add_buffer_probe(self._src_buffer_probe_cb)
## realpad = pad.get_target()
## self._probe_id = realpad.add_buffer_probe(self._src_buffer_probe_cb)
## self._probed = False
## while True:
## (ret, cur, pen) = self.pipeline.get_state()
## if ret == gst.STATE_CHANGE_SUCCESS and cur == gst.STATE_PLAYING:
## break
## while not self._probed:
## pass
## while self._handoffs < 10:
## pass
## self.pipeline.set_state(gst.STATE_NULL)
## while True:
## (ret, cur, pen) = self.pipeline.get_state()
## if ret == gst.STATE_CHANGE_SUCCESS and cur == gst.STATE_NULL:
## break
def _src_buffer_probe_cb(self, pad, buffer):
gst.debug("received probe on pad %r" % pad)
self._probed = True
gst.debug('adding sink bin')
self.pipeline.add(self.sink)
# this seems to get rid of the warnings about pushing on an unactivated
# pad
gst.debug('setting sink state')
# FIXME: attempt one: sync to current pending state of bin
(res, cur, pen) = self.pipeline.get_state(timeout=0)
target = pen
if target == gst.STATE_VOID_PENDING:
target = cur
gst.debug("setting sink state to %r" % target)
# FIXME: the following print can cause a lock-up; why ?
# print target
# if we don't set async, it will possibly end up in PAUSED
self.sink.set_state(target)
gst.debug('linking')
self.src.link(self.sink)
gst.debug('removing buffer probe id %r' % self._probe_id)
pad.remove_buffer_probe(self._probe_id)
self._probe_id = None
gst.debug('done')
def _sink_handoff_cb(self, sink, buffer, pad):
gst.debug('received handoff on pad %r' % pad)
self._handoffs += 1
class TargetTest(TestCase):
def test_target(self):
src = gst.Pad("src", gst.PAD_SRC)
ghost = gst.GhostPad("ghost_src", src)
self.failUnless(ghost.get_target() is src)
ghost.set_target(None)
self.failUnless(ghost.get_target() is None)
ghost.set_target(src)
self.failUnless(ghost.get_target() is src)
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,92 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2002 David I. Lehn
# Copyright (C) 2004 Johan Dahlin
# Copyright (C) 2005 Edward Hervey
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from common import gst, unittest, TestCase
import gobject
def find_mixer_element():
""" Searches for an element implementing the mixer interface """
allmix = [x for x in gst.registry_get_default().get_feature_list(gst.ElementFactory)
if x.has_interface("GstMixer") and x.has_interface("GstPropertyProbe")]
if allmix == []:
return None
return allmix[0]
class Availability(TestCase):
def testXOverlay(self):
assert hasattr(gst.interfaces, 'XOverlay')
assert issubclass(gst.interfaces.XOverlay, gobject.GInterface)
def testMixer(self):
assert hasattr(gst.interfaces, 'Mixer')
assert issubclass(gst.interfaces.Mixer, gobject.GInterface)
class FunctionCall(TestCase):
def FIXME_testXOverlay(self):
# obviously a testsuite is not allowed to instantiate this
# since it needs a running X or will fail. find some way to
# deal with that.
element = gst.element_factory_make('xvimagesink')
assert isinstance(element, gst.Element)
assert isinstance(element, gst.interfaces.XOverlay)
element.set_xwindow_id(0L)
class MixerTest(TestCase):
def setUp(self):
TestCase.setUp(self)
amix = find_mixer_element()
if amix:
self.mixer = amix.create()
else:
self.mixer = None
def tearDown(self):
del self.mixer
TestCase.tearDown(self)
def testGetProperty(self):
if self.mixer == None:
return
self.failUnless(self.mixer.probe_get_property('device'))
self.assertRaises(ValueError,
self.mixer.probe_get_property, 'non-existent')
def testGetProperties(self):
if self.mixer == None:
return
properties = self.mixer.probe_get_properties()
self.failUnless(properties)
self.assertEqual(type(properties), list)
prop = properties[0]
self.assertEqual(prop.name, 'device')
self.assertEqual(prop.value_type, gobject.TYPE_STRING)
def testGetValuesName(self):
if self.mixer == None:
return
values = self.mixer.probe_get_values_name('device')
self.assertEqual(type(values), list)
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,117 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# Copyright (C) 2005 Johan Dahlin
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import unittest
from common import gst, TestCase
class IteratorTest(TestCase):
# XXX: Elements
def testBinIterateElements(self):
pipeline = gst.parse_launch("fakesrc name=src ! fakesink name=sink")
elements = list(pipeline.elements())
fakesrc = pipeline.get_by_name("src")
fakesink = pipeline.get_by_name("sink")
self.assertEqual(len(elements), 2)
self.failUnless(fakesrc in elements)
self.failUnless(fakesink in elements)
pipeline.remove(fakesrc)
elements = list(pipeline.elements())
self.assertEqual(len(elements), 1)
self.failUnless(not fakesrc in pipeline)
# XXX : There seems to be a problem about the GType
# set in gst_bin_iterated_sorted
def testBinIterateSorted(self):
pipeline = gst.parse_launch("fakesrc name=src ! fakesink name=sink")
elements = list(pipeline.sorted())
fakesrc = pipeline.get_by_name("src")
fakesink = pipeline.get_by_name("sink")
self.assertEqual(elements[0], fakesink)
self.assertEqual(elements[1], fakesrc)
def testBinIterateRecurse(self):
pipeline = gst.parse_launch("fakesrc name=src ! fakesink name=sink")
elements = list(pipeline.recurse())
fakesrc = pipeline.get_by_name("src")
fakesink = pipeline.get_by_name("sink")
self.assertEqual(elements[0], fakesink)
self.assertEqual(elements[1], fakesrc)
def testBinIterateSinks(self):
pipeline = gst.parse_launch("fakesrc name=src ! fakesink name=sink")
elements = list(pipeline.sinks())
fakesrc = pipeline.get_by_name("src")
fakesink = pipeline.get_by_name("sink")
self.assertEqual(len(elements), 1)
self.failUnless(fakesink in elements)
self.failUnless(not fakesrc in elements)
def testIteratePadsFakeSrc(self):
fakesrc = gst.element_factory_make('fakesrc')
pads = list(fakesrc.pads())
srcpad = fakesrc.get_pad('src')
self.assertEqual(len(pads), 1)
self.assertEqual(pads[0], srcpad)
srcpads = list(fakesrc.src_pads())
self.assertEqual(len(srcpads), 1)
self.assertEqual(srcpads[0], srcpad)
sinkpads = list(fakesrc.sink_pads())
self.assertEqual(sinkpads, [])
self.assertEqual(len(list(fakesrc)), 1)
for pad in fakesrc:
self.assertEqual(pad, srcpad)
break
else:
raise AssertionError
def testIteratePadsFakeSink(self):
fakesink = gst.element_factory_make('fakesink')
pads = list(fakesink.pads())
sinkpad = fakesink.get_pad('sink')
self.assertEqual(len(pads), 1)
self.assertEqual(pads[0], sinkpad)
srcpads = list(fakesink.src_pads())
self.assertEqual(srcpads, [])
sinkpads = list(fakesink.sink_pads())
self.assertEqual(len(sinkpads), 1)
self.assertEqual(sinkpads[0], sinkpad)
self.assertEqual(len(list(fakesink)), 1)
for pad in fakesink:
self.assertEqual(pad, sinkpad)
break
else:
raise AssertionError
def testInvalidIterator(self):
p = gst.Pad("p", gst.PAD_SRC)
# The C function will return NULL, we should
# therefore have an exception raised
self.assertRaises(TypeError, p.iterate_internal_links)
del p

View file

@ -0,0 +1,37 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from common import gst, TestCase
from gst import tag
class TesLibTag(TestCase):
def testXmp(self):
taglist = gst.TagList()
taglist['title'] = 'my funny title'
taglist['geo-location-latitude'] = 23.25
xmp = tag.tag_list_to_xmp_buffer (taglist, True)
self.assertNotEquals(xmp, None)
taglist2 = tag.tag_list_from_xmp_buffer (xmp)
self.assertEquals(len(taglist2), 2)
self.assertEquals(taglist2['title'], 'my funny title')
self.assertEquals(taglist2['geo-location-latitude'], 23.25)

View file

@ -0,0 +1,202 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2005 Thomas Vander Stichele
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import sys
from common import gobject, gst, unittest, TestCase
import gc
class NewTest(TestCase):
def testEOS(self):
gst.info("creating new bin")
b = gst.Bin()
gst.info("creating new EOS message from that bin")
m = gst.message_new_eos(b)
gst.info("got message : %s" % m)
def message_application_cb(self, bus, message):
gst.info("got application message")
self.got_message = True
self.loop.quit()
def testApplication(self):
self.loop = gobject.MainLoop()
gst.info("creating new pipeline")
bin = gst.Pipeline()
bus = bin.get_bus()
bus.add_signal_watch()
self.got_message = False
bus.connect('message::application', self.message_application_cb)
struc = gst.Structure("foo")
msg = gst.message_new_application(bin, struc)
# the bus is flushing in NULL, so we need to set the pipeline to READY
bin.set_state(gst.STATE_READY)
bus.post(msg)
self.loop.run()
bus.remove_signal_watch()
bin.set_state(gst.STATE_NULL)
self.failUnless(self.got_message == True)
self.gccollect()
class TestCreateMessages(TestCase):
def setUp(self):
TestCase.setUp(self)
self.element = gst.Bin()
def tearDown(self):
del self.element
def testCustomMessage(self):
# create two custom messages using the same structure
s = gst.Structure("something")
assert s != None
e1 = gst.message_new_custom(gst.MESSAGE_APPLICATION, self.element, s)
assert e1
e2 = gst.message_new_custom(gst.MESSAGE_APPLICATION, self.element, s)
assert e2
# make sure the two structures are equal
self.assertEquals(e1.structure.to_string(),
e2.structure.to_string())
def testTagMessage(self):
# Create a taglist
t = gst.TagList()
t['something'] = "else"
t['another'] = 42
# Create two messages using that same taglist
m1 = gst.message_new_tag(self.element, t)
assert m1
m2 = gst.message_new_tag(self.element, t)
assert m2
# make sure the two messages have the same taglist
t1 = m1.parse_tag()
assert t1
keys = t1.keys()
keys.sort()
self.assertEquals(keys, ['another', 'something'])
self.assertEquals(t1['something'], "else")
self.assertEquals(t1['another'], 42)
t2 = m2.parse_tag()
assert t2
keys = t2.keys()
keys.sort()
self.assertEquals(keys, ['another', 'something'])
self.assertEquals(t2['something'], "else")
self.assertEquals(t2['another'], 42)
def testTagFullMessage(self):
if hasattr(gst.Message, 'parse_tag_full'):
p = gst.Pad("blahblah", gst.PAD_SRC)
# Create a taglist
t = gst.TagList()
t['something'] = "else"
t['another'] = 42
# Create two messages using that same taglist
m1 = gst.message_new_tag_full(self.element, p, t)
assert m1
m2 = gst.message_new_tag_full(self.element, p, t)
assert m2
# make sure the two messages have the same taglist
p1, t1 = m1.parse_tag_full()
assert t1
keys = t1.keys()
keys.sort()
self.assertEquals(p1, p)
self.assertEquals(keys, ['another', 'something'])
self.assertEquals(t1['something'], "else")
self.assertEquals(t1['another'], 42)
p2, t2 = m2.parse_tag_full()
assert t2
keys = t2.keys()
keys.sort()
self.assertEquals(p2, p)
self.assertEquals(keys, ['another', 'something'])
self.assertEquals(t2['something'], "else")
self.assertEquals(t2['another'], 42)
def testStepStartMessage(self):
if hasattr(gst, 'message_new_step_start'):
m = gst.message_new_step_start(self.element, True,
gst.FORMAT_TIME, 42, 1.0,
True, True)
self.assertEquals(m.type, gst.MESSAGE_STEP_START)
active, format, amount, rate, flush, intermediate = m.parse_step_start()
self.assertEquals(active, True)
self.assertEquals(format, gst.FORMAT_TIME)
self.assertEquals(amount, 42)
self.assertEquals(rate, 1.0)
self.assertEquals(flush, True)
self.assertEquals(intermediate, True)
def testStepDoneMessage(self):
if hasattr(gst, 'message_new_step_done'):
m = gst.message_new_step_done(self.element, gst.FORMAT_TIME, 42,
1.0, True, True, 54, True)
self.assertEquals(m.type, gst.MESSAGE_STEP_DONE)
fmt, am, rat, flu, inter, dur, eos = m.parse_step_done()
self.assertEquals(fmt, gst.FORMAT_TIME)
self.assertEquals(am, 42)
self.assertEquals(rat, 1.0)
self.assertEquals(flu, True)
self.assertEquals(inter, True)
self.assertEquals(dur, 54)
self.assertEquals(eos, True)
def testStructureChangeMessage(self):
if hasattr(gst, 'message_new_structure_change'):
p = gst.Pad("blah", gst.PAD_SINK)
m = gst.message_new_structure_change(p,
gst.STRUCTURE_CHANGE_TYPE_PAD_LINK,
self.element, True)
self.assertEquals(m.type, gst.MESSAGE_STRUCTURE_CHANGE)
sct, owner, busy = m.parse_structure_change()
self.assertEquals(sct, gst.STRUCTURE_CHANGE_TYPE_PAD_LINK)
self.assertEquals(owner, self.element)
self.assertEquals(busy, True)
def testRequestStateMessage(self):
if hasattr(gst, 'message_new_request_state'):
m = gst.message_new_request_state(self.element, gst.STATE_NULL)
self.assertEquals(m.type, gst.MESSAGE_REQUEST_STATE)
self.assertEquals(m.parse_request_state(), gst.STATE_NULL)
def testBufferingStatsMessage(self):
if hasattr(gst.Message, 'set_buffering_stats'):
gst.debug("Creating buffering message")
m = gst.message_new_buffering(self.element, 50)
gst.debug("Setting stats")
m.set_buffering_stats(gst.BUFFERING_LIVE, 30, 1024, 123456)
self.assertEquals(m.type, gst.MESSAGE_BUFFERING)
mode, ain, aout, left = m.parse_buffering_stats()
self.assertEquals(mode, gst.BUFFERING_LIVE)
self.assertEquals(ain, 30)
self.assertEquals(aout, 1024)
self.assertEquals(left, 123456)
if __name__ == "__main__":
unittest.main()

568
testsuite/old/test_pad.py Normal file
View file

@ -0,0 +1,568 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2002 David I. Lehn
# Copyright (C) 2004 Johan Dahlin
# Copyright (C) 2005 Edward Hervey
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from common import gst, unittest, TestCase, pygobject_2_13
import sys
import time
class PadTemplateTest(TestCase):
def testConstructor(self):
template = gst.PadTemplate("template", gst.PAD_SINK,
gst.PAD_ALWAYS, gst.caps_from_string("audio/x-raw-int"))
self.failUnless(template)
self.assertEquals(sys.getrefcount(template), pygobject_2_13 and 2 or 3)
#self.assertEquals(template.__gstrefcount__, 1)
class PadPushUnlinkedTest(TestCase):
def setUp(self):
TestCase.setUp(self)
self.src = gst.Pad("src", gst.PAD_SRC)
self.sink = gst.Pad("sink", gst.PAD_SINK)
def tearDown(self):
self.assertEquals(sys.getrefcount(self.src), pygobject_2_13 and 2 or 3)
self.assertEquals(self.src.__gstrefcount__, 1)
del self.src
self.assertEquals(sys.getrefcount(self.sink), pygobject_2_13 and 2 or 3)
self.assertEquals(self.sink.__gstrefcount__, 1)
del self.sink
TestCase.tearDown(self)
def testNoProbe(self):
self.buffer = gst.Buffer()
self.assertEquals(self.buffer.__grefcount__, 1)
self.assertEquals(self.src.push(self.buffer), gst.FLOW_NOT_LINKED)
# pushing it takes a ref in the python wrapper to keep buffer
# alive afterwards; but the core unrefs the ref it receives
self.assertEquals(self.buffer.__grefcount__, 1)
def testFalseProbe(self):
id = self.src.add_buffer_probe(self._probe_handler, False)
self.buffer = gst.Buffer()
self.assertEquals(self.buffer.__grefcount__, 1)
self.assertEquals(self.src.push(self.buffer), gst.FLOW_OK)
self.assertEquals(self.buffer.__grefcount__, 1)
self.src.remove_buffer_probe(id)
def testTrueProbe(self):
id = self.src.add_buffer_probe(self._probe_handler, True)
self.buffer = gst.Buffer()
self.assertEquals(self.buffer.__grefcount__, 1)
self.assertEquals(self.src.push(self.buffer), gst.FLOW_NOT_LINKED)
self.assertEquals(self.buffer.__grefcount__, 1)
self.src.remove_buffer_probe(id)
def _probe_handler(self, pad, buffer, ret):
return ret
class PadPushLinkedTest(TestCase):
def setUp(self):
TestCase.setUp(self)
self.src = gst.Pad("src", gst.PAD_SRC)
self.sink = gst.Pad("sink", gst.PAD_SINK)
caps = gst.caps_from_string("foo/bar")
self.src.set_caps(caps)
self.sink.set_caps(caps)
self.sink.set_chain_function(self._chain_func)
self.src.set_active(True)
self.sink.set_active(True)
self.src.link(self.sink)
self.buffers = []
def tearDown(self):
self.assertEquals(sys.getrefcount(self.src), pygobject_2_13 and 2 or 3)
self.assertEquals(self.src.__gstrefcount__, 1)
self.src.set_caps(None)
del self.src
self.assertEquals(sys.getrefcount(self.sink), pygobject_2_13 and 2 or 3)
self.assertEquals(self.sink.__gstrefcount__, 1)
self.sink.set_caps(None)
del self.sink
TestCase.tearDown(self)
def _chain_func(self, pad, buffer):
gst.debug('got buffer %r, id %x, with GMO rc %d'% (
buffer, id(buffer), buffer.__grefcount__))
self.buffers.append(buffer)
return gst.FLOW_OK
def testNoProbe(self):
self.buffer = gst.Buffer()
gst.debug('created new buffer %r, id %x' % (
self.buffer, id(self.buffer)))
self.assertEquals(self.buffer.__grefcount__, 1)
gst.debug('pushing buffer on linked pad, no probe')
self.assertEquals(self.src.push(self.buffer), gst.FLOW_OK)
gst.debug('pushed buffer on linked pad, no probe')
# one refcount is held by our scope, another is held on
# self.buffers through _chain_func
self.assertEquals(self.buffer.__grefcount__, 2)
self.assertEquals(len(self.buffers), 1)
self.buffers = None
self.assertEquals(self.buffer.__grefcount__, 1)
def testFalseProbe(self):
id = self.src.add_buffer_probe(self._probe_handler, False)
self.buffer = gst.Buffer()
self.assertEquals(self.buffer.__grefcount__, 1)
self.assertEquals(self.src.push(self.buffer), gst.FLOW_OK)
self.assertEquals(self.buffer.__grefcount__, 1)
self.src.remove_buffer_probe(id)
self.assertEquals(len(self.buffers), 0)
def testTrueProbe(self):
probe_id = self.src.add_buffer_probe(self._probe_handler, True)
self.buffer = gst.Buffer()
self.assertEquals(self.buffer.__grefcount__, 1)
self.assertEquals(self.src.push(self.buffer), gst.FLOW_OK)
# one refcount is held by our scope, another is held on
# self.buffers through _chain_func
self.assertEquals(self.buffer.__grefcount__, 2)
# they are not the same Python object ...
self.failIf(self.buffer is self.buffers[0])
self.failIf(id(self.buffer) == id(self.buffers[0]))
# ... but they wrap the same GstBuffer
self.failUnless(self.buffer == self.buffers[0])
self.assertEquals(repr(self.buffer), repr(self.buffers[0]))
self.src.remove_buffer_probe(probe_id)
self.assertEquals(len(self.buffers), 1)
self.buffers = None
self.assertEquals(self.buffer.__grefcount__, 1)
def _probe_handler(self, pad, buffer, ret):
return ret
# test for event probes with linked pads
class PadPushEventLinkedTest(TestCase):
def setUp(self):
TestCase.setUp(self)
self.src = gst.Pad("src", gst.PAD_SRC)
self.sink = gst.Pad("sink", gst.PAD_SINK)
caps = gst.caps_from_string("foo/bar")
self.src.set_caps(caps)
self.sink.set_caps(caps)
self.sink.set_chain_function(self._chain_func)
self.src.set_active(True)
self.sink.set_active(True)
self.src.link(self.sink)
self.events = []
def tearDown(self):
self.assertEquals(sys.getrefcount(self.src), pygobject_2_13 and 2 or 3)
self.assertEquals(self.src.__gstrefcount__, 1)
self.src.set_caps(None)
del self.src
self.assertEquals(sys.getrefcount(self.sink), pygobject_2_13 and 2 or 3)
self.assertEquals(self.sink.__gstrefcount__, 1)
self.sink.set_caps(None)
del self.sink
TestCase.tearDown(self)
def _chain_func(self, pad, buffer):
gst.debug('got buffer %r, id %x, with GMO rc %d'% (
buffer, id(buffer), buffer.__grefcount__))
self.buffers.append(buffer)
return gst.FLOW_OK
def testNoProbe(self):
self.event = gst.event_new_eos()
gst.debug('created new eos %r, id %x' % (
self.event, id(self.event)))
self.assertEquals(self.event.__grefcount__, 1)
gst.debug('pushing event on linked pad, no probe')
self.assertEquals(self.src.push_event(self.event), True)
gst.debug('pushed event on linked pad, no probe')
# one refcount is held by our scope
self.assertEquals(self.event.__grefcount__, 1)
# the event has reffed the src pad as the src of the event
self.assertEquals(self.src.__grefcount__, 2)
# clear it
self.event = None
self.assertEquals(self.src.__grefcount__, 1)
def testFalseProbe(self):
probe_id = self.src.add_event_probe(self._probe_handler, False)
self.event = gst.event_new_eos()
gst.debug('created new eos %r, id %x' % (
self.event, id(self.event)))
self.assertEquals(self.event.__grefcount__, 1)
# a false probe return drops the event and returns False
self.assertEquals(self.src.push_event(self.event), False)
# one ref in our local scope, another in self.events
self.assertEquals(self.event.__grefcount__, 2)
self.assertEquals(self.sink.__grefcount__, 1)
# the event has reffed the src pad as the src of the event
self.assertEquals(self.src.__grefcount__, 2)
# remove the event from existence
self.event = None
self.events = None
self.assertEquals(self.src.__grefcount__, 1)
self.src.remove_buffer_probe(probe_id)
def testTrueProbe(self):
probe_id = self.src.add_event_probe(self._probe_handler, True)
self.event = gst.event_new_eos()
gst.debug('created new eos %r, id %x' % (
self.event, id(self.event)))
self.assertEquals(self.event.__grefcount__, 1)
# a True probe lets it pass
self.assertEquals(self.src.push_event(self.event), True)
# one refcount is held by our scope, another is held on
# self.events through _probe
self.assertEquals(self.event.__grefcount__, 2)
# they are not the same Python object ...
self.failIf(self.event is self.events[0])
self.failIf(id(self.event) == id(self.events[0]))
# ... but they wrap the same GstEvent
self.assertEquals(repr(self.event), repr(self.events[0]))
self.failUnless(self.event == self.events[0])
self.src.remove_buffer_probe(probe_id)
self.assertEquals(len(self.events), 1)
self.events = None
self.assertEquals(self.event.__grefcount__, 1)
# the event has reffed the src pad as the src of the event
self.assertEquals(self.src.__grefcount__, 2)
# clear it
self.event = None
self.assertEquals(self.src.__grefcount__, 1)
def _probe_handler(self, pad, event, ret):
gst.debug("probed, pad %r, event %r" % (pad, event))
self.events.append(event)
return ret
# a test to show that we can link a pad from the probe handler
class PadPushProbeLinkTest(TestCase):
def setUp(self):
TestCase.setUp(self)
self.src = gst.Pad("src", gst.PAD_SRC)
self.sink = gst.Pad("sink", gst.PAD_SINK)
caps = gst.caps_from_string("foo/bar")
self.src.set_caps(caps)
self.sink.set_caps(caps)
self.src.set_active(True)
self.sink.set_active(True)
self.sink.set_chain_function(self._chain_func)
self.buffers = []
def tearDown(self):
self.assertEquals(sys.getrefcount(self.src), pygobject_2_13 and 2 or 3)
self.assertEquals(self.src.__gstrefcount__, 1)
self.src.set_caps(None)
del self.src
self.assertEquals(sys.getrefcount(self.sink), pygobject_2_13 and 2 or 3)
self.assertEquals(self.sink.__gstrefcount__, 1)
self.sink.set_caps(None)
del self.sink
TestCase.tearDown(self)
def _chain_func(self, pad, buffer):
self.buffers.append(buffer)
return gst.FLOW_OK
def testProbeLink(self):
id = self.src.add_buffer_probe(self._probe_handler)
self.buffer = gst.Buffer()
self.assertEquals(self.buffer.__grefcount__, 1)
gst.debug('pushing buffer on linked pad, no probe')
self.assertEquals(self.src.push(self.buffer), gst.FLOW_OK)
gst.debug('pushed buffer on linked pad, no probe')
# one refcount is held by our scope, another is held on
# self.buffers through _chain_func
self.assertEquals(self.buffer.__grefcount__, 2)
self.assertEquals(len(self.buffers), 1)
self.buffers = None
self.assertEquals(self.buffer.__grefcount__, 1)
def _probe_handler(self, pad, buffer):
self.src.link(self.sink)
return True
class PadTest(TestCase):
def testConstructor(self):
# first style uses gst_pad_new
gst.debug('creating pad with name src')
pad = gst.Pad("src", gst.PAD_SRC)
self.failUnless(pad)
self.assertEquals(sys.getrefcount(pad), pygobject_2_13 and 2 or 3)
self.assertEquals(pad.__gstrefcount__, 1)
gst.debug('creating pad with no name')
self.failUnless(gst.Pad(None, gst.PAD_SRC))
self.failUnless(gst.Pad(name=None, direction=gst.PAD_SRC))
self.failUnless(gst.Pad(direction=gst.PAD_SRC, name=None))
self.failUnless(gst.Pad(direction=gst.PAD_SRC, name="src"))
# second uses gst_pad_new_from_template
#template = gst.PadTemplate()
class PadPipelineTest(TestCase):
def setUp(self):
TestCase.setUp(self)
self.pipeline = gst.parse_launch('fakesrc name=source ! fakesink')
src = self.pipeline.get_by_name('source')
self.srcpad = src.get_pad('src')
def tearDown(self):
del self.pipeline
del self.srcpad
TestCase.tearDown(self)
# FIXME: now that GstQuery is a miniobject with various _new_ factory
# functions, we need to figure out a way to deal with them in python
# def testQuery(self):
# assert self.sink.query(gst.QUERY_TOTAL, gst.FORMAT_BYTES) == -1
# assert self.srcpad.query(gst.QUERY_POSITION, gst.FORMAT_BYTES) == 0
# assert self.srcpad.query(gst.QUERY_POSITION, gst.FORMAT_TIME) == 0
class PadProbePipeTest(TestCase):
def setUp(self):
TestCase.setUp(self)
self.pipeline = gst.Pipeline()
self.assertEquals(self.pipeline.__gstrefcount__, 1)
self.assertEquals(sys.getrefcount(self.pipeline), pygobject_2_13 and 2 or 3)
self.fakesrc = gst.element_factory_make('fakesrc')
self.fakesink = gst.element_factory_make('fakesink')
self.assertEquals(self.fakesrc.__gstrefcount__, 1)
self.assertEquals(sys.getrefcount(self.fakesrc), pygobject_2_13 and 2 or 3)
self.pipeline.add(self.fakesrc, self.fakesink)
self.assertEquals(self.fakesrc.__gstrefcount__, 2) # added
self.assertEquals(sys.getrefcount(self.fakesrc), pygobject_2_13 and 2 or 3)
self.assertEquals(self.fakesink.__gstrefcount__, 2) # added
self.assertEquals(sys.getrefcount(self.fakesink), pygobject_2_13 and 2 or 3)
self.fakesrc.link(self.fakesink)
self.assertEquals(self.pipeline.__gstrefcount__, 1)
self.assertEquals(sys.getrefcount(self.pipeline), pygobject_2_13 and 2 or 3)
self.assertEquals(self.fakesrc.__gstrefcount__, 2)
self.assertEquals(sys.getrefcount(self.fakesrc), pygobject_2_13 and 2 or 3)
self.assertEquals(self.fakesink.__gstrefcount__, 2)
self.assertEquals(sys.getrefcount(self.fakesink), pygobject_2_13 and 2 or 3)
def tearDown(self):
# Refcount must be either 1 or 2, to allow for a possibly still running
# state-recalculation thread
self.assertTrue (self.pipeline.__gstrefcount__ >= 1 and self.pipeline.__gstrefcount__ <= 2)
self.assertEquals(sys.getrefcount(self.pipeline), pygobject_2_13 and 2 or 3)
self.assertEquals(self.fakesrc.__gstrefcount__, 2)
self.assertEquals(sys.getrefcount(self.fakesrc), pygobject_2_13 and 2 or 3)
gst.debug('deleting pipeline')
del self.pipeline
self.gccollect()
self.assertEquals(self.fakesrc.__gstrefcount__, 1) # parent gone
self.assertEquals(self.fakesink.__gstrefcount__, 1) # parent gone
self.assertEquals(sys.getrefcount(self.fakesrc), pygobject_2_13 and 2 or 3)
self.assertEquals(sys.getrefcount(self.fakesink), pygobject_2_13 and 2 or 3)
gst.debug('deleting fakesrc')
del self.fakesrc
self.gccollect()
gst.debug('deleting fakesink')
del self.fakesink
self.gccollect()
TestCase.tearDown(self)
def testFakeSrcProbeOnceKeep(self):
self.fakesrc.set_property('num-buffers', 1)
self.fakesink.set_property('signal-handoffs', True)
self.fakesink.connect('handoff', self._handoff_callback_fakesink)
pad = self.fakesrc.get_pad('src')
id = pad.add_buffer_probe(self._probe_callback_fakesrc)
self._got_fakesrc_buffer = 0
self._got_fakesink_buffer = 0
self.pipeline.set_state(gst.STATE_PLAYING)
while not self._got_fakesrc_buffer:
gst.debug('waiting for fakesrc buffer')
pass
while not self._got_fakesink_buffer:
gst.debug('waiting for fakesink buffer')
pass
gst.debug('got buffers from fakesrc and fakesink')
self.assertEquals(self._got_fakesink_buffer, 1)
pad.remove_buffer_probe(id)
self.pipeline.set_state(gst.STATE_NULL)
def testFakeSrcProbeMany(self):
self.fakesrc.set_property('num-buffers', 1000)
pad = self.fakesrc.get_pad('src')
id = pad.add_buffer_probe(self._probe_callback_fakesrc)
self._got_fakesrc_buffer = 0
self.pipeline.set_state(gst.STATE_PLAYING)
while not self._got_fakesrc_buffer == 1000:
import time
# allow for context switching; a busy loop here locks up the
# streaming thread too much
time.sleep(.0001)
pad.remove_buffer_probe(id)
self.pipeline.set_state(gst.STATE_NULL)
def _probe_callback_fakesrc(self, pad, buffer):
self.failUnless(isinstance(pad, gst.Pad))
self.failUnless(isinstance(buffer, gst.Buffer))
self._got_fakesrc_buffer += 1
gst.debug('fakesrc sent buffer %r, %d total sent' % (
buffer, self._got_fakesrc_buffer))
return True
def _handoff_callback_fakesink(self, sink, buffer, pad):
self.failUnless(isinstance(buffer, gst.Buffer))
self.failUnless(isinstance(pad, gst.Pad))
self._got_fakesink_buffer += 1
gst.debug('fakesink got buffer %r, %d total received' % (
buffer, self._got_fakesrc_buffer))
gst.debug('pad %r, py refcount %d, go rc %d, gst rc %d' % (
pad, sys.getrefcount(pad), pad.__grefcount__, pad.__gstrefcount__))
return True
def testRemovingProbe(self):
self.fakesrc.set_property('num-buffers', 10)
handle = None
self._num_times_called = 0
def buffer_probe(pad, buffer, data):
self._num_times_called += 1
pad.remove_buffer_probe(handle)
return True
pad = self.fakesrc.get_pad('src')
data = []
handle = pad.add_buffer_probe(buffer_probe, data)
self.pipeline.set_state(gst.STATE_PLAYING)
m = self.pipeline.get_bus().poll(gst.MESSAGE_EOS, -1)
assert m
assert self._num_times_called == 1
self.pipeline.set_state(gst.STATE_NULL)
assert sys.getrefcount(buffer_probe) == 2
assert sys.getrefcount(data) == 2
# FIXME: having m going out of scope doesn't seem to be enough
# to get it gc collected, and it keeps a ref to the pipeline.
# Look for a way to not have to do this explicitly
del m
self.gccollect()
class PadRefCountTest(TestCase):
def testAddPad(self):
# add a pad to an element
e = gst.element_factory_make('fakesrc')
self.assertEquals(sys.getrefcount(e), pygobject_2_13 and 2 or 3)
self.assertEquals(e.__gstrefcount__, 1)
gst.debug('creating pad with name mypad')
pad = gst.Pad("mypad", gst.PAD_SRC)
self.failUnless(pad)
self.assertEquals(sys.getrefcount(pad), pygobject_2_13 and 2 or 3)
self.assertEquals(pad.__gstrefcount__, 1)
gst.debug('adding pad to element')
e.add_pad(pad)
self.assertEquals(sys.getrefcount(e), pygobject_2_13 and 2 or 3)
self.assertEquals(e.__gstrefcount__, 1)
self.assertEquals(sys.getrefcount(pad), pygobject_2_13 and 2 or 3)
self.assertEquals(pad.__gstrefcount__, 2) # added to element
gst.debug('deleting element and collecting')
self.gccollect()
del e
if not pygobject_2_13:
# the element will be collected at 'del e' if we're using
# pygobject >= 2.13.0
self.assertEquals(self.gccollect(), 1) # collected the element
self.assertEquals(sys.getrefcount(pad), pygobject_2_13 and 2 or 3)
self.assertEquals(pad.__gstrefcount__, 1) # removed from element
gst.debug('deleting pad and collecting')
del pad
if not pygobject_2_13:
# the pad will be collected at 'del pad' if we're using
# pygobject >= 2.13.0
self.assertEquals(self.gccollect(), 1) # collected the pad
gst.debug('going into teardown')
class PadBlockTest(TestCase):
def testCallbackFlush(self):
# check that the same block callback can be called more than once (weird
# test but it was broken)
def blocked_cb(pad, blocked):
pad.push_event(gst.event_new_flush_start())
pad = gst.Pad('src', gst.PAD_SRC)
pad.set_active(True)
pad.set_blocked_async(True, blocked_cb)
for i in xrange(10):
buf = gst.Buffer('ciao')
pad.push(buf)
pad.push_event(gst.event_new_flush_stop())
def testCallbackRefcount(self):
def blocked_cb(pad, blocked):
pad.set_blocked_async(False, unblocked_cb)
def unblocked_cb(pad, blocked):
pass
cb_refcount = sys.getrefcount(blocked_cb)
# sys.getrefcount() returns refcount + 1
self.assertEquals(cb_refcount, 2)
pad = gst.Pad('src', gst.PAD_SRC)
pad.set_active(True)
pad.set_blocked_async(True, blocked_cb)
# set_blocked_async refs the callback
self.assertEquals(sys.getrefcount(blocked_cb), 3)
buf = gst.Buffer('ciao')
pad.push(buf)
# in blocked_cb() we called set_blocked_async() with a different
# callback, so blocked_cb() should have been unreffed
cb_refcount_after = sys.getrefcount(blocked_cb)
self.assertEquals(sys.getrefcount(blocked_cb), cb_refcount)
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,63 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2008 Edward Hervey <edward.hervey@collabora.co.uk>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from common import gobject, gst, unittest, TestCase
class Descriptions(TestCase):
def testSourceDescription(self):
assert hasattr(gst.pbutils, 'get_source_description')
self.assertEquals(gst.pbutils.get_source_description("file"),
"FILE protocol source")
def testSinkDescription(self):
assert hasattr(gst.pbutils, 'get_sink_description')
self.assertEquals(gst.pbutils.get_sink_description("file"),
"FILE protocol sink")
def testDecoderDescription(self):
assert hasattr(gst.pbutils, 'get_decoder_description')
self.assertEquals(gst.pbutils.get_decoder_description(gst.caps_from_string("audio/mpeg,mpegversion=1,layer=3")),
'MPEG-1 Layer 3 (MP3) decoder')
def testCodecDescription(self):
assert hasattr(gst.pbutils, 'get_codec_description')
self.assertEquals(gst.pbutils.get_codec_description(gst.caps_from_string("audio/mpeg,mpegversion=1,layer=3")),
'MPEG-1 Layer 3 (MP3)')
def testEncoderDescription(self):
assert hasattr(gst.pbutils, 'get_encoder_description')
self.assertEquals(gst.pbutils.get_encoder_description(gst.caps_from_string("audio/mpeg,mpegversion=1,layer=3")),
'MPEG-1 Layer 3 (MP3) encoder')
def testElementDescription(self):
assert hasattr(gst.pbutils, 'get_element_description')
self.assertEquals(gst.pbutils.get_element_description("something"),
"GStreamer element something")
def testAddCodecDescription(self):
assert hasattr(gst.pbutils, 'add_codec_description_to_tag_list')
# TODO
# Add tests for the other parts of pbutils:
# * missing-plugins
# * install-plugins (and detect if there weren't compiled because of a version
# of plugins-base too low)

View file

@ -0,0 +1,250 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2002 David I. Lehn
# Copyright (C) 2004 Johan Dahlin
# Copyright (C) 2005 Edward Hervey
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import time
from common import gst, unittest, TestCase, pygobject_2_13
import gobject
class TestConstruction(TestCase):
def setUp(self):
self.gctrack()
def tearDown(self):
self.gccollect()
self.gcverify()
def testGoodConstructor(self):
name = 'test-pipeline'
pipeline = gst.Pipeline(name)
self.assertEquals(pipeline.__gstrefcount__, 1)
assert pipeline is not None, 'pipeline is None'
self.failUnless(isinstance(pipeline, gst.Pipeline),
'pipeline is not a GstPipline')
assert pipeline.get_name() == name, 'pipelines name is wrong'
self.assertEquals(pipeline.__gstrefcount__, 1)
def testParseLaunch(self):
pipeline = gst.parse_launch('fakesrc ! fakesink')
class Pipeline(TestCase):
def setUp(self):
self.gctrack()
self.pipeline = gst.Pipeline('test-pipeline')
source = gst.element_factory_make('fakesrc', 'source')
source.set_property('num-buffers', 5)
sink = gst.element_factory_make('fakesink', 'sink')
self.pipeline.add(source, sink)
gst.element_link_many(source, sink)
def tearDown(self):
del self.pipeline
self.gccollect()
self.gcverify()
def testRun(self):
self.assertEqual(self.pipeline.get_state()[1], gst.STATE_NULL)
self.pipeline.set_state(gst.STATE_PLAYING)
self.assertEqual(self.pipeline.get_state()[1], gst.STATE_PLAYING)
time.sleep(1)
self.assertEqual(self.pipeline.get_state()[1], gst.STATE_PLAYING)
self.pipeline.set_state(gst.STATE_NULL)
self.assertEqual(self.pipeline.get_state()[1], gst.STATE_NULL)
class PipelineTags(TestCase):
def setUp(self):
self.gctrack()
self.pipeline = gst.parse_launch('audiotestsrc num-buffers=100 ! vorbisenc name=encoder ! oggmux name=muxer ! fakesink')
def tearDown(self):
del self.pipeline
self.gccollect()
self.gcverify()
def testRun(self):
# in 0.10.15.1, this triggers
# sys:1: gobject.Warning: g_value_get_uint: assertion `G_VALUE_HOLDS_UINT (value)' failed
# during pipeline playing
l = gst.TagList()
l[gst.TAG_ARTIST] = 'artist'
l[gst.TAG_TRACK_NUMBER] = 1
encoder = self.pipeline.get_by_name('encoder')
encoder.merge_tags(l, gst.TAG_MERGE_APPEND)
self.assertEqual(self.pipeline.get_state()[1], gst.STATE_NULL)
self.pipeline.set_state(gst.STATE_PLAYING)
self.assertEqual(self.pipeline.get_state()[1], gst.STATE_PLAYING)
time.sleep(1)
self.assertEqual(self.pipeline.get_state()[1], gst.STATE_PLAYING)
self.pipeline.set_state(gst.STATE_NULL)
self.assertEqual(self.pipeline.get_state()[1], gst.STATE_NULL)
class Bus(TestCase):
def testGet(self):
pipeline = gst.Pipeline('test')
self.assertEquals(pipeline.__gstrefcount__, 1)
bus = pipeline.get_bus()
self.assertEquals(pipeline.__gstrefcount__, 1)
# one for python and one for the pipeline
self.assertEquals(bus.__gstrefcount__, 2)
del pipeline
if not pygobject_2_13:
self.failUnless(self.gccollect())
self.assertEquals(bus.__gstrefcount__, 1)
class PipelineAndBus(TestCase):
def setUp(self):
TestCase.setUp(self)
self.pipeline = gst.Pipeline('test-pipeline')
source = gst.element_factory_make('fakesrc', 'source')
sink = gst.element_factory_make('fakesink', 'sink')
self.pipeline.add(source, sink)
gst.element_link_many(source, sink)
self.bus = self.pipeline.get_bus()
self.assertEquals(self.bus.__gstrefcount__, 2)
self.handler = self.bus.add_watch(self._message_received)
self.assertEquals(self.bus.__gstrefcount__, 3)
self.assertEquals(self.pipeline.__gstrefcount__, 1)
self.loop = gobject.MainLoop()
def tearDown(self):
# FIXME: fix the refcount issues with the bus/pipeline
# flush the bus to be able to assert on the pipeline refcount
#while self.pipeline.__gstrefcount__ > 1:
self.gccollect()
# one for the pipeline, two for the snake
# three for the watch now shake shake shake but don't you
self.assertEquals(self.bus.__gstrefcount__, 3)
self.failUnless(gobject.source_remove(self.handler))
self.assertEquals(self.bus.__gstrefcount__, 2)
self.gccollect()
gst.debug('THOMAS: pipeline rc %d' % self.pipeline.__gstrefcount__)
#self.assertEquals(self.pipeline.__gstrefcount__, 1)
del self.pipeline
self.gccollect()
#self.assertEquals(self.bus.__gstrefcount__, 2)
del self.bus
self.gccollect()
# the async thread can be holding a ref, Wim is going to work on this
#TestCase.tearDown(self)
def _message_received(self, bus, message):
gst.debug('received message: %s, %s' % (
message.src.get_path_string(), message.type.value_nicks[1]))
t = message.type
if t == gst.MESSAGE_STATE_CHANGED:
old, new, pen = message.parse_state_changed()
gst.debug('%r state change from %r to %r' % (
message.src.get_path_string(), old, new))
if message.src == self.pipeline and new == self.final:
self.loop.quit()
return True
def testPlaying(self):
self.final = gst.STATE_PLAYING
ret = self.pipeline.set_state(gst.STATE_PLAYING)
self.assertEquals(ret, gst.STATE_CHANGE_ASYNC)
# go into a main loop to wait for messages
self.loop.run()
# we go to READY so we get messages; going to NULL would set
# the bus flushing
self.final = gst.STATE_READY
ret = self.pipeline.set_state(gst.STATE_READY)
self.assertEquals(ret, gst.STATE_CHANGE_SUCCESS)
self.loop.run()
# FIXME: not setting to NULL causes a deadlock; we might want to
# fix this in the bindings
self.assertEquals(self.pipeline.set_state(gst.STATE_NULL),
gst.STATE_CHANGE_SUCCESS)
self.assertEquals(self.pipeline.get_state(),
(gst.STATE_CHANGE_SUCCESS, gst.STATE_NULL, gst.STATE_VOID_PENDING))
self.gccollect()
class TestPipeSub(gst.Pipeline):
def do_handle_message(self, message):
self.debug('do_handle_message')
gst.Pipeline.do_handle_message(self, message)
self.type = message.type
gobject.type_register(TestPipeSub)
class TestPipeSubSub(TestPipeSub):
def do_handle_message(self, message):
self.debug('do_handle_message')
TestPipeSub.do_handle_message(self, message)
gobject.type_register(TestPipeSubSub)
# see http://bugzilla.gnome.org/show_bug.cgi?id=577735
class TestSubClass(TestCase):
def setUp(self):
self.gctrack()
def tearDown(self):
self.gccollect()
self.gcverify()
def testSubClass(self):
p = TestPipeSub()
u = gst.element_factory_make('uridecodebin')
self.assertEquals(u.__grefcount__, 1)
self.failIf(getattr(p, 'type', None))
# adding uridecodebin triggers a clock-provide message;
# this message should be dropped, and thus not affect
# the refcount of u beyond the parenting.
p.add(u)
self.assertEquals(getattr(p, 'type', None), gst.MESSAGE_CLOCK_PROVIDE)
self.assertEquals(u.__grefcount__, 2)
del p
self.assertEquals(u.__grefcount__, 1)
def testSubSubClass(self):
# Edward is worried that a subclass of a subclass will screw up
# the refcounting wrt. GST_BUS_DROP
p = TestPipeSubSub()
u = gst.element_factory_make('uridecodebin')
self.assertEquals(u.__grefcount__, 1)
self.failIf(getattr(p, 'type', None))
p.add(u)
self.assertEquals(getattr(p, 'type', None), gst.MESSAGE_CLOCK_PROVIDE)
self.assertEquals(u.__grefcount__, 2)
del p
self.assertEquals(u.__grefcount__, 1)
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,68 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2002 David I. Lehn
# Copyright (C) 2004 Johan Dahlin
# Copyright (C) 2005 Edward Hervey
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import sys
import gc
from common import gst, unittest, TestCase
class RegistryTest(TestCase):
def setUp(self):
self.registry = gst.registry_get_default()
self.plugins = self.registry.get_plugin_list()
TestCase.setUp(self)
def testGetDefault(self):
assert(self.registry)
def testPluginList(self):
names = map(lambda p: p.get_name(), self.plugins)
self.failUnless('staticelements' in names)
def testGetPathList(self):
# FIXME: this returns an empty list; probably due to core;
# examine problem
paths = self.registry.get_path_list()
class RegistryFeatureTest(TestCase):
def setUp(self):
self.registry = gst.registry_get_default()
self.plugins = self.registry.get_plugin_list()
self.efeatures = self.registry.get_feature_list(gst.TYPE_ELEMENT_FACTORY)
self.tfeatures = self.registry.get_feature_list(gst.TYPE_TYPE_FIND_FACTORY)
self.ifeatures = self.registry.get_feature_list(gst.TYPE_INDEX_FACTORY)
TestCase.setUp(self)
def testFeatureList(self):
self.assertRaises(TypeError, self.registry.get_feature_list, "kaka")
elements = map(lambda f: f.get_name(), self.efeatures)
self.failUnless('fakesink' in elements)
typefinds = map(lambda f: f.get_name(), self.tfeatures)
indexers = map(lambda f: f.get_name(), self.ifeatures)
self.failUnless('memindex' in indexers)
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,62 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2006 Edward Hervey <edward@fluendo.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from common import gst, unittest, TestCase
class SegmentTest(TestCase):
def testSeekNoSize(self):
segment = gst.Segment()
segment.init(gst.FORMAT_BYTES)
# configure segment to start at 100 with no defined stop position
update = segment.set_seek(1.0, gst.FORMAT_BYTES, gst.SEEK_FLAG_NONE,
gst.SEEK_TYPE_SET, 100,
gst.SEEK_TYPE_NONE, -1)
self.assertEquals(update, True)
self.assertEquals(segment.start, 100)
self.assertEquals(segment.stop, -1)
# configure segment to stop relative, should not do anything since
# size is unknown
update = segment.set_seek(1.0, gst.FORMAT_BYTES, gst.SEEK_FLAG_NONE,
gst.SEEK_TYPE_NONE, 200,
gst.SEEK_TYPE_CUR, -100)
# the update flag is deprecated, we cannot check for proper behaviour.
#self.assertEquals(update, False)
self.assertEquals(segment.start, 100)
self.assertEquals(segment.stop, -1)
# clipping on outside range, always returns False
res, cstart, cstop = segment.clip(gst.FORMAT_BYTES, 0, 50)
self.assertEquals(res, False)
# touching lower bound but outside
res, cstart, cstop = segment.clip(gst.FORMAT_BYTES, 50, 100)
self.assertEquals(res, False)
# partially inside
res, cstart, cstop = segment.clip(gst.FORMAT_BYTES, 50, 150)
self.assertEquals(res, True)
self.assertEquals(cstart, 100)
self.assertEquals(cstop, 150)
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,123 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2002 David I. Lehn
# Copyright (C) 2004 Johan Dahlin
# Copyright (C) 2005 Edward Hervey
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import sys
from common import gst, unittest, TestCase
class StructureTest(TestCase):
def setUp(self):
TestCase.setUp(self)
self.struct = gst.structure_from_string('video/x-raw-yuv,width=10,foo="bar",pixel-aspect-ratio=1/2,framerate=5/1,boolean=(boolean)true')
def testName(self):
assert self.struct.get_name() == 'video/x-raw-yuv'
self.struct.set_name('foobar')
assert self.struct.get_name() == 'foobar'
def testInt(self):
assert self.struct.has_key('width')
assert isinstance(self.struct['width'], int)
assert self.struct['width'] == 10, self.struct['width']
self.struct['width'] = 5
assert self.struct.has_key('width')
assert isinstance(self.struct['width'], int)
assert self.struct['width'] == 5, self.struct['width']
def testString(self):
assert self.struct.has_key('foo')
assert isinstance(self.struct['foo'], unicode)
assert self.struct['foo'] == 'bar', self.struct['foo']
self.struct['foo'] = 'baz'
assert self.struct.has_key('foo')
assert isinstance(self.struct['foo'], unicode)
assert self.struct['foo'] == 'baz', self.struct['foo']
def testBoolean(self):
assert self.struct.has_key('boolean')
assert isinstance(self.struct['boolean'], bool)
assert self.struct['boolean'] == True, self.struct['boolean']
self.struct['boolean'] = False
assert self.struct.has_key('boolean')
assert isinstance(self.struct['boolean'], bool)
assert self.struct['boolean'] == False, self.struct['boolean']
def testCreateInt(self):
self.struct['integer'] = 5
assert self.struct.has_key('integer')
assert isinstance(self.struct['integer'], int)
assert self.struct['integer'] == 5, self.struct['integer']
def testGstValue(self):
s = self.struct
s['fourcc'] = gst.Fourcc('XVID')
assert s['fourcc'].fourcc == 'XVID'
s['frac'] = gst.Fraction(3,4)
assert s['frac'].num == 3
assert s['frac'].denom == 4
s['fracrange'] = gst.FractionRange(gst.Fraction(0,1),
gst.Fraction(25,3))
assert s['fracrange'].low.num == 0
assert s['fracrange'].low.denom == 1
assert s['fracrange'].high.num == 25
assert s['fracrange'].high.denom == 3
s['intrange'] = gst.IntRange(5,21)
assert s['intrange'].low == 5
assert s['intrange'].high == 21
s['doublerange'] = gst.DoubleRange(6.,21.)
assert s['doublerange'].low == 6.
assert s['doublerange'].high == 21.
s['fixedlist'] = (4, 5, 6)
assert isinstance(s['fixedlist'], tuple)
assert s['fixedlist'] == (4, 5, 6)
s['list'] = [4, 5, 6]
assert isinstance(s['list'], list)
assert s['list'] == [4, 5, 6]
s['boolean'] = True
assert isinstance(s['boolean'], bool)
assert s['boolean'] == True
# finally, some recursive tests
s['rflist'] = ([(['a', 'b'], ['c', 'd']),'e'], ['f', 'g'])
assert s['rflist'] == ([(['a', 'b'], ['c', 'd']),'e'], ['f', 'g'])
s['rlist'] = [([(['a', 'b'], ['c', 'd']),'e'], ['f', 'g']), 'h']
assert s['rlist'] == [([(['a', 'b'], ['c', 'd']),'e'], ['f', 'g']), 'h']
def testStructureChange(self):
assert self.struct['framerate'] == gst.Fraction(5, 1)
self.struct['framerate'] = gst.Fraction(10, 1)
assert self.struct['framerate'] == gst.Fraction(10, 1)
self.struct['pixel-aspect-ratio'] = gst.Fraction(4, 2)
assert self.struct['pixel-aspect-ratio'].num == 2
assert self.struct['pixel-aspect-ratio'].denom == 1
def testKeys(self):
k = self.struct.keys()
self.failUnless(k)
self.assertEquals(len(k), 5)
self.failUnless("width" in k)
self.failUnless("foo" in k)
self.failUnless("framerate" in k)
self.failUnless("pixel-aspect-ratio" in k)
self.failUnless("boolean" in k)
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,71 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2007 Johan Dahlin
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from common import gst, TestCase
class TestTagList(TestCase):
def testContains(self):
taglist = gst.TagList()
self.failIf('key' in taglist)
taglist['key'] = 'value'
self.failUnless('key' in taglist)
def testLength(self):
taglist = gst.TagList()
self.assertEqual(len(taglist), 0)
taglist['key1'] = 'value'
taglist['key2'] = 'value'
self.assertEqual(len(taglist), 2)
def testKeys(self):
taglist = gst.TagList()
self.assertEqual(taglist.keys(), [])
taglist['key1'] = 'value'
taglist['key2'] = 'value'
keys = taglist.keys()
keys.sort()
self.assertEqual(keys, ['key1', 'key2'])
def testUnicode(self):
taglist = gst.TagList()
# normal ASCII text
taglist[gst.TAG_ARTIST] = 'Artist'
self.failUnless(isinstance(taglist[gst.TAG_ARTIST], unicode))
self.assertEquals(taglist[gst.TAG_ARTIST], u'Artist')
self.assertEquals(taglist[gst.TAG_ARTIST], 'Artist')
# normal ASCII text as unicode
taglist[gst.TAG_ARTIST] = u'Artist'
self.failUnless(isinstance(taglist[gst.TAG_ARTIST], unicode))
self.assertEquals(taglist[gst.TAG_ARTIST], u'Artist')
self.assertEquals(taglist[gst.TAG_ARTIST], 'Artist')
# real unicode
taglist[gst.TAG_ARTIST] = u'S\xc3\xadgur R\xc3\xb3s'
self.failUnless(isinstance(taglist[gst.TAG_ARTIST], unicode))
self.assertEquals(taglist[gst.TAG_ARTIST], u'S\xc3\xadgur R\xc3\xb3s')
def testUnsignedInt(self):
taglist = gst.TagList()
taglist[gst.TAG_TRACK_NUMBER] = 1
vorbis = gst.tag.to_vorbis_comments(taglist, gst.TAG_TRACK_NUMBER)
self.assertEquals(vorbis, ['TRACKNUMBER=1'])

View file

@ -0,0 +1,65 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2008 Alessandro Decina
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from common import gst, unittest, TestCase, pygobject_2_13
import sys
import time
class TypeFindTest(TestCase):
def testTypeFind(self):
def application_awesome_type_find(typefind, arg1, arg2):
self.failUnlessEqual(arg1, 'arg1')
self.failUnlessEqual(arg2, 'arg2')
data = typefind.peek(0, 5)
self.failUnless(data == '', 'peek out of length??')
data = typefind.peek(0, 0)
self.failUnless(data == '', '0 peek??')
data = typefind.peek(3, 1)
self.failUnless(data == 'M')
data = typefind.peek(0, 4)
self.failUnless(data == 'AWSM')
typefind.suggest(gst.TYPE_FIND_MAXIMUM,
gst.Caps('application/awesome'))
res = gst.type_find_register('application/awesome', gst.RANK_PRIMARY,
application_awesome_type_find, ['.twi'],
gst.Caps('application/awesome'), 'arg1', 'arg2')
self.failUnless(res, 'type_find_register failed')
factory = None
factories = gst.type_find_factory_get_list()
for typefind_factory in factories:
if typefind_factory.get_name() == 'application/awesome':
factory = typefind_factory
break
self.failUnless(factory is not None)
obj = gst.Pad('src', gst.PAD_SRC)
buffer = gst.Buffer('AWSM')
caps, probability = gst.type_find_helper_for_buffer(obj, buffer)
self.failUnlessEqual(str(caps), 'application/awesome')
self.failUnlessEqual(probability, gst.TYPE_FIND_MAXIMUM)

53
testsuite/old/test_xml.py Normal file
View file

@ -0,0 +1,53 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2002 David I. Lehn
# Copyright (C) 2004 Johan Dahlin
# Copyright (C) 2005 Edward Hervey
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from common import gst, unittest, TestCase
class PadTest(TestCase):
def testQuery(self):
# don't run this test if we don't have the libxml2 module
try:
import libxml2
except:
return
xml = gst.XML()
xml.parse_memory("""<?xml version="1.0"?>
<gstreamer xmlns:gst="http://gstreamer.net/gst-core/1.0/">
<gst:element>
<gst:name>test-pipeline</gst:name>
<gst:type>pipeline</gst:type>
<gst:param>
<gst:name>name</gst:name>
<gst:value>test-pipeline</gst:value>
</gst:param>
</gst:element>
</gstreamer>""")
elements = xml.get_topelements()
assert len(elements) == 1
element = elements[0]
assert isinstance(element, gst.Pipeline)
assert element.get_name() == 'test-pipeline'
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,56 @@
#include "pygobject.h"
#include "test-object.h"
#include <gst/gst.h>
#include <glib-object.h>
static PyObject *
_wrap_get_object (PyObject * self)
{
GObject *obj;
obj = g_object_new (TEST_TYPE_OBJECT, NULL);
if (!obj) {
return NULL;
}
return pygobject_new (obj);
}
static PyObject *
_wrap_emit_event (PyObject * self, PyObject * args)
{
PyGObject *obj;
GstEventType event_type = GST_EVENT_UNKNOWN;
GstEvent *event;
if (!PyArg_ParseTuple (args, "O|i", &obj, &event_type))
return NULL;
event = gst_event_new_custom (event_type, NULL);
g_signal_emit_by_name (G_OBJECT (obj->obj), "event", event);
gst_mini_object_unref (GST_MINI_OBJECT (event));
Py_INCREF (Py_None);
return Py_None;
}
static PyMethodDef testhelper_methods[] = {
{"get_object", (PyCFunction) _wrap_get_object, METH_NOARGS},
{"emit_event", (PyCFunction) _wrap_emit_event, METH_VARARGS},
{NULL, NULL}
};
void
inittesthelper ()
{
PyObject *m, *d;
init_pygobject ();
gst_init (NULL, NULL);
m = Py_InitModule ("testhelper", testhelper_methods);
d = PyModule_GetDict (m);
}

View file

@ -0,0 +1,34 @@
import os
import sys
import imp
class GstOverrideImport:
def find_module(self, fullname, path=None):
if fullname in ('gi.overrides.Gst', 'gi.overrides._gi_gst'):
return self
return None
def load_module(self, name):
if name in sys.modules:
return sys.modules[name]
fp, pathname, description = imp.find_module(name.split('.')[-1], [
os.environ.get('GST_OVERRIDE_SRC_PATH'),
os.environ.get('GST_OVERRIDE_BUILD_PATH'),
])
try:
module = imp.load_module(name, fp, pathname, description)
finally:
if fp:
fp.close()
sys.modules[name] = module
return module
if sys.version_info.major >= 3:
sys.meta_path.insert(0, GstOverrideImport())
else:
import gi.overrides
gi.overrides.__path__.append(os.environ.get('GST_OVERRIDE_SRC_PATH'))
gi.overrides.__path__.append(os.environ.get('GST_OVERRIDE_BUILD_PATH'))

526
testsuite/python.supp Normal file
View file

@ -0,0 +1,526 @@
#
# This is a valgrind suppression file that should be used when using valgrind.
#
# Here's an example of running valgrind:
#
# cd python/dist/src
# valgrind --tool=memcheck --suppressions=Misc/valgrind-python.supp \
# ./python -E -tt ./Lib/test/regrtest.py -u bsddb,network
#
# You must edit Objects/obmalloc.c and uncomment Py_USING_MEMORY_DEBUGGER
# to use the preferred suppressions with Py_ADDRESS_IN_RANGE.
#
# If you do not want to recompile Python, you can uncomment
# suppressions for PyObject_Free and PyObject_Realloc.
#
# See Misc/README.valgrind for more information.
# all tool names: Addrcheck,Memcheck,cachegrind,helgrind,massif
{
ADDRESS_IN_RANGE/Invalid read of size 4
Memcheck:Addr4
fun:Py_ADDRESS_IN_RANGE
}
{
ADDRESS_IN_RANGE/Invalid read of size 4
Memcheck:Value4
fun:Py_ADDRESS_IN_RANGE
}
{
ADDRESS_IN_RANGE/Invalid read of size 8 (x86_64)
Memcheck:Value8
fun:Py_ADDRESS_IN_RANGE
}
{
ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
Memcheck:Cond
fun:Py_ADDRESS_IN_RANGE
}
{
ADDRESS_IN_RANGE/Invalid read of size 4
Memcheck:Addr4
fun:PyObject_Free
}
{
ADDRESS_IN_RANGE/Invalid read of size 8 (x86_64)
Memcheck:Addr8
fun:PyObject_Free
}
{
ADDRESS_IN_RANGE/Invalid read of size 4
Memcheck:Value4
fun:PyObject_Free
}
{
ADDRESS_IN_RANGE/Invalid read of size 8 (x86_64)
Memcheck:Value8
fun:PyObject_Free
}
{
ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
Memcheck:Cond
fun:PyObject_Free
}
{
ADDRESS_IN_RANGE/Invalid read of size 4
Memcheck:Addr4
fun:PyObject_Realloc
}
{
ADDRESS_IN_RANGE/Invalid read of size 8 (x86_64)
Memcheck:Addr8
fun:PyObject_Realloc
}
{
ADDRESS_IN_RANGE/Invalid read of size 4
Memcheck:Value4
fun:PyObject_Realloc
}
{
ADDRESS_IN_RANGE/Invalid read of size 8 (x86_64)
Memcheck:Value8
fun:PyObject_Realloc
}
{
ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
Memcheck:Cond
fun:PyObject_Realloc
}
###
### All the suppressions below are for errors that occur within libraries
### that Python uses. The problems to not appear to be related to Python's
### use of the libraries.
###
{
GDBM problems, see test_gdbm
Memcheck:Param
write(buf)
fun:write
fun:gdbm_open
}
{
Avoid problem in libc on gentoo
Memcheck:Cond
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
}
{
Avoid problem in glibc on gentoo
Memcheck:Addr8
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/libc-2.3.4.so
obj:/lib/ld-2.3.4.so
fun:_dl_open
obj:/lib/libdl-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/libdl-2.3.4.so
fun:dlopen
}
{
Avoid problem in glibc on gentoo
Memcheck:Addr8
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/libc-2.3.4.so
obj:/lib/ld-2.3.4.so
fun:_dl_open
obj:/lib/libdl-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/libdl-2.3.4.so
fun:dlopen
}
{
Avoid problem in glibc on gentoo
Memcheck:Cond
obj:/lib/ld-2.3.4.so
obj:/lib/libc-2.3.4.so
obj:/lib/ld-2.3.4.so
fun:_dl_open
obj:/lib/libdl-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/libdl-2.3.4.so
fun:dlopen
}
{
Avoid problem in glibc on gentoo
Memcheck:Cond
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/libc-2.3.4.so
obj:/lib/ld-2.3.4.so
fun:_dl_open
obj:/lib/libdl-2.3.4.so
obj:/lib/ld-2.3.4.so
obj:/lib/libdl-2.3.4.so
fun:dlopen
}
{
Avoid problems w/readline doing a putenv and leaking on exit
Memcheck:Leak
fun:malloc
fun:xmalloc
fun:sh_set_lines_and_columns
fun:_rl_get_screen_size
fun:_rl_init_terminal_io
obj:/lib/libreadline.so.4.3
fun:rl_initialize
fun:setup_readline
fun:initreadline
fun:_PyImport_LoadDynamicModule
fun:load_module
fun:import_submodule
fun:load_next
fun:import_module_ex
fun:PyImport_ImportModuleEx
}
{
Mysterious leak that seems to deal w/pthreads
Memcheck:Leak
fun:calloc
obj:/lib/ld-2.3.4.so
obj:/lib/ld-2.3.4.so
fun:_dl_allocate_tls
fun:__pthread_initialize_minimal
}
{
Mysterious leak that seems to deal w/pthreads
Memcheck:Leak
fun:memalign
obj:/lib/ld-2.3.4.so
fun:_dl_allocate_tls
fun:__pthread_initialize_minimal
}
###
### These occur from somewhere within the SSL, when running
### test_socket_sll. They are too general to leave on by default.
###
###{
### somewhere in SSL stuff
### Memcheck:Cond
### fun:memset
###}
###{
### somewhere in SSL stuff
### Memcheck:Value4
### fun:memset
###}
###
###{
### somewhere in SSL stuff
### Memcheck:Cond
### fun:MD5_Update
###}
###
###{
### somewhere in SSL stuff
### Memcheck:Value4
### fun:MD5_Update
###}
#
# All of these problems come from using test_socket_ssl
#
{
from test_socket_ssl
Memcheck:Cond
fun:BN_bin2bn
}
{
from test_socket_ssl
Memcheck:Cond
fun:BN_num_bits_word
}
{
from test_socket_ssl
Memcheck:Value4
fun:BN_num_bits_word
}
{
from test_socket_ssl
Memcheck:Cond
fun:BN_mod_exp_mont_word
}
{
from test_socket_ssl
Memcheck:Cond
fun:BN_mod_exp_mont
}
{
from test_socket_ssl
Memcheck:Param
write(buf)
fun:write
obj:/usr/lib/libcrypto.so.0.9.7
}
{
from test_socket_ssl
Memcheck:Cond
fun:RSA_verify
}
{
from test_socket_ssl
Memcheck:Value4
fun:RSA_verify
}
{
from test_socket_ssl
Memcheck:Value4
fun:DES_set_key_unchecked
}
{
from test_socket_ssl
Memcheck:Value4
fun:DES_encrypt2
}
{
from test_socket_ssl
Memcheck:Cond
obj:/usr/lib/libssl.so.0.9.7
}
{
from test_socket_ssl
Memcheck:Value4
obj:/usr/lib/libssl.so.0.9.7
}
{
from test_socket_ssl
Memcheck:Cond
fun:BUF_MEM_grow_clean
}
{
from test_socket_ssl
Memcheck:Cond
fun:memcpy
fun:ssl3_read_bytes
}
{
from test_socket_ssl
Memcheck:Cond
fun:SHA1_Update
}
{
from test_socket_ssl
Memcheck:Value4
fun:SHA1_Update
}
# python init memleak
{
Py_Main memleak
Memcheck:Leak
fun:malloc
fun:PyObject_Malloc
fun:_PyObject_GC_Malloc
fun:_PyObject_GC_*
fun:*
fun:*
fun:*
fun:*
fun:Py_InitializeEx
}
{
Py_Main memleak
Memcheck:Leak
fun:malloc
fun:PyObject_Malloc
fun:_PyObject_GC_Malloc
fun:_PyObject_GC_*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:Py_InitializeEx
}
{
Py_Main memleak v2
Memcheck:Leak
fun:malloc
fun:_PyObject_GC_Malloc
fun:_PyObject_GC_New
fun:*
fun:*
fun:*
fun:*
fun:*
fun:Py_InitializeEx
}
{
Read compiled module memleak
Memcheck:Leak
fun:malloc
fun:PyObject_Malloc
fun:_PyObject_GC_Malloc
fun:_PyObject_GC_NewVar
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:read_compiled_module
}
{
PyRun_SimpleFileExFlags memleak
Memcheck:Leak
fun:malloc
fun:_PyObject_GC_Malloc
fun:_PyObject_GC_New*
fun:*
fun:*
fun:*
fun:PyRun_SimpleFileExFlags
}
# memleak in update_keyword_args
{
update_keyword_args
Memcheck:Leak
fun:malloc
fun:_PyObject_GC_Malloc
fun:*
fun:*
fun:update_keyword_args
}
# memleaks/conds in import_submodule
{
memleak in import_submodule
Memcheck:Cond
fun:strcpy
fun:find_module
}
{
wrong jump in import_submodule
Memcheck:Cond
fun:find_module
fun:import_submodule
}
{
wrong jump in import_submodule
Memcheck:Cond
fun:find_module
fun:load_package
fun:load_module
fun:import_submodule
}
{
Use of uninitialised value of size 4
Memcheck:Value4
fun:strcpy
fun:find_module
}
## KNOWN MEMORY LEAK in gst_element_state_get_name
## See gstreamer/gst/gstutils.c
{
Known leak in gst_element_state_get_name
Memcheck:Leak
fun:*
fun:*
fun:*
fun:*
fun:g_strdup_printf
fun:gst_element_state_get_name
}
## Suppressions for FC5 64bit
{
Wrong jump in PyImport_ImportModuleEx
Memcheck:Cond
fun:__strcpy_chk
obj:/usr/lib64/libpython2.4.so.1.0
obj:/usr/lib64/libpython2.4.so.1.0
obj:/usr/lib64/libpython2.4.so.1.0
fun:PyImport_ImportModuleEx
}
{
Wrong jump in PyImport_ImportModuleEx
Memcheck:Cond
fun:__strcpy_chk
fun:PyImport_ImportModuleEx
}
{
Wrong jump in PyImport_ImportModuleEx
Memcheck:Cond
fun:__strcpy_chk
obj:/usr/lib64/libpython2.4.so.1.0
obj:/usr/lib64/libpython2.4.so.1.0
fun:PyObject_Call
fun:PyObject_CallFunction
obj:/usr/lib64/libpython2.4.so.1.0
obj:/usr/lib64/libpython2.4.so.1.0
obj:/usr/lib64/libpython2.4.so.1.0
fun:PyImport_ImportModuleEx
}
{
Wrong jump in PyUnicode_Decode
Memcheck:Cond
fun:PyUnicode_Decode
}

View file

@ -0,0 +1,46 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# identity.py
# 2016 Marianna S. Buschle <msb@qtec.com>
#
# Simple identity element in python
#
# You can run the example from the source doing from gst-python/:
#
# $ export GST_PLUGIN_PATH=$GST_PLUGIN_PATH:$PWD/plugin:$PWD/examples/plugins
# $ GST_DEBUG=python:4 gst-launch-1.0 fakesrc num-buffers=10 ! identity_py ! fakesink
import gi
gi.require_version('Gst', '1.0')
gi.require_version('GstBase', '1.0')
from gi.repository import Gst, GObject, GstBase
Gst.init(None)
#
# Simple Identity element created entirely in python
#
class Identity(GstBase.BaseTransform):
__gstmetadata__ = ('Identity Python','Transform', \
'Simple identity element written in python', 'Marianna S. Buschle')
__gsttemplates__ = (Gst.PadTemplate.new("src",
Gst.PadDirection.SRC,
Gst.PadPresence.ALWAYS,
Gst.Caps.new_any()),
Gst.PadTemplate.new("sink",
Gst.PadDirection.SINK,
Gst.PadPresence.ALWAYS,
Gst.Caps.new_any()))
def __init__(self):
self.transformed = False
def do_transform_ip(self, buffer):
self.transformed = True
return Gst.FlowReturn.OK
GObject.type_register(Identity)
__gstelementfactory__ = ("test_identity_py", Gst.Rank.NONE, Identity)

70
testsuite/runtests.py Normal file
View file

@ -0,0 +1,70 @@
#!/usr/bin/env python3
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2002 David I. Lehn
# Copyright (C) 2004 Johan Dahlin
# Copyright (C) 2005 Edward Hervey
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import os
import sys
import unittest
def _testcases(filenames):
"""Yield testcases out of filenames."""
for filename in filenames:
if filename.endswith(".py"):
yield filename[:-3]
def _tests_suite():
"""Pick which tests to run."""
testcase = os.getenv("TESTCASE")
if testcase:
testcases = [testcase]
else:
testcases = _testcases(sys.argv[1:])
loader = unittest.TestLoader()
return loader.loadTestsFromNames(testcases)
def setup():
return
if __name__ == "__main__":
setup()
# Set verbosity.
descriptions = 1
verbosity = 1
if 'VERBOSE' in os.environ:
descriptions = 2
verbosity = 2
suite = _tests_suite()
if not list(suite):
raise Exception("No tests found")
# Run the tests.
testRunner = unittest.TextTestRunner(descriptions=descriptions,
verbosity=verbosity)
result = testRunner.run(suite)
if result.failures or result.errors:
sys.exit(1)

116
testsuite/test_gst.py Normal file
View file

@ -0,0 +1,116 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# Copyright (C) 2009 Thomas Vander Stichele
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import sys
import overrides_hack
overrides_hack
from common import TestCase, unittest
from gi.repository import Gst
class TimeArgsTest(TestCase):
def testNoneTime(self):
self.assertRaises(TypeError, Gst.TIME_ARGS, None)
def testStringTime(self):
self.assertRaises(TypeError, Gst.TIME_ARGS, "String")
def testClockTimeNone(self):
self.assertEquals(Gst.TIME_ARGS(Gst.CLOCK_TIME_NONE), 'CLOCK_TIME_NONE')
def testOneSecond(self):
self.assertEquals(Gst.TIME_ARGS(Gst.SECOND), '0:00:01.000000000')
class TestNotInitialized(TestCase):
def testNotInitialized(self):
if sys.version_info >= (3, 0):
assert_type = Gst.NotInitialized
else:
assert_type = TypeError
with self.assertRaises(assert_type):
Gst.Caps.from_string("audio/x-raw")
with self.assertRaises(assert_type):
Gst.Structure.from_string("audio/x-raw")
with self.assertRaises(assert_type):
Gst.ElementFactory.make("identity", None)
def testNotDeinitialized(self):
Gst.init(None)
assert(Gst.Caps.from_string("audio/x-raw"))
assert(Gst.Structure.from_string("audio/x-raw"))
assert(Gst.ElementFactory.make("identity", None))
Gst.deinit()
if sys.version_info >= (3, 0):
assert_type = Gst.NotInitialized
else:
assert_type = TypeError
with self.assertRaises(assert_type):
Gst.Caps.from_string("audio/x-raw")
with self.assertRaises(assert_type):
Gst.Structure.from_string("audio/x-raw")
with self.assertRaises(assert_type):
Gst.ElementFactory.make("identity", None)
class TestStructure(TestCase):
def test_new(self):
Gst.init(None)
test = Gst.Structure('test', test=1)
self.assertEqual(test['test'], 1)
test = Gst.Structure('test,test=1')
self.assertEqual(test['test'], 1)
class TestBin(TestCase):
def test_add_pad(self):
Gst.init(None)
self.assertEqual(Gst.ElementFactory.make("bin", None).sinkpads, [])
class TestBufferMap(TestCase):
def test_map_unmap_manual(self):
Gst.init(None)
buf = Gst.Buffer.new_wrapped([42])
info = buf.map(Gst.MapFlags.READ | Gst.MapFlags.WRITE)
self.assertEqual(info.data[0], 42)
buf.unmap(info)
with self.assertRaises(ValueError):
info.data[0]
def test_map_unmap_context(self):
Gst.init(None)
buf = Gst.Buffer.new_wrapped([42])
with buf.map(Gst.MapFlags.READ | Gst.MapFlags.WRITE) as info:
self.assertEqual(info.data[0], 42)
with self.assertRaises(ValueError):
info.data[0]
if __name__ == "__main__":
unittest.main()

42
testsuite/test_plugin.py Normal file
View file

@ -0,0 +1,42 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2007 Johan Dahlin
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import overrides_hack
overrides_hack
from common import TestCase, unittest
import gi
gi.require_version("Gst", "1.0")
from gi.repository import Gst
class TestPlugin(TestCase):
def testLoad(self):
Gst.init(None)
p = Gst.parse_launch ("fakesrc ! test_identity_py name=id ! fakesink")
assert p.get_by_name("id").transformed == False
p.set_state(Gst.State.PLAYING)
p.get_state(Gst.CLOCK_TIME_NONE)
p.set_state(Gst.State.NULL)
assert p.get_by_name("id").transformed == True
if __name__ == "__main__":
unittest.main()

402
testsuite/test_types.py Normal file
View file

@ -0,0 +1,402 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2007 Johan Dahlin
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import overrides_hack
overrides_hack
from common import TestCase
import unittest, sys
import gi
gi.require_version("Gst", "1.0")
from gi.repository import Gst
Gst.init(None)
Gst.DoubleRange = Gst.DoubleRange
class TestDoubleRange(TestCase):
def testConstructor(self):
Gst.init(None)
Gst.DoubleRange = Gst.DoubleRange(1.2, 3.4)
self.assertEqual(r.start, 1.2)
self.assertEqual(r.stop, 3.4)
self.assertRaises(TypeError, Gst.DoubleRange, {}, 2)
self.assertRaises(TypeError, Gst.DoubleRange, 2, ())
self.assertRaises(TypeError, Gst.DoubleRange, 2, 1)
self.assertRaises(TypeError, Gst.DoubleRange)
def testRepr(self):
Gst.init(None)
self.assertEqual(repr(Gst.DoubleRange(1,2)), '<Gst.DoubleRange [1.0,2.0]>')
def testGetValue(self):
Gst.init(None)
st = Gst.Structure.new_empty("video/x-raw")
st["range"] = Gst.DoubleRange(1,2)
value = st["range"]
self.assertEqual(value.start, 1.0)
self.assertEqual(value.stop, 2.0)
class TestFraction(TestCase):
def testConstructor(self):
Gst.init(None)
frac = Gst.Fraction(1, 2)
self.assertEqual(frac.num, 1)
self.assertEqual(frac.denom, 2)
frac = Gst.Fraction(1)
self.assertEqual(frac.num, 1)
self.assertEqual(frac.denom, 1)
self.assertRaises(TypeError, Gst.Fraction)
def testRepr(self):
Gst.init(None)
self.assertEqual(repr(Gst.Fraction(1, 2)), '<Gst.Fraction 1/2>')
def testEqNe(self):
Gst.init(None)
frac = Gst.Fraction(1, 2)
self.assertEqual(frac, frac)
self.assertEqual(Gst.Fraction(1, 2), Gst.Fraction(1, 2))
self.assertEqual(Gst.Fraction(2, 4), Gst.Fraction(1, 2))
self.assertNotEqual(Gst.Fraction(1, 3), Gst.Fraction(1, 2))
self.assertNotEqual(Gst.Fraction(2, 1), Gst.Fraction(1, 2))
def testMul(self):
Gst.init(None)
self.assertEqual(Gst.Fraction(1, 2) * Gst.Fraction(1, 2), Gst.Fraction(1, 4))
self.assertEqual(Gst.Fraction(2, 3) * Gst.Fraction(4, 5), Gst.Fraction(8, 15))
self.assertEqual(Gst.Fraction(1, 3) * Gst.Fraction(4), Gst.Fraction(4, 3))
self.assertEqual(Gst.Fraction(1, 3) * 4, Gst.Fraction(4, 3))
def testRMul(self):
Gst.init(None)
self.assertEqual(2 * Gst.Fraction(1, 2), Gst.Fraction(1))
self.assertEqual(4 * Gst.Fraction(1, 2), Gst.Fraction(2))
self.assertEqual(-10 * Gst.Fraction(1, 2), Gst.Fraction(-5))
def testDiv(self):
Gst.init(None)
self.assertEqual(Gst.Fraction(1, 3) / Gst.Fraction(1, 4), Gst.Fraction(4, 3))
self.assertEqual(Gst.Fraction(2, 3) / Gst.Fraction(4, 5), Gst.Fraction(10, 12))
self.assertEqual(Gst.Fraction(1, 3) / Gst.Fraction(4), Gst.Fraction(1, 12))
self.assertEqual(Gst.Fraction(1, 3) / 4, Gst.Fraction(1, 12))
self.assertEqual(Gst.Fraction(1, 3) / 2, Gst.Fraction(1, 6))
self.assertEqual(Gst.Fraction(1, 5) / -4, Gst.Fraction(1, -20))
def testRDiv(self):
Gst.init(None)
self.assertEqual(2 / Gst.Fraction(1, 3), Gst.Fraction(6, 1))
self.assertEqual(-4 / Gst.Fraction(1, 5), Gst.Fraction(-20, 1))
def testFloat(self):
Gst.init(None)
self.assertEqual(float(Gst.Fraction(1, 2)), 0.5)
def testPropertyMarshalling(self):
Gst.init(None)
obj = Gst.ElementFactory.make("rawvideoparse")
if not obj:
obj = Gst.ElementFactory.make("rawvideoparse")
if not obj:
# no (raw)videoparse and I don't know of any elements in core or -base using
# fraction properties. Skip this test.
return
value = obj.props.framerate
self.assertEqual(value.num, 25)
self.assertEqual(value.denom, 1)
obj.props.framerate = Gst.Fraction(2, 1)
value = obj.props.framerate
self.assertEqual(value.num, 2)
self.assertEqual(value.denom, 1)
def bad():
obj.props.framerate = 1
self.assertRaises(TypeError, bad)
value = obj.props.framerate
self.assertEqual(value.num, 2)
self.assertEqual(value.denom, 1)
def testGetFractionValue(self):
Gst.init(None)
st = Gst.Structure.from_string("video/x-raw,framerate=10/1")[0]
value = st["framerate"]
self.assertEqual(value.num, 10)
self.assertEqual(value.denom, 1)
class TestFractionRange(TestCase):
def testConstructor(self):
Gst.init(None)
r = Gst.FractionRange(Gst.Fraction(1, 30), Gst.Fraction(1, 2))
self.assertEqual(r.start, Gst.Fraction(1, 30))
self.assertEqual(r.stop, Gst.Fraction(1, 2))
self.assertRaises(TypeError, Gst.FractionRange, Gst.Fraction(1, 2), Gst.Fraction(1, 30))
self.assertRaises(TypeError, Gst.FractionRange, 2, Gst.Fraction(1, 2))
self.assertRaises(TypeError, Gst.FractionRange, Gst.Fraction(1, 2), 2)
self.assertRaises(TypeError, Gst.FractionRange)
def testRepr(self):
Gst.init(None)
self.assertEqual(repr(Gst.FractionRange(Gst.Fraction(1,30), Gst.Fraction(1,2))),
'<Gst.FractionRange [1/30,1/2]>')
def testGetValue(self):
Gst.init(None)
st = Gst.Structure.new_empty("video/x-raw")
st["range"] = Gst.FractionRange(Gst.Fraction(1, 30), Gst.Fraction(1, 2))
value = st["range"]
self.assertEqual(value.start, Gst.Fraction(1, 30))
self.assertEqual(value.stop, Gst.Fraction(1, 2))
class TestDoubleRange(TestCase):
def testConstructor(self):
Gst.init(None)
r = Gst.DoubleRange(1.2, 3.4)
self.assertEqual(r.start, 1.2)
self.assertEqual(r.stop, 3.4)
self.assertRaises(TypeError, Gst.DoubleRange, {}, 2)
self.assertRaises(TypeError, Gst.DoubleRange, 2, ())
self.assertRaises(TypeError, Gst.DoubleRange, 2, 1)
self.assertRaises(TypeError, Gst.DoubleRange)
def testRepr(self):
Gst.init(None)
self.assertEqual(repr(Gst.DoubleRange(1,2)), '<Gst.DoubleRange [1.0,2.0]>')
def testGetValue(self):
Gst.init(None)
st = Gst.Structure.new_empty("video/x-raw")
st["range"] = Gst.DoubleRange(1,2)
value = st["range"]
self.assertEqual(value.start, 1.0)
self.assertEqual(value.stop, 2.0)
class TestInt64Range(TestCase):
@unittest.skipUnless(sys.version_info >= (3, 0), "requires Python 3")
def testConstructor(self):
Gst.init(None)
r = Gst.Int64Range(range(0, 10, 2))
self.assertEqual(r.range, range(0, 10, 2))
self.assertRaises(TypeError, Gst.Int64Range, range(1, 10, 2))
self.assertRaises(TypeError, Gst.Int64Range, range(0, 9, 2))
self.assertRaises(TypeError, Gst.Int64Range, range(10, 0))
self.assertRaises(TypeError, Gst.Int64Range, 1)
self.assertRaises(TypeError, Gst.Int64Range)
@unittest.skipUnless(sys.version_info >= (3, 0), "requires Python 3")
def testRepr(self):
Gst.init(None)
self.assertEqual(repr(Gst.Int64Range(range(0, 10, 2))), '<Gst.Int64Range [0,10,2]>')
@unittest.skipUnless(sys.version_info >= (3, 0), "requires Python 3")
def testGetValue(self):
Gst.init(None)
st = Gst.Structure.new_empty("video/x-raw")
st["range"] = Gst.Int64Range(range(0, 10, 2))
value = st["range"]
self.assertEqual(value, range(0, 10, 2))
class TestValueArray(TestCase):
def testConstructor(self):
Gst.init(None)
a = Gst.ValueArray((1,2,3))
self.assertEqual(a.array, [1,2,3])
self.assertRaises(TypeError, Gst.ValueArray, 1)
self.assertRaises(TypeError, Gst.ValueArray)
def testRepr(self):
Gst.init(None)
self.assertEqual(repr(Gst.ValueArray([1,2,3])), '<Gst.ValueArray <1,2,3>>')
def testPropertyMarshalling(self):
Gst.init(None)
obj = Gst.ElementFactory.make("rawvideoparse")
if not obj:
# no rawvideoparse and I don't know of any elements in core or -base using
# fraction properties. Skip this test.
return
value = obj.props.plane_strides
self.assertEqual(value[0], 320)
self.assertEqual(value[1], 160)
self.assertEqual(value[2], 160)
obj.props.plane_strides = Gst.ValueArray([640,320,320])
value = obj.props.plane_strides
self.assertEqual(value[0], 640)
self.assertEqual(value[1], 320)
self.assertEqual(value[2], 320)
def bad():
obj.props.plane_strides = 1
self.assertRaises(TypeError, bad)
value = obj.props.plane_strides
self.assertEqual(value[0], 640)
self.assertEqual(value[1], 320)
self.assertEqual(value[2], 320)
def testGetValue(self):
Gst.init(None)
st = Gst.Structure.new_empty("video/x-raw")
st["array"] = Gst.ValueArray([Gst.Fraction(1, 30), Gst.Fraction(1, 2)])
value = st["array"]
st["array"] = Gst.ValueArray(value)
self.assertEqual(value[0], Gst.Fraction(1, 30))
self.assertEqual(value[1], Gst.Fraction(1, 2))
st["matrix"] = Gst.ValueArray([Gst.ValueArray([0, 1]), Gst.ValueArray([-1, 0])])
value = st["matrix"]
self.assertEqual(value[0][0], 0)
self.assertEqual(value[0][1], 1)
self.assertEqual(value[1][0], -1)
self.assertEqual(value[1][1], 0)
class TestValueList(TestCase):
def testConstructor(self):
Gst.init(None)
a = Gst.ValueList((1,2,3))
self.assertEqual(a.array, [1,2,3])
self.assertRaises(TypeError, Gst.ValueList, 1)
self.assertRaises(TypeError, Gst.ValueList)
def testRepr(self):
Gst.init(None)
self.assertEqual(repr(Gst.ValueList([1,2,3])), '<Gst.ValueList {1,2,3}>')
def testGetValue(self):
Gst.init(None)
st = Gst.Structure.new_empty("video/x-raw")
st["framerate"] = Gst.ValueList([Gst.Fraction(1, 30), Gst.Fraction(1, 2)])
value = st["framerate"]
self.assertEqual(value[0], Gst.Fraction(1, 30))
self.assertEqual(value[1], Gst.Fraction(1, 2))
st["matrix"] = Gst.ValueList([Gst.ValueList([0, 1]), Gst.ValueList([-1 ,0])])
value = st["matrix"]
self.assertEqual(value[0][0], 0)
self.assertEqual(value[0][1], 1)
self.assertEqual(value[1][0], -1)
self.assertEqual(value[1][1], 0)
class TestIntRange(TestCase):
@unittest.skipUnless(sys.version_info >= (3, 0), "requires Python 3")
def testConstructor(self):
Gst.init(None)
r = Gst.IntRange(range(0, 10, 2))
self.assertEqual(r.range, range(0, 10, 2))
self.assertRaises(TypeError, Gst.IntRange, range(1, 10, 2))
self.assertRaises(TypeError, Gst.IntRange, range(0, 9, 2))
self.assertRaises(TypeError, Gst.IntRange, range(10, 0))
self.assertRaises(TypeError, Gst.IntRange, 1)
self.assertRaises(TypeError, Gst.IntRange)
@unittest.skipUnless(sys.version_info >= (3, 0), "requires Python 3")
def testRepr(self):
Gst.init(None)
self.assertEqual(repr(Gst.IntRange(range(0, 10, 2))), '<Gst.IntRange [0,10,2]>')
@unittest.skipUnless(sys.version_info >= (3, 0), "requires Python 3")
def testGetValue(self):
Gst.init(None)
st = Gst.Structure.new_empty("video/x-raw")
st["range"] = Gst.IntRange(range(0, 10, 2))
value = st["range"]
self.assertEqual(value, range(0, 10, 2))
class TestBitmask(TestCase):
def testConstructor(self):
Gst.init(None)
r = Gst.Bitmask(1 << 5)
self.assertEqual(r, 1 << 5)
def testGetValue(self):
Gst.init(None)
self.assertEqual(Gst.Structure('test,test=(bitmask)0x20')['test'], 1 << 5)
def testStr(self):
Gst.init(None)
r = Gst.Bitmask(1 << 5)
if sys.version_info >= (3, 0):
self.assertEqual(str(r), '0x20')
else:
self.assertEqual(str(r), '0x20L')