Merging gst-plugins-ugly

This commit is contained in:
Thibault Saunier 2021-09-24 16:14:42 -03:00
commit 43c69ab201
177 changed files with 92422 additions and 0 deletions

15
.gitignore vendored Normal file
View file

@ -0,0 +1,15 @@
*~
*.bak
Build
*.user
*.suo
*.ipch
*.sdf
*.opensdf
*.DS_Store
# Meson
/build
/_build
/subprojects

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"

21
AUTHORS Normal file
View file

@ -0,0 +1,21 @@
Erik Walthinsen <omega@temple-baptist.com>
Matt Howell <mhowell@users.sourceforge.net>
Brent Bradburn <bbradburn@users.sourceforge.net>
Wim Taymans <wim.taymans@chello.be>
Richard Boulton <richard@tartarus.org>
Zaheer Abbas Merali <zaheerabbas at merali dot org>
David I. Lehn <dlehn@users.sourceforge.net>
Chris Emerson <chris@tartarus.org>
Jens Thiele <karme@unforgettable.com>
Thomas Nyberg <thomas@codefactory.se>
Bastien Nocera <hadess@hadess.net>
Christian Fredrik Kalager Schaller <Uraeus@linuxrising.org>
Thomas Vander Stichele <thomas@apestaart.org>
Andy Wingo <wingo@pobox.com>
Cameron Hutchison <camh@xdna.net>
David Schleef <ds@schleef.org>
Benjamin Otte <in7y118@public.uni-hamburg.de>
Ronald Bultje <rbultje@ronald.bitfreak.net>
Julien MOUTTE <julien@moutte.net>
Jan Schmidt <thaytan@mad.scientist.com>
Arwed v. Merkatz <v.merkatz@gmx.net>

504
COPYING Normal file
View file

@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin St, 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 St, 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!

25034
ChangeLog Normal file

File diff suppressed because it is too large Load diff

12
MAINTAINERS Normal file
View file

@ -0,0 +1,12 @@
GStreamer is currently maintained by the consensus of a number
of people, including, but not limited to:
Jan Schmidt <thaytan@noraisin.net>
Wim Taymans <wim.taymans@gmail.com>
David Schleef <ds@schleef.org>
Tim-Philipp Müller <tim centricular net>
Sebastian Dröge <slomo@coaxion.net>
Maintainer-related issues should be addressed to:
gstreamer-devel@lists.freedesktop.org

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

252
README Normal file
View file

@ -0,0 +1,252 @@
GStreamer 1.19.x development series
WHAT IT IS
----------
This is GStreamer, a framework for streaming media.
WHERE TO START
--------------
We have a website at
https://gstreamer.freedesktop.org
Our documentation, including tutorials, API reference and FAQ can be found at
https://gstreamer.freedesktop.org/documentation/
You can subscribe to our mailing lists:
https://lists.freedesktop.org/mailman/listinfo/gstreamer-announce
https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel
We track bugs, feature requests and merge requests (patches) in GitLab at
https://gitlab.freedesktop.org/gstreamer/
You can join us on IRC - #gstreamer on irc.freenode.org
GStreamer 1.0 series
--------------------
Starring
GSTREAMER
The core around which all other modules revolve. Base functionality and
libraries, some essential elements, documentation, and testing.
BASE
A well-groomed and well-maintained collection of GStreamer plug-ins and
elements, spanning the range of possible types of elements one would want
to write for GStreamer.
And introducing, for the first time ever, on the development screen ...
THE GOOD
--- "Such ingratitude. After all the times I've saved your life."
A collection of plug-ins you'd want to have right next to you on the
battlefield. Shooting sharp and making no mistakes, these plug-ins have it
all: good looks, good code, and good licensing. Documented and dressed up
in tests. If you're looking for a role model to base your own plug-in on,
here it is.
If you find a plot hole or a badly lip-synced line of code in them,
let us know - it is a matter of honour for us to ensure Blondie doesn't look
like he's been walking 100 miles through the desert without water.
THE UGLY
--- "When you have to shoot, shoot. Don't talk."
There are times when the world needs a color between black and white.
Quality code to match the good's, but two-timing, backstabbing and ready to
sell your freedom down the river. These plug-ins might have a patent noose
around their neck, or a lock-up license, or any other problem that makes you
think twice about shipping them.
We don't call them ugly because we like them less. Does a mother love her
son less because he's not as pretty as the other ones ? No - she commends
him on his great personality. These plug-ins are the life of the party.
And we'll still step in and set them straight if you report any unacceptable
behaviour - because there are two kinds of people in the world, my friend:
those with a rope around their neck and the people who do the cutting.
THE BAD
--- "That an accusation?"
No perfectly groomed moustache or any amount of fine clothing is going to
cover up the truth - these plug-ins are Bad with a capital B.
They look fine on the outside, and might even appear to get the job done, but
at the end of the day they're a black sheep. Without a golden-haired angel
to watch over them, they'll probably land in an unmarked grave at the final
showdown.
Don't bug us about their quality - exercise your Free Software rights,
patch up the offender and send us the patch on the fastest steed you can
steal from the Confederates. Because you see, in this world, there's two
kinds of people, my friend: those with loaded guns and those who dig.
You dig.
The Lowdown
-----------
--- "I've never seen so many plug-ins wasted so badly."
GStreamer Plug-ins has grown so big that it's hard to separate the wheat from
the chaff. Also, distributors have brought up issues about the legal status
of some of the plug-ins we ship. To remedy this, we've divided the previous
set of available plug-ins into four modules:
- gst-plugins-base: a small and fixed set of plug-ins, covering a wide range
of possible types of elements; these are continuously kept up-to-date
with any core changes during the development series.
- We believe distributors can safely ship these plug-ins.
- People writing elements should base their code on these elements.
- These elements come with examples, documentation, and regression tests.
- gst-plugins-good: a set of plug-ins that we consider to have good quality
code, correct functionality, our preferred license (LGPL for the plug-in
code, LGPL or LGPL-compatible for the supporting library).
- We believe distributors can safely ship these plug-ins.
- People writing elements should base their code on these elements.
- gst-plugins-ugly: a set of plug-ins that have good quality and correct
functionality, but distributing them might pose problems. The license
on either the plug-ins or the supporting libraries might not be how we'd
like. The code might be widely known to present patent problems.
- Distributors should check if they want/can ship these plug-ins.
- People writing elements should base their code on these elements.
- gst-plugins-bad: a set of plug-ins that aren't up to par compared to the
rest. They might be close to being good quality, but they're missing
something - be it a good code review, some documentation, a set of tests,
a real live maintainer, or some actual wide use.
If the blanks are filled in they might be upgraded to become part of
either gst-plugins-good or gst-plugins-ugly, depending on the other factors.
- If the plug-ins break, you can't complain - instead, you can fix the
problem and send us a patch, or bribe someone into fixing them for you.
- New contributors can start here for things to work on.
PLATFORMS
---------
- Linux is of course fully supported
- FreeBSD is reported to work; other BSDs should work too; same for Solaris
- MacOS works, binary 1.x packages can be built using the cerbero build tool
- Windows works; binary 1.x packages can be built using the cerbero build tool
- MSys/MinGW builds
- Microsoft Visual Studio builds are also available and supported
- Android works, binary 1.x packages can be built using the cerbero build tool
- iOS works
INSTALLING FROM PACKAGES
------------------------
You should always prefer installing from packages first. GStreamer is
well-maintained for a number of distributions, including Fedora, Debian,
Ubuntu, Mandrake, Arch Linux, Gentoo, ...
Only in cases where you:
- want to hack on GStreamer
- want to verify that a bug has been fixed
- do not have a sane distribution
should you choose to build from source tarballs or git.
Find more information about the various packages at
https://gstreamer.freedesktop.org/download/
COMPILING FROM SOURCE TARBALLS
------------------------------
- again, make sure that you really need to install from source!
If GStreamer is one of your first projects ever that you build from source,
consider taking on an easier project.
- you need a recent version of Meson installed, see
http://mesonbuild.com/Getting-meson.html
and
https://gitlab.freedesktop.org/gstreamer/gst-build/blob/master/README.md
- run
meson build
ninja -C build
to build GStreamer.
- if you want to install it (not required, but what you usually want to do), run
ninja -C build install
- try out a simple test:
gst-launch-1.0 -v fakesrc num_buffers=5 ! fakesink
(If you didn't install GStreamer, run `./build/tools/gst-launch-1.0`)
If it outputs a bunch of messages from fakesrc and fakesink, everything is
ok.
If it did not work, keep in mind that you might need to adjust the
PATH and/or LD_LIBRARY_PATH environment variables to make the system
find GStreamer in the prefix where you installed (by default that is /usr/local).
- After this, you're ready to install gst-plugins, which will provide the
functionality you're probably looking for by now, so go on and read
that README.
COMPILING FROM GIT
------------------
You can build an uninstalled GStreamer from git for development or testing
purposes without affecting your system installation.
Get started with:
git clone https://gitlab.freedesktop.org/gstreamer/gst-build
meson build
ninja -C build
ninja -C build uninstalled
For more information, see the `gst-build` module and its documentation:
https://gitlab.freedesktop.org/gstreamer/gst-build/blob/master/README.md
PLUG-IN DEPENDENCIES AND LICENSES
---------------------------------
GStreamer is developed under the terms of the LGPL (see COPYING file for
details). Some of our plug-ins however rely on libraries which are available
under other licenses. This means that if you are distributing an application
which has a non-GPL compatible license (for instance a closed-source
application) with GStreamer, you have to make sure not to distribute GPL-linked
plug-ins.
When using GPL-linked plug-ins, GStreamer is for all practical reasons
under the GPL itself.
HISTORY
-------
The fundamental design comes from the video pipeline at Oregon Graduate
Institute, as well as some ideas from DirectMedia. It's based on plug-ins that
will provide the various codec and other functionality. The interface
hopefully is generic enough for various companies (ahem, Apple) to release
binary codecs for Linux, until such time as they get a clue and release the
source.

174
README.static-linking Normal file
View file

@ -0,0 +1,174 @@
=================================
GStreamer Static Linking README
=================================
DRAFT, April 2013
I. INTRODUCTION
It is possible to link GStreamer libraries, plugins and applications
statically, both in case of free/libre/open-source software applications
and proprietary applications. On some platforms static linking may even
be required.
However, distributing statically linked binaries using GStreamer usually
requires additional effort to stay compliant with the GNU LGPL v2.1 license.
The purpose of this document is to draw attention to this fact, and to
summarise in layman's terms what we believe is required from anyone
distributing statically linked GStreamer binaries. Most of this also
applies to dynamically linked GStreamer binaries.
II. DISCLAIMER
This document is not legal advice, nor is it comprehensive. It may use
words in ways that do not match the definition or use in the license
text. It may even be outright wrong. Read the license text for all the
details, it is the only legally binding document in this respect.
This document is primarily concerned with the implications for the
distribution of binaries based on LGPL-licensed software as imposed by
the LGPL license, but there may be other restrictions to the distribution
of such binaries, such as terms and conditions of distribution channels
(e.g. "app stores").
III. THE SPIRIT OF THE LGPL LICENSE
The GNU LGPL v2.1 license allows use of such-licensed software by
proprietary applications, but still aims to ensure that at least the
LGPL-licensed software parts remain free under all circumstances. This
means any changes to LGPL-licensed source code must be documented and
be made available on request to those who received binaries of the
software. It also means that it must be possible to make changes to the
LGPL-licensed software parts and make the application use those, as far
as that is possible. And that recipients of an application using
LGPL-licensed software are made aware of their rights according to the
LGPL license.
In an environment where GStreamer libraries and plugins are used as
dynamically-loaded shared objects (DLL/.so/.dyn files), this is usually
not a big problem, because it is fairly easy to compile a modified version
of the GStreamer libraries or LGPL plugins, and the application will/should
just pick up and use the modified version automatically. All that is needed
is for the original, LGPL-licensed source code and source code modifications
to be made available, and for a way to build the libraries or plugins for
the platform required (usually that will be using the build system scripts
that come with GStreamer, and using the typical build environment on the
system in question, but where that is not the case the needed build scripts
and/or tools would need to be provided as well).
IV. THINGS YOU NEED TO DO
* You must tell users of your application that you are using LGPL-licensed
software, which LGPL-licensed software exactly, and you must provide them
with a copy of the license so they know their rights under the LGPL.
* You must provide (on request) all the source code and all the changes
or additions you have made to the LGPL-licensed software you are using.
For GStreamer code we would recommend that the changes be provided either
in form of a branch in a git repository, or as a set of "git format-patch"-
style patches against a GStreamer release or a snapshot of a GStreamer git
repository. The patches should ideally say what was changed and why it
was changed, and there should ideally be separate patches for independent
changes.
* You must provide a way for users of your application to make changes to
the LGPL-licensed parts of the code, and re-create a full application
binary with the changes (using the standard toolchain and tools of the
target platform; if you are using a custom toolchain or custom tools
you must provide these and document how to use them to create a new
application binary).
Note that this of course does not mean that the user is allowed to
re-distribute the changed application. Nor does it mean that you have
to provide your proprietary source code - it is sufficient to provide a
ready-made compiled object file that can be relinked into an application
binary with the re-compiled LGPL components.
V. THINGS TO LOOK OUT FOR
While most GStreamer plugins and the libraries they depend on are licensed
under the LGPL or even more permissive licenses, that is not the case for
all plugins and libraries used, esp. those in the gst-plugins-ugly or
some of those in the gst-plugins-bad set of plugins.
When statically linking proprietary code, care must be taken not to
statically link plugins or libraries that are licensed under less permissive
terms than the LGPL, such as e.g. GPL-licensed libraries.
VI. SPECIAL CONSIDERATIONS FOR SPECIFIC USE-CASES
1. Proprietary GStreamer/GLib-based Application On iOS
Let's assume an individual or a company wants to distribute a proprietary
iOS application that is built on top of GStreamer and GLib through
Apple's App Store. At the time of writing the Apple iPhone developer
agreement didnt allow the bundling of shared libraries, so distributing
a proprietary iOS application with shared libraries is only possible using
distribution mechanisms outside of the App Store and/or only to jailbroken
devices, a prospect that may not appeal to our individual or company. So the
only alternative then is to link everything statically, which means the
obligations mentioned above come into play.
2. Example: Jabber on iOS
Tandberg (now Cisco) created a Jabber application for iOS, based on GStreamer.
On request they provided an LGPL compliance bundle in form of a zip file, with
roughly the following contents:
buildapp.sh
readme.txt
Jabber/Jabber-Info.plist
Jabber/libip.a [236MB binary with proprietary code]
Jabber/main.mm
Jabber/xcconfig/Application.xcconfig
Jabber/xcconfig/Debug.xcconfig
Jabber/xcconfig/Release.xcconfig
Jabber/xcconfig/Shared.xcconfig
Jabber/Resources/*.lproj/Localizable.strings
Jabber/Resources/{Images,Audio,Sounds,IB,Message Styles,Emoticons,Fonts}/*
Jabber/Resources/*
Jabber.xcodeproj/project.pbxproj
Jabber.xcodeproj/project.xcworkspace/contents.xcworkspacedata
opensource/build/config.site
opensource/build/m4/movi.m4
opensource/build/scripts/clean-deps.sh
opensource/build/scripts/fixup-makefile.sh
opensource/build/scripts/MoviMaker.py
opensource/build.sh
opensource/env.sh
opensource/Makefile
opensource/external/glib/*
opensource/external/gstreamer/{gstreamer,gst-plugins-*}/*
opensource/external/openssl/*
opensource/external/proxy-libintl/*
opensource/toolchain/darwin-x86/bin/{misc autotoools,m4,glib-mkenums,glib-genmarshal,libtool,pkg-config,etc.}
opensource/toolchain/darwin-x86/share/{aclocal,aclocal-1.11,autoconf,automake-1.11,libtool}/*
opensource/toolchain/darwin-x86/share/Config.pm
opensource/toolchain/darwin-x86/share/Config.pm.movi.in
patches/glib/glib.patch
patches/gst-plugins-bad/gst-plugins-bad.patch
patches/gst-plugins-base/gst-plugins-base.patch
patches/gst-plugins-good/gst-plugins-good.patch
patches/gstreamer/gstreamer.patch
patches/openssl/openssl.patch
readme.txt starts with "This Readme file describes how to build the Cisco
Jabber for iPad application. You need to install Xcode, but the final package
is built by running buildapp.sh." and describes how to build project,
prerequisites, the procedure in detail, and a "How to Include Provisioning
Profile Manually / Alternate Code Signing Instructions" section.
3. Random Links Which May Be Of Interest
[0] http://multinc.com/2009/08/24/compatibility-between-the-iphone-app-store-and-the-lgpl/

96
RELEASE Normal file
View file

@ -0,0 +1,96 @@
This is GStreamer gst-plugins-ugly 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

48
REQUIREMENTS Normal file
View file

@ -0,0 +1,48 @@
GStreamer uses a *large* array of tools and libraries, most of which are
optional. We have attempted to make sure that any code that depends on
optional libraries doesn't get built unless you have those libraries. If
you find this not to be the case, please, let us know by filing a bug
report at http://bugzilla.gnome.org/.
Required tools:
===============
An extra set of tools is required if you wish to build GStreamer out of
git (using autogen.sh):
autoconf 2.52 or better
automake 1.5
gettext 0.11.5
libtool v1.4 or better
pkgconfig 0.9.0 or better (http://www.freedesktop.org/software/pkgconfig/)
Required libraries:
===================
The core GStreamer libraries. See the gstreamer/ module in GStreamer cvs, or
the version that corresponds to this plugin release.
Optional libraries:
===================
This file lists supporting libraries for which gst-plugins contains plugins,
as well as their minimum version. You can find the corresponding plugins in
ext/(library)
a52dec (for the a52dec AC-3 decoder)
http://liba52.sourceforge.net/
opencore-amr (for the AMR-NB decoder and encoder and the AMR-WB decoder)
http://sourceforge.net/projects/opencore-amr/
libdvdread (for the dvdreadsrc)
http://www.dtek.chalmers.se/groups/dvd/
(optional: libcss for encrypted DVDs)
mpeg2dec (for mpeg2 related plugins and dvd playback)
http://libmpeg2.sourceforge.net/
liborc (for the liborc optimization library)
http://code.entropywave.com/download/orc/
cdio (for the cdio CDDA CD audio source)
http://savannah.gnu.org/projects/libcdio/
x264 (for the x264enc H.264 encoder)
http://www.videolan.org/developers/x264.html
sidplay FIXME

6
docs/all_index.md Normal file
View file

@ -0,0 +1,6 @@
---
short-description: Plugins from gst-plugins-ugly
...
# Plugins

1
docs/gst_api_version.in Normal file
View file

@ -0,0 +1 @@
@GST_API_VERSION@

2015
docs/gst_plugins_cache.json Normal file

File diff suppressed because it is too large Load diff

0
docs/index.md Normal file
View file

102
docs/meson.build Normal file
View file

@ -0,0 +1,102 @@
build_hotdoc = false
required_hotdoc_extensions = ['gst-extension']
plugins_cache = join_paths(meson.current_source_dir(), 'gst_plugins_cache.json')
if gst_dep.type_name() == 'internal'
gst_proj = subproject('gstreamer')
plugins_cache_generator = gst_proj.get_variable('plugins_cache_generator')
else
plugins_cache_generator = find_program(join_paths(gst_dep.get_pkgconfig_variable('libexecdir'), 'gstreamer-' + api_version, 'gst-plugins-doc-cache-generator'),
required: false)
endif
if plugins.length() == 0
message('All ugly plugins have been disabled')
elif plugins_cache_generator.found()
plugins_doc_dep = custom_target('ugly-plugins-doc-cache',
command: [plugins_cache_generator, plugins_cache, '@OUTPUT@', '@INPUT@'],
input: plugins,
output: 'gst_plugins_cache.json',
build_always_stale: true,
)
else
warning('GStreamer plugin inspector for documentation not found, can\'t update the cache')
endif
hotdoc_p = find_program('hotdoc', required: get_option('doc'))
if not hotdoc_p.found()
message('Hotdoc not found, not building the documentation')
subdir_done()
endif
hotdoc_req = '>= 0.11.0'
hotdoc_version = run_command(hotdoc_p, '--version').stdout()
if not hotdoc_version.version_compare(hotdoc_req)
if get_option('doc').enabled()
error('Hotdoc version @0@ not found, got @1@'.format(hotdoc_req, hotdoc_version))
else
message('Hotdoc version @0@ not found, got @1@'.format(hotdoc_req, hotdoc_version))
subdir_done()
endif
endif
hotdoc = import('hotdoc')
foreach extension: required_hotdoc_extensions
if not hotdoc.has_extensions(extension)
if get_option('doc').enabled()
error('Documentation enabled but @0@ missing'.format(extension))
endif
message('@0@ extension not found, not building documentation'.format(extension))
subdir_done()
endif
endforeach
build_hotdoc = true
docconf = configuration_data()
docconf.set('GST_API_VERSION', api_version)
configure_file(input : 'gst_api_version.in',
output : 'gst_api_version.md',
configuration : docconf)
root_rel = '..'
excludes = []
foreach f: []
excludes += [join_paths(meson.current_source_dir(), root_rel, '..', f)]
endforeach
libs_doc = []
plugins_doc = []
list_plugin_res = run_command(python3, '-c',
'''
import sys
import json
with open("@0@") as f:
print(':'.join(json.load(f).keys()), end='')
'''.format(plugins_cache))
assert(list_plugin_res.returncode() == 0,
'Could not list plugins from @0@'.format(plugins_cache))
foreach plugin_name: list_plugin_res.stdout().split(':')
plugins_doc += [hotdoc.generate_doc(plugin_name,
project_version: api_version,
sitemap: 'sitemap.txt',
index: 'index.md',
gst_index: 'index.md',
gst_smart_index: true,
gst_c_sources: [
join_paths(root_rel, 'sys/*/*.[ch]'),
join_paths(root_rel, 'ext/*/*.[ch]'),
join_paths(root_rel, 'gst/*/*.[ch]'),
],
gst_c_source_filters: excludes,
dependencies: [gst_dep, plugins],
gst_order_generated_subpages: true,
disable_incremental_build: true,
gst_cache_file: plugins_cache,
gst_plugin_name: plugin_name,
)]
endforeach

17692
docs/random/ChangeLog-0.8 Normal file

File diff suppressed because it is too large Load diff

18
docs/random/LICENSE Normal file
View file

@ -0,0 +1,18 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/

17
docs/random/PORTED_09 Normal file
View file

@ -0,0 +1,17 @@
When porting a plugin start with 0.8 CVS head, not the old code in this module. There are many bugfixes which have gone into 0.8 which you want to keep.
List of ported plugins (update when you commit a ported plugin):
effectv (wim)
mad (wim)
videofilter (wim)
aalib (wim)
libcaca (zeeshan)
law (wim)
shout2 (zaheer) - not fully tested
esdsink (arwed)
osssink is partially done in the threaded branch (wim)
- Remember that some plugins are already ported and now in the gst-plugins-base module.
When you have ported a plugin remember to copy the relevant parts from configure.ac.orig into configure.ac and re-enable it in the Makefile.am files.

1
docs/sitemap.txt Normal file
View file

@ -0,0 +1 @@
gst-index

848
ext/a52dec/gsta52dec.c Normal file
View file

@ -0,0 +1,848 @@
/* GStreamer
* Copyright (C) <2001> David I. Lehn <dlehn@users.sourceforge.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-a52dec
* @title: a52dec
*
* Dolby Digital (AC-3) audio decoder.
*
* ## Example launch line
* |[
* gst-launch-1.0 dvdreadsrc title=1 ! mpegpsdemux ! a52dec ! audioconvert ! audioresample ! autoaudiosink
* ]| Play audio part of a dvd title.
* |[
* gst-launch-1.0 filesrc location=abc.ac3 ! ac3parse ! a52dec ! audioconvert ! audioresample ! autoaudiosink
* ]| Decode and play a stand alone AC-3 file.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <stdlib.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <gst/gst.h>
#include <a52dec/a52.h>
#if !defined(A52_ACCEL_DETECT)
# include <a52dec/mm_accel.h>
#endif
#include "gsta52dec.h"
#if HAVE_ORC
#include <orc/orc.h>
#endif
#ifdef LIBA52_DOUBLE
#define SAMPLE_WIDTH 64
#define SAMPLE_FORMAT GST_AUDIO_NE(F64)
#define SAMPLE_TYPE GST_AUDIO_FORMAT_F64
#else
#define SAMPLE_WIDTH 32
#define SAMPLE_FORMAT GST_AUDIO_NE(F32)
#define SAMPLE_TYPE GST_AUDIO_FORMAT_F32
#endif
GST_DEBUG_CATEGORY_STATIC (a52dec_debug);
#define GST_CAT_DEFAULT (a52dec_debug)
/* A52Dec args */
enum
{
ARG_0,
ARG_DRC,
ARG_MODE,
ARG_LFE,
};
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-ac3; audio/ac3; audio/x-private1-ac3")
);
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw, "
"format = (string) " SAMPLE_FORMAT ", "
"layout = (string) interleaved, "
"rate = (int) [ 4000, 96000 ], " "channels = (int) [ 1, 6 ]")
);
static gboolean a52_element_init (GstPlugin * plugin);
#define gst_a52dec_parent_class parent_class
G_DEFINE_TYPE (GstA52Dec, gst_a52dec, GST_TYPE_AUDIO_DECODER);
GST_ELEMENT_REGISTER_DEFINE_CUSTOM (a52dec, a52_element_init);
static gboolean gst_a52dec_start (GstAudioDecoder * dec);
static gboolean gst_a52dec_stop (GstAudioDecoder * dec);
static gboolean gst_a52dec_set_format (GstAudioDecoder * bdec, GstCaps * caps);
static GstFlowReturn gst_a52dec_parse (GstAudioDecoder * dec,
GstAdapter * adapter, gint * offset, gint * length);
static GstFlowReturn gst_a52dec_handle_frame (GstAudioDecoder * dec,
GstBuffer * buffer);
static GstFlowReturn gst_a52dec_chain (GstPad * pad, GstObject * parent,
GstBuffer * buffer);
static void gst_a52dec_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_a52dec_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
#define GST_TYPE_A52DEC_MODE (gst_a52dec_mode_get_type())
static GType
gst_a52dec_mode_get_type (void)
{
static GType a52dec_mode_type = 0;
static const GEnumValue a52dec_modes[] = {
{A52_MONO, "Mono", "mono"},
{A52_STEREO, "Stereo", "stereo"},
{A52_3F, "3 Front", "3f"},
{A52_2F1R, "2 Front, 1 Rear", "2f1r"},
{A52_3F1R, "3 Front, 1 Rear", "3f1r"},
{A52_2F2R, "2 Front, 2 Rear", "2f2r"},
{A52_3F2R, "3 Front, 2 Rear", "3f2r"},
{A52_DOLBY, "Dolby", "dolby"},
{0, NULL, NULL},
};
if (!a52dec_mode_type) {
a52dec_mode_type = g_enum_register_static ("GstA52DecMode", a52dec_modes);
}
return a52dec_mode_type;
}
static void
gst_a52dec_class_init (GstA52DecClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstAudioDecoderClass *gstbase_class;
guint cpuflags = 0;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstbase_class = (GstAudioDecoderClass *) klass;
gobject_class->set_property = gst_a52dec_set_property;
gobject_class->get_property = gst_a52dec_get_property;
gstbase_class->start = GST_DEBUG_FUNCPTR (gst_a52dec_start);
gstbase_class->stop = GST_DEBUG_FUNCPTR (gst_a52dec_stop);
gstbase_class->set_format = GST_DEBUG_FUNCPTR (gst_a52dec_set_format);
gstbase_class->parse = GST_DEBUG_FUNCPTR (gst_a52dec_parse);
gstbase_class->handle_frame = GST_DEBUG_FUNCPTR (gst_a52dec_handle_frame);
/**
* GstA52Dec::drc
*
* Set to true to apply the recommended Dolby Digital dynamic range compression
* to the audio stream. Dynamic range compression makes loud sounds
* softer and soft sounds louder, so you can more easily listen
* to the stream without disturbing other people.
*/
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DRC,
g_param_spec_boolean ("drc", "Dynamic Range Compression",
"Use Dynamic Range Compression", FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstA52Dec::mode
*
* Force a particular output channel configuration from the decoder. By default,
* the channel downmix (if any) is chosen automatically based on the downstream
* capabilities of the pipeline.
*/
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MODE,
g_param_spec_enum ("mode", "Decoder Mode", "Decoding Mode (default 3f2r)",
GST_TYPE_A52DEC_MODE, A52_3F2R,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstA52Dec::lfe
*
* Whether to output the LFE (Low Frequency Emitter) channel of the audio stream.
*/
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LFE,
g_param_spec_boolean ("lfe", "LFE", "LFE", TRUE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_add_static_pad_template (gstelement_class, &sink_factory);
gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
gst_element_class_set_static_metadata (gstelement_class,
"ATSC A/52 audio decoder", "Codec/Decoder/Audio/Converter",
"Decodes ATSC A/52 encoded audio streams",
"David I. Lehn <dlehn@users.sourceforge.net>");
GST_DEBUG_CATEGORY_INIT (a52dec_debug, "a52dec", 0,
"AC3/A52 software decoder");
/* If no CPU instruction based acceleration is available, end up using the
* generic software djbfft based one when available in the used liba52 */
#ifdef MM_ACCEL_DJBFFT
klass->a52_cpuflags = MM_ACCEL_DJBFFT;
#elif defined(A52_ACCEL_DETECT)
klass->a52_cpuflags = A52_ACCEL_DETECT;
#else
klass->a52_cpuflags = 0;
#endif
#if HAVE_ORC && !defined(A52_ACCEL_DETECT)
cpuflags = orc_target_get_default_flags (orc_target_get_by_name ("mmx"));
if (cpuflags & ORC_TARGET_MMX_MMX)
klass->a52_cpuflags |= MM_ACCEL_X86_MMX;
if (cpuflags & ORC_TARGET_MMX_3DNOW)
klass->a52_cpuflags |= MM_ACCEL_X86_3DNOW;
if (cpuflags & ORC_TARGET_MMX_MMXEXT)
klass->a52_cpuflags |= MM_ACCEL_X86_MMXEXT;
#endif
GST_LOG ("CPU flags: a52=%08x, orc=%08x", klass->a52_cpuflags, cpuflags);
gst_type_mark_as_plugin_api (GST_TYPE_A52DEC_MODE, 0);
}
static void
gst_a52dec_init (GstA52Dec * a52dec)
{
a52dec->request_channels = A52_CHANNEL;
a52dec->dynamic_range_compression = FALSE;
a52dec->state = NULL;
a52dec->samples = NULL;
gst_audio_decoder_set_use_default_pad_acceptcaps (GST_AUDIO_DECODER_CAST
(a52dec), TRUE);
GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_DECODER_SINK_PAD (a52dec));
/* retrieve and intercept base class chain.
* Quite HACKish, but that's dvd specs/caps for you,
* since one buffer needs to be split into 2 frames */
a52dec->base_chain = GST_PAD_CHAINFUNC (GST_AUDIO_DECODER_SINK_PAD (a52dec));
gst_pad_set_chain_function (GST_AUDIO_DECODER_SINK_PAD (a52dec),
GST_DEBUG_FUNCPTR (gst_a52dec_chain));
}
static gboolean
gst_a52dec_start (GstAudioDecoder * dec)
{
GstA52Dec *a52dec = GST_A52DEC (dec);
GstA52DecClass *klass;
static GMutex init_mutex;
GST_DEBUG_OBJECT (dec, "start");
klass = GST_A52DEC_CLASS (G_OBJECT_GET_CLASS (a52dec));
g_mutex_lock (&init_mutex);
#if defined(A52_ACCEL_DETECT)
a52dec->state = a52_init ();
/* This line is just to avoid being accused of not using klass */
a52_accel (klass->a52_cpuflags & A52_ACCEL_DETECT);
#else
a52dec->state = a52_init (klass->a52_cpuflags);
#endif
g_mutex_unlock (&init_mutex);
if (!a52dec->state) {
GST_ELEMENT_ERROR (GST_ELEMENT (a52dec), LIBRARY, INIT, (NULL),
("failed to initialize a52 state"));
return FALSE;
}
a52dec->samples = a52_samples (a52dec->state);
a52dec->bit_rate = -1;
a52dec->sample_rate = -1;
a52dec->stream_channels = A52_CHANNEL;
a52dec->using_channels = A52_CHANNEL;
a52dec->level = 1;
a52dec->bias = 0;
a52dec->flag_update = TRUE;
/* call upon legacy upstream byte support (e.g. seeking) */
gst_audio_decoder_set_estimate_rate (dec, TRUE);
return TRUE;
}
static gboolean
gst_a52dec_stop (GstAudioDecoder * dec)
{
GstA52Dec *a52dec = GST_A52DEC (dec);
GST_DEBUG_OBJECT (dec, "stop");
a52dec->samples = NULL;
if (a52dec->state) {
a52_free (a52dec->state);
a52dec->state = NULL;
}
return TRUE;
}
static GstFlowReturn
gst_a52dec_parse (GstAudioDecoder * bdec, GstAdapter * adapter,
gint * _offset, gint * len)
{
GstA52Dec *a52dec;
const guint8 *data;
gint av, size;
gint length = 0, flags, sample_rate, bit_rate;
GstFlowReturn result = GST_FLOW_EOS;
a52dec = GST_A52DEC (bdec);
size = av = gst_adapter_available (adapter);
data = (const guint8 *) gst_adapter_map (adapter, av);
/* find and read header */
bit_rate = a52dec->bit_rate;
sample_rate = a52dec->sample_rate;
flags = 0;
while (size >= 7) {
length = a52_syncinfo ((guint8 *) data, &flags, &sample_rate, &bit_rate);
if (length == 0) {
/* shift window to re-find sync */
data++;
size--;
} else if (length <= size) {
GST_LOG_OBJECT (a52dec, "Sync: frame size %d", length);
result = GST_FLOW_OK;
break;
} else {
GST_LOG_OBJECT (a52dec, "Not enough data available (needed %d had %d)",
length, size);
break;
}
}
gst_adapter_unmap (adapter);
*_offset = av - size;
*len = length;
return result;
}
static gint
gst_a52dec_channels (int flags, GstAudioChannelPosition * pos)
{
gint chans = 0;
if (flags & A52_LFE) {
chans += 1;
if (pos) {
pos[0] = GST_AUDIO_CHANNEL_POSITION_LFE1;
}
}
flags &= A52_CHANNEL_MASK;
switch (flags) {
case A52_3F2R:
if (pos) {
pos[0 + chans] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
pos[1 + chans] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
pos[2 + chans] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
pos[3 + chans] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
pos[4 + chans] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
}
chans += 5;
break;
case A52_2F2R:
if (pos) {
pos[0 + chans] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
pos[1 + chans] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
pos[2 + chans] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
pos[3 + chans] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
}
chans += 4;
break;
case A52_3F1R:
if (pos) {
pos[0 + chans] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
pos[1 + chans] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
pos[2 + chans] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
pos[3 + chans] = GST_AUDIO_CHANNEL_POSITION_REAR_CENTER;
}
chans += 4;
break;
case A52_2F1R:
if (pos) {
pos[0 + chans] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
pos[1 + chans] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
pos[2 + chans] = GST_AUDIO_CHANNEL_POSITION_REAR_CENTER;
}
chans += 3;
break;
case A52_3F:
if (pos) {
pos[0 + chans] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
pos[1 + chans] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
pos[2 + chans] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
}
chans += 3;
break;
case A52_CHANNEL: /* Dual mono. Should really be handled as 2 src pads */
case A52_STEREO:
case A52_DOLBY:
if (pos) {
pos[0 + chans] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
pos[1 + chans] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
}
chans += 2;
break;
case A52_MONO:
if (pos) {
pos[0 + chans] = GST_AUDIO_CHANNEL_POSITION_MONO;
}
chans += 1;
break;
default:
/* error, caller should post error message */
return 0;
}
return chans;
}
static gboolean
gst_a52dec_reneg (GstA52Dec * a52dec)
{
gint channels;
gboolean result = FALSE;
GstAudioChannelPosition from[6], to[6];
GstAudioInfo info;
channels = gst_a52dec_channels (a52dec->using_channels, from);
if (!channels)
goto done;
GST_INFO_OBJECT (a52dec, "reneg channels:%d rate:%d",
channels, a52dec->sample_rate);
memcpy (to, from, sizeof (GstAudioChannelPosition) * channels);
gst_audio_channel_positions_to_valid_order (to, channels);
gst_audio_get_channel_reorder_map (channels, from, to,
a52dec->channel_reorder_map);
gst_audio_info_init (&info);
gst_audio_info_set_format (&info,
SAMPLE_TYPE, a52dec->sample_rate, channels, (channels > 1 ? to : NULL));
if (!gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (a52dec), &info))
goto done;
result = TRUE;
done:
return result;
}
static void
gst_a52dec_update_streaminfo (GstA52Dec * a52dec)
{
GstTagList *taglist;
taglist = gst_tag_list_new_empty ();
gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_BITRATE,
(guint) a52dec->bit_rate, NULL);
gst_audio_decoder_merge_tags (GST_AUDIO_DECODER (a52dec), taglist,
GST_TAG_MERGE_REPLACE);
gst_tag_list_unref (taglist);
}
static GstFlowReturn
gst_a52dec_handle_frame (GstAudioDecoder * bdec, GstBuffer * buffer)
{
GstA52Dec *a52dec;
gint channels, i;
gboolean need_reneg = FALSE;
gint chans;
gint length = 0, flags, sample_rate, bit_rate;
GstMapInfo map;
GstFlowReturn result = GST_FLOW_OK;
GstBuffer *outbuf;
const gint num_blocks = 6;
a52dec = GST_A52DEC (bdec);
/* no fancy draining */
if (G_UNLIKELY (!buffer))
return GST_FLOW_OK;
/* parsed stuff already, so this should work out fine */
gst_buffer_map (buffer, &map, GST_MAP_READ);
g_assert (map.size >= 7);
/* re-obtain some sync header info,
* should be same as during _parse and could also be cached there,
* but anyway ... */
bit_rate = a52dec->bit_rate;
sample_rate = a52dec->sample_rate;
flags = 0;
length = a52_syncinfo (map.data, &flags, &sample_rate, &bit_rate);
g_assert (length == map.size);
/* update stream information, renegotiate or re-streaminfo if needed */
need_reneg = FALSE;
if (a52dec->sample_rate != sample_rate) {
GST_DEBUG_OBJECT (a52dec, "sample rate changed");
need_reneg = TRUE;
a52dec->sample_rate = sample_rate;
}
if (flags) {
if (a52dec->stream_channels != (flags & (A52_CHANNEL_MASK | A52_LFE))) {
GST_DEBUG_OBJECT (a52dec, "stream channel flags changed, marking update");
a52dec->flag_update = TRUE;
}
a52dec->stream_channels = flags & (A52_CHANNEL_MASK | A52_LFE);
}
if (bit_rate != a52dec->bit_rate) {
a52dec->bit_rate = bit_rate;
gst_a52dec_update_streaminfo (a52dec);
}
/* If we haven't had an explicit number of channels chosen through properties
* at this point, choose what to downmix to now, based on what the peer will
* accept - this allows a52dec to do downmixing in preference to a
* downstream element such as audioconvert.
*/
if (a52dec->request_channels != A52_CHANNEL) {
flags = a52dec->request_channels;
} else if (a52dec->flag_update) {
GstCaps *caps;
a52dec->flag_update = FALSE;
caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (a52dec));
if (caps && gst_caps_get_size (caps) > 0) {
GstCaps *copy = gst_caps_copy_nth (caps, 0);
GstStructure *structure = gst_caps_get_structure (copy, 0);
gint orig_channels = flags ? gst_a52dec_channels (flags, NULL) : 6;
gint fixed_channels = 0;
const int a52_channels[6] = {
A52_MONO,
A52_STEREO,
A52_STEREO | A52_LFE,
A52_2F2R,
A52_2F2R | A52_LFE,
A52_3F2R | A52_LFE,
};
/* Prefer the original number of channels, but fixate to something
* preferred (first in the caps) downstream if possible.
*/
gst_structure_fixate_field_nearest_int (structure, "channels",
orig_channels);
if (gst_structure_get_int (structure, "channels", &fixed_channels)
&& fixed_channels <= 6) {
if (fixed_channels < orig_channels)
flags = a52_channels[fixed_channels - 1];
} else {
flags = a52_channels[5];
}
gst_caps_unref (copy);
} else if (flags)
flags = a52dec->stream_channels;
else
flags = A52_3F2R | A52_LFE;
if (caps)
gst_caps_unref (caps);
} else {
flags = a52dec->using_channels;
}
/* process */
flags |= A52_ADJUST_LEVEL;
a52dec->level = 1;
if (a52_frame (a52dec->state, map.data, &flags, &a52dec->level, a52dec->bias)) {
gst_buffer_unmap (buffer, &map);
GST_AUDIO_DECODER_ERROR (a52dec, 1, STREAM, DECODE, (NULL),
("a52_frame error"), result);
goto exit;
}
gst_buffer_unmap (buffer, &map);
channels = flags & (A52_CHANNEL_MASK | A52_LFE);
if (a52dec->using_channels != channels) {
need_reneg = TRUE;
a52dec->using_channels = channels;
}
/* negotiate if required */
if (need_reneg) {
GST_DEBUG_OBJECT (a52dec,
"a52dec reneg: sample_rate:%d stream_chans:%d using_chans:%d",
a52dec->sample_rate, a52dec->stream_channels, a52dec->using_channels);
if (!gst_a52dec_reneg (a52dec))
goto failed_negotiation;
}
if (a52dec->dynamic_range_compression == FALSE) {
a52_dynrng (a52dec->state, NULL, NULL);
}
flags &= (A52_CHANNEL_MASK | A52_LFE);
chans = gst_a52dec_channels (flags, NULL);
if (!chans)
goto invalid_flags;
/* handle decoded data;
* each frame has 6 blocks, one block is 256 samples, ea */
outbuf =
gst_buffer_new_and_alloc (256 * chans * (SAMPLE_WIDTH / 8) * num_blocks);
gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
{
guint8 *ptr = map.data;
for (i = 0; i < num_blocks; i++) {
if (a52_block (a52dec->state)) {
/* also marks discont */
GST_AUDIO_DECODER_ERROR (a52dec, 1, STREAM, DECODE, (NULL),
("error decoding block %d", i), result);
if (result != GST_FLOW_OK) {
gst_buffer_unmap (outbuf, &map);
gst_buffer_unref (outbuf);
goto exit;
}
} else {
gint n, c;
gint *reorder_map = a52dec->channel_reorder_map;
for (n = 0; n < 256; n++) {
for (c = 0; c < chans; c++) {
((sample_t *) ptr)[n * chans + reorder_map[c]] =
a52dec->samples[c * 256 + n];
}
}
}
ptr += 256 * chans * (SAMPLE_WIDTH / 8);
}
}
gst_buffer_unmap (outbuf, &map);
result = gst_audio_decoder_finish_frame (bdec, outbuf, 1);
exit:
return result;
/* ERRORS */
failed_negotiation:
{
GST_ELEMENT_ERROR (a52dec, CORE, NEGOTIATION, (NULL), (NULL));
return GST_FLOW_ERROR;
}
invalid_flags:
{
GST_ELEMENT_ERROR (GST_ELEMENT (a52dec), STREAM, DECODE, (NULL),
("Invalid channel flags: %d", flags));
return GST_FLOW_ERROR;
}
}
static gboolean
gst_a52dec_set_format (GstAudioDecoder * bdec, GstCaps * caps)
{
GstA52Dec *a52dec = GST_A52DEC (bdec);
GstStructure *structure;
structure = gst_caps_get_structure (caps, 0);
if (structure && gst_structure_has_name (structure, "audio/x-private1-ac3"))
a52dec->dvdmode = TRUE;
else
a52dec->dvdmode = FALSE;
return TRUE;
}
static GstFlowReturn
gst_a52dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
GstA52Dec *a52dec = GST_A52DEC (parent);
GstFlowReturn ret = GST_FLOW_OK;
gint first_access;
if (a52dec->dvdmode) {
gsize size;
guint8 data[2];
gint offset;
gint len;
GstBuffer *subbuf;
size = gst_buffer_get_size (buf);
if (size < 2)
goto not_enough_data;
gst_buffer_extract (buf, 0, data, 2);
first_access = (data[0] << 8) | data[1];
/* Skip the first_access header */
offset = 2;
if (first_access > 1) {
/* Length of data before first_access */
len = first_access - 1;
if (len <= 0 || offset + len > size)
goto bad_first_access_parameter;
subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset, len);
GST_BUFFER_TIMESTAMP (subbuf) = GST_CLOCK_TIME_NONE;
ret = a52dec->base_chain (pad, parent, subbuf);
if (ret != GST_FLOW_OK) {
gst_buffer_unref (buf);
goto done;
}
offset += len;
len = size - offset;
if (len > 0) {
subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset, len);
GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf);
ret = a52dec->base_chain (pad, parent, subbuf);
}
gst_buffer_unref (buf);
} else {
/* first_access = 0 or 1, so if there's a timestamp it applies to the first byte */
subbuf =
gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset,
size - offset);
GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf);
gst_buffer_unref (buf);
ret = a52dec->base_chain (pad, parent, subbuf);
}
} else {
ret = a52dec->base_chain (pad, parent, buf);
}
done:
return ret;
/* ERRORS */
not_enough_data:
{
GST_ELEMENT_ERROR (GST_ELEMENT (a52dec), STREAM, DECODE, (NULL),
("Insufficient data in buffer. Can't determine first_acess"));
gst_buffer_unref (buf);
return GST_FLOW_ERROR;
}
bad_first_access_parameter:
{
GST_ELEMENT_ERROR (GST_ELEMENT (a52dec), STREAM, DECODE, (NULL),
("Bad first_access parameter (%d) in buffer", first_access));
gst_buffer_unref (buf);
return GST_FLOW_ERROR;
}
}
static void
gst_a52dec_set_property (GObject * object, guint prop_id, const GValue * value,
GParamSpec * pspec)
{
GstA52Dec *src = GST_A52DEC (object);
switch (prop_id) {
case ARG_DRC:
GST_OBJECT_LOCK (src);
src->dynamic_range_compression = g_value_get_boolean (value);
GST_OBJECT_UNLOCK (src);
break;
case ARG_MODE:
GST_OBJECT_LOCK (src);
src->request_channels &= ~A52_CHANNEL_MASK;
src->request_channels |= g_value_get_enum (value);
GST_OBJECT_UNLOCK (src);
break;
case ARG_LFE:
GST_OBJECT_LOCK (src);
src->request_channels &= ~A52_LFE;
src->request_channels |= g_value_get_boolean (value) ? A52_LFE : 0;
GST_OBJECT_UNLOCK (src);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_a52dec_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstA52Dec *src = GST_A52DEC (object);
switch (prop_id) {
case ARG_DRC:
GST_OBJECT_LOCK (src);
g_value_set_boolean (value, src->dynamic_range_compression);
GST_OBJECT_UNLOCK (src);
break;
case ARG_MODE:
GST_OBJECT_LOCK (src);
g_value_set_enum (value, src->request_channels & A52_CHANNEL_MASK);
GST_OBJECT_UNLOCK (src);
break;
case ARG_LFE:
GST_OBJECT_LOCK (src);
g_value_set_boolean (value, src->request_channels & A52_LFE);
GST_OBJECT_UNLOCK (src);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
a52_element_init (GstPlugin * plugin)
{
#if HAVE_ORC
orc_init ();
#endif
return gst_element_register (plugin, "a52dec", GST_RANK_SECONDARY,
GST_TYPE_A52DEC);
}
static gboolean
plugin_init (GstPlugin * plugin)
{
return GST_ELEMENT_REGISTER (a52dec, plugin);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
a52dec,
"Decodes ATSC A/52 encoded audio streams",
plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);

105
ext/a52dec/gsta52dec.h Normal file
View file

@ -0,0 +1,105 @@
/* GStreamer
* Copyright (C) <2001> David I. Lehn <dlehn@users.sourceforge.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_A52DEC_H__
#define __GST_A52DEC_H__
#include <gst/gst.h>
#include <gst/audio/audio.h>
#include <gst/audio/gstaudiodecoder.h>
G_BEGIN_DECLS
#define GST_TYPE_A52DEC \
(gst_a52dec_get_type())
#define GST_A52DEC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_A52DEC,GstA52Dec))
#define GST_A52DEC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_A52DEC,GstA52DecClass))
#define GST_IS_A52DEC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_A52DEC))
#define GST_IS_A52DEC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_A52DEC))
typedef struct _GstA52Dec GstA52Dec;
typedef struct _GstA52DecClass GstA52DecClass;
struct _GstA52Dec {
GstAudioDecoder element;
GstPadChainFunction base_chain;
gboolean dvdmode;
gboolean flag_update;
int prev_flags;
/* stream properties */
int bit_rate;
int sample_rate;
int stream_channels;
int request_channels;
int using_channels;
gint channel_reorder_map[6];
sample_t level;
sample_t bias;
gboolean dynamic_range_compression;
sample_t *samples;
a52_state_t *state;
};
struct _GstA52DecClass {
GstAudioDecoderClass parent_class;
guint32 a52_cpuflags;
};
GType gst_a52dec_get_type (void);
GST_ELEMENT_REGISTER_DECLARE (a52dec);
#ifndef A52_MONO
#define A52_MONO 1
#endif
#ifndef A52_STEREO
#define A52_STEREO 2
#endif
#ifndef A52_3F
#define A52_3F 3
#endif
#ifndef A52_2F1R
#define A52_2F1R 4
#endif
#ifndef A52_3F1R
#define A52_3F1R 5
#endif
#ifndef A52_2F2R
#define A52_2F2R 6
#endif
#ifndef A52_3F2R
#define A52_3F2R 7
#endif
#ifndef A52_DOLBY
#define A52_DOLBY 10
#endif
G_END_DECLS
#endif /* __GST_A52DEC_H__ */

22
ext/a52dec/meson.build Normal file
View file

@ -0,0 +1,22 @@
if get_option('a52dec').disabled()
subdir_done()
endif
a52_dep = cc.find_library('a52', required : get_option('a52dec'))
have_a52_h = cc.has_header_symbol('a52dec/a52.h', 'a52_init', prefix : '#include <stdint.h>')
if not have_a52_h and get_option('a52dec').enabled()
error('a52dec plugin enabled but a52.h not found')
endif
if a52_dep.found() and have_a52_h
a52dec = library('gsta52dec',
'gsta52dec.c',
c_args : ugly_args,
include_directories : [configinc],
dependencies : [gstaudio_dep, orc_dep, a52_dep],
install : true,
install_dir : plugins_install_dir,
)
pkgconfig.generate(a52dec, install_dir : plugins_pkgconfig_install_dir)
plugins += [a52dec]
endif

11
ext/amrnb/GstAmrnbEnc.prs Normal file
View file

@ -0,0 +1,11 @@
[_presets_]
version=0.10
element-name=GstAmrnbEnc
[enhance-size]
_meta/comment=Maximize compression, lowest bitrate
band-mode=0
[enhance-quality]
_meta/comment=Maximize quality, highest bitrate
band-mode=7

6
ext/amrnb/README Normal file
View file

@ -0,0 +1,6 @@
Compiling AMRNB:
================
To compile the amrnb plugin, you need the opencore-amrnb development package.
If your distribution does not provide this package, you can download the
source code from "http://sourceforge.net/projects/opencore-amr".

44
ext/amrnb/amrnb.c Normal file
View file

@ -0,0 +1,44 @@
/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin
* Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "amrnbdec.h"
#include "amrnbenc.h"
static gboolean
plugin_init (GstPlugin * plugin)
{
gboolean ret = FALSE;
ret |= GST_ELEMENT_REGISTER (amrnbdec, plugin);
ret |= GST_ELEMENT_REGISTER (amrnbenc, plugin);
return ret;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
amrnb,
"Adaptive Multi-Rate Narrow-Band",
plugin_init, VERSION, GST_LICENSE_UNKNOWN, GST_PACKAGE_NAME,
GST_PACKAGE_ORIGIN);

309
ext/amrnb/amrnbdec.c Normal file
View file

@ -0,0 +1,309 @@
/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin
* Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-amrnbdec
* @title: amrnbdec
* @see_also: #GstAmrnbEnc, #GstAmrParse
*
* AMR narrowband decoder based on the
* [opencore codec implementation](http://sourceforge.net/projects/opencore-amr).
*
* ## Example launch line
* |[
* gst-launch-1.0 filesrc location=abc.amr ! amrparse ! amrnbdec ! audioconvert ! audioresample ! autoaudiosink
* ]|
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "amrnbdec.h"
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/AMR, " "rate = (int) 8000, " "channels = (int) 1")
);
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw, format = (string) " GST_AUDIO_NE (S16) ", "
"layout = (string) interleaved, "
"rate = (int) 8000," "channels = (int) 1")
);
GST_DEBUG_CATEGORY_STATIC (gst_amrnbdec_debug);
#define GST_CAT_DEFAULT gst_amrnbdec_debug
static const gint block_size_if1[16] = { 12, 13, 15, 17, 19, 20, 26, 31, 5,
0, 0, 0, 0, 0, 0, 0
};
static const gint block_size_if2[16] = { 12, 13, 15, 17, 18, 20, 25, 30, 5,
0, 0, 0, 0, 0, 0, 0
};
static GType
gst_amrnb_variant_get_type (void)
{
static GType gst_amrnb_variant_type = 0;
static const GEnumValue gst_amrnb_variant[] = {
{GST_AMRNB_VARIANT_IF1, "IF1", "IF1"},
{GST_AMRNB_VARIANT_IF2, "IF2", "IF2"},
{0, NULL, NULL},
};
if (!gst_amrnb_variant_type) {
gst_amrnb_variant_type =
g_enum_register_static ("GstAmrnbVariant", gst_amrnb_variant);
}
return gst_amrnb_variant_type;
}
#define GST_AMRNB_VARIANT_TYPE (gst_amrnb_variant_get_type())
#define VARIANT_DEFAULT GST_AMRNB_VARIANT_IF1
enum
{
PROP_0,
PROP_VARIANT
};
static void gst_amrnbdec_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_amrnbdec_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static gboolean gst_amrnbdec_start (GstAudioDecoder * dec);
static gboolean gst_amrnbdec_stop (GstAudioDecoder * dec);
static gboolean gst_amrnbdec_set_format (GstAudioDecoder * dec, GstCaps * caps);
static GstFlowReturn gst_amrnbdec_parse (GstAudioDecoder * dec,
GstAdapter * adapter, gint * offset, gint * length);
static GstFlowReturn gst_amrnbdec_handle_frame (GstAudioDecoder * dec,
GstBuffer * buffer);
#define gst_amrnbdec_parent_class parent_class
G_DEFINE_TYPE (GstAmrnbDec, gst_amrnbdec, GST_TYPE_AUDIO_DECODER);
GST_ELEMENT_REGISTER_DEFINE (amrnbdec, "amrnbdec", GST_RANK_PRIMARY,
GST_TYPE_AMRNBDEC);
static void
gst_amrnbdec_class_init (GstAmrnbDecClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstAudioDecoderClass *base_class = GST_AUDIO_DECODER_CLASS (klass);
object_class->set_property = gst_amrnbdec_set_property;
object_class->get_property = gst_amrnbdec_get_property;
gst_element_class_add_static_pad_template (element_class, &sink_template);
gst_element_class_add_static_pad_template (element_class, &src_template);
gst_element_class_set_static_metadata (element_class, "AMR-NB audio decoder",
"Codec/Decoder/Audio",
"Adaptive Multi-Rate Narrow-Band audio decoder",
"GStreamer maintainers <gstreamer-devel@lists.freedesktop.org>");
base_class->start = GST_DEBUG_FUNCPTR (gst_amrnbdec_start);
base_class->stop = GST_DEBUG_FUNCPTR (gst_amrnbdec_stop);
base_class->set_format = GST_DEBUG_FUNCPTR (gst_amrnbdec_set_format);
base_class->parse = GST_DEBUG_FUNCPTR (gst_amrnbdec_parse);
base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_amrnbdec_handle_frame);
g_object_class_install_property (object_class, PROP_VARIANT,
g_param_spec_enum ("variant", "Variant",
"The decoder variant", GST_AMRNB_VARIANT_TYPE,
VARIANT_DEFAULT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
GST_DEBUG_CATEGORY_INIT (gst_amrnbdec_debug, "amrnbdec", 0,
"AMR-NB audio decoder");
gst_type_mark_as_plugin_api (GST_AMRNB_VARIANT_TYPE, 0);
}
static void
gst_amrnbdec_init (GstAmrnbDec * amrnbdec)
{
gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (amrnbdec), TRUE);
gst_audio_decoder_set_use_default_pad_acceptcaps (GST_AUDIO_DECODER_CAST
(amrnbdec), TRUE);
GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_DECODER_SINK_PAD (amrnbdec));
}
static gboolean
gst_amrnbdec_start (GstAudioDecoder * dec)
{
GstAmrnbDec *amrnbdec = GST_AMRNBDEC (dec);
GST_DEBUG_OBJECT (dec, "start");
if (!(amrnbdec->handle = Decoder_Interface_init ()))
return FALSE;
amrnbdec->rate = 0;
amrnbdec->channels = 0;
return TRUE;
}
static gboolean
gst_amrnbdec_stop (GstAudioDecoder * dec)
{
GstAmrnbDec *amrnbdec = GST_AMRNBDEC (dec);
GST_DEBUG_OBJECT (dec, "stop");
Decoder_Interface_exit (amrnbdec->handle);
return TRUE;
}
static void
gst_amrnbdec_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstAmrnbDec *self = GST_AMRNBDEC (object);
switch (prop_id) {
case PROP_VARIANT:
self->variant = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
return;
}
static void
gst_amrnbdec_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstAmrnbDec *self = GST_AMRNBDEC (object);
switch (prop_id) {
case PROP_VARIANT:
g_value_set_enum (value, self->variant);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
return;
}
static gboolean
gst_amrnbdec_set_format (GstAudioDecoder * dec, GstCaps * caps)
{
GstStructure *structure;
GstAmrnbDec *amrnbdec;
GstAudioInfo info;
amrnbdec = GST_AMRNBDEC (dec);
structure = gst_caps_get_structure (caps, 0);
/* get channel count */
gst_structure_get_int (structure, "channels", &amrnbdec->channels);
gst_structure_get_int (structure, "rate", &amrnbdec->rate);
/* create reverse caps */
gst_audio_info_init (&info);
gst_audio_info_set_format (&info,
GST_AUDIO_FORMAT_S16, amrnbdec->rate, amrnbdec->channels, NULL);
return gst_audio_decoder_set_output_format (dec, &info);
}
static GstFlowReturn
gst_amrnbdec_parse (GstAudioDecoder * dec, GstAdapter * adapter,
gint * offset, gint * length)
{
GstAmrnbDec *amrnbdec = GST_AMRNBDEC (dec);
guint8 head[1];
guint size;
gboolean sync, eos;
gint block, mode;
size = gst_adapter_available (adapter);
if (size < 1)
return GST_FLOW_ERROR;
gst_audio_decoder_get_parse_state (dec, &sync, &eos);
/* need to peek data to get the size */
gst_adapter_copy (adapter, head, 0, 1);
/* get size */
switch (amrnbdec->variant) {
case GST_AMRNB_VARIANT_IF1:
mode = (head[0] >> 3) & 0x0F;
block = block_size_if1[mode] + 1;
break;
case GST_AMRNB_VARIANT_IF2:
mode = head[0] & 0x0F;
block = block_size_if2[mode] + 1;
break;
default:
g_assert_not_reached ();
return GST_FLOW_ERROR;
break;
}
GST_DEBUG_OBJECT (amrnbdec, "mode %d, block %d", mode, block);
if (block > size)
return GST_FLOW_EOS;
*offset = 0;
*length = block;
return GST_FLOW_OK;
}
static GstFlowReturn
gst_amrnbdec_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer)
{
GstAmrnbDec *amrnbdec;
GstMapInfo inmap, outmap;
GstBuffer *out;
amrnbdec = GST_AMRNBDEC (dec);
/* no fancy flushing */
if (!buffer || !gst_buffer_get_size (buffer))
return GST_FLOW_OK;
gst_buffer_map (buffer, &inmap, GST_MAP_READ);
/* get output */
out = gst_buffer_new_and_alloc (160 * 2);
/* decode */
gst_buffer_map (out, &outmap, GST_MAP_WRITE);
Decoder_Interface_Decode (amrnbdec->handle, inmap.data,
(gint16 *) outmap.data, 0);
gst_buffer_unmap (out, &outmap);
gst_buffer_unmap (buffer, &inmap);
return gst_audio_decoder_finish_frame (dec, out, 1);
}

71
ext/amrnb/amrnbdec.h Normal file
View file

@ -0,0 +1,71 @@
/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin
* Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_AMRNBDEC_H__
#define __GST_AMRNBDEC_H__
#include <gst/gst.h>
#include <gst/audio/gstaudiodecoder.h>
#include <opencore-amrnb/interf_dec.h>
G_BEGIN_DECLS
#define GST_TYPE_AMRNBDEC \
(gst_amrnbdec_get_type())
#define GST_AMRNBDEC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRNBDEC, GstAmrnbDec))
#define GST_AMRNBDEC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRNBDEC, GstAmrnbDecClass))
#define GST_IS_AMRNBDEC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRNBDEC))
#define GST_IS_AMRNBDEC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRNBDEC))
typedef struct _GstAmrnbDec GstAmrnbDec;
typedef struct _GstAmrnbDecClass GstAmrnbDecClass;
typedef enum
{
GST_AMRNB_VARIANT_IF1,
GST_AMRNB_VARIANT_IF2
} GstAmrnbVariant;
struct _GstAmrnbDec {
GstAudioDecoder element;
GstAmrnbVariant variant;
/* library handle */
void *handle;
/* output settings */
gint channels, rate;
};
struct _GstAmrnbDecClass {
GstAudioDecoderClass parent_class;
};
GType gst_amrnbdec_get_type (void);
GST_ELEMENT_REGISTER_DECLARE (amrnbdec);
G_END_DECLS
#endif /* __GST_AMRNBDEC_H__ */

295
ext/amrnb/amrnbenc.c Normal file
View file

@ -0,0 +1,295 @@
/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin
* Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-amrnbenc
* @title: amrnbenc
* @see_also: #GstAmrnbDec, #GstAmrnbParse
*
* AMR narrowband encoder based on the
* [opencore codec implementation](http://sourceforge.net/projects/opencore-amr).
*
* ## Example launch line
* |[
* gst-launch-1.0 filesrc location=abc.wav ! wavparse ! audioconvert ! audioresample ! amrnbenc ! filesink location=abc.amr
* ]|
* Please note that the above stream misses the header, that is needed to play
* the stream.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "amrnbenc.h"
static GType
gst_amrnbenc_bandmode_get_type (void)
{
static GType gst_amrnbenc_bandmode_type = 0;
static const GEnumValue gst_amrnbenc_bandmode[] = {
{MR475, "MR475", "MR475"},
{MR515, "MR515", "MR515"},
{MR59, "MR59", "MR59"},
{MR67, "MR67", "MR67"},
{MR74, "MR74", "MR74"},
{MR795, "MR795", "MR795"},
{MR102, "MR102", "MR102"},
{MR122, "MR122", "MR122"},
{MRDTX, "MRDTX", "MRDTX"},
{0, NULL, NULL},
};
if (!gst_amrnbenc_bandmode_type) {
gst_amrnbenc_bandmode_type =
g_enum_register_static ("GstAmrnbEncBandMode", gst_amrnbenc_bandmode);
}
return gst_amrnbenc_bandmode_type;
}
#define GST_AMRNBENC_BANDMODE_TYPE (gst_amrnbenc_bandmode_get_type())
#define BANDMODE_DEFAULT MR122
enum
{
PROP_0,
PROP_BANDMODE
};
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw, format = (string) " GST_AUDIO_NE (S16) ", "
"layout = (string) interleaved, "
"rate = (int) 8000," "channels = (int) 1")
);
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/AMR, " "rate = (int) 8000, " "channels = (int) 1")
);
GST_DEBUG_CATEGORY_STATIC (gst_amrnbenc_debug);
#define GST_CAT_DEFAULT gst_amrnbenc_debug
static gboolean gst_amrnbenc_start (GstAudioEncoder * enc);
static gboolean gst_amrnbenc_stop (GstAudioEncoder * enc);
static gboolean gst_amrnbenc_set_format (GstAudioEncoder * enc,
GstAudioInfo * info);
static GstFlowReturn gst_amrnbenc_handle_frame (GstAudioEncoder * enc,
GstBuffer * in_buf);
#define gst_amrnbenc_parent_class parent_class
G_DEFINE_TYPE (GstAmrnbEnc, gst_amrnbenc, GST_TYPE_AUDIO_ENCODER);
GST_ELEMENT_REGISTER_DEFINE (amrnbenc, "amrnbenc", GST_RANK_SECONDARY,
GST_TYPE_AMRNBENC);
static void
gst_amrnbenc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstAmrnbEnc *self = GST_AMRNBENC (object);
switch (prop_id) {
case PROP_BANDMODE:
self->bandmode = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
return;
}
static void
gst_amrnbenc_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstAmrnbEnc *self = GST_AMRNBENC (object);
switch (prop_id) {
case PROP_BANDMODE:
g_value_set_enum (value, self->bandmode);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
return;
}
static void
gst_amrnbenc_class_init (GstAmrnbEncClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstAudioEncoderClass *base_class = GST_AUDIO_ENCODER_CLASS (klass);
object_class->set_property = gst_amrnbenc_set_property;
object_class->get_property = gst_amrnbenc_get_property;
base_class->start = GST_DEBUG_FUNCPTR (gst_amrnbenc_start);
base_class->stop = GST_DEBUG_FUNCPTR (gst_amrnbenc_stop);
base_class->set_format = GST_DEBUG_FUNCPTR (gst_amrnbenc_set_format);
base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_amrnbenc_handle_frame);
g_object_class_install_property (object_class, PROP_BANDMODE,
g_param_spec_enum ("band-mode", "Band Mode",
"Encoding Band Mode (Kbps)", GST_AMRNBENC_BANDMODE_TYPE,
BANDMODE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
gst_element_class_add_static_pad_template (element_class, &sink_template);
gst_element_class_add_static_pad_template (element_class, &src_template);
gst_element_class_set_static_metadata (element_class, "AMR-NB audio encoder",
"Codec/Encoder/Audio",
"Adaptive Multi-Rate Narrow-Band audio encoder",
"Wim Taymans <wim.taymans@gmail.com>");
GST_DEBUG_CATEGORY_INIT (gst_amrnbenc_debug, "amrnbenc", 0,
"AMR-NB audio encoder");
gst_type_mark_as_plugin_api (GST_AMRNBENC_BANDMODE_TYPE, 0);
}
static void
gst_amrnbenc_init (GstAmrnbEnc * amrnbenc)
{
GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_ENCODER_SINK_PAD (amrnbenc));
}
static gboolean
gst_amrnbenc_start (GstAudioEncoder * enc)
{
GstAmrnbEnc *amrnbenc = GST_AMRNBENC (enc);
GST_DEBUG_OBJECT (amrnbenc, "start");
if (!(amrnbenc->handle = Encoder_Interface_init (0)))
return FALSE;
return TRUE;
}
static gboolean
gst_amrnbenc_stop (GstAudioEncoder * enc)
{
GstAmrnbEnc *amrnbenc = GST_AMRNBENC (enc);
GST_DEBUG_OBJECT (amrnbenc, "stop");
Encoder_Interface_exit (amrnbenc->handle);
return TRUE;
}
static gboolean
gst_amrnbenc_set_format (GstAudioEncoder * enc, GstAudioInfo * info)
{
GstAmrnbEnc *amrnbenc;
GstCaps *copy;
amrnbenc = GST_AMRNBENC (enc);
/* parameters already parsed for us */
amrnbenc->rate = GST_AUDIO_INFO_RATE (info);
amrnbenc->channels = GST_AUDIO_INFO_CHANNELS (info);
/* we do not really accept other input, but anyway ... */
/* this is not wrong but will sound bad */
if (amrnbenc->channels != 1) {
g_warning ("amrnbdec is only optimized for mono channels");
}
if (amrnbenc->rate != 8000) {
g_warning ("amrnbdec is only optimized for 8000 Hz samplerate");
}
/* create reverse caps */
copy = gst_caps_new_simple ("audio/AMR",
"channels", G_TYPE_INT, amrnbenc->channels,
"rate", G_TYPE_INT, amrnbenc->rate, NULL);
gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (amrnbenc), copy);
gst_caps_unref (copy);
/* report needs to base class: hand one frame at a time */
gst_audio_encoder_set_frame_samples_min (enc, 160);
gst_audio_encoder_set_frame_samples_max (enc, 160);
gst_audio_encoder_set_frame_max (enc, 1);
return TRUE;
}
static GstFlowReturn
gst_amrnbenc_handle_frame (GstAudioEncoder * enc, GstBuffer * buffer)
{
GstAmrnbEnc *amrnbenc;
GstFlowReturn ret;
GstBuffer *out;
GstMapInfo in_map, out_map;
gsize out_size;
amrnbenc = GST_AMRNBENC (enc);
g_return_val_if_fail (amrnbenc->handle, GST_FLOW_FLUSHING);
/* we don't deal with squeezing remnants, so simply discard those */
if (G_UNLIKELY (buffer == NULL)) {
GST_DEBUG_OBJECT (amrnbenc, "no data");
return GST_FLOW_OK;
}
gst_buffer_map (buffer, &in_map, GST_MAP_READ);
if (G_UNLIKELY (in_map.size < 320)) {
gst_buffer_unmap (buffer, &in_map);
GST_DEBUG_OBJECT (amrnbenc, "discarding trailing data of %" G_GSIZE_FORMAT
" bytes", in_map.size);
return gst_audio_encoder_finish_frame (enc, NULL, -1);
}
/* get output, max size is 32 */
out = gst_buffer_new_and_alloc (32);
/* AMR encoder actually writes into the source data buffers it gets */
/* should be able to handle that with what we are given */
gst_buffer_map (out, &out_map, GST_MAP_WRITE);
/* encode */
out_size =
Encoder_Interface_Encode (amrnbenc->handle, amrnbenc->bandmode,
(short *) in_map.data, out_map.data, 0);
gst_buffer_unmap (out, &out_map);
gst_buffer_resize (out, 0, out_size);
gst_buffer_unmap (buffer, &in_map);
GST_LOG_OBJECT (amrnbenc, "output data size %" G_GSIZE_FORMAT, out_size);
if (out_size) {
ret = gst_audio_encoder_finish_frame (enc, out, 160);
} else {
/* should not happen (without dtx or so at least) */
GST_WARNING_OBJECT (amrnbenc, "no encoded data; discarding input");
gst_buffer_unref (out);
ret = gst_audio_encoder_finish_frame (enc, NULL, -1);
}
return ret;
}

67
ext/amrnb/amrnbenc.h Normal file
View file

@ -0,0 +1,67 @@
/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin
* Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_AMRNBENC_H__
#define __GST_AMRNBENC_H__
#include <gst/gst.h>
#include <gst/audio/gstaudioencoder.h>
#include <opencore-amrnb/interf_enc.h>
G_BEGIN_DECLS
#define GST_TYPE_AMRNBENC \
(gst_amrnbenc_get_type())
#define GST_AMRNBENC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRNBENC, GstAmrnbEnc))
#define GST_AMRNBENC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRNBENC, GstAmrnbEncClass))
#define GST_IS_AMRNBENC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRNBENC))
#define GST_IS_AMRNBENC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRNBENC))
typedef struct _GstAmrnbEnc GstAmrnbEnc;
typedef struct _GstAmrnbEncClass GstAmrnbEncClass;
struct _GstAmrnbEnc {
GstAudioEncoder element;
/* library handle */
void *handle;
/* input settings */
gint channels, rate;
gint duration;
/* property */
enum Mode bandmode;
};
struct _GstAmrnbEncClass {
GstAudioEncoderClass parent_class;
};
GType gst_amrnbenc_get_type (void);
GST_ELEMENT_REGISTER_DECLARE (amrnbenc);
G_END_DECLS
#endif /* __GST_AMRNBENC_H__ */

15
ext/amrnb/meson.build Normal file
View file

@ -0,0 +1,15 @@
amrnb_dep = dependency('opencore-amrnb', version : '>= 0.1.3', required : get_option('amrnb'))
if amrnb_dep.found()
amrnb = library('gstamrnb',
['amrnb.c', 'amrnbdec.c', 'amrnbenc.c'],
c_args : ugly_args,
include_directories : [configinc],
dependencies : [gstaudio_dep, amrnb_dep],
install : true,
install_dir : plugins_install_dir,
)
pkgconfig.generate(amrnb, install_dir : plugins_pkgconfig_install_dir)
plugins += [amrnb]
install_data(sources: 'GstAmrnbEnc.prs', install_dir: presetdir)
endif

6
ext/amrwbdec/README Normal file
View file

@ -0,0 +1,6 @@
Compiling AMRWB decoder:
========================
To compile the amrwbdec plugin, you need the opencore-amrwb development
package. If your distribution does not provide this package, you can
download the source code from "http://sourceforge.net/projects/opencore-amr".

38
ext/amrwbdec/amrwb.c Normal file
View file

@ -0,0 +1,38 @@
/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) Decoder plugin
* Copyright (C) 2006 Edgard Lima <edgard.lima@gmail.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "amrwbdec.h"
static gboolean
plugin_init (GstPlugin * plugin)
{
return GST_ELEMENT_REGISTER (amrwbdec, plugin);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
amrwbdec,
"Adaptive Multi-Rate Wide-Band Decoder",
plugin_init, VERSION, GST_LICENSE_UNKNOWN, GST_PACKAGE_NAME,
GST_PACKAGE_ORIGIN);

234
ext/amrwbdec/amrwbdec.c Normal file
View file

@ -0,0 +1,234 @@
/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin
* Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-amrwbdec
* @title: amrwbdec
* @see_also: #GstAmrwbEnc
*
* AMR wideband decoder based on the
* [opencore codec implementation](http://sourceforge.net/projects/opencore-amr).
*
* ## Example launch line
* |[
* gst-launch-1.0 filesrc location=abc.amr ! amrparse ! amrwbdec ! audioconvert ! audioresample ! autoaudiosink
* ]|
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/audio/audio.h>
#include "amrwbdec.h"
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/AMR-WB, "
"rate = (int) 16000, " "channels = (int) 1")
);
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw, "
"format = (string) " GST_AUDIO_NE (S16) ", "
"layout = (string) interleaved, "
"rate = (int) 16000, " "channels = (int) 1")
);
GST_DEBUG_CATEGORY_STATIC (gst_amrwbdec_debug);
#define GST_CAT_DEFAULT gst_amrwbdec_debug
#define L_FRAME16k 320 /* Frame size at 16kHz */
static const unsigned char block_size[16] =
{ 18, 24, 33, 37, 41, 47, 51, 59, 61,
6, 0, 0, 0, 0, 1, 1
};
static gboolean gst_amrwbdec_start (GstAudioDecoder * dec);
static gboolean gst_amrwbdec_stop (GstAudioDecoder * dec);
static gboolean gst_amrwbdec_set_format (GstAudioDecoder * dec, GstCaps * caps);
static GstFlowReturn gst_amrwbdec_parse (GstAudioDecoder * dec,
GstAdapter * adapter, gint * offset, gint * length);
static GstFlowReturn gst_amrwbdec_handle_frame (GstAudioDecoder * dec,
GstBuffer * buffer);
#define gst_amrwbdec_parent_class parent_class
G_DEFINE_TYPE (GstAmrwbDec, gst_amrwbdec, GST_TYPE_AUDIO_DECODER);
GST_ELEMENT_REGISTER_DEFINE (amrwbdec, "amrwbdec",
GST_RANK_PRIMARY, GST_TYPE_AMRWBDEC);
static void
gst_amrwbdec_class_init (GstAmrwbDecClass * klass)
{
GstAudioDecoderClass *base_class = GST_AUDIO_DECODER_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_add_static_pad_template (element_class, &sink_template);
gst_element_class_add_static_pad_template (element_class, &src_template);
gst_element_class_set_static_metadata (element_class, "AMR-WB audio decoder",
"Codec/Decoder/Audio",
"Adaptive Multi-Rate Wideband audio decoder",
"Renato Araujo <renato.filho@indt.org.br>");
base_class->start = GST_DEBUG_FUNCPTR (gst_amrwbdec_start);
base_class->stop = GST_DEBUG_FUNCPTR (gst_amrwbdec_stop);
base_class->set_format = GST_DEBUG_FUNCPTR (gst_amrwbdec_set_format);
base_class->parse = GST_DEBUG_FUNCPTR (gst_amrwbdec_parse);
base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_amrwbdec_handle_frame);
GST_DEBUG_CATEGORY_INIT (gst_amrwbdec_debug, "amrwbdec", 0,
"AMR-WB audio decoder");
}
static void
gst_amrwbdec_init (GstAmrwbDec * amrwbdec)
{
gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (amrwbdec), TRUE);
gst_audio_decoder_set_use_default_pad_acceptcaps (GST_AUDIO_DECODER_CAST
(amrwbdec), TRUE);
GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_DECODER_SINK_PAD (amrwbdec));
}
static gboolean
gst_amrwbdec_start (GstAudioDecoder * dec)
{
GstAmrwbDec *amrwbdec = GST_AMRWBDEC (dec);
GST_DEBUG_OBJECT (dec, "start");
if (!(amrwbdec->handle = D_IF_init ()))
return FALSE;
amrwbdec->rate = 0;
amrwbdec->channels = 0;
return TRUE;
}
static gboolean
gst_amrwbdec_stop (GstAudioDecoder * dec)
{
GstAmrwbDec *amrwbdec = GST_AMRWBDEC (dec);
GST_DEBUG_OBJECT (dec, "stop");
D_IF_exit (amrwbdec->handle);
return TRUE;
}
static gboolean
gst_amrwbdec_set_format (GstAudioDecoder * dec, GstCaps * caps)
{
GstStructure *structure;
GstAmrwbDec *amrwbdec;
GstAudioInfo info;
amrwbdec = GST_AMRWBDEC (dec);
structure = gst_caps_get_structure (caps, 0);
/* get channel count */
gst_structure_get_int (structure, "channels", &amrwbdec->channels);
gst_structure_get_int (structure, "rate", &amrwbdec->rate);
/* create reverse caps */
gst_audio_info_init (&info);
gst_audio_info_set_format (&info,
GST_AUDIO_FORMAT_S16, amrwbdec->rate, amrwbdec->channels, NULL);
gst_audio_decoder_set_output_format (dec, &info);
return TRUE;
}
static GstFlowReturn
gst_amrwbdec_parse (GstAudioDecoder * dec, GstAdapter * adapter,
gint * offset, gint * length)
{
GstAmrwbDec *amrwbdec = GST_AMRWBDEC (dec);
guint8 header[1];
guint size;
gboolean sync, eos;
gint block, mode;
size = gst_adapter_available (adapter);
if (size < 1)
return GST_FLOW_ERROR;
gst_audio_decoder_get_parse_state (dec, &sync, &eos);
/* need to peek data to get the size */
gst_adapter_copy (adapter, header, 0, 1);
mode = (header[0] >> 3) & 0x0F;
block = block_size[mode];
GST_DEBUG_OBJECT (amrwbdec, "mode %d, block %d", mode, block);
if (block) {
if (block > size)
return GST_FLOW_EOS;
*offset = 0;
*length = block;
} else {
/* no frame yet, skip one byte */
GST_LOG_OBJECT (amrwbdec, "skipping byte");
*offset = 1;
return GST_FLOW_EOS;
}
return GST_FLOW_OK;
}
static GstFlowReturn
gst_amrwbdec_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer)
{
GstAmrwbDec *amrwbdec;
GstBuffer *out;
GstMapInfo inmap, outmap;
amrwbdec = GST_AMRWBDEC (dec);
/* no fancy flushing */
if (!buffer || !gst_buffer_get_size (buffer))
return GST_FLOW_OK;
/* the library seems to write into the source data, hence the copy. */
/* should be no problem */
gst_buffer_map (buffer, &inmap, GST_MAP_READ);
/* get output */
out = gst_buffer_new_and_alloc (sizeof (gint16) * L_FRAME16k);
gst_buffer_map (out, &outmap, GST_MAP_WRITE);
/* decode */
D_IF_decode (amrwbdec->handle, (unsigned char *) inmap.data,
(short int *) outmap.data, _good_frame);
gst_buffer_unmap (out, &outmap);
gst_buffer_unmap (buffer, &inmap);
/* send out */
return gst_audio_decoder_finish_frame (dec, out, 1);
}

69
ext/amrwbdec/amrwbdec.h Normal file
View file

@ -0,0 +1,69 @@
/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin
* Copyright (C) 2006 Edgard Lima <edgard.lima@gmail.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_AMRWBDEC_H__
#define __GST_AMRWBDEC_H__
#include <gst/gst.h>
#include <gst/audio/gstaudiodecoder.h>
#include <opencore-amrwb/dec_if.h>
#include <opencore-amrwb/if_rom.h>
G_BEGIN_DECLS
#define GST_TYPE_AMRWBDEC \
(gst_amrwbdec_get_type())
#define GST_AMRWBDEC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRWBDEC, GstAmrwbDec))
#define GST_AMRWBDEC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRWBDEC, GstAmrwbDecClass))
#define GST_IS_AMRWBDEC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRWBDEC))
#define GST_IS_AMRWBDEC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRWBDEC))
typedef struct _GstAmrwbDec GstAmrwbDec;
typedef struct _GstAmrwbDecClass GstAmrwbDecClass;
/**
* GstAmrwbDec:
*
* Opaque data structure.
*/
struct _GstAmrwbDec {
GstAudioDecoder element;
/* library handle */
void *handle;
/* output settings */
gint channels, rate;
};
struct _GstAmrwbDecClass {
GstAudioDecoderClass parent_class;
};
GType gst_amrwbdec_get_type (void);
GST_ELEMENT_REGISTER_DECLARE (amrwbdec);
G_END_DECLS
#endif /* __GST_AMRWBDEC_H__ */

14
ext/amrwbdec/meson.build Normal file
View file

@ -0,0 +1,14 @@
amrwb_dep = dependency('opencore-amrwb', version : '>= 0.1.3', required : get_option('amrwbdec'))
if amrwb_dep.found()
amrwbdec = library('gstamrwbdec',
['amrwb.c', 'amrwbdec.c'],
c_args : ugly_args,
include_directories : [configinc],
dependencies : [gstaudio_dep, amrwb_dep],
install : true,
install_dir : plugins_install_dir,
)
pkgconfig.generate(amrwbdec, install_dir : plugins_pkgconfig_install_dir)
plugins += [amrwbdec]
endif

150
ext/cdio/gstcdio.c Normal file
View file

@ -0,0 +1,150 @@
/* GStreamer
* Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <gst/tag/tag.h>
GST_DEBUG_CATEGORY_EXTERN (gst_cdio_debug);
#define GST_CAT_DEFAULT gst_cdio_debug
static gboolean plugin_init (GstPlugin * plugin);
/* cdio headers redefine VERSION etc., so do this here before including them */
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
cdio,
"Read audio from audio CDs",
plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
#include "gstcdio.h"
#include "gstcdiocddasrc.h"
void
gst_cdio_add_cdtext_field (GstObject * src, cdtext_t * cdtext, track_t track,
cdtext_field_t field, const gchar * gst_tag, GstTagList ** p_tags)
{
const gchar *vars[] = { "GST_CDTEXT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
const gchar *txt;
gchar *txt_utf8;
#if LIBCDIO_VERSION_NUM > 83 || LIBCDIO_VERSION_NUM < 76
txt = cdtext_get_const (cdtext, field, track);
#else
txt = cdtext_get_const (field, cdtext);
#endif
if (txt == NULL || *txt == '\0') {
GST_DEBUG_OBJECT (src, "empty CD-TEXT field %u (%s)", field, gst_tag);
return;
}
/* The character encoding is not specified, and there is no provision
* for indicating in the CD-Text data which encoding is in use.. */
txt_utf8 = gst_tag_freeform_string_to_utf8 (txt, -1, vars);
if (txt_utf8 == NULL) {
GST_WARNING_OBJECT (src, "CD-TEXT %s could not be converted to UTF-8, "
"try setting the GST_CDTEXT_TAG_ENCODING or GST_TAG_ENCODING "
"environment variable", gst_tag);
return;
}
/* FIXME: beautify strings (they might be all uppercase for example)? */
if (*p_tags == NULL)
*p_tags = gst_tag_list_new_empty ();
gst_tag_list_add (*p_tags, GST_TAG_MERGE_REPLACE, gst_tag, txt_utf8, NULL);
GST_DEBUG_OBJECT (src, "CD-TEXT: %s = %s", gst_tag, txt_utf8);
g_free (txt_utf8);
}
GstTagList *
#if LIBCDIO_VERSION_NUM > 83 || LIBCDIO_VERSION_NUM < 76
gst_cdio_get_cdtext (GstObject * src, cdtext_t * t, track_t track)
{
GstTagList *tags = NULL;
#else
gst_cdio_get_cdtext (GstObject * src, CdIo * cdio, track_t track)
{
GstTagList *tags = NULL;
cdtext_t *t;
t = cdio_get_cdtext (cdio, track);
if (t == NULL) {
GST_DEBUG_OBJECT (src, "no CD-TEXT for track %u", track);
return NULL;
}
#endif
gst_cdio_add_cdtext_field (src, t, track, CDTEXT_FIELD_PERFORMER,
GST_TAG_ARTIST, &tags);
gst_cdio_add_cdtext_field (src, t, track, CDTEXT_FIELD_TITLE, GST_TAG_TITLE,
&tags);
return tags;
}
void
#if LIBCDIO_VERSION_NUM > 83 || LIBCDIO_VERSION_NUM < 76
gst_cdio_add_cdtext_album_tags (GstObject * src, cdtext_t * t,
GstTagList * tags)
{
#else
gst_cdio_add_cdtext_album_tags (GstObject * src, CdIo * cdio, GstTagList * tags)
{
cdtext_t *t;
t = cdio_get_cdtext (cdio, 0);
if (t == NULL) {
GST_DEBUG_OBJECT (src, "no CD-TEXT for album");
return;
}
#endif
gst_cdio_add_cdtext_field (src, t, 0, CDTEXT_FIELD_PERFORMER,
GST_TAG_ALBUM_ARTIST, &tags);
gst_cdio_add_cdtext_field (src, t, 0, CDTEXT_FIELD_TITLE, GST_TAG_ALBUM,
&tags);
gst_cdio_add_cdtext_field (src, t, 0, CDTEXT_FIELD_GENRE, GST_TAG_GENRE,
&tags);
GST_DEBUG ("CD-TEXT album tags: %" GST_PTR_FORMAT, tags);
}
void
gst_cdio_log_handler (cdio_log_level_t level, const char *msg)
{
const gchar *level_str[] = { "DEBUG", "INFO", "WARN", "ERROR", "ASSERT" };
const gchar *s;
s = level_str[CLAMP (level, 1, G_N_ELEMENTS (level_str)) - 1];
GST_DEBUG ("CDIO-%s: %s", s, GST_STR_NULL (msg));
}
static gboolean
plugin_init (GstPlugin * plugin)
{
return GST_ELEMENT_REGISTER (cdiocddasrc, plugin);
}

61
ext/cdio/gstcdio.h Normal file
View file

@ -0,0 +1,61 @@
/* GStreamer
* Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_CDIO_H__
#define __GST_CDIO_H__
#include <gst/gst.h>
#include <cdio/cdio.h>
#include <cdio/cdtext.h>
#include <cdio/logging.h>
#if LIBCDIO_VERSION_NUM <= 83 && LIBCDIO_VERSION_NUM >= 76
#define CDTEXT_FIELD_PERFORMER CDTEXT_PERFORMER
#define CDTEXT_FIELD_GENRE CDTEXT_GENRE
#define CDTEXT_FIELD_TITLE CDTEXT_TITLE
#endif
void gst_cdio_add_cdtext_field (GstObject * src,
cdtext_t * cdtext,
track_t track,
cdtext_field_t field,
const gchar * gst_tag,
GstTagList ** p_tags);
GstTagList * gst_cdio_get_cdtext (GstObject * src,
#if LIBCDIO_VERSION_NUM > 83 || LIBCDIO_VERSION_NUM < 76
cdtext_t * t,
#else
CdIo * cdio,
#endif
track_t track);
void gst_cdio_add_cdtext_album_tags (GstObject * src,
#if LIBCDIO_VERSION_NUM > 83 || LIBCDIO_VERSION_NUM < 76
cdtext_t * t,
#else
CdIo * cdio,
#endif
GstTagList * tags);
void gst_cdio_log_handler (cdio_log_level_t level, const char *msg);
#endif /* __GST_CDIO_H__ */

518
ext/cdio/gstcdiocddasrc.c Normal file
View file

@ -0,0 +1,518 @@
/* GStreamer
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-cdiocddasrc
* @title: cdiocddasrc
* @see_also: GstCdParanoiaSrc, GstAudioCdSrc
*
* cdiocddasrc reads and extracts raw audio from Audio CDs. It can operate
* in one of two modes:
*
* * treat each track as a separate stream, counting time from the start
* of the track to the end of the track and posting EOS at the end of
* a track, or
* * treat the entire disc as one stream, counting time from the start of
* the first track to the end of the last track, posting EOS only at
* the end of the last track.
*
* With a recent-enough version of libcdio, the element will extract
* CD-TEXT if this is supported by the CD-drive and CD-TEXT information
* is available on the CD. The information will be posted on the bus in
* form of a tag message.
*
* When opened, the element will also calculate a CDDB disc ID and a
* MusicBrainz disc ID, which applications can use to query online
* databases for artist/title information. These disc IDs will also be
* posted on the bus as part of the tag messages.
*
* cdiocddasrc supports the GstUriHandler interface, so applications can use
* playbin with cdda://&lt;track-number&gt; URIs for playback (they will have
* to connect to playbin's notify::source signal and set the device on the
* cd source in the notify callback if they want to set the device property).
* Applications should use seeks in "track" format to switch between different
* tracks of the same CD (passing a new cdda:// URI to playbin involves opening
* and closing the CD device, which is much slower).
*
* ## Example launch line
*
* |[
* gst-launch-1.0 cdiocddasrc track=5 device=/dev/cdrom ! audioconvert ! vorbisenc ! oggmux ! filesink location=track5.ogg
* ]|
* This pipeline extracts track 5 of the audio CD and encodes it into an
* Ogg/Vorbis file.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstcdio.h"
#include "gstcdiocddasrc.h"
#include <gst/gst.h>
#include "gst/gst-i18n-plugin.h"
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define SAMPLES_PER_SECTOR (CDIO_CD_FRAMESIZE_RAW / sizeof (gint16))
#define DEFAULT_READ_SPEED -1
enum
{
PROP_0 = 0,
PROP_READ_SPEED
};
GST_DEBUG_CATEGORY (gst_cdio_debug);
G_DEFINE_TYPE (GstCdioCddaSrc, gst_cdio_cdda_src, GST_TYPE_AUDIO_CD_SRC);
#define _do_init \
GST_DEBUG_CATEGORY_INIT (gst_cdio_debug, "cdio", 0, "libcdio elements"); \
cdio_log_set_handler (gst_cdio_log_handler);
GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (cdiocddasrc, "cdiocddasrc",
GST_RANK_SECONDARY - 1, GST_TYPE_CDIO_CDDA_SRC, _do_init);
static void gst_cdio_cdda_src_finalize (GObject * obj);
static void gst_cdio_cdda_src_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_cdio_cdda_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static GstBuffer *gst_cdio_cdda_src_read_sector (GstAudioCdSrc * src,
gint sector);
static gboolean gst_cdio_cdda_src_open (GstAudioCdSrc * src,
const gchar * device);
static void gst_cdio_cdda_src_close (GstAudioCdSrc * src);
#if 0
static gchar *
gst_cdio_cdda_src_get_default_device (GstAudioCdSrc * audiocdsrc)
{
GstCdioCddaSrc *src;
gchar *default_device, *ret;
src = GST_CDIO_CDDA_SRC (audiocdsrc);
/* src->cdio may be NULL here */
default_device = cdio_get_default_device (src->cdio);
ret = g_strdup (default_device);
free (default_device);
GST_LOG_OBJECT (src, "returning default device: %s", GST_STR_NULL (ret));
return ret;
}
static gchar **
gst_cdio_cdda_src_probe_devices (GstAudioCdSrc * audiocdsrc)
{
char **devices, **ret, **d;
/* FIXME: might return the same hardware device twice, e.g.
* as /dev/cdrom and /dev/dvd - gotta do something more sophisticated */
devices = cdio_get_devices (DRIVER_DEVICE);
if (devices == NULL)
goto no_devices;
if (*devices == NULL)
goto empty_devices;
ret = g_strdupv (devices);
for (d = devices; *d != NULL; ++d) {
GST_DEBUG_OBJECT (audiocdsrc, "device: %s", GST_STR_NULL (*d));
free (*d);
}
free (devices);
return ret;
/* ERRORS */
no_devices:
{
GST_DEBUG_OBJECT (audiocdsrc, "no devices found");
return NULL;
}
empty_devices:
{
GST_DEBUG_OBJECT (audiocdsrc, "empty device list found");
free (devices);
return NULL;
}
}
#endif
static GstBuffer *
gst_cdio_cdda_src_read_sector (GstAudioCdSrc * audiocdsrc, gint sector)
{
GstCdioCddaSrc *src;
guint8 *data;
src = GST_CDIO_CDDA_SRC (audiocdsrc);
data = g_malloc (CDIO_CD_FRAMESIZE_RAW);
/* can't use pad_alloc because we can't return the GstFlowReturn (FIXME 0.11) */
if (cdio_read_audio_sector (src->cdio, data, sector) != 0)
goto read_failed;
if (src->swap_le_be) {
gint16 *pcm_data = (gint16 *) data;
gint i;
for (i = 0; i < SAMPLES_PER_SECTOR; ++i)
pcm_data[i] = GUINT16_SWAP_LE_BE (pcm_data[i]);
}
return gst_buffer_new_wrapped (data, CDIO_CD_FRAMESIZE_RAW);
/* ERRORS */
read_failed:
{
GST_WARNING_OBJECT (src, "read at sector %d failed!", sector);
GST_ELEMENT_ERROR (src, RESOURCE, READ,
(_("Could not read from CD.")),
("cdio_read_audio_sector at %d failed: %s", sector,
g_strerror (errno)));
g_free (data);
return NULL;
}
}
static gboolean
gst_cdio_cdda_src_do_detect_drive_endianness (GstCdioCddaSrc * src, gint from,
gint to)
{
gint16 pcm_data[SAMPLES_PER_SECTOR], last_pcm_ne, last_pcm_oe;
gdouble ne_sumd0, ne_sumd1, ne_factor;
gdouble oe_sumd0, oe_sumd1, oe_factor;
gdouble diff;
gint sector;
gint i;
ne_sumd0 = ne_sumd1 = 0.0;
oe_sumd0 = oe_sumd1 = 0.0;
last_pcm_ne = 0;
last_pcm_oe = 0;
GST_LOG_OBJECT (src, "checking sector %d to %d", from, to);
for (sector = from; sector < to; ++sector) {
if (cdio_read_audio_sector (src->cdio, pcm_data, sector) != 0)
goto read_failed;
/* only evaluate samples for left channel */
for (i = 0; i < SAMPLES_PER_SECTOR; i += 2) {
gint16 pcm;
/* Native endianness first */
pcm = pcm_data[i];
ne_sumd0 += abs (pcm);
ne_sumd1 += abs (pcm - last_pcm_ne);
last_pcm_ne = pcm;
/* other endianness next */
pcm = GUINT16_SWAP_LE_BE (pcm);
oe_sumd0 += abs (pcm);
oe_sumd1 += abs (pcm - last_pcm_oe);
last_pcm_oe = pcm;
}
}
ne_factor = (ne_sumd1 / ne_sumd0);
oe_factor = (oe_sumd1 / oe_sumd0);
diff = ne_factor - oe_factor;
GST_DEBUG_OBJECT (src, "Native: %.2f, Other: %.2f, diff: %.2f",
ne_factor, oe_factor, diff);
if (diff > 0.5) {
GST_INFO_OBJECT (src, "Drive produces samples in other endianness");
src->swap_le_be = TRUE;
return TRUE;
} else if (diff < -0.5) {
GST_INFO_OBJECT (src, "Drive produces samples in host endianness");
src->swap_le_be = FALSE;
return TRUE;
} else {
GST_INFO_OBJECT (src, "Inconclusive, assuming host endianness");
src->swap_le_be = FALSE;
return FALSE;
}
/* ERRORS */
read_failed:
{
GST_WARNING_OBJECT (src, "could not read sector %d", sector);
src->swap_le_be = FALSE;
return FALSE;
}
}
static void
gst_cdio_cdda_src_detect_drive_endianness (GstCdioCddaSrc * src, gint first,
gint last)
{
gint from, to;
GST_INFO ("Detecting drive endianness");
/* try middle of disc first */
from = (first + last) / 2;
to = MIN (from + 10, last);
if (gst_cdio_cdda_src_do_detect_drive_endianness (src, from, to))
return;
/* if that was inconclusive, try other places */
from = (first + last) / 4;
to = MIN (from + 10, last);
if (gst_cdio_cdda_src_do_detect_drive_endianness (src, from, to))
return;
from = (first + last) * 3 / 4;
to = MIN (from + 10, last);
if (gst_cdio_cdda_src_do_detect_drive_endianness (src, from, to))
return;
/* if that's still inconclusive, we give up and assume host endianness */
return;
}
static gboolean
notcdio_track_is_audio_track (const CdIo * p_cdio, track_t i_track)
{
return (cdio_get_track_format (p_cdio, i_track) == TRACK_FORMAT_AUDIO);
}
static gboolean
gst_cdio_cdda_src_open (GstAudioCdSrc * audiocdsrc, const gchar * device)
{
GstCdioCddaSrc *src;
discmode_t discmode;
gint first_track, num_tracks, i;
gint first_audio_sector = 0, last_audio_sector = 0;
#if LIBCDIO_VERSION_NUM > 83 || LIBCDIO_VERSION_NUM < 76
cdtext_t *cdtext;
#endif
src = GST_CDIO_CDDA_SRC (audiocdsrc);
g_assert (device != NULL);
g_assert (src->cdio == NULL);
GST_LOG_OBJECT (src, "trying to open device %s", device);
if (!(src->cdio = cdio_open (device, DRIVER_UNKNOWN)))
goto open_failed;
discmode = cdio_get_discmode (src->cdio);
GST_LOG_OBJECT (src, "discmode: %d", (gint) discmode);
if (discmode != CDIO_DISC_MODE_CD_DA && discmode != CDIO_DISC_MODE_CD_MIXED)
goto not_audio;
first_track = cdio_get_first_track_num (src->cdio);
num_tracks = cdio_get_num_tracks (src->cdio);
if (num_tracks <= 0 || first_track < 0)
return TRUE; /* base class will generate 'has no tracks' error */
if (src->read_speed != -1)
cdio_set_speed (src->cdio, src->read_speed);
#if LIBCDIO_VERSION_NUM > 83 || LIBCDIO_VERSION_NUM < 76
cdtext = cdio_get_cdtext (src->cdio);
if (NULL == cdtext)
GST_DEBUG_OBJECT (src, "no CD-TEXT on disc");
else
gst_cdio_add_cdtext_album_tags (GST_OBJECT_CAST (src), cdtext,
audiocdsrc->tags);
#else
gst_cdio_add_cdtext_album_tags (GST_OBJECT_CAST (src), src->cdio,
audiocdsrc->tags);
#endif
GST_LOG_OBJECT (src, "%u tracks, first track: %d", num_tracks, first_track);
for (i = 0; i < num_tracks; ++i) {
GstAudioCdSrcTrack track = { 0, };
gint len_sectors;
len_sectors = cdio_get_track_sec_count (src->cdio, i + first_track);
track.num = i + first_track;
track.is_audio = notcdio_track_is_audio_track (src->cdio, i + first_track);
/* Note: LSN/LBA confusion all around us; in any case, this does
* the right thing here (for cddb id calculations etc. as well) */
track.start = cdio_get_track_lsn (src->cdio, i + first_track);
track.end = track.start + len_sectors - 1; /* -1? */
if (track.is_audio) {
first_audio_sector = MIN (first_audio_sector, track.start);
last_audio_sector = MAX (last_audio_sector, track.end);
}
#if LIBCDIO_VERSION_NUM > 83 || LIBCDIO_VERSION_NUM < 76
if (NULL != cdtext)
track.tags = gst_cdio_get_cdtext (GST_OBJECT (src), cdtext,
i + first_track);
#else
track.tags = gst_cdio_get_cdtext (GST_OBJECT (src), src->cdio,
i + first_track);
#endif
gst_audio_cd_src_add_track (GST_AUDIO_CD_SRC (src), &track);
}
/* Try to detect if we need to byte-order swap the samples coming from the
* drive, which might be the case if the CD drive operates in a different
* endianness than the host CPU's endianness (happens on e.g. Powerbook G4) */
gst_cdio_cdda_src_detect_drive_endianness (src, first_audio_sector,
last_audio_sector);
return TRUE;
/* ERRORS */
open_failed:
{
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
(_("Could not open CD device for reading.")),
("cdio_open() failed: %s", g_strerror (errno)));
return FALSE;
}
not_audio:
{
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
(_("Disc is not an Audio CD.")), ("discmode: %d", (gint) discmode));
cdio_destroy (src->cdio);
src->cdio = NULL;
return FALSE;
}
}
static void
gst_cdio_cdda_src_close (GstAudioCdSrc * audiocdsrc)
{
GstCdioCddaSrc *src = GST_CDIO_CDDA_SRC (audiocdsrc);
if (src->cdio) {
cdio_destroy (src->cdio);
src->cdio = NULL;
}
}
static void
gst_cdio_cdda_src_init (GstCdioCddaSrc * src)
{
src->read_speed = DEFAULT_READ_SPEED; /* don't need atomic access here */
src->cdio = NULL;
}
static void
gst_cdio_cdda_src_finalize (GObject * obj)
{
GstCdioCddaSrc *src = GST_CDIO_CDDA_SRC (obj);
if (src->cdio) {
cdio_destroy (src->cdio);
src->cdio = NULL;
}
G_OBJECT_CLASS (gst_cdio_cdda_src_parent_class)->finalize (obj);
}
static void
gst_cdio_cdda_src_class_init (GstCdioCddaSrcClass * klass)
{
GstAudioCdSrcClass *audiocdsrc_class = GST_AUDIO_CD_SRC_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = gst_cdio_cdda_src_set_property;
gobject_class->get_property = gst_cdio_cdda_src_get_property;
gobject_class->finalize = gst_cdio_cdda_src_finalize;
audiocdsrc_class->open = gst_cdio_cdda_src_open;
audiocdsrc_class->close = gst_cdio_cdda_src_close;
audiocdsrc_class->read_sector = gst_cdio_cdda_src_read_sector;
#if 0
audiocdsrc_class->probe_devices = gst_cdio_cdda_src_probe_devices;
audiocdsrc_class->get_default_device = gst_cdio_cdda_src_get_default_device;
#endif
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_READ_SPEED,
g_param_spec_int ("read-speed", "Read speed",
"Read from device at the specified speed (-1 = default)", -1, 100,
DEFAULT_READ_SPEED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_set_static_metadata (element_class,
"CD audio source (CDDA)", "Source/File",
"Read audio from CD using libcdio",
"Tim-Philipp Müller <tim centricular net>");
}
static void
gst_cdio_cdda_src_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstCdioCddaSrc *src = GST_CDIO_CDDA_SRC (object);
switch (prop_id) {
case PROP_READ_SPEED:{
gint speed;
speed = g_value_get_int (value);
g_atomic_int_set (&src->read_speed, speed);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_cdio_cdda_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstCdioCddaSrc *src = GST_CDIO_CDDA_SRC (object);
switch (prop_id) {
case PROP_READ_SPEED:{
gint speed;
speed = g_atomic_int_get (&src->read_speed);
g_value_set_int (value, speed);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}

55
ext/cdio/gstcdiocddasrc.h Normal file
View file

@ -0,0 +1,55 @@
/* GStreamer
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_CDIO_CDDA_SRC_H__
#define __GST_CDIO_CDDA_SRC_H__
#include <gst/audio/gstaudiocdsrc.h>
#include <cdio/cdio.h>
#define GST_TYPE_CDIO_CDDA_SRC (gst_cdio_cdda_src_get_type ())
#define GST_CDIO_CDDA_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_CDIO_CDDA_SRC, GstCdioCddaSrc))
#define GST_CDIO_CDDA_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_CDIO_CDDA_SRC, GstCdioCddaSrcClass))
#define GST_IS_CDIO_CDDA_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_CDIO_CDDA_SRC))
#define GST_IS_CDIO_CDDA_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_CDIO_CDDA_SRC))
typedef struct _GstCdioCddaSrc GstCdioCddaSrc;
typedef struct _GstCdioCddaSrcClass GstCdioCddaSrcClass;
struct _GstCdioCddaSrc
{
GstAudioCdSrc audiocdsrc;
gint read_speed; /* ATOMIC */
gboolean swap_le_be; /* Drive produces samples in other endianness */
CdIo *cdio; /* NULL if not open */
};
struct _GstCdioCddaSrcClass
{
GstAudioCdSrcClass audiocdsrc_class;
};
GType gst_cdio_cdda_src_get_type (void);
GST_ELEMENT_REGISTER_DECLARE (cdiocddasrc);
#endif /* __GST_CDIO_CDDA_SRC_H__ */

14
ext/cdio/meson.build Normal file
View file

@ -0,0 +1,14 @@
cdio_dep = dependency('libcdio', version : '>= 0.76', required : get_option('cdio'))
if cdio_dep.found()
cdio = library('gstcdio',
['gstcdio.c', 'gstcdiocddasrc.c'],
c_args : ugly_args,
include_directories : [configinc, libsinc],
dependencies : [gstaudio_dep, gsttag_dep, cdio_dep],
install : true,
install_dir : plugins_install_dir,
)
pkgconfig.generate(cdio, install_dir : plugins_pkgconfig_install_dir)
plugins += [cdio]
endif

4
ext/dvdread/README Normal file
View file

@ -0,0 +1,4 @@
Various Info
============
http://dvd.sourceforge.net/

6
ext/dvdread/demo-play Executable file
View file

@ -0,0 +1,6 @@
#!/bin/sh
AUDIOSINK=`gconftool-2 -g /system/gstreamer/0.8/default/audiosink`
VIDEOSINK=`gconftool-2 -g /system/gstreamer/0.8/default/videosink`
gst-launch dvdreadsrc ! dvddemux name=demux .video_00 ! \
{ queue ! mpeg2dec ! videoconvert ! videoscale ! $VIDEOSINK } \
{ demux.audio_00 ! queue ! a52dec ! audioconvert ! audioscale ! $AUDIOSINK }

1819
ext/dvdread/dvdreadsrc.c Normal file

File diff suppressed because it is too large Load diff

103
ext/dvdread/dvdreadsrc.h Normal file
View file

@ -0,0 +1,103 @@
/* GStreamer DVD title source
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) <2001> Billy Biggs <vektor@dumbterm.net>.
* Copyright (C) <2006> Tim-Philipp Müller <tim centricular 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_DVD_READ_SRC_H__
#define __GST_DVD_READ_SRC_H__
#include <gst/gst.h>
#include <gst/base/gstpushsrc.h>
#include <dvdread/dvd_reader.h>
#include <dvdread/ifo_types.h>
#include <dvdread/ifo_read.h>
#include <dvdread/nav_read.h>
#include <dvdread/nav_print.h>
G_BEGIN_DECLS
#define GST_TYPE_DVD_READ_SRC (gst_dvd_read_src_get_type())
#define GST_DVD_READ_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DVD_READ_SRC,GstDvdReadSrc))
#define GST_DVD_READ_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DVD_READ_SRC,GstDvdReadSrcClass))
#define GST_IS_DVD_READ_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DVD_READ_SRC))
#define GST_IS_DVD_READ_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DVD_READ_SRC))
typedef struct _GstDvdReadSrc GstDvdReadSrc;
typedef struct _GstDvdReadSrcClass GstDvdReadSrcClass;
struct _GstDvdReadSrc {
GstPushSrc pushsrc;
/* location */
gchar *location;
gboolean first_seek;
gboolean new_seek;
gboolean change_cell;
gboolean new_cell;
gint uri_title; /* set via the URI handler or properties, */
gint uri_chapter; /* otherwise not touched; these values */
gint uri_angle; /* start from 1 */
gint title; /* current position while open, set to the */
gint chapter; /* URI-set values in ::start(). these */
gint angle; /* values start from 0 */
gint start_cell, last_cell, cur_cell;
gint cur_pack;
gint next_cell;
dvd_reader_t *dvd;
ifo_handle_t *vmg_file;
/* title stuff */
gint ttn;
tt_srpt_t *tt_srpt;
ifo_handle_t *vts_file;
vts_ptt_srpt_t *vts_ptt_srpt;
vts_tmapt_t *vts_tmapt;
dvd_file_t *dvd_title;
gint num_chapters;
gint num_angles;
GstClockTime *chapter_starts; /* start time of chapters within title */
/* which program chain to watch (based on title and chapter number) */
pgc_t *cur_pgc;
gint pgc_id;
gint pgn;
gboolean need_newsegment;
GstEvent *title_lang_event_pending;
GstEvent *pending_clut_event;
};
struct _GstDvdReadSrcClass {
GstPushSrcClass parent_class;
};
GType gst_dvd_read_src_get_type (void);
GST_ELEMENT_REGISTER_DECLARE (dvdreadsrc);
G_END_DECLS
#endif /* __GST_DVD_READ_SRC_H__ */

14
ext/dvdread/meson.build Normal file
View file

@ -0,0 +1,14 @@
dvdread_dep = dependency('dvdread', version : '>= 0.5.0', required : get_option('dvdread'))
if gmodule_dep.found() and dvdread_dep.found()
dvdread = library('gstdvdread',
['dvdreadsrc.c'],
c_args : ugly_args,
include_directories : [configinc, libsinc],
dependencies : [gstbase_dep, gmodule_dep, dvdread_dep],
install : true,
install_dir : plugins_install_dir,
)
pkgconfig.generate(dvdread, install_dir : plugins_pkgconfig_install_dir)
plugins += [dvdread]
endif

8
ext/meson.build Normal file
View file

@ -0,0 +1,8 @@
subdir('a52dec')
subdir('amrnb')
subdir('amrwbdec')
subdir('cdio')
subdir('dvdread')
subdir('mpeg2dec')
subdir('sidplay')
subdir('x264')

1180
ext/mpeg2dec/gstmpeg2dec.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,90 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_MPEG2DEC_H__
#define __GST_MPEG2DEC_H__
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/video/gstvideodecoder.h>
#include <mpeg2.h>
G_BEGIN_DECLS
#define GST_TYPE_MPEG2DEC \
(gst_mpeg2dec_get_type())
#define GST_MPEG2DEC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MPEG2DEC,GstMpeg2dec))
#define GST_MPEG2DEC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MPEG2DEC,GstMpeg2decClass))
#define GST_IS_MPEG2DEC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MPEG2DEC))
#define GST_IS_MPEG2DEC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MPEG2DEC))
#define MPEG_TIME_TO_GST_TIME(time) ((time) == -1 ? -1 : ((time) * (GST_MSECOND/10)) / G_GINT64_CONSTANT(9))
#define GST_TIME_TO_MPEG_TIME(time) ((time) == -1 ? -1 : ((time) * G_GINT64_CONSTANT(9)) / (GST_MSECOND/10))
typedef struct _GstMpeg2dec GstMpeg2dec;
typedef struct _GstMpeg2decClass GstMpeg2decClass;
typedef enum
{
MPEG2DEC_DISC_NONE = 0,
MPEG2DEC_DISC_NEW_PICTURE,
MPEG2DEC_DISC_NEW_KEYFRAME
} DiscontState;
struct _GstMpeg2dec {
GstVideoDecoder element;
mpeg2dec_t *decoder;
const mpeg2_info_t *info;
/* Buffer lifetime management */
GList *buffers;
/* FIXME This should not be necessary. It is used to prevent image
* corruption when the parser does not behave the way it should.
* See https://bugzilla.gnome.org/show_bug.cgi?id=674238
*/
DiscontState discont_state;
/* video state */
GstVideoCodecState *input_state;
GstVideoInfo decoded_info;
GstVideoAlignment valign;
GstBufferPool * downstream_pool;
gboolean need_alignment;
guint8 *dummybuf[4];
};
struct _GstMpeg2decClass {
GstVideoDecoderClass parent_class;
};
GType gst_mpeg2dec_get_type(void);
GST_ELEMENT_REGISTER_DECLARE (mpeg2dec);
G_END_DECLS
#endif /* __GST_MPEG2DEC_H__ */

14
ext/mpeg2dec/meson.build Normal file
View file

@ -0,0 +1,14 @@
mpeg2_dep = dependency('libmpeg2', version : '>= 0.4.0', required : get_option('mpeg2dec'))
if mpeg2_dep.found()
mpeg2dec = library('gstmpeg2dec',
['gstmpeg2dec.c'],
c_args : ugly_args,
include_directories : [configinc],
dependencies : [gstvideo_dep, mpeg2_dep],
install : true,
install_dir : plugins_install_dir,
)
pkgconfig.generate(mpeg2dec, install_dir : plugins_pkgconfig_install_dir)
plugins += [mpeg2dec]
endif

780
ext/sidplay/gstsiddec.cc Normal file
View file

@ -0,0 +1,780 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* (C) <2006> Wim Taymans <wim@fluendo.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-siddec
*
* This element decodes .sid files to raw audio. .sid files are in fact
* small Commodore 64 programs that are executed on an emulated 6502 CPU and a
* MOS 6581 sound chip.
*
* This plugin will first load the complete program into memory before starting
* the emulator and producing output.
*
* Seeking is not (and cannot be) implemented.
*
* ## Example pipelines
*
* |[
* gst-launch-1.0 -v filesrc location=Hawkeye.sid ! siddec ! audioconvert ! audioresample ! autoaudiosink
* ]| Decode a sid file and play it back.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <gst/audio/audio.h>
#include "gstsiddec.h"
#define DEFAULT_TUNE 0
#define DEFAULT_CLOCK SIDTUNE_CLOCK_PAL
#define DEFAULT_MEMORY MPU_BANK_SWITCHING
#define DEFAULT_FILTER TRUE
#define DEFAULT_MEASURED_VOLUME TRUE
#define DEFAULT_MOS8580 FALSE
#define DEFAULT_FORCE_SPEED FALSE
#define DEFAULT_BLOCKSIZE 4096
enum
{
PROP_0,
PROP_TUNE,
PROP_CLOCK,
PROP_MEMORY,
PROP_FILTER,
PROP_MEASURED_VOLUME,
PROP_MOS8580,
PROP_FORCE_SPEED,
PROP_BLOCKSIZE,
PROP_METADATA
};
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-sid")
);
#define FORMATS "{ " GST_AUDIO_NE(S16) "," GST_AUDIO_NE(U16) ", S8, U8 }"
static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw, "
"format = (string) " FORMATS ", "
"layout = (string) interleaved, "
"rate = (int) [ 8000, 48000 ], " "channels = (int) [ 1, 2 ]")
);
GST_DEBUG_CATEGORY_STATIC (gst_siddec_debug);
#define GST_CAT_DEFAULT gst_siddec_debug
#define GST_TYPE_SID_CLOCK (gst_sid_clock_get_type())
static GType
gst_sid_clock_get_type (void)
{
static GType sid_clock_type = 0;
static const GEnumValue sid_clock[] = {
{SIDTUNE_CLOCK_PAL, "PAL", "pal"},
{SIDTUNE_CLOCK_NTSC, "NTSC", "ntsc"},
{0, NULL, NULL},
};
if (!sid_clock_type) {
sid_clock_type = g_enum_register_static ("GstSidClock", sid_clock);
}
return sid_clock_type;
}
#define GST_TYPE_SID_MEMORY (gst_sid_memory_get_type())
static GType
gst_sid_memory_get_type (void)
{
static GType sid_memory_type = 0;
static const GEnumValue sid_memory[] = {
{MPU_BANK_SWITCHING, "Bank Switching", "bank-switching"},
{MPU_TRANSPARENT_ROM, "Transparent ROM", "transparent-rom"},
{MPU_PLAYSID_ENVIRONMENT, "Playsid Environment", "playsid-environment"},
{0, NULL, NULL},
};
if (!sid_memory_type) {
sid_memory_type = g_enum_register_static ("GstSidMemory", sid_memory);
}
return sid_memory_type;
}
static void gst_siddec_finalize (GObject * object);
static GstFlowReturn gst_siddec_chain (GstPad * pad, GstObject * parent,
GstBuffer * buffer);
static gboolean gst_siddec_sink_event (GstPad * pad, GstObject * parent,
GstEvent * event);
static gboolean gst_siddec_src_convert (GstPad * pad, GstFormat src_format,
gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
static gboolean gst_siddec_src_event (GstPad * pad, GstObject * parent,
GstEvent * event);
static gboolean gst_siddec_src_query (GstPad * pad, GstObject * parent,
GstQuery * query);
static void gst_siddec_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_siddec_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
#define gst_siddec_parent_class parent_class
G_DEFINE_TYPE (GstSidDec, gst_siddec, GST_TYPE_ELEMENT);
GST_ELEMENT_REGISTER_DEFINE (siddec, "siddec", GST_RANK_PRIMARY,
GST_TYPE_SIDDEC);
static void
gst_siddec_class_init (GstSidDecClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->finalize = gst_siddec_finalize;
gobject_class->set_property = gst_siddec_set_property;
gobject_class->get_property = gst_siddec_get_property;
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TUNE,
g_param_spec_int ("tune", "tune", "tune",
0, 100, DEFAULT_TUNE,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CLOCK,
g_param_spec_enum ("clock", "clock", "clock",
GST_TYPE_SID_CLOCK, DEFAULT_CLOCK,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MEMORY,
g_param_spec_enum ("memory", "memory", "memory", GST_TYPE_SID_MEMORY,
DEFAULT_MEMORY,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FILTER,
g_param_spec_boolean ("filter", "filter", "filter", DEFAULT_FILTER,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MEASURED_VOLUME,
g_param_spec_boolean ("measured-volume", "measured_volume",
"measured_volume", DEFAULT_MEASURED_VOLUME,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MOS8580,
g_param_spec_boolean ("mos8580", "mos8580", "mos8580", DEFAULT_MOS8580,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FORCE_SPEED,
g_param_spec_boolean ("force-speed", "force_speed", "force_speed",
DEFAULT_FORCE_SPEED,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BLOCKSIZE,
g_param_spec_uint ("blocksize", "Block size",
"Size in bytes to output per buffer", 1, G_MAXUINT,
DEFAULT_BLOCKSIZE,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (gobject_class, PROP_METADATA,
g_param_spec_boxed ("metadata", "Metadata", "Metadata", GST_TYPE_CAPS,
(GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
gst_element_class_set_static_metadata (gstelement_class, "Sid decoder",
"Codec/Decoder/Audio", "Use libsidplay to decode SID audio tunes",
"Wim Taymans <wim.taymans@gmail.com>");
gst_element_class_add_static_pad_template (gstelement_class, &src_templ);
gst_element_class_add_static_pad_template (gstelement_class, &sink_templ);
GST_DEBUG_CATEGORY_INIT (gst_siddec_debug, "siddec", 0,
"C64 sid song player");
gst_type_mark_as_plugin_api (GST_TYPE_SID_CLOCK, static_cast<GstPluginAPIFlags>(0));
gst_type_mark_as_plugin_api (GST_TYPE_SID_MEMORY, static_cast<GstPluginAPIFlags>(0));
}
static void
gst_siddec_init (GstSidDec * siddec)
{
siddec->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
gst_pad_set_event_function (siddec->sinkpad, gst_siddec_sink_event);
gst_pad_set_chain_function (siddec->sinkpad, gst_siddec_chain);
gst_element_add_pad (GST_ELEMENT (siddec), siddec->sinkpad);
siddec->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
gst_pad_set_event_function (siddec->srcpad, gst_siddec_src_event);
gst_pad_set_query_function (siddec->srcpad, gst_siddec_src_query);
gst_pad_use_fixed_caps (siddec->srcpad);
gst_element_add_pad (GST_ELEMENT (siddec), siddec->srcpad);
siddec->engine = new emuEngine ();
siddec->tune = new sidTune (0);
siddec->config = (emuConfig *) g_malloc (sizeof (emuConfig));
/* get default config parameters */
siddec->engine->getConfig (*siddec->config);
siddec->config->mos8580 = DEFAULT_MOS8580; // mos8580
siddec->config->memoryMode = DEFAULT_MEMORY; // memory mode
siddec->config->clockSpeed = DEFAULT_CLOCK; // clock speed
siddec->config->forceSongSpeed = DEFAULT_FORCE_SPEED; // force song speed
siddec->engine->setConfig (*siddec->config);
siddec->tune_buffer = (guchar *) g_malloc (maxSidtuneFileLen);
siddec->tune_len = 0;
siddec->tune_number = 0;
siddec->total_bytes = 0;
siddec->blocksize = DEFAULT_BLOCKSIZE;
siddec->have_group_id = FALSE;
siddec->group_id = G_MAXUINT;
}
static void
gst_siddec_finalize (GObject * object)
{
GstSidDec *siddec = GST_SIDDEC (object);
g_free (siddec->config);
g_free (siddec->tune_buffer);
delete (siddec->tune);
delete (siddec->engine);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
update_tags (GstSidDec * siddec)
{
sidTuneInfo info;
GstTagList *list;
if (siddec->tune->getInfo (info)) {
list = gst_tag_list_new_empty ();
if (info.nameString) {
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_TITLE, info.nameString, (void *) NULL);
}
if (info.authorString) {
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_ARTIST, info.authorString, (void *) NULL);
}
if (info.copyrightString) {
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_COPYRIGHT, info.copyrightString, (void *) NULL);
}
gst_pad_push_event (siddec->srcpad, gst_event_new_tag (list));
}
}
static gboolean
siddec_negotiate (GstSidDec * siddec)
{
GstCaps *allowed;
GstStructure *structure;
int rate = 44100;
int channels = 1;
GstCaps *caps;
const gchar *str;
GstAudioFormat format;
GstEvent *event;
gchar *stream_id;
allowed = gst_pad_get_allowed_caps (siddec->srcpad);
if (!allowed)
goto nothing_allowed;
GST_DEBUG_OBJECT (siddec, "allowed caps: %" GST_PTR_FORMAT, allowed);
allowed = gst_caps_normalize (allowed);
structure = gst_caps_get_structure (allowed, 0);
str = gst_structure_get_string (structure, "format");
if (str == NULL)
goto invalid_format;
format = gst_audio_format_from_string (str);
switch (format) {
case GST_AUDIO_FORMAT_S8:
siddec->config->bitsPerSample = 8;
siddec->config->sampleFormat = SIDEMU_SIGNED_PCM;
break;
case GST_AUDIO_FORMAT_U8:
siddec->config->bitsPerSample = 8;
siddec->config->sampleFormat = SIDEMU_UNSIGNED_PCM;
break;
case GST_AUDIO_FORMAT_S16:
siddec->config->bitsPerSample = 16;
siddec->config->sampleFormat = SIDEMU_SIGNED_PCM;
break;
case GST_AUDIO_FORMAT_U16:
siddec->config->bitsPerSample = 16;
siddec->config->sampleFormat = SIDEMU_UNSIGNED_PCM;
break;
default:
goto invalid_format;
}
gst_structure_get_int (structure, "rate", &rate);
siddec->config->frequency = rate;
gst_structure_get_int (structure, "channels", &channels);
siddec->config->channels = channels;
stream_id =
gst_pad_create_stream_id (siddec->srcpad, GST_ELEMENT_CAST (siddec),
NULL);
event = gst_pad_get_sticky_event (siddec->sinkpad, GST_EVENT_STREAM_START, 0);
if (event) {
if (gst_event_parse_group_id (event, &siddec->group_id))
siddec->have_group_id = TRUE;
else
siddec->have_group_id = FALSE;
gst_event_unref (event);
} else if (!siddec->have_group_id) {
siddec->have_group_id = TRUE;
siddec->group_id = gst_util_group_id_next ();
}
event = gst_event_new_stream_start (stream_id);
if (siddec->have_group_id)
gst_event_set_group_id (event, siddec->group_id);
gst_pad_push_event (siddec->srcpad, event);
g_free (stream_id);
caps = gst_caps_new_simple ("audio/x-raw",
"format", G_TYPE_STRING, gst_audio_format_to_string (format),
"layout", G_TYPE_STRING, "interleaved",
"rate", G_TYPE_INT, siddec->config->frequency,
"channels", G_TYPE_INT, siddec->config->channels, NULL);
gst_pad_set_caps (siddec->srcpad, caps);
gst_caps_unref (caps);
gst_caps_unref (allowed);
siddec->engine->setConfig (*siddec->config);
return TRUE;
/* ERRORS */
nothing_allowed:
{
GST_DEBUG_OBJECT (siddec, "could not get allowed caps");
return FALSE;
}
invalid_format:
{
GST_DEBUG_OBJECT (siddec, "invalid audio caps");
gst_caps_unref (allowed);
return FALSE;
}
}
static void
play_loop (GstPad * pad)
{
GstFlowReturn ret;
GstSidDec *siddec;
GstBuffer *out;
GstMapInfo outmap;
gint64 value, offset, time = 0;
GstFormat format;
siddec = GST_SIDDEC (gst_pad_get_parent (pad));
out = gst_buffer_new_and_alloc (siddec->blocksize);
gst_buffer_map (out, &outmap, GST_MAP_WRITE);
sidEmuFillBuffer (*siddec->engine, *siddec->tune,
outmap.data, siddec->blocksize);
gst_buffer_unmap (out, &outmap);
/* get offset in samples */
format = GST_FORMAT_DEFAULT;
if (gst_siddec_src_convert (siddec->srcpad,
GST_FORMAT_BYTES, siddec->total_bytes, &format, &offset))
GST_BUFFER_OFFSET (out) = offset;
/* get current timestamp */
format = GST_FORMAT_TIME;
if (gst_siddec_src_convert (siddec->srcpad,
GST_FORMAT_BYTES, siddec->total_bytes, &format, &time))
GST_BUFFER_TIMESTAMP (out) = time;
/* update position and get new timestamp to calculate duration */
siddec->total_bytes += siddec->blocksize;
/* get offset in samples */
format = GST_FORMAT_DEFAULT;
if (gst_siddec_src_convert (siddec->srcpad,
GST_FORMAT_BYTES, siddec->total_bytes, &format, &value))
GST_BUFFER_OFFSET_END (out) = value;
format = GST_FORMAT_TIME;
if (gst_siddec_src_convert (siddec->srcpad,
GST_FORMAT_BYTES, siddec->total_bytes, &format, &value))
GST_BUFFER_DURATION (out) = value - time;
if ((ret = gst_pad_push (siddec->srcpad, out)) != GST_FLOW_OK)
goto pause;
done:
gst_object_unref (siddec);
return;
/* ERRORS */
pause:
{
if (ret == GST_FLOW_EOS) {
/* perform EOS logic, FIXME, segment seek? */
gst_pad_push_event (pad, gst_event_new_eos ());
} else if (ret < GST_FLOW_EOS || ret == GST_FLOW_NOT_LINKED) {
/* for fatal errors we post an error message */
GST_ELEMENT_FLOW_ERROR (siddec, ret);
gst_pad_push_event (pad, gst_event_new_eos ());
}
GST_INFO_OBJECT (siddec, "pausing task, reason: %s",
gst_flow_get_name (ret));
gst_pad_pause_task (pad);
goto done;
}
}
static gboolean
start_play_tune (GstSidDec * siddec)
{
gboolean res;
GstSegment segment;
if (!siddec->tune->load (siddec->tune_buffer, siddec->tune_len))
goto could_not_load;
update_tags (siddec);
if (!siddec_negotiate (siddec))
goto could_not_negotiate;
if (!sidEmuInitializeSong (*siddec->engine, *siddec->tune,
siddec->tune_number))
goto could_not_init;
gst_segment_init (&segment, GST_FORMAT_TIME);
gst_pad_push_event (siddec->srcpad, gst_event_new_segment (&segment));
siddec->total_bytes = 0;
siddec->have_group_id = FALSE;
siddec->group_id = G_MAXUINT;
res = gst_pad_start_task (siddec->srcpad,
(GstTaskFunction) play_loop, siddec->srcpad, NULL);
return res;
/* ERRORS */
could_not_load:
{
GST_ELEMENT_ERROR (siddec, LIBRARY, INIT,
("Could not load tune"), ("Could not load tune"));
return FALSE;
}
could_not_negotiate:
{
GST_ELEMENT_ERROR (siddec, CORE, NEGOTIATION,
("Could not negotiate format"), ("Could not negotiate format"));
return FALSE;
}
could_not_init:
{
GST_ELEMENT_ERROR (siddec, LIBRARY, INIT,
("Could not initialize song"), ("Could not initialize song"));
return FALSE;
}
}
static gboolean
gst_siddec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
GstSidDec *siddec;
gboolean res;
siddec = GST_SIDDEC (parent);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
res = start_play_tune (siddec);
break;
case GST_EVENT_SEGMENT:
res = TRUE;
break;
default:
res = TRUE;
break;
}
gst_event_unref (event);
return res;
}
static GstFlowReturn
gst_siddec_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
GstSidDec *siddec;
guint64 size;
siddec = GST_SIDDEC (parent);
size = gst_buffer_get_size (buffer);
if (siddec->tune_len + size > maxSidtuneFileLen)
goto overflow;
gst_buffer_extract (buffer, 0, siddec->tune_buffer + siddec->tune_len, size);
siddec->tune_len += size;
gst_buffer_unref (buffer);
return GST_FLOW_OK;
/* ERRORS */
overflow:
{
GST_ELEMENT_ERROR (siddec, STREAM, DECODE,
(NULL), ("Input data bigger than allowed buffer size"));
return GST_FLOW_ERROR;
}
}
static gboolean
gst_siddec_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
GstFormat * dest_format, gint64 * dest_value)
{
gboolean res = TRUE;
guint scale = 1;
GstSidDec *siddec;
gint bytes_per_sample;
siddec = GST_SIDDEC (gst_pad_get_parent (pad));
if (src_format == *dest_format) {
*dest_value = src_value;
return TRUE;
}
bytes_per_sample =
(siddec->config->bitsPerSample >> 3) * siddec->config->channels;
switch (src_format) {
case GST_FORMAT_BYTES:
switch (*dest_format) {
case GST_FORMAT_DEFAULT:
if (bytes_per_sample == 0)
return FALSE;
*dest_value = src_value / bytes_per_sample;
break;
case GST_FORMAT_TIME:
{
gint byterate = bytes_per_sample * siddec->config->frequency;
if (byterate == 0)
return FALSE;
*dest_value =
gst_util_uint64_scale_int (src_value, GST_SECOND, byterate);
break;
}
default:
res = FALSE;
}
break;
case GST_FORMAT_DEFAULT:
switch (*dest_format) {
case GST_FORMAT_BYTES:
*dest_value = src_value * bytes_per_sample;
break;
case GST_FORMAT_TIME:
if (siddec->config->frequency == 0)
return FALSE;
*dest_value =
gst_util_uint64_scale_int (src_value, GST_SECOND,
siddec->config->frequency);
break;
default:
res = FALSE;
}
break;
case GST_FORMAT_TIME:
switch (*dest_format) {
case GST_FORMAT_BYTES:
scale = bytes_per_sample;
/* fallthrough */
case GST_FORMAT_DEFAULT:
*dest_value =
gst_util_uint64_scale_int (src_value,
scale * siddec->config->frequency, GST_SECOND);
break;
default:
res = FALSE;
}
break;
default:
res = FALSE;
}
return res;
}
static gboolean
gst_siddec_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
gboolean res = FALSE;
switch (GST_EVENT_TYPE (event)) {
default:
break;
}
gst_event_unref (event);
return res;
}
static gboolean
gst_siddec_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
{
gboolean res = TRUE;
GstSidDec *siddec;
siddec = GST_SIDDEC (parent);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_POSITION:
{
GstFormat format;
gint64 current;
gst_query_parse_position (query, &format, NULL);
/* we only know about our bytes, convert to requested format */
res &= gst_siddec_src_convert (pad,
GST_FORMAT_BYTES, siddec->total_bytes, &format, &current);
if (res) {
gst_query_set_position (query, format, current);
}
break;
}
default:
res = gst_pad_query_default (pad, parent, query);
break;
}
return res;
}
static void
gst_siddec_set_property (GObject * object, guint prop_id, const GValue * value,
GParamSpec * pspec)
{
GstSidDec *siddec = GST_SIDDEC (object);
switch (prop_id) {
case PROP_TUNE:
siddec->tune_number = g_value_get_int (value);
break;
case PROP_CLOCK:
siddec->config->clockSpeed = g_value_get_enum (value);
break;
case PROP_MEMORY:
siddec->config->memoryMode = g_value_get_enum (value);
break;
case PROP_FILTER:
siddec->config->emulateFilter = g_value_get_boolean (value);
break;
case PROP_MEASURED_VOLUME:
siddec->config->measuredVolume = g_value_get_boolean (value);
break;
case PROP_MOS8580:
siddec->config->mos8580 = g_value_get_boolean (value);
break;
case PROP_BLOCKSIZE:
siddec->blocksize = g_value_get_uint (value);
break;
case PROP_FORCE_SPEED:
siddec->config->forceSongSpeed = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
return;
}
siddec->engine->setConfig (*siddec->config);
}
static void
gst_siddec_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstSidDec *siddec = GST_SIDDEC (object);
switch (prop_id) {
case PROP_TUNE:
g_value_set_int (value, siddec->tune_number);
break;
case PROP_CLOCK:
g_value_set_enum (value, siddec->config->clockSpeed);
break;
case PROP_MEMORY:
g_value_set_enum (value, siddec->config->memoryMode);
break;
case PROP_FILTER:
g_value_set_boolean (value, siddec->config->emulateFilter);
break;
case PROP_MEASURED_VOLUME:
g_value_set_boolean (value, siddec->config->measuredVolume);
break;
case PROP_MOS8580:
g_value_set_boolean (value, siddec->config->mos8580);
break;
case PROP_FORCE_SPEED:
g_value_set_boolean (value, siddec->config->forceSongSpeed);
break;
case PROP_BLOCKSIZE:
g_value_set_uint (value, siddec->blocksize);
break;
case PROP_METADATA:
g_value_set_boxed (value, NULL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
plugin_init (GstPlugin * plugin)
{
return GST_ELEMENT_REGISTER (siddec, plugin);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
sid,
"Uses libsidplay to decode .sid files",
plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);

76
ext/sidplay/gstsiddec.h Normal file
View file

@ -0,0 +1,76 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_SIDDEC_H__
#define __GST_SIDDEC_H__
#include <stdlib.h>
#include <sidplay/player.h>
#include <gst/gst.h>
G_BEGIN_DECLS
#define GST_TYPE_SIDDEC \
(gst_siddec_get_type())
#define GST_SIDDEC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SIDDEC,GstSidDec))
#define GST_SIDDEC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SIDDEC,GstSidDecClass))
#define GST_IS_SIDDEC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SIDDEC))
#define GST_IS_SIDDEC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SIDDEC))
typedef struct _GstSidDec GstSidDec;
typedef struct _GstSidDecClass GstSidDecClass;
struct _GstSidDec {
GstElement element;
/* pads */
GstPad *sinkpad,
*srcpad;
gboolean have_group_id;
guint group_id;
guchar *tune_buffer;
gint tune_len;
gint tune_number;
guint64 total_bytes;
emuEngine *engine;
sidTune *tune;
emuConfig *config;
guint blocksize;
};
struct _GstSidDecClass {
GstElementClass parent_class;
};
GType gst_siddec_get_type (void);
GST_ELEMENT_REGISTER_DECLARE (siddec);
G_END_DECLS
#endif /* __GST_SIDDEC_H__ */

38
ext/sidplay/meson.build Normal file
View file

@ -0,0 +1,38 @@
sidplay_option = get_option('sidplay')
if sidplay_option.disabled()
subdir_done()
endif
if not add_languages('cpp', native: false, required: sidplay_option)
subdir_done()
endif
if not cxx.has_header('sidplay/player.h', required: sidplay_option)
subdir_done()
endif
sidplay_dep = cxx.find_library('sidplay', required: sidplay_option)
if not sidplay_dep.found()
subdir_done()
endif
# sidplay plugin works with libsidplay 1.36.x (not 2.x.x)
sid_code = '''#include <sidplay/player.h>
void somefunc (void) {
sidTune tune = sidTune(0);
}'''
if not cxx.compiles(sid_code, dependencies: sidplay_dep, name : 'sidplay usage')
if sidplay_option.enabled()
error('sidplay headers and libraries were found, but were not usable')
endif
subdir_done()
endif
gstsid = library('gstsid', 'gstsiddec.cc',
cpp_args : ugly_args,
include_directories : [configinc],
dependencies : [gstaudio_dep, sidplay_dep],
install : true,
install_dir : plugins_install_dir)
pkgconfig.generate(gstsid, install_dir : plugins_pkgconfig_install_dir)
plugins += [gstsid]

54
ext/x264/GstX264Enc.prs Normal file
View file

@ -0,0 +1,54 @@
[_presets_]
version=0.10
element-name=GstX264Enc
[Profile Baseline]
_meta/comment=Baseline Profile
bframes=0
cabac=false
dct8x8=false
[Profile Main]
_meta/comment=Main Profile
cabac=true
dct8x8=false
[Profile High]
_meta/comment=High Profile
cabac=true
dct8x8=true
[Quality Low]
_meta/comment=Low quality
pass=qual
quantizer=27
subme=4
threads=0
[Quality Normal]
_meta/comment=Normal quality
pass=qual
quantizer=21
me=umh
subme=6
ref=3
threads=0
[Quality High]
_meta/comment=High quality
pass=qual
quantizer=18
me=umh
subme=6
ref=3
threads=0
[Profile YouTube]
_meta/comment=YouTube recommended settings (https://support.google.com/youtube/answer/1722171)
pass=qual
cabac=true
dct8x8=true
bframes=2
[Zero Latency]
tune=zerolatency

View file

@ -0,0 +1,224 @@
/* GStreamer
* Copyright (C) 2019 Thibault Saunier <tsaunier@igalia.com>
*
* gstencoderbitrateprofilemanager.c
*
* 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.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
* 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 "gstencoderbitrateprofilemanager.h"
GST_DEBUG_CATEGORY_STATIC (encoderbitratemanager_debug);
#define GST_CAT_DEFAULT encoderbitratemanager_debug
typedef struct
{
gchar *name;
gsize n_vals;
GstEncoderBitrateTargetForPixelsMap *map;
} GstEncoderBitrateProfile;
struct _GstEncoderBitrateProfileManager
{
GList *profiles;
gchar *preset;
guint bitrate;
gboolean setting_preset;
gboolean user_bitrate;
};
/* *INDENT-OFF* */
/* Copied from https://support.google.com/youtube/answer/1722171?hl=en */
static const GstEncoderBitrateTargetForPixelsMap youtube_bitrate_profiles[] = {
{
.n_pixels = 3840 * 2160,
.low_framerate_bitrate = 40000,
.high_framerate_bitrate = 60000,
},
{
.n_pixels = 2560 * 1440,
.low_framerate_bitrate = 16000,
.high_framerate_bitrate = 24000,
},
{
.n_pixels = 1920 * 1080,
.low_framerate_bitrate = 8000,
.high_framerate_bitrate = 12000,
},
{
.n_pixels = 1080 * 720,
.low_framerate_bitrate = 5000,
.high_framerate_bitrate = 7500,
},
{
.n_pixels = 640 * 480,
.low_framerate_bitrate = 2500,
.high_framerate_bitrate = 4000,
},
{
.n_pixels = 0,
.low_framerate_bitrate = 2500,
.high_framerate_bitrate = 4000,
},
{
.n_pixels = 0,
.low_framerate_bitrate = 0,
.high_framerate_bitrate = 0,
},
};
/* *INDENT-ON* */
static void
gst_encoder_bitrate_profile_free (GstEncoderBitrateProfile * profile)
{
g_free (profile->name);
g_free (profile->map);
g_free (profile);
}
void
gst_encoder_bitrate_profile_manager_add_profile (GstEncoderBitrateProfileManager
* self, const gchar * profile_name,
const GstEncoderBitrateTargetForPixelsMap * map)
{
guint n_vals;
GstEncoderBitrateProfile *profile;
for (n_vals = 0;
map[n_vals].low_framerate_bitrate != 0
&& map[n_vals].high_framerate_bitrate != 0; n_vals++);
n_vals++;
profile = g_new0 (GstEncoderBitrateProfile, 1);
profile->name = g_strdup (profile_name);
profile->n_vals = n_vals;
profile->map
= g_memdup2 (map, sizeof (GstEncoderBitrateTargetForPixelsMap) * n_vals);
self->profiles = g_list_prepend (self->profiles, profile);
}
guint
gst_encoder_bitrate_profile_manager_get_bitrate (GstEncoderBitrateProfileManager
* self, GstVideoInfo * info)
{
gint i;
gboolean high_fps;
guint num_pix;
GList *tmp;
GstEncoderBitrateProfile *profile = NULL;
g_return_val_if_fail (self != NULL, -1);
if (!info || info->finfo == NULL
|| info->finfo->format == GST_VIDEO_FORMAT_UNKNOWN) {
GST_INFO ("Video info %p not usable, returning current bitrate", info);
return self->bitrate;
}
if (!self->preset) {
GST_INFO ("No preset used, returning current bitrate");
return self->bitrate;
}
for (tmp = self->profiles; tmp; tmp = tmp->next) {
GstEncoderBitrateProfile *tmpprof = tmp->data;
if (!g_strcmp0 (tmpprof->name, self->preset)) {
profile = tmpprof;
break;
}
}
if (!profile) {
GST_INFO ("Could not find map for profile: %s", self->preset);
return self->bitrate;
}
high_fps = GST_VIDEO_INFO_FPS_N (info) / GST_VIDEO_INFO_FPS_D (info) > 30.0;
num_pix = GST_VIDEO_INFO_WIDTH (info) * GST_VIDEO_INFO_HEIGHT (info);
for (i = 0; i < profile->n_vals; i++) {
GstEncoderBitrateTargetForPixelsMap *bitrate_values = &profile->map[i];
if (num_pix < bitrate_values->n_pixels)
continue;
self->bitrate =
high_fps ? bitrate_values->
high_framerate_bitrate : bitrate_values->low_framerate_bitrate;
GST_INFO ("Using %s bitrate! %d", self->preset, self->bitrate);
return self->bitrate;
}
return -1;
}
void gst_encoder_bitrate_profile_manager_start_loading_preset
(GstEncoderBitrateProfileManager * self)
{
self->setting_preset = TRUE;
}
void gst_encoder_bitrate_profile_manager_end_loading_preset
(GstEncoderBitrateProfileManager * self, const gchar * preset)
{
self->setting_preset = FALSE;
g_free (self->preset);
self->preset = g_strdup (preset);
}
void
gst_encoder_bitrate_profile_manager_set_bitrate (GstEncoderBitrateProfileManager
* self, guint bitrate)
{
self->bitrate = bitrate;
self->user_bitrate = !self->setting_preset;
}
void
gst_encoder_bitrate_profile_manager_free (GstEncoderBitrateProfileManager *
self)
{
g_free (self->preset);
g_list_free_full (self->profiles,
(GDestroyNotify) gst_encoder_bitrate_profile_free);
g_free (self);
}
GstEncoderBitrateProfileManager *
gst_encoder_bitrate_profile_manager_new (guint default_bitrate)
{
GstEncoderBitrateProfileManager *self =
g_new0 (GstEncoderBitrateProfileManager, 1);
static gsize _init = 0;
if (g_once_init_enter (&_init)) {
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "encoderbitratemanager", 0,
"Encoder bitrate manager");
g_once_init_leave (&_init, 1);
}
self->bitrate = default_bitrate;
gst_encoder_bitrate_profile_manager_add_profile (self,
"Profile YouTube", youtube_bitrate_profiles);
return self;
}

View file

@ -0,0 +1,46 @@
/* GStreamer
* Copyright (C) 2019 Thibault Saunier <tsaunier@igalia.com>
*
* gstencoderbitrateprofilemanager.h
*
* 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.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
* 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.
*/
#pragma once
#include <gst/gst.h>
#include <gst/video/video.h>
typedef struct _GstEncoderBitrateProfileManager GstEncoderBitrateProfileManager;
typedef struct _GstEncoderBitrateTargetForPixelsMap
{
guint n_pixels;
guint low_framerate_bitrate;
guint high_framerate_bitrate;
gpointer _gst_reserved[GST_PADDING_LARGE];
} GstEncoderBitrateTargetForPixelsMap;
void
gst_encoder_bitrate_profile_manager_add_profile(GstEncoderBitrateProfileManager* self,
const gchar* profile_name, const GstEncoderBitrateTargetForPixelsMap* map);
guint gst_encoder_bitrate_profile_manager_get_bitrate(GstEncoderBitrateProfileManager* self, GstVideoInfo* info);
void gst_encoder_bitrate_profile_manager_start_loading_preset (GstEncoderBitrateProfileManager* self);
void gst_encoder_bitrate_profile_manager_end_loading_preset(GstEncoderBitrateProfileManager* self, const gchar* preset);
void gst_encoder_bitrate_profile_manager_set_bitrate(GstEncoderBitrateProfileManager* self, guint bitrate);
GstEncoderBitrateProfileManager* gst_encoder_bitrate_profile_manager_new(guint default_bitrate);
void gst_encoder_bitrate_profile_manager_free(GstEncoderBitrateProfileManager* self);

3057
ext/x264/gstx264enc.c Normal file

File diff suppressed because it is too large Load diff

144
ext/x264/gstx264enc.h Normal file
View file

@ -0,0 +1,144 @@
/* GStreamer H264 encoder plugin
* Copyright (C) 2005 Michal Benes <michal.benes@itonis.tv>
* Copyright (C) 2005 Josef Zlomek <josef.zlomek@itonis.tv>
* Copyright (C) 2016 Sebastian Dröge <sebastian@centricular.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_X264_ENC_H__
#define __GST_X264_ENC_H__
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/video/gstvideoencoder.h>
#include "gstencoderbitrateprofilemanager.h"
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
/* The x264.h header says this isn't needed with MinGW, but sometimes the
* compiler is unable to correctly do the pointer indirection for us, which
* leads to a segfault when you try to dereference any const values provided
* by x264.dll. See: https://bugzilla.gnome.org/show_bug.cgi?id=779249 */
#if defined(_WIN32) && !defined(X264_API_IMPORTS)
# define X264_API_IMPORTS
#endif
#include <x264.h>
G_BEGIN_DECLS
#define GST_TYPE_X264_ENC \
(gst_x264_enc_get_type())
#define GST_X264_ENC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_X264_ENC,GstX264Enc))
#define GST_X264_ENC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_X264_ENC,GstX264EncClass))
#define GST_IS_X264_ENC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_X264_ENC))
#define GST_IS_X264_ENC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_X264_ENC))
typedef struct _GstX264Enc GstX264Enc;
typedef struct _GstX264EncClass GstX264EncClass;
typedef struct _GstX264EncVTable GstX264EncVTable;
struct _GstX264Enc
{
GstVideoEncoder element;
/*< private >*/
GstX264EncVTable *vtable;
x264_t *x264enc;
x264_param_t x264param;
gint current_byte_stream;
/* List of frame/buffer mapping structs for
* pending frames */
GList *pending_frames;
/* properties */
guint threads;
gboolean sliced_threads;
gint sync_lookahead;
gint pass;
guint quantizer;
gchar *mp_cache_file;
gboolean byte_stream;
guint bitrate;
gboolean intra_refresh;
gint me;
guint subme;
guint analyse;
gboolean dct8x8;
guint ref;
guint bframes;
gboolean b_adapt;
gboolean b_pyramid;
gboolean weightb;
guint sps_id;
gboolean au_nalu;
gboolean trellis;
guint vbv_buf_capacity;
guint keyint_max;
gboolean cabac;
gfloat ip_factor;
gfloat pb_factor;
guint qp_min;
guint qp_max;
guint qp_step;
gboolean mb_tree;
gint rc_lookahead;
guint noise_reduction;
gboolean interlaced;
gint speed_preset;
gint psy_tune;
guint tune;
GString *tunings;
GString *option_string_prop; /* option-string property */
GString *option_string; /* used by set prop */
gint frame_packing;
gboolean insert_vui;
/* input description */
GstVideoCodecState *input_state;
/* configuration changed while playing */
gboolean reconfig;
/* from the downstream caps */
const gchar *peer_profile;
gboolean peer_intra_profile;
gint peer_level_idc;
/* cached values to set x264_picture_t */
gint x264_nplanes;
GstEncoderBitrateProfileManager *bitrate_manager;
};
struct _GstX264EncClass
{
GstVideoEncoderClass parent_class;
};
GType gst_x264_enc_get_type (void);
GST_ELEMENT_REGISTER_DECLARE (x264enc);
G_END_DECLS
#endif /* __GST_X264_ENC_H__ */

31
ext/x264/meson.build Normal file
View file

@ -0,0 +1,31 @@
x264_sources = [
'gstx264enc.c',
'gstencoderbitrateprofilemanager.c',
]
x264_dep = dependency('x264', required : get_option('x264'),
fallback: ['x264', 'libx264_dep'])
if x264_dep.found()
x264_libraries = get_option('x264_libraries')
x264_args = []
if x264_libraries != ''
x264_args += ['-DHAVE_X264_ADDITIONAL_LIBRARIES="@0@"'.format(x264_libraries)]
extra_gmodule_dep = [gmodule_dep]
else
extra_gmodule_dep = []
endif
gstx264 = library('gstx264',
x264_sources,
c_args : ugly_args + x264_args,
include_directories : [configinc],
dependencies : [gstbase_dep, gstvideo_dep, gstpbutils_dep, x264_dep] + extra_gmodule_dep,
install : true,
install_dir : plugins_install_dir,
)
pkgconfig.generate(gstx264, install_dir : plugins_pkgconfig_install_dir)
plugins += [gstx264]
install_data(sources: 'GstX264Enc.prs', install_dir: presetdir)
endif

69
gst-libs/gst/gettext.h Normal file
View file

@ -0,0 +1,69 @@
/* Convenience header for conditional use of GNU <libintl.h>.
Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
This program 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, 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
USA. */
#ifndef _LIBGETTEXT_H
#define _LIBGETTEXT_H 1
/* NLS can be disabled through the configure --disable-nls option. */
#ifdef ENABLE_NLS
/* Get declarations of GNU message catalog functions. */
# include <libintl.h>
#else
/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
chokes if dcgettext is defined as a macro. So include it now, to make
later inclusions of <locale.h> a NOP. We don't include <libintl.h>
as well because people using "gettext.h" will not include <libintl.h>,
and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
is OK. */
#if defined(__sun)
# include <locale.h>
#endif
/* Disabled NLS.
The casts to 'const char *' serve the purpose of producing warnings
for invalid uses of the value returned from these functions.
On pre-ANSI systems without 'const', the config.h file is supposed to
contain "#define const". */
# define gettext(Msgid) ((const char *) (Msgid))
# define dgettext(Domainname, Msgid) ((const char *) (Msgid))
# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid))
# define ngettext(Msgid1, Msgid2, N) \
((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
# define dngettext(Domainname, Msgid1, Msgid2, N) \
((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
# define textdomain(Domainname) ((const char *) (Domainname))
# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset))
#endif
/* A pseudo function call that serves as a marker for the automated
extraction of messages, but does not call gettext(). The run-time
translation is done at a different place in the code.
The argument, String, should be a literal string. Concatenated strings
and other string expressions won't work.
The macro's expansion is not parenthesized, so that it is suitable as
initializer for static 'char[]' or 'const char[]' variables. */
#define gettext_noop(String) String
#endif /* _LIBGETTEXT_H */

View file

@ -0,0 +1,36 @@
/*
* glib-compat.c
* Functions copied from glib 2.10
*
* Copyright 2005 David Schleef <ds@schleef.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GLIB_COMPAT_PRIVATE_H__
#define __GLIB_COMPAT_PRIVATE_H__
#include <glib.h>
G_BEGIN_DECLS
/* copies */
/* adaptations */
G_END_DECLS
#endif

View file

@ -0,0 +1,37 @@
/* GStreamer
* Copyright (C) 2004 Thomas Vander Stichele <thomas@apestaart.org>
*
* gst-i18n-plugins.h: internationalization macros for the GStreamer plugins
*
* 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_I18N_PLUGIN_H__
#define __GST_I18N_PLUGIN_H__
#include <locale.h> /* some people need it and some people don't */
#include "gettext.h" /* included with gettext distribution and copied */
#ifndef GETTEXT_PACKAGE
#error You must define GETTEXT_PACKAGE before including this header.
#endif
/* we want to use shorthand _() for translating and N_() for marking */
#define _(String) dgettext (GETTEXT_PACKAGE, String)
#define N_(String) gettext_noop (String)
/* FIXME: if we need it, we can add Q_ as well, like in glib */
#endif /* __GST_I18N_PLUGIN_H__ */

875
gst-plugins-ugly.doap Normal file
View file

@ -0,0 +1,875 @@
<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 Ugly Plug-ins</name>
<shortname>gst-plugins-ugly</shortname>
<homepage rdf:resource="http://gstreamer.freedesktop.org/modules/gst-plugins-ugly.html" />
<created>2005-09-06</created>
<shortdesc xml:lang="en">
a set of good-quality plug-ins with license or patent problems.
</shortdesc>
<description xml:lang="en">
GStreamer Ugly Plug-ins is a set of plug-ins that have good quality and correct
functionality, but distributing them might pose problems. The license on either
the plug-ins or the supporting libraries might not be how we'd like. The code
might be widely known to present patent problems.
</description>
<category></category>
<bug-database rdf:resource="https://gitlab.freedesktop.org/gstreamer/gst-plugins-ugly/issues/" />
<screenshots></screenshots>
<mailing-list rdf:resource="http://lists.freedesktop.org/mailman/listinfo/gstreamer-devel" />
<programming-language>C</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="git://gitlab.freedesktop.org/gstreamer/gst-plugins-ugly"/>
<browse rdf:resource="http://gitlab.freedesktop.org/gstreamer/gst-plugins-ugly"/>
</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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-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-plugins-ugly/gst-plugins-ugly-1.11.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.10.0</revision>
<branch>master</branch>
<name></name>
<created>2016-11-01</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.10.0.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.9.90</revision>
<branch>master</branch>
<name></name>
<created>2016-09-30</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.9.90.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.9.2</revision>
<branch>master</branch>
<name></name>
<created>2016-09-01</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.9.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.9.1</revision>
<branch>master</branch>
<name></name>
<created>2016-06-06</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.9.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.8.0</revision>
<branch>master</branch>
<name></name>
<created>2016-03-24</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.8.0.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.7.91</revision>
<branch>master</branch>
<name></name>
<created>2016-03-15</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.7.91.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.7.90</revision>
<branch>master</branch>
<name></name>
<created>2016-03-01</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.7.90.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.7.2</revision>
<branch>master</branch>
<name></name>
<created>2016-02-19</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.7.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.7.1</revision>
<branch>master</branch>
<name></name>
<created>2015-12-24</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.7.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.6.2</revision>
<branch>1.6</branch>
<name></name>
<created>2015-12-14</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.6.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.6.1</revision>
<branch>1.6</branch>
<name></name>
<created>2015-10-30</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.6.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.6.0</revision>
<branch>1.6</branch>
<name></name>
<created>2015-09-25</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.6.0.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.5.91</revision>
<branch>1.5</branch>
<name></name>
<created>2015-09-18</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.5.91.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.5.90</revision>
<branch>1.5</branch>
<name></name>
<created>2015-08-19</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.5.90.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.5.2</revision>
<branch>1.5</branch>
<name></name>
<created>2015-06-24</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.5.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.5.1</revision>
<branch>1.5</branch>
<name></name>
<created>2015-06-07</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.5.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.4.0</revision>
<branch>1.4</branch>
<name></name>
<created>2014-07-19</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.4.0.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.3.91</revision>
<branch>1.3</branch>
<name></name>
<created>2014-07-11</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.3.91.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.3.90</revision>
<branch>1.3</branch>
<name></name>
<created>2014-06-28</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.3.90.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.3.3</revision>
<branch>1.3</branch>
<name></name>
<created>2014-06-22</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.3.3.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.3.2</revision>
<branch>1.3</branch>
<name></name>
<created>2014-05-21</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.3.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.3.1</revision>
<branch>1.3</branch>
<name></name>
<created>2014-05-03</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.3.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.2.0</revision>
<branch>1.2</branch>
<name></name>
<created>2013-09-24</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.2.0.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.1.90</revision>
<branch>1.1</branch>
<name></name>
<created>2013-09-19</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.1.90.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.1.4</revision>
<branch>1.1</branch>
<name></name>
<created>2013-08-28</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.1.4.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.1.3</revision>
<branch>1.1</branch>
<name></name>
<created>2013-07-29</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.1.3.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.1.2</revision>
<branch>1.1</branch>
<name></name>
<created>2013-07-11</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.1.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.1.1</revision>
<branch>1.1</branch>
<name></name>
<created>2013-06-05</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.1.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.0.2</revision>
<branch>1.0</branch>
<name></name>
<created>2012-10-24</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.0.2.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.0.1</revision>
<branch>1.0</branch>
<name></name>
<created>2012-10-07</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.0.1.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>1.0.0</revision>
<branch>1.0</branch>
<name></name>
<created>2012-09-24</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.0.0.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>0.11.99</revision>
<branch>0.11</branch>
<name>Stole Many a Mans Soul and Faith</name>
<created>2012-09-17</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.11.99.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>0.11.94</revision>
<branch>0.11</branch>
<name>Rag Hummin'</name>
<created>2012-09-14</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.11.94.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>0.11.93</revision>
<branch>0.11</branch>
<name>Rag Doll Physics</name>
<created>2012-08-08</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.11.93.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>0.11.92</revision>
<branch>0.11</branch>
<name>Hey You</name>
<created>2012-06-07</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.11.92.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>0.11.91</revision>
<branch>0.11</branch>
<name>Ill grind his bones to make my bread!</name>
<created>2012-05-13</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.11.91.tar.xz" />
</Version>
</release>
<release>
<Version>
<revision>0.11.90</revision>
<branch>0.11</branch>
<name>Is all that we see or seem but a dream within a dream?</name>
<created>2012-04-12</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.11.90.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.11.90.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.11.2</revision>
<branch>0.11</branch>
<name>Savior from Anger</name>
<created>2012-03-22</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.11.2.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.11.2.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.11.1</revision>
<branch>0.11</branch>
<name>Moonlapse Vertigo</name>
<created>2012-02-16</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.11.1.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.11.1.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.18</revision>
<branch>0.10</branch>
<name>Extra Life</name>
<created>2011-05-10</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.18.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.18.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.17</revision>
<branch>0.10</branch>
<name>Raised by Wolves</name>
<created>2011-01-21</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.17.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.17.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.16</revision>
<branch>0.10</branch>
<name>Because He Knows the Time is Short</name>
<created>2010-09-02</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.16.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.16.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.15</revision>
<branch>0.10</branch>
<name>Englishman in a Baseball Cap</name>
<created>2010-05-30</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.15.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.15.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.14</revision>
<branch>0.10</branch>
<name>Run Rabbit</name>
<created>2010-03-06</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.14.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.14.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.13</revision>
<branch>0.10</branch>
<name>Appointed Point</name>
<created>2009-10-21</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.13.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.13.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.12</revision>
<branch>0.10</branch>
<name>Moving Along The Highway</name>
<created>2009-06-18</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.12.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.12.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.11</revision>
<branch>0.10</branch>
<name>Their swords and their knives</name>
<created>2009-03-20</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.11.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.11.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.10</revision>
<branch>0.10</branch>
<name>Under the House</name>
<created>2008-11-19</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.10.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.10.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.9</revision>
<branch>0.10</branch>
<name>Back The Way It Was</name>
<created>2008-08-26</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.9.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.9.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.8</revision>
<branch>0.10</branch>
<name>And yet</name>
<created>2008-05-21</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.8.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.8.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.7</revision>
<branch>0.10</branch>
<name>I Cheated Time</name>
<created>2008-01-20</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.7.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.7.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.6</revision>
<branch>0.10</branch>
<name>The Sacrifice</name>
<created>2007-06-19</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.6.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.6.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.5</revision>
<branch>0.10</branch>
<name>She said</name>
<created>2006-12-13</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.5.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.5.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.4</revision>
<branch>0.10</branch>
<name>Metal Molly</name>
<created>2006-08-14</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.4.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.4.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.3</revision>
<branch>0.10</branch>
<name>Late Plane</name>
<created>2006-03-31</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.3.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.3.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.2</revision>
<branch>0.10</branch>
<name>Season to Taste</name>
<created>2006-02-20</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.2.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.2.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.1</revision>
<branch>0.10</branch>
<name>Peng</name>
<created>2006-01-13</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.1.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.1.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.0</revision>
<branch>0.10</branch>
<name>Brie</name>
<created>2005-12-05</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.0.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-0.10.0.tar.gz" />
</Version>
</release>
<maintainer>
<foaf:Person>
<foaf:name>Wim Taymans</foaf:name>
<foaf:mbox_sha1sum>0d93fde052812d51a05fd86de9bdbf674423daa2</foaf:mbox_sha1sum>
</foaf:Person>
</maintainer>
</Project>

88
gst/asfdemux/README Normal file
View file

@ -0,0 +1,88 @@
ASF Demuxer Plugin
==================
Overview
--------
This plugin is a demuxer for Microsoft's ASF Advanced Streaming Format
or ASF [1]. This demuxer only supports ASF v1.0 since the vast
majority of existing ASF files use that version. The specification
has been derived from a third party source [2] without reference to
the original.
Design
------
The ASF format can carry any combination of audio, video or
'ASF_Command_Media' streams. For simplicity it is assumed that each
file can carry up to 16 audio streams and 16 video streams. These are
implemented as dynamic pads and appear as appropriate once the file
headers have been parsed.
(-------------------------)
! asfdemux !
! (video/raw0)---
! (video/raw1)---
! (video/raw...
--- src !
! (audio/raw0)---
! (audio/raw1)---
! (audio/raw...
! !
(-------------------------)
Known stream fourccs are:
Type Tags MIME type
------------------------------------------
H263 H263 I263 video/x-h263
MJPEG MJPG image/jpeg
MPEG4 DIVX divx DX50 video/mpeg
XVID xvid mp4s
MP4S M4S2 m4s2
0x04000000
MSMPEG4V1 MPG4 video/mpeg
MSMPEG4V2 MP42 video/mpeg
MSMPEG4V3 MP43 DIV3 video/mpeg
WMV1 WMV1 video/x-wmv, wmvversion = (int) 1
WMV2 WMV2 video/x-wmv, wmvversion = (int) 2
WMV3 WMV3 video/x-wmv, wmvversion = (int) 3
WMA1 WMA1 audio/x-wma, wmaversion = (int) 1
WMA2 WMA2 audio/x-wma, wmaversion = (int) 2
audio/x-wma, wmaversion = (int) 3
These video stream headers is very similar to that used in the AVI
format as are the audio stream headers. In addition the content types
are basically the same also so, for compatibility with existing
plugins the src pads are set up as video/x-msvideo. This enables
compatibility with the ffmpeg plugin.
The demuxing process begins with the loop function gst_asf_demux_loop
and parses the file in a recursive tree as follows:
gst_asf_demux_loop()
+-> gst_asf_demux_process_object() <----
+-> gst_asf_demux_process_stream() \
|-> gst_asf_demux_process_file() |
|-> gst_asf_demux_process_header() --+
|-> gst_asf_demux_process_data()
+-> gst_asf_demux_process_segment()
+-> gst_asf_demux_process_chunk()
Todo
----
- Support for ASF v2.0
- Support for command media streams
References
----------
[1] Microsoft. ASF Specification - Windows Media Technologies.
http://www.microsoft.com/windows/windowsmedia/format/asfspec.aspx (v01.20.01e, September 2003)
[2] divx at euro.ru. ASF format version 1.0,
reconstruction. http://avifile.sourceforge.net/asf-1.0.htm

212
gst/asfdemux/asfheaders.c Normal file
View file

@ -0,0 +1,212 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <gst/gst.h>
#include "asfheaders.h"
const ASFGuidHash asf_payload_ext_guids[] = {
{ASF_PAYLOAD_EXTENSION_DURATION, "ASF_PAYLOAD_EXTENSION_DURATION",
{0xC6BD9450, 0x4907867F, 0x79C7A383, 0xAD33B721}
},
{ASF_PAYLOAD_EXTENSION_SYSTEM_CONTENT, "ASF_PAYLOAD_EXTENSION_SYSTEM_CONTENT",
{0xD590DC20, 0x436C07BC, 0xBBF3f79C, 0xDCA4F1FB}},
{ASF_PAYLOAD_EXTENSION_SYSTEM_PIXEL_ASPECT_RATIO,
"ASF_PAYLOAD_EXTENSION_SYSTEM_PIXEL_ASPECT_RATIO",
{0x1b1ee554, 0x4bc8f9ea, 0x6b371a82, 0xb8c4e474}},
{ASF_PAYLOAD_EXTENSION_TIMING, "ASF_PAYLOAD_EXTENSION_TIMING",
{0XFD3CC02A, 0X4CFA06DB, 0X12721C80, 0XE44587D3}},
{ASF_PAYLOAD_EXTENSION_UNDEFINED, "ASF_PAYLOAD_EXTENSION_UNDEFINED",
{0, 0, 0, 0}
}
};
const ASFGuidHash asf_correction_guids[] = {
{ASF_CORRECTION_ON, "ASF_CORRECTION_ON",
{0xBFC3CD50, 0x11CF618F, 0xAA00B28B, 0x20E2B400}
},
{ASF_CORRECTION_OFF, "ASF_CORRECTION_OFF",
{0x20FB5700, 0x11CF5B55, 0x8000FDA8, 0x2B445C5F}
},
/* CHECKME: where does this 49F1A440... GUID come from? (tpm) */
{ASF_CORRECTION_OFF, "ASF_CORRECTION_OFF",
{0x49F1A440, 0x11D04ECE, 0xA000ACA3, 0xF64803C9}
},
{ASF_CORRECTION_UNDEFINED, "ASF_CORRECTION_UNDEFINED",
{0, 0, 0, 0}
}
};
const ASFGuidHash asf_stream_guids[] = {
{ASF_STREAM_VIDEO, "ASF_STREAM_VIDEO",
{0xBC19EFC0, 0x11CF5B4D, 0x8000FDA8, 0x2B445C5F}
},
{ASF_STREAM_AUDIO, "ASF_STREAM_AUDIO",
{0xF8699E40, 0x11CF5B4D, 0x8000FDA8, 0x2B445C5F}
},
{ASF_STREAM_EXT_EMBED_HEADER, "ASF_STREAM_EXT_EMBED_HEADER",
{0X3AFB65E2, 0X40F247EF, 0XA9702CAC, 0X43D3710D}},
{ASF_STREAM_UNDEFINED, "ASF_STREAM_UNDEFINED",
{0, 0, 0, 0}
}
};
const ASFGuidHash asf_ext_stream_guids[] = {
{ASF_EXT_STREAM_AUDIO, "ASF_EXT_STREAM_AUDIO",
{0X31178C9D, 0X452803E1, 0XF93D82B5, 0X03F522DB}
},
{ASF_EXT_STREAM_UNDEFINED, "ASF_EXT_STREAM_UNDEFINED",
{0, 0, 0, 0}
}
};
const ASFGuidHash asf_object_guids[] = {
{ASF_OBJ_STREAM, "ASF_OBJ_STREAM",
{0xB7DC0791, 0x11CFA9B7, 0xC000E68E, 0x6553200C}
},
{ASF_OBJ_DATA, "ASF_OBJ_DATA",
{0x75b22636, 0x11cf668e, 0xAA00D9a6, 0x6Cce6200}
},
{ASF_OBJ_FILE, "ASF_OBJ_FILE",
{0x8CABDCA1, 0x11CFA947, 0xC000E48E, 0x6553200C}
},
{ASF_OBJ_HEADER, "ASF_OBJ_HEADER",
{0x75B22630, 0x11CF668E, 0xAA00D9A6, 0x6CCE6200}
},
{ASF_OBJ_CONCEAL_NONE, "ASF_OBJ_CONCEAL_NONE",
{0x20fb5700, 0x11cf5b55, 0x8000FDa8, 0x2B445C5f}
},
{ASF_OBJ_COMMENT, "ASF_OBJ_COMMENT",
{0x75b22633, 0x11cf668e, 0xAA00D9a6, 0x6Cce6200}
},
{ASF_OBJ_CODEC_COMMENT, "ASF_OBJ_CODEC_COMMENT",
{0x86D15240, 0x11D0311D, 0xA000A4A3, 0xF64803C9}
},
{ASF_OBJ_CODEC_COMMENT1, "ASF_OBJ_CODEC_COMMENT1",
{0x86d15241, 0x11d0311d, 0xA000A4a3, 0xF64803c9}
},
{ASF_OBJ_SIMPLE_INDEX, "ASF_OBJ_SIMPLE_INDEX",
{0x33000890, 0x11cfe5b1, 0xA000F489, 0xCB4903c9}
},
{ASF_OBJ_INDEX, "ASF_OBJ_INDEX",
{0xd6e229d3, 0x11d135da, 0xa0003490, 0xbe4903c9}
},
{ASF_OBJ_HEAD1, "ASF_OBJ_HEAD1",
{0x5fbf03b5, 0x11cfa92e, 0xC000E38e, 0x6553200c}
},
{ASF_OBJ_HEAD2, "ASF_OBJ_HEAD2",
{0xabd3d211, 0x11cfa9ba, 0xC000E68e, 0x6553200c}
},
{ASF_OBJ_PADDING, "ASF_OBJ_PADDING",
{0x1806D474, 0x4509CADF, 0xAB9ABAA4, 0xE8AA96CB}
},
{ASF_OBJ_BITRATE_PROPS, "ASF_OBJ_BITRATE_PROPS",
{0x7bf875ce, 0x11d1468d, 0x6000828d, 0xb2a2c997}
},
{ASF_OBJ_EXT_CONTENT_DESC, "ASF_OBJ_EXT_CONTENT_DESC",
{0xd2d0a440, 0x11d2e307, 0xa000f097, 0x50a85ec9}
},
{ASF_OBJ_BITRATE_MUTEX, "ASF_OBJ_BITRATE_MUTEX",
{0xd6e229dc, 0x11d135da, 0xa0003490, 0xbe4903c9}
},
{ASF_OBJ_LANGUAGE_LIST, "ASF_OBJ_LANGUAGE_LIST",
{0x7c4346a9, 0x4bfcefe0, 0x3e3929b2, 0x855c41de}
},
{ASF_OBJ_METADATA_OBJECT, "ASF_OBJ_METADATA_OBJECT",
{0xc5f8cbea, 0x48775baf, 0x8caa6784, 0xca4cfa44}
},
{ASF_OBJ_EXTENDED_STREAM_PROPS, "ASF_OBJ_EXTENDED_STREAM_PROPS",
{0x14e6a5cb, 0x4332c672, 0x69a99983, 0x5a5b0652}
},
{ASF_OBJ_COMPATIBILITY, "ASF_OBJ_COMPATIBILITY",
{0x26f18b5d, 0x47ec4584, 0x650e5f9f, 0xc952041f}
},
{ASF_OBJ_INDEX_PLACEHOLDER, "ASF_OBJ_INDEX_PLACEHOLDER",
{0xd9aade20, 0x4f9c7c17, 0x558528bc, 0xa2e298dd}
},
{ASF_OBJ_INDEX_PARAMETERS, "ASF_OBJ_INDEX_PARAMETERS",
{0xd6e229df, 0x11d135da, 0xa0003490, 0xbe4903c9}
},
{ASF_OBJ_ADVANCED_MUTUAL_EXCLUSION, "ASF_OBJ_ADVANCED_MUTUAL_EXCLUSION",
{0xa08649cf, 0x46704775, 0x356e168a, 0xcd667535}
},
{ASF_OBJ_STREAM_PRIORITIZATION, "ASF_OBJ_STREAM_PRIORITIZATION",
{0xd4fed15b, 0x454f88d3, 0x5cedf081, 0x249e9945}
},
{ASF_OBJ_CONTENT_ENCRYPTION, "ASF_OBJ_CONTENT_ENCRYPTION",
{0x2211b3fb, 0x11d2bd23, 0xa000b7b4, 0x6efc55c9}
},
{ASF_OBJ_EXT_CONTENT_ENCRYPTION, "ASF_OBJ_EXT_CONTENT_ENCRYPTION",
{0x298ae614, 0x4c172622, 0xe0da35b9, 0x9c28e97e}
},
{ASF_OBJ_DIGITAL_SIGNATURE_OBJECT, "ASF_OBJ_DIGITAL_SIGNATURE_OBJECT",
{0x2211b3fc, 0x11d2bd23, 0xa000b7b4, 0x6efc55c9}
},
{ASF_OBJ_SCRIPT_COMMAND, "ASF_OBJ_SCRIPT_COMMAND",
{0x1efb1a30, 0x11d00b62, 0xa0009ba3, 0xf64803c9}
},
{ASF_OBJ_MARKER, "ASF_OBJ_MARKER",
{0xf487cd01, 0x11cfa951, 0xc000e68e, 0x6553200c}
},
/* This guid is definitely used for encryption (mentioned in MS smooth
* streaming docs) in new PlayReady (c) (tm) (wtf) system, but I haven't
* found a proper name for it.
* (Edward Jan 11 2011).*/
{ASF_OBJ_UNKNOWN_ENCRYPTION_OBJECT, "ASF_OBJ_UNKNOWN_ENCRYPTION_OBJECT",
{0x9a04f079, 0x42869840, 0x5be692ab, 0x955f88e0}
},
{ASF_OBJ_METADATA_LIBRARY_OBJECT, "ASF_OBJ_METADATA_LIBRARY_OBJECT",
{0x44231c94, 0x49d19498, 0x131d41a1, 0x5470454e}
},
{ASF_OBJ_UNDEFINED, "ASF_OBJ_UNDEFINED",
{0, 0, 0, 0}
}
};
guint32
gst_asf_identify_guid (const ASFGuidHash * guids, ASFGuid * guid)
{
gint i;
for (i = 0; guids[i].obj_id != ASF_OBJ_UNDEFINED; ++i) {
if (guids[i].guid.v1 == guid->v1 &&
guids[i].guid.v2 == guid->v2 &&
guids[i].guid.v3 == guid->v3 && guids[i].guid.v4 == guid->v4) {
return guids[i].obj_id;
}
}
/* The base case if none is found */
return ASF_OBJ_UNDEFINED;
}
const gchar *
gst_asf_get_guid_nick (const ASFGuidHash * guids, guint32 obj_id)
{
gint i;
for (i = 0; guids[i].obj_id != ASF_OBJ_UNDEFINED; ++i) {
if (guids[i].obj_id == obj_id) {
return guids[i].obj_id_str;
}
}
/* The base case if none is found */
return "ASF_OBJ_UNDEFINED";
}

157
gst/asfdemux/asfheaders.h Normal file
View file

@ -0,0 +1,157 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __ASFHEADERS_H__
#define __ASFHEADERS_H__
G_BEGIN_DECLS
typedef struct {
guint32 v1;
guint32 v2;
guint32 v3;
guint32 v4;
} ASFGuid;
typedef struct {
guint8 obj_id;
const gchar *obj_id_str;
ASFGuid guid;
} ASFGuidHash;
typedef enum {
ASF_OBJ_UNDEFINED = 0,
ASF_OBJ_STREAM,
ASF_OBJ_DATA,
ASF_OBJ_FILE,
ASF_OBJ_HEADER,
ASF_OBJ_CONCEAL_NONE,
ASF_OBJ_COMMENT,
ASF_OBJ_CODEC_COMMENT,
ASF_OBJ_CODEC_COMMENT1,
ASF_OBJ_SIMPLE_INDEX,
ASF_OBJ_INDEX,
ASF_OBJ_HEAD1,
ASF_OBJ_HEAD2,
ASF_OBJ_PADDING,
ASF_OBJ_BITRATE_PROPS,
ASF_OBJ_EXT_CONTENT_DESC,
ASF_OBJ_BITRATE_MUTEX,
ASF_OBJ_LANGUAGE_LIST,
ASF_OBJ_METADATA_OBJECT,
ASF_OBJ_EXTENDED_STREAM_PROPS,
ASF_OBJ_COMPATIBILITY,
ASF_OBJ_INDEX_PLACEHOLDER,
ASF_OBJ_INDEX_PARAMETERS,
ASF_OBJ_ADVANCED_MUTUAL_EXCLUSION,
ASF_OBJ_STREAM_PRIORITIZATION,
ASF_OBJ_CONTENT_ENCRYPTION,
ASF_OBJ_EXT_CONTENT_ENCRYPTION,
ASF_OBJ_DIGITAL_SIGNATURE_OBJECT,
ASF_OBJ_SCRIPT_COMMAND,
ASF_OBJ_MARKER,
ASF_OBJ_UNKNOWN_ENCRYPTION_OBJECT,
ASF_OBJ_METADATA_LIBRARY_OBJECT,
} AsfObjectID;
typedef enum {
ASF_STREAM_UNDEFINED = 0,
ASF_STREAM_VIDEO,
ASF_STREAM_AUDIO,
ASF_STREAM_EXT_EMBED_HEADER
} AsfStreamType;
typedef enum {
ASF_EXT_STREAM_UNDEFINED = 0,
ASF_EXT_STREAM_AUDIO
} AsfExtStreamType;
typedef enum {
ASF_CORRECTION_UNDEFINED = 0,
ASF_CORRECTION_ON,
ASF_CORRECTION_OFF
} AsfCorrectionType;
typedef enum {
ASF_PAYLOAD_EXTENSION_UNDEFINED = 0,
ASF_PAYLOAD_EXTENSION_DURATION,
ASF_PAYLOAD_EXTENSION_SYSTEM_CONTENT,
ASF_PAYLOAD_EXTENSION_SYSTEM_PIXEL_ASPECT_RATIO,
ASF_PAYLOAD_EXTENSION_TIMING
} AsfPayloadExtensionID;
extern const ASFGuidHash asf_payload_ext_guids[];
extern const ASFGuidHash asf_correction_guids[];
extern const ASFGuidHash asf_stream_guids[];
extern const ASFGuidHash asf_ext_stream_guids[];
extern const ASFGuidHash asf_object_guids[];
/* GUID utilities */
guint32 gst_asf_identify_guid (const ASFGuidHash * guids,
ASFGuid * guid);
const gchar *gst_asf_get_guid_nick (const ASFGuidHash * guids,
guint32 obj_id);
struct _asf_stream_audio {
guint16 codec_tag;
guint16 channels;
guint32 sample_rate;
guint32 byte_rate;
guint16 block_align;
guint16 word_size;
guint16 size;
};
typedef struct _asf_stream_audio asf_stream_audio;
struct _asf_stream_video {
guint32 width;
guint32 height;
guint8 unknown;
guint16 size;
};
typedef struct _asf_stream_video asf_stream_video;
struct _asf_stream_video_format {
guint32 size;
guint32 width;
guint32 height;
guint16 planes;
guint16 depth;
guint32 tag;
guint32 image_size;
guint32 xpels_meter;
guint32 ypels_meter;
guint32 num_colors;
guint32 imp_colors;
};
typedef struct _asf_stream_video_format asf_stream_video_format;
G_END_DECLS
#endif /* __ASFHEADERS_H__ */

808
gst/asfdemux/asfpacket.c Normal file
View file

@ -0,0 +1,808 @@
/* GStreamer ASF/WMV/WMA demuxer
* Copyright (C) 2007 Tim-Philipp Müller <tim centricular 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/* FIXME:
* file:///home/tpm/samples/video/asf//336370-regis-velo862.wmv
* file:///home/tpm/samples/video/asf//336370-eichhoer.wmv
* throw errors (not always necessarily) in this code path
* (looks like they carry broken payloads/packets though) */
#include "asfpacket.h"
#include <gst/gstutils.h>
#include <gst/gstinfo.h>
#include <string.h>
#define GST_ASF_PAYLOAD_KF_COMPLETE(stream, payload) (stream->is_video && payload->keyframe && payload->buf_filled >= payload->mo_size)
/* we are unlikely to deal with lengths > 2GB here any time soon, so just
* return a signed int and use that for error reporting */
static inline gint
asf_packet_read_varlen_int (guint lentype_flags, guint lentype_bit_offset,
const guint8 ** p_data, guint * p_size)
{
static const guint lens[4] = { 0, 1, 2, 4 };
guint len, val;
len = lens[(lentype_flags >> lentype_bit_offset) & 0x03];
/* will make caller bail out with a short read if there's not enough data */
if (G_UNLIKELY (*p_size < len)) {
GST_WARNING ("need %u bytes, but only %u bytes available", len, *p_size);
return -1;
}
switch (len) {
case 0:
val = 0;
break;
case 1:
val = GST_READ_UINT8 (*p_data);
break;
case 2:
val = GST_READ_UINT16_LE (*p_data);
break;
case 4:
val = GST_READ_UINT32_LE (*p_data);
break;
default:
val = 0;
g_assert_not_reached ();
}
*p_data += len;
*p_size -= len;
return (gint) val;
}
static GstBuffer *
asf_packet_create_payload_buffer (AsfPacket * packet, const guint8 ** p_data,
guint * p_size, guint payload_len)
{
guint off;
g_assert (payload_len <= *p_size);
off = (guint) (*p_data - packet->bdata);
g_assert (off < gst_buffer_get_size (packet->buf));
*p_data += payload_len;
*p_size -= payload_len;
return gst_buffer_copy_region (packet->buf, GST_BUFFER_COPY_ALL, off,
payload_len);
}
static AsfPayload *
asf_payload_search_payloads_queue (AsfPayload * payload, GArray * payload_list)
{
AsfPayload *ret = NULL;
gint idx;
for (idx = payload_list->len - 1; idx >= 0; idx--) {
ret = &g_array_index (payload_list, AsfPayload, idx);
if (G_UNLIKELY (ret->mo_size == payload->mo_size &&
ret->mo_number == payload->mo_number)) {
return ret;
}
}
return NULL;
}
static AsfPayload *
asf_payload_find_previous_fragment (GstASFDemux * demux, AsfPayload * payload,
AsfStream * stream)
{
AsfPayload *ret = NULL;
if (GST_ASF_DEMUX_IS_REVERSE_PLAYBACK (demux->segment)) {
/* Search in queued payloads list */
ret = asf_payload_search_payloads_queue (payload, stream->payloads);
if (ret) {
GST_DEBUG
("previous fragments found in payloads queue for reverse playback : object ID %d",
ret->mo_number);
return ret;
}
/* Search in payloads 'to be queued' list */
ret = asf_payload_search_payloads_queue (payload, stream->payloads_rev);
if (ret) {
GST_DEBUG
("previous fragments found in temp payload queue for reverse playback : object ID %d",
ret->mo_number);
return ret;
}
} else {
if (G_UNLIKELY (stream->payloads->len == 0)) {
GST_DEBUG ("No previous fragments to merge with for stream %u",
stream->id);
return NULL;
}
ret =
&g_array_index (stream->payloads, AsfPayload,
stream->payloads->len - 1);
if (G_UNLIKELY (ret->mo_size != payload->mo_size ||
ret->mo_number != payload->mo_number || ret->mo_offset != 0)) {
if (payload->mo_size != 0) {
GST_WARNING ("Previous fragment does not match continued fragment");
return NULL;
} else {
/* Warn about this case, but accept it anyway: files in the wild sometimes
* have continued packets where the subsequent fragments say that they're
* zero-sized. */
GST_WARNING ("Previous fragment found, but current fragment has "
"zero size, accepting anyway");
}
}
}
#if 0
if (this_fragment->mo_offset + this_payload_len > first_fragment->mo_size) {
GST_WARNING ("Merged fragments would be bigger than the media object");
return FALSE;
}
#endif
return ret;
}
/* TODO: if we have another payload already queued for this stream and that
* payload doesn't have a duration, maybe we can calculate a duration for it
* (if the previous timestamp is smaller etc. etc.) */
static void
gst_asf_payload_queue_for_stream_forward (GstASFDemux * demux,
AsfPayload * payload, AsfStream * stream)
{
GST_DEBUG_OBJECT (demux, "Got payload for stream %d ts:%" GST_TIME_FORMAT,
stream->id, GST_TIME_ARGS (payload->ts));
/* make timestamps start from 0; first_ts will be determined during activation (once we have enough data),
which will also update ts of all packets queued before we knew first_ts; */
if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (demux->first_ts)
&& GST_CLOCK_TIME_IS_VALID (payload->ts))) {
if (payload->ts > demux->first_ts)
payload->ts -= demux->first_ts;
else
payload->ts = 0;
}
/* remove any incomplete payloads that will never be completed */
while (stream->payloads->len > 0) {
AsfPayload *prev;
guint idx_last;
idx_last = stream->payloads->len - 1;
prev = &g_array_index (stream->payloads, AsfPayload, idx_last);
if (G_UNLIKELY (gst_asf_payload_is_complete (prev)))
break;
GST_DEBUG_OBJECT (demux, "Dropping incomplete fragmented media object "
"queued for stream %u", stream->id);
gst_buffer_replace (&prev->buf, NULL);
g_array_remove_index (stream->payloads, idx_last);
/* there's data missing, so there's a discontinuity now */
GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DISCONT);
}
/* If we're about to queue a key frame that is before the segment start, we
* can ditch any previously queued payloads (which would also be before the
* segment start). This makes sure the decoder doesn't decode more than
* absolutely necessary after a seek (we don't push out payloads that are
* before the segment start until we have at least one that falls within the
* segment) */
if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (payload->ts) &&
payload->ts < demux->segment.start && payload->keyframe)) {
GST_DEBUG_OBJECT (demux, "Queueing keyframe before segment start, removing"
" %u previously-queued payloads, which would be out of segment too and"
" hence don't have to be decoded", stream->payloads->len);
while (stream->payloads->len > 0) {
AsfPayload *last;
guint idx_last;
idx_last = stream->payloads->len - 1;
last = &g_array_index (stream->payloads, AsfPayload, idx_last);
gst_buffer_replace (&last->buf, NULL);
g_array_remove_index (stream->payloads, idx_last);
}
/* Mark discontinuity (should be done via stream->discont anyway though) */
GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DISCONT);
}
g_array_append_vals (stream->payloads, payload, 1);
}
static void
gst_asf_payload_queue_for_stream_reverse (GstASFDemux * demux,
AsfPayload * payload, AsfStream * stream)
{
GST_DEBUG_OBJECT (demux, "Got payload for stream %d ts:%" GST_TIME_FORMAT,
stream->id, GST_TIME_ARGS (payload->ts));
if (demux->multiple_payloads) {
/* store the payload in temporary buffer, until we parse all payloads in this packet */
g_array_append_vals (stream->payloads_rev, payload, 1);
} else {
if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (payload->ts))) {
g_array_append_vals (stream->payloads, payload, 1);
if (GST_ASF_PAYLOAD_KF_COMPLETE (stream, payload)) {
stream->kf_pos = stream->payloads->len - 1;
}
} else {
gst_buffer_unref (payload->buf);
}
}
}
static void
gst_asf_payload_queue_for_stream (GstASFDemux * demux, AsfPayload * payload,
AsfStream * stream)
{
GST_DEBUG_OBJECT (demux, "Got payload for stream %d ts:%" GST_TIME_FORMAT,
stream->id, GST_TIME_ARGS (payload->ts));
if (GST_ASF_DEMUX_IS_REVERSE_PLAYBACK (demux->segment)) {
gst_asf_payload_queue_for_stream_reverse (demux, payload, stream);
} else {
gst_asf_payload_queue_for_stream_forward (demux, payload, stream);
}
}
static void
asf_payload_parse_replicated_data_extensions (AsfStream * stream,
AsfPayload * payload)
{
AsfPayloadExtension *ext;
guint off;
guint16 ext_len;
if (!stream->ext_props.valid || stream->ext_props.payload_extensions == NULL)
return;
off = 8;
for (ext = stream->ext_props.payload_extensions; ext->len > 0; ++ext) {
ext_len = ext->len;
if (ext_len == 0xFFFF) { /* extension length is determined by first two bytes in replicated data */
ext_len = GST_READ_UINT16_LE (payload->rep_data + off);
off += 2;
}
if (G_UNLIKELY (off + ext_len > payload->rep_data_len)) {
GST_WARNING ("not enough replicated data for defined extensions");
return;
}
switch (ext->id) {
case ASF_PAYLOAD_EXTENSION_DURATION:
if (G_LIKELY (ext_len == 2)) {
guint16 tdur = GST_READ_UINT16_LE (payload->rep_data + off);
/* packet durations of 1ms are mostly invalid */
if (tdur != 1)
payload->duration = tdur * GST_MSECOND;
} else {
GST_WARNING ("unexpected DURATION extensions len %u", ext_len);
}
break;
case ASF_PAYLOAD_EXTENSION_SYSTEM_CONTENT:
if (G_LIKELY (ext_len == 1)) {
guint8 data = payload->rep_data[off];
payload->interlaced = data & 0x1;
payload->rff = data & 0x8;
payload->tff = (data & 0x2) || !(data & 0x4);
GST_DEBUG ("SYSTEM_CONTENT: interlaced:%d, rff:%d, tff:%d",
payload->interlaced, payload->rff, payload->tff);
} else {
GST_WARNING ("unexpected SYSTEM_CONTE extensions len %u", ext_len);
}
break;
case ASF_PAYLOAD_EXTENSION_SYSTEM_PIXEL_ASPECT_RATIO:
if (G_LIKELY (ext_len == 2)) {
payload->par_x = payload->rep_data[off];
payload->par_y = payload->rep_data[off + 1];
GST_DEBUG ("PAR %d / %d", payload->par_x, payload->par_y);
} else {
GST_WARNING ("unexpected SYSTEM_PIXEL_ASPECT_RATIO extensions len %u",
ext_len);
}
break;
case ASF_PAYLOAD_EXTENSION_TIMING:
{
/* dvr-ms timing - this will override packet timestamp */
guint64 time = GST_READ_UINT64_LE (payload->rep_data + off + 8);
if (time != 0xFFFFFFFFFFFFFFFF)
payload->ts = time * 100;
else
payload->ts = GST_CLOCK_TIME_NONE;
}
break;
default:
GST_LOG ("UNKNOWN PAYLOAD EXTENSION!");
break;
}
off += ext_len;
}
}
static gboolean
gst_asf_demux_parse_payload (GstASFDemux * demux, AsfPacket * packet,
gint lentype, const guint8 ** p_data, guint * p_size)
{
AsfPayload payload = { 0, };
AsfStream *stream;
gboolean is_compressed;
guint payload_len;
guint stream_num;
if (G_UNLIKELY (*p_size < 1)) {
GST_WARNING_OBJECT (demux, "Short packet!");
return FALSE;
}
stream_num = GST_READ_UINT8 (*p_data) & 0x7f;
payload.keyframe = ((GST_READ_UINT8 (*p_data) & 0x80) != 0);
*p_data += 1;
*p_size -= 1;
payload.ts = GST_CLOCK_TIME_NONE;
payload.duration = GST_CLOCK_TIME_NONE;
payload.par_x = 0;
payload.par_y = 0;
payload.interlaced = FALSE;
payload.tff = FALSE;
payload.rff = FALSE;
payload.mo_number =
asf_packet_read_varlen_int (packet->prop_flags, 4, p_data, p_size);
payload.mo_offset =
asf_packet_read_varlen_int (packet->prop_flags, 2, p_data, p_size);
payload.rep_data_len =
asf_packet_read_varlen_int (packet->prop_flags, 0, p_data, p_size);
is_compressed = (payload.rep_data_len == 1);
GST_LOG_OBJECT (demux, "payload for stream %u", stream_num);
GST_LOG_OBJECT (demux, "keyframe : %s", (payload.keyframe) ? "yes" : "no");
GST_LOG_OBJECT (demux, "compressed : %s", (is_compressed) ? "yes" : "no");
if (G_UNLIKELY (*p_size < payload.rep_data_len)) {
GST_WARNING_OBJECT (demux, "Short packet! rep_data_len=%u, size=%u",
payload.rep_data_len, *p_size);
return FALSE;
}
memcpy (payload.rep_data, *p_data,
MIN (sizeof (payload.rep_data), payload.rep_data_len));
*p_data += payload.rep_data_len;
*p_size -= payload.rep_data_len;
if (G_UNLIKELY (*p_size == 0)) {
GST_WARNING_OBJECT (demux, "payload without data!?");
return FALSE;
}
/* we use -1 as lentype for a single payload that's the size of the packet */
if (G_UNLIKELY ((lentype >= 0 && lentype <= 3))) {
payload_len = asf_packet_read_varlen_int (lentype, 0, p_data, p_size);
if (*p_size < payload_len) {
GST_WARNING_OBJECT (demux, "Short packet! payload_len=%u, size=%u",
payload_len, *p_size);
return FALSE;
}
} else {
payload_len = *p_size;
}
GST_LOG_OBJECT (demux, "payload length: %u", payload_len);
stream = gst_asf_demux_get_stream (demux, stream_num);
if (G_UNLIKELY (stream == NULL)) {
if (gst_asf_demux_is_unknown_stream (demux, stream_num)) {
GST_WARNING_OBJECT (demux, "Payload for unknown stream %u, skipping",
stream_num);
}
if (*p_size < payload_len) {
*p_data += *p_size;
*p_size = 0;
} else {
*p_data += payload_len;
*p_size -= payload_len;
}
return TRUE;
}
if (!stream->is_video)
stream->kf_pos = 0;
if (G_UNLIKELY (!is_compressed)) {
GST_LOG_OBJECT (demux, "replicated data length: %u", payload.rep_data_len);
if (payload.rep_data_len >= 8) {
payload.mo_size = GST_READ_UINT32_LE (payload.rep_data);
payload.ts = GST_READ_UINT32_LE (payload.rep_data + 4) * GST_MSECOND;
if (G_UNLIKELY (payload.ts < demux->preroll))
payload.ts = 0;
else
payload.ts -= demux->preroll;
asf_payload_parse_replicated_data_extensions (stream, &payload);
GST_LOG_OBJECT (demux, "media object size : %u", payload.mo_size);
GST_LOG_OBJECT (demux, "media object ts : %" GST_TIME_FORMAT,
GST_TIME_ARGS (payload.ts));
GST_LOG_OBJECT (demux, "media object dur : %" GST_TIME_FORMAT,
GST_TIME_ARGS (payload.duration));
} else if (payload.rep_data_len == 0) {
payload.mo_size = 0;
} else if (payload.rep_data_len != 0) {
GST_WARNING_OBJECT (demux, "invalid replicated data length, very bad");
*p_data += payload_len;
*p_size -= payload_len;
return FALSE;
}
GST_LOG_OBJECT (demux, "media object offset : %u", payload.mo_offset);
GST_LOG_OBJECT (demux, "payload length: %u", payload_len);
if (payload_len == 0) {
GST_DEBUG_OBJECT (demux, "skipping empty payload");
} else if (payload.mo_offset == 0 && payload.mo_size == payload_len) {
/* if the media object is not fragmented, just create a sub-buffer */
GST_LOG_OBJECT (demux, "unfragmented media object size %u", payload_len);
payload.buf = asf_packet_create_payload_buffer (packet, p_data, p_size,
payload_len);
payload.buf_filled = payload_len;
gst_asf_payload_queue_for_stream (demux, &payload, stream);
} else if (GST_ASF_DEMUX_IS_REVERSE_PLAYBACK (demux->segment)) {
/* Handle fragmented payloads for reverse playback */
AsfPayload *prev;
const guint8 *payload_data = *p_data;
prev = asf_payload_find_previous_fragment (demux, &payload, stream);
if (prev) {
gint idx;
AsfPayload *p;
gst_buffer_fill (prev->buf, payload.mo_offset,
payload_data, payload_len);
prev->buf_filled += payload_len;
if (payload.keyframe && payload.mo_offset == 0) {
stream->reverse_kf_ready = TRUE;
for (idx = stream->payloads->len - 1; idx >= 0; idx--) {
p = &g_array_index (stream->payloads, AsfPayload, idx);
if (p->mo_number == payload.mo_number) {
/* Mark position of KF for reverse play */
stream->kf_pos = idx;
}
}
}
} else {
payload.buf = gst_buffer_new_allocate (NULL, payload.mo_size, NULL); /* can we use (mo_size - offset) for size? */
gst_buffer_fill (payload.buf, payload.mo_offset,
payload_data, payload_len);
payload.buf_filled = payload.mo_size - (payload.mo_offset);
gst_asf_payload_queue_for_stream (demux, &payload, stream);
}
*p_data += payload_len;
*p_size -= payload_len;
} else {
const guint8 *payload_data = *p_data;
g_assert (payload_len <= *p_size);
*p_data += payload_len;
*p_size -= payload_len;
/* n-th fragment of a fragmented media object? */
if (payload.mo_offset != 0) {
AsfPayload *prev;
if ((prev =
asf_payload_find_previous_fragment (demux, &payload, stream))) {
if (prev->buf == NULL || (payload.mo_size > 0
&& payload.mo_size != prev->mo_size)
|| payload.mo_offset >= gst_buffer_get_size (prev->buf)
|| payload.mo_offset + payload_len >
gst_buffer_get_size (prev->buf)) {
GST_WARNING_OBJECT (demux, "Offset doesn't match previous data?!");
} else {
/* we assume fragments are payloaded with increasing mo_offset */
if (payload.mo_offset != prev->buf_filled) {
GST_WARNING_OBJECT (demux, "media object payload discontinuity: "
"offset=%u vs buf_filled=%u", payload.mo_offset,
prev->buf_filled);
}
gst_buffer_fill (prev->buf, payload.mo_offset,
payload_data, payload_len);
prev->buf_filled =
MAX (prev->buf_filled, payload.mo_offset + payload_len);
GST_LOG_OBJECT (demux, "Merged media object fragments, size now %u",
prev->buf_filled);
}
} else {
GST_DEBUG_OBJECT (demux, "n-th payload fragment, but don't have "
"any previous fragment, ignoring payload");
}
} else {
GST_LOG_OBJECT (demux, "allocating buffer of size %u for fragmented "
"media object", payload.mo_size);
payload.buf = gst_buffer_new_allocate (NULL, payload.mo_size, NULL);
gst_buffer_fill (payload.buf, 0, payload_data, payload_len);
payload.buf_filled = payload_len;
gst_asf_payload_queue_for_stream (demux, &payload, stream);
}
}
} else {
const guint8 *payload_data;
GstClockTime ts, ts_delta;
guint num;
GST_LOG_OBJECT (demux, "Compressed payload, length=%u", payload_len);
payload_data = *p_data;
*p_data += payload_len;
*p_size -= payload_len;
ts = payload.mo_offset * GST_MSECOND;
if (G_UNLIKELY (ts < demux->preroll))
ts = 0;
else
ts -= demux->preroll;
ts_delta = payload.rep_data[0] * GST_MSECOND;
for (num = 0; payload_len > 0; ++num) {
guint sub_payload_len;
sub_payload_len = GST_READ_UINT8 (payload_data);
GST_LOG_OBJECT (demux, "subpayload #%u: len=%u, ts=%" GST_TIME_FORMAT,
num, sub_payload_len, GST_TIME_ARGS (ts));
++payload_data;
--payload_len;
if (G_UNLIKELY (payload_len < sub_payload_len)) {
GST_WARNING_OBJECT (demux, "Short payload! %u bytes left", payload_len);
return FALSE;
}
if (G_LIKELY (sub_payload_len > 0)) {
payload.buf = asf_packet_create_payload_buffer (packet,
&payload_data, &payload_len, sub_payload_len);
payload.buf_filled = sub_payload_len;
payload.ts = ts;
if (G_LIKELY (ts_delta))
payload.duration = ts_delta;
else
payload.duration = GST_CLOCK_TIME_NONE;
gst_asf_payload_queue_for_stream (demux, &payload, stream);
}
ts += ts_delta;
}
}
return TRUE;
}
GstAsfDemuxParsePacketError
gst_asf_demux_parse_packet (GstASFDemux * demux, GstBuffer * buf)
{
AsfPacket packet = { 0, };
GstMapInfo map;
const guint8 *data;
gboolean has_multiple_payloads;
GstAsfDemuxParsePacketError ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_NONE;
guint8 ec_flags, flags1;
guint size;
gst_buffer_map (buf, &map, GST_MAP_READ);
data = map.data;
size = map.size;
GST_LOG_OBJECT (demux, "Buffer size: %u", size);
/* need at least two payload flag bytes, send time, and duration */
if (G_UNLIKELY (size < 2 + 4 + 2)) {
GST_WARNING_OBJECT (demux, "Packet size is < 8");
ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE;
goto done;
}
packet.buf = buf;
/* evidently transient */
packet.bdata = data;
ec_flags = GST_READ_UINT8 (data);
/* skip optional error correction stuff */
if ((ec_flags & 0x80) != 0) {
guint ec_len_type, ec_len;
ec_len_type = (ec_flags & 0x60) >> 5;
if (ec_len_type == 0) {
ec_len = ec_flags & 0x0f;
} else {
GST_WARNING_OBJECT (demux, "unexpected error correction length type %u",
ec_len_type);
ec_len = 2;
}
GST_LOG_OBJECT (demux, "packet has error correction (%u bytes)", ec_len);
/* still need at least two payload flag bytes, send time, and duration */
if (size <= (1 + ec_len) + 2 + 4 + 2) {
GST_WARNING_OBJECT (demux, "Packet size is < 8 with Error Correction");
ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_FATAL;
goto done;
}
data += 1 + ec_len;
size -= 1 + ec_len;
}
/* parse payload info */
flags1 = GST_READ_UINT8 (data);
packet.prop_flags = GST_READ_UINT8 (data + 1);
data += 2;
size -= 2;
has_multiple_payloads = (flags1 & 0x01) != 0;
packet.length = asf_packet_read_varlen_int (flags1, 5, &data, &size);
packet.sequence = asf_packet_read_varlen_int (flags1, 1, &data, &size);
packet.padding = asf_packet_read_varlen_int (flags1, 3, &data, &size);
if (G_UNLIKELY (size < 6)) {
GST_WARNING_OBJECT (demux, "Packet size is < 6");
ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_FATAL;
goto done;
}
packet.send_time = GST_READ_UINT32_LE (data) * GST_MSECOND;
packet.duration = GST_READ_UINT16_LE (data + 4) * GST_MSECOND;
data += 4 + 2;
size -= 4 + 2;
GST_LOG_OBJECT (demux, "flags : 0x%x", flags1);
GST_LOG_OBJECT (demux, "multiple payloads: %u", has_multiple_payloads);
GST_LOG_OBJECT (demux, "packet length : %u", packet.length);
GST_LOG_OBJECT (demux, "sequence : %u", packet.sequence);
GST_LOG_OBJECT (demux, "padding : %u", packet.padding);
GST_LOG_OBJECT (demux, "send time : %" GST_TIME_FORMAT,
GST_TIME_ARGS (packet.send_time));
GST_LOG_OBJECT (demux, "duration : %" GST_TIME_FORMAT,
GST_TIME_ARGS (packet.duration));
if (GST_ASF_DEMUX_IS_REVERSE_PLAYBACK (demux->segment)
&& demux->seek_to_cur_pos == TRUE) {
/* For reverse playback, initially parse packets forward until we reach packet with 'seek' timestamp */
if (packet.send_time - demux->preroll > demux->segment.stop) {
demux->seek_to_cur_pos = FALSE;
}
ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_NONE;
goto done;
}
if (G_UNLIKELY (packet.padding == (guint) - 1 || size < packet.padding)) {
GST_WARNING_OBJECT (demux, "No padding, or padding bigger than buffer");
ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE;
goto done;
}
size -= packet.padding;
/* adjust available size for parsing if there's less actual packet data for
* parsing than there is data in bytes (for sample see bug 431318) */
if (G_UNLIKELY (packet.length != 0 && packet.padding == 0
&& packet.length < demux->packet_size)) {
GST_LOG_OBJECT (demux, "shortened packet with implicit padding, "
"adjusting available data size");
if (size < demux->packet_size - packet.length) {
/* the buffer is smaller than the implicit padding */
GST_WARNING_OBJECT (demux, "Buffer is smaller than the implicit padding");
ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE;
goto done;
} else {
/* subtract the implicit padding */
size -= (demux->packet_size - packet.length);
}
}
if (has_multiple_payloads) {
guint i, num, lentype;
demux->multiple_payloads = TRUE;
if (G_UNLIKELY (size < 1)) {
GST_WARNING_OBJECT (demux, "No room more in buffer");
ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE;
goto done;
}
num = (GST_READ_UINT8 (data) & 0x3F) >> 0;
lentype = (GST_READ_UINT8 (data) & 0xC0) >> 6;
++data;
--size;
GST_LOG_OBJECT (demux, "num payloads : %u", num);
for (i = 0; i < num; ++i) {
GST_LOG_OBJECT (demux, "Parsing payload %u/%u, size left: %u", i + 1, num,
size);
if (G_UNLIKELY (!gst_asf_demux_parse_payload (demux, &packet, lentype,
&data, &size))) {
GST_WARNING_OBJECT (demux, "Failed to parse payload %u/%u", i + 1, num);
ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_FATAL;
break;
}
}
if (GST_ASF_DEMUX_IS_REVERSE_PLAYBACK (demux->segment)) {
/* In reverse playback, we parsed the packet (with multiple payloads) and stored the payloads in temporary queue.
Now, add them to the stream's payload queue */
for (i = 0; i < demux->num_streams; i++) {
AsfStream *s = &demux->stream[i];
while (s->payloads_rev->len > 0) {
AsfPayload *p;
p = &g_array_index (s->payloads_rev, AsfPayload,
s->payloads_rev->len - 1);
g_array_append_vals (s->payloads, p, 1);
if (GST_ASF_PAYLOAD_KF_COMPLETE (s, p)) {
/* Mark position of KF for reverse play */
s->kf_pos = s->payloads->len - 1;
}
g_array_remove_index (s->payloads_rev, (s->payloads_rev->len - 1));
}
}
}
} else {
GST_LOG_OBJECT (demux, "Parsing single payload");
demux->multiple_payloads = FALSE;
if (G_UNLIKELY (!gst_asf_demux_parse_payload (demux, &packet, -1, &data,
&size))) {
GST_WARNING_OBJECT (demux, "Failed to parse payload");
ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE;
}
}
done:
gst_buffer_unmap (buf, &map);
return ret;
}

74
gst/asfdemux/asfpacket.h Normal file
View file

@ -0,0 +1,74 @@
/* GStreamer ASF/WMV/WMA demuxer
* Copyright (C) 2007 Tim-Philipp Müller <tim centricular 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __ASF_PACKET_H__
#define __ASF_PACKET_H__
#include <gst/gstbuffer.h>
#include <gst/gstclock.h>
#include "gstasfdemux.h"
G_BEGIN_DECLS
typedef struct {
gboolean keyframe; /* buffer flags might not survive merge.. */
guint mo_number; /* media object number (unused) */
guint mo_offset; /* offset (timestamp for compressed data) */
guint mo_size; /* size of media-object-to-be, or 0 */
guint buf_filled; /* how much of the mo data we got so far */
GstBuffer *buf; /* buffer to assemble media-object or NULL*/
guint rep_data_len; /* should never be more than 256, since */
guint8 rep_data[256]; /* the length should be stored in a byte */
GstClockTime ts;
GstClockTime duration; /* is not always available */
guint8 par_x; /* not always available (0:deactivated) */
guint8 par_y; /* not always available (0:deactivated) */
gboolean interlaced; /* default: FALSE */
gboolean tff;
gboolean rff;
} AsfPayload;
typedef struct {
GstBuffer *buf;
const guint8 *bdata;
guint length; /* packet length (unused) */
guint padding; /* length of padding at end of packet */
guint sequence; /* sequence (unused) */
GstClockTime send_time;
GstClockTime duration;
guint8 prop_flags; /* payload length types */
} AsfPacket;
typedef enum {
GST_ASF_DEMUX_PARSE_PACKET_ERROR_NONE,
GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE,
GST_ASF_DEMUX_PARSE_PACKET_ERROR_FATAL
} GstAsfDemuxParsePacketError;
GstAsfDemuxParsePacketError gst_asf_demux_parse_packet (GstASFDemux * demux, GstBuffer * buf);
#define gst_asf_payload_is_complete(payload) \
((payload)->buf_filled >= (payload)->mo_size)
G_END_DECLS
#endif /* __ASF_PACKET_H__ */

52
gst/asfdemux/gstasf.c Normal file
View file

@ -0,0 +1,52 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <gst/riff/riff-read.h>
#include "gst/gst-i18n-plugin.h"
#include "gstasfelements.h"
/* #include "gstasfmux.h" */
static gboolean
plugin_init (GstPlugin * plugin)
{
gboolean ret = FALSE;
ret |= GST_ELEMENT_REGISTER (asfdemux, plugin);
ret |= GST_ELEMENT_REGISTER (rtspwms, plugin);
ret |= GST_ELEMENT_REGISTER (rtpasfdepay, plugin);
/*
if (!gst_element_register (plugin, "asfmux", GST_RANK_NONE, GST_TYPE_ASFMUX))
return FALSE;
*/
return ret;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
asf,
"Demuxes and muxes audio and video in Microsofts ASF format",
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

4921
gst/asfdemux/gstasfdemux.c Normal file

File diff suppressed because it is too large Load diff

256
gst/asfdemux/gstasfdemux.h Normal file
View file

@ -0,0 +1,256 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __ASF_DEMUX_H__
#define __ASF_DEMUX_H__
#include <gst/gst.h>
#include <gst/base/gstadapter.h>
#include <gst/base/gstflowcombiner.h>
#include "asfheaders.h"
G_BEGIN_DECLS
#define GST_TYPE_ASF_DEMUX \
(gst_asf_demux_get_type())
#define GST_ASF_DEMUX(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ASF_DEMUX,GstASFDemux))
#define GST_ASF_DEMUX_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ASF_DEMUX,GstASFDemuxClass))
#define GST_IS_ASF_DEMUX(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ASF_DEMUX))
#define GST_IS_ASF_DEMUX_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ASF_DEMUX))
typedef struct _GstASFDemux GstASFDemux;
typedef struct _GstASFDemuxClass GstASFDemuxClass;
typedef enum _GstASF3DMode GstASF3DMode;
typedef struct {
guint32 packet;
guint16 count;
} AsfSimpleIndexEntry;
typedef struct {
AsfPayloadExtensionID id : 16; /* extension ID; the :16 makes sure the
* struct gets packed into 4 bytes */
guint16 len; /* save this so we can skip unknown IDs */
} AsfPayloadExtension;
/*
* 3D Types for Media play
*/
enum _GstASF3DMode
{
GST_ASF_3D_NONE = 0x00,
//added, interim format - half
GST_ASF_3D_SIDE_BY_SIDE_HALF_LR = 0x01,
GST_ASF_3D_SIDE_BY_SIDE_HALF_RL = 0x02,
GST_ASF_3D_TOP_AND_BOTTOM_HALF_LR = 0x03,
GST_ASF_3D_TOP_AND_BOTTOM_HALF_RL = 0x04,
GST_ASF_3D_DUAL_STREAM = 0x0D, /*< Full format*/
};
typedef struct
{
gboolean valid; /* TRUE if structure is valid/filled */
GstClockTime start_time;
GstClockTime end_time;
GstClockTime avg_time_per_frame;
guint32 data_bitrate;
guint32 buffer_size;
guint32 intial_buf_fullness;
guint32 data_bitrate2;
guint32 buffer_size2;
guint32 intial_buf_fullness2;
guint32 max_obj_size;
guint32 flags;
guint16 lang_idx;
/* may be NULL if there are no extensions; otherwise, terminated by
* an AsfPayloadExtension record with len 0 */
AsfPayloadExtension *payload_extensions;
/* missing: stream names */
} AsfStreamExtProps;
typedef struct
{
AsfStreamType type;
gboolean active; /* if the stream has been activated (pad added) */
GstPad *pad;
guint16 id;
/* video-only */
gboolean is_video;
gboolean fps_known;
GstCaps *caps;
GstBuffer *streamheader;
GstTagList *pending_tags;
gboolean discont;
gboolean first_buffer;
/* Descrambler settings */
guint8 span;
guint16 ds_packet_size;
guint16 ds_chunk_size;
guint16 ds_data_size;
/* for new parsing code */
GArray *payloads; /* pending payloads */
/* Video stream PAR & interlacing */
guint8 par_x;
guint8 par_y;
gboolean interlaced;
/* For reverse playback */
gboolean reverse_kf_ready; /* Found complete KF payload*/
GArray *payloads_rev; /* Temp queue for storing multiple payloads of packet*/
gint kf_pos; /* KF position in payload queue. Payloads from this pos will be pushed */
/* extended stream properties (optional) */
AsfStreamExtProps ext_props;
gboolean inspect_payload;
} AsfStream;
typedef enum {
GST_ASF_DEMUX_STATE_HEADER,
GST_ASF_DEMUX_STATE_DATA,
GST_ASF_DEMUX_STATE_INDEX
} GstASFDemuxState;
#define GST_ASF_DEMUX_IS_REVERSE_PLAYBACK(seg) (seg.rate < 0.0? TRUE:FALSE)
#define GST_ASF_DEMUX_NUM_VIDEO_PADS 16
#define GST_ASF_DEMUX_NUM_AUDIO_PADS 32
#define GST_ASF_DEMUX_NUM_STREAMS 32
#define GST_ASF_DEMUX_NUM_STREAM_IDS 127
struct _GstASFDemux {
GstElement element;
GstPad *sinkpad;
gboolean have_group_id;
guint group_id;
GstAdapter *adapter;
GstTagList *taglist;
GstASFDemuxState state;
/* byte offset where the asf starts, which might not be zero on chained
* asfs, index_offset and data_offset already are 'offseted' by base_offset */
guint64 base_offset;
guint64 index_offset; /* byte offset where index might be, or 0 */
guint64 data_offset; /* byte offset where packets start */
guint64 data_size; /* total size of packet data in bytes, or 0 */
guint64 num_packets; /* total number of data packets, or 0 */
gint64 packet; /* current packet */
guint speed_packets; /* Known number of packets to get in one go*/
gchar **languages;
guint num_languages;
GstCaps *metadata; /* metadata, for delayed parsing; one
* structure ('stream-N') per stream */
GstStructure *global_metadata; /* metadata which isn't specific to one stream */
GSList *ext_stream_props; /* for delayed processing (buffers) */
GSList *mut_ex_streams; /* mutually exclusive streams */
guint32 num_audio_streams;
guint32 num_video_streams;
guint32 num_streams;
AsfStream stream[GST_ASF_DEMUX_NUM_STREAMS];
gboolean activated_streams;
GstFlowCombiner *flowcombiner;
/* for chained asf handling, we need to hold the old asf streams until
* we detect the new ones */
AsfStream old_stream[GST_ASF_DEMUX_NUM_STREAMS];
gboolean old_num_streams;
GstClockTime first_ts; /* smallest timestamp found */
guint32 packet_size;
guint64 play_time;
guint64 preroll;
gboolean seekable;
gboolean broadcast;
GstSegment segment; /* configured play segment */
gboolean keyunit_sync;
gboolean accurate;
gboolean need_newsegment; /* do we need to send a new-segment event? */
guint32 segment_seqnum; /* if the new segment must have this seqnum */
GstClockTime segment_ts; /* streaming; timestamp for segment start */
GstSegment in_segment; /* streaming; upstream segment info */
GstClockTime in_gap; /* streaming; upstream initial segment gap for interpolation */
gboolean segment_running; /* if we've started the current segment */
gboolean streaming; /* TRUE if we are operating chain-based */
GstClockTime latency;
/* for debugging only */
gchar *objpath;
/* simple index, if available */
GstClockTime sidx_interval; /* interval between entries in ns */
guint sidx_num_entries; /* number of index entries */
AsfSimpleIndexEntry *sidx_entries; /* packet number for each entry */
GSList *other_streams; /* remember streams that are in header but have unknown type */
/* For reverse playback */
gboolean seek_to_cur_pos; /* Search packets till we reach 'seek' time */
gboolean multiple_payloads; /* Whether packet has multiple payloads */
/* parsing 3D */
GstASF3DMode asf_3D_mode;
gboolean saw_file_header;
};
struct _GstASFDemuxClass {
GstElementClass parent_class;
};
GType gst_asf_demux_get_type (void);
AsfStream * gst_asf_demux_get_stream (GstASFDemux * demux, guint16 id);
gboolean gst_asf_demux_is_unknown_stream(GstASFDemux *demux, guint stream_num);
G_END_DECLS
#endif /* __ASF_DEMUX_H__ */

View file

@ -0,0 +1,52 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <gst/riff/riff-read.h>
#include "gst/gst-i18n-plugin.h"
#include "gstasfelements.h"
/* #include "gstasfmux.h" */
GST_DEBUG_CATEGORY_EXTERN (asfdemux_dbg);
#define GST_CAT_DEFAULT asfdemux_dbg
void
asf_element_init (GstPlugin * plugin)
{
static gsize res = FALSE;
if (g_once_init_enter (&res)) {
GST_DEBUG_CATEGORY_INIT (asfdemux_dbg, "asfdemux", 0,
"asf demuxer element");
#ifdef ENABLE_NLS
GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
LOCALEDIR);
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
#endif /* ENABLE_NLS */
gst_riff_init ();
g_once_init_leave (&res, TRUE);
}
}

View file

@ -0,0 +1,39 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_ASF_ELEMENTS_H__
#define __GST_ASF_ELEMENTS_H__
#include <gst/gst.h>
#include <gst/video/video.h>
G_BEGIN_DECLS
void asf_element_init (GstPlugin * plugin);
GST_ELEMENT_REGISTER_DECLARE (asfdemux);
GST_ELEMENT_REGISTER_DECLARE (rtspwms);
GST_ELEMENT_REGISTER_DECLARE (rtpasfdepay);
G_END_DECLS
#endif /* __GST_ASF_ELEMENTS_H__ */

View file

@ -0,0 +1,546 @@
/* GStreamer RTP ASF depayloader
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
* 2009 Wim Taymans <wim.taymans@gmail.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "gstasfelements.h"
#include "gstrtpasfdepay.h"
#include <gst/rtp/gstrtpbuffer.h>
#include <string.h>
#include <stdlib.h>
GST_DEBUG_CATEGORY_STATIC (rtpasfdepayload_debug);
#define GST_CAT_DEFAULT rtpasfdepayload_debug
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-ms-asf")
);
/* Other parameters: config, maxps */
#define SINK_CAPS \
"application/x-rtp, " \
"media = (string) { \"application\", \"video\", \"audio\" }, " \
"payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " \
"clock-rate = (int) [1, MAX ], " \
"encoding-name = (string) \"X-ASF-PF\""
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (SINK_CAPS)
);
#define gst_rtp_asf_depay_parent_class parent_class
G_DEFINE_TYPE (GstRtpAsfDepay, gst_rtp_asf_depay, GST_TYPE_RTP_BASE_DEPAYLOAD);
GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (rtpasfdepay, "rtpasfdepay",
GST_RANK_MARGINAL, GST_TYPE_RTP_ASF_DEPAY, asf_element_init (plugin));
static void gst_rtp_asf_depay_finalize (GObject * object);
static GstStateChangeReturn gst_rtp_asf_depay_change_state (GstElement *
element, GstStateChange transition);
static gboolean gst_rtp_asf_depay_setcaps (GstRTPBaseDepayload * depay,
GstCaps * caps);
static GstBuffer *gst_rtp_asf_depay_process (GstRTPBaseDepayload * basedepay,
GstBuffer * buf);
static void
gst_rtp_asf_depay_class_init (GstRtpAsfDepayClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstRTPBaseDepayloadClass *gstrtpbasedepayload_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass;
gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
gst_element_class_add_static_pad_template (gstelement_class, &sink_factory);
gst_element_class_set_static_metadata (gstelement_class,
"RTP ASF packet depayloader", "Codec/Depayloader/Network",
"Extracts ASF streams from RTP",
"Tim-Philipp Müller <tim centricular net>, "
"Wim Taymans <wim.taymans@gmail.com>");
gobject_class->finalize = gst_rtp_asf_depay_finalize;
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_rtp_asf_depay_change_state);
gstrtpbasedepayload_class->set_caps =
GST_DEBUG_FUNCPTR (gst_rtp_asf_depay_setcaps);
gstrtpbasedepayload_class->process =
GST_DEBUG_FUNCPTR (gst_rtp_asf_depay_process);
GST_DEBUG_CATEGORY_INIT (rtpasfdepayload_debug, "rtpasfdepayload", 0,
"RTP asf depayloader element");
}
static void
gst_rtp_asf_depay_init (GstRtpAsfDepay * depay)
{
depay->adapter = gst_adapter_new ();
}
static void
gst_rtp_asf_depay_finalize (GObject * object)
{
GstRtpAsfDepay *depay;
depay = GST_RTP_ASF_DEPAY (object);
g_object_unref (depay->adapter);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static const guint8 asf_marker[16] = { 0x30, 0x26, 0xb2, 0x75, 0x8e, 0x66,
0xcf, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c
};
static gboolean
gst_rtp_asf_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
{
GstRtpAsfDepay *depay;
GstStructure *s;
const gchar *config_str, *ps_string;
GstBuffer *buf;
GstCaps *src_caps;
guint8 *headers;
gsize headers_len;
gint clock_rate;
depay = GST_RTP_ASF_DEPAY (depayload);
s = gst_caps_get_structure (caps, 0);
if (!gst_structure_get_int (s, "clock-rate", &clock_rate) || clock_rate < 0)
clock_rate = 1000;
depayload->clock_rate = clock_rate;
/* config contains the asf headers in base64 coding */
config_str = gst_structure_get_string (s, "config");
if (config_str == NULL || *config_str == '\0')
goto no_config;
ps_string = gst_structure_get_string (s, "maxps");
if (ps_string == NULL || *ps_string == '\0')
goto no_packetsize;
if (depay->packet_size) {
/* header sent again following seek;
* discard to avoid confusing upstream */
if (depay->packet_size == atoi (ps_string)) {
goto duplicate_header;
} else {
/* since we should fiddle with downstream state to handle this */
goto refuse_renegotiation;
}
} else
depay->packet_size = atoi (ps_string);
if (depay->packet_size <= 16)
goto invalid_packetsize;
headers = (guint8 *) g_base64_decode (config_str, &headers_len);
if (headers == NULL || headers_len < 16
|| memcmp (headers, asf_marker, 16) != 0)
goto invalid_headers;
src_caps = gst_caps_new_empty_simple ("video/x-ms-asf");
gst_pad_set_caps (depayload->srcpad, src_caps);
gst_caps_unref (src_caps);
buf = gst_buffer_new ();
gst_buffer_append_memory (buf,
gst_memory_new_wrapped (0, headers, headers_len, 0, headers_len, headers,
g_free));
gst_rtp_base_depayload_push (depayload, buf);
return TRUE;
/* ERRORS */
no_config:
{
GST_WARNING_OBJECT (depay, "caps without 'config' field with asf headers");
return FALSE;
}
no_packetsize:
{
GST_WARNING_OBJECT (depay, "caps without 'maxps' (packet size) field");
return FALSE;
}
invalid_packetsize:
{
GST_WARNING_OBJECT (depay, "packet size %u invalid", depay->packet_size);
return FALSE;
}
invalid_headers:
{
GST_WARNING_OBJECT (depay, "headers don't look like valid ASF headers");
g_free (headers);
return FALSE;
}
duplicate_header:
{
GST_DEBUG_OBJECT (depayload, "discarding duplicate header");
return TRUE;
}
refuse_renegotiation:
{
GST_WARNING_OBJECT (depayload, "cannot renegotiate to different header");
return FALSE;
}
}
static gint
field_size (guint8 field)
{
switch (field) {
/* DWORD - 32 bits */
case 3:
return 4;
/* WORD - 16 bits */
case 2:
return 2;
/* BYTE - 8 bits */
case 1:
return 1;
/* non-exitent */
case 0:
default:
return 0;
}
}
/* Set the padding field to te correct value as the spec
* says it should be se to 0 in the rtp packets
*/
static GstBuffer *
gst_rtp_asf_depay_update_padding (GstRtpAsfDepay * depayload, GstBuffer * buf)
{
GstBuffer *result;
GstMapInfo map;
guint8 *data;
gint offset = 0;
guint8 aux;
guint8 seq_type;
guint8 pad_type;
guint8 pkt_type;
gsize plen, padding;
plen = gst_buffer_get_size (buf);
if (plen == depayload->packet_size)
return buf;
padding = depayload->packet_size - plen;
GST_LOG_OBJECT (depayload,
"padding buffer size %" G_GSIZE_FORMAT " to packet size %d", plen,
depayload->packet_size);
result = gst_buffer_new_and_alloc (depayload->packet_size);
gst_buffer_map (result, &map, GST_MAP_READ);
data = map.data;
memset (data + plen, 0, padding);
gst_buffer_extract (buf, 0, data, plen);
gst_buffer_unref (buf);
aux = data[offset++];
if (aux & 0x80) {
guint8 err_len = 0;
if (aux & 0x60) {
GST_WARNING_OBJECT (depayload, "Error correction length type should be "
"set to 0");
/* this packet doesn't follow the spec */
gst_buffer_unmap (result, &map);
return result;
}
err_len = aux & 0x0F;
offset += err_len;
aux = data[offset++];
}
seq_type = (aux >> 1) & 0x3;
pad_type = (aux >> 3) & 0x3;
pkt_type = (aux >> 5) & 0x3;
offset += 1; /* skip property flags */
offset += field_size (pkt_type); /* skip packet length */
offset += field_size (seq_type); /* skip sequence field */
/* write padding */
switch (pad_type) {
/* DWORD */
case 3:
GST_WRITE_UINT32_LE (&(data[offset]), padding);
break;
/* WORD */
case 2:
GST_WRITE_UINT16_LE (&(data[offset]), padding);
break;
/* BYTE */
case 1:
data[offset] = (guint8) padding;
break;
/* non-existent */
case 0:
default:
break;
}
gst_buffer_unmap (result, &map);
return result;
}
/* Docs: 'RTSP Protocol PDF' document from http://sdp.ppona.com/ (page 8) */
static GstBuffer *
gst_rtp_asf_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf)
{
GstRtpAsfDepay *depay;
const guint8 *payload;
GstBuffer *outbuf;
gboolean S, L, R, D, I;
guint payload_len, hdr_len, offset;
guint len_offs;
GstClockTime timestamp;
GstRTPBuffer rtpbuf = { NULL };
depay = GST_RTP_ASF_DEPAY (depayload);
/* flush remaining data on discont */
if (GST_BUFFER_IS_DISCONT (buf)) {
GST_LOG_OBJECT (depay, "got DISCONT");
gst_adapter_clear (depay->adapter);
depay->discont = TRUE;
}
gst_rtp_buffer_map (buf, GST_MAP_READ, &rtpbuf);
timestamp = GST_BUFFER_TIMESTAMP (buf);
payload_len = gst_rtp_buffer_get_payload_len (&rtpbuf);
payload = gst_rtp_buffer_get_payload (&rtpbuf);
offset = 0;
GST_LOG_OBJECT (depay, "got payload len of %u", payload_len);
do {
guint packet_len;
/* packet header is at least 4 bytes */
if (payload_len < 4)
goto too_small;
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |S|L|R|D|I|RES | Length/Offset |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Relative Timestamp (optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Duration (optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | LocationId (optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* S: packet contains a keyframe.
* L: If 1, Length/Offset contains length, else contains the byte offset
* of the fragment's first byte counted from the beginning of the
* complete ASF data packet.
* R: relative timestamp present
* D: duration present
* I: locationid present
*/
S = ((payload[0] & 0x80) != 0);
L = ((payload[0] & 0x40) != 0);
R = ((payload[0] & 0x20) != 0);
D = ((payload[0] & 0x10) != 0);
I = ((payload[0] & 0x08) != 0);
hdr_len = 4;
len_offs = (payload[1] << 16) | (payload[2] << 8) | payload[3];
if (R) {
GST_DEBUG ("Relative timestamp field present : %u",
GST_READ_UINT32_BE (payload + hdr_len));
hdr_len += 4;
}
if (D) {
GST_DEBUG ("Duration field present : %u",
GST_READ_UINT32_BE (payload + hdr_len));
hdr_len += 4;
}
if (I) {
GST_DEBUG ("LocationId field present : %u",
GST_READ_UINT32_BE (payload + hdr_len));
hdr_len += 4;
}
GST_LOG_OBJECT (depay, "S %d, L %d, R %d, D %d, I %d", S, L, R, D, I);
GST_LOG_OBJECT (depay, "payload_len:%d, hdr_len:%d, len_offs:%d",
payload_len, hdr_len, len_offs);
if (payload_len < hdr_len)
goto too_small;
/* skip headers */
payload_len -= hdr_len;
payload += hdr_len;
offset += hdr_len;
if (L) {
/* L bit set, len contains the length of the packet */
packet_len = len_offs;
} else {
/* else it contains an offset which we don't handle yet */
GST_LOG_OBJECT (depay, "We have a fragmented packet");
packet_len = payload_len;
}
if (packet_len > payload_len)
packet_len = payload_len;
GST_LOG_OBJECT (depay, "packet len %u, payload len %u, packet_size:%u",
packet_len, payload_len, depay->packet_size);
if (!L) {
guint available;
GstBuffer *sub;
/* Fragmented packet handling */
outbuf = NULL;
if (len_offs == (available = gst_adapter_available (depay->adapter))) {
/* fragment aligns with what we have, add it */
GST_LOG_OBJECT (depay, "collecting fragment");
sub =
gst_rtp_buffer_get_payload_subbuffer (&rtpbuf, offset, packet_len);
gst_adapter_push (depay->adapter, sub);
/* RTP marker bit M is set if this is last fragment */
if (gst_rtp_buffer_get_marker (&rtpbuf)) {
GST_LOG_OBJECT (depay, "last fragment, assembling packet");
outbuf =
gst_adapter_take_buffer (depay->adapter, available + packet_len);
}
} else {
if (available) {
GST_WARNING_OBJECT (depay, "Offset doesn't match previous data?!");
GST_DEBUG_OBJECT (depay, "clearing for re-sync");
gst_adapter_clear (depay->adapter);
} else
GST_DEBUG_OBJECT (depay, "waiting for start of packet");
}
} else {
GST_LOG_OBJECT (depay, "collecting packet");
outbuf =
gst_rtp_buffer_get_payload_subbuffer (&rtpbuf, offset, packet_len);
}
/* If we haven't completed a full ASF packet, return */
if (!outbuf)
return NULL;
outbuf = gst_rtp_asf_depay_update_padding (depay, outbuf);
if (!S)
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
if (depay->discont) {
GST_LOG_OBJECT (depay, "setting DISCONT");
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
depay->discont = FALSE;
}
GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
gst_rtp_base_depayload_push (depayload, outbuf);
/* only apply the timestamp to the first buffer of this packet */
timestamp = -1;
/* skip packet data */
payload += packet_len;
offset += packet_len;
payload_len -= packet_len;
} while (payload_len > 0);
gst_rtp_buffer_unmap (&rtpbuf);
return NULL;
/* ERRORS */
too_small:
{
gst_rtp_buffer_unmap (&rtpbuf);
GST_WARNING_OBJECT (depayload, "Payload too small, expected at least 4 "
"bytes for header, but got only %d bytes", payload_len);
return NULL;
}
}
static GstStateChangeReturn
gst_rtp_asf_depay_change_state (GstElement * element, GstStateChange trans)
{
GstStateChangeReturn ret;
GstRtpAsfDepay *depay;
depay = GST_RTP_ASF_DEPAY (element);
switch (trans) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
gst_adapter_clear (depay->adapter);
depay->discont = TRUE;
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, trans);
switch (trans) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
gst_adapter_clear (depay->adapter);
break;
default:
break;
}
return ret;
}

View file

@ -0,0 +1,64 @@
/* GStreamer RTP ASF depayloader
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
* 2009 Wim Taymans <wim.taymans@gmail.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_RTP_ASF_DEPAY_H__
#define __GST_RTP_ASF_DEPAY_H__
#include <gst/gst.h>
#include <gst/base/gstadapter.h>
#include <gst/rtp/gstrtpbasedepayload.h>
G_BEGIN_DECLS
#define GST_TYPE_RTP_ASF_DEPAY \
(gst_rtp_asf_depay_get_type())
#define GST_RTP_ASF_DEPAY(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_ASF_DEPAY,GstRtpAsfDepay))
#define GST_RTP_ASF_DEPAY_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_ASF_DEPAY,GstRtpAsfDepayClass))
#define GST_IS_RTP_ASF_DEPAY(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_ASF_DEPAY))
#define GST_IS_RTP_ASF_DEPAY_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_ASF_DEPAY))
typedef struct _GstRtpAsfDepay GstRtpAsfDepay;
typedef struct _GstRtpAsfDepayClass GstRtpAsfDepayClass;
struct _GstRtpAsfDepay
{
GstRTPBaseDepayload depayload;
guint packet_size;
GstAdapter *adapter;
gboolean discont;
};
struct _GstRtpAsfDepayClass
{
GstRTPBaseDepayloadClass depayload_class;
};
GType gst_rtp_asf_depay_get_type (void);
G_END_DECLS
#endif /* __GST_RTP_ASF_DEPAY_H__ */

240
gst/asfdemux/gstrtspwms.c Normal file
View file

@ -0,0 +1,240 @@
/* GStreamer
* Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/* Element-Checklist-Version: 5 */
/**
* SECTION:element-rtspwms
* @title: rtspwms
*
* A WMS RTSP extension
*/
#include <string.h>
#include <gst/rtsp/gstrtspextension.h>
#include "gstasfelements.h"
#include "gstrtspwms.h"
GST_DEBUG_CATEGORY_STATIC (rtspwms_debug);
#define GST_CAT_DEFAULT (rtspwms_debug)
#define SERVER_PREFIX "WMServer/"
#define HEADER_PREFIX "data:application/vnd.ms.wms-hdr.asfv1;base64,"
#define EXTENSION_CMD "application/x-wms-extension-cmd"
static GstRTSPResult
gst_rtsp_wms_before_send (GstRTSPExtension * ext, GstRTSPMessage * request)
{
GstRTSPWMS *ctx = (GstRTSPWMS *) ext;
GST_DEBUG_OBJECT (ext, "before send");
switch (request->type_data.request.method) {
case GST_RTSP_OPTIONS:
{
/* activate ourselves with the first request */
ctx->active = TRUE;
break;
}
default:
break;
}
return GST_RTSP_OK;
}
static GstRTSPResult
gst_rtsp_wms_after_send (GstRTSPExtension * ext, GstRTSPMessage * req,
GstRTSPMessage * resp)
{
GstRTSPWMS *ctx = (GstRTSPWMS *) ext;
GST_DEBUG_OBJECT (ext, "after send");
switch (req->type_data.request.method) {
case GST_RTSP_OPTIONS:
{
gchar *server = NULL;
gst_rtsp_message_get_header (resp, GST_RTSP_HDR_SERVER, &server, 0);
if (server && g_str_has_prefix (server, SERVER_PREFIX))
ctx->active = TRUE;
else
ctx->active = FALSE;
break;
}
default:
break;
}
return GST_RTSP_OK;
}
static GstRTSPResult
gst_rtsp_wms_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp,
GstStructure * props)
{
const gchar *config, *maxps;
gint i;
GstRTSPWMS *ctx = (GstRTSPWMS *) ext;
if (!ctx->active)
return GST_RTSP_OK;
for (i = 0; (config = gst_sdp_message_get_attribute_val_n (sdp, "pgmpu", i));
i++) {
if (g_str_has_prefix (config, HEADER_PREFIX)) {
config += strlen (HEADER_PREFIX);
gst_structure_set (props, "config", G_TYPE_STRING, config, NULL);
break;
}
}
if (config == NULL)
goto no_config;
gst_structure_set (props, "config", G_TYPE_STRING, config, NULL);
maxps = gst_sdp_message_get_attribute_val (sdp, "maxps");
if (maxps)
gst_structure_set (props, "maxps", G_TYPE_STRING, maxps, NULL);
gst_structure_set (props, "encoding-name", G_TYPE_STRING, "X-ASF-PF", NULL);
gst_structure_set (props, "media", G_TYPE_STRING, "application", NULL);
return GST_RTSP_OK;
/* ERRORS */
no_config:
{
GST_DEBUG_OBJECT (ctx, "Could not find config SDP field, deactivating.");
ctx->active = FALSE;
return GST_RTSP_OK;
}
}
static gboolean
gst_rtsp_wms_configure_stream (GstRTSPExtension * ext, GstCaps * caps)
{
GstRTSPWMS *ctx;
GstStructure *s;
const gchar *encoding;
ctx = (GstRTSPWMS *) ext;
s = gst_caps_get_structure (caps, 0);
encoding = gst_structure_get_string (s, "encoding-name");
if (!encoding)
return TRUE;
GST_DEBUG_OBJECT (ctx, "%" GST_PTR_FORMAT " encoding-name: %s", caps,
encoding);
/* rtx streams do not need to be configured */
if (!strcmp (encoding, "X-WMS-RTX"))
return FALSE;
return TRUE;
}
static GstRTSPResult
gst_rtsp_wms_receive_request (GstRTSPExtension * ext, GstRTSPMessage * request)
{
GstRTSPWMS *ctx;
GstRTSPResult res = GST_RTSP_ENOTIMPL;
GstRTSPMessage response = { 0 };
ctx = (GstRTSPWMS *) ext;
GST_DEBUG_OBJECT (ext, "before send");
switch (request->type_data.request.method) {
case GST_RTSP_SET_PARAMETER:
{
gchar *content_type = NULL;
gst_rtsp_message_get_header (request, GST_RTSP_HDR_CONTENT_TYPE,
&content_type, 0);
if (content_type && !g_ascii_strcasecmp (content_type, EXTENSION_CMD)) {
/* parse the command */
/* default implementation, send OK */
res = gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, "OK",
request);
if (res < 0)
goto send_error;
GST_DEBUG_OBJECT (ctx, "replying with OK");
/* send reply */
if ((res = gst_rtsp_extension_send (ext, request, &response)) < 0)
goto send_error;
res = GST_RTSP_EEOF;
}
break;
}
default:
break;
}
return res;
send_error:
{
return res;
}
}
static void gst_rtsp_wms_extension_init (gpointer g_iface, gpointer iface_data);
G_DEFINE_TYPE_WITH_CODE (GstRTSPWMS, gst_rtsp_wms, GST_TYPE_ELEMENT,
G_IMPLEMENT_INTERFACE (GST_TYPE_RTSP_EXTENSION,
gst_rtsp_wms_extension_init));
GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (rtspwms, "rtspwms", GST_RANK_SECONDARY,
GST_TYPE_RTSP_WMS, asf_element_init (plugin));
static void
gst_rtsp_wms_class_init (GstRTSPWMSClass * g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
GST_DEBUG_CATEGORY_INIT (rtspwms_debug, "rtspwms", 0, "WMS RTSP extension");
gst_element_class_set_static_metadata (element_class, "WMS RTSP Extension",
"Network/Extension/Protocol",
"Extends RTSP so that it can handle WMS setup",
"Wim Taymans <wim.taymans@gmail.com>");
}
static void
gst_rtsp_wms_init (GstRTSPWMS * rtspwms)
{
}
static void
gst_rtsp_wms_extension_init (gpointer g_iface, gpointer iface_data)
{
GstRTSPExtensionInterface *iface = (GstRTSPExtensionInterface *) g_iface;
iface->parse_sdp = gst_rtsp_wms_parse_sdp;
iface->before_send = gst_rtsp_wms_before_send;
iface->after_send = gst_rtsp_wms_after_send;
iface->configure_stream = gst_rtsp_wms_configure_stream;
iface->receive_request = gst_rtsp_wms_receive_request;
}

50
gst/asfdemux/gstrtspwms.h Normal file
View file

@ -0,0 +1,50 @@
/* GStreamer
* Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_RTSP_WMS_H__
#define __GST_RTSP_WMS_H__
#include <gst/gst.h>
G_BEGIN_DECLS
#define GST_TYPE_RTSP_WMS (gst_rtsp_wms_get_type())
#define GST_IS_RTSP_WMS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTSP_WMS))
#define GST_IS_RTSP_WMS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTSP_WMS))
#define GST_RTSP_WMS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTSP_WMS, GstRTSPWMS))
#define GST_RTSP_WMS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTSP_WMS, GstRTSPWMSClass))
typedef struct _GstRTSPWMS GstRTSPWMS;
typedef struct _GstRTSPWMSClass GstRTSPWMSClass;
struct _GstRTSPWMS {
GstElement element;
gboolean active;
};
struct _GstRTSPWMSClass {
GstElementClass parent_class;
};
GType gst_rtsp_wms_get_type(void);
G_END_DECLS
#endif /* __GST_RTSP_WMS_H__ */

22
gst/asfdemux/meson.build Normal file
View file

@ -0,0 +1,22 @@
asf_sources = [
'gstasfdemux.c',
'gstasf.c',
'gstasfelement.c',
'asfheaders.c',
'asfpacket.c',
'gstrtpasfdepay.c',
'gstrtspwms.c',
]
gstasf = library('gstasf',
asf_sources,
c_args : ugly_args,
include_directories : [configinc, libsinc],
dependencies : [gstbase_dep, gstrtp_dep, gstvideo_dep,
gstaudio_dep, gsttag_dep, gstriff_dep,
gstrtsp_dep, gstsdp_dep],
install : true,
install_dir : plugins_install_dir,
)
pkgconfig.generate(gstasf, install_dir : plugins_pkgconfig_install_dir)
plugins += [gstasf]

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,79 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) <2005> Jan Schmidt <jan@noraisin.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_DVDLPCMDEC_H__
#define __GST_DVDLPCMDEC_H__
#include <gst/gst.h>
#include <gst/audio/audio.h>
#include <gst/audio/gstaudiodecoder.h>
G_BEGIN_DECLS
#define GST_TYPE_DVDLPCMDEC \
(gst_dvdlpcmdec_get_type())
#define GST_DVDLPCMDEC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DVDLPCMDEC,GstDvdLpcmDec))
#define GST_DVDLPCMDEC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DVDLPCMDEC,GstDvdLpcmDecClass))
#define GST_IS_DVDLPCMDEC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DVDLPCMDEC))
#define GST_IS_DVDLPCMDEC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DVDLPCMDEC))
typedef struct _GstDvdLpcmDec GstDvdLpcmDec;
typedef struct _GstDvdLpcmDecClass GstDvdLpcmDecClass;
typedef enum {
GST_LPCM_UNKNOWN,
GST_LPCM_RAW,
GST_LPCM_DVD,
GST_LPCM_1394,
GST_LPCM_BLURAY
} GstDvdLpcmMode;
struct _GstDvdLpcmDec {
GstAudioDecoder element;
GstPadChainFunction base_chain;
GstDvdLpcmMode mode;
guint32 header;
GstAudioInfo info;
const GstAudioChannelPosition *lpcm_layout;
gint width;
gint dynamic_range;
gint emphasis;
gint mute;
GstClockTime timestamp;
};
struct _GstDvdLpcmDecClass {
GstAudioDecoderClass parent_class;
};
GType gst_dvdlpcmdec_get_type (void);
GST_ELEMENT_REGISTER_DECLARE (dvdlpcmdec);
G_END_DECLS
#endif /* __GST_DVDLPCMDEC_H__ */

View file

@ -0,0 +1,14 @@
dvdpl_sources = [
'gstdvdlpcmdec.c'
]
gstdvdlpcmdec = library('gstdvdlpcmdec',
dvdpl_sources,
c_args : ugly_args,
include_directories : [configinc],
dependencies : [gstbase_dep, gstaudio_dep],
install : true,
install_dir : plugins_install_dir,
)
pkgconfig.generate(gstdvdlpcmdec, install_dir : plugins_pkgconfig_install_dir)
plugins += [gstdvdlpcmdec]

1164
gst/dvdsub/gstdvdsubdec.c Normal file

File diff suppressed because it is too large Load diff

111
gst/dvdsub/gstdvdsubdec.h Normal file
View file

@ -0,0 +1,111 @@
/* GStreamer
* Copyright (C) <2005> Jan Schmidt <jan@fluendo.com>
* Copyright (C) <2002> Wim Taymans <wim@fluendo.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_DVDSUBDEC_H__
#define __GST_DVDSUBDEC_H__
#include <gst/gst.h>
#include <gst/video/video.h>
G_BEGIN_DECLS
#define GST_TYPE_DVD_SUB_DEC (gst_dvd_sub_dec_get_type())
#define GST_DVD_SUB_DEC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DVD_SUB_DEC,GstDvdSubDec))
#define GST_DVD_SUB_DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DVD_SUB_DEC,GstDvdSubDecClass))
#define GST_IS_DVD_SUB_DEC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DVD_SUB_DEC))
#define GST_IS_DVD_SUB_DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DVD_SUB_DEC))
typedef struct _GstDvdSubDec GstDvdSubDec;
typedef struct _GstDvdSubDecClass GstDvdSubDecClass;
/* Hold premultimplied colour values */
typedef struct Color_val
{
guchar Y_R;
guchar U_G;
guchar V_B;
guchar A;
} Color_val;
struct _GstDvdSubDec
{
GstElement element;
GstPad *sinkpad;
GstPad *srcpad;
gint in_width, in_height;
/* Collect together subtitle buffers until we have a full control sequence */
GstBuffer *partialbuf;
GstMapInfo partialmap;
gboolean have_title;
guchar subtitle_index[4];
guchar menu_index[4];
guchar subtitle_alpha[4];
guchar menu_alpha[4];
guint32 current_clut[16];
Color_val palette_cache_yuv[4];
Color_val hl_palette_cache_yuv[4];
Color_val palette_cache_rgb[4];
Color_val hl_palette_cache_rgb[4];
GstVideoInfo info;
gboolean use_ARGB;
GstClockTime next_ts;
/*
* State info for the current subpicture
* buffer
*/
guchar *parse_pos;
guint16 packet_size;
guint16 data_size;
gint offset[2];
gboolean forced_display;
gboolean visible;
gint left, top, right, bottom;
gint hl_left, hl_top, hl_right, hl_bottom;
gint current_button;
GstClockTime next_event_ts;
gboolean buf_dirty;
};
struct _GstDvdSubDecClass
{
GstElementClass parent_class;
};
GType gst_dvd_sub_dec_get_type (void);
GST_ELEMENT_REGISTER_DECLARE (dvdsubdec);
G_END_DECLS
#endif /* __GST_DVDSUBDEC_H__ */

241
gst/dvdsub/gstdvdsubparse.c Normal file
View file

@ -0,0 +1,241 @@
/* GStreamer DVD subtitle parser
* Copyright (C) 2007 Mark Nauwelaerts <mnauw@users.sourceforge.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <gst/gst.h>
#include "gstdvdsubparse.h"
GST_DEBUG_CATEGORY_STATIC (dvdsubparse_debug);
#define GST_CAT_DEFAULT dvdsubparse_debug
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("subpicture/x-dvd, parsed=(boolean)true")
);
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("subpicture/x-dvd")
);
static void gst_dvd_sub_parse_finalize (GObject * object);
static void gst_dvd_sub_parse_reset (GstDvdSubParse * parse);
static gboolean gst_dvd_sub_parse_event (GstPad * pad, GstObject * parent,
GstEvent * event);
static GstFlowReturn gst_dvd_sub_parse_chain (GstPad * pad, GstObject * parent,
GstBuffer * buf);
static GstStateChangeReturn gst_dvd_sub_parse_change_state (GstElement *
element, GstStateChange transition);
#define gst_dvd_sub_parse_parent_class parent_class
G_DEFINE_TYPE (GstDvdSubParse, gst_dvd_sub_parse, GST_TYPE_ELEMENT);
GST_ELEMENT_REGISTER_DEFINE (dvdsubparse, "dvdsubparse", GST_RANK_NONE,
GST_TYPE_DVD_SUB_PARSE);
static void
gst_dvd_sub_parse_class_init (GstDvdSubParseClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->finalize = gst_dvd_sub_parse_finalize;
GST_DEBUG_CATEGORY_INIT (dvdsubparse_debug, "dvdsubparse", 0,
"DVD subtitle parser");
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_dvd_sub_parse_change_state);
gst_element_class_add_static_pad_template (gstelement_class, &src_template);
gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
gst_element_class_set_static_metadata (gstelement_class,
"DVD subtitle parser", "Codec/Parser/Subtitle",
"Parses and packetizes DVD subtitle streams",
"Mark Nauwelaerts <mnauw@users.sourceforge.net>");
}
static void
gst_dvd_sub_parse_finalize (GObject * object)
{
GstDvdSubParse *parse = GST_DVD_SUB_PARSE (object);
g_object_unref (parse->adapter);
parse->adapter = NULL;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_dvd_sub_parse_init (GstDvdSubParse * parse)
{
parse->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
gst_pad_set_chain_function (parse->sinkpad,
GST_DEBUG_FUNCPTR (gst_dvd_sub_parse_chain));
gst_pad_set_event_function (parse->sinkpad,
GST_DEBUG_FUNCPTR (gst_dvd_sub_parse_event));
gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad);
parse->srcpad = gst_pad_new_from_static_template (&src_template, "src");
gst_pad_use_fixed_caps (parse->srcpad);
gst_pad_set_caps (parse->srcpad,
gst_static_pad_template_get_caps (&src_template));
gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad);
/* remainder */
parse->adapter = gst_adapter_new ();
gst_dvd_sub_parse_reset (parse);
}
static void
gst_dvd_sub_parse_reset (GstDvdSubParse * parse)
{
parse->needed = 0;
parse->stamp = GST_CLOCK_TIME_NONE;
gst_adapter_clear (parse->adapter);
}
static gboolean
gst_dvd_sub_parse_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
GstDvdSubParse *parse;
gboolean ret;
parse = GST_DVD_SUB_PARSE (parent);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CAPS:
{
GstCaps *caps;
gst_event_unref (event);
caps = gst_static_pad_template_get_caps (&src_template);
gst_pad_push_event (parse->srcpad, gst_event_new_caps (caps));
gst_caps_unref (caps);
ret = TRUE;
break;
}
case GST_EVENT_FLUSH_STOP:
gst_dvd_sub_parse_reset (parse);
/* fall-through */
default:
ret = gst_pad_event_default (pad, parent, event);
break;
}
return ret;
}
static GstFlowReturn
gst_dvd_sub_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
GstDvdSubParse *parse = GST_DVD_SUB_PARSE (parent);
GstAdapter *adapter;
GstBuffer *outbuf = NULL;
GstFlowReturn ret = GST_FLOW_OK;
adapter = parse->adapter;
GST_LOG_OBJECT (parse, "%" G_GSIZE_FORMAT " bytes, ts: %" GST_TIME_FORMAT,
gst_buffer_get_size (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
gst_adapter_push (adapter, buf);
if (!parse->needed) {
guint8 data[2];
gst_adapter_copy (adapter, data, 0, 2);
parse->needed = GST_READ_UINT16_BE (data);
}
if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
if (GST_CLOCK_TIME_IS_VALID (parse->stamp))
/* normally, we expect only the first fragment to carry a timestamp */
GST_WARNING_OBJECT (parse, "Received more timestamps than expected.");
else
parse->stamp = GST_BUFFER_TIMESTAMP (buf);
}
if (parse->needed) {
guint av;
av = gst_adapter_available (adapter);
if (av >= parse->needed) {
if (av > parse->needed) {
/* normally, we expect several fragment, boundary aligned */
GST_WARNING_OBJECT (parse, "Unexpected: needed %d, "
"but more (%d) is available.", parse->needed, av);
}
outbuf = gst_adapter_take_buffer (adapter, parse->needed);
/* decorate buffer */
GST_BUFFER_TIMESTAMP (outbuf) = parse->stamp;
/* reset state */
parse->stamp = GST_CLOCK_TIME_NONE;
parse->needed = 0;
/* and send along */
ret = gst_pad_push (parse->srcpad, outbuf);
}
}
return ret;
}
static GstStateChangeReturn
gst_dvd_sub_parse_change_state (GstElement * element, GstStateChange transition)
{
GstDvdSubParse *parse = GST_DVD_SUB_PARSE (element);
GstStateChangeReturn ret;
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
case GST_STATE_CHANGE_READY_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret != GST_STATE_CHANGE_SUCCESS)
return ret;
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
gst_dvd_sub_parse_reset (parse);
break;
default:
break;
}
return GST_STATE_CHANGE_SUCCESS;
}

View file

@ -0,0 +1,66 @@
/* GStreamer DVD subtitle parser
* Copyright (C) 2007 Mark Nauwelaerts <mnauw@users.sourceforge.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_DVDSUBPARSE_H__
#define __GST_DVDSUBPARSE_H__
#include <gst/gst.h>
#include <gst/base/gstadapter.h>
G_BEGIN_DECLS
#define GST_TYPE_DVD_SUB_PARSE \
(gst_dvd_sub_parse_get_type())
#define GST_DVD_SUB_PARSE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_DVD_SUB_PARSE, GstDvdSubParse))
#define GST_DVD_SUB_PARSE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_DVD_SUB_PARSE, GstDvdSubParseClass))
#define GST_DVD_SUB_PARSE_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_DVD_SUB_PARSE, GstDvdSubParseClass))
#define GST_IS_DVD_SUB_PARSE(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_DVD_SUB_PARSE))
#define GST_IS_DVD_SUB_PARSE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_DVD_SUB_PARSE))
typedef struct _GstDvdSubParse GstDvdSubParse;
typedef struct _GstDvdSubParseClass GstDvdSubParseClass;
struct _GstDvdSubParse {
GstElement element;
/*< private >*/
GstPad *srcpad;
GstPad *sinkpad;
GstAdapter *adapter; /* buffer incoming data */
GstClockTime stamp; /* timestamp of current packet */
guint needed; /* size of current packet to be assembled */
};
struct _GstDvdSubParseClass {
GstElementClass parent_class;
};
GType gst_dvd_sub_parse_get_type (void);
GST_ELEMENT_REGISTER_DECLARE (dvdsubparse);
G_END_DECLS
#endif /* __GST_DVDSUBPARSE_H__ */

15
gst/dvdsub/meson.build Normal file
View file

@ -0,0 +1,15 @@
dvdsub_sources = [
'gstdvdsubdec.c',
'gstdvdsubparse.c',
]
gstdvdsub = library('gstdvdsub',
dvdsub_sources,
c_args : ugly_args,
include_directories : [configinc],
dependencies : [gstbase_dep, gstvideo_dep],
install : true,
install_dir : plugins_install_dir,
)
pkgconfig.generate(gstdvdsub, install_dir : plugins_pkgconfig_install_dir)
plugins += [gstdvdsub]

5
gst/meson.build Normal file
View file

@ -0,0 +1,5 @@
foreach plugin : ['asfdemux', 'dvdlpcmdec', 'dvdsub', 'realmedia', 'xingmux']
if not get_option(plugin).disabled()
subdir(plugin)
endif
endforeach

712
gst/realmedia/asmrules.c Normal file
View file

@ -0,0 +1,712 @@
/* GStreamer
* Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <string.h>
#include <stdlib.h>
#include "asmrules.h"
#define MAX_RULE_LENGTH 2048
/* define to enable some more debug */
#undef DEBUG
static GstASMNode *
gst_asm_node_new (void)
{
GstASMNode *node;
node = g_new0 (GstASMNode, 1);
node->type = GST_ASM_NODE_UNKNOWN;
return node;
}
static void
gst_asm_node_free (GstASMNode * node)
{
if (node->left)
gst_asm_node_free (node->left);
if (node->right)
gst_asm_node_free (node->right);
if (node->type == GST_ASM_NODE_VARIABLE && node->data.varname)
g_free (node->data.varname);
g_free (node);
}
static gfloat
gst_asm_operator_eval (GstASMOp optype, gfloat left, gfloat right)
{
gfloat result = 0.0;
switch (optype) {
case GST_ASM_OP_GREATER:
result = (gfloat) (left > right);
break;
case GST_ASM_OP_LESS:
result = (gfloat) (left < right);
break;
case GST_ASM_OP_GREATEREQUAL:
result = (gfloat) (left >= right);
break;
case GST_ASM_OP_LESSEQUAL:
result = (gfloat) (left <= right);
break;
case GST_ASM_OP_EQUAL:
result = (gfloat) (left == right);
break;
case GST_ASM_OP_NOTEQUAL:
result = (gfloat) (left != right);
break;
case GST_ASM_OP_AND:
result = (gfloat) (left && right);
break;
case GST_ASM_OP_OR:
result = (gfloat) (left || right);
break;
default:
break;
}
return result;
}
static gfloat
gst_asm_node_evaluate (GstASMNode * node, GHashTable * vars)
{
gfloat result = 0.0;
if (node == NULL)
return 0.0;
switch (node->type) {
case GST_ASM_NODE_VARIABLE:
{
gchar *val;
val = g_hash_table_lookup (vars, node->data.varname);
if (val)
result = (gfloat) atof (val);
break;
}
case GST_ASM_NODE_INTEGER:
result = (gfloat) node->data.intval;
break;
case GST_ASM_NODE_FLOAT:
result = node->data.floatval;
break;
case GST_ASM_NODE_OPERATOR:
{
gfloat left, right;
left = gst_asm_node_evaluate (node->left, vars);
right = gst_asm_node_evaluate (node->right, vars);
result = gst_asm_operator_eval (node->data.optype, left, right);
break;
}
default:
break;
}
return result;
}
#define IS_SPACE(p) (((p) == ' ') || ((p) == '\n') || \
((p) == '\r') || ((p) == '\t'))
#define IS_RULE_DELIM(p) (((p) == ',') || ((p) == ';') || ((p) == ')'))
#define IS_OPERATOR(p) (((p) == '>') || ((p) == '<') || \
((p) == '=') || ((p) == '!') || \
((p) == '&') || ((p) == '|'))
#define IS_NUMBER(p) ((((p) >= '0') && ((p) <= '9')) || ((p) == '.'))
#define IS_CHAR(p) (!IS_OPERATOR(ch) && !IS_RULE_DELIM(ch) && (ch != '\0'))
#define IS_OP_TOKEN(t) (((t) == GST_ASM_TOKEN_AND) || ((t) == GST_ASM_TOKEN_OR))
#define IS_COND_TOKEN(t) (((t) == GST_ASM_TOKEN_LESS) || ((t) == GST_ASM_TOKEN_LESSEQUAL) || \
((t) == GST_ASM_TOKEN_GREATER) || ((t) == GST_ASM_TOKEN_GREATEREQUAL) || \
((t) == GST_ASM_TOKEN_EQUAL) || ((t) == GST_ASM_TOKEN_NOTEQUAL))
typedef struct
{
const gchar *buffer;
gint pos;
gchar ch;
GstASMToken token;
gchar val[MAX_RULE_LENGTH];
} GstASMScan;
#define NEXT_CHAR(scan) ((scan)->ch = (scan)->buffer[(scan)->pos++])
#define THIS_CHAR(scan) ((scan)->ch)
static GstASMScan *
gst_asm_scan_new (const gchar * buffer)
{
GstASMScan *scan;
scan = g_new0 (GstASMScan, 1);
scan->buffer = buffer;
NEXT_CHAR (scan);
return scan;
}
static void
gst_asm_scan_free (GstASMScan * scan)
{
g_free (scan);
}
static void
gst_asm_scan_string (GstASMScan * scan, gchar delim)
{
gchar ch;
gint i = 0;
ch = THIS_CHAR (scan);
while ((ch != delim) && (ch != '\0')) {
if (i < MAX_RULE_LENGTH - 1)
scan->val[i++] = ch;
ch = NEXT_CHAR (scan);
if (ch == '\\')
ch = NEXT_CHAR (scan);
}
scan->val[i] = '\0';
if (ch == delim)
NEXT_CHAR (scan);
scan->token = GST_ASM_TOKEN_STRING;
}
static void
gst_asm_scan_number (GstASMScan * scan)
{
gchar ch;
gint i = 0;
gboolean have_float = FALSE;
ch = THIS_CHAR (scan);
/* real strips all spaces that are not inside quotes for numbers */
while ((IS_NUMBER (ch) || IS_SPACE (ch))) {
if (i < (MAX_RULE_LENGTH - 1) && !IS_SPACE (ch))
scan->val[i++] = ch;
if (ch == '.')
have_float = TRUE;
ch = NEXT_CHAR (scan);
}
scan->val[i] = '\0';
if (have_float)
scan->token = GST_ASM_TOKEN_FLOAT;
else
scan->token = GST_ASM_TOKEN_INT;
}
static void
gst_asm_scan_identifier (GstASMScan * scan)
{
gchar ch;
gint i = 0;
ch = THIS_CHAR (scan);
/* real strips all spaces that are not inside quotes for identifiers */
while ((IS_CHAR (ch) || IS_SPACE (ch))) {
if (i < (MAX_RULE_LENGTH - 1) && !IS_SPACE (ch))
scan->val[i++] = ch;
ch = NEXT_CHAR (scan);
}
scan->val[i] = '\0';
scan->token = GST_ASM_TOKEN_IDENTIFIER;
}
static void
gst_asm_scan_print_token (GstASMScan * scan)
{
#ifdef DEBUG
switch (scan->token) {
case GST_ASM_TOKEN_NONE:
g_print ("none\n");
break;
case GST_ASM_TOKEN_EOF:
g_print ("EOF\n");
break;
case GST_ASM_TOKEN_INT:
g_print ("INT %d\n", atoi (scan->val));
break;
case GST_ASM_TOKEN_FLOAT:
g_print ("FLOAT %f\n", atof (scan->val));
break;
case GST_ASM_TOKEN_IDENTIFIER:
g_print ("ID %s\n", scan->val);
break;
case GST_ASM_TOKEN_STRING:
g_print ("STRING %s\n", scan->val);
break;
case GST_ASM_TOKEN_HASH:
g_print ("HASH\n");
break;
case GST_ASM_TOKEN_SEMICOLON:
g_print ("SEMICOLON\n");
break;
case GST_ASM_TOKEN_COMMA:
g_print ("COMMA\n");
break;
case GST_ASM_TOKEN_EQUAL:
g_print ("==\n");
break;
case GST_ASM_TOKEN_NOTEQUAL:
g_print ("!=\n");
break;
case GST_ASM_TOKEN_AND:
g_print ("&&\n");
break;
case GST_ASM_TOKEN_OR:
g_print ("||\n");
break;
case GST_ASM_TOKEN_LESS:
g_print ("<\n");
break;
case GST_ASM_TOKEN_LESSEQUAL:
g_print ("<=\n");
break;
case GST_ASM_TOKEN_GREATER:
g_print (">\n");
break;
case GST_ASM_TOKEN_GREATEREQUAL:
g_print (">=\n");
break;
case GST_ASM_TOKEN_DOLLAR:
g_print ("$\n");
break;
case GST_ASM_TOKEN_LPAREN:
g_print ("(\n");
break;
case GST_ASM_TOKEN_RPAREN:
g_print (")\n");
break;
default:
break;
}
#endif
}
static GstASMToken
gst_asm_scan_next_token (GstASMScan * scan)
{
gchar ch;
ch = THIS_CHAR (scan);
/* skip spaces */
while (IS_SPACE (ch))
ch = NEXT_CHAR (scan);
/* remove \ which is common in front of " */
while (ch == '\\')
ch = NEXT_CHAR (scan);
switch (ch) {
case '#':
scan->token = GST_ASM_TOKEN_HASH;
NEXT_CHAR (scan);
break;
case ';':
scan->token = GST_ASM_TOKEN_SEMICOLON;
NEXT_CHAR (scan);
break;
case ',':
scan->token = GST_ASM_TOKEN_COMMA;
NEXT_CHAR (scan);
break;
case '=':
scan->token = GST_ASM_TOKEN_EQUAL;
if (NEXT_CHAR (scan) == '=')
NEXT_CHAR (scan);
break;
case '!':
if (NEXT_CHAR (scan) == '=') {
scan->token = GST_ASM_TOKEN_NOTEQUAL;
NEXT_CHAR (scan);
}
break;
case '&':
scan->token = GST_ASM_TOKEN_AND;
if (NEXT_CHAR (scan) == '&')
NEXT_CHAR (scan);
break;
case '|':
scan->token = GST_ASM_TOKEN_OR;
if (NEXT_CHAR (scan) == '|')
NEXT_CHAR (scan);
break;
case '<':
scan->token = GST_ASM_TOKEN_LESS;
if (NEXT_CHAR (scan) == '=') {
scan->token = GST_ASM_TOKEN_LESSEQUAL;
NEXT_CHAR (scan);
}
break;
case '>':
scan->token = GST_ASM_TOKEN_GREATER;
if (NEXT_CHAR (scan) == '=') {
scan->token = GST_ASM_TOKEN_GREATEREQUAL;
NEXT_CHAR (scan);
}
break;
case '$':
scan->token = GST_ASM_TOKEN_DOLLAR;
NEXT_CHAR (scan);
break;
case '(':
scan->token = GST_ASM_TOKEN_LPAREN;
NEXT_CHAR (scan);
break;
case ')':
scan->token = GST_ASM_TOKEN_RPAREN;
NEXT_CHAR (scan);
break;
case '"':
NEXT_CHAR (scan);
gst_asm_scan_string (scan, '"');
break;
case '\'':
NEXT_CHAR (scan);
gst_asm_scan_string (scan, '\'');
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
gst_asm_scan_number (scan);
break;
case '\0':
scan->token = GST_ASM_TOKEN_EOF;
break;
default:
gst_asm_scan_identifier (scan);
break;
}
gst_asm_scan_print_token (scan);
return scan->token;
}
static GstASMRule *
gst_asm_rule_new (void)
{
GstASMRule *rule;
rule = g_new (GstASMRule, 1);
rule->root = NULL;
rule->props = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
return rule;
}
static void
gst_asm_rule_free (GstASMRule * rule)
{
g_hash_table_destroy (rule->props);
if (rule->root)
gst_asm_node_free (rule->root);
g_free (rule);
}
static void
gst_asm_rule_add_property (GstASMRule * rule, gchar * key, gchar * val)
{
g_hash_table_insert (rule->props, key, val);
}
static GstASMNode *gst_asm_scan_parse_condition (GstASMScan * scan);
static GstASMNode *
gst_asm_scan_parse_operand (GstASMScan * scan)
{
GstASMNode *node;
switch (scan->token) {
case GST_ASM_TOKEN_DOLLAR:
gst_asm_scan_next_token (scan);
if (scan->token != GST_ASM_TOKEN_IDENTIFIER)
g_warning ("identifier expected");
node = gst_asm_node_new ();
node->type = GST_ASM_NODE_VARIABLE;
node->data.varname = g_strdup (scan->val);
break;
case GST_ASM_TOKEN_INT:
node = gst_asm_node_new ();
node->type = GST_ASM_NODE_INTEGER;
node->data.intval = (gfloat) atof (scan->val);
break;
case GST_ASM_TOKEN_FLOAT:
node = gst_asm_node_new ();
node->type = GST_ASM_NODE_FLOAT;
node->data.floatval = atoi (scan->val);
break;
case GST_ASM_TOKEN_LPAREN:
gst_asm_scan_next_token (scan);
node = gst_asm_scan_parse_condition (scan);
if (scan->token != GST_ASM_TOKEN_RPAREN)
g_warning (") expected");
break;
default:
g_warning ("$ <number> or ) expected");
node = NULL;
break;
}
gst_asm_scan_next_token (scan);
return node;
}
static GstASMNode *
gst_asm_scan_parse_expression (GstASMScan * scan)
{
GstASMNode *node, *left;
node = gst_asm_scan_parse_operand (scan);
while (IS_COND_TOKEN (scan->token)) {
left = node;
node = gst_asm_node_new ();
node->type = GST_ASM_NODE_OPERATOR;
node->data.optype = (GstASMOp) scan->token;
gst_asm_scan_next_token (scan);
node->right = gst_asm_scan_parse_operand (scan);
node->left = left;
}
return node;
}
static GstASMNode *
gst_asm_scan_parse_condition (GstASMScan * scan)
{
GstASMNode *node, *left;
node = gst_asm_scan_parse_expression (scan);
while (IS_OP_TOKEN (scan->token)) {
left = node;
node = gst_asm_node_new ();
node->type = GST_ASM_NODE_OPERATOR;
node->data.optype = (GstASMOp) scan->token;
gst_asm_scan_next_token (scan);
node->right = gst_asm_scan_parse_expression (scan);
node->left = left;
}
return node;
}
static void
gst_asm_scan_parse_property (GstASMRule * rule, GstASMScan * scan)
{
gchar *key, *val;
if (scan->token != GST_ASM_TOKEN_IDENTIFIER) {
g_warning ("identifier expected");
return;
}
key = g_strdup (scan->val);
gst_asm_scan_next_token (scan);
if (scan->token != GST_ASM_TOKEN_EQUAL) {
g_warning ("= expected");
g_free (key);
return;
}
gst_asm_scan_next_token (scan);
val = g_strdup (scan->val);
gst_asm_rule_add_property (rule, key, val);
gst_asm_scan_next_token (scan);
}
static GstASMRule *
gst_asm_scan_parse_rule (GstASMScan * scan)
{
GstASMRule *rule;
rule = gst_asm_rule_new ();
if (scan->token == GST_ASM_TOKEN_HASH) {
gst_asm_scan_next_token (scan);
rule->root = gst_asm_scan_parse_condition (scan);
if (scan->token == GST_ASM_TOKEN_COMMA)
gst_asm_scan_next_token (scan);
}
if (scan->token != GST_ASM_TOKEN_SEMICOLON) {
gst_asm_scan_parse_property (rule, scan);
while (scan->token == GST_ASM_TOKEN_COMMA) {
gst_asm_scan_next_token (scan);
gst_asm_scan_parse_property (rule, scan);
}
gst_asm_scan_next_token (scan);
}
return rule;
}
static gboolean
gst_asm_rule_evaluate (GstASMRule * rule, GHashTable * vars)
{
gboolean res;
if (rule->root) {
res = (gboolean) gst_asm_node_evaluate (rule->root, vars);
} else
res = TRUE;
return res;
}
GstASMRuleBook *
gst_asm_rule_book_new (const gchar * rulebook)
{
GstASMRuleBook *book;
GstASMRule *rule = NULL;
GstASMScan *scan;
GstASMToken token;
book = g_new0 (GstASMRuleBook, 1);
book->rulebook = rulebook;
scan = gst_asm_scan_new (book->rulebook);
gst_asm_scan_next_token (scan);
do {
rule = gst_asm_scan_parse_rule (scan);
if (rule) {
book->rules = g_list_append (book->rules, rule);
book->n_rules++;
}
token = scan->token;
} while (token != GST_ASM_TOKEN_EOF);
gst_asm_scan_free (scan);
return book;
}
void
gst_asm_rule_book_free (GstASMRuleBook * book)
{
GList *walk;
for (walk = book->rules; walk; walk = g_list_next (walk)) {
GstASMRule *rule = (GstASMRule *) walk->data;
gst_asm_rule_free (rule);
}
g_list_free (book->rules);
g_free (book);
}
gint
gst_asm_rule_book_match (GstASMRuleBook * book, GHashTable * vars,
gint * rulematches)
{
GList *walk;
gint i, n = 0;
for (walk = book->rules, i = 0; walk; walk = g_list_next (walk), i++) {
GstASMRule *rule = (GstASMRule *) walk->data;
if (gst_asm_rule_evaluate (rule, vars)) {
rulematches[n++] = i;
}
}
return n;
}
#ifdef TEST
gint
main (gint argc, gchar * argv[])
{
GstASMRuleBook *book;
gint rulematch[MAX_RULEMATCHES];
GHashTable *vars;
gint i, n;
static const gchar rules1[] =
"#($Bandwidth < 67959),TimestampDelivery=T,DropByN=T,"
"priority=9;#($Bandwidth >= 67959) && ($Bandwidth < 167959),"
"AverageBandwidth=67959,Priority=9;#($Bandwidth >= 67959) && ($Bandwidth"
" < 167959),AverageBandwidth=0,Priority=5,OnDepend=\\\"1\\\";#($Bandwidth >= 167959)"
" && ($Bandwidth < 267959),AverageBandwidth=167959,Priority=9;#($Bandwidth >= 167959)"
" && ($Bandwidth < 267959),AverageBandwidth=0,Priority=5,OnDepend=\\\"3\\\";"
"#($Bandwidth >= 267959),AverageBandwidth=267959,Priority=9;#($Bandwidth >= 267959)"
",AverageBandwidth=0,Priority=5,OnDepend=\\\"5\\\";";
static const gchar rules2[] =
"AverageBandwidth=32041,Priority=5;AverageBandwidth=0,"
"Priority=5,OnDepend=\\\"0\\\", OffDepend=\\\"0\\\";";
static const gchar rules3[] =
"#(($Bandwidth >= 27500) && ($OldPNMPlayer)),AverageBandwidth=27500,priority=9,PNMKeyframeRule=T;#(($Bandwidth >= 27500) && ($OldPNMPlayer)),AverageBandwidth=0,priority=5,PNMNonKeyframeRule=T;#(($Bandwidth < 27500) && ($OldPNMPlayer)),TimestampDelivery=T,DropByN=T,priority=9,PNMThinningRule=T;#($Bandwidth < 13899),TimestampDelivery=T,DropByN=T,priority=9;#($Bandwidth >= 13899) && ($Bandwidth < 19000),AverageBandwidth=13899,Priority=9;#($Bandwidth >= 13899) && ($Bandwidth < 19000),AverageBandwidth=0,Priority=5,OnDepend=\\\"4\\\";#($Bandwidth >= 19000) && ($Bandwidth < 27500),AverageBandwidth=19000,Priority=9;#($Bandwidth >= 19000) && ($Bandwidth < 27500),AverageBandwidth=0,Priority=5,OnDepend=\\\"6\\\";#($Bandwidth >= 27500) && ($Bandwidth < 132958),AverageBandwidth=27500,Priority=9;#($Bandwidth >= 27500) && ($Bandwidth < 132958),AverageBandwidth=0,Priority=5,OnDepend=\\\"8\\\";#($Bandwidth >= 132958) && ($Bandwidth < 187958),AverageBandwidth=132958,Priority=9;#($Bandwidth >= 132958) && ($Bandwidth < 187958),AverageBandwidth=0,Priority=5,OnDepend=\\\"10\\\";#($Bandwidth >= 187958),AverageBandwidth=187958,Priority=9;#($Bandwidth >= 187958),AverageBandwidth=0,Priority=5,OnDepend=\\\"12\\\";";
vars = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (vars, (gchar *) "Bandwidth", (gchar *) "300000");
book = gst_asm_rule_book_new (rules1);
n = gst_asm_rule_book_match (book, vars, rulematch);
gst_asm_rule_book_free (book);
g_print ("%d rules matched\n", n);
for (i = 0; i < n; i++) {
g_print ("rule %d matched\n", rulematch[i]);
}
book = gst_asm_rule_book_new (rules2);
n = gst_asm_rule_book_match (book, vars, rulematch);
gst_asm_rule_book_free (book);
g_print ("%d rules matched\n", n);
for (i = 0; i < n; i++) {
g_print ("rule %d matched\n", rulematch[i]);
}
book = gst_asm_rule_book_new (rules3);
n = gst_asm_rule_book_match (book, vars, rulematch);
gst_asm_rule_book_free (book);
g_print ("%d rules matched\n", n);
for (i = 0; i < n; i++) {
g_print ("rule %d matched\n", rulematch[i]);
}
g_hash_table_destroy (vars);
return 0;
}
#endif

115
gst/realmedia/asmrules.h Normal file
View file

@ -0,0 +1,115 @@
/* GStreamer
* Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_ASM_RULES_H__
#define __GST_ASM_RULES_H__
#include <gst/gst.h>
G_BEGIN_DECLS
#define MAX_RULEMATCHES 16
typedef struct _GstASMNode GstASMNode;
typedef struct _GstASMRule GstASMRule;
typedef struct _GstASMRuleBook GstASMRuleBook;
typedef enum {
GST_ASM_TOKEN_NONE,
GST_ASM_TOKEN_EOF,
GST_ASM_TOKEN_INT,
GST_ASM_TOKEN_FLOAT,
GST_ASM_TOKEN_IDENTIFIER,
GST_ASM_TOKEN_STRING,
GST_ASM_TOKEN_HASH,
GST_ASM_TOKEN_SEMICOLON,
GST_ASM_TOKEN_COMMA,
GST_ASM_TOKEN_DOLLAR,
GST_ASM_TOKEN_LPAREN,
GST_ASM_TOKEN_RPAREN,
GST_ASM_TOKEN_GREATER,
GST_ASM_TOKEN_LESS,
GST_ASM_TOKEN_GREATEREQUAL,
GST_ASM_TOKEN_LESSEQUAL,
GST_ASM_TOKEN_EQUAL,
GST_ASM_TOKEN_NOTEQUAL,
GST_ASM_TOKEN_AND,
GST_ASM_TOKEN_OR
} GstASMToken;
typedef enum {
GST_ASM_NODE_UNKNOWN,
GST_ASM_NODE_VARIABLE,
GST_ASM_NODE_INTEGER,
GST_ASM_NODE_FLOAT,
GST_ASM_NODE_OPERATOR
} GstASMNodeType;
typedef enum {
GST_ASM_OP_GREATER = GST_ASM_TOKEN_GREATER,
GST_ASM_OP_LESS = GST_ASM_TOKEN_LESS,
GST_ASM_OP_GREATEREQUAL = GST_ASM_TOKEN_GREATEREQUAL,
GST_ASM_OP_LESSEQUAL = GST_ASM_TOKEN_LESSEQUAL,
GST_ASM_OP_EQUAL = GST_ASM_TOKEN_EQUAL,
GST_ASM_OP_NOTEQUAL = GST_ASM_TOKEN_NOTEQUAL,
GST_ASM_OP_AND = GST_ASM_TOKEN_AND,
GST_ASM_OP_OR = GST_ASM_TOKEN_OR
} GstASMOp;
struct _GstASMNode {
GstASMNodeType type;
union {
gchar *varname;
gint intval;
gfloat floatval;
GstASMOp optype;
} data;
GstASMNode *left;
GstASMNode *right;
};
struct _GstASMRule {
GstASMNode *root;
GHashTable *props;
};
struct _GstASMRuleBook {
const gchar *rulebook;
guint n_rules;
GList *rules;
};
G_END_DECLS
GstASMRuleBook* gst_asm_rule_book_new (const gchar *rulebook);
void gst_asm_rule_book_free (GstASMRuleBook *book);
gint gst_asm_rule_book_match (GstASMRuleBook *book, GHashTable *vars,
gint *rulematches);
#endif /* __GST_ASM_RULES_H__ */

View file

@ -0,0 +1,477 @@
/* GStreamer
* Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <string.h>
#include "gstrdtbuffer.h"
gboolean
gst_rdt_buffer_validate_data (guint8 * data, guint len)
{
return TRUE;
}
gboolean
gst_rdt_buffer_validate (GstBuffer * buffer)
{
return TRUE;
}
guint
gst_rdt_buffer_get_packet_count (GstBuffer * buffer)
{
GstRDTPacket packet;
guint count;
g_return_val_if_fail (GST_IS_BUFFER (buffer), 0);
count = 0;
if (gst_rdt_buffer_get_first_packet (buffer, &packet)) {
do {
count++;
} while (gst_rdt_packet_move_to_next (&packet));
}
return count;
}
static gboolean
read_packet_header (GstRDTPacket * packet)
{
GstMapInfo map;
guint8 *data;
gsize size;
guint offset;
guint length;
guint length_offset;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (GST_IS_BUFFER (packet->buffer), FALSE);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
data = map.data;
size = map.size;
offset = packet->offset;
/* check if we are at the end of the buffer, we add 3 because we also want to
* ensure we can read the type, which is always at offset 1 and 2 bytes long. */
if (offset + 3 > size)
goto packet_end;
/* read type */
packet->type = GST_READ_UINT16_BE (&data[offset + 1]);
length = -1;
length_offset = -1;
/* figure out the length of the packet, this depends on the type */
if (GST_RDT_IS_DATA_TYPE (packet->type)) {
if (data[offset] & 0x80)
/* length is present */
length_offset = 3;
} else {
switch (packet->type) {
case GST_RDT_TYPE_ASMACTION:
if (data[offset] & 0x80)
length_offset = 5;
break;
case GST_RDT_TYPE_BWREPORT:
if (data[offset] & 0x80)
length_offset = 3;
break;
case GST_RDT_TYPE_ACK:
if (data[offset] & 0x80)
length_offset = 3;
break;
case GST_RDT_TYPE_RTTREQ:
length = 3;
break;
case GST_RDT_TYPE_RTTRESP:
length = 11;
break;
case GST_RDT_TYPE_CONGESTION:
length = 11;
break;
case GST_RDT_TYPE_STREAMEND:
length = 9;
/* total_reliable */
if (data[offset] & 0x80)
length += 2;
/* stream_id_expansion */
if ((data[offset] & 0x7c) == 0x7c)
length += 2;
/* ext_flag, FIXME, get string length */
if ((data[offset] & 0x1) == 0x1)
length += 7;
break;
case GST_RDT_TYPE_REPORT:
if (data[offset] & 0x80)
length_offset = 3;
break;
case GST_RDT_TYPE_LATENCY:
if (data[offset] & 0x80)
length_offset = 3;
break;
case GST_RDT_TYPE_INFOREQ:
length = 3;
/* request_time_ms */
if (data[offset] & 0x2)
length += 2;
break;
case GST_RDT_TYPE_INFORESP:
length = 3;
/* has_rtt_info */
if (data[offset] & 0x4) {
length += 4;
/* is_delayed */
if (data[offset] & 0x2) {
length += 4;
}
}
if (data[offset] & 0x1) {
/* buffer_info_count, FIXME read and skip */
length += 2;
}
break;
case GST_RDT_TYPE_AUTOBW:
if (data[offset] & 0x80)
length_offset = 3;
break;
case GST_RDT_TYPE_INVALID:
default:
goto unknown_packet;
}
}
if (length != -1) {
/* we have a fixed length */
packet->length = length;
} else if (length_offset != -1) {
/* we can read the length from an offset */
packet->length = GST_READ_UINT16_BE (&data[length_offset]);
} else {
/* length is remainder of packet */
packet->length = size - offset;
}
gst_buffer_unmap (packet->buffer, &map);
/* the length should be smaller than the remaining size */
if (packet->length + offset > size)
goto invalid_length;
return TRUE;
/* ERRORS */
packet_end:
{
gst_buffer_unmap (packet->buffer, &map);
return FALSE;
}
unknown_packet:
{
packet->type = GST_RDT_TYPE_INVALID;
gst_buffer_unmap (packet->buffer, &map);
return FALSE;
}
invalid_length:
{
packet->type = GST_RDT_TYPE_INVALID;
packet->length = 0;
return FALSE;
}
}
gboolean
gst_rdt_buffer_get_first_packet (GstBuffer * buffer, GstRDTPacket * packet)
{
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
g_return_val_if_fail (packet != NULL, FALSE);
/* init to 0 */
packet->buffer = buffer;
packet->offset = 0;
packet->type = GST_RDT_TYPE_INVALID;
memset (&packet->map, 0, sizeof (GstMapInfo));
if (!read_packet_header (packet))
return FALSE;
return TRUE;
}
gboolean
gst_rdt_packet_move_to_next (GstRDTPacket * packet)
{
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID, FALSE);
g_return_val_if_fail (GST_IS_BUFFER (packet->buffer), FALSE);
/* if we have an invalid packet, it must be the last,
* return FALSE */
if (packet->type == GST_RDT_TYPE_INVALID)
goto end;
/* move to next packet */
packet->offset += packet->length;
/* try to read new header */
if (!read_packet_header (packet))
goto end;
return TRUE;
/* ERRORS */
end:
{
packet->type = GST_RDT_TYPE_INVALID;
return FALSE;
}
}
GstRDTType
gst_rdt_packet_get_type (GstRDTPacket * packet)
{
g_return_val_if_fail (packet != NULL, GST_RDT_TYPE_INVALID);
g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID,
GST_RDT_TYPE_INVALID);
return packet->type;
}
guint16
gst_rdt_packet_get_length (GstRDTPacket * packet)
{
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID, 0);
return packet->length;
}
GstBuffer *
gst_rdt_packet_to_buffer (GstRDTPacket * packet)
{
GstBuffer *result;
g_return_val_if_fail (packet != NULL, NULL);
g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID, NULL);
result =
gst_buffer_copy_region (packet->buffer, GST_BUFFER_COPY_ALL,
packet->offset, packet->length);
/* timestamp applies to all packets in this buffer */
GST_BUFFER_TIMESTAMP (result) = GST_BUFFER_TIMESTAMP (packet->buffer);
return result;
}
gint
gst_rdt_buffer_compare_seqnum (guint16 seqnum1, guint16 seqnum2)
{
return (gint16) (seqnum2 - seqnum1);
}
guint16
gst_rdt_packet_data_get_seq (GstRDTPacket * packet)
{
GstMapInfo map;
guint header;
guint16 result;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), FALSE);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
/* skip header bits */
header = packet->offset + 1;
/* read seq_no */
result = GST_READ_UINT16_BE (&map.data[header]);
gst_buffer_unmap (packet->buffer, &map);
return result;
}
guint8 *
gst_rdt_packet_data_map (GstRDTPacket * packet, guint * size)
{
GstMapInfo map;
guint header;
gboolean length_included_flag;
gboolean need_reliable_flag;
guint8 stream_id;
guint8 asm_rule_number;
g_return_val_if_fail (packet != NULL, NULL);
g_return_val_if_fail (packet->map.data == NULL, NULL);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), NULL);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
header = packet->offset;
length_included_flag = (map.data[header] & 0x80) == 0x80;
need_reliable_flag = (map.data[header] & 0x40) == 0x40;
stream_id = (map.data[header] & 0x3e) >> 1;
/* skip seq_no and header bits */
header += 3;
if (length_included_flag) {
/* skip length */
header += 2;
}
asm_rule_number = (map.data[header] & 0x3f);
/* skip timestamp and asm_rule_number */
header += 5;
if (stream_id == 0x1f) {
/* skip stream_id_expansion */
header += 2;
}
if (need_reliable_flag) {
/* skip total_reliable */
header += 2;
}
if (asm_rule_number == 63) {
/* skip asm_rule_number_expansion */
header += 2;
}
if (size)
*size = packet->length - (header - packet->offset);
packet->map = map;
return &map.data[header];
}
gboolean
gst_rdt_packet_data_unmap (GstRDTPacket * packet)
{
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->map.data != NULL, FALSE);
gst_buffer_unmap (packet->buffer, &packet->map);
packet->map.data = NULL;
return TRUE;
}
guint16
gst_rdt_packet_data_get_stream_id (GstRDTPacket * packet)
{
GstMapInfo map;
guint16 result;
guint header;
gboolean length_included_flag;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), 0);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
header = packet->offset;
length_included_flag = (map.data[header] & 0x80) == 0x80;
result = (map.data[header] & 0x3e) >> 1;
if (result == 31) {
/* skip seq_no and header bits */
header += 3;
if (length_included_flag) {
/* skip length */
header += 2;
}
/* skip asm_rule_number and timestamp */
header += 5;
/* stream_id_expansion */
result = GST_READ_UINT16_BE (&map.data[header]);
}
gst_buffer_unmap (packet->buffer, &map);
return result;
}
guint32
gst_rdt_packet_data_get_timestamp (GstRDTPacket * packet)
{
GstMapInfo map;
guint header;
gboolean length_included_flag;
guint32 result;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), 0);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
header = packet->offset;
length_included_flag = (map.data[header] & 0x80) == 0x80;
/* skip seq_no and header bits */
header += 3;
if (length_included_flag) {
/* skip length */
header += 2;
}
/* skip asm_rule_number */
header += 1;
/* get timestamp */
result = GST_READ_UINT32_BE (&map.data[header]);
gst_buffer_unmap (packet->buffer, &map);
return result;
}
guint8
gst_rdt_packet_data_get_flags (GstRDTPacket * packet)
{
GstMapInfo map;
guint8 result;
guint header;
gboolean length_included_flag;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), 0);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
header = packet->offset;
length_included_flag = (map.data[header] & 0x80) == 0x80;
/* skip seq_no and header bits */
header += 3;
if (length_included_flag) {
/* skip length */
header += 2;
}
/* get flags */
result = map.data[header];
gst_buffer_unmap (packet->buffer, &map);
return result;
}

View file

@ -0,0 +1,121 @@
/* GStreamer
* Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com>
*
* gstrdtbuffer.h: various helper functions to manipulate buffers
* with RDT payload.
*
* 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_RDTBUFFER_H__
#define __GST_RDTBUFFER_H__
#include <gst/gst.h>
G_BEGIN_DECLS
/**
* GstRDTType:
* @GST_RDT_TYPE_INVALID:
* @GST_RDT_TYPE_ASMACTION:
* @GST_RDT_TYPE_ACK:
* @GST_RDT_TYPE_RTTREQ:
* @GST_RDT_TYPE_RTTRESP:
* @GST_RDT_TYPE_CONGESTION:
* @GST_RDT_TYPE_STREAMEND:
* @GST_RDT_TYPE_LATENCY:
* @GST_RDT_TYPE_INFOREQ:
* @GST_RDT_TYPE_INFORESP:
* @GST_RDT_TYPE_AUTOBW:
*
* Different RDT packet types.
*/
typedef enum
{
GST_RDT_TYPE_INVALID = 0xffff,
GST_RDT_TYPE_ASMACTION = 0xff00,
GST_RDT_TYPE_BWREPORT = 0xff01,
GST_RDT_TYPE_ACK = 0xff02,
GST_RDT_TYPE_RTTREQ = 0xff03,
GST_RDT_TYPE_RTTRESP = 0xff04,
GST_RDT_TYPE_CONGESTION = 0xff05,
GST_RDT_TYPE_STREAMEND = 0xff06,
GST_RDT_TYPE_REPORT = 0xff07,
GST_RDT_TYPE_LATENCY = 0xff08,
GST_RDT_TYPE_INFOREQ = 0xff09,
GST_RDT_TYPE_INFORESP = 0xff0a,
GST_RDT_TYPE_AUTOBW = 0xff0b
} GstRDTType;
/**
* GST_RDT_IS_DATA_TYPE:
* @t: the #GstRDTType to check
*
* Check if @t is a data packet type.
*/
#define GST_RDT_IS_DATA_TYPE(t) ((t) < 0xff00)
typedef struct _GstRDTPacket GstRDTPacket;
/**
* GstRDTPacket:
* @buffer: pointer to RDT buffer
* @offset: offset of packet in buffer data
*
* Data structure that points to a packet at @offset in @buffer.
* The size of the structure is made public to allow stack allocations.
*/
struct _GstRDTPacket
{
GstBuffer *buffer;
guint offset;
/*< private >*/
GstRDTType type; /* type of current packet */
guint16 length; /* length of current packet in bytes */
GstMapInfo map; /* last mapped data */
};
/* validate buffers */
gboolean gst_rdt_buffer_validate_data (guint8 *data, guint len);
gboolean gst_rdt_buffer_validate (GstBuffer *buffer);
/* retrieving packets */
guint gst_rdt_buffer_get_packet_count (GstBuffer *buffer);
gboolean gst_rdt_buffer_get_first_packet (GstBuffer *buffer, GstRDTPacket *packet);
gboolean gst_rdt_packet_move_to_next (GstRDTPacket *packet);
/* working with packets */
GstRDTType gst_rdt_packet_get_type (GstRDTPacket *packet);
guint16 gst_rdt_packet_get_length (GstRDTPacket *packet);
GstBuffer* gst_rdt_packet_to_buffer (GstRDTPacket *packet);
/* data packets */
guint16 gst_rdt_packet_data_get_seq (GstRDTPacket *packet);
guint8 * gst_rdt_packet_data_map (GstRDTPacket *packet, guint *size);
gboolean gst_rdt_packet_data_unmap (GstRDTPacket *packet);
guint16 gst_rdt_packet_data_get_stream_id (GstRDTPacket *packet);
guint32 gst_rdt_packet_data_get_timestamp (GstRDTPacket *packet);
guint8 gst_rdt_packet_data_get_flags (GstRDTPacket * packet);
/* utils */
gint gst_rdt_buffer_compare_seqnum (guint16 seqnum1, guint16 seqnum2);
G_END_DECLS
#endif /* __GST_RDTBUFFER_H__ */

25
gst/realmedia/meson.build Normal file
View file

@ -0,0 +1,25 @@
real_sources = [
'rademux.c',
'rmdemux.c',
'rmutils.c',
'rdtdepay.c',
'rdtmanager.c',
'rtspreal.c',
'realhash.c',
'asmrules.c',
'rdtjitterbuffer.c',
'gstrdtbuffer.c',
'pnmsrc.c',
'realmedia.c'
]
gstrmdemux = library('gstrealmedia',
real_sources,
c_args : ugly_args,
include_directories : [configinc, libsinc],
dependencies : [gstbase_dep, gstrtsp_dep, gstsdp_dep, gstpbutils_dep],
install : true,
install_dir : plugins_install_dir,
)
pkgconfig.generate(gstrmdemux, install_dir : plugins_pkgconfig_install_dir)
plugins += [gstrmdemux]

234
gst/realmedia/pnmsrc.c Normal file
View file

@ -0,0 +1,234 @@
/* GStreamer
* Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include "pnmsrc.h"
GST_DEBUG_CATEGORY_STATIC (pnmsrc_debug);
#define GST_CAT_DEFAULT pnmsrc_debug
/* PNMSrc signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
#define DEFAULT_LOCATION NULL
enum
{
PROP_0,
PROP_LOCATION
};
static GstStaticPadTemplate gst_pnm_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/vnd.rn-realmedia")
);
static GstFlowReturn gst_pnm_src_create (GstPushSrc * psrc, GstBuffer ** buf);
static void gst_pnm_src_uri_handler_init (gpointer g_iface,
gpointer iface_data);
#define gst_pnm_src_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstPNMSrc, gst_pnm_src, GST_TYPE_PUSH_SRC,
G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_pnm_src_uri_handler_init));
GST_ELEMENT_REGISTER_DEFINE (pnmsrc, "pnmsrc",
GST_RANK_MARGINAL, GST_TYPE_PNM_SRC);
static void gst_pnm_src_finalize (GObject * object);
static void gst_pnm_src_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_pnm_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void
gst_pnm_src_class_init (GstPNMSrcClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstPushSrcClass *gstpushsrc_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstpushsrc_class = (GstPushSrcClass *) klass;
parent_class = g_type_class_peek_parent (klass);
gobject_class->set_property = gst_pnm_src_set_property;
gobject_class->get_property = gst_pnm_src_get_property;
gobject_class->finalize = gst_pnm_src_finalize;
g_object_class_install_property (gobject_class, PROP_LOCATION,
g_param_spec_string ("location", "PNM Location",
"Location of the PNM url to read",
DEFAULT_LOCATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_add_static_pad_template (gstelement_class,
&gst_pnm_src_template);
gst_element_class_set_static_metadata (gstelement_class,
"PNM packet receiver", "Source/Network",
"Receive data over the network via PNM",
"Wim Taymans <wim.taymans@gmail.com>");
gstpushsrc_class->create = gst_pnm_src_create;
GST_DEBUG_CATEGORY_INIT (pnmsrc_debug, "pnmsrc",
0, "Source for the pnm:// uri");
}
static void
gst_pnm_src_init (GstPNMSrc * pnmsrc)
{
pnmsrc->location = g_strdup (DEFAULT_LOCATION);
}
static void
gst_pnm_src_finalize (GObject * object)
{
GstPNMSrc *pnmsrc;
pnmsrc = GST_PNM_SRC (object);
g_free (pnmsrc->location);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_pnm_src_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstPNMSrc *src;
src = GST_PNM_SRC (object);
switch (prop_id) {
case PROP_LOCATION:
g_free (src->location);
src->location = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_pnm_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstPNMSrc *src;
src = GST_PNM_SRC (object);
switch (prop_id) {
case PROP_LOCATION:
g_value_set_string (value, src->location);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static GstFlowReturn
gst_pnm_src_create (GstPushSrc * psrc, GstBuffer ** buf)
{
GstPNMSrc *src;
GstMessage *m;
gchar *url;
src = GST_PNM_SRC (psrc);
if (src->location == NULL)
return GST_FLOW_ERROR;
url = g_strdup_printf ("rtsp%s", &src->location[3]);
/* the only thing we do is redirect to an RTSP url */
m = gst_message_new_element (GST_OBJECT_CAST (src),
gst_structure_new ("redirect", "new-location", G_TYPE_STRING, url, NULL));
g_free (url);
gst_element_post_message (GST_ELEMENT_CAST (src), m);
return GST_FLOW_EOS;
}
/*** GSTURIHANDLER INTERFACE *************************************************/
static GstURIType
gst_pnm_src_uri_get_type (GType type)
{
return GST_URI_SRC;
}
static const gchar *const *
gst_pnm_src_uri_get_protocols (GType type)
{
static const gchar *protocols[] = { "pnm", NULL };
return protocols;
}
static gchar *
gst_pnm_src_uri_get_uri (GstURIHandler * handler)
{
GstPNMSrc *src = GST_PNM_SRC (handler);
/* FIXME: make thread-safe */
return g_strdup (src->location);
}
static gboolean
gst_pnm_src_uri_set_uri (GstURIHandler * handler, const gchar * uri,
GError ** error)
{
GstPNMSrc *src = GST_PNM_SRC (handler);
g_free (src->location);
src->location = g_strdup (uri);
return TRUE;
}
static void
gst_pnm_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
{
GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
iface->get_type = gst_pnm_src_uri_get_type;
iface->get_protocols = gst_pnm_src_uri_get_protocols;
iface->get_uri = gst_pnm_src_uri_get_uri;
iface->set_uri = gst_pnm_src_uri_set_uri;
}

59
gst/realmedia/pnmsrc.h Normal file
View file

@ -0,0 +1,59 @@
/* GStreamer
* Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_PNM_SRC_H__
#define __GST_PNM_SRC_H__
#include <gst/gst.h>
#include <gst/base/gstpushsrc.h>
G_BEGIN_DECLS
#define GST_TYPE_PNM_SRC \
(gst_pnm_src_get_type())
#define GST_PNM_SRC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PNM_SRC,GstPNMSrc))
#define GST_PNM_SRC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PNM_SRC,GstPNMSrcClass))
#define GST_IS_PNM_SRC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PNM_SRC))
#define GST_IS_PNM_SRC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PNM_SRC))
typedef struct _GstPNMSrc GstPNMSrc;
typedef struct _GstPNMSrcClass GstPNMSrcClass;
struct _GstPNMSrc
{
GstPushSrc parent;
gchar *location;
};
struct _GstPNMSrcClass
{
GstPushSrcClass parent_class;
};
GType gst_pnm_src_get_type (void);
GST_ELEMENT_REGISTER_DECLARE (pnmsrc);
G_END_DECLS
#endif /* __GST_PNM_SRC_H__ */

1000
gst/realmedia/rademux.c Normal file

File diff suppressed because it is too large Load diff

104
gst/realmedia/rademux.h Normal file
View file

@ -0,0 +1,104 @@
/* GStreamer RealAudio demuxer
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_REAL_AUDIO_DEMUX_H__
#define __GST_REAL_AUDIO_DEMUX_H__
#include <gst/gst.h>
#include <gst/base/gstadapter.h>
G_BEGIN_DECLS
#define GST_TYPE_REAL_AUDIO_DEMUX \
(gst_real_audio_demux_get_type())
#define GST_REAL_AUDIO_DEMUX(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_REAL_AUDIO_DEMUX,GstRealAudioDemux))
#define GST_REAL_AUDIO_DEMUX_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_REAL_AUDIO_DEMUX,GstRealAudioDemuxClass))
#define GST_IS_REAL_AUDIO_DEMUX(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_REAL_AUDIO_DEMUX))
#define GST_IS_REAL_AUDIO_DEMUX_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_REAL_AUDIO_DEMUX))
typedef enum
{
REAL_AUDIO_DEMUX_STATE_MARKER,
REAL_AUDIO_DEMUX_STATE_HEADER,
REAL_AUDIO_DEMUX_STATE_DATA
} GstRealAudioDemuxState;
typedef struct _GstRealAudioDemux GstRealAudioDemux;
typedef struct _GstRealAudioDemuxClass GstRealAudioDemuxClass;
struct _GstRealAudioDemux {
GstElement element;
GstPad *sinkpad;
GstPad *srcpad;
gboolean have_group_id;
guint group_id;
GstAdapter *adapter;
GstRealAudioDemuxState state;
guint ra_version;
guint data_offset;
guint packet_size;
guint leaf_size;
guint height;
guint flavour;
guint sample_rate;
guint sample_width;
guint channels;
guint32 fourcc;
gboolean segment_running;
gboolean need_newsegment;
GstTagList *pending_tags;
guint byterate_num; /* bytes per second */
guint byterate_denom;
gint64 duration;
gint64 upstream_size;
guint64 offset; /* current read byte offset for
* pull_range-based mode */
/* playback start/stop positions */
GstSegment segment;
gboolean seekable;
};
struct _GstRealAudioDemuxClass {
GstElementClass element_class;
};
GType gst_real_audio_demux_get_type (void);
GST_ELEMENT_REGISTER_DECLARE (rademux);
G_END_DECLS
#endif /* __GST_REAL_AUDIO_DEMUX_H__ */

496
gst/realmedia/rdtdepay.c Normal file
View file

@ -0,0 +1,496 @@
/* GStreamer
* Copyright (C) <2006> Lutz Mueller <lutz at topfrose dot de>
* <2006> Wim Taymans <wim@fluendo.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include "gstrdtbuffer.h"
#include "rdtdepay.h"
GST_DEBUG_CATEGORY_STATIC (rdtdepay_debug);
#define GST_CAT_DEFAULT rdtdepay_debug
/* RDTDepay signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
enum
{
PROP_0,
};
static GstStaticPadTemplate gst_rdt_depay_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/vnd.rn-realmedia")
);
static GstStaticPadTemplate gst_rdt_depay_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-rdt, "
"media = (string) \"application\", "
"clock-rate = (int) [1, MAX ], "
"encoding-name = (string) \"X-REAL-RDT\""
/* All optional parameters
*
* "config="
*/
)
);
#define gst_rdt_depay_parent_class parent_class
G_DEFINE_TYPE (GstRDTDepay, gst_rdt_depay, GST_TYPE_ELEMENT);
GST_ELEMENT_REGISTER_DEFINE (rdtdepay, "rdtdepay",
GST_RANK_MARGINAL, GST_TYPE_RDT_DEPAY);
static void gst_rdt_depay_finalize (GObject * object);
static GstStateChangeReturn gst_rdt_depay_change_state (GstElement *
element, GstStateChange transition);
static gboolean gst_rdt_depay_sink_event (GstPad * pad, GstObject * parent,
GstEvent * event);
static GstFlowReturn gst_rdt_depay_chain (GstPad * pad, GstObject * parent,
GstBuffer * buf);
static void
gst_rdt_depay_class_init (GstRDTDepayClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
parent_class = g_type_class_peek_parent (klass);
gobject_class->finalize = gst_rdt_depay_finalize;
gstelement_class->change_state = gst_rdt_depay_change_state;
gst_element_class_add_static_pad_template (gstelement_class,
&gst_rdt_depay_src_template);
gst_element_class_add_static_pad_template (gstelement_class,
&gst_rdt_depay_sink_template);
gst_element_class_set_static_metadata (gstelement_class, "RDT packet parser",
"Codec/Depayloader/Network",
"Extracts RealMedia from RDT packets",
"Lutz Mueller <lutz at topfrose dot de>, "
"Wim Taymans <wim@fluendo.com>");
GST_DEBUG_CATEGORY_INIT (rdtdepay_debug, "rdtdepay",
0, "Depayloader for RDT RealMedia packets");
}
static void
gst_rdt_depay_init (GstRDTDepay * rdtdepay)
{
rdtdepay->sinkpad =
gst_pad_new_from_static_template (&gst_rdt_depay_sink_template, "sink");
gst_pad_set_chain_function (rdtdepay->sinkpad, gst_rdt_depay_chain);
gst_pad_set_event_function (rdtdepay->sinkpad, gst_rdt_depay_sink_event);
gst_element_add_pad (GST_ELEMENT_CAST (rdtdepay), rdtdepay->sinkpad);
rdtdepay->srcpad =
gst_pad_new_from_static_template (&gst_rdt_depay_src_template, "src");
gst_element_add_pad (GST_ELEMENT_CAST (rdtdepay), rdtdepay->srcpad);
}
static void
gst_rdt_depay_finalize (GObject * object)
{
GstRDTDepay *rdtdepay;
rdtdepay = GST_RDT_DEPAY (object);
if (rdtdepay->header)
gst_buffer_unref (rdtdepay->header);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
gst_rdt_depay_setcaps (GstPad * pad, GstCaps * caps)
{
GstStructure *structure;
GstRDTDepay *rdtdepay;
GstCaps *srccaps;
gint clock_rate = 1000; /* default */
const GValue *value;
GstBuffer *header;
rdtdepay = GST_RDT_DEPAY (GST_PAD_PARENT (pad));
structure = gst_caps_get_structure (caps, 0);
if (gst_structure_has_field (structure, "clock-rate"))
gst_structure_get_int (structure, "clock-rate", &clock_rate);
/* config contains the RealMedia header as a buffer. */
value = gst_structure_get_value (structure, "config");
if (!value)
goto no_header;
header = gst_value_get_buffer (value);
if (!header)
goto no_header;
/* get other values for newsegment */
value = gst_structure_get_value (structure, "npt-start");
if (value && G_VALUE_HOLDS_UINT64 (value))
rdtdepay->npt_start = g_value_get_uint64 (value);
else
rdtdepay->npt_start = 0;
GST_DEBUG_OBJECT (rdtdepay, "NPT start %" G_GUINT64_FORMAT,
rdtdepay->npt_start);
value = gst_structure_get_value (structure, "npt-stop");
if (value && G_VALUE_HOLDS_UINT64 (value))
rdtdepay->npt_stop = g_value_get_uint64 (value);
else
rdtdepay->npt_stop = -1;
GST_DEBUG_OBJECT (rdtdepay, "NPT stop %" G_GUINT64_FORMAT,
rdtdepay->npt_stop);
value = gst_structure_get_value (structure, "play-speed");
if (value && G_VALUE_HOLDS_DOUBLE (value))
rdtdepay->play_speed = g_value_get_double (value);
else
rdtdepay->play_speed = 1.0;
value = gst_structure_get_value (structure, "play-scale");
if (value && G_VALUE_HOLDS_DOUBLE (value))
rdtdepay->play_scale = g_value_get_double (value);
else
rdtdepay->play_scale = 1.0;
/* caps seem good, configure element */
rdtdepay->clock_rate = clock_rate;
/* set caps on pad and on header */
srccaps = gst_caps_new_empty_simple ("application/vnd.rn-realmedia");
gst_pad_set_caps (rdtdepay->srcpad, srccaps);
gst_caps_unref (srccaps);
if (rdtdepay->header)
gst_buffer_unref (rdtdepay->header);
rdtdepay->header = gst_buffer_ref (header);
return TRUE;
/* ERRORS */
no_header:
{
GST_ERROR_OBJECT (rdtdepay, "no header found in caps, no 'config' field");
return FALSE;
}
}
static gboolean
gst_rdt_depay_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
GstRDTDepay *depay;
gboolean res = TRUE;
depay = GST_RDT_DEPAY (parent);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CAPS:
{
GstCaps *caps;
gst_event_parse_caps (event, &caps);
res = gst_rdt_depay_setcaps (pad, caps);
gst_event_unref (event);
break;
}
case GST_EVENT_FLUSH_STOP:
res = gst_pad_push_event (depay->srcpad, event);
gst_segment_init (&depay->segment, GST_FORMAT_UNDEFINED);
depay->need_newsegment = TRUE;
depay->next_seqnum = -1;
break;
case GST_EVENT_SEGMENT:
{
gst_event_copy_segment (event, &depay->segment);
/* don't pass the event downstream, we generate our own segment
* including the NTP time and other things we receive in caps */
gst_event_unref (event);
break;
}
default:
/* pass other events forward */
res = gst_pad_push_event (depay->srcpad, event);
break;
}
return res;
}
static GstEvent *
create_segment_event (GstRDTDepay * depay, gboolean update,
GstClockTime position)
{
GstSegment segment;
gst_segment_init (&segment, GST_FORMAT_TIME);
segment.rate = depay->play_speed;
segment.applied_rate = depay->play_scale;
segment.start = position;
if (depay->npt_stop != -1)
segment.stop = depay->npt_stop - depay->npt_start;
else
segment.stop = -1;
segment.time = position + depay->npt_start;
return gst_event_new_segment (&segment);
}
static GstFlowReturn
gst_rdt_depay_push (GstRDTDepay * rdtdepay, GstBuffer * buffer)
{
GstFlowReturn ret;
if (rdtdepay->need_newsegment) {
GstEvent *event;
event = create_segment_event (rdtdepay, FALSE, 0);
gst_pad_push_event (rdtdepay->srcpad, event);
rdtdepay->need_newsegment = FALSE;
}
if (rdtdepay->discont) {
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
rdtdepay->discont = FALSE;
}
ret = gst_pad_push (rdtdepay->srcpad, buffer);
return ret;
}
static GstFlowReturn
gst_rdt_depay_handle_data (GstRDTDepay * rdtdepay, GstClockTime outtime,
GstRDTPacket * packet)
{
GstFlowReturn ret;
GstBuffer *outbuf;
GstMapInfo outmap;
guint8 *data, *outdata;
guint size;
guint16 stream_id;
guint32 timestamp;
gint gap;
guint16 seqnum;
guint8 flags;
guint16 outflags;
/* get pointers to the packet data */
data = gst_rdt_packet_data_map (packet, &size);
outbuf = gst_buffer_new_and_alloc (12 + size);
GST_BUFFER_TIMESTAMP (outbuf) = outtime;
GST_DEBUG_OBJECT (rdtdepay, "have size %u", size);
/* copy over some things */
stream_id = gst_rdt_packet_data_get_stream_id (packet);
timestamp = gst_rdt_packet_data_get_timestamp (packet);
flags = gst_rdt_packet_data_get_flags (packet);
seqnum = gst_rdt_packet_data_get_seq (packet);
GST_DEBUG_OBJECT (rdtdepay, "stream_id %u, timestamp %u, seqnum %d, flags %d",
stream_id, timestamp, seqnum, flags);
if (rdtdepay->next_seqnum != -1) {
gap = gst_rdt_buffer_compare_seqnum (seqnum, rdtdepay->next_seqnum);
/* if we have no gap, all is fine */
if (G_UNLIKELY (gap != 0)) {
GST_LOG_OBJECT (rdtdepay, "got packet %u, expected %u, gap %d", seqnum,
rdtdepay->next_seqnum, gap);
if (gap < 0) {
/* seqnum > next_seqnum, we are missing some packets, this is always a
* DISCONT. */
GST_LOG_OBJECT (rdtdepay, "%d missing packets", gap);
rdtdepay->discont = TRUE;
} else {
/* seqnum < next_seqnum, we have seen this packet before or the sender
* could be restarted. If the packet is not too old, we throw it away as
* a duplicate, otherwise we mark discont and continue. 100 misordered
* packets is a good threshold. See also RFC 4737. */
if (gap < 100)
goto dropping;
GST_LOG_OBJECT (rdtdepay,
"%d > 100, packet too old, sender likely restarted", gap);
rdtdepay->discont = TRUE;
}
}
}
rdtdepay->next_seqnum = (seqnum + 1);
if (rdtdepay->next_seqnum == 0xff00)
rdtdepay->next_seqnum = 0;
if ((flags & 1) == 0)
outflags = 2;
else
outflags = 0;
gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
outdata = outmap.data;
GST_WRITE_UINT16_BE (outdata + 0, 0); /* version */
GST_WRITE_UINT16_BE (outdata + 2, size + 12); /* length */
GST_WRITE_UINT16_BE (outdata + 4, stream_id); /* stream */
GST_WRITE_UINT32_BE (outdata + 6, timestamp); /* timestamp */
GST_WRITE_UINT16_BE (outdata + 10, outflags); /* flags */
memcpy (outdata + 12, data, size);
gst_buffer_unmap (outbuf, &outmap);
gst_buffer_resize (outbuf, 0, 12 + size);
gst_rdt_packet_data_unmap (packet);
GST_DEBUG_OBJECT (rdtdepay, "Pushing packet, outtime %" GST_TIME_FORMAT,
GST_TIME_ARGS (outtime));
ret = gst_rdt_depay_push (rdtdepay, outbuf);
return ret;
/* ERRORS */
dropping:
{
GST_WARNING_OBJECT (rdtdepay, "%d <= 100, dropping old packet", gap);
return GST_FLOW_OK;
}
}
static GstFlowReturn
gst_rdt_depay_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
GstRDTDepay *rdtdepay;
GstFlowReturn ret;
GstClockTime timestamp;
gboolean more;
GstRDTPacket packet;
rdtdepay = GST_RDT_DEPAY (parent);
if (GST_BUFFER_IS_DISCONT (buf)) {
GST_LOG_OBJECT (rdtdepay, "received discont");
rdtdepay->discont = TRUE;
}
if (rdtdepay->header) {
GstBuffer *out;
out = rdtdepay->header;
rdtdepay->header = NULL;
/* push header data first */
gst_rdt_depay_push (rdtdepay, out);
}
/* save timestamp */
timestamp = GST_BUFFER_TIMESTAMP (buf);
ret = GST_FLOW_OK;
GST_LOG_OBJECT (rdtdepay, "received buffer timestamp %" GST_TIME_FORMAT,
GST_TIME_ARGS (timestamp));
/* data is in RDT format. */
more = gst_rdt_buffer_get_first_packet (buf, &packet);
while (more) {
GstRDTType type;
type = gst_rdt_packet_get_type (&packet);
GST_DEBUG_OBJECT (rdtdepay, "Have packet of type %04x", type);
if (GST_RDT_IS_DATA_TYPE (type)) {
GST_DEBUG_OBJECT (rdtdepay, "We have a data packet");
ret = gst_rdt_depay_handle_data (rdtdepay, timestamp, &packet);
} else {
switch (type) {
default:
GST_DEBUG_OBJECT (rdtdepay, "Ignoring packet");
break;
}
}
if (ret != GST_FLOW_OK)
break;
more = gst_rdt_packet_move_to_next (&packet);
}
gst_buffer_unref (buf);
return ret;
}
static GstStateChangeReturn
gst_rdt_depay_change_state (GstElement * element, GstStateChange transition)
{
GstRDTDepay *rdtdepay;
GstStateChangeReturn ret;
rdtdepay = GST_RDT_DEPAY (element);
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
gst_segment_init (&rdtdepay->segment, GST_FORMAT_UNDEFINED);
rdtdepay->next_seqnum = -1;
rdtdepay->need_newsegment = TRUE;
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
if (rdtdepay->header)
gst_buffer_unref (rdtdepay->header);
rdtdepay->header = NULL;
break;
case GST_STATE_CHANGE_READY_TO_NULL:
break;
default:
break;
}
return ret;
}

74
gst/realmedia/rdtdepay.h Normal file
View file

@ -0,0 +1,74 @@
/* GStreamer
* Copyright (C) <2006> Lutz Mueller <lutz at topfrose dot de>
* <2006> Wim Taymans <wim@fluendo.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_RDT_DEPAY_H__
#define __GST_RDT_DEPAY_H__
#include <gst/gst.h>
G_BEGIN_DECLS
#define GST_TYPE_RDT_DEPAY \
(gst_rdt_depay_get_type())
#define GST_RDT_DEPAY(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RDT_DEPAY,GstRDTDepay))
#define GST_RDT_DEPAY_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RDT_DEPAY,GstRDTDepayClass))
#define GST_IS_RDT_DEPAY(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RDT_DEPAY))
#define GST_IS_RDT_DEPAY_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RDT_DEPAY))
typedef struct _GstRDTDepay GstRDTDepay;
typedef struct _GstRDTDepayClass GstRDTDepayClass;
struct _GstRDTDepay
{
GstElement parent;
GstPad *sinkpad;
GstPad *srcpad;
guint clock_rate;
GstClockTime npt_start;
GstClockTime npt_stop;
gdouble play_speed;
gdouble play_scale;
guint32 next_seqnum;
gboolean discont;
gboolean need_newsegment;
GstSegment segment;
GstBuffer *header;
};
struct _GstRDTDepayClass
{
GstElementClass parent_class;
};
GType gst_rdt_depay_get_type (void);
GST_ELEMENT_REGISTER_DECLARE (rdtdepay);
G_END_DECLS
#endif /* __GST_RDT_DEPAY_H__ */

View file

@ -0,0 +1,531 @@
/* GStreamer
* Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <string.h>
#include <stdlib.h>
#include "rdtjitterbuffer.h"
#include "gstrdtbuffer.h"
GST_DEBUG_CATEGORY_STATIC (rdt_jitter_buffer_debug);
#define GST_CAT_DEFAULT rdt_jitter_buffer_debug
#define MAX_WINDOW RDT_JITTER_BUFFER_MAX_WINDOW
#define MAX_TIME (2 * GST_SECOND)
/* signals and args */
enum
{
LAST_SIGNAL
};
enum
{
PROP_0
};
/* GObject vmethods */
static void rdt_jitter_buffer_finalize (GObject * object);
/* static guint rdt_jitter_buffer_signals[LAST_SIGNAL] = { 0 }; */
G_DEFINE_TYPE (RDTJitterBuffer, rdt_jitter_buffer, G_TYPE_OBJECT);
static void
rdt_jitter_buffer_class_init (RDTJitterBufferClass * klass)
{
GObjectClass *gobject_class;
gobject_class = (GObjectClass *) klass;
gobject_class->finalize = rdt_jitter_buffer_finalize;
GST_DEBUG_CATEGORY_INIT (rdt_jitter_buffer_debug, "rdtjitterbuffer", 0,
"RDT Jitter Buffer");
}
static void
rdt_jitter_buffer_init (RDTJitterBuffer * jbuf)
{
jbuf->packets = g_queue_new ();
rdt_jitter_buffer_reset_skew (jbuf);
}
static void
rdt_jitter_buffer_finalize (GObject * object)
{
RDTJitterBuffer *jbuf;
jbuf = RDT_JITTER_BUFFER_CAST (object);
rdt_jitter_buffer_flush (jbuf);
g_queue_free (jbuf->packets);
G_OBJECT_CLASS (rdt_jitter_buffer_parent_class)->finalize (object);
}
/**
* rdt_jitter_buffer_new:
*
* Create an #RDTJitterBuffer.
*
* Returns: a new #RDTJitterBuffer. Use g_object_unref() after usage.
*/
RDTJitterBuffer *
rdt_jitter_buffer_new (void)
{
RDTJitterBuffer *jbuf;
jbuf = g_object_new (RDT_TYPE_JITTER_BUFFER, NULL);
return jbuf;
}
void
rdt_jitter_buffer_reset_skew (RDTJitterBuffer * jbuf)
{
jbuf->base_time = -1;
jbuf->base_rtptime = -1;
jbuf->ext_rtptime = -1;
jbuf->window_pos = 0;
jbuf->window_filling = TRUE;
jbuf->window_min = 0;
jbuf->skew = 0;
jbuf->prev_send_diff = -1;
}
/* For the clock skew we use a windowed low point averaging algorithm as can be
* found in http://www.grame.fr/pub/TR-050601.pdf. The idea is that the jitter is
* composed of:
*
* J = N + n
*
* N : a constant network delay.
* n : random added noise. The noise is concentrated around 0
*
* In the receiver we can track the elapsed time at the sender with:
*
* send_diff(i) = (Tsi - Ts0);
*
* Tsi : The time at the sender at packet i
* Ts0 : The time at the sender at the first packet
*
* This is the difference between the RDT timestamp in the first received packet
* and the current packet.
*
* At the receiver we have to deal with the jitter introduced by the network.
*
* recv_diff(i) = (Tri - Tr0)
*
* Tri : The time at the receiver at packet i
* Tr0 : The time at the receiver at the first packet
*
* Both of these values contain a jitter Ji, a jitter for packet i, so we can
* write:
*
* recv_diff(i) = (Cri + D + ni) - (Cr0 + D + n0))
*
* Cri : The time of the clock at the receiver for packet i
* D + ni : The jitter when receiving packet i
*
* We see that the network delay is irrelevant here as we can eliminate D:
*
* recv_diff(i) = (Cri + ni) - (Cr0 + n0))
*
* The drift is now expressed as:
*
* Drift(i) = recv_diff(i) - send_diff(i);
*
* We now keep the W latest values of Drift and find the minimum (this is the
* one with the lowest network jitter and thus the one which is least affected
* by it). We average this lowest value to smooth out the resulting network skew.
*
* Both the window and the weighting used for averaging influence the accuracy
* of the drift estimation. Finding the correct parameters turns out to be a
* compromise between accuracy and inertia.
*
* We use a 2 second window or up to 512 data points, which is statistically big
* enough to catch spikes (FIXME, detect spikes).
* We also use a rather large weighting factor (125) to smoothly adapt. During
* startup, when filling the window, we use a parabolic weighting factor, the
* more the window is filled, the faster we move to the detected possible skew.
*
* Returns: @time adjusted with the clock skew.
*/
static GstClockTime
calculate_skew (RDTJitterBuffer * jbuf, guint32 rtptime, GstClockTime time,
guint32 clock_rate)
{
guint64 ext_rtptime;
guint64 send_diff, recv_diff;
gint64 delta;
gint64 old;
gint pos, i;
GstClockTime gstrtptime, out_time;
//ext_rtptime = gst_rtp_buffer_ext_timestamp (&jbuf->ext_rtptime, rtptime);
ext_rtptime = rtptime;
gstrtptime = gst_util_uint64_scale_int (ext_rtptime, GST_SECOND, clock_rate);
again:
/* first time, lock on to time and gstrtptime */
if (jbuf->base_time == -1)
jbuf->base_time = time;
if (jbuf->base_rtptime == -1)
jbuf->base_rtptime = gstrtptime;
if (gstrtptime >= jbuf->base_rtptime)
send_diff = gstrtptime - jbuf->base_rtptime;
else {
/* elapsed time at sender, timestamps can go backwards and thus be smaller
* than our base time, take a new base time in that case. */
GST_DEBUG ("backward timestamps at server, taking new base time");
jbuf->base_rtptime = gstrtptime;
jbuf->base_time = time;
send_diff = 0;
}
GST_DEBUG ("extrtp %" G_GUINT64_FORMAT ", gstrtp %" GST_TIME_FORMAT ", base %"
GST_TIME_FORMAT ", send_diff %" GST_TIME_FORMAT, ext_rtptime,
GST_TIME_ARGS (gstrtptime), GST_TIME_ARGS (jbuf->base_rtptime),
GST_TIME_ARGS (send_diff));
if (jbuf->prev_send_diff != -1 && time != -1) {
gint64 delta_diff;
if (send_diff > jbuf->prev_send_diff)
delta_diff = send_diff - jbuf->prev_send_diff;
else
delta_diff = jbuf->prev_send_diff - send_diff;
/* server changed rtp timestamps too quickly, reset skew detection and start
* again. This value is sortof arbitrary and can be a bad measurement up if
* there are many packets missing because then we get a big gap that is
* unrelated to a timestamp switch. */
if (delta_diff > GST_SECOND) {
GST_DEBUG ("delta changed too quickly %" GST_TIME_FORMAT " reset skew",
GST_TIME_ARGS (delta_diff));
rdt_jitter_buffer_reset_skew (jbuf);
goto again;
}
}
jbuf->prev_send_diff = send_diff;
/* we don't have an arrival timestamp so we can't do skew detection. we
* should still apply a timestamp based on RDT timestamp and base_time */
if (time == -1)
goto no_skew;
/* elapsed time at receiver, includes the jitter */
recv_diff = time - jbuf->base_time;
GST_DEBUG ("time %" GST_TIME_FORMAT ", base %" GST_TIME_FORMAT ", recv_diff %"
GST_TIME_FORMAT, GST_TIME_ARGS (time), GST_TIME_ARGS (jbuf->base_time),
GST_TIME_ARGS (recv_diff));
/* measure the diff */
delta = ((gint64) recv_diff) - ((gint64) send_diff);
pos = jbuf->window_pos;
if (jbuf->window_filling) {
/* we are filling the window */
GST_DEBUG ("filling %d, delta %" G_GINT64_FORMAT, pos, delta);
jbuf->window[pos++] = delta;
/* calc the min delta we observed */
if (pos == 1 || delta < jbuf->window_min)
jbuf->window_min = delta;
if (send_diff >= MAX_TIME || pos >= MAX_WINDOW) {
jbuf->window_size = pos;
/* window filled */
GST_DEBUG ("min %" G_GINT64_FORMAT, jbuf->window_min);
/* the skew is now the min */
jbuf->skew = jbuf->window_min;
jbuf->window_filling = FALSE;
} else {
gint perc_time, perc_window, perc;
/* figure out how much we filled the window, this depends on the amount of
* time we have or the max number of points we keep. */
perc_time = send_diff * 100 / MAX_TIME;
perc_window = pos * 100 / MAX_WINDOW;
perc = MAX (perc_time, perc_window);
/* make a parabolic function, the closer we get to the MAX, the more value
* we give to the scaling factor of the new value */
perc = perc * perc;
/* quickly go to the min value when we are filling up, slowly when we are
* just starting because we're not sure it's a good value yet. */
jbuf->skew =
(perc * jbuf->window_min + ((10000 - perc) * jbuf->skew)) / 10000;
jbuf->window_size = pos + 1;
}
} else {
/* pick old value and store new value. We keep the previous value in order
* to quickly check if the min of the window changed */
old = jbuf->window[pos];
jbuf->window[pos++] = delta;
if (delta <= jbuf->window_min) {
/* if the new value we inserted is smaller or equal to the current min,
* it becomes the new min */
jbuf->window_min = delta;
} else if (old == jbuf->window_min) {
gint64 min = G_MAXINT64;
/* if we removed the old min, we have to find a new min */
for (i = 0; i < jbuf->window_size; i++) {
/* we found another value equal to the old min, we can stop searching now */
if (jbuf->window[i] == old) {
min = old;
break;
}
if (jbuf->window[i] < min)
min = jbuf->window[i];
}
jbuf->window_min = min;
}
/* average the min values */
jbuf->skew = (jbuf->window_min + (124 * jbuf->skew)) / 125;
GST_DEBUG ("delta %" G_GINT64_FORMAT ", new min: %" G_GINT64_FORMAT,
delta, jbuf->window_min);
}
/* wrap around in the window */
if (pos >= jbuf->window_size)
pos = 0;
jbuf->window_pos = pos;
no_skew:
/* the output time is defined as the base timestamp plus the RDT time
* adjusted for the clock skew .*/
out_time = jbuf->base_time + send_diff + jbuf->skew;
GST_DEBUG ("skew %" G_GINT64_FORMAT ", out %" GST_TIME_FORMAT,
jbuf->skew, GST_TIME_ARGS (out_time));
return out_time;
}
/**
* rdt_jitter_buffer_insert:
* @jbuf: an #RDTJitterBuffer
* @buf: a buffer
* @time: a running_time when this buffer was received in nanoseconds
* @clock_rate: the clock-rate of the payload of @buf
* @tail: TRUE when the tail element changed.
*
* Inserts @buf into the packet queue of @jbuf. The sequence number of the
* packet will be used to sort the packets. This function takes ownerhip of
* @buf when the function returns %TRUE.
* @buf should have writable metadata when calling this function.
*
* Returns: %FALSE if a packet with the same number already existed.
*/
gboolean
rdt_jitter_buffer_insert (RDTJitterBuffer * jbuf, GstBuffer * buf,
GstClockTime time, guint32 clock_rate, gboolean * tail)
{
GList *list;
guint32 rtptime;
guint16 seqnum;
GstRDTPacket packet;
gboolean more;
g_return_val_if_fail (jbuf != NULL, FALSE);
g_return_val_if_fail (buf != NULL, FALSE);
more = gst_rdt_buffer_get_first_packet (buf, &packet);
/* programmer error */
g_return_val_if_fail (more == TRUE, FALSE);
seqnum = gst_rdt_packet_data_get_seq (&packet);
/* do skew calculation by measuring the difference between rtptime and the
* receive time, this function will retimestamp @buf with the skew corrected
* running time. */
rtptime = gst_rdt_packet_data_get_timestamp (&packet);
/* loop the list to skip strictly smaller seqnum buffers */
for (list = jbuf->packets->head; list; list = g_list_next (list)) {
guint16 qseq;
gint gap;
more =
gst_rdt_buffer_get_first_packet (GST_BUFFER_CAST (list->data), &packet);
/* programmer error */
g_return_val_if_fail (more == TRUE, FALSE);
qseq = gst_rdt_packet_data_get_seq (&packet);
/* compare the new seqnum to the one in the buffer */
gap = gst_rdt_buffer_compare_seqnum (seqnum, qseq);
/* we hit a packet with the same seqnum, notify a duplicate */
if (G_UNLIKELY (gap == 0))
goto duplicate;
/* seqnum > qseq, we can stop looking */
if (G_LIKELY (gap < 0))
break;
}
if (clock_rate) {
time = calculate_skew (jbuf, rtptime, time, clock_rate);
GST_BUFFER_TIMESTAMP (buf) = time;
}
if (list)
g_queue_insert_before (jbuf->packets, list, buf);
else
g_queue_push_tail (jbuf->packets, buf);
/* tail was changed when we did not find a previous packet, we set the return
* flag when requested. */
if (tail)
*tail = (list == NULL);
return TRUE;
/* ERRORS */
duplicate:
{
GST_WARNING ("duplicate packet %d found", (gint) seqnum);
return FALSE;
}
}
/**
* rdt_jitter_buffer_pop:
* @jbuf: an #RDTJitterBuffer
*
* Pops the oldest buffer from the packet queue of @jbuf. The popped buffer will
* have its timestamp adjusted with the incoming running_time and the detected
* clock skew.
*
* Returns: a #GstBuffer or %NULL when there was no packet in the queue.
*/
GstBuffer *
rdt_jitter_buffer_pop (RDTJitterBuffer * jbuf)
{
GstBuffer *buf;
g_return_val_if_fail (jbuf != NULL, FALSE);
buf = g_queue_pop_tail (jbuf->packets);
return buf;
}
/**
* rdt_jitter_buffer_peek:
* @jbuf: an #RDTJitterBuffer
*
* Peek the oldest buffer from the packet queue of @jbuf. Register a callback
* with rdt_jitter_buffer_set_tail_changed() to be notified when an older packet
* was inserted in the queue.
*
* Returns: a #GstBuffer or %NULL when there was no packet in the queue.
*/
GstBuffer *
rdt_jitter_buffer_peek (RDTJitterBuffer * jbuf)
{
GstBuffer *buf;
g_return_val_if_fail (jbuf != NULL, FALSE);
buf = g_queue_peek_tail (jbuf->packets);
return buf;
}
/**
* rdt_jitter_buffer_flush:
* @jbuf: an #RDTJitterBuffer
*
* Flush all packets from the jitterbuffer.
*/
void
rdt_jitter_buffer_flush (RDTJitterBuffer * jbuf)
{
GstBuffer *buffer;
g_return_if_fail (jbuf != NULL);
while ((buffer = g_queue_pop_head (jbuf->packets)))
gst_buffer_unref (buffer);
}
/**
* rdt_jitter_buffer_num_packets:
* @jbuf: an #RDTJitterBuffer
*
* Get the number of packets currently in "jbuf.
*
* Returns: The number of packets in @jbuf.
*/
guint
rdt_jitter_buffer_num_packets (RDTJitterBuffer * jbuf)
{
g_return_val_if_fail (jbuf != NULL, 0);
return jbuf->packets->length;
}
/**
* rdt_jitter_buffer_get_ts_diff:
* @jbuf: an #RDTJitterBuffer
*
* Get the difference between the timestamps of first and last packet in the
* jitterbuffer.
*
* Returns: The difference expressed in the timestamp units of the packets.
*/
guint32
rdt_jitter_buffer_get_ts_diff (RDTJitterBuffer * jbuf)
{
guint64 high_ts, low_ts;
GstBuffer *high_buf, *low_buf;
guint32 result;
g_return_val_if_fail (jbuf != NULL, 0);
high_buf = g_queue_peek_head (jbuf->packets);
low_buf = g_queue_peek_tail (jbuf->packets);
if (!high_buf || !low_buf || high_buf == low_buf)
return 0;
//high_ts = gst_rtp_buffer_get_timestamp (high_buf);
//low_ts = gst_rtp_buffer_get_timestamp (low_buf);
high_ts = 0;
low_ts = 0;
/* it needs to work if ts wraps */
if (high_ts >= low_ts) {
result = (guint32) (high_ts - low_ts);
} else {
result = (guint32) (high_ts + G_MAXUINT32 + 1 - low_ts);
}
return result;
}

View file

@ -0,0 +1,91 @@
/* GStreamer
* Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __RDT_JITTER_BUFFER_H__
#define __RDT_JITTER_BUFFER_H__
#include <gst/gst.h>
typedef struct _RDTJitterBuffer RDTJitterBuffer;
typedef struct _RDTJitterBufferClass RDTJitterBufferClass;
#define RDT_TYPE_JITTER_BUFFER (rdt_jitter_buffer_get_type())
#define RDT_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_CAST((src),RDT_TYPE_JITTER_BUFFER,RDTJitterBuffer))
#define RDT_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RDT_TYPE_JITTER_BUFFER,RDTJitterBufferClass))
#define RDT_IS_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_TYPE((src),RDT_TYPE_JITTER_BUFFER))
#define RDT_IS_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),RDT_TYPE_JITTER_BUFFER))
#define RDT_JITTER_BUFFER_CAST(src) ((RDTJitterBuffer *)(src))
/**
* RTPTailChanged:
* @jbuf: an #RDTJitterBuffer
* @user_data: user data specified when registering
*
* This callback will be called when the tail buffer of @jbuf changed.
*/
typedef void (*RTPTailChanged) (RDTJitterBuffer *jbuf, gpointer user_data);
#define RDT_JITTER_BUFFER_MAX_WINDOW 512
/**
* RDTJitterBuffer:
*
* A JitterBuffer in the #RTPSession
*/
struct _RDTJitterBuffer {
GObject object;
GQueue *packets;
/* for calculating skew */
GstClockTime base_time;
GstClockTime base_rtptime;
guint64 ext_rtptime;
gint64 window[RDT_JITTER_BUFFER_MAX_WINDOW];
guint window_pos;
guint window_size;
gboolean window_filling;
gint64 window_min;
gint64 skew;
gint64 prev_send_diff;
};
struct _RDTJitterBufferClass {
GObjectClass parent_class;
};
GType rdt_jitter_buffer_get_type (void);
/* managing lifetime */
RDTJitterBuffer* rdt_jitter_buffer_new (void);
void rdt_jitter_buffer_reset_skew (RDTJitterBuffer *jbuf);
gboolean rdt_jitter_buffer_insert (RDTJitterBuffer *jbuf, GstBuffer *buf,
GstClockTime time,
guint32 clock_rate,
gboolean *tail);
GstBuffer * rdt_jitter_buffer_peek (RDTJitterBuffer *jbuf);
GstBuffer * rdt_jitter_buffer_pop (RDTJitterBuffer *jbuf);
void rdt_jitter_buffer_flush (RDTJitterBuffer *jbuf);
guint rdt_jitter_buffer_num_packets (RDTJitterBuffer *jbuf);
guint32 rdt_jitter_buffer_get_ts_diff (RDTJitterBuffer *jbuf);
#endif /* __RDT_JITTER_BUFFER_H__ */

Some files were not shown because too many files have changed in this diff Show more