mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-18 14:26:43 +00:00
Merging gst-plugins-ugly
This commit is contained in:
commit
43c69ab201
177 changed files with 92422 additions and 0 deletions
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
*~
|
||||
*.bak
|
||||
|
||||
Build
|
||||
*.user
|
||||
*.suo
|
||||
*.ipch
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.DS_Store
|
||||
|
||||
# Meson
|
||||
/build
|
||||
/_build
|
||||
/subprojects
|
1
.gitlab-ci.yml
Normal file
1
.gitlab-ci.yml
Normal file
|
@ -0,0 +1 @@
|
|||
include: "https://gitlab.freedesktop.org/gstreamer/gst-ci/raw/master/gitlab/ci_template.yml"
|
21
AUTHORS
Normal file
21
AUTHORS
Normal 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
504
COPYING
Normal 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!
|
||||
|
||||
|
12
MAINTAINERS
Normal file
12
MAINTAINERS
Normal 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
299
NEWS
Normal 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 that’s 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 don’t 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
252
README
Normal 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
174
README.static-linking
Normal 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 didn’t 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
96
RELEASE
Normal 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
48
REQUIREMENTS
Normal 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
6
docs/all_index.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
short-description: Plugins from gst-plugins-ugly
|
||||
...
|
||||
|
||||
# Plugins
|
||||
|
1
docs/gst_api_version.in
Normal file
1
docs/gst_api_version.in
Normal file
|
@ -0,0 +1 @@
|
|||
@GST_API_VERSION@
|
2015
docs/gst_plugins_cache.json
Normal file
2015
docs/gst_plugins_cache.json
Normal file
File diff suppressed because it is too large
Load diff
0
docs/index.md
Normal file
0
docs/index.md
Normal file
102
docs/meson.build
Normal file
102
docs/meson.build
Normal 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
17692
docs/random/ChangeLog-0.8
Normal file
File diff suppressed because it is too large
Load diff
18
docs/random/LICENSE
Normal file
18
docs/random/LICENSE
Normal 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
17
docs/random/PORTED_09
Normal 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
1
docs/sitemap.txt
Normal file
|
@ -0,0 +1 @@
|
|||
gst-index
|
848
ext/a52dec/gsta52dec.c
Normal file
848
ext/a52dec/gsta52dec.c
Normal 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
105
ext/a52dec/gsta52dec.h
Normal 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
22
ext/a52dec/meson.build
Normal 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
11
ext/amrnb/GstAmrnbEnc.prs
Normal 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
6
ext/amrnb/README
Normal 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
44
ext/amrnb/amrnb.c
Normal 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
309
ext/amrnb/amrnbdec.c
Normal 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
71
ext/amrnb/amrnbdec.h
Normal 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
295
ext/amrnb/amrnbenc.c
Normal 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
67
ext/amrnb/amrnbenc.h
Normal 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
15
ext/amrnb/meson.build
Normal 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
6
ext/amrwbdec/README
Normal 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
38
ext/amrwbdec/amrwb.c
Normal 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
234
ext/amrwbdec/amrwbdec.c
Normal 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
69
ext/amrwbdec/amrwbdec.h
Normal 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
14
ext/amrwbdec/meson.build
Normal 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
150
ext/cdio/gstcdio.c
Normal 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
61
ext/cdio/gstcdio.h
Normal 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
518
ext/cdio/gstcdiocddasrc.c
Normal 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://<track-number> 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
55
ext/cdio/gstcdiocddasrc.h
Normal 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
14
ext/cdio/meson.build
Normal 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
4
ext/dvdread/README
Normal file
|
@ -0,0 +1,4 @@
|
|||
Various Info
|
||||
============
|
||||
|
||||
http://dvd.sourceforge.net/
|
6
ext/dvdread/demo-play
Executable file
6
ext/dvdread/demo-play
Executable 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
1819
ext/dvdread/dvdreadsrc.c
Normal file
File diff suppressed because it is too large
Load diff
103
ext/dvdread/dvdreadsrc.h
Normal file
103
ext/dvdread/dvdreadsrc.h
Normal 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
14
ext/dvdread/meson.build
Normal 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
8
ext/meson.build
Normal 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
1180
ext/mpeg2dec/gstmpeg2dec.c
Normal file
File diff suppressed because it is too large
Load diff
90
ext/mpeg2dec/gstmpeg2dec.h
Normal file
90
ext/mpeg2dec/gstmpeg2dec.h
Normal 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
14
ext/mpeg2dec/meson.build
Normal 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
780
ext/sidplay/gstsiddec.cc
Normal 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, ¤t);
|
||||
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
76
ext/sidplay/gstsiddec.h
Normal 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
38
ext/sidplay/meson.build
Normal 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
54
ext/x264/GstX264Enc.prs
Normal 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
|
224
ext/x264/gstencoderbitrateprofilemanager.c
Normal file
224
ext/x264/gstencoderbitrateprofilemanager.c
Normal 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;
|
||||
}
|
46
ext/x264/gstencoderbitrateprofilemanager.h
Normal file
46
ext/x264/gstencoderbitrateprofilemanager.h
Normal 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
3057
ext/x264/gstx264enc.c
Normal file
File diff suppressed because it is too large
Load diff
144
ext/x264/gstx264enc.h
Normal file
144
ext/x264/gstx264enc.h
Normal 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
31
ext/x264/meson.build
Normal 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
69
gst-libs/gst/gettext.h
Normal 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 */
|
36
gst-libs/gst/glib-compat-private.h
Normal file
36
gst-libs/gst/glib-compat-private.h
Normal 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
|
37
gst-libs/gst/gst-i18n-plugin.h
Normal file
37
gst-libs/gst/gst-i18n-plugin.h
Normal 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
875
gst-plugins-ugly.doap
Normal 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>I’ll 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
88
gst/asfdemux/README
Normal 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
212
gst/asfdemux/asfheaders.c
Normal 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
157
gst/asfdemux/asfheaders.h
Normal 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
808
gst/asfdemux/asfpacket.c
Normal 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
74
gst/asfdemux/asfpacket.h
Normal 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
52
gst/asfdemux/gstasf.c
Normal 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
4921
gst/asfdemux/gstasfdemux.c
Normal file
File diff suppressed because it is too large
Load diff
256
gst/asfdemux/gstasfdemux.h
Normal file
256
gst/asfdemux/gstasfdemux.h
Normal 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__ */
|
52
gst/asfdemux/gstasfelement.c
Normal file
52
gst/asfdemux/gstasfelement.c
Normal 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);
|
||||
}
|
||||
|
||||
}
|
39
gst/asfdemux/gstasfelements.h
Normal file
39
gst/asfdemux/gstasfelements.h
Normal 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__ */
|
546
gst/asfdemux/gstrtpasfdepay.c
Normal file
546
gst/asfdemux/gstrtpasfdepay.c
Normal 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;
|
||||
}
|
64
gst/asfdemux/gstrtpasfdepay.h
Normal file
64
gst/asfdemux/gstrtpasfdepay.h
Normal 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
240
gst/asfdemux/gstrtspwms.c
Normal 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
50
gst/asfdemux/gstrtspwms.h
Normal 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
22
gst/asfdemux/meson.build
Normal 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]
|
1018
gst/dvdlpcmdec/gstdvdlpcmdec.c
Normal file
1018
gst/dvdlpcmdec/gstdvdlpcmdec.c
Normal file
File diff suppressed because it is too large
Load diff
79
gst/dvdlpcmdec/gstdvdlpcmdec.h
Normal file
79
gst/dvdlpcmdec/gstdvdlpcmdec.h
Normal 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__ */
|
14
gst/dvdlpcmdec/meson.build
Normal file
14
gst/dvdlpcmdec/meson.build
Normal 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
1164
gst/dvdsub/gstdvdsubdec.c
Normal file
File diff suppressed because it is too large
Load diff
111
gst/dvdsub/gstdvdsubdec.h
Normal file
111
gst/dvdsub/gstdvdsubdec.h
Normal 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
241
gst/dvdsub/gstdvdsubparse.c
Normal 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;
|
||||
}
|
66
gst/dvdsub/gstdvdsubparse.h
Normal file
66
gst/dvdsub/gstdvdsubparse.h
Normal 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
15
gst/dvdsub/meson.build
Normal 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
5
gst/meson.build
Normal 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
712
gst/realmedia/asmrules.c
Normal 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
115
gst/realmedia/asmrules.h
Normal 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__ */
|
477
gst/realmedia/gstrdtbuffer.c
Normal file
477
gst/realmedia/gstrdtbuffer.c
Normal 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;
|
||||
}
|
121
gst/realmedia/gstrdtbuffer.h
Normal file
121
gst/realmedia/gstrdtbuffer.h
Normal 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
25
gst/realmedia/meson.build
Normal 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
234
gst/realmedia/pnmsrc.c
Normal 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
59
gst/realmedia/pnmsrc.h
Normal 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
1000
gst/realmedia/rademux.c
Normal file
File diff suppressed because it is too large
Load diff
104
gst/realmedia/rademux.h
Normal file
104
gst/realmedia/rademux.h
Normal 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
496
gst/realmedia/rdtdepay.c
Normal 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
74
gst/realmedia/rdtdepay.h
Normal 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__ */
|
531
gst/realmedia/rdtjitterbuffer.c
Normal file
531
gst/realmedia/rdtjitterbuffer.c
Normal 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;
|
||||
}
|
91
gst/realmedia/rdtjitterbuffer.h
Normal file
91
gst/realmedia/rdtjitterbuffer.h
Normal 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
Loading…
Reference in a new issue