mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-19 14:56:36 +00:00
Merging gst-libav
This commit is contained in:
commit
97408bc7b6
50 changed files with 164143 additions and 0 deletions
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
*~
|
||||
|
||||
Build
|
||||
*.user
|
||||
*.suo
|
||||
*.ipch
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.DS_Store
|
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"
|
1
AUTHORS
Normal file
1
AUTHORS
Normal file
|
@ -0,0 +1 @@
|
|||
Ronald Bultje
|
503
COPYING
Normal file
503
COPYING
Normal file
|
@ -0,0 +1,503 @@
|
|||
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!
|
||||
|
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
|
23
README.md
Normal file
23
README.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
# gst-libav
|
||||
|
||||
This module contains a GStreamer plugin for using the encoders, decoders,
|
||||
muxers, and demuxers provided by FFmpeg. It is called gst-libav for historical
|
||||
reasons.
|
||||
|
||||
# Plugin Dependencies and Licenses
|
||||
|
||||
GStreamer is developed under the terms of the LGPL-2.1 (see COPYING file for
|
||||
details), and that includes the code in this repository.
|
||||
|
||||
However, this repository depends on FFmpeg, which can be built in the following
|
||||
modes using various `./configure` switches: LGPL-2.1, LGPL-3, GPL, or non-free.
|
||||
|
||||
This can mean, for example, that if you are distributing an application which
|
||||
has a non-GPL compatible license (like a closed-source application) with
|
||||
GStreamer, you have to make sure not to build FFmpeg with GPL code enabled.
|
||||
|
||||
Overall, when using plugins that link to GPL libraries, GStreamer is for all
|
||||
practical reasons under the GPL itself.
|
||||
|
||||
The above recommendations are not legal advice, and you are responsible for
|
||||
ensuring that you meet your licensing obligations.
|
96
RELEASE
Normal file
96
RELEASE
Normal file
|
@ -0,0 +1,96 @@
|
|||
This is GStreamer gst-libav 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
|
130794
docs/gst_plugins_cache.json
Normal file
130794
docs/gst_plugins_cache.json
Normal file
File diff suppressed because it is too large
Load diff
5
docs/index.md
Normal file
5
docs/index.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
short-description: GStreamer plugins from gst-ffmpeg
|
||||
...
|
||||
|
||||
# FFMPEG plugin
|
79
docs/meson.build
Normal file
79
docs/meson.build
Normal file
|
@ -0,0 +1,79 @@
|
|||
build_hotdoc = false
|
||||
|
||||
if meson.is_cross_build()
|
||||
if get_option('doc').enabled()
|
||||
error('Documentation enabled but building the doc while cross building is not supported yet.')
|
||||
endif
|
||||
|
||||
message('Documentation not built as building it while cross building is not supported yet.')
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
required_hotdoc_extensions = ['gst-extension']
|
||||
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
|
||||
|
||||
plugins_cache = join_paths(meson.current_source_dir(), 'gst_plugins_cache.json')
|
||||
if plugins_cache_generator.found()
|
||||
plugins_doc_dep = custom_target('libav-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
|
||||
|
||||
build_hotdoc = true
|
||||
hotdoc = import('hotdoc')
|
||||
docconf = configuration_data()
|
||||
docconf.set('GST_API_VERSION', api_version)
|
||||
|
||||
foreach extension: required_hotdoc_extensions
|
||||
if not hotdoc.has_extensions(extension)
|
||||
if get_option('doc').enabled()
|
||||
error('Documentation enabled but gi-extension missing')
|
||||
endif
|
||||
|
||||
message('@0@ extensions not found, not building documentation requiring it'.format(extension))
|
||||
subdir_done()
|
||||
endif
|
||||
endforeach
|
||||
|
||||
|
||||
libs_doc = []
|
||||
plugins_doc = [hotdoc.generate_doc('libav',
|
||||
project_version: api_version,
|
||||
sitemap: 'sitemap.txt',
|
||||
index: 'index.md',
|
||||
gst_index: 'index.md',
|
||||
gst_smart_index: true,
|
||||
gst_c_sources: ['../ext/*/*.[ch]',],
|
||||
gst_cache_file: plugins_cache,
|
||||
gst_plugin_name: 'libav',
|
||||
dependencies: [gst_dep, gstlibav_plugin, plugins_doc_dep],
|
||||
disable_incremental_build: true,
|
||||
)]
|
1
docs/sitemap.txt
Normal file
1
docs/sitemap.txt
Normal file
|
@ -0,0 +1 @@
|
|||
gst-index
|
174
ext/libav/gstav.c
Normal file
174
ext/libav/gstav.c
Normal file
|
@ -0,0 +1,174 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/* First, include the header file for the plugin, to bring in the
|
||||
* object definition and other useful things.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "gstav.h"
|
||||
#include "gstavutils.h"
|
||||
#include "gstavcfg.h"
|
||||
|
||||
#ifdef GST_LIBAV_ENABLE_GPL
|
||||
#define LICENSE "GPL"
|
||||
#else
|
||||
#define LICENSE "LGPL"
|
||||
#endif
|
||||
|
||||
GST_DEBUG_CATEGORY (ffmpeg_debug);
|
||||
|
||||
static GMutex gst_avcodec_mutex;
|
||||
|
||||
/*
|
||||
* Check for FFmpeg-provided libavcodec/format
|
||||
*/
|
||||
static inline gboolean
|
||||
gst_ffmpeg_avcodec_is_ffmpeg (void)
|
||||
{
|
||||
guint av_version = avcodec_version ();
|
||||
|
||||
GST_DEBUG ("Using libavcodec version %d.%d.%d",
|
||||
av_version >> 16, (av_version & 0x00ff00) >> 8, av_version & 0xff);
|
||||
|
||||
/* FFmpeg *_MICRO versions start at 100 and Libav's at 0 */
|
||||
if ((av_version & 0xff) < 100)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
gst_ffmpeg_avcodec_open (AVCodecContext * avctx, AVCodec * codec)
|
||||
{
|
||||
int ret;
|
||||
|
||||
g_mutex_lock (&gst_avcodec_mutex);
|
||||
ret = avcodec_open2 (avctx, codec, NULL);
|
||||
g_mutex_unlock (&gst_avcodec_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
gst_ffmpeg_avcodec_close (AVCodecContext * avctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
g_mutex_lock (&gst_avcodec_mutex);
|
||||
ret = avcodec_close (avctx);
|
||||
g_mutex_unlock (&gst_avcodec_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
gst_ffmpeg_av_find_stream_info (AVFormatContext * ic)
|
||||
{
|
||||
int ret;
|
||||
|
||||
g_mutex_lock (&gst_avcodec_mutex);
|
||||
ret = avformat_find_stream_info (ic, NULL);
|
||||
g_mutex_unlock (&gst_avcodec_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef GST_DISABLE_GST_DEBUG
|
||||
static void
|
||||
gst_ffmpeg_log_callback (void *ptr, int level, const char *fmt, va_list vl)
|
||||
{
|
||||
GstDebugLevel gst_level;
|
||||
gint len = strlen (fmt);
|
||||
gchar *fmt2 = NULL;
|
||||
|
||||
switch (level) {
|
||||
case AV_LOG_QUIET:
|
||||
gst_level = GST_LEVEL_NONE;
|
||||
break;
|
||||
case AV_LOG_ERROR:
|
||||
gst_level = GST_LEVEL_ERROR;
|
||||
break;
|
||||
case AV_LOG_INFO:
|
||||
gst_level = GST_LEVEL_INFO;
|
||||
break;
|
||||
case AV_LOG_DEBUG:
|
||||
gst_level = GST_LEVEL_DEBUG;
|
||||
break;
|
||||
default:
|
||||
gst_level = GST_LEVEL_INFO;
|
||||
break;
|
||||
}
|
||||
|
||||
/* remove trailing newline as it gets already appended by the logger */
|
||||
if (fmt[len - 1] == '\n') {
|
||||
fmt2 = g_strdup (fmt);
|
||||
fmt2[len - 1] = '\0';
|
||||
}
|
||||
|
||||
gst_debug_log_valist (ffmpeg_debug, gst_level, "", "", 0, NULL,
|
||||
fmt2 ? fmt2 : fmt, vl);
|
||||
|
||||
g_free (fmt2);
|
||||
}
|
||||
#endif
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
GST_DEBUG_CATEGORY_INIT (ffmpeg_debug, "libav", 0, "libav elements");
|
||||
|
||||
/* Bail if not FFmpeg. We can no longer ensure operation with Libav */
|
||||
if (!gst_ffmpeg_avcodec_is_ffmpeg ()) {
|
||||
GST_ERROR_OBJECT (plugin,
|
||||
"Incompatible, non-FFmpeg libavcodec/format found");
|
||||
return FALSE;
|
||||
}
|
||||
#ifndef GST_DISABLE_GST_DEBUG
|
||||
av_log_set_callback (gst_ffmpeg_log_callback);
|
||||
#endif
|
||||
|
||||
gst_ffmpeg_init_pix_fmt_info ();
|
||||
|
||||
/* build global ffmpeg param/property info */
|
||||
gst_ffmpeg_cfg_init ();
|
||||
|
||||
gst_ffmpegaudenc_register (plugin);
|
||||
gst_ffmpegvidenc_register (plugin);
|
||||
gst_ffmpegauddec_register (plugin);
|
||||
gst_ffmpegviddec_register (plugin);
|
||||
gst_ffmpegdemux_register (plugin);
|
||||
gst_ffmpegmux_register (plugin);
|
||||
gst_ffmpegdeinterlace_register (plugin);
|
||||
|
||||
/* Now we can return the pointer to the newly created Plugin object. */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
libav,
|
||||
"All libav codecs and formats (" LIBAV_SOURCE ")",
|
||||
plugin_init, PACKAGE_VERSION, LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
|
57
ext/libav/gstav.h
Normal file
57
ext/libav/gstav.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/* First, include the header file for the plugin, to bring in the
|
||||
* object definition and other useful things.
|
||||
*/
|
||||
|
||||
#ifndef __GST_FFMPEG_H__
|
||||
#define __GST_FFMPEG_H__
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/audio.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (ffmpeg_debug);
|
||||
#define GST_CAT_DEFAULT ffmpeg_debug
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
extern gboolean gst_ffmpegdemux_register (GstPlugin * plugin);
|
||||
extern gboolean gst_ffmpegauddec_register (GstPlugin * plugin);
|
||||
extern gboolean gst_ffmpegviddec_register (GstPlugin * plugin);
|
||||
extern gboolean gst_ffmpegaudenc_register (GstPlugin * plugin);
|
||||
extern gboolean gst_ffmpegvidenc_register (GstPlugin * plugin);
|
||||
extern gboolean gst_ffmpegmux_register (GstPlugin * plugin);
|
||||
extern gboolean gst_ffmpegdeinterlace_register (GstPlugin * plugin);
|
||||
|
||||
int gst_ffmpeg_avcodec_open (AVCodecContext *avctx, AVCodec *codec);
|
||||
int gst_ffmpeg_avcodec_close (AVCodecContext *avctx);
|
||||
int gst_ffmpeg_av_find_stream_info(AVFormatContext *ic);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
/* use GST_FFMPEG URL_STREAMHEADER with URL_WRONLY if the first
|
||||
* buffer should be used as streamheader property on the pad's caps. */
|
||||
#define GST_FFMPEG_URL_STREAMHEADER 16
|
||||
|
||||
#endif /* __GST_FFMPEG_H__ */
|
899
ext/libav/gstavauddec.c
Normal file
899
ext/libav/gstavauddec.c
Normal file
|
@ -0,0 +1,899 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||
* Copyright (C) <2012> Collabora Ltd.
|
||||
* Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||
*
|
||||
* 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 <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/channel_layout.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "gstav.h"
|
||||
#include "gstavcodecmap.h"
|
||||
#include "gstavutils.h"
|
||||
#include "gstavauddec.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
|
||||
|
||||
/* A number of function prototypes are given so we can refer to them later. */
|
||||
static void gst_ffmpegauddec_base_init (GstFFMpegAudDecClass * klass);
|
||||
static void gst_ffmpegauddec_class_init (GstFFMpegAudDecClass * klass);
|
||||
static void gst_ffmpegauddec_init (GstFFMpegAudDec * ffmpegdec);
|
||||
static void gst_ffmpegauddec_finalize (GObject * object);
|
||||
static gboolean gst_ffmpegauddec_propose_allocation (GstAudioDecoder * decoder,
|
||||
GstQuery * query);
|
||||
|
||||
static gboolean gst_ffmpegauddec_start (GstAudioDecoder * decoder);
|
||||
static gboolean gst_ffmpegauddec_stop (GstAudioDecoder * decoder);
|
||||
static void gst_ffmpegauddec_flush (GstAudioDecoder * decoder, gboolean hard);
|
||||
static gboolean gst_ffmpegauddec_set_format (GstAudioDecoder * decoder,
|
||||
GstCaps * caps);
|
||||
static GstFlowReturn gst_ffmpegauddec_handle_frame (GstAudioDecoder * decoder,
|
||||
GstBuffer * inbuf);
|
||||
|
||||
static gboolean gst_ffmpegauddec_negotiate (GstFFMpegAudDec * ffmpegdec,
|
||||
AVCodecContext * context, AVFrame * frame, gboolean force);
|
||||
|
||||
static GstFlowReturn gst_ffmpegauddec_drain (GstFFMpegAudDec * ffmpegdec,
|
||||
gboolean force);
|
||||
|
||||
#define GST_FFDEC_PARAMS_QDATA g_quark_from_static_string("avdec-params")
|
||||
|
||||
static GstElementClass *parent_class = NULL;
|
||||
|
||||
static void
|
||||
gst_ffmpegauddec_base_init (GstFFMpegAudDecClass * klass)
|
||||
{
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||
GstPadTemplate *sinktempl, *srctempl;
|
||||
GstCaps *sinkcaps, *srccaps;
|
||||
AVCodec *in_plugin;
|
||||
gchar *longname, *description;
|
||||
|
||||
in_plugin =
|
||||
(AVCodec *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
|
||||
GST_FFDEC_PARAMS_QDATA);
|
||||
g_assert (in_plugin != NULL);
|
||||
|
||||
/* construct the element details struct */
|
||||
longname = g_strdup_printf ("libav %s decoder", in_plugin->long_name);
|
||||
description = g_strdup_printf ("libav %s decoder", in_plugin->name);
|
||||
gst_element_class_set_metadata (element_class, longname,
|
||||
"Codec/Decoder/Audio", description,
|
||||
"Wim Taymans <wim.taymans@gmail.com>, "
|
||||
"Ronald Bultje <rbultje@ronald.bitfreak.net>, "
|
||||
"Edward Hervey <bilboed@bilboed.com>");
|
||||
g_free (longname);
|
||||
g_free (description);
|
||||
|
||||
/* get the caps */
|
||||
sinkcaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, FALSE);
|
||||
if (!sinkcaps) {
|
||||
GST_DEBUG ("Couldn't get sink caps for decoder '%s'", in_plugin->name);
|
||||
sinkcaps = gst_caps_from_string ("unknown/unknown");
|
||||
}
|
||||
srccaps = gst_ffmpeg_codectype_to_audio_caps (NULL,
|
||||
in_plugin->id, FALSE, in_plugin);
|
||||
if (!srccaps) {
|
||||
GST_DEBUG ("Couldn't get source caps for decoder '%s'", in_plugin->name);
|
||||
srccaps = gst_caps_from_string ("audio/x-raw");
|
||||
}
|
||||
|
||||
/* pad templates */
|
||||
sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS, sinkcaps);
|
||||
srctempl = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, srccaps);
|
||||
|
||||
gst_element_class_add_pad_template (element_class, srctempl);
|
||||
gst_element_class_add_pad_template (element_class, sinktempl);
|
||||
|
||||
gst_caps_unref (sinkcaps);
|
||||
gst_caps_unref (srccaps);
|
||||
|
||||
klass->in_plugin = in_plugin;
|
||||
klass->srctempl = srctempl;
|
||||
klass->sinktempl = sinktempl;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegauddec_class_init (GstFFMpegAudDecClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GstAudioDecoderClass *gstaudiodecoder_class = GST_AUDIO_DECODER_CLASS (klass);
|
||||
|
||||
parent_class = g_type_class_peek_parent (klass);
|
||||
|
||||
gobject_class->finalize = gst_ffmpegauddec_finalize;
|
||||
|
||||
gstaudiodecoder_class->start = GST_DEBUG_FUNCPTR (gst_ffmpegauddec_start);
|
||||
gstaudiodecoder_class->stop = GST_DEBUG_FUNCPTR (gst_ffmpegauddec_stop);
|
||||
gstaudiodecoder_class->set_format =
|
||||
GST_DEBUG_FUNCPTR (gst_ffmpegauddec_set_format);
|
||||
gstaudiodecoder_class->handle_frame =
|
||||
GST_DEBUG_FUNCPTR (gst_ffmpegauddec_handle_frame);
|
||||
gstaudiodecoder_class->flush = GST_DEBUG_FUNCPTR (gst_ffmpegauddec_flush);
|
||||
gstaudiodecoder_class->propose_allocation =
|
||||
GST_DEBUG_FUNCPTR (gst_ffmpegauddec_propose_allocation);
|
||||
|
||||
GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegauddec_init (GstFFMpegAudDec * ffmpegdec)
|
||||
{
|
||||
GstFFMpegAudDecClass *klass =
|
||||
(GstFFMpegAudDecClass *) G_OBJECT_GET_CLASS (ffmpegdec);
|
||||
|
||||
/* some ffmpeg data */
|
||||
ffmpegdec->context = avcodec_alloc_context3 (klass->in_plugin);
|
||||
ffmpegdec->context->opaque = ffmpegdec;
|
||||
ffmpegdec->opened = FALSE;
|
||||
|
||||
ffmpegdec->frame = av_frame_alloc ();
|
||||
|
||||
GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_DECODER_SINK_PAD (ffmpegdec));
|
||||
gst_audio_decoder_set_use_default_pad_acceptcaps (GST_AUDIO_DECODER_CAST
|
||||
(ffmpegdec), TRUE);
|
||||
|
||||
gst_audio_decoder_set_drainable (GST_AUDIO_DECODER (ffmpegdec), TRUE);
|
||||
gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (ffmpegdec), TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegauddec_finalize (GObject * object)
|
||||
{
|
||||
GstFFMpegAudDec *ffmpegdec = (GstFFMpegAudDec *) object;
|
||||
|
||||
av_frame_free (&ffmpegdec->frame);
|
||||
|
||||
if (ffmpegdec->context != NULL) {
|
||||
gst_ffmpeg_avcodec_close (ffmpegdec->context);
|
||||
av_free (ffmpegdec->context);
|
||||
ffmpegdec->context = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* With LOCK */
|
||||
static gboolean
|
||||
gst_ffmpegauddec_close (GstFFMpegAudDec * ffmpegdec, gboolean reset)
|
||||
{
|
||||
GstFFMpegAudDecClass *oclass;
|
||||
|
||||
oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
|
||||
|
||||
GST_LOG_OBJECT (ffmpegdec, "closing libav codec");
|
||||
|
||||
gst_caps_replace (&ffmpegdec->last_caps, NULL);
|
||||
|
||||
gst_ffmpeg_avcodec_close (ffmpegdec->context);
|
||||
ffmpegdec->opened = FALSE;
|
||||
|
||||
if (ffmpegdec->context->extradata) {
|
||||
av_free (ffmpegdec->context->extradata);
|
||||
ffmpegdec->context->extradata = NULL;
|
||||
}
|
||||
|
||||
if (reset) {
|
||||
if (avcodec_get_context_defaults3 (ffmpegdec->context,
|
||||
oclass->in_plugin) < 0) {
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Failed to set context defaults");
|
||||
return FALSE;
|
||||
}
|
||||
ffmpegdec->context->opaque = ffmpegdec;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_ffmpegauddec_start (GstAudioDecoder * decoder)
|
||||
{
|
||||
GstFFMpegAudDec *ffmpegdec = (GstFFMpegAudDec *) decoder;
|
||||
GstFFMpegAudDecClass *oclass;
|
||||
|
||||
oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
|
||||
|
||||
GST_OBJECT_LOCK (ffmpegdec);
|
||||
gst_ffmpeg_avcodec_close (ffmpegdec->context);
|
||||
if (avcodec_get_context_defaults3 (ffmpegdec->context, oclass->in_plugin) < 0) {
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Failed to set context defaults");
|
||||
GST_OBJECT_UNLOCK (ffmpegdec);
|
||||
return FALSE;
|
||||
}
|
||||
ffmpegdec->context->opaque = ffmpegdec;
|
||||
GST_OBJECT_UNLOCK (ffmpegdec);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_ffmpegauddec_stop (GstAudioDecoder * decoder)
|
||||
{
|
||||
GstFFMpegAudDec *ffmpegdec = (GstFFMpegAudDec *) decoder;
|
||||
|
||||
GST_OBJECT_LOCK (ffmpegdec);
|
||||
gst_ffmpegauddec_close (ffmpegdec, FALSE);
|
||||
g_free (ffmpegdec->padded);
|
||||
ffmpegdec->padded = NULL;
|
||||
ffmpegdec->padded_size = 0;
|
||||
GST_OBJECT_UNLOCK (ffmpegdec);
|
||||
gst_audio_info_init (&ffmpegdec->info);
|
||||
gst_caps_replace (&ffmpegdec->last_caps, NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* with LOCK */
|
||||
static gboolean
|
||||
gst_ffmpegauddec_open (GstFFMpegAudDec * ffmpegdec)
|
||||
{
|
||||
GstFFMpegAudDecClass *oclass;
|
||||
|
||||
oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
|
||||
|
||||
if (gst_ffmpeg_avcodec_open (ffmpegdec->context, oclass->in_plugin) < 0)
|
||||
goto could_not_open;
|
||||
|
||||
ffmpegdec->opened = TRUE;
|
||||
|
||||
GST_LOG_OBJECT (ffmpegdec, "Opened libav codec %s, id %d",
|
||||
oclass->in_plugin->name, oclass->in_plugin->id);
|
||||
|
||||
gst_audio_info_init (&ffmpegdec->info);
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
could_not_open:
|
||||
{
|
||||
gst_ffmpegauddec_close (ffmpegdec, TRUE);
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "avdec_%s: Failed to open libav codec",
|
||||
oclass->in_plugin->name);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_ffmpegauddec_propose_allocation (GstAudioDecoder * decoder,
|
||||
GstQuery * query)
|
||||
{
|
||||
GstAllocationParams params;
|
||||
|
||||
gst_allocation_params_init (¶ms);
|
||||
params.flags = GST_MEMORY_FLAG_ZERO_PADDED;
|
||||
params.align = 15;
|
||||
params.padding = AV_INPUT_BUFFER_PADDING_SIZE;
|
||||
/* we would like to have some padding so that we don't have to
|
||||
* memcpy. We don't suggest an allocator. */
|
||||
gst_query_add_allocation_param (query, NULL, ¶ms);
|
||||
|
||||
return GST_AUDIO_DECODER_CLASS (parent_class)->propose_allocation (decoder,
|
||||
query);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_ffmpegauddec_set_format (GstAudioDecoder * decoder, GstCaps * caps)
|
||||
{
|
||||
GstFFMpegAudDec *ffmpegdec = (GstFFMpegAudDec *) decoder;
|
||||
GstFFMpegAudDecClass *oclass;
|
||||
gboolean ret = TRUE;
|
||||
|
||||
oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
|
||||
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "setcaps called");
|
||||
|
||||
GST_OBJECT_LOCK (ffmpegdec);
|
||||
|
||||
if (ffmpegdec->last_caps && gst_caps_is_equal (ffmpegdec->last_caps, caps)) {
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "same caps");
|
||||
GST_OBJECT_UNLOCK (ffmpegdec);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gst_caps_replace (&ffmpegdec->last_caps, caps);
|
||||
|
||||
/* close old session */
|
||||
if (ffmpegdec->opened) {
|
||||
GST_OBJECT_UNLOCK (ffmpegdec);
|
||||
gst_ffmpegauddec_drain (ffmpegdec, FALSE);
|
||||
GST_OBJECT_LOCK (ffmpegdec);
|
||||
if (!gst_ffmpegauddec_close (ffmpegdec, TRUE)) {
|
||||
GST_OBJECT_UNLOCK (ffmpegdec);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* get size and so */
|
||||
gst_ffmpeg_caps_with_codecid (oclass->in_plugin->id,
|
||||
oclass->in_plugin->type, caps, ffmpegdec->context);
|
||||
|
||||
/* workaround encoder bugs */
|
||||
ffmpegdec->context->workaround_bugs |= FF_BUG_AUTODETECT;
|
||||
ffmpegdec->context->err_recognition = 1;
|
||||
|
||||
/* open codec - we don't select an output pix_fmt yet,
|
||||
* simply because we don't know! We only get it
|
||||
* during playback... */
|
||||
if (!gst_ffmpegauddec_open (ffmpegdec))
|
||||
goto open_failed;
|
||||
|
||||
done:
|
||||
GST_OBJECT_UNLOCK (ffmpegdec);
|
||||
|
||||
return ret;
|
||||
|
||||
/* ERRORS */
|
||||
open_failed:
|
||||
{
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Failed to open");
|
||||
ret = FALSE;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
settings_changed (GstFFMpegAudDec * ffmpegdec, AVFrame * frame)
|
||||
{
|
||||
GstAudioFormat format;
|
||||
GstAudioLayout layout;
|
||||
gint channels = av_get_channel_layout_nb_channels (frame->channel_layout);
|
||||
|
||||
format = gst_ffmpeg_smpfmt_to_audioformat (frame->format, &layout);
|
||||
if (format == GST_AUDIO_FORMAT_UNKNOWN)
|
||||
return TRUE;
|
||||
|
||||
return !(ffmpegdec->info.rate == frame->sample_rate &&
|
||||
ffmpegdec->info.channels == channels &&
|
||||
ffmpegdec->info.finfo->format == format &&
|
||||
ffmpegdec->info.layout == layout);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_ffmpegauddec_negotiate (GstFFMpegAudDec * ffmpegdec,
|
||||
AVCodecContext * context, AVFrame * frame, gboolean force)
|
||||
{
|
||||
GstFFMpegAudDecClass *oclass;
|
||||
GstAudioFormat format;
|
||||
GstAudioLayout layout;
|
||||
gint channels;
|
||||
GstAudioChannelPosition pos[64] = { 0, };
|
||||
|
||||
oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
|
||||
|
||||
format = gst_ffmpeg_smpfmt_to_audioformat (frame->format, &layout);
|
||||
if (format == GST_AUDIO_FORMAT_UNKNOWN)
|
||||
goto no_caps;
|
||||
channels = av_get_channel_layout_nb_channels (frame->channel_layout);
|
||||
if (channels == 0)
|
||||
channels = frame->channels;
|
||||
if (channels == 0)
|
||||
goto no_caps;
|
||||
|
||||
if (!force && !settings_changed (ffmpegdec, frame))
|
||||
return TRUE;
|
||||
|
||||
GST_DEBUG_OBJECT (ffmpegdec,
|
||||
"Renegotiating audio from %dHz@%dchannels (%d, interleaved=%d) "
|
||||
"to %dHz@%dchannels (%d, interleaved=%d)",
|
||||
ffmpegdec->info.rate, ffmpegdec->info.channels,
|
||||
ffmpegdec->info.finfo->format,
|
||||
ffmpegdec->info.layout == GST_AUDIO_LAYOUT_INTERLEAVED,
|
||||
frame->sample_rate, channels, format,
|
||||
layout == GST_AUDIO_LAYOUT_INTERLEAVED);
|
||||
|
||||
gst_ffmpeg_channel_layout_to_gst (frame->channel_layout, channels, pos);
|
||||
memcpy (ffmpegdec->ffmpeg_layout, pos,
|
||||
sizeof (GstAudioChannelPosition) * channels);
|
||||
|
||||
/* Get GStreamer channel layout */
|
||||
gst_audio_channel_positions_to_valid_order (pos, channels);
|
||||
ffmpegdec->needs_reorder =
|
||||
memcmp (pos, ffmpegdec->ffmpeg_layout, sizeof (pos[0]) * channels) != 0;
|
||||
gst_audio_info_set_format (&ffmpegdec->info, format,
|
||||
frame->sample_rate, channels, pos);
|
||||
ffmpegdec->info.layout = layout;
|
||||
|
||||
if (!gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (ffmpegdec),
|
||||
&ffmpegdec->info))
|
||||
goto caps_failed;
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
no_caps:
|
||||
{
|
||||
#ifdef HAVE_LIBAV_UNINSTALLED
|
||||
/* using internal ffmpeg snapshot */
|
||||
GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION,
|
||||
("Could not find GStreamer caps mapping for libav codec '%s'.",
|
||||
oclass->in_plugin->name), (NULL));
|
||||
#else
|
||||
/* using external ffmpeg */
|
||||
GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION,
|
||||
("Could not find GStreamer caps mapping for libav codec '%s', and "
|
||||
"you are using an external libavcodec. This is most likely due to "
|
||||
"a packaging problem and/or libavcodec having been upgraded to a "
|
||||
"version that is not compatible with this version of "
|
||||
"gstreamer-libav. Make sure your gstreamer-libav and libavcodec "
|
||||
"packages come from the same source/repository.",
|
||||
oclass->in_plugin->name), (NULL));
|
||||
#endif
|
||||
return FALSE;
|
||||
}
|
||||
caps_failed:
|
||||
{
|
||||
GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, (NULL),
|
||||
("Could not set caps for libav decoder (%s), not fixed?",
|
||||
oclass->in_plugin->name));
|
||||
memset (&ffmpegdec->info, 0, sizeof (ffmpegdec->info));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_avpacket_init (AVPacket * packet, guint8 * data, guint size)
|
||||
{
|
||||
memset (packet, 0, sizeof (AVPacket));
|
||||
packet->data = data;
|
||||
packet->size = size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns: whether a frame was decoded
|
||||
*/
|
||||
static gboolean
|
||||
gst_ffmpegauddec_audio_frame (GstFFMpegAudDec * ffmpegdec,
|
||||
AVCodec * in_plugin, GstBuffer ** outbuf, GstFlowReturn * ret)
|
||||
{
|
||||
gboolean got_frame = FALSE;
|
||||
gint res;
|
||||
|
||||
res = avcodec_receive_frame (ffmpegdec->context, ffmpegdec->frame);
|
||||
|
||||
if (res >= 0) {
|
||||
gint nsamples, channels, byte_per_sample;
|
||||
gsize output_size;
|
||||
gboolean planar;
|
||||
|
||||
if (!gst_ffmpegauddec_negotiate (ffmpegdec, ffmpegdec->context,
|
||||
ffmpegdec->frame, FALSE)) {
|
||||
*outbuf = NULL;
|
||||
*ret = GST_FLOW_NOT_NEGOTIATED;
|
||||
goto beach;
|
||||
}
|
||||
|
||||
got_frame = TRUE;
|
||||
|
||||
channels = ffmpegdec->info.channels;
|
||||
nsamples = ffmpegdec->frame->nb_samples;
|
||||
byte_per_sample = ffmpegdec->info.finfo->width / 8;
|
||||
planar = av_sample_fmt_is_planar (ffmpegdec->frame->format);
|
||||
|
||||
g_return_val_if_fail (ffmpegdec->info.layout == (planar ?
|
||||
GST_AUDIO_LAYOUT_NON_INTERLEAVED : GST_AUDIO_LAYOUT_INTERLEAVED),
|
||||
GST_FLOW_NOT_NEGOTIATED);
|
||||
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Creating output buffer");
|
||||
|
||||
/* ffmpegdec->frame->linesize[0] might contain padding, allocate only what's needed */
|
||||
output_size = nsamples * byte_per_sample * channels;
|
||||
|
||||
*outbuf =
|
||||
gst_audio_decoder_allocate_output_buffer (GST_AUDIO_DECODER
|
||||
(ffmpegdec), output_size);
|
||||
|
||||
if (planar) {
|
||||
gint i;
|
||||
GstAudioMeta *meta;
|
||||
|
||||
meta = gst_buffer_add_audio_meta (*outbuf, &ffmpegdec->info, nsamples,
|
||||
NULL);
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
gst_buffer_fill (*outbuf, meta->offsets[i],
|
||||
ffmpegdec->frame->extended_data[i], nsamples * byte_per_sample);
|
||||
}
|
||||
} else {
|
||||
gst_buffer_fill (*outbuf, 0, ffmpegdec->frame->data[0], output_size);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Buffer created. Size: %" G_GSIZE_FORMAT,
|
||||
output_size);
|
||||
|
||||
/* Reorder channels to the GStreamer channel order */
|
||||
if (ffmpegdec->needs_reorder) {
|
||||
*outbuf = gst_buffer_make_writable (*outbuf);
|
||||
gst_audio_buffer_reorder_channels (*outbuf, ffmpegdec->info.finfo->format,
|
||||
ffmpegdec->info.channels, ffmpegdec->ffmpeg_layout,
|
||||
ffmpegdec->info.position);
|
||||
}
|
||||
|
||||
/* Mark corrupted frames as corrupted */
|
||||
if (ffmpegdec->frame->flags & AV_FRAME_FLAG_CORRUPT)
|
||||
GST_BUFFER_FLAG_SET (*outbuf, GST_BUFFER_FLAG_CORRUPTED);
|
||||
} else if (res == AVERROR (EAGAIN)) {
|
||||
*outbuf = NULL;
|
||||
} else if (res == AVERROR_EOF) {
|
||||
*ret = GST_FLOW_EOS;
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Context was entirely flushed");
|
||||
} else if (res < 0) {
|
||||
*ret = GST_FLOW_OK;
|
||||
GST_WARNING_OBJECT (ffmpegdec, "Legitimate decoding error");
|
||||
}
|
||||
|
||||
beach:
|
||||
av_frame_unref (ffmpegdec->frame);
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "return flow %d, out %p, got_frame %d",
|
||||
*ret, *outbuf, got_frame);
|
||||
return got_frame;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns: whether a frame was decoded
|
||||
*/
|
||||
static gboolean
|
||||
gst_ffmpegauddec_frame (GstFFMpegAudDec * ffmpegdec, GstFlowReturn * ret)
|
||||
{
|
||||
GstFFMpegAudDecClass *oclass;
|
||||
GstBuffer *outbuf = NULL;
|
||||
gboolean got_frame = FALSE;
|
||||
|
||||
if (G_UNLIKELY (ffmpegdec->context->codec == NULL))
|
||||
goto no_codec;
|
||||
|
||||
*ret = GST_FLOW_OK;
|
||||
ffmpegdec->context->frame_number++;
|
||||
|
||||
oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
|
||||
|
||||
got_frame =
|
||||
gst_ffmpegauddec_audio_frame (ffmpegdec, oclass->in_plugin, &outbuf, ret);
|
||||
|
||||
if (outbuf) {
|
||||
GST_LOG_OBJECT (ffmpegdec, "Decoded data, buffer %" GST_PTR_FORMAT, outbuf);
|
||||
*ret =
|
||||
gst_audio_decoder_finish_subframe (GST_AUDIO_DECODER_CAST (ffmpegdec),
|
||||
outbuf);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "We didn't get a decoded buffer");
|
||||
}
|
||||
|
||||
beach:
|
||||
return got_frame;
|
||||
|
||||
/* ERRORS */
|
||||
no_codec:
|
||||
{
|
||||
GST_ERROR_OBJECT (ffmpegdec, "no codec context");
|
||||
goto beach;
|
||||
}
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_ffmpegauddec_drain (GstFFMpegAudDec * ffmpegdec, gboolean force)
|
||||
{
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
gboolean got_any_frames = FALSE;
|
||||
gboolean got_frame;
|
||||
|
||||
if (avcodec_send_packet (ffmpegdec->context, NULL))
|
||||
goto send_packet_failed;
|
||||
|
||||
do {
|
||||
got_frame = gst_ffmpegauddec_frame (ffmpegdec, &ret);
|
||||
if (got_frame)
|
||||
got_any_frames = TRUE;
|
||||
} while (got_frame);
|
||||
avcodec_flush_buffers (ffmpegdec->context);
|
||||
|
||||
/* FFMpeg will return AVERROR_EOF if it's internal was fully drained
|
||||
* then we are translating it to GST_FLOW_EOS. However, because this behavior
|
||||
* is fully internal stuff of this implementation and gstaudiodecoder
|
||||
* baseclass doesn't convert this GST_FLOW_EOS to GST_FLOW_OK,
|
||||
* convert this flow returned here */
|
||||
if (ret == GST_FLOW_EOS)
|
||||
ret = GST_FLOW_OK;
|
||||
|
||||
if (got_any_frames || force) {
|
||||
GstFlowReturn new_ret =
|
||||
gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (ffmpegdec), NULL, 1);
|
||||
|
||||
if (ret == GST_FLOW_OK)
|
||||
ret = new_ret;
|
||||
}
|
||||
|
||||
done:
|
||||
return ret;
|
||||
|
||||
send_packet_failed:
|
||||
GST_WARNING_OBJECT (ffmpegdec, "send packet failed, could not drain decoder");
|
||||
goto done;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegauddec_flush (GstAudioDecoder * decoder, gboolean hard)
|
||||
{
|
||||
GstFFMpegAudDec *ffmpegdec = (GstFFMpegAudDec *) decoder;
|
||||
|
||||
if (ffmpegdec->opened) {
|
||||
avcodec_flush_buffers (ffmpegdec->context);
|
||||
}
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_ffmpegauddec_handle_frame (GstAudioDecoder * decoder, GstBuffer * inbuf)
|
||||
{
|
||||
GstFFMpegAudDec *ffmpegdec;
|
||||
GstFFMpegAudDecClass *oclass;
|
||||
guint8 *data;
|
||||
GstMapInfo map;
|
||||
gint size;
|
||||
gboolean got_any_frames = FALSE;
|
||||
gboolean got_frame;
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
gboolean is_header;
|
||||
AVPacket packet;
|
||||
|
||||
ffmpegdec = (GstFFMpegAudDec *) decoder;
|
||||
|
||||
if (G_UNLIKELY (!ffmpegdec->opened))
|
||||
goto not_negotiated;
|
||||
|
||||
if (inbuf == NULL) {
|
||||
return gst_ffmpegauddec_drain (ffmpegdec, FALSE);
|
||||
}
|
||||
|
||||
inbuf = gst_buffer_ref (inbuf);
|
||||
is_header = GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_HEADER);
|
||||
|
||||
oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
|
||||
|
||||
GST_LOG_OBJECT (ffmpegdec,
|
||||
"Received new data of size %" G_GSIZE_FORMAT ", offset:%" G_GUINT64_FORMAT
|
||||
", ts:%" GST_TIME_FORMAT ", dur:%" GST_TIME_FORMAT,
|
||||
gst_buffer_get_size (inbuf), GST_BUFFER_OFFSET (inbuf),
|
||||
GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)),
|
||||
GST_TIME_ARGS (GST_BUFFER_DURATION (inbuf)));
|
||||
|
||||
/* workarounds, functions write to buffers:
|
||||
* libavcodec/svq1.c:svq1_decode_frame writes to the given buffer.
|
||||
* libavcodec/svq3.c:svq3_decode_slice_header too.
|
||||
* ffmpeg devs know about it and will fix it (they said). */
|
||||
if (oclass->in_plugin->id == AV_CODEC_ID_SVQ1 ||
|
||||
oclass->in_plugin->id == AV_CODEC_ID_SVQ3) {
|
||||
inbuf = gst_buffer_make_writable (inbuf);
|
||||
}
|
||||
|
||||
gst_buffer_map (inbuf, &map, GST_MAP_READ);
|
||||
|
||||
data = map.data;
|
||||
size = map.size;
|
||||
|
||||
if (size > 0 && (!GST_MEMORY_IS_ZERO_PADDED (map.memory)
|
||||
|| (map.maxsize - map.size) < AV_INPUT_BUFFER_PADDING_SIZE)) {
|
||||
/* add padding */
|
||||
if (ffmpegdec->padded_size < size + AV_INPUT_BUFFER_PADDING_SIZE) {
|
||||
ffmpegdec->padded_size = size + AV_INPUT_BUFFER_PADDING_SIZE;
|
||||
ffmpegdec->padded = g_realloc (ffmpegdec->padded, ffmpegdec->padded_size);
|
||||
GST_LOG_OBJECT (ffmpegdec, "resized padding buffer to %d",
|
||||
ffmpegdec->padded_size);
|
||||
}
|
||||
GST_CAT_TRACE_OBJECT (GST_CAT_PERFORMANCE, ffmpegdec,
|
||||
"Copy input to add padding");
|
||||
memcpy (ffmpegdec->padded, data, size);
|
||||
memset (ffmpegdec->padded + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
|
||||
|
||||
data = ffmpegdec->padded;
|
||||
}
|
||||
|
||||
gst_avpacket_init (&packet, data, size);
|
||||
|
||||
if (!packet.size)
|
||||
goto unmap;
|
||||
|
||||
if (avcodec_send_packet (ffmpegdec->context, &packet) < 0) {
|
||||
goto send_packet_failed;
|
||||
}
|
||||
|
||||
do {
|
||||
/* decode a frame of audio now */
|
||||
got_frame = gst_ffmpegauddec_frame (ffmpegdec, &ret);
|
||||
|
||||
if (got_frame)
|
||||
got_any_frames = TRUE;
|
||||
|
||||
if (ret != GST_FLOW_OK) {
|
||||
GST_LOG_OBJECT (ffmpegdec, "breaking because of flow ret %s",
|
||||
gst_flow_get_name (ret));
|
||||
/* bad flow return, make sure we discard all data and exit */
|
||||
break;
|
||||
}
|
||||
} while (got_frame);
|
||||
|
||||
if (is_header || got_any_frames) {
|
||||
/* Even if previous return wasn't GST_FLOW_OK, we need to call
|
||||
* _finish_frame() since baseclass is expecting that _finish_frame()
|
||||
* is followed by _finish_subframe()
|
||||
*/
|
||||
GstFlowReturn new_ret =
|
||||
gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (ffmpegdec), NULL, 1);
|
||||
|
||||
/* Only override the flow return value if previously did have a GST_FLOW_OK.
|
||||
* Failure to do this would result in skipping downstream issues caught in
|
||||
* earlier steps. */
|
||||
if (ret == GST_FLOW_OK)
|
||||
ret = new_ret;
|
||||
}
|
||||
|
||||
unmap:
|
||||
gst_buffer_unmap (inbuf, &map);
|
||||
gst_buffer_unref (inbuf);
|
||||
|
||||
done:
|
||||
return ret;
|
||||
|
||||
/* ERRORS */
|
||||
not_negotiated:
|
||||
{
|
||||
oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
|
||||
GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, (NULL),
|
||||
("avdec_%s: input format was not set before data start",
|
||||
oclass->in_plugin->name));
|
||||
ret = GST_FLOW_NOT_NEGOTIATED;
|
||||
goto done;
|
||||
}
|
||||
|
||||
send_packet_failed:
|
||||
{
|
||||
GST_WARNING_OBJECT (ffmpegdec, "decoding error");
|
||||
/* Even if ffmpeg was not able to decode current audio frame,
|
||||
* we should call gst_audio_decoder_finish_frame() so that baseclass
|
||||
* can clear its internal status and can respect timestamp of later
|
||||
* incoming buffers */
|
||||
ret = gst_ffmpegauddec_drain (ffmpegdec, TRUE);
|
||||
goto unmap;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_ffmpegauddec_register (GstPlugin * plugin)
|
||||
{
|
||||
GTypeInfo typeinfo = {
|
||||
sizeof (GstFFMpegAudDecClass),
|
||||
(GBaseInitFunc) gst_ffmpegauddec_base_init,
|
||||
NULL,
|
||||
(GClassInitFunc) gst_ffmpegauddec_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof (GstFFMpegAudDec),
|
||||
0,
|
||||
(GInstanceInitFunc) gst_ffmpegauddec_init,
|
||||
};
|
||||
GType type;
|
||||
AVCodec *in_plugin;
|
||||
void *i = 0;
|
||||
gint rank;
|
||||
|
||||
GST_LOG ("Registering decoders");
|
||||
|
||||
while ((in_plugin = (AVCodec *) av_codec_iterate (&i))) {
|
||||
gchar *type_name;
|
||||
|
||||
/* only decoders */
|
||||
if (!av_codec_is_decoder (in_plugin)
|
||||
|| in_plugin->type != AVMEDIA_TYPE_AUDIO) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* no quasi codecs, please */
|
||||
if (in_plugin->id == AV_CODEC_ID_PCM_S16LE_PLANAR ||
|
||||
(in_plugin->id >= AV_CODEC_ID_PCM_S16LE &&
|
||||
in_plugin->id <= AV_CODEC_ID_PCM_BLURAY) ||
|
||||
(in_plugin->id >= AV_CODEC_ID_PCM_S8_PLANAR &&
|
||||
in_plugin->id <= AV_CODEC_ID_PCM_F24LE))
|
||||
continue;
|
||||
|
||||
/* No decoders depending on external libraries (we don't build them, but
|
||||
* people who build against an external ffmpeg might have them.
|
||||
* We have native gstreamer plugins for all of those libraries anyway. */
|
||||
if (!strncmp (in_plugin->name, "lib", 3)) {
|
||||
GST_DEBUG
|
||||
("Not using external library decoder %s. Use the gstreamer-native ones instead.",
|
||||
in_plugin->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
GST_DEBUG ("Trying plugin %s [%s]", in_plugin->name, in_plugin->long_name);
|
||||
|
||||
/* no codecs for which we're GUARANTEED to have better alternatives */
|
||||
/* MP1 : Use MP3 for decoding */
|
||||
/* MP2 : Use MP3 for decoding */
|
||||
/* Theora: Use libtheora based theoradec */
|
||||
if (!strcmp (in_plugin->name, "vorbis") ||
|
||||
!strcmp (in_plugin->name, "wavpack") ||
|
||||
!strcmp (in_plugin->name, "mp1") ||
|
||||
!strcmp (in_plugin->name, "mp2") ||
|
||||
!strcmp (in_plugin->name, "libfaad") ||
|
||||
!strcmp (in_plugin->name, "mpeg4aac") ||
|
||||
!strcmp (in_plugin->name, "ass") ||
|
||||
!strcmp (in_plugin->name, "srt") ||
|
||||
!strcmp (in_plugin->name, "pgssub") ||
|
||||
!strcmp (in_plugin->name, "dvdsub") ||
|
||||
!strcmp (in_plugin->name, "dvbsub")) {
|
||||
GST_LOG ("Ignoring decoder %s", in_plugin->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* construct the type */
|
||||
type_name = g_strdup_printf ("avdec_%s", in_plugin->name);
|
||||
g_strdelimit (type_name, ".,|-<> ", '_');
|
||||
|
||||
type = g_type_from_name (type_name);
|
||||
|
||||
if (!type) {
|
||||
/* create the gtype now */
|
||||
type =
|
||||
g_type_register_static (GST_TYPE_AUDIO_DECODER, type_name, &typeinfo,
|
||||
0);
|
||||
g_type_set_qdata (type, GST_FFDEC_PARAMS_QDATA, (gpointer) in_plugin);
|
||||
}
|
||||
|
||||
/* (Ronald) MPEG-4 gets a higher priority because it has been well-
|
||||
* tested and by far outperforms divxdec/xviddec - so we prefer it.
|
||||
* msmpeg4v3 same, as it outperforms divxdec for divx3 playback.
|
||||
* VC1/WMV3 are not working and thus unpreferred for now. */
|
||||
switch (in_plugin->id) {
|
||||
case AV_CODEC_ID_RA_144:
|
||||
case AV_CODEC_ID_RA_288:
|
||||
case AV_CODEC_ID_COOK:
|
||||
case AV_CODEC_ID_AAC:
|
||||
rank = GST_RANK_PRIMARY;
|
||||
break;
|
||||
/* SIPR: decoder should have a higher rank than realaudiodec.
|
||||
*/
|
||||
case AV_CODEC_ID_SIPR:
|
||||
rank = GST_RANK_SECONDARY;
|
||||
break;
|
||||
default:
|
||||
rank = GST_RANK_MARGINAL;
|
||||
break;
|
||||
}
|
||||
if (!gst_element_register (plugin, type_name, rank, type)) {
|
||||
g_warning ("Failed to register %s", type_name);
|
||||
g_free (type_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_free (type_name);
|
||||
}
|
||||
|
||||
GST_LOG ("Finished Registering decoders");
|
||||
|
||||
return TRUE;
|
||||
}
|
76
ext/libav/gstavauddec.h
Normal file
76
ext/libav/gstavauddec.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_FFMPEGAUDDEC_H__
|
||||
#define __GST_FFMPEGAUDDEC_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/audio.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
typedef struct _GstFFMpegAudDec GstFFMpegAudDec;
|
||||
struct _GstFFMpegAudDec
|
||||
{
|
||||
GstAudioDecoder parent;
|
||||
|
||||
/* decoding */
|
||||
AVCodecContext *context;
|
||||
gboolean opened;
|
||||
|
||||
AVFrame *frame;
|
||||
|
||||
guint8 *padded;
|
||||
gint padded_size;
|
||||
|
||||
/* prevent reopening the decoder on GST_EVENT_CAPS when caps are same as last time. */
|
||||
GstCaps *last_caps;
|
||||
|
||||
/* current output format */
|
||||
GstAudioInfo info;
|
||||
GstAudioChannelPosition ffmpeg_layout[64];
|
||||
gboolean needs_reorder;
|
||||
};
|
||||
|
||||
typedef struct _GstFFMpegAudDecClass GstFFMpegAudDecClass;
|
||||
|
||||
struct _GstFFMpegAudDecClass
|
||||
{
|
||||
GstAudioDecoderClass parent_class;
|
||||
|
||||
AVCodec *in_plugin;
|
||||
GstPadTemplate *srctempl, *sinktempl;
|
||||
};
|
||||
|
||||
#define GST_TYPE_FFMPEGDEC \
|
||||
(gst_ffmpegauddec_get_type())
|
||||
#define GST_FFMPEGDEC(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGDEC,GstFFMpegAudDec))
|
||||
#define GST_FFMPEGAUDDEC_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGDEC,GstFFMpegAudDecClass))
|
||||
#define GST_IS_FFMPEGDEC(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGDEC))
|
||||
#define GST_IS_FFMPEGAUDDEC_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGDEC))
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
857
ext/libav/gstavaudenc.c
Normal file
857
ext/libav/gstavaudenc.c
Normal file
|
@ -0,0 +1,857 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||
* Copyright (C) <2012> Collabora Ltd.
|
||||
* Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||
*
|
||||
* 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 <assert.h>
|
||||
#include <string.h>
|
||||
/* for stats file handling */
|
||||
#include <stdio.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/opt.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "gstav.h"
|
||||
#include "gstavcfg.h"
|
||||
#include "gstavcodecmap.h"
|
||||
#include "gstavutils.h"
|
||||
#include "gstavaudenc.h"
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_CFG_BASE,
|
||||
};
|
||||
|
||||
static void gst_ffmpegaudenc_class_init (GstFFMpegAudEncClass * klass);
|
||||
static void gst_ffmpegaudenc_base_init (GstFFMpegAudEncClass * klass);
|
||||
static void gst_ffmpegaudenc_init (GstFFMpegAudEnc * ffmpegaudenc);
|
||||
static void gst_ffmpegaudenc_finalize (GObject * object);
|
||||
|
||||
static gboolean gst_ffmpegaudenc_set_format (GstAudioEncoder * encoder,
|
||||
GstAudioInfo * info);
|
||||
static GstFlowReturn gst_ffmpegaudenc_handle_frame (GstAudioEncoder * encoder,
|
||||
GstBuffer * inbuf);
|
||||
static gboolean gst_ffmpegaudenc_start (GstAudioEncoder * encoder);
|
||||
static gboolean gst_ffmpegaudenc_stop (GstAudioEncoder * encoder);
|
||||
static void gst_ffmpegaudenc_flush (GstAudioEncoder * encoder);
|
||||
|
||||
static void gst_ffmpegaudenc_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec);
|
||||
static void gst_ffmpegaudenc_get_property (GObject * object,
|
||||
guint prop_id, GValue * value, GParamSpec * pspec);
|
||||
|
||||
#define GST_FFENC_PARAMS_QDATA g_quark_from_static_string("avenc-params")
|
||||
|
||||
static GstElementClass *parent_class = NULL;
|
||||
|
||||
static void
|
||||
gst_ffmpegaudenc_base_init (GstFFMpegAudEncClass * klass)
|
||||
{
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||
AVCodec *in_plugin;
|
||||
GstPadTemplate *srctempl = NULL, *sinktempl = NULL;
|
||||
GstCaps *srccaps = NULL, *sinkcaps = NULL;
|
||||
gchar *longname, *description;
|
||||
|
||||
in_plugin =
|
||||
(AVCodec *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
|
||||
GST_FFENC_PARAMS_QDATA);
|
||||
g_assert (in_plugin != NULL);
|
||||
|
||||
/* construct the element details struct */
|
||||
longname = g_strdup_printf ("libav %s encoder", in_plugin->long_name);
|
||||
description = g_strdup_printf ("libav %s encoder", in_plugin->name);
|
||||
gst_element_class_set_metadata (element_class, longname,
|
||||
"Codec/Encoder/Audio", description,
|
||||
"Wim Taymans <wim.taymans@gmail.com>, "
|
||||
"Ronald Bultje <rbultje@ronald.bitfreak.net>");
|
||||
g_free (longname);
|
||||
g_free (description);
|
||||
|
||||
if (!(srccaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, TRUE))) {
|
||||
GST_DEBUG ("Couldn't get source caps for encoder '%s'", in_plugin->name);
|
||||
srccaps = gst_caps_new_empty_simple ("unknown/unknown");
|
||||
}
|
||||
|
||||
sinkcaps = gst_ffmpeg_codectype_to_audio_caps (NULL,
|
||||
in_plugin->id, TRUE, in_plugin);
|
||||
if (!sinkcaps) {
|
||||
GST_DEBUG ("Couldn't get sink caps for encoder '%s'", in_plugin->name);
|
||||
sinkcaps = gst_caps_new_empty_simple ("unknown/unknown");
|
||||
}
|
||||
|
||||
/* pad templates */
|
||||
sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS, sinkcaps);
|
||||
srctempl = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, srccaps);
|
||||
|
||||
gst_element_class_add_pad_template (element_class, srctempl);
|
||||
gst_element_class_add_pad_template (element_class, sinktempl);
|
||||
|
||||
gst_caps_unref (sinkcaps);
|
||||
gst_caps_unref (srccaps);
|
||||
|
||||
klass->in_plugin = in_plugin;
|
||||
klass->srctempl = srctempl;
|
||||
klass->sinktempl = sinktempl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegaudenc_class_init (GstFFMpegAudEncClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstAudioEncoderClass *gstaudioencoder_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
gstaudioencoder_class = (GstAudioEncoderClass *) klass;
|
||||
|
||||
parent_class = g_type_class_peek_parent (klass);
|
||||
|
||||
gobject_class->set_property = gst_ffmpegaudenc_set_property;
|
||||
gobject_class->get_property = gst_ffmpegaudenc_get_property;
|
||||
|
||||
gst_ffmpeg_cfg_install_properties (gobject_class, klass->in_plugin,
|
||||
PROP_CFG_BASE, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM);
|
||||
|
||||
gobject_class->finalize = gst_ffmpegaudenc_finalize;
|
||||
|
||||
gstaudioencoder_class->start = GST_DEBUG_FUNCPTR (gst_ffmpegaudenc_start);
|
||||
gstaudioencoder_class->stop = GST_DEBUG_FUNCPTR (gst_ffmpegaudenc_stop);
|
||||
gstaudioencoder_class->flush = GST_DEBUG_FUNCPTR (gst_ffmpegaudenc_flush);
|
||||
gstaudioencoder_class->set_format =
|
||||
GST_DEBUG_FUNCPTR (gst_ffmpegaudenc_set_format);
|
||||
gstaudioencoder_class->handle_frame =
|
||||
GST_DEBUG_FUNCPTR (gst_ffmpegaudenc_handle_frame);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegaudenc_init (GstFFMpegAudEnc * ffmpegaudenc)
|
||||
{
|
||||
GstFFMpegAudEncClass *klass =
|
||||
(GstFFMpegAudEncClass *) G_OBJECT_GET_CLASS (ffmpegaudenc);
|
||||
|
||||
GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_ENCODER_SINK_PAD (ffmpegaudenc));
|
||||
|
||||
/* ffmpeg objects */
|
||||
ffmpegaudenc->context = avcodec_alloc_context3 (klass->in_plugin);
|
||||
ffmpegaudenc->refcontext = avcodec_alloc_context3 (klass->in_plugin);
|
||||
ffmpegaudenc->opened = FALSE;
|
||||
ffmpegaudenc->frame = av_frame_alloc ();
|
||||
|
||||
gst_audio_encoder_set_drainable (GST_AUDIO_ENCODER (ffmpegaudenc), TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegaudenc_finalize (GObject * object)
|
||||
{
|
||||
GstFFMpegAudEnc *ffmpegaudenc = (GstFFMpegAudEnc *) object;
|
||||
|
||||
/* clean up remaining allocated data */
|
||||
av_frame_free (&ffmpegaudenc->frame);
|
||||
gst_ffmpeg_avcodec_close (ffmpegaudenc->context);
|
||||
gst_ffmpeg_avcodec_close (ffmpegaudenc->refcontext);
|
||||
av_free (ffmpegaudenc->context);
|
||||
av_free (ffmpegaudenc->refcontext);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_ffmpegaudenc_start (GstAudioEncoder * encoder)
|
||||
{
|
||||
GstFFMpegAudEnc *ffmpegaudenc = (GstFFMpegAudEnc *) encoder;
|
||||
GstFFMpegAudEncClass *oclass =
|
||||
(GstFFMpegAudEncClass *) G_OBJECT_GET_CLASS (ffmpegaudenc);
|
||||
|
||||
ffmpegaudenc->opened = FALSE;
|
||||
ffmpegaudenc->need_reopen = FALSE;
|
||||
|
||||
gst_ffmpeg_avcodec_close (ffmpegaudenc->context);
|
||||
if (avcodec_get_context_defaults3 (ffmpegaudenc->context,
|
||||
oclass->in_plugin) < 0) {
|
||||
GST_DEBUG_OBJECT (ffmpegaudenc, "Failed to set context defaults");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_ffmpegaudenc_stop (GstAudioEncoder * encoder)
|
||||
{
|
||||
GstFFMpegAudEnc *ffmpegaudenc = (GstFFMpegAudEnc *) encoder;
|
||||
|
||||
/* close old session */
|
||||
gst_ffmpeg_avcodec_close (ffmpegaudenc->context);
|
||||
ffmpegaudenc->opened = FALSE;
|
||||
ffmpegaudenc->need_reopen = FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegaudenc_flush (GstAudioEncoder * encoder)
|
||||
{
|
||||
GstFFMpegAudEnc *ffmpegaudenc = (GstFFMpegAudEnc *) encoder;
|
||||
|
||||
if (ffmpegaudenc->opened) {
|
||||
avcodec_flush_buffers (ffmpegaudenc->context);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_ffmpegaudenc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info)
|
||||
{
|
||||
GstFFMpegAudEnc *ffmpegaudenc = (GstFFMpegAudEnc *) encoder;
|
||||
GstCaps *other_caps;
|
||||
GstCaps *allowed_caps;
|
||||
GstCaps *icaps;
|
||||
gsize frame_size;
|
||||
GstFFMpegAudEncClass *oclass =
|
||||
(GstFFMpegAudEncClass *) G_OBJECT_GET_CLASS (ffmpegaudenc);
|
||||
|
||||
ffmpegaudenc->need_reopen = FALSE;
|
||||
|
||||
/* close old session */
|
||||
if (ffmpegaudenc->opened) {
|
||||
gst_ffmpeg_avcodec_close (ffmpegaudenc->context);
|
||||
ffmpegaudenc->opened = FALSE;
|
||||
if (avcodec_get_context_defaults3 (ffmpegaudenc->context,
|
||||
oclass->in_plugin) < 0) {
|
||||
GST_DEBUG_OBJECT (ffmpegaudenc, "Failed to set context defaults");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
gst_ffmpeg_cfg_fill_context (G_OBJECT (ffmpegaudenc), ffmpegaudenc->context);
|
||||
|
||||
/* fetch pix_fmt and so on */
|
||||
gst_ffmpeg_audioinfo_to_context (info, ffmpegaudenc->context);
|
||||
if (!ffmpegaudenc->context->time_base.den) {
|
||||
ffmpegaudenc->context->time_base.den = GST_AUDIO_INFO_RATE (info);
|
||||
ffmpegaudenc->context->time_base.num = 1;
|
||||
ffmpegaudenc->context->ticks_per_frame = 1;
|
||||
}
|
||||
|
||||
if (ffmpegaudenc->context->channel_layout) {
|
||||
gst_ffmpeg_channel_layout_to_gst (ffmpegaudenc->context->channel_layout,
|
||||
ffmpegaudenc->context->channels, ffmpegaudenc->ffmpeg_layout);
|
||||
ffmpegaudenc->needs_reorder =
|
||||
(memcmp (ffmpegaudenc->ffmpeg_layout, info->position,
|
||||
sizeof (GstAudioChannelPosition) *
|
||||
ffmpegaudenc->context->channels) != 0);
|
||||
}
|
||||
|
||||
/* some codecs support more than one format, first auto-choose one */
|
||||
GST_DEBUG_OBJECT (ffmpegaudenc, "picking an output format ...");
|
||||
allowed_caps = gst_pad_get_allowed_caps (GST_AUDIO_ENCODER_SRC_PAD (encoder));
|
||||
if (!allowed_caps) {
|
||||
GST_DEBUG_OBJECT (ffmpegaudenc, "... but no peer, using template caps");
|
||||
/* we need to copy because get_allowed_caps returns a ref, and
|
||||
* get_pad_template_caps doesn't */
|
||||
allowed_caps =
|
||||
gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SRC_PAD (encoder));
|
||||
}
|
||||
GST_DEBUG_OBJECT (ffmpegaudenc, "chose caps %" GST_PTR_FORMAT, allowed_caps);
|
||||
gst_ffmpeg_caps_with_codecid (oclass->in_plugin->id,
|
||||
oclass->in_plugin->type, allowed_caps, ffmpegaudenc->context);
|
||||
|
||||
/* open codec */
|
||||
if (gst_ffmpeg_avcodec_open (ffmpegaudenc->context, oclass->in_plugin) < 0) {
|
||||
gst_caps_unref (allowed_caps);
|
||||
gst_ffmpeg_avcodec_close (ffmpegaudenc->context);
|
||||
GST_DEBUG_OBJECT (ffmpegaudenc, "avenc_%s: Failed to open FFMPEG codec",
|
||||
oclass->in_plugin->name);
|
||||
if (avcodec_get_context_defaults3 (ffmpegaudenc->context,
|
||||
oclass->in_plugin) < 0)
|
||||
GST_DEBUG_OBJECT (ffmpegaudenc, "Failed to set context defaults");
|
||||
|
||||
if ((oclass->in_plugin->capabilities & AV_CODEC_CAP_EXPERIMENTAL) &&
|
||||
ffmpegaudenc->context->strict_std_compliance !=
|
||||
FF_COMPLIANCE_EXPERIMENTAL) {
|
||||
GST_ELEMENT_ERROR (ffmpegaudenc, LIBRARY, SETTINGS,
|
||||
("Codec is experimental, but settings don't allow encoders to "
|
||||
"produce output of experimental quality"),
|
||||
("This codec may not create output that is conformant to the specs "
|
||||
"or of good quality. If you must use it anyway, set the "
|
||||
"compliance property to experimental"));
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* try to set this caps on the other side */
|
||||
other_caps = gst_ffmpeg_codecid_to_caps (oclass->in_plugin->id,
|
||||
ffmpegaudenc->context, TRUE);
|
||||
|
||||
if (!other_caps) {
|
||||
gst_caps_unref (allowed_caps);
|
||||
gst_ffmpeg_avcodec_close (ffmpegaudenc->context);
|
||||
GST_DEBUG ("Unsupported codec - no caps found");
|
||||
if (avcodec_get_context_defaults3 (ffmpegaudenc->context,
|
||||
oclass->in_plugin) < 0)
|
||||
GST_DEBUG_OBJECT (ffmpegaudenc, "Failed to set context defaults");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
icaps = gst_caps_intersect (allowed_caps, other_caps);
|
||||
gst_caps_unref (allowed_caps);
|
||||
gst_caps_unref (other_caps);
|
||||
if (gst_caps_is_empty (icaps)) {
|
||||
gst_caps_unref (icaps);
|
||||
return FALSE;
|
||||
}
|
||||
icaps = gst_caps_fixate (icaps);
|
||||
|
||||
if (!gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (ffmpegaudenc),
|
||||
icaps)) {
|
||||
gst_ffmpeg_avcodec_close (ffmpegaudenc->context);
|
||||
gst_caps_unref (icaps);
|
||||
if (avcodec_get_context_defaults3 (ffmpegaudenc->context,
|
||||
oclass->in_plugin) < 0)
|
||||
GST_DEBUG_OBJECT (ffmpegaudenc, "Failed to set context defaults");
|
||||
return FALSE;
|
||||
}
|
||||
gst_caps_unref (icaps);
|
||||
|
||||
frame_size = ffmpegaudenc->context->frame_size;
|
||||
if (frame_size > 1) {
|
||||
gst_audio_encoder_set_frame_samples_min (GST_AUDIO_ENCODER (ffmpegaudenc),
|
||||
frame_size);
|
||||
gst_audio_encoder_set_frame_samples_max (GST_AUDIO_ENCODER (ffmpegaudenc),
|
||||
frame_size);
|
||||
gst_audio_encoder_set_frame_max (GST_AUDIO_ENCODER (ffmpegaudenc), 1);
|
||||
} else {
|
||||
gst_audio_encoder_set_frame_samples_min (GST_AUDIO_ENCODER (ffmpegaudenc),
|
||||
0);
|
||||
gst_audio_encoder_set_frame_samples_max (GST_AUDIO_ENCODER (ffmpegaudenc),
|
||||
0);
|
||||
gst_audio_encoder_set_frame_max (GST_AUDIO_ENCODER (ffmpegaudenc), 0);
|
||||
}
|
||||
|
||||
/* Store some tags */
|
||||
{
|
||||
GstTagList *tags = gst_tag_list_new_empty ();
|
||||
const gchar *codec;
|
||||
|
||||
gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_NOMINAL_BITRATE,
|
||||
(guint) ffmpegaudenc->context->bit_rate, NULL);
|
||||
|
||||
if ((codec =
|
||||
gst_ffmpeg_get_codecid_longname (ffmpegaudenc->context->codec_id)))
|
||||
gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_AUDIO_CODEC, codec,
|
||||
NULL);
|
||||
|
||||
gst_audio_encoder_merge_tags (encoder, tags, GST_TAG_MERGE_REPLACE);
|
||||
gst_tag_list_unref (tags);
|
||||
}
|
||||
|
||||
/* success! */
|
||||
ffmpegaudenc->opened = TRUE;
|
||||
ffmpegaudenc->need_reopen = FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegaudenc_free_avpacket (gpointer pkt)
|
||||
{
|
||||
av_packet_unref ((AVPacket *) pkt);
|
||||
g_slice_free (AVPacket, pkt);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstBuffer *buffer;
|
||||
GstMapInfo map;
|
||||
|
||||
guint8 **ext_data_array, *ext_data;
|
||||
} BufferInfo;
|
||||
|
||||
static void
|
||||
buffer_info_free (void *opaque, guint8 * data)
|
||||
{
|
||||
BufferInfo *info = opaque;
|
||||
|
||||
if (info->buffer) {
|
||||
gst_buffer_unmap (info->buffer, &info->map);
|
||||
gst_buffer_unref (info->buffer);
|
||||
} else {
|
||||
av_free (info->ext_data);
|
||||
av_free (info->ext_data_array);
|
||||
}
|
||||
g_slice_free (BufferInfo, info);
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_ffmpegaudenc_send_frame (GstFFMpegAudEnc * ffmpegaudenc, GstBuffer * buffer)
|
||||
{
|
||||
GstAudioEncoder *enc;
|
||||
AVCodecContext *ctx;
|
||||
GstFlowReturn ret;
|
||||
gint res;
|
||||
GstAudioInfo *info;
|
||||
AVFrame *frame = ffmpegaudenc->frame;
|
||||
gboolean planar;
|
||||
gint nsamples = -1;
|
||||
|
||||
enc = GST_AUDIO_ENCODER (ffmpegaudenc);
|
||||
|
||||
ctx = ffmpegaudenc->context;
|
||||
|
||||
if (buffer != NULL) {
|
||||
BufferInfo *buffer_info = g_slice_new0 (BufferInfo);
|
||||
guint8 *audio_in;
|
||||
guint in_size;
|
||||
|
||||
buffer_info->buffer = buffer;
|
||||
gst_buffer_map (buffer, &buffer_info->map, GST_MAP_READ);
|
||||
audio_in = buffer_info->map.data;
|
||||
in_size = buffer_info->map.size;
|
||||
|
||||
GST_LOG_OBJECT (ffmpegaudenc, "encoding buffer %p size:%u", audio_in,
|
||||
in_size);
|
||||
|
||||
info = gst_audio_encoder_get_audio_info (enc);
|
||||
planar = av_sample_fmt_is_planar (ffmpegaudenc->context->sample_fmt);
|
||||
frame->format = ffmpegaudenc->context->sample_fmt;
|
||||
frame->sample_rate = ffmpegaudenc->context->sample_rate;
|
||||
frame->channels = ffmpegaudenc->context->channels;
|
||||
frame->channel_layout = ffmpegaudenc->context->channel_layout;
|
||||
|
||||
if (planar && info->channels > 1) {
|
||||
gint channels;
|
||||
gint i, j;
|
||||
|
||||
nsamples = frame->nb_samples = in_size / info->bpf;
|
||||
channels = info->channels;
|
||||
|
||||
frame->buf[0] =
|
||||
av_buffer_create (NULL, 0, buffer_info_free, buffer_info, 0);
|
||||
|
||||
if (info->channels > AV_NUM_DATA_POINTERS) {
|
||||
buffer_info->ext_data_array = frame->extended_data =
|
||||
av_malloc_array (info->channels, sizeof (uint8_t *));
|
||||
} else {
|
||||
frame->extended_data = frame->data;
|
||||
}
|
||||
|
||||
buffer_info->ext_data = frame->extended_data[0] = av_malloc (in_size);
|
||||
frame->linesize[0] = in_size / channels;
|
||||
for (i = 1; i < channels; i++)
|
||||
frame->extended_data[i] =
|
||||
frame->extended_data[i - 1] + frame->linesize[0];
|
||||
|
||||
switch (info->finfo->width) {
|
||||
case 8:{
|
||||
const guint8 *idata = (const guint8 *) audio_in;
|
||||
|
||||
for (i = 0; i < nsamples; i++) {
|
||||
for (j = 0; j < channels; j++) {
|
||||
((guint8 *) frame->extended_data[j])[i] = idata[j];
|
||||
}
|
||||
idata += channels;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 16:{
|
||||
const guint16 *idata = (const guint16 *) audio_in;
|
||||
|
||||
for (i = 0; i < nsamples; i++) {
|
||||
for (j = 0; j < channels; j++) {
|
||||
((guint16 *) frame->extended_data[j])[i] = idata[j];
|
||||
}
|
||||
idata += channels;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 32:{
|
||||
const guint32 *idata = (const guint32 *) audio_in;
|
||||
|
||||
for (i = 0; i < nsamples; i++) {
|
||||
for (j = 0; j < channels; j++) {
|
||||
((guint32 *) frame->extended_data[j])[i] = idata[j];
|
||||
}
|
||||
idata += channels;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 64:{
|
||||
const guint64 *idata = (const guint64 *) audio_in;
|
||||
|
||||
for (i = 0; i < nsamples; i++) {
|
||||
for (j = 0; j < channels; j++) {
|
||||
((guint64 *) frame->extended_data[j])[i] = idata[j];
|
||||
}
|
||||
idata += channels;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
gst_buffer_unmap (buffer, &buffer_info->map);
|
||||
gst_buffer_unref (buffer);
|
||||
buffer_info->buffer = NULL;
|
||||
} else {
|
||||
frame->data[0] = audio_in;
|
||||
frame->extended_data = frame->data;
|
||||
frame->linesize[0] = in_size;
|
||||
frame->nb_samples = nsamples = in_size / info->bpf;
|
||||
frame->buf[0] =
|
||||
av_buffer_create (NULL, 0, buffer_info_free, buffer_info, 0);
|
||||
}
|
||||
|
||||
/* we have a frame to feed the encoder */
|
||||
res = avcodec_send_frame (ctx, frame);
|
||||
|
||||
av_frame_unref (frame);
|
||||
} else {
|
||||
GstFFMpegAudEncClass *oclass =
|
||||
(GstFFMpegAudEncClass *) G_OBJECT_GET_CLASS (ffmpegaudenc);
|
||||
|
||||
GST_LOG_OBJECT (ffmpegaudenc, "draining");
|
||||
/* flushing the encoder */
|
||||
res = avcodec_send_frame (ctx, NULL);
|
||||
|
||||
/* If AV_CODEC_CAP_ENCODER_FLUSH wasn't set, we need to re-open
|
||||
* encoder */
|
||||
if (!(oclass->in_plugin->capabilities & AV_CODEC_CAP_ENCODER_FLUSH)) {
|
||||
GST_DEBUG_OBJECT (ffmpegaudenc, "Encoder needs reopen later");
|
||||
|
||||
/* we will reopen later handle_frame() */
|
||||
ffmpegaudenc->need_reopen = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
ret = GST_FLOW_OK;
|
||||
} else if (res == AVERROR_EOF) {
|
||||
ret = GST_FLOW_EOS;
|
||||
} else { /* Any other return value is an error in our context */
|
||||
ret = GST_FLOW_OK;
|
||||
GST_WARNING_OBJECT (ffmpegaudenc, "Failed to encode buffer");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_ffmpegaudenc_receive_packet (GstFFMpegAudEnc * ffmpegaudenc,
|
||||
gboolean * got_packet)
|
||||
{
|
||||
GstAudioEncoder *enc;
|
||||
AVCodecContext *ctx;
|
||||
gint res;
|
||||
GstFlowReturn ret;
|
||||
AVPacket *pkt;
|
||||
|
||||
enc = GST_AUDIO_ENCODER (ffmpegaudenc);
|
||||
|
||||
ctx = ffmpegaudenc->context;
|
||||
|
||||
pkt = g_slice_new0 (AVPacket);
|
||||
|
||||
res = avcodec_receive_packet (ctx, pkt);
|
||||
|
||||
if (res == 0) {
|
||||
GstBuffer *outbuf;
|
||||
|
||||
GST_LOG_OBJECT (ffmpegaudenc, "pushing size %d", pkt->size);
|
||||
|
||||
outbuf =
|
||||
gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, pkt->data,
|
||||
pkt->size, 0, pkt->size, pkt, gst_ffmpegaudenc_free_avpacket);
|
||||
|
||||
ret =
|
||||
gst_audio_encoder_finish_frame (enc, outbuf,
|
||||
pkt->duration > 0 ? pkt->duration : -1);
|
||||
*got_packet = TRUE;
|
||||
} else {
|
||||
GST_LOG_OBJECT (ffmpegaudenc, "no output produced");
|
||||
g_slice_free (AVPacket, pkt);
|
||||
ret = GST_FLOW_OK;
|
||||
*got_packet = FALSE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_ffmpegaudenc_drain (GstFFMpegAudEnc * ffmpegaudenc)
|
||||
{
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
gboolean got_packet;
|
||||
|
||||
ret = gst_ffmpegaudenc_send_frame (ffmpegaudenc, NULL);
|
||||
|
||||
if (ret == GST_FLOW_OK) {
|
||||
do {
|
||||
ret = gst_ffmpegaudenc_receive_packet (ffmpegaudenc, &got_packet);
|
||||
if (ret != GST_FLOW_OK)
|
||||
break;
|
||||
} while (got_packet);
|
||||
}
|
||||
|
||||
/* NOTE: this may or may not work depending on capability */
|
||||
avcodec_flush_buffers (ffmpegaudenc->context);
|
||||
|
||||
/* FFMpeg will return AVERROR_EOF if it's internal was fully drained
|
||||
* then we are translating it to GST_FLOW_EOS. However, because this behavior
|
||||
* is fully internal stuff of this implementation and gstaudioencoder
|
||||
* baseclass doesn't convert this GST_FLOW_EOS to GST_FLOW_OK,
|
||||
* convert this flow returned here */
|
||||
if (ret == GST_FLOW_EOS)
|
||||
ret = GST_FLOW_OK;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_ffmpegaudenc_handle_frame (GstAudioEncoder * encoder, GstBuffer * inbuf)
|
||||
{
|
||||
GstFFMpegAudEnc *ffmpegaudenc;
|
||||
GstFlowReturn ret;
|
||||
gboolean got_packet;
|
||||
|
||||
ffmpegaudenc = (GstFFMpegAudEnc *) encoder;
|
||||
|
||||
if (G_UNLIKELY (!ffmpegaudenc->opened))
|
||||
goto not_negotiated;
|
||||
|
||||
if (!inbuf)
|
||||
return gst_ffmpegaudenc_drain (ffmpegaudenc);
|
||||
|
||||
/* endoder was drained or flushed, and ffmpeg encoder doesn't support
|
||||
* flushing. We need to re-open encoder then */
|
||||
if (ffmpegaudenc->need_reopen) {
|
||||
GST_DEBUG_OBJECT (ffmpegaudenc, "Open encoder again");
|
||||
|
||||
if (!gst_ffmpegaudenc_set_format (encoder,
|
||||
gst_audio_encoder_get_audio_info (encoder))) {
|
||||
GST_ERROR_OBJECT (ffmpegaudenc, "Couldn't re-open encoder");
|
||||
return GST_FLOW_NOT_NEGOTIATED;
|
||||
}
|
||||
}
|
||||
|
||||
inbuf = gst_buffer_ref (inbuf);
|
||||
|
||||
GST_DEBUG_OBJECT (ffmpegaudenc,
|
||||
"Received time %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
|
||||
", size %" G_GSIZE_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)),
|
||||
GST_TIME_ARGS (GST_BUFFER_DURATION (inbuf)), gst_buffer_get_size (inbuf));
|
||||
|
||||
/* Reorder channels to the GStreamer channel order */
|
||||
if (ffmpegaudenc->needs_reorder) {
|
||||
GstAudioInfo *info = gst_audio_encoder_get_audio_info (encoder);
|
||||
|
||||
inbuf = gst_buffer_make_writable (inbuf);
|
||||
gst_audio_buffer_reorder_channels (inbuf, info->finfo->format,
|
||||
info->channels, info->position, ffmpegaudenc->ffmpeg_layout);
|
||||
}
|
||||
|
||||
ret = gst_ffmpegaudenc_send_frame (ffmpegaudenc, inbuf);
|
||||
|
||||
if (ret != GST_FLOW_OK)
|
||||
goto send_frame_failed;
|
||||
|
||||
do {
|
||||
ret = gst_ffmpegaudenc_receive_packet (ffmpegaudenc, &got_packet);
|
||||
} while (got_packet);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
|
||||
/* ERRORS */
|
||||
not_negotiated:
|
||||
{
|
||||
GST_ELEMENT_ERROR (ffmpegaudenc, CORE, NEGOTIATION, (NULL),
|
||||
("not configured to input format before data start"));
|
||||
gst_buffer_unref (inbuf);
|
||||
return GST_FLOW_NOT_NEGOTIATED;
|
||||
}
|
||||
send_frame_failed:
|
||||
{
|
||||
GST_DEBUG_OBJECT (ffmpegaudenc, "Failed to send frame %d (%s)", ret,
|
||||
gst_flow_get_name (ret));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegaudenc_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstFFMpegAudEnc *ffmpegaudenc;
|
||||
|
||||
ffmpegaudenc = (GstFFMpegAudEnc *) (object);
|
||||
|
||||
if (ffmpegaudenc->opened) {
|
||||
GST_WARNING_OBJECT (ffmpegaudenc,
|
||||
"Can't change properties once encoder is setup !");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (prop_id) {
|
||||
default:
|
||||
if (!gst_ffmpeg_cfg_set_property (ffmpegaudenc->refcontext, value, pspec))
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegaudenc_get_property (GObject * object,
|
||||
guint prop_id, GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstFFMpegAudEnc *ffmpegaudenc;
|
||||
|
||||
ffmpegaudenc = (GstFFMpegAudEnc *) (object);
|
||||
|
||||
switch (prop_id) {
|
||||
default:
|
||||
if (!gst_ffmpeg_cfg_get_property (ffmpegaudenc->refcontext, value, pspec))
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_ffmpegaudenc_register (GstPlugin * plugin)
|
||||
{
|
||||
GTypeInfo typeinfo = {
|
||||
sizeof (GstFFMpegAudEncClass),
|
||||
(GBaseInitFunc) gst_ffmpegaudenc_base_init,
|
||||
NULL,
|
||||
(GClassInitFunc) gst_ffmpegaudenc_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof (GstFFMpegAudEnc),
|
||||
0,
|
||||
(GInstanceInitFunc) gst_ffmpegaudenc_init,
|
||||
};
|
||||
GType type;
|
||||
AVCodec *in_plugin;
|
||||
void *i = 0;
|
||||
|
||||
|
||||
GST_LOG ("Registering encoders");
|
||||
|
||||
while ((in_plugin = (AVCodec *) av_codec_iterate (&i))) {
|
||||
gchar *type_name;
|
||||
guint rank;
|
||||
|
||||
/* Skip non-AV codecs */
|
||||
if (in_plugin->type != AVMEDIA_TYPE_AUDIO)
|
||||
continue;
|
||||
|
||||
/* no quasi codecs, please */
|
||||
if (in_plugin->id == AV_CODEC_ID_PCM_S16LE_PLANAR ||
|
||||
(in_plugin->id >= AV_CODEC_ID_PCM_S16LE &&
|
||||
in_plugin->id <= AV_CODEC_ID_PCM_BLURAY) ||
|
||||
(in_plugin->id >= AV_CODEC_ID_PCM_S8_PLANAR &&
|
||||
in_plugin->id <= AV_CODEC_ID_PCM_F24LE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* No encoders depending on external libraries (we don't build them, but
|
||||
* people who build against an external ffmpeg might have them.
|
||||
* We have native gstreamer plugins for all of those libraries anyway. */
|
||||
if (!strncmp (in_plugin->name, "lib", 3)) {
|
||||
GST_DEBUG
|
||||
("Not using external library encoder %s. Use the gstreamer-native ones instead.",
|
||||
in_plugin->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* only encoders */
|
||||
if (!av_codec_is_encoder (in_plugin)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* FIXME : We should have a method to know cheaply whether we have a mapping
|
||||
* for the given plugin or not */
|
||||
|
||||
GST_DEBUG ("Trying plugin %s [%s]", in_plugin->name, in_plugin->long_name);
|
||||
|
||||
/* no codecs for which we're GUARANTEED to have better alternatives */
|
||||
if (!strcmp (in_plugin->name, "vorbis")
|
||||
|| !strcmp (in_plugin->name, "flac")) {
|
||||
GST_LOG ("Ignoring encoder %s", in_plugin->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* construct the type */
|
||||
type_name = g_strdup_printf ("avenc_%s", in_plugin->name);
|
||||
|
||||
type = g_type_from_name (type_name);
|
||||
|
||||
if (!type) {
|
||||
|
||||
/* create the glib type now */
|
||||
type =
|
||||
g_type_register_static (GST_TYPE_AUDIO_ENCODER, type_name, &typeinfo,
|
||||
0);
|
||||
g_type_set_qdata (type, GST_FFENC_PARAMS_QDATA, (gpointer) in_plugin);
|
||||
|
||||
{
|
||||
static const GInterfaceInfo preset_info = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
g_type_add_interface_static (type, GST_TYPE_PRESET, &preset_info);
|
||||
}
|
||||
}
|
||||
|
||||
switch (in_plugin->id) {
|
||||
/* avenc_aac: see https://bugzilla.gnome.org/show_bug.cgi?id=691617 */
|
||||
case AV_CODEC_ID_AAC:
|
||||
rank = GST_RANK_NONE;
|
||||
break;
|
||||
default:
|
||||
rank = GST_RANK_SECONDARY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!gst_element_register (plugin, type_name, rank, type)) {
|
||||
g_free (type_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_free (type_name);
|
||||
}
|
||||
|
||||
GST_LOG ("Finished registering encoders");
|
||||
|
||||
return TRUE;
|
||||
}
|
73
ext/libav/gstavaudenc.h
Normal file
73
ext/libav/gstavaudenc.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/* First, include the header file for the plugin, to bring in the
|
||||
* object definition and other useful things.
|
||||
*/
|
||||
|
||||
#ifndef __GST_FFMPEGAUDENC_H__
|
||||
#define __GST_FFMPEGAUDENC_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/gstaudioencoder.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstFFMpegAudEnc GstFFMpegAudEnc;
|
||||
|
||||
struct _GstFFMpegAudEnc
|
||||
{
|
||||
GstAudioEncoder parent;
|
||||
|
||||
AVCodecContext *context;
|
||||
AVCodecContext *refcontext;
|
||||
gboolean opened;
|
||||
gboolean need_reopen;
|
||||
|
||||
AVFrame *frame;
|
||||
|
||||
GstAudioChannelPosition ffmpeg_layout[64];
|
||||
gboolean needs_reorder;
|
||||
};
|
||||
|
||||
typedef struct _GstFFMpegAudEncClass GstFFMpegAudEncClass;
|
||||
|
||||
struct _GstFFMpegAudEncClass
|
||||
{
|
||||
GstAudioEncoderClass parent_class;
|
||||
|
||||
AVCodec *in_plugin;
|
||||
GstPadTemplate *srctempl, *sinktempl;
|
||||
};
|
||||
|
||||
#define GST_TYPE_FFMPEGAUDENC \
|
||||
(gst_ffmpegaudenc_get_type())
|
||||
#define GST_FFMPEGAUDENC(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGAUDENC,GstFFMpegAudEnc))
|
||||
#define GST_FFMPEGAUDENC_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGAUDENC,GstFFMpegAudEncClass))
|
||||
#define GST_IS_FFMPEGAUDENC(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGAUDENC))
|
||||
#define GST_IS_FFMPEGAUDENC_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGAUDENC))
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_FFMPEGAUDENC_H__ */
|
606
ext/libav/gstavcfg.c
Normal file
606
ext/libav/gstavcfg.c
Normal file
|
@ -0,0 +1,606 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* FFMpeg Configuration
|
||||
*
|
||||
* Copyright (C) <2006> Mark Nauwelaerts <manauw@skynet.be>
|
||||
*
|
||||
* 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 "gstav.h"
|
||||
#include "gstavvidenc.h"
|
||||
#include "gstavcfg.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <libavutil/opt.h>
|
||||
|
||||
static GQuark avoption_quark;
|
||||
static GHashTable *generic_overrides = NULL;
|
||||
|
||||
static void
|
||||
make_generic_overrides (void)
|
||||
{
|
||||
g_assert (!generic_overrides);
|
||||
generic_overrides = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, (GDestroyNotify) gst_structure_free);
|
||||
|
||||
g_hash_table_insert (generic_overrides, g_strdup ("b"),
|
||||
gst_structure_new_empty ("bitrate"));
|
||||
g_hash_table_insert (generic_overrides, g_strdup ("ab"),
|
||||
gst_structure_new_empty ("bitrate"));
|
||||
g_hash_table_insert (generic_overrides, g_strdup ("g"),
|
||||
gst_structure_new_empty ("gop-size"));
|
||||
g_hash_table_insert (generic_overrides, g_strdup ("bt"),
|
||||
gst_structure_new_empty ("bitrate-tolerance"));
|
||||
g_hash_table_insert (generic_overrides, g_strdup ("bf"),
|
||||
gst_structure_new_empty ("max-bframes"));
|
||||
|
||||
/* Those are exposed through caps */
|
||||
g_hash_table_insert (generic_overrides, g_strdup ("profile"),
|
||||
gst_structure_new ("profile", "skip", G_TYPE_BOOLEAN, TRUE, NULL));
|
||||
g_hash_table_insert (generic_overrides, g_strdup ("level"),
|
||||
gst_structure_new ("level", "skip", G_TYPE_BOOLEAN, TRUE, NULL));
|
||||
g_hash_table_insert (generic_overrides, g_strdup ("color_primaries"),
|
||||
gst_structure_new ("color_primaries", "skip", G_TYPE_BOOLEAN, TRUE,
|
||||
NULL));
|
||||
g_hash_table_insert (generic_overrides, g_strdup ("color_trc"),
|
||||
gst_structure_new ("color_trc", "skip", G_TYPE_BOOLEAN, TRUE, NULL));
|
||||
g_hash_table_insert (generic_overrides, g_strdup ("colorspace"),
|
||||
gst_structure_new ("colorspace", "skip", G_TYPE_BOOLEAN, TRUE, NULL));
|
||||
g_hash_table_insert (generic_overrides, g_strdup ("color_range"),
|
||||
gst_structure_new ("color_range", "skip", G_TYPE_BOOLEAN, TRUE, NULL));
|
||||
}
|
||||
|
||||
void
|
||||
gst_ffmpeg_cfg_init (void)
|
||||
{
|
||||
avoption_quark = g_quark_from_static_string ("ffmpeg-cfg-param-spec-data");
|
||||
make_generic_overrides ();
|
||||
}
|
||||
|
||||
static gint
|
||||
cmp_enum_value (GEnumValue * val1, GEnumValue * val2)
|
||||
{
|
||||
return val1->value - val2->value;
|
||||
}
|
||||
|
||||
static GType
|
||||
register_enum (const AVClass ** obj, const AVOption * top_opt)
|
||||
{
|
||||
const AVOption *opt = NULL;
|
||||
GType res = 0;
|
||||
GArray *values = g_array_new (TRUE, TRUE, sizeof (GEnumValue));
|
||||
gchar *lower_obj_name = g_ascii_strdown ((*obj)->class_name, -1);
|
||||
gchar *enum_name = g_strdup_printf ("%s-%s", lower_obj_name, top_opt->unit);
|
||||
gboolean none_default = TRUE;
|
||||
|
||||
g_strcanon (enum_name, G_CSET_a_2_z G_CSET_DIGITS, '-');
|
||||
|
||||
if ((res = g_type_from_name (enum_name)))
|
||||
goto done;
|
||||
|
||||
while ((opt = av_opt_next (obj, opt))) {
|
||||
if (opt->type == AV_OPT_TYPE_CONST && !g_strcmp0 (top_opt->unit, opt->unit)) {
|
||||
GEnumValue val;
|
||||
|
||||
val.value = opt->default_val.i64;
|
||||
val.value_name = g_strdup (opt->help ? opt->help : opt->name);
|
||||
val.value_nick = g_strdup (opt->name);
|
||||
|
||||
if (opt->default_val.i64 == top_opt->default_val.i64)
|
||||
none_default = FALSE;
|
||||
|
||||
g_array_append_val (values, val);
|
||||
}
|
||||
}
|
||||
|
||||
if (values->len) {
|
||||
guint i = 0;
|
||||
gint cur_val;
|
||||
gboolean cur_val_set = FALSE;
|
||||
|
||||
/* Sometimes ffmpeg sets a default value but no named constants with
|
||||
* this value, we assume this means "unspecified" and add our own
|
||||
*/
|
||||
if (none_default) {
|
||||
GEnumValue val;
|
||||
|
||||
val.value = top_opt->default_val.i64;
|
||||
val.value_name = g_strdup ("Unspecified");
|
||||
val.value_nick = g_strdup ("unknown");
|
||||
g_array_append_val (values, val);
|
||||
}
|
||||
|
||||
g_array_sort (values, (GCompareFunc) cmp_enum_value);
|
||||
|
||||
/* Dedup, easy once sorted
|
||||
* We do this because ffmpeg can expose multiple names for the
|
||||
* same constant, the way we expose enums makes this too confusing.
|
||||
*/
|
||||
while (i < values->len) {
|
||||
if (cur_val_set) {
|
||||
if (g_array_index (values, GEnumValue, i).value == cur_val) {
|
||||
g_array_remove_index (values, i);
|
||||
} else {
|
||||
cur_val = g_array_index (values, GEnumValue, i).value;
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
cur_val = g_array_index (values, GEnumValue, i).value;
|
||||
cur_val_set = TRUE;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
res =
|
||||
g_enum_register_static (enum_name, &g_array_index (values, GEnumValue,
|
||||
0));
|
||||
|
||||
gst_type_mark_as_plugin_api (res, 0);
|
||||
}
|
||||
|
||||
done:
|
||||
g_free (lower_obj_name);
|
||||
g_free (enum_name);
|
||||
return res;
|
||||
}
|
||||
|
||||
static gint
|
||||
cmp_flags_value (GEnumValue * val1, GEnumValue * val2)
|
||||
{
|
||||
return val1->value - val2->value;
|
||||
}
|
||||
|
||||
static GType
|
||||
register_flags (const AVClass ** obj, const AVOption * top_opt)
|
||||
{
|
||||
const AVOption *opt = NULL;
|
||||
GType res = 0;
|
||||
GArray *values = g_array_new (TRUE, TRUE, sizeof (GEnumValue));
|
||||
gchar *lower_obj_name = g_ascii_strdown ((*obj)->class_name, -1);
|
||||
gchar *flags_name = g_strdup_printf ("%s-%s", lower_obj_name, top_opt->unit);
|
||||
|
||||
g_strcanon (flags_name, G_CSET_a_2_z G_CSET_DIGITS, '-');
|
||||
|
||||
if ((res = g_type_from_name (flags_name)))
|
||||
goto done;
|
||||
|
||||
while ((opt = av_opt_next (obj, opt))) {
|
||||
if (opt->type == AV_OPT_TYPE_CONST && !g_strcmp0 (top_opt->unit, opt->unit)) {
|
||||
GFlagsValue val;
|
||||
|
||||
/* We expose pass manually, hardcoding this isn't very nice, but
|
||||
* I don't expect we want to do that sort of things often enough
|
||||
* to warrant a general mechanism
|
||||
*/
|
||||
if (!g_strcmp0 (top_opt->name, "flags")) {
|
||||
if (opt->default_val.i64 == AV_CODEC_FLAG_QSCALE ||
|
||||
opt->default_val.i64 == AV_CODEC_FLAG_PASS1 ||
|
||||
opt->default_val.i64 == AV_CODEC_FLAG_PASS2) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
val.value = opt->default_val.i64;
|
||||
val.value_name = g_strdup (opt->help ? opt->help : opt->name);
|
||||
val.value_nick = g_strdup (opt->name);
|
||||
|
||||
g_array_append_val (values, val);
|
||||
}
|
||||
}
|
||||
|
||||
if (values->len) {
|
||||
g_array_sort (values, (GCompareFunc) cmp_flags_value);
|
||||
|
||||
res =
|
||||
g_flags_register_static (flags_name, &g_array_index (values,
|
||||
GFlagsValue, 0));
|
||||
|
||||
gst_type_mark_as_plugin_api (res, 0);
|
||||
}
|
||||
|
||||
done:
|
||||
g_free (lower_obj_name);
|
||||
g_free (flags_name);
|
||||
return res;
|
||||
}
|
||||
|
||||
static guint
|
||||
install_opts (GObjectClass * gobject_class, const AVClass ** obj, guint prop_id,
|
||||
gint flags, const gchar * extra_help, GHashTable * overrides)
|
||||
{
|
||||
const AVOption *opt = NULL;
|
||||
|
||||
while ((opt = av_opt_next (obj, opt))) {
|
||||
GParamSpec *pspec = NULL;
|
||||
AVOptionRanges *r;
|
||||
gdouble min = G_MINDOUBLE;
|
||||
gdouble max = G_MAXDOUBLE;
|
||||
gchar *help;
|
||||
const gchar *name;
|
||||
|
||||
if (overrides && g_hash_table_contains (overrides, opt->name)) {
|
||||
gboolean skip;
|
||||
const GstStructure *s =
|
||||
(GstStructure *) g_hash_table_lookup (overrides, opt->name);
|
||||
|
||||
name = gst_structure_get_name (s);
|
||||
if (gst_structure_get_boolean (s, "skip", &skip) && skip) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
name = opt->name;
|
||||
}
|
||||
|
||||
if ((opt->flags & flags) != flags)
|
||||
continue;
|
||||
|
||||
if (g_object_class_find_property (gobject_class, name))
|
||||
continue;
|
||||
|
||||
if (av_opt_query_ranges (&r, obj, opt->name, AV_OPT_SEARCH_FAKE_OBJ) >= 0) {
|
||||
if (r->nb_ranges == 1) {
|
||||
min = r->range[0]->value_min;
|
||||
max = r->range[0]->value_max;
|
||||
}
|
||||
av_opt_freep_ranges (&r);
|
||||
}
|
||||
|
||||
help = g_strdup_printf ("%s%s", opt->help, extra_help);
|
||||
|
||||
switch (opt->type) {
|
||||
case AV_OPT_TYPE_INT:
|
||||
if (opt->unit) {
|
||||
GType enum_gtype;
|
||||
enum_gtype = register_enum (obj, opt);
|
||||
|
||||
if (enum_gtype) {
|
||||
pspec = g_param_spec_enum (name, name, help,
|
||||
enum_gtype, opt->default_val.i64, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, prop_id++, pspec);
|
||||
} else { /* Some options have a unit but no named constants associated */
|
||||
pspec = g_param_spec_int (name, name, help,
|
||||
(gint) min, (gint) max, opt->default_val.i64,
|
||||
G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, prop_id++, pspec);
|
||||
}
|
||||
} else {
|
||||
pspec = g_param_spec_int (name, name, help,
|
||||
(gint) min, (gint) max, opt->default_val.i64, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, prop_id++, pspec);
|
||||
}
|
||||
break;
|
||||
case AV_OPT_TYPE_FLAGS:
|
||||
if (opt->unit) {
|
||||
GType flags_gtype;
|
||||
flags_gtype = register_flags (obj, opt);
|
||||
|
||||
if (flags_gtype) {
|
||||
pspec = g_param_spec_flags (name, name, help,
|
||||
flags_gtype, opt->default_val.i64, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, prop_id++, pspec);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AV_OPT_TYPE_DURATION: /* Fall through */
|
||||
case AV_OPT_TYPE_INT64:
|
||||
/* FIXME 2.0: Workaround for worst property related API change. We
|
||||
* continue using a 32 bit integer for the bitrate property as
|
||||
* otherwise too much existing code will fail at runtime.
|
||||
*
|
||||
* See https://gitlab.freedesktop.org/gstreamer/gst-libav/issues/41#note_142808 */
|
||||
if (g_strcmp0 (name, "bitrate") == 0) {
|
||||
pspec = g_param_spec_int (name, name, help,
|
||||
(gint) MAX (min, G_MININT), (gint) MIN (max, G_MAXINT),
|
||||
(gint) opt->default_val.i64, G_PARAM_READWRITE);
|
||||
} else {
|
||||
/* ffmpeg expresses all ranges with doubles, this is sad */
|
||||
pspec = g_param_spec_int64 (name, name, help,
|
||||
(min == (gdouble) INT64_MIN ? INT64_MIN : (gint64) min),
|
||||
(max == (gdouble) INT64_MAX ? INT64_MAX : (gint64) max),
|
||||
opt->default_val.i64, G_PARAM_READWRITE);
|
||||
}
|
||||
g_object_class_install_property (gobject_class, prop_id++, pspec);
|
||||
break;
|
||||
case AV_OPT_TYPE_DOUBLE:
|
||||
pspec = g_param_spec_double (name, name, help,
|
||||
min, max, opt->default_val.dbl, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, prop_id++, pspec);
|
||||
break;
|
||||
case AV_OPT_TYPE_FLOAT:
|
||||
pspec = g_param_spec_float (name, name, help,
|
||||
(gfloat) min, (gfloat) max, (gfloat) opt->default_val.dbl,
|
||||
G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, prop_id++, pspec);
|
||||
break;
|
||||
case AV_OPT_TYPE_STRING:
|
||||
pspec = g_param_spec_string (name, name, help,
|
||||
opt->default_val.str, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, prop_id++, pspec);
|
||||
break;
|
||||
case AV_OPT_TYPE_UINT64:
|
||||
/* ffmpeg expresses all ranges with doubles, this is appalling */
|
||||
pspec = g_param_spec_uint64 (name, name, help,
|
||||
(guint64) (min <= (gdouble) 0 ? 0 : (guint64) min),
|
||||
(guint64) (max >=
|
||||
/* Biggest value before UINT64_MAX that can be represented as double */
|
||||
(gdouble) 18446744073709550000.0 ?
|
||||
/* The Double conversion rounds UINT64_MAX to a bigger */
|
||||
/* value, so the following smaller limit must be used. */
|
||||
G_GUINT64_CONSTANT (18446744073709550000) : (guint64) max),
|
||||
opt->default_val.i64, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, prop_id++, pspec);
|
||||
break;
|
||||
case AV_OPT_TYPE_BOOL:
|
||||
pspec = g_param_spec_boolean (name, name, help,
|
||||
opt->default_val.i64 ? TRUE : FALSE, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, prop_id++, pspec);
|
||||
break;
|
||||
/* TODO: didn't find options for the video encoders with
|
||||
* the following type, add support if needed */
|
||||
case AV_OPT_TYPE_CHANNEL_LAYOUT:
|
||||
case AV_OPT_TYPE_COLOR:
|
||||
case AV_OPT_TYPE_VIDEO_RATE:
|
||||
case AV_OPT_TYPE_SAMPLE_FMT:
|
||||
case AV_OPT_TYPE_PIXEL_FMT:
|
||||
case AV_OPT_TYPE_IMAGE_SIZE:
|
||||
case AV_OPT_TYPE_DICT:
|
||||
case AV_OPT_TYPE_BINARY:
|
||||
case AV_OPT_TYPE_RATIONAL:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
g_free (help);
|
||||
|
||||
if (pspec) {
|
||||
g_param_spec_set_qdata (pspec, avoption_quark, (gpointer) opt);
|
||||
}
|
||||
}
|
||||
|
||||
return prop_id;
|
||||
}
|
||||
|
||||
void
|
||||
gst_ffmpeg_cfg_install_properties (GObjectClass * klass, AVCodec * in_plugin,
|
||||
guint base, gint flags)
|
||||
{
|
||||
gint prop_id;
|
||||
AVCodecContext *ctx;
|
||||
|
||||
prop_id = base;
|
||||
g_return_if_fail (base > 0);
|
||||
|
||||
ctx = avcodec_alloc_context3 (in_plugin);
|
||||
if (!ctx)
|
||||
g_warning ("could not get context");
|
||||
|
||||
prop_id =
|
||||
install_opts ((GObjectClass *) klass, &in_plugin->priv_class, prop_id, 0,
|
||||
" (Private codec option)", NULL);
|
||||
prop_id =
|
||||
install_opts ((GObjectClass *) klass, &ctx->av_class, prop_id, flags,
|
||||
" (Generic codec option, might have no effect)", generic_overrides);
|
||||
|
||||
if (ctx) {
|
||||
gst_ffmpeg_avcodec_close (ctx);
|
||||
av_free (ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
set_option_value (AVCodecContext * ctx, GParamSpec * pspec,
|
||||
const GValue * value, const AVOption * opt)
|
||||
{
|
||||
int res = -1;
|
||||
|
||||
switch (G_PARAM_SPEC_VALUE_TYPE (pspec)) {
|
||||
case G_TYPE_INT:
|
||||
res = av_opt_set_int (ctx, opt->name,
|
||||
g_value_get_int (value), AV_OPT_SEARCH_CHILDREN);
|
||||
break;
|
||||
case G_TYPE_INT64:
|
||||
res = av_opt_set_int (ctx, opt->name,
|
||||
g_value_get_int64 (value), AV_OPT_SEARCH_CHILDREN);
|
||||
break;
|
||||
case G_TYPE_UINT64:
|
||||
res = av_opt_set_int (ctx, opt->name,
|
||||
g_value_get_uint64 (value), AV_OPT_SEARCH_CHILDREN);
|
||||
break;
|
||||
case G_TYPE_DOUBLE:
|
||||
res = av_opt_set_double (ctx, opt->name,
|
||||
g_value_get_double (value), AV_OPT_SEARCH_CHILDREN);
|
||||
break;
|
||||
case G_TYPE_FLOAT:
|
||||
res = av_opt_set_double (ctx, opt->name,
|
||||
g_value_get_float (value), AV_OPT_SEARCH_CHILDREN);
|
||||
break;
|
||||
case G_TYPE_STRING:
|
||||
res = av_opt_set (ctx, opt->name,
|
||||
g_value_get_string (value), AV_OPT_SEARCH_CHILDREN);
|
||||
/* Some code in FFmpeg returns ENOMEM if the string is NULL:
|
||||
* *dst = av_strdup(val);
|
||||
* return *dst ? 0 : AVERROR(ENOMEM);
|
||||
* That makes little sense, let's ignore that
|
||||
*/
|
||||
if (!g_value_get_string (value))
|
||||
res = 0;
|
||||
break;
|
||||
case G_TYPE_BOOLEAN:
|
||||
res = av_opt_set_int (ctx, opt->name,
|
||||
g_value_get_boolean (value), AV_OPT_SEARCH_CHILDREN);
|
||||
break;
|
||||
default:
|
||||
if (G_IS_PARAM_SPEC_ENUM (pspec)) {
|
||||
res = av_opt_set_int (ctx, opt->name,
|
||||
g_value_get_enum (value), AV_OPT_SEARCH_CHILDREN);
|
||||
} else if (G_IS_PARAM_SPEC_FLAGS (pspec)) {
|
||||
res = av_opt_set_int (ctx, opt->name,
|
||||
g_value_get_flags (value), AV_OPT_SEARCH_CHILDREN);
|
||||
} else { /* oops, bit lazy we don't cover this case yet */
|
||||
g_critical ("%s does not yet support type %s", GST_FUNCTION,
|
||||
g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_ffmpeg_cfg_set_property (AVCodecContext * refcontext, const GValue * value,
|
||||
GParamSpec * pspec)
|
||||
{
|
||||
const AVOption *opt;
|
||||
|
||||
opt = g_param_spec_get_qdata (pspec, avoption_quark);
|
||||
|
||||
if (!opt)
|
||||
return FALSE;
|
||||
|
||||
return set_option_value (refcontext, pspec, value, opt) >= 0;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_ffmpeg_cfg_get_property (AVCodecContext * refcontext, GValue * value,
|
||||
GParamSpec * pspec)
|
||||
{
|
||||
const AVOption *opt;
|
||||
int res = -1;
|
||||
|
||||
opt = g_param_spec_get_qdata (pspec, avoption_quark);
|
||||
|
||||
if (!opt)
|
||||
return FALSE;
|
||||
|
||||
switch (G_PARAM_SPEC_VALUE_TYPE (pspec)) {
|
||||
case G_TYPE_INT:
|
||||
{
|
||||
int64_t val;
|
||||
if ((res = av_opt_get_int (refcontext, opt->name,
|
||||
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
|
||||
g_value_set_int (value, val);
|
||||
break;
|
||||
}
|
||||
case G_TYPE_INT64:
|
||||
{
|
||||
int64_t val;
|
||||
if ((res = av_opt_get_int (refcontext, opt->name,
|
||||
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
|
||||
g_value_set_int64 (value, val);
|
||||
break;
|
||||
}
|
||||
case G_TYPE_UINT64:
|
||||
{
|
||||
int64_t val;
|
||||
if ((res = av_opt_get_int (refcontext, opt->name,
|
||||
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
|
||||
g_value_set_uint64 (value, val);
|
||||
break;
|
||||
}
|
||||
case G_TYPE_DOUBLE:
|
||||
{
|
||||
gdouble val;
|
||||
if ((res = av_opt_get_double (refcontext, opt->name,
|
||||
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
|
||||
g_value_set_double (value, val);
|
||||
break;
|
||||
}
|
||||
case G_TYPE_FLOAT:
|
||||
{
|
||||
gdouble val;
|
||||
if ((res = av_opt_get_double (refcontext, opt->name,
|
||||
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
|
||||
g_value_set_float (value, (gfloat) val);
|
||||
break;
|
||||
}
|
||||
case G_TYPE_STRING:
|
||||
{
|
||||
uint8_t *val;
|
||||
if ((res = av_opt_get (refcontext, opt->name,
|
||||
AV_OPT_SEARCH_CHILDREN | AV_OPT_ALLOW_NULL, &val) >= 0)) {
|
||||
g_value_set_string (value, (gchar *) val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case G_TYPE_BOOLEAN:
|
||||
{
|
||||
int64_t val;
|
||||
if ((res = av_opt_get_int (refcontext, opt->name,
|
||||
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
|
||||
g_value_set_boolean (value, val ? TRUE : FALSE);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (G_IS_PARAM_SPEC_ENUM (pspec)) {
|
||||
int64_t val;
|
||||
|
||||
if ((res = av_opt_get_int (refcontext, opt->name,
|
||||
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
|
||||
g_value_set_enum (value, val);
|
||||
} else if (G_IS_PARAM_SPEC_FLAGS (pspec)) {
|
||||
int64_t val;
|
||||
|
||||
if ((res = av_opt_get_int (refcontext, opt->name,
|
||||
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
|
||||
g_value_set_flags (value, val);
|
||||
} else { /* oops, bit lazy we don't cover this case yet */
|
||||
g_critical ("%s does not yet support type %s", GST_FUNCTION,
|
||||
g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
|
||||
}
|
||||
}
|
||||
|
||||
return res >= 0;
|
||||
}
|
||||
|
||||
void
|
||||
gst_ffmpeg_cfg_fill_context (GObject * object, AVCodecContext * context)
|
||||
{
|
||||
GParamSpec **pspecs;
|
||||
guint num_props, i;
|
||||
|
||||
pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object),
|
||||
&num_props);
|
||||
|
||||
for (i = 0; i < num_props; ++i) {
|
||||
GParamSpec *pspec = pspecs[i];
|
||||
const AVOption *opt;
|
||||
GValue value = G_VALUE_INIT;
|
||||
|
||||
opt = g_param_spec_get_qdata (pspec, avoption_quark);
|
||||
|
||||
if (!opt)
|
||||
continue;
|
||||
|
||||
g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
|
||||
g_object_get_property (object, pspec->name, &value);
|
||||
set_option_value (context, pspec, &value, opt);
|
||||
g_value_unset (&value);
|
||||
}
|
||||
g_free (pspecs);
|
||||
}
|
||||
|
||||
void
|
||||
gst_ffmpeg_cfg_finalize (void)
|
||||
{
|
||||
GST_ERROR ("Finalizing");
|
||||
g_assert (generic_overrides);
|
||||
g_hash_table_unref (generic_overrides);
|
||||
}
|
45
ext/libav/gstavcfg.h
Normal file
45
ext/libav/gstavcfg.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <2006> Mark Nauwelaerts <manauw@skynet.be>
|
||||
*
|
||||
* 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_FFMPEGCFG_H__
|
||||
#define __GST_FFMPEGCFG_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gst_ffmpeg_cfg_init (void);
|
||||
|
||||
void gst_ffmpeg_cfg_install_properties (GObjectClass * klass, AVCodec *in_plugin, guint base, gint flags);
|
||||
|
||||
gboolean gst_ffmpeg_cfg_set_property (AVCodecContext *refcontext,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
|
||||
gboolean gst_ffmpeg_cfg_get_property (AVCodecContext *refcontext,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
void gst_ffmpeg_cfg_fill_context (GObject *object, AVCodecContext * context);
|
||||
void gst_ffmpeg_cfg_finalize (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#endif /* __GST_FFMPEGCFG_H__ */
|
4419
ext/libav/gstavcodecmap.c
Normal file
4419
ext/libav/gstavcodecmap.c
Normal file
File diff suppressed because it is too large
Load diff
133
ext/libav/gstavcodecmap.h
Normal file
133
ext/libav/gstavcodecmap.h
Normal file
|
@ -0,0 +1,133 @@
|
|||
/* 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_FFMPEG_CODECMAP_H__
|
||||
#define __GST_FFMPEG_CODECMAP_H__
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/audio.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
/*
|
||||
* _codecid_is_image() returns TRUE for image formats
|
||||
*/
|
||||
gboolean
|
||||
gst_ffmpeg_codecid_is_image (enum AVCodecID codec_id);
|
||||
|
||||
/*
|
||||
* _codecid_to_caps () gets the GstCaps that belongs to
|
||||
* a certain CodecID for a pad with compressed data.
|
||||
*/
|
||||
|
||||
GstCaps *
|
||||
gst_ffmpeg_codecid_to_caps (enum AVCodecID codec_id,
|
||||
AVCodecContext *context,
|
||||
gboolean encode);
|
||||
|
||||
/*
|
||||
* _codectype_to_caps () gets the GstCaps that belongs to
|
||||
* a certain AVMediaType for a pad with uncompressed data.
|
||||
*/
|
||||
|
||||
GstCaps *
|
||||
gst_ffmpeg_codectype_to_audio_caps (AVCodecContext *context,
|
||||
enum AVCodecID codec_id,
|
||||
gboolean encode,
|
||||
AVCodec *codec);
|
||||
GstCaps *
|
||||
gst_ffmpeg_codectype_to_video_caps (AVCodecContext *context,
|
||||
enum AVCodecID codec_id,
|
||||
gboolean encode,
|
||||
AVCodec *codec);
|
||||
|
||||
/*
|
||||
* caps_to_codecid () transforms a GstCaps that belongs to
|
||||
* a pad for compressed data to (optionally) a filled-in
|
||||
* context and a codecID.
|
||||
*/
|
||||
|
||||
enum AVCodecID
|
||||
gst_ffmpeg_caps_to_codecid (const GstCaps *caps,
|
||||
AVCodecContext *context);
|
||||
|
||||
/*
|
||||
* caps_with_codecid () transforms a GstCaps for a known codec
|
||||
* ID into a filled-in context.
|
||||
*/
|
||||
|
||||
void
|
||||
gst_ffmpeg_caps_with_codecid (enum AVCodecID codec_id,
|
||||
enum AVMediaType codec_type,
|
||||
const GstCaps *caps,
|
||||
AVCodecContext *context);
|
||||
|
||||
/*
|
||||
* caps_with_codectype () transforms a GstCaps that belongs to
|
||||
* a pad for uncompressed data to a filled-in context.
|
||||
*/
|
||||
|
||||
void
|
||||
gst_ffmpeg_caps_with_codectype (enum AVMediaType type,
|
||||
const GstCaps *caps,
|
||||
AVCodecContext *context);
|
||||
|
||||
void
|
||||
gst_ffmpeg_videoinfo_to_context (GstVideoInfo *info,
|
||||
AVCodecContext *context);
|
||||
|
||||
void
|
||||
gst_ffmpeg_audioinfo_to_context (GstAudioInfo *info,
|
||||
AVCodecContext *context);
|
||||
|
||||
GstVideoFormat gst_ffmpeg_pixfmt_to_videoformat (enum AVPixelFormat pixfmt);
|
||||
enum AVPixelFormat gst_ffmpeg_videoformat_to_pixfmt (GstVideoFormat format);
|
||||
|
||||
GstAudioFormat gst_ffmpeg_smpfmt_to_audioformat (enum AVSampleFormat sample_fmt,
|
||||
GstAudioLayout * layout);
|
||||
|
||||
/*
|
||||
* _formatid_to_caps () is meant for muxers/demuxers, it
|
||||
* transforms a name (ffmpeg way of ID'ing these, why don't
|
||||
* they have unique numerical IDs?) to the corresponding
|
||||
* caps belonging to that mux-format
|
||||
*/
|
||||
|
||||
GstCaps *
|
||||
gst_ffmpeg_formatid_to_caps (const gchar *format_name);
|
||||
|
||||
/*
|
||||
* _formatid_get_codecids () can be used to get the codecIDs
|
||||
* (AV_CODEC_ID_NONE-terminated list) that fit that specific
|
||||
* output format.
|
||||
*/
|
||||
|
||||
gboolean
|
||||
gst_ffmpeg_formatid_get_codecids (const gchar *format_name,
|
||||
enum AVCodecID ** video_codec_list,
|
||||
enum AVCodecID ** audio_codec_list,
|
||||
AVOutputFormat * plugin);
|
||||
|
||||
|
||||
gboolean
|
||||
gst_ffmpeg_channel_layout_to_gst (guint64 channel_layout, gint channels,
|
||||
GstAudioChannelPosition * pos);
|
||||
|
||||
#endif /* __GST_FFMPEG_CODECMAP_H__ */
|
506
ext/libav/gstavdeinterlace.c
Normal file
506
ext/libav/gstavdeinterlace.c
Normal file
|
@ -0,0 +1,506 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||
* This file:
|
||||
* Copyright (C) 2005 Luca Ognibene <luogni@tin.it>
|
||||
* Copyright (C) 2006 Martin Zlomek <martin.zlomek@itonis.tv>
|
||||
*
|
||||
* 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 <libavcodec/avcodec.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libavfilter/avfilter.h>
|
||||
#include <libavfilter/buffersrc.h>
|
||||
#include <libavfilter/buffersink.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
#include "gstav.h"
|
||||
#include "gstavcodecmap.h"
|
||||
#include "gstavutils.h"
|
||||
|
||||
|
||||
/* Properties */
|
||||
|
||||
#define DEFAULT_MODE GST_FFMPEGDEINTERLACE_MODE_AUTO
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_MODE,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_FFMPEGDEINTERLACE_MODE_AUTO,
|
||||
GST_FFMPEGDEINTERLACE_MODE_INTERLACED,
|
||||
GST_FFMPEGDEINTERLACE_MODE_DISABLED
|
||||
} GstFFMpegDeinterlaceMode;
|
||||
|
||||
#define GST_TYPE_FFMPEGDEINTERLACE_MODES (gst_ffmpegdeinterlace_modes_get_type ())
|
||||
static GType
|
||||
gst_ffmpegdeinterlace_modes_get_type (void)
|
||||
{
|
||||
static GType deinterlace_modes_type = 0;
|
||||
|
||||
static const GEnumValue modes_types[] = {
|
||||
{GST_FFMPEGDEINTERLACE_MODE_AUTO, "Auto detection", "auto"},
|
||||
{GST_FFMPEGDEINTERLACE_MODE_INTERLACED, "Force deinterlacing",
|
||||
"interlaced"},
|
||||
{GST_FFMPEGDEINTERLACE_MODE_DISABLED, "Run in passthrough mode",
|
||||
"disabled"},
|
||||
{0, NULL, NULL},
|
||||
};
|
||||
|
||||
if (!deinterlace_modes_type) {
|
||||
deinterlace_modes_type =
|
||||
g_enum_register_static ("GstLibAVDeinterlaceModes", modes_types);
|
||||
}
|
||||
return deinterlace_modes_type;
|
||||
}
|
||||
|
||||
typedef struct _GstFFMpegDeinterlace
|
||||
{
|
||||
GstElement element;
|
||||
|
||||
GstPad *sinkpad, *srcpad;
|
||||
|
||||
gint width, height;
|
||||
gint to_size;
|
||||
|
||||
GstFFMpegDeinterlaceMode mode;
|
||||
|
||||
gboolean interlaced; /* is input interlaced? */
|
||||
gboolean passthrough;
|
||||
|
||||
gboolean reconfigure;
|
||||
GstFFMpegDeinterlaceMode new_mode;
|
||||
|
||||
enum AVPixelFormat pixfmt;
|
||||
AVFrame from_frame, to_frame;
|
||||
|
||||
AVFilterContext *buffersink_ctx;
|
||||
AVFilterContext *buffersrc_ctx;
|
||||
AVFilterGraph *filter_graph;
|
||||
AVFrame *filter_frame;
|
||||
int last_width, last_height;
|
||||
enum AVPixelFormat last_pixfmt;
|
||||
|
||||
} GstFFMpegDeinterlace;
|
||||
|
||||
typedef struct _GstFFMpegDeinterlaceClass
|
||||
{
|
||||
GstElementClass parent_class;
|
||||
} GstFFMpegDeinterlaceClass;
|
||||
|
||||
#define GST_TYPE_FFMPEGDEINTERLACE \
|
||||
(gst_ffmpegdeinterlace_get_type())
|
||||
#define GST_FFMPEGDEINTERLACE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGDEINTERLACE,GstFFMpegDeinterlace))
|
||||
#define GST_FFMPEGDEINTERLACE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGDEINTERLACE,GstFFMpegDeinterlace))
|
||||
#define GST_IS_FFMPEGDEINTERLACE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGDEINTERLACE))
|
||||
#define GST_IS_FFMPEGDEINTERLACE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGDEINTERLACE))
|
||||
|
||||
GType gst_ffmpegdeinterlace_get_type (void);
|
||||
|
||||
static void gst_ffmpegdeinterlace_set_property (GObject * self, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void gst_ffmpegdeinterlace_get_property (GObject * self, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("I420"))
|
||||
);
|
||||
|
||||
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("I420"))
|
||||
);
|
||||
|
||||
G_DEFINE_TYPE (GstFFMpegDeinterlace, gst_ffmpegdeinterlace, GST_TYPE_ELEMENT);
|
||||
|
||||
static GstFlowReturn gst_ffmpegdeinterlace_chain (GstPad * pad,
|
||||
GstObject * parent, GstBuffer * inbuf);
|
||||
|
||||
static void gst_ffmpegdeinterlace_dispose (GObject * obj);
|
||||
|
||||
static void
|
||||
gst_ffmpegdeinterlace_class_init (GstFFMpegDeinterlaceClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||
|
||||
gobject_class->set_property = gst_ffmpegdeinterlace_set_property;
|
||||
gobject_class->get_property = gst_ffmpegdeinterlace_get_property;
|
||||
|
||||
/**
|
||||
* GstFFMpegDeinterlace:mode
|
||||
*
|
||||
* This selects whether the deinterlacing methods should
|
||||
* always be applied or if they should only be applied
|
||||
* on content that has the "interlaced" flag on the caps.
|
||||
*
|
||||
* Since: 0.10.13
|
||||
*/
|
||||
g_object_class_install_property (gobject_class, PROP_MODE,
|
||||
g_param_spec_enum ("mode", "Mode", "Deinterlace Mode",
|
||||
GST_TYPE_FFMPEGDEINTERLACE_MODES,
|
||||
DEFAULT_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
|
||||
);
|
||||
|
||||
gst_element_class_add_static_pad_template (element_class, &src_factory);
|
||||
gst_element_class_add_static_pad_template (element_class, &sink_factory);
|
||||
|
||||
gst_element_class_set_static_metadata (element_class,
|
||||
"libav Deinterlace element", "Filter/Effect/Video/Deinterlace",
|
||||
"Deinterlace video", "Luca Ognibene <luogni@tin.it>");
|
||||
|
||||
gobject_class->dispose = gst_ffmpegdeinterlace_dispose;
|
||||
|
||||
gst_type_mark_as_plugin_api (GST_TYPE_FFMPEGDEINTERLACE_MODES, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegdeinterlace_update_passthrough (GstFFMpegDeinterlace * deinterlace)
|
||||
{
|
||||
deinterlace->passthrough =
|
||||
(deinterlace->mode == GST_FFMPEGDEINTERLACE_MODE_DISABLED
|
||||
|| (!deinterlace->interlaced
|
||||
&& deinterlace->mode != GST_FFMPEGDEINTERLACE_MODE_INTERLACED));
|
||||
GST_DEBUG_OBJECT (deinterlace, "Passthrough: %d", deinterlace->passthrough);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_ffmpegdeinterlace_sink_setcaps (GstPad * pad, GstCaps * caps)
|
||||
{
|
||||
GstFFMpegDeinterlace *deinterlace =
|
||||
GST_FFMPEGDEINTERLACE (gst_pad_get_parent (pad));
|
||||
GstStructure *structure = gst_caps_get_structure (caps, 0);
|
||||
const gchar *imode;
|
||||
AVCodecContext *ctx;
|
||||
GstCaps *src_caps;
|
||||
gboolean ret;
|
||||
|
||||
/* FIXME: use GstVideoInfo etc. */
|
||||
if (!gst_structure_get_int (structure, "width", &deinterlace->width))
|
||||
return FALSE;
|
||||
if (!gst_structure_get_int (structure, "height", &deinterlace->height))
|
||||
return FALSE;
|
||||
|
||||
deinterlace->interlaced = FALSE;
|
||||
imode = gst_structure_get_string (structure, "interlace-mode");
|
||||
if (imode && (!strcmp (imode, "interleaved") || !strcmp (imode, "mixed"))) {
|
||||
deinterlace->interlaced = TRUE;
|
||||
}
|
||||
gst_ffmpegdeinterlace_update_passthrough (deinterlace);
|
||||
|
||||
ctx = avcodec_alloc_context3 (NULL);
|
||||
ctx->width = deinterlace->width;
|
||||
ctx->height = deinterlace->height;
|
||||
ctx->pix_fmt = AV_PIX_FMT_NB;
|
||||
gst_ffmpeg_caps_with_codectype (AVMEDIA_TYPE_VIDEO, caps, ctx);
|
||||
if (ctx->pix_fmt == AV_PIX_FMT_NB) {
|
||||
gst_ffmpeg_avcodec_close (ctx);
|
||||
av_free (ctx);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
deinterlace->pixfmt = ctx->pix_fmt;
|
||||
|
||||
av_free (ctx);
|
||||
|
||||
deinterlace->to_size =
|
||||
av_image_get_buffer_size (deinterlace->pixfmt, deinterlace->width,
|
||||
deinterlace->height, 1);
|
||||
|
||||
src_caps = gst_caps_copy (caps);
|
||||
gst_caps_set_simple (src_caps, "interlace-mode", G_TYPE_STRING,
|
||||
deinterlace->interlaced ? "progressive" : imode, NULL);
|
||||
ret = gst_pad_set_caps (deinterlace->srcpad, src_caps);
|
||||
gst_caps_unref (src_caps);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_ffmpegdeinterlace_sink_event (GstPad * pad, GstObject * parent,
|
||||
GstEvent * event)
|
||||
{
|
||||
GstFFMpegDeinterlace *deinterlace = GST_FFMPEGDEINTERLACE (parent);
|
||||
gboolean ret = FALSE;
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_CAPS:
|
||||
{
|
||||
GstCaps *caps;
|
||||
|
||||
gst_event_parse_caps (event, &caps);
|
||||
ret = gst_ffmpegdeinterlace_sink_setcaps (pad, caps);
|
||||
gst_event_unref (event);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = gst_pad_push_event (deinterlace->srcpad, event);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegdeinterlace_init (GstFFMpegDeinterlace * deinterlace)
|
||||
{
|
||||
deinterlace->sinkpad =
|
||||
gst_pad_new_from_static_template (&sink_factory, "sink");
|
||||
gst_pad_set_event_function (deinterlace->sinkpad,
|
||||
gst_ffmpegdeinterlace_sink_event);
|
||||
gst_pad_set_chain_function (deinterlace->sinkpad,
|
||||
gst_ffmpegdeinterlace_chain);
|
||||
gst_element_add_pad (GST_ELEMENT (deinterlace), deinterlace->sinkpad);
|
||||
|
||||
deinterlace->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
|
||||
gst_element_add_pad (GST_ELEMENT (deinterlace), deinterlace->srcpad);
|
||||
|
||||
deinterlace->pixfmt = AV_PIX_FMT_NB;
|
||||
|
||||
deinterlace->interlaced = FALSE;
|
||||
deinterlace->passthrough = FALSE;
|
||||
deinterlace->reconfigure = FALSE;
|
||||
deinterlace->mode = DEFAULT_MODE;
|
||||
deinterlace->new_mode = -1;
|
||||
deinterlace->last_width = -1;
|
||||
deinterlace->last_height = -1;
|
||||
deinterlace->last_pixfmt = AV_PIX_FMT_NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
delete_filter_graph (GstFFMpegDeinterlace * deinterlace)
|
||||
{
|
||||
if (deinterlace->filter_graph) {
|
||||
av_frame_free (&deinterlace->filter_frame);
|
||||
avfilter_graph_free (&deinterlace->filter_graph);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegdeinterlace_dispose (GObject * obj)
|
||||
{
|
||||
GstFFMpegDeinterlace *deinterlace = GST_FFMPEGDEINTERLACE (obj);
|
||||
|
||||
delete_filter_graph (deinterlace);
|
||||
|
||||
G_OBJECT_CLASS (gst_ffmpegdeinterlace_parent_class)->dispose (obj);
|
||||
}
|
||||
|
||||
static int
|
||||
init_filter_graph (GstFFMpegDeinterlace * deinterlace,
|
||||
enum AVPixelFormat pixfmt, int width, int height)
|
||||
{
|
||||
AVFilterInOut *inputs = NULL, *outputs = NULL;
|
||||
char args[512];
|
||||
int res;
|
||||
|
||||
delete_filter_graph (deinterlace);
|
||||
deinterlace->filter_graph = avfilter_graph_alloc ();
|
||||
snprintf (args, sizeof (args),
|
||||
"buffer=video_size=%dx%d:pix_fmt=%d:time_base=1/1:pixel_aspect=0/1[in];"
|
||||
"[in]yadif[out];" "[out]buffersink", width, height, pixfmt);
|
||||
res =
|
||||
avfilter_graph_parse2 (deinterlace->filter_graph, args, &inputs,
|
||||
&outputs);
|
||||
if (res < 0)
|
||||
return res;
|
||||
if (inputs || outputs)
|
||||
return -1;
|
||||
res = avfilter_graph_config (deinterlace->filter_graph, NULL);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
deinterlace->buffersrc_ctx =
|
||||
avfilter_graph_get_filter (deinterlace->filter_graph, "Parsed_buffer_0");
|
||||
deinterlace->buffersink_ctx =
|
||||
avfilter_graph_get_filter (deinterlace->filter_graph,
|
||||
"Parsed_buffersink_2");
|
||||
if (!deinterlace->buffersrc_ctx || !deinterlace->buffersink_ctx)
|
||||
return -1;
|
||||
deinterlace->filter_frame = av_frame_alloc ();
|
||||
deinterlace->last_width = width;
|
||||
deinterlace->last_height = height;
|
||||
deinterlace->last_pixfmt = pixfmt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
process_filter_graph (GstFFMpegDeinterlace * deinterlace, AVFrame * dst,
|
||||
const AVFrame * src, enum AVPixelFormat pixfmt, int width, int height)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (!deinterlace->filter_graph || width != deinterlace->last_width ||
|
||||
height != deinterlace->last_height
|
||||
|| pixfmt != deinterlace->last_pixfmt) {
|
||||
res = init_filter_graph (deinterlace, pixfmt, width, height);
|
||||
if (res < 0)
|
||||
return res;
|
||||
}
|
||||
|
||||
memcpy (deinterlace->filter_frame->data, src->data, sizeof (src->data));
|
||||
memcpy (deinterlace->filter_frame->linesize, src->linesize,
|
||||
sizeof (src->linesize));
|
||||
deinterlace->filter_frame->width = width;
|
||||
deinterlace->filter_frame->height = height;
|
||||
deinterlace->filter_frame->format = pixfmt;
|
||||
res =
|
||||
av_buffersrc_add_frame (deinterlace->buffersrc_ctx,
|
||||
deinterlace->filter_frame);
|
||||
if (res < 0)
|
||||
return res;
|
||||
res =
|
||||
av_buffersink_get_frame (deinterlace->buffersink_ctx,
|
||||
deinterlace->filter_frame);
|
||||
if (res < 0)
|
||||
return res;
|
||||
av_image_copy (dst->data, dst->linesize,
|
||||
(const uint8_t **) deinterlace->filter_frame->data,
|
||||
deinterlace->filter_frame->linesize, pixfmt, width, height);
|
||||
av_frame_unref (deinterlace->filter_frame);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_ffmpegdeinterlace_chain (GstPad * pad, GstObject * parent,
|
||||
GstBuffer * inbuf)
|
||||
{
|
||||
GstFFMpegDeinterlace *deinterlace = GST_FFMPEGDEINTERLACE (parent);
|
||||
GstBuffer *outbuf = NULL;
|
||||
GstFlowReturn result;
|
||||
GstMapInfo from_map, to_map;
|
||||
|
||||
GST_OBJECT_LOCK (deinterlace);
|
||||
if (deinterlace->reconfigure) {
|
||||
GstCaps *caps;
|
||||
|
||||
if ((gint) deinterlace->new_mode != -1)
|
||||
deinterlace->mode = deinterlace->new_mode;
|
||||
deinterlace->new_mode = -1;
|
||||
|
||||
deinterlace->reconfigure = FALSE;
|
||||
GST_OBJECT_UNLOCK (deinterlace);
|
||||
if ((caps = gst_pad_get_current_caps (deinterlace->srcpad))) {
|
||||
gst_ffmpegdeinterlace_sink_setcaps (deinterlace->sinkpad, caps);
|
||||
gst_caps_unref (caps);
|
||||
}
|
||||
} else {
|
||||
GST_OBJECT_UNLOCK (deinterlace);
|
||||
}
|
||||
|
||||
if (deinterlace->passthrough)
|
||||
return gst_pad_push (deinterlace->srcpad, inbuf);
|
||||
|
||||
outbuf = gst_buffer_new_and_alloc (deinterlace->to_size);
|
||||
|
||||
gst_buffer_map (inbuf, &from_map, GST_MAP_READ);
|
||||
gst_ffmpeg_avpicture_fill (&deinterlace->from_frame, from_map.data,
|
||||
deinterlace->pixfmt, deinterlace->width, deinterlace->height);
|
||||
|
||||
gst_buffer_map (outbuf, &to_map, GST_MAP_WRITE);
|
||||
gst_ffmpeg_avpicture_fill (&deinterlace->to_frame, to_map.data,
|
||||
deinterlace->pixfmt, deinterlace->width, deinterlace->height);
|
||||
|
||||
process_filter_graph (deinterlace, &deinterlace->to_frame,
|
||||
&deinterlace->from_frame, deinterlace->pixfmt, deinterlace->width,
|
||||
deinterlace->height);
|
||||
gst_buffer_unmap (outbuf, &to_map);
|
||||
gst_buffer_unmap (inbuf, &from_map);
|
||||
|
||||
gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
|
||||
|
||||
result = gst_pad_push (deinterlace->srcpad, outbuf);
|
||||
|
||||
gst_buffer_unref (inbuf);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_ffmpegdeinterlace_register (GstPlugin * plugin)
|
||||
{
|
||||
return gst_element_register (plugin, "avdeinterlace",
|
||||
GST_RANK_NONE, GST_TYPE_FFMPEGDEINTERLACE);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegdeinterlace_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstFFMpegDeinterlace *self;
|
||||
|
||||
g_return_if_fail (GST_IS_FFMPEGDEINTERLACE (object));
|
||||
self = GST_FFMPEGDEINTERLACE (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_MODE:{
|
||||
gint new_mode;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
new_mode = g_value_get_enum (value);
|
||||
if (self->mode != new_mode && gst_pad_has_current_caps (self->srcpad)) {
|
||||
self->reconfigure = TRUE;
|
||||
self->new_mode = new_mode;
|
||||
} else {
|
||||
self->mode = new_mode;
|
||||
gst_ffmpegdeinterlace_update_passthrough (self);
|
||||
}
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegdeinterlace_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstFFMpegDeinterlace *self;
|
||||
|
||||
g_return_if_fail (GST_IS_FFMPEGDEINTERLACE (object));
|
||||
self = GST_FFMPEGDEINTERLACE (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_MODE:
|
||||
g_value_set_enum (value, self->mode);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
|
||||
}
|
||||
}
|
2202
ext/libav/gstavdemux.c
Normal file
2202
ext/libav/gstavdemux.c
Normal file
File diff suppressed because it is too large
Load diff
962
ext/libav/gstavmux.c
Normal file
962
ext/libav/gstavmux.c
Normal file
|
@ -0,0 +1,962 @@
|
|||
/* 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 <string.h>
|
||||
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstcollectpads.h>
|
||||
|
||||
#include "gstav.h"
|
||||
#include "gstavcodecmap.h"
|
||||
#include "gstavutils.h"
|
||||
#include "gstavprotocol.h"
|
||||
|
||||
typedef struct _GstFFMpegMux GstFFMpegMux;
|
||||
typedef struct _GstFFMpegMuxPad GstFFMpegMuxPad;
|
||||
|
||||
struct _GstFFMpegMuxPad
|
||||
{
|
||||
GstCollectData collect; /* we extend the CollectData */
|
||||
|
||||
gint padnum;
|
||||
};
|
||||
|
||||
struct _GstFFMpegMux
|
||||
{
|
||||
GstElement element;
|
||||
|
||||
GstCollectPads *collect;
|
||||
/* We need to keep track of our pads, so we do so here. */
|
||||
GstPad *srcpad;
|
||||
|
||||
AVFormatContext *context;
|
||||
gboolean opened;
|
||||
|
||||
guint videopads, audiopads;
|
||||
|
||||
/*< private > */
|
||||
/* event_function is the collectpads default eventfunction */
|
||||
GstPadEventFunction event_function;
|
||||
int max_delay;
|
||||
int preload;
|
||||
};
|
||||
|
||||
typedef struct _GstFFMpegMuxClass GstFFMpegMuxClass;
|
||||
|
||||
struct _GstFFMpegMuxClass
|
||||
{
|
||||
GstElementClass parent_class;
|
||||
|
||||
AVOutputFormat *in_plugin;
|
||||
};
|
||||
|
||||
#define GST_TYPE_FFMPEGMUX \
|
||||
(gst_ffmpegdec_get_type())
|
||||
#define GST_FFMPEGMUX(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGMUX,GstFFMpegMux))
|
||||
#define GST_FFMPEGMUX_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGMUX,GstFFMpegMuxClass))
|
||||
#define GST_IS_FFMPEGMUX(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGMUX))
|
||||
#define GST_IS_FFMPEGMUX_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGMUX))
|
||||
|
||||
enum
|
||||
{
|
||||
/* FILL ME */
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_PRELOAD,
|
||||
PROP_MAXDELAY
|
||||
};
|
||||
|
||||
/* A number of function prototypes are given so we can refer to them later. */
|
||||
static void gst_ffmpegmux_class_init (GstFFMpegMuxClass * klass);
|
||||
static void gst_ffmpegmux_base_init (gpointer g_class);
|
||||
static void gst_ffmpegmux_init (GstFFMpegMux * ffmpegmux,
|
||||
GstFFMpegMuxClass * g_class);
|
||||
static void gst_ffmpegmux_finalize (GObject * object);
|
||||
|
||||
static gboolean gst_ffmpegmux_setcaps (GstPad * pad, GstCaps * caps);
|
||||
static GstPad *gst_ffmpegmux_request_new_pad (GstElement * element,
|
||||
GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
|
||||
static GstFlowReturn gst_ffmpegmux_collected (GstCollectPads * pads,
|
||||
gpointer user_data);
|
||||
|
||||
static gboolean gst_ffmpegmux_sink_event (GstPad * pad, GstObject * parent,
|
||||
GstEvent * event);
|
||||
|
||||
static GstStateChangeReturn gst_ffmpegmux_change_state (GstElement * element,
|
||||
GstStateChange transition);
|
||||
|
||||
static void gst_ffmpegmux_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void gst_ffmpegmux_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
static GstCaps *gst_ffmpegmux_get_id_caps (enum AVCodecID *id_list);
|
||||
static void gst_ffmpeg_mux_simple_caps_set_int_list (GstCaps * caps,
|
||||
const gchar * field, guint num, const gint * values);
|
||||
|
||||
#define GST_FFMUX_PARAMS_QDATA g_quark_from_static_string("avmux-params")
|
||||
|
||||
static GstElementClass *parent_class = NULL;
|
||||
|
||||
/*static guint gst_ffmpegmux_signals[LAST_SIGNAL] = { 0 }; */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *name;
|
||||
const char *replacement;
|
||||
} GstFFMpegMuxReplacement;
|
||||
|
||||
static const char *
|
||||
gst_ffmpegmux_get_replacement (const char *name)
|
||||
{
|
||||
static const GstFFMpegMuxReplacement blacklist[] = {
|
||||
{"avi", "avimux"},
|
||||
{"matroska", "matroskamux"},
|
||||
{"mov", "qtmux"},
|
||||
{"mpegts", "mpegtsmux"},
|
||||
{"mp4", "mp4mux"},
|
||||
{"mpjpeg", "multipartmux"},
|
||||
{"ogg", "oggmux"},
|
||||
{"wav", "wavenc"},
|
||||
{"webm", "webmmux"},
|
||||
{"mxf", "mxfmux"},
|
||||
{"3gp", "gppmux"},
|
||||
{"yuv4mpegpipe", "y4menc"},
|
||||
{"aiff", "aiffmux"},
|
||||
{"adts", "aacparse"},
|
||||
{"asf", "asfmux"},
|
||||
{"asf_stream", "asfmux"},
|
||||
{"flv", "flvmux"},
|
||||
{"mp3", "id3v2mux"},
|
||||
{"mp2", "id3v2mux"}
|
||||
};
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < sizeof (blacklist) / sizeof (blacklist[0]); i++) {
|
||||
if (strcmp (blacklist[i].name, name) == 0) {
|
||||
return blacklist[i].replacement;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_ffmpegmux_is_formatter (const char *name)
|
||||
{
|
||||
static const char *replace[] = {
|
||||
"mp2", "mp3", NULL
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; replace[i]; i++)
|
||||
if (strcmp (replace[i], name) == 0)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegmux_base_init (gpointer g_class)
|
||||
{
|
||||
GstFFMpegMuxClass *klass = (GstFFMpegMuxClass *) g_class;
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
||||
GstPadTemplate *videosinktempl, *audiosinktempl, *srctempl;
|
||||
AVOutputFormat *in_plugin;
|
||||
GstCaps *srccaps, *audiosinkcaps, *videosinkcaps;
|
||||
enum AVCodecID *video_ids = NULL, *audio_ids = NULL;
|
||||
gchar *longname, *description, *name;
|
||||
const char *replacement;
|
||||
gboolean is_formatter;
|
||||
|
||||
in_plugin =
|
||||
(AVOutputFormat *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
|
||||
GST_FFMUX_PARAMS_QDATA);
|
||||
g_assert (in_plugin != NULL);
|
||||
|
||||
name = g_strdup (in_plugin->name);
|
||||
g_strdelimit (name, ".,|-<> ", '_');
|
||||
|
||||
/* construct the element details struct */
|
||||
replacement = gst_ffmpegmux_get_replacement (in_plugin->name);
|
||||
is_formatter = gst_ffmpegmux_is_formatter (in_plugin->name);
|
||||
if (replacement != NULL) {
|
||||
longname =
|
||||
g_strdup_printf ("libav %s %s (not recommended, use %s instead)",
|
||||
in_plugin->long_name, is_formatter ? "formatter" : "muxer",
|
||||
replacement);
|
||||
description =
|
||||
g_strdup_printf ("libav %s %s (not recommended, use %s instead)",
|
||||
in_plugin->long_name, is_formatter ? "formatter" : "muxer",
|
||||
replacement);
|
||||
} else {
|
||||
longname = g_strdup_printf ("libav %s %s", in_plugin->long_name,
|
||||
is_formatter ? "formatter" : "muxer");
|
||||
description = g_strdup_printf ("libav %s %s", in_plugin->long_name,
|
||||
is_formatter ? "formatter" : "muxer");
|
||||
}
|
||||
gst_element_class_set_metadata (element_class, longname,
|
||||
is_formatter ? "Formatter/Metadata" : "Codec/Muxer", description,
|
||||
"Wim Taymans <wim.taymans@chello.be>, "
|
||||
"Ronald Bultje <rbultje@ronald.bitfreak.net>");
|
||||
g_free (longname);
|
||||
g_free (description);
|
||||
|
||||
/* Try to find the caps that belongs here */
|
||||
srccaps = gst_ffmpeg_formatid_to_caps (name);
|
||||
if (!srccaps) {
|
||||
GST_DEBUG ("Couldn't get source caps for muxer '%s', skipping", name);
|
||||
goto beach;
|
||||
}
|
||||
|
||||
if (!gst_ffmpeg_formatid_get_codecids (in_plugin->name,
|
||||
&video_ids, &audio_ids, in_plugin)) {
|
||||
gst_caps_unref (srccaps);
|
||||
GST_DEBUG ("Couldn't get sink caps for muxer '%s'. Most likely because "
|
||||
"no input format mapping exists.", name);
|
||||
goto beach;
|
||||
}
|
||||
|
||||
videosinkcaps = video_ids ? gst_ffmpegmux_get_id_caps (video_ids) : NULL;
|
||||
audiosinkcaps = audio_ids ? gst_ffmpegmux_get_id_caps (audio_ids) : NULL;
|
||||
|
||||
/* fix up allowed caps for some muxers */
|
||||
/* FIXME : This should be in gstffmpegcodecmap.c ! */
|
||||
if (strcmp (in_plugin->name, "flv") == 0) {
|
||||
const gint rates[] = { 44100, 22050, 11025 };
|
||||
|
||||
gst_ffmpeg_mux_simple_caps_set_int_list (audiosinkcaps, "rate", 3, rates);
|
||||
} else if (strcmp (in_plugin->name, "dv") == 0) {
|
||||
gst_caps_set_simple (audiosinkcaps,
|
||||
"rate", G_TYPE_INT, 48000, "channels", G_TYPE_INT, 2, NULL);
|
||||
|
||||
}
|
||||
|
||||
/* pad templates */
|
||||
srctempl = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, srccaps);
|
||||
gst_element_class_add_pad_template (element_class, srctempl);
|
||||
gst_caps_unref (srccaps);
|
||||
|
||||
if (audiosinkcaps) {
|
||||
audiosinktempl = gst_pad_template_new ("audio_%u",
|
||||
GST_PAD_SINK, GST_PAD_REQUEST, audiosinkcaps);
|
||||
gst_element_class_add_pad_template (element_class, audiosinktempl);
|
||||
gst_caps_unref (audiosinkcaps);
|
||||
}
|
||||
|
||||
if (videosinkcaps) {
|
||||
videosinktempl = gst_pad_template_new ("video_%u",
|
||||
GST_PAD_SINK, GST_PAD_REQUEST, videosinkcaps);
|
||||
gst_element_class_add_pad_template (element_class, videosinktempl);
|
||||
gst_caps_unref (videosinkcaps);
|
||||
}
|
||||
|
||||
beach:
|
||||
klass->in_plugin = in_plugin;
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegmux_class_init (GstFFMpegMuxClass * 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->set_property = GST_DEBUG_FUNCPTR (gst_ffmpegmux_set_property);
|
||||
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_ffmpegmux_get_property);
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_PRELOAD,
|
||||
g_param_spec_int ("preload", "preload",
|
||||
"Set the initial demux-decode delay (in microseconds)",
|
||||
0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_MAXDELAY,
|
||||
g_param_spec_int ("maxdelay", "maxdelay",
|
||||
"Set the maximum demux-decode delay (in microseconds)", 0, G_MAXINT,
|
||||
0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
gstelement_class->request_new_pad = gst_ffmpegmux_request_new_pad;
|
||||
gstelement_class->change_state = gst_ffmpegmux_change_state;
|
||||
gobject_class->finalize = gst_ffmpegmux_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegmux_init (GstFFMpegMux * ffmpegmux, GstFFMpegMuxClass * g_class)
|
||||
{
|
||||
GstElementClass *klass = GST_ELEMENT_CLASS (g_class);
|
||||
GstFFMpegMuxClass *oclass = (GstFFMpegMuxClass *) klass;
|
||||
GstPadTemplate *templ = gst_element_class_get_pad_template (klass, "src");
|
||||
|
||||
ffmpegmux->srcpad = gst_pad_new_from_template (templ, "src");
|
||||
gst_pad_set_caps (ffmpegmux->srcpad, gst_pad_template_get_caps (templ));
|
||||
gst_element_add_pad (GST_ELEMENT (ffmpegmux), ffmpegmux->srcpad);
|
||||
|
||||
ffmpegmux->collect = gst_collect_pads_new ();
|
||||
gst_collect_pads_set_function (ffmpegmux->collect,
|
||||
(GstCollectPadsFunction) gst_ffmpegmux_collected, ffmpegmux);
|
||||
|
||||
ffmpegmux->context = avformat_alloc_context ();
|
||||
ffmpegmux->context->oformat = oclass->in_plugin;
|
||||
ffmpegmux->context->nb_streams = 0;
|
||||
ffmpegmux->opened = FALSE;
|
||||
|
||||
ffmpegmux->videopads = 0;
|
||||
ffmpegmux->audiopads = 0;
|
||||
ffmpegmux->max_delay = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegmux_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstFFMpegMux *src;
|
||||
|
||||
src = (GstFFMpegMux *) object;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PRELOAD:
|
||||
src->preload = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_MAXDELAY:
|
||||
src->max_delay = g_value_get_int (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegmux_get_property (GObject * object, guint prop_id, GValue * value,
|
||||
GParamSpec * pspec)
|
||||
{
|
||||
GstFFMpegMux *src;
|
||||
|
||||
src = (GstFFMpegMux *) object;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PRELOAD:
|
||||
g_value_set_int (value, src->preload);
|
||||
break;
|
||||
case PROP_MAXDELAY:
|
||||
g_value_set_int (value, src->max_delay);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_ffmpegmux_finalize (GObject * object)
|
||||
{
|
||||
GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) object;
|
||||
|
||||
avformat_free_context (ffmpegmux->context);
|
||||
ffmpegmux->context = NULL;
|
||||
|
||||
gst_object_unref (ffmpegmux->collect);
|
||||
|
||||
if (G_OBJECT_CLASS (parent_class)->finalize)
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static GstPad *
|
||||
gst_ffmpegmux_request_new_pad (GstElement * element,
|
||||
GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
|
||||
{
|
||||
GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) element;
|
||||
GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
|
||||
GstFFMpegMuxPad *collect_pad;
|
||||
gchar *padname;
|
||||
GstPad *pad;
|
||||
AVStream *st;
|
||||
enum AVMediaType type;
|
||||
gint bitrate = 0, framesize = 0;
|
||||
|
||||
g_return_val_if_fail (templ != NULL, NULL);
|
||||
g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
|
||||
g_return_val_if_fail (ffmpegmux->opened == FALSE, NULL);
|
||||
|
||||
/* figure out a name that *we* like */
|
||||
if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
|
||||
padname = g_strdup_printf ("video_%u", ffmpegmux->videopads++);
|
||||
type = AVMEDIA_TYPE_VIDEO;
|
||||
bitrate = 64 * 1024;
|
||||
framesize = 1152;
|
||||
} else if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
|
||||
padname = g_strdup_printf ("audio_%u", ffmpegmux->audiopads++);
|
||||
type = AVMEDIA_TYPE_AUDIO;
|
||||
bitrate = 285 * 1024;
|
||||
} else {
|
||||
g_warning ("avmux: unknown pad template!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* create pad */
|
||||
pad = gst_pad_new_from_template (templ, padname);
|
||||
collect_pad = (GstFFMpegMuxPad *)
|
||||
gst_collect_pads_add_pad (ffmpegmux->collect, pad,
|
||||
sizeof (GstFFMpegMuxPad), NULL, TRUE);
|
||||
collect_pad->padnum = ffmpegmux->context->nb_streams;
|
||||
|
||||
/* small hack to put our own event pad function and chain up to collect pad */
|
||||
ffmpegmux->event_function = GST_PAD_EVENTFUNC (pad);
|
||||
gst_pad_set_event_function (pad,
|
||||
GST_DEBUG_FUNCPTR (gst_ffmpegmux_sink_event));
|
||||
|
||||
gst_element_add_pad (element, pad);
|
||||
|
||||
/* AVStream needs to be created */
|
||||
st = avformat_new_stream (ffmpegmux->context, NULL);
|
||||
st->id = collect_pad->padnum;
|
||||
st->codecpar->codec_type = type;
|
||||
st->codecpar->codec_id = AV_CODEC_ID_NONE; /* this is a check afterwards */
|
||||
st->codecpar->bit_rate = bitrate;
|
||||
st->codecpar->frame_size = framesize;
|
||||
/* we fill in codec during capsnego */
|
||||
|
||||
/* we love debug output (c) (tm) (r) */
|
||||
GST_DEBUG ("Created %s pad for avmux_%s element",
|
||||
padname, ((GstFFMpegMuxClass *) klass)->in_plugin->name);
|
||||
g_free (padname);
|
||||
|
||||
return pad;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_ffmpegmux_setcaps
|
||||
* @pad: #GstPad
|
||||
* @caps: New caps.
|
||||
*
|
||||
* Set caps to pad.
|
||||
*
|
||||
* Returns: #TRUE on success.
|
||||
*/
|
||||
static gboolean
|
||||
gst_ffmpegmux_setcaps (GstPad * pad, GstCaps * caps)
|
||||
{
|
||||
GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) (gst_pad_get_parent (pad));
|
||||
GstFFMpegMuxPad *collect_pad;
|
||||
AVStream *st;
|
||||
AVCodecContext tmp;
|
||||
|
||||
collect_pad = (GstFFMpegMuxPad *) gst_pad_get_element_private (pad);
|
||||
|
||||
st = ffmpegmux->context->streams[collect_pad->padnum];
|
||||
av_opt_set_int (ffmpegmux->context, "preload", ffmpegmux->preload, 0);
|
||||
ffmpegmux->context->max_delay = ffmpegmux->max_delay;
|
||||
memset (&tmp, 0, sizeof (tmp));
|
||||
|
||||
/* for the format-specific guesses, we'll go to
|
||||
* our famous codec mapper */
|
||||
if (gst_ffmpeg_caps_to_codecid (caps, &tmp) == AV_CODEC_ID_NONE)
|
||||
goto not_accepted;
|
||||
|
||||
avcodec_parameters_from_context (st->codecpar, &tmp);
|
||||
|
||||
/* copy over the aspect ratios, ffmpeg expects the stream aspect to match the
|
||||
* codec aspect. */
|
||||
st->sample_aspect_ratio = st->codecpar->sample_aspect_ratio;
|
||||
|
||||
GST_LOG_OBJECT (pad, "accepted caps %" GST_PTR_FORMAT, caps);
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
not_accepted:
|
||||
{
|
||||
GST_LOG_OBJECT (pad, "rejecting caps %" GST_PTR_FORMAT, caps);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
gst_ffmpegmux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
||||
{
|
||||
GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) parent;
|
||||
gboolean res = TRUE;
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_TAG:{
|
||||
GstTagList *taglist;
|
||||
GstTagSetter *setter = GST_TAG_SETTER (ffmpegmux);
|
||||
const GstTagMergeMode mode = gst_tag_setter_get_tag_merge_mode (setter);
|
||||
|
||||
gst_event_parse_tag (event, &taglist);
|
||||
gst_tag_setter_merge_tags (setter, taglist, mode);
|
||||
break;
|
||||
}
|
||||
case GST_EVENT_CAPS:{
|
||||
GstCaps *caps;
|
||||
gst_event_parse_caps (event, &caps);
|
||||
if (!(res = gst_ffmpegmux_setcaps (pad, caps)))
|
||||
goto beach;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* chaining up to collectpads default event function */
|
||||
res = ffmpegmux->event_function (pad, parent, event);
|
||||
|
||||
beach:
|
||||
return res;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_ffmpegmux_collected (GstCollectPads * pads, gpointer user_data)
|
||||
{
|
||||
GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) user_data;
|
||||
GSList *collected;
|
||||
GstFFMpegMuxPad *best_pad;
|
||||
GstClockTime best_time;
|
||||
#if 0
|
||||
/* Re-enable once converted to new AVMetaData API
|
||||
* See #566605
|
||||
*/
|
||||
const GstTagList *tags;
|
||||
#endif
|
||||
|
||||
/* open "file" (gstreamer protocol to next element) */
|
||||
if (!ffmpegmux->opened) {
|
||||
int open_flags = AVIO_FLAG_WRITE;
|
||||
|
||||
/* we do need all streams to have started capsnego,
|
||||
* or things will go horribly wrong */
|
||||
for (collected = ffmpegmux->collect->data; collected;
|
||||
collected = g_slist_next (collected)) {
|
||||
GstFFMpegMuxPad *collect_pad = (GstFFMpegMuxPad *) collected->data;
|
||||
AVStream *st = ffmpegmux->context->streams[collect_pad->padnum];
|
||||
|
||||
/* check whether the pad has successfully completed capsnego */
|
||||
if (st->codecpar->codec_id == AV_CODEC_ID_NONE) {
|
||||
GST_ELEMENT_ERROR (ffmpegmux, CORE, NEGOTIATION, (NULL),
|
||||
("no caps set on stream %d (%s)", collect_pad->padnum,
|
||||
(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) ?
|
||||
"video" : "audio"));
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
/* set framerate for audio */
|
||||
if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
switch (st->codecpar->codec_id) {
|
||||
case AV_CODEC_ID_PCM_S16LE:
|
||||
case AV_CODEC_ID_PCM_S16BE:
|
||||
case AV_CODEC_ID_PCM_U16LE:
|
||||
case AV_CODEC_ID_PCM_U16BE:
|
||||
case AV_CODEC_ID_PCM_S8:
|
||||
case AV_CODEC_ID_PCM_U8:
|
||||
st->codecpar->frame_size = 1;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
GstBuffer *buffer;
|
||||
|
||||
/* FIXME : This doesn't work for RAW AUDIO...
|
||||
* in fact I'm wondering if it even works for any kind of audio... */
|
||||
buffer = gst_collect_pads_peek (ffmpegmux->collect,
|
||||
(GstCollectData *) collect_pad);
|
||||
if (buffer) {
|
||||
st->codecpar->frame_size =
|
||||
st->codecpar->sample_rate *
|
||||
GST_BUFFER_DURATION (buffer) / GST_SECOND;
|
||||
gst_buffer_unref (buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Re-enable once converted to new AVMetaData API
|
||||
* See #566605
|
||||
*/
|
||||
|
||||
/* tags */
|
||||
tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (ffmpegmux));
|
||||
if (tags) {
|
||||
gint i;
|
||||
gchar *s;
|
||||
|
||||
/* get the interesting ones */
|
||||
if (gst_tag_list_get_string (tags, GST_TAG_TITLE, &s)) {
|
||||
strncpy (ffmpegmux->context->title, s,
|
||||
sizeof (ffmpegmux->context->title));
|
||||
}
|
||||
if (gst_tag_list_get_string (tags, GST_TAG_ARTIST, &s)) {
|
||||
strncpy (ffmpegmux->context->author, s,
|
||||
sizeof (ffmpegmux->context->author));
|
||||
}
|
||||
if (gst_tag_list_get_string (tags, GST_TAG_COPYRIGHT, &s)) {
|
||||
strncpy (ffmpegmux->context->copyright, s,
|
||||
sizeof (ffmpegmux->context->copyright));
|
||||
}
|
||||
if (gst_tag_list_get_string (tags, GST_TAG_COMMENT, &s)) {
|
||||
strncpy (ffmpegmux->context->comment, s,
|
||||
sizeof (ffmpegmux->context->comment));
|
||||
}
|
||||
if (gst_tag_list_get_string (tags, GST_TAG_ALBUM, &s)) {
|
||||
strncpy (ffmpegmux->context->album, s,
|
||||
sizeof (ffmpegmux->context->album));
|
||||
}
|
||||
if (gst_tag_list_get_string (tags, GST_TAG_GENRE, &s)) {
|
||||
strncpy (ffmpegmux->context->genre, s,
|
||||
sizeof (ffmpegmux->context->genre));
|
||||
}
|
||||
if (gst_tag_list_get_int (tags, GST_TAG_TRACK_NUMBER, &i)) {
|
||||
ffmpegmux->context->track = i;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* set the streamheader flag for gstffmpegprotocol if codec supports it */
|
||||
if (!strcmp (ffmpegmux->context->oformat->name, "flv")) {
|
||||
open_flags |= GST_FFMPEG_URL_STREAMHEADER;
|
||||
}
|
||||
|
||||
/* some house-keeping for downstream before starting data flow */
|
||||
/* stream-start (FIXME: create id based on input ids) */
|
||||
{
|
||||
gchar s_id[32];
|
||||
|
||||
g_snprintf (s_id, sizeof (s_id), "avmux-%08x", g_random_int ());
|
||||
gst_pad_push_event (ffmpegmux->srcpad, gst_event_new_stream_start (s_id));
|
||||
}
|
||||
/* segment */
|
||||
{
|
||||
GstSegment segment;
|
||||
|
||||
/* let downstream know we think in BYTES and expect to do seeking later on */
|
||||
gst_segment_init (&segment, GST_FORMAT_BYTES);
|
||||
gst_pad_push_event (ffmpegmux->srcpad, gst_event_new_segment (&segment));
|
||||
}
|
||||
|
||||
if (gst_ffmpegdata_open (ffmpegmux->srcpad, open_flags,
|
||||
&ffmpegmux->context->pb) < 0) {
|
||||
GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, TOO_LAZY, (NULL),
|
||||
("Failed to open stream context in avmux"));
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
/* now open the mux format */
|
||||
if (avformat_write_header (ffmpegmux->context, NULL) < 0) {
|
||||
GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, SETTINGS, (NULL),
|
||||
("Failed to write file header - check codec settings"));
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
/* we're now opened */
|
||||
ffmpegmux->opened = TRUE;
|
||||
|
||||
/* flush the header so it will be used as streamheader */
|
||||
avio_flush (ffmpegmux->context->pb);
|
||||
}
|
||||
|
||||
/* take the one with earliest timestamp,
|
||||
* and push it forward */
|
||||
best_pad = NULL;
|
||||
best_time = GST_CLOCK_TIME_NONE;
|
||||
for (collected = ffmpegmux->collect->data; collected;
|
||||
collected = g_slist_next (collected)) {
|
||||
GstFFMpegMuxPad *collect_pad = (GstFFMpegMuxPad *) collected->data;
|
||||
GstBuffer *buffer = gst_collect_pads_peek (ffmpegmux->collect,
|
||||
(GstCollectData *) collect_pad);
|
||||
|
||||
/* if there's no buffer, just continue */
|
||||
if (buffer == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if we have no buffer yet, just use the first one */
|
||||
if (best_pad == NULL) {
|
||||
best_pad = collect_pad;
|
||||
best_time = GST_BUFFER_TIMESTAMP (buffer);
|
||||
goto next_pad;
|
||||
}
|
||||
|
||||
/* if we do have one, only use this one if it's older */
|
||||
if (GST_BUFFER_TIMESTAMP (buffer) < best_time) {
|
||||
best_time = GST_BUFFER_TIMESTAMP (buffer);
|
||||
best_pad = collect_pad;
|
||||
}
|
||||
|
||||
next_pad:
|
||||
gst_buffer_unref (buffer);
|
||||
|
||||
/* Mux buffers with invalid timestamp first */
|
||||
if (!GST_CLOCK_TIME_IS_VALID (best_time))
|
||||
break;
|
||||
}
|
||||
|
||||
/* now handle the buffer, or signal EOS if we have
|
||||
* no buffers left */
|
||||
if (best_pad != NULL) {
|
||||
GstBuffer *buf;
|
||||
AVPacket pkt = { 0, };
|
||||
GstMapInfo map;
|
||||
|
||||
/* push out current buffer */
|
||||
buf =
|
||||
gst_collect_pads_pop (ffmpegmux->collect, (GstCollectData *) best_pad);
|
||||
|
||||
/* set time */
|
||||
pkt.pts = gst_ffmpeg_time_gst_to_ff (GST_BUFFER_TIMESTAMP (buf),
|
||||
ffmpegmux->context->streams[best_pad->padnum]->time_base);
|
||||
pkt.dts = pkt.pts;
|
||||
|
||||
gst_buffer_map (buf, &map, GST_MAP_READ);
|
||||
pkt.data = map.data;
|
||||
pkt.size = map.size;
|
||||
|
||||
pkt.stream_index = best_pad->padnum;
|
||||
|
||||
if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
|
||||
pkt.flags |= AV_PKT_FLAG_KEY;
|
||||
|
||||
if (GST_BUFFER_DURATION_IS_VALID (buf))
|
||||
pkt.duration =
|
||||
gst_ffmpeg_time_gst_to_ff (GST_BUFFER_DURATION (buf),
|
||||
ffmpegmux->context->streams[best_pad->padnum]->time_base);
|
||||
av_write_frame (ffmpegmux->context, &pkt);
|
||||
gst_buffer_unmap (buf, &map);
|
||||
gst_buffer_unref (buf);
|
||||
} else {
|
||||
/* close down */
|
||||
av_write_trailer (ffmpegmux->context);
|
||||
ffmpegmux->opened = FALSE;
|
||||
avio_flush (ffmpegmux->context->pb);
|
||||
gst_ffmpegdata_close (ffmpegmux->context->pb);
|
||||
gst_pad_push_event (ffmpegmux->srcpad, gst_event_new_eos ());
|
||||
return GST_FLOW_EOS;
|
||||
}
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_ffmpegmux_change_state (GstElement * element, GstStateChange transition)
|
||||
{
|
||||
GstStateChangeReturn ret;
|
||||
GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) (element);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_NULL_TO_READY:
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||
gst_collect_pads_start (ffmpegmux->collect);
|
||||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
gst_collect_pads_stop (ffmpegmux->collect);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
gst_tag_setter_reset_tags (GST_TAG_SETTER (ffmpegmux));
|
||||
if (ffmpegmux->opened) {
|
||||
ffmpegmux->opened = FALSE;
|
||||
gst_ffmpegdata_close (ffmpegmux->context->pb);
|
||||
}
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_NULL:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_ffmpegmux_get_id_caps (enum AVCodecID *id_list)
|
||||
{
|
||||
GstCaps *caps, *t;
|
||||
gint i;
|
||||
|
||||
caps = gst_caps_new_empty ();
|
||||
for (i = 0; id_list[i] != AV_CODEC_ID_NONE; i++) {
|
||||
if ((t = gst_ffmpeg_codecid_to_caps (id_list[i], NULL, TRUE)))
|
||||
gst_caps_append (caps, t);
|
||||
}
|
||||
if (gst_caps_is_empty (caps)) {
|
||||
gst_caps_unref (caps);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
/* set a list of integer values on the caps, e.g. for sample rates */
|
||||
static void
|
||||
gst_ffmpeg_mux_simple_caps_set_int_list (GstCaps * caps, const gchar * field,
|
||||
guint num, const gint * values)
|
||||
{
|
||||
GValue list = { 0, };
|
||||
GValue val = { 0, };
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (GST_CAPS_IS_SIMPLE (caps));
|
||||
|
||||
g_value_init (&list, GST_TYPE_LIST);
|
||||
g_value_init (&val, G_TYPE_INT);
|
||||
|
||||
for (i = 0; i < num; ++i) {
|
||||
g_value_set_int (&val, values[i]);
|
||||
gst_value_list_append_value (&list, &val);
|
||||
}
|
||||
|
||||
gst_structure_set_value (gst_caps_get_structure (caps, 0), field, &list);
|
||||
|
||||
g_value_unset (&val);
|
||||
g_value_unset (&list);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_ffmpegmux_register (GstPlugin * plugin)
|
||||
{
|
||||
GTypeInfo typeinfo = {
|
||||
sizeof (GstFFMpegMuxClass),
|
||||
(GBaseInitFunc) gst_ffmpegmux_base_init,
|
||||
NULL,
|
||||
(GClassInitFunc) gst_ffmpegmux_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof (GstFFMpegMux),
|
||||
0,
|
||||
(GInstanceInitFunc) gst_ffmpegmux_init,
|
||||
};
|
||||
static const GInterfaceInfo tag_setter_info = {
|
||||
NULL, NULL, NULL
|
||||
};
|
||||
GType type;
|
||||
const AVOutputFormat *in_plugin;
|
||||
void *i = 0;
|
||||
|
||||
GST_LOG ("Registering muxers");
|
||||
|
||||
while ((in_plugin = av_muxer_iterate (&i))) {
|
||||
gchar *type_name;
|
||||
GstRank rank = GST_RANK_MARGINAL;
|
||||
|
||||
if ((!strncmp (in_plugin->name, "u16", 3)) ||
|
||||
(!strncmp (in_plugin->name, "s16", 3)) ||
|
||||
(!strncmp (in_plugin->name, "u24", 3)) ||
|
||||
(!strncmp (in_plugin->name, "s24", 3)) ||
|
||||
(!strncmp (in_plugin->name, "u8", 2)) ||
|
||||
(!strncmp (in_plugin->name, "s8", 2)) ||
|
||||
(!strncmp (in_plugin->name, "u32", 3)) ||
|
||||
(!strncmp (in_plugin->name, "s32", 3)) ||
|
||||
(!strncmp (in_plugin->name, "f32", 3)) ||
|
||||
(!strncmp (in_plugin->name, "f64", 3)) ||
|
||||
(!strncmp (in_plugin->name, "raw", 3)) ||
|
||||
(!strncmp (in_plugin->name, "crc", 3)) ||
|
||||
(!strncmp (in_plugin->name, "null", 4)) ||
|
||||
(!strncmp (in_plugin->name, "gif", 3)) ||
|
||||
(!strncmp (in_plugin->name, "fifo", 4)) ||
|
||||
(!strncmp (in_plugin->name, "frame", 5)) ||
|
||||
(!strncmp (in_plugin->name, "image", 5)) ||
|
||||
(!strncmp (in_plugin->name, "mulaw", 5)) ||
|
||||
(!strncmp (in_plugin->name, "alaw", 4)) ||
|
||||
(!strncmp (in_plugin->name, "h26", 3)) ||
|
||||
(!strncmp (in_plugin->name, "rtp", 3)) ||
|
||||
(!strncmp (in_plugin->name, "ass", 3)) ||
|
||||
(!strncmp (in_plugin->name, "ffmetadata", 10)) ||
|
||||
(!strncmp (in_plugin->name, "srt", 3)) ||
|
||||
(!strncmp (in_plugin->name, "scc", 3)) ||
|
||||
!strcmp (in_plugin->name, "ttml") ||
|
||||
!strcmp (in_plugin->name, "segment") ||
|
||||
!strcmp (in_plugin->name, "stream_segment,ssegment") ||
|
||||
!strcmp (in_plugin->name, "jacosub") ||
|
||||
!strcmp (in_plugin->name, "webvtt") ||
|
||||
!strcmp (in_plugin->name, "lrc") ||
|
||||
!strcmp (in_plugin->name, "microdvd") ||
|
||||
!strcmp (in_plugin->name, "tee") ||
|
||||
!strncmp (in_plugin->name, "webm", 4)
|
||||
) {
|
||||
GST_LOG ("Ignoring muxer %s", in_plugin->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_plugin->long_name != NULL) {
|
||||
if ((!strncmp (in_plugin->long_name, "raw ", 4))) {
|
||||
GST_LOG ("Ignoring raw muxer %s", in_plugin->name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (gst_ffmpegmux_get_replacement (in_plugin->name))
|
||||
rank = GST_RANK_NONE;
|
||||
|
||||
/* FIXME : We need a fast way to know whether we have mappings for this
|
||||
* muxer type. */
|
||||
|
||||
/* construct the type */
|
||||
type_name = g_strdup_printf ("avmux_%s", in_plugin->name);
|
||||
g_strdelimit (type_name, ".,|-<> ", '_');
|
||||
|
||||
type = g_type_from_name (type_name);
|
||||
|
||||
if (!type) {
|
||||
/* create the type now */
|
||||
type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
|
||||
g_type_set_qdata (type, GST_FFMUX_PARAMS_QDATA, (gpointer) in_plugin);
|
||||
g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
|
||||
}
|
||||
|
||||
if (!gst_element_register (plugin, type_name, rank, type)) {
|
||||
g_free (type_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_free (type_name);
|
||||
}
|
||||
|
||||
GST_LOG ("Finished registering muxers");
|
||||
|
||||
return TRUE;
|
||||
}
|
369
ext/libav/gstavprotocol.c
Normal file
369
ext/libav/gstavprotocol.c
Normal file
|
@ -0,0 +1,369 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||
* <2006> Edward Hervey <bilboed@bilboed.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 <errno.h>
|
||||
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "gstav.h"
|
||||
#include "gstavprotocol.h"
|
||||
|
||||
typedef struct _GstProtocolInfo GstProtocolInfo;
|
||||
|
||||
struct _GstProtocolInfo
|
||||
{
|
||||
GstPad *pad;
|
||||
|
||||
guint64 offset;
|
||||
gboolean eos;
|
||||
gint set_streamheader;
|
||||
};
|
||||
|
||||
static int
|
||||
gst_ffmpegdata_peek (void *priv_data, unsigned char *buf, int size)
|
||||
{
|
||||
GstProtocolInfo *info;
|
||||
GstBuffer *inbuf = NULL;
|
||||
GstFlowReturn ret;
|
||||
int total = 0;
|
||||
|
||||
info = (GstProtocolInfo *) priv_data;
|
||||
|
||||
GST_DEBUG ("Pulling %d bytes at position %" G_GUINT64_FORMAT, size,
|
||||
info->offset);
|
||||
|
||||
ret = gst_pad_pull_range (info->pad, info->offset, (guint) size, &inbuf);
|
||||
|
||||
switch (ret) {
|
||||
case GST_FLOW_OK:
|
||||
total = (gint) gst_buffer_get_size (inbuf);
|
||||
gst_buffer_extract (inbuf, 0, buf, total);
|
||||
gst_buffer_unref (inbuf);
|
||||
break;
|
||||
case GST_FLOW_EOS:
|
||||
total = 0;
|
||||
break;
|
||||
case GST_FLOW_FLUSHING:
|
||||
total = -1;
|
||||
break;
|
||||
default:
|
||||
case GST_FLOW_ERROR:
|
||||
total = -2;
|
||||
break;
|
||||
}
|
||||
|
||||
GST_DEBUG ("Got %d (%s) return result %d", ret, gst_flow_get_name (ret),
|
||||
total);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static int
|
||||
gst_ffmpegdata_read (void *priv_data, unsigned char *buf, int size)
|
||||
{
|
||||
gint res;
|
||||
GstProtocolInfo *info;
|
||||
|
||||
info = (GstProtocolInfo *) priv_data;
|
||||
|
||||
GST_DEBUG ("Reading %d bytes of data at position %" G_GUINT64_FORMAT, size,
|
||||
info->offset);
|
||||
|
||||
res = gst_ffmpegdata_peek (priv_data, buf, size);
|
||||
if (res >= 0)
|
||||
info->offset += res;
|
||||
|
||||
GST_DEBUG ("Returning %d bytes", res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
gst_ffmpegdata_write (void *priv_data, uint8_t * buf, int size)
|
||||
{
|
||||
GstProtocolInfo *info;
|
||||
GstBuffer *outbuf;
|
||||
|
||||
GST_DEBUG ("Writing %d bytes", size);
|
||||
info = (GstProtocolInfo *) priv_data;
|
||||
|
||||
/* create buffer and push data further */
|
||||
outbuf = gst_buffer_new_and_alloc (size);
|
||||
|
||||
gst_buffer_fill (outbuf, 0, buf, size);
|
||||
|
||||
if (gst_pad_push (info->pad, outbuf) != GST_FLOW_OK)
|
||||
return 0;
|
||||
|
||||
info->offset += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
gst_ffmpegdata_seek (void *priv_data, int64_t pos, int whence)
|
||||
{
|
||||
GstProtocolInfo *info;
|
||||
guint64 newpos = 0, oldpos;
|
||||
|
||||
GST_DEBUG ("Seeking to %" G_GINT64_FORMAT ", whence=%d",
|
||||
(gint64) pos, whence);
|
||||
|
||||
info = (GstProtocolInfo *) priv_data;
|
||||
|
||||
/* TODO : if we are push-based, we need to return sensible info */
|
||||
|
||||
if (GST_PAD_IS_SINK (info->pad)) {
|
||||
/* sinkpad */
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
newpos = (guint64) pos;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
newpos = info->offset + pos;
|
||||
break;
|
||||
case SEEK_END:
|
||||
case AVSEEK_SIZE:
|
||||
/* ffmpeg wants to know the current end position in bytes ! */
|
||||
{
|
||||
gint64 duration;
|
||||
|
||||
GST_DEBUG ("Seek end");
|
||||
|
||||
if (gst_pad_is_linked (info->pad))
|
||||
if (gst_pad_query_duration (GST_PAD_PEER (info->pad),
|
||||
GST_FORMAT_BYTES, &duration))
|
||||
newpos = ((guint64) duration) + pos;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert (0);
|
||||
break;
|
||||
}
|
||||
/* FIXME : implement case for push-based behaviour */
|
||||
if (whence != AVSEEK_SIZE)
|
||||
info->offset = newpos;
|
||||
} else if (GST_PAD_IS_SRC (info->pad)) {
|
||||
GstSegment segment;
|
||||
|
||||
oldpos = info->offset;
|
||||
|
||||
/* srcpad */
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
{
|
||||
info->offset = (guint64) pos;
|
||||
break;
|
||||
}
|
||||
case SEEK_CUR:
|
||||
info->offset += pos;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
newpos = info->offset;
|
||||
|
||||
if (newpos != oldpos) {
|
||||
gst_segment_init (&segment, GST_FORMAT_BYTES);
|
||||
segment.start = newpos;
|
||||
segment.time = newpos;
|
||||
gst_pad_push_event (info->pad, gst_event_new_segment (&segment));
|
||||
}
|
||||
} else {
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
GST_DEBUG ("Now at offset %" G_GUINT64_FORMAT " (returning %" G_GUINT64_FORMAT
|
||||
")", info->offset, newpos);
|
||||
return newpos;
|
||||
}
|
||||
|
||||
int
|
||||
gst_ffmpegdata_close (AVIOContext * h)
|
||||
{
|
||||
GstProtocolInfo *info;
|
||||
|
||||
if (h == NULL)
|
||||
return 0;
|
||||
|
||||
info = (GstProtocolInfo *) h->opaque;
|
||||
if (info == NULL)
|
||||
return 0;
|
||||
|
||||
GST_LOG ("Closing file");
|
||||
|
||||
if (GST_PAD_IS_SRC (info->pad)) {
|
||||
/* send EOS - that closes down the stream */
|
||||
gst_pad_push_event (info->pad, gst_event_new_eos ());
|
||||
}
|
||||
|
||||
/* clean up data */
|
||||
g_free (info);
|
||||
h->opaque = NULL;
|
||||
|
||||
av_freep (&h->buffer);
|
||||
av_free (h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
gst_ffmpegdata_open (GstPad * pad, int flags, AVIOContext ** context)
|
||||
{
|
||||
GstProtocolInfo *info;
|
||||
static const int buffer_size = 4096;
|
||||
unsigned char *buffer = NULL;
|
||||
|
||||
info = g_new0 (GstProtocolInfo, 1);
|
||||
|
||||
info->set_streamheader = flags & GST_FFMPEG_URL_STREAMHEADER;
|
||||
flags &= ~GST_FFMPEG_URL_STREAMHEADER;
|
||||
|
||||
/* we don't support R/W together */
|
||||
if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) {
|
||||
GST_WARNING ("Only read-only or write-only are supported");
|
||||
g_free (info);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* make sure we're a pad and that we're of the right type */
|
||||
g_return_val_if_fail (GST_IS_PAD (pad), -EINVAL);
|
||||
|
||||
if ((flags & AVIO_FLAG_READ))
|
||||
g_return_val_if_fail (GST_PAD_IS_SINK (pad), -EINVAL);
|
||||
if ((flags & AVIO_FLAG_WRITE))
|
||||
g_return_val_if_fail (GST_PAD_IS_SRC (pad), -EINVAL);
|
||||
|
||||
info->eos = FALSE;
|
||||
info->pad = pad;
|
||||
info->offset = 0;
|
||||
|
||||
buffer = av_malloc (buffer_size);
|
||||
if (buffer == NULL) {
|
||||
GST_WARNING ("Failed to allocate buffer");
|
||||
g_free (info);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*context =
|
||||
avio_alloc_context (buffer, buffer_size, flags, (void *) info,
|
||||
gst_ffmpegdata_read, gst_ffmpegdata_write, gst_ffmpegdata_seek);
|
||||
if (*context == NULL) {
|
||||
GST_WARNING ("Failed to allocate memory");
|
||||
g_free (info);
|
||||
av_free (buffer);
|
||||
return -ENOMEM;
|
||||
}
|
||||
(*context)->seekable = AVIO_SEEKABLE_NORMAL;
|
||||
if (!(flags & AVIO_FLAG_WRITE)) {
|
||||
(*context)->buf_ptr = (*context)->buf_end;
|
||||
(*context)->write_flag = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* specialized protocol for cross-thread pushing,
|
||||
* based on ffmpeg's pipe protocol */
|
||||
|
||||
static int
|
||||
gst_ffmpeg_pipe_read (void *priv_data, uint8_t * buf, int size)
|
||||
{
|
||||
GstFFMpegPipe *ffpipe;
|
||||
guint available;
|
||||
|
||||
ffpipe = (GstFFMpegPipe *) priv_data;
|
||||
|
||||
GST_LOG ("requested size %d", size);
|
||||
|
||||
GST_FFMPEG_PIPE_MUTEX_LOCK (ffpipe);
|
||||
|
||||
GST_LOG ("requested size %d", size);
|
||||
|
||||
while ((available = gst_adapter_available (ffpipe->adapter)) < size
|
||||
&& !ffpipe->eos) {
|
||||
GST_DEBUG ("Available:%d, requested:%d", available, size);
|
||||
ffpipe->needed = size;
|
||||
GST_FFMPEG_PIPE_SIGNAL (ffpipe);
|
||||
GST_FFMPEG_PIPE_WAIT (ffpipe);
|
||||
}
|
||||
|
||||
size = MIN (available, size);
|
||||
if (size) {
|
||||
GST_LOG ("Getting %d bytes", size);
|
||||
gst_adapter_copy (ffpipe->adapter, buf, 0, size);
|
||||
gst_adapter_flush (ffpipe->adapter, size);
|
||||
GST_LOG ("%" G_GSIZE_FORMAT " bytes left in adapter",
|
||||
gst_adapter_available (ffpipe->adapter));
|
||||
ffpipe->needed = 0;
|
||||
}
|
||||
GST_FFMPEG_PIPE_MUTEX_UNLOCK (ffpipe);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int
|
||||
gst_ffmpeg_pipe_close (AVIOContext * h)
|
||||
{
|
||||
GST_LOG ("Closing pipe");
|
||||
|
||||
if (h == NULL)
|
||||
return 0;
|
||||
|
||||
h->opaque = NULL;
|
||||
av_freep (&h->buffer);
|
||||
av_free (h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
gst_ffmpeg_pipe_open (GstFFMpegPipe * ffpipe, int flags, AVIOContext ** context)
|
||||
{
|
||||
static const int buffer_size = 4096;
|
||||
unsigned char *buffer = NULL;
|
||||
|
||||
/* sanity check */
|
||||
g_return_val_if_fail (GST_IS_ADAPTER (ffpipe->adapter), -EINVAL);
|
||||
|
||||
buffer = av_malloc (buffer_size);
|
||||
if (buffer == NULL) {
|
||||
GST_WARNING ("Failed to allocate buffer");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*context =
|
||||
avio_alloc_context (buffer, buffer_size, 0, (void *) ffpipe,
|
||||
gst_ffmpeg_pipe_read, NULL, NULL);
|
||||
if (*context == NULL) {
|
||||
GST_WARNING ("Failed to allocate memory");
|
||||
av_free (buffer);
|
||||
return -ENOMEM;
|
||||
}
|
||||
(*context)->seekable = 0;
|
||||
(*context)->buf_ptr = (*context)->buf_end;
|
||||
|
||||
return 0;
|
||||
}
|
78
ext/libav/gstavprotocol.h
Normal file
78
ext/libav/gstavprotocol.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <2006> Mark Nauwelaerts <manauw@skynet.be>
|
||||
*
|
||||
* 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_FFMPEGPROTOCOL_H__
|
||||
#define __GST_FFMPEGPROTOCOL_H__
|
||||
|
||||
#include <gst/base/gstadapter.h>
|
||||
#include "gstav.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* pipe protocol helpers */
|
||||
#define GST_FFMPEG_PIPE_MUTEX_LOCK(m) G_STMT_START { \
|
||||
GST_LOG ("locking tlock from thread %p", g_thread_self ()); \
|
||||
g_mutex_lock (&m->tlock); \
|
||||
GST_LOG ("locked tlock from thread %p", g_thread_self ()); \
|
||||
} G_STMT_END
|
||||
|
||||
#define GST_FFMPEG_PIPE_MUTEX_UNLOCK(m) G_STMT_START { \
|
||||
GST_LOG ("unlocking tlock from thread %p", g_thread_self ()); \
|
||||
g_mutex_unlock (&m->tlock); \
|
||||
} G_STMT_END
|
||||
|
||||
#define GST_FFMPEG_PIPE_WAIT(m) G_STMT_START { \
|
||||
GST_LOG ("thread %p waiting", g_thread_self ()); \
|
||||
g_cond_wait (&m->cond, &m->tlock); \
|
||||
} G_STMT_END
|
||||
|
||||
#define GST_FFMPEG_PIPE_SIGNAL(m) G_STMT_START { \
|
||||
GST_LOG ("signalling from thread %p", g_thread_self ()); \
|
||||
g_cond_signal (&m->cond); \
|
||||
} G_STMT_END
|
||||
|
||||
typedef struct _GstFFMpegPipe GstFFMpegPipe;
|
||||
|
||||
struct _GstFFMpegPipe
|
||||
{
|
||||
/* lock for syncing */
|
||||
GMutex tlock;
|
||||
/* with TLOCK */
|
||||
/* signals counterpart thread to have a look */
|
||||
GCond cond;
|
||||
/* seen eos */
|
||||
gboolean eos;
|
||||
/* flowreturn obtained by src task */
|
||||
GstFlowReturn srcresult;
|
||||
/* adpater collecting data */
|
||||
GstAdapter *adapter;
|
||||
/* amount needed in adapter by src task */
|
||||
guint needed;
|
||||
};
|
||||
|
||||
int gst_ffmpeg_pipe_open (GstFFMpegPipe *ffpipe, int flags, AVIOContext ** context);
|
||||
int gst_ffmpeg_pipe_close (AVIOContext * h);
|
||||
|
||||
int gst_ffmpegdata_open (GstPad * pad, int flags, AVIOContext ** context);
|
||||
int gst_ffmpegdata_close (AVIOContext * h);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_FFMPEGPROTOCOL_H__ */
|
495
ext/libav/gstavutils.c
Normal file
495
ext/libav/gstavutils.c
Normal file
|
@ -0,0 +1,495 @@
|
|||
/* GStreamer
|
||||
* Copyright (c) 2009 Edward Hervey <bilboed@bilboed.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 "gstavutils.h"
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
#ifdef __MINGW32__
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include <libavutil/mem.h>
|
||||
|
||||
const gchar *
|
||||
gst_ffmpeg_get_codecid_longname (enum AVCodecID codec_id)
|
||||
{
|
||||
AVCodec *codec;
|
||||
/* Let's use what ffmpeg can provide us */
|
||||
|
||||
if ((codec = avcodec_find_decoder (codec_id)) ||
|
||||
(codec = avcodec_find_encoder (codec_id)))
|
||||
return codec->long_name;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gint
|
||||
av_smp_format_depth (enum AVSampleFormat smp_fmt)
|
||||
{
|
||||
gint depth = -1;
|
||||
switch (smp_fmt) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
case AV_SAMPLE_FMT_U8P:
|
||||
depth = 1;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
depth = 2;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
depth = 4;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_DBL:
|
||||
case AV_SAMPLE_FMT_DBLP:
|
||||
depth = 8;
|
||||
break;
|
||||
default:
|
||||
GST_ERROR ("UNHANDLED SAMPLE FORMAT !");
|
||||
break;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fill in pointers to memory in a AVFrame, where
|
||||
* everything is aligned by 4 (as required by X).
|
||||
* This is mostly a copy from imgconvert.c with some
|
||||
* small changes.
|
||||
*/
|
||||
|
||||
#define FF_COLOR_RGB 0 /* RGB color space */
|
||||
#define FF_COLOR_GRAY 1 /* gray color space */
|
||||
#define FF_COLOR_YUV 2 /* YUV color space. 16 <= Y <= 235, 16 <= U, V <= 240 */
|
||||
#define FF_COLOR_YUV_JPEG 3 /* YUV color space. 0 <= Y <= 255, 0 <= U, V <= 255 */
|
||||
|
||||
#define FF_PIXEL_PLANAR 0 /* each channel has one component in AVFrame */
|
||||
#define FF_PIXEL_PACKED 1 /* only one components containing all the channels */
|
||||
#define FF_PIXEL_PALETTE 2 /* one components containing indexes for a palette */
|
||||
|
||||
typedef struct PixFmtInfo
|
||||
{
|
||||
const char *name;
|
||||
uint8_t nb_channels; /* number of channels (including alpha) */
|
||||
uint8_t color_type; /* color type (see FF_COLOR_xxx constants) */
|
||||
uint8_t pixel_type; /* pixel storage type (see FF_PIXEL_xxx constants) */
|
||||
uint8_t is_alpha:1; /* true if alpha can be specified */
|
||||
uint8_t x_chroma_shift; /* X chroma subsampling factor is 2 ^ shift */
|
||||
uint8_t y_chroma_shift; /* Y chroma subsampling factor is 2 ^ shift */
|
||||
uint8_t depth; /* bit depth of the color components */
|
||||
} PixFmtInfo;
|
||||
|
||||
|
||||
/* this table gives more information about formats */
|
||||
static PixFmtInfo pix_fmt_info[AV_PIX_FMT_NB];
|
||||
void
|
||||
gst_ffmpeg_init_pix_fmt_info (void)
|
||||
{
|
||||
/* YUV formats */
|
||||
pix_fmt_info[AV_PIX_FMT_YUV420P].name = g_strdup ("yuv420p");
|
||||
pix_fmt_info[AV_PIX_FMT_YUV420P].nb_channels = 3;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV420P].color_type = FF_COLOR_YUV;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV420P].pixel_type = FF_PIXEL_PLANAR;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV420P].depth = 8,
|
||||
pix_fmt_info[AV_PIX_FMT_YUV420P].x_chroma_shift = 1,
|
||||
pix_fmt_info[AV_PIX_FMT_YUV420P].y_chroma_shift = 1;
|
||||
|
||||
pix_fmt_info[AV_PIX_FMT_YUV422P].name = g_strdup ("yuv422p");
|
||||
pix_fmt_info[AV_PIX_FMT_YUV422P].nb_channels = 3;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV422P].color_type = FF_COLOR_YUV;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV422P].pixel_type = FF_PIXEL_PLANAR;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV422P].depth = 8;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV422P].x_chroma_shift = 1;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV422P].y_chroma_shift = 0;
|
||||
|
||||
pix_fmt_info[AV_PIX_FMT_YUV444P].name = g_strdup ("yuv444p");
|
||||
pix_fmt_info[AV_PIX_FMT_YUV444P].nb_channels = 3;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV444P].color_type = FF_COLOR_YUV;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV444P].pixel_type = FF_PIXEL_PLANAR;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV444P].depth = 8;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV444P].x_chroma_shift = 0;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV444P].y_chroma_shift = 0;
|
||||
|
||||
pix_fmt_info[AV_PIX_FMT_YUYV422].name = g_strdup ("yuv422");
|
||||
pix_fmt_info[AV_PIX_FMT_YUYV422].nb_channels = 1;
|
||||
pix_fmt_info[AV_PIX_FMT_YUYV422].color_type = FF_COLOR_YUV;
|
||||
pix_fmt_info[AV_PIX_FMT_YUYV422].pixel_type = FF_PIXEL_PACKED;
|
||||
pix_fmt_info[AV_PIX_FMT_YUYV422].depth = 8;
|
||||
pix_fmt_info[AV_PIX_FMT_YUYV422].x_chroma_shift = 1;
|
||||
pix_fmt_info[AV_PIX_FMT_YUYV422].y_chroma_shift = 0;
|
||||
|
||||
pix_fmt_info[AV_PIX_FMT_YUV410P].name = g_strdup ("yuv410p");
|
||||
pix_fmt_info[AV_PIX_FMT_YUV410P].nb_channels = 3;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV410P].color_type = FF_COLOR_YUV;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV410P].pixel_type = FF_PIXEL_PLANAR;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV410P].depth = 8;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV410P].x_chroma_shift = 2;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV410P].y_chroma_shift = 2;
|
||||
|
||||
pix_fmt_info[AV_PIX_FMT_YUV411P].name = g_strdup ("yuv411p");
|
||||
pix_fmt_info[AV_PIX_FMT_YUV411P].nb_channels = 3;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV411P].color_type = FF_COLOR_YUV;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV411P].pixel_type = FF_PIXEL_PLANAR;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV411P].depth = 8;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV411P].x_chroma_shift = 2;
|
||||
pix_fmt_info[AV_PIX_FMT_YUV411P].y_chroma_shift = 0;
|
||||
|
||||
/* JPEG YUV */
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ420P].name = g_strdup ("yuvj420p");
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ420P].nb_channels = 3;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ420P].color_type = FF_COLOR_YUV_JPEG;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ420P].pixel_type = FF_PIXEL_PLANAR;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ420P].depth = 8;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ420P].x_chroma_shift = 1;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ420P].y_chroma_shift = 1;
|
||||
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ422P].name = g_strdup ("yuvj422p");
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ422P].nb_channels = 3;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ422P].color_type = FF_COLOR_YUV_JPEG;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ422P].pixel_type = FF_PIXEL_PLANAR;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ422P].depth = 8;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ422P].x_chroma_shift = 1;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ422P].y_chroma_shift = 0;
|
||||
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ444P].name = g_strdup ("yuvj444p");
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ444P].nb_channels = 3;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ444P].color_type = FF_COLOR_YUV_JPEG;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ444P].pixel_type = FF_PIXEL_PLANAR;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ444P].depth = 8;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ444P].x_chroma_shift = 0;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVJ444P].y_chroma_shift = 0;
|
||||
|
||||
/* RGB formats */
|
||||
pix_fmt_info[AV_PIX_FMT_RGB24].name = g_strdup ("rgb24");
|
||||
pix_fmt_info[AV_PIX_FMT_RGB24].nb_channels = 3;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB24].color_type = FF_COLOR_RGB;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB24].pixel_type = FF_PIXEL_PACKED;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB24].depth = 8;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB24].x_chroma_shift = 0;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB24].y_chroma_shift = 0;
|
||||
|
||||
pix_fmt_info[AV_PIX_FMT_BGR24].name = g_strdup ("bgr24");
|
||||
pix_fmt_info[AV_PIX_FMT_BGR24].nb_channels = 3;
|
||||
pix_fmt_info[AV_PIX_FMT_BGR24].color_type = FF_COLOR_RGB;
|
||||
pix_fmt_info[AV_PIX_FMT_BGR24].pixel_type = FF_PIXEL_PACKED;
|
||||
pix_fmt_info[AV_PIX_FMT_BGR24].depth = 8;
|
||||
pix_fmt_info[AV_PIX_FMT_BGR24].x_chroma_shift = 0;
|
||||
pix_fmt_info[AV_PIX_FMT_BGR24].y_chroma_shift = 0;
|
||||
|
||||
pix_fmt_info[AV_PIX_FMT_RGB32].name = g_strdup ("rgba32");
|
||||
pix_fmt_info[AV_PIX_FMT_RGB32].nb_channels = 4;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB32].is_alpha = 1;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB32].color_type = FF_COLOR_RGB;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB32].pixel_type = FF_PIXEL_PACKED;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB32].depth = 8;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB32].x_chroma_shift = 0;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB32].y_chroma_shift = 0;
|
||||
|
||||
pix_fmt_info[AV_PIX_FMT_RGB565].name = g_strdup ("rgb565");
|
||||
pix_fmt_info[AV_PIX_FMT_RGB565].nb_channels = 3;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB565].color_type = FF_COLOR_RGB;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB565].pixel_type = FF_PIXEL_PACKED;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB565].depth = 5;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB565].x_chroma_shift = 0;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB565].y_chroma_shift = 0;
|
||||
|
||||
pix_fmt_info[AV_PIX_FMT_RGB555].name = g_strdup ("rgb555");
|
||||
pix_fmt_info[AV_PIX_FMT_RGB555].nb_channels = 4;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB555].is_alpha = 1;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB555].color_type = FF_COLOR_RGB;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB555].pixel_type = FF_PIXEL_PACKED;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB555].depth = 5;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB555].x_chroma_shift = 0;
|
||||
pix_fmt_info[AV_PIX_FMT_RGB555].y_chroma_shift = 0;
|
||||
|
||||
/* gray / mono formats */
|
||||
pix_fmt_info[AV_PIX_FMT_GRAY8].name = g_strdup ("gray");
|
||||
pix_fmt_info[AV_PIX_FMT_GRAY8].nb_channels = 1;
|
||||
pix_fmt_info[AV_PIX_FMT_GRAY8].color_type = FF_COLOR_GRAY;
|
||||
pix_fmt_info[AV_PIX_FMT_GRAY8].pixel_type = FF_PIXEL_PLANAR;
|
||||
pix_fmt_info[AV_PIX_FMT_GRAY8].depth = 8;
|
||||
|
||||
pix_fmt_info[AV_PIX_FMT_MONOWHITE].name = g_strdup ("monow");
|
||||
pix_fmt_info[AV_PIX_FMT_MONOWHITE].nb_channels = 1;
|
||||
pix_fmt_info[AV_PIX_FMT_MONOWHITE].color_type = FF_COLOR_GRAY;
|
||||
pix_fmt_info[AV_PIX_FMT_MONOWHITE].pixel_type = FF_PIXEL_PLANAR;
|
||||
pix_fmt_info[AV_PIX_FMT_MONOWHITE].depth = 1;
|
||||
|
||||
pix_fmt_info[AV_PIX_FMT_MONOBLACK].name = g_strdup ("monob");
|
||||
pix_fmt_info[AV_PIX_FMT_MONOBLACK].nb_channels = 1;
|
||||
pix_fmt_info[AV_PIX_FMT_MONOBLACK].color_type = FF_COLOR_GRAY;
|
||||
pix_fmt_info[AV_PIX_FMT_MONOBLACK].pixel_type = FF_PIXEL_PLANAR;
|
||||
pix_fmt_info[AV_PIX_FMT_MONOBLACK].depth = 1;
|
||||
|
||||
/* paletted formats */
|
||||
pix_fmt_info[AV_PIX_FMT_PAL8].name = g_strdup ("pal8");
|
||||
pix_fmt_info[AV_PIX_FMT_PAL8].nb_channels = 4;
|
||||
pix_fmt_info[AV_PIX_FMT_PAL8].is_alpha = 1;
|
||||
pix_fmt_info[AV_PIX_FMT_PAL8].color_type = FF_COLOR_RGB;
|
||||
pix_fmt_info[AV_PIX_FMT_PAL8].pixel_type = FF_PIXEL_PALETTE;
|
||||
pix_fmt_info[AV_PIX_FMT_PAL8].depth = 8;
|
||||
|
||||
pix_fmt_info[AV_PIX_FMT_YUVA420P].name = g_strdup ("yuva420p");
|
||||
pix_fmt_info[AV_PIX_FMT_YUVA420P].nb_channels = 4;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVA420P].is_alpha = 1;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVA420P].color_type = FF_COLOR_YUV;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVA420P].pixel_type = FF_PIXEL_PLANAR;
|
||||
pix_fmt_info[AV_PIX_FMT_YUVA420P].depth = 8,
|
||||
pix_fmt_info[AV_PIX_FMT_YUVA420P].x_chroma_shift = 1,
|
||||
pix_fmt_info[AV_PIX_FMT_YUVA420P].y_chroma_shift = 1;
|
||||
};
|
||||
|
||||
int
|
||||
gst_ffmpeg_avpicture_get_size (int pix_fmt, int width, int height)
|
||||
{
|
||||
AVFrame dummy_pict;
|
||||
|
||||
return gst_ffmpeg_avpicture_fill (&dummy_pict, NULL, pix_fmt, width, height);
|
||||
}
|
||||
|
||||
#define GEN_MASK(x) ((1<<(x))-1)
|
||||
#define ROUND_UP_X(v,x) (((v) + GEN_MASK(x)) & ~GEN_MASK(x))
|
||||
#define ROUND_UP_2(x) ROUND_UP_X (x, 1)
|
||||
#define ROUND_UP_4(x) ROUND_UP_X (x, 2)
|
||||
#define ROUND_UP_8(x) ROUND_UP_X (x, 3)
|
||||
#define DIV_ROUND_UP_X(v,x) (((v) + GEN_MASK(x)) >> (x))
|
||||
|
||||
int
|
||||
gst_ffmpeg_avpicture_fill (AVFrame * picture,
|
||||
uint8_t * ptr, enum AVPixelFormat pix_fmt, int width, int height)
|
||||
{
|
||||
int size, w2, h2, size2;
|
||||
int stride, stride2;
|
||||
PixFmtInfo *pinfo;
|
||||
|
||||
pinfo = &pix_fmt_info[pix_fmt];
|
||||
|
||||
switch (pix_fmt) {
|
||||
case AV_PIX_FMT_YUV420P:
|
||||
case AV_PIX_FMT_YUV422P:
|
||||
case AV_PIX_FMT_YUV444P:
|
||||
case AV_PIX_FMT_YUV410P:
|
||||
case AV_PIX_FMT_YUV411P:
|
||||
case AV_PIX_FMT_YUVJ420P:
|
||||
case AV_PIX_FMT_YUVJ422P:
|
||||
case AV_PIX_FMT_YUVJ444P:
|
||||
stride = ROUND_UP_4 (width);
|
||||
h2 = ROUND_UP_X (height, pinfo->y_chroma_shift);
|
||||
size = stride * h2;
|
||||
w2 = DIV_ROUND_UP_X (width, pinfo->x_chroma_shift);
|
||||
stride2 = ROUND_UP_4 (w2);
|
||||
h2 = DIV_ROUND_UP_X (height, pinfo->y_chroma_shift);
|
||||
size2 = stride2 * h2;
|
||||
picture->data[0] = ptr;
|
||||
picture->data[1] = picture->data[0] + size;
|
||||
picture->data[2] = picture->data[1] + size2;
|
||||
picture->data[3] = NULL;
|
||||
picture->linesize[0] = stride;
|
||||
picture->linesize[1] = stride2;
|
||||
picture->linesize[2] = stride2;
|
||||
picture->linesize[3] = 0;
|
||||
GST_DEBUG ("planes %d %d %d", 0, size, size + size2);
|
||||
GST_DEBUG ("strides %d %d %d", stride, stride2, stride2);
|
||||
return size + 2 * size2;
|
||||
case AV_PIX_FMT_YUVA420P:
|
||||
stride = ROUND_UP_4 (width);
|
||||
h2 = ROUND_UP_X (height, pinfo->y_chroma_shift);
|
||||
size = stride * h2;
|
||||
w2 = DIV_ROUND_UP_X (width, pinfo->x_chroma_shift);
|
||||
stride2 = ROUND_UP_4 (w2);
|
||||
h2 = DIV_ROUND_UP_X (height, pinfo->y_chroma_shift);
|
||||
size2 = stride2 * h2;
|
||||
picture->data[0] = ptr;
|
||||
picture->data[1] = picture->data[0] + size;
|
||||
picture->data[2] = picture->data[1] + size2;
|
||||
picture->data[3] = picture->data[2] + size2;
|
||||
picture->linesize[0] = stride;
|
||||
picture->linesize[1] = stride2;
|
||||
picture->linesize[2] = stride2;
|
||||
picture->linesize[3] = stride;
|
||||
GST_DEBUG ("planes %d %d %d %d", 0, size, size + size2, size + 2 * size2);
|
||||
GST_DEBUG ("strides %d %d %d %d", stride, stride2, stride2, stride);
|
||||
return 2 * size + 2 * size2;
|
||||
case AV_PIX_FMT_RGB24:
|
||||
case AV_PIX_FMT_BGR24:
|
||||
stride = ROUND_UP_4 (width * 3);
|
||||
size = stride * height;
|
||||
picture->data[0] = ptr;
|
||||
picture->data[1] = NULL;
|
||||
picture->data[2] = NULL;
|
||||
picture->data[3] = NULL;
|
||||
picture->linesize[0] = stride;
|
||||
picture->linesize[1] = 0;
|
||||
picture->linesize[2] = 0;
|
||||
picture->linesize[3] = 0;
|
||||
return size;
|
||||
/*case AV_PIX_FMT_AYUV4444:
|
||||
case AV_PIX_FMT_BGR32:
|
||||
case AV_PIX_FMT_BGRA32:
|
||||
case AV_PIX_FMT_RGB32: */
|
||||
case AV_PIX_FMT_RGB32:
|
||||
stride = width * 4;
|
||||
size = stride * height;
|
||||
picture->data[0] = ptr;
|
||||
picture->data[1] = NULL;
|
||||
picture->data[2] = NULL;
|
||||
picture->data[3] = NULL;
|
||||
picture->linesize[0] = stride;
|
||||
picture->linesize[1] = 0;
|
||||
picture->linesize[2] = 0;
|
||||
picture->linesize[3] = 0;
|
||||
return size;
|
||||
case AV_PIX_FMT_RGB555:
|
||||
case AV_PIX_FMT_RGB565:
|
||||
case AV_PIX_FMT_YUYV422:
|
||||
case AV_PIX_FMT_UYVY422:
|
||||
stride = ROUND_UP_4 (width * 2);
|
||||
size = stride * height;
|
||||
picture->data[0] = ptr;
|
||||
picture->data[1] = NULL;
|
||||
picture->data[2] = NULL;
|
||||
picture->data[3] = NULL;
|
||||
picture->linesize[0] = stride;
|
||||
picture->linesize[1] = 0;
|
||||
picture->linesize[2] = 0;
|
||||
picture->linesize[3] = 0;
|
||||
return size;
|
||||
case AV_PIX_FMT_UYYVYY411:
|
||||
/* FIXME, probably not the right stride */
|
||||
stride = ROUND_UP_4 (width);
|
||||
size = stride * height;
|
||||
picture->data[0] = ptr;
|
||||
picture->data[1] = NULL;
|
||||
picture->data[2] = NULL;
|
||||
picture->data[3] = NULL;
|
||||
picture->linesize[0] = width + width / 2;
|
||||
picture->linesize[1] = 0;
|
||||
picture->linesize[2] = 0;
|
||||
picture->linesize[3] = 0;
|
||||
return size + size / 2;
|
||||
case AV_PIX_FMT_GRAY8:
|
||||
stride = ROUND_UP_4 (width);
|
||||
size = stride * height;
|
||||
picture->data[0] = ptr;
|
||||
picture->data[1] = NULL;
|
||||
picture->data[2] = NULL;
|
||||
picture->data[3] = NULL;
|
||||
picture->linesize[0] = stride;
|
||||
picture->linesize[1] = 0;
|
||||
picture->linesize[2] = 0;
|
||||
picture->linesize[3] = 0;
|
||||
return size;
|
||||
case AV_PIX_FMT_MONOWHITE:
|
||||
case AV_PIX_FMT_MONOBLACK:
|
||||
stride = ROUND_UP_4 ((width + 7) >> 3);
|
||||
size = stride * height;
|
||||
picture->data[0] = ptr;
|
||||
picture->data[1] = NULL;
|
||||
picture->data[2] = NULL;
|
||||
picture->data[3] = NULL;
|
||||
picture->linesize[0] = stride;
|
||||
picture->linesize[1] = 0;
|
||||
picture->linesize[2] = 0;
|
||||
picture->linesize[3] = 0;
|
||||
return size;
|
||||
case AV_PIX_FMT_PAL8:
|
||||
/* already forced to be with stride, so same result as other function */
|
||||
stride = ROUND_UP_4 (width);
|
||||
size = stride * height;
|
||||
picture->data[0] = ptr;
|
||||
picture->data[1] = ptr + size; /* palette is stored here as 256 32 bit words */
|
||||
picture->data[2] = NULL;
|
||||
picture->data[3] = NULL;
|
||||
picture->linesize[0] = stride;
|
||||
picture->linesize[1] = 4;
|
||||
picture->linesize[2] = 0;
|
||||
picture->linesize[3] = 0;
|
||||
return size + 256 * 4;
|
||||
default:
|
||||
picture->data[0] = NULL;
|
||||
picture->data[1] = NULL;
|
||||
picture->data[2] = NULL;
|
||||
picture->data[3] = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create a GstBuffer of the requested size and caps.
|
||||
* The memory will be allocated by ffmpeg, making sure it's properly aligned
|
||||
* for any processing. */
|
||||
|
||||
GstBuffer *
|
||||
new_aligned_buffer (gint size)
|
||||
{
|
||||
GstBuffer *buf;
|
||||
guint8 *data;
|
||||
|
||||
data = av_malloc (size);
|
||||
|
||||
buf = gst_buffer_new ();
|
||||
gst_buffer_append_memory (buf,
|
||||
gst_memory_new_wrapped (0, data, size, 0, size, data, av_free));
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
int
|
||||
gst_ffmpeg_auto_max_threads (void)
|
||||
{
|
||||
static gsize n_threads = 0;
|
||||
if (g_once_init_enter (&n_threads)) {
|
||||
int n = 1;
|
||||
#if defined(_WIN32)
|
||||
{
|
||||
const char *s = getenv ("NUMBER_OF_PROCESSORS");
|
||||
if (s) {
|
||||
n = atoi (s);
|
||||
}
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
{
|
||||
int mib[] = { CTL_HW, HW_NCPU };
|
||||
size_t dataSize = sizeof (int);
|
||||
|
||||
if (sysctl (mib, 2, &n, &dataSize, NULL, 0)) {
|
||||
n = 1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
n = sysconf (_SC_NPROCESSORS_CONF);
|
||||
#endif
|
||||
if (n < 1)
|
||||
n = 1;
|
||||
|
||||
g_once_init_leave (&n_threads, n);
|
||||
}
|
||||
|
||||
return (int) (n_threads);
|
||||
}
|
117
ext/libav/gstavutils.h
Normal file
117
ext/libav/gstavutils.h
Normal file
|
@ -0,0 +1,117 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <2009> Edward Hervey <bilboed@bilboed.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_FFMPEG_UTILS_H__
|
||||
#define __GST_FFMPEG_UTILS_H__
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/mathematics.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
/* Introduced since ffmpeg version 4.3
|
||||
*
|
||||
* Note: Not all ffmpeg encoders seem to be reusable after flushing/draining.
|
||||
* So if ffmpeg encoder doesn't support it, we should reopen encoding session.
|
||||
*
|
||||
* Before ffmpeg 4.3, avcodec_flush_buffers() was implemented in
|
||||
* libavcodec/decodec.c but it was moved to libavcodec/utils.c and it would be
|
||||
* accepted if encoder supports AV_CODEC_CAP_ENCODER_FLUSH flag.
|
||||
* That implies that avcodec_flush_buffers() wasn't intended to be working
|
||||
* properly for encoders.
|
||||
*/
|
||||
#ifndef AV_CODEC_CAP_ENCODER_FLUSH
|
||||
/*
|
||||
* This encoder can be flushed using avcodec_flush_buffers(). If this flag is
|
||||
* not set, the encoder must be closed and reopened to ensure that no frames
|
||||
* remain pending.
|
||||
*/
|
||||
#define AV_CODEC_CAP_ENCODER_FLUSH (1 << 21)
|
||||
#endif
|
||||
|
||||
/*
|
||||
*Get the size of an picture
|
||||
*/
|
||||
int
|
||||
gst_ffmpeg_avpicture_get_size (int pix_fmt, int width, int height);
|
||||
|
||||
/*
|
||||
* Fill in pointers in an AVFrame, aligned by 4 (required by X).
|
||||
*/
|
||||
|
||||
int
|
||||
gst_ffmpeg_avpicture_fill (AVFrame * picture,
|
||||
uint8_t * ptr,
|
||||
enum AVPixelFormat pix_fmt,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
/*
|
||||
* Convert from/to a GStreamer <-> FFMpeg timestamp.
|
||||
*/
|
||||
static inline guint64
|
||||
gst_ffmpeg_time_ff_to_gst (gint64 pts, AVRational base)
|
||||
{
|
||||
guint64 out;
|
||||
|
||||
if (pts == AV_NOPTS_VALUE){
|
||||
out = GST_CLOCK_TIME_NONE;
|
||||
} else {
|
||||
AVRational bq = { 1, GST_SECOND };
|
||||
out = av_rescale_q (pts, base, bq);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline gint64
|
||||
gst_ffmpeg_time_gst_to_ff (guint64 time, AVRational base)
|
||||
{
|
||||
gint64 out;
|
||||
|
||||
if (!GST_CLOCK_TIME_IS_VALID (time) || base.num == 0) {
|
||||
out = AV_NOPTS_VALUE;
|
||||
} else {
|
||||
AVRational bq = { 1, GST_SECOND };
|
||||
out = av_rescale_q (time, bq, base);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void
|
||||
gst_ffmpeg_init_pix_fmt_info(void);
|
||||
|
||||
int
|
||||
gst_ffmpeg_auto_max_threads(void);
|
||||
|
||||
const gchar *
|
||||
gst_ffmpeg_get_codecid_longname (enum AVCodecID codec_id);
|
||||
|
||||
gint
|
||||
av_smp_format_depth(enum AVSampleFormat smp_fmt);
|
||||
|
||||
GstBuffer *
|
||||
new_aligned_buffer (gint size);
|
||||
|
||||
#endif /* __GST_FFMPEG_UTILS_H__ */
|
2600
ext/libav/gstavviddec.c
Normal file
2600
ext/libav/gstavviddec.c
Normal file
File diff suppressed because it is too large
Load diff
94
ext/libav/gstavviddec.h
Normal file
94
ext/libav/gstavviddec.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
/* 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_FFMPEGVIDDEC_H__
|
||||
#define __GST_FFMPEGVIDDEC_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstFFMpegVidDec GstFFMpegVidDec;
|
||||
struct _GstFFMpegVidDec
|
||||
{
|
||||
GstVideoDecoder parent;
|
||||
|
||||
GstVideoCodecState *input_state;
|
||||
GstVideoCodecState *output_state;
|
||||
|
||||
/* decoding */
|
||||
AVCodecContext *context;
|
||||
AVFrame *picture;
|
||||
GstVideoMultiviewMode picture_multiview_mode;
|
||||
GstVideoMultiviewFlags picture_multiview_flags;
|
||||
gint stride[AV_NUM_DATA_POINTERS];
|
||||
gboolean opened;
|
||||
|
||||
/* current output pictures */
|
||||
enum AVPixelFormat pic_pix_fmt;
|
||||
gint pic_width;
|
||||
gint pic_height;
|
||||
gint pic_par_n;
|
||||
gint pic_par_d;
|
||||
gint pic_interlaced;
|
||||
/* GST_VIDEO_BUFFER_FLAG_RFF | GST_VIDEO_BUFFER_FLAG_TFF */
|
||||
gint pic_field_order;
|
||||
gboolean pic_field_order_changed;
|
||||
GstVideoMultiviewMode cur_multiview_mode;
|
||||
GstVideoMultiviewFlags cur_multiview_flags;
|
||||
/* current context */
|
||||
gint ctx_ticks;
|
||||
gint ctx_time_d;
|
||||
gint ctx_time_n;
|
||||
GstBuffer *palette;
|
||||
|
||||
guint8 *padded;
|
||||
gint padded_size;
|
||||
|
||||
/* some properties */
|
||||
enum AVDiscard skip_frame;
|
||||
gint lowres;
|
||||
gboolean direct_rendering;
|
||||
int max_threads;
|
||||
gboolean output_corrupt;
|
||||
guint thread_type;
|
||||
|
||||
GstCaps *last_caps;
|
||||
|
||||
/* Internally used for direct rendering */
|
||||
GstBufferPool *internal_pool;
|
||||
gint pool_width;
|
||||
gint pool_height;
|
||||
enum AVPixelFormat pool_format;
|
||||
GstVideoInfo pool_info;
|
||||
};
|
||||
|
||||
typedef struct _GstFFMpegVidDecClass GstFFMpegVidDecClass;
|
||||
|
||||
struct _GstFFMpegVidDecClass
|
||||
{
|
||||
GstVideoDecoderClass parent_class;
|
||||
|
||||
AVCodec *in_plugin;
|
||||
};
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
1059
ext/libav/gstavvidenc.c
Normal file
1059
ext/libav/gstavvidenc.c
Normal file
File diff suppressed because it is too large
Load diff
72
ext/libav/gstavvidenc.h
Normal file
72
ext/libav/gstavvidenc.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/* First, include the header file for the plugin, to bring in the
|
||||
* object definition and other useful things.
|
||||
*/
|
||||
|
||||
#ifndef __GST_FFMPEGVIDENC_H__
|
||||
#define __GST_FFMPEGVIDENC_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstFFMpegVidEnc GstFFMpegVidEnc;
|
||||
|
||||
struct _GstFFMpegVidEnc
|
||||
{
|
||||
GstVideoEncoder parent;
|
||||
|
||||
GstVideoCodecState *input_state;
|
||||
|
||||
AVCodecContext *context;
|
||||
AVFrame *picture;
|
||||
gboolean opened;
|
||||
gboolean need_reopen;
|
||||
gboolean discont;
|
||||
guint pass;
|
||||
gfloat quantizer;
|
||||
|
||||
/* statistics file */
|
||||
gchar *filename;
|
||||
FILE *file;
|
||||
|
||||
/* cache */
|
||||
guint8 *working_buf;
|
||||
gsize working_buf_size;
|
||||
|
||||
AVCodecContext *refcontext;
|
||||
};
|
||||
|
||||
typedef struct _GstFFMpegVidEncClass GstFFMpegVidEncClass;
|
||||
|
||||
struct _GstFFMpegVidEncClass
|
||||
{
|
||||
GstVideoEncoderClass parent_class;
|
||||
|
||||
AVCodec *in_plugin;
|
||||
GstPadTemplate *srctempl, *sinktempl;
|
||||
};
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_FFMPEGVIDENC_H__ */
|
26
ext/libav/meson.build
Normal file
26
ext/libav/meson.build
Normal file
|
@ -0,0 +1,26 @@
|
|||
sources = [
|
||||
'gstav.c',
|
||||
'gstavprotocol.c',
|
||||
'gstavcodecmap.c',
|
||||
'gstavutils.c',
|
||||
'gstavaudenc.c',
|
||||
'gstavvidenc.c',
|
||||
'gstavauddec.c',
|
||||
'gstavviddec.c',
|
||||
'gstavcfg.c',
|
||||
'gstavdemux.c',
|
||||
'gstavmux.c',
|
||||
'gstavdeinterlace.c',
|
||||
]
|
||||
|
||||
gstlibav_plugin = library('gstlibav',
|
||||
sources,
|
||||
c_args : gst_libav_args,
|
||||
include_directories : [configinc],
|
||||
dependencies : libav_deps + [gst_dep, gstbase_dep, gstvideo_dep,
|
||||
gstaudio_dep, gstpbutils_dep],
|
||||
install : true,
|
||||
install_dir : plugins_install_dir,
|
||||
)
|
||||
pkgconfig.generate(gstlibav_plugin, install_dir : plugins_pkgconfig_install_dir)
|
||||
plugins += [gstlibav_plugin]
|
913
gst-libav.doap
Normal file
913
gst-libav.doap
Normal file
|
@ -0,0 +1,913 @@
|
|||
<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 libav Plug-ins</name>
|
||||
<shortname>gst-libav</shortname>
|
||||
<homepage rdf:resource="http://gstreamer.freedesktop.org/modules/gst-libav.html" />
|
||||
<created>2004-02-26</created>
|
||||
<shortdesc xml:lang="en">
|
||||
a plug-in using the libav library
|
||||
</shortdesc>
|
||||
<description xml:lang="en">
|
||||
GStreamer libav plug-in contains elements using the libav library code. It
|
||||
contains most popular decoders as well as very fast
|
||||
colorspace conversion elements.
|
||||
</description>
|
||||
<category></category>
|
||||
<bug-database rdf:resource="https://gitlab.freedesktop.org/gstreamer/gst-libav/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-libav"/>
|
||||
<browse rdf:resource="http://gitlab.freedesktop.org/gstreamer/gst-libav"/>
|
||||
</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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-1.12.2.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.12.1</revision>
|
||||
<branch>1.12</branch>
|
||||
<name></name>
|
||||
<created>2017-06-12</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-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-libav/gst-libav-1.0.0.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.11.99</revision>
|
||||
<branch>0.11</branch>
|
||||
<name>Have Some Courtesy</name>
|
||||
<created>2012-09-17</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-libav/gst-libav-0.11.99.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.11.94</revision>
|
||||
<branch>0.11</branch>
|
||||
<name>To Each His Own Symphony</name>
|
||||
<created>2012-09-14</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-libav/gst-libav-0.11.94.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.11.93</revision>
|
||||
<branch>0.11</branch>
|
||||
<name>Pink Noise Waltz</name>
|
||||
<created>2012-08-08</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-libav/gst-libav-0.11.93.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.11.92</revision>
|
||||
<branch>0.11</branch>
|
||||
<name>Wish You Were Here</name>
|
||||
<created>2012-06-07</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-libav/gst-libav-0.11.92.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.11.91</revision>
|
||||
<branch>0.11</branch>
|
||||
<name>Help master! A boy is stealing me!</name>
|
||||
<created>2012-05-13</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-libav/gst-libav-0.11.91.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.11.90</revision>
|
||||
<branch>0.11</branch>
|
||||
<name>Nameless here for evermore</name>
|
||||
<created>2012-04-13</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-libav/gst-libav-0.11.90.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-libav/gst-libav-0.11.90.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.11.2</revision>
|
||||
<branch>0.11</branch>
|
||||
<name>Sadistic Symphony</name>
|
||||
<created>2012-03-22</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.11.2.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.11.2.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.11.1</revision>
|
||||
<branch>0.11</branch>
|
||||
<name>Serpentine Sibilance</name>
|
||||
<created>2012-02-16</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.11.1.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.11.1.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.10.13</revision>
|
||||
<branch>0.10</branch>
|
||||
<name>A year in hell</name>
|
||||
<created>2011-11-02</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.13.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.13.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.10.12</revision>
|
||||
<branch>0.10</branch>
|
||||
<name>A year in hell</name>
|
||||
<created>2011-07-20</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.12.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.12.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.10.11</revision>
|
||||
<branch>0.10</branch>
|
||||
<name>Feeding trolls is strictly forbidden</name>
|
||||
<created>2010-07-15</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.11.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.11.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.10.10</revision>
|
||||
<branch>0.10</branch>
|
||||
<name>It's the bomb</name>
|
||||
<created>2010-03-06</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.10.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.10.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.10.9</revision>
|
||||
<branch>0.10</branch>
|
||||
<name>Shooting the moon</name>
|
||||
<created>2009-10-05</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.9.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.9.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.10.8</revision>
|
||||
<branch>0.10</branch>
|
||||
<name>Brilliant in the morning</name>
|
||||
<created>2009-06-29</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.8.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.8.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.10.7</revision>
|
||||
<branch>0.10</branch>
|
||||
<name>Some high ground is not worth taking</name>
|
||||
<created>2009-03-20</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.7.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.7.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.10.6</revision>
|
||||
<branch>0.10</branch>
|
||||
<name>A little itching in our bones</name>
|
||||
<created>2008-11-27</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.6.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.6.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.10.5</revision>
|
||||
<branch>0.10</branch>
|
||||
<name>This little piggy went to market</name>
|
||||
<created>2008-09-03</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.5.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.5.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.10.4</revision>
|
||||
<branch>0.10</branch>
|
||||
<name>A jump to the left</name>
|
||||
<created>2008-05-21</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.4.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.4.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.10.3</revision>
|
||||
<branch>0.10</branch>
|
||||
<name>My T-Shirt is slowly fading</name>
|
||||
<created>2007-12-04</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.3.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.3.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.10.2</revision>
|
||||
<branch>0.10</branch>
|
||||
<name>At the edge of Taymans</name>
|
||||
<created>2006-12-31</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.2.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.2.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.10.1</revision>
|
||||
<branch>0.10</branch>
|
||||
<name>Late Train</name>
|
||||
<created>2006-03-31</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.1.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.1.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.10.0</revision>
|
||||
<branch>0.10</branch>
|
||||
<name>Rocamadour</name>
|
||||
<created>2005-12-05</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.0.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.10.0.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.8.7</revision>
|
||||
<branch>0.8</branch>
|
||||
<name>Redneck soup</name>
|
||||
<created>2005-10-25</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.7.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.7.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.8.6</revision>
|
||||
<branch>0.8</branch>
|
||||
<name>Vamoz a la Playa</name>
|
||||
<created>2005-08-05</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.6.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.6.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.8.5</revision>
|
||||
<branch>0.8</branch>
|
||||
<name>For the better of the world</name>
|
||||
<created>2005-06-11</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.5.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.5.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.8.4</revision>
|
||||
<branch>0.8</branch>
|
||||
<name>Mellow on my fingers</name>
|
||||
<created>2005-03-11</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.4.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.4.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.8.3</revision>
|
||||
<branch>0.8</branch>
|
||||
<name>Tiny Piece of Plastic</name>
|
||||
<created>2004-12-27</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.3.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.3.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.8.2</revision>
|
||||
<branch>0.8</branch>
|
||||
<name>Titan Shifting Gears</name>
|
||||
<created>2004-10-09</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.2.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.2.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.8.1</revision>
|
||||
<branch>0.8</branch>
|
||||
<name>Just Another Victim</name>
|
||||
<created>2004-07-16</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.1.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.1.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>0.8.0</revision>
|
||||
<branch>0.8</branch>
|
||||
<name>In Buildings</name>
|
||||
<created>2004-03-16</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.0.tar.bz2" />
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-ffmpeg/gst-ffmpeg-0.8.0.tar.gz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
|
||||
<maintainer>
|
||||
<foaf:Person>
|
||||
<foaf:name>Wim Taymans</foaf:name>
|
||||
<foaf:mbox_sha1sum>0d93fde052812d51a05fd86de9bdbf674423daa2</foaf:mbox_sha1sum>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
<maintainer>
|
||||
<foaf:Person>
|
||||
<foaf:name>Edward Hervey</foaf:name>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
|
||||
</Project>
|
83
hooks/pre-commit.hook
Executable file
83
hooks/pre-commit.hook
Executable file
|
@ -0,0 +1,83 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Check that the code follows a consistant code style
|
||||
#
|
||||
|
||||
# Check for existence of indent, and error out if not present.
|
||||
# On some *bsd systems the binary seems to be called gnunindent,
|
||||
# so check for that first.
|
||||
|
||||
version=`gnuindent --version 2>/dev/null`
|
||||
if test "x$version" = "x"; then
|
||||
version=`gindent --version 2>/dev/null`
|
||||
if test "x$version" = "x"; then
|
||||
version=`indent --version 2>/dev/null`
|
||||
if test "x$version" = "x"; then
|
||||
echo "GStreamer git pre-commit hook:"
|
||||
echo "Did not find GNU indent, please install it before continuing."
|
||||
exit 1
|
||||
else
|
||||
INDENT=indent
|
||||
fi
|
||||
else
|
||||
INDENT=gindent
|
||||
fi
|
||||
else
|
||||
INDENT=gnuindent
|
||||
fi
|
||||
|
||||
case `$INDENT --version` in
|
||||
GNU*)
|
||||
;;
|
||||
default)
|
||||
echo "GStreamer git pre-commit hook:"
|
||||
echo "Did not find GNU indent, please install it before continuing."
|
||||
echo "(Found $INDENT, but it doesn't seem to be GNU indent)"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
INDENT_PARAMETERS="--braces-on-if-line \
|
||||
--case-brace-indentation0 \
|
||||
--case-indentation2 \
|
||||
--braces-after-struct-decl-line \
|
||||
--line-length80 \
|
||||
--no-tabs \
|
||||
--cuddle-else \
|
||||
--dont-line-up-parentheses \
|
||||
--continuation-indentation4 \
|
||||
--honour-newlines \
|
||||
--tab-size8 \
|
||||
--indent-level2 \
|
||||
--leave-preprocessor-space"
|
||||
|
||||
echo "--Checking style--"
|
||||
for file in `git diff-index --cached --name-only HEAD --diff-filter=ACMR| grep "\.c$"` ; do
|
||||
# nf is the temporary checkout. This makes sure we check against the
|
||||
# revision in the index (and not the checked out version).
|
||||
nf=`git checkout-index --temp ${file} | cut -f 1`
|
||||
newfile=`mktemp /tmp/${nf}.XXXXXX` || exit 1
|
||||
$INDENT ${INDENT_PARAMETERS} \
|
||||
$nf -o $newfile 2>> /dev/null
|
||||
# FIXME: Call indent twice as it tends to do line-breaks
|
||||
# different for every second call.
|
||||
$INDENT ${INDENT_PARAMETERS} \
|
||||
$newfile 2>> /dev/null
|
||||
diff -u -p "${nf}" "${newfile}"
|
||||
r=$?
|
||||
rm "${newfile}"
|
||||
rm "${nf}"
|
||||
if [ $r != 0 ] ; then
|
||||
echo "================================================================================================="
|
||||
echo " Code style error in: $file "
|
||||
echo " "
|
||||
echo " Please fix before committing. Don't forget to run git add before trying to commit again. "
|
||||
echo " If the whole file is to be committed, this should work (run from the top-level directory): "
|
||||
echo " "
|
||||
echo " gst-indent $file; git add $file; git commit"
|
||||
echo " "
|
||||
echo "================================================================================================="
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "--Checking style pass--"
|
217
meson.build
Normal file
217
meson.build
Normal file
|
@ -0,0 +1,217 @@
|
|||
project('gst-libav', 'c', 'cpp',
|
||||
version : '1.19.2',
|
||||
meson_version : '>= 0.54',
|
||||
default_options : [ 'warning_level=1',
|
||||
'buildtype=debugoptimized' ])
|
||||
|
||||
gst_version = meson.project_version()
|
||||
version_arr = gst_version.split('.')
|
||||
gst_version_major = version_arr[0].to_int()
|
||||
gst_version_minor = version_arr[1].to_int()
|
||||
gst_version_micro = version_arr[2].to_int()
|
||||
if version_arr.length() == 4
|
||||
gst_version_nano = version_arr[3].to_int()
|
||||
else
|
||||
gst_version_nano = 0
|
||||
endif
|
||||
|
||||
api_version = '1.0'
|
||||
libavfilter_dep = dependency('libavfilter', version: '>= 7.16.100',
|
||||
fallback: ['FFmpeg', 'libavfilter_dep'])
|
||||
libavformat_dep = dependency('libavformat', version: '>= 58.12.100',
|
||||
fallback: ['FFmpeg', 'libavformat_dep'])
|
||||
libavcodec_dep = dependency('libavcodec', version: '>= 58.18.100',
|
||||
fallback: ['FFmpeg', 'libavcodec_dep'])
|
||||
libavutil_dep = dependency('libavutil', version: '>= 56.14.100',
|
||||
fallback: ['FFmpeg', 'libavutil_dep'])
|
||||
|
||||
libav_deps = [libavfilter_dep, libavformat_dep, libavcodec_dep, libavutil_dep]
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
check_ffmpeg_src = '''#include <libavcodec/avcodec.h>
|
||||
#if LIBAVCODEC_VERSION_MICRO >= 100
|
||||
/* FFmpeg uses 100+ as its micro version */
|
||||
#else
|
||||
#error libav provider should be FFmpeg
|
||||
#endif'''
|
||||
|
||||
libav_deps_type_name = ''
|
||||
|
||||
foreach dep: libav_deps
|
||||
if libav_deps_type_name != '' and dep.type_name() != libav_deps_type_name
|
||||
error('Libav deps must be either all internal or all external')
|
||||
endif
|
||||
libav_deps_type_name = dep.type_name()
|
||||
endforeach
|
||||
|
||||
if dep.type_name() != 'internal'
|
||||
if not cc.compiles(check_ffmpeg_src, dependencies : libav_deps, name : 'libav is provided by FFmpeg')
|
||||
error('Uncompatible libavcodec found')
|
||||
endif
|
||||
endif
|
||||
|
||||
cdata = configuration_data()
|
||||
cdata.set('LIBAV_SOURCE', '"system install"')
|
||||
cdata.set('PACKAGE_VERSION', '"@0@"'.format(gst_version))
|
||||
cdata.set('PACKAGE', '"gst-libav"')
|
||||
|
||||
# GStreamer package name and origin url
|
||||
gst_package_name = get_option('package-name')
|
||||
if gst_package_name == ''
|
||||
if gst_version_nano == 0
|
||||
gst_package_name = 'GStreamer FFMPEG Plug-ins source release'
|
||||
elif gst_version_nano == 1
|
||||
gst_package_name = 'GStreamer FFMPEG Plug-ins git'
|
||||
else
|
||||
gst_package_name = 'GStreamer FFMPEG Plug-ins prerelease'
|
||||
endif
|
||||
endif
|
||||
cdata.set_quoted('GST_PACKAGE_NAME', gst_package_name)
|
||||
cdata.set_quoted('GST_PACKAGE_ORIGIN', get_option('package-origin'))
|
||||
|
||||
|
||||
check_headers = [['unistd.h', 'HAVE_UNISTD_H']]
|
||||
|
||||
foreach h : check_headers
|
||||
if cc.has_header(h.get(0))
|
||||
cdata.set(h.get(1), 1)
|
||||
endif
|
||||
endforeach
|
||||
|
||||
gst_req = '>= @0@.@1@.0'.format(gst_version_major, gst_version_minor)
|
||||
gst_dep = dependency('gstreamer-1.0', version : gst_req,
|
||||
fallback : ['gstreamer', 'gst_dep'])
|
||||
gstbase_dep = dependency('gstreamer-base-1.0', version : gst_req,
|
||||
fallback : ['gstreamer', 'gst_base_dep'])
|
||||
gstcheck_dep = dependency('gstreamer-check-1.0', version : gst_req,
|
||||
required : get_option('tests'),
|
||||
fallback : ['gstreamer', 'gst_check_dep'])
|
||||
|
||||
gstvideo_dep = dependency('gstreamer-video-1.0', version : gst_req,
|
||||
fallback : ['gst-plugins-base', 'video_dep'])
|
||||
gstaudio_dep = dependency('gstreamer-audio-1.0', version : gst_req,
|
||||
fallback : ['gst-plugins-base', 'audio_dep'])
|
||||
gstpbutils_dep = dependency('gstreamer-pbutils-1.0', version : gst_req,
|
||||
fallback : ['gst-plugins-base', 'pbutils_dep'])
|
||||
libm = cc.find_library('m', required : false)
|
||||
|
||||
gst_libav_args = ['-DHAVE_CONFIG_H']
|
||||
if cc.get_id() == 'msvc'
|
||||
msvc_args = [
|
||||
# Ignore several spurious warnings for things gstreamer does very commonly
|
||||
# If a warning is completely useless and spammy, use '/wdXXXX' to suppress it
|
||||
# If a warning is harmless but hard to fix, use '/woXXXX' so it's shown once
|
||||
# NOTE: Only add warnings here if you are sure they're spurious
|
||||
'/wd4018', # implicit signed/unsigned conversion
|
||||
'/wd4146', # unary minus on unsigned (beware INT_MIN)
|
||||
'/wd4244', # lossy type conversion (e.g. double -> int)
|
||||
'/wd4305', # truncating type conversion (e.g. double -> float)
|
||||
cc.get_supported_arguments(['/utf-8']), # set the input encoding to utf-8
|
||||
|
||||
# Enable some warnings on MSVC to match GCC/Clang behaviour
|
||||
'/w14062', # enumerator 'identifier' in switch of enum 'enumeration' is not handled
|
||||
'/w14101', # 'identifier' : unreferenced local variable
|
||||
'/w14189', # 'identifier' : local variable is initialized but not referenced
|
||||
]
|
||||
add_project_arguments(msvc_args, language: ['c', 'cpp'])
|
||||
endif
|
||||
|
||||
# Symbol visibility
|
||||
if cc.has_argument('-fvisibility=hidden')
|
||||
add_project_arguments('-fvisibility=hidden', language: 'c')
|
||||
endif
|
||||
|
||||
# Don't export any symbols from static ffmpeg libraries
|
||||
if cc.has_link_argument('-Wl,--exclude-libs=ALL')
|
||||
add_project_link_arguments('-Wl,--exclude-libs=ALL', language: 'c')
|
||||
endif
|
||||
|
||||
# Disable strict aliasing
|
||||
if cc.has_argument('-fno-strict-aliasing')
|
||||
add_project_arguments('-fno-strict-aliasing', language: 'c')
|
||||
endif
|
||||
|
||||
if gst_dep.type_name() == 'internal'
|
||||
gst_proj = subproject('gstreamer')
|
||||
|
||||
if not gst_proj.get_variable('gst_debug')
|
||||
message('GStreamer debug system is disabled')
|
||||
add_project_arguments('-Wno-unused', language: 'c')
|
||||
else
|
||||
message('GStreamer debug system is enabled')
|
||||
endif
|
||||
else
|
||||
# We can't check that in the case of subprojects as we won't
|
||||
# be able to build against an internal dependency (which is not built yet)
|
||||
if not cc.compiles('''
|
||||
#include <gst/gstconfig.h>
|
||||
#ifdef GST_DISABLE_GST_DEBUG
|
||||
#error "debugging disabled, make compiler fail"
|
||||
#endif''' , dependencies: gst_dep)
|
||||
message('GStreamer debug system is disabled')
|
||||
add_project_arguments('-Wno-unused', language: 'c')
|
||||
else
|
||||
message('GStreamer debug system is enabled')
|
||||
endif
|
||||
endif
|
||||
|
||||
warning_flags = [
|
||||
'-Wmissing-declarations',
|
||||
'-Wmissing-prototypes',
|
||||
'-Wold-style-definition',
|
||||
'-Wredundant-decls',
|
||||
'-Wundef',
|
||||
'-Wwrite-strings',
|
||||
'-Wformat',
|
||||
'-Wformat-nonliteral',
|
||||
'-Wformat-security',
|
||||
'-Winit-self',
|
||||
'-Wmissing-include-dirs',
|
||||
'-Waddress',
|
||||
'-Wno-multichar',
|
||||
'-Waggregate-return',
|
||||
'-Wdeclaration-after-statement',
|
||||
'-Wvla',
|
||||
'-Wpointer-arith',
|
||||
]
|
||||
|
||||
foreach extra_arg : warning_flags
|
||||
if cc.has_argument (extra_arg)
|
||||
add_project_arguments([extra_arg], language: 'c')
|
||||
endif
|
||||
endforeach
|
||||
|
||||
configinc = include_directories('.')
|
||||
plugins_install_dir = '@0@/gstreamer-1.0'.format(get_option('libdir'))
|
||||
|
||||
pkgconfig = import('pkgconfig')
|
||||
plugins_pkgconfig_install_dir = join_paths(plugins_install_dir, 'pkgconfig')
|
||||
if get_option('default_library') == 'shared'
|
||||
# If we don't build static plugins there is no need to generate pc files
|
||||
plugins_pkgconfig_install_dir = disabler()
|
||||
endif
|
||||
|
||||
plugins = []
|
||||
subdir('ext/libav')
|
||||
subdir('docs')
|
||||
subdir('tests')
|
||||
|
||||
# Set release date
|
||||
if gst_version_nano == 0
|
||||
extract_release_date = find_program('scripts/extract-release-date-from-doap-file.py')
|
||||
run_result = run_command(extract_release_date, gst_version, files('gst-libav.doap'))
|
||||
if run_result.returncode() == 0
|
||||
release_date = run_result.stdout().strip()
|
||||
cdata.set_quoted('GST_PACKAGE_RELEASE_DATETIME', release_date)
|
||||
message('Package release date: ' + release_date)
|
||||
else
|
||||
# Error out if our release can't be found in the .doap file
|
||||
error(run_result.stderr())
|
||||
endif
|
||||
endif
|
||||
|
||||
configure_file(output: 'config.h', configuration: cdata)
|
||||
|
||||
python3 = import('python').find_installation()
|
||||
run_command(python3, '-c', 'import shutil; shutil.copy("hooks/pre-commit.hook", ".git/hooks/pre-commit")')
|
8
meson_options.txt
Normal file
8
meson_options.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
option('package-name', type : 'string', yield : true,
|
||||
description : 'package name to use in plugins')
|
||||
option('package-origin', type : 'string',
|
||||
value : 'Unknown package origin', yield : true,
|
||||
description : 'package origin URL to use in plugins')
|
||||
option('doc', type : 'feature', value : 'auto', yield: true,
|
||||
description: 'Enable documentation.')
|
||||
option('tests', type : 'feature', value : 'auto', yield : true)
|
45
scripts/extract-release-date-from-doap-file.py
Executable file
45
scripts/extract-release-date-from-doap-file.py
Executable file
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# extract-release-date-from-doap-file.py VERSION DOAP-FILE
|
||||
#
|
||||
# Extract release date for the given release version from a DOAP file
|
||||
#
|
||||
# Copyright (C) 2020 Tim-Philipp Müller <tim 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.
|
||||
|
||||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
sys.exit('Usage: {} VERSION DOAP-FILE'.format(sys.argv[0]))
|
||||
|
||||
release_version = sys.argv[1]
|
||||
doap_fn = sys.argv[2]
|
||||
|
||||
tree = ET.parse(doap_fn)
|
||||
root = tree.getroot()
|
||||
|
||||
namespaces = {'doap': 'http://usefulinc.com/ns/doap#'}
|
||||
|
||||
for v in root.findall('doap:release/doap:Version', namespaces=namespaces):
|
||||
if v.findtext('doap:revision', namespaces=namespaces) == release_version:
|
||||
release_date = v.findtext('doap:created', namespaces=namespaces)
|
||||
if release_date:
|
||||
print(release_date)
|
||||
sys.exit(0)
|
||||
|
||||
sys.exit('Could not find a release with version {} in {}'.format(release_version, doap_fn))
|
126
tests/check/elements/avaudenc.c
Normal file
126
tests/check/elements/avaudenc.c
Normal file
|
@ -0,0 +1,126 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2020 Seungha Yang <seungha@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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/check/gstcheck.h>
|
||||
#include <gst/check/gstharness.h>
|
||||
#include <gst/audio/audio.h>
|
||||
|
||||
GST_START_TEST (test_audioenc_drain)
|
||||
{
|
||||
GstHarness *h;
|
||||
GstAudioInfo info;
|
||||
GstBuffer *in_buf;
|
||||
gint i = 0;
|
||||
gint num_output = 0;
|
||||
GstFlowReturn ret;
|
||||
GstSegment segment;
|
||||
GstCaps *caps;
|
||||
gint samples_per_buffer = 1024;
|
||||
gint rate = 44100;
|
||||
gint size;
|
||||
GstClockTime duration;
|
||||
|
||||
h = gst_harness_new ("avenc_aac");
|
||||
fail_unless (h != NULL);
|
||||
|
||||
gst_audio_info_set_format (&info, GST_AUDIO_FORMAT_F32, rate, 1, NULL);
|
||||
|
||||
caps = gst_audio_info_to_caps (&info);
|
||||
gst_harness_set_src_caps (h, gst_caps_copy (caps));
|
||||
|
||||
duration = gst_util_uint64_scale_int (samples_per_buffer, GST_SECOND, rate);
|
||||
size = samples_per_buffer * GST_AUDIO_INFO_BPF (&info);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
in_buf = gst_buffer_new_and_alloc (size);
|
||||
|
||||
gst_buffer_memset (in_buf, 0, 0, size);
|
||||
|
||||
/* small rounding error would be expected, but should be fine */
|
||||
GST_BUFFER_PTS (in_buf) = i * duration;
|
||||
GST_BUFFER_DURATION (in_buf) = duration;
|
||||
|
||||
ret = gst_harness_push (h, in_buf);
|
||||
|
||||
fail_unless (ret == GST_FLOW_OK, "GstFlowReturn was %s",
|
||||
gst_flow_get_name (ret));
|
||||
}
|
||||
|
||||
gst_segment_init (&segment, GST_FORMAT_TIME);
|
||||
fail_unless (gst_segment_set_running_time (&segment, GST_FORMAT_TIME,
|
||||
2 * duration));
|
||||
|
||||
/* Push new eos event to drain encoder */
|
||||
fail_unless (gst_harness_push_event (h, gst_event_new_eos ()));
|
||||
|
||||
/* And start new stream */
|
||||
fail_unless (gst_harness_push_event (h,
|
||||
gst_event_new_stream_start ("new-stream-id")));
|
||||
gst_harness_set_src_caps (h, caps);
|
||||
fail_unless (gst_harness_push_event (h, gst_event_new_segment (&segment)));
|
||||
|
||||
in_buf = gst_buffer_new_and_alloc (size);
|
||||
|
||||
GST_BUFFER_PTS (in_buf) = 2 * duration;
|
||||
GST_BUFFER_DURATION (in_buf) = duration;
|
||||
|
||||
ret = gst_harness_push (h, in_buf);
|
||||
fail_unless (ret == GST_FLOW_OK, "GstFlowReturn was %s",
|
||||
gst_flow_get_name (ret));
|
||||
|
||||
/* Finish encoding and drain again */
|
||||
fail_unless (gst_harness_push_event (h, gst_event_new_eos ()));
|
||||
do {
|
||||
GstBuffer *out_buf = NULL;
|
||||
|
||||
out_buf = gst_harness_try_pull (h);
|
||||
if (out_buf) {
|
||||
num_output++;
|
||||
gst_buffer_unref (out_buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
fail_unless (num_output >= 3);
|
||||
|
||||
gst_harness_teardown (h);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
avaudenc_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("avaudenc");
|
||||
TCase *tc_chain = tcase_create ("general");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
tcase_add_test (tc_chain, test_audioenc_drain);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (avaudenc)
|
168
tests/check/elements/avdec_adpcm.c
Normal file
168
tests/check/elements/avdec_adpcm.c
Normal file
|
@ -0,0 +1,168 @@
|
|||
/* GStreamer unit tests for avdec_adpcm
|
||||
*
|
||||
* Copyright (C) 2009 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.
|
||||
*/
|
||||
|
||||
#include <gst/check/gstcheck.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
static void
|
||||
pad_added_cb (GstElement * decodebin, GstPad * pad, GstBin * pipeline)
|
||||
{
|
||||
GstElement *sink;
|
||||
|
||||
GST_INFO_OBJECT (pad, "got pad");
|
||||
|
||||
sink = gst_bin_get_by_name (pipeline, "fakesink");
|
||||
fail_unless (gst_element_link (decodebin, sink));
|
||||
gst_object_unref (sink);
|
||||
|
||||
gst_element_set_state (sink, GST_STATE_PAUSED);
|
||||
}
|
||||
|
||||
static GstBusSyncReply
|
||||
error_cb (GstBus * bus, GstMessage * msg, gpointer user_data)
|
||||
{
|
||||
if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
|
||||
const gchar *file = (const gchar *) user_data;
|
||||
GError *err = NULL;
|
||||
gchar *dbg = NULL;
|
||||
|
||||
gst_message_parse_error (msg, &err, &dbg);
|
||||
g_error ("ERROR for %s: %s\n%s\n", file, err->message, dbg);
|
||||
}
|
||||
|
||||
return GST_BUS_PASS;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
decode_file (const gchar * file, gboolean push_mode)
|
||||
{
|
||||
GstStateChangeReturn state_ret;
|
||||
GstElement *sink, *src, *dec, *queue, *pipeline;
|
||||
GstMessage *msg;
|
||||
GstBus *bus;
|
||||
gchar *path;
|
||||
|
||||
pipeline = gst_pipeline_new ("pipeline");
|
||||
fail_unless (pipeline != NULL, "Failed to create pipeline!");
|
||||
|
||||
src = gst_element_factory_make ("filesrc", "filesrc");
|
||||
fail_unless (src != NULL, "Failed to create filesrc!");
|
||||
|
||||
if (push_mode) {
|
||||
queue = gst_element_factory_make ("queue", "queue");
|
||||
} else {
|
||||
queue = gst_element_factory_make ("identity", "identity");
|
||||
}
|
||||
|
||||
dec = gst_element_factory_make ("decodebin", "decodebin");
|
||||
fail_unless (dec != NULL, "Failed to create decodebin!");
|
||||
|
||||
sink = gst_element_factory_make ("fakesink", "fakesink");
|
||||
fail_unless (sink != NULL, "Failed to create fakesink!");
|
||||
|
||||
bus = gst_element_get_bus (pipeline);
|
||||
|
||||
/* kids, don't use a sync handler for this at home, really; we do because
|
||||
* we just want to abort and nothing else */
|
||||
gst_bus_set_sync_handler (bus, error_cb, (gpointer) file, NULL);
|
||||
|
||||
gst_bin_add_many (GST_BIN (pipeline), src, queue, dec, sink, NULL);
|
||||
gst_element_link_many (src, queue, dec, NULL);
|
||||
|
||||
path = g_build_filename (GST_TEST_FILES_PATH, file, NULL);
|
||||
GST_LOG ("reading file '%s'", path);
|
||||
g_object_set (src, "location", path, NULL);
|
||||
|
||||
/* can't link uridecodebin and sink yet, do that later */
|
||||
g_signal_connect (dec, "pad-added", G_CALLBACK (pad_added_cb), pipeline);
|
||||
|
||||
state_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||
fail_unless (state_ret != GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
if (state_ret == GST_STATE_CHANGE_ASYNC) {
|
||||
GST_LOG ("waiting for pipeline to reach PAUSED state");
|
||||
state_ret = gst_element_get_state (pipeline, NULL, NULL, -1);
|
||||
fail_unless_equals_int (state_ret, GST_STATE_CHANGE_SUCCESS);
|
||||
}
|
||||
|
||||
state_ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
fail_unless (state_ret != GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
GST_LOG ("PAUSED, let's decode");
|
||||
msg = gst_bus_timed_pop_filtered (bus, 10 * GST_SECOND, GST_MESSAGE_EOS);
|
||||
GST_LOG ("Done, got EOS message");
|
||||
fail_unless (msg != NULL);
|
||||
gst_message_unref (msg);
|
||||
gst_object_unref (bus);
|
||||
|
||||
fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_NULL),
|
||||
GST_STATE_CHANGE_SUCCESS);
|
||||
gst_object_unref (pipeline);
|
||||
|
||||
g_free (path);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
run_check_for_file (const gchar * filename)
|
||||
{
|
||||
gboolean ret;
|
||||
|
||||
/* first, pull-based */
|
||||
ret = decode_file (filename, FALSE);
|
||||
fail_unless (ret == TRUE, "Failed to decode '%s' (pull mode)", filename);
|
||||
|
||||
/* second, push-based */
|
||||
ret = decode_file (filename, TRUE);
|
||||
fail_unless (ret == TRUE, "Failed to decode '%s' (push mode)", filename);
|
||||
}
|
||||
|
||||
GST_START_TEST (test_low_sample_rate_adpcm)
|
||||
{
|
||||
#define MIN_VERSION GST_VERSION_MAJOR, GST_VERSION_MINOR, 0
|
||||
if (!gst_registry_check_feature_version (gst_registry_get (), "wavparse",
|
||||
MIN_VERSION)
|
||||
|| !gst_registry_check_feature_version (gst_registry_get (), "decodebin",
|
||||
MIN_VERSION)) {
|
||||
g_printerr ("skipping test_low_sample_rate_adpcm: required element "
|
||||
"wavparse or element decodebin not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
run_check_for_file ("591809.wav");
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
avdec_adpcm_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("avdec_adpcm");
|
||||
TCase *tc_chain = tcase_create ("general");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
tcase_skip_broken_test (tc_chain, test_low_sample_rate_adpcm);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (avdec_adpcm)
|
206
tests/check/elements/avdemux_ape.c
Normal file
206
tests/check/elements/avdemux_ape.c
Normal file
|
@ -0,0 +1,206 @@
|
|||
/* GStreamer unit tests for avdemux_ape
|
||||
*
|
||||
* Copyright (C) 2009 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.
|
||||
*/
|
||||
|
||||
#include <gst/check/gstcheck.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
typedef void (CheckTagsFunc) (const GstTagList * tags, const gchar * file);
|
||||
|
||||
static void
|
||||
pad_added_cb (GstElement * decodebin, GstPad * pad, GstBin * pipeline)
|
||||
{
|
||||
GstElement *sink;
|
||||
|
||||
sink = gst_bin_get_by_name (pipeline, "fakesink");
|
||||
fail_unless (gst_element_link (decodebin, sink));
|
||||
gst_object_unref (sink);
|
||||
|
||||
gst_element_set_state (sink, GST_STATE_PAUSED);
|
||||
}
|
||||
|
||||
static GstBusSyncReply
|
||||
error_cb (GstBus * bus, GstMessage * msg, gpointer user_data)
|
||||
{
|
||||
if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
|
||||
const gchar *file = (const gchar *) user_data;
|
||||
GError *err = NULL;
|
||||
gchar *dbg = NULL;
|
||||
|
||||
gst_message_parse_error (msg, &err, &dbg);
|
||||
g_error ("ERROR for %s: %s\n%s\n", file, err->message, dbg);
|
||||
}
|
||||
|
||||
return GST_BUS_PASS;
|
||||
}
|
||||
|
||||
static GstPadProbeReturn
|
||||
event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
|
||||
{
|
||||
GstTagList **p_tags = user_data;
|
||||
GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
|
||||
|
||||
if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) {
|
||||
GST_INFO ("tag event: %" GST_PTR_FORMAT, event);
|
||||
if (*p_tags == NULL) {
|
||||
GstTagList *event_tags;
|
||||
|
||||
GST_INFO ("first tag, saving");
|
||||
gst_event_parse_tag (event, &event_tags);
|
||||
*p_tags = gst_tag_list_copy (event_tags);
|
||||
}
|
||||
}
|
||||
return GST_PAD_PROBE_OK; /* keep the data */
|
||||
}
|
||||
|
||||
/* FIXME: push_mode not used currently */
|
||||
static GstTagList *
|
||||
read_tags_from_file (const gchar * file, gboolean push_mode)
|
||||
{
|
||||
GstStateChangeReturn state_ret;
|
||||
GstTagList *tags = NULL;
|
||||
GstElement *sink, *src, *dec, *pipeline;
|
||||
GstBus *bus;
|
||||
GstPad *pad;
|
||||
gchar *path;
|
||||
|
||||
pipeline = gst_pipeline_new ("pipeline");
|
||||
fail_unless (pipeline != NULL, "Failed to create pipeline!");
|
||||
|
||||
src = gst_element_factory_make ("filesrc", "filesrc");
|
||||
fail_unless (src != NULL, "Failed to create filesrc!");
|
||||
|
||||
dec = gst_element_factory_make ("decodebin", "decodebin");
|
||||
fail_unless (dec != NULL, "Failed to create decodebin!");
|
||||
|
||||
sink = gst_element_factory_make ("fakesink", "fakesink");
|
||||
fail_unless (sink != NULL, "Failed to create fakesink!");
|
||||
|
||||
bus = gst_element_get_bus (pipeline);
|
||||
|
||||
/* kids, don't use a sync handler for this at home, really; we do because
|
||||
* we just want to abort and nothing else */
|
||||
gst_bus_set_sync_handler (bus, error_cb, (gpointer) file, NULL);
|
||||
|
||||
gst_bin_add_many (GST_BIN (pipeline), src, dec, sink, NULL);
|
||||
gst_element_link_many (src, dec, NULL);
|
||||
|
||||
path = g_build_filename (GST_TEST_FILES_PATH, file, NULL);
|
||||
GST_LOG ("reading file '%s'", path);
|
||||
g_object_set (src, "location", path, NULL);
|
||||
|
||||
/* can't link uridecodebin and sink yet, do that later */
|
||||
g_signal_connect (dec, "pad-added", G_CALLBACK (pad_added_cb), pipeline);
|
||||
|
||||
/* we want to make sure there's a tag event coming out of avdemux_ape
|
||||
* (ie. the one apedemux generated) */
|
||||
pad = gst_element_get_static_pad (sink, "sink");
|
||||
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, event_probe,
|
||||
&tags, NULL);
|
||||
gst_object_unref (pad);
|
||||
|
||||
state_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||
fail_unless (state_ret != GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
if (state_ret == GST_STATE_CHANGE_ASYNC) {
|
||||
GST_LOG ("waiting for pipeline to reach PAUSED state");
|
||||
state_ret = gst_element_get_state (pipeline, NULL, NULL, -1);
|
||||
fail_unless_equals_int (state_ret, GST_STATE_CHANGE_SUCCESS);
|
||||
}
|
||||
|
||||
GST_LOG ("PAUSED, let's retrieve our tags");
|
||||
|
||||
fail_unless (tags != NULL, "Expected tag event! (%s)", file);
|
||||
|
||||
gst_object_unref (bus);
|
||||
|
||||
fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_NULL),
|
||||
GST_STATE_CHANGE_SUCCESS);
|
||||
gst_object_unref (pipeline);
|
||||
|
||||
g_free (path);
|
||||
|
||||
GST_INFO ("%s: tags = %" GST_PTR_FORMAT, file, tags);
|
||||
return tags;
|
||||
}
|
||||
|
||||
static void
|
||||
run_check_for_file (const gchar * filename, CheckTagsFunc * check_func)
|
||||
{
|
||||
GstTagList *tags;
|
||||
|
||||
/* first, pull-based */
|
||||
tags = read_tags_from_file (filename, FALSE);
|
||||
fail_unless (tags != NULL, "Failed to extract tags from '%s'", filename);
|
||||
check_func (tags, filename);
|
||||
gst_tag_list_unref (tags);
|
||||
}
|
||||
|
||||
#define tag_list_has_tag(taglist,tag) \
|
||||
(gst_tag_list_get_value_index((taglist),(tag),0) != NULL)
|
||||
|
||||
/* just make sure avdemux_ape forwarded the tags extracted by apedemux
|
||||
* (should be the first tag list / tag event too) */
|
||||
static void
|
||||
check_for_apedemux_tags (const GstTagList * tags, const gchar * file)
|
||||
{
|
||||
gchar *artist = NULL;
|
||||
|
||||
fail_unless (gst_tag_list_get_string (tags, GST_TAG_ARTIST, &artist));
|
||||
fail_unless (artist != NULL);
|
||||
fail_unless_equals_string (artist, "Marvin Gaye");
|
||||
g_free (artist);
|
||||
|
||||
fail_unless (tag_list_has_tag (tags, GST_TAG_CONTAINER_FORMAT));
|
||||
|
||||
GST_LOG ("all good");
|
||||
}
|
||||
|
||||
GST_START_TEST (test_tag_caching)
|
||||
{
|
||||
#define MIN_VERSION GST_VERSION_MAJOR, GST_VERSION_MINOR, 0
|
||||
|
||||
if (!gst_registry_check_feature_version (gst_registry_get (), "apedemux",
|
||||
MIN_VERSION)
|
||||
|| !gst_registry_check_feature_version (gst_registry_get (), "decodebin",
|
||||
MIN_VERSION)) {
|
||||
g_printerr ("Skipping test_tag_caching: required element apedemux or "
|
||||
"decodebin element not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
run_check_for_file ("586957.ape", check_for_apedemux_tags);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
avdemux_ape_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("avdemux_ape");
|
||||
TCase *tc_chain = tcase_create ("general");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
tcase_add_test (tc_chain, test_tag_caching);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (avdemux_ape)
|
116
tests/check/elements/avvidenc.c
Normal file
116
tests/check/elements/avvidenc.c
Normal file
|
@ -0,0 +1,116 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2020 Seungha Yang <seungha@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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/check/gstcheck.h>
|
||||
#include <gst/check/gstharness.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
GST_START_TEST (test_videoenc_drain)
|
||||
{
|
||||
GstHarness *h;
|
||||
GstVideoInfo info;
|
||||
GstBuffer *in_buf;
|
||||
gint i = 0;
|
||||
gint num_output = 0;
|
||||
GstFlowReturn ret;
|
||||
GstSegment segment;
|
||||
GstCaps *caps;
|
||||
|
||||
h = gst_harness_new ("avenc_mjpeg");
|
||||
fail_unless (h != NULL);
|
||||
|
||||
caps = gst_caps_from_string ("video/x-raw,format=I420,width=64,height=64");
|
||||
|
||||
gst_harness_set_src_caps (h, gst_caps_copy (caps));
|
||||
gst_video_info_set_format (&info, GST_VIDEO_FORMAT_I420, 64, 64);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
in_buf = gst_buffer_new_and_alloc (GST_VIDEO_INFO_SIZE (&info));
|
||||
|
||||
GST_BUFFER_PTS (in_buf) = i * GST_SECOND;
|
||||
GST_BUFFER_DURATION (in_buf) = GST_SECOND;
|
||||
|
||||
ret = gst_harness_push (h, in_buf);
|
||||
|
||||
fail_unless (ret == GST_FLOW_OK, "GstFlowReturn was %s",
|
||||
gst_flow_get_name (ret));
|
||||
}
|
||||
|
||||
gst_segment_init (&segment, GST_FORMAT_TIME);
|
||||
fail_unless (gst_segment_set_running_time (&segment, GST_FORMAT_TIME,
|
||||
2 * GST_SECOND));
|
||||
|
||||
/* Push new eos event to drain encoder */
|
||||
fail_unless (gst_harness_push_event (h, gst_event_new_eos ()));
|
||||
|
||||
/* And start new stream */
|
||||
fail_unless (gst_harness_push_event (h,
|
||||
gst_event_new_stream_start ("new-stream-id")));
|
||||
gst_harness_set_src_caps (h, caps);
|
||||
fail_unless (gst_harness_push_event (h, gst_event_new_segment (&segment)));
|
||||
|
||||
in_buf = gst_buffer_new_and_alloc (GST_VIDEO_INFO_SIZE (&info));
|
||||
|
||||
GST_BUFFER_PTS (in_buf) = 2 * GST_SECOND;
|
||||
GST_BUFFER_DURATION (in_buf) = GST_SECOND;
|
||||
|
||||
ret = gst_harness_push (h, in_buf);
|
||||
fail_unless (ret == GST_FLOW_OK, "GstFlowReturn was %s",
|
||||
gst_flow_get_name (ret));
|
||||
|
||||
/* Finish encoding and drain again */
|
||||
fail_unless (gst_harness_push_event (h, gst_event_new_eos ()));
|
||||
do {
|
||||
GstBuffer *out_buf = NULL;
|
||||
|
||||
out_buf = gst_harness_try_pull (h);
|
||||
if (out_buf) {
|
||||
num_output++;
|
||||
gst_buffer_unref (out_buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
fail_unless_equals_int (num_output, 3);
|
||||
|
||||
gst_harness_teardown (h);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
avvidenc_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("avvidenc");
|
||||
TCase *tc_chain = tcase_create ("general");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
tcase_add_test (tc_chain, test_videoenc_drain);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (avvidenc)
|
161
tests/check/generic/libavcodec-locking.c
Normal file
161
tests/check/generic/libavcodec-locking.c
Normal file
|
@ -0,0 +1,161 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2005 Luca Ognibene <luogni@tin.it>
|
||||
* Based (copied) on simple_launch_lines.c
|
||||
*
|
||||
* libavcodec-locking.c: Unit test for libavcodec's locks
|
||||
*
|
||||
* 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/check/gstcheck.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define NUM_SINKS 10
|
||||
|
||||
static GstElement *
|
||||
setup_pipeline (const gchar * pipe_descr)
|
||||
{
|
||||
GstElement *pipeline;
|
||||
|
||||
pipeline = gst_parse_launch (pipe_descr, NULL);
|
||||
g_return_val_if_fail (GST_IS_PIPELINE (pipeline), NULL);
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
/*
|
||||
* run_pipeline:
|
||||
* @pipe: the pipeline to run
|
||||
* @desc: the description for use in messages
|
||||
* @events: is a mask of expected events
|
||||
* @tevent: is the expected terminal event.
|
||||
*
|
||||
* the poll call will time out after half a second.
|
||||
*/
|
||||
static void
|
||||
run_pipeline (GstElement * pipe, const gchar * descr,
|
||||
GstMessageType events, GstMessageType tevent)
|
||||
{
|
||||
GstBus *bus;
|
||||
GstMessage *message;
|
||||
GstMessageType revent;
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
g_assert (pipe);
|
||||
bus = gst_element_get_bus (pipe);
|
||||
g_assert (bus);
|
||||
|
||||
ret = gst_element_set_state (pipe, GST_STATE_PLAYING);
|
||||
ret = gst_element_get_state (pipe, NULL, NULL, GST_CLOCK_TIME_NONE);
|
||||
if (ret != GST_STATE_CHANGE_SUCCESS) {
|
||||
g_critical ("Couldn't set pipeline to PLAYING");
|
||||
goto done;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 2);
|
||||
|
||||
/* always have to pop the message before getting back into poll */
|
||||
if (message) {
|
||||
revent = GST_MESSAGE_TYPE (message);
|
||||
gst_message_unref (message);
|
||||
} else {
|
||||
revent = GST_MESSAGE_UNKNOWN;
|
||||
}
|
||||
|
||||
if (revent == tevent) {
|
||||
break;
|
||||
} else if (revent == GST_MESSAGE_UNKNOWN) {
|
||||
g_critical ("Unexpected timeout in gst_bus_poll, looking for %d: %s",
|
||||
tevent, descr);
|
||||
break;
|
||||
} else if (revent & events) {
|
||||
continue;
|
||||
}
|
||||
g_critical
|
||||
("Unexpected message received of type %d, '%s', looking for %d: %s",
|
||||
revent, gst_message_type_get_name (revent), tevent, descr);
|
||||
}
|
||||
|
||||
done:
|
||||
gst_element_set_state (pipe, GST_STATE_NULL);
|
||||
gst_object_unref (pipe);
|
||||
}
|
||||
|
||||
GST_START_TEST (test_libavcodec_locks)
|
||||
{
|
||||
gchar *sink[NUM_SINKS + 1], *s, *sinks;
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < NUM_SINKS; i++)
|
||||
sink[i] =
|
||||
g_strdup_printf
|
||||
(" t.src_%u ! queue ! avenc_mpeg4 ! avdec_mpeg4 ! fakesink sync=true",
|
||||
i);
|
||||
|
||||
sink[NUM_SINKS] = NULL;
|
||||
|
||||
sinks = g_strjoinv (" ", sink);
|
||||
|
||||
s = g_strdup_printf
|
||||
("videotestsrc ! video/x-raw,format=(string)I420,width=320,height=240,framerate=(fraction)10/1 ! tee name=t %s",
|
||||
sinks);
|
||||
|
||||
run_pipeline (setup_pipeline (s), s,
|
||||
GST_MESSAGE_ANY & ~(GST_MESSAGE_ERROR | GST_MESSAGE_WARNING),
|
||||
GST_MESSAGE_UNKNOWN);
|
||||
g_free (s);
|
||||
|
||||
for (i = 0; i < NUM_SINKS; i++)
|
||||
g_free (sink[i]);
|
||||
g_free (sinks);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
simple_launch_lines_suite (void)
|
||||
{
|
||||
gint timeout = 0;
|
||||
|
||||
Suite *s = suite_create ("Pipelines");
|
||||
TCase *tc_chain = tcase_create ("linear");
|
||||
|
||||
if (g_getenv ("CK_DEFAULT_TIMEOUT"))
|
||||
timeout = atoi (g_getenv ("CK_DEFAULT_TIMEOUT"));
|
||||
|
||||
if (timeout == 0)
|
||||
timeout = 3;
|
||||
|
||||
/* set multiple of default timeout (random magic value) */
|
||||
tcase_set_timeout (tc_chain, timeout * 12);
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
|
||||
#ifndef GST_DISABLE_PARSE
|
||||
/* only run this if we haven't been configured with --disable-encoders */
|
||||
if (gst_registry_check_feature_version (gst_registry_get (), "avenc_mpeg4",
|
||||
GST_VERSION_MAJOR, GST_VERSION_MINOR, 0)) {
|
||||
tcase_add_test (tc_chain, test_libavcodec_locks);
|
||||
} else {
|
||||
g_print ("******* Skipping libavcodec_locks test, no encoder available\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (simple_launch_lines);
|
98
tests/check/generic/plugin-test.c
Normal file
98
tests/check/generic/plugin-test.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
|
||||
*
|
||||
* Test that the FFmpeg plugin is loadable, and not broken in some stupid
|
||||
* way.
|
||||
*
|
||||
* 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/check/gstcheck.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
GST_START_TEST (test_libav_plugin)
|
||||
{
|
||||
GstPlugin *plugin = gst_plugin_load_by_name ("libav");
|
||||
|
||||
fail_if (plugin == NULL, "Could not load FFmpeg plugin");
|
||||
|
||||
gst_object_unref (plugin);
|
||||
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_libav_update_reg)
|
||||
{
|
||||
GstElement *encoder, *muxer, *decoder;
|
||||
|
||||
/* Ask for elements the first time */
|
||||
encoder = gst_element_factory_make ("avenc_mpeg2video", "sink");
|
||||
GST_DEBUG ("Creating element avenc_mpeg2video %p", encoder);
|
||||
fail_unless (encoder != NULL);
|
||||
|
||||
decoder = gst_element_factory_make ("avdec_mpeg2video", "sink");
|
||||
GST_DEBUG ("Creating element avdec_mpeg2video %p", decoder);
|
||||
fail_unless (decoder != NULL);
|
||||
|
||||
muxer = gst_element_factory_make ("avmux_dvd", "sink");
|
||||
GST_DEBUG ("Creating element avmux_dvd %p", muxer);
|
||||
fail_unless (muxer != NULL);
|
||||
|
||||
gst_object_unref (encoder);
|
||||
gst_object_unref (decoder);
|
||||
gst_object_unref (muxer);
|
||||
|
||||
GST_DEBUG ("calls gst_update_registry");
|
||||
gst_update_registry ();
|
||||
|
||||
/* Ask for elements the second time */
|
||||
|
||||
encoder = gst_element_factory_make ("avenc_mpeg2video", "sink");
|
||||
GST_DEBUG ("Creating element avenc_mpeg2video %p", encoder);
|
||||
fail_unless (encoder != NULL);
|
||||
|
||||
decoder = gst_element_factory_make ("avdec_mpeg2video", "sink");
|
||||
GST_DEBUG ("Creating element avdec_mpeg2video %p", decoder);
|
||||
fail_unless (decoder != NULL);
|
||||
|
||||
muxer = gst_element_factory_make ("avmux_dvd", "sink");
|
||||
GST_DEBUG ("Creating element avmux_dvd %p", muxer);
|
||||
fail_unless (muxer != NULL);
|
||||
|
||||
gst_object_unref (encoder);
|
||||
gst_object_unref (decoder);
|
||||
gst_object_unref (muxer);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
plugin_test_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("Plugin");
|
||||
TCase *tc_chain = tcase_create ("existence");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
|
||||
tcase_add_test (tc_chain, test_libav_plugin);
|
||||
tcase_add_test (tc_chain, test_libav_update_reg);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (plugin_test);
|
0
tests/check/gst-libav.supp
Normal file
0
tests/check/gst-libav.supp
Normal file
60
tests/check/meson.build
Normal file
60
tests/check/meson.build
Normal file
|
@ -0,0 +1,60 @@
|
|||
# name, condition when to skip the test and extra dependencies
|
||||
libav_tests = [
|
||||
[ 'elements/avaudenc' ],
|
||||
[ 'elements/avdec_adpcm' ],
|
||||
[ 'elements/avdemux_ape' ],
|
||||
[ 'elements/avvidenc' ],
|
||||
[ 'generic/libavcodec-locking' ],
|
||||
[ 'generic/plugin-test' ]
|
||||
]
|
||||
|
||||
test_defines = [
|
||||
'-UG_DISABLE_ASSERT',
|
||||
'-UG_DISABLE_CAST_CHECKS',
|
||||
'-DGST_CHECK_TEST_ENVIRONMENT_BEACON="GST_PLUGIN_LOADING_WHITELIST"',
|
||||
'-DGST_TEST_FILES_PATH="' + meson.current_source_dir() + '/../files"',
|
||||
'-DGST_USE_UNSTABLE_API',
|
||||
]
|
||||
|
||||
pluginsdirs = []
|
||||
if gst_dep.type_name() == 'pkgconfig'
|
||||
pbase = dependency('gstreamer-plugins-base-' + api_version, required: true)
|
||||
pluginsdirs = [gst_dep.get_pkgconfig_variable('pluginsdir'),
|
||||
pbase.get_pkgconfig_variable('pluginsdir')]
|
||||
gst_plugin_scanner_dir = gst_dep.get_pkgconfig_variable('pluginscannerdir')
|
||||
else
|
||||
gst_plugin_scanner_dir = subproject('gstreamer').get_variable('gst_scanner_dir')
|
||||
endif
|
||||
gst_plugin_scanner_path = join_paths(gst_plugin_scanner_dir, 'gst-plugin-scanner')
|
||||
|
||||
# FIXME: check, also + PTHREAD_CFLAGS
|
||||
test_deps = [gst_dep, gstbase_dep, gstcheck_dep, gstaudio_dep,
|
||||
gstvideo_dep, gstpbutils_dep]
|
||||
|
||||
# FIXME: add valgrind suppression common/gst.supp gst-libav.supp
|
||||
foreach t : libav_tests
|
||||
fname = '@0@.c'.format(t.get(0))
|
||||
test_name = t.get(0).underscorify()
|
||||
extra_sources = t.get(3, [ ])
|
||||
extra_deps = t.get(2, [ ])
|
||||
skip_test = t.get(1, false)
|
||||
if not skip_test
|
||||
env = environment()
|
||||
env.set('GST_PLUGIN_SYSTEM_PATH_1_0', '')
|
||||
env.set('CK_DEFAULT_TIMEOUT', '20')
|
||||
env.set('GST_PLUGIN_LOADING_WHITELIST', 'gstreamer', 'gst-plugins-base',
|
||||
'gst-libav@' + meson.build_root())
|
||||
env.set('GST_PLUGIN_PATH_1_0', [meson.build_root()] + pluginsdirs)
|
||||
env.set('GSETTINGS_BACKEND', 'memory')
|
||||
|
||||
env.set('GST_REGISTRY', join_paths(meson.current_build_dir(), '@0@.registry'.format(test_name)))
|
||||
env.set('GST_PLUGIN_SCANNER_1_0', gst_plugin_scanner_path)
|
||||
exe = executable(test_name, fname, extra_sources,
|
||||
include_directories : [configinc],
|
||||
c_args : ['-DHAVE_CONFIG_H=1' ] + test_defines,
|
||||
dependencies : [libm] + test_deps + extra_deps,
|
||||
)
|
||||
test(test_name, exe, env: env, timeout: 3 * 60)
|
||||
endif
|
||||
endforeach
|
||||
|
BIN
tests/files/586957.ape
Normal file
BIN
tests/files/586957.ape
Normal file
Binary file not shown.
BIN
tests/files/591809.wav
Normal file
BIN
tests/files/591809.wav
Normal file
Binary file not shown.
3
tests/meson.build
Normal file
3
tests/meson.build
Normal file
|
@ -0,0 +1,3 @@
|
|||
if not get_option('tests').disabled() and gstcheck_dep.found()
|
||||
subdir('check')
|
||||
endif
|
Loading…
Reference in a new issue